fix: disable macOS mpv menu shortcuts, buffer latest subtitle IPC state

- Pass --macos-menu-shortcuts=no on Darwin so SubMiner bindings reach mpv
- Replace queued IPC listener with latest-value variant for subtitle channels
- Skip JSONC line/block comments in duplicate-key offset helpers
- Preserve configured Anki note model name in selectPreferredNoteFieldModelName
- Guard known-words deck rename against collision; add chooseKnownWordsDeckRenameValue
- Apply asCssColor on hover token CSS compat reads
This commit is contained in:
2026-05-18 16:03:16 -07:00
parent 193b3136f2
commit 553117356d
12 changed files with 208 additions and 35 deletions
+17 -9
View File
@@ -2,17 +2,17 @@ import test from 'node:test';
import assert from 'node:assert/strict';
import * as ankiControls from './settings-anki-controls';
test('note field model preference ignores configured sentence-card model before Kiku fallback', () => {
test('note field model preference keeps configured sentence-card model before Kiku fallback', () => {
assert.equal(
ankiControls.selectPreferredNoteFieldModelName(['Lapis Morph', 'Kiku'], 'Lapis Morph'),
'Kiku',
'Lapis Morph',
);
});
test('note field model preference ignores configured sentence-card model case-insensitively', () => {
test('note field model preference keeps configured sentence-card model case-insensitively', () => {
assert.equal(
ankiControls.selectPreferredNoteFieldModelName(['Lapis Morph', 'Kiku'], 'lapis morph'),
'Kiku',
'Lapis Morph',
);
});
@@ -29,15 +29,23 @@ test('note field model preference does not treat partial Kiku matches as Kiku',
});
test('note field model preference does not treat partial Lapis matches as Lapis', () => {
assert.equal(
ankiControls.selectPreferredNoteFieldModelName(['Mining', 'Lapis Morph'], ''),
'',
);
assert.equal(ankiControls.selectPreferredNoteFieldModelName(['Mining', 'Lapis Morph'], ''), '');
});
test('note field model preference stays blank when no Kiku or Lapis note type exists', () => {
test('note field model preference stays blank when no current Kiku or Lapis note type exists', () => {
assert.equal(
ankiControls.selectPreferredNoteFieldModelName(['Basic', 'Mining'], 'Lapis Morph'),
'',
);
});
test('known word deck rename selection keeps current deck on collision', () => {
assert.equal(
ankiControls.chooseKnownWordsDeckRenameValue(
{ Mining: ['Word'], Core: ['Expression'] },
'Core',
'Mining',
),
'Core',
);
});
+29 -2
View File
@@ -55,7 +55,13 @@ export function selectPreferredNoteFieldModelName(
modelNames: readonly string[],
currentModelName = '',
): string {
void currentModelName;
const normalizedCurrent = currentModelName.trim().toLowerCase();
if (normalizedCurrent) {
const current = modelNames.find((name) => name.trim().toLowerCase() === normalizedCurrent);
if (current) {
return current;
}
}
const exactKiku = modelNames.find((name) => name.trim().toLowerCase() === 'kiku');
if (exactKiku) {
@@ -70,6 +76,20 @@ export function selectPreferredNoteFieldModelName(
return '';
}
export function chooseKnownWordsDeckRenameValue(
decks: Record<string, string[]>,
currentDeckName: string,
nextDeckName: string,
): string {
if (
nextDeckName !== currentDeckName &&
Object.prototype.hasOwnProperty.call(decks, nextDeckName)
) {
return currentDeckName;
}
return nextDeckName;
}
function normalizeStringArray(value: unknown): string[] {
return Array.isArray(value)
? value.filter((entry): entry is string => typeof entry === 'string' && entry.length > 0)
@@ -491,16 +511,23 @@ export function renderKnownWordsDecksInput(
void loadAnkiDeckFieldNames(deckName, draftUrl);
}
const row = createElement('div', 'deck-field-row');
const usedDeckNames = new Set(Object.keys(currentDecks));
const deckSelect = createElement('select', 'config-input') as HTMLSelectElement;
for (const candidateDeck of uniqueSorted([...deckNames, deckName])) {
if (candidateDeck !== deckName && usedDeckNames.has(candidateDeck)) continue;
addOption(deckSelect, candidateDeck);
}
deckSelect.value = deckName;
deckSelect.addEventListener('change', () => {
const nextDecks = normalizeKnownWordsDecks(context.valueForField(field));
const nextDeckName = chooseKnownWordsDeckRenameValue(nextDecks, deckName, deckSelect.value);
if (nextDeckName !== deckSelect.value) {
deckSelect.value = nextDeckName;
return;
}
const fields = nextDecks[deckName] ?? [];
delete nextDecks[deckName];
nextDecks[deckSelect.value] = fields;
nextDecks[nextDeckName] = fields;
setKnownWordsDecks(context, field.configPath, nextDecks);
requestRender();
});