Commit Graph
719 Commits
Author SHA1 Message Date
stroblme ef1eb7db8b serve names the dashboard's address when the image serves it
The banner said nothing was served whenever a portal was configured,
which stopped being true once the image carries the dashboard.
2026-10-16 15:18:11 +02:00
stroblme 3c0912a3fe Docs: x86 and arm64, Raspberry Pi included; one container in the start pages 2026-10-16 14:56:08 +02:00
stroblme 84be4084f8 Dashboard stage builds on the build machine's CPU
Its output is the same on every platform. Under emulation for an arm64
image this stage alone took 31 of 33 minutes, now about 2.
2026-10-16 14:15:05 +02:00
stroblme 026625ec4b Clipboard and values specs wait out the side panel's animation
The clipboard spec selected text in the previous node's panel while it was
still sliding out after the paste, so the selection collapsed when it
unmounted and the canvas took the copy. The values spec read the panel's box
mid-slide-in, where the translate leaves the width a float off 400.
2026-10-16 14:12:10 +02:00
stroblme dde3d0af0d Portal app-shell as a busybox target, git without perl, quieter first start
The portal's copy of the dashboard is the `appshell` target of
backend/Dockerfile (18 MB instead of the 252 MB nginx image); the nginx
frontend Dockerfile, its confs and csp.check.ts are gone, since the app and
the hub both hash the CSP from the index.html they serve.

The runtime image copies the 4 MB git binary alone: the store's init, add,
commit, gc and rev-parse are builtins, and a build-time round trip fails the
build if that ever changes. About 100 MB of perl and helpers less.

The bundled Redis's overcommit warning and FastMCP's lifespan warning no
longer greet every `docker logs`.
2026-10-16 12:20:13 +02:00
stroblme dea25ec08d One image for the whole app: engine, API and dashboard on one port
The backend image now builds the SPA and serves it on the API's own origin
(fluksio/spa.py), so an instance is a single container with no proxy, DNS,
port 80 or .env: `docker compose up -d` with the new compose.yml runs it on
localhost:8080. It starts on fluksio serve (persisted secret key, first
admin printed once) and brings its own Redis when REDIS_HOST is unset.

Slimmer: python:3.13-slim, no dev group, uv once, fastapi without the cloud
CLI. Optional features are per-feature extras (mqtt, influx, email, sentry,
ml, server for all) picked with the EXTRAS build argument; numpy moved to
`ml`. ffmpeg left the image for imageio-ffmpeg via the Modules screen.

