Commit Graph
232 Commits
Author SHA1 Message Date
sudacode 269c0708cb feat(stats): overhaul the Trends tab (#325) 2026-11-04 21:55:18 -08:00
sudacode f89f78b861 refactor(main): move plugins, session actions and subtitle generation into assemblies (#327)
* refactor(main): build overlay geometry and pointer interaction in the overlay assembly

Inject Electron's screen (and the stats-window promotion) into the overlay geometry and
visible-overlay interaction runtimes so they load under bun test, then construct both in
createOverlayAssembly together with the overlay visibility composer and the mpv subtitle
suppression handlers. Replace the three regex tests that pinned the moved Linux input and
shape code with behaviour tests on the runtimes.

* refactor(main): move the autoplay-ready gate into the playback assembly

The autoplay-ready gate, its post-warm subtitle prime, the current-media tokenization gate and
managed local subtitle selection all read playback state, and playback signals through the
gate, so createPlaybackAssembly now builds them. The overlay reaches the gate through a getter.

Replace the last four regex guards with behaviour tests: the gate holds the release until the
visible overlay has measured the line and ignores untokenized lines during warmup (playback),
cached autoplay priming never paints raw first and the sidebar media path is tagged only after
prefetch loads (priming runtime), and dashboard mining overrides the Hachidori Anki server only
when the proxy is on (stats server runtime). main-wiring.test.ts is now empty and removed.

* refactor(main): move the plugin host, panel and windows into a plugins assembly

* refactor(main): move session action dispatch and renderer mpv commands into an assembly

createSessionActionsAssembly owns the session action dispatcher, the secondary-subtitle mode cycle, queueing the clipboard's video, and the mpv commands the overlay renderer sends. The runtime option cycle both paths used is now one function.

* refactor(main): move Whisper subtitle generation into an assembly

* refactor(main): resolve subtitle annotation options in a runtime module

The runtime-option-over-config resolution that the texthooker page and the MeCab startup decision share moves out of main.ts into createAnnotationOptions.

* test(main): cover a non-boolean runtime annotation option value

* docs: describe the plugins, session actions and subtitle generation assemblies

Update ownership for overlay geometry and pointer interaction, the autoplay-ready gate, and annotation options; point the overlay priming entry points at their new modules; the main-wiring regex debt is gone.

* build: export MpvClientEvents so declaration emit can name the plugins assembly's return type
2026-11-04 21:54:49 -08:00
YashaNoosandClaude Opus 5.5 14ef7e823c Controller: character navigation in keyboard-only mode, plain Xbox face names (#326)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-11-04 21:54:00 -08:00
33f5f890d5 feat(jimaku): auto-open modal when a video has no Japanese subtitle (#319)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Ivan Melck <ivan@spec-kitty.ai>
2026-11-03 23:12:46 -08:00
sudacode 8976d78390 feat(controller): route status messages through notifications
- Send controller connected and disabled messages over a new IPC channel so they follow ankiConnect.behavior.notificationType
- Remove the separate controller status toast from the overlay
- Only announce connected controllers when controller.enabled is on
- Announce a controller again after it reconnects, and update usage docs
2026-11-03 21:55:55 -08:00
sudacode 8e29cb3c55 fix(controller): pick up macOS presses while mpv is in front (#321) 2026-11-03 21:37:11 -08:00
sudacode fa3051f9cb feat(subtitles): add regex subtitle filters with built-in presets (#318)
- Add `subtitleFilter` config with four presets: parentheses, brackets, speaker prefix and music symbols. Custom regex rules run after the presets.
- Filter primary text where it enters the app (live `sub-text` and parsed cues), so the overlay, sidebar, mining and stats all get the same filtered text.
- Changes hot-reload: the current line is read again and the active track is parsed again, using the filter config as the prefetch parse key.
- Add a custom rule editor in Settings under Behavior → Subtitle Filters that checks patterns as you type.
- Update the config example, the configuration docs and the architecture domains doc, and add a changelog fragment.
2026-11-03 00:33:58 -08:00
sudacode 848a6c4021 fix(plugin): keep loading OSD off on warm playlist advance (#317)
* fix(plugin): keep loading OSD off on warm playlist advance

- Track overlay_content_ready_seen when the app sends subminer-overlay-loading-ready
- Skip restarting the "Overlay loading" spinner for an overlay that already reported ready, so it no longer times out into "Overlay did not become ready" (#315)
- Reset the flag on stop, restart, start failure and shutdown
- Add start-gate tests for warm and not-yet-ready playlist advances

* fix(plugin): only report overlay ready once content has loaded

Hide paths dismissed the loading OSD with subminer-overlay-loading-ready,
which the plugin now remembers to skip the spinner on later playlist files.
A hide before the overlay had content would wrongly mark it ready. The app
now sends ready only when the overlay window content is ready, and a new
stop-only subminer-overlay-loading-dismissed message otherwise.

* fix(plugin): resync overlay readiness on dismissal and mpv connect

A dismissal now clears the plugin's readiness flag, since the app sends it
only while its overlay window has no content (e.g. a replaced window).
On mpv (re)connect the app resends subminer-overlay-loading-ready when the
overlay already has content, so a ready signal lost while mpv was away, or
sent before this mpv's plugin existed, still reaches the plugin.

* refactor(main): pass overlay readiness handler directly
2026-11-02 21:47:32 -08:00
YashaNoosandClaude Opus 5.5 b99409bbcd Controller: subtitle line actions, Button Style, keyboard-only mode pause, Alt+C fixes (#316)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-11-02 21:41:46 -08:00
YashaNoosandClaude Opus 5.5 6d0f37ac21 Controller: layouts for the media timing review and duplicate card dialogs (#313)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 01:38:26 -08:00
sudacode 696462a82e refactor(main): move IPC, CLI and app-ready wiring into assemblies (#311) 2026-11-01 17:59:52 -08:00
YashaNoosandClaude Opus 5.5 655510cf2e Controller: hold to show the secondary subtitle, dictionary entry navigation, standard button names (#312)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 15:54:47 -08:00
sudacode af44735bb8 refactor(main): move core layers into assemblies (#310)
* refactor(main): move first-run setup into an assembly

* refactor(main): move playback and subtitle pipeline state into an assembly

Moves the mpv runtime composer wiring together with the subtitle processing, prefetch, sidebar playback-state, seek-intent, secondary-subtitle and autoplay-priming state it mutates into createPlaybackAssembly. Replaces the main-wiring regex tests for the moved code with behaviour tests that drive real mpv events through a fake client.

* refactor(main): move overlay windows and visibility into an assembly

* refactor(main): read Electron screen lazily in the overlay assembly deps

* docs: describe overlay, playback, and first-run setup assemblies

* test(playback): clear scheduled prefetch refresh in warm-tokenization tests
2026-10-31 22:04:42 -07:00
sudacode 2924f04316 refactor(main): move domains into per-domain assemblies (#309)
* refactor(main): move the YouTube domain into an assembly

createYoutubeAssembly owns the YouTube flow, playback, media cache,
whisper subtitles, primary-subtitle notifications, and the in-app
browser, plus the browser quit-on-close state. main.ts builds it once
where the first YouTube runtime used to be and calls through the result.
Siblings built later are passed as getters or callbacks; tests pin that
construction calls none of them.

* docs(architecture): describe per-domain assemblies

* test(e2e): blank the line before mining the sub-delay cue

The previous test leaves the same cue on screen, so the overlay check
passed before the seek landed and --mine-sentence could read mpv's empty
mid-seek sub-text and skip the card.

* refactor(main): move the Jellyfin domain into an assembly

createJellyfinAssembly owns the Jellyfin composer call, its playback and
remote-progress state, the subtitle cache IO, mpv defaults, and the tray
discovery deps. main.ts builds it where the composer call used to be and
reads state through getActivePlayback/getQuitOnDisconnectArmed.

* refactor(main): move the AniList domain into an assembly

createAnilistAssembly owns the AniList setup and tracking composer calls,
the setup window, the state runtime, and the token, media-guess, in-flight
and attempted-update state they share. main.ts builds it where the setup
composer call used to be and keeps the rate limiter, which the stats
cover-art fetcher also uses.

* refactor(main): move config hot-reload, settings, and sync UI into assemblies

createSettingsAssembly owns config hot-reload and the settings window,
which share the hot-reload-applied handler; main.ts still supplies that
handler's cross-domain deps and the Yomitan deck-name helper.
createSyncAssembly owns the sync window, sync IPC runtime, and auto-sync
scheduler. Both are built where their blocks were; main.ts keeps the
registerHandlers()/start() calls at the same points.

* refactor(main): move dictionary runtimes into assemblies

createDictionariesAssembly owns the JLPT and frequency lookups and their
dictionary-root builders; main.ts still resolves the root paths at the
same point. createCharacterDictionaryAssembly owns the character
dictionary runtime, Yomitan auto-sync, and the portrait and name-candidate
lookups. Both are built where their blocks were; later siblings (Yomitan
profile policy, notifications, OSD, subtitle refresh) come in as getters
or callbacks.

* refactor(main): move overlay modal openers into an assembly

createOverlayModalsAssembly owns the shared open-with-notification helper
and the runtime options, Jimaku, TsukiHime, subtitle generation, session
help, changelog, character dictionary manager, controller, and playlist
browser openers. main.ts builds it where the openers used to live.

* refactor(logger): export the Logger type for assembly deps

The build emits declarations, and the Jellyfin assembly returns the logger
in its tray discovery deps, so tsc needs a nameable Logger type. Assemblies
now Pick from it directly.
2026-10-31 19:39:27 -07:00
sudacode a9a4c4f083 feat(plugins): add JavaScript plugin system with overlay panel (#303) 2026-10-31 19:09:54 -07:00
sudacode 90fc7cf940 refactor(main): fold remaining main-deps builders into their handlers (#308)
* refactor(main): drop remaining pass-through main-deps builders

Jellyfin client-info/setup-window/remote, overlay shortcuts/visibility/
layout/window-factory, config hot-reload, and startup bootstrap factory
builders only forwarded deps. Callers now pass deps to the handlers
directly; createWatchConfigPathHandler moves into config-hot-reload-handlers.

* refactor(main): fold small-logic main-deps builders into their handlers

Tray click, mpv client factory, startup bootstrap exit callbacks, overlay
runtime options, and dictionary roots/runtime builders move their logic
into the owning handler or call site. The two duplicated dictionary-root
builders become one createBuildDictionaryRootsHandler(vendorDirName).

* refactor(main): fold cli command context builders into the factory

createCliCommandContextFactory now adapts appState and main services to
the CLI command context deps directly (socket-path change notification,
texthooker port/websocket URL, browser-open default). The pass-through
cli-command-context-main-deps and cli-command-context-deps layers are
removed; their behavioural tests now run against the factory's context.

* refactor(main): fold subtitle tokenization builder into the mpv composer

The tokenizer deps builder's logic (known-word lookup recording and the
character-dictionary gate on name matching) becomes
buildTokenizerRuntimeOptions in mpv-runtime-composer, tested through the
composer. The MeCab check and dictionary prewarm handlers move to
subtitle-tokenization-warmup.ts, and the unused BuiltMainDeps type is
removed from composer contracts.

* refactor(main): drop redundant tokenizer deps keys and cast

* refactor(main): end the main-deps builder pattern

mpv-main-event-main-deps becomes mpv-main-event-binding-deps
(createMpvMainEventBindingDeps), returning the binding deps directly
instead of a thunk; it owns the subtitle-recording state. The will-quit
cleanup adapter likewise returns its deps directly, and the pass-through
app-lifecycle activate builders are removed.

* docs: drop references to main-deps builders
2026-10-31 14:07:07 -07:00
sudacode 0129e03c0f fix(anki): default fields.audio to SentenceAudio
- Generated sentence audio no longer overwrites Yomitan's ExpressionAudio on default configs
- fields.wordAudio still defaults to ExpressionAudio
- Regenerate config.example.jsonc and update the Anki and configuration docs
- Add changelog fragment
2026-10-28 22:47:54 -07:00
e59b78dca0 fix(anilist): add missing list entry on confident match (#305)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Ivan Melck <ivan@spec-kitty.ai>
2026-10-28 17:20:03 -07:00
sudacode 203a61a20f chore(release): v0.21.1 2026-10-26 23:27:24 -07:00
sudacode 6219a81144 feat(stats): redesign expanded session view with density chart (#304) 2026-10-26 23:08:48 -07:00
sudacode d3a94536a8 docs: link Hachidori project page from README and setup docs 2026-10-25 21:42:36 -07:00
sudacode 3068d02f89 docs(readme): refresh anime screenshots from current build
- Replace annotations, one-key-mining, and yomitan-lookup screenshots
- Update README alt text to match the new images
- Add docs changelog fragment
2026-10-25 19:32:25 -07:00
sudacode 3549e0ed93 chore(build): consolidate packaging inputs and build output layout (#299) 2026-10-24 23:28:38 -07:00
sudacode b0a8b6b3fc chore(release): prepare v0.21.0 2026-10-24 21:56:02 -07:00
sudacode cbaa4626e1 feat(dictionary): add Hachidori backend support (#266) 2026-10-24 21:08:18 -07:00
sudacode d047e3e0a4 fix(jellyfin): find mpv when SubMiner is launched from Finder (#298) 2026-10-24 18:27:31 -07:00
sudacode bf4e5bb609 feat(subtitles): generate Japanese subtitles during Jellyfin playback (#294)
* feat(subtitles): generate Japanese subtitles during Jellyfin playback

- Read the item's original file via toJellyfinOriginalStreamUrl and write output to a temp dir
- Transcoded playback uses Jellyfin's audio index and only external references
- Show Jellyfin media by title and redact URL tokens in subprocess errors
- Update overlay copy, docs, and changelog fragment

* fix(subtitles): redact full stream tokens and reuse one Jellyfin output folder

* test(subtitles): make Jellyfin output path assertions platform-neutral

- Compare dirname/basename via node:path instead of POSIX-only regexes
- Assert the full sub-add command uses the generated output path
2026-10-13 21:55:28 -07:00
sudacode 8fc20b2499 refactor: remove dead code and enable noUnusedLocals (#292)
* refactor: remove dead code and enable noUnusedLocals

- delete launcher/jimaku.ts and the launcher Args/config jimaku* fields
  that only fed it (the SUBMINER_JIMAKU_* env vars had no effect)
- delete plugin/subminer/hover.lua; nothing has sent subminer-hover-token
  since Overlay 2.0
- drop never-read anilist.characterDictionary.refreshTtlHours and
  evictionPolicy (still hidden in the settings UI for old configs)
- drop unused root deps: eslint, @fontsource-variable/geist{,-mono}
  (stats/ declares its own)
- delete the committed draft renderer-performance plan doc
- turn on noUnusedLocals for typecheck and remove what it surfaced,
  including Windows focus-handoff values computed but never consumed

* docs(changes): add docs fragment and note retired hover messages
2026-10-12 22:10:54 -07:00
sudacode 78a9b6346f fix(jellyfin): make terminal play work with a running app and report progress (#288) 2026-10-10 17:27:48 -07:00
sudacode 9ffa2bce94 chore(release): prepare v0.20.1 2026-10-04 00:01:18 -07:00
sudacode e77aa5c3b3 fix(stats): count mined cards once and keep lexical stats exact after deletes (#280) 2026-10-03 21:16:12 -07:00
sudacode a5d0b63792 fix(storage): protect Yomitan profiles across Electron runtime changes (#211) 2026-10-01 21:44:53 -07:00
sudacode 9e79fa93c2 feat(overlay): forward mpv mouse button and wheel bindings (#279) 2026-10-01 00:44:51 -07:00
sudacode 9fadbc8cdd docs: align README with installation guide and fix Arch MeCab install
- Install MeCab from the AUR (mecab-git) on Arch, since it is not in the official repos
- Rewrite the README feature blurbs, requirements table, and quick start so they match docs-site/installation.md
- Add fuse2, X11 window tools, whisper.cpp, and the thumbnail tools to the README requirements
- Drop the playlist browser and texthooker sections and the standalone launcher download steps
- Mention AniList as a guessit consumer in the installation guide
2026-09-30 23:45:53 -07:00
sudacode 0bbd8617f4 feat(overlay): run session help commands with Enter or double-click (#275) 2026-09-29 18:48:56 -07:00
sudacode 4e43c5c6e1 fix(overlay): keep overlay mapped during Hyprland/Sway fullscreen (#276) 2026-09-29 18:39:55 -07:00
sudacode fdb0a31fff fix(overlay): drive timing review playhead from mpv playback position
- Preview session observes time-pos, waits for loaded media before seeking, and acks each command
- Forward positions to the modal over media-timing-review:preview-position
- Cursor follows the real position, pauses while buffering, and stays at the clip end when done
- Replace the fixed end-of-clip timer with a 15s stall timeout
- Document preview cursor behavior and ownership in docs
2026-09-29 18:07:07 -07:00
sudacode 07b32626a6 feat(youtube): add YouTube browser window and Whisper subtitle source (#273) 2026-09-28 20:35:47 -07:00
sudacode c68b73c8c1 fix(overlay): thin centered trim handles and label-free subtitle boundaries in timing review (#274) 2026-09-28 19:53:38 -07:00
sudacode 23af9c6464 docs: restore docs/architecture pointer in development page 2026-09-24 16:58:31 -07:00
sudacode 8825cca642 docs: rewrite docs site pages to be shorter and easier to scan
- Pages now start with setup and usage, and reference material is in compact tables
- Configuration reference gives each config block a short explanation and a key/default table
- Internal detail removed from user pages, and docs that had drifted from current behavior fixed
- The status line shows today's date, set on the client, instead of the page's last-updated date
- Add changelog fragment
2026-09-24 16:20:42 -07:00
sudacode bc73a02623 ci(docs): serve frozen version archives from R2 (#269) 2026-09-24 15:28:58 -07:00
sudacode 7fbc36dc41 chore(release): prepare v0.20.0 2026-09-23 01:15:20 -07:00
sudacode 7c161723cd fix(anki): regenerate sentence furigana from the final sentence (#268) 2026-09-22 21:43:19 -07:00
sudacode b0fb39a8f5 feat(overlay): add optional subtitle selection modal (#265) 2026-09-22 18:30:26 -07:00
Abdulrazzaq Alhendi a7a302bdd0 fix(jellyfin): respect Windows mpv configuration when casting (#267) 2026-09-22 18:29:53 -07:00
sudacode 42d1384c68 fix(jellyfin): support modern authentication (#264) 2026-09-21 20:40:57 -07:00
sudacode 1508863dbb feat(stats): add TMDB metadata for live-action dramas in the Library (#252) 2026-09-21 00:10:00 -07:00
sudacode ab48a5678e fix(stats): restrict local requests and serve the dashboard over HTTP (#263) 2026-09-20 23:36:55 -07:00
sudacode 2f21582666 fix(overlay): cancel pending window transitions and timing reviews (#262) 2026-09-20 23:35:48 -07:00