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.
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`.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.