# 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 `//`. - `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-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-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`.