Commit Graph
542 Commits
Author SHA1 Message Date
Atif Ali 584c61acb5 fix: mark connecting agents as unhealthy instead of healthy (#24044)
## Problem

Workspaces showed as "Healthy" immediately after creation while the
agent was still downloading, starting, or connecting. If the agent never
connected, the workspace stayed "Healthy" for the entire connection
timeout (~120s), then abruptly flipped to "Unhealthy".

## Root cause

In `db2sdk.WorkspaceAgent`, the health switch had no case for
`WorkspaceAgentConnecting`. Agents in `connecting` status with a
non-`off` lifecycle (e.g. `created` after a fresh build) fell through to
the `default` case and were marked `Healthy = true`.

## Fix

Add an explicit case for `WorkspaceAgentConnecting` that sets `Healthy =
false` with reason `"agent has not yet connected"`. The case is placed
after the existing `!connected + off` case (which correctly catches
stopped agents as "not running") and before the `timeout`/`disconnected`
cases.

```
Status        + Lifecycle       → Health reason
──────────────────────────────────────────────────────
any !connected + off           → "agent is not running"
connecting    + created/starting → "agent has not yet connected"  ← NEW
timeout       + any            → "agent is taking too long to connect"
disconnected  + any            → "agent has lost connection"
connected     + start_error    → "agent startup script exited with an error"
connected     + shutting_down  → "agent is shutting down"
connected     + ready/starting → healthy
```

The frontend already handles this case — `getAgentHealthIssue()` returns
"Workspace agent is still connecting" with `severity: "info"` for
unhealthy workspaces with connecting agents.

## Test changes

- **Healthy test**: now actually connects the agent via `agenttest.New`
before asserting health (previously passed due to the bug).
- **New Connecting test**: verifies a never-connected agent is correctly
marked unhealthy.
- **Mixed health test**: connects a1 and waits for the mixed state
(`a1.Healthy && !workspace.Healthy`) to avoid a race where both agents
are initially connecting.
- **Sub-agent excluded test**: connects the parent agent and waits for
it to be healthy before creating the sub-agent.
- **TestWorkspaceAgent/Connect**: flipped assertion to `Health.Healthy
== false` for a `dbfake` agent that never connects.

<details>
<summary>Review notes</summary>

### Known follow-up

The `healthy:false` workspace search filter maps to `[disconnected,
timeout]` and does not include `connecting`. This is a pre-existing gap
that is now more consequential — a workspace unhealthy solely due to a
connecting agent won't appear in `healthy:false` results. Worth a
follow-up issue.

### Deep review findings addressed

| Finding | Severity | Status |
|---------|----------|--------|
| Mixed health test race (all 3 reviewers) | P2 | Fixed — tightened
`Eventually` condition |
| `TestWorkspaceAgent/Connect` assertion break | P1 | Fixed — flipped
assertion |
| CLI renders red for connecting agents | Obs | Acknowledged — design
trade-off, accurate but visually strong for transient state |
| Switch case ordering overlap | Obs | Documented with inline comment |

</details>

> 🤖 This PR was created with the help of Coder Agents, and needs a human
review. 🧑💻
2026-04-09 13:21:28 +05:00
Cian Johnston 461a31e5d8 feat(site): add under-construction navbar stripes for pre-release builds (#24157)
Dev and RC builds now show diagonal warning stripes in the navbar plus a
centered version badge, making it impossible to miss which build you're
running.

**Devel build:** amber "warning" from theme

**RC build:** sky "pending" from theme

> 🤖 Written by a Coder Agent. Will be reviewed by a human.
2026-04-08 20:10:03 +00:00
Jaayden Halko 1f808cdc62 fix(site): standardize scrollbar styling with global baseline (#24019)
## Summary

Standardizes all frontend scrollbars to use `scrollbar-width: thin` and
`scrollbar-color: hsl(var(--surface-quaternary)) transparent`.

### Changes

**Global baseline** (`site/src/index.css`):
- Both properties are inherited, so this cascades to all scroll
containers
- Components that hide scrollbars (e.g. `SidebarTabView`) override
locally with `scrollbar-width: none`

**Removed redundant per-component scrollbar utilities**:
- `AgentDetailView.tsx` — removed `[scrollbar-width:thin]` and
`[scrollbar-color:...]` (preserved `[scrollbar-gutter:stable]`)
- `ConfigureAgentsDialog.tsx` — removed redundant scrollbar utilities
from two locations
- `DeploymentBannerView.tsx` — removed `[scrollbar-width:thin]`
- `ChatMessageInput.tsx` — removed redundant scrollbar utilities

**Aligned specialized scrollbar surfaces**:
- `TerminalPage.tsx` — updated webkit scrollbar thumb from hardcoded
`rgba(255, 255, 255, 0.18)` → `hsl(var(--surface-quaternary))`, track
from `inherit` → `transparent`, width from `10px` → `8px`
- `Chart.tsx` — removed local JS-style scrollbar overrides (now covered
by global baseline)

### Preserved as-is
- `SidebarTabView.tsx` — intentional hidden scrollbar (`scrollbar-width:
none` overrides global)
- `ScrollArea.tsx` — already uses `bg-surface-quaternary` ✓
- `MonacoEditor.tsx` — Monaco manages its own scrollbars internally
- All `[scrollbar-gutter:stable]` usages preserved
2026-04-08 16:41:23 +01: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
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
Jake Howell f0521cfa3c fix: resolve <LogLine /> storybook flake (#24084)
This pull-request ensures we have a stable test where the content
doesn't change every time we have a new storybook artifact by setting it
to a consistent date.

Closes https://github.com/coder/internal/issues/1454
2026-04-08 00:17:06 +10:00
Danielle Maywood aede045549 chore: bump @biomejs/biome from 2.2 to 2.4.10 (#24074) 2026-04-07 12:22:18 +01:00
Jake Howell df8f695e84 fix: update logline prefix to use timestamp (#23966)
This change replaces the line number display in agent logs with formatted timestamps. The `AgentLogLine` component now shows timestamps in `HH:mm:ss.SSS` format using dayjs instead of sequential line numbers. The component no longer requires `number` and `maxLineNumber` props, and the associated styling for line number formatting has been removed.

This is a global change.. but I don't think its one that will do much damage.
2026-04-07 04:43:35 +10:00
Jake Howell 8bb48ffdda feat: implement kebab menu overflow to <Tabs /> (#23959)
Added `data-slot` attributes to all Tabs components for better CSS
targeting and component identification. Replaced generic button
selectors with data-slot attribute selectors in tab styling variants.

Implemented `useTabOverflowKebabMenu` hook to handle tab overflow
scenarios by measuring tab widths and determining which tabs should be
hidden in a dropdown menu when container space is limited.

Enhanced the AgentRow logs section with:

- Tab overflow handling using a kebab menu (three dots) for tabs that
don't fit
- Copy logs button with visual feedback using CheckIcon animation
- Download logs functionality for selected tab content with proper
filename generation
- Improved layout with flex containers and proper spacing

Few props and components updates

* Added `overflowKebabMenu` prop to TabsList component to enable
`flex-nowrap` behavior when overflow handling is active.
* Created `<DownloadSelectedAgentLogsButton />` component to replace the
previous download functionality, now working with filtered log content
based on selected tab.


https://github.com/user-attachments/assets/af48ca39-c906-4a11-a891-0d4399eee827
2026-04-07 04:42:01 +10:00
Jake Howell 0060dee222 fix: remove all mui <IconButton /> instances (#24045)
This pull-request removes all instances of `<IconButton />` being
imported from `@mui/material/IconButton`. This means that we've removed
one whole dependency from MUI and replaced all instances with the local
variant.
2026-04-06 15:38:21 +00:00
Jake Howell b36619b905 feat: implement tabs for agent logs (#23952)
This PR adds log source tabs to the workspace agent logs panel so users
can quickly focus on specific log streams instead of scanning one
combined feed. It also updates the shared tabs trigger behavior to
explicitly use `type="button"` when rendered as a native button,
preventing unintended form submission behavior.

- Adds per-source tabs in `AgentRow` with an **All Logs** default view.
- Shows only log sources that currently have output, with source icons
and sorted labels.
- Filters rendered log lines based on the active tab while preserving
existing log streaming/scroll behavior.
- Refines the logs container layout/styling for the new tabbed UI.
- Updates `TabsTrigger` to safely default button type when not using
`asChild`.


https://github.com/user-attachments/assets/9b3e7a9d-72e3-4c12-aba2-2b70cdbc04c1
2026-04-06 18:51:00 +10:00
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