From 912ecdd9ede0f1ba616ca06d2dfa690980511bcd Mon Sep 17 00:00:00 2001 From: Martino Ferrari Date: Wed, 6 May 2026 15:55:45 +0200 Subject: [PATCH] Improved UI --- docs/FUNCTIONAL_SPEC.md | 166 ++++++++----- docs/TECHNICAL_SPEC.md | 248 +++++++++++++------- internal/datasource/synthetic/dsp_bridge.go | 10 + internal/dsp/nodes.go | 62 +++++ web/src/App.tsx | 2 + web/src/EditMode.tsx | 28 ++- web/src/InterfaceList.tsx | 5 +- web/src/LuaEditor.tsx | 137 +++++++++++ web/src/PlotPanel.tsx | 224 +++++++++++++++--- web/src/PropertiesPane.tsx | 5 +- web/src/SignalTree.tsx | 5 +- web/src/SyntheticEditor.tsx | 42 ++-- web/src/SyntheticWizard.tsx | 36 ++- web/src/ViewMode.tsx | 33 ++- web/src/ZoomControl.tsx | 35 +++ web/src/styles.css | 210 ++++++++++++++++- web/src/widgets/Gauge.tsx | 23 +- web/src/widgets/PlotWidget.tsx | 64 +++-- web/src/widgets/SetValue.tsx | 85 +++++-- 19 files changed, 1141 insertions(+), 279 deletions(-) create mode 100644 web/src/LuaEditor.tsx create mode 100644 web/src/ZoomControl.tsx diff --git a/docs/FUNCTIONAL_SPEC.md b/docs/FUNCTIONAL_SPEC.md index deed87e..97e69af 100644 --- a/docs/FUNCTIONAL_SPEC.md +++ b/docs/FUNCTIONAL_SPEC.md @@ -24,58 +24,97 @@ In the initial version all users share the same access level. Role-based access The default mode when opening the application. -**Interface list pane (left, collapsible)** -- Displays all interfaces saved on the server, grouped into a tree by folder. +**Interface list pane (left, collapsible, resizable)** +- Displays all interfaces saved on the server. - Right-click on an interface: options to open in Edit mode or clone it. - "New interface" button opens Edit mode with a blank canvas. -- "Import" option loads an interface from a local XML file. +- The pane width can be adjusted by dragging the resize handle on its right edge. + +**Tabs** +- **HMI tab**: the live widget canvas (described below). +- **Plot tab**: a dedicated live multi-signal plot panel (see §3.3). +- **Info tab**: signal info and metadata display for the last right-clicked signal. **HMI canvas (center)** - Renders the selected interface as a live, interactive panel. - Widgets display real-time data; controls (set-value, buttons) are active. - No drag, resize, or layout operations are possible in this mode. - Right-clicking any widget opens a context menu: - - **Signal info** — floating window showing DS name, type, unit, range, current value and timestamp. + - **Signal info** — switches to the Info tab showing DS name, type, unit, range, current value and timestamp. - **Copy signal name** — copies the signal identifier to the clipboard. - - **Export data to CSV** — downloads buffered/historical data for the signal(s) used by the widget. + - **Export data to CSV** — downloads buffered data for the signal(s) used by the widget. + - **Plot** — adds the signal(s) to the Plot tab. **Top toolbar** - Show/hide interface list pane. -- Time selector: navigate to a past timestamp (enabled only when the server has archive access for all signals on the canvas). -- "Live" button: return to real-time data after historical navigation. +- **⏱ History** button: toggle historical time navigation bar. +- Zoom control (A− / % / A+): adjust the UI scale (see §3.4). +- Edit button: switch to Edit mode for the current interface. +- Connection status indicator. + +**Historical time navigation bar** (shown when History is active) +- Date/time range pickers (start and end). +- **Load** button fetches archive data and replaces live data in all widgets. +- **Live** button discards archive data and resumes real-time streaming. +- Status shown on plot widgets: "Loading history…", "No archive data for this range", "Archive unavailable". ### 3.2 Edit Mode -Activated via the "New interface" button or by right-clicking an existing interface. +Activated via the "New interface" button or by clicking Edit in the toolbar. **Signal tree pane (left, resizable and collapsible)** - Shows all signals known to each connected data source. - Sources are shown as top-level nodes; signals are nested within. - User can add custom entries: - For EPICS: manually enter a PV name. - - For Synthetic: define a new synthetic signal (see §5.2). -- User can load a CSV file with columns `NAME, DataSource, DS_PARAMETERS` to import a batch of signals. + - For Synthetic: define a new synthetic signal via the Synthetic Wizard (see §5.2). - Filter/search box to narrow the list. +- Synthetic signals show an edit (✎) button to reopen the wizard. **Widget canvas (center)** - Free-form canvas where widgets can be placed at arbitrary pixel positions. -- Background grid with optional snap-to-grid. +- Background grid with snap-to-grid. -**Properties pane (right, collapsible)** +**Properties pane (right, resizable and collapsible)** - Appears when one or more widgets are selected. - Displays and edits all options for the selected widget (see §4). +- Width is adjustable by dragging the resize handle on its left edge. **Top toolbar** -- Show/hide signal pane. -- Show/hide properties pane. +- Show/hide signal pane / properties pane. - Undo / Redo (also Ctrl+Z / Ctrl+Shift+Z). +- Import / Export interface to/from local XML file. - Save interface to server. -- Load interface from server. -- Export interface to local XML file. -- Import interface from local XML file. -- "Add text" tool — inserts a static text label. -- "Add image" tool — inserts a static image (uploaded to server or embedded as base64). -- "Add link" tool — inserts a button that opens another interface. +- Close (return to View mode). +- Zoom control (A− / % / A+). + +### 3.3 Plot Tab (Live Multi-Signal Plot) + +A dedicated panel for live time-series plotting of any signals, independent of the interface layout. + +**Adding signals:** right-click any widget in the HMI tab → **Plot**. The signal is added to the Plot tab immediately. + +**Time window selector:** 10s / 30s / 1m / 5m / 15m / 1h buttons control how much history is displayed. + +**Per-signal legend:** +- Color swatch and signal name. +- Statistics table: Last / Min / Max / Mean over the current window. +- ✎ button opens an inline style editor: + - **Color**: color picker. + - **Width**: line width (none, 1, 1.5, 2, 3 px). + - **Line**: solid / dashed / dotted. + - **Markers**: none, S (3 px), M (5 px), L (8 px). +- ✕ button removes the signal from the plot. + +**Chart area:** rendered with uPlot; auto-scaled Y axis; time axis tracks the rolling window in real time. + +### 3.4 UI Zoom + +The toolbar in both modes contains a zoom control (**A−** / **nn%** / **A+**) that adjusts the base font size of the entire UI: + +- 11 zoom steps from 50% to 250% (50, 60, 75, 85, 100, 115, 130, 150, 175, 200, 250%). +- The selected zoom level is persisted in `localStorage` and restored on the next page load. +- At 100%, the base font size adapts to viewport height via `clamp(13px, 1.5vh, 18px)`, making the UI naturally usable on 4K displays without any manual adjustment. --- @@ -83,7 +122,7 @@ Activated via the "New interface" button or by right-clicking an existing interf ### 4.1 Creating Widgets -Drag a signal from the signal tree and drop it onto the canvas. A picker appears showing all widget types compatible with the signal's data type (iconised). The user selects one and the widget is placed at the drop location with default size. +Drag a signal from the signal tree and drop it onto the canvas. A picker appears showing all widget types compatible with the signal's data type. The user selects one and the widget is placed at the drop location with default size. ### 4.2 Selecting Widgets @@ -104,8 +143,7 @@ When multiple widgets are selected: - An align/distribute toolbar appears above the canvas with: - Align left / center horizontal / right. - Align top / center vertical / bottom. - - Distribute evenly — by center spacing (horizontal/vertical). - - Distribute evenly — by gap size (horizontal/vertical). + - Distribute evenly (horizontal/vertical). ### 4.4 Widget Catalogue @@ -115,7 +153,7 @@ When multiple widgets are selected: | Gauge | numeric scalar | Circular or arc gauge with configurable range | | Vertical bar | numeric scalar | Vertical level indicator | | Horizontal bar | numeric scalar | Horizontal level indicator | -| Set value | numeric or string, writable | Shows `name: [input field] current_value unit` + Set button | +| Set value | numeric, string, or enum; writable | Input field or enum dropdown + Set button | | LED | boolean / numeric | Coloured indicator with configurable condition and label | | Multi-LED | integer (bitset) | One LED per bit with individual labels and conditions | | Button | writable | Sends a fixed value or command on click | @@ -140,45 +178,62 @@ When multiple widgets are selected: Common to all: - Label text, font size, text colour. - Position (X, Y) and size (W, H) — editable numerically. -- Data source and signal name (read-only after creation; reassignable via drag). +- Data source and signal name. Per type: - **Gauge / Bar**: min value, max value, alert thresholds with colours, unit label. -- **LED / Multi-LED**: condition expression (e.g. `value > 0`), colours for true/false states. -- **Plot**: plot sub-type selector, Y-axis range (auto or manual), time window duration, legend position, colour per signal, line style. -- **Set value**: input type (numeric / string / enum), confirmation prompt toggle. +- **LED / Multi-LED**: condition expression (e.g. `value > 0`), colours for true/false states, label. +- **Plot**: plot sub-type selector, Y-axis range (auto or manual min/max), time window duration, legend position (top / bottom / none), value format string. +- **Set value**: no special options — enum mode is detected automatically from signal metadata. - **Link**: target interface name. +### 4.6 Set-value Widget — Enum Mode + +When the signal's metadata reports enum strings (e.g. EPICS mbbi/mbbo records): +- The input field is replaced by a `