From 8bf86ecb92670a7dc317ae2d7a5e31a826fa7f73 Mon Sep 17 00:00:00 2001 From: indifferentketchup Date: Fri, 29 May 2026 12:46:40 +0000 Subject: [PATCH] web(coder): keep composer refresh on the top line + icon-only Mode picker on mobile The AgentComposerBar refresh button wrapped to a second line on mobile: the status dot had ml-auto (pinned to the far-right edge) and the refresh button followed it in DOM order, overflowing past the edge. Group the dot + refresh into one right-aligned (ml-auto) unit so the refresh stays on the top line. Also add an iconOnly option to CompactPicker and render the Mode (permission) picker icon-only on mobile (shield + chevron, no label; aria-label/title + tap-to-open list still convey the selection) to free row width. Desktop unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 4 ++ apps/web/src/components/AgentComposerBar.tsx | 43 ++++++++++++-------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff36f33..47c4f59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes per release tag. Most recent on top, ordered by tag creation date (which matches the git history). Tag names follow `vMAJOR.MINOR.PATCH-slug` — the slug describes what shipped, so the tag name alone is enough to recall the batch. +## v2.5.8-mobile-composer-row — 2026-05-29 + +Mobile fix for the `AgentComposerBar`: the refresh button was wrapping to a second line. Root cause was layout order, not width — the status dot carried `ml-auto` (pinned to the far-right edge) and the refresh button followed it in DOM order, so it overflowed and wrapped. The dot + refresh are now one right-aligned (`ml-auto`) unit, keeping the refresh on the top line. Additionally, `CompactPicker` gained an `iconOnly` option and the Mode (permission) picker now renders icon-only on mobile (shield + chevron, no "Bypass"/"Plan" text label; `aria-label`/`title` and the tap-to-open list still convey the value) to free row width. Desktop is unchanged (full labels). Web-only change. + ## v2.5.7-claude-models-and-picker-fix — 2026-05-29 Two provider-layer changes. **(1) Fix the empty provider picker** — a regression from `v2.5.5` (Phase 2): on a cache miss `getProviderSnapshot` returned synchronous `installed:false` `loading` entries, which `AgentComposerBar` filters out (`e.installed && e.status !== 'error'`); with the client-side poll deferred to Phase 5, a single fetch landed on `loading` forever and no providers appeared. `getProviderSnapshot` now awaits the build and returns terminal entries (the sync `loading` return is deferred until Phase 5 ships the poll); builds stay fast via the tier-2 cold-probe skip. **(2) Claude models** — the list was a hardcoded 2-entry static list (Opus 4 / Sonnet 4, May 2025), and the v2.3 config schema's `models`/`additionalModels` were parsed but never wired. `buildResolvedRegistry` now carries config `models` (replace) + `additionalModels` (merge) onto `ResolvedProviderDef`, and `provider-snapshot` applies them to every ready model list — so `/data/coder-providers.json` can add or replace any provider's models with no code change. Claude `staticModels` bumped to `opus`/`sonnet`/`haiku` latest-aliases plus pinned `claude-opus-4-8` / `claude-sonnet-4-6` / `claude-haiku-4-5-20251001` (passed verbatim to `claude --model`; the CLI accepts both aliases and pinned full names). +2 unit tests (109 total). Builds on `v2.5.6-provider-lifecycle-phase3`. diff --git a/apps/web/src/components/AgentComposerBar.tsx b/apps/web/src/components/AgentComposerBar.tsx index 6672fca..a342441 100644 --- a/apps/web/src/components/AgentComposerBar.tsx +++ b/apps/web/src/components/AgentComposerBar.tsx @@ -92,9 +92,11 @@ interface PickerProps { options: Array<{ id: string; label: string }>; onPick: (id: string) => void; icon?: React.ReactNode; + /** Mobile: render icon + chevron only (no value label) to save row width. */ + iconOnly?: boolean; } -function CompactPicker({ label, value, disabled, options, onPick, icon }: PickerProps) { +function CompactPicker({ label, value, disabled, options, onPick, icon, iconOnly }: PickerProps) { const { isMobile } = useViewport(); const [open, setOpen] = useState(false); const currentLabel = options.find((o) => o.id === value)?.label ?? (value || label); @@ -129,7 +131,7 @@ function CompactPicker({ label, value, disabled, options, onPick, icon }: Picker className="inline-flex items-center gap-1 min-h-[44px] px-1.5 rounded text-xs text-muted-foreground hover:text-foreground disabled:opacity-40" > {icon} - {currentLabel} + {!iconOnly && {currentLabel}} setOpen(false)} title={label}> @@ -290,6 +292,7 @@ export function AgentComposerBar({ projectPath, value, onChange, onProviderComma options={modeOptions} onPick={(modeId) => persist({ ...value, modeId })} icon={} + iconOnly /> } /> )} - {connected !== undefined && ( - - )} - + {/* Status dot + refresh as one right-aligned unit so the refresh button + stays on the top line instead of wrapping past the edge-pinned dot. */} +
+ {connected !== undefined && ( + + )} + +
); }