render-dom-lake: mark reused lakes to prevent SPA nav conflicts

After reusing an SSR lake element, set data-sx-lake-claimed attribute.
Subsequent dom-query uses :not([data-sx-lake-claimed]) to skip already-
reused elements, preventing SPA navigation from picking up stale lakes
from previous pages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-02 15:30:50 +00:00
parent 547d271571
commit bea8779aea
11 changed files with 45 additions and 20 deletions

View File

@@ -920,7 +920,7 @@
(dict "i" 0 "skip" false)
args)
(let
((existing (when (and (client?) lake-id) (dom-query (str "[data-sx-lake=\"" lake-id "\"]")))))
((existing (when (and (client?) lake-id) (let ((el (dom-query (str "[data-sx-lake=\"" lake-id "\"]:not([data-sx-lake-claimed])")))) (when el (dom-set-attr el "data-sx-lake-claimed" "1")) el))))
(if
existing
existing

File diff suppressed because one or more lines are too long

View File

@@ -501,7 +501,10 @@
"#sx-content")))
(if
(try-client-route (url-pathname href) target-sel)
(do (browser-push-state nil "" href) (browser-scroll-to 0 0))
(do
(save-scroll-position)
(browser-push-state nil "" href)
(browser-scroll-to 0 0))
(do
(log-info (str "sx:route server fetch " href))
(dom-set-attr link "sx-get" href)

File diff suppressed because one or more lines are too long

View File

@@ -433,7 +433,15 @@
(run-post-render-hooks)
(flush-collected-styles)
(set-timeout (fn () (process-elements nil)) 0)
(dom-listen (dom-window) "popstate" (fn (e) (handle-popstate 0)))
(dom-listen
(dom-window)
"popstate"
(fn
(e)
(let
((state (host-get e "state"))
(scrollY (if state (or (dict-get state "scrollY") 0) 0)))
(handle-popstate scrollY))))
(dom-set-attr
(host-get (dom-document) "documentElement")
"data-sx-ready"

File diff suppressed because one or more lines are too long

View File

@@ -719,7 +719,9 @@
hdr-replace
(browser-replace-state hdr-replace)
(and push-url (not (= push-url "false")))
(browser-push-state (if (= push-url "true") url push-url))
(do
(save-scroll-position)
(browser-push-state (if (= push-url "true") url push-url)))
(and replace-url (not (= replace-url "false")))
(browser-replace-state (if (= replace-url "true") url replace-url))))))

File diff suppressed because one or more lines are too long

View File

@@ -527,6 +527,7 @@
(if
client-routed
(do
(save-scroll-position)
(browser-push-state (get live-info "url"))
(browser-scroll-to 0 0))
(do
@@ -1553,11 +1554,23 @@
(dom-dispatch el event-name detail))))))))
els))))
(define
save-scroll-position
:effects (io)
(fn
()
(let
((scrollY (host-get (dom-window) "scrollY")))
(browser-replace-state
(dict "scrollY" scrollY)
""
(browser-location-href)))))
(define
handle-popstate
:effects (mutation io)
(fn
((scrollY :as number))
(scrollY)
(let
((url (browser-location-href))
(boost-el (dom-query "[sx-boost]"))
@@ -1566,9 +1579,8 @@
boost-el
(let
((attr (dom-get-attr boost-el "sx-boost")))
(if (and attr (not (= attr "true"))) attr nil))
nil))
(target-sel (or target-sel "#main-panel"))
(if (and attr (not (= attr "true"))) attr "#sx-content"))
"#sx-content"))
(target (dom-query target-sel))
(pathname (url-pathname url)))
(when
@@ -1577,7 +1589,7 @@
(try-client-route pathname target-sel)
(browser-scroll-to 0 scrollY)
(let
((headers (build-request-headers target (loaded-component-names) _css-hash)))
((headers (build-request-headers target "GET" url)))
(fetch-and-restore target url headers scrollY)))))))
(define

File diff suppressed because one or more lines are too long

View File

@@ -920,7 +920,7 @@
(dict "i" 0 "skip" false)
args)
(let
((existing (when (and (client?) lake-id) (dom-query (str "[data-sx-lake=\"" lake-id "\"]")))))
((existing (when (and (client?) lake-id) (let ((el (dom-query (str "[data-sx-lake=\"" lake-id "\"]:not([data-sx-lake-claimed])")))) (when el (dom-set-attr el "data-sx-lake-claimed" "1")) el))))
(if
existing
existing