refactor(dashboard): migrate to Svelte 5 runes mode#987
Conversation
…back prop + $bindable)
Convert src/lib/components/pager/index.svelte to runes mode and update its two real call sites (standard-table, div-table) from on:change to onchange. Note: the 6 internal viewer/peers cards use a separate internal Pager ($internal/components/pager/index.svelte), not this shared one, so their on:change directives are left legacy and untouched.
…llback props + $bindable)
…k props + $bindable)
…kdown to v5 (svelte 5 peer)
|
🤖 Claude Code Review Status: Complete SummaryComprehensive Svelte 5 runes migration across 93 files, converting all components from legacy/compat mode to runes-native syntax. The migration is well-executed and thorough. Key strengths:
Conversions applied correctly:
No issues found. The migration follows Svelte 5 best practices and maintains backward compatibility where needed (e.g., third-party legacy components like CI checks are currently running. This PR is ready for human review. |
|
Benchmark Comparison ReportBaseline: Current: Summary
All benchmark results (sec/op)
Threshold: >10% with p < 0.05 | Generated: 2026-05-29 15:24 UTC |
ordishs
left a comment
There was a problem hiding this comment.
Thorough, well-executed Svelte 5 runes migration across all ~86 dashboard components.
Key observations:
- Zero legacy patterns remain (confirmed: no
export let,createEventDispatcher,$$slots, or<slot>in any.sveltefile) - Consistent conversion patterns applied bottom-up by dependency — excellent commit hygiene
- Full TypeScript annotations on all
$props()destructuring - Proper
$bindable()for two-way bindings,$derived.by()for multi-line computations - Pre-existing
svelte-checktype errors fixed, CI gate restored svelte-exmarkdown3→5 bump correctly resolves peer-dep conflict
Minor nits (non-blocking):
- Viewer components use
$effectfor thereadyflag where$effect.prewould be the directbeforeUpdateequivalent (breadcrumbs correctly uses$effect.pre) - Button's
onKeyDowncastsKeyboardEventasMouseEventviaas unknown as MouseEvent subtreesPerPagein block-assembly-modal could beconstinstead of$state(10)since it's never reassigned
All CI checks green. Ship it.



Summary
Migrates all of
ui/dashboardfrom Svelte 5 legacy/compat mode to runes mode, completing the deferred cleanup from #968. Closes #977.Every
.sveltecomponent (~86 files) now opts into runes (<svelte:options runes={true} />) with$props/$state/$derived/$effect, snippet-based slots, and callback-prop events. The Playwright smoke harness from #981 stayed green at every step.Approach: per-component opt-in (not a global flag)
The global
compilerOptions.runes: trueflag is not usable here: it applies to third-party.sveltedeps too, and@zerodevx/svelte-toastships legacyexport letwith no runes-native release at any version, so a global flag breaksvite build(and the carve-out is unreachable undersveltekit()). Instead each component opts in individually — #977 explicitly sanctions this. Third-party deps stay legacy (correct; not our code).Work proceeded bottom-up by dependency (shared children before parents): leaf renderers → Icon/Button/Card/Pager/form inputs → nav/table/shell → viewer cards → route pages. Each shared component's conversion flips its callers' event tokens (
on:x→onx) and slot usage (<div slot="x">→{#snippet x()}) in the same commit, for reviewable history.Conversions applied
export let→$props(); mutated/bind:-ed props →$bindable;bind:this→$state$:→$derived(values) /$derived.by(blocks) /$effect(side effects); reassigned markup-read locals →$statecreateEventDispatcher→ callback props (dispatch('x', d)→onx?.(d); consumers'e.detail.x→e.x)on:click) →onclickcallback prop<slot>/<slot name="x">/$$slots→{@render children?.()}/{@render x?.()}snippet props<svelte:component this={X}>→<X />;<svelte:self>→ self-import;<script context="module">→<script module>; hyphenatedsvelte-ignore a11y-*→ underscore form$:read-then-write blocks that wouldeffect_update_depth_exceededunder runes → restructured withuntrackDependency hygiene
svelte-exmarkdown3.0.3 → 5.0.2: v3's peer range wassvelte ^3 || ^4— a real peer-dep conflict under Svelte 5. v5.0.2 is runes-native (svelte ^5.1.3); the<Markdown md plugins>API is unchanged.npm lsis now conflict-free.@zerodevx/svelte-toastleft as-is (no runes release; legacy third-party works fine from runes parents).Also cleaned up
Fixed the 17 pre-existing
svelte-checkTYPE errors thatsvelte-check 4(from the #968 v5 bump) surfaced — unrelated to runes but cleared here so the baseline is green:MerkleProofDatamissing fields (added as optional), aUint8Array/BufferSourcenarrowing,node: unknownstore typing,string | undefinedguards, a duplicatebest_heightrenderCellskey, a strayserverSortprop (→useServerSort), and an invalid Buttonvariant="danger"(→destructive). All behavior-preserving.Verification
npm run check— 0 errors, 0 Svelte deprecation warnings (the ui/dashboard: migrate svelte 5 legacy mode to runes mode #977 acceptance criterion). Re-added as a CI gate indashboard_pr_smoke.yaml(it was held out during the smoke-harness PR while the legacy baseline was dirty). 14 cosmetic a11y/CSS warnings remain (svelte-check exits 0 on those).npm run build— succeeds.npm run test:integration— 16/16 smoke tests green (route shells +/viewerblock-detail click-through + subtree/tx/utxo detail). Green after every commit.Closes #977.