Skip to content

fix(web): tidy the new-session form (#175) - #210

Merged
lionello merged 2 commits into
masterfrom
fix/175-new-session-ui
Aug 12, 2026
Merged

fix(web): tidy the new-session form (#175)#210
lionello merged 2 commits into
masterfrom
fix/175-new-session-ui

Conversation

@defangdevs

@defangdevs defangdevs commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Closes #175.

Before

new-session form before: white textarea, misaligned, label detached

After

new-session form after: dark textarea, one baseline, label above its input

What changed

  • Theme. The shared input, select rule in settings.css now also covers
    textarea, so the kickoff prompt box gets the same dark background, border and
    radius as every other field instead of the browser default white. It is
    full-width with vertical resize. box-sizing: border-box keeps the select and
    the text input the same height.
  • Alignment. The label + input stack lives in a .cwd-control column, and
    the row it sits in aligns on flex-end, so the agent select, the
    working-directory input and the Add session button share one baseline. The
    prompt row gets its own top margin.
  • Label. The working-directory input has an id and a real
    <label for=…> directly above it. The .note below no longer repeats the
    label text; it only explains the default. The ARIA attributes and the
    .combo autocomplete popup are untouched.
  • De-duplication. The settings page and the workspace rendered two
    near-identical copies of this form. Both now come from one
    NEW_SESSION_FIELDS_TPL, so they cannot drift apart again.

On the fourth checkbox

is textbox the right input? This is passed to agent harnesses on cmd line as
single arg, no?

Per @lionello this is about the kickoff prompt, and the premise is right: the
supervisor reads initialPrompt and appends it to the tmux command line as one
positional argument escaped with printf '%q'.

The textarea is still the right control. %q emits $'…\n…', the login shell is
pinned to bash, the webhook spawner already passes multi-line preambles, and the
sessions VM test already covers a multi-line prompt. A single-line input would
only remove capability.

But the round trip was broken, so this PR fixes it. Browsers submit textarea line
endings as CRLF, and the daemon did form.get("prompt", [""])[0].strip(),
which only trims the ends. Posting line one\r\nline two to a running daemon
stored exactly that in sessions.json, carriage returns and all, and they
travelled into the agent's argv. Line endings are now normalized where the form
value is read; re-running the same POST stores 'line one\nline two'. The
secret and password fields are single-line inputs, so nothing else needs it.

(For the record, the working-directory field is also correctly a text input: that
value goes to tmux new-session -c as one argument and never onto an agent
command line — that is the separate extraArgs array.)

Verification

  • bin/assemble-module.py --check: modules/agent-box.nix is up to date. The
    golden fixture is regenerated in the same commit.
  • Screenshots above are #session-editor on the settings page, rendered from the
    expanded sources at 2× on a 820px viewport, before and after.
  • No behaviour change: no route, no form field name and no stored value moved.

The screenshots live on the assets/175-new-session-ui branch; delete it with
this branch after merge.

The fix was written by codex (gpt-5.6-sol); I reviewed it, caught one
regression in the first round, and verified the result.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PsqGhkpjsb4kKAdVz25xKN

The add-session form read as unstyled next to the rest of the dark page
and its working-directory label floated free of its input.

- The shared input rule now covers <textarea>, so the kickoff prompt box
  gets the same dark theme, border and radius as every other field, and
  box-sizing: border-box keeps the select and the text input the same
  height.
- The working-directory input gets an id and a real <label for=...>; the
  .note below it drops the duplicated label text and only explains the
  default. The label+input stack sits in .cwd-control, and the row it
  lives in aligns on flex-end so the select, the input and the submit
  button share one baseline.
- Both copies of the form (settings page and workspace) now render from
  one NEW_SESSION_FIELDS_TPL, so they cannot drift apart again.

The text input is the right control (item 4 in the issue): the value is
stored as workingDirectory in sessions.json and reaches tmux new-session
-c as a single quoted argument. It is never placed on an agent harness
command line — that is the separate extraArgs array. No behaviour change.

Written by codex (gpt-5.6-sol). Verified by rendering the settings page
from the expanded sources and by bin/assemble-module.py --check; not
verified visually, as the box has no browser installed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PsqGhkpjsb4kKAdVz25xKN
@lionello

Copy link
Copy Markdown
Collaborator

@defangdevs "is textbox the right input" was referring to the initial prompt.

Checkbox 4 of the issue asked whether a textbox is the right input for
the kickoff prompt, since it reaches the harness as one command-line
argument. It is: the supervisor appends initialPrompt to the tmux
command line as a single positional arg escaped with printf %q, so
newlines survive, and multi-line prompts are already used by the webhook
spawner and covered by the sessions VM test. A single-line input would
only remove capability.

What was wrong is the round trip. Browsers submit textarea line endings
as CRLF and .strip() only trims the ends, so an embedded \r was stored
in initialPrompt and travelled into the agent's argv. Verified against a
running daemon: POSTing "line one\r\nline two" stored it verbatim.

Normalize CRLF and lone CR to \n where the form value is read. The
control stays a <textarea>. No other multi-line field exists in the
daemon — the secret and password fields are single-line inputs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PsqGhkpjsb4kKAdVz25xKN
@defangdevs

Copy link
Copy Markdown
Owner Author

Thanks — I had answered for the working-directory field. Re-checked for the kickoff prompt, and your premise is right, but it turned up a bug rather than a wrong control.

The prompt does reach the harness as a single command-line argument: the supervisor reads initialPrompt and appends it with cmd="$cmd -- $(printf '%q' "$prompt")". That is not an argument against a textarea, though. printf %q emits $'…\n…' for embedded newlines, the login shell is pinned to bash, the webhook spawner already sends multi-line preambles, and tests/sessions.nix already covers a multi-line prompt. Making it single-line would only take capability away.

The real defect is the round trip. A browser submits textarea newlines as CRLF, and the daemon did form.get("prompt", [""])[0].strip(), which only trims the ends. Against a running daemon, posting

prompt=line one\r\nline two

stored 'line one\r\nline two' in sessions.json — the carriage returns survive into the agent's argv. So a multi-line kickoff prompt typed in the web UI was subtly corrupted, while the same prompt via agent-box-session add --prompt was clean.

Pushed edd23e7: normalize CRLF and lone CR to \n where the form value is read, keeping the <textarea>. Same POST now stores 'line one\nline two'. No other multi-line field exists in the daemon; the secret and password fields are single-line inputs. Checkbox 4 is updated in the PR description.

@lionello
lionello merged commit 208f361 into master Aug 12, 2026
1 check passed
@lionello
lionello deleted the fix/175-new-session-ui branch August 12, 2026 21:05
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.

Ugly "new session" UI

2 participants