mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-05-26 00:55:16 -07:00
a4314ab5de
- 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
139 lines
4.2 KiB
TypeScript
139 lines
4.2 KiB
TypeScript
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;
|
|
}
|