diff --git a/.sisyphus/PHASE_3_HUMAN_ACTIONS.md b/.sisyphus/PHASE_3_HUMAN_ACTIONS.md new file mode 100644 index 0000000..ffc8034 --- /dev/null +++ b/.sisyphus/PHASE_3_HUMAN_ACTIONS.md @@ -0,0 +1,114 @@ +# Phase 3 — Human Actions Required Before Cutover + +> Stacked on PR #4. Merge order: PR #1 → #2 → #3 → #4 → #5. + +## 1. Optional: load handwriting font + +Mơ's Notebook uses a `Caveat`/`Be Vietnam Pro` handwriting fallback stack. For best visual: + +- Add to `index.html` ``: + ```html + + + + ``` +- Or self-host via the same workflow as the existing `Be Vietnam Pro`. + +## 2. Sóng Đi — no infrastructure needed + +The recorder is fully client-side (WebAudio + MediaRecorder). Recorded audio stays on-device unless you later wire upload to Supabase Storage (not in this PR). + +## 3. Mơ persona illustration + +`Mơ viết thư cho bạn` currently renders text + SVG doodle placeholders. Once you hire the Vietnamese watercolor illustrator (per FINAL plan), drop replacement SVGs into `services/moNotebook.ts` `DOODLE_SVG_LIBRARY`. + +## 4. Personal World — Supabase RLS reminder + +`PersonalWorldBadge` calls `listOwnedTrips()` which uses the `trips_owner_all` RLS policy from Phase 0b. Verify this policy is still active in Supabase Studio before relying on the badge to scope correctly. + +## 5. F1 public share — verify slug uniqueness + +`PublicShareButton` uses `ensurePublicTrip()` which generates a fresh `share_slug` via `generateShareSlug()` if no public version exists. The `UNIQUE` constraint on `share_slug` in the migration catches collisions, but the client doesn't currently retry on collision — extremely rare (10^15 space) but worth flagging. + +## 6. Smoke test after deploy + +1. **Mơ's Notebook**: open a result view → click "✍️ Mơ viết thư cho bạn" → letter renders within 8s → click "In / Lưu PDF" → printable layout opens. +2. **Personal World badge**: log in as a user with 0 / 1 / 5 / 10+ trips and verify milestone progression. +3. **Tiếng Vùng dialect override**: open `About` page (or wherever you mount ``), pick "Miền Trung" → generate a trip to Sài Gòn → Mơ should now use central dialect instead of southern. +4. **Sóng Đi**: open a result view on a mobile device with mic permission → tap "Bắt đầu ghi" → 5s recording → waveform visualization appears → playback works. +5. **F1 public share**: log in, generate a trip, click "🔗 Chia sẻ công khai" → URL returned → open in incognito → SharedTripView loads with itinerary visible. + +## What this PR ships + +### Mơ's Notebook — End-of-trip handwritten letter +- `services/moNotebook.ts`: + - `composeMoLetter(trip)`: calls Gemini flash-lite with Mơ persona + structured JSON schema + - `buildDoodleSvg(seed)`: 4 preset SVG doodles (nón lá, cafe, biển, núi) + text fallback +- `components/MoNotebookModal.tsx`: + - Letter modal with paper-textured background, Caveat handwriting font + - In-modal `print()` action that opens a print-friendly window with full letter + doodle + - Graceful handling of RATE_LIMIT_EXCEEDED / BUDGET_EXCEEDED with VN copy +- Wired into result view as "✍️ Mơ viết thư cho bạn" CTA + +### Sóng Đi — Sound postcard (scaffolding) +- `services/songDi.ts`: + - Browser feature detection (MediaRecorder + getUserMedia + supported MIME types) + - `startSoundRecorder({ maxDurationMs, label })`: 5-second cap, cleans up MediaStream tracks + - `computeWaveform(blob, samples)`: client-side WebAudio decode + peak extraction for visualization +- `components/SongDiRecorder.tsx`: + - 5-state machine: idle/requesting/recording/processing/ready + - Live waveform visualization after recording + - Audio playback with `