content: render boundary (asHTML/asSx polymorphic) + 29 tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 48s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 48s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
78
lib/content/render.sx
Normal file
78
lib/content/render.sx
Normal file
@@ -0,0 +1,78 @@
|
||||
;; content-on-sx — render boundary.
|
||||
;;
|
||||
;; Rendering is a message, not a property switch: every block (and the document)
|
||||
;; answers asHTML and asSx. The internal model carries no presentation — the
|
||||
;; boundary format is chosen by which message you send. The document folds its
|
||||
;; children's renderings, so (asHTML doc) / (asSx doc) are pure polymorphic
|
||||
;; sends with no type dispatch in the SX layer.
|
||||
;;
|
||||
;; NOTE: no HTML escaping yet — text is emitted verbatim. Escaping is a boundary
|
||||
;; concern to add before any untrusted content reaches render.
|
||||
|
||||
(define
|
||||
content-bootstrap-render!
|
||||
(fn
|
||||
()
|
||||
(begin
|
||||
(ct-def-method!
|
||||
"CtHeading"
|
||||
"asHTML"
|
||||
"asHTML | t | t := level printString. ^ '<h' , t , '>' , text , '</h' , t , '>'")
|
||||
(ct-def-method! "CtText" "asHTML" "asHTML ^ '<p>' , text , '</p>'")
|
||||
(ct-def-method!
|
||||
"CtCode"
|
||||
"asHTML"
|
||||
"asHTML ^ '<pre><code class=\"language-' , language , '\">' , text , '</code></pre>'")
|
||||
(ct-def-method!
|
||||
"CtQuote"
|
||||
"asHTML"
|
||||
"asHTML ^ '<blockquote>' , text , '</blockquote>'")
|
||||
(ct-def-method!
|
||||
"CtImage"
|
||||
"asHTML"
|
||||
"asHTML ^ '<img src=\"' , src , '\" alt=\"' , alt , '\">'")
|
||||
(ct-def-method!
|
||||
"CtEmbed"
|
||||
"asHTML"
|
||||
"asHTML ^ '<iframe src=\"' , url , '\"></iframe>'")
|
||||
(ct-def-method! "CtDivider" "asHTML" "asHTML ^ '<hr>'")
|
||||
(ct-def-method!
|
||||
"CtList"
|
||||
"asHTML"
|
||||
"asHTML | tag | tag := ordered ifTrue: ['ol'] ifFalse: ['ul']. ^ '<' , tag , '>' , (items inject: '' into: [:a :x | a , '<li>' , x , '</li>']) , '</' , tag , '>'")
|
||||
(ct-def-method!
|
||||
"CtDoc"
|
||||
"asHTML"
|
||||
"asHTML ^ blocks inject: '' into: [:a :b | a , (b asHTML)]")
|
||||
(ct-def-method!
|
||||
"CtHeading"
|
||||
"asSx"
|
||||
"asSx | t | t := level printString. ^ '(h' , t , ' \"' , text , '\")'")
|
||||
(ct-def-method! "CtText" "asSx" "asSx ^ '(p \"' , text , '\")'")
|
||||
(ct-def-method! "CtCode" "asSx" "asSx ^ '(pre (code \"' , text , '\"))'")
|
||||
(ct-def-method! "CtQuote" "asSx" "asSx ^ '(blockquote \"' , text , '\")'")
|
||||
(ct-def-method!
|
||||
"CtImage"
|
||||
"asSx"
|
||||
"asSx ^ '(img :src \"' , src , '\" :alt \"' , alt , '\")'")
|
||||
(ct-def-method! "CtEmbed" "asSx" "asSx ^ '(iframe :src \"' , url , '\")'")
|
||||
(ct-def-method! "CtDivider" "asSx" "asSx ^ '(hr)'")
|
||||
(ct-def-method!
|
||||
"CtList"
|
||||
"asSx"
|
||||
"asSx | tag | tag := ordered ifTrue: ['ol'] ifFalse: ['ul']. ^ '(' , tag , ' ' , (items inject: '' into: [:a :x | a , '(li \"' , x , '\")']) , ')'")
|
||||
(ct-def-method!
|
||||
"CtDoc"
|
||||
"asSx"
|
||||
"asSx ^ '(article ' , (blocks inject: '' into: [:a :b | a , (b asSx)]) , ')'")
|
||||
true)))
|
||||
|
||||
;; ── SX boundary API — pure message sends ──
|
||||
(define asHTML (fn (node) (str (st-send node "asHTML" (list)))))
|
||||
(define asSx (fn (node) (str (st-send node "asSx" (list)))))
|
||||
|
||||
;; readable aliases
|
||||
(define render-html asHTML)
|
||||
(define render-sx asSx)
|
||||
(define block-html asHTML)
|
||||
(define block-sx asSx)
|
||||
Reference in New Issue
Block a user