export const INPUT_MODAL_SCHEMA_VERSION = 1 as const; export type InputModalKind = "NONE" | "TOUCH_NAVIGATION" | "PEN_STROKE"; export interface InputModalState { schemaVersion: typeof INPUT_MODAL_SCHEMA_VERSION; kind: InputModalKind; activePointerIds: number[]; cancelled: boolean; navigationRevision: number; mainCommitCount: number; } export function createInputModalState(): InputModalState { return { schemaVersion: 1, kind: "NONE", activePointerIds: [], cancelled: false, navigationRevision: 0, mainCommitCount: 0 }; } export function beginTouch(state: InputModalState, pointerId: number): InputModalState { if (!Number.isSafeInteger(pointerId) || pointerId < 0) throw new Error("POINTER_ID_INVALID"); const ids = state.activePointerIds.includes(pointerId) ? state.activePointerIds : [...state.activePointerIds, pointerId].sort((a, b) => a - b); return { ...state, kind: "TOUCH_NAVIGATION", activePointerIds: ids, cancelled: false, navigationRevision: ids.length >= 2 && state.activePointerIds.length < 2 ? state.navigationRevision + 1 : state.navigationRevision }; } export function cancelInputModal(state: InputModalState): InputModalState { return { ...state, kind: "NONE", activePointerIds: [], cancelled: true }; } export function endTouch(state: InputModalState, pointerId: number): InputModalState { const ids = state.activePointerIds.filter((id) => id !== pointerId); return { ...state, kind: ids.length > 0 ? "TOUCH_NAVIGATION" : "NONE", activePointerIds: ids }; } export function beginPenStroke(state: InputModalState, pointerId: number): InputModalState { if (!Number.isSafeInteger(pointerId) || pointerId < 0) throw new Error("POINTER_ID_INVALID"); return { ...state, kind: "PEN_STROKE", activePointerIds: [pointerId], cancelled: false }; } export function commitPenStroke(state: InputModalState, pointerId: number): InputModalState { if (state.kind !== "PEN_STROKE" || !state.activePointerIds.includes(pointerId) || state.cancelled) return state; return { ...state, kind: "NONE", activePointerIds: [], mainCommitCount: state.mainCommitCount + 1 }; }