Commit Graph
617 Commits
Author SHA1 Message Date
Codeman maintainer 04f232f239 feat(local-model): the start screen's local model button in the size of the main one
The owner asked for it much bigger and in the middle. The button keeps the
chip's markup and takes the primary button's height, radius and weight, with
an accent outline and its glyph in a disc, so a model on this Mac is offered
as a main way in rather than as a footnote under the launchers.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-07 00:04:57 +01:00
Codeman maintainer 77d29d4621 feat(local-model): one frame for the window, honest figures, a table for the consent
The window is one frame for the whole flow: a fixed height on desktop, App
Settings' boxed close button, the buttons in a foot of their own outside
the part that scrolls, so a click never moves the next button away and
nothing is cut off; the short screens are centred in it on desktop and sit
above the buttons on a phone.

The chooser lists the pick first and then the other models by download
size, largest first, a column the reader can see. A pick that only just
fits says "Best fit, tight". The memory figure is an estimate and says so
once, in whole GB; its legend names the peak, and the installed line uses
the same words beside what the model takes now, so the two figures are not
read as a contradiction. The legend, the column headers and the foot's
promise are set in --text-dim, the bar's track in --text-muted so its free
part can be seen, and only the download figure keeps tabular figures.

The consent screen is the download as a table: the chosen row, the runtime,
the coding agent with its installer's command as the row's second line, the
total in the strong weight, then where it goes in one line. The download bar
means the model download alone and is gone once the model is in; the screen
that ends an install shows "Start a session" alone; an error on an installed
model is the headline with a grey dot; an error's first sentence is the
headline; Remove is quiet everywhere but red in its own question. The
no-recommendation reasons are one sentence each, and the untested note is
in Codeman's voice. A refusal the server names is printed from its sentence.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-06 23:55:02 +01:00
Codeman maintainer 84d5c691ff feat(local-model): one button and a chooser window replace the welcome card
The read-only card is gone. The welcome screen has one button, "Run a local
model" ("Local model" once one is installed), and App Settings > Models has
one row; both open the same window.

The window follows `install.state`: choose a model (a radio group, with a
memory bar filled to what the selected model needs), confirm the sizes and
the folder, watch the download and the steps, and end on "Start a session".
Paused, failed, installed (running or stopped), Remove and "no model fits"
are screens of the same window. It polls the GET once a second while open.

Every dynamic string goes in through textContent, labels and sentences come
from the payload, no CLI id is compared, and every new string has a zh-CN
entry.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-06 22:16:38 +01:00
Codeman maintainer 8281045cf2 fix(local-model): say what is true of the Mac, and why a model is missing
Review fixes for the read-only recommendation and its card.

Recommendation (pure):
- `limited` is now a statement about the Mac: no model with 12 billion
  parameters or more fits. It is judged on everything that fits, before
  the disk filter and whatever the pick is. A 32 or 36 GB Mac whose pick
  is a 9B model no longer reads "This Mac can run a small model" above a
  list of 20B, 27B and 35B models, and knowing the chip no longer lowers
  the verdict. A small pick under `ok` carries a note that says so.
- A model that fits the memory but has no room on the disk is no longer
  dropped in silence: the best-ranked one is named in a note with the GB
  that are missing. A full disk cannot turn a 48 GB Mac into a
  small-model Mac.
- "Models fit, but not at a context a harness needs" has its own
  sentence with the budget and the need, worded as an estimate. The
  16 GB Mac mini misses by 1.3 points (10.3 of 10.4 GB) and was told
  flatly that its memory is too little, the sentence of an 8 GB Mac.
- The installed memory is named by the size the Mac is sold as (48, not
  51.5): the hardware line and the memory sentence. Every computed size
  stays decimal GB from the one formatter. An unknown memory size is no
  longer reported as "0.0 GB".
- Slow is graded on the unrounded speed: 9.95 to 9.9996 tokens per
  second printed as 10 and read ok.
- The disk sentence names the 10 GiB that must stay free, so its
  numbers add up for the reader.

Card and Settings group:
- The Settings group "Model on this Mac" is hidden on a server that is
  not a Mac, and until the first answer says it is one.
- Only `403 FORBIDDEN` is a refusal. `403 PASSWORD_CHANGE_REQUIRED`
  (an admin who must change the password) is a failed request and is
  asked again, so that admin no longer loses the card until a reload.
