feat(theme): clean-modern light mode across theme surfaces#375
feat(theme): clean-modern light mode across theme surfaces#375HanSur94 wants to merge 22 commits into
Conversation
Redesign the FastSense 'light' preset with a clean-modern identity: crisp white widget surfaces on a soft neutral-gray canvas, blue #2563EB drag/drop accent, hairline cool borders, and a crisp semantic green/amber/red status trio tuned for readability on white. Palette approved via a Pencil mockup and mapped 1:1 to flat-render primitives (fills, 1px borders, solid arcs) so it stays cheap to draw in MATLAB. - FastSenseTheme.m: rewrite the light/legacy-alias preset (canvas bg, cool low-contrast grid at alpha 0.7, semantic-red threshold). 'muted' line palette unchanged. - DashboardTheme.m: rewrite the light branch surfaces/borders/accent; make StatusOk/Warn/Alarm per-preset (guarded shared fallback) so light gets its own crisp trio while dark stays byte-identical. - CompanionTheme inherits automatically (Accent<-DragHandle, LineColors<-muted). - Update TestTheme/TestFastSenseTheme light Background assertions to the new value. Verified in MATLAB: light values match the approved palette, dark preset output isequaln to pre-change baseline, legacy aliases resolve to light, companion inherits the accent. Theme suites green (TestTheme 12, DashboardTheme 6, FastSenseTheme 11, StatusWidget 11, companion theme-walker 36). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Give the dashboard grid inter-widget gutters + outer padding so widgets read as separated cards on the canvas — the first, cheapest slice of the approved Pencil redesign. DashboardLayout.computePosition already consumes GapH/GapV/Padding and allocatePanels already draws a hairline line-border per cell; they just defaulted to 0 (flush grid). This wires tasteful values in via the theme: - DashboardTheme.m: add additive shared fields WidgetGapH (0.006), WidgetGapV (0.010), DashboardPad ([0.008 0.010 0.008 0.010], normalized [L B R T]). Both presets; no field renamed/removed. - DashboardEngine.m: before allocatePanels, set Layout.GapH/GapV/Padding from the theme, isfield-guarded so older serialized themes still get the refreshed spacing rather than a flush grid. - DashboardLayout.m unchanged (already consumes the knobs). Verified in MATLAB: example_dashboard_all_widgets (light, 18 widgets) renders with visible gutters + edge padding, widgets read as separated cards, no overlap or scroll breakage. Suites green: TestDashboardLayout 8/8, TestDashboardEngine 18/18, TestDashboardTheme 6/6. TestDashboardDetach testDetachButtonInjected fails identically on the clean baseline (confirmed pre-existing, not a regression; tracked separately). Backward-compatible: additive theme fields only; dashboards built with a directly-constructed DashboardLayout (no theme) keep Gap=0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
De-clutter the dashboard by suppressing the redundant MATLAB axes exploration toolbar (download/brush/datatips/pan/zoom/home) that renders on every plot widget. The dashboard already provides its own controls (widget button bar + time navigator), so the per-axes toolbar is pure visual noise — the approved Pencil redesign removes it. - DashboardLayout.realizeWidget: after each widget renders, call a new private static helper hideAxesToolbars_(widget.hCellPanel). Central, so it covers all 14 axes-bearing widget types and re-applies on scroll-realize. - hideAxesToolbars_: findall axes under the panel, set Toolbar.Visible='off', per-axes try/catch so a toolbar-less/stale axes never aborts realize. Scope: axes-toolbar suppression only. The X V A L axis-toggle + ^/+ button strip consolidation is deferred to its own phase (interaction-sensitive; hover-hiding fights uifigure primitives). Verified in MATLAB: example_dashboard_all_widgets (light, 18 widgets) — the floating toolbars are gone from every visible plot (before/after exportapp compare); the 3 residual 'visible' toolbars are on Visible='off' overlay axes (render nothing). Suites green: TestDashboardLayout 8/8, TestDashboardEngine 18/18, TestScatterWidget 5/5, TestHeatmapWidget 5/5. No functional regression (button bar, detach, zoom-via-scroll unaffected); standalone FastSense plots untouched (helper only runs in the dashboard realize path). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Widget titles no longer overlap their plots. Chart widgets used to create a full-panel axes then call title(ax, obj.Title), which floated text just above the plot box with a ~10% margin — so 'Machine Mode', 'Violation Count by Sensor', 'Temperature Distribution', 'Temp vs Pressure' collided with the plot / clipped at the panel top. - DashboardWidget: new protected drawPanelTitle_(parentPanel, theme) renders obj.Title as a bold header band (a sibling uicontrol text, immune to the newplot title-clearing that forced the old in-refresh re-apply). Tagged + self-deleting so repeated render() calls never stack duplicates. - BarChart/Heatmap/Histogram/Scatter/EventTimeline: draw the header via the helper in render(); drop the axes top to make room; remove the in-axes title() calls (both the render and the newplot re-apply sites in BarChart/ Histogram). Band sized to ~18% so it holds the font even in short dashboard cells (the first cut used 9px, smaller than the 15px glyphs, so the text clipped to nothing — fixed). Verified in MATLAB: example_dashboard_all_widgets — the four main chart titles now sit in clean headers above their plots, no overlap/clip (before/after exportapp). Suites green: TestBarChartWidget 5/5, TestHistogramWidget 6/6, TestScatterWidget 5/5, TestHeatmapWidget 5/5, TestDashboardEngine 18/18. Known limitation: the 1-row EventTimeline is too short to show its header in this example; deferred (needs a min-height or inline title for degenerate cells). FastSenseWidget's core-drawn title left as-is (reads fine). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
NumberWidget gets a proper card when the cell has real height: title top-left (muted), a big left-aligned value with an inline unit, and a trend indicator top-right in the accent color — matching the Pencil redesign. Adaptive by design: dashboard KPI cells are often only a couple of grid rows (an ~11px content panel in the all-widgets example), far too short for a stacked card — a first pass there clipped the value/title to nothing (font > band height). So render() branches on panel pixel height: >=55px uses the card, otherwise the proven compact single-row layout (restored verbatim), so short cells never regress. Verified in MATLAB: main example KPI cells fall back to compact and still show their values (72.9 / 56); a tall [1 1 8 8] number widget renders the card — value fs=30 in a 282px box (no clip), title/unit/trend laid out cleanly. Suites green: TestNumberWidget 12/12, TestDashboardEngine 18/18. relayout_ (clears + re-renders on resize) picks the right branch for the new size automatically. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The clean-modern light theme (PR #375) set TabActiveBg (accent-soft) and TabInactiveBg to near-identical luminance (delta 0.0027), so active vs inactive group tabs were visually indistinguishable — TestGroupWidget/ testTabContrastAllThemes failed for the light preset and its legacy aliases. Darken TabInactiveBg to [0.850 0.870 0.900] so the accent-soft active tab clearly stands out (>=0.05 luminance delta). Dark preset unchanged. Caught by a post-Phase-4 confidence sweep; regression predates the UI-refresh phases (introduced with the light palette). TestGroupWidget now 36/36. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
MultiStatusWidget derived a dot's color by calling tag.valueAt(now) and treating value >= 0.5 as alarm. That 0/1 mapping is only valid for monitor-kind tags (binary alarm signals). A plain SensorTag returns its raw reading (e.g. 72.9 °F), which is always >= 0.5 — so every sensor dot rendered alarm-red regardless of its actual state (visible in example_dashboard_all_widgets: Temperature/Pressure/Flow all red despite 0 violations on Pressure/Flow). Gate the >= 0.5 -> alarm mapping on monitor-kind in both color paths (deriveColor bare-tag branch + deriveColorFromTag_), via tag.getKind()=='monitor' (polymorphic, per Pitfall 1 — no isa-on-subclass). Non-monitor tags with no threshold now fall to the default OK color. Verified in MATLAB: example MultiStatus dots now OK-green x5, alarm-red x0 (were all red). Monitor-kind alarm behavior preserved: TestMultiStatusWidgetTag 7/7, test_multistatus_monitortag_bare 4/4. (Pre-existing unrelated failure: test_multistatus_widget_tag references the removed 'Threshold' class.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Hide the 14 native uicontrol/uipanel toolbar handles (Visible=off, never deleted) and paint a custom axes+patch visual layer over obj.hPanel — brand square + bold name (left, click-to-rename via inputdlg), flat bordered action buttons/toggles, and a last-update label (right) — for the light-mode redesign. Every click routes to the SAME existing handler; the three toggles flip the hidden control's Value before calling the on*Toggle method with the hidden handle, keeping DashboardEngine/DashboardConfigDialog reads and existing tests working unchanged. Cheap-render only; all colors/sizes from the theme struct.
Add paintToggleActive_ helper and wire it into setLiveActiveIndicator (green/StatusOkColor), setFollowActiveIndicator and setEventsActiveIndicator (blue/InfoColor) so the custom Live/Follow/ Events pills reflect active state — while preserving every existing hidden *Panel HighlightColor + hEventsBtn String/Value behavior the must-pass test suites assert. setLastUpdateTime now dual-writes both the hidden hLastUpdate and the custom hLastUpdateText_ from one computed timestamp string. Constructor primes the three custom pills from the hidden controls' initial Values (Events starts active).
Token contract (Pencil var ↔ hex ↔ MATLAB RGB triplet ↔ theme field), cheap-render budget, per-surface geometry (plot/dashboard/companion) mapped to real MATLAB mechanisms, and redline gap list. Backs the light-mode work on this branch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…dline d) Compatibility-shim: 14 native toolbar handles hidden (Visible='off', never deleted) behind a custom axes+patch layer matching the Pencil mock. Live-verified; 7/8 must-pass suites green, 1 failure proven pre-existing via A/B. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…le=off - Add 'Visible','off' to the 4 folded creation sites: addPlantLogToggle (L), addCrosshairLinkToggle (X), addYLimitButton_ (V+A shared helper), addCreateEventButton (+) - Buttons remain fully alive (Tag/Position/Callback/TooltipString/bg/UserData unchanged) - addInfoIcon and addDetachButton left untouched (Info/Detach stay visible) - MISS_HIT mh_style + mh_lint clean
- New addOverflowMenu_ (private) creates the single ... OverflowMenuButton, guarded to fold-eligible widgets only (>=1 of the 5 folded buttons present) - New showOverflowMenu_ (private) rebuilds a fresh OverflowContextMenu on every click, routing items to the existing action methods (onCrosshairLinkTogglePressed_ / onYLimitButtonClicked_ / onPlantLogTogglePressed_ / invokeCreateEventCallback_) with live Checked/label state - Wired into realizeWidget + the 3 callback-driven rebuild methods (addCrosshairLinkToggle, addPlantLogToggle, addYLimitButtons_), each guarded with try/catch -> DashboardLayout:overflowMenuFailed - Theme tokens only (ToolbarBackground/ToolbarFontColor); glyph is char(8230) for Octave/MISS_HIT safety - MISS_HIT mh_style + mh_lint clean
- Hoist xLink so it is computed unconditionally (was previously only assigned inside the CrosshairLinkButton presence check), reused by both the CrosshairLinkButton anchor and the new overflow anchor - OverflowMenuButton lands in the leftmost folded slot: xLink -> xVisible -> xAll fallback, matching whichever folded buttons are present - No change to how Detach/Create/Info/PlantLog/V/A/CrosshairLink anchor - MISS_HIT mh_style + mh_lint clean
…ne e) Fold X/V/A/L/+ behind a single '…' overflow menu via the hide-not-delete shim; Info/Detach stay visible. Live-verified; isolated chrome suites green; two failures (plant-log toggle multi-suite ordering + companion tag-registry) proven pre-existing via A/B against base. Marks redlines d+e DONE in the design spec. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ign-d00ff7 # Conflicts: # .planning/STATE.md # libs/Dashboard/BarChartWidget.m # libs/Dashboard/HistogramWidget.m
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
|
Re: the This check is advisory / non-blocking on this repo (the If you'd rather this stop red-flagging UI PRs going forward, a small 🤖 Addressed by Claude Code |
What
Redesigns the FastSense light theme with a clean-modern identity — designed in Pencil, then translated 1:1 into the existing MATLAB theme structs. Crisp white widget surfaces on a soft neutral-gray canvas (
#F5F6F8), blue#2563EBdrag/drop accent, hairline cool borders, and a crisp semantic green/amber/red status trio tuned for readability on white.Every value maps to a flat-render primitive (solid fills, 1px borders, solid arcs, native dashed threshold) — no gradients, shadows, blur, or transparency stacks — so the theme stays cheap for MATLAB to draw.
Changes
libs/FastSense/FastSenseTheme.m— rewrite the'light'/legacy-alias preset:Background [0.961 0.965 0.973],ForegroundColor [0.208 0.255 0.333],GridColor [0.914 0.929 0.949],GridAlpha 0.7,ThresholdColor [0.937 0.267 0.267].LineColorOrderkept'muted'. Dark preset untouched.libs/Dashboard/DashboardTheme.m— rewrite the light branch (surfaces, hairline borders, blue accent, cool group/tab backgrounds).StatusOk/Warn/Alarmmade per-preset with a guarded (if ~isfield) shared fallback, so light gets its crisp trio while dark keeps its original values.CompanionTheme.m— no edit;AccentandLineColorsderive from DashboardTheme, so the light palette flows through automatically.TestTheme(3) andTestFastSenseTheme(1) lightBackgroundassertions to the new value.Verification (live MATLAB session)
isequaln == 1for bothFastSenseTheme('dark')andDashboardTheme('dark')vs a pre-change baseline snapshot.default/ocean/scientific/industrial) resolve to light.Accent == DragHandleColor == [0.145 0.388 0.922].example_dashboard_all_widgets(18 widgets) — figure background confirmed[0.961 0.965 0.973].Backward compatibility
No field renamed or removed; no API change; legacy preset aliases intact; dark preset unchanged. Existing dashboards, scripts, and serialized themes keep working.
🤖 Generated with Claude Code