Phase 2: document CRUD + auto-save

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
This commit is contained in:
prosolis
2026-06-25 20:30:39 -07:00
parent 9c98e97030
commit 5e00cdce88
13 changed files with 1006 additions and 42 deletions

View File

@@ -53,3 +53,56 @@ body {
button, a, input {
transition: all 200ms ease;
}
/* Editor body — serif Lora for prose, warm plum ink, roomy line height. The
title and headings use the Nunito UI face for contrast. */
.petal-prose {
font-family: var(--font-body);
font-size: 1.125rem;
line-height: 1.75;
color: var(--color-plum);
}
.petal-prose > * + * {
margin-top: 0.9em;
}
.petal-prose h1,
.petal-prose h2,
.petal-prose h3 {
font-family: var(--font-ui);
font-weight: 800;
line-height: 1.3;
}
.petal-prose h1 { font-size: 1.6em; }
.petal-prose h2 { font-size: 1.3em; }
.petal-prose h3 { font-size: 1.1em; }
.petal-prose ul,
.petal-prose ol {
padding-left: 1.4em;
}
.petal-prose ul { list-style: disc; }
.petal-prose ol { list-style: decimal; }
.petal-prose a {
color: var(--color-accent-hover);
text-decoration: underline;
}
.petal-prose blockquote {
border-left: 3px solid var(--color-border);
padding-left: 1em;
color: var(--color-muted);
font-style: italic;
}
.petal-prose code {
font-family: var(--font-mono);
font-size: 0.9em;
background: var(--color-surface-alt);
padding: 0.1em 0.35em;
border-radius: 6px;
}
/* Placeholder shown on the empty first paragraph (Tiptap Placeholder ext). */
.petal-prose p.is-editor-empty:first-child::before {
content: attr(data-placeholder);
color: var(--color-muted);
float: left;
height: 0;
pointer-events: none;
}