From 3a2030634251081103ecbb0ad661f9e7457a884b Mon Sep 17 00:00:00 2001 From: stroblme Date: Sat, 22 Aug 2026 12:05:08 +0200 Subject: [PATCH] NOTEPAD: clear what wave 1 closed, record what it left --- NOTEPAD.md | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/NOTEPAD.md b/NOTEPAD.md index 9440109..e359e0b 100644 --- a/NOTEPAD.md +++ b/NOTEPAD.md @@ -12,8 +12,29 @@ Deferring because out of scope is fine, but don't mention deferring than. ### To be sorted +- CHORE/UI: no affordance for `POST /panels/{id}/unpair`. The panels dialog can pair a + device but not re-pair one; `PanelsService.unpairPanel({ panelId })` is generated and + the docs already describe it. Invalidate `panelKeys.all` after. +- CHORE/UI: the transmit pulse and the editor's selection ring are both a 2px inset + `--primary` ring, so a control selected in edit mode and one mid-publish look the same. + Only visible while editing. +- CHORE/UI: slider tick labels are laid out by percentage without measuring, so five + labels of four characters can crowd on a tile narrower than its default four columns. + A width-aware count needs a `ResizeObserver`. +- FEAT/API: neither `FlowSummary` nor `DashboardSummary` carries a modified time, so + Home's "recently modified" order is a proxy — drafts first, then version counter, then + name (`byRecency`, `Common/DashboardMosaic.tsx`). The store is git-backed, so an + `updated_at` on both summaries would make it exact. +- PERF/UI: the home mosaic reads each dashboard's document for its footprint, capped at + eight. An installation with dozens shows name-only tiles past that; a placement digest + on `DashboardSummary` is the fix. +- PERF/API: `_panel_may` re-reads `panels.json` and every published dashboard document of + the panel on each request a screen makes, to resolve the message allowlist. Marked + `# ponytail:` in `api/deps.py`; cache behind the dashboard store's version if it shows + up in a profile. +- CHORE/TEST: the frontend has no unit-test runner (Playwright only), so pure helpers like + the slider's `tickIntervals` have nowhere to be checked cheaply. - FEAT/UX add a color-wheel widget to the dashboard to control the color of e.g. RGB leds; The color picker should be a custom component that integrates smoothly with the existing UI and follow a functional and clean design usable for touch wall panels -- BUG/UI when adjusting e.g. a slider value (drag), upon release the slider briefly jumps back to the previous position. We should fix that such that the slider stays at the dragged position. If sth. goes wrong with the transmit; we should show a notification. Check for all other widgets where this would apply as well. Make sure that this aligns with the philosophy of the app; clearly indicating if a transmit failed/is in progress/succeeded (UX options needed) - FEAT/UI when an installation is added to the hub, the dialog which shows the access code should disappear automatically - BUG/UI the "Connect" button in "Remote Access" when adding an installtion to the hub is invisible and only shows upon hovering (could also be a local browser issue) - CHORE/PKG the SPA is not in the wheel: `fluksio serve` serves no UI, on the @@ -27,10 +48,8 @@ Deferring because out of scope is fine, but don't mention deferring than. - CHORE/DEPS `sentry-sdk` went to 2.x and the `requires-python` cap came off with it. Nothing exercises Python 3.13/3.14 in CI — the matrix is one version. -- FEAT/UI add a list of the dashboards next to the list of flows in the home view. Use a mosaic like structure with previews of the dashboards; Then make the list of flows capped at a certain number; everything above that should be scrollable; sort by recently modified. The dashboard mosaic should have the exact same height as the flow list both capped at a lower limit to allow showing 1 flow and 1 dashboard - BUG/UI assimilate the design of the settings in the app to mirror the design of the settings in the portal -- BUG/UI: `CubeLoader` (`frontend/src/components/ui/cube-loader.tsx`) paints nothing. Its `` carries `className="n3xd-cube-line"` and the header comment points at that rule plus `n3xd-build-cube` keyframes "in index.css" — neither exists in `frontend/src/index.css` (nor in the index repo, which has the same component). Every pending state using it shows an invisible SVG. Related to the global loading animation below. -- BUG/UI the "Installation Offline" warning (and notification in general) should be centered w.r.t. the viewport (currently it is a bit left, discarding the width of the sidebar). Furthermore, the notification does not seem to disappear on its own. Reloading the page solves it +- BUG/UI: `CubeLoader` (`index/frontend/src/components/ui/cube-loader.tsx`; the index repo only, nothing in `app/` references it) paints nothing. Its `` carries `className="n3xd-cube-line"` and the header comment points at that rule plus `n3xd-build-cube` keyframes "in index.css" — neither exists in `frontend/src/index.css` (nor in the index repo, which has the same component). Every pending state using it shows an invisible SVG. Related to the global loading animation below. - CHORE/INFRA: the `generate-frontend-sdk` pre-commit hook runs `scripts/generate-client.sh` on any `backend/**` change, and that script ends by formatting the whole frontend tree — while `biome.json` excludes `src/client`, so it formats nothing the generator wrote. Every @@ -51,7 +70,6 @@ Deferring because out of scope is fine, but don't mention deferring than. - FEAT/UX add an option to the settings of an installation to configure automatic updates. If enabled, the installation would send a request e.g. every 1h to the hub at fluksio.com and the hub then checks if a new version is available. The settings should include a second toggle for automatically installing an update (which might cause a short outage). Later this mechanism should be extended to check if updating would cause things to break. - FEAT/UI allow setting icons for multi-page dashboard (when configuring a panel, we could simply add an icon picker there) - BUG/UI hovering the sidebar where we can switch dashboards on a multi-page dashboard shows a horizontal scrollbar. We should remove that; no scrollbars at all should be shown in this type of sidebar -- BUG/UI the "Installation Offline" warning (and notification in general) should be centered w.r.t. the viewport (currently it is a bit left, discarding the width of the sidebar). Furthermore, the notification does not seem to disappear on its own. Reloading the page solves it - CHORE/UI: `.u-legend` is styled in the dashboard's own CSS chunk, so a uPlot legend on a Health or Home page renders unstyled until a dashboard has been visited in that session. `RangePicker` already side-effect-imports `dashboard.css` for the segmented thumb; the @@ -74,7 +92,6 @@ Deferring because out of scope is fine, but don't mention deferring than. `make test-backend` then fails at the coverage HTML step *after* every test has passed — which reads like a test failure and is not one. (tests/flows.spec.ts, tests/admin.spec.ts)". There are nine. -- FEAT/UI add (multi-)select to the flows and dashboards view to allow deleting (multiple) items; long press to select -> "Add" button should change into "Trash" icon button - BUG/UI in the brain view: make the chasing circle animation running entirely in the gap between the ring and the node (using the full width) - BUG when clicking "edit" in the "Home" dashboard of the demo on hub.fluksio.com, most of the panels disappear (only a handfull is left for actual edit) - CHORE/UI: the edge popover shows the same value twice — `MessageSparkline` falls through to a collapsed `ValuePreview` for a non-numeric value, and `EdgeInspector` then renders its own `ValuePreview defaultOpen` below it. Cosmetic; one of the two is redundant. @@ -86,7 +103,6 @@ Deferring because out of scope is fine, but don't mention deferring than. - BUG/UI auto node placement on flows should be improved in regards to least crossing edges and a more vertical layout on mobile devices - INFRA: ensure that all the packages/ dependencies needed to run fluksio are available on arm to make this software runnable on e.g. raspbian - INFRA: merge the philosophy statement at the beginning of vision.md into the rest of the document. Dissolve the decision dates and fold the decisions into a clean structure -- FEAT/UI add a loading animation for the initial app load and when loading individual pages; make sure that elements e.g. in the home dashboard load independently to ensure a fast loading of the initial site but figures charts, tables, graph etc. follow after that - FEAT/UI introduce a graph panel which renders at the top right next to the graph view (to make more use of the horizontal space) and which allows (de-) selecting flows to be excluded from the graph view or search for individual nodes where only the flows containing this node should be shown (like slicing the brain) - FEAT/UI labels in flows (indicating dashboard widget connections) naturally can't pulse. Instead add an animation (enlightning fade) from either ltr or rtl depending if the label is in- or outbound - CHORE/UI: `layoutGraph` treats every node as 220×56 rather than measuring, because feeding a measurement back into the layout oscillates. A node wider than that crowds its neighbours; take the sizes from `node.measured` once they have settled if it shows. @@ -201,8 +217,6 @@ Decisions taken up front, because most items below depend on them: - CHORE/FLOW: one request/answer pair per InfluxDB node — the first input carrying a `flux` key is the request and the answer leaves on the first output port. A second query stream through one node needs a second node. -- FEAT/UI: the slider offers `step` now, but no tick labels — the `datalist` - marks are unlabelled and drop out past fifty steps. - FEAT/UI: per-dashboard theme — forced light, forced dark, or switched on a schedule. View mode inherits localStorage and the OS preference today, which a panel in a room has no way to set. NOTE: to solve this, we could introduce a general message sending to the overall dashboard (so far we only treat widgets in a dashboard as a receiver). We could e.g. have a toggle in the dashboard settings which says "propagate theme" which enables a field for defining a consume input (identical to a standard node input) and then a node can connect to this property by producing a corresponding message. This would nicely generalize to other dashboard settings later. This could later also serve as a security mechanism, i.e. the possibility to lock down dashboards remotely @@ -223,12 +237,9 @@ as an em dash. - 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: `ROW_HEIGHT` is a fixed 80px while column width follows the canvas, so a 1920-wide panel at 12 columns has 160×80 cells. If that reads too wide, the row height could derive from the canvas too. - CHORE/UI: multi-page and multi-section dashboards still have no UI, and now need none — a panel carries several whole dashboards instead, each with its own canvas and its own publish. `PageDef`/`SectionDef` stay in the schema and the editor still edits `sectionsOf(page)[0]`, so the page `Tabs` in `DashboardEditor` are dead until something writes a second page through the API. -- CHORE/API: a panel credential may publish *any* message, not only the ones its own widgets bind to — the allowlist is the `/messages/` prefix rather than a walk of the panel's widgets. The walk now exists: `panels.messages_for()` is what bounds the live socket. Pointing `_panel_may` at it would close this too, but it tightens what already-paired screens may do, so it wants a deliberate look at the query-chart request path first. -- CHORE/API: unpairing a device means deleting the panel. A per-panel nonce in the token, bumped on demand, would let one screen be re-paired without disturbing the assignment. -- CHORE/API: a panel paired through the portal is revoked here the moment the panel is deleted — `_panel_may` finds nothing and answers 401 — but the hub's copy of the token stays valid until it expires or the installation's generation counter is bumped ("New code"). The hub has no per-panel revocation, and giving it one means telling it which panels exist, which is exactly what this design avoids. The generation bump is the lever; it is blunt, cutting every credential the portal minted for the installation. +- CHORE/API: a panel paired through the portal is revoked here the moment the panel is deleted — `_panel_may` finds nothing and answers 401 — but the hub's copy of the token stays valid until it expires or the installation's generation counter is bumped ("New code"). The hub has no per-panel revocation, and giving it one means telling it which panels exist, which is exactly what this design avoids. The generation bump is the lever; it is blunt, cutting every credential the portal minted for the installation. The per-panel nonce does not reach it either: `pnc` is only checked on a token this installation signed. - CHORE/UI: the device line under a pairing code is the raw user agent plus the address the request came from. Both are self-reported and neither is proof; it is there so an admin can tell the screen they just hung from one they were not expecting, not to authenticate anything. - CHORE/API: `POST /panels/pair` is reachable from the internet once an installation is enrolled — the hub forwards it without a session, since a device with no credential is the point of it. Bounded three ways (the hub's per-installation and per-address limits, and the fifty-code cap here), but it is the first unauthenticated surface this installation exposes outward. -- CHORE/API: `POST /panels/pair` is unauthenticated and capped at fifty pending codes in one process. A second API worker would each keep their own dictionary, so pairing would work only when the poll lands on the process that minted the code. The same holds for a screen pairing through the portal, which lands on whichever worker holds the tunnel. - CHORE/UI: only `layout.lg` is ever written, and `md`/`sm` stay unwritten by decision — a phone stacks the widgets (`.widget-stacked`) rather than carrying an arrangement of its own, since arranging is not a phone feature. The keys stay in the schema for a panel that one day wants a second size. - PERF/UI: `ChartWidget`'s cost per live value is the `uPlot.join` in `UplotChart`, not the tail append — the fetched half comes from React Query and is replaced wholesale on every refetch, so a ring buffer over the live tail would leave the dominant cost untouched. If this is ever profiled and fixed, the `setData` effect must stay dependency-free: a mutable buffer's identity never changes, so keying the effect on it reintroduces the staleness that the point-count dependency used to cause, and more quietly. - CHORE/UI: opening edit mode on a dashboard whose widgets predate placement writes the migrated positions immediately, bumping the version once.