From 12c5d956bcd4454436d6ea87432239e4a4b3944e Mon Sep 17 00:00:00 2001 From: sudacode Date: Fri, 20 Feb 2026 03:35:47 -0800 Subject: [PATCH] refactor: extract numeric and overlay shortcuts runtime wiring --- docs/subagents/INDEX.md | 2 +- .../codex-task85-20260219T233711Z-46hc.md | 5 + src/main.ts | 157 +++--------------- ...-shortcut-session-runtime-handlers.test.ts | 35 ++++ ...meric-shortcut-session-runtime-handlers.ts | 67 ++++++++ ...overlay-shortcuts-runtime-handlers.test.ts | 24 +++ .../overlay-shortcuts-runtime-handlers.ts | 51 ++++++ 7 files changed, 203 insertions(+), 138 deletions(-) create mode 100644 src/main/runtime/numeric-shortcut-session-runtime-handlers.test.ts create mode 100644 src/main/runtime/numeric-shortcut-session-runtime-handlers.ts create mode 100644 src/main/runtime/overlay-shortcuts-runtime-handlers.test.ts create mode 100644 src/main/runtime/overlay-shortcuts-runtime-handlers.ts diff --git a/docs/subagents/INDEX.md b/docs/subagents/INDEX.md index 99b07a3..73bb452 100644 --- a/docs/subagents/INDEX.md +++ b/docs/subagents/INDEX.md @@ -6,7 +6,7 @@ Read first. Keep concise. | ------------ | -------------- | ---------------------------------------------------- | --------- | ------------------------------------- | ---------------------- | | `codex-generate-minecard-image-20260220T112900Z-vsxr` | `codex-generate-minecard-image` | `Generate media fallbacks (GIF) from assets/minecard.webm and wire README/docs fallback markup` | `done` | `docs/subagents/agents/codex-generate-minecard-image-20260220T112900Z-vsxr.md` | `2026-02-20T11:35:30Z` | | `codex-main` | `planner-exec` | `Fix frequency/N+1 regression in plugin --start flow` | `in_progress` | `docs/subagents/agents/codex-main.md` | `2026-02-19T19:36:46Z` | -| `codex-task85-20260219T233711Z-46hc` | `codex-task85` | `Resume TASK-85 maintainability refactor from latest handoff point` | `in_progress` | `docs/subagents/agents/codex-task85-20260219T233711Z-46hc.md` | `2026-02-20T11:32:45Z` | +| `codex-task85-20260219T233711Z-46hc` | `codex-task85` | `Resume TASK-85 maintainability refactor from latest handoff point` | `in_progress` | `docs/subagents/agents/codex-task85-20260219T233711Z-46hc.md` | `2026-02-20T11:35:21Z` | | `codex-config-validation-20260219T172015Z-iiyf` | `codex-config-validation` | `Find root cause of config validation error for ~/.config/SubMiner/config.jsonc` | `completed` | `docs/subagents/agents/codex-config-validation-20260219T172015Z-iiyf.md` | `2026-02-19T17:26:17Z` | | `codex-task85-20260219T233711Z-46hc` | `codex-task85` | `Resume TASK-85 maintainability refactor from latest handoff point` | `in_progress` | `docs/subagents/agents/codex-task85-20260219T233711Z-46hc.md` | `2026-02-20T02:56:34Z` | | `codex-anilist-deeplink-20260219T233926Z` | `anilist-deeplink` | `Fix external subminer:// AniList callback handling from browser` | `done` | `docs/subagents/agents/codex-anilist-deeplink-20260219T233926Z.md` | `2026-02-19T23:59:21Z` | diff --git a/docs/subagents/agents/codex-task85-20260219T233711Z-46hc.md b/docs/subagents/agents/codex-task85-20260219T233711Z-46hc.md index 13a556c..a55903f 100644 --- a/docs/subagents/agents/codex-task85-20260219T233711Z-46hc.md +++ b/docs/subagents/agents/codex-task85-20260219T233711Z-46hc.md @@ -9,6 +9,11 @@ ## Current Work (newest first) +- [2026-02-20T11:35:21Z] progress: extracted numeric shortcut session composition from `src/main.ts` into `src/main/runtime/numeric-shortcut-session-runtime-handlers.ts`; `main.ts` now gets `cancel/start` handlers for multi-copy and mine-sentence sessions from one runtime factory. +- [2026-02-20T11:35:21Z] progress: extracted overlay shortcuts lifecycle composition from `src/main.ts` into `src/main/runtime/overlay-shortcuts-runtime-handlers.ts`; `main.ts` now gets register/unregister/sync/refresh handlers via one runtime factory. +- [2026-02-20T11:35:21Z] progress: added parity tests `src/main/runtime/numeric-shortcut-session-runtime-handlers.test.ts` and `src/main/runtime/overlay-shortcuts-runtime-handlers.test.ts`. +- [2026-02-20T11:35:21Z] test: `bun run build` pass (expected macOS helper Swift cache fallback) + focused suites pass for `numeric-shortcut-session-runtime-handlers*`, `overlay-shortcuts-runtime-handlers*`, `numeric-shortcut-session-*`, and `overlay-shortcuts-lifecycle-*` (10/10). +- [2026-02-20T11:35:21Z] scope: staging `src/main.ts`, new numeric/overlay-shortcuts runtime handler modules/tests, and subagent bookkeeping only. - [2026-02-20T11:32:45Z] progress: extracted MPV OSD composition from `src/main.ts` into `src/main/runtime/mpv-osd-runtime-handlers.ts`; `main.ts` now receives `appendToMpvLog` and `showMpvOsd` from one runtime factory. - [2026-02-20T11:32:45Z] progress: extracted secondary subtitle mode composition from `src/main.ts` into `src/main/runtime/secondary-sub-mode-runtime-handler.ts`; `main.ts` now builds `cycleSecondarySubMode` via one runtime handler factory. - [2026-02-20T11:32:45Z] progress: added parity tests `src/main/runtime/mpv-osd-runtime-handlers.test.ts` and `src/main/runtime/secondary-sub-mode-runtime-handler.test.ts`. diff --git a/src/main.ts b/src/main.ts index e0527c6..5022f4c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -290,27 +290,9 @@ import { buildTrayMenuTemplateRuntime, resolveTrayIconPathRuntime } from './main import { createGlobalShortcutsRuntimeHandlers } from './main/runtime/global-shortcuts-runtime-handlers'; import { createMpvOsdRuntimeHandlers } from './main/runtime/mpv-osd-runtime-handlers'; import { createCycleSecondarySubModeRuntimeHandler } from './main/runtime/secondary-sub-mode-runtime-handler'; -import { - createCancelNumericShortcutSessionHandler, - createStartNumericShortcutSessionHandler, -} from './main/runtime/numeric-shortcut-session-handlers'; -import { - createBuildCancelNumericShortcutSessionMainDepsHandler, - createBuildStartNumericShortcutSessionMainDepsHandler, -} from './main/runtime/numeric-shortcut-session-main-deps'; +import { createNumericShortcutSessionRuntimeHandlers } from './main/runtime/numeric-shortcut-session-runtime-handlers'; import { createBuildNumericShortcutRuntimeMainDepsHandler } from './main/runtime/numeric-shortcut-runtime-main-deps'; -import { - createRefreshOverlayShortcutsHandler, - createRegisterOverlayShortcutsHandler, - createSyncOverlayShortcutsHandler, - createUnregisterOverlayShortcutsHandler, -} from './main/runtime/overlay-shortcuts-lifecycle'; -import { - createBuildRefreshOverlayShortcutsMainDepsHandler, - createBuildRegisterOverlayShortcutsMainDepsHandler, - createBuildSyncOverlayShortcutsMainDepsHandler, - createBuildUnregisterOverlayShortcutsMainDepsHandler, -} from './main/runtime/overlay-shortcuts-lifecycle-main-deps'; +import { createOverlayShortcutsRuntimeHandlers } from './main/runtime/overlay-shortcuts-runtime-handlers'; import { createBuildOverlayShortcutsRuntimeMainDepsHandler } from './main/runtime/overlay-shortcuts-runtime-main-deps'; import { createMarkLastCardAsAudioCardHandler, @@ -2565,108 +2547,32 @@ const numericShortcutRuntime = createNumericShortcutRuntime( ); const multiCopySession = numericShortcutRuntime.createSession(); const mineSentenceSession = numericShortcutRuntime.createSession(); -const buildCancelPendingMultiCopyMainDepsHandler = - createBuildCancelNumericShortcutSessionMainDepsHandler({ - session: multiCopySession, +const { + cancelPendingMultiCopy, + startPendingMultiCopy, + cancelPendingMineSentenceMultiple, + startPendingMineSentenceMultiple, +} = createNumericShortcutSessionRuntimeHandlers({ + multiCopySession, + mineSentenceSession, + onMultiCopyDigit: (count) => handleMultiCopyDigit(count), + onMineSentenceDigit: (count) => handleMineSentenceDigit(count), }); -const cancelPendingMultiCopyMainDeps = - buildCancelPendingMultiCopyMainDepsHandler(); -const cancelPendingMultiCopyHandler = createCancelNumericShortcutSessionHandler( - cancelPendingMultiCopyMainDeps, -); - -const buildStartPendingMultiCopyMainDepsHandler = - createBuildStartNumericShortcutSessionMainDepsHandler({ - session: multiCopySession, - onDigit: (count) => handleMultiCopyDigit(count), - messages: { - prompt: 'Copy how many lines? Press 1-9 (Esc to cancel)', - timeout: 'Copy timeout', - cancelled: 'Cancelled', +const { + registerOverlayShortcuts, + unregisterOverlayShortcuts, + syncOverlayShortcuts, + refreshOverlayShortcuts, +} = createOverlayShortcutsRuntimeHandlers({ + overlayShortcutsRuntimeMainDeps: { + overlayShortcutsRuntime, }, }); -const startPendingMultiCopyMainDeps = - buildStartPendingMultiCopyMainDepsHandler(); -const startPendingMultiCopyHandler = createStartNumericShortcutSessionHandler( - startPendingMultiCopyMainDeps, -); - -const buildCancelPendingMineSentenceMultipleMainDepsHandler = - createBuildCancelNumericShortcutSessionMainDepsHandler({ - session: mineSentenceSession, -}); -const cancelPendingMineSentenceMultipleMainDeps = - buildCancelPendingMineSentenceMultipleMainDepsHandler(); -const cancelPendingMineSentenceMultipleHandler = createCancelNumericShortcutSessionHandler( - cancelPendingMineSentenceMultipleMainDeps, -); - -const buildStartPendingMineSentenceMultipleMainDepsHandler = - createBuildStartNumericShortcutSessionMainDepsHandler({ - session: mineSentenceSession, - onDigit: (count) => handleMineSentenceDigit(count), - messages: { - prompt: 'Mine how many lines? Press 1-9 (Esc to cancel)', - timeout: 'Mine sentence timeout', - cancelled: 'Cancelled', - }, -}); -const startPendingMineSentenceMultipleMainDeps = - buildStartPendingMineSentenceMultipleMainDepsHandler(); -const startPendingMineSentenceMultipleHandler = createStartNumericShortcutSessionHandler( - startPendingMineSentenceMultipleMainDeps, -); - -const buildRegisterOverlayShortcutsMainDepsHandler = - createBuildRegisterOverlayShortcutsMainDepsHandler({ - overlayShortcutsRuntime, -}); -const registerOverlayShortcutsMainDeps = - buildRegisterOverlayShortcutsMainDepsHandler(); -const registerOverlayShortcutsHandler = createRegisterOverlayShortcutsHandler( - registerOverlayShortcutsMainDeps, -); - -const buildUnregisterOverlayShortcutsMainDepsHandler = - createBuildUnregisterOverlayShortcutsMainDepsHandler({ - overlayShortcutsRuntime, -}); -const unregisterOverlayShortcutsMainDeps = - buildUnregisterOverlayShortcutsMainDepsHandler(); -const unregisterOverlayShortcutsHandler = createUnregisterOverlayShortcutsHandler( - unregisterOverlayShortcutsMainDeps, -); - -const buildSyncOverlayShortcutsMainDepsHandler = createBuildSyncOverlayShortcutsMainDepsHandler({ - overlayShortcutsRuntime, -}); -const syncOverlayShortcutsMainDeps = buildSyncOverlayShortcutsMainDepsHandler(); -const syncOverlayShortcutsHandler = createSyncOverlayShortcutsHandler( - syncOverlayShortcutsMainDeps, -); - -const buildRefreshOverlayShortcutsMainDepsHandler = - createBuildRefreshOverlayShortcutsMainDepsHandler({ - overlayShortcutsRuntime, -}); -const refreshOverlayShortcutsMainDeps = - buildRefreshOverlayShortcutsMainDepsHandler(); -const refreshOverlayShortcutsHandler = createRefreshOverlayShortcutsHandler( - refreshOverlayShortcutsMainDeps, -); async function triggerSubsyncFromConfig(): Promise { await subsyncRuntime.triggerFromConfig(); } -function cancelPendingMultiCopy(): void { - cancelPendingMultiCopyHandler(); -} - -function startPendingMultiCopy(timeoutMs: number): void { - startPendingMultiCopyHandler(timeoutMs); -} - function handleMultiCopyDigit(count: number): void { handleMultiCopyDigitHandler(count); } @@ -3089,33 +2995,10 @@ async function mineSentenceCard(): Promise { await mineSentenceCardHandler(); } -function cancelPendingMineSentenceMultiple(): void { - cancelPendingMineSentenceMultipleHandler(); -} - -function startPendingMineSentenceMultiple(timeoutMs: number): void { - startPendingMineSentenceMultipleHandler(timeoutMs); -} - function handleMineSentenceDigit(count: number): void { handleMineSentenceDigitHandler(count); } -function registerOverlayShortcuts(): void { - registerOverlayShortcutsHandler(); -} - -function unregisterOverlayShortcuts(): void { - unregisterOverlayShortcutsHandler(); -} - -function syncOverlayShortcuts(): void { - syncOverlayShortcutsHandler(); -} -function refreshOverlayShortcuts(): void { - refreshOverlayShortcutsHandler(); -} - function setVisibleOverlayVisible(visible: boolean): void { setVisibleOverlayVisibleHandler(visible); } diff --git a/src/main/runtime/numeric-shortcut-session-runtime-handlers.test.ts b/src/main/runtime/numeric-shortcut-session-runtime-handlers.test.ts new file mode 100644 index 0000000..65f6013 --- /dev/null +++ b/src/main/runtime/numeric-shortcut-session-runtime-handlers.test.ts @@ -0,0 +1,35 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { createNumericShortcutSessionRuntimeHandlers } from './numeric-shortcut-session-runtime-handlers'; + +test('numeric shortcut session runtime handlers compose cancel/start handlers', () => { + const calls: string[] = []; + const createSession = (name: string) => ({ + start: ({ timeoutMs, onDigit }: { timeoutMs: number; onDigit: (digit: number) => void }) => { + calls.push(`${name}:start:${timeoutMs}`); + onDigit(3); + }, + cancel: () => calls.push(`${name}:cancel`), + }); + + const runtime = createNumericShortcutSessionRuntimeHandlers({ + multiCopySession: createSession('multi-copy'), + mineSentenceSession: createSession('mine-sentence'), + onMultiCopyDigit: (count) => calls.push(`multi-copy:digit:${count}`), + onMineSentenceDigit: (count) => calls.push(`mine-sentence:digit:${count}`), + }); + + runtime.cancelPendingMultiCopy(); + runtime.startPendingMultiCopy(500); + runtime.cancelPendingMineSentenceMultiple(); + runtime.startPendingMineSentenceMultiple(700); + + assert.deepEqual(calls, [ + 'multi-copy:cancel', + 'multi-copy:start:500', + 'multi-copy:digit:3', + 'mine-sentence:cancel', + 'mine-sentence:start:700', + 'mine-sentence:digit:3', + ]); +}); diff --git a/src/main/runtime/numeric-shortcut-session-runtime-handlers.ts b/src/main/runtime/numeric-shortcut-session-runtime-handlers.ts new file mode 100644 index 0000000..c0c098f --- /dev/null +++ b/src/main/runtime/numeric-shortcut-session-runtime-handlers.ts @@ -0,0 +1,67 @@ +import { + createCancelNumericShortcutSessionHandler, + createStartNumericShortcutSessionHandler, +} from './numeric-shortcut-session-handlers'; +import { + createBuildCancelNumericShortcutSessionMainDepsHandler, + createBuildStartNumericShortcutSessionMainDepsHandler, +} from './numeric-shortcut-session-main-deps'; + +type CancelNumericShortcutSessionMainDeps = Parameters< + typeof createBuildCancelNumericShortcutSessionMainDepsHandler +>[0]; + +export function createNumericShortcutSessionRuntimeHandlers(deps: { + multiCopySession: CancelNumericShortcutSessionMainDeps['session']; + mineSentenceSession: CancelNumericShortcutSessionMainDeps['session']; + onMultiCopyDigit: (count: number) => void; + onMineSentenceDigit: (count: number) => void; +}) { + const cancelPendingMultiCopyMainDeps = createBuildCancelNumericShortcutSessionMainDepsHandler({ + session: deps.multiCopySession, + })(); + const cancelPendingMultiCopyHandler = + createCancelNumericShortcutSessionHandler(cancelPendingMultiCopyMainDeps); + + const startPendingMultiCopyMainDeps = createBuildStartNumericShortcutSessionMainDepsHandler({ + session: deps.multiCopySession, + onDigit: deps.onMultiCopyDigit, + messages: { + prompt: 'Copy how many lines? Press 1-9 (Esc to cancel)', + timeout: 'Copy timeout', + cancelled: 'Cancelled', + }, + })(); + const startPendingMultiCopyHandler = + createStartNumericShortcutSessionHandler(startPendingMultiCopyMainDeps); + + const cancelPendingMineSentenceMultipleMainDeps = + createBuildCancelNumericShortcutSessionMainDepsHandler({ + session: deps.mineSentenceSession, + })(); + const cancelPendingMineSentenceMultipleHandler = createCancelNumericShortcutSessionHandler( + cancelPendingMineSentenceMultipleMainDeps, + ); + + const startPendingMineSentenceMultipleMainDeps = + createBuildStartNumericShortcutSessionMainDepsHandler({ + session: deps.mineSentenceSession, + onDigit: deps.onMineSentenceDigit, + messages: { + prompt: 'Mine how many lines? Press 1-9 (Esc to cancel)', + timeout: 'Mine sentence timeout', + cancelled: 'Cancelled', + }, + })(); + const startPendingMineSentenceMultipleHandler = createStartNumericShortcutSessionHandler( + startPendingMineSentenceMultipleMainDeps, + ); + + return { + cancelPendingMultiCopy: () => cancelPendingMultiCopyHandler(), + startPendingMultiCopy: (timeoutMs: number) => startPendingMultiCopyHandler(timeoutMs), + cancelPendingMineSentenceMultiple: () => cancelPendingMineSentenceMultipleHandler(), + startPendingMineSentenceMultiple: (timeoutMs: number) => + startPendingMineSentenceMultipleHandler(timeoutMs), + }; +} diff --git a/src/main/runtime/overlay-shortcuts-runtime-handlers.test.ts b/src/main/runtime/overlay-shortcuts-runtime-handlers.test.ts new file mode 100644 index 0000000..8db02d3 --- /dev/null +++ b/src/main/runtime/overlay-shortcuts-runtime-handlers.test.ts @@ -0,0 +1,24 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { createOverlayShortcutsRuntimeHandlers } from './overlay-shortcuts-runtime-handlers'; + +test('overlay shortcuts runtime handlers compose lifecycle handlers', () => { + const calls: string[] = []; + const runtime = createOverlayShortcutsRuntimeHandlers({ + overlayShortcutsRuntimeMainDeps: { + overlayShortcutsRuntime: { + registerOverlayShortcuts: () => calls.push('register'), + unregisterOverlayShortcuts: () => calls.push('unregister'), + syncOverlayShortcuts: () => calls.push('sync'), + refreshOverlayShortcuts: () => calls.push('refresh'), + }, + }, + }); + + runtime.registerOverlayShortcuts(); + runtime.unregisterOverlayShortcuts(); + runtime.syncOverlayShortcuts(); + runtime.refreshOverlayShortcuts(); + + assert.deepEqual(calls, ['register', 'unregister', 'sync', 'refresh']); +}); diff --git a/src/main/runtime/overlay-shortcuts-runtime-handlers.ts b/src/main/runtime/overlay-shortcuts-runtime-handlers.ts new file mode 100644 index 0000000..03624ff --- /dev/null +++ b/src/main/runtime/overlay-shortcuts-runtime-handlers.ts @@ -0,0 +1,51 @@ +import { + createRefreshOverlayShortcutsHandler, + createRegisterOverlayShortcutsHandler, + createSyncOverlayShortcutsHandler, + createUnregisterOverlayShortcutsHandler, +} from './overlay-shortcuts-lifecycle'; +import { + createBuildRefreshOverlayShortcutsMainDepsHandler, + createBuildRegisterOverlayShortcutsMainDepsHandler, + createBuildSyncOverlayShortcutsMainDepsHandler, + createBuildUnregisterOverlayShortcutsMainDepsHandler, +} from './overlay-shortcuts-lifecycle-main-deps'; + +type RegisterOverlayShortcutsMainDeps = Parameters< + typeof createBuildRegisterOverlayShortcutsMainDepsHandler +>[0]; + +export function createOverlayShortcutsRuntimeHandlers(deps: { + overlayShortcutsRuntimeMainDeps: RegisterOverlayShortcutsMainDeps; +}) { + const registerOverlayShortcutsMainDeps = createBuildRegisterOverlayShortcutsMainDepsHandler( + deps.overlayShortcutsRuntimeMainDeps, + )(); + const registerOverlayShortcutsHandler = + createRegisterOverlayShortcutsHandler(registerOverlayShortcutsMainDeps); + + const unregisterOverlayShortcutsMainDeps = + createBuildUnregisterOverlayShortcutsMainDepsHandler( + deps.overlayShortcutsRuntimeMainDeps, + )(); + const unregisterOverlayShortcutsHandler = + createUnregisterOverlayShortcutsHandler(unregisterOverlayShortcutsMainDeps); + + const syncOverlayShortcutsMainDeps = createBuildSyncOverlayShortcutsMainDepsHandler( + deps.overlayShortcutsRuntimeMainDeps, + )(); + const syncOverlayShortcutsHandler = createSyncOverlayShortcutsHandler(syncOverlayShortcutsMainDeps); + + const refreshOverlayShortcutsMainDeps = createBuildRefreshOverlayShortcutsMainDepsHandler( + deps.overlayShortcutsRuntimeMainDeps, + )(); + const refreshOverlayShortcutsHandler = + createRefreshOverlayShortcutsHandler(refreshOverlayShortcutsMainDeps); + + return { + registerOverlayShortcuts: () => registerOverlayShortcutsHandler(), + unregisterOverlayShortcuts: () => unregisterOverlayShortcutsHandler(), + syncOverlayShortcuts: () => syncOverlayShortcutsHandler(), + refreshOverlayShortcuts: () => refreshOverlayShortcutsHandler(), + }; +}