From 105c643258654608cf3cbaaa5489befe6a455ed3 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Thu, 12 Nov 2026 18:57:13 +0100 Subject: [PATCH] frontend: live-test fixes - dark panel titles, phone width, rugnux-live empty states MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dark mode: the panel title band kept the light mode's pale indigo, so the light text on it was invisible. It is now the drawer's selected-entry tint at night. Phone width (390 px): Plotly plots were drawn to the box they had when the drawer was open and never redrawn, as `responsive` follows the window only - the Plot component now resizes them with a ResizeObserver on their own box. The dashboard plot toolbar's 500 px minimum width made the page overflow; the toolbar wraps and the card takes its own height below md. On the rugnux page the By-resolution and Collection plots took their height from boxes that below xl take their height from the plots, so the plots overflowed onto the strip: fixed heights below xl, single column collection plots and hero tiles two or three to a row, the progress details under the bars. The top bar's three action buttons go to a second dense row and the wordmark is hidden below sm. The preview and data-collection pages stack to one column. During a rotation collection with rugnux live the per-image plots are empty by design (the values come with rugnux's first pass): the empty plot now says so instead of showing autoscaled axes, and the statistics card shows the collection's progress in percent while measuring, as the efficiency is over the images expected and read like a fault. The rugnux page header no longer shows "Done ·" when a run has no verdict, and the Collection strip says how many images are held for the serial pre-pass (or are in, for a rotation run) instead of "waiting for images". Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01HVeDQsU5a8AJUdJPgWevAK --- frontend/src/App.tsx | 8 +-- frontend/src/components/DataCollection.tsx | 8 +-- .../src/components/DataProcessingPlot.tsx | 17 +++++- .../src/components/DataProcessingPlots.tsx | 11 ++-- .../src/components/MeasurementStatistics.tsx | 12 ++-- frontend/src/components/Plot.jsx | 18 +++++- frontend/src/components/PreviewImage.tsx | 4 +- .../src/components/RugnuxCollectionPlots.tsx | 10 +++- frontend/src/components/RugnuxHero.tsx | 6 +- frontend/src/components/RugnuxPage.tsx | 15 +++-- frontend/src/components/RugnuxProgress.tsx | 7 ++- frontend/src/components/SettingsPanel.tsx | 7 ++- frontend/src/components/StatusBar.tsx | 55 ++++++++++++------- 13 files changed, 123 insertions(+), 55 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 39e3c8831..b73aa5b25 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -28,7 +28,7 @@ import _ from "lodash"; import Markdown from "react-markdown"; import remarkGfm from "remark-gfm"; -import StatusBar from "./components/StatusBar"; +import StatusBar, { StatusBarSpacer } from "./components/StatusBar"; import ErrorMessage from "./components/ErrorMessage"; import DataProcessingPlots from "./components/DataProcessingPlots"; import MeasurementStatistics from "./components/MeasurementStatistics"; @@ -164,7 +164,7 @@ function App() { - + @@ -270,7 +270,7 @@ function App() { sx={{ width: drawerWidth, flexShrink: 0, '& .MuiDrawer-paper': { width: drawerWidth, boxSizing: 'border-box', bgcolor: drawer.bg, borderRight: 'none' } }}> - + {sections.map((sec, i) => ( )} - + {activeItem.render?.()} diff --git a/frontend/src/components/DataCollection.tsx b/frontend/src/components/DataCollection.tsx index 6bcd6515a..73cf2990e 100644 --- a/frontend/src/components/DataCollection.tsx +++ b/frontend/src/components/DataCollection.tsx @@ -94,7 +94,7 @@ function DataCollection({frame_time_us}: MyProps) { value={tokens} sx={{width:"90%"}} /> - + { @@ -137,7 +137,7 @@ function DataCollection({frame_time_us}: MyProps) { default={s.images_per_file} /> - + { @@ -196,7 +196,7 @@ function DataCollection({frame_time_us}: MyProps) { {mode === 'rotation' && ( - + setGoniometer(prev => ({...prev, start: val}))} @@ -220,7 +220,7 @@ function DataCollection({frame_time_us}: MyProps) { )} {mode === 'grid' && ( - + setGrid(prev => ({...prev, n_fast: val}))} diff --git a/frontend/src/components/DataProcessingPlot.tsx b/frontend/src/components/DataProcessingPlot.tsx index bdd485a1a..539b8badd 100644 --- a/frontend/src/components/DataProcessingPlot.tsx +++ b/frontend/src/components/DataProcessingPlot.tsx @@ -3,11 +3,20 @@ import {ReactNode} from 'react'; import {useQuery} from "@tanstack/react-query"; import ShowChartIcon from "@mui/icons-material/ShowChart"; import {azint_unit, plot_type, plot_unit_x} from "../client"; -import {getPreviewPlotOptions} from "../client/@tanstack/react-query.gen"; +import {getConfigRugnuxOptions, getPreviewPlotOptions, getRugnuxStatusOptions} from "../client/@tanstack/react-query.gen"; import MultiLinePlotWrapper from "./MultiLinePlotWrapper"; import EmptyState from "./EmptyState"; import {useTokenRequired} from "../auth"; +// Whether rugnux live is on a run: the per-image values then come with its first pass over the images, +// not image by image. The same queries the rugnux card polls, so this costs no request of its own. +function useRugnuxRunning() { + const {data: settings} = useQuery({...getConfigRugnuxOptions(), refetchInterval: 2000}); + const enabled = settings?.enable === true; + const {data: s} = useQuery({...getRugnuxStatusOptions(), refetchInterval: 1000, enabled}); + return enabled && s?.state === "Running"; +} + type MyProps = { type: plot_type; binning: number; @@ -108,6 +117,7 @@ function DataProcessingPlot({type, binning, angle, azint}: MyProps) { }); const tokenRequired = useTokenRequired(); + const rugnuxRunning = useRugnuxRunning(); if (isError || (plots === undefined) @@ -116,6 +126,11 @@ function DataProcessingPlot({type, binning, angle, azint}: MyProps) { return } text={tokenRequired ? "Dataset token required (key button in the top bar)" : "No plots available"}/>; + // a plot with no points (an image without a value is left out): nothing measured yet + if (plots.plot.every(d => d.y.length === 0)) + return } + text={rugnuxRunning ? "Per-image values come with rugnux's first pass over the images" : "No values yet"}/>; + let data: PlotlyData = []; if ((plots.plot[0].z !== undefined) && (plots.plot[0].z.length > 0)) { diff --git a/frontend/src/components/DataProcessingPlots.tsx b/frontend/src/components/DataProcessingPlots.tsx index bff4a20bf..8dbe43bd1 100644 --- a/frontend/src/components/DataProcessingPlots.tsx +++ b/frontend/src/components/DataProcessingPlots.tsx @@ -31,11 +31,12 @@ function DataProcessingPlots({type: initialType, height}: MyProps) { setBinning(String(event.target.value).toString()); }; - return - + // On a phone the toolbar wraps, the select above the three controls, and the card takes the height it needs + return + - - + +