Skip to content

tech-debt batch 5c: split layoutEngine + lift transforms + registry defaultVisible (#157/#155)#170

Merged
delabrcd merged 2 commits into
mainfrom
tech-debt/batch-5c-layout
Jun 22, 2026
Merged

tech-debt batch 5c: split layoutEngine + lift transforms + registry defaultVisible (#157/#155)#170
delabrcd merged 2 commits into
mainfrom
tech-debt/batch-5c-layout

Conversation

@delabrcd

Copy link
Copy Markdown
Owner

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 transforms

  • Split into lib/layout/{placements,merge,pagination}.ts behind a re-export barrel (layoutEngine.ts) — pure mechanical move, no function body changed; all ~18 importers unchanged. clampPage moved into pagination (cockpit re-exports).
  • Lifted the inlined placement transforms out of Dashboard.tsx (add/remove/addSpacer/togglePin/buildCurrentLg) and WidgetLayout.tsx (spacer-fold/strip-repair/persist-blob) into pure, hand-calc-tested lib/layout/transforms.ts (§2). Components keep RGL wiring + DOM effects.
  • Band helpers left un-unified (genuinely different geometry — couldn't prove byte-identical). Persisted layout JSON shape + merge semantics unchanged.

#155 — make "add a widget" registry-only

  • New WidgetDef.defaultVisible flag (the 3 interval tiles). Dashboard derives intervalWidgetTypes/fullChartUniverse (renamed from the pitfall-named availableChartsAll)/the palette by filtering the registry instead of hardcoded id lists. The 3 interval defs come from one INTERVAL_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 isPlaced gate is byte-identical (no forced unconditional append — the #121 pitfall); the derived defaultVisibleWidgetTypes() equals the old hardcoded list in the same order (test-asserted); the persisted dashboard.layout shape + migration are unchanged (existing layoutEngine.test.ts 70/70 passes unchanged = proof).

Verification

  • Docker test 69 files / 1005 tests (+42 new hand-calc); production build (lint+typecheck) green.
  • ⚠ UI-only — lead force-mounts all widgets on staging (cleared layout) + loads a saved layout + tests remove/re-add before release. No numeric change (/api/verify not in scope).

delabrcd 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.
@delabrcd delabrcd merged commit a11fb54 into main Jun 22, 2026
6 checks passed
@delabrcd delabrcd deleted the tech-debt/batch-5c-layout branch June 22, 2026 20:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant