- Svelte 46.6%
- TypeScript 40.5%
- JavaScript 7.8%
- PLpgSQL 3.2%
- CSS 1.7%
- Other 0.2%
- /flashcard load: fetch profiles.sponsored and short-circuit the stats queries when the user has not paid; expose 'sponsored' to the page. - /flashcard page: when logged in but not sponsored, render a paywall hero card that POSTs to /api/pay/create and redirects to the returned Xunhupay URL. - /flashcard/review load: redirect non-sponsored users back to /flashcard so the review screen cannot be reached by typing the URL. |
||
|---|---|---|
| assets | ||
| docs | ||
| scripts | ||
| site | ||
| src | ||
| supabase | ||
| .env.example | ||
| .gitignore | ||
| build.mjs | ||
| manifest.json | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| popup.html | ||
| README.md | ||
| styles.css | ||
| tsconfig.json | ||
LearnByWatching
注:本项目的所有代码均由 AI 自动生成并构建。
LearnByWatching 是一款适用于 Chromium 浏览器的扩展程序。它通过连接本地或云端的大语言模型(LLM),在观看 YouTube 与 Nebula 视频时提供实时翻译与深度的上下文解析,并配套一个独立的复习站点(基于 SvelteKit + Supabase),帮助用户把视频里学到的生词沉淀下来反复练习。
仓库包含两部分:
src/— 浏览器扩展(MV3,TypeScript + Svelte,由 esbuild 打包到dist/)site/— 独立的复习站点(SvelteKit,部署在 Cloudflare Pages)
核心功能
-
暂停深度解析 (Pause & Analyze) 在视频播放过程中按下空格键暂停,插件会立即在屏幕中央显示当前字幕的深度解析面板。
- 精准高亮: 在生成的中文段落翻译中精准高亮当前屏幕上正在显示的英文残句所对应的中文意思,实现"所见即所指"。
- 百科与词汇提取: 结合视频上下文智能提取生词、短语以及出场人物、地名等专有名词的背景解释。
- 零延迟预加载: 利用后台预加载队列提前将后续字幕送给 LLM 翻译并缓存。
-
实时双语字幕 在 YouTube / Nebula 原生播放器上叠加由大模型生成的实时翻译字幕。
-
生词本 + 间隔复习 (SRS) 在解析面板里点选生词即加入云端生词本(需登录),随后通过配套的复习站点完成多选题与抽卡复习。
-
灵活的模型配置 任何兼容 OpenAI API 的服务都可接入:本地 vLLM / Ollama,或云端的 OpenAI / DeepSeek / Qwen 等。可在设置面板中自由配置 API URL、API Key、Model Name 与英文水平描述。
-
结构化输出与容错 通过 JSON Schema 强制约束模型输出格式,并在解析失败 / 缓存校验失败时自动重试或允许手动重试。
-
共享翻译缓存 登录用户之间共享一份按
SHA-256(text + model + langLevel)索引的翻译缓存(Supabase 表 + RLS 保护),同样的句子由后来者直接命中,省 token 也省时间。
仓库结构
.
├── src/ # Chrome 扩展源码
├── site/ # SvelteKit 复习站点
├── supabase/ # 数据库 schema 与 migrations
├── scripts/ # 一次性脚本(如 ECDICT 词典导入)
├── build.mjs # 扩展打包入口
└── manifest.json # MV3 manifest(被复制到 dist/)
准备工作
必备依赖
- Node.js ≥ 20
- pnpm ≥ 9
- 一个 Supabase 项目(自托管或 supabase.com)
配置环境变量
复制 .env.example 为 .env,填入你自己的 Supabase 项目值:
cp .env.example .env
# 编辑 .env,填入 SUPABASE_URL 与 SUPABASE_ANON_KEY(publishable key)
⚠️ 只填 publishable / anon key。
service_rolekey 绕过 RLS,永远不要写进客户端代码或这个文件。
初始化 Supabase
pnpm dlx supabase db push # 应用 supabase/migrations/ 下的所有 schema
如果需要导入 ECDICT 词典数据(提供单词词性、柯林斯星级等元信息),见 scripts/README.md。
构建扩展
pnpm install
pnpm exec node build.mjs # 单次构建,产物输出到 dist/
pnpm exec node build.mjs --watch # 开发模式,热重新打包
构建脚本会读取 .env,把 Supabase URL / anon key 通过 esbuild 的 define 注入到产物中;缺少环境变量时构建直接 fail,避免把空配置推到产物里。
加载到浏览器
- 打开
chrome://extensions/,开启右上角的 开发者模式。 - 点击 加载已解压的扩展程序,选择
dist/目录(不是仓库根目录)。 - 建议将插件固定到浏览器工具栏。
运行复习站点(可选)
cd site
cp .env.example .env.local # 填入 PUBLIC_SUPABASE_URL 与 PUBLIC_SUPABASE_PUBLISHABLE_KEY
pnpm install
pnpm dev # 本地开发
pnpm build # 部署到 Cloudflare Pages
使用配置
点击浏览器工具栏的插件图标,打开配置面板:
- API URL — 大模型接口地址,例如
http://localhost:30000/v1。 - API Key — 后端鉴权 Token(本地服务可留空)。
- Model Name — 模型名称,例如
default、gpt-4o-mini。 - English level — 用一段自然语言描述自己的英语水平,会拼进 system prompt 并参与缓存键计算。
- Operation Mode — 仅暂停分析 / 仅实时翻译 / 两者开启。
- Prefetch Buffer — 后台预翻译的句子数量,越大命中率越高。
配置完成后,打开任意带有字幕流的 YouTube 或 Nebula 视频,按下空格键暂停即可体验。
隐私声明
- 字幕文本会被发送到 你自己配置的 LLM 接口;本项目自身不做任何遥测。
- 登录后,你写入的翻译缓存会按
SHA-256(text + model + langLevel)上传到 Supabase 与所有登录用户共享,但不包含你的身份标识(created_by字段不在公开视图中)。 - 生词本是每用户私有的,受 Supabase RLS 保护。
- 扩展所需的 LLM API Key 存放在
chrome.storage.sync中(明文)。如果你不希望它跨设备同步,请在 Chrome 中关闭扩展数据的账号同步。
安全说明
dist/不在版本控制中。如果你打算分发预构建产物,请确认其中的 Supabase URL / anon key 是你愿意公开的(anon key 受 RLS 保护,但仍意味着任何人都能连到你的项目并消耗配额)。translation_cache的写入策略已通过 RLS 限制为"只能写自己的行",防止恶意用户覆盖他人写入的缓存(详见supabase/migrations/20260502120000_*.sql)。- 如果你 fork 本项目,请使用你自己的 Supabase 项目,不要直接复用上游的实例。