The operator stack runs the same image with app. and api. routed to it; the
nginx frontend service, the LAN nginx proxy and DOMAIN in the backend are
gone, and the MCP issuer is FRONTEND_HOST.
2026-10-16 11:40:02 +02:00
stroblme 9d9dac0bd0 Chat panel follows its tail by scrolling its own body (SidePanel body gets data-slot side-panel-body) instead of scrollIntoView, which scrolls every ancestor 2026-10-16 10:50:23 +02:00
stroblme cb377ba00e Logs panel follows its tail by scrolling its own list: scrollIntoView scrolled every ancestor too, so with the code editor expanded each new line slid the whole editor up to bring the hidden dock into view 2026-10-16 10:39:09 +02:00
stroblme d06804b145 Panels dialog: opens without focusing a field, a panel title saves on blur, Enter or Escape instead of per keystroke, and a new panel's name previews its URL. Docs 2026-10-16 09:25:34 +02:00
stroblme 07ecbaab3a Duplicate flow: references to its own messages under its own name follow the copy 2026-10-16 09:22:57 +02:00
stroblme c37a191c82 Overview lists: a duplicate button beside the trash while selecting copies the picked flows or dashboards as drafts, one request each, then clears the selection. Publish, delete and duplicate share one batch helper with their own toasts. Docs 2026-10-16 09:21:42 +02:00
stroblme 6ab5062f59 Duplicate flows and dashboards: POST /flows/{name}/duplicate and /dashboards/{name}/duplicate copy the working copy into {name}_copy (then _copy_2, ...) as an unpublished draft with " (copy)" on the title. A flow copy takes its node sources as drafts and leaves origin and runtime.json behind; copying code nodes is a superuser's (the save check, now taking the replaced copy). Client regenerated 2026-10-16 09:21:30 +02:00
stroblme 85539741a1 Dashboard editor: copy and paste widgets across dashboards
Copy widget in the widget panel or mod+c keeps one widget in localStorage;
a paste button in the dock and mod+v insert it below the other widgets with
a fresh id. Placement is shared with adding a widget.
2026-10-16 09:20:23 +02:00
Melvin Strobl bfab6e1d2e Counter wrap stays on the step grid: it counts in span = top - lo + the finest step rather than the period, so one counter of step 5 up to 100 wraps 100 + 5 to 0 and 0 - 5 to 100; the time picker is unchanged. Check cases and docs formula 2026-10-16 09:00:36 +02:00
Melvin Strobl 21c7961d49 Counter widget: one to three linked counters are the digits of one number, published on one message in the finest counter's unit (hours and minutes send 07:30 as 450, 07:55 + 5 min carries to 08:00); wraps or stops at the coarsest's min/max. The arithmetic is one pure module, ui/core/counter.ts, with counter.check.ts. A tile steps each counter with a press up and down and publishes once 300 ms after the last; a round face turns the selected counter on the ring (wheel, keys, a drag counted in steps), its arc where that counter stands in its turn, and a press on a counter's digits selects it. Default config is the time picker. Editor rows, widgetIssue, server type and dtype, docs 2026-10-16 08:58:42 +02:00
Melvin Strobl 4ce50b5c3a LAN stack gives out its LAN address: dev-lan sets FRONTEND_HOST to the published port on the host's default-route address, so the Panels dialog links a screen somewhere it can reach instead of app.localhost 2026-10-15 22:22:58 +02:00
stroblme 244a43ac8c Merge remote-tracking branch 'origin/main' 2026-10-15 22:05:01 +02:00
stroblme baa73bb3a3 no-em-dash pre-commit hook for user-facing strings, existing em-dashes rewritten
frontend/scripts/no-em-dash.ts walks src/ with the TypeScript parser and flags
an em-dash in JSX text, string literals and template text. A bare "—"
placeholder passes, separators go in no-em-dash.allow.json. The script is
identical to index's copy.
2026-10-15 22:03:36 +02:00
stroblme 8d3165c8c4 A lane chip clears the card above its run: running downwards ELK keeps runs half a chip and a border (14px) from nodes, so House history's feedback chip no longer covers the node border at 390 (crossings and node order unchanged, Demo house 178px taller); an empty string reads "" on its chip 2026-10-15 21:46:05 +02:00
stroblme dc2802d4c0 ELK runs in a worker: the flow layout loads elkjs's worker build as a Vite ?worker behind elk-api, so a phone's eight layout passes no longer block the page (Demo house at 390px with 4x CPU throttling, long tasks 15.0 to 0.8 s, longest 2.1 to 0.3 s); positions and routes unchanged 2026-10-15 21:37:31 +02:00
stroblme ce2ce4e3e7 Header removals wait for the code editor to be left: autosave saves with drop=false, so follow_header adds and renames from the header but keeps a port or setting whose parameter is gone, and leaving the editor (blur or closing the panel) saves once more with removals; source saves of a flow run one at a time 2026-10-15 21:31:04 +02:00
Melvin Strobl f600f46924 Merge remote-tracking branch 'origin/main' 2026-10-15 21:17:33 +02:00
stroblme 5ca90ffece A phone's wrapped flow stacks into one column: when a graph is still wider than three nodes after wrapping, a last pass centres every rank, so wrapped rows sit under the row above (Demo house 3721 to 947px across at 390, crossings 295 to 263) 2026-10-15 18:32:44 +02:00
stroblme 453b0be6f6 Edges follow ELK's routes: layoutGraph returns each edge's start, bends, end and chip point, LiveEdge draws them square with rounded corners, moved onto the drawn handles, and keeps the curve for an edge whose node moved since; a feedback edge clears the nodes it passes 2026-10-15 18:24:38 +02:00
stroblme ff17d08254 useIsMobile reads the width on the first render, so a phone's flow nodes no longer draw desktop handles first and React Flow cannot measure those: the Music and House history edges stop changing between loads 2026-10-15 18:23:40 +02:00
stroblme 80e350549c A dashboard's portalled select menu restates its look, so a Glass menu blurs what is under it (the colour wheel no longer shows through) and a Material menu gets its tonal fill back 2026-10-15 18:19:41 +02:00
stroblme bf7086250e A round face's press is not clipped by the face body, so Glass light's disc shadow falls soft instead of ending in hard edges 2026-10-15 18:19:34 +02:00
stroblme abfb03c441 Header of process and node ports follow each other both ways: a panel edit rewrites only the def line of edited code, a code save reads the header back into input ports and settings (draft and response); Space types in the node editor again 2026-10-15 18:19:19 +02:00
stroblme 87ee7e3429 Brain force layout runs in a worker: 300 ticks took ~75 ms cold, ~30 ms warm on the Demo's render thread 2026-10-15 17:48:52 +02:00
stroblme 44c665b3a5 Namespace the subtree theme scopes as .theme-light/.theme-dark: dark: follows the nearest scope, portalled dashboard menus and tooltips restate it 2026-10-15 17:42:59 +02:00
stroblme 1b5a16f636 Assistant streams its answer token by token
ai/client.py streams the completion and puts the chunks back together
(Assembler), covering OpenAI's piecewise tool-call arguments, Ollama's whole
calls, OpenRouter's comment lines, unnumbered calls and in-stream errors.
The turn emits a delta event per piece and a message per round as before,
and the chat grows the last answer as deltas arrive.
2026-10-15 17:37:56 +02:00
stroblme 29156fcaec Vendor stylesheets load in Tailwind's base layer through a vendor.css shim, so our overrides win outright; the editor's drag placeholder takes the 12% primary 2026-10-15 17:27:17 +02:00
stroblme 8f8330c9e0 Assistant answers render as markdown
react-markdown, lazy-loaded with the first answer (37 kB gzip chunk, main
bundle unchanged). No raw HTML, images dropped, links open in a new tab.
The system prompt asks for lists, inline code and fenced code blocks.
2026-10-15 17:18:12 +02:00
stroblme 37f4ac597d Live chart lets a held window go once its points have scrolled off 2026-10-15 17:12:39 +02:00
stroblme 87f5b6b58a One elapsed-time notation: ago() reads dur's units app-wide, health runs cell widened 2026-10-15 17:10:36 +02:00
stroblme 6a847a2f28 development.md: only the CI jobs other than test-backend write .env 2026-10-15 17:08:09 +02:00
stroblme 424af471d4 bench-engine in the Makefile's .PHONY 2026-10-15 17:07:39 +02:00
stroblme 0a1b38a89b LAN nginx proxies to the container name fluksio-api, which the test stack's backend does not carry 2026-10-15 17:07:22 +02:00
stroblme 4b216dcbcf Modules resolve the manifest with uv pip compile before syncing, so listed packages bring their dependencies 2026-10-15 17:06:20 +02:00
stroblme d3b52bd7d6 Docs: speech-to-text and text-to-speech recipes for a panel's mic and speaker
faster-whisper and piper as plain Function nodes, the mic -> STT -> logic ->
TTS -> speaker wiring, the Modules manifest they need (uv pip compile, av<19)
and measured sizes, memory and CPU times. Modules docs now say uv pip sync
installs no dependencies.
2026-10-15 17:01:25 +02:00
stroblme 56fa34c2c9 Home charts execution time per node type on a log scale, from a /observability/node-types rollup 2026-10-15 16:57:07 +02:00
stroblme fa8b178866 Panels dialog saves a ready I/O port draft on close and keeps an unfinished one; one qualified message-name rule on both sides 2026-10-15 16:49:26 +02:00
stroblme 9836f3ff5f Clicking a flow input label opens a popover with its declaration and current value, and a way into the flow settings 2026-10-15 16:42:28 +02:00
stroblme 6d0fd53d08 Following a flow: endpoint lands on the label leading back, selected, centred and with its edges highlighted 2026-10-15 16:35:30 +02:00
stroblme 2f780ad570 Flow canvas draws panel I/O ports as panel:<id>:<port> endpoints that open their panel in the Panels dialog 2026-10-15 16:31:33 +02:00
stroblme 5bd504ba9d LiveEdge pulse gaps: reset timer survives other producers, snapshots do not flash, flow: endpoints pulse as their node 2026-10-15 16:26:20 +02:00
stroblme 08adc9ef1d RemoteWorker.cancel makes no coroutine once the loop is closed
run_coroutine_threadsafe refused it and it was never awaited, a RuntimeWarning
under pytest 9. The cancel still logs that it could not reach the worker.
2026-10-15 16:17:30 +02:00
stroblme 146ab72c17 make test-webkit runs the drag specs in WebKit against the test stack
WebKit takes *.localhost to loopback through GLib and ignores /etc/hosts, so
the container shares Traefik's network namespace. The webkit project only
exists with PLAYWRIGHT_WEBKIT=1, so test-frontend stays Chromium only.
2026-10-15 16:15:39 +02:00
stroblme fbd886f7a7 make test-frontend runs against a stack of its own
The suite creates and deletes flows, dashboards and users, and ran against
whatever stack was up, which is the dev instance's data. make test-stack starts
the compose project fluksio-test (compose.test.yml over the local stack's
files) with its own containers, volumes and image tags, served by the local
Traefik on app.test.localhost and api.test.localhost, and leaves it running.
The hostname is fixed, so it no longer comes off the running dev stack, and a
LAN stack makes no difference. The suite's default origin is the test stack.
2026-10-15 16:15:32 +02:00
stroblme 70677dcc20 Stagger a rectangular panel's new dashboard by rail order
Changing dashboard kept the grid mounted, so every new tile rose at once.
The grid is drawn afresh per dashboard and its tiles come in row by row:
from below, top row first, for one further down the rail, and from above,
bottom row first, for one further up, as a round panel's dashboards slide.
2026-10-15 16:12:38 +02:00