Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 17s
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>
4.1 KiB
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
--httpisland pipeline (sx.rose-ash.com, which already SSRs + hydrates islands), NOT in thehttp-listenhost (the host deliberately doesn't do island hydration — seeplans/host-on-sx.mdPhase 5). - It publishes to the host: the host serves
blog.rose-ash.comand 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-submitinlib/host/blog.sx; routehost/blog-open-create-routes(currently UNGUARDED experimental — gate before real use).- A form POST (303 redirect) needs no CORS. If the editor uses
fetchinstead, the host needs CORS on/new— the host loop can adddream-cors-with(lib/dream/cors.sx) in minutes; just ask.
sx_content format — what to emit
SX element markup, rendered host-side by render-page → render-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-htmlknows: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-mdbut 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
defislandserved as adefpageon--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 …);letis parallel (nest for sequencing); reactive text needs(deref (computed …)); effects go in an innerlet. - 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_contentand form-POSTs to the host's/new. - Test with
sx_playwright(inspect / hydrate / interact / trace-boot) — hydrate the island, simulate typing, assert the serializedsx_contentand 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/newPOST is the target;host/blog-rendershows exactly which markup renders). render-page(host's component renderer) + the static-page pattern:lib/host/page.sx,plans/host-on-sx.mdPhase 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.