Update docs and gitignore changes

This commit is contained in:
2026-02-16 19:26:34 -08:00
parent 48f93f4344
commit 23b78e6c9b
4 changed files with 179 additions and 267 deletions

2
.gitignore vendored
View File

@@ -29,7 +29,7 @@ environment.toml
.vitepress/dist/
docs/.vitepress/cache/
docs/.vitepress/dist/
test/*
tests/*
.worktrees/
.codex/*
.agents/*

149
README.md
View File

@@ -1,64 +1,37 @@
<div align="center">
<img src="assets/SubMiner.png" width="169" alt="SubMiner logo">
<h1>SubMiner</h1>
<p>Immersion mining overlay for mpv — look up words, mine to Anki, and enrich cards with context without leaving the video.</p>
</div>
An all-in-one immersion mining overlay for MPV with AnkiConnect and dictionary (Yomitan) integration.
---
## What This Project Is For
SubMiner is for Japanese learners who watch subtitled content in mpv and want a low-friction mining loop:
- stay inside the video while doing lookups
- mine to Anki quickly without manual copy/paste workflows
- preserve card context (sentence, audio, screenshot, translation, metadata)
- reduce tool switching between player, dictionary, and card workflow
## Project Goals
1. Keep immersion continuous by making lookup and mining happen over mpv subtitles.
2. Preserve card quality with context-rich media and subtitle timing.
3. Support real daily workflows (subtitle management, sync, known-word awareness, keyboard-first controls).
4. Stay configurable with sensible defaults and advanced customization.
5. Evolve quickly and safely with a TypeScript codebase and automated tests that make refactors easier to ship.
## Who It's For
- learners using mpv as their primary immersion player
- users already working with Yomitan and AnkiConnect
- miners who care about long-term card quality, not just quick word capture
SubMiner is likely overkill if you only want lightweight dictionary lookup without card enrichment or integrated workflow tools.
[![Demo](./assets/demo-poster.jpg)](https://github.com/user-attachments/assets/9235a554-ea51-4284-b14b-7bbf3defaf58)
## Features
- Real-time subtitle display from MPV via IPC socket
- Yomitan integration for fast, on-screen lookups
- Japanese text tokenization using MeCab with smart word boundary detection
- Integrated texthooker-ui server for use with Yomitan
- Integrated WebSocket server (if [mpv_websocket](https://github.com/kuroahna/mpv_websocket) is not found) to send lines to the texthooker
- AnkiConnect integration for automatic card creation with media (audio/image)
- Invisible subtitle position edit mode (`Ctrl/Cmd+Shift+P`, arrow keys to adjust, `Enter`/`Ctrl+S` save, `Esc` cancel)
## Demo
[![Demo screenshot](./assets/demo-poster.jpg)](https://github.com/user-attachments/assets/9235a554-ea51-4284-b14b-7bbf3defaf58)
- **Yomitan Integration** — Hover subtitle words to trigger dictionary lookups in the player
- **Anki Card Enrichment** — Fills sentence, audio, screenshot, and translation on new cards automatically
- **Dual-Layer Subtitles** — Interactive visible overlay + invisible click-through layer aligned with mpv rendering
- **N+1 Highlighting** — Marks known vocabulary from your Anki deck so you can spot new words at a glance
- **Texthooker & WebSocket** — Built-in texthooker page with WebSocket streaming for external tools
- **Subtitle Download & Sync** — Search Jimaku, sync with alass or ffsubsync — all from the player
- **Keyboard-Driven** — Mine, copy, cycle display modes, and navigate from configurable shortcuts
- **Japanese Tokenization** — MeCab-powered word boundary detection with smart grouping
## Requirements
- `mpv` with IPC socket support
- `mecab` and `mecab-ipadic` (recommended for Japanese tokenization)
- `mecab` and `mecab-ipadic`
- Linux: Hyprland (`hyprctl`) or X11 (`xdotool` + `xwininfo`)
- macOS: Accessibility permission for window tracking
Optional but recommended: `yt-dlp`, `fzf`, `rofi`, `chafa`, `ffmpegthumbnailer`.
Optional: `yt-dlp`, `fzf`, `rofi`, `chafa`, `ffmpegthumbnailer`
## Install
### Linux (AppImage)
Download the latest release from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest):
```bash
wget https://github.com/ksyasuda/SubMiner/releases/download/v0.1.0/SubMiner-0.1.0.AppImage -O ~/.local/bin/SubMiner.AppImage
chmod +x ~/.local/bin/SubMiner.AppImage
@@ -73,49 +46,34 @@ The `subminer` wrapper uses a [Bun](https://bun.sh) shebang, so `bun` must be on
```bash
git clone --recurse-submodules https://github.com/ksyasuda/SubMiner.git
cd SubMiner
make build
make install
make build && make install
```
If you already cloned without submodules:
```bash
cd SubMiner
git submodule update --init --recursive
```
For macOS builds, signing, and platform-specific details, see [docs/installation.md](docs/installation.md).
For macOS builds and platform details, see the [installation docs](docs/installation.md).
## Quick Start
1. Copy and customize [`config.example.jsonc`](config.example.jsonc) to `$XDG_CONFIG_HOME/SubMiner/config.jsonc` (or `~/.config/SubMiner/config.jsonc`).
2. Start mpv with IPC enabled:
```bash
mpv --input-ipc-server=/tmp/subminer-socket video.mkv
```
1. Copy [`config.example.jsonc`](config.example.jsonc) to `~/.config/SubMiner/config.jsonc`
2. Start mpv with IPC:
```bash
mpv --input-ipc-server=/tmp/subminer-socket video.mkv
```
3. Launch SubMiner:
```bash
subminer video.mkv
```
```bash
subminer video.mkv
# or
subminer https://youtu.be/...
subminer # pick video from cwd (fzf)
subminer -R # rofi picker
subminer -d ~/Videos # set source directory
subminer -r -d ~/Anime # recursive search
subminer -p gpu-hq video.mkv # override mpv profile
subminer -T video.mkv # disable texthooker
subminer https://youtu.be/... # YouTube playback
```
## Common Commands
```bash
subminer # pick video from current dir (fzf)
subminer -R # use rofi picker
subminer -d ~/Videos # set source directory
subminer -r -d ~/Anime # recursive search
subminer video.mkv # launch with default mpv profile (subminer)
subminer -p gpu-hq video.mkv # override mpv profile
subminer -T video.mkv # disable texthooker
```
## MPV Plugin (Optional)
## MPV Plugin
```bash
cp plugin/subminer.lua ~/.config/mpv/scripts/
@@ -123,46 +81,23 @@ cp plugin/subminer.conf ~/.config/mpv/script-opts/
# or: make install-plugin
```
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).
Overlay Jimaku shortcut default: `Ctrl+Shift+J` (`shortcuts.openJimaku`).
Default chord prefix: `y` (`y-y` menu, `y-s` start, `y-S` stop, `y-t` toggle overlay).
Jimaku shortcut: `Ctrl+Shift+J`.
## Documentation
Detailed guides live in [`docs/`](docs/README.md):
Full guides at [**docs/**](docs/README.md):
[Installation](docs/installation.md) · [Usage](docs/usage.md) · [Mining Workflow](docs/mining-workflow.md) · [Configuration](docs/configuration.md) · [Anki Integration](docs/anki-integration.md) · [MPV Plugin](docs/mpv-plugin.md) · [Troubleshooting](docs/troubleshooting.md) · [Architecture](docs/architecture.md)
- [Installation](docs/installation.md) — Platform requirements, AppImage/macOS/source installs, mpv plugin
- [Usage](docs/usage.md) — Script vs plugin workflow, keybindings, YouTube playback
- [Mining Workflow](docs/mining-workflow.md) — End-to-end mining guide, overlay layers, card creation
- [Configuration](docs/configuration.md) — Full config reference and option details
- [Anki Integration](docs/anki-integration.md) — AnkiConnect setup, field mapping, media generation
- [MPV Plugin](docs/mpv-plugin.md) — Chord keybindings, subminer.conf options, script messages
- [Troubleshooting](docs/troubleshooting.md) — Common issues and solutions
- [Development](docs/development.md) — Building, testing, contributing
- [Architecture](docs/architecture.md) — Service-oriented design, composition model, and modular renderer layout (`src/renderer/{modals,handlers,utils,...}`)
## Acknowledgments
### Third-Party Components
This project includes the following third-party components:
- **[Yomitan](https://github.com/yomidevs/yomitan)** - Pop-up dictionary
- **[texthooker-ui](https://github.com/ksyasuda/texthooker-ui/tree/subminer)** - Texthooker Page
- **[yomitan-jlpt-vocab](https://github.com/stephenmk/yomitan-jlpt-vocab)** - JLPT Yomitan Dictionary
- **[Jiten Frequency Dictionary](https://jiten.moe/)** - Frequency Dictionary
### Acknowledgments
- **[GameSentenceMiner](https://github.com/bpwhelan/GameSentenceMiner)** — Inspiration for the subtitle overlay and Yomitan integration
- **[Jimaku.cc](https://jimaku.cc)** — Japanese subtitle provider
This project cherry-picks features from the following MPV scripts, ported to TypeScript:
- **[mpvacious](https://github.com/Ajatt-Tools/mpvacious)** — Sentence mining, screenshotting, and card updating logic
- **[Anacreon-Script (animecards)](https://github.com/friedrich-de/Anacreon-Script)** — Copy/paste to card update flow
- **[autosubsync-mpv](https://github.com/joaquintorres/autosubsync-mpv)** — Subtitle synchronization
- [GameSentenceMiner](https://github.com/bpwhelan/GameSentenceMiner) — Inspiration for the overlay and Yomitan integration
- [Jimaku.cc](https://jimaku.cc) — Japanese subtitle provider
- [mpvacious](https://github.com/Ajatt-Tools/mpvacious), [Anacreon-Script](https://github.com/friedrich-de/Anacreon-Script), [autosubsync-mpv](https://github.com/joaquintorres/autosubsync-mpv) — Mining and sync logic ported to TypeScript
**Third-party:**
[Yomitan](https://github.com/yomidevs/yomitan) · [texthooker-ui](https://github.com/ksyasuda/texthooker-ui/tree/subminer) · [yomitan-jlpt-vocab](https://github.com/stephenmk/yomitan-jlpt-vocab) · [Jiten Frequency Dictionary](https://jiten.moe/)
## License
GNU General Public License v3.0. See [LICENSE](LICENSE).
[GNU General Public License v3.0](LICENSE)

View File

@@ -84,53 +84,63 @@ src/renderer/
## Flow Diagram
The main process has three layers: `main.ts` delegates to composition modules that wire together domain services. The renderer runs in a separate Electron process, connected through `preload.ts`.
```mermaid
flowchart TD
classDef root fill:#c6a0f6,stroke:#24273a,color:#24273a,stroke-width:2px
classDef comp fill:#b7bdf8,stroke:#24273a,color:#24273a,stroke-width:1.5px
classDef svc fill:#8aadf4,stroke:#24273a,color:#24273a,stroke-width:1.5px
classDef ext fill:#a6da95,stroke:#24273a,color:#24273a,stroke-width:1.5px
classDef entry fill:#c6a0f6,stroke:#363a4f,color:#24273a,stroke-width:2px
classDef comp fill:#b7bdf8,stroke:#363a4f,color:#24273a,stroke-width:1.5px
classDef svc fill:#8aadf4,stroke:#363a4f,color:#24273a,stroke-width:1.5px
classDef bridge fill:#f5a97f,stroke:#363a4f,color:#24273a,stroke-width:1.5px
classDef rend fill:#8bd5ca,stroke:#363a4f,color:#24273a,stroke-width:1.5px
classDef ext fill:#a6da95,stroke:#363a4f,color:#24273a,stroke-width:1.5px
Main["src/main.ts"]:::root
Main["main.ts"]:::entry
subgraph Composition["Composition Modules"]
Startup["Startup & Lifecycle"]:::comp
IpcCli["IPC & CLI Wiring"]:::comp
Overlay["Overlay & Shortcuts"]:::comp
Subsync["Subsync"]:::comp
subgraph Comp["Composition — src/main/"]
Startup["Startup & Lifecycle<br/>startup · app-lifecycle<br/>startup-lifecycle · state"]:::comp
Wiring["Runtime Wiring<br/>ipc-runtime · cli-runtime<br/>overlay-runtime · subsync-runtime"]:::comp
end
subgraph Services["Domain Services"]
OverlaySvc["Overlay Services"]:::svc
MpvSvc["MPV Stack"]:::svc
MiningSvc["Mining & Subtitles"]:::svc
ShortcutIpc["Shortcuts & IPC"]:::svc
subgraph Svc["Services — src/core/services/"]
Mpv["MPV Stack<br/>transport · protocol<br/>state · properties"]:::svc
Overlay["Overlay<br/>manager · window<br/>visibility · bridge"]:::svc
Mining["Mining & Subtitles<br/>mining · field-grouping<br/>subtitle-ws · tokenizer"]:::svc
Integrations["Integrations<br/>jimaku · subsync<br/>texthooker · yomitan"]:::svc
end
subgraph External["External Boundaries"]
Config["Config & CLI"]:::ext
Trackers["Window Trackers"]:::ext
Integrations["Jimaku & Subsync"]:::ext
Bridge(["preload.ts — Electron IPC"]):::bridge
subgraph Rend["Renderer — src/renderer/"]
Orchestration["renderer.ts<br/>orchestration · IPC wiring"]:::rend
UI["subtitle-render · positioning<br/>handlers · modals"]:::rend
end
Main --> Startup
Main --> IpcCli
Main --> Overlay
Main --> Subsync
subgraph Ext["External Systems"]
mpv["mpv"]:::ext
Anki["AnkiConnect"]:::ext
Jimaku["Jimaku API"]:::ext
Tracker["Window Tracker"]:::ext
end
Startup --> OverlaySvc
IpcCli --> ShortcutIpc
Overlay --> OverlaySvc
Overlay --> MpvSvc
Subsync --> Integrations
Main -->|delegates| Comp
Startup -->|initializes| Svc
Wiring -->|dispatches to| Svc
OverlaySvc --> Trackers
MpvSvc --> MiningSvc
ShortcutIpc --> Config
Overlay <--> Bridge
Mining <--> Bridge
Bridge <--> Orchestration
Orchestration --> UI
style Composition fill:#363a4f,stroke:#494d64,color:#cad3f5
style Services fill:#363a4f,stroke:#494d64,color:#cad3f5
style External fill:#363a4f,stroke:#494d64,color:#cad3f5
Mpv <-->|JSON socket| mpv
Mining -->|HTTP| Anki
Integrations -->|HTTP| Jimaku
Overlay --> Tracker
style Comp fill:#363a4f,stroke:#494d64,color:#cad3f5
style Svc fill:#363a4f,stroke:#494d64,color:#cad3f5
style Rend fill:#363a4f,stroke:#494d64,color:#cad3f5
style Ext fill:#363a4f,stroke:#494d64,color:#cad3f5
```
## Composition Pattern
@@ -154,43 +164,53 @@ The composition root (`src/main.ts`) delegates to focused modules in `src/main/`
This keeps side effects explicit and makes behavior easy to unit-test with fakes.
## Lifecycle Model
## Program Lifecycle
- **Startup:**
- `src/main/startup.ts` (`startup-service`) handles initial argv/env/backend setup and decides generate-config flow vs app lifecycle start.
- `src/main/app-lifecycle.ts` (`app-lifecycle-service`) handles Electron single-instance + lifecycle event registration.
- `src/main/startup-lifecycle.ts` performs ready-time initialization (config load, websocket policy, tokenizer/tracker setup, overlay auto-init decisions).
- **Runtime:**
- CLI/shortcut/IPC events map to service calls through `src/main/cli-runtime.ts`, `src/main/ipc-runtime.ts`, and `src/main/overlay-runtime.ts`.
- Overlay and MPV state sync through dedicated services.
- Runtime options and mining flows are coordinated via service boundaries.
- **Shutdown:**
- `app-lifecycle-service` registers cleanup hooks (`will-quit`) while teardown behavior stays delegated to focused services from `src/main/*` composition modules.
- **Startup:** `startup.ts` parses CLI args and detects the compositor backend. If `--generate-config` is passed, it writes the template and exits. Otherwise `app-lifecycle.ts` acquires the single-instance lock and registers Electron lifecycle hooks.
- **Initialization:** Once `app.whenReady()` fires, `startup-lifecycle.ts` loads config, resolves keybindings, creates the mpv client, initializes the MeCab tokenizer, starts the window tracker, and applies WebSocket policy — then creates the overlay window and establishes the IPC bridge.
- **Runtime:** Event-driven. mpv property changes, IPC messages, CLI commands, and keyboard shortcuts all route through the composition layer to domain services, which update state and broadcast to the renderer.
- **Shutdown:** Electron's `will-quit` triggers service teardown — closes the mpv socket, unregisters shortcuts, stops WebSocket and texthooker servers, destroys the window tracker, and cleans up Anki state.
```mermaid
flowchart TD
classDef phase fill:#b7bdf8,stroke:#24273a,color:#24273a,stroke-width:1.5px
classDef decision fill:#f5a97f,stroke:#24273a,color:#24273a,stroke-width:1.5px
classDef runtime fill:#8aadf4,stroke:#24273a,color:#24273a,stroke-width:1.5px
classDef shutdown fill:#a6da95,stroke:#24273a,color:#24273a,stroke-width:1.5px
classDef start fill:#c6a0f6,stroke:#363a4f,color:#24273a,stroke-width:2px
classDef phase fill:#b7bdf8,stroke:#363a4f,color:#24273a,stroke-width:1.5px
classDef decision fill:#f5a97f,stroke:#363a4f,color:#24273a,stroke-width:1.5px
classDef init fill:#8aadf4,stroke:#363a4f,color:#24273a,stroke-width:1.5px
classDef runtime fill:#8bd5ca,stroke:#363a4f,color:#24273a,stroke-width:1.5px
classDef shutdown fill:#ed8796,stroke:#363a4f,color:#24273a,stroke-width:1.5px
Args["CLI args / env"]:::phase --> Startup["src/main/startup.ts"]:::phase
CLI["CLI args & environment"]:::start
CLI --> Parse["startup.ts<br/>Parse argv · detect backend · resolve config"]:::phase
Parse --> GenCheck{"--generate-config?"}:::decision
GenCheck -->|yes| GenExit["Write config template & exit"]:::phase
GenCheck -->|no| Lifecycle["app-lifecycle.ts<br/>Acquire single-instance lock<br/>Register Electron lifecycle hooks"]:::phase
Lifecycle -->|"app.whenReady()"| Ready["startup-lifecycle.ts"]:::phase
Startup --> Decision{"generate-config?"}:::decision
Ready --> Init
subgraph Init["Initialization"]
direction LR
Config["Load config<br/>resolve keybindings"]:::init
Runtime["Create mpv client<br/>init MeCab tokenizer"]:::init
Platform["Start window tracker<br/>WebSocket policy"]:::init
end
Decision -->|yes| WriteConfig["Write config + exit"]:::phase
Decision -->|no| AppLifecycle["src/main/app-lifecycle.ts"]:::phase
Init --> Create["Create overlay window<br/>Establish IPC bridge<br/>Load Yomitan extension"]:::phase
AppLifecycle --> Ready["src/main/startup-lifecycle.ts\nConfig · WebSocket · Tracker · Tokenizer · State"]:::phase
Create --> Loop
subgraph Loop["Runtime — event-driven"]
direction LR
Events["mpv · IPC · CLI<br/>shortcut events"]:::runtime
Dispatch["Route to service<br/>via composition layer"]:::runtime
State["Update state<br/>broadcast to renderer"]:::runtime
Events --> Dispatch --> State
end
Ready --> Runtime["Runtime Modules\nipc · cli · overlay · subsync"]:::runtime
Loop -->|"app close"| Quit["Electron will-quit"]:::shutdown
Quit --> Teardown["Close mpv socket · unregister shortcuts<br/>Stop WebSocket & texthooker<br/>Destroy tracker · clean Anki state"]:::shutdown
Runtime --> Overlay["Overlay & Mining"]:::runtime
Runtime --> Subtitle["Subtitle Processing"]:::runtime
Runtime --> SubsyncInt["Subsync & Jimaku"]:::runtime
Runtime --> WillQuit["Electron will-quit"]:::shutdown
WillQuit --> Cleanup["Service Teardown"]:::shutdown
style Init fill:#363a4f,stroke:#494d64,color:#cad3f5
style Loop fill:#363a4f,stroke:#494d64,color:#cad3f5
```
## Why This Design

View File

@@ -6,8 +6,8 @@ titleTemplate: Immersion Mining Workflow for MPV
hero:
name: SubMiner
text: Built for Immersion Mining
tagline: A self-contained MPV overlay for Japanese study. Look up words, mine cards, and enrich Anki without breaking playback flow.
text: Immersion Mining for MPV
tagline: Look up words, mine to Anki, and enrich cards with context — all without leaving the video.
image:
src: /assets/SubMiner.png
alt: SubMiner logo
@@ -18,80 +18,80 @@ hero:
- theme: alt
text: Mining Workflow
link: /mining-workflow
- theme: alt
text: Is This For Me?
link: "#who-this-is-for"
features:
- icon:
src: /assets/mpv.svg
alt: mpv icon
title: Built for mpv
details: Connects directly to mpv over IPC tracks subtitles in real time, observes playback properties, and renders a self-contained overlay with everything bundled in a single application.
details: Connects via IPC to track subtitles in real time and render a self-contained overlay everything bundled in a single application.
- icon:
src: /assets/yomitan-icon.svg
alt: Yomitan logo
title: Yomitan Integration
details: Hover over any word in the subtitles to trigger Yomitan dictionary lookups — get instant definitions without leaving the video player.
details: Hover over any word in the subtitle overlay to trigger dictionary lookups — instant definitions without leaving the player.
- icon:
src: /assets/anki-card.svg
alt: Anki card icon
title: Anki Card Enrichment
details: Add a word from Yomitan and SubMiner automatically updates the card with the sentence, audio clip, screenshot, and translation — no extra steps needed.
details: Add a word from Yomitan and SubMiner fills in the sentence, audio clip, screenshot, and translation automatically.
- icon:
src: /assets/dual-layer.svg
alt: Dual layer icon
title: Dual-Layer Subtitle System
details: Visible overlay with styled, interactive subtitles — plus an invisible layer that aligns with mpv's own subtitle rendering for seamless click-through lookup.
title: Dual-Layer Subtitles
details: Interactive visible overlay plus an invisible layer aligned with mpv's own rendering for seamless click-through lookup.
- icon:
src: /assets/highlight.svg
alt: Highlight icon
title: N+1 Word Highlighting
details: Highlights words you already know from your Anki deck, making it easy to spot new vocabulary and identify true N+1 sentences during immersion.
title: N+1 Highlighting
details: Marks words you already know from your Anki deck so you can spot new vocabulary and identify N+1 sentences at a glance.
- icon:
src: /assets/texthooker.svg
alt: Texthooker icon
title: Texthooker & WebSocket
details: Built-in texthooker page that receives subtitles over WebSocket — use it as a clipboard inserter for Yomitan or connect external tools for real-time subtitle streaming.
details: Built-in texthooker page that receives subtitles over WebSocket — use it as a clipboard inserter or connect external tools.
- icon:
src: /assets/subtitle-download.svg
alt: Subtitle download icon
title: Subtitle Download & Sync
details: Search and download Japanese subtitles from Jimaku, then sync them to the audio with alass or ffsubsync — all from within the player.
details: Search and download Japanese subtitles from Jimaku, then sync to audio with alass or ffsubsync — all from within the player.
- icon:
src: /assets/keyboard.svg
alt: Keyboard icon
title: Keyboard-Driven Workflow
details: Mine sentences, copy subtitles, cycle display modes, and trigger field grouping — all from configurable keyboard shortcuts without touching the mouse.
title: Keyboard-Driven
details: Mine sentences, copy subtitles, cycle display modes, and trigger field grouping — all from configurable shortcuts.
---
<style>
.demo-section {
max-width: 960px;
margin: 2rem auto 0;
margin: 0 auto;
padding: 0 24px;
}
.demo-section h2 {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 0.75rem;
margin-bottom: 0.5rem;
letter-spacing: -0.01em;
}
.demo-section p {
color: var(--vp-c-text-2);
margin-bottom: 1rem;
margin-bottom: 1.25rem;
line-height: 1.6;
}
.demo-section video {
width: 100%;
border-radius: 8px;
border-radius: 12px;
border: 1px solid var(--vp-c-divider);
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15);
}
.workflow-section {
max-width: 960px;
margin: 3rem auto 0;
margin: 4rem auto 0;
padding: 0 24px 3rem;
}
@@ -99,106 +99,52 @@ features:
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 1.5rem;
letter-spacing: -0.01em;
}
.workflow-steps {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
grid-template-columns: repeat(4, 1fr);
gap: 1px;
background: var(--vp-c-divider);
border-radius: 12px;
overflow: hidden;
}
@media (max-width: 768px) {
.workflow-steps {
grid-template-columns: 1fr;
}
}
.workflow-step {
padding: 1rem;
border-radius: 8px;
border: 1px solid var(--vp-c-divider);
padding: 1.25rem 1.5rem;
background: var(--vp-c-bg-soft);
}
.workflow-step .step-number {
font-size: 0.8rem;
display: inline-block;
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.05em;
color: var(--vp-c-brand-1);
margin-bottom: 0.25rem;
margin-bottom: 0.5rem;
font-variant-numeric: tabular-nums;
}
.workflow-step .step-title {
font-weight: 600;
margin-bottom: 0.25rem;
font-size: 1rem;
margin-bottom: 0.35rem;
}
.workflow-step .step-desc {
font-size: 0.875rem;
font-size: 0.85rem;
color: var(--vp-c-text-2);
line-height: 1.5;
}
</style>
<div class="demo-section">
## What SubMiner Is For
SubMiner is for people who learn Japanese by watching subtitled content in mpv and want a low-friction mining loop:
- stay inside the video while looking up words
- send mined content to Anki quickly
- keep media context (audio, screenshot, timestamp, subtitle context) attached to each card
- reduce tool switching between player, dictionary, and card workflow
</div>
<div class="workflow-section">
## Project Goals
<div class="workflow-steps">
<div class="workflow-step">
<div class="step-title">1. Keep Immersion Continuous</div>
<div class="step-desc">Minimize context switching by making lookup and mining happen directly over mpv subtitles.</div>
</div>
<div class="workflow-step">
<div class="step-title">2. Preserve Card Quality</div>
<div class="step-desc">Attach sentence context, audio, image, and translation so mined cards stay reviewable and useful long-term.</div>
</div>
<div class="workflow-step">
<div class="step-title">3. Support Real Workflows</div>
<div class="step-desc">Handle day-to-day immersion needs: subtitle management, syncing, known-word awareness, and keyboard-first controls.</div>
</div>
<div class="workflow-step">
<div class="step-title">4. Stay Configurable</div>
<div class="step-desc">Offer defaults that work out of the box, while still letting advanced users shape behavior around their note type and setup.</div>
</div>
<div class="workflow-step">
<div class="step-title">5. Evolve Safely</div>
<div class="step-desc">Use a modular TypeScript codebase and automated tests so features can ship faster without breaking core mining behavior.</div>
</div>
</div>
</div>
<div class="demo-section">
## See It in Action
SubMiner sits as a transparent overlay on top of mpv. Subtitles appear as interactive, clickable text — click a word to look it up with Yomitan, then add it to Anki with one click.
<video controls playsinline preload="metadata" poster="/assets/demo-poster.jpg">
<source :src="'/assets/card-mine.webm'" type="video/webm" />
Your browser does not support the video tag.
</video>
</div>
<div class="workflow-section">
## Who This Is For
- learners using mpv as their main immersion player
- users who already rely on Yomitan + AnkiConnect
- miners who care about preserving context on cards, not just raw words
SubMiner is likely overkill if you only want lightweight lookup without card enrichment, overlay controls, or integrated workflow tooling.
</div>
<div class="workflow-section">
## How It Works
@@ -212,7 +158,7 @@ SubMiner is likely overkill if you only want lightweight lookup without card enr
<div class="workflow-step">
<div class="step-number">02</div>
<div class="step-title">Look Up</div>
<div class="step-desc">Hover over a word in the subtitle overlay and hold Shift to trigger a Yomitan dictionary lookup.</div>
<div class="step-desc">Hover over a word in the subtitle overlay and hold Shift to trigger a Yomitan lookup.</div>
</div>
<div class="workflow-step">
<div class="step-number">03</div>
@@ -227,3 +173,14 @@ SubMiner is likely overkill if you only want lightweight lookup without card enr
</div>
</div>
<div class="demo-section">
## See It in Action
<video controls playsinline preload="metadata" poster="/assets/demo-poster.jpg">
<source :src="'/assets/card-mine.webm'" type="video/webm" />
Your browser does not support the video tag.
</video>
</div>