Files
rose-ash/plans/blog-editor-island.md
giles 2713636e36
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 17s
host: hand off the native SX-island editor (browser-capable session)
The editor is the interactivity layer — it belongs on the --http island pipeline
(SSRs + hydrates islands), not the http-listen host, and needs browser/Playwright
iteration which this worktree lacks. plans/blog-editor-island.md is the handoff:
goal, architecture (docs-side island -> host /new), the live host contract
(form-urlencoded title/sx_content/status -> 303), the sx_content markup to emit
(standard tags, NOT legacy ~kg-* cards), island authoring gotchas, and pointers.
Host side is ready (ingest proven; CORS on request). Phase 5.5 marked handed off.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 21:04:21 +00:00

4.1 KiB

Handoff: native SX-island blog editor

Handed off from the host-on-sx loop (2026-06-19). Build this in a browser-capable session (Playwright installed) — a reactive island only proves out when it hydrates in a browser; this worktree has no Playwright.

Goal

A native SX reactive island WYSIWYG block editor for blog posts — replacing the legacy shared/static/scripts/sx-editor.js (Koenig-era JS, ~2500 lines). It edits blocks reactively and, on publish, emits sx_content (SX element markup) + a title + status, and submits to the host's create endpoint.

Architecture (decided this session)

  • The editor is the interactivity layer, so it lives on the --http island pipeline (sx.rose-ash.com, which already SSRs + hydrates islands), NOT in the http-listen host (the host deliberately doesn't do island hydration — see plans/host-on-sx.md Phase 5).
  • It publishes to the host: the host serves blog.rose-ash.com and owns the durable store + create/render. The editor is a docs-side island that talks to the host's API. Two cooperating SX servers: host = content/API/state, --http = interactive UI.

The host contract (already live + proven)

POST /new on the host (blog.rose-ash.com) — works today:

  • Body: form-urlencoded title, sx_content, status (draft/published).
  • Behaviour: slug derived from title, post stored in the durable KV, 303 redirect to /<slug>/.
  • host/blog-form-submit in lib/host/blog.sx; route host/blog-open-create-routes (currently UNGUARDED experimental — gate before real use).
  • A form POST (303 redirect) needs no CORS. If the editor uses fetch instead, the host needs CORS on /new — the host loop can add dream-cors-with (lib/dream/cors.sx) in minutes; just ask.

sx_content format — what to emit

SX element markup, rendered host-side by render-pagerender-to-html, per block, guarded (host/blog-render in lib/host/blog.sx). So:

  • Top level is a fragment: (<> (h2 "Title") (p "para " (strong "bold")) (ul (li "a") (li "b"))).
  • Use standard tags render-to-html knows: p h1..h6 ul ol li blockquote code pre strong em a img figure hr br span div. These render cleanly + fast.
  • AVOID the legacy ~kg-* card components — they show as (unsupported block) placeholders (the legacy editor emits bare ~kg-md but the components are ~kg_cards/kg-md — name drift we deliberately did NOT alias). If cards are wanted, define canonical card components the host loads (no bare-name shim).
  • A bad/unknown block degrades to a placeholder, never crashes the page — but aim to emit only renderable markup.

Build notes

  • It's a defisland served as a defpage on --http. Example island: sx/sx/home/stepper.sx. Reactive primitives: signal/deref/computed/ effect (see the signals spec).
  • SX island authoring gotchas (CLAUDE.md "SX Island Authoring Rules"): multi-expr bodies need (do …); let is parallel (nest for sequencing); reactive text needs (deref (computed …)); effects go in an inner let.
  • A reasonable MVP: title input (signal) + an ordered list of block signals (type + text), add/remove/reorder, a few block types (paragraph, heading, list, quote, code), a live preview (computed → rendered), and a Publish that serialises blocks → sx_content and form-POSTs to the host's /new.
  • Test with sx_playwright (inspect / hydrate / interact / trace-boot) — hydrate the island, simulate typing, assert the serialized sx_content and the live preview. Don't ship an island you haven't hydrated in a browser.

Pointers

  • Host ingest + render + page shell: lib/host/blog.sx (the /new POST is the target; host/blog-render shows exactly which markup renders).
  • render-page (host's component renderer) + the static-page pattern: lib/host/page.sx, plans/host-on-sx.md Phase 5.
  • Island example: sx/sx/home/stepper.sx. HTML renderer (tags it knows): web/adapter-html.sx. Legacy editor (reference only, being replaced): shared/static/scripts/sx-editor.js.