Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
50d2222
fix(cmux-next): attach composer location tray
teamleaderleo Oct 7, 2026
8e1ed81
test(cmux-next): update composer tray theme assertions
teamleaderleo Oct 7, 2026
371bf91
fix(cmux-next): keep composer tray truthful and narrow
teamleaderleo Oct 7, 2026
d4c4058
build(cmux-next): regenerate agent pane bundle
teamleaderleo Oct 7, 2026
c957c8a
Merge remote-tracking branch 'origin/feat-cmux-next' into fix/compose…
teamleaderleo Oct 7, 2026
aec8037
Merge remote-tracking branch 'origin/feat-cmux-next' into fix/compose…
teamleaderleo Oct 7, 2026
beab8c6
fix(cmux-next): refine composer footer controls
teamleaderleo Oct 7, 2026
d3e48c6
Merge remote-tracking branch 'origin/feat-cmux-next' into fix/compose…
teamleaderleo Oct 7, 2026
f9e0df1
fix(cmux-next): use shared composer menu wrappers
teamleaderleo Oct 7, 2026
29a63fc
chore(cmux-next): regenerate the agent pane bundle
teamleaderleo Oct 7, 2026
755b517
Merge remote-tracking branch 'origin/feat-cmux-next' into fix/compose…
teamleaderleo Oct 7, 2026
3487577
chore(cmux-next): regenerate the agent pane bundle
teamleaderleo Oct 7, 2026
a81f568
Merge remote-tracking branch 'origin/feat-cmux-next' into fix/compose…
teamleaderleo Oct 7, 2026
d2bb472
chore(cmux-next): regenerate the agent pane bundle
teamleaderleo Oct 7, 2026
d252ebe
Merge remote-tracking branch 'origin/feat-cmux-next' into fix/compose…
teamleaderleo Oct 7, 2026
5d094a7
chore(cmux-next): regenerate the agent pane bundle
teamleaderleo Oct 7, 2026
77ccb6d
Merge remote-tracking branch 'origin/feat-cmux-next' into fix/compose…
teamleaderleo Oct 7, 2026
fa166ac
chore(cmux-next): regenerate the agent pane bundle
teamleaderleo Oct 7, 2026
3243ce3
chore(cmux-next): refresh the markdown viewer bundle
teamleaderleo Oct 7, 2026
ae8efeb
chore(cmux-next): regenerate webviews app assets
teamleaderleo Oct 7, 2026
50c9ac0
fix(cmux-next): mark the current branch as checked
teamleaderleo Oct 7, 2026
12b0cb0
chore(cmux-next): regenerate the agent pane bundle
teamleaderleo Oct 7, 2026
490631f
Merge remote-tracking branch 'origin/feat-cmux-next' into fix/compose…
teamleaderleo Oct 7, 2026
f25fb19
chore(cmux-next): regenerate the agent pane bundle
teamleaderleo Oct 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2328,7 +2328,7 @@
box-sizing: border-box;
width: 100%;
min-height: 45px;
max-height: 40vh;
max-height: calc(22px * 8 + 23px);
margin: 0;
padding: 15px 16px 8px 32px;
border: 0;
Expand All @@ -2347,7 +2347,7 @@
.acpmux-composer[data-shell] .acpmux-composer-box {
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--acpmux-shell-accent) 55%, transparent);
}
/* The location row above the composer (ComposerContext.tsx): "This Mac ⌄ · <folder> ⌄", one quiet
/* The location tray below the composer (ComposerContext.tsx): folder and branch controls, one quiet
row of text menu buttons that all look alike. A control is a plain text button (no native bezel,
no fill, no weight change); hover and an open menu lay a soft rounded wash from the theme under
it, and keyboard focus draws a ring in the theme's text color. The padding is always there, so
Expand All @@ -2356,14 +2356,23 @@
display: flex;
align-items: center;
justify-content: flex-end;
gap: 2px;
gap: 8px;
min-width: 0;
height: 28px;
margin: 0 12px -2px;
height: 36px;
margin: -12px 0 16px;
padding: 0 14px;
box-sizing: border-box;
border: 1px solid var(--acpmux-composer-edge);
border-top: 0;
border-radius: 0 0 20px 20px;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate selector creates dead CSS property values

Low Severity

The .acpmux-composer-context selector is declared twice at the same cascade level with no conditional wrapper. The second block (around line 2445) unconditionally overrides height, margin, padding, and border-radius set in the first block (around line 2361). Those four properties in the first block are dead — they never take visual effect, but a future developer editing them would reasonably expect them to control the layout. The values from the two blocks could be consolidated into a single rule to avoid this confusion.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit fa166ac. Configure here.

background: var(--acpmux-composer-tray);
Comment thread
cursor[bot] marked this conversation as resolved.
color: var(--agent-muted);
font-size: 12px;
line-height: 16px;
}
.acpmux-composer-context > .acpmux-location-leading { display: flex; align-items: center; gap: 2px; min-width: 0; margin-right: auto; flex: 1 1 0; }
.acpmux-composer-context > .acpmux-location-leading > .acpmux-location-picker,
.acpmux-composer-context > .acpmux-location-picker { flex: 1 1 0; min-width: 0; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Location controls stretch with empty gaps

Medium Severity

Folder, computer, and branch pickers all use flex: 1 1 0, so they split the shelf evenly, while each control is capped at max-width: 220px. At the 760px composer width the buttons stop growing and leave empty gaps between compact labels. margin-right: auto on .acpmux-location-leading cannot pack the branch to the end because the branch picker also grows.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3487577. Configure here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Branch picker steals tray space

Medium Severity

The shelf gives both the leading cluster and the branch picker flex: 1 1 0, so margin-right: auto cannot keep the branch compact on the right. With width: 100% on the control, the branch button grows toward 220px and sits in the middle of the tray instead of hugging the end.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a81f568. Configure here.

.acpmux-location-picker {
position: relative;
display: inline-flex;
Expand All @@ -2377,6 +2386,7 @@
align-items: center;
gap: 4px;
min-width: 0;
width: 100%;
max-width: 220px;
height: 22px;
margin: 0;
Expand Down Expand Up @@ -2428,6 +2438,37 @@
.acpmux-location-menu .ui-menu-item[data-highlighted] { background: var(--acpmux-menu-hover); }
.acpmux-location-menu .acpmux-menu-description { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acpmux-location-menu .ui-separator { flex: none; height: 1px; margin: 4px 6px; background: color-mix(in srgb, var(--agent-text) 12%, transparent); }

/* The shelf sits behind the composer edge instead of reading as a second full row. The composer
owns the shadow and the shelf keeps a stable inset, so opening a menu never moves the transcript. */
.acpmux-composer-box { z-index: 2; }
.acpmux-composer-context {
position: relative;
z-index: 1;
height: 40px;
margin: -14px 2px 12px;
padding: 10px 10px 0;
border-top: 0;
border-radius: 0 0 18px 18px;
box-shadow: inset 0 0 0 .5px var(--acpmux-composer-edge), 0 4px 10px color-mix(in srgb, var(--agent-text) 6%, transparent);
}
.acpmux-composer-bar {
min-height: 52px;
height: auto;
padding: 6px 12px;
}
.acpmux-mode .acpmux-mode-text { display: none; }
.acpmux-mode .acpmux-picker-button {
width: 30px;
justify-content: center;
gap: 0;
padding: 0;
}
.acpmux-mode .acpmux-picker-button .acpmux-icon:last-child { display: none; }
.acpmux-mode .acpmux-picker-button[aria-expanded="true"],
.acpmux-mode .acpmux-picker-button:hover { color: var(--agent-text); }
.acpmux-send { width: 32px; height: 32px; }
.acpmux-context-ring { width: 32px; height: 32px; }
/* The composer's attachment row (Composer.tsx AttachmentChip), laid out like a native chat composer:
the row sits inside the composer at the text's own inset, the attachments keep a gap between them
and above the text, and each thumbnail's corners nest in the composer's (inner radius = the
Expand Down Expand Up @@ -2685,7 +2726,9 @@
user formats. Theme tokens only. */
.acpmux-md-field {
position: relative;
max-height: 40vh;
/* Keep long drafts inside the composer. Eight lines matches the reference composer and
leaves the transcript and location shelf anchored while the draft grows. */
max-height: calc(22px * 8 + 23px);
overflow-y: auto;
padding: 15px 16px 8px;
font-size: 15px;
Expand Down

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Resources/markdown-viewer/webviews-app/chunks/Tooltip.mjs

Large diffs are not rendered by default.

52 changes: 26 additions & 26 deletions webviews/src/agent-session/acpmux/Composer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -583,32 +583,6 @@ export function Composer({
{t(blocked.reason)}
</p>
)}
<ComposerContext
projectChoices={projectChoices}
onBrowseProject={onBrowseProject}
summary={snapshot.summary}
sessions={snapshot.sessions}
peers={snapshot.peers}
started={(snapshot.summary?.turnCount ?? 0) > 0 || snapshot.rows.length > 0}
onProject={
onProject &&
((cwd, peer) => {
onProject(cwd, peer);
field.current?.focus();
})
}
localName={localName}
movedTo={movedTo}
busy={snapshot.isWorking}
onMove={
onMove &&
((cwd) => {
const move = onMove(cwd);
setAttachments((current) => [...current.filter((item) => !item.move), moveAttachment(move)]);
field.current?.focus();
})
}
/>
{findingFiles &&
searchFiles &&
form.current?.parentElement &&
Expand Down Expand Up @@ -792,6 +766,32 @@ export function Composer({
</span>
</div>
</div>
<ComposerContext
projectChoices={projectChoices}
onBrowseProject={onBrowseProject}
summary={snapshot.summary}
sessions={snapshot.sessions}
peers={snapshot.peers}
started={(snapshot.summary?.turnCount ?? 0) > 0 || snapshot.rows.length > 0}
onProject={
onProject &&
((cwd, peer) => {
onProject(cwd, peer);
field.current?.focus();
})
}
localName={localName}
movedTo={movedTo}
busy={snapshot.isWorking}
onMove={
onMove &&
((cwd) => {
const move = onMove(cwd);
setAttachments((current) => [...current.filter((item) => !item.move), moveAttachment(move)]);
field.current?.focus();
})
}
/>
</form>
);
}
Expand Down
130 changes: 87 additions & 43 deletions webviews/src/agent-session/acpmux/ComposerContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type Summary = NonNullable<AcpmuxSnapshot["summary"]>;
type Session = AcpmuxSnapshot["sessions"][number];
type Location = { id: string; label: string; detail?: string };

/// The small location row above the composer. New chats can choose a local or Cloud
/// The location tray attached below the composer. New chats can choose a local or Cloud
/// computer and one of its known folders. Once the first turn starts the computer is a label and
/// the folder moves the chat (`onMove`), except while a turn runs.
export function ComposerContext({
Expand Down Expand Up @@ -84,56 +84,99 @@ export function ComposerContext({
if (!currentComputer && !currentFolder && !projectChoices) return null;
const readOnly = started || onProject === undefined;
const moves = started && onMove !== undefined && !busy;
const branch = summary?.branch;
return (
<div className="acpmux-composer-context" data-readonly={readOnly ? "true" : undefined}>
<LocationPicker
label={t(CONTEXT_LABELS.computer)}
menu="Computer"
value={currentComputer?.label ?? t(CONTEXT_LABELS.chooseComputer)}
options={computers}
selected={selectedComputer}
disabled={readOnly}
onPick={(id) => {
if (!readOnly && id !== selectedComputer) {
setSelectedComputer(id);
const folder = availableFolders(summary, sessions, id)[0]?.id;
if (folder) onProject?.(folder, id === "local" ? undefined : id);
}
}}
/>
<span className="acpmux-context-divider" aria-hidden="true">
·
</span>
{!readOnly && selectedComputer === "local" && projectChoices ? (
<FolderMenu
label={t(CONTEXT_LABELS.folder)}
menu="Location"
folders={folders}
current={currentFolder}
onPick={(cwd) => onProject?.(cwd)}
onBrowse={onBrowseProject}
/>
) : (
<div className="acpmux-location-leading">
{!readOnly && selectedComputer === "local" && projectChoices ? (
<FolderMenu
label={t(CONTEXT_LABELS.folder)}
menu="Location"
folders={folders}
current={currentFolder}
onPick={(cwd) => onProject?.(cwd)}
onBrowse={onBrowseProject}
/>
) : (
<LocationPicker
label={t(CONTEXT_LABELS.folder)}
menu="Location"
value={currentFolder ? projectLabel(currentFolder) : t(CONTEXT_LABELS.chooseFolder)}
options={folders}
selected={currentFolder}
disabled={readOnly && !moves}
icon={<FolderIcon />}
allowPath
onPick={(cwd) => {
if (moves) {
if (cwd !== currentFolder) onMove?.(cwd);
} else if (!readOnly) onProject?.(cwd, selectedComputer === "local" ? undefined : selectedComputer);
}}
/>
)}
<LocationPicker
label={t(CONTEXT_LABELS.folder)}
menu="Location"
value={currentFolder ? projectLabel(currentFolder) : t(CONTEXT_LABELS.chooseFolder)}
options={folders}
selected={currentFolder}
disabled={readOnly && !moves}
icon={<FolderIcon />}
allowPath
onPick={(cwd) => {
if (moves) {
if (cwd !== currentFolder) onMove?.(cwd);
} else if (!readOnly) onProject?.(cwd, selectedComputer === "local" ? undefined : selectedComputer);
label={t(CONTEXT_LABELS.computer)}
menu="Computer"
value={currentComputer?.label ?? t(CONTEXT_LABELS.chooseComputer)}
options={computers}
selected={selectedComputer}
disabled={readOnly}
onPick={(id) => {
if (!readOnly && id !== selectedComputer) {
setSelectedComputer(id);
const folder = availableFolders(summary, sessions, id)[0]?.id;
if (folder) onProject?.(folder, id === "local" ? undefined : id);
}
}}
/>
)}
</div>
{branch && <BranchPicker branch={branch} />}
</div>
);
}

function BranchPicker({ branch }: { branch: string }) {
const [open, setOpen] = useState(false);
return (
<span className="acpmux-location-picker" title={branch}>
<Menu open={open} onOpenChange={setOpen}>
<MenuButton className="acpmux-location-button" label={t("changes.scope.branch")}>
<LocationFace icon={<BranchIcon />} value={branch} chevron />
</MenuButton>
<MenuPopup side="top" className="acpmux-menu acpmux-location-menu" align="end">
<MenuRadioGroup value={branch} onValueChange={() => undefined}>
<MenuRadioItem value={branch} disabled className="acpmux-menu-item">
<span className="acpmux-menu-text">
<span className="acpmux-menu-label">{branch}</span>
</span>
</MenuRadioItem>
</MenuRadioGroup>
</MenuPopup>
</Menu>
</span>
);
}

function BranchIcon() {
return (
<svg
className="acpmux-icon acpmux-location-icon"
width={14}
height={14}
viewBox="0 0 16 16"
fill="none"
stroke="currentColor"
strokeWidth={1.25}
aria-hidden="true"
>
<circle cx="4" cy="3" r="1.5" />
<circle cx="4" cy="13" r="1.5" />
<circle cx="12" cy="13" r="1.5" />
<path d="M4 4.5v5A3.5 3.5 0 0 0 7.5 13H10.5M4 6.5A3.5 3.5 0 0 1 7.5 3H10" />
</svg>
);
}

function computerId(summary?: Summary): string {
return summary?.hostKind === "cloud" && (summary.peer || summary.host) ? (summary.peer ?? summary.host)! : "local";
}
Expand Down Expand Up @@ -267,7 +310,7 @@ function FolderMenu({
<MenuButton className="acpmux-location-button" label={label}>
<LocationFace icon={<FolderIcon />} value={value} chevron />
</MenuButton>
<MenuPopup className="acpmux-menu acpmux-location-menu" align="end">
<MenuPopup side="top" className="acpmux-menu acpmux-location-menu" align="end">
Comment thread
cursor[bot] marked this conversation as resolved.
{folders.length > 0 && (
<>
<div className="acpmux-location-folders">
Expand Down Expand Up @@ -411,6 +454,7 @@ function LocationPicker({
anchor={open ? trigger.current : null}
label={label}
className="acpmux-menu acpmux-location-menu"
side="top"
Comment thread
cursor[bot] marked this conversation as resolved.
>
<Combobox
suggestions={suggestions}
Expand Down
2 changes: 1 addition & 1 deletion webviews/src/agent-session/acpmux/ComposerPickers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ export function ComposerPickers({
button={
<>
<ShieldIcon />
<span>{mode?.name ?? t(PICKER_LABELS.mode)}</span>
<span className="acpmux-mode-text">{mode?.name ?? t(PICKER_LABELS.mode)}</span>
<ChevronIcon />
</>
}
Expand Down
Loading
Loading