v0.6.0 — resolve all known limitations + SSR/wasm DX#2
Merged
Conversation
…nc deps Spans three threads of work from this session: WASM delivery + SSR DX - serveStaticAsset: prefer pre-compressed .br/.gz siblings, else gzip on the fly; gzip the SSR HTML; Vary + Cache-Control:no-cache. ~11.7MB→3.0MB on the fullstack app.wasm. - gutter build/deploy precompress dist (BestCompression); nginx gzip_static. - SSR doc template carries the CSR margin reset (fixes stray body padding). Branded starter template - gutter new embeds gutter.ico → public/favicon.ico, shows the logo, defaults to themes.Meta, adds Runway Club footer/caption + a slogan, and a Head with favicon/font links so SSR matches CSR. Known-limitations Batch A (each with tests; host + browser green) - a11y: Body/Caption render <p> (Inline opt-out); Scaffold <main>/<footer> landmarks; overlays get role=dialog/aria-modal/aria-hidden. - SSR <head> hints: gutter.Head widget + RenderDocument collect title/meta/og; ServeSSR injects them. - AsyncBuilder Deps []any re-runs Load on change (DidUpdateWidget/DeepEqual). - Router guards/redirects: NavGuard on NewRouter/Guard, applied to every nav. - Tag-stability: canUpdate compares Host.Tag, remounting on tag change. - gutter new --ssr scaffolds SSR + typed RPC + hydration. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
gutter.Portal{Child} leaves a zero-size <template> anchor in place and mounts
its child into #gutter-portal-root, so position:fixed overlays escape an
ancestor's transform/overflow/stacking context. portalElement (wasm) handles
mount/hydrate/update/unmount; SSR renders only the placeholder. Popup, Drawer,
and BottomSheet now render through Portal.
Browser test: TestPortalTeleportsChild (mount/update/unmount).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- ItemExtent func(i) float64 for variable row sizes, backed by a prefix-sum offset cache + binary-search indexAt (listMetrics); ItemHeight stays the uniform fast path. - Direction ListHorizontal virtualizes along X (scrollLeft/clientWidth, width slots, row inner), bounded by Width; vertical unchanged. - attachScrollListener takes a horizontal flag and reports axis offset/extent. Tests: list_metrics_test.go (host: metrics + virtualWindow) and list_wasm_test.go (browser: vertical window, variable extents, horizontal). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ThemeProvider{Theme, Child} rides the same inherited-scope machinery as
Provider, so a subtree can override the theme (correct under isolated SetState
rebuilds and SSR). activeTheme now prefers a ThemeProvider over
BuildContext.Theme, then falls back to Apple.
Documents that Provider's immutable Value needs no separate dependent-tracking
invalidation — reactive cross-tree state is Provider[*Notifier[T]] +
ObserverBuilder by design.
Tests: theme_provider_test.go (override + nested shadowing via RenderToHTML).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A State implementing gutter.SSRResolver resolves its async Load synchronously during the SSR walk (called before InitState, which then skips re-loading), so server-rendered HTML shows the resolved UI instead of the Pending placeholder. AsyncBuilder implements it. RenderDocumentCtx threads a context (SSRHandler passes the request's) so loads honor deadlines/cancellation. Documents the hydration-refetch caveat and that HTTP streaming (chunked flush) is still not implemented. Tests: AsyncBuilder resolved/error during RenderToHTML + context propagation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
gutter.Transition(fn) marks the SetStates made inside fn as low-priority: they drain on a macrotask (setTimeout 0) via a separate transition queue, after the urgent microtask flush, so input stays responsive while a large transition rebuild runs. Urgent path unchanged; rebuild() idempotency covers the rare both-queues element. Host stub runs fn directly. Browser tests: transition defers behind urgent; urgent flushes while a transition is pending (flushTransitions added as the deterministic test seam). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
On a hydration tag mismatch, recreate only the offending element with the correct tag and MOVE the server-rendered children into it (then hydrate them), so descendant DOM identity survives instead of rebuilding the whole subtree — relevant now that async SSR can render a different (resolved) tag than the client's initial (pending) one. warnHydrationMismatch logs the divergence. Browser test: TestHydrateTagMismatchSalvagesChildren (descendant node identity preserved across a parent tag swap). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
gutter.Inspect() walks every mounted root into a plain InspectNode tree (Kind/Type/Tag/Key/Children, with String()+Count()) — safe to log or assert on. gutter.EnableDevtools() installs a Ctrl+Shift+G overlay rendering it. MountInto registers roots; host stubs return empty. Tests: inspect_test.go (host: String/Count) and inspect_wasm_test.go (browser: Inspect walks a mounted tree; EnableDevtools toggles the panel). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Resolves every entry in the Known limitations section of CLAUDE.md, plus SSR delivery + starter-template DX. Each feature landed with tests across all three layers (host unit, browser via wasmbrowsertest, Playwright e2e) and per-feature commits.
SSR delivery + DX
serveStaticAssetprefers pre-compressed.br/.gzsiblings, else gzips on the fly;gutter build/deploywrite max-level.gz; nginxgzip_static.app.wasm~11.7MB → ~3.0MB.gutter newstarter: embedded favicon, logo,themes.Metadefault, Runway Club footer + slogan,Headfor SSR;gutter new --ssrscaffolds SSR + typed RPC + hydration.Known limitations — now resolved
Body/Caption→<p>(Inlineopt-out);Scaffold<main>/<footer>landmarks; overlaysrole=dialog/aria-modal.<head>hints:gutter.Head+RenderDocumentcollect title/meta/og.Deps: re-runsLoadon dependency change.NavGuardon every navigation.canUpdatecomparesHost.Tag, remounting on change.gutter.Portal→ body-level root; overlays routed through it.ItemExtent) + horizontal virtualization.gutter.ThemeProviderper-subtree theme.SSRResolver/RenderDocumentCtxresolveLoadserver-side.gutter.Transition(fn)low-priority SetState.gutter.Inspect()/EnableDevtools()element-tree inspector.Fine-grained Provider invalidation is intentionally served by
Provider[*Notifier[T]]+ObserverBuilder(documented) rather than a redundant dependent-tracker.Verification
Host
go test, browserGOOS=js go test, Playwright e2e (7/7),go vetboth targets — all green. Examples + e2e testapp compile.🤖 Generated with Claude Code