From a5faef5aee0b389586fb0c16fd0866ae7c86e3f4 Mon Sep 17 00:00:00 2001 From: sudacode Date: Tue, 31 Mar 2026 22:23:02 -0700 Subject: [PATCH] feat: streamline Kiku duplicate grouping and popup flow --- ...ate-IDs-for-generic-Kiku-field-grouping.md | 39 ++++++ ...-popup-duplicate-IDs-in-SubMiner-bridge.md | 41 ++++++ ...d-add-and-pause-close-before-Kiku-modal.md | 43 +++++++ ...or-configured-subtitle-jump-keybindings.md | 34 +++++ ...up-changes-to-fork-and-resync-submodule.md | 34 +++++ changes/267-yomitan-kiku-popup.md | 6 + docs-site/configuration.md | 1 + src/anki-integration.ts | 39 ++++++ .../anki-connect-proxy.test.ts | 117 ++++++++++++++++++ src/anki-integration/anki-connect-proxy.ts | 77 +++++++++++- src/anki-integration/card-creation.test.ts | 90 ++++++++++++++ src/anki-integration/card-creation.ts | 51 ++++++++ src/anki-integration/duplicate.ts | 34 +++-- src/anki-integration/field-grouping.test.ts | 42 +++++++ src/anki-integration/field-grouping.ts | 25 +++- .../tokenizer/yomitan-parser-runtime.test.ts | 17 +++ .../tokenizer/yomitan-parser-runtime.ts | 32 ++++- src/main.ts | 9 +- src/renderer/handlers/keyboard.test.ts | 75 +++++++++++ src/renderer/handlers/keyboard.ts | 29 ++++- src/renderer/kiku-open.test.ts | 35 ++++++ src/renderer/kiku-open.ts | 7 ++ src/renderer/renderer.ts | 7 ++ src/stats-word-helper.ts | 3 +- vendor/subminer-yomitan | 2 +- 25 files changed, 864 insertions(+), 25 deletions(-) create mode 100644 backlog/tasks/task-263 - Reuse-pre-add-duplicate-IDs-for-generic-Kiku-field-grouping.md create mode 100644 backlog/tasks/task-263.1 - Reuse-Yomitan-popup-duplicate-IDs-in-SubMiner-bridge.md create mode 100644 backlog/tasks/task-263.2 - Keep-Yomitan-popup-responsive-during-background-add-and-pause-close-before-Kiku-modal.md create mode 100644 backlog/tasks/task-266 - Preserve-paused-state-for-configured-subtitle-jump-keybindings.md create mode 100644 backlog/tasks/task-267 - Port-validated-Yomitan-popup-changes-to-fork-and-resync-submodule.md create mode 100644 changes/267-yomitan-kiku-popup.md create mode 100644 src/renderer/kiku-open.test.ts create mode 100644 src/renderer/kiku-open.ts diff --git a/backlog/tasks/task-263 - Reuse-pre-add-duplicate-IDs-for-generic-Kiku-field-grouping.md b/backlog/tasks/task-263 - Reuse-pre-add-duplicate-IDs-for-generic-Kiku-field-grouping.md new file mode 100644 index 00000000..c09e9050 --- /dev/null +++ b/backlog/tasks/task-263 - Reuse-pre-add-duplicate-IDs-for-generic-Kiku-field-grouping.md @@ -0,0 +1,39 @@ +--- +id: TASK-263 +title: Reuse pre-add duplicate IDs for generic Kiku field grouping +status: Done +assignee: [] +created_date: '2026-03-31 20:44' +updated_date: '2026-03-31 20:48' +labels: + - anki + - kiku +dependencies: [] +priority: high +--- + +## Description + + +Avoid the extra post-add duplicate lookup on the generic sentence-card creation path by capturing duplicate note IDs before add and reusing that result for Kiku field grouping. Keep Yomitan semantics aligned where practical so duplicate selection is consistent across mining paths. + + +## Acceptance Criteria + +- [x] #1 Generic sentence-card creation captures duplicate note IDs before add and reuses them for Kiku field grouping instead of running the existing post-add duplicate finder +- [x] #2 Duplicate selection remains deterministic when multiple matching notes exist +- [x] #3 Regression tests cover the generic path duplicate reuse behavior and preserve existing non-Kiku behavior +- [x] #4 Internal docs/config comments are updated if the behavior or operator-facing semantics changed + + +## Implementation Notes + + +No docs update was required because this is internal duplicate-selection plumbing and does not change user-facing config surface. + + +## Final Summary + + +Generic sentence-card creation now captures exact duplicate note IDs before add when Kiku field grouping is enabled and stores that context by created note ID. Manual field grouping reuses the tracked duplicate IDs first and deterministically picks the most recent matching note, falling back to the legacy duplicate finder only when no tracked context exists. Verified with bun test src/anki-integration/duplicate.test.ts src/anki-integration/card-creation.test.ts src/anki-integration/field-grouping.test.ts and bun run typecheck. + diff --git a/backlog/tasks/task-263.1 - Reuse-Yomitan-popup-duplicate-IDs-in-SubMiner-bridge.md b/backlog/tasks/task-263.1 - Reuse-Yomitan-popup-duplicate-IDs-in-SubMiner-bridge.md new file mode 100644 index 00000000..7caf7fe4 --- /dev/null +++ b/backlog/tasks/task-263.1 - Reuse-Yomitan-popup-duplicate-IDs-in-SubMiner-bridge.md @@ -0,0 +1,41 @@ +--- +id: TASK-263.1 +title: Reuse Yomitan popup duplicate IDs in SubMiner bridge +status: Done +assignee: [] +created_date: '2026-03-31 22:15' +updated_date: '2026-03-31 22:21' +labels: + - anki + - kiku + - yomitan +dependencies: [] +parent_task_id: TASK-263 +priority: high +--- + +## Description + + +Thread Yomitan popup/search duplicate note IDs through the existing SubMiner bridge so Kiku/manual grouping can reuse the same duplicate context that already drives the Add duplicate button. Implement and test against the vendored Yomitan copy first; do not rely on upstreamed fork changes yet. + + +## Acceptance Criteria + +- [x] #1 Vendored Yomitan bridge returns duplicate note IDs for popup/search mining when available +- [x] #2 SubMiner consumes the bridged duplicate IDs and prefers them for Kiku/manual grouping on the Yomitan mining path +- [x] #3 Regression tests cover the popup/search bridge payload and duplicate-id reuse behavior +- [x] #4 No commit is made for vendored Yomitan-only changes in this repo state + + +## Implementation Notes + + +Vendored files changed locally for validation only: vendor/subminer-yomitan/ext/js/display/display-anki.js, vendor/subminer-yomitan/ext/js/comm/api.js, vendor/subminer-yomitan/ext/js/comm/anki-connect.js, vendor/subminer-yomitan/ext/js/background/backend.js. Do not commit those vendor changes in this repo; port them to the fork instead. + + +## Final Summary + + +Vendored Yomitan popup/search mining now precomputes duplicate note IDs, sends them to the SubMiner Anki proxy as private addNote metadata, and still returns note/duplicate data through the parser bridge. The proxy strips the private metadata before forwarding to upstream AnkiConnect, associates the duplicate IDs with the created note before auto-enrichment begins, and SubMiner also records the bridge result as a secondary cache path. Verified with bun test src/anki-integration/duplicate.test.ts src/anki-integration/card-creation.test.ts src/anki-integration/field-grouping.test.ts src/anki-integration/anki-connect-proxy.test.ts src/core/services/tokenizer/yomitan-parser-runtime.test.ts and bun run typecheck. + diff --git a/backlog/tasks/task-263.2 - Keep-Yomitan-popup-responsive-during-background-add-and-pause-close-before-Kiku-modal.md b/backlog/tasks/task-263.2 - Keep-Yomitan-popup-responsive-during-background-add-and-pause-close-before-Kiku-modal.md new file mode 100644 index 00000000..5b663ab9 --- /dev/null +++ b/backlog/tasks/task-263.2 - Keep-Yomitan-popup-responsive-during-background-add-and-pause-close-before-Kiku-modal.md @@ -0,0 +1,43 @@ +--- +id: TASK-263.2 +title: >- + Keep Yomitan popup responsive during background add and pause/close before + Kiku modal +status: Done +assignee: [] +created_date: '2026-04-01 00:42' +updated_date: '2026-04-01 02:35' +labels: + - anki + - yomitan + - kiku + - ux +dependencies: [] +parent_task_id: TASK-263 +priority: high +--- + +## Description + + +Make Yomitan popup add run in background without blocking popup responsiveness. Before opening Kiku field-grouping modal, pause MPV and close the Yomitan popup/parser window if open. + + +## Definition of Done + +- [x] #1 Popup save path returns immediately and prevents duplicate submits +- [x] #2 Field-grouping modal request pauses MPV and closes Yomitan popup window first +- [x] #3 Regression tests cover async save dispatch and main-side pause/close hook + + +## Implementation Notes + + +2026-03-31: Removed the custom pending label/gray save-button presentation from vendored Yomitan. Background add still runs asynchronously with the internal pending-save guard, so duplicate clicks are ignored while the button keeps its stock appearance. + + +## Final Summary + + +Yomitan popup save dispatches note creation/add in the background with an internal pending-save guard so repeated clicks are ignored without blocking the popup. Before opening the Kiku field-grouping modal, the renderer now closes the visible lookup popup and pauses MPV. Follow-up UX polish removed the custom pending label/gray styling so the save button keeps Yomitan’s stock presentation while the background action runs. + diff --git a/backlog/tasks/task-266 - Preserve-paused-state-for-configured-subtitle-jump-keybindings.md b/backlog/tasks/task-266 - Preserve-paused-state-for-configured-subtitle-jump-keybindings.md new file mode 100644 index 00000000..d480d437 --- /dev/null +++ b/backlog/tasks/task-266 - Preserve-paused-state-for-configured-subtitle-jump-keybindings.md @@ -0,0 +1,34 @@ +--- +id: TASK-266 +title: Preserve paused state for configured subtitle-jump keybindings +status: Done +assignee: [] +created_date: '2026-04-01 03:19' +updated_date: '2026-04-01 03:19' +labels: + - renderer + - mpv + - keybindings + - regression +dependencies: [] +priority: high +--- + +## Description + + +Regression: configured overlay keybindings that forward raw mpv subtitle-jump commands (for example previous-subtitle on H) can resume playback when invoked while paused. Keyboard-driven edge jumps already preserve paused state; configured keybindings should match that behavior. + + +## Acceptance Criteria + +- [x] #1 Configured subtitle-jump keybindings preserve paused playback state after backward seek +- [x] #2 Existing keyboard-driven subtitle navigation behavior remains unchanged +- [x] #3 Regression test covers paused configured subtitle-jump keybinding handling + + +## Final Summary + + +Configured overlay keybindings that forward `sub-seek` commands now re-check paused state and reapply pause after the seek when playback was already paused. This aligns raw configured subtitle-jump keybindings with the existing keyboard-driven edge-jump behavior and adds regression coverage for the paused backward-seek case. + diff --git a/backlog/tasks/task-267 - Port-validated-Yomitan-popup-changes-to-fork-and-resync-submodule.md b/backlog/tasks/task-267 - Port-validated-Yomitan-popup-changes-to-fork-and-resync-submodule.md new file mode 100644 index 00000000..84332bdc --- /dev/null +++ b/backlog/tasks/task-267 - Port-validated-Yomitan-popup-changes-to-fork-and-resync-submodule.md @@ -0,0 +1,34 @@ +--- +id: TASK-267 +title: Port validated Yomitan popup changes to fork and resync submodule +status: Done +assignee: [] +created_date: '2026-04-01 03:30' +updated_date: '2026-04-01 03:33' +labels: + - yomitan + - submodule + - git + - integration +dependencies: [] +priority: high +--- + +## Description + + +Take the locally validated Yomitan popup/bridge changes from the vendored copy, apply them to the standalone `../subminer-yomitan` fork, verify the fork, push the fork commit, then reset the vendored working tree in SubMiner and update the submodule pointer to the pushed fork commit. + + +## Acceptance Criteria + +- [x] #1 Standalone `../subminer-yomitan` contains the validated popup/bridge changes and passes the relevant regression test +- [x] #2 The fork commit is pushed to its configured remote branch +- [x] #3 SubMiner vendored Yomitan working tree is reset and the submodule pointer is updated to the pushed fork commit + + +## Final Summary + + +Applied the validated popup/bridge changes from the vendored Yomitan copy into `../subminer-yomitan`, added the focused async-save regression test there, installed fork deps, and verified with `npx vitest run test/display-anki-save.test.js`. Committed the fork changes as `feat: preserve async popup save state and duplicate metadata`, rebased onto the updated remote `main`, and pushed commit `69620abc` to `origin/main`. Then reset the vendored submodule working tree in SubMiner, checked it out at `69620abc`, and left the superproject with the submodule pointer updated from `3c9ee577` to `69620abc`. + diff --git a/changes/267-yomitan-kiku-popup.md b/changes/267-yomitan-kiku-popup.md new file mode 100644 index 00000000..c41f8bd7 --- /dev/null +++ b/changes/267-yomitan-kiku-popup.md @@ -0,0 +1,6 @@ +type: fixed +area: overlay + +- Fixed Kiku duplicate grouping to reuse duplicate note IDs from both generic sentence-card creation and Yomitan popup mining instead of running extra duplicate scans after add. +- Fixed the Yomitan popup mining flow to add cards in the background while keeping the stock popup progress feedback, then pause playback and close the lookup popup before the Kiku merge modal opens. +- Fixed configured subtitle-jump keybindings so backward and forward subtitle seeks keep playback paused when invoked from a paused state. diff --git a/docs-site/configuration.md b/docs-site/configuration.md index b26b9e2c..16022e0d 100644 --- a/docs-site/configuration.md +++ b/docs-site/configuration.md @@ -969,6 +969,7 @@ To refresh roughly once per day, set: | `disabled` | No field grouping; duplicate cards are left as-is | `deleteDuplicateInAuto` controls whether `auto` mode deletes the duplicate after merge (default: `true`). In `manual` mode, the popup asks each time whether to delete the duplicate. +When the manual merge popup opens, SubMiner pauses playback and closes any open Yomitan popup first so the merge flow can take focus.