Two editor features that were in flight alongside the sound work:
- FontSize TipTap extension (rides on textStyle) with Small/Normal/Large/
Title presets in the toolbar; StatusBar + CSS support
- Image handling: internal/images handler, upload route + config, client
API, EditorCore wiring, and md/html/docx export support for images
Claude-Session: https://claude.ai/code/session_016Yr6jELuRc7hyzYLccQKZd
Add a fixed, GPU-driven petal layer that drifts soft blossoms over the
page (sparse, translucent, paused when hidden, skipped under
prefers-reduced-motion). Add a synthesized cute sound palette — bubble
pop, water droplet, soft bell, sparkle, cheer — generated as embedded
WAV assets (scripts/gen_sounds.py) and played through a quiet Web Audio
bus with soft-clipping and a persisted mute toggle in the status bar.
Sounds fire per suggestion type when fresh advice arrives (staggered,
old pending advice stays silent) and on companion bubbles by tone.
Claude-Session: https://claude.ai/code/session_016Yr6jELuRc7hyzYLccQKZd
Cross-document FTS5 search (trigram tokenizer for EN + space-free CJK, kept in
sync by triggers, back-filled from existing docs). GET /api/search uses the FTS
index for queries >=3 runes and a LIKE fallback for 1-2 (so 2-char Chinese words
resolve); snippets are built in Go with rune-aware boundaries and sentinel
highlights.
Tags: user-scoped tags + document_tags join (both cascade), idempotent
create/assign, per-tag doc counts. Doc list and search carry each doc's tags
(one tagsByDoc query). Frontend: useTags, TagChip/TagPicker/SearchBox, rewritten
DocList with chips + filter bar + search.
Tablet/touch: responsive sidebar drawer (hamburger + scrim <768px), coarse-
pointer tap targets, tap-to-open + outside-pointerdown-close for suggestion
cards.
Warm LLM-down state: useCheckpoint llmDown flag drives a gentle bilingual
StatusBar note (writing still saves locally).
Migration 0004 (tags + FTS). Tests: tags lifecycle, search EN/CJK/LIKE/update-
reindex. go build/vet/test, tsc, vite all clean; verified live on deployment host.
Claude-Session: https://claude.ai/code/session_016Yr6jELuRc7hyzYLccQKZd
Four enhancements to make the editor fit real school usage:
- Per-document tone (academic/professional/casual/humorous/creative/
persuasive/general): new documents.tone column (migration 0002), threaded
through the docs API, a bilingual ToneSelect dropdown on the title row, and
injected into the grammar-checkpoint LLM prompt so advice fits the register.
The voice pass stays tone-agnostic.
- Right-click word lookup: a new offline `lexicon` package serves definitions
(Wordset, modern ESL-friendly glosses) and synonyms (WordNet synsets first,
then frequency+stopword-ranked Moby for breadth) from gzipped embedded data,
behind /api/word/{word} with light morphology. The WordCard popover shows the
definition and tappable synonym pills that swap the word in place.
- Expanded writing stats: clicking the word count opens a StatsPanel with page
count, sentences, paragraphs, reading time, average word length, word variety,
and Flesch-Kincaid reading level — all computed client-side.
Claude-Session: https://claude.ai/code/session_016Yr6jELuRc7hyzYLccQKZd
Tier-1 voice-consistency pass: whole-document LLM review surfacing passages
that read tonally out of place (formal/over-polished/paraphrased-too-closely),
as honey-decorated `voice` flags with no correction (awareness-only).
- internal/llm/voice.go: RunVoice sends the whole document (no TruncateDoc),
MaxTokens 2048, 20s per-doc floor (VoiceInterval). Standalone voice prompt
in prompts.go (not bundled with the grammar checkpoint, per spec).
- internal/suggestions: POST /api/docs/:id/voice. replacePending is now
family-scoped (pendingScope) so grammar and voice never clobber each other's
pending flags; both passes return the unified pending set. check/voice share
one runPass helper. TestVoicePassCoexists covers both directions.
- Frontend: api.voiceDoc, useCheckpoint voicing/runVoice, honey "Check my
voice" toolbar pill, breathing honey dot in StatusBar.
Claude-Session: https://claude.ai/code/session_016Yr6jELuRc7hyzYLccQKZd
Backend (internal/llm): backend-agnostic LLMClient interface + factory
with vLLM (OpenAI-compat) and Ollama (native) clients, each Complete +
Stream. prompts.go holds the checkpoint and Ask Petal templates;
checkpoint.go salvages JSON from model output (brace-matched), enforces a
per-doc 30s RateLimiter, and truncates the doc to a latency cap.
internal/suggestions: POST /api/docs/:id/check runs a checkpoint and
replaces the doc's pending suggestions in one tx (accepted/rejected kept
as history); GET /api/docs/:id/suggestions lists pending;
POST /api/suggestions/:id/{accept,dismiss} resolves one. Throttled checks
return the current set rather than erroring.
Frontend: useCheckpoint (4s debounce, loads existing on open, stale-guard
tokens); SuggestionHighlight renders ProseMirror decorations re-anchored
by the `original` string on every doc change (not stored marks), with
precise textblock-offset→PM-position mapping; SuggestionCard shows the
type tag + diff + explanation and applies the replacement in-editor on
accept; breathing rose checkpoint dot in the StatusBar; fade-float +
breathe animations.
Tests: llm parse/rate-limit/truncate; suggestions full flow + rate-limit
over httptest with a stub client. Smoke-tested end-to-end against a fake
vLLM endpoint (anchoring verified) and the LLM-unreachable 502 path.
Claude-Session: https://claude.ai/code/session_016Yr6jELuRc7hyzYLccQKZd
Backend internal/docs: chi sub-router (list/create/get/update/delete)
mounted at /api/docs, scoped to the local user. Create uses RETURNING;
update is a COALESCE partial-update so rename and full editor save share
one PUT. JSON 404/400 errors; handlers_test.go walks the lifecycle.
Frontend: api/client.ts, useAutoSave (1.5s debounce + saveNow flush
before doc switch), EditorCore (Tiptap StarterKit/Underline/TextAlign/
Placeholder/CharacterCount) + Toolbar, DocList/DocListItem, StatusBar,
and an App.tsx that orchestrates load/select/create/delete with
optimistic sidebar patching. content + content_text + word_count are
emitted together on every edit. .petal-prose styling (Lora body, Nunito
headings).
Verified: tsc clean, vite build, go build, full CRUD smoke test incl.
CJK title round-trip and SPA serve.
Claude-Session: https://claude.ai/code/session_016Yr6jELuRc7hyzYLccQKZd