(defisland
  (&key initial-expr)
  (let
    ((source (signal (or initial-expr "(div (~cssx/tw :tokens \"p-6 rounded-lg border border-stone-200 bg-white text-center\")\n  (h1 (~cssx/tw :tokens \"text-3xl font-bold mb-2\")\n    (span (~cssx/tw :tokens \"text-rose-500\") \"the \")\n    (span (~cssx/tw :tokens \"text-amber-500\") \"joy \")\n    (span (~cssx/tw :tokens \"text-emerald-500\") \"of \")\n    (span (~cssx/tw :tokens \"text-violet-600 text-4xl\") \"sx\")))")))
      (steps (signal (list)))
      (step-idx (signal 0))
      (parsed-ok (signal false))
      (error-msg (signal nil))
      (dom-stack-sig (signal (list))))
    (letrec
      ((split-tag (fn (expr result) (cond (not (list? expr)) (append! result {:expr expr :type "leaf"}) (empty? expr) nil (not (= (type-of (first expr)) "symbol")) (append! result {:expr expr :type "leaf"}) (is-html-tag? (symbol-name (first expr))) (let ((ctag (symbol-name (first expr))) (cargs (rest expr)) (cch (list)) (cat (list)) (spreads (list)) (ckw false)) (for-each (fn (a) (cond (= (type-of a) "keyword") (do (set! ckw true) (append! cat a)) ckw (do (set! ckw false) (append! cat a)) (and (list? a) (not (empty? a)) (= (type-of (first a)) "symbol") (starts-with? (symbol-name (first a)) "~")) (do (set! ckw false) (append! spreads a)) :else (do (set! ckw false) (append! cch a)))) cargs) (append! result {:spreads spreads :tag ctag :type "open" :attrs cat}) (for-each (fn (c) (split-tag c result)) cch) (append! result {:tag ctag :type "close"})) :else (append! result {:expr expr :type "expr"}))))
        (get-preview (fn () (dom-query "[data-sx-lake=\"preview\"]")))
        (get-stack (fn () (deref dom-stack-sig)))
        (set-stack (fn (v) (reset! dom-stack-sig v)))
        (push-stack
          (fn
            (el)
            (reset! dom-stack-sig (append (deref dom-stack-sig) (list el)))))
        (pop-stack
          (fn
            ()
            (let
              ((s (deref dom-stack-sig)))
              (when
                (> (len s) 1)
                (reset! dom-stack-sig (slice s 0 (- (len s) 1)))))))
        (do-parse
          (fn
            ()
            (reset! error-msg nil)
            (reset! step-idx 0)
            (reset! parsed-ok false)
            (set-stack (list))
            (let
              ((container (get-preview)))
              (when container (dom-set-prop container "innerHTML" "")))
            (let
              ((parsed (sx-parse (deref source))))
              (if
                (empty? parsed)
                (do (reset! error-msg "Parse error") (reset! steps (list)))
                (let
                  ((result (list)))
                  (split-tag (first parsed) result)
                  (reset! steps result)
                  (reset! parsed-ok true)
                  (set-stack (list (get-preview))))))))
        (do-step
          (fn
            ()
            (when
              (and
                (deref parsed-ok)
                (< (deref step-idx) (len (deref steps))))
              (let
                ((step (nth (deref steps) (deref step-idx)))
                  (step-type (get step "type"))
                  (stack (get-stack))
                  (parent
                    (if
                      (empty? (get-stack))
                      (get-preview)
                      (last (get-stack)))))
                (cond
                  (= step-type "open")
                  (let
                    ((el (dom-create-element (get step "tag") nil))
                      (attrs (get step "attrs"))
                      (spreads (or (get step "spreads") (list))))
                    (let
                      loop
                      ((i 0))
                      (when
                        (< i (len attrs))
                        (dom-set-attr
                          el
                          (keyword-name (nth attrs i))
                          (nth attrs (+ i 1)))
                        (loop (+ i 2))))
                    (for-each
                      (fn
                        (sp)
                        (let
                          ((result (eval-expr sp (make-env))))
                          (when
                            (and result (spread? result))
                            (let
                              ((sattrs (spread-attrs result)))
                              (for-each
                                (fn
                                  (k)
                                  (if
                                    (= k "class")
                                    (dom-set-attr
                                      el
                                      "class"
                                      (str
                                        (or (dom-get-attr el "class") "")
                                        " "
                                        (get sattrs k)))
                                    (dom-set-attr el k (get sattrs k))))
                                (keys sattrs))))))
                      spreads)
                    (when parent (dom-append parent el))
                    (push-stack el))
                  (= step-type "close")
                  (pop-stack)
                  (= step-type "leaf")
                  (when
                    parent
                    (let
                      ((val (get step "expr")))
                      (dom-append
                        parent
                        (create-text-node (if (string? val) val (str val))))))
                  (= step-type "expr")
                  (let
                    ((rendered (render-to-dom (get step "expr") (make-env) nil)))
                    (when (and parent rendered) (dom-append parent rendered)))))
              (swap! step-idx inc))))
        (do-run
          (fn
            ()
            (let
              loop
              ()
              (when
                (< (deref step-idx) (len (deref steps)))
                (do-step)
                (loop)))))
        (do-back
          (fn
            ()
            (when
              (and (deref parsed-ok) (> (deref step-idx) 0))
              (let
                ((target (- (deref step-idx) 1))
                  (container (get-preview)))
                (when container (dom-set-prop container "innerHTML" ""))
                (set-stack (list (get-preview)))
                (reset! step-idx 0)
                (for-each
                  (fn (_) (do-step))
                  (slice (deref steps) 0 target)))))))
      (div
        (~tw :tokens "space-y-4")
        (div
          (label
            (~tw :tokens "text-xs text-stone-400 block mb-1")
            "Component expression")
          (textarea
            :bind source
            :rows 4
            (~tw :tokens "w-full px-3 py-2 rounded border border-stone-300 font-mono text-xs focus:outline-none focus:border-violet-400")))
        (div
          (~tw :tokens "flex gap-1")
          (button
            :on-click (fn (e) (do-parse))
            (~tw :tokens "px-3 py-1.5 rounded bg-stone-700 text-white text-sm hover:bg-stone-800")
            "Parse")
          (button
            :on-click (fn (e) (do-back))
            :class (str
              "px-3 py-1.5 rounded text-sm "
              (if
                (and (deref parsed-ok) (> (deref step-idx) 0))
                "bg-stone-200 text-stone-700 hover:bg-stone-300"
                "bg-stone-100 text-stone-300 cursor-not-allowed"))
            "◀")
          (button
            :on-click (fn (e) (do-step))
            :class (str
              "px-3 py-1.5 rounded text-sm "
              (if
                (and
                  (deref parsed-ok)
                  (< (deref step-idx) (len (deref steps))))
                "bg-violet-500 text-white hover:bg-violet-600"
                "bg-violet-200 text-violet-400 cursor-not-allowed"))
            "Step ▶")
          (button
            :on-click (fn (e) (do-run))
            :class (str
              "px-3 py-1.5 rounded text-sm "
              (if
                (deref parsed-ok)
                "bg-violet-700 text-white hover:bg-violet-800"
                "bg-violet-200 text-violet-400 cursor-not-allowed"))
            "Run ▶▶"))
        (when
          (deref error-msg)
          (div (~tw :tokens "text-red-600 text-sm") (deref error-msg)))
        (when
          (and (deref parsed-ok) (= (deref step-idx) 0))
          (div
            (~tw :tokens "text-sm text-stone-500 bg-stone-50 rounded p-2")
            (str
              "Parsed "
              (len (deref steps))
              " render steps. Click Step to begin.")))
        (div
          (~tw :tokens "grid grid-cols-1 md:grid-cols-2 gap-4")
          (when
            (deref parsed-ok)
            (div
              (~tw :tokens "rounded border border-stone-200 bg-white p-3 min-h-24")
              (div
                (~tw :tokens "text-xs text-stone-400 mb-2")
                (str
                  (deref step-idx)
                  " / "
                  (len (deref steps))
                  (if
                    (= (deref step-idx) (len (deref steps)))
                    " — complete"
                    "")))
              (div
                (~tw :tokens "space-y-0.5 font-mono text-xs max-h-64 overflow-y-auto")
                (map-indexed
                  (fn
                    (i step)
                    (div
                      :class (str
                        "flex gap-2 px-1 rounded "
                        (cond
                          (= i (deref step-idx))
                          "bg-violet-100 text-violet-700 font-bold"
                          (< i (deref step-idx))
                          "text-stone-400"
                          :else "text-stone-300"))
                      (span
                        (~tw :tokens "w-4 text-right")
                        (if (< i (deref step-idx)) "✓" (str (+ i 1))))
                      (span
                        (~tw :tokens "truncate")
                        (let
                          ((lbl (get step "label")))
                          (if
                            lbl
                            (if
                              (> (len lbl) 60)
                              (str (slice lbl 0 57) "...")
                              lbl)
                            (let
                              ((tp (get step "type")))
                              (cond
                                (= tp "open")
                                (str "<" (get step "tag") ">")
                                (= tp "close")
                                (str "</" (get step "tag") ">")
                                :else (sx-serialize (get step "expr")))))))))
                  (deref steps)))))
          (div
            (~tw :tokens "rounded border border-stone-200 p-3 min-h-24")
            (div (~tw :tokens "text-xs text-stone-400 mb-2") "Live DOM")
            (lake :id "preview")))))))