- Each fit and speed label carries what it measures as its tooltip and
  accessible name, an alternative with a speed estimate prints it, and
  the count beside "Other models that fit" is gone (it was the number
  of rows, capped at four, not the number of models that fit).
- The line saying nothing can be set up yet sits right under the pick,
  in the colour of every other line (it was 3.9:1), and says whose step
  it is.
- The dismissal key is named for this stage, so hiding the read-only
  card does not hide the install offer later.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-06 17:37:53 +01:00
Codeman maintainer 1976431ce6 feat(local-model): welcome card and App Settings row for the model on this Mac
The read-only face of GET /api/local-model: what the Mac the server runs on
can run as a local coding model.

- local-model-ui.js: one builder paints both containers from the same
  payload: the hardware line, the server's headline, the pick with its numbers
  (context, download, memory need against the budget, speed), the server's
  notes, the other models that fit with their Good, Tight, Slow or Speed
  unknown labels, and one line saying that setting it up from Codeman is the
  next step and is not built yet.
- The welcome card ships hidden and shows only once the server answered for a
  Mac. It stays hidden for not-macos, for a refused request (a non-admin in
  multi-user mode) and after "Hide", a per-device localStorage key. The App
  Settings group "Model on this Mac" (Models) carries the loading and failure
  sentences and is the way back after a Hide.
- Every dynamic string goes in through textContent, both containers are
  data-i18n-skip and the module translates its own sentences, repainting on a
  language change. Sizes are printed as sent. Labels and harness names come
  from the payload: the module names no CLI and the word "Recommended" is not
  in it.
- One request a minute at most, shared while in flight; a failed refresh keeps
  the last answer on screen.
- i18n.js: zh-CN entries for the module's strings and for every server label.
- frontend-cli-no-id-branching.test.ts scans the new module.

