Skip to content

Lisbon: dashboard widget sizing strategy + demo data + status polish#127

Merged
ZingerLittleBee merged 19 commits into
mainfrom
lisbon
May 28, 2026
Merged

Lisbon: dashboard widget sizing strategy + demo data + status polish#127
ZingerLittleBee merged 19 commits into
mainfrom
lisbon

Conversation

@ZingerLittleBee

Copy link
Copy Markdown
Owner

Summary

This branch bundles a focused refactor of the dashboard widget sizing model with several adjacent improvements that landed on the same long-running branch:

  • Dashboard widget sizing strategy (new) — formalises four sizing strategies (free, fixed, aspect-square, content-height) into an explicit sizing field on every widget type, backed by a two-layer dispatcher: render-time normalizeRenderItem (Layer A) for idle h, and RGL v2 LayoutConstraints (Layer B) for resize-time enforcement. Replaces the scattered SQUARE_TYPES/AUTO_HEIGHT_TYPES/visualSquareHFine/interactionStateRef markers with a single dispatcher in apps/web/src/components/dashboard/sizing-strategies/. Gauge cells now render visually pixel-square in the grid; aspect-square widgets snap to legal tiers ([2,3,4,5,6]) on resize end; content-height widgets lock height to their measured content; fixed widgets stay non-resizable. Spec: docs/superpowers/specs/2026-05-28-dashboard-widget-sizing-strategy-design.md; plan: docs/superpowers/plans/2026-05-28-dashboard-widget-sizing-strategy.md.
  • Local demo data mode — new make dev-demo flow (admin/admin123) seeds a curated multi-server dataset for frontend development without a real agent fleet (crates/server/src/dev_demo.rs, scripts/dev-demo.sh, scripts/server-dev-demo.sh).
  • Status page polish — server summary card / row refactor with new grid + list layout toggle, richer public server metrics, and anonymous endpoint redaction.
  • Gauge widget visual — radial-gauge redesign (apps/web/src/components/dashboard/widgets/gauge.tsx) with metric icons, pixel-square inner card, and centered subtitle.
  • Misc: harden metric-card config against missing metric; traffic helper tweaks; layout-toggle component; spec/plan docs.

The sizing-strategy work is fully covered by Vitest (32 new tests across sizing-strategies/*.test.ts and dashboard-layout.test.ts), and the previously-failing dashboard-grid integration tests (gauge h expectations) now pass under the new dispatcher.

Test plan

  • cargo test --workspace — Rust suite passes
  • bun run typecheck — frontend + docs typecheck clean
  • cd apps/web && bun run test — 631/631 passing (added 41 tests under sizing-strategies/ + dashboard-layout.test.ts)
  • bun x ultracite check — frontend lint clean
  • Manual verification via make dev-demo against Chrome DevTools:
    • Aspect-square (gauge): pixel-square cell, only se resize handle, aspectRatio(1) constraint attached
    • Content-height (top-n): e-only handle, lockHeight(measured) constraint
    • Fixed (stat-number): no resize handles, isResizable: false
    • Free (line-chart): s/e/se handles per resizeConfig
    • No console errors during edit-mode interactions
  • CI green

- move 270° gap to the bottom (speedometer-style horseshoe)
- fill the card by enlarging the ring radius and dropping end-cap balls
- lock the visible card to a 1:1 aspect via container-size queries so it
  stays square inside non-square grid cells
- add internal padding between the ring and the card border
- move the server name into the bottom arc gap
- guard metric-card render with a friendly empty state when the metric
  spec is missing (also blocks the records query so it stops looping)
- seed default 'cpu' metric in WidgetConfigDialog on save so a no-change
  submit persists a valid config
- show selected option label in ServerSelect/MetricSelect triggers via
  SelectValue children render function instead of raw value
The 12-col grid step (~85px) and the fine row step (24px) are asymmetric, so
a square widget with `w_coarse === h_coarse` rendered as a tall rectangle.

Derive the rendered h from w via `visualSquareHFine = round(w * colStep /
fineRowStep)` so the cell ends up pixel-square in steady state. The persisted
`grid_h` still mirrors `grid_w` (square invariant); only the rendered fine h
differs.

To stop RGL from undoing the override:
- minH/maxH are derived too so the post-render clamp matches the override
- during an active resize, h tracks `w * SCALE` so react-resizable's cursor
  delta stays in sync; idle RGL echoes keep the pixel-square value via an
  interactionState ref check
The percent sign is decoration, not data — render it smaller and dimmer so
the eye lands on the number first.
Formalize the four sizing strategies (free, fixed, aspect-square,
content-height) into an explicit `sizing` field on each widget type,
backed by a per-strategy dispatcher. Replaces the scattered
SQUARE_TYPES / AUTO_HEIGHT_TYPES / inline isResizable derivation.
Address review feedback:

- Fix react-grid-layout/core subpath import for aspectRatio and
  LayoutConstraint (root index does not re-export them).
- Split the design into Layer A (render-time normalizeRenderItem in
  baseLayout) and Layer B (resize-time LayoutConstraint via RGL's
  applySizeConstraints pipeline). RGL does not run constraints on idle
  layout sync, so aspect-square / content-height idle h must be set by
  baseLayout, not by constraints alone.
- snapOnRelease now returns a coarse-unit SnapPatch with an explicit
  applyCoarsePatch helper, ending the fine/coarse mix that broke the
  previous draft.
- updateLiveLayout keeps the existing SCALE-multiple h snap for free /
  fixed widgets to prevent resize jitter; aspect-square and
  content-height own their fine h via constraints / measurement.
Replace SQUARE_TYPES / AUTO_HEIGHT_TYPES / visualSquareHFine /
interactionStateRef with widgetById + getStrategy + normalizeRenderItem
(layer A, idle) + applyStrategy (layer B, RGL constraints). Per-strategy
h snap in updateLiveLayout/commitLayoutChange replaces the inline
overrides. snapOnRelease + applyCoarsePatch land aspect-square tier
snaps on resize end.
@ZingerLittleBee ZingerLittleBee merged commit 9895921 into main May 28, 2026
2 checks passed
@ZingerLittleBee ZingerLittleBee deleted the lisbon branch May 28, 2026 01:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant