mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-05-04 00:41:33 -07:00
feat: open texthooker from cli and tray
This commit is contained in:
@@ -124,9 +124,12 @@ test('youtube playback does not use generic overlay-runtime bootstrap classifica
|
||||
|
||||
test('standalone texthooker classification excludes integrated start flow', () => {
|
||||
const standalone = parseArgs(['--texthooker']);
|
||||
const standaloneOpenBrowser = parseArgs(['--texthooker', '--open-browser']);
|
||||
const integrated = parseArgs(['--start', '--texthooker']);
|
||||
|
||||
assert.equal(isStandaloneTexthookerCommand(standalone), true);
|
||||
assert.equal(standaloneOpenBrowser.texthookerOpenBrowser, true);
|
||||
assert.equal(isStandaloneTexthookerCommand(standaloneOpenBrowser), true);
|
||||
assert.equal(isStandaloneTexthookerCommand(integrated), false);
|
||||
});
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ export interface CliArgs {
|
||||
jellyfinRemoteAnnounce: boolean;
|
||||
jellyfinPreviewAuth: boolean;
|
||||
texthooker: boolean;
|
||||
texthookerOpenBrowser: boolean;
|
||||
help: boolean;
|
||||
autoStartOverlay: boolean;
|
||||
generateConfig: boolean;
|
||||
@@ -164,6 +165,7 @@ export function parseArgs(argv: string[]): CliArgs {
|
||||
jellyfinRemoteAnnounce: false,
|
||||
jellyfinPreviewAuth: false,
|
||||
texthooker: false,
|
||||
texthookerOpenBrowser: false,
|
||||
help: false,
|
||||
autoStartOverlay: false,
|
||||
generateConfig: false,
|
||||
@@ -327,6 +329,7 @@ export function parseArgs(argv: string[]): CliArgs {
|
||||
else if (arg === '--jellyfin-remote-announce') args.jellyfinRemoteAnnounce = true;
|
||||
else if (arg === '--jellyfin-preview-auth') args.jellyfinPreviewAuth = true;
|
||||
else if (arg === '--texthooker') args.texthooker = true;
|
||||
else if (arg === '--open-browser') args.texthookerOpenBrowser = true;
|
||||
else if (arg === '--auto-start-overlay') args.autoStartOverlay = true;
|
||||
else if (arg === '--generate-config') args.generateConfig = true;
|
||||
else if (arg === '--backup-overwrite') args.backupOverwrite = true;
|
||||
|
||||
@@ -19,6 +19,7 @@ test('printHelp includes configured texthooker port', () => {
|
||||
assert.match(output, /default: 7777/);
|
||||
assert.match(output, /--launch-mpv.*Launch mpv with SubMiner defaults and exit/);
|
||||
assert.match(output, /--stats\s+Open the stats dashboard in your browser/);
|
||||
assert.match(output, /--open-browser\s+Open texthooker in your default browser/);
|
||||
assert.doesNotMatch(output, /--refresh-known-words/);
|
||||
assert.match(output, /--setup\s+Open first-run setup window/);
|
||||
assert.match(output, /--anilist-status/);
|
||||
|
||||
@@ -16,6 +16,7 @@ ${B}Session${R}
|
||||
--stop Stop the running instance
|
||||
--stats Open the stats dashboard in your browser
|
||||
--texthooker Start texthooker server only ${D}(no overlay)${R}
|
||||
--open-browser Open texthooker in your default browser
|
||||
|
||||
${B}Overlay${R}
|
||||
--toggle-visible-overlay Toggle subtitle overlay
|
||||
|
||||
@@ -66,6 +66,7 @@ function makeArgs(overrides: Partial<CliArgs> = {}): CliArgs {
|
||||
jellyfinRemoteAnnounce: false,
|
||||
jellyfinPreviewAuth: false,
|
||||
texthooker: false,
|
||||
texthookerOpenBrowser: false,
|
||||
help: false,
|
||||
autoStartOverlay: false,
|
||||
generateConfig: false,
|
||||
|
||||
@@ -68,6 +68,7 @@ function makeArgs(overrides: Partial<CliArgs> = {}): CliArgs {
|
||||
jellyfinRemoteAnnounce: false,
|
||||
jellyfinPreviewAuth: false,
|
||||
texthooker: false,
|
||||
texthookerOpenBrowser: false,
|
||||
help: false,
|
||||
autoStartOverlay: false,
|
||||
generateConfig: false,
|
||||
@@ -399,6 +400,21 @@ test('handleCliCommand runs texthooker flow with browser open', () => {
|
||||
assert.ok(calls.includes('openTexthookerInBrowser:http://127.0.0.1:5174'));
|
||||
});
|
||||
|
||||
test('handleCliCommand opens texthooker browser when requested even if config disables auto-open', () => {
|
||||
const { deps, calls } = createDeps({
|
||||
shouldOpenTexthookerBrowser: () => false,
|
||||
});
|
||||
const args = {
|
||||
...makeArgs({ texthooker: true }),
|
||||
texthookerOpenBrowser: true,
|
||||
} as CliArgs;
|
||||
|
||||
handleCliCommand(args, 'initial', deps);
|
||||
|
||||
assert.ok(calls.includes('ensureTexthookerRunning:5174:'));
|
||||
assert.ok(calls.includes('openTexthookerInBrowser:http://127.0.0.1:5174'));
|
||||
});
|
||||
|
||||
test('handleCliCommand forwards resolved websocket url to texthooker startup', () => {
|
||||
const { deps, calls } = createDeps({
|
||||
getTexthookerWebsocketUrl: () => 'ws://127.0.0.1:6678',
|
||||
|
||||
@@ -704,7 +704,7 @@ export function handleCliCommand(
|
||||
} else if (args.texthooker) {
|
||||
const texthookerPort = deps.getTexthookerPort();
|
||||
deps.ensureTexthookerRunning(texthookerPort, deps.getTexthookerWebsocketUrl());
|
||||
if (deps.shouldOpenTexthookerBrowser()) {
|
||||
if (args.texthookerOpenBrowser || deps.shouldOpenTexthookerBrowser()) {
|
||||
deps.openTexthookerInBrowser(`http://127.0.0.1:${texthookerPort}`);
|
||||
}
|
||||
deps.log(`Texthooker available at http://127.0.0.1:${texthookerPort}`);
|
||||
|
||||
@@ -66,6 +66,7 @@ function makeArgs(overrides: Partial<CliArgs> = {}): CliArgs {
|
||||
jellyfinRemoteAnnounce: false,
|
||||
jellyfinPreviewAuth: false,
|
||||
texthooker: false,
|
||||
texthookerOpenBrowser: false,
|
||||
help: false,
|
||||
autoStartOverlay: false,
|
||||
generateConfig: false,
|
||||
|
||||
@@ -5150,6 +5150,8 @@ const { ensureTray: ensureTrayHandler, destroyTray: destroyTrayHandler } =
|
||||
initializeOverlayRuntime: () => initializeOverlayRuntime(),
|
||||
isOverlayRuntimeInitialized: () => appState.overlayRuntimeInitialized,
|
||||
openSessionHelpModal: () => openSessionHelpOverlay(),
|
||||
openTexthookerInBrowser: () =>
|
||||
handleCliCommand(parseArgs(['--texthooker', '--open-browser'])),
|
||||
showFirstRunSetup: () => !firstRunSetupService.isSetupCompleted(),
|
||||
openFirstRunSetupWindow: () => openFirstRunSetupWindow(),
|
||||
showWindowsMpvLauncherSetup: () => process.platform === 'win32',
|
||||
|
||||
@@ -80,6 +80,7 @@ function makeArgs(overrides: Partial<CliArgs> = {}): CliArgs {
|
||||
jellyfinRemoteAnnounce: false,
|
||||
jellyfinPreviewAuth: false,
|
||||
texthooker: false,
|
||||
texthookerOpenBrowser: false,
|
||||
help: false,
|
||||
autoStartOverlay: false,
|
||||
generateConfig: false,
|
||||
|
||||
@@ -42,6 +42,7 @@ test('build tray template handler wires actions and init guards', () => {
|
||||
const buildTemplate = createBuildTrayMenuTemplateHandler({
|
||||
buildTrayMenuTemplateRuntime: (handlers) => {
|
||||
handlers.openSessionHelp();
|
||||
handlers.openTexthookerInBrowser();
|
||||
handlers.openFirstRunSetup();
|
||||
handlers.openWindowsMpvLauncherSetup();
|
||||
handlers.openYomitanSettings();
|
||||
@@ -57,6 +58,7 @@ test('build tray template handler wires actions and init guards', () => {
|
||||
},
|
||||
isOverlayRuntimeInitialized: () => initialized,
|
||||
openSessionHelpModal: () => calls.push('help'),
|
||||
openTexthookerInBrowser: () => calls.push('texthooker'),
|
||||
showFirstRunSetup: () => true,
|
||||
openFirstRunSetupWindow: () => calls.push('setup'),
|
||||
showWindowsMpvLauncherSetup: () => true,
|
||||
@@ -72,6 +74,7 @@ test('build tray template handler wires actions and init guards', () => {
|
||||
assert.deepEqual(calls, [
|
||||
'init',
|
||||
'help',
|
||||
'texthooker',
|
||||
'setup',
|
||||
'setup',
|
||||
'yomitan',
|
||||
|
||||
@@ -29,6 +29,7 @@ export function createResolveTrayIconPathHandler(deps: {
|
||||
export function createBuildTrayMenuTemplateHandler<TMenuItem>(deps: {
|
||||
buildTrayMenuTemplateRuntime: (handlers: {
|
||||
openSessionHelp: () => void;
|
||||
openTexthookerInBrowser: () => void;
|
||||
openFirstRunSetup: () => void;
|
||||
showFirstRunSetup: boolean;
|
||||
openWindowsMpvLauncherSetup: () => void;
|
||||
@@ -42,6 +43,7 @@ export function createBuildTrayMenuTemplateHandler<TMenuItem>(deps: {
|
||||
initializeOverlayRuntime: () => void;
|
||||
isOverlayRuntimeInitialized: () => boolean;
|
||||
openSessionHelpModal: () => void;
|
||||
openTexthookerInBrowser: () => void;
|
||||
showFirstRunSetup: () => boolean;
|
||||
openFirstRunSetupWindow: () => void;
|
||||
showWindowsMpvLauncherSetup: () => boolean;
|
||||
@@ -59,6 +61,9 @@ export function createBuildTrayMenuTemplateHandler<TMenuItem>(deps: {
|
||||
}
|
||||
deps.openSessionHelpModal();
|
||||
},
|
||||
openTexthookerInBrowser: () => {
|
||||
deps.openTexthookerInBrowser();
|
||||
},
|
||||
openFirstRunSetup: () => {
|
||||
deps.openFirstRunSetupWindow();
|
||||
},
|
||||
|
||||
@@ -25,6 +25,7 @@ test('tray main deps builders return mapped handlers', () => {
|
||||
initializeOverlayRuntime: () => calls.push('init'),
|
||||
isOverlayRuntimeInitialized: () => false,
|
||||
openSessionHelpModal: () => calls.push('help'),
|
||||
openTexthookerInBrowser: () => calls.push('texthooker'),
|
||||
showFirstRunSetup: () => true,
|
||||
openFirstRunSetupWindow: () => calls.push('setup'),
|
||||
showWindowsMpvLauncherSetup: () => true,
|
||||
@@ -37,6 +38,7 @@ test('tray main deps builders return mapped handlers', () => {
|
||||
|
||||
const template = menuDeps.buildTrayMenuTemplateRuntime({
|
||||
openSessionHelp: () => calls.push('open-help'),
|
||||
openTexthookerInBrowser: () => calls.push('open-texthooker'),
|
||||
openFirstRunSetup: () => calls.push('open-setup'),
|
||||
showFirstRunSetup: true,
|
||||
openWindowsMpvLauncherSetup: () => calls.push('open-windows-mpv'),
|
||||
|
||||
@@ -28,6 +28,7 @@ export function createBuildResolveTrayIconPathMainDepsHandler(deps: {
|
||||
export function createBuildTrayMenuTemplateMainDepsHandler<TMenuItem>(deps: {
|
||||
buildTrayMenuTemplateRuntime: (handlers: {
|
||||
openSessionHelp: () => void;
|
||||
openTexthookerInBrowser: () => void;
|
||||
openFirstRunSetup: () => void;
|
||||
showFirstRunSetup: boolean;
|
||||
openWindowsMpvLauncherSetup: () => void;
|
||||
@@ -41,6 +42,7 @@ export function createBuildTrayMenuTemplateMainDepsHandler<TMenuItem>(deps: {
|
||||
initializeOverlayRuntime: () => void;
|
||||
isOverlayRuntimeInitialized: () => boolean;
|
||||
openSessionHelpModal: () => void;
|
||||
openTexthookerInBrowser: () => void;
|
||||
showFirstRunSetup: () => boolean;
|
||||
openFirstRunSetupWindow: () => void;
|
||||
showWindowsMpvLauncherSetup: () => boolean;
|
||||
@@ -55,6 +57,7 @@ export function createBuildTrayMenuTemplateMainDepsHandler<TMenuItem>(deps: {
|
||||
initializeOverlayRuntime: deps.initializeOverlayRuntime,
|
||||
isOverlayRuntimeInitialized: deps.isOverlayRuntimeInitialized,
|
||||
openSessionHelpModal: deps.openSessionHelpModal,
|
||||
openTexthookerInBrowser: deps.openTexthookerInBrowser,
|
||||
showFirstRunSetup: deps.showFirstRunSetup,
|
||||
openFirstRunSetupWindow: deps.openFirstRunSetupWindow,
|
||||
showWindowsMpvLauncherSetup: deps.showWindowsMpvLauncherSetup,
|
||||
|
||||
@@ -25,6 +25,7 @@ test('tray runtime handlers compose resolve/menu/ensure/destroy handlers', () =>
|
||||
},
|
||||
isOverlayRuntimeInitialized: () => overlayInitialized,
|
||||
openSessionHelpModal: () => {},
|
||||
openTexthookerInBrowser: () => {},
|
||||
showFirstRunSetup: () => true,
|
||||
openFirstRunSetupWindow: () => {},
|
||||
showWindowsMpvLauncherSetup: () => true,
|
||||
|
||||
@@ -30,6 +30,7 @@ test('tray menu template contains expected entries and handlers', () => {
|
||||
const calls: string[] = [];
|
||||
const template = buildTrayMenuTemplateRuntime({
|
||||
openSessionHelp: () => calls.push('help'),
|
||||
openTexthookerInBrowser: () => calls.push('texthooker'),
|
||||
openFirstRunSetup: () => calls.push('setup'),
|
||||
showFirstRunSetup: true,
|
||||
openWindowsMpvLauncherSetup: () => calls.push('windows-mpv'),
|
||||
@@ -41,18 +42,24 @@ test('tray menu template contains expected entries and handlers', () => {
|
||||
quitApp: () => calls.push('quit'),
|
||||
});
|
||||
|
||||
assert.equal(template.length, 9);
|
||||
assert.equal(template.some((entry) => entry.label === 'Open Overlay'), false);
|
||||
assert.equal(template.length, 10);
|
||||
assert.equal(
|
||||
template.some((entry) => entry.label === 'Open Overlay'),
|
||||
false,
|
||||
);
|
||||
assert.equal(template[0]!.label, 'Open Help');
|
||||
template[0]!.click?.();
|
||||
template[7]!.type === 'separator' ? calls.push('separator') : calls.push('bad');
|
||||
template[8]!.click?.();
|
||||
assert.deepEqual(calls, ['help', 'separator', 'quit']);
|
||||
assert.equal(template[1]!.label, 'Open Texthooker');
|
||||
template[1]!.click?.();
|
||||
template[8]!.type === 'separator' ? calls.push('separator') : calls.push('bad');
|
||||
template[9]!.click?.();
|
||||
assert.deepEqual(calls, ['help', 'texthooker', 'separator', 'quit']);
|
||||
});
|
||||
|
||||
test('tray menu template omits first-run setup entry when setup is complete', () => {
|
||||
const labels = buildTrayMenuTemplateRuntime({
|
||||
openSessionHelp: () => undefined,
|
||||
openTexthookerInBrowser: () => undefined,
|
||||
openFirstRunSetup: () => undefined,
|
||||
showFirstRunSetup: false,
|
||||
openWindowsMpvLauncherSetup: () => undefined,
|
||||
|
||||
@@ -31,6 +31,7 @@ export function resolveTrayIconPathRuntime(deps: {
|
||||
|
||||
export type TrayMenuActionHandlers = {
|
||||
openSessionHelp: () => void;
|
||||
openTexthookerInBrowser: () => void;
|
||||
openFirstRunSetup: () => void;
|
||||
showFirstRunSetup: boolean;
|
||||
openWindowsMpvLauncherSetup: () => void;
|
||||
@@ -52,6 +53,10 @@ export function buildTrayMenuTemplateRuntime(handlers: TrayMenuActionHandlers):
|
||||
label: 'Open Help',
|
||||
click: handlers.openSessionHelp,
|
||||
},
|
||||
{
|
||||
label: 'Open Texthooker',
|
||||
click: handlers.openTexthookerInBrowser,
|
||||
},
|
||||
...(handlers.showFirstRunSetup
|
||||
? [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user