Edit one element end-to-end: sidecar schema → generated form → canonical save → live preview #3
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/deckle#3
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What to build
The first true tracer bullet (docs/DECISIONS.md D6): load element sidecar schemas from
editor/elements/*.yaml(label, icon, group, version, params with types, body policy, placement), expose them via API, render a schema-generated param form in the SPA for one element on one page, and on Save write the canonically serialized file so the preview updates via LiveReload. v1 field types: text, textarea, link, image (path string for now), select, checkbox, number, color, date.Acceptance criteria
Blocked by
Agent Brief
Category: enhancement
Summary: First editing tracer bullet: load element sidecar schemas from
editor/elements/*.yaml, serve them via the harness API, render a schema-generated param form in the SPA for one element on one page, and on Save write the canonically serialized file (via the engine from #2) so the preview updates through LiveReload.Dependency note: this issue is hard-blocked by #2 (shortcode round-trip engine). Do not start it until #2 is merged; the save path must go through that engine, not a parallel serializer. The issue body's
docs/DECISIONS.md D6reference points at a file that is not committed — this brief is self-contained.Current behavior:
The harness (from #1) serves the SolidJS shell with a placeholder form pane and a live preview iframe, and (once #2 lands) can parse a content file into an element tree and serialize it canonically, with a JSON debug endpoint. There is no schema system, no form generation, and no write path: nothing in the tool can yet modify a content file. The sample site has no sidecar schemas and (before #2's corpus) no shortcode-based content.
Desired behavior:
Sidecar schemas. The tool discovers element schemas from
editor/elements/*.yamlunder the site directory (one file per element type, filename matching the shortcode name). Each sidecar describes: a human-readable label, an icon identifier, a picker group, a schema version, a body policy (whether the element has a body), a placement declaration, and a list of params. Each param has a name, a field type, a label, and optionally a default, required flag, and (for selects) options. The nine v1 field types:text,textarea,link,image(a plain path string in this slice),select,checkbox,number,color,date. A suggested minimal shape (exact key naming is the implementer's choice, but every concept below must be representable):Schema loading is validated: a malformed YAML file, an unknown field type, or a structurally invalid sidecar must not crash the tool — it produces an actionable error (naming the file and the problem) that is exposed via the API and listed in the UI. Valid sidecars still load when invalid ones are present.
API. The harness API exposes: the loaded element schemas (with any load errors), a page's element tree (from #2's parser), and a save operation for a page that accepts the edited element data, validates it against the schema, and writes the canonically serialized file. Save rewrites only that one page's file and nothing else on disk.
Form generation. Selecting an element on a page renders a form generated purely from its schema — labels, defaults, required markers, select options all come from the sidecar. There is no per-element hand-written form code; a new sidecar dropped into
editor/elements/gets a working form with zero SPA changes. Each of the nine field types renders as an appropriate input. How the user reaches the element is minimal in this slice: a crude element list for one page (e.g. derived from the parsed tree of the currently previewed sample page) is sufficient — the real tree/element-list navigation is #4.Validation. A required param that is missing/empty, or a value that doesn't fit the field type (e.g. non-numeric in
number), blocks the save and is shown inline on the offending field. Server-side validation is authoritative; inline display in the form is required either way.Preview. After a successful save, the preview iframe reflects the change via Hugo LiveReload without a manual reload and without the tool restarting Hugo.
Sample site. The sample site gains at least two element types — each with a Hugo shortcode template that renders visibly and a matching sidecar — plus at least one content page using them, so the end-to-end flow is demonstrable and testable. Minimal markup is fine; the full element library is #14.
Key interfaces:
placementandversionare parsed, validated for basic well-formedness, and served — their behavior (nesting rules, migrations) belongs to later issues.Acceptance criteria:
editor/elements/are discovered and validated; unknown field types or malformed files produce actionable errors listed in the UI while valid sidecars still loadOut of scope:
Landing audit — PR #20 (
afk/3)Verdict: PASS.
Verification signal relied on: Forgejo Actions CI
build-and-test (pull_request)— green (ci / build-and-test, Successful in 1m4s). Not re-run. Its run coversgo vet ./...,go test ./...,npx tsc --noEmit,npm test,npm run build.Checks performed:
feat: edit one element end-to-end …is valid Conventional Commits. ✓afk/3; body carries a workingCloses #3. ✓mainHEAD == merge-base; merge is clean, no conflict. ✓internal/element— strict sidecar decode (unknown keys rejected), actionable per-file load errors, valid sidecars survive alongside invalid ones; nine field types;ValidateValues/CoerceValuescover required/type/select/date and int-vs-float coercion. ✓internal/serversave path — content-relative path withfilepath.IsLocaltraversal guard,resolveShortcodereturns 409 on drift, server-authoritative 422 field errors,mergeParamspreserves non-schema params,atomicWriteFile(same-dir temp + chmod + rename) writes only the target page. ✓Minor note (non-blocking): string params — including
textarea— reject line breaks. Defensible, since shortcode params serialize on a single line; rich-body editing is explicitly deferred to #7.Awaiting free-text merge confirmation from the human.