Draw the panel as a panel
Five things a wall panel showed that a wall panel should not. **A tile's body no longer clips.** It scrolled, and a box that scrolls also cuts whatever crosses its edge — which took the glow off a lit button at exactly the width where the button filled its tile, and off a gauge's arc at exactly the height where the dial filled its own. Only what is written or listed asks for a scroller now; everything else is a picture drawn to fit, and what overflows is left to the frame, which clips at the tile's edge where a shadow has already faded out. The slider's phantom scrollbar goes with it. **The selector is a selector.** Named for what it does rather than what it is, and the choice it is holding is held in the dashboard's own primary — a pill that slides between the options rather than a grey one that had to be looked for. The stored type is untouched, so no document changes meaning. **The arrangement is held off the panel's edges**, by the same distance it holds between two widgets. The ground is not held off with it: a background covers the whole panel, and only what is arranged on it has a margin. No stored panel loses a row to it. **The rail is drawn on the panel.** It was chrome bolted to the edge of the screen beside the canvas — in the app's own design rather than the dashboard's, and on a scaled canvas not even lined up with it. It now takes a column out of the canvas the way the margin does, scaled with it and wearing its look. Which cell each widget sits in is unchanged; only how big a cell is. Two of these were the same mistake twice: an unlayered rule stating `position` for everything wearing a class — `.gl-surface` on a rail placed by a utility, and a blanket lift over every child of a pressable on a pill placed by `layoutId`. Both now say it one element at a time.
This commit is contained in:
+13
-68
@@ -12,8 +12,11 @@ Deferring because out of scope is fine, but don't mention deferring than.
|
||||
|
||||
### To be sorted
|
||||
|
||||
- FEAT/UI: we promise testing, but currently don't provide an UI for testing e.g. mock values or probing edge cases of a flow. This should be resolved (in a dedicated session); I'm thinking of a "Labs" page, which allows simulating an installation with all the flows (using their draft states) and which allows injecting values or mocking values based on events in the past
|
||||
- FEAT/UI: check if PWA (https://whatpwacando.today/) notifications could be used to have a panel sending notifications to the device event bus (or generally using PWA to retrieve e.g. location etc). We could introduce a general concept of having a panel (a device, like a wall panel or a phone where the pwa (dashboard) runs) being effectively a node with various outputs. Then various inputs could trigger actions like authentification (i.e. you get home and get a notification which allows you to authenticate the door unlock), get notified on alarms (native alarm connector) or to query geolocation (check where the user is before turning of all lights) etc
|
||||
- FEAT/UI when a dashboard has more widgets than the canvas allows for, the canvas should become scrollable (temporarily) to allow scrolling to the widgets outside the canvas and reordering them
|
||||
- BUG/UI some nodes (especially smaller ones) in the brain graph still don't have a pixel perfect centered ring and node background
|
||||
- FEAT/UI: make the "Settings" button in the dashboard panel a toggle; such that it becomes stateful and pressing again closes the settings panel
|
||||
- FEAT/UI: we promise testing, but currently don't provide an UI for testing e.g. mock values or probing edge cases of a flow. I'm thinking of a "Labs" page, which allows simulating an installation with all the flows (using their draft states) and which allows injecting values or mocking values based on events in the past. This should then use the draft version of flows / dashboards. For the flows we could make a tab-style view and re-embedd the flow canvas. For the dashboards we could reuse the icon rail
|
||||
- FEAT/UI: check if PWA (https://whatpwacando.today/) notifications could be used to have a panel sending notifications to the device event bus (or generally using PWA to retrieve e.g. location etc). We could introduce a general concept of having a panel (a device, like a wall panel or a phone where the pwa (dashboard) runs) being effectively a node with various outputs. Then various inputs could trigger actions like authentification (i.e. you get home and get a notification which allows you to authenticate the door unlock), get notified on alarms (native alarm connector) or to query geolocation (check where the user is before turning of all lights) etc. Another example would be a wall panel with speech in- and output; we could use pwa as an interface here for any tts/stt instance managed through fluksio
|
||||
- BUG/UI sync the theme state between panels and installations
|
||||
- CHORE/UI: a dashboard forced to one theme inside a shell on the other still
|
||||
matches `dark:` utilities, because the variant is `&:is(.dark *)` and the
|
||||
@@ -32,6 +35,10 @@ Deferring because out of scope is fine, but don't mention deferring than.
|
||||
(`ui/segmented.tsx`, `Common/RangePicker.tsx`, and the flow screens). The
|
||||
dashboard no longer shares them — it has its own, one per look — so
|
||||
unifying the remaining three is now purely an app-side job.
|
||||
- CHORE/UI: a panel's rail runs the full height of the canvas whatever it
|
||||
carries, so two dashboards leave most of a tall pill empty. Hugging its
|
||||
contents and centring would read better; the column it reserves stays the
|
||||
same either way.
|
||||
- CHORE/UI: the house panels are laid out for 1280x800 — twelve columns, twelve
|
||||
rows. A chart's fixed chrome is now its legend alone: the range picker moved
|
||||
to a column down the right-hand edge and gave its row back, so a third chart
|
||||
@@ -209,30 +216,16 @@ Deferring because out of scope is fine, but don't mention deferring than.
|
||||
- FEAT/UI (deferred until MCP lands): add a "bot" icon button to the home view (graph panel) which opens a chat window (reuse general concept of a side panel like in flows/nodes to make it a chat panel which can open on any screen (stacks below any other existing panel -> introduce stacking) to give support on errors/write code, generate dashboards etc) to explain the error(s)
|
||||
- FEAT/UI make the header (Fluksio - YEAR) and the logo in the sidebar link to the main page (fluksio.com)
|
||||
- FEAT/UI consider adding a diagram to the Home view which shows a histogram of the different classes of nodes and which time it takes to execute (logarithmic scale); this should give a hint on the load and help to detect bottle necks/hotspots
|
||||
- CHORE/API: revoking an OAuth client does not invalidate access tokens already issued; they are stateless JWTs valid up to `MCP_TOKEN_EXPIRE_MINUTES`. Immediate revocation means `app/mcp/http.py` checking the client row still exists.
|
||||
- CHORE/FLOW: `Pipeline.trigger`'s docstring says a paused flow still publishes so the value shows on the canvas. True only without a queue; with one the item parks before `apply_outputs` and nothing shows. Docstring and behaviour disagree.
|
||||
|
||||
### Persistence and databases
|
||||
|
||||
From the 2026-08 database review. Verdict recorded under Deferred: the
|
||||
Postgres + Redis + git-files split stays; the actionable part is durability.
|
||||
|
||||
- CHORE/INFRA: Redis AOF runs at `appendfsync everysec`, so up to ~1 s of journaled work-queue entries can vanish on a crash — softer than "journaled before it runs" reads. Queue write volume is low, so `appendfsync always` is likely affordable; otherwise document the loss window.
|
||||
- CHORE/INFRA: Redis has no auth (`requirepass` unset). Fine on the compose-internal network; a blocker for M5 remote workers, which turn Redis into a network-exposed shared bus.
|
||||
|
||||
### Connector write paths
|
||||
|
||||
Needs someone watching the real hardware, so it is not a background task. This
|
||||
is what M4 still waits on, together with porting the flows.
|
||||
|
||||
Art-Net can write now: `ConnectorNode.write` carries a node's input ports, a
|
||||
per-port `channels` map places each on its own DMX channel, and `transmit`
|
||||
still gates the socket. Verified on the wire against a listener (channel 33 =
|
||||
255, channel 31 = 60, nothing else set) and the MQTT half was driven end to
|
||||
end against the house broker. The rig was the `house_control` flow and its
|
||||
dashboard; it has been deleted now that `dmx` owns the universe, and
|
||||
`make -C app seed-house` rebuilds it if that verification is ever wanted
|
||||
again.
|
||||
|
||||
- FEAT/NODE: Art-Net against the real fixtures is still untried. The house's own dmxnet sender re-emits universe 1 every 1000 ms, so fluksio and Node-RED overwrite each other; the test needs Node-RED's Art-Net sender stopped, and while it is stopped every channel fluksio does not set is dark.
|
||||
- CHORE/NODE: the operatorId worry was unfounded — the reference Node-RED `setstat` node for this unit is configured with an empty operatorId and deviceId, so a command needs no registration. The second unit may still differ.
|
||||
- PERF/NODE: a `wfrac` command is two round trips (read, then set) on the scheduler's thread, so at the default timeout a command can hold a cascade for several seconds. Fine for a person pressing a button; a flow commanding it on a schedule would want the work off that thread.
|
||||
- CHORE/NODE: `wfrac` writes carry the unit's whole state, so two flows commanding one unit will each undo whatever the other set between their read and their write. One writer per unit, the same rule Art-Net has for a universe.
|
||||
@@ -243,26 +236,11 @@ again.
|
||||
|
||||
### Porting the Node-RED flows
|
||||
|
||||
Built: `scripts/tinyhouse/`, `make -C app seed-tinyhouse`. Nineteen flows, 109
|
||||
nodes, three dashboards, against the reference's 865. What each requirement
|
||||
became, what was deliberately dropped, and the cutover order are in
|
||||
`docs/private/node-red-transition.md`. What is left here is what the seeding
|
||||
does not settle.
|
||||
|
||||
- CHORE/FLOW: the DMX channel collisions were decided rather than ported — ch 9
|
||||
is one bathroom fixture, and `light/traverseAmbientLight` (28-30, colliding
|
||||
with the window opener's 28-29) is left out on the assumption that it is
|
||||
decommissioned, since the live scene engine never drove it. The stray 1CH
|
||||
mapping on 129 beside the awning's 129-130 is dropped. **Check the wiring
|
||||
before `dmx` transmits.**
|
||||
- CHORE/FLOW: 1CH values are still used raw, so those fixtures still never go
|
||||
above 100/255, and the 4CH master channel still takes `v` on a different
|
||||
scale from its colour channels. Both are `scale` and `master_raw` settings on
|
||||
the encoders now, so fixing one is a decision about one fixture rather than a
|
||||
surprise across all of them.
|
||||
- CHORE/FLOW: `artnet.baseline` in `house.json` is empty. A frame carries the
|
||||
whole universe, so the first one this node sends darkens every channel it is
|
||||
not driving. Fill it in from what the fixtures are at, before transmitting.
|
||||
- FEAT/FLOW: the deferred half of the port — the media plug and the radio at
|
||||
the media plug's address, the alarm clock, audio through `thgui/display/audio`, and the
|
||||
fire alarm. The alarm's hook exists: every arbiter takes a `force_at` pulse,
|
||||
@@ -280,10 +258,7 @@ does not settle.
|
||||
nodes speak to the house broker here. Either a shared client registry or a
|
||||
correction to the sentence.
|
||||
|
||||
### Bugs found while building the screens
|
||||
|
||||
- CHORE/API: revoking an OAuth client does not invalidate access tokens already issued; they are stateless JWTs valid up to `MCP_TOKEN_EXPIRE_MINUTES`. Immediate revocation means `app/mcp/http.py` checking the client row still exists.
|
||||
- CHORE/FLOW: `Pipeline.trigger`'s docstring says a paused flow still publishes so the value shows on the canvas. True only without a queue; with one the item parks before `apply_outputs` and nothing shows. Docstring and behaviour disagree.
|
||||
|
||||
### Out-of-process nodes and modules
|
||||
|
||||
@@ -307,32 +282,7 @@ does not settle.
|
||||
- PERF/API: `queue.stats()` does a keyspace `scan_iter` on every call while two endpoints poll it.
|
||||
- CHORE/INFRA: dev only — memory-queue ids (`mem-{seq}`) restart at 0 each boot and `FlowRun.id` is the primary key, so a restart without Redis upserts over the previous boot's run rows.
|
||||
|
||||
### Wall-panel parity with the current home dashboard
|
||||
|
||||
What a fluksio dashboard still lacks to replace `geli-dash` (Dash/Plotly, e-ink
|
||||
panel: clock and nav chrome, indoor climate, weather forecast strip, calendar
|
||||
agenda, room light groups, sliders, power/battery bars, and three pages of
|
||||
InfluxDB time series). Component-level only; the arrangement and the styling are
|
||||
this design system's business, not that one's.
|
||||
|
||||
Decisions taken up front, because most items below depend on them:
|
||||
|
||||
- Structured data reaches a widget as a *declared shape*, not as opaque JSON with
|
||||
a path per binding. A path would leave the picker with nothing to offer and
|
||||
`widgetIssue` unable to judge a tile from the document alone.
|
||||
- BUG/UI double check that this aligns with the new data-science pipeline feature
|
||||
- A chart asks a flow for its series the way every other input widget speaks:
|
||||
it publishes a request message and reads the answer. No query API, no
|
||||
database knowledge in the widget.
|
||||
- Database nodes are transport and credentials only. The InfluxDB node runs the
|
||||
Flux it is handed and echoes back every other field of the request; building
|
||||
the query and shaping the answer are Python nodes either side of it. That is
|
||||
what keeps a widget ignorant of the database, and it is also what a series
|
||||
read mode inside the node would have prevented. A "grouped nodes" concept
|
||||
could later package the standard chart→build→db→parse→chart quintet so a
|
||||
dashboard is not five nodes of wiring each time.
|
||||
- Nothing e-ink-specific in the widgets. Panel access is a credential problem
|
||||
(see below); the display's demands are a rendering profile, deferred.
|
||||
### Wall-panel
|
||||
|
||||
- CHORE/UI: identical in-flight chart requests are deduplicated per browser tab,
|
||||
so two wall panels showing the same tile still run the query twice. An
|
||||
@@ -348,12 +298,7 @@ Decisions taken up front, because most items below depend on them:
|
||||
answered by flow inputs. What is left is the naming: an input a panel writes
|
||||
looks the same as one a run passes in.
|
||||
|
||||
Deliberately not ported: the local-state/timestamp reconciliation the old
|
||||
dashboard does per widget — publishing on release and reading the value back
|
||||
covers it — and its demo mode, since an unbound or silent message already renders
|
||||
as an em dash.
|
||||
|
||||
### Dashboard follow-ups
|
||||
### Dashboard
|
||||
|
||||
- FEAT/NODE: the hosted demo places six of the fifteen built-in node types (`python`, `inject`, `change`, `join`, `rbe`, `trigger`); it does cover all fifteen dashboard widget types. `switch` and `delay` are the awkward ones — a `switch` branch needs either a dead-end port or trivial nodes to turn a branch back into a label, and neither reads as something a person would hang — while the I/O types (`mqtt`, `http`, `influxdb`, `exec`, `file`, `ntfy`, `mlp`) are unplaced because the demo has nothing real to talk to. Worth revisiting when the demo grows a second page.
|
||||
- CHORE/UI: multi-page and multi-section dashboards have no UI and need none — a panel carries
|
||||
|
||||
Reference in New Issue
Block a user