Build tooling: updated OCaml bootstrapper, compile-modules, bundle.sh, sx-build-all. WASM browser: rebuilt sx_browser.bc.js/wasm, sx-platform-2.js, .sxbc bytecode files. CSSX/Tailwind: reworked cssx.sx templates and tw-layout, added tw-type support. Content: refreshed essays, plans, geography, reactive islands, docs, demos, handlers. New tools: bisect_sxbc.sh, test-spa.js, render-trace.sx, morph playwright spec. Tests: added test-match.sx, test-examples.sx, updated test-tw.sx and web tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
129 lines
5.9 KiB
Plaintext
129 lines
5.9 KiB
Plaintext
;; Interactive demos for sx-tree tools.
|
|
;; Each demo runs client-side using the same SX functions the MCP server uses.
|
|
|
|
(defisland ~sx-tools/tool-playground ()
|
|
(let ((source (signal "(defcomp ~card (&key title subtitle &rest children)\n (div :class \"card\"\n (h2 title)\n (when subtitle (p :class \"sub\" subtitle))\n children))"))
|
|
(tool (signal "summarise"))
|
|
(output (signal ""))
|
|
(parsed (signal nil)))
|
|
|
|
(letrec
|
|
((do-parse (fn ()
|
|
(reset! parsed (sx-parse (deref source)))))
|
|
|
|
(fmt-path (fn (path)
|
|
(str "[" (join "," (map str path)) "]")))
|
|
|
|
(node-preview (fn (node depth)
|
|
(cond
|
|
(nil? node) "nil"
|
|
(string? node) (str "\"" (if (> (len node) 30) (str (substring node 0 30) "...") node) "\"")
|
|
(number? node) (str node)
|
|
(= (type-of node) "boolean") (if node "true" "false")
|
|
(= (type-of node) "symbol") (symbol-name node)
|
|
(= (type-of node) "keyword") (str ":" (keyword-name node))
|
|
(list? node) (if (empty? node) "()"
|
|
(str "(" (node-preview (first node) 0)
|
|
(if (> (len node) 1) (str " ... " (len node) " children") "") ")"))
|
|
:else (str node))))
|
|
|
|
(summarise-node (fn (node path depth max-depth)
|
|
(if (not (list? node))
|
|
(str (fmt-path path) " " (node-preview node 0) "\n")
|
|
(if (or (empty? node) (>= depth max-depth))
|
|
(str (fmt-path path) " " (node-preview node 0) "\n")
|
|
(str (fmt-path path) " (" (node-preview (first node) 0) " ... " (len node) " children)\n"
|
|
(join "" (map-indexed (fn (i child)
|
|
(summarise-node child (concat path (list (+ i 1))) (+ depth 1) max-depth))
|
|
(rest node))))))))
|
|
|
|
(annotate-node (fn (node path depth)
|
|
(if (not (list? node))
|
|
(str (fmt-path path) " " (node-preview node 0) "\n")
|
|
(if (empty? node) (str (fmt-path path) " ()\n")
|
|
(str (fmt-path path) " (" (node-preview (first node) 0)
|
|
(if (and (<= (len node) 3) (not (some list? (rest node))))
|
|
(str " " (join " " (map (fn (c) (node-preview c 0)) (rest node))) ")\n")
|
|
(str "\n" (join "" (map-indexed (fn (i child)
|
|
(annotate-node child (concat path (list (+ i 1))) (+ depth 1)))
|
|
(rest node))) ")\n")))))))
|
|
|
|
(find-pattern (fn (tree pattern path results)
|
|
(when (list? tree)
|
|
(when (and (not (empty? tree))
|
|
(= (type-of (first tree)) "symbol")
|
|
(contains? (symbol-name (first tree)) pattern))
|
|
(append! results (str (fmt-path path) " " (node-preview tree 0))))
|
|
(for-each (fn (i)
|
|
(let ((child (nth tree i)))
|
|
(when (list? child)
|
|
(find-pattern child pattern (concat path (list i)) results))))
|
|
(range 0 (len tree))))))
|
|
|
|
(get-context (fn (tree path)
|
|
(let ((result (list)))
|
|
(for-each (fn (depth)
|
|
(let ((prefix (slice path 0 (+ depth 1)))
|
|
(node (reduce (fn (cur idx) (if (and (list? cur) (< idx (len cur))) (nth cur idx) nil)) tree prefix)))
|
|
(when node
|
|
(append! result (str (if (= depth (- (len path) 1)) "→ " " ")
|
|
(fmt-path prefix) " " (node-preview node 0))))))
|
|
(range 0 (len path)))
|
|
result)))
|
|
|
|
(run-tool (fn ()
|
|
(do-parse)
|
|
(let ((tree (deref parsed))
|
|
(t (deref tool)))
|
|
(when tree
|
|
(reset! output
|
|
(cond
|
|
(= t "summarise")
|
|
(join "" (map-indexed (fn (i expr)
|
|
(summarise-node expr (list i) 0 2))
|
|
tree))
|
|
(= t "read-tree")
|
|
(join "" (map-indexed (fn (i expr)
|
|
(annotate-node expr (list i) 0))
|
|
tree))
|
|
(= t "find-all")
|
|
(let ((results (list)))
|
|
(for-each (fn (i) (find-pattern (nth tree i) "def" (list i) results))
|
|
(range 0 (len tree)))
|
|
(if (empty? results) "No matches for \"def\""
|
|
(join "\n" results)))
|
|
(= t "validate")
|
|
(if (and tree (not (empty? tree))) "OK — structurally valid" "Parse error")
|
|
(= t "get-context")
|
|
(let ((ctx (get-context (first tree) (list 2))))
|
|
(if (empty? ctx) "Path (0 2) not found"
|
|
(join "\n" ctx)))
|
|
(= t "serialize")
|
|
(join "\n\n" (map sx-serialize tree))
|
|
(= t "hypersx")
|
|
(sx->hypersx tree)
|
|
:else "Select a tool")))))))
|
|
|
|
(run-tool)
|
|
|
|
(div (~tw :tokens "space-y-3")
|
|
(div (~tw :tokens "space-y-2")
|
|
(label (~tw :tokens "text-sm font-medium text-stone-700") "Source")
|
|
(textarea (~tw :tokens "w-full font-mono text-xs bg-stone-50 border border-stone-300 rounded p-2")
|
|
:rows 6 :bind source))
|
|
(div (~tw :tokens "flex gap-2 flex-wrap items-center")
|
|
(for-each (fn (t)
|
|
(button
|
|
:class (str "px-3 py-1 text-xs rounded border transition "
|
|
(if (= (deref tool) t)
|
|
"bg-violet-600 text-white border-violet-600"
|
|
"bg-white text-stone-600 border-stone-300 hover:border-violet-400"))
|
|
:on-click (fn (e) (reset! tool t) (run-tool))
|
|
t))
|
|
(list "summarise" "read-tree" "find-all" "validate" "get-context" "serialize" "hypersx"))
|
|
(button (~tw :tokens "px-3 py-1 text-xs rounded bg-stone-700 text-white")
|
|
:on-click (fn (e) (run-tool))
|
|
"Run"))
|
|
(pre (~tw :tokens "font-mono text-xs text-stone-700 bg-stone-50 border border-stone-200 rounded p-3 whitespace-pre-wrap overflow-x-auto max-h-64 overflow-y-auto")
|
|
(deref output))))))
|