Tests: test/local-model-ui.test.ts (JSDOM over the real markup),
test/local-model-i18n.test.ts.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-06 16:28:04 +01:00
Codeman maintainer 9442ea8cf4 fix: remove the em-dashes this release added (release review)
105 new em-dashes sat in comments, test names and one test fixture from the
foreign tmux adoption work. Replaced by hand with commas, colons or
parentheses, one reading at a time. The fixture now quotes the real refusal
text, which says "It may still be running, try again."

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 08:20:39 +01:00
Codeman maintainer c65706a3e4 Merge #510 feat(adopt): adopt tmux sessions a human started, in all three locations
The attach line now takes the rule #770 gave remote and docker launches: bashCArg() single-quotes it at both respawn-pane sites, so it reaches bash verbatim. quoteAdoptLaunch is retired, since its double quotes kept the same $ and backtick safety but lost a real newline. #510's no-cd condition stays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 05:36:20 +01:00
Codeman maintainer aacf105cea Merge #614 fix(files): prevent large folders starving File Viewer results
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 05:04:44 +01:00
Codeman maintainer cddfc07550 Merge relprep/rtl fix(response-viewer): lay out Persian, Arabic and Hebrew right to left (#715)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 05:02:36 +01:00
Codeman maintainer 9ba5a89123 Merge #761 feat(picker): New folder button for Create New's custom folder picker
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 05:02:08 +01:00
Codeman maintainer 3ef65d7c74 fix: let the path picker's error status wrap (release review)
The cases-folder refusal grew to 143 characters, and the status line is one
line with an ellipsis and no title, so at 513px on desktop it ended at "or
make the case with Ad..." and lost the instruction. .path-picker-status.error
now wraps: 2 lines on desktop, 4 on a phone, measured clear in Chromium at
both, and it covers the picker's other refusals too.

Tests: a pin on that rule (fails without the fix), and the option check now
reads each picker's whole options object instead of a fixed 400 characters,
which stopped short of the container-workdir picker's 829. Retitled to say
what it checks: Create New has the flag too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 04:02:11 +01:00
Codeman maintainer f37f101137 Merge remote-tracking branch 'origin/master' into relprep/rtl 2026-11-06 04:01:39 +01:00
Codeman maintainer a908be05ac fix(files): mark folders the scan limit stopped short of
The fair rounds give a directory its node before reading it, so every folder
still queued when the 5,000-node budget ends sat in the tree with empty or
partial children and read as an empty folder. Keep the node on the tree cursor
and set an additive incomplete: true on every cursor left in the queue, then
draw one dim, inert row under such a folder while it is expanded. The envelope
check ignores keys it does not know, so old and new clients stay compatible.

Stop each round at the top once the budget is spent, before the shift: the
rest stay queued unread, and flagged, rather than read and sorted for nothing.

Say what the walk is: a per-directory round-robin (breadth-first), fair per
directory and not per subtree, with search results now shallow-first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 02:57:28 +01:00
Codeman maintainer 57b2dcc466 Merge remote-tracking branch 'origin/master' into relprep/pr-510
Brings the 1.33.2 based adoption feature up to master. Conflicts were
resolved by keeping master's intent and re-applying the feature on top:
both sides kept for imports, the script tag and the stylesheet blocks; the
trust-dialog guard keeps master's stopped check plus the adopted refusal;
the Ralph and image watchers keep master's remote branch plus the adopted
skip; the close confirmation keeps master's worktree option plus the
adopted wording.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 02:51:00 +01:00
Codeman maintainer 89a93c871f fix(files): list the git worktree an agent moved into (#765)
Claude Code's EnterWorktree moves the running process into
<repo>/.claude/worktrees/<name>, while Session.workingDir stays on the main
checkout. The Files panel kept walking workingDir, so it listed a tree the
agent had left.

paneCwd already follows the move, measured on Claude Code 2.1.318 in both
directions, so nothing new has to track the agent. It also follows every cd
in a shell, so a new SessionState.liveWorktree narrows it to a linked
worktree, read from the .git file without starting git.

Only a folder strictly below workingDir is ever an answer, and the listing
only starts there: returned paths stay relative to workingDir, so the
preview, raw and edit routes are unchanged and confinement does not widen.
A worktree outside the session folder stays unfollowed.

The panel asks with root=live, shows a strip naming the worktree with a
switch to the session folder, and follows the agent in and out while open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 23:47:38 +01:00
Codeman maintainer 4960bee779 chore: one changeset for the release, and no new em-dashes
Consolidates the four contributor changesets, and rewords the em-dashes this release
added in comments and docs. The five in test/settings-save-failure-ui.test.ts stay:
they assert existing toast text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 17:31:48 +01:00
Codeman maintainer f36bea86c7 Merge pull request #693: feat: edit history panel for local claude sessions (files edited, diffs, generated images) 2026-11-04 17:04:46 +01:00
Codeman maintainer f7dfe091c5 Merge pull request #757: feat(custom-model): mark the model a session is using in the endpoint model picker 2026-11-04 16:51:56 +01:00
Codeman maintainer 3bcb55ce96 Merge pull request #752: fix(custom-model): context limits, Codex model override, context discovery and picker filter 2026-11-04 16:51:56 +01:00
Codeman maintainer d9d6fad698 fix: cap the first tab on every pass, before a selection widens it (#748)
selectSession() toggles .active at once while _syncTabStripCorner() only runs in
the debounced render, so a cap set after measuring left a newly selected first
tab wider than the room and dropped the first row below the toolbar until then.
max-width changes nothing on a narrower tab, so the cap is simply always on, and
the clear-before-measure and finish-the-animation loops go. The no-usable-room
test uses 8 tabs: at 40 the scroll fallback fires first and it passed at any
threshold.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 16:48:57 +01:00
Codeman maintainer 1efe1d40c3 Merge pull request #745: feat(picker): New folder button in the path picker, on for Link Existing 2026-11-04 16:47:48 +01:00
Devvyn 4aeab0d9ce fix(custom-model): keep the in-use picker honest about down endpoints and exited panes
Review round on PR #757. The route turns EVERY upstream failure into
isLlamaSwap: false, so the old line silently promised an unreachable
endpoint was merely mute, and the live-session check missed a null pid
(the PTY exited, the same test keep-warm applies).

- Require pid !== null in _getCustomModelInUseBySession, pinned by a
  'process exited' case in the marks-nothing table
- Reword #customModelPickUnknown to claim only that nothing was
  reported, true for a down endpoint too; drop the 'stays quiet for a
  failed probe' promise from changeset, docs and comments
- architecture-invariants: add the In-use tier, both pin fences
  (customModel not displayModel; stopped/null-pid/paneExit never
  count) and the unknown-line rule
- Mirror the wording through i18n (key + zh-CN), index.html fixture,
  styles.css comment, and the run-menu tests
2026-11-02 19:15:37 +08:00
Devvyn 1f0823c582 fix(custom-model): show which model a session is using when an endpoint cannot report it
Only llama-swap answers what is loaded, so behind a gateway such as Bifrost
nothing was marked and the picker fell back to discovery order apart from the
one CLI that had launched there before. A live session whose own injected
endpoint is that host now promotes its model as "In use by a session", under
its own tag and below llama-swap's "Currently loaded" answer, reading only
what Codeman injected rather than terminal text, and never a stopped session.
When genuinely nothing is known the picker says so instead of leaving an
unmarked wall, and stays quiet for an endpoint that merely failed to answer.
2026-11-02 19:05:22 +08:00
Devvyn 5582ec523b fix(custom-model): fix picker spacing and the no-matches line 2026-11-01 21:06:39 +08:00
Devvyn ed67596387 fix(custom-model): pass context limits and selected model to CLIs, and add model picker filter
- opencode: write limit.context/output from the discovered context length (#749)
- codex: override the native launch model when a custom endpoint is selected (#750), and set model_context_window
- model picker: add a search/filter box with a no-matches line
2026-11-01 21:06:38 +08:00
timkjrandClaude Opus 5.5 6a23fecb1c fix: keep the L-shaped tab strip whichever tab is selected
The wrapped header strip fell back to the plain wrap whenever its first
tab was wider than the room beside the toolbar. A selected tab is wider
(its gear and close show), so selecting the first tab flipped the whole
strip back: the toolbar took its own column again and, with long names,
every tab took a line of its own. Selecting any other tab brought the
full-width rows back.

The first tab is now trimmed to the room instead (tab-corner-fit, a
max-width from --tab-corner-first-max, its name ellipsized). Only a
toolbar that leaves less than TAB_CORNER_MIN_FIRST_TAB (120px) beside
it keeps the plain wrap, which does not depend on the selection.

Two measuring details make the trim hold:
- the room comes from the rounded-up float width, less the tab's 2px
  margin and a pixel for subpixel layout (a tab at the exact width still
  wrapped under the float);
- the first tab is measured with its trim cleared and its gear/close
  width transitions finished, or a grown room reads the last trim as the
  width and a selection change is judged by the width the tab is leaving.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 16:41:26 -05:00
Devvyn 869633ae6d fix(picker): make New folder names Link Existing can use, and tighten the route
The name rule now matches safePathSchema (letters, digits, space and _ - . ~,
no .., no leading dot, at most 255 bytes) so every folder it makes can be
linked. A file as the parent is a 400, errno text is no longer returned,
showHidden is forwarded like browse, and Create is disabled while the request
runs. Adds multi-user scoping tests.
2026-10-31 19:44:02 +08:00
Devvyn d5e4fe0f87 feat(picker): New folder button in the path picker, on for Link Existing
The picker could only choose an existing folder. POST /api/filesystem/mkdir
creates one folder inside a folder the picker may browse (same roots,
ownership and blocked-tree rules as browse), and PathPicker gains an opt-in
allowNewFolder that adds a New folder button and selects the result.
2026-10-31 18:26:00 +08:00
Codeman maintainer 593b2eb2ea Merge pull request #721: feat(tabs): L-shaped header strip, wrapped rows run under the toolbar 2026-10-28 09:10:09 +01:00
Codeman maintainer 9ee6df027d Merge pull request #730: fix(file-preview): let rendered Markdown fill the resized window 2026-10-28 09:10:08 +01:00
Codeman maintainer c708312a3f fix: apply the review items for #730 at merge
Set --rv-content-max: none on the File Viewer's rendered document, so
ASCII-diagram blocks (capped by that variable with a 720px fallback the
Response Viewer sets) follow the window like the rest of the document,
and pin it with a third assertion.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 09:09:37 +01:00
timkjr 285aa0b2b2 fix(tabs): keep the plain wrap when the strip scrolls, add a browser test
Review follow-up for the L-shaped header strip:

- _syncTabStripCorner() keeps the plain wrap when the plain strip is
  already past --tab-strip-max-height, checked before .tabs-corner goes
  on: the float scrolled with the tabs while the toolbar stayed put, so a
  scrolled strip put tabs under #headerRight, and a re-render of a
  scrolled two-rows strip sized the float from the scrolled frame. The
  "- scrollTop" in contentTop is gone (always 0 now).
- A strip just under its max-height that passes it once the float is in
  falls back to the plain wrap as well.
- test/tab-strip-corner.browser.test.ts (real Chromium, real header and
  styles.css), listed in BROWSER_TEST_GLOBS.
- src/web/CLAUDE.md Tab layouts entry describes the L-shaped classic strip.
2026-10-26 21:35:04 -05:00
Codeman maintainer 0f1fca6ea0 Merge pull request #712: feat(tabs): start a session beside any tab from an opt-in + in its actions 2026-10-27 02:24:17 +01:00
Codeman maintainer 98526527c8 Merge pull request #711: fix(mobile): hold the PTY rows while the on-screen keyboard is up 2026-10-27 02:24:17 +01:00
Codeman maintainer 6539bc6c82 fix: apply the review items for #714 at merge
- Session update no longer resets "How often" while typing: updateGitFetchStatus
  (session-ui.js) leaves a control alone while it is document.activeElement, and
  autoSaveGitFetch writes the clamped interval back itself (Enter saves with the
  field still focused).
- Partial override shows the app values: enabled = session ?? app switch,
  interval = session ?? app interval ?? 300 (session-ui.js updateGitFetchStatus).
- runFetch (git-fetch.ts) arms the spawn with what the core.sshCommand read left
  of timeoutMs, and answers ETIMEDOUT without spawning when nothing is left, so a
  git that never answers costs one timeout, not two.
- fetchEnvFor keeps a GIT_SSH from the server's environment (repoSshCommand ||
  base.GIT_SSH), fileoverview and CLAUDE.md line updated to say so.
- i18n.js: the zh-CN "Git fetch on an interval" description scopes the only
  writing git command to the Git window.
- styles.css: the disabled/busy rules are scoped to .git-status-actions
  .btn-icon-sm (moved beside .git-status-actions), and the busy pulse stops
  under prefers-reduced-motion.
- Comments that called the override per case now say per session: session.ts
  (field, restore and getter docs, setGitFetchSettings), git-fetch-scheduler.ts:2,
  server-timing.ts:78, server.ts (cleanup and restore), app.js _onSessionUpdated,
  session-ui.js (openSessionOptions and updateGitFetchStatus).
- Tests: test/git-fetch-session-options-ui.test.ts (new, JSDOM: typing survives
  an SSE update, the Enter write-back, both partial cases); test/git-fetch.test.ts
  (GIT_SSH kept; one bound for the read and the fetch, stub git on PATH).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 01:58:14 +01:00
timkjr 3dafc89095 feat(tabs): L-shaped header strip, wrapped rows run under the toolbar
A wrapped classic tab strip stopped at the toolbar's left edge on every row,
so the space under the header chips stayed empty and long tabs took a row
each. Once the strip wraps, the toolbar now sits over the strip's top-right
corner and the strip lays its tabs out inline around a float sized to the
toolbar's box (_syncTabStripCorner), so only the rows beside the toolbar stop
short of it and every later row runs the header's full width.

- Wrap is still measured with the toolbar in flow (the corner is dropped
  before the measure), so the single-row strip is unchanged.
- A ResizeObserver on the toolbar re-decides wrap + corner and redraws the
  lineage routes when a reading or the divider changes its size.
- Falls back to the plain strip when the first tab cannot fit beside the
  toolbar (it would otherwise leave an empty band).
- Classic arrangement, desktop, header host only; state/case/ledger, the
  sidebar and the tab rail keep their own layouts.
2026-10-26 19:19:06 -05:00
Codeman maintainer 538fcae708 fix(response-viewer): lay out Persian, Arabic and Hebrew right to left (#715)
The Response Viewer and the File Viewer's markdown view gave no block a
dir, so a right-to-left answer was left-aligned with its punctuation on
the wrong end and its bullets and quote bar on the left.

_renderMarkdown now gives each outermost prose block dir="auto" (nested
blocks inherit, so a list keeps its markers on one side), pins pre and
code to dir="ltr", and keeps a dir the agent wrote. _linkifyFilePaths
pins the path anchors it creates to ltr. Because dir="auto" skips
descendants that carry a dir, code and paths no longer decide a
sentence's direction. The list indent, quote bar and table borders move
to logical properties, and the phone Compose textarea gets dir="auto".

The terminal pane is unchanged: xterm.js has no right-to-left support
(xtermjs/xterm.js#701).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 16:03:41 +01:00
Devvyn 9432d9f9e2 feat(git): a Fetch button in the Git status header, and an interval for it app-wide and per case
Fetch is the only git command Codeman runs that writes, and the only one that runs a
repository's own fetch-post hook, so it stays behind a click and off by default. The Git
status window's header row gains Fetch; App Settings -> Bottom bar gains the switch and
the interval, synced because the scheduler acts on them, and Session Options -> Git
overrides both per case. The scheduler reads settings fresh each tick, times the interval
from the start of a run, never overlaps runs, and re-checks the Docker and remote rules
every tick before any git runs. Nothing but remote-tracking refs moves: HEAD, the index
and the working tree are proven unchanged against real git while origin/main advances.

Tests: src/git-fetch.ts and the scheduler, the per-case settings route, the synced/display
setting split, the two new i18n rows, and the Fetch block of the Git status browser suite.
2026-10-25 17:23:55 +08:00
DevvynandClaude Code 34f7766d43 fix(file-preview): let rendered markdown fill the resized window
The corner grip and Expand size the window correctly, but the document
kept a fixed 960px reading column from before the resize feature
existed, so text stopped reflowing once the window grew past it.
Pinned by new rule 13 in file-preview-window.test.ts.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-25 09:56:40 +08:00
JD c7835a159c feat(tabs): start a session beside any tab from an opt-in + in its actions
Opening another session in the case you are already working in meant re-picking the case and the mode in the toolbar. An opt-in + now sits with each tab's gear and close icons (App Settings, Tabs, New Session Button on Tabs: per device, off by default everywhere). It opens a menu of the CLIs that tab's case can start, each shown with its logo and name as in the Run menus, and a pick starts one session in that tab's case, or the managed worktree it runs in, and in its named group, without changing the Run button's mode or case.

The CLIs offered follow the Run menu's own rule for the case, so a Docker case lists what its container has rather than what the host has. A tab outside any named group sends no group, so the case's default session group places the new session the way it places a toolbar Run. Wherever a tab has a row menu (the vertical rail, the sidebar) that menu gains "New session here…", the keyboard route to the same picker, since Enter on the + itself does nothing. On a phone the + joins gear and close on the active tab only, and the label reserve grows to 68px while the setting is on, so the tab's centre stays off the icons.

The shared tab layout menu no longer closes when focus leaves an item for nowhere: Safari never focuses a clicked button, so pressing an item blurred the focused one with no related target and the click was lost. This is the tile count menu's rule, and it fixes the named-group menu as well.
2026-10-24 20:29:00 -04:00
JD 3e334357e8 fix(mobile): hold the PTY rows while the on-screen keyboard is up
Every keyboard open and close resized the PTY, and Claude Code's inline renderer answers each height change by clearing the screen and reprinting only the transcript tail that fits, so rows above that tail were lost from tmux for good. Measured on Claude Code 2.1.306 with tmux 3.2a: five keyboard cycles (62x38 to 62x15 and back) left 75 of 100 numbered lines in the pane's history; with the rows held the PTY stays 62x38, no SIGWINCH reaches the pane and all 100 remain (#672).

While the keyboard is up the rows captured at the show transition are held through getTerminalDimensions, columns still follow the box, and the screen, now taller than its box, is translated up by --kb-clip so the last content row sits at the bottom of the visible area. This is the one documented exception to the columns-only geometry rule: there the extra rows came from another device and nothing kept the composer in view, here they are this device's own rows held on purpose and the clip keeps the composer visible. The local-echo overlay's setRowLimit narrows the renderer's existing totalRows bound, so one place decides where a long draft stops growing downward.

Only inline renderers hold, through a new registry capability keyboardHold: 'inline' (Claude alone) published to the page as window.__codemanKeyboardHoldModes. Full-height layouts such as codex, Claude's fullscreen mode (mouse tracking on, unmeasured), and remote or docker panes resize as before. The hold is released on keyboard close, a shape change, a tab switch, a session update, a reconnect, or the setting turned off. Per-device setting Keep Size Under Keyboard, default on.
2026-10-24 20:17:45 -04:00
Codeman maintainer fb83a5c8df fix: keep the host readings' labels under header density, hide ringless ones at Compact tiny (release review)
#689's density steps hide every `.header-system-stats .stat-label` once the
toolbar is narrowed, and #692's readings (GPU, VRAM, GPU MHz, CPU MHz) have
nothing else naming them: at tight, dense and tiny the header showed "40%"
beside CPU's "12%", "3.2G" beside MEM's "7.9G", and two bare "1800 MHz" /
"4200 MHz" with no way to tell the GPU clock from the CPU one. At Compact +
tiny, where values are hidden too, the two clocks (no ring) were left as
0-px items that still took an 8 px flex gap each. Neither PR shows it alone.

- The four host items carry `stat-item--host`, excluded from the density
  label hide, so they keep their labels at every step.
- `stat-item--ringless` (static on the two clocks, toggled by
  `_updateHostSensors` on VRAM while its total, and so its ring, is unknown)
  is hidden at Compact + tiny instead of staying an empty item.

Tests: the browser test drives every density in all three styles against
the real markup and styles.css (it fails on the previous files), and the
unit test pins the VRAM ringless toggle.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 16:45:05 +02:00
Codeman maintainer c7b6226cb0 Merge pull request #692: feat(header): opt-in GPU and CPU clock readings after CPU and MEM
Conflict in config/test-suites.ts resolved by keeping both sides: #689's
header-divider and #692's header-host-sensors browser tests are both
listed in BROWSER_TEST_GLOBS.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 15:25:02 +02:00
Codeman maintainer aad67f2056 Merge pull request #689: feat(header): resizable toolbar divider that collapses the usage readings as it narrows 2026-10-24 15:23:39 +02:00
Devvyn e1d6471808 feat(header): opt-in GPU and CPU clock readings after CPU and MEM 2026-10-23 21:36:10 +08:00
RandalixandClaude Opus 5.5 95e2272f89 feat(edit-history): edit history panel for local claude sessions
Part 1 of #643 (local sessions only; remote follows as its own PR). An
opt-in, per-device header button opens a panel with the active session's
edits, newest first: every Edit/Write/MultiEdit/NotebookEdit of the main
agent and its subagents with its diff, plus images a Bash call rendered or
the agent sent. Remote, Docker and non-claude sessions answer `unsupported`.

- Reads only on request: `?sync=1` when the panel opens; while it is open on
  that session it re-reads after an edit-capable `transcript:tool_end` or a
  `transcript:complete` from the existing transcript watcher. No hook, timer,
  second watcher or new SSE event; a wiring guard pins it.
- Main transcript: the path the transcript watcher follows (hook-reported),
  else `<id>.jsonl` under the session's own CLAUDE_CONFIG_DIR override or
  the server's config dir. A config dir set outside Codeman is found with
  the next hook; until then the panel says so.
- Store: dataPath('edit-history')/<id>.json, 0600 via tmp+rename, capped at
  500 entries and 4 MiB of diff text per session, deleted with the session,
  blocked in isSensitivePath, never fed to /api/search. Every route goes
  through findSessionOrFail with req.
- Images only through existing file surfaces: file-raw inside the
  workspace, the attachment route with notify:false outside it.
- textContent rendering, bounded byte-offset reads, marker-class header
  button, hidden on phones (locked in the mobile policy test).

Docs: Working-With-Files, Settings-Reference, architecture invariants, API
reference, CLAUDE.md key pattern.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 14:00:48 +02:00
Devvyn f3c90abeb0 fix(header): settle the divider when readings change, keep density to the header layout, restore the chosen width 2026-10-23 19:58:40 +08:00
Devvyn fa2d75c951 feat(header): resizable toolbar divider that collapses the usage readings as it narrows 2026-10-23 19:01:06 +08:00
Aamer Akhter c6a5995ac6 feat(palette): choose the agent when starting a session from Cmd+K
The palette's "New session" row now shows the agent it will start and
lets Left/Right (or the row's arrows) switch it. The agents come from the
injected CLI catalog, filtered to enabled and installed entries, so no CLI
id list lives in the palette. The pick is a one-shot launch through run()
with a temporary _runMode swap: the Run button's mode is left unchanged.
2026-10-22 17:23:03 -04:00
Codeman maintainer 463a083a23 Merge pull request #676: feat(tabs): per-case default session group
Conflict in CLAUDE.md's State Files line resolved by keeping #661's new
config-backup entries and this PR's "case default groups" in state.json.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 19:54:54 +02:00