;; Phase 1 — render boundary. asHTML / asSx are polymorphic message sends on ;; blocks and the document. Escaping happens at the boundary. (st-bootstrap-classes!) (content-bootstrap-blocks!) (content-bootstrap-doc!) (content-bootstrap-render!) (define h (mk-heading "h" 2 "Title")) (define p (mk-text "p" "Hello")) (define code (mk-code "c" "sx" "(+ 1 2)")) (define q (mk-quote "q" "Ada" "to err")) (define img (mk-image "i" "/c.png" "cat")) (define em (mk-embed "e" "https://v/1" "vimeo")) (define dv (mk-divider "d")) (define ul (mk-list "u" false (list "a" "b"))) (define ol (mk-list "o" true (list "x" "y"))) ;; ── per-block asHTML ── (content-test "heading html" (asHTML h) "

Title

") (content-test "text html" (asHTML p) "

Hello

") (content-test "code html" (asHTML code) "
(+ 1 2)
") (content-test "quote html" (asHTML q) "
to err
") (content-test "image html" (asHTML img) "\"cat\"") (content-test "embed html" (asHTML em) "") (content-test "divider html" (asHTML dv) "
") (content-test "ul html" (asHTML ul) "") (content-test "ol html" (asHTML ol) "
  1. x
  2. y
") ;; ── per-block asSx ── (content-test "heading sx" (asSx h) "(h2 \"Title\")") (content-test "text sx" (asSx p) "(p \"Hello\")") (content-test "code sx" (asSx code) "(pre (code \"(+ 1 2)\"))") (content-test "quote sx" (asSx q) "(blockquote \"to err\")") (content-test "image sx" (asSx img) "(img :src \"/c.png\" :alt \"cat\")") (content-test "embed sx" (asSx em) "(iframe :src \"https://v/1\")") (content-test "divider sx" (asSx dv) "(hr)") (content-test "ul sx" (asSx ul) "(ul (li \"a\")(li \"b\"))") (content-test "ol sx" (asSx ol) "(ol (li \"x\")(li \"y\"))") ;; ── document folds children (pure message dispatch) ── (define d (doc-append (doc-append (doc-append (doc-empty "doc") h) p) dv)) (content-test "doc html" (asHTML d) "

Title

Hello


") (content-test "doc sx" (asSx d) "(article (h2 \"Title\")(p \"Hello\")(hr))") (content-test "empty doc html" (asHTML (doc-empty "e")) "") (content-test "empty doc sx" (asSx (doc-empty "e")) "(article )") ;; ── render-* / block-* aliases ── (content-test "render-html alias" (render-html d) (asHTML d)) (content-test "render-sx alias" (render-sx d) (asSx d)) (content-test "block-html alias" (block-html h) "

Title

") ;; ── render reflects edits (immutability: each render is of a version) ── (define d2 (doc-update d "p" "text" "Edited")) (content-test "render after update" (asHTML d2) "

Title

Edited


") (content-test "original render unchanged" (asHTML d) "

Title

Hello


") (content-test "render after move" (asHTML (doc-move d "h" 2)) "

Hello


Title

") (content-test "render after delete" (asHTML (doc-delete d "p")) "

Title


") ;; ── HTML escaping at the boundary ── (define xh (mk-heading "xh" 2 "A < B & \"C\"")) (define xp (mk-text "xp" "")) (define xi (mk-image "xi" "/a.png?x=1&y=2" "tag ")) (define xl (mk-list "xl" false (list "a<1" "b&2"))) (content-test "escape heading text" (asHTML xh) "

A < B & "C"

") (content-test "escape paragraph" (asHTML xp) "

<script>alert(1)</script>

") (content-test "escape image attrs" (asHTML xi) "\"tag") (content-test "escape list items" (asHTML xl) "") (content-test "escape ampersand once" (asHTML (mk-text "amp" "a & b")) "

a & b

") (content-test "escape in document" (asHTML (doc-append (doc-empty "e") xp)) "

<script>alert(1)</script>

") (content-test "no over-escape plain" (asHTML (mk-text "plain" "hello world")) "

hello world

") (content-test "escape code body" (asHTML (mk-code "xc" "html" "
&
")) "
<div> & </div>
") ;; ── asSx string-escaping (build expected via q/bs to avoid miscounts) ── (define q1 (str "\"")) (define bs (str "\\")) (content-test "asSx escapes quote" (asSx (mk-text "qt" (str "say " q1 "hi" q1))) (str "(p " q1 "say " bs q1 "hi" bs q1 q1 ")")) (content-test "asSx escapes backslash" (asSx (mk-text "qb" (str "a" bs "b"))) (str "(p " q1 "a" bs bs "b" q1 ")")) (content-test "asSx plain unchanged" (asSx (mk-text "pp" "plain")) "(p \"plain\")") (content-test "asSx escapes image attr" (asSx (mk-image "im" (str "/a" q1) "x")) (str "(img :src " q1 "/a" bs q1 q1 " :alt " q1 "x" q1 ")")) (content-test "asSx escapes list item" (asSx (mk-list "lq" false (list (str "i" q1) "j"))) (str "(ul (li " q1 "i" bs q1 q1 ")(li " q1 "j" q1 "))"))