| author | |
| committer | |
| log | 866fc907592c9746b407484087d66c409e8034fa |
| tree | 5c2f1f7ff106515694e5b2e1449721cd322284b7 |
| parent | 361b047f5f559abccacc802cd5ab9992b01473ea |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>12 files changed, 78 insertions(+), 96 deletions(-)
apps/web/src/components/BranchToolbar.tsx+3-3| ... | ... | @@ -41,7 +41,7 @@ import { |
| 41 | 41 | } from "./BranchToolbarBranchSelector"; |
| 42 | 42 | import { BranchToolbarEnvironmentSelector } from "./BranchToolbarEnvironmentSelector"; |
| 43 | 43 | import { BranchToolbarEnvModeSelector } from "./BranchToolbarEnvModeSelector"; |
| 44 | import { Button } from "./ui/button"; | |
| 44 | import { ComposerControl } from "./chat/ComposerControl"; | |
| 45 | 45 | import { |
| 46 | 46 | Menu, |
| 47 | 47 | MenuGroup, |
| ... | ... | @@ -206,8 +206,8 @@ const MobileRunContextSelector = memo(function MobileRunContextSelector({ |
| 206 | 206 | return ( |
| 207 | 207 | <Menu> |
| 208 | 208 | <MenuTrigger |
| 209 | render={<Button variant="ghost" size="xs" />} | |
| 210 | className="min-w-0 max-w-[48%] flex-initial justify-start font-normal text-muted-foreground/70 text-xs! hover:text-foreground/80" | |
| 209 | render={<ComposerControl size="xs" />} | |
| 210 | className="min-w-0 max-w-[48%] flex-initial justify-start" | |
| 211 | 211 | data-composer-context-control |
| 212 | 212 | data-composer-shortcut={[ |
| 213 | 213 | showEnvironmentPicker && !envLocked ? "composer.host" : "", |
apps/web/src/components/LegacySidebar.tsx+1-3| ... | ... | @@ -2732,9 +2732,7 @@ function ProjectSortMenu({ |
| 2732 | 2732 | <Menu> |
| 2733 | 2733 | <Tooltip> |
| 2734 | 2734 | <TooltipTrigger |
| 2735 | render={ | |
| 2736 | <MenuTrigger className="inline-flex h-6 min-w-6 cursor-pointer items-center justify-center rounded-md px-[calc(--spacing(1)-1px)] text-icon-muted transition-colors hover:bg-accent hover:text-foreground" /> | |
| 2737 | } | |
| 2735 | render={<MenuTrigger render={<Button size="icon-xs" variant="ghost-muted" />} />} | |
| 2738 | 2736 | > |
| 2739 | 2737 | <ArrowUpDownIcon className="size-3.5" /> |
| 2740 | 2738 | </TooltipTrigger> |
apps/web/src/components/ProjectScriptsControl.tsx+12-23| ... | ... | @@ -101,8 +101,6 @@ export default function ProjectScriptsControl({ |
| 101 | 101 | ), |
| 102 | 102 | [fileScripts, scripts], |
| 103 | 103 | ); |
| 104 | const dropdownItemClassName = | |
| 105 | "data-highlighted:bg-transparent data-highlighted:text-foreground hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground data-highlighted:hover:bg-accent data-highlighted:hover:text-accent-foreground data-highlighted:focus-visible:bg-accent data-highlighted:focus-visible:text-accent-foreground"; | |
| 106 | 104 | |
| 107 | 105 | const openAddDialog = () => { |
| 108 | 106 | setEditorRequest({ scriptId: null, initial: EMPTY_PROJECT_SCRIPT_INPUT }); |
| ... | ... | @@ -153,7 +151,6 @@ export default function ProjectScriptsControl({ |
| 153 | 151 | <MenuItem |
| 154 | 152 | density={presentation === "menu" ? "touch" : "default"} |
| 155 | 153 | key={`${fileScript.name} ${fileScript.command}`} |
| 156 | className={dropdownItemClassName} | |
| 157 | 154 | onClick={() => void importFileScript(fileScript)} |
| 158 | 155 | > |
| 159 | 156 | <ScriptIcon icon={fileScript.icon ?? "play"} className="size-4" /> |
| ... | ... | @@ -178,7 +175,7 @@ export default function ProjectScriptsControl({ |
| 178 | 175 | <MenuItem |
| 179 | 176 | density={presentation === "menu" ? "touch" : "default"} |
| 180 | 177 | key={script.id} |
| 181 | className={`group ${dropdownItemClassName}`} | |
| 178 | className="group" | |
| 182 | 179 | onClick={() => onRunScript(script)} |
| 183 | 180 | > |
| 184 | 181 | <ScriptIcon icon={script.icon} className="size-4" /> |
| ... | ... | @@ -186,17 +183,15 @@ export default function ProjectScriptsControl({ |
| 186 | 183 | {script.runOnWorktreeCreate ? `${script.name} (setup)` : script.name} |
| 187 | 184 | </MenuItemLabel> |
| 188 | 185 | <span className="relative ms-auto flex h-6 min-w-6 items-center justify-end"> |
| 189 | {shortcutLabel && ( | |
| 190 | <MenuShortcut | |
| 191 | className={ | |
| 192 | presentation === "menu" | |
| 193 | ? "ms-0 mr-7" | |
| 194 | : "ms-0 transition-opacity group-hover:opacity-0 group-focus-visible:opacity-0" | |
| 195 | } | |
| 196 | > | |
| 197 | {shortcutLabel} | |
| 198 | </MenuShortcut> | |
| 199 | )} | |
| 186 | {shortcutLabel && | |
| 187 | (presentation === "menu" ? ( | |
| 188 | <MenuShortcut className="ms-0 mr-7">{shortcutLabel}</MenuShortcut> | |
| 189 | ) : ( | |
| 190 | // The shortcut yields its slot to the edit button on hover. | |
| 191 | <span className="transition-opacity group-hover:opacity-0 group-focus-visible:opacity-0"> | |
| 192 | <MenuShortcut className="ms-0">{shortcutLabel}</MenuShortcut> | |
| 193 | </span> | |
| 194 | ))} | |
| 200 | 195 | <Button |
| 201 | 196 | type="button" |
| 202 | 197 | variant="ghost" |
| ... | ... | @@ -220,11 +215,7 @@ export default function ProjectScriptsControl({ |
| 220 | 215 | ); |
| 221 | 216 | })} |
| 222 | 217 | {importMenuItems} |
| 223 | <MenuItem | |
| 224 | density={presentation === "menu" ? "touch" : "default"} | |
| 225 | className={dropdownItemClassName} | |
| 226 | onClick={openAddDialog} | |
| 227 | > | |
| 218 | <MenuItem density={presentation === "menu" ? "touch" : "default"} onClick={openAddDialog}> | |
| 228 | 219 | <PlusIcon className="size-4" /> |
| 229 | 220 | <MenuItemLabel>Add action</MenuItemLabel> |
| 230 | 221 | </MenuItem> |
| ... | ... | @@ -296,7 +287,6 @@ export default function ProjectScriptsControl({ |
| 296 | 287 | </Tooltip> |
| 297 | 288 | <GroupSeparator className="hidden @3xl/header-actions:block" /> |
| 298 | 289 | <Menu |
| 299 | highlightItemOnHover={false} | |
| 300 | 290 | open={actionsMenuOpen.scripts} |
| 301 | 291 | onOpenChange={(open) => |
| 302 | 292 | setActionsMenuOpen({ presentation, scripts: open, imports: false }) |
| ... | ... | @@ -312,7 +302,6 @@ export default function ProjectScriptsControl({ |
| 312 | 302 | </Group> |
| 313 | 303 | ) : importableScripts.length > 0 ? ( |
| 314 | 304 | <Menu |
| 315 | highlightItemOnHover={false} | |
| 316 | 305 | open={actionsMenuOpen.imports} |
| 317 | 306 | onOpenChange={(open) => |
| 318 | 307 | setActionsMenuOpen({ presentation, scripts: false, imports: open }) |
| ... | ... | @@ -327,7 +316,7 @@ export default function ProjectScriptsControl({ |
| 327 | 316 | </MenuTrigger> |
| 328 | 317 | <MenuPopup align="end"> |
| 329 | 318 | {importMenuItems} |
| 330 | <MenuItem className={dropdownItemClassName} onClick={openAddDialog}> | |
| 319 | <MenuItem onClick={openAddDialog}> | |
| 331 | 320 | <PlusIcon className="size-4" /> |
| 332 | 321 | Add action |
| 333 | 322 | </MenuItem> |
apps/web/src/components/chat/DraftHeroHeadline.tsx+23-22| ... | ... | @@ -29,6 +29,7 @@ import { |
| 29 | 29 | MenuTrigger, |
| 30 | 30 | } from "../ui/menu"; |
| 31 | 31 | import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; |
| 32 | import { InlineButton } from "../ui/button"; | |
| 32 | 33 | import { resolveProjectSettings } from "@t3tools/shared/projectSettings"; |
| 33 | 34 | |
| 34 | 35 | interface DraftHeroHeadlineProps { |
| ... | ... | @@ -140,10 +141,13 @@ export function DraftHeroHeadline({ |
| 140 | 141 | // project title) so the hero sentence reads naturally: an |
| 141 | 142 | // aria-label here would replace the title with an action phrase |
| 142 | 143 | // mid-sentence and baffle screen-reader users. |
| 143 | <MenuTrigger className="pointer-events-auto inline-block max-w-64 truncate border-foreground/60 border-b border-dotted align-baseline text-foreground transition-colors hover:border-foreground/80 focus-visible:rounded-sm focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring" /> | |
| 144 | <MenuTrigger | |
| 145 | render={<InlineButton tone="picker" />} | |
| 146 | className="pointer-events-auto max-w-64 align-baseline" | |
| 147 | /> | |
| 144 | 148 | } |
| 145 | 149 | > |
| 146 | {activeProjectDisplayName ?? "Choose a project"} | |
| 150 | <span className="min-w-0 truncate">{activeProjectDisplayName ?? "Choose a project"}</span> | |
| 147 | 151 | </TooltipTrigger> |
| 148 | 152 | {activeProjectDisplayName ? ( |
| 149 | 153 | <TooltipPopup side="top">{activeProjectDisplayName}</TooltipPopup> |
| ... | ... | @@ -189,26 +193,23 @@ export function DraftHeroHeadline({ |
| 189 | 193 | > |
| 190 | 194 | {projectPickerEntries.map(({ group }) => { |
| 191 | 195 | return ( |
| 192 | <MenuRadioItem | |
| 193 | key={group.projectKey} | |
| 194 | value={group.projectKey} | |
| 195 | closeOnClick | |
| 196 | className="[&>span:last-child]:flex [&>span:last-child]:min-w-0 [&>span:last-child]:items-center [&>span:last-child]:gap-2" | |
| 197 | > | |
| 198 | <ProjectFavicon project={group} className="size-4 shrink-0" /> | |
| 199 | <Tooltip> | |
| 200 | <TooltipTrigger render={<span className="block min-w-0 truncate" />}> | |
| 201 | {group.displayName} | |
| 202 | </TooltipTrigger> | |
| 203 | <TooltipPopup side="top">{group.displayName}</TooltipPopup> | |
| 204 | </Tooltip> | |
| 205 | {showProjectEnvironments ? ( | |
| 206 | <ProjectEnvironmentBadge | |
| 207 | group={group} | |
| 208 | primaryEnvironmentId={primaryEnvironmentId} | |
| 209 | machineByEnvironmentId={environmentMachineById} | |
| 210 | /> | |
| 211 | ) : null} | |
| 196 | <MenuRadioItem key={group.projectKey} value={group.projectKey} closeOnClick> | |
| 197 | <span className="flex min-w-0 items-center gap-2"> | |
| 198 | <ProjectFavicon project={group} className="size-4 shrink-0" /> | |
| 199 | <Tooltip> | |
| 200 | <TooltipTrigger render={<span className="block min-w-0 truncate" />}> | |
| 201 | {group.displayName} | |
| 202 | </TooltipTrigger> | |
| 203 | <TooltipPopup side="top">{group.displayName}</TooltipPopup> | |
| 204 | </Tooltip> | |
| 205 | {showProjectEnvironments ? ( | |
| 206 | <ProjectEnvironmentBadge | |
| 207 | group={group} | |
| 208 | primaryEnvironmentId={primaryEnvironmentId} | |
| 209 | machineByEnvironmentId={environmentMachineById} | |
| 210 | /> | |
| 211 | ) : null} | |
| 212 | </span> | |
| 212 | 213 | </MenuRadioItem> |
| 213 | 214 | ); |
| 214 | 215 | })} |
apps/web/src/components/files/FileBreadcrumbs.tsx+10-3| ... | ... | @@ -155,7 +155,16 @@ function BreadcrumbMenuContent(props: { |
| 155 | 155 | <> |
| 156 | 156 | <PierreEntryIcon pathValue={entry.path} kind={entry.kind} theme={resolvedTheme} /> |
| 157 | 157 | <Tooltip> |
| 158 | <TooltipTrigger render={<span className="min-w-0 flex-1 truncate" />}> | |
| 158 | <TooltipTrigger | |
| 159 | render={ | |
| 160 | <span | |
| 161 | className={cn( | |
| 162 | "min-w-0 flex-1 truncate", | |
| 163 | entry.ignored && "text-muted-foreground", | |
| 164 | )} | |
| 165 | /> | |
| 166 | } | |
| 167 | > | |
| 159 | 168 | {entry.label} |
| 160 | 169 | </TooltipTrigger> |
| 161 | 170 | <TooltipPopup side="right">{entry.path}</TooltipPopup> |
| ... | ... | @@ -166,7 +175,6 @@ function BreadcrumbMenuContent(props: { |
| 166 | 175 | <MenuItem |
| 167 | 176 | key={entry.path} |
| 168 | 177 | closeOnClick={false} |
| 169 | className={entry.ignored ? "text-muted-foreground" : undefined} | |
| 170 | 178 | onClick={() => props.onDirectoryChange(entry.path)} |
| 171 | 179 | > |
| 172 | 180 | {row} |
| ... | ... | @@ -178,7 +186,6 @@ function BreadcrumbMenuContent(props: { |
| 178 | 186 | value={entry.path} |
| 179 | 187 | closeOnClick |
| 180 | 188 | aria-current={isCurrentFile ? "page" : undefined} |
| 181 | className={entry.ignored ? "text-muted-foreground" : undefined} | |
| 182 | 189 | > |
| 183 | 190 | <span className="flex min-w-0 items-center gap-2">{row}</span> |
| 184 | 191 | </MenuRadioItem> |
apps/web/src/components/preview/PreviewMoreMenu.tsx+5-4| ... | ... | @@ -206,10 +206,11 @@ export function PreviewMoreMenu({ |
| 206 | 206 | each one drove the popup far past its width. |
| 207 | 207 | */} |
| 208 | 208 | {profileName ? ( |
| 209 | // Truncation sits on the label itself: it renders a block box, so | |
| 210 | // `text-overflow` on an inline child inside it never applies and a | |
| 211 | // long name would push the popup past its width instead. | |
| 212 | <MenuGroupLabel className="max-w-64 truncate">Profile: {profileName}</MenuGroupLabel> | |
| 209 | // Truncation needs a block box: `text-overflow` on an inline child | |
| 210 | // never applies and a long name would push the popup past its width. | |
| 211 | <MenuGroupLabel className="max-w-64"> | |
| 212 | <span className="block truncate">Profile: {profileName}</span> | |
| 213 | </MenuGroupLabel> | |
| 213 | 214 | ) : null} |
| 214 | 215 | <MenuItem |
| 215 | 216 | onClick={() => |
apps/web/src/components/pullRequest/PullRequestStackHeader.tsx+12-10| ... | ... | @@ -11,16 +11,18 @@ export function PullRequestStackHeader({ |
| 11 | 11 | stale?: boolean; |
| 12 | 12 | }) { |
| 13 | 13 | return ( |
| 14 | <MenuGroupLabel className="flex items-center justify-between gap-2"> | |
| 15 | <span>Stack #{number}</span> | |
| 16 | {notice ? ( | |
| 17 | <Tooltip> | |
| 18 | <TooltipTrigger render={<span role="status" className="text-xs font-normal" />}> | |
| 19 | {stale ? "May be stale" : "Refreshing…"} | |
| 20 | </TooltipTrigger> | |
| 21 | <TooltipPopup>{notice}</TooltipPopup> | |
| 22 | </Tooltip> | |
| 23 | ) : null} | |
| 14 | <MenuGroupLabel> | |
| 15 | <div className="flex items-center justify-between gap-2"> | |
| 16 | <span>Stack #{number}</span> | |
| 17 | {notice ? ( | |
| 18 | <Tooltip> | |
| 19 | <TooltipTrigger render={<span role="status" className="text-xs font-normal" />}> | |
| 20 | {stale ? "May be stale" : "Refreshing…"} | |
| 21 | </TooltipTrigger> | |
| 22 | <TooltipPopup>{notice}</TooltipPopup> | |
| 23 | </Tooltip> | |
| 24 | ) : null} | |
| 25 | </div> | |
| 24 | 26 | </MenuGroupLabel> |
| 25 | 27 | ); |
| 26 | 28 | } |
apps/web/src/components/settings/EnvironmentIconPicker.tsx+1-1| ... | ... | @@ -102,7 +102,7 @@ export function EnvironmentIconMenu({ |
| 102 | 102 | <MenuSubPopup> |
| 103 | 103 | {lock !== null ? ( |
| 104 | 104 | <> |
| 105 | <MenuItem disabled className="whitespace-normal text-xs"> | |
| 105 | <MenuItem disabled className="whitespace-normal"> | |
| 106 | 106 | {lock} |
| 107 | 107 | </MenuItem> |
| 108 | 108 | <MenuSeparator /> |
apps/web/src/components/settings/SnapShotSettings.tsx+3-16| ... | ... | @@ -54,10 +54,7 @@ import { |
| 54 | 54 | type CaptureSetupStep, |
| 55 | 55 | } from "./SnapShotSetupDialog.logic"; |
| 56 | 56 | |
| 57 | const soundOptionRowClassName = | |
| 58 | "grid grid-cols-[1fr_auto] rounded-sm has-data-checked:bg-foreground/[0.08]"; | |
| 59 | const soundOptionItemClassName = "data-checked:bg-transparent"; | |
| 60 | const soundPreviewClassName = "min-h-7 w-7 justify-center px-0"; | |
| 57 | const soundOptionRowClassName = "grid grid-cols-[1fr_auto]"; | |
| 61 | 58 | |
| 62 | 59 | function captureSettingsError(title: string, error: unknown) { |
| 63 | 60 | return { title, message: error instanceof Error ? error.message : "Try again." }; |
| ... | ... | @@ -520,16 +517,11 @@ export function SnapShotSettings() { |
| 520 | 517 | Off |
| 521 | 518 | </MenuRadioItem> |
| 522 | 519 | <div className={soundOptionRowClassName}> |
| 523 | <MenuRadioItem | |
| 524 | className={soundOptionItemClassName} | |
| 525 | closeOnClick | |
| 526 | value="soft-pop" | |
| 527 | > | |
| 520 | <MenuRadioItem closeOnClick value="soft-pop"> | |
| 528 | 521 | Whoosh <span className="text-muted-foreground">(Default)</span> |
| 529 | 522 | </MenuRadioItem> |
| 530 | 523 | <MenuItem |
| 531 | 524 | aria-label="Play Whoosh" |
| 532 | className={soundPreviewClassName} | |
| 533 | 525 | closeOnClick={false} |
| 534 | 526 | onClick={() => playSnapShotSound("soft-pop")} |
| 535 | 527 | > |
| ... | ... | @@ -537,16 +529,11 @@ export function SnapShotSettings() { |
| 537 | 529 | </MenuItem> |
| 538 | 530 | </div> |
| 539 | 531 | <div className={soundOptionRowClassName}> |
| 540 | <MenuRadioItem | |
| 541 | className={soundOptionItemClassName} | |
| 542 | closeOnClick | |
| 543 | value="camera-shutter" | |
| 544 | > | |
| 532 | <MenuRadioItem closeOnClick value="camera-shutter"> | |
| 545 | 533 | Click |
| 546 | 534 | </MenuRadioItem> |
| 547 | 535 | <MenuItem |
| 548 | 536 | aria-label="Play Click" |
| 549 | className={soundPreviewClassName} | |
| 550 | 537 | closeOnClick={false} |
| 551 | 538 | onClick={() => playSnapShotSound("camera-shutter")} |
| 552 | 539 | > |
apps/web/src/components/usage/UsagePage.tsx+1-1| ... | ... | @@ -738,7 +738,7 @@ function UsageEnvironmentFilter({ |
| 738 | 738 | <Menu> |
| 739 | 739 | <MenuTrigger |
| 740 | 740 | render={<InlineButton />} |
| 741 | className="group/usage-environment min-w-0 max-w-full gap-1" | |
| 741 | className="group/usage-environment min-w-0 max-w-full" | |
| 742 | 742 | > |
| 743 | 743 | <span className="min-w-0 truncate">{label}</span> |
| 744 | 744 | <span className="flex size-3.5 shrink-0 items-center justify-center text-muted-foreground"> |
apps/web/src/routes/_chat.pull-requests.tsx+6-9| ... | ... | @@ -2234,16 +2234,13 @@ function CompactFilterMenu<Value extends string>({ |
| 2234 | 2234 | aria-label={triggerLabel || iconOnly ? `${label}: ${current.label}` : label} |
| 2235 | 2235 | title={iconOnly ? `${label}: ${current.label}` : undefined} |
| 2236 | 2236 | render={ |
| 2237 | outlined ? <Button variant="outline" size={iconOnly ? "icon" : "default"} /> : undefined | |
| 2238 | } | |
| 2239 | className={ | |
| 2240 | outlined | |
| 2241 | ? className | |
| 2242 | : cn( | |
| 2243 | "inline-flex h-7 min-w-0 items-center gap-1 rounded-md px-1.5 text-sm font-medium text-muted-foreground hover:bg-accent hover:text-foreground", | |
| 2244 | className, | |
| 2245 | ) | |
| 2237 | outlined ? ( | |
| 2238 | <Button variant="outline" size={iconOnly ? "icon" : "default"} /> | |
| 2239 | ) : ( | |
| 2240 | <Button variant="ghost-muted" size="sm" /> | |
| 2241 | ) | |
| 2246 | 2242 | } |
| 2243 | className={cn("min-w-0", className)} | |
| 2247 | 2244 | > |
| 2248 | 2245 | {iconOnly ? ( |
| 2249 | 2246 | <current.Icon aria-hidden className="size-4" /> |
scripts/lint-restyle-ceiling.ts+1-1| ... | ... | @@ -8,7 +8,7 @@ import * as NodeURL from "node:url"; |
| 8 | 8 | // components/ui exports are migrated to variants (see vite.config.ts). This gate keeps the |
| 9 | 9 | // count from growing: CI fails when findings exceed the ceiling. Lower the ceiling when you |
| 10 | 10 | // migrate a file, and delete this script when the rule becomes an error. |
| 11 | export const RESTYLE_CEILING = 582; | |
| 11 | export const RESTYLE_CEILING = 499; | |
| 12 | 12 | |
| 13 | 13 | const RULE = "shadcn(no-restyle)"; |
| 14 | 14 |