0 works both ways now

This commit is contained in:
2025-10-19 15:09:41 +01:00
parent 47aea08d49
commit 07f218f4dd
4 changed files with 99 additions and 89 deletions
+7 -6
View File
@@ -90,12 +90,13 @@ export type CursorManager = {
moveToTop: () => void;
moveToBottom: () => void;
repeatLastMotion: () => void;
resetCount: () => void;
getCount: () => string;
setCount: (digit: string) => void;
setLastKey: (key: string) => void;
getLastKey: () => string;
hero?: UseHeroType;
resetCount: () => void;
getCount: () => string;
hasCount: () => boolean;
setCount: (digit: string) => void;
setLastKey: (key: string) => void;
getLastKey: () => string;
hero?: UseHeroType;
}
export type HeroManager = {
+80 -75
View File
@@ -12,7 +12,7 @@ function validateAnchorMove(direction: 'start' | 'end' | 'top' | 'bottom', curre
const navigator = useMazeNavigation(board.mazeRef.current);
const target = navigator.findAnchorTarget(currentPos, navDirection);
if (!target) return false;
const dr = target.row > currentPos.row ? 1 :
@@ -78,61 +78,61 @@ export function useCursor(board: BoardManager, gameStatusManager: GameStatusMana
move(0, 1, count);
}
function moveToAnchor(target: 'start' | 'end' | 'top' | 'bottom'): void {
const currentPos = position.current;
const direction = target === 'start' ? 'left' :
target === 'end' ? 'right' :
target === 'top' ? 'top' : 'bottom';
function moveToAnchor(target: 'start' | 'end' | 'top' | 'bottom'): void {
const currentPos = position.current;
const direction = target === 'start' ? 'left' :
target === 'end' ? 'right' :
target === 'top' ? 'top' : 'bottom';
const anchorTarget = navigator.findAnchorTarget(currentPos, direction);
if (!anchorTarget) return;
const anchorTarget = navigator.findAnchorTarget(currentPos, direction);
if (!anchorTarget) return;
const dr = anchorTarget.row > currentPos.row ? 1 :
anchorTarget.row < currentPos.row ? -1 : 0;
const dc = anchorTarget.col > currentPos.col ? 1 :
anchorTarget.col < currentPos.col ? -1 : 0;
const steps = Math.abs(anchorTarget.row - currentPos.row) + Math.abs(anchorTarget.col - currentPos.col);
const dr = anchorTarget.row > currentPos.row ? 1 :
anchorTarget.row < currentPos.row ? -1 : 0;
const dc = anchorTarget.col > currentPos.col ? 1 :
anchorTarget.col < currentPos.col ? -1 : 0;
const steps = Math.abs(anchorTarget.row - currentPos.row) + Math.abs(anchorTarget.col - currentPos.col);
position.current = anchorTarget;
if (hero && hero.heroPos) {
hero.moveTo(anchorTarget);
if (steps > 0) {
vimMotions.setLastMotion({ dr, dc, steps });
}
}
}
position.current = anchorTarget;
if (hero && hero.heroPos) {
hero.moveTo(anchorTarget);
if (steps > 0) {
vimMotions.setLastMotion({ dr, dc, steps });
}
}
}
function moveToStart(): void {
if (!validateAnchorMove('start', position.current, board)) {
window.dispatchEvent(new Event("maze-invalid"));
return;
}
moveToAnchor('start');
}
function moveToStart(): void {
if (!validateAnchorMove('start', position.current, board)) {
window.dispatchEvent(new Event("maze-invalid"));
return;
}
moveToAnchor('start');
}
function moveToEnd(): void {
if (!validateAnchorMove('end', position.current, board)) {
window.dispatchEvent(new Event("maze-invalid"));
return;
}
moveToAnchor('end');
}
function moveToEnd(): void {
if (!validateAnchorMove('end', position.current, board)) {
window.dispatchEvent(new Event("maze-invalid"));
return;
}
moveToAnchor('end');
}
function moveToTop(): void {
if (!validateAnchorMove('top', position.current, board)) {
window.dispatchEvent(new Event("maze-invalid"));
return;
}
moveToAnchor('top');
}
function moveToTop(): void {
if (!validateAnchorMove('top', position.current, board)) {
window.dispatchEvent(new Event("maze-invalid"));
return;
}
moveToAnchor('top');
}
function moveToBottom(): void {
if (!validateAnchorMove('bottom', position.current, board)) {
window.dispatchEvent(new Event("maze-invalid"));
return;
}
moveToAnchor('bottom');
}
function moveToBottom(): void {
if (!validateAnchorMove('bottom', position.current, board)) {
window.dispatchEvent(new Event("maze-invalid"));
return;
}
moveToAnchor('bottom');
}
function repeatLastMotion(): void {
const motion = vimMotions.repeatLastMotion();
@@ -156,13 +156,17 @@ export function useCursor(board: BoardManager, gameStatusManager: GameStatusMana
vimMotions.resetCount();
}
function getCount(): string {
return String(vimMotions.getCount());
}
function getCount(): string {
return String(vimMotions.getCount());
}
function setCount(digit: string): void {
vimMotions.processKey(digit);
}
function hasCount(): boolean {
return vimMotions.hasCount();
}
function setCount(digit: string): void {
vimMotions.processKey(digit);
}
function setLastKey(key: string): void {
lastKeyRef.current = key;
@@ -172,26 +176,27 @@ export function useCursor(board: BoardManager, gameStatusManager: GameStatusMana
return lastKeyRef.current;
}
const cursor = {
position: () => position.current,
mode: () => mode.current,
move,
moveLeft,
moveRight,
moveUp,
moveDown,
moveToStart,
moveToEnd,
moveToTop,
moveToBottom,
repeatLastMotion,
resetCount,
getCount,
setCount,
setLastKey,
getLastKey,
hero,
}
const cursor = {
position: () => position.current,
mode: () => mode.current,
move,
moveLeft,
moveRight,
moveUp,
moveDown,
moveToStart,
moveToEnd,
moveToTop,
moveToBottom,
repeatLastMotion,
resetCount,
getCount,
hasCount,
setCount,
setLastKey,
getLastKey,
hero,
}
const keyManager = useGameKeys({ cursor, gameStatusManager });
+6 -8
View File
@@ -34,17 +34,15 @@ export const useGameKeys = (props: UseGameKeysParams) => {
const handler = (ev: KeyboardEvent) => {
if (["waiting", "game-over", "game-won"].includes(gameStatus)) return;
// "0" behaves like Vim: beginning of line
if (ev.key === "0" && cursor.getCount() === "") {
recordKey("0");
cursor.resetCount();
cursor.moveToStart();
return;
}
if (ev.key === "0" && !cursor.hasCount()) {
recordKey("0");
cursor.resetCount();
cursor.moveToStart();
return;
}
const isDigit = ev.key >= "0" && ev.key <= "9";
if (isDigit) {
if (ev.key === "0" && cursor.getCount() === "") return;
recordKey(ev.key);
cursor.setCount(ev.key);
return;
+6
View File
@@ -6,6 +6,7 @@ export type VimMotionSystem = {
processKey: (key: string) => { type: 'move'|'anchor'|'repeat'|'count', data: any } | null;
resetCount: () => void;
getCount: () => number;
hasCount: () => boolean;
setLastMotion: (motion: Motion) => void;
repeatLastMotion: () => Motion | null;
};
@@ -98,10 +99,15 @@ export function useVimMotions(): VimMotionSystem {
return lastMotionRef.current;
}
function hasCount(): boolean {
return countRef.current !== '';
}
return {
processKey,
resetCount,
getCount,
hasCount,
setLastMotion,
repeatLastMotion
};