diff --git a/.gitignore b/.gitignore
index a213e3f..d028be8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,6 +29,8 @@ data/notegen.db-shm
# DB 备份(e2e/迁移留存),同样含 bcrypt 哈希,严禁入库
data/*.e2ebak
data/user_notes/
+backups/*.zip
+.playwright-cli/
models/
models_msc_cache/
wheels/
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..a2b4244
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,75 @@
+# Changelog
+
+All notable project changes are tracked here.
+
+## v1.0.0-demo - 2026-06-25
+
+This is the first demo checkpoint for a reproducible local NoteGen release. The goal is not feature expansion; it is a stable version that can be built, smoked, demonstrated, and rolled back.
+
+### Added
+
+- Added `scripts/smoke_local.ps1` for local release smoke checks:
+ - Redis TCP availability.
+ - API `/api/health`.
+ - single worker process check.
+ - public notes API.
+ - optional login and private notes check.
+ - DOCX export endpoint.
+ - production web build.
+- Added local release documentation:
+ - `docs/SMOKE_TEST.md`
+ - `docs/DEPLOY_LOCAL.md`
+- Added v1 demo artifacts under `output/playwright/`:
+ - `v1-home.png`
+ - `v1-login-gate.png`
+ - `v1-workspace.png`
+ - `v1-demo.gif`
+ - `v1-export.docx`
+
+### Changed
+
+- Migrated the core QA entry in `web/components/ChatPanel.tsx` from legacy `--bg` / `--fg` / `--accent` tokens to Warm Fold `--wf-*` tokens.
+- Migrated the `/notes/[id]` workspace shell in `web/components/NoteWorkspace.tsx` to Warm Fold tokens for error state, skeleton state, top controls, right-side tools, and the mobile chapter drawer.
+- Migrated `web/components/CreateNotePanel.tsx`, used by `/notebooks`, from legacy tokens and `apple-button` to Warm Fold tokens.
+- Reworked `web/lib/progress.ts` to read chapter progress through `useSyncExternalStore`, keeping localStorage behavior while clearing the React hooks lint baseline.
+- Removed unused legacy visual components after confirming no runtime references:
+ - `web/components/AskBar.tsx`
+ - `web/components/FluidBG.tsx`
+ - `web/components/ParticleBG.tsx`
+
+### Fixed
+
+- Cleared blocking ESLint errors in:
+ - `web/components/ChatPanel.tsx`
+ - `web/lib/progress.ts`
+- Adjusted local smoke worker detection for Windows virtualenv behavior, where a venv shim and the real Python child process can both contain `run_worker.py` in their command line.
+- Adjusted DOCX smoke verification to use `Invoke-WebRequest -UseBasicParsing`, avoiding a Windows PowerShell null-reference failure on binary responses.
+
+### Verified
+
+Latest verified local commands:
+
+```powershell
+cd web
+npm run lint
+npm run test
+npx tsc --noEmit
+npm run build
+```
+
+Latest local smoke command:
+
+```powershell
+powershell -ExecutionPolicy Bypass -File scripts\smoke_local.ps1
+```
+
+Smoke result at checkpoint time:
+
+- Failures: `0`
+- Warnings: login skipped unless `NOTEGEN_SMOKE_EMAIL` and `NOTEGEN_SMOKE_PASSWORD` are provided.
+
+### Known Baseline
+
+- `npm run lint` exits successfully but still reports non-blocking warnings for existing `` usage and one unused ESLint disable directive outside the files touched for this checkpoint.
+- Learning progress is still localStorage-backed, not account-synchronized.
+- Full GPU video generation is intentionally not part of the automated smoke script; the script checks service readiness and lightweight API behavior.
diff --git a/README.md b/README.md
index f4739db..57d2282 100644
--- a/README.md
+++ b/README.md
@@ -116,6 +116,12 @@ cd web; npm run dev # 4. Web → http://localhost:3000
输出在 `data/outputs/.large-v3.neural.texttile.mm.vl.md`。
+## v1 checkpoint docs
+
+- [CHANGELOG.md](CHANGELOG.md) records the `v1.0.0-demo` checkpoint and known baseline.
+- [docs/SMOKE_TEST.md](docs/SMOKE_TEST.md) explains the local smoke path and expected results.
+- [docs/DEPLOY_LOCAL.md](docs/DEPLOY_LOCAL.md) documents the local deployment/startup flow.
+
## API 概览
| 分组 | 端点 |
diff --git a/docs/DEPLOY_LOCAL.md b/docs/DEPLOY_LOCAL.md
new file mode 100644
index 0000000..2642e86
--- /dev/null
+++ b/docs/DEPLOY_LOCAL.md
@@ -0,0 +1,199 @@
+# Local Deployment
+
+This document describes how to run NoteGen locally for the v1 demo checkpoint.
+
+## Architecture
+
+The local stack has four moving parts:
+
+```text
+Browser -> Next.js web (:3000)
+ -> FastAPI API (:8000)
+ -> Redis/RQ (:6379)
+ -> one Python worker for qa + default queues
+```
+
+The worker stays on the host machine because it owns the GPU path. Redis can run in Docker. The API and web app can run either directly on the host or through the `full` Docker Compose profile.
+
+## Prerequisites
+
+- Windows 11.
+- NVIDIA GPU for full video pipeline runs.
+- Python 3.10 virtualenv at `.venv`.
+- Node.js 18+ with dependencies installed in `web`.
+- Docker Desktop.
+- `ffmpeg` available on `PATH`.
+- Local model directories under `models/` for full pipeline and QA generation.
+
+Install dependencies:
+
+```powershell
+python -m venv .venv
+.\.venv\Scripts\Activate.ps1
+pip install -r requirements.txt
+cd web
+npm install
+```
+
+Install the CUDA PyTorch build separately when setting up a new machine:
+
+```powershell
+pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
+```
+
+## Recommended Local Demo Startup
+
+Open separate terminals from the repository root.
+
+Terminal 1: Redis
+
+```powershell
+docker compose up -d redis
+```
+
+Terminal 2: API
+
+```powershell
+.\.venv\Scripts\python.exe server.py
+```
+
+For a clean smoke run without automatic maintenance or backup side effects:
+
+```powershell
+$env:NOTEGEN_AUTO_MAINTENANCE="0"
+$env:NOTEGEN_AUTO_BACKUP="0"
+.\.venv\Scripts\python.exe server.py
+```
+
+Terminal 3: Worker
+
+```powershell
+.\.venv\Scripts\python.exe scripts\run_worker.py
+```
+
+Only run one worker. The v1 GPU model assumes serial execution across QA and generation jobs.
+
+Terminal 4: Web
+
+```powershell
+cd web
+npm run dev
+```
+
+Open:
+
+```text
+http://localhost:3000
+```
+
+## Production Build Check
+
+Before a demo checkpoint, verify the web app builds:
+
+```powershell
+cd web
+npm run lint
+npm run test
+npx tsc --noEmit
+npm run build
+```
+
+## Full Compose Option
+
+Redis, API, and web can be started through Docker Compose:
+
+```powershell
+docker compose --profile full up -d --build
+```
+
+The worker still runs on the host:
+
+```powershell
+.\.venv\Scripts\python.exe scripts\run_worker.py
+```
+
+Use the full Compose path when you want to test container wiring. Use the host path when you want the shortest local iteration loop.
+
+## Environment Variables
+
+Common local variables:
+
+| Variable | Default | Purpose |
+|---|---:|---|
+| `NOTEGEN_REDIS_URL` | `redis://127.0.0.1:6379/0` | Redis queue backend. |
+| `NOTEGEN_DB_PATH` | `data/notegen.db` | SQLite database path. |
+| `NOTEGEN_COOKIE_SECURE` | `0` | Set to `1` only behind HTTPS. |
+| `NOTEGEN_VERIFY_BASE` | `http://localhost:3000` | Base URL used in email verification links. |
+| `NOTEGEN_SMTP_HOST` / `NOTEGEN_SMTP_USER` / `NOTEGEN_SMTP_PASS` | unset | SMTP settings for real email verification. |
+| `NOTEGEN_AUTO_MAINTENANCE` | `1` | Set to `0` to stop startup/daily raw cleanup. |
+| `NOTEGEN_AUTO_BACKUP` | `1` | Set to `0` to stop startup/daily backup. |
+| `NOTEGEN_BACKUP_DIR` | `backups/` | Backup output directory. |
+| `NOTEGEN_MIN_FREE_RATIO` | `0.15` | Disk low-water mark. |
+| `NEXT_PUBLIC_API_URL` | `http://localhost:8000` | API URL baked into the web build. |
+
+## Health Check
+
+Verify API and Redis:
+
+```powershell
+Invoke-RestMethod http://127.0.0.1:8000/api/health
+```
+
+Expected shape:
+
+```json
+{
+ "ok": true,
+ "redis": true,
+ "queue_depth": 0,
+ "disk": {
+ "low": false
+ }
+}
+```
+
+Run the smoke script after all services are up:
+
+```powershell
+powershell -ExecutionPolicy Bypass -File scripts\smoke_local.ps1
+```
+
+## Shutdown
+
+Stop Docker Redis:
+
+```powershell
+docker compose down
+```
+
+Stop host API, worker, and web processes with `Ctrl+C` in their terminals.
+
+If they were started in the background, find them with:
+
+```powershell
+Get-CimInstance Win32_Process |
+ Where-Object { $_.CommandLine -match 'server\.py|run_worker\.py|next' } |
+ Select-Object ProcessId,CommandLine
+```
+
+Then stop a specific process:
+
+```powershell
+Stop-Process -Id
+```
+
+## Git Hygiene
+
+Running the local stack can change runtime data:
+
+- Redis writes to `data/redis`.
+- API startup can create backups in `backups/`.
+- maintenance can remove old files under `data/raw` and `data/audio`.
+
+Before committing, check:
+
+```powershell
+git status --short
+```
+
+Do not include runtime data in a v1 code/docs commit unless that is the explicit intent.
diff --git a/docs/SMOKE_TEST.md b/docs/SMOKE_TEST.md
new file mode 100644
index 0000000..e87d5f8
--- /dev/null
+++ b/docs/SMOKE_TEST.md
@@ -0,0 +1,166 @@
+# Local Smoke Test
+
+This guide describes the v1 local smoke path for NoteGen. It verifies that the local stack is ready for a demo without running a full GPU video pipeline.
+
+## What It Checks
+
+`scripts/smoke_local.ps1` checks:
+
+- Redis is reachable on `127.0.0.1:6379`.
+- API health responds from `http://127.0.0.1:8000/api/health`.
+- Exactly one root `scripts/run_worker.py` process is running.
+- Public notes can be read.
+- Optional login works when smoke credentials are provided.
+- Private notes can be read after login.
+- DOCX export returns a valid binary response.
+- `web` can produce a production build unless `-SkipBuild` is passed.
+
+The script does not enqueue a full video generation job and does not run the GPU-heavy pipeline.
+
+## Prerequisites
+
+- Windows PowerShell.
+- Docker Desktop running.
+- Python virtualenv installed at `.venv`.
+- Frontend dependencies installed in `web/node_modules`.
+- Redis, API, and one worker started.
+
+Start the services in separate terminals:
+
+```powershell
+docker compose up -d redis
+.\.venv\Scripts\python.exe server.py
+.\.venv\Scripts\python.exe scripts\run_worker.py
+cd web
+npm run dev
+```
+
+For a release smoke where you do not want startup maintenance or automatic backups to change local data, start the API with:
+
+```powershell
+$env:NOTEGEN_AUTO_MAINTENANCE="0"
+$env:NOTEGEN_AUTO_BACKUP="0"
+.\.venv\Scripts\python.exe server.py
+```
+
+## Run The Smoke
+
+Run the full smoke from the repository root:
+
+```powershell
+powershell -ExecutionPolicy Bypass -File scripts\smoke_local.ps1
+```
+
+Run a faster smoke that skips the web production build:
+
+```powershell
+powershell -ExecutionPolicy Bypass -File scripts\smoke_local.ps1 -SkipBuild
+```
+
+Run against a different API base:
+
+```powershell
+powershell -ExecutionPolicy Bypass -File scripts\smoke_local.ps1 -ApiBase http://127.0.0.1:8000
+```
+
+## Optional Login Check
+
+Set a verified test account before running the script:
+
+```powershell
+$env:NOTEGEN_SMOKE_EMAIL="demo@example.com"
+$env:NOTEGEN_SMOKE_PASSWORD="your-password"
+powershell -ExecutionPolicy Bypass -File scripts\smoke_local.ps1
+```
+
+If these variables are not set, the login and private-notes checks are skipped with a warning. That warning is acceptable for an unauthenticated infrastructure smoke.
+
+## Expected Result
+
+A passing infrastructure smoke ends with:
+
+```text
+== Summary ==
+Failures: 0
+```
+
+Warnings are acceptable when they are intentional, for example:
+
+- login skipped because no smoke credentials were provided.
+- web build skipped because `-SkipBuild` was passed.
+
+## Manual Browser Checks
+
+After the script passes, use the browser for the parts that require real UI interaction:
+
+1. Open `http://localhost:3000/notebooks`.
+2. Confirm public notes and the create-note entry render.
+3. Open an existing note.
+4. Confirm video playback and seeking.
+5. Confirm chapter navigation and transcript highlighting.
+6. Export Markdown.
+7. Export Word.
+8. Create or copy a share link and open `/s/{token}`.
+9. If logged in, ask one QA question and confirm timestamp citations seek the video.
+
+The current v1 demo artifacts are stored in `output/playwright/`:
+
+- `v1-home.png`
+- `v1-login-gate.png`
+- `v1-workspace.png`
+- `v1-demo.gif`
+- `v1-export.docx`
+
+## Troubleshooting
+
+### Redis TCP Fails
+
+Start Redis:
+
+```powershell
+docker compose up -d redis
+```
+
+### API Health Fails
+
+Start the API from the repository root:
+
+```powershell
+.\.venv\Scripts\python.exe server.py
+```
+
+Then verify:
+
+```powershell
+Invoke-RestMethod http://127.0.0.1:8000/api/health
+```
+
+### Worker Check Fails
+
+Start exactly one worker:
+
+```powershell
+.\.venv\Scripts\python.exe scripts\run_worker.py
+```
+
+Do not run multiple workers on one GPU. The current v1 architecture assumes single-worker serial execution.
+
+### DOCX Export Fails
+
+Check that the API is running and that `python-docx` is installed in the virtualenv:
+
+```powershell
+.\.venv\Scripts\python.exe -c "import docx; print('ok')"
+```
+
+### Web Build Fails
+
+Run the frontend checks directly:
+
+```powershell
+cd web
+npm run lint
+npm run test
+npx tsc --noEmit
+npm run build
+```
diff --git a/docs/frontend-redesign.md b/docs/frontend-redesign.md
index 5085174..362c067 100644
--- a/docs/frontend-redesign.md
+++ b/docs/frontend-redesign.md
@@ -1,5 +1,10 @@
# NoteGen 前端重设计方案(NotebookLM 风格)
+> 2026-06-25 v1 收敛更新:`FluidBG.tsx`、`ParticleBG.tsx`、`AskBar.tsx`
+> 已确认无运行代码引用并删除。核心页面已完成 Warm Fold token 迁移:
+> `ChatPanel`、`NoteWorkspace`、`CreateNotePanel`。剩余未完成项集中在浏览器
+> walkthrough、截图/GIF、a11y 复查和可提交工作树收敛。
+
> 2026-06-10 · 参照 [NotebookLM](https://notebooklm.google/) · 配套视觉稿见会话内 mockup
> 范围:首页(笔记本库)+ 笔记详情(三栏工作台)两个核心页,其余页面只做 token 级统一
> 2026-06-15 状态更新:P0/P1 主体已落地,实际产品入口调整为 `/` 精简 landing、
diff --git a/docs/memory/2026-06-25-v1-demo-checkpoint.md b/docs/memory/2026-06-25-v1-demo-checkpoint.md
new file mode 100644
index 0000000..60ceddf
--- /dev/null
+++ b/docs/memory/2026-06-25-v1-demo-checkpoint.md
@@ -0,0 +1,165 @@
+# 2026-06-25 v1 Demo Checkpoint
+
+## Context
+
+This memory records the v1 release-convergence pass after the Warm Fold UI work,
+QA/export/share implementation, and local smoke automation work.
+
+The goal was not to add more product surface. The goal was to make the current
+project easier to verify, demonstrate, and roll back.
+
+## Completed
+
+- Cleared the active frontend lint baseline in `web/components/ChatPanel.tsx`
+ and `web/lib/progress.ts`.
+- Migrated the core workspace UI away from mixed legacy visual tokens:
+ - `web/components/ChatPanel.tsx`
+ - `web/components/NoteWorkspace.tsx`
+ - `web/components/CreateNotePanel.tsx`
+- Removed unused legacy visual components:
+ - `web/components/AskBar.tsx`
+ - `web/components/FluidBG.tsx`
+ - `web/components/ParticleBG.tsx`
+- Added `scripts/smoke_local.ps1` for repeatable local infrastructure smoke.
+- Added release docs:
+ - `CHANGELOG.md`
+ - `docs/SMOKE_TEST.md`
+ - `docs/DEPLOY_LOCAL.md`
+- Linked the v1 checkpoint docs from `README.md`.
+- Ignored generated backup zip files with `backups/*.zip`.
+
+## Verification
+
+Frontend verification passed after the code changes:
+
+```powershell
+cd web
+npm run lint
+npm run test
+npx tsc --noEmit
+npm run build
+```
+
+Results:
+
+- ESLint completed with 0 errors.
+- The remaining ESLint output was the existing warning baseline:
+ - `` usage warnings in image-heavy UI files.
+ - One unused eslint-disable warning in `TranscriptPanel`.
+- Vitest passed 6 files / 17 tests.
+- TypeScript passed.
+- Next production build passed.
+
+Token migration scan passed for the migrated core surfaces:
+
+```powershell
+rg "var\(--(bg|fg|accent|border|shadow|on-accent)|apple-card|apple-button|tag-chip" `
+ web\components\ChatPanel.tsx `
+ web\components\NoteWorkspace.tsx `
+ web\components\CreateNotePanel.tsx `
+ web\app\notebooks `
+ web\app\login `
+ web\app\register `
+ web\app\page.tsx -n
+```
+
+Result: no matches.
+
+## Smoke Status
+
+`scripts/smoke_local.ps1` passed earlier in this pass when Redis, API, and one
+worker were running. It verified:
+
+- Redis TCP availability.
+- API `/api/health`.
+- exactly one root worker process.
+- public notes API.
+- DOCX export.
+- web production build.
+
+After Redis was started again, the final rerun also passed:
+
+```powershell
+powershell -ExecutionPolicy Bypass -File scripts\smoke_local.ps1
+```
+
+Result:
+
+- Failures: 0
+- Warnings: 1
+- Warning: authenticated session was skipped because no
+ `NOTEGEN_SMOKE_EMAIL` / `NOTEGEN_SMOKE_PASSWORD` credentials were provided.
+
+Current honest status:
+
+- Code-level v1 checkpoint verification is green.
+- Automated local smoke exists and passes with Redis, API, and one worker.
+- Authenticated login/private-notes smoke still needs configured smoke
+ credentials.
+- Browser walkthrough screenshots and a lightweight demo GIF are captured.
+
+## Browser Walkthrough
+
+After the automated smoke passed, a production Next server was started with
+Redis, API, and one worker available. Playwright CLI was used at a 1440 x 1000
+viewport.
+
+Captured artifacts:
+
+- `output/playwright/v1-home.png`
+- `output/playwright/v1-login-gate.png`
+- `output/playwright/v1-workspace.png`
+- `output/playwright/v1-demo.gif`
+- `output/playwright/v1-export.docx`
+
+Validated:
+
+- `/` loads with title `NoteGen · 教学视频结构化笔记`.
+- `/notebooks` redirects unauthenticated users to `/login?next=%2Fnotebooks`.
+- `/notes/BV141Ly6LE7x_p0` loads the three-column workspace.
+- The source/chapter rail, note body, video panel, and tools panel render.
+- The next-chapter control seeks the video to `01:07` and updates the current
+ chapter card to `AI 订阅详情`.
+- Word export downloads a DOCX file successfully.
+
+Observed console errors:
+
+- `401 Unauthorized` for `/api/auth/me` in unauthenticated mode.
+- `401 Unauthorized` for `/api/bookmarks` in unauthenticated mode.
+
+These match the current unauthenticated walkthrough. Authenticated notebooks,
+bookmarks, share creation, and QA polling still require a configured smoke user.
+
+## Worktree Notes
+
+The expected code/docs changes are:
+
+- `.gitignore`
+- `README.md`
+- `CHANGELOG.md`
+- `docs/SMOKE_TEST.md`
+- `docs/DEPLOY_LOCAL.md`
+- `docs/memory/2026-06-25-v1-demo-checkpoint.md`
+- `scripts/smoke_local.ps1`
+- `web/components/ChatPanel.tsx`
+- `web/components/CreateNotePanel.tsx`
+- `web/components/NoteWorkspace.tsx`
+- `web/lib/progress.ts`
+- deleted `web/components/AskBar.tsx`
+- deleted `web/components/FluidBG.tsx`
+- deleted `web/components/ParticleBG.tsx`
+- demo artifacts under `output/playwright/`
+
+Runtime Redis files may also appear modified after local service runs:
+
+- `data/redis/appendonlydir/appendonly.aof.1.incr.aof`
+- `data/redis/dump.rdb`
+
+Do not include those Redis runtime files in a code/docs checkpoint commit unless
+the intent is to update repository-seeded Redis state.
+
+## Next
+
+1. Add smoke credentials and rerun authenticated smoke.
+2. Commit code/docs and demo artifacts without Redis runtime state.
+3. Tag or branch the resulting checkpoint as `v1.0.0-demo`.
diff --git a/output/playwright/v1-demo.gif b/output/playwright/v1-demo.gif
new file mode 100644
index 0000000..a295644
Binary files /dev/null and b/output/playwright/v1-demo.gif differ
diff --git a/output/playwright/v1-export.docx b/output/playwright/v1-export.docx
new file mode 100644
index 0000000..0ddc3b4
Binary files /dev/null and b/output/playwright/v1-export.docx differ
diff --git a/output/playwright/v1-home.png b/output/playwright/v1-home.png
new file mode 100644
index 0000000..8181113
Binary files /dev/null and b/output/playwright/v1-home.png differ
diff --git a/output/playwright/v1-login-gate.png b/output/playwright/v1-login-gate.png
new file mode 100644
index 0000000..153300b
Binary files /dev/null and b/output/playwright/v1-login-gate.png differ
diff --git a/output/playwright/v1-workspace.png b/output/playwright/v1-workspace.png
new file mode 100644
index 0000000..a39fd00
Binary files /dev/null and b/output/playwright/v1-workspace.png differ
diff --git a/scripts/smoke_local.ps1 b/scripts/smoke_local.ps1
new file mode 100644
index 0000000..0306ee2
--- /dev/null
+++ b/scripts/smoke_local.ps1
@@ -0,0 +1,157 @@
+param(
+ [string]$ApiBase = "http://127.0.0.1:8000",
+ [string]$WebDir = (Join-Path $PSScriptRoot "..\web"),
+ [string]$Email = $env:NOTEGEN_SMOKE_EMAIL,
+ [string]$Password = $env:NOTEGEN_SMOKE_PASSWORD,
+ [switch]$SkipBuild
+)
+
+$ErrorActionPreference = "Continue"
+$failures = New-Object System.Collections.Generic.List[string]
+$warnings = New-Object System.Collections.Generic.List[string]
+
+function Pass([string]$Message) {
+ Write-Host " OK $Message" -ForegroundColor Green
+}
+
+function Warn([string]$Message) {
+ $warnings.Add($Message) | Out-Null
+ Write-Host " WARN $Message" -ForegroundColor Yellow
+}
+
+function Fail([string]$Message) {
+ $failures.Add($Message) | Out-Null
+ Write-Host " FAIL $Message" -ForegroundColor Red
+}
+
+function Invoke-SmokeCheck([string]$Name, [scriptblock]$Check) {
+ Write-Host ""
+ Write-Host "== $Name =="
+ try {
+ & $Check
+ } catch {
+ Fail "$Name`: $($_.Exception.Message)"
+ }
+}
+
+function Assert-True([bool]$Condition, [string]$Message) {
+ if (-not $Condition) {
+ throw $Message
+ }
+}
+
+Write-Host "NoteGen local smoke"
+Write-Host "API: $ApiBase"
+Write-Host "Web: $WebDir"
+
+Invoke-SmokeCheck "Redis TCP" {
+ $redisOnline = Test-NetConnection -ComputerName "127.0.0.1" -Port 6379 -InformationLevel Quiet -WarningAction SilentlyContinue
+ Assert-True $redisOnline "127.0.0.1:6379 is not reachable. Start it with: docker compose up -d redis"
+ Pass "127.0.0.1:6379 accepts TCP connections"
+}
+
+Invoke-SmokeCheck "API health" {
+ $health = Invoke-RestMethod -Uri "$ApiBase/api/health" -Method Get -TimeoutSec 8
+ Assert-True ($null -ne $health) "empty health response"
+ Assert-True ([bool]$health.redis) "health.redis is false"
+ Assert-True (-not [bool]$health.disk.low) "disk.low is true"
+ Pass "redis=$($health.redis), queue_depth=$($health.queue_depth), disk.low=$($health.disk.low)"
+}
+
+Invoke-SmokeCheck "Worker process" {
+ $workers = Get-CimInstance Win32_Process |
+ Where-Object { $_.CommandLine -match "scripts[\\/]+run_worker\.py" }
+ $workerIds = @($workers | ForEach-Object { $_.ProcessId })
+ $rootWorkers = @($workers | Where-Object { $workerIds -notcontains $_.ParentProcessId })
+ if (-not $rootWorkers) {
+ Warn "no run_worker.py process found. Start one worker with: .\.venv\Scripts\python.exe scripts\run_worker.py"
+ return
+ }
+ Assert-True ($rootWorkers.Count -eq 1) "expected exactly one worker, found $($rootWorkers.Count)"
+ Pass "one run_worker.py process is running (pid=$($rootWorkers[0].ProcessId))"
+}
+
+Invoke-SmokeCheck "Public notes API" {
+ $notes = Invoke-RestMethod -Uri "$ApiBase/api/notes/public" -Method Get -TimeoutSec 8
+ Assert-True ($notes -is [array]) "public notes response is not an array"
+ Pass "public notes endpoint returned $($notes.Count) item(s)"
+}
+
+Invoke-SmokeCheck "Authenticated session" {
+ if ([string]::IsNullOrWhiteSpace($Email) -or [string]::IsNullOrWhiteSpace($Password)) {
+ Warn "skip login. Set NOTEGEN_SMOKE_EMAIL and NOTEGEN_SMOKE_PASSWORD, or pass -Email/-Password"
+ return
+ }
+
+ $body = @{ email = $Email; password = $Password } | ConvertTo-Json
+ $login = Invoke-RestMethod `
+ -Uri "$ApiBase/api/auth/login" `
+ -Method Post `
+ -ContentType "application/json" `
+ -Body $body `
+ -SessionVariable session `
+ -TimeoutSec 8
+ Assert-True ($null -ne $login.id) "login response has no user id"
+
+ $mine = Invoke-RestMethod -Uri "$ApiBase/api/notes/mine" -Method Get -WebSession $session -TimeoutSec 8
+ Assert-True ($mine -is [array]) "private notes response is not an array"
+ Pass "login ok for $Email; private notes returned $($mine.Count) item(s)"
+}
+
+Invoke-SmokeCheck "DOCX export" {
+ $body = @{
+ filename = "smoke.docx"
+ markdown = "# Smoke Test`n`n- DOCX export endpoint is available."
+ } | ConvertTo-Json
+ $response = Invoke-WebRequest `
+ -UseBasicParsing `
+ -Uri "$ApiBase/api/export/docx" `
+ -Method Post `
+ -ContentType "application/json" `
+ -Body $body `
+ -TimeoutSec 15
+ $contentLength = $response.Content.Length
+ Assert-True ($response.StatusCode -eq 200) "expected 200, got $($response.StatusCode)"
+ Assert-True ($contentLength -gt 1000) "DOCX response is unexpectedly small: $contentLength bytes"
+ Pass "DOCX export returned $contentLength bytes"
+}
+
+Invoke-SmokeCheck "Web production build" {
+ if ($SkipBuild) {
+ Warn "skip web build because -SkipBuild was passed"
+ return
+ }
+ Assert-True (Test-Path $WebDir) "web directory not found: $WebDir"
+ Push-Location $WebDir
+ try {
+ npm run build
+ Assert-True ($LASTEXITCODE -eq 0) "npm run build exited with $LASTEXITCODE"
+ Pass "npm run build completed"
+ } finally {
+ Pop-Location
+ }
+}
+
+Write-Host ""
+Write-Host "== Summary =="
+Write-Host "Failures: $($failures.Count)"
+Write-Host "Warnings: $($warnings.Count)"
+
+if ($warnings.Count -gt 0) {
+ Write-Host ""
+ Write-Host "Warnings:"
+ foreach ($warning in $warnings) {
+ Write-Host " - $warning"
+ }
+}
+
+if ($failures.Count -gt 0) {
+ Write-Host ""
+ Write-Host "Failures:"
+ foreach ($failure in $failures) {
+ Write-Host " - $failure"
+ }
+ exit 1
+}
+
+exit 0
diff --git a/web/README.md b/web/README.md
index 12da347..6485b15 100644
--- a/web/README.md
+++ b/web/README.md
@@ -22,7 +22,8 @@ app/
page.tsx # landing:粒子背景 + hero + demo 卡片
notes/[id]/page.tsx # 详情:左视频 + 右笔记
components/
- ParticleBG.tsx # canvas 流动粒子
+ NoteWorkspace.tsx # 三栏工作台:章节、笔记、视频与工具
+ ChatPanel.tsx # 时间戳问答入口
VideoPlayer.tsx # Plyr 包装,dynamic import 避开 SSR
ChapterChip.tsx # Dynamic Island 风格章节浮窗
NotesContent.tsx # 笔记主体(顶部卡 + 知识点速览 + 章节 + 术语表)
diff --git a/web/components/AskBar.tsx b/web/components/AskBar.tsx
deleted file mode 100644
index b91fe27..0000000
--- a/web/components/AskBar.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-"use client";
-import { MessageCircle } from "lucide-react";
-import { useLang } from "./LangContext";
-
-/**
- * 「对视频提问」输入条占位(Phase 2,接口契约见 docs/frontend-redesign.md §5)。
- * 后端 /api/notes/{id}/ask 落地后替换为真实 ChatPanel 入口。
- */
-export default function AskBar() {
- const { lang } = useLang();
- return (
-
-
-
- {lang === "en"
- ? "Ask about this video — answers will cite timestamps…"
- : "对这个视频提问,回答附时间戳引用…"}
-
-
- {lang === "en" ? "Coming soon" : "规划中"}
-
-
+
+
{t("登录后可对这个视频提问,回答附时间戳引用", "Sign in to ask about this video")}
+ className="shrink-0 text-xs font-semibold text-[var(--wf-accent)] hover:underline">
{t("去登录", "Sign in")}