From b732d3b659f96c6559930086c8baab0f41ee9349 Mon Sep 17 00:00:00 2001 From: Michael Johnston Date: Thu, 3 Sep 2026 10:31:57 -0700 Subject: [PATCH 1/2] fix(web): theme sidebar update controls --- apps/web/src/components/Sidebar.tsx | 4 ++-- .../src/components/settings/LastCodeSettings.tsx | 4 ++-- .../sidebar/SidebarThreadHoverContent.tsx | 2 +- .../components/sidebar/SidebarUpdatePill.test.ts | 4 ++-- .../src/components/sidebar/SidebarUpdatePill.tsx | 4 ++-- apps/web/src/environmentIcons.test.tsx | 15 ++++++++------- apps/web/src/environmentIcons.tsx | 14 +++++++------- docs/user/thread-sidebar.md | 2 +- 8 files changed, 25 insertions(+), 24 deletions(-) diff --git a/apps/web/src/components/Sidebar.tsx b/apps/web/src/components/Sidebar.tsx index 47b8b7df8d12f..d8f1807afc581 100644 --- a/apps/web/src/components/Sidebar.tsx +++ b/apps/web/src/components/Sidebar.tsx @@ -963,7 +963,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { props.configuredEnvironmentIconColor, props.environmentKnown, ); - const environmentIconKind = isRemote ? "server" : "monitor"; + const environmentIconKind = isRemote ? "server" : "laptop"; const cleanupBlockerTitle = cleanup?.status === "queued" ? (readThreadShell(scopeThreadRef(thread.environmentId, cleanup.blockedByThreadId))?.title ?? @@ -1872,7 +1872,7 @@ const SidebarSearchResultRow = memo(function SidebarSearchResultRow(props: { projectFaviconPath={props.projectFaviconPath} projectIcon={props.projectIcon} environmentLabel={props.environmentLabel} - environmentIconKind={thread.environmentId === primaryEnvironmentId ? "monitor" : "server"} + environmentIconKind={thread.environmentId === primaryEnvironmentId ? "laptop" : "server"} environmentIconColor={environmentIconColor} providerEntry={providerEntry} showInstanceBadge={showInstanceBadge} diff --git a/apps/web/src/components/settings/LastCodeSettings.tsx b/apps/web/src/components/settings/LastCodeSettings.tsx index 22e54adc03cb8..edb4ccfe70fbe 100644 --- a/apps/web/src/components/settings/LastCodeSettings.tsx +++ b/apps/web/src/components/settings/LastCodeSettings.tsx @@ -308,7 +308,7 @@ export function LastCodeSettingsPanel() { title={
Show local icon

- Show a Monitor icon for local threads. Legacy rows always reserve its space. + Show a Laptop icon for local threads. Legacy rows always reserve its space.

{ }); describe("SidebarUpdatePill local failure", () => { - it("uses a destructive button tone without changing the ordinary update tone", () => { + it("uses the themed update tone and lets a local failure override it", () => { expect( resolveSidebarUpdateButtonToneClassName({ hasLocalBuildFailure: true, @@ -130,7 +130,7 @@ describe("SidebarUpdatePill local failure", () => { isInteractionDisabled: false, showUpdateIconState: true, }), - ).toContain("bg-sidebar-control-surface"); + ).toContain("bg-update-surface text-update-foreground ring-update/40"); }); it("keeps disabled update details hoverable without applying action hover styling", () => { diff --git a/apps/web/src/components/sidebar/SidebarUpdatePill.tsx b/apps/web/src/components/sidebar/SidebarUpdatePill.tsx index 247b6b03c3394..5a9957b4b02b6 100644 --- a/apps/web/src/components/sidebar/SidebarUpdatePill.tsx +++ b/apps/web/src/components/sidebar/SidebarUpdatePill.tsx @@ -111,8 +111,8 @@ export function resolveSidebarUpdateButtonToneClassName({ } if (showUpdateIconState) { return cn( - "bg-sidebar-control-surface text-sidebar-foreground", - !isInteractionDisabled && "hover:bg-sidebar-row-hover", + "bg-update-surface text-update-foreground ring-update/40", + !isInteractionDisabled && "hover:bg-update/18", ); } return cn( diff --git a/apps/web/src/environmentIcons.test.tsx b/apps/web/src/environmentIcons.test.tsx index 569e66edce689..2341cf4632183 100644 --- a/apps/web/src/environmentIcons.test.tsx +++ b/apps/web/src/environmentIcons.test.tsx @@ -29,8 +29,8 @@ describe("environment icon preferences", () => { expect(updateEnvironmentIconColors({ buildbox: "#2563eb" }, buildbox, "")).toEqual({}); }); - it("uses absolute Monitor and Server identity", () => { - expect(environmentIconKind(local, local)).toBe("monitor"); + it("uses absolute Laptop and Server identity", () => { + expect(environmentIconKind(local, local)).toBe("laptop"); expect(environmentIconKind(buildbox, local)).toBe("server"); expect(environmentIconKind(local, buildbox)).toBe("server"); }); @@ -48,7 +48,7 @@ describe("environment icon preferences", () => { showLocalEnvironmentIcon: false, environmentLabel: "Workstation", }), - ).toEqual({ kind: "monitor", showRowIcon: false, hoverLabel: null }); + ).toEqual({ kind: "laptop", showRowIcon: false, hoverLabel: null }); expect( legacyThreadEnvironmentPresentation({ isPrimary: true, @@ -56,7 +56,7 @@ describe("environment icon preferences", () => { showLocalEnvironmentIcon: true, environmentLabel: "Workstation", }), - ).toEqual({ kind: "monitor", showRowIcon: true, hoverLabel: "Workstation (local)" }); + ).toEqual({ kind: "laptop", showRowIcon: true, hoverLabel: "Workstation (local)" }); expect( legacyThreadEnvironmentPresentation({ isPrimary: false, @@ -93,7 +93,7 @@ describe("project environment icons", () => { }; expect(projectEnvironmentIconEntries({ ...base, showLocalEnvironmentIcon: false })).toEqual([]); expect(projectEnvironmentIconEntries({ ...base, showLocalEnvironmentIcon: true })).toEqual([ - { environmentId: local, kind: "monitor", label: "Workstation (local)" }, + { environmentId: local, kind: "laptop", label: "Workstation (local)" }, ]); }); @@ -112,7 +112,7 @@ describe("project environment icons", () => { showLocalEnvironmentIcon: false, }), ).toEqual([ - { environmentId: local, kind: "monitor", label: "Workstation (local)" }, + { environmentId: local, kind: "laptop", label: "Workstation (local)" }, { environmentId: buildbox, kind: "server", label: "Buildbox" }, { environmentId: production, kind: "server", label: "Production" }, { environmentId: wsl, kind: "container", label: "Ubuntu" }, @@ -131,8 +131,9 @@ describe("EnvironmentIcon", () => { it("uses an explicit color without the contextual opacity class", () => { const markup = renderToStaticMarkup( - , + , ); + expect(markup).toContain("lucide-laptop"); expect(markup).toContain("color:#2563eb"); expect(markup).not.toContain("text-sidebar-muted-foreground/70"); }); diff --git a/apps/web/src/environmentIcons.tsx b/apps/web/src/environmentIcons.tsx index 4e6745900cfe0..98ef8b58765ff 100644 --- a/apps/web/src/environmentIcons.tsx +++ b/apps/web/src/environmentIcons.tsx @@ -1,11 +1,11 @@ import type { EnvironmentId } from "@t3tools/contracts"; import type { EnvironmentIconColor } from "@t3tools/contracts/settings"; -import { ContainerIcon, MonitorIcon, ServerIcon } from "lucide-react"; +import { ContainerIcon, LaptopIcon, ServerIcon } from "lucide-react"; import type { CSSProperties } from "react"; import { cn } from "./lib/utils"; -export type EnvironmentIconKind = "container" | "monitor" | "server"; +export type EnvironmentIconKind = "container" | "laptop" | "server"; export type EnvironmentIconContext = "hover" | "legacy-row" | "project" | "settings" | "v2-row"; const DEFAULT_CONTEXT_CLASS: Record = { @@ -44,8 +44,8 @@ export function formatLocalEnvironmentLabel(label: string | null | undefined): s export function environmentIconKind( environmentId: EnvironmentId, primaryEnvironmentId: EnvironmentId | null, -): "monitor" | "server" { - return environmentId === primaryEnvironmentId ? "monitor" : "server"; +): "laptop" | "server" { + return environmentId === primaryEnvironmentId ? "laptop" : "server"; } export function resolveEnvironmentIconColor( @@ -61,7 +61,7 @@ export function legacyThreadEnvironmentPresentation(input: { readonly showLocalEnvironmentIcon: boolean; readonly environmentLabel: string | null | undefined; }) { - const kind = input.isPrimary ? ("monitor" as const) : ("server" as const); + const kind = input.isPrimary ? ("laptop" as const) : ("server" as const); return { kind, showRowIcon: input.isPrimary ? input.showLocalEnvironmentIcon : !input.isDesktopLocal, @@ -111,7 +111,7 @@ export function projectEnvironmentIconEntries(input: { return [ { environmentId: member.environmentId, - kind: "monitor", + kind: "laptop", label: formatLocalEnvironmentLabel(member.environmentLabel), }, ]; @@ -144,7 +144,7 @@ export function EnvironmentIcon(props: { readonly "aria-hidden"?: boolean | undefined; }) { const Icon = - props.kind === "monitor" ? MonitorIcon : props.kind === "server" ? ServerIcon : ContainerIcon; + props.kind === "laptop" ? LaptopIcon : props.kind === "server" ? ServerIcon : ContainerIcon; return (