diff --git a/README.md b/README.md index 182479c4..8f83722a 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Look up words with Yomitan, export to Anki in one key, track your immersion — SubMiner runs as an invisible Electron overlay on top of mpv. Subtitles render as an interactive layer. Move your cursor over any word and trigger a [Yomitan](https://github.com/yomidevs/yomitan) lookup. Press one key to snapshot the sentence, audio, and screenshot into Anki via AnkiConnect. -On Windows, the recommended playback entry point is the optional `SubMiner mpv` shortcut created during setup. First-run setup requires the mpv plugin before it can finish. The shortcut launches `mpv` with SubMiner's defaults directly, so you do not need an `mpv.conf` profile just to use it. +First-run setup requires the mpv plugin before it can finish. On Windows, the optional `SubMiner mpv` shortcut created during setup is the recommended playback entry point because it launches `mpv` with SubMiner's defaults directly, so you do not need an `mpv.conf` profile just to use it. ## Features diff --git a/changes/270-first-run-setup-requires-mpv-plugin.md b/changes/270-first-run-setup-requires-mpv-plugin.md index afa34b41..22bbe57d 100644 --- a/changes/270-first-run-setup-requires-mpv-plugin.md +++ b/changes/270-first-run-setup-requires-mpv-plugin.md @@ -2,4 +2,4 @@ type: changed area: setup - Made mpv plugin installation mandatory in the first-run setup flow, removed the skip path, and kept Finish disabled until the plugin is installed. -- Updated the Windows setup copy to make the `SubMiner mpv` shortcut the recommended playback entry point after setup completes. +- Clarified that the mpv plugin requirement applies to setup on every platform, while the optional `SubMiner mpv` shortcut remains the recommended Windows playback entry point. diff --git a/docs-site/installation.md b/docs-site/installation.md index 3713620e..c4faa9b0 100644 --- a/docs-site/installation.md +++ b/docs-site/installation.md @@ -171,7 +171,7 @@ Install `mpv` separately and ensure `mpv.exe` is on `PATH`. `ffmpeg` is still re ### Windows Usage Notes -- Launch `SubMiner.exe` once to let the first-run setup flow seed `%APPDATA%\\SubMiner\\config.jsonc`, require mpv plugin installation, open bundled Yomitan settings, and optionally create `SubMiner mpv` Start Menu/Desktop shortcuts. On Windows, that shortcut is the recommended way to launch mpv playback with SubMiner defaults. +- Launch `SubMiner.exe` once to let the first-run setup flow seed `%APPDATA%\\SubMiner\\config.jsonc`, require mpv plugin installation, and open bundled Yomitan settings. The optional `SubMiner mpv` Start Menu/Desktop shortcut can also be created during setup, and on Windows it is the recommended way to launch mpv playback with SubMiner defaults. - `SubMiner.exe --launch-mpv` and the optional `SubMiner mpv` shortcut pass SubMiner's default mpv socket/subtitle args directly, including the Windows-safe subtitle search paths that skip the extra current-directory scan; they do not require an `mpv.conf` profile named `subminer`. - First-run mpv plugin installs pin `binary_path` to the current `SubMiner.exe` automatically. Manual plugin configs can leave `binary_path` empty unless SubMiner is installed in a non-standard location. - Windows plugin installs rewrite `socket_path` to `\\.\pipe\subminer-socket`; do not keep `/tmp/subminer-socket` on Windows. diff --git a/docs-site/usage.md b/docs-site/usage.md index de53099d..c35159ad 100644 --- a/docs-site/usage.md +++ b/docs-site/usage.md @@ -117,8 +117,10 @@ SubMiner.AppImage --help # Show all options ### Windows mpv Shortcut +First-run setup requires the mpv plugin before it can finish. + If you enabled the optional Windows shortcut during install, SubMiner creates a `SubMiner mpv` shortcut in the Start menu and/or on the desktop. On Windows, that shortcut is the recommended way to launch local files with SubMiner because it starts `mpv.exe` with the right defaults directly. -First-run setup requires the mpv plugin before it can finish, so the shortcut is the normal Windows playback entry point after setup completes. +After setup completes, the shortcut is the normal Windows playback entry point. You can use it three ways: diff --git a/src/main/runtime/first-run-setup-service.test.ts b/src/main/runtime/first-run-setup-service.test.ts index b60a86c2..5272aa2f 100644 --- a/src/main/runtime/first-run-setup-service.test.ts +++ b/src/main/runtime/first-run-setup-service.test.ts @@ -251,6 +251,48 @@ test('setup service reopens when external-yomitan completion later has no extern }); }); +test('setup service reopens when a completed setup no longer has the mpv plugin installed', async () => { + await withTempDir(async (root) => { + const configDir = path.join(root, 'SubMiner'); + fs.mkdirSync(configDir, { recursive: true }); + fs.writeFileSync(path.join(configDir, 'config.jsonc'), '{}'); + + const completedService = createFirstRunSetupService({ + configDir, + getYomitanDictionaryCount: async () => 2, + detectPluginInstalled: () => true, + installPlugin: async () => ({ + ok: true, + pluginInstallStatus: 'installed', + pluginInstallPathSummary: '/tmp/mpv', + message: 'ok', + }), + onStateChanged: () => undefined, + }); + + await completedService.ensureSetupStateInitialized(); + await completedService.markSetupCompleted(); + + const service = createFirstRunSetupService({ + configDir, + getYomitanDictionaryCount: async () => 2, + detectPluginInstalled: () => false, + installPlugin: async () => ({ + ok: true, + pluginInstallStatus: 'installed', + pluginInstallPathSummary: null, + message: 'ok', + }), + onStateChanged: () => undefined, + }); + + const snapshot = await service.ensureSetupStateInitialized(); + assert.equal(snapshot.state.status, 'incomplete'); + assert.equal(snapshot.canFinish, false); + assert.equal(snapshot.pluginStatus, 'required'); + }); +}); + test('setup service keeps completed when external-yomitan completion later has internal dictionaries available', async () => { await withTempDir(async (root) => { const configDir = path.join(root, 'SubMiner'); diff --git a/src/main/runtime/first-run-setup-service.ts b/src/main/runtime/first-run-setup-service.ts index 91ccf000..42af4aee 100644 --- a/src/main/runtime/first-run-setup-service.ts +++ b/src/main/runtime/first-run-setup-service.ts @@ -272,24 +272,20 @@ export function createFirstRunSetupService(deps: { getYomitanDictionaryCount: deps.getYomitanDictionaryCount, isExternalYomitanConfigured: deps.isExternalYomitanConfigured, }); - const yomitanSetupSatisfied = isYomitanSetupSatisfied({ - configReady, - dictionaryCount, - externalYomitanConfigured, - }); - if ( - isSetupCompleted(state) && - !( - state.yomitanSetupMode === 'external' && - !externalYomitanConfigured && - !yomitanSetupSatisfied - ) - ) { + const pluginInstalled = await deps.detectPluginInstalled(); + const canFinish = + pluginInstalled && + isYomitanSetupSatisfied({ + configReady, + dictionaryCount, + externalYomitanConfigured, + }); + if (isSetupCompleted(state) && canFinish) { completed = true; return refreshWithState(state); } - if (yomitanSetupSatisfied) { + if (canFinish) { const completedState = writeState({ ...state, status: 'completed',