rename config window to settings and update CLI entry points

- Replace `--config`/`subminer config` (no action) with `--settings`/`subminer settings`
- `subminer config` now requires an explicit action (`path` or `show`)
- `--settings` previously opened Yomitan; replaced by `--yomitan`
- Linux tray update installs AppImage via electron-updater instead of manual flow
- macOS update dialog activation and curl-fetch routing fixes
- Delete stale compiled artifacts (main.js, app-updater.js)
This commit is contained in:
2026-05-20 20:31:02 -07:00
parent fcd6511aa1
commit 166015897d
63 changed files with 500 additions and 5281 deletions
+2 -2
View File
@@ -6,8 +6,8 @@ export function runAppPassthroughCommand(context: LauncherCommandContext): boole
if (!appPath) {
return false;
}
if (args.configSettings) {
runAppCommandWithInherit(appPath, ['--config']);
if (args.settings) {
runAppCommandWithInherit(appPath, ['--settings']);
return true;
}
if (!args.appPassthrough) {
+1 -1
View File
@@ -53,7 +53,7 @@ function createContext(): LauncherCommandContext {
doctor: false,
doctorRefreshKnownWords: false,
version: false,
configSettings: false,
settings: false,
configPath: false,
configShow: false,
mpvIdle: false,
+44 -4
View File
@@ -124,6 +124,7 @@ test('applyInvocationsToArgs maps config and jellyfin invocation state', () => {
action: 'show',
logLevel: 'warn',
},
settingsInvocation: null,
mpvInvocation: null,
appInvocation: null,
dictionaryTriggered: false,
@@ -159,13 +160,14 @@ test('applyInvocationsToArgs maps config and jellyfin invocation state', () => {
assert.equal(parsed.logLevel, 'warn');
});
test('applyInvocationsToArgs maps bare config invocation to settings window', () => {
test('applyInvocationsToArgs maps settings invocation to settings window', () => {
const parsed = createDefaultArgs({});
applyInvocationsToArgs(parsed, {
jellyfinInvocation: null,
configInvocation: {
action: undefined,
configInvocation: null,
settingsInvocation: {
logLevel: undefined,
},
mpvInvocation: null,
appInvocation: null,
@@ -190,16 +192,54 @@ test('applyInvocationsToArgs maps bare config invocation to settings window', ()
texthookerOpenBrowser: false,
});
assert.equal(parsed.configSettings, true);
assert.equal(parsed.settings, true);
assert.equal(parsed.configPath, false);
});
test('applyInvocationsToArgs fails when config invocation has no action', () => {
const parsed = createDefaultArgs({});
const error = withProcessExitIntercept(() => {
applyInvocationsToArgs(parsed, {
jellyfinInvocation: null,
configInvocation: {
action: undefined,
},
settingsInvocation: null,
mpvInvocation: null,
appInvocation: null,
dictionaryTriggered: false,
dictionaryTarget: null,
dictionaryLogLevel: null,
dictionaryCandidates: false,
dictionarySelect: false,
dictionaryAnilistId: null,
statsTriggered: false,
statsBackground: false,
statsStop: false,
statsCleanup: false,
statsCleanupVocab: false,
statsCleanupLifetime: false,
statsLogLevel: null,
doctorTriggered: false,
doctorLogLevel: null,
doctorRefreshKnownWords: false,
texthookerTriggered: false,
texthookerLogLevel: null,
texthookerOpenBrowser: false,
});
});
assert.equal(error.code, 1);
});
test('applyInvocationsToArgs maps texthooker browser-open request', () => {
const parsed = createDefaultArgs({});
applyInvocationsToArgs(parsed, {
jellyfinInvocation: null,
configInvocation: null,
settingsInvocation: null,
mpvInvocation: null,
appInvocation: null,
dictionaryTriggered: false,
+11 -5
View File
@@ -158,7 +158,7 @@ export function createDefaultArgs(
doctorRefreshKnownWords: false,
version: false,
update: false,
configSettings: false,
settings: false,
configPath: false,
configShow: false,
mpvIdle: false,
@@ -222,7 +222,7 @@ export function applyRootOptionsToArgs(
if (options.rofi === true) parsed.useRofi = true;
if (options.update === true) parsed.update = true;
if (options.version === true) parsed.version = true;
if (options.config === true) parsed.configSettings = true;
if (options.settings === true) parsed.settings = true;
if (options.startOverlay === true) parsed.autoStartOverlay = true;
if (options.texthooker === false) parsed.useTexthooker = false;
if (typeof options.args === 'string') parsed.mpvArgs = options.args;
@@ -311,10 +311,16 @@ export function applyInvocationsToArgs(parsed: Args, invocations: CliInvocations
parsed.logLevel = parseLogLevel(invocations.configInvocation.logLevel);
}
const action = (invocations.configInvocation.action || '').toLowerCase();
if (!action) parsed.configSettings = true;
else if (action === 'path') parsed.configPath = true;
if (action === 'path') parsed.configPath = true;
else if (action === 'show') parsed.configShow = true;
else fail(`Unknown config action: ${invocations.configInvocation.action}`);
else fail(`Unknown config action: ${invocations.configInvocation.action || '(none)'}. Expected path or show.`);
}
if (invocations.settingsInvocation) {
if (invocations.settingsInvocation.logLevel) {
parsed.logLevel = parseLogLevel(invocations.settingsInvocation.logLevel);
}
parsed.settings = true;
}
if (invocations.mpvInvocation) {
+16 -2
View File
@@ -22,6 +22,7 @@ export interface CommandActionInvocation {
export interface CliInvocations {
jellyfinInvocation: JellyfinInvocation | null;
configInvocation: CommandActionInvocation | null;
settingsInvocation: CommandActionInvocation | null;
mpvInvocation: CommandActionInvocation | null;
appInvocation: { appArgs: string[] } | null;
dictionaryTriggered: boolean;
@@ -58,7 +59,7 @@ function applyRootOptions(program: Command): void {
.option('--start', 'Explicitly start overlay')
.option('--log-level <level>', 'Log level')
.option('-v, --version', 'Show SubMiner version')
.option('--config', 'Open configuration window')
.option('--settings', 'Open settings window')
.option('-u, --update', 'Check for updates')
.option('-R, --rofi', 'Use rofi picker')
.option('-S, --start-overlay', 'Auto-start overlay')
@@ -88,6 +89,7 @@ function getTopLevelCommand(argv: string[]): { name: string; index: number } | n
'jf',
'doctor',
'config',
'settings',
'mpv',
'dictionary',
'dict',
@@ -138,6 +140,7 @@ export function parseCliPrograms(
} {
let jellyfinInvocation: JellyfinInvocation | null = null;
let configInvocation: CommandActionInvocation | null = null;
let settingsInvocation: CommandActionInvocation | null = null;
let mpvInvocation: CommandActionInvocation | null = null;
let appInvocation: { appArgs: string[] } | null = null;
let dictionaryTriggered = false;
@@ -293,7 +296,7 @@ export function parseCliPrograms(
commandProgram
.command('config')
.description('Config helpers')
.description('Config file helpers (path|show)')
.argument('[action]', 'path|show')
.option('--log-level <level>', 'Log level')
.action((action: string | undefined, options: Record<string, unknown>) => {
@@ -303,6 +306,16 @@ export function parseCliPrograms(
};
});
commandProgram
.command('settings')
.description('Open SubMiner settings window')
.option('--log-level <level>', 'Log level')
.action((options: Record<string, unknown>) => {
settingsInvocation = {
logLevel: typeof options.logLevel === 'string' ? options.logLevel : undefined,
};
});
commandProgram
.command('mpv')
.description('MPV helpers')
@@ -356,6 +369,7 @@ export function parseCliPrograms(
invocations: {
jellyfinInvocation,
configInvocation,
settingsInvocation,
mpvInvocation,
appInvocation,
dictionaryTriggered,
+8 -8
View File
@@ -232,7 +232,7 @@ test('doctor refresh-known-words forwards app refresh command without requiring
});
});
test('launcher config option forwards app configuration window command', () => {
test('launcher settings option forwards app settings window command', () => {
withTempDir((root) => {
const homeDir = path.join(root, 'home');
const xdgConfigHome = path.join(root, 'xdg');
@@ -249,14 +249,14 @@ test('launcher config option forwards app configuration window command', () => {
SUBMINER_APPIMAGE_PATH: appPath,
SUBMINER_TEST_CAPTURE: capturePath,
};
const result = runLauncher(['--config'], env);
const result = runLauncher(['--settings'], env);
assert.equal(result.status, 0);
assert.equal(fs.readFileSync(capturePath, 'utf8'), '--config\n');
assert.equal(fs.readFileSync(capturePath, 'utf8'), '--settings\n');
});
});
test('launcher config command forwards app configuration window command', () => {
test('launcher settings command forwards app settings window command', () => {
withTempDir((root) => {
const homeDir = path.join(root, 'home');
const xdgConfigHome = path.join(root, 'xdg');
@@ -273,14 +273,14 @@ test('launcher config command forwards app configuration window command', () =>
SUBMINER_APPIMAGE_PATH: appPath,
SUBMINER_TEST_CAPTURE: capturePath,
};
const result = runLauncher(['config'], env);
const result = runLauncher(['settings'], env);
assert.equal(result.status, 0);
assert.equal(fs.readFileSync(capturePath, 'utf8'), '--config\n');
assert.equal(fs.readFileSync(capturePath, 'utf8'), '--settings\n');
});
});
test('launcher config command suppresses known Electron macOS menu diagnostics', () => {
test('launcher settings command suppresses known Electron macOS menu diagnostics', () => {
withTempDir((root) => {
const homeDir = path.join(root, 'home');
const xdgConfigHome = path.join(root, 'xdg');
@@ -301,7 +301,7 @@ test('launcher config command suppresses known Electron macOS menu diagnostics',
...makeTestEnv(homeDir, xdgConfigHome),
SUBMINER_APPIMAGE_PATH: appPath,
};
const result = runLauncher(['config'], env);
const result = runLauncher(['settings'], env);
assert.equal(result.status, 0);
assert.equal(result.stderr, 'real stderr line\n');
+1 -1
View File
@@ -569,7 +569,7 @@ function makeArgs(overrides: Partial<Args> = {}): Args {
doctor: false,
doctorRefreshKnownWords: false,
version: false,
configSettings: false,
settings: false,
configPath: false,
configShow: false,
mpvIdle: false,
+15 -7
View File
@@ -57,10 +57,10 @@ test('parseArgs captures mpv args string', () => {
assert.equal(parsed.mpvArgs, '--pause=yes --title="movie night"');
});
test('parseArgs maps root config window option', () => {
const parsed = parseArgs(['--config'], 'subminer', {});
test('parseArgs maps root settings window option', () => {
const parsed = parseArgs(['--settings'], 'subminer', {});
assert.equal(parsed.configSettings, true);
assert.equal(parsed.settings, true);
});
test('parseArgs maps root update flags without conflicting with jellyfin username', () => {
@@ -107,10 +107,10 @@ test('parseArgs maps config show action', () => {
assert.equal(parsed.configPath, false);
});
test('parseArgs maps bare config command to settings window', () => {
const parsed = parseArgs(['config'], 'subminer', {});
test('parseArgs maps settings command to settings window', () => {
const parsed = parseArgs(['settings'], 'subminer', {});
assert.equal(parsed.configSettings, true);
assert.equal(parsed.settings, true);
assert.equal(parsed.configPath, false);
assert.equal(parsed.configShow, false);
});
@@ -119,7 +119,7 @@ test('parseArgs maps config path action to config path output', () => {
const parsed = parseArgs(['config', 'path'], 'subminer', {});
assert.equal(parsed.configPath, true);
assert.equal(parsed.configSettings, false);
assert.equal(parsed.settings, false);
});
test('parseArgs rejects removed config open and launch actions', () => {
@@ -134,6 +134,14 @@ test('parseArgs rejects removed config open and launch actions', () => {
assert.equal(exit.code, 1);
});
test('parseArgs requires an explicit action for the config subcommand', () => {
const exit = withProcessExitIntercept(() => {
parseArgs(['config'], 'subminer', {});
});
assert.equal(exit.code, 1);
});
test('parseArgs maps mpv idle action', () => {
const parsed = parseArgs(['mpv', 'idle'], 'subminer', {});
+1 -1
View File
@@ -133,7 +133,7 @@ export interface Args {
doctorRefreshKnownWords: boolean;
version: boolean;
update?: boolean;
configSettings: boolean;
settings: boolean;
configPath: boolean;
configShow: boolean;
mpvIdle: boolean;