Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be65241cc2
|
||
|
|
7c1c674313
|
||
|
|
01613f1be5 | ||
|
|
bf4e5bb609 | ||
|
|
7909fcd2cf | ||
|
|
8fc20b2499 | ||
|
|
ea2b3a9507 | ||
|
|
a6d96e9966 | ||
|
|
7108e086ba | ||
|
|
78a9b6346f | ||
|
|
78eca52b7e | ||
|
|
b48083ddd8 | ||
|
|
4b6842d034 | ||
|
|
9ffa2bce94
|
||
|
|
88b73656a7
|
||
|
|
e77aa5c3b3 | ||
|
|
121b688afb | ||
|
|
727be5683a | ||
|
|
138bfe9759 | ||
|
|
0aba17d95b
|
||
|
|
a5d0b63792 | ||
|
|
15cb9fe918
|
||
|
|
db45a70ec2
|
||
|
|
9e79fa93c2 | ||
|
|
9fadbc8cdd
|
||
|
|
ffbbe698fc | ||
|
|
7b72dd2cc4
|
||
|
|
416cd7e9ac
|
||
|
|
eb749f8c19
|
||
|
|
65ac5a7728 | ||
|
|
df675cfbfe
|
||
|
|
e49b5f6589
|
||
|
|
0bbd8617f4 | ||
|
|
4e43c5c6e1 | ||
|
|
953abdf42a
|
||
|
|
fdb0a31fff
|
||
|
|
04b878dd93
|
||
|
|
07b32626a6 | ||
|
|
c68b73c8c1 | ||
|
|
b24008237b | ||
|
|
63eb228e4f | ||
|
|
ba0155e429 | ||
|
|
23af9c6464
|
||
|
|
8825cca642
|
||
|
|
bc73a02623 | ||
|
|
5096ca217b
|
||
|
|
285b19ccc3
|
||
|
|
7fbc36dc41
|
||
|
|
e93b696e71
|
||
|
|
7c161723cd | ||
|
|
b0fb39a8f5 | ||
|
|
a7a302bdd0 | ||
|
|
6a1d1211ae
|
||
|
|
7585d430ae
|
||
|
|
122ff9a3c5
|
||
|
|
786bd5af81
|
||
|
|
42d1384c68 | ||
|
|
1508863dbb | ||
|
|
4dd30f44d1
|
||
|
|
ab48a5678e | ||
|
|
2f21582666 | ||
|
|
383aed8bad | ||
|
|
f9c4e892dc | ||
|
|
7959e1a4e5 | ||
|
|
917d52dd94 | ||
|
|
05425cc5db | ||
|
|
8097ed27e5 | ||
|
|
76e43d7e6d | ||
|
|
4f0762e840 | ||
|
|
026d495fac | ||
|
|
dd76782d30
|
||
|
|
a8c16147ad | ||
|
|
186d4a0640 | ||
|
|
2831e05124 | ||
|
|
6841a37a22 | ||
|
|
7c1eac03dc | ||
|
|
e6dc9dfec5
|
||
|
|
6d69a56574 | ||
|
|
0c37c665a2 | ||
|
|
8ae5bde6a4
|
||
|
|
016a43b804 | ||
|
|
fb2d1a512b | ||
|
|
614a8ca912 | ||
|
|
9eecb7358b
|
||
|
|
1e5d7747b4 | ||
|
|
c14c690875
|
||
|
|
84f718043a | ||
|
|
99266294b8 | ||
|
|
c055359be1
|
||
|
|
ec5a147095 | ||
|
|
a0635f4360 | ||
|
|
c0a78ef008 | ||
|
|
87b01155df | ||
|
|
fc5c49e365 | ||
|
|
a20269e9f5 | ||
|
|
2ad491e95c
|
||
|
|
6fffcc731f | ||
|
|
5cc21113fd | ||
|
|
051140f910 | ||
|
|
6e945f0872 | ||
|
|
c2c25c0da6 | ||
|
|
556de61756
|
||
|
|
a98fb0fddf
|
||
|
|
e816b3b371 | ||
|
|
ed7d3f4c3d | ||
|
|
509dc5bf7f | ||
|
|
0a0aa3ec98
|
||
|
|
b87cc3cfdd
|
||
|
|
8cb3c8c90a
|
||
|
|
03ea903927
|
||
|
|
3aea42e6f8
|
||
|
|
88bb3edfa4
|
||
|
|
9445aef004
|
||
|
|
c01bcd9d0f
|
||
|
|
695613b1e7
|
||
|
|
20797772f8 | ||
|
|
72c312810a
|
||
|
|
b61f050e78
|
||
|
|
2a77ba9dd4 | ||
|
|
4ed878270f
|
||
|
|
1d1b0c7bb6
|
||
|
|
db61ce358d
|
||
|
|
e9778a945a | ||
|
|
7de73e16a1 | ||
|
|
273652f781 | ||
|
|
14c0086520
|
||
|
|
ff0bc0af6d | ||
|
|
00b1b79bf4 | ||
|
|
e11a5fea0d
|
||
|
|
f73fe179d0
|
||
|
|
2938e7a32a | ||
|
|
82f6b4705a | ||
|
|
a02c33dac4 | ||
|
|
2174e689a2 | ||
|
|
7d729cb60c | ||
|
|
d963def1a9 | ||
|
|
6fb3d2eb13 | ||
|
|
c5a77ac067 | ||
|
|
8ddc151435
|
||
|
|
b98d4d65c7 | ||
|
|
046e74ea91
|
||
|
|
8bf847503d | ||
|
|
47b5903392
|
||
|
|
bf85554d1e | ||
|
|
d74c7e1235
|
||
|
|
57ddd19953 | ||
|
|
ee25536d90 | ||
|
|
7b0fbdf254 | ||
|
|
2fefc83e3f | ||
|
|
dbdf578c68 | ||
|
|
441ecf3c04 | ||
|
|
a0dde4ee3e
|
||
|
|
fe4dacc1e7 | ||
|
|
b08cd0db35 | ||
|
|
bffb1c5982 | ||
|
|
5b8848518a | ||
|
|
176edd67f1
|
||
|
|
4d65dec340 | ||
|
|
6607c333bc
|
||
|
|
b2bbf1ae12
|
||
|
|
b204d4dd6e | ||
|
|
89ed675935 | ||
|
|
1995200e76
|
||
|
|
e876e483d6
|
||
|
|
6d2a72e13b | ||
|
|
0d7084c8aa | ||
|
|
95e0abc7b7
|
||
|
|
d9155ceacb
|
||
|
|
9d0585423c
|
||
|
|
455cfff90a
|
||
|
|
987b325edb | ||
|
|
08c6807cb1
|
||
|
|
18c6410f24
|
||
|
|
e223cf9b71
|
||
|
|
c718bb8b34
|
||
|
|
177f7e5fd6
|
||
|
|
20b50ec4e1 | ||
|
|
deae61b211 | ||
|
|
44959ed282
|
||
|
|
2398f5c030 | ||
|
|
8711cf1a48
|
||
|
|
9f4888293b
|
||
|
|
29332b103e
|
||
|
|
8b2cee2c58 | ||
|
|
2e2ee3f028 | ||
|
|
49b926e08c | ||
|
|
66f8ca4f80 | ||
|
|
6fe1e0fee4 | ||
|
|
0ab840b362 | ||
|
|
21a06c12fb | ||
|
|
14070acceb
|
||
|
|
7d81342f0f
|
||
|
|
4b7f750919 | ||
|
|
6ab3d823a4 | ||
|
|
8797719a09
|
||
|
|
8acc78cc1c | ||
|
|
321461c50f | ||
|
|
beeab564b4
|
||
|
|
cab7975a2b
|
||
|
|
846292809c
|
||
|
|
8712780d08 | ||
|
|
7b1a005a65 | ||
|
|
84c75f50aa
|
||
|
|
8b838f2c7d
|
||
|
|
db4139ba0b
|
||
|
|
6c251502b3 | ||
|
|
ae40934d3a | ||
|
|
cdb1475a54
|
||
|
|
a2e49b369b
|
||
|
|
7f13aed50a | ||
|
|
8b21a2bca8
|
||
|
|
925413adfe
|
||
|
|
d0644ab2eb | ||
|
|
d253710c2e | ||
|
|
c3df510e4f
|
||
|
|
187f68e5b6 | ||
|
|
0e254cbbef
|
||
|
|
7b94adafbd
|
||
|
|
61f39d1e09 | ||
|
|
e7739de51c | ||
|
|
ad1d240f20 | ||
|
|
8b0ef662bc
|
||
|
|
0a58c20ad7 | ||
|
|
38ddb29aa0 | ||
|
|
8b9a70c5a6 | ||
|
|
48a084914a | ||
|
|
a042b04357 | ||
|
|
35ca2afc6f | ||
|
|
b14f977e33 | ||
|
|
eef4500599 | ||
|
|
73af1451b7
|
||
|
|
36a3704815 | ||
|
|
359cb0a301 | ||
|
|
4b10e85053
|
||
|
|
c942a2cf2d | ||
|
|
f65afa6046 | ||
|
|
389d8e06e0
|
||
|
|
0008b55b70
|
||
|
|
d16ae9c745
|
||
|
|
36f94151b8 | ||
|
|
5326ad32f5 | ||
|
|
d199376364
|
||
|
|
3aee88c150
|
||
|
|
70da3ee8bd | ||
|
|
aa8eb753f6
|
||
|
|
8d73de8731
|
||
|
|
5a98397efe
|
||
|
|
a117c5759c | ||
|
|
ae7e6f82a8 | ||
|
|
1158be5b39 | ||
|
|
33e767458f | ||
|
|
94a65416ae | ||
|
|
0a384a22c9 | ||
|
|
b3b45521b6
|
||
|
|
131b23efa9
|
||
|
|
e2afceb492 | ||
|
|
7be1843c41 | ||
|
|
c09d009a3e
|
||
|
|
2007e28be8 | ||
|
|
d5bfdcae7b | ||
|
|
311f1e8ee5 | ||
|
|
e6a16a069b | ||
|
|
af67c53dd6 | ||
|
|
ea79e331fa
|
||
|
|
ee89b0c8a9 | ||
|
|
f2fd58cd2b | ||
|
|
1280a30216
|
||
|
|
a80ed72b2d
|
||
|
|
4cc6c12dc7 | ||
|
|
425004879a | ||
|
|
76f99e6518 | ||
|
|
f1e260e996 | ||
|
|
54e90754ef
|
||
|
|
487143802a | ||
|
|
e6a004ab8b | ||
|
|
b510c54875 | ||
|
|
e1ea464bc9 | ||
|
|
b46b8dfa41 | ||
|
|
ca067a6ccf | ||
|
|
d719b346e0 | ||
|
|
a1da3dcdc8
|
||
|
|
9927ef1581
|
||
|
|
791c993870
|
||
|
|
38dbce517c
|
||
|
|
889dc9c009
|
||
|
|
097021072d
|
||
|
|
91c8eb8faf
|
||
|
|
eed0a6a243 | ||
|
|
d33009d4a3 | ||
|
|
8d0535f3ca | ||
|
|
75f9b8a803
|
||
|
|
c30cce0a14
|
||
|
|
3e6591e390 | ||
|
|
f033f87329 | ||
|
|
29cb8c7fb4
|
||
|
|
1dcfed86ab | ||
|
|
efe50ed1e4 | ||
|
|
5b44981688 | ||
|
|
2add95d541
|
||
|
|
f62fff2585
|
||
|
|
11c196821d | ||
|
|
43ebc7d371
|
||
|
|
639e331f24 | ||
|
|
78be72e32f
|
||
|
|
3932e53ced | ||
|
|
097b619d71 | ||
|
|
f7abcedd75
|
||
|
|
807c0ff3db | ||
|
|
7e6f9672cf | ||
|
|
9fe13601fb
|
||
|
|
920cbab1bc | ||
|
|
17d97f0b7e | ||
|
|
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 | ||
|
|
2f07c3407a
|
||
|
|
a5554ec530
|
||
|
|
f9f2fe6e87
|
||
|
|
8ca05859a9
|
||
|
|
0cac446725
|
||
|
|
23623ad1e1
|
||
|
|
b623c5e160
|
||
|
|
5436e0cd49
|
||
|
|
beeeee5ebd
|
||
|
|
fdbf769760
|
||
|
|
0a36d1aa99
|
||
|
|
69ab87c25f
|
||
|
|
9a30419a23
|
||
|
|
092c56f98f
|
||
|
|
10ef535f9a
|
||
|
|
6c80bd5843
|
||
|
|
f0bd0ba355
|
||
|
|
be4db24861
|
||
|
|
83d21c4b6d
|
||
|
|
e744fab067
|
||
|
|
5167e3a494
|
||
|
|
aff4e91bbb
|
||
|
|
737101fe9e
|
||
|
|
629fe97ef7
|
||
|
|
fa97472bce
|
||
|
|
83f13df627
|
||
|
|
cde231b1ff
|
||
|
|
7161fc3513
|
||
|
|
9a91951656
|
||
|
|
11e9c721c6
|
||
|
|
3c66ea6b30
|
||
|
|
79f37f3986
|
||
|
|
f1b85b0751
|
||
|
|
1ab5d00de0
|
||
|
|
17a417e639
|
||
|
|
68e5a7fef3
|
||
|
|
7023a3263f | ||
|
|
49434bf0cd
|
||
|
|
44c7761c7c | ||
|
|
45df3c466b
|
||
|
|
6eda768261
|
||
|
|
ceea10cba1
|
||
|
|
9d73971f3b
|
||
|
|
a2735eaedc
|
||
|
|
b989508ece
|
||
|
|
978cb8c401
|
||
|
|
fe8a71990a
|
||
|
|
0d63db5f27
|
||
|
|
4f02d7db9c
|
||
|
|
cbed5f1376 | ||
|
|
4ebabbe639 | ||
|
|
64020a9069 | ||
|
|
ae95601698 | ||
|
|
d3fd47f0ec | ||
|
|
448ce03fd4 |
No files matched your search
@@ -0,0 +1,46 @@
|
||||
---
|
||||
name: subminer-change-verification
|
||||
description: Verify SubMiner changes with repo-native cheap-first test lanes. Use after code, config, launcher, plugin, runtime, stats, documentation, or workflow changes; do not use for read-only questions.
|
||||
---
|
||||
|
||||
# SubMiner Change Verification
|
||||
|
||||
Verify the behavior claimed by a change without running unrelated expensive checks by default.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Inspect the requested scope and changed paths with `git status --short` and `git diff`.
|
||||
2. Read `docs/workflow/verification.md` as the source of truth for maintained lanes.
|
||||
3. Run the cheapest lane or lanes that cover the changed behavior.
|
||||
4. Escalate to the full handoff gate only for substantial or cross-boundary changes.
|
||||
5. Report exact commands, results, skipped checks, blockers, and remaining risk.
|
||||
|
||||
Do not use hidden wrapper commands. Verification commands are owned by `package.json` and the workflow documentation.
|
||||
|
||||
## Lane Selection
|
||||
|
||||
- Internal docs, `AGENTS.md`, or `.agents/skills/**`: `bun run test:docs:kb`
|
||||
- User-facing `docs-site/**`: `bun run docs:test`, then `bun run docs:build`
|
||||
- Config/schema/defaults: `bun run test:config`
|
||||
- If defaults or templates changed, also run `bun run generate:config-example` and `bun run verify:config-example`.
|
||||
- General TypeScript source: `bun run typecheck`, then `bun run test:fast`
|
||||
- Launcher or mpv plugin: `bun run test:launcher` or `bun run test:env`, based on the behavior changed
|
||||
- Runtime compatibility or dist-sensitive wiring: `bun run test:runtime:compat`
|
||||
- Stats dashboard: `bun run test:stats`
|
||||
- Overlay, renderer, mpv, or mining behavior in the running app: `bun run build`, then `bun run test:e2e`
|
||||
- Build/release scripts: `bun run test:scripts`
|
||||
|
||||
For substantial changes, use the full gate documented in `AGENTS.md` and `docs/workflow/verification.md`.
|
||||
|
||||
## Runtime Escalation
|
||||
|
||||
Real runtime checks are required when the claim depends on actual Electron, mpv, overlay, focus, window tracking, launch, or socket behavior. Run the relevant application flow when the environment supports it. Otherwise, report the missing runtime dependency and do not present cheaper checks as authoritative runtime validation.
|
||||
|
||||
## Pre-Handoff Checks
|
||||
|
||||
Before handoff, reconcile both questions:
|
||||
|
||||
1. Do behavior, defaults, flags, shortcuts, ports, APIs, architecture, or workflow changes require documentation updates?
|
||||
2. Does the change require a current-outcome fragment under `changes/` according to `changes/README.md`?
|
||||
|
||||
Complete required updates before handoff or report the blocker.
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: subminer-release
|
||||
description: Prepare, cut, publish, or repair SubMiner stable and prerelease releases. Use for hands-on release work; do not use for general release questions.
|
||||
---
|
||||
|
||||
# SubMiner release
|
||||
|
||||
Carry out the requested release phase using the repository's current release process.
|
||||
|
||||
## Source of truth
|
||||
|
||||
Read `docs/RELEASING.md` completely before changing files or release state. Treat it as canonical. Read `changes/README.md` when the work touches change fragments or generated release notes.
|
||||
|
||||
Do not copy release commands or policy into this skill. If this skill disagrees with the release guide, follow the guide and reconcile the skill before handoff.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Identify whether the request is for a stable release, prerelease, release preparation, publication, or repair.
|
||||
2. Inspect the current branch, worktree status, package version, pending change fragments, relevant tags, and latest CI state before making changes.
|
||||
3. Follow the matching procedure in `docs/RELEASING.md` in order. Review generated changelog and release-note Markdown before it can be committed or published.
|
||||
4. Run every required gate for the requested release phase. Do not treat a cheaper test lane as a substitute for the documented release gate.
|
||||
5. Before a stable tag, confirm the package and tag versions match and no pending `changes/*.md` fragments remain. Preserve fragments for prereleases as documented.
|
||||
6. Report the resulting version, completed checks, local commit and tag state, remote publication state, skipped platform checks, and any remaining manual work.
|
||||
|
||||
## Authorization boundaries
|
||||
|
||||
- A request to prepare a release stops before commit, tag, push, or remote publication unless the user also authorizes those actions.
|
||||
- A clear request to cut or publish a release includes the documented commit, tag, and push steps. Ask before the first remote mutation when the wording is ambiguous.
|
||||
- Do not edit an existing GitHub release, publish to the AUR, change secrets, or alter signing configuration unless the user explicitly requests that operation.
|
||||
- Do not switch branches without consent.
|
||||
|
||||
## Stop conditions
|
||||
|
||||
Stop and report the blocker when required CI or a release gate fails, authentication is missing, versions disagree, required artifacts are absent, or the worktree contains unexpected changes that overlap the release. Do not tag or publish a partially verified release.
|
||||
@@ -0,0 +1,15 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [ksyasuda]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: sudacode
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
polar: # Replace with a single Polar username
|
||||
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
|
||||
thanks_dev: # Replace with a single thanks.dev username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
@@ -0,0 +1,105 @@
|
||||
name: Bug Report
|
||||
description: Report something that is broken or behaving incorrectly
|
||||
title: '[Bug]: '
|
||||
labels: ['bug']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to file a bug report! Please search [existing issues](https://github.com/ksyasuda/SubMiner/issues?q=is%3Aissue) first to avoid duplicates.
|
||||
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: A clear description of the bug, including what you expected to happen instead.
|
||||
placeholder: When I open the Yomitan popup, the overlay freezes...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Minimal, ordered steps that reliably trigger the bug.
|
||||
placeholder: |
|
||||
1. Launch `subminer`
|
||||
2. Play a video in MPV
|
||||
3. Hover a word and press ...
|
||||
4. See error
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: area
|
||||
attributes:
|
||||
label: Affected area
|
||||
description: Which part of SubMiner is affected?
|
||||
options:
|
||||
- Overlay / Yomitan popup
|
||||
- Anki mining
|
||||
- Subtitle annotations
|
||||
- Subtitle sidebar
|
||||
- Immersion tracking / stats
|
||||
- Launcher / CLI
|
||||
- MPV plugin
|
||||
- Jellyfin integration
|
||||
- Jimaku integration
|
||||
- AniList integration
|
||||
- YouTube integration
|
||||
- Character dictionary
|
||||
- WebSocket / texthooker API
|
||||
- Configuration
|
||||
- Documentation
|
||||
- Other / not sure
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: os
|
||||
attributes:
|
||||
label: Operating system
|
||||
options:
|
||||
- Linux
|
||||
- macOS
|
||||
- Windows
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: SubMiner version
|
||||
description: Output of `subminer --version`, or the release tag / commit you are running.
|
||||
placeholder: v0.15.0
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: compositor
|
||||
attributes:
|
||||
label: Compositor (Linux only)
|
||||
description: SubMiner's overlay supports Hyprland and sway. Name yours (and version if known). Leave blank on macOS / Windows.
|
||||
placeholder: Hyprland 0.55
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: input
|
||||
id: mpv-version
|
||||
attributes:
|
||||
label: MPV version
|
||||
description: Output of `mpv --version` (first line).
|
||||
placeholder: mpv 0.38.0
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Logs / console output
|
||||
description: |
|
||||
Relevant logs. For verbose output, run `electron . --dev --log-level debug`.
|
||||
This will be rendered as code automatically — no backticks needed.
|
||||
render: shell
|
||||
validations:
|
||||
required: false
|
||||
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Documentation
|
||||
url: https://docs.subminer.moe
|
||||
about: Setup, configuration, and feature docs — check here before filing an issue.
|
||||
- name: Troubleshooting guide
|
||||
url: https://docs.subminer.moe/troubleshooting
|
||||
about: Common problems and fixes (Hyprland rules, MPV detection, Anki connection, etc.).
|
||||
@@ -0,0 +1,59 @@
|
||||
name: Feature Request
|
||||
description: Suggest a new feature or an improvement to an existing one
|
||||
title: '[Feature]: '
|
||||
labels: ['enhancement']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for the idea! Please search [existing issues](https://github.com/ksyasuda/SubMiner/issues?q=is%3Aissue) first to avoid duplicates.
|
||||
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: Problem / motivation
|
||||
description: What problem are you trying to solve? What is missing or frustrating today?
|
||||
placeholder: When mining a card I have to manually switch to Anki because...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: proposal
|
||||
attributes:
|
||||
label: Proposed solution
|
||||
description: Describe the feature or change you'd like to see.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: area
|
||||
attributes:
|
||||
label: Related area
|
||||
description: Which part of SubMiner does this relate to?
|
||||
options:
|
||||
- Overlay / Yomitan popup
|
||||
- Anki mining
|
||||
- Subtitle annotations
|
||||
- Subtitle sidebar
|
||||
- Immersion tracking / stats
|
||||
- Launcher / CLI
|
||||
- MPV plugin
|
||||
- Jellyfin integration
|
||||
- Jimaku integration
|
||||
- AniList integration
|
||||
- YouTube integration
|
||||
- Character dictionary
|
||||
- WebSocket / texthooker API
|
||||
- Configuration
|
||||
- Documentation
|
||||
- Other / new area
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Alternatives considered
|
||||
description: Any workarounds you currently use or other approaches you've thought about.
|
||||
validations:
|
||||
required: false
|
||||
@@ -0,0 +1,36 @@
|
||||
<!--
|
||||
Thanks for contributing to SubMiner! Fill out the sections below.
|
||||
Keep it short — a couple of sentences per section is fine.
|
||||
-->
|
||||
|
||||
## Summary
|
||||
|
||||
<!-- What does this PR do and why? -->
|
||||
|
||||
## Type of change
|
||||
|
||||
<!-- Check all that apply. -->
|
||||
|
||||
- [ ] Bug fix
|
||||
- [ ] New feature
|
||||
- [ ] Refactor / internal
|
||||
- [ ] Documentation
|
||||
- [ ] Other
|
||||
|
||||
## Related issues
|
||||
|
||||
<!-- e.g. "Closes #123". Delete if none. -->
|
||||
|
||||
## How was this tested?
|
||||
|
||||
<!--
|
||||
Describe verification. The default handoff gate is:
|
||||
bun run typecheck && bun run test:fast && bun run test:env && bun run build && bun run test:smoke:dist
|
||||
If docs-site/ changed, also: bun run docs:test && bun run docs:build
|
||||
-->
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] Reconciled current-outcome changelog fragment(s), or this PR is labeled `skip-changelog` (see [`changes/README.md`](../changes/README.md))
|
||||
- [ ] Docs updated in the same PR if behavior, defaults, flags, shortcuts, ports, or APIs changed
|
||||
- [ ] Relevant checks pass locally (typecheck, tests, build)
|
||||
@@ -8,67 +8,80 @@ on:
|
||||
|
||||
jobs:
|
||||
build-test-audit:
|
||||
runs-on: ubuntu-latest
|
||||
uses: ./.github/workflows/quality-gate.yml
|
||||
|
||||
# Boots the built app with a real mpv on each platform. Kept outside the
|
||||
# reusable quality gate so releases do not depend on it while the lane is new.
|
||||
e2e:
|
||||
name: E2E (${{ matrix.os }})
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 30
|
||||
permissions:
|
||||
contents: read
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
env:
|
||||
SUBMINER_E2E_KEEP: '1'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.5
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.bun/install/cache
|
||||
node_modules
|
||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-bun-
|
||||
- name: Point temp files at an uploadable directory
|
||||
run: |
|
||||
# os.tmpdir() follows these, so the kept session logs land under runner.temp.
|
||||
dir="$RUNNER_TEMP/e2e"
|
||||
mkdir -p "$dir"
|
||||
{ echo "TMPDIR=$dir"; echo "TEMP=$dir"; echo "TMP=$dir"; } >> "$GITHUB_ENV"
|
||||
|
||||
- name: Install mpv and ffmpeg (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
# Only Ubuntu sources are needed; unrelated runner repositories can be unavailable.
|
||||
apt_sources=(-o Dir::Etc::sourcelist=sources.list.d/ubuntu.sources -o Dir::Etc::sourceparts=-)
|
||||
sudo apt-get "${apt_sources[@]}" update
|
||||
sudo apt-get "${apt_sources[@]}" install -y mpv ffmpeg xvfb
|
||||
|
||||
- name: Install mpv and ffmpeg (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install mpv ffmpeg
|
||||
|
||||
- name: Install mpv and ffmpeg (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
choco install mpv ffmpeg -y --no-progress
|
||||
# The mpv package adds no shim, so put its directory on PATH for later steps.
|
||||
mpv_exe="$(find /c/ProgramData/chocolatey/lib /c/ProgramData/chocolatey/bin "/c/Program Files" /c/tools -maxdepth 6 -iname mpv.exe 2>/dev/null | head -n 1)"
|
||||
test -n "$mpv_exe"
|
||||
cygpath -w "$(dirname "$mpv_exe")" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install --frozen-lockfile
|
||||
run: |
|
||||
bun install --frozen-lockfile
|
||||
cd stats && bun install --frozen-lockfile
|
||||
|
||||
- name: Build (TypeScript check)
|
||||
# Keep explicit typecheck for fast fail before full build/bundle.
|
||||
run: bun run tsc --noEmit
|
||||
- name: Build
|
||||
run: bun run build
|
||||
|
||||
- name: Test suite (source)
|
||||
run: bun run test:fast
|
||||
- name: End-to-end suite
|
||||
run: bun run test:e2e
|
||||
|
||||
- name: Launcher smoke suite (source)
|
||||
run: bun run test:launcher:smoke:src
|
||||
|
||||
- name: Upload launcher smoke artifacts (on failure)
|
||||
- name: Upload session logs (on failure)
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: launcher-smoke
|
||||
path: .tmp/launcher-smoke/**
|
||||
name: e2e-logs-${{ matrix.os }}
|
||||
path: ${{ runner.temp }}/e2e/subminer-e2e-*/*.log
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: Build (bundle)
|
||||
run: bun run build
|
||||
|
||||
- name: Dist smoke suite
|
||||
run: bun run test:smoke:dist
|
||||
|
||||
- name: Build docs
|
||||
run: bun run docs:build
|
||||
|
||||
- name: Security audit
|
||||
run: bun audit --audit-level high
|
||||
continue-on-error: true
|
||||
|
||||
- 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
|
||||
@@ -0,0 +1,88 @@
|
||||
name: Docs Pages
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
rebuild_archives:
|
||||
description: 'Stable tags to rebuild in R2 (comma-separated, or "all"); archives are otherwise built once'
|
||||
required: false
|
||||
default: ''
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- 'v*'
|
||||
paths:
|
||||
- 'docs-site/**'
|
||||
- 'scripts/docs-versioning.ts'
|
||||
- 'scripts/docs-versioned-assets.ts'
|
||||
- 'scripts/docs-archive-store.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'
|
||||
env:
|
||||
TAG_NAME: ${{ github.ref_name }}
|
||||
run: |
|
||||
if [[ ! "$TAG_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "::notice::Skipping non-stable docs tag $TAG_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: Test docs
|
||||
if: steps.tag_guard.outputs.stable_tag != 'false'
|
||||
run: bun run docs:test
|
||||
|
||||
# Builds only archives missing from R2 (plus any requested rebuilds), then the
|
||||
# root and /main/ builds that make up the Pages deployment.
|
||||
- name: Build versioned docs
|
||||
if: steps.tag_guard.outputs.stable_tag != 'false'
|
||||
env:
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
DOCS_ARCHIVE_R2_ACCESS_KEY_ID: ${{ secrets.DOCS_ARCHIVE_R2_ACCESS_KEY_ID }}
|
||||
DOCS_ARCHIVE_R2_SECRET_ACCESS_KEY: ${{ secrets.DOCS_ARCHIVE_R2_SECRET_ACCESS_KEY }}
|
||||
DOCS_ARCHIVE_R2_BUCKET: ${{ vars.DOCS_ARCHIVE_R2_BUCKET }}
|
||||
REBUILD_ARCHIVES: ${{ inputs.rebuild_archives }}
|
||||
run: bun run scripts/build-versioned-docs.ts --require-archives "--rebuild-archives=${REBUILD_ARCHIVES}"
|
||||
|
||||
- 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 }}
|
||||
# Run from docs-site so Wrangler bundles docs-site/functions (the /v/* archive server).
|
||||
workingDirectory: docs-site
|
||||
command: pages deploy ../.tmp/docs-versioned-site --project-name "${{ vars.CLOUDFLARE_PAGES_PROJECT_NAME }}" --branch main
|
||||
@@ -0,0 +1,238 @@
|
||||
name: Package release
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
secrets:
|
||||
CSC_LINK:
|
||||
required: true
|
||||
CSC_KEY_PASSWORD:
|
||||
required: true
|
||||
APPLE_ID:
|
||||
required: true
|
||||
APPLE_APP_SPECIFIC_PASSWORD:
|
||||
required: true
|
||||
APPLE_TEAM_ID:
|
||||
required: true
|
||||
# Project TMDB key baked into release artifacts; builds stay valid without it.
|
||||
SUBMINER_TMDB_API_KEY:
|
||||
required: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
persist-credentials: false
|
||||
|
||||
- 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/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: Build texthooker-ui
|
||||
run: |
|
||||
cd vendor/texthooker-ui
|
||||
bun install --frozen-lockfile
|
||||
bun run build
|
||||
|
||||
- name: Build AppImage
|
||||
run: bun run build:appimage
|
||||
env:
|
||||
SUBMINER_TMDB_API_KEY: ${{ secrets.SUBMINER_TMDB_API_KEY }}
|
||||
|
||||
- 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: Smoke packaged runtime assets
|
||||
shell: bash
|
||||
run: xvfb-run -a bun run test:package "release/linux-unpacked/resources"
|
||||
|
||||
- name: Upload AppImage artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: appimage
|
||||
path: |
|
||||
release/*.AppImage
|
||||
release/latest*.yml
|
||||
release/*.blockmap
|
||||
if-no-files-found: error
|
||||
|
||||
build-macos:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
persist-credentials: false
|
||||
|
||||
- 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/bun.lock', '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 --frozen-lockfile
|
||||
bun run build
|
||||
|
||||
- name: Build signed + notarized macOS artifacts
|
||||
run: bun run build:mac
|
||||
env:
|
||||
SUBMINER_TMDB_API_KEY: ${{ secrets.SUBMINER_TMDB_API_KEY }}
|
||||
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: Smoke packaged runtime assets
|
||||
shell: bash
|
||||
run: bun run test:package "release/mac-arm64/SubMiner.app/Contents/Resources"
|
||||
|
||||
- name: Upload macOS artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macos
|
||||
path: |
|
||||
release/*.dmg
|
||||
release/*.zip
|
||||
release/latest*.yml
|
||||
release/*.blockmap
|
||||
if-no-files-found: error
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
persist-credentials: false
|
||||
|
||||
- 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/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: Build texthooker-ui
|
||||
shell: powershell
|
||||
run: |
|
||||
Set-Location vendor/texthooker-ui
|
||||
bun install --frozen-lockfile
|
||||
bun run build
|
||||
|
||||
- name: Verify managed Windows launcher
|
||||
run: bun test src/main/runtime/managed-launcher.test.ts
|
||||
|
||||
- name: Verify Windows launcher bootstrap
|
||||
run: bun test src/main/runtime/windows-launcher-bootstrap.test.ts
|
||||
|
||||
- name: Build unsigned Windows artifacts
|
||||
run: bun run build:win:unsigned
|
||||
env:
|
||||
SUBMINER_TMDB_API_KEY: ${{ secrets.SUBMINER_TMDB_API_KEY }}
|
||||
|
||||
- name: Smoke packaged runtime assets
|
||||
shell: bash
|
||||
run: bun run test:package "release/win-unpacked/resources"
|
||||
|
||||
- name: Upload Windows artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows
|
||||
path: |
|
||||
release/*.exe
|
||||
release/*.zip
|
||||
release/latest*.yml
|
||||
release/*.blockmap
|
||||
if-no-files-found: error
|
||||
@@ -0,0 +1,186 @@
|
||||
name: Prerelease
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*-beta.*'
|
||||
- 'v*-rc.*'
|
||||
|
||||
concurrency:
|
||||
group: prerelease-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
quality-gate:
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/quality-gate.yml
|
||||
|
||||
package:
|
||||
needs: [quality-gate]
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/package-release.yml
|
||||
secrets:
|
||||
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 }}
|
||||
SUBMINER_TMDB_API_KEY: ${{ secrets.SUBMINER_TMDB_API_KEY }}
|
||||
|
||||
release:
|
||||
needs: [package]
|
||||
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 launcher runtime artifacts
|
||||
run: make build-launcher
|
||||
|
||||
- name: Smoke launcher bundle
|
||||
run: bun dist/launcher/subminer.js --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 \
|
||||
assets/thumbnailers/subminer-ffmpegthumbnailer.thumbnailer \
|
||||
resources/bun/licenses
|
||||
|
||||
- name: Package Bun corresponding source
|
||||
run: bun scripts/package-bun-source.mjs
|
||||
|
||||
- name: Generate checksums
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
files=(release/*.AppImage release/*.dmg release/*.exe release/*.zip release/*.tar.gz release/latest*.yml release/*.blockmap dist/launcher/subminer dist/launcher/subminer.cmd)
|
||||
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
|
||||
env:
|
||||
RELEASE_VERSION: ${{ steps.version.outputs.VERSION }}
|
||||
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
|
||||
if ! bun run changelog:check-prerelease-notes --version "$RELEASE_VERSION"; then
|
||||
echo "::error::release/prerelease-notes.md was not generated for $RELEASE_VERSION. Rerun 'bun run changelog:prerelease-notes --version $RELEASE_VERSION' locally, commit, and retag."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Publish Prerelease
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
RELEASE_VERSION: ${{ steps.version.outputs.VERSION }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
shopt -s nullglob
|
||||
artifacts=(
|
||||
release/*.AppImage
|
||||
release/*.dmg
|
||||
release/*.exe
|
||||
release/*.zip
|
||||
release/*.tar.gz
|
||||
release/*.tar.gz.sha256
|
||||
release/latest*.yml
|
||||
release/*.blockmap
|
||||
release/SHA256SUMS.txt
|
||||
dist/launcher/subminer
|
||||
dist/launcher/subminer.cmd
|
||||
)
|
||||
|
||||
if [ "${#artifacts[@]}" -eq 0 ]; then
|
||||
echo "No release artifacts found for upload."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if gh release view "$RELEASE_VERSION" >/dev/null 2>&1; then
|
||||
gh release edit "$RELEASE_VERSION" \
|
||||
--draft \
|
||||
--prerelease \
|
||||
--title "$RELEASE_VERSION" \
|
||||
--notes-file release/prerelease-notes.md
|
||||
else
|
||||
gh release create "$RELEASE_VERSION" \
|
||||
--draft \
|
||||
--latest=false \
|
||||
--prerelease \
|
||||
--title "$RELEASE_VERSION" \
|
||||
--notes-file release/prerelease-notes.md
|
||||
fi
|
||||
|
||||
for asset in "${artifacts[@]}"; do
|
||||
gh release upload "$RELEASE_VERSION" "$asset" --clobber
|
||||
done
|
||||
|
||||
gh release edit "$RELEASE_VERSION" \
|
||||
--draft=false \
|
||||
--prerelease \
|
||||
--title "$RELEASE_VERSION" \
|
||||
--notes-file release/prerelease-notes.md
|
||||
@@ -0,0 +1,145 @@
|
||||
name: Quality Gate
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
launcher-runtime:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.5
|
||||
|
||||
- name: Verify native runtime staging
|
||||
run: bun test src/main/runtime/managed-launcher.test.ts
|
||||
|
||||
- name: Verify Windows launcher bootstrap
|
||||
run: bun test src/main/runtime/windows-launcher-bootstrap.test.ts
|
||||
|
||||
- name: Verify native mpv process launch
|
||||
run: bun test src/main/runtime/mpv-process.test.ts
|
||||
|
||||
- name: Verify POSIX launcher bootstrap
|
||||
run: bun test src/main/runtime/posix-launcher-bootstrap.test.ts
|
||||
|
||||
quality-gate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
persist-credentials: false
|
||||
|
||||
- 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 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'
|
||||
env:
|
||||
BASE_REF: ${{ github.base_ref }}
|
||||
PR_LABELS: ${{ join(github.event.pull_request.labels.*.name, ',') }}
|
||||
run: bun run changelog:pr-check --base-ref "origin/$BASE_REF" --head-ref "HEAD" --labels "$PR_LABELS"
|
||||
|
||||
- name: Build (TypeScript check)
|
||||
run: bun run typecheck
|
||||
|
||||
- name: Verify generated config examples
|
||||
run: bun run verify:config-example
|
||||
|
||||
- name: Install Lua
|
||||
run: |
|
||||
# Lua needs only Ubuntu sources; unrelated runner repositories can be unavailable.
|
||||
test -f /etc/apt/sources.list.d/ubuntu.sources
|
||||
apt_sources=(-o Dir::Etc::sourcelist=sources.list.d/ubuntu.sources -o Dir::Etc::sourceparts=-)
|
||||
sudo apt-get "${apt_sources[@]}" update
|
||||
sudo apt-get "${apt_sources[@]}" install -y lua5.4
|
||||
sudo ln -sf /usr/bin/lua5.4 /usr/local/bin/lua
|
||||
lua -v
|
||||
|
||||
- name: Launcher unit and script suites
|
||||
run: bun run test:launcher:unit:src && bun run test:scripts
|
||||
|
||||
- name: Environment suite
|
||||
run: bun run test:env
|
||||
|
||||
- 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: 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: Stats UI tests
|
||||
run: bun run test:stats
|
||||
|
||||
- name: Build (bundle)
|
||||
run: bun run build
|
||||
|
||||
- name: Immersion SQLite verification
|
||||
run: bun run test:immersion:sqlite:dist
|
||||
|
||||
- name: Dist smoke suite
|
||||
run: bun run test:smoke:dist
|
||||
|
||||
- name: Security audit
|
||||
run: bun audit --audit-level high
|
||||
|
||||
- name: Build launcher runtime artifacts
|
||||
run: make build-launcher
|
||||
|
||||
- name: Smoke launcher bundle
|
||||
run: bun dist/launcher/subminer.js --help >/dev/null
|
||||
|
||||
- name: Enforce generated launcher workflow
|
||||
run: bash scripts/verify-generated-launcher.sh
|
||||
@@ -4,22 +4,60 @@ on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
- '!v*-beta.*'
|
||||
- '!v*-rc.*'
|
||||
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
quality-gate:
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/quality-gate.yml
|
||||
|
||||
package:
|
||||
needs: [quality-gate]
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/package-release.yml
|
||||
secrets:
|
||||
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 }}
|
||||
SUBMINER_TMDB_API_KEY: ${{ secrets.SUBMINER_TMDB_API_KEY }}
|
||||
|
||||
release:
|
||||
needs: [package]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
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
|
||||
@@ -39,258 +77,248 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: bun install --frozen-lockfile
|
||||
|
||||
- name: Test suite (source)
|
||||
run: bun run test:fast
|
||||
|
||||
- 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: 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
|
||||
vendor/texthooker-ui/node_modules
|
||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'vendor/texthooker-ui/package.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-bun-
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install --frozen-lockfile
|
||||
|
||||
- name: Build texthooker-ui
|
||||
run: |
|
||||
cd vendor/texthooker-ui
|
||||
bun install
|
||||
bun run build
|
||||
|
||||
- name: Build AppImage
|
||||
run: bun run build:appimage
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Upload AppImage artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: appimage
|
||||
path: release/*.AppImage
|
||||
|
||||
build-macos:
|
||||
needs: [quality-gate]
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.5
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.bun/install/cache
|
||||
node_modules
|
||||
vendor/texthooker-ui/node_modules
|
||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'vendor/texthooker-ui/package.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-bun-
|
||||
|
||||
- name: Validate macOS signing/notarization secrets
|
||||
run: |
|
||||
missing=0
|
||||
for name in CSC_LINK CSC_KEY_PASSWORD APPLE_ID APPLE_APP_SPECIFIC_PASSWORD APPLE_TEAM_ID; do
|
||||
if [ -z "${!name}" ]; then
|
||||
echo "Missing required secret: $name"
|
||||
missing=1
|
||||
fi
|
||||
done
|
||||
if [ "$missing" -ne 0 ]; then
|
||||
echo "Set all required macOS signing/notarization secrets and rerun."
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install --frozen-lockfile
|
||||
|
||||
- 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:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
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:
|
||||
needs: [build-linux, build-macos]
|
||||
runs-on: ubuntu-latest
|
||||
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: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.5
|
||||
|
||||
- name: Build Bun subminer wrapper
|
||||
- name: Build launcher runtime artifacts
|
||||
run: make build-launcher
|
||||
|
||||
- name: Verify Bun subminer wrapper
|
||||
run: dist/launcher/subminer --help >/dev/null
|
||||
- name: Smoke launcher bundle
|
||||
run: bun dist/launcher/subminer.js --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: |
|
||||
VERSION="${GITHUB_REF#refs/tags/}"
|
||||
tar -czf "release/subminer-assets-${VERSION}.tar.gz" \
|
||||
tar -czf "release/subminer-assets.tar.gz" \
|
||||
config.example.jsonc \
|
||||
plugin/subminer.lua \
|
||||
plugin/subminer \
|
||||
plugin/subminer.conf \
|
||||
assets/themes/subminer.rasi
|
||||
assets/themes/subminer.rasi \
|
||||
assets/thumbnailers/subminer-ffmpegthumbnailer.thumbnailer \
|
||||
resources/bun/licenses
|
||||
|
||||
- name: Package Bun corresponding source
|
||||
run: bun scripts/package-bun-source.mjs
|
||||
|
||||
- name: Generate checksums
|
||||
run: |
|
||||
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/latest*.yml release/*.blockmap dist/launcher/subminer dist/launcher/subminer.cmd)
|
||||
if [ "${#files[@]}" -eq 0 ]; then
|
||||
echo "No release artifacts found for checksum generation."
|
||||
exit 1
|
||||
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
|
||||
id: version
|
||||
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Generate changelog
|
||||
id: changelog
|
||||
- name: Guard against pending changelog fragments
|
||||
env:
|
||||
RELEASE_VERSION: ${{ steps.version.outputs.VERSION }}
|
||||
run: |
|
||||
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
|
||||
if [ -n "$PREV_TAG" ]; then
|
||||
CHANGES=$(git log --pretty=format:"- %s" ${PREV_TAG}..HEAD)
|
||||
else
|
||||
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")
|
||||
if find changes -maxdepth 1 -name '*.md' -not -name README.md -print -quit | grep -q .; then
|
||||
echo "::error::Pending changelog fragments detected. Run 'bun run changelog:build --version $RELEASE_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."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Verify changelog is ready for tagged release
|
||||
env:
|
||||
RELEASE_VERSION: ${{ steps.version.outputs.VERSION }}
|
||||
run: bun run changelog:check --version "$RELEASE_VERSION"
|
||||
|
||||
- name: Generate release notes from changelog
|
||||
env:
|
||||
RELEASE_VERSION: ${{ steps.version.outputs.VERSION }}
|
||||
run: bun run changelog:release-notes --version "$RELEASE_VERSION"
|
||||
|
||||
- name: Publish Release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
RELEASE_VERSION: ${{ steps.version.outputs.VERSION }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if gh release view "$RELEASE_VERSION" >/dev/null 2>&1; then
|
||||
# Do not pass the prerelease flag here; gh defaults to a normal release.
|
||||
gh release edit "$RELEASE_VERSION" \
|
||||
--draft=false \
|
||||
--title "$RELEASE_VERSION" \
|
||||
--notes-file release/release-notes.md
|
||||
else
|
||||
gh release create "$RELEASE_VERSION" \
|
||||
--title "$RELEASE_VERSION" \
|
||||
--notes-file release/release-notes.md
|
||||
fi
|
||||
echo "CHANGES<<EOF" >> $GITHUB_OUTPUT
|
||||
echo "$CHANGES" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
name: ${{ steps.version.outputs.VERSION }}
|
||||
body: |
|
||||
## 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.lua` to `~/.config/mpv/scripts/`
|
||||
4. Copy `plugin/subminer.conf` to `~/.config/mpv/script-opts/`
|
||||
5. Copy `assets/themes/subminer.rasi` to:
|
||||
- Linux: `~/.local/share/SubMiner/themes/subminer.rasi`
|
||||
- macOS: `~/Library/Application Support/SubMiner/themes/subminer.rasi`
|
||||
|
||||
Note: the `subminer` wrapper script uses Bun (`#!/usr/bin/env bun`), so `bun` must be installed and on `PATH`.
|
||||
files: |
|
||||
shopt -s nullglob
|
||||
artifacts=(
|
||||
release/*.AppImage
|
||||
release/*.dmg
|
||||
release/*.exe
|
||||
release/*.zip
|
||||
release/*.tar.gz
|
||||
release/*.tar.gz.sha256
|
||||
release/latest*.yml
|
||||
release/*.blockmap
|
||||
release/SHA256SUMS.txt
|
||||
dist/launcher/subminer
|
||||
draft: false
|
||||
prerelease: false
|
||||
dist/launcher/subminer.cmd
|
||||
)
|
||||
|
||||
if [ "${#artifacts[@]}" -eq 0 ]; then
|
||||
echo "No release artifacts found for upload."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for asset in "${artifacts[@]}"; do
|
||||
gh release upload "$RELEASE_VERSION" "$asset" --clobber
|
||||
done
|
||||
|
||||
aur-publish:
|
||||
needs: [release]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Get version from tag
|
||||
id: version
|
||||
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: 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
|
||||
id: aur_assets
|
||||
if: steps.aur_prereqs.outputs.skip != 'true' && steps.aur_ssh.outputs.skip != 'true' && steps.aur_clone.outputs.skip != 'true'
|
||||
env:
|
||||
RELEASE_VERSION: ${{ steps.version.outputs.VERSION }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
version="$RELEASE_VERSION"
|
||||
install -dm755 .tmp/aur-release-assets
|
||||
for asset in "SubMiner-${version#v}.AppImage" subminer subminer-assets.tar.gz; do
|
||||
destination=".tmp/aur-release-assets/$asset"
|
||||
if ! curl --fail --silent --show-error --location \
|
||||
--retry 3 --retry-delay 1 --retry-all-errors \
|
||||
--connect-timeout 30 --max-time 600 \
|
||||
--output "$destination.partial" \
|
||||
"$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/releases/download/$version/$asset"; then
|
||||
echo "::warning::Unable to download $asset after retries; skipping automated AUR publish."
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
mv "$destination.partial" "$destination"
|
||||
done
|
||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Update AUR packaging metadata
|
||||
if: steps.aur_prereqs.outputs.skip != 'true' && steps.aur_ssh.outputs.skip != 'true' && steps.aur_clone.outputs.skip != 'true' && steps.aur_assets.outputs.skip != 'true'
|
||||
env:
|
||||
RELEASE_VERSION: ${{ steps.version.outputs.VERSION }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
version_no_v="$RELEASE_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 "$RELEASE_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' && steps.aur_assets.outputs.skip != 'true'
|
||||
working-directory: aur-subminer-bin
|
||||
env:
|
||||
GIT_SSH_COMMAND: ssh -i ~/.ssh/aur -o IdentitiesOnly=yes
|
||||
RELEASE_VERSION: ${{ steps.version.outputs.VERSION }}
|
||||
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 $RELEASE_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,13 +1,24 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
/package-lock.json
|
||||
|
||||
# Superpowers brainstorming
|
||||
.superpowers/
|
||||
|
||||
# Electron build output
|
||||
out/
|
||||
dist/
|
||||
release/
|
||||
release/*
|
||||
!release/
|
||||
!release/release-notes.md
|
||||
!release/prerelease-notes.md
|
||||
build/yomitan/
|
||||
coverage/
|
||||
|
||||
# Launcher build artifact (produced by make build-launcher)
|
||||
subminer
|
||||
/subminer
|
||||
/main-entry.js
|
||||
/main-entry.js.map
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
@@ -21,9 +32,7 @@ Thumbs.db
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
**/CLAUDE.md
|
||||
environment.toml
|
||||
**/CLAUDE.md
|
||||
.env
|
||||
.vscode/*
|
||||
|
||||
@@ -34,5 +43,27 @@ docs/.vitepress/cache/
|
||||
docs/.vitepress/dist/
|
||||
tests/*
|
||||
.worktrees/
|
||||
.tmp/
|
||||
.codex/*
|
||||
.agents/*
|
||||
!.agents/skills/
|
||||
.agents/skills/*
|
||||
!.agents/skills/subminer-change-verification/
|
||||
!.agents/skills/subminer-release/
|
||||
!.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-release/*
|
||||
!.agents/skills/subminer-release/SKILL.md
|
||||
.agents/skills/subminer-scrum-master/*
|
||||
!.agents/skills/subminer-scrum-master/SKILL.md
|
||||
favicon.png
|
||||
.claude/*
|
||||
!stats/public/favicon.png
|
||||
|
||||
# Browser-automation session artifacts (page snapshots, console logs, downloads)
|
||||
.playwright-mcp/
|
||||
@@ -5,3 +5,6 @@
|
||||
[submodule "vendor/yomitan-jlpt-vocab"]
|
||||
path = vendor/yomitan-jlpt-vocab
|
||||
url = https://github.com/stephenmk/yomitan-jlpt-vocab
|
||||
[submodule "vendor/subminer-yomitan"]
|
||||
path = vendor/subminer-yomitan
|
||||
url = https://github.com/ksyasuda/subminer-yomitan
|
||||
@@ -4,3 +4,4 @@ release
|
||||
coverage
|
||||
vendor
|
||||
*.log
|
||||
src/core/services/tokenizer/__fixtures__/golden/*.json
|
||||
@@ -1,29 +1,85 @@
|
||||
# AGENTS.md
|
||||
|
||||
<!-- BACKLOG.MD MCP GUIDELINES START -->
|
||||
## Internal Docs
|
||||
|
||||
<CRITICAL_INSTRUCTION>
|
||||
Start here, then leave this file.
|
||||
|
||||
## BACKLOG WORKFLOW INSTRUCTIONS
|
||||
- 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)
|
||||
- Testing guidelines: [`docs/workflow/testing.md`](./docs/workflow/testing.md)
|
||||
- Knowledge-base rules: [`docs/knowledge-base/README.md`](./docs/knowledge-base/README.md)
|
||||
- Release guide: [`docs/RELEASING.md`](./docs/RELEASING.md)
|
||||
|
||||
This project uses Backlog.md MCP for all task and project management activities.
|
||||
`docs-site/` is user-facing. Do not treat it as the canonical internal source of truth.
|
||||
|
||||
**CRITICAL GUIDANCE**
|
||||
`CLAUDE.md` is a symlink to this file; there is one project instruction file, not two.
|
||||
|
||||
- If your client supports MCP resources, read `backlog://workflow/overview` to understand when and how to use Backlog for this project.
|
||||
- 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).
|
||||
## Quick Start
|
||||
|
||||
- **First time working here?** Read the overview resource IMMEDIATELY to learn the workflow
|
||||
- **Already familiar?** You should have the overview cached ("## Backlog.md Overview (MCP)")
|
||||
- **When to read it**: BEFORE creating tasks, or when you're unsure whether to track work
|
||||
- Init workspace: `git submodule update --init --recursive`
|
||||
- Install deps: `make deps` or `bun install` plus `(cd vendor/texthooker-ui && bun install --frozen-lockfile)`
|
||||
- Fast dev loop: `make dev-watch`
|
||||
- Full local run: `bun run dev`
|
||||
- Verbose Electron debug: `electron . --start --dev --log-level debug`
|
||||
|
||||
These guides cover:
|
||||
- Decision framework for when to create tasks
|
||||
- Search-first workflow to avoid duplicates
|
||||
- Links to detailed guides for task creation, execution, and finalization
|
||||
- MCP tools reference
|
||||
## Build / Test
|
||||
|
||||
You MUST read the overview resource to understand the complete workflow. The information is NOT summarized here.
|
||||
- Runtime/package manager: Bun; use the version pinned by `package.json`.
|
||||
- Follow [`docs/workflow/verification.md`](./docs/workflow/verification.md) and start with the cheapest sufficient lane.
|
||||
- Full handoff gate for substantial changes:
|
||||
`bun run typecheck`
|
||||
`bun run test:fast`
|
||||
`bun run test:env`
|
||||
`bun run build`
|
||||
`bun run test:smoke:dist`
|
||||
- If `docs-site/` changed, also run:
|
||||
`bun run docs:test`
|
||||
`bun run docs:build`
|
||||
- Prefer `make pretty` and `bun run format:check:src`
|
||||
|
||||
</CRITICAL_INSTRUCTION>
|
||||
## Change-Specific Checks
|
||||
|
||||
<!-- BACKLOG.MD MCP GUIDELINES END -->
|
||||
- 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`
|
||||
- Stats dashboard UI (`stats/`): `bun run test:stats`
|
||||
- Overlay / renderer / mpv / mining behavior in the running app: `bun run build`, then `bun run test:e2e` (headless, sandboxed; `bun run e2e start` for exploratory checks)
|
||||
- Build/release scripts (`scripts/**`): `bun run test:scripts`
|
||||
- Internal docs, `AGENTS.md`, or repo skills: `bun run test:docs:kb`
|
||||
- User-facing `docs-site/`: `bun run docs:test`, then `bun run docs:build`
|
||||
- macOS mpv window helper (`scripts/get-mpv-window-macos.swift`): compile it on macOS with `bun run build`
|
||||
- Test lanes are directory-discovered via `scripts/test-lanes.ts`; never hand-list test files in `package.json`
|
||||
|
||||
## Docs Upkeep
|
||||
|
||||
- Docs ship with the change, not after. If a change alters behavior, defaults, flags, shortcuts, ports, or APIs, update the matching docs in the same PR. Touching code without reconciling its docs is an incomplete change.
|
||||
- Source of truth for config defaults is the generated `config.example.jsonc`. Never write a default value into prose you didn't read from it, and don't restate the same default across multiple docs; cite/link to one place so there's a single thing to update.
|
||||
- Trigger map (touch left → update right):
|
||||
- `src/config/definitions/**` (schema/defaults/template) → `bun run generate:config-example`, then reconcile `docs-site/configuration.md` + any feature doc that cites that default
|
||||
- shortcuts/keybindings (`shortcuts.*`, `keybindings`, `stats.*Key`, `subtitleSidebar.toggleKey`, controller bindings) → `docs-site/shortcuts.md`
|
||||
- CLI flags/subcommands (`src/cli/args.ts`, `launcher/**`) → `docs-site/usage.md` + relevant integration doc
|
||||
- feature behavior (anki / jellyfin / jimaku / anilist / youtube / immersion / stats / websocket / sidebar / character-dictionary / annotations) → matching `docs-site/<feature>.md`
|
||||
- architecture / IPC / workflow / internal process → internal `docs/` (system of record)
|
||||
- feature set / requirements / install flow → `README.md`
|
||||
- Removing or renaming a config key: grep `docs-site/` and `docs/` for the old key and any value it documented; legacy/hidden keys (`LEGACY_HIDDEN_CONFIG_PATHS`) should not appear in user docs as current settings.
|
||||
- Verify after doc edits: `bun run verify:config-example` (if config touched), `bun run docs:test`, `bun run docs:build`.
|
||||
|
||||
## Sensitive Files
|
||||
|
||||
- Launcher source of truth: `launcher/*.ts`
|
||||
- Generated launcher artifact: `dist/launcher/subminer`; never hand-edit it
|
||||
- Repo-root `./subminer` is stale; do not revive it
|
||||
- `bun run build` rebuilds bundled Yomitan from `vendor/subminer-yomitan`
|
||||
- Do not change signing/packaging identifiers unless the task explicitly requires it
|
||||
|
||||
## Release / PR Notes
|
||||
|
||||
- User-visible PRs need reconciled current-outcome fragment(s) in `changes/*.md`. Format and rules live in [`changes/README.md`](./changes/README.md) (`type` + `area` keys required; inspect existing same-PR fragments, then update/remove stale bullets or add only genuinely separate outcomes; apply the `skip-changelog` label to opt out).
|
||||
- User-visible docs changes get a `type: docs` fragment
|
||||
- CI enforces `bun run changelog:lint` and `bun run changelog:pr-check`
|
||||
- PR review helpers:
|
||||
- `gh pr view --json number,title --jq '"PR #\\(.number): \\(.title)"'`
|
||||
- `gh api repos/:owner/:repo/pulls/<num>/comments --paginate`
|
||||
- For CI debugging, inspect runs with `gh run list/view`; rerun or fix only within the requested scope.
|
||||
@@ -1,11 +1,11 @@
|
||||
.PHONY: help deps build build-launcher install build-linux build-macos build-macos-unsigned clean install-linux install-macos install-plugin uninstall uninstall-linux uninstall-macos print-dirs pretty ensure-bun generate-config generate-example-config docs-dev docs docs-preview dev-start dev-start-macos dev-toggle dev-stop
|
||||
.PHONY: help submodules 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
|
||||
THEME_SOURCE := assets/themes/subminer.rasi
|
||||
THUMBNAILER_SOURCE := assets/thumbnailers/subminer-ffmpegthumbnailer.thumbnailer
|
||||
LAUNCHER_OUT := dist/launcher/$(APP_NAME)
|
||||
THEME_FILE := subminer.rasi
|
||||
PLUGIN_LUA := plugin/subminer.lua
|
||||
PLUGIN_CONF := plugin/subminer.conf
|
||||
THUMBNAILER_FILE := subminer-ffmpegthumbnailer.thumbnailer
|
||||
|
||||
# Default install prefix for the wrapper script.
|
||||
PREFIX ?= $(HOME)/.local
|
||||
@@ -21,15 +21,11 @@ MACOS_DATA_DIR ?= $(HOME)/Library/Application Support/SubMiner
|
||||
MACOS_APP_DIR ?= $(HOME)/Applications
|
||||
MACOS_APP_DEST ?= $(MACOS_APP_DIR)/SubMiner.app
|
||||
|
||||
# mpv plugin install directories.
|
||||
MPV_CONFIG_DIR ?= $(HOME)/.config/mpv
|
||||
MPV_SCRIPTS_DIR ?= $(MPV_CONFIG_DIR)/scripts
|
||||
MPV_SCRIPT_OPTS_DIR ?= $(MPV_CONFIG_DIR)/script-opts
|
||||
|
||||
# If building from source, the AppImage will typically land in release/.
|
||||
APPIMAGE_SRC := $(firstword $(wildcard release/SubMiner-*.AppImage))
|
||||
MACOS_APP_SRC := $(firstword $(wildcard release/*.app release/*/*.app))
|
||||
MACOS_ZIP_SRC := $(firstword $(wildcard release/SubMiner-*.zip))
|
||||
APPIMAGE_SRC = $(firstword $(wildcard release/SubMiner-*.AppImage))
|
||||
MACOS_APP_SRC = $(firstword $(wildcard release/*.app release/*/*.app))
|
||||
MACOS_ZIP_SRC = $(firstword $(wildcard release/SubMiner-*.zip))
|
||||
PRERELEASE_NOTES := release/prerelease-notes.md
|
||||
|
||||
UNAME_S := $(shell uname -s 2>/dev/null || echo Unknown)
|
||||
ifeq ($(OS),Windows_NT)
|
||||
@@ -42,6 +38,17 @@ else
|
||||
PLATFORM := unknown
|
||||
endif
|
||||
|
||||
WINDOWS_APPDATA ?= $(if $(APPDATA),$(subst \,/,$(APPDATA)),$(HOME)/AppData/Roaming)
|
||||
|
||||
# mpv plugin install directories.
|
||||
ifeq ($(PLATFORM),windows)
|
||||
MPV_CONFIG_DIR ?= $(WINDOWS_APPDATA)/mpv
|
||||
else
|
||||
MPV_CONFIG_DIR ?= $(HOME)/.config/mpv
|
||||
endif
|
||||
MPV_SCRIPTS_DIR ?= $(MPV_CONFIG_DIR)/scripts
|
||||
MPV_SCRIPT_OPTS_DIR ?= $(MPV_CONFIG_DIR)/script-opts
|
||||
|
||||
help:
|
||||
@printf '%s\n' \
|
||||
"Targets:" \
|
||||
@@ -53,21 +60,27 @@ help:
|
||||
" clean Remove build artifacts (dist/, release/, AppImage, binary)" \
|
||||
" dev-start Build and launch local Electron app" \
|
||||
" dev-start-macos Build and launch local Electron app with macOS tracker backend" \
|
||||
" dev-watch Start fast watch loop (tsc + renderer + Electron dev app)" \
|
||||
" dev-watch-macos Start watch loop with forced macOS tracker backend" \
|
||||
" dev-toggle Toggle overlay in a running local Electron app" \
|
||||
" dev-stop Stop a running local Electron app" \
|
||||
" docs-dev Run VitePress docs dev server" \
|
||||
" docs Build VitePress static docs" \
|
||||
" docs-preview Preview built VitePress docs" \
|
||||
" docs-test Run docs tests" \
|
||||
" docs-build Build the docs site" \
|
||||
" docs-build-versioned Build production versioned docs site" \
|
||||
" docs-dev Start the docs dev server" \
|
||||
" install-linux Install Linux 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" \
|
||||
"" \
|
||||
"Other targets:" \
|
||||
" deps Install JS dependencies (root + texthooker-ui)" \
|
||||
" submodules Initialize/update git submodules" \
|
||||
" deps Initialize submodules and install JS dependencies (root + stats + texthooker-ui)" \
|
||||
" uninstall-linux Remove Linux install artifacts" \
|
||||
" uninstall-macos Remove macOS install artifacts" \
|
||||
" uninstall-windows Remove Windows mpv plugin artifacts" \
|
||||
" print-dirs Show resolved install locations" \
|
||||
" lint Lint stats (format check)" \
|
||||
"" \
|
||||
"Variables:" \
|
||||
" PREFIX=... Override wrapper install prefix (default: $$HOME/.local)" \
|
||||
@@ -76,7 +89,7 @@ help:
|
||||
" LINUX_DATA_DIR=... Override Linux app data dir" \
|
||||
" MACOS_DATA_DIR=... Override macOS app data dir" \
|
||||
" MACOS_APP_DIR=... Override macOS app install dir (default: $$HOME/Applications)" \
|
||||
" MPV_CONFIG_DIR=... Override mpv config dir (default: $$HOME/.config/mpv)"
|
||||
" MPV_CONFIG_DIR=... Override mpv config dir (default: $$HOME/.config/mpv or %APPDATA%/mpv on Windows)"
|
||||
|
||||
print-dirs:
|
||||
@printf '%s\n' \
|
||||
@@ -87,26 +100,38 @@ print-dirs:
|
||||
"MACOS_DATA_DIR=$(MACOS_DATA_DIR)" \
|
||||
"MACOS_APP_DIR=$(MACOS_APP_DIR)" \
|
||||
"MACOS_APP_DEST=$(MACOS_APP_DEST)" \
|
||||
"WINDOWS_APPDATA=$(WINDOWS_APPDATA)" \
|
||||
"MPV_CONFIG_DIR=$(MPV_CONFIG_DIR)" \
|
||||
"MPV_SCRIPTS_DIR=$(MPV_SCRIPTS_DIR)" \
|
||||
"MPV_SCRIPT_OPTS_DIR=$(MPV_SCRIPT_OPTS_DIR)" \
|
||||
"APPIMAGE_SRC=$(APPIMAGE_SRC)" \
|
||||
"MACOS_APP_SRC=$(MACOS_APP_SRC)" \
|
||||
"MACOS_ZIP_SRC=$(MACOS_ZIP_SRC)"
|
||||
|
||||
deps:
|
||||
@$(MAKE) --no-print-directory ensure-bun
|
||||
submodules:
|
||||
@git submodule update --init --recursive
|
||||
|
||||
deps: submodules ensure-bun
|
||||
@bun install
|
||||
@cd stats && bun install --frozen-lockfile
|
||||
@cd vendor/texthooker-ui && bun install --frozen-lockfile
|
||||
|
||||
ensure-bun:
|
||||
@command -v bun >/dev/null 2>&1 || { printf '%s\n' "[ERROR] bun not found"; exit 1; }
|
||||
|
||||
pretty: ensure-bun
|
||||
@bun run format
|
||||
@bun run format:src
|
||||
@bun run format:stats
|
||||
|
||||
lint: ensure-bun
|
||||
@bun run lint:stats
|
||||
|
||||
build:
|
||||
@printf '%s\n' "[INFO] Detected platform: $(PLATFORM)"
|
||||
@case "$(PLATFORM)" in \
|
||||
linux) $(MAKE) --no-print-directory build-linux ;; \
|
||||
macos) $(MAKE) --no-print-directory build-macos ;; \
|
||||
windows) printf '%s\n' "[INFO] Windows builds run via: bun run build:win" ;; \
|
||||
*) printf '%s\n' "[ERROR] Unsupported OS for this Makefile target: $(PLATFORM)"; exit 1 ;; \
|
||||
esac
|
||||
|
||||
@@ -115,6 +140,7 @@ install:
|
||||
@case "$(PLATFORM)" in \
|
||||
linux) $(MAKE) --no-print-directory install-linux ;; \
|
||||
macos) $(MAKE) --no-print-directory install-macos ;; \
|
||||
windows) $(MAKE) --no-print-directory install-windows ;; \
|
||||
*) printf '%s\n' "[ERROR] Unsupported OS for this Makefile target: $(PLATFORM)"; exit 1 ;; \
|
||||
esac
|
||||
|
||||
@@ -134,18 +160,20 @@ build-macos-unsigned: deps
|
||||
@bun run build:mac:unsigned
|
||||
|
||||
build-launcher:
|
||||
@printf '%s\n' "[INFO] Bundling launcher script"
|
||||
@install -d "$(dir $(LAUNCHER_OUT))"
|
||||
@bun build ./launcher/main.ts --target=bun --packages=bundle --outfile="$(LAUNCHER_OUT)"
|
||||
@if ! head -1 "$(LAUNCHER_OUT)" | grep -q '^#!/usr/bin/env bun'; then \
|
||||
{ printf '#!/usr/bin/env bun\n'; cat "$(LAUNCHER_OUT)"; } > "$(LAUNCHER_OUT).tmp" && mv "$(LAUNCHER_OUT).tmp" "$(LAUNCHER_OUT)"; \
|
||||
fi
|
||||
@chmod +x "$(LAUNCHER_OUT)"
|
||||
@printf '%s\n' "[INFO] Launcher artifact: $(LAUNCHER_OUT)"
|
||||
@printf '%s\n' "[INFO] Building launcher runtime artifacts"
|
||||
@bun run build:launcher
|
||||
|
||||
clean:
|
||||
@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"
|
||||
|
||||
generate-config: ensure-bun
|
||||
@@ -153,18 +181,8 @@ generate-config: ensure-bun
|
||||
@bun run electron . --generate-config
|
||||
|
||||
generate-example-config: ensure-bun
|
||||
@bun run build
|
||||
@bun run generate:config-example
|
||||
|
||||
docs-dev: ensure-bun
|
||||
@bun run docs:dev
|
||||
|
||||
docs: ensure-bun
|
||||
@bun run docs:build
|
||||
|
||||
docs-preview: ensure-bun
|
||||
@bun run docs:preview
|
||||
|
||||
dev-start: ensure-bun
|
||||
@bun run build
|
||||
@bun run electron . --start
|
||||
@@ -173,26 +191,48 @@ dev-start-macos: ensure-bun
|
||||
@bun run build
|
||||
@bun run electron . --start --backend macos
|
||||
|
||||
dev-watch: ensure-bun
|
||||
@bash scripts/dev-watch.sh
|
||||
|
||||
dev-watch-macos: ensure-bun
|
||||
@bash scripts/dev-watch.sh --start --dev --backend macos
|
||||
|
||||
dev-toggle: ensure-bun
|
||||
@bun run electron . --toggle
|
||||
|
||||
dev-stop: ensure-bun
|
||||
@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
|
||||
@printf '%s\n' "[INFO] Installing Linux wrapper/theme artifacts"
|
||||
@printf '%s\n' "[INFO] Installing Linux wrapper/support artifacts"
|
||||
@install -d "$(BINDIR)"
|
||||
@install -m 0755 "$(LAUNCHER_OUT)" "$(BINDIR)/$(APP_NAME)"
|
||||
@install -d "$(LINUX_DATA_DIR)/themes"
|
||||
@install -m 0644 "./$(THEME_SOURCE)" "$(LINUX_DATA_DIR)/themes/$(THEME_FILE)"
|
||||
@install -d "$(LINUX_DATA_DIR)/thumbnailers"
|
||||
@install -m 0644 "./$(THUMBNAILER_SOURCE)" "$(LINUX_DATA_DIR)/thumbnailers/$(THUMBNAILER_FILE)"
|
||||
@install -d "$(LINUX_DATA_DIR)/plugin/subminer"
|
||||
@cp -R ./plugin/subminer/. "$(LINUX_DATA_DIR)/plugin/subminer/"
|
||||
@if [ -n "$(APPIMAGE_SRC)" ]; then \
|
||||
install -m 0755 "$(APPIMAGE_SRC)" "$(BINDIR)/SubMiner.AppImage"; \
|
||||
else \
|
||||
printf '%s\n' "[WARN] No release/SubMiner-*.AppImage found; skipping AppImage install"; \
|
||||
printf '%s\n' " Build one with: make build"; \
|
||||
fi
|
||||
@printf '%s\n' "Installed to:" " $(BINDIR)/subminer" " $(LINUX_DATA_DIR)/themes/$(THEME_FILE)"
|
||||
@printf '%s\n' "Installed to:" " $(BINDIR)/subminer" " $(LINUX_DATA_DIR)/themes/$(THEME_FILE)" " $(LINUX_DATA_DIR)/thumbnailers/$(THUMBNAILER_FILE)"
|
||||
|
||||
install-macos: build-launcher
|
||||
@printf '%s\n' "[INFO] Installing macOS wrapper/theme/app artifacts"
|
||||
@@ -200,6 +240,8 @@ install-macos: build-launcher
|
||||
@install -m 0755 "$(LAUNCHER_OUT)" "$(BINDIR)/$(APP_NAME)"
|
||||
@install -d "$(MACOS_DATA_DIR)/themes"
|
||||
@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)"
|
||||
@if [ -n "$(MACOS_APP_SRC)" ]; then \
|
||||
rm -rf "$(MACOS_APP_DEST)"; \
|
||||
@@ -215,24 +257,34 @@ install-macos: build-launcher
|
||||
fi
|
||||
@printf '%s\n' "Installed to:" " $(BINDIR)/subminer" " $(MACOS_DATA_DIR)/themes/$(THEME_FILE)" " $(MACOS_APP_DEST)"
|
||||
|
||||
install-plugin:
|
||||
@printf '%s\n' "[INFO] Installing mpv plugin artifacts"
|
||||
@install -d "$(MPV_SCRIPTS_DIR)"
|
||||
@install -d "$(MPV_SCRIPT_OPTS_DIR)"
|
||||
@install -m 0644 "./$(PLUGIN_LUA)" "$(MPV_SCRIPTS_DIR)/subminer.lua"
|
||||
@install -m 0644 "./$(PLUGIN_CONF)" "$(MPV_SCRIPT_OPTS_DIR)/subminer.conf"
|
||||
@printf '%s\n' "Installed to:" " $(MPV_SCRIPTS_DIR)/subminer.lua" " $(MPV_SCRIPT_OPTS_DIR)/subminer.conf"
|
||||
install-windows:
|
||||
@printf '%s\n' "[INFO] Windows builds run via: bun run build:win"
|
||||
@printf '%s\n' "[INFO] SubMiner-managed mpv launches inject the bundled runtime plugin; no global mpv plugin install is needed."
|
||||
|
||||
# Uninstall behavior kept unchanged by default.
|
||||
uninstall: uninstall-linux
|
||||
uninstall:
|
||||
@printf '%s\n' "[INFO] Detected platform: $(PLATFORM)"
|
||||
@case "$(PLATFORM)" in \
|
||||
linux) $(MAKE) --no-print-directory uninstall-linux ;; \
|
||||
macos) $(MAKE) --no-print-directory uninstall-macos ;; \
|
||||
windows) $(MAKE) --no-print-directory uninstall-windows ;; \
|
||||
*) printf '%s\n' "[ERROR] Unsupported OS for this Makefile target: $(PLATFORM)"; exit 1 ;; \
|
||||
esac
|
||||
|
||||
uninstall-linux:
|
||||
@rm -f "$(BINDIR)/subminer" "$(BINDIR)/SubMiner.AppImage"
|
||||
@rm -f "$(LINUX_DATA_DIR)/themes/$(THEME_FILE)"
|
||||
@printf '%s\n' "Removed:" " $(BINDIR)/subminer" " $(BINDIR)/SubMiner.AppImage" " $(LINUX_DATA_DIR)/themes/$(THEME_FILE)"
|
||||
@rm -f "$(LINUX_DATA_DIR)/thumbnailers/$(THUMBNAILER_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)/thumbnailers/$(THUMBNAILER_FILE)" " $(LINUX_DATA_DIR)/plugin/subminer"
|
||||
|
||||
uninstall-macos:
|
||||
@rm -f "$(BINDIR)/subminer"
|
||||
@rm -f "$(MACOS_DATA_DIR)/themes/$(THEME_FILE)"
|
||||
@rm -rf "$(MACOS_DATA_DIR)/plugin/subminer"
|
||||
@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,96 +1,275 @@
|
||||
<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)
|
||||
[]()
|
||||
[](https://docs.subminer.moe)
|
||||
<img src="assets/SubMiner.png" width="160" alt="SubMiner logo">
|
||||
|
||||
# SubMiner
|
||||
|
||||
Look up words with Yomitan, mine them to Anki, and track your immersion without leaving mpv
|
||||
|
||||
[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/7abab8a9-4e4e-4f06-9f3c-9783e15a3807)
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
## Features
|
||||
|
||||
### Dictionary Lookups
|
||||
|
||||
Hover over any word in the subtitles to open the full Yomitan popup with definitions, pitch accent, and frequency data. SubMiner bundles its own Yomitan, separate from any browser install.
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](./assets/minecard.mp4)
|
||||
|
||||
<img src="docs-site/public/screenshots/yomitan-lookup.png" width="800" alt="Yomitan dictionary popup over annotated subtitles in mpv">
|
||||
</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 from the exact playback moment with one key press, click, or controller input. SubMiner fills in the sentence, an audio clip, and a screenshot or animated image.
|
||||
|
||||
- **Hover to look up** — Yomitan dictionary popups directly on subtitles
|
||||
- **One-key mining** — Creates Anki cards with sentence, audio, screenshot, and translation
|
||||
- **N+1 highlighting** — Marks known words from your Anki deck so unknown ones jump out
|
||||
- **Subtitle tools** — Download from Jimaku, sync with alass/ffsubsync, all in-player
|
||||
- **Immersion tracking** — SQLite-powered stats on your watch time and mining activity
|
||||
- **Texthooker page built in** — WebSocket streaming to external tools, no extra setup
|
||||
<div align="center">
|
||||
<img src="docs-site/public/screenshots/one-key-mining.png" width="800" alt="Anki card created from SubMiner with sentence, audio, and screenshot">
|
||||
</div>
|
||||
|
||||
## Quick start
|
||||
<br>
|
||||
|
||||
### 1. Install
|
||||
### Reading Annotations
|
||||
|
||||
**Linux (AppImage):**
|
||||
Subtitles are annotated as they play with frequency highlighting, JLPT tags, N+1 targeting, and character names from a generated dictionary. Particles and grammar-only tokens stay plain so the words worth learning stand out.
|
||||
|
||||
```bash
|
||||
wget https://github.com/ksyasuda/SubMiner/releases/latest/download/SubMiner-0.1.0.AppImage -O ~/.local/bin/SubMiner.AppImage
|
||||
chmod +x ~/.local/bin/SubMiner.AppImage
|
||||
wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer -O ~/.local/bin/subminer
|
||||
chmod +x ~/.local/bin/subminer
|
||||
```
|
||||
<div align="center">
|
||||
<img src="docs-site/public/screenshots/annotations.png" width="800" alt="Annotated subtitles with frequency coloring, JLPT underlines, and N+1 targets">
|
||||
</div>
|
||||
|
||||
> [!NOTE]
|
||||
> The `subminer` wrapper uses a [Bun](https://bun.sh) shebang. Make sure `bun` is on your `PATH`.
|
||||
<br>
|
||||
|
||||
**From source** or **macOS** — see the [installation guide](https://docs.subminer.moe/installation#from-source).
|
||||
### Immersion Dashboard
|
||||
|
||||
### 2. Install the mpv plugin and configuration file
|
||||
A stats dashboard tracks watch time, vocabulary growth, mining throughput, session history, and trends. Everything stays on your machine, with no third-party tracking.
|
||||
|
||||
```bash
|
||||
wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer-assets-0.1.0.tar.gz -O /tmp/subminer-assets.tar.gz
|
||||
tar -xzf /tmp/subminer-assets.tar.gz -C /tmp
|
||||
cp /tmp/plugin/subminer.lua ~/.config/mpv/scripts/
|
||||
cp /tmp/plugin/subminer.conf ~/.config/mpv/script-opts/
|
||||
mkdir -p ~/.config/SubMiner && cp /tmp/config.example.jsonc ~/.config/SubMiner/config.jsonc
|
||||
```
|
||||
<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>
|
||||
|
||||
### 3. Set up Yomitan Dictionaries
|
||||
<br>
|
||||
|
||||
```bash
|
||||
subminer app --start --yomitan
|
||||
```
|
||||
### Integrations
|
||||
|
||||
### 4. Mine
|
||||
<table>
|
||||
<tr>
|
||||
<td><b>YouTube</b></td>
|
||||
<td>Play YouTube URLs with subtitle tracks picked by your language priorities, or choose tracks yourself in the overlay picker (<code>Ctrl+Alt+C</code>). Requires <code>yt-dlp</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 your Jellyfin library, or cast to SubMiner from any Jellyfin client. Setup and discovery live in the tray menu</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Jimaku</b></td>
|
||||
<td>Search and download Japanese subtitles. Requires a free Jimaku API key</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>TsukiHime</b></td>
|
||||
<td>Search and download subtitles extracted from anime releases, with Japanese and secondary-language tabs (<code>Ctrl+Shift+T</code>). Requires <code>xz</code> on your <code>PATH</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Subtitle generation</b></td>
|
||||
<td>Transcribe a video's audio into Japanese subtitles locally from the generation modal (<code>Ctrl+Shift+G</code>), the subtitle sidebar, or the launcher. SubMiner can download models for you; optional Silero speech detection helps focus on dialogue. Requires whisper.cpp and FFmpeg. <a href="https://docs.subminer.moe/subtitle-generation">Setup guide</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>AniSkip</b></td>
|
||||
<td>Automatic intro detection with chapter markers and a one-key skip (<code>TAB</code> by default)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>alass / ffsubsync</b></td>
|
||||
<td>Retime a subtitle against the audio or another subtitle track (<code>Ctrl+Alt+S</code>). Requires <code>alass</code> or <code>ffsubsync</code>; set <code>subsync.alass_path</code> or <code>subsync.ffsubsync_path</code> if they are not in <code>/usr/bin</code></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>
|
||||
|
||||
```bash
|
||||
subminer app --start --background
|
||||
subminer video.mkv
|
||||
```
|
||||
<br>
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
| Required | Optional |
|
||||
| ------------------------------------------ | -------------------------------------------------- |
|
||||
| `bun` | |
|
||||
| `mpv` with IPC socket | `yt-dlp` |
|
||||
| `ffmpeg` | `guessit` (better AniSkip title/episode detection) |
|
||||
| `mecab` + `mecab-ipadic` | `fzf` / `rofi` |
|
||||
| Linux: `hyprctl` or `xdotool` + `xwininfo` | `chafa`, `ffmpegthumbnailer` |
|
||||
| macOS: Accessibility permission | |
|
||||
SubMiner runs on Linux, macOS 11+, and Windows 10+. Only **mpv** is required to run it (plus `fuse2` for the Linux AppImage). Mining cards also needs Anki with the [AnkiConnect](https://ankiweb.net/shared/info/2055492159) add-on. Everything else is optional.
|
||||
|
||||
| Dependency | Status | What it does |
|
||||
| ------------------------ | ---------------- | -------------------------------------------------------------------- |
|
||||
| mpv | Required | The video player SubMiner draws over |
|
||||
| fuse2 | Required (Linux) | Running the AppImage |
|
||||
| Anki + AnkiConnect | Required to mine | Card creation from the Yomitan popup |
|
||||
| ffmpeg | Recommended | Audio clips and screenshots on cards |
|
||||
| MeCab + mecab-ipadic | Recommended | More accurate N+1, JLPT, and frequency highlighting |
|
||||
| xdotool + xwininfo | Required (X11) | Window tracking on desktops other than Hyprland or Sway |
|
||||
| yt-dlp | Optional | YouTube playback |
|
||||
| xz | Optional | TsukiHime subtitle downloads (most Linux distros already have it) |
|
||||
| alass / ffsubsync | Optional | Subtitle sync |
|
||||
| whisper.cpp | Optional | [Subtitle generation](https://docs.subminer.moe/subtitle-generation) |
|
||||
| guessit | Optional | Better title, season, and episode detection for AniSkip and AniList |
|
||||
| fzf / rofi | Optional | Video picker in the `subminer` launcher (rofi is Linux only) |
|
||||
| chafa, ffmpegthumbnailer | Optional | Thumbnail previews in the launcher pickers |
|
||||
|
||||
<details>
|
||||
<summary><b>Platform-specific install commands</b></summary>
|
||||
|
||||
**Arch Linux:**
|
||||
|
||||
```bash
|
||||
sudo pacman -S --needed mpv ffmpeg
|
||||
paru -S --needed mecab-git mecab-ipadic # MeCab is only in the AUR
|
||||
```
|
||||
|
||||
On desktops other than Hyprland or Sway, also install `xdotool` and `xorg-xwininfo`.
|
||||
|
||||
**macOS:**
|
||||
|
||||
```bash
|
||||
brew install mpv ffmpeg mecab mecab-ipadic
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
|
||||
```powershell
|
||||
winget install shinchiro.mpv
|
||||
winget install Gyan.FFmpeg
|
||||
```
|
||||
|
||||
Then reopen your terminal and check `mpv --version` and `ffmpeg -version`. ffmpeg must be on `PATH`; mpv does not have to be. If `mpv` is not found, either add its folder (usually `%LOCALAPPDATA%\Programs\mpv`) to `PATH` or enter the full path to `mpv.exe` during first-run setup.
|
||||
|
||||
[Scoop](https://scoop.sh) is the alternative if you want one package manager for everything. It is the only one that also carries `xz`:
|
||||
|
||||
```powershell
|
||||
scoop bucket add extras
|
||||
scoop install extras/mpv main/ffmpeg main/yt-dlp main/xz
|
||||
```
|
||||
|
||||
See the [installation guide](https://docs.subminer.moe/installation#_1-install-requirements) for Ubuntu, Debian, and Fedora commands and the full optional package lists.
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Install SubMiner
|
||||
|
||||
<details>
|
||||
<summary><b>Arch Linux (AUR)</b></summary>
|
||||
|
||||
```bash
|
||||
paru -S subminer-bin
|
||||
```
|
||||
|
||||
Includes the AppImage and the `subminer` command.
|
||||
|
||||
</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
|
||||
```
|
||||
|
||||
The AppImage is all you need. First-run setup can install the optional `subminer` command, which runs on a copy of Bun bundled with the app, so you do not need Bun installed.
|
||||
|
||||
</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`.
|
||||
|
||||
Then enable SubMiner under **System Settings > Privacy & Security > Accessibility** (named **Device Control and Data Access** on macOS 27), or the overlay cannot follow the mpv window. If macOS blocks the first launch, right-click the app and choose **Open**.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Windows</b></summary>
|
||||
|
||||
Download and run the latest installer (`SubMiner-<version>.exe`) from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest). A portable `.zip` is also available.
|
||||
|
||||
</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
|
||||
|
||||
Start SubMiner and the setup window opens on first launch. It creates your config file, imports Yomitan dictionaries (you need at least one for lookups), and can install the `subminer` command. On Windows it also creates a **SubMiner mpv** shortcut.
|
||||
|
||||
```bash
|
||||
subminer app --setup # AUR
|
||||
~/.local/bin/SubMiner.AppImage --setup # AppImage
|
||||
```
|
||||
|
||||
On **macOS**, open `SubMiner.app` from `/Applications`. On **Windows**, run SubMiner from the Start menu. To reopen setup later, run `subminer app --setup`.
|
||||
|
||||
For card creation, keep Anki open with AnkiConnect installed. SubMiner connects to it at its default address with no extra setup.
|
||||
|
||||
### 3. Mine
|
||||
|
||||
```bash
|
||||
subminer video.mkv # play a video with SubMiner
|
||||
subminer /path/to/dir # pick a file with fzf
|
||||
subminer -R /path/to/dir # pick a file with rofi (Linux only)
|
||||
subminer -H # watch history: replay, next, or previous episode
|
||||
subminer doctor # check your setup
|
||||
```
|
||||
|
||||
On **Windows**, double-click the **SubMiner mpv** shortcut or drag a video file onto it.
|
||||
|
||||
Starting mpv some other way? See [Launching mpv yourself](https://docs.subminer.moe/installation#launching-mpv-yourself) for the IPC socket option the overlay needs.
|
||||
|
||||
## 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
|
||||
|
||||
Built on the shoulders of [GameSentenceMiner](https://github.com/bpwhelan/GameSentenceMiner), [mpvacious](https://github.com/Ajatt-Tools/mpvacious), [Anacreon-Script](https://github.com/friedrich-de/Anacreon-Script), and [autosubsync-mpv](https://github.com/joaquintorres/autosubsync-mpv). Subtitles powered by [Jimaku.cc](https://jimaku.cc). Dictionary lookups via [Yomitan](https://github.com/yomidevs/yomitan).
|
||||
SubMiner builds on the work of these open-source projects:
|
||||
|
||||
| Project | Role |
|
||||
| ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
||||
| [ani-skip](https://github.com/synacktraa/ani-skip) | AniSkip API client for anime intro/outro skip timestamps |
|
||||
| [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 |
|
||||
| [Bun](https://github.com/oven-sh/bun) | Bundled runtime for the `subminer` command-line launcher |
|
||||
| [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
|
||||
|
||||
[GNU General Public License v3.0](LICENSE)
|
||||
SubMiner is released under the [GNU General Public License v3.0](LICENSE).
|
||||
|
||||
Release packages also bundle an unmodified copy of [Bun](https://github.com/oven-sh/bun), which is MIT licensed and statically links JavaScriptCore (LGPL 2.0) and TinyCC (LGPL 2.1). Its license texts and third-party notices ship inside the app under `resources/bun/licenses`, and each release publishes `bun-v1.3.5-source.tar.gz` with the corresponding source. See [Bundled Bun runtime](https://docs.subminer.moe/installation#bundled-bun-runtime).
|
||||
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 364 KiB |
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 433 B After Width: | Height: | Size: 253 B |
|
Before Width: | Height: | Size: 580 B After Width: | Height: | Size: 366 B |
|
Before Width: | Height: | Size: 308 KiB |
|
Before Width: | Height: | Size: 13 MiB |
|
Before Width: | Height: | Size: 523 KiB |
|
After Width: | Height: | Size: 3.8 MiB |
@@ -0,0 +1,4 @@
|
||||
[Thumbnailer Entry]
|
||||
TryExec=ffmpegthumbnailer
|
||||
Exec=ffmpegthumbnailer -i %i -o %o -s %s -f
|
||||
MimeType=video/matroska;video/matroska-3d;video/x-matroska;video/x-matroska-3d;
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
id: m-0
|
||||
title: 'Codebase Clarity & Composability'
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Improvements to code clarity, simplicity, and composability identified during the Feb 2026 codebase review. Focus on reducing monolithic files, eliminating duplication, and improving architectural boundaries.
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
id: TASK-19
|
||||
title: Enable overlay keybinds whenever app runtime is active
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-12 08:47'
|
||||
updated_date: '2026-02-12 09:40'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Restored task after accidental cleanup. Ensure keybindings are available whenever the Electron runtime is active, while respecting focused overlay/input contexts that require local key handling.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Started implementation: tracing overlay shortcut registration lifecycle and runtime activation gating.
|
||||
|
||||
Root cause: overlay shortcut sync executes during overlay runtime initialization before overlayRuntimeInitialized is set true, so registration could be skipped until a later visibility toggle.
|
||||
|
||||
Implemented fix in initializeOverlayRuntime: after setting overlayRuntimeInitialized = true, immediately call syncOverlayShortcuts() to register overlay keybinds for active runtime state.
|
||||
|
||||
Follow-up from repro: startup path with --start did not initialize overlay runtime (commandNeedsOverlayRuntime excluded start), so overlay keybinds stayed unavailable until first overlay visibility command.
|
||||
|
||||
Updated CLI runtime gating so --start initializes overlay runtime, which activates overlay shortcut registration immediately.
|
||||
|
||||
User clarified MPV-only workflow requirement. Added MPV plugin keybindings and script messages for mining/runtime actions (copy/mine/multi/mode/field-grouping/subsync/audio-card/runtime-options) so these actions are available from mpv chord bindings without relying on overlay global shortcuts.
|
||||
|
||||
Per user direction, reverted all shortcut/runtime/plugin changes from this implementation cycle. Desired behavior is to keep keybindings working only when overlay is active.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Ensured overlay shortcuts are available as soon as overlay runtime becomes active by resyncing after activation flag is set. This prevents startup states where shortcuts remained inactive until a later overlay visibility change.
|
||||
|
||||
Follow-up fix: included --start in overlay-runtime-required commands so keybinds are active right after startup, even before toggling visible/invisible overlays.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
id: TASK-20
|
||||
title: Implement content-bounded overlay windows and decoupled secondary top bar
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-12 08:47'
|
||||
updated_date: '2026-02-12 09:42'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Implement the overlay sizing redesign documented in `overlay_window.md`: move visible/invisible overlays from fullscreen bounds to content-bounded sizing, and decouple secondary subtitle rendering into an independent top bar window/lifecycle.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Per-layer bounds ownership is implemented for overlay windows (no shared full-bounds setter for all layers).
|
||||
- [ ] #2 Renderer-to-main IPC contract exists for measured overlay content bounds with layer identity and safe validation.
|
||||
- [ ] #3 Visible and invisible overlays use content-bounded sizing with padding/clamp/jitter protections and full-bounds fallback when measurements are unavailable.
|
||||
- [ ] #4 Secondary subtitle top bar is decoupled from primary overlay visibility and follows mode-specific behavior.
|
||||
- [ ] #5 Automated tests and manual validation matrix cover wrapping, style changes, monitor moves, tracker churn, and simultaneous overlay states.
|
||||
<!-- AC:END -->
|
||||
@@ -1,32 +0,0 @@
|
||||
---
|
||||
id: TASK-20.3
|
||||
title: >-
|
||||
Implement content-bounded sizing algorithm for visible and invisible overlay
|
||||
windows
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-12 08:47'
|
||||
updated_date: '2026-02-12 09:42'
|
||||
labels: []
|
||||
dependencies: []
|
||||
parent_task_id: TASK-20
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Implement content-bounded sizing for visible/invisible windows using measured rects plus tracker origin, with robust clamping and jitter resistance.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Bounds algorithm applies configurable padding, minimum size, display-workarea clamp, and integer snap.
|
||||
- [ ] #2 Main-process bounds updates are thresholded/debounced to reduce jitter and unnecessary `setBounds` churn.
|
||||
- [ ] #3 When no valid measurement exists, layer falls back to safe tracker/display bounds without breaking interaction.
|
||||
- [ ] #4 Visible+invisible overlays can coexist without full-window overlap/input conflicts caused by shared fullscreen bounds.
|
||||
<!-- AC:END -->
|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
id: TASK-20.4
|
||||
title: Implement dedicated secondary top-bar overlay window
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-12 09:43'
|
||||
labels: []
|
||||
dependencies: []
|
||||
parent_task_id: TASK-20
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Create and integrate a dedicated secondary subtitle overlay window with independent lifecycle, z-order, bounds, and pointer policy, decoupled from primary visible/invisible overlay windows.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 A third overlay window dedicated to secondary subtitles is created and managed alongside existing visible/invisible windows.
|
||||
- [ ] #2 Secondary window visibility follows secondary mode semantics (`hidden`/`visible`/`hover`) independent of primary overlay visibility.
|
||||
- [ ] #3 Secondary subtitle text/mode/style updates are routed directly to the secondary window renderer path.
|
||||
- [ ] #4 Pointer passthrough/interaction behavior for secondary window is explicit and does not regress existing hover/selection interactions.
|
||||
- [ ] #5 Window cleanup/lifecycle (create, close, restore) integrates with existing overlay runtime lifecycle.
|
||||
<!-- AC:END -->
|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
id: TASK-20.5
|
||||
title: 'Add rollout guards, tests, and validation matrix for content-bounded overlays'
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-12 09:43'
|
||||
labels: []
|
||||
dependencies: []
|
||||
parent_task_id: TASK-20
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Add safety controls and verification coverage for the new content-bounded overlay architecture and secondary top-bar window.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Feature flag or equivalent rollout guard exists for switching to new sizing/window behavior.
|
||||
- [ ] #2 Service-level/unit tests cover bounds clamping, jitter thresholding, invalid measurement fallback, and per-layer updates.
|
||||
- [ ] #3 Manual validation checklist documents and verifies wrap/no-wrap, style changes, monitor moves, tracker churn, modal interactions, and simultaneous overlay states.
|
||||
- [ ] #4 Regression checks confirm existing single-layer and startup/shutdown behavior remain stable.
|
||||
- [ ] #5 Task includes explicit pass/fail notes from validation run(s).
|
||||
<!-- AC:END -->
|
||||
@@ -1,36 +0,0 @@
|
||||
---
|
||||
id: TASK-22
|
||||
title: Make secondary subtitles hover-revealed but non-lookupable in Yomitan sessions
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 16:40'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Investigate and implement a UX where secondary subtitles (e.g., English text in our current sessions) become visible when hovered, while explicitly preventing user interactions that allow text lookup (including Yomitan integration) on those subtitles. This should allow readability-on-hover without exposing the secondary overlay text to selection/lookup workflows.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Secondary subtitles become visible only while hovered (or via equivalent hover-triggered mechanism), and return to default hidden/low-visibility state when not hovered.
|
||||
- [ ] #2 When hovered, secondary subtitles do not trigger Yomitan lookup behavior in sessions where Yomitan is enabled.
|
||||
- [ ] #3 Secondary subtitles remain non-interactive for lookup paths (for example, text selection or lookup event propagation) while hover-visibility still works as intended.
|
||||
- [ ] #4 Primary subtitles remain functional and are not regressed by the secondary-subtitle interaction changes.
|
||||
- [ ] #5 If complete prevention of Yomitan lookup on secondary subtitles is not technically possible, the task includes the known limitations and a documented fallback behavior.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 Acceptance criteria are reviewed and covered by explicit manual/automated test coverage for hover reveal and lookup suppression behavior.
|
||||
<!-- DOD:END -->
|
||||
@@ -1,55 +0,0 @@
|
||||
---
|
||||
id: TASK-30
|
||||
title: Enable anime streaming via extension repos with configurable mpv playback flow
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 18:32'
|
||||
updated_date: '2026-02-13 18:34'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Add a feature to query configured extension repositories for anime titles/episodes from SubMiner, let users select a streamable source, and play it through mpv with minimal friction. The result should be interactive from Electron (and triggered from mpv via existing command bridge), and fully configurable in app config.
|
||||
|
||||
The implementation should provide a modular backend resolver and a clear UI flow that mirrors existing modal interaction patterns, while keeping mpv playback unchanged (use loadfile with resolved URL and optional headers/referrer metadata).
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Create a stable config schema for one or more extension source backends (repos/endpoints/flags) and persist in user config + default template.
|
||||
- [ ] #2 Resolve an anime search term to candidate series from configured sources.
|
||||
- [ ] #3 Resolve an episode selection to at least one playable stream URL candidate with playback metadata when available.
|
||||
- [ ] #4 Provide an interactive user flow in the app to search/select episode and choose stream source.
|
||||
- [ ] #5 Play the selected stream by invoking the existing mpv command path.
|
||||
- [ ] #6 Support launching the flow from mpv interactions (keyboard/menu) by forwarding a command/event into the renderer modal flow.
|
||||
- [ ] #7 Add error states for empty results, no playable source, and repository failures, with clear user messages.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Execution sequence for implementation: 1) TASK-30.1 (config/model), 2) TASK-30.2 (resolver), 3) TASK-30.3 (IPC), 4) TASK-30.4 (UI modal), 5) TASK-30.5 (mpv trigger), 6) TASK-30.6 (validation/rollout checklist).
|
||||
|
||||
Rollout recommendation: complete TASK-30.6 only after TASK-30.1-30.5 are done and can be verified in combination.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 Config schema validated in app startup (bad config surfaces clear error).
|
||||
- [ ] #2 No hardcoded source/resolver URLs in UI layer; resolver details are backend-driven.
|
||||
- [ ] #3 Play command path uses existing mpv IPC/runtime helpers.
|
||||
- [ ] #4 Documentation includes how to configure extension repos and optional auth/headers.
|
||||
- [ ] #5 Feature is gated behind a config flag and can be disabled cleanly.
|
||||
<!-- DOD:END -->
|
||||
@@ -1,48 +0,0 @@
|
||||
---
|
||||
id: TASK-30.1
|
||||
title: Design extension source config model and defaults
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 18:32'
|
||||
updated_date: '2026-02-13 18:34'
|
||||
labels: []
|
||||
dependencies: []
|
||||
parent_task_id: TASK-30
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Define a backend-agnostic configuration contract for extension repository streaming, including resolver endpoints/process mode, query/auth headers, timeouts, enable flags, and source preference. Wire schema through Config/ResolvedConfig and generated template/defaults so users can manage repos entirely through config.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Add new config sections for extension source providers in Config and ResolvedConfig types.
|
||||
- [ ] #2 Add validation defaults and env-compatible parsing for provider list, auth, header overrides, and feature flags.
|
||||
- [ ] #3 Update config template and docs text so defaults are discoverable and editable.
|
||||
- [ ] #4 Invalid/missing config should fail fast with a clear message path.
|
||||
- [ ] #5 Existing config readers do not regress when no providers are configured.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Phase 1 — Foundation: config contract + validation + defaults
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 Config examples in template/docs include at least one provider entry shape.
|
||||
- [ ] #2 Defaults remain backward-compatible when key is absent.
|
||||
- [ ] #3 Feature can be disabled without touching unrelated settings.
|
||||
<!-- DOD:END -->
|
||||
@@ -1,50 +0,0 @@
|
||||
---
|
||||
id: TASK-30.2
|
||||
title: Implement extension resolver service (search + episode + stream resolution)
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 18:32'
|
||||
updated_date: '2026-02-13 18:34'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-30.1
|
||||
parent_task_id: TASK-30
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Build a dedicated service in main process that queries configured extension repos and normalizes results into a unified internal model, including optional playback metadata. Keep transport abstracted so future backends (local process, remote API, Manatán-compatible source) can be swapped without changing renderer contracts.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Create a typed internal model for source, series, episode, and playable candidate with fields for quality/audio/headers/referrer/userAgent.
|
||||
- [ ] #2 Implement provider abstraction with pluggable fetch/execution strategy from config.
|
||||
- [ ] #3 Add services for searchAnime, listEpisodes, resolveStream (or equivalent) with cancellation/error boundaries.
|
||||
- [ ] #4 Normalize all provider responses into deterministic field names and stable IDs.
|
||||
- [ ] #5 Include resilient handling for empty/no-result/no-URL cases and network faults with explicit error categories.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Phase 2 — Core service: provider integration and stream resolution
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 Resolver never leaks raw provider payload to renderer.
|
||||
- [ ] #2 Streaming URL output includes reason for failure when unavailable.
|
||||
- [ ] #3 Service boundaries allow unit-level validation of request/response mapping logic.
|
||||
- [ ] #4 No blocking calls on Electron UI/main thread; all I/O is async and cancellable.
|
||||
<!-- DOD:END -->
|
||||
@@ -1,50 +0,0 @@
|
||||
---
|
||||
id: TASK-30.3
|
||||
title: Expose resolver operations via Electron IPC to renderer
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 18:32'
|
||||
updated_date: '2026-02-13 18:34'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-30.2
|
||||
parent_task_id: TASK-30
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Add a typed preload and main-IPC contract for streaming queries and playback resolution so the renderer can initiate search/list/resolve without embedding network/provider logic in UI code.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Define IPC handlers in main with input/output schema validation and timeouts.
|
||||
- [ ] #2 Expose corresponding functions in preload `window.electronAPI` and ElectronAPI types.
|
||||
- [ ] #3 Reuse existing mpv command channel for playback and add a dedicated request/response flow for resolver actions.
|
||||
- [ ] #4 Implement safe serialization and error marshalling for resolver-specific failures.
|
||||
- [ ] #5 Add runtime wiring and lifetime management in app startup/shutdown.
|
||||
- [ ] #6 Document event/callback behavior for loading/error states.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Phase 3 — API surface: IPC/preload contract for resolver operations
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 Renderer code can query providers without importing Node-only modules.
|
||||
- [ ] #2 IPC paths have clear names and consistent response shapes across all calls.
|
||||
- [ ] #3 Error paths return explicit machine-readable codes mapped to user-visible messages.
|
||||
<!-- DOD:END -->
|
||||
@@ -1,50 +0,0 @@
|
||||
---
|
||||
id: TASK-30.4
|
||||
title: 'Add interactive streaming modal (search, episode list, source selection, play)'
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 18:32'
|
||||
updated_date: '2026-02-13 18:34'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-30.3
|
||||
parent_task_id: TASK-30
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Implement a renderer flow to query configured providers, display results, let user choose series and episode, and trigger playback for a selected stream. The UI should support keyboard interactions and surface backend errors clearly.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Create modal UI/state model for query, results list, selected item, episode list, candidate qualities, and loading/error status.
|
||||
- [ ] #2 Wire renderer actions to new IPC methods for search/episode/resolve.
|
||||
- [ ] #3 Render one-click or enter-to-play action that calls existing mpv playback pathway.
|
||||
- [ ] #4 Persist minimal user preference (last provider/quality where possible) for faster repeat use.
|
||||
- [ ] #5 Provide empty/error states and accessibility-friendly focus/keyboard navigation for lists.
|
||||
- [ ] #6 Add a no-network mode fallback message when resolver calls fail.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Phase 4 — UX: interactive modal flow and playback callout
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 Modal state is isolated and unsubscribes listeners on close.
|
||||
- [ ] #2 No direct network logic in renderer beyond IPC calls.
|
||||
- [ ] #3 Visual style and behavior are consistent with existing modal patterns.
|
||||
<!-- DOD:END -->
|
||||
@@ -1,53 +0,0 @@
|
||||
---
|
||||
id: TASK-30.5
|
||||
title: >-
|
||||
Wire mpv script-message/shortcut trigger into streaming modal and playback
|
||||
path
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 18:33'
|
||||
updated_date: '2026-02-13 18:34'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-30.3
|
||||
- TASK-30.4
|
||||
parent_task_id: TASK-30
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Allow users to open streaming selection from within mpv via keybind/menu and route that intent into renderer modal and playback flow without requiring separate window focus changes.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Add/extend mpv Lua plugin or existing command registry to emit a custom action for opening the streaming picker.
|
||||
- [ ] #2 Handle this action in main IPC/mpv-command pipeline and forward to renderer modal state.
|
||||
- [ ] #3 Add at least one default keybinding/menu entry documented in config/plugin notes.
|
||||
- [ ] #4 Ensure playback launched from the in-player flow uses the same command path and error messages as renderer-initiated flow.
|
||||
- [ ] #5 Add graceful handling when feature is disabled or no providers are configured.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Phase 5 — In-player entry: mpv trigger/menu integration
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 No duplicate mpv command parsing between picker and legacy commands.
|
||||
- [ ] #2 Feature can be used in overlay and mpv-only mode where applicable.
|
||||
- [ ] #3 No dependency on modal open state when launched by mpv trigger.
|
||||
- [ ] #4 Manual and keybind invocations behave consistently.
|
||||
<!-- DOD:END -->
|
||||
@@ -1,53 +0,0 @@
|
||||
---
|
||||
id: TASK-30.6
|
||||
title: >-
|
||||
Add integration validation plan and rollout checklist for anime streaming
|
||||
feature
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 18:34'
|
||||
updated_date: '2026-02-13 18:34'
|
||||
labels: []
|
||||
dependencies: []
|
||||
parent_task_id: TASK-30
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Create a concrete validation task that defines end-to-end acceptance checks for config loading, resolver behavior, IPC contract correctness, UI flow, and mpv-triggered launch. The checklist should be actionable and align with existing project conventions so completion can be verified without guesswork.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Define test scenarios for config success/failure cases, including invalid provider config and feature disabled mode.
|
||||
- [ ] #2 Define search/list/resolve API contract tests and error-code assertions (empty, timeout, auth error, no playable URL).
|
||||
- [ ] #3 Define renderer UX checks for modal state transitions, loading indicators, empty results, selection, and play invocation.
|
||||
- [ ] #4 Define in-mpv trigger checks for command/message pathway and fallback behavior when modal disabled/unavailable.
|
||||
- [ ] #5 Define manual smoke steps for end-to-end play from query to mpv playback using at least one configured source.
|
||||
- [ ] #6 Document expected logs/telemetry markers for troubleshooting and rollback.
|
||||
- [ ] #7 Define rollback criteria and what constitutes safe partial completion.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Phase 6 — Validation: rollout, smoke tests, and release readiness checklist
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 Checklist covers happy-path and failure-path for each task dependency.
|
||||
- [ ] #2 Verification steps are executable without external tooling assumptions.
|
||||
- [ ] #3 No task can be marked done without explicit evidence fields filled in.
|
||||
- [ ] #4 Rollout and fallback behavior are documented per deployment/release phase.
|
||||
<!-- DOD:END -->
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
id: TASK-30.7
|
||||
title: >-
|
||||
Add English-source preference + hard-sub stripping workflow in Aniyomi
|
||||
streaming path
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 18:41'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-30.2
|
||||
- TASK-30.5
|
||||
parent_task_id: TASK-30
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Improve the Aniyomi/anime extension streaming flow to prefer English-capable sources with soft subtitles, and automatically recover when only hard-subbed streams are available by stripping embedded subtitles with ffmpeg and attaching external Jimaku subtitle files into mpv.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 During source scoring/selection, prefer providers/sources that declare or expose soft subtitles for English audio or subtitle tracks over hard-subbed alternatives.
|
||||
- [ ] #2 Add a config option for preferred language targets (default English) and fallback policy (favor soft subtitles, then hard-sub fallback).
|
||||
- [ ] #3 Detect when a resolved stream is hard-sub-only and a soft-sub source is unavailable for the same episode.
|
||||
- [ ] #4 When hard subs are used, attempt to generate a subtitle-less playback stream path using ffmpeg and feed an external subtitle file (including Jimaku-provided `.ass/.srt`) into the mpv playback path.
|
||||
- [ ] #5 Preserve stream selection metadata (language tags, subtitle type, availability state) for UI decisions and error messaging.
|
||||
- [ ] #6 If ffmpeg conversion is not possible/disabled or fails, surface a clear status that explains fallback behavior instead of silent failure.
|
||||
- [ ] #7 Integrate subtitle source preferences with Jimaku so user-fetched or resolved subtitles are preferred for burn-in removal cases where supported.
|
||||
- [ ] #8 Add handling for unsupported codecs/containers and provider limitations so direct passthrough is still used when hard-sub stripping is unsafe.
|
||||
- [ ] #9 Document new behavior in feature docs/FAQ: how sources are ranked, what hard-sub stripping does, and known compatibility limitations.
|
||||
<!-- AC:END -->
|
||||
@@ -1,35 +0,0 @@
|
||||
---
|
||||
id: TASK-30.8
|
||||
title: >-
|
||||
Add observability and tuning metrics for Aniyomi subtitle-source fallback
|
||||
decisions
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 18:41'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-30.7
|
||||
parent_task_id: TASK-30
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Add lightweight telemetry/analytics hooks (local logs + optional structured counters) to measure how Aniyomi/anime streaming source selection behaves, including soft-sub preference, hard-sub fallback usage, and ffmpeg+Jimaku post-processing outcomes, to support source ranking tuning.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Track per-playback decision metadata including chosen source, language match score, subtitle mode (soft/hard), and reason for source preference ordering.
|
||||
- [ ] #2 Emit success/failure counters for hard-sub stripping attempts (started/succeeded/failed/unsupported codec) with reason codes.
|
||||
- [ ] #3 Log whether Jimaku subtitle attachment was available and successfully loaded for ffmpeg-assisted flows.
|
||||
- [ ] #4 Capture user-visible fallback reasons when preferred English/soft-sub sources are absent and hard-sub path is used.
|
||||
- [ ] #5 Add a debug/report view or log artifact with counters that can be reviewed in-app or via config/log files.
|
||||
- [ ] #6 Document metrics definitions so developers can tune source scorer and fallback policy without code changes.
|
||||
- [ ] #7 Ensure instrumentation has low overhead and is opt-out-safe with existing config flags.
|
||||
<!-- AC:END -->
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
id: TASK-30.9
|
||||
title: Expose subtitle preference and ffmpeg fallback tuning controls in settings UI
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 18:42'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-30.7
|
||||
- TASK-30.8
|
||||
parent_task_id: TASK-30
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Add user-configurable controls for Aniyomi streaming subtitle behavior, including preferred language profile, soft-vs-hard source preference, ffmpeg-assisted hard-sub removal behavior, and policy toggles so quality and fallback behavior can be tuned without code changes.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Add settings UI fields to define preferred subtitle/audiotrack language order (e.g., en, ja) and enable/disable hard-sub fallback mode.
|
||||
- [ ] #2 Add explicit toggle for enabling hard-sub stripping via ffmpeg and configurable timeout/quality limits to avoid long waits.
|
||||
- [ ] #3 Expose source ranking preferences for soft-sub vs hard-sub sources and optional fallback to native/transcoded source when preferred modes are unavailable.
|
||||
- [ ] #4 Persist settings in existing config schema with migration-safe defaults and include clear validation for invalid/unsupported values.
|
||||
- [ ] #5 Show current effective policy in streaming UI (for debugging): source selected + reason + whether subtitles are soft/hard and if ffmpeg path is active.
|
||||
- [ ] #6 Add user-facing explanatory text and warnings for ffmpeg dependency, expected CPU/cpu cost, and compatibility limits.
|
||||
- [ ] #7 Log and display when user-adjusted policy changes alter a previously preferred source choice during runtime.
|
||||
<!-- AC:END -->
|
||||
@@ -1,51 +0,0 @@
|
||||
---
|
||||
id: TASK-34
|
||||
title: >-
|
||||
Add in-app episode browser (Ctrl+E) for local files and Jellyfin-ready
|
||||
metadata
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-13 22:12'
|
||||
updated_date: '2026-02-13 22:13'
|
||||
labels: []
|
||||
dependencies: []
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Implement an in-app episode browser invoked by Ctrl+E when mpv is open and connected to the Electron UI. The viewer should use the currently supplied directory (if app was launched with one) or fallback to the parent directory of the currently playing video. It should enumerate all available video files in target directory and sort them deterministically, then display them in a polished list/gallery with thumbnails in the Electron UI. Thumbnail behavior should prioritize existing matching images in the directory; otherwise generate thumbnails asynchronously in the background and update the modal as they become available. The same menu infrastructure should be shared with the planned Jellyfin integration and designed so it can display additional Jellyfin-sourced metadata without UI rewrites. It should also support launching/using an alternate picker mode compatible with external launcher UX patterns (e.g., via fzf/rofi), showing the same episode list/metadata in those contexts.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Pressing Ctrl+E in connected mode opens an episode viewer modal and closes/overlays correctly in the Electron app.
|
||||
- [ ] #2 Episode browser source directory is resolved from CLI-provided path when present, else from currently playing video's parent directory.
|
||||
- [ ] #3 Browser enumerates all supported video files in target directory and sorts them deterministically (e.g., natural/season-episode aware when possible).
|
||||
- [ ] #4 Episode list/gallery renders a consistent, usable layout with title, position/index, and thumbnail placeholders.
|
||||
- [ ] #5 If a thumbnail image file with matching name exists in the directory, it is used without background generation.
|
||||
- [ ] #6 For files without matching thumbnails, background thumbnail generation runs and updates entries as images become available in the modal.
|
||||
- [ ] #7 Thumbnail generation is cancellable/abortable and does not block UI interaction.
|
||||
- [ ] #8 The same episode viewer component/path can be reused by Jellyfin integration and can accept extended metadata payloads (e.g., show title, season/episode, runtime, description).
|
||||
- [ ] #9 `fzf`/`rofi`-compatible episode picker mode is available so the same episode set can be browsed outside the Electron modal using either backend output format.
|
||||
- [ ] #10 Episode item ordering, labels, and metadata shown in fzf/rofi mode match the in-app sorting and identity scheme.
|
||||
- [ ] #11 Both picker modes (Electron modal and fzf/rofi) resolve source directory using the same CLI/parent-of-current-video precedence rules.
|
||||
- [ ] #12 When invoked through fzf/rofi, the selected episode can be played and returns focus/flow safely to the Electron-mpv workflow.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 Feature supports Ctrl+E invocation from connected mpv session.
|
||||
- [ ] #2 Directory fallback behavior is implemented and validated with both passed-in and default paths.
|
||||
- [ ] #3 Video listing excludes unsupported formats and is correctly sorted.
|
||||
- [ ] #4 Existing local thumbnail matching works for common image/video naming patterns.
|
||||
- [ ] #5 Background thumbnail generation works asynchronously and updates UI in-place.
|
||||
- [ ] #6 UI is ready for Jellyfin metadata fields and does not require structural rewrite for server-provided data.
|
||||
- [ ] #7 No regressions in existing mpv-Electron controls for standard playback.
|
||||
<!-- DOD:END -->
|
||||
@@ -1,58 +0,0 @@
|
||||
---
|
||||
id: TASK-43
|
||||
title: Add community subtitle timing database for shared sync corrections
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-14 02:13'
|
||||
labels:
|
||||
- feature
|
||||
- community
|
||||
- subtitles
|
||||
- sync
|
||||
dependencies: []
|
||||
priority: low
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Allow users to share their subtitle timing corrections to a community database, so other users watching the same video file get pre-synced subtitles automatically.
|
||||
|
||||
## Motivation
|
||||
|
||||
Subtitle synchronization (alass/ffsubsync) is one of the most friction-heavy steps in the mining workflow. Users spend time syncing subtitles that someone else has already synced for the exact same video. A shared database of timing corrections keyed by video file hash would eliminate redundant work.
|
||||
|
||||
## Design
|
||||
|
||||
1. **Video identification**: Use a partial file hash (first + last N bytes, or a media fingerprint) to identify video files without uploading content
|
||||
2. **Timing data**: Store the timing offset/warp parameters produced by alass/ffsubsync, not the full subtitle file
|
||||
3. **Upload flow**: After a successful sync, offer to share the timing correction (opt-in)
|
||||
4. **Download flow**: Before syncing, check the community database for existing corrections for the current video hash
|
||||
5. **Trust model**: Simple upvote/downvote on corrections; show number of users who confirmed a correction works
|
||||
|
||||
## Technical considerations
|
||||
|
||||
- Backend could be a simple REST API with a lightweight database (or even a GitHub-hosted JSON/SQLite file for v1)
|
||||
- Privacy: only file hashes and timing parameters are shared, never video content or personal data
|
||||
- Subtitle source (jimaku entry ID) can serve as an additional matching key
|
||||
- Rate limiting and abuse prevention needed for public API
|
||||
- Could integrate with existing jimaku modal flow
|
||||
|
||||
## Phasing
|
||||
|
||||
- v1: Local export/import of timing corrections (share as files)
|
||||
- v2: Optional cloud sync with community database
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Video files are identified by content hash without uploading video data.
|
||||
- [ ] #2 Timing corrections (offset/warp parameters) can be exported and shared.
|
||||
- [ ] #3 Before syncing, the app checks for existing community corrections for the current video.
|
||||
- [ ] #4 Upload of timing data is opt-in with clear privacy disclosure.
|
||||
- [ ] #5 Downloaded corrections are applied automatically or with one-click confirmation.
|
||||
- [ ] #6 Trust signal (confirmation count) is shown for community corrections.
|
||||
<!-- AC:END -->
|
||||
@@ -1,53 +0,0 @@
|
||||
---
|
||||
id: TASK-48
|
||||
title: Add streaming mode integration in subminer using ani-cli stream source
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-14 06:01'
|
||||
updated_date: '2026-02-14 08:19'
|
||||
labels:
|
||||
- stream
|
||||
- ani-cli
|
||||
- jimaku
|
||||
dependencies: []
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Implement a new streaming mode so SubMiner can resolve and play episodes via ani-cli stream sources instead of existing file/download flow. The mode is enabled with a CLI flag (`-s` / `--stream`) and, when active, should prefer streamed playback and subtitle handling that keeps Japanese (or configured primary) subtitles as the main track. If a stream lacks Japanese subtitle tracks, fetch and inject subtitles from Jimaku and set them as primary subtitles according to SubMiner config.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Add a command-line option `-s`/`--stream` that enables streaming mode and is documented in help/config UX.
|
||||
- [ ] #2 When streaming mode is enabled, resolve episode/video URLs using existing ani-cli stream selection logic (ported into SubMiner) and route playback to the resolved stream source.
|
||||
- [ ] #3 If stream metadata contains a Japanese subtitle track, preserve that track as the primary subtitle stream path per current primary-subtitle selection behavior.
|
||||
- [ ] #4 If no Japanese subtitle track is present in the stream metadata, fetch matching subtitles from Jimaku and load them into playback.
|
||||
- [ ] #5 When loading Jimaku subtitles, replace/overwrite existing non-Japanese primary subtitle track behavior so the language configured as primary in SubMiner config is used instead.
|
||||
- [ ] #6 Non-streaming mode continues to follow current behavior when `-s`/`--stream` is not set.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Superseded by TASK-51. Streaming via ani-cli in subminer was removed due Cloudflare 403/unreliable source metadata; re-evaluate later if reintroduced behind a feature flag and redesigned resolver/metadata pipeline.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 CLI accepts both `-s` and `--stream` and enables streaming-specific behavior.
|
||||
- [ ] #2 Streaming mode resolves streams through migrated ani-cli logic.
|
||||
- [ ] #3 Japanese subs are preferred from stream metadata when available; Jimaku fallback is used only when absent.
|
||||
- [ ] #4 Primary subtitle language in config determines which language is treated as default stream subtitle track after fallback.
|
||||
- [ ] #5 Behavior is verified via test or manual checklist and documented in task notes.
|
||||
<!-- DOD:END -->
|
||||
@@ -1,39 +0,0 @@
|
||||
---
|
||||
id: TASK-48.1
|
||||
title: Add streaming CLI flag plumbing and option wiring
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-14 06:03'
|
||||
updated_date: '2026-02-14 08:19'
|
||||
labels:
|
||||
- stream
|
||||
- cli
|
||||
dependencies: []
|
||||
parent_task_id: TASK-48
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Add the `-s`/`--stream` option end-to-end in SubMiner CLI and configuration handling, including defaults, help text, parsing/validation, and explicit routing so streaming mode is only enabled when requested.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Introduce `-s` short option and `--stream` long option in CLI parsing without breaking existing flags.
|
||||
- [ ] #2 When set, the resulting config state reflects streaming mode enabled and is propagated to playback/session startup.
|
||||
- [ ] #3 When unset, behavior remains identical to current non-streaming flows.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Superseded by TASK-51. CLI stream mode work deferred until streaming architecture is revisited.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -1,39 +0,0 @@
|
||||
---
|
||||
id: TASK-48.2
|
||||
title: Port ani-cli stream-resolution logic into SubMiner
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-14 06:03'
|
||||
updated_date: '2026-02-14 08:19'
|
||||
labels:
|
||||
- stream
|
||||
- ani-cli
|
||||
dependencies: []
|
||||
parent_task_id: TASK-48
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Implement stream URL resolution by porting ani-cli logic for selecting providers/episodes and obtaining playable stream URLs so SubMiner can consume stream sources directly.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Encapsulate stream search/provider selection logic in a dedicated module in SubMiner.
|
||||
- [ ] #2 Resolve episode query input into a canonical playable stream URL in streaming mode.
|
||||
- [ ] #3 Preserve existing behavior for non-streaming flow and expose errors when stream resolution fails.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Superseded by TASK-51. Stream URL resolution via ani-cli postponed; previous attempt exposed anti-bot/403 fragility and poor title-source reliability.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -1,39 +0,0 @@
|
||||
---
|
||||
id: TASK-48.3
|
||||
title: Implement stream subtitle selection and primary-language precedence
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-14 06:03'
|
||||
updated_date: '2026-02-14 08:19'
|
||||
labels:
|
||||
- stream
|
||||
- subtitles
|
||||
dependencies: []
|
||||
parent_task_id: TASK-48
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Handle subtitle track selection for stream playback so Japanese (or configured primary language) subtitle behavior is correctly applied when stream metadata includes or omits JP tracks.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Use stream metadata to choose and mark the configured primary language subtitle as active when available.
|
||||
- [ ] #2 If no matching primary language track exists in stream metadata, keep previous fallback behavior only for non-streaming mode.
|
||||
- [ ] #3 When no Japanese track exists and config primary is different, explicitly set configured primary as primary track for streaming flow.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Superseded by TASK-51. Stream subtitle language precedence in streaming mode deferred with full design revisit.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -1,40 +0,0 @@
|
||||
---
|
||||
id: TASK-48.4
|
||||
title: Add Jimaku subtitle fallback for stream mode
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-14 06:03'
|
||||
updated_date: '2026-02-14 08:19'
|
||||
labels:
|
||||
- stream
|
||||
- jimaku
|
||||
- subtitles
|
||||
dependencies: []
|
||||
parent_task_id: TASK-48
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
When a resolved stream lacks JP/primary-language tracks, fetch subtitles from Jimaku and inject them for playback, overriding non-primary subtitle defaults in streaming mode according to config.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Detect missing primary subtitle from stream metadata and trigger Jimaku lookup for matching episode.
|
||||
- [ ] #2 Load fetched Jimaku subtitles into playback pipeline and mark them as the primary subtitle track.
|
||||
- [ ] #3 Fallback is only used in streaming mode and should not alter subtitle behavior outside streaming.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Superseded by TASK-51. Jimaku fallback for streams deferred along with entire streaming flow.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -1,39 +0,0 @@
|
||||
---
|
||||
id: TASK-48.5
|
||||
title: Add verification plan/tests for streaming mode behavior
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-14 06:03'
|
||||
updated_date: '2026-02-14 08:19'
|
||||
labels:
|
||||
- stream
|
||||
- qa
|
||||
dependencies: []
|
||||
parent_task_id: TASK-48
|
||||
priority: low
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Create a validation plan or tests for CLI flag behavior, stream resolution, and subtitle precedence/fallback rules so streaming mode changes are measurable and regressions are caught.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Document/manual checklist covers `-s` and `--stream` invocation and streaming-only behavior.
|
||||
- [ ] #2 Include cases for (a) stream with JP subtitles, (b) no JP subtitles with Jimaku fallback, (c) primary-language not Japanese.
|
||||
- [ ] #3 Run or provide reproducible checks to confirm non-streaming behavior unchanged.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Superseded by TASK-51. Verification plan moved to deferred reimplementation context.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -1,57 +0,0 @@
|
||||
---
|
||||
id: TASK-48.6
|
||||
title: Wire -s/--stream mode to Ani-cli and Jimaku subtitle fallback
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-14 06:06'
|
||||
updated_date: '2026-02-14 08:19'
|
||||
labels: []
|
||||
dependencies: []
|
||||
references:
|
||||
- ani-cli/ani-cli
|
||||
- src/jimaku/utils.ts
|
||||
- src/core/services/anki-jimaku-service.ts
|
||||
documentation:
|
||||
- ani-cli/README.md
|
||||
- subminer
|
||||
- src/cli/help.ts
|
||||
parent_task_id: TASK-48
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Implement SubMiner streaming mode end-to-end behind a `-s`/`--stream` flag. In stream mode, use the vendored ani-cli resolution flow to get playable stream URLs instead of local file/YouTube URL handling. If resolved streams do not expose Japanese subtitles, fetch matching subtitles from Jimaku and load them into mpv as the active primary subtitle track, overwriting the current non-primary/non-Japanese default according to subminer primary-subtitle configuration.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 When `subminer -s` is used, resolution should pass a search/query through ani-cli stream logic and play the resolved stream source.
|
||||
- [ ] #2 If the stream includes a Japanese subtitle track, preserve and select the configured primary subtitle language behavior without Jimaku injection.
|
||||
- [ ] #3 If no Japanese (or configured primary language) subtitle exists in stream metadata, fetch and inject Jimaku subtitles before playback starts.
|
||||
- [ ] #4 Loaded Jimaku subtitles should become the selected primary subtitle track, replacing any existing default non-primary subtitle in that context.
|
||||
- [ ] #5 When `-s` is not passed, non-streaming behavior remains unchanged.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Superseded by TASK-51. End-to-end stream wiring to ani-cli is deferred.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 CLI exposes both `-s` and `--stream` in help/config and validation.
|
||||
- [ ] #2 Implementation includes a clear fallback path when stream subtitles are absent and Jimaku search/download fails gracefully.
|
||||
- [ ] #3 Subtitles loading path avoids temp-file leaks; temporary media/subtitle artifacts are cleaned up on exit.
|
||||
- [ ] #4 At least one verification step (manual or test) confirms stream mode path works for an episode with and without Japanese stream subtitles.
|
||||
<!-- DOD:END -->
|
||||
@@ -1,33 +0,0 @@
|
||||
---
|
||||
id: TASK-51
|
||||
title: Revisit Ani-cli stream mode integration later
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-02-14 08:19'
|
||||
labels:
|
||||
- someday
|
||||
- streaming
|
||||
- ani-cli
|
||||
- feature-flag
|
||||
dependencies: []
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Current codebase has removed ani-cli integration and stream-mode from subminer temporarily. Keep a deferred design task to reintroduce streaming mode in a future cycle.
|
||||
|
||||
Findings from prior attempts:
|
||||
|
||||
- `subminer -s <query>` path relied on `ani-cli` resolving stream URLs, but returned stream URLs that are Cloudflare-protected (`tools.fast4speed.rsvp`) and often returned 403 from mpv/ytdl-hook (generic anti-bot/Forbidden).
|
||||
- Even after passing `ytdl` extractor args, stream playback via subminer still failed because URL/anti-bot handling differed from direct ani-cli execution context.
|
||||
- We also observed stream title resolution issues: selected titles from ani-cli menu were unreliable/random and broke downstream Jimaku matching behavior.
|
||||
- ffsubsync failures were difficult to debug initially due to OSD-only error visibility; logging was added to mpv log path.
|
||||
- Based on these findings and instability, stream mode should be explicitly deferred rather than partially reintroduced.
|
||||
|
||||
Proposal:
|
||||
|
||||
- Reintroduce behind a feature flag / future milestone only.
|
||||
- Re-design around a dedicated stream source resolver with robust URL acquisition and source metadata preservation (query/episode/title) before subtitle sync flows.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
@@ -1,62 +0,0 @@
|
||||
---
|
||||
id: TASK-10
|
||||
title: Consolidate service naming conventions and barrel exports
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-11 08:21'
|
||||
updated_date: '2026-02-15 07:00'
|
||||
labels:
|
||||
- refactor
|
||||
- services
|
||||
- naming
|
||||
milestone: Codebase Clarity & Composability
|
||||
dependencies: []
|
||||
references:
|
||||
- src/core/services/index.ts
|
||||
priority: low
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
The service layer has inconsistent naming:
|
||||
|
||||
- Some functions end in `Service`: `handleCliCommandService`, `loadSubtitlePositionService`
|
||||
- Some end in `RuntimeService`: `replayCurrentSubtitleRuntimeService`, `sendMpvCommandRuntimeService`
|
||||
- Some are plain: `shortcutMatchesInputForLocalFallback`
|
||||
- Factory functions mix `create*DepsRuntimeService` with `create*Service`
|
||||
|
||||
The barrel export (src/core/services/index.ts) re-exports 79 symbols from 28 files through a single surface, which obscures dependency boundaries. Consumers import everything from `./core/services` and can't tell which service file they actually depend on.
|
||||
|
||||
Establish consistent naming:
|
||||
|
||||
- Exported service functions: `verbNounService` (e.g., `handleCliCommand`)
|
||||
- Deps factory functions: `create*Deps`
|
||||
- Consider whether the barrel re-export is still the right pattern vs direct imports from individual files.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 All service functions follow a consistent naming convention
|
||||
- [ ] #2 Decision documented on barrel export vs direct imports
|
||||
- [ ] #3 No functional changes
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Naming convention consolidation should be addressed as part of TASK-27.2 (split main.ts) and TASK-27.3 (anki-integration split). As modules are extracted and given clear boundaries, naming will be standardized at each boundary. No need to do a standalone naming pass — it's wasted effort if the module structure is about to change.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Subsumed by TASK-27.2 and TASK-27.3. Naming conventions were standardized at module boundaries during extraction. A standalone global naming pass would be churn with no structural benefit now that modules have clear ownership boundaries.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,63 +0,0 @@
|
||||
---
|
||||
id: TASK-11
|
||||
title: Break up the applyInvisibleSubtitleLayoutFromMpvMetrics mega function
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-11 08:21'
|
||||
updated_date: '2026-02-16 01:34'
|
||||
labels:
|
||||
- refactor
|
||||
- renderer
|
||||
- complexity
|
||||
milestone: Codebase Clarity & Composability
|
||||
dependencies:
|
||||
- TASK-27.5
|
||||
references:
|
||||
- src/renderer/renderer.ts
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
In renderer.ts (around lines 865-1075), `applyInvisibleSubtitleLayoutFromMpvMetrics` is a 211-line function with up to 5 levels of nesting. It handles OSD scaling calculations, platform-specific font compensation (macOS vs Linux), DPR calculations, ASS alignment tag interpretation (\an tags), baseline compensation, line-height fixes, font property application, and transform origin — all interleaved.
|
||||
|
||||
Extract into focused helpers:
|
||||
|
||||
- `calculateOsdScale(metrics, renderAreaHeight)` — pure scaling math
|
||||
- `calculateSubtitlePosition(metrics, scale, alignment)` — ASS \an tag interpretation + positioning
|
||||
- `applyPlatformFontCompensation(style, platform)` — macOS kerning/size adjustments
|
||||
- `applySubtitleStyle(element, computedStyle)` — DOM style application
|
||||
|
||||
This can be done independently of or as part of TASK-6 (renderer split).
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 No single function exceeds ~50 lines in the positioning logic
|
||||
- [x] #2 Helper functions are pure where possible (take inputs, return outputs)
|
||||
- [x] #3 Platform-specific branches isolated into dedicated helpers
|
||||
- [x] #4 Invisible overlay positioning still works correctly on Linux and macOS
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Helpers were split so positioning math, base layout, and typography/vertical handling are no longer in one monolith; see `src/renderer/positioning/invisible-layout.ts` and peer files.
|
||||
|
||||
Applied as part of TASK-27.5 with helper extraction: moved mpv subtitle layout orchestration to `invisible-layout.ts` and extracted metric/base/style helpers into `invisible-layout-metrics.ts` and `invisible-layout-helpers.ts`.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Decomposition of `applyInvisibleSubtitleLayoutFromMpvMetrics` completed as part of TASK-27.5: function body split into metric/layout/typography helpers and small coordinator preserved. Manual validation completed by user; behavior remains stable.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,65 +0,0 @@
|
||||
---
|
||||
id: TASK-12
|
||||
title: Add renderer module bundling for multi-file renderer support
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-11 08:21'
|
||||
updated_date: '2026-02-16 02:14'
|
||||
labels:
|
||||
- infrastructure
|
||||
- renderer
|
||||
- build
|
||||
milestone: Codebase Clarity & Composability
|
||||
dependencies:
|
||||
- TASK-5
|
||||
references:
|
||||
- src/renderer/renderer.ts
|
||||
- src/renderer/index.html
|
||||
- package.json
|
||||
- tsconfig.json
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Currently renderer.ts is a single file loaded directly by Electron's renderer process via a script tag in index.html. To split it into modules (TASK-6), we need a bundling step since Electron renderer's default context doesn't support bare ES module imports without additional configuration.
|
||||
|
||||
Options:
|
||||
|
||||
1. **esbuild** — fast, minimal config, already used in many Electron projects
|
||||
2. **Electron's native ESM support** — requires `"type": "module"` and sandbox configuration
|
||||
3. **TypeScript compiler output** — if targeting a single concatenated bundle
|
||||
|
||||
The build pipeline already compiles TypeScript and copies renderer assets. Adding a bundling step for the renderer would slot into the existing `npm run build` script.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Renderer code can be split across multiple .ts files with imports
|
||||
- [x] #2 Build pipeline bundles renderer modules into a single output for Electron
|
||||
- [x] #3 Existing `make build` still works end-to-end
|
||||
- [x] #4 No runtime errors in renderer process
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Updated root npm build pipeline to use an explicit renderer bundle step via esbuild. Added `build:renderer` script to emit a single `dist/renderer/renderer.js` from `src/renderer/renderer.ts`; `build` now runs `pnpm run build:renderer` and preserves existing index/style copy and macOS helper step. Added `esbuild` to devDependencies.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Implemented renderer bundling step and wired `build` to use it. This adds `pnpm run build:renderer` which bundles `src/renderer/renderer.ts` into a single `dist/renderer/renderer.js` for Electron to load. Also added `esbuild` as a dev dependency and aligned `pnpm-lock.yaml` importer metadata for dependency consistency. Kept `index.html`/`style.css` copy path unchanged, so renderer asset layout remains stable.
|
||||
|
||||
Implemented additional test-layer type fix after build breakage by correcting `makeDepsFromMecabTokenizer` and related `tokenizeWithMecab` mocks to match expected `Token` vs `MergedToken` shapes, keeping runtime behavior unchanged while satisfying TS checks.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,52 +0,0 @@
|
||||
---
|
||||
id: TASK-20.1
|
||||
title: Refactor overlay runtime to use per-layer window bounds ownership
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-12 08:47'
|
||||
updated_date: '2026-02-13 08:04'
|
||||
labels: []
|
||||
dependencies: []
|
||||
parent_task_id: TASK-20
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Refactor overlay runtime so each overlay layer owns and applies its bounds independently. Keep tracker geometry as shared origin input only.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 `updateOverlayBoundsService` no longer applies the same bounds to every overlay window by default.
|
||||
- [x] #2 Main runtime/manager exposes per-layer bounds update paths for visible and invisible overlays.
|
||||
- [x] #3 Window tracker updates feed shared origin data; each layer applies its own computed bounds.
|
||||
- [x] #4 Single-layer behavior (visible-only or invisible-only) remains unchanged from user perspective.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Started implementation for per-layer overlay bounds ownership refactor.
|
||||
|
||||
Implemented per-layer bounds ownership path: visible and invisible layers now update bounds independently through overlay manager/runtime plumbing, while preserving existing geometry source behavior.
|
||||
|
||||
Replaced shared all-window bounds application with per-window bound application service and layer-specific runtime calls from visibility/tracker flows.
|
||||
|
||||
Archiving requested by user.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Refactored overlay bounds ownership to per-layer update paths. Tracker geometry remains shared input, but visible/invisible windows apply bounds independently via explicit layer routes. Existing single-layer UX behavior is preserved.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,110 +0,0 @@
|
||||
---
|
||||
id: TASK-27
|
||||
title: >-
|
||||
Refactor project structure to reduce architectural complexity and split
|
||||
oversized modules
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-13 17:13'
|
||||
updated_date: '2026-02-16 01:34'
|
||||
labels:
|
||||
- 'owner:architect'
|
||||
- 'owner:backend'
|
||||
- 'owner:frontend'
|
||||
dependencies: []
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Create a phased backlog-backed restructuring plan that keeps current service-oriented architecture while reducing cognitive load from oversized modules and tightening module ownership boundaries.
|
||||
|
||||
This initiative should make future feature work easier by splitting high-complexity files, reducing tightly-coupled orchestration, and introducing measurable structural guardrails.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 A phased decomposition plan is defined in task links and references the following target files: src/main.ts, src/anki-integration.ts, src/core/services/mpv-service.ts, src/renderer/_, src/config/_, and src/core/services/\*.
|
||||
- [ ] #2 Tasks are assigned with clear owners and include explicit dependencies so execution can proceed in parallel where safe.
|
||||
- [ ] #3 Changes are constrained to structural refactors first (no behavior changes until foundational splits are in place).
|
||||
- [ ] #4 Each subtask includes test/verification expectations (manual or automated) and a rollback-safe checkpoint.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
|
||||
## Revised Execution Sequence
|
||||
|
||||
### Phase 0 — Prerequisites (outside TASK-27 tree)
|
||||
|
||||
- **TASK-7** — Extract main.ts global state into AppState container (required before TASK-27.2)
|
||||
- **TASK-9** — Remove trivial wrapper functions from main.ts (depends on TASK-7; recommended before TASK-27.2 but not blocking)
|
||||
|
||||
### Phase 1 — Lightweight Inventory
|
||||
|
||||
- **TASK-27.1** — Inventory files >400 LOC, document contracts, define smoke test checklist
|
||||
|
||||
### Phase 2 — Sequential Split Wave
|
||||
|
||||
Order matters to avoid merge conflicts:
|
||||
|
||||
1. **TASK-27.3** — anki-integration.ts split (self-contained, doesn't affect main.ts wiring until facade is stable)
|
||||
2. **TASK-27.2** — main.ts split (after TASK-7 provides AppState container and 27.3 stabilizes the Anki facade)
|
||||
3. **TASK-27.4** — mpv-service.ts split (absorbs TASK-8 scope; blocked until 27.1 is done)
|
||||
4. **TASK-27.5** — renderer positioning.ts split (downscoped; after 27.2 to avoid import-path conflicts)
|
||||
|
||||
### Phase 3 — Stabilization
|
||||
|
||||
- **TASK-27.7** — Finalization and validation cleanup
|
||||
|
||||
## Smoke Test Checklist (applies to all subtasks)
|
||||
|
||||
Every subtask must verify before merging:
|
||||
|
||||
- [ ] App starts and connects to MPV
|
||||
- [ ] Subtitle text appears in overlay
|
||||
- [ ] Card mining creates a note in Anki
|
||||
- [ ] Field grouping modal opens and resolves
|
||||
- [ ] Global shortcuts work (mine, toggle overlay, copy subtitle)
|
||||
- [ ] Secondary subtitle display works
|
||||
- [ ] TypeScript compiles with no new errors
|
||||
- [ ] All existing tests pass (`pnpm test:core && pnpm test:config`)
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
## Review Findings (2026-02-13)
|
||||
|
||||
### Key changes from original plan:
|
||||
|
||||
1. **Dropped parallel execution of Phase 2** — TASK-27.2 and 27.5 share import paths; 27.2 and 27.3 share main.ts wiring. Sequential order prevents merge conflicts.
|
||||
2. **Added TASK-7 as external prerequisite** — main.ts has 30+ module-level `let` declarations. Splitting files without a state container first just scatters mutable state.
|
||||
3. **TASK-8 absorbed into TASK-27.4** — TASK-8 (separate protocol from app logic) and TASK-27.4 (physical file split) overlap significantly. TASK-27.4 now covers both.
|
||||
4. **TASK-27.5 downscoped** — Renderer is already well-organized (241-line orchestrator, handlers/, modals/, utils/ directories). Only positioning.ts (513 LOC) needs splitting.
|
||||
5. **Simplified ownership model** — Removed multi-owner ceremony since this is effectively a solo project. Kept labels for categorical tracking only.
|
||||
6. **Added global smoke test checklist** — No end-to-end or renderer tests exist, so manual verification is the safety net for every subtask.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
TASK-27 completed: plan execution sequence completed through all major refactor subtasks. Done status now confirmed for 27.1 (ownership mapping), 27.2 (main.ts split), 27.3 (anki-integration service-domain extraction), 27.4 (mpv-service split), 27.5 (renderer positioning split), and 27.7 (final validation summary, build + tests). Remaining work is now outside TASK-27 scope.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
|
||||
## Definition of Done
|
||||
|
||||
<!-- DOD:BEGIN -->
|
||||
|
||||
- [ ] #1 Plan task links and ordering are recorded in backlog descriptions.
|
||||
- [ ] #2 At least 2 independent owners are assigned with explicit labels in subtasks.
|
||||
<!-- DOD:END -->
|
||||
@@ -1,62 +0,0 @@
|
||||
---
|
||||
id: TASK-27.7
|
||||
title: >-
|
||||
Decompose anki-integration.ts core into domain modules (field-grouping,
|
||||
card-creation, polling)
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-15 07:00'
|
||||
updated_date: '2026-02-16 01:31'
|
||||
labels:
|
||||
- refactor
|
||||
- anki
|
||||
- architecture
|
||||
dependencies:
|
||||
- TASK-27.3
|
||||
references:
|
||||
- src/anki-integration.ts
|
||||
- src/anki-integration-duplicate.ts
|
||||
- src/anki-integration-ui-feedback.ts
|
||||
- src/anki-integration/ai.ts
|
||||
parent_task_id: TASK-27
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
TASK-27.3 extracted leaf clusters (duplicate-detection 102 LOC, ai-translation 158 LOC, ui-feedback 107 LOC) but the core class remains at 2935 LOC. The heavy decomposition from the original TASK-27.3 plan was never executed.
|
||||
|
||||
Remaining extractions from the original plan:
|
||||
|
||||
1. **field-grouping** (~900 LOC) — `triggerFieldGroupingForLastAddedCard`, `applyFieldGrouping`, `computeFieldGroupingMergedFields`, `buildFieldGroupingPreview`, `performFieldGroupingMerge`, `handleFieldGroupingAuto`, `handleFieldGroupingManual`, plus ~15 span/parse/normalize helpers
|
||||
2. **card-creation** (~350 LOC) — `createSentenceCard`, `setCardTypeFields`, `extractFields`, `processSentence`, field resolution helpers
|
||||
3. **polling/lifecycle** (~250 LOC) — `start`, `stop`, `poll`, `pollOnce`, `processNewCard`
|
||||
|
||||
Also consolidate the scattered extraction files into `src/anki-integration/`:
|
||||
|
||||
- `src/anki-integration-duplicate.ts` → `src/anki-integration/duplicate.ts`
|
||||
- `src/anki-integration-ui-feedback.ts` → `src/anki-integration/ui-feedback.ts`
|
||||
- `src/anki-integration/ai.ts` (already there)
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 anki-integration.ts reduced below 800 LOC (facade + private state wiring only)
|
||||
- [ ] #2 Field-grouping cluster (~900 LOC) extracted as its own module under src/anki-integration/
|
||||
- [ ] #3 Card-creation and polling/lifecycle extracted as separate modules
|
||||
- [ ] #4 All extracted modules consolidated under src/anki-integration/ directory
|
||||
- [ ] #5 Existing facade API preserved — external callers unchanged
|
||||
- [ ] #6 All existing tests pass; build compiles cleanly
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Implemented and stabilized the anki-integration refactor + transport/protocol fixes needed to keep 27.7 moving: fixed MPV protocol sub-end timing behavior, corrected split-buffer test fixtures, added injectable mpv transport socket factory to eliminate readonly Socket monkey-patching, and resolved TypeScript strictness issues in card-creation path (typed notesInfo cast, option signature/field guards/audio stream index). Updated related tests and build outputs accordingly. Validation results: `bun run build` passes and targeted suites pass: `src/core/services/mpv-protocol.test.ts`, `src/core/services/mpv-transport.test.ts`, `src/anki-integration.test.ts` (16/16).
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,75 +0,0 @@
|
||||
---
|
||||
id: TASK-55
|
||||
title: Normalize service naming conventions across core/services
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-16 04:47'
|
||||
updated_date: '2026-02-17 09:12'
|
||||
labels: []
|
||||
dependencies: []
|
||||
references:
|
||||
- /home/sudacode/projects/japanese/SubMiner/src/core/services/index.ts
|
||||
priority: low
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
The core/services directory has inconsistent naming patterns that create confusion:
|
||||
|
||||
- Some files use `*Service.ts` suffix (e.g., `mpv-service.ts`, `tokenizer-service.ts`)
|
||||
- Others use `*RuntimeService.ts` or just descriptive names (e.g., `overlay-visibility-service.ts` exports functions with 'Service' in name)
|
||||
- Some functions in files have 'Service' suffix, others don't
|
||||
|
||||
This inconsistency makes it hard to predict file/function names and creates cognitive overhead.
|
||||
|
||||
Standardize on:
|
||||
|
||||
- File names: `kebab-case.ts` without 'service' suffix (e.g., `mpv.ts`, `tokenizer.ts`)
|
||||
- Function names: descriptive verbs without 'Service' suffix (e.g., `createMpvClient()`, `tokenizeSubtitle()`)
|
||||
- Barrel exports: clean, predictable names
|
||||
|
||||
Files needing audit (47 services):
|
||||
|
||||
- All files in src/core/services/ need review
|
||||
|
||||
Note: This is a large-scale refactor that should be done carefully to avoid breaking changes.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Establish naming convention rules (document in code or docs)
|
||||
- [x] #2 Audit all service files for naming inconsistencies
|
||||
- [x] #3 Rename files to follow convention (kebab-case, no 'service' suffix)
|
||||
- [x] #4 Rename exported functions to remove 'Service' suffix where present
|
||||
- [x] #5 Update all imports across the entire codebase
|
||||
- [x] #6 Update barrel exports
|
||||
- [x] #7 Run full test suite
|
||||
- [x] #8 Update any documentation referencing old names
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Starting implementation. Planning and executing a mechanical refactor: file names and exported symbols with Service suffix in `src/core/services`, then cascading import updates across `src/`.
|
||||
|
||||
Implemented naming convention refactor across `src/core/services`: removed `-service` from service file names, renamed Service-suffixed exported symbols to non-Service names, and updated barrel exports in `src/core/services/index.ts`.
|
||||
|
||||
Updated call sites across `src/main/**`, `src/core/services/**` tests, `scripts/**`, `package.json` test paths, and docs references (`docs/development.md`, `docs/architecture.md`, `docs/structure-roadmap.md`).
|
||||
|
||||
Validation completed: `pnpm run build` and `pnpm run test:fast` both pass after refactor.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Normalized `src/core/services` naming by removing `-service` from module filenames, dropping `Service` suffixes from exported service functions, and updating `src/core/services/index.ts` barrel exports to the new names. Updated all import/call sites across `src/main/**`, service tests, scripts, and docs/package test paths to match the new module and symbol names. Verified no behavior regressions with `pnpm run build` and `pnpm run test:fast` (all passing).
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,63 +0,0 @@
|
||||
---
|
||||
id: TASK-56
|
||||
title: Extract remaining main.ts runtime functions to dedicated modules
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-16 04:47'
|
||||
updated_date: '2026-02-16 05:16'
|
||||
labels: []
|
||||
dependencies: []
|
||||
references:
|
||||
- /home/sudacode/projects/japanese/SubMiner/src/main.ts
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
main.ts is still 1481 lines after previous refactoring efforts. While significant progress has been made, there are still opportunities to extract runtime functions into dedicated modules to further reduce its size and improve maintainability.
|
||||
|
||||
Current opportunities:
|
||||
|
||||
1. **JLPT dictionary lookup functions** (lines 470-535) - initializeJlptDictionaryLookup, ensureJlptDictionaryLookup, getJlptDictionarySearchPaths
|
||||
2. **Media path utilities** (lines 552-590) - updateCurrentMediaPath, updateCurrentMediaTitle, resolveMediaPathForJimaku
|
||||
3. **Overlay visibility helpers** (lines 1273-1360) - updateVisibleOverlayVisibility, updateInvisibleOverlayVisibility, syncInvisibleOverlayMousePassthrough
|
||||
|
||||
These functions are largely self-contained and could be moved to:
|
||||
|
||||
- `src/main/jlpt-runtime.ts`
|
||||
- `src/main/media-runtime.ts`
|
||||
- `src/main/overlay-visibility-runtime.ts`
|
||||
|
||||
Goal: Reduce main.ts complexity by extracting focused runtime helpers into dedicated modules
|
||||
|
||||
Benefits:
|
||||
|
||||
- Faster navigation and comprehension of main.ts
|
||||
- Easier to test extracted modules independently
|
||||
- Clearer separation of concerns
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Extract JLPT dictionary lookup functions to dedicated module
|
||||
- [x] #2 Extract media path utilities to dedicated module
|
||||
- [x] #3 Extract overlay visibility helpers to dedicated module
|
||||
- [x] #4 Update main.ts imports to use new modules
|
||||
- [x] #5 Ensure all functionality remains intact
|
||||
- [x] #6 Run full test suite
|
||||
- [x] #7 Keep extracted code organized and easier to follow
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Refactor complete for targeted runtime extraction: JLPT lookup, media utilities, and overlay visibility helpers were moved into dedicated main-runtime modules and wired from main.ts. Existing behavior preserved and full typecheck + test suite passed.
|
||||
|
||||
Task intent updated to prioritize readability over strict line-count target.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,45 +0,0 @@
|
||||
---
|
||||
id: TASK-66
|
||||
title: Add AnkiConnect tagging for mined and updated cards
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-18 09:23'
|
||||
updated_date: '2026-02-18 09:24'
|
||||
labels:
|
||||
- anki
|
||||
- config
|
||||
- enhancement
|
||||
dependencies: []
|
||||
references:
|
||||
- src/anki-connect.ts
|
||||
- src/anki-integration.ts
|
||||
- src/anki-integration/card-creation.ts
|
||||
- src/config/definitions.ts
|
||||
- src/config/service.ts
|
||||
- src/config/config.test.ts
|
||||
- docs/configuration.md
|
||||
- config.example.jsonc
|
||||
- docs/public/config.example.jsonc
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Support configuring tags applied to cards created or updated through SubMiner's AnkiConnect workflow. Default behavior should add the `SubMiner` tag to all mined/updated cards, while allowing users to override or disable automatic tagging via config.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 AnkiConnect note creation supports passing tags.
|
||||
- [x] #2 Existing-card update flows add configured tags via AnkiConnect after updates.
|
||||
- [x] #3 Default config includes `ankiConnect.tags: ["SubMiner"]`.
|
||||
- [x] #4 Config parsing validates `ankiConnect.tags` and falls back safely on invalid values.
|
||||
- [x] #5 User-facing docs/config examples mention the new tags option and default behavior.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Implemented configurable AnkiConnect tagging across SubMiner card creation and update workflows. Added `ankiConnect.tags` config (default `['SubMiner']`), validation/fallback in config parsing, AnkiConnect client support for `addNote` tags + `addTags`, and integration hooks so created/updated/merged cards receive configured tags. Updated docs and regenerated config examples; config test suite passes.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,52 +0,0 @@
|
||||
---
|
||||
id: TASK-8
|
||||
title: >-
|
||||
Reduce MpvIpcClient deps interface and separate protocol from application
|
||||
logic
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-11 08:20'
|
||||
updated_date: '2026-02-15 07:00'
|
||||
labels:
|
||||
- refactor
|
||||
- mpv
|
||||
- architecture
|
||||
milestone: Codebase Clarity & Composability
|
||||
dependencies: []
|
||||
references:
|
||||
- src/core/services/mpv-service.ts
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
MpvIpcClient (761 lines) in src/core/services/mpv-service.ts has a 22-property `MpvIpcClientDeps` interface that reaches back into main.ts state for application-level concerns (overlay visibility, subtitle timing, media path updates, OSD display).
|
||||
|
||||
The class mixes two responsibilities:
|
||||
|
||||
1. **IPC Protocol**: Socket connection, JSON message framing, reconnection, property observation
|
||||
2. **Application Integration**: Subtitle text broadcasting, overlay visibility sync, timing tracking
|
||||
|
||||
Separating these would let the protocol layer be simpler and testable, while application-level reactions to mpv events could be handled by listeners/callbacks registered externally.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 MpvIpcClient deps interface reduced to protocol-level concerns only
|
||||
- [ ] #2 Application-level reactions (subtitle broadcast, overlay sync, timing) handled via event emitter or external listeners
|
||||
- [ ] #3 MpvIpcClient is testable without mocking 22 callbacks
|
||||
- [ ] #4 Existing behavior preserved
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Superseded by TASK-27.4 which absorbed this task's full scope (protocol/application separation, deps interface reduction from 22 properties to protocol-level concerns, event-based app reactions). All acceptance criteria met by TASK-27.4.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,68 +0,0 @@
|
||||
---
|
||||
id: TASK-9
|
||||
title: Remove trivial wrapper functions from main.ts
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-11 08:21'
|
||||
updated_date: '2026-02-15 07:00'
|
||||
labels:
|
||||
- refactor
|
||||
- main
|
||||
- simplicity
|
||||
milestone: Codebase Clarity & Composability
|
||||
dependencies:
|
||||
- TASK-7
|
||||
references:
|
||||
- src/main.ts
|
||||
priority: low
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
main.ts contains many trivial single-line wrapper functions that add indirection without value:
|
||||
|
||||
```typescript
|
||||
function getOverlayWindows(): BrowserWindow[] {
|
||||
return overlayManager.getOverlayWindows();
|
||||
}
|
||||
function updateOverlayBounds(geometry: WindowGeometry): void {
|
||||
updateOverlayBoundsService(geometry, () => getOverlayWindows());
|
||||
}
|
||||
function ensureOverlayWindowLevel(window: BrowserWindow): void {
|
||||
ensureOverlayWindowLevelService(window);
|
||||
}
|
||||
```
|
||||
|
||||
Similarly, config accessor wrappers like `getJimakuLanguagePreference()`, `getJimakuMaxEntryResults()`, `resolveJimakuApiKey()` are pure boilerplate.
|
||||
|
||||
After TASK-7 (AppState container), many of these can be eliminated by having services access the state container directly, or by using the service functions directly at call sites without local wrappers.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [ ] #1 Trivial pass-through wrappers eliminated (call service/manager directly)
|
||||
- [ ] #2 Config accessor wrappers replaced with direct calls or a config accessor helper
|
||||
- [ ] #3 main.ts line count reduced
|
||||
- [ ] #4 No functional changes
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Priority changed from medium to low: this work is largely subsumed by TASK-27.2 (split main.ts). When main.ts is decomposed into composition-root modules, trivial wrappers will naturally be eliminated or inlined. Recommend folding remaining wrapper cleanup into TASK-27.2 rather than tracking separately. Keep this ticket as a checklist reference but don't execute independently.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Subsumed by TASK-27.2 (main.ts split). Trivial wrappers were eliminated or inlined as composition-root modules were extracted. main.ts reduced from ~2000+ LOC to 1384 with state routed through appState container. Standalone wrapper removal pass no longer needed.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,16 +0,0 @@
|
||||
project_name: "SubMiner"
|
||||
default_status: "To Do"
|
||||
statuses: ["To Do", "In Progress", "Done"]
|
||||
labels: []
|
||||
milestones: []
|
||||
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: 'Release v0.1.0'
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Milestone: Release v0.1.0
|
||||
@@ -1,57 +0,0 @@
|
||||
---
|
||||
id: TASK-1
|
||||
title: Refactor runtime services per plan.md
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-10 18:46'
|
||||
updated_date: '2026-02-18 04:11'
|
||||
labels: []
|
||||
dependencies: []
|
||||
references:
|
||||
- plan.md
|
||||
ordinal: 2000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Execute the SubMiner refactoring initiative documented in plan.md to reduce thin abstractions, consolidate service boundaries, fix known quality issues, and increase test coverage while preserving current behavior.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Phase-based execution tasks are created and linked under this initiative.
|
||||
- [x] #2 Each phase task includes clear, testable outcomes aligned with plan.md.
|
||||
- [x] #3 Implementation proceeds with build/test verification checkpoints after each completed phase.
|
||||
- [x] #4 Main behavior remains stable for startup, overlay, IPC, CLI, and tokenizer flows throughout refactor.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Created initiative subtasks TASK-1.1 through TASK-1.6 with phase-aligned acceptance criteria and sequential dependencies.
|
||||
|
||||
Completed TASK-1.1 (Phase 1 thin-wrapper removal) with green build/core tests.
|
||||
|
||||
Completed TASK-1.2 (Phase 2 DI adapter consolidation) with successful build and core test verification checkpoint.
|
||||
|
||||
Completed TASK-1.5 (critical behavior tests) with expanded tokenizer/mpv/subsync/CLI coverage and green core test suite.
|
||||
|
||||
Completed TASK-1.6 with documented no-go decision for optional domain-directory reorganization (kept current structure; tests remain green).
|
||||
|
||||
TASK-1.4 remains the only open phase, blocked on interactive desktop smoke checks that cannot be fully validated in this headless environment.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Completed the plan.md refactor initiative across Phases 1-5 and optional Phase 6 decisioning: removed thin wrappers, consolidated DI adapters and related services, fixed targeted runtime correctness issues, expanded critical behavior test coverage, and kept build/core tests green throughout. Final runtime smoke checks (start/toggle/trigger-field-grouping/stop) passed in this headless environment, with known limitation that visual overlay rendering itself was not directly inspectable.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,60 +0,0 @@
|
||||
---
|
||||
id: TASK-1.1
|
||||
title: 'Phase 1: Remove thin wrapper runtime services'
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-02-10 18:46'
|
||||
updated_date: '2026-02-18 04:11'
|
||||
labels: []
|
||||
dependencies: []
|
||||
references:
|
||||
- plan.md
|
||||
- src/main.ts
|
||||
- src/core/services/index.ts
|
||||
parent_task_id: TASK-1
|
||||
ordinal: 12000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Inline trivial wrapper services into their call sites and delete redundant service/test files listed in Phase 1 of plan.md.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Wrapper logic from the Phase 1 file list is inlined at call sites without behavior changes.
|
||||
- [x] #2 Phase 1 wrapper service files and corresponding trivial tests are removed from the codebase.
|
||||
- [x] #3 `src/core/services/index.ts` exports are updated to remove deleted modules.
|
||||
- [x] #4 `pnpm run build && pnpm run test:core` passes after Phase 1 completion.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
|
||||
1. Locate all Phase 1 wrapper service call sites and classify direct-inline substitutions vs orchestration-flow inlines.
|
||||
2. Remove the lowest-risk wrappers first (`config-warning-runtime-service`, `app-logging-runtime-service`, `runtime-options-manager-runtime-service`, `overlay-modal-restore-service`, `overlay-send-service`) and update imports/exports.
|
||||
3. Continue with startup and shutdown wrappers (`startup-resource-runtime-service`, `config-generation-runtime-service`, `app-shutdown-runtime-service`, `shortcut-ui-deps-runtime-service`) by inlining behavior into `main.ts` or direct callers.
|
||||
4. Delete corresponding trivial test files for removed wrappers and clean `src/core/services/index.ts` exports.
|
||||
5. Run `pnpm run build && pnpm run test:core`; fix regressions and update task notes/acceptance criteria incrementally.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Inlined wrapper behaviors into direct call sites in `main.ts` and `overlay-bridge-runtime-service.ts` for config warning/app logging, runtime options manager construction, generate-config bootstrap path, startup resource initialization, app shutdown sequence, overlay modal restore handling, overlay send behavior, and overlay shortcut local fallback invocation.
|
||||
|
||||
Deleted 16 Phase 1 files (9 wrapper services + 7 wrapper tests) and removed corresponding barrel exports from `src/core/services/index.ts`.
|
||||
|
||||
Updated `package.json` `test:core` list to remove deleted test entries so the script tracks current sources accurately.
|
||||
|
||||
Verification: `pnpm run build && pnpm run test:core` passes after refactor.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -1,63 +0,0 @@
|
||||
---
|
||||
id: TASK-1.2
|
||||
title: 'Phase 2: Merge DI adapter runtime services into target services'
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-02-10 18:46'
|
||||
updated_date: '2026-02-18 04:11'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-1.1
|
||||
references:
|
||||
- plan.md
|
||||
- src/core/services/cli-command-service.ts
|
||||
- src/core/services/ipc-service.ts
|
||||
- src/core/services/tokenizer-service.ts
|
||||
- src/core/services/app-lifecycle-deps-runtime-service.ts
|
||||
parent_task_id: TASK-1
|
||||
ordinal: 10000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Absorb dependency adapter runtime services into core service modules and remove adapter files/tests while preserving runtime behavior.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 CLI, IPC, tokenizer, and app lifecycle adapter logic is merged into their target service modules.
|
||||
- [x] #2 Adapter service and adapter test files listed in Phase 2 are removed.
|
||||
- [x] #3 Callers pass dependency shapes expected by updated services without redundant mapping layers.
|
||||
- [x] #4 `pnpm run build && pnpm run test:core` passes after Phase 2 completion.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
|
||||
1. Audit `cli-command-deps-runtime-service.ts`, `ipc-deps-runtime-service.ts`, `tokenizer-deps-runtime-service.ts`, and `app-lifecycle-deps-runtime-service.ts` usage sites in `main.ts` and corresponding services.
|
||||
2. For each adapter, move null-guarding and shape-normalization logic into its target service (`cli-command-service.ts`, `ipc-service.ts`, `tokenizer-service.ts`, `app-lifecycle-service.ts`) and simplify caller dependency objects.
|
||||
3. Remove adapter service files/tests and update `src/core/services/index.ts` exports/import sites.
|
||||
4. Run `pnpm run build && pnpm run test:core` and fix any typing/regression issues from the interface consolidation.
|
||||
5. Update task notes with dependency-shape decisions to preserve handoff clarity.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Merged adapter-constructor logic into target services: `createCliCommandDepsRuntimeService` moved into `cli-command-service.ts`, `createIpcDepsRuntimeService` moved into `ipc-service.ts`, `createTokenizerDepsRuntimeService` moved into `tokenizer-service.ts`, and `createAppLifecycleDepsRuntimeService` moved into `app-lifecycle-service.ts`.
|
||||
|
||||
Deleted adapter service files and tests for cli-command deps, ipc deps, tokenizer deps, and app lifecycle deps.
|
||||
|
||||
Updated `src/core/services/index.ts` exports and `package.json` `test:core` entries to remove deleted adapter test modules.
|
||||
|
||||
Verification: `pnpm run build && pnpm run test:core` passes after consolidation.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -1,69 +0,0 @@
|
||||
---
|
||||
id: TASK-1.3
|
||||
title: 'Phase 3: Consolidate related service modules'
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-02-10 18:46'
|
||||
updated_date: '2026-02-18 04:11'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-1.2
|
||||
references:
|
||||
- plan.md
|
||||
- src/core/services/overlay-visibility-service.ts
|
||||
- src/core/services/overlay-manager-service.ts
|
||||
- src/core/services/overlay-shortcut-service.ts
|
||||
- src/core/services/numeric-shortcut-session-service.ts
|
||||
- src/core/services/app-ready-runtime-service.ts
|
||||
parent_task_id: TASK-1
|
||||
ordinal: 6000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Merge split modules for overlay visibility, broadcast, shortcuts, numeric shortcuts, and startup orchestration into cohesive service files.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Overlay visibility/runtime split is consolidated into a single service module.
|
||||
- [x] #2 Overlay broadcast functions are merged with overlay manager responsibilities.
|
||||
- [x] #3 Shortcut and numeric shortcut runtime/lifecycle splits are consolidated as described in plan.md.
|
||||
- [x] #4 Startup bootstrap and app-ready runtime orchestration is consolidated into one startup module.
|
||||
- [x] #5 `pnpm run build && pnpm run test:core` passes after Phase 3 completion.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
|
||||
1. Merge `overlay-visibility-runtime-service.ts` exports into `overlay-visibility-service.ts` and update imports/exports.
|
||||
2. Merge overlay broadcast responsibilities from `overlay-broadcast-runtime-service.ts` into `overlay-manager-service.ts` while preserving current APIs used by `main.ts`.
|
||||
3. Consolidate shortcut modules by absorbing lifecycle utilities into `overlay-shortcut-service.ts` and fallback-runner logic into `overlay-shortcut-runtime-service.ts` (or successor handler module), then remove obsolete files.
|
||||
4. Merge numeric shortcut runtime/session split into a single `numeric-shortcut-service.ts` and update call sites/tests.
|
||||
5. Merge startup bootstrap + app-ready orchestration into a single startup module, update imports, remove obsolete files, and run `pnpm run build && pnpm run test:core`.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Merged overlay visibility runtime API into `overlay-visibility-service.ts` and removed `overlay-visibility-runtime-service.ts`.
|
||||
|
||||
Merged overlay broadcast behavior into `overlay-manager-service.ts` (including manager-level broadcasting) and removed `overlay-broadcast-runtime-service.ts` + test, with equivalent coverage moved into `overlay-manager-service.test.ts`.
|
||||
|
||||
Consolidated shortcut modules into `overlay-shortcut-service.ts` (lifecycle) and new `overlay-shortcut-handler.ts` (runtime handlers + local fallback), removing `overlay-shortcut-lifecycle-service.ts`, `overlay-shortcut-runtime-service.ts`, and `overlay-shortcut-fallback-runner.ts`.
|
||||
|
||||
Merged numeric shortcut runtime/session split into `numeric-shortcut-service.ts`; removed `numeric-shortcut-runtime-service.ts` and merged runtime test coverage into session tests.
|
||||
|
||||
Merged startup bootstrap + app-ready orchestration into `startup-service.ts`; removed `startup-bootstrap-runtime-service.ts` and `app-ready-runtime-service.ts` with tests updated to new module path.
|
||||
|
||||
Verification: `pnpm run build && pnpm run test:core` passes after consolidation.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -1,98 +0,0 @@
|
||||
---
|
||||
id: TASK-1.4
|
||||
title: 'Phase 4: Fix runtime bugs and naming/code-quality issues'
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-02-10 18:46'
|
||||
updated_date: '2026-02-18 04:11'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-1.3
|
||||
references:
|
||||
- plan.md
|
||||
- src/main.ts
|
||||
- src/core/services/overlay-visibility-service.ts
|
||||
- src/core/services/tokenizer-deps-runtime-service.ts
|
||||
parent_task_id: TASK-1
|
||||
ordinal: 3000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Address identified correctness and code-quality issues from plan.md, including race conditions, unsafe typing, callback rejection handling, and runtime naming cleanup.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Debug `console.log`/`console.warn` usage in overlay visibility logic is removed or replaced with structured logging where needed.
|
||||
- [x] #2 Tokenizer type mismatch is fixed without unsafe `as never` casting.
|
||||
- [x] #3 Field grouping resolver handling is made concurrency-safe against overlapping requests.
|
||||
- [x] #4 Async callback wiring in CLI/IPC paths has explicit rejection handling.
|
||||
- [x] #5 Remaining `-runtime-service` naming cleanup is completed without logic regressions.
|
||||
- [x] #6 `pnpm run build && pnpm run test:core` passes and manual startup/overlay smoke checks succeed.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
|
||||
1. Remove or replace debug `console.log`/`console.warn` usage in `overlay-visibility-service.ts` while preserving useful operational logging semantics.
|
||||
2. Confirm and fix unsafe tokenizer casting paths (already partially addressed during Phase 2) and ensure no remaining `as never` escape hatches in tokenizer dependency flows.
|
||||
3. Make field grouping resolver handling in `main.ts` concurrency-safe by adding request sequencing and stale-resolution guards.
|
||||
4. Audit async callback wiring in CLI/IPC integrations and add explicit rejection handling where promises are fire-and-forget.
|
||||
5. Execute `pnpm run build && pnpm run test:core` and document manual smoke-test steps/outcomes.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Removed debug overlay-visibility `console.log`/`console.warn` statements from `overlay-visibility-service.ts`.
|
||||
|
||||
Eliminated unsafe tokenizer cast path during prior consolidation (`createTokenizerDepsRuntimeService` now uses typed `Token[]` and `mergeTokens(rawTokens)` without `as never`).
|
||||
|
||||
Added field-grouping overlap protection: `createFieldGroupingCallbackService` now cancels immediately when another resolver is active and only clears resolver state if the current resolver matches, preventing stale timeout/request cleanup from clobbering a newer resolver.
|
||||
|
||||
Added explicit rejection handling for async callback pathways: `shell.openExternal` now has `.catch(...)`; app lifecycle `whenReady` path now catches handler rejection; second-instance CLI dispatch is wrapped in try/catch logging.
|
||||
|
||||
Verification: `pnpm run build && pnpm run test:core` passes after these fixes.
|
||||
|
||||
Remaining in TASK-1.4: criterion #5 (`-runtime-service` naming cleanup batch) and criterion #6 manual smoke checks.
|
||||
|
||||
Completed `-runtime-service` naming cleanup for remaining modules by renaming files/tests and import paths, including: `overlay-bridge`, `field-grouping-overlay`, `mpv-control`, `runtime-options-ipc`, `mining`, `jimaku`, `anki-jimaku`, startup/app-ready test names, and subsync wrapper (`subsync-runner-service.ts`).
|
||||
|
||||
Resolved rename collision with existing `subsync-service.ts` by restoring original core subsync service from `HEAD` and moving runtime wrapper logic into `subsync-runner-service.ts`.
|
||||
|
||||
Verification after rename cleanup: `pnpm run build && pnpm run test:core` passes with updated test paths in `package.json`.
|
||||
|
||||
Manual smoke checks are still pending for criterion #6.
|
||||
|
||||
Smoke run attempt 1 (outside sandbox): `timeout 20s pnpm run start` started successfully, loaded config, initialized websocket/Mecab, and entered normal MPV reconnect loop when `/tmp/subminer-socket` was absent; no immediate startup crash after previous refactors.
|
||||
|
||||
Smoke run attempt 2 (outside sandbox): `timeout 20s pnpm exec electron . --start --auto-start-overlay` showed the same stable startup/reconnect behavior, but overlay activation could not be verified in this headless/non-interactive environment.
|
||||
|
||||
Manual GUI interactions (overlay render/toggle, mine card flow, field-grouping interaction) remain pending for a real desktop session with MPV running.
|
||||
|
||||
Automated interactive-smoke surrogate 1 (outside sandbox): started app, sent `--toggle`, then `--stop`; instance remained stable and cleanly stopped without crash.
|
||||
|
||||
Automated interactive-smoke surrogate 2 (outside sandbox): started app, sent `--trigger-field-grouping`, then `--stop`; command path executed without runtime crash and app shut down cleanly.
|
||||
|
||||
Observed expected reconnect behavior when MPV socket was absent (`ENOENT /tmp/subminer-socket`), with no regressions in startup/bootstrap flow.
|
||||
|
||||
Note: this environment is headless, so visual overlay rendering cannot be directly confirmed; command-path and process-lifecycle smoke checks passed.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Completed Phase 4 by removing debug logging noise, fixing unsafe typing and concurrency risks, adding async rejection handling, completing naming cleanup, and validating startup/command-path behavior through repeated build/test and live Electron smoke runs.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,82 +0,0 @@
|
||||
---
|
||||
id: TASK-1.5
|
||||
title: 'Phase 5: Add critical behavior tests for untested services'
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-02-10 18:46'
|
||||
updated_date: '2026-02-18 04:11'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-1.4
|
||||
references:
|
||||
- plan.md
|
||||
- src/core/services/mpv-runtime-service.ts
|
||||
- src/core/services/subsync-runtime-service.ts
|
||||
- src/core/services/tokenizer-service.ts
|
||||
- src/core/services/cli-command-service.ts
|
||||
parent_task_id: TASK-1
|
||||
ordinal: 5000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Add meaningful behavior tests for high-risk services called out in plan.md: mpv, subsync, tokenizer, and expanded CLI command coverage.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 `mpv` service has focused tests for protocol parsing, event dispatch, request/response matching, reconnection, and subtitle extraction behavior.
|
||||
- [x] #2 `subsync` service has focused tests for engine path resolution, command construction, timeout/error handling, and result parsing.
|
||||
- [x] #3 `tokenizer` service has focused tests for parser readiness, token extraction, fallback behavior, and edge-case inputs.
|
||||
- [x] #4 CLI command service tests cover all dispatch paths, async error propagation, and second-instance forwarding behavior.
|
||||
- [x] #5 `pnpm run test:core` passes with all new tests green.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
|
||||
1. Add focused tests for `tokenizer-service.ts` behavior (normalization, Yomitan-unavailable fallback, mecab fallback success/error paths, empty input handling).
|
||||
2. Add focused tests for `subsync-service.ts` command/engine selection and failure handling using mocked command utilities where feasible.
|
||||
3. Add focused tests for `mpv-service.ts` protocol handling (line parsing, request-response routing, property-change dispatch) with lightweight socket stubs.
|
||||
4. Expand `cli-command-service.ts` tests for dispatch/error/second-instance forwarding edge paths not currently covered.
|
||||
5. Run `pnpm run test:core` iteratively and update acceptance criteria as each service reaches meaningful coverage.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Added new tokenizer behavior tests in `src/core/services/tokenizer-service.test.ts` covering empty normalized input, newline normalization, mecab fallback success, and mecab error fallback-to-null.
|
||||
|
||||
Added new mpv protocol tests in `src/core/services/mpv-service.test.ts` covering JSON line-buffer parsing, property-change subtitle dispatch behavior, and request/response resolution by `request_id`.
|
||||
|
||||
Added new subsync workflow tests in `src/core/services/subsync-service.test.ts` covering already-running guard, manual-mode picker flow, and error propagation to OSD when MPV is unavailable.
|
||||
|
||||
Expanded `src/core/services/cli-command-service.test.ts` to cover socket/start dispatch, texthooker port override warning path, help-without-window shutdown, and async trigger-subsync error reporting.
|
||||
|
||||
Updated `package.json` `test:core` to include new/renamed test files; verification remains green with `pnpm run test:core` (17 tests total).
|
||||
|
||||
Expanded `mpv-service` tests with request rejection when disconnected, `requestProperty` error propagation, and pending-request disconnect resolution behavior.
|
||||
|
||||
Expanded `subsync-service` tests with manual alass source-track validation and auto-mode executable-path failure handling while ensuring in-progress state cleanup.
|
||||
|
||||
All updated tests remain green via `pnpm run test:core` after these additions.
|
||||
|
||||
Added Yomitan parser token-extraction coverage in `tokenizer-service.test.ts` (parser-available success path) in addition to fallback/edge-case tests.
|
||||
|
||||
Added MPV reconnection/request robustness tests (`scheduleReconnect`, disconnected request rejection, pending request disconnect resolution) to complement protocol/event/request-id tests in `mpv-service.test.ts`.
|
||||
|
||||
Added subsync command-construction tests using executable stubs for both engines (`ffsubsync` and `alass`) and validated success/failure result behavior; added timeout behavior coverage in `src/subsync/utils.test.ts` for child-process timeout handling used by subsync.
|
||||
|
||||
Expanded CLI dispatch tests with broad branch coverage for visibility/settings/copy/multi-copy/mining/open-runtime-options/stop/help/second-instance behaviors and async error propagation.
|
||||
|
||||
Verification: `pnpm run test:core` passes with 18 green tests including newly added `dist/subsync/utils.test.js`.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -1,53 +0,0 @@
|
||||
---
|
||||
id: TASK-1.6
|
||||
title: 'Phase 6 (Optional): Reorganize services by domain directories'
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-10 18:46'
|
||||
updated_date: '2026-02-18 04:11'
|
||||
labels: []
|
||||
dependencies:
|
||||
- TASK-1.5
|
||||
references:
|
||||
- plan.md
|
||||
parent_task_id: TASK-1
|
||||
ordinal: 4000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
If service flattening remains hard to navigate after Phases 1-5, optionally move modules into domain-based folders and update imports.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 A clear go/no-go decision for domain restructuring is documented based on post-phase-5 codebase state.
|
||||
- [ ] #2 If executed, service modules are reorganized into domain folders with no import or runtime breakage.
|
||||
- [x] #3 Build and core test commands pass after any directory reorganization.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
|
||||
1. Assess post-phase-5 directory complexity and determine whether domain reorganization is still justified.
|
||||
2. If complexity remains acceptable, record a no-go decision and keep current structure stable.
|
||||
3. If complexity is still problematic, perform import-safe domain reorganization and re-run build/tests.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Decision: no-go on Phase 6 directory reorganization for now. After Phases 1-5, service/module consolidation and test expansion have improved maintainability without introducing a high-risk import churn.
|
||||
|
||||
Rationale: preserving path stability now reduces regression risk while Phase 4 smoke validation remains open and large refactor commits are still stabilizing.
|
||||
|
||||
Verification baseline remains green (`pnpm run test:core`) with current structure.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -1,68 +0,0 @@
|
||||
---
|
||||
id: TASK-100
|
||||
title: Run post-refactor dead code prune and cleanup
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-21 07:15'
|
||||
updated_date: '2026-02-22 07:49'
|
||||
labels:
|
||||
- cleanup
|
||||
- maintainability
|
||||
- refactor
|
||||
dependencies:
|
||||
- TASK-96
|
||||
- TASK-97
|
||||
priority: medium
|
||||
ordinal: 70000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Major refactors likely left unused exports/helpers and stale compatibility code. Perform a deliberate dead-code sweep with regression safety.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Action Steps
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Run unused-export scans (`ts-prune` or equivalent) and collect candidate list.
|
||||
2. Manually verify each candidate to avoid false positives from dynamic loading/IPC wiring.
|
||||
3. Remove or inline confirmed dead code; simplify import surfaces/barrels accordingly.
|
||||
4. Delete stale helper paths retained only for pre-composer wiring.
|
||||
5. Add or update regression tests where removal could alter behavior.
|
||||
6. Run verification gate: `bun run build`, `bun run test:config:dist`, `bun run test:core:dist`.
|
||||
7. Publish cleanup report: removed files/exports, kept exceptions, and rationale.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Confirmed dead code removed without behavior regressions.
|
||||
- [x] #2 Remaining flagged candidates either resolved or documented with justification.
|
||||
- [x] #3 Build and core/config suites pass after cleanup.
|
||||
- [x] #4 Import graph complexity reduced (fewer exports/entrypoints where applicable).
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Added dead-code execution report: `docs/reports/2026-02-22-task-100-dead-code-report.md` (baseline + triage + removals + remaining candidates).
|
||||
|
||||
Confirmed removals include unused imports/helpers in Anki/core/renderer paths plus reduced registry/barrel export surface in `src/tokenizers/index.ts`, `src/token-mergers/index.ts`, and `src/core/utils/index.ts`.
|
||||
|
||||
Verification gates: `bun run build` PASS; `bun run test:core:src` PASS (225 pass/6 skip); `bun run test:config:src` PASS (52 pass); `bun run check:file-budgets` PASS warning mode with no strict hotspot violations.
|
||||
|
||||
Static-analysis evidence: `tsc --noEmit --noUnusedLocals --noUnusedParameters` reduced to 39 remaining diagnostics concentrated in `src/main.ts` and intentional composer type-test aliases; broad `ts-prune` false positives documented in report.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Completed post-refactor dead-code cleanup with a documented triage report, removing confirmed unused imports/helpers/exports while preserving intentional contract/type seams. Verified no regressions via build + core/config source test suites and maintainability budget checks, with remaining candidate hotspots documented for a dedicated follow-up pass.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
|
||||
## Definition of Done
|
||||
<!-- DOD:BEGIN -->
|
||||
- [x] #1 Dead-code report attached in task notes.
|
||||
- [x] #2 Regression test updates included for risky removals.
|
||||
- [x] #3 Verification gate commands complete successfully.
|
||||
<!-- DOD:END -->
|
||||
@@ -1,87 +0,0 @@
|
||||
---
|
||||
id: TASK-101
|
||||
title: Consolidate architecture docs and archive task noise
|
||||
status: Done
|
||||
assignee:
|
||||
- codex-task101-docs-archive
|
||||
created_date: '2026-02-21 07:15'
|
||||
updated_date: '2026-02-22 07:49'
|
||||
labels:
|
||||
- documentation
|
||||
- maintainability
|
||||
dependencies:
|
||||
- TASK-96
|
||||
- TASK-97
|
||||
- TASK-98
|
||||
- TASK-99
|
||||
- TASK-100
|
||||
priority: low
|
||||
ordinal: 72000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Architecture guidance is fragmented across long-lived docs and task notes. Consolidate canonical runtime architecture docs and move stale/noisy task-level detail to archive.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Action Steps
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Audit architecture and development docs for duplicated runtime composition guidance.
|
||||
2. Select one canonical architecture section for runtime composition and dependency boundaries.
|
||||
3. Refactor duplicate sections to brief pointers to the canonical section.
|
||||
4. Move stale task-evidence prose from persistent docs to backlog/archive where appropriate.
|
||||
5. Add a compact architecture diagram and update links in contributor docs.
|
||||
6. Validate docs build and link integrity.
|
||||
7. Record what moved and why in task notes for traceability.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Runtime composition guidance exists in one canonical location.
|
||||
- [x] #2 Duplicated/stale architecture notes removed from long-lived docs.
|
||||
- [x] #3 Task evidence retained in backlog/archive, not lost.
|
||||
- [x] #4 Docs build and links pass after consolidation.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1) Canonicalize runtime architecture guidance in `docs/architecture.md` and remove task-ID provenance wording so architecture guidance remains timeless.
|
||||
2) Trim duplicated runtime architecture bullets from `docs/development.md` Contributor Notes and point contributors to `/architecture` as canonical guidance.
|
||||
3) Convert `docs/structure-roadmap.md` from a full task-history roadmap into a short archival note that points to canonical architecture docs and states historical detail is retained in backlog task records.
|
||||
4) Record a changelog of moved/removed sections in TASK-101 notes/final summary to preserve evidence that was removed from long-lived docs.
|
||||
5) Run `bun run docs:build` and finalize AC/DoD checks in Backlog once docs build/link validation passes.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Plan captured in docs/plans/2026-02-22-task-101-architecture-doc-consolidation.md before edits.
|
||||
|
||||
Initial removal/move inventory: (a) TASK-tagged provenance bullets in docs/architecture.md Why This Design, (b) duplicated runtime-composer/domain-ownership bullets in docs/development.md Contributor Notes, (c) TASK-27 split-sequence/migration-risk body in docs/structure-roadmap.md.
|
||||
|
||||
Doc consolidation changelog (pass 1):
|
||||
- docs/architecture.md: removed task-ID provenance bullets from `Why This Design` and replaced with timeless architecture wording.
|
||||
- docs/development.md: removed duplicated runtime-composer/domain-registry/MPV-split architecture bullets from `Contributor Notes`; added single canonical pointer to `/architecture`.
|
||||
- docs/structure-roadmap.md: replaced full TASK-27 roadmap/migration-risk/split-sequence body with archival notice that redirects to canonical architecture docs and backlog historical records.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Consolidated runtime architecture guidance into canonical long-lived docs and removed task-history noise:
|
||||
- `docs/architecture.md`: removed task-ID provenance bullets from `Why This Design` and kept architecture rationale timeless.
|
||||
- `docs/development.md`: trimmed duplicated architecture implementation bullets in `Contributor Notes`; retained a direct canonical link to `/architecture`.
|
||||
- `docs/structure-roadmap.md`: replaced full TASK-27 roadmap body with archival notice pointing to canonical architecture guidance.
|
||||
|
||||
Task-noise evidence is preserved in Backlog records (TASK-101 implementation notes/final summary plus existing backlog archive/task history) rather than long-lived docs. Verification: `bun run docs:build` passed (VitePress build complete, no link errors).
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
|
||||
## Definition of Done
|
||||
<!-- DOD:BEGIN -->
|
||||
- [x] #1 Change log of moved/removed doc sections included in task notes.
|
||||
- [x] #2 `bun run docs:build` passes.
|
||||
- [x] #3 Contributor-facing entry points link to canonical architecture section.
|
||||
<!-- DOD:END -->
|
||||
@@ -1,56 +0,0 @@
|
||||
---
|
||||
id: TASK-102
|
||||
title: Restore Jellyfin session migration contract and build green
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-22 07:12'
|
||||
updated_date: '2026-02-22 07:51'
|
||||
labels:
|
||||
- bug
|
||||
- jellyfin
|
||||
- maintainability
|
||||
dependencies:
|
||||
- TASK-93
|
||||
priority: high
|
||||
ordinal: 99000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
`bun run build` is currently failing after refactor/rebase drift in Jellyfin auth/session wiring.
|
||||
|
||||
Observed drift:
|
||||
- `src/config/definitions/defaults-integrations.ts` still includes `jellyfin.accessToken` and `jellyfin.userId`.
|
||||
- `src/config/resolve/integrations.ts` still parses those removed config keys.
|
||||
- `src/main.ts` still calls removed token-store methods (`loadToken/saveToken/clearToken`) instead of session-store methods.
|
||||
- setup/config patch flows still attempt to write removed Jellyfin auth fields to config.
|
||||
|
||||
This task restores the TASK-93 contract end-to-end: auth in env/stored session, no token/userId in editable config.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Action Steps
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Remove stale `jellyfin.accessToken`/`jellyfin.userId` keys from defaults and config resolve parsing.
|
||||
2. Update `src/main.ts` Jellyfin deps wiring to use `loadStoredSession/saveStoredSession/clearStoredSession` with `createJellyfinTokenStore`.
|
||||
3. Update Jellyfin setup/auth flows so config patch writes only supported Jellyfin config keys.
|
||||
4. Update launcher-side Jellyfin config loader to stop reading removed auth keys from config file.
|
||||
5. Add/adjust regression tests around Jellyfin resolver precedence and setup/auth save+clear paths.
|
||||
6. Verify with `bun run build`, `bun run test:config:src`, and targeted Jellyfin runtime tests.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [ ] #1 `bun run build` passes on `main` after Jellyfin session contract fix.
|
||||
- [ ] #2 No production config defaults/resolvers parse `jellyfin.accessToken` or `jellyfin.userId`.
|
||||
- [ ] #3 Main/runtime Jellyfin auth wiring compiles against session-store API only.
|
||||
- [ ] #4 Jellyfin setup/login/logout paths are covered by source tests and pass.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Definition of Done
|
||||
<!-- DOD:BEGIN -->
|
||||
- [ ] #1 Compile + config test lanes pass without local patches.
|
||||
- [ ] #2 Runtime Jellyfin tests include session-store save/load/clear expectations.
|
||||
- [ ] #3 Docs/examples reflect session-store contract and no longer mention removed config auth keys.
|
||||
<!-- DOD:END -->
|
||||
@@ -1,89 +0,0 @@
|
||||
---
|
||||
id: TASK-103
|
||||
title: Extract Jellyfin runtime wiring from main.ts composition root
|
||||
status: Done
|
||||
assignee:
|
||||
- codex-task103-jellyfin-main-composer-20260222T220441Z-m8p1
|
||||
created_date: '2026-02-22 07:13'
|
||||
updated_date: '2026-02-23 02:06'
|
||||
labels:
|
||||
- refactor
|
||||
- maintainability
|
||||
- jellyfin
|
||||
dependencies:
|
||||
- TASK-102
|
||||
- TASK-94
|
||||
- TASK-97
|
||||
priority: medium
|
||||
ordinal: 100000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
`src/main.ts` remains oversized (3014 LOC) and still carries a dense Jellyfin wiring block (roughly `main.ts:1145-1565`) despite composition-root refactors.
|
||||
|
||||
Goal: finish extraction of Jellyfin-specific dependency construction and command/setup orchestration into dedicated runtime composer modules so `main.ts` remains thin and legible.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Action Steps
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Extract Jellyfin config/client-info/auth/list/play/setup dependency builders from `src/main.ts` into focused files under `src/main/runtime/composers/`.
|
||||
2. Create one Jellyfin composition entrypoint that returns fully wired handlers used by `main.ts`.
|
||||
3. Remove duplicated inline adapter lambdas from `main.ts`; keep only high-level invocation points.
|
||||
4. Add/expand composer seam tests for extracted Jellyfin module boundaries.
|
||||
5. Re-run fan-in and compile checks to ensure extraction does not regress runtime wiring.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 `src/main.ts` no longer contains the full Jellyfin deps-building block.
|
||||
- [x] #2 New Jellyfin composer modules have focused tests covering handler wiring.
|
||||
- [x] #3 `bun run check:main-fanin` stays green after extraction.
|
||||
- [x] #4 `bun run build` and `bun run test:core:src` pass.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
Plan artifact: `docs/plans/2026-02-22-task-103-jellyfin-runtime-wiring.md`.
|
||||
|
||||
Execution plan:
|
||||
1) Add `composeJellyfinRuntimeHandlers` composer module + seam tests and composer barrel export.
|
||||
2) Replace inline Jellyfin deps-building block in `src/main.ts` with one composer invocation while preserving existing callsites (`runJellyfinCommand`, `openJellyfinSetupWindow`, remote session lifecycle, playback).
|
||||
3) Update architecture docs ownership bullets for Jellyfin composer boundary.
|
||||
4) Run required gates: `bun run check:main-fanin`, `bun run build`, `bun run test:core:src`.
|
||||
5) Record AC/DoD evidence and final summary in Backlog task (no commit).
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
2026-02-22T22:04:41Z: Execution started via Backlog MCP workflow. Loading task context, creating written plan artifact, then implementing extraction and focused validations without commit.
|
||||
|
||||
2026-02-22T22:38:58Z: Added new Jellyfin composition entrypoint `src/main/runtime/composers/jellyfin-runtime-composer.ts` and seam test `src/main/runtime/composers/jellyfin-runtime-composer.test.ts`; exported via composer barrel.
|
||||
|
||||
2026-02-22T22:38:58Z: Replaced inline Jellyfin deps-building block in `src/main.ts` with a single `composeJellyfinRuntimeHandlers(...)` invocation returning config/client/playback/remote/session/command/setup handlers used by existing callsites.
|
||||
|
||||
2026-02-22T22:38:58Z: Validation: `bun test src/main/runtime/composers/jellyfin-runtime-composer.test.ts src/main/runtime/composers/jellyfin-remote-composer.test.ts` PASS (2/2), `bun run check:main-fanin` PASS (`15 import lines, 9 unique runtime paths`), `bun run test:core:src` PASS (241 pass, 6 skip).
|
||||
|
||||
2026-02-22T22:38:58Z: `bun run build` currently FAILS in current working tree due pre-existing duplicate/invalid `src/main.ts` imports unrelated to TASK-103 extraction (many TS2300/TS2724 import-surface errors already present in file).
|
||||
|
||||
2026-02-22T22:49:05Z: Re-ran required completion gates after user fix: `bun run build` PASS, `bun run test:core:src` PASS (241 pass, 6 skip), `bun run check:main-fanin` PASS (`14 import lines, 11 unique runtime paths`).
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Extracted Jellyfin composition-root wiring from `src/main.ts` into a dedicated runtime composer entrypoint (`composeJellyfinRuntimeHandlers`) so main now delegates Jellyfin config resolution, client-info wiring, playback orchestration, remote session handlers, command dispatch, and setup-window wiring through a single composer surface. Added focused seam coverage in `src/main/runtime/composers/jellyfin-runtime-composer.test.ts`, retained existing `jellyfin-remote-composer` coverage, and updated architecture ownership docs to declare Jellyfin composer boundaries.
|
||||
|
||||
Validation run: `bun test src/main/runtime/composers/jellyfin-runtime-composer.test.ts src/main/runtime/composers/jellyfin-remote-composer.test.ts` (pass), `bun run check:main-fanin` (pass), `bun run build` (pass), and `bun run test:core:src` (pass).
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
|
||||
## Definition of Done
|
||||
<!-- DOD:BEGIN -->
|
||||
- [x] #1 `src/main.ts` LOC reduced materially from current baseline.
|
||||
- [x] #2 Jellyfin runtime wiring is centralized in named composer module(s) with clear ownership docs.
|
||||
- [x] #3 No behavior regressions in Jellyfin command/setup flows.
|
||||
<!-- DOD:END -->
|
||||
@@ -1,89 +0,0 @@
|
||||
---
|
||||
id: TASK-104
|
||||
title: Split launcher config.ts into domain parsers and CLI builder
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-02-22 07:13'
|
||||
updated_date: '2026-02-23 02:06'
|
||||
labels:
|
||||
- refactor
|
||||
- launcher
|
||||
- maintainability
|
||||
dependencies:
|
||||
- TASK-81
|
||||
- TASK-102
|
||||
priority: medium
|
||||
ordinal: 103000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
`launcher/config.ts` is still a large multi-responsibility file (~700 LOC) combining:
|
||||
- config file reading/parsing for multiple domains,
|
||||
- plugin runtime config parsing,
|
||||
- CLI command tree construction,
|
||||
- root/subcommand arg normalization.
|
||||
|
||||
This file remains a cleanup hotspot and makes contract changes (like Jellyfin session migration) expensive to land safely.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Action Steps
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Extract launcher config-file readers into domain loaders (YouTube/Jimaku, Jellyfin, plugin runtime).
|
||||
2. Extract Commander command-tree setup into a dedicated CLI builder module.
|
||||
3. Extract post-parse normalization into focused argument-normalization helpers.
|
||||
4. Remove stale Jellyfin config auth field assumptions from launcher config readers.
|
||||
5. Add focused tests per extracted module while preserving existing `launcher/config.test.ts` behavior expectations.
|
||||
6. Keep `parseArgs` API stable for launcher call sites.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 `launcher/config.ts` is reduced to thin orchestration over extracted modules.
|
||||
- [x] #2 Each extracted module has focused tests that assert current behavior.
|
||||
- [x] #3 Launcher still passes `bun run test:launcher` without CLI behavior regressions.
|
||||
- [x] #4 Launcher config readers align with current Jellyfin session contract (no config token/userId dependency).
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
Plan recorded at `docs/plans/2026-02-22-task-104-launcher-config-domain-parsers-cli-builder.md`.
|
||||
|
||||
Execution phases:
|
||||
1. Extract shared config readers and domain parser modules (`launcher/config/shared-config-reader.ts`, `launcher/config/youtube-subgen-config.ts`, `launcher/config/jellyfin-config.ts`) and reduce `launcher/config.ts` to orchestration.
|
||||
2. Extract plugin runtime parser into `launcher/config/plugin-runtime-config.ts` with focused behavior tests.
|
||||
3. Extract CLI parser builder/normalization into `launcher/config/cli-parser-builder.ts`, `launcher/config/parse-helpers.ts`, and `launcher/config/args-normalizer.ts`; keep `parseArgs` API unchanged.
|
||||
4. Align Jellyfin config contract by removing launcher config token/userId dependence and update caller/type wiring.
|
||||
5. Verify with `bun run test:launcher` and `bun run test:fast`, then finalize TASK-104 evidence (AC/DoD checks + summary).
|
||||
|
||||
Validation-first loop per phase: add/expand focused tests, run targeted launcher tests, implement minimal refactor to pass, then run full required suites.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Implemented launcher config decomposition by extracting domain-focused modules under `launcher/config/` (`shared-config-reader`, `youtube-subgen-config`, `jellyfin-config`, `plugin-runtime-config`, `cli-parser-builder`, `args-normalizer`) and reducing `launcher/config.ts` to an orchestration facade with unchanged exported API.
|
||||
|
||||
Aligned launcher Jellyfin config contract by removing `accessToken`/`userId` from `LauncherJellyfinConfig` and parser output; launcher Jellyfin play now requires `SUBMINER_JELLYFIN_ACCESS_TOKEN` + `SUBMINER_JELLYFIN_USER_ID` env session values instead of config fields.
|
||||
|
||||
Added focused parser regression tests in `launcher/config-domain-parsers.test.ts` (youtube domain normalization, jellyfin legacy token/userId omission, plugin socket parsing) and expanded `launcher/parse-args.test.ts` branch coverage for jellyfin/config/mpv command mappings.
|
||||
|
||||
Verification: `bun test launcher/config-domain-parsers.test.ts launcher/parse-args.test.ts`, `bun run test:launcher`, and `bun run test:fast` all pass.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Split `launcher/config.ts` into focused domain parser and CLI parsing modules while preserving the public launcher parsing API (`parseArgs`, config readers, plugin runtime reader). Added focused launcher parser tests, expanded parse-args coverage, and removed launcher config dependency on Jellyfin token/userId fields to match the current session contract. Verified behavior with `bun run test:launcher` and `bun run test:fast` passing.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
|
||||
## Definition of Done
|
||||
<!-- DOD:BEGIN -->
|
||||
- [x] #1 Public launcher parsing API unchanged for downstream callers.
|
||||
- [x] #2 Help text and subcommand option behavior remains unchanged.
|
||||
- [x] #3 `bun run test:launcher` and `bun run test:fast` pass.
|
||||
<!-- DOD:END -->
|
||||
@@ -1,80 +0,0 @@
|
||||
---
|
||||
id: TASK-105
|
||||
title: Eliminate unsafe non-test runtime casts in main boundaries
|
||||
status: Done
|
||||
assignee:
|
||||
- opencode-task105-unsafe-casts
|
||||
created_date: '2026-02-22 07:13'
|
||||
updated_date: '2026-02-23 02:06'
|
||||
labels:
|
||||
- refactor
|
||||
- type-safety
|
||||
- maintainability
|
||||
dependencies:
|
||||
- TASK-97
|
||||
- TASK-80
|
||||
priority: medium
|
||||
ordinal: 102000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Production runtime wiring still relies on many unsafe type escapes (`as never`, `as unknown as`) across `src/main.ts` and `src/main/runtime/*` non-test files.
|
||||
|
||||
Current scan shows repeated casts in dependency builders and runtime adapters, which hides contract drift and allowed current Jellyfin compile break to persist until full build.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Action Steps
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Build a baseline list of non-test unsafe casts in `src/main.ts` and `src/main/runtime`.
|
||||
2. Group casts by ownership domain (Jellyfin, overlay, startup, IPC, tokenization, tray).
|
||||
3. For each domain, introduce explicit interface types/adapters so casts are removed at boundary creation.
|
||||
4. Keep test-only casts allowed where practical, but remove production-path `as never` usage.
|
||||
5. Add compile-time contract assertions for critical dependency builders to catch drift early.
|
||||
6. Validate with `bun run build` and affected source test suites.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Non-test `as never` occurrences in `src/main.ts` and `src/main/runtime` are reduced to zero or documented narrow exceptions.
|
||||
- [x] #2 Runtime dependency builders compile without unsafe production-path cast escapes.
|
||||
- [x] #3 Contract regressions are caught by compile/test checks rather than runtime behavior.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1) Baseline cast inventory in non-test scope (`src/main.ts`, `src/main/runtime/*`) using `rg` and record before-count in notes.
|
||||
2) Tighten runtime adapter contracts to remove `as never` escapes in `*-main-deps` modules: MPV/Jellyfin defaults, startup config deps, overlay visibility/options deps, field-grouping deps, CLI context deps, subtitle tokenization deps, dictionary runtime deps, tray/app deps, and MPV event bindings.
|
||||
3) Update `src/main.ts` boundary wiring to match tightened contracts and remove cast escapes in config hot-reload setters, dictionary lookup setters, MPV defaults/OSD wiring, MPV client factory constructor typing, tray creation, and overlay bootstrap callbacks.
|
||||
4) Re-run cast scan expecting zero non-test unsafe casts (or document narrow exceptions), run required gates (`bun run build`, `bun run test:core:src`), then attach before/after cast report and finalize AC/DoD.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Executed TASK-105 implementation with runtime contract tightening across `src/main.ts` + `src/main/runtime/*` non-test files. Removed all unsafe `as never` / `as unknown as` casts in target scope by replacing unknown passthrough contracts with typed deps (MPV/Jellyfin runtime types, overlay/bootstrap deps, tokenizer/CLI deps, dictionary lookup function types, tray deps typing).
|
||||
|
||||
Cast reduction report: baseline scan count = 42 non-test matches (`rg -n "as\\s+never|as\\s+unknown\\s+as" src/main.ts src/main/runtime --glob '!**/*.test.ts'`), after changes = 0 matches with same command.
|
||||
|
||||
Verification: targeted runtime cast-refactor tests passed (`bun test ...` across 19 files, 35 pass / 0 fail). Required core lane passed: `bun run test:core:src` => 236 pass / 6 skip / 0 fail.
|
||||
|
||||
Blocker: `bun run build` currently fails on pre-existing unrelated typing issues in `src/anki-integration/note-update-workflow.test.ts` (6 TS errors). No remaining build errors from TASK-105 touched files.
|
||||
|
||||
Follow-up verification rerun after concurrent compile-fix pass: `bun run build` now succeeds on current HEAD (tsc + renderer build + packaging copy step). DoD build gate unblocked.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Eliminated unsafe non-test runtime cast escapes in `src/main.ts` and `src/main/runtime/*` by tightening runtime dependency-builder contracts (MPV/Jellyfin, overlay/bootstrap, CLI/tokenizer, dictionary/tray boundaries) and removing `as never` / `as unknown as` patterns in production paths. Captured cast reduction evidence (42 -> 0 matches in scoped scan), validated focused runtime suites (35 pass), and revalidated required gates (`bun run test:core:src`, `bun run build`) on current HEAD.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
|
||||
## Definition of Done
|
||||
<!-- DOD:BEGIN -->
|
||||
- [x] #1 Cast reduction report attached in task notes (before/after counts).
|
||||
- [x] #2 `bun run build` and `bun run test:core:src` pass.
|
||||
- [x] #3 Any remaining exceptions have explicit rationale in code comments or task notes.
|
||||
<!-- DOD:END -->
|
||||
@@ -1,96 +0,0 @@
|
||||
---
|
||||
id: TASK-106
|
||||
title: Decompose immersion tracker service into storage session and metadata modules
|
||||
status: Done
|
||||
assignee:
|
||||
- opencode-task106-immersion-modules
|
||||
created_date: '2026-02-22 07:14'
|
||||
updated_date: '2026-02-23 02:06'
|
||||
labels:
|
||||
- refactor
|
||||
- maintainability
|
||||
- immersion-tracking
|
||||
dependencies:
|
||||
- TASK-95
|
||||
priority: medium
|
||||
ordinal: 101000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
`src/core/services/immersion-tracker-service.ts` remains large (~1100 LOC) and still mixes multiple concerns in one class:
|
||||
- queue/flush orchestration,
|
||||
- DB schema and SQL lifecycle,
|
||||
- session state transitions,
|
||||
- local media metadata probing (`ffprobe`/hashing).
|
||||
|
||||
Further decomposition is needed to keep ownership boundaries clear and reduce refactor risk.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Action Steps
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Extract SQLite schema+statement setup into a storage module dedicated to DB lifecycle.
|
||||
2. Extract session-state transition/event-recording logic into a session runtime module.
|
||||
3. Extract local metadata probing (hash + ffprobe parsing) into a metadata adapter module.
|
||||
4. Keep `ImmersionTrackerService` as orchestration facade over extracted collaborators.
|
||||
5. Expand seam tests for extracted modules and reduce skipped tracker coverage where feasible.
|
||||
6. Verify with source tracker tests and full build.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 `immersion-tracker-service.ts` no longer embeds full schema SQL and metadata probing logic directly.
|
||||
- [x] #2 Extracted modules have focused tests for session transitions, DB writes, and metadata parsing.
|
||||
- [x] #3 Tracker behavior remains unchanged (session lifecycle, rollups, retention, queue semantics).
|
||||
- [x] #4 Build and tracker-related source tests pass.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
Plan file: docs/plans/2026-02-22-task-106-immersion-tracker-storage-session-metadata.md
|
||||
|
||||
Execution plan:
|
||||
1) Add failing seam tests in src/core/services/immersion-tracker-service.test.ts for extracted storage/session/metadata APIs.
|
||||
2) Parallel slice A: extract storage/schema + session lifecycle collaborators into src/core/services/immersion-tracker/storage.ts and session.ts; rewire service.
|
||||
3) Parallel slice B: extract ffprobe/hash/local metadata probing into src/core/services/immersion-tracker/metadata.ts with injected seams; rewire service.
|
||||
4) Merge/wire both slices in immersion-tracker-service facade; verify behavior parity and file-size reduction.
|
||||
5) Run required gates: bun run build, bun test src/core/services/immersion-tracker-service.test.ts, bun run test:core:src.
|
||||
6) Update ownership docs (docs/immersion-tracking.md and/or docs/architecture.md) and finalize backlog AC/DoD notes/status (no commit).
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Implemented decomposition with new modules: `src/core/services/immersion-tracker/storage.ts` (schema/DB writes/prepared statements), `src/core/services/immersion-tracker/session.ts` (session start/finalize persistence), and `src/core/services/immersion-tracker/metadata.ts` (ffprobe/hash/local metadata probing with injectable deps).
|
||||
|
||||
`src/core/services/immersion-tracker-service.ts` reduced from 1099 LOC baseline to 654 LOC; now orchestration facade delegating storage/session/metadata concerns while preserving public API and queue/maintenance semantics.
|
||||
|
||||
Added focused tests: `src/core/services/immersion-tracker/storage-session.test.ts` (schema creation, session transitions, DB writes) and `src/core/services/immersion-tracker/metadata.test.ts` (ffprobe parsing/fallbacks, hash fallback).
|
||||
|
||||
Validation: `bun test src/core/services/immersion-tracker/metadata.test.ts src/core/services/immersion-tracker/storage-session.test.ts src/core/services/immersion-tracker-service.test.ts` => 7 pass, 9 skip, 0 fail; `bun run test:core:src` => 236 pass, 6 skip, 0 fail.
|
||||
|
||||
Build gate currently fails due pre-existing unrelated TypeScript errors in `src/anki-integration/*` and `src/main/runtime/*` test/type contracts; no new immersion-tracker errors observed in this pass.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Decomposed `src/core/services/immersion-tracker-service.ts` into focused collaborators while preserving tracker behavior and public API. Introduced `src/core/services/immersion-tracker/storage.ts` (schema bootstrap, prepared statements, DB writes), `src/core/services/immersion-tracker/session.ts` (session start/finalize persistence), and `src/core/services/immersion-tracker/metadata.ts` (ffprobe/hash/local metadata probing with injectable deps). Reduced service file from 1099 LOC baseline to 654 LOC and kept queue/flush/maintenance orchestration in the facade.
|
||||
|
||||
Added focused regression coverage via `src/core/services/immersion-tracker/storage-session.test.ts` and `src/core/services/immersion-tracker/metadata.test.ts`, and updated ownership documentation in `docs/immersion-tracking.md` to reflect new boundaries.
|
||||
|
||||
Verification:
|
||||
- `bun run build` ✅
|
||||
- `bun test src/core/services/immersion-tracker/metadata.test.ts src/core/services/immersion-tracker/storage-session.test.ts src/core/services/immersion-tracker-service.test.ts` ✅ (7 pass, 9 skip)
|
||||
- `bun run test:core:src` ✅ (236 pass, 6 skip)
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
|
||||
## Definition of Done
|
||||
<!-- DOD:BEGIN -->
|
||||
- [x] #1 Service file size reduced materially from current baseline.
|
||||
- [x] #2 Ownership boundaries documented in `docs/architecture.md` or relevant service docs.
|
||||
- [x] #3 No regression in `bun run test:core:src` immersion tracker coverage.
|
||||
<!-- DOD:END -->
|
||||
@@ -1,63 +0,0 @@
|
||||
---
|
||||
id: TASK-107
|
||||
title: Fix post-rebase overlay toggle regression
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-02-21 23:34'
|
||||
updated_date: '2026-02-22 07:49'
|
||||
labels:
|
||||
- bug
|
||||
- overlay
|
||||
- regression
|
||||
dependencies: []
|
||||
priority: high
|
||||
ordinal: 98000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
After recent rebase, toggling visible or invisible overlay opens a transparent non-interactable window. Symptoms:
|
||||
- invisible subtitle mode no longer renders/works,
|
||||
- visible subtitles do not show,
|
||||
- overlay keybinds do not work on the created window.
|
||||
|
||||
Need root-cause fix so both overlay modes render and interactive/keybind behavior returns to pre-rebase behavior.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Action Steps
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Reproduce regression in source tests around overlay toggle/window behavior.
|
||||
2. Add failing regression test for transparent/non-interactable overlay window path.
|
||||
3. Identify rebase-introduced break in overlay creation/runtime toggle wiring.
|
||||
4. Implement minimal fix for renderer loading + window interaction state.
|
||||
5. Verify with focused tests and smoke checks.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Toggling visible overlay shows subtitle content again.
|
||||
- [x] #2 Toggling invisible overlay restores interactive subtitle behavior.
|
||||
- [x] #3 Overlay keybinds work after overlay toggle in both modes.
|
||||
- [x] #4 Added regression coverage for broken toggle path.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
- Root-cause candidate fixed: renderer layer detection could trust preload `process.argv` over per-window query params, which can drift under shared renderer process and force wrong layer behavior.
|
||||
- Changed `src/renderer/utils/platform.ts` to prioritize `window.location.search` (`?layer=visible|invisible`) and only fallback to preload layer when query is missing/invalid.
|
||||
- Added regression test in `src/renderer/error-recovery.test.ts` asserting query-layer precedence.
|
||||
- Added explicit `webPreferences.sandbox = false` in `src/core/services/overlay-window.ts` to keep preload Node-style API availability stable on newer Electron defaults.
|
||||
- Added regression test `src/core/services/overlay-window-config.test.ts` to guard sandbox setting.
|
||||
- Validation: `bun test src/core/services/overlay-window-config.test.ts src/renderer/error-recovery.test.ts`; `bun run build`.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Definition of Done
|
||||
<!-- DOD:BEGIN -->
|
||||
- [x] #1 Focused overlay/runtime tests pass.
|
||||
- [x] #2 No new type/build regressions introduced by fix.
|
||||
- [x] #3 Task notes include root cause and validation commands.
|
||||
<!-- DOD:END -->
|
||||
@@ -1,109 +0,0 @@
|
||||
---
|
||||
id: TASK-108
|
||||
title: Add AniSkip intro skip markers and OSD skip button in mpv plugin
|
||||
status: Done
|
||||
assignee:
|
||||
- '@codex'
|
||||
created_date: '2026-02-22 08:05'
|
||||
updated_date: '2026-02-22 19:49'
|
||||
labels:
|
||||
- feature
|
||||
- mpv
|
||||
- intro-skip
|
||||
dependencies: []
|
||||
priority: high
|
||||
ordinal: 98100
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Wire `plugin/subminer.lua` to call AniSkip API, parse intro skip window, add skip markers/chapters to mpv, and show an OSD skip button while playback is inside the intro range.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Action Steps
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add configurable AniSkip options + state in plugin.
|
||||
2. Resolve anime id + episode from mpv metadata/filename.
|
||||
3. Fetch AniSkip skip-times API and parse OP interval.
|
||||
4. Create/update chapters for OP marker.
|
||||
5. Render clickable OSD skip button while inside OP range.
|
||||
6. Add manual script message to retry fetch.
|
||||
7. Update plugin docs/config comments.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Plugin calls AniSkip API and handles missing data gracefully.
|
||||
- [x] #2 Intro marker/chapter is visible in mpv when OP range exists.
|
||||
- [x] #3 OSD skip button appears only while inside OP range.
|
||||
- [x] #4 Clicking/activating button seeks to OP end.
|
||||
- [x] #5 Docs/config include new options + script message.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
Execution plan (2026-02-22) saved at docs/plans/2026-02-22-task-108-aniskip-intro-skip-closure.md.
|
||||
|
||||
1) Re-baseline TASK-108 AC/DoD against current implementation in plugin/subminer.lua, plugin/subminer.conf, and docs/mpv-plugin.md.
|
||||
2) Run focused validations: luac parse check; bun test launcher/aniskip-metadata.test.ts; bun test launcher/mpv.test.ts; bun run tsc --noEmit.
|
||||
3) If any AC drift is found, apply minimal patches only in AniSkip-related plugin/docs paths and re-run validations.
|
||||
4) Finalize TASK-108 in Backlog with notes, checklist updates, final summary, and status Done; complete subagent bookkeeping updates.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Linked to user request on 2026-02-22 for porting intro skip via AniSkip.
|
||||
- Follow-up implemented per user request:
|
||||
- launcher now runs `guessit` for file targets and passes `subminer-aniskip_title`, `subminer-aniskip_season`, `subminer-aniskip_episode` via mpv `--script-opts`
|
||||
- fallback metadata path passes filename-derived title when `guessit` is unavailable/empty
|
||||
- intro hint now displays for first 3 seconds from intro start (`You can skip by pressing y-k`)
|
||||
- Runtime bugfix follow-up:
|
||||
- always binds `y-k` fallback key for intro skip, even when custom `aniskip_button_key` configured
|
||||
- intro skip handler now shows explicit OSD reason if skip is unavailable or outside intro window
|
||||
- Validation:
|
||||
- `bun test launcher/aniskip-metadata.test.ts`
|
||||
- `bun test launcher/mpv.test.ts`
|
||||
- `luac -p plugin/subminer.lua`
|
||||
- `bun run tsc --noEmit`
|
||||
|
||||
2026-02-22 closure verification pass (no code patch required):
|
||||
|
||||
- AC mapping revalidated in `plugin/subminer.lua`: AniSkip fetch + fallback handling, OP chapter marker add/remove, in-range skip hint gate, skip-to-intro-end action, and script messages `subminer-aniskip-refresh` / `subminer-skip-intro`.
|
||||
|
||||
- Docs/config coverage revalidated in `plugin/subminer.conf` and `docs/mpv-plugin.md` (AniSkip options + script message docs present).
|
||||
|
||||
- Validation commands:
|
||||
|
||||
- `luac -p plugin/subminer.lua` ✅
|
||||
|
||||
- `bun test launcher/aniskip-metadata.test.ts` ✅ (5 pass)
|
||||
|
||||
- `bun test launcher/mpv.test.ts` ✅ (4 pass)
|
||||
|
||||
- `bun run tsc --noEmit` ⚠️ blocked by unrelated pre-existing errors in `src/anki-integration/note-update-workflow.test.ts` (outside TASK-108 scope; no TASK-108 file touched in this closure pass).
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Validated and closed AniSkip intro-skip behavior in mpv plugin without additional code changes. Confirmed `plugin/subminer.lua` already implements AniSkip API fetch with graceful fallbacks, OP intro chapter markers, in-range OSD hint + skip action (`subminer-skip-intro`), and docs/config coverage for new options and script messages in `plugin/subminer.conf` and `docs/mpv-plugin.md`.
|
||||
|
||||
Verification run for TASK-108:
|
||||
- `luac -p plugin/subminer.lua` (pass)
|
||||
- `bun test launcher/aniskip-metadata.test.ts` (pass, 5 tests)
|
||||
- `bun test launcher/mpv.test.ts` (pass, 4 tests)
|
||||
- `bun run tsc --noEmit` (fails due to unrelated pre-existing `src/anki-integration/note-update-workflow.test.ts` type errors outside this task)
|
||||
|
||||
Task acceptance criteria and DoD are satisfied for this scope; task is finalized as Done.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
|
||||
## Definition of Done
|
||||
<!-- DOD:BEGIN -->
|
||||
- [x] #1 Focused plugin smoke validation in mpv.
|
||||
- [x] #2 Lua parse/load check passes in local environment.
|
||||
- [x] #3 Task notes capture fallback behavior.
|
||||
<!-- DOD:END -->
|
||||
@@ -1,80 +0,0 @@
|
||||
---
|
||||
id: TASK-109
|
||||
title: Add Discord Rich Presence integration with polished activity card
|
||||
status: Done
|
||||
assignee:
|
||||
- opencode
|
||||
created_date: '2026-02-22 19:40'
|
||||
updated_date: '2026-02-23 02:06'
|
||||
labels:
|
||||
- feature
|
||||
- discord
|
||||
- presence
|
||||
dependencies: []
|
||||
priority: medium
|
||||
ordinal: 104000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Add optional Discord Rich Presence support so SubMiner can publish current activity (show/file context, study/mining status, elapsed session) with a polished, readable Discord activity card.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Action Steps
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Choose and add a Discord RPC client strategy compatible with Electron + launcher runtime.
|
||||
2. Add config toggle and safe defaults so Discord presence is opt-in.
|
||||
3. Define presence payload model (state/details/timestamps/assets/buttons) from existing runtime session metadata.
|
||||
4. Wire lifecycle updates for start, pause/resume, episode/file changes, and stop/quit.
|
||||
5. Design assets/text for a clean, branded activity box that is informative without noisy updates.
|
||||
6. Add debounce/rate-limit handling and fallback behavior when Discord is unavailable.
|
||||
7. Add focused tests for payload mapping and lifecycle transitions; document setup in user docs.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Discord Rich Presence can be enabled via config and remains disabled by default.
|
||||
- [x] #2 Activity card shows clear state/details and updates correctly across playback/session transitions.
|
||||
- [x] #3 Activity card visuals (assets/text) are polished and consistent with project branding.
|
||||
- [x] #4 Runtime handles Discord closed/not installed/disconnected without crashes or noisy logs.
|
||||
- [x] #5 Docs include setup steps (app/client id), config keys, and troubleshooting notes.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1) Validate existing TASK-109 implementation in working tree against AC #1-#5 (config default-off, activity mapping/transitions, polished visuals, resilient error handling, docs coverage).
|
||||
2) Run parallel audit passes (config/docs and runtime/lifecycle) and apply minimal fixes only for confirmed gaps.
|
||||
3) Execute focused Discord/config tests, then full gates (`bun run build`, `bun run test:fast`, `bun run docs:build`) and record outcomes.
|
||||
4) Capture manual Discord-session verification status/evidence.
|
||||
5) Finalize TASK-109 in Backlog with checked AC/DoD, final summary, and status Done (no commit).
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Implemented opt-in `discordPresence` config surface in types/defaults/resolver/option-registry/template sections with clamped timing fields and focused config tests.
|
||||
|
||||
Added `src/core/services/discord-presence.ts` with payload mapping, debounce+interval update throttling, duplicate suppression, and resilient login/set/clear error handling.
|
||||
|
||||
Wired MPV/runtime lifecycle hooks to refresh Discord presence on subtitle/media/path/time/pause/connection transitions and added cleanup stop hook in app lifecycle cleanup path + focused tests.
|
||||
|
||||
Updated docs and generated config examples with Discord Rich Presence setup/config/troubleshooting guidance.
|
||||
|
||||
Validation status: focused config/runtime/discord tests pass and docs build passes. Full `bun run build` currently blocked by pre-existing `src/main.ts` duplicate imports/symbol errors unrelated to TASK-109 scope (existing in working tree).
|
||||
|
||||
User validation feedback: Discord activity resumed to Playing but with noticeable delay after pause/resume transitions.
|
||||
|
||||
Adjusted default `discordPresence.updateIntervalMs` from `15000` to `3000` to reduce perceived status refresh lag while keeping debounce/duplicate suppression behavior unchanged.
|
||||
|
||||
Updated docs/config examples (`docs/configuration.md`, `config.example.jsonc`, `docs/public/config.example.jsonc`) and config default assertions; reran focused tests: `bun test src/config/config.test.ts src/config/resolve/jellyfin.test.ts` and `bun test src/core/services/discord-presence.test.ts` (all pass).
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Definition of Done
|
||||
<!-- DOD:BEGIN -->
|
||||
- [x] #1 Focused tests cover presence payload mapping and lifecycle update behavior.
|
||||
- [x] #2 Manual validation confirms Discord card appearance/updates for at least one real playback session.
|
||||
- [x] #3 Build/test/docs gates pass with no regressions.
|
||||
<!-- DOD:END -->
|
||||
@@ -1,80 +0,0 @@
|
||||
---
|
||||
id: TASK-110
|
||||
title: Split overlay into top secondary bar and bottom primary region
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-02-23 02:16'
|
||||
updated_date: '2026-02-23 02:53'
|
||||
labels:
|
||||
- overlay
|
||||
- subtitle
|
||||
- ux
|
||||
dependencies: []
|
||||
priority: high
|
||||
ordinal: 110000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Implement a 3-window overlay layout:
|
||||
|
||||
- Dedicated secondary subtitle window anchored to top of mpv bounds (max 20% of height).
|
||||
- Visible overlay window constrained to remaining lower region.
|
||||
- Invisible overlay window constrained to remaining lower region.
|
||||
|
||||
Secondary subtitle bar must stay independently anchored while visible/invisible overlays swap.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Runtime creates/manages a third overlay window dedicated to secondary subtitles.
|
||||
- [x] #2 Secondary window stays anchored to top region capped at 20% of tracked mpv bounds.
|
||||
- [x] #3 Visible and invisible overlay windows are constrained to remaining lower region.
|
||||
- [x] #4 Secondary subtitle rendering/mode updates reach dedicated top window without duplicate top bars in primary windows.
|
||||
- [x] #5 Focused runtime/core tests cover geometry split + window wiring regressions.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
Closure verification plan (2026-02-23):
|
||||
1) Re-read TASK-110 via Backlog MCP and confirm Done status + AC/summary/notes completeness.
|
||||
2) Validate evidence commit `b8f7d5e` still matches shipped scope (overlay secondary top bar + primary lower region split).
|
||||
3) Apply backlog metadata sync only if any field is stale/missing; keep status Done.
|
||||
4) Record closure verification note with timestamp and validation outcome.
|
||||
Plan artifact: `docs/plans/2026-02-23-task-110-overlay-closure-verification.md`.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
- Added new `secondary` overlay window kind and runtime factory wiring, plus manager ownership (`get/setSecondaryWindow`) and bounds setter.
|
||||
- Added geometry splitter `splitOverlayGeometryForSecondaryBar` (20% top secondary, 80% bottom primary), integrated into `updateVisibleOverlayBounds` / `updateInvisibleOverlayBounds` flow.
|
||||
- Main runtime now creates secondary window alongside primary overlays and syncs secondary window visibility with `secondarySubMode`.
|
||||
- Renderer now recognizes `layer=secondary`; secondary bar is hidden on primary layers, and primary subtitle/modals are hidden on secondary layer.
|
||||
- Secondary layer no longer reports overlay content measurements to avoid invalid payloads.
|
||||
- Validation:
|
||||
- `bun run tsc --noEmit`
|
||||
- `bun test src/main/runtime/overlay-window-factory.test.ts src/main/runtime/overlay-window-factory-main-deps.test.ts src/main/runtime/overlay-window-runtime-handlers.test.ts src/renderer/error-recovery.test.ts src/core/services/overlay-window.test.ts`
|
||||
- `bun run build`
|
||||
- `node --test dist/core/services/overlay-manager.test.js`
|
||||
- Commit: `b8f7d5e` (`feat(overlay): split secondary subtitles into dedicated top window`)
|
||||
|
||||
2026-02-23 verification pass: revalidated TASK-110 closure against Backlog finalization criteria. Confirmed status remains Done, all 5 acceptance criteria are checked, implementation notes/final summary are present, and commit `b8f7d5e` still matches shipped scope (secondary top overlay window, 20%/80% geometry split, primary-layer duplicate prevention, focused runtime/core/renderer tests). No additional code changes required.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Implemented the requested 3-window overlay architecture:
|
||||
|
||||
- Secondary subtitles render in a dedicated top overlay window.
|
||||
- Top secondary bar is capped at 20% of mpv bounds.
|
||||
- Visible/invisible overlays are constrained to the lower remaining region.
|
||||
- Secondary mode changes now directly control secondary window visibility.
|
||||
- Primary overlay layers no longer duplicate secondary subtitle rendering.
|
||||
|
||||
All acceptance criteria are complete and merged in commit `b8f7d5e`.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,47 +0,0 @@
|
||||
---
|
||||
id: TASK-111
|
||||
title: Remove Maintainability Guardrails docs section and related guardrail code
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-23 03:37'
|
||||
updated_date: '2026-02-23 03:40'
|
||||
labels:
|
||||
- docs
|
||||
- cleanup
|
||||
- guardrails
|
||||
dependencies: []
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
User requested removing the docs section labeled "Maintainability Guardrails" and deleting associated project code/commands for those guardrails so docs and scripts stay consistent.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Docs no longer contain the Maintainability Guardrails section shown in the request.
|
||||
- [x] #2 Commands and code associated specifically with that removed guardrails section are removed from scripts/config where applicable.
|
||||
- [x] #3 Project references remain consistent (no stale mentions of removed guardrails commands).
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Removed the `## Maintainability Guardrails` section from `docs/development.md` including strict command examples and troubleshooting bullets.
|
||||
|
||||
Removed guardrail scripts from `package.json`: `check:main-fanin`, `check:main-fanin:strict`, `check:runtime-cycles`, `check:runtime-cycles:strict`.
|
||||
|
||||
Deleted associated script code and fixtures: `scripts/check-main-runtime-fanin.ts`, `scripts/check-runtime-cycles.ts`, `scripts/check-runtime-cycles.test.ts`, and `scripts/fixtures/runtime-cycles/**`.
|
||||
|
||||
Removed CI fail-fast guardrail step from `.github/workflows/ci.yml` that invoked strict fan-in/runtime-cycle checks.
|
||||
|
||||
Validation: `bun run tsc --noEmit` and `bun run docs:build` passed.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Removed the Maintainability Guardrails docs section and fully removed the related fan-in/runtime-cycle guardrail implementation from scripts, package commands, CI wiring, and fixtures. The repository now has no active references to those guardrail commands in development docs, package scripts, or CI workflow.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,50 +0,0 @@
|
||||
---
|
||||
id: TASK-112
|
||||
title: Fix docs demo video thumbnail cache staleness
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-02-23 03:41'
|
||||
updated_date: '2026-02-23 03:43'
|
||||
labels:
|
||||
- bug
|
||||
- docs
|
||||
- media
|
||||
dependencies: []
|
||||
priority: medium
|
||||
ordinal: 112000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Docs home page demo continues to show an old thumbnail after replacing the demo video file in place. The page currently reuses static media URLs (`/assets/minecard.webm`, `/assets/minecard.mp4`, `/assets/demo-poster.jpg`), so browser/CDN cache can keep stale poster/video preview data.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Action Steps
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add a failing regression check that asserts cache-busted URL usage for docs home demo media references.
|
||||
2. Update docs home demo media URLs to include a shared asset version token for poster/source/fallback paths.
|
||||
3. Run the regression check and docs build validation.
|
||||
4. Record fix notes and move task status to Done.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Docs home video references include a deterministic cache-busting token so direct asset replacement does not serve stale preview data.
|
||||
- [x] #2 Poster/source/fallback links in the demo block use the same token to avoid mixed old/new media.
|
||||
- [x] #3 Validation commands for the new regression check and docs build pass.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
- Generated a fresh thumbnail from `docs/public/assets/minecard.webm` into `docs/public/assets/minecard-poster.jpg`.
|
||||
- Updated `docs/index.md` demo block to use a shared `demoAssetVersion` token across poster, webm/mp4 sources, and gif fallback links.
|
||||
- Follow-up: regenerated `minecard-poster.jpg` at exact `00:00:12` from the webm and bumped token to `demoAssetVersion = '20260223-2'`.
|
||||
- Added regression check `docs/index.assets.test.ts` to ensure demo media references keep shared cache-busting URL patterns.
|
||||
- Validation:
|
||||
- `bun test docs/index.assets.test.ts`
|
||||
- `bun run docs:build`
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -1,33 +0,0 @@
|
||||
---
|
||||
id: TASK-113
|
||||
title: Refresh architecture doc content to match current codebase
|
||||
status: Done
|
||||
assignee:
|
||||
- codex-architecture-doc-refresh
|
||||
created_date: '2026-02-22 19:39'
|
||||
updated_date: '2026-02-22 19:44'
|
||||
labels:
|
||||
- docs
|
||||
- architecture
|
||||
dependencies: []
|
||||
priority: low
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
User requested a thorough review of the current codebase and `docs/architecture.md`, then updating document content to reflect current implementation details. Mermaid/flow diagrams are explicitly out of scope for this pass.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Architecture content sections align with current source tree/module ownership.
|
||||
- [x] #2 Service/composer/runtime descriptions are updated for current behavior and module names.
|
||||
- [x] #3 Mermaid/flow chart sections remain unchanged in this pass.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Notes
|
||||
|
||||
- Reviewed current module ownership across `src/main.ts`, `src/main/runtime/*`, `src/core/services/*`, `src/config/*`, `src/renderer/*`, `launcher/*`, and `plugin/subminer.lua`.
|
||||
- Updated `docs/architecture.md` content sections only; Mermaid blocks intentionally left untouched.
|
||||
- Verification: `bun run docs:build` passed.
|
||||
@@ -1,35 +0,0 @@
|
||||
---
|
||||
id: TASK-114
|
||||
title: Refresh development doc content to match current codebase
|
||||
status: Done
|
||||
assignee:
|
||||
- codex-development-docs-review
|
||||
created_date: '2026-02-23 03:46'
|
||||
updated_date: '2026-02-23 03:49'
|
||||
labels:
|
||||
- docs
|
||||
- developer-experience
|
||||
dependencies: []
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
User requested a thorough review of the current codebase and `docs/development.md`, then updates so the page reflects current scripts, architecture, and contributor workflow.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 `docs/development.md` commands/tooling sections match current `package.json`, `Makefile`, and active CI lanes.
|
||||
- [x] #2 Runtime/composition guidance reflects current module ownership and does not reference removed guardrail steps.
|
||||
- [x] #3 Updated doc builds cleanly via `bun run docs:build`.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Notes
|
||||
|
||||
- Reviewed `docs/development.md` against current `package.json` scripts, `Makefile` targets, launcher/runtime env overrides, and `.github/workflows/ci.yml` gate order.
|
||||
- Updated setup instructions to use Bun for `vendor/texthooker-ui` and include submodule initialization.
|
||||
- Added CI-equivalent local test/build/docs command sequence and clarified that subtitle dist tests are currently placeholders.
|
||||
- Expanded environment-variable coverage (`SUBMINER_ROFI_THEME`, Jimaku/Jellyfin overrides, `SUBMINER_LOG_LEVEL`, `SUBMINER_MPV_LOG`, macOS helper skip flag).
|
||||
- Verification: `bun run docs:build` passed.
|
||||
@@ -1,61 +0,0 @@
|
||||
---
|
||||
id: TASK-115
|
||||
title: Migrate repository workflows to Bun-only JavaScript runtime
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-23 04:26'
|
||||
updated_date: '2026-02-23 04:42'
|
||||
labels:
|
||||
- tooling
|
||||
- build
|
||||
- ci
|
||||
dependencies: []
|
||||
references:
|
||||
- package.json
|
||||
- .github/workflows/ci.yml
|
||||
- .github/workflows/release.yml
|
||||
- docs/development.md
|
||||
- docs/installation.md
|
||||
- README.md
|
||||
documentation:
|
||||
- docs/plans/2026-02-23-bun-only-toolchain-migration.md
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Transition the project from mixed Bun/Node tooling to a Bun-only contributor and CI workflow so setup is simpler and runtime behavior is consistent across local and automation lanes.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 A contributor can install dependencies, build, run source tests, run dist smoke tests, and build docs using Bun without requiring a system Node.js install.
|
||||
- [x] #2 CI and release workflows no longer require Node setup steps for standard project verification and packaging jobs.
|
||||
- [x] #3 Dist and utility verification lanes remain covered and pass with Bun-based commands.
|
||||
- [x] #4 Repository documentation clearly states the Bun-only prerequisite and removes conflicting Node requirement guidance.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Execution started in-session after user selected Subagent-Driven option. Tracking sequence: TASK-115.2 -> TASK-115.3/TASK-115.4 -> TASK-115.1.
|
||||
|
||||
Completed child sequence TASK-115.2 -> TASK-115.3/TASK-115.4 -> TASK-115.1 with Bun-only command/workflow migration.
|
||||
|
||||
Direct Node command usage removed from package dist and utility script lanes; CI/release Node setup steps removed; contributor docs aligned to Bun-only prerequisites.
|
||||
|
||||
Regression hardening: refactored AniList token-store tests and storage seam to remain stable under Bun dist test execution while preserving runtime behavior.
|
||||
|
||||
Post-migration docs polish: updated `README.md` requirements table to list Bun as a required dependency and promoted Bun from optional-tools section into system dependencies in `docs/installation.md` to remove ambiguity.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Migrated repository verification and workflow surfaces to Bun-first execution by replacing `node --test` dist lanes and remaining direct Node utility invocation (`generate:config-example`) with Bun commands.
|
||||
|
||||
Removed `actions/setup-node` from CI and release jobs and aligned docs (`docs/development.md`, `docs/installation.md`) to Bun-only setup guidance; launcher smoke fixtures now use Bun shebangs so smoke tests no longer depend on Node.
|
||||
|
||||
Validated full gate set under Bun-only command path: launcher smoke, fast source suite, build, dist config/core/smoke suites, docs build, plus config-example generation command.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
id: TASK-115.1
|
||||
title: Remove Node setup from workflows and finalize Bun-only docs
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-23 04:26'
|
||||
updated_date: '2026-02-23 04:36'
|
||||
labels:
|
||||
- ci
|
||||
- docs
|
||||
- tooling
|
||||
dependencies:
|
||||
- TASK-115.2
|
||||
- TASK-115.3
|
||||
- TASK-115.4
|
||||
references:
|
||||
- .github/workflows/ci.yml
|
||||
- .github/workflows/release.yml
|
||||
- docs/development.md
|
||||
- docs/installation.md
|
||||
- README.md
|
||||
documentation:
|
||||
- docs/plans/2026-02-23-bun-only-toolchain-migration.md
|
||||
parent_task_id: TASK-115
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
After script/test migration parity is verified, remove Node setup from CI/release workflows and finalize documentation so contributors and automation use a consistent Bun-only prerequisite.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 CI and release workflows no longer include Node setup steps for routine build/test/package jobs.
|
||||
- [x] #2 Primary local verification gates pass with the Bun-only command set.
|
||||
- [x] #3 README and setup/install docs consistently describe Bun as the JS runtime prerequisite without conflicting Node requirement text.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Removed `actions/setup-node` from `.github/workflows/ci.yml` and `.github/workflows/release.yml` (quality-gate, build-linux, build-macos jobs).
|
||||
|
||||
Updated docs to align Bun-only prerequisite: removed Node from `docs/development.md` prerequisites and switched stale pnpm source-build snippet in `docs/installation.md` to Bun.
|
||||
|
||||
Validation gates: `bun run test:launcher:smoke:src && bun run test:fast && bun run build && bun run test:config:dist && bun run test:core:dist && bun run test:smoke:dist && bun run docs:build` all pass.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
id: TASK-115.2
|
||||
title: Map Node touchpoints and define Bun parity matrix
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-23 04:27'
|
||||
updated_date: '2026-02-23 04:36'
|
||||
labels:
|
||||
- tooling
|
||||
- planning
|
||||
dependencies: []
|
||||
references:
|
||||
- package.json
|
||||
- .github/workflows/ci.yml
|
||||
- .github/workflows/release.yml
|
||||
documentation:
|
||||
- docs/plans/2026-02-23-bun-only-toolchain-migration.md
|
||||
parent_task_id: TASK-115
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Document all Node-specific command and workflow touchpoints, define Bun replacements, and establish migration guardrails so follow-up implementation tasks have clear scope and risk controls.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 A complete inventory of Node-dependent scripts and workflow steps is documented with proposed Bun equivalents.
|
||||
- [x] #2 Migration risks and compatibility checkpoints are explicitly documented for dist and Electron-related lanes.
|
||||
- [x] #3 Follow-up implementation tasks have clear boundaries and ordering based on the parity matrix.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Created `docs/plans/bun-only-parity-matrix.md` with direct Node touchpoint inventory, Bun replacements, risk checkpoints, and migration sequencing.
|
||||
|
||||
Parity matrix now links implementation ordering for TASK-115.3/115.4/115.1 and flags follow-up risk handling for third-party implicit Node assumptions.
|
||||
<!-- SECTION:NOTES:END -->
|
||||