Commit Graph
529 Commits
Author SHA1 Message Date
Jake Howell e81275a91c feat: cleanup <Tabs /> component (#23839)
This refactors `<Tabs />` into two clearer patterns: link tabs for route
navigation and Radix tabs for stateful tab panels. That gives us proper
accessibility semantics where we need them without overloading simple
navigation tabs.

As part of that split, this updates several consumers, adds coverage for
both variants, and cleans up some nearby styling.

- introduce Radix-backed tabs primitives for tabbed content
- move router-based tabs to `LinkTabs`
- update notifications, IdP sync, and workspace build pages to use
semantic tabs
- preserve route navigation tabs for groups and templates
- add stories/tests for both tab implementations
- simplify related layout and styling in touched components
2026-04-02 03:45:20 +11:00
Jake HowellandDanielle Maywood 4a363b0d85 fix: resolve <Alert /> button poor visibility (#22597)
Closes #22244

This pull-request makes our `<Alert />`'s more inline with the Figma
style-system, we're looking to ensure that these are vertically rendered
now and not horizontal WCAG nightmares.

---------

Co-authored-by: Danielle Maywood <danielle@themaywoods.com>
2026-04-01 16:41:45 +00:00
Danielle Maywood 28062862a0 chore(site): upgrade to Vite 8 (#23485) 2026-04-01 15:11:47 +00:00
Thomas Kosiewski 12f87acad6 feat(site): add terminal panel to chat sidebar (#23231)
## Add terminal panel to chat sidebar

Extract the reusable terminal runtime from `TerminalPage` into
`modules/terminal/` and wire it into the agents chat right sidebar as a
new **Terminal** tab.

### Changes

- **`modules/terminal/WorkspaceTerminal.tsx`** — Shared xterm +
websocket terminal component (container-sized, no route dependency)
- **`modules/terminal/WorkspaceTerminalAlerts.tsx`** — Moved from
`TerminalPage/` to shared module
- **`pages/AgentsPage/TerminalPanel.tsx`** — Sidebar wrapper around
`WorkspaceTerminal`
- **`pages/AgentsPage/AgentDetailView.tsx`** — Terminal tab added (gated
on `hasWorkspace`)
- **`pages/TerminalPage/TerminalPage.tsx`** — Slimmed to page-shell
using shared component

### Demo


[dogfood-terminal-demo.webm](https://github.com/user-attachments/assets/359200dc-f8e4-4a9a-b00b-923f142dc228)

### Behavior

- Terminal tab appears only when the chat has a workspace with a
connected agent
- Connects via the existing workspace agent PTY websocket
- Resizes correctly on panel width changes, expand/collapse, and
viewport resize
- `fitAddon.fit()` guarded against pre-renderer crashes (fixes proxy
access)
- Tab switching unmounts/remounts cleanly (reconnects via session token)
- No changes to Git or Desktop panel behavior

---

_Generated with [`mux`](https://github.com/coder/mux) • Model:
`anthropic:claude-opus-4-6` • Thinking: `xhigh`_
2026-04-01 13:38:23 +00:00
Jake Howell 3758b02595 fix: resolve <WorkspacePage /> colors (#23902)
This pull-request ensures that our borders and content are all inline
with the design-system, whilst also ripping out the old Material UI
based design system. Furthermore, we're enforcing the background
gradient to always be showing regardless of if `<ResourceMetadata />`
has content.

| Old | New |
| --- | --- |
| <img width="1624" height="1061" alt="image"
src="https://github.com/user-attachments/assets/0accc324-b012-43e4-bb13-ec3629fbc909"
/> | <img width="1624" height="1061" alt="image"
src="https://github.com/user-attachments/assets/e89c752a-057c-4256-9f8e-728d1f89a1fd"
/> |
2026-04-01 23:28:59 +11:00
Kayla はな b9f140e53e chore: remove Language objects (#23866) 2026-03-31 15:26:59 -06:00
Danielle Maywood e769d1bd7d fix(site): update story play functions after HelpTooltip→HelpPopover migration (#23876) 2026-03-31 21:50:05 +01:00
code-qtzl f44a8994da fix(site): improve keyboard navigation in help popovers (#23374) 2026-03-31 11:10:33 -06:00
Kayla はな 755e8be5ad chore: migrate some emotion styles to tailwind (#23817) 2026-03-31 10:07:33 -06:00
Atif Ali e3e17e15f7 fix(site): show accurate message and warning color for startup script failures in agent row (#23654)
The agent row tooltip showed "Error starting the agent" / "Something
went wrong during the agent startup" with a red border when a startup
script fails. This is misleading — the agent is started and functional,
only the startup script exited with a non-zero code.

Extracts shared message constants (`agentLifecycleMessages`,
`agentStatusMessages`) from `health.ts` so both the workspace-level
health classification and the per-agent-row tooltips reference the same
single source of truth. No more duplicated wording that can drift.

Changes:
- **`health.ts`**: Exports `agentLifecycleMessages` and
`agentStatusMessages` maps; `getAgentHealthIssue` now references them
instead of inline strings.
- **`AgentStatus.tsx`**: All lifecycle/status tooltip components
(`StartErrorLifecycle`, `StartTimeoutLifecycle`,
`ShutdownTimeoutLifecycle`, `ShutdownErrorLifecycle`, `TimeoutStatus`)
now import and render from the shared message constants.
`StartErrorLifecycle` icon changed from red (`errorWarning`) to orange
(`timeoutWarning`).
- **`AgentRow.tsx`**: `start_error` border changed from
`border-border-destructive` (red) to `border-border-warning` (orange).

Closes #23652
Refs #21389

> 🤖 This PR was created with the help of Coder Agents, and has been
reviewed by my human. 🧑‍💻
2026-03-31 12:04:51 +00:00
Cian JohnstonandCopilot 3190406de3 fix(site): stop workspace deletes playing hide-and-seek (#23641)
- Fix workspaces list invalidation after kebab-menu delete and add
Storybook coverage for the immediate `Deleting` state.

> 🤖 This PR was made by Coder Agents and read by me.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-31 11:36:47 +01:00
blinkagent[bot]andblink-so[bot] a00afe4b5a chore(site): update proxy menu dialog text (#23765)
Updates the descriptive text in the proxy selection dropdown menu to be
clearer and more concise.

**Before:**
> Workspace proxies improve terminal and web app connections to
workspaces. This does not apply to CLI connections. A region must be
manually selected, otherwise the default primary region will be used.

**After:**
> Workspace proxies improve terminal and web app connections. CLI
connections are unaffected. If no region is selected, the primary region
will be used.

---------

Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
2026-03-30 11:20:05 -07:00
Jaayden Halko 0e78156bcd fix: create scrollable proxy menu list (#23764)
This allows the proxy menu to scroll for very large numbers of proxies
in the menu.

<img width="334" height="802" alt="screenshot"
src="https://github.com/user-attachments/assets/f0e45b9c-5b77-43da-b566-28d0572fd56b"
/>
2026-03-30 09:39:30 +01:00
Kayla はな 04f7d19645 chore: additional typescript import modernization (#23722) 2026-03-27 16:41:56 -06:00
Charlie VoiselleandKayla はな 8c494e2a77 fix(site): sever opener in openAppInNewWindow for slim-window apps (#23117)
Split out from #23000.

## Problem

`openAppInNewWindow()` opens workspace apps in a slim popup without
`noopener`, leaving `window.opener` intact. Since workspace apps can
proxy arbitrary user-hosted content under the dashboard origin, this
exposes the Coder dashboard to tabnabbing and same-origin DOM access.

We cannot simply pass `"noopener"` to `window.open()` because the WHATWG
spec mandates that `window.open()` returns `null` when `"noopener"` is
present — indistinguishable from a blocked popup — which would break the
popup-blocked error toast.

## Solution

Use a two-step approach in `openAppInNewWindow()`:

1. Open `about:blank` first (without `noopener`) so we can detect popup
blockers via the `null` return
2. If the popup succeeded, sever the opener reference with `popup.opener
= null`
3. Navigate the popup to the target URL via `popup.location.href`

This preserves popup-block detection while eliminating the security
exposure.

## Tests

A vitest case in `AppLink.test.tsx` asserts that `open_in="slim-window"`
anchors do not carry `target` or `rel` attributes (since slim-window
opening is handled programmatically via `onClick` / `window.open()`, not
anchor attributes).

---------

Co-authored-by: Kayla はな <kayla@tree.camp>
2026-03-27 15:35:10 -04:00
Jaayden Halko 86c3983fc0 feat: add AI Governance seat capacity banners (#23411)
## Summary

Add site-wide banners for AI Governance seat usage thresholds:

1. **90% capacity warning (admin-only):** When actual AI Governance
seats are ≥90% and <100% of the license limit, admins see:
   > "You have used 90% of your AI governance add-on seats."

2. **Over-limit banner (admin-only):** When actual seats exceed the
license limit, admins see a prominent warning:
> "Your organization is using {actual} / {limit} AI Governance user
seats ({X}% over the limit). Contact sales@coder.com"
   - Uses floor whole percentage (Go int division / `Math.floor`)
   - Includes a clickable `mailto:sales@coder.com` link
2026-03-27 05:51:51 +00:00
Kayla はな 4fab372bdc chore: modernize utils/ imports (#23698) 2026-03-26 16:20:25 -06:00
Kayla はな aa81238cd0 chore: modernize all typescript imports (#23511) 2026-03-26 16:04:24 -06:00
Asher ea4070c0ce feat: add multi-user dialog select for adding group members (#23396)
Instead of the single-user dropdown we had before.
2026-03-26 10:42:04 -08:00
Jeremy Ruppel fc60a6bf9b feat(site): add AIBridge Sessions to deployment menu (#23679)
Adds AI Bridge Sessions link to the deployment menu.
2026-03-26 13:56:51 -04:00
david-fraley fea4560a64 fix(site): use docs() helper for hardcoded documentation URLs (#23606) 2026-03-26 17:10:47 +00:00
Jaayden Halko 3fb7c6264f feat: display the AI add-on column in the UI on the Users and Organization Members tables (#23291)
## Summary

Adds an entitlement-gated **AI add-on** column to both the **Users**
table and the **Organization Members** table. When
`ai_governance_user_limit` is entitled, each row shows whether the user
is consuming an AI seat.

## Background

The AI governance add-on tracks which users are consuming AI seats.
Admins need visibility into per-user seat consumption directly from the
user management tables. This change surfaces that information through
both the site-wide Users table and the per-organization Members table,
gated behind the `ai_governance_user_limit` entitlement so the column
only appears when the feature is licensed.

## Implementation

### Backend
- **New SQL query** `GetUserAISeatStates`
(`coderd/database/queries/aiseatstate.sql`) — returns user IDs consuming
an AI seat, derived from:
  - Users with entries in `aibridge_interceptions` (AI Bridge usage)
- Users who own workspaces with `has_ai_task = true` builds (AI Tasks
usage)
- **SDK types** — added `has_ai_seat: boolean` to `codersdk.User` and
`codersdk.OrganizationMemberWithUserData`
- **Handler wiring** — both the Users list endpoint (`coderd/users.go`)
and all Members endpoints (`coderd/members.go`) query AI seat state per
page of user IDs and populate the response field
- **dbauthz** — per-user `ActionRead` checks on `ResourceUserObject`

### Frontend
- **Shared `AISeatCell` component**
(`site/src/modules/users/AISeatCell.tsx`) — green `CircleCheck` for
consuming, gray `X` for non-consuming
- **`TableColumnHelpTooltip`** — extended with `ai_addon` variant with
tooltip: *"Users with access to AI features like AI Bridge, Boundary, or
Tasks who are actively consuming a seat."*
- **Column visibility** gated behind
`useFeatureVisibility().ai_governance_user_limit`

## Validation

- Backend: dbauthz full method suite (`TestMethodTestSuite`) passes
including new `GetUserAISeatStates` test
- Backend: `TestGetUsers`, `TestUsersFilter`, CLI golden file tests pass
- Frontend: 7/7 tests pass across `UsersPage.test.tsx` and
`OrganizationMembersPage.test.tsx` (column visibility gating both
directions)
- `go build ./coderd/...` compiles clean
- `pnpm --dir site run lint:types` passes
- `make gen` clean

## Risks

- **Pagination performance**: The AI seat query is scoped to the current
page's user IDs (not a full table scan), keeping it efficient for
paginated views.
- **Semantic scope**: The workspace-side AI seat derivation uses "any
build with `has_ai_task = true`" rather than "latest build only". If the
product intent is latest-build-only, this can be tightened in a
follow-up.

---

_Generated with `mux` • Model: `anthropic:claude-opus-4-6` • Thinking:
`xhigh` • Cost: `$27.25`_

<!-- mux-attribution: model=anthropic:claude-opus-4-6 thinking=xhigh
costs=27.25 -->
2026-03-26 10:36:40 +00:00
david-fraley dab4e6f0a4 fix(site): use standard dismiss label for cancel confirmation dialogs (#23599) 2026-03-25 21:24:53 +00:00
Kayla はな 0e69e0eaca chore: modernize typescript api client/types imports (#23637) 2026-03-25 15:21:19 -06:00
Atif Ali 6ce35b4af2 fix(site): show accurate health messages in workspace hover menu and status tooltip (#23591) 2026-03-25 21:54:15 +05:00
Danielle Maywood 8bfc5e0868 fix(site): use focus-visible instead of focus for keyboard-only outlines (#23581) 2026-03-25 13:31:50 +00:00
Danielle Maywood 5a03ec302d fix(site): show Agents tab on dev builds without page refresh (#23512) 2026-03-24 22:08:05 +00:00
Kayla はな e045f8c9e4 chore: additional typescript import modernization (#23536) 2026-03-24 16:04:39 -06:00
Jeremy Ruppel 9ba822628a refactor(site): remove derivable useEffect antipatterns (#23267)
React's [Why You Might Not Need An
Effect](https://react.dev/learn/you-might-not-need-an-effect) article
describes several antipatterns and footguns you might encounter when
working with useEffect, so I fed it to an agent and let it determine
some low hanging fruit to fix:

Replace useEffect+setState patterns with direct computations where the
values are purely derived from props, state, or query data:

- useWebpushNotifications: derive `enabled` inline from query data
instead of setting it via useEffect
- ProxyContext: replace `proxy` state + updateProxy callback + useEffect
with a single useMemo over its three inputs
- useSyncFormParameters: replace ref-sync useEffect with direct
assignment during render
- AgentsSidebar (LoadMoreSentinel): replace two ref-sync useEffects with
direct assignments during render

Co-authored by Coder Agent 🤖
2026-03-18 19:26:17 -04:00
Thomas Kosiewski 20ac96e68d feat(site): include chatId in editor deep links (#23214)
## Summary

- include the current agent chat ID in VS Code and Cursor deep links
opened from the agent detail page
- extend `getVSCodeHref` so `chatId` is added only when provided
- add focused tests for deep-link generation with and without `chatId`

## Testing

- `pnpm -C site run format -- src/modules/apps/apps.ts
src/modules/apps/apps.test.ts src/pages/AgentsPage/AgentDetail.tsx`
- `pnpm -C site run check -- src/modules/apps/apps.ts
src/modules/apps/apps.test.ts src/pages/AgentsPage/AgentDetail.tsx`
- `pnpm -C site exec vitest run src/modules/apps/apps.test.ts`
- `pnpm -C site run lint:types`

---
_Generated with [`mux`](https://github.com/coder/mux) • Model:
`openai:gpt-5.4` • Thinking: `high`_
2026-03-18 14:25:38 +01:00
Danielle Maywood 4f566f92b5 fix(site): use ExternalImage for preset icons in task prompt (#23206) 2026-03-18 11:16:30 +00:00
Charlie VoiselleandKayla はな d3c9469e13 fix: open coder_app links in new tab when open_in is tab (#23000)
Fixes #18573

## Changes

When a `coder_app` resource sets `open_in = "tab"`, clicking the app
link now opens in a new browser tab instead of navigating in the same
tab.

`target="_blank"` and `rel="noreferrer"` are set inline on the
`<a>` elements in `AppLink.tsx`, gated on `app.open_in === "tab"`. This
follows the codebase convention of co-locating `target` and `rel` at the
render site.

`noreferrer` suppresses the Referer header to avoid leaking workspace IDs
to destination servers and implies `noopener`.
`noopener` prevents tabnabbing — without it, the opened page can
redirect the Coder dashboard tab via `window.opener`. This is especially
relevant for same-origin path-based apps, which would otherwise have
full DOM access to the dashboard. 

> **Future enhancement**: template admins could opt into sending the
referrer via a `coder_app` setting, enabling feedback pages built around
workspace context.

## Tests

A vitest case is added in `AppLink.test.tsx` (rather than a Storybook
story, since the assertions are purely behavioral with no visual
component):

- **`sets target=_blank and rel=noopener noreferrer when open_in is
tab`** — renders the app link with `open_in: "tab"` and asserts
`target="_blank"` and `rel="noreferrer"` are present on the
anchor.

## Slim-window behavior

The `slim-window` test case and the `openAppInNewWindow()` comment in
`apps.ts` have been split out into a follow-up PR for separate review,
since the `window.open()` / `noopener` tradeoffs there deserve dedicated
discussion.

---------

Co-authored-by: Kayla はな <kayla@tree.camp>
2026-03-17 15:32:45 -04:00
Michael Suchacz eacabd8390 feat(site): add chat cost analytics frontend (#23037)
Add UI components for viewing and managing LLM chat cost analytics.

## Changes
- `UserAnalyticsDialog`: personal cost summary with 30-day date range
- `ChatCostSummaryView`: shared component for cost breakdowns by model
and chat
- `ConfigureAgentsDialog`: admin Usage tab with deployment-wide cost
rollup
- Storybook stories for all new and existing components
- Replace `ModelsSection.test.tsx`, `DashboardLayout.test.tsx`,
`AuditPage.test.tsx` with Storybook stories
- Cost-related API client methods and React Query hooks
- Analytics utilities for formatting microdollar values

Backend: #23036
2026-03-13 18:59:14 +00:00
Jaayden Halko 4e2640e506 fix(site): WCAG 2.1 AA remediation — landmarks, semantics, and a11y tooling (#22746)
## Summary

Targeted WCAG 2.1 AA accessibility remediation — continuation of #22673
— addressing remaining semantic, landmark, and tooling gaps identified
in the frontend accessibility review.

### Changes

#### Document semantics (WCAG 3.1.1)
- **`site/index.html`**: Added `<html lang="en">` root wrapper so screen
readers and browser features correctly identify the document language.

#### Landmark & bypass (WCAG 1.3.1, 2.4.1)
- **`DashboardLayout.tsx`**: Replaced `<div id="main-content">` with
`<main id="main-content">` so assistive technology exposes a proper main
landmark and the skip link targets a semantic region.

#### Table header relationships (WCAG 1.3.1)
- **`Table.tsx`**: `TableHead` now renders `scope="col"` by default
(overridable via prop), giving data cells an explicit header
relationship.

#### Semantic interactive controls (WCAG 2.1.1, 4.1.2)
- **`AuditLogRow.tsx`**: Replaced `<div role="button" tabIndex={0}>`
with native `<button type="button">`, removing the manual keyboard
handler (native button provides Enter/Space for free).
- **`Autocomplete.tsx`**: Replaced clear `<span role="button"
tabIndex={0}>` with native `<button type="button" aria-label="Clear
selection">`.

#### Reduced motion (WCAG 2.3.3 best practice)
- **`index.css`**: Added global `@media (prefers-reduced-motion:
reduce)` block that suppresses non-essential animations and transitions.

#### Accessibility regression tooling
- **Storybook**: Added `@storybook/addon-a11y` (version-matched to
existing Storybook 10.x).
- **vitest-axe**: Added `vitest-axe` with setup wiring and an exemplar
`Table.axe.test.tsx` that runs axe-core assertions in vitest.

### Test plan

- 12 new/updated tests pass across 5 test files:
  - `DashboardLayout.test.tsx` — main landmark + skip link behavior
  - `Table.test.tsx` — scope default + override
  - `Table.axe.test.tsx` — axe-core violation scan
  - `AuditPage.test.tsx` — keyboard toggle with native button
  - `Autocomplete.test.tsx` — clear control semantics
- `pnpm lint` clean (biome, TypeScript, circular deps)
- Manual keyboard traversal: skip link → main content, audit row toggle,
autocomplete clear
2026-03-13 10:47:53 +00:00
Asher 53304df70d fix: disallow deselecting dynamic dropdown value (#22931)
This was accomplished by switching from the comboxbox (which has
deselection logic) to a select (which does not).

As a side effect, the dropdowns are wider now, which seems to match
better with other inputs anyway.  And, it seems it uses the `combobox`
role instead of `button` which also seems to make more sense.  Lastly, 
they lose some bolding.
2026-03-11 17:08:52 -08:00
TJ 2f2ba0ef7e fix(site): prevent vertical scrollbar caused by deployment banner (#22877)
## Summary

When the deployment banner's horizontal scrollbar appears on narrow
viewports, it triggers an unwanted vertical scrollbar on the page.
<img width="2262" height="598" alt="image"
src="https://github.com/user-attachments/assets/5ef98d44-87ba-4db0-baa1-d9914abfae0e"
/>

## Root Cause

The app sets `scrollbar-gutter: stable` on `<html>` (in `index.css`)
which reserves space for a vertical scrollbar. The `DashboardLayout`
uses `min-h-screen` with `justify-between`, making content fill exactly
100vh. When the deployment banner's `overflow-x: auto` activates a
horizontal scrollbar, the scrollbar track adds height that pushes the
document past 100vh, triggering the vertical scrollbar.

## Fix

Add `overflow-y-hidden` to the deployment banner. This prevents the
horizontal scrollbar's track height from contributing to the document's
vertical overflow.

## Changes

- `DeploymentBannerView.tsx`: Added `overflow-y-hidden` alongside
existing `overflow-x-auto`
2026-03-11 07:42:38 -07:00
david-fraley c7c789f9e4 fix(site): fix file tree click not scrolling to file section (#22924) 2026-03-11 08:50:49 -05:00
Kayla はな cbe46c816e feat: add workspace sharing buttons to tasks (#22729)
Attempt to re-merge https://github.com/coder/coder/pull/21491 now that
the supporting backend work is done

Closes https://github.com/coder/coder/issues/22278
2026-03-10 12:26:33 -06:00
Kyle Carberry 47846c0ee4 fix(site): inject permissions and organizations metadata to eliminate loading spinners (#22741)
## Problem

Two network requests were blocking the initial page render with
fullscreen `<Loader fullscreen />` spinners:

1. **`POST /api/v2/authcheck`** (permissions) — blocked in `RequireAuth`
via `AuthProvider.isLoading`
2. **`GET /api/v2/organizations`** — blocked in `DashboardProvider`

All other bootstrap queries (`user`, `entitlements`, `appearance`,
`experiments`, `build-info`, `regions`) already used server-side
metadata injection via `index.html` meta tags and resolved instantly.
These two did not.

## Solution

Follow the existing `cachedQuery` + `<meta>` tag pattern to inject both
datasets server-side:

### Server-side (`site/site.go`)
- Add `Permissions` and `Organizations` fields to `htmlState`
- Fetch organizations via `GetOrganizationsByUserID` in parallel with
existing queries
- Evaluate all `permissionChecks` using the RBAC authorizer directly
- Inject results as HTML-escaped JSON into `<meta>` tags

### Frontend
- Register `permissions` and `organizations` in `useEmbeddedMetadata`
- Update `checkAuthorization()` to accept optional metadata and use
`disabledRefetchOptions` when available
- Update `organizations()` to accept optional metadata and use
`cachedQuery` when available
- Wire metadata through `AuthProvider` and `DashboardProvider`

### Note
The Go `permissionChecks` map in `site/site.go` mirrors
`site/src/modules/permissions/index.ts` and must be kept in sync.
2026-03-09 16:12:04 +00:00
Jaayden Halko ec48636ba8 fix(site): WCAG 2.1 AA accessibility remediation for core frontend flows (#22673)
This PR is several accessibility improvements researched by Mux.
Manually tested and these changes should be mostly harmless.


## Summary

Targeted WCAG 2.1 AA accessibility remediation across core frontend user
flows: login, dashboard navigation, audit interactions, settings, and
workspace parameter inputs.

### Changes

#### Navigation, keyboard & focus visibility (WCAG 2.4.1, 2.1.1, 2.4.7,
1.4.11)

- **DashboardLayout**: Added "Skip to main content" link (visually
hidden, visible on focus) with `#main-content` target on the main outlet
container.
- **AuditLogRow**: Expanded keyboard handler so both Enter and Space
toggle expandable audit details (with `preventDefault` on Space to
prevent scroll).
- **model-selector**: Removed `focus:ring-0 focus-visible:ring-0` from
`SelectTrigger` to restore the default visible focus indicator.

#### Forms & input assistance (WCAG 3.3.1, 3.3.2, 1.3.5)

- **PasswordSignInForm**: Wired `aria-invalid` and `aria-describedby` on
email/password inputs, pointing to stable-ID error elements
(`signin-email-error`, `signin-password-error`).
- **AccountForm**: Added `autoComplete="name"` to the Name field.

#### Name/role/value & status messages (WCAG 1.1.1, 4.1.2, 4.1.3, 1.3.1)

- **PortForwardButton**: Added `aria-label="Delete shared port"` to the
icon-only delete button.
- **DynamicParameter**: Replaced mouse-only peek-and-hold reveal with a
persistent keyboard-accessible toggle. Added dynamic `aria-label` ("Show
value"/"Hide value") and `aria-pressed`.
- **Tabs**: Removed incorrect `role="tablist"` (these are route
navigation links, not ARIA tabs). Added `aria-current="page"` on the
active `TabLink`.
- **Loader**: Wrapped spinner in `role="status" aria-live="polite"`
container with an `aria-label` for screen reader announcements.
- **Alert**: Changed `AlertTitle` from `<h1>` to `<h2>` to avoid
multiple page-level headings.

### Testing

- **9 Vitest test files** (22 tests) — all new or extended to cover the
a11y changes.
- **1 Jest test file** (38 tests) — DynamicParameter tests updated for
toggle semantics + keyboard activation.
- `pnpm lint:types` ✅
- `pnpm check` (Biome lint + format) ✅

### Files changed

| File | Change |
|------|--------|
| `site/src/modules/dashboard/DashboardLayout.tsx` | Skip link +
`#main-content` id |
| `site/src/modules/dashboard/DashboardLayout.test.tsx` | Skip link
assertions |
| `site/src/pages/AuditPage/AuditLogRow/AuditLogRow.tsx` | Space + Enter
keyboard handling |
| `site/src/pages/AuditPage/AuditPage.test.tsx` | Keyboard toggle tests
|
| `site/src/components/ai-elements/model-selector.tsx` | Remove focus
ring suppression |
| `site/src/components/ai-elements/model-selector.test.tsx` | **New** —
focus ring assertion |
| `site/src/pages/LoginPage/PasswordSignInForm.tsx` | aria-invalid +
aria-describedby |
| `site/src/pages/LoginPage/LoginPage.test.tsx` | Error association
tests |
| `site/src/pages/UserSettingsPage/AccountPage/AccountForm.tsx` |
autoComplete="name" |
| `site/src/pages/UserSettingsPage/AccountPage/AccountForm.test.tsx` |
Autocomplete assertion |
| `site/src/modules/resources/PortForwardButton.tsx` | aria-label on
delete button |
| `site/src/modules/resources/PortForwardButton.test.tsx` | **New** —
accessible name test |
| `site/src/modules/workspaces/DynamicParameter/DynamicParameter.tsx` |
Keyboard toggle + ARIA |
|
`site/src/modules/workspaces/DynamicParameter/DynamicParameter.jest.tsx`
| Toggle semantics tests |
| `site/src/components/Tabs/Tabs.tsx` | Remove tablist role, add
aria-current |
| `site/src/components/Tabs/Tabs.test.tsx` | **New** —
tablist/aria-current tests |
| `site/src/components/Loader/Loader.tsx` | role="status" + aria-live |
| `site/src/components/Loader/Loader.test.tsx` | **New** — status
semantics tests |
| `site/src/components/Alert/Alert.tsx` | h1 → h2 |
| `site/src/components/Alert/Alert.test.tsx` | **New** — heading level
test |
2026-03-06 15:19:13 +00:00
Jake Howell dba688662c feat: add <ErrorAlert /> debug details (#22462)
Closes #22140

Short simple and sweet PR to add a bunch of details to our `<Alert />`
stacks. This means we aren't simply asking the user to read the
developer console and surface things easier.

- Implement `Response data` and `Stack trace` `<details />`
- Fix overflow in `ErrorAlert` debug accordions so long `Response data`
and `Stack Trace` content stays inside the alert.
- Add horizontal scroll wrappers around both `<pre>` blocks used in
debug details.
- Update `Alert` layout with `min-w-0` on flex containers so nested
content can shrink correctly and internal scrolling works as intended.

<img width="739" height="550" alt="preview-validation"
src="https://github.com/user-attachments/assets/a6f890d3-8f1f-4fd6-b9d0-882838db04a4"
/>
2026-03-06 03:48:01 +11:00
Kyle Carberry 17d214b4a4 fix(site): resolve WS/HTTP race condition on workspace parameters page (#22556)
## Problem

Flaky e2e test: `update workspace, new required, mutable parameter
added`

```
Error: Timed out 15000ms waiting for expect(locator).toHaveValue(expected)
Locator: getByTestId('parameter-field-Sixth parameter').locator('input')
Expected string: "99"
Received string: ""
```

## Root Cause

When the workspace parameters page loads, the WebSocket sends an initial
response with template defaults. For parameters with no default (like
`sixth_parameter`), the server returns `{valid: false, value: ""}`. On
first render, `useSyncFormParameters` sees this invalid server value and
overwrites the form's correctly-autofilled value ("99" from the previous
build) with "".

## Fix

When the server value is `{valid: false}`, preserve the current form
value instead of overwriting with "". This prevents the sync hook from
clobbering autofilled values before the server has had a chance to
process them.

## Verification

- TypeScript: zero type errors
- Biome lint: clean
- Unit tests: 2/2 passing
- **E2E soak test: 849/854 passed across 854 runs (99.5% pass rate)**
  - 0 occurrences of the original flake (empty value on settings page)
- 5 residual failures are a separate pre-existing race in
`fillParameters` where user input is overwritten during the 500ms
debounce window
2026-03-04 16:20:49 +00:00
Kyle Carberry 2ceac319b8 fix(site): eagerly fetch API key for Open in Cursor/VS Code buttons (#22554)
## Problem

The **Open in Cursor** and **Open in VS Code** buttons on the agent
detail page were broken. Clicking them did nothing.

### Root Cause

The `handleOpenInEditor` handler in `AgentDetail.tsx` called
`window.location.assign()` with a custom protocol URI (`vscode://` or
`cursor://`) **after** an `await API.getApiKey()` call. This creates an
async boundary that breaks the browser's user gesture chain, causing
custom protocol navigations (`vscode://`, `cursor://`) to be silently
blocked by the browser.

The handler was invoked from a Radix `DropdownMenuItem.onSelect`, which
adds another layer of event indirection that makes the gesture chain
more fragile.

In contrast, the workspace page's `VSCodeDesktopButton` works because it
uses a direct `onClick` handler on a button element.

## Fix

- **Eagerly fetch and cache the API key** via `useQuery` when workspace
and agent data is available
- **Make `handleOpenInEditor` synchronous** — it reads the cached key
instead of awaiting a network call, keeping `window.location.assign()`
within the original user gesture context
- **Disable buttons** while the API key is still loading
(`canOpenEditors` now gates on key availability)
- **Simplify** the `onOpenInEditor` callback (remove `void` async
wrapper)
2026-03-03 16:54:27 +00:00
Cian Johnston 8a095ae722 fix(site): remove ugly webpush button (#22560) 2026-03-03 16:44:46 +00:00
Kayla はな 2bdf80d452 fix: disable sharing ui when sharing is unavailable (#22390)
Currently the sharing UI is only hidden under certain circumstances,
rather than on a permission basis. This makes it permissions based, and
makes some backend changes to make sure permissions are correct.
2026-03-03 02:04:55 +00:00
edee917d88 feat: add experimental agents support (#22290)
feat: add AI chat system with agent tools and chat UI

Introduce the chatd subsystem and Agents UI for AI-powered chat
within Coder workspaces.

- Add chatd package with chat loop, message compaction, prompt
  management, and LLM provider integration (OpenAI, Anthropic)
- Add agent tools: create workspace, list/read templates, read/write/
  edit files, execute commands
- Add chat API endpoints with streaming, message editing, and
  durable reconnection
- Add database schema and migrations for chats, chat messages, chat
  providers, and chat model configs
- Add RBAC policies and dbauthz enforcement for chat resources
- Add Agents UI pages with conversation timeline, queued messages
  list, diff viewer, and model configuration panel
- Add comprehensive test coverage including coderd integration tests,
  chatd unit tests, and Storybook stories
- Gate feature behind experiments flag

---------

Co-authored-by: Cian Johnston <cian@coder.com>
Co-authored-by: Danielle Maywood <danielle@themaywoods.com>
Co-authored-by: Jeremy Ruppel <jeremy@coder.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 16:50:56 +00:00
Jake Howell 358f521bbb feat: implement error message on failure to popup (#22380)
This change adds user-facing feedback when opening apps in a new window
fails due to popup blocking, replacing a silent no-op with a clear
recovery message. It improves reliability and supportability across
app-launch flows by helping users immediately understand and fix the
issue.
2026-02-28 02:41:44 +11:00
Jake Howell d204e6fb84 feat: implement file icons in <TemplateFiles /> (#22369)
This pull-request takes our icons shown in the sidebar tree and shows
them alongside the names of the files in the `Source Code` page of our
templates.

Also does a quick de-mui of this page.

<img width="637" height="345" alt="image"
src="https://github.com/user-attachments/assets/f3013eb6-9572-4d05-a683-10bb99b4e802"
/>
2026-02-27 22:53:09 +11:00
Jake Howell dbc0daa64b feat: add animation to copy button <Check />s (#22319)
This pull-request adds a tiny little animation for the `<Check />` when
the copy button activates. This is inline with how `sonner` does their
animations (means things are a little more uniform).


![preview-check-animation](https://github.com/user-attachments/assets/533f644a-1d86-4b11-8ca3-c670a9913b57)
2026-02-27 21:22:57 +11:00
Jaayden Halko 4e1cedf8fd chore: migrate workspace agent row styling to Tailwind (#22195) 2026-02-25 17:43:29 +00:00