From a4a6187de20346d2f35d79481e55fd796fc908c1 Mon Sep 17 00:00:00 2001 From: jlightner Date: Sat, 4 Apr 2026 11:56:06 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20Updated=207=20Forgejo=20wiki=20pages=20?= =?UTF-8?q?with=20M024=20feature=20documentation=20cove=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - "Home.md" - "Data-Model.md" - "API-Surface.md" - "Frontend.md" - "Pipeline.md" - "Player.md" - "Chat-Engine.md" GSD-Task: S06/T01 --- .gsd/KNOWLEDGE.md | 6 + .gsd/milestones/M024/M024-ROADMAP.md | 2 +- .../milestones/M024/slices/S05/S05-SUMMARY.md | 94 +++++++++++++++ .gsd/milestones/M024/slices/S05/S05-UAT.md | 60 ++++++++++ .../M024/slices/S05/tasks/T02-VERIFY.json | 16 +++ .gsd/milestones/M024/slices/S06/S06-PLAN.md | 84 +++++++++++++- .../M024/slices/S06/S06-RESEARCH.md | 99 ++++++++++++++++ .../M024/slices/S06/tasks/T01-PLAN.md | 109 ++++++++++++++++++ .../M024/slices/S06/tasks/T01-SUMMARY.md | 88 ++++++++++++++ 9 files changed, 556 insertions(+), 2 deletions(-) create mode 100644 .gsd/milestones/M024/slices/S05/S05-SUMMARY.md create mode 100644 .gsd/milestones/M024/slices/S05/S05-UAT.md create mode 100644 .gsd/milestones/M024/slices/S05/tasks/T02-VERIFY.json create mode 100644 .gsd/milestones/M024/slices/S06/S06-RESEARCH.md create mode 100644 .gsd/milestones/M024/slices/S06/tasks/T01-PLAN.md create mode 100644 .gsd/milestones/M024/slices/S06/tasks/T01-SUMMARY.md diff --git a/.gsd/KNOWLEDGE.md b/.gsd/KNOWLEDGE.md index c090549..2426c63 100644 --- a/.gsd/KNOWLEDGE.md +++ b/.gsd/KNOWLEDGE.md @@ -374,3 +374,9 @@ **Context:** Some API endpoints need to be publicly accessible but provide different behavior for authenticated users (e.g., showing draft posts to the owner). Using the standard `get_current_user` dependency rejects unauthenticated requests with 401. **Fix:** Create `get_optional_user` using `OAuth2PasswordBearer(auto_error=False)`. When `auto_error=False`, missing/invalid tokens return `None` instead of raising 401. The endpoint receives `Optional[User]` and branches on whether the user is present and matches the resource owner. + +## Pass CSS module styles as Record to shared utilities + +**Context:** When extracting a shared React utility (e.g., `parseChatCitations`) that renders JSX with CSS module class names, the utility needs the calling component's styles object. TypeScript's `CSSModuleClasses` type is structurally incompatible across module boundaries — passing `styles` typed as the specific module's interface causes type errors. + +**Fix:** Type the styles parameter as `Record`. CSS modules already satisfy this shape at runtime. This allows any component to pass its own CSS module styles to the shared utility without type gymnastics. diff --git a/.gsd/milestones/M024/M024-ROADMAP.md b/.gsd/milestones/M024/M024-ROADMAP.md index e60c8ee..89286d1 100644 --- a/.gsd/milestones/M024/M024-ROADMAP.md +++ b/.gsd/milestones/M024/M024-ROADMAP.md @@ -10,5 +10,5 @@ Shorts pipeline goes end-to-end with captioning and templates. Player gets key m | S02 | [A] Key Moment Pins on Player Timeline | low | — | ✅ | Key technique moments appear as clickable pins on the player timeline | | S03 | [A] Embed Support (iframe Snippet) | low | — | ✅ | Creators can copy an iframe embed snippet to put the player on their own site | | S04 | [B] Auto-Captioning + Template System | medium | — | ✅ | Shorts have Whisper-generated animated subtitles and creator-configurable intro/outro cards | -| S05 | [B] Citation UX Improvements | low | — | ⬜ | Chat citations show timestamp links that seek the player and source cards with video thumbnails | +| S05 | [B] Citation UX Improvements | low | — | ✅ | Chat citations show timestamp links that seek the player and source cards with video thumbnails | | S06 | Forgejo KB Update — Shorts, Embed, Citations | low | S01, S02, S03, S04, S05 | ⬜ | Forgejo wiki updated with shorts pipeline, embed system, citation architecture | diff --git a/.gsd/milestones/M024/slices/S05/S05-SUMMARY.md b/.gsd/milestones/M024/slices/S05/S05-SUMMARY.md new file mode 100644 index 0000000..7721ce9 --- /dev/null +++ b/.gsd/milestones/M024/slices/S05/S05-SUMMARY.md @@ -0,0 +1,94 @@ +--- +id: S05 +parent: M024 +milestone: M024 +provides: + - Video metadata (source_video_id, start_time, end_time, video_filename) in search results and chat SSE source events + - Shared chatCitations.tsx and formatTime.ts utilities +requires: + [] +affects: + - S06 +key_files: + - backend/search_service.py + - backend/chat_service.py + - frontend/src/utils/chatCitations.tsx + - frontend/src/utils/formatTime.ts + - frontend/src/api/chat.ts + - frontend/src/pages/ChatPage.tsx + - frontend/src/pages/ChatPage.module.css + - frontend/src/components/ChatWidget.tsx + - frontend/src/components/ChatWidget.module.css +key_decisions: + - Batch-fetch SourceVideo filenames using same pattern as creator batch-fetch + - Video fields are empty/None for non-key_moment types to keep dict shape uniform + - Pass CSS module styles as Record to shared citation parser to avoid CSSModuleClasses structural typing mismatch +patterns_established: + - Shared parseChatCitations utility for citation rendering across ChatPage and ChatWidget + - Shared formatTime utility replacing duplicated time formatting across 4+ files +observability_surfaces: + - none +drill_down_paths: + - .gsd/milestones/M024/slices/S05/tasks/T01-SUMMARY.md + - .gsd/milestones/M024/slices/S05/tasks/T02-SUMMARY.md +duration: "" +verification_result: passed +completed_at: 2026-04-04T11:47:32.918Z +blocker_discovered: false +--- + +# S05: [B] Citation UX Improvements + +**Chat citations now carry video metadata (source_video_id, start_time, end_time, video_filename) from backend through SSE to frontend, where timestamp badge links and video filename labels appear on source cards.** + +## What Happened + +Two tasks delivered end-to-end citation UX improvements for chat source cards. + +**T01 (Backend):** Propagated video metadata through three code paths. In `_enrich_qdrant_results()`, added batch-fetch of SourceVideo filenames (same pattern as creator batch-fetch) and four video fields to the enriched dict. In `_keyword_search_and()`, added the same four fields from the already-joined SourceVideo. In `_build_sources()` in chat_service.py, passed all four fields through to SSE source events. Non-key_moment types get empty/None values, keeping dict shape uniform. + +**T02 (Frontend):** Extended the ChatSource TypeScript interface with video fields. Created `utils/chatCitations.tsx` — a shared `parseChatCitations()` function that replaces duplicate implementations in both ChatPage and ChatWidget. Created `utils/formatTime.ts` — a shared hour-aware time formatter replacing copies in 4+ files. Updated source card rendering in both components: when `start_time` is defined, a timestamp badge links to `/watch/:id?t=N`; video filename appears as subtle metadata. Added CSS classes `.timestampBadge` and `.videoMeta` to both module stylesheets. + +## Verification + +Backend: Both `from chat_service import _build_sources` and `from search_service import SearchService` import cleanly (exit 0). Frontend: `npm run build` passes with zero TypeScript/Vite errors. Deduplication confirmed: no `CITATION_RE` or local `parseCitations` functions remain in ChatPage.tsx or ChatWidget.tsx. + +## Requirements Advanced + +None. + +## Requirements Validated + +None. + +## New Requirements Surfaced + +None. + +## Requirements Invalidated or Re-scoped + +None. + +## Deviations + +T01: Fixed operator precedence bug — `(sv.filename or "") if sv else ""` needed parens. T02: Used `Record` for styles param instead of strict CSSModuleClasses interface. Added `.sourceContent` wrapper div in ChatWidget for vertical layout. + +## Known Limitations + +Timestamp badges link to `/watch/:id?t=N` but actual player seek-on-load depends on WatchPage reading the `t` query param (already implemented in prior slices). No runtime integration test against live Qdrant/SSE — verified via import checks and build. + +## Follow-ups + +None. + +## Files Created/Modified + +- `backend/search_service.py` — Added video metadata fields to _enrich_qdrant_results() and _keyword_search_and() for key_moment results +- `backend/chat_service.py` — Added video fields to _build_sources() SSE source events +- `frontend/src/api/chat.ts` — Extended ChatSource interface with source_video_id, start_time, end_time, video_filename +- `frontend/src/utils/chatCitations.tsx` — New shared parseChatCitations utility extracted from ChatPage and ChatWidget +- `frontend/src/utils/formatTime.ts` — New shared formatTime utility replacing duplicated implementations +- `frontend/src/pages/ChatPage.tsx` — Replaced local citation parser with shared import, added timestamp badges and video metadata to source cards +- `frontend/src/pages/ChatPage.module.css` — Added .timestampBadge and .videoMeta CSS classes +- `frontend/src/components/ChatWidget.tsx` — Replaced local citation parser with shared import, added timestamp badges and video metadata to source cards +- `frontend/src/components/ChatWidget.module.css` — Added .timestampBadge, .videoMeta, and .sourceContent CSS classes diff --git a/.gsd/milestones/M024/slices/S05/S05-UAT.md b/.gsd/milestones/M024/slices/S05/S05-UAT.md new file mode 100644 index 0000000..20f4369 --- /dev/null +++ b/.gsd/milestones/M024/slices/S05/S05-UAT.md @@ -0,0 +1,60 @@ +# S05: [B] Citation UX Improvements — UAT + +**Milestone:** M024 +**Written:** 2026-04-04T11:47:32.918Z + +## UAT: Citation UX Improvements + +### Preconditions +- Chrysopedia stack running on ub01:8096 +- At least one key_moment with a source_video_id exists in the database +- Chat endpoint functional with LightRAG backend + +### Test 1: Backend — Video metadata in search results +1. Open browser to `http://ub01:8096` +2. Search for a term that matches a key moment (e.g., a technique name from a known video) +3. Open browser dev tools → Network tab +4. Observe the search API response JSON +5. **Expected:** Key moment results include `source_video_id` (non-empty string), `start_time` (number or null), `end_time` (number or null), `video_filename` (string or empty) + +### Test 2: Backend — Video metadata in chat SSE source events +1. Open the chat interface at `http://ub01:8096/chat` +2. Ask a question that should retrieve key moment sources +3. Open browser dev tools → Network tab → find the SSE stream +4. Inspect the `sources` SSE event data +5. **Expected:** Source objects for key_moment-type results include `source_video_id`, `start_time`, `end_time`, `video_filename` fields + +### Test 3: Frontend — Timestamp badge on chat source cards +1. In the chat page, after receiving a response with key_moment sources +2. Look at the source cards below the response +3. **Expected:** Source cards for key moments show a timestamp badge (e.g., "1:23 – 2:45") that is a clickable link +4. Click the timestamp badge +5. **Expected:** Navigates to `/watch/{video_id}?t={start_time}` — the video watch page at the correct timestamp + +### Test 4: Frontend — Video filename metadata on source cards +1. In the chat page with key_moment sources visible +2. Look at the source card metadata +3. **Expected:** Video filename appears as subtle text below or beside the source title + +### Test 5: Frontend — ChatWidget has same citation features +1. Navigate to a technique page that has the ChatWidget in the sidebar +2. Ask a question that returns key_moment sources +3. **Expected:** Source cards in the widget also show timestamp badges and video filename, matching ChatPage behavior + +### Test 6: Frontend — Shared citation parser deduplication +1. Open `frontend/src/pages/ChatPage.tsx` in editor +2. Search for `CITATION_RE` or `function parseCitations` +3. **Expected:** Neither found — all citation parsing uses the shared `utils/chatCitations.tsx` +4. Repeat for `frontend/src/components/ChatWidget.tsx` +5. **Expected:** Same — no local citation parsing code + +### Test 7: Frontend — formatTime utility +1. Open `frontend/src/utils/formatTime.ts` +2. **Expected:** Exports a `formatTime(seconds: number): string` function +3. Format: `M:SS` for times under 1 hour, `H:MM:SS` for 1 hour or more +4. Verify ChatPage and ChatWidget import from this shared utility (not local copies) + +### Edge Cases +- **Non-key_moment sources:** Technique page sources should render normally without timestamp badges (start_time is undefined/null) +- **Missing video_filename:** Source card should render without the filename line when video_filename is empty +- **Zero start_time:** A start_time of 0 should still show the badge (formatted as "0:00") diff --git a/.gsd/milestones/M024/slices/S05/tasks/T02-VERIFY.json b/.gsd/milestones/M024/slices/S05/tasks/T02-VERIFY.json new file mode 100644 index 0000000..fcfd5f3 --- /dev/null +++ b/.gsd/milestones/M024/slices/S05/tasks/T02-VERIFY.json @@ -0,0 +1,16 @@ +{ + "schemaVersion": 1, + "taskId": "T02", + "unitId": "M024/S05/T02", + "timestamp": 1775303160365, + "passed": true, + "discoverySource": "task-plan", + "checks": [ + { + "command": "cd frontend", + "exitCode": 0, + "durationMs": 6, + "verdict": "pass" + } + ] +} diff --git a/.gsd/milestones/M024/slices/S06/S06-PLAN.md b/.gsd/milestones/M024/slices/S06/S06-PLAN.md index d7dd896..e0f7177 100644 --- a/.gsd/milestones/M024/slices/S06/S06-PLAN.md +++ b/.gsd/milestones/M024/slices/S06/S06-PLAN.md @@ -1,6 +1,88 @@ # S06: Forgejo KB Update — Shorts, Embed, Citations -**Goal:** Document new systems in Forgejo knowledgebase +**Goal:** Forgejo wiki updated with M024 feature documentation covering shorts publishing, embed support, key moment pins, auto-captioning/templates, and citation UX improvements. **Demo:** After this: Forgejo wiki updated with shorts pipeline, embed system, citation architecture ## Tasks +- [x] **T01: Updated 7 Forgejo wiki pages with M024 feature documentation covering shorts publishing, embed support, timeline pins, auto-captioning, templates, and citation UX** — Clone the Chrysopedia Forgejo wiki on ub01, update Home.md, Data-Model.md, API-Surface.md, Frontend.md, Pipeline.md, Player.md, and Chat-Engine.md with M024 S01-S05 feature content, then commit and push. + +**CRITICAL:** Never use the Forgejo wiki PATCH API — it corrupts pages (see KNOWLEDGE.md). Git clone → edit → push only. + +All SSH/git operations happen on ub01 via `ssh ub01`. +Wiki repo: `https://git.xpltd.co/xpltdco/chrysopedia.wiki.git` + +## Content to add per page: + +### Home.md +Add to the feature list / recent additions: +- Shorts publishing flow: public shareable URLs via /shorts/{token}, share_token generated at pipeline completion +- Embed support: chrome-free /embed/:videoId player, iframe snippet with audio-aware height +- Key moment timeline pins: 12px color-coded circle pins (technique=cyan, settings=amber, reasoning=purple, workflow=green), active-state highlighting, touch-friendly hit areas +- Inline collapsible player on technique pages with bibliography seek wiring +- Auto-captioning: ASS karaoke subtitles from Whisper word-level timings, \k tags for word-by-word highlighting +- Template system: creator-configurable intro/outro cards via admin API, ffmpeg lavfi card rendering, concat demuxer assembly +- Citation UX: timestamp badge links to /watch/:id?t=N, video filename on source cards, shared chatCitations.tsx + formatTime.ts utilities + +### Data-Model.md +Add new columns: +- `share_token` (String(16), unique-indexed, nullable) on GeneratedShort — generated via secrets.token_urlsafe(8) at pipeline completion +- `captions_enabled` (Boolean, default false) on GeneratedShort — indicates successful ASS subtitle generation +- `shorts_template` (JSONB, nullable) on Creator — intro/outro card config with parse_template_config normalizer +Add migrations: 026 (share_token + backfill + unique index), 027 (captions_enabled), 028 (shorts_template) + +### API-Surface.md +Add new endpoints: +- `GET /api/v1/public/shorts/{share_token}` — unauthenticated, returns metadata + presigned MinIO URL, 404 for missing/non-complete shorts +- `GET /api/v1/admin/creators/{id}/shorts-template` — returns JSONB template config +- `PUT /api/v1/admin/creators/{id}/shorts-template` — updates template config with validation (hex color, duration 1.0-5.0) +Update: `POST generate-shorts` now accepts `captions` boolean parameter +Update endpoint total count (was ~92, now ~95) + +### Frontend.md +Add new pages/components: +- ShortPlayer (`/shorts/:token`) — public page outside ProtectedRoute, fetches via unauthenticated API, renders video + metadata + share/embed copy buttons +- EmbedPlayer (`/embed/:videoId`) — chrome-free, registered at top-level Routes before AppShell catch-all, content-type-aware height (120px audio, 405px video), "Powered by Chrysopedia" branding +- ChapterMarkers upgrade: 12px circle pins replacing 3px lines, color-coded by content_type via --color-pin-{type} CSS custom properties, active-state 1.3x scale, ::before inset:-6px touch targets, tooltips with title + time range + content type +- Inline collapsible player on TechniquePage between summary and body, grid-template-rows 0fr/1fr animation, multi-source-video selector, bibliography seek buttons +Add shared utilities: +- `utils/clipboard.ts` — shared copyToClipboard (navigator.clipboard + execCommand fallback) +- `utils/chatCitations.tsx` — shared parseChatCitations replacing duplicate implementations +- `utils/formatTime.ts` — shared hour-aware time formatter +Add HighlightQueue updates: share link + embed code copy buttons, collapsible template config panel (intro/outro text, duration sliders, show/hide, color picker, font), per-highlight captions toggle + +### Pipeline.md +Add new modules: +- `caption_generator.py`: generate_ass_captions() converts Whisper word-level timings to ASS subtitles with \k karaoke tags, clip-relative timing. Non-blocking — failures log WARNING, don't fail stage. +- `card_renderer.py`: render_card() (lavfi color + drawtext), render_card_to_file() (ffmpeg executor), build_concat_list() + concat_segments() (ffmpeg concat demuxer), parse_template_config() (JSONB normalizer with defaults). Cards include anullsrc silent audio for codec-compatible concat. +- `shorts_generator.py` updates: extract_clip() accepts optional ass_path for subtitle burn-in, extract_clip_with_template() for intro/main/outro concatenation +- stage_generate_shorts updates: loads transcripts for captions, loads creator templates for cards, generates share_token on completion +Note 45 unit tests (17 caption + 28 card) + +### Player.md +Add key moment pins section: +- ChapterMarkers: 12px circle pins, color-coded (technique=cyan, settings=amber, reasoning=purple, workflow=green) +- Active-state: 1.3x scale when playback within time range +- Touch: ::before pseudo-element with inset:-6px +- Tooltips: title + formatted time range + content type label +Add inline player on technique pages: +- Collapsible section between summary and body +- Multi-source-video selector for multi-video technique pages +- Bibliography time links render as seek buttons when inline player active, Links to WatchPage otherwise +Add embed player: +- /embed/:videoId — chrome-free, no header/nav/footer +- Content-type-aware: video (405px) vs audio (120px) +- "Powered by Chrysopedia" branding link with noopener + +### Chat-Engine.md +Add citation metadata propagation: +- Backend: search_service enriches results with source_video_id, start_time, end_time, video_filename via batch-fetch of SourceVideo filenames +- Backend: chat_service _build_sources() passes video fields through SSE source events +- Frontend: ChatSource interface extended with video fields +- Frontend: shared parseChatCitations() replaces duplicate citation parsers in ChatPage and ChatWidget +- Frontend: timestamp badge links to /watch/:id?t=N on source cards +- Frontend: video filename metadata on source cards + - Estimate: 45m + - Files: Home.md (wiki), Data-Model.md (wiki), API-Surface.md (wiki), Frontend.md (wiki), Pipeline.md (wiki), Player.md (wiki), Chat-Engine.md (wiki) + - Verify: 1. `ssh ub01 'cd /tmp/chrysopedia-wiki-m024 && git log --oneline -1'` — shows M024 commit +2. `ssh ub01 'curl -s https://git.xpltd.co/api/v1/repos/xpltdco/chrysopedia/wiki/pages -H "Authorization: token $(cat /vmPool/r/repos/xpltdco/.forgejo-token)" | python3 -c "import sys,json; print(len(json.load(sys.stdin)))"'` — returns 20 +3. `ssh ub01 'cd /tmp/chrysopedia-wiki-m024 && grep -c share_token Data-Model.md'` — returns ≥1 diff --git a/.gsd/milestones/M024/slices/S06/S06-RESEARCH.md b/.gsd/milestones/M024/slices/S06/S06-RESEARCH.md new file mode 100644 index 0000000..e230e59 --- /dev/null +++ b/.gsd/milestones/M024/slices/S06/S06-RESEARCH.md @@ -0,0 +1,99 @@ +# S06 Research: Forgejo KB Update — Shorts, Embed, Citations + +## Summary + +Straightforward documentation slice following the established pattern (D034, prior KB slices M019/S06 through M023/S05). Clone Forgejo wiki via git on ub01, update existing pages with M024 feature content from S01-S05, commit, push. **Never use the Forgejo wiki PATCH API** (corrupts pages per KNOWLEDGE.md). + +## Recommendation + +Single task. Clone wiki, update 7 pages, push. Content sourced entirely from S01-S05 summaries already inlined in planner context. No new decisions to document from M024. + +## Implementation Landscape + +### Established Pattern (from M023/S05, M022/S07, etc.) + +1. SSH to ub01 +2. `git clone https://git.xpltd.co/xpltdco/chrysopedia.wiki.git` into a temp dir +3. Edit markdown files +4. `git add . && git commit -m "..." && git push origin main` +5. Verify via wiki pages API count (currently 20 pages) + +### Current Wiki State + +20 pages exist. Relevant ones to update: + +| Page | What to add from M024 | +|------|----------------------| +| **Home.md** | Shorts publishing flow (public URLs), embed support (iframe snippets), key moment timeline pins, auto-captioning + template system, citation timestamp links | +| **Data-Model.md** | `share_token` (String(16), unique-indexed) on GeneratedShort, `captions_enabled` (Boolean) on GeneratedShort, `shorts_template` (JSONB) on Creator. Migrations 026-028. | +| **API-Surface.md** | 3 new endpoints: GET /api/v1/public/shorts/{share_token} (unauthenticated), GET/PUT /api/v1/admin/creators/{id}/shorts-template. `captions` param added to generate-shorts. Update total from ~71 to ~74. | +| **Frontend.md** | ShortPlayer (public /shorts/:token), EmbedPlayer (chrome-free /embed/:videoId before AppShell), ChapterMarkers (12px circle pins, color-coded by content_type, active state), inline collapsible player on TechniquePage with bibliography seek, shared copyToClipboard utility, shared chatCitations.tsx + formatTime.ts utilities | +| **Pipeline.md** | caption_generator.py (ASS karaoke subtitles from word-level timings, \k tags), card_renderer.py (ffmpeg lavfi drawtext cards, concat demuxer for intro/main/outro), parse_template_config normalizer, non-blocking enrichment pattern | +| **Player.md** | Key moment pins (color-coded: technique=cyan, settings=amber, reasoning=purple, workflow=green), active-state highlighting, touch-friendly ::before hit areas, inline player on technique pages, embed player | +| **Chat-Engine.md** | Citation video metadata propagation: source_video_id, start_time, end_time, video_filename in search results and SSE source events. Batch-fetch SourceVideo filenames. Timestamp badge links to /watch/:id?t=N | + +### New Content by Feature + +**S01 — Shorts Publishing Flow:** +- share_token column (secrets.token_urlsafe(8)), generated at pipeline completion +- Alembic 026: add column + backfill existing + unique index +- Public endpoint GET /api/v1/public/shorts/{share_token} — unauthenticated, returns metadata + presigned MinIO URL +- Returns 404 for both missing and non-complete shorts (no status leaking) +- ShortPlayer.tsx at /shorts/:token — public page outside ProtectedRoute +- Share link (🔗) and embed code (📋) copy buttons on HighlightQueue for completed shorts + +**S02 — Key Moment Pins:** +- ChapterMarkers.tsx: 12px circle pins replacing 3px line markers +- Color-coded by content_type via --color-pin-{type} CSS custom properties +- Active-state highlighting (1.3x scale when playback within time range) +- Tooltips: title + formatted time range + content type label +- ::before pseudo-element with inset:-6px for touch-friendly hit areas +- Inline collapsible player on TechniquePage between summary and body sections +- Multi-source-video selector dropdown for multi-video technique pages +- Bibliography time links → seek buttons when inline player active, Links otherwise + +**S03 — Embed Support:** +- EmbedPlayer.tsx at /embed/:videoId — chrome-free (no header/nav/footer) +- Route registered at top-level Routes before AppShell catch-all +- Video/audio support with content-type-aware height (120px audio, 405px video) +- "Powered by Chrysopedia" branding link with noopener +- Copy Embed Code button on WatchPage with audio-aware iframe snippet +- Shared copyToClipboard utility extracted to utils/clipboard.ts + +**S04 — Auto-Captioning + Template System:** +- caption_generator.py: ASS subtitles with \k karaoke tags, word-by-word highlighting from Whisper word timings +- card_renderer.py: ffmpeg lavfi (color + drawtext) card generation, anullsrc silent audio for codec-compatible concat +- build_concat_list + concat_segments: ffmpeg concat demuxer for intro/main/outro assembly +- parse_template_config: JSONB normalizer with defaults +- Non-blocking: caption/card failures log WARNING, never fail parent short +- shorts_template JSONB on Creator model (Alembic 028) +- captions_enabled Boolean on GeneratedShort (Alembic 027) +- Admin API: GET/PUT /api/v1/admin/creators/{id}/shorts-template +- Frontend: collapsible template config panel in HighlightQueue, per-highlight captions toggle +- 45 unit tests (17 caption + 28 card) + +**S05 — Citation UX Improvements:** +- Backend: video metadata (source_video_id, start_time, end_time, video_filename) added to search_service enrichment and chat_service SSE source events +- Batch-fetch SourceVideo filenames pattern (same as creator batch-fetch) +- Frontend: chatCitations.tsx shared utility, formatTime.ts shared utility +- Timestamp badge links to /watch/:id?t=N on source cards +- Video filename metadata on source cards + +### No New Decisions + +M024 made no new registered decisions (no D04x entries). Existing decisions (D042-D044 from M023) already documented in prior KB update. + +### Constraints + +- **CRITICAL:** Never use Forgejo wiki PATCH API. Git clone → edit → push only (KNOWLEDGE.md). +- Wiki repo URL: `https://git.xpltd.co/xpltdco/chrysopedia.wiki.git` +- All SSH/git operations happen on ub01 via `ssh ub01` +- Content must come from S01-S05 summaries — no fabrication +- Current endpoint count: ~92 (from rg count of @router decorators across all routers) +- Current wiki page count: 20 + +### Verification + +1. `git push origin main` — exit code 0 +2. Wiki page count API — should remain 20 (no new pages, only updates) +3. Spot-check one updated page via API diff --git a/.gsd/milestones/M024/slices/S06/tasks/T01-PLAN.md b/.gsd/milestones/M024/slices/S06/tasks/T01-PLAN.md new file mode 100644 index 0000000..8ad6ad4 --- /dev/null +++ b/.gsd/milestones/M024/slices/S06/tasks/T01-PLAN.md @@ -0,0 +1,109 @@ +--- +estimated_steps: 67 +estimated_files: 7 +skills_used: [] +--- + +# T01: Update 7 Forgejo wiki pages with M024 feature content + +Clone the Chrysopedia Forgejo wiki on ub01, update Home.md, Data-Model.md, API-Surface.md, Frontend.md, Pipeline.md, Player.md, and Chat-Engine.md with M024 S01-S05 feature content, then commit and push. + +**CRITICAL:** Never use the Forgejo wiki PATCH API — it corrupts pages (see KNOWLEDGE.md). Git clone → edit → push only. + +All SSH/git operations happen on ub01 via `ssh ub01`. +Wiki repo: `https://git.xpltd.co/xpltdco/chrysopedia.wiki.git` + +## Content to add per page: + +### Home.md +Add to the feature list / recent additions: +- Shorts publishing flow: public shareable URLs via /shorts/{token}, share_token generated at pipeline completion +- Embed support: chrome-free /embed/:videoId player, iframe snippet with audio-aware height +- Key moment timeline pins: 12px color-coded circle pins (technique=cyan, settings=amber, reasoning=purple, workflow=green), active-state highlighting, touch-friendly hit areas +- Inline collapsible player on technique pages with bibliography seek wiring +- Auto-captioning: ASS karaoke subtitles from Whisper word-level timings, \k tags for word-by-word highlighting +- Template system: creator-configurable intro/outro cards via admin API, ffmpeg lavfi card rendering, concat demuxer assembly +- Citation UX: timestamp badge links to /watch/:id?t=N, video filename on source cards, shared chatCitations.tsx + formatTime.ts utilities + +### Data-Model.md +Add new columns: +- `share_token` (String(16), unique-indexed, nullable) on GeneratedShort — generated via secrets.token_urlsafe(8) at pipeline completion +- `captions_enabled` (Boolean, default false) on GeneratedShort — indicates successful ASS subtitle generation +- `shorts_template` (JSONB, nullable) on Creator — intro/outro card config with parse_template_config normalizer +Add migrations: 026 (share_token + backfill + unique index), 027 (captions_enabled), 028 (shorts_template) + +### API-Surface.md +Add new endpoints: +- `GET /api/v1/public/shorts/{share_token}` — unauthenticated, returns metadata + presigned MinIO URL, 404 for missing/non-complete shorts +- `GET /api/v1/admin/creators/{id}/shorts-template` — returns JSONB template config +- `PUT /api/v1/admin/creators/{id}/shorts-template` — updates template config with validation (hex color, duration 1.0-5.0) +Update: `POST generate-shorts` now accepts `captions` boolean parameter +Update endpoint total count (was ~92, now ~95) + +### Frontend.md +Add new pages/components: +- ShortPlayer (`/shorts/:token`) — public page outside ProtectedRoute, fetches via unauthenticated API, renders video + metadata + share/embed copy buttons +- EmbedPlayer (`/embed/:videoId`) — chrome-free, registered at top-level Routes before AppShell catch-all, content-type-aware height (120px audio, 405px video), "Powered by Chrysopedia" branding +- ChapterMarkers upgrade: 12px circle pins replacing 3px lines, color-coded by content_type via --color-pin-{type} CSS custom properties, active-state 1.3x scale, ::before inset:-6px touch targets, tooltips with title + time range + content type +- Inline collapsible player on TechniquePage between summary and body, grid-template-rows 0fr/1fr animation, multi-source-video selector, bibliography seek buttons +Add shared utilities: +- `utils/clipboard.ts` — shared copyToClipboard (navigator.clipboard + execCommand fallback) +- `utils/chatCitations.tsx` — shared parseChatCitations replacing duplicate implementations +- `utils/formatTime.ts` — shared hour-aware time formatter +Add HighlightQueue updates: share link + embed code copy buttons, collapsible template config panel (intro/outro text, duration sliders, show/hide, color picker, font), per-highlight captions toggle + +### Pipeline.md +Add new modules: +- `caption_generator.py`: generate_ass_captions() converts Whisper word-level timings to ASS subtitles with \k karaoke tags, clip-relative timing. Non-blocking — failures log WARNING, don't fail stage. +- `card_renderer.py`: render_card() (lavfi color + drawtext), render_card_to_file() (ffmpeg executor), build_concat_list() + concat_segments() (ffmpeg concat demuxer), parse_template_config() (JSONB normalizer with defaults). Cards include anullsrc silent audio for codec-compatible concat. +- `shorts_generator.py` updates: extract_clip() accepts optional ass_path for subtitle burn-in, extract_clip_with_template() for intro/main/outro concatenation +- stage_generate_shorts updates: loads transcripts for captions, loads creator templates for cards, generates share_token on completion +Note 45 unit tests (17 caption + 28 card) + +### Player.md +Add key moment pins section: +- ChapterMarkers: 12px circle pins, color-coded (technique=cyan, settings=amber, reasoning=purple, workflow=green) +- Active-state: 1.3x scale when playback within time range +- Touch: ::before pseudo-element with inset:-6px +- Tooltips: title + formatted time range + content type label +Add inline player on technique pages: +- Collapsible section between summary and body +- Multi-source-video selector for multi-video technique pages +- Bibliography time links render as seek buttons when inline player active, Links to WatchPage otherwise +Add embed player: +- /embed/:videoId — chrome-free, no header/nav/footer +- Content-type-aware: video (405px) vs audio (120px) +- "Powered by Chrysopedia" branding link with noopener + +### Chat-Engine.md +Add citation metadata propagation: +- Backend: search_service enriches results with source_video_id, start_time, end_time, video_filename via batch-fetch of SourceVideo filenames +- Backend: chat_service _build_sources() passes video fields through SSE source events +- Frontend: ChatSource interface extended with video fields +- Frontend: shared parseChatCitations() replaces duplicate citation parsers in ChatPage and ChatWidget +- Frontend: timestamp badge links to /watch/:id?t=N on source cards +- Frontend: video filename metadata on source cards + +## Inputs + +- ``~/.gsd/milestones/M024/slices/S01/S01-SUMMARY.md` — shorts publishing flow details` +- ``~/.gsd/milestones/M024/slices/S02/S02-SUMMARY.md` — key moment pins details` +- ``~/.gsd/milestones/M024/slices/S03/S03-SUMMARY.md` — embed support details` +- ``~/.gsd/milestones/M024/slices/S04/S04-SUMMARY.md` — auto-captioning + template details` +- ``~/.gsd/milestones/M024/slices/S05/S05-SUMMARY.md` — citation UX details` + +## Expected Output + +- ``Home.md` — updated with M024 feature summary` +- ``Data-Model.md` — updated with share_token, captions_enabled, shorts_template columns and migrations 026-028` +- ``API-Surface.md` — updated with 3 new endpoints and captions parameter` +- ``Frontend.md` — updated with ShortPlayer, EmbedPlayer, ChapterMarkers upgrade, inline player, shared utilities` +- ``Pipeline.md` — updated with caption_generator, card_renderer, shorts_generator changes` +- ``Player.md` — updated with pin markers, inline player, embed player` +- ``Chat-Engine.md` — updated with citation metadata propagation` + +## Verification + +1. `ssh ub01 'cd /tmp/chrysopedia-wiki-m024 && git log --oneline -1'` — shows M024 commit +2. `ssh ub01 'curl -s https://git.xpltd.co/api/v1/repos/xpltdco/chrysopedia/wiki/pages -H "Authorization: token $(cat /vmPool/r/repos/xpltdco/.forgejo-token)" | python3 -c "import sys,json; print(len(json.load(sys.stdin)))"'` — returns 20 +3. `ssh ub01 'cd /tmp/chrysopedia-wiki-m024 && grep -c share_token Data-Model.md'` — returns ≥1 diff --git a/.gsd/milestones/M024/slices/S06/tasks/T01-SUMMARY.md b/.gsd/milestones/M024/slices/S06/tasks/T01-SUMMARY.md new file mode 100644 index 0000000..f40733f --- /dev/null +++ b/.gsd/milestones/M024/slices/S06/tasks/T01-SUMMARY.md @@ -0,0 +1,88 @@ +--- +id: T01 +parent: S06 +milestone: M024 +provides: [] +requires: [] +affects: [] +key_files: ["Home.md", "Data-Model.md", "API-Surface.md", "Frontend.md", "Pipeline.md", "Player.md", "Chat-Engine.md"] +key_decisions: ["Used git clone/push for wiki updates per KNOWLEDGE.md (never use Forgejo wiki PATCH API)"] +patterns_established: [] +drill_down_paths: [] +observability_surfaces: [] +duration: "" +verification_result: "All 3 verification checks pass: git log shows M024 commit 0451abf, Forgejo API returns 20 wiki pages, grep finds share_token in Data-Model.md." +completed_at: 2026-04-04T11:56:03.189Z +blocker_discovered: false +--- + +# T01: Updated 7 Forgejo wiki pages with M024 feature documentation covering shorts publishing, embed support, timeline pins, auto-captioning, templates, and citation UX + +> Updated 7 Forgejo wiki pages with M024 feature documentation covering shorts publishing, embed support, timeline pins, auto-captioning, templates, and citation UX + +## What Happened +--- +id: T01 +parent: S06 +milestone: M024 +key_files: + - Home.md + - Data-Model.md + - API-Surface.md + - Frontend.md + - Pipeline.md + - Player.md + - Chat-Engine.md +key_decisions: + - Used git clone/push for wiki updates per KNOWLEDGE.md (never use Forgejo wiki PATCH API) +duration: "" +verification_result: passed +completed_at: 2026-04-04T11:56:03.190Z +blocker_discovered: false +--- + +# T01: Updated 7 Forgejo wiki pages with M024 feature documentation covering shorts publishing, embed support, timeline pins, auto-captioning, templates, and citation UX + +**Updated 7 Forgejo wiki pages with M024 feature documentation covering shorts publishing, embed support, timeline pins, auto-captioning, templates, and citation UX** + +## What Happened + +Cloned the Chrysopedia Forgejo wiki on ub01, read all 5 slice summaries (S01-S05) for content details, updated Home.md, Data-Model.md, API-Surface.md, Frontend.md, Pipeline.md, Player.md, and Chat-Engine.md with comprehensive M024 feature documentation, then committed and pushed via git (per KNOWLEDGE.md rule to never use Forgejo wiki PATCH API). + +## Verification + +All 3 verification checks pass: git log shows M024 commit 0451abf, Forgejo API returns 20 wiki pages, grep finds share_token in Data-Model.md. + +## Verification Evidence + +| # | Command | Exit Code | Verdict | Duration | +|---|---------|-----------|---------|----------| +| 1 | `ssh ub01 'cd /tmp/chrysopedia-wiki-m024 && git log --oneline -1'` | 0 | ✅ pass | 500ms | +| 2 | `ssh ub01 'curl -s .../wiki/pages | python3 -c "..."'` | 0 | ✅ pass | 800ms | +| 3 | `ssh ub01 'cd /tmp/chrysopedia-wiki-m024 && grep -c share_token Data-Model.md'` | 0 | ✅ pass | 300ms | + + +## Deviations + +None. + +## Known Issues + +None. + +## Files Created/Modified + +- `Home.md` +- `Data-Model.md` +- `API-Surface.md` +- `Frontend.md` +- `Pipeline.md` +- `Player.md` +- `Chat-Engine.md` + + +## Deviations +None. + +## Known Issues +None.