= {
+ Idle: { fill: "secondary.main", text: "rgba(0,0,0,.87)" }, // lime + black text
+ Busy: { fill: deepOrange[300], text: "rgba(0,0,0,.87)" }, // light coral
+ Pedestal: { fill: deepOrange[300], text: "rgba(0,0,0,.87)" }, // light coral
+ Error: { fill: deepOrange[700], text: "#fff" }, // deep coral
+ Inactive: { fill: indigo[100], text: "rgba(0,0,0,.87)" }, // sidebar light blue
+};
+
+// Fixed width so the pill fits the longest label ("Measuring · 100 %") and never
+// resizes as the state changes.
+const pillSx = {
+ position: "relative", width: 200, height: 36, borderRadius: 999, overflow: "hidden",
+ display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0,
+} as const;
+const pillLabelSx = { position: "relative", fontWeight: 600, fontSize: "0.95rem", whiteSpace: "nowrap" } as const;
+
+// Three action-button tiers so exactly one button reads as "click me now":
+// hot = lime — the action to take right now
+// active = blue-on-blue (no frame), white text — available, secondary emphasis
+// off = faint ghost — not relevant in this state
+const activeColdSx = { bgcolor: "primary.dark", color: "#fff",
+ "&:hover": { bgcolor: "primary.dark", filter: "brightness(1.12)" } };
+const offSx = { bgcolor: "rgba(255,255,255,.10)", color: "rgba(255,255,255,.6)", boxShadow: "none",
+ "&:hover": { bgcolor: "rgba(255,255,255,.18)" } };
+const TIER = {
+ hot: { color: "secondary", variant: "contained" },
+ active: { color: "primary", variant: "contained", sx: activeColdSx },
+ off: { color: "inherit", variant: "contained", sx: offSx },
+} as const;
+type Tier = keyof typeof TIER;
+
+function StatusChip({s}: {s?: broker_status}) {
+ if (s === undefined)
+ return (
+
+ Not connected
+
+ );
+
+ // Measuring: a light-blue (sidebar) track filled lime to the progress fraction,
+ // with black text that stays legible over both the track and the lime fill.
+ if (s.state === "Measuring") {
+ const pct = Math.max(0, Math.min(100, (s.progress ?? 0) * 100.0));
+ return (
+
+
+
+ Measuring · {pct.toFixed(0)} %
+
+
+ );
+ }
+
+ const style = STATE_STYLE[s.state] ?? { fill: "grey.500", text: "#fff" };
+ return (
+
+ {s.state.toString()}
+
+ );
}
function StatusBar({s, onMenuClick}: MyProps) {
- const statusDescription = (): ReactNode => {
- if (s === undefined)
- return <>Not connected>;
- else
- return
- State: {s.state.toString()}
- {(s.progress !== undefined) ? " (" + FormatNumber(s.progress * 100.0) + " %)" : ""}
-
- };
+ // Pick each button's tier from the current state, so the one action that makes
+ // sense right now is lime and the rest recede.
+ const st = s?.state;
+ const cancelTier: Tier = (st === 'Measuring' || st === 'Pedestal') ? 'hot'
+ : (st === 'Busy' || st === 'Error') ? 'active' : 'off';
+ const initTier: Tier = (s === undefined || st === 'Inactive') ? 'hot'
+ : (st === 'Idle') ? 'active' : 'off';
+ const pedestalActive = st === 'Idle';
return theme.zIndex.drawer + 1}}>
@@ -46,27 +102,29 @@ function StatusBar({s, onMenuClick}: MyProps) {
Jungfraujoch
-
- {statusDescription()}
-
+
+
+
-
-
-
+
+
+
+
+
}
diff --git a/frontend/src/components/ZeroMQPreview.tsx b/frontend/src/components/ZeroMQPreview.tsx
index bc9dfb5c..3740b442 100644
--- a/frontend/src/components/ZeroMQPreview.tsx
+++ b/frontend/src/components/ZeroMQPreview.tsx
@@ -1,12 +1,14 @@
import {memo, useEffect, useState} from 'react';
import {FormControlLabel, Radio, RadioGroup} from "@mui/material";
+import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
import NumberTextField from "./NumberTextField";
import {zeromq_preview_settings} from "../client";
import {putConfigZeromqPreviewMutation} from "../client/@tanstack/react-query.gen";
import _ from "lodash";
import FormControl from "@mui/material/FormControl";
import SettingsPanel from "./SettingsPanel";
+import EmptyState from "./EmptyState";
import {useUpload} from "./useUpload";
type MyProps = {
@@ -78,9 +80,9 @@ function ZeroMQPreview({s: serverS}: MyProps) {
onUpload={() => submit(s)}
uploadDisabled={pending || ((freqChoice == "custom") && periodErr)}
snackbar={snackbar}>
-
- {empty() ? "No preview available" : `ZeroMQ socket: ${s.socket_address}`}
-
+ {empty()
+ ? } text="No preview available"/>
+ : {`ZeroMQ socket: ${s.socket_address}`}
}