Event buffering during hydration gap
Clicks on island elements before hydration completes are captured and replayed after boot finishes: - shell.sx: inline script (capture phase) buffers clicks on [data-sx-island] elements that aren't hydrated yet into window._sxQ - boot.sx: after hydration + process-elements, replays buffered clicks by calling target.click() on elements still connected to the DOM This makes SSR islands feel interactive immediately — the user can click a button while the SX kernel is still loading/hydrating, and the action fires as soon as the handler is wired up. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -492,7 +492,23 @@
|
||||
(process-elements nil)
|
||||
;; Wire up popstate for back/forward navigation
|
||||
(dom-listen (dom-window) "popstate"
|
||||
(fn (e) (handle-popstate 0))))))
|
||||
(fn (e) (handle-popstate 0)))
|
||||
;; Replay buffered clicks from hydration gap
|
||||
(let ((queued (host-get (dom-window) "_sxQ")))
|
||||
(when queued
|
||||
(let ((arr (host-call (host-global "Array") "from" queued)))
|
||||
(let ((n (host-get arr "length")))
|
||||
(when (> n 0)
|
||||
(log-info (str "replaying " n " buffered click(s)"))
|
||||
(let loop ((i 0))
|
||||
(when (< i n)
|
||||
(let ((entry (host-call arr "at" i)))
|
||||
(when entry
|
||||
(let ((target (host-get entry "t")))
|
||||
(when (and target (host-get target "isConnected"))
|
||||
(host-call target "click")))))
|
||||
(loop (+ i 1)))))))
|
||||
(host-set! (dom-window) "_sxQ" nil))))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
@@ -76,6 +76,8 @@ details.group{overflow:hidden}details.group>summary{list-style:none}details.grou
|
||||
(body :class "bg-stone-50 text-stone-900"
|
||||
;; Server-rendered HTML — visible immediately before JS loads
|
||||
(div :id "sx-root" (raw! (or body-html "")))
|
||||
;; Event buffer — captures clicks during hydration gap, replayed after boot
|
||||
(script (raw! "document.addEventListener('click',function(e){var i=e.target.closest('[data-sx-island]');if(i&&!i._sxBoundislandHydrated){e.stopPropagation();(window._sxQ=window._sxQ||[]).push({t:e.target,ts:Date.now()})}},true)"))
|
||||
(script :type "text/sx" :data-components true :data-hash component-hash
|
||||
(raw! (or component-defs "")))
|
||||
(when init-sx
|
||||
|
||||
18
web/boot.sx
18
web/boot.sx
@@ -492,7 +492,23 @@
|
||||
(process-elements nil)
|
||||
;; Wire up popstate for back/forward navigation
|
||||
(dom-listen (dom-window) "popstate"
|
||||
(fn (e) (handle-popstate 0))))))
|
||||
(fn (e) (handle-popstate 0)))
|
||||
;; Replay buffered clicks from hydration gap
|
||||
(let ((queued (host-get (dom-window) "_sxQ")))
|
||||
(when queued
|
||||
(let ((arr (host-call (host-global "Array") "from" queued)))
|
||||
(let ((n (host-get arr "length")))
|
||||
(when (> n 0)
|
||||
(log-info (str "replaying " n " buffered click(s)"))
|
||||
(let loop ((i 0))
|
||||
(when (< i n)
|
||||
(let ((entry (host-call arr "at" i)))
|
||||
(when entry
|
||||
(let ((target (host-get entry "t")))
|
||||
(when (and target (host-get target "isConnected"))
|
||||
(host-call target "click")))))
|
||||
(loop (+ i 1)))))))
|
||||
(host-set! (dom-window) "_sxQ" nil))))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user