chore: commit remaining docs and project updates

This commit is contained in:
2026-02-09 21:29:51 -08:00
parent a4df79feb1
commit 12d389728a
19 changed files with 2397 additions and 9 deletions

6
.gitignore vendored
View File

@@ -23,3 +23,9 @@ environment.toml
**/CLAUDE.md **/CLAUDE.md
.env .env
.vscode/* .vscode/*
# VitePress
.vitepress/cache/
.vitepress/dist/
docs/.vitepress/cache/
docs/.vitepress/dist/

View File

@@ -1,4 +1,4 @@
.PHONY: help deps build install build-linux build-macos build-macos-unsigned install-linux install-macos install-plugin uninstall uninstall-linux uninstall-macos print-dirs pretty ensure-pnpm generate-config generate-example-config .PHONY: help deps build install build-linux build-macos build-macos-unsigned install-linux install-macos install-plugin uninstall uninstall-linux uninstall-macos print-dirs pretty ensure-pnpm generate-config generate-example-config docs-dev docs-build docs-preview
APP_NAME := subminer APP_NAME := subminer
THEME_FILE := subminer.rasi THEME_FILE := subminer.rasi
@@ -48,6 +48,9 @@ help:
" build-linux Build Linux AppImage" \ " build-linux Build Linux AppImage" \
" build-macos Build macOS DMG/ZIP (signed if configured)" \ " build-macos Build macOS DMG/ZIP (signed if configured)" \
" build-macos-unsigned Build macOS DMG/ZIP without signing/notarization" \ " build-macos-unsigned Build macOS DMG/ZIP without signing/notarization" \
" docs-dev Run VitePress docs dev server" \
" docs-build Build VitePress static docs" \
" docs-preview Preview built VitePress docs" \
" install-linux Install Linux wrapper/theme/app artifacts" \ " install-linux Install Linux wrapper/theme/app artifacts" \
" install-macos Install macOS wrapper/theme/app artifacts" \ " install-macos Install macOS wrapper/theme/app artifacts" \
" install-plugin Install mpv Lua plugin and plugin config" \ " install-plugin Install mpv Lua plugin and plugin config" \
@@ -131,6 +134,15 @@ generate-example-config: ensure-pnpm
@pnpm run build @pnpm run build
@pnpm run generate:config-example @pnpm run generate:config-example
docs-dev: ensure-pnpm
@pnpm run docs:dev
docs-build: ensure-pnpm
@pnpm run docs:build
docs-preview: ensure-pnpm
@pnpm run docs:preview
install-linux: install-linux:
@printf '%s\n' "[INFO] Installing Linux wrapper/theme artifacts" @printf '%s\n' "[INFO] Installing Linux wrapper/theme artifacts"

View File

@@ -0,0 +1,53 @@
# Overlay Positioning Flow
## 1) Window Bounds Flow (visible + invisible Electron windows)
```mermaid
flowchart LR
A[Platform backend selection<br/>src/window-trackers/index.ts] --> B[Tracker emits geometry<br/>onWindowFound/onGeometryChange]
B --> C[updateOverlayBounds<br/>src/main.ts]
C --> D[mainWindow.setBounds]
C --> E[invisibleWindow.setBounds]
```
## 2) Invisible Subtitle Layout Flow (mpv render metrics -> DOM layout)
```mermaid
flowchart LR
A[mpv property changes<br/>sub-pos, sub-font-size, osd-dimensions, etc.] --> B[MpvIpcClient parses events<br/>src/main.ts]
B --> C[updateMpvSubtitleRenderMetrics<br/>src/main.ts]
C --> D[broadcast mpv-subtitle-render-metrics:set]
D --> E[preload onMpvSubtitleRenderMetrics<br/>src/preload.ts]
E --> F[renderer receives metrics event<br/>src/renderer/renderer.ts]
F --> G[applyInvisibleSubtitleLayoutFromMpvMetrics]
G --> H[subtitleContainer/subtitleRoot inline styles updated]
```
## 3) Visible Subtitle Manual Position Flow
```mermaid
flowchart LR
A[User right-click drags subtitle] --> B[setupDragging<br/>src/renderer/renderer.ts]
B --> C[applyYPercent]
C --> D[saveSubtitlePosition IPC]
D --> E[saveSubtitlePosition in main<br/>src/main.ts]
E --> F[load/broadcast subtitle-position:set]
F --> G[applyStoredSubtitlePosition in renderer]
```
## 4) Fallback Bounds Flow (tracker not ready)
```mermaid
flowchart LR
A[windowTracker exists but not tracking] --> B["screen.getDisplayNearestPoint(cursor)"]
B --> C[display.workArea]
C --> D[updateOverlayBounds]
```
## Key Files
- `src/main.ts`
- `src/renderer/renderer.ts`
- `src/preload.ts`
- `src/window-trackers/base-tracker.ts`
- `src/window-trackers/index.ts`

201
comparison.md Normal file
View File

@@ -0,0 +1,201 @@
# SubMiner vs Memento: Comparative Analysis
## Overview
| | **SubMiner** | **Memento** |
| ------------------ | ------------------------------------- | ---------------------------------------- |
| **Language** | TypeScript (Electron) | C++ (Qt6) |
| **Video Player** | External mpv (IPC socket) | Embedded libmpv (OpenGL widget) |
| **Dictionary** | Yomitan (embedded browser extension) | Own SQLite engine (Yomichan-format dicts) |
| **Platforms** | Linux (X11/Wayland), macOS | Linux, macOS, Windows |
| **Maturity** | Early (v0.1.0) | Mature (v1.7.2, AUR/Flathub packages) |
---
## Where Memento is Stronger
### 1. Self-Contained Native Application
Memento embeds mpv directly via libmpv, creating a seamless single-window experience. Player controls, subtitle display, and dictionary lookups all live in one cohesive application. SubMiner runs as a separate overlay process communicating with mpv via IPC, which introduces latency and compositor-specific complexity (Hyprland, Sway, X11 each need their own window-tracking backend).
### 2. Built-In Dictionary Engine
Memento has its own SQLite-backed dictionary engine that directly imports Yomichan zip files. No external browser extension needed. This gives Memento tighter control over the lookup UX — results appear inline, kanji cards show stroke order, pitch accent diagrams render natively. SubMiner delegates all dictionary rendering to Yomitan, so the lookup experience depends entirely on the external extension.
### 3. Deconjugation Engine
Memento has a full 90+ rule deconjugation engine as a fallback when MeCab isn't available. This means grammar-aware search works even without optional dependencies. SubMiner relies entirely on MeCab for morphological analysis and falls back to raw, unsegmented text if MeCab is unavailable.
### 4. Windows Support
Memento has fully documented Windows builds (MSYS2/MinGW), portable installs, and platform-specific troubleshooting docs. SubMiner doesn't ship Windows builds and the platform detection code is untested on Windows.
### 5. Kanji Detail Cards
Memento displays rich kanji detail cards with stroke order visualization, JLPT level, frequency rankings, classifications, and codepoints — all rendered natively in Qt. SubMiner delegates kanji display entirely to Yomitan.
### 6. Packaging & Distribution
Memento is available on AUR, Flathub, and provides pre-built binaries for all three platforms. It's an established project with a known user base and mature release process.
### 7. OCR Support
Memento supports optional OCR via manga-ocr for extracting text from on-screen images (useful for hardsubbed content or manga). SubMiner has no OCR capability.
### 8. Resource Efficiency
As a native C++/Qt application, Memento has a significantly smaller memory footprint than SubMiner's Electron-based architecture, which typically consumes 150-300MB of RAM for the Chromium runtime alone.
---
## Where SubMiner is Stronger
### 1. Anki Integration Depth
SubMiner's Anki workflow is significantly more advanced:
- **Automatic polling** detects new cards and auto-populates media fields (no manual trigger needed after the initial mine action)
- **Kiku field grouping** detects duplicate words across cards and merges them into grouped cards with a two-step preview modal
- **Dual note type support** (Lapis sentence cards + Kiku vocabulary cards) with independent field mappings
- **AI translation fallback** uses OpenAI-compatible APIs (OpenRouter, local models) to generate translations when secondary subtitles are unavailable
Memento's Anki integration is capable but more traditional: manual "Add to Anki" buttons with configurable templates and no automation beyond the initial card creation.
### 2. Subtitle Timing Intelligence
SubMiner maintains a subtitle timing cache (200-entry LRU with 5-minute TTL) and uses fuzzy edit-distance matching to locate audio segments for media extraction. This means audio clips are precisely timed even when card creation is delayed or when the subtitle text has been slightly modified. Memento captures the current frame and audio at the moment of card creation.
### 3. Multi-Copy & Batch Mining
SubMiner supports multi-copy mode (`Ctrl+Shift+C` to batch-copy N recent subtitle lines) and multi-mine mode (`Ctrl+Alt+S`). This is useful for dialogue-heavy scenes where you want context spanning multiple subtitle lines in a single card.
### 4. YouTube Subtitle Generation
SubMiner has built-in Whisper integration for generating subtitles from YouTube videos that lack them. Three modes are available: automatic (generate on-the-fly), preprocess (generate before playback), and off. Memento uses yt-dlp for streaming but has no subtitle generation capability for videos without existing subtitles.
### 5. Jimaku Integration
SubMiner connects to the Jimaku anime subtitle API for searching and downloading subtitle files directly from the overlay UI. This streamlines the workflow of finding Japanese subtitles for anime. Memento has no equivalent feature.
### 6. Subtitle Sync (Subsync)
SubMiner integrates with alass and ffsubsync for in-session subtitle resynchronization, with a modal UI for selecting the sync engine and source audio track. Memento relies on mpv's native subtitle delay controls, which only handle constant offsets rather than non-linear drift.
### 7. Texthooker / WebSocket Broadcasting
SubMiner runs a texthooker HTTP server (port 5174) and a WebSocket server (port 6677), broadcasting subtitles and tokenization data to external tools in real-time. This enables integration with browser-based dictionaries, other mining tools, or custom automation workflows. Memento is fully self-contained with no external broadcast mechanism.
### 8. AI Translation
SubMiner can call OpenAI-compatible APIs to generate translations on-the-fly when secondary subtitles aren't available. This includes configurable system prompts, target language selection, and auto-retry with exponential backoff. Memento has no AI or machine translation capability.
### 9. Runtime Options Modal
SubMiner has a `Ctrl+Shift+O` palette for toggling session-only options (auto-update cards, field grouping, etc.) without editing config files or restarting. Memento requires navigating through its settings dialog for configuration changes.
### 10. Overlay Architecture
SubMiner's transparent overlay design means it works with any existing mpv instance — users keep their mpv configuration, plugins, shaders, and keybindings untouched. Memento requires using its own player, and while it supports mpv config files, they must be placed in Memento's separate config directory.
---
## Where They're Comparable
| Feature | SubMiner | Memento |
| ------------------------ | ------------------------------------- | ------------------------------------- |
| MeCab tokenization | Yes (optional) | Yes (optional) |
| Secondary subtitles | Yes (3 display modes) | Yes (requires mpv >= 0.35) |
| Configurable styling | CSS variables in config | Qt stylesheet + settings dialog |
| mpv config support | Via mpv directly (user's own config) | Separate Memento config directory |
| AnkiConnect protocol | Yes | Yes |
| Media capture | FFmpeg (audio + image/AVIF) | mpv screenshot + audio extraction |
| Yomichan dict compat | Via Yomitan extension | Native import of Yomichan zip files |
| Pitch accent display | Via Yomitan | Native rendering |
| Streaming (yt-dlp) | Yes | Yes |
---
## Architectural Trade-offs
### Overlay vs Integrated App
| Aspect | SubMiner (Overlay) | Memento (Integrated) |
| --------------- | ------------------------------------------------------------ | ------------------------------------------------------- |
| UX cohesion | Separate window; requires compositor cooperation | Single window; everything in one place |
| mpv flexibility | Works with any mpv instance and config | Must use Memento's player; separate config directory |
| Latency | IPC socket introduces small delay | Direct libmpv calls; near-zero latency |
| Platform quirks | Needs per-compositor window tracking (Hyprland/Sway/X11) | Qt handles platform abstraction |
### Electron vs Native C++/Qt
| Aspect | SubMiner (Electron/TypeScript) | Memento (C++/Qt6) |
| ------------------ | ------------------------------------------- | ----------------------------------------- |
| Dev velocity | Rapid iteration, rich npm ecosystem | Slower development, manual memory mgmt |
| Memory footprint | ~150-300MB (Chromium runtime) | ~30-80MB typical |
| Startup time | Slower (Chromium spin-up) | Fast native startup |
| Dependency weight | Node modules + Electron binary (~200MB) | System libraries (Qt, mpv, SQLite) |
| UI flexibility | Full HTML/CSS/JS; easy to style | Qt widgets; powerful but more constrained |
### External Yomitan vs Built-In Dictionary
| Aspect | SubMiner (Yomitan) | Memento (Built-in) |
| ------------------ | ------------------------------------------------- | --------------------------------------------- |
| Maintenance burden | Free updates from Yomitan project | Must maintain dictionary engine in-house |
| UX control | Limited to Yomitan's popup behavior | Full control over lookup UI and rendering |
| Setup complexity | Requires Yomitan extension + dictionaries | Import dictionaries directly; no extension |
| Feature parity | Gets all Yomitan features automatically | Must implement each feature (pitch, kanji) |
---
## Code Quality Comparison
### SubMiner
**Strengths:**
- Clean TypeScript with strict mode
- Centralized config registry with validation and example generation
- Robust error handling with exponential backoff retry logic
- Good separation of concerns (tokenization, media generation, Anki polling)
**Weaknesses:**
- Monolithic `main.ts` at ~5,000 lines — could benefit from modularization
- Limited test coverage (only `config.test.ts` exists)
- Token merging heuristics are complex (14 helper functions) and may miss edge cases
### Memento
**Strengths:**
- Modular C++ architecture with clear directory structure (dict/, player/, gui/, anki/)
- Qt signal/slot pattern keeps components decoupled
- Thread-safe dictionary access via `QReadWriteLock`
- Smart pointer usage throughout; no obvious memory management issues
- Settings migration system for version upgrades
**Weaknesses:**
- 90+ hardcoded deconjugation rules need manual maintenance for new patterns
- No visible test suite in the repository
- MpvWidget property map handles 40+ properties in a single class
---
## Summary
**Memento** is a more mature, polished, self-contained desktop application. Its strength is the integrated experience — one app that handles video playback, dictionary lookups, and Anki card creation in a single native window. It has better Windows support, a built-in dictionary engine with deconjugation fallback, kanji stroke-order cards, and OCR support. It's the safer choice for a user who wants something that works out of the box with minimal setup.
**SubMiner** is more innovative in its Anki workflow automation, mining UX, and external integrations. Features like automatic card polling, Kiku field grouping, AI translation fallback, Whisper subtitle generation, Jimaku subtitle search, subsync integration, and WebSocket broadcasting are all capabilities Memento doesn't have. The overlay architecture also means users can keep their existing mpv setup completely untouched.
### Key Memento Advantages to Learn From
- Built-in deconjugation fallback (grammar awareness without MeCab)
- OCR support for non-subtitle text
- Windows platform support
- Self-contained dictionary engine (no external browser extension dependency)
- Lower resource footprint as a native application
### Key SubMiner Advantages to Protect
- Anki automation depth (polling, field grouping, AI translation)
- Subtitle intelligence (timing cache, Whisper generation, Jimaku, subsync)
- External tool interop (WebSocket, texthooker server)
- Overlay model (works with any mpv instance and configuration)
- Rapid feature development enabled by TypeScript/Electron

324
composability.md Normal file
View File

@@ -0,0 +1,324 @@
# SubMiner Composability and Extensibility Plan
## Goals
- Reduce coupling concentrated in `src/main.ts`.
- Make new features additive (new files/modules) instead of invasive (edits across many files).
- Standardize extension points for trackers, subtitle processing, IPC actions, and integrations.
- Preserve existing behavior while incrementally migrating architecture.
## Current Constraints (From Existing Code)
- `src/main.ts` is the effective integration bus (~5k LOC) and mixes lifecycle, IPC, shortcuts, mpv integration, overlay control, and feature flows.
- Input surfaces are fragmented (CLI args, global shortcuts, renderer IPC) and often route through separate logic paths.
- Some extension points already exist (`src/window-trackers/*`, centralized config definitions in `src/config/definitions.ts`) but still use hardcoded selection/wiring.
- Type contracts are duplicated between main/preload/renderer in places, which increases drift risk.
## Target Architecture
### 1. Composition Root + Services
- Keep `src/main.ts` as bootstrap only.
- Move behavior into focused services:
- `src/core/app-orchestrator.ts`
- `src/core/services/overlay-service.ts`
- `src/core/services/mpv-service.ts`
- `src/core/services/shortcut-service.ts`
- `src/core/services/ipc-service.ts`
- `src/core/services/subtitle-service.ts`
### 2. Module System
- Introduce module contract:
- `src/core/module.ts`
- `src/core/module-registry.ts`
- Built-in features become modules:
- anki module
- jimaku module
- runtime options module
- texthooker/websocket module
- subsync module
### 3. Action Bus
- Add typed action dispatch (single command path for CLI/shortcut/IPC):
- `src/core/actions.ts` (action type map)
- `src/core/action-bus.ts` (register/dispatch)
- All triggers emit actions; business logic subscribes once.
### 4. Provider Registries (Strategy Pattern)
- Replace hardcoded switch/if wiring with registries:
- tracker providers
- tokenizer providers
- translation providers
- subsync backend providers
### 5. Shared IPC Contract
- Single source of truth for IPC channels and payloads:
- `src/ipc/contract.ts`
- `src/ipc/main-api.ts`
- `src/ipc/renderer-api.ts`
- `preload.ts` and renderer consume typed wrappers instead of ad hoc channel strings.
### 6. Subtitle Pipeline
- Middleware/stage pipeline:
- ingest -> normalize -> tokenize -> merge -> enrich -> emit
- Files:
- `src/subtitle/pipeline.ts`
- `src/subtitle/stages/*.ts`
### 7. Module-Owned Config Extensions
- Keep `src/config/definitions.ts` as the central resolved output.
- Add registration path so modules can contribute config/runtime option metadata and defaults before final resolution.
## Phased Delivery Plan
## Phase 0: Baseline and Safety Net
### Scope
- Add architecture docs and migration guardrails.
- Increase tests around currently stable behavior before structural changes.
### Changes
- Add architecture decision notes:
- `docs/development.md` (new section: architecture/migration)
- `docs/architecture.md` (new)
- Add basic smoke tests for command routing, overlay visibility toggles, and config load behavior.
### Exit Criteria
- Existing behavior documented with acceptance checklist.
- CI/build + config tests green.
## Phase 1: Extract I/O Surfaces
### Scope
- Isolate IPC and global shortcut registration from `src/main.ts` without changing semantics.
### Changes
- Create:
- `src/core/services/ipc-service.ts`
- `src/core/services/shortcut-service.ts`
- Move `ipcMain.handle/on` registration blocks from `src/main.ts` into `IpcService.register(...)`.
- Move global shortcut registration into `ShortcutService.registerGlobal(...)`.
- Keep old entrypoints in `main.ts` delegating to new service methods.
### Exit Criteria
- No user-visible behavior changes.
- `src/main.ts` shrinks materially.
- Manual verification:
- overlay toggles
- copy/mine shortcuts
- runtime options open/cycle
## Phase 2: Introduce Action Bus
### Scope
- Canonicalize command execution path.
### Changes
- Add typed action bus (`src/core/action-bus.ts`, `src/core/actions.ts`).
- Convert these triggers to dispatch actions instead of direct calls:
- CLI handling (`handleCliCommand` path)
- shortcut handlers
- IPC command handlers
- Keep existing business functions as subscribers initially.
### Exit Criteria
- One action path per command (no duplicated behavior branches).
- Unit tests for command mapping (CLI -> action, shortcut -> action, IPC -> action).
## Phase 3: Module Runtime Skeleton
### Scope
- Add module contract and migrate one low-risk feature first.
### Changes
- Introduce:
- `src/core/module.ts`
- `src/core/module-registry.ts`
- `src/core/app-context.ts`
- First migration target: runtime options
- create `src/modules/runtime-options/module.ts`
- wire existing `RuntimeOptionsManager` through module lifecycle.
### Exit Criteria
- Runtime options fully owned by module.
- Core app can start with module list and deterministic module init order.
## Phase 4: Provider Registries
### Scope
- Remove hardcoded provider selection.
### Changes
- Window tracker:
- Replace switch in `src/window-trackers/index.ts` with registry API.
- Add provider objects for hyprland/sway/x11/macos.
- Tokenizer/translator/subsync:
- Add analogous registries in new provider directories.
### Exit Criteria
- Adding a provider requires adding one file + registration line.
- No edits required in composition root for new provider variants.
## Phase 5: Shared IPC Contract
### Scope
- Eliminate channel/payload drift between main/preload/renderer.
### Changes
- Add typed IPC contract files under `src/ipc/`.
- Refactor `src/preload.ts` to generate API from shared contract wrappers.
- Refactor renderer calls to consume typed API interface from shared module.
### Exit Criteria
- Channel names declared once.
- Compile-time checking across main/preload/renderer for payload mismatch.
## Phase 6: Subtitle Pipeline
### Scope
- Extract subtitle transformations into composable stages.
### Changes
- Create pipeline framework and migrate existing tokenization/merge flow:
- stage: normalize subtitle
- stage: tokenize (`MecabTokenizer` adapter)
- stage: merge tokens (`mergeTokens` adapter)
- stage: post-processing/enrichment hooks
- Integrate pipeline execution into subtitle update loop.
### Exit Criteria
- Current output parity for tokenization/merged token behavior.
- Stage-level tests for deterministic input/output.
## Phase 7: Moduleized Integrations
### Scope
- Convert major features to modules in descending complexity.
### Migration Order
1. Jimaku
2. Texthooker/WebSocket bridge
3. Subsync
4. Anki integration
### Exit Criteria
- Each feature has independent init/start/stop lifecycle.
- App startup composes modules instead of hardcoded inline setup.
## Recommended File Layout (Target)
```text
src/
core/
app-orchestrator.ts
app-context.ts
actions.ts
action-bus.ts
module.ts
module-registry.ts
services/
ipc-service.ts
shortcut-service.ts
overlay-service.ts
mpv-service.ts
subtitle-service.ts
modules/
runtime-options/module.ts
jimaku/module.ts
texthooker/module.ts
subsync/module.ts
anki/module.ts
ipc/
contract.ts
main-api.ts
renderer-api.ts
subtitle/
pipeline.ts
stages/
normalize.ts
tokenize-mecab.ts
merge-default.ts
enrich.ts
```
## PR Breakdown (Suggested)
1. PR1: Phase 1 (`IpcService`, `ShortcutService`, no behavior changes)
2. PR2: Phase 2 (action bus + trigger mapping)
3. PR3: Phase 3 (module skeleton + runtime options module)
4. PR4: Phase 4 (window tracker registry + provider pattern)
5. PR5: Phase 5 (shared IPC contract)
6. PR6: Phase 6 (subtitle pipeline)
7. PR7+: Phase 7 (feature-by-feature module migrations)
## Validation Matrix
- Build/typecheck:
- `pnpm run build`
- Config correctness:
- `pnpm run test:config`
- Manual checks per PR:
- app start/stop/toggle CLI
- visible/invisible overlay control
- global shortcuts
- subtitle render and token display
- runtime options open + cycle
- anki mine flow + update-from-clipboard
## Backward Compatibility Rules
- Preserve existing CLI flags and IPC channel behavior during migration.
- Maintain config shape compatibility; only additive changes unless versioned migration is added.
- Keep default behavior equivalent for all supported compositor backends.
## Key Risks and Mitigations
- Risk: behavior regressions during extraction.
- Mitigation: move code verbatim first, refactor second; keep thin delegates in `main.ts` until stable.
- Risk: module lifecycle race conditions.
- Mitigation: explicit init order, idempotent `start/stop`, and startup dependency checks.
- Risk: IPC contract churn breaks renderer.
- Mitigation: contract wrappers and compile-time types; temporary compatibility adapters.
- Risk: feature coupling around anki/mine flows.
- Mitigation: defer high-coupling module migrations until action bus and shared app context are stable.
## Definition of Done (Program-Level)
- `src/main.ts` reduced to bootstrap/composition responsibilities.
- New feature prototype can be added as an isolated module with:
- module file
- optional provider registration
- optional config schema registration
- no invasive edits across unrelated subsystems
- IPC and command routing are single-path and typed.
- Existing user workflows remain intact.

47
docs/.vitepress/config.ts Normal file
View File

@@ -0,0 +1,47 @@
const repositoryName = process.env.GITHUB_REPOSITORY?.split('/')[1];
const base = process.env.GITHUB_ACTIONS && repositoryName ? `/${repositoryName}/` : '/';
export default {
title: 'SubMiner',
description: 'Documentation for SubMiner',
base,
appearance: 'dark',
cleanUrls: true,
lastUpdated: true,
markdown: {
theme: {
light: 'catppuccin-latte',
dark: 'catppuccin-macchiato',
},
},
themeConfig: {
logo: '/assets/SubMiner.png',
nav: [
{ text: 'Docs', link: '/' },
{ text: 'Installation', link: '/installation' },
{ text: 'Usage', link: '/usage' },
{ text: 'Configuration', link: '/configuration' },
{ text: 'Development', link: '/development' },
{ text: 'Architecture', link: '/architecture' },
],
sidebar: [
{
text: 'Getting Started',
items: [
{ text: 'Overview', link: '/' },
{ text: 'Installation', link: '/installation' },
{ text: 'Usage', link: '/usage' },
],
},
{
text: 'Reference',
items: [
{ text: 'Configuration', link: '/configuration' },
{ text: 'Development', link: '/development' },
{ text: 'Architecture', link: '/architecture' },
],
},
],
socialLinks: [{ icon: 'github', link: 'https://github.com/ksyasuda/SubMiner' }],
},
};

View File

@@ -0,0 +1,4 @@
import DefaultTheme from 'vitepress/theme';
import '@catppuccin/vitepress/theme/macchiato/mauve.css';
export default DefaultTheme;

View File

@@ -2,17 +2,36 @@
Use this directory for detailed SubMiner documentation. Use this directory for detailed SubMiner documentation.
- [Installation](installation.md) ## Local Docs Site
Run the VitePress docs site locally:
```bash
pnpm run docs:dev
```
Build static docs output:
```bash
pnpm run docs:build
```
- [Installation](/installation)
- Platform requirements - Platform requirements
- AppImage / macOS / source installs - AppImage / macOS / source installs
- mpv plugin setup - mpv plugin setup
- [Usage](usage.md) - [Usage](/usage)
- Script vs plugin workflow - Script vs plugin workflow
- Running SubMiner with mpv - Running SubMiner with mpv
- Keybindings and runtime behavior - Keybindings and runtime behavior
- [Configuration](configuration.md) - [Configuration](/configuration)
- Full config file reference and option details - Full config file reference and option details
- [Development](development.md) - [Development](/development)
- Contributor notes - Contributor notes
- Architecture migration overview
- Environment variables - Environment variables
- License and acknowledgments - License and acknowledgments
- [Architecture](/architecture)
- Composability migration status
- Core runtime structure
- Extension design rules

51
docs/architecture.md Normal file
View File

@@ -0,0 +1,51 @@
# Architecture
SubMiner is migrating from a single, monolithic `src/main.ts` runtime toward a composable architecture with clear extension points.
## Current Structure
- `src/main.ts`: bootstrap/composition root plus remaining legacy orchestration.
- `src/core/`: shared runtime primitives:
- `app-orchestrator.ts`: lifecycle wiring for ready/activate/quit hooks.
- `action-bus.ts`: typed action dispatch path.
- `actions.ts`: canonical app action types.
- `module.ts` / `module-registry.ts`: module lifecycle contract.
- `services/`: extracted runtime services (IPC, shortcuts, subtitle, overlay, MPV command routing).
- `src/ipc/`: shared IPC contract + wrappers used by main, preload, and renderer.
- `src/subtitle/`: staged subtitle pipeline (`normalize` -> `tokenize` -> `merge`).
- `src/modules/`: feature modules:
- `runtime-options`
- `jimaku`
- `subsync`
- `anki`
- `texthooker`
- provider registries:
- `src/window-trackers/index.ts`
- `src/tokenizers/index.ts`
- `src/token-mergers/index.ts`
- `src/translators/index.ts`
- `src/subsync/engines.ts`
## Migration Status
- Completed:
- Action bus wired for CLI, shortcuts, and IPC-triggered commands.
- Command/action mapping covered by focused core tests.
- Shared IPC channel contract adopted across main/preload/renderer.
- Runtime options extracted into module lifecycle.
- Provider registries replace hardcoded backend selection.
- Subtitle tokenization/merge/enrich flow moved to staged pipeline.
- Stage-level subtitle pipeline tests added for deterministic behavior.
- Jimaku, Subsync, Anki, and texthooker/websocket flows moduleized.
- In progress:
- Further shrink `src/main.ts` by moving orchestration into dedicated services/orchestrator files.
- Continue moduleizing remaining integrations with complex lifecycle coupling.
## Design Rules
- New feature behavior should be added as:
- a module (`src/modules/*`) and/or
- a provider registration (`src/*/index.ts` registries),
instead of editing unrelated branches in `src/main.ts`.
- New command triggers should dispatch `AppAction` and reuse existing action handlers.
- New IPC channels should be added only via `src/ipc/contract.ts`.

View File

@@ -2,6 +2,10 @@
To add or change a config option, update `src/config/definitions.ts` first. Defaults, runtime-option metadata, and generated `config.example.jsonc` are derived from this centralized source. To add or change a config option, update `src/config/definitions.ts` first. Defaults, runtime-option metadata, and generated `config.example.jsonc` are derived from this centralized source.
## Architecture
The composability migration state and extension-point guidelines are documented in [`architecture.md`](/architecture).
## Environment Variables ## Environment Variables
| Variable | Description | | Variable | Description |

43
docs/index.md Normal file
View File

@@ -0,0 +1,43 @@
---
layout: home
title: SubMiner
tagline: All-in-one sentence mining overlay for MPV with AnkiConnect and dictionary integration
hero:
name: SubMiner
text: Documentation
tagline: Install, configure, and use SubMiner for subtitle-driven mining workflows.
image:
src: /assets/SubMiner.png
alt: SubMiner logo
actions:
- theme: brand
text: Installation
link: /installation
- theme: alt
text: Configuration
link: /configuration
- theme: alt
text: Development
link: /development
- theme: alt
text: Architecture
link: /architecture
features:
- title: End-to-end workflow
details: Connect mpv subtitle capture, Yomitan lookup, and Anki card generation in one overlay.
- title: Practical configuration
details: Use the complete option reference to tune behavior for mining, media generation, and keybindings.
- title: Contributor docs
details: Build, test, and package SubMiner with the development notes in this docs set.
---
## Documentation Sections
- [Installation](/installation)
- [Usage](/usage)
- [Configuration](/configuration)
- [Development](/development)
- [Architecture](/architecture)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

1617
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,10 +3,17 @@ import * as path from "path";
import { DEFAULT_CONFIG, generateConfigTemplate } from "./config"; import { DEFAULT_CONFIG, generateConfigTemplate } from "./config";
function main(): void { function main(): void {
const outputPath = path.join(process.cwd(), "config.example.jsonc");
const template = generateConfigTemplate(DEFAULT_CONFIG); const template = generateConfigTemplate(DEFAULT_CONFIG);
fs.writeFileSync(outputPath, template, "utf-8"); const outputPaths = [
console.log(`Generated ${outputPath}`); path.join(process.cwd(), "config.example.jsonc"),
path.join(process.cwd(), "docs", "public", "config.example.jsonc"),
];
for (const outputPath of outputPaths) {
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
fs.writeFileSync(outputPath, template, "utf-8");
console.log(`Generated ${outputPath}`);
}
} }
main(); main();

View File

@@ -32,7 +32,7 @@ export function detectCompositor(): Compositor {
if (process.platform === "darwin") return "macos"; if (process.platform === "darwin") return "macos";
if (process.env.HYPRLAND_INSTANCE_SIGNATURE) return "hyprland"; if (process.env.HYPRLAND_INSTANCE_SIGNATURE) return "hyprland";
if (process.env.SWAYSOCK) return "sway"; if (process.env.SWAYSOCK) return "sway";
if (process.env.XDG_SESSION_TYPE === "x11") return "x11"; if (process.platform === "linux") return "x11";
return null; return null;
} }