mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-05-26 00:55:16 -07:00
feat(config): reorganize settings window and move annotation colors to subtitleStyle
- Reorganize Configuration window into Appearance, Behavior, Anki, Input, and Integration sections - Add AnkiConnect-backed deck, note-type, and field pickers in the Anki section - Add click-to-learn keybinding controls - Move known-word and N+1 highlight colors to subtitleStyle.knownWordColor / subtitleStyle.nPlusOneColor; legacy ankiConnect.knownWords.color and ankiConnect.nPlusOne.nPlusOne keys still accepted with deprecation warnings - Add deckNames, modelNames, modelFieldNames, and fieldNamesForDeck methods to AnkiConnectClient - Mark discordPresence.presenceStyle as an enum in the config registry
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
import { DEFAULT_KEYBINDINGS } from '../config/definitions/shared';
|
||||
import type { ConfigSettingsField } from '../types/settings';
|
||||
import {
|
||||
buildMpvKeybindingConfigValue,
|
||||
createMpvKeybindingRows,
|
||||
keyboardEventToConfigKey,
|
||||
parseMpvCommandText,
|
||||
type KeyInputMode,
|
||||
type MpvKeybindingRow,
|
||||
} from './key-input';
|
||||
import type { SettingsControlContext } from './settings-control-context';
|
||||
import { createElement } from './settings-control-dom';
|
||||
|
||||
let activeKeyLearningStop: (() => void) | null = null;
|
||||
|
||||
function startKeyLearning(
|
||||
button: HTMLButtonElement,
|
||||
mode: KeyInputMode,
|
||||
onValue: (value: string) => void,
|
||||
): void {
|
||||
activeKeyLearningStop?.();
|
||||
const previousText = button.textContent ?? '';
|
||||
button.textContent = 'Press Keys...';
|
||||
button.classList.add('learning');
|
||||
let onKeyDown: (event: KeyboardEvent) => void;
|
||||
let onBlur: () => void;
|
||||
let onMouseDown: (event: MouseEvent) => void;
|
||||
|
||||
const stop = (): void => {
|
||||
window.removeEventListener('keydown', onKeyDown, true);
|
||||
window.removeEventListener('blur', onBlur, true);
|
||||
window.removeEventListener('mousedown', onMouseDown, true);
|
||||
button.classList.remove('learning');
|
||||
if (button.textContent === 'Press Keys...') {
|
||||
button.textContent = previousText;
|
||||
}
|
||||
if (activeKeyLearningStop === stop) {
|
||||
activeKeyLearningStop = null;
|
||||
}
|
||||
};
|
||||
|
||||
onKeyDown = (event: KeyboardEvent): void => {
|
||||
if (event.key === 'Escape') {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const next = keyboardEventToConfigKey(event, mode);
|
||||
if (!next) return;
|
||||
stop();
|
||||
onValue(next);
|
||||
};
|
||||
onBlur = (): void => stop();
|
||||
onMouseDown = (event: MouseEvent): void => {
|
||||
if (event.target !== button) {
|
||||
stop();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', onKeyDown, true);
|
||||
window.addEventListener('blur', onBlur, true);
|
||||
window.addEventListener('mousedown', onMouseDown, true);
|
||||
activeKeyLearningStop = stop;
|
||||
}
|
||||
|
||||
function renderKeyLearnButton(
|
||||
value: string,
|
||||
mode: KeyInputMode,
|
||||
onValue: (value: string) => void,
|
||||
): HTMLButtonElement {
|
||||
const button = createElement('button', 'key-learn-button') as HTMLButtonElement;
|
||||
button.type = 'button';
|
||||
button.textContent = value || 'Unset';
|
||||
button.addEventListener('click', () =>
|
||||
startKeyLearning(button, mode, (next) => {
|
||||
button.textContent = next;
|
||||
onValue(next);
|
||||
}),
|
||||
);
|
||||
return button;
|
||||
}
|
||||
|
||||
export function renderKeyboardInput(
|
||||
context: SettingsControlContext,
|
||||
field: ConfigSettingsField,
|
||||
mode: KeyInputMode,
|
||||
): HTMLElement {
|
||||
const value = context.valueForField(field);
|
||||
return renderKeyLearnButton(typeof value === 'string' ? value : '', mode, (next) => {
|
||||
context.updateDraft(field.configPath, next);
|
||||
});
|
||||
}
|
||||
|
||||
function applyMpvRows(
|
||||
context: SettingsControlContext,
|
||||
field: ConfigSettingsField,
|
||||
rows: MpvKeybindingRow[],
|
||||
): void {
|
||||
context.updateDraft(field.configPath, buildMpvKeybindingConfigValue(DEFAULT_KEYBINDINGS, rows));
|
||||
}
|
||||
|
||||
export function renderMpvKeybindingsInput(
|
||||
context: SettingsControlContext,
|
||||
field: ConfigSettingsField,
|
||||
): HTMLElement {
|
||||
const rows = createMpvKeybindingRows(DEFAULT_KEYBINDINGS, context.valueForField(field));
|
||||
const container = createElement('div', 'keybinding-editor');
|
||||
|
||||
for (const row of rows) {
|
||||
const item = createElement('div', 'keybinding-row');
|
||||
const keyButton = renderKeyLearnButton(row.key, 'dom-code', (next) => {
|
||||
row.key = next;
|
||||
applyMpvRows(context, field, rows);
|
||||
});
|
||||
const command = createElement('input', 'config-input mono-input') as HTMLInputElement;
|
||||
command.type = 'text';
|
||||
command.value = row.commandText;
|
||||
command.placeholder = '["cycle","pause"]';
|
||||
command.addEventListener('input', () => {
|
||||
const parsed = parseMpvCommandText(command.value);
|
||||
if (parsed === undefined) {
|
||||
command.classList.add('invalid');
|
||||
context.setFieldError(field.configPath, 'Invalid MPV command JSON');
|
||||
return;
|
||||
}
|
||||
command.classList.remove('invalid');
|
||||
context.setFieldError(field.configPath, null);
|
||||
row.command = parsed;
|
||||
row.commandText = command.value;
|
||||
applyMpvRows(context, field, rows);
|
||||
});
|
||||
item.append(keyButton, command);
|
||||
container.append(item);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
Reference in New Issue
Block a user