Compare commits
174 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
10463e7348
|
|||
|
e9abbd5f05
|
|||
|
d6ff50455a
|
|||
| b1bdeabca8 | |||
| da3c971ee6 | |||
|
c02edc90cc
|
|||
|
4d1a20d69b
|
|||
| 7e86c4ea3d | |||
| c4f99fec2f | |||
|
c6328eef09
|
|||
| dc52bc2fba | |||
| a54f03f0cd | |||
| 799cce6991 | |||
| 6b2cb002ac | |||
| e84674e3b5 | |||
|
6ca5cede3e
|
|||
|
4d010e6a18
|
|||
| 5250ca8214 | |||
| 49f89e6452 | |||
|
89723e2ccb
|
|||
|
d05e2bd8ec
|
|||
|
7484d3c102
|
|||
|
f78a875ba3
|
|||
|
a025652542
|
|||
| 91a01b86a9 | |||
| 105713361e | |||
| 4cb0dbfaad | |||
|
801cdcafca
|
|||
|
094bcce0dc
|
|||
|
d1ec678d7a
|
|||
|
f0324cd93a
|
|||
|
1b2ee03678
|
|||
|
18e61d2048
|
|||
|
f0a11c2c99
|
|||
|
bc8d1edf6f
|
|||
| d48d880ba3 | |||
| 7c9b65db8b | |||
| e5c1135501 | |||
| 430373f010 | |||
|
b68d17614d
|
|||
| 30712738dc | |||
| 0915b23dc8 | |||
| db30c61327 | |||
|
27f5b2bb58
|
|||
|
baabdb6d30
|
|||
|
3a67e23bc3
|
|||
|
13e2b5f8c8
|
|||
| 53aa58d044 | |||
| d8934647a9 | |||
|
7ac51cd5e9
|
|||
| 52bab1d611 | |||
|
49e46e6b9b
|
|||
|
c1c40c8d40
|
|||
|
c71482cb44
|
|||
| 05cf4a6fe5 | |||
|
9b4de93283
|
|||
|
16ffbbc4b3
|
|||
|
de4f3efa30
|
|||
| bc7dde3b02 | |||
|
7a64488ed5
|
|||
| 5f3c3871d3 | |||
| 4d24e22bb5 | |||
| c47cfb52af | |||
|
da0087bba6
|
|||
|
8338f27794
|
|||
|
b029d65c90
|
|||
|
c24f99899b
|
|||
|
3aca581764
|
|||
|
ba540d09b2
|
|||
|
6530d2ccbc
|
|||
|
a784091ecb
|
|||
| 61c3e1e3c6 | |||
|
ce76a75630
|
|||
|
52249db5b4
|
|||
|
09d8b52fbf
|
|||
|
0edd566904
|
|||
|
6eb1b0f197
|
|||
|
e4137d9760
|
|||
|
864f4124ae
|
|||
| 7514985feb | |||
| d6c72806bb | |||
|
3502cdc607
|
|||
| d51e7fe401 | |||
|
f9a4039ad2
|
|||
|
8e5c21b443
|
|||
|
55b350c3a2
|
|||
|
54324df3be
|
|||
| 35adf8299c | |||
|
2d4f2d1139
|
|||
|
77e632276b
|
|||
|
4c95b57885
|
|||
|
242402b253
|
|||
|
61d15f9431
|
|||
|
508864bcbb
|
|||
|
23c54bb01e
|
|||
|
ec667c64e8
|
|||
|
39b2ccad8e
|
|||
|
23815945bf
|
|||
|
9dca83acd9
|
|||
|
55300e2d8c
|
|||
|
28afd15134
|
|||
|
58304757aa
|
|||
|
c95518e94a
|
|||
| 5ee4617607 | |||
|
842008b089
|
|||
|
6f56a0bcf6
|
|||
| 5feed360ca | |||
| c17f0a4080 | |||
| 0317c7f011 | |||
|
13797b5005
|
|||
|
b24d9d7487
|
|||
| 3a01cffc6b | |||
|
eddf6f0456
|
|||
|
f6c024d61e
|
|||
| 6749ff843c | |||
|
42abdd1268
|
|||
|
5d914b1547
|
|||
| 50b45cac0b | |||
|
e35aac6ee0
|
|||
|
fe2da22d29
|
|||
|
2045ffbff8
|
|||
| 478869ff28 | |||
| 9eed37420e | |||
| 99f4d2baaf | |||
|
f4e8c3feec
|
|||
|
d0b308f340
|
|||
| 1b56360a24 | |||
|
68833c76c4
|
|||
| 4d7c80f2e4 | |||
|
2f17859b7b
|
|||
|
9c7e02cbf0
|
|||
|
5f320edab5
|
|||
|
f7ce3371a1
|
|||
|
71805dccd7
|
|||
|
a13b7352d7
|
|||
|
ff5ecdaded
|
|||
|
b32c3cf58c
|
|||
|
b791262860
|
|||
|
29b8c6e02a
|
|||
|
618727d8e8
|
|||
|
fed60c265d
|
|||
|
a34a7489db
|
|||
|
e59192bbe1
|
|||
|
e0f82d28f0
|
|||
|
a0521aeeaf
|
|||
|
2127f759ca
|
|||
|
5e787183d0
|
|||
|
81ca31b899
|
|||
|
e2a7597b4f
|
|||
|
2e59c21078
|
|||
|
7b5ab3294d
|
|||
|
2bbf38f987
|
|||
|
f09c91494d
|
|||
|
58ec9b76e0
|
|||
|
7a196f69d6
|
|||
| c799a8de3c | |||
|
34d2dce8dc
|
|||
|
3a22a97761
|
|||
|
962243e959
|
|||
|
021010a338
|
|||
|
4c0575afe0
|
|||
|
9e46176519
|
|||
|
f10e905dbd
|
|||
| e4aa8ff907 | |||
|
a6ece5388a
|
|||
|
6a44b54b51
|
|||
|
93cd688625
|
|||
|
8e319a417d
|
|||
|
38034db1e4
|
|||
|
f775f90360
|
|||
|
55dff6ced7
|
|||
|
d0c11d347b
|
|||
|
f0418c6e56
|
|||
| e18985fb14 |
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "subminer-local",
|
||||||
|
"interface": {
|
||||||
|
"displayName": "SubMiner Local"
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "subminer-workflow",
|
||||||
|
"source": {
|
||||||
|
"source": "local",
|
||||||
|
"path": "./plugins/subminer-workflow"
|
||||||
|
},
|
||||||
|
"policy": {
|
||||||
|
"installation": "AVAILABLE",
|
||||||
|
"authentication": "ON_INSTALL"
|
||||||
|
},
|
||||||
|
"category": "Productivity"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
name: 'subminer-change-verification'
|
||||||
|
description: 'Compatibility shim. Canonical SubMiner change verification workflow now lives in the repo-local subminer-workflow plugin.'
|
||||||
|
---
|
||||||
|
|
||||||
|
# Compatibility Shim
|
||||||
|
|
||||||
|
Canonical source:
|
||||||
|
|
||||||
|
- `plugins/subminer-workflow/skills/subminer-change-verification/SKILL.md`
|
||||||
|
|
||||||
|
Canonical helper scripts:
|
||||||
|
|
||||||
|
- `plugins/subminer-workflow/skills/subminer-change-verification/scripts/classify_subminer_diff.sh`
|
||||||
|
- `plugins/subminer-workflow/skills/subminer-change-verification/scripts/verify_subminer_change.sh`
|
||||||
|
|
||||||
|
When this shim is invoked:
|
||||||
|
|
||||||
|
1. Read the canonical plugin-owned skill.
|
||||||
|
2. Follow the plugin-owned skill as the source of truth.
|
||||||
|
3. Use the wrapper scripts in this shim directory only for compatibility with existing commands and docs.
|
||||||
|
4. Do not duplicate workflow changes here; update the plugin-owned skill and scripts instead.
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
REPO_ROOT=$(cd "$SCRIPT_DIR/../../../.." && pwd)
|
||||||
|
TARGET="$REPO_ROOT/plugins/subminer-workflow/skills/subminer-change-verification/scripts/classify_subminer_diff.sh"
|
||||||
|
|
||||||
|
if [[ ! -x "$TARGET" ]]; then
|
||||||
|
echo "Missing canonical script: $TARGET" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$TARGET" "$@"
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
REPO_ROOT=$(cd "$SCRIPT_DIR/../../../.." && pwd)
|
||||||
|
TARGET="$REPO_ROOT/plugins/subminer-workflow/skills/subminer-change-verification/scripts/verify_subminer_change.sh"
|
||||||
|
|
||||||
|
if [[ ! -x "$TARGET" ]]; then
|
||||||
|
echo "Missing canonical script: $TARGET" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$TARGET" "$@"
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
name: 'subminer-scrum-master'
|
||||||
|
description: 'Compatibility shim. Canonical SubMiner scrum-master workflow now lives in the repo-local subminer-workflow plugin.'
|
||||||
|
---
|
||||||
|
|
||||||
|
# Compatibility Shim
|
||||||
|
|
||||||
|
Canonical source:
|
||||||
|
|
||||||
|
- `plugins/subminer-workflow/skills/subminer-scrum-master/SKILL.md`
|
||||||
|
|
||||||
|
When this shim is invoked:
|
||||||
|
|
||||||
|
1. Read the canonical plugin-owned skill.
|
||||||
|
2. Follow the plugin-owned skill as the source of truth.
|
||||||
|
3. Do not duplicate workflow changes here; update the plugin-owned skill instead.
|
||||||
|
|
||||||
|
This shim exists so existing repo references and prompts keep resolving during the migration to the repo-local plugin workflow.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] Added a changelog fragment in `changes/`, or this PR is labeled `skip-changelog`
|
||||||
@@ -13,6 +13,7 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- name: Setup Bun
|
- name: Setup Bun
|
||||||
@@ -26,20 +27,50 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
~/.bun/install/cache
|
~/.bun/install/cache
|
||||||
node_modules
|
node_modules
|
||||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
|
stats/node_modules
|
||||||
|
vendor/subminer-yomitan/node_modules
|
||||||
|
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/subminer-yomitan/package-lock.json') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-bun-
|
${{ runner.os }}-bun-
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: bun install --frozen-lockfile
|
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, ',') }}"
|
||||||
|
|
||||||
- name: Build (TypeScript check)
|
- name: Build (TypeScript check)
|
||||||
# Keep explicit typecheck for fast fail before full build/bundle.
|
# Keep explicit typecheck for fast fail before full build/bundle.
|
||||||
run: bun run tsc --noEmit
|
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
|
||||||
|
|
||||||
- name: Test suite (source)
|
- name: Test suite (source)
|
||||||
run: bun run test:fast
|
run: bun run test:fast
|
||||||
|
|
||||||
|
- name: Coverage suite (maintained source lane)
|
||||||
|
run: bun run test:coverage:src
|
||||||
|
|
||||||
|
- name: Upload coverage artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: coverage-test-src
|
||||||
|
path: coverage/test-src/lcov.info
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Launcher smoke suite (source)
|
- name: Launcher smoke suite (source)
|
||||||
run: bun run test:launcher:smoke:src
|
run: bun run test:launcher:smoke:src
|
||||||
|
|
||||||
@@ -54,12 +85,12 @@ jobs:
|
|||||||
- name: Build (bundle)
|
- name: Build (bundle)
|
||||||
run: bun run build
|
run: bun run build
|
||||||
|
|
||||||
|
- name: Immersion SQLite verification
|
||||||
|
run: bun run test:immersion:sqlite:dist
|
||||||
|
|
||||||
- name: Dist smoke suite
|
- name: Dist smoke suite
|
||||||
run: bun run test:smoke:dist
|
run: bun run test:smoke:dist
|
||||||
|
|
||||||
- name: Build docs
|
|
||||||
run: bun run docs:build
|
|
||||||
|
|
||||||
- name: Security audit
|
- name: Security audit
|
||||||
run: bun audit --audit-level high
|
run: bun audit --audit-level high
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
name: Docs Pages
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
paths:
|
||||||
|
- 'docs-site/**'
|
||||||
|
- 'scripts/docs-versioning.ts'
|
||||||
|
- 'scripts/build-versioned-docs.ts'
|
||||||
|
- '.github/workflows/docs-pages.yml'
|
||||||
|
- 'package.json'
|
||||||
|
- 'bun.lock'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: docs-pages-production
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
if: ${{ github.ref_type != 'tag' || !contains(github.ref_name, '-') }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Guard stable docs tag shape
|
||||||
|
id: tag_guard
|
||||||
|
if: github.ref_type == 'tag'
|
||||||
|
run: |
|
||||||
|
if [[ ! "${{ github.ref_name }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||||
|
echo "::notice::Skipping non-stable docs tag ${{ github.ref_name }}"
|
||||||
|
echo "stable_tag=false" >> "$GITHUB_OUTPUT"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "stable_tag=true" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Setup Bun
|
||||||
|
if: steps.tag_guard.outputs.stable_tag != 'false'
|
||||||
|
uses: oven-sh/setup-bun@v2
|
||||||
|
with:
|
||||||
|
bun-version: 1.3.5
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
if: steps.tag_guard.outputs.stable_tag != 'false'
|
||||||
|
run: |
|
||||||
|
bun install --frozen-lockfile
|
||||||
|
cd docs-site && bun install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Cache versioned docs archives
|
||||||
|
if: steps.tag_guard.outputs.stable_tag != 'false'
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: .tmp/docs-versioned-archive-cache
|
||||||
|
key: docs-versioned-archives-${{ runner.os }}-${{ hashFiles('docs-site/.vitepress/**', 'docs-site/public/assets/fonts/**', 'docs-site/package.json', 'docs-site/bun.lock', 'scripts/build-versioned-docs.ts', 'scripts/docs-versioning.ts') }}
|
||||||
|
|
||||||
|
- name: Test docs
|
||||||
|
if: steps.tag_guard.outputs.stable_tag != 'false'
|
||||||
|
run: bun run docs:test
|
||||||
|
|
||||||
|
- name: Build versioned docs
|
||||||
|
if: steps.tag_guard.outputs.stable_tag != 'false'
|
||||||
|
run: bun run docs:build:versioned
|
||||||
|
|
||||||
|
- name: Deploy docs to Cloudflare Pages
|
||||||
|
if: steps.tag_guard.outputs.stable_tag != 'false'
|
||||||
|
uses: cloudflare/wrangler-action@v3
|
||||||
|
with:
|
||||||
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
command: pages deploy .tmp/docs-versioned-site --project-name "${{ vars.CLOUDFLARE_PAGES_PROJECT_NAME }}" --branch main
|
||||||
@@ -0,0 +1,426 @@
|
|||||||
|
name: Prerelease
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*-beta.*'
|
||||||
|
- 'v*-rc.*'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: prerelease-${{ github.ref }}
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
quality-gate:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Setup Bun
|
||||||
|
uses: oven-sh/setup-bun@v2
|
||||||
|
with:
|
||||||
|
bun-version: 1.3.5
|
||||||
|
|
||||||
|
- name: Cache dependencies
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.bun/install/cache
|
||||||
|
node_modules
|
||||||
|
stats/node_modules
|
||||||
|
vendor/subminer-yomitan/node_modules
|
||||||
|
key: ${{ runner.os }}-${{ runner.arch }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/subminer-yomitan/package-lock.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-${{ runner.arch }}-bun-
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
bun install --frozen-lockfile
|
||||||
|
cd stats && bun install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Lint stats (formatting)
|
||||||
|
run: bun run lint:stats
|
||||||
|
|
||||||
|
- name: Build (TypeScript check)
|
||||||
|
run: bun run typecheck
|
||||||
|
|
||||||
|
- name: Install Lua
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y lua5.4
|
||||||
|
sudo ln -sf /usr/bin/lua5.4 /usr/local/bin/lua
|
||||||
|
lua -v
|
||||||
|
|
||||||
|
- name: Test suite (source)
|
||||||
|
run: bun run test:fast
|
||||||
|
|
||||||
|
- name: Environment suite
|
||||||
|
run: bun run test:env
|
||||||
|
|
||||||
|
- name: Coverage suite (maintained source lane)
|
||||||
|
run: bun run test:coverage:src
|
||||||
|
|
||||||
|
- name: Upload coverage artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: coverage-test-src
|
||||||
|
path: coverage/test-src/lcov.info
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
- name: Launcher smoke suite (source)
|
||||||
|
run: bun run test:launcher:smoke:src
|
||||||
|
|
||||||
|
- name: Upload launcher smoke artifacts (on failure)
|
||||||
|
if: failure()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: launcher-smoke
|
||||||
|
path: .tmp/launcher-smoke/**
|
||||||
|
if-no-files-found: ignore
|
||||||
|
|
||||||
|
- name: Build (bundle)
|
||||||
|
run: bun run build
|
||||||
|
|
||||||
|
- name: Immersion SQLite verification
|
||||||
|
run: bun run test:immersion:sqlite:dist
|
||||||
|
|
||||||
|
- name: Dist smoke suite
|
||||||
|
run: bun run test:smoke:dist
|
||||||
|
|
||||||
|
build-linux:
|
||||||
|
needs: [quality-gate]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Setup Bun
|
||||||
|
uses: oven-sh/setup-bun@v2
|
||||||
|
with:
|
||||||
|
bun-version: 1.3.5
|
||||||
|
|
||||||
|
- name: Cache dependencies
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.bun/install/cache
|
||||||
|
node_modules
|
||||||
|
stats/node_modules
|
||||||
|
vendor/texthooker-ui/node_modules
|
||||||
|
vendor/subminer-yomitan/node_modules
|
||||||
|
key: ${{ runner.os }}-${{ runner.arch }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/texthooker-ui/package.json', 'vendor/subminer-yomitan/package-lock.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-${{ runner.arch }}-bun-
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
bun install --frozen-lockfile
|
||||||
|
cd stats && bun install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Build texthooker-ui
|
||||||
|
run: |
|
||||||
|
cd vendor/texthooker-ui
|
||||||
|
bun install
|
||||||
|
bun run build
|
||||||
|
|
||||||
|
- name: Build AppImage
|
||||||
|
run: bun run build:appimage
|
||||||
|
|
||||||
|
- name: Build unversioned AppImage
|
||||||
|
run: |
|
||||||
|
shopt -s nullglob
|
||||||
|
appimages=(release/SubMiner-*.AppImage)
|
||||||
|
if [ "${#appimages[@]}" -eq 0 ]; then
|
||||||
|
echo "No versioned AppImage found to create unversioned artifact."
|
||||||
|
ls -la release
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
cp "${appimages[0]}" release/SubMiner.AppImage
|
||||||
|
|
||||||
|
- name: Upload AppImage artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: appimage
|
||||||
|
path: |
|
||||||
|
release/*.AppImage
|
||||||
|
release/*.yml
|
||||||
|
release/*.blockmap
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
build-macos:
|
||||||
|
needs: [quality-gate]
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Setup Bun
|
||||||
|
uses: oven-sh/setup-bun@v2
|
||||||
|
with:
|
||||||
|
bun-version: 1.3.5
|
||||||
|
|
||||||
|
- name: Cache dependencies
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.bun/install/cache
|
||||||
|
node_modules
|
||||||
|
stats/node_modules
|
||||||
|
vendor/texthooker-ui/node_modules
|
||||||
|
vendor/subminer-yomitan/node_modules
|
||||||
|
key: ${{ runner.os }}-${{ runner.arch }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/texthooker-ui/package.json', 'vendor/subminer-yomitan/package-lock.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-${{ runner.arch }}-bun-
|
||||||
|
|
||||||
|
- name: Validate macOS signing/notarization secrets
|
||||||
|
run: |
|
||||||
|
missing=0
|
||||||
|
for name in CSC_LINK CSC_KEY_PASSWORD APPLE_ID APPLE_APP_SPECIFIC_PASSWORD APPLE_TEAM_ID; do
|
||||||
|
if [ -z "${!name}" ]; then
|
||||||
|
echo "Missing required secret: $name"
|
||||||
|
missing=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ "$missing" -ne 0 ]; then
|
||||||
|
echo "Set all required macOS signing/notarization secrets and rerun."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||||
|
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||||
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||||
|
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||||
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
bun install --frozen-lockfile
|
||||||
|
cd stats && bun install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Build texthooker-ui
|
||||||
|
run: |
|
||||||
|
cd vendor/texthooker-ui
|
||||||
|
bun install
|
||||||
|
bun run build
|
||||||
|
|
||||||
|
- name: Build signed + notarized macOS artifacts
|
||||||
|
run: bun run build:mac
|
||||||
|
env:
|
||||||
|
CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||||
|
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||||
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||||
|
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||||
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||||
|
|
||||||
|
- name: Upload macOS artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: macos
|
||||||
|
path: |
|
||||||
|
release/*.dmg
|
||||||
|
release/*.zip
|
||||||
|
release/*.yml
|
||||||
|
release/*.blockmap
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
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 }}-${{ runner.arch }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/texthooker-ui/package.json', 'vendor/subminer-yomitan/package-lock.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-${{ runner.arch }}-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
|
||||||
|
release/*.yml
|
||||||
|
release/*.blockmap
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
release:
|
||||||
|
needs: [build-linux, build-macos, build-windows]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Download AppImage
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: appimage
|
||||||
|
path: release
|
||||||
|
|
||||||
|
- name: Download macOS artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: macos
|
||||||
|
path: release
|
||||||
|
|
||||||
|
- name: Download Windows artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: windows
|
||||||
|
path: release
|
||||||
|
|
||||||
|
- name: Setup Bun
|
||||||
|
uses: oven-sh/setup-bun@v2
|
||||||
|
with:
|
||||||
|
bun-version: 1.3.5
|
||||||
|
|
||||||
|
- name: Cache dependencies
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.bun/install/cache
|
||||||
|
node_modules
|
||||||
|
key: ${{ runner.os }}-${{ runner.arch }}-bun-${{ hashFiles('bun.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-${{ runner.arch }}-bun-
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: bun install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Build Bun subminer wrapper
|
||||||
|
run: make build-launcher
|
||||||
|
|
||||||
|
- name: Verify Bun subminer wrapper
|
||||||
|
run: dist/launcher/subminer --help >/dev/null
|
||||||
|
|
||||||
|
- name: Enforce generated launcher workflow
|
||||||
|
run: bash scripts/verify-generated-launcher.sh
|
||||||
|
|
||||||
|
- name: Verify generated config examples
|
||||||
|
run: bun run verify:config-example
|
||||||
|
|
||||||
|
- name: Package optional assets bundle
|
||||||
|
run: |
|
||||||
|
tar -czf "release/subminer-assets.tar.gz" \
|
||||||
|
config.example.jsonc \
|
||||||
|
plugin/subminer \
|
||||||
|
plugin/subminer.conf \
|
||||||
|
assets/themes/subminer.rasi
|
||||||
|
|
||||||
|
- name: Generate checksums
|
||||||
|
run: |
|
||||||
|
shopt -s nullglob
|
||||||
|
files=(release/*.AppImage release/*.dmg release/*.exe release/*.zip release/*.tar.gz release/*.yml release/*.blockmap dist/launcher/subminer)
|
||||||
|
if [ "${#files[@]}" -eq 0 ]; then
|
||||||
|
echo "No release artifacts found for checksum generation."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
: > release/SHA256SUMS.txt
|
||||||
|
for file in "${files[@]}"; do
|
||||||
|
printf '%s %s\n' \
|
||||||
|
"$(sha256sum "$file" | awk '{print $1}')" \
|
||||||
|
"${file##*/}" >> release/SHA256SUMS.txt
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Get version from tag
|
||||||
|
id: version
|
||||||
|
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Verify committed prerelease notes
|
||||||
|
run: |
|
||||||
|
if [ ! -s release/prerelease-notes.md ]; then
|
||||||
|
echo "::error::release/prerelease-notes.md is missing or empty. Run 'bun run changelog:prerelease-notes --version <version>' locally and commit the file before tagging."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Publish Prerelease
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
shopt -s nullglob
|
||||||
|
artifacts=(
|
||||||
|
release/*.AppImage
|
||||||
|
release/*.dmg
|
||||||
|
release/*.exe
|
||||||
|
release/*.zip
|
||||||
|
release/*.tar.gz
|
||||||
|
release/*.yml
|
||||||
|
release/*.blockmap
|
||||||
|
release/SHA256SUMS.txt
|
||||||
|
dist/launcher/subminer
|
||||||
|
)
|
||||||
|
|
||||||
|
if [ "${#artifacts[@]}" -eq 0 ]; then
|
||||||
|
echo "No release artifacts found for upload."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if gh release view "${{ steps.version.outputs.VERSION }}" >/dev/null 2>&1; then
|
||||||
|
gh release edit "${{ steps.version.outputs.VERSION }}" \
|
||||||
|
--draft \
|
||||||
|
--prerelease \
|
||||||
|
--title "${{ steps.version.outputs.VERSION }}" \
|
||||||
|
--notes-file release/prerelease-notes.md
|
||||||
|
else
|
||||||
|
gh release create "${{ steps.version.outputs.VERSION }}" \
|
||||||
|
--draft \
|
||||||
|
--latest=false \
|
||||||
|
--prerelease \
|
||||||
|
--title "${{ steps.version.outputs.VERSION }}" \
|
||||||
|
--notes-file release/prerelease-notes.md
|
||||||
|
fi
|
||||||
|
|
||||||
|
for asset in "${artifacts[@]}"; do
|
||||||
|
gh release upload "${{ steps.version.outputs.VERSION }}" "$asset" --clobber
|
||||||
|
done
|
||||||
|
|
||||||
|
gh release edit "${{ steps.version.outputs.VERSION }}" \
|
||||||
|
--draft=false \
|
||||||
|
--prerelease \
|
||||||
|
--title "${{ steps.version.outputs.VERSION }}" \
|
||||||
|
--notes-file release/prerelease-notes.md
|
||||||
@@ -4,14 +4,13 @@ on:
|
|||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- 'v*'
|
- 'v*'
|
||||||
|
- '!v*-beta.*'
|
||||||
|
- '!v*-rc.*'
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: release-${{ github.ref }}
|
group: release-${{ github.ref }}
|
||||||
cancel-in-progress: false
|
cancel-in-progress: false
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
quality-gate:
|
quality-gate:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -26,25 +25,42 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
bun-version: 1.3.5
|
bun-version: 1.3.5
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: bun install --frozen-lockfile
|
|
||||||
|
|
||||||
- name: Cache dependencies
|
- name: Cache dependencies
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.bun/install/cache
|
~/.bun/install/cache
|
||||||
node_modules
|
node_modules
|
||||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
|
stats/node_modules
|
||||||
|
vendor/subminer-yomitan/node_modules
|
||||||
|
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/subminer-yomitan/package-lock.json') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-bun-
|
${{ runner.os }}-bun-
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: bun install --frozen-lockfile
|
run: |
|
||||||
|
bun install --frozen-lockfile
|
||||||
|
cd stats && bun install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Lint stats (formatting)
|
||||||
|
run: bun run lint:stats
|
||||||
|
|
||||||
|
- name: Build (TypeScript check)
|
||||||
|
run: bun run typecheck
|
||||||
|
|
||||||
- name: Test suite (source)
|
- name: Test suite (source)
|
||||||
run: bun run test:fast
|
run: bun run test:fast
|
||||||
|
|
||||||
|
- name: Coverage suite (maintained source lane)
|
||||||
|
run: bun run test:coverage:src
|
||||||
|
|
||||||
|
- name: Upload coverage artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: coverage-test-src
|
||||||
|
path: coverage/test-src/lcov.info
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Launcher smoke suite (source)
|
- name: Launcher smoke suite (source)
|
||||||
run: bun run test:launcher:smoke:src
|
run: bun run test:launcher:smoke:src
|
||||||
|
|
||||||
@@ -59,6 +75,9 @@ jobs:
|
|||||||
- name: Build (bundle)
|
- name: Build (bundle)
|
||||||
run: bun run build
|
run: bun run build
|
||||||
|
|
||||||
|
- name: Immersion SQLite verification
|
||||||
|
run: bun run test:immersion:sqlite:dist
|
||||||
|
|
||||||
- name: Dist smoke suite
|
- name: Dist smoke suite
|
||||||
run: bun run test:smoke:dist
|
run: bun run test:smoke:dist
|
||||||
|
|
||||||
@@ -82,13 +101,17 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
~/.bun/install/cache
|
~/.bun/install/cache
|
||||||
node_modules
|
node_modules
|
||||||
|
stats/node_modules
|
||||||
vendor/texthooker-ui/node_modules
|
vendor/texthooker-ui/node_modules
|
||||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'vendor/texthooker-ui/package.json') }}
|
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: |
|
restore-keys: |
|
||||||
${{ runner.os }}-bun-
|
${{ runner.os }}-bun-
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: bun install --frozen-lockfile
|
run: |
|
||||||
|
bun install --frozen-lockfile
|
||||||
|
cd stats && bun install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build texthooker-ui
|
- name: Build texthooker-ui
|
||||||
run: |
|
run: |
|
||||||
@@ -98,8 +121,6 @@ jobs:
|
|||||||
|
|
||||||
- name: Build AppImage
|
- name: Build AppImage
|
||||||
run: bun run build:appimage
|
run: bun run build:appimage
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Build unversioned AppImage
|
- name: Build unversioned AppImage
|
||||||
run: |
|
run: |
|
||||||
@@ -116,7 +137,10 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: appimage
|
name: appimage
|
||||||
path: release/*.AppImage
|
path: |
|
||||||
|
release/*.AppImage
|
||||||
|
release/*.yml
|
||||||
|
release/*.blockmap
|
||||||
|
|
||||||
build-macos:
|
build-macos:
|
||||||
needs: [quality-gate]
|
needs: [quality-gate]
|
||||||
@@ -138,8 +162,10 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
~/.bun/install/cache
|
~/.bun/install/cache
|
||||||
node_modules
|
node_modules
|
||||||
|
stats/node_modules
|
||||||
vendor/texthooker-ui/node_modules
|
vendor/texthooker-ui/node_modules
|
||||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'vendor/texthooker-ui/package.json') }}
|
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: |
|
restore-keys: |
|
||||||
${{ runner.os }}-bun-
|
${{ runner.os }}-bun-
|
||||||
|
|
||||||
@@ -164,7 +190,9 @@ jobs:
|
|||||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: bun install --frozen-lockfile
|
run: |
|
||||||
|
bun install --frozen-lockfile
|
||||||
|
cd stats && bun install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build texthooker-ui
|
- name: Build texthooker-ui
|
||||||
run: |
|
run: |
|
||||||
@@ -175,7 +203,6 @@ jobs:
|
|||||||
- name: Build signed + notarized macOS artifacts
|
- name: Build signed + notarized macOS artifacts
|
||||||
run: bun run build:mac
|
run: bun run build:mac
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
CSC_LINK: ${{ secrets.CSC_LINK }}
|
CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||||
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||||
@@ -189,10 +216,67 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
release/*.dmg
|
release/*.dmg
|
||||||
release/*.zip
|
release/*.zip
|
||||||
|
release/*.yml
|
||||||
|
release/*.blockmap
|
||||||
|
|
||||||
|
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
|
||||||
|
release/*.yml
|
||||||
|
release/*.blockmap
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
release:
|
release:
|
||||||
needs: [build-linux, build-macos]
|
needs: [build-linux, build-macos, build-windows]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -211,6 +295,12 @@ jobs:
|
|||||||
name: macos
|
name: macos
|
||||||
path: release
|
path: release
|
||||||
|
|
||||||
|
- name: Download Windows artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: windows
|
||||||
|
path: release
|
||||||
|
|
||||||
- name: Setup Bun
|
- name: Setup Bun
|
||||||
uses: oven-sh/setup-bun@v2
|
uses: oven-sh/setup-bun@v2
|
||||||
with:
|
with:
|
||||||
@@ -238,6 +328,9 @@ jobs:
|
|||||||
- name: Enforce generated launcher workflow
|
- name: Enforce generated launcher workflow
|
||||||
run: bash scripts/verify-generated-launcher.sh
|
run: bash scripts/verify-generated-launcher.sh
|
||||||
|
|
||||||
|
- name: Verify generated config examples
|
||||||
|
run: bun run verify:config-example
|
||||||
|
|
||||||
- name: Package optional assets bundle
|
- name: Package optional assets bundle
|
||||||
run: |
|
run: |
|
||||||
tar -czf "release/subminer-assets.tar.gz" \
|
tar -czf "release/subminer-assets.tar.gz" \
|
||||||
@@ -249,34 +342,34 @@ jobs:
|
|||||||
- name: Generate checksums
|
- name: Generate checksums
|
||||||
run: |
|
run: |
|
||||||
shopt -s nullglob
|
shopt -s nullglob
|
||||||
files=(release/*.AppImage release/*.dmg release/*.zip release/*.tar.gz dist/launcher/subminer)
|
files=(release/*.AppImage release/*.dmg release/*.exe release/*.zip release/*.tar.gz release/*.yml release/*.blockmap dist/launcher/subminer)
|
||||||
if [ "${#files[@]}" -eq 0 ]; then
|
if [ "${#files[@]}" -eq 0 ]; then
|
||||||
echo "No release artifacts found for checksum generation."
|
echo "No release artifacts found for checksum generation."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
sha256sum "${files[@]}" > release/SHA256SUMS.txt
|
: > release/SHA256SUMS.txt
|
||||||
|
for file in "${files[@]}"; do
|
||||||
|
printf '%s %s\n' \
|
||||||
|
"$(sha256sum "$file" | awk '{print $1}')" \
|
||||||
|
"${file##*/}" >> release/SHA256SUMS.txt
|
||||||
|
done
|
||||||
|
|
||||||
- name: Get version from tag
|
- name: Get version from tag
|
||||||
id: version
|
id: version
|
||||||
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Generate changelog
|
- name: Guard against pending changelog fragments
|
||||||
id: changelog
|
|
||||||
run: |
|
run: |
|
||||||
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
|
if find changes -maxdepth 1 -name '*.md' -not -name README.md -print -quit | grep -q .; then
|
||||||
if [ -n "$PREV_TAG" ]; then
|
echo "::error::Pending changelog fragments detected. Run 'bun run changelog:build --version ${{ steps.version.outputs.VERSION }}' locally and commit the polished CHANGELOG.md before tagging. CI no longer auto-builds the changelog because the polish step requires the local 'claude' CLI."
|
||||||
CHANGES=$(git log --pretty=format:"- %s" ${PREV_TAG}..HEAD)
|
exit 1
|
||||||
else
|
|
||||||
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
|
fi
|
||||||
echo "CHANGES<<EOF" >> $GITHUB_OUTPUT
|
|
||||||
echo "$CHANGES" >> $GITHUB_OUTPUT
|
- name: Verify changelog is ready for tagged release
|
||||||
echo "EOF" >> $GITHUB_OUTPUT
|
run: bun run changelog:check --version "${{ steps.version.outputs.VERSION }}"
|
||||||
|
|
||||||
|
- name: Generate release notes from changelog
|
||||||
|
run: bun run changelog:release-notes --version "${{ steps.version.outputs.VERSION }}"
|
||||||
|
|
||||||
- name: Publish Release
|
- name: Publish Release
|
||||||
env:
|
env:
|
||||||
@@ -284,55 +377,27 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
cat > release-body.md <<'EOF'
|
|
||||||
## Changes
|
|
||||||
${{ steps.changelog.outputs.CHANGES }}
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
### AppImage (Recommended)
|
|
||||||
1. Download the AppImage below
|
|
||||||
2. Make it executable: `chmod +x SubMiner.AppImage`
|
|
||||||
3. Run: `./SubMiner.AppImage`
|
|
||||||
|
|
||||||
### 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
|
|
||||||
|
|
||||||
### 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/` directory contents 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`.
|
|
||||||
EOF
|
|
||||||
|
|
||||||
if gh release view "${{ steps.version.outputs.VERSION }}" >/dev/null 2>&1; then
|
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 }}" \
|
gh release edit "${{ steps.version.outputs.VERSION }}" \
|
||||||
|
--draft=false \
|
||||||
--title "${{ steps.version.outputs.VERSION }}" \
|
--title "${{ steps.version.outputs.VERSION }}" \
|
||||||
--notes-file release-body.md \
|
--notes-file release/release-notes.md
|
||||||
--prerelease false
|
|
||||||
else
|
else
|
||||||
gh release create "${{ steps.version.outputs.VERSION }}" \
|
gh release create "${{ steps.version.outputs.VERSION }}" \
|
||||||
--title "${{ steps.version.outputs.VERSION }}" \
|
--title "${{ steps.version.outputs.VERSION }}" \
|
||||||
--notes-file release-body.md \
|
--notes-file release/release-notes.md
|
||||||
--prerelease false
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
shopt -s nullglob
|
shopt -s nullglob
|
||||||
artifacts=(
|
artifacts=(
|
||||||
release/*.AppImage
|
release/*.AppImage
|
||||||
release/*.dmg
|
release/*.dmg
|
||||||
|
release/*.exe
|
||||||
release/*.zip
|
release/*.zip
|
||||||
release/*.tar.gz
|
release/*.tar.gz
|
||||||
|
release/*.yml
|
||||||
|
release/*.blockmap
|
||||||
release/SHA256SUMS.txt
|
release/SHA256SUMS.txt
|
||||||
dist/launcher/subminer
|
dist/launcher/subminer
|
||||||
)
|
)
|
||||||
@@ -345,3 +410,130 @@ jobs:
|
|||||||
for asset in "${artifacts[@]}"; do
|
for asset in "${artifacts[@]}"; do
|
||||||
gh release upload "${{ steps.version.outputs.VERSION }}" "$asset" --clobber
|
gh release upload "${{ steps.version.outputs.VERSION }}" "$asset" --clobber
|
||||||
done
|
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: Check AUR publish prerequisites
|
||||||
|
id: aur_prereqs
|
||||||
|
env:
|
||||||
|
AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
if [ -z "${AUR_SSH_PRIVATE_KEY}" ]; then
|
||||||
|
echo "::warning::Missing AUR_SSH_PRIVATE_KEY; skipping automated AUR publish."
|
||||||
|
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Configure SSH for AUR
|
||||||
|
id: aur_ssh
|
||||||
|
if: steps.aur_prereqs.outputs.skip != 'true'
|
||||||
|
env:
|
||||||
|
AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
if 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; then
|
||||||
|
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "::warning::Unable to configure SSH for AUR; skipping automated AUR publish."
|
||||||
|
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Clone AUR repo
|
||||||
|
id: aur_clone
|
||||||
|
if: steps.aur_prereqs.outputs.skip != 'true' && steps.aur_ssh.outputs.skip != 'true'
|
||||||
|
env:
|
||||||
|
GIT_SSH_COMMAND: ssh -i ~/.ssh/aur -o IdentitiesOnly=yes
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
attempts=3
|
||||||
|
for attempt in $(seq 1 "$attempts"); do
|
||||||
|
if git clone ssh://aur@aur.archlinux.org/subminer-bin.git aur-subminer-bin; then
|
||||||
|
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -rf aur-subminer-bin
|
||||||
|
|
||||||
|
if [ "$attempt" -lt "$attempts" ]; then
|
||||||
|
sleep $((attempt * 15))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "::warning::Unable to clone subminer-bin from AUR after ${attempts} attempts; skipping automated AUR publish."
|
||||||
|
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Download release assets for AUR
|
||||||
|
if: steps.aur_prereqs.outputs.skip != 'true' && steps.aur_ssh.outputs.skip != 'true' && steps.aur_clone.outputs.skip != 'true'
|
||||||
|
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
|
||||||
|
if: steps.aur_prereqs.outputs.skip != 'true' && steps.aur_ssh.outputs.skip != 'true' && steps.aur_clone.outputs.skip != 'true'
|
||||||
|
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
|
||||||
|
if: steps.aur_prereqs.outputs.skip != 'true' && steps.aur_ssh.outputs.skip != 'true' && steps.aur_clone.outputs.skip != 'true'
|
||||||
|
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 }}"
|
||||||
|
|
||||||
|
attempts=3
|
||||||
|
for attempt in $(seq 1 "$attempts"); do
|
||||||
|
if git push origin HEAD:master; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$attempt" -lt "$attempts" ]; then
|
||||||
|
sleep $((attempt * 15))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "::warning::Unable to push the AUR update after ${attempts} attempts; GitHub release is published, but subminer-bin needs manual follow-up."
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
# Dependencies
|
# Dependencies
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
|
# Superpowers brainstorming
|
||||||
|
.superpowers/
|
||||||
|
|
||||||
# Electron build output
|
# Electron build output
|
||||||
out/
|
out/
|
||||||
dist/
|
dist/
|
||||||
release/
|
release/*
|
||||||
|
!release/
|
||||||
|
!release/release-notes.md
|
||||||
|
!release/prerelease-notes.md
|
||||||
|
build/yomitan/
|
||||||
|
coverage/
|
||||||
|
|
||||||
# Launcher build artifact (produced by make build-launcher)
|
# Launcher build artifact (produced by make build-launcher)
|
||||||
/subminer
|
/subminer
|
||||||
@@ -21,9 +29,7 @@ Thumbs.db
|
|||||||
.idea/
|
.idea/
|
||||||
*.swp
|
*.swp
|
||||||
*.swo
|
*.swo
|
||||||
**/CLAUDE.md
|
|
||||||
environment.toml
|
environment.toml
|
||||||
**/CLAUDE.md
|
|
||||||
.env
|
.env
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
|
||||||
@@ -34,5 +40,21 @@ docs/.vitepress/cache/
|
|||||||
docs/.vitepress/dist/
|
docs/.vitepress/dist/
|
||||||
tests/*
|
tests/*
|
||||||
.worktrees/
|
.worktrees/
|
||||||
|
.tmp/
|
||||||
.codex/*
|
.codex/*
|
||||||
.agents/*
|
.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,6 @@
|
|||||||
[submodule "vendor/yomitan-jlpt-vocab"]
|
[submodule "vendor/yomitan-jlpt-vocab"]
|
||||||
path = vendor/yomitan-jlpt-vocab
|
path = vendor/yomitan-jlpt-vocab
|
||||||
url = https://github.com/stephenmk/yomitan-jlpt-vocab
|
url = https://github.com/stephenmk/yomitan-jlpt-vocab
|
||||||
[submodule "yomitan-jlpt-vocab"]
|
[submodule "vendor/subminer-yomitan"]
|
||||||
path = vendor/yomitan-jlpt-vocab
|
path = vendor/subminer-yomitan
|
||||||
url = https://github.com/stephenmk/yomitan-jlpt-vocab
|
url = https://github.com/ksyasuda/subminer-yomitan
|
||||||
|
|||||||
@@ -1,29 +1,66 @@
|
|||||||
<!-- BACKLOG.MD MCP GUIDELINES START -->
|
# AGENTS.MD
|
||||||
|
|
||||||
<CRITICAL_INSTRUCTION>
|
## Internal Docs
|
||||||
|
|
||||||
## BACKLOG WORKFLOW INSTRUCTIONS
|
Start here, then leave this file.
|
||||||
|
|
||||||
This project uses Backlog.md MCP for all task and project management activities.
|
- 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)
|
||||||
|
|
||||||
**CRITICAL GUIDANCE**
|
`docs-site/` is user-facing. Do not treat it as the canonical internal source of truth.
|
||||||
|
|
||||||
- If your client supports MCP resources, read `backlog://workflow/overview` to understand when and how to use Backlog for this project.
|
## Quick Start
|
||||||
- If your client only supports tools or the above request fails, call `backlog.get_workflow_overview()` tool to load the tool-oriented overview (it lists the matching guide tools).
|
|
||||||
|
|
||||||
- **First time working here?** Read the overview resource IMMEDIATELY to learn the workflow
|
- Init workspace: `git submodule update --init --recursive`
|
||||||
- **Already familiar?** You should have the overview cached ("## Backlog.md Overview (MCP)")
|
- Install deps: `make deps` or `bun install` plus `(cd vendor/texthooker-ui && bun install --frozen-lockfile)`
|
||||||
- **When to read it**: BEFORE creating tasks, or when you're unsure whether to track work
|
- Fast dev loop: `make dev-watch`
|
||||||
|
- Full local run: `bun run dev`
|
||||||
|
- Verbose Electron debug: `electron . --start --dev --log-level debug`
|
||||||
|
|
||||||
These guides cover:
|
## Build / Test
|
||||||
|
|
||||||
- Decision framework for when to create tasks
|
- Runtime/package manager: Bun (`packageManager: bun@1.3.5`)
|
||||||
- Search-first workflow to avoid duplicates
|
- Default handoff gate:
|
||||||
- Links to detailed guides for task creation, execution, and finalization
|
`bun run typecheck`
|
||||||
- MCP tools reference
|
`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`
|
||||||
|
|
||||||
You MUST read the overview resource to understand the complete workflow. The information is NOT summarized here.
|
## Change-Specific Checks
|
||||||
|
|
||||||
</CRITICAL_INSTRUCTION>
|
- 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`
|
||||||
|
|
||||||
<!-- BACKLOG.MD MCP GUIDELINES END -->
|
## 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
|
||||||
|
|||||||
@@ -0,0 +1,515 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## v0.14.0 (2026-05-12)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Character Dictionary:** Added AniList-based character dictionary selection for resolving title mismatches — open it in-app with the new `Ctrl+Alt+A` shortcut or from the CLI with `subminer dictionary --candidates` / `--select`. Series-scoped overrides replace stale entries in the merged dictionary.
|
||||||
|
- **Primary Subtitle Bar:** Added a `V` shortcut and mpv plugin binding to toggle the primary subtitle bar without affecting mpv's native subtitle visibility.
|
||||||
|
- **Texthooker:** Added `subminer texthooker -o` and a tray menu item to open the local texthooker page in the default browser.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **mpv Plugin Setup:** SubMiner-managed launches now automatically inject the bundled mpv plugin when no global plugin is installed. The legacy plugin removal prompt appears before mpv starts so users can trash the old global plugin and relaunch immediately.
|
||||||
|
- **Tray Menu:** Replaced the "Open Overlay" menu item with "Open Help", which opens the session help modal.
|
||||||
|
- **Stats Exclusions:** Vocabulary exclusions now persist in the immersion database and import any existing browser-local exclusions on first load.
|
||||||
|
- **Config Example:** The generated example config now lists every built-in default keybinding, with regression coverage ensuring they compile, dispatch in the overlay, and register through the mpv plugin.
|
||||||
|
- **Default Startup Options:** Texthooker startup and subtitle/annotation WebSocket servers are now disabled by default. Fresh installs also get updated primary subtitle style defaults: a Japanese font stack, transparent subtitle backgrounds, stronger text shadows, and teal N4/fourth-band coloring.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **JLPT Underlines:** Restored persistent JLPT underlines on subtitle tokens; they now keep their JLPT color after dictionary lookups and when a token also carries a known-word or frequency annotation color.
|
||||||
|
- **Hyprland Fullscreen Overlay:** Fixed fullscreen transitions on Hyprland so mpv fullscreen changes refresh overlay geometry, reassert topmost stacking, and keep hover-pause working through resize/toggle cycles. The overlay now aligns exactly with the mpv window, disables floating-window decoration on exact placement, and no longer pins across workspaces.
|
||||||
|
- **macOS Overlay:** Kept the overlay visible and interactive during transient tracker refreshes and while mpv is the active tracked window. Fixed z-order so the overlay stays above mpv but behind other foreground windows.
|
||||||
|
- **Subtitle Annotation Colors:** Fixed annotated token colors so `subtitleStyle` typography is preserved and higher-priority known-word and frequency colors correctly take precedence over JLPT colors. Added a subtle brightness lift for hover states so transparent hover backgrounds still show a visible affordance. Hid the browser focus ring on the overlay surface so focused overlays no longer show a viewport border.
|
||||||
|
- **Grammar and Particle Annotation Filters:** Suppressed annotation styling (N+1, JLPT, frequency, name) for grammar-only tokens including auxiliary inflection fragments like `れる`/`れた`, polite copula tails like `です`/`ですよ`, negative copula phrases like `じゃないですか`, and standalone particles like `に`; lexical forms like `くれる` remain eligible.
|
||||||
|
- **Interjection and Existence Verb Filters:** Suppressed annotations for ハァ-style interjections and `ある`/`有る` existence verbs; known-word highlighting still applies for `ある`.
|
||||||
|
- **Known-Word Compound Tokens:** Preserved Yomitan compound token boundaries for known-word highlighting so known component words no longer color a larger unknown compound green.
|
||||||
|
- **Kana Token Annotations:** Stopped kana-only tokens from being selected as N+1 targets or receiving N+1, JLPT, or frequency annotation metadata.
|
||||||
|
- **Subtitle Bar Modes:** Changed `v` to cycle the primary subtitle bar through visible, hover, and hidden modes with OSD feedback. A new `subtitleStyle.primaryDefaultMode` config option sets the startup default independently of secondary subtitles.
|
||||||
|
- **Subtitle Keybindings:** Fixed the default replay/next subtitle keybindings by moving session help to `Ctrl/Cmd+/`, freeing `Ctrl+Shift+H` and `Ctrl+Shift+L` for subtitle playback controls. The mpv plugin now registers shifted letter chords correctly so `Ctrl+Shift+L` reaches play-next-subtitle; play-next also starts playback from a paused state before re-pausing at subtitle end.
|
||||||
|
- **Yomitan Popup Precedence:** Fixed keyboard-only Yomitan popup controls so popup shortcuts take precedence over overlay keybindings like `j`.
|
||||||
|
- **Subtitle Prefetch:** Kept annotation prefetch running after immediate cache-hit renders so upcoming subtitle colors stay ready.
|
||||||
|
- **Frequency Highlighting:** Fixed frequency highlighting for ordinal prefix-noun tokens like `第二` so JPDB ranks are preserved in subtitle annotations.
|
||||||
|
- **Known-Word Refresh After Mining:** Refreshed the current subtitle after successful card mining so newly known words recolor immediately.
|
||||||
|
- **Linux Multi-Line Copy:** Fixed multi-line subtitle copy on Linux so the overlay handles the follow-up digit selection locally, eliminating the timeout.
|
||||||
|
- **mpv Crash Notifications:** Stopped mpv from owning long-running SubMiner subprocesses during shutdown, preventing desktop crash notifications when closing video.
|
||||||
|
- **mpv Buffering Reload:** Kept the overlay alive across same-media reloads during buffering, avoiding duplicate startup gates and AniSkip lookups.
|
||||||
|
- **mpv Playlist Navigation:** Playlist navigation now reuses the running overlay without repeating the pause-until-ready warmup gate.
|
||||||
|
- **Anki Manual Updates:** Manual clipboard subtitle updates now replace sentence audio and media even when audio overwrite is disabled, while preserving existing word audio.
|
||||||
|
- **AniList Progress Tracking:** Fixed post-watch progress to check on mpv time-position updates using the fresh mpv position, wired manual mark-watched to force a progress sync, and filled missing episode metadata from the filename parser. Prevented duplicate post-watch writes during concurrent checks and preserved manual watched marks when sync fails.
|
||||||
|
- **AniList Setup:** Prevented config reload from opening the setup window during playback when token storage is unavailable; stopped setup from reporting success when encrypted token persistence fails.
|
||||||
|
- **AniList Linux Keyring:** Retried keyring availability after transient GNOME libsecret startup failures so stored tokens load once the keyring becomes available.
|
||||||
|
- **Stats Daemon:** Fixed stats background mode to route through the isolated stats daemon and deferred in-app stats startup when a daemon is already running, so video launches no longer fail when the stats port is in use.
|
||||||
|
- **Stats Session Detail:** Fixed recent session detail pages showing "Media not found" before lifetime media summaries are available.
|
||||||
|
- **Hover Background Config:** Accepted `subtitleStyle.hoverBackground` as an alias for `subtitleStyle.hoverTokenBackgroundColor` so setting it to `transparent` removes hover token backgrounds.
|
||||||
|
- **Managed Playback Exit:** Launcher-managed playback now exits the background SubMiner app when the video closes; explicit background launches remain persistent.
|
||||||
|
- **Multi-Mine Shortcuts:** Multi-line subtitle mining now accepts follow-up number-row digits even when the original shortcut modifiers are still held.
|
||||||
|
- **Jellyfin Setup:** Improved setup with recent server selection and inline authentication feedback; added a tray "Jellyfin Discovery" toggle for runtime-only cast discovery.
|
||||||
|
- **Subtitle POS Filtering:** Used Yomitan `wordClasses` metadata for subtitle part-of-speech filtering and backfilled blank MeCab POS detail fields during parser enrichment.
|
||||||
|
|
||||||
|
### Docs
|
||||||
|
|
||||||
|
- Improved the docs homepage with canonical URLs and a cleaner sitemap for better search engine indexing.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Internal changes</summary>
|
||||||
|
|
||||||
|
### Internal
|
||||||
|
|
||||||
|
- Replaced the changelog renderer with an AI polish pass that merges related fragments and writes user-facing release notes. `CHANGELOG.md` keeps internal items in a collapsed `<details>` block; GitHub release notes omit them entirely.
|
||||||
|
- Release CI no longer auto-builds pending `changes/*.md` fragments on tag. Tagging now fails fast if fragments remain — run `bun run changelog:build` (requires the `claude` CLI) and commit before tagging.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## v0.12.0 (2026-04-11)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Overlay: Added configurable overlay shortcuts for session help, controller select, and controller debug actions.
|
||||||
|
- Overlay: Added mpv/plugin and CLI routing for session help, controller utilities, and subtitle sidebar toggling through the shared session-action path.
|
||||||
|
- Overlay: Improved dedicated overlay modal retry and focus handling for runtime options, Jimaku, session help, controller tools, and the playlist browser.
|
||||||
|
- Overlay: Fixed controller configuration and controller debug shortcut opens so configured bindings bring up their modals again instead of tripping renderer recovery.
|
||||||
|
- Stats: Sessions are rolled up per episode within each day, with a bulk delete that wipes every session in the group.
|
||||||
|
- Stats: Trends add a 365-day range next to the existing 7d/30d/90d/all options.
|
||||||
|
- Stats: Library detail view gets a delete-episode action that removes the video and all its sessions.
|
||||||
|
- Stats: Vocabulary Top 50 tightens the word/reading column so katakana entries no longer push the scores off screen.
|
||||||
|
- Stats: Episode detail hides card events whose Anki notes have been deleted, instead of showing phantom mining activity.
|
||||||
|
- Stats: Trend and watch-time charts share a unified theme with horizontal gridlines and larger ticks for legibility.
|
||||||
|
- Stats: Overview, Library, Trends, Sessions, and Vocabulary now use generic "title" wording so YouTube videos and anime live comfortably side by side in the dashboard.
|
||||||
|
- Stats: Session timeline no longer plots seek-forward/seek-backward markers — they were too noisy on sessions with lots of rewinds.
|
||||||
|
- Stats: Replaced the "Library — Per Day" section on the Stats → Trends page with a "Library — Summary" section. The new section shows a top-10 watch-time leaderboard chart and a sortable per-title table (watch time, videos, sessions, cards, words, lookups, lookups/100w, date range), all scoped to the current date range selector.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Overlay: Fixed overlay drag-and-drop routing so dropping external subtitle files like `.ass` onto mpv still loads them when the overlay is visible.
|
||||||
|
- Overlay: Addressed the latest CodeRabbit follow-ups on PR #49, including generation-scoped Lua session binding names, stricter session command validation, session-help shortcut visibility, the numeric-selection key guard, stats-overlay startup classification, and safer session-binding persistence.
|
||||||
|
- Overlay: Addressed the latest CodeRabbit follow-ups on the Windows overlay flow, including exact mpv target resolution, lower-overlay helper arguments, Win32 failure detection, and overlay cleanup on tracker loss.
|
||||||
|
- Overlay: Fixed Windows overlay z-order so the visible subtitle overlay stops staying above unrelated apps after mpv loses focus.
|
||||||
|
- Overlay: Fixed Windows overlay tracking to use native window polling and owner/z-order binding, which keeps the subtitle overlay aligned to the active mpv window more reliably.
|
||||||
|
- Overlay: Fixed Windows overlay hide/restore behavior so minimizing mpv immediately hides the overlay and restoring mpv brings it back on top of the mpv window without requiring a click.
|
||||||
|
- Overlay: Fixed stats overlay layering so the in-player stats page now stays above mpv and the subtitle overlay while it is open.
|
||||||
|
- Overlay: Fixed Windows subtitle overlay stability so transient tracker misses and restore events keep the current subtitle visible instead of waiting for the next subtitle line.
|
||||||
|
- Overlay: Fixed Windows focus handoff from the interactive subtitle overlay back to mpv so the overlay no longer drops behind mpv and briefly disappears.
|
||||||
|
- Overlay: Fixed Windows visible-overlay startup so it no longer briefly opens as an interactive or opaque surface before the tracked transparent overlay state settles.
|
||||||
|
- Overlay: Fixed spurious auto-pause after overlay visibility recovery and window resize so the overlay no longer pauses mpv until the pointer genuinely re-enters the subtitle area.
|
||||||
|
- Overlay: Fixed Windows secondary subtitle hover mode so the expanded hover hit area no longer blocks the native minimize, maximize, and close buttons.
|
||||||
|
- Overlay: Fixed Windows Yomitan popup focus loss after closing nested lookups so the original popup stays interactive instead of falling through to mpv.
|
||||||
|
- Stats: Fixed immersion-tracker timestamp handling under Bun/libsql so library rows, session timelines, and lifetime summaries keep real wall-clock millisecond values instead of truncating to invalid negative timestamps.
|
||||||
|
- Mpv Plugin: Fixed the mpv Lua plugin so hover and environment modules no longer use the `goto continue` pattern that can fail to parse on some user Lua runtimes.
|
||||||
|
|
||||||
|
### Internal
|
||||||
|
|
||||||
|
- Release: Added a dedicated beta/rc prerelease GitHub Actions workflow that publishes GitHub prereleases without consuming pending changelog fragments or updating AUR.
|
||||||
|
- Release: Added prerelease note generation so beta and release-candidate tags can reuse the current pending `changes/*.md` fragments while leaving stable changelog publication for the final release cut.
|
||||||
|
|
||||||
|
## v0.11.2 (2026-04-07)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Launcher: Replaced the launcher-only fullscreen toggle with `mpv.launchMode` so SubMiner-managed mpv playback can start in normal, maximized, or fullscreen mode.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Launcher: Fixed launcher-managed mpv spawning to force an explicit X11 GPU path when Wayland trackers are unavailable.
|
||||||
|
- Launcher: Local playback now promotes a single unlabeled external subtitle sidecar to the primary slot instead of leaving mpv's embedded English auto-selection in place.
|
||||||
|
- Release: Fixed Linux AppImage startup packaging so Chromium child relaunches can resolve the bundled `libffmpeg.so` instead of crash-looping on startup.
|
||||||
|
|
||||||
|
## v0.11.1 (2026-04-04)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Release: Linux packaged builds now expose the canonical `SubMiner` app identity to Electron's startup metadata so native Wayland compositors stop reporting the window class/app-id as lowercase `subminer`.
|
||||||
|
- Linux: Linux now restores the runtime options, Jimaku, and Subsync shortcuts after the Electron 39 regression by routing those actions through the overlay's mpv/IPC shortcut path.
|
||||||
|
|
||||||
|
## v0.11.0 (2026-04-03)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Overlay: Added a playlist browser overlay modal for browsing sibling video files and the live mpv queue during playback.
|
||||||
|
- Overlay: Added the default `Ctrl+Alt+P` keybinding to open the playlist browser and manage queue order without leaving playback.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Setup: Made mpv plugin installation mandatory in the first-run setup flow, removed the skip path, and kept Finish disabled until the plugin is installed.
|
||||||
|
- Setup: Clarified that the mpv plugin requirement applies to setup on every platform, while the optional `SubMiner mpv` shortcut remains the recommended Windows playback entry point.
|
||||||
|
- Launcher: Streamlined Windows setup and config by making the `SubMiner mpv` shortcut self-contained and keeping `mpv.executablePath` as the simple fallback when `mpv.exe` is not on `PATH`.
|
||||||
|
- Overlay: Changed fresh-install default config to keep texthooker and stats from auto-opening browser tabs.
|
||||||
|
- Overlay: Changed fresh-install default config to enable AnkiConnect, Discord Rich Presence, subtitle-sidebar, and Yomitan-popup auto-pause by default, while disabling controller input by default.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Main: Resolve the YouTube playback socket path lazily so startup honors CLI and config overrides.
|
||||||
|
- Main: Add regression coverage for the lazy socket-path lookup during Windows mpv startup.
|
||||||
|
- Main: Keep integrated `--start --texthooker` launches on the full app-ready startup path so the texthooker page and websocket servers start together during normal playback startup.
|
||||||
|
- Main: Stop the mpv/plugin auto-start flow from spawning a separate standalone texthooker helper during normal `subminer <video>` launches.
|
||||||
|
- Overlay: Keep tracked macOS visible overlays click-through by default so subtitle sidebar passthrough works immediately without requiring a subtitle hover cycle first.
|
||||||
|
- Overlay: Add regression coverage for the macOS visible-overlay passthrough default.
|
||||||
|
- Anilist: Stop AniList post-watch from sending a second progress update when the current episode was already satisfied by a ready retry item in the same watch-completion pass.
|
||||||
|
- Anilist: Add regression coverage for the retry-queue plus live-update duplicate path.
|
||||||
|
- Overlay: Fixed Kiku duplicate grouping to reuse duplicate note IDs from both generic sentence-card creation and Yomitan popup mining instead of running extra duplicate scans after add.
|
||||||
|
- Overlay: Fixed the Yomitan popup mining flow to add cards in the background while keeping the stock popup progress feedback, then pause playback and close the lookup popup before the Kiku merge modal opens.
|
||||||
|
- Overlay: Fixed configured subtitle-jump keybindings so backward and forward subtitle seeks keep playback paused when invoked from a paused state.
|
||||||
|
- Launcher: Fixed the Windows `SubMiner mpv` shortcut and `SubMiner.exe --launch-mpv` flow to launch mpv with SubMiner's required default args directly instead of requiring an `mpv.conf` profile named `subminer`.
|
||||||
|
- Launcher: Clarified the Windows install and usage docs so the shortcut path is documented as self-contained, while the optional `subminer` mpv profile remains available for manual mpv launches.
|
||||||
|
- Launcher: Hardened the first-run setup blocker copy and stale custom-scheme handling so setup messages stay aligned with config, plugin, and dictionary readiness.
|
||||||
|
- Launcher: Fixed the Windows `SubMiner mpv` shortcut idle launch so loading a video after opening the shortcut keeps mpv in the expected SubMiner-managed session, auto-starts the overlay, and re-arms subtitle auto-selection for the newly opened file.
|
||||||
|
- Launcher: Removed the redundant `.` subtitle search path from the Windows shortcut launch args and deduped repeated subtitle source tracks in the manual sync picker so duplicate external subtitle entries no longer appear from the shortcut path.
|
||||||
|
- Playback: Fixed managed local playback so duplicate startup-ready retries no longer unpause media after a later manual pause on the same file.
|
||||||
|
- Playback: Fixed managed local subtitle auto-selection so local files reuse configured primary and secondary subtitle language priorities instead of staying on mpv's initial `sid=auto` guess.
|
||||||
|
- Launcher: Added a blank-by-default `mpv.executablePath` override for Windows playback so users can point SubMiner at `mpv.exe` when it is not on `PATH`.
|
||||||
|
- Launcher: Kept the Windows shortcut and `--launch-mpv` flow simple by preserving PATH auto-discovery as the default and exposing the override in first-run setup.
|
||||||
|
- Launcher: Added `windows` as a recognized launcher backend option and auto-detection target on Windows.
|
||||||
|
- Launcher: Honored `SUBMINER_YTDLP_BIN` consistently across YouTube playback URL resolution, track probing, subtitle downloads, and metadata probing.
|
||||||
|
- Launcher: Kept the first-run setup window from navigating away on unexpected URLs.
|
||||||
|
- Launcher: Made Windows mpv honor an explicitly configured executable path instead of silently falling back to PATH.
|
||||||
|
- Launcher: Hardened `--launch-mpv` parsing and Windows binary resolution so valueless flags do not swallow media targets and symlinked launcher installs do not short-circuit PATH lookup.
|
||||||
|
- Launcher: Fixed first-run setup blocking playback on macOS when the SubMiner mpv plugin was already installed at the canonical `~/.config/mpv` path.
|
||||||
|
- Launcher: Fixed setup gating so stale cancelled setup state no longer prevents playback when the canonical mpv plugin entrypoint already exists.
|
||||||
|
- Playback: Prevented stale async playlist-browser subtitle rearm callbacks from overriding newer subtitle selections during rapid file changes.
|
||||||
|
|
||||||
|
### Docs
|
||||||
|
|
||||||
|
- Docs Site: Added a dedicated Subtitle Sidebar guide and linked it from the homepage and configuration docs.
|
||||||
|
- Docs Site: Linked Jimaku integration from the homepage to its dedicated docs page.
|
||||||
|
- Docs Site: Refreshed docs-site theme tokens and hover/selection styling for the updated pages.
|
||||||
|
|
||||||
|
### Internal
|
||||||
|
|
||||||
|
- Release: Retried AUR clone and push operations in the tagged release workflow.
|
||||||
|
- Release: Kept GitHub Releases green when AUR publish flakes and needs manual follow-up.
|
||||||
|
- Release: Updated Electron to 39.8.6 and pinned patched transitive build dependencies to clear the reported high-severity audit findings.
|
||||||
|
|
||||||
|
## v0.10.0 (2026-03-29)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Integrations: Replaced the deprecated Discord Rich Presence wrapper with the maintained `@xhayper/discord-rpc` package.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Stats: Fixed stats startup so the immersion tracker can run when `Bun.serve` is unavailable.
|
||||||
|
- Stats: Stats server now falls back to a Node `http` listener in Electron/runtime paths that do not expose Bun.
|
||||||
|
- Overlay: Fixed the macOS visible-overlay toggle path so manual hides stay hidden and the plugin uses the explicit visible-overlay toggle command.
|
||||||
|
- Subtitle Sidebar: Restored macOS mpv passthrough while the overlay subtitle sidebar is open so clicks outside the sidebar can refocus mpv and keep native keybindings working.
|
||||||
|
|
||||||
|
### Internal
|
||||||
|
|
||||||
|
- Release: Added a maintained source coverage lane that shards Bun coverage one test file at a time and merges LCOV output into `coverage/test-src/lcov.info`.
|
||||||
|
- Release: CI and release quality-gate now upload the merged source-lane LCOV artifact for inspection.
|
||||||
|
- Runtime: Extracted remaining inline runtime logic from `src/main.ts` into dedicated runtime modules and composer helpers.
|
||||||
|
- Runtime: Added focused regression tests for the extracted runtime/composer boundaries.
|
||||||
|
- Runtime: Updated task tracking notes to mark TASK-238.6 complete and confirm follow-on boot-phase split can be deferred.
|
||||||
|
- Runtime: Split `src/main.ts` boot wiring into dedicated `src/main/boot/services.ts`, `src/main/boot/runtimes.ts`, and `src/main/boot/handlers.ts` modules.
|
||||||
|
- Runtime: Added focused tests for the new boot-phase seams and kept the startup/typecheck/build verification lanes green.
|
||||||
|
- Runtime: Updated internal architecture/task docs to record the boot-phase split and new ownership boundary.
|
||||||
|
|
||||||
|
## v0.9.3 (2026-03-25)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Launcher: Moved YouTube primary subtitle language defaults to `youtube.primarySubLanguages`.
|
||||||
|
- Launcher: Removed the placeholder YouTube subtitle retime step and now uses downloaded primary subtitle tracks directly, so there is no fake path rewrite before playback/sidebar loading.
|
||||||
|
- YouTube: Removed the `src/core/services/youtube/retime` helper and its tests after retiring the internal retime strategy.
|
||||||
|
- Docs: Clarified optional `alass` / `ffsubsync` subtitle-sync requirements and setup steps, including fallback behavior when sync tools are absent.
|
||||||
|
- Launcher: Removed the old `youtubeSubgen.primarySubLanguages` config path from the generated config and docs.
|
||||||
|
|
||||||
|
## v0.9.2 (2026-03-25)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Overlay: Fixed overlay pointer tracking so Windows click-through toggles immediately when the cursor enters or leaves subtitle regions, without waiting for a later hover resync.
|
||||||
|
- Overlay: Fixed Windows overlay window tracking on scaled displays by converting native tracked window bounds to Electron DIP coordinates before applying overlay bounds.
|
||||||
|
- Launcher: Fixed Windows direct `--youtube-play` startup so MPV boots reliably, stays paused until the app-owned subtitle flow is ready, and reuses an already-running SubMiner instance when available.
|
||||||
|
- Launcher: Fixed standalone Windows `--youtube-play` sessions so closing MPV fully exits SubMiner instead of leaving hidden overlay windows or a background process behind.
|
||||||
|
- Overlay: Fixed `subminer <youtube-url>` on Linux so the YouTube playback flow waits for Yomitan to load before creating the overlay window, avoiding the broken lookup popup state that previously required a manual overlay refresh.
|
||||||
|
|
||||||
|
## v0.9.1 (2026-03-24)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Release: Reduced packaged release size by excluding duplicate `extraResources` payload and pruning docs, tests, sourcemaps, and other source-only files from Electron bundles.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Overlay: Restored controller navigation and lookup/mining controls while the subtitle sidebar is open, while keeping true modal dialogs blocking controller actions.
|
||||||
|
- Tokenizer: Fixed subtitle annotation clearing so explanatory contrast endings like `んですけど` are excluded consistently across the shared tokenizer filter and annotation stage.
|
||||||
|
|
||||||
|
## v0.9.0 (2026-03-23)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Docs: Added a new WebSocket / Texthooker API and integration guide covering WebSocket payloads, custom client patterns, mpv plugin automation, and webhook-style relay examples. Linked from configuration and mining workflow docs for easier discovery.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Launcher: Added an app-owned YouTube subtitle flow that pauses mpv, uses absPlayer-style YouTube timedtext parsing/conversion to download subtitle tracks, and injects them as external files before playback resumes.
|
||||||
|
- Launcher: Changed YouTube subtitle startup to auto-load the best-available primary and secondary subtitle tracks at launch instead of forcing the picker modal first. Secondary subtitle failures no longer block playback resume.
|
||||||
|
- Launcher: Added `Ctrl+Alt+C` as the default keybinding to manually open the YouTube subtitle picker during active YouTube playback.
|
||||||
|
- Launcher: Added yt-dlp metadata probing so YouTube playback and immersion tracking record canonical video title and channel metadata.
|
||||||
|
- Launcher: Stopped forcing `--ytdl-raw-options=` before user-provided mpv options so existing YouTube cookie integrations in user `--args` are no longer clobbered.
|
||||||
|
- Launcher: Disabled mpv native YouTube subtitle auto-loading for the app-owned flow so injected external subtitle files remain authoritative.
|
||||||
|
- Launcher: Added OSD status messages for YouTube playback startup, subtitle acquisition, and subtitle loading so the flow stays visible before and during the picker.
|
||||||
|
- Subtitle Sidebar: Added startup-auto-open controls and resume positioning improvements so the sidebar jumps directly to the first resolved active cue.
|
||||||
|
- Subtitle Sidebar: Improved subtitle prefetch and embedded overlay passthrough sync so sidebar and overlay subtitle states stay consistent across media transitions.
|
||||||
|
- Subtitle Sidebar: Updated scroll handling, embedded layout styling, and active-cue visual behavior.
|
||||||
|
- Stats: Stats Library tab now displays YouTube video title, channel name, and channel thumbnail for YouTube media entries, with retry logic to fill in metadata that arrives after initial load.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Launcher: Fixed Anki media mining for mpv YouTube streams by unwrapping the stream URL so audio and screenshot capture work correctly for YouTube playback sessions.
|
||||||
|
- Immersion: Fixed YouTube media path handling in the immersion runtime and tracking so YouTube sessions record correct media references, AniList guessing skips YouTube URLs, and post-watch state transitions do not fire for YouTube media.
|
||||||
|
- Launcher: Fixed startup-launched YouTube playback so primary subtitle overlay updates continue after auto-load completes.
|
||||||
|
- Launcher: Fixed auto-loaded YouTube primary subtitles so parsed cues appear in the subtitle sidebar without needing a manual picker retry.
|
||||||
|
- Launcher: Fixed the YouTube picker to guard against duplicate subtitle submissions and tightened YouTube URL detection so follow-up runtime flows only treat real YouTube hosts as YouTube playback.
|
||||||
|
- Launcher: Fixed primary subtitle failure notifications being shown while app-owned YouTube subtitle probing and downloads are still in flight.
|
||||||
|
- Launcher: Preserved existing authoritative YouTube subtitle tracks when available; downloaded tracks are used only to fill missing sides, and native mpv secondary subtitle rendering is hidden so the overlay remains the sole secondary display.
|
||||||
|
|
||||||
|
## v0.8.0 (2026-03-22)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Overlay: Added the subtitle sidebar feature with a new `subtitleSidebar` configuration surface and rendered sidebar modal with cue list rendering, click-to-seek, active-cue highlighting, and embedded layout support.
|
||||||
|
- IPC: Added sidebar snapshot plumbing between renderer and main process for overlay/sidebar synchronization.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Config: Added hot-reloadable sidebar options for enablement, layout, visibility, typography, opacity, sizing, and interaction behavior (`autoOpen`, `pauseOnHover`, `autoScroll`, toggle key).
|
||||||
|
- Docs: Added full `subtitleSidebar` documentation coverage, including sample config, option table, and toggle shortcut notes.
|
||||||
|
- Runtime: Improved subtitle prefetch/rendering flow so sidebar and overlay subtitle states stay in sync across media transitions.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Overlay: Kept sidebar cue tracking stable across playback transitions and timing edge cases.
|
||||||
|
- Overlay: Improved sidebar resume/start behavior to jump directly to the first resolved active cue.
|
||||||
|
- Overlay: Stopped stale subtitle refreshes from regressing active-cue and text state.
|
||||||
|
|
||||||
|
## 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,10 +1,9 @@
|
|||||||
.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 docs-watch 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-windows 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 docs-test docs-build docs-build-versioned docs-dev
|
||||||
|
|
||||||
APP_NAME := subminer
|
APP_NAME := subminer
|
||||||
THEME_SOURCE := assets/themes/subminer.rasi
|
THEME_SOURCE := assets/themes/subminer.rasi
|
||||||
LAUNCHER_OUT := dist/launcher/$(APP_NAME)
|
LAUNCHER_OUT := dist/launcher/$(APP_NAME)
|
||||||
THEME_FILE := subminer.rasi
|
THEME_FILE := subminer.rasi
|
||||||
PLUGIN_CONF := plugin/subminer.conf
|
|
||||||
|
|
||||||
# Default install prefix for the wrapper script.
|
# Default install prefix for the wrapper script.
|
||||||
PREFIX ?= $(HOME)/.local
|
PREFIX ?= $(HOME)/.local
|
||||||
@@ -20,15 +19,11 @@ MACOS_DATA_DIR ?= $(HOME)/Library/Application Support/SubMiner
|
|||||||
MACOS_APP_DIR ?= $(HOME)/Applications
|
MACOS_APP_DIR ?= $(HOME)/Applications
|
||||||
MACOS_APP_DEST ?= $(MACOS_APP_DIR)/SubMiner.app
|
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/.
|
# If building from source, the AppImage will typically land in release/.
|
||||||
APPIMAGE_SRC := $(firstword $(wildcard release/SubMiner-*.AppImage))
|
APPIMAGE_SRC = $(firstword $(wildcard release/SubMiner-*.AppImage))
|
||||||
MACOS_APP_SRC := $(firstword $(wildcard release/*.app release/*/*.app))
|
MACOS_APP_SRC = $(firstword $(wildcard release/*.app release/*/*.app))
|
||||||
MACOS_ZIP_SRC := $(firstword $(wildcard release/SubMiner-*.zip))
|
MACOS_ZIP_SRC = $(firstword $(wildcard release/SubMiner-*.zip))
|
||||||
|
PRERELEASE_NOTES := release/prerelease-notes.md
|
||||||
|
|
||||||
UNAME_S := $(shell uname -s 2>/dev/null || echo Unknown)
|
UNAME_S := $(shell uname -s 2>/dev/null || echo Unknown)
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
@@ -41,6 +36,17 @@ else
|
|||||||
PLATFORM := unknown
|
PLATFORM := unknown
|
||||||
endif
|
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:
|
help:
|
||||||
@printf '%s\n' \
|
@printf '%s\n' \
|
||||||
"Targets:" \
|
"Targets:" \
|
||||||
@@ -56,20 +62,22 @@ help:
|
|||||||
" dev-watch-macos Start watch loop with forced macOS tracker backend" \
|
" dev-watch-macos Start watch loop with forced macOS tracker backend" \
|
||||||
" dev-toggle Toggle overlay in a running local Electron app" \
|
" dev-toggle Toggle overlay in a running local Electron app" \
|
||||||
" dev-stop Stop a running local Electron app" \
|
" dev-stop Stop a running local Electron app" \
|
||||||
" docs-dev Run VitePress docs dev server" \
|
" docs-test Run docs tests" \
|
||||||
" docs-watch Run VitePress docs dev + Backlog browser together" \
|
" docs-build Build the docs site" \
|
||||||
" docs Build VitePress static docs" \
|
" docs-build-versioned Build production versioned docs site" \
|
||||||
" docs-preview Preview built VitePress docs" \
|
" docs-dev Start the docs dev server" \
|
||||||
" install-linux Install Linux wrapper/theme/app artifacts" \
|
" install-linux Install Linux wrapper/theme/app artifacts" \
|
||||||
" install-macos Install macOS wrapper/theme/app artifacts" \
|
" install-macos Install macOS wrapper/theme/app artifacts" \
|
||||||
" install-plugin Install mpv Lua plugin and plugin config" \
|
" install-windows Print Windows packaging/install guidance" \
|
||||||
" generate-config Generate ~/.config/SubMiner/config.jsonc from centralized defaults" \
|
" generate-config Generate ~/.config/SubMiner/config.jsonc from centralized defaults" \
|
||||||
"" \
|
"" \
|
||||||
"Other targets:" \
|
"Other targets:" \
|
||||||
" deps Install JS dependencies (root + texthooker-ui)" \
|
" deps Install JS dependencies (root + stats + texthooker-ui)" \
|
||||||
" uninstall-linux Remove Linux install artifacts" \
|
" uninstall-linux Remove Linux install artifacts" \
|
||||||
" uninstall-macos Remove macOS install artifacts" \
|
" uninstall-macos Remove macOS install artifacts" \
|
||||||
|
" uninstall-windows Remove Windows mpv plugin artifacts" \
|
||||||
" print-dirs Show resolved install locations" \
|
" print-dirs Show resolved install locations" \
|
||||||
|
" lint Lint stats (format check)" \
|
||||||
"" \
|
"" \
|
||||||
"Variables:" \
|
"Variables:" \
|
||||||
" PREFIX=... Override wrapper install prefix (default: $$HOME/.local)" \
|
" PREFIX=... Override wrapper install prefix (default: $$HOME/.local)" \
|
||||||
@@ -78,7 +86,7 @@ help:
|
|||||||
" LINUX_DATA_DIR=... Override Linux app data dir" \
|
" LINUX_DATA_DIR=... Override Linux app data dir" \
|
||||||
" MACOS_DATA_DIR=... Override macOS app data dir" \
|
" MACOS_DATA_DIR=... Override macOS app data dir" \
|
||||||
" MACOS_APP_DIR=... Override macOS app install dir (default: $$HOME/Applications)" \
|
" MACOS_APP_DIR=... Override macOS app install dir (default: $$HOME/Applications)" \
|
||||||
" MPV_CONFIG_DIR=... Override mpv config dir (default: $$HOME/.config/mpv)"
|
" MPV_CONFIG_DIR=... Override mpv config dir (default: $$HOME/.config/mpv or %APPDATA%/mpv on Windows)"
|
||||||
|
|
||||||
print-dirs:
|
print-dirs:
|
||||||
@printf '%s\n' \
|
@printf '%s\n' \
|
||||||
@@ -89,6 +97,10 @@ print-dirs:
|
|||||||
"MACOS_DATA_DIR=$(MACOS_DATA_DIR)" \
|
"MACOS_DATA_DIR=$(MACOS_DATA_DIR)" \
|
||||||
"MACOS_APP_DIR=$(MACOS_APP_DIR)" \
|
"MACOS_APP_DIR=$(MACOS_APP_DIR)" \
|
||||||
"MACOS_APP_DEST=$(MACOS_APP_DEST)" \
|
"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)" \
|
"APPIMAGE_SRC=$(APPIMAGE_SRC)" \
|
||||||
"MACOS_APP_SRC=$(MACOS_APP_SRC)" \
|
"MACOS_APP_SRC=$(MACOS_APP_SRC)" \
|
||||||
"MACOS_ZIP_SRC=$(MACOS_ZIP_SRC)"
|
"MACOS_ZIP_SRC=$(MACOS_ZIP_SRC)"
|
||||||
@@ -96,19 +108,25 @@ print-dirs:
|
|||||||
deps:
|
deps:
|
||||||
@$(MAKE) --no-print-directory ensure-bun
|
@$(MAKE) --no-print-directory ensure-bun
|
||||||
@bun install
|
@bun install
|
||||||
|
@cd stats && bun install --frozen-lockfile
|
||||||
@cd vendor/texthooker-ui && bun install --frozen-lockfile
|
@cd vendor/texthooker-ui && bun install --frozen-lockfile
|
||||||
|
|
||||||
ensure-bun:
|
ensure-bun:
|
||||||
@command -v bun >/dev/null 2>&1 || { printf '%s\n' "[ERROR] bun not found"; exit 1; }
|
@command -v bun >/dev/null 2>&1 || { printf '%s\n' "[ERROR] bun not found"; exit 1; }
|
||||||
|
|
||||||
pretty: ensure-bun
|
pretty: ensure-bun
|
||||||
@bun run format
|
@bun run format:src
|
||||||
|
@bun run format:stats
|
||||||
|
|
||||||
|
lint: ensure-bun
|
||||||
|
@bun run lint:stats
|
||||||
|
|
||||||
build:
|
build:
|
||||||
@printf '%s\n' "[INFO] Detected platform: $(PLATFORM)"
|
@printf '%s\n' "[INFO] Detected platform: $(PLATFORM)"
|
||||||
@case "$(PLATFORM)" in \
|
@case "$(PLATFORM)" in \
|
||||||
linux) $(MAKE) --no-print-directory build-linux ;; \
|
linux) $(MAKE) --no-print-directory build-linux ;; \
|
||||||
macos) $(MAKE) --no-print-directory build-macos ;; \
|
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 ;; \
|
*) printf '%s\n' "[ERROR] Unsupported OS for this Makefile target: $(PLATFORM)"; exit 1 ;; \
|
||||||
esac
|
esac
|
||||||
|
|
||||||
@@ -117,6 +135,7 @@ install:
|
|||||||
@case "$(PLATFORM)" in \
|
@case "$(PLATFORM)" in \
|
||||||
linux) $(MAKE) --no-print-directory install-linux ;; \
|
linux) $(MAKE) --no-print-directory install-linux ;; \
|
||||||
macos) $(MAKE) --no-print-directory install-macos ;; \
|
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 ;; \
|
*) printf '%s\n' "[ERROR] Unsupported OS for this Makefile target: $(PLATFORM)"; exit 1 ;; \
|
||||||
esac
|
esac
|
||||||
|
|
||||||
@@ -147,7 +166,15 @@ build-launcher:
|
|||||||
|
|
||||||
clean:
|
clean:
|
||||||
@printf '%s\n' "[INFO] Removing build artifacts"
|
@printf '%s\n' "[INFO] Removing build artifacts"
|
||||||
@rm -rf dist release
|
@if [ -f "$(PRERELEASE_NOTES)" ]; then \
|
||||||
|
PRERELEASE_NOTES_BACKUP="$$(mktemp -t subminer-prerelease-notes.XXXXXX)" && \
|
||||||
|
cp "$(PRERELEASE_NOTES)" "$$PRERELEASE_NOTES_BACKUP" && \
|
||||||
|
rm -rf dist release && \
|
||||||
|
install -d release && \
|
||||||
|
mv "$$PRERELEASE_NOTES_BACKUP" "$(PRERELEASE_NOTES)"; \
|
||||||
|
else \
|
||||||
|
rm -rf dist release; \
|
||||||
|
fi
|
||||||
@rm -f "$(BINDIR)/subminer" "$(BINDIR)/SubMiner.AppImage"
|
@rm -f "$(BINDIR)/subminer" "$(BINDIR)/SubMiner.AppImage"
|
||||||
|
|
||||||
generate-config: ensure-bun
|
generate-config: ensure-bun
|
||||||
@@ -155,21 +182,8 @@ generate-config: ensure-bun
|
|||||||
@bun run electron . --generate-config
|
@bun run electron . --generate-config
|
||||||
|
|
||||||
generate-example-config: ensure-bun
|
generate-example-config: ensure-bun
|
||||||
@bun run build
|
|
||||||
@bun run generate:config-example
|
@bun run generate:config-example
|
||||||
|
|
||||||
docs-dev: ensure-bun
|
|
||||||
@bun run docs:dev
|
|
||||||
|
|
||||||
docs-watch: ensure-bun
|
|
||||||
@bun run docs:watch
|
|
||||||
|
|
||||||
docs: ensure-bun
|
|
||||||
@bun run docs:build
|
|
||||||
|
|
||||||
docs-preview: ensure-bun
|
|
||||||
@bun run docs:preview
|
|
||||||
|
|
||||||
dev-start: ensure-bun
|
dev-start: ensure-bun
|
||||||
@bun run build
|
@bun run build
|
||||||
@bun run electron . --start
|
@bun run electron . --start
|
||||||
@@ -190,6 +204,18 @@ dev-toggle: ensure-bun
|
|||||||
dev-stop: ensure-bun
|
dev-stop: ensure-bun
|
||||||
@bun run electron . --stop
|
@bun run electron . --stop
|
||||||
|
|
||||||
|
docs-test: ensure-bun
|
||||||
|
@bun run docs:test
|
||||||
|
|
||||||
|
docs-build: ensure-bun
|
||||||
|
@bun run docs:build
|
||||||
|
|
||||||
|
docs-build-versioned: ensure-bun
|
||||||
|
@bun run docs:build:versioned
|
||||||
|
|
||||||
|
docs-dev: ensure-bun
|
||||||
|
@bun run docs:dev
|
||||||
|
|
||||||
|
|
||||||
install-linux: build-launcher
|
install-linux: build-launcher
|
||||||
@printf '%s\n' "[INFO] Installing Linux wrapper/theme artifacts"
|
@printf '%s\n' "[INFO] Installing Linux wrapper/theme artifacts"
|
||||||
@@ -197,6 +223,8 @@ install-linux: build-launcher
|
|||||||
@install -m 0755 "$(LAUNCHER_OUT)" "$(BINDIR)/$(APP_NAME)"
|
@install -m 0755 "$(LAUNCHER_OUT)" "$(BINDIR)/$(APP_NAME)"
|
||||||
@install -d "$(LINUX_DATA_DIR)/themes"
|
@install -d "$(LINUX_DATA_DIR)/themes"
|
||||||
@install -m 0644 "./$(THEME_SOURCE)" "$(LINUX_DATA_DIR)/themes/$(THEME_FILE)"
|
@install -m 0644 "./$(THEME_SOURCE)" "$(LINUX_DATA_DIR)/themes/$(THEME_FILE)"
|
||||||
|
@install -d "$(LINUX_DATA_DIR)/plugin/subminer"
|
||||||
|
@cp -R ./plugin/subminer/. "$(LINUX_DATA_DIR)/plugin/subminer/"
|
||||||
@if [ -n "$(APPIMAGE_SRC)" ]; then \
|
@if [ -n "$(APPIMAGE_SRC)" ]; then \
|
||||||
install -m 0755 "$(APPIMAGE_SRC)" "$(BINDIR)/SubMiner.AppImage"; \
|
install -m 0755 "$(APPIMAGE_SRC)" "$(BINDIR)/SubMiner.AppImage"; \
|
||||||
else \
|
else \
|
||||||
@@ -211,6 +239,8 @@ install-macos: build-launcher
|
|||||||
@install -m 0755 "$(LAUNCHER_OUT)" "$(BINDIR)/$(APP_NAME)"
|
@install -m 0755 "$(LAUNCHER_OUT)" "$(BINDIR)/$(APP_NAME)"
|
||||||
@install -d "$(MACOS_DATA_DIR)/themes"
|
@install -d "$(MACOS_DATA_DIR)/themes"
|
||||||
@install -m 0644 "./$(THEME_SOURCE)" "$(MACOS_DATA_DIR)/themes/$(THEME_FILE)"
|
@install -m 0644 "./$(THEME_SOURCE)" "$(MACOS_DATA_DIR)/themes/$(THEME_FILE)"
|
||||||
|
@install -d "$(MACOS_DATA_DIR)/plugin/subminer"
|
||||||
|
@cp -R ./plugin/subminer/. "$(MACOS_DATA_DIR)/plugin/subminer/"
|
||||||
@install -d "$(MACOS_APP_DIR)"
|
@install -d "$(MACOS_APP_DIR)"
|
||||||
@if [ -n "$(MACOS_APP_SRC)" ]; then \
|
@if [ -n "$(MACOS_APP_SRC)" ]; then \
|
||||||
rm -rf "$(MACOS_APP_DEST)"; \
|
rm -rf "$(MACOS_APP_DEST)"; \
|
||||||
@@ -226,26 +256,33 @@ install-macos: build-launcher
|
|||||||
fi
|
fi
|
||||||
@printf '%s\n' "Installed to:" " $(BINDIR)/subminer" " $(MACOS_DATA_DIR)/themes/$(THEME_FILE)" " $(MACOS_APP_DEST)"
|
@printf '%s\n' "Installed to:" " $(BINDIR)/subminer" " $(MACOS_DATA_DIR)/themes/$(THEME_FILE)" " $(MACOS_APP_DEST)"
|
||||||
|
|
||||||
install-plugin:
|
install-windows:
|
||||||
@printf '%s\n' "[INFO] Installing mpv plugin artifacts"
|
@printf '%s\n' "[INFO] Windows builds run via: bun run build:win"
|
||||||
@install -d "$(MPV_SCRIPTS_DIR)"
|
@printf '%s\n' "[INFO] SubMiner-managed mpv launches inject the bundled runtime plugin; no global mpv plugin install is needed."
|
||||||
@rm -f "$(MPV_SCRIPTS_DIR)/subminer.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_CONF)" "$(MPV_SCRIPT_OPTS_DIR)/subminer.conf"
|
|
||||||
@printf '%s\n' "Installed to:" " $(MPV_SCRIPTS_DIR)/subminer/main.lua" " $(MPV_SCRIPTS_DIR)/subminer/" " $(MPV_SCRIPT_OPTS_DIR)/subminer.conf"
|
|
||||||
|
|
||||||
# Uninstall behavior kept unchanged by default.
|
uninstall:
|
||||||
uninstall: uninstall-linux
|
@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-linux:
|
uninstall-linux:
|
||||||
@rm -f "$(BINDIR)/subminer" "$(BINDIR)/SubMiner.AppImage"
|
@rm -f "$(BINDIR)/subminer" "$(BINDIR)/SubMiner.AppImage"
|
||||||
@rm -f "$(LINUX_DATA_DIR)/themes/$(THEME_FILE)"
|
@rm -f "$(LINUX_DATA_DIR)/themes/$(THEME_FILE)"
|
||||||
@printf '%s\n' "Removed:" " $(BINDIR)/subminer" " $(BINDIR)/SubMiner.AppImage" " $(LINUX_DATA_DIR)/themes/$(THEME_FILE)"
|
@rm -rf "$(LINUX_DATA_DIR)/plugin/subminer"
|
||||||
|
@printf '%s\n' "Removed:" " $(BINDIR)/subminer" " $(BINDIR)/SubMiner.AppImage" " $(LINUX_DATA_DIR)/themes/$(THEME_FILE)" " $(LINUX_DATA_DIR)/plugin/subminer"
|
||||||
|
|
||||||
uninstall-macos:
|
uninstall-macos:
|
||||||
@rm -f "$(BINDIR)/subminer"
|
@rm -f "$(BINDIR)/subminer"
|
||||||
@rm -f "$(MACOS_DATA_DIR)/themes/$(THEME_FILE)"
|
@rm -f "$(MACOS_DATA_DIR)/themes/$(THEME_FILE)"
|
||||||
|
@rm -rf "$(MACOS_DATA_DIR)/plugin/subminer"
|
||||||
@rm -rf "$(MACOS_APP_DEST)"
|
@rm -rf "$(MACOS_APP_DEST)"
|
||||||
@printf '%s\n' "Removed:" " $(BINDIR)/subminer" " $(MACOS_DATA_DIR)/themes/$(THEME_FILE)" " $(MACOS_APP_DEST)"
|
@printf '%s\n' "Removed:" " $(BINDIR)/subminer" " $(MACOS_DATA_DIR)/themes/$(THEME_FILE)" " $(MACOS_DATA_DIR)/plugin/subminer" " $(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,104 +1,242 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<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)
|
<img src="assets/SubMiner.png" width="160" alt="SubMiner logo">
|
||||||
[]()
|
|
||||||
[](https://docs.subminer.moe)
|
# SubMiner
|
||||||
|
|
||||||
|
Integrates Yomitan and mpv - on-screen lookups, mine to Anki, and track immersion without leaving the player
|
||||||
|
|
||||||
|
[Installation](#quick-start) · [Requirements](#requirements) · [Usage](https://docs.subminer.moe/usage) · [Documentation](https://docs.subminer.moe)
|
||||||
|
|
||||||
|
[](https://github.com/ksyasuda/SubMiner/releases)
|
||||||
|
[](https://github.com/ksyasuda/SubMiner/releases/latest)
|
||||||
|
[](https://aur.archlinux.org/packages/subminer-bin)
|
||||||
|
[](https://github.com/ksyasuda/SubMiner)
|
||||||
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||||
|
[](https://www.typescriptlang.org)
|
||||||
|
|
||||||
|
[](https://github.com/user-attachments/assets/89e61895-e2b7-4b47-8d50-a35afe4132b2)
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br />
|
## Features
|
||||||
|
|
||||||
|
### Dictionary Lookups
|
||||||
|
|
||||||
|
Hover over any word and trigger a lookup to get the full Yomitan popup - definitions, pitch accent, and frequency data - without ever leaving mpv.
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
<img src="docs-site/public/screenshots/yomitan-lookup.png" width="800" alt="Yomitan dictionary popup over annotated subtitles in mpv">
|
||||||
[](./assets/minecard.mp4)
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br />
|
<br>
|
||||||
|
|
||||||
## What it does
|
### Instant Anki Mining
|
||||||
|
|
||||||
SubMiner is an Electron overlay that sits on top of mpv. It turns your video player into a full sentence-mining workstation:
|
Create an Anki card with the sentence, audio clip, screenshot, and machine translation from the exact playback moment with one key press, click, or controller input.
|
||||||
|
|
||||||
- **Hover to look up** — Yomitan dictionary popups directly on subtitles
|
<div align="center">
|
||||||
- **Keyboard-driven lookup mode** — Navigate token-by-token, keep lookup open across tokens, and control popup scrolling/audio/mining without leaving the overlay
|
<img src="docs-site/public/screenshots/one-key-mining.png" width="800" alt="Anki card created from SubMiner with sentence, audio, and screenshot">
|
||||||
- **One-key mining** — Creates Anki cards with sentence, audio, screenshot, and translation
|
</div>
|
||||||
- **Instant auto-enrichment** — Optional local AnkiConnect proxy enriches new Yomitan cards immediately
|
|
||||||
- **Reading annotations** — Combines N+1 targeting, frequency-dictionary highlighting, and JLPT underlining while you read
|
|
||||||
- **Hover-aware playback** — By default, hovering subtitle text pauses mpv and resumes on mouse leave (`subtitleStyle.autoPauseVideoOnHover`)
|
|
||||||
- **Subtitle tools** — Download from Jimaku, sync with alass/ffsubsync
|
|
||||||
- **Immersion tracking** — SQLite-powered stats on your watch time and mining activity
|
|
||||||
- **Custom texthooker page** — Built-in custom texthooker page and websocket, no extra setup
|
|
||||||
- **Jellyfin integration** — Remote playback setup, cast device mode, and direct playback launch
|
|
||||||
- **AniList progress** — Track episode completion and push watching progress automatically
|
|
||||||
|
|
||||||
## Quick start
|
<br>
|
||||||
|
|
||||||
### 1. Install
|
### Reading Annotations
|
||||||
|
|
||||||
**Linux (AppImage):**
|
Real-time subtitle annotations with frequency highlighting, JLPT tags, N+1 targeting, and a character name dictionary. Grammar-only tokens and particles render as plain text so you focus on what matters.
|
||||||
|
|
||||||
```bash
|
<div align="center">
|
||||||
wget https://github.com/ksyasuda/SubMiner/releases/latest/download/SubMiner.AppImage -O ~/.local/bin/SubMiner.AppImage
|
<img src="docs-site/public/screenshots/annotations.png" width="800" alt="Annotated subtitles with frequency coloring, JLPT underlines, and N+1 targets">
|
||||||
chmod +x ~/.local/bin/SubMiner.AppImage
|
</div>
|
||||||
wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer -O ~/.local/bin/subminer
|
|
||||||
chmod +x ~/.local/bin/subminer
|
|
||||||
|
|
||||||
```
|
<br>
|
||||||
|
|
||||||
> [!NOTE]
|
### Immersion Dashboard
|
||||||
> The `subminer` wrapper uses a [Bun](https://bun.sh) shebang. Make sure `bun` is on your `PATH`.
|
|
||||||
|
|
||||||
**From source** or **macOS** — see the [installation guide](https://docs.subminer.moe/installation#from-source).
|
Local stats dashboard tracking watch time, vocabulary growth, mining throughput, session history, and trends. All stored locally, no third-party tracking.
|
||||||
|
|
||||||
### 2. Install the mpv plugin and configuration file
|
<div align="center">
|
||||||
|
<img src="docs-site/public/screenshots/stats-overview.png" width="800" alt="Stats dashboard showing watch time, cards mined, streaks, and tracking data">
|
||||||
|
</div>
|
||||||
|
|
||||||
```bash
|
<br>
|
||||||
wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer-assets.tar.gz -O /tmp/subminer-assets.tar.gz
|
|
||||||
tar -xzf /tmp/subminer-assets.tar.gz -C /tmp
|
|
||||||
mkdir -p ~/.config/mpv/scripts/subminer
|
|
||||||
mkdir -p ~/.config/mpv/script-opts
|
|
||||||
cp -R /tmp/plugin/subminer/. ~/.config/mpv/scripts/subminer/
|
|
||||||
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
|
### Playlist Browser
|
||||||
|
|
||||||
```bash
|
Browse sibling episode files and the active mpv queue in one overlay modal. Open it with `Ctrl+Alt+P` to append episodes from the current directory, jump to queued items, remove entries, or reorder the playlist without leaving playback.
|
||||||
subminer app --yomitan
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Mine
|
<div align="center">
|
||||||
|
<img src="docs-site/public/screenshots/playlist-browser.png" width="800" alt="Stats dashboard showing watch time, cards mined, streaks, and tracking data">
|
||||||
|
</div>
|
||||||
|
|
||||||
```bash
|
<br>
|
||||||
subminer app --start --background
|
|
||||||
subminer video.mkv # default plugin config auto-starts visible overlay + resumes playback when ready
|
### Integrations
|
||||||
subminer --start video.mkv # optional explicit overlay start when plugin auto_start=no
|
|
||||||
```
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td><b>YouTube</b></td>
|
||||||
|
<td>Auto-loaded yt-dlp subtitle tracks at startup with config-driven primary/secondary language priorities and a manual overlay picker on demand (<code>Ctrl+Alt+C</code>)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><b>AniList</b></td>
|
||||||
|
<td>Automatic episode tracking and progress sync</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><b>Jellyfin</b></td>
|
||||||
|
<td>Browse, launch, and cast media from your Jellyfin server with setup and discovery controls in the app tray</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><b>Jimaku</b></td>
|
||||||
|
<td>Search and download Japanese subtitles</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><b>alass / ffsubsync</b></td>
|
||||||
|
<td>Manual subtitle retiming — requires <code>alass</code> or <code>ffsubsync</code> on your <code>PATH</code> (optional; subtitle syncing is disabled without them)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><b>WebSocket</b></td>
|
||||||
|
<td>Plain subtitle feed plus a dedicated annotated feed for texthooker pages and custom tools</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="docs-site/public/screenshots/texthooker.png" width="800" alt="Texthooker page receiving annotated subtitle lines via WebSocket">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
| Required | Optional |
|
Only **mpv** and Anki+AnkiConnect are required. Everything else is optional but enhances the experience.
|
||||||
| ------------------------------------------ | -------------------------------------------------- |
|
|
||||||
| `bun` | |
|
| Dependency | Status | What it does |
|
||||||
| `mpv` with IPC socket | `yt-dlp` |
|
| -------------------- | ----------- | ---------------------------------------- |
|
||||||
| `ffmpeg` | `guessit` (better AniSkip title/episode detection) |
|
| mpv | Required | The video player SubMiner overlays on |
|
||||||
| `mecab` + `mecab-ipadic` | `fzf` / `rofi` |
|
| Anki + AnkiConnect | Required | Card creation from the Yomitan popup |
|
||||||
| Linux: `hyprctl` or `xdotool` + `xwininfo` | `chafa`, `ffmpegthumbnailer` |
|
| ffmpeg | Recommended | Audio clips & screenshots for Anki cards |
|
||||||
| macOS: Accessibility permission | |
|
| MeCab + mecab-ipadic | Recommended | More precise annotations and filtering |
|
||||||
|
| yt-dlp | Optional | YouTube playback |
|
||||||
|
| fzf / rofi | Optional | Video picker in the launcher |
|
||||||
|
| alass / ffsubsync | Optional | Subtitle sync |
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>Platform-specific install commands</b></summary>
|
||||||
|
|
||||||
|
**Arch Linux:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo pacman -S --needed mpv ffmpeg mecab mecab-ipadic
|
||||||
|
```
|
||||||
|
|
||||||
|
**macOS:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install mpv ffmpeg mecab mecab-ipadic
|
||||||
|
```
|
||||||
|
|
||||||
|
**Windows:** Install [mpv](https://mpv.io/installation/) and [ffmpeg](https://ffmpeg.org/download.html) and ensure both are on `PATH`.
|
||||||
|
|
||||||
|
See the [full requirements list](https://docs.subminer.moe/installation#1-install-requirements) for optional dependencies.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### 1. Install SubMiner
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>Arch Linux (AUR)</b></summary>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
paru -S subminer-bin
|
||||||
|
```
|
||||||
|
|
||||||
|
</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
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>macOS (DMG)</b></summary>
|
||||||
|
|
||||||
|
Download the latest DMG from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest) and drag `SubMiner.app` into `/Applications`.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>Windows</b></summary>
|
||||||
|
|
||||||
|
Download and run the latest installer (`.exe`) from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest).
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>From source</b></summary>
|
||||||
|
|
||||||
|
See the [build-from-source guide](https://docs.subminer.moe/installation#from-source).
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### 2. Launch & Set Up
|
||||||
|
|
||||||
|
Run SubMiner and the first-run setup wizard will guide you through importing Yomitan dictionaries and optionally installing the `subminer` command-line launcher.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Linux
|
||||||
|
subminer app --setup
|
||||||
|
|
||||||
|
# macOS — open SubMiner.app, or:
|
||||||
|
subminer app --setup
|
||||||
|
```
|
||||||
|
|
||||||
|
On **Windows**, just run `SubMiner.exe` and the setup will open automatically on first launch.
|
||||||
|
|
||||||
|
### 3. Mine
|
||||||
|
|
||||||
|
```bash
|
||||||
|
subminer video.mkv # launch mpv with SubMiner
|
||||||
|
subminer /path/to/dir # pick a file with fzf
|
||||||
|
subminer -R /path/to/dir # pick a file with rofi (Linux only)
|
||||||
|
```
|
||||||
|
|
||||||
|
On **Windows**, use the **SubMiner mpv** shortcut created during setup. Double-click it or drag a video file onto it.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
For full guides on configuration, Anki, Jellyfin, and more, see [docs.subminer.moe](https://docs.subminer.moe).
|
Full guides on configuration, Anki setup, Jellyfin, immersion tracking, and more: **[docs.subminer.moe](https://docs.subminer.moe)**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Acknowledgments
|
## Acknowledgments
|
||||||
|
|
||||||
Built on the shoulders of [GameSentenceMiner](https://github.com/bpwhelan/GameSentenceMiner), [texthooker-ui](https://github.com/Renji-XD/texthooker-ui), [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).
|
SubMiner builds on the work of these open-source projects:
|
||||||
|
|
||||||
|
| Project | Role |
|
||||||
|
| ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
||||||
|
| [Anacreon-Script](https://github.com/friedrich-de/Anacreon-Script) | Inspiration for the mining workflow |
|
||||||
|
| [asbplayer](https://github.com/killergerbah/asbplayer) | Inspiration for subtitle sidebar and logic for YouTube subtitle parsing |
|
||||||
|
| [Bee's Character Dictionary](https://github.com/bee-san/Japanese_Character_Name_Dictionary) | Character name recognition in subtitles |
|
||||||
|
| [GameSentenceMiner](https://github.com/bpwhelan/GameSentenceMiner) | Inspiration for Electron overlay with Yomitan integration |
|
||||||
|
| [jellyfin-mpv-shim](https://github.com/jellyfin/jellyfin-mpv-shim) | Jellyfin integration |
|
||||||
|
| [Jimaku.cc](https://jimaku.cc) | Japanese subtitle search and downloads |
|
||||||
|
| [Renji's Texthooker Page](https://github.com/Renji-XD/texthooker-ui) | Base for the WebSocket texthooker integration |
|
||||||
|
| [Yomitan](https://github.com/yomidevs/yomitan) | Dictionary engine powering all lookups and the morphological parser |
|
||||||
|
| [yomitan-jlpt-vocab](https://github.com/stephenmk/yomitan-jlpt-vocab) | JLPT level tags for vocabulary |
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 21 MiB After Width: | Height: | Size: 3.0 MiB |
@@ -1,16 +0,0 @@
|
|||||||
project_name: 'SubMiner'
|
|
||||||
default_status: 'To Do'
|
|
||||||
statuses: ['To Do', 'In Progress', 'Done']
|
|
||||||
labels: []
|
|
||||||
definition_of_done: []
|
|
||||||
date_format: yyyy-mm-dd
|
|
||||||
max_column_width: 20
|
|
||||||
default_editor: 'nvim'
|
|
||||||
auto_open_browser: false
|
|
||||||
default_port: 6420
|
|
||||||
remote_operations: true
|
|
||||||
auto_commit: false
|
|
||||||
bypass_git_hooks: false
|
|
||||||
check_active_branches: true
|
|
||||||
active_branch_days: 30
|
|
||||||
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.
|
|
||||||
@@ -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 -->
|
|
||||||
@@ -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,37 +0,0 @@
|
|||||||
---
|
|
||||||
id: TASK-84
|
|
||||||
title: 'Docs Plausible endpoint uses /api/event path'
|
|
||||||
status: Done
|
|
||||||
assignee: []
|
|
||||||
created_date: '2026-03-03 00:00'
|
|
||||||
updated_date: '2026-03-03 00:00'
|
|
||||||
labels: []
|
|
||||||
dependencies: []
|
|
||||||
priority: medium
|
|
||||||
ordinal: 12000
|
|
||||||
---
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
|
||||||
|
|
||||||
Fix VitePress docs Plausible tracker config to post to hosted worker API event endpoint instead of worker root URL.
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:END -->
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
<!-- AC:BEGIN -->
|
|
||||||
|
|
||||||
- [x] #1 Docs theme Plausible `endpoint` points to `https://worker.subminer.moe/api/event`.
|
|
||||||
- [x] #2 Plausible docs test asserts `/api/event` endpoint path.
|
|
||||||
|
|
||||||
<!-- AC:END -->
|
|
||||||
|
|
||||||
## Final Summary
|
|
||||||
|
|
||||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
|
||||||
|
|
||||||
Updated docs Plausible tracker endpoint to `https://worker.subminer.moe/api/event` and updated regression test expectation accordingly.
|
|
||||||
|
|
||||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
---
|
|
||||||
id: TASK-84
|
|
||||||
title: Migrate AniSkip metadata+lookup orchestration to launcher/Electron
|
|
||||||
status: Done
|
|
||||||
assignee:
|
|
||||||
- Codex
|
|
||||||
created_date: '2026-03-03 08:31'
|
|
||||||
updated_date: '2026-03-03 08:35'
|
|
||||||
labels:
|
|
||||||
- enhancement
|
|
||||||
- aniskip
|
|
||||||
- launcher
|
|
||||||
- mpv-plugin
|
|
||||||
dependencies: []
|
|
||||||
references:
|
|
||||||
- launcher/aniskip-metadata.ts
|
|
||||||
- launcher/mpv.ts
|
|
||||||
- plugin/subminer/aniskip.lua
|
|
||||||
- plugin/subminer/options.lua
|
|
||||||
- plugin/subminer/state.lua
|
|
||||||
- plugin/subminer/lifecycle.lua
|
|
||||||
- plugin/subminer/messages.lua
|
|
||||||
- plugin/subminer.conf
|
|
||||||
- launcher/aniskip-metadata.test.ts
|
|
||||||
documentation:
|
|
||||||
- docs/mpv-plugin.md
|
|
||||||
- launcher/aniskip-metadata.ts
|
|
||||||
- plugin/subminer/aniskip.lua
|
|
||||||
- docs/architecture.md
|
|
||||||
priority: medium
|
|
||||||
---
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
|
||||||
|
|
||||||
Move AniSkip MAL/title-to-MAL lookup and intro payload resolution from mpv Lua to launcher Electron flow, while keeping mpv-side intro skip UX and chapter/chapter prompt behavior in plugin. Launcher should infer/analyze file metadata, fetch AniSkip payload when launching files, and pass resolved skip window via script options; plugin should trust launcher payload and fall back only when absent.
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:END -->
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
<!-- AC:BEGIN -->
|
|
||||||
|
|
||||||
- [x] #1 Launcher infers AniSkip metadata for file targets using existing guessit/fallback logic and performs AniSkip MAL + payload resolution during mpv startup.
|
|
||||||
- [x] #2 Launcher injects script options containing resolved MAL id and intro window fields (or explicit lookup-failure status) into mpv startup.
|
|
||||||
- [x] #3 Lua plugin consumes launcher-provided AniSkip intro data and skips all network lookups when payload is present.
|
|
||||||
- [x] #4 Standalone mpv/plugin usage without launcher payload continues to function using existing async in-plugin lookup path.
|
|
||||||
- [x] #5 Docs and defaults are updated to document new script-option contract.
|
|
||||||
- [x] #6 Launcher tests cover payload generation contract and fallback behavior where metadata is unavailable.
|
|
||||||
<!-- AC:END -->
|
|
||||||
|
|
||||||
## Implementation Plan
|
|
||||||
|
|
||||||
<!-- SECTION:PLAN:BEGIN -->
|
|
||||||
|
|
||||||
1. Add launcher-side AniSkip payload resolution helpers in launcher/aniskip-metadata.ts (MAL prefix lookup + AniSkip payload fetch + result normalization).
|
|
||||||
2. Wire launcher/mpv.ts + buildSubminerScriptOpts to pass resolved AniSkip fields/mode in --script-opts for file playback.
|
|
||||||
3. Update plugin/subminer/aniskip.lua plus options/state to consume injected payload: if intro_start/end present, apply immediately and skip network lookup; otherwise retain existing async behavior.
|
|
||||||
4. Ensure fallback for standalone mpv usage remains intact for no-launcher/manual refresh.
|
|
||||||
5. Add/update tests/docs/config references for new script-opt contract and edge cases.
|
|
||||||
<!-- SECTION:PLAN:END -->
|
|
||||||
|
|
||||||
## Final Summary
|
|
||||||
|
|
||||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
|
||||||
|
|
||||||
Executed end-to-end migration so launcher resolves AniSkip title/MAL/payload before mpv start and injects it via --script-opts. Plugin now parses and consumes launcher payload (JSON/url/base64), applies OP intro from payload, tracks payload metadata in state, and keeps legacy async lookup path for non-launcher/absent payload playback. Added launcher config key aniskip_payload and updated launcher/aniskip-metadata tests for resolve/payload behavior and contract validation.
|
|
||||||
|
|
||||||
<!-- 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 -->
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
---
|
|
||||||
id: TASK-86
|
|
||||||
title: 'Renderer: keyboard-driven Yomitan lookup mode and popup key forwarding'
|
|
||||||
status: Done
|
|
||||||
assignee:
|
|
||||||
- Codex
|
|
||||||
created_date: '2026-03-04 13:40'
|
|
||||||
updated_date: '2026-03-05 11:30'
|
|
||||||
labels:
|
|
||||||
- enhancement
|
|
||||||
- renderer
|
|
||||||
- yomitan
|
|
||||||
dependencies:
|
|
||||||
- TASK-77
|
|
||||||
references:
|
|
||||||
- src/renderer/handlers/keyboard.ts
|
|
||||||
- src/renderer/handlers/mouse.ts
|
|
||||||
- src/renderer/renderer.ts
|
|
||||||
- src/renderer/state.ts
|
|
||||||
- src/renderer/yomitan-popup.ts
|
|
||||||
- src/core/services/overlay-window.ts
|
|
||||||
- src/preload.ts
|
|
||||||
- src/shared/ipc/contracts.ts
|
|
||||||
- src/types.ts
|
|
||||||
- vendor/yomitan/js/app/frontend.js
|
|
||||||
- vendor/yomitan/js/app/popup.js
|
|
||||||
- vendor/yomitan/js/display/display.js
|
|
||||||
- vendor/yomitan/js/display/popup-main.js
|
|
||||||
- vendor/yomitan/js/display/display-audio.js
|
|
||||||
documentation:
|
|
||||||
- README.md
|
|
||||||
- docs/usage.md
|
|
||||||
- docs/shortcuts.md
|
|
||||||
priority: medium
|
|
||||||
ordinal: 13000
|
|
||||||
---
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
|
||||||
|
|
||||||
Add true keyboard-driven token lookup flow in overlay:
|
|
||||||
|
|
||||||
- Toggle keyboard token-selection mode and navigate tokens by keyboard (`Arrow` + `HJKL`).
|
|
||||||
- Toggle Yomitan lookup window for selected token via fixed accelerator (`Ctrl/Cmd+Y`) without requiring mouse click.
|
|
||||||
- Preserve keyboard-only workflow while popup is open by forwarding popup keys (`J/K`, `M`, `P`, `[`, `]`) and restoring overlay focus on popup close.
|
|
||||||
- Ensure selection styling and hover metadata tooltips (frequency/JLPT) work for keyboard-selected token.
|
|
||||||
<!-- SECTION:DESCRIPTION:END -->
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
<!-- AC:BEGIN -->
|
|
||||||
|
|
||||||
- [x] #1 Keyboard mode toggle exists and shows visual selection outline for active token.
|
|
||||||
- [x] #2 Navigation works via arrows and vim keys while keyboard mode is enabled.
|
|
||||||
- [x] #3 Lookup window toggles from selected token with `Ctrl/Cmd+Y`; close path restores overlay keyboard focus.
|
|
||||||
- [x] #4 Popup-local controls work via keyboard forwarding (`J/K`, `M`, `P`, `[`, `]`), including mine action.
|
|
||||||
- [x] #5 Frequency/JLPT hover tags render for keyboard-selected token.
|
|
||||||
- [x] #6 Renderer/runtime tests cover new visibility/selection behavior, and docs are updated.
|
|
||||||
<!-- AC:END -->
|
|
||||||
|
|
||||||
## Final Summary
|
|
||||||
|
|
||||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
|
||||||
|
|
||||||
Implemented keyboard-driven Yomitan workflow end-to-end in renderer + bundled Yomitan runtime bridge. Added overlay-level keyboard mode state, token selection sync, lookup toggle routing, popup command forwarding, and focus recovery after popup close. Follow-up fixes kept lookup open while moving between tokens, made popup-local `J/K` and `ArrowUp/ArrowDown` scroll work from overlay-owned focus with key repeat, skipped keyboard/token annotation flow for parser groups that have no dictionary-backed headword, and preserved paused playback when token navigation jumps across subtitle lines. Updated user docs/README to document the final shortcut behavior.
|
|
||||||
|
|
||||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
---
|
|
||||||
id: TASK-87
|
|
||||||
title: >-
|
|
||||||
Codebase health: harden verification and retire dead architecture identified
|
|
||||||
in the March 2026 review
|
|
||||||
status: To Do
|
|
||||||
assignee: []
|
|
||||||
created_date: '2026-03-06 03:19'
|
|
||||||
updated_date: '2026-03-06 03:20'
|
|
||||||
labels:
|
|
||||||
- tech-debt
|
|
||||||
- tests
|
|
||||||
- maintainability
|
|
||||||
milestone: m-0
|
|
||||||
dependencies: []
|
|
||||||
references:
|
|
||||||
- package.json
|
|
||||||
- README.md
|
|
||||||
- src/main.ts
|
|
||||||
- src/anki-integration.ts
|
|
||||||
- src/core/services/immersion-tracker-service.test.ts
|
|
||||||
- src/translators/index.ts
|
|
||||||
- src/subsync/engines.ts
|
|
||||||
- src/subtitle/pipeline.ts
|
|
||||||
documentation:
|
|
||||||
- docs/reports/2026-02-22-task-100-dead-code-report.md
|
|
||||||
priority: high
|
|
||||||
---
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
|
||||||
|
|
||||||
Track the remediation work from the March 6, 2026 code review. The review found that the default test gate only exercises 53 of 241 test files, the dedicated subtitle test lane is a no-op, SQLite-backed immersion tracking tests are conditionally skipped in the standard Bun run, src/main.ts still contains a large dead-symbol backlog, several registry/pipeline modules appear unreferenced from live execution paths, and src/anki-integration.ts remains an oversized orchestration file. This parent task should coordinate a safe sequence: improve verification first, then remove dead code and continue decomposition with good test coverage in place.
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:END -->
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
<!-- AC:BEGIN -->
|
|
||||||
|
|
||||||
- [ ] #1 Child tasks are created for each remediation workstream with explicit dependencies and enough context for an isolated agent to execute them.
|
|
||||||
- [ ] #2 The parent task records the recommended sequencing and parallelization strategy so replacement agents can resume without conversation history.
|
|
||||||
- [ ] #3 Completion of the parent task leaves the repository with a materially more trustworthy test gate, less dead architecture, and clearer ownership boundaries for the main runtime and Anki integration surfaces.
|
|
||||||
<!-- AC:END -->
|
|
||||||
|
|
||||||
## Implementation Plan
|
|
||||||
|
|
||||||
<!-- SECTION:PLAN:BEGIN -->
|
|
||||||
|
|
||||||
Recommended sequencing:
|
|
||||||
|
|
||||||
1. Run TASK-87.1, TASK-87.2, TASK-87.3, and TASK-87.7 first. These are the safety-net and tooling tasks and can largely proceed in parallel.
|
|
||||||
2. Start TASK-87.4 once TASK-87.1 lands so src/main.ts cleanup happens under a more trustworthy test matrix.
|
|
||||||
3. Start TASK-87.5 after TASK-87.1 and TASK-87.2 so dead subsync/pipeline cleanup happens with stronger subtitle and runtime verification.
|
|
||||||
4. Start TASK-87.6 after TASK-87.1 so Anki refactors happen with broader default coverage in place.
|
|
||||||
5. Keep PRs focused: do not combine verification work with architectural cleanup unless a narrow dependency requires it.
|
|
||||||
|
|
||||||
Parallelization guidance:
|
|
||||||
|
|
||||||
- Wave 1 parallel: TASK-87.1, TASK-87.2, TASK-87.3, TASK-87.7
|
|
||||||
- Wave 2 parallel: TASK-87.4, TASK-87.5, TASK-87.6
|
|
||||||
|
|
||||||
Shared review context to restate in child tasks:
|
|
||||||
|
|
||||||
- Standard test scripts currently reference only 53 unique test files out of 241 discovered test and type-test files under src/ and launcher/.
|
|
||||||
- test:subtitle is currently a placeholder echo even though subtitle sync is a user-facing feature.
|
|
||||||
- SQLite-backed immersion tracker tests are conditionally skipped in the standard Bun run.
|
|
||||||
- src/main.ts trips many noUnusedLocals/noUnusedParameters diagnostics.
|
|
||||||
- src/translators/index.ts, src/subsync/engines.ts, src/subtitle/pipeline.ts, src/tokenizers/index.ts, and src/token-mergers/index.ts appeared unreferenced during review and must be re-verified before deletion.
|
|
||||||
<!-- SECTION:PLAN:END -->
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
---
|
|
||||||
id: TASK-87.1
|
|
||||||
title: >-
|
|
||||||
Testing workflow: make standard test commands reflect the maintained test
|
|
||||||
surface
|
|
||||||
status: To Do
|
|
||||||
assignee: []
|
|
||||||
created_date: '2026-03-06 03:19'
|
|
||||||
updated_date: '2026-03-06 03:21'
|
|
||||||
labels:
|
|
||||||
- tests
|
|
||||||
- maintainability
|
|
||||||
milestone: m-0
|
|
||||||
dependencies: []
|
|
||||||
references:
|
|
||||||
- package.json
|
|
||||||
- src/main-entry-runtime.test.ts
|
|
||||||
- src/anki-integration/anki-connect-proxy.test.ts
|
|
||||||
- src/main/runtime/jellyfin-remote-playback.test.ts
|
|
||||||
- src/main/runtime/registry.test.ts
|
|
||||||
documentation:
|
|
||||||
- docs/reports/2026-02-22-task-100-dead-code-report.md
|
|
||||||
parent_task_id: TASK-87
|
|
||||||
priority: high
|
|
||||||
---
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
|
||||||
|
|
||||||
The current package scripts hand-enumerate a small subset of test files, which leaves the standard green signal misleading. A local audit found 241 test/type-test files under src/ and launcher/, but only 53 unique files referenced by the standard package.json test scripts. This task should redesign the runnable test matrix so maintained tests are either executed by the standard commands or intentionally excluded through a documented rule, instead of silently drifting out of coverage.
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:END -->
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
<!-- AC:BEGIN -->
|
|
||||||
|
|
||||||
- [ ] #1 The repository has a documented and reproducible test matrix for standard development commands, including which suites belong in the default lane versus slower or environment-specific lanes.
|
|
||||||
- [ ] #2 The standard test entrypoints stop relying on a brittle hand-maintained allowlist for the currently covered unit and integration suites, or an explicit documented mechanism exists that prevents silent omission of new tests.
|
|
||||||
- [ ] #3 Representative tests that were previously outside the standard lane from src/main/runtime, src/anki-integration, and entry/runtime surfaces are executed by an automated command and included in the documented matrix.
|
|
||||||
- [ ] #4 Documentation for contributors explains which command to run for fast verification, full verification, and environment-specific verification.
|
|
||||||
<!-- AC:END -->
|
|
||||||
|
|
||||||
## Implementation Plan
|
|
||||||
|
|
||||||
<!-- SECTION:PLAN:BEGIN -->
|
|
||||||
|
|
||||||
1. Inventory the current test surface under src/ and launcher/ and compare it to package.json scripts to classify fast, full, slow, and environment-specific suites.
|
|
||||||
2. Replace or reduce the brittle hand-maintained allowlist so new maintained tests do not silently miss the standard matrix.
|
|
||||||
3. Update contributor docs with the intended fast/full/environment-specific commands.
|
|
||||||
4. Verify the new matrix by running the relevant commands and by demonstrating at least one previously omitted runtime/Anki/entry test now belongs to an automated lane.
|
|
||||||
<!-- SECTION:PLAN:END -->
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
---
|
|
||||||
id: TASK-87.2
|
|
||||||
title: >-
|
|
||||||
Subtitle sync verification: replace the no-op subtitle lane with real
|
|
||||||
automated coverage
|
|
||||||
status: To Do
|
|
||||||
assignee: []
|
|
||||||
created_date: '2026-03-06 03:19'
|
|
||||||
updated_date: '2026-03-06 03:21'
|
|
||||||
labels:
|
|
||||||
- tests
|
|
||||||
- subsync
|
|
||||||
milestone: m-0
|
|
||||||
dependencies: []
|
|
||||||
references:
|
|
||||||
- package.json
|
|
||||||
- README.md
|
|
||||||
- src/core/services/subsync.ts
|
|
||||||
- src/core/services/subsync.test.ts
|
|
||||||
- src/subsync/utils.ts
|
|
||||||
documentation:
|
|
||||||
- docs/reports/2026-02-22-task-100-dead-code-report.md
|
|
||||||
parent_task_id: TASK-87
|
|
||||||
priority: high
|
|
||||||
---
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
|
||||||
|
|
||||||
SubMiner advertises subtitle syncing with alass and ffsubsync, but the dedicated test:subtitle command currently does not run any tests. There is already lower-level coverage in src/core/services/subsync.test.ts, but the test matrix and contributor-facing commands do not reflect that reality. This task should replace the no-op lane with real verification, align scripts with the existing subsync test surface, and make the user-facing docs honest about how subtitle sync is verified.
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:END -->
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
<!-- AC:BEGIN -->
|
|
||||||
|
|
||||||
- [ ] #1 The test:subtitle entrypoint runs real automated verification instead of echoing a placeholder message.
|
|
||||||
- [ ] #2 The subtitle verification lane covers both alass and ffsubsync behavior, including at least one non-happy-path scenario relevant to current functionality.
|
|
||||||
- [ ] #3 Contributor-facing documentation points to the real subtitle verification command and no longer implies a dedicated test lane exists when it does not.
|
|
||||||
- [ ] #4 The resulting verification strategy integrates cleanly with the repository-wide test matrix without duplicating or hiding existing subsync coverage.
|
|
||||||
<!-- AC:END -->
|
|
||||||
|
|
||||||
## Implementation Plan
|
|
||||||
|
|
||||||
<!-- SECTION:PLAN:BEGIN -->
|
|
||||||
|
|
||||||
1. Audit the existing subtitle-sync test surface, especially src/core/services/subsync.test.ts, and decide whether test:subtitle should reuse or regroup that coverage.
|
|
||||||
2. Replace the placeholder script with a real automated command and keep the matrix legible alongside TASK-87.1 work.
|
|
||||||
3. Update README or related docs so the advertised subtitle verification path matches reality.
|
|
||||||
4. Verify both alass and ffsubsync behavior remain covered by the resulting lane.
|
|
||||||
<!-- SECTION:PLAN:END -->
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
---
|
|
||||||
id: TASK-87.3
|
|
||||||
title: >-
|
|
||||||
Immersion tracking verification: make SQLite-backed persistence tests visible
|
|
||||||
and reproducible
|
|
||||||
status: To Do
|
|
||||||
assignee: []
|
|
||||||
created_date: '2026-03-06 03:19'
|
|
||||||
updated_date: '2026-03-06 03:21'
|
|
||||||
labels:
|
|
||||||
- tests
|
|
||||||
- immersion-tracking
|
|
||||||
milestone: m-0
|
|
||||||
dependencies: []
|
|
||||||
references:
|
|
||||||
- src/core/services/immersion-tracker-service.test.ts
|
|
||||||
- src/core/services/immersion-tracker/storage-session.test.ts
|
|
||||||
- src/core/services/immersion-tracker-service.ts
|
|
||||||
- package.json
|
|
||||||
documentation:
|
|
||||||
- docs/reports/2026-02-22-task-100-dead-code-report.md
|
|
||||||
parent_task_id: TASK-87
|
|
||||||
priority: medium
|
|
||||||
---
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
|
||||||
|
|
||||||
The immersion tracker is persistence-heavy, but its SQLite-backed tests are conditionally skipped in the standard Bun run when node:sqlite support is unavailable. That creates a blind spot around session finalization, telemetry persistence, and retention behavior. This task should establish a reliable automated verification path for the database-backed cases and make the prerequisite/runtime behavior explicit to contributors and CI.
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:END -->
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
<!-- AC:BEGIN -->
|
|
||||||
|
|
||||||
- [ ] #1 Database-backed immersion tracking tests run in at least one documented automated command that is practical for contributors or CI to execute.
|
|
||||||
- [ ] #2 If the current runtime cannot execute the SQLite-backed tests, the repository exposes that limitation clearly instead of silently reporting a misleading green result.
|
|
||||||
- [ ] #3 Contributor-facing documentation explains how to run the immersion tracker verification lane and any environment prerequisites it depends on.
|
|
||||||
- [ ] #4 The resulting verification covers session persistence or finalization behavior that is not exercised by the pure seam tests alone.
|
|
||||||
<!-- AC:END -->
|
|
||||||
|
|
||||||
## Implementation Plan
|
|
||||||
|
|
||||||
<!-- SECTION:PLAN:BEGIN -->
|
|
||||||
|
|
||||||
1. Confirm which SQLite-backed immersion tests are currently skipped and why in the standard Bun environment.
|
|
||||||
2. Establish a reproducible command or lane for the DB-backed cases, or make the unsupported-runtime limitation explicit and actionable.
|
|
||||||
3. Document prerequisites and expected behavior for contributors and CI.
|
|
||||||
4. Verify at least one persistence/finalization path beyond the seam tests is exercised by the new lane.
|
|
||||||
<!-- SECTION:PLAN:END -->
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
---
|
|
||||||
id: TASK-87.4
|
|
||||||
title: >-
|
|
||||||
Runtime composition root: remove dead symbols and tighten module boundaries in
|
|
||||||
src/main.ts
|
|
||||||
status: To Do
|
|
||||||
assignee: []
|
|
||||||
created_date: '2026-03-06 03:19'
|
|
||||||
updated_date: '2026-03-06 03:21'
|
|
||||||
labels:
|
|
||||||
- tech-debt
|
|
||||||
- runtime
|
|
||||||
- maintainability
|
|
||||||
milestone: m-0
|
|
||||||
dependencies:
|
|
||||||
- TASK-87.1
|
|
||||||
references:
|
|
||||||
- src/main.ts
|
|
||||||
- src/main/runtime
|
|
||||||
- package.json
|
|
||||||
documentation:
|
|
||||||
- docs/reports/2026-02-22-task-100-dead-code-report.md
|
|
||||||
parent_task_id: TASK-87
|
|
||||||
priority: high
|
|
||||||
---
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
|
||||||
|
|
||||||
A noUnusedLocals/noUnusedParameters compile pass reports a large concentration of dead imports and dead locals in src/main.ts. The file is also far beyond the repo’s preferred size guideline, which makes the runtime composition root difficult to review and easy to break. This task should remove confirmed dead symbols, continue extracting coherent slices where that improves readability, and leave the entrypoint materially easier to understand without changing behavior.
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:END -->
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
<!-- AC:BEGIN -->
|
|
||||||
|
|
||||||
- [ ] #1 src/main.ts no longer emits dead-symbol diagnostics under a noUnusedLocals/noUnusedParameters compile pass for the areas touched by this cleanup.
|
|
||||||
- [ ] #2 Unused imports, destructured values, and stale locals identified in the current composition root are removed or relocated without behavior changes.
|
|
||||||
- [ ] #3 The resulting composition root has clearer ownership boundaries for at least one runtime slice that is currently buried in the monolith.
|
|
||||||
- [ ] #4 Relevant runtime and startup verification commands pass after the cleanup, and any command changes are documented if needed.
|
|
||||||
<!-- AC:END -->
|
|
||||||
|
|
||||||
## Implementation Plan
|
|
||||||
|
|
||||||
<!-- SECTION:PLAN:BEGIN -->
|
|
||||||
|
|
||||||
1. Re-run the noUnusedLocals/noUnusedParameters compile pass and capture the src/main.ts diagnostics cluster before editing.
|
|
||||||
2. Remove dead imports, destructured values, and stale locals in small reviewable slices; extract a coherent helper/module only where that materially reduces coupling.
|
|
||||||
3. Keep changes behavior-preserving and avoid mixing unrelated cleanup outside src/main.ts unless required to compile.
|
|
||||||
4. Verify with the updated runtime/startup test commands from TASK-87.1 plus a noUnused compile pass.
|
|
||||||
<!-- SECTION:PLAN:END -->
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
---
|
|
||||||
id: TASK-87.5
|
|
||||||
title: >-
|
|
||||||
Dead architecture cleanup: delete unused registry and pipeline modules that
|
|
||||||
are off the live path
|
|
||||||
status: To Do
|
|
||||||
assignee: []
|
|
||||||
created_date: '2026-03-06 03:20'
|
|
||||||
updated_date: '2026-03-06 03:21'
|
|
||||||
labels:
|
|
||||||
- tech-debt
|
|
||||||
- dead-code
|
|
||||||
milestone: m-0
|
|
||||||
dependencies:
|
|
||||||
- TASK-87.1
|
|
||||||
- TASK-87.2
|
|
||||||
references:
|
|
||||||
- src/translators/index.ts
|
|
||||||
- src/subsync/engines.ts
|
|
||||||
- src/subtitle/pipeline.ts
|
|
||||||
- src/tokenizers/index.ts
|
|
||||||
- src/token-mergers/index.ts
|
|
||||||
- src/core/services/subsync.ts
|
|
||||||
- src/core/services/tokenizer.ts
|
|
||||||
documentation:
|
|
||||||
- docs/reports/2026-02-22-task-100-dead-code-report.md
|
|
||||||
parent_task_id: TASK-87
|
|
||||||
priority: high
|
|
||||||
---
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
|
||||||
|
|
||||||
The review found several modules that appear self-contained but unused from the application’s live execution paths: src/translators/index.ts, src/subsync/engines.ts, src/subtitle/pipeline.ts, src/tokenizers/index.ts, and src/token-mergers/index.ts. At the same time, the real runtime behavior is implemented elsewhere. This task should verify those modules are truly unused, remove or consolidate them, and clean up any stale exports, docs, or tests so contributors are not misled by duplicate architecture.
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:END -->
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
<!-- AC:BEGIN -->
|
|
||||||
|
|
||||||
- [ ] #1 Each candidate module identified in the review is either removed as dead code or justified and reconnected to a real supported execution path.
|
|
||||||
- [ ] #2 Any stale exports, imports, or tests associated with the removed or consolidated modules are cleaned up so the codebase has a single obvious path for the affected behavior.
|
|
||||||
- [ ] #3 The cleanup does not regress live tokenization or subtitle sync behavior and the relevant verification commands remain green.
|
|
||||||
- [ ] #4 Contributor-facing documentation or internal notes no longer imply that removed duplicate architecture is part of the current design.
|
|
||||||
<!-- AC:END -->
|
|
||||||
|
|
||||||
## Implementation Plan
|
|
||||||
|
|
||||||
<!-- SECTION:PLAN:BEGIN -->
|
|
||||||
|
|
||||||
1. Re-verify each candidate module is off the live path by tracing imports from current runtime entrypoints before deleting anything.
|
|
||||||
2. Remove or consolidate truly dead modules and clean associated exports/imports/tests so only the supported path remains obvious.
|
|
||||||
3. Pay special attention to subtitle sync and tokenization surfaces, since duplicate architecture exists near active code.
|
|
||||||
4. Verify the relevant tokenization and subsync commands/tests still pass and update any stale docs or notes.
|
|
||||||
<!-- SECTION:PLAN:END -->
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
---
|
|
||||||
id: TASK-87.6
|
|
||||||
title: >-
|
|
||||||
Anki integration maintainability: continue decomposing the oversized
|
|
||||||
orchestration layer
|
|
||||||
status: To Do
|
|
||||||
assignee: []
|
|
||||||
created_date: '2026-03-06 03:20'
|
|
||||||
updated_date: '2026-03-06 03:21'
|
|
||||||
labels:
|
|
||||||
- tech-debt
|
|
||||||
- anki
|
|
||||||
- maintainability
|
|
||||||
milestone: m-0
|
|
||||||
dependencies:
|
|
||||||
- TASK-87.1
|
|
||||||
references:
|
|
||||||
- src/anki-integration.ts
|
|
||||||
- src/anki-integration/field-grouping-workflow.ts
|
|
||||||
- src/anki-integration/note-update-workflow.ts
|
|
||||||
- src/anki-integration/card-creation.ts
|
|
||||||
- src/anki-integration/anki-connect-proxy.ts
|
|
||||||
- src/anki-integration.test.ts
|
|
||||||
documentation:
|
|
||||||
- docs/reports/2026-02-22-task-100-dead-code-report.md
|
|
||||||
- docs/anki-integration.md
|
|
||||||
parent_task_id: TASK-87
|
|
||||||
priority: medium
|
|
||||||
---
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
|
||||||
|
|
||||||
src/anki-integration.ts remains an oversized orchestration file even after earlier extractions. It still mixes config normalization, polling setup, media generation, duplicate resolution, field grouping workflows, and user feedback coordination in one class. This task should continue the decomposition so the remaining orchestration surface is smaller and easier to reason about, while preserving existing Anki, proxy, field grouping, and note update behavior.
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:END -->
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
<!-- AC:BEGIN -->
|
|
||||||
|
|
||||||
- [ ] #1 The responsibilities currently concentrated in src/anki-integration.ts are split into clearer modules or services with narrow ownership boundaries.
|
|
||||||
- [ ] #2 The resulting orchestration surface is materially smaller and easier to review, with at least one mixed-responsibility cluster extracted behind a well-named interface.
|
|
||||||
- [ ] #3 Existing Anki integration behavior remains covered by automated verification, including note update, field grouping, and proxy-related flows that the refactor touches.
|
|
||||||
- [ ] #4 Any developer-facing docs or notes needed to understand the new structure are updated in the same task.
|
|
||||||
<!-- AC:END -->
|
|
||||||
|
|
||||||
## Implementation Plan
|
|
||||||
|
|
||||||
<!-- SECTION:PLAN:BEGIN -->
|
|
||||||
|
|
||||||
1. Map the remaining responsibility clusters inside src/anki-integration.ts and choose one or more extraction seams that reduce mixed concerns without changing behavior.
|
|
||||||
2. Move logic behind narrow interfaces/modules rather than creating another giant helper; keep orchestration readable.
|
|
||||||
3. Preserve coverage for field grouping, note update, proxy, and card creation flows touched by the refactor.
|
|
||||||
4. Update docs or internal notes if the new structure changes where contributors should look for a given behavior.
|
|
||||||
<!-- SECTION:PLAN: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,153 @@
|
|||||||
|
!include "MUI2.nsh"
|
||||||
|
!include "nsDialogs.nsh"
|
||||||
|
|
||||||
|
Var WindowsMpvShortcutStartMenuPath
|
||||||
|
Var WindowsMpvShortcutDesktopPath
|
||||||
|
|
||||||
|
!macro ResolveWindowsMpvShortcutPaths
|
||||||
|
!ifdef MENU_FILENAME
|
||||||
|
StrCpy $WindowsMpvShortcutStartMenuPath "$SMPROGRAMS\${MENU_FILENAME}\SubMiner mpv.lnk"
|
||||||
|
!else
|
||||||
|
StrCpy $WindowsMpvShortcutStartMenuPath "$SMPROGRAMS\SubMiner mpv.lnk"
|
||||||
|
!endif
|
||||||
|
StrCpy $WindowsMpvShortcutDesktopPath "$DESKTOP\SubMiner mpv.lnk"
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
!ifndef BUILD_UNINSTALLER
|
||||||
|
Var WindowsMpvShortcutStartMenuCheckbox
|
||||||
|
Var WindowsMpvShortcutDesktopCheckbox
|
||||||
|
Var WindowsMpvShortcutStartMenuEnabled
|
||||||
|
Var WindowsMpvShortcutDesktopEnabled
|
||||||
|
Var WindowsMpvShortcutDefaultsInitialized
|
||||||
|
|
||||||
|
!macro customInit
|
||||||
|
StrCpy $WindowsMpvShortcutStartMenuEnabled "1"
|
||||||
|
StrCpy $WindowsMpvShortcutDesktopEnabled "1"
|
||||||
|
StrCpy $WindowsMpvShortcutDefaultsInitialized "0"
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
!macro customPageAfterChangeDir
|
||||||
|
PageEx custom
|
||||||
|
PageCallbacks WindowsMpvShortcutPageCreate WindowsMpvShortcutPageLeave
|
||||||
|
Caption " "
|
||||||
|
PageExEnd
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
Function HasExistingInstallation
|
||||||
|
ReadRegStr $0 SHELL_CONTEXT "Software\${APP_GUID}" InstallLocation
|
||||||
|
${if} $0 == ""
|
||||||
|
Push "0"
|
||||||
|
${else}
|
||||||
|
Push "1"
|
||||||
|
${endif}
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
|
Function InitializeWindowsMpvShortcutDefaults
|
||||||
|
${if} $WindowsMpvShortcutDefaultsInitialized == "1"
|
||||||
|
Return
|
||||||
|
${endif}
|
||||||
|
|
||||||
|
!insertmacro ResolveWindowsMpvShortcutPaths
|
||||||
|
Call HasExistingInstallation
|
||||||
|
Pop $0
|
||||||
|
|
||||||
|
${if} $0 == "1"
|
||||||
|
${if} ${FileExists} "$WindowsMpvShortcutStartMenuPath"
|
||||||
|
StrCpy $WindowsMpvShortcutStartMenuEnabled "1"
|
||||||
|
${else}
|
||||||
|
StrCpy $WindowsMpvShortcutStartMenuEnabled "0"
|
||||||
|
${endif}
|
||||||
|
|
||||||
|
${if} ${FileExists} "$WindowsMpvShortcutDesktopPath"
|
||||||
|
StrCpy $WindowsMpvShortcutDesktopEnabled "1"
|
||||||
|
${else}
|
||||||
|
StrCpy $WindowsMpvShortcutDesktopEnabled "0"
|
||||||
|
${endif}
|
||||||
|
${else}
|
||||||
|
StrCpy $WindowsMpvShortcutStartMenuEnabled "1"
|
||||||
|
StrCpy $WindowsMpvShortcutDesktopEnabled "1"
|
||||||
|
${endif}
|
||||||
|
|
||||||
|
StrCpy $WindowsMpvShortcutDefaultsInitialized "1"
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
|
Function WindowsMpvShortcutPageCreate
|
||||||
|
Call InitializeWindowsMpvShortcutDefaults
|
||||||
|
|
||||||
|
!insertmacro MUI_HEADER_TEXT "Windows mpv launcher" "Choose where to create the optional SubMiner mpv shortcuts."
|
||||||
|
|
||||||
|
nsDialogs::Create 1018
|
||||||
|
Pop $0
|
||||||
|
|
||||||
|
${NSD_CreateLabel} 0u 0u 300u 30u "SubMiner mpv launches SubMiner.exe --launch-mpv so people can open mpv with the SubMiner profile from a separate Windows shortcut."
|
||||||
|
Pop $0
|
||||||
|
|
||||||
|
${NSD_CreateCheckbox} 0u 44u 280u 12u "Create Start Menu shortcut"
|
||||||
|
Pop $WindowsMpvShortcutStartMenuCheckbox
|
||||||
|
${if} $WindowsMpvShortcutStartMenuEnabled == "1"
|
||||||
|
${NSD_Check} $WindowsMpvShortcutStartMenuCheckbox
|
||||||
|
${endif}
|
||||||
|
|
||||||
|
${NSD_CreateCheckbox} 0u 64u 280u 12u "Create Desktop shortcut"
|
||||||
|
Pop $WindowsMpvShortcutDesktopCheckbox
|
||||||
|
${if} $WindowsMpvShortcutDesktopEnabled == "1"
|
||||||
|
${NSD_Check} $WindowsMpvShortcutDesktopCheckbox
|
||||||
|
${endif}
|
||||||
|
|
||||||
|
${NSD_CreateLabel} 0u 90u 300u 24u "Upgrades preserve the current SubMiner mpv shortcut locations instead of recreating shortcuts you already removed."
|
||||||
|
Pop $0
|
||||||
|
|
||||||
|
nsDialogs::Show
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
|
Function WindowsMpvShortcutPageLeave
|
||||||
|
${NSD_GetState} $WindowsMpvShortcutStartMenuCheckbox $0
|
||||||
|
${if} $0 == ${BST_CHECKED}
|
||||||
|
StrCpy $WindowsMpvShortcutStartMenuEnabled "1"
|
||||||
|
${else}
|
||||||
|
StrCpy $WindowsMpvShortcutStartMenuEnabled "0"
|
||||||
|
${endif}
|
||||||
|
|
||||||
|
${NSD_GetState} $WindowsMpvShortcutDesktopCheckbox $0
|
||||||
|
${if} $0 == ${BST_CHECKED}
|
||||||
|
StrCpy $WindowsMpvShortcutDesktopEnabled "1"
|
||||||
|
${else}
|
||||||
|
StrCpy $WindowsMpvShortcutDesktopEnabled "0"
|
||||||
|
${endif}
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
|
!macro customInstall
|
||||||
|
Call InitializeWindowsMpvShortcutDefaults
|
||||||
|
!insertmacro ResolveWindowsMpvShortcutPaths
|
||||||
|
|
||||||
|
${if} $WindowsMpvShortcutStartMenuEnabled == "1"
|
||||||
|
!ifdef MENU_FILENAME
|
||||||
|
CreateDirectory "$SMPROGRAMS\${MENU_FILENAME}"
|
||||||
|
!endif
|
||||||
|
CreateShortCut "$WindowsMpvShortcutStartMenuPath" "$appExe" "--launch-mpv" "$appExe" 0 "" "" "Launch mpv with the SubMiner profile"
|
||||||
|
# electron-builder's upstream NSIS templates use the same WinShell call for AppUserModelID wiring.
|
||||||
|
# WinShell.dll comes from electron-builder's cached nsis-resources bundle, so bun run build:win needs no extra repo-local setup.
|
||||||
|
ClearErrors
|
||||||
|
WinShell::SetLnkAUMI "$WindowsMpvShortcutStartMenuPath" "${APP_ID}"
|
||||||
|
${else}
|
||||||
|
Delete "$WindowsMpvShortcutStartMenuPath"
|
||||||
|
${endif}
|
||||||
|
|
||||||
|
${if} $WindowsMpvShortcutDesktopEnabled == "1"
|
||||||
|
CreateShortCut "$WindowsMpvShortcutDesktopPath" "$appExe" "--launch-mpv" "$appExe" 0 "" "" "Launch mpv with the SubMiner profile"
|
||||||
|
# ClearErrors keeps the optional AUMI assignment non-fatal if the packaging environment is missing WinShell.
|
||||||
|
ClearErrors
|
||||||
|
WinShell::SetLnkAUMI "$WindowsMpvShortcutDesktopPath" "${APP_ID}"
|
||||||
|
${else}
|
||||||
|
Delete "$WindowsMpvShortcutDesktopPath"
|
||||||
|
${endif}
|
||||||
|
|
||||||
|
System::Call 'Shell32::SHChangeNotify(i 0x8000000, i 0, i 0, i 0)'
|
||||||
|
!macroend
|
||||||
|
!endif
|
||||||
|
|
||||||
|
!macro customUnInstall
|
||||||
|
!insertmacro ResolveWindowsMpvShortcutPaths
|
||||||
|
Delete "$WindowsMpvShortcutStartMenuPath"
|
||||||
|
Delete "$WindowsMpvShortcutDesktopPath"
|
||||||
|
!macroend
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<artifact-configuration xmlns="http://signpath.io/artifact-configuration/v1">
|
||||||
|
<zip-file>
|
||||||
|
<pe-file path="SubMiner-*.exe" max-matches="unbounded">
|
||||||
|
<authenticode-sign />
|
||||||
|
</pe-file>
|
||||||
|
<zip-file path="SubMiner-*.zip" max-matches="unbounded">
|
||||||
|
<directory path="*">
|
||||||
|
<pe-file path="*.exe" max-matches="unbounded">
|
||||||
|
<authenticode-sign />
|
||||||
|
</pe-file>
|
||||||
|
<pe-file path="*.dll" max-matches="unbounded">
|
||||||
|
<authenticode-sign />
|
||||||
|
</pe-file>
|
||||||
|
<pe-file path="*.node" max-matches="unbounded">
|
||||||
|
<authenticode-sign />
|
||||||
|
</pe-file>
|
||||||
|
</directory>
|
||||||
|
</zip-file>
|
||||||
|
</zip-file>
|
||||||
|
</artifact-configuration>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: jellyfin
|
||||||
|
|
||||||
|
- Fixed the Jellyfin setup popup login path on Windows by using an IPC bridge, showing immediate login progress, and timing out unreachable server login attempts with an inline error.
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# Changelog Fragments
|
||||||
|
|
||||||
|
Add one `.md` file per user-visible PR in this directory.
|
||||||
|
|
||||||
|
Use this format:
|
||||||
|
|
||||||
|
```md
|
||||||
|
type: added
|
||||||
|
area: overlay
|
||||||
|
|
||||||
|
- Added keyboard navigation for Yomitan popups.
|
||||||
|
- Added auto-pause toggle when opening the popup.
|
||||||
|
```
|
||||||
|
|
||||||
|
For breaking changes, add `breaking: true`:
|
||||||
|
|
||||||
|
```md
|
||||||
|
type: changed
|
||||||
|
area: config
|
||||||
|
breaking: true
|
||||||
|
|
||||||
|
- Renamed `foo.bar` to `foo.baz`.
|
||||||
|
```
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- `type` required: `added`, `changed`, `fixed`, `docs`, or `internal`
|
||||||
|
- `area` required: short product area like `overlay`, `launcher`, `release`
|
||||||
|
- `breaking` optional: set to `true` to flag as a breaking change
|
||||||
|
- each non-empty body line becomes a bullet
|
||||||
|
- `README.md` is ignored by the generator
|
||||||
|
- if a PR should not produce release notes, apply the `skip-changelog` label instead of adding a fragment
|
||||||
|
|
||||||
|
How fragments turn into a release:
|
||||||
|
|
||||||
|
- At release time, `bun run changelog:build` (and `bun run changelog:prerelease-notes`) pipes every pending fragment through `claude -p` to merge related items, drop noise, and rewrite into a clean user-facing release body. Write fragments as raw, informative notes — don't worry about polished prose, deduping across PRs, or line-by-line phrasing. The polish step handles all of that.
|
||||||
|
- The polish step treats pending fragments as the final release outcome, not prerelease history. If a feature is added and then renamed or fixed before the stable cut, ship the final feature bullet instead of separate prerelease-only breaking/fix entries.
|
||||||
|
- `internal` fragments stay in `CHANGELOG.md` (inside a collapsed `<details>` block) but are dropped from the GitHub release notes entirely.
|
||||||
|
- The polished `CHANGELOG.md` and `release/release-notes.md` are committed and reviewed before tagging — edit the Markdown by hand if Claude misses something.
|
||||||
|
|
||||||
|
Prerelease notes:
|
||||||
|
|
||||||
|
- prerelease tags like `v0.11.3-beta.1` and `v0.11.3-rc.1` reuse the current pending fragments to generate `release/prerelease-notes.md`
|
||||||
|
- existing prerelease notes are a reviewed baseline; later prerelease runs should replace stale beta/RC wording with the current outcome instead of appending fix churn
|
||||||
|
- prerelease note generation does not consume fragments and does not update `CHANGELOG.md` or `docs-site/changelog.md`
|
||||||
|
- the final stable release is the point where `bun run changelog:build` consumes fragments into the stable changelog and release notes
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
type: fixed
|
||||||
|
area: anki
|
||||||
|
|
||||||
|
- Made sentence-audio padding opt-in by default, and kept animated AVIF motion aligned when padding is configured by freezing the first frame during leading audio padding.
|
||||||
|
- Kept multi-line sentence mining aligned when repeated subtitle text appears in the selected history range.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: changed
|
||||||
|
area: config
|
||||||
|
|
||||||
|
- Settings: Changed the AniSkip button key setting to use click-to-learn key capture instead of raw text entry.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: added
|
||||||
|
area: updater
|
||||||
|
|
||||||
|
- Added tray and `subminer -u` update checks for SubMiner releases, including app update prompts, launcher updates, Linux rofi theme updates, checksum verification, configurable update notifications, and an opt-in prerelease update channel for beta/RC testing.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: launcher
|
||||||
|
|
||||||
|
- Reused an already-running background SubMiner app for launcher-opened videos, closed launcher-owned tray apps after playback ends, and reapplied preferred subtitles for warm launches.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: changed
|
||||||
|
area: release
|
||||||
|
|
||||||
|
- Release-note polishing now treats pending fragments and reviewed prerelease notes as a cumulative final outcome, so prerelease-only fixes or breakages collapse into the final user-facing change.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: character-dictionary
|
||||||
|
|
||||||
|
- Reused cached character-dictionary media matches so loading a title with an existing snapshot no longer sends another AniList search request.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: config
|
||||||
|
|
||||||
|
- Updated the generated example config to use the same CSS declaration paths written by the Settings window for subtitle and sidebar appearance.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: config
|
||||||
|
|
||||||
|
- Preserved user config files during legacy config compatibility handling.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: changed
|
||||||
|
area: config
|
||||||
|
|
||||||
|
- Reorganized the Settings window into clearer Appearance, Behavior, Anki, input, and integration sections with learned keybinding controls and AnkiConnect-backed deck, field, and note-type pickers.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: config
|
||||||
|
|
||||||
|
- Fixed Settings window search so it searches across all categories, narrows on multi-word terms, hides settings owned by richer editors, and no longer shows the Open File button.
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
type: added
|
||||||
|
area: config
|
||||||
|
|
||||||
|
- Added a dedicated Settings window with launcher entry points via `subminer --settings` and `subminer settings`.
|
||||||
|
- Fixed the Settings window preload so launcher-opened windows can initialize even when Electron sandboxing is active.
|
||||||
|
- Kept settings-window startup lightweight by skipping AniList token refresh and automatic update polling.
|
||||||
|
- Marked safe live config options in the Settings window and expanded hot reload for stats keys, logging level, Jimaku, Subsync, YouTube language defaults, Anki media/sentence/misc field mappings, sentence card model, and selected Anki annotation/runtime options.
|
||||||
|
- Hid AI and translation fields from the Settings window while keeping them supported in config files.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
type: fixed
|
||||||
|
area: overlay
|
||||||
|
|
||||||
|
- Controller config and debug shortcuts now stay closed while controller support is disabled and show a notice to enable `controller.enabled` manually.
|
||||||
|
- Controller binding rows now start learn mode from the edit pencil, so clicking edit and pressing a controller button saves the remap.
|
||||||
|
- Controller remaps are now saved per controller profile, binding badges also start learn mode, and row reset buttons restore individual bindings to their defaults.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: changed
|
||||||
|
area: config
|
||||||
|
|
||||||
|
- Defaulted Jellyfin remote-session startup warmup and character-name subtitle highlighting to off.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: docs
|
||||||
|
area: docs
|
||||||
|
|
||||||
|
- Published stable docs at the site root with current development docs under `/main/`.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: changed
|
||||||
|
area: runtime
|
||||||
|
|
||||||
|
- Updated the bundled Electron runtime from 39.8.6 to 42.2.0, moving SubMiner back onto a supported Electron release line.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
type: added
|
||||||
|
area: setup
|
||||||
|
|
||||||
|
- Added optional first-run setup controls to install Bun and the `subminer` command-line launcher on Linux, macOS, and Windows.
|
||||||
|
- Added a Windows `subminer.cmd` user PATH shim so users can type `subminer` without adding `SubMiner.exe` to PATH.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
type: fixed
|
||||||
|
area: anilist
|
||||||
|
|
||||||
|
- Used fresh mpv time-position, duration, and subtitle timing events for AniList post-watch threshold checks so progress updates still fire when playback reaches or skips past the watched threshold.
|
||||||
|
- Prefer season-specific AniList search results for multi-season files before falling back to the base title.
|
||||||
|
- Show a clear AniList message when the matched season is not in Planning or Watching instead of silently queueing an impossible progress update.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: anki
|
||||||
|
|
||||||
|
- Fixed animated AVIF word-audio sync so the frozen lead-in matches the word audio duration without adding sentence audio padding a second time.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: overlay
|
||||||
|
|
||||||
|
- Primed the first startup subtitle before autoplay resumes so the overlay can render text before video playback begins.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
type: fixed
|
||||||
|
area: launcher
|
||||||
|
|
||||||
|
- Kept launcher-opened videos paused when attaching to an already-running background app until subtitle priming and tokenization readiness complete.
|
||||||
|
- Moved mpv plugin subtitle auto-selection to pre-load so launch-time subtitle choices are not reset after the video opens.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: subtitles
|
||||||
|
|
||||||
|
- Kept frequency highlighting for determiner-led noun compounds like `その場` while still filtering standalone determiners.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: integrations
|
||||||
|
|
||||||
|
- Prevented Discord Rich Presence from falling back to Jellyfin stream URLs, and primed Jellyfin playback titles before loading tokenized streams so presence shows the show/episode title
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: overlay
|
||||||
|
|
||||||
|
- Refreshed Linux overlay placement after leaving mpv fullscreen so Hyprland keeps the visible overlay aligned to the player.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
type: fixed
|
||||||
|
area: overlay
|
||||||
|
|
||||||
|
- Kept the Hyprland visible overlay stacked above mpv after mpv receives focus from clicks or overlay movement.
|
||||||
|
- Suspended the visible overlay while the in-player stats window is open, then restored it mouse-passive after stats closes.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: jellyfin
|
||||||
|
|
||||||
|
- Prevented Jellyfin discovery playback from reloading the active item, misreporting paused mpv playback as still playing, retrying startup unpause after playback is paused again, unpausing after a manual `y-t` overlay toggle during startup, repeatedly restoring the overlay from duplicate ready signals, missing delayed Japanese subtitle selection on startup, letting later German/Russian subtitle loads steal the selected Japanese track, and spawning long-lived sidebar ffmpeg extractors against Jellyfin stream URLs.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: jellyfin
|
||||||
|
|
||||||
|
- Fixed Jellyfin discovery resume playback when a remote play command sends `StartPositionTicks: 0` despite saved progress on the item.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: jellyfin
|
||||||
|
|
||||||
|
- Derived Jellyfin cast device identity from the OS hostname, always reports the client as SubMiner, and ignores legacy configurable Jellyfin client/device identity fields so multiple SubMiner installs no longer share the same remote-session identity.
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
type: fixed
|
||||||
|
area: jellyfin
|
||||||
|
|
||||||
|
- Fixed Jellyfin `y-t` overlay hide so the plugin sends an explicit hide command when it knows the overlay is visible, avoiding overlay reloads and paused playback resumes.
|
||||||
|
- Kept that manual hide sticky across Jellyfin stream redirects that change mpv's path, even when the redirected URL drops mpv's media title.
|
||||||
|
- Re-armed managed subtitle defaults during those path-changing redirects so Japanese primary subtitles can load on the redirected stream.
|
||||||
|
- Routed visible-overlay shortcuts and app-side visibility changes back through the mpv plugin so SubMiner overlay toggling stays independent of Jellyfin playback controls.
|
||||||
|
- Collapsed duplicate visible-overlay toggle events so Hyprland does not process one physical shortcut as hide-then-show.
|
||||||
|
- Kept passive Linux/Hyprland visible-overlay shows from taking keyboard focus away from mpv/Jellyfin.
|
||||||
|
- Made Jellyfin external subtitle selection tolerate transient mpv `track-list` read failures and numeric string track IDs so Japanese subtitles are selected after preload on Linux.
|
||||||
|
- Fixed AppImage-launched Jellyfin playback controls so mpv sends overlay commands to the running SubMiner app-control socket instead of the mounted Electron binary.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: jellyfin
|
||||||
|
|
||||||
|
- Fixed Jellyfin remote controller visibility and progress syncing for mpv/SubMiner seek jumps, stopped sessions, startup path changes, and Linux websocket reconnect windows.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: stats
|
||||||
|
|
||||||
|
- Grouped Jellyfin playback stats under Jellyfin item metadata instead of stream URLs, so watched episodes merge with matching local library titles and keep clean display names.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: jellyfin
|
||||||
|
|
||||||
|
- Improved Jellyfin subtitle timing behavior by preferring default embedded subtitle streams over external sidecars, stripping Jellyfin's server-selected subtitle stream from mpv playback URLs, suppressing mpv's subtitle auto-selection and plugin overlay auto-start while SubMiner stages managed tracks, automatically correcting clear Japanese-vs-English cue timeline offsets, and restoring saved per-stream subtitle delay shifts.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: jellyfin
|
||||||
|
|
||||||
|
- Keep the Jellyfin discovery tray checkbox in sync on Linux after tray, CLI, or startup remote-session changes, with a visible check mark when Linux tray hosts ignore native checkbox rendering.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: jellyfin
|
||||||
|
|
||||||
|
- Restarted stale Jellyfin tray discovery sessions when the server no longer lists the SubMiner cast target, avoiding a needless Jellyfin re-login.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
type: fixed
|
||||||
|
area: jellyfin
|
||||||
|
|
||||||
|
- Preserved Jellyfin-visible resume progress when mpv resets its position during playback stop by reusing SubMiner's last known playback position for final progress and stopped reports.
|
||||||
|
- Kept Jellyfin remote Play and Resume distinct so normal Play starts from the beginning, while Resume starts at Jellyfin's requested position without an early mpv seek race.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: launcher
|
||||||
|
|
||||||
|
- Suppressed Electron macOS menu diagnostics from `subminer settings` launcher output.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: shortcuts
|
||||||
|
|
||||||
|
- Disabled native mpv menu shortcuts during managed macOS playback so configured SubMiner shortcuts also work while mpv has focus.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
type: fixed
|
||||||
|
area: overlay
|
||||||
|
|
||||||
|
- Hid the macOS visible overlay when mpv is no longer the foreground target so other apps and Spaces are not covered by SubMiner subtitles.
|
||||||
|
- Kept the macOS overlay layered above active mpv while stats mouse passthrough is enabled, and treated the frontmost mpv app as the focus signal.
|
||||||
|
- Opened the stats overlay inactive on macOS so it appears over fullscreen mpv instead of switching back to SubMiner's original desktop.
|
||||||
|
- Preserved the active mpv focus state through transient macOS helper misses so subtitles do not flicker while mpv remains foreground.
|
||||||
|
- Kept fullscreen macOS overlays stable when mpv remains frontmost but window geometry temporarily disappears from the macOS window APIs.
|
||||||
|
- Released the macOS overlay when the helper reports mpv is no longer foreground so other apps are no longer covered.
|
||||||
|
- Reduced macOS window-tracker background work by preferring the compiled helper and slowing polls while mpv is stably focused.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: overlay
|
||||||
|
|
||||||
|
- Fixed macOS overlay tracking so transient mpv window misses no longer hide the overlay; minimizing mpv still hides it.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: overlay
|
||||||
|
|
||||||
|
- Fixed macOS overlay passthrough so mpv controls remain clickable before hovering subtitle bars.
|
||||||