Skip to content

Rework the UI to work like Kubri, with a read-only switch - #21

Merged
snaketech-tu merged 12 commits into
mainfrom
feat/kubri-ux
Nov 10, 2026
Merged

snaketech-tu merged 12 commits into
mainfrom
feat/kubri-ux

Conversation

@snaketech-tu

Copy link
Copy Markdown
Owner

Reworks the whole UI to work like Kubri, for Docker stacks and swarms. Stackline keeps its name, logo, colors and fonts. Read-only becomes a switch per connection, like Kubri's: locked by default, and when unlocked Stackline can restart, stop, start and scale. Delete, shell and edit did not come over.

What changes

Shell. The sidebar groups the pages (Overview; Workloads: Stacks, Services, Tasks; Cluster: Nodes; Logs), lists pinned services, and has a box that finds a page or service. The top bar has Back/Forward, the connection, a stack picker that narrows every list (like Kubri's namespace picker), "Search or run a command", the theme and the read-only switch. A status bar shows live state and the keys of the page.

Lists. Stacks, Services, Tasks (Containers without swarm) and Nodes each get a breadcrumb, title with count, / filter, tabs (All / Needs attention / Pinned or Running), sortable columns and an action bar that names the row it acts on. j/k or the arrow keys move the cursor, which only shows once moved, so nothing acts on a row out of sight. Enter drills down: stack to services, service to tasks, node to tasks. Back restores the list with its filter and cursor. Filter and tab survive a restart.

Inspector. The service and node panels dock beside the list instead of covering it. A click or d opens one, and it follows the cursor. Service tabs: Overview (likely problem, top error, at a glance, replicas), Events (deploys, stops, crashes), Config, Logs, Errors. The expand button lets it use the whole page.

Log workspace. l on any row (or Tail below) opens a tab at the bottom that keeps following its lines while you browse. It can be resized, shrunk with w, and is remembered per connection.

Read-only switch. Every connection starts read-only and the server refuses any change until Read-only in the top bar is clicked and confirmed (or "Allow changes" is run from the palette). The badge then turns amber and says Changes allowed; one click locks it again. Changing a connection's host or kind locks it. stackline serve --read-only (or STACKLINE_READ_ONLY=1) keeps every connection locked, and the update restart keeps the flag.

Changes. With changes allowed, the Services and Containers lists and the inspector offer: r restart (swarm: docker service update --force), x stop and u start (Compose and lone containers, per service or per container), c scale (replicated swarm services, 0 to 100). Each change first asks in a dialog that shows the docker command it amounts to, worked out by the server in a dry run. Nothing is created or removed. Changes that fit no row in a list are left out of its action bar.

Keys. : or Ctrl K opens the palette, which now also runs commands. ? lists every key. g plus a letter jumps to a page; Alt ←/→ go back and forward.

Tests

  • svelte-check clean, vite build ok
  • cargo test, cargo clippy --all-targets, cargo fmt --check clean. New unit tests for the plans and spec changes, and an API test that a connection refuses changes until unlocked and locks again when its host changes
  • e2e: 28 of 28 pass against the demo stack, including the read-only guard, which still sees only reads. changes.spec.ts checks that the API refuses changes while locked, that unlocking asks first, that r and Stop show the right docker command and cancel cleanly, and that the inspector offers the changes. Existing tests were moved to the new layout, and navigation.spec.ts was added for drill-down plus Back, the inspector following the cursor, the workspace surviving a page change and a reload, the stack picker, : and ?

Screenshots (light and dark) are in the project files under screens/kubri-ux/.

…pace

- New shell: grouped sidebar with pinned services and a find box; top bar
  with back/forward, connection, stack picker, command search, theme and a
  read-only badge; status bar with live state and the page's keys
- Stacks, Services, Tasks and Nodes as lists with filter, tabs, sortable
  columns, an action bar naming the target, j/k cursor and Enter drill-down
- Service and node panels dock beside the list as an inspector that follows
  the cursor; service tabs Overview, Events, Config, Logs, Errors
- Log workspace at the bottom: tabs that keep following while you browse
- : / Ctrl K command palette with commands, ? key overlay, g-prefixed jumps
- e2e tests moved to the new layout, plus navigation tests
… scale

Every connection starts read-only and the server refuses changes until it is
switched off; --read-only (STACKLINE_READ_ONLY) keeps every connection locked.
With changes allowed, services and containers can be restarted, stopped,
started and scaled. Each change asks first and shows its docker command.
Nothing is created or removed.
- Read the state again right after a change, so the next look (and the
  next change) sees it; start right after stop said everything was running
- Stop and start only containers Docker can stop or start (paused, dead)
- Say how many calls went through when a later one fails
- The palette matches the names of commands, so "allow changes" finds it
- The confirm dialog keeps Enter, Esc and Tab after a click on its text,
  takes no other keys, and calls scaling to 0 what it is
- A refresh that was under way no longer flips the read-only switch or
  the log collection back for a moment
On macOS and Linux, ssh now forwards the remote Docker socket to a socket
file in a folder made with mode 0700 (StreamLocalBindMask=0177), instead of
a TCP port on 127.0.0.1 that every local program, and a web page, could
reach. The folder is removed with the tunnel. Windows keeps the port for now.

Requests to Docker also give up when no headers come within 30 seconds, so
a hung daemon or tunnel no longer stops log collection for good.
- A clear can no longer leave a batch behind: the store checks the clear
  counter under its write lock and moves the cursor in the same step
- Another server behind a connection pauses collecting and asks whether
  to keep or delete the old logs, instead of deleting them silently
- Over the size limit, connections share the space equally; only those
  above their share lose lines
- First reads of a replica run in the background, two at a time, so a
  slow one no longer holds up the other replicas and the events
- Deploys are looked up by kind instead of among the newest 5,000 events
- --host beyond this computer answers any IP address of the machine (names
  stay refused) and warns that the link travels unencrypted
- A settings file that cannot be read is set aside as config.broken.json;
  Stackline starts with the defaults and says so in the UI
- CSV cells that start like a formula start with an apostrophe
# Conflicts:
#	CHANGELOG.md
#	ui/src/App.svelte
- Switching read-only off remembers the server the connection reaches
  (swarm or engine id). Changes are refused while the connection reaches
  any other server, so an SSH alias that starts pointing elsewhere never
  inherits the switch. Switching needs a live connection.
- The dry run returns a fingerprint of its calls; the confirmed request
  sends it back and is refused (409) when the plan made then differs, for
  example because a container was replaced in between.
Each restart, stop, start or scale is stored as an event of kind
"action" with its docker command and how it went. The Overview chart
marks it, and the service's Events tab lists it with the lines after it.
The fake slow replica answered after 4 s, and two polls may wait 2 s each
for first reads, so on a busy runner it finished during the second poll.
It now takes 8 s.
@snaketech-tu
snaketech-tu merged commit 70918f2 into main Nov 10, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant