Skip to content

fix(console): give .content a horizontal gutter - #91

Merged
brettchien merged 5 commits into
mainfrom
fix/content-horizontal-padding
Aug 19, 2026
Merged

fix(console): give .content a horizontal gutter#91
brettchien merged 5 commits into
mainfrom
fix/content-horizontal-padding

Conversation

@brettchien

Copy link
Copy Markdown
Contributor

Brett flagged: no left/right padding on the console — content runs edge-to-edge.

Root cause

.content (console/src/styles.css) has been display:flex; flex-direction:column
with zero padding since the very first commit. Every top-level section
(.chat-wrap, .config-wrap, .deploy-wrap, ...) only carries a bottom
margin — nothing ever set a horizontal gutter. Pre-existing, not something
the ADR #83 drill-down slices introduced; just more visible now that the
layout changed.

Fix

.content { padding: 16px 18px; } — horizontal inset matches the topbar's
own padding: 12px 18px, so left/right edges line up visually with the
topbar content.

Verification

  • tsc --noEmit — clean
  • vitest run — 107/107 passing
  • vite build — clean

🤖 Generated by Orca ('ecs-claude').

.content has been display:flex/flex-direction:column with no padding
since the very first commit — every top-level section only carries a
bottom margin, so nothing ever set a left/right gutter and content ran
edge-to-edge. Add padding matching the topbar's horizontal inset (18px).

🤖 Generated by Orca ('ecs-claude').
…ger chat

Brett flagged three related issues live on #91's layout:
- the "MANAGING <cluster>" identity banner should sit above Agent chat in
  the right column, not as a full-width strip below the drilldown row
- Agent chat's fixed 120–340px log box reads as too small
- the two-column split (drill-down content vs. chat) should be adjustable

## What
- `#identity` moves into a new `.drilldown-side` wrapper alongside `#chat`,
  right column of the drilldown row (was a standalone full-width section).
- `.drilldown-side > .chat-wrap .chat-log` bumps to a 240–560px range.
- A drag handle (`#drilldown-resizer`) between the two columns lets the
  operator widen/narrow `.drilldown-side` (280–900px), width persisted to
  localStorage. `src/splitPane.ts` owns the pure clamp/persist logic
  (`clampWidth`/`readWidth`/`saveWidth`) plus the DOM wiring
  (`initSplitPane`), mirroring `theme.ts`'s split.

## Verification
- `tsc --noEmit` — clean
- `vitest run` — 112/112 passing (5 new for `splitPane.ts`)
- `vite build` — clean
- Playwright: confirmed identity renders above chat inside `.drilldown-side`,
  and dragging the handle grows the column live (420px → 626px in the test
  drag) — screenshots eyeballed for visual regressions.

🤖 Generated by Orca ('ecs-claude').
@brettchien

Copy link
Copy Markdown
Contributor Author

Pushed a second commit addressing your three follow-ups:

  1. MANAGING banner → right column, above Agent chat: #identity moved into a new .drilldown-side wrapper next to #chat (was a full-width strip below the row).
  2. Agent chat too small: the chat-log's fixed 120–340px height bumps to 240–560px for this panel specifically (scoped so it doesn't affect the per-agent console chat, which reuses the same .chat-wrap/.chat-log classes).
  3. Adjustable 2-column split: drag handle between the drill-down content and the chat column, 280–900px range, width persisted across reloads (src/splitPane.ts).

tsc/vitest (112/112)/vite build all clean; verified interactively with Playwright (identity renders above chat, drag grows the column live) — screenshots eyeballed for regressions.

…p Files, fix chat box + Debug drawer

Four more items from Brett live-testing #91's layout, all against the same
per-agent (fleet-detail / agent-console) drill-down:

- **Remote + Compose not relevant per-agent**: both are Fleets-screen
  concerns (management-connection setup, template/bundle authoring) —
  `updateScreen()` now hides `#remote`/`#compose-standalone` alongside the
  Fleets screen whenever a fleet is selected, same condition as
  `#config`/`#fleet-detail` already used.
- **Remove the Files part for agent console**: `.ac-files` (the remote file
  browser) was a permanent stub — every real endpoint reports no fs
  capability ("pending the fs MCP files server"), so it never showed
  anything but a placeholder. Deleted `fileBrowser.ts`, its wiring in
  `agentConsole.ts`, the markup, the now-dead `render.ts` pure functions
  (`fsListingHtml`/`fsUnavailableHtml`) + their tests, and the `.ac-files`/
  `.fs-*` CSS. `Source.fsList`/`fsRead`/`fsCapability` are left alone — a
  backend contract, not UI surface.
- **Chat input fixed at 2 lines**: `.chat-text` was `resize: vertical` with
  only a `min-height` floor, so a drag could grow it arbitrarily.
  `resize: none` — `rows="2"` on the textarea already sets the height.
- **Debug drawer: unreachable Close + hidden top**: `.debug-drawer` was
  `position: fixed; top: 0`, but the topbar sits above it in z-index
  (intentionally, so the drawer slides in *below* it) — the drawer's own
  header (title + Close) rendered directly underneath the topbar's opaque
  background, both invisible and unclickable. Anchors to `--topbar-h` now
  (measured in `main.ts`, kept live via `ResizeObserver` since the topbar's
  height isn't hard-coded), so the drawer starts right at the topbar's
  bottom edge.

## Verification
- `tsc --noEmit` — clean
- `vitest run` — 102/102 passing (10 fewer: the deleted file-browser tests)
- `vite build` — clean
- Playwright: confirmed Remote/Compose hide on fleet-detail, `.ac-files` is
  gone from the DOM, the chat textarea computes `resize: none` at 2 rows,
  and the Debug drawer's top now lines up exactly with the topbar's bottom
  edge — a real mouse click on Close closes it.

🤖 Generated by Orca ('ecs-claude').
@brettchien

Copy link
Copy Markdown
Contributor Author

Third commit, addressing your four follow-ups on the per-agent view:

  1. Remote + Compose not relevant per-agent: hidden alongside the Fleets screen whenever a fleet is selected (same condition #config/#fleet-detail already toggle on).
  2. Files removed from agent console: .ac-files was a permanent stub — every real endpoint reports no fs capability today, so it only ever showed a "pending" placeholder. Deleted fileBrowser.ts, its wiring, the dead render.ts helpers + tests, and the CSS. Left Source.fsList/fsRead/fsCapability alone (backend contract, not UI).
  3. Chat input fixed at 2 lines: was resize: vertical with only a min-height floor — now resize: none.
  4. Debug drawer top hidden / Close unreachable: .debug-drawer was top: 0, but the topbar sits above it in z-index on purpose (so it stays visible) — that meant the drawer's own header rendered underneath the topbar, invisible and unclickable. Now anchors to a live-measured --topbar-h so it starts right at the topbar's bottom edge.

tsc/vitest (102/102 — 10 fewer from the deleted file-browser tests)/vite build all clean. Verified interactively with Playwright: Remote/Compose hide on drill-down, .ac-files is gone from the DOM, the textarea computes resize: none at 2 rows, and a real mouse click on the Debug drawer's Close now lands on the button and closes it.

…r links

Two more from Brett's live testing:

- **Same agent reachable as both the management connection and a normal
  per-agent console**: `agentConsole.ts`'s existing guard only checked a
  roster entry's name against agents.toml's own `management: true` flag.
  But the persistent management connection is a *separate* config
  (`remote.toml`'s url) — a roster entry can point at the exact same
  physical agent without ever being flagged, and nothing stopped opening a
  second console (a second live ACP session) against it. Confirmed this is
  exactly what Brett hit: the physical agent was reachable both via the
  always-on "Agent chat" (remote.toml) and via its own roster row, each
  session stepping on the other's turn state.

  Fixed by comparing urls, not just names: `agentRow`/`agentListHtml` (pure,
  `render.ts`) now also treat a roster entry as "management" when its url
  matches the active management connection's url — same disabled/"console
  above" row treatment agents.toml's own flag already gets, so the row
  doesn't look clickable when it isn't. `agentConsole.ts`'s `open()` gets
  the matching guard server-side of the click, for the same race the
  existing name check already defended against (list render vs. live
  connect state). The url is only considered "active" while
  connecting/connected/erroring (`remoteConfig.status !== "disconnected"`)
  — an unconfigured/never-activated management connection doesn't block
  anything.

- **Roster row didn't read as clickable**: `button.row-open` only turned
  link-colored + underlined on `:hover` — at rest it was indistinguishable
  from plain text, so the click target ("prod/orca" in the roster) was
  undiscoverable without first hovering it. Now link-styled at rest.

## Verification
- `tsc --noEmit` — clean
- `vitest run` — 103/103 passing (1 new: url-matched management dedup)
- `vite build` — clean
- Playwright: confirmed the roster link computes link-blue + underline with
  no hover.

Also checked two other reports and didn't find code to change:
- The Fleets-screen `[⚙]` (opens the Debug drawer) dials and opens
  correctly on current HEAD, verified with a real synthetic mouse click —
  most likely the same topbar/z-index bug fixed two commits back on
  d78ed19 (nothing visibly happened because the drawer's own header rendered
  under the topbar); flag again post-deploy if it's still stuck.
- The Compose panel's box height exactly matches its content (measured:
  612px total, fully accounted for by the grid + padding) — no CSS
  overflow/stretch bug. The blank space below it is the page ending inside
  a viewport taller than the content, ordinary document flow.

🤖 Generated by Orca ('ecs-claude').
@brettchien

Copy link
Copy Markdown
Contributor Author

Fourth commit — the two items I could actually confirm and fix from this last round:

  1. Same agent double-connected (management + roster): confirmed this is real. agentConsole.ts's guard only checked a roster entry's name against agents.toml's own management: true flag — but the persistent "Agent chat" connection dials whatever remote.toml points at, a separate config. A roster entry can reach the identical physical agent without ever being flagged management, and nothing stopped a second console (a second live ACP session) from opening against it — which is exactly what your screenshot showed (garbled/mismatched turns between the two panels talking to the same orca process). Fixed by comparing urls, not just names: a roster row now gets the same disabled "console above" treatment whenever its url matches the active management connection's url, whether or not agents.toml flagged it. Guarded in both the render (so the row doesn't look clickable) and the open() handler (belt-and-suspenders against a render/connect race).
  2. Roster row didn't look clickable: button.row-open only went link-styled on :hover — at rest it was plain text. Now link-colored + underlined by default.

On the other three:

  • [⚙] "not working": tested against current HEAD with a real synthetic mouse click — it opens the drawer correctly. My best guess is this was the same topbar/z-index bug fixed two commits ago (the drawer's own header rendering invisibly under the topbar) — nothing looked like it happened even though the drawer did slide in. Should be resolved by this branch; flag me again post-deploy if it's still stuck.
  • Compose "blank area at the bottom": measured it — the box height (612px) exactly matches its content (head + grid + padding), no CSS stretch/overflow bug. The blank space is just the page ending inside a viewport taller than the content — ordinary scrolling-document behavior, not a sizing bug. If you want the whole app to fill the window edge-to-edge (a fixed-height app-shell instead of a naturally-scrolling page), that's a legitimate ask but a bigger structural change touching every section — let me know if that's actually what you want before I take it on.
  • Start/Stop/Configure/Restart actions: checked the backend contract — today Source only exposes scaleDeployment(name, 0|1), i.e. Start/Stop. There's no Restart or Configure RPC yet. Before I build UI for buttons that don't do anything: is Restart okay as scale-off-then-on (not a true zero-downtime restart, just what's achievable with what exists), and should Configure open the existing Compose/deploy flow scoped to that agent (reuse, not a new inline editor)? Say the word and I'll scope + build it.
  • "hard to understand how to use the compose panel": what specifically trips you up — the two-column layout (author on the left vs. deploy on the right), the field order, or something else? Happy to take a pass at clearer copy/labels once I know what's confusing.

tsc/vitest (103/103)/vite build all clean.

Brett: "+New fleet"/"+Add instance" should land in the left column, not
appear as a separate block below an unrelated-looking roster+chat row.

Root cause was two-fold:

- `deploy.ts` set `#drilldown-row.hidden = true` on open, meaning to hide
  *both* columns (main content + the persistent chat side) while the deploy
  form took over full-width below. But `.drilldown-row { display: flex }` is
  unconditional, and an author rule beats the UA `[hidden]` rule at equal
  specificity — same bug class as `.debug-drawer[hidden]` two commits back,
  just not yet caught here. So the row never actually hid; the deploy panel
  just appeared as an extra full-width block underneath everything, which is
  exactly what looked wrong in the screenshot.
- Even fixed, hiding the whole row was the wrong target: it would drop the
  persistent Agent chat too, breaking the "chat pairs with whichever depth
  is showing, visible at every depth" rule the rest of the drill-down
  follows (Fleets/Fleet-detail/Agent-console).

Fixed by moving `#deploy-wrap` inside `.drilldown-main` as a sibling of
`#config`/`#fleet-detail`, so it's just another depth of the same
drill-down: open hides whichever of those two is showing (both, harmlessly,
covers either), close calls back into `main.ts`'s `updateScreen()` (new
`restoreScreen` dep) to restore the right one — no second copy of "which
screen was active" logic in deploy.ts. `.drilldown-row[hidden]`/
`.deploy-identity[hidden]` (the "new fleet" step-1 form, same unconditional-
`display: flex` bug) get the same `[hidden]` override as `.debug-drawer` as
defensive insurance either way.

## Verification
- `tsc --noEmit` — clean
- `vitest run` — 103/103 passing (no behavior change in tested modules)
- `vite build` — clean
- Playwright: confirmed both `+ Add instance` (from fleet-detail) and
  `+ New fleet` (from the Fleets screen) render in the left column with
  identity/Agent chat still visible on the right, and Cancel correctly
  restores the prior screen either way.

🤖 Generated by Orca ('ecs-claude').
@brettchien

Copy link
Copy Markdown
Contributor Author

Fifth commit: deploy panel now takes over the left column instead of appearing as a stray block below.

Two things were wrong:

  1. `deploy.ts` set `#drilldown-row.hidden = true`, meaning to hide both columns while the form took over full-width. But `.drilldown-row { display: flex }` is unconditional — same bug as `.debug-drawer[hidden]` from two commits back (author rule beats the UA `[hidden]` rule at equal specificity), just not caught there yet. The row never actually hid, so the form landed as an extra block underneath the still-visible roster+chat — exactly your screenshot.
  2. Even with that fixed, hiding the whole row was the wrong target — it'd drop Agent chat too, breaking "chat stays visible at every depth."

Moved `#deploy-wrap` inside `.drilldown-main`, a sibling of `#config`/`#fleet-detail` — it's just another depth now. Open hides whichever of those two is showing; Cancel calls back into `updateScreen()` to restore the right one. Added the same `[hidden]` override to `.deploy-identity` (the "new fleet" step-1 form had the identical bug) and to `.drilldown-row` itself as insurance.

`tsc`/vitest (103/103)/`vite build` clean. Verified both `+ New fleet` and `+ Add instance` land in the left column with identity + Agent chat still visible on the right, Cancel restores correctly either way.

@brettchien
brettchien merged commit 843ff60 into main Aug 19, 2026
2 checks passed
@brettchien
brettchien deleted the fix/content-horizontal-padding branch August 19, 2026 03:09
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