Compare commits

..

12 Commits

Author SHA1 Message Date
06708c9882 Harden playlist browser test cleanup and keydown fixture
- Wrap injected global cleanup assertions in `try/finally`
- Return the post-append mutation snapshot before Ctrl+ArrowDown coverage
2026-03-30 23:24:50 -07:00
ff760eaa32 fix: address CodeRabbit PR feedback 2026-03-30 22:47:27 -07:00
13680af3f6 Preserve playlist browser selection across refreshes
- keep modal selection stable when playlist snapshots mutate
- align test clock and timestamp fixtures with db helpers
- add regression coverage for selection and time parsing
2026-03-30 22:16:21 -07:00
e16250dedc Use shared local-day helpers in immersion tracker tests
- derive midnight and week-boundary timestamps from query-shared utilities
- keep split-module coverage aligned with db-local time handling
2026-03-30 19:59:45 -07:00
6118c46192 Fix immersion tracker SQLite timestamp truncation
- Bind epoch ms values as text to avoid libsql numeric truncation
- Update retention, lifetime, and query tests for string timestamps
- Add backlog ticket for the SQLite timestamp bug
2026-03-30 19:52:18 -07:00
c8e42b3973 refactor: split playlist browser wiring 2026-03-30 18:43:41 -07:00
90772f994c fix: restore macos overlay passthrough 2026-03-30 18:10:43 -07:00
c471bdf554 update backlog 2026-03-30 18:00:03 -07:00
f901433eea fix: address playlist browser coderabbit feedback 2026-03-30 17:50:39 -07:00
6ae3888b53 feat: add playlist browser overlay modal
- Add overlay modal for browsing sibling video files and live mpv queue
- Add IPC commands for playlist operations (add, remove, move, play)
- Add playlist-browser-runtime and playlist-browser-sort modules
- Add keyboard handler and preload bindings for playlist browser
- Add default Ctrl+Alt+P keybinding to open the modal
- Add HTML structure, renderer wiring, and state for the modal
- Add changelog fragment and docs updates
2026-03-30 01:50:38 -07:00
6e041bc68e style: fix modal background colors to use Catppuccin Macchiato values
- Playlist browser outer container: Base (#24273a) → Mantle (#1e2030)
  gradient instead of near-black values
- Playlist browser panes: Surface0 (#363a4f) → Base gradient for visible
  layering against the outer background
- Playlist browser list: Mantle (#1e2030) instead of near-black (#0c0e18)
2026-03-30 01:46:53 -07:00
8db89c2239 style: apply Catppuccin Macchiato palette to all modals
- Add full Catppuccin Macchiato CSS variables to :root (--ctp-*)
- Replace all generic rgba(255,255,255,...) and ad-hoc accent colors
  in modal components with shared --ctp-* variables
- Affected modals: jimaku, kiku, subsync, runtime options, controller
  select/config/debug, session help, YouTube picker, playlist browser,
  subtitle sidebar
- Shared button classes (.kiku-confirm-button, .kiku-cancel-button,
  .btn-learn, .btn-secondary) also updated
2026-03-30 01:40:25 -07:00
312 changed files with 1494 additions and 9304 deletions

View File

@@ -1,389 +0,0 @@
name: Prerelease
on:
push:
tags:
- 'v*-beta.*'
- 'v*-rc.*'
concurrency:
group: prerelease-${{ github.ref }}
cancel-in-progress: false
jobs:
quality-gate:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.5
- name: Cache dependencies
uses: actions/cache@v4
with:
path: |
~/.bun/install/cache
node_modules
stats/node_modules
vendor/subminer-yomitan/node_modules
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/subminer-yomitan/package-lock.json') }}
restore-keys: |
${{ runner.os }}-bun-
- name: Install dependencies
run: |
bun install --frozen-lockfile
cd stats && bun install --frozen-lockfile
- name: Lint stats (formatting)
run: bun run lint:stats
- name: Build (TypeScript check)
run: bun run typecheck
- name: Test suite (source)
run: bun run test:fast
- name: Coverage suite (maintained source lane)
run: bun run test:coverage:src
- name: Upload coverage artifact
uses: actions/upload-artifact@v4
with:
name: coverage-test-src
path: coverage/test-src/lcov.info
if-no-files-found: error
- name: Launcher smoke suite (source)
run: bun run test:launcher:smoke:src
- name: Upload launcher smoke artifacts (on failure)
if: failure()
uses: actions/upload-artifact@v4
with:
name: launcher-smoke
path: .tmp/launcher-smoke/**
if-no-files-found: ignore
- name: Build (bundle)
run: bun run build
- name: Immersion SQLite verification
run: bun run test:immersion:sqlite:dist
- name: Dist smoke suite
run: bun run test:smoke:dist
build-linux:
needs: [quality-gate]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.5
- name: Cache dependencies
uses: actions/cache@v4
with:
path: |
~/.bun/install/cache
node_modules
stats/node_modules
vendor/texthooker-ui/node_modules
vendor/subminer-yomitan/node_modules
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/texthooker-ui/package.json', 'vendor/subminer-yomitan/package-lock.json') }}
restore-keys: |
${{ runner.os }}-bun-
- name: Install dependencies
run: |
bun install --frozen-lockfile
cd stats && bun install --frozen-lockfile
- name: Build texthooker-ui
run: |
cd vendor/texthooker-ui
bun install
bun run build
- name: Build AppImage
run: bun run build:appimage
- name: Build unversioned AppImage
run: |
shopt -s nullglob
appimages=(release/SubMiner-*.AppImage)
if [ "${#appimages[@]}" -eq 0 ]; then
echo "No versioned AppImage found to create unversioned artifact."
ls -la release
exit 1
fi
cp "${appimages[0]}" release/SubMiner.AppImage
- name: Upload AppImage artifact
uses: actions/upload-artifact@v4
with:
name: appimage
path: release/*.AppImage
build-macos:
needs: [quality-gate]
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.5
- name: Cache dependencies
uses: actions/cache@v4
with:
path: |
~/.bun/install/cache
node_modules
stats/node_modules
vendor/texthooker-ui/node_modules
vendor/subminer-yomitan/node_modules
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/texthooker-ui/package.json', 'vendor/subminer-yomitan/package-lock.json') }}
restore-keys: |
${{ runner.os }}-bun-
- name: Validate macOS signing/notarization secrets
run: |
missing=0
for name in CSC_LINK CSC_KEY_PASSWORD APPLE_ID APPLE_APP_SPECIFIC_PASSWORD APPLE_TEAM_ID; do
if [ -z "${!name}" ]; then
echo "Missing required secret: $name"
missing=1
fi
done
if [ "$missing" -ne 0 ]; then
echo "Set all required macOS signing/notarization secrets and rerun."
exit 1
fi
env:
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
- name: Install dependencies
run: |
bun install --frozen-lockfile
cd stats && bun install --frozen-lockfile
- name: Build texthooker-ui
run: |
cd vendor/texthooker-ui
bun install
bun run build
- name: Build signed + notarized macOS artifacts
run: bun run build:mac
env:
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
- name: Upload macOS artifacts
uses: actions/upload-artifact@v4
with:
name: macos
path: |
release/*.dmg
release/*.zip
build-windows:
needs: [quality-gate]
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.5
- name: Cache dependencies
uses: actions/cache@v4
with:
path: |
~/.bun/install/cache
node_modules
stats/node_modules
vendor/texthooker-ui/node_modules
vendor/subminer-yomitan/node_modules
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/texthooker-ui/package.json', 'vendor/subminer-yomitan/package-lock.json') }}
restore-keys: |
${{ runner.os }}-bun-
- name: Install dependencies
run: |
bun install --frozen-lockfile
cd stats && bun install --frozen-lockfile
- name: Build texthooker-ui
shell: powershell
run: |
Set-Location vendor/texthooker-ui
bun install
bun run build
- name: Build unsigned Windows artifacts
run: bun run build:win:unsigned
- name: Upload Windows artifacts
uses: actions/upload-artifact@v4
with:
name: windows
path: |
release/*.exe
release/*.zip
if-no-files-found: error
release:
needs: [build-linux, build-macos, build-windows]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Download AppImage
uses: actions/download-artifact@v4
with:
name: appimage
path: release
- name: Download macOS artifacts
uses: actions/download-artifact@v4
with:
name: macos
path: release
- name: Download Windows artifacts
uses: actions/download-artifact@v4
with:
name: windows
path: release
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.5
- name: Cache dependencies
uses: actions/cache@v4
with:
path: |
~/.bun/install/cache
node_modules
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Build Bun subminer wrapper
run: make build-launcher
- name: Verify Bun subminer wrapper
run: dist/launcher/subminer --help >/dev/null
- name: Enforce generated launcher workflow
run: bash scripts/verify-generated-launcher.sh
- name: Verify generated config examples
run: bun run verify:config-example
- name: Package optional assets bundle
run: |
tar -czf "release/subminer-assets.tar.gz" \
config.example.jsonc \
plugin/subminer \
plugin/subminer.conf \
assets/themes/subminer.rasi
- name: Generate checksums
run: |
shopt -s nullglob
files=(release/*.AppImage release/*.dmg release/*.exe release/*.zip release/*.tar.gz dist/launcher/subminer)
if [ "${#files[@]}" -eq 0 ]; then
echo "No release artifacts found for checksum generation."
exit 1
fi
sha256sum "${files[@]}" > release/SHA256SUMS.txt
- name: Get version from tag
id: version
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
- name: Generate prerelease notes from pending fragments
run: bun run changelog:prerelease-notes --version "${{ steps.version.outputs.VERSION }}"
- name: Publish Prerelease
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
if gh release view "${{ steps.version.outputs.VERSION }}" >/dev/null 2>&1; then
gh release edit "${{ steps.version.outputs.VERSION }}" \
--draft=false \
--prerelease \
--title "${{ steps.version.outputs.VERSION }}" \
--notes-file release/prerelease-notes.md
else
gh release create "${{ steps.version.outputs.VERSION }}" \
--latest=false \
--prerelease \
--title "${{ steps.version.outputs.VERSION }}" \
--notes-file release/prerelease-notes.md
fi
shopt -s nullglob
artifacts=(
release/*.AppImage
release/*.dmg
release/*.exe
release/*.zip
release/*.tar.gz
release/SHA256SUMS.txt
dist/launcher/subminer
)
if [ "${#artifacts[@]}" -eq 0 ]; then
echo "No release artifacts found for upload."
exit 1
fi
for asset in "${artifacts[@]}"; do
gh release upload "${{ steps.version.outputs.VERSION }}" "$asset" --clobber
done

View File

@@ -4,8 +4,6 @@ on:
push:
tags:
- 'v*'
- '!v*-beta.*'
- '!v*-rc.*'
concurrency:
group: release-${{ github.ref }}

View File

@@ -1,74 +1,5 @@
# Changelog
## v0.11.2 (2026-04-07)
### Changed
- Launcher: Replaced the launcher-only fullscreen toggle with `mpv.launchMode` so SubMiner-managed mpv playback can start in normal, maximized, or fullscreen mode.
### Fixed
- Launcher: Fixed launcher-managed mpv spawning to force an explicit X11 GPU path when Wayland trackers are unavailable.
- Launcher: Local playback now promotes a single unlabeled external subtitle sidecar to the primary slot instead of leaving mpv's embedded English auto-selection in place.
- Release: Fixed Linux AppImage startup packaging so Chromium child relaunches can resolve the bundled `libffmpeg.so` instead of crash-looping on startup.
## v0.11.1 (2026-04-04)
### Fixed
- Release: Linux packaged builds now expose the canonical `SubMiner` app identity to Electron's startup metadata so native Wayland compositors stop reporting the window class/app-id as lowercase `subminer`.
- Linux: Linux now restores the runtime options, Jimaku, and Subsync shortcuts after the Electron 39 regression by routing those actions through the overlay's mpv/IPC shortcut path.
## v0.11.0 (2026-04-03)
### Added
- Overlay: Added a playlist browser overlay modal for browsing sibling video files and the live mpv queue during playback.
- Overlay: Added the default `Ctrl+Alt+P` keybinding to open the playlist browser and manage queue order without leaving playback.
### Changed
- 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.
- Setup: 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.
- Launcher: Streamlined Windows setup and config by making the `SubMiner mpv` shortcut self-contained and keeping `mpv.executablePath` as the simple fallback when `mpv.exe` is not on `PATH`.
- Overlay: Changed fresh-install default config to keep texthooker and stats from auto-opening browser tabs.
- Overlay: Changed fresh-install default config to enable AnkiConnect, Discord Rich Presence, subtitle-sidebar, and Yomitan-popup auto-pause by default, while disabling controller input by default.
### Fixed
- Main: Resolve the YouTube playback socket path lazily so startup honors CLI and config overrides.
- Main: Add regression coverage for the lazy socket-path lookup during Windows mpv startup.
- Main: Keep integrated `--start --texthooker` launches on the full app-ready startup path so the texthooker page and websocket servers start together during normal playback startup.
- Main: Stop the mpv/plugin auto-start flow from spawning a separate standalone texthooker helper during normal `subminer <video>` launches.
- Overlay: Keep tracked macOS visible overlays click-through by default so subtitle sidebar passthrough works immediately without requiring a subtitle hover cycle first.
- Overlay: Add regression coverage for the macOS visible-overlay passthrough default.
- Anilist: Stop AniList post-watch from sending a second progress update when the current episode was already satisfied by a ready retry item in the same watch-completion pass.
- Anilist: Add regression coverage for the retry-queue plus live-update duplicate path.
- Overlay: Fixed Kiku duplicate grouping to reuse duplicate note IDs from both generic sentence-card creation and Yomitan popup mining instead of running extra duplicate scans after add.
- Overlay: Fixed the Yomitan popup mining flow to add cards in the background while keeping the stock popup progress feedback, then pause playback and close the lookup popup before the Kiku merge modal opens.
- Overlay: Fixed configured subtitle-jump keybindings so backward and forward subtitle seeks keep playback paused when invoked from a paused state.
- Launcher: Fixed the Windows `SubMiner mpv` shortcut and `SubMiner.exe --launch-mpv` flow to launch mpv with SubMiner's required default args directly instead of requiring an `mpv.conf` profile named `subminer`.
- Launcher: Clarified the Windows install and usage docs so the shortcut path is documented as self-contained, while the optional `subminer` mpv profile remains available for manual mpv launches.
- Launcher: Hardened the first-run setup blocker copy and stale custom-scheme handling so setup messages stay aligned with config, plugin, and dictionary readiness.
- Launcher: Fixed the Windows `SubMiner mpv` shortcut idle launch so loading a video after opening the shortcut keeps mpv in the expected SubMiner-managed session, auto-starts the overlay, and re-arms subtitle auto-selection for the newly opened file.
- Launcher: Removed the redundant `.` subtitle search path from the Windows shortcut launch args and deduped repeated subtitle source tracks in the manual sync picker so duplicate external subtitle entries no longer appear from the shortcut path.
- Playback: Fixed managed local playback so duplicate startup-ready retries no longer unpause media after a later manual pause on the same file.
- Playback: Fixed managed local subtitle auto-selection so local files reuse configured primary and secondary subtitle language priorities instead of staying on mpv's initial `sid=auto` guess.
- Launcher: Added a blank-by-default `mpv.executablePath` override for Windows playback so users can point SubMiner at `mpv.exe` when it is not on `PATH`.
- Launcher: Kept the Windows shortcut and `--launch-mpv` flow simple by preserving PATH auto-discovery as the default and exposing the override in first-run setup.
- Launcher: Added `windows` as a recognized launcher backend option and auto-detection target on Windows.
- Launcher: Honored `SUBMINER_YTDLP_BIN` consistently across YouTube playback URL resolution, track probing, subtitle downloads, and metadata probing.
- Launcher: Kept the first-run setup window from navigating away on unexpected URLs.
- Launcher: Made Windows mpv honor an explicitly configured executable path instead of silently falling back to PATH.
- Launcher: Hardened `--launch-mpv` parsing and Windows binary resolution so valueless flags do not swallow media targets and symlinked launcher installs do not short-circuit PATH lookup.
- Launcher: Fixed first-run setup blocking playback on macOS when the SubMiner mpv plugin was already installed at the canonical `~/.config/mpv` path.
- Launcher: Fixed setup gating so stale cancelled setup state no longer prevents playback when the canonical mpv plugin entrypoint already exists.
- Playback: Prevented stale async playlist-browser subtitle rearm callbacks from overriding newer subtitle selections during rapid file changes.
### Docs
- Docs Site: Added a dedicated Subtitle Sidebar guide and linked it from the homepage and configuration docs.
- Docs Site: Linked Jimaku integration from the homepage to its dedicated docs page.
- Docs Site: Refreshed docs-site theme tokens and hover/selection styling for the updated pages.
### Internal
- Release: Retried AUR clone and push operations in the tagged release workflow.
- Release: Kept GitHub Releases green when AUR publish flakes and needs manual follow-up.
- Release: Updated Electron to 39.8.6 and pinned patched transitive build dependencies to clear the reported high-severity audit findings.
## v0.10.0 (2026-03-29)
### Changed

View File

@@ -4,21 +4,23 @@
# SubMiner
## Turn mpv into a sentence-mining workstation.
Look up words with Yomitan, export to Anki in one key, track your immersion — all without leaving mpv.
[Installation](#quick-start) · [Requirements](#requirements) · [Usage](https://docs.subminer.moe/usage) · [Documentation](https://docs.subminer.moe)
[![Downloads](https://img.shields.io/github/downloads/ksyasuda/SubMiner/total?style=flat-square&color=1a1a2e)](https://github.com/ksyasuda/SubMiner/releases)
[![Release](https://img.shields.io/github/v/release/ksyasuda/SubMiner?style=flat-square&color=1a1a2e)](https://github.com/ksyasuda/SubMiner/releases/latest)
[![AUR](https://img.shields.io/aur/version/subminer-bin?style=flat-square&color=1a1a2e)](https://aur.archlinux.org/packages/subminer-bin)
[![License: GPL v3](https://img.shields.io/badge/license-GPLv3-1a1a2e?style=flat-square)](https://www.gnu.org/licenses/gpl-3.0)
[![Platform](https://img.shields.io/badge/platform-Linux%20·%20macOS%20·%20Windows-1a1a2e?style=flat-square)](https://github.com/ksyasuda/SubMiner)
[![License](https://img.shields.io/github/license/ksyasuda/SubMiner?style=flat-square&color=1a1a2e)](https://www.gnu.org/licenses/gpl-3.0)
[![TypeScript](https://img.shields.io/badge/TypeScript-1a1a2e?style=flat-square&logo=typescript&logoColor=3178c6)](https://www.typescriptlang.org)
[![Docs](https://img.shields.io/badge/docs-docs.subminer.moe-e6a817?style=flat-square)](https://docs.subminer.moe)
[![AUR](https://img.shields.io/aur/version/subminer-bin?style=flat-square&color=1a1a2e)](https://aur.archlinux.org/packages/subminer-bin)
[![SubMiner demo](./assets/minecard.webp)](https://github.com/user-attachments/assets/89e61895-e2b7-4b47-8d50-a35afe4132b2)
[![SubMiner demo](./assets/minecard.webp)](./assets/minecard.mp4)
</div>
## How It Works
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.
## Features
### Dictionary Lookups
@@ -65,10 +67,6 @@ Local stats dashboard — watch time, anime library, vocabulary growth, mining t
Browse sibling episode files and the active mpv queue in one overlay modal. Open it with `Ctrl+Alt+P` to append episodes from the current directory, jump to queued items, remove entries, or reorder the playlist without leaving playback.
<div align="center">
<img src="docs-site/public/screenshots/playlist-browser.png" width="800" alt="Stats dashboard showing watch time, cards mined, streaks, and tracking data">
</div>
<br>
### Integrations
@@ -76,7 +74,7 @@ Browse sibling episode files and the active mpv queue in one overlay modal. Open
<table>
<tr>
<td><b>YouTube</b></td>
<td>Auto-loaded yt-dlp subtitle tracks at startup with config-driven primary/secondary language priorities and a manual overlay picker on demand (<code>Ctrl+Alt+C</code>)</td>
<td>Auto-loaded yt-dlp subtitle tracks at startup with a manual overlay picker on demand (<code>Ctrl+Alt+C</code>)</td>
</tr>
<tr>
<td><b>AniList</b></td>
@@ -110,15 +108,12 @@ Browse sibling episode files and the active mpv queue in one overlay modal. Open
## Requirements
| | Required | Recommended | Optional |
| -------------- | --------------------------------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| **Player** | [`mpv`](https://mpv.io) with IPC socket | — | — |
| **Processing** | — | `ffmpeg` (audio clips & screenshots) | `mecab` + `mecab-ipadic` (annotation POS filtering), `guessit` (AniSkip), `alass` / `ffsubsync` (subtitle sync) |
| **Media** | — | — | `yt-dlp`, `chafa`, `ffmpegthumbnailer` |
| **Selection** | — | — | `fzf` / `rofi` |
> [!TIP]
> `ffmpeg` is not strictly required to run SubMiner, but without it audio clips and screenshots will not be attached to Anki cards. Most users will want it installed.
| | Required | Optional |
| -------------- | --------------------------------------- | -------------------------------------- |
| **Player** | [`mpv`](https://mpv.io) with IPC socket | — |
| **Processing** | `ffmpeg`, `mecab` + `mecab-ipadic` | `guessit` (AniSkip), `alass` / `ffsubsync` (subtitle sync) |
| **Media** | — | `yt-dlp`, `chafa`, `ffmpegthumbnailer` |
| **Selection** | — | `fzf` / `rofi` |
> [!NOTE]
> [`bun`](https://bun.sh) is required if building from source or using the CLI wrapper: `subminer`. Pre-built releases (AppImage, DMG, installer) do not require it.
@@ -126,22 +121,19 @@ Browse sibling episode files and the active mpv queue in one overlay modal. Open
**Platform-specific:**
| Linux | macOS | Windows |
| ------------------------------------------------------------ | ------------------------ | ------------- |
| Hyprland (`hyprctl`) · X11/Xwayland (`xdotool` + `xwininfo`) | Accessibility permission | No extra deps |
> [!NOTE]
> **Wayland support is compositor-specific.** Wayland has no universal API for window positioning and each compositor exposes its own IPC, so SubMiner needs a dedicated backend per compositor. Hyprland is the only native Wayland backend supported currenlty. All other Linux compositors require both mpv and SubMiner to run under X11 or Xwayland. The launcher detects your compositor and configures this automatically.
| ----------------------------------- | ------------------------ | ------------- |
| `hyprctl` or `xdotool` + `xwininfo` | Accessibility permission | No extra deps |
<details>
<summary><b>Arch Linux</b></summary>
```bash
paru -S --needed mpv ffmpeg
paru -S --needed mpv ffmpeg mecab-git mecab-ipadic
# Optional
paru -S --needed mecab-git mecab-ipadic yt-dlp fzf rofi chafa ffmpegthumbnailer xdotool xorg-xwininfo
paru -S --needed yt-dlp fzf rofi chafa ffmpegthumbnailer xdotool xorg-xwininfo
# Optional: subtitle sync (install at least one for subtitle syncing to work)
paru -S --needed alass python-ffsubsync
# X11 / Xwayland (required for non-Hyprland compositors)
# X11 / XWAYLAND
paru -S --needed xdotool xorg-xwininfo
```
@@ -151,9 +143,9 @@ paru -S --needed xdotool xorg-xwininfo
<summary><b>macOS</b></summary>
```bash
brew install mpv ffmpeg
brew install mpv ffmpeg mecab mecab-ipadic
# Optional
brew install mecab mecab-ipadic yt-dlp fzf rofi chafa ffmpegthumbnailer
brew install yt-dlp fzf rofi chafa ffmpegthumbnailer
# Optional: subtitle sync (install at least one for subtitle syncing to work)
brew install alass
pip install ffsubsync
@@ -168,7 +160,7 @@ Grant Accessibility permission to SubMiner in **System Settings > Privacy & Secu
Install [`mpv`](https://mpv.io/installation/) and [`ffmpeg`](https://ffmpeg.org/download.html) and ensure both are on your `PATH`.
Optionally install [MeCab for Windows](https://taku910.github.io/mecab/#download) with the UTF-8 dictionary for additional metadata enrichment.
For MeCab, install [MeCab for Windows](https://taku910.github.io/mecab/#download) with the UTF-8 dictionary.
</details>
@@ -214,16 +206,6 @@ wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer -O ~
Download the latest DMG or ZIP from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest) and drag `SubMiner.app` into `/Applications`.
Also download the `subminer` launcher (recommended):
```bash
sudo curl -fSL https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer -o /usr/local/bin/subminer \
&& sudo chmod +x /usr/local/bin/subminer
```
> [!NOTE]
> The `subminer` launcher uses a [Bun](https://bun.sh) shebang. Make sure `bun` is on your `PATH`.
</details>
<details>
@@ -231,10 +213,6 @@ sudo curl -fSL https://github.com/ksyasuda/SubMiner/releases/latest/download/sub
Download the latest installer or portable `.zip` from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest). Make sure `mpv` is on your `PATH`.
**Windows support is experimental.** Core features such as mining, annotations, and dictionary lookups work, but some functionality may be missing or unstable. Bug reports welcome.
**Note:** On Windows the `subminer` launcher requires [`bun`](https://bun.sh) and must be invoked with `bun run subminer` instead of running the script directly. The recommended alternative is the **SubMiner mpv** shortcut created during first-run setup — double-click it, drag files onto it, or run `SubMiner.exe --launch-mpv` from a terminal. See the [Windows mpv Shortcut](https://docs.subminer.moe/usage#windows-mpv-shortcut) section for details.
</details>
<details>
@@ -246,25 +224,9 @@ See the [build-from-source guide](https://docs.subminer.moe/installation#from-so
### 2. First Launch
```bash
subminer app --setup # launch the first-run setup wizard
```
Run the app. On first launch SubMiner starts in the system tray, creates a default config, and opens a setup popup to install the mpv plugin and configure Yomitan dictionaries.
SubMiner creates a default config, starts in the system tray, and opens a setup popup that walks you through installing the mpv plugin and configuring Yomitan dictionaries. Follow the on-screen steps to complete setup.
> [!NOTE]
> On Windows, run `SubMiner.exe` directly — it opens the setup wizard automatically on first launch.
### 3. Verify Setup
```bash
subminer doctor # verify mpv, ffmpeg, config, and socket
```
> [!NOTE]
> On Windows, use `bun run subminer doctor` or run `SubMiner.exe` directly — first-run setup will guide you through dependency checks.
### 4. Mine
### 3. Mine
```bash
subminer video.mkv # play video with overlay
@@ -274,7 +236,7 @@ subminer stats -b # stats daemon in background
subminer stats -s # stop background stats daemon
```
On **Windows**, the `subminer` script must be run with `bun run subminer` (e.g. `bun run subminer video.mkv`). The recommended alternative is the **SubMiner mpv** shortcut (created during setup) or `SubMiner.exe --launch-mpv`. Drag a video file onto the shortcut to play it, or double-click it to open mpv with SubMiner's defaults.
---
## Documentation

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

View File

@@ -1,23 +0,0 @@
---
id: TASK-279
title: Prepare v0.11.2 release notes and verify release gate
status: In Progress
assignee: []
created_date: '2026-04-04 07:38'
labels: []
dependencies: []
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Generate release metadata for the pending changelog fragments, review the resulting changelog/release notes output, and run the documented local release gate so the repo is ready for a v0.11.2 cut if checks pass.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 CHANGELOG.md and release/release-notes.md are generated for v0.11.2 using the pending changes fragments.
- [ ] #2 The documented local release gate for release prep is run and the pass/fail state is recorded with any blockers.
- [ ] #3 Any release-prep file updates needed for the generated notes are left in the worktree for review without tagging or pushing.
<!-- AC:END -->

View File

@@ -1,39 +0,0 @@
---
id: TASK-272
title: 'Assess and address PR #40 CodeRabbit review follow-ups'
status: Done
assignee: []
created_date: '2026-04-03 07:52'
updated_date: '2026-04-03 08:04'
labels:
- coderabbit
- review
- launcher
milestone: 'PR #40'
dependencies: []
references:
- 'https://github.com/ksyasuda/SubMiner/pull/40'
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Implement the valid CodeRabbit findings on PR #40 and keep the Windows mpv shortcut / first-run setup flow consistent end to end.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Windows binary resolution does not return install directories as executable candidates
- [ ] #2 Launch-mpv arg parsing preserves space-separated mpv option values and target separation
- [ ] #3 Windows mpv launch args keep the final input-ipc-server and script-opts socket path in sync when custom values are supplied
- [ ] #4 First-run setup navigation swallows stale or invalid custom-scheme actions without navigating away
- [ ] #5 Setup messaging and footer copy reflect configReady, plugin, and dictionary gates consistently
- [ ] #6 Regression tests cover the fixed behaviors
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Addressed CodeRabbit follow-ups for PR #40. Hardened launcher binary discovery on Windows and PATH resolution, fixed launch-mpv argument parsing for value-bearing flags, synced custom Windows mpv IPC socket values into script opts, and tightened first-run setup messaging/navigation to handle stale actions and blocker copy. Verified with `bun test src/main-entry-runtime.test.ts src/main/runtime/windows-mpv-launch.test.ts src/main/runtime/first-run-setup-window.test.ts launcher/mpv.test.ts`.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- codex
created_date: '2026-03-22 21:25'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-24 06:44'
labels:
- stats
- immersion-tracker
@@ -21,7 +21,6 @@ references:
- >-
/Users/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker-service.test.ts
priority: medium
ordinal: 178500
---
## Description

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- codex
created_date: '2026-03-26 03:59'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-26 04:01'
labels:
- review-comments
- coderabbit
@@ -18,7 +18,6 @@ references:
- >-
/Users/sudacode/projects/japanese/SubMiner/src/main/runtime/youtube-playback-launch.ts
priority: medium
ordinal: 177500
---
## Description

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- codex
created_date: '2026-03-26 04:30'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-26 04:31'
labels:
- review-comments
- coderabbit
@@ -13,7 +13,6 @@ dependencies: []
references:
- /Users/sudacode/projects/japanese/SubMiner/src/main.ts
priority: medium
ordinal: 176500
---
## Description

View File

@@ -4,7 +4,7 @@ title: Introduce domain type entrypoints and shrink src/types.ts import surface
status: Done
assignee: []
created_date: '2026-03-26 20:49'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-27 00:14'
labels:
- tech-debt
- types
@@ -18,7 +18,6 @@ references:
- docs/architecture/README.md
parent_task_id: TASK-238
priority: medium
ordinal: 174500
---
## Description
@@ -28,6 +27,7 @@ ordinal: 174500
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Domain-focused type modules exist for the main clusters currently mixed together in `src/types.ts` (for example Anki, config/runtime, subtitle/media, and integration/runtime-option types).
- [x] #2 `src/types.ts` becomes a thinner compatibility layer or barrel instead of the sole source of truth for every shared type.

View File

@@ -1,10 +1,10 @@
---
id: TASK-238.4
title: 'Decompose character dictionary runtime into fetch, build, and cache modules'
title: Decompose character dictionary runtime into fetch, build, and cache modules
status: Done
updated_date: '2026-03-27 00:20'
assignee: []
created_date: '2026-03-26 20:49'
updated_date: '2026-03-31 19:37'
labels:
- tech-debt
- runtime
@@ -19,7 +19,6 @@ references:
- docs/architecture/README.md
parent_task_id: TASK-238
priority: medium
ordinal: 173500
---
## Description
@@ -29,6 +28,7 @@ ordinal: 173500
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 AniList fetch/parsing logic, dictionary-entry building, and snapshot/cache/zip persistence no longer live in one giant file.
- [x] #2 The public runtime API stays behavior-compatible for current callers.

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- codex
created_date: '2026-03-26 20:49'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-27 00:00'
labels:
- tech-debt
- stats
@@ -20,7 +20,6 @@ references:
- src/core/services/immersion-tracker-service.ts
parent_task_id: TASK-238
priority: medium
ordinal: 175500
---
## Description
@@ -30,6 +29,7 @@ ordinal: 175500
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Query responsibilities are grouped into focused modules such as library/session detail, vocabulary/kanji detail, and maintenance/cleanup helpers.
- [x] #2 The stats server and immersion tracker service depend on stable exported query surfaces instead of one monolithic file.

View File

@@ -4,7 +4,7 @@ title: Extract remaining inline runtime logic and composer gaps from src/main.ts
status: Done
assignee: []
created_date: '2026-03-27 00:00'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-27 22:13'
labels:
- tech-debt
- runtime
@@ -24,7 +24,6 @@ references:
- src/main/runtime/composers
parent_task_id: TASK-238
priority: high
ordinal: 172500
---
## Description
@@ -34,6 +33,7 @@ ordinal: 172500
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 `runYoutubePlaybackFlow`, `maybeSignalPluginAutoplayReady`, `refreshSubtitlePrefetchFromActiveTrack`, `publishDiscordPresence`, and `handleModalInputStateChange` no longer live as substantial inline logic in `src/main.ts`.
- [x] #2 The large subtitle/prefetch, stats startup, and overlay visibility dependency groupings are wrapped behind named composer helpers instead of remaining inline in `src/main.ts`.

View File

@@ -1,10 +1,10 @@
---
id: TASK-238.7
title: 'Split src/main.ts into boot-phase services, runtimes, and handlers'
title: Split src/main.ts into boot-phase services, runtimes, and handlers
status: Done
assignee: []
created_date: '2026-03-27 00:00'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-27 22:45'
labels:
- tech-debt
- runtime
@@ -21,7 +21,6 @@ references:
- src/main/runtime/composers
parent_task_id: TASK-238
priority: high
ordinal: 171500
---
## Description
@@ -31,6 +30,7 @@ After the remaining inline runtime logic and composer gaps are extracted, `src/m
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Service instantiation lives in a dedicated boot module instead of a large inline setup block in `src/main.ts`.
- [x] #2 Domain runtime composition lives in a dedicated boot module, separate from lifecycle and handler dispatch.

View File

@@ -4,7 +4,7 @@ title: Fix stats server Bun fallback in coverage lane
status: Done
assignee: []
created_date: '2026-03-29 07:31'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-29 07:37'
labels:
- ci
- bug
@@ -13,7 +13,6 @@ dependencies: []
references:
- 'PR #36'
priority: high
ordinal: 170500
---
## Description

View File

@@ -4,14 +4,13 @@ title: Migrate Discord Rich Presence to maintained RPC wrapper
status: Done
assignee: []
created_date: '2026-03-29 08:17'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-29 08:22'
labels:
- dependency
- discord
- presence
dependencies: []
priority: medium
ordinal: 169500
---
## Description

View File

@@ -5,14 +5,13 @@ status: Done
assignee:
- codex
created_date: '2026-03-29 10:01'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-29 10:10'
labels: []
dependencies: []
references:
- src/core/services/subtitle-cue-parser.ts
- src/renderer/modals/subtitle-sidebar.ts
- src/core/services/subtitle-cue-parser.test.ts
ordinal: 168500
---
## Description

View File

@@ -4,7 +4,7 @@ title: Fix macOS visible overlay toggle getting immediately restored
status: Done
assignee: []
created_date: '2026-03-29 10:03'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-29 22:14'
labels: []
dependencies: []
references:
@@ -13,7 +13,6 @@ references:
- /Users/sudacode/projects/japanese/SubMiner/src/core/services/cli-command.ts
- >-
/Users/sudacode/projects/japanese/SubMiner/src/main/overlay-visibility-runtime.ts
ordinal: 165500
---
## Description

View File

@@ -4,7 +4,7 @@ title: Fix AniList token persistence on setup login
status: Done
assignee: []
created_date: '2026-03-29 10:08'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-29 19:42'
labels:
- anilist
- bug
@@ -15,7 +15,6 @@ documentation:
- src/main/runtime/anilist-token-refresh.ts
- docs-site/anilist-integration.md
priority: high
ordinal: 166500
---
## Description

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- '@codex'
created_date: '2026-03-29 10:10'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-29 10:23'
labels:
- bug
- macos
@@ -24,7 +24,6 @@ references:
- >-
/Users/sudacode/projects/japanese/SubMiner/src/renderer/overlay-mouse-ignore.test.ts
priority: high
ordinal: 167500
---
## Description

View File

@@ -4,12 +4,11 @@ title: 'Docs: add subtitle sidebar and Jimaku integration pages'
status: Done
assignee: []
created_date: '2026-03-29 22:36'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-29 22:38'
labels:
- docs
dependencies: []
priority: medium
ordinal: 164500
---
## Description

View File

@@ -4,14 +4,13 @@ title: Harden AUR publish release step against transient SSH failures
status: Done
assignee: []
created_date: '2026-03-29 23:46'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-29 23:49'
labels:
- release
- ci
- aur
dependencies: []
priority: high
ordinal: 163500
---
## Description

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- codex
created_date: '2026-03-30 01:59'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-30 02:03'
labels: []
dependencies: []
references:
@@ -14,7 +14,6 @@ references:
- /Users/sudacode/projects/japanese/SubMiner/src/anki-integration.ts
- /Users/sudacode/projects/japanese/SubMiner/src/core/services/stats-server.ts
- /Users/sudacode/projects/japanese/SubMiner/src/media-generator.ts
ordinal: 162500
---
## Description

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- codex
created_date: '2026-03-30 02:10'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-30 02:20'
labels:
- bug
- anilist
@@ -17,7 +17,6 @@ references:
- >-
/Users/sudacode/projects/japanese/SubMiner/src/main/runtime/anilist-token-refresh.ts
priority: high
ordinal: 161500
---
## Description

View File

@@ -1,18 +1,17 @@
---
id: TASK-255
title: Add overlay playlist browser modal for sibling video files and mpv queue
status: Done
status: In Progress
assignee:
- '@codex'
created_date: '2026-03-30 05:46'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-31 05:59'
labels:
- feature
- overlay
- mpv
- launcher
dependencies: []
ordinal: 180500
---
## Description
@@ -23,10 +22,10 @@ Add an in-session overlay modal that opens from a keybinding during active playb
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 An overlay modal can be opened during active playback from a dedicated keybinding and closed without disrupting existing modal behavior.
- [x] #2 The modal shows video files from the current media file's parent directory in best-effort episode order and highlights the current file when present.
- [x] #3 The modal shows the active mpv playlist/queue with enough metadata to identify the current item and queued order.
- [x] #4 The user can add a directory file to the mpv playlist, remove playlist items, and reorder playlist items from the modal using both mouse and keyboard interactions.
- [ ] #1 An overlay modal can be opened during active playback from a dedicated keybinding and closed without disrupting existing modal behavior.
- [ ] #2 The modal shows video files from the current media file's parent directory in best-effort episode order and highlights the current file when present.
- [ ] #3 The modal shows the active mpv playlist/queue with enough metadata to identify the current item and queued order.
- [ ] #4 The user can add a directory file to the mpv playlist, remove playlist items, and reorder playlist items from the modal using both mouse and keyboard interactions.
- [x] #5 Modal state stays in sync after playlist mutations so the rendered queue reflects mpv's current playlist order.
- [x] #6 Feature coverage includes automated tests for ordering/playlist behavior and docs or shortcut/help updates for the new modal.
<!-- AC:END -->
@@ -85,8 +84,4 @@ Split playlist-browser UI row rendering into `src/renderer/modals/playlist-brows
2026-03-30 latest CodeRabbit remediation on PR #37: switched nowMs() numeric test-clock branch from Math.floor() to Math.trunc() so numeric and string-backed mock clocks agree for negative fractional values. Refactored playlist-browser modal tests onto a shared setup/teardown fixture that restores global window/document descriptors correctly, and added regression coverage that injected globals are deleted when originally absent. Verification: `bun test src/core/services/immersion-tracker/time.test.ts src/renderer/modals/playlist-browser.test.ts`, `bun run typecheck`.
2026-03-30 CodeRabbit follow-up: wrapped the injected-globals cleanup regression in try/finally so restore always runs, and changed the keydown test append mock to return createMutationSnapshot() before exercising Ctrl+ArrowDown. Verified with `bun test src/renderer/modals/playlist-browser.test.ts` and `bun run typecheck`.
2026-03-31 assessment: the playlist-browser feature is landed on `main` via `d51e7fe4 Add playlist browser overlay modal (#37)` with runtime, IPC, renderer, keybinding, and changelog/docs coverage present. Verified passes: `bun test src/main/runtime/playlist-browser-runtime.test.ts src/main/runtime/playlist-browser-open.test.ts src/main/runtime/playlist-browser-sort.test.ts src/renderer/handlers/keyboard.test.ts src/core/services/ipc.test.ts src/core/services/ipc-command.test.ts src/config/definitions/domain-registry.test.ts`.
Remaining action item before close: fix `src/renderer/modals/playlist-browser.test.ts` so the cleanup regression does not assume `globalThis.window` / `globalThis.document` start absent under Bun, rerun the playlist-browser modal lane (and then typecheck/build if you want the full closeout proof), then finalize the task.
<!-- SECTION:NOTES:END -->

View File

@@ -5,14 +5,13 @@ status: Done
assignee:
- codex
created_date: '2026-03-30 06:04'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-30 06:12'
labels:
- bug
- texthooker
- websocket
dependencies: []
priority: medium
ordinal: 160500
---
## Description

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- codex
created_date: '2026-03-30 06:15'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-30 06:17'
labels:
- bug
- texthooker
@@ -13,7 +13,6 @@ labels:
- startup
dependencies: []
priority: high
ordinal: 159500
---
## Description

View File

@@ -5,7 +5,7 @@ status: Done
assignee:
- codex
created_date: '2026-03-30 06:25'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-30 06:26'
labels:
- bug
- texthooker
@@ -14,7 +14,6 @@ labels:
- startup
dependencies: []
priority: high
ordinal: 158500
---
## Description

View File

@@ -4,14 +4,13 @@ title: Fix integrated --start --texthooker startup skipping texthooker server
status: Done
assignee: []
created_date: '2026-03-30 06:48'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-30 06:56'
labels:
- bug
- texthooker
- startup
dependencies: []
priority: high
ordinal: 157500
---
## Description

View File

@@ -7,7 +7,7 @@ status: Done
assignee:
- '@codex'
created_date: '2026-03-31 00:58'
updated_date: '2026-03-31 19:37'
updated_date: '2026-03-31 01:01'
labels:
- bug
- macos
@@ -27,7 +27,6 @@ references:
documentation:
- docs/workflow/verification.md
priority: high
ordinal: 156500
---
## Description

View File

@@ -1,10 +1,9 @@
---
id: TASK-261
title: Fix immersion tracker SQLite timestamp truncation
status: Done
status: In Progress
assignee: []
created_date: '2026-03-31 01:45'
updated_date: '2026-03-31 19:37'
labels:
- immersion-tracker
- sqlite
@@ -13,7 +12,7 @@ dependencies: []
references:
- src/core/services/immersion-tracker
priority: medium
ordinal: 179500
ordinal: 1200
---
## Description
@@ -24,17 +23,7 @@ Current-epoch millisecond values are being truncated by the libsql driver when b
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Current-epoch millisecond timestamps persist correctly in session, telemetry, lifetime, and rollup tables
- [x] #2 Startup backfill and destroy/finalize flows keep retained sessions and lifetime summaries consistent
- [x] #3 Regression tests cover the destroyed-session, startup backfill, and distinct-day/distinct-video lifetime semantics
- [ ] #1 Current-epoch millisecond timestamps persist correctly in session, telemetry, lifetime, and rollup tables
- [ ] #2 Startup backfill and destroy/finalize flows keep retained sessions and lifetime summaries consistent
- [ ] #3 Regression tests cover the destroyed-session, startup backfill, and distinct-day/distinct-video lifetime semantics
<!-- AC:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
2026-03-31 assessment: epoch-ms timestamp writes now route through `toDbMs()` / `toDbTimestamp()` in `src/core/services/immersion-tracker/query-shared.ts`, which avoids libsql numeric-parameter truncation by binding BigInt/string values before they hit SQLite. The fix is wired through the session, storage/telemetry, lifetime, and rollup-maintenance paths in `src/core/services/immersion-tracker/session.ts`, `src/core/services/immersion-tracker/storage.ts`, `src/core/services/immersion-tracker/lifetime.ts`, and `src/core/services/immersion-tracker/maintenance.ts`.
Acceptance coverage is present: `bun test src/core/services/immersion-tracker-service.test.ts` passed with explicit regressions for destroy/finalize persistence, startup backfill when retained sessions exist but lifetime tables are empty, startup reconciliation of stale active sessions, `rebuildLifetimeSummaries`, and distinct-day / distinct-video lifetime semantics. `bun test src/core/services/immersion-tracker/time.test.ts src/core/services/immersion-tracker/maintenance.test.ts` also passed.
Remaining action item before close: fix the two `src/main/runtime/stats-cli-command.test.ts` cleanup-lifetime assertions that currently use Bun-misparsed underscored millisecond literals (`1_710_000_000_000` evaluates to `-2147483648` under Bun 1.3.11), rerun that verification lane, then write the final summary and mark the task Done.
<!-- SECTION:NOTES:END -->

View File

@@ -1,51 +0,0 @@
---
id: TASK-262
title: Fix duplicate AniList post-watch updates for watched episodes
status: Done
assignee:
- codex
created_date: '2026-03-31 19:03'
updated_date: '2026-03-31 19:37'
labels:
- bug
- anilist
dependencies: []
ordinal: 155500
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Watching an episode can currently produce two AniList activity updates for the same episode. The duplicate happens when the post-watch flow drains a queued retry for the current episode and then proceeds to run the live post-watch update for that same media/episode in the same pass. User report says this reproduces both when crossing the watched threshold naturally and when using the mark-watched keybinding. Fix the duplicate so one successful watch produces at most one AniList progress update for a given mediaKey/episode pair.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 A watched episode triggers at most one AniList post-watch progress update for a given media key and episode during a single post-watch pass, even if that episode already exists in the retry queue.
- [x] #2 Both watched-threshold and manual mark-watched flows are protected by regression coverage for the duplicate-update case.
- [x] #3 Relevant user-visible change note is added if required by repo policy.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Reproduce the duplicate in a unit test around `createMaybeRunAnilistPostWatchUpdateHandler` by simulating a ready retry for the same `mediaKey::episode` the live path would also submit.
2. Fix the handler so that after processing a queued retry, it does not perform a second live update when the retry already satisfied the current attempt key.
3. Run focused AniList runtime tests and adjacent immersion tests to confirm both threshold-driven and manual mark-watched entry points stay covered through the shared post-watch path.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Added a regression in `src/main/runtime/anilist-post-watch.test.ts` for the case where `processNextAnilistRetryUpdate()` already satisfies the current `mediaKey::episode` before the live path runs.
Updated `createMaybeRunAnilistPostWatchUpdateHandler` to re-check `hasAttemptedUpdateKey(attemptKey)` immediately after draining the retry queue and short-circuit before a second live AniList submission.
Verification: `bun test src/main/runtime/anilist-post-watch.test.ts src/main/runtime/anilist-post-watch-main-deps.test.ts`; `bun test src/core/services/immersion-tracker-service.test.ts --test-name-pattern 'recordPlaybackPosition marks watched at 85% completion|markActiveVideoWatched'`; `bun run typecheck`; `bun run changelog:lint`.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Fixed duplicate AniList post-watch submissions by short-circuiting the live update path when a ready retry item already handled the current `mediaKey::episode` in the same pass. Added a focused regression test for the retry-plus-live duplicate scenario and a changelog fragment documenting the fix.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -1,39 +0,0 @@
---
id: TASK-263
title: Reuse pre-add duplicate IDs for generic Kiku field grouping
status: Done
assignee: []
created_date: '2026-03-31 20:44'
updated_date: '2026-03-31 20:48'
labels:
- anki
- kiku
dependencies: []
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Avoid the extra post-add duplicate lookup on the generic sentence-card creation path by capturing duplicate note IDs before add and reusing that result for Kiku field grouping. Keep Yomitan semantics aligned where practical so duplicate selection is consistent across mining paths.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Generic sentence-card creation captures duplicate note IDs before add and reuses them for Kiku field grouping instead of running the existing post-add duplicate finder
- [x] #2 Duplicate selection remains deterministic when multiple matching notes exist
- [x] #3 Regression tests cover the generic path duplicate reuse behavior and preserve existing non-Kiku behavior
- [x] #4 Internal docs/config comments are updated if the behavior or operator-facing semantics changed
<!-- AC:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
No docs update was required because this is internal duplicate-selection plumbing and does not change user-facing config surface.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Generic sentence-card creation now captures exact duplicate note IDs before add when Kiku field grouping is enabled and stores that context by created note ID. Manual field grouping reuses the tracked duplicate IDs first and deterministically picks the most recent matching note, falling back to the legacy duplicate finder only when no tracked context exists. Verified with bun test src/anki-integration/duplicate.test.ts src/anki-integration/card-creation.test.ts src/anki-integration/field-grouping.test.ts and bun run typecheck.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -1,41 +0,0 @@
---
id: TASK-263.1
title: Reuse Yomitan popup duplicate IDs in SubMiner bridge
status: Done
assignee: []
created_date: '2026-03-31 22:15'
updated_date: '2026-03-31 22:21'
labels:
- anki
- kiku
- yomitan
dependencies: []
parent_task_id: TASK-263
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Thread Yomitan popup/search duplicate note IDs through the existing SubMiner bridge so Kiku/manual grouping can reuse the same duplicate context that already drives the Add duplicate button. Implement and test against the vendored Yomitan copy first; do not rely on upstreamed fork changes yet.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Vendored Yomitan bridge returns duplicate note IDs for popup/search mining when available
- [x] #2 SubMiner consumes the bridged duplicate IDs and prefers them for Kiku/manual grouping on the Yomitan mining path
- [x] #3 Regression tests cover the popup/search bridge payload and duplicate-id reuse behavior
- [x] #4 No commit is made for vendored Yomitan-only changes in this repo state
<!-- AC:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Vendored files changed locally for validation only: vendor/subminer-yomitan/ext/js/display/display-anki.js, vendor/subminer-yomitan/ext/js/comm/api.js, vendor/subminer-yomitan/ext/js/comm/anki-connect.js, vendor/subminer-yomitan/ext/js/background/backend.js. Do not commit those vendor changes in this repo; port them to the fork instead.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Vendored Yomitan popup/search mining now precomputes duplicate note IDs, sends them to the SubMiner Anki proxy as private addNote metadata, and still returns note/duplicate data through the parser bridge. The proxy strips the private metadata before forwarding to upstream AnkiConnect, associates the duplicate IDs with the created note before auto-enrichment begins, and SubMiner also records the bridge result as a secondary cache path. Verified with bun test src/anki-integration/duplicate.test.ts src/anki-integration/card-creation.test.ts src/anki-integration/field-grouping.test.ts src/anki-integration/anki-connect-proxy.test.ts src/core/services/tokenizer/yomitan-parser-runtime.test.ts and bun run typecheck.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -1,43 +0,0 @@
---
id: TASK-263.2
title: >-
Keep Yomitan popup responsive during background add and pause/close before
Kiku modal
status: Done
assignee: []
created_date: '2026-04-01 00:42'
updated_date: '2026-04-01 02:35'
labels:
- anki
- yomitan
- kiku
- ux
dependencies: []
parent_task_id: TASK-263
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Make Yomitan popup add run in background without blocking popup responsiveness. Before opening Kiku field-grouping modal, pause MPV and close the Yomitan popup/parser window if open.
<!-- SECTION:DESCRIPTION:END -->
## Definition of Done
<!-- DOD:BEGIN -->
- [x] #1 Popup save path returns immediately and prevents duplicate submits
- [x] #2 Field-grouping modal request pauses MPV and closes Yomitan popup window first
- [x] #3 Regression tests cover async save dispatch and main-side pause/close hook
<!-- DOD:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
2026-03-31: Removed the custom pending label/gray save-button presentation from vendored Yomitan. Background add still runs asynchronously with the internal pending-save guard, so duplicate clicks are ignored while the button keeps its stock appearance.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Yomitan popup save dispatches note creation/add in the background with an internal pending-save guard so repeated clicks are ignored without blocking the popup. Before opening the Kiku field-grouping modal, the renderer now closes the visible lookup popup and pauses MPV. Follow-up UX polish removed the custom pending label/gray styling so the save button keeps Yomitans stock presentation while the background action runs.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -1,26 +0,0 @@
---
id: TASK-264
title: Replace axios with native fetch across the project
status: To Do
assignee: []
created_date: '2026-04-01 00:44'
labels: []
dependencies: []
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Remove axios from the codebase and migrate all project HTTP requests to the platform fetch API, preserving existing request behavior and error handling where applicable.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 No production code paths import or depend on axios.
- [ ] #2 All existing HTTP requests use fetch or a project-local abstraction built on fetch.
- [ ] #3 Request behavior remains functionally equivalent for headers, query params, bodies, status handling, and abort/error cases that are currently supported.
- [ ] #4 Tests are updated or added to cover the migrated request flows.
- [ ] #5 Documentation is updated if any request semantics or setup steps change.
- [ ] #6 axios is removed from project dependencies if it is no longer needed.
<!-- AC:END -->

View File

@@ -1,53 +0,0 @@
---
id: TASK-265
title: Add remote backend for immersion tracking and stats (prefer Postgres)
status: To Do
assignee: []
created_date: '2026-04-01 00:47'
labels: []
dependencies: []
references:
- >-
/home/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker-service.ts
- >-
/home/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker/storage.ts
- >-
/home/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker/sqlite.ts
- /home/sudacode/projects/japanese/SubMiner/src/stats-daemon-runner.ts
- /home/sudacode/projects/japanese/SubMiner/src/core/services/stats-server.ts
- /home/sudacode/projects/japanese/SubMiner/src/main/boot/services.ts
- /home/sudacode/projects/japanese/SubMiner/package.json
documentation:
- /home/sudacode/projects/japanese/SubMiner/docs/architecture/README.md
- >-
/home/sudacode/projects/japanese/SubMiner/docs/architecture/stats-trends-data-flow.md
- /home/sudacode/projects/japanese/SubMiner/README.md
- /home/sudacode/projects/japanese/SubMiner/config.example.jsonc
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Enable immersion tracking/stats to use a remote authoritative backend so multiple devices can share the same history.
Current state: `ImmersionTrackerService` opens a local `immersion.sqlite` file from the app data/config path, `stats-daemon-runner` points at that same local file, and `config.example.jsonc` only exposes `immersionTracking.dbPath` for a local path override. The stats API/dashboard reads from the same tracker service and assumes the local database is the source of truth.
Goal: add a remote backend option that avoids shared filesystem/database-file syncing between devices. Do not use SSH/rsync/shared network filesystem as the primary sync strategy for live multi-device use.
Backend choice: prefer Postgres if it can be integrated without a broad new dependency surface or destabilizing the current runtime; otherwise use the least invasive remote backend that can be shipped with the current stack and document the tradeoff clearly. Preserve the current local SQLite mode as the default/offline fallback if possible.
This ticket should cover the full product/architecture change: configuration, storage access, stats reads, startup/error handling, migration/bootstrap from existing local data, tests, and docs.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 The app can be configured to use a remote authoritative backend for immersion tracking instead of only a local `immersion.sqlite` file.
- [ ] #2 The chosen backend persists tracker writes and serves the existing stats read models across app restarts.
- [ ] #3 Two devices can point at the same remote backend without relying on a shared filesystem or raw SQLite file sync.
- [ ] #4 Local SQLite remains supported as the default or fallback mode for offline use.
- [ ] #5 If the remote backend is unavailable or misconfigured, startup/write paths fail with actionable errors instead of silent data loss.
- [ ] #6 A migration or bootstrap path exists to move existing local immersion data into the remote backend or seed a new device from it.
- [ ] #7 Config/examples/docs explain the backend choice, required connection/setup details, and any security/network assumptions.
- [ ] #8 Tests cover backend selection plus at least one representative write/read path against the remote backend.
<!-- AC:END -->

View File

@@ -1,34 +0,0 @@
---
id: TASK-266
title: Preserve paused state for configured subtitle-jump keybindings
status: Done
assignee: []
created_date: '2026-04-01 03:19'
updated_date: '2026-04-01 03:19'
labels:
- renderer
- mpv
- keybindings
- regression
dependencies: []
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Regression: configured overlay keybindings that forward raw mpv subtitle-jump commands (for example previous-subtitle on H) can resume playback when invoked while paused. Keyboard-driven edge jumps already preserve paused state; configured keybindings should match that behavior.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Configured subtitle-jump keybindings preserve paused playback state after backward seek
- [x] #2 Existing keyboard-driven subtitle navigation behavior remains unchanged
- [x] #3 Regression test covers paused configured subtitle-jump keybinding handling
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Configured overlay keybindings that forward `sub-seek` commands now re-check paused state and reapply pause after the seek when playback was already paused. This aligns raw configured subtitle-jump keybindings with the existing keyboard-driven edge-jump behavior and adds regression coverage for the paused backward-seek case.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -1,34 +0,0 @@
---
id: TASK-267
title: Port validated Yomitan popup changes to fork and resync submodule
status: Done
assignee: []
created_date: '2026-04-01 03:30'
updated_date: '2026-04-01 03:33'
labels:
- yomitan
- submodule
- git
- integration
dependencies: []
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Take the locally validated Yomitan popup/bridge changes from the vendored copy, apply them to the standalone `../subminer-yomitan` fork, verify the fork, push the fork commit, then reset the vendored working tree in SubMiner and update the submodule pointer to the pushed fork commit.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Standalone `../subminer-yomitan` contains the validated popup/bridge changes and passes the relevant regression test
- [x] #2 The fork commit is pushed to its configured remote branch
- [x] #3 SubMiner vendored Yomitan working tree is reset and the submodule pointer is updated to the pushed fork commit
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Applied the validated popup/bridge changes from the vendored Yomitan copy into `../subminer-yomitan`, added the focused async-save regression test there, installed fork deps, and verified with `npx vitest run test/display-anki-save.test.js`. Committed the fork changes as `feat: preserve async popup save state and duplicate metadata`, rebased onto the updated remote `main`, and pushed commit `69620abc` to `origin/main`. Then reset the vendored submodule working tree in SubMiner, checked it out at `69620abc`, and left the superproject with the submodule pointer updated from `3c9ee577` to `69620abc`.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -1,44 +0,0 @@
---
id: TASK-268
title: 'Address CodeRabbit review action items for PR #38'
status: Done
assignee: []
created_date: '2026-04-01 05:35'
updated_date: '2026-04-01 06:07'
labels:
- pr-review
- coderabbit
dependencies: []
references:
- 'https://github.com/ksyasuda/SubMiner/pull/38'
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Review unresolved CodeRabbit feedback on PR #38 and implement the actionable fixes without regressing duplicate grouping or popup behavior.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 All unresolved actionable CodeRabbit review comments on PR #38 are triaged and either fixed in code or explicitly identified as non-actionable or ambiguous.
- [x] #2 Code changes preserve duplicate grouping and popup flow behavior covered by existing or added regression tests.
- [x] #3 Relevant local verification for the affected areas passes.
<!-- AC:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
2026-04-01: Reopened for follow-up CodeRabbit round after commit 233bde58. Remaining actionable items: guard maxMatches <= 0 in duplicate exact-match helper and strengthen the duplicate tracking test fixture to prove deduplication as well as sorting.
2026-04-01: Follow-up round addressed locally. Added guard for maxMatches <= 0 in duplicate exact-match scanning and strengthened the pre-add duplicate tracking test fixture to prove deduplication as well as sorting.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Addressed all unresolved actionable CodeRabbit comments on PR #38. Fixed duplicate tracking so empty duplicate lists are not persisted after sentence-card creation, sanitized Yomitan add-note noteId values to accept only positive integers, preserved paused playback for configured subtitle-seek keybindings when pause state is unknown, and short-circuited duplicate exact-match scanning for single-result lookups. Added regression tests for each case and verified with `bun test` on the affected suites plus `bun run typecheck`, `bun run test:fast`, `bun run test:env`, `bun run build`, and `bun run test:smoke:dist`.
Follow-up CodeRabbit round addressed locally: `findExactDuplicateNoteIds()` now returns early when `maxMatches <= 0`, and the sentence-card duplicate tracking regression test now uses a repeated duplicate ID to assert deduplication plus sorting. Re-verified with targeted duplicate/card tests, `bun run typecheck`, and `bun run test:fast`.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -1,30 +0,0 @@
---
id: TASK-270
title: Make Windows mpv shortcut self-contained
status: Done
assignee: []
created_date: '2026-04-02 07:13'
updated_date: '2026-04-02 07:19'
labels: []
dependencies: []
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Remove the Windows mpv shortcut's dependency on a pre-existing mpv profile so the installer-created `SubMiner mpv` flow works out of the box without requiring the user to edit `mpv.conf`. Keep docs aligned with the new behavior and preserve the optional profile guidance for manual mpv usage.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 `SubMiner.exe --launch-mpv` launches mpv with SubMiner's required default args without requiring an mpv profile named `subminer`.
- [x] #2 Windows shortcut/help/docs no longer describe `--launch-mpv` as depending on the SubMiner mpv profile.
- [x] #3 Automated tests cover the Windows launch args behavior and pass after the change.
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Updated the Windows `--launch-mpv` path to pass SubMiner's default mpv args directly instead of requiring `--profile=subminer`. Adjusted Windows shortcut/help text to describe the self-contained defaults-based launch, and updated Windows docs to state that `mpv.conf` is not required for the shortcut path while preserving the optional profile guidance for manual mpv launches.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -1,56 +0,0 @@
---
id: TASK-271
title: Fix local playback subtitle auto-selection and startup pause release
status: Done
assignee:
- codex
created_date: '2026-04-03 07:47'
updated_date: '2026-04-03 08:03'
labels:
- bug
- playback
- subtitles
dependencies: []
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Investigate local-video startup on desktop playback where managed subtitle defaults can bind the wrong primary subtitle track and startup readiness retries can force playback to resume after the user manually pauses. Scope includes fixing the pause/unpause loop and making local subtitle auto-selection prefer the intended primary/secondary tracks for sentence mining sessions.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Desktop local playback no longer forces `pause=no` after the user manually pauses during or after startup readiness handling.
- [x] #2 Managed local subtitle startup selects the expected primary track before secondary track selection for mixed-language subtitle sets like Japanese primary plus English secondary.
- [x] #3 Regression tests cover the pause-release bug and the local subtitle auto-selection behavior.
- [x] #4 Internal docs are updated if runtime behavior or operator expectations change.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add regression coverage for startup autoplay release so duplicate ready handling cannot unpause playback after the user manually pauses the same media.
2. Add regression coverage for managed local subtitle startup selection using configured primary/secondary subtitle language preferences, with JA/EN remaining the default fallback behavior.
3. Extract or add reusable subtitle track ranking logic that prefers configured primary and secondary subtitle languages, with stable scoring for external tracks and non-SDH labels.
4. Update local playback startup/runtime wiring so managed subtitle defaults use explicit ranked selection instead of raw sid=auto / secondary-sid=auto while preserving config-driven language preference ordering.
5. Run focused subtitle/playback tests, then update task notes/final summary with any behavior or docs impact.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Implemented config-aware managed local subtitle selection runtime for local media path changes and playlist-browser local playback rearm, using `youtube.primarySubLanguages` for primary preference and `secondarySub.secondarySubLanguages` for secondary preference with JA/EN fallback defaults.
Updated autoplay ready gate to ignore duplicate readiness signals for the same media so later manual pauses are not overridden by repeated tokenization-ready events.
Updated config/template wording to document that the existing subtitle language preferences now drive managed subtitle auto-selection beyond YouTube-only flows.
Verification: `bun test src/config/config.test.ts src/main/runtime/autoplay-ready-gate.test.ts src/main/runtime/local-subtitle-selection.test.ts src/main/runtime/playlist-browser-runtime.test.ts`; `bun run typecheck`.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Stopped startup readiness retries from re-unpausing the same media after a later manual pause, and added config-aware managed local subtitle selection so local playback prefers the configured primary/secondary subtitle languages instead of relying on raw mpv `sid=auto` behavior. Added regression coverage for autoplay-ready gating, local subtitle selection, playlist-browser local playback rearm, and config template wording updates.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -1,53 +0,0 @@
---
id: TASK-273
title: >-
Fix first-run setup false positive when canonical mpv plugin is already
installed
status: Done
assignee:
- Kyle Yasuda
created_date: '2026-04-03 23:26'
updated_date: '2026-04-04 00:31'
labels:
- bug
- macos
- first-run-setup
dependencies: []
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Investigate and fix launcher/app first-run setup gating so playback does not block when the SubMiner mpv plugin is already installed at the canonical mpv config path on macOS. Align mpv path resolution with the actual install location, keep plugin detection scoped to the canonical plugin entrypoint, and make launcher setup gating resilient to stale cancelled setup state.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 `resolveDefaultMpvInstallPaths` resolves the canonical macOS mpv config path used by existing installs.
- [ ] #2 Playback launcher bypasses first-run setup when the canonical `scripts/subminer/main.lua` plugin entrypoint already exists, even if `setup-state.json` is stale.
- [ ] #3 Regression tests cover canonical plugin detection and launcher handling of stale cancelled setup state.
<!-- AC:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Root cause ended up split across path resolution and launcher gating. No automated test command was executed in this pass by request.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Updated macOS mpv install path resolution to use the canonical `~/.config/mpv` location so first-run plugin detection matches the actual installed plugin path.
Restricted plugin detection to the canonical `scripts/subminer/main.lua` entrypoint instead of config presence or legacy loader files.
Updated the launcher setup gate to bypass stale `setup-state.json` when the mpv plugin is already installed, and to ignore an initially stale `cancelled` state after spawning setup.
Added regression coverage for canonical macOS detection and launcher setup-gate bypass behavior. No automated test command was executed in this pass by request.
<!-- SECTION:FINAL_SUMMARY:END -->
## Definition of Done
<!-- DOD:BEGIN -->
- [ ] #1 Manual verification with scenario: existing plugin installed in custom mpv config path does not open first-run setup.
<!-- DOD:END -->

View File

@@ -1,59 +0,0 @@
---
id: TASK-274
title: Stabilize current failing test regressions
status: Done
assignee:
- codex
created_date: '2026-04-04 04:40'
updated_date: '2026-04-04 05:01'
labels: []
dependencies: []
documentation:
- docs/workflow/verification.md
- docs/architecture/README.md
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Investigate and fix the current src/test failures across stats CLI lifetime rebuild handling, immersion tracker lifetime rebuild idempotency, renderer test environment cleanup helpers, subtitle sidebar auto-follow behavior, and log retention pruning so the maintained test lanes pass again.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Stats CLI lifetime rebuild behavior passes the current regression coverage.
- [x] #2 Immersion tracker lifetime rebuild backfill remains idempotent under the existing runtime test.
- [x] #3 Renderer modal test helpers restore injected globals exactly to prior state.
- [x] #4 Log pruning removes files older than the configured retention window deterministically.
- [x] #5 Relevant targeted test files pass after the fixes.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Reproduce the failing specs in isolation to separate deterministic regressions from suite-order pollution.
2. Fix source or test-helper logic for the three isolated failures: log retention cutoff, stats CLI lifetime rebuild timestamp handling, and subtitle sidebar initial jump behavior.
3. Harden renderer modal cleanup regressions so tests verify descriptor restoration without assuming global window/document start absent.
4. Re-run the targeted failing files, then the required verification gate for the touched areas and record results.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Targeted regressions fixed in log pruning, stats CLI lifetime logging/tests, subtitle sidebar auto-follow timing, and renderer global cleanup test isolation.
Verification: `bun test src/main/runtime/stats-cli-command.test.ts src/shared/log-files.test.ts src/renderer/modals/playlist-browser.test.ts src/renderer/modals/youtube-track-picker.test.ts src/renderer/modals/subtitle-sidebar.test.ts` passed.
Verification: `bun run test:src` still exits non-zero because of unrelated existing errors in `src/core/services/anilist/anilist-token-store.test.ts` (`Bun.serve is not a function`) plus one remaining non-task failure elsewhere; the originally reported regressions are green in the maintained lane.
User reported `test:full` still failing after the first regression pass. Reopened to clear the remaining `test:src` fail plus the existing unhandled test errors before handoff.
Verified final gate with `bun run test:launcher:unit:src` and `bun run test:full`; both pass after fixing the launcher AniSkip fallback title regression and the earlier src-lane regressions.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Stabilized the failing test regressions across source and launcher lanes. Fixed log pruning cutoff math under Bun BigInt mtimes, subtitle sidebar auto-follow timing, renderer global cleanup test isolation, stats CLI lifetime rebuild logging/tests, stats-server node:http fallback isolation, and launcher AniSkip fallback title resolution so basename titles beat generic parent directories while episode-only filenames still prefer the series directory. Verification passed with `bun test launcher/aniskip-metadata.test.ts`, `bun run test:launcher:unit:src`, and `bun run test:full`.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -1,56 +0,0 @@
---
id: TASK-275
title: Patch high-severity audit findings with minimal dependency changes
status: Done
assignee:
- codex
created_date: '2026-04-04 04:45'
updated_date: '2026-04-04 04:50'
labels:
- security
- dependencies
dependencies: []
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Update SubMiner's direct Electron runtime and vulnerable build-time transitive dependencies to patched versions using the smallest safe version moves. Keep electron-builder on the current pinned line unless verification shows a blocker. Verify that bun audit no longer reports the current high-severity findings and that the standard project gate still passes.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Electron is updated to a patched supported release on the current supported line with no broader dependency refresh
- [x] #2 Vulnerable transitive packages @xmldom/xmldom, lodash, and picomatch resolve to patched versions via targeted dependency changes
- [x] #3 `bun audit --audit-level high` no longer reports the currently listed high-severity findings
- [x] #4 The default handoff verification gate passes, or any failure is documented with the exact command and error output
- [x] #5 Any dependency or lockfile changes remain minimal and do not change the pinned electron-builder line unless required
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Update package.json with the smallest set of dependency changes: bump electron from ^37.10.3 to 39.8.6 and add overrides for @xmldom/xmldom 0.8.12, lodash 4.18.0, and picomatch 4.0.4 while leaving electron-builder pinned at 26.8.2.
2. Refresh bun.lock with a lockfile-only install/update and confirm the resolved versions for electron, @xmldom/xmldom, lodash, and picomatch.
3. Run bun audit --audit-level high and verify the current high-severity findings are gone.
4. Run the default verification gate: bun run typecheck, bun run test:fast, bun run test:env, bun run build, bun run test:smoke:dist.
5. If any verification step fails, capture the exact failing command and error, assess whether it is caused by the dependency updates, and stop without broadening scope.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Updated package.json to pin electron 39.8.6 and add overrides for @xmldom/xmldom 0.8.12, lodash 4.18.0, and picomatch 4.0.4 while keeping electron-builder pinned at 26.8.2.
Refreshed bun.lock with bun install and confirmed the patched versions resolved in the lockfile.
Verification passed: bun audit --audit-level high, bun run typecheck, bun run test:fast, bun run test:env, bun run build, bun run test:smoke:dist.
Added changelog fragment changes/patch-audit-dependencies.md for the security/dependency maintenance update. No internal docs or docs-site updates were needed because the change does not alter user-facing behavior, configuration, or workflows.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Cleared the reported high-severity audit findings with minimal dependency churn by pinning electron to 39.8.6 and overriding @xmldom/xmldom, lodash, and picomatch to patched versions. Kept electron-builder on 26.8.2. bun audit is clean and the full default handoff gate passed: typecheck, fast tests, env tests, build, and dist smoke tests.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -1,62 +0,0 @@
---
id: TASK-276
title: Restore canonical SubMiner Linux app-id metadata
status: Done
assignee:
- codex
created_date: '2026-04-04 06:31'
updated_date: '2026-04-04 06:34'
labels:
- bug
- linux
- electron
dependencies: []
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Fix the Linux/Wayland packaged app metadata so the OS-facing desktop/app-id metadata stays canonical `SubMiner` instead of the lowercase npm package name `subminer`. Add regression coverage around packaged metadata so future Electron/runtime changes do not silently reintroduce the lowercase class/app-id behavior.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Top-level package metadata provides the canonical capitalized app name used by Electron runtime bootstrap on Linux
- [x] #2 Packaged Linux app metadata resolves to `SubMiner`/`SubMiner.desktop` instead of lowercase `subminer`
- [x] #3 Regression coverage fails before the fix and passes after it
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add a focused packaging/runtime regression test that reads the packaged app metadata source and asserts the Linux Electron bootstrap-visible fields resolve to canonical `SubMiner` / `SubMiner.desktop`.
2. Run the targeted test first to capture the failing pre-fix state.
3. Update top-level package metadata in `package.json` with the canonical Electron runtime-facing fields needed for Linux bootstrap.
4. Re-run the targeted test and a lightweight packaging validation to confirm the packaged metadata now stays canonical.
5. Record verification notes and complete the task if all acceptance criteria pass.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Added a regression in `src/release-workflow.test.ts` asserting top-level `productName` and `desktopName` stay canonical for Linux Electron runtime bootstrap. Verified the new test failed before the fix because both fields were missing from top-level package metadata.
Updated top-level `package.json` metadata with `productName: SubMiner` and `desktopName: SubMiner.desktop` so packaged `app.asar` exposes the canonical Linux startup identity Electron reads before app code runs.
Verification passed with `bun test src/release-workflow.test.ts`, `bun run build && ./node_modules/.bin/electron-builder --linux dir --publish never`, packaged `release/linux-unpacked/resources/app.asar` inspection showing `{ name: subminer, productName: SubMiner, desktopName: SubMiner.desktop }`, and `bun run changelog:lint`.
Ran the full default handoff gate after the targeted/package verification: `bun run typecheck`, `bun run test:fast`, `bun run test:env`, and `bun run test:smoke:dist` all passed.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Restored canonical Linux/Wayland app identity metadata by adding top-level Electron runtime fields to `package.json`: `productName: SubMiner` and `desktopName: SubMiner.desktop`. This fixes the packaged app metadata Electron reads before user code runs, so native Wayland compositors no longer need to derive the app-id/class from the lowercase npm package name alone.
Added a regression test in `src/release-workflow.test.ts` that asserts the runtime-visible top-level metadata stays canonical. The new test was run first and failed before the fix because `productName` was missing, then passed after the metadata update.
Verification: `bun test src/release-workflow.test.ts`; `bun run build && ./node_modules/.bin/electron-builder --linux dir --publish never`; inspected `release/linux-unpacked/resources/app.asar` and confirmed `productName: SubMiner` plus `desktopName: SubMiner.desktop`; `bun run changelog:lint`. Added changelog fragment `changes/2026-04-04-linux-app-id-metadata.md`.
Full default handoff gate also passed: `bun run typecheck`; `bun run test:fast`; `bun run test:env`; `bun run test:smoke:dist`.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -1,58 +0,0 @@
---
id: TASK-277
title: Restore Linux shortcut-backed modal actions after Electron 39 upgrade
status: Done
assignee:
- codex
created_date: '2026-04-04 06:49'
updated_date: '2026-04-04 07:08'
labels:
- bug
- linux
- electron
- shortcuts
dependencies: []
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Linux builds on Electron 39 no longer open the runtime options, Jimaku, or Subsync flows from their configured shortcuts (`Ctrl/Cmd+Shift+O`, `Ctrl+Shift+J`, `Ctrl+Alt+S`). Other renderer-driven modals like session help, subtitle sidebar, and playlist browser still work, which points to the Linux `globalShortcut` / overlay shortcut path rather than modal rendering in general. Investigate the Electron 39 regression and restore these Linux shortcut-triggered actions without regressing macOS or Windows behavior.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 On Linux, the configured runtime options, Jimaku, and Subsync shortcuts trigger their existing actions again under Electron 39.
- [x] #2 The fix is covered by automated tests around the Linux shortcut/backend behavior that regressed.
- [x] #3 A changelog fragment is added for the Linux shortcut regression fix.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Extend the special-command / mpv IPC command path to cover Jimaku alongside the existing runtime-options and subsync commands.
2. Add tests for IPC command dispatch and any keybinding/session-help metadata affected by the new special command.
3. Route Linux modal-opening shortcuts through the working mpv/IPC command path instead of depending on Electron globalShortcut delivery for those actions.
4. Re-run targeted tests, then the handoff verification gate, and update the changelog/task summary to reflect the final root cause and fix.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
User approved plan; proceeding with test-first implementation.
Root cause: Linux startup unconditionally enabled Electron's GlobalShortcutsPortal path, so Electron 39 X11 sessions were routed through the portal-backed globalShortcut path even though that path should only be used for Wayland-style launches. The affected runtime-options, Jimaku, and Subsync actions all depend on that shortcut path, while renderer-driven modals did not regress.
User retested after the portal gating fix; issue persists. Updating investigation scope from portal selection alone to Linux overlay shortcut delivery/registration under Electron 39.
Revised fix path: Linux renderer now matches configured overlay shortcuts for runtime options, subsync, and Jimaku locally, then dispatches the existing mpv/IPC special-command route instead of depending on Electron globalShortcut delivery.
Added Jimaku mpv special command plumbing through IPC/runtime deps and exposed an overlay-runtime helper for opening the Jimaku modal from that IPC path.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Restored Linux shortcut-backed modal actions by routing runtime options, Subsync, and Jimaku through the working mpv/IPC special-command path. Added renderer-side Linux shortcut matching for configured overlay shortcuts, threaded a new Jimaku special command through IPC/runtime deps, refreshed configured shortcuts on hot reload, and covered the regression with IPC/runtime keyboard tests. Verification: bun test src/core/services/ipc-command.test.ts src/renderer/handlers/keyboard.test.ts src/main/runtime/ipc-mpv-command-main-deps.test.ts; bun run typecheck; bun run test:fast; bun run test:env; bun run build; bun run test:smoke:dist.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -1,67 +0,0 @@
---
id: TASK-278
title: Prepare patch release 0.11.1
status: Done
assignee:
- '@codex'
created_date: '2026-04-04 07:16'
updated_date: '2026-04-04 07:41'
labels:
- release
dependencies: []
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Bump SubMiner from 0.11.0 to 0.11.1, run the local release checklist, and confirm whether the branch is ready for tagging/publishing.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 package.json is bumped to 0.11.1 without overwriting unrelated local metadata edits.
- [x] #2 Release prep checks are run and summarized, including changelog/build verification and local build/test gates.
- [x] #3 Any remaining release blockers are called out explicitly.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Generate release metadata with `bun run changelog:build --version 0.11.1 --date 2026-04-04`.
2. Review the resulting `CHANGELOG.md` and `release/release-notes.md` content for the 0.11.1 section.
3. Rerun the documented local release gate: `bun run changelog:check --version 0.11.1`, `bun run verify:config-example`, `bun run typecheck`, `bun run test:fast`, `bun run test:env`, and `bun run build`.
4. Record results, remaining blockers, and ready-for-tagging status in the task notes/final summary.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Bumped package.json from 0.11.0 to 0.11.1 while preserving the existing local productName/desktopName edits.
Fixed the Linux shortcut changelog fragment metadata so changelog lint now passes and the previously failing CI cause is addressed locally.
Release checks run: bun run changelog:lint, bun run changelog:check --version 0.11.1, bun run verify:config-example, bun run typecheck, bun run test:fast, bun run test:env, bun run build. All passed except changelog:check, which is expected until pending fragments are built into CHANGELOG.md/release/release-notes.md.
Current release blocker: pending changelog fragments /changes/2026-04-04-linux-app-id-metadata.md and /changes/2026-04-04-linux-shortcut-portal-regression.md still need bun run changelog:build --version 0.11.1 --date 2026-04-04 before tagging.
Reopened to complete the remaining release-prep work: generate changelog/release notes artifacts, rerun the documented release gate, and confirm ready-for-tagging status.
Completed the remaining release-prep step with `bun run changelog:build --version 0.11.1 --date 2026-04-04`, which prepended `CHANGELOG.md`, generated `release/release-notes.md`, and removed the two pending `changes/*.md` fragments.
Reran the release gate after changelog generation: `bun run changelog:check --version 0.11.1`, `bun run verify:config-example`, `bun run typecheck`, `bun run test:fast`, `bun run test:env`, and `bun run build`; all passed.
Extra confidence checks also passed: `bun run changelog:lint`, `bun run test:smoke:dist`, and `gh run list --workflow CI --limit 5` shows the two latest `main` CI runs succeeded on 2026-04-04 after the earlier pre-fix failure.
`release/release-notes.md` is intentionally generated under the gitignored `release/` directory, so readiness evidence in git status is `CHANGELOG.md` plus deletion of the released change fragments.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Finished the 0.11.1 release prep by generating the changelog artifacts from the pending Linux fix fragments and re-running the full documented local release gate. `CHANGELOG.md` now contains the `v0.11.1 (2026-04-04)` section, `release/release-notes.md` was regenerated in the ignored `release/` directory, and the released `changes/2026-04-04-linux-app-id-metadata.md` plus `changes/2026-04-04-linux-shortcut-portal-regression.md` fragments were removed.
Verification results: `bun run changelog:check --version 0.11.1`, `bun run verify:config-example`, `bun run typecheck`, `bun run test:fast`, `bun run test:env`, `bun run build`, `bun run changelog:lint`, and `bun run test:smoke:dist` all passed locally. Remote CI also looks green for the latest release-prep head: `gh run list --workflow CI --limit 5` showed successful `main` runs for `chore: prep 0.11.1 release` and `Change demo image link to GitHub asset` on 2026-04-04, with the earlier `fix: restore linux modal shortcuts` failure already superseded by later green runs.
Remaining manual release step: commit the generated release-prep changes if desired, tag `v0.11.1`, and push the commit plus tag when ready.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -1,62 +0,0 @@
---
id: TASK-279
title: Fix Linux AppImage child-process libffmpeg resolution
status: Done
assignee:
- '@codex'
created_date: '2026-04-05 17:17'
updated_date: '2026-04-05 17:56'
labels: []
dependencies: []
references:
- 'https://github.com/ksyasuda/SubMiner/issues/41'
documentation:
- docs/workflow/verification.md
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Fix the Linux AppImage packaging so Chromium child processes relaunched from the bundled binary can resolve the packaged libffmpeg shared library and SubMiner starts cleanly instead of crash-looping on network-service restarts.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Linux AppImage packaging ensures bundled Chromium child processes can resolve the packaged libffmpeg shared library during relaunch.
- [x] #2 Regression coverage exercises the Linux packaging/build configuration that provides the AppImage shared-library path.
- [x] #3 Release notes/changelog reflect the Linux AppImage startup fix.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add focused regression tests for Linux release packaging that assert the build config invokes an `afterPack` hook and that the hook stages bundled `libffmpeg.so` into `usr/lib` for AppImage runtime lookup.
2. Implement a small electron-builder `afterPack` hook that runs only for Linux, copies `libffmpeg.so` from the packaged app root into `usr/lib`, and no-ops when the source library is absent.
3. Wire the hook into `package.json` build config and add a changelog fragment for the Linux AppImage startup fix.
4. Run the focused test lane first, then the default handoff gate because the change touches release-sensitive packaging behavior.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Chose a repo-local electron-builder `afterPack` hook instead of patching/forking `electron-builder`. The hook copies bundled `libffmpeg.so` from the packaged Linux app root into `usr/lib`, matching the AppImage runtime's existing `LD_LIBRARY_PATH` search path.
Added regression coverage for both config wiring (`src/release-workflow.test.ts`) and the hook behavior (`scripts/electron-builder-after-pack.test.ts`), then wired the new script test into `test:fast` so the maintained lane keeps exercising the fix.
Verification passed: `bun test scripts/electron-builder-after-pack.test.ts src/release-workflow.test.ts`, `bun run typecheck`, `bun run test:fast`, `bun run test:env`, `bun run build`, `bun run test:smoke:dist`.
Addressed PR #45 CodeRabbit review thread: Linux `afterPack` staging now hard-fails when `libffmpeg.so` is missing instead of silently no-oping. Updated focused hook tests to assert the new failure contract and that `afterPack` propagates Linux staging errors.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Added a shared electron-builder `afterPack` hook at `scripts/electron-builder-after-pack.cjs` and wired it into `package.json` so Linux packaging stages the bundled `libffmpeg.so` into `usr/lib` inside the packaged app. This keeps Chromium child relaunches compatible with the AppImage runtime's existing `LD_LIBRARY_PATH` layout without forking or patching upstream `electron-builder`.
Regression coverage now checks both the packaging config and the hook behavior: `src/release-workflow.test.ts` asserts the hook stays wired into release config, and `scripts/electron-builder-after-pack.test.ts` verifies Linux copies `libffmpeg.so` into `usr/lib` while non-Linux and missing-library cases no-op safely. The new script test is included in `test:fast`, and a changelog fragment was added under `changes/fix-appimage-libffmpeg-path.md`.
Verification passed with `bun test scripts/electron-builder-after-pack.test.ts src/release-workflow.test.ts`, `bun run typecheck`, `bun run test:fast`, `bun run test:env`, `bun run build`, and `bun run test:smoke:dist`.
Follow-up review fix on PR #45: Linux packaging now throws when `libffmpeg.so` is missing from the packaged app root, preventing silent shipment of a broken AppImage. Focused regression coverage was updated so the missing-library case rejects and `afterPack` propagates the failure.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -1,59 +0,0 @@
---
id: TASK-280
title: >-
Force launcher-spawned mpv onto X11 when backend resolves to x11 or no
supported Wayland tracker is available
status: Done
assignee:
- codex
created_date: '2026-04-05 21:01'
updated_date: '2026-04-05 21:05'
labels:
- bug
- linux
- launcher
- overlay
dependencies: []
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
On Linux Plasma Wayland and similar sessions, `subminer --backend=x11` currently only changes SubMiner's window-tracker override. The launcher still spawns mpv without forcing an X11/XWayland backend, so the X11 tracker cannot find the mpv window and the overlay remains hidden. Update launcher-side mpv spawn behavior so launcher-managed mpv runs under X11 when backend resolves to `x11`, and also when auto detection cannot resolve to a supported Wayland tracker. Preserve existing Hyprland/Sway behavior.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Launcher-managed mpv is spawned with X11/XWayland-forcing environment/config when backend resolves to `x11`.
- [x] #2 Linux auto mode falls back to X11/XWayland-forced mpv when no supported Wayland tracker backend is detected.
- [x] #3 Hyprland and Sway launcher flows do not regress to forced X11 mpv.
- [x] #4 Regression tests cover launcher env/backend selection for these Linux cases.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add focused launcher tests that assert the mpv spawn environment forces X11 when backend resolves to `x11`, and when Linux auto mode cannot use a supported Wayland tracker.
2. Refactor launcher mpv spawn code to compute an mpv-specific environment without changing existing Hyprland/Sway flows.
3. Route all launcher-managed mpv spawns through the new environment helper.
4. Run focused launcher tests, then summarize behavior and any remaining verification gaps.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
User approved scope: force launcher-managed mpv to X11 for explicit `--backend=x11` and for unsupported Linux Wayland auto-detect fallback; preserve Hyprland/Sway behavior.
Implemented launcher-side `buildMpvEnv` to strip Wayland hints and force X11/XWayland for launcher-managed mpv when `--backend=x11`, and for Linux auto mode on unsupported Wayland desktops with an X11 display available. Wired both normal mpv launches and idle detached mpv launches through the helper.
Verification: `bun test launcher/mpv.test.ts --test-name-pattern "buildMpvEnv"` passed; `bun run tsc --noEmit` passed. A broader `bun test launcher/mpv.test.ts` run still hits a pre-existing sandbox-specific failure in `launchAppCommandDetached handles child process spawn errors` because this environment cannot write the default app log path under `/home/sudacode/.config/SubMiner/logs`.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Updated the launcher so mpv gets an X11/XWayland-oriented spawn environment whenever the user explicitly requests `--backend=x11`, and when Linux auto mode is running under an unsupported Wayland desktop that still exposes an X11 display. The new helper reuses the launcher child-process base environment, strips Wayland-specific hints (`WAYLAND_DISPLAY`, Hyprland/Sway markers), and flips `XDG_SESSION_TYPE` to `x11` only for those fallback cases. Both foreground mpv launches and detached idle mpv launches now use the same helper so overlay-tracked playback stays consistent.
Added focused regression coverage in `launcher/mpv.test.ts` for three cases: explicit `x11` forcing, unsupported Wayland auto fallback (for example KDE Plasma Wayland), and preserving native Wayland env for supported Hyprland/Sway auto backends. Verification completed with `bun test launcher/mpv.test.ts --test-name-pattern "buildMpvEnv"` and `bun run tsc --noEmit`. A broader launcher mpv test run still shows an unrelated sandbox write failure for the default app log path in this environment.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -1,62 +0,0 @@
---
id: TASK-281
title: Prevent Windows launcher tests from leaking backslash temp files on POSIX
status: Done
assignee:
- codex
created_date: '2026-04-05 21:13'
updated_date: '2026-04-05 21:20'
labels:
- tests
- launcher
- bug
dependencies: []
documentation:
- /home/sudacode/github/SubMiner2/AGENTS.md
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Windows-specific launcher tests in launcher/mpv.test.ts currently create real filesystem entries using path.win32.join(...) with a POSIX mkdtemp base. On Linux/macOS this produces literal backslash-named paths like \\tmp\\subminer-test-win-dir-* inside the repo/worktree, and the existing cleanup only removes the POSIX /tmp base directory. Fix the tests so they still cover Windows path resolution behavior without leaking stray files into the working tree.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Running the Windows findAppBinary tests on a POSIX host does not create new untracked \\tmp\\subminer-test-win-* files in the repository root.
- [x] #2 The Windows launcher tests still validate PATH and install-directory resolution behavior.
- [x] #3 Relevant launcher tests pass after the change.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add a regression test in launcher/mpv.test.ts that exercises the Windows findAppBinary cases on a POSIX host and asserts they do not leave new backslash-named temp artifacts in the repository root.
2. Refactor the Windows launcher tests to avoid creating real filesystem paths from path.win32.join(...) on POSIX; keep Windows path assertions via stubs and only create real files with native POSIX paths where needed.
3. Run the targeted launcher tests and confirm no new \\tmp\\subminer-test-win-* artifacts appear in git status.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Investigation: reproduced the leak locally. The source is launcher/mpv.test.ts Windows findAppBinary tests that combine a POSIX mkdtemp base with path.win32.join(...), creating literal backslash-named entries like \\tmp\\subminer-test-win-dir-* in the repo root. Existing cleanup only removes the POSIX /tmp base directory.
User approved implementation plan on 2026-04-05.
Implemented in launcher/mpv.test.ts by replacing the leaky Windows PATH/install-directory helpers with pure fs stubs (access/exists/stat) and fixed Windows path strings instead of creating real path.win32 filesystem entries on POSIX. Added a regression test that snapshots repo-root \\tmp\\subminer-test-win-* artifacts before/after running the Windows cases and asserts no new entries are created.
Verification: `bun test launcher/mpv.test.ts --test-name-pattern 'findAppBinary Windows cases do not leak backslash temp artifacts on POSIX|findAppBinary resolves SubMiner.exe on PATH on Windows|findAppBinary resolves a Windows install directory to SubMiner.exe'` passed (3/3). `bun test launcher/mpv.test.ts` still has one unrelated pre-existing sandbox failure in `launchAppCommandDetached handles child process spawn errors` because the test opens `~/.config/SubMiner/logs/app-2026-04-05.log` and hits `EROFS` in this environment.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Reworked the Windows `findAppBinary` tests in `launcher/mpv.test.ts` so they no longer create real backslash-named temp files on POSIX hosts. The PATH and install-directory cases now use synthetic Windows path strings plus `fs.accessSync` / `fs.existsSync` / `fs.statSync` stubs to exercise the same resolver behavior without writing `\\tmp\\subminer-test-win-*` entries into the repository root.
Added a POSIX regression test that snapshots existing repo-root `\\tmp\\subminer-test-win-*` artifacts, runs the Windows path-resolution cases, and asserts the artifact set is unchanged. This catches future regressions where a Windows-path test accidentally writes literal backslash paths on Linux/macOS.
Tests run:
- `bun test launcher/mpv.test.ts --test-name-pattern 'findAppBinary Windows cases do not leak backslash temp artifacts on POSIX|findAppBinary resolves SubMiner.exe on PATH on Windows|findAppBinary resolves a Windows install directory to SubMiner.exe'`
- `bun test launcher/mpv.test.ts` (all relevant `findAppBinary` tests passed; one unrelated existing sandbox failure remains in `launchAppCommandDetached handles child process spawn errors` due `EROFS` opening `~/.config/SubMiner/logs/...`)
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -1,58 +0,0 @@
---
id: TASK-282
title: >-
Force launcher-managed mpv to an explicit X11 GPU context when X11 fallback is
active
status: Done
assignee:
- codex
created_date: '2026-04-05 21:14'
updated_date: '2026-04-05 21:15'
labels:
- bug
- linux
- launcher
- mpv
- overlay
dependencies: []
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Follow-up to the launcher X11 fallback work: on Plasma Wayland, stripping Wayland env vars alone is not sufficient for launcher-managed mpv. mpv can still fail GPU initialization under the default video output path (`vo/gpu-next`) unless an explicit X11 GPU context is selected. Update launcher-managed mpv startup so X11 fallback mode also appends explicit mpv options for an X11 GPU context, while preserving supported Hyprland/Sway Wayland flows.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Launcher-managed mpv appends explicit X11 GPU context args when explicit `--backend=x11` is used on Linux.
- [x] #2 Launcher-managed mpv appends the same explicit X11 GPU context args for Linux auto-mode fallback on unsupported Wayland desktops.
- [x] #3 Supported Hyprland/Sway Wayland flows do not receive the forced X11 GPU context args.
- [x] #4 Regression tests cover the forced-X11 mpv arg selection.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add focused launcher tests for explicit X11 GPU-context arg selection, unsupported Wayland auto fallback, and Hyprland/Sway no-regression cases.
2. Introduce a shared launcher helper that decides when mpv should be forced onto X11 fallback mode.
3. Use that helper to append explicit mpv X11 GPU-context args in both normal and detached idle mpv launch paths.
4. Run focused launcher tests plus TypeScript verification, then record remaining runtime follow-up guidance.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
User runtime log showed `vo/gpu-next` failing with `Failed initializing any suitable GPU context!` under forced-X11 playback, which indicates env forcing alone was insufficient. Selected `--gpu-context=x11egl,x11` as the explicit mpv fallback: prefer X11/EGL, with GLX as a compatibility fallback.
Verification: `bun test launcher/mpv.test.ts --test-name-pattern "buildMpv(Env|BackendArgs)"` passed. `bun run tsc --noEmit` passed.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Added explicit mpv backend args for launcher-managed X11 fallback mode. The launcher now uses a shared `shouldForceX11MpvBackend` decision for both env rewriting and mpv arg selection, so explicit `--backend=x11` and unsupported Linux Wayland auto fallback both append `--gpu-context=x11egl,x11` while still stripping Wayland env hints. This preserves supported Hyprland/Sway native Wayland flows and makes the X11 fallback more explicit for mpv's GPU initialization path.
Wired the new X11 GPU-context args into both the normal playback launch path and the detached idle mpv launch path. Added focused regression coverage for explicit `x11`, Plasma-style unsupported Wayland auto fallback, and Hyprland/Sway no-regression behavior. Verification completed with `bun test launcher/mpv.test.ts --test-name-pattern "buildMpv(Env|BackendArgs)"` and `bun run tsc --noEmit`.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -1,56 +0,0 @@
---
id: TASK-283
title: Force launcher-managed X11 fallback to mpv vo=gpu with OpenGL on Linux
status: Done
assignee:
- codex
created_date: '2026-04-05 21:19'
updated_date: '2026-04-05 21:20'
labels:
- bug
- linux
- launcher
- mpv
- overlay
dependencies: []
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Follow-up to explicit X11 gpu-context forcing: Plasma Wayland runtime logs still show mpv using `vo/gpu-next` and failing to initialize any suitable GPU context under launcher-managed X11 fallback. Update launcher-managed mpv X11 fallback mode to force a more compatible renderer stack: `--vo=gpu`, `--gpu-api=opengl`, and `--gpu-context=x11egl,x11`, while preserving supported native Wayland flows and allowing explicit user mpv args to override later on the command line.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Launcher-managed mpv appends `--vo=gpu`, `--gpu-api=opengl`, and `--gpu-context=x11egl,x11` when explicit `--backend=x11` is used on Linux.
- [x] #2 Launcher-managed mpv appends the same renderer args for Linux auto-mode fallback on unsupported Wayland desktops.
- [x] #3 Supported Hyprland/Sway Wayland flows do not receive the forced X11 renderer args.
- [x] #4 Regression tests cover the forced-X11 renderer arg selection.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Tighten launcher tests so forced-X11 renderer args require `--vo=gpu`, `--gpu-api=opengl`, and `--gpu-context=x11egl,x11`.
2. Update the shared launcher X11-fallback helper to return the full renderer arg stack for explicit `x11` and unsupported Wayland auto fallback.
3. Re-run focused launcher env/backend tests and TypeScript verification.
4. Hand back with retry instructions and next debugging branch if runtime still fails.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Runtime log still showed `[vo/gpu-next] Failed initializing any suitable GPU context!`, which meant forcing only the context was not enough. Updated the fallback to force the classic OpenGL renderer path too: `--vo=gpu --gpu-api=opengl --gpu-context=x11egl,x11`.
Verification: `bun test launcher/mpv.test.ts --test-name-pattern "buildMpv(Env|BackendArgs)"` passed. `bun run tsc --noEmit` passed.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Updated launcher-managed X11 fallback mode to force a more compatible mpv renderer stack on Linux: `--vo=gpu`, `--gpu-api=opengl`, and `--gpu-context=x11egl,x11`. This applies both to explicit `--backend=x11` and to unsupported Wayland auto fallback, while supported Hyprland/Sway Wayland sessions still keep their native path. The renderer args are still inserted before user-supplied `--args`, so an explicit user override can win later on the command line if needed.
Adjusted regression coverage to require the full renderer stack for forced-X11 mode and verified the helper behavior with focused launcher tests plus TypeScript compilation. Verification completed with `bun test launcher/mpv.test.ts --test-name-pattern "buildMpv(Env|BackendArgs)"` and `bun run tsc --noEmit`.
<!-- SECTION:FINAL_SUMMARY:END -->

Some files were not shown because too many files have changed in this diff Show More