fix: Kiku field grouping, frequency particles, sidebar media, Yomitan po

- Fix Kiku duplicate-card field grouping: local dupes trigger manual modal or auto-merge; fix modal-open ack race; fix merged field ordering, sentence-audio, furigana, and tag semantics
- Fix frequency annotations for single-token Yomitan compounds with internal particles (e.g. 目の前); keep pure grammar/kana spans unannotated
- Fix subtitle sidebar mining: use audio/image from clicked sidebar line, not current primary line
- Add `subtitleStyle.primaryVisibleOnYomitanPopup` to keep hover-mode primary subtitle visible while Yomitan popup is open
- Normalize trailing commas in config.example.jsonc
This commit is contained in:
2026-05-27 00:12:21 -07:00
parent 5b44981688
commit eb04ea97b1
49 changed files with 1711 additions and 662 deletions
@@ -4,12 +4,14 @@ import { getPreferredWordValueFromExtractedFields } from '../anki-field-config';
export interface FieldGroupingWorkflowNoteInfo {
noteId: number;
fields: Record<string, { value: string }>;
tags?: string[];
}
export interface FieldGroupingWorkflowDeps {
client: {
notesInfo(noteIds: number[]): Promise<unknown>;
updateNoteFields(noteId: number, fields: Record<string, string>): Promise<void>;
addTags(noteIds: number[], tags: string[]): Promise<void>;
deleteNotes(noteIds: number[]): Promise<void>;
};
getConfig: () => {
@@ -156,6 +158,11 @@ export class FieldGroupingWorkflow {
await this.deps.addConfiguredTagsToNote(keepNoteId);
}
const tagsToAdd = this.getMergeTagsToAdd(keepNoteInfo, deleteNoteInfo);
if (tagsToAdd.length > 0) {
await this.deps.client.addTags([keepNoteId], tagsToAdd);
}
if (deleteDuplicate) {
await this.deps.client.deleteNotes([deleteNoteId]);
this.deps.removeTrackedNoteId(deleteNoteId);
@@ -200,6 +207,24 @@ export class FieldGroupingWorkflow {
return getPreferredWordValueFromExtractedFields(fields, this.deps.getConfig());
}
private getMergeTagsToAdd(
keepNoteInfo: FieldGroupingWorkflowNoteInfo,
deleteNoteInfo: FieldGroupingWorkflowNoteInfo,
): string[] {
const targetTags = new Set((keepNoteInfo.tags ?? []).map((tag) => tag.trim()).filter(Boolean));
const unwantedSourceTags = new Set(['leech', 'marked', 'potential_leech']);
const tagsToAdd: string[] = [];
for (const rawTag of deleteNoteInfo.tags ?? []) {
const tag = rawTag.trim();
if (!tag || targetTags.has(tag) || unwantedSourceTags.has(tag)) continue;
targetTags.add(tag);
tagsToAdd.push(tag);
}
return tagsToAdd;
}
private async resolveFieldGroupingCallback(): Promise<
| ((data: {
original: KikuDuplicateCardInfo;