feat(dictionary): add Hachidori backend support

- Add backend selection, setup gating, Anki integration, and external host support
- Add launcher flags, documentation, packaging, and focused tests
- Open on-demand overlay modals on the first attempt
This commit is contained in:
2026-09-22 00:21:19 -07:00
parent 1508863dbb
commit d9fdc7ef6d
446 changed files with 109060 additions and 244 deletions
+34 -4
View File
@@ -224,6 +224,31 @@ export class AnkiConnectProxyServer {
typeof requestJson.action === 'string'
? requestJson.action
: String(requestJson.action ?? '');
if (action === 'updateNoteFields') {
const params = requestJson.params;
if (
!params ||
typeof params !== 'object' ||
!('subminerEnrich' in params) ||
params.subminerEnrich !== true
)
return;
const note = 'note' in params ? params.note : null;
if (!note || typeof note !== 'object' || !('id' in note)) return;
const response = this.tryParseJsonValue(responseBody);
// AnkiConnect confirms updates with {result:null,error:null}; failures must never enrich.
if (
!response ||
typeof response !== 'object' ||
!('error' in response) ||
response.error !== null ||
!('result' in response) ||
response.result !== null
)
return;
this.enqueueNotes(this.collectSingleResultId(note.id), false);
return;
}
if (action !== 'addNote' && action !== 'addNotes' && action !== 'multi') {
return;
}
@@ -293,7 +318,7 @@ export class AnkiConnectProxyServer {
typeof requestJson.action === 'string'
? requestJson.action
: String(requestJson.action ?? '');
if (action !== 'addNote') {
if (action !== 'addNote' && action !== 'updateNoteFields') {
return requestJson;
}
@@ -301,12 +326,17 @@ export class AnkiConnectProxyServer {
requestJson.params && typeof requestJson.params === 'object'
? (requestJson.params as Record<string, unknown>)
: null;
if (!params || !Object.prototype.hasOwnProperty.call(params, 'subminerDuplicateNoteIds')) {
if (
!params ||
(!Object.prototype.hasOwnProperty.call(params, 'subminerDuplicateNoteIds') &&
!Object.prototype.hasOwnProperty.call(params, 'subminerEnrich'))
) {
return requestJson;
}
const nextParams = { ...params };
delete nextParams.subminerDuplicateNoteIds;
delete nextParams.subminerEnrich;
return {
...requestJson,
params: nextParams,
@@ -455,7 +485,7 @@ export class AnkiConnectProxyServer {
});
}
private enqueueNotes(noteIds: number[]): void {
private enqueueNotes(noteIds: number[], recordAdded = true): void {
let enqueuedCount = 0;
const acceptedIds: number[] = [];
for (const noteId of noteIds) {
@@ -472,7 +502,7 @@ export class AnkiConnectProxyServer {
return;
}
this.deps.recordCardsAdded?.(enqueuedCount, acceptedIds);
if (recordAdded) this.deps.recordCardsAdded?.(enqueuedCount, acceptedIds);
this.deps.logInfo(`[anki-proxy] Enqueued ${enqueuedCount} note(s) for enrichment`);
this.processQueue();
}