# TODO

- **BUG FIX**:
  - [x] connecting from firefox always get default user — native SPNEGO/Kerberos auth (`[server.kerberos]`, `internal/server/kerberos.go`): uopi challenges with `401 Negotiate` and resolves the user from the validated ticket instead of relying on a proxy header Firefox never triggers; on non-Kerberos hosts (SSSD/LDAP) use standalone built-in HTTP Basic auth — either validated via PAM (`[server.basic_auth]`, `internal/pamauth`, build with `make backend-pam`) or, keeping the static binary, pure-Go LDAP search-then-bind (`[server.ldap]`, `internal/ldapauth`) — sharing `internal/server/basicauth.go` (which also challenges the page load so the browser shows its login dialog) with optional built-in TLS (`[server.tls]`)
  - [x] dpi scaling seems not to work on firefox — root font-size now folds in `window.devicePixelRatio` (`applyZoom` = 16×zoom×dpr) with `watchDpr()` re-applying on monitor change
- [ ] **MAJOR** Implement configuration manager:
  - configuration is a set of signals (that can be organized in group and subgroup) that are used to configure a system or a sub system
  - with configuration manager user should be able to:
    - Define and manage configuration sets: delete (keep server side copy of deleted config), create (specifying default to parameters, mandatory, optional etc), edit (with versioning git style) and compare set
      - user can fork an existing config instance to create a new one
      - user can compare two instances: show unified diff or side by side diff
      - etc...
    - Create, delete (keep server cache), edit (git style versioning) and compare configuration instances: meaning set actual value to a configuration set
      - user can fork an existing config instance to create a new one
      - user can compare two instances: show unified diff or side by side diff
      - etc...
  - [x] user can apply and save configuration instances from manager
  - [x] **instance snapshot**: create a new instance from the current live value of all of a set's target signals (optional label, else auto name) — exposed in the config editor (⎙ Snapshot), control loop (`action.config.snapshot`) and logic editor (`action.config.snapshot`)
  - [x] **live diff**: compare a stored instance against current signal values ("Diff vs current" button → `/config/instances/{id}/livediff`)
  - [x] git-style versioning for config sets and instances: fork any revision, click to view, promote to current, with unified / side-by-side diff (shared `VersionTree`)
  - [x] in logic editor and control loop add nodes to read/write/create/apply config instances
    - [x] **apply** and **read** config-instance nodes in both control logic (server Go) and panel logic (client TS)
    - [x] **create / write** nodes (mutate versioned instances from automation) in both engines
    - [x] **Config Selector** panel widget: operator picks an instance of a chosen set (optionally a subset) from a combo, writing its id to a panel-local string variable; apply/read/write panel-logic nodes can read their instance dynamically from that variable ("From variable" source)
  - [x] support for all supported types (number, bools, enums, arrays, string etc)
  - [x] ux elements for config set
    - [x] the configuration editor should automatically get type and info from signal when possible (type is read-only, auto-derived from the bound signal)
    - [x] a slick tree drag and drop editor to order elements and organize in group and sub-groups
    - [x] possibility to customise unit, max, min etc
    - [x] export / import a config set as JSON
    - [x] full-screen config window
  - [x] ux elements for config instances:
    - automatically use the correct setting widget depending on the type (e.g. combo for enum, nubmer input for number):
      - for arrays: import csv option, display points as mini plot (+ open dialog plot with more info), manual enter points via table like interface
    - automatically fill with default or existing values (when forking an existing instance)
    - explicity show errors/missing info and give additional info on hover
  - [x] add advanced validation / transformation framework to configurations using custom CUE rules:
    - [x] backend runs validation / transformation rules (real CUE via `cuelang.org/go`; `internal/confmgr/cue.go`). Rules are a third versioned `Kind` bound to a set; evaluated on instance create/update — violations block the save, concrete derivations transform & persist the values
    - [x] user can create/edit/delete/compare rules from webui (Rules tab in ConfigManager; git-style versioning + side-by-side/unified source diff)
      - [x] integrate syntax highlight (hand-rolled `CueEditor.tsx`, mirroring `LuaEditor`)
      - [x] integrate autocomplete for signals names and cue grammars (param keys + target signals + CUE keywords/types; Ctrl+Space)
      - [ ] ~~integrate cue lsp~~ — descoped: a separate language-server process does not fit the no-npm / single portable-binary architecture
    - [x] when validation fails the error is propagated to the user (live `/config/rules/check` panel while editing; save returns the structured violation)
    - [x] all actions tracked via history (versioned rules) + audit (`config.rule.create/update/delete/promote/fork`)
