Compare commits
328 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 904ca3f3bb | |||
| 7f2d84ad42 | |||
| 9f619d73ef | |||
| a07d5ecdb3 | |||
| f33b5e1e98 | |||
| 1d3f099e44 | |||
| 36e9346595 | |||
| 64acf22292 | |||
| e0621d042d | |||
| 0a2461f45a | |||
| badb82280a | |||
| f1dc418e2d | |||
| edfe6640ac | |||
| 43a8a37f5b | |||
| 4deef69928 | |||
| a6d85def34 | |||
| 420b985c7a | |||
| 82a9d83820 | |||
| e1ffd8770f | |||
| c480fe6ad4 | |||
| 4be3ecf7ac | |||
| f6e7dd496a | |||
| b3b55de4b9 | |||
| 26c031aea8 | |||
| 44c7b05f96 | |||
| 7a561fca45 | |||
| 4682938d17 | |||
| 01f01f18e3 | |||
| 430c4e7120 | |||
| 8b8a99dc79 | |||
| 75c3b15792 | |||
| 00170c6a75 | |||
| da48fdeb97 | |||
| ace809b575 | |||
| 704e664cc3 | |||
| 47301d7492 | |||
| 4ad8109508 | |||
| c749430c77 | |||
| 05be13be9e | |||
| 2a5830c4c5 | |||
| 16b8d80498 | |||
| ebec472daf | |||
| 631e0450b1 | |||
| 7a869ad291 | |||
| b71a1a3d29 | |||
| 7e1a7df403 | |||
| 10b94ce889 | |||
| 2b77ab2406 | |||
| a693cc1866 | |||
| 5cb0ee1591 | |||
| 54109deb94 | |||
| 69474c9642 | |||
| 5805d774ca | |||
| c8c7f46a16 | |||
| f8db9e7119 | |||
| 4fc34ec787 | |||
| 23b88bf20e | |||
| 8ad8ff1671 | |||
| b271a3b1a9 | |||
| 35580ea3e9 | |||
| ab1d5f19fd | |||
| 1c424b4a0b | |||
| e1338113b5 | |||
| 8ac3d517fe | |||
| d6676f7132 | |||
| 28d2da1e64 | |||
| 06892b4838 | |||
| 12c5d956bc | |||
| eef8a7eb41 | |||
| 2d89dd43f2 | |||
| 2ffd503898 | |||
| 2b70b54faf | |||
| 6634ee7626 | |||
| 9b3cb4a42c | |||
| 9db54f8037 | |||
| e8db67e621 | |||
| b6b81a72f5 | |||
| f56de54c10 | |||
| 5b432fa156 | |||
| 3aeb10ae61 | |||
| 062677dcc5 | |||
| 65878e0d8a | |||
| 5b84ba5ef8 | |||
| 40184c67ed | |||
| 2be7829aa5 | |||
| a33d030d34 | |||
| 6287409c83 | |||
| e1a66800b9 | |||
| e73381aa36 | |||
| c3afea6d40 | |||
| 86e0527630 | |||
| 98902b6b0e | |||
| 4010fc1b04 | |||
| c6fa197d0d | |||
| 197636aabe | |||
| 46a2ac5dc7 | |||
| a4532a5fa0 | |||
| 06e8223d63 | |||
| ad2652b21a | |||
| a3569afdcf | |||
| 18648cb6fc | |||
| 5476d44005 | |||
| df380ed1ca | |||
| 0d7b65ec88 | |||
| 8c2d82e361 | |||
| bc75a0cfbd | |||
| 4193a6ce8e | |||
| 45c326db6d | |||
| c9605345bb | |||
| aaa19a33c5 | |||
| 1efc0f8650 | |||
| 7795cc3d69 | |||
| d5d71816ac | |||
| 162be118e1 | |||
| 58f28b7b55 | |||
| 9384d67b8e | |||
| 9e6d039a32 | |||
| 07cedabfe3 | |||
| 2c2f342854 | |||
| e73a380f38 | |||
| 1c189974fc | |||
| 209ab73a31 | |||
| d1aeb3b754 | |||
| f299f2a19e | |||
| ebaed49f76 | |||
| 4703b995da | |||
| fd49e73762 | |||
| 3803d4d47b | |||
| 25fddce372 | |||
| f20d019c11 | |||
| ffeef9c136 | |||
| 6da8ddda3f | |||
| 846b075206 | |||
| a531527e1f | |||
| 7c1d81ea80 | |||
| 9b0fdab840 | |||
| 817a949f99 | |||
| 1233e3630f | |||
| e38a1c945e | |||
| a6a28f52f3 | |||
| e7a522a485 | |||
| ba94a33b46 | |||
| b4df3f8295 | |||
| 518015f534 | |||
| 37cc3a6b01 | |||
| 25faf3ef3e | |||
| a359e91b14 | |||
| 02034e6dc7 | |||
| 30c363375a | |||
| 804755bd3d | |||
| 78715e801c | |||
| 75d5389036 | |||
| 79bf5ebefb | |||
| 4d28efabd0 | |||
| 1cd1cdb11d | |||
| 23b78e6c9b | |||
| 48f93f4344 | |||
| a499554848 | |||
| 7610bba16e | |||
| 5602d751eb | |||
| 457e6f0f10 | |||
| 0eb2868805 | |||
| 1d7406f3d4 | |||
| 107971f151 | |||
| e142d2dc3b | |||
| f448106f92 | |||
| faf82fa3ed | |||
| 2622949ac7 | |||
| af17f54ecd | |||
| b6975eae07 | |||
| 1ab7e6e1da | |||
| f21fc95d17 | |||
| a38b4d8583 | |||
| 8e9d392b21 | |||
| 01a48f4714 | |||
| e3c870143f | |||
| dae1f817e0 | |||
| bec69d1b71 | |||
| c6ac962f7a | |||
| 2211c086c0 | |||
| e14dad410e | |||
| 8c8f828b59 | |||
| 667bde944c | |||
| f1b5082801 | |||
| 1ca9cbc20d | |||
| 47aeabbc58 | |||
| 3e445aee9e | |||
| 42b5b6ef89 | |||
| 396fde3011 | |||
| 20f5de1cf7 | |||
| 8ebf6f02ec | |||
| 2a2eee825c | |||
| af1200b8d7 | |||
| 0674daf9c4 | |||
| f492622a8b | |||
| ca2b7bb2fe | |||
| 3a27c026b6 | |||
| 88099e2ffa | |||
| a8682c33f2 | |||
| d8cf83d517 | |||
| 6332fc4800 | |||
| a1f196ee52 | |||
| fb20e1ca25 | |||
| 51829159f2 | |||
| 162223943d | |||
| 41f7d754cd | |||
| f9ef8b31b1 | |||
| 64dd5ecc3d | |||
| 824443d93b | |||
| 1fb8e2e168 | |||
| 1e20704d39 | |||
| bf1a866f2f | |||
| c432f35a91 | |||
| 354a1a5796 | |||
| 5a610d9d02 | |||
| 585fea972c | |||
| 84c2bbcc0d | |||
| 65d9f5d54d | |||
| 910cf2dca4 | |||
| d2ca24f1c7 | |||
| 61cf0a2570 | |||
| d8859ec918 | |||
| ca916d2ef5 | |||
| 0bd58f72ea | |||
| 603cafff20 | |||
| c5f4ffebe5 | |||
| 94c1b131ef | |||
| 0bd1a18cd7 | |||
| c8b286ad66 | |||
| fcfd98843c | |||
| 656d686208 | |||
| a1209ca69f | |||
| cb9a599b23 | |||
| 3dfa713b29 | |||
| c16bc26a58 | |||
| 9c542b57e0 | |||
| 7856967920 | |||
| 6dcb979f4c | |||
| 978a859cc2 | |||
| f345547963 | |||
| dfb54630df | |||
| 402788b1e2 | |||
| 5162cf416a | |||
| 986ae971d1 | |||
| 185915628d | |||
| b8d9873f14 | |||
| 79616abde9 | |||
| 97d063baa8 | |||
| 8a82a1b5f9 | |||
| ee21c77fd0 | |||
| 1d36409fc7 | |||
| bba1bd554e | |||
| 781e6dd4fa | |||
| 9f0f8a2ce9 | |||
| 7a83fc5168 | |||
| 08d44499d3 | |||
| cfdc6668df | |||
| b6f3d0aad3 | |||
| dc54daa79d | |||
| 9d49e9eaa8 | |||
| a37ab476dd | |||
| b5fcd4f072 | |||
| 09e142279a | |||
| 531f8027bd | |||
| 35cad19839 | |||
| f868fdbbb3 | |||
| 5cc22e3f1b | |||
| 36085b6d1c | |||
| 8343b42b8e | |||
| 579661fbef | |||
| 1c69452356 | |||
| 321e59b3ac | |||
| f2e9c37f61 | |||
| 2d3bb19458 | |||
| 31f76ad476 | |||
| cb93601e16 | |||
| a17c2296d5 | |||
| b177be0831 | |||
| 528cf0a57e | |||
| 7bad8ac65e | |||
| 444873c803 | |||
| f1cf13b59c | |||
| 073f84b03e | |||
| 119f0da7a6 | |||
| adcae2ee01 | |||
| e95728b4d1 | |||
| 3686788a72 | |||
| b21204c7a0 | |||
| bb605fc051 | |||
| bafe400b95 | |||
| 2f34119a67 | |||
| db327c4ea8 | |||
| a6f20730ad | |||
| 6ca07abbc2 | |||
| 8b286f15e8 | |||
| 2878a1f3d1 | |||
| 83fd351080 | |||
| ff389208c8 | |||
| 7715c1ddd2 | |||
| 53c2e6353a | |||
| 3be0258286 | |||
| 35c2dd0e32 | |||
| 688eedbfc0 | |||
| 9c4a9769a5 | |||
| dbfd9105de | |||
| 10d9cc2db4 | |||
| d516238aba | |||
| 8ab04c3fa6 | |||
| 469091a2a8 | |||
| 8ba1b7953d | |||
| d796a7cfa5 | |||
| 2ff66f1621 | |||
| e773db7e88 | |||
| 57d4d4602c | |||
| 5ef5da2f8c | |||
| ee5803883e | |||
| 5c600b0cbe | |||
| 31d90b0296 | |||
| 250989c495 | |||
| 3f36c3d85b | |||
| a1846ba23d | |||
| a25c39dd22 | |||
| 1aa0e410ec | |||
| 0402b773ed | |||
| 3fb18c6b03 | |||
| f61524bef4 | |||
| 6922a6741f | |||
| 272d92169d |
@@ -1,127 +0,0 @@
|
||||
---
|
||||
name: "subminer-change-verification"
|
||||
description: "Use when working in the SubMiner repo and you need to verify code changes actually work. Covers targeted regression checks during debugging and pre-handoff verification, with cheap-first lane selection for config, docs, launcher/plugin, runtime-compat, and optional real-runtime escalation."
|
||||
---
|
||||
|
||||
# SubMiner Change Verification
|
||||
|
||||
Use this skill for SubMiner code changes. Default to cheap, repo-native verification first. Escalate only when the changed behavior actually depends on Electron, mpv, overlay/window tracking, or other GUI-sensitive runtime behavior.
|
||||
|
||||
## Scripts
|
||||
|
||||
- `scripts/classify_subminer_diff.sh`
|
||||
- Emits suggested lanes and flags from explicit paths or current git changes.
|
||||
- `scripts/verify_subminer_change.sh`
|
||||
- Runs selected lanes, captures artifacts, and writes a compact summary.
|
||||
|
||||
If you need an explicit installed path, use the directory that contains this `SKILL.md`. The helper scripts live under:
|
||||
|
||||
```bash
|
||||
export SUBMINER_VERIFY_SKILL="<path-to-skill>"
|
||||
```
|
||||
|
||||
## Default workflow
|
||||
|
||||
1. Inspect the changed files or user-requested area.
|
||||
2. Run the classifier unless you already know the right lane.
|
||||
3. Run the verifier with the cheapest sufficient lane set.
|
||||
4. If the classifier emits `flag:real-runtime-candidate`, do not jump straight to runtime verification. First run the non-runtime lanes.
|
||||
5. Escalate to explicit `--lane real-runtime --allow-real-runtime` only when cheaper lanes cannot validate the behavior claim.
|
||||
6. Return:
|
||||
- verification summary
|
||||
- exact commands run
|
||||
- artifact paths
|
||||
- skipped lanes and blockers
|
||||
|
||||
## Quick start
|
||||
|
||||
Repo-source quick start:
|
||||
|
||||
```bash
|
||||
bash .agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh
|
||||
```
|
||||
|
||||
Installed-skill quick start:
|
||||
|
||||
```bash
|
||||
bash "$SUBMINER_VERIFY_SKILL/scripts/classify_subminer_diff.sh"
|
||||
```
|
||||
|
||||
Classify explicit files:
|
||||
|
||||
```bash
|
||||
bash .agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh \
|
||||
launcher/main.ts \
|
||||
plugin/subminer/lifecycle.lua \
|
||||
src/main/runtime/mpv-client-runtime-service.ts
|
||||
```
|
||||
|
||||
Run automatic lane selection:
|
||||
|
||||
```bash
|
||||
bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh
|
||||
```
|
||||
|
||||
Installed-skill form:
|
||||
|
||||
```bash
|
||||
bash "$SUBMINER_VERIFY_SKILL/scripts/verify_subminer_change.sh"
|
||||
```
|
||||
|
||||
Run targeted lanes:
|
||||
|
||||
```bash
|
||||
bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh \
|
||||
--lane launcher-plugin \
|
||||
--lane runtime-compat
|
||||
```
|
||||
|
||||
Dry-run to inspect planned commands and artifact layout:
|
||||
|
||||
```bash
|
||||
bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh \
|
||||
--dry-run \
|
||||
launcher/main.ts \
|
||||
src/main.ts
|
||||
```
|
||||
|
||||
## Lane guidance
|
||||
|
||||
- `docs`
|
||||
- For `docs-site/`, `docs/`, and doc-only edits.
|
||||
- `config`
|
||||
- For `src/config/` and config-template-sensitive edits.
|
||||
- `core`
|
||||
- For general source changes where `typecheck` + `test:fast` is the best cheap signal.
|
||||
- `launcher-plugin`
|
||||
- For `launcher/`, `plugin/subminer/`, plugin gating scripts, and wrapper/mpv routing work.
|
||||
- `runtime-compat`
|
||||
- For `src/main*`, runtime/composer wiring, mpv/overlay services, window trackers, and dist-sensitive behavior.
|
||||
- `real-runtime`
|
||||
- Only after deliberate escalation.
|
||||
|
||||
## Real Runtime Escalation
|
||||
|
||||
Escalate only when the change claim depends on actual runtime behavior, for example:
|
||||
|
||||
- overlay appears, hides, or tracks a real mpv window
|
||||
- mpv launch flags or pause-until-ready behavior
|
||||
- plugin/socket/auto-start handshake under a real player
|
||||
- macOS/window-tracker/focus-sensitive behavior
|
||||
|
||||
If the environment cannot support authoritative runtime verification, report the blocker explicitly. Do not silently downgrade a runtime-required claim to a pass.
|
||||
|
||||
## Artifact contract
|
||||
|
||||
The verifier writes under `.tmp/skill-verification/<timestamp>/`:
|
||||
|
||||
- `summary.json`
|
||||
- `summary.txt`
|
||||
- `classification.txt`
|
||||
- `env.txt`
|
||||
- `lanes.txt`
|
||||
- `steps.tsv`
|
||||
- `steps/*.stdout.log`
|
||||
- `steps/*.stderr.log`
|
||||
|
||||
On failure, quote the exact failing command and point at the artifact directory.
|
||||
@@ -1,163 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: classify_subminer_diff.sh [path ...]
|
||||
|
||||
Emit suggested verification lanes for explicit paths or current local git changes.
|
||||
|
||||
Output format:
|
||||
lane:<name>
|
||||
flag:<name>
|
||||
reason:<text>
|
||||
EOF
|
||||
}
|
||||
|
||||
has_item() {
|
||||
local needle=$1
|
||||
shift || true
|
||||
local item
|
||||
for item in "$@"; do
|
||||
if [[ "$item" == "$needle" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
add_lane() {
|
||||
local lane=$1
|
||||
if ! has_item "$lane" "${LANES[@]:-}"; then
|
||||
LANES+=("$lane")
|
||||
fi
|
||||
}
|
||||
|
||||
add_flag() {
|
||||
local flag=$1
|
||||
if ! has_item "$flag" "${FLAGS[@]:-}"; then
|
||||
FLAGS+=("$flag")
|
||||
fi
|
||||
}
|
||||
|
||||
add_reason() {
|
||||
REASONS+=("$1")
|
||||
}
|
||||
|
||||
collect_git_paths() {
|
||||
local top_level
|
||||
if ! top_level=$(git rev-parse --show-toplevel 2>/dev/null); then
|
||||
return 0
|
||||
fi
|
||||
|
||||
(
|
||||
cd "$top_level"
|
||||
if git rev-parse --verify HEAD >/dev/null 2>&1; then
|
||||
git diff --name-only --relative HEAD --
|
||||
git diff --name-only --relative --cached --
|
||||
else
|
||||
git diff --name-only --relative --
|
||||
git diff --name-only --relative --cached --
|
||||
fi
|
||||
git ls-files --others --exclude-standard
|
||||
) | awk 'NF' | sort -u
|
||||
}
|
||||
|
||||
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
||||
usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
declare -a PATHS=()
|
||||
declare -a LANES=()
|
||||
declare -a FLAGS=()
|
||||
declare -a REASONS=()
|
||||
|
||||
if [[ $# -gt 0 ]]; then
|
||||
while [[ $# -gt 0 ]]; do
|
||||
PATHS+=("$1")
|
||||
shift
|
||||
done
|
||||
else
|
||||
while IFS= read -r line; do
|
||||
[[ -n "$line" ]] && PATHS+=("$line")
|
||||
done < <(collect_git_paths)
|
||||
fi
|
||||
|
||||
if [[ ${#PATHS[@]} -eq 0 ]]; then
|
||||
add_lane "core"
|
||||
add_reason "no changed paths detected -> default to core"
|
||||
fi
|
||||
|
||||
for path in "${PATHS[@]}"; do
|
||||
specialized=0
|
||||
|
||||
case "$path" in
|
||||
docs-site/*|docs/*|changes/*|README.md)
|
||||
add_lane "docs"
|
||||
add_reason "$path -> docs"
|
||||
specialized=1
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$path" in
|
||||
src/config/*|src/generate-config-example.ts|src/verify-config-example.ts|docs-site/public/config.example.jsonc|config.example.jsonc)
|
||||
add_lane "config"
|
||||
add_reason "$path -> config"
|
||||
specialized=1
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$path" in
|
||||
launcher/*|plugin/subminer/*|plugin/subminer.conf|scripts/test-plugin-*|scripts/get-mpv-window-*|scripts/configure-plugin-binary-path.mjs)
|
||||
add_lane "launcher-plugin"
|
||||
add_reason "$path -> launcher-plugin"
|
||||
add_flag "real-runtime-candidate"
|
||||
add_reason "$path -> real-runtime-candidate"
|
||||
specialized=1
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$path" in
|
||||
src/main.ts|src/main-entry.ts|src/preload.ts|src/main/*|src/core/services/mpv*|src/core/services/overlay*|src/renderer/*|src/window-trackers/*|scripts/prepare-build-assets.mjs)
|
||||
add_lane "runtime-compat"
|
||||
add_reason "$path -> runtime-compat"
|
||||
add_flag "real-runtime-candidate"
|
||||
add_reason "$path -> real-runtime-candidate"
|
||||
specialized=1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ "$specialized" == "0" ]]; then
|
||||
case "$path" in
|
||||
src/*|package.json|tsconfig*.json|scripts/*|Makefile)
|
||||
add_lane "core"
|
||||
add_reason "$path -> core"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
case "$path" in
|
||||
package.json|src/main.ts|src/main-entry.ts|src/preload.ts)
|
||||
add_flag "broad-impact"
|
||||
add_reason "$path -> broad-impact"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ ${#LANES[@]} -eq 0 ]]; then
|
||||
add_lane "core"
|
||||
add_reason "no lane-specific matches -> default to core"
|
||||
fi
|
||||
|
||||
for lane in "${LANES[@]}"; do
|
||||
printf 'lane:%s\n' "$lane"
|
||||
done
|
||||
|
||||
for flag in "${FLAGS[@]}"; do
|
||||
printf 'flag:%s\n' "$flag"
|
||||
done
|
||||
|
||||
for reason in "${REASONS[@]}"; do
|
||||
printf 'reason:%s\n' "$reason"
|
||||
done
|
||||
@@ -1,566 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: verify_subminer_change.sh [options] [path ...]
|
||||
|
||||
Options:
|
||||
--lane <name> Force a verification lane. Repeatable.
|
||||
--artifact-dir <dir> Use an explicit artifact directory.
|
||||
--allow-real-runtime Allow explicit real-runtime execution.
|
||||
--allow-real-gui Deprecated alias for --allow-real-runtime.
|
||||
--dry-run Record planned steps without executing commands.
|
||||
--help Show this help text.
|
||||
|
||||
If no lanes are supplied, the script classifies the provided paths. If no paths are
|
||||
provided, it classifies the current local git changes.
|
||||
|
||||
Authoritative real-runtime verification should be requested with explicit path
|
||||
arguments instead of relying on inferred local git changes.
|
||||
EOF
|
||||
}
|
||||
|
||||
timestamp() {
|
||||
date +%Y%m%d-%H%M%S
|
||||
}
|
||||
|
||||
timestamp_iso() {
|
||||
date -u +%Y-%m-%dT%H:%M:%SZ
|
||||
}
|
||||
|
||||
generate_session_id() {
|
||||
local tmp_dir
|
||||
tmp_dir=$(mktemp -d "${TMPDIR:-/tmp}/subminer-verify-$(timestamp)-XXXXXX")
|
||||
basename "$tmp_dir"
|
||||
rmdir "$tmp_dir"
|
||||
}
|
||||
|
||||
has_item() {
|
||||
local needle=$1
|
||||
shift || true
|
||||
local item
|
||||
for item in "$@"; do
|
||||
if [[ "$item" == "$needle" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
normalize_lane_name() {
|
||||
case "$1" in
|
||||
real-gui)
|
||||
printf '%s' "real-runtime"
|
||||
;;
|
||||
*)
|
||||
printf '%s' "$1"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
add_lane() {
|
||||
local lane
|
||||
lane=$(normalize_lane_name "$1")
|
||||
if ! has_item "$lane" "${SELECTED_LANES[@]:-}"; then
|
||||
SELECTED_LANES+=("$lane")
|
||||
fi
|
||||
}
|
||||
|
||||
add_blocker() {
|
||||
BLOCKERS+=("$1")
|
||||
BLOCKED=1
|
||||
}
|
||||
|
||||
append_step_record() {
|
||||
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
|
||||
"$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" >>"$STEPS_TSV"
|
||||
}
|
||||
|
||||
record_env() {
|
||||
{
|
||||
printf 'repo_root=%s\n' "$REPO_ROOT"
|
||||
printf 'session_id=%s\n' "$SESSION_ID"
|
||||
printf 'artifact_dir=%s\n' "$ARTIFACT_DIR"
|
||||
printf 'path_selection_mode=%s\n' "$PATH_SELECTION_MODE"
|
||||
printf 'dry_run=%s\n' "$DRY_RUN"
|
||||
printf 'allow_real_runtime=%s\n' "$ALLOW_REAL_RUNTIME"
|
||||
printf 'session_home=%s\n' "$SESSION_HOME"
|
||||
printf 'session_xdg_config_home=%s\n' "$SESSION_XDG_CONFIG_HOME"
|
||||
printf 'session_mpv_dir=%s\n' "$SESSION_MPV_DIR"
|
||||
printf 'session_logs_dir=%s\n' "$SESSION_LOGS_DIR"
|
||||
printf 'session_mpv_log=%s\n' "$SESSION_MPV_LOG"
|
||||
printf 'pwd=%s\n' "$(pwd)"
|
||||
git rev-parse --short HEAD 2>/dev/null | sed 's/^/git_head=/' || true
|
||||
git status --short 2>/dev/null || true
|
||||
if [[ ${#PATH_ARGS[@]} -gt 0 ]]; then
|
||||
printf 'requested_paths=\n'
|
||||
printf ' %s\n' "${PATH_ARGS[@]}"
|
||||
fi
|
||||
} >"$ARTIFACT_DIR/env.txt"
|
||||
}
|
||||
|
||||
run_step() {
|
||||
local lane=$1
|
||||
local name=$2
|
||||
local command=$3
|
||||
local note=${4:-}
|
||||
local slug=${name//[^a-zA-Z0-9_-]/-}
|
||||
local stdout_rel="steps/${slug}.stdout.log"
|
||||
local stderr_rel="steps/${slug}.stderr.log"
|
||||
local stdout_path="$ARTIFACT_DIR/$stdout_rel"
|
||||
local stderr_path="$ARTIFACT_DIR/$stderr_rel"
|
||||
local status exit_code
|
||||
|
||||
COMMANDS_RUN+=("$command")
|
||||
printf '%s\n' "$command" >"$ARTIFACT_DIR/steps/${slug}.command.txt"
|
||||
|
||||
if [[ "$DRY_RUN" == "1" ]]; then
|
||||
printf '[dry-run] %s\n' "$command" >"$stdout_path"
|
||||
: >"$stderr_path"
|
||||
status="dry-run"
|
||||
exit_code=0
|
||||
else
|
||||
if bash -lc "cd \"$REPO_ROOT\" && $command" >"$stdout_path" 2>"$stderr_path"; then
|
||||
status="passed"
|
||||
exit_code=0
|
||||
EXECUTED_REAL_STEPS=1
|
||||
else
|
||||
exit_code=$?
|
||||
status="failed"
|
||||
FAILED=1
|
||||
fi
|
||||
fi
|
||||
|
||||
append_step_record "$lane" "$name" "$status" "$exit_code" "$command" "$stdout_rel" "$stderr_rel" "$note"
|
||||
printf '%s\t%s\t%s\n' "$lane" "$name" "$status"
|
||||
|
||||
if [[ "$status" == "failed" ]]; then
|
||||
FAILURE_STEP="$name"
|
||||
FAILURE_COMMAND="$command"
|
||||
FAILURE_STDOUT="$stdout_rel"
|
||||
FAILURE_STDERR="$stderr_rel"
|
||||
return "$exit_code"
|
||||
fi
|
||||
}
|
||||
|
||||
record_nonpassing_step() {
|
||||
local lane=$1
|
||||
local name=$2
|
||||
local status=$3
|
||||
local note=$4
|
||||
local slug=${name//[^a-zA-Z0-9_-]/-}
|
||||
local stdout_rel="steps/${slug}.stdout.log"
|
||||
local stderr_rel="steps/${slug}.stderr.log"
|
||||
printf '%s\n' "$note" >"$ARTIFACT_DIR/$stdout_rel"
|
||||
: >"$ARTIFACT_DIR/$stderr_rel"
|
||||
append_step_record "$lane" "$name" "$status" "0" "" "$stdout_rel" "$stderr_rel" "$note"
|
||||
printf '%s\t%s\t%s\n' "$lane" "$name" "$status"
|
||||
}
|
||||
|
||||
record_skipped_step() {
|
||||
record_nonpassing_step "$1" "$2" "skipped" "$3"
|
||||
}
|
||||
|
||||
record_blocked_step() {
|
||||
add_blocker "$3"
|
||||
record_nonpassing_step "$1" "$2" "blocked" "$3"
|
||||
}
|
||||
|
||||
record_failed_step() {
|
||||
FAILED=1
|
||||
FAILURE_STEP=$2
|
||||
FAILURE_COMMAND=${FAILURE_COMMAND:-"(validation)"}
|
||||
FAILURE_STDOUT="steps/${2//[^a-zA-Z0-9_-]/-}.stdout.log"
|
||||
FAILURE_STDERR="steps/${2//[^a-zA-Z0-9_-]/-}.stderr.log"
|
||||
add_blocker "$3"
|
||||
record_nonpassing_step "$1" "$2" "failed" "$3"
|
||||
}
|
||||
|
||||
find_real_runtime_helper() {
|
||||
local candidate
|
||||
for candidate in \
|
||||
"$SCRIPT_DIR/run_real_runtime_smoke.sh" \
|
||||
"$SCRIPT_DIR/run_real_mpv_smoke.sh"; do
|
||||
if [[ -x "$candidate" ]]; then
|
||||
printf '%s' "$candidate"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
acquire_real_runtime_lease() {
|
||||
local lease_root="$REPO_ROOT/.tmp/skill-verification/locks"
|
||||
local lease_dir="$lease_root/exclusive-real-runtime"
|
||||
mkdir -p "$lease_root"
|
||||
if mkdir "$lease_dir" 2>/dev/null; then
|
||||
REAL_RUNTIME_LEASE_DIR="$lease_dir"
|
||||
printf '%s\n' "$SESSION_ID" >"$lease_dir/session_id"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local owner=""
|
||||
if [[ -f "$lease_dir/session_id" ]]; then
|
||||
owner=$(cat "$lease_dir/session_id")
|
||||
fi
|
||||
add_blocker "real-runtime lease already held${owner:+ by $owner}"
|
||||
return 1
|
||||
}
|
||||
|
||||
release_real_runtime_lease() {
|
||||
if [[ -n "$REAL_RUNTIME_LEASE_DIR" && -d "$REAL_RUNTIME_LEASE_DIR" ]]; then
|
||||
if [[ -f "$REAL_RUNTIME_LEASE_DIR/session_id" ]]; then
|
||||
local owner
|
||||
owner=$(cat "$REAL_RUNTIME_LEASE_DIR/session_id")
|
||||
if [[ "$owner" != "$SESSION_ID" ]]; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
rm -rf "$REAL_RUNTIME_LEASE_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
compute_final_status() {
|
||||
if [[ "$FAILED" == "1" ]]; then
|
||||
FINAL_STATUS="failed"
|
||||
elif [[ "$BLOCKED" == "1" ]]; then
|
||||
FINAL_STATUS="blocked"
|
||||
elif [[ "$EXECUTED_REAL_STEPS" == "1" ]]; then
|
||||
FINAL_STATUS="passed"
|
||||
else
|
||||
FINAL_STATUS="skipped"
|
||||
fi
|
||||
}
|
||||
|
||||
write_summary_files() {
|
||||
local lane_lines
|
||||
lane_lines=$(printf '%s\n' "${SELECTED_LANES[@]}")
|
||||
printf '%s\n' "$lane_lines" >"$ARTIFACT_DIR/lanes.txt"
|
||||
printf '%s\n' "${BLOCKERS[@]}" >"$ARTIFACT_DIR/blockers.txt"
|
||||
printf '%s\n' "${PATH_ARGS[@]}" >"$ARTIFACT_DIR/requested-paths.txt"
|
||||
|
||||
ARTIFACT_DIR_ENV="$ARTIFACT_DIR" \
|
||||
SESSION_ID_ENV="$SESSION_ID" \
|
||||
FINAL_STATUS_ENV="$FINAL_STATUS" \
|
||||
PATH_SELECTION_MODE_ENV="$PATH_SELECTION_MODE" \
|
||||
ALLOW_REAL_RUNTIME_ENV="$ALLOW_REAL_RUNTIME" \
|
||||
SESSION_HOME_ENV="$SESSION_HOME" \
|
||||
SESSION_XDG_CONFIG_HOME_ENV="$SESSION_XDG_CONFIG_HOME" \
|
||||
SESSION_MPV_DIR_ENV="$SESSION_MPV_DIR" \
|
||||
SESSION_LOGS_DIR_ENV="$SESSION_LOGS_DIR" \
|
||||
SESSION_MPV_LOG_ENV="$SESSION_MPV_LOG" \
|
||||
STARTED_AT_ENV="$STARTED_AT" \
|
||||
FINISHED_AT_ENV="$FINISHED_AT" \
|
||||
FAILED_ENV="$FAILED" \
|
||||
FAILURE_COMMAND_ENV="${FAILURE_COMMAND:-}" \
|
||||
FAILURE_STDOUT_ENV="${FAILURE_STDOUT:-}" \
|
||||
FAILURE_STDERR_ENV="${FAILURE_STDERR:-}" \
|
||||
bun -e '
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
function readLines(filePath) {
|
||||
if (!fs.existsSync(filePath)) return [];
|
||||
return fs.readFileSync(filePath, "utf8").split(/\r?\n/).filter(Boolean);
|
||||
}
|
||||
|
||||
const artifactDir = process.env.ARTIFACT_DIR_ENV;
|
||||
const reportsDir = path.join(artifactDir, "reports");
|
||||
const lanes = readLines(path.join(artifactDir, "lanes.txt"));
|
||||
const blockers = readLines(path.join(artifactDir, "blockers.txt"));
|
||||
const requestedPaths = readLines(path.join(artifactDir, "requested-paths.txt"));
|
||||
const steps = readLines(path.join(artifactDir, "steps.tsv")).map((line) => {
|
||||
const [lane, name, status, exitCode, command, stdout, stderr, note] = line.split("\t");
|
||||
return {
|
||||
lane,
|
||||
name,
|
||||
status,
|
||||
exitCode: Number(exitCode || 0),
|
||||
command,
|
||||
stdout,
|
||||
stderr,
|
||||
note,
|
||||
};
|
||||
});
|
||||
const summary = {
|
||||
sessionId: process.env.SESSION_ID_ENV || "",
|
||||
artifactDir,
|
||||
reportsDir,
|
||||
status: process.env.FINAL_STATUS_ENV || "failed",
|
||||
selectedLanes: lanes,
|
||||
failed: process.env.FAILED_ENV === "1",
|
||||
failure:
|
||||
process.env.FAILED_ENV === "1"
|
||||
? {
|
||||
command: process.env.FAILURE_COMMAND_ENV || "",
|
||||
stdout: process.env.FAILURE_STDOUT_ENV || "",
|
||||
stderr: process.env.FAILURE_STDERR_ENV || "",
|
||||
}
|
||||
: null,
|
||||
blockers,
|
||||
pathSelectionMode: process.env.PATH_SELECTION_MODE_ENV || "git-inferred",
|
||||
requestedPaths,
|
||||
allowRealRuntime: process.env.ALLOW_REAL_RUNTIME_ENV === "1",
|
||||
startedAt: process.env.STARTED_AT_ENV || "",
|
||||
finishedAt: process.env.FINISHED_AT_ENV || "",
|
||||
env: {
|
||||
home: process.env.SESSION_HOME_ENV || "",
|
||||
xdgConfigHome: process.env.SESSION_XDG_CONFIG_HOME_ENV || "",
|
||||
mpvDir: process.env.SESSION_MPV_DIR_ENV || "",
|
||||
logsDir: process.env.SESSION_LOGS_DIR_ENV || "",
|
||||
mpvLog: process.env.SESSION_MPV_LOG_ENV || "",
|
||||
},
|
||||
steps,
|
||||
};
|
||||
|
||||
const summaryJson = JSON.stringify(summary, null, 2) + "\n";
|
||||
fs.writeFileSync(path.join(artifactDir, "summary.json"), summaryJson);
|
||||
fs.writeFileSync(path.join(reportsDir, "summary.json"), summaryJson);
|
||||
|
||||
const lines = [];
|
||||
lines.push(`session_id: ${summary.sessionId}`);
|
||||
lines.push(`artifact_dir: ${artifactDir}`);
|
||||
lines.push(`selected_lanes: ${lanes.join(", ") || "(none)"}`);
|
||||
lines.push(`status: ${summary.status}`);
|
||||
lines.push(`path_selection_mode: ${summary.pathSelectionMode}`);
|
||||
if (requestedPaths.length > 0) {
|
||||
lines.push(`requested_paths: ${requestedPaths.join(", ")}`);
|
||||
}
|
||||
if (blockers.length > 0) {
|
||||
lines.push(`blockers: ${blockers.join(" | ")}`);
|
||||
}
|
||||
for (const step of steps) {
|
||||
lines.push(`${step.lane}/${step.name}: ${step.status}`);
|
||||
if (step.command) lines.push(` command: ${step.command}`);
|
||||
lines.push(` stdout: ${step.stdout}`);
|
||||
lines.push(` stderr: ${step.stderr}`);
|
||||
if (step.note) lines.push(` note: ${step.note}`);
|
||||
}
|
||||
if (summary.failed) {
|
||||
lines.push(`failure_command: ${process.env.FAILURE_COMMAND_ENV || ""}`);
|
||||
}
|
||||
const summaryText = lines.join("\n") + "\n";
|
||||
fs.writeFileSync(path.join(artifactDir, "summary.txt"), summaryText);
|
||||
fs.writeFileSync(path.join(reportsDir, "summary.txt"), summaryText);
|
||||
'
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
release_real_runtime_lease
|
||||
}
|
||||
|
||||
CLASSIFIER_OUTPUT=""
|
||||
ARTIFACT_DIR=""
|
||||
ALLOW_REAL_RUNTIME=0
|
||||
DRY_RUN=0
|
||||
FAILED=0
|
||||
BLOCKED=0
|
||||
EXECUTED_REAL_STEPS=0
|
||||
FINAL_STATUS=""
|
||||
FAILURE_STEP=""
|
||||
FAILURE_COMMAND=""
|
||||
FAILURE_STDOUT=""
|
||||
FAILURE_STDERR=""
|
||||
REAL_RUNTIME_LEASE_DIR=""
|
||||
STARTED_AT=""
|
||||
FINISHED_AT=""
|
||||
|
||||
declare -a EXPLICIT_LANES=()
|
||||
declare -a SELECTED_LANES=()
|
||||
declare -a PATH_ARGS=()
|
||||
declare -a COMMANDS_RUN=()
|
||||
declare -a BLOCKERS=()
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--lane)
|
||||
EXPLICIT_LANES+=("$(normalize_lane_name "$2")")
|
||||
shift 2
|
||||
;;
|
||||
--artifact-dir)
|
||||
ARTIFACT_DIR=$2
|
||||
shift 2
|
||||
;;
|
||||
--allow-real-runtime|--allow-real-gui)
|
||||
ALLOW_REAL_RUNTIME=1
|
||||
shift
|
||||
;;
|
||||
--dry-run)
|
||||
DRY_RUN=1
|
||||
shift
|
||||
;;
|
||||
--help|-h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
while [[ $# -gt 0 ]]; do
|
||||
PATH_ARGS+=("$1")
|
||||
shift
|
||||
done
|
||||
;;
|
||||
*)
|
||||
PATH_ARGS+=("$1")
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
||||
SESSION_ID=$(generate_session_id)
|
||||
PATH_SELECTION_MODE="git-inferred"
|
||||
if [[ ${#PATH_ARGS[@]} -gt 0 ]]; then
|
||||
PATH_SELECTION_MODE="explicit"
|
||||
fi
|
||||
|
||||
if [[ -z "$ARTIFACT_DIR" ]]; then
|
||||
mkdir -p "$REPO_ROOT/.tmp/skill-verification"
|
||||
ARTIFACT_DIR="$REPO_ROOT/.tmp/skill-verification/$SESSION_ID"
|
||||
fi
|
||||
|
||||
SESSION_HOME="$ARTIFACT_DIR/home"
|
||||
SESSION_XDG_CONFIG_HOME="$ARTIFACT_DIR/xdg"
|
||||
SESSION_MPV_DIR="$ARTIFACT_DIR/mpv"
|
||||
SESSION_LOGS_DIR="$ARTIFACT_DIR/logs"
|
||||
SESSION_MPV_LOG="$SESSION_LOGS_DIR/mpv.log"
|
||||
|
||||
mkdir -p "$ARTIFACT_DIR/steps" "$ARTIFACT_DIR/reports" "$SESSION_HOME" "$SESSION_XDG_CONFIG_HOME" "$SESSION_MPV_DIR" "$SESSION_LOGS_DIR"
|
||||
STEPS_TSV="$ARTIFACT_DIR/steps.tsv"
|
||||
: >"$STEPS_TSV"
|
||||
|
||||
trap cleanup EXIT
|
||||
STARTED_AT=$(timestamp_iso)
|
||||
|
||||
if [[ ${#EXPLICIT_LANES[@]} -gt 0 ]]; then
|
||||
local_lane=""
|
||||
for local_lane in "${EXPLICIT_LANES[@]}"; do
|
||||
add_lane "$local_lane"
|
||||
done
|
||||
printf 'reason:explicit lanes supplied\n' >"$ARTIFACT_DIR/classification.txt"
|
||||
else
|
||||
if [[ ${#PATH_ARGS[@]} -gt 0 ]]; then
|
||||
CLASSIFIER_OUTPUT=$(bash "$SCRIPT_DIR/classify_subminer_diff.sh" "${PATH_ARGS[@]}")
|
||||
else
|
||||
CLASSIFIER_OUTPUT=$(bash "$SCRIPT_DIR/classify_subminer_diff.sh")
|
||||
fi
|
||||
printf '%s\n' "$CLASSIFIER_OUTPUT" >"$ARTIFACT_DIR/classification.txt"
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
lane:*)
|
||||
add_lane "${line#lane:}"
|
||||
;;
|
||||
esac
|
||||
done <<<"$CLASSIFIER_OUTPUT"
|
||||
fi
|
||||
|
||||
record_env
|
||||
|
||||
printf 'artifact_dir=%s\n' "$ARTIFACT_DIR"
|
||||
printf 'selected_lanes=%s\n' "$(IFS=,; echo "${SELECTED_LANES[*]}")"
|
||||
|
||||
for lane in "${SELECTED_LANES[@]}"; do
|
||||
case "$lane" in
|
||||
docs)
|
||||
run_step "$lane" "docs-test" "bun run docs:test" || break
|
||||
[[ "$FAILED" == "1" ]] && break
|
||||
run_step "$lane" "docs-build" "bun run docs:build" || break
|
||||
;;
|
||||
config)
|
||||
run_step "$lane" "test-config" "bun run test:config" || break
|
||||
;;
|
||||
core)
|
||||
run_step "$lane" "typecheck" "bun run typecheck" || break
|
||||
[[ "$FAILED" == "1" ]] && break
|
||||
run_step "$lane" "test-fast" "bun run test:fast" || break
|
||||
;;
|
||||
launcher-plugin)
|
||||
run_step "$lane" "launcher-smoke-src" "bun run test:launcher:smoke:src" || break
|
||||
[[ "$FAILED" == "1" ]] && break
|
||||
run_step "$lane" "plugin-src" "bun run test:plugin:src" || break
|
||||
;;
|
||||
runtime-compat)
|
||||
run_step "$lane" "build" "bun run build" || break
|
||||
[[ "$FAILED" == "1" ]] && break
|
||||
run_step "$lane" "test-runtime-compat" "bun run test:runtime:compat" || break
|
||||
[[ "$FAILED" == "1" ]] && break
|
||||
run_step "$lane" "test-smoke-dist" "bun run test:smoke:dist" || break
|
||||
;;
|
||||
real-runtime)
|
||||
if [[ "$PATH_SELECTION_MODE" != "explicit" ]]; then
|
||||
record_blocked_step \
|
||||
"$lane" \
|
||||
"real-runtime-guard" \
|
||||
"real-runtime lane requires explicit paths; inferred local git changes are non-authoritative"
|
||||
break
|
||||
fi
|
||||
|
||||
if [[ "$ALLOW_REAL_RUNTIME" != "1" ]]; then
|
||||
record_blocked_step \
|
||||
"$lane" \
|
||||
"real-runtime-guard" \
|
||||
"real-runtime lane requested but --allow-real-runtime was not supplied"
|
||||
break
|
||||
fi
|
||||
|
||||
if ! acquire_real_runtime_lease; then
|
||||
record_blocked_step \
|
||||
"$lane" \
|
||||
"real-runtime-lease" \
|
||||
"real-runtime lease already held; rerun after the active runtime verification finishes"
|
||||
break
|
||||
fi
|
||||
|
||||
if ! REAL_RUNTIME_HELPER=$(find_real_runtime_helper); then
|
||||
record_blocked_step \
|
||||
"$lane" \
|
||||
"real-runtime-helper" \
|
||||
"real-runtime helper not implemented yet"
|
||||
break
|
||||
fi
|
||||
|
||||
printf -v REAL_RUNTIME_COMMAND \
|
||||
'SESSION_ID=%q HOME=%q XDG_CONFIG_HOME=%q SUBMINER_MPV_LOG=%q bash %q' \
|
||||
"$SESSION_ID" \
|
||||
"$SESSION_HOME" \
|
||||
"$SESSION_XDG_CONFIG_HOME" \
|
||||
"$SESSION_MPV_LOG" \
|
||||
"$REAL_RUNTIME_HELPER"
|
||||
|
||||
run_step "$lane" "real-runtime-smoke" "$REAL_RUNTIME_COMMAND" || break
|
||||
;;
|
||||
*)
|
||||
record_failed_step "$lane" "lane-validation" "unknown lane: $lane"
|
||||
break
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ "$FAILED" == "1" || "$BLOCKED" == "1" ]]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
FINISHED_AT=$(timestamp_iso)
|
||||
compute_final_status
|
||||
write_summary_files
|
||||
|
||||
printf 'status=%s\n' "$FINAL_STATUS"
|
||||
printf 'artifact_dir=%s\n' "$ARTIFACT_DIR"
|
||||
|
||||
case "$FINAL_STATUS" in
|
||||
failed)
|
||||
printf 'result=failed\n'
|
||||
printf 'failure_command=%s\n' "$FAILURE_COMMAND"
|
||||
exit 1
|
||||
;;
|
||||
blocked)
|
||||
printf 'result=blocked\n'
|
||||
exit 2
|
||||
;;
|
||||
*)
|
||||
printf 'result=ok\n'
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
@@ -1,146 +0,0 @@
|
||||
---
|
||||
name: "subminer-scrum-master"
|
||||
description: "Use in the SubMiner repo when a request should be turned into planned work and driven through execution. Assesses whether backlog tracking is warranted, creates or updates tasks when needed, records a plan, dispatches one or more subagents, and requires verification before handoff."
|
||||
---
|
||||
|
||||
# SubMiner Scrum Master
|
||||
|
||||
Own workflow, not code by default.
|
||||
|
||||
Use this skill when the user gives a feature request, bug report, issue, refactor, or implementation ask and the agent should manage intake, planning, backlog hygiene, worker dispatch, and verification through completion.
|
||||
|
||||
## Core Rules
|
||||
|
||||
1. Decide first whether backlog tracking is warranted.
|
||||
2. If backlog is needed, search first. Update existing work when it clearly matches.
|
||||
3. If backlog is not needed, keep the process light. Do not invent ticket ceremony.
|
||||
4. Record a plan before dispatching coding work.
|
||||
5. Use parent + subtasks for multi-part work when backlog is used.
|
||||
6. Dispatch conservatively. Parallelize only disjoint write scopes.
|
||||
7. Require verification before handoff, typically via `subminer-change-verification`.
|
||||
8. Report backlog actions, dispatched workers, verification, blockers, and remaining risks.
|
||||
|
||||
## Backlog Decision
|
||||
|
||||
Skip backlog when the request is:
|
||||
- question only
|
||||
- obvious mechanical edit
|
||||
- tiny isolated change with no real planning
|
||||
|
||||
Use backlog when the work:
|
||||
- needs planning or scope decisions
|
||||
- spans multiple phases or subsystems
|
||||
- is likely to need subagent dispatch
|
||||
- should remain traceable for handoff/resume
|
||||
|
||||
If backlog is used:
|
||||
- search existing tasks first
|
||||
- create/update a standalone task for one focused deliverable
|
||||
- create/update a parent task plus subtasks for multi-part work
|
||||
- record the implementation plan in the task before implementation begins
|
||||
|
||||
## Intake Workflow
|
||||
|
||||
1. Parse the request.
|
||||
Classify it as question, mechanical edit, bugfix, feature, refactor, investigation, or follow-up.
|
||||
2. Decide whether backlog is needed.
|
||||
3. If backlog is needed:
|
||||
- search first
|
||||
- update existing task if clearly relevant
|
||||
- otherwise create the right structure
|
||||
- write the implementation plan before dispatch
|
||||
4. If backlog is skipped:
|
||||
- write a short working plan in-thread
|
||||
- proceed without fake ticketing
|
||||
5. Choose execution mode:
|
||||
- no subagents for trivial work
|
||||
- one worker for focused work
|
||||
- parallel workers only for disjoint scopes
|
||||
6. Run verification before handoff.
|
||||
|
||||
## Dispatch Rules
|
||||
|
||||
The scrum master orchestrates. Workers implement.
|
||||
|
||||
- Do not become the default implementer unless delegation is unnecessary.
|
||||
- Do not parallelize overlapping files or tightly coupled runtime work.
|
||||
- Give every worker explicit ownership of files/modules.
|
||||
- Tell every worker other agents may be active and they must not revert unrelated edits.
|
||||
- Require each worker to report:
|
||||
- changed files
|
||||
- tests run
|
||||
- blockers
|
||||
|
||||
Use worker agents for implementation and explorer agents only for bounded codebase questions.
|
||||
|
||||
## Verification
|
||||
|
||||
Every nontrivial code task gets verification.
|
||||
|
||||
Preferred flow:
|
||||
1. use `subminer-change-verification`
|
||||
2. start with the cheapest sufficient lane
|
||||
3. escalate only when needed
|
||||
4. if worker verification is sufficient, accept it or run one final consolidating pass
|
||||
|
||||
Never hand off nontrivial work without stating what was verified and what was skipped.
|
||||
|
||||
## Pre-Handoff Policy Checks (Required)
|
||||
|
||||
Before handoff, always ask and answer both of these questions explicitly:
|
||||
|
||||
1. **Docs update required?**
|
||||
2. **Changelog fragment required?**
|
||||
|
||||
Rules:
|
||||
- Do not assume silence implies "no." Record an explicit yes/no decision for each item.
|
||||
- If the answer is yes, either complete the update or report the blocker before handoff.
|
||||
- Include the final answers in the handoff summary even when both answers are "no."
|
||||
|
||||
## Failure / Scope Handling
|
||||
|
||||
- If a worker hits ambiguity, pause and ask the user.
|
||||
- If verification fails, either:
|
||||
- send the worker back with exact failure context, or
|
||||
- fix it directly if it is tiny and clearly in scope
|
||||
- If new scope appears, revisit backlog structure before silently expanding work.
|
||||
|
||||
## Representative Flows
|
||||
|
||||
### Trivial no-ticket work
|
||||
|
||||
- decide backlog is unnecessary
|
||||
- keep a short plan
|
||||
- implement directly or with one worker if helpful
|
||||
- run targeted verification
|
||||
- report outcome concisely
|
||||
|
||||
### Single-task implementation
|
||||
|
||||
- search/create/update one task
|
||||
- record plan
|
||||
- dispatch one worker
|
||||
- integrate
|
||||
- verify
|
||||
- update task and report outcome
|
||||
|
||||
### Parent + subtasks execution
|
||||
|
||||
- search/create/update parent task
|
||||
- create subtasks for distinct deliverables/phases
|
||||
- record sequencing in the plan
|
||||
- dispatch workers only where scopes are disjoint
|
||||
- integrate
|
||||
- run consolidated verification
|
||||
- update task state and report outcome
|
||||
|
||||
## Output Expectations
|
||||
|
||||
At the end, report:
|
||||
- whether backlog was used and what changed
|
||||
- which workers were dispatched and what they owned
|
||||
- what verification ran
|
||||
- explicit answers to:
|
||||
- docs update required?
|
||||
- changelog fragment required?
|
||||
- blockers, skips, and risks
|
||||
@@ -1,3 +0,0 @@
|
||||
## Checklist
|
||||
|
||||
- [ ] Added a changelog fragment in `changes/`, or this PR is labeled `skip-changelog`
|
||||
@@ -13,7 +13,6 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
|
||||
- name: Setup Bun
|
||||
@@ -27,36 +26,16 @@ jobs:
|
||||
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') }}
|
||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-bun-
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
bun install --frozen-lockfile
|
||||
cd stats && bun install --frozen-lockfile
|
||||
|
||||
- name: Lint changelog fragments
|
||||
run: bun run changelog:lint
|
||||
|
||||
- name: Lint stats (formatting)
|
||||
run: bun run lint:stats
|
||||
|
||||
- name: Enforce pull request changelog fragments (`skip-changelog` label bypass)
|
||||
if: github.event_name == 'pull_request'
|
||||
run: bun run changelog:pr-check --base-ref "origin/${{ github.base_ref }}" --head-ref "HEAD" --labels "${{ join(github.event.pull_request.labels.*.name, ',') }}"
|
||||
run: bun install --frozen-lockfile
|
||||
|
||||
- name: Build (TypeScript check)
|
||||
# Keep explicit typecheck for fast fail before full build/bundle.
|
||||
run: bun run typecheck
|
||||
|
||||
- name: Verify generated config examples
|
||||
run: bun run verify:config-example
|
||||
|
||||
- name: Internal docs knowledge-base checks
|
||||
run: bun run test:docs:kb
|
||||
run: bun run tsc --noEmit
|
||||
|
||||
- name: Test suite (source)
|
||||
run: bun run test:fast
|
||||
@@ -75,12 +54,12 @@ jobs:
|
||||
- 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
|
||||
|
||||
- name: Build docs
|
||||
run: bun run docs:build
|
||||
|
||||
- name: Security audit
|
||||
run: bun audit --audit-level high
|
||||
continue-on-error: true
|
||||
|
||||
@@ -9,6 +9,9 @@ concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
quality-gate:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -29,22 +32,12 @@ jobs:
|
||||
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') }}
|
||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
|
||||
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
|
||||
run: bun install --frozen-lockfile
|
||||
|
||||
- name: Test suite (source)
|
||||
run: bun run test:fast
|
||||
@@ -63,9 +56,6 @@ jobs:
|
||||
- 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
|
||||
|
||||
@@ -89,17 +79,13 @@ jobs:
|
||||
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') }}
|
||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'vendor/texthooker-ui/package.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-bun-
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
bun install --frozen-lockfile
|
||||
cd stats && bun install --frozen-lockfile
|
||||
run: bun install --frozen-lockfile
|
||||
|
||||
- name: Build texthooker-ui
|
||||
run: |
|
||||
@@ -109,17 +95,8 @@ jobs:
|
||||
|
||||
- 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
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Upload AppImage artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -147,10 +124,8 @@ jobs:
|
||||
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') }}
|
||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'vendor/texthooker-ui/package.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-bun-
|
||||
|
||||
@@ -175,9 +150,7 @@ jobs:
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
bun install --frozen-lockfile
|
||||
cd stats && bun install --frozen-lockfile
|
||||
run: bun install --frozen-lockfile
|
||||
|
||||
- name: Build texthooker-ui
|
||||
run: |
|
||||
@@ -188,6 +161,7 @@ jobs:
|
||||
- name: Build signed + notarized macOS artifacts
|
||||
run: bun run build:mac
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
@@ -202,62 +176,9 @@ jobs:
|
||||
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]
|
||||
needs: [build-linux, build-macos]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -276,30 +197,11 @@ jobs:
|
||||
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
|
||||
|
||||
@@ -309,21 +211,19 @@ jobs:
|
||||
- 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" \
|
||||
VERSION="${GITHUB_REF#refs/tags/}"
|
||||
tar -czf "release/subminer-assets-${VERSION}.tar.gz" \
|
||||
config.example.jsonc \
|
||||
plugin/subminer \
|
||||
plugin/subminer.lua \
|
||||
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)
|
||||
files=(release/*.AppImage release/*.dmg release/*.zip release/*.tar.gz dist/launcher/subminer)
|
||||
if [ "${#files[@]}" -eq 0 ]; then
|
||||
echo "No release artifacts found for checksum generation."
|
||||
exit 1
|
||||
@@ -332,138 +232,65 @@ jobs:
|
||||
|
||||
- name: Get version from tag
|
||||
id: version
|
||||
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
|
||||
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build changelog artifacts for release
|
||||
- name: Generate changelog
|
||||
id: changelog
|
||||
run: |
|
||||
if find changes -maxdepth 1 -name '*.md' -not -name README.md -print -quit | grep -q .; then
|
||||
bun run changelog:build --version "${{ steps.version.outputs.VERSION }}"
|
||||
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
|
||||
if [ -n "$PREV_TAG" ]; then
|
||||
CHANGES=$(git log --pretty=format:"- %s" ${PREV_TAG}..HEAD)
|
||||
else
|
||||
echo "No pending changelog fragments found."
|
||||
COMMIT_COUNT=$(git rev-list --count HEAD)
|
||||
if [ "$COMMIT_COUNT" -gt 10 ]; then
|
||||
CHANGES=$(git log --pretty=format:"- %s" HEAD~10..HEAD)
|
||||
else
|
||||
CHANGES=$(git log --pretty=format:"- %s")
|
||||
fi
|
||||
fi
|
||||
echo "CHANGES<<EOF" >> $GITHUB_OUTPUT
|
||||
echo "$CHANGES" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Verify changelog is ready for tagged release
|
||||
run: bun run changelog:check --version "${{ steps.version.outputs.VERSION }}"
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
name: ${{ steps.version.outputs.VERSION }}
|
||||
body: |
|
||||
## Changes
|
||||
${{ steps.changelog.outputs.CHANGES }}
|
||||
|
||||
- name: Generate release notes from changelog
|
||||
run: bun run changelog:release-notes --version "${{ steps.version.outputs.VERSION }}"
|
||||
## Installation
|
||||
|
||||
- name: Publish Release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
### AppImage (Recommended)
|
||||
1. Download the AppImage below
|
||||
2. Make it executable: `chmod +x SubMiner-*.AppImage`
|
||||
3. Run: `./SubMiner-*.AppImage`
|
||||
|
||||
if gh release view "${{ steps.version.outputs.VERSION }}" >/dev/null 2>&1; then
|
||||
# Do not pass the prerelease flag here; gh defaults to a normal release.
|
||||
gh release edit "${{ steps.version.outputs.VERSION }}" \
|
||||
--draft=false \
|
||||
--title "${{ steps.version.outputs.VERSION }}" \
|
||||
--notes-file release/release-notes.md
|
||||
else
|
||||
gh release create "${{ steps.version.outputs.VERSION }}" \
|
||||
--title "${{ steps.version.outputs.VERSION }}" \
|
||||
--notes-file release/release-notes.md
|
||||
fi
|
||||
### macOS
|
||||
1. Download `subminer-*.dmg`
|
||||
2. Open the DMG and drag `SubMiner.app` into `/Applications`
|
||||
3. If needed, use the ZIP artifact as an alternative
|
||||
|
||||
shopt -s nullglob
|
||||
artifacts=(
|
||||
### Manual Installation
|
||||
See the [README](https://github.com/${{ github.repository }}#installation) for manual installation instructions.
|
||||
|
||||
### Optional Assets (config example + mpv plugin + rofi theme)
|
||||
1. Download `subminer-assets-*.tar.gz`
|
||||
2. Extract and copy `config.example.jsonc` to `~/.config/SubMiner/config.jsonc`
|
||||
3. Copy `plugin/subminer.lua` to `~/.config/mpv/scripts/`
|
||||
4. Copy `plugin/subminer.conf` to `~/.config/mpv/script-opts/`
|
||||
5. Copy `assets/themes/subminer.rasi` to:
|
||||
- Linux: `~/.local/share/SubMiner/themes/subminer.rasi`
|
||||
- macOS: `~/Library/Application Support/SubMiner/themes/subminer.rasi`
|
||||
|
||||
Note: the `subminer` wrapper script uses Bun (`#!/usr/bin/env bun`), so `bun` must be installed and on `PATH`.
|
||||
files: |
|
||||
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
|
||||
|
||||
aur-publish:
|
||||
needs: [release]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Get version from tag
|
||||
id: version
|
||||
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Validate AUR SSH secret
|
||||
env:
|
||||
AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -z "${AUR_SSH_PRIVATE_KEY}" ]; then
|
||||
echo "Missing required secret: AUR_SSH_PRIVATE_KEY"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Configure SSH for AUR
|
||||
env:
|
||||
AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
install -dm700 ~/.ssh
|
||||
printf '%s\n' "${AUR_SSH_PRIVATE_KEY}" > ~/.ssh/aur
|
||||
chmod 600 ~/.ssh/aur
|
||||
ssh-keyscan aur.archlinux.org >> ~/.ssh/known_hosts
|
||||
chmod 644 ~/.ssh/known_hosts
|
||||
|
||||
- name: Clone AUR repo
|
||||
env:
|
||||
GIT_SSH_COMMAND: ssh -i ~/.ssh/aur -o IdentitiesOnly=yes
|
||||
run: git clone ssh://aur@aur.archlinux.org/subminer-bin.git aur-subminer-bin
|
||||
|
||||
- name: Download release assets for AUR
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
version="${{ steps.version.outputs.VERSION }}"
|
||||
install -dm755 .tmp/aur-release-assets
|
||||
gh release download "$version" \
|
||||
--dir .tmp/aur-release-assets \
|
||||
--pattern "SubMiner-${version#v}.AppImage" \
|
||||
--pattern "subminer" \
|
||||
--pattern "subminer-assets.tar.gz"
|
||||
|
||||
- name: Update AUR packaging metadata
|
||||
run: |
|
||||
set -euo pipefail
|
||||
version_no_v="${{ steps.version.outputs.VERSION }}"
|
||||
version_no_v="${version_no_v#v}"
|
||||
cp packaging/aur/subminer-bin/PKGBUILD aur-subminer-bin/PKGBUILD
|
||||
cp packaging/aur/subminer-bin/.SRCINFO aur-subminer-bin/.SRCINFO
|
||||
bash scripts/update-aur-package.sh \
|
||||
--pkg-dir aur-subminer-bin \
|
||||
--version "${{ steps.version.outputs.VERSION }}" \
|
||||
--appimage ".tmp/aur-release-assets/SubMiner-${version_no_v}.AppImage" \
|
||||
--wrapper ".tmp/aur-release-assets/subminer" \
|
||||
--assets ".tmp/aur-release-assets/subminer-assets.tar.gz"
|
||||
|
||||
- name: Commit and push AUR update
|
||||
working-directory: aur-subminer-bin
|
||||
env:
|
||||
GIT_SSH_COMMAND: ssh -i ~/.ssh/aur -o IdentitiesOnly=yes
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if git diff --quiet -- PKGBUILD .SRCINFO; then
|
||||
echo "AUR packaging already up to date."
|
||||
exit 0
|
||||
fi
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add PKGBUILD .SRCINFO
|
||||
git commit -m "Update to ${{ steps.version.outputs.VERSION }}"
|
||||
git push origin HEAD:master
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
|
||||
# Superpowers brainstorming
|
||||
.superpowers/
|
||||
|
||||
# Electron build output
|
||||
out/
|
||||
dist/
|
||||
release/
|
||||
build/yomitan/
|
||||
|
||||
# Launcher build artifact (produced by make build-launcher)
|
||||
/subminer
|
||||
subminer
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
@@ -25,7 +21,9 @@ Thumbs.db
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
**/CLAUDE.md
|
||||
environment.toml
|
||||
**/CLAUDE.md
|
||||
.env
|
||||
.vscode/*
|
||||
|
||||
@@ -36,21 +34,5 @@ docs/.vitepress/cache/
|
||||
docs/.vitepress/dist/
|
||||
tests/*
|
||||
.worktrees/
|
||||
.tmp/
|
||||
.codex/*
|
||||
.agents/*
|
||||
!.agents/skills/
|
||||
.agents/skills/*
|
||||
!.agents/skills/subminer-change-verification/
|
||||
!.agents/skills/subminer-scrum-master/
|
||||
.agents/skills/subminer-change-verification/*
|
||||
!.agents/skills/subminer-change-verification/SKILL.md
|
||||
!.agents/skills/subminer-change-verification/scripts/
|
||||
.agents/skills/subminer-change-verification/scripts/*
|
||||
!.agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh
|
||||
!.agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh
|
||||
.agents/skills/subminer-scrum-master/*
|
||||
!.agents/skills/subminer-scrum-master/SKILL.md
|
||||
favicon.png
|
||||
.claude/*
|
||||
!stats/public/favicon.png
|
||||
|
||||
@@ -5,6 +5,3 @@
|
||||
[submodule "vendor/yomitan-jlpt-vocab"]
|
||||
path = vendor/yomitan-jlpt-vocab
|
||||
url = https://github.com/stephenmk/yomitan-jlpt-vocab
|
||||
[submodule "vendor/subminer-yomitan"]
|
||||
path = vendor/subminer-yomitan
|
||||
url = https://github.com/ksyasuda/subminer-yomitan
|
||||
|
||||
@@ -1,69 +1,3 @@
|
||||
# AGENTS.MD
|
||||
|
||||
## Internal Docs
|
||||
|
||||
Start here, then leave this file.
|
||||
|
||||
- Internal system of record: [`docs/README.md`](./docs/README.md)
|
||||
- Architecture map: [`docs/architecture/README.md`](./docs/architecture/README.md)
|
||||
- Workflow map: [`docs/workflow/README.md`](./docs/workflow/README.md)
|
||||
- Verification lanes: [`docs/workflow/verification.md`](./docs/workflow/verification.md)
|
||||
- Knowledge-base rules: [`docs/knowledge-base/README.md`](./docs/knowledge-base/README.md)
|
||||
- Release guide: [`docs/RELEASING.md`](./docs/RELEASING.md)
|
||||
|
||||
`docs-site/` is user-facing. Do not treat it as the canonical internal source of truth.
|
||||
|
||||
## Quick Start
|
||||
|
||||
- Init workspace: `git submodule update --init --recursive`
|
||||
- Install deps: `make deps` or `bun install` plus `(cd vendor/texthooker-ui && bun install --frozen-lockfile)`
|
||||
- Fast dev loop: `make dev-watch`
|
||||
- Full local run: `bun run dev`
|
||||
- Verbose Electron debug: `electron . --start --dev --log-level debug`
|
||||
|
||||
## Build / Test
|
||||
|
||||
- Runtime/package manager: Bun (`packageManager: bun@1.3.5`)
|
||||
- Default handoff gate:
|
||||
`bun run typecheck`
|
||||
`bun run test:fast`
|
||||
`bun run test:env`
|
||||
`bun run build`
|
||||
`bun run test:smoke:dist`
|
||||
- If `docs-site/` changed, also run:
|
||||
`bun run docs:test`
|
||||
`bun run docs:build`
|
||||
- Prefer `make pretty` and `bun run format:check:src`
|
||||
|
||||
## Change-Specific Checks
|
||||
|
||||
- Config/schema/defaults: `bun run test:config`; if template/defaults changed, `bun run generate:config-example`
|
||||
- Launcher/plugin: `bun run test:launcher` or `bun run test:env`
|
||||
- Runtime-compat / dist-sensitive: `bun run test:runtime:compat`
|
||||
- Docs-only: `bun run docs:test`, then `bun run docs:build`
|
||||
|
||||
## Sensitive Files
|
||||
|
||||
- Launcher source of truth: `launcher/*.ts`
|
||||
- Generated launcher artifact: `dist/launcher/subminer`; never hand-edit it
|
||||
- Repo-root `./subminer` is stale; do not revive it
|
||||
- `bun run build` rebuilds bundled Yomitan from `vendor/subminer-yomitan`
|
||||
- Do not change signing/packaging identifiers unless the task explicitly requires it
|
||||
|
||||
## Release / PR Notes
|
||||
|
||||
- User-visible PRs need one fragment in `changes/*.md`
|
||||
- CI enforces `bun run changelog:lint` and `bun run changelog:pr-check`
|
||||
- PR review helpers:
|
||||
- `gh pr view --json number,title,url --jq '"PR #\\(.number): \\(.title)\\n\\(.url)"'`
|
||||
- `gh api repos/:owner/:repo/pulls/<num>/comments --paginate`
|
||||
|
||||
## Runtime Notes
|
||||
|
||||
- Use Codex background for long jobs; tmux only when persistence/interaction is required
|
||||
- CI red: `gh run list/view`, rerun, fix, repeat until green
|
||||
- TypeScript: keep files small; follow existing patterns
|
||||
- Swift: use workspace helper/daemon; validate `swift build` + tests
|
||||
|
||||
<!-- BACKLOG.MD MCP GUIDELINES START -->
|
||||
|
||||
@@ -83,7 +17,6 @@ This project uses Backlog.md MCP for all task and project management activities.
|
||||
- **When to read it**: BEFORE creating tasks, or when you're unsure whether to track work
|
||||
|
||||
These guides cover:
|
||||
|
||||
- Decision framework for when to create tasks
|
||||
- Search-first workflow to avoid duplicates
|
||||
- Links to detailed guides for task creation, execution, and finalization
|
||||
|
||||
@@ -1,220 +0,0 @@
|
||||
# Changelog
|
||||
|
||||
## v0.7.0 (2026-03-19)
|
||||
|
||||
### Added
|
||||
- Immersion: Added Mine Word, Mine Sentence, and Mine Audio buttons to word detail example lines in the stats dashboard.
|
||||
- Immersion: Mine Word creates a full Yomitan card (definition, reading, pitch accent) via the hidden search page bridge, then enriches with sentence audio, screenshot, and metadata extracted from the source video.
|
||||
- Immersion: Mine Sentence and Mine Audio create cards directly with appropriate Lapis/Kiku flags, sentence highlighting, and media from the source file.
|
||||
- Immersion: Media generation (audio + image/AVIF) runs in parallel and respects all AnkiConnect config options.
|
||||
- Immersion: Added word exclusion list to the Vocabulary tab with localStorage persistence and a management modal.
|
||||
- Immersion: Fixed truncated readings in the frequency rank table (e.g. お前 now shows おまえ instead of まえ).
|
||||
- Immersion: Clicking a bar in the Top Repeated Words chart now opens the word detail panel.
|
||||
- Immersion: Secondary subtitle text is now stored alongside primary subtitle lines for use as translation when mining cards from the stats page.
|
||||
- Stats: Added `subminer stats -b` to start or reuse a dedicated background stats server without blocking normal SubMiner instances.
|
||||
- Stats: Added `subminer stats -s` to stop the dedicated background stats server without closing browser tabs.
|
||||
- Stats: Stats server startup now reuses a running background stats daemon instead of trying to bind a second local server in another SubMiner instance.
|
||||
- Launcher: Added launcher passthrough for `-a/--args` so mpv receives raw extra launch flags (`--fs`, `--ytdl-format`, custom audio/video settings, etc.) from the `subminer` command.
|
||||
- Launcher: Added `subminer stats` to launch the local stats dashboard, force-start the stats server on demand, and open the dashboard in your browser.
|
||||
- Launcher: Added `subminer stats cleanup` to backfill vocabulary metadata and prune stale or excluded immersion rows on demand.
|
||||
- Launcher: Added `stats.autoOpenBrowser` so browser launch after `subminer stats` can be enabled or disabled explicitly.
|
||||
- Immersion: Added a local stats dashboard for immersion tracking with Overview, Anime, Trends, Vocabulary, and Sessions views.
|
||||
- Immersion: Added anime progress, episode completion, Anki card links, and occurrence drill-down across the stats dashboard.
|
||||
- Immersion: Added richer session timelines with new-word activity, cumulative totals, and pause/seek/card event markers.
|
||||
- Immersion: Added completed-episodes and completed-anime totals to the Overview tracking snapshot.
|
||||
|
||||
### Changed
|
||||
- Anki: Changed known-word cache settings to live under `ankiConnect.knownWords` instead of mixing them into `ankiConnect.nPlusOne`.
|
||||
- Anki: Kept legacy `ankiConnect.nPlusOne` known-word keys and older `ankiConnect.behavior.nPlusOne*` keys as deprecated compatibility fallbacks.
|
||||
- Stats: Added session deletion to the Sessions tab with the same confirmation prompt used by anime episode/session deletes, and removed all associated session rows from the stats database.
|
||||
- Immersion: Kept immersion tracking history by default while preserving daily/monthly rollup maintenance.
|
||||
- Immersion: Added exact lifetime summary reads for overview/anime/media stats so dashboard totals no longer depend on rescanning raw telemetry.
|
||||
- Immersion: Reduced tracker storage overhead by removing duplicated subtitle text from subtitle-line event payloads.
|
||||
- Immersion: Deduplicated episode cover-art blobs through a shared blob store and updated cover-art reads/writes to resolve shared images correctly.
|
||||
- Immersion: Added indexes for large-history session, telemetry, vocabulary, kanji, and cover-art queries to keep dashboard reads fast as the SQLite database grows.
|
||||
- Immersion: Renamed the stats dashboard's Anime tab to Library so the media browser label matches non-anime sources like YouTube and other yt-dlp-backed content.
|
||||
- Anilist: Standardized episode completion threshold by introducing `DEFAULT_MIN_WATCH_RATIO` and using it for both local watched state transitions and AniList post-watch progress updates.
|
||||
- Anilist: Episode auto-marking now uses the same threshold as AniList (`85%`), removing divergent completion behavior.
|
||||
- Overlay: Excluded interjections and sound-effect tokens from subtitle annotation styling so they no longer inherit misleading lexical highlight treatment while still remaining visible and hoverable as plain subtitle tokens.
|
||||
- Overlay: Expanded subtitle annotation noise filtering to also strip annotation metadata from standalone grammar-only helper tokens such as particles, auxiliaries, adnominals, common explanatory endings like `んです` / `のだ`, and merged trailing quote-particle forms like `...って` while keeping them tokenized for hover lookup.
|
||||
|
||||
### Fixed
|
||||
- Launcher: Fixed mpv Lua plugin binary auto-detection on Linux to also search `/usr/bin/subminer` and `/usr/local/bin/subminer` (lowercase), matching the conventional Unix wrapper name used by packaged installs such as the AUR package.
|
||||
- Stats: Fixed the in-app stats overlay so it connects to the configured `stats.serverPort` instead of falling back to the default port.
|
||||
- Overlay: Fixed subtitle frequency tagging for merged lookup-backed tokens like `陰に` by falling back to exact surface-form Yomitan frequencies when the normalized headword lookup misses.
|
||||
- Overlay: Fixed MeCab merged-token position mapping across line breaks so merged content-plus-particle tokens like `陰に` keep their matched Yomitan frequency instead of inheriting shifted POS tags.
|
||||
- Overlay: Fixed grouped frequency parsing in both Yomitan and fallback frequency-dictionary lookups so display values like `118,121` use the leading rank instead of collapsing the rank and occurrence count into `118121`.
|
||||
- Overlay: Fixed frequency-rank ingestion to ignore Yomitan dictionaries explicitly marked `occurrence-based`, so raw occurrence counts are no longer treated as subtitle rank values.
|
||||
- Overlay: Fixed inflected headword frequency tagging to prefer ranks from the selected Yomitan `termsFind` popup entry itself, ordered by configured dictionary priority, so forms like `潜み` use primary-dictionary ranks like `4073` before falling back to lower-priority raw lemma metadata such as `CC100`.
|
||||
- Overlay: Fixed annotation-stage frequency filtering so exact kanji noun tokens like `者` keep their matched rank even when MeCab labels them `名詞/非自立`, instead of dropping the highlight after scan-time frequency lookup succeeds.
|
||||
- Anki: Fixed repeated character-dictionary startup work by scheduling auto-sync only from mpv media-path changes instead of also re-triggering it from connection and media-title events for the same title.
|
||||
- Overlay: Fixed macOS fullscreen overlay stability by keeping the passive visible overlay from stealing focus, re-raising the overlay window when reasserting its macOS topmost level, and tolerating one transient macOS tracker/helper miss before hiding the overlay.
|
||||
- Overlay: Kept subtitle tokenization warmup one-shot for the lifetime of the app so later fullscreen/media churn on macOS does not replay the startup warmup gate after the first file is ready.
|
||||
- Overlay: Added a bounded macOS tracker loss-grace window so fullscreen enter/leave transitions do not immediately hide and reload the overlay when the helper briefly loses the mpv window.
|
||||
- Overlay: Skipped subtitle/tokenization refresh invalidation on character-dictionary auto-sync completion when the dictionary was already current, preventing startup flash/reload loops on unchanged media.
|
||||
- Stats: Fixed session stats so known-word counts track real known-word occurrences without collapsing subtitle-line gaps.
|
||||
- Stats: Fixed session word totals in session-facing stats views to prefer token counts when available, preventing known words from exceeding total words in the session chart.
|
||||
- Stats: Fixed the stats Vocabulary tab blank-screen regression caused by a hook-order crash after vocabulary data finished loading.
|
||||
- Anki: Fixed card-mine OSD feedback so the final mine result stops the Anki spinner first, then shows a single-line `✓`/`x` status without being overwritten by a later spinner tick.
|
||||
- Stats: Removed the misleading `New words` series from expanded session charts; session detail now shows only the real total-word and known-word lines.
|
||||
- Stats: Restored the cross-anime word table behavior in stats vocabulary surfaces so shared vocabulary entries no longer disappear or merge incorrectly across related media.
|
||||
- Stats: `subminer stats -b` now runs as a standalone background stats daemon instead of reusing the main SubMiner app process, so the overlay app can still be launched separately for normal video watching.
|
||||
- Stats: Dashboard word mining still works against the background daemon by using a short-lived hidden helper for the Yomitan add-note flow.
|
||||
- Stats: Load full session timelines by default in stats session detail views so long sessions preserve complete telemetry history instead of being truncated by a fixed sample limit.
|
||||
- Stats: Replaced heuristic stats word counts with Yomitan token counts, so session, media, anime, and trend subtitle totals now come directly from parsed subtitle tokens.
|
||||
- Stats: Updated stats UI labels and lookup-rate copy to refer to tokens instead of words where those counts are shown.
|
||||
- Overlay: Reduced repeated `Overlay loading...` popups on macOS when fullscreen tracker flaps briefly hide and recover the visible overlay.
|
||||
- Stats: Scaled expanded session-detail known-word charts to the session's actual percentage range so small changes no longer render as a nearly flat line.
|
||||
- Jlpt: Reduced JLPT dictionary startup log noise by summarizing duplicate surface-form collisions instead of logging one line per duplicate entry.
|
||||
|
||||
## v0.6.5 (2026-03-15)
|
||||
|
||||
### Internal
|
||||
- Release: Seed the AUR checkout with the repo `.SRCINFO` template before rewriting metadata so tagged releases do not depend on prior AUR state.
|
||||
|
||||
## v0.6.4 (2026-03-15)
|
||||
|
||||
### Internal
|
||||
- Release: Reworked AUR metadata generation to update `.SRCINFO` directly instead of depending on runner `makepkg`, fixing tagged release publishing for `subminer-bin`.
|
||||
|
||||
## v0.6.3 (2026-03-15)
|
||||
|
||||
### Changed
|
||||
- Overlay: Expanded the `Alt+C` controller modal into an inline config/remap flow with preferred-controller saving and per-action learn mode for buttons, triggers, and stick directions.
|
||||
|
||||
### Internal
|
||||
- Workflow: Hardened the `subminer-scrum-master` skill to explicitly answer whether docs updates and changelog fragments are required before handoff.
|
||||
- Release: Automate `subminer-bin` AUR package updates from the tagged release workflow.
|
||||
|
||||
## v0.6.2 (2026-03-12)
|
||||
|
||||
### Changed
|
||||
- Config: Added `yomitan.externalProfilePath` to reuse another Electron app's Yomitan profile in read-only mode.
|
||||
- Config: SubMiner now reuses external Yomitan dictionaries/settings without writing back to that profile.
|
||||
- Config: Launcher-managed playback now respects `yomitan.externalProfilePath` and no longer forces first-run setup when external Yomitan is configured.
|
||||
- Config: SubMiner now seeds `config.jsonc` even when the default config directory already exists.
|
||||
- Config: First-run setup now allows zero internal dictionaries when `yomitan.externalProfilePath` is configured, and falls back to requiring at least one internal dictionary if that external profile is later removed.
|
||||
|
||||
## v0.6.1 (2026-03-12)
|
||||
|
||||
### Added
|
||||
- Overlay: Added Chrome Gamepad API controller support for keyboard-only overlay mode, including configurable logical bindings for lookup, mining, popup navigation, Yomitan audio, mpv pause, d-pad fallback navigation, and slower smooth popup scrolling.
|
||||
- Overlay: Added `Alt+C` controller selection and `Alt+Shift+C` controller debug modals, with preferred controller persistence and live raw input inspection.
|
||||
- Overlay: Added a transient in-overlay controller-detected indicator when a controller is first found.
|
||||
- Overlay: Fixed stale keyboard-only token highlight cleanup when keyboard-only mode turns off or the Yomitan popup closes.
|
||||
|
||||
### Docs
|
||||
- Install: Added Arch Linux AUR install docs for `subminer-bin` in the README and installation guide.
|
||||
|
||||
### Internal
|
||||
- Config: add an enforced `verify:config-example` gate so checked-in example config artifacts cannot drift silently
|
||||
- Release: Fixed the release workflow token permissions so tagged builds can download `oven-sh/setup-bun` and publish artifacts again.
|
||||
|
||||
## v0.5.6 (2026-03-10)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Dictionary: Persist merged character-dictionary MRU state as soon as a new retained set is built so revisits do not get dropped if later Yomitan import work fails, and skip merged dictionary rebuilds for reorder-only revisits when the retained anime set itself has not changed.
|
||||
- Startup: Fixed early Electron startup writing config and user data under a lowercase `~/.config/subminer` path instead of the canonical `~/.config/SubMiner` directory.
|
||||
- Overlay: Kept JLPT underline colors stable during Yomitan hover and selection states, even when tokens also use known, N+1, name-match, or frequency styling.
|
||||
|
||||
## v0.5.5 (2026-03-09)
|
||||
|
||||
### Changed
|
||||
|
||||
- Overlay: Added `f` as the default overlay fullscreen toggle and changed the default AniSkip intro-jump key to `Tab`.
|
||||
- Dictionary: Aligned AniList character dictionary generation more closely with the upstream reference by preserving duplicate shared names across characters, skipping characters without native Japanese names, restoring richer character info fields, and using upstream-style role mapping plus hint-aware kanji readings.
|
||||
- Startup: Ordered startup OSD messages so tokenization loads first, annotation loading appears next if still pending, and character dictionary sync progress waits until annotation loading finishes.
|
||||
- Dictionary: Added a visible startup OSD step for merged character-dictionary building so long rebuilds show progress before the later import/upload phase.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Dictionary: Fixed AniList media guessing for character dictionary auto-sync by using filename-only `guessit` input and preserving multi-part guessit titles instead of truncating them to the first segment.
|
||||
- Dictionary: Refresh the current subtitle after character dictionary auto-sync completes so newly imported character names highlight on the active line instead of waiting for the next subtitle change.
|
||||
- Dictionary: Show character dictionary auto-sync progress on the mpv OSD without sending desktop notifications.
|
||||
- Dictionary: Keep character dictionary auto-sync non-blocking during startup by letting snapshot/build work run in parallel and delaying only the Yomitan import/settings phase until current-media tokenization is already ready.
|
||||
- Overlay: Fixed visible overlay keyboard handling so pressing `Tab` still reaches mpv and triggers the default AniSkip skip-intro binding while the overlay has focus.
|
||||
- Plugin: Fix Windows mpv plugin binary override lookup so `SUBMINER_BINARY_PATH` still resolves to `SubMiner.exe` when no AppImage override is set.
|
||||
|
||||
## v0.5.3 (2026-03-09)
|
||||
|
||||
### Changed
|
||||
|
||||
- Release: Publish unsigned Windows `.exe` and `.zip` artifacts directly from release CI instead of routing them through SignPath.
|
||||
- Release: Added `bun run build:win:unsigned` for explicit local unsigned Windows packaging.
|
||||
|
||||
## v0.5.2 (2026-03-09)
|
||||
|
||||
### Internal
|
||||
|
||||
- Release: Pinned the Windows SignPath submission workflow to an explicit artifact-configuration slug instead of relying on the SignPath project's default configuration.
|
||||
|
||||
## v0.5.1 (2026-03-09)
|
||||
|
||||
### Changed
|
||||
|
||||
- Launcher: Removed the YouTube subtitle generation mode switch so YouTube playback always preloads subtitles before mpv starts.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Launcher: Hardened YouTube AI subtitle fixing so fenced SRT output and text-only one-cue-per-block responses can still be applied without losing original cue timing.
|
||||
- Launcher: Skipped AniSkip lookup during URL playback and YouTube subtitle-preload playback, limiting AniSkip to local file targets where it can actually resolve anime metadata.
|
||||
- Launcher: Keep the background SubMiner process running after a launcher-managed mpv session exits so the next mpv instance can reconnect without restarting the app.
|
||||
- Launcher: Reuse prior tokenization readiness after the background app is already warm so reopening a video does not pause again waiting for duplicate warmup completion.
|
||||
- Windows: Acquire the app single-instance lock earlier so Windows overlay/video launches reuse the running background SubMiner process instead of booting a second full app and repeating startup warmups.
|
||||
|
||||
## v0.3.0 (2026-03-05)
|
||||
|
||||
- Added keyboard-driven Yomitan navigation and popup controls, including optional auto-pause.
|
||||
- Added subtitle/jump keyboard handling fixes for smoother subtitle playback control.
|
||||
- Improved Anki/Yomitan reliability with stronger Yomitan proxy syncing and safer extension refresh logic.
|
||||
- Added Subsync `replace` option and deterministic retime naming for subtitle workflows.
|
||||
- Moved aniskip resolution to launcher-script options for better control.
|
||||
- Tuned tokenizer frequency highlighting filters for improved term visibility.
|
||||
- Added release build quality-of-life for CLI publish (`gh`-based clobber upload).
|
||||
- Removed docs Plausible integration and cleaned associated tracker settings.
|
||||
|
||||
## v0.2.3 (2026-03-02)
|
||||
|
||||
- Added performance and tokenization optimizations (faster warmup, persistent MeCab usage, reduced enrichment lookups).
|
||||
- Added subtitle controls for no-jump delay shifts.
|
||||
- Improved subtitle highlight logic with priority and reliability fixes.
|
||||
- Fixed plugin loading behavior to keep OSD visible during startup.
|
||||
- Fixed Jellyfin remote resume behavior and improved autoplay/tokenization interaction.
|
||||
- Updated startup flow to load dictionaries asynchronously and unblock first tokenization sooner.
|
||||
|
||||
## v0.2.2 (2026-03-01)
|
||||
|
||||
- Improved subtitle highlighting reliability for frequency modes.
|
||||
- Fixed Jellyfin misc info formatting cleanup.
|
||||
- Version bump maintenance for 0.2.2.
|
||||
|
||||
## v0.2.1 (2026-03-01)
|
||||
|
||||
- Delivered Jellyfin and Subsync fixes from release patch cycle.
|
||||
- Version bump maintenance for 0.2.1.
|
||||
|
||||
## v0.2.0 (2026-03-01)
|
||||
|
||||
- Added task-related release work for the overlay 2.0 cycle.
|
||||
- Introduced Overlay 2.0.
|
||||
- Improved release automation reliability.
|
||||
|
||||
## v0.1.2 (2026-02-24)
|
||||
|
||||
- Added encrypted AniList token handling and default GNOME keyring support.
|
||||
- Added launcher passthrough for password-store flows (Jellyfin path).
|
||||
- Updated docs for auth and integration behavior.
|
||||
- Version bump maintenance for 0.1.2.
|
||||
|
||||
## v0.1.1 (2026-02-23)
|
||||
|
||||
- Fixed overlay modal focus handling (`grab input`) behavior.
|
||||
- Version bump maintenance for 0.1.1.
|
||||
|
||||
## v0.1.0 (2026-02-23)
|
||||
|
||||
- Bootstrapped Electron runtime, services, and composition model.
|
||||
- Added runtime asset packaging and dependency vendoring.
|
||||
- Added project docs baseline, setup guides, architecture notes, and submodule/runtime assets.
|
||||
- Added CI release job dependency ordering fixes before launcher build.
|
||||
@@ -1,9 +1,10 @@
|
||||
.PHONY: help deps build build-launcher install build-linux build-macos build-macos-unsigned clean install-linux install-macos install-windows install-plugin uninstall uninstall-linux uninstall-macos uninstall-windows print-dirs pretty lint ensure-bun generate-config generate-example-config dev-start dev-start-macos dev-watch dev-watch-macos dev-toggle dev-stop
|
||||
.PHONY: help deps build build-launcher install build-linux build-macos build-macos-unsigned clean install-linux install-macos install-plugin uninstall uninstall-linux uninstall-macos print-dirs pretty ensure-bun generate-config generate-example-config docs-dev docs docs-preview dev-start dev-start-macos dev-toggle dev-stop
|
||||
|
||||
APP_NAME := subminer
|
||||
THEME_SOURCE := assets/themes/subminer.rasi
|
||||
LAUNCHER_OUT := dist/launcher/$(APP_NAME)
|
||||
THEME_FILE := subminer.rasi
|
||||
PLUGIN_LUA := plugin/subminer.lua
|
||||
PLUGIN_CONF := plugin/subminer.conf
|
||||
|
||||
# Default install prefix for the wrapper script.
|
||||
@@ -20,6 +21,11 @@ MACOS_DATA_DIR ?= $(HOME)/Library/Application Support/SubMiner
|
||||
MACOS_APP_DIR ?= $(HOME)/Applications
|
||||
MACOS_APP_DEST ?= $(MACOS_APP_DIR)/SubMiner.app
|
||||
|
||||
# mpv plugin install directories.
|
||||
MPV_CONFIG_DIR ?= $(HOME)/.config/mpv
|
||||
MPV_SCRIPTS_DIR ?= $(MPV_CONFIG_DIR)/scripts
|
||||
MPV_SCRIPT_OPTS_DIR ?= $(MPV_CONFIG_DIR)/script-opts
|
||||
|
||||
# If building from source, the AppImage will typically land in release/.
|
||||
APPIMAGE_SRC := $(firstword $(wildcard release/SubMiner-*.AppImage))
|
||||
MACOS_APP_SRC := $(firstword $(wildcard release/*.app release/*/*.app))
|
||||
@@ -36,17 +42,6 @@ else
|
||||
PLATFORM := unknown
|
||||
endif
|
||||
|
||||
WINDOWS_APPDATA ?= $(if $(APPDATA),$(subst \,/,$(APPDATA)),$(HOME)/AppData/Roaming)
|
||||
|
||||
# mpv plugin install directories.
|
||||
ifeq ($(PLATFORM),windows)
|
||||
MPV_CONFIG_DIR ?= $(WINDOWS_APPDATA)/mpv
|
||||
else
|
||||
MPV_CONFIG_DIR ?= $(HOME)/.config/mpv
|
||||
endif
|
||||
MPV_SCRIPTS_DIR ?= $(MPV_CONFIG_DIR)/scripts
|
||||
MPV_SCRIPT_OPTS_DIR ?= $(MPV_CONFIG_DIR)/script-opts
|
||||
|
||||
help:
|
||||
@printf '%s\n' \
|
||||
"Targets:" \
|
||||
@@ -58,23 +53,21 @@ help:
|
||||
" clean Remove build artifacts (dist/, release/, AppImage, binary)" \
|
||||
" dev-start Build and launch local Electron app" \
|
||||
" dev-start-macos Build and launch local Electron app with macOS tracker backend" \
|
||||
" dev-watch Start fast watch loop (tsc + renderer + Electron dev app)" \
|
||||
" dev-watch-macos Start watch loop with forced macOS tracker backend" \
|
||||
" dev-toggle Toggle overlay in a running local Electron app" \
|
||||
" dev-stop Stop a running local Electron app" \
|
||||
" docs-dev Run VitePress docs dev server" \
|
||||
" docs Build VitePress static docs" \
|
||||
" docs-preview Preview built VitePress docs" \
|
||||
" install-linux Install Linux wrapper/theme/app artifacts" \
|
||||
" install-macos Install macOS wrapper/theme/app artifacts" \
|
||||
" install-windows Install Windows mpv plugin artifacts" \
|
||||
" install-plugin Install mpv Lua plugin and plugin config" \
|
||||
" generate-config Generate ~/.config/SubMiner/config.jsonc from centralized defaults" \
|
||||
"" \
|
||||
"Other targets:" \
|
||||
" deps Install JS dependencies (root + stats + texthooker-ui)" \
|
||||
" deps Install JS dependencies (root + texthooker-ui)" \
|
||||
" uninstall-linux Remove Linux install artifacts" \
|
||||
" uninstall-macos Remove macOS install artifacts" \
|
||||
" uninstall-windows Remove Windows mpv plugin artifacts" \
|
||||
" print-dirs Show resolved install locations" \
|
||||
" lint Lint stats (format check)" \
|
||||
"" \
|
||||
"Variables:" \
|
||||
" PREFIX=... Override wrapper install prefix (default: $$HOME/.local)" \
|
||||
@@ -83,7 +76,7 @@ help:
|
||||
" LINUX_DATA_DIR=... Override Linux app data dir" \
|
||||
" MACOS_DATA_DIR=... Override macOS app data dir" \
|
||||
" MACOS_APP_DIR=... Override macOS app install dir (default: $$HOME/Applications)" \
|
||||
" MPV_CONFIG_DIR=... Override mpv config dir (default: $$HOME/.config/mpv or %APPDATA%/mpv on Windows)"
|
||||
" MPV_CONFIG_DIR=... Override mpv config dir (default: $$HOME/.config/mpv)"
|
||||
|
||||
print-dirs:
|
||||
@printf '%s\n' \
|
||||
@@ -94,10 +87,6 @@ print-dirs:
|
||||
"MACOS_DATA_DIR=$(MACOS_DATA_DIR)" \
|
||||
"MACOS_APP_DIR=$(MACOS_APP_DIR)" \
|
||||
"MACOS_APP_DEST=$(MACOS_APP_DEST)" \
|
||||
"WINDOWS_APPDATA=$(WINDOWS_APPDATA)" \
|
||||
"MPV_CONFIG_DIR=$(MPV_CONFIG_DIR)" \
|
||||
"MPV_SCRIPTS_DIR=$(MPV_SCRIPTS_DIR)" \
|
||||
"MPV_SCRIPT_OPTS_DIR=$(MPV_SCRIPT_OPTS_DIR)" \
|
||||
"APPIMAGE_SRC=$(APPIMAGE_SRC)" \
|
||||
"MACOS_APP_SRC=$(MACOS_APP_SRC)" \
|
||||
"MACOS_ZIP_SRC=$(MACOS_ZIP_SRC)"
|
||||
@@ -105,25 +94,19 @@ print-dirs:
|
||||
deps:
|
||||
@$(MAKE) --no-print-directory ensure-bun
|
||||
@bun install
|
||||
@cd stats && bun install --frozen-lockfile
|
||||
@cd vendor/texthooker-ui && bun install --frozen-lockfile
|
||||
|
||||
ensure-bun:
|
||||
@command -v bun >/dev/null 2>&1 || { printf '%s\n' "[ERROR] bun not found"; exit 1; }
|
||||
|
||||
pretty: ensure-bun
|
||||
@bun run format:src
|
||||
@bun run format:stats
|
||||
|
||||
lint: ensure-bun
|
||||
@bun run lint:stats
|
||||
@bun run format
|
||||
|
||||
build:
|
||||
@printf '%s\n' "[INFO] Detected platform: $(PLATFORM)"
|
||||
@case "$(PLATFORM)" in \
|
||||
linux) $(MAKE) --no-print-directory build-linux ;; \
|
||||
macos) $(MAKE) --no-print-directory build-macos ;; \
|
||||
windows) printf '%s\n' "[INFO] Windows builds run via: bun run build:win" ;; \
|
||||
*) printf '%s\n' "[ERROR] Unsupported OS for this Makefile target: $(PLATFORM)"; exit 1 ;; \
|
||||
esac
|
||||
|
||||
@@ -132,7 +115,6 @@ install:
|
||||
@case "$(PLATFORM)" in \
|
||||
linux) $(MAKE) --no-print-directory install-linux ;; \
|
||||
macos) $(MAKE) --no-print-directory install-macos ;; \
|
||||
windows) $(MAKE) --no-print-directory install-windows ;; \
|
||||
*) printf '%s\n' "[ERROR] Unsupported OS for this Makefile target: $(PLATFORM)"; exit 1 ;; \
|
||||
esac
|
||||
|
||||
@@ -171,8 +153,18 @@ generate-config: ensure-bun
|
||||
@bun run electron . --generate-config
|
||||
|
||||
generate-example-config: ensure-bun
|
||||
@bun run build
|
||||
@bun run generate:config-example
|
||||
|
||||
docs-dev: ensure-bun
|
||||
@bun run docs:dev
|
||||
|
||||
docs: ensure-bun
|
||||
@bun run docs:build
|
||||
|
||||
docs-preview: ensure-bun
|
||||
@bun run docs:preview
|
||||
|
||||
dev-start: ensure-bun
|
||||
@bun run build
|
||||
@bun run electron . --start
|
||||
@@ -181,12 +173,6 @@ dev-start-macos: ensure-bun
|
||||
@bun run build
|
||||
@bun run electron . --start --backend macos
|
||||
|
||||
dev-watch: ensure-bun
|
||||
@bash scripts/dev-watch.sh
|
||||
|
||||
dev-watch-macos: ensure-bun
|
||||
@bash scripts/dev-watch.sh --start --dev --backend macos
|
||||
|
||||
dev-toggle: ensure-bun
|
||||
@bun run electron . --toggle
|
||||
|
||||
@@ -229,31 +215,16 @@ install-macos: build-launcher
|
||||
fi
|
||||
@printf '%s\n' "Installed to:" " $(BINDIR)/subminer" " $(MACOS_DATA_DIR)/themes/$(THEME_FILE)" " $(MACOS_APP_DEST)"
|
||||
|
||||
install-windows:
|
||||
@printf '%s\n' "[INFO] Installing Windows mpv plugin artifacts"
|
||||
@$(MAKE) --no-print-directory install-plugin
|
||||
|
||||
install-plugin:
|
||||
@printf '%s\n' "[INFO] Installing mpv plugin artifacts"
|
||||
@install -d "$(MPV_SCRIPTS_DIR)"
|
||||
@rm -f "$(MPV_SCRIPTS_DIR)/subminer.lua" "$(MPV_SCRIPTS_DIR)/subminer-loader.lua"
|
||||
@install -d "$(MPV_SCRIPTS_DIR)/subminer"
|
||||
@install -d "$(MPV_SCRIPT_OPTS_DIR)"
|
||||
@cp -R ./plugin/subminer/. "$(MPV_SCRIPTS_DIR)/subminer/"
|
||||
@install -m 0644 "./$(PLUGIN_LUA)" "$(MPV_SCRIPTS_DIR)/subminer.lua"
|
||||
@install -m 0644 "./$(PLUGIN_CONF)" "$(MPV_SCRIPT_OPTS_DIR)/subminer.conf"
|
||||
@if [ "$(PLATFORM)" = "windows" ]; then \
|
||||
bun ./scripts/configure-plugin-binary-path.mjs "$(MPV_SCRIPT_OPTS_DIR)/subminer.conf" "$(CURDIR)" win32; \
|
||||
fi
|
||||
@printf '%s\n' "Installed to:" " $(MPV_SCRIPTS_DIR)/subminer/main.lua" " $(MPV_SCRIPTS_DIR)/subminer/" " $(MPV_SCRIPT_OPTS_DIR)/subminer.conf"
|
||||
@printf '%s\n' "Installed to:" " $(MPV_SCRIPTS_DIR)/subminer.lua" " $(MPV_SCRIPT_OPTS_DIR)/subminer.conf"
|
||||
|
||||
uninstall:
|
||||
@printf '%s\n' "[INFO] Detected platform: $(PLATFORM)"
|
||||
@case "$(PLATFORM)" in \
|
||||
linux) $(MAKE) --no-print-directory uninstall-linux ;; \
|
||||
macos) $(MAKE) --no-print-directory uninstall-macos ;; \
|
||||
windows) $(MAKE) --no-print-directory uninstall-windows ;; \
|
||||
*) printf '%s\n' "[ERROR] Unsupported OS for this Makefile target: $(PLATFORM)"; exit 1 ;; \
|
||||
esac
|
||||
# Uninstall behavior kept unchanged by default.
|
||||
uninstall: uninstall-linux
|
||||
|
||||
uninstall-linux:
|
||||
@rm -f "$(BINDIR)/subminer" "$(BINDIR)/SubMiner.AppImage"
|
||||
@@ -265,8 +236,3 @@ uninstall-macos:
|
||||
@rm -f "$(MACOS_DATA_DIR)/themes/$(THEME_FILE)"
|
||||
@rm -rf "$(MACOS_APP_DEST)"
|
||||
@printf '%s\n' "Removed:" " $(BINDIR)/subminer" " $(MACOS_DATA_DIR)/themes/$(THEME_FILE)" " $(MACOS_APP_DEST)"
|
||||
|
||||
uninstall-windows:
|
||||
@rm -rf "$(MPV_SCRIPTS_DIR)/subminer"
|
||||
@rm -f "$(MPV_SCRIPTS_DIR)/subminer.lua" "$(MPV_SCRIPTS_DIR)/subminer-loader.lua" "$(MPV_SCRIPT_OPTS_DIR)/subminer.conf"
|
||||
@printf '%s\n' "Removed:" " $(MPV_SCRIPTS_DIR)/subminer" " $(MPV_SCRIPT_OPTS_DIR)/subminer.conf"
|
||||
|
||||
@@ -1,140 +1,95 @@
|
||||
<div align="center">
|
||||
<img src="assets/SubMiner.png" width="140" alt="SubMiner logo">
|
||||
|
||||
# SubMiner
|
||||
|
||||
**Sentence-mine from mpv — look up words, one-key Anki export, immersion tracking.**
|
||||
<img src="assets/SubMiner.png" width="169" alt="SubMiner logo">
|
||||
<h1>SubMiner</h1>
|
||||
<strong>Look up words, mine to Anki, and enrich cards with context — without leaving mpv.</strong>
|
||||
<br /><br />
|
||||
|
||||
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||
[](https://github.com/ksyasuda/SubMiner)
|
||||
[]()
|
||||
[](https://docs.subminer.moe)
|
||||
[](https://aur.archlinux.org/packages/subminer-bin)
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
SubMiner is an Electron overlay for [mpv](https://mpv.io) that turns video into a sentence-mining workstation. Look up any word with [Yomitan](https://github.com/yomidevs/yomitan), mine it to Anki with one key, and track your immersion over time.
|
||||
<br />
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](./assets/minecard.mp4)
|
||||
[](./assets/minecard.mp4)
|
||||
|
||||
</div>
|
||||
|
||||
## Features
|
||||
<br />
|
||||
|
||||
**Dictionary lookups** — Yomitan runs inside the overlay. Hover or navigate to any word for full dictionary popups without leaving mpv.
|
||||
## What it does
|
||||
|
||||
**One-key Anki mining** — Press one key to create a card with the sentence, audio clip, screenshot, and machine translation from the exact playback moment.
|
||||
SubMiner is an Electron overlay that sits on top of mpv. It turns your video player into a full sentence-mining workstation:
|
||||
|
||||
<div align="center">
|
||||
<img src="docs-site/public/screenshots/yomitan-lookup.png" width="800" alt="Yomitan popup with dictionary entry and mine button over annotated subtitles in mpv">
|
||||
</div>
|
||||
- **Hover to look up** — Yomitan dictionary popups directly on subtitles
|
||||
- **One-key mining** — Creates Anki cards with sentence, audio, screenshot, and translation
|
||||
- **N+1 highlighting** — Marks known words from your Anki deck so unknown ones jump out
|
||||
- **Subtitle tools** — Download from Jimaku, sync with alass/ffsubsync, all in-player
|
||||
- **Immersion tracking** — SQLite-powered stats on your watch time and mining activity
|
||||
- **Texthooker page built in** — WebSocket streaming to external tools, no extra setup
|
||||
|
||||
**Reading annotations** — Real-time subtitle annotations with N+1 targeting, frequency highlighting, JLPT tags, and a character name dictionary. Grammar-only tokens render as plain text.
|
||||
## Quick start
|
||||
|
||||
<div align="center">
|
||||
<img src="docs-site/public/screenshots/annotations.png" width="800" alt="Annotated subtitles with frequency highlighting, JLPT underlines, known words, and N+1 targets">
|
||||
</div>
|
||||
### 1. Install
|
||||
|
||||
**Immersion dashboard** — Local stats dashboard with watch time, anime progress, vocabulary growth, mining throughput, and session history.
|
||||
|
||||
<div align="center">
|
||||
<img src="docs-site/public/screenshots/stats-overview.png" width="800" alt="Stats dashboard with watch time, cards mined, streaks, and tracking snapshot">
|
||||
</div>
|
||||
|
||||
**Integrations** — AniList episode tracking, Jellyfin remote playback, Jimaku subtitle downloads, alass/ffsubsync, and an annotated websocket feed for external clients.
|
||||
|
||||
<div align="center">
|
||||
<img src="docs-site/public/screenshots/texthooker.png" width="800" alt="Texthooker page with annotated subtitle lines and frequency highlighting">
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Install
|
||||
|
||||
<details>
|
||||
<summary><b>Arch Linux (AUR)</b></summary>
|
||||
**Linux (AppImage):**
|
||||
|
||||
```bash
|
||||
paru -S subminer-bin
|
||||
```
|
||||
|
||||
Or manually:
|
||||
|
||||
```bash
|
||||
git clone https://aur.archlinux.org/subminer-bin.git && cd subminer-bin && makepkg -si
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Linux (AppImage)</b></summary>
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.local/bin
|
||||
wget https://github.com/ksyasuda/SubMiner/releases/latest/download/SubMiner.AppImage -O ~/.local/bin/SubMiner.AppImage \
|
||||
&& chmod +x ~/.local/bin/SubMiner.AppImage
|
||||
wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer -O ~/.local/bin/subminer \
|
||||
&& chmod +x ~/.local/bin/subminer
|
||||
wget https://github.com/ksyasuda/SubMiner/releases/latest/download/SubMiner-0.1.0.AppImage -O ~/.local/bin/SubMiner.AppImage
|
||||
chmod +x ~/.local/bin/SubMiner.AppImage
|
||||
wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer -O ~/.local/bin/subminer
|
||||
chmod +x ~/.local/bin/subminer
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> The `subminer` wrapper uses a [Bun](https://bun.sh) shebang. Make sure `bun` is on your `PATH`.
|
||||
|
||||
</details>
|
||||
**From source** or **macOS** — see the [installation guide](https://docs.subminer.moe/installation#from-source).
|
||||
|
||||
<details>
|
||||
<summary><b>macOS / Windows / From source</b></summary>
|
||||
|
||||
**macOS** — Download the latest DMG/ZIP from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest) and drag `SubMiner.app` into `/Applications`.
|
||||
|
||||
**Windows** — Download the latest installer or portable `.zip` from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest). Keep `mpv` on `PATH`.
|
||||
|
||||
**From source** — See [docs.subminer.moe/installation#from-source](https://docs.subminer.moe/installation#from-source).
|
||||
|
||||
</details>
|
||||
|
||||
### First Launch
|
||||
|
||||
Run `SubMiner.AppImage` (Linux), `SubMiner.app` (macOS), or `SubMiner.exe` (Windows). On first launch, SubMiner starts in the tray, creates a default config, and opens a setup popup where you can install the mpv plugin and configure Yomitan dictionaries.
|
||||
|
||||
### Mine
|
||||
### 2. Install the mpv plugin and configuration file
|
||||
|
||||
```bash
|
||||
subminer video.mkv # auto-starts overlay + resumes playback
|
||||
subminer --start video.mkv # explicit overlay start (if plugin auto_start=no)
|
||||
subminer stats # open the immersion dashboard
|
||||
subminer stats -b # keep the stats daemon running in background
|
||||
subminer stats -s # stop the dedicated stats daemon
|
||||
subminer stats cleanup # repair/prune stored stats vocabulary rows
|
||||
wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer-assets-0.1.0.tar.gz -O /tmp/subminer-assets.tar.gz
|
||||
tar -xzf /tmp/subminer-assets.tar.gz -C /tmp
|
||||
cp /tmp/plugin/subminer.lua ~/.config/mpv/scripts/
|
||||
cp /tmp/plugin/subminer.conf ~/.config/mpv/script-opts/
|
||||
mkdir -p ~/.config/SubMiner && cp /tmp/config.example.jsonc ~/.config/SubMiner/config.jsonc
|
||||
```
|
||||
|
||||
---
|
||||
### 3. Set up Yomitan Dictionaries
|
||||
|
||||
```bash
|
||||
subminer app --start --yomitan
|
||||
```
|
||||
|
||||
### 4. Mine
|
||||
|
||||
```bash
|
||||
subminer app --start --background
|
||||
subminer video.mkv
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
| Required | Optional |
|
||||
| ------------------------------------------------------ | ----------------------------- |
|
||||
| [`mpv`](https://mpv.io) with IPC socket | `yt-dlp` |
|
||||
| `ffmpeg` | `guessit` (AniSkip detection) |
|
||||
| `mecab` + `mecab-ipadic` | `fzf` / `rofi` |
|
||||
| [`bun`](https://bun.sh) (source builds, Linux wrapper) | `chafa`, `ffmpegthumbnailer` |
|
||||
| Linux: `hyprctl` or `xdotool` + `xwininfo` | |
|
||||
| macOS: Accessibility permission | |
|
||||
|
||||
Windows uses native window tracking and does not need the Linux compositor tools.
|
||||
| Required | Optional |
|
||||
| ------------------------------------------ | -------------------------------------------------- |
|
||||
| `bun` | |
|
||||
| `mpv` with IPC socket | `yt-dlp` |
|
||||
| `ffmpeg` | `guessit` (better AniSkip title/episode detection) |
|
||||
| `mecab` + `mecab-ipadic` | `fzf` / `rofi` |
|
||||
| Linux: `hyprctl` or `xdotool` + `xwininfo` | `chafa`, `ffmpegthumbnailer` |
|
||||
| macOS: Accessibility permission | |
|
||||
|
||||
## Documentation
|
||||
|
||||
Full guides on configuration, Anki, Jellyfin, immersion tracking, and more at **[docs.subminer.moe](https://docs.subminer.moe)**.
|
||||
For full guides on configuration, Anki, Jellyfin, and more, see [docs.subminer.moe](https://docs.subminer.moe).
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
Built on [GameSentenceMiner](https://github.com/bpwhelan/GameSentenceMiner), [Renji's Texthooker Page](https://github.com/Renji-XD/texthooker-ui), [Anacreon-Script](https://github.com/friedrich-de/Anacreon-Script), and [Bee's Character Dictionary](https://github.com/bee-san/Japanese_Character_Name_Dictionary). Subtitles from [Jimaku.cc](https://jimaku.cc). Lookups via [Yomitan](https://github.com/yomidevs/yomitan). JLPT tags from [yomitan-jlpt-vocab](https://github.com/stephenmk/yomitan-jlpt-vocab).
|
||||
Built on the shoulders of [GameSentenceMiner](https://github.com/bpwhelan/GameSentenceMiner), [mpvacious](https://github.com/Ajatt-Tools/mpvacious), [Anacreon-Script](https://github.com/friedrich-de/Anacreon-Script), and [autosubsync-mpv](https://github.com/joaquintorres/autosubsync-mpv). Subtitles powered by [Jimaku.cc](https://jimaku.cc). Dictionary lookups via [Yomitan](https://github.com/yomidevs/yomitan).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 308 KiB |
|
Before Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 23 MiB After Width: | Height: | Size: 13 MiB |
|
Before Width: | Height: | Size: 303 KiB |
|
Before Width: | Height: | Size: 523 KiB After Width: | Height: | Size: 523 KiB |
|
Before Width: | Height: | Size: 3.0 MiB |
@@ -0,0 +1,8 @@
|
||||
---
|
||||
id: m-0
|
||||
title: 'Codebase Clarity & Composability'
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Improvements to code clarity, simplicity, and composability identified during the Feb 2026 codebase review. Focus on reducing monolithic files, eliminating duplication, and improving architectural boundaries.
|
||||
@@ -1,33 +0,0 @@
|
||||
---
|
||||
id: TASK-175
|
||||
title: Address latest PR 19 review comments
|
||||
status: In Progress
|
||||
assignee: []
|
||||
created_date: '2026-03-15 10:25'
|
||||
labels:
|
||||
- pr-review
|
||||
- stats-dashboard
|
||||
dependencies: []
|
||||
references:
|
||||
- src/core/services/ipc.ts
|
||||
- src/core/services/stats-server.ts
|
||||
- src/core/services/immersion-tracker/__tests__/query.test.ts
|
||||
- src/core/services/stats-window-runtime.ts
|
||||
- src/core/services/stats-window.test.ts
|
||||
- src/shared/ipc/contracts.ts
|
||||
- src/main.ts
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Validate the latest automated review comments on PR #19 against the current branch, implement the technically valid fixes, and document any items intentionally left unchanged.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [ ] #1 Validated the latest PR #19 review comments against current branch behavior and existing architecture
|
||||
- [ ] #2 Implemented the accepted fixes with regression coverage where it fits
|
||||
- [ ] #3 Documented which latest review items were intentionally not changed because they were already addressed or not technically warranted
|
||||
<!-- AC:END -->
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
id: TASK-19
|
||||
title: Enable overlay keybinds whenever app runtime is active
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-12 08:47'
|
||||
updated_date: '2026-02-12 09:40'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Restored task after accidental cleanup. Ensure keybindings are available whenever the Electron runtime is active, while respecting focused overlay/input contexts that require local key handling.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Started implementation: tracing overlay shortcut registration lifecycle and runtime activation gating.
|
||||
|
||||
Root cause: overlay shortcut sync executes during overlay runtime initialization before overlayRuntimeInitialized is set true, so registration could be skipped until a later visibility toggle.
|
||||
|
||||
Implemented fix in initializeOverlayRuntime: after setting overlayRuntimeInitialized = true, immediately call syncOverlayShortcuts() to register overlay keybinds for active runtime state.
|
||||
|
||||
Follow-up from repro: startup path with --start did not initialize overlay runtime (commandNeedsOverlayRuntime excluded start), so overlay keybinds stayed unavailable until first overlay visibility command.
|
||||
|
||||
Updated CLI runtime gating so --start initializes overlay runtime, which activates overlay shortcut registration immediately.
|
||||
|
||||
User clarified MPV-only workflow requirement. Added MPV plugin keybindings and script messages for mining/runtime actions (copy/mine/multi/mode/field-grouping/subsync/audio-card/runtime-options) so these actions are available from mpv chord bindings without relying on overlay global shortcuts.
|
||||
|
||||
Per user direction, reverted all shortcut/runtime/plugin changes from this implementation cycle. Desired behavior is to keep keybindings working only when overlay is active.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Ensured overlay shortcuts are available as soon as overlay runtime becomes active by resyncing after activation flag is set. This prevents startup states where shortcuts remained inactive until a later overlay visibility change.
|
||||
|
||||
Follow-up fix: included --start in overlay-runtime-required commands so keybinds are active right after startup, even before toggling visible/invisible overlays.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
id: TASK-20
|
||||
title: Implement content-bounded overlay windows and decoupled secondary top bar
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-12 08:47'
|
||||
updated_date: '2026-02-12 09:42'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Implement the overlay sizing redesign documented in `overlay_window.md`: move visible/invisible overlays from fullscreen bounds to content-bounded sizing, and decouple secondary subtitle rendering into an independent top bar window/lifecycle.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Per-layer bounds ownership is implemented for overlay windows (no shared full-bounds setter for all layers).
|
||||
- [ ] #2 Renderer-to-main IPC contract exists for measured overlay content bounds with layer identity and safe validation.
|
||||
- [ ] #3 Visible and invisible overlays use content-bounded sizing with padding/clamp/jitter protections and full-bounds fallback when measurements are unavailable.
|
||||
- [ ] #4 Secondary subtitle top bar is decoupled from primary overlay visibility and follows mode-specific behavior.
|
||||
- [ ] #5 Automated tests and manual validation matrix cover wrapping, style changes, monitor moves, tracker churn, and simultaneous overlay states.
|
||||
<!-- AC:END -->
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
id: TASK-20.3
|
||||
title: >-
|
||||
Implement content-bounded sizing algorithm for visible and invisible overlay
|
||||
windows
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-12 08:47'
|
||||
updated_date: '2026-02-12 09:42'
|
||||
labels: []
|
||||
dependencies: []
|
||||
parent_task_id: TASK-20
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Implement content-bounded sizing for visible/invisible windows using measured rects plus tracker origin, with robust clamping and jitter resistance.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Bounds algorithm applies configurable padding, minimum size, display-workarea clamp, and integer snap.
|
||||
- [ ] #2 Main-process bounds updates are thresholded/debounced to reduce jitter and unnecessary `setBounds` churn.
|
||||
- [ ] #3 When no valid measurement exists, layer falls back to safe tracker/display bounds without breaking interaction.
|
||||
- [ ] #4 Visible+invisible overlays can coexist without full-window overlap/input conflicts caused by shared fullscreen bounds.
|
||||
<!-- AC:END -->
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
id: TASK-20.4
|
||||
title: Implement dedicated secondary top-bar overlay window
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-12 09:43'
|
||||
labels: []
|
||||
dependencies: []
|
||||
parent_task_id: TASK-20
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Create and integrate a dedicated secondary subtitle overlay window with independent lifecycle, z-order, bounds, and pointer policy, decoupled from primary visible/invisible overlay windows.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 A third overlay window dedicated to secondary subtitles is created and managed alongside existing visible/invisible windows.
|
||||
- [ ] #2 Secondary window visibility follows secondary mode semantics (`hidden`/`visible`/`hover`) independent of primary overlay visibility.
|
||||
- [ ] #3 Secondary subtitle text/mode/style updates are routed directly to the secondary window renderer path.
|
||||
- [ ] #4 Pointer passthrough/interaction behavior for secondary window is explicit and does not regress existing hover/selection interactions.
|
||||
- [ ] #5 Window cleanup/lifecycle (create, close, restore) integrates with existing overlay runtime lifecycle.
|
||||
<!-- AC:END -->
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
id: TASK-20.5
|
||||
title: 'Add rollout guards, tests, and validation matrix for content-bounded overlays'
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-12 09:43'
|
||||
labels: []
|
||||
dependencies: []
|
||||
parent_task_id: TASK-20
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Add safety controls and verification coverage for the new content-bounded overlay architecture and secondary top-bar window.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Feature flag or equivalent rollout guard exists for switching to new sizing/window behavior.
|
||||
- [ ] #2 Service-level/unit tests cover bounds clamping, jitter thresholding, invalid measurement fallback, and per-layer updates.
|
||||
- [ ] #3 Manual validation checklist documents and verifies wrap/no-wrap, style changes, monitor moves, tracker churn, modal interactions, and simultaneous overlay states.
|
||||
- [ ] #4 Regression checks confirm existing single-layer and startup/shutdown behavior remain stable.
|
||||
- [ ] #5 Task includes explicit pass/fail notes from validation run(s).
|
||||
<!-- AC:END -->
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
id: TASK-22
|
||||
title: Make secondary subtitles hover-revealed but non-lookupable in Yomitan sessions
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 16:40'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Investigate and implement a UX where secondary subtitles (e.g., English text in our current sessions) become visible when hovered, while explicitly preventing user interactions that allow text lookup (including Yomitan integration) on those subtitles. This should allow readability-on-hover without exposing the secondary overlay text to selection/lookup workflows.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Secondary subtitles become visible only while hovered (or via equivalent hover-triggered mechanism), and return to default hidden/low-visibility state when not hovered.
|
||||
- [ ] #2 When hovered, secondary subtitles do not trigger Yomitan lookup behavior in sessions where Yomitan is enabled.
|
||||
- [ ] #3 Secondary subtitles remain non-interactive for lookup paths (for example, text selection or lookup event propagation) while hover-visibility still works as intended.
|
||||
- [ ] #4 Primary subtitles remain functional and are not regressed by the secondary-subtitle interaction changes.
|
||||
- [ ] #5 If complete prevention of Yomitan lookup on secondary subtitles is not technically possible, the task includes the known limitations and a documented fallback behavior.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 Acceptance criteria are reviewed and covered by explicit manual/automated test coverage for hover reveal and lookup suppression behavior.
|
||||
<!-- DOD:END -->
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
id: TASK-30
|
||||
title: Enable anime streaming via extension repos with configurable mpv playback flow
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 18:32'
|
||||
updated_date: '2026-02-13 18:34'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Add a feature to query configured extension repositories for anime titles/episodes from SubMiner, let users select a streamable source, and play it through mpv with minimal friction. The result should be interactive from Electron (and triggered from mpv via existing command bridge), and fully configurable in app config.
|
||||
|
||||
The implementation should provide a modular backend resolver and a clear UI flow that mirrors existing modal interaction patterns, while keeping mpv playback unchanged (use loadfile with resolved URL and optional headers/referrer metadata).
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Create a stable config schema for one or more extension source backends (repos/endpoints/flags) and persist in user config + default template.
|
||||
- [ ] #2 Resolve an anime search term to candidate series from configured sources.
|
||||
- [ ] #3 Resolve an episode selection to at least one playable stream URL candidate with playback metadata when available.
|
||||
- [ ] #4 Provide an interactive user flow in the app to search/select episode and choose stream source.
|
||||
- [ ] #5 Play the selected stream by invoking the existing mpv command path.
|
||||
- [ ] #6 Support launching the flow from mpv interactions (keyboard/menu) by forwarding a command/event into the renderer modal flow.
|
||||
- [ ] #7 Add error states for empty results, no playable source, and repository failures, with clear user messages.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Execution sequence for implementation: 1) TASK-30.1 (config/model), 2) TASK-30.2 (resolver), 3) TASK-30.3 (IPC), 4) TASK-30.4 (UI modal), 5) TASK-30.5 (mpv trigger), 6) TASK-30.6 (validation/rollout checklist).
|
||||
|
||||
Rollout recommendation: complete TASK-30.6 only after TASK-30.1-30.5 are done and can be verified in combination.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 Config schema validated in app startup (bad config surfaces clear error).
|
||||
- [ ] #2 No hardcoded source/resolver URLs in UI layer; resolver details are backend-driven.
|
||||
- [ ] #3 Play command path uses existing mpv IPC/runtime helpers.
|
||||
- [ ] #4 Documentation includes how to configure extension repos and optional auth/headers.
|
||||
- [ ] #5 Feature is gated behind a config flag and can be disabled cleanly.
|
||||
<!-- DOD:END -->
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
id: TASK-30.1
|
||||
title: Design extension source config model and defaults
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 18:32'
|
||||
updated_date: '2026-02-13 18:34'
|
||||
labels: []
|
||||
dependencies: []
|
||||
parent_task_id: TASK-30
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Define a backend-agnostic configuration contract for extension repository streaming, including resolver endpoints/process mode, query/auth headers, timeouts, enable flags, and source preference. Wire schema through Config/ResolvedConfig and generated template/defaults so users can manage repos entirely through config.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Add new config sections for extension source providers in Config and ResolvedConfig types.
|
||||
- [ ] #2 Add validation defaults and env-compatible parsing for provider list, auth, header overrides, and feature flags.
|
||||
- [ ] #3 Update config template and docs text so defaults are discoverable and editable.
|
||||
- [ ] #4 Invalid/missing config should fail fast with a clear message path.
|
||||
- [ ] #5 Existing config readers do not regress when no providers are configured.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Phase 1 — Foundation: config contract + validation + defaults
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 Config examples in template/docs include at least one provider entry shape.
|
||||
- [ ] #2 Defaults remain backward-compatible when key is absent.
|
||||
- [ ] #3 Feature can be disabled without touching unrelated settings.
|
||||
<!-- DOD:END -->
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
id: TASK-30.2
|
||||
title: Implement extension resolver service (search + episode + stream resolution)
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 18:32'
|
||||
updated_date: '2026-02-13 18:34'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-30.1
|
||||
parent_task_id: TASK-30
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Build a dedicated service in main process that queries configured extension repos and normalizes results into a unified internal model, including optional playback metadata. Keep transport abstracted so future backends (local process, remote API, Manatán-compatible source) can be swapped without changing renderer contracts.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Create a typed internal model for source, series, episode, and playable candidate with fields for quality/audio/headers/referrer/userAgent.
|
||||
- [ ] #2 Implement provider abstraction with pluggable fetch/execution strategy from config.
|
||||
- [ ] #3 Add services for searchAnime, listEpisodes, resolveStream (or equivalent) with cancellation/error boundaries.
|
||||
- [ ] #4 Normalize all provider responses into deterministic field names and stable IDs.
|
||||
- [ ] #5 Include resilient handling for empty/no-result/no-URL cases and network faults with explicit error categories.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Phase 2 — Core service: provider integration and stream resolution
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 Resolver never leaks raw provider payload to renderer.
|
||||
- [ ] #2 Streaming URL output includes reason for failure when unavailable.
|
||||
- [ ] #3 Service boundaries allow unit-level validation of request/response mapping logic.
|
||||
- [ ] #4 No blocking calls on Electron UI/main thread; all I/O is async and cancellable.
|
||||
<!-- DOD:END -->
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
id: TASK-30.3
|
||||
title: Expose resolver operations via Electron IPC to renderer
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 18:32'
|
||||
updated_date: '2026-02-13 18:34'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-30.2
|
||||
parent_task_id: TASK-30
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Add a typed preload and main-IPC contract for streaming queries and playback resolution so the renderer can initiate search/list/resolve without embedding network/provider logic in UI code.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Define IPC handlers in main with input/output schema validation and timeouts.
|
||||
- [ ] #2 Expose corresponding functions in preload `window.electronAPI` and ElectronAPI types.
|
||||
- [ ] #3 Reuse existing mpv command channel for playback and add a dedicated request/response flow for resolver actions.
|
||||
- [ ] #4 Implement safe serialization and error marshalling for resolver-specific failures.
|
||||
- [ ] #5 Add runtime wiring and lifetime management in app startup/shutdown.
|
||||
- [ ] #6 Document event/callback behavior for loading/error states.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Phase 3 — API surface: IPC/preload contract for resolver operations
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 Renderer code can query providers without importing Node-only modules.
|
||||
- [ ] #2 IPC paths have clear names and consistent response shapes across all calls.
|
||||
- [ ] #3 Error paths return explicit machine-readable codes mapped to user-visible messages.
|
||||
<!-- DOD:END -->
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
id: TASK-30.4
|
||||
title: 'Add interactive streaming modal (search, episode list, source selection, play)'
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 18:32'
|
||||
updated_date: '2026-02-13 18:34'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-30.3
|
||||
parent_task_id: TASK-30
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Implement a renderer flow to query configured providers, display results, let user choose series and episode, and trigger playback for a selected stream. The UI should support keyboard interactions and surface backend errors clearly.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Create modal UI/state model for query, results list, selected item, episode list, candidate qualities, and loading/error status.
|
||||
- [ ] #2 Wire renderer actions to new IPC methods for search/episode/resolve.
|
||||
- [ ] #3 Render one-click or enter-to-play action that calls existing mpv playback pathway.
|
||||
- [ ] #4 Persist minimal user preference (last provider/quality where possible) for faster repeat use.
|
||||
- [ ] #5 Provide empty/error states and accessibility-friendly focus/keyboard navigation for lists.
|
||||
- [ ] #6 Add a no-network mode fallback message when resolver calls fail.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Phase 4 — UX: interactive modal flow and playback callout
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 Modal state is isolated and unsubscribes listeners on close.
|
||||
- [ ] #2 No direct network logic in renderer beyond IPC calls.
|
||||
- [ ] #3 Visual style and behavior are consistent with existing modal patterns.
|
||||
<!-- DOD:END -->
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
id: TASK-30.5
|
||||
title: >-
|
||||
Wire mpv script-message/shortcut trigger into streaming modal and playback
|
||||
path
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 18:33'
|
||||
updated_date: '2026-02-13 18:34'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-30.3
|
||||
- TASK-30.4
|
||||
parent_task_id: TASK-30
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Allow users to open streaming selection from within mpv via keybind/menu and route that intent into renderer modal and playback flow without requiring separate window focus changes.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Add/extend mpv Lua plugin or existing command registry to emit a custom action for opening the streaming picker.
|
||||
- [ ] #2 Handle this action in main IPC/mpv-command pipeline and forward to renderer modal state.
|
||||
- [ ] #3 Add at least one default keybinding/menu entry documented in config/plugin notes.
|
||||
- [ ] #4 Ensure playback launched from the in-player flow uses the same command path and error messages as renderer-initiated flow.
|
||||
- [ ] #5 Add graceful handling when feature is disabled or no providers are configured.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Phase 5 — In-player entry: mpv trigger/menu integration
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 No duplicate mpv command parsing between picker and legacy commands.
|
||||
- [ ] #2 Feature can be used in overlay and mpv-only mode where applicable.
|
||||
- [ ] #3 No dependency on modal open state when launched by mpv trigger.
|
||||
- [ ] #4 Manual and keybind invocations behave consistently.
|
||||
<!-- DOD:END -->
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
id: TASK-30.6
|
||||
title: >-
|
||||
Add integration validation plan and rollout checklist for anime streaming
|
||||
feature
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 18:34'
|
||||
updated_date: '2026-02-13 18:34'
|
||||
labels: []
|
||||
dependencies: []
|
||||
parent_task_id: TASK-30
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Create a concrete validation task that defines end-to-end acceptance checks for config loading, resolver behavior, IPC contract correctness, UI flow, and mpv-triggered launch. The checklist should be actionable and align with existing project conventions so completion can be verified without guesswork.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Define test scenarios for config success/failure cases, including invalid provider config and feature disabled mode.
|
||||
- [ ] #2 Define search/list/resolve API contract tests and error-code assertions (empty, timeout, auth error, no playable URL).
|
||||
- [ ] #3 Define renderer UX checks for modal state transitions, loading indicators, empty results, selection, and play invocation.
|
||||
- [ ] #4 Define in-mpv trigger checks for command/message pathway and fallback behavior when modal disabled/unavailable.
|
||||
- [ ] #5 Define manual smoke steps for end-to-end play from query to mpv playback using at least one configured source.
|
||||
- [ ] #6 Document expected logs/telemetry markers for troubleshooting and rollback.
|
||||
- [ ] #7 Define rollback criteria and what constitutes safe partial completion.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Phase 6 — Validation: rollout, smoke tests, and release readiness checklist
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 Checklist covers happy-path and failure-path for each task dependency.
|
||||
- [ ] #2 Verification steps are executable without external tooling assumptions.
|
||||
- [ ] #3 No task can be marked done without explicit evidence fields filled in.
|
||||
- [ ] #4 Rollout and fallback behavior are documented per deployment/release phase.
|
||||
<!-- DOD:END -->
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
id: TASK-30.7
|
||||
title: >-
|
||||
Add English-source preference + hard-sub stripping workflow in Aniyomi
|
||||
streaming path
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 18:41'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-30.2
|
||||
- TASK-30.5
|
||||
parent_task_id: TASK-30
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Improve the Aniyomi/anime extension streaming flow to prefer English-capable sources with soft subtitles, and automatically recover when only hard-subbed streams are available by stripping embedded subtitles with ffmpeg and attaching external Jimaku subtitle files into mpv.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 During source scoring/selection, prefer providers/sources that declare or expose soft subtitles for English audio or subtitle tracks over hard-subbed alternatives.
|
||||
- [ ] #2 Add a config option for preferred language targets (default English) and fallback policy (favor soft subtitles, then hard-sub fallback).
|
||||
- [ ] #3 Detect when a resolved stream is hard-sub-only and a soft-sub source is unavailable for the same episode.
|
||||
- [ ] #4 When hard subs are used, attempt to generate a subtitle-less playback stream path using ffmpeg and feed an external subtitle file (including Jimaku-provided `.ass/.srt`) into the mpv playback path.
|
||||
- [ ] #5 Preserve stream selection metadata (language tags, subtitle type, availability state) for UI decisions and error messaging.
|
||||
- [ ] #6 If ffmpeg conversion is not possible/disabled or fails, surface a clear status that explains fallback behavior instead of silent failure.
|
||||
- [ ] #7 Integrate subtitle source preferences with Jimaku so user-fetched or resolved subtitles are preferred for burn-in removal cases where supported.
|
||||
- [ ] #8 Add handling for unsupported codecs/containers and provider limitations so direct passthrough is still used when hard-sub stripping is unsafe.
|
||||
- [ ] #9 Document new behavior in feature docs/FAQ: how sources are ranked, what hard-sub stripping does, and known compatibility limitations.
|
||||
<!-- AC:END -->
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
id: TASK-30.8
|
||||
title: >-
|
||||
Add observability and tuning metrics for Aniyomi subtitle-source fallback
|
||||
decisions
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 18:41'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-30.7
|
||||
parent_task_id: TASK-30
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Add lightweight telemetry/analytics hooks (local logs + optional structured counters) to measure how Aniyomi/anime streaming source selection behaves, including soft-sub preference, hard-sub fallback usage, and ffmpeg+Jimaku post-processing outcomes, to support source ranking tuning.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Track per-playback decision metadata including chosen source, language match score, subtitle mode (soft/hard), and reason for source preference ordering.
|
||||
- [ ] #2 Emit success/failure counters for hard-sub stripping attempts (started/succeeded/failed/unsupported codec) with reason codes.
|
||||
- [ ] #3 Log whether Jimaku subtitle attachment was available and successfully loaded for ffmpeg-assisted flows.
|
||||
- [ ] #4 Capture user-visible fallback reasons when preferred English/soft-sub sources are absent and hard-sub path is used.
|
||||
- [ ] #5 Add a debug/report view or log artifact with counters that can be reviewed in-app or via config/log files.
|
||||
- [ ] #6 Document metrics definitions so developers can tune source scorer and fallback policy without code changes.
|
||||
- [ ] #7 Ensure instrumentation has low overhead and is opt-out-safe with existing config flags.
|
||||
<!-- AC:END -->
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
id: TASK-30.9
|
||||
title: Expose subtitle preference and ffmpeg fallback tuning controls in settings UI
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 18:42'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-30.7
|
||||
- TASK-30.8
|
||||
parent_task_id: TASK-30
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Add user-configurable controls for Aniyomi streaming subtitle behavior, including preferred language profile, soft-vs-hard source preference, ffmpeg-assisted hard-sub removal behavior, and policy toggles so quality and fallback behavior can be tuned without code changes.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Add settings UI fields to define preferred subtitle/audiotrack language order (e.g., en, ja) and enable/disable hard-sub fallback mode.
|
||||
- [ ] #2 Add explicit toggle for enabling hard-sub stripping via ffmpeg and configurable timeout/quality limits to avoid long waits.
|
||||
- [ ] #3 Expose source ranking preferences for soft-sub vs hard-sub sources and optional fallback to native/transcoded source when preferred modes are unavailable.
|
||||
- [ ] #4 Persist settings in existing config schema with migration-safe defaults and include clear validation for invalid/unsupported values.
|
||||
- [ ] #5 Show current effective policy in streaming UI (for debugging): source selected + reason + whether subtitles are soft/hard and if ffmpeg path is active.
|
||||
- [ ] #6 Add user-facing explanatory text and warnings for ffmpeg dependency, expected CPU/cpu cost, and compatibility limits.
|
||||
- [ ] #7 Log and display when user-adjusted policy changes alter a previously preferred source choice during runtime.
|
||||
<!-- AC:END -->
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
id: TASK-34
|
||||
title: >-
|
||||
Add in-app episode browser (Ctrl+E) for local files and Jellyfin-ready
|
||||
metadata
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 22:12'
|
||||
updated_date: '2026-02-13 22:13'
|
||||
labels: []
|
||||
dependencies: []
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Implement an in-app episode browser invoked by Ctrl+E when mpv is open and connected to the Electron UI. The viewer should use the currently supplied directory (if app was launched with one) or fallback to the parent directory of the currently playing video. It should enumerate all available video files in target directory and sort them deterministically, then display them in a polished list/gallery with thumbnails in the Electron UI. Thumbnail behavior should prioritize existing matching images in the directory; otherwise generate thumbnails asynchronously in the background and update the modal as they become available. The same menu infrastructure should be shared with the planned Jellyfin integration and designed so it can display additional Jellyfin-sourced metadata without UI rewrites. It should also support launching/using an alternate picker mode compatible with external launcher UX patterns (e.g., via fzf/rofi), showing the same episode list/metadata in those contexts.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Pressing Ctrl+E in connected mode opens an episode viewer modal and closes/overlays correctly in the Electron app.
|
||||
- [ ] #2 Episode browser source directory is resolved from CLI-provided path when present, else from currently playing video's parent directory.
|
||||
- [ ] #3 Browser enumerates all supported video files in target directory and sorts them deterministically (e.g., natural/season-episode aware when possible).
|
||||
- [ ] #4 Episode list/gallery renders a consistent, usable layout with title, position/index, and thumbnail placeholders.
|
||||
- [ ] #5 If a thumbnail image file with matching name exists in the directory, it is used without background generation.
|
||||
- [ ] #6 For files without matching thumbnails, background thumbnail generation runs and updates entries as images become available in the modal.
|
||||
- [ ] #7 Thumbnail generation is cancellable/abortable and does not block UI interaction.
|
||||
- [ ] #8 The same episode viewer component/path can be reused by Jellyfin integration and can accept extended metadata payloads (e.g., show title, season/episode, runtime, description).
|
||||
- [ ] #9 `fzf`/`rofi`-compatible episode picker mode is available so the same episode set can be browsed outside the Electron modal using either backend output format.
|
||||
- [ ] #10 Episode item ordering, labels, and metadata shown in fzf/rofi mode match the in-app sorting and identity scheme.
|
||||
- [ ] #11 Both picker modes (Electron modal and fzf/rofi) resolve source directory using the same CLI/parent-of-current-video precedence rules.
|
||||
- [ ] #12 When invoked through fzf/rofi, the selected episode can be played and returns focus/flow safely to the Electron-mpv workflow.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 Feature supports Ctrl+E invocation from connected mpv session.
|
||||
- [ ] #2 Directory fallback behavior is implemented and validated with both passed-in and default paths.
|
||||
- [ ] #3 Video listing excludes unsupported formats and is correctly sorted.
|
||||
- [ ] #4 Existing local thumbnail matching works for common image/video naming patterns.
|
||||
- [ ] #5 Background thumbnail generation works asynchronously and updates UI in-place.
|
||||
- [ ] #6 UI is ready for Jellyfin metadata fields and does not require structural rewrite for server-provided data.
|
||||
- [ ] #7 No regressions in existing mpv-Electron controls for standard playback.
|
||||
<!-- DOD:END -->
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
id: TASK-43
|
||||
title: Add community subtitle timing database for shared sync corrections
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-14 02:13'
|
||||
labels:
|
||||
- feature
|
||||
- community
|
||||
- subtitles
|
||||
- sync
|
||||
dependencies: []
|
||||
priority: low
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Allow users to share their subtitle timing corrections to a community database, so other users watching the same video file get pre-synced subtitles automatically.
|
||||
|
||||
## Motivation
|
||||
|
||||
Subtitle synchronization (alass/ffsubsync) is one of the most friction-heavy steps in the mining workflow. Users spend time syncing subtitles that someone else has already synced for the exact same video. A shared database of timing corrections keyed by video file hash would eliminate redundant work.
|
||||
|
||||
## Design
|
||||
|
||||
1. **Video identification**: Use a partial file hash (first + last N bytes, or a media fingerprint) to identify video files without uploading content
|
||||
2. **Timing data**: Store the timing offset/warp parameters produced by alass/ffsubsync, not the full subtitle file
|
||||
3. **Upload flow**: After a successful sync, offer to share the timing correction (opt-in)
|
||||
4. **Download flow**: Before syncing, check the community database for existing corrections for the current video hash
|
||||
5. **Trust model**: Simple upvote/downvote on corrections; show number of users who confirmed a correction works
|
||||
|
||||
## Technical considerations
|
||||
|
||||
- Backend could be a simple REST API with a lightweight database (or even a GitHub-hosted JSON/SQLite file for v1)
|
||||
- Privacy: only file hashes and timing parameters are shared, never video content or personal data
|
||||
- Subtitle source (jimaku entry ID) can serve as an additional matching key
|
||||
- Rate limiting and abuse prevention needed for public API
|
||||
- Could integrate with existing jimaku modal flow
|
||||
|
||||
## Phasing
|
||||
|
||||
- v1: Local export/import of timing corrections (share as files)
|
||||
- v2: Optional cloud sync with community database
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Video files are identified by content hash without uploading video data.
|
||||
- [ ] #2 Timing corrections (offset/warp parameters) can be exported and shared.
|
||||
- [ ] #3 Before syncing, the app checks for existing community corrections for the current video.
|
||||
- [ ] #4 Upload of timing data is opt-in with clear privacy disclosure.
|
||||
- [ ] #5 Downloaded corrections are applied automatically or with one-click confirmation.
|
||||
- [ ] #6 Trust signal (confirmation count) is shown for community corrections.
|
||||
<!-- AC:END -->
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
id: TASK-48
|
||||
title: Add streaming mode integration in subminer using ani-cli stream source
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-14 06:01'
|
||||
updated_date: '2026-02-14 08:19'
|
||||
labels:
|
||||
- stream
|
||||
- ani-cli
|
||||
- jimaku
|
||||
dependencies: []
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Implement a new streaming mode so SubMiner can resolve and play episodes via ani-cli stream sources instead of existing file/download flow. The mode is enabled with a CLI flag (`-s` / `--stream`) and, when active, should prefer streamed playback and subtitle handling that keeps Japanese (or configured primary) subtitles as the main track. If a stream lacks Japanese subtitle tracks, fetch and inject subtitles from Jimaku and set them as primary subtitles according to SubMiner config.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Add a command-line option `-s`/`--stream` that enables streaming mode and is documented in help/config UX.
|
||||
- [ ] #2 When streaming mode is enabled, resolve episode/video URLs using existing ani-cli stream selection logic (ported into SubMiner) and route playback to the resolved stream source.
|
||||
- [ ] #3 If stream metadata contains a Japanese subtitle track, preserve that track as the primary subtitle stream path per current primary-subtitle selection behavior.
|
||||
- [ ] #4 If no Japanese subtitle track is present in the stream metadata, fetch matching subtitles from Jimaku and load them into playback.
|
||||
- [ ] #5 When loading Jimaku subtitles, replace/overwrite existing non-Japanese primary subtitle track behavior so the language configured as primary in SubMiner config is used instead.
|
||||
- [ ] #6 Non-streaming mode continues to follow current behavior when `-s`/`--stream` is not set.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Superseded by TASK-51. Streaming via ani-cli in subminer was removed due Cloudflare 403/unreliable source metadata; re-evaluate later if reintroduced behind a feature flag and redesigned resolver/metadata pipeline.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 CLI accepts both `-s` and `--stream` and enables streaming-specific behavior.
|
||||
- [ ] #2 Streaming mode resolves streams through migrated ani-cli logic.
|
||||
- [ ] #3 Japanese subs are preferred from stream metadata when available; Jimaku fallback is used only when absent.
|
||||
- [ ] #4 Primary subtitle language in config determines which language is treated as default stream subtitle track after fallback.
|
||||
- [ ] #5 Behavior is verified via test or manual checklist and documented in task notes.
|
||||
<!-- DOD:END -->
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
id: TASK-48.1
|
||||
title: Add streaming CLI flag plumbing and option wiring
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-14 06:03'
|
||||
updated_date: '2026-02-14 08:19'
|
||||
labels:
|
||||
- stream
|
||||
- cli
|
||||
dependencies: []
|
||||
parent_task_id: TASK-48
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Add the `-s`/`--stream` option end-to-end in SubMiner CLI and configuration handling, including defaults, help text, parsing/validation, and explicit routing so streaming mode is only enabled when requested.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Introduce `-s` short option and `--stream` long option in CLI parsing without breaking existing flags.
|
||||
- [ ] #2 When set, the resulting config state reflects streaming mode enabled and is propagated to playback/session startup.
|
||||
- [ ] #3 When unset, behavior remains identical to current non-streaming flows.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Superseded by TASK-51. CLI stream mode work deferred until streaming architecture is revisited.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
id: TASK-48.2
|
||||
title: Port ani-cli stream-resolution logic into SubMiner
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-14 06:03'
|
||||
updated_date: '2026-02-14 08:19'
|
||||
labels:
|
||||
- stream
|
||||
- ani-cli
|
||||
dependencies: []
|
||||
parent_task_id: TASK-48
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Implement stream URL resolution by porting ani-cli logic for selecting providers/episodes and obtaining playable stream URLs so SubMiner can consume stream sources directly.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Encapsulate stream search/provider selection logic in a dedicated module in SubMiner.
|
||||
- [ ] #2 Resolve episode query input into a canonical playable stream URL in streaming mode.
|
||||
- [ ] #3 Preserve existing behavior for non-streaming flow and expose errors when stream resolution fails.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Superseded by TASK-51. Stream URL resolution via ani-cli postponed; previous attempt exposed anti-bot/403 fragility and poor title-source reliability.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
id: TASK-48.3
|
||||
title: Implement stream subtitle selection and primary-language precedence
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-14 06:03'
|
||||
updated_date: '2026-02-14 08:19'
|
||||
labels:
|
||||
- stream
|
||||
- subtitles
|
||||
dependencies: []
|
||||
parent_task_id: TASK-48
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Handle subtitle track selection for stream playback so Japanese (or configured primary language) subtitle behavior is correctly applied when stream metadata includes or omits JP tracks.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Use stream metadata to choose and mark the configured primary language subtitle as active when available.
|
||||
- [ ] #2 If no matching primary language track exists in stream metadata, keep previous fallback behavior only for non-streaming mode.
|
||||
- [ ] #3 When no Japanese track exists and config primary is different, explicitly set configured primary as primary track for streaming flow.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Superseded by TASK-51. Stream subtitle language precedence in streaming mode deferred with full design revisit.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
id: TASK-48.4
|
||||
title: Add Jimaku subtitle fallback for stream mode
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-14 06:03'
|
||||
updated_date: '2026-02-14 08:19'
|
||||
labels:
|
||||
- stream
|
||||
- jimaku
|
||||
- subtitles
|
||||
dependencies: []
|
||||
parent_task_id: TASK-48
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
When a resolved stream lacks JP/primary-language tracks, fetch subtitles from Jimaku and inject them for playback, overriding non-primary subtitle defaults in streaming mode according to config.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Detect missing primary subtitle from stream metadata and trigger Jimaku lookup for matching episode.
|
||||
- [ ] #2 Load fetched Jimaku subtitles into playback pipeline and mark them as the primary subtitle track.
|
||||
- [ ] #3 Fallback is only used in streaming mode and should not alter subtitle behavior outside streaming.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Superseded by TASK-51. Jimaku fallback for streams deferred along with entire streaming flow.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
id: TASK-48.5
|
||||
title: Add verification plan/tests for streaming mode behavior
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-14 06:03'
|
||||
updated_date: '2026-02-14 08:19'
|
||||
labels:
|
||||
- stream
|
||||
- qa
|
||||
dependencies: []
|
||||
parent_task_id: TASK-48
|
||||
priority: low
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Create a validation plan or tests for CLI flag behavior, stream resolution, and subtitle precedence/fallback rules so streaming mode changes are measurable and regressions are caught.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Document/manual checklist covers `-s` and `--stream` invocation and streaming-only behavior.
|
||||
- [ ] #2 Include cases for (a) stream with JP subtitles, (b) no JP subtitles with Jimaku fallback, (c) primary-language not Japanese.
|
||||
- [ ] #3 Run or provide reproducible checks to confirm non-streaming behavior unchanged.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Superseded by TASK-51. Verification plan moved to deferred reimplementation context.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
id: TASK-48.6
|
||||
title: Wire -s/--stream mode to Ani-cli and Jimaku subtitle fallback
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-14 06:06'
|
||||
updated_date: '2026-02-14 08:19'
|
||||
labels: []
|
||||
dependencies: []
|
||||
references:
|
||||
- ani-cli/ani-cli
|
||||
- src/jimaku/utils.ts
|
||||
- src/core/services/anki-jimaku-service.ts
|
||||
documentation:
|
||||
- ani-cli/README.md
|
||||
- subminer
|
||||
- src/cli/help.ts
|
||||
parent_task_id: TASK-48
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Implement SubMiner streaming mode end-to-end behind a `-s`/`--stream` flag. In stream mode, use the vendored ani-cli resolution flow to get playable stream URLs instead of local file/YouTube URL handling. If resolved streams do not expose Japanese subtitles, fetch matching subtitles from Jimaku and load them into mpv as the active primary subtitle track, overwriting the current non-primary/non-Japanese default according to subminer primary-subtitle configuration.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 When `subminer -s` is used, resolution should pass a search/query through ani-cli stream logic and play the resolved stream source.
|
||||
- [ ] #2 If the stream includes a Japanese subtitle track, preserve and select the configured primary subtitle language behavior without Jimaku injection.
|
||||
- [ ] #3 If no Japanese (or configured primary language) subtitle exists in stream metadata, fetch and inject Jimaku subtitles before playback starts.
|
||||
- [ ] #4 Loaded Jimaku subtitles should become the selected primary subtitle track, replacing any existing default non-primary subtitle in that context.
|
||||
- [ ] #5 When `-s` is not passed, non-streaming behavior remains unchanged.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Superseded by TASK-51. End-to-end stream wiring to ani-cli is deferred.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 CLI exposes both `-s` and `--stream` in help/config and validation.
|
||||
- [ ] #2 Implementation includes a clear fallback path when stream subtitles are absent and Jimaku search/download fails gracefully.
|
||||
- [ ] #3 Subtitles loading path avoids temp-file leaks; temporary media/subtitle artifacts are cleaned up on exit.
|
||||
- [ ] #4 At least one verification step (manual or test) confirms stream mode path works for an episode with and without Japanese stream subtitles.
|
||||
<!-- DOD:END -->
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
id: TASK-51
|
||||
title: Revisit Ani-cli stream mode integration later
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-14 08:19'
|
||||
labels:
|
||||
- someday
|
||||
- streaming
|
||||
- ani-cli
|
||||
- feature-flag
|
||||
dependencies: []
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Current codebase has removed ani-cli integration and stream-mode from subminer temporarily. Keep a deferred design task to reintroduce streaming mode in a future cycle.
|
||||
|
||||
Findings from prior attempts:
|
||||
|
||||
- `subminer -s <query>` path relied on `ani-cli` resolving stream URLs, but returned stream URLs that are Cloudflare-protected (`tools.fast4speed.rsvp`) and often returned 403 from mpv/ytdl-hook (generic anti-bot/Forbidden).
|
||||
- Even after passing `ytdl` extractor args, stream playback via subminer still failed because URL/anti-bot handling differed from direct ani-cli execution context.
|
||||
- We also observed stream title resolution issues: selected titles from ani-cli menu were unreliable/random and broke downstream Jimaku matching behavior.
|
||||
- ffsubsync failures were difficult to debug initially due to OSD-only error visibility; logging was added to mpv log path.
|
||||
- Based on these findings and instability, stream mode should be explicitly deferred rather than partially reintroduced.
|
||||
|
||||
Proposal:
|
||||
|
||||
- Reintroduce behind a feature flag / future milestone only.
|
||||
- Re-design around a dedicated stream source resolver with robust URL acquisition and source metadata preservation (query/episode/title) before subtitle sync flows.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
@@ -1,51 +0,0 @@
|
||||
---
|
||||
id: TASK-87.7
|
||||
title: >-
|
||||
Developer workflow hygiene: make docs watch reproducible and remove stale
|
||||
small-surface drift
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-03-06 03:20'
|
||||
updated_date: '2026-03-06 03:21'
|
||||
labels:
|
||||
- tooling
|
||||
- tech-debt
|
||||
milestone: m-0
|
||||
dependencies: []
|
||||
references:
|
||||
- package.json
|
||||
- bun.lock
|
||||
- src/anki-integration/field-grouping-workflow.ts
|
||||
documentation:
|
||||
- docs/reports/2026-02-22-task-100-dead-code-report.md
|
||||
parent_task_id: TASK-87
|
||||
priority: low
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
The review found a few low-risk but recurring hygiene issues: docs:watch depends on bunx concurrently even though concurrently is not declared in package metadata, and small stale API surface remains after recent refactors, such as unused parameters in field-grouping workflow code. This task should make the developer workflow reproducible and clean up low-risk stale symbols that do not warrant a dedicated architecture task.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 The docs:watch workflow runs through declared project tooling or is rewritten to avoid undeclared dependencies.
|
||||
- [ ] #2 Small stale symbols or parameters identified during the review outside the main composition-root cleanup are removed without behavior changes.
|
||||
- [ ] #3 Any contributor-facing command changes are reflected in repository documentation.
|
||||
- [ ] #4 The cleanup remains scoped to low-risk workflow and hygiene fixes rather than expanding into large architectural refactors.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
|
||||
1. Fix the docs:watch workflow so it relies on declared project tooling or an equivalent checked-in command path.
|
||||
2. Clean up low-risk stale symbols surfaced by the review outside the main.ts architecture task, such as unused parameters left behind by refactors.
|
||||
3. Keep the task scoped: avoid pulling in main composition-root cleanup or larger Anki/runtime refactors.
|
||||
4. Verify the affected developer commands still work and document any usage changes.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
id: TASK-10
|
||||
title: Consolidate service naming conventions and barrel exports
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-11 08:21'
|
||||
updated_date: '2026-02-15 07:00'
|
||||
labels:
|
||||
- refactor
|
||||
- services
|
||||
- naming
|
||||
milestone: Codebase Clarity & Composability
|
||||
dependencies: []
|
||||
references:
|
||||
- src/core/services/index.ts
|
||||
priority: low
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
The service layer has inconsistent naming:
|
||||
|
||||
- Some functions end in `Service`: `handleCliCommandService`, `loadSubtitlePositionService`
|
||||
- Some end in `RuntimeService`: `replayCurrentSubtitleRuntimeService`, `sendMpvCommandRuntimeService`
|
||||
- Some are plain: `shortcutMatchesInputForLocalFallback`
|
||||
- Factory functions mix `create*DepsRuntimeService` with `create*Service`
|
||||
|
||||
The barrel export (src/core/services/index.ts) re-exports 79 symbols from 28 files through a single surface, which obscures dependency boundaries. Consumers import everything from `./core/services` and can't tell which service file they actually depend on.
|
||||
|
||||
Establish consistent naming:
|
||||
|
||||
- Exported service functions: `verbNounService` (e.g., `handleCliCommand`)
|
||||
- Deps factory functions: `create*Deps`
|
||||
- Consider whether the barrel re-export is still the right pattern vs direct imports from individual files.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 All service functions follow a consistent naming convention
|
||||
- [ ] #2 Decision documented on barrel export vs direct imports
|
||||
- [ ] #3 No functional changes
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Naming convention consolidation should be addressed as part of TASK-27.2 (split main.ts) and TASK-27.3 (anki-integration split). As modules are extracted and given clear boundaries, naming will be standardized at each boundary. No need to do a standalone naming pass — it's wasted effort if the module structure is about to change.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Subsumed by TASK-27.2 and TASK-27.3. Naming conventions were standardized at module boundaries during extraction. A standalone global naming pass would be churn with no structural benefit now that modules have clear ownership boundaries.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,42 +0,0 @@
|
||||
---
|
||||
id: TASK-107
|
||||
title: 'Fix Yomitan scan-token fallback fragmentation on exact-source misses'
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-07 01:10'
|
||||
updated_date: '2026-03-07 01:12'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: high
|
||||
ordinal: 9007
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Left-to-right Yomitan scanning can emit bogus fallback tokens when `termsFind` returns entries but none of their headwords carries an exact primary source for the consumed substring. Repro: `だが それでも届かぬ高みがあった` currently yields trailing fragments like `があ` / `た`, which blocks the real `あった` token from receiving frequency highlighting.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Scanner skips `termsFind` fallback entries that are not backed by an exact primary source for the consumed substring.
|
||||
- [x] #2 Repro line no longer yields bogus trailing fragments such as `があ`.
|
||||
- [x] #3 Regression coverage added for the scan-token path.
|
||||
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Removed the scan-token helper fallback that previously emitted a token from the first returned headword even when Yomitan did not report an exact primary source for the consumed substring. Added a focused regression test covering `だが それでも届かぬ高みがあった`, ensuring bogus `があ` fragmentation is skipped so the later `あった` exact match can still be tokenized and highlighted.
|
||||
|
||||
Verification:
|
||||
|
||||
- `bun test src/core/services/tokenizer/yomitan-parser-runtime.test.ts src/core/services/tokenizer.test.ts --timeout 20000`
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,43 +0,0 @@
|
||||
---
|
||||
id: TASK-108
|
||||
title: 'Exclude single kana tokens from frequency highlighting'
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-07 01:18'
|
||||
updated_date: '2026-03-07 01:22'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: medium
|
||||
ordinal: 9008
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Suppress frequency highlighting for single-character hiragana or katakana tokens. Scope is frequency-only: known/N+1/JLPT behavior stays unchanged.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Single-character hiragana tokens do not retain `frequencyRank`.
|
||||
- [x] #2 Single-character katakana tokens do not retain `frequencyRank`.
|
||||
- [x] #3 Regression coverage exists at annotation-stage and tokenizer levels.
|
||||
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Added a frequency-only suppression rule for single-character kana tokens based on token `surface`, so bogus merged fragments like `た` and standalone one-character kana no longer keep `frequencyRank`. Regression coverage now exists both in the annotation stage and in the tokenizer path, while multi-character tokens and N+1/JLPT behavior remain unchanged.
|
||||
|
||||
Verification:
|
||||
|
||||
- `bun test src/core/services/tokenizer/annotation-stage.test.ts --timeout 20000`
|
||||
- `bun test src/core/services/tokenizer.test.ts --timeout 20000`
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
id: TASK-11
|
||||
title: Break up the applyInvisibleSubtitleLayoutFromMpvMetrics mega function
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-11 08:21'
|
||||
updated_date: '2026-02-16 01:34'
|
||||
labels:
|
||||
- refactor
|
||||
- renderer
|
||||
- complexity
|
||||
milestone: Codebase Clarity & Composability
|
||||
dependencies:
|
||||
- TASK-27.5
|
||||
references:
|
||||
- src/renderer/renderer.ts
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
In renderer.ts (around lines 865-1075), `applyInvisibleSubtitleLayoutFromMpvMetrics` is a 211-line function with up to 5 levels of nesting. It handles OSD scaling calculations, platform-specific font compensation (macOS vs Linux), DPR calculations, ASS alignment tag interpretation (\an tags), baseline compensation, line-height fixes, font property application, and transform origin — all interleaved.
|
||||
|
||||
Extract into focused helpers:
|
||||
|
||||
- `calculateOsdScale(metrics, renderAreaHeight)` — pure scaling math
|
||||
- `calculateSubtitlePosition(metrics, scale, alignment)` — ASS \an tag interpretation + positioning
|
||||
- `applyPlatformFontCompensation(style, platform)` — macOS kerning/size adjustments
|
||||
- `applySubtitleStyle(element, computedStyle)` — DOM style application
|
||||
|
||||
This can be done independently of or as part of TASK-6 (renderer split).
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 No single function exceeds ~50 lines in the positioning logic
|
||||
- [x] #2 Helper functions are pure where possible (take inputs, return outputs)
|
||||
- [x] #3 Platform-specific branches isolated into dedicated helpers
|
||||
- [x] #4 Invisible overlay positioning still works correctly on Linux and macOS
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Helpers were split so positioning math, base layout, and typography/vertical handling are no longer in one monolith; see `src/renderer/positioning/invisible-layout.ts` and peer files.
|
||||
|
||||
Applied as part of TASK-27.5 with helper extraction: moved mpv subtitle layout orchestration to `invisible-layout.ts` and extracted metric/base/style helpers into `invisible-layout-metrics.ts` and `invisible-layout-helpers.ts`.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Decomposition of `applyInvisibleSubtitleLayoutFromMpvMetrics` completed as part of TASK-27.5: function body split into metric/layout/typography helpers and small coordinator preserved. Manual validation completed by user; behavior remains stable.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,60 +0,0 @@
|
||||
---
|
||||
id: TASK-117
|
||||
title: Prepare initial Windows release docs and version bump
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-08 15:17'
|
||||
updated_date: '2026-03-16 05:13'
|
||||
labels:
|
||||
- release
|
||||
- docs
|
||||
- windows
|
||||
dependencies: []
|
||||
references:
|
||||
- package.json
|
||||
- README.md
|
||||
- ../subminer-docs/installation.md
|
||||
- ../subminer-docs/usage.md
|
||||
- ../subminer-docs/changelog.md
|
||||
priority: medium
|
||||
ordinal: 53500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Prepare the initial packaged Windows release by bumping the app version and refreshing the release-facing README/backlog/docs surfaces so install and direct-command guidance no longer reads Linux-only.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 App version is bumped for the Windows release cut
|
||||
- [x] #2 README and sibling docs describe Windows packaged installation alongside Linux/macOS guidance
|
||||
- [x] #3 Backlog records the release-doc/version update with the modified references
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Bump the package version for the release cut.
|
||||
2. Update the root README install/start guidance to mention Windows packaged builds.
|
||||
3. Patch the sibling docs repo installation, usage, and changelog pages for the Windows release.
|
||||
4. Record the work in Backlog and run targeted verification on the touched surfaces.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
The public README still advertised Linux/macOS only, while the sibling docs had Windows-specific runtime notes but no actual Windows install section and several direct-command examples still assumed `SubMiner.AppImage`.
|
||||
|
||||
Bumped `package.json` to `0.5.0`, expanded the README platform/install copy to include Windows, added a Windows install section to `../subminer-docs/installation.md`, clarified in `../subminer-docs/usage.md` that direct packaged-app examples use `SubMiner.exe` on Windows, and added a `v0.5.0` changelog entry covering the initial Windows release plus the latest overlay behavior polish.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Prepared the initial Windows release documentation pass and version bump. `package.json` now reports `0.5.0`. The root `README.md` now advertises Linux, macOS, and Windows support, includes Windows packaged-install guidance, and clarifies first-launch behavior across platforms. In the sibling docs repo, `installation.md` now includes a dedicated Windows install section, `usage.md` explains that direct packaged-app examples use `SubMiner.exe` on Windows, and `changelog.md` now includes the `v0.5.0` release notes for the initial Windows build and recent overlay behavior changes.
|
||||
|
||||
Verification: targeted `bun run tsc --noEmit -p tsconfig.typecheck.json` in the app repo and `bun run docs:build` in `../subminer-docs`.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,75 +0,0 @@
|
||||
---
|
||||
id: TASK-117
|
||||
title: >-
|
||||
Replace YouTube subtitle generation with pure TypeScript pipeline and shared
|
||||
AI config
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-08 03:16'
|
||||
updated_date: '2026-03-08 03:35'
|
||||
labels: []
|
||||
dependencies: []
|
||||
references:
|
||||
- /Users/sudacode/projects/japanese/SubMiner/launcher/youtube.ts
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/anki-integration/ai.ts
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/types.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/config/definitions/defaults-integrations.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/config/resolve/subtitle-domains.ts
|
||||
- /Users/sudacode/projects/japanese/SubMiner/config.example.jsonc
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Replace the launcher YouTube subtitle generation flow with a pure TypeScript pipeline that prefers real downloadable YouTube subtitles, never uses YouTube auto-generated subtitles, locally generates missing tracks with whisper.cpp, and can optionally fix generated subtitles via a shared OpenAI-compatible AI provider config. This feature also introduces a breaking config cleanup: move provider settings to a new top-level ai section and reduce ankiConnect.ai to a boolean feature toggle.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Launcher YouTube subtitle generation prefers downloadable manual YouTube subtitles, never uses YouTube auto-generated subtitles, and locally generates only missing tracks with whisper.cpp.
|
||||
- [x] #2 Generated whisper subtitle tracks can optionally be post-processed with an OpenAI-compatible AI provider using shared top-level ai config, with validation and fallback to raw whisper output on failure.
|
||||
- [x] #3 Configuration is updated so top-level ai is canonical shared provider config, ankiConnect.ai is boolean-only, and youtubeSubgen includes whisperVadModel, whisperThreads, and fixWithAi.
|
||||
- [x] #4 Launcher CLI/config parsing, config example, and docs reflect the new breaking config shape with no migration layer.
|
||||
- [x] #5 Automated tests cover the new YouTube generation behavior, AI-fix fallback/validation behavior, shared AI config usage, and breaking config validation.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
|
||||
1. Introduce canonical top-level ai config plus youtubeSubgen runtime knobs (whisperVadModel, whisperThreads, fixWithAi) and convert ankiConnect.ai to a boolean-only toggle across types, defaults, validation, option registries, launcher config parsing, and config example/docs.
|
||||
2. Extract shared OpenAI-compatible AI client helpers from the current Anki translation code, including base URL normalization, API key / apiKeyCommand resolution, timeout handling, and response text extraction.
|
||||
3. Update Anki translation flow and hot-reload/runtime plumbing to consume global ai config while treating ankiConnect.ai as a feature gate only.
|
||||
4. Replace launcher/youtube.ts with a modular launcher/youtube pipeline that fetches only manual YouTube subtitles, generates missing tracks locally with ffmpeg + whisper.cpp + optional VAD/thread controls, and preserves preprocess/automatic playback behavior.
|
||||
5. Add optional AI subtitle-fix processing for whisper-generated tracks using the shared ai client, with strict SRT batching/validation and fallback to raw whisper output on provider or format failure.
|
||||
6. Expand automated coverage for config validation, shared AI usage, launcher config parsing, and YouTube subtitle generation behavior including removal of yt-dlp auto-subs and AI-fix fallback rules.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Implemented pure TypeScript launcher/youtube pipeline modules for manual subtitle fetch, audio extraction, whisper runs, SRT utilities, and optional AI subtitle fixing. Removed yt-dlp auto-subtitle usage from the generation path.
|
||||
|
||||
Added shared top-level ai config plus shared AI client helpers; converted ankiConnect.ai to a boolean feature gate and updated Anki runtime wiring to consume global ai config.
|
||||
|
||||
Updated launcher config parsing, config template sections, and config.example.jsonc for the breaking config shape including youtubeSubgen.whisperVadModel, youtubeSubgen.whisperThreads, and youtubeSubgen.fixWithAi.
|
||||
|
||||
Verification: bun run test:config:src passed; targeted AI/Anki/runtime tests passed; bun run typecheck passed. bun run test:launcher:unit:src reported one unrelated existing failure in launcher/aniskip-metadata.test.ts (resolveAniSkipMetadataForFile resolves MAL id and intro payload).
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Replaced the launcher YouTube subtitle flow with a modular TypeScript pipeline that prefers manual YouTube subtitles, transcribes only missing tracks with whisper.cpp, and can optionally post-fix whisper output through a shared OpenAI-compatible AI client with strict SRT validation/fallback. Introduced canonical top-level ai config, reduced ankiConnect.ai to a boolean feature gate, updated launcher/config parsing and checked-in config artifacts, and added coverage for YouTube orchestration, whisper args, SRT validation, AI fix behavior, and breaking config validation.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,65 +0,0 @@
|
||||
---
|
||||
id: TASK-118
|
||||
title: Add Windows release build and SignPath signing
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-08 15:17'
|
||||
updated_date: '2026-03-16 05:13'
|
||||
labels:
|
||||
- release
|
||||
- windows
|
||||
- signing
|
||||
dependencies: []
|
||||
references:
|
||||
- .github/workflows/release.yml
|
||||
- build/installer.nsh
|
||||
- build/signpath-windows-artifact-config.xml
|
||||
- package.json
|
||||
priority: high
|
||||
ordinal: 54500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Extend the tag-driven release workflow so Windows artifacts are built on GitHub-hosted runners and submitted to SignPath for free open-source Authenticode signing, while preserving the existing macOS notarization path.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Release workflow builds Windows installer and ZIP artifacts on `windows-latest`
|
||||
- [x] #2 Workflow submits unsigned Windows artifacts to SignPath and uploads the signed outputs for release publication
|
||||
- [x] #3 Repository includes a checked-in SignPath artifact-configuration source of truth for the Windows release files
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Inspect the existing release workflow and current Windows packaging configuration.
|
||||
2. Add a Windows release job that builds unsigned artifacts, uploads them as a workflow artifact, and submits them to SignPath.
|
||||
3. Update the release aggregation job to publish signed Windows assets and mention Windows install steps in the generated release notes.
|
||||
4. Check in the Windows SignPath artifact configuration XML used to define what gets signed.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
The repository already had Windows packaging configuration (`build:win`, NSIS include script, Windows helper asset packaging), but the release workflow still built Linux and macOS only.
|
||||
|
||||
Added a `build-windows` job to `.github/workflows/release.yml` that runs on `windows-latest`, validates required SignPath secrets, builds unsigned Windows artifacts, uploads them with `actions/upload-artifact@v4`, and then calls the official `signpath/github-action-submit-signing-request@v2` action to retrieve signed outputs.
|
||||
|
||||
Checked in `build/signpath-windows-artifact-config.xml` as the source-of-truth artifact configuration for SignPath. It signs the top-level NSIS installer EXE and deep-signs `.exe` and `.dll` files inside the portable ZIP artifact.
|
||||
|
||||
Updated the release aggregation job to download the signed Windows artifacts and added a Windows install section to the generated GitHub release body.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Windows release publishing is now wired into the tag-driven workflow. `.github/workflows/release.yml` builds Windows artifacts on `windows-latest`, submits them to SignPath using the official GitHub action, and publishes the signed `.exe` and `.zip` outputs alongside the Linux and macOS artifacts. The workflow now requests the additional `actions: read` permission required by the SignPath GitHub integration, and the generated release notes now include Windows installation steps.
|
||||
|
||||
The checked-in `build/signpath-windows-artifact-config.xml` file defines the SignPath artifact structure expected by the workflow artifact ZIP: sign the top-level `SubMiner-*.exe` installer and deep-sign `.exe` and `.dll` files inside `SubMiner-*.zip`.
|
||||
|
||||
Verification: workflow/static changes were checked with `git diff --check` on the touched files. Actual signing requires configured SignPath secrets and a matching artifact configuration in your SignPath project.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,73 +0,0 @@
|
||||
---
|
||||
id: TASK-118
|
||||
title: Fix GitHub release workflow publish step failure
|
||||
status: Done
|
||||
assignee:
|
||||
- Codex
|
||||
created_date: '2026-03-08 03:34'
|
||||
updated_date: '2026-03-08 03:38'
|
||||
labels:
|
||||
- ci
|
||||
- release
|
||||
- github-actions
|
||||
dependencies: []
|
||||
references:
|
||||
- /Users/sudacode/projects/japanese/SubMiner/.github/workflows/release.yml
|
||||
- 'https://github.com/ksyasuda/SubMiner/actions/runs/22812335927'
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
The GitHub Actions Release workflow fails during the Publish Release step for tag releases because the gh CLI invocation passes invalid arguments when creating or editing the GitHub release. Restore successful release publication for tagged builds without changing unrelated release packaging behavior.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Tagged Release workflow completes the Publish Release step without gh CLI argument errors.
|
||||
- [x] #2 Release workflow still creates or updates the GitHub release as a non-prerelease for normal version tags.
|
||||
- [x] #3 A regression check covers the publish command shape or workflow behavior that caused this failure.
|
||||
- [x] #4 Any release workflow behavior change is documented in repository docs or workflow comments if needed.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
|
||||
1. Add a targeted regression test for .github/workflows/release.yml that fails if the publish step passes an argument to the gh --prerelease boolean flag or otherwise omits explicit non-prerelease behavior.
|
||||
2. Run the targeted test to confirm the current workflow fails for the expected reason.
|
||||
3. Patch the Publish Release step in .github/workflows/release.yml to remove the invalid gh CLI usage while preserving non-prerelease release creation/update behavior.
|
||||
4. Re-run the targeted regression test and any relevant lightweight verification, then record results in task notes.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Identified root cause from GitHub Actions run 22812335927: Publish Release failed with `accepts 1 arg(s), received 2` because the workflow passed a value to gh's boolean prerelease flag.
|
||||
|
||||
Added a workflow comment clarifying that omitting the prerelease flag keeps normal releases as non-prerelease releases.
|
||||
|
||||
Added src/release-workflow.test.ts and wired it into `bun run test:fast` so CI catches the invalid workflow shape before the next tag.
|
||||
|
||||
Verification: `bun test src/release-workflow.test.ts`, `bun run typecheck`, and `bun run test:fast` all passed locally.
|
||||
|
||||
Code-review pass found no issues; remaining caveat is that prerelease tag semantics are still not modeled for tags like `v1.0.0-beta.1`, which is outside this fix scope.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Fixed the GitHub Actions release publish step so tagged releases no longer fail on invalid gh CLI usage. The workflow now omits the prerelease flag when creating or editing normal releases, which preserves existing non-prerelease behavior and avoids the `accepts 1 arg(s), received 2` failure seen in run 22812335927.
|
||||
|
||||
Added a small regression test that reads `.github/workflows/release.yml` and asserts the publish step does not set the prerelease flag, then included that test in `bun run test:fast` so the main verification lane catches this class of workflow regression before the next release.
|
||||
|
||||
Validation run locally: `bun test src/release-workflow.test.ts`, `bun run typecheck`, and `bun run test:fast`. Residual risk: prerelease-tag semantics remain unchanged for tags such as `v1.0.0-beta.1`; this fix is intentionally scoped to restoring normal tagged release publication.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
id: TASK-12
|
||||
title: Add renderer module bundling for multi-file renderer support
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-11 08:21'
|
||||
updated_date: '2026-02-16 02:14'
|
||||
labels:
|
||||
- infrastructure
|
||||
- renderer
|
||||
- build
|
||||
milestone: Codebase Clarity & Composability
|
||||
dependencies:
|
||||
- TASK-5
|
||||
references:
|
||||
- src/renderer/renderer.ts
|
||||
- src/renderer/index.html
|
||||
- package.json
|
||||
- tsconfig.json
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Currently renderer.ts is a single file loaded directly by Electron's renderer process via a script tag in index.html. To split it into modules (TASK-6), we need a bundling step since Electron renderer's default context doesn't support bare ES module imports without additional configuration.
|
||||
|
||||
Options:
|
||||
|
||||
1. **esbuild** — fast, minimal config, already used in many Electron projects
|
||||
2. **Electron's native ESM support** — requires `"type": "module"` and sandbox configuration
|
||||
3. **TypeScript compiler output** — if targeting a single concatenated bundle
|
||||
|
||||
The build pipeline already compiles TypeScript and copies renderer assets. Adding a bundling step for the renderer would slot into the existing `npm run build` script.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Renderer code can be split across multiple .ts files with imports
|
||||
- [x] #2 Build pipeline bundles renderer modules into a single output for Electron
|
||||
- [x] #3 Existing `make build` still works end-to-end
|
||||
- [x] #4 No runtime errors in renderer process
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Updated root npm build pipeline to use an explicit renderer bundle step via esbuild. Added `build:renderer` script to emit a single `dist/renderer/renderer.js` from `src/renderer/renderer.ts`; `build` now runs `pnpm run build:renderer` and preserves existing index/style copy and macOS helper step. Added `esbuild` to devDependencies.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Implemented renderer bundling step and wired `build` to use it. This adds `pnpm run build:renderer` which bundles `src/renderer/renderer.ts` into a single `dist/renderer/renderer.js` for Electron to load. Also added `esbuild` as a dev dependency and aligned `pnpm-lock.yaml` importer metadata for dependency consistency. Kept `index.html`/`style.css` copy path unchanged, so renderer asset layout remains stable.
|
||||
|
||||
Implemented additional test-layer type fix after build breakage by correcting `makeDepsFromMecabTokenizer` and related `tokenizeWithMecab` mocks to match expected `Token` vs `MergedToken` shapes, keeping runtime behavior unchanged while satisfying TS checks.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,50 +0,0 @@
|
||||
---
|
||||
id: TASK-155
|
||||
title: Move user docs site back into main repo
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-10 19:20'
|
||||
updated_date: '2026-03-10 19:38'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: medium
|
||||
ordinal: 15500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Move the standalone VitePress docs site from the sibling `../subminer-docs` checkout back into the main `SubMiner` repo so docs can be updated alongside code and local tooling can reference one repository.
|
||||
|
||||
Scope:
|
||||
|
||||
- import the tracked docs-site source into a dedicated in-repo subdirectory
|
||||
- update scripts/tests/docs instructions that assume a sibling `../subminer-docs` checkout
|
||||
- preserve Cloudflare Pages deployability from a repo subdirectory
|
||||
- verify the app repo and docs site both still build/test from the new layout
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 The user-facing VitePress docs source lives inside the `SubMiner` repo in a dedicated subdirectory.
|
||||
- [x] #2 First-party scripts/tests/docs no longer require `../subminer-docs` for normal operation.
|
||||
- [x] #3 In-repo docs instructions include the Cloudflare Pages subdirectory deploy settings.
|
||||
- [x] #4 Verification covers the relocated docs site build/tests plus affected app-repo checks.
|
||||
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Imported the VitePress site into `docs-site/` inside the main repo and updated project instructions, docs contributor guidance, generator logic, and regression tests to treat that in-repo directory as the docs source of truth.
|
||||
|
||||
Added root proxy scripts for `docs:dev`, `docs:build`, `docs:preview`, and `docs:test`, repointed config-example generation to `docs-site/public/config.example.jsonc`, switched docs edit links to the main `SubMiner` repo, and documented the Cloudflare Pages subdirectory settings (`docs-site` root, `.vitepress/dist` output, `docs-site/**` watch path).
|
||||
|
||||
Verified with `bun run format:check:src`, `bun run typecheck`, `bun run docs:test`, `bun run docs:build`, `bun run test:config:src`, and `bun run test:fast`.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
id: TASK-20.1
|
||||
title: Refactor overlay runtime to use per-layer window bounds ownership
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-12 08:47'
|
||||
updated_date: '2026-02-13 08:04'
|
||||
labels: []
|
||||
dependencies: []
|
||||
parent_task_id: TASK-20
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Refactor overlay runtime so each overlay layer owns and applies its bounds independently. Keep tracker geometry as shared origin input only.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 `updateOverlayBoundsService` no longer applies the same bounds to every overlay window by default.
|
||||
- [x] #2 Main runtime/manager exposes per-layer bounds update paths for visible and invisible overlays.
|
||||
- [x] #3 Window tracker updates feed shared origin data; each layer applies its own computed bounds.
|
||||
- [x] #4 Single-layer behavior (visible-only or invisible-only) remains unchanged from user perspective.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Started implementation for per-layer overlay bounds ownership refactor.
|
||||
|
||||
Implemented per-layer bounds ownership path: visible and invisible layers now update bounds independently through overlay manager/runtime plumbing, while preserving existing geometry source behavior.
|
||||
|
||||
Replaced shared all-window bounds application with per-window bound application service and layer-specific runtime calls from visibility/tracker flows.
|
||||
|
||||
Archiving requested by user.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Refactored overlay bounds ownership to per-layer update paths. Tracker geometry remains shared input, but visible/invisible windows apply bounds independently via explicit layer routes. Existing single-layer UX behavior is preserved.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,110 @@
|
||||
---
|
||||
id: TASK-27
|
||||
title: >-
|
||||
Refactor project structure to reduce architectural complexity and split
|
||||
oversized modules
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-13 17:13'
|
||||
updated_date: '2026-02-16 01:34'
|
||||
labels:
|
||||
- 'owner:architect'
|
||||
- 'owner:backend'
|
||||
- 'owner:frontend'
|
||||
dependencies: []
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Create a phased backlog-backed restructuring plan that keeps current service-oriented architecture while reducing cognitive load from oversized modules and tightening module ownership boundaries.
|
||||
|
||||
This initiative should make future feature work easier by splitting high-complexity files, reducing tightly-coupled orchestration, and introducing measurable structural guardrails.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 A phased decomposition plan is defined in task links and references the following target files: src/main.ts, src/anki-integration.ts, src/core/services/mpv-service.ts, src/renderer/_, src/config/_, and src/core/services/\*.
|
||||
- [ ] #2 Tasks are assigned with clear owners and include explicit dependencies so execution can proceed in parallel where safe.
|
||||
- [ ] #3 Changes are constrained to structural refactors first (no behavior changes until foundational splits are in place).
|
||||
- [ ] #4 Each subtask includes test/verification expectations (manual or automated) and a rollback-safe checkpoint.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
|
||||
## Revised Execution Sequence
|
||||
|
||||
### Phase 0 — Prerequisites (outside TASK-27 tree)
|
||||
|
||||
- **TASK-7** — Extract main.ts global state into AppState container (required before TASK-27.2)
|
||||
- **TASK-9** — Remove trivial wrapper functions from main.ts (depends on TASK-7; recommended before TASK-27.2 but not blocking)
|
||||
|
||||
### Phase 1 — Lightweight Inventory
|
||||
|
||||
- **TASK-27.1** — Inventory files >400 LOC, document contracts, define smoke test checklist
|
||||
|
||||
### Phase 2 — Sequential Split Wave
|
||||
|
||||
Order matters to avoid merge conflicts:
|
||||
|
||||
1. **TASK-27.3** — anki-integration.ts split (self-contained, doesn't affect main.ts wiring until facade is stable)
|
||||
2. **TASK-27.2** — main.ts split (after TASK-7 provides AppState container and 27.3 stabilizes the Anki facade)
|
||||
3. **TASK-27.4** — mpv-service.ts split (absorbs TASK-8 scope; blocked until 27.1 is done)
|
||||
4. **TASK-27.5** — renderer positioning.ts split (downscoped; after 27.2 to avoid import-path conflicts)
|
||||
|
||||
### Phase 3 — Stabilization
|
||||
|
||||
- **TASK-27.7** — Finalization and validation cleanup
|
||||
|
||||
## Smoke Test Checklist (applies to all subtasks)
|
||||
|
||||
Every subtask must verify before merging:
|
||||
|
||||
- [ ] App starts and connects to MPV
|
||||
- [ ] Subtitle text appears in overlay
|
||||
- [ ] Card mining creates a note in Anki
|
||||
- [ ] Field grouping modal opens and resolves
|
||||
- [ ] Global shortcuts work (mine, toggle overlay, copy subtitle)
|
||||
- [ ] Secondary subtitle display works
|
||||
- [ ] TypeScript compiles with no new errors
|
||||
- [ ] All existing tests pass (`pnpm test:core && pnpm test:config`)
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
## Review Findings (2026-02-13)
|
||||
|
||||
### Key changes from original plan:
|
||||
|
||||
1. **Dropped parallel execution of Phase 2** — TASK-27.2 and 27.5 share import paths; 27.2 and 27.3 share main.ts wiring. Sequential order prevents merge conflicts.
|
||||
2. **Added TASK-7 as external prerequisite** — main.ts has 30+ module-level `let` declarations. Splitting files without a state container first just scatters mutable state.
|
||||
3. **TASK-8 absorbed into TASK-27.4** — TASK-8 (separate protocol from app logic) and TASK-27.4 (physical file split) overlap significantly. TASK-27.4 now covers both.
|
||||
4. **TASK-27.5 downscoped** — Renderer is already well-organized (241-line orchestrator, handlers/, modals/, utils/ directories). Only positioning.ts (513 LOC) needs splitting.
|
||||
5. **Simplified ownership model** — Removed multi-owner ceremony since this is effectively a solo project. Kept labels for categorical tracking only.
|
||||
6. **Added global smoke test checklist** — No end-to-end or renderer tests exist, so manual verification is the safety net for every subtask.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
TASK-27 completed: plan execution sequence completed through all major refactor subtasks. Done status now confirmed for 27.1 (ownership mapping), 27.2 (main.ts split), 27.3 (anki-integration service-domain extraction), 27.4 (mpv-service split), 27.5 (renderer positioning split), and 27.7 (final validation summary, build + tests). Remaining work is now outside TASK-27 scope.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 Plan task links and ordering are recorded in backlog descriptions.
|
||||
- [ ] #2 At least 2 independent owners are assigned with explicit labels in subtasks.
|
||||
<!-- DOD:END -->
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
id: TASK-27.7
|
||||
title: >-
|
||||
Decompose anki-integration.ts core into domain modules (field-grouping,
|
||||
card-creation, polling)
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-15 07:00'
|
||||
updated_date: '2026-02-16 01:31'
|
||||
labels:
|
||||
- refactor
|
||||
- anki
|
||||
- architecture
|
||||
dependencies:
|
||||
- TASK-27.3
|
||||
references:
|
||||
- src/anki-integration.ts
|
||||
- src/anki-integration-duplicate.ts
|
||||
- src/anki-integration-ui-feedback.ts
|
||||
- src/anki-integration/ai.ts
|
||||
parent_task_id: TASK-27
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
TASK-27.3 extracted leaf clusters (duplicate-detection 102 LOC, ai-translation 158 LOC, ui-feedback 107 LOC) but the core class remains at 2935 LOC. The heavy decomposition from the original TASK-27.3 plan was never executed.
|
||||
|
||||
Remaining extractions from the original plan:
|
||||
|
||||
1. **field-grouping** (~900 LOC) — `triggerFieldGroupingForLastAddedCard`, `applyFieldGrouping`, `computeFieldGroupingMergedFields`, `buildFieldGroupingPreview`, `performFieldGroupingMerge`, `handleFieldGroupingAuto`, `handleFieldGroupingManual`, plus ~15 span/parse/normalize helpers
|
||||
2. **card-creation** (~350 LOC) — `createSentenceCard`, `setCardTypeFields`, `extractFields`, `processSentence`, field resolution helpers
|
||||
3. **polling/lifecycle** (~250 LOC) — `start`, `stop`, `poll`, `pollOnce`, `processNewCard`
|
||||
|
||||
Also consolidate the scattered extraction files into `src/anki-integration/`:
|
||||
|
||||
- `src/anki-integration-duplicate.ts` → `src/anki-integration/duplicate.ts`
|
||||
- `src/anki-integration-ui-feedback.ts` → `src/anki-integration/ui-feedback.ts`
|
||||
- `src/anki-integration/ai.ts` (already there)
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 anki-integration.ts reduced below 800 LOC (facade + private state wiring only)
|
||||
- [ ] #2 Field-grouping cluster (~900 LOC) extracted as its own module under src/anki-integration/
|
||||
- [ ] #3 Card-creation and polling/lifecycle extracted as separate modules
|
||||
- [ ] #4 All extracted modules consolidated under src/anki-integration/ directory
|
||||
- [ ] #5 Existing facade API preserved — external callers unchanged
|
||||
- [ ] #6 All existing tests pass; build compiles cleanly
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Implemented and stabilized the anki-integration refactor + transport/protocol fixes needed to keep 27.7 moving: fixed MPV protocol sub-end timing behavior, corrected split-buffer test fixtures, added injectable mpv transport socket factory to eliminate readonly Socket monkey-patching, and resolved TypeScript strictness issues in card-creation path (typed notesInfo cast, option signature/field guards/audio stream index). Updated related tests and build outputs accordingly. Validation results: `bun run build` passes and targeted suites pass: `src/core/services/mpv-protocol.test.ts`, `src/core/services/mpv-transport.test.ts`, `src/anki-integration.test.ts` (16/16).
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
id: TASK-55
|
||||
title: Normalize service naming conventions across core/services
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-16 04:47'
|
||||
updated_date: '2026-02-17 09:12'
|
||||
labels: []
|
||||
dependencies: []
|
||||
references:
|
||||
- /home/sudacode/projects/japanese/SubMiner/src/core/services/index.ts
|
||||
priority: low
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
The core/services directory has inconsistent naming patterns that create confusion:
|
||||
|
||||
- Some files use `*Service.ts` suffix (e.g., `mpv-service.ts`, `tokenizer-service.ts`)
|
||||
- Others use `*RuntimeService.ts` or just descriptive names (e.g., `overlay-visibility-service.ts` exports functions with 'Service' in name)
|
||||
- Some functions in files have 'Service' suffix, others don't
|
||||
|
||||
This inconsistency makes it hard to predict file/function names and creates cognitive overhead.
|
||||
|
||||
Standardize on:
|
||||
|
||||
- File names: `kebab-case.ts` without 'service' suffix (e.g., `mpv.ts`, `tokenizer.ts`)
|
||||
- Function names: descriptive verbs without 'Service' suffix (e.g., `createMpvClient()`, `tokenizeSubtitle()`)
|
||||
- Barrel exports: clean, predictable names
|
||||
|
||||
Files needing audit (47 services):
|
||||
|
||||
- All files in src/core/services/ need review
|
||||
|
||||
Note: This is a large-scale refactor that should be done carefully to avoid breaking changes.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Establish naming convention rules (document in code or docs)
|
||||
- [x] #2 Audit all service files for naming inconsistencies
|
||||
- [x] #3 Rename files to follow convention (kebab-case, no 'service' suffix)
|
||||
- [x] #4 Rename exported functions to remove 'Service' suffix where present
|
||||
- [x] #5 Update all imports across the entire codebase
|
||||
- [x] #6 Update barrel exports
|
||||
- [x] #7 Run full test suite
|
||||
- [x] #8 Update any documentation referencing old names
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Starting implementation. Planning and executing a mechanical refactor: file names and exported symbols with Service suffix in `src/core/services`, then cascading import updates across `src/`.
|
||||
|
||||
Implemented naming convention refactor across `src/core/services`: removed `-service` from service file names, renamed Service-suffixed exported symbols to non-Service names, and updated barrel exports in `src/core/services/index.ts`.
|
||||
|
||||
Updated call sites across `src/main/**`, `src/core/services/**` tests, `scripts/**`, `package.json` test paths, and docs references (`docs/development.md`, `docs/architecture.md`, `docs/structure-roadmap.md`).
|
||||
|
||||
Validation completed: `pnpm run build` and `pnpm run test:fast` both pass after refactor.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Normalized `src/core/services` naming by removing `-service` from module filenames, dropping `Service` suffixes from exported service functions, and updating `src/core/services/index.ts` barrel exports to the new names. Updated all import/call sites across `src/main/**`, service tests, scripts, and docs/package test paths to match the new module and symbol names. Verified no behavior regressions with `pnpm run build` and `pnpm run test:fast` (all passing).
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
id: TASK-56
|
||||
title: Extract remaining main.ts runtime functions to dedicated modules
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-16 04:47'
|
||||
updated_date: '2026-02-16 05:16'
|
||||
labels: []
|
||||
dependencies: []
|
||||
references:
|
||||
- /home/sudacode/projects/japanese/SubMiner/src/main.ts
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
main.ts is still 1481 lines after previous refactoring efforts. While significant progress has been made, there are still opportunities to extract runtime functions into dedicated modules to further reduce its size and improve maintainability.
|
||||
|
||||
Current opportunities:
|
||||
|
||||
1. **JLPT dictionary lookup functions** (lines 470-535) - initializeJlptDictionaryLookup, ensureJlptDictionaryLookup, getJlptDictionarySearchPaths
|
||||
2. **Media path utilities** (lines 552-590) - updateCurrentMediaPath, updateCurrentMediaTitle, resolveMediaPathForJimaku
|
||||
3. **Overlay visibility helpers** (lines 1273-1360) - updateVisibleOverlayVisibility, updateInvisibleOverlayVisibility, syncInvisibleOverlayMousePassthrough
|
||||
|
||||
These functions are largely self-contained and could be moved to:
|
||||
|
||||
- `src/main/jlpt-runtime.ts`
|
||||
- `src/main/media-runtime.ts`
|
||||
- `src/main/overlay-visibility-runtime.ts`
|
||||
|
||||
Goal: Reduce main.ts complexity by extracting focused runtime helpers into dedicated modules
|
||||
|
||||
Benefits:
|
||||
|
||||
- Faster navigation and comprehension of main.ts
|
||||
- Easier to test extracted modules independently
|
||||
- Clearer separation of concerns
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Extract JLPT dictionary lookup functions to dedicated module
|
||||
- [x] #2 Extract media path utilities to dedicated module
|
||||
- [x] #3 Extract overlay visibility helpers to dedicated module
|
||||
- [x] #4 Update main.ts imports to use new modules
|
||||
- [x] #5 Ensure all functionality remains intact
|
||||
- [x] #6 Run full test suite
|
||||
- [x] #7 Keep extracted code organized and easier to follow
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Refactor complete for targeted runtime extraction: JLPT lookup, media utilities, and overlay visibility helpers were moved into dedicated main-runtime modules and wired from main.ts. Existing behavior preserved and full typecheck + test suite passed.
|
||||
|
||||
Task intent updated to prioritize readability over strict line-count target.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
id: TASK-66
|
||||
title: Add AnkiConnect tagging for mined and updated cards
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-18 09:23'
|
||||
updated_date: '2026-02-18 09:24'
|
||||
labels:
|
||||
- anki
|
||||
- config
|
||||
- enhancement
|
||||
dependencies: []
|
||||
references:
|
||||
- src/anki-connect.ts
|
||||
- src/anki-integration.ts
|
||||
- src/anki-integration/card-creation.ts
|
||||
- src/config/definitions.ts
|
||||
- src/config/service.ts
|
||||
- src/config/config.test.ts
|
||||
- docs/configuration.md
|
||||
- config.example.jsonc
|
||||
- docs/public/config.example.jsonc
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Support configuring tags applied to cards created or updated through SubMiner's AnkiConnect workflow. Default behavior should add the `SubMiner` tag to all mined/updated cards, while allowing users to override or disable automatic tagging via config.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 AnkiConnect note creation supports passing tags.
|
||||
- [x] #2 Existing-card update flows add configured tags via AnkiConnect after updates.
|
||||
- [x] #3 Default config includes `ankiConnect.tags: ["SubMiner"]`.
|
||||
- [x] #4 Config parsing validates `ankiConnect.tags` and falls back safely on invalid values.
|
||||
- [x] #5 User-facing docs/config examples mention the new tags option and default behavior.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Implemented configurable AnkiConnect tagging across SubMiner card creation and update workflows. Added `ankiConnect.tags` config (default `['SubMiner']`), validation/fallback in config parsing, AnkiConnect client support for `addNote` tags + `addTags`, and integration hooks so created/updated/merged cards receive configured tags. Updated docs and regenerated config examples; config test suite passes.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,51 +0,0 @@
|
||||
---
|
||||
id: TASK-70
|
||||
title: >-
|
||||
Overlay runtime refactor: remove invisible mode and bind visible overlay to
|
||||
mpv subtitles
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-28 02:38'
|
||||
updated_date: '2026-02-28 22:36'
|
||||
labels: []
|
||||
dependencies: []
|
||||
references:
|
||||
- 'commit:a14c9da'
|
||||
- 'commit:74554a3'
|
||||
- 'commit:75442a4'
|
||||
- 'commit:dde51f8'
|
||||
- 'commit:9e4e588'
|
||||
- src/main/overlay-runtime.ts
|
||||
- src/main/runtime/overlay-mpv-sub-visibility.ts
|
||||
- src/renderer/renderer.ts
|
||||
- docs/plans/2026-02-26-secondary-subtitles-main-overlay.md
|
||||
priority: medium
|
||||
ordinal: 1000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Scope: Branch-only commits main..HEAD on refactor-overlay (a14c9da through 9e4e588) rebuilt overlay behavior around visible overlay mode and removed legacy invisible overlay paths.
|
||||
|
||||
Delivered behavior:
|
||||
|
||||
- Removed renderer invisible overlay layout/offset helpers and main hover-highlight runtime code paths.
|
||||
- Added explicit overlay-to-mpv subtitle visibility synchronization so visible overlay state controls primary subtitle visibility consistently.
|
||||
- Hardened overlay runtime/bootstrap lifecycle around modal fallback open state and bridge send path edge cases.
|
||||
- Updated plugin/config/docs defaults to reflect visible-overlay-first behavior and subtitle binding controls.
|
||||
|
||||
Risk/impact context:
|
||||
|
||||
- Large cross-layer refactor touching runtime wiring, renderer event handling, and plugin behavior.
|
||||
- Regression coverage added/updated for overlay runtime, mpv protocol handling, renderer cleanup, and subtitle rendering paths.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Completed and validated in branch commit set before merge. Refactor reduces dead overlay modes, centralizes subtitle visibility behavior, and documents new defaults/constraints.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,58 +0,0 @@
|
||||
---
|
||||
id: TASK-71
|
||||
title: >-
|
||||
Anki integration: add local AnkiConnect proxy transport for push-based
|
||||
auto-enrichment
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-28 02:38'
|
||||
updated_date: '2026-03-04 13:55'
|
||||
labels: []
|
||||
dependencies: []
|
||||
references:
|
||||
- src/anki-integration/anki-connect-proxy.ts
|
||||
- src/anki-integration/anki-connect-proxy.test.ts
|
||||
- src/anki-integration.ts
|
||||
- src/config/resolve/anki-connect.ts
|
||||
- src/core/services/tokenizer/yomitan-parser-runtime.ts
|
||||
- src/core/services/tokenizer/yomitan-parser-runtime.test.ts
|
||||
- docs/anki-integration.md
|
||||
- config.example.jsonc
|
||||
priority: medium
|
||||
ordinal: 2000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Scope: Current unmerged working-tree changes implement an optional local AnkiConnect-compatible proxy and transport switching for card enrichment.
|
||||
|
||||
Delivered behavior:
|
||||
|
||||
- Added proxy server that forwards AnkiConnect requests and enqueues addNote/addNotes note IDs for post-create enrichment, with de-duplication and loop-configuration protection.
|
||||
- Added follow-up response-shape compatibility handling so proxy enqueue works for both envelope (`{result,error}`) and bare JSON payloads, including `multi` variants.
|
||||
- Added config schema/defaults/resolution for ankiConnect.proxy (enabled, host, port, upstreamUrl) with validation warnings and fallback behavior.
|
||||
- Runtime now supports transport switching (polling vs proxy) and restarts transport when runtime config patches change transport keys.
|
||||
- Added Yomitan default-profile server sync helper to keep bundled parser profile aligned with configured Anki endpoint.
|
||||
- Updated user docs/config examples for proxy mode setup, troubleshooting, and mining workflow behavior.
|
||||
|
||||
Risk/impact context:
|
||||
|
||||
- New network surface on local host/port; correctness depends on safe proxy upstream configuration and robust response handling.
|
||||
- Tests added for proxy queue behavior, config resolution, and parser sync routines.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Completed implementation in branch working tree; ready to merge once local changes are committed and test gate passes.
|
||||
|
||||
Follow-up fix (2026-03-04):
|
||||
|
||||
- Updated bundled Yomitan server-sync behavior to target `profileCurrent` instead of hardcoded `profiles[0]`.
|
||||
- Added proxy-mode force override so bundled Yomitan always points at SubMiner proxy URL when `ankiConnect.proxy.enabled=true`; this ensures mined cards pass through proxy and trigger auto-enrichment.
|
||||
- Added regression tests for blocked existing-server case and force-override injection path.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,42 +0,0 @@
|
||||
---
|
||||
id: TASK-72
|
||||
title: 'macOS config validation UX: show full warning details in native dialog'
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-28 02:38'
|
||||
updated_date: '2026-02-28 22:36'
|
||||
labels: []
|
||||
dependencies: []
|
||||
references:
|
||||
- 'commit:cc2f9ef'
|
||||
- src/main/config-validation.ts
|
||||
- src/main/runtime/startup-config.ts
|
||||
- docs/configuration.md
|
||||
priority: low
|
||||
ordinal: 3000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Scope: Commit cc2f9ef improves startup config-warning visibility on macOS by ensuring full details are surfaced in the native UI path and reflected in docs.
|
||||
|
||||
Delivered behavior:
|
||||
|
||||
- Config validation/runtime wiring updated so macOS users can access complete warning details instead of truncated notification-only text.
|
||||
- Added/updated tests around config validation and startup config warning flows.
|
||||
- Updated configuration docs to clarify platform-specific warning presentation behavior.
|
||||
|
||||
Risk/impact context:
|
||||
|
||||
- Low runtime risk; primarily user-facing diagnostics clarity improvement.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Completed small follow-up fix to reduce config-debug friction on macOS.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,84 +0,0 @@
|
||||
---
|
||||
id: TASK-73
|
||||
title: 'MPV plugin: split into modules and optimize startup/command runtime'
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-28 20:50'
|
||||
updated_date: '2026-02-28 22:36'
|
||||
labels: []
|
||||
dependencies: []
|
||||
references:
|
||||
- plugin/subminer/main.lua
|
||||
- plugin/subminer/bootstrap.lua
|
||||
- plugin/subminer/process.lua
|
||||
- plugin/subminer/aniskip.lua
|
||||
- plugin/subminer/environment.lua
|
||||
- plugin/subminer/lifecycle.lua
|
||||
- plugin/subminer/messages.lua
|
||||
- plugin/subminer/ui.lua
|
||||
- plugin/subminer/hover.lua
|
||||
- plugin/subminer/options.lua
|
||||
- plugin/subminer/state.lua
|
||||
- plugin/subminer.conf
|
||||
- scripts/test-plugin-start-gate.lua
|
||||
- scripts/test-plugin-process-start-retries.lua
|
||||
- launcher/commands/playback-command.ts
|
||||
- launcher/mpv.ts
|
||||
- launcher/mpv.test.ts
|
||||
- launcher/smoke.e2e.test.ts
|
||||
- Makefile
|
||||
- package.json
|
||||
- docs/mpv-plugin.md
|
||||
- docs/installation.md
|
||||
- docs/architecture.md
|
||||
- README.md
|
||||
priority: medium
|
||||
ordinal: 4000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Scope: Replace monolithic `plugin/subminer.lua` with modular plugin runtime; optimize command execution paths; align install/docs/tests; fix launcher smoke instability.
|
||||
|
||||
Delivered behavior:
|
||||
|
||||
- Full plugin cutover to `plugin/subminer/main.lua` + module directory (no runtime compatibility shim with old monolith file).
|
||||
- Process/control command path moved toward async subprocess usage for non-start actions (`stop`, `toggle`, `settings`, restart stop leg), reducing synchronous blocking in mpv script runtime.
|
||||
- AniSkip path guarded: lookup runs only in SubMiner context (launcher metadata, explicit script-message refresh, or detected running app), instead of every opened file.
|
||||
- AniSkip lookup pipeline moved to async subprocess calls (no sync `ps`/`curl` on `file-loaded`) with deferred fetch after auto-start and session-level MAL/title/payload caching.
|
||||
- Startup/runtime loading updated with lazy module initialization via bootstrap proxies.
|
||||
- Plugin install flow updated to copy `plugin/subminer/` directory and remove legacy `~/.config/mpv/scripts/subminer.lua` file.
|
||||
- Added plugin gate script wiring to package scripts (`test:plugin:src`) and launcher test flow.
|
||||
- Smoke tests stabilized across sandbox environments where UNIX socket bind can return `EPERM` while preserving normal-path assertions.
|
||||
- Playback command cleanup race fixed when mpv exits before exit-listener registration.
|
||||
|
||||
Risk/impact context:
|
||||
|
||||
- mpv plugin loading path changed from single-file to module directory; packaging/install paths must stay consistent with release assets.
|
||||
- Async control/AniSkip path changes reduce blocking but can surface timing differences; regression checks added for cold start, file-load gating, and explicit refresh behavior.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
AniSkip gate/async update delivered in plugin runtime:
|
||||
|
||||
- `plugin/subminer/lifecycle.lua`: deferred AniSkip fetch and overlay-start trigger.
|
||||
- `plugin/subminer/aniskip.lua`: async lookup pipeline + context guard + session caches.
|
||||
- `plugin/subminer/environment.lua`: async app-running detection with short cache.
|
||||
- `plugin/subminer/messages.lua`: explicit script-message trigger wiring.
|
||||
|
||||
Regression coverage updated:
|
||||
|
||||
- `scripts/test-plugin-start-gate.lua` now verifies:
|
||||
- no sync `ps`/`curl` on non-context file load
|
||||
- no AniSkip network lookup on non-context file load
|
||||
- script-message refresh forces async AniSkip lookup
|
||||
|
||||
Validation run:
|
||||
|
||||
- `bun run test:plugin:src` pass.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,76 +0,0 @@
|
||||
---
|
||||
id: TASK-74
|
||||
title: 'Startup warmups: configurable warmup vs defer with low-power mode'
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-27 21:05'
|
||||
updated_date: '2026-03-01 04:14'
|
||||
labels: []
|
||||
dependencies: []
|
||||
references:
|
||||
- src/types.ts
|
||||
- src/config/definitions/defaults-core.ts
|
||||
- src/config/definitions/options-core.ts
|
||||
- src/config/definitions/template-sections.ts
|
||||
- src/config/resolve/core-domains.ts
|
||||
- src/main/runtime/startup-warmups.ts
|
||||
- src/main/runtime/startup-warmups-main-deps.ts
|
||||
- src/main/runtime/composers/mpv-runtime-composer.ts
|
||||
- src/core/services/startup.ts
|
||||
- src/main.ts
|
||||
- src/config/config.test.ts
|
||||
- src/main/runtime/startup-warmups.test.ts
|
||||
- src/main/runtime/startup-warmups-main-deps.test.ts
|
||||
- src/core/services/app-ready.test.ts
|
||||
priority: medium
|
||||
ordinal: 7000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Add startup warmup controls to allow per-integration warmup or deferred first-use loading.
|
||||
|
||||
Scope:
|
||||
|
||||
- New config section `startupWarmups` with toggles for `mecab`, `yomitanExtension`, `subtitleDictionaries`, and `jellyfinRemoteSession`.
|
||||
- New `startupWarmups.lowPowerMode` policy: defer everything except Yomitan extension.
|
||||
- Keep default behavior as full warmup.
|
||||
- Ensure deferred integrations lazy-load on first real usage path.
|
||||
- Add test coverage for config parsing/defaults and warmup scheduling behavior.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Implemented:
|
||||
|
||||
- Added `startupWarmups` to config types/defaults/options/template/resolve.
|
||||
- Warmup scheduler now uses per-integration gating functions.
|
||||
- Low-power mode now defers MeCab, subtitle dictionaries, and Jellyfin remote session warmups while still warming Yomitan extension.
|
||||
- Tokenization path guarantees lazy first-use init for deferred dependencies (Yomitan extension, MeCab when missing, subtitle dictionaries).
|
||||
- Added/updated tests across config and runtime warmup modules.
|
||||
|
||||
Validation:
|
||||
|
||||
- `bun run test:config:src`
|
||||
- `bun run test:core:src`
|
||||
- `tsc --noEmit`
|
||||
|
||||
Follow-up updates:
|
||||
|
||||
- Startup now triggers warmups earlier in app-ready flow (right after config validation/log-level setup) instead of waiting for initial args/overlay actions. Goal: tokenization warmup is already done or mostly done by first visible-subs toggle.
|
||||
- Tokenization warmup scheduling consolidated as `subtitle-tokenization` stage; when enabled by toggles, it runs Yomitan extension first, then MeCab/dictionary warmups.
|
||||
- Added per-stage debug logs for warmup progress and skip reasons:
|
||||
- `stage start/ready: yomitan-extension`
|
||||
- `stage start/ready: mecab`
|
||||
- `stage start/ready: subtitle-dictionaries`
|
||||
- `stage start/ready: jellyfin-remote-session`
|
||||
- `stage skipped: jellyfin-remote-session (disabled|auto-connect off)`
|
||||
- Added regression tests for stage-level logging and earlier startup ordering:
|
||||
- `src/main/runtime/startup-warmups.test.ts`
|
||||
- `src/main/runtime/startup-warmups-main-deps.test.ts`
|
||||
- `src/core/services/app-ready.test.ts`
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
id: TASK-75
|
||||
title: 'Tokenizer: configurable POS exclusions for N+1 and frequency annotations'
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-01 01:23'
|
||||
updated_date: '2026-03-01 04:14'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: medium
|
||||
ordinal: 6000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
N+1 and frequency highlighting should ignore non-learning tokens (e.g., particles/auxiliary forms) based on MeCab POS1 tags, while remaining user-configurable.
|
||||
|
||||
Problem example: for subtitle phrase containing になれば, the highlighted N+1 target should not be the non-useful inflection/token piece when POS indicates an excluded class.
|
||||
|
||||
Implement configurable exclusion defaults with add/remove overrides so users can tune behavior without code changes.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Default exclusion set omits non-useful POS1 classes from both N+1 candidate selection and frequency highlighting.
|
||||
- [x] #2 Users can add extra POS1 exclusions and remove defaults via config.
|
||||
- [x] #3 Tokenizer/annotation tests cover default behavior and config add/remove overrides.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Implemented configurable annotation POS exclusions with defaults+add/remove for both MeCab POS1 and POS2, wired to N+1 candidate selection and frequency highlighting. Added POS2 default exclusion (非自立), expanded POS1 defaults for function words, added Yomitan->MeCab enrichment to carry pos2/pos3 metadata, updated config docs/examples, and added regression tests including になれば case.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,39 +0,0 @@
|
||||
---
|
||||
id: TASK-76
|
||||
title: 'Tokenizer: remove POS exclusion config surface and keep hardcoded defaults'
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-01 02:45'
|
||||
updated_date: '2026-03-01 04:14'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: medium
|
||||
ordinal: 5000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Remove user-facing config keys for annotation POS exclusions. Keep N+1/frequency POS exclusion behavior as built-in defaults with no config required.
|
||||
|
||||
Scope: remove config parsing/registry/docs/example for annotationFilters.pos1Exclusions/pos2Exclusions while preserving runtime filtering behavior.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 No user-facing config option exists for annotation POS exclusions.
|
||||
- [x] #2 Runtime N+1/frequency exclusion behavior remains active via built-in defaults.
|
||||
- [x] #3 Config/docs/example/tests updated accordingly.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Removed user-facing subtitleStyle.annotationFilters POS exclusion configuration (schema/resolver/options/docs/example). POS-based N+1/frequency filtering now always uses built-in defaults in runtime. Preserved robust exclusion behavior including merged-token overlap POS handling and N+1-only MeCab enrichment path.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,53 +0,0 @@
|
||||
---
|
||||
id: TASK-77
|
||||
title: 'Subtitle hover: auto-pause playback with config toggle'
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-28 22:43'
|
||||
updated_date: '2026-03-04 12:07'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: medium
|
||||
ordinal: 8000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Add a user-facing subtitle config option to pause mpv playback when the cursor hovers subtitle text and resume playback when the cursor leaves.
|
||||
|
||||
Scope:
|
||||
|
||||
- New config key: `subtitleStyle.autoPauseVideoOnHover`.
|
||||
- Default should be enabled.
|
||||
- Hover pause/resume must not unpause if playback was already paused before hover.
|
||||
- Docs/examples/tests updated.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 `subtitleStyle.autoPauseVideoOnHover` exists and defaults to `true`.
|
||||
- [x] #2 Overlay pauses playback on subtitle hover and resumes on leave only when hover-triggered pause occurred.
|
||||
- [x] #3 Main/renderer IPC exposes pause-state query for safe hover behavior.
|
||||
- [x] #4 Config docs/examples and user docs/readme mention the new behavior and toggle.
|
||||
- [x] #5 Regression tests cover config parsing/validation and hover behavior edge cases.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Implemented `subtitleStyle.autoPauseVideoOnHover` with default `true`, wired through config defaults/resolution/types, renderer state/style, and mouse hover handlers. Added playback pause-state IPC (`getPlaybackPaused`) to avoid false resume when media was already paused. Added renderer hover behavior tests (including race/cancel case) and config/resolve tests. Updated config examples and docs (`README`, usage, shortcuts, mining workflow, configuration) to document default hover pause/resume behavior and disable path.
|
||||
|
||||
Follow-up adjustments (2026-03-04):
|
||||
|
||||
- Hover pause now resumes immediately when leaving subtitle text (no Yomitan-popup hover retention).
|
||||
- Added `subtitleStyle.autoPauseVideoOnYomitanPopup` (default `false`) to optionally keep playback paused while Yomitan popup is open, with auto-resume on close only when SubMiner initiated the popup pause.
|
||||
- Yomitan popup control keybinds added while popup is open: `J/K` scroll, `M` mine, `P` audio play, `[` previous audio variant, `]` next audio variant (within selected source).
|
||||
- Extension copy drift detection widened so popup runtime changes are reliably re-copied on launch (`popup.js`, `popup-main.js`, `display.js`, `display-audio.js`).
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,55 +0,0 @@
|
||||
---
|
||||
id: TASK-78
|
||||
title: 'Launcher + mpv plugin: auto-start visible overlay pause-until-ready and single-start guard'
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-28 22:45'
|
||||
updated_date: '2026-02-28 22:45'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: medium
|
||||
ordinal: 9000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Add startup gating behavior for wrapper + mpv plugin flow so playback starts paused when visible overlay auto-start is enabled, then auto-resumes only after subtitle tokenization is ready.
|
||||
|
||||
Scope:
|
||||
|
||||
- Plugin option `auto_start_pause_until_ready` (default `yes`).
|
||||
- Launcher reads plugin runtime config and starts mpv paused when `auto_start=yes`, `auto_start_visible_overlay=yes`, and `auto_start_pause_until_ready=yes`.
|
||||
- Main process signals readiness via mpv script message after tokenized subtitle delivery.
|
||||
- Prevent duplicate auto-start attempts from showing `SubMiner already running` OSD.
|
||||
- Keep startup/loading OSD messaging visible and update docs/tests.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Launcher reads `auto_start`, `auto_start_visible_overlay`, and `auto_start_pause_until_ready` from `subminer.conf` and starts mpv with `--pause=yes` when all are enabled.
|
||||
- [x] #2 Plugin pauses on eligible auto-start and resumes only on readiness signal or timeout fallback.
|
||||
- [x] #3 Main process emits `script-message subminer-autoplay-ready` after subtitle tokenization is ready.
|
||||
- [x] #4 Auto-start duplicate triggers are idempotent (no duplicate `--start` behavior and no spurious `Already running` OSD for auto-start path).
|
||||
- [x] #5 Docs and regression tests cover defaults, startup gating behavior, and duplicate-start suppression.
|
||||
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Implemented startup pause gate across launcher/plugin/main runtime:
|
||||
|
||||
- Added plugin runtime config parsing in launcher (`auto_start`, `auto_start_visible_overlay`, `auto_start_pause_until_ready`) and mpv start-paused behavior for eligible runs.
|
||||
- Added plugin auto-play gate state, timeout fallback, and readiness release via `subminer-autoplay-ready` script message.
|
||||
- Added main-process readiness signaling after tokenization delivery, including unpause fallback command path.
|
||||
- Split auto-start visibility control into separate control commands and added duplicate auto-start idempotency guard to suppress repeated auto-start `Already running` noise.
|
||||
- Updated plugin defaults to enabled (`auto_start=yes`, `auto_start_visible_overlay=yes`, `auto_start_pause_until_ready=yes`) and refreshed docs (`README`, usage, launcher, installation, plugin/config docs).
|
||||
- Added/updated regression coverage (`scripts/test-plugin-start-gate.lua`, launcher smoke/unit tests) validating paused startup, readiness resume, and duplicate-start suppression.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,50 +0,0 @@
|
||||
---
|
||||
id: TASK-79
|
||||
title: 'Jimaku modal: auto-close after successful subtitle load'
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-01 13:52'
|
||||
updated_date: '2026-03-01 14:06'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: medium
|
||||
ordinal: 10000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Fix Jimaku modal UX so selecting a subtitle file closes the modal automatically once subtitle download+load succeeds.
|
||||
|
||||
Current behavior:
|
||||
|
||||
- Subtitle file downloads and loads into mpv.
|
||||
- Jimaku modal remains open until manual close.
|
||||
|
||||
Expected behavior:
|
||||
|
||||
- On successful `jimakuDownloadFile` result, close modal immediately.
|
||||
- Keep error behavior unchanged (stay open + show error).
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Successful subtitle file selection/download in Jimaku closes modal automatically.
|
||||
- [x] #2 Existing error path keeps modal open and shows error.
|
||||
- [x] #3 Regression test covers success auto-close behavior.
|
||||
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Fixed renderer Jimaku success flow to close modal immediately after successful `jimakuDownloadFile` result. Added regression test (`src/renderer/modals/jimaku.test.ts`) that reproduces keyboard file-selection success path and asserts modal close state + `notifyOverlayModalClosed('jimaku')` emission. Kept failure path unchanged.
|
||||
|
||||
Also wired new test into `test:core:src` and `test:core:dist` package scripts.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
id: TASK-8
|
||||
title: >-
|
||||
Reduce MpvIpcClient deps interface and separate protocol from application
|
||||
logic
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-11 08:20'
|
||||
updated_date: '2026-02-15 07:00'
|
||||
labels:
|
||||
- refactor
|
||||
- mpv
|
||||
- architecture
|
||||
milestone: Codebase Clarity & Composability
|
||||
dependencies: []
|
||||
references:
|
||||
- src/core/services/mpv-service.ts
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
MpvIpcClient (761 lines) in src/core/services/mpv-service.ts has a 22-property `MpvIpcClientDeps` interface that reaches back into main.ts state for application-level concerns (overlay visibility, subtitle timing, media path updates, OSD display).
|
||||
|
||||
The class mixes two responsibilities:
|
||||
|
||||
1. **IPC Protocol**: Socket connection, JSON message framing, reconnection, property observation
|
||||
2. **Application Integration**: Subtitle text broadcasting, overlay visibility sync, timing tracking
|
||||
|
||||
Separating these would let the protocol layer be simpler and testable, while application-level reactions to mpv events could be handled by listeners/callbacks registered externally.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 MpvIpcClient deps interface reduced to protocol-level concerns only
|
||||
- [ ] #2 Application-level reactions (subtitle broadcast, overlay sync, timing) handled via event emitter or external listeners
|
||||
- [ ] #3 MpvIpcClient is testable without mocking 22 callbacks
|
||||
- [ ] #4 Existing behavior preserved
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Superseded by TASK-27.4 which absorbed this task's full scope (protocol/application separation, deps interface reduction from 22 properties to protocol-level concerns, event-based app reactions). All acceptance criteria met by TASK-27.4.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,50 +0,0 @@
|
||||
---
|
||||
id: TASK-80
|
||||
title: 'Jimaku download: rename subtitle to current video basename'
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-01 14:17'
|
||||
updated_date: '2026-03-01 14:19'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: medium
|
||||
ordinal: 11000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
When user selects a Jimaku subtitle, save subtitle with filename derived from currently playing media filename instead of Jimaku release filename.
|
||||
|
||||
Example:
|
||||
|
||||
- Current media: `anime.mkv`
|
||||
- Downloaded subtitle extension: `.srt`
|
||||
- Saved subtitle path: `anime.ja.srt`
|
||||
|
||||
Scope:
|
||||
|
||||
- Apply in Jimaku download IPC path before writing file.
|
||||
- Preserve collision-avoidance behavior (suffix with jimaku entry id/counter when target exists).
|
||||
- Keep mpv load flow unchanged except using renamed path.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Jimaku subtitle destination name uses current media basename plus `.ja` and subtitle extension.
|
||||
- [x] #2 Existing duplicate filename conflict handling still works.
|
||||
- [x] #3 Regression tests cover renamed destination path behavior.
|
||||
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Jimaku download path generation now derives subtitle filename from currently playing media basename and keeps subtitle extension from Jimaku file (`anime.mkv` + `.srt` => `anime.ja.srt`). Added pure helper `buildJimakuSubtitleFilenameFromMediaPath` and routed IPC download flow through it before existing duplicate-path conflict handling. Added regression tests for local path, missing extension fallback, and remote URL media paths.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,58 +0,0 @@
|
||||
---
|
||||
id: TASK-81
|
||||
title: 'Tokenization performance: disable Yomitan MeCab parser, gate local MeCab init, and add persistent MeCab process'
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-02 07:44'
|
||||
updated_date: '2026-03-02 20:44'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: high
|
||||
ordinal: 9001
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Reduce subtitle annotation latency by:
|
||||
|
||||
- disabling Yomitan-side MeCab parser requests (`useMecabParser=false`);
|
||||
- initializing local MeCab only when POS-dependent annotations are enabled (N+1 / JLPT / frequency);
|
||||
- replacing per-line local MeCab process spawning with a persistent parser process that auto-shuts down after idle time and restarts on demand.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Yomitan parse requests disable MeCab parser path.
|
||||
- [x] #2 MeCab warmup/init is skipped when all POS-dependent annotation toggles are off.
|
||||
- [x] #3 Local MeCab tokenizer uses persistent process across subtitle lines.
|
||||
- [x] #4 Persistent MeCab process auto-shuts down after idle timeout and restarts on next tokenize activity.
|
||||
- [x] #5 Tests cover parser flag, warmup gating, and persistent MeCab lifecycle behavior.
|
||||
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Implemented tokenizer latency optimizations:
|
||||
|
||||
- switched Yomitan parse requests to `useMecabParser: false`;
|
||||
- added annotation-aware MeCab initialization gating in runtime warmup flow;
|
||||
- added persistent local MeCab process (default idle shutdown: 30s) with queued requests, retry-on-process-end, idle auto-shutdown, and automatic restart on new work;
|
||||
- added regression tests for Yomitan parse flag, MeCab warmup gating, and persistent/idle lifecycle behavior;
|
||||
- fixed tokenization warmup gate so first-use warmup completion is sticky (`tokenizationWarmupCompleted`) and sequential `tokenizeSubtitle` calls no longer re-run Yomitan/dictionary warmup path;
|
||||
- added regression coverage in `src/main/runtime/composers/mpv-runtime-composer.test.ts` for sequential tokenize calls (`warmup` side effects run once);
|
||||
- post-review critical fix: treat Yomitan default-profile Anki server sync `no-change` as successful check, so `lastSyncedYomitanAnkiServer` is cached and expensive sync checks do not repeat on every subtitle line;
|
||||
- added regression assertion in `src/core/services/tokenizer/yomitan-parser-runtime.test.ts` for `updated: false` path returning sync success;
|
||||
- post-review performance fix: refactored POS enrichment to pre-index MeCab tokens by surface plus character-position overlap index, replacing repeated active-candidate filtering/full-scan behavior with direct overlap candidate lookup per token;
|
||||
- added regression tests in `src/core/services/tokenizer/parser-enrichment-stage.test.ts` for repeated distant-token scan access and repeated active-candidate filter scans; both fail on scan-based behavior and pass with indexed lookup;
|
||||
- post-review startup fix: moved JLPT/frequency dictionary initialization from synchronous FS APIs to async `fs/promises` path inspection/read and cooperative chunked entry processing to reduce main-thread stall risk during cold start;
|
||||
- post-review first-line latency fix: decoupled tokenization warmup gating so first `tokenizeSubtitle` only waits on Yomitan extension readiness, while MeCab check + dictionary prewarm continue in parallel background warmups;
|
||||
- validated with targeted tests and `tsc --noEmit`.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,60 +0,0 @@
|
||||
---
|
||||
id: TASK-82
|
||||
title: 'Subtitle frequency highlighting: fix noisy Yomitan readings and restore known/N+1 color priority'
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-02 20:10'
|
||||
updated_date: '2026-03-02 01:44'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: high
|
||||
ordinal: 9002
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Address frequency-highlighting regressions:
|
||||
|
||||
- tokens like `断じて` missed rank assignment when Yomitan merged-token reading was truncated/noisy;
|
||||
- known/N+1 tokens were incorrectly colored by frequency color instead of known/N+1 color.
|
||||
|
||||
Expected behavior:
|
||||
|
||||
- known/N+1 color always wins;
|
||||
- if token is frequent and within `topX`, frequency rank label can still appear on hover/metadata.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Frequency lookup succeeds for noisy/truncated merged-token readings via robust fallback behavior.
|
||||
- [x] #2 Merged-token reading normalization restores missing kana suffixes where safe (`headword === surface` path).
|
||||
- [x] #3 Known/N+1 tokens keep known/N+1 color classes; frequency color class does not override them.
|
||||
- [x] #4 Frequency rank hover label remains available for in-range frequent tokens, including known/N+1.
|
||||
- [x] #5 Regression tests added for tokenizer and renderer behavior.
|
||||
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Implemented and validated:
|
||||
|
||||
- tokenizer now normalizes selected Yomitan merged-token readings by appending missing trailing kana suffixes when safe (`headword === surface`);
|
||||
- frequency lookup now does lazy fallback: requests `{term, reading}` first, and only requests `{term, reading: null}` for misses;
|
||||
- this removes eager `(term, null)` payload inflation on medium-frequency lines and reduces extension RPC payload/load;
|
||||
- renderer restored known/N+1 color priority over frequency class coloring;
|
||||
- frequency rank label display remains available for frequent known/N+1 tokens;
|
||||
- added regression tests covering noisy-reading fallback, lazy fallback-query behavior, and renderer class/label precedence.
|
||||
|
||||
Related commits:
|
||||
|
||||
- `17a417e` (`fix(subtitle): improve frequency highlight reliability`)
|
||||
- `79f37f3` (`fix(subtitle): prioritize known and n+1 colors over frequency`)
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,53 +0,0 @@
|
||||
---
|
||||
id: TASK-83
|
||||
title: 'Jellyfin subtitle delay: shift to adjacent cue without seek jumps'
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-02 00:06'
|
||||
updated_date: '2026-03-02 00:06'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: high
|
||||
ordinal: 9003
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Add keybinding-friendly special commands that shift `sub-delay` to align current subtitle start with next/previous cue start, without `sub-seek` probing (avoid playback jump).
|
||||
|
||||
Scope:
|
||||
|
||||
- add special commands for next/previous line alignment;
|
||||
- compute delta from active subtitle cue timeline (external subtitle file/URL, including Jellyfin-delivered URLs);
|
||||
- apply `add sub-delay <delta>` and show OSD value;
|
||||
- keep existing proxy OSD behavior for direct `sub-delay` keybinding commands.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 New special commands exist for subtitle-delay shift to next/previous cue boundary.
|
||||
- [x] #2 Shift logic parses active external subtitle source timings (SRT/VTT/ASS) and computes delta from current `sub-start`.
|
||||
- [x] #3 Runtime applies delay shift without `sub-seek` and shows OSD feedback.
|
||||
- [x] #4 Direct `sub-delay` proxy commands also show OSD current value.
|
||||
- [x] #5 Tests added for cue parsing/shift behavior and IPC dispatch wiring.
|
||||
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Implemented no-jump subtitle-delay alignment commands:
|
||||
|
||||
- added `__sub-delay-next-line` and `__sub-delay-prev-line` special commands;
|
||||
- added `createShiftSubtitleDelayToAdjacentCueHandler` to parse cue start times from active external subtitle source and apply `add sub-delay` delta from current `sub-start`;
|
||||
- wired command handling through IPC runtime deps into main runtime;
|
||||
- retained/extended OSD proxy feedback for `sub-delay` keybindings;
|
||||
- updated configuration docs and added regression tests for subtitle-delay shift and IPC command routing.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,51 +0,0 @@
|
||||
---
|
||||
id: TASK-85
|
||||
title: >-
|
||||
Add launcher dictionary subcommand and initial AniList character dictionary
|
||||
zip generation
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-03 08:47'
|
||||
updated_date: '2026-03-16 05:13'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: high
|
||||
ordinal: 96500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Implement initial character dictionary flow: launcher `dictionary` subcommand, app `--dictionary` command, AniList media resolution from current playback, Yomitan zip generation to local file, and local cache to avoid repeated API fetches for same AniList id. Manual Yomitan import path only in this phase.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Launcher supports `dictionary` (and alias) and forwards to app command path.
|
||||
- [x] #2 App CLI accepts `--dictionary` and dispatches to dictionary runtime command.
|
||||
- [x] #3 Dictionary command resolves current anime to AniList id, generates Yomitan-compatible zip, and logs output path for manual load.
|
||||
- [x] #4 Generated dictionaries are cached by AniList id so repeated commands reuse existing zip when available.
|
||||
- [x] #5 Backlog task is updated with implementation notes and completion summary for this phase.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Implemented launcher `dictionary`/`dict` subcommand parsing and normalized args flow (`launcher/config/cli-parser-builder.ts`, `launcher/config/args-normalizer.ts`, `launcher/types.ts`).
|
||||
|
||||
Added launcher command dispatch (`launcher/commands/dictionary-command.ts`) and wired `launcher/main.ts` to forward `--dictionary` (plus non-default `--log-level`) to app binary.
|
||||
|
||||
Added app CLI flag `--dictionary` in parser/help and startup routing (`src/cli/args.ts`, `src/cli/help.ts`).
|
||||
|
||||
Added dictionary runtime service (`src/main/character-dictionary-runtime.ts`) that resolves AniList media id from current playback guess, fetches AniList character edges, builds Yomitan-compatible banks/index, writes zip, and caches by AniList id in user data.
|
||||
|
||||
Threaded dictionary generation dependency through CLI runtime/context builders and `src/main.ts` context composition so command executes from launcher/app entrypoints.
|
||||
|
||||
Added/updated tests for parser, command modules, launcher main forwarding, CLI command dispatch, and context/deps wiring. Updated docs for launcher/usage command lists to include dictionary subcommand.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Initial phase shipped: `subminer dictionary` now routes to `SubMiner.AppImage --dictionary`, generates a Yomitan-importable character dictionary zip for the current anime (AniList-based), logs zip output path for manual import, and reuses cached zips by AniList id to avoid repeated API fetches.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
id: TASK-85
|
||||
title: 'Remove docs Plausible analytics integration'
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-03 00:00'
|
||||
updated_date: '2026-03-03 00:00'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: medium
|
||||
ordinal: 12001
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Remove Plausible analytics integration from docs theme and dependency graph. Keep docs build/runtime analytics-free.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Docs theme no longer imports or initializes Plausible tracker.
|
||||
- [x] #2 `@plausible-analytics/tracker` removed from dependencies and lockfile.
|
||||
- [x] #3 Docs analytics test reflects absence of Plausible wiring.
|
||||
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Deleted Plausible runtime wiring from VitePress theme, removed tracker package via `bun remove`, and updated docs test to assert no Plausible integration remains.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
id: TASK-9
|
||||
title: Remove trivial wrapper functions from main.ts
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-11 08:21'
|
||||
updated_date: '2026-02-15 07:00'
|
||||
labels:
|
||||
- refactor
|
||||
- main
|
||||
- simplicity
|
||||
milestone: Codebase Clarity & Composability
|
||||
dependencies:
|
||||
- TASK-7
|
||||
references:
|
||||
- src/main.ts
|
||||
priority: low
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
main.ts contains many trivial single-line wrapper functions that add indirection without value:
|
||||
|
||||
```typescript
|
||||
function getOverlayWindows(): BrowserWindow[] {
|
||||
return overlayManager.getOverlayWindows();
|
||||
}
|
||||
function updateOverlayBounds(geometry: WindowGeometry): void {
|
||||
updateOverlayBoundsService(geometry, () => getOverlayWindows());
|
||||
}
|
||||
function ensureOverlayWindowLevel(window: BrowserWindow): void {
|
||||
ensureOverlayWindowLevelService(window);
|
||||
}
|
||||
```
|
||||
|
||||
Similarly, config accessor wrappers like `getJimakuLanguagePreference()`, `getJimakuMaxEntryResults()`, `resolveJimakuApiKey()` are pure boilerplate.
|
||||
|
||||
After TASK-7 (AppState container), many of these can be eliminated by having services access the state container directly, or by using the service functions directly at call sites without local wrappers.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Trivial pass-through wrappers eliminated (call service/manager directly)
|
||||
- [ ] #2 Config accessor wrappers replaced with direct calls or a config accessor helper
|
||||
- [ ] #3 main.ts line count reduced
|
||||
- [ ] #4 No functional changes
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Priority changed from medium to low: this work is largely subsumed by TASK-27.2 (split main.ts). When main.ts is decomposed into composition-root modules, trivial wrappers will naturally be eliminated or inlined. Recommend folding remaining wrapper cleanup into TASK-27.2 rather than tracking separately. Keep this ticket as a checklist reference but don't execute independently.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Subsumed by TASK-27.2 (main.ts split). Trivial wrappers were eliminated or inlined as composition-root modules were extracted. main.ts reduced from ~2000+ LOC to 1384 with state routed through appState container. Standalone wrapper removal pass no longer needed.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,11 +1,11 @@
|
||||
project_name: 'SubMiner'
|
||||
default_status: 'To Do'
|
||||
statuses: ['To Do', 'In Progress', 'Done']
|
||||
project_name: "SubMiner"
|
||||
default_status: "To Do"
|
||||
statuses: ["To Do", "In Progress", "Done"]
|
||||
labels: []
|
||||
definition_of_done: []
|
||||
milestones: []
|
||||
date_format: yyyy-mm-dd
|
||||
max_column_width: 20
|
||||
default_editor: 'nvim'
|
||||
default_editor: "nvim"
|
||||
auto_open_browser: false
|
||||
default_port: 6420
|
||||
remote_operations: true
|
||||
@@ -13,4 +13,4 @@ auto_commit: false
|
||||
bypass_git_hooks: false
|
||||
check_active_branches: true
|
||||
active_branch_days: 30
|
||||
task_prefix: 'task'
|
||||
task_prefix: "task"
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
id: m-0
|
||||
title: 'Codebase Health Remediation'
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Follow-up work from the March 6, 2026 codebase review: strengthen the runnable test gate, remove confirmed dead architecture, and continue decomposition of oversized runtime entrypoints.
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
id: m-0
|
||||
title: 'Release v0.1.0'
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Milestone: Release v0.1.0
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
id: m-1
|
||||
title: "Stats Dashboard"
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Milestone: Stats Dashboard
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
id: TASK-1
|
||||
title: Refactor runtime services per plan.md
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-10 18:46'
|
||||
updated_date: '2026-02-18 04:11'
|
||||
labels: []
|
||||
dependencies: []
|
||||
references:
|
||||
- plan.md
|
||||
ordinal: 2000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Execute the SubMiner refactoring initiative documented in plan.md to reduce thin abstractions, consolidate service boundaries, fix known quality issues, and increase test coverage while preserving current behavior.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Phase-based execution tasks are created and linked under this initiative.
|
||||
- [x] #2 Each phase task includes clear, testable outcomes aligned with plan.md.
|
||||
- [x] #3 Implementation proceeds with build/test verification checkpoints after each completed phase.
|
||||
- [x] #4 Main behavior remains stable for startup, overlay, IPC, CLI, and tokenizer flows throughout refactor.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Created initiative subtasks TASK-1.1 through TASK-1.6 with phase-aligned acceptance criteria and sequential dependencies.
|
||||
|
||||
Completed TASK-1.1 (Phase 1 thin-wrapper removal) with green build/core tests.
|
||||
|
||||
Completed TASK-1.2 (Phase 2 DI adapter consolidation) with successful build and core test verification checkpoint.
|
||||
|
||||
Completed TASK-1.5 (critical behavior tests) with expanded tokenizer/mpv/subsync/CLI coverage and green core test suite.
|
||||
|
||||
Completed TASK-1.6 with documented no-go decision for optional domain-directory reorganization (kept current structure; tests remain green).
|
||||
|
||||
TASK-1.4 remains the only open phase, blocked on interactive desktop smoke checks that cannot be fully validated in this headless environment.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Completed the plan.md refactor initiative across Phases 1-5 and optional Phase 6 decisioning: removed thin wrappers, consolidated DI adapters and related services, fixed targeted runtime correctness issues, expanded critical behavior test coverage, and kept build/core tests green throughout. Final runtime smoke checks (start/toggle/trigger-field-grouping/stop) passed in this headless environment, with known limitation that visual overlay rendering itself was not directly inspectable.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
id: TASK-1.1
|
||||
title: 'Phase 1: Remove thin wrapper runtime services'
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-02-10 18:46'
|
||||
updated_date: '2026-02-18 04:11'
|
||||
labels: []
|
||||
dependencies: []
|
||||
references:
|
||||
- plan.md
|
||||
- src/main.ts
|
||||
- src/core/services/index.ts
|
||||
parent_task_id: TASK-1
|
||||
ordinal: 12000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Inline trivial wrapper services into their call sites and delete redundant service/test files listed in Phase 1 of plan.md.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Wrapper logic from the Phase 1 file list is inlined at call sites without behavior changes.
|
||||
- [x] #2 Phase 1 wrapper service files and corresponding trivial tests are removed from the codebase.
|
||||
- [x] #3 `src/core/services/index.ts` exports are updated to remove deleted modules.
|
||||
- [x] #4 `pnpm run build && pnpm run test:core` passes after Phase 1 completion.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
|
||||
1. Locate all Phase 1 wrapper service call sites and classify direct-inline substitutions vs orchestration-flow inlines.
|
||||
2. Remove the lowest-risk wrappers first (`config-warning-runtime-service`, `app-logging-runtime-service`, `runtime-options-manager-runtime-service`, `overlay-modal-restore-service`, `overlay-send-service`) and update imports/exports.
|
||||
3. Continue with startup and shutdown wrappers (`startup-resource-runtime-service`, `config-generation-runtime-service`, `app-shutdown-runtime-service`, `shortcut-ui-deps-runtime-service`) by inlining behavior into `main.ts` or direct callers.
|
||||
4. Delete corresponding trivial test files for removed wrappers and clean `src/core/services/index.ts` exports.
|
||||
5. Run `pnpm run build && pnpm run test:core`; fix regressions and update task notes/acceptance criteria incrementally.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Inlined wrapper behaviors into direct call sites in `main.ts` and `overlay-bridge-runtime-service.ts` for config warning/app logging, runtime options manager construction, generate-config bootstrap path, startup resource initialization, app shutdown sequence, overlay modal restore handling, overlay send behavior, and overlay shortcut local fallback invocation.
|
||||
|
||||
Deleted 16 Phase 1 files (9 wrapper services + 7 wrapper tests) and removed corresponding barrel exports from `src/core/services/index.ts`.
|
||||
|
||||
Updated `package.json` `test:core` list to remove deleted test entries so the script tracks current sources accurately.
|
||||
|
||||
Verification: `pnpm run build && pnpm run test:core` passes after refactor.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
id: TASK-1.2
|
||||
title: 'Phase 2: Merge DI adapter runtime services into target services'
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-02-10 18:46'
|
||||
updated_date: '2026-02-18 04:11'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-1.1
|
||||
references:
|
||||
- plan.md
|
||||
- src/core/services/cli-command-service.ts
|
||||
- src/core/services/ipc-service.ts
|
||||
- src/core/services/tokenizer-service.ts
|
||||
- src/core/services/app-lifecycle-deps-runtime-service.ts
|
||||
parent_task_id: TASK-1
|
||||
ordinal: 10000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Absorb dependency adapter runtime services into core service modules and remove adapter files/tests while preserving runtime behavior.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 CLI, IPC, tokenizer, and app lifecycle adapter logic is merged into their target service modules.
|
||||
- [x] #2 Adapter service and adapter test files listed in Phase 2 are removed.
|
||||
- [x] #3 Callers pass dependency shapes expected by updated services without redundant mapping layers.
|
||||
- [x] #4 `pnpm run build && pnpm run test:core` passes after Phase 2 completion.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
|
||||
1. Audit `cli-command-deps-runtime-service.ts`, `ipc-deps-runtime-service.ts`, `tokenizer-deps-runtime-service.ts`, and `app-lifecycle-deps-runtime-service.ts` usage sites in `main.ts` and corresponding services.
|
||||
2. For each adapter, move null-guarding and shape-normalization logic into its target service (`cli-command-service.ts`, `ipc-service.ts`, `tokenizer-service.ts`, `app-lifecycle-service.ts`) and simplify caller dependency objects.
|
||||
3. Remove adapter service files/tests and update `src/core/services/index.ts` exports/import sites.
|
||||
4. Run `pnpm run build && pnpm run test:core` and fix any typing/regression issues from the interface consolidation.
|
||||
5. Update task notes with dependency-shape decisions to preserve handoff clarity.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Merged adapter-constructor logic into target services: `createCliCommandDepsRuntimeService` moved into `cli-command-service.ts`, `createIpcDepsRuntimeService` moved into `ipc-service.ts`, `createTokenizerDepsRuntimeService` moved into `tokenizer-service.ts`, and `createAppLifecycleDepsRuntimeService` moved into `app-lifecycle-service.ts`.
|
||||
|
||||
Deleted adapter service files and tests for cli-command deps, ipc deps, tokenizer deps, and app lifecycle deps.
|
||||
|
||||
Updated `src/core/services/index.ts` exports and `package.json` `test:core` entries to remove deleted adapter test modules.
|
||||
|
||||
Verification: `pnpm run build && pnpm run test:core` passes after consolidation.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
id: TASK-1.3
|
||||
title: 'Phase 3: Consolidate related service modules'
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-02-10 18:46'
|
||||
updated_date: '2026-02-18 04:11'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-1.2
|
||||
references:
|
||||
- plan.md
|
||||
- src/core/services/overlay-visibility-service.ts
|
||||
- src/core/services/overlay-manager-service.ts
|
||||
- src/core/services/overlay-shortcut-service.ts
|
||||
- src/core/services/numeric-shortcut-session-service.ts
|
||||
- src/core/services/app-ready-runtime-service.ts
|
||||
parent_task_id: TASK-1
|
||||
ordinal: 6000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Merge split modules for overlay visibility, broadcast, shortcuts, numeric shortcuts, and startup orchestration into cohesive service files.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Overlay visibility/runtime split is consolidated into a single service module.
|
||||
- [x] #2 Overlay broadcast functions are merged with overlay manager responsibilities.
|
||||
- [x] #3 Shortcut and numeric shortcut runtime/lifecycle splits are consolidated as described in plan.md.
|
||||
- [x] #4 Startup bootstrap and app-ready runtime orchestration is consolidated into one startup module.
|
||||
- [x] #5 `pnpm run build && pnpm run test:core` passes after Phase 3 completion.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
|
||||
1. Merge `overlay-visibility-runtime-service.ts` exports into `overlay-visibility-service.ts` and update imports/exports.
|
||||
2. Merge overlay broadcast responsibilities from `overlay-broadcast-runtime-service.ts` into `overlay-manager-service.ts` while preserving current APIs used by `main.ts`.
|
||||
3. Consolidate shortcut modules by absorbing lifecycle utilities into `overlay-shortcut-service.ts` and fallback-runner logic into `overlay-shortcut-runtime-service.ts` (or successor handler module), then remove obsolete files.
|
||||
4. Merge numeric shortcut runtime/session split into a single `numeric-shortcut-service.ts` and update call sites/tests.
|
||||
5. Merge startup bootstrap + app-ready orchestration into a single startup module, update imports, remove obsolete files, and run `pnpm run build && pnpm run test:core`.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Merged overlay visibility runtime API into `overlay-visibility-service.ts` and removed `overlay-visibility-runtime-service.ts`.
|
||||
|
||||
Merged overlay broadcast behavior into `overlay-manager-service.ts` (including manager-level broadcasting) and removed `overlay-broadcast-runtime-service.ts` + test, with equivalent coverage moved into `overlay-manager-service.test.ts`.
|
||||
|
||||
Consolidated shortcut modules into `overlay-shortcut-service.ts` (lifecycle) and new `overlay-shortcut-handler.ts` (runtime handlers + local fallback), removing `overlay-shortcut-lifecycle-service.ts`, `overlay-shortcut-runtime-service.ts`, and `overlay-shortcut-fallback-runner.ts`.
|
||||
|
||||
Merged numeric shortcut runtime/session split into `numeric-shortcut-service.ts`; removed `numeric-shortcut-runtime-service.ts` and merged runtime test coverage into session tests.
|
||||
|
||||
Merged startup bootstrap + app-ready orchestration into `startup-service.ts`; removed `startup-bootstrap-runtime-service.ts` and `app-ready-runtime-service.ts` with tests updated to new module path.
|
||||
|
||||
Verification: `pnpm run build && pnpm run test:core` passes after consolidation.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -0,0 +1,98 @@
|
||||
---
|
||||
id: TASK-1.4
|
||||
title: 'Phase 4: Fix runtime bugs and naming/code-quality issues'
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-02-10 18:46'
|
||||
updated_date: '2026-02-18 04:11'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-1.3
|
||||
references:
|
||||
- plan.md
|
||||
- src/main.ts
|
||||
- src/core/services/overlay-visibility-service.ts
|
||||
- src/core/services/tokenizer-deps-runtime-service.ts
|
||||
parent_task_id: TASK-1
|
||||
ordinal: 3000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Address identified correctness and code-quality issues from plan.md, including race conditions, unsafe typing, callback rejection handling, and runtime naming cleanup.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Debug `console.log`/`console.warn` usage in overlay visibility logic is removed or replaced with structured logging where needed.
|
||||
- [x] #2 Tokenizer type mismatch is fixed without unsafe `as never` casting.
|
||||
- [x] #3 Field grouping resolver handling is made concurrency-safe against overlapping requests.
|
||||
- [x] #4 Async callback wiring in CLI/IPC paths has explicit rejection handling.
|
||||
- [x] #5 Remaining `-runtime-service` naming cleanup is completed without logic regressions.
|
||||
- [x] #6 `pnpm run build && pnpm run test:core` passes and manual startup/overlay smoke checks succeed.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
|
||||
1. Remove or replace debug `console.log`/`console.warn` usage in `overlay-visibility-service.ts` while preserving useful operational logging semantics.
|
||||
2. Confirm and fix unsafe tokenizer casting paths (already partially addressed during Phase 2) and ensure no remaining `as never` escape hatches in tokenizer dependency flows.
|
||||
3. Make field grouping resolver handling in `main.ts` concurrency-safe by adding request sequencing and stale-resolution guards.
|
||||
4. Audit async callback wiring in CLI/IPC integrations and add explicit rejection handling where promises are fire-and-forget.
|
||||
5. Execute `pnpm run build && pnpm run test:core` and document manual smoke-test steps/outcomes.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Removed debug overlay-visibility `console.log`/`console.warn` statements from `overlay-visibility-service.ts`.
|
||||
|
||||
Eliminated unsafe tokenizer cast path during prior consolidation (`createTokenizerDepsRuntimeService` now uses typed `Token[]` and `mergeTokens(rawTokens)` without `as never`).
|
||||
|
||||
Added field-grouping overlap protection: `createFieldGroupingCallbackService` now cancels immediately when another resolver is active and only clears resolver state if the current resolver matches, preventing stale timeout/request cleanup from clobbering a newer resolver.
|
||||
|
||||
Added explicit rejection handling for async callback pathways: `shell.openExternal` now has `.catch(...)`; app lifecycle `whenReady` path now catches handler rejection; second-instance CLI dispatch is wrapped in try/catch logging.
|
||||
|
||||
Verification: `pnpm run build && pnpm run test:core` passes after these fixes.
|
||||
|
||||
Remaining in TASK-1.4: criterion #5 (`-runtime-service` naming cleanup batch) and criterion #6 manual smoke checks.
|
||||
|
||||
Completed `-runtime-service` naming cleanup for remaining modules by renaming files/tests and import paths, including: `overlay-bridge`, `field-grouping-overlay`, `mpv-control`, `runtime-options-ipc`, `mining`, `jimaku`, `anki-jimaku`, startup/app-ready test names, and subsync wrapper (`subsync-runner-service.ts`).
|
||||
|
||||
Resolved rename collision with existing `subsync-service.ts` by restoring original core subsync service from `HEAD` and moving runtime wrapper logic into `subsync-runner-service.ts`.
|
||||
|
||||
Verification after rename cleanup: `pnpm run build && pnpm run test:core` passes with updated test paths in `package.json`.
|
||||
|
||||
Manual smoke checks are still pending for criterion #6.
|
||||
|
||||
Smoke run attempt 1 (outside sandbox): `timeout 20s pnpm run start` started successfully, loaded config, initialized websocket/Mecab, and entered normal MPV reconnect loop when `/tmp/subminer-socket` was absent; no immediate startup crash after previous refactors.
|
||||
|
||||
Smoke run attempt 2 (outside sandbox): `timeout 20s pnpm exec electron . --start --auto-start-overlay` showed the same stable startup/reconnect behavior, but overlay activation could not be verified in this headless/non-interactive environment.
|
||||
|
||||
Manual GUI interactions (overlay render/toggle, mine card flow, field-grouping interaction) remain pending for a real desktop session with MPV running.
|
||||
|
||||
Automated interactive-smoke surrogate 1 (outside sandbox): started app, sent `--toggle`, then `--stop`; instance remained stable and cleanly stopped without crash.
|
||||
|
||||
Automated interactive-smoke surrogate 2 (outside sandbox): started app, sent `--trigger-field-grouping`, then `--stop`; command path executed without runtime crash and app shut down cleanly.
|
||||
|
||||
Observed expected reconnect behavior when MPV socket was absent (`ENOENT /tmp/subminer-socket`), with no regressions in startup/bootstrap flow.
|
||||
|
||||
Note: this environment is headless, so visual overlay rendering cannot be directly confirmed; command-path and process-lifecycle smoke checks passed.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Completed Phase 4 by removing debug logging noise, fixing unsafe typing and concurrency risks, adding async rejection handling, completing naming cleanup, and validating startup/command-path behavior through repeated build/test and live Electron smoke runs.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
id: TASK-1.5
|
||||
title: 'Phase 5: Add critical behavior tests for untested services'
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-02-10 18:46'
|
||||
updated_date: '2026-02-18 04:11'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-1.4
|
||||
references:
|
||||
- plan.md
|
||||
- src/core/services/mpv-runtime-service.ts
|
||||
- src/core/services/subsync-runtime-service.ts
|
||||
- src/core/services/tokenizer-service.ts
|
||||
- src/core/services/cli-command-service.ts
|
||||
parent_task_id: TASK-1
|
||||
ordinal: 5000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Add meaningful behavior tests for high-risk services called out in plan.md: mpv, subsync, tokenizer, and expanded CLI command coverage.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 `mpv` service has focused tests for protocol parsing, event dispatch, request/response matching, reconnection, and subtitle extraction behavior.
|
||||
- [x] #2 `subsync` service has focused tests for engine path resolution, command construction, timeout/error handling, and result parsing.
|
||||
- [x] #3 `tokenizer` service has focused tests for parser readiness, token extraction, fallback behavior, and edge-case inputs.
|
||||
- [x] #4 CLI command service tests cover all dispatch paths, async error propagation, and second-instance forwarding behavior.
|
||||
- [x] #5 `pnpm run test:core` passes with all new tests green.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
|
||||
1. Add focused tests for `tokenizer-service.ts` behavior (normalization, Yomitan-unavailable fallback, mecab fallback success/error paths, empty input handling).
|
||||
2. Add focused tests for `subsync-service.ts` command/engine selection and failure handling using mocked command utilities where feasible.
|
||||
3. Add focused tests for `mpv-service.ts` protocol handling (line parsing, request-response routing, property-change dispatch) with lightweight socket stubs.
|
||||
4. Expand `cli-command-service.ts` tests for dispatch/error/second-instance forwarding edge paths not currently covered.
|
||||
5. Run `pnpm run test:core` iteratively and update acceptance criteria as each service reaches meaningful coverage.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Added new tokenizer behavior tests in `src/core/services/tokenizer-service.test.ts` covering empty normalized input, newline normalization, mecab fallback success, and mecab error fallback-to-null.
|
||||
|
||||
Added new mpv protocol tests in `src/core/services/mpv-service.test.ts` covering JSON line-buffer parsing, property-change subtitle dispatch behavior, and request/response resolution by `request_id`.
|
||||
|
||||
Added new subsync workflow tests in `src/core/services/subsync-service.test.ts` covering already-running guard, manual-mode picker flow, and error propagation to OSD when MPV is unavailable.
|
||||
|
||||
Expanded `src/core/services/cli-command-service.test.ts` to cover socket/start dispatch, texthooker port override warning path, help-without-window shutdown, and async trigger-subsync error reporting.
|
||||
|
||||
Updated `package.json` `test:core` to include new/renamed test files; verification remains green with `pnpm run test:core` (17 tests total).
|
||||
|
||||
Expanded `mpv-service` tests with request rejection when disconnected, `requestProperty` error propagation, and pending-request disconnect resolution behavior.
|
||||
|
||||
Expanded `subsync-service` tests with manual alass source-track validation and auto-mode executable-path failure handling while ensuring in-progress state cleanup.
|
||||
|
||||
All updated tests remain green via `pnpm run test:core` after these additions.
|
||||
|
||||
Added Yomitan parser token-extraction coverage in `tokenizer-service.test.ts` (parser-available success path) in addition to fallback/edge-case tests.
|
||||
|
||||
Added MPV reconnection/request robustness tests (`scheduleReconnect`, disconnected request rejection, pending request disconnect resolution) to complement protocol/event/request-id tests in `mpv-service.test.ts`.
|
||||
|
||||
Added subsync command-construction tests using executable stubs for both engines (`ffsubsync` and `alass`) and validated success/failure result behavior; added timeout behavior coverage in `src/subsync/utils.test.ts` for child-process timeout handling used by subsync.
|
||||
|
||||
Expanded CLI dispatch tests with broad branch coverage for visibility/settings/copy/multi-copy/mining/open-runtime-options/stop/help/second-instance behaviors and async error propagation.
|
||||
|
||||
Verification: `pnpm run test:core` passes with 18 green tests including newly added `dist/subsync/utils.test.js`.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
id: TASK-1.6
|
||||
title: 'Phase 6 (Optional): Reorganize services by domain directories'
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-10 18:46'
|
||||
updated_date: '2026-02-18 04:11'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-1.5
|
||||
references:
|
||||
- plan.md
|
||||
parent_task_id: TASK-1
|
||||
ordinal: 4000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
If service flattening remains hard to navigate after Phases 1-5, optionally move modules into domain-based folders and update imports.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 A clear go/no-go decision for domain restructuring is documented based on post-phase-5 codebase state.
|
||||
- [ ] #2 If executed, service modules are reorganized into domain folders with no import or runtime breakage.
|
||||
- [x] #3 Build and core test commands pass after any directory reorganization.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
|
||||
1. Assess post-phase-5 directory complexity and determine whether domain reorganization is still justified.
|
||||
2. If complexity remains acceptable, record a no-go decision and keep current structure stable.
|
||||
3. If complexity is still problematic, perform import-safe domain reorganization and re-run build/tests.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Decision: no-go on Phase 6 directory reorganization for now. After Phases 1-5, service/module consolidation and test expansion have improved maintainability without introducing a high-risk import churn.
|
||||
|
||||
Rationale: preserving path stability now reduces regression risk while Phase 4 smoke validation remains open and large refactor commits are still stabilizing.
|
||||
|
||||
Verification baseline remains green (`pnpm run test:core`) with current structure.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -1,42 +0,0 @@
|
||||
---
|
||||
id: TASK-100
|
||||
title: Add configurable texthooker startup launch
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-06 23:30'
|
||||
updated_date: '2026-03-16 05:13'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: medium
|
||||
ordinal: 11010
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Add a config option under `texthooker` to launch the built-in texthooker server automatically when SubMiner starts.
|
||||
|
||||
Scope:
|
||||
|
||||
- Add `texthooker.launchAtStartup`.
|
||||
- Default to `true`.
|
||||
- Start the existing texthooker server during normal app startup when enabled.
|
||||
- Keep `texthooker.openBrowser` as separate behavior.
|
||||
- Add regression coverage and update generated config docs/example.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Default config enables automatic texthooker startup.
|
||||
- [x] #2 Config parser accepts valid boolean values and warns on invalid values.
|
||||
- [x] #3 App-ready startup launches texthooker when enabled.
|
||||
- [x] #4 Generated config template/example documents the new option.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Added `texthooker.launchAtStartup` with a default of `true`, wired it through config defaults/validation/template generation, and started the existing texthooker server during app-ready startup without coupling it to browser auto-open behavior.
|
||||
|
||||
Also added regression coverage for config parsing/template output and app-ready dependency wiring, then regenerated the checked-in config example artifacts.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
id: TASK-100
|
||||
title: Run post-refactor dead code prune and cleanup
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-21 07:15'
|
||||
updated_date: '2026-02-22 07:49'
|
||||
labels:
|
||||
- cleanup
|
||||
- maintainability
|
||||
- refactor
|
||||
dependencies:
|
||||
- TASK-96
|
||||
- TASK-97
|
||||
priority: medium
|
||||
ordinal: 70000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Major refactors likely left unused exports/helpers and stale compatibility code. Perform a deliberate dead-code sweep with regression safety.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Action Steps
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Run unused-export scans (`ts-prune` or equivalent) and collect candidate list.
|
||||
2. Manually verify each candidate to avoid false positives from dynamic loading/IPC wiring.
|
||||
3. Remove or inline confirmed dead code; simplify import surfaces/barrels accordingly.
|
||||
4. Delete stale helper paths retained only for pre-composer wiring.
|
||||
5. Add or update regression tests where removal could alter behavior.
|
||||
6. Run verification gate: `bun run build`, `bun run test:config:dist`, `bun run test:core:dist`.
|
||||
7. Publish cleanup report: removed files/exports, kept exceptions, and rationale.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Confirmed dead code removed without behavior regressions.
|
||||
- [x] #2 Remaining flagged candidates either resolved or documented with justification.
|
||||
- [x] #3 Build and core/config suites pass after cleanup.
|
||||
- [x] #4 Import graph complexity reduced (fewer exports/entrypoints where applicable).
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Added dead-code execution report: `docs/reports/2026-02-22-task-100-dead-code-report.md` (baseline + triage + removals + remaining candidates).
|
||||
|
||||
Confirmed removals include unused imports/helpers in Anki/core/renderer paths plus reduced registry/barrel export surface in `src/tokenizers/index.ts`, `src/token-mergers/index.ts`, and `src/core/utils/index.ts`.
|
||||
|
||||
Verification gates: `bun run build` PASS; `bun run test:core:src` PASS (225 pass/6 skip); `bun run test:config:src` PASS (52 pass); `bun run check:file-budgets` PASS warning mode with no strict hotspot violations.
|
||||
|
||||
Static-analysis evidence: `tsc --noEmit --noUnusedLocals --noUnusedParameters` reduced to 39 remaining diagnostics concentrated in `src/main.ts` and intentional composer type-test aliases; broad `ts-prune` false positives documented in report.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Completed post-refactor dead-code cleanup with a documented triage report, removing confirmed unused imports/helpers/exports while preserving intentional contract/type seams. Verified no regressions via build + core/config source test suites and maintainability budget checks, with remaining candidate hotspots documented for a dedicated follow-up pass.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
|
||||
## Definition of Done
|
||||
<!-- DOD:BEGIN -->
|
||||
- [x] #1 Dead-code report attached in task notes.
|
||||
- [x] #2 Regression test updates included for risky removals.
|
||||
- [x] #3 Verification gate commands complete successfully.
|
||||
<!-- DOD:END -->
|
||||