FluentFrame is a local-first Chrome extension for learning English while watching YouTube. It downloads real English captions, asks a local Codex or Claude CLI to produce corrected bilingual learning subtitles and phrase explanations, then renders them directly inside the video experience.
Read the quick Chrome install guide
Features
Β·
Getting Started
Β·
Architecture
Β·
Manual Verification
Table of Contents
FluentFrame turns YouTube videos into an English-learning workspace. It keeps the original caption timing as the source of truth, then adds corrected English, natural Chinese translation, phrase explanations, usage notes, a Now pane, History, and Personal Notes.
The system is local-first. The Chrome extension never calls a hosted LLM API directly. Caption download, local agent execution, cache, notes, and diagnostics run through a native host on your machine.
It has two main runtime paths:
- Video learning mode - the YouTube page overlay and side panel show bilingual subtitles, learning events, history, and saved notes.
- Local setup mode - development-mode installation scripts build the extension, register the native host, link the Chrome extension ID, and diagnose missing dependencies.
The goal is not to replace watching the video. It lets the video remain the main experience while adding enough bilingual context and phrase explanation to make each clip useful for English study.
Detailed operation and verification docs:
- docs/architecture.md
- docs/local-chrome-install.md
- docs/local-development-installation.md
- docs/manual-verification.md
- Language / runtime - TypeScript on Node.js 24+
- Browser surface - Chrome extension Manifest V3, content script, service worker, popup
- Local bridge - Chrome Native Messaging host
- Caption source -
yt-dlp - Agent backends - Codex CLI or Claude CLI
- Build & test - Vite, Vitest, Playwright, gitleaks, pnpm workspaces
- Real YouTube caption pipeline - downloads available English captions with
yt-dlp. - Timing-safe bilingual subtitles - preserves original SRT timing while adding corrected English and Chinese text.
- Learning event generation - uses Codex or Claude to produce phrase explanations, usage notes, and difficulty labels.
- Streaming long-video batching - splits long caption files into as many batches as needed, with at most 20 cues per local-agent batch, and renders each completed batch while later batches continue.
- Video-native overlay - renders subtitles and the Now pane inside the YouTube video instead of a detached learning page.
- History and Personal Notes - keeps generated learning sentences and user-saved notes under
~/.fluent-frame. - Development-mode install wizard - avoids Chrome Web Store payment while making local setup guided and repeatable.
- Native-host diagnostics - popup and CLI health checks report extension linking,
yt-dlp, selected agent, and local paths. - Local quality gates - unit tests, browser extension E2E, live YouTube E2E, AI output validation, CI, and Gitleaks.
Chrome / YouTube
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β YouTube watch page β
β content script, video overlay, Now pane, History, Notes β
ββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ
β chrome.runtime.sendMessage
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Chrome extension β
β popup, service worker, protocol normalization β
ββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ
β chrome.runtime.connect / connectNative
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Native Messaging boundary β
β com.octopusgarage.fluent_frame β
β allowed_origins = chrome-extension://<extension-id>/ β
ββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ
β stdio JSON messages
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FluentFrame native host β
β request validation, health checks, cache, notes, processing β
βββββββββββββββββ¬βββββββββββββββββββββββ¬ββββββββββββββββββββββ
β β
βΌ βΌ
ββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββ
β caption downloader β β local agent runner β
β yt-dlp -> source SRT β β codex exec / claude --print β
ββββββββββββββββ¬ββββββββββββ ββββββββββββββββ¬βββββββββββββββ
β β
ββββββββββββββββ¬ββββββββββββββββ
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β result validation + timing merge β
β original SRT timing remains authoritative β
ββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ~/.fluent-frame β
β config.json, cache, notes.json, native-host wrapper β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Key points:
- Chrome cannot run shell commands directly - it can only start the registered native host when the manifest allows the exact extension ID.
- Original captions own timing - the agent can correct English, translate Chinese, and explain phrases, but source SRT timing is merged back into the final result.
- Long videos are batched dynamically - there is no fixed video-duration limit and no fixed batch count. The native host derives the batch count from the source SRT size, with at most 20 cues per batch, so longer videos naturally create more batches.
- Streaming generation is idempotent per video - repeated clicks while one video is generating reuse the active stream; YouTube navigation disconnects stale work before the next video starts.
- Local agents are explicit - Codex and Claude are invoked by the native host only for generation requests.
- One shared protocol - extension and native host both import
packages/sharedtypes so request and response drift is caught by TypeScript and tests. - Development-mode distribution -
pnpm setupandpnpm link:chromeprovide a no-store install path while preserving Chrome's required manualLoad unpackedstep.
Local state is stored under ~/.fluent-frame by default:
~/.fluent-frame/
βββ bin/native-host
βββ config.json
βββ cache/<videoId>/<language>/<workflowVersion>/result.json
βββ notes.json
Browser-side generation timing estimates are stored in YouTube page localStorage:
fluentFrame.generationHistory.v1
See docs/architecture.md for the full architecture notes.
- Node.js 24+
- pnpm 10+
- Google Chrome
yt-dlpavailable onPATH, or an absolute path passed withFF_YTDLP_PATH- Codex CLI or Claude CLI available on
PATH, or absolute paths passed withFF_CODEX_PATH/FF_CLAUDE_PATH
Optional overrides:
FF_YTDLP_PATH=/absolute/path/to/yt-dlpFF_AGENT=codexorFF_AGENT=claudeFF_CODEX_PATH=/absolute/path/to/codexFF_CLAUDE_PATH=/absolute/path/to/claudeFF_CACHE_DIR=/absolute/path/to/cache
FluentFrame is intended to run as a Chrome development-mode extension. No Chrome Web Store payment is required.
pnpm local:installpnpm setup is kept as an alias for the same guided installer.
The interactive wizard detects local tools, lets you choose Codex or Claude, builds the workspace, installs the native host, opens chrome://extensions, and guides you through loading apps/extension/dist.
After the first install, update the development-mode extension with:
pnpm local:updateThis pulls the latest source with git pull --ff-only, installs dependencies with the lockfile, rebuilds the extension and native host, refreshes the native-host wrapper, runs diagnostics, and opens chrome://extensions so you can click Reload on FluentFrame.
Useful local install commands:
pnpm local:install
pnpm local:update
pnpm local:open
pnpm run doctor
pnpm ff:doctor
pnpm link:chrome <extension-id>
pnpm uninstall:localSee docs/local-development-installation.md for the full guided install flow.
- Run
pnpm local:install. - In Chrome, open
chrome://extensions. - Enable Developer Mode.
- Click
Load unpacked. - Select
apps/extension/dist. - Copy the extension ID and run
pnpm link:chrome <extension-id>if you did not paste it into the setup wizard. - Open a YouTube video with English captions.
- Click
Generate learning subtitles.
For later updates, run pnpm local:update, then click Reload for FluentFrame in chrome://extensions.
Run local diagnostics any time:
pnpm run doctor- Open a YouTube video with English captions.
- Click
Generate learning subtitles. - Read the corrected English subtitle and Chinese translation in the video overlay.
- Use the Now pane and History to review generated learning events.
- Save useful sentences and phrase explanations to Personal Notes.
Results are cached by video and workflow version. The default cache location is:
~/.fluent-frame/cache
The extension popup runs a native-host health check and shows whether FluentFrame can reach:
- the native host
yt-dlp- the selected Codex or Claude agent
- the linked Chrome extension origin
If the popup reports setup issues, run:
pnpm run doctor
pnpm link:chrome <extension-id>The preferred path is pnpm setup. Manual native-host commands are kept for debugging:
pnpm --filter @fluent-frame/native-host build
FF_EXTENSION_ID=<copied-extension-id> pnpm --filter @fluent-frame/native-host install:native-hostChrome does not reliably inherit your interactive shell PATH on macOS. During install, the native-host installer resolves yt-dlp, Codex, and Claude, then writes absolute paths into the wrapper that Chrome launches. If a tool is not found, rerun the installer with explicit paths:
FF_EXTENSION_ID=<copied-extension-id> \
FF_YTDLP_PATH=/absolute/path/to/yt-dlp \
FF_CODEX_PATH=/absolute/path/to/codex \
FF_CLAUDE_PATH=/absolute/path/to/claude \
pnpm --filter @fluent-frame/native-host install:native-hostThe installer writes the Chrome manifest to:
~/Library/Application Support/Google/Chrome/NativeMessagingHosts/com.octopusgarage.fluent_frame.json
Local config is stored in:
~/.fluent-frame/config.json
Example:
{
"agent": "codex",
"ytDlpPath": "/opt/homebrew/bin/yt-dlp",
"codexPath": "/opt/homebrew/bin/codex",
"claudePath": "/opt/homebrew/bin/claude"
}Environment variables override the config file:
FF_AGENT=claude FF_CLAUDE_PATH=/absolute/path/to/claude pnpm setuppnpm install
pnpm lint
pnpm typecheck
pnpm test
pnpm build
pnpm verify:localRun the browser E2E test with a real Chrome extension context:
pnpm e2eRun the live YouTube E2E test with the real native host:
pnpm e2e:liveThis opens a real YouTube watch page, downloads real captions with yt-dlp, runs the configured local agent through the native host, and verifies that generated subtitles and phrase explanations render in the extension. It defaults to https://www.youtube.com/watch?v=dQw4w9WgXcQ; override with FF_LIVE_YOUTUBE_URL=<url>.
Run the local subtitle-quality eval:
pnpm eval:aiThe AI eval sends a small SRT sample to the same prompt used by the native host, then validates that the local agent returns strict JSON with preserved cue IDs/timing, corrected English, natural Chinese text, phrase references, explanations, and valid difficulty labels.
Use docs/manual-verification.md for the full local QA checklist, including missing dependency checks, same-tab YouTube navigation, overlay toggling, cache behavior, and native-host wrapper verification.
- CONTRIBUTING.md describes the local quality gate and project conventions.
- SECURITY.md documents the native-host trust boundary and vulnerability reporting path.
MIT. See LICENSE.