Commit Graph

1444 Commits

Author SHA1 Message Date
Kayla はな 660fa9478f style(site): use shorthand for boolean JSX props (#25096) 2026-05-13 10:56:50 -06:00
Kayla はな 638e2220e9 chore: refactor BuildIcon and remove useClassName (#25017) 2026-05-08 14:11:49 -06:00
Kayla はな 9fd2cc78fe refactor(site): migrate more styles from emotion to tailwind (#24914) 2026-05-07 11:09:25 -06:00
TJ 6737e2588e fix(site): reduce agents beta badge size from sm to xs (#25011)
Reduces the beta badge size in the Coder Agents UI from `sm` to `xs` for
better visual balance with the logo.

## Changes

- Added `xs` size variant to `FeatureStageBadge`, styled to match the
existing `Badge` component's `xs` variant (`text-2xs`, `h-[18px]`,
`border-0`, `rounded`)
- Updated both usages in `AgentPageHeader` (mobile) and `AgentsSidebar`
(desktop) from `size="sm"` to `size="xs"`
- Added `ExtraSmallBeta` Storybook story for the new size

> Generated by Coder Agents
2026-05-06 18:25:41 -07:00
Kayla はな 9d1315ffba refactor(site): align user settings layout with organization settings (#25016) 2026-05-06 17:29:01 -06:00
Jake Howell 8c2b1c7d69 chore: de-emotion style constant (#24835)
This pull-request looks at all (most) of our instances of `const styles
= { ... }` and attempts to smooth them down into the minimum viable
Tailwind equivalent 🙂
2026-05-07 09:21:24 +10:00
Kayla はな 4e1dccaabd refactor: remove ChooseOne component (#24983) 2026-05-06 12:08:51 -06:00
Kayla はな 21a877df84 feat: update OrganizationMembersPage role editing to match new designs (#24858) 2026-05-05 13:18:01 -06:00
Kayla はな 162acaf8bf feat: update UsersPage role editing to match new designs (#24857) 2026-05-04 11:19:21 -06:00
Ben Potter f6eccbab23 feat(site/src): add ports submenu to WorkspacePill in agents chat (#24887)
Adds a **Ports (n) >** submenu item to the `WorkspacePill` dropdown
shown in the `/agents` chat UI when a workspace is attached, sitting
alongside VS Code, Terminal, and other app items.

The submenu shows a **Listening Ports** section with clickable port
links that open in a new tab, a **Shared Ports** section with
sharing-level icons when any ports are shared, and a **Manage sharing**
footer link to the workspace detail page. Port data is fetched on a 5s
polling interval while the dropdown is open and only when the agent is
connected. The trigger is disabled when the workspace is not running.

Also adds `DropdownMenuSub`, `DropdownMenuSubTrigger`, and
`DropdownMenuSubContent` to the shared `DropdownMenu` component for use
here and future consumers.

![ports submenu
demo](https://raw.githubusercontent.com/coder/coder/screenshots/ports-submenu/ports-submenu-demo.gif)

<details>
<summary>Implementation notes</summary>

- `host` for port-forward URL construction comes from
`useProxy().proxy.preferredWildcardHostname`, the same source used by
`PortForwardButton` on the workspace detail page.
- Port queries are gated on `isOpen && agent.status === "connected"` so
no requests fire when the dropdown is closed or the agent is
disconnected.
- Shared ports that overlap with listening ports are deduplicated: they
only appear in the Shared section.
- Port sharing controls (create/update/delete) are intentionally
excluded to keep the agents toolbar lightweight; the "Manage sharing"
link surfaces the workspace page for that.
- The port count badge on the trigger (`Ports (n)`) uses the raw
listening-port count, matching the existing `PortForwardButton` behavior
on the workspace detail page.
</details>

> Generated by [Coder Agent](https://coder.com)
2026-05-04 11:56:51 -05:00
Thomas Kosiewski 69610cca75 feat(site/src): add Known Model autocomplete and frontend defaults (#24842)
Replaces the blank Model Identifier free-text input on the **Add Model**
page with provider-scoped Known Model autocomplete and frontend-only
metadata defaults for native OpenAI and Anthropic providers. Selecting a
Known Model, or typing an exact canonical identifier and blurring the
field, prefills `contextLimit`, the appropriate max-output-tokens field,
and flat base pricing in the existing form. Edit mode, duplicate mode,
and unsupported providers preserve the existing plain `Input` behavior
and submit payload byte-for-byte.

The catalog is curated TypeScript records sourced from `models.dev`,
scoped initially to 6 OpenAI and 5 Anthropic models in declared display
order. The pure `applyKnownModelDefaults` helper only writes a field
when its current value still equals the form's initial value (or was
last applied by Known Model defaulting in this form session, tracked
cumulatively across selections). It never sets `compressionThreshold` or
any reasoning/thinking fields, ignores tiered pricing, and never writes
to the `model` field (canonicalization is the caller's responsibility).

This PR also makes two narrow, additive changes outside the panel
directory:

- `site/src/components/Autocomplete/Autocomplete.tsx` gains optional
`triggerAriaInvalid`, `triggerAriaDescribedBy`, and `onEscapeKeyDown`
props so the new catalog branch can preserve `aria-invalid` /
`aria-describedby` parity with the plain input and observe Escape close
intent reliably across the Radix portal. Existing `Autocomplete`
consumers are unaffected; `stopPropagation` is gated on
`onEscapeKeyDown` being provided.
-
`site/src/pages/AgentsPage/components/ChatModelAdminPanel/modelConfigFormLogic.ts`
exports `deepGet` / `deepSet` so the defaulting helper can reuse them
instead of re-implementing the same path traversal.

No backend, API, SDK, or DB changes. No edits to `ModelsSection.tsx`,
`ModelConfigFields.tsx`, `pricingFields.ts`, or
`providerPolicyDefaults.ts`.

## Validation

- 37 colocated unit tests across `knownModels/` (catalog, search,
exact-canonical lookup, exact-alias lookup, badge, defaulting helper).
- 134 unit tests across the full ChatModelAdminPanel directory pass.
- 50 Storybook play tests on `ChatModelAdminPanel.stories.tsx` pass,
including 17 DEREM-traceable interaction tests covering each plan-listed
and review-driven scenario (open-no-error, Escape cancellation,
sequential selection, double-apply guard, blur-canonical, alias
cancellation, provider-change reset, ARIA parity, no-options copy,
off-catalog substring commit, stale-cost-field, off-catalog
interleaving, chain tracking, keyboard selection, clearable-disabled,
off-catalog punctuation variant).
- `tsc -p .` passes.

## Dogfooding

Storybook was run locally and the user-facing flows were exercised
end-to-end via `agent-browser`, capturing screenshots for:

1. OpenAI happy path (selection → defaults applied note → populated
fields).
2. Anthropic happy path (selection → populated fields,
reasoning/thinking blank).
3. Unsupported provider fallback (Google plain input, no popover).
4. OpenAI suggestion popover at empty focus (declared catalog order,
context badges).
5. OpenAI search filter (typing `5.4` filters to GPT-5.4 / 5.4 mini /
5.4 nano).
6. Edit mode plain input (autocomplete correctly gated to add mode
only).
7. DEREM-3: empty popover open on Add Model — no premature `Model ID is
required.` error.
8. DEREM-1: autocomplete trigger `aria-invalid="true"` and
`aria-describedby` matching the rendered error element.
9. DEREM-6: exact `No matching known models. You can still use this
identifier.` copy.


---

<details>
<summary>📋 Implementation Plan</summary>

# Plan: Known Model autocomplete and frontend-only defaults for Chat
Model Admin

## Goal

Improve the admin Add Model onboarding flow by replacing the blank Model
Identifier experience with provider-scoped Known Model discovery
suggestions for native OpenAI and Anthropic providers. Selecting a Known
Model, or typing an exact canonical Known Model identifier and blurring
the field, should prefill safe objective model metadata in the existing
form without changing backend APIs, database schema, or runtime
behavior.

The primary UX goal is discovery for admins who do not know exact
provider model identifiers or metadata. Typing convenience is a
secondary benefit.

## Evidence and current code facts

- The current Model Identifier field is a plain free-text `Input` in
`site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelForm.tsx`.
It submits as `model` and is only validated as a non-empty string.
- The provider selector is disabled in edit and duplicate modes. In add
mode, `ModelsSection.tsx` keys the form by provider, so provider changes
remount `ModelForm`.
- The shared `site/src/components/Autocomplete/Autocomplete.tsx`
primitive already supports free-text input with suggestions and is the
right UI primitive for this feature.
- `modelConfigFormLogic.ts` owns form initialization via
`buildInitialModelFormValues(...)`, and `modelConfigFormLogic.test.ts`
already covers this pure logic area.
- No frontend or backend Known Model catalog exists today.
- The database has a non-unique `(provider, model)` index, not a
uniqueness constraint. Multiple Model Configs can share the same
Provider and Model Identifier, so suggestions must not hide
already-configured models.
- `models.dev/api.json` has provider-keyed model metadata with canonical
IDs, names, limits, pricing, release dates, and `last_updated` values.
The Phase 1 catalog should copy a curated subset into TypeScript
records, not fetch at runtime.

## Domain language

Use these terms consistently in code, tests, docs, and review
discussion:

- **Provider**: configured external AI service such as native `openai`
or `anthropic`.
- **Model Config**: persisted admin-defined config row used by Coder
chat runtime.
- **Model Identifier**: exact provider API string submitted as `model`,
such as `gpt-5.5`.
- **Known Model**: curated frontend catalog entry with advisory metadata
for one canonical Model Identifier.
- **Model Catalog**: checked-in frontend-only list of Known Models.
- **Off-catalog Model Identifier**: user-entered Model Identifier that
does not match any Known Model and remains valid.
- **Default application**: copying advisory Known Model metadata into a
draft add-mode Model Config form.

## Resolved design decisions

### UX scope

- Implement this on the Add Model page/form only.
- Do not add provider success popups, provider-side calls to action, or
new deep-link behavior in this pass.
- Use `Autocomplete` only when all are true:
  - form mode is add;
  - selected Provider is native `openai` or native `anthropic`;
  - that Provider has Known Models.
- Edit mode, duplicate mode, and unsupported providers keep the existing
free-text input behavior.

### Suggestion behavior

- Suggestions open on focus only when the Model Identifier field is
empty.
- Once the field has text, suggestions open while typing or interacting
with the autocomplete.
- Empty unsupported-provider catalogs degrade silently to the existing
plain input behavior.
- When a supported provider has zero matches for a non-empty query, show
a non-blocking empty state such as: `No matching known models. You can
still use this identifier.`
- Suggestion rows show:
  - display name;
  - canonical Model Identifier;
  - context-window badge, for example `1.05M context`.
- Format context badges with a deterministic helper covered by tests,
for example `200K context`, `400K context`, and `1.05M context`.
- Do not show pricing, recommendations, capability tags, or
large-context caveats in suggestion rows.
- Keep catalog display order as product ordering. Do not show a visible
`Recommended` badge.

### Canonical IDs and aliases

- Selecting a Known Model always writes its canonical Model Identifier
into the form.
- Use non-date latest aliases as canonical onboarding IDs when the
provider exposes them, such as `gpt-5.5` or `claude-sonnet-4-6`.
- Date-pinned IDs may be aliases for search, but selecting a Known Model
writes the non-date canonical ID.
- Typing aliases filters suggestions but does not rewrite the field and
does not apply defaults by itself.
- Search over canonical ID, display name, and explicit aliases.
- Search is case-insensitive and normalizes spaces, hyphens,
underscores, and dots before substring matching.
- Aliases are objective name or identifier variants only. Do not include
editorial intent tags such as `best`, `cheap`, `fast`, `coding`, or
`reasoning`.
- Do not implement typo-tolerant fuzzy search in Phase 1.

### Default application rules

- Default application only runs in add mode.
- Explicit Known Model selection applies defaults immediately.
- Exact typed or pasted canonical Model Identifier applies defaults on
blur, not on every keystroke. This avoids prematurely applying `gpt-5.5`
while the admin is typing `gpt-5.5-pro`.
- Defaults fill only target fields whose current values still equal this
form session's initial values.
- Do not use Formik touched state as the source of truth for safety.
- Do not implement field-level provenance tracking in Phase 1.
- Capture an immutable `initialValuesRef` at `ModelForm` mount/remount
and compare against that snapshot for safe default application. Do not
compare against a live Formik reference that can drift.
- Do not reapply repeatedly for the same provider/model pair in a single
form session.
- The defaulting helper must return both the next values and the list of
applied form paths:

```ts
interface ApplyKnownModelDefaultsResult {
  values: ModelFormValues;
  appliedFields: readonly string[];
}
```

- Treat Model Identifier canonicalization separately from metadata
default application. `appliedFields` tracks populated metadata/form
paths only, not the `model` field change caused by selecting a Known
Model.
- Show an inline note near Model Identifier only when
`appliedFields.length > 0`, such as: `Defaults applied from GPT-5.5.
Review and adjust before saving.`
- Do not show a note for off-catalog identifiers, no-op Known Model
selections, or selections that only canonicalize the Model Identifier.

### Initial Model Catalog

Use curated TypeScript records with source metadata copied from
models.dev. Do not check in the full `models.dev/api.json` snapshot and
do not add a generator in Phase 1. Add a file-level comment that array
order controls suggestion order so future cleanup does not accidentally
change onboarding UX.

Initial native OpenAI entries, in display order:

1. `gpt-5.5`
2. `gpt-5.5-pro`
3. `gpt-5.4`
4. `gpt-5.4-mini`
5. `gpt-5.4-nano`
6. `gpt-5.3-codex`

Initial native Anthropic entries, in display order:

1. `claude-opus-4-7`
2. `claude-opus-4-6`
3. `claude-sonnet-4-6`
4. `claude-haiku-4-5`
5. `claude-sonnet-4-5`

Do not include GPT-4.x, pre-5.3 GPT models, or Claude models older than
4.5 in this onboarding catalog unless product intentionally expands
scope.

Each Known Model record should include:

- provider;
- canonical Model Identifier;
- display name;
- aliases;
- source metadata, including `sourceName: "models.dev"`,
`sourceRetrievedAt`, and the model record's `last_updated` value;
- `contextLimit` from `limit.context`;
- `maxOutputTokens` from `limit.output`;
- flat base pricing from supported `cost.*` fields.

### Field mapping

- `models.dev.limit.context` maps to `contextLimit`.
- `models.dev.limit.output` maps to the selected provider's exact
max-output-tokens field when one exists, otherwise to generic
`config.maxOutputTokens`.
- Never fill both generic and provider-specific output-token fields for
the same Known Model.
- Ignore `models.dev.limit.input` unless the current form schema already
exposes an exact matching field.
- Map only flat base pricing fields that the existing form can persist:
  - `cost.input`;
  - `cost.output`;
  - `cost.cache_read`;
  - `cost.cache_write`.
- Reuse `pricingFields.ts` or the existing pricing field descriptors
instead of hard-coding cost form paths.
- If `cache_read` or `cache_write` is absent from a models.dev entry,
leave the corresponding field at its initial value and do not include it
in `appliedFields`.
- Ignore tiered pricing such as `context_over_200k` in Phase 1. Add a
code comment in the adapter explaining that Coder currently persists
flat pricing only.
- Do not show a UI caveat for tiered pricing in Phase 1.
- Do not set `compressionThreshold` from Known Models.
- Do not prefill provider-specific reasoning or thinking fields in Phase
1, including:
  - OpenAI `reasoningEffort` and `reasoningSummary`;
  - Anthropic `sendReasoning`, `effort`, and `thinking.budgetTokens`.

## Proposed file structure

Use `knownModels/` rather than `modelDefaults/` because the data powers
both discovery and default application.

New files:

-
`site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/types.ts`
-
`site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/openai.ts`
-
`site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/anthropic.ts`
-
`site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/index.ts`
-
`site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/applyKnownModelDefaults.ts`
-
`site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelIdentifierField.tsx`

Existing files to modify:

-
`site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelForm.tsx`
-
`site/src/pages/AgentsPage/components/ChatModelAdminPanel/modelConfigFormLogic.ts`
-
`site/src/pages/AgentsPage/components/ChatModelAdminPanel/modelConfigFormLogic.test.ts`
-
`site/src/pages/AgentsPage/components/ChatModelAdminPanel/ChatModelAdminPanel.stories.tsx`

Documentation artifacts to keep in sync if implementing from a clean
workspace:

- `site/src/pages/AgentsPage/components/ChatModelAdminPanel/CONTEXT.md`
-
`site/src/pages/AgentsPage/components/ChatModelAdminPanel/docs/adr/0001-frontend-known-model-catalog.md`

## Implementation plan

### Phase 1: Red, define pure behavior first

1. Add tests in `modelConfigFormLogic.test.ts` or a colocated
`knownModels` test file for:
   - provider-scoped lookup;
   - normalized alias search;
   - canonicalization on selection;
   - unknown model leaves values unchanged;
   - exact canonical ID lookup;
   - safe initial-value patching;
- `appliedFields` output that excludes Model Identifier
canonicalization;
   - tiered pricing ignored;
   - missing cache pricing fields left at initial values;
   - compression threshold not populated;
   - reasoning/thinking fields not populated;
- output-token mapping prefers provider-specific exact field and never
fills both;
   - context badge formatting.
2. Add lifecycle tests where feasible:
- provider change in add mode remounts the form and resets
`initialValuesRef`, `lastAppliedProviderModelRef`, and inline
default-feedback state.
3. Add edge-case tests for event and reapplication semantics:
- selecting `gpt-5.5` then blurring does not apply defaults a second
time;
- typing `gpt-5.5-pro` then blurring applies only pro defaults, never
prefix `gpt-5.5` defaults;
- selecting one Known Model, then another, does not overwrite fields
already populated by the first selection because they no longer match
initial values;
- typing an alias then blurring does not canonicalize or apply defaults;
- an Off-catalog value for a supported provider remains valid and
preserves existing required-field validation behavior.
4. Add tests for the initial OpenAI and Anthropic catalog entries to
ensure IDs, source metadata, and display order remain intentional.

Quality gate: targeted unit tests fail for missing implementation.

### Phase 2: Green, add Known Model catalog and pure helpers

1. Add `knownModels/types.ts` with readonly types for catalog records
and source metadata.
2. Add `knownModels/openai.ts` and `knownModels/anthropic.ts` with the
initial catalog entries and file-level refresh comments.
3. Add lookup and search helpers in `knownModels/index.ts`.
4. Add `applyKnownModelDefaults(...)` as a pure helper that accepts:
   - current form values;
   - initial form values;
   - selected provider;
   - Known Model;
   - provider field mapping helpers if needed.
5. Ensure assertions or explicit guards make impossible cases fail fast
during tests, for example missing provider, missing canonical ID, or
invalid source metadata.

Quality gate: targeted unit tests pass.

### Phase 3: Wire Model Identifier autocomplete UX

Autocomplete integration constraints:

- Control the shared `Autocomplete` with `inputValue` for the free-text
Model Identifier string and `value: KnownModel | null` for selected
suggestions.
- Pass pre-filtered Known Model options to `Autocomplete`; do not rely
on `cmdk` internal filtering once `inputValue` is controlled.
- Clear the selected `KnownModel | null` value whenever the admin types
arbitrary text that no longer corresponds to the selected Known Model.
- Guard selection and blur event ordering so selecting a row does not
cause the input blur handler to apply defaults a second time.
- Run exact-match blur behavior only when focus leaves the whole
field/combobox, not when focus moves into the suggestion list.
- Store the last-applied provider/model pair in form-local state or a
ref so add-mode provider remounts reset it naturally.
- Preserve the existing field contract: label, tooltip/help text,
`name`, validation error rendering, `aria-invalid`, `aria-describedby`,
disabled state, Formik blur/touched behavior, and submitted request
shape.

1. Add `ModelIdentifierField.tsx`.
2. Preserve existing plain `Input` markup for edit mode, duplicate mode,
and unsupported providers.
3. For add-mode supported providers, render `Autocomplete` with:
   - controlled free-text value tied to Formik's `model` field;
- custom row rendering with display name, canonical ID, and context
badge;
   - open-on-empty-focus behavior;
- non-blocking no-match copy for non-empty supported-provider queries;
   - keyboard support inherited from `Autocomplete`.
4. On Known Model selection:
   - set the form's `model` field to the canonical ID;
   - apply defaults immediately;
   - show inline feedback only if fields changed.
5. On blur:
- if the final field value exactly equals a Known Model canonical ID,
apply defaults safely;
   - do not auto-apply aliases on blur.
6. Track the last applied provider/model pair in the form session to
avoid repeated reapplication.

Quality gate: Storybook stories compile and the main interaction paths
work locally.

### Phase 4: Storybook and UX coverage

Add or extend `ChatModelAdminPanel.stories.tsx` with three user-visible
flows:

1. OpenAI happy path:
   - open Add Model for OpenAI;
   - focus empty Model Identifier;
   - suggestions appear;
   - select `GPT-5.5`;
   - assert `gpt-5.5` is in the input;
   - assert inline defaults note appears;
   - assert visible context limit and max output fields populate;
- expand the pricing section before asserting pricing fields, or keep
detailed pricing assertions in unit tests if the Storybook UI would
become brittle.
2. Anthropic happy path:
   - open Add Model for Anthropic;
   - select `Claude Opus 4.7`;
- assert canonical ID, visible context limit, and output field populate;
- expand the pricing section before asserting pricing fields, or keep
detailed pricing assertions in unit tests if the Storybook UI would
become brittle;
   - assert Anthropic reasoning/thinking fields remain blank.
3. Unsupported provider fallback:
   - open Add Model for Azure or openai-compat;
- assert Model Identifier behaves as plain free text and no suggestion
popover appears.

If practical, include one keyboard selection path in Storybook or manual
dogfooding:

- tab/focus Model Identifier;
- arrow to a suggestion;
- press Enter;
- verify canonicalization and defaults.

Quality gate: Storybook interaction tests pass for touched stories.

### Phase 5: Refactor and documentation pass

1. Keep catalog data isolated from UI rendering code.
2. Keep provider field mapping in one helper so future Google, Bedrock,
OpenRouter, or Azure support does not require editing defaulting logic
everywhere.
3. Ensure comments explain why tiered pricing and reasoning defaults are
excluded.
4. Update `CONTEXT.md` and ADR if implementation changes any design
decision captured there.
5. Run formatting and linting for touched frontend files.

Quality gate: no broad refactors beyond this feature's files.

## Validation commands

Use the repo's existing frontend validation commands, scoped where
possible:

- `pnpm -C site test <targeted ChatModelAdminPanel pattern>`
- `pnpm -C site test <targeted modelConfigFormLogic pattern>`
- `pnpm -C site test:storybook`
- `pnpm -C site lint:types`
- `pnpm -C site check`

If command names differ in this workspace, inspect `site/package.json`
and use the closest existing targeted commands. Do not claim success
until the actual commands run and pass.

## Dogfooding plan

Primary dogfood path is Storybook because this is a form-level UI
improvement using mocked admin data.

1. Run Storybook for the Chat Model Admin Panel.
2. Record a short video showing:
- OpenAI Add Model, focus empty Model Identifier, suggestions appear,
select `GPT-5.5`, defaults note appears, fields populate;
- Anthropic Add Model, select `Claude Opus 4.7`, fields populate,
reasoning/thinking fields remain blank;
- unsupported provider Add Model, Model Identifier stays free text with
no suggestions.
3. Capture screenshots for the final state of each flow and attach them
for review.
4. If implementation touches routing, `ModelsSection` URL state, or
provider pages, also run the local UI and record
`/agents/settings/models?newModel=openai` exercising the same OpenAI
flow.

## Acceptance criteria

- Add-mode native OpenAI and Anthropic Model Identifier fields provide
discovery suggestions from the curated Known Model catalog.
- Suggestions appear on empty focus and filter as the admin types.
- Unsupported providers, edit mode, and duplicate mode preserve the
current plain input behavior.
- Selecting a Known Model canonicalizes the field and safely applies
objective defaults.
- Exact typed/pasted canonical IDs apply defaults on blur.
- Off-catalog Model Identifiers remain valid and non-blocking.
- Display name, context limit, output-token field, and flat pricing fill
only when target fields still match initial values.
- Compression threshold, tiered pricing, and provider-specific
reasoning/thinking fields are not populated by Phase 1 defaults.
- Inline feedback appears only when default application changed at least
one field.
- Unit tests, Storybook coverage, typecheck, formatting, and lint/check
commands pass.
- Dogfooding includes screenshots and video recordings.

## Risks and mitigations

- **Catalog staleness**: models change frequently. Mitigate with source
metadata and clear file-level refresh comments.
- **Provider namespace mistakes**: Azure, Bedrock, OpenRouter, and
openai-compat use different identifier semantics. Mitigate by supporting
only native OpenAI and Anthropic in Phase 1.
- **Auto-fill surprise**: defaults can feel magical. Mitigate with
selection-first UX, blur-only exact-match behavior, initial-value safety
checks, and inline feedback.
- **Pricing inaccuracy for tiered models**: current form persists flat
prices only. Mitigate by mapping base flat prices only and documenting
tiered pricing as out of scope.
- **Reasoning option overreach**: generic source metadata does not map
cleanly to provider-specific controls. Mitigate by leaving
reasoning/thinking fields blank in Phase 1.
- **Overbroad UI changes**: replacing an input can affect accessibility
and keyboard users. Mitigate by using the shared Autocomplete primitive,
preserving plain Input fallback, and dogfooding keyboard selection.


</details>

---
_Generated with [`mux`](https://github.com/coder/mux) • Model:
`anthropic:claude-opus-4-7` • Thinking: `max`_
2026-05-04 16:40:11 +02:00
Jeremy Ruppel 8709d42fe0 feat(site): add loading <Spinner /> to AgentRow (#24825)
Adds a `<Spinner />` next to the log count during agent startup.

Also, there was some complexity in sizing the spinner because `<Badge
/>` automatically sizes any `svg`s it contains to `size-icon-xs`. In
order to maintain the `svg` sizing inside existing Badges across the
site, this introduces a new `svgSize` prop that defaults to `xs`.
Existing consumers will still get `[&_svg]:size-icon-xs` regardless of
the Badge size, but can now be overridden to `sm` or `lg` (there is no
`md`).

Also also fixes a tiny spacing issue with the warning triangle
🕵️‍♂️

<img width="1203" height="624" alt="Screenshot 2026-04-29 at 3 24 35 PM"
src="https://github.com/user-attachments/assets/e4fc4a3a-e88f-4253-a697-195f8a347230"
/>
2026-05-01 10:57:30 -04:00
Kayla はな 90bee3aaef refactor: reorganize and restyle some oddities (#24855) 2026-04-30 19:09:41 -06:00
Kayla はな 19535d9771 refactor: add modern DialogActions component (#24856) 2026-04-30 18:13:25 -06:00
Jeremy Ruppel 0754016512 feat: add role selector in the create user form (#24711)
Adds a role selector to the create user form so admins can assign
site-level roles at creation time rather than navigating to the user
afterward.

The `POST /api/v2/users` endpoint now accepts an optional `roles` field,
wiring it through to the existing `RBACRoles` field on the internal
`CreateUserRequest`. No database changes are needed since roles are
already stored inline on the user row.

On the frontend, a `RoleSelector` component renders the assignable roles
as a scrollable multiselect checklist with the non-assignable Member
role pinned as a non-interactive footer. The selector appears once a
login type is chosen.

Also adds a `condensed` size (690px) to `Margins` between the existing
`small` (460px) and `medium` (1080px), and exposes a `size` prop on
`FullPageForm`. The create user form uses `condensed` to give the role
selector more breathing room. Also fixes `MockUserAdminRole` and
`MockTemplateAdminRole` in test helpers to use hyphenated names
(`user-admin`, `template-admin`) matching the canonical names in the Go
RBAC layer.

Fixes `sortRolesByAccessLevel` in `UserRoleCell` to sort unranked roles
(e.g. `member`) after all known roles. Previously, `indexOf` returned -1
for unknown names, placing them first; now they receive
`POSITIVE_INFINITY` as their rank.

🤖 Generated with [Claude Code](<https://claude.ai/claude-code>)

---


https://github.com/user-attachments/assets/75e7c8c5-d0d2-481d-86e8-1fcfb574517c

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 10:57:10 -04:00
Kayla はな 5afb297042 refactor(site): remove Stack component (#24503)
## Summary

Remove the deprecated `Stack` component and replace all usages with
Tailwind flex utility classes.

- Replaced `<Stack>` → `<div className="flex flex-col gap-4">` (and
variants per props)
- Updated `StackLabel` and `FormFields` to no longer depend on `Stack`
- Deleted `Stack.tsx` and `Stack.stories.tsx`

74 files changed, -226 lines net.

> 🤖 Generated by Coder Agents

---------

Co-authored-by: Jake Howell <jacob@coder.com>
2026-04-28 12:02:13 -06:00
Jeremy Ruppel 33ffedf411 fix(site): use theme-aware color for agent row tab bottom border (#24737)
noticed on the light theme that the bottom border on the Agent row is
hardcoded white on white. switched this to an inverted border color so
you can see the tab border on both themes

before:

<img width="859" height="185" alt="Screenshot 2026-04-27 at 10 50 24 AM"
src="https://github.com/user-attachments/assets/aa81c9e2-3a11-456a-a5aa-84909982f5f7"
/>
<img width="875" height="207" alt="Screenshot 2026-04-27 at 10 50 37 AM"
src="https://github.com/user-attachments/assets/2e6d5a4a-08a4-4c85-ba9b-d81f33059fa0"
/>


after:

<img width="537" height="209" alt="Screenshot 2026-04-27 at 10 52 47 AM"
src="https://github.com/user-attachments/assets/6415a95d-e3b6-4f35-8819-b79ffe58ee59"
/>
<img width="543" height="222" alt="Screenshot 2026-04-27 at 10 52 29 AM"
src="https://github.com/user-attachments/assets/6631544f-9f32-4dab-b443-e88a6c6bb0ea"
/>
2026-04-27 12:35:17 -04:00
Jake Howell 056203f8fc fix: resolve outsideBox style for tabs (#24561)
> 🤖 This PR was modified by Coder Agents on behalf of Jake Howell.

Fixes the `outsideBox` variant styling for tabs and simplifies the kebab
overflow logic. The overflow calculation now accounts for `column-gap`
between tabs so the menu trigger appears at the correct breakpoint.

- Fix Tailwind hover selector syntax for `outsideBox` variant
(`[&_[data-slot=tabs-trigger]:hover]` instead of
`[&_[data-slot=tabs-trigger]]:hover`)
- Account for `column-gap` in `useKebabMenu` overflow calculation via a
new `getTabGap` helper
- Use content-box width (`getContentBoxWidth`) for the initial overflow
pass so it matches `ResizeObserver`'s `contentRect.width`
- Consolidate `calculateTabValues` into a single-pass loop, removing the
separate `findFirstTabIndex` function
- Drop `FC` wrapper in favor of inline prop destructuring across all tab
components
- Forward `ref` through `TabsList` so consumers can attach refs directly
- Add `useKebabMenu` unit tests covering both all-visible and overflow
scenarios
2026-04-25 11:32:04 +10:00
TJ e56b409873 fix(site): use highlight-orange for warning badge text and border (#24674)
## Summary

Fixes the warning badge color in light mode by switching text and border
tokens from `content-warning`/`border-warning` to `highlight-orange`.

## What changed

The `warning` variant in `Badge` was the only colored badge variant not
using the `highlight-*` token pattern:

| Variant | Before | After |
|---|---|---|
| warning (text) | `text-content-warning` | `text-highlight-orange` |
| warning (border) | `border-border-warning` | `border-highlight-orange`
|

In light mode, `content-warning` (HSL 27 96% 61%) produced a washed-out
orange. `highlight-orange` (HSL 30 100% 32%) gives a darker, more
legible result that matches the screenshot in the issue.

> [!NOTE]
> This PR was authored by Coder Agents.
2026-04-23 08:41:59 -07:00
christin 5cce3ee5f4 refactor(site): rename border-hover token to border-secondary (#24553)
Renames the `--border-hover` design token to `--border-secondary` and
updates the color values to work well on `surface-secondary`
backgrounds, preparing for the template creation flow.

- Light theme: `240 5% 65%` (`#A1A1AA`)
- Dark theme: `240 5% 26%` (`#3F3F46`)

All 7 component usages updated to use the new token name.

<details>
<summary>Context</summary>

The previous `border-hover` token used a single value (`#52525B`) for
both themes, which didn't provide enough contrast on `surface-secondary`
backgrounds. The rename to `border-secondary` better reflects its
semantic role as a secondary border color rather than a hover-specific
one, and the updated values give proper contrast in both light and dark
themes. This change is a prerequisite for the upcoming template creation
flow work.

</details>

> 🤖 Generated by Coder Agents
2026-04-22 09:54:28 +02:00
Jake Howell 67c57abb63 chore: tighten .vscode IDE and typescript configuration (#24537)
This branch tightens import hygiene and editor guidance to reduce
accidental use of legacy or discouraged patterns.
It also updates consumers too, by propagating the new `lucide-react`
import convention across the existing UI surfaces that reference those
icons.

- Updated `.vscode/settings.json` to prefer non-relative imports and
improve TypeScript auto-import behavior.
- Re-enabled and expanded Biome restricted-import enforcement in
`biome.jsonc` for migration guardrails.
- Added/used `lucide-react` `-Icon` naming conventions for clarity and
consistency.
- Updated consumers too across components, modules, and pages so the new
import rules are applied end-to-end.
2026-04-21 13:45:08 +10:00
Jake Howell 9324c16c97 chore(site): demui <CodeExample /> (#24528)
This pull-request takes our `<CodeExample />` component and removes the
MUI specific styles.

---------

Co-authored-by: Jeremy Ruppel <jeremyruppel@users.noreply.github.com>
2026-04-21 13:40:37 +10:00
Jake Howell 7f1b9cb648 chore(site): demui <Avatar /> and <AvatarCard /> (#24527)
This pull-request takes our `<Avatar />` and `<AvatarCard />` component
and removes the bespoke styles to them. As an added bonus I took care of
the `getExternalImageStylesFromUrl` helper util and cleaned out the
MUI-specific code to it.

There are some remnants of `@emotion/react` in
`getExternalImageStylesFromUrl` however we don't have a theme switcher
that could appropriately handle it yet.
2026-04-21 02:22:16 +10:00
Jaayden Halko 410f9a5e19 feat: allow renaming of agent chat title (#24489)
Co-authored-by: Coder Agents <noreply@coder.com>
2026-04-20 14:00:46 +01:00
Ethan 596e55b136 test: pin DateRangePicker calendar today to caller-supplied clock (#24517)
The `DateRangePicker` accepts a `now` prop that stories and tests use to
pin preset ranges and future-date disabling to a deterministic clock,
but it never forwarded that value to the underlying `Calendar`.
`react-day-picker` then computed its own `today` from wall-clock time,
which drives both the "today" highlight modifier and the initial visible
month — causing stories like `TemplateInsightsControls:Day` to _flake_
as real time advanced.

Thread `currentTime` into `react-day-picker`'s `today` prop. When `now`
is omitted (production usage), `currentTime` falls back to `new Date()`,
which matches `react-day-picker`'s existing default, so there is no
runtime behavior change.
2026-04-20 16:36:13 +10:00
christin 23f9e26796 refactor(site): replace shadcn color aliases with semantic design tokens (#24284)
The shadcn-compatible CSS aliases (`--background`, `--foreground`,
`--muted`,
`--muted-foreground`, `--primary`, `--primary-foreground`) were added as
part
of the Coder agents work with hardcoded HSL values that duplicated
existing
semantic design tokens. These non-standard color classes (`bg-muted`,
`text-muted-foreground`, `text-foreground`) had started spreading
through
components like Table, MultiUserSelect, and WorkspacesTable.

This PR makes two changes:

1. **CSS aliases now derive from canonical tokens via `var()`
references**
instead of duplicating HSL values. The aliases remain for `streamdown`
   and other external consumers, but `--background` now resolves to
   `--surface-primary` (`#FFFFFF` in light, was `#FAFAFA`), and the rest
   map to their semantic equivalents (`--content-primary`,
   `--surface-secondary`, `--content-secondary`, `--content-link`).

2. **Component classes replaced with semantic equivalents:**
   - `bg-muted` → `bg-surface-secondary`
   - `text-muted-foreground` → `text-content-secondary`
   - `text-foreground` → `text-content-primary`
   - `text-amber-400` → `text-content-warning`
   - `hsl(var(--background))` → `hsl(var(--surface-primary))`
   - `hsl(var(--muted-foreground))` → `hsl(var(--content-secondary))`

> This PR was initially created by Claude Opus 4.

Co-authored-by: Jaayden Halko <jaayden@coder.com>
Co-authored-by: Jaayden Halko <jaayden.halko@gmail.com>
2026-04-18 13:39:55 +01:00
Cian Johnston 383b10f71e fix(site): allow search by label in MultiSelectComboBox (#24421)
Relates to  https://linear.app/codercom/issue/CODAGT-103

- Add `keywords={[option.label]}` to `CommandItem` in
`MultiSelectCombobox` so cmdk's default filter matches against the
visible label text, not just the value (UUID)
- Extend `OpenCombobox` story with type-to-filter assertions
- Add "search filters by display name" step to `TemplateAllowlist` story

> 🤖

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-16 17:44:26 +01:00
Jaayden Halko 4064b602de chore: update design of add users dialog (#24287)
update design to match Figma design

Figma:
https://www.figma.com/design/klGTlHSPQwI4KBvAMdebrx/Customer-Usage-Controls-for-AI-Governance-Add-On?node-id=448-9898&m=dev

Before:

<img width="482" height="638" alt="Screenshot 2026-04-13 at 16 48 32"
src="https://github.com/user-attachments/assets/940dbbd0-4817-4365-aad2-08148550109c"
/>

After:

<img width="474" height="653" alt="Screenshot 2026-04-13 at 16 45 55"
src="https://github.com/user-attachments/assets/c63591fa-1513-4c66-b48f-82df794ca080"
/>
2026-04-16 13:48:08 +01:00
Jake Howell 074ff79af7 fix: restore kebab menu flex (#24359)
Agent log tabs could spill over the Copy/Download area, and the overflow
kebab sometimes never wired up because ResizeObserver ran with no node
or while inactive. This ties the hook to when the strip is real and
clips the tab column.

- `AgentRow`: `enabled` for `useKebabMenu` is `hasStartupFeatures &&
hasAnyLogs && showLogs`; `hasAnyLogs` is computed before the hook; tab
list wrapper gets `overflow-hidden`.
- `useKebabMenu`: attach `ResizeObserver` in `useLayoutEffect`; bail if
missing container or `!enabled || !isActive`; deps include `enabled` and
`isActive`.
- `TabsList` (`overflowKebabMenu`): add `min-w-0 w-full max-w-full` with
`flex-nowrap` so the list stays inside the flex column.
2026-04-16 22:06:36 +10:00
35C4n0r 517bb1f9f7 fix: prevent 'See all templates' from overlapping template list in New Workspace dropdown (#24356)
## Summary

Fixes the "See all templates" link overlapping template items in the New
Workspace dropdown.

## Root cause

Two compounding issues:

1. **`OverflowY` className was being overwritten, not merged.** The
component spread `...attrs` (which included the caller's `className`)
onto the div, silently replacing its own base classes
(`overflow-y-auto`, `shrink`, `w-full`). This meant the template list
never scrolled independently.

2. **`PopoverContent` has `overflow-y-auto` in its base styles.** With
the inner `OverflowY` not scrolling, the *entire popover* became the
scroll container. The "See all templates" footer was part of that
scrollable flow and overlapped template rows as the user scrolled.

## Fix

- **`OverflowY`**: Destructure `className` explicitly and merge it with
the base classes using `cn()` so `overflow-y-auto` and `shrink` are
always preserved.
- **`PopoverContent`**: Add `overflow-hidden flex flex-col` to make it a
non-scrolling flex container. Only the `OverflowY` child scrolls.
- **`OverflowY` usage**: Add `min-h-0` so the flex child can shrink
below its content size when the popover's available height is
constrained.

## Screenshot
<img width="1460" height="1072" alt="image"
src="https://github.com/user-attachments/assets/9b519f2d-9806-44ca-a354-12248de36952"
/>

> 🤖 Generated with [Coder Agents](https://coder.com/agents)
2026-04-15 20:03:30 +05:30
Mathias Fredriksson a1ef3043bb fix: prevent site storybook tests from hanging after completion (#23936)
The vitest process hung after all 2132 story tests passed because
leftover refetchInterval polls kept the Node.js event loop alive.
Components that set per-query refetchInterval override the
QueryClient default, causing HTTP requests through vite's proxy
to localhost:3000 (no backend) that never resolve cleanly.

Three fixes:

- preview.tsx: disable all automatic refetching defaults and cancel
  in-flight queries on story unmount via useEffect cleanup
- storybook.tsx: save/restore the original window.WebSocket in the
  withWebSocket decorator, clear pending timers in close()
- vite.config.mts: add explicit testTimeout, hookTimeout, bail, and
  retry settings to the storybook vitest project

Also fix 5 story files that imported from @testing-library/react
instead of storybook/test.
2026-04-14 12:19:55 +00:00
Jake Howell e0902e3c27 feat: demui the <LinearProgress /> dependency (#24275)
Adds a small LinearProgress (determinate + MUI-style dual-bar
indeterminate) and uses it on workspace build progress; drops MUI and
adds Tailwind keyframes + Storybook stories.

- New component under `site/src/components/LinearProgress/`
- `WorkspaceBuildProgress` switched off `@mui/material/LinearProgress`
- Added `bar-indeterminate` / `bar-indeterminate-2` animation keyframes

<img width="563" height="115" alt="image"
src="https://github.com/user-attachments/assets/bdcd8584-fa2d-487c-96f6-084891a9b084"
/>
2026-04-14 02:48:29 +10:00
Cian Johnston 11fe4972b6 fix(site): use readonly Organization[] and explicit is_default lookups (#24288)
`OrganizationAutocomplete` declared `options: Organization[]` (mutable),
but `useDashboard().organizations` returns `readonly Organization[]`.
Callers forced to spread to satisfy the type even though the component
never mutates the array.

While fixing this, found two more issues with how organization context
is resolved:

- `AgentCreateForm`, `ChatPageContent`, and `OrganizationRedirect` used
`organizations[0]` assuming the default org is first — nothing
guarantees that ordering.
- `ChatPageInput` was guessing the org from `useDashboard()` for file
uploads on existing chats, even though the chat already has
`organization_id`. In multi-org deployments, uploads could land in the
wrong org.

Changes:
- Widen `OrganizationAutocomplete` `options` to `readonly
Organization[]`
- Remove unnecessary `[...organizations]` spread in `AgentCreateForm`
- Replace all `organizations[0]` with `.find(o => o.is_default) ??
organizations[0]` for users not in the default org
- Eliminate `[...organizations].sort()` allocation in
`OrganizationRedirect` with direct `.find()` lookups (equivalent
priority: editable default → any editable → viewable default → any
viewable)
- Thread `chat.organization_id` from `AgentChatPage` →
`AgentChatPageView` → `ChatPageInput` so file uploads use the chat's
actual org instead of guessing

Closes #24285

> 🤖 PR initially created by Claude Opus 4.6
2026-04-13 14:28:48 +00:00
Jake Howell 58c6855c59 feat: de-mui <Skeleton /> component (#24278)
Replaces remaining MUI `<Skeleton />` usages with our shared `Skeleton`
component to keep loading states consistent and reduce MUI/Emotion
coupling. Also cleans up a few adjacent touched styles while preserving
existing behavior.

- Replace `@mui/material/Skeleton` with `#/components/Skeleton/Skeleton`
- Remove a few MUI-specific skeleton patterns at callsites
- De-mui adjacent Emotion styles
- Simplify filter layout handling with class-based responsive wrapping
2026-04-13 22:50:01 +10:00
Jake Howell 982739f3bf feat: add a debounce to menu filtering (#24048)
This pull-request implements a small debounce to ensure we aren't
constantly pinging the backend on each keystroke of an input.

<img width="962" height="317" alt="image"
src="https://github.com/user-attachments/assets/4f187c18-0dd8-4456-bcc1-59ad7ce9c7dd"
/>


https://github.com/user-attachments/assets/5787310a-2c1e-448a-a4b7-123eb9d50124
2026-04-11 15:12:03 +10:00
Kayla はな b149433138 chore: complete jest to vitest migration (#24216) 2026-04-10 14:04:24 -06:00
Cian Johnston 0a14bb529e refactor(site): convert OrganizationAutocomplete to fully controlled component (#24211)
Fixes https://github.com/coder/internal/issues/1440

- Convert `OrganizationAutocomplete` to a purely presentational, fully
controlled component
- Accept `value`, `onChange`, `options` from parent; remove internal
state, data fetching, and permission filtering
- Update `CreateTemplateForm` and `CreateUserForm` to own org fetching,
permission checks, auto-select, and invalid-value clearing inline
- Memoize `orgOptions` in callers for stable `useEffect` deps
- Rewrite Storybook stories for the new controlled API


> 🤖 Written by a Coder Agent. Reviewed by a human.
2026-04-10 13:56:43 +01:00
Jaayden Halko 19e0e0e8e6 perf(site): split InlineMarkdown out of Markdown to avoid loading PrismJS in initial bundle (#24192)
\`InlineMarkdown\` and \`MemoizedInlineMarkdown\` lived in
\`Markdown.tsx\`
alongside a static \`import { Prism as SyntaxHighlighter } from
"react-syntax-highlighter"\` — the full PrismJS build with ~300 language
grammars. Because \`DashboardLayout\` eagerly imports
\`AnnouncementBannerView → InlineMarkdown\`, every authenticated page
loaded and evaluated the entire Prism/refractor bundle on startup even
though syntax highlighting is only used in secondary views.

This PR moves \`InlineMarkdown\` and \`MemoizedInlineMarkdown\` into
their
own \`InlineMarkdown.tsx\` file that depends only on \`react-markdown\`
and
updates all six consumers to import from the new module.
\`Markdown.tsx\`
keeps the PrismJS import for the full \`Markdown\` component, which is
only reached through lazy-loaded routes.

> 🤖 Generated by Coder Agents
2026-04-10 07:34:31 +01:00
Kayla はな 9d6557d173 refactor(site): migrate some components from emotion to tailwind (#24182) 2026-04-09 10:33:01 -06:00
Kayla はな 224db483d7 refactor(site): remove mui from a few components (#24125) 2026-04-09 10:02:26 -06:00
Jake Howell 2e6fdf2344 fix: resolve <Badge /> incorrect sizes (#22539)
This pull-request makes a few changes to our `<Badge />` component to
bring it inline with Figma.

* Added all variants to the stories of Figma (they can vary per
badge-type, so its better we track everything).
* Removed the `border` variant of the component, border variants should
be on all `sm` and `md`.
* Added a hover effect to the `default` variant (per-design).
* Resolved issue with sizings of `xs` and `sm` plus resolved
iconography.
* Resolved issue with icons not showing at all on `xs` variants.
2026-04-09 19:55:59 +10:00
Danielle Maywood 86b919e4f7 refactor: replace useEffectEvent polyfill with native React 19.2 hook (#24060) 2026-04-08 11:17:11 +01:00
Kayla はな 1187b84c54 refactor(site): remove mui from icon components (#24117) 2026-04-07 17:32:05 -06:00
Jeremy Ruppel 45336bd9ce fix(site): use field value instead of controlled value in PasswordField (#24123)
`<PasswordField>`'s value should come from the field helpers, not from a
prop
2026-04-07 19:04:29 -04:00
Jake Howell 655d647d40 fix: resolve style not passing in <LogLine /> (#24111)
This pull-request resolves an regression where the spread was overriding
the required styles from the `react-window` virtualised rows. This was
causing the scroll to act a little crazy.
2026-04-07 17:54:16 +00:00
Jake Howell 21c08a37d7 feat: de-mui <LogLine /> and <Logs /> (#24043)
Migrated LogLine and Logs components from Emotion CSS-in-JS to Tailwind
CSS classes.

- Replaced Emotion `css` prop and theme-based styling with Tailwind
utility classes in `LogLine` and `LogLinePrefix` components
- Converted CSS-in-JS styles object to conditional Tailwind classes
using the `cn` utility function
- Updated log level styling (error, debug, warn) to use Tailwind classes
with design token references
- Migrated the Logs container component styling from Emotion to Tailwind
classes
- Removed Emotion imports and theme dependencies
2026-04-07 16:35:10 +00:00
Jake Howell 2bd261fbbf fix: cleanup useKebabMenu code (#24042)
Refactored the tab overflow hook by renaming `useTabOverflowKebabMenu`
to `useKebabMenu` and removing the configurable `alwaysVisibleTabsCount`
parameter.

- Renamed `useTabOverflowKebabMenu` to `useKebabMenu` and moved it to a
new file
- Removed the `alwaysVisibleTabsCount` parameter and hardcoded it to 1
tab as `ALWAYS_VISIBLE_TABS_COUNT`
- Removed the `utils/index.ts` export file for the Tabs component
- Updated the import in `AgentRow.tsx` to use the new hook name and
removed the `alwaysVisibleTabsCount` prop
- Refactored the internal logic to use a more functional approach with
`reduce` instead of imperative loops
- Added better performance optimizations to prevent unnecessary
re-renders
2026-04-08 02:25:18 +10:00
Jake Howell 6e5335df1e feat: implement new workspace download logs dropdown (#23963)
This PR improves the agent log download functionality by replacing the
single download button with a comprehensive dropdown menu system.

- Replaced single download button with a dropdown menu offering multiple
download options
- Added ability to download all logs or individual log sources
separately
- Updated download button to show chevron icon indicating dropdown
functionality
- Enhanced download options with appropriate icons for each log source

<img width="370" height="305" alt="image"
src="https://github.com/user-attachments/assets/ddf025f5-f936-499a-9165-6e81b62d6860"
/>
2026-04-07 15:27:43 +00:00
George K 86ca61d6ca perf: cap count queries and emit native UUID comparisons for audit/connection logs (#23835)
Audit and connection log pages were timing out due to expensive COUNT(*)
queries over large tables. This commit adds opt-in count capping: requests can
return a `count_cap` field signaling that the count was truncated at a threshold,
avoiding full table scans that caused page timeouts.

Text-cast UUID comparisons in regosql-generated authorization queries
also contributed to the slowdown by preventing index usage for connection
and audit log queries. These now emit native UUID operators.

Frontend changes handle the capped state in usePaginatedQuery and
PaginationWidget, optionally displaying a capped count in the pagination
UI (e.g. "Showing 2,076 to 2,100 of 2,000+ logs")

Related to:
https://linear.app/codercom/issue/PLAT-31/connectionaudit-log-performance-issue
2026-04-07 07:24:53 -07:00
Danielle Maywood aede045549 chore: bump @biomejs/biome from 2.2 to 2.4.10 (#24074) 2026-04-07 12:22:18 +01:00