Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01fcacb6cf
|
||
|
|
cd7a65ec80
|
||
|
|
89479f7045
|
||
|
|
7bc9a07a52
|
||
|
|
68dd789fbf
|
||
|
|
516fadd530
|
||
|
|
18beac13f4
|
||
|
|
484a9e047d
|
||
|
|
0d66747f2e
|
||
|
|
b01f0bcb4f
|
||
|
|
9a1b6e650f
|
||
|
|
835a09fa67
|
||
|
|
2bcb6c7e98
|
||
|
|
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 | ||
|
|
1d1575f414
|
||
|
|
e11a5fea0d
|
||
|
|
f6993ae507
|
||
|
|
f73fe179d0
|
||
|
|
2938e7a32a | ||
|
|
4300517da7
|
||
|
|
82f6b4705a | ||
|
|
877f350353
|
||
|
|
7a1450ddb7
|
||
|
|
cc43d3007a
|
||
|
|
333ee5eea4
|
||
|
|
935e4c145f
|
||
|
|
183560d2c3
|
||
|
|
3ca7dcd664
|
||
|
|
51fc9034f7
|
||
|
|
8c22567da9
|
||
|
|
b22001f68e
|
||
|
|
8d55d64ee0
|
||
|
|
3dd0750b98
|
||
|
|
8aff2ae505
|
||
|
|
289c74da35
|
||
|
|
f5369b8b24
|
||
|
|
c94830531c
|
||
|
|
035ff8e7bf
|
||
|
|
4abbe8d567
|
||
|
|
feb8d5a55c
|
||
|
|
d1d128b3f2
|
||
|
|
2fbf27faff
|
||
|
|
2e54a3ac16
|
||
|
|
891c4a13d5
|
||
|
|
4739c3da78
|
||
|
|
ec66f59257
|
||
|
|
ec9da6bed3
|
||
|
|
7643855e61
|
||
|
|
f8a8235b7d
|
||
|
|
7220f6fc9b
|
||
|
|
c201520da3
|
||
|
|
a612d44535
|
||
|
|
ec48f90cd4
|
||
|
|
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
|
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"name": "subminer-local",
|
||||
"interface": {
|
||||
"displayName": "SubMiner Local"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"name": "subminer-workflow",
|
||||
"source": {
|
||||
"source": "local",
|
||||
"path": "./plugins/subminer-workflow"
|
||||
},
|
||||
"policy": {
|
||||
"installation": "AVAILABLE",
|
||||
"authentication": "ON_INSTALL"
|
||||
},
|
||||
"category": "Productivity"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,22 +1,45 @@
|
||||
---
|
||||
name: 'subminer-change-verification'
|
||||
description: 'Compatibility shim. Canonical SubMiner change verification workflow now lives in the repo-local subminer-workflow plugin.'
|
||||
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.
|
||||
---
|
||||
|
||||
# Compatibility Shim
|
||||
# SubMiner Change Verification
|
||||
|
||||
Canonical source:
|
||||
Verify the behavior claimed by a change without running unrelated expensive checks by default.
|
||||
|
||||
- `plugins/subminer-workflow/skills/subminer-change-verification/SKILL.md`
|
||||
## Workflow
|
||||
|
||||
Canonical helper scripts:
|
||||
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.
|
||||
|
||||
- `plugins/subminer-workflow/skills/subminer-change-verification/scripts/classify_subminer_diff.sh`
|
||||
- `plugins/subminer-workflow/skills/subminer-change-verification/scripts/verify_subminer_change.sh`
|
||||
Do not use hidden wrapper commands. Verification commands are owned by `package.json` and the workflow documentation.
|
||||
|
||||
When this shim is invoked:
|
||||
## Lane Selection
|
||||
|
||||
1. Read the canonical plugin-owned skill.
|
||||
2. Follow the plugin-owned skill as the source of truth.
|
||||
3. Use the wrapper scripts in this shim directory only for compatibility with existing commands and docs.
|
||||
4. Do not duplicate workflow changes here; update the plugin-owned skill and scripts instead.
|
||||
- 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`
|
||||
- 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.
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
REPO_ROOT=$(cd "$SCRIPT_DIR/../../../.." && pwd)
|
||||
TARGET="$REPO_ROOT/plugins/subminer-workflow/skills/subminer-change-verification/scripts/classify_subminer_diff.sh"
|
||||
|
||||
if [[ ! -x "$TARGET" ]]; then
|
||||
echo "Missing canonical script: $TARGET" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exec "$TARGET" "$@"
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
REPO_ROOT=$(cd "$SCRIPT_DIR/../../../.." && pwd)
|
||||
TARGET="$REPO_ROOT/plugins/subminer-workflow/skills/subminer-change-verification/scripts/verify_subminer_change.sh"
|
||||
|
||||
if [[ ! -x "$TARGET" ]]; then
|
||||
echo "Missing canonical script: $TARGET" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exec "$TARGET" "$@"
|
||||
@@ -0,0 +1,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.
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
name: 'subminer-scrum-master'
|
||||
description: 'Compatibility shim. Canonical SubMiner scrum-master workflow now lives in the repo-local subminer-workflow plugin.'
|
||||
---
|
||||
|
||||
# Compatibility Shim
|
||||
|
||||
Canonical source:
|
||||
|
||||
- `plugins/subminer-workflow/skills/subminer-scrum-master/SKILL.md`
|
||||
|
||||
When this shim is invoked:
|
||||
|
||||
1. Read the canonical plugin-owned skill.
|
||||
2. Follow the plugin-owned skill as the source of truth.
|
||||
3. Do not duplicate workflow changes here; update the plugin-owned skill instead.
|
||||
|
||||
This shim exists so existing repo references and prompts keep resolving during the migration to the repo-local plugin workflow.
|
||||
@@ -0,0 +1,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
|
||||
@@ -1,3 +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
|
||||
|
||||
- [ ] Added a changelog fragment in `changes/`, or this PR is labeled `skip-changelog`
|
||||
- [ ] 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,98 +8,4 @@ on:
|
||||
|
||||
jobs:
|
||||
build-test-audit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.5
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.bun/install/cache
|
||||
node_modules
|
||||
stats/node_modules
|
||||
vendor/subminer-yomitan/node_modules
|
||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/subminer-yomitan/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-bun-
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
bun install --frozen-lockfile
|
||||
cd stats && bun install --frozen-lockfile
|
||||
|
||||
- name: Lint changelog fragments
|
||||
run: bun run changelog:lint
|
||||
|
||||
- name: Lint stats (formatting)
|
||||
run: bun run lint:stats
|
||||
|
||||
- name: Enforce pull request changelog fragments (`skip-changelog` label bypass)
|
||||
if: github.event_name == 'pull_request'
|
||||
run: bun run changelog:pr-check --base-ref "origin/${{ github.base_ref }}" --head-ref "HEAD" --labels "${{ join(github.event.pull_request.labels.*.name, ',') }}"
|
||||
|
||||
- name: Build (TypeScript check)
|
||||
# Keep explicit typecheck for fast fail before full build/bundle.
|
||||
run: bun run typecheck
|
||||
|
||||
- name: Verify generated config examples
|
||||
run: bun run verify:config-example
|
||||
|
||||
- name: Internal docs knowledge-base checks
|
||||
run: bun run test:docs:kb
|
||||
|
||||
- name: Test suite (source)
|
||||
run: bun run test:fast
|
||||
|
||||
- name: Coverage suite (maintained source lane)
|
||||
run: bun run test:coverage:src
|
||||
|
||||
- name: Upload coverage artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-test-src
|
||||
path: coverage/test-src/lcov.info
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Launcher smoke suite (source)
|
||||
run: bun run test:launcher:smoke:src
|
||||
|
||||
- name: Upload launcher smoke artifacts (on failure)
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: launcher-smoke
|
||||
path: .tmp/launcher-smoke/**
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: Build (bundle)
|
||||
run: bun run build
|
||||
|
||||
- name: Immersion SQLite verification
|
||||
run: bun run test:immersion:sqlite:dist
|
||||
|
||||
- name: Dist smoke suite
|
||||
run: bun run test:smoke:dist
|
||||
|
||||
- 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
|
||||
uses: ./.github/workflows/quality-gate.yml
|
||||
|
||||
@@ -32,9 +32,11 @@ jobs:
|
||||
- name: Guard stable docs tag shape
|
||||
id: tag_guard
|
||||
if: github.ref_type == 'tag'
|
||||
env:
|
||||
TAG_NAME: ${{ github.ref_name }}
|
||||
run: |
|
||||
if [[ ! "${{ github.ref_name }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "::notice::Skipping non-stable docs tag ${{ github.ref_name }}"
|
||||
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
|
||||
|
||||
@@ -12,83 +12,9 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
quality-gate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.5
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.bun/install/cache
|
||||
node_modules
|
||||
stats/node_modules
|
||||
vendor/subminer-yomitan/node_modules
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/subminer-yomitan/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.arch }}-bun-
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
bun install --frozen-lockfile
|
||||
cd stats && bun install --frozen-lockfile
|
||||
|
||||
- name: Lint stats (formatting)
|
||||
run: bun run lint:stats
|
||||
|
||||
- name: Build (TypeScript check)
|
||||
run: bun run typecheck
|
||||
|
||||
- name: Install Lua
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y lua5.4
|
||||
sudo ln -sf /usr/bin/lua5.4 /usr/local/bin/lua
|
||||
lua -v
|
||||
|
||||
- name: Test suite (source)
|
||||
run: bun run test:fast
|
||||
|
||||
- name: Environment suite
|
||||
run: bun run test:env
|
||||
|
||||
- name: Coverage suite (maintained source lane)
|
||||
run: bun run test:coverage:src
|
||||
|
||||
- name: Upload coverage artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-test-src
|
||||
path: coverage/test-src/lcov.info
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Launcher smoke suite (source)
|
||||
run: bun run test:launcher:smoke:src
|
||||
|
||||
- name: Upload launcher smoke artifacts (on failure)
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: launcher-smoke
|
||||
path: .tmp/launcher-smoke/**
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: Build (bundle)
|
||||
run: bun run build
|
||||
|
||||
- name: Immersion SQLite verification
|
||||
run: bun run test:immersion:sqlite:dist
|
||||
|
||||
- name: Dist smoke suite
|
||||
run: bun run test:smoke:dist
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/quality-gate.yml
|
||||
|
||||
build-linux:
|
||||
needs: [quality-gate]
|
||||
@@ -148,7 +74,7 @@ jobs:
|
||||
name: appimage
|
||||
path: |
|
||||
release/*.AppImage
|
||||
release/*.yml
|
||||
release/latest*.yml
|
||||
release/*.blockmap
|
||||
if-no-files-found: error
|
||||
|
||||
@@ -226,7 +152,7 @@ jobs:
|
||||
path: |
|
||||
release/*.dmg
|
||||
release/*.zip
|
||||
release/*.yml
|
||||
release/latest*.yml
|
||||
release/*.blockmap
|
||||
if-no-files-found: error
|
||||
|
||||
@@ -279,7 +205,7 @@ jobs:
|
||||
path: |
|
||||
release/*.exe
|
||||
release/*.zip
|
||||
release/*.yml
|
||||
release/latest*.yml
|
||||
release/*.blockmap
|
||||
if-no-files-found: error
|
||||
|
||||
@@ -348,12 +274,13 @@ jobs:
|
||||
config.example.jsonc \
|
||||
plugin/subminer \
|
||||
plugin/subminer.conf \
|
||||
assets/themes/subminer.rasi
|
||||
assets/themes/subminer.rasi \
|
||||
assets/thumbnailers/subminer-ffmpegthumbnailer.thumbnailer
|
||||
|
||||
- name: Generate checksums
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
files=(release/*.AppImage release/*.dmg release/*.exe release/*.zip release/*.tar.gz release/*.yml release/*.blockmap dist/launcher/subminer)
|
||||
files=(release/*.AppImage release/*.dmg release/*.exe release/*.zip release/*.tar.gz release/latest*.yml release/*.blockmap dist/launcher/subminer)
|
||||
if [ "${#files[@]}" -eq 0 ]; then
|
||||
echo "No release artifacts found for checksum generation."
|
||||
exit 1
|
||||
@@ -370,15 +297,22 @@ jobs:
|
||||
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
|
||||
|
||||
@@ -389,7 +323,7 @@ jobs:
|
||||
release/*.exe
|
||||
release/*.zip
|
||||
release/*.tar.gz
|
||||
release/*.yml
|
||||
release/latest*.yml
|
||||
release/*.blockmap
|
||||
release/SHA256SUMS.txt
|
||||
dist/launcher/subminer
|
||||
@@ -400,27 +334,27 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if gh release view "${{ steps.version.outputs.VERSION }}" >/dev/null 2>&1; then
|
||||
gh release edit "${{ steps.version.outputs.VERSION }}" \
|
||||
if gh release view "$RELEASE_VERSION" >/dev/null 2>&1; then
|
||||
gh release edit "$RELEASE_VERSION" \
|
||||
--draft \
|
||||
--prerelease \
|
||||
--title "${{ steps.version.outputs.VERSION }}" \
|
||||
--title "$RELEASE_VERSION" \
|
||||
--notes-file release/prerelease-notes.md
|
||||
else
|
||||
gh release create "${{ steps.version.outputs.VERSION }}" \
|
||||
gh release create "$RELEASE_VERSION" \
|
||||
--draft \
|
||||
--latest=false \
|
||||
--prerelease \
|
||||
--title "${{ steps.version.outputs.VERSION }}" \
|
||||
--title "$RELEASE_VERSION" \
|
||||
--notes-file release/prerelease-notes.md
|
||||
fi
|
||||
|
||||
for asset in "${artifacts[@]}"; do
|
||||
gh release upload "${{ steps.version.outputs.VERSION }}" "$asset" --clobber
|
||||
gh release upload "$RELEASE_VERSION" "$asset" --clobber
|
||||
done
|
||||
|
||||
gh release edit "${{ steps.version.outputs.VERSION }}" \
|
||||
gh release edit "$RELEASE_VERSION" \
|
||||
--draft=false \
|
||||
--prerelease \
|
||||
--title "${{ steps.version.outputs.VERSION }}" \
|
||||
--title "$RELEASE_VERSION" \
|
||||
--notes-file release/prerelease-notes.md
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
name: Quality Gate
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
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: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y lua5.4
|
||||
sudo ln -sf /usr/bin/lua5.4 /usr/local/bin/lua
|
||||
lua -v
|
||||
|
||||
- name: Test suite (source)
|
||||
run: bun run test:fast
|
||||
|
||||
- name: Environment suite
|
||||
run: bun run test:env
|
||||
|
||||
- name: Coverage suite (maintained source lane)
|
||||
run: bun run test:coverage:src
|
||||
|
||||
- name: Upload coverage artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-test-src
|
||||
path: coverage/test-src/lcov.info
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Stats UI tests
|
||||
run: bun run test:stats
|
||||
|
||||
- name: Launcher smoke suite (source)
|
||||
run: bun run test:launcher:smoke:src
|
||||
|
||||
- name: Upload launcher smoke artifacts (on failure)
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: launcher-smoke
|
||||
path: .tmp/launcher-smoke/**
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: Build (bundle)
|
||||
run: bun run build
|
||||
|
||||
- name: Immersion SQLite verification
|
||||
run: bun run test:immersion:sqlite:dist
|
||||
|
||||
- name: Dist smoke suite
|
||||
run: bun run test:smoke:dist
|
||||
|
||||
- name: Security audit
|
||||
run: bun audit --audit-level high
|
||||
|
||||
- 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
|
||||
@@ -13,73 +13,9 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
quality-gate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.5
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.bun/install/cache
|
||||
node_modules
|
||||
stats/node_modules
|
||||
vendor/subminer-yomitan/node_modules
|
||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/subminer-yomitan/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-bun-
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
bun install --frozen-lockfile
|
||||
cd stats && bun install --frozen-lockfile
|
||||
|
||||
- name: Lint stats (formatting)
|
||||
run: bun run lint:stats
|
||||
|
||||
- name: Build (TypeScript check)
|
||||
run: bun run typecheck
|
||||
|
||||
- name: Test suite (source)
|
||||
run: bun run test:fast
|
||||
|
||||
- name: Coverage suite (maintained source lane)
|
||||
run: bun run test:coverage:src
|
||||
|
||||
- name: Upload coverage artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-test-src
|
||||
path: coverage/test-src/lcov.info
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Launcher smoke suite (source)
|
||||
run: bun run test:launcher:smoke:src
|
||||
|
||||
- name: Upload launcher smoke artifacts (on failure)
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: launcher-smoke
|
||||
path: .tmp/launcher-smoke/**
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: Build (bundle)
|
||||
run: bun run build
|
||||
|
||||
- name: Immersion SQLite verification
|
||||
run: bun run test:immersion:sqlite:dist
|
||||
|
||||
- name: Dist smoke suite
|
||||
run: bun run test:smoke:dist
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/quality-gate.yml
|
||||
|
||||
build-linux:
|
||||
needs: [quality-gate]
|
||||
@@ -139,7 +75,7 @@ jobs:
|
||||
name: appimage
|
||||
path: |
|
||||
release/*.AppImage
|
||||
release/*.yml
|
||||
release/latest*.yml
|
||||
release/*.blockmap
|
||||
|
||||
build-macos:
|
||||
@@ -216,7 +152,7 @@ jobs:
|
||||
path: |
|
||||
release/*.dmg
|
||||
release/*.zip
|
||||
release/*.yml
|
||||
release/latest*.yml
|
||||
release/*.blockmap
|
||||
|
||||
build-windows:
|
||||
@@ -268,7 +204,7 @@ jobs:
|
||||
path: |
|
||||
release/*.exe
|
||||
release/*.zip
|
||||
release/*.yml
|
||||
release/latest*.yml
|
||||
release/*.blockmap
|
||||
if-no-files-found: error
|
||||
|
||||
@@ -337,12 +273,13 @@ jobs:
|
||||
config.example.jsonc \
|
||||
plugin/subminer \
|
||||
plugin/subminer.conf \
|
||||
assets/themes/subminer.rasi
|
||||
assets/themes/subminer.rasi \
|
||||
assets/thumbnailers/subminer-ffmpegthumbnailer.thumbnailer
|
||||
|
||||
- name: Generate checksums
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
files=(release/*.AppImage release/*.dmg release/*.exe release/*.zip release/*.tar.gz release/*.yml release/*.blockmap dist/launcher/subminer)
|
||||
files=(release/*.AppImage release/*.dmg release/*.exe release/*.zip release/*.tar.gz release/latest*.yml release/*.blockmap dist/launcher/subminer)
|
||||
if [ "${#files[@]}" -eq 0 ]; then
|
||||
echo "No release artifacts found for checksum generation."
|
||||
exit 1
|
||||
@@ -359,33 +296,40 @@ jobs:
|
||||
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Guard against pending changelog fragments
|
||||
env:
|
||||
RELEASE_VERSION: ${{ steps.version.outputs.VERSION }}
|
||||
run: |
|
||||
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 ${{ steps.version.outputs.VERSION }}' locally and commit the polished CHANGELOG.md before tagging. CI no longer auto-builds the changelog because the polish step requires the local 'claude' CLI."
|
||||
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
|
||||
run: bun run changelog:check --version "${{ steps.version.outputs.VERSION }}"
|
||||
env:
|
||||
RELEASE_VERSION: ${{ steps.version.outputs.VERSION }}
|
||||
run: bun run changelog:check --version "$RELEASE_VERSION"
|
||||
|
||||
- name: Generate release notes from changelog
|
||||
run: bun run changelog:release-notes --version "${{ steps.version.outputs.VERSION }}"
|
||||
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 "${{ steps.version.outputs.VERSION }}" >/dev/null 2>&1; then
|
||||
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 "${{ steps.version.outputs.VERSION }}" \
|
||||
gh release edit "$RELEASE_VERSION" \
|
||||
--draft=false \
|
||||
--title "${{ steps.version.outputs.VERSION }}" \
|
||||
--title "$RELEASE_VERSION" \
|
||||
--notes-file release/release-notes.md
|
||||
else
|
||||
gh release create "${{ steps.version.outputs.VERSION }}" \
|
||||
--title "${{ steps.version.outputs.VERSION }}" \
|
||||
gh release create "$RELEASE_VERSION" \
|
||||
--title "$RELEASE_VERSION" \
|
||||
--notes-file release/release-notes.md
|
||||
fi
|
||||
|
||||
@@ -396,7 +340,7 @@ jobs:
|
||||
release/*.exe
|
||||
release/*.zip
|
||||
release/*.tar.gz
|
||||
release/*.yml
|
||||
release/latest*.yml
|
||||
release/*.blockmap
|
||||
release/SHA256SUMS.txt
|
||||
dist/launcher/subminer
|
||||
@@ -408,7 +352,7 @@ jobs:
|
||||
fi
|
||||
|
||||
for asset in "${artifacts[@]}"; do
|
||||
gh release upload "${{ steps.version.outputs.VERSION }}" "$asset" --clobber
|
||||
gh release upload "$RELEASE_VERSION" "$asset" --clobber
|
||||
done
|
||||
|
||||
aur-publish:
|
||||
@@ -484,9 +428,10 @@ jobs:
|
||||
if: steps.aur_prereqs.outputs.skip != 'true' && steps.aur_ssh.outputs.skip != 'true' && steps.aur_clone.outputs.skip != 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
RELEASE_VERSION: ${{ steps.version.outputs.VERSION }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
version="${{ steps.version.outputs.VERSION }}"
|
||||
version="$RELEASE_VERSION"
|
||||
install -dm755 .tmp/aur-release-assets
|
||||
gh release download "$version" \
|
||||
--dir .tmp/aur-release-assets \
|
||||
@@ -496,15 +441,17 @@ jobs:
|
||||
|
||||
- name: Update AUR packaging metadata
|
||||
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_no_v="${{ steps.version.outputs.VERSION }}"
|
||||
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 "${{ steps.version.outputs.VERSION }}" \
|
||||
--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"
|
||||
@@ -514,6 +461,7 @@ jobs:
|
||||
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
|
||||
@@ -523,7 +471,7 @@ jobs:
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add PKGBUILD .SRCINFO
|
||||
git commit -m "Update to ${{ steps.version.outputs.VERSION }}"
|
||||
git commit -m "Update to $RELEASE_VERSION"
|
||||
|
||||
attempts=3
|
||||
for attempt in $(seq 1 "$attempts"); do
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
/package-lock.json
|
||||
|
||||
# Superpowers brainstorming
|
||||
.superpowers/
|
||||
@@ -16,6 +17,8 @@ coverage/
|
||||
|
||||
# Launcher build artifact (produced by make build-launcher)
|
||||
/subminer
|
||||
/main-entry.js
|
||||
/main-entry.js.map
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
@@ -46,6 +49,7 @@ tests/*
|
||||
!.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
|
||||
@@ -53,8 +57,13 @@ tests/*
|
||||
.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/
|
||||
|
||||
@@ -4,3 +4,4 @@ release
|
||||
coverage
|
||||
vendor
|
||||
*.log
|
||||
src/core/services/tokenizer/__fixtures__/golden/*.json
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# AGENTS.MD
|
||||
# AGENTS.md
|
||||
|
||||
## Internal Docs
|
||||
|
||||
@@ -13,6 +13,8 @@ Start here, then leave this file.
|
||||
|
||||
`docs-site/` is user-facing. Do not treat it as the canonical internal source of truth.
|
||||
|
||||
`CLAUDE.md` is a symlink to this file; there is one project instruction file, not two.
|
||||
|
||||
## Quick Start
|
||||
|
||||
- Init workspace: `git submodule update --init --recursive`
|
||||
@@ -23,8 +25,9 @@ Start here, then leave this file.
|
||||
|
||||
## Build / Test
|
||||
|
||||
- Runtime/package manager: Bun (`packageManager: bun@1.3.5`)
|
||||
- Default handoff gate:
|
||||
- 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`
|
||||
@@ -40,7 +43,26 @@ Start here, then leave this file.
|
||||
- Config/schema/defaults: `bun run test:config`; if template/defaults changed, `bun run generate:config-example`
|
||||
- Launcher/plugin: `bun run test:launcher` or `bun run test:env`
|
||||
- Runtime-compat / dist-sensitive: `bun run test:runtime:compat`
|
||||
- Docs-only: `bun run docs:test`, then `bun run docs:build`
|
||||
- Stats dashboard UI (`stats/`): `bun run test:stats`
|
||||
- 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: `bun test scripts/get-mpv-window-macos.test.ts`
|
||||
- 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
|
||||
|
||||
@@ -52,15 +74,10 @@ Start here, then leave this file.
|
||||
|
||||
## Release / PR Notes
|
||||
|
||||
- User-visible PRs need one fragment in `changes/*.md`
|
||||
- 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,url --jq '"PR #\\(.number): \\(.title)\\n\\(.url)"'`
|
||||
- `gh pr view --json number,title --jq '"PR #\\(.number): \\(.title)"'`
|
||||
- `gh api repos/:owner/:repo/pulls/<num>/comments --paginate`
|
||||
|
||||
## Runtime Notes
|
||||
|
||||
- Use Codex background for long jobs; tmux only when persistence/interaction is required
|
||||
- CI red: `gh run list/view`, rerun, fix, repeat until green
|
||||
- TypeScript: keep files small; follow existing patterns
|
||||
- Swift: use workspace helper/daemon; validate `swift build` + tests
|
||||
- For CI debugging, inspect runs with `gh run list/view`; rerun or fix only within the requested scope.
|
||||
|
||||
@@ -1,10 +1,461 @@
|
||||
# Changelog
|
||||
|
||||
## v0.19.5 (2026-08-30)
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Anki Card Update Progress**: The card-update spinner now stays visible until audio and image updates finish, instead of disappearing early.
|
||||
- **Anki Word-Card Fields**: Word-card enrichment now writes sentence text and audio to the fields configured in AnkiConnect, while the dedicated sentence-card and audio-card actions keep their existing compatible field names.
|
||||
- **Overlapping Subtitles**:
|
||||
- Subtitle lines that start while another line is still on screen now appear alongside it, instead of staying hidden until a track switch or seek.
|
||||
- Subtitles shown at the same time now stack by their authored screen position, with top signs and song lines above bottom dialogue.
|
||||
- Half-size ASS furigana is no longer shown as if it were a dialogue line.
|
||||
- **YouTube Auto Captions**:
|
||||
- Auto-generated captions now follow their intended timing and two-row roll-up layout.
|
||||
- Long speech is paged instead of covering the video with a wall of text.
|
||||
- Explicitly timed sound cues like `[音楽]` no longer cover later dialogue.
|
||||
|
||||
## v0.19.4 (2026-08-25)
|
||||
|
||||
### Added
|
||||
- **Library Merge & Move**: Duplicate library cards for the same show can now be combined. Select cards in the library grid and use "Merge Selected" to pick which entry to keep and move every episode onto it, preserving sessions, mined cards, and watch time. Episodes can also be reassigned individually via the "→" button, useful when a file lands under a stray title; manual assignments survive later filename parsing, Jellyfin refreshes, and season repair. Exact AniList title matches with compatible seasons now merge automatically, while fuzzy matches surface as dismissible "Possible duplicate" reviews instead of merging silently.
|
||||
- **Duplicate Line Cleanup Tool**: The Vocabulary tab's new "Duplicates" button scans a chosen time window (7 days through all time) for old karaoke/typeset duplicate-line bursts, shows what it found, and collapses each run to one line once confirmed; `subminer stats cleanup --duplicate-lines` does the same from the terminal, with `--dry-run` and `--lookback-days <n>` options. Watch time and lines-seen totals are left unchanged.
|
||||
|
||||
### Changed
|
||||
- **Prerelease Release Notes**: Prerelease notes now open with a "Changes since" section listing only what changed versus the previous beta/RC of the same version, above the cumulative highlights, and CI rejects prerelease tags whose committed notes were generated for a different beta/RC.
|
||||
|
||||
### Fixed
|
||||
- **Subtitle & Karaoke Duplication**:
|
||||
- Karaoke and animated signs are reconstructed once from their authored text and shown only while actually sung, with original word spacing preserved, instead of flooding the overlay, subtitle sidebar, immersion history, mining, or stats with glyph fragments, per-frame color phases, and repeated animation events.
|
||||
- Decorative layers (highlight sweeps, glow/shadow copies, symbol-font decoration, particle swarms, hidden or zero-scaled text) stay out of published text, while ordinary repeated dialogue, positioned signs, wrapped lyric rows, and multi-row CC-style blocks still display correctly.
|
||||
- Embedded subtitle tracks on network-mounted (SMB/NFS) media are extracted and parsed again instead of falling back to live-text-only, restoring karaoke reconstruction, sidebar cues, and mining for releases that only ship subtitles inside the container.
|
||||
- Secondary subtitles go through the same deduplication pipeline as primary subtitles and no longer clip display after about four lines.
|
||||
- Event-heavy karaoke files that previously stalled subtitle loading for several seconds now parse in well under a second.
|
||||
- **Character Dictionary Reliability**:
|
||||
- Generation, merged rebuilds, and imports no longer freeze the app on large dictionaries; snapshot I/O, archive building, and image/name lookup caches moved off the UI's critical path.
|
||||
- Dictionaries are reused instead of regenerated when MeCab finds no name splits.
|
||||
- Cached portraits restore correctly after the portrait index finishes loading post-tokenization.
|
||||
- Desktop progress notifications on Linux AppImage installs update in place instead of flickering, fixing a bug where the AppImage's bundled libraries broke the system notification helper.
|
||||
- **Overlay Startup & Modals**:
|
||||
- The macOS window-tracking helper targets macOS 12.0+ instead of requiring the build machine's exact macOS version, fixing crashes on older systems like Ventura that left the overlay stuck on "Overlay loading".
|
||||
- mpv IPC connection attempts time out and retry, showing an actionable error if content still isn't ready after 30 seconds.
|
||||
- Dedicated overlay modals are prewarmed on macOS and Windows so shortcuts open them promptly.
|
||||
- On macOS, reused modals and the stats window open above fullscreen mpv on its current Space instead of jumping to another desktop.
|
||||
- **Wayland File Drop**: Fixed native Wayland drag-and-drop from file managers such as Thunar, so subtitle and video files dropped on the visible overlay are resolved and forwarded to mpv.
|
||||
- **Windows Mouse Lag**: Fixed system-wide mouse lag on Windows while SubMiner is running, caused by the overlay's global mouse hook for click-through forwarding and by the mpv window tracker blocking the app on repeated PowerShell lookups.
|
||||
- **Sentence Mining Audio & Clips**: Sentence-audio generation no longer times out on slow network-mounted media with many subtitle/font streams (bounded FFmpeg probing, two-minute extraction budget, clearer error reporting), and mined audio/animated AVIF clips now capture the subtitle line that was actually mined by snapshotting the clip range at lookup time instead of reading live mpv state later.
|
||||
- **Stats Performance & Reliability**: Immersion stats storage now sets its SQLite busy timeout before WAL setup, avoiding transient lock errors under concurrent writes. Deletes in the stats dashboard no longer freeze the UI, run proportional to what's deleted instead of rebuilding full lifetime summaries, retry safely if the delete worker crashes, and no longer rescan the whole library when deleting very common words; a new index also makes large session deletes drop from minutes to milliseconds. Library merges, video moves, and AniList reassignments got the same lifetime-summary fix.
|
||||
- **Vocabulary Stats Accuracy**: Vocabulary totals and charts now count all tracked vocabulary instead of only the first page, new-word history uses corrected daily rollups (fixing legacy timestamp and time-zone issues), summary cards refresh automatically after edits to the exclusion list, and rapid exclusion edits no longer race each other.
|
||||
- **Rofi MKV Thumbnails**: Fixed missing MKV thumbnails in the Linux rofi picker when system thumbnailer registrations only advertise legacy Matroska MIME aliases.
|
||||
|
||||
<details>
|
||||
<summary>Internal changes</summary>
|
||||
|
||||
### Internal
|
||||
- Docs Site Indexing: Excluded the `/main/` and `/v/<version>/` docs trees from search indexing (self-referential canonical, `noindex,follow`, matching `X-Robots-Tag`) so crawlers focus on current docs instead of ~30 archived copies of every page, and restored `<lastmod>` dates in the docs sitemap that were silently dropped by production builds.
|
||||
|
||||
</details>
|
||||
|
||||
## v0.19.3 (2026-08-13)
|
||||
|
||||
### Added
|
||||
- Changelog Modal: Adds an in-app changelog you can open from the tray ("View Changelog") or the "What's New" button on the update notification, so the notification stays reachable while you read. It shows the newest published release notes (falling back to the bundled changelog if that fetch fails), folds older versions while keeping the current one expanded, and supports keyboard navigation (`J`/`K`/arrows, `Enter`, `R`, `Esc`).
|
||||
|
||||
### Changed
|
||||
- Subtitle Tokenization Performance: Reworks subtitle dictionary lookups to cut per-line work roughly in half, cache repeated lookups across lines, and stop tokenization from competing with on-screen subtitle prefetching. Also fixes several accuracy issues along the way: dropped readings on trailing kana, character names being skipped after a dictionary sync, annotations not refreshing after mining a card, and halfwidth katakana character names losing their reading or being swallowed by other words.
|
||||
|
||||
### Fixed
|
||||
- Character Dictionary Large Imports: Large character dictionaries (e.g. One Piece) no longer fail to install from a fixed timeout budget; the import now scales its time budget to dictionary size and reports detailed progress (page/character counts, image download progress, elapsed time) instead of one static message.
|
||||
- Stats Delete Responsiveness: Deleting sessions, episodes, or library entries no longer freezes the stats page or an active video player; deletes are now batched into a single transaction.
|
||||
- Styled Subtitle Cue Parsing: Heavily typeset subtitles (karaoke, signs) no longer flood the subtitle sidebar with garbage; vector drawing commands are no longer shown as text, and duplicate/animation-burst cues now collapse into one.
|
||||
- X11 mpv Renderer: Fixes an mpv crash on the first fullscreen toggle for X11/XWayland users with `gpu-next` shaders (e.g. ArtCNN), which was caused by X11 mode forcing the legacy OpenGL renderer.
|
||||
- X11 Overlay Display Scaling: Fixes the overlay appearing oversized and offset from mpv on X11/XWayland under fractional or mixed-monitor display scaling.
|
||||
|
||||
<details>
|
||||
<summary>Internal changes</summary>
|
||||
|
||||
### Internal
|
||||
- Subtitle text is now decoded from ASS exactly once at ingest, so the renderer, timing tracker, and tokenizer all share one decoded value instead of each re-deriving it.
|
||||
- Added per-stage debug timings (`scanMs`, `mecabMs`, `frequencyMs`, `annotateMs`) to the subtitle tokenization pipeline log.
|
||||
|
||||
</details>
|
||||
|
||||
## v0.19.2 (2026-08-04)
|
||||
|
||||
### Changed
|
||||
- Subsync: The sync modal now lets you choose both the reference subtitle (correct timing) and the out-of-sync subtitle to retime, for both alass and ffsubsync. alass can also use the loaded video's audio as a reference for local files. Retiming the secondary track now reloads the result into the secondary slot instead of overwriting the primary subtitle.
|
||||
|
||||
### Fixed
|
||||
- Streaming Subtitle Tokenization: Jellyfin streams now seed subtitle tokenization directly from the downloaded subtitle file instead of relying on an mpv event that could be missed, and prefetching now runs to the end of the file and clears between episodes. The tokenization cache was raised from 256 to 2500 lines, and parsed cues are no longer lost when the active subtitle track briefly can't be resolved (e.g. switching to an embedded track). Together these prevent episodes from falling back to slow, line-by-line tokenization during playback.
|
||||
- Overlay: Subtitle lines now appear immediately at their cue time even on a tokenization cache miss, upgrading in place once tokens and annotations are ready, instead of waiting on a line still being processed. A failed tokenization is no longer cached as plain text, so repeated lines get another chance at annotations.
|
||||
- Background Logging: Background startup now respects the configured logging level when no explicit log level is passed.
|
||||
|
||||
<details>
|
||||
<summary>Internal changes</summary>
|
||||
|
||||
### Internal
|
||||
- Patched three high-severity dependency advisories (`undici`, `brace-expansion`, `fast-uri`).
|
||||
|
||||
</details>
|
||||
|
||||
## v0.19.1 (2026-08-01)
|
||||
|
||||
### Added
|
||||
- Word Card Type: Adds a setting (Settings > Mining/Anki > Kiku/Lapis Features > "Word Card Type") to choose which card-type flag SubMiner marks on Kiku/Lapis word cards — `word-and-sentence` (default), `click`, `sentence`, `audio`, or `none`. Click cards (`IsClickCard`) can now be flagged, and setting any card-type flag clears the others so a note can't claim two types at once.
|
||||
|
||||
### Fixed
|
||||
- Yomitan Popup: Fixes the macOS Yomitan popup going inert after mining a card — clicks outside the popup no longer pass through to mpv, and scrolling over the popup scrolls its definitions instead of seeking playback.
|
||||
- YouTube Playlist Links: Fixes opening a video from a playlist URL (e.g. a Watch Later link with `list=`/`index=`) timing out while probing subtitles, metadata, or the playback URL.
|
||||
|
||||
## v0.19.0 (2026-07-29)
|
||||
|
||||
### Added
|
||||
- Anki Maturity Highlighting: Known-word subtitle highlights can now be colored by Anki card maturity (new, learning, young, mature), similar to asbplayer. Tier thresholds and colors are configurable, with a runtime toggle and an updated help legend.
|
||||
- Post-Playback Menu: After a watch-history episode ends, the fzf/rofi launcher returns to that series with options to play the previous or next episode, rewatch, pick another episode, or quit. The pre-playback series menu now offers the previous episode too.
|
||||
- Delete Library Entries: The stats Library detail view can now delete an entire title in one step (episodes, sessions, subtitle lines, rollups, cover art, and vocabulary counts). Delete progress is now shown app-wide via a progress bar and status toast instead of disappearing when you switch tabs.
|
||||
- Cross-Machine Sync: Added SSH-based syncing of stats and watch history between machines, available from the tray ("Sync Stats & History") or `subminer sync`, with saved devices, per-host sync direction, background auto-sync, connection testing, manual snapshots, and support for Windows remotes.
|
||||
- TsukiHime Subtitle Downloads: Added subtitle downloads for the current video via TsukiHime, loading Japanese as the primary track and your configured secondary language directly into mpv.
|
||||
|
||||
### Changed
|
||||
- Clipboard-Video Shortcut: The "append clipboard video to queue" shortcut is now configurable.
|
||||
|
||||
### Fixed
|
||||
- AniList Season Resolution: Season 2+ files now resolve to the correct AniList entry instead of silently falling back to season 1 (which mismatched character dictionaries and watch progress). Manual overrides now stay scoped per season, fix both the dictionary and progress tracking together, and also correct per-season cover art.
|
||||
- Subtitle Annotation Accuracy: Fixed several annotation edge cases, including inconsistent POS exclusions on merged quote-particle tokens, dropped annotations on supplementary-plane kanji, katakana punctuation wrongly treated as noise, and certain kanji vocabulary losing N+1 highlighting eligibility.
|
||||
- AnkiConnect Proxy Port Conflict: Video startup no longer crashes when another process already holds the configured AnkiConnect proxy port; a notification now explains how to resolve it.
|
||||
- AppImage Quit Crash: Fixed a "Service Crash" desktop notification appearing after closing a video when running the Linux AppImage.
|
||||
- Autoplay Pause Timing: Fixed playback resuming a few seconds before subtitle tokenization warmup finished, most noticeable when resuming mid-episode or when a cue starts within the first two seconds.
|
||||
- Stats Known-Word Count: Fixed stats reporting 0 known words for every session after the known-word cache format changed.
|
||||
- Stats Library Cover Art: Relinking a title to a different AniList entry now updates its cover in the Library grid immediately instead of leaving a stale, mismatched cover cached.
|
||||
- Rofi Prompt Spacing: Rofi menu prompts now keep a space before the input field instead of running into the placeholder text.
|
||||
- Stats Settings & Reliability: Hardened stats settings validation (nested/legacy AnkiConnect config now falls back safely instead of breaking) and stats routes against malformed requests and other edge cases.
|
||||
- Stats Delete Performance: Deleting sessions, episodes, and library entries is now dramatically faster and no longer stalls playback (e.g. a 12-episode title dropped from about a minute to under a second on a large library); the Vocabulary tab also loads much faster. The first launch after upgrading runs a one-time database migration.
|
||||
|
||||
<details>
|
||||
<summary>Internal changes</summary>
|
||||
|
||||
### Internal
|
||||
- Added a golden-file regression test corpus for the tokenizer/annotation pipeline, plus scripts to record new fixtures and diff against stock Yomitan.
|
||||
- Consolidated renderer modal state handling into a descriptor registry.
|
||||
- Consolidated CI quality checks (PR, stable, and prerelease) into one reusable workflow with mpv plugin tests and dependency audits.
|
||||
- Removed the unused stats IPC transport and unified stats dashboard HTTP types with the backend contract.
|
||||
- Added a script to verify known-word highlight tiers against live Anki data outside of playback.
|
||||
|
||||
</details>
|
||||
|
||||
## v0.18.0 (2026-07-10)
|
||||
|
||||
### Added
|
||||
- Sentence Audio Normalization: Generated sentence audio is now normalized to -23 LUFS by default, and clips mined from playback mirror mpv's software volume curve with a limiter to prevent clipping. Both behaviors are configurable independently.
|
||||
- Watch History Command: Added `subminer -H` / `--history` to browse watch history, replay or continue episodes, or pick one via fzf or rofi, with cover art shown in the rofi picker.
|
||||
|
||||
### Changed
|
||||
- Fzf Preview Layout: Moved fzf previews below launcher menus, giving long titles and metadata more room.
|
||||
- Known-Word Highlighting: Now compares subtitle and Anki-card readings, preventing false matches between homographs and unrelated words that share a reading, while still supporting matching across kana and kanji spellings.
|
||||
- Annotation Filtering: Standalone suffix tokens (e.g. さん, れる) are now excluded from JLPT/frequency/N+1 highlighting by default, matching how particles and interjections are treated; configurable via the pos2 exclusion setting.
|
||||
- App Icon: Replaced the app icon with new pixel-art submarine artwork contributed by the community, used across the app icon, tray, notifications, README, docs site, and Stats page.
|
||||
- Stats Trend Charts: Overhauled with persisted title visibility, per-chart title limits, "top" and "most recent" ranking modes, an option to show or hide empty days, calendar-aligned periods, and value-sorted tooltips.
|
||||
|
||||
### Fixed
|
||||
- Background Stats Server: `subminer app` background launches now auto-start the stats server when enabled, and skip startup if one is already running.
|
||||
- Character Name Highlighting: Character dictionaries now split unspaced native names more reliably, and portraits, highlights, and hover lookup survive punctuation, unmatched text, and competing dictionary matches without incorrectly splitting longer words.
|
||||
- Highlighting Coverage: Frequency/JLPT highlighting and vocabulary stats now include content adverbs (e.g. 確かに, やはり) and kanji nouns MeCab tags as non-independent (e.g. 日, 点, 以外), while still suppressing interjections, pronouns, and grammar fragments; lexicalized kana expressions like かといって keep their annotations.
|
||||
- Cover Art Fetching: Stats now fetches AniList cover art as soon as a new series starts playing, and backfills missing art for existing series on the next Stats visit.
|
||||
- Kiku Field Grouping: The manual field-grouping dialog now stays above fullscreen mpv, remains usable across repeated attempts, closes abandoned windows after timeouts, and reports a clear error when the original card can no longer be loaded.
|
||||
- Karaoke Subtitle Collapse: Secondary subtitles no longer stack dozens of one-syllable lines during openings and endings; repeated events collapse into one line, capped to a strip at the top.
|
||||
- Unparsed Token Hover: Subtitle text Yomitan can't parse (truncated inflections, elongation runs) remains hoverable, while staying excluded from highlighting, N+1 candidate math, and vocabulary stats.
|
||||
- YouTube Streaming: Fixed direct YouTube stream extraction that could corrupt signed URLs and cause ffmpeg 403 errors.
|
||||
|
||||
<details>
|
||||
<summary>Internal changes</summary>
|
||||
|
||||
### Internal
|
||||
- Test lanes moved to `scripts/test-lanes.ts` with per-directory discovery and isolated per-file timeouts; CI now covers previously orphaned stats, scripts, plugin process-retry, and runtime-compat suites, plus a new stats lane in the change-verification workflow.
|
||||
|
||||
</details>
|
||||
|
||||
## v0.17.2 (2026-06-28)
|
||||
|
||||
### Fixed
|
||||
- YouTube Background Cache: Fixed Windows YouTube background media cache startup for YouTube URLs opened directly in mpv, including resolved stream URLs when mpv still exposes the original YouTube playlist entry, so queued Anki media updates can append audio and images after the cache finishes.
|
||||
- YouTube Subtitle Picker: Manual subtitle picker requests now show an immediate configured notification while SubMiner probes tracks and opens the modal. Subtitle download progress is replaced with a transient success notification after tracks load.
|
||||
|
||||
## v0.17.1 (2026-06-27)
|
||||
|
||||
### Added
|
||||
- YouTube Media Cache Mode: Adds `youtube.mediaCache.mode` with `direct` and `background` options. Background mode uses a yt-dlp cache download when direct stream extraction is unreliable — creates a text-only card immediately, queues media updates for mined notes, and fills audio/image fields once the download finishes. Progress is announced via overlay/OSD notifications. Downloads are capped at 720p by default (`youtube.mediaCache.maxHeight`). Switching back to direct mode cancels any in-flight background download.
|
||||
|
||||
### Fixed
|
||||
- Log Export: Fixed log filenames to use the local date so exports around UTC midnight include the current day's logs rather than stale prior-day files. Expanded export redaction to mask IPs, emails, auth and cookie headers, yt-dlp cookie arguments, URL credentials, token/key/password fields, and signed YouTube media URL parameters.
|
||||
- YouTube Card Media: Improved media generation reliability by sending safer ffmpeg options for resolved streams and skipping stale stream maps (including cached YouTube files). Hardened background cache downloads with IPv4 and extractor retry flags; failed downloads now notify the user and clear queued media updates instead of leaving them silently pending. Stale background cache files are cleaned on startup and before each new download.
|
||||
|
||||
## v0.17.0 (2026-06-15)
|
||||
|
||||
### Changed
|
||||
|
||||
- **Subtitle Delay Keybindings**: Updated default overlay subtitle delay and step bindings to match mpv conventions: `z`, `Z`, and `x` adjust `sub-delay`; `Ctrl+Shift+Left/Right` run native `sub-step` with OSD feedback. The previous SubMiner-only adjacent-cue delay action has been removed.
|
||||
- **Update Notifications**: New installs now default update notifications to overlay-only instead of overlay + system notifications.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Anki – Highlight Word**: Fixed bolding of the mined word in Kiku sentence and sentence-furigana fields when the source Yomitan sentence did not already contain bold markup.
|
||||
- **Anki – Lapis/Kiku Word Cards**: Fixed word-and-sentence marker missing from Lapis/Kiku word cards enriched through SubMiner, which could hide sentence context on the card front.
|
||||
- **Anki – Windows Media Generation**: Fixed known-word cache refreshes when no deck is configured, and fixed audio/image generation after background launches by recreating missing FFmpeg temp directories before clipping.
|
||||
- **Character Dictionary – Windows**: Fixed the Windows "SubMiner mpv" shortcut so character dictionary auto-sync can fall back to mpv's current video path when app media state is not yet ready.
|
||||
- **Notifications**: Restored the SubMiner app icon on system notifications that do not supply a custom notification image.
|
||||
- **Overlay – macOS Yomitan Popup**: Fixed Yomitan popup focus after card mining or popup reload; fixed popups staying open when clicking transparent overlay space — click-away now closes the popup and returns click passthrough to mpv without a hide/reappear cycle.
|
||||
- **Overlay – Linux Auto-Pause Startup**: Fixed the visible overlay on Linux auto-paused startup to stay interactive during the initial measurement gap; startup subtitle cache misses now paint raw text before tokenization finishes, and temporarily empty `sub-text` refreshes are resolved before warm readiness resumes playback.
|
||||
- **Overlay – Playlist Advance**: Fixed the visible overlay being dismissed when mpv advances to the next playlist item, including when the next episode loads after the warm transition delay.
|
||||
- **Overlay – Windows Subtitle Bar**: Fixed shaky hover and click interaction on the Windows subtitle bar when a video attaches to an already-running background SubMiner instance.
|
||||
- **Stats – AniList Linking**: Fixed manual AniList linking from the stats anime page so auto-searches strip the generated "Season N" suffix and query only the anime title.
|
||||
- **Updates – Linux Support Assets**: Fixed Linux updates to correctly install and refresh the launcher runtime plugin copy and rofi theme alongside AppImage and launcher updates; unrelated SubMiner data directories are now left untouched and plugin copies are staged before replacing the live runtime. Fixed first-launch playback on fresh installs by auto-installing missing managed support assets from the bundled app before mpv starts.
|
||||
|
||||
### Docs
|
||||
|
||||
- **Linux Update Flows**: Documented that Linux update flows manage the launcher runtime plugin copy and rofi theme from `subminer-assets.tar.gz`, and that normal launcher playback auto-installs those managed support assets on a fresh install if either is missing.
|
||||
|
||||
<details>
|
||||
<summary>Internal changes</summary>
|
||||
|
||||
### Internal
|
||||
|
||||
- **Runtime Modules**: Split main-process runtime wiring into focused modules without changing user-facing behavior; hardened helpers against stale background stats daemon PIDs, stalled subtitle extraction, and dropped async errors.
|
||||
- **Release CI**: Fixed GitHub release notes to preserve the `What's Changed` and `New Contributors` attribution sections when CI regenerates from the committed changelog; scoped prerelease note reuse to the same base version so a new beta line starts from current fragments.
|
||||
|
||||
</details>
|
||||
|
||||
## v0.16.0 (2026-06-10)
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- **Notification Mode `both`**: `notificationType: "both"` now routes to overlay + system notifications. Users who previously used `"both"` for mpv OSD + system notifications should set `notificationType` to `"osd-system"` in `config.jsonc`. The `osd` and `osd-system` values remain valid as config-file entries but no longer appear in Settings.
|
||||
|
||||
### Added
|
||||
|
||||
- **Overlay Notifications**: New overlay notification stack (Catppuccin Macchiato theme) with configurable screen position (`notifications.overlayPosition`: top-left, top-center, top-right), 3-second transient dismissals, and persistent cards for long-running jobs like character dictionary sync.
|
||||
- **Notification History Panel**: `Ctrl/Cmd+N` (configurable via `shortcuts.toggleNotificationHistory`) opens a session log of all notifications. Works whether the overlay or mpv has focus; slides in from the notification edge; entries can be individually removed or cleared.
|
||||
- **Mined-Card Notification Actions**: Mined-card overlay notifications show generated card thumbnails and include an Open in Anki button in both live cards and history entries.
|
||||
- **Update Notification Action**: Update-available overlay notifications include an Update button to start the app update flow directly from the notification.
|
||||
- **Stats Search**: New Search tab in Stats for realtime subtitle sentence search with media context, headword matching, and mining actions for source-backed sentence cards or exact-match word/audio cards.
|
||||
|
||||
### Changed
|
||||
|
||||
- **AniSkip**: Moved intro detection from the mpv plugin to the SubMiner app. Lookups now cover every file loaded during a session including playlist advances, and `mpv.aniskipEnabled`/`mpv.aniskipButtonKey` hot-reload without restarting playback. The bundled plugin no longer makes network calls. Note: AniSkip now requires the SubMiner app to be connected; plugin-only mpv sessions will not fetch skip windows.
|
||||
- **Stats Library**: Entries are now split by detected season (season folder first, filename parsing as fallback). Existing combined-series rows are automatically migrated to per-season entries on startup. Cover art and anime details refresh immediately after a manual AniList entry change.
|
||||
- **Stats Vocabulary**: Remembers Hide Known/Hide Kana filters across sessions, applies Hide Kana filtering cross-title, collapses duplicate token variants in exclusions, and matches Related Seen Words by shared readings or kanji.
|
||||
- **Stats Trends**: Reorganized into Activity, Cumulative Totals, Efficiency, Patterns, and Library sections; disambiguated per-period vs. cumulative charts; added Words/Min and Cards/Hour efficiency charts.
|
||||
- **Stats Mining**: Sentence cards are created before slow media generation finishes; stored/requested secondary subtitles are preserved before falling back to sidecar or alass-retimed English subtitles; empty `ankiConnect.deck` falls back to Yomitan's mining deck; partial media failures are surfaced.
|
||||
- **Stats Browsing**: Remembers library card size; retries stored cover art without extra AniList lookups; preserves PNG/WebP MIME types; honors custom AnkiConnect URLs for Browse; shows progress during session deletes.
|
||||
- **Startup Notifications**: Tokenization, subtitle annotation, and character dictionary status now route through queued overlay notifications in `overlay`/`both` mode instead of falling back to mpv OSD while the overlay loads.
|
||||
- **Notification Deduplication**: Cycling subtitle modes updates the active overlay card in place rather than stacking duplicates; repeated progress updates (e.g. subsync) tick in place without flickering.
|
||||
- **Update Notification Default**: New installs default `notificationType` to `both` so update alerts appear in both overlay and system notifications.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **AniList Completion**: Entries are now marked completed when a post-watch update reaches the final known episode of the season.
|
||||
- **AniSkip Markers**: Fixed intro markers disappearing after same-media mpv reloads; fixed metadata detection for intros that start at 0 seconds and common release-group filenames.
|
||||
- **Jellyfin Session**: Remote session now restarts after setup login so the websocket reconnects with fresh credentials, and stops cleanly on logout.
|
||||
- **Sentence Card Audio**: Mining a sentence card no longer fills the expression audio field; generated audio goes only to the configured sentence audio field.
|
||||
- **Stats Mining Fields**: Sentence clips update `SentenceAudio` correctly; word audio uses configured Yomitan sources; English subtitle text is not written to word cards; secondary subtitle auto-selection prefers regular English tracks over Signs/Songs tracks.
|
||||
- **Overlay Hover Readiness**: Subtitle bars are hoverable and clickable from the first subtitle line on visible overlay startup or resume, without waiting for the next subtitle event.
|
||||
- **Startup Autoplay**: Playback is released after tokenization and overlay content are ready even when playback begins before the first subtitle line appears.
|
||||
- **Overlay Startup Feedback**: Restored mpv OSD loading spinner that starts on connect, media open, or overlay request, and clears once the overlay is content-ready and visible.
|
||||
- **Linux Overlay Input**: Notification close and action buttons remain clickable above subtitle bars on Linux.
|
||||
|
||||
<details>
|
||||
<summary>Internal changes</summary>
|
||||
|
||||
### Internal
|
||||
- **Build**: `make deps` now initializes git submodules before installing dependencies on a fresh source checkout.
|
||||
- **Release Tooling**: Release notes now credit contributors and first-time authors resolved from changelog fragments via git and the GitHub API.
|
||||
- **Changelog Guidance**: PR fragment guidance updated to preserve separate-outcome fragments while directing contributors to consolidate same-PR follow-up notes before adding churn.
|
||||
|
||||
</details>
|
||||
|
||||
## v0.15.2 (2026-06-02)
|
||||
|
||||
### Changed
|
||||
- Yomitan: Updated the bundled Yomitan build to the latest vendored revision.
|
||||
|
||||
### Fixed
|
||||
- Anki - Animated AVIF: Clip timing no longer starts or ends early; word-audio lead-in and clip duration are now aligned to frame boundaries.
|
||||
- Overlay (Hyprland): Fixed fullscreen overlay alignment - modal, stats, and sidebar content no longer shift below the mpv window.
|
||||
- Overlay (macOS): Subtitle bars are now interactive immediately after autoplay starts with "wait for overlay to be ready" enabled, without requiring a manual click.
|
||||
- Overlay (macOS): Fixed overlay, subtitles, and subtitle sidebar staying hidden after a modal closes until the user clicked the mpv window; focus is now restored to mpv when the last modal closes, so playback shortcuts and the overlay reappear correctly - including in native fullscreen.
|
||||
|
||||
## v0.15.1 (2026-05-31)
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Linux Overlay Stacking**: Fixed the overlay intermittently dropping behind mpv on KDE Plasma and other non-Hyprland/Sway Wayland sessions; restored subtitle hover, pause-on-hover, and Yomitan lookups on X11/XWayland; the overlay now correctly layers above/below mpv based on fullscreen state, yields to foreground windows (Settings, Yomitan, AniList, etc.), and avoids startup flashes and fullscreen transition glitches.
|
||||
- **Linux Overlay (Hyprland Lua)**: Fixed overlay placement on Hyprland 0.55+ when using a Lua-based config.
|
||||
- **Manual Overlay Startup**: Fixed manual visible-overlay startup from mpv - now correctly attaches to playback, keeps the window bounds synced with mpv, and primes current subtitles before showing.
|
||||
- **Playlist Transitions**: Reused the warm overlay when mpv advances to the next playlist item, avoiding a redundant tokenization pause and preserving visible subtitles across tracks.
|
||||
- **macOS Overlay**: Fixed the visible subtitle overlay staying click-through after pause-until-ready releases playback; restored mpv focus after closing modal windows so subtitles and keybinds resume without clicking the player.
|
||||
- **Mouse Keybindings**: Fixed keybinding capture and runtime handling for mouse buttons, including side buttons like `MBTN_BACK` and `MBTN_FORWARD`.
|
||||
- **Windows mpv Shortcut**: Fixed the Windows `SubMiner mpv` shortcut so videos attach to an already-running background app instead of spawning a second process.
|
||||
|
||||
### Docs
|
||||
|
||||
- **Troubleshooting**: Updated Hyprland overlay docs with current Lua (`hl.window_rule`) and legacy config syntax; added troubleshooting for KDE/Wayland and other non-Hyprland/Sway Wayland sessions; added a Character Dictionary troubleshooting section; added a "See Also" index linking each feature's troubleshooting page.
|
||||
|
||||
## v0.15.0 (2026-05-29)
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- **Subsync:**
|
||||
- The `subsync.defaultMode` config option has been removed
|
||||
- Subsync now always opens the manual subtitle picker regardless of any previously set default mode
|
||||
- **N+1 Highlighting:**
|
||||
- N+1 highlighting now has its own dedicated `ankiConnect.nPlusOne.enabled` option, separate from known-word highlighting
|
||||
- It is no longer enabled automatically when known-word highlighting is on - enable it explicitly to keep N+1 annotations
|
||||
|
||||
### Added
|
||||
|
||||
- **Auto-Updater:**
|
||||
- Tray and `subminer -u` update checks with app update prompts
|
||||
- Launcher and Linux rofi theme auto-updates
|
||||
- Checksum verification and configurable notifications
|
||||
- Opt-in prerelease channel via `updates.channel: "prerelease"`
|
||||
- **Settings Window:**
|
||||
- New dedicated Settings window via `subminer --settings` or `subminer settings`, organized into Appearance, Behavior, Anki, Input, and Integration sections
|
||||
- Click-to-learn keybinding controls
|
||||
- AnkiConnect-backed deck, field, and note-type pickers that auto-fill from the configured Anki deck
|
||||
- Cross-category search
|
||||
- Live save for most options including subtitle CSS, stats keys, logging level, Jimaku, Subsync, and Anki mappings
|
||||
- AI and translation settings remain config-file only
|
||||
- **Inline Character Portraits:**
|
||||
- Optional AniList character portraits appear inline for name-matched subtitle text
|
||||
- Manual AniList overrides scoped per parent media directory so separate season folders maintain separate character dictionary selections
|
||||
- **Character Dictionary Manager:** New `Ctrl/Cmd+D` manager modal to remove, reorder, or override loaded entries.
|
||||
- **Log Export:** Sanitized log ZIP export from the tray menu and via `subminer logs -e`, with home-directory usernames redacted from exported contents.
|
||||
- **Launcher CLI:**
|
||||
- `subminer --version` / `subminer -v` prints the installed app version
|
||||
- `mpv.profile` config and Settings support passes a named mpv profile to managed launches
|
||||
- Bundled mpv plugin startup options are now configurable from SubMiner config
|
||||
- **First-Run Setup:**
|
||||
- Optional installer for Bun and the `subminer` CLI on Linux, macOS, and Windows
|
||||
- Windows `subminer.cmd` PATH shim so `subminer` works without manually adding `SubMiner.exe` to PATH
|
||||
- Setup recognizes existing Homebrew or user PATH installs and avoids writing into Homebrew-owned paths
|
||||
- Includes an Open SubMiner Settings button
|
||||
- Standalone setup app quits after completing, returning terminal control
|
||||
- **Primary Subtitle Visibility on Yomitan Popup:** New `subtitleStyle.primaryVisibleOnYomitanPopup` option keeps hover-mode primary subtitles visible while a Yomitan popup is open.
|
||||
|
||||
### Changed
|
||||
|
||||
- **Subtitle Appearance Config:**
|
||||
- Primary and secondary subtitle appearance now use color controls plus CSS declaration editors, saved as `subtitleStyle.css`, `subtitleStyle.secondary.css`, and `subtitleSidebar.css`
|
||||
- Known-word and N+1 annotation colors moved to `subtitleStyle.knownWordColor` and `subtitleStyle.nPlusOneColor`
|
||||
- Subtitle font defaults updated to `Hiragino Sans, M PLUS 1, Source Han Sans JP, Noto Sans CJK JP`
|
||||
- Existing configs migrate automatically; legacy Anki color keys still accepted with deprecation warnings
|
||||
- **Subtitle Style Defaults:**
|
||||
- Stronger outline-style text shadow
|
||||
- Thicker JLPT underlines
|
||||
- Frequency `topX` default raised to `10000`
|
||||
- **Character Dictionary:**
|
||||
- Entries scoped to the current AniList media for name matching and inline portraits
|
||||
- Generates Japanese-only name aliases so raw romanized/English aliases no longer surface as separate results
|
||||
- In-app AniList selector waits for an explicit search with the box prefilled from the current filename
|
||||
- `subtitleStyle.nameMatchEnabled` is now the sole switch for dictionary sync and builds
|
||||
- **Electron Runtime:** Updated from 39.8.6 to 42.2.0, returning SubMiner to a supported Electron release line.
|
||||
- **Jellyfin Setup:**
|
||||
- Removed the server presets dropdown
|
||||
- Setup now shows a single editable server URL field
|
||||
- **Jellyfin Cast Identity:**
|
||||
- Device identity now derived from the OS hostname and always reported as SubMiner
|
||||
- Previously configurable identity fields are ignored, preventing multiple installs from sharing a remote-session identity
|
||||
- **Startup Defaults:** Jellyfin remote-session startup warmup and character-name subtitle highlighting now default to off.
|
||||
- **Setup Appearance:** Removed the bundled mpv runtime plugin readiness card from the setup flow.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **AniList Progress:**
|
||||
- Progress updates fire correctly when playback reaches or skips past the watched threshold, using fresh mpv timing events
|
||||
- Season-specific results preferred for multi-season files, with a clear message when the matched season is not in Planning or Watching
|
||||
- Repeated missing-token checks no longer exhaust retry attempts or duplicate dead-letter entries
|
||||
- **Anki Mining:**
|
||||
- Sentence-audio padding is opt-in by default
|
||||
- Animated AVIF freeze-frame duration aligned to word audio length without double-counting
|
||||
- Multi-line sentence alignment fixed for repeated subtitle text
|
||||
- Kiku duplicate-card detection, auto-merge, modal acknowledgment race, and field/tag ordering corrected
|
||||
- YouTube playback cards use mpv's resolved stream URLs
|
||||
- Sentence cards refresh the secondary subtitle before saving
|
||||
- **Jellyfin Discovery:**
|
||||
- Startup, subtitle track selection, and duplicate ready-signal handling all fixed
|
||||
- Paused mpv no longer misreported as playing
|
||||
- Resume corrected when a remote play command sends `StartPositionTicks: 0` despite saved progress
|
||||
- **Jellyfin Remote:**
|
||||
- Tray checkbox stays in sync on Linux after tray, CLI, or startup changes
|
||||
- Remote controller visibility and progress sync fixed for seeks, stops, startup path changes, and Linux websocket reconnect windows
|
||||
- Play and Resume now behave correctly (Play from beginning, Resume from saved position)
|
||||
- Final progress reports reuse SubMiner's last known position when mpv resets on stop
|
||||
- Windows setup login flow fixed with an IPC bridge, immediate feedback, and a timeout with inline error for unreachable servers
|
||||
- **Overlay (macOS):**
|
||||
- Overlay hides when mpv loses focus, is minimized, or is no longer the foreground app
|
||||
- Stays stable through transient window geometry disappearances from macOS APIs and when clicking from the overlay back into mpv
|
||||
- Stats overlay opened inactive so it appears over fullscreen mpv without switching Spaces
|
||||
- Passthrough fixed so mpv controls stay clickable before hovering a subtitle bar
|
||||
- **Yomitan Sidebar:**
|
||||
- Playback stays paused for sidebar-opened Yomitan popups when auto-pause is enabled
|
||||
- Popups now open when startup races the Yomitan extension load
|
||||
- Sidebar mining cards use audio and images from the clicked sidebar line instead of the current primary subtitle
|
||||
- **Launcher:**
|
||||
- `subminer app` on Linux returns terminal control immediately
|
||||
- `subminer app --setup` opens the setup flow when SubMiner is already running in the background
|
||||
- **YouTube Playback:**
|
||||
- Selected subtitles downloaded to local temp files so the primary bar and sidebar read the same source, with cleanup on reload and quit
|
||||
- False load-failure notifications suppressed
|
||||
- Tray icon created on launcher-managed playback that attaches to an already-running process
|
||||
- **Shortcuts:**
|
||||
- Native mpv menu shortcuts disabled during managed macOS playback so configured SubMiner shortcuts work while mpv has focus
|
||||
- Custom session shortcuts including `stats.markWatchedKey` wired through mpv
|
||||
- Multi-line copy/mine overlay correctly focused so number keys choose the line count on macOS and Windows
|
||||
- **Controller Bindings:**
|
||||
- Controller config and debug shortcuts stay closed while controller support is disabled
|
||||
- Binding learn mode starts from the edit pencil
|
||||
- Remaps saved per controller profile
|
||||
- Binding badges also start learn mode
|
||||
- Row reset buttons restore individual bindings to defaults
|
||||
- **Logging:**
|
||||
- `logging.level` forwarded to launcher-started and Windows shortcut-started mpv sessions, covering mpv log verbosity, plugin logging, and plugin-launched app logging
|
||||
- `logging.rotation` (default 7 days) and per-component `logging.files` toggles added, with mpv logs disabled by default
|
||||
- Repeated IPC socket warning spam suppressed while waiting for mpv to recreate the socket
|
||||
- Windows mpv IPC, subtitle track, and Yomitan diagnostics added
|
||||
- **In-Player Stats:**
|
||||
- Layering fixed so delete confirmations, overlay modals, and update-check dialogs appear above the stats window
|
||||
- Jellyfin playback stats grouped by item metadata so watched episodes merge with matching local library titles and keep clean display names
|
||||
- **WebSocket Annotations:**
|
||||
- Annotation spans and token metadata stay on the annotation WebSocket
|
||||
- The regular subtitle WebSocket is plain-text only
|
||||
- **Subtitle Annotation Prefetching:** Cached colored annotations and character images ready sooner for live subtitle changes without delaying raw subtitle display.
|
||||
- **Windows Startup Errors:** Fatal startup failures now show a native error dialog and write details to the app log instead of exiting silently.
|
||||
|
||||
### Docs
|
||||
|
||||
- **Documentation Site:**
|
||||
- Published stable docs at the site root with current development docs under `/main/`
|
||||
- Fixed versioned docs navigation, archived page link handling, and local dev version routing
|
||||
- Documented all previously undocumented config options including `subtitleStyle.primaryDefaultMode`, `stats.markWatchedKey`, `immersionTracking.lifetimeSummaries.*`, and all seven `mpv.*` launcher options
|
||||
- Added Playback Startup Flow and Runtime Sockets diagrams to the architecture docs with cross-reference pointers in the MPV Plugin and Troubleshooting pages
|
||||
|
||||
<details>
|
||||
<summary>Internal changes</summary>
|
||||
|
||||
### Internal
|
||||
|
||||
- **Release Tooling:**
|
||||
- Release-note polishing treats pending fragments and reviewed prerelease notes as a cumulative final outcome, collapsing prerelease-only fixes into the final user-facing change
|
||||
- Prerelease generation reuses existing reviewed notes and merges only new fragment material
|
||||
- `make clean` preserves `release/prerelease-notes.md`
|
||||
- **Tests:** Removed stale Yomitan vendor source-inspection assertions for changes that were not shipped.
|
||||
|
||||
</details>
|
||||
|
||||
## v0.14.0 (2026-05-12)
|
||||
|
||||
### Added
|
||||
|
||||
- **Character Dictionary:** Added AniList-based character dictionary selection for resolving title mismatches — open it in-app with the new `Ctrl+Alt+A` shortcut or from the CLI with `subminer dictionary --candidates` / `--select`. Series-scoped overrides replace stale entries in the merged dictionary.
|
||||
- **Character Dictionary:** Added AniList-based character dictionary selection for resolving title mismatches - open it in-app with the new `Ctrl+Alt+A` shortcut or from the CLI with `subminer dictionary --candidates` / `--select`. Series-scoped overrides replace stale entries in the merged dictionary.
|
||||
- **Primary Subtitle Bar:** Added a `V` shortcut and mpv plugin binding to toggle the primary subtitle bar without affecting mpv's native subtitle visibility.
|
||||
- **Texthooker:** Added `subminer texthooker -o` and a tray menu item to open the local texthooker page in the default browser.
|
||||
|
||||
@@ -58,7 +509,7 @@
|
||||
### Internal
|
||||
|
||||
- Replaced the changelog renderer with an AI polish pass that merges related fragments and writes user-facing release notes. `CHANGELOG.md` keeps internal items in a collapsed `<details>` block; GitHub release notes omit them entirely.
|
||||
- Release CI no longer auto-builds pending `changes/*.md` fragments on tag. Tagging now fails fast if fragments remain — run `bun run changelog:build` (requires the `claude` CLI) and commit before tagging.
|
||||
- Release CI no longer auto-builds pending `changes/*.md` fragments on tag. Tagging now fails fast if fragments remain - run `bun run changelog:build` (requires the `claude` CLI) and commit before tagging.
|
||||
|
||||
</details>
|
||||
|
||||
@@ -77,8 +528,8 @@
|
||||
- Stats: Episode detail hides card events whose Anki notes have been deleted, instead of showing phantom mining activity.
|
||||
- Stats: Trend and watch-time charts share a unified theme with horizontal gridlines and larger ticks for legibility.
|
||||
- Stats: Overview, Library, Trends, Sessions, and Vocabulary now use generic "title" wording so YouTube videos and anime live comfortably side by side in the dashboard.
|
||||
- Stats: Session timeline no longer plots seek-forward/seek-backward markers — they were too noisy on sessions with lots of rewinds.
|
||||
- Stats: Replaced the "Library — Per Day" section on the Stats → Trends page with a "Library — Summary" section. The new section shows a top-10 watch-time leaderboard chart and a sortable per-title table (watch time, videos, sessions, cards, words, lookups, lookups/100w, date range), all scoped to the current date range selector.
|
||||
- Stats: Session timeline no longer plots seek-forward/seek-backward markers - they were too noisy on sessions with lots of rewinds.
|
||||
- Stats: Replaced the "Library - Per Day" section on the Stats → Trends page with a "Library - Summary" section. The new section shows a top-10 watch-time leaderboard chart and a sortable per-title table (watch time, videos, sessions, cards, words, lookups, lookups/100w, date range), all scoped to the current date range selector.
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
.PHONY: help deps build build-launcher install build-linux build-macos build-macos-unsigned clean install-linux install-macos install-windows uninstall uninstall-linux uninstall-macos uninstall-windows print-dirs pretty lint ensure-bun generate-config generate-example-config dev-start dev-start-macos dev-watch dev-watch-macos dev-toggle dev-stop docs-test docs-build docs-build-versioned docs-dev
|
||||
.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
|
||||
THUMBNAILER_FILE := subminer-ffmpegthumbnailer.thumbnailer
|
||||
|
||||
# Default install prefix for the wrapper script.
|
||||
PREFIX ?= $(HOME)/.local
|
||||
@@ -72,7 +74,8 @@ help:
|
||||
" generate-config Generate ~/.config/SubMiner/config.jsonc from centralized defaults" \
|
||||
"" \
|
||||
"Other targets:" \
|
||||
" deps Install JS dependencies (root + stats + 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" \
|
||||
@@ -105,8 +108,10 @@ print-dirs:
|
||||
"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
|
||||
@@ -218,11 +223,13 @@ docs-dev: ensure-bun
|
||||
|
||||
|
||||
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 \
|
||||
@@ -231,7 +238,7 @@ install-linux: build-launcher
|
||||
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"
|
||||
@@ -272,8 +279,9 @@ uninstall:
|
||||
uninstall-linux:
|
||||
@rm -f "$(BINDIR)/subminer" "$(BINDIR)/SubMiner.AppImage"
|
||||
@rm -f "$(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)/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"
|
||||
|
||||
@@ -15,7 +15,7 @@ Integrates Yomitan and mpv - on-screen lookups, mine to Anki, and track immersio
|
||||
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||
[](https://www.typescriptlang.org)
|
||||
|
||||
[](https://github.com/user-attachments/assets/89e61895-e2b7-4b47-8d50-a35afe4132b2)
|
||||
[](https://github.com/user-attachments/assets/7abab8a9-4e4e-4f06-9f3c-9783e15a3807)
|
||||
|
||||
</div>
|
||||
|
||||
@@ -66,7 +66,7 @@ Local stats dashboard tracking watch time, vocabulary growth, mining throughput,
|
||||
Browse sibling episode files and the active mpv queue in one overlay modal. Open it with `Ctrl+Alt+P` to append episodes from the current directory, jump to queued items, remove entries, or reorder the playlist without leaving playback.
|
||||
|
||||
<div align="center">
|
||||
<img src="docs-site/public/screenshots/playlist-browser.png" width="800" alt="Stats dashboard showing watch time, cards mined, streaks, and tracking data">
|
||||
<img src="docs-site/public/screenshots/playlist-browser.png" width="800" alt="Playlist browser modal showing sibling episode files beside the active mpv queue">
|
||||
</div>
|
||||
|
||||
<br>
|
||||
@@ -86,10 +86,22 @@ Browse sibling episode files and the active mpv queue in one overlay modal. Open
|
||||
<td><b>Jellyfin</b></td>
|
||||
<td>Browse, launch, and cast media from your Jellyfin server with setup and discovery controls in the app tray</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Anime Browser</b></td>
|
||||
<td>Search anime sources you supply as <a href="https://github.com/aniyomiorg/aniyomi">Aniyomi</a> extension APKs and play an episode in mpv with the overlay attached (<code>subminer anime</code>); SubMiner ships no repositories and bundles no sources</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Jimaku</b></td>
|
||||
<td>Search and download Japanese subtitles</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>) — no API key, requires <code>xz</code> on your <code>PATH</code></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>Manual subtitle retiming — requires <code>alass</code> or <code>ffsubsync</code> on your <code>PATH</code> (optional; subtitle syncing is disabled without them)</td>
|
||||
@@ -110,17 +122,19 @@ Browse sibling episode files and the active mpv queue in one overlay modal. Open
|
||||
|
||||
## Requirements
|
||||
|
||||
Only **mpv** and Anki+AnkiConnect are required. Everything else is optional but enhances the experience.
|
||||
Only **mpv** is required to run SubMiner. Anki + AnkiConnect are required to mine cards, which is the point of the app, but everything else is optional.
|
||||
|
||||
| Dependency | Status | What it does |
|
||||
| -------------------- | ----------- | ---------------------------------------- |
|
||||
| mpv | Required | The video player SubMiner overlays on |
|
||||
| Anki + AnkiConnect | Required | Card creation from the Yomitan popup |
|
||||
| ffmpeg | Recommended | Audio clips & screenshots for Anki cards |
|
||||
| MeCab + mecab-ipadic | Recommended | More precise annotations and filtering |
|
||||
| yt-dlp | Optional | YouTube playback |
|
||||
| fzf / rofi | Optional | Video picker in the launcher |
|
||||
| alass / ffsubsync | Optional | Subtitle sync |
|
||||
| Dependency | Status | What it does |
|
||||
| -------------------- | ---------------- | -------------------------------------------------------- |
|
||||
| mpv | Required | The video player SubMiner overlays on |
|
||||
| Anki + AnkiConnect | Required to mine | Card creation from the Yomitan popup |
|
||||
| ffmpeg | Recommended | Audio clips & screenshots for Anki cards |
|
||||
| MeCab + mecab-ipadic | Recommended | More precise annotations and filtering |
|
||||
| yt-dlp | Optional | YouTube playback |
|
||||
| xz | Optional | TsukiHime subtitle downloads (not on Windows by default) |
|
||||
| alass / ffsubsync | Optional | Subtitle sync |
|
||||
| guessit | Optional | Better anime title and episode detection |
|
||||
| fzf / rofi | Optional | Video picker in the `subminer` launcher (Linux/macOS) |
|
||||
|
||||
<details>
|
||||
<summary><b>Platform-specific install commands</b></summary>
|
||||
@@ -137,9 +151,23 @@ sudo pacman -S --needed mpv ffmpeg mecab mecab-ipadic
|
||||
brew install mpv ffmpeg mecab mecab-ipadic
|
||||
```
|
||||
|
||||
**Windows:** Install [mpv](https://mpv.io/installation/) and [ffmpeg](https://ffmpeg.org/download.html) and ensure both are on `PATH`.
|
||||
**Windows:**
|
||||
|
||||
See the [full requirements list](https://docs.subminer.moe/installation#1-install-requirements) for optional dependencies.
|
||||
```powershell
|
||||
winget install shinchiro.mpv
|
||||
winget install Gyan.FFmpeg
|
||||
```
|
||||
|
||||
Then reopen your terminal and check `mpv --version` and `ffmpeg -version`. winget puts `ffmpeg` on `PATH` automatically; mpv uses a regular installer that may not, so if `mpv` is not found, either add its folder (usually `%LOCALAPPDATA%\Programs\mpv`) to `PATH` or set `mpv.executablePath` during first-run setup.
|
||||
|
||||
[Scoop](https://scoop.sh) is the alternative if you want one package manager for everything, since 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 [full requirements list](https://docs.subminer.moe/installation#_1-install-requirements) for optional dependencies.
|
||||
|
||||
</details>
|
||||
|
||||
@@ -154,6 +182,8 @@ See the [full requirements list](https://docs.subminer.moe/installation#1-instal
|
||||
|
||||
```bash
|
||||
paru -S subminer-bin
|
||||
# optional: the anime browser bridge, shared with Mangatan and updated by pacman
|
||||
paru -S mangatan-extension-server
|
||||
```
|
||||
|
||||
</details>
|
||||
@@ -165,6 +195,11 @@ paru -S subminer-bin
|
||||
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. The optional `subminer` command-line launcher runs on [Bun](https://bun.sh), and first-run setup can install both for you. To grab it manually instead, install Bun first, then:
|
||||
|
||||
```bash
|
||||
wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer -O ~/.local/bin/subminer \
|
||||
&& chmod +x ~/.local/bin/subminer
|
||||
```
|
||||
@@ -212,6 +247,7 @@ On **Windows**, just run `SubMiner.exe` and the setup will open automatically on
|
||||
subminer video.mkv # launch mpv with SubMiner
|
||||
subminer /path/to/dir # pick a file with fzf
|
||||
subminer -R /path/to/dir # pick a file with rofi (Linux only)
|
||||
subminer -H # browse history, then previous / replay / next / select / quit
|
||||
```
|
||||
|
||||
On **Windows**, use the **SubMiner mpv** shortcut created during setup. Double-click it or drag a video file onto it.
|
||||
@@ -226,18 +262,28 @@ Full guides on configuration, Anki setup, Jellyfin, immersion tracking, and more
|
||||
|
||||
SubMiner builds on the work of these open-source projects:
|
||||
|
||||
| Project | Role |
|
||||
| ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
||||
| [Anacreon-Script](https://github.com/friedrich-de/Anacreon-Script) | Inspiration for the mining workflow |
|
||||
| [asbplayer](https://github.com/killergerbah/asbplayer) | Inspiration for subtitle sidebar and logic for YouTube subtitle parsing |
|
||||
| [Bee's Character Dictionary](https://github.com/bee-san/Japanese_Character_Name_Dictionary) | Character name recognition in subtitles |
|
||||
| [GameSentenceMiner](https://github.com/bpwhelan/GameSentenceMiner) | Inspiration for Electron overlay with Yomitan integration |
|
||||
| [jellyfin-mpv-shim](https://github.com/jellyfin/jellyfin-mpv-shim) | Jellyfin integration |
|
||||
| [Jimaku.cc](https://jimaku.cc) | Japanese subtitle search and downloads |
|
||||
| [Renji's Texthooker Page](https://github.com/Renji-XD/texthooker-ui) | Base for the WebSocket texthooker integration |
|
||||
| [Yomitan](https://github.com/yomidevs/yomitan) | Dictionary engine powering all lookups and the morphological parser |
|
||||
| [yomitan-jlpt-vocab](https://github.com/stephenmk/yomitan-jlpt-vocab) | JLPT level tags for vocabulary |
|
||||
| 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 |
|
||||
| [Aniyomi](https://github.com/aniyomiorg/aniyomi) | Anime extension API and data model the anime browser targets |
|
||||
| [asbplayer](https://github.com/killergerbah/asbplayer) | Inspiration for subtitle sidebar and logic for YouTube subtitle parsing |
|
||||
| [Bee's Character Dictionary](https://github.com/bee-san/Japanese_Character_Name_Dictionary) | Character name recognition in subtitles |
|
||||
| [GameSentenceMiner](https://github.com/bpwhelan/GameSentenceMiner) | Inspiration for Electron overlay with Yomitan integration |
|
||||
| [jellyfin-mpv-shim](https://github.com/jellyfin/jellyfin-mpv-shim) | Jellyfin integration |
|
||||
| [Jimaku.cc](https://jimaku.cc) | Japanese subtitle search and downloads |
|
||||
| [M-Extension-Server](https://github.com/1Selxo/M-Extension-Server) | Runs Aniyomi extension APKs off Android; the bridge the anime browser drives |
|
||||
| [Mangatan](https://github.com/1Selxo/Mangatan) | Reference client for the bridge protocol the anime browser speaks |
|
||||
| [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)
|
||||
|
||||
The anime browser drives [M-Extension-Server](https://github.com/1Selxo/M-Extension-Server),
|
||||
downloaded from its upstream releases at runtime rather than
|
||||
bundled or redistributed here; its bundles carry their own dependencies, including a JRE and
|
||||
GPL-3.0 NewPipe Extractor. SubMiner includes none of them and talks to the bridge
|
||||
over its own HTTP protocol. It ships no extensions or repositories by default.
|
||||
|
||||
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 65 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: 114 KiB |
|
Before Width: | Height: | Size: 23 MiB |
|
Before Width: | Height: | Size: 303 KiB |
|
Before Width: | Height: | Size: 3.0 MiB 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;
|
||||
@@ -7,48 +7,57 @@
|
||||
"dependencies": {
|
||||
"@fontsource-variable/geist": "^5.2.8",
|
||||
"@fontsource-variable/geist-mono": "^5.2.7",
|
||||
"@xhayper/discord-rpc": "^1.3.3",
|
||||
"axios": "^1.13.5",
|
||||
"@xhayper/discord-rpc": "^1.3.4",
|
||||
"axios": "^1.18.1",
|
||||
"commander": "^14.0.3",
|
||||
"electron-updater": "^6.8.3",
|
||||
"hono": "^4.12.7",
|
||||
"electron-updater": "^6.8.9",
|
||||
"hono": "^4.12.28",
|
||||
"jsonc-parser": "^3.3.1",
|
||||
"koffi": "^2.15.6",
|
||||
"libsql": "^0.5.22",
|
||||
"ws": "^8.19.0",
|
||||
"ws": "^8.21.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.10.0",
|
||||
"@types/ws": "^8.18.1",
|
||||
"electron": "42.2.0",
|
||||
"electron-builder": "26.8.2",
|
||||
"electron": "42.6.0",
|
||||
"electron-builder": "26.15.3",
|
||||
"esbuild": "^0.25.12",
|
||||
"eslint": "^10.4.0",
|
||||
"eslint": "^10.8.0",
|
||||
"prettier": "^3.8.1",
|
||||
"typescript": "^5.9.3",
|
||||
"undici": "7.29.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"@discordjs/rest@2.6.1": "patches/@discordjs%2Frest@2.6.1.patch",
|
||||
},
|
||||
"overrides": {
|
||||
"@xmldom/xmldom": "0.8.12",
|
||||
"app-builder-lib": "26.8.2",
|
||||
"electron-builder-squirrel-windows": "26.8.2",
|
||||
"@xmldom/xmldom": "0.8.13",
|
||||
"app-builder-lib": "26.15.3",
|
||||
"brace-expansion": "5.0.9",
|
||||
"electron-builder-squirrel-windows": "26.15.3",
|
||||
"fast-uri": "3.1.6",
|
||||
"form-data": "4.0.6",
|
||||
"ip-address": "10.2.0",
|
||||
"js-yaml": "4.3.1",
|
||||
"lodash": "4.18.0",
|
||||
"minimatch": "10.2.3",
|
||||
"minimatch": "10.2.5",
|
||||
"picomatch": "4.0.4",
|
||||
"tar": "7.5.11",
|
||||
"tar": "7.5.21",
|
||||
"tmp": "0.2.7",
|
||||
"undici": "7.29.0",
|
||||
},
|
||||
"packages": {
|
||||
"7zip-bin": ["7zip-bin@5.2.0", "", {}, "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A=="],
|
||||
|
||||
"@develar/schema-utils": ["@develar/schema-utils@2.6.5", "", { "dependencies": { "ajv": "^6.12.0", "ajv-keywords": "^3.4.1" } }, "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig=="],
|
||||
|
||||
"@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="],
|
||||
|
||||
"@discordjs/rest": ["@discordjs/rest@2.6.1", "", { "dependencies": { "@discordjs/collection": "^2.1.1", "@discordjs/util": "^1.2.0", "@sapphire/async-queue": "^1.5.3", "@sapphire/snowflake": "^3.5.5", "@vladfrangu/async_event_emitter": "^2.4.6", "discord-api-types": "^0.38.40", "magic-bytes.js": "^1.13.0", "tslib": "^2.6.3", "undici": "6.24.1" } }, "sha512-wwQdgjeaoYFiaG+atbqx6aJDpqW7JHAo0HrQkBTbYzM3/PJ3GweQIpgElNcGZ26DCUOXMyawYd0YF7vtr+fZXg=="],
|
||||
"@discordjs/rest": ["@discordjs/rest@2.6.1", "", { "dependencies": { "@discordjs/collection": "^2.1.1", "@discordjs/util": "^1.2.0", "@sapphire/async-queue": "^1.5.3", "@sapphire/snowflake": "^3.5.5", "@vladfrangu/async_event_emitter": "^2.4.6", "discord-api-types": "^0.38.40", "magic-bytes.js": "^1.13.0", "tslib": "^2.6.3", "undici": "6.27.0" } }, "sha512-wwQdgjeaoYFiaG+atbqx6aJDpqW7JHAo0HrQkBTbYzM3/PJ3GweQIpgElNcGZ26DCUOXMyawYd0YF7vtr+fZXg=="],
|
||||
|
||||
"@discordjs/util": ["@discordjs/util@1.2.0", "", { "dependencies": { "discord-api-types": "^0.38.33" } }, "sha512-3LKP7F2+atl9vJFhaBjn4nOaSWahZ/yWjOvA4e5pnXkt2qyXRCHLxoBQy81GFtLGCq7K9lPm9R517M1U+/90Qg=="],
|
||||
|
||||
"@electron-internal/extract-zip": ["@electron-internal/extract-zip@1.0.4", "", {}, "sha512-Zr1Vs7E9tpCNhZHDAbFVXc2gEVCG9RqPDjrno5+bdgB6LRAuvgyMHJut4NCVyYwtAieapMzc3fiQ3CSTi75ARg=="],
|
||||
|
||||
"@electron/asar": ["@electron/asar@3.4.1", "", { "dependencies": { "commander": "^5.0.0", "glob": "^7.1.6", "minimatch": "^3.0.4" }, "bin": { "asar": "bin/asar.js" } }, "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA=="],
|
||||
|
||||
"@electron/fuses": ["@electron/fuses@1.8.0", "", { "dependencies": { "chalk": "^4.1.1", "fs-extra": "^9.0.1", "minimist": "^1.2.5" }, "bin": { "electron-fuses": "dist/bin.js" } }, "sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw=="],
|
||||
@@ -59,7 +68,7 @@
|
||||
|
||||
"@electron/osx-sign": ["@electron/osx-sign@1.3.3", "", { "dependencies": { "compare-version": "^0.1.2", "debug": "^4.3.4", "fs-extra": "^10.0.0", "isbinaryfile": "^4.0.8", "minimist": "^1.2.6", "plist": "^3.0.5" }, "bin": { "electron-osx-flat": "bin/electron-osx-flat.js", "electron-osx-sign": "bin/electron-osx-sign.js" } }, "sha512-KZ8mhXvWv2rIEgMbWZ4y33bDHyUKMXnx4M0sTyPNK/vcB81ImdeY9Ggdqy0SWbMDgmbqyQ+phgejh6V3R2QuSg=="],
|
||||
|
||||
"@electron/rebuild": ["@electron/rebuild@4.0.3", "", { "dependencies": { "@malept/cross-spawn-promise": "^2.0.0", "debug": "^4.1.1", "detect-libc": "^2.0.1", "got": "^11.7.0", "graceful-fs": "^4.2.11", "node-abi": "^4.2.0", "node-api-version": "^0.2.1", "node-gyp": "^11.2.0", "ora": "^5.1.0", "read-binary-file-arch": "^1.0.6", "semver": "^7.3.5", "tar": "^7.5.6", "yargs": "^17.0.1" }, "bin": { "electron-rebuild": "lib/cli.js" } }, "sha512-u9vpTHRMkOYCs/1FLiSVAFZ7FbjsXK+bQuzviJZa+lG7BHZl1nz52/IcGvwa3sk80/fc3llutBkbCq10Vh8WQA=="],
|
||||
"@electron/rebuild": ["@electron/rebuild@4.2.0", "", { "dependencies": { "@malept/cross-spawn-promise": "^2.0.0", "debug": "^4.1.1", "node-abi": "^4.2.0", "node-api-version": "^0.2.1", "node-gyp": "^12.2.0", "read-binary-file-arch": "^1.0.6" }, "bin": { "electron-rebuild": "lib/cli.js" } }, "sha512-RKL/O+jGoXJMxrx/5771y1n0xTKmFuOYGO3gMmwypBM6rsH0kou0mswwdXA2JrhIkE4xyC7v9vGk0n6NPzgOxQ=="],
|
||||
|
||||
"@electron/universal": ["@electron/universal@2.0.3", "", { "dependencies": { "@electron/asar": "^3.3.1", "@malept/cross-spawn-promise": "^2.0.0", "debug": "^4.3.1", "dir-compare": "^4.2.0", "fs-extra": "^11.1.1", "minimatch": "^9.0.3", "plist": "^3.1.0" } }, "sha512-Wn9sPYIVFRFl5HmwMJkARCCf7rqK/EurkfQ/rJZ14mHP3iYTjZSIOSVonEAnhWeAXwtw7zOekGRlc6yTtZ0t+g=="],
|
||||
|
||||
@@ -123,13 +132,13 @@
|
||||
|
||||
"@eslint/config-array": ["@eslint/config-array@0.23.5", "", { "dependencies": { "@eslint/object-schema": "^3.0.5", "debug": "^4.3.1", "minimatch": "^10.2.4" } }, "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA=="],
|
||||
|
||||
"@eslint/config-helpers": ["@eslint/config-helpers@0.6.0", "", { "dependencies": { "@eslint/core": "^1.2.1" } }, "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA=="],
|
||||
"@eslint/config-helpers": ["@eslint/config-helpers@0.7.0", "", { "dependencies": { "@eslint/core": "^1.2.1" } }, "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw=="],
|
||||
|
||||
"@eslint/core": ["@eslint/core@1.2.1", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ=="],
|
||||
|
||||
"@eslint/object-schema": ["@eslint/object-schema@3.0.5", "", {}, "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw=="],
|
||||
|
||||
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.7.1", "", { "dependencies": { "@eslint/core": "^1.2.1", "levn": "^0.4.1" } }, "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ=="],
|
||||
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.7.2", "", { "dependencies": { "@eslint/core": "^1.2.1", "levn": "^0.4.1" } }, "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A=="],
|
||||
|
||||
"@fontsource-variable/geist": ["@fontsource-variable/geist@5.2.8", "", {}, "sha512-cJ6m9e+8MQ5dCYJsLylfZrgBh6KkG4bOLckB35Tr9J/EqdkEM6QllH5PxqP1dhTvFup+HtMRPuz9xOjxXJggxw=="],
|
||||
|
||||
@@ -145,8 +154,6 @@
|
||||
|
||||
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
|
||||
|
||||
"@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
|
||||
|
||||
"@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
|
||||
|
||||
"@libsql/darwin-arm64": ["@libsql/darwin-arm64@0.5.28", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Lc/b8JXO2W2+H+5UXfw7PCHZCim1jlrB0CmLPsjfVmihMluBpdYafFImhjAHxHlWGfuZ32WzjVPUap5fGmkthw=="],
|
||||
@@ -173,11 +180,15 @@
|
||||
|
||||
"@neon-rs/load": ["@neon-rs/load@0.0.4", "", {}, "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw=="],
|
||||
|
||||
"@npmcli/agent": ["@npmcli/agent@3.0.0", "", { "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.1", "lru-cache": "^10.0.1", "socks-proxy-agent": "^8.0.3" } }, "sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q=="],
|
||||
"@noble/hashes": ["@noble/hashes@2.2.0", "", {}, "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg=="],
|
||||
|
||||
"@npmcli/fs": ["@npmcli/fs@4.0.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q=="],
|
||||
"@peculiar/asn1-schema": ["@peculiar/asn1-schema@2.8.0", "", { "dependencies": { "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-7YT0U/ze0tF2QOBbE15gKZwy5tvgGyLRiRHLzhlbOpf7BT032oBSd0haZqXn5W6l26WLlu3dyxzjM+2638/z2Q=="],
|
||||
|
||||
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
|
||||
"@peculiar/json-schema": ["@peculiar/json-schema@1.1.12", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w=="],
|
||||
|
||||
"@peculiar/utils": ["@peculiar/utils@2.0.3", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ=="],
|
||||
|
||||
"@peculiar/webcrypto": ["@peculiar/webcrypto@1.7.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.7.0", "@peculiar/json-schema": "^1.1.12", "@peculiar/utils": "^2.0.2", "tslib": "^2.8.1", "webcrypto-core": "^1.9.2" } }, "sha512-ODOov0sGMJMf3jPonOkgGqPknTsu+DdQ7kD++gz8aI+aFMOMHFbWAA2taqXXVTdP+OTOQR/znGvSpmkeI0WTYQ=="],
|
||||
|
||||
"@sapphire/async-queue": ["@sapphire/async-queue@1.5.5", "", {}, "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg=="],
|
||||
|
||||
@@ -207,47 +218,35 @@
|
||||
|
||||
"@types/node": ["@types/node@24.12.4", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA=="],
|
||||
|
||||
"@types/plist": ["@types/plist@3.0.5", "", { "dependencies": { "@types/node": "*", "xmlbuilder": ">=11.0.1" } }, "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA=="],
|
||||
|
||||
"@types/responselike": ["@types/responselike@1.0.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw=="],
|
||||
|
||||
"@types/verror": ["@types/verror@1.10.11", "", {}, "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg=="],
|
||||
|
||||
"@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
|
||||
|
||||
"@types/yauzl": ["@types/yauzl@2.10.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="],
|
||||
|
||||
"@vladfrangu/async_event_emitter": ["@vladfrangu/async_event_emitter@2.4.7", "", {}, "sha512-Xfe6rpCTxSxfbswi/W/Pz7zp1WWSNn4A0eW4mLkQUewCrXXtMj31lCg+iQyTkh/CkusZSq9eDflu7tjEDXUY6g=="],
|
||||
|
||||
"@xhayper/discord-rpc": ["@xhayper/discord-rpc@1.3.3", "", { "dependencies": { "@discordjs/rest": "^2.6.1", "@vladfrangu/async_event_emitter": "^2.4.7", "discord-api-types": "^0.38.42", "ws": "^8.20.0" } }, "sha512-Ih48GHiua7TtZgKO+f0uZPhCeQqb84fY2qUys/oMh8UbUfiUkUJLVCmd/v2AK0/pV33euh0aqSXo7+9LiPSwGw=="],
|
||||
"@xhayper/discord-rpc": ["@xhayper/discord-rpc@1.3.4", "", { "dependencies": { "@discordjs/rest": "^2.6.1", "@vladfrangu/async_event_emitter": "^2.4.7", "discord-api-types": "^0.38.47", "ws": "^8.20.0" } }, "sha512-ff0uEXuibh9wi+l4vOj7xInLUjtlTaQBje/SCyQkeXZ0j2V0y+Zge5PQIQFRHH9TjjGaYJkTofEcQhncM2q7/w=="],
|
||||
|
||||
"@xmldom/xmldom": ["@xmldom/xmldom@0.8.12", "", {}, "sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg=="],
|
||||
"@xmldom/xmldom": ["@xmldom/xmldom@0.8.13", "", {}, "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw=="],
|
||||
|
||||
"abbrev": ["abbrev@3.0.1", "", {}, "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg=="],
|
||||
"abbrev": ["abbrev@4.0.0", "", {}, "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA=="],
|
||||
|
||||
"acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="],
|
||||
|
||||
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
||||
|
||||
"agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||
"agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
|
||||
|
||||
"ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="],
|
||||
|
||||
"ajv-keywords": ["ajv-keywords@3.5.2", "", { "peerDependencies": { "ajv": "^6.9.1" } }, "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="],
|
||||
|
||||
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||
|
||||
"app-builder-bin": ["app-builder-bin@5.0.0-alpha.12", "", {}, "sha512-j87o0j6LqPL3QRr8yid6c+Tt5gC7xNfYo6uQIQkorAC6MpeayVMZrEDzKmJJ/Hlv7EnOQpaRm53k6ktDYZyB6w=="],
|
||||
|
||||
"app-builder-lib": ["app-builder-lib@26.8.2", "", { "dependencies": { "@develar/schema-utils": "~2.6.5", "@electron/asar": "3.4.1", "@electron/fuses": "^1.8.0", "@electron/get": "^3.0.0", "@electron/notarize": "2.5.0", "@electron/osx-sign": "1.3.3", "@electron/rebuild": "^4.0.3", "@electron/universal": "2.0.3", "@malept/flatpak-bundler": "^0.4.0", "@types/fs-extra": "9.0.13", "async-exit-hook": "^2.0.1", "builder-util": "26.8.1", "builder-util-runtime": "9.5.1", "chromium-pickle-js": "^0.2.0", "ci-info": "4.3.1", "debug": "^4.3.4", "dotenv": "^16.4.5", "dotenv-expand": "^11.0.6", "ejs": "^3.1.8", "electron-publish": "26.8.1", "fs-extra": "^10.1.0", "hosted-git-info": "^4.1.0", "isbinaryfile": "^5.0.0", "jiti": "^2.4.2", "js-yaml": "^4.1.0", "json5": "^2.2.3", "lazy-val": "^1.0.5", "minimatch": "^10.0.3", "plist": "3.1.0", "proper-lockfile": "^4.1.2", "resedit": "^1.7.0", "semver": "~7.7.3", "tar": "^7.5.7", "temp-file": "^3.4.0", "tiny-async-pool": "1.3.0", "which": "^5.0.0" }, "peerDependencies": { "dmg-builder": "26.8.2", "electron-builder-squirrel-windows": "26.8.2" } }, "sha512-z3ptLzJwNl35fyR0wxv4qWOfZuU36VysYHnbs8PDtf8S0QzIl2OWimdDFVmCxYMkIV1k/RT9CeTgcP7oUznFOw=="],
|
||||
"app-builder-lib": ["app-builder-lib@26.15.3", "", { "dependencies": { "@electron/asar": "3.4.1", "@electron/fuses": "^1.8.0", "@electron/get": "^3.0.0", "@electron/notarize": "2.5.0", "@electron/osx-sign": "1.3.3", "@electron/rebuild": "^4.0.4", "@electron/universal": "2.0.3", "@malept/flatpak-bundler": "^0.4.0", "@noble/hashes": "^2.2.0", "@peculiar/webcrypto": "^1.7.1", "@types/fs-extra": "9.0.13", "ajv": "^8.18.0", "asn1js": "^3.0.10", "async-exit-hook": "^2.0.1", "builder-util": "26.15.3", "builder-util-runtime": "9.7.0", "chromium-pickle-js": "^0.2.0", "ci-info": "4.3.1", "debug": "^4.3.4", "dotenv": "^16.4.5", "dotenv-expand": "^11.0.6", "ejs": "^3.1.8", "electron-publish": "26.15.3", "fs-extra": "^10.1.0", "hosted-git-info": "^4.1.0", "isbinaryfile": "^5.0.0", "jiti": "^2.4.2", "js-yaml": "^4.1.0", "json5": "^2.2.3", "lazy-val": "^1.0.5", "minimatch": "^10.2.5", "pkijs": "^3.4.0", "plist": "3.1.0", "proper-lockfile": "^4.1.2", "resedit": "^1.7.0", "semver": "~7.7.3", "tar": "^7.5.7", "temp-file": "^3.4.0", "tiny-async-pool": "1.3.0", "unzipper": "^0.12.3", "which": "^5.0.0" }, "peerDependencies": { "dmg-builder": "26.15.3", "electron-builder-squirrel-windows": "26.15.3" } }, "sha512-2VnyWkqsP5v5XbBhL3tD5Syx8iNPBYsoU7kY4S2fz7wg8Rj/nztWKCUzGKaFRTv0Xwf3/H058CR1Kvtd/3lRow=="],
|
||||
|
||||
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||
|
||||
"assert-plus": ["assert-plus@1.0.0", "", {}, "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="],
|
||||
|
||||
"astral-regex": ["astral-regex@2.0.0", "", {}, "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ=="],
|
||||
"asn1js": ["asn1js@3.0.10", "", { "dependencies": { "pvtsutils": "^1.3.6", "pvutils": "^1.1.5", "tslib": "^2.8.1" } }, "sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg=="],
|
||||
|
||||
"async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="],
|
||||
|
||||
@@ -257,29 +256,27 @@
|
||||
|
||||
"at-least-node": ["at-least-node@1.0.0", "", {}, "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg=="],
|
||||
|
||||
"axios": ["axios@1.13.6", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ=="],
|
||||
"aws4": ["aws4@1.13.2", "", {}, "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw=="],
|
||||
|
||||
"axios": ["axios@1.18.1", "", { "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.5", "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g=="],
|
||||
|
||||
"balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
||||
|
||||
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
|
||||
|
||||
"bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
|
||||
"bluebird": ["bluebird@3.7.2", "", {}, "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="],
|
||||
|
||||
"boolean": ["boolean@3.2.0", "", {}, "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw=="],
|
||||
|
||||
"brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
|
||||
|
||||
"buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
|
||||
|
||||
"buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="],
|
||||
"brace-expansion": ["brace-expansion@5.0.9", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg=="],
|
||||
|
||||
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
|
||||
|
||||
"builder-util": ["builder-util@26.8.1", "", { "dependencies": { "7zip-bin": "~5.2.0", "@types/debug": "^4.1.6", "app-builder-bin": "5.0.0-alpha.12", "builder-util-runtime": "9.5.1", "chalk": "^4.1.2", "cross-spawn": "^7.0.6", "debug": "^4.3.4", "fs-extra": "^10.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "js-yaml": "^4.1.0", "sanitize-filename": "^1.6.3", "source-map-support": "^0.5.19", "stat-mode": "^1.0.0", "temp-file": "^3.4.0", "tiny-async-pool": "1.3.0" } }, "sha512-pm1lTYbGyc90DHgCDO7eo8Rl4EqKLciayNbZqGziqnH9jrlKe8ZANGdityLZU+pJh16dfzjAx2xQq9McuIPEtw=="],
|
||||
"builder-util": ["builder-util@26.15.3", "", { "dependencies": { "@types/debug": "^4.1.6", "builder-util-runtime": "9.7.0", "chalk": "^4.1.2", "cross-spawn": "^7.0.6", "debug": "^4.3.4", "fs-extra": "^10.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "js-yaml": "^4.1.0", "sanitize-filename": "^1.6.3", "source-map-support": "^0.5.19", "stat-mode": "^1.0.0", "temp-file": "^3.4.0", "tiny-async-pool": "1.3.0" } }, "sha512-q2hn7Mbo2nFNkVekPiHFx6Nfo3hURmES3tfBn+k5Pqxl2RkmP3QGqZUhH/q9Pch/4G05NRhPjDlVj1O8q4Txvw=="],
|
||||
|
||||
"builder-util-runtime": ["builder-util-runtime@9.5.1", "", { "dependencies": { "debug": "^4.3.4", "sax": "^1.2.4" } }, "sha512-qt41tMfgHTllhResqM5DcnHyDIWNgzHvuY2jDcYP9iaGpkWxTUzV6GQjDeLnlR1/DtdlcsWQbA7sByMpmJFTLQ=="],
|
||||
"builder-util-runtime": ["builder-util-runtime@9.7.0", "", { "dependencies": { "debug": "^4.3.4", "sax": "^1.2.4" } }, "sha512-g/kR520giAFYkSXTzcmF3kqQq7wi8F6N6SzeDgZrqTBN+VHdmgWOyTdD1yD7AATDId/yXLvuP34CxW46/BwCdw=="],
|
||||
|
||||
"cacache": ["cacache@19.0.1", "", { "dependencies": { "@npmcli/fs": "^4.0.0", "fs-minipass": "^3.0.0", "glob": "^10.2.2", "lru-cache": "^10.0.1", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^7.0.2", "ssri": "^12.0.0", "tar": "^7.4.3", "unique-filename": "^4.0.0" } }, "sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ=="],
|
||||
"bytestreamjs": ["bytestreamjs@2.0.1", "", {}, "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ=="],
|
||||
|
||||
"cacheable-lookup": ["cacheable-lookup@5.0.4", "", {}, "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA=="],
|
||||
|
||||
@@ -295,16 +292,8 @@
|
||||
|
||||
"ci-info": ["ci-info@4.4.0", "", {}, "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg=="],
|
||||
|
||||
"cli-cursor": ["cli-cursor@3.1.0", "", { "dependencies": { "restore-cursor": "^3.1.0" } }, "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw=="],
|
||||
|
||||
"cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="],
|
||||
|
||||
"cli-truncate": ["cli-truncate@2.1.0", "", { "dependencies": { "slice-ansi": "^3.0.0", "string-width": "^4.2.0" } }, "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg=="],
|
||||
|
||||
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
|
||||
|
||||
"clone": ["clone@1.0.4", "", {}, "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg=="],
|
||||
|
||||
"clone-response": ["clone-response@1.0.3", "", { "dependencies": { "mimic-response": "^1.0.0" } }, "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA=="],
|
||||
|
||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||
@@ -319,8 +308,6 @@
|
||||
|
||||
"core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="],
|
||||
|
||||
"crc": ["crc@3.8.0", "", { "dependencies": { "buffer": "^5.1.0" } }, "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ=="],
|
||||
|
||||
"cross-dirname": ["cross-dirname@0.1.0", "", {}, "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q=="],
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
@@ -331,8 +318,6 @@
|
||||
|
||||
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
||||
|
||||
"defaults": ["defaults@1.0.4", "", { "dependencies": { "clone": "^1.0.2" } }, "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A=="],
|
||||
|
||||
"defer-to-connect": ["defer-to-connect@2.0.1", "", {}, "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="],
|
||||
|
||||
"define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="],
|
||||
@@ -347,11 +332,9 @@
|
||||
|
||||
"dir-compare": ["dir-compare@4.2.0", "", { "dependencies": { "minimatch": "^3.0.5", "p-limit": "^3.1.0 " } }, "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ=="],
|
||||
|
||||
"discord-api-types": ["discord-api-types@0.38.43", "", {}, "sha512-sSoBf/nK6m7BGtw65mi+QBuvEWaHE8MMziFLqWL+gT6ME/BLg34dRSVKS3Husx40uU06bvxUc3/X+D9Y6/zAbw=="],
|
||||
"discord-api-types": ["discord-api-types@0.38.49", "", {}, "sha512-XnqcWmnFZFAE8ZM8SHAw9DIV8D3Or00rMQ8iQLotrEA2PmXhl+ykaf6L6q4l474hrSUH1JaYcv+iOMRWp2p6Tg=="],
|
||||
|
||||
"dmg-builder": ["dmg-builder@26.8.2", "", { "dependencies": { "app-builder-lib": "26.8.2", "builder-util": "26.8.1", "fs-extra": "^10.1.0", "iconv-lite": "^0.6.2", "js-yaml": "^4.1.0" }, "optionalDependencies": { "dmg-license": "^1.0.11" } }, "sha512-DaWI+p4DOqiFVZFMovdGYammBOyJAiHHFWUTQ0Z7gNc0twfdIN0LvyJ+vFsgZEDR1fjgbpCj690IVtbYIsZObQ=="],
|
||||
|
||||
"dmg-license": ["dmg-license@1.0.11", "", { "dependencies": { "@types/plist": "^3.0.1", "@types/verror": "^1.10.3", "ajv": "^6.10.0", "crc": "^3.8.0", "iconv-corefoundation": "^1.1.7", "plist": "^3.0.4", "smart-buffer": "^4.0.2", "verror": "^1.10.0" }, "os": "darwin", "bin": { "dmg-license": "bin/dmg-license.js" } }, "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q=="],
|
||||
"dmg-builder": ["dmg-builder@26.15.3", "", { "dependencies": { "app-builder-lib": "26.15.3", "builder-util": "26.15.3", "fs-extra": "^10.1.0", "js-yaml": "^4.1.0" } }, "sha512-O3zJUFUYHJKgzPqioHxfxzBzlSC1eXCSr79gMSBKBP5AgjjpmrydMsMLotEg9fAJF36vdUncb+4ndRNxoPdlSQ=="],
|
||||
|
||||
"dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
|
||||
|
||||
@@ -359,26 +342,24 @@
|
||||
|
||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||
|
||||
"eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
|
||||
"duplexer2": ["duplexer2@0.1.4", "", { "dependencies": { "readable-stream": "^2.0.2" } }, "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA=="],
|
||||
|
||||
"ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="],
|
||||
|
||||
"electron": ["electron@42.2.0", "", { "dependencies": { "@electron/get": "^5.0.0", "@types/node": "^24.9.0", "extract-zip": "^2.0.1" }, "bin": { "electron": "cli.js", "install-electron": "install.js" } }, "sha512-b2Tc7sIKiZEl0tBVwFM5GJ+FT5KYhmy9QJHjx8BGVZPVW2SctXWEvrE959ElB56qw7H05dBkhlikDA1DmpaAMw=="],
|
||||
"electron": ["electron@42.6.0", "", { "dependencies": { "@electron-internal/extract-zip": "^1.0.1", "@electron/get": "^5.0.0", "@types/node": "^24.9.0" }, "bin": { "electron": "cli.js", "install-electron": "install.js" } }, "sha512-axGNgd+yCTg+vi1VEGrQqAj9WVWkePKwbICSAvMiT2eTaxhij9a/xhBHD6rXV8wrlW9ZfJzE5+xg752ImxrmTw=="],
|
||||
|
||||
"electron-builder": ["electron-builder@26.8.2", "", { "dependencies": { "app-builder-lib": "26.8.2", "builder-util": "26.8.1", "builder-util-runtime": "9.5.1", "chalk": "^4.1.2", "ci-info": "^4.2.0", "dmg-builder": "26.8.2", "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "simple-update-notifier": "2.0.0", "yargs": "^17.6.2" }, "bin": { "electron-builder": "cli.js", "install-app-deps": "install-app-deps.js" } }, "sha512-ieiiXPdgH3qrG6lcvy2mtnI5iEmAopmLuVRMSJ5j40weU0tgpNx0OAk9J5X5nnO0j9+KIkxHzwFZVUDk1U3aGw=="],
|
||||
"electron-builder": ["electron-builder@26.15.3", "", { "dependencies": { "app-builder-lib": "26.15.3", "builder-util": "26.15.3", "builder-util-runtime": "9.7.0", "chalk": "^4.1.2", "ci-info": "^4.2.0", "dmg-builder": "26.15.3", "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "simple-update-notifier": "2.0.0", "yargs": "^17.6.2" }, "bin": { "electron-builder": "./cli.js", "install-app-deps": "./install-app-deps.js" } }, "sha512-a1KM5heqS3gQCZzizXEI8RjJy3QVogULPdeSknt76uLDpBIW/HDGsMg/XgP0riP6PI9COsRvFITKKGDqA8fJxA=="],
|
||||
|
||||
"electron-builder-squirrel-windows": ["electron-builder-squirrel-windows@26.8.2", "", { "dependencies": { "app-builder-lib": "26.8.2", "builder-util": "26.8.1", "electron-winstaller": "5.4.0" } }, "sha512-kXhajX6DzdIQcTlctVTKoG1oO39JhWcTG0lH7ZEJ4FzPaKJy7KFNfNJUd5BoEmLjv5GlrRZpEOYnniD+LcwNJA=="],
|
||||
"electron-builder-squirrel-windows": ["electron-builder-squirrel-windows@26.15.3", "", { "dependencies": { "app-builder-lib": "26.15.3", "builder-util": "26.15.3", "electron-winstaller": "5.4.0" } }, "sha512-Jc19XPV9y9+2bAdZPkXuVNGNIEFBq9poHC61l8Kv6FdK7DRG3+Ic0rerC0DXOaeHNz8yW0fg/JnF8GQROOF5MA=="],
|
||||
|
||||
"electron-publish": ["electron-publish@26.8.1", "", { "dependencies": { "@types/fs-extra": "^9.0.11", "builder-util": "26.8.1", "builder-util-runtime": "9.5.1", "chalk": "^4.1.2", "form-data": "^4.0.5", "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "mime": "^2.5.2" } }, "sha512-q+jrSTIh/Cv4eGZa7oVR+grEJo/FoLMYBAnSL5GCtqwUpr1T+VgKB/dn1pnzxIxqD8S/jP1yilT9VrwCqINR4w=="],
|
||||
"electron-publish": ["electron-publish@26.15.3", "", { "dependencies": { "@types/fs-extra": "^9.0.11", "aws4": "^1.13.2", "builder-util": "26.15.3", "builder-util-runtime": "9.7.0", "chalk": "^4.1.2", "form-data": "^4.0.5", "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "mime": "^2.5.2" } }, "sha512-g/2bn8YTavY4cuS5F+jOS7zmZbXXBV8KZ8yHKfJjFPoKtzBqrpCdNPxBd3tqdBwP7BVd0lGzf7Bk2s0KesWZ4Q=="],
|
||||
|
||||
"electron-updater": ["electron-updater@6.8.3", "", { "dependencies": { "builder-util-runtime": "9.5.1", "fs-extra": "^10.1.0", "js-yaml": "^4.1.0", "lazy-val": "^1.0.5", "lodash.escaperegexp": "^4.1.2", "lodash.isequal": "^4.5.0", "semver": "~7.7.3", "tiny-typed-emitter": "^2.1.0" } }, "sha512-Z6sgw3jgbikWKXei1ENdqFOxBP0WlXg3TtKfz0rgw2vIZFJUyI4pD7ZN7jrkm7EoMK+tcm/qTnPUdqfZukBlBQ=="],
|
||||
"electron-updater": ["electron-updater@6.8.9", "", { "dependencies": { "builder-util-runtime": "9.7.0", "fs-extra": "^10.1.0", "js-yaml": "^4.1.0", "lazy-val": "^1.0.5", "lodash.escaperegexp": "^4.1.2", "lodash.isequal": "^4.5.0", "semver": "~7.7.3", "tiny-typed-emitter": "^2.1.0" } }, "sha512-ZhVxM9iGONUpZGI1FxdMRgJjUFXi7AYGVa5PwKlO1tV1/4zDxQmfKpXOHVztKrd6L9rLcFjERvi1Mf2vxyTkig=="],
|
||||
|
||||
"electron-winstaller": ["electron-winstaller@5.4.0", "", { "dependencies": { "@electron/asar": "^3.2.1", "debug": "^4.1.1", "fs-extra": "^7.0.1", "lodash": "^4.17.21", "temp": "^0.9.0" }, "optionalDependencies": { "@electron/windows-sign": "^1.1.2" } }, "sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg=="],
|
||||
|
||||
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"encoding": ["encoding@0.1.13", "", { "dependencies": { "iconv-lite": "^0.6.2" } }, "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A=="],
|
||||
|
||||
"end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="],
|
||||
|
||||
"env-paths": ["env-paths@3.0.0", "", {}, "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A=="],
|
||||
@@ -401,7 +382,7 @@
|
||||
|
||||
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
||||
|
||||
"eslint": ["eslint@10.4.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.5", "@eslint/config-helpers": "^0.6.0", "@eslint/core": "^1.2.1", "@eslint/plugin-kit": "^0.7.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^9.1.2", "eslint-visitor-keys": "^5.0.1", "espree": "^11.2.0", "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "minimatch": "^10.2.4", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ=="],
|
||||
"eslint": ["eslint@10.8.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.5", "@eslint/config-helpers": "^0.7.0", "@eslint/core": "^1.2.1", "@eslint/plugin-kit": "^0.7.2", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^9.1.2", "eslint-visitor-keys": "^5.0.1", "espree": "^11.2.0", "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "minimatch": "^10.2.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-nuKKvN+oIBO0koN7Tm7dlkmnkc21mtt0QJLwAKzjLq14y6lRTdVG36MZHJ8eQHwdJMwZbQNMlPOYedMq/oVJvQ=="],
|
||||
|
||||
"eslint-scope": ["eslint-scope@9.1.2", "", { "dependencies": { "@types/esrecurse": "^4.3.1", "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ=="],
|
||||
|
||||
@@ -419,17 +400,13 @@
|
||||
|
||||
"exponential-backoff": ["exponential-backoff@3.1.3", "", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="],
|
||||
|
||||
"extract-zip": ["extract-zip@2.0.1", "", { "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", "yauzl": "^2.10.0" }, "optionalDependencies": { "@types/yauzl": "^2.9.1" }, "bin": { "extract-zip": "cli.js" } }, "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg=="],
|
||||
|
||||
"extsprintf": ["extsprintf@1.4.1", "", {}, "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA=="],
|
||||
|
||||
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||
|
||||
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
|
||||
|
||||
"fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
|
||||
|
||||
"fd-slicer": ["fd-slicer@1.1.0", "", { "dependencies": { "pend": "~1.2.0" } }, "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g=="],
|
||||
"fast-uri": ["fast-uri@3.1.6", "", {}, "sha512-7Ical1vFEMr0onbVzEDIreM22I4khW+fzyQPwvAFWBp1iwdshSZRsL4jjRvPG9JP1uiqMHRto+YU6R2/CzDz5Q=="],
|
||||
|
||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||
|
||||
@@ -443,16 +420,12 @@
|
||||
|
||||
"flatted": ["flatted@3.4.2", "", {}, "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA=="],
|
||||
|
||||
"follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="],
|
||||
"follow-redirects": ["follow-redirects@1.16.0", "", {}, "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw=="],
|
||||
|
||||
"foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
|
||||
|
||||
"form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="],
|
||||
"form-data": ["form-data@4.0.6", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.4", "mime-types": "^2.1.35" } }, "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ=="],
|
||||
|
||||
"fs-extra": ["fs-extra@10.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="],
|
||||
|
||||
"fs-minipass": ["fs-minipass@3.0.3", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw=="],
|
||||
|
||||
"fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="],
|
||||
|
||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||
@@ -487,9 +460,9 @@
|
||||
|
||||
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
|
||||
|
||||
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||
"hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="],
|
||||
|
||||
"hono": ["hono@4.12.8", "", {}, "sha512-VJCEvtrezO1IAR+kqEYnxUOoStaQPGrCmX3j4wDTNOcD1uRPFpGlwQUIW8niPuvHXaTUxeOUl5MMDGrl+tmO9A=="],
|
||||
"hono": ["hono@4.12.28", "", {}, "sha512-YwUvVpSF7m1yOblFPrU3Hbo8XhPheBoiyfGuII6z19LnOr6JpDnyyp7LFNrfV56wS8tpvtBFGRISHN02pDdLOA=="],
|
||||
|
||||
"hosted-git-info": ["hosted-git-info@4.1.0", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="],
|
||||
|
||||
@@ -499,13 +472,7 @@
|
||||
|
||||
"http2-wrapper": ["http2-wrapper@1.0.3", "", { "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.0.0" } }, "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg=="],
|
||||
|
||||
"https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||
|
||||
"iconv-corefoundation": ["iconv-corefoundation@1.1.7", "", { "dependencies": { "cli-truncate": "^2.1.0", "node-addon-api": "^1.6.3" }, "os": "darwin" }, "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ=="],
|
||||
|
||||
"iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
||||
|
||||
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||
"https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="],
|
||||
|
||||
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
||||
|
||||
@@ -515,29 +482,23 @@
|
||||
|
||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||
|
||||
"ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="],
|
||||
|
||||
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
||||
|
||||
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
||||
|
||||
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
|
||||
|
||||
"is-interactive": ["is-interactive@1.0.0", "", {}, "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w=="],
|
||||
|
||||
"is-unicode-supported": ["is-unicode-supported@0.1.0", "", {}, "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="],
|
||||
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
|
||||
|
||||
"isbinaryfile": ["isbinaryfile@5.0.7", "", {}, "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ=="],
|
||||
|
||||
"isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="],
|
||||
|
||||
"jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
|
||||
|
||||
"jake": ["jake@10.9.4", "", { "dependencies": { "async": "^3.2.6", "filelist": "^1.0.4", "picocolors": "^1.1.1" }, "bin": { "jake": "bin/cli.js" } }, "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA=="],
|
||||
|
||||
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
||||
|
||||
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
||||
"js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="],
|
||||
|
||||
"json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
|
||||
|
||||
@@ -571,16 +532,12 @@
|
||||
|
||||
"lodash.isequal": ["lodash.isequal@4.5.0", "", {}, "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="],
|
||||
|
||||
"log-symbols": ["log-symbols@4.1.0", "", { "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" } }, "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg=="],
|
||||
|
||||
"lowercase-keys": ["lowercase-keys@2.0.0", "", {}, "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="],
|
||||
|
||||
"lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
|
||||
|
||||
"magic-bytes.js": ["magic-bytes.js@1.13.0", "", {}, "sha512-afO2mnxW7GDTXMm5/AoN1WuOcdoKhtgXjIvHmobqTD1grNplhGdv3PFOyjCVmrnOZBIT/gD/koDKpYG+0mvHcg=="],
|
||||
|
||||
"make-fetch-happen": ["make-fetch-happen@14.0.3", "", { "dependencies": { "@npmcli/agent": "^3.0.0", "cacache": "^19.0.1", "http-cache-semantics": "^4.1.1", "minipass": "^7.0.2", "minipass-fetch": "^4.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^1.0.0", "proc-log": "^5.0.0", "promise-retry": "^2.0.1", "ssri": "^12.0.0" } }, "sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ=="],
|
||||
|
||||
"matcher": ["matcher@3.0.0", "", { "dependencies": { "escape-string-regexp": "^4.0.0" } }, "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng=="],
|
||||
|
||||
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||
@@ -591,26 +548,14 @@
|
||||
|
||||
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||
|
||||
"mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
|
||||
|
||||
"mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="],
|
||||
|
||||
"minimatch": ["minimatch@10.2.3", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg=="],
|
||||
"minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
|
||||
|
||||
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
||||
|
||||
"minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
|
||||
|
||||
"minipass-collect": ["minipass-collect@2.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw=="],
|
||||
|
||||
"minipass-fetch": ["minipass-fetch@4.0.1", "", { "dependencies": { "minipass": "^7.0.3", "minipass-sized": "^1.0.3", "minizlib": "^3.0.1" }, "optionalDependencies": { "encoding": "^0.1.13" } }, "sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ=="],
|
||||
|
||||
"minipass-flush": ["minipass-flush@1.0.5", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw=="],
|
||||
|
||||
"minipass-pipeline": ["minipass-pipeline@1.2.4", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A=="],
|
||||
|
||||
"minipass-sized": ["minipass-sized@1.0.3", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g=="],
|
||||
|
||||
"minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="],
|
||||
|
||||
"mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="],
|
||||
@@ -619,17 +564,15 @@
|
||||
|
||||
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
|
||||
|
||||
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
||||
|
||||
"node-abi": ["node-abi@4.28.0", "", { "dependencies": { "semver": "^7.6.3" } }, "sha512-Qfp5XZL1cJDOabOT8H5gnqMTmM4NjvYzHp4I/Kt/Sl76OVkOBBHRFlPspGV0hYvMoqQsypFjT/Yp7Km0beXW9g=="],
|
||||
|
||||
"node-addon-api": ["node-addon-api@1.7.2", "", {}, "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg=="],
|
||||
|
||||
"node-api-version": ["node-api-version@0.2.1", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q=="],
|
||||
|
||||
"node-gyp": ["node-gyp@11.5.0", "", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "graceful-fs": "^4.2.6", "make-fetch-happen": "^14.0.3", "nopt": "^8.0.0", "proc-log": "^5.0.0", "semver": "^7.3.5", "tar": "^7.4.3", "tinyglobby": "^0.2.12", "which": "^5.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ=="],
|
||||
"node-gyp": ["node-gyp@12.4.0", "", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "graceful-fs": "^4.2.6", "nopt": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", "tar": "^7.5.4", "tinyglobby": "^0.2.12", "undici": "^6.25.0", "which": "^6.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw=="],
|
||||
|
||||
"nopt": ["nopt@8.1.0", "", { "dependencies": { "abbrev": "^3.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A=="],
|
||||
"node-int64": ["node-int64@0.4.0", "", {}, "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="],
|
||||
|
||||
"nopt": ["nopt@9.0.0", "", { "dependencies": { "abbrev": "^4.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw=="],
|
||||
|
||||
"normalize-url": ["normalize-url@6.1.0", "", {}, "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A=="],
|
||||
|
||||
@@ -637,38 +580,28 @@
|
||||
|
||||
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
||||
|
||||
"onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
|
||||
|
||||
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
|
||||
|
||||
"ora": ["ora@5.4.1", "", { "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", "cli-spinners": "^2.5.0", "is-interactive": "^1.0.0", "is-unicode-supported": "^0.1.0", "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" } }, "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ=="],
|
||||
|
||||
"p-cancelable": ["p-cancelable@2.1.1", "", {}, "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg=="],
|
||||
|
||||
"p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
|
||||
|
||||
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
|
||||
|
||||
"p-map": ["p-map@7.0.4", "", {}, "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ=="],
|
||||
|
||||
"package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="],
|
||||
|
||||
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
|
||||
|
||||
"path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="],
|
||||
|
||||
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||
|
||||
"path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
||||
|
||||
"pe-library": ["pe-library@0.4.1", "", {}, "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw=="],
|
||||
|
||||
"pend": ["pend@1.2.0", "", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
|
||||
|
||||
"pkijs": ["pkijs@3.4.0", "", { "dependencies": { "@noble/hashes": "1.4.0", "asn1js": "^3.0.6", "bytestreamjs": "^2.0.1", "pvtsutils": "^1.3.6", "pvutils": "^1.1.3", "tslib": "^2.8.1" } }, "sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw=="],
|
||||
|
||||
"plist": ["plist@3.1.0", "", { "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" } }, "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ=="],
|
||||
|
||||
"postject": ["postject@1.0.0-alpha.6", "", { "dependencies": { "commander": "^9.4.0" }, "bin": { "postject": "dist/cli.js" } }, "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A=="],
|
||||
@@ -677,7 +610,9 @@
|
||||
|
||||
"prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="],
|
||||
|
||||
"proc-log": ["proc-log@5.0.0", "", {}, "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ=="],
|
||||
"proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="],
|
||||
|
||||
"process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="],
|
||||
|
||||
"progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="],
|
||||
|
||||
@@ -685,37 +620,39 @@
|
||||
|
||||
"proper-lockfile": ["proper-lockfile@4.1.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "retry": "^0.12.0", "signal-exit": "^3.0.2" } }, "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA=="],
|
||||
|
||||
"proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
|
||||
"proxy-from-env": ["proxy-from-env@2.1.0", "", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="],
|
||||
|
||||
"pump": ["pump@3.0.4", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA=="],
|
||||
|
||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||
|
||||
"pvtsutils": ["pvtsutils@1.3.6", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg=="],
|
||||
|
||||
"pvutils": ["pvutils@1.1.5", "", {}, "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA=="],
|
||||
|
||||
"quick-lru": ["quick-lru@5.1.1", "", {}, "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="],
|
||||
|
||||
"read-binary-file-arch": ["read-binary-file-arch@1.0.6", "", { "dependencies": { "debug": "^4.3.4" }, "bin": { "read-binary-file-arch": "cli.js" } }, "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg=="],
|
||||
|
||||
"readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
||||
"readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
||||
|
||||
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
|
||||
|
||||
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
|
||||
|
||||
"resedit": ["resedit@1.7.2", "", { "dependencies": { "pe-library": "^0.4.1" } }, "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA=="],
|
||||
|
||||
"resolve-alpn": ["resolve-alpn@1.2.1", "", {}, "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="],
|
||||
|
||||
"responselike": ["responselike@2.0.1", "", { "dependencies": { "lowercase-keys": "^2.0.0" } }, "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw=="],
|
||||
|
||||
"restore-cursor": ["restore-cursor@3.1.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA=="],
|
||||
|
||||
"retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="],
|
||||
|
||||
"rimraf": ["rimraf@2.6.3", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA=="],
|
||||
|
||||
"roarr": ["roarr@2.15.4", "", { "dependencies": { "boolean": "^3.0.1", "detect-node": "^2.0.4", "globalthis": "^1.0.1", "json-stringify-safe": "^5.0.1", "semver-compare": "^1.0.0", "sprintf-js": "^1.1.2" } }, "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A=="],
|
||||
|
||||
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||
|
||||
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||
"safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
||||
|
||||
"sanitize-filename": ["sanitize-filename@1.6.4", "", { "dependencies": { "truncate-utf8-bytes": "^1.0.0" } }, "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg=="],
|
||||
|
||||
@@ -735,39 +672,25 @@
|
||||
|
||||
"simple-update-notifier": ["simple-update-notifier@2.0.0", "", { "dependencies": { "semver": "^7.5.3" } }, "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w=="],
|
||||
|
||||
"slice-ansi": ["slice-ansi@3.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" } }, "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ=="],
|
||||
|
||||
"smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="],
|
||||
|
||||
"socks": ["socks@2.8.7", "", { "dependencies": { "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" } }, "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A=="],
|
||||
|
||||
"socks-proxy-agent": ["socks-proxy-agent@8.0.5", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" } }, "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw=="],
|
||||
|
||||
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
|
||||
"source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="],
|
||||
|
||||
"sprintf-js": ["sprintf-js@1.1.3", "", {}, "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="],
|
||||
|
||||
"ssri": ["ssri@12.0.0", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ=="],
|
||||
|
||||
"stat-mode": ["stat-mode@1.0.0", "", {}, "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg=="],
|
||||
|
||||
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
|
||||
"string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
||||
|
||||
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"sumchecker": ["sumchecker@3.0.1", "", { "dependencies": { "debug": "^4.1.0" } }, "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg=="],
|
||||
|
||||
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
||||
|
||||
"tar": ["tar@7.5.11", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-ChjMH33/KetonMTAtpYdgUFr0tbz69Fp2v7zWxQfYZX4g5ZN2nOBXm1R2xyA+lMIKrLKIoKAwFj93jE/avX9cQ=="],
|
||||
"tar": ["tar@7.5.21", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-XdhtCvlMywwxpCW8YEq3lOXBJpUPTR2OHHcwLPO3HwsJqOHa2Ok/oJ7ruGzp+JrKoRPVCzJwAdEjqLW/vNRPHA=="],
|
||||
|
||||
"temp": ["temp@0.9.4", "", { "dependencies": { "mkdirp": "^0.5.1", "rimraf": "~2.6.2" } }, "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA=="],
|
||||
|
||||
@@ -779,7 +702,7 @@
|
||||
|
||||
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
||||
|
||||
"tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="],
|
||||
"tmp": ["tmp@0.2.7", "", {}, "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw=="],
|
||||
|
||||
"tmp-promise": ["tmp-promise@3.0.3", "", { "dependencies": { "tmp": "^0.2.0" } }, "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ=="],
|
||||
|
||||
@@ -793,25 +716,21 @@
|
||||
|
||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"undici": ["undici@7.25.0", "", {}, "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ=="],
|
||||
"undici": ["undici@7.29.0", "", {}, "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw=="],
|
||||
|
||||
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
||||
|
||||
"unique-filename": ["unique-filename@4.0.0", "", { "dependencies": { "unique-slug": "^5.0.0" } }, "sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ=="],
|
||||
|
||||
"unique-slug": ["unique-slug@5.0.0", "", { "dependencies": { "imurmurhash": "^0.1.4" } }, "sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg=="],
|
||||
|
||||
"universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="],
|
||||
|
||||
"unzipper": ["unzipper@0.12.5", "", { "dependencies": { "bluebird": "~3.7.2", "duplexer2": "~0.1.4", "fs-extra": "11.3.1", "graceful-fs": "^4.2.2", "node-int64": "^0.4.0" } }, "sha512-tXYOi9R57Uj/2Z25SOs5RRSzq886MBQj2gY8dPL+xl/kv6s6SvByoKfAtvfVeEuhntWDgjd2o9p2lb4TVPAz0A=="],
|
||||
|
||||
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
||||
|
||||
"utf8-byte-length": ["utf8-byte-length@1.0.5", "", {}, "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA=="],
|
||||
|
||||
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||
|
||||
"verror": ["verror@1.10.1", "", { "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg=="],
|
||||
|
||||
"wcwidth": ["wcwidth@1.0.1", "", { "dependencies": { "defaults": "^1.0.3" } }, "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg=="],
|
||||
"webcrypto-core": ["webcrypto-core@1.9.2", "", { "dependencies": { "@peculiar/asn1-schema": "^2.7.0", "@peculiar/json-schema": "^1.1.12", "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-gsXecm82UQNlTBURJGuqOWy1Ww08S3kZUcr3aOJS02Pk0xLtkfeUAVC0u0xhgdonFme80edSJUIJyuvL/7250Q=="],
|
||||
|
||||
"which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="],
|
||||
|
||||
@@ -819,11 +738,9 @@
|
||||
|
||||
"wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||
|
||||
"wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||
|
||||
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
||||
|
||||
"ws": ["ws@8.20.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA=="],
|
||||
"ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="],
|
||||
|
||||
"xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="],
|
||||
|
||||
@@ -835,11 +752,11 @@
|
||||
|
||||
"yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
|
||||
|
||||
"yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="],
|
||||
|
||||
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
||||
|
||||
"@discordjs/rest/undici": ["undici@6.24.1", "", {}, "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA=="],
|
||||
"@discordjs/rest/discord-api-types": ["discord-api-types@0.38.43", "", {}, "sha512-sSoBf/nK6m7BGtw65mi+QBuvEWaHE8MMziFLqWL+gT6ME/BLg34dRSVKS3Husx40uU06bvxUc3/X+D9Y6/zAbw=="],
|
||||
|
||||
"@discordjs/util/discord-api-types": ["discord-api-types@0.38.43", "", {}, "sha512-sSoBf/nK6m7BGtw65mi+QBuvEWaHE8MMziFLqWL+gT6ME/BLg34dRSVKS3Husx40uU06bvxUc3/X+D9Y6/zAbw=="],
|
||||
|
||||
"@electron/asar/commander": ["commander@5.1.0", "", {}, "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg=="],
|
||||
|
||||
@@ -855,37 +772,25 @@
|
||||
|
||||
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
||||
|
||||
"@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
|
||||
|
||||
"@isaacs/cliui/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
|
||||
|
||||
"@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
|
||||
|
||||
"@malept/flatpak-bundler/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="],
|
||||
|
||||
"@npmcli/agent/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||
|
||||
"@types/cacheable-request/@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
|
||||
|
||||
"@types/fs-extra/@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
|
||||
|
||||
"@types/keyv/@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
|
||||
|
||||
"@types/plist/@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
|
||||
|
||||
"@types/responselike/@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
|
||||
|
||||
"@types/ws/@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
|
||||
|
||||
"@types/yauzl/@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
|
||||
|
||||
"app-builder-lib/@electron/get": ["@electron/get@3.1.0", "", { "dependencies": { "debug": "^4.1.1", "env-paths": "^2.2.0", "fs-extra": "^8.1.0", "got": "^11.8.5", "progress": "^2.0.3", "semver": "^6.2.0", "sumchecker": "^3.0.1" }, "optionalDependencies": { "global-agent": "^3.0.0" } }, "sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ=="],
|
||||
|
||||
"app-builder-lib/ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="],
|
||||
|
||||
"app-builder-lib/ci-info": ["ci-info@4.3.1", "", {}, "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA=="],
|
||||
|
||||
"cacache/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
|
||||
|
||||
"cacache/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||
"builder-util/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||
|
||||
"clone-response/mimic-response": ["mimic-response@1.0.1", "", {}, "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="],
|
||||
|
||||
@@ -893,29 +798,25 @@
|
||||
|
||||
"electron-winstaller/fs-extra": ["fs-extra@7.0.1", "", { "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw=="],
|
||||
|
||||
"foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||
"es-set-tostringtag/hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||
|
||||
"get-intrinsic/hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||
|
||||
"http-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||
|
||||
"lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
|
||||
|
||||
"minipass-flush/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
|
||||
|
||||
"minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
|
||||
|
||||
"minipass-sized/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
|
||||
|
||||
"node-gyp/env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="],
|
||||
|
||||
"path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||
"node-gyp/which": ["which@6.0.1", "", { "dependencies": { "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" } }, "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg=="],
|
||||
|
||||
"pkijs/@noble/hashes": ["@noble/hashes@1.4.0", "", {}, "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg=="],
|
||||
|
||||
"postject/commander": ["commander@9.5.0", "", {}, "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ=="],
|
||||
|
||||
"tiny-async-pool/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="],
|
||||
|
||||
"@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
||||
|
||||
"@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
||||
|
||||
"@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
||||
"unzipper/fs-extra": ["fs-extra@11.3.1", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g=="],
|
||||
|
||||
"@types/cacheable-request/@types/node/undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||
|
||||
@@ -923,31 +824,27 @@
|
||||
|
||||
"@types/keyv/@types/node/undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||
|
||||
"@types/plist/@types/node/undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||
|
||||
"@types/responselike/@types/node/undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||
|
||||
"@types/ws/@types/node/undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||
|
||||
"@types/yauzl/@types/node/undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||
|
||||
"app-builder-lib/@electron/get/env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="],
|
||||
|
||||
"app-builder-lib/@electron/get/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="],
|
||||
|
||||
"app-builder-lib/@electron/get/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"app-builder-lib/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
||||
|
||||
"builder-util/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||
|
||||
"cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
|
||||
"electron-winstaller/fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="],
|
||||
|
||||
"electron-winstaller/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="],
|
||||
|
||||
"minipass-flush/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
|
||||
|
||||
"minipass-pipeline/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
|
||||
|
||||
"minipass-sized/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
|
||||
"node-gyp/which/isexe": ["isexe@4.0.0", "", {}, "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw=="],
|
||||
|
||||
"app-builder-lib/@electron/get/fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="],
|
||||
|
||||
|
||||
@@ -31,17 +31,25 @@ Rules:
|
||||
- `README.md` is ignored by the generator
|
||||
- if a PR should not produce release notes, apply the `skip-changelog` label instead of adding a fragment
|
||||
|
||||
PR branch workflow:
|
||||
|
||||
- Before adding a fragment or bullet, inspect the `changes/*.md` files already changed in the PR
|
||||
- If the new work fixes, modifies, renames, or supersedes behavior introduced or referenced by that fragment, edit or remove the stale bullet instead of adding follow-up churn
|
||||
- Add a new bullet only when it describes a truly separate user-visible outcome
|
||||
- Multiple fragment files are allowed when one PR has genuinely separate release-note outcomes, but keep them minimized and current
|
||||
|
||||
How fragments turn into a release:
|
||||
|
||||
- At release time, `bun run changelog:build` (and `bun run changelog:prerelease-notes`) pipes every pending fragment through `claude -p` to merge related items, drop noise, and rewrite into a clean user-facing release body. Write fragments as raw, informative notes — don't worry about polished prose, deduping across PRs, or line-by-line phrasing. The polish step handles all of that.
|
||||
- The polish step treats pending fragments as the final release outcome, not prerelease history. If a feature is added and then renamed or fixed before the stable cut, ship the final feature bullet instead of separate prerelease-only breaking/fix entries.
|
||||
- GitHub release notes and prerelease notes use short top-level items with nested bullets for the change, user benefit, and any useful action note. The stable `CHANGELOG.md` can stay in compact single-line bullets.
|
||||
- `CHANGELOG.md`, GitHub release notes, and prerelease notes all use short top-level items with one nested bullet per distinct change, instead of packing a release's worth of detail into a single paragraph bullet. An item with only one thing to say stays inline on the top-level bullet. Release notes and prerelease notes additionally cover user benefit and any useful action note in their nested bullets.
|
||||
- `internal` fragments stay in `CHANGELOG.md` (inside a collapsed `<details>` block) but are dropped from the GitHub release notes entirely.
|
||||
- The polished `CHANGELOG.md` and `release/release-notes.md` are committed and reviewed before tagging — edit the Markdown by hand if Claude misses something.
|
||||
|
||||
Prerelease notes:
|
||||
|
||||
- prerelease tags like `v0.11.3-beta.1` and `v0.11.3-rc.1` reuse the current pending fragments to generate `release/prerelease-notes.md`
|
||||
- from the second prerelease of a base version onward, the notes also open with a `## Changes since <previous tag>` section generated from the fragment diff against the previous beta/RC tag; keep fragment edits meaningful. Editorial-only rewording is filtered out of that section, while genuinely changed behavior and deleted fragments (reverted changes) are reported
|
||||
- existing prerelease notes are a reviewed baseline; later prerelease runs should replace stale beta/RC wording with the current outcome instead of appending fix churn
|
||||
- prerelease note generation does not consume fragments and does not update `CHANGELOG.md` or `docs-site/changelog.md`
|
||||
- the final stable release is the point where `bun run changelog:build` consumes fragments into the stable changelog and release notes
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
type: fixed
|
||||
area: anilist
|
||||
|
||||
- Used fresh mpv time-position, duration, and subtitle timing events for AniList post-watch threshold checks so progress updates still fire when playback reaches or skips past the watched threshold.
|
||||
- Prefer season-specific AniList search results for multi-season files before falling back to the base title, and show a clear message when the matched season is not in Planning or Watching instead of silently queueing an impossible update.
|
||||
- Prevent repeated missing-token checks from rapidly exhausting AniList retry attempts or duplicating dead-letter entries for the same episode.
|
||||
@@ -0,0 +1,7 @@
|
||||
type: fixed
|
||||
area: anilist
|
||||
|
||||
- Resolving "season N" against AniList no longer lands one season short for franchises whose broadcast seasons are listed as several entries. AniList records the back half of a split cour ("… Season 2 Part 2", "… Cour 2", "第2クール") as its own sequel, and the resolver counted each as a season — so Mushoku Tensei season 3 resolved to the season 2 entry and Re:ZERO season 4 to season 3, which then drove the character dictionary and AniList progress updates to the wrong series. A sequel carrying a part or cour marker that matches the season it continues is now followed without advancing the season count, in both the relation walk and the air-order fallback. Titles are compared ignoring punctuation, and the marker is looked for in every title and synonym rather than just the display title. A wrong match cached before this fix is remembered in `character-dictionaries/anilist-resolution-cache.json` and needs removing (or overriding from the character dictionary picker) for the affected series.
|
||||
- A special or OVA sitting in a franchise's sequel chain is walked through without counting as a season. Dr. STONE links STONE WARS to New World through a one-episode special, which made New World resolve as season 4 and every later season shift with it.
|
||||
- An AniList rate limit or network failure while walking sequel relations is reported so the caller can retry, instead of falling through to the air-date fallback. That fallback is for a franchise with missing relation edges; running it after a failed lookup turned a transient 429 into a confidently wrong season.
|
||||
- The shared title parser recognizes spelled-out episode labels (`Episode 4`, `第4話`) and a season named at the end of the title (`… Season 3`, `… 3rd Season`, `… S3`), which now fill the Season field instead of being searched for as part of the series name. The TsukiHime modal gained a Season field to match Jimaku, and seasons after the first are included in its search query so a later season's releases are actually found.
|
||||
@@ -0,0 +1,34 @@
|
||||
type: added
|
||||
area: anime
|
||||
|
||||
- Added an anime browser window that searches Aniyomi extension sources, shows cover art and episode lists, and plays an episode in mpv so the overlay and mining tools attach as usual.
|
||||
- Added `subminer anime` and the `--anime` flag to open the browser, plus a "Browse Anime" tray entry.
|
||||
- Anime extensions are read from `<userData>/anime-extensions`; drop Aniyomi `.apk` files there to add sources.
|
||||
- Added a source settings tab so extensions that need configuration (server address, credentials, quality) can be set up from the browser; values persist per extension and source, and updated extension schemas replace stale saved field definitions without losing values. Older unscoped preferences are discarded once because their package ownership cannot be proven safely.
|
||||
- Added an Extensions tab for adding repository URLs and installing, updating, or removing extensions in place; extensions that fail to load are listed with the reason. Repository requests time out instead of hanging, APK updates are staged before replacing the installed copy, and removing an extension refreshes the runtime even when saved credential cleanup fails while still reporting that failure.
|
||||
- Browse, Extensions, and Source settings are tabs, so each one gets the full window instead of sharing it with the search results.
|
||||
- Repository URLs only need to be an https URL to a `.json` index; the file name is not restricted to `index.min.json`.
|
||||
- The source picker offers "All sources", which searches every installed source at once. Results stream in as each source answers, with per-source progress in the status bar. Results are tagged with their source, failures do not blank the grid, and **Load more** appends later pages without duplicating streamed entries.
|
||||
- The Extensions tab opens with an Installed section listing every extension on disk with Remove, including ones added by hand or whose repository has since been removed. It compares APK and repository version codes, enables Update only for newer builds, marks current extensions as Up to date, and offers Update all when multiple updates are waiting.
|
||||
- Added `anime.repos`, `anime.extensionsDir`, `anime.preferredQuality`, and `anime.defaultSource` config keys; **Set default** on an Installed row in the Extensions tab makes that source (or All sources) the one the browser opens on, and the current default carries a tag. SubMiner ships no extension repositories and performs no discovery.
|
||||
- The settings app now groups Anime Browser config under an **Aniyomi** section in Integrations. A new `anime.autoOpenJimaku` option pauses newly loaded Anime Browser episodes, waits for mpv's video window to appear, closes the in-player browser, hides the standalone browser window so it is not pulled in front of mpv when the modals close, opens Jimaku with the episode details filled in, and resumes after a subtitle loads or the modal closes without overriding playback that was already paused.
|
||||
- Anime playback targets Japanese audio: dub-labelled entries are skipped when the source offers an alternative, `alang` prefers Japanese, and the source's own audio and subtitle tracks are loaded into mpv (Japanese selected) instead of being discarded, so all of them can be switched from mpv's track menu.
|
||||
- The primary subtitle slot stays reserved for Japanese: a source that only has, say, English subtitles gets them added with a normalized language tag (`English` → `en`) but not selected, so the regular `secondarySub` auto-load can route them to the secondary slot instead.
|
||||
- HLS streams pass through a local strip proxy that removes fake image headers some hosts glue onto their video segments and gives disguised segment URLs (`.image`, `.jpg`, `.css`, and other rotating fake extensions) a media-safe local alias, so those streams play in mpv and support Anki audio and image extraction with current ffmpeg releases. Upstream responses use identity encoding so playlists remain readable for URL rewriting.
|
||||
- The strip proxy retries a failed segment fetch once after a short pause and logs upstream error statuses; a host that errors on the very first fetches right after an episode resolves no longer kills the whole playback, disconnecting clients release their active upstream fetch instead of consuming the socket and bandwidth in the background, stalled response bodies retain the upstream inactivity timeout, and only origin-form requests can reach the configured local bridge.
|
||||
- The strip proxy no longer forwards `Range` headers to the bridge: ffmpeg opens every HLS segment with `Range: bytes=0-`, the bridge answers some of those with 206, and a partial response bypassed the disguise strip, so whether an episode played depended on the bridge's cache state.
|
||||
- A bridge that dies out from under the app (killed, crashed, or stopped mid-operation) no longer leaves the browser failing every request until an app restart: the exit is detected, surfaced in the status bar, and the bridge restarts on the next request.
|
||||
- "Playing" is only reported once mpv actually configures a video output; when a stream fails to decode, the browser shows mpv's error instead of claiming playback started while no window ever appeared.
|
||||
- The bridge bundle is downloaded from the newest upstream release that ships a bundle for the platform (skipping releases older than the oldest server SubMiner is known to work with), the same way Mangatan fetches it, so all four published bundles (macOS arm64 and x64, Linux x64, Windows x64) work without a maintainer hashing each one first.
|
||||
- The Extensions tab's available list has a language chip row: pick one or more languages to narrow a repository index that otherwise lists every language it knows, or "All" to clear the filter. Rows name the language ("Japanese" instead of `ja`) and the Available heading counts what the filter leaves.
|
||||
- A stream's subtitle tracks are downloaded to a temp directory and loaded into mpv as files rather than streamed from the source URL, so they can serve as the alass reference in Subsync the way Jellyfin subtitles do. The format is detected from the file's own content, a track that fails to download falls back to its URL so the episode still plays, and the directory is removed when the next episode starts or the app exits.
|
||||
- An episode launched from the browser carries its series, season and episode number through the app instead of a single joined string: stats group by series (every stream previously landed in one entry named `m3u8`), rewatching reuses the existing entry, the Jimaku and TsukiHime modals prefill Title/Season/Episode from the source's own listing, AniList updates use those fields directly, and the mpv title reads `Series S03E04 - Episode Name`.
|
||||
- Opening the browser shows a tray icon on every platform and — on macOS — puts the app in the Cmd+Tab switcher and Dock, so you can switch between it and mpv; the Dock icon is released again when the window closes during playback. Launching a video starts a regular SubMiner session, and in `subminer anime` standalone mode closing the window during playback no longer quits the app and kills the stream.
|
||||
- The episode list has a filter box: type an episode number (`12`), a range (`12-18`), or part of an episode name to narrow a long list, with a `6 of 25` counter while it is applied. Escape inside the box clears the filter instead of leaving the detail page.
|
||||
- Episodes already watched are dimmed and marked in the episode list, with a watched count in the header. The marks come from the stats history playback already writes (an episode is marked once a session passes the completion threshold), so they match the stats window and survive the stream URL changing between playbacks. They refresh when the browser window comes back to the front, and stay empty when immersion tracking is disabled.
|
||||
- Right-clicking an episode opens a menu for marking it watched or unwatched by hand, plus "Mark this and N below watched/unwatched" for the episode and every episode listed under it. Sources list newest first, so a span covers the back catalogue, which is how a series watched elsewhere gets caught up. A filter never narrows what a span covers, and the status bar reports how many episodes were touched.
|
||||
- Marking an episode that was never played creates its stats row, carrying the same series, season and episode fields playback would have recorded. Both stats library views join the lifetime tables, so a manual mark does not show up there as watch time nobody spent, and clearing a mark creates nothing.
|
||||
- Episodes can be queued instead of replacing what is playing. Every episode row has **Play** and **Queue** buttons (clicking the row still plays now), the right-click menu offers the same two, and a queued episode shows its place in line ("next up", "#2 in queue") with a queue count and **Clear queue** in the episode header. The queue spans anime, resolves and appends each episode to mpv's real playlist as soon as it is queued while subtitle tracks cache in the background, so next/previous navigation works immediately and the next episode starts without a resolution pause when the current one ends. Queueing with nothing playing just plays.
|
||||
- Added an in-player Anime Browser modal on `Ctrl+Alt+A`. The shortcut toggles it without losing its page or scroll position. It stays within the player bounds and shares the active episode, playback queue, source configuration, and watch history with the standalone browser, while each surface keeps independent search and navigation state. The modal validates its embedded page before changing overlay state, so a load setup failure leaves it closed rather than revealing a broken modal. The SubMiner logo identifies the Anime Browser in both the modal and standalone window.
|
||||
- The bridge is reused from a package-manager install when one exists: on Arch the AUR `mangatan-extension-server` package (shared with Mangatan) is picked up from `/usr/share/mangatan/extension_server`, so nothing is downloaded and pacman keeps it current. `anime.bridgeDir` points SubMiner at a bundle anywhere else. `subminer-bin` lists the package as an optional dependency.
|
||||
- SubMiner records which bridge release it installed and, once the bridge is running, checks GitHub for a newer one; when there is, the banner offers an **Update to vX** button. The new release is downloaded beside the running bridge, then the bridge restarts on it. The updater waits for a bridge still starting to stop and keeps the previous bundle until the replacement is active. The Extensions tab shows the bridge version, where it lives, and who updates it without describing an unchecked install as current.
|
||||
@@ -1,10 +0,0 @@
|
||||
type: fixed
|
||||
area: anki
|
||||
|
||||
- Made sentence-audio padding opt-in by default, and kept animated AVIF freeze-frame duration aligned to the word audio length without double-counting sentence audio padding.
|
||||
- Kept multi-line sentence mining aligned when repeated subtitle text appears in the selected history range.
|
||||
- Fixed Kiku duplicate-card detection so local duplicate sentence cards trigger the manual modal or auto merge, modal-open acknowledgement races no longer cancel the flow, and merged fields follow Kiku's group ordering, sentence-audio, furigana, and tag semantics.
|
||||
- Fixed manual clipboard card updates from YouTube playback so generated audio and images use mpv's resolved stream URLs instead of the YouTube page URL.
|
||||
- Sentence cards now refresh the current secondary subtitle before saving, so the translation field uses the loaded subtitle instead of repeating the primary text.
|
||||
- Fixed immediate known-word cache append when no default Anki mining deck is configured but multiple known-word deck field mappings are present.
|
||||
- Added an AnkiConnect deck dropdown at the top of Mining & Anki settings that auto-fills from Yomitan's current mining deck when available.
|
||||
@@ -1,4 +0,0 @@
|
||||
type: added
|
||||
area: updater
|
||||
|
||||
- Added tray and `subminer -u` update checks for SubMiner releases, including app update prompts, launcher and Linux rofi theme updates, checksum verification, configurable update notifications, and an opt-in prerelease channel. Set `updates.channel` to `"prerelease"` to receive beta/RC builds.
|
||||
@@ -0,0 +1,4 @@
|
||||
type: fixed
|
||||
area: overlay
|
||||
|
||||
- Broadcast-style Japanese caption tracks (Crunchyroll JA subs) that split one sentence across two positioned events now publish it as a single line, so `preserveLineBreaks: false` flattens it, the sidebar lists it once, and mined sentences are whole. Rows from two different speakers, sound effects, and labeled turns still stay on separate lines.
|
||||
@@ -1,8 +0,0 @@
|
||||
type: fixed
|
||||
area: character-dictionary
|
||||
|
||||
- Reused cached character-dictionary media matches so loading a title with an existing snapshot no longer sends another AniList search request.
|
||||
- Block the character dictionary manager when annotations are disabled, with a notice through the configured OSD/system notification surfaces.
|
||||
- Added surname honorific matches for Japanese localized character aliases embedded in AniList alternative names (e.g. Korean-source characters with Japanese names in parentheses), and refresh cached snapshots so those aliases are regenerated.
|
||||
- Use `subtitleStyle.nameMatchEnabled` as the only switch for character-dictionary sync/builds, hiding the legacy `anilist.characterDictionary.enabled` option.
|
||||
- Forward character dictionary manager session-action keybindings to the mpv plugin.
|
||||
@@ -1,6 +0,0 @@
|
||||
type: changed
|
||||
area: character-dictionary
|
||||
|
||||
- Character dictionary entries are now scoped to the current AniList media for name matching and inline portraits, and generate Japanese name aliases only so raw romanized/English aliases no longer surface as separate results.
|
||||
- Added a `Ctrl/Cmd+D` manager modal to remove, reorder, or override loaded dictionary entries.
|
||||
- The in-app AniList selector now waits for an explicit title search, with the search box prefilled from the current filename guess so you can edit it before choosing an override.
|
||||
@@ -1,7 +0,0 @@
|
||||
type: added
|
||||
area: subtitles
|
||||
|
||||
- Added optional inline AniList portraits for character-name subtitle matches, including automatic refresh of cached character dictionary snapshots that do not contain portrait data.
|
||||
- Scoped manual AniList overrides by parent media directory, so separate season folders can keep separate character dictionary selections.
|
||||
- Fixed large character dictionary imports by serving the merged ZIP through a local URL when supported, with a base64 fallback for older bundled Yomitan builds.
|
||||
- Allowed subtitle overlay data image sources so inline character portraits render instead of showing a broken image icon.
|
||||
@@ -1,6 +0,0 @@
|
||||
type: fixed
|
||||
area: overlay
|
||||
|
||||
- Controller config and debug shortcuts now stay closed while controller support is disabled and show a notice to enable `controller.enabled` manually.
|
||||
- Controller binding rows now start learn mode from the edit pencil, so clicking edit and pressing a controller button saves the remap.
|
||||
- Controller remaps are now saved per controller profile, binding badges also start learn mode, and row reset buttons restore individual bindings to their defaults.
|
||||
@@ -1,4 +0,0 @@
|
||||
type: changed
|
||||
area: config
|
||||
|
||||
- Defaulted Jellyfin remote-session startup warmup and character-name subtitle highlighting to off.
|
||||
@@ -1,6 +0,0 @@
|
||||
type: docs
|
||||
area: docs
|
||||
|
||||
- Published stable docs at the site root with current development docs under `/main/`, and fixed versioned docs navigation so archived pages keep local links under the selected version, the version switcher no longer nests paths incorrectly, local dev version routes serve warmed archive files, and internal README files no longer break archived builds.
|
||||
- Documented all previously undocumented config options, including `subtitleStyle.primaryDefaultMode`, `stats.markWatchedKey`, `immersionTracking.lifetimeSummaries.*`, and all seven `mpv.*` launcher options.
|
||||
- Added a Playback Startup Flow diagram and a Runtime Sockets section/diagram to the architecture docs, with cross-reference pointers in the MPV Plugin and Troubleshooting pages.
|
||||
@@ -1,4 +0,0 @@
|
||||
type: changed
|
||||
area: runtime
|
||||
|
||||
- Updated the bundled Electron runtime from 39.8.6 to 42.2.0, moving SubMiner back onto a supported Electron release line.
|
||||
@@ -1,4 +0,0 @@
|
||||
type: fixed
|
||||
area: integrations
|
||||
|
||||
- Prevented Discord Rich Presence from falling back to Jellyfin stream URLs, and primed Jellyfin playback titles before loading tokenized streams so presence shows the show/episode title
|
||||
@@ -1,4 +0,0 @@
|
||||
type: changed
|
||||
area: setup
|
||||
|
||||
- Setup: Removed the bundled mpv runtime plugin readiness card; legacy mpv plugin removal still appears when needed.
|
||||
@@ -1,6 +0,0 @@
|
||||
type: fixed
|
||||
area: jellyfin
|
||||
|
||||
- Fixed Jellyfin discovery playback: the active item is no longer reloaded on startup, paused mpv is no longer misreported as playing, startup unpause no longer repeats after a manual pause or `y-t` toggle, duplicate ready signals no longer re-show the overlay, delayed Japanese subtitle selection is handled correctly, later-loading foreign tracks no longer steal the active Japanese track, and long-lived sidebar ffmpeg extractors no longer run against stream URLs.
|
||||
- Fixed discovery resume when a remote play command sends `StartPositionTicks: 0` despite saved progress on the item.
|
||||
- Kept Jellyfin picker library discovery working when the app log level is above info.
|
||||
@@ -1,8 +0,0 @@
|
||||
type: fixed
|
||||
area: jellyfin
|
||||
|
||||
- Keep the discovery tray checkbox in sync on Linux after tray, CLI, or startup remote-session changes, and restart stale discovery sessions when the server no longer lists the SubMiner cast target.
|
||||
- Fixed remote controller visibility and progress sync for mpv/SubMiner seek jumps, stopped sessions, startup path changes, and Linux websocket reconnect windows.
|
||||
- Kept Play and Resume distinct: Play starts from the beginning while Resume starts at the saved position, and final progress reports reuse SubMiner's last known position when mpv resets during stop.
|
||||
- Derived cast device identity from the OS hostname and always report the client as SubMiner, ignoring legacy configurable identity fields so multiple installs no longer share a remote-session identity.
|
||||
- Fixed the Windows setup login flow with an IPC bridge, immediate progress feedback, and a timeout with an inline error for unreachable servers.
|
||||
@@ -1,6 +0,0 @@
|
||||
type: fixed
|
||||
area: jellyfin
|
||||
|
||||
- Show the visible subtitle overlay automatically during Jellyfin playback so `subtitleStyle` appearance applies, and inject the bundled mpv plugin when SubMiner auto-launches mpv so mpv-side keybindings work without overlay focus.
|
||||
- Made the `y-t` overlay toggle reliable and sticky across stream redirects that change mpv's path, re-arming managed subtitle defaults on redirect so Japanese primary subtitles load, collapsing duplicate toggle events on Hyprland, and keeping passive Linux/Hyprland overlay shows from stealing keyboard focus from mpv.
|
||||
- Improved subtitle timing by preferring default embedded streams over external sidecars, stripping Jellyfin's server-selected stream from playback URLs, suppressing mpv auto-selection while SubMiner stages managed tracks, correcting clear Japanese-vs-English cue offsets, and restoring per-stream subtitle delay shifts. Track selection tolerates transient `track-list` read failures and numeric string track IDs on Linux.
|
||||
@@ -0,0 +1,4 @@
|
||||
type: fixed
|
||||
area: jellyfin
|
||||
|
||||
- Jellyfin subtitle files now load with zero mpv delay instead of inferring and saving an offset from Japanese and English cue timelines.
|
||||
@@ -1,8 +0,0 @@
|
||||
type: fixed
|
||||
area: launcher
|
||||
|
||||
- Launcher-opened videos reuse an already-running background SubMiner instance, reapply preferred subtitles on warm launches, and close launcher-owned tray apps after playback ends.
|
||||
- Videos stay paused when attaching to a running background app until subtitle priming and tokenization readiness complete, with mpv plugin subtitle auto-selection moved to pre-load so launch-time choices are not reset.
|
||||
- `subminer settings` on macOS no longer emits Electron menu diagnostics and exits cleanly when the window is closed.
|
||||
- `subminer app` on Linux returns control to the terminal immediately, and Linux first-run launcher installs build with a valid Bun shebang.
|
||||
- `subminer app --setup` opens the setup flow when SubMiner is already running in the background.
|
||||
@@ -1,6 +0,0 @@
|
||||
type: added
|
||||
area: launcher
|
||||
|
||||
- Added `subminer --version` / `subminer -v` to print the installed app version.
|
||||
- Added `mpv.profile` config and Settings support for passing an mpv profile to SubMiner-managed mpv launches.
|
||||
- Made bundled mpv plugin startup options configurable from SubMiner config.
|
||||
@@ -0,0 +1,4 @@
|
||||
type: fixed
|
||||
area: overlay
|
||||
|
||||
- Fixed the overlay never loading (stuck on the "Overlay loading" OSD spinner) when the Yomitan content-script reload raced overlay window creation at startup, most visible when playing from the anime browser on Linux/Wayland: a hidden window stops painting after that reload, so the ready-to-show signal that gates showing the overlay never fired. Content-ready now falls back to did-finish-load after a short grace period.
|
||||
@@ -1,5 +0,0 @@
|
||||
type: changed
|
||||
area: updater
|
||||
|
||||
- Linux tray "Check for Updates" now installs the new AppImage automatically via `electron-updater`, matching the macOS and Windows tray flow, instead of stopping at a "manual update required" dialog. AppImages managed by a system package (AUR `/opt/SubMiner/SubMiner.AppImage`) and non-AppImage launches (no `APPIMAGE` env) still fall back to the GitHub-asset flow.
|
||||
- Routed `electron-updater` HTTP through `/usr/bin/curl` on Linux and disabled differential downloads, matching the macOS path, so background update checks stay off Electron's network service.
|
||||
@@ -1,4 +0,0 @@
|
||||
type: added
|
||||
area: logs
|
||||
|
||||
- Add sanitized log ZIP exports from the tray menu and `subminer logs -e`, with home-directory usernames redacted from exported log contents.
|
||||
@@ -1,7 +0,0 @@
|
||||
type: fixed
|
||||
area: logging
|
||||
|
||||
- Forward SubMiner `logging.level` into launcher-started and Windows shortcut-started mpv sessions, covering mpv log verbosity, plugin script logging, and plugin-launched app logging.
|
||||
- Added numeric `logging.rotation` (default 7 days of retained daily app, launcher, and mpv logs) and `logging.files` toggles per component, with mpv logs disabled by default unless explicitly enabled for debugging.
|
||||
- Added Windows mpv launch, IPC socket, subtitle track, and Yomitan/dictionary diagnostics, including expected/active IPC socket values when plugin auto-start skips on a socket mismatch.
|
||||
- Stop repeated mpv IPC socket warning spam while the app waits in the background for mpv to recreate the socket.
|
||||
@@ -0,0 +1,4 @@
|
||||
type: fixed
|
||||
area: macos
|
||||
|
||||
- `subminer anime` (and `--settings` / `--sync`) now bring their window to the front on macOS. `show()`/`focus()` only reorder windows inside the app that is already active, so the window opened behind the terminal that launched it; SubMiner now activates itself when opening one. The anime browser also restores its Dock icon before showing rather than after, because the overlay's fullscreen transform leaves the app as an accessory process that macOS refuses to bring forward at all.
|
||||
@@ -0,0 +1,4 @@
|
||||
type: fixed
|
||||
area: mining
|
||||
|
||||
- Multi-line copy and mining now select backward from the current subtitle in timeline order after seeking, instead of copying lines in playback encounter order. Jumping back to a short previous line also counts as a seek with external subtitle files, so that line becomes the current one.
|
||||
@@ -1,5 +0,0 @@
|
||||
type: changed
|
||||
area: config
|
||||
|
||||
- `ankiConnect.nPlusOne.enabled` is no longer implicitly set to `true` when known-word highlighting is enabled; existing configs that already had N+1 highlighting keep it, but new configs leave it disabled unless `ankiConnect.nPlusOne.enabled` is set explicitly.
|
||||
- Updated known-word cache docs and examples to recommend expression/word fields and removed legacy-option references from user-facing config docs.
|
||||
@@ -1,8 +0,0 @@
|
||||
type: fixed
|
||||
area: overlay
|
||||
|
||||
- Primed the first startup subtitle before autoplay resumes so the overlay renders text before video playback begins.
|
||||
- Kept the visible overlay and subtitle stream alive after restarting SubMiner from the mpv `y-r` shortcut, with correct Linux bounds reapplication and user-paused playback preserved through readiness gates.
|
||||
- Fixed managed mpv startup so launcher-owned videos quit SubMiner when playback ends while background/tray sessions stay alive, with pause-until-ready waiting for overlay and tokenization readiness.
|
||||
- Fixed the subtitle sync modal on macOS so it no longer flashes and hides on the first attempt or leaves stale state after syncing.
|
||||
- Fixed Windows managed mpv launches from a background instance so the warm app receives the start command, retargets the new mpv socket, binds to the player window, and receives startup overlay options.
|
||||
@@ -1,7 +0,0 @@
|
||||
type: fixed
|
||||
area: overlay
|
||||
|
||||
- Refreshed overlay placement after leaving mpv fullscreen so the visible overlay stays aligned to the player on Hyprland.
|
||||
- Kept the visible overlay stacked above mpv after mpv regains focus from clicks or overlay movement, and suspended it while the in-player stats window is open, restoring it mouse-passive afterward.
|
||||
- Promoted SubMiner and Yomitan settings windows above the subtitle overlay on Hyprland instead of opening behind it, without hiding subtitles.
|
||||
- Hid the visible overlay as soon as the character dictionary modal opens, including while AniList lookup is loading or returns no results.
|
||||
@@ -1,7 +0,0 @@
|
||||
type: fixed
|
||||
area: overlay
|
||||
|
||||
- Hid the macOS visible overlay when mpv loses focus, is minimized, or is no longer the foreground target so other apps and Spaces are not covered, treating the frontmost mpv app as the focus signal.
|
||||
- Kept the overlay stable through transient window-tracking misses, when mpv remains frontmost but window geometry temporarily disappears from macOS APIs, and when clicking from the overlay back into mpv.
|
||||
- Kept the overlay correctly layered during stats mouse passthrough, opened the stats overlay inactive so it appears over fullscreen mpv without switching Spaces, and fixed passthrough so mpv controls stay clickable before hovering a subtitle bar.
|
||||
- Reduced window-tracker background work by preferring the compiled helper and slowing polls while mpv is stably focused.
|
||||
@@ -1,6 +0,0 @@
|
||||
type: fixed
|
||||
area: overlay
|
||||
|
||||
- Kept playback paused for Yomitan lookup popups opened from the subtitle sidebar when popup auto-pause is enabled.
|
||||
- Fixed Yomitan popups not opening when playback/overlay startup races the Yomitan extension load.
|
||||
- Fixed subtitle sidebar mining so Yomitan-enriched cards use audio and images from the clicked sidebar line instead of the current primary subtitle line.
|
||||
@@ -1,5 +0,0 @@
|
||||
type: fixed
|
||||
area: release
|
||||
|
||||
- Fixed macOS packaging so the compiled mpv window helper is built into `dist/scripts` and bundled, preventing the overlay from falling back to slow Swift source startup, and removed a stale Windows helper resource entry that produced harmless missing-file warnings.
|
||||
- Fixed one-shot `make clean build install` flows so install picks up the AppImage built earlier in the same make invocation.
|
||||
@@ -1,4 +0,0 @@
|
||||
type: fixed
|
||||
area: websocket
|
||||
|
||||
- WebSocket: Kept the regular subtitle websocket plain-text only; annotation spans and token metadata now stay on the annotation websocket.
|
||||
@@ -1,4 +0,0 @@
|
||||
type: added
|
||||
area: config
|
||||
|
||||
- Added `subtitleStyle.primaryVisibleOnYomitanPopup` to keep hover-mode primary subtitles visible while a Yomitan popup is open.
|
||||
@@ -1,6 +0,0 @@
|
||||
type: internal
|
||||
area: release
|
||||
|
||||
- Release-note polishing treats pending fragments and reviewed prerelease notes as a cumulative final outcome, collapsing prerelease-only fixes or breakages into the final user-facing change.
|
||||
- Prerelease note generation reuses existing reviewed notes and merges only new fragment material, and `make clean` preserves `release/prerelease-notes.md`.
|
||||
- Release-note polishing now asks Claude to write short, nested highlight bullets so longer changes are easier to scan.
|
||||
@@ -1,4 +0,0 @@
|
||||
type: changed
|
||||
area: jellyfin
|
||||
|
||||
- Removed the Jellyfin setup server presets dropdown; setup now shows a single editable server URL field.
|
||||
@@ -1,4 +0,0 @@
|
||||
type: internal
|
||||
area: tests
|
||||
|
||||
- Removed stale Yomitan vendor source-inspection assertions for changes that were not shipped.
|
||||
@@ -0,0 +1,4 @@
|
||||
type: fixed
|
||||
area: overlay
|
||||
|
||||
- Native mpv secondary subtitles stay hidden when switching secondary subtitle tracks during playback.
|
||||
@@ -0,0 +1,5 @@
|
||||
type: added
|
||||
area: anki
|
||||
|
||||
- Senren note type support for duplicate-card field grouping: enable `ankiConnect.isSenren` to merge duplicate mined cards using Senren's scene-switching markup, with grouped sentence, furigana, audio, picture, and miscInfo entries.
|
||||
- Senren field grouping supports the same auto/manual/disabled modes as Kiku, including the manual merge modal, and is mutually exclusive with Kiku (only one can be enabled at a time).
|
||||
@@ -1,8 +0,0 @@
|
||||
type: added
|
||||
area: config
|
||||
|
||||
- Added a dedicated Settings window via `subminer --settings` or `subminer settings`, organized into Appearance, Behavior, Anki, Input, and Integration sections with click-to-learn keybinding controls (including the AniSkip button key) and AnkiConnect-backed deck, field, and note-type pickers.
|
||||
- Expanded live reload so Settings saves apply immediately for stats keys, logging level, Jimaku, Subsync, YouTube language defaults, Anki field mappings, sentence card model, and selected annotation/runtime options.
|
||||
- Settings search works across all categories, narrows on multi-word terms, and hides settings owned by richer editors.
|
||||
- The note-fields note type picker defaults to the configured Anki deck's note type, then exact `Kiku`, then exact `Lapis`, leaving it blank for manual selection otherwise.
|
||||
- AI and translation settings remain config-file only.
|
||||
@@ -1,7 +0,0 @@
|
||||
type: added
|
||||
area: setup
|
||||
|
||||
- Added optional first-run setup controls to install Bun and the `subminer` command-line launcher on Linux, macOS, and Windows, with a Windows `subminer.cmd` PATH shim so `subminer` works without manually adding `SubMiner.exe` to PATH.
|
||||
- Added an Open SubMiner Settings button to first-run setup and moved Finish to the right-side action slot.
|
||||
- First-run setup recognizes existing `subminer` installs in Homebrew or user PATH directories, while manual setup avoids writing into Homebrew-owned paths.
|
||||
- The standalone setup app quits after completing first-run setup, returning the terminal instead of leaving the process open.
|
||||
@@ -1,6 +0,0 @@
|
||||
type: fixed
|
||||
area: shortcuts
|
||||
|
||||
- Disabled native mpv menu shortcuts during managed macOS playback so configured SubMiner shortcuts work while mpv has focus.
|
||||
- Wired configured session shortcuts, including `stats.markWatchedKey`, through mpv so custom changes work while mpv has focus.
|
||||
- Focus the visible overlay when entering multi-line copy/mine selection so number keys choose the line count on macOS and Windows.
|
||||
@@ -1,5 +0,0 @@
|
||||
type: fixed
|
||||
area: stats
|
||||
|
||||
- Fixed in-player stats layering so delete confirmations, overlay modals, and update-check dialogs appear above the stats window.
|
||||
- Grouped Jellyfin playback stats under item metadata instead of stream URLs, so watched episodes merge with matching local library titles and keep clean display names.
|
||||
@@ -0,0 +1,4 @@
|
||||
type: fixed
|
||||
area: subsync
|
||||
|
||||
- Subsync no longer fails with `Protocol "file:" not supported` on a subtitle that was dropped onto mpv. mpv reports such a track as a percent-encoded `file://` URL, which subsync read as a stream and tried to fetch over HTTP; the URL is now decoded back to its path, so both the retimed target and an alass reference work. A `file://` video path is treated as local too, which restores the video reference and ffsubsync for a dropped file.
|
||||
@@ -1,4 +0,0 @@
|
||||
type: changed
|
||||
area: subtitles
|
||||
|
||||
- Subsync now always opens the manual picker and the `subsync.defaultMode` config/settings option has been removed.
|
||||
@@ -0,0 +1,7 @@
|
||||
type: fixed
|
||||
area: subsync
|
||||
|
||||
- Subsync now works when mpv loaded a subtitle track from a URL, which is how Aniyomi extension streams and Jellyfin add theirs. The track is downloaded to a temporary file first — reusing mpv's own request headers, so authenticated and referer-gated hosts stay reachable — instead of being rejected with "Subtitle file not found: https://…". This applies to both the sync target and the alass reference, so a Jimaku or TsukiHime download can be retimed against a stream's own subtitles. Internal tracks of a stream also pass mpv's headers through to `ffmpeg`.
|
||||
- Alass now works on tracks that arrive as WebVTT, which is what Aniyomi extension streams serve. Alass picks its parser from the file extension and has no WebVTT support, so it treated a `.vtt` reference as a video file and failed with "no audio stream in file". Both the target and the reference are rewritten as SRT for alass, keeping the cue text as-is, and the originals are left untouched.
|
||||
- Leaving `subsync.alass_path`, `ffsubsync_path`, or `ffmpeg_path` empty now actually auto-discovers the binary, as the config help has always claimed. Previously it fell back to a hard-coded `/usr/bin/<tool>`, which does not exist on macOS and broke subsync for every default-config install there. Discovery searches `PATH` plus the usual install prefixes (a GUI launch inherits a minimal `PATH`) and accepts `alass-cli` as well as `alass`. An explicitly configured path is still used verbatim and never silently substituted.
|
||||
- Subsync failures are written to the application log. Previously the only trace was an OSD toast that vanished after a few seconds, leaving nothing to diagnose from.
|
||||
@@ -1,8 +0,0 @@
|
||||
type: changed
|
||||
area: config
|
||||
|
||||
- Primary and secondary subtitle appearance now use color controls plus CSS declaration editors, saved as `subtitleStyle.css` and `subtitleStyle.secondary.css`; sidebar appearance uses `subtitleSidebar.css`.
|
||||
- Moved known-word and N+1 annotation colors to `subtitleStyle.knownWordColor` and `subtitleStyle.nPlusOneColor`; legacy Anki color keys are still accepted with deprecation warnings.
|
||||
- Updated subtitle font defaults to `Hiragino Sans, M PLUS 1, Source Han Sans JP, Noto Sans CJK JP`.
|
||||
- Existing configs are migrated automatically: legacy primary/secondary appearance options and hover token colors fold into `subtitleStyle.css`, and user config files are preserved during legacy compatibility handling.
|
||||
- Live Settings saves apply subtitle CSS declarations immediately to open video overlays, and the generated example config uses the same CSS declaration paths.
|
||||
@@ -1,5 +0,0 @@
|
||||
type: fixed
|
||||
area: subtitles
|
||||
|
||||
- Kept frequency highlighting for determiner-led noun compounds like `その場` while still filtering standalone determiners.
|
||||
- Fixed frequency annotations for Yomitan single-token compounds with internal particles such as `目の前`, while keeping pure grammar/kana helper spans unannotated.
|
||||
@@ -1,4 +0,0 @@
|
||||
type: fixed
|
||||
area: subtitles
|
||||
|
||||
- Improved subtitle annotation prefetching so cached colored annotations and character images are ready for more live subtitle changes without delaying raw subtitle display.
|
||||
@@ -0,0 +1,4 @@
|
||||
type: fixed
|
||||
area: subtitles
|
||||
|
||||
- Copying the current subtitle, Anki sentence mining from recent lines, and immersion stats no longer include the separate furigana lines that broadcast-caption ASS files place above a word; recorders now use the same furigana-free text the overlay displays.
|
||||
@@ -1,4 +0,0 @@
|
||||
type: changed
|
||||
area: subtitles
|
||||
|
||||
- Updated subtitle defaults with a stronger outline-style text shadow, thicker JLPT underlines, and a `topX` frequency highlighting default of `10000`.
|
||||
@@ -1,7 +0,0 @@
|
||||
type: fixed
|
||||
area: tray
|
||||
|
||||
- Kept the tray app running when closing tray-launched Yomitan settings, with a close-only menu so closing settings does not quit the tray, and an in-page close button on Hyprland where native window controls are unavailable.
|
||||
- Kept settings loading from blocking other tray actions, serialized copied Yomitan extension refreshes at startup, and disabled the embedded popup preview to avoid renderer hangs during sidebar navigation.
|
||||
- Fixed session help focus handling so the modal can close without mpv running.
|
||||
- Fixed the Windows tray "Open SubMiner Setup" action so it opens the setup window after first-run setup is complete.
|
||||
@@ -1,6 +0,0 @@
|
||||
type: fixed
|
||||
area: updater
|
||||
|
||||
- Linux: `subminer -u` performs release updates independently of any running tray app (reporting `up to date` without downloading when not newer), and update checks use GitHub release metadata/assets instead of the native Electron updater to avoid network-service crashes during startup.
|
||||
- macOS: update dialogs from `subminer -u` reliably appear in the foreground; builds that cannot apply native updates show a manual-install message instead of a restart prompt; `electron-updater` metadata and ZIP downloads route through `/usr/bin/curl` to avoid Electron network crashes while preserving the Squirrel install path; and metadata mismatches from conflicting ZIP filenames are resolved.
|
||||
- Windows: automatic updates keep the native `electron-updater`/NSIS install path while routing updater HTTP through main-process fetch, avoiding the delayed app exit after launch.
|
||||
@@ -1,4 +0,0 @@
|
||||
type: fixed
|
||||
area: windows
|
||||
|
||||
- Windows startup failures now show a native error dialog and write fatal details to the SubMiner app log instead of exiting silently.
|
||||
@@ -1,6 +0,0 @@
|
||||
type: fixed
|
||||
area: youtube
|
||||
|
||||
- Downloaded selected YouTube primary subtitles to temporary local files so the primary bar and sidebar read the same source, with cleanup on reload and quit, and suppressed false load-failure notifications by re-checking live mpv subtitle state.
|
||||
- Launcher-managed playback commands create the tray icon even when attaching to an already-running process, and app-owned YouTube playback no longer lets the mpv plugin start a second SubMiner instance.
|
||||
- Logged Linux tray registration failures with a StatusNotifier/AppIndicator hint and documented the Hyprland tray-host requirement.
|
||||
@@ -172,10 +172,19 @@
|
||||
"updates": {
|
||||
"enabled": true, // Run automatic update checks in the background. Values: true | false
|
||||
"checkIntervalHours": 24, // Minimum hours between automatic update checks.
|
||||
"notificationType": "system", // How SubMiner announces available updates. Values: system | osd | both | none
|
||||
"notificationType": "overlay", // How SubMiner announces available updates. overlay shows notifications on the overlay, system uses OS notifications, both uses overlay and system. osd and osd-system are legacy config-file-only values. Values: overlay | system | both | none | osd | osd-system
|
||||
"channel": "stable" // Release channel used for update checks. Values: stable | prerelease
|
||||
}, // Automatic update check behavior.
|
||||
|
||||
// ==========================================
|
||||
// Notifications
|
||||
// Overlay notification display behavior.
|
||||
// Hot-reload: position changes apply to the next overlay notification.
|
||||
// ==========================================
|
||||
"notifications": {
|
||||
"overlayPosition": "top-right" // Position for in-overlay notification cards. Values: top-left | top | top-right
|
||||
}, // Overlay notification display behavior.
|
||||
|
||||
// ==========================================
|
||||
// Keyboard Shortcuts
|
||||
// Overlay keyboard shortcuts. Set a shortcut to null to disable.
|
||||
@@ -196,10 +205,13 @@
|
||||
"openCharacterDictionaryManager": "CommandOrControl+D", // Accelerator that opens the character dictionary manager modal.
|
||||
"openRuntimeOptions": "CommandOrControl+Shift+O", // Accelerator that opens the runtime options modal.
|
||||
"openJimaku": "Ctrl+Shift+J", // Accelerator that opens the Jimaku subtitle search modal.
|
||||
"openTsukihime": "Ctrl+Shift+T", // Accelerator that opens the TsukiHime subtitle search modal (configured secondary/Japanese primary tabs).
|
||||
"openSessionHelp": "CommandOrControl+Slash", // Accelerator that opens the session help / keybinding cheatsheet.
|
||||
"openControllerSelect": "Alt+C", // Accelerator that opens the controller selection and learn-mode modal.
|
||||
"openControllerDebug": "Alt+Shift+C", // Accelerator that opens the controller debug modal with live axis/button readouts.
|
||||
"toggleSubtitleSidebar": "Backslash" // Accelerator that toggles the subtitle sidebar visibility.
|
||||
"toggleSubtitleSidebar": "Backslash", // Accelerator that toggles the subtitle sidebar visibility.
|
||||
"toggleNotificationHistory": "CommandOrControl+N", // Accelerator that toggles the overlay notification history panel.
|
||||
"appendClipboardVideoToQueue": "CommandOrControl+A" // Accelerator that appends a video path from the clipboard to the mpv playlist.
|
||||
}, // Overlay keyboard shortcuts. Set a shortcut to null to disable.
|
||||
|
||||
// ==========================================
|
||||
@@ -280,15 +292,41 @@
|
||||
] // Command setting.
|
||||
},
|
||||
{
|
||||
"key": "Shift+BracketRight", // Key setting.
|
||||
"key": "Ctrl+Shift+ArrowLeft", // Key setting.
|
||||
"command": [
|
||||
"__sub-delay-next-line"
|
||||
"sub-step",
|
||||
-1
|
||||
] // Command setting.
|
||||
},
|
||||
{
|
||||
"key": "Shift+BracketLeft", // Key setting.
|
||||
"key": "Ctrl+Shift+ArrowRight", // Key setting.
|
||||
"command": [
|
||||
"__sub-delay-prev-line"
|
||||
"sub-step",
|
||||
1
|
||||
] // Command setting.
|
||||
},
|
||||
{
|
||||
"key": "KeyZ", // Key setting.
|
||||
"command": [
|
||||
"add",
|
||||
"sub-delay",
|
||||
-0.1
|
||||
] // Command setting.
|
||||
},
|
||||
{
|
||||
"key": "Shift+KeyZ", // Key setting.
|
||||
"command": [
|
||||
"add",
|
||||
"sub-delay",
|
||||
0.1
|
||||
] // Command setting.
|
||||
},
|
||||
{
|
||||
"key": "KeyX", // Key setting.
|
||||
"command": [
|
||||
"add",
|
||||
"sub-delay",
|
||||
0.1
|
||||
] // Command setting.
|
||||
},
|
||||
{
|
||||
@@ -303,6 +341,12 @@
|
||||
"__playlist-browser-open"
|
||||
] // Command setting.
|
||||
},
|
||||
{
|
||||
"key": "Ctrl+Alt+KeyA", // Key setting.
|
||||
"command": [
|
||||
"__anime-browser-open"
|
||||
] // Command setting.
|
||||
},
|
||||
{
|
||||
"key": "Ctrl+Shift+KeyH", // Key setting.
|
||||
"command": [
|
||||
@@ -395,6 +439,12 @@
|
||||
"nameMatchColor": "#f5bde6", // Hex color used when a subtitle token matches an entry from the SubMiner character dictionary.
|
||||
"nPlusOneColor": "#c6a0f6", // Color used for the single N+1 target token subtitle highlight.
|
||||
"knownWordColor": "#a6da95", // Color used for known-word subtitle highlights.
|
||||
"knownWordMaturityColors": {
|
||||
"new": "#ee99a0", // Color for known words whose Anki cards are new (never reviewed), when maturity highlighting is enabled.
|
||||
"learning": "#b7bdf8", // Color for known words whose Anki cards are in (re)learning, when maturity highlighting is enabled.
|
||||
"young": "#91d7e3", // Color for known words whose Anki cards are in review below the mature threshold, when maturity highlighting is enabled.
|
||||
"mature": "#a6da95" // Color for known words whose Anki cards are at or above the mature interval threshold, when maturity highlighting is enabled.
|
||||
}, // Known word maturity colors setting.
|
||||
"jlptColors": {
|
||||
"N1": "#ed8796", // N1 setting.
|
||||
"N2": "#f5a97f", // N2 setting.
|
||||
@@ -479,7 +529,7 @@
|
||||
// ==========================================
|
||||
// AnkiConnect Integration
|
||||
// Automatic Anki updates and media generation options.
|
||||
// Hot-reload: ankiConnect.ai.enabled, knownWords, nPlusOne, fields.word/audio/image/sentence/miscInfo, behavior.autoUpdateNewCards, isLapis.sentenceCardModel, and isKiku.fieldGrouping update live while SubMiner is running.
|
||||
// Hot-reload: ankiConnect.ai.enabled, media.normalizeAudio/mirrorMpvVolume, knownWords, nPlusOne, fields.word/audio/image/sentence/miscInfo, behavior.autoUpdateNewCards, isLapis.sentenceCardModel, isKiku.fieldGrouping, isSenren.fieldGrouping, and lapisKiku.wordCardKind update live while SubMiner is running.
|
||||
// Shared AI provider transport settings are read from top-level ai and typically require restart.
|
||||
// Most other AnkiConnect settings still require restart.
|
||||
// ==========================================
|
||||
@@ -496,7 +546,7 @@
|
||||
"tags": [
|
||||
"SubMiner"
|
||||
], // Tags to add to cards mined or updated by SubMiner. Provide an empty array to disable automatic tagging.
|
||||
"deck": "", // Restrict duplicate detection and card enrichment to this Anki deck. Leave empty to search all decks.
|
||||
"deck": "", // Restrict duplicate detection and card enrichment to this Anki deck. Leave empty to use the Yomitan mining deck when available.
|
||||
"fields": {
|
||||
"word": "Expression", // Card field for the mined word or expression text.
|
||||
"audio": "ExpressionAudio", // Card field that receives generated sentence audio.
|
||||
@@ -523,23 +573,27 @@
|
||||
"animatedMaxHeight": 0, // Maximum height for animated AVIF captures, in pixels. Set to 0 to preserve aspect ratio.
|
||||
"animatedCrf": 35, // Animated AVIF CRF quality target. Lower values produce larger, higher-quality files.
|
||||
"syncAnimatedImageToWordAudio": true, // For animated AVIF images, prepend a frozen first frame matching the existing word-audio duration so motion starts with sentence audio. Values: true | false
|
||||
"normalizeAudio": true, // Normalize generated sentence audio loudness during media extraction. Changes apply live. Values: true | false
|
||||
"mirrorMpvVolume": true, // Apply mpv's current software volume curve to generated sentence audio. Changes apply live. Values: true | false
|
||||
"audioPadding": 0, // Seconds of padding appended to both ends of generated sentence audio and animated AVIF clips.
|
||||
"fallbackDuration": 3, // Fallback clip duration in seconds when subtitle timing data is unavailable.
|
||||
"maxMediaDuration": 30 // Maximum allowed media clip duration in seconds.
|
||||
}, // Media setting.
|
||||
"knownWords": {
|
||||
"highlightEnabled": false, // Enable fast local highlighting for words already known in Anki. Values: true | false
|
||||
"maturityEnabled": false, // Color known-word highlights by Anki card maturity (new, learning, young, mature) instead of a single color. Requires known-word highlighting. Values: true | false
|
||||
"matureThresholdDays": 21, // Card interval in days at which a known word counts as mature (Anki convention: 21).
|
||||
"refreshMinutes": 1440, // Minutes between known-word cache refreshes.
|
||||
"addMinedWordsImmediately": true, // Immediately append newly mined card words into the known-word cache. Values: true | false
|
||||
"matchMode": "headword", // Known-word matching strategy for subtitle annotations. Cache matches always receive known-word highlighting even when POS filters suppress other annotation types. Values: headword | surface
|
||||
"decks": {} // Decks and expression/word fields for known-word cache. Object mapping deck names to arrays of field names to extract, e.g. { "Kaishi 1.5k": ["Word"] }.
|
||||
"decks": {} // Decks and expression/word fields for known-word cache. Object mapping deck names to arrays of field names to extract, e.g. { "Kaishi 1.5k": ["Word"] }. Reading fields (Reading, Word Reading, ExpressionReading) are always probed so cached words match only in the reading their note teaches; words from notes without readings match in any reading.
|
||||
}, // Known words setting.
|
||||
"behavior": {
|
||||
"overwriteAudio": true, // When updating an existing card, overwrite the audio field instead of skipping it. Values: true | false
|
||||
"overwriteImage": true, // When updating an existing card, overwrite the image field instead of skipping it. Values: true | false
|
||||
"mediaInsertMode": "append", // Whether new media is appended after or prepended before existing field contents on update. Values: append | prepend
|
||||
"highlightWord": true, // Bold the mined word inside the sentence field on the saved Anki card. Values: true | false
|
||||
"notificationType": "osd", // Notification surface used to announce mining and update outcomes. Values: osd | system | both | none
|
||||
"notificationType": "overlay", // Notification surface used to announce mining and update outcomes. overlay shows notifications on the overlay, system uses OS notifications, both uses overlay and system. osd and osd-system are legacy config-file-only values. Values: overlay | system | both | none | osd | osd-system
|
||||
"autoUpdateNewCards": true // Automatically update newly added cards. Values: true | false
|
||||
}, // Behavior setting.
|
||||
"nPlusOne": {
|
||||
@@ -557,9 +611,32 @@
|
||||
"enabled": false, // Enable Kiku-specific mining behaviors (duplicate handling, field grouping). Values: true | false
|
||||
"fieldGrouping": "disabled", // Kiku duplicate-card field grouping mode. Values: auto | manual | disabled
|
||||
"deleteDuplicateInAuto": true // When Kiku field grouping is "auto", delete the duplicate source card after grouping completes. Values: true | false
|
||||
} // Is kiku setting.
|
||||
}, // Is kiku setting.
|
||||
"isSenren": {
|
||||
"enabled": false, // Enable Senren-specific duplicate handling (scene-switching field grouping, including miscInfo grouping). Mutually exclusive with isKiku.enabled. Values: true | false
|
||||
"fieldGrouping": "auto", // Senren duplicate-card field grouping mode (scene switching). Values: auto | manual | disabled
|
||||
"deleteDuplicateInAuto": true // When Senren field grouping is "auto", delete the duplicate source card after grouping completes. Values: true | false
|
||||
}, // Is senren setting.
|
||||
"lapisKiku": {
|
||||
"wordCardKind": "word-and-sentence" // Card-type flag SubMiner marks on Kiku/Lapis word cards. Only one flag is set at a time; the others are cleared. Requires isKiku.enabled or isLapis.enabled. Values: word-and-sentence | click | sentence | audio | none
|
||||
} // Lapis kiku setting.
|
||||
}, // Automatic Anki updates and media generation options.
|
||||
|
||||
// ==========================================
|
||||
// Anime Browser
|
||||
// Anime browser sources. SubMiner ships no extension repositories and bundles no sources;
|
||||
// add a repository index URL here (or drop .apk files in the extensions directory) to have any.
|
||||
// Hot-reload: autoOpenJimaku applies to the next episode; other anime changes apply the next time the anime browser opens.
|
||||
// ==========================================
|
||||
"anime": {
|
||||
"autoOpenJimaku": false, // Pause Anime Browser playback and open Jimaku when an episode loads. Playback resumes after a subtitle loads or the modal closes only when auto-open initiated the pause. Values: true | false
|
||||
"extensionsDir": "", // Directory holding Aniyomi extension .apk files. Empty uses <userData>/anime-extensions.
|
||||
"repos": [], // Extension repository index URLs (any https .json index, e.g. https://.../index.min.json). Empty by default; SubMiner ships no repositories.
|
||||
"preferredQuality": "", // Preferred stream quality label, matched as a substring (for example: 1080). Empty uses the source order.
|
||||
"defaultSource": "", // Source the Anime Browser selects when it opens: a source id (<package>:<source>) or "all" for every installed source. Empty selects the first installed source. "Set default" in the Extensions tab writes this value.
|
||||
"bridgeDir": "" // Directory holding an M-Extension-Server bundle (java runtime plus server jar) to run instead of the copy SubMiner downloads. Empty checks the package-manager install (Arch: mangatan-extension-server), then <userData>/anime-bridge.
|
||||
}, // Anime browser sources. SubMiner ships no extension repositories and bundles no sources;
|
||||
|
||||
// ==========================================
|
||||
// Jimaku
|
||||
// Jimaku API configuration and defaults.
|
||||
@@ -573,6 +650,16 @@
|
||||
"maxEntryResults": 10 // Maximum Jimaku search results returned.
|
||||
}, // Jimaku API configuration and defaults.
|
||||
|
||||
// ==========================================
|
||||
// TsukiHime
|
||||
// TsukiHime subtitle search configuration for Japanese primary and configured secondary subtitles. No API key required.
|
||||
// Hot-reload: TsukiHime changes apply to the next TsukiHime request.
|
||||
// ==========================================
|
||||
"tsukihime": {
|
||||
"apiBaseUrl": "https://api.tsukihime.org/v1", // Base URL of the TsukiHime API (Animetosho successor). No API key required.
|
||||
"maxSearchResults": 10 // Maximum TsukiHime search results returned.
|
||||
}, // TsukiHime subtitle search configuration for Japanese primary and configured secondary subtitles. No API key required.
|
||||
|
||||
// ==========================================
|
||||
// YouTube Playback Settings
|
||||
// Defaults for managed subtitle language preferences and YouTube subtitle loading.
|
||||
@@ -582,7 +669,11 @@
|
||||
"primarySubLanguages": [
|
||||
"ja",
|
||||
"jpn"
|
||||
] // Comma-separated primary subtitle language priority for managed subtitle auto-selection.
|
||||
], // Comma-separated primary subtitle language priority for managed subtitle auto-selection.
|
||||
"mediaCache": {
|
||||
"mode": "direct", // How YouTube card audio/images are extracted. Values: direct | background
|
||||
"maxHeight": 720 // Maximum video height downloaded for the YouTube background media cache. Set to 0 for unlimited.
|
||||
} // Media cache setting.
|
||||
}, // Defaults for managed subtitle language preferences and YouTube subtitle loading.
|
||||
|
||||
// ==========================================
|
||||
@@ -629,13 +720,13 @@
|
||||
"executablePath": "", // Optional absolute path to mpv.exe for Windows launch flows. Leave empty to auto-discover from SUBMINER_MPV_PATH or PATH.
|
||||
"launchMode": "normal", // Default window state for SubMiner-managed mpv launches. Values: normal | maximized | fullscreen
|
||||
"profile": "", // Optional mpv profile name passed to SubMiner-managed mpv launches. Leave empty to pass no profile.
|
||||
"socketPath": "\\\\.\\pipe\\subminer-socket", // mpv IPC socket path used by SubMiner-managed playback and the bundled mpv plugin.
|
||||
"socketPath": "/tmp/subminer-socket", // mpv IPC socket path used by SubMiner-managed playback and the bundled mpv plugin.
|
||||
"backend": "auto", // Window tracking backend passed to the bundled mpv plugin. Auto detects the current platform. Values: auto | hyprland | sway | x11 | macos | windows
|
||||
"autoStartSubMiner": true, // Start SubMiner in the background when SubMiner-managed mpv loads a file. Values: true | false
|
||||
"pauseUntilOverlayReady": true, // Pause mpv on visible-overlay auto-start until SubMiner signals subtitle tokenization readiness. Values: true | false
|
||||
"subminerBinaryPath": "", // Optional SubMiner app binary path passed to the bundled mpv plugin. Leave empty to use the launcher-detected app path.
|
||||
"aniskipEnabled": true, // Enable AniSkip intro detection and skip markers in the bundled mpv plugin. Values: true | false
|
||||
"aniskipButtonKey": "TAB" // mpv key used to trigger the AniSkip button while the skip marker is visible.
|
||||
"aniskipEnabled": true, // Enable AniSkip intro detection, chapter markers, and the skip-intro key. Values: true | false
|
||||
"aniskipButtonKey": "TAB" // mpv key used to skip the detected intro while the skip prompt is visible.
|
||||
}, // SubMiner-managed mpv launch and bundled plugin options.
|
||||
|
||||
// ==========================================
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { existsSync, readFileSync, statSync } from 'node:fs';
|
||||
import { extname, join, posix, resolve, sep } from 'node:path';
|
||||
import type { DefaultTheme, HeadConfig, TransformContext, UserConfig } from 'vitepress';
|
||||
@@ -26,9 +27,12 @@ function optionalEnv(value: string | undefined): string | undefined {
|
||||
const base = normalizeBase(optionalEnv(process.env.SUBMINER_DOCS_BASE) ?? '/');
|
||||
const outDir = optionalEnv(process.env.SUBMINER_DOCS_OUT_DIR);
|
||||
const docsSourceDir = optionalEnv(process.env.SUBMINER_DOCS_SOURCE_DIR) ?? process.cwd();
|
||||
// The tracked `docs-site/` checkout, which stays a git working tree even when
|
||||
// `docsSourceDir` points at an untracked release snapshot. Used for git lookups only.
|
||||
const repoDocsDir = optionalEnv(process.env.SUBMINER_DOCS_REPO_DIR) ?? process.cwd();
|
||||
const channel = normalizeChannel(optionalEnv(process.env.SUBMINER_DOCS_CHANNEL));
|
||||
const docsVersion = optionalEnv(process.env.SUBMINER_DOCS_VERSION);
|
||||
const latestStable = optionalEnv(process.env.SUBMINER_DOCS_LATEST_STABLE) ?? 'v0.14.0';
|
||||
const latestStable = optionalEnv(process.env.SUBMINER_DOCS_LATEST_STABLE) ?? 'v0.18.0';
|
||||
const versionManifest = parseVersionManifest(process.env.SUBMINER_DOCS_VERSION_MANIFEST);
|
||||
const versionLinkOrigin =
|
||||
optionalEnv(process.env.SUBMINER_DOCS_VERSION_LINK_ORIGIN) ?? 'production';
|
||||
@@ -82,15 +86,18 @@ function pageToRoute(page: string): string | null {
|
||||
return route ? `/${route}` : '/';
|
||||
}
|
||||
|
||||
// Only the root channel is indexable. `main` and every /v/<version>/ archive are
|
||||
// near-verbatim copies of it, so they own their URL via a self-referential canonical
|
||||
// and are excluded from the index instead of being consolidated onto root. Uniform
|
||||
// self-canonical plus noindex avoids mixing noindex with a cross-page canonical,
|
||||
// which Google treats as a conflicting signal.
|
||||
const isIndexableChannel = channel === 'stable-root';
|
||||
|
||||
function pageToCanonicalHref(page: string): string | null {
|
||||
const route = pageToRoute(page);
|
||||
if (!route) return null;
|
||||
|
||||
if (channel === 'main') {
|
||||
return `${DOCS_HOSTNAME}${canonicalRouteWithBase(route)}`;
|
||||
}
|
||||
|
||||
if (channel === 'stable-archive' && docsVersion !== latestStable) {
|
||||
if (!isIndexableChannel) {
|
||||
return `${DOCS_HOSTNAME}${canonicalRouteWithBase(route)}`;
|
||||
}
|
||||
|
||||
@@ -106,7 +113,9 @@ function transformPageHead({ page }: TransformContext): HeadConfig[] {
|
||||
const href = pageToCanonicalHref(page);
|
||||
const head: HeadConfig[] = href ? [['link', { rel: 'canonical', href }]] : [];
|
||||
|
||||
if (channel === 'main') {
|
||||
// Crawlable so links still pass through, but out of the index: ~30 archived copies
|
||||
// of every page otherwise soak up the crawl budget the current docs need.
|
||||
if (!isIndexableChannel) {
|
||||
head.push(['meta', { name: 'robots', content: 'noindex,follow' }]);
|
||||
}
|
||||
|
||||
@@ -287,6 +296,39 @@ const versionItems = [
|
||||
})),
|
||||
];
|
||||
|
||||
function sitemapUrlToPage(url: string): string {
|
||||
const route = url.replace(/\.html$/, '').replace(/^\/+|\/+$/g, '');
|
||||
return route ? `${route}.md` : 'index.md';
|
||||
}
|
||||
|
||||
// VitePress derives <lastmod> by running `git log` inside its source dir. Production
|
||||
// builds point that at an untracked snapshot of the release tag, so the lookup comes
|
||||
// back empty and the sitemap ships with no dates at all. Resolve it from the tracked
|
||||
// checkout at the ref being built instead.
|
||||
function lastModifiedFor(url: string): string | undefined {
|
||||
const ref = docsVersion && docsVersion !== 'main' ? docsVersion : 'HEAD';
|
||||
const result = spawnSync('git', ['log', '-1', '--format=%cI', ref, '--', sitemapUrlToPage(url)], {
|
||||
cwd: repoDocsDir,
|
||||
encoding: 'utf8',
|
||||
});
|
||||
|
||||
return (result.status === 0 && result.stdout.trim()) || undefined;
|
||||
}
|
||||
|
||||
// Only the root channel publishes a sitemap. Archived and `main` builds would emit
|
||||
// their own copies listing the same canonical URLs, which just advertises the
|
||||
// duplicate trees we are trying to keep out of the index.
|
||||
const sitemap: UserConfig['sitemap'] = isIndexableChannel
|
||||
? {
|
||||
hostname: DOCS_HOSTNAME,
|
||||
transformItems(items) {
|
||||
return items
|
||||
.filter((item) => item.url !== 'README' && item.url !== `${DOCS_HOSTNAME}/README`)
|
||||
.map((item) => ({ ...item, lastmod: item.lastmod ?? lastModifiedFor(item.url) }));
|
||||
},
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const nav: DefaultTheme.NavItem[] = [
|
||||
{ text: 'Home', link: '/' },
|
||||
{ text: 'Get Started', link: '/installation' },
|
||||
@@ -326,8 +368,11 @@ const sidebar: DefaultTheme.SidebarItem[] = [
|
||||
{ text: 'Anki', link: '/anki-integration' },
|
||||
{ text: 'Jellyfin', link: '/jellyfin-integration' },
|
||||
{ text: 'YouTube', link: '/youtube-integration' },
|
||||
{ text: 'Anime Browser', link: '/anime-browser' },
|
||||
{ text: 'Jimaku', link: '/jimaku-integration' },
|
||||
{ text: 'TsukiHime', link: '/tsukihime-integration' },
|
||||
{ text: 'AniList', link: '/anilist-integration' },
|
||||
{ text: 'AniSkip', link: '/aniskip-integration' },
|
||||
{ text: 'Character Dictionary', link: '/character-dictionary' },
|
||||
],
|
||||
},
|
||||
@@ -417,14 +462,7 @@ const config: UserConfig = {
|
||||
appearance: 'dark',
|
||||
cleanUrls: true,
|
||||
metaChunk: true,
|
||||
sitemap: {
|
||||
hostname: DOCS_HOSTNAME,
|
||||
transformItems(items) {
|
||||
return items.filter(
|
||||
(item) => item.url !== 'README' && item.url !== `${DOCS_HOSTNAME}/README`,
|
||||
);
|
||||
},
|
||||
},
|
||||
sitemap,
|
||||
transformHead: transformPageHead,
|
||||
lastUpdated: true,
|
||||
srcExclude: ['subagents/**', 'README.md'],
|
||||
|
||||
@@ -38,8 +38,10 @@ bun run docs:dev
|
||||
The public docs root is stable-only:
|
||||
|
||||
- `/` serves the latest stable release docs.
|
||||
- `/main/` serves development docs from `main` and is marked `noindex,follow`.
|
||||
- `/main/` serves development docs from `main`.
|
||||
- `/v/<version>/` serves stable release archives.
|
||||
- Prerelease tags do not update the docs site.
|
||||
|
||||
Only `/` is indexable. `/main/` and every `/v/<version>/` page carries a self-referential canonical plus `noindex,follow`, and the generated `_headers` file repeats that as an `X-Robots-Tag`. They stay crawlable so their links still resolve, but ~30 archived copies of every page would otherwise consume the crawl budget the current docs need. Only the root build emits `sitemap.xml`, and its `<lastmod>` dates come from `git log` against the tracked checkout at the released tag, because the build renders from an untracked snapshot that VitePress cannot date itself.
|
||||
|
||||
Keep Cloudflare Git auto-deploy disabled. The production deploy is `.github/workflows/docs-pages.yml`, which uploads `.tmp/docs-versioned-site` with `--branch main` so tag-triggered runs update Production instead of creating preview deployments.
|
||||
|
||||
@@ -4,7 +4,7 @@ SubMiner can sync your watch progress to [AniList](https://anilist.co) automatic
|
||||
|
||||
AniList data also powers two additional features: [cover art](#cover-art) for the stats dashboard and the [Character Dictionary](/character-dictionary) for in-overlay name lookup.
|
||||
|
||||
[AniList](https://anilist.co) is a free website for tracking which anime you have watched. An **access token** is a private key SubMiner stores so it can update your list on your behalf — you approve it once during setup, and you never paste a password into SubMiner.
|
||||
[AniList](https://anilist.co) is a free website for tracking which anime you have watched. An **access token** is a private key SubMiner stores so it can update your list on your behalf - you approve it once during setup, and you never paste a password into SubMiner.
|
||||
|
||||
## Setup
|
||||
|
||||
@@ -38,10 +38,10 @@ SubMiner monitors playback and triggers an AniList progress update when an episo
|
||||
|
||||
The update flow:
|
||||
|
||||
1. **Title detection** -- SubMiner extracts the anime title, season, and episode number from the media filename. It tries [`guessit`](https://github.com/guessit-io/guessit) first for accurate parsing, then falls back to an internal filename parser if guessit is unavailable.
|
||||
2. **AniList search** -- The detected title is searched against the AniList GraphQL API. For season 2 and later files, SubMiner searches the season-specific title first, then falls back to the base title. SubMiner picks the best match by comparing titles (romaji, English, native) and filtering by episode count.
|
||||
1. **Title detection** -- SubMiner extracts the anime title, season, and episode number from the media filename and path. Season folders such as `Season 2` are treated as a strong season signal. SubMiner tries [`guessit`](https://github.com/guessit-io/guessit) first for accurate parsing, then falls back to an internal filename parser if guessit is unavailable.
|
||||
2. **AniList search** -- The base title (with any `Season N` / `SN` marker stripped) is searched against the AniList GraphQL API, and SubMiner picks the best match by comparing titles (romaji, English, native, synonyms) and filtering by episode count. AniList has no notion of numbered seasons -- sequels are separate entries with their own titles (`Zoku`, `Kan`, `2nd Season`), so searching `<title> Season 3` finds nothing. For season 2 and later, SubMiner instead walks `SEQUEL` relations from the season 1 entry, preferring the TV line, and falls back to ordering the franchise's TV entries by air date when the relation chain is incomplete. If neither locates the season, SubMiner **skips the update** rather than writing progress to the season 1 entry, and tells you to pin the right entry with a [character dictionary override](/character-dictionary#correcting-anilist-matches).
|
||||
3. **Progress check** -- SubMiner fetches your current list entry for the matched media. The media must already be in Planning or Watching; otherwise SubMiner shows an MPV message explaining that the update is not possible. If your recorded progress already meets or exceeds the detected episode, the update is skipped.
|
||||
4. **Mutation** -- A `SaveMediaListEntry` mutation sets the new progress and marks the entry as `CURRENT`.
|
||||
4. **Mutation** -- A `SaveMediaListEntry` mutation sets the new progress and marks the entry as `CURRENT`, or `COMPLETED` when the watched episode is the final episode of the season (the "already at this progress" skip is bypassed for the final episode so completion still lands).
|
||||
|
||||
## Update Queue and Retry
|
||||
|
||||
@@ -69,6 +69,8 @@ SubMiner fetches cover art from AniList for display in the stats dashboard. When
|
||||
|
||||
A no-match result is cached for 5 minutes before SubMiner retries, preventing repeated API calls for unrecognized media.
|
||||
|
||||
If the automatic match is wrong, use **Change AniList Entry** on a title in the stats Library. Relinking rewrites the cached art for every episode of that title, and both the detail view and the Library grid pick up the new cover right away: the grid refetches after a relink, and cover responses carry an ETag and are revalidated on each request instead of being cached for a day.
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
All AniList API calls go through a shared rate limiter that enforces a sliding window of 20 requests per minute. The limiter also reads AniList's `X-RateLimit-Remaining` and `Retry-After` response headers and pauses requests when the server signals throttling. This applies to both episode tracking and cover art fetching.
|
||||
@@ -81,7 +83,6 @@ All AniList API calls go through a shared rate limiter that enforces a sliding w
|
||||
"enabled": true,
|
||||
"accessToken": "",
|
||||
"characterDictionary": {
|
||||
"enabled": false,
|
||||
"maxLoaded": 3,
|
||||
"profileScope": "all",
|
||||
"collapsibleSections": {
|
||||
@@ -94,15 +95,17 @@ All AniList API calls go through a shared rate limiter that enforces a sliding w
|
||||
}
|
||||
```
|
||||
|
||||
| Option | Values | Description |
|
||||
| ------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------ |
|
||||
| `enabled` | `true`, `false` | Enable AniList post-watch progress updates (default: `false`) |
|
||||
| `accessToken` | string | Explicit AniList access token override; when blank, SubMiner uses the stored encrypted token (default: `""`) |
|
||||
| `characterDictionary.maxLoaded` | number | Number of recent media snapshots kept in the merged dictionary (default: `3`) |
|
||||
| `characterDictionary.profileScope` | `"all"`, `"active"` | Apply dictionary to all Yomitan profiles or only the active one |
|
||||
| `characterDictionary.collapsibleSections.*` | `true`, `false` | Control which dictionary entry sections start expanded |
|
||||
| Option | Values | Description |
|
||||
| ------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------ |
|
||||
| `enabled` | `true`, `false` | Enable AniList post-watch progress updates (default: `false`) |
|
||||
| `accessToken` | string | Explicit AniList access token override; when blank, SubMiner uses the stored encrypted token (default: `""`) |
|
||||
| `characterDictionary.maxLoaded` | number | Number of recent media snapshots kept in the merged dictionary (default: `3`) |
|
||||
| `characterDictionary.refreshTtlHours` | number | Hours before a cached media snapshot is refreshed (default: `168`, clamped to 1–8760) |
|
||||
| `characterDictionary.evictionPolicy` | `"delete"`, `"disable"` | What happens to snapshots evicted beyond `maxLoaded` (default: `"delete"`) |
|
||||
| `characterDictionary.profileScope` | `"all"`, `"active"` | Apply dictionary to all Yomitan profiles or only the active one |
|
||||
| `characterDictionary.collapsibleSections.*` | `true`, `false` | Control which dictionary entry sections start expanded |
|
||||
|
||||
See the [Character Dictionary](/character-dictionary) page for full details on the character dictionary feature, including name generation, matching, auto-sync lifecycle, and dictionary entry format. Character dictionary sync follows `subtitleStyle.nameMatchEnabled`.
|
||||
There is no `characterDictionary.enabled` key: character dictionary sync is enabled by `subtitleStyle.nameMatchEnabled`. See the [Character Dictionary](/character-dictionary) page for full details on the character dictionary feature, including name generation, matching, auto-sync lifecycle, and dictionary entry format.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
|
||||
@@ -0,0 +1,393 @@
|
||||
# Anime Browser
|
||||
|
||||
Search anime sources, pick an episode, and play it in mpv with SubMiner's overlay
|
||||
and mining tools attached — the same way a local file or a Jellyfin stream works.
|
||||
|
||||
Open it with `subminer anime`, with `SubMiner.AppImage --anime`, or from
|
||||
**Browse Anime** in the tray menu. The window stays open while you watch, so you
|
||||
can queue the next episode without reopening it.
|
||||
|
||||
During playback, `Ctrl+Alt+A` toggles the same browser as a modal inside the mpv
|
||||
player bounds. It uses a dedicated modal surface, so it stays above fullscreen
|
||||
playback and closes like the other in-player tools. Toggling it off keeps its
|
||||
current page and scroll position ready for the next toggle. The standalone
|
||||
window and the modal keep their own search, selected source, tab, and scroll
|
||||
state, so using one does not replace or cancel what you were doing in the other.
|
||||
|
||||
Both surfaces use the same playback queue, source configuration, and stats
|
||||
history. Queue changes and the currently playing episode appear in both
|
||||
immediately, and watched marks come from the same history that playback and the
|
||||
stats window update. Closing and reopening the modal therefore picks up progress
|
||||
made from either browser surface.
|
||||
|
||||
While the window is open, SubMiner shows a tray icon and — on macOS — appears
|
||||
in the Cmd+Tab switcher and the Dock (macOS ties the two together), so you can
|
||||
flip between the browser and mpv. SubMiner normally hides itself from the Dock
|
||||
because the subtitle overlay needs that to float above fullscreen video; it
|
||||
hides again when the window closes during playback.
|
||||
|
||||
Launching an episode starts a full SubMiner playback session, the same as
|
||||
playing a local file: the overlay and mining tools attach, and the tray icon
|
||||
stays available. In standalone `subminer anime` mode, closing the window while
|
||||
a video is playing leaves playback running — reopen the browser from the tray
|
||||
(**Browse Anime**). The app only exits with the window when nothing is playing.
|
||||
|
||||
## How it works
|
||||
|
||||
SubMiner does not implement any anime source itself. It runs **Aniyomi extension
|
||||
APKs** through a bundled JVM sidecar ([M-Extension-Server][mes]), asks the
|
||||
selected extension to resolve an episode, and hands the resulting URL to mpv.
|
||||
|
||||
```
|
||||
extension APK → bridge (JVM) → { url, headers } → mpv → SubMiner overlay
|
||||
```
|
||||
|
||||
Because the extension resolves the stream, whichever sources you install decide
|
||||
what is available. SubMiner only hosts them.
|
||||
|
||||
## Installing extensions
|
||||
|
||||
**SubMiner ships no extension repositories and bundles no sources.** There is no
|
||||
default repository, no suggested list, and no discovery. Until you add one, the
|
||||
browser has nothing to search — that is deliberate, and it is what keeps SubMiner
|
||||
a neutral host rather than a distributor.
|
||||
|
||||
There are two ways to add extensions.
|
||||
|
||||
### From a repository
|
||||
|
||||
The window has three tabs — **Browse**, **Extensions**, and **Source settings** —
|
||||
and each one fills the window, so a long extension list is not squeezed in above
|
||||
the search results.
|
||||
|
||||
Open the **Extensions** tab, paste a repository index URL, and choose
|
||||
**Add repository**. The URL must be `https` and point at a `.json` index file —
|
||||
`index.min.json` is the common Aniyomi name, but repositories are free to publish
|
||||
under another one (for example `video.min.json`). Anything else is rejected
|
||||
immediately rather than failing later. Everything before the file name is treated
|
||||
as the repository root, so `.apk` and icon URLs are resolved relative to it.
|
||||
|
||||
Extensions your repositories offer but you do not have appear under
|
||||
**Available**, each with **Install**. Repositories are stored in config under
|
||||
`anime.repos`, so you can also manage them there and keep them in a dotfile.
|
||||
|
||||
Every row carries the extension's icon, as the repository publishes it, so a
|
||||
site is recognisable before you read the name. A repository row shows its host's
|
||||
favicon instead. Icons are the only part of a row that is fetched from the
|
||||
network, and a row whose icon is missing falls back to the first letter of its
|
||||
name rather than an empty box.
|
||||
|
||||
A repository index lists every language it knows about, which is far more than
|
||||
any one person reads, so the **Available** list has a language chip row above
|
||||
it. Pick one or more languages to narrow it, or **All** to clear the filter;
|
||||
picking a language replaces **All** rather than sitting beside it. Rows name the
|
||||
language in full ("Japanese" rather than `ja`), extensions whose sources span
|
||||
languages are grouped under **Multi-language**, and the Available heading counts
|
||||
how many of the offered extensions the filter leaves.
|
||||
|
||||
### Managing what is installed
|
||||
|
||||
The Extensions tab opens with an **Installed** section listing everything in the
|
||||
extensions directory, with the sources each one provides and a **Remove**
|
||||
button. It is built from the directory rather than from a repository, so an
|
||||
extension you dropped in by hand — or one whose repository you have since
|
||||
removed — is still listed and still removable. An installed extension borrows
|
||||
its icon from the catalogue, so one no repository carries shows its monogram.
|
||||
|
||||
SubMiner compares the APK's Android version code with the newest build in the
|
||||
configured repositories. **Update** is clickable only when the repository has a
|
||||
newer build. A current extension says **Up to date**. If an unusual APK has no
|
||||
readable version code, its disabled button says **Version unknown**. **Update
|
||||
all** installs every newer build in one pass and disables itself when there is
|
||||
nothing to update.
|
||||
|
||||
### From a file
|
||||
|
||||
Drop Aniyomi `.apk` files into the extensions directory, shown at the top of the
|
||||
Extensions tab. It defaults to `<userData>/anime-extensions` — on macOS,
|
||||
`~/Library/Application Support/SubMiner/anime-extensions` — and can be moved with
|
||||
`anime.extensionsDir`.
|
||||
|
||||
A single APK may provide several sources; each appears separately in the
|
||||
**Source** picker. Extensions that fail to load are listed in the Installed
|
||||
section with the reason, so a broken APK is visible rather than silently
|
||||
missing.
|
||||
|
||||
An extension that fails to load is skipped rather than blocking the others, so
|
||||
one bad APK will not hide the rest.
|
||||
|
||||
### Default source
|
||||
|
||||
The browser opens on the first installed source. To open on a different one,
|
||||
press **Set default** on its row in the Installed list; the row then carries a
|
||||
**default** tag, and only one source can carry it. The choice is written to
|
||||
`anime.defaultSource` and applies the next time a browser window or the
|
||||
in-player modal opens. With more than one source installed, an **All sources**
|
||||
row at the top of the list can be the default too. A default that is no longer
|
||||
installed falls back to the first source.
|
||||
|
||||
## Searching every source at once
|
||||
|
||||
With more than one source installed, the **Source** picker gains an
|
||||
**All sources** entry. Searching with it selected runs the query against every
|
||||
installed source at once, and each source's results appear the moment that
|
||||
source answers — a fast source is on screen while a slow one is still
|
||||
resolving. The status bar counts sources as they finish
|
||||
(`Searching… 3/5 sources · 42 results`).
|
||||
|
||||
Each cover is labelled with the source it came from, and opening one always
|
||||
queries that source, whatever the picker says afterwards.
|
||||
|
||||
A source that errors is named in the status bar and the rest still show their
|
||||
results; one extension that needs a login cannot blank the grid. If every
|
||||
source fails, the first error is shown in full.
|
||||
|
||||
Typing a new search while one is still running simply starts over: results
|
||||
from the superseded search are discarded, even if its sources answer late.
|
||||
|
||||
When a source reports another page, **Load more** appears below the covers.
|
||||
It appends the next page without duplicating entries that already arrived in
|
||||
the live result stream. A failed next-page request remains available to retry.
|
||||
|
||||
Source settings belong to a single extension, so the **Source settings** tab
|
||||
asks you to pick one while **All sources** is selected.
|
||||
|
||||
## Finding an episode, and what you have watched
|
||||
|
||||
An episode list can run to hundreds of entries, so the episode header carries a
|
||||
filter box:
|
||||
|
||||
- A number, `12`, keeps that episode. Sources that report no numbers at all
|
||||
are still searched by name, so `12` also matches `Episode 12` in a title.
|
||||
- A range, `12-18`, keeps the episodes between the two, in either order
|
||||
(`18-12` reads the same). Episodes the source gave no number are left out of
|
||||
a range.
|
||||
- Anything else is a case-insensitive substring of the episode name, so `beach`
|
||||
finds `OVA: Beach Special`.
|
||||
|
||||
The counter next to **Episodes** reads `6 of 25` while a filter is applied.
|
||||
Pressing Escape in the filter box clears it; pressing it anywhere else goes back
|
||||
to the results grid.
|
||||
|
||||
Episodes you have already watched are dimmed and marked `✓ watched`, with a
|
||||
count in the header. This is not a separate list the browser keeps: it reads the
|
||||
same stats history the rest of SubMiner writes to, where an episode is marked
|
||||
watched once a session runs past the completion threshold. Streams are recorded
|
||||
under a stable per-episode identity, so the mark survives the stream URL
|
||||
changing between playbacks, and it is the same mark the stats window and
|
||||
`--mark-watched` use.
|
||||
|
||||
Because playback marks an episode partway through the session, the marks
|
||||
refresh when the browser window comes back to the front: finish an episode in
|
||||
mpv, switch back, and it is marked. With
|
||||
[immersion tracking](configuration.md) disabled there is no history to read, so
|
||||
no episode is marked.
|
||||
|
||||
### Marking by hand, and catching up
|
||||
|
||||
Right-click an episode for:
|
||||
|
||||
- **Mark watched** / **Mark unwatched**: the single episode, whichever way it
|
||||
is not already.
|
||||
- **Mark this and N below watched** / **... unwatched**: that episode and every
|
||||
episode listed below it. Sources list newest first, so "below" is the back
|
||||
catalogue: right-click the last episode you saw and mark everything down to
|
||||
the start, which is how you catch up a series you watched somewhere else.
|
||||
|
||||
A filter narrows what you are looking at, not what you mark: a span always
|
||||
covers the full episode list, and the status bar says how many episodes it
|
||||
touched. The oldest episode has nothing below it, so it only offers the single
|
||||
entry. Escape closes the menu.
|
||||
|
||||
Marking an episode you have never played creates its stats row so the mark has
|
||||
somewhere to live. That row carries the same series, season and episode fields
|
||||
playback would have recorded, and both stats library views join the lifetime
|
||||
tables, so a manually marked episode does not appear there as watch time you
|
||||
never spent. Clearing a mark never creates anything.
|
||||
|
||||
Marks are written to the stats history, so with immersion tracking disabled
|
||||
there is nowhere to write them and the status bar says so.
|
||||
|
||||
## Settings
|
||||
|
||||
| Key | Purpose |
|
||||
| ------------------------ | ------------------------------------------------------------------------ |
|
||||
| `anime.autoOpenJimaku` | Pause new episodes and open Jimaku for Japanese subtitles. |
|
||||
| `anime.repos` | Repository index URLs. Empty by default. |
|
||||
| `anime.extensionsDir` | Where APKs are read from. Empty uses `<userData>/anime-extensions`. |
|
||||
| `anime.preferredQuality` | Preferred stream label, matched as a substring (for example `1080`). |
|
||||
| `anime.defaultSource` | Source the browser opens on: a source id or `all`. Empty uses the first. |
|
||||
| `anime.bridgeDir` | A bridge bundle to run instead of the downloaded one. Empty by default. |
|
||||
|
||||
Enable `anime.autoOpenJimaku` to hand each newly loaded Anime Browser episode
|
||||
to Jimaku. SubMiner pauses playback on its first frame, waits for mpv's video
|
||||
window to appear, closes the in-player browser if it is open, hides the
|
||||
standalone browser window, and opens Jimaku with the source title, season, and
|
||||
episode already filled in. Playback resumes after the selected subtitle loads.
|
||||
Closing Jimaku also releases the automatic pause, while playback that was
|
||||
already paused stays paused. A stream that never shows a window resumes without
|
||||
opening Jimaku. Reopen the browser window with `subminer anime`, the tray
|
||||
entry, or the shortcut to keep browsing.
|
||||
|
||||
## Source settings
|
||||
|
||||
Most extensions need configuration before they return anything — a server
|
||||
address and credentials, a preferred quality, a language filter. Open the
|
||||
**Source settings** tab to edit them. Changes save as you make them and
|
||||
persist across restarts in `<userData>/anime-source-preferences.json`.
|
||||
|
||||
Each save is handed back to the extension, so it can react: the Jellyfin source
|
||||
logs in when the address and password land, then fills in its media-library
|
||||
picker. Password-like fields are masked. Because that file can hold
|
||||
credentials, it is written with owner-only permissions. Values are scoped to
|
||||
the exact extension package and source, so two extensions that reuse the same
|
||||
internal source ID cannot read each other's settings. Preferences saved by an
|
||||
older build without package ownership are discarded; re-enter those source
|
||||
settings once after upgrading.
|
||||
|
||||
## The bridge
|
||||
|
||||
SubMiner looks for the server in three places, in order, and runs the first
|
||||
one it finds:
|
||||
|
||||
1. `anime.bridgeDir`, if set. It must hold a Java runtime and the
|
||||
`MExtensionServer-*.jar`, laid out as upstream ships them. A directory that
|
||||
holds neither is an error rather than a silent fallback.
|
||||
2. A package-manager install. On Arch that is the AUR
|
||||
[`mangatan-extension-server`](https://aur.archlinux.org/packages/mangatan-extension-server)
|
||||
package, which Mangatan also uses, at `/usr/share/mangatan/extension_server`.
|
||||
Installing it means no download, and pacman keeps the bridge current. The
|
||||
`subminer-bin` package lists it as an optional dependency.
|
||||
3. SubMiner's own copy in `<userData>/anime-bridge`. The first launch downloads
|
||||
the newest upstream release that ships a bundle for your platform (~130 MB,
|
||||
containing the server and a matching Java runtime, so no system JDK is
|
||||
required), the same way Mangatan does. Releases older than the oldest server
|
||||
SubMiner is known to work with are skipped. Progress appears in the banner
|
||||
at the top of the window.
|
||||
|
||||
The **Bridge** note at the bottom of the Extensions tab says which one is in
|
||||
use, its version, and who updates it.
|
||||
|
||||
### Updating the bridge
|
||||
|
||||
Only the copy SubMiner downloaded is ever updated by SubMiner. A package-manager
|
||||
install or an `anime.bridgeDir` belongs to whoever put it there.
|
||||
|
||||
SubMiner records the release it installed and, once the bridge is running,
|
||||
asks GitHub for the newest one. When upstream has published a newer release,
|
||||
the banner reads "Extension bridge v… is installed; v… is available" with an
|
||||
**Update to v…** button. Clicking it downloads the new release beside the
|
||||
running bridge, so a failed download changes nothing, then stops the bridge,
|
||||
swaps the directories, and starts it again. The restart takes a few seconds
|
||||
and kills the stream of an episode that is playing, the same as when the
|
||||
bridge exits for any other reason; the queue and browser state survive. The
|
||||
check is one unauthenticated GitHub API call per bridge start; if it fails
|
||||
(offline, rate limited) it is logged and no update is offered until the next
|
||||
start.
|
||||
|
||||
Upstream publishes no checksums, so the download is trusted the way Mangatan
|
||||
and the AUR package trust it: TLS to GitHub and the maintainer's account. That
|
||||
is the same trust running the server implies in the first place.
|
||||
|
||||
The bridge stays running while the window is open. Resolved video URLs point at
|
||||
its own loopback proxy so the extension's cookies and headers apply, which means
|
||||
those URLs stop working once it exits — the window keeps it alive for the whole
|
||||
session.
|
||||
|
||||
If the bridge dies anyway (killed by hand, crashed, or stopped mid-operation),
|
||||
the exit is detected and named in the status bar, and the next request starts a
|
||||
new one. Playback already in flight still ends when its stream URL dies, but the
|
||||
browser recovers without an app restart.
|
||||
|
||||
Two known limits:
|
||||
|
||||
- There is no Android WebView, so extensions that need one (typically for
|
||||
Cloudflare challenges) will fail with an error from the source.
|
||||
- Bundles are published for macOS (arm64, x64), Linux (x64), and Windows (x64).
|
||||
Other platforms are unsupported outright.
|
||||
|
||||
## Playback
|
||||
|
||||
Selecting an episode resolves the best available stream, applies the source's
|
||||
required headers as mpv `http-header-fields`, and loads it. The headers are
|
||||
readable back off mpv, so Anki card audio and screenshots fetch correctly too.
|
||||
|
||||
HLS streams are routed through a small local proxy before mpv sees them. Some
|
||||
hosts disguise their video segments by prepending a fake image header (a real
|
||||
1x1 PNG) so scrapers back off; Aniyomi's own player strips this, but ffmpeg
|
||||
probes the segment as a picture and playback dies with "no audio or video data
|
||||
played". The proxy scans each segment for the first genuine MPEG-TS packet run
|
||||
and drops whatever junk sits in front of it. Segments that are not TS (fMP4,
|
||||
subtitles, encryption keys) pass through untouched, and direct-file streams
|
||||
skip the proxy entirely. Segment URLs disguised behind fake extensions
|
||||
(`.image`, `.jpg`, `.css`, and friends) are exposed locally with a `.ts`
|
||||
suffix so current ffmpeg releases accept them when Anki extracts audio,
|
||||
screenshots, or animated images from the playing stream.
|
||||
|
||||
"Playing" in the status bar means playing: after handing mpv the stream,
|
||||
SubMiner waits until mpv actually configures a video output before reporting
|
||||
success. If mpv gives up instead — a dead host, an undecodable stream — the
|
||||
browser shows mpv's error rather than pretending playback started (a failed
|
||||
load leaves no mpv window, because the player idles windowless).
|
||||
|
||||
Choosing **Queue** resolves the episode and appends the playable stream to mpv's
|
||||
own playlist immediately, while its subtitle tracks cache in the background.
|
||||
That makes mpv's next command available at once and lets the next episode begin
|
||||
automatically when the current one ends, without waiting for another source
|
||||
request. Resolved stream URLs can be short-lived, so a very long queue can still
|
||||
outlive what its source issued; dequeue and queue that episode again to refresh
|
||||
it.
|
||||
|
||||
### Japanese audio, and switching tracks
|
||||
|
||||
Sources often return a dub and the original audio as two separate entries — or
|
||||
as two audio tracks of one stream — and the dub is frequently listed first.
|
||||
SubMiner always aims at the Japanese audio:
|
||||
|
||||
- Entries labelled as a dub are skipped as long as another entry exists. This
|
||||
outranks `anime.preferredQuality`: a 1080p dub is the wrong file, not a better
|
||||
one. If every entry is a dub, it still plays.
|
||||
- mpv's `alang` is set to `ja,jpn,jp,japanese` before the file loads, so a
|
||||
stream carrying several audio tracks starts on the Japanese one. With no
|
||||
Japanese track, mpv falls back to the first one as usual.
|
||||
- Any audio or subtitle tracks the extension supplies separately are added to
|
||||
mpv with `audio-add` / `sub-add`, tagged with their language, and the
|
||||
Japanese one is selected.
|
||||
|
||||
The primary subtitle slot is reserved for Japanese — it is what the overlay
|
||||
mines. A source that only carries, say, English subtitles does not get them
|
||||
promoted to primary; instead the track is added with a normalized language tag
|
||||
(`English` → `en`), and the regular [dual-subtitle settings](configuration.md)
|
||||
apply: with `secondarySub.autoLoadSecondarySub` enabled and the language listed
|
||||
in `secondarySub.secondarySubLanguages`, it is picked up as the secondary
|
||||
subtitle, exactly as it would be for a local file.
|
||||
|
||||
Every track is added, including the ones that are not selected, so all of them
|
||||
appear in mpv's track menu and can be switched by hand while watching
|
||||
(`#` cycles audio, `j` cycles subtitles by default).
|
||||
|
||||
The extension hands over subtitle tracks as URLs, but SubMiner downloads each
|
||||
one to a temporary directory and gives mpv the local file. mpv is happy either
|
||||
way; [Subsync](/troubleshooting#subtitle-sync-subsync) is not, because alass
|
||||
needs a file on disk to use as the timing reference. A track that fails to
|
||||
download falls back to its URL so the episode still plays, the format is
|
||||
detected from the file's own content rather than its URL, and the directory is
|
||||
removed when the next episode starts or the app exits.
|
||||
|
||||
### Series, season, and episode
|
||||
|
||||
The episode's identity travels with it instead of being guessed back out of the
|
||||
stream URL, which carries nothing but a proxy path and a file extension. The
|
||||
title and episode label from the source's own listing are split into series,
|
||||
season, and episode number once, at launch, and everything downstream reads
|
||||
those fields:
|
||||
|
||||
- mpv's title reads `Series S03E04 - Episode Name`.
|
||||
- Stats group by series, and rewatching an episode reuses its entry instead of
|
||||
creating a new one.
|
||||
- The [Jimaku](/jimaku-integration) and [TsukiHime](/tsukihime-integration)
|
||||
modals open with Title, Season, and Episode already filled in, so a subtitle
|
||||
search is one keypress rather than a retype.
|
||||
- [AniList](/anilist-integration) progress updates use those fields directly.
|
||||
|
||||
[mes]: https://github.com/1Selxo/M-Extension-Server
|
||||
@@ -0,0 +1,53 @@
|
||||
# AniSkip Integration
|
||||
|
||||
SubMiner integrates with [AniSkip](https://aniskip.com) to automatically detect anime intro intervals and let you skip them with a single key press.
|
||||
|
||||
Intro detection runs in the SubMiner app over the mpv IPC socket. It is available whenever the overlay is connected to mpv - not just at launch - and covers every local file loaded during an mpv session, including playlist advances.
|
||||
|
||||
## Setup
|
||||
|
||||
AniSkip is enabled by default. Disable it or change the skip key in your config:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"mpv": {
|
||||
"aniskipEnabled": true, // default: true
|
||||
"aniskipButtonKey": "TAB",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Both settings hot-reload: changing them in your config takes effect immediately without restarting playback or mpv.
|
||||
|
||||
For best title and episode detection, install [`guessit`](https://github.com/guessit-io/guessit):
|
||||
|
||||
```bash
|
||||
python3 -m pip install --user guessit
|
||||
```
|
||||
|
||||
Without `guessit`, SubMiner falls back to an internal filename parser which handles most common naming conventions but may miss unusual formats.
|
||||
|
||||
## How It Works
|
||||
|
||||
On each local file load:
|
||||
|
||||
1. SubMiner infers the anime title, season, and episode number from the filename and path (using `guessit` if available, otherwise the built-in parser). Remote URLs are skipped entirely.
|
||||
2. The title is matched against MyAnimeList to resolve a MAL id.
|
||||
3. SubMiner queries the AniSkip API for an OP skip interval for that MAL id and episode.
|
||||
4. If an interval is found, SubMiner adds `AniSkip Intro Start` and `AniSkip Intro End` chapter markers to the current file and binds the skip key (`mpv.aniskipButtonKey`, default `TAB`).
|
||||
5. At the start of the intro, an OSD prompt appears for 3 seconds: `You can skip by pressing TAB` (reflects your configured key). Pressing the key at any point during the intro seeks to the intro end.
|
||||
|
||||
When a custom key (other than `TAB` or `y-k`) is configured, the legacy `y-k` chord is also bound as a fallback skip trigger.
|
||||
|
||||
Results are cached per file for the app session; only definitive "no intro found" results are cached, so transient lookup failures are retried on the next file load. Reload detection is also handled: if mpv reloads the same file, SubMiner re-applies the chapter markers without a new API lookup.
|
||||
|
||||
## Triggering from mpv
|
||||
|
||||
You can trigger AniSkip actions from mpv script-messages:
|
||||
|
||||
| Command | Effect |
|
||||
| ------- | ------ |
|
||||
| `script-message subminer-skip-intro` | Skip to the intro end immediately (same as pressing the key) |
|
||||
| `script-message subminer-aniskip-refresh` | Force a fresh lookup for the current file, discarding any cached result |
|
||||
|
||||
These are handled by the SubMiner app over the IPC socket.
|
||||
@@ -4,11 +4,12 @@ SubMiner uses the [AnkiConnect](https://ankiweb.net/shared/info/2055492159) add-
|
||||
This project is built primarily for [Kiku](https://kiku.youyoumu.my.id/) and [Lapis](https://github.com/donkuri/lapis) note types, including sentence-card and field-grouping behavior.
|
||||
|
||||
::: tip New to these terms?
|
||||
|
||||
- **Anki** is the flashcard app where your study cards live.
|
||||
- **AnkiConnect** is a free add-on that lets other programs (like SubMiner) talk to Anki over a local connection. SubMiner needs it installed to add or edit cards.
|
||||
- A **note type** (also called a "model") is the template that defines what a card looks like — for example the Kiku or Lapis templates many Japanese learners use.
|
||||
- A **note type** (also called a "model") is the template that defines what a card looks like - for example the Kiku or Lapis templates many Japanese learners use.
|
||||
- A **field** is one labeled slot in that template, such as `Sentence`, `Expression`, or `Picture`. SubMiner fills these fields when it mines a card.
|
||||
:::
|
||||
:::
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -22,9 +23,9 @@ AnkiConnect listens on `http://127.0.0.1:8765` by default. If you changed the po
|
||||
|
||||
When you add a word via Yomitan, SubMiner detects the new card and fills in the sentence, audio, image, and translation fields automatically. Two detection methods are available:
|
||||
|
||||
**Proxy mode** (default) — SubMiner runs a local *proxy*: a small middleman server that sits between Yomitan and Anki. Yomitan sends new cards to SubMiner, SubMiner enriches them, then passes them along to Anki. This makes enrichment instant.
|
||||
**Proxy mode** (default) - SubMiner runs a local _proxy_: a small middleman server that sits between Yomitan and Anki. Yomitan sends new cards to SubMiner, SubMiner enriches them, then passes them along to Anki. This makes enrichment instant.
|
||||
|
||||
**Polling mode** (fallback, when the proxy is disabled) — SubMiner asks AnkiConnect every few seconds whether any new cards were added, then enriches them. Simpler setup, but with a short delay (~3 seconds).
|
||||
**Polling mode** (fallback, when the proxy is disabled) - SubMiner asks AnkiConnect every few seconds whether any new cards were added, then enriches them. Simpler setup, but with a short delay (~3 seconds).
|
||||
|
||||
Use proxy mode if you want immediate enrichment. Use polling mode if your Yomitan instance is external (browser-based) or you prefer minimal configuration.
|
||||
|
||||
@@ -36,7 +37,7 @@ In both modes, the enrichment workflow is the same:
|
||||
4. Fills the translation field from the secondary subtitle or AI.
|
||||
5. Writes metadata to the miscInfo field.
|
||||
|
||||
Polling mode uses the query `"deck:<ankiConnect.deck>" added:1` to find recently added cards. If no deck is configured, it searches all decks. In Settings, the AnkiConnect deck dropdown auto-fills from Yomitan's current mining deck when available, then falls back to the decks reported by AnkiConnect.
|
||||
Polling mode uses the query `"deck:<ankiConnect.deck>" added:1` to find recently added cards. If no deck is configured, it searches all decks (`added:1`). In Settings, the AnkiConnect deck dropdown auto-fills and persists Yomitan's current mining deck when available, then falls back to the decks reported by AnkiConnect; stats-dashboard mining also falls back to Yomitan's mining deck when `ankiConnect.deck` is empty.
|
||||
Known-word sync scope is controlled by `ankiConnect.knownWords.decks`.
|
||||
|
||||
### Proxy Mode Setup (Yomitan / Texthooker)
|
||||
@@ -55,12 +56,12 @@ Known-word sync scope is controlled by `ankiConnect.knownWords.decks`.
|
||||
|
||||
Then point Yomitan/clients to `http://127.0.0.1:8766` instead of `8765`.
|
||||
|
||||
When SubMiner loads the bundled Yomitan extension, it also attempts to update the **default Yomitan profile** (`profiles[0].options.anki.server`) to the active SubMiner endpoint:
|
||||
When SubMiner loads the bundled Yomitan extension, it also attempts to update the **currently active Yomitan profile**'s Anki server to the active SubMiner endpoint (falling back to `profiles[0]` if the active-profile index is invalid):
|
||||
|
||||
- proxy URL when `ankiConnect.proxy.enabled` is `true`
|
||||
- direct `ankiConnect.url` when proxy mode is disabled
|
||||
|
||||
To avoid clobbering custom setups, this auto-update only changes the default profile when its current server is blank or the stock Yomitan default (`http://127.0.0.1:8765`).
|
||||
To avoid clobbering custom setups, this auto-update only changes the profile when its current server is blank or the stock Yomitan default (`http://127.0.0.1:8765`).
|
||||
|
||||
For browser-based Yomitan or other external clients (for example Texthooker in a normal browser profile), set their Anki server to the same proxy URL separately: `http://127.0.0.1:8766` (or your configured `proxy.host` + `proxy.port`).
|
||||
|
||||
@@ -80,7 +81,7 @@ In Yomitan, go to Settings → Profile and:
|
||||
3. Set server to `http://127.0.0.1:8766` (or your configured proxy URL).
|
||||
4. Save and make that profile active when using SubMiner.
|
||||
|
||||
This is only for non-bundled, external/browser Yomitan or other clients. The bundled profile auto-update logic only targets `profiles[0]` when it is blank or still default.
|
||||
This is only for non-bundled, external/browser Yomitan or other clients. The bundled profile auto-update logic only targets the active profile when its server is blank or still default.
|
||||
|
||||
### Proxy Troubleshooting (quick checks)
|
||||
|
||||
@@ -100,10 +101,11 @@ curl -sS http://127.0.0.1:8766 \
|
||||
-d '{"action":"version","version":2}'
|
||||
```
|
||||
|
||||
3. Check both log sinks:
|
||||
3. Check the log sinks in `~/.config/SubMiner/logs/`:
|
||||
|
||||
- Launcher/mpv-integrated log: `~/.cache/SubMiner/mp.log`
|
||||
- App runtime log: `~/.config/SubMiner/logs/SubMiner-YYYY-MM-DD.log`
|
||||
- App runtime log: `app-YYYY-MM-DD.log`
|
||||
- Launcher log: `launcher-YYYY-MM-DD.log`
|
||||
- mpv log: `mpv-YYYY-MM-DD.log`
|
||||
|
||||
4. Ensure config JSONC is valid and logging shape is correct:
|
||||
|
||||
@@ -132,7 +134,11 @@ SubMiner maps its data to your Anki note fields. Configure these under `ankiConn
|
||||
}
|
||||
```
|
||||
|
||||
Field names must match your Anki note type exactly (case-sensitive). If a configured field does not exist on the note type, SubMiner skips it without error.
|
||||
Field names are matched against your Anki note type case-insensitively (an exact match wins, then a lowercase comparison). If a configured field does not exist on the note type, SubMiner skips it without error.
|
||||
|
||||
These mappings always control normal word-card enrichment, including Yomitan proxy/polling updates and manual clipboard updates. Enabling Lapis or Kiku does not replace the configured word-card sentence and audio fields with `Sentence` and `SentenceAudio`. The dedicated sentence-card and audio-card shortcuts still use those Lapis/Kiku field names.
|
||||
|
||||
Two related options live alongside `fields`: `ankiConnect.deck` (target deck; empty falls back as described above) and `ankiConnect.tags` (tags added to mined cards, default `["SubMiner"]`; set `[]` to disable tagging). The `miscInfo` content is controlled by `ankiConnect.metadata.pattern` (default `[SubMiner] %f (%t)`; tokens: `%f` filename, `%F` filename with extension, `%t` timestamp, `%T` timestamp with milliseconds, `<br>` newline).
|
||||
|
||||
### Minimal Config
|
||||
|
||||
@@ -160,13 +166,17 @@ Audio is extracted from the video file using the subtitle's start and end timest
|
||||
"ankiConnect": {
|
||||
"media": {
|
||||
"generateAudio": true,
|
||||
"normalizeAudio": true, // normalize generated clip loudness
|
||||
"mirrorMpvVolume": true, // apply the current mpv volume level
|
||||
"audioPadding": 0, // optional seconds before and after subtitle timing
|
||||
"maxMediaDuration": 30 // cap total duration in seconds
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Output format: MP3 at 44100 Hz. If the video has multiple audio streams, SubMiner uses the active stream.
|
||||
Output format: MP3 at 44100 Hz. If the video has multiple audio streams, SubMiner uses the active stream. Generated sentence audio is loudness-normalized to -23 LUFS by default during extraction; set `normalizeAudio` to `false` to keep raw source loudness. When subtitle timing is missing, clips fall back to `media.fallbackDuration` seconds (default `3`). Changing these settings applies to the next extraction without restarting SubMiner.
|
||||
|
||||
`mirrorMpvVolume` is also enabled by default. Immediately before extracting each playback-overlay card's audio, SubMiner reads mpv's numeric `volume` and applies mpv's cubic software-volume curve after loudness normalization. For example, mpv volume `50` produces `0.5³ = 0.125` gain. Amplified output above mpv volume `100` is limited to a `-1 dBFS` ceiling before MP3 encoding to prevent clipping. It ignores mpv's separate `mute` state. If the volume property is missing, invalid, or unavailable, extraction continues with unity scaling; disabling this option skips the query and volume filter. Changing this setting applies to the next extraction without restarting SubMiner. YouTube cards queued for a background media-cache download retain the volume captured when the card was mined. Stats-dashboard mining does not currently have access to the active mpv property client, so it does not apply mpv volume scaling.
|
||||
|
||||
The audio is uploaded to Anki's media folder and inserted as `[sound:audio_<timestamp>.mp3]`.
|
||||
|
||||
@@ -181,8 +191,8 @@ A single frame is captured at the current playback position.
|
||||
"imageType": "static",
|
||||
"imageFormat": "jpg", // "jpg", "png", or "webp"
|
||||
"imageQuality": 92, // 1–100
|
||||
"imageMaxWidth": null, // optional, preserves aspect ratio
|
||||
"imageMaxHeight": null
|
||||
"imageMaxWidth": 0, // 0 = preserve source resolution
|
||||
"imageMaxHeight": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -198,13 +208,13 @@ Instead of a static screenshot, SubMiner can generate an animated AVIF covering
|
||||
"imageType": "avif",
|
||||
"animatedFps": 10,
|
||||
"animatedMaxWidth": 640,
|
||||
"animatedMaxHeight": null,
|
||||
"animatedMaxHeight": 0, // 0 = preserve aspect ratio
|
||||
"animatedCrf": 35 // 0–63, lower = better quality
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Animated AVIF requires an AV1 encoder (`libaom-av1`, `libsvtav1`, or `librav1e`) in your FFmpeg build. Generation timeout is 60 seconds.
|
||||
Animated AVIF requires an AV1 encoder (`libaom-av1`, `libsvtav1`, or `librav1e`) in your FFmpeg build. Generation timeout is 60 seconds. `media.syncAnimatedImageToWordAudio` (default `true`) prepends a frozen first frame matching the existing word-audio duration, so the motion starts together with the sentence audio.
|
||||
|
||||
### Behavior Options
|
||||
|
||||
@@ -215,12 +225,17 @@ Animated AVIF requires an AV1 encoder (`libaom-av1`, `libsvtav1`, or `librav1e`)
|
||||
"overwriteImage": true, // replace existing image, or append
|
||||
"mediaInsertMode": "append", // "append" or "prepend" to field content
|
||||
"autoUpdateNewCards": true, // auto-update when new card detected
|
||||
"notificationType": "osd" // "osd", "system", "both", or "none"
|
||||
"highlightWord": true, // bold the mined word inside the sentence field
|
||||
"notificationType": "overlay" // "overlay", "system", "both", or "none"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`overwriteAudio` applies to automatic card updates and duplicate-card enrichment. Manual clipboard subtitle updates (`Ctrl/Cmd+C`, then `Ctrl/Cmd+V`) always replace generated sentence audio, while leaving the word audio field unchanged.
|
||||
`both` now means overlay + system notification. `osd` and `osd-system` are legacy config-file-only values; set `notificationType` to `"osd-system"` in `config.jsonc` if you previously used `both` and want to keep mpv OSD + system notifications. The Settings window shows `osd` or `osd-system` when already configured, but only offers `overlay`, `system`, `both`, and `none` as normal choices.
|
||||
|
||||
When media is available, mined-card overlay and system notifications include the same current-frame thumbnail.
|
||||
|
||||
`overwriteAudio` applies to automatic card updates and duplicate-card enrichment. Manual clipboard subtitle updates (`Ctrl/Cmd+C`, then `Ctrl/Cmd+V`) always replace generated sentence audio in `ankiConnect.fields.audio`, even when `overwriteAudio` is disabled.
|
||||
|
||||
## AI Translation
|
||||
|
||||
@@ -260,25 +275,42 @@ The built-in translation request asks for English output by default. Customize t
|
||||
SubMiner can create standalone sentence cards (without a word/expression) using a separate note type. This is designed for use with [Lapis](https://github.com/donkuri/Lapis) and similar sentence-focused note types.
|
||||
|
||||
::: warning Required config
|
||||
Sentence card creation and audio card marking both require `ankiConnect.isLapis.enabled: true` and a valid `sentenceCardModel` pointing to your Lapis/Kiku note type. Without this, the `Ctrl/Cmd+S` and `Ctrl/Cmd+Shift+A` shortcuts will not create cards.
|
||||
Sentence card creation and audio card marking require a non-empty `ankiConnect.isLapis.sentenceCardModel` naming a note type that exists in Anki (default: `"Lapis"`). If the model is empty or missing, the `Ctrl/Cmd+S` and `Ctrl/Cmd+Shift+A` shortcuts will not create cards.
|
||||
:::
|
||||
|
||||
```jsonc
|
||||
"ankiConnect": {
|
||||
"isLapis": {
|
||||
"enabled": true,
|
||||
"sentenceCardModel": "Japanese sentences"
|
||||
"sentenceCardModel": "Lapis" // default; point at your Lapis/Kiku note type
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Trigger with the mine sentence shortcut (`Ctrl/Cmd+S` by default). The card is created directly via AnkiConnect with the sentence, audio, and image filled in.
|
||||
|
||||
The dedicated sentence-card and audio-card shortcuts use the Lapis/Kiku-compatible `Sentence` and `SentenceAudio` fields. This does not affect the configured fields used to enrich normal word cards.
|
||||
|
||||
To mine multiple subtitle lines as one sentence card, use `Ctrl/Cmd+Shift+S` followed by a digit (1–9) to select how many recent lines to combine.
|
||||
|
||||
## Field Grouping (Kiku)
|
||||
## Word Card Type (Kiku/Lapis)
|
||||
|
||||
When you mine the same word multiple times, SubMiner can merge the cards instead of creating duplicates. This is designed for note types like [Kiku](https://github.com/youyoumu/kiku) that support grouped sentence/audio/image fields.
|
||||
Word cards get a card-type flag when SubMiner fills their sentence, whether that comes from Yomitan auto-enrichment, a manual clipboard update, or stats-dashboard word mining. By default the flag is `IsWordAndSentenceCard`; pick a different one with `ankiConnect.lapisKiku.wordCardKind`.
|
||||
|
||||
```jsonc
|
||||
"ankiConnect": {
|
||||
"isKiku": { "enabled": true },
|
||||
"lapisKiku": {
|
||||
"wordCardKind": "click" // word-and-sentence (default), click, sentence, audio, none
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`click` marks `IsClickCard`, `sentence` marks `IsSentenceCard`, `audio` marks `IsAudioCard`, and `none` leaves the flags untouched for templates that manage them elsewhere. Whichever flag is chosen, the other card-type flags are cleared so the note never claims two card types. The setting is only read when `isKiku` or `isLapis` is enabled, and cards mined with Mine Sentence or Mine Audio keep their own flag.
|
||||
|
||||
## Field Grouping (Kiku/Senren)
|
||||
|
||||
When you mine the same word multiple times, SubMiner can merge the cards instead of creating duplicates. This is designed for note types that support grouped fields: [Kiku](https://github.com/youyoumu/kiku) and [Senren](https://github.com/BrenoAqua/Senren) (which calls the feature scene switching).
|
||||
|
||||
```jsonc
|
||||
"ankiConnect": {
|
||||
@@ -290,29 +322,47 @@ When you mine the same word multiple times, SubMiner can merge the cards instead
|
||||
}
|
||||
```
|
||||
|
||||
For Senren note types, enable `isSenren` instead. Kiku and Senren write incompatible markup into the same fields, so only one can be enabled at a time; if both are enabled, Kiku wins and a config warning is emitted.
|
||||
|
||||
```jsonc
|
||||
"ankiConnect": {
|
||||
"isSenren": {
|
||||
"enabled": true,
|
||||
"fieldGrouping": "auto", // "auto" (default), "manual", or "disabled"
|
||||
"deleteDuplicateInAuto": true // delete new card after auto-merge
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Modes
|
||||
|
||||
**Disabled** (`"disabled"`): No duplicate detection. Each card is independent.
|
||||
|
||||
**Auto** (`"auto"`): When a duplicate expression is found, SubMiner merges the new card into the existing one automatically. Both sentences, audio clips, and images are preserved, and exact duplicate values are collapsed to one entry. If `deleteDuplicateInAuto` is true, the new card is deleted after merging.
|
||||
**Auto** (`"auto"`): When a duplicate expression is found, SubMiner merges the new card into the existing one automatically. Both cards' sentences, audio clips, and images are preserved as grouped entries. If `deleteDuplicateInAuto` is true, the new card is deleted after merging.
|
||||
|
||||
**Manual** (`"manual"`): A modal appears in the overlay showing both cards. You choose which card to keep, preview the merge result, then confirm. The modal has a 90-second timeout, after which it cancels automatically.
|
||||
|
||||
### What Gets Merged
|
||||
|
||||
| Field | Merge behavior |
|
||||
| -------- | --------------------------------------------------------------- |
|
||||
| Sentence | Both sentences preserved (exact duplicate text is deduplicated) |
|
||||
| Audio | Both `[sound:...]` entries kept (exact duplicates deduplicated) |
|
||||
| Image | Both images kept (exact duplicates deduplicated) |
|
||||
| Field | Merge behavior |
|
||||
| -------- | --------------------------------------------- |
|
||||
| Sentence | Both cards' sentences kept as grouped entries |
|
||||
| Audio | Both cards' `[sound:...]` entries kept |
|
||||
| Image | Both cards' images kept |
|
||||
| MiscInfo | Both cards' source info kept as grouped entries |
|
||||
|
||||
Identical values from both cards are kept as separate grouped entries; the merge does not deduplicate.
|
||||
|
||||
The merge markup depends on the note type. Kiku entries are wrapped in `<span data-group-id="...">` spans ordered newest first. Senren entries follow the [scene switching](https://github.com/BrenoAqua/Senren/blob/main/docs/scene_switching.md) format: sentence, sentenceFurigana, and miscInfo entries use `group` spans when ordinal order is sufficient and numbered `groupN` spans when they need an absolute scene target. Audio and pictures are appended positionally, and the number of sentenceAudio entries drives Senren's scene count. Ungrouped legacy content is wrapped into a group span on first merge, and source `groupN` spans are rebased after the kept note's existing audio scenes.
|
||||
|
||||
### Keyboard Shortcuts in the Modal
|
||||
|
||||
| Key | Action |
|
||||
| --------- | ---------------------------------- |
|
||||
| `1` / `2` | Select card 1 or card 2 to keep |
|
||||
| `Enter` | Confirm selection |
|
||||
| `Esc` | Cancel (keep both cards unchanged) |
|
||||
| Key | Action |
|
||||
| ----------- | ---------------------------------- |
|
||||
| `1` / `2` | Select card 1 or card 2 to keep |
|
||||
| `Enter` | Confirm selection |
|
||||
| `Backspace` | Go back from the merge preview |
|
||||
| `Esc` | Cancel (keep both cards unchanged) |
|
||||
|
||||
## Full Config Example
|
||||
|
||||
@@ -322,8 +372,10 @@ When you mine the same word multiple times, SubMiner can merge the cards instead
|
||||
"enabled": true,
|
||||
"url": "http://127.0.0.1:8765",
|
||||
"pollingRate": 3000,
|
||||
"deck": "",
|
||||
"tags": ["SubMiner"],
|
||||
"proxy": {
|
||||
"enabled": false,
|
||||
"enabled": true, // default
|
||||
"host": "127.0.0.1",
|
||||
"port": 8766,
|
||||
"upstreamUrl": "http://127.0.0.1:8765",
|
||||
@@ -342,6 +394,8 @@ When you mine the same word multiple times, SubMiner can merge the cards instead
|
||||
"imageType": "static",
|
||||
"imageFormat": "jpg",
|
||||
"imageQuality": 92,
|
||||
"normalizeAudio": true,
|
||||
"mirrorMpvVolume": true,
|
||||
"audioPadding": 0,
|
||||
"maxMediaDuration": 30,
|
||||
},
|
||||
@@ -350,12 +404,15 @@ When you mine the same word multiple times, SubMiner can merge the cards instead
|
||||
"overwriteImage": true,
|
||||
"mediaInsertMode": "append",
|
||||
"autoUpdateNewCards": true,
|
||||
"notificationType": "osd",
|
||||
"notificationType": "overlay",
|
||||
},
|
||||
"metadata": {
|
||||
"pattern": "[SubMiner] %f (%t)",
|
||||
},
|
||||
"ai": {
|
||||
"enabled": false,
|
||||
"model": "openai/gpt-4o-mini",
|
||||
"systemPrompt": "Translate mined sentence text only.",
|
||||
"model": "", // e.g. "openai/gpt-4o-mini"
|
||||
"systemPrompt": "",
|
||||
},
|
||||
"isKiku": {
|
||||
"enabled": false,
|
||||
@@ -364,7 +421,7 @@ When you mine the same word multiple times, SubMiner can merge the cards instead
|
||||
},
|
||||
"isLapis": {
|
||||
"enabled": false,
|
||||
"sentenceCardModel": "Japanese sentences",
|
||||
"sentenceCardModel": "Lapis",
|
||||
},
|
||||
},
|
||||
"ai": {
|
||||
|
||||