fix(hachidori): finish character dictionary imports without keep-alive w

- Close lingering sockets on the one-shot dictionary ZIP server so imports report completion as soon as the archive is read
- Run test:dictionary:electron in the Linux CI E2E job
- Note that annotations and lookups pause during Hachidori imports
- Keep only dictionaryBackend in the Dictionary Lookup settings section
- Move the requestHachidoriSharing import below the license header
This commit is contained in:
sudacode committed 2026-10-18 22:14:44 -07:00
1 parent 9e53ecb27b
commit 4842359e39
7 files changed
+12 -4

No files matched your search

+4
View File
@@ -78,6 +78,10 @@ jobs:
- name: End-to-end suite - name: End-to-end suite
run: bun run test:e2e run: bun run test:e2e
- name: Dictionary backend windows (Linux)
if: runner.os == 'Linux'
run: xvfb-run -a bun run test:dictionary:electron
- name: Upload session logs (on failure) - name: Upload session logs (on failure)
if: failure() if: failure()
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
+1
View File
@@ -10,5 +10,6 @@ area: dictionary
- First-run setup remembers each backend that finished it, including when reopened for legacy plugin cleanup, so switching back does not repeat setup, and the launcher gates playback on the backend the running app started with. Current incomplete or cancelled setup takes precedence over stale completion history. A running Yomitan session keeps using its external profile until it restarts into Hachidori. - First-run setup remembers each backend that finished it, including when reopened for legacy plugin cleanup, so switching back does not repeat setup, and the launcher gates playback on the backend the running app started with. Current incomplete or cancelled setup takes precedence over stale completion history. A running Yomitan session keeps using its external profile until it restarts into Hachidori.
- Stats dashboard mining and deck lookup use the selected backend, with the Anki proxy enabled or disabled. Dashboard cards retain the selected history line and media instead of being processed again with the current mpv subtitle, with both Yomitan and Hachidori. Stats cards carry a `SubMiner::Stats` tag so polling also preserves their context. Hachidori word mining supplies dictionary aliases, IDs, and frequency metadata to its native Anki renderer. Settings labels for popup pause and the dictionary deck no longer name Yomitan, and the backend selector sits with the other dictionary settings. - Stats dashboard mining and deck lookup use the selected backend, with the Anki proxy enabled or disabled. Dashboard cards retain the selected history line and media instead of being processed again with the current mpv subtitle, with both Yomitan and Hachidori. Stats cards carry a `SubMiner::Stats` tag so polling also preserves their context. Hachidori word mining supplies dictionary aliases, IDs, and frequency metadata to its native Anki renderer. Settings labels for popup pause and the dictionary deck no longer name Yomitan, and the backend selector sits with the other dictionary settings.
- Hachidori scans, dictionary counts, and settings reads wait for the dictionary engine to finish loading or importing instead of caching empty results. - Hachidori scans, dictionary counts, and settings reads wait for the dictionary engine to finish loading or importing instead of caching empty results.
- Character dictionary imports report completion as soon as the archive is read instead of waiting out the temporary server's keep-alive timeout.
- First-run setup can link an external Hachidori dictionary host in an app, browser, or Docker container, verify its library, and unlink back to local dictionaries. The optional host controls are collapsed by default and explain which apps or containers must stay running. Unresponsive connection checks time out so setup remains usable. Anki mining, media enrichment, and custom toolbar buttons stay in SubMiner, including when editing buttons while linked. - First-run setup can link an external Hachidori dictionary host in an app, browser, or Docker container, verify its library, and unlink back to local dictionaries. The optional host controls are collapsed by default and explain which apps or containers must stay running. Unresponsive connection checks time out so setup remains usable. Anki mining, media enrichment, and custom toolbar buttons stay in SubMiner, including when editing buttons while linked.
- Hachidori frequency highlighting reuses dictionary-entry ranks and fills missing ranks through its existing API. Entries without a matching definition may remain unranked. The bundled integration is maintained in a pinned fork submodule using upstream HoshiDicts and WASM binaries. - Hachidori frequency highlighting reuses dictionary-entry ranks and fills missing ranks through its existing API. Entries without a matching definition may remain unranked. The bundled integration is maintained in a pinned fork submodule using upstream HoshiDicts and WASM binaries.
+1 -1
View File
@@ -24,7 +24,7 @@ The bundled Yomitan is separate from any Yomitan in your browser. It has its own
Hachidori is an alternative lookup backend. Set `dictionaryBackend` to `"hachidori"` in settings or `config.jsonc`, then restart SubMiner. Set it back to `"yomitan"` and restart to switch back. Hachidori is an alternative lookup backend. Set `dictionaryBackend` to `"hachidori"` in settings or `config.jsonc`, then restart SubMiner. Set it back to `"yomitan"` and restart to switch back.
Open Hachidori settings with `Alt+Shift+Y`, the tray's **Open Hachidori Settings**, or `subminer app --hachidori`. Import dictionary ZIPs or use Hachidori's recommended dictionary installer, then set up its Anki template (SubMiner [fills in what it can](/anki-integration#hachidori-settings-from-subminer)). Yomitan and Hachidori keep separate dictionaries and settings. Yomitan profiles, custom Handlebars templates, and `yomitan.externalProfilePath` do not carry over. Open Hachidori settings with `Alt+Shift+Y`, the tray's **Open Hachidori Settings**, or `subminer app --hachidori`. Import dictionary ZIPs or use Hachidori's recommended dictionary installer, then set up its Anki template (SubMiner [fills in what it can](/anki-integration#hachidori-settings-from-subminer)). Yomitan and Hachidori keep separate dictionaries and settings. Yomitan profiles, custom Handlebars templates, and `yomitan.externalProfilePath` do not carry over. Subtitle annotations and lookups wait while Hachidori imports a dictionary, so large imports pause them until the import finishes.
Hachidori uses SubMiner's subtitle scanning, popup pause, controller commands, character dictionaries, and Anki media. Keep the [Anki proxy](/anki-integration#proxy-mode-setup-yomitan-texthooker) on for screenshots and sentence audio. Hachidori's own screen recorder and screenshot capture are off inside SubMiner. `startupWarmups.yomitanExtension` and `subtitleStyle.autoPauseVideoOnYomitanPopup` apply to whichever backend is selected. Hachidori uses SubMiner's subtitle scanning, popup pause, controller commands, character dictionaries, and Anki media. Keep the [Anki proxy](/anki-integration#proxy-mode-setup-yomitan-texthooker) on for screenshots and sentence audio. Hachidori's own screen recorder and screenshot capture are off inside SubMiner. `startupWarmups.yomitanExtension` and `subtitleStyle.autoPauseVideoOnYomitanPopup` apply to whichever backend is selected.
+1 -1
View File
@@ -66,7 +66,7 @@ bun run docs:build
Content checks run inside the electron-builder afterPack hook. See the Content checks run inside the electron-builder afterPack hook. See the
[release guide](../RELEASING.md#package-contents-checks) for the [release guide](../RELEASING.md#package-contents-checks) for the
installed-app verification checklist. installed-app verification checklist.
- Dictionary backend windows: after a full build, run `xvfb-run -a bun run test:dictionary:electron` on headless Linux. It uses temporary profiles to check both named settings commands, backend session isolation, the overlay's external-link bridge, and Hachidori's native dictionary parser. - Dictionary backend windows: after a full build, run `xvfb-run -a bun run test:dictionary:electron` on headless Linux (CI runs it in the Linux E2E job). It uses temporary profiles to check both named settings commands, backend session isolation, the overlay's external-link bridge, and Hachidori's native dictionary parser.
- Coverage for the maintained source lane: `bun run test:coverage:src` - Coverage for the maintained source lane: `bun run test:coverage:src`
- Deep/local full gate: default handoff gate above - Deep/local full gate: default handoff gate above
+1 -1
View File
@@ -335,7 +335,7 @@ function humanizePath(path: string): string {
} }
function categoryAndSection(path: string): { category: ConfigSettingsCategory; section: string } { function categoryAndSection(path: string): { category: ConfigSettingsCategory; section: string } {
if (path === 'dictionaryBackend' || path.startsWith('hachidori.')) { if (path === 'dictionaryBackend') {
return { category: 'integrations', section: 'Dictionary Lookup' }; return { category: 'integrations', section: 'Dictionary Lookup' };
} }
if ( if (
@@ -872,6 +872,9 @@ async function serveDictionaryZipOnce<T>(
} }
return await callback(`http://127.0.0.1:${address.port}${requestPath}`); return await callback(`http://127.0.0.1:${address.port}${requestPath}`);
} finally { } finally {
// The extension's fetch keeps its socket open; close() alone would wait out
// Node's keep-alive timeout before the import reports completion.
server.closeAllConnections();
await new Promise<void>((resolve) => server.close(() => resolve())); await new Promise<void>((resolve) => server.close(() => resolve()));
} }
} }
+1 -1
View File
@@ -1,4 +1,3 @@
import { requestHachidoriSharing } from './core/services/tokenizer/yomitan-parser-runtime';
/* /*
SubMiner - All-in-one sentence mining overlay SubMiner - All-in-one sentence mining overlay
Copyright (C) 2026 sudacode Copyright (C) 2026 sudacode
@@ -38,6 +37,7 @@ import {
createHachidoriExtensionRuntime, createHachidoriExtensionRuntime,
getHachidoriSession, getHachidoriSession,
} from './core/services/hachidori-extension'; } from './core/services/hachidori-extension';
import { requestHachidoriSharing } from './core/services/tokenizer/yomitan-parser-runtime';
import { import {
DICTIONARY_EXTERNAL_LINK_CHANNEL, DICTIONARY_EXTERNAL_LINK_CHANNEL,
parseDictionaryExternalUrl, parseDictionaryExternalUrl,