mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-05-27 00:55:16 -07:00
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:
@@ -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',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user