tech-debt batch 5c: split layoutEngine + lift transforms + registry defaultVisible (#157/#155)#170
Merged
Merged
Conversation
added 2 commits
June 22, 2026 20:23
…transforms
Part 1 — mechanical split of the 982-LOC lib/layoutEngine.ts along its existing
comment-banner seams, behind a re-export barrel so the ~18 call sites keep
importing from @/lib/layoutEngine unchanged. No function body changed.
• lib/layout/placements.ts — types + default-placement generators (band/
statBand/evenBand, generateLg/Scrolling/Xs/Default/Strip, strip-key read/write).
• lib/layout/merge.ts — migration/merge + structural equality
(mergePlacements, placementsEqual + private helpers).
• lib/layout/pagination.ts — page fit + partition (computePageFit,
paginatePlacements, clampToPages, rebaseToLocal, pageCount, findFreeSlot,
MIN_ROW_HEIGHT) + clampPage (moved from cockpit.ts; cockpit re-exports it).
Part 2 — lift the inlined placement transforms out of the components into the
pure layout lib (lib/layout/transforms.ts), each taking the registry-derived data
the component supplies (sizes/mins/ids — never imported, so the module stays pure)
and returning a new Placements:
• from Dashboard.tsx: addToPlacements / removeFromPlacements / addSpacer /
togglePin (component supplies defaultSize / materialized lg / fullDefault).
• from WidgetLayout.tsx: foldSpacerDefaults (spacer defaults), repairStrip
(strip repair), buildPersistBlob (persist-blob build), clampToPagesSafe.
The components now call the pure fns and setPlacements/feed the result. The
isPlaced gating and the persisted layout JSON shape (dashboard.layout AppSetting,
keep-known / append-new / drop-unknown semantics) are unchanged — the existing
layoutEngine.test.ts (70 tests) passes unchanged as the behaviour-preserving
proof. Added hand-calc tests for the lifted transforms (layoutTransforms.test.ts,
21 tests).
The three near-identical band helpers (band/statBand/evenBand) are left
un-unified: they produce genuinely different geometry (ragged-wrap vs
edge-to-edge-fill-with-wideIds vs equal-width-trailing-gap), so a single
layoutBand could not be proven byte-identical against the existing tests.
Add a defaultVisible flag to WidgetDef marking a self-contained chart tile that is shown by default on a fresh layout, removable, and re-addable. Set it on the three interval tiles (load-shape #76, history #121, heatmap #77) — built from one INTERVAL_WIDGET_TABLE instead of three copy-pasted defs — and false on every other widget (charts own visibility via widgetConfig.visible, stats via isVisible, bills panel + spacer via placement presence). Dashboard.tsx now DERIVES intervalWidgetTypes, the chart universe, and the add-back palette by filtering the registry on defaultVisible (defaultVisibleWidgetTypes()) instead of three hardcoded id-lists; rename availableChartsAll -> fullChartUniverse (the old name invited the #121 mistake). The isPlaced gating is unchanged: chartIds still gates the interval tiles on isPlaced, so default-visible / removable / re-addable behavior is byte-identical. Adding a 4th interval widget is now a single registry-table row + a test-count bump, no Dashboard edits. Update the registry HOW-TO-ADD-A-WIDGET comment to the now-true registry-only path A. Add a test asserting defaultVisibleWidgetTypes() is exactly the 3 interval tiles in order and no other kind carries the flag.
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.
Tech-debt batch 5c — final batch (epic #161). Behavior-preserving layout/widget rework, laid out last.
Closes #157, closes #155.
#157 — split
layoutEngine.ts(982 LOC) + lift component transformslib/layout/{placements,merge,pagination}.tsbehind a re-export barrel (layoutEngine.ts) — pure mechanical move, no function body changed; all ~18 importers unchanged.clampPagemoved into pagination (cockpit re-exports).Dashboard.tsx(add/remove/addSpacer/togglePin/buildCurrentLg) andWidgetLayout.tsx(spacer-fold/strip-repair/persist-blob) into pure, hand-calc-testedlib/layout/transforms.ts(§2). Components keep RGL wiring + DOM effects.#155 — make "add a widget" registry-only
WidgetDef.defaultVisibleflag (the 3 interval tiles). Dashboard derivesintervalWidgetTypes/fullChartUniverse(renamed from the pitfall-namedavailableChartsAll)/the palette by filtering the registry instead of hardcoded id lists. The 3 interval defs come from oneINTERVAL_WIDGET_TABLE. Registry "HOW TO ADD A WIDGET" comment updated to the now-true path.Regression-safety (this is the v0.40.2 crash surface — widget mounting + default visibility)
The
isPlacedgate is byte-identical (no forced unconditional append — the #121 pitfall); the deriveddefaultVisibleWidgetTypes()equals the old hardcoded list in the same order (test-asserted); the persisteddashboard.layoutshape + migration are unchanged (existinglayoutEngine.test.ts70/70 passes unchanged = proof).Verification
/api/verifynot in scope).