mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-27 18:22:41 -08:00
feat: replace y-j with configurable Jimaku shortcut
This commit is contained in:
@@ -90,6 +90,7 @@ cp plugin/subminer.conf ~/.config/mpv/script-opts/
|
|||||||
Requires mpv IPC: `--input-ipc-server=/tmp/subminer-socket`
|
Requires mpv IPC: `--input-ipc-server=/tmp/subminer-socket`
|
||||||
|
|
||||||
Default chord prefix: `y` (`y-y` menu, `y-s` start, `y-S` stop, `y-t` toggle visible layer).
|
Default chord prefix: `y` (`y-y` menu, `y-s` start, `y-S` stop, `y-t` toggle visible layer).
|
||||||
|
Overlay Jimaku shortcut default: `Ctrl+Alt+J` (`shortcuts.openJimaku`).
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Settings are stored in `~/.config/SubMiner/config.jsonc`
|
|||||||
|
|
||||||
### Configuration File
|
### Configuration File
|
||||||
|
|
||||||
See `config.example.jsonc` for a comprehensive example configuration file with all available options, default values, and detailed comments. Only include the options you want to customize in your config file.
|
See [config.example.jsonc](/config.example.jsonc) for a comprehensive example configuration file with all available options, default values, and detailed comments. Only include the options you want to customize in your config file.
|
||||||
|
|
||||||
Generate a fresh default config from the centralized config registry:
|
Generate a fresh default config from the centralized config registry:
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ subminer.AppImage --generate-config --backup-overwrite
|
|||||||
- `--generate-config` writes a default JSONC config template.
|
- `--generate-config` writes a default JSONC config template.
|
||||||
- If the target file exists, SubMiner prompts to create a timestamped backup and overwrite.
|
- If the target file exists, SubMiner prompts to create a timestamped backup and overwrite.
|
||||||
- In non-interactive shells, use `--backup-overwrite` to explicitly back up and overwrite.
|
- In non-interactive shells, use `--backup-overwrite` to explicitly back up and overwrite.
|
||||||
- `pnpm run generate:config-example` regenerates repository `config.example.jsonc` from the same centralized defaults.
|
- `pnpm run generate:config-example` regenerates both repository `config.example.jsonc` and docs-served `/config.example.jsonc` from the same centralized defaults.
|
||||||
- `make generate-config` builds and runs the same default-config generator via local Electron.
|
- `make generate-config` builds and runs the same default-config generator via local Electron.
|
||||||
|
|
||||||
Invalid config values are handled with warn-and-fallback behavior: SubMiner logs the bad key/value and continues with the default for that option.
|
Invalid config values are handled with warn-and-fallback behavior: SubMiner logs the bad key/value and continues with the default for that option.
|
||||||
@@ -164,7 +164,12 @@ When enabled, sentence cards automatically set `IsSentenceCard` to `"x"` and pop
|
|||||||
|
|
||||||
Kiku extends Lapis with **field grouping** — when a duplicate card is detected (same Word/Expression), SubMiner merges the two cards' content into one using Kiku's `data-group-id` HTML structure, organizing each mining instance into separate pages within the note.
|
Kiku extends Lapis with **field grouping** — when a duplicate card is detected (same Word/Expression), SubMiner merges the two cards' content into one using Kiku's `data-group-id` HTML structure, organizing each mining instance into separate pages within the note.
|
||||||
|
|
||||||
[](https://github.com/user-attachments/assets/bf2476cb-2351-4622-8143-c90e59b19213)
|
<video controls playsinline preload="metadata" poster="/assets/kiku-integration-poster.jpg" style="width: 100%; max-width: 960px;">
|
||||||
|
<source :src="'/assets/kiku-integration.webm'" type="video/webm" />
|
||||||
|
Your browser does not support the video tag.
|
||||||
|
</video>
|
||||||
|
|
||||||
|
<a :href="'/assets/kiku-integration.webm'" target="_blank" rel="noreferrer">Open demo in a new tab</a>
|
||||||
|
|
||||||
|
|
||||||
| Mode | Behavior |
|
| Mode | Behavior |
|
||||||
@@ -416,6 +421,7 @@ See `config.example.jsonc` for detailed configuration options.
|
|||||||
"mineSentenceMultiple": "CommandOrControl+Shift+S",
|
"mineSentenceMultiple": "CommandOrControl+Shift+S",
|
||||||
"markAudioCard": "CommandOrControl+Shift+A",
|
"markAudioCard": "CommandOrControl+Shift+A",
|
||||||
"openRuntimeOptions": "CommandOrControl+Shift+O",
|
"openRuntimeOptions": "CommandOrControl+Shift+O",
|
||||||
|
"openJimaku": "Ctrl+Alt+J",
|
||||||
"multiCopyTimeoutMs": 3000
|
"multiCopyTimeoutMs": 3000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -436,6 +442,7 @@ See `config.example.jsonc` for detailed configuration options.
|
|||||||
| `toggleSecondarySub` | string \| `null` | Accelerator for cycling secondary subtitle mode (default: `"CommandOrControl+Shift+V"`) |
|
| `toggleSecondarySub` | string \| `null` | Accelerator for cycling secondary subtitle mode (default: `"CommandOrControl+Shift+V"`) |
|
||||||
| `markAudioCard` | string \| `null` | Accelerator for marking last card as audio card (default: `"CommandOrControl+Shift+A"`) |
|
| `markAudioCard` | string \| `null` | Accelerator for marking last card as audio card (default: `"CommandOrControl+Shift+A"`) |
|
||||||
| `openRuntimeOptions` | string \| `null` | Opens runtime options palette for live session-only toggles (default: `"CommandOrControl+Shift+O"`) |
|
| `openRuntimeOptions` | string \| `null` | Opens runtime options palette for live session-only toggles (default: `"CommandOrControl+Shift+O"`) |
|
||||||
|
| `openJimaku` | string \| `null` | Opens the Jimaku search modal (default: `"Ctrl+Alt+J"`) |
|
||||||
|
|
||||||
**See `config.example.jsonc`** for the complete list of shortcut configuration options.
|
**See `config.example.jsonc`** for the complete list of shortcut configuration options.
|
||||||
|
|
||||||
|
|||||||
@@ -6,14 +6,14 @@
|
|||||||
- Hyprland (uses `hyprctl`)
|
- Hyprland (uses `hyprctl`)
|
||||||
- X11 (uses `xdotool` and `xwininfo`)
|
- X11 (uses `xdotool` and `xwininfo`)
|
||||||
- mpv (with IPC socket support)
|
- mpv (with IPC socket support)
|
||||||
- mecab and mecab-ipadic (Japanese morphological analyzer)
|
- mecab and mecab-ipadic (optional fallback Japanese morphological analyzer)
|
||||||
- fuse2 (for AppImage support)
|
- fuse2 (for AppImage support)
|
||||||
|
|
||||||
### macOS
|
### macOS
|
||||||
|
|
||||||
- macOS 10.13 or later
|
- macOS 10.13 or later
|
||||||
- mpv (with IPC socket support)
|
- mpv (with IPC socket support)
|
||||||
- mecab and mecab-ipadic (Japanese morphological analyzer) - optional
|
- mecab and mecab-ipadic (optional fallback Japanese morphological analyzer)
|
||||||
- **Accessibility permission** required for window tracking (see [macOS Installation](#macos-installation))
|
- **Accessibility permission** required for window tracking (see [macOS Installation](#macos-installation))
|
||||||
|
|
||||||
**Optional:**
|
**Optional:**
|
||||||
@@ -154,8 +154,9 @@ binary_path=/Applications/SubMiner.app/Contents/MacOS/subminer
|
|||||||
|
|
||||||
The Lua plugin allows you to control the overlay directly from mpv using keybindings:
|
The Lua plugin allows you to control the overlay directly from mpv using keybindings:
|
||||||
|
|
||||||
> [!IMPORTANT]
|
::: warning Important
|
||||||
> `mpv` must be launched with `--input-ipc-server=/tmp/subminer-socket` to allow communication with the application
|
`mpv` must be launched with `--input-ipc-server=/tmp/subminer-socket` to allow communication with the application.
|
||||||
|
:::
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Copy plugin files to mpv config
|
# Copy plugin files to mpv config
|
||||||
@@ -182,6 +183,8 @@ All keybindings use chord sequences starting with `y`:
|
|||||||
|
|
||||||
The menu provides options to start/stop/toggle the visible or invisible overlay layers and open settings. Type to filter or use arrow keys to navigate.
|
The menu provides options to start/stop/toggle the visible or invisible overlay layers and open settings. Type to filter or use arrow keys to navigate.
|
||||||
|
|
||||||
|
Jimaku modal shortcut is configured separately in SubMiner overlay shortcuts (`shortcuts.openJimaku`), default `Ctrl+Alt+J`.
|
||||||
|
|
||||||
#### Plugin Configuration
|
#### Plugin Configuration
|
||||||
|
|
||||||
Edit `~/.config/mpv/script-opts/subminer.conf`:
|
Edit `~/.config/mpv/script-opts/subminer.conf`:
|
||||||
@@ -243,4 +246,3 @@ Launch mpv with:
|
|||||||
```bash
|
```bash
|
||||||
mpv --input-ipc-server=\\\\.\\pipe\\subminer-socket video.mkv
|
mpv --input-ipc-server=\\\\.\\pipe\\subminer-socket video.mkv
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
211
docs/public/config.example.jsonc
Normal file
211
docs/public/config.example.jsonc
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
/**
|
||||||
|
* SubMiner Example Configuration File
|
||||||
|
*
|
||||||
|
* This file is auto-generated from src/config/definitions.ts.
|
||||||
|
* Copy to ~/.config/SubMiner/config.jsonc and edit as needed.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// Overlay Auto-Start
|
||||||
|
// When overlay connects to mpv, automatically show overlay and hide mpv subtitles.
|
||||||
|
// ==========================================
|
||||||
|
"auto_start_overlay": false,
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// Visible Overlay Subtitle Binding
|
||||||
|
// Control whether visible overlay toggles also toggle MPV subtitle visibility.
|
||||||
|
// When enabled, visible overlay hides MPV subtitles; when disabled, MPV subtitles are left unchanged.
|
||||||
|
// ==========================================
|
||||||
|
"bind_visible_overlay_to_mpv_sub_visibility": true,
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// Texthooker Server
|
||||||
|
// Control whether browser opens automatically for texthooker.
|
||||||
|
// ==========================================
|
||||||
|
"texthooker": {
|
||||||
|
"openBrowser": true
|
||||||
|
},
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// WebSocket Server
|
||||||
|
// Built-in WebSocket server broadcasts subtitle text to connected clients.
|
||||||
|
// Auto mode disables built-in server if mpv_websocket is detected.
|
||||||
|
// ==========================================
|
||||||
|
"websocket": {
|
||||||
|
"enabled": "auto",
|
||||||
|
"port": 6677
|
||||||
|
},
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// AnkiConnect Integration
|
||||||
|
// Automatic Anki updates and media generation options.
|
||||||
|
// ==========================================
|
||||||
|
"ankiConnect": {
|
||||||
|
"enabled": false,
|
||||||
|
"url": "http://127.0.0.1:8765",
|
||||||
|
"pollingRate": 3000,
|
||||||
|
"fields": {
|
||||||
|
"audio": "ExpressionAudio",
|
||||||
|
"image": "Picture",
|
||||||
|
"sentence": "Sentence",
|
||||||
|
"miscInfo": "MiscInfo",
|
||||||
|
"translation": "SelectionText"
|
||||||
|
},
|
||||||
|
"ai": {
|
||||||
|
"enabled": false,
|
||||||
|
"alwaysUseAiTranslation": false,
|
||||||
|
"apiKey": "",
|
||||||
|
"model": "openai/gpt-4o-mini",
|
||||||
|
"baseUrl": "https://openrouter.ai/api",
|
||||||
|
"targetLanguage": "English",
|
||||||
|
"systemPrompt": "You are a translation engine. Return only the translated text with no explanations."
|
||||||
|
},
|
||||||
|
"media": {
|
||||||
|
"generateAudio": true,
|
||||||
|
"generateImage": true,
|
||||||
|
"imageType": "static",
|
||||||
|
"imageFormat": "jpg",
|
||||||
|
"imageQuality": 92,
|
||||||
|
"animatedFps": 10,
|
||||||
|
"animatedMaxWidth": 640,
|
||||||
|
"animatedCrf": 35,
|
||||||
|
"audioPadding": 0.5,
|
||||||
|
"fallbackDuration": 3,
|
||||||
|
"maxMediaDuration": 30
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"overwriteAudio": true,
|
||||||
|
"overwriteImage": true,
|
||||||
|
"mediaInsertMode": "append",
|
||||||
|
"highlightWord": true,
|
||||||
|
"notificationType": "osd",
|
||||||
|
"autoUpdateNewCards": true
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"pattern": "[SubMiner] %f (%t)"
|
||||||
|
},
|
||||||
|
"isLapis": {
|
||||||
|
"enabled": false,
|
||||||
|
"sentenceCardModel": "Japanese sentences",
|
||||||
|
"sentenceCardSentenceField": "Sentence",
|
||||||
|
"sentenceCardAudioField": "SentenceAudio"
|
||||||
|
},
|
||||||
|
"isKiku": {
|
||||||
|
"enabled": false,
|
||||||
|
"fieldGrouping": "disabled",
|
||||||
|
"deleteDuplicateInAuto": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// Keyboard Shortcuts
|
||||||
|
// Overlay keyboard shortcuts. Set a shortcut to null to disable.
|
||||||
|
// ==========================================
|
||||||
|
"shortcuts": {
|
||||||
|
"toggleVisibleOverlayGlobal": "Alt+Shift+O",
|
||||||
|
"toggleInvisibleOverlayGlobal": "Alt+Shift+I",
|
||||||
|
"copySubtitle": "CommandOrControl+C",
|
||||||
|
"copySubtitleMultiple": "CommandOrControl+Shift+C",
|
||||||
|
"updateLastCardFromClipboard": "CommandOrControl+V",
|
||||||
|
"triggerFieldGrouping": "CommandOrControl+G",
|
||||||
|
"triggerSubsync": "Ctrl+Alt+S",
|
||||||
|
"mineSentence": "CommandOrControl+S",
|
||||||
|
"mineSentenceMultiple": "CommandOrControl+Shift+S",
|
||||||
|
"multiCopyTimeoutMs": 3000,
|
||||||
|
"toggleSecondarySub": "CommandOrControl+Shift+V",
|
||||||
|
"markAudioCard": "CommandOrControl+Shift+A",
|
||||||
|
"openRuntimeOptions": "CommandOrControl+Shift+O",
|
||||||
|
"openJimaku": "Ctrl+Alt+J"
|
||||||
|
},
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// Invisible Overlay
|
||||||
|
// Startup behavior for the invisible interactive subtitle mining layer.
|
||||||
|
// ==========================================
|
||||||
|
"invisibleOverlay": {
|
||||||
|
"startupVisibility": "platform-default"
|
||||||
|
},
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// Keybindings (MPV Commands)
|
||||||
|
// Extra keybindings that are merged with built-in defaults.
|
||||||
|
// Set command to null to disable a default keybinding.
|
||||||
|
// ==========================================
|
||||||
|
"keybindings": [],
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// Subtitle Appearance
|
||||||
|
// Primary and secondary subtitle styling.
|
||||||
|
// ==========================================
|
||||||
|
"subtitleStyle": {
|
||||||
|
"fontFamily": "Noto Sans CJK JP Regular, Noto Sans CJK JP, Arial Unicode MS, Arial, sans-serif",
|
||||||
|
"fontSize": 35,
|
||||||
|
"fontColor": "#cad3f5",
|
||||||
|
"fontWeight": "normal",
|
||||||
|
"fontStyle": "normal",
|
||||||
|
"backgroundColor": "rgba(54, 58, 79, 0.5)",
|
||||||
|
"secondary": {
|
||||||
|
"fontSize": 24,
|
||||||
|
"fontColor": "#ffffff",
|
||||||
|
"backgroundColor": "transparent",
|
||||||
|
"fontWeight": "normal",
|
||||||
|
"fontStyle": "normal",
|
||||||
|
"fontFamily": "Noto Sans CJK JP Regular, Noto Sans CJK JP, Arial Unicode MS, Arial, sans-serif"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// Secondary Subtitles
|
||||||
|
// Dual subtitle track options.
|
||||||
|
// Used by subminer YouTube subtitle generation as secondary language preferences.
|
||||||
|
// ==========================================
|
||||||
|
"secondarySub": {
|
||||||
|
"secondarySubLanguages": [],
|
||||||
|
"autoLoadSecondarySub": false,
|
||||||
|
"defaultMode": "hover"
|
||||||
|
},
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// Auto Subtitle Sync
|
||||||
|
// Subsync engine and executable paths.
|
||||||
|
// ==========================================
|
||||||
|
"subsync": {
|
||||||
|
"defaultMode": "auto",
|
||||||
|
"alass_path": "",
|
||||||
|
"ffsubsync_path": "",
|
||||||
|
"ffmpeg_path": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// Subtitle Position
|
||||||
|
// Initial vertical subtitle position from the bottom.
|
||||||
|
// ==========================================
|
||||||
|
"subtitlePosition": {
|
||||||
|
"yPercent": 10
|
||||||
|
},
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// Jimaku
|
||||||
|
// Jimaku API configuration and defaults.
|
||||||
|
// ==========================================
|
||||||
|
"jimaku": {
|
||||||
|
"apiBaseUrl": "https://jimaku.cc",
|
||||||
|
"languagePreference": "ja",
|
||||||
|
"maxEntryResults": 10
|
||||||
|
},
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// YouTube Subtitle Generation
|
||||||
|
// Defaults for subminer YouTube subtitle extraction/transcription mode.
|
||||||
|
// ==========================================
|
||||||
|
"youtubeSubgen": {
|
||||||
|
"mode": "automatic",
|
||||||
|
"whisperBin": "",
|
||||||
|
"whisperModel": "",
|
||||||
|
"primarySubLanguages": [
|
||||||
|
"ja",
|
||||||
|
"jpn"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,8 @@ There are two ways to use SubMiner:
|
|||||||
| **subminer script** | All-in-one solution. Handles video selection, launches MPV with the correct socket, starts the overlay automatically, and cleans up on exit. |
|
| **subminer script** | All-in-one solution. Handles video selection, launches MPV with the correct socket, starts the overlay automatically, and cleans up on exit. |
|
||||||
| **MPV plugin** | When you launch MPV yourself or from other tools. Provides in-MPV chord keybindings (e.g. `y-y` for menu) to control visible and invisible overlay layers. Requires `--input-ipc-server=/tmp/subminer-socket`. |
|
| **MPV plugin** | When you launch MPV yourself or from other tools. Provides in-MPV chord keybindings (e.g. `y-y` for menu) to control visible and invisible overlay layers. Requires `--input-ipc-server=/tmp/subminer-socket`. |
|
||||||
|
|
||||||
|
Jimaku modal shortcut is an overlay shortcut, not an MPV plugin chord: default `Ctrl+Alt+J` via `shortcuts.openJimaku`.
|
||||||
|
|
||||||
You can use both together—install the plugin for on-demand control, but use `subminer` when you want the streamlined workflow.
|
You can use both together—install the plugin for on-demand control, but use `subminer` when you want the streamlined workflow.
|
||||||
|
|
||||||
`subminer` is implemented as a Bun script and runs directly via shebang (no `bun run` needed), for example: `subminer video.mkv`.
|
`subminer` is implemented as a Bun script and runs directly via shebang (no `bun run` needed), for example: `subminer video.mkv`.
|
||||||
@@ -113,7 +115,7 @@ Notes:
|
|||||||
| `Right-click` | Toggle MPV pause (outside subtitle area) |
|
| `Right-click` | Toggle MPV pause (outside subtitle area) |
|
||||||
| `Right-click + drag` | Move subtitle position (on subtitle) |
|
| `Right-click + drag` | Move subtitle position (on subtitle) |
|
||||||
|
|
||||||
These keybindings only work when the overlay window has focus. See [Configuration](configuration.md) for customization.
|
These keybindings only work when the overlay window has focus. See [Configuration](/configuration) for customization.
|
||||||
|
|
||||||
### Overlay Chord Shortcuts
|
### Overlay Chord Shortcuts
|
||||||
|
|
||||||
@@ -125,7 +127,7 @@ These keybindings only work when the overlay window has focus. See [Configuratio
|
|||||||
|
|
||||||
1. MPV runs with an IPC socket at `/tmp/subminer-socket`
|
1. MPV runs with an IPC socket at `/tmp/subminer-socket`
|
||||||
2. The overlay connects and subscribes to subtitle changes
|
2. The overlay connects and subscribes to subtitle changes
|
||||||
3. Subtitles are tokenized with MeCab and merged into natural word boundaries
|
3. Subtitles are tokenized with Yomitan's internal parser, with MeCab fallback when needed
|
||||||
4. Words are displayed as clickable spans
|
4. Words are displayed as clickable spans
|
||||||
5. Clicking a word triggers Yomitan popup for dictionary lookup
|
5. Clicking a word triggers Yomitan popup for dictionary lookup
|
||||||
6. Texthooker server runs at `http://127.0.0.1:5174` for external tools
|
6. Texthooker server runs at `http://127.0.0.1:5174` for external tools
|
||||||
|
|||||||
@@ -43,7 +43,8 @@ test("parses invisible overlay config and new global shortcuts", () => {
|
|||||||
`{
|
`{
|
||||||
"shortcuts": {
|
"shortcuts": {
|
||||||
"toggleVisibleOverlayGlobal": "Alt+Shift+U",
|
"toggleVisibleOverlayGlobal": "Alt+Shift+U",
|
||||||
"toggleInvisibleOverlayGlobal": "Alt+Shift+I"
|
"toggleInvisibleOverlayGlobal": "Alt+Shift+I",
|
||||||
|
"openJimaku": "Ctrl+Alt+J"
|
||||||
},
|
},
|
||||||
"invisibleOverlay": {
|
"invisibleOverlay": {
|
||||||
"startupVisibility": "hidden"
|
"startupVisibility": "hidden"
|
||||||
@@ -60,6 +61,7 @@ test("parses invisible overlay config and new global shortcuts", () => {
|
|||||||
const config = service.getConfig();
|
const config = service.getConfig();
|
||||||
assert.equal(config.shortcuts.toggleVisibleOverlayGlobal, "Alt+Shift+U");
|
assert.equal(config.shortcuts.toggleVisibleOverlayGlobal, "Alt+Shift+U");
|
||||||
assert.equal(config.shortcuts.toggleInvisibleOverlayGlobal, "Alt+Shift+I");
|
assert.equal(config.shortcuts.toggleInvisibleOverlayGlobal, "Alt+Shift+I");
|
||||||
|
assert.equal(config.shortcuts.openJimaku, "Ctrl+Alt+J");
|
||||||
assert.equal(config.invisibleOverlay.startupVisibility, "hidden");
|
assert.equal(config.invisibleOverlay.startupVisibility, "hidden");
|
||||||
assert.equal(config.bind_visible_overlay_to_mpv_sub_visibility, false);
|
assert.equal(config.bind_visible_overlay_to_mpv_sub_visibility, false);
|
||||||
assert.deepEqual(config.youtubeSubgen.primarySubLanguages, ["ja", "jpn", "jp"]);
|
assert.deepEqual(config.youtubeSubgen.primarySubLanguages, ["ja", "jpn", "jp"]);
|
||||||
|
|||||||
@@ -152,6 +152,7 @@ export const DEFAULT_CONFIG: ResolvedConfig = {
|
|||||||
toggleSecondarySub: "CommandOrControl+Shift+V",
|
toggleSecondarySub: "CommandOrControl+Shift+V",
|
||||||
markAudioCard: "CommandOrControl+Shift+A",
|
markAudioCard: "CommandOrControl+Shift+A",
|
||||||
openRuntimeOptions: "CommandOrControl+Shift+O",
|
openRuntimeOptions: "CommandOrControl+Shift+O",
|
||||||
|
openJimaku: "Ctrl+Alt+J",
|
||||||
},
|
},
|
||||||
secondarySub: {
|
secondarySub: {
|
||||||
secondarySubLanguages: [],
|
secondarySubLanguages: [],
|
||||||
|
|||||||
@@ -214,6 +214,7 @@ export class ConfigService {
|
|||||||
"toggleSecondarySub",
|
"toggleSecondarySub",
|
||||||
"markAudioCard",
|
"markAudioCard",
|
||||||
"openRuntimeOptions",
|
"openRuntimeOptions",
|
||||||
|
"openJimaku",
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
for (const key of shortcutKeys) {
|
for (const key of shortcutKeys) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { ConfiguredShortcuts } from "../utils/shortcut-config";
|
|||||||
|
|
||||||
export interface OverlayShortcutFallbackHandlers {
|
export interface OverlayShortcutFallbackHandlers {
|
||||||
openRuntimeOptions: () => void;
|
openRuntimeOptions: () => void;
|
||||||
|
openJimaku: () => void;
|
||||||
markAudioCard: () => void;
|
markAudioCard: () => void;
|
||||||
copySubtitleMultiple: (timeoutMs: number) => void;
|
copySubtitleMultiple: (timeoutMs: number) => void;
|
||||||
copySubtitle: () => void;
|
copySubtitle: () => void;
|
||||||
@@ -34,6 +35,12 @@ export function runOverlayShortcutLocalFallback(
|
|||||||
handlers.openRuntimeOptions();
|
handlers.openRuntimeOptions();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
accelerator: shortcuts.openJimaku,
|
||||||
|
run: () => {
|
||||||
|
handlers.openJimaku();
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
accelerator: shortcuts.markAudioCard,
|
accelerator: shortcuts.markAudioCard,
|
||||||
run: () => {
|
run: () => {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export interface OverlayShortcutHandlers {
|
|||||||
toggleSecondarySub: () => void;
|
toggleSecondarySub: () => void;
|
||||||
markAudioCard: () => void;
|
markAudioCard: () => void;
|
||||||
openRuntimeOptions: () => void;
|
openRuntimeOptions: () => void;
|
||||||
|
openJimaku: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function registerOverlayShortcutsService(
|
export function registerOverlayShortcutsService(
|
||||||
@@ -118,6 +119,13 @@ export function registerOverlayShortcutsService(
|
|||||||
"openRuntimeOptions",
|
"openRuntimeOptions",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (shortcuts.openJimaku) {
|
||||||
|
registerOverlayShortcut(
|
||||||
|
shortcuts.openJimaku,
|
||||||
|
() => handlers.openJimaku(),
|
||||||
|
"openJimaku",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return registeredAny;
|
return registeredAny;
|
||||||
}
|
}
|
||||||
@@ -155,4 +163,7 @@ export function unregisterOverlayShortcutsService(
|
|||||||
if (shortcuts.openRuntimeOptions) {
|
if (shortcuts.openRuntimeOptions) {
|
||||||
globalShortcut.unregister(shortcuts.openRuntimeOptions);
|
globalShortcut.unregister(shortcuts.openRuntimeOptions);
|
||||||
}
|
}
|
||||||
|
if (shortcuts.openJimaku) {
|
||||||
|
globalShortcut.unregister(shortcuts.openJimaku);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export interface ConfiguredShortcuts {
|
|||||||
toggleSecondarySub: string | null | undefined;
|
toggleSecondarySub: string | null | undefined;
|
||||||
markAudioCard: string | null | undefined;
|
markAudioCard: string | null | undefined;
|
||||||
openRuntimeOptions: string | null | undefined;
|
openRuntimeOptions: string | null | undefined;
|
||||||
|
openJimaku: string | null | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveConfiguredShortcuts(
|
export function resolveConfiguredShortcuts(
|
||||||
@@ -78,5 +79,8 @@ export function resolveConfiguredShortcuts(
|
|||||||
config.shortcuts?.openRuntimeOptions ??
|
config.shortcuts?.openRuntimeOptions ??
|
||||||
defaultConfig.shortcuts?.openRuntimeOptions,
|
defaultConfig.shortcuts?.openRuntimeOptions,
|
||||||
),
|
),
|
||||||
|
openJimaku: normalizeShortcut(
|
||||||
|
config.shortcuts?.openJimaku ?? defaultConfig.shortcuts?.openJimaku,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2265,6 +2265,9 @@ function tryHandleOverlayShortcutLocalFallback(input: Electron.Input): boolean {
|
|||||||
openRuntimeOptions: () => {
|
openRuntimeOptions: () => {
|
||||||
openRuntimeOptionsPalette();
|
openRuntimeOptionsPalette();
|
||||||
},
|
},
|
||||||
|
openJimaku: () => {
|
||||||
|
sendToVisibleOverlay("jimaku:open");
|
||||||
|
},
|
||||||
markAudioCard: () => {
|
markAudioCard: () => {
|
||||||
markLastCardAsAudioCard().catch((err) => {
|
markLastCardAsAudioCard().catch((err) => {
|
||||||
console.error("markLastCardAsAudioCard failed:", err);
|
console.error("markLastCardAsAudioCard failed:", err);
|
||||||
@@ -2644,6 +2647,9 @@ function registerOverlayShortcuts(): void {
|
|||||||
openRuntimeOptions: () => {
|
openRuntimeOptions: () => {
|
||||||
openRuntimeOptionsPalette();
|
openRuntimeOptionsPalette();
|
||||||
},
|
},
|
||||||
|
openJimaku: () => {
|
||||||
|
sendToVisibleOverlay("jimaku:open");
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -259,6 +259,11 @@ const electronAPI: ElectronAPI = {
|
|||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
onOpenJimaku: (callback: () => void) => {
|
||||||
|
ipcRenderer.on("jimaku:open", () => {
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
},
|
||||||
notifyOverlayModalClosed: (modal: "runtime-options" | "subsync") => {
|
notifyOverlayModalClosed: (modal: "runtime-options" | "subsync") => {
|
||||||
ipcRenderer.send("overlay:modal-closed", modal);
|
ipcRenderer.send("overlay:modal-closed", modal);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1953,7 +1953,6 @@ const CHORD_MAP = new Map<string, ChordAction>([
|
|||||||
["KeyR", { type: "mpv", command: ["script-message", "subminer-restart"] }],
|
["KeyR", { type: "mpv", command: ["script-message", "subminer-restart"] }],
|
||||||
["KeyC", { type: "mpv", command: ["script-message", "subminer-status"] }],
|
["KeyC", { type: "mpv", command: ["script-message", "subminer-status"] }],
|
||||||
["KeyY", { type: "mpv", command: ["script-message", "subminer-menu"] }],
|
["KeyY", { type: "mpv", command: ["script-message", "subminer-menu"] }],
|
||||||
["KeyJ", { type: "electron", action: () => openJimakuModal() }],
|
|
||||||
[
|
[
|
||||||
"KeyD",
|
"KeyD",
|
||||||
{ type: "electron", action: () => window.electronAPI.toggleDevTools() },
|
{ type: "electron", action: () => window.electronAPI.toggleDevTools() },
|
||||||
@@ -2398,6 +2397,9 @@ async function init(): Promise<void> {
|
|||||||
window.electronAPI.notifyOverlayModalClosed("runtime-options");
|
window.electronAPI.notifyOverlayModalClosed("runtime-options");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
window.electronAPI.onOpenJimaku(() => {
|
||||||
|
openJimakuModal();
|
||||||
|
});
|
||||||
window.electronAPI.onSubsyncManualOpen((payload: SubsyncManualPayload) => {
|
window.electronAPI.onSubsyncManualOpen((payload: SubsyncManualPayload) => {
|
||||||
openSubsyncModal(payload);
|
openSubsyncModal(payload);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -275,6 +275,7 @@ export interface ShortcutsConfig {
|
|||||||
toggleSecondarySub?: string | null;
|
toggleSecondarySub?: string | null;
|
||||||
markAudioCard?: string | null;
|
markAudioCard?: string | null;
|
||||||
openRuntimeOptions?: string | null;
|
openRuntimeOptions?: string | null;
|
||||||
|
openJimaku?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type JimakuLanguagePreference = "ja" | "en" | "none";
|
export type JimakuLanguagePreference = "ja" | "en" | "none";
|
||||||
@@ -606,6 +607,7 @@ export interface ElectronAPI {
|
|||||||
callback: (options: RuntimeOptionState[]) => void,
|
callback: (options: RuntimeOptionState[]) => void,
|
||||||
) => void;
|
) => void;
|
||||||
onOpenRuntimeOptions: (callback: () => void) => void;
|
onOpenRuntimeOptions: (callback: () => void) => void;
|
||||||
|
onOpenJimaku: (callback: () => void) => void;
|
||||||
notifyOverlayModalClosed: (modal: "runtime-options" | "subsync") => void;
|
notifyOverlayModalClosed: (modal: "runtime-options" | "subsync") => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user