web/console-render.sx: render-to-console walks a live DOM element tree through the engine's own dom-* accessors and prints it as terminal text — the results <ul> becomes a bulleted list, the filter <input> a text field, the load-more sentinel a "…" line, an .sx-error element a flagged line. It's the console platform's draw step: the browser PAINTS the engine's tree, the harness ASSERTS it, this PRINTS it — one tree, three bindings, the proof the engine is a general runtime not a browser library. Wired into the picker's SX engine tests (web/tests/test-relate-picker.sx): the load and error tests now ALSO assert their console rendering — the same tree the engine built drives both the DOM assertion and the terminal output, so Phase 1's suite is the console renderer's regression suite for free. Plus a relate-picker:console suite for the field/bullet/sentinel/error shapes. 7/7 green, no web-suite regressions. (Class membership reads the live classList via dom-has-class?, not the static class attribute — the engine adds .sx-error through classList.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
95 lines
3.9 KiB
Plaintext
95 lines
3.9 KiB
Plaintext
;; web/console-render.sx — render a live DOM element tree to terminal text.
|
|
;;
|
|
;; The SX hypermedia engine builds the SAME element tree whatever the platform: a
|
|
;; browser PAINTS that tree, the test harness ASSERTS it, and this PRINTS it. Three
|
|
;; bindings of one tree — the proof that the engine is a general runtime, not a
|
|
;; browser library (Phase 3 of plans/sx-native-engine-tests.md).
|
|
;;
|
|
;; It reads the tree through the engine's own dom-* accessors, so it renders
|
|
;; whatever the engine produced — e.g. the ~relate-picker after a load/filter/paging
|
|
;; swap: the results <ul> becomes a bulleted list, the filter <input> a text field,
|
|
;; the load-more sentinel a "…" line, and an .sx-error element a flagged line. The
|
|
;; same render-to-console is the console platform's draw step; the picker's SX engine
|
|
;; tests (web/tests/test-relate-picker.sx) double as its regression suite — they
|
|
;; already drive the tree, this just prints it.
|
|
|
|
;; class membership via the live classList (dom-has-class?), NOT the parsed `class`
|
|
;; attribute — the engine adds .sx-error through classList, which leaves the static
|
|
;; attribute untouched, so an attribute read would miss it.
|
|
(define cr/has-class?
|
|
(fn (el cls) (dom-has-class? el cls)))
|
|
|
|
;; The visible text of an element: its own textContent when it has no element
|
|
;; children, else the concatenation of its children's text (the mock parser hangs
|
|
;; leaf text on textContent, so this covers both row buttons and the sentinel).
|
|
(define cr/text-of
|
|
(fn (el)
|
|
(if (nil? el)
|
|
""
|
|
(let ((kids (dom-child-list el)))
|
|
(if (empty? kids)
|
|
(or (host-get el "textContent") "")
|
|
(let ((parts (map cr/text-of kids)))
|
|
(let ((joined (join "" parts)))
|
|
(if (= joined "")
|
|
(or (host-get el "textContent") "")
|
|
joined))))))))
|
|
|
|
;; A <ul>/<ol> renders one line per <li>: a bullet for a candidate row, an ellipsis
|
|
;; for the load-more sentinel (.rp-more).
|
|
(define cr/list-lines
|
|
(fn (el)
|
|
(map
|
|
(fn (li)
|
|
(if (cr/has-class? li "rp-more")
|
|
(str " … " (cr/text-of li))
|
|
(str " • " (cr/text-of li))))
|
|
(dom-child-list el))))
|
|
|
|
;; An <input> renders as a labelled text field; hidden inputs aren't drawn.
|
|
(define cr/input-line
|
|
(fn (el)
|
|
(let ((type (or (dom-get-attr el "type") "text")))
|
|
(if (= type "hidden")
|
|
""
|
|
(let ((label (or (dom-get-attr el "placeholder") (dom-get-attr el "name") ""))
|
|
(val (or (host-get el "value") "")))
|
|
(str label ": [" (if (= val "") " " val) "]"))))))
|
|
|
|
;; Flatten the rendered lines of an element's children, dropping blanks.
|
|
(define cr/children-lines
|
|
(fn (el)
|
|
(let ((out (list)))
|
|
(for-each
|
|
(fn (kid)
|
|
(for-each
|
|
(fn (ln) (when (not (= ln "")) (append! out ln)))
|
|
(cr/lines kid)))
|
|
(dom-child-list el))
|
|
out)))
|
|
|
|
;; Render one element to a list of text lines, tag-driven. An .sx-error element is
|
|
;; flagged with a leading status line (the terminal's red line).
|
|
(define cr/lines
|
|
(fn (el)
|
|
(if (nil? el)
|
|
(list)
|
|
(let ((tag (lower (dom-tag-name el)))
|
|
(err (cr/has-class? el "sx-error")))
|
|
(let
|
|
((body
|
|
(cond
|
|
(or (= tag "ul") (= tag "ol")) (cr/list-lines el)
|
|
(= tag "input") (let ((l (cr/input-line el))) (if (= l "") (list) (list l)))
|
|
(= tag "button") (list (str "[ " (cr/text-of el) " ]"))
|
|
(or (= tag "h1") (= tag "h2") (= tag "h3")) (list (upper (cr/text-of el)))
|
|
:else (cr/children-lines el))))
|
|
(if err
|
|
(cons "✖ connection problem — retrying…" body)
|
|
body))))))
|
|
|
|
;; render-to-console — the DOM-tree → terminal-text mode, alongside render-to-html /
|
|
;; render-to-dom. Returns the rendered tree as a single newline-joined string.
|
|
(define render-to-console
|
|
(fn (el) (join "\n" (cr/lines el))))
|