- [ ] Improve UX:
  - Config editor:
    - [x] Instance should be filtered by config set (combo box) (`ConfigManager.tsx` InstancesManager: `setFilter` combo in the instance list head, shown when >1 set; empty-set hint)
    - [x] Validation rules should also be filtered by config set (combo box) (`ConfigManager.tsx` RulesManager: `setFilter` combo mirroring instances, shown when >1 set; empty-set hint; `Meta.setId` surfaced via `List`)
    - [x] Validation rules can be enabled / disabled — disabled rules are skipped when an instance is created/updated/applied (`Enabled *bool`, nil=enabled for legacy; `IsEnabled()`; `rulesForSet` skips disabled; UI checkbox + list `off` badge)
    - [x] Rule editor preview button — runs the working (unsaved) source against a live signal snapshot of the set without persisting it, showing the input snapshot JSON and processed output JSON (`POST /config/rules/preview` → `previewConfigRule`; `RulePreviewView`)
  - [ ] Synthetic editor:
    - [x] color code the node link by type
      - [x] edges color depends on type (e.g. float) including arrays (scalar = slate, array = purple)
      - [x] input / outputs are color coded (out port = node's type; in port = accepted type, gray when it accepts either)
      - [x] can not connect input / output that are not compatible (definite scalar↔array mismatch blocked; unknown/any always allowed)
    - [x] hover on a block in error should show the reason
    - [x] add proper array functionality
    - [x] add shortcut to add Signals (S) and nodes (N) with HUD
  - [ ] Panels:
    - [x] in view mode the widgets should have no border/bg but blend with background
    - [ ] add widgets such as toggle switch, table and other industrial hmi widgets
      - [x] toggle switch (`web/src/widgets/Toggle.tsx`; read+write bool control, configurable on/off value+label, confirm dialog)
      - [x] table widget (`web/src/widgets/TableWidget.tsx`; multi-signal value table, one row per bound signal, configurable columns name/value/unit/status/time, optional header + title, per-signal value format + row-label overrides; blends in view mode)
      - [ ] other industrial hmi widgets
    - [x] widget panel exposes an editable Widget ID field (default = generated uuid) for easier logic interaction (`PropertiesPane.tsx` `IdInput`; `EditMode.renameWidgetId` propagates the rename to `action.widget` logic refs + plot-layout leaves; rejects empty/duplicate ids)
    - [x] add container widgets: labelled/title pane, tab panes, collapsable panes (`web/src/widgets/Container.tsx`; decorative grouping frame rendered behind widgets, accent/bg options; `pane` variant = title + view-mode collapse; `tabs` variant = tab bar where each contained widget is assigned a tab via its "Tab" field and only the active tab shows; geometric membership via `web/src/lib/containers.ts`)
      - [x] moving container widget should move the widgets on it — starting a move (drag or arrow nudge) on a container snapshots the widgets geometrically inside it (centre-inside, transitive through nested containers) and moves them by the same delta; membership is captured at move start so widgets never re-parent mid-move (`withContainedWidgets` in `web/src/lib/containers.ts`, used by `EditCanvas` drag-start + `EditMode` arrow nudge)
  - [x] plot pane:
    - [x] add toolbar (hover toolbar in `PlotWidget`, `.plot-toolbar`)
    - [x] add time window selector to toolbar (Auto/15s/30s/1m/5m/15m/1h rolling-window override, live windowed plot types)
    - [x] plot x-axis synchronised (shared uPlot cursor crosshair across all linked timeseries plots + zoom/pan range sync in historical mode; per-plot 🔗 link/unlink toolbar toggle; `web/src/lib/plotSync.ts`)
    - [x] cursors and measuraments (📏 measure mode: click cursor A then B → on-canvas A/B lines + readout panel with Δt/rate and per-signal value@A→value@B and Δ)
    - [x] pause/resume (local toolbar pause, OR'd with panel-logic pause; buffers persist across plot-type/window changes)
    - [x] save screenshot (PNG export — uPlot canvas / ECharts getDataURL)
  - [x] clean ui:
    - [x] create small statusbar where connection widget and other status related info will be placed (bottom `.statusbar`: connection chip + current panel name + history indicator)
    - [x] group advanced items (audit/control loops/config manager etc) in a tool menu or something similar to not fill the toolbar (⋯ Tools ▾ dropdown)
    - [x] make the toolbar as clean as possible (toolbar-right now: History · 📖 · zoom · Tools · Edit)
    - [x] panel-selection list: the whole row is clickable to open a panel (not just the name text); `onClick` moved to the `<li>`, action buttons `stopPropagation` (`InterfaceList.tsx`)
    - [x] panel-selection list: plot vs panel entries shown with distinguishing monochrome inline-SVG icons (line-chart = plot / control-panel = HMI; `KindIcon`, currentColor); backend `InterfaceMeta.Kind` surfaced from the XML `kind` attr, `InterfaceListItem.kind` in the frontend type (preserved through the list normalizer)
  - [x] implement proper grouping strategy, in all selector tree the options should be filtered by user (private config), group (combo to select group if user in multiple groups), global (public config). Uniform scope model: each item carries owner + scope ∈ {private,group,global} + scope-groups; empty/unknown scope = global (legacy-safe). Shared Go helper `access.CanSee` (`internal/access/scope.go`) filters list endpoints by the caller; shared frontend `web/src/lib/scope.tsx` provides `bucketOf`/`filterByScope`, the segmented `[Mine | Group ▾ | Global]` `ScopeFilter`, and the `ScopePicker` create/save visibility editor. Visibility is a selector filter, not a hard security boundary (owner always sees own items)
    - [x] panel tree by user / group / global — scope bucket derived server-side from the panel ACL (`panelScope` in `internal/api/api.go`: public→global, group grant→group, else→private; unmanaged→global), surfaced on `InterfaceListItem.scope`/`groups`; `InterfaceList.tsx` filters panels by bucket and hides folders with no in-scope descendants; visibility is edited via the existing Share dialog
    - [x] signal tree by user / group / global — synthetic `SignalDef` gained a `group` visibility mode + `Groups[]`; `synVisible` (`api.go`) routes it through `access.CanSee`; `SyntheticGraphEditor.tsx` wizard offers panel/user/group/global with group checkboxes
    - [x] config tree by user / group / global — `ConfigSet`/`ConfigInstance` carry owner+scope+groups (`internal/confmgr`), list endpoints filter via `filterConfigMetas`/`CanSee`, owner preserved across updates; `ConfigManager.tsx` adds `ScopeFilter` + `ScopePicker` to both Sets and Instances managers
    - [x] control sequence by user / group / global — control-logic `Graph` gained owner+scope+scopeGroups (`internal/controllogic/model.go`; named `scopeGroups` to avoid the existing cosmetic `Groups []NodeGroup`); `listControlLogic` filters via `CanSee`, create/update stamp/preserve owner; `ControlLogicEditor.tsx` adds `ScopeFilter` + `ScopePicker` (`filterByScope(..., g => g.scopeGroups ?? [])`)
- [ ] Node editors:
  - [x] In all editors, implement node grouping and collapsing feature (with optional group label) (Shift+click multi-select → G/⊞ to group; editable label; ▾/▸ collapse to a compact box that reroutes crossing wires; Delete ungroups; shared `web/src/lib/nodeGroups.ts`; persisted in panel-logic XML + control-logic/synthetic JSON via Go `NodeGroup` structs — cosmetic editor metadata, ignored at eval)
    - [x] opened group should be on top of other nodes (group frame lifted to z-index 1 above loose nodes; member nodes get `.flow-node-grouped` at z-index 2 so they stay above their own frame and clickable; applied in all three editors)
    - [x] node counter should be better padded (`.flow-group-count` gained vertical padding so the "N nodes" line isn't cramped against the collapsed-box header)
  - [x] In all editors: undo / redo + copy / paste with shortcuts (Ctrl+Z / Ctrl+Shift+Z / Ctrl+Y undo-redo; Ctrl+C / Ctrl+V copy-paste of the selection, multi-select aware, internal wires preserved, ids remapped, pasted +offset; ref-based 50-deep history + clipboard scoped per editor; toolbar ↩/↪ buttons). Panel `LogicEditor` already had this; added to `ControlLogicEditor` (undo/redo + copy/paste) and `SyntheticGraphEditor` (copy/paste — it already had undo/redo). Synthetic paste never copies the permanent output node
  - [x] In all editors: implement zoom in / out, home zoom and fit zoom to help user navigate complex graph (shared `web/src/lib/flowZoom.ts` `useFlowZoom` hook; CSS `transform: scale()` on a `.flow-canvas-zoom` layer inside the scaled `.flow-canvas-inner`; toolbar row −/%/＋/⤢ in all three editors; `toCanvas` divides pointer offsets by zoom)
    - [x] fix: the fit-zoom (⤢) button was clipped outside the fixed-width palette — toolbar buttons now shrink to fit (`min-width:0`, zero side padding in `.flow-palette-toolbar .toolbar-btn`)
    - [x] fix: zoom value (e.g. 100%) is overflowing, reduce padding and increase the zoom value widget width (`flow-zoom-pct` class on the zoom-value button → `flex:1.9` so it gets ~2× the width of the single-glyph icon buttons; toolbar font dropped to 0.75rem, gap to 0.25rem, `tabular-nums` to stop width jitter; all three editors)
  - [x] Live / debug mode in all three node editors — evaluate the graph online and badge each node's value at human speed (~0.5 s). Shared frontend `web/src/lib/flowDebug.ts` (badge/active classes, `useFlowDebug` poller) + CSS `.flow-node-active`/`.flow-node-badge`/`.flow-wire-active`; a green ◉ toggle in each `.flow-palette-toolbar`. Per-editor backends:
  - [ ] Syntetic signal editor:
    - [x] Implement live / debug mode where value are evaluated online and visualized at human speed (e.g. 0.5s) — server-side stateless single-shot trace: `POST /api/v1/synthetic/trace` snapshots live source signals (broker `ReadNow`) and runs `evalSampleTrace` with fresh state, returning every node's value; stateful ops (moving_average/rms/lowpass/derivative/integrate/lua) badged `~approx`
  - [ ] Logic editor:
    - [x] Implement live / debug mode where value are evaluated online and visualized at human speed (e.g. 0.5s) — editor spins up a second client-side `LogicEngine` in new `dryRun` mode (real writes/config/dialogs suppressed) loaded with the edited graph; per-node values polled into the shared badges; the view-mode singleton is untouched
    - add full suppor to local array values: dynamic, dynamic but capped max, fixed size etc:
      - array functions should work with new local array
  - [ ] Control loop:
    - [x] Implement live / debug mode where value are evaluated online and visualized at human speed (e.g. 0.5s) — server pushes per-node events over the WS (`debugSubscribe`/`debugNode`) via a new `DebugObserver` on the engine (lock-free `atomic.Value`, gated by a per-graph watch set) + `internal/server/debughub.go` (drop-on-full fan-out). Two modes share one message shape: **Live** observes the running enabled graph; **Simulate** dry-runs the unsaved edits in a throwaway sandbox (`StartSimulate`, side effects suppressed). Live/Sim sub-toggle in the editor
    - add full support to server side array values
- [x] Implement git style versioning for: synthetic variable, panels, control logic:
  - [x] possibility to fork any version
  - [x] click to view the version
  - [x] possibility to view graphical diff between versions (side by side or unified diff)
  - [x] simple slick versioning pane:
    - [x] vertical tree like
    - [x] each version represented by a circle
    - [x] active (the one currently view/edited) version has circle bigger then rest
    - [x] selected (the one that will be executed/showed by user) version has circle full, not active only border
    - [x] unsaved / new version appear with connection line dashed
- [x] Implement admin pane: create / manage groups, set users permits, manage auditors etc
  - [x] user manager
  - [x] group manager
  - [x] server statistics: load, conenctions, observed signals, average latency, other statistics
- [ ] Implement new datasources:
  - [ ] Finalize alarm service
  - [ ] modbus tcp
  - [ ] scpi tcp / VXI-11 protocol
  - [ ] udp? other?
- [ ] **MAJOR** Implement proper distributed server side nodes to balance load and have redundancy (if a node is not available anymore all its clients migrate seamelessly to another)
  - [ ] clients should be distributed to balance load\
    - same user should be (if possible) connect to only one service to simplify synch issue
  - [ ] control sequences should be executed in only in one server instance but with backups ones to take over if the active service stop or die
  - [ ] sync config between service
    - [ ] manage conflict
    - [ ] avoid incorrect
  - [ ] ensure that the system can survive with up to only one instance alive
  - [ ] advance admin panel:
    - [ ] should have info about service topology and statistics of each server
    - [ ] admin should deploy new instances via ssh to target machines directly from the admin panel
  - [ ] for phisical connect datasource (e.g. modbus) pin the service to a specific machine or deploy specific datasource only service to a machine: user can setup backup instances (e.g. machine in the same sub-network that can be switched on in case primary fail)
- [ ] QA and CI
  - [ ] coverage of service 90+%
  - [ ] coverage of client 80+%
  - [ ] add integrated tests with interface simulations
  - [ ] update doc and keep user manual up to date
    - [ ] add code example for lua scripts
    - [ ] add tutorial
