authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-02 15:56:28-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-02 15:56:28-07:00
logb59b7d0af9536a2d61ddfcf8d33420b890b2faca
tree30b4716b04307a9554c2bb9a77513f581fe93954
parentfb93902ee24d4ba380508df33d25478d3baf7a72
signature Signed by PGP key B5690EEEBB952194

fix(web): unify control sizing across settings pages (#9281)

Co-authored-by: Claude Code <noreply@anthropic.com>

20 files changed, 154 insertions(+), 93 deletions(-)

apps/web/src/components/BranchToolbarBranchSelector.tsx+1-1
...@@ -850,7 +850,7 @@ export function BranchToolbarBranchSelector({...@@ -850,7 +850,7 @@ export function BranchToolbarBranchSelector({
850 <Switch850 <Switch
851 id={startFromOriginSwitchId}851 id={startFromOriginSwitchId}
852 checked={startFromOrigin}852 checked={startFromOrigin}
853 className="[--thumb-size:--spacing(3.5)]"853 size="sm"
854 aria-label="Start worktree from origin"854 aria-label="Start worktree from origin"
855 onCheckedChange={(checked) => onStartFromOriginChange(Boolean(checked))}855 onCheckedChange={(checked) => onStartFromOriginChange(Boolean(checked))}
856 />856 />
apps/web/src/components/ServerUpdateAction.tsx+4-2
...@@ -83,6 +83,7 @@ export function ServerUpdateAction({...@@ -83,6 +83,7 @@ export function ServerUpdateAction({
83 targetVersion,83 targetVersion,
84 label = "Update",84 label = "Update",
85 variant = "outline",85 variant = "outline",
86 size = "xs",
86}: {87}: {
87 readonly environmentId: EnvironmentId;88 readonly environmentId: EnvironmentId;
88 readonly serverLabel: string;89 readonly serverLabel: string;
...@@ -95,6 +96,7 @@ export function ServerUpdateAction({...@@ -95,6 +96,7 @@ export function ServerUpdateAction({
95 readonly targetVersion: string;96 readonly targetVersion: string;
96 readonly label?: string;97 readonly label?: string;
97 readonly variant?: ComponentProps<typeof Button>["variant"];98 readonly variant?: ComponentProps<typeof Button>["variant"];
99 readonly size?: ComponentProps<typeof Button>["size"];
98}) {100}) {
99 const isDesktopAppUpdate = selfUpdate === "desktop-managed";101 const isDesktopAppUpdate = selfUpdate === "desktop-managed";
100 const continueThreadsAfterServerUpdate = useClientSettings(102 const continueThreadsAfterServerUpdate = useClientSettings(
...@@ -185,14 +187,14 @@ export function ServerUpdateAction({...@@ -185,14 +187,14 @@ export function ServerUpdateAction({
185 if (selfUpdate === null) {187 if (selfUpdate === null) {
186 const command = manualServerUpdateCommand(targetVersion);188 const command = manualServerUpdateCommand(targetVersion);
187 return (189 return (
188 <Button size="xs" variant={variant} onClick={() => copyToClipboard(command, { command })}>190 <Button size={size} variant={variant} onClick={() => copyToClipboard(command, { command })}>
189 Copy update command191 Copy update command
190 </Button>192 </Button>
191 );193 );
192 }194 }
193195
194 return (196 return (
195 <Button size="xs" variant={variant} onClick={() => void handleUpdate()}>197 <Button size={size} variant={variant} onClick={() => void handleUpdate()}>
196 {label}198 {label}
197 </Button>199 </Button>
198 );200 );
apps/web/src/components/settings/AddProviderInstanceDialog.tsx+4-9
...@@ -380,9 +380,9 @@ export function AddProviderInstanceDialog({...@@ -380,9 +380,9 @@ export function AddProviderInstanceDialog({
380 {accentColor ? (380 {accentColor ? (
381 <Button381 <Button
382 type="button"382 type="button"
383 size="sm"383 size="xs"
384 variant="ghost"384 variant="ghost"
385 className="h-7 px-2 text-xs text-muted-foreground"385 className="text-muted-foreground"
386 onClick={() => setAccentColor("")}386 onClick={() => setAccentColor("")}
387 >387 >
388 Clear388 Clear
...@@ -417,7 +417,6 @@ export function AddProviderInstanceDialog({...@@ -417,7 +417,6 @@ export function AddProviderInstanceDialog({
417 <DialogFooter variant="bare">417 <DialogFooter variant="bare">
418 <Button418 <Button
419 variant="outline"419 variant="outline"
420 size="sm"
421 onClick={() => {420 onClick={() => {
422 if (wizardStep === 0) {421 if (wizardStep === 0) {
423 onOpenChange(false);422 onOpenChange(false);
...@@ -429,13 +428,9 @@ export function AddProviderInstanceDialog({...@@ -429,13 +428,9 @@ export function AddProviderInstanceDialog({
429 {wizardStep === 0 ? "Cancel" : "Back"}428 {wizardStep === 0 ? "Cancel" : "Back"}
430 </Button>429 </Button>
431 {wizardStep < ADD_PROVIDER_WIZARD_STEPS.length - 1 ? (430 {wizardStep < ADD_PROVIDER_WIZARD_STEPS.length - 1 ? (
432 <Button size="sm" onClick={() => navigateToStep(wizardStep + 1)}>431 <Button onClick={() => navigateToStep(wizardStep + 1)}>Next</Button>
433 Next
434 </Button>
435 ) : (432 ) : (
436 <Button size="sm" onClick={handleSave}>433 <Button onClick={handleSave}>Add instance</Button>
437 Add instance
438 </Button>
439 )}434 )}
440 </DialogFooter>435 </DialogFooter>
441 </div>436 </div>
apps/web/src/components/settings/ConnectionsSettings.tsx+6-3
...@@ -785,7 +785,7 @@ const PairingLinkListRow = memo(function PairingLinkListRow({...@@ -785,7 +785,7 @@ const PairingLinkListRow = memo(function PairingLinkListRow({
785 Done785 Done
786 </Button>786 </Button>
787 {canCopyToClipboard ? (787 {canCopyToClipboard ? (
788 <Button variant="outline" size="xs" onClick={handleCopyCode}>788 <Button variant="outline" onClick={handleCopyCode}>
789 Copy code789 Copy code
790 </Button>790 </Button>
791 ) : null}791 ) : null}
...@@ -2784,7 +2784,7 @@ export function ConnectionsSettings() {...@@ -2784,7 +2784,7 @@ export function ConnectionsSettings() {
2784 status={<span className="block text-destructive">{desktopWslError}</span>}2784 status={<span className="block text-destructive">{desktopWslError}</span>}
2785 control={2785 control={
2786 <Button2786 <Button
2787 size="xs"2787 size="sm"
2788 variant="outline"2788 variant="outline"
2789 onClick={loadWslState}2789 onClick={loadWslState}
2790 disabled={isLoadingWslState}2790 disabled={isLoadingWslState}
...@@ -2819,6 +2819,7 @@ export function ConnectionsSettings() {...@@ -2819,6 +2819,7 @@ export function ConnectionsSettings() {
2819 }2819 }
2820 control={2820 control={
2821 <Button2821 <Button
2822 size="sm"
2822 variant="outline"2823 variant="outline"
2823 disabled={isUpdatingWslBackend}2824 disabled={isUpdatingWslBackend}
2824 onClick={() => handleSelectWslMode(BACKEND_VALUE_WSL_OFF)}2825 onClick={() => handleSelectWslMode(BACKEND_VALUE_WSL_OFF)}
...@@ -2867,6 +2868,7 @@ export function ConnectionsSettings() {...@@ -2867,6 +2868,7 @@ export function ConnectionsSettings() {
2867 }}2868 }}
2868 >2869 >
2869 <SelectTrigger2870 <SelectTrigger
2871 size="sm"
2870 className="w-full sm:w-56"2872 className="w-full sm:w-56"
2871 aria-label="WSL backend"2873 aria-label="WSL backend"
2872 disabled={isUpdatingWslBackend}2874 disabled={isUpdatingWslBackend}
...@@ -3062,6 +3064,7 @@ export function ConnectionsSettings() {...@@ -3062,6 +3064,7 @@ export function ConnectionsSettings() {
3062 primaryEnvironmentId !== null &&3064 primaryEnvironmentId !== null &&
3063 primaryServerUpdateState.status !== "running" ? (3065 primaryServerUpdateState.status !== "running" ? (
3064 <ServerUpdateAction3066 <ServerUpdateAction
3067 size="sm"
3065 environmentId={primaryEnvironmentId}3068 environmentId={primaryEnvironmentId}
3066 serverLabel={3069 serverLabel={
3067 primaryEnvironment ? `${primaryEnvironment.label} server` : "server"3070 primaryEnvironment ? `${primaryEnvironment.label} server` : "server"
...@@ -3418,7 +3421,7 @@ export function ConnectionsSettings() {...@@ -3418,7 +3421,7 @@ export function ConnectionsSettings() {
3418 <Button3421 <Button
3419 size="xs"3422 size="xs"
3420 variant="ghost"3423 variant="ghost"
3421 className="h-5 gap-1 rounded-sm px-1 text-[11px] font-normal text-muted-foreground/60 hover:text-muted-foreground"3424 className="font-normal text-muted-foreground/60 hover:text-muted-foreground"
3422 aria-label="Add environment"3425 aria-label="Add environment"
3423 >3426 >
3424 <PlusIcon className="size-3" />3427 <PlusIcon className="size-3" />
apps/web/src/components/settings/DiagnosticsSettings.tsx+4-4
...@@ -793,13 +793,13 @@ function DiagnosticsRefreshButton({...@@ -793,13 +793,13 @@ function DiagnosticsRefreshButton({
793 <TooltipTrigger793 <TooltipTrigger
794 render={794 render={
795 <Button795 <Button
796 size="icon-micro"796 size="icon-xs"
797 variant="ghost-muted"797 variant="ghost-muted"
798 disabled={isPending}798 disabled={isPending}
799 onClick={onClick}799 onClick={onClick}
800 aria-label={label}800 aria-label={label}
801 >801 >
802 <RefreshCwIcon className={cn("size-3", isPending && "animate-spin")} />802 <RefreshCwIcon className={cn(isPending && "animate-spin")} />
803 </Button>803 </Button>
804 }804 }
805 />805 />
...@@ -1129,13 +1129,13 @@ export function DiagnosticsSettingsPanel() {...@@ -1129,13 +1129,13 @@ export function DiagnosticsSettingsPanel() {
1129 <TooltipTrigger1129 <TooltipTrigger
1130 render={1130 render={
1131 <Button1131 <Button
1132 size="icon-micro"1132 size="icon-xs"
1133 variant="ghost-muted"1133 variant="ghost-muted"
1134 disabled={!observability?.logsDirectoryPath || isOpeningLogsDirectory}1134 disabled={!observability?.logsDirectoryPath || isOpeningLogsDirectory}
1135 onClick={openLogsDirectory}1135 onClick={openLogsDirectory}
1136 aria-label="Open logs folder"1136 aria-label="Open logs folder"
1137 >1137 >
1138 <FolderOpenIcon className="size-3" />1138 <FolderOpenIcon />
1139 </Button>1139 </Button>
1140 }1140 }
1141 />1141 />
apps/web/src/components/settings/FontFamilyPicker.tsx+3-5
...@@ -11,6 +11,7 @@ import {...@@ -11,6 +11,7 @@ import {
11 ComboboxPopup,11 ComboboxPopup,
12 ComboboxTrigger,12 ComboboxTrigger,
13} from "../ui/combobox";13} from "../ui/combobox";
14import { selectTriggerVariants } from "../ui/select";
1415
15const DEFAULT_FONT_VALUE = "__default__";16const DEFAULT_FONT_VALUE = "__default__";
1617
...@@ -203,14 +204,11 @@ export function FontFamilyPicker({...@@ -203,14 +204,11 @@ export function FontFamilyPicker({
203 void listRef.current?.scrollIndexIntoView?.({ index: eventDetails.index, animated: false });204 void listRef.current?.scrollIndexIntoView?.({ index: eventDetails.index, animated: false });
204 }}205 }}
205 >206 >
206 <ComboboxTrigger207 <ComboboxTrigger aria-label={ariaLabel} className={selectTriggerVariants({ size: "sm" })}>
207 aria-label={ariaLabel}
208 className="relative inline-flex min-h-9 w-full min-w-36 cursor-pointer select-none items-center justify-between gap-2 rounded-lg border border-input bg-background px-[calc(--spacing(3)-1px)] text-left text-base text-foreground shadow-xs/5 outline-none transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/24 sm:min-h-8 sm:text-sm dark:bg-input/32"
209 >
210 <span className="min-w-0 truncate">208 <span className="min-w-0 truncate">
211 {selectedFamily.length === 0 ? defaultFamily : selectedFamily}209 {selectedFamily.length === 0 ? defaultFamily : selectedFamily}
212 </span>210 </span>
213 <ChevronDownIcon className="-me-1 size-3 shrink-0 text-muted-foreground opacity-50" />211 <ChevronDownIcon className="-me-1 size-3 opacity-50" />
214 </ComboboxTrigger>212 </ComboboxTrigger>
215 <ComboboxPopup align="end" className="flex w-72 flex-col">213 <ComboboxPopup align="end" className="flex w-72 flex-col">
216 <div className="shrink-0 px-3 pt-2.5">214 <div className="shrink-0 px-3 pt-2.5">
apps/web/src/components/settings/IntegrationsSettings.tsx+12-4
...@@ -354,7 +354,7 @@ function BrowserZoomSetting({ disabled }: { readonly disabled: boolean }) {...@@ -354,7 +354,7 @@ function BrowserZoomSetting({ disabled }: { readonly disabled: boolean }) {
354 if (next !== undefined) updateSettings({ browserDefaultZoomFactor: next });354 if (next !== undefined) updateSettings({ browserDefaultZoomFactor: next });
355 }}355 }}
356 >356 >
357 <SelectTrigger className="w-full sm:w-40" aria-label="Default browser zoom">357 <SelectTrigger size="sm" className="w-full sm:w-40" aria-label="Default browser zoom">
358 <SelectValue>{zoomLabel(zoomFactor)}</SelectValue>358 <SelectValue>{zoomLabel(zoomFactor)}</SelectValue>
359 </SelectTrigger>359 </SelectTrigger>
360 <SelectPopup align="end" alignItemWithTrigger={false}>360 <SelectPopup align="end" alignItemWithTrigger={false}>
...@@ -396,7 +396,11 @@ function BrowserAppearanceSetting({ disabled }: { readonly disabled: boolean })...@@ -396,7 +396,11 @@ function BrowserAppearanceSetting({ disabled }: { readonly disabled: boolean })
396 }396 }
397 }}397 }}
398 >398 >
399 <SelectTrigger className="w-full sm:w-40" aria-label="Default browser appearance">399 <SelectTrigger
400 size="sm"
401 className="w-full sm:w-40"
402 aria-label="Default browser appearance"
403 >
400 <SelectValue>{APPEARANCE_LABELS[appearance]}</SelectValue>404 <SelectValue>{APPEARANCE_LABELS[appearance]}</SelectValue>
401 </SelectTrigger>405 </SelectTrigger>
402 <SelectPopup align="end" alignItemWithTrigger={false}>406 <SelectPopup align="end" alignItemWithTrigger={false}>
...@@ -441,7 +445,11 @@ function BrowserRecordingFrameRateSetting({ disabled }: { readonly disabled: boo...@@ -441,7 +445,11 @@ function BrowserRecordingFrameRateSetting({ disabled }: { readonly disabled: boo
441 }445 }
442 }}446 }}
443 >447 >
444 <SelectTrigger className="w-full sm:w-40" aria-label="Browser recording frame rate">448 <SelectTrigger
449 size="sm"
450 className="w-full sm:w-40"
451 aria-label="Browser recording frame rate"
452 >
445 <SelectValue>{frameRate} fps</SelectValue>453 <SelectValue>{frameRate} fps</SelectValue>
446 </SelectTrigger>454 </SelectTrigger>
447 <SelectPopup align="end" alignItemWithTrigger={false}>455 <SelectPopup align="end" alignItemWithTrigger={false}>
...@@ -706,7 +714,7 @@ function BrowserProfilesSetting({ disabled }: { readonly disabled: boolean }) {...@@ -706,7 +714,7 @@ function BrowserProfilesSetting({ disabled }: { readonly disabled: boolean }) {
706 render={714 render={
707 <span className="inline-flex" {...(!removalAvailable ? { tabIndex: 0 } : {})}>715 <span className="inline-flex" {...(!removalAvailable ? { tabIndex: 0 } : {})}>
708 <Button716 <Button
709 size="icon-sm"717 size="icon-xs"
710 variant="ghost-muted"718 variant="ghost-muted"
711 disabled={profileWritesDisabled || !removalAvailable}719 disabled={profileWritesDisabled || !removalAvailable}
712 aria-label={`Remove ${profile.name}`}720 aria-label={`Remove ${profile.name}`}
apps/web/src/components/settings/KeybindingsSettings.tsx+15-15
...@@ -131,12 +131,12 @@ function ExpandableHeaderSearch({...@@ -131,12 +131,12 @@ function ExpandableHeaderSearch({
131 render={131 render={
132 <Button132 <Button
133 type="button"133 type="button"
134 size="icon-micro"134 size="icon-xs"
135 variant="ghost-muted"135 variant="ghost-muted"
136 onClick={() => onOpenChange(true)}136 onClick={() => onOpenChange(true)}
137 aria-label="Search keybindings"137 aria-label="Search keybindings"
138 >138 >
139 <SearchIcon className="size-3" />139 <SearchIcon />
140 </Button>140 </Button>
141 }141 }
142 />142 />
...@@ -168,7 +168,7 @@ function ExpandableHeaderSearch({...@@ -168,7 +168,7 @@ function ExpandableHeaderSearch({
168 placeholder="Search keybindings"168 placeholder="Search keybindings"
169 aria-label="Search keybindings"169 aria-label="Search keybindings"
170 className="w-44 [&_[data-slot=input]]:pl-7"170 className="w-44 [&_[data-slot=input]]:pl-7"
171 size="compact"171 size="sm"
172 />172 />
173 </div>173 </div>
174 );174 );
...@@ -850,7 +850,7 @@ function KeybindingKeyControl({...@@ -850,7 +850,7 @@ function KeybindingKeyControl({
850 <>850 <>
851 {isDirty ? (851 {isDirty ? (
852 <Button852 <Button
853 size="compact"853 size="sm"
854 disabled={isSaving || keyDraft.trim().length === 0 || !isWhenDraftValid}854 disabled={isSaving || keyDraft.trim().length === 0 || !isWhenDraftValid}
855 onClick={save}855 onClick={save}
856 >856 >
...@@ -863,7 +863,7 @@ function KeybindingKeyControl({...@@ -863,7 +863,7 @@ function KeybindingKeyControl({
863 onClick={() => setDraft({ isRecording: true })}863 onClick={() => setDraft({ isRecording: true })}
864 aria-label={`Edit shortcut for ${commandLabel(row.command)}: ${formatShortcutLabel(row.binding.shortcut)}`}864 aria-label={`Edit shortcut for ${commandLabel(row.command)}: ${formatShortcutLabel(row.binding.shortcut)}`}
865 className={cn(865 className={cn(
866 "inline-flex h-7 cursor-pointer items-center rounded-md border border-transparent px-1.5 outline-none transition-colors hover:border-border/70 hover:bg-accent focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/24",866 "inline-flex h-8 cursor-pointer items-center rounded-md border border-transparent px-1.5 sm:h-7 outline-none transition-colors hover:border-border/70 hover:bg-accent focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/24",
867 pillClassName,867 pillClassName,
868 )}868 )}
869 >869 >
...@@ -876,7 +876,7 @@ function KeybindingKeyControl({...@@ -876,7 +876,7 @@ function KeybindingKeyControl({
876 aria-label={`Keybinding for ${commandLabel(row.command)}`}876 aria-label={`Keybinding for ${commandLabel(row.command)}`}
877 value={isRecording ? "" : keyDraft}877 value={isRecording ? "" : keyDraft}
878 placeholder={isRecording ? "Press shortcut" : "Unassigned"}878 placeholder={isRecording ? "Press shortcut" : "Unassigned"}
879 size="compact"879 size="sm"
880 className={cn("w-44 font-mono", isRecording && "border-primary/70 bg-primary/5")}880 className={cn("w-44 font-mono", isRecording && "border-primary/70 bg-primary/5")}
881 onFocus={() => setDraft({ isRecording: true })}881 onFocus={() => setDraft({ isRecording: true })}
882 onBlur={() => setDraft({ isRecording: false })}882 onBlur={() => setDraft({ isRecording: false })}
...@@ -954,7 +954,7 @@ function KeybindingRowMenu({...@@ -954,7 +954,7 @@ function KeybindingRowMenu({
954 type="button"954 type="button"
955 variant="ghost"955 variant="ghost"
956 size="icon-sm"956 size="icon-sm"
957 className="size-7 text-muted-foreground hover:text-foreground sm:size-7"957 className="text-muted-foreground hover:text-foreground"
958 disabled={isSaving}958 disabled={isSaving}
959 aria-label={`Actions for ${commandLabel(row.command)}`}959 aria-label={`Actions for ${commandLabel(row.command)}`}
960 />960 />
...@@ -1155,7 +1155,7 @@ function NewKeybindingCommandSelect({...@@ -1155,7 +1155,7 @@ function NewKeybindingCommandSelect({
1155 value={draft.commandDraft}1155 value={draft.commandDraft}
1156 onValueChange={(value) => draft.setCommandDraft(value as KeybindingCommand)}1156 onValueChange={(value) => draft.setCommandDraft(value as KeybindingCommand)}
1157 >1157 >
1158 <SelectTrigger size="compact" className={className}>1158 <SelectTrigger size="sm" className={className}>
1159 <SelectValue placeholder="Command" />1159 <SelectValue placeholder="Command" />
1160 </SelectTrigger>1160 </SelectTrigger>
1161 <SelectContent1161 <SelectContent
...@@ -1189,7 +1189,7 @@ function NewKeybindingKeyInput({...@@ -1189,7 +1189,7 @@ function NewKeybindingKeyInput({
1189 aria-label={`Keybinding for ${draft.commandLabelText}`}1189 aria-label={`Keybinding for ${draft.commandLabelText}`}
1190 value={draft.isRecording ? "" : draft.keyDraft}1190 value={draft.isRecording ? "" : draft.keyDraft}
1191 placeholder={draft.isRecording ? "Press shortcut" : "Unassigned"}1191 placeholder={draft.isRecording ? "Press shortcut" : "Unassigned"}
1192 size="compact"1192 size="sm"
1193 className={cn("font-mono", draft.isRecording && "border-primary/70 bg-primary/5", className)}1193 className={cn("font-mono", draft.isRecording && "border-primary/70 bg-primary/5", className)}
1194 onFocus={() => draft.setDraft({ isRecording: true })}1194 onFocus={() => draft.setDraft({ isRecording: true })}
1195 onBlur={() => draft.setDraft({ isRecording: false })}1195 onBlur={() => draft.setDraft({ isRecording: false })}
...@@ -1233,7 +1233,7 @@ function NewKeybindingCancelIcon({...@@ -1233,7 +1233,7 @@ function NewKeybindingCancelIcon({
1233 type="button"1233 type="button"
1234 variant="ghost"1234 variant="ghost"
1235 size="icon-sm"1235 size="icon-sm"
1236 className="size-7 text-muted-foreground hover:text-foreground"1236 className="text-muted-foreground hover:text-foreground"
1237 disabled={isSaving}1237 disabled={isSaving}
1238 aria-label="Cancel new keybinding"1238 aria-label="Cancel new keybinding"
1239 onClick={onCancel}1239 onClick={onCancel}
...@@ -1271,7 +1271,7 @@ function NewKeybindingSettingsRow(props: NewKeybindingProps) {...@@ -1271,7 +1271,7 @@ function NewKeybindingSettingsRow(props: NewKeybindingProps) {
1271 />1271 />
1272 <KeybindingConflictWarning labels={draft.conflictLabels} />1272 <KeybindingConflictWarning labels={draft.conflictLabels} />
1273 <NewKeybindingKeyInput draft={draft} className="w-44" />1273 <NewKeybindingKeyInput draft={draft} className="w-44" />
1274 <Button size="compact" disabled={isSaving || !draft.canSave} onClick={draft.save}>1274 <Button size="sm" disabled={isSaving || !draft.canSave} onClick={draft.save}>
1275 {isSaving ? "Saving" : "Save"}1275 {isSaving ? "Saving" : "Save"}
1276 </Button>1276 </Button>
1277 <NewKeybindingCancelIcon isSaving={isSaving} onCancel={onCancel} />1277 <NewKeybindingCancelIcon isSaving={isSaving} onCancel={onCancel} />
...@@ -1514,12 +1514,12 @@ export function KeybindingsSettingsPanel() {...@@ -1514,12 +1514,12 @@ export function KeybindingsSettingsPanel() {
1514 render={1514 render={
1515 <Button1515 <Button
1516 type="button"1516 type="button"
1517 size="icon-micro"1517 size="icon-xs"
1518 variant="ghost-muted"1518 variant="ghost-muted"
1519 onClick={() => setIsAddingBinding(true)}1519 onClick={() => setIsAddingBinding(true)}
1520 aria-label="Add keybinding"1520 aria-label="Add keybinding"
1521 >1521 >
1522 <PlusIcon className="size-3" />1522 <PlusIcon />
1523 </Button>1523 </Button>
1524 }1524 }
1525 />1525 />
...@@ -1530,13 +1530,13 @@ export function KeybindingsSettingsPanel() {...@@ -1530,13 +1530,13 @@ export function KeybindingsSettingsPanel() {
1530 render={1530 render={
1531 <Button1531 <Button
1532 type="button"1532 type="button"
1533 size="icon-micro"1533 size="icon-xs"
1534 variant="ghost-muted"1534 variant="ghost-muted"
1535 disabled={!keybindingsConfigPath}1535 disabled={!keybindingsConfigPath}
1536 onClick={openKeybindingsFile}1536 onClick={openKeybindingsFile}
1537 aria-label="Open keybindings.json"1537 aria-label="Open keybindings.json"
1538 >1538 >
1539 <FileJsonIcon className="size-3" />1539 <FileJsonIcon />
1540 </Button>1540 </Button>
1541 }1541 }
1542 />1542 />
apps/web/src/components/settings/ProjectSettingsPanel.tsx+10-7
...@@ -106,6 +106,7 @@ import {...@@ -106,6 +106,7 @@ import {
106} from "../WorkspaceBreadcrumb";106} from "../WorkspaceBreadcrumb";
107import { WorkspacePageHeader } from "../WorkspacePageHeader";107import { WorkspacePageHeader } from "../WorkspacePageHeader";
108import {108import {
109 SETTINGS_PICKER_TRIGGER_CLASSNAME,
109 SettingResetButton,110 SettingResetButton,
110 SettingsPageContainer,111 SettingsPageContainer,
111 SettingsRow,112 SettingsRow,
...@@ -803,6 +804,7 @@ function ProjectDetail({ group }: { group: SidebarProjectSnapshot }) {...@@ -803,6 +804,7 @@ function ProjectDetail({ group }: { group: SidebarProjectSnapshot }) {
803 control={804 control={
804 <Input805 <Input
805 key={`${group.projectKey}:${group.displayName}`}806 key={`${group.projectKey}:${group.displayName}`}
807 size="sm"
806 className="w-full sm:w-64"808 className="w-full sm:w-64"
807 aria-label="Project name"809 aria-label="Project name"
808 defaultValue={group.displayName}810 defaultValue={group.displayName}
...@@ -841,7 +843,7 @@ function ProjectDetail({ group }: { group: SidebarProjectSnapshot }) {...@@ -841,7 +843,7 @@ function ProjectDetail({ group }: { group: SidebarProjectSnapshot }) {
841 className="size-6"843 className="size-6"
842 />844 />
843 <Button845 <Button
844 size="xs"846 size="sm"
845 variant="outline"847 variant="outline"
846 type="button"848 type="button"
847 aria-label="Choose a project icon file"849 aria-label="Choose a project icon file"
...@@ -874,7 +876,7 @@ function ProjectDetail({ group }: { group: SidebarProjectSnapshot }) {...@@ -874,7 +876,7 @@ function ProjectDetail({ group }: { group: SidebarProjectSnapshot }) {
874 instanceEntries={instanceEntries}876 instanceEntries={instanceEntries}
875 modelOptionsByInstance={modelOptionsByInstance}877 modelOptionsByInstance={modelOptionsByInstance}
876 triggerVariant="outline"878 triggerVariant="outline"
877 triggerClassName="min-w-0 max-w-none shrink-0 text-foreground/90 hover:text-foreground"879 triggerClassName={SETTINGS_PICKER_TRIGGER_CLASSNAME}
878 onInstanceModelChange={(instanceId, model) => {880 onInstanceModelChange={(instanceId, model) => {
879 setDefaultModel(createModelSelection(instanceId, model));881 setDefaultModel(createModelSelection(instanceId, model));
880 }}882 }}
...@@ -889,7 +891,7 @@ function ProjectDetail({ group }: { group: SidebarProjectSnapshot }) {...@@ -889,7 +891,7 @@ function ProjectDetail({ group }: { group: SidebarProjectSnapshot }) {
889 allowPromptInjectedEffort={false}891 allowPromptInjectedEffort={false}
890 planModeEnabled={projectSettings.planModeEnabled}892 planModeEnabled={projectSettings.planModeEnabled}
891 triggerVariant="outline"893 triggerVariant="outline"
892 triggerClassName="min-w-0 max-w-none shrink-0 text-foreground/90 hover:text-foreground"894 triggerClassName={SETTINGS_PICKER_TRIGGER_CLASSNAME}
893 onModelOptionsChange={(nextOptions) => {895 onModelOptionsChange={(nextOptions) => {
894 setDefaultModel(896 setDefaultModel(
895 createModelSelection(897 createModelSelection(
...@@ -928,7 +930,7 @@ function ProjectDetail({ group }: { group: SidebarProjectSnapshot }) {...@@ -928,7 +930,7 @@ function ProjectDetail({ group }: { group: SidebarProjectSnapshot }) {
928 }930 }
929 }}931 }}
930 >932 >
931 <SelectTrigger aria-label="New-thread workspace">933 <SelectTrigger size="sm" aria-label="New-thread workspace">
932 <SelectValue>934 <SelectValue>
933 {storedEnvMode === null935 {storedEnvMode === null
934 ? group.memberProjects.length > 1936 ? group.memberProjects.length > 1
...@@ -969,7 +971,7 @@ function ProjectDetail({ group }: { group: SidebarProjectSnapshot }) {...@@ -969,7 +971,7 @@ function ProjectDetail({ group }: { group: SidebarProjectSnapshot }) {
969 value={selectedCheckout.physicalProjectKey}971 value={selectedCheckout.physicalProjectKey}
970 onValueChange={(value) => setSelectedCheckoutKey(String(value))}972 onValueChange={(value) => setSelectedCheckoutKey(String(value))}
971 >973 >
972 <SelectTrigger className="max-w-64" aria-label="Selected checkout">974 <SelectTrigger size="sm" className="max-w-64" aria-label="Selected checkout">
973 <SelectValue>{selectedCheckoutLabel}</SelectValue>975 <SelectValue>{selectedCheckoutLabel}</SelectValue>
974 </SelectTrigger>976 </SelectTrigger>
975 <SelectPopup align="end" alignItemWithTrigger={false}>977 <SelectPopup align="end" alignItemWithTrigger={false}>
...@@ -1034,7 +1036,7 @@ function ProjectDetail({ group }: { group: SidebarProjectSnapshot }) {...@@ -1034,7 +1036,7 @@ function ProjectDetail({ group }: { group: SidebarProjectSnapshot }) {
1034 }1036 }
1035 }}1037 }}
1036 >1038 >
1037 <SelectTrigger aria-label={`Grouping rule for ${selectedCheckoutLabel}`}>1039 <SelectTrigger size="sm" aria-label={`Grouping rule for ${selectedCheckoutLabel}`}>
1038 <SelectValue>1040 <SelectValue>
1039 {selectedCheckoutGrouping === "inherit"1041 {selectedCheckoutGrouping === "inherit"
1040 ? `Default (${PROJECT_GROUPING_MODE_LABELS[projectGroupingSettings.sidebarProjectGroupingMode]})`1042 ? `Default (${PROJECT_GROUPING_MODE_LABELS[projectGroupingSettings.sidebarProjectGroupingMode]})`
...@@ -1064,7 +1066,7 @@ function ProjectDetail({ group }: { group: SidebarProjectSnapshot }) {...@@ -1064,7 +1066,7 @@ function ProjectDetail({ group }: { group: SidebarProjectSnapshot }) {
1064 description="Removes this checkout and its threads from the project group. Files on disk are not touched."1066 description="Removes this checkout and its threads from the project group. Files on disk are not touched."
1065 control={1067 control={
1066 <Button1068 <Button
1067 size="xs"1069 size="sm"
1068 variant="destructive-outline"1070 variant="destructive-outline"
1069 onClick={() => void removeMembers([selectedCheckout])}1071 onClick={() => void removeMembers([selectedCheckout])}
1070 >1072 >
...@@ -1210,6 +1212,7 @@ function ProjectDetail({ group }: { group: SidebarProjectSnapshot }) {...@@ -1210,6 +1212,7 @@ function ProjectDetail({ group }: { group: SidebarProjectSnapshot }) {
1210 }1212 }
1211 control={1213 control={
1212 <Button1214 <Button
1215 size="sm"
1213 variant="destructive-outline"1216 variant="destructive-outline"
1214 onClick={() => void removeMembers(group.memberProjects)}1217 onClick={() => void removeMembers(group.memberProjects)}
1215 >1218 >
apps/web/src/components/settings/ProviderInstanceCard.tsx+6-5
...@@ -258,7 +258,7 @@ function ProviderEnvironmentSection(props: {...@@ -258,7 +258,7 @@ function ProviderEnvironmentSection(props: {
258 {rows.map((variable, index) => (258 {rows.map((variable, index) => (
259 <div key={variable.id} className="flex min-w-0 items-center gap-1.5">259 <div key={variable.id} className="flex min-w-0 items-center gap-1.5">
260 <DraftInput260 <DraftInput
261 size="compact"261 size="sm"
262 className="w-44 shrink-0 font-mono"262 className="w-44 shrink-0 font-mono"
263 value={variable.name}263 value={variable.name}
264 onCommit={(name) => updateVariable(variable.id, { name: name.trim() })}264 onCommit={(name) => updateVariable(variable.id, { name: name.trim() })}
...@@ -270,7 +270,7 @@ function ProviderEnvironmentSection(props: {...@@ -270,7 +270,7 @@ function ProviderEnvironmentSection(props: {
270 =270 =
271 </span>271 </span>
272 <DraftInput272 <DraftInput
273 size="compact"273 size="sm"
274 className="min-w-0 flex-1 font-mono"274 className="min-w-0 flex-1 font-mono"
275 value={variable.valueRedacted ? "" : variable.value}275 value={variable.valueRedacted ? "" : variable.value}
276 onCommit={(value) => updateVariable(variable.id, { value })}276 onCommit={(value) => updateVariable(variable.id, { value })}
...@@ -679,10 +679,10 @@ export function ProviderInstanceCard({...@@ -679,10 +679,10 @@ export function ProviderInstanceCard({
679 render={679 render={
680 <Button680 <Button
681 type="button"681 type="button"
682 size="icon-xs"682 size="icon-micro"
683 variant="ghost"683 variant="ghost"
684 className={cn(684 className={cn(
685 "size-5 rounded-sm p-0 [--control-icon-color:currentColor]",685 "[--control-icon-color:currentColor]",
686 versionAdvisory.emphasis === "strong"686 versionAdvisory.emphasis === "strong"
687 ? "text-warning hover:text-warning"687 ? "text-warning hover:text-warning"
688 : "text-muted-foreground hover:text-foreground",688 : "text-muted-foreground hover:text-foreground",
...@@ -748,7 +748,7 @@ export function ProviderInstanceCard({...@@ -748,7 +748,7 @@ export function ProviderInstanceCard({
748 type="button"748 type="button"
749 size="icon-xs"749 size="icon-xs"
750 variant="ghost"750 variant="ghost"
751 className="size-6 shrink-0 rounded-sm p-0 text-muted-foreground hover:text-foreground"751 className="shrink-0 text-muted-foreground hover:text-foreground"
752 onClick={() =>752 onClick={() =>
753 copyToClipboard(updateCommand, {753 copyToClipboard(updateCommand, {
754 providerName: displayName,754 providerName: displayName,
...@@ -858,6 +858,7 @@ export function ProviderInstanceCard({...@@ -858,6 +858,7 @@ export function ProviderInstanceCard({
858 <div className="flex min-w-0 flex-wrap items-center gap-3">858 <div className="flex min-w-0 flex-wrap items-center gap-3">
859 <DraftInput859 <DraftInput
860 id={`provider-instance-${instanceId}-display-name`}860 id={`provider-instance-${instanceId}-display-name`}
861 size="sm"
861 className="w-44"862 className="w-44"
862 value={instance.displayName ?? ""}863 value={instance.displayName ?? ""}
863 onCommit={updateDisplayName}864 onCommit={updateDisplayName}
apps/web/src/components/settings/ProviderModelsSection.tsx+5-4
...@@ -386,7 +386,7 @@ export function ProviderModelsSection({...@@ -386,7 +386,7 @@ export function ProviderModelsSection({
386 <Tooltip>386 <Tooltip>
387 <TooltipTrigger render={<span className="flex shrink-0 items-center" />}>387 <TooltipTrigger render={<span className="flex shrink-0 items-center" />}>
388 <Switch388 <Switch
389 className="sm:[--thumb-size:--spacing(3.5)]"389 size="sm"
390 checked={!isHidden}390 checked={!isHidden}
391 disabled={model.isCustom}391 disabled={model.isCustom}
392 onCheckedChange={(checked) => setHidden(model.slug, !checked)}392 onCheckedChange={(checked) => setHidden(model.slug, !checked)}
...@@ -463,7 +463,7 @@ export function ProviderModelsSection({...@@ -463,7 +463,7 @@ export function ProviderModelsSection({
463 value={filter}463 value={filter}
464 onChange={(event) => setFilter(event.target.value)}464 onChange={(event) => setFilter(event.target.value)}
465 placeholder="Filter models"465 placeholder="Filter models"
466 size="compact"466 size="sm"
467 className="w-56"467 className="w-56"
468 spellCheck={false}468 spellCheck={false}
469 aria-label="Filter models"469 aria-label="Filter models"
...@@ -509,6 +509,7 @@ export function ProviderModelsSection({...@@ -509,6 +509,7 @@ export function ProviderModelsSection({
509 <div className="mt-3 flex flex-col gap-2 sm:flex-row">509 <div className="mt-3 flex flex-col gap-2 sm:flex-row">
510 <Input510 <Input
511 id={`provider-instance-${instanceId}-custom-model`}511 id={`provider-instance-${instanceId}-custom-model`}
512 size="sm"
512 autoFocus513 autoFocus
513 value={input}514 value={input}
514 onChange={(event) => {515 onChange={(event) => {
...@@ -529,10 +530,10 @@ export function ProviderModelsSection({...@@ -529,10 +530,10 @@ export function ProviderModelsSection({
529 spellCheck={false}530 spellCheck={false}
530 />531 />
531 <div className="flex shrink-0 gap-2">532 <div className="flex shrink-0 gap-2">
532 <Button variant="outline" onClick={handleAdd}>533 <Button size="sm" variant="outline" onClick={handleAdd}>
533 Add534 Add
534 </Button>535 </Button>
535 <Button variant="ghost" onClick={cancelAdd}>536 <Button size="sm" variant="ghost" onClick={cancelAdd}>
536 Cancel537 Cancel
537 </Button>538 </Button>
538 </div>539 </div>
apps/web/src/components/settings/ProviderSettingsForm.tsx+3-1
...@@ -219,7 +219,7 @@ function ProviderSettingsFieldRow({...@@ -219,7 +219,7 @@ function ProviderSettingsFieldRow({
219 )}219 )}
220 <div className="flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1">220 <div className="flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1">
221 {field.control === "switch" ? (221 {field.control === "switch" ? (
222 <span className="flex h-8 items-center">222 <span className="flex h-8 items-center sm:h-7">
223 <Switch223 <Switch
224 checked={readProviderConfigBoolean(value, field.key, field.defaultBooleanValue)}224 checked={readProviderConfigBoolean(value, field.key, field.defaultBooleanValue)}
225 onCheckedChange={(checked) =>225 onCheckedChange={(checked) =>
...@@ -245,6 +245,7 @@ function ProviderSettingsFieldRow({...@@ -245,6 +245,7 @@ function ProviderSettingsFieldRow({
245 <DraftInput245 <DraftInput
246 id={inputId}246 id={inputId}
247 aria-describedby={descriptionId}247 aria-describedby={descriptionId}
248 size="sm"
248 className="w-56"249 className="w-56"
249 type={field.control === "password" ? "password" : undefined}250 type={field.control === "password" ? "password" : undefined}
250 autoComplete={field.control === "password" ? "off" : undefined}251 autoComplete={field.control === "password" ? "off" : undefined}
...@@ -313,6 +314,7 @@ function ProviderSettingsFieldRow({...@@ -313,6 +314,7 @@ function ProviderSettingsFieldRow({
313 {variant === "card" ? (314 {variant === "card" ? (
314 <DraftInput315 <DraftInput
315 id={inputId}316 id={inputId}
317 size="sm"
316 className="mt-1.5"318 className="mt-1.5"
317 type={type}319 type={type}
318 autoComplete={field.control === "password" ? "off" : undefined}320 autoComplete={field.control === "password" ? "off" : undefined}
apps/web/src/components/settings/ProviderSettingsPanel.tsx+3-3
...@@ -869,7 +869,7 @@ export function EnvironmentProviderSettings({...@@ -869,7 +869,7 @@ export function EnvironmentProviderSettings({
869 <TooltipTrigger869 <TooltipTrigger
870 render={870 render={
871 <Button871 <Button
872 size="compact"872 size="xs"
873 variant="ghost-muted"873 variant="ghost-muted"
874 disabled={isRefreshingProviders}874 disabled={isRefreshingProviders}
875 onClick={() => void refreshProviders()}875 onClick={() => void refreshProviders()}
...@@ -892,12 +892,12 @@ export function EnvironmentProviderSettings({...@@ -892,12 +892,12 @@ export function EnvironmentProviderSettings({
892 <TooltipTrigger892 <TooltipTrigger
893 render={893 render={
894 <Button894 <Button
895 size="icon-micro"895 size="icon-xs"
896 variant="ghost-muted"896 variant="ghost-muted"
897 onClick={() => setIsAddInstanceDialogOpen(true)}897 onClick={() => setIsAddInstanceDialogOpen(true)}
898 aria-label="Add provider"898 aria-label="Add provider"
899 >899 >
900 <PlusIcon className="size-3" />900 <PlusIcon />
901 </Button>901 </Button>
902 }902 }
903 />903 />
apps/web/src/components/settings/SettingsPanels.tsx+35-14
...@@ -146,6 +146,7 @@ import {...@@ -146,6 +146,7 @@ import {
146} from "./SettingsPanels.logic";146} from "./SettingsPanels.logic";
147import {147import {
148 PolicyTooltip,148 PolicyTooltip,
149 SETTINGS_PICKER_TRIGGER_CLASSNAME,
149 SettingResetButton,150 SettingResetButton,
150 SettingsPageContainer,151 SettingsPageContainer,
151 SettingsRow,152 SettingsRow,
...@@ -391,7 +392,7 @@ function AboutVersionSection() {...@@ -391,7 +392,7 @@ function AboutVersionSection() {
391 <TooltipTrigger392 <TooltipTrigger
392 render={393 render={
393 <Button394 <Button
394 size="xs"395 size="sm"
395 variant="outline"396 variant="outline"
396 disabled={buttonDisabled || isUpdateActionPending}397 disabled={buttonDisabled || isUpdateActionPending}
397 onClick={handleButtonClick}398 onClick={handleButtonClick}
...@@ -416,6 +417,7 @@ function AboutVersionSection() {...@@ -416,6 +417,7 @@ function AboutVersionSection() {
416 }}417 }}
417 >418 >
418 <SelectTrigger419 <SelectTrigger
420 size="sm"
419 className="w-full sm:w-40"421 className="w-full sm:w-40"
420 aria-label="Update track"422 aria-label="Update track"
421 disabled={isChangingUpdateChannel}423 disabled={isChangingUpdateChannel}
...@@ -449,7 +451,7 @@ function AboutVersionSection() {...@@ -449,7 +451,7 @@ function AboutVersionSection() {
449 );451 );
450 }}452 }}
451 >453 >
452 <SelectTrigger className="w-full sm:w-40" aria-label="Update track">454 <SelectTrigger size="sm" className="w-full sm:w-40" aria-label="Update track">
453 <SelectValue>{HOSTED_APP_CHANNEL_LABEL}</SelectValue>455 <SelectValue>{HOSTED_APP_CHANNEL_LABEL}</SelectValue>
454 </SelectTrigger>456 </SelectTrigger>
455 <SelectPopup align="end" alignItemWithTrigger={false}>457 <SelectPopup align="end" alignItemWithTrigger={false}>
...@@ -805,7 +807,11 @@ function BackgroundActivityAdvancedDialog({...@@ -805,7 +807,11 @@ function BackgroundActivityAdvancedDialog({
805 }807 }
806 }}808 }}
807 >809 >
808 <SelectTrigger className="w-full sm:w-40" aria-label="Shared background policy">810 <SelectTrigger
811 size="sm"
812 className="w-full sm:w-40"
813 aria-label="Shared background policy"
814 >
809 <SelectValue>{BACKGROUND_ACTIVITY_PROFILE_LABELS[activeProfile]}</SelectValue>815 <SelectValue>{BACKGROUND_ACTIVITY_PROFILE_LABELS[activeProfile]}</SelectValue>
810 </SelectTrigger>816 </SelectTrigger>
811 <SelectPopup align="end" alignItemWithTrigger={false}>817 <SelectPopup align="end" alignItemWithTrigger={false}>
...@@ -1194,7 +1200,11 @@ export function AppearanceSettingsPanel() {...@@ -1194,7 +1200,11 @@ export function AppearanceSettingsPanel() {
1194 }1200 }
1195 }}1201 }}
1196 >1202 >
1197 <SelectTrigger className="w-full sm:w-40" aria-label="Environment identification">1203 <SelectTrigger
1204 size="sm"
1205 className="w-full sm:w-40"
1206 aria-label="Environment identification"
1207 >
1198 <SelectValue>1208 <SelectValue>
1199 {ENVIRONMENT_IDENTIFICATION_LABELS[settings.environmentIdentificationMode]}1209 {ENVIRONMENT_IDENTIFICATION_LABELS[settings.environmentIdentificationMode]}
1200 </SelectValue>1210 </SelectValue>
...@@ -1701,6 +1711,7 @@ function FontFamilySettingsRow({...@@ -1701,6 +1711,7 @@ function FontFamilySettingsRow({
1701 />1711 />
1702 ) : (1712 ) : (
1703 <Input1713 <Input
1714 size="sm"
1704 aria-label={`${title} family`}1715 aria-label={`${title} family`}
1705 aria-invalid={draftPending || undefined}1716 aria-invalid={draftPending || undefined}
1706 autoCapitalize="off"1717 autoCapitalize="off"
...@@ -1760,7 +1771,7 @@ function FontFamilySettingsRow({...@@ -1760,7 +1771,7 @@ function FontFamilySettingsRow({
1760 }1771 }
1761 }}1772 }}
1762 >1773 >
1763 <SelectTrigger className="w-22 shrink-0" aria-label={size.label}>1774 <SelectTrigger size="sm" className="w-22 shrink-0" aria-label={size.label}>
1764 <SelectValue>{size.value} px</SelectValue>1775 <SelectValue>{size.value} px</SelectValue>
1765 </SelectTrigger>1776 </SelectTrigger>
1766 <SelectPopup align="end" alignItemWithTrigger={false}>1777 <SelectPopup align="end" alignItemWithTrigger={false}>
...@@ -1806,6 +1817,7 @@ function AutoSettleDaysInput({...@@ -1806,6 +1817,7 @@ function AutoSettleDaysInput({
18061817
1807 return (1818 return (
1808 <Input1819 <Input
1820 size="sm"
1809 type="number"1821 type="number"
1810 min={MIN_SIDEBAR_AUTO_SETTLE_AFTER_DAYS}1822 min={MIN_SIDEBAR_AUTO_SETTLE_AFTER_DAYS}
1811 max={MAX_SIDEBAR_AUTO_SETTLE_AFTER_DAYS}1823 max={MAX_SIDEBAR_AUTO_SETTLE_AFTER_DAYS}
...@@ -2167,7 +2179,7 @@ export function GeneralSettingsPanel() {...@@ -2167,7 +2179,7 @@ export function GeneralSettingsPanel() {
2167 }2179 }
2168 }}2180 }}
2169 >2181 >
2170 <SelectTrigger className="w-full sm:w-40" aria-label="Timestamp format">2182 <SelectTrigger size="sm" className="w-full sm:w-40" aria-label="Timestamp format">
2171 <SelectValue>{TIMESTAMP_FORMAT_LABELS[settings.timestampFormat]}</SelectValue>2183 <SelectValue>{TIMESTAMP_FORMAT_LABELS[settings.timestampFormat]}</SelectValue>
2172 </SelectTrigger>2184 </SelectTrigger>
2173 <SelectPopup align="end" alignItemWithTrigger={false}>2185 <SelectPopup align="end" alignItemWithTrigger={false}>
...@@ -2358,7 +2370,11 @@ export function GeneralSettingsPanel() {...@@ -2358,7 +2370,11 @@ export function GeneralSettingsPanel() {
2358 }2370 }
2359 }}2371 }}
2360 >2372 >
2361 <SelectTrigger className="w-full sm:w-40" aria-label="Background activity profile">2373 <SelectTrigger
2374 size="sm"
2375 className="w-full sm:w-40"
2376 aria-label="Background activity profile"
2377 >
2362 <SelectValue>2378 <SelectValue>
2363 {BACKGROUND_ACTIVITY_PROFILE_OPTION_LABELS[backgroundActivityProfileOption]}2379 {BACKGROUND_ACTIVITY_PROFILE_OPTION_LABELS[backgroundActivityProfileOption]}
2364 </SelectValue>2380 </SelectValue>
...@@ -2432,7 +2448,7 @@ export function GeneralSettingsPanel() {...@@ -2432,7 +2448,7 @@ export function GeneralSettingsPanel() {
2432 }2448 }
2433 }}2449 }}
2434 >2450 >
2435 <SelectTrigger className="w-full sm:w-44" aria-label="Default thread mode">2451 <SelectTrigger size="sm" className="w-full sm:w-44" aria-label="Default thread mode">
2436 <SelectValue>2452 <SelectValue>
2437 {settings.defaultThreadEnvMode === "worktree" ? "New worktree" : "Local"}2453 {settings.defaultThreadEnvMode === "worktree" ? "New worktree" : "Local"}
2438 </SelectValue>2454 </SelectValue>
...@@ -2500,6 +2516,7 @@ export function GeneralSettingsPanel() {...@@ -2500,6 +2516,7 @@ export function GeneralSettingsPanel() {
2500 }2516 }
2501 control={2517 control={
2502 <DraftInput2518 <DraftInput
2519 size="sm"
2503 className="w-full sm:w-72"2520 className="w-full sm:w-72"
2504 value={settings.addProjectBaseDirectory}2521 value={settings.addProjectBaseDirectory}
2505 onCommit={(next) => updateSettings({ addProjectBaseDirectory: next })}2522 onCommit={(next) => updateSettings({ addProjectBaseDirectory: next })}
...@@ -2611,7 +2628,11 @@ export function GeneralSettingsPanel() {...@@ -2611,7 +2628,11 @@ export function GeneralSettingsPanel() {
2611 }2628 }
2612 }}2629 }}
2613 >2630 >
2614 <SelectTrigger className="w-full sm:w-40" aria-label="Quit shortcut behavior">2631 <SelectTrigger
2632 size="sm"
2633 className="w-full sm:w-40"
2634 aria-label="Quit shortcut behavior"
2635 >
2615 <SelectValue>{QUIT_CONFIRMATION_MODE_LABELS[settings.confirmQuit]}</SelectValue>2636 <SelectValue>{QUIT_CONFIRMATION_MODE_LABELS[settings.confirmQuit]}</SelectValue>
2616 </SelectTrigger>2637 </SelectTrigger>
2617 <SelectPopup align="end" alignItemWithTrigger={false}>2638 <SelectPopup align="end" alignItemWithTrigger={false}>
...@@ -2652,7 +2673,7 @@ export function GeneralSettingsPanel() {...@@ -2652,7 +2673,7 @@ export function GeneralSettingsPanel() {
2652 instanceEntries={textGenerationModelInstanceEntries}2673 instanceEntries={textGenerationModelInstanceEntries}
2653 modelOptionsByInstance={textGenerationModelOptionsByInstance}2674 modelOptionsByInstance={textGenerationModelOptionsByInstance}
2654 triggerVariant="outline"2675 triggerVariant="outline"
2655 triggerClassName="min-w-0 max-w-none shrink-0 text-foreground/90 hover:text-foreground"2676 triggerClassName={SETTINGS_PICKER_TRIGGER_CLASSNAME}
2656 onInstanceModelChange={(instanceId, model) => {2677 onInstanceModelChange={(instanceId, model) => {
2657 updateSettings({2678 updateSettings({
2658 textGenerationModelSelection: resolveAppModelSelectionState(2679 textGenerationModelSelection: resolveAppModelSelectionState(
...@@ -2681,7 +2702,7 @@ export function GeneralSettingsPanel() {...@@ -2681,7 +2702,7 @@ export function GeneralSettingsPanel() {
2681 allowPromptInjectedEffort={false}2702 allowPromptInjectedEffort={false}
2682 planModeEnabled={settings.planModeEnabled}2703 planModeEnabled={settings.planModeEnabled}
2683 triggerVariant="outline"2704 triggerVariant="outline"
2684 triggerClassName="min-w-0 max-w-none shrink-0 text-foreground/90 hover:text-foreground"2705 triggerClassName={SETTINGS_PICKER_TRIGGER_CLASSNAME}
2685 onModelOptionsChange={(nextOptions) => {2706 onModelOptionsChange={(nextOptions) => {
2686 updateSettings({2707 updateSettings({
2687 textGenerationModelSelection: resolveAppModelSelectionState(2708 textGenerationModelSelection: resolveAppModelSelectionState(
...@@ -2716,7 +2737,7 @@ export function GeneralSettingsPanel() {...@@ -2716,7 +2737,7 @@ export function GeneralSettingsPanel() {
2716 {...searchableSetting("diagnostics")}2737 {...searchableSetting("diagnostics")}
2717 description={diagnosticsDescription}2738 description={diagnosticsDescription}
2718 control={2739 control={
2719 <Button render={<Link to="/settings/diagnostics" />} size="xs" variant="outline">2740 <Button render={<Link to="/settings/diagnostics" />} size="sm" variant="outline">
2720 View diagnostics2741 View diagnostics
2721 </Button>2742 </Button>
2722 }2743 }
...@@ -2924,8 +2945,8 @@ export function ArchivedThreadsPanel() {...@@ -2924,8 +2945,8 @@ export function ArchivedThreadsPanel() {
2924 <Button2945 <Button
2925 type="button"2946 type="button"
2926 variant="outline"2947 variant="outline"
2927 size="sm"2948 size="xs"
2928 className="h-7 shrink-0 cursor-pointer gap-1.5 px-2.5"2949 className="shrink-0"
2929 onClick={() => {2950 onClick={() => {
2930 void (async () => {2951 void (async () => {
2931 const result = await unarchiveThread(2952 const result = await unarchiveThread(
apps/web/src/components/settings/SharedSettingsMismatchAlert.tsx+1-1
...@@ -23,7 +23,7 @@ export function SharedSettingsMismatchAlert() {...@@ -23,7 +23,7 @@ export function SharedSettingsMismatchAlert() {
23 every environment.23 every environment.
24 </AlertDescription>24 </AlertDescription>
25 <AlertAction>25 <AlertAction>
26 <Button variant="outline" size="compact" onClick={applyToAll}>26 <Button variant="outline" size="xs" onClick={applyToAll}>
27 Apply to all27 Apply to all
28 </Button>28 </Button>
29 </AlertAction>29 </AlertAction>
apps/web/src/components/settings/SourceControlSettings.tsx+4-10
...@@ -312,7 +312,7 @@ function DiscoveryItemRow({...@@ -312,7 +312,7 @@ function DiscoveryItemRow({
312 <div className="flex w-full shrink-0 items-center gap-2 sm:w-auto sm:justify-end">312 <div className="flex w-full shrink-0 items-center gap-2 sm:w-auto sm:justify-end">
313 {hasDetails ? (313 {hasDetails ? (
314 <Button314 <Button
315 size="compact"315 size="icon-xs"
316 variant="ghost-muted"316 variant="ghost-muted"
317 onClick={() => setIsExpanded((open) => !open)}317 onClick={() => setIsExpanded((open) => !open)}
318 aria-expanded={isExpanded}318 aria-expanded={isExpanded}
...@@ -487,13 +487,7 @@ function EmptySourceControlDiscovery({...@@ -487,13 +487,7 @@ function EmptySourceControlDiscovery({
487 </EmptyDescription>487 </EmptyDescription>
488 </EmptyHeader>488 </EmptyHeader>
489 <EmptyContent>489 <EmptyContent>
490 <Button490 <Button size="sm" variant="outline" onClick={onScan} disabled={isPending}>
491 size="sm"
492 variant="outline"
493 className="h-8 gap-1.5 px-3 text-xs"
494 onClick={onScan}
495 disabled={isPending}
496 >
497 <RefreshCwIcon className={cn("size-3.5", isPending && "animate-spin")} />491 <RefreshCwIcon className={cn("size-3.5", isPending && "animate-spin")} />
498 Scan492 Scan
499 </Button>493 </Button>
...@@ -533,13 +527,13 @@ export function SourceControlSettingsPanel() {...@@ -533,13 +527,13 @@ export function SourceControlSettingsPanel() {
533 <TooltipTrigger527 <TooltipTrigger
534 render={528 render={
535 <Button529 <Button
536 size="icon-micro"530 size="icon-xs"
537 variant="ghost-muted"531 variant="ghost-muted"
538 onClick={handleScan}532 onClick={handleScan}
539 disabled={discovery.isPending}533 disabled={discovery.isPending}
540 aria-label="Rescan server environment"534 aria-label="Rescan server environment"
541 >535 >
542 <RefreshCwIcon className={cn("size-3", discovery.isPending && "animate-spin")} />536 <RefreshCwIcon className={cn(discovery.isPending && "animate-spin")} />
543 </Button>537 </Button>
544 }538 }
545 />539 />
apps/web/src/components/settings/SourceControlWritingSettings.tsx+12-3
...@@ -20,7 +20,12 @@ import { ProviderModelPicker } from "../chat/ProviderModelPicker";...@@ -20,7 +20,12 @@ import { ProviderModelPicker } from "../chat/ProviderModelPicker";
20import { Select, SelectItem, SelectPopup, SelectTrigger, SelectValue } from "../ui/select";20import { Select, SelectItem, SelectPopup, SelectTrigger, SelectValue } from "../ui/select";
21import { Switch } from "../ui/switch";21import { Switch } from "../ui/switch";
22import { Textarea } from "../ui/textarea";22import { Textarea } from "../ui/textarea";
23import { SettingResetButton, SettingsRow, SettingsSection } from "./settingsLayout";23import {
24 SETTINGS_PICKER_TRIGGER_CLASSNAME,
25 SettingResetButton,
26 SettingsRow,
27 SettingsSection,
28} from "./settingsLayout";
24import { searchableSetting } from "./settingsSearch";29import { searchableSetting } from "./settingsSearch";
2530
26const MODE_OPTIONS: Record<SourceControlWritingStyleMode, { label: string; description: string }> =31const MODE_OPTIONS: Record<SourceControlWritingStyleMode, { label: string; description: string }> =
...@@ -105,7 +110,11 @@ export function SourceControlWritingSettingsSection() {...@@ -105,7 +110,11 @@ export function SourceControlWritingSettingsSection() {
105 });110 });
106 }}111 }}
107 >112 >
108 <SelectTrigger className="w-full sm:w-56" aria-label="Source control writing style">113 <SelectTrigger
114 size="sm"
115 className="w-full sm:w-56"
116 aria-label="Source control writing style"
117 >
109 <SelectValue>{MODE_OPTIONS[style.mode].label}</SelectValue>118 <SelectValue>{MODE_OPTIONS[style.mode].label}</SelectValue>
110 </SelectTrigger>119 </SelectTrigger>
111 <SelectPopup align="end" alignItemWithTrigger={false}>120 <SelectPopup align="end" alignItemWithTrigger={false}>
...@@ -185,7 +194,7 @@ export function SourceControlWritingSettingsSection() {...@@ -185,7 +194,7 @@ export function SourceControlWritingSettingsSection() {
185 instanceEntries={instanceEntries}194 instanceEntries={instanceEntries}
186 modelOptionsByInstance={modelOptionsByInstance}195 modelOptionsByInstance={modelOptionsByInstance}
187 triggerVariant="outline"196 triggerVariant="outline"
188 triggerClassName="min-w-0 max-w-none shrink-0 text-foreground/90 hover:text-foreground"197 triggerClassName={SETTINGS_PICKER_TRIGGER_CLASSNAME}
189 triggerAriaLabel="Source control writer model"198 triggerAriaLabel="Source control writer model"
190 onInstanceModelChange={(instanceId, model) => {199 onInstanceModelChange={(instanceId, model) => {
191 updateSettings({200 updateSettings({
apps/web/src/components/settings/ThemeEditorPanel.tsx+2
...@@ -928,6 +928,7 @@ export function ThemeEditorPanel({...@@ -928,6 +928,7 @@ export function ThemeEditorPanel({
928 <span className="text-sm font-medium">Theme name</span>928 <span className="text-sm font-medium">Theme name</span>
929 <Input929 <Input
930 autoFocus930 autoFocus
931 size="sm"
931 onChange={(event) => {932 onChange={(event) => {
932 setName(event.currentTarget.value);933 setName(event.currentTarget.value);
933 // Most save failures are name collisions; retyping is the fix, so934 // Most save failures are name collisions; retyping is the fix, so
...@@ -949,6 +950,7 @@ export function ThemeEditorPanel({...@@ -949,6 +950,7 @@ export function ThemeEditorPanel({
949 <Button950 <Button
950 aria-disabled={lockReason !== null}951 aria-disabled={lockReason !== null}
951 aria-pressed={isActive}952 aria-pressed={isActive}
953 size="sm"
952 className={lockReason !== null ? "opacity-50" : undefined}954 className={lockReason !== null ? "opacity-50" : undefined}
953 style={isActive ? { boxShadow: "inset 0 0 0 1px var(--ring)" } : undefined}955 style={isActive ? { boxShadow: "inset 0 0 0 1px var(--ring)" } : undefined}
954 variant={isActive ? "secondary" : "outline"}956 variant={isActive ? "secondary" : "outline"}
apps/web/src/components/settings/settingsLayout.tsx+14
...@@ -100,6 +100,14 @@ export function SettingsSearchTarget({...@@ -100,6 +100,14 @@ export function SettingsSearchTarget({
100 );100 );
101}101}
102102
103/**
104 * Trigger classes for the composer model/traits pickers when they sit in a
105 * settings row: match the `sm` control box (the composer pins them to 28px at
106 * every breakpoint) and drop the composer's max-width.
107 */
108export const SETTINGS_PICKER_TRIGGER_CLASSNAME =
109 "h-8 min-h-8 min-w-0 max-w-none shrink-0 text-foreground/90 hover:text-foreground sm:h-7 sm:min-h-7";
110
103/** Info affordance explaining how a setting interacts with the shared background policy. */111/** Info affordance explaining how a setting interacts with the shared background policy. */
104export function PolicyTooltip({ children }: { readonly children: string }) {112export function PolicyTooltip({ children }: { readonly children: string }) {
105 return (113 return (
...@@ -168,6 +176,12 @@ export function SettingsSection({...@@ -168,6 +176,12 @@ export function SettingsSection({
168 * environment's settings.json; where there is no primary (the hosted app)176 * environment's settings.json; where there is no primary (the hosted app)
169 * the control goes inert with a tooltip instead of showing an editable177 * the control goes inert with a tooltip instead of showing an editable
170 * default that would never save.178 * default that would never save.
179 *
180 * Control sizing across settings follows three tiers so rows share a baseline:
181 * - `control` slot: `size="sm"` (Button, Select, Input, NumberField) or `icon-sm`.
182 * - Section `headerAction`s and buttons inside list items, cards, toolbars: `xs` / `icon-xs`.
183 * - Inline affordances (reset arrows, info tooltips, table-cell buttons): `icon-micro`.
184 * Dialog footers keep the app-wide default button size.
171 */185 */
172export function SettingsRow({186export function SettingsRow({
173 title,187 title,
apps/web/src/components/ui/switch.tsx+10-2
...@@ -4,13 +4,21 @@ import { Switch as SwitchPrimitive } from "@base-ui/react/switch";...@@ -4,13 +4,21 @@ import { Switch as SwitchPrimitive } from "@base-ui/react/switch";
44
5import { cn } from "~/lib/utils";5import { cn } from "~/lib/utils";
66
7function Switch({ className, ...props }: SwitchPrimitive.Root.Props) {7function Switch({
8 className,
9 size = "default",
10 ...props
11}: SwitchPrimitive.Root.Props & { size?: "default" | "sm" }) {
8 return (12 return (
9 <SwitchPrimitive.Root13 <SwitchPrimitive.Root
10 className={cn(14 className={cn(
11 "inline-flex h-[calc(var(--thumb-size)+2px)] w-[calc(var(--thumb-size)*2-2px)] shrink-0 cursor-pointer items-center rounded-full p-px outline-none transition-[background-color,box-shadow] duration-200 [--thumb-size:--spacing(5)] focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-checked:bg-primary data-unchecked:bg-input data-disabled:cursor-not-allowed data-disabled:opacity-64 sm:[--thumb-size:--spacing(4)]",15 "inline-flex h-[calc(var(--thumb-size)+2px)] w-[calc(var(--thumb-size)*2-2px)] shrink-0 cursor-pointer items-center rounded-full p-px outline-none transition-[background-color,box-shadow] duration-200 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-checked:bg-primary data-unchecked:bg-input data-disabled:cursor-not-allowed data-disabled:opacity-64",
16 size === "sm"
17 ? "[--thumb-size:--spacing(4)] sm:[--thumb-size:--spacing(3.5)]"
18 : "[--thumb-size:--spacing(5)] sm:[--thumb-size:--spacing(4)]",
12 className,19 className,
13 )}20 )}
21 data-size={size}
14 data-slot="switch"22 data-slot="switch"
15 {...props}23 {...props}
16 >24 >