Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions apps/web/src/components/settings/ConnectionsSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1712,7 +1712,7 @@ function ConfiguredCloudLinkRow({ canManageRelay }: { readonly canManageRelay: b
) : null}
<SettingsRow
title={searchableSetting("publish-agent-activity").title}
description="Send activity from this environment to your mobile clients for push notifications and Live Activities. Works without a T3 Connect tunnel."
description="Send activity to mobile notifications and Live Activities without T3 Connect."
control={
<CloudLinkSwitch
ariaLabel="Publish agent activity to mobile clients"
Expand Down Expand Up @@ -2828,7 +2828,7 @@ export function ConnectionsSettings() {
return (
<SettingsRow
{...searchableSetting("wsl-backend")}
description="WSL is no longer available, so the Windows backend is running instead. Switch off the WSL backend to clear this preference."
description="WSL is unavailable, so Windows is running instead. Turn WSL off to clear this preference."
status={
desktopWslError ? (
<span className="block text-destructive">{desktopWslError}</span>
Expand Down Expand Up @@ -2866,7 +2866,7 @@ export function ConnectionsSettings() {
<>
<SettingsRow
{...searchableSetting("wsl-backend")}
description="Run a second backend inside a WSL distro alongside the Windows one. Pick a distro to start it; pick Off to stop it. Projects opened against the WSL backend live on the Linux side; Windows projects stay where they are."
description="Run the selected WSL distro alongside Windows. Projects remain on their current filesystem."
status={
desktopWslError ? (
<span className="block text-destructive">{desktopWslError}</span>
Expand Down Expand Up @@ -2915,7 +2915,7 @@ export function ConnectionsSettings() {
{desktopWslState.enabled ? (
<SettingsRow
title="WSL only"
description="Stop the Windows backend and run only the WSL backend. Useful if you develop entirely inside WSL and don't want a second backend process. T3 Code restarts when you change this."
description="Run only the WSL backend. T3 Code restarts when this changes."
className="bg-muted/20 pl-7 sm:pl-8"
control={
<Switch
Expand Down Expand Up @@ -3018,8 +3018,8 @@ export function ConnectionsSettings() {
title={searchableSetting("network-access").title}
description={
currentAuthPolicy === "remote-reachable"
? "This backend is already configured for remote access. Network exposure changes must be made where the server is launched."
: "This backend is only reachable on this machine. Restart it with a non-loopback host to enable remote pairing."
? "Remote access is already configured. Change network exposure where the server starts."
: "Only this machine can connect. Restart with a non-loopback host for remote pairing."
}
control={
<Tooltip>
Expand Down
12 changes: 6 additions & 6 deletions apps/web/src/components/settings/IntegrationsSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ function BrowserViewportSetting({ disabled }: { readonly disabled: boolean }) {
return (
<SettingsRow
{...searchableSetting("browser-default-viewport")}
description="The viewport a browser tab opens at, for both you and agents. Fill sizes the page to the panel; any other choice opens the device toolbar at that size."
description="Tab size for you and agents. Fill fits the panel; other sizes show the device toolbar."
resetAction={
!disabled && viewport._tag !== DEFAULT_BROWSER_VIEWPORT._tag ? (
<SettingResetButton
Expand Down Expand Up @@ -425,7 +425,7 @@ function BrowserRecordingFrameRateSetting({ disabled }: { readonly disabled: boo
return (
<SettingsRow
{...searchableSetting("browser-recording-frame-rate")}
description="Maximum frame rate for browser recordings. 30 fps is the default and uses less CPU and storage; 60 fps captures smoother motion."
description="Maximum recording rate. 30 fps saves CPU and storage; 60 fps is smoother."
resetAction={
!disabled && frameRate !== DEFAULT_BROWSER_RECORDING_FRAME_RATE ? (
<SettingResetButton
Expand Down Expand Up @@ -522,7 +522,7 @@ function AgentBrowserAccessSetting() {
<SettingsRow
serverScoped
{...searchableSetting("agent-browser-access")}
description="Let agents open and drive the preview browser. When off, the browser tools and the instructions describing them are withheld from agent sessions. Your own browser panel is unaffected."
description="Allow agents to use the preview browser. Off hides browser tools from agents, not you."
status={
settings.enableAgentBrowserAccess
? undefined
Expand Down Expand Up @@ -560,7 +560,7 @@ function BrowserAutoShowFloatingPreviewSetting({ disabled }: { readonly disabled
return (
<SettingsRow
{...searchableSetting("browser-auto-show-floating-preview")}
description="Pop the floating preview into view when an agent opens a browser. An agent that explicitly asks to show or hide its preview still gets what it asked for."
description="Show the floating preview when an agent opens a browser unless the agent says otherwise."
resetAction={
!disabled && autoShow !== DEFAULT_BROWSER_AUTO_SHOW_FLOATING_PREVIEW ? (
<SettingResetButton
Expand Down Expand Up @@ -603,7 +603,7 @@ function BrowserAutoShowFloatingPreviewSetting({ disabled }: { readonly disabled
*/
function DesktopOnlyBrowserDefaults({ children }: { readonly children: ReactNode }) {
return (
<div className="rounded-xl border border-border/60 bg-muted/20 py-1.5">
<div className="border-border/60 bg-muted/20 py-1.5">
<div className="flex items-start gap-2 px-3 py-2 text-[12px] leading-relaxed text-muted-foreground sm:px-4">
<InfoIcon className="mt-0.5 size-3.5 shrink-0 text-warning" />
<p>Only available in the desktop app.</p>
Expand Down Expand Up @@ -698,7 +698,7 @@ function BrowserProfilesSetting({ disabled }: { readonly disabled: boolean }) {
return (
<SettingsRow
{...searchableSetting("browser-profiles")}
description="Each profile keeps its own cookies and logins, so a tab opened under one can't see another's. Incognito discards everything when the app closes."
description="Profiles separate cookies and logins. Incognito data is cleared when the app closes."
control={
<Button
size="sm"
Expand Down
102 changes: 32 additions & 70 deletions apps/web/src/components/settings/ProviderAccentColorPicker.tsx
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,12 @@
import { PipetteIcon, XIcon } from "lucide-react";
import { useCallback, useEffect, useMemo, useRef, useState, type PointerEvent } from "react";

import { ColorSelector } from "../color-selector";
import { Button } from "../ui/button";
import { Popover, PopoverPopup, PopoverTrigger } from "../ui/popover";
import { Popover, PopoverClose, PopoverPopup, PopoverTrigger } from "../ui/popover";
import { normalizeProviderAccentColor } from "../../providerInstances";
import { cn } from "../../lib/utils";

const PROVIDER_ACCENT_SWATCHES = [
"#2563eb",
"#16a34a",
"#ea580c",
"#dc2626",
"#7c3aed",
"#0891b2",
] as const;

const FALLBACK_ACCENT_COLOR = PROVIDER_ACCENT_SWATCHES[0];
const FALLBACK_ACCENT_COLOR = "#2563eb";

function clamp(value: number, min = 0, max = 1) {
return Math.min(max, Math.max(min, value));
Expand Down Expand Up @@ -181,8 +171,8 @@ function ProviderCustomColorPanel(props: {
function ProviderCustomColorPicker(props: {
readonly displayName: string;
readonly value: string | undefined;
readonly selected: boolean;
readonly onCommit: (value: string) => void;
readonly onClear: () => void;
}) {
const normalized = normalizeProviderAccentColor(props.value) ?? FALLBACK_ACCENT_COLOR;

Expand All @@ -193,20 +183,13 @@ function ProviderCustomColorPicker(props: {
<button
type="button"
className={cn(
"flex size-6 cursor-pointer items-center justify-center rounded-full text-white transition-transform duration-200 active:scale-90",
"hover:scale-105",
"flex size-7 shrink-0 cursor-pointer items-center justify-center rounded-md border border-input text-white shadow-xs transition-transform duration-200 active:scale-95",
"hover:scale-105 hover:border-ring/60",
)}
style={{
backgroundColor: normalized,
...(props.selected
? {
boxShadow: `inset 0 0 0 2px var(--card), 0 0 0 2px ${normalized}`,
}
: {}),
}}
aria-label={`Choose custom accent color for ${props.displayName}`}
style={{ backgroundColor: normalized }}
aria-label={`Choose accent color for ${props.displayName}`}
>
<PipetteIcon className="size-3 text-foreground/25" aria-hidden />
<PipetteIcon className="size-3 text-white/70 drop-shadow-sm" aria-hidden />
</button>
}
/>
Expand All @@ -217,6 +200,21 @@ function ProviderCustomColorPicker(props: {
className="overflow-hidden rounded-md p-0 [--viewport-inline-padding:0px] [&_[data-slot=popover-viewport]]:p-0"
>
<ProviderCustomColorPanel value={normalized} onCommit={props.onCommit} />
<PopoverClose
render={
<Button
type="button"
size="sm"
variant="ghost"
className="h-8 w-full justify-start rounded-none border-t border-border/60 px-3 text-xs text-muted-foreground"
Comment thread
maria-rcks marked this conversation as resolved.
Outdated
onClick={props.onClear}
disabled={!props.value}
>
<XIcon className="size-3.5" aria-hidden />
Clear color
</Button>
Comment thread
cursor[bot] marked this conversation as resolved.
}
/>
</PopoverPopup>
</Popover>
);
Expand Down Expand Up @@ -297,59 +295,23 @@ export function ProviderAccentColorPicker(props: {
);

const normalized = normalizeProviderAccentColor(optimisticValue);
const selectedValue =
normalized &&
PROVIDER_ACCENT_SWATCHES.includes(normalized as (typeof PROVIDER_ACCENT_SWATCHES)[number])
? normalized
: "";
const customSelected = Boolean(normalized && selectedValue === "");

const swatchRow = (
<div
role="group"
aria-label="Accent color"
className="flex min-w-0 flex-wrap items-center gap-2"
>
<ProviderCustomColorPicker
displayName={displayName}
value={normalized}
selected={customSelected}
onCommit={commitAccentColor}
/>
<ColorSelector
key={selectedValue}
colors={[...PROVIDER_ACCENT_SWATCHES]}
defaultValue={selectedValue}
size="lg"
onColorSelect={commitAccentColor}
className="flex-wrap gap-1.5"
/>
<Button
type="button"
size="icon"
variant="ghost"
className={cn(
"size-7 shrink-0 text-muted-foreground transition-opacity",
normalized ? "opacity-100" : "pointer-events-none opacity-0",
)}
onClick={() => commitAccentColor("")}
aria-label={`Clear accent color for ${displayName}`}
aria-hidden={!normalized}
tabIndex={normalized ? 0 : -1}
>
<XIcon className="size-3.5" aria-hidden />
</Button>
</div>
const picker = (
<ProviderCustomColorPicker
displayName={displayName}
value={normalized}
onCommit={commitAccentColor}
onClear={() => commitAccentColor("")}
/>
);

if (layout === "inline") {
return swatchRow;
return picker;
}

return (
<div className="grid gap-2">
<span className="text-xs font-medium text-foreground">Accent color</span>
{swatchRow}
{picker}
{description ? <span className="text-xs text-muted-foreground">{description}</span> : null}
</div>
);
Expand Down
16 changes: 8 additions & 8 deletions apps/web/src/components/settings/ProviderInstanceCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -855,7 +855,14 @@ export function ProviderInstanceCard({
>
Display name
</label>
<div className="flex min-w-0 flex-wrap items-center gap-3">
<div className="flex min-w-0 flex-nowrap items-center gap-2 sm:justify-end">
<ProviderAccentColorPicker
layout="inline"
displayName={displayName}
value={accentColor}
onCommit={updateAccentColor}
commitDelayMs={120}
/>
<DraftInput
id={`provider-instance-${instanceId}-display-name`}
size="sm"
Expand All @@ -865,13 +872,6 @@ export function ProviderInstanceCard({
placeholder={driverOption?.label ?? "Instance label"}
spellCheck={false}
/>
<ProviderAccentColorPicker
layout="inline"
displayName={displayName}
value={accentColor}
onCommit={updateAccentColor}
commitDelayMs={120}
/>
</div>
<div className={PROVIDER_FIELD_GROUP_LABEL_CLASS_NAME}>Runtime</div>
{driverOption ? (
Expand Down
25 changes: 13 additions & 12 deletions apps/web/src/components/settings/ProviderSettingsForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,8 @@ interface ProviderSettingsFormProps {
readonly idPrefix: string;
/**
* `card` stacks label over control, `dialog` is the compact wizard layout,
* `grid` emits a label cell and a control cell per field for a parent
* two-column grid (label column left, control right), with the description
* beside a fixed-width control so each field stays on one line.
* `grid` emits label and control cells followed by an optional full-width
* description row for a parent two-column grid.
*/
readonly variant: "card" | "dialog" | "grid";
readonly onChange: (nextConfig: Record<string, unknown> | undefined) => void;
Expand Down Expand Up @@ -204,9 +203,8 @@ function ProviderSettingsFieldRow({
) : null;

if (variant === "grid") {
// Label cell, then a control cell where the description sits beside a
// fixed-width control and wraps under it when the pane is narrow. The
// description is outside the label, so the control points at it instead.
// Label and control cells are followed by a full-width description row.
// The description remains outside the label so the control points at it.
const descriptionId = field.description ? `${inputId}-description` : undefined;
return (
<>
Expand All @@ -217,7 +215,7 @@ function ProviderSettingsFieldRow({
{field.label}
</label>
)}
<div className="flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1">
<div className="flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1 sm:justify-end">
{field.control === "switch" ? (
<span className="flex h-8 items-center sm:h-7">
<Switch
Expand Down Expand Up @@ -255,12 +253,15 @@ function ProviderSettingsFieldRow({
spellCheck={false}
/>
)}
{field.description ? (
<span id={descriptionId} className="text-[11px] leading-snug text-muted-foreground">
{field.description}
</span>
) : null}
</div>
{field.description ? (
<span
id={descriptionId}
className="col-span-full -mt-1.5 text-[11px] leading-snug text-muted-foreground"
>
{field.description}
</span>
) : null}
</>
);
}
Expand Down
11 changes: 4 additions & 7 deletions apps/web/src/components/settings/ProviderSettingsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,7 @@ function ProviderSettingsPanelContent() {
const deviceTabs =
!onlyPrimaryDevice && options.length > 0 ? (
<ScrollArea hideScrollbars scrollFade className="mx-3 h-11 min-w-0 rounded-none sm:mx-4">
<div
role="group"
aria-label="Devices"
className="flex h-full w-max min-w-full border-b border-border/70 px-1"
>
<div role="group" aria-label="Devices" className="flex h-full w-max min-w-full px-1">
{options.map((environment) => {
const Icon = providerEnvironmentIcon(environment);
const selected = environment.environmentId === effectiveEnvironmentId;
Expand Down Expand Up @@ -857,6 +853,7 @@ export function EnvironmentProviderSettings({
<>
<SettingsSection
{...searchableSetting("providers")}
variant="plain"
headerAction={
<div className="flex min-w-0 items-center gap-2">
{readOnly ? (
Expand Down Expand Up @@ -912,7 +909,7 @@ export function EnvironmentProviderSettings({
{readOnly ? (
<SettingsRow
title="Limited permissions"
description={`This session can view ${environmentLabel}'s providers, but its credential does not allow changing their configuration.`}
description={`This session can view ${environmentLabel}'s providers but can't change their settings.`}
/>
) : null}
<div className="space-y-1">
Expand Down Expand Up @@ -959,7 +956,7 @@ export function EnvironmentProviderSettings({
</PolicyTooltip>
</span>
}
description="Refresh availability, versions, auth state, and models in the background. 0 seconds turns background checks off."
description="Refresh provider status, versions, and models in the background. Set to 0 to disable."
resetAction={
providerHealthRefreshIntervalSeconds !==
defaultProviderHealthRefreshIntervalSeconds ? (
Expand Down
Loading
Loading