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
Replace the kitten's random generic tips with a deterministic, LLM-free
prose checker that reads the actual document and surfaces one gentle,
bilingual note at a time — quoting a slice of her own sentence so the
advice is clearly about *this* draft.
Rules (all conservative, tuned for a Mandarin-native ESL writer):
run-ons, comma splices, unclear antecedents, Oxford comma, a/an,
uncountable plurals, capitalize languages/days/months, subject-verb
agreement, plural-after-number, double determiner, "there is" + plural,
its/it's, than/then, comma-after-transition, doubled words, lowercase
"i", sentence capitals, and spacing around punctuation.
Each rule is paired with false-positive guards (e.g. "a university",
"After dinner, I went home and read", existing Oxford lists) and pinned
by a 45-case Vitest suite (npm test).
Also: bubbles now linger proportional to how much there is to read
(zh+en) and pause-on-hover, so denser advice no longer vanishes mid-read.
Claude-Session: https://claude.ai/code/session_016Yr6jELuRc7hyzYLccQKZd
Vendored Hunspell en.aff/en.dic into web/public/dictionaries/en/ (served as a
static asset + embedded in the binary, kept out of the JS bundle). dictionary-en
moved to a devDep — only used to source the files.
- useSpellChecker (App-level, loads once/session): fetches the dict, builds an
nspell instance, replays a localStorage personal word list; addWord persists
and bumps a version so consumers re-decorate. Ambient types in
src/types/nspell.d.ts (the package ships none).
- SpellCheck Tiptap extension: misspellings as ProseMirror decorations (no
stored marks), recomputed on edit / caret move / checker swap. Latin-only
tokenizer so CJK is never flagged; skips short tokens + all-caps acronyms;
exempts the caret word to avoid mid-typing jitter. Reuses mapOffset (now
exported from SuggestionHighlight); wordAt resolves the exact span on click.
- MisspellCard: soft rose wavy underline, bilingual popover with up to 5 nspell
corrections (click to replace) + add-to-dictionary. Closes on outside-pointer,
edit, or doc switch.
Chinese spell check intentionally omitted — nspell is dictionary-based and
English-only; Chinese typos (homophone 别字) need an LLM, out of v1 scope.
tsc/vite/go build+vet clean; live server serves both dict files; nspell
behavior smoke-tested.
Claude-Session: https://claude.ai/code/session_016Yr6jELuRc7hyzYLccQKZd
A cozy bottom-right mascot that reacts to the writing session: cheers on
accepted suggestions and word-count milestones, drops Mandarin-first writing
tips, suggests screen breaks after long stretches, and naps when idle.
- useCompanion: library-agnostic behavior engine (priority + cooldown paced
so it never nags); tips.ts holds all copy, bilingual zh-first.
- LottiePlayer wraps lottie-web's light build (offline, no eval/CDN) so it
bundles into the Go binary. Ships with an emoji-kitten placeholder per mood;
dropping a Lottie cat JSON into animations/index.ts is the only swap needed.
- Speech bubble uses the CJK-first font stack (spec Note #17).
Claude-Session: https://claude.ai/code/session_016Yr6jELuRc7hyzYLccQKZd
- Go module + chi server with embedded SPA serving and /api/health
- internal/config env loader (local-dev defaults; auth/copyleaks deferred)
- React 19 + Vite 6 + Tailwind v4 frontend with full Petal design tokens
- Frontend embedded into the binary via web/embed.go (go:embed all:dist)
- README dev workflow, .env.example, BUILD_PLAN progress tracker
- Verified end-to-end: binary serves health, embedded SPA, and SPA fallback