Launch macOS app background-detached when no args passed

- Add `launchAppBackgroundDetached` that spawns with `--start --background` and `SUBMINER_BACKGROUND_CHILD=1`
- On darwin with empty appArgs, use detached background launch instead of inherited process
- Add `extraEnv` param to `launchAppCommandDetached` for env injection
- Inject deps into `runAppPassthroughCommand` for testability
- Bump vendor/subminer-yomitan submodule
This commit is contained in:
2026-05-25 02:12:41 -07:00
parent 920cbab1bc
commit 9fe13601fb
5 changed files with 114 additions and 6 deletions
+30 -4
View File
@@ -1,19 +1,45 @@
import { launchTexthookerOnly, runAppCommandWithInherit } from '../mpv.js';
import {
launchAppBackgroundDetached,
launchTexthookerOnly,
runAppCommandWithInherit,
} from '../mpv.js';
import type { LauncherCommandContext } from './context.js';
export function runAppPassthroughCommand(context: LauncherCommandContext): boolean {
type AppCommandDeps = {
platform: () => NodeJS.Platform;
runAppCommandWithInherit: (appPath: string, appArgs: string[]) => void;
launchAppBackgroundDetached: (
appPath: string,
logLevel: LauncherCommandContext['args']['logLevel'],
) => void;
};
const defaultAppCommandDeps: AppCommandDeps = {
platform: () => process.platform,
runAppCommandWithInherit,
launchAppBackgroundDetached,
};
export function runAppPassthroughCommand(
context: LauncherCommandContext,
deps: AppCommandDeps = defaultAppCommandDeps,
): boolean {
const { args, appPath } = context;
if (!appPath) {
return false;
}
if (args.settings) {
runAppCommandWithInherit(appPath, ['--settings']);
deps.runAppCommandWithInherit(appPath, ['--settings']);
return true;
}
if (!args.appPassthrough) {
return false;
}
runAppCommandWithInherit(appPath, args.appArgs);
if (deps.platform() === 'darwin' && args.appArgs.length === 0) {
deps.launchAppBackgroundDetached(appPath, args.logLevel);
return true;
}
deps.runAppCommandWithInherit(appPath, args.appArgs);
return true;
}
+43
View File
@@ -7,6 +7,7 @@ import { runConfigCommand } from './config-command.js';
import { runDictionaryCommand } from './dictionary-command.js';
import { runDoctorCommand } from './doctor-command.js';
import { runMpvPreAppCommand } from './mpv-command.js';
import { runAppPassthroughCommand } from './app-command.js';
import { runStatsCommand } from './stats-command.js';
import { runUpdateCommand } from './update-command.js';
@@ -168,6 +169,48 @@ test('doctor command forwards refresh-known-words to app binary', () => {
assert.deepEqual(forwarded, [['--refresh-known-words']]);
});
test('app command starts default macOS background app detached from launcher', () => {
const context = createContext();
context.args.appPassthrough = true;
context.args.appArgs = [];
const calls: string[] = [];
const handled = runAppPassthroughCommand(context, {
platform: () => 'darwin',
runAppCommandWithInherit: () => {
calls.push('attached');
},
launchAppBackgroundDetached: (appPath, logLevel) => {
calls.push(`detached:${appPath}:${logLevel}`);
},
});
assert.equal(handled, true);
assert.deepEqual(calls, ['detached:/tmp/subminer.app:info']);
});
test('app command keeps explicit passthrough args attached', () => {
const context = createContext();
context.args.appPassthrough = true;
context.args.appArgs = ['--settings'];
const forwarded: string[][] = [];
const detached: string[] = [];
const handled = runAppPassthroughCommand(context, {
platform: () => 'darwin',
runAppCommandWithInherit: (_appPath, appArgs) => {
forwarded.push(appArgs);
},
launchAppBackgroundDetached: () => {
detached.push('detached');
},
});
assert.equal(handled, true);
assert.deepEqual(forwarded, [['--settings']]);
assert.deepEqual(detached, []);
});
test('mpv pre-app command exits non-zero when socket is not ready', async () => {
const context = createContext();
context.args.mpvStatus = true;