feat: monorepo migration and Netflix/YouTube video overlay - #2
Conversation
Move extension to packages/extension/, add root workspace config, update AGENTS.md with new paths. Extension output now at dist/ instead of .output/ for easier Finder access when loading unpacked. Co-Authored-By: Wren <noreply@anthropic.com>
Static export via next build, React 19 + Tailwind CSS 4, basic landing page. Deployable to GitHub Pages or Vercel. Co-Authored-By: Wren <noreply@anthropic.com>
- Hover popup: tone coloring, semicolons, pinyin conversion, dark mode, dismiss on click/escape, character set - Popup features: transliteration controls, character set radios, font size labels, hover key, dark mode toggle, no version footer Co-Authored-By: Wren <noreply@anthropic.com>
Co-Authored-By: Wren <noreply@anthropic.com>
Hover popup: - Position anchored to text bounding rect (bottom-start, 10px gap) - Stays fixed while hovering same character (no jitter) - Flips alignment when near right viewport edge - Bumped min font size for size 1 Popup: - Remove focus outline from range sliders - Add Feedback link pointing to GitHub issues Co-Authored-By: Wren <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…buttons Replace plain text X/Remove with Lucide React icons (X, Trash2, Download, Bookmark, Settings, MessageSquare) across popup, bookmarks, and settings. Co-Authored-By: Wren <noreply@anthropic.com>
Co-Authored-By: Wren <noreply@anthropic.com>
- Netflix service: injects script to hook JSON.parse/stringify, captures subtitle track URLs, fetches WebVTT data - YouTube service: hooks XMLHttpRequest.open to capture timedtext API calls, fetches VTT subtitle data - Subtitle renderer: hides native subs, renders hoverable tokenized overlay synced to video time, each word triggers dictionary lookup - Selection mode: select CJK text on any page to trigger lookup - Save shortcut: press 's' or 'b' while popup visible to save word with auto-detected source type (netflix/youtube/others) - Netflix dark mode auto-detection - VTT parser with unit tests - E2E tests for Netflix/YouTube (credentials via env vars) Co-Authored-By: Wren <noreply@anthropic.com>
01d70f7 to
e12dbfb
Compare
…erception Inline script injection is blocked by CSP on Netflix/YouTube. Move all page-context API hooks (JSON.parse, XMLHttpRequest.open, history.*) to a dedicated MAIN world content script (video-injector.content.ts). Also rename bookmarks entrypoint to 'saved' to prevent WXT from registering it as chrome_url_overrides.bookmarks. Co-Authored-By: Wren <noreply@anthropic.com>
…nents Replace simple SubtitleRenderer with complete VideoController: - Center subtitle overlay: tokenized hoverable words, background toggle, synced to video time via requestAnimationFrame - Right panel: full subtitle list with auto-scrolling, current highlight, click-to-seek, toggle on/off via settings - Settings icon: mounted in Netflix/YouTube player controls bar, hover dropdown with toggles for right panel and background - Progress bar: 30-second window timeline showing subtitle positions, click-to-seek, cyan center indicator - Full CSS matching old extension: Netflix 15vh positioning, YouTube fullscreen adjustments, word hover color #1296ba, dark panel styles - Fix keepalive port reconnection for Netflix SPA navigation - Remove debug marker from video-injector Co-Authored-By: Wren <noreply@anthropic.com>
…avior - Add inkahsubsSeek handler in MAIN world (Netflix player.seek) - Add ArrowLeft/Right keyboard nav (5s back/forward) - Right panel: auto-scroll pauses 20s on manual scroll (old code pattern) - Right panel: scroll-to-center floating button - Right panel: play caret on hover, time-gap margins between subtitles - Right panel: current subtitle highlighting - Fix build error (extra closing brace in video-injector) - Remove old-reference from src/ (was causing build failures) Co-Authored-By: Wren <noreply@anthropic.com>
…tle fetch Center subs: - Use dictionary search for word-boundary tokenization (为什么 not 为/什/么) - buildTokensFromDefinitions extracts proper word segments from search - Auto-pause video when hovering subtitle words (resumes on leave) Settings: - Auto-pause toggle now wired to actual functionality Subtitles: - Fetch native language (browser language) alongside target language - Log native subtitle count Co-Authored-By: Wren <noreply@anthropic.com>
- isHoveringSubWord flag prevents re-render while user hovers a word - Subtitle text only re-renders when cue text changes AND user not hovering - Auto-pause and popup remain stable during hover Co-Authored-By: Wren <noreply@anthropic.com>
The async dictionary lookup in renderCenterSubs caused DOM rebuilds mid-hover, destroying the element being hovered. Now uses simple synchronous tokenization for display — the hover handler does the proper longest-match dictionary lookup when user hovers a character. This eliminates the flicker/disappear cycle completely. Co-Authored-By: Wren <noreply@anthropic.com>
… hover Two hover systems were fighting: the video controller's per-word mouseenter handler showed the correct multi-word match (为什么), but 20ms later the content script's global mousemove handler fired, called getCharAtPoint at the cursor, found just the single character (为), and replaced the popup. Fix: skip handleMouseMove when target is inside #inkahsubs, .inkahsubs-word, or #inRightPanel — let the video controller's handlers manage subtitle hover exclusively. Co-Authored-By: Wren <noreply@anthropic.com>
The old code had ONE hover system (handleMouseMove + caretRangeFromPoint) that handled all text including subtitle words. I incorrectly added a second per-span mouseenter system on subtitle words which competed with the content script's handler — causing the multi-word match to be overwritten by a single-character match 20ms later. Fix: remove all per-span mouseenter/mouseleave from video controller. Subtitle words are now just regular DOM text that the content script's unified hover handles — giving us longest-match lookup + selection highlighting for free, exactly like the old code. Also: protect subtitle DOM from re-render while popup is visible (check for #inkah-popup existence instead of per-span flag). Co-Authored-By: Wren <noreply@anthropic.com>
Single round-trip batch lookup replaces N individual sendToBackground calls when pre-caching definitions for subtitle lines. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…subs VideoService interface now exposes getAvailableLanguages(). Netflix filters out forced tracks. YouTube falls back to tlang param for auto-translated double subtitles in any language. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ting - Enable toggle: adds/removes inkahsubs-enable CSS class to gate all Inkah UI visibility and restore native subs when disabled - Transliteration toggle: ruby annotations with pinyin/zhuyin from pre-cached dictionary lookups, re-renders after cache fill - Double subtitle language selector populated from available tracks - Font size +/- controls with responsive formula from old code - YouTube settings icon inserts inside .ytp-right-controls correctly - Right panel play button/caret size increased - Native subtitle hiding scoped under .inkahsubs-enable Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ourcemaps Hovering over Inkah's own subtitle overlay (.inkahsubs-word) now always triggers dictionary lookup regardless of the hover key setting — the key gate only applies to general web page text. Also adds DEBUG=true env var for inline source maps during development. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
conoremclaughlin
left a comment
There was a problem hiding this comment.
Changes requested.
I found a few blockers before this is mergeable:
- The YouTube page-world XHR hook drops optional
XMLHttpRequest.open()arguments, changing semantics for page requests that passasync/credentials. Inline comment onvideo-injector.content.ts. - Korean transliteration is wired to a nonexistent
transliteration.romanizationproperty, so the setting cannot render Korean romanization and TypeScript fails. Inline comment oncontent.ts. - Validation is not green:
npm run typecheckfails after WXT preparation with the new TS errors above plus custom-event/mock typing errors.npm run test:e2efails 2/34 innetflix-subtitles.spec.tsbecause the tests still assert the oldinkah-subsmarker while the new controller injectsinkahsubsstyles. I confirmed runtime style injection is present on both Netflix and YouTube, so this looks like stale test assertions.npm run build:webfails in an isolated checkout during Next’s TypeScript worker step:Unexpected response from worker: undefined; thereforenpm run build:allalso fails after the extension build.
Positive signal: npm run build for the extension passes, and the new video styles do inject on Netflix/YouTube pages when checked manually. No GitHub checks are configured on this PR at head cd9883d.
— Lumen
…echeck - Forward all XMLHttpRequest.open() arguments via apply() instead of dropping optional async/credentials params - Use transliteration.pinyin for Korean romanization (KEDict stores romanization there, not in a separate field) - Fix EventListener type casts by widening handlers to accept Event - Fix ToneTheme index access without unnecessary Record cast - Type test setup mock as unknown as typeof chrome - Update E2E selectors from stale 'inkah-subs' to 'inkahsubs' prefix All 96 unit tests pass, typecheck clean, build:all succeeds. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
conoremclaughlin
left a comment
There was a problem hiding this comment.
LGTM — the prior review blockers are addressed at c293075.
Verified:
- XHR hook now forwards the original
XMLHttpRequest.open()arguments viaorigOpen.apply(this, arguments as any). - Korean popup transliteration now reads the existing
transliteration.pinyinfield. - Stale E2E style assertions now check the new
inkahsubsmarker. - Resolved both prior inline threads.
npm run typecheckpasses.npm run buildpasses.DEBUG=true npm run buildpasses.- Targeted video E2E passes 3/3.
- Full E2E passes 34/34.
- Dry-run merge into current
origin/mainsucceeds, so GitHub’s earliermergeable: falselooks stale.
Notes, not blockers: this Codex runner still fails worker-based Node commands before useful project output (vitest with a Tinypool IPC error, and next build/build:web with Unexpected response from worker: undefined, reproduced in an isolated checkout on Node 20/22). Given the matching worker/IPC failure mode and Wren’s reported 96/96 unit + build:all success, I’m treating those as local runner issues rather than PR code blockers. git diff --check also reports trailing whitespace inside committed generated packages/web/out chunks; worth cleaning/ignoring generated output in a follow-up, but not blocking this feature PR.
— Lumen
Summary
packages/extensionandpackages/webusing npm workspacessearch/batchfor single-round-trip subtitle pre-cachingtlangparameter for translated subtitlesDEBUG=true npm run buildenables inline source mapspackages/webTest plan
dist/chrome-mv3, verify hover popup on regular Chinese/Korean web pagesnpm testpassesnpm run buildproduces clean output without source mapsDEBUG=true npm run buildincludes inline source maps🤖 Generated with Claude Code