Add shared/sx/html_to_sx.py (HTMLParser-based HTML→sx converter) and
update lexical_to_sx.py so HTML cards, markdown cards, and captions all
produce native sx expressions instead of opaque HTML strings.
- ~kg-html now wraps native sx children (editor can identify the block)
- New ~kg-md component for markdown card blocks
- Captions are sx expressions, not escaped HTML strings
- kg_cards.sx: replace (raw! caption) with direct caption rendering
- sx-editor.js: htmlToSx() via DOMParser, serializeInline for captions,
_childrenSx for ~kg-html/~kg-md, new kg-md edit UI
- Migration script (blog/scripts/migrate_sx_html.py) to re-convert
stored sx_content from lexical source
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix _markdown() in lexical_to_sx.py: render markdown to HTML with
mistune.html() before storing in ~kg-html
- Add shared/sx/prettify.py: sx_to_pretty_sx and json_to_pretty_sx
produce sx AST for syntax-highlighted DOM (uses canonical serialize())
- Add preview tab to admin header nav
- Add GET /preview/ route with 4 views: prettified sx, prettified
lexical JSON, sx rendered HTML, lexical rendered HTML
- Add ~blog-preview-panel and ~blog-preview-section components
- Add syntax highlight CSS for sx/JSON tokens
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ghost's extended-text node can appear both as a block (with children) and
inline (with text field). When used as a child of a link node, it has a
text field and should produce a text literal, not a (p ...) wrapper.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pure s-expression block editor replacing React/Koenig: single hover + button,
slash commands, full card edit modes (image/gallery/video/audio/file/embed/
bookmark/callout/toggle/button/HTML/code), inline format toolbar, keyboard
shortcuts, drag-drop uploads, oEmbed/bookmark metadata fetching.
Includes lexical_to_sx converter for backfilling existing posts, KG card
components matching Ghost's card CSS, migration for sx_content column, and
31 converter tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>