Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 5 additions & 1 deletion apps/web/src/components/BranchToolbarBranchSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -779,7 +779,11 @@ export function BranchToolbarBranchSelector({
)}
/>
<ComposerContextLabel displayMode={displayMode}>
<MiddleTruncate value={triggerLabel} className="w-full" />
<MiddleTruncate
value={triggerLabel}
variant={displayMode === "panel" ? "cap-aligned" : "default"}
className="w-full"
/>
</ComposerContextLabel>
{displayMode !== "panel" ? (
<ChevronDownIcon className="size-3 shrink-0 opacity-50" />
Expand Down
4 changes: 3 additions & 1 deletion apps/web/src/components/ComposerContextLabel.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ReactNode } from "react";

import { cn } from "../lib/utils";
import { THREAD_DETAILS_PANEL_LABEL_CLASS } from "./chat/threadDetailsPanelStyles";

/** Keeps text measurable while the composer's outer label box collapses. */
export function ComposerContextLabel({
Expand All @@ -16,14 +17,15 @@ export function ComposerContextLabel({
className={cn(
"min-w-0",
displayMode === "panel"
? "flex-1 truncate text-left"
? "flex-1 overflow-x-clip overflow-y-visible text-left"
: "max-w-[240px] group-data-[compact]/composer-context:max-w-0",
)}
>
<span
data-composer-label-motion
className={cn(
"block w-full min-w-0 truncate",
displayMode === "panel" && THREAD_DETAILS_PANEL_LABEL_CLASS,
displayMode === "toolbar" &&
"max-w-[240px] transition-opacity duration-180 ease-drawer group-data-[compact]/composer-context:opacity-0 motion-reduce:transition-none",
)}
Expand Down
41 changes: 23 additions & 18 deletions apps/web/src/components/GitActionsControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ import { type DraftId, useComposerDraftStore } from "~/composerDraftStore";
import {
THREAD_DETAILS_PANEL_CHEVRON_CLASS,
THREAD_DETAILS_PANEL_ICON_CLASS,
THREAD_DETAILS_PANEL_LABEL_CLASS,
THREAD_DETAILS_PANEL_SPLIT_GROUP_CLASS,
THREAD_DETAILS_PANEL_SPLIT_SEPARATOR_CLASS,
} from "./chat/threadDetailsPanelStyles";
Expand Down Expand Up @@ -432,23 +433,25 @@ function GitActionProgressButtonContent({
aria-live="polite"
className={cn(
"grid min-w-0 flex-1 items-center",
// Pin the title row to the button's minimum content height (min-height
// minus vertical padding and border) so revealing the output row
// extends the button downward without re-centering — the title must
// not shift. No row gap: the collapsed output row must contribute zero
// height so the single-line running button matches the static button
// exactly. The panel column gap matches the static row's icon-to-label
// distance (gap-2.5 plus the label's ml-0.5). In the panel the elapsed
// counter renders outside the button (in the menu-chevron slot), so
// there is no trailing column.
// Pin the title row to the button's minimum content height so output
// expands below it. Panel controls leave 24px after padding and border.
// No row gap: collapsed output must not add height. The panel's elapsed
// counter uses the menu-chevron slot, so it has no trailing column.
isPanel
? "grid-cols-[auto_minmax(0,1fr)] grid-rows-[1.75rem] gap-x-3"
? "grid-cols-[auto_minmax(0,1fr)] grid-rows-[1.5rem] gap-x-3"
: "grid-cols-[auto_minmax(0,1fr)_auto] grid-rows-[1.25rem] gap-x-2.5 sm:grid-rows-[1rem]",
)}
role="status"
>
<Spinner aria-hidden="true" className="row-start-1 -mx-0.5 shrink-0" />
<p className="row-start-1 min-w-0 truncate text-left">{progress.status}</p>
<p
className={cn(
"row-start-1 min-w-0 truncate text-left",
isPanel && THREAD_DETAILS_PANEL_LABEL_CLASS,
)}
>
{progress.status}
</p>
{!isPanel ? (
<GitActionElapsedTime
startedAtMs={progress.startedAtMs}
Expand Down Expand Up @@ -487,11 +490,13 @@ function GitActionSuccessButtonContent({ success }: { success: InlineGitActionSu
return (
<div
aria-live="polite"
className="grid min-w-0 flex-1 grid-cols-[auto_minmax(0,1fr)] grid-rows-[1.75rem] items-center gap-x-3"
className="grid min-w-0 flex-1 grid-cols-[auto_minmax(0,1fr)] grid-rows-[1.5rem] items-center gap-x-3"
role="status"
>
<CheckIcon aria-hidden="true" className="size-3.5 shrink-0 text-success" />
<p className="min-w-0 truncate text-left">{success.title}</p>
<p className={cn("min-w-0 truncate text-left", THREAD_DETAILS_PANEL_LABEL_CLASS)}>
{success.title}
</p>
<div
className={cn(
"col-start-2 grid min-w-0 transition-[grid-template-rows,opacity] duration-200 ease-out motion-reduce:transition-none",
Expand Down Expand Up @@ -1694,7 +1699,7 @@ export default function GitActionsControl({
<PopoverTrigger
openOnHover
nativeButton={false}
render={<span className="block w-max cursor-not-allowed" />}
render={<span className="block w-full cursor-not-allowed" />}
>
<MenuItem
density={presentation === "menu" ? "touch" : "default"}
Expand Down Expand Up @@ -1814,7 +1819,7 @@ export default function GitActionsControl({
onClick={initializeGit}
>
<GitBranchPlusIcon className="size-3.5" aria-hidden />
<span className="ml-0.5">
<span className={cn("ml-0.5", isPanel && THREAD_DETAILS_PANEL_LABEL_CLASS)}>
{initAction.isPending ? "Initializing..." : "Initialize Git"}
</span>
</ThreadDetailsControl>
Expand Down Expand Up @@ -1872,7 +1877,7 @@ export default function GitActionsControl({
<span
className={cn(
"sr-only @3xl/header-actions:not-sr-only @3xl/header-actions:ml-0.5",
isPanel && "not-sr-only ml-0 truncate",
isPanel && cn("not-sr-only ml-0 truncate", THREAD_DETAILS_PANEL_LABEL_CLASS),
)}
>
{quickAction.label}
Expand All @@ -1899,7 +1904,7 @@ export default function GitActionsControl({
<span
className={cn(
"sr-only @3xl/header-actions:not-sr-only @3xl/header-actions:ml-0.5",
isPanel && "not-sr-only ml-0 truncate",
isPanel && cn("not-sr-only ml-0 truncate", THREAD_DETAILS_PANEL_LABEL_CLASS),
)}
>
{quickAction.label}
Expand Down Expand Up @@ -1969,7 +1974,7 @@ export default function GitActionsControl({
onClick={onOpenChanges}
>
<FileDiffIcon className={THREAD_DETAILS_PANEL_ICON_CLASS} aria-hidden />
<span className="flex-1 text-left">Changes</span>
<span className={cn("flex-1 text-left", THREAD_DETAILS_PANEL_LABEL_CLASS)}>Changes</span>
<span className="flex items-center gap-1 font-mono text-2xs tabular-nums">
<span className="text-success">+{changesTotals?.insertions ?? 0}</span>
<span className="text-destructive">-{changesTotals?.deletions ?? 0}</span>
Expand Down
9 changes: 6 additions & 3 deletions apps/web/src/components/ProjectScriptsControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import { cn } from "~/lib/utils";
import {
THREAD_DETAILS_PANEL_CHEVRON_CLASS,
THREAD_DETAILS_PANEL_ICON_CLASS,
THREAD_DETAILS_PANEL_LABEL_CLASS,
THREAD_DETAILS_PANEL_SPLIT_GROUP_CLASS,
THREAD_DETAILS_PANEL_SPLIT_SEPARATOR_CLASS,
} from "./chat/threadDetailsPanelStyles";
Expand Down Expand Up @@ -322,7 +323,7 @@ export default function ProjectScriptsControl({
<span
className={cn(
"sr-only @3xl/header-actions:not-sr-only @3xl/header-actions:ml-0.5",
isPanel && "not-sr-only ml-0 truncate",
isPanel && cn("not-sr-only ml-0 truncate", THREAD_DETAILS_PANEL_LABEL_CLASS),
)}
>
{primaryScript.name}
Expand Down Expand Up @@ -387,7 +388,9 @@ export default function ProjectScriptsControl({
onClick={() => setActionsMenuOpen({ presentation, scripts: false, imports: true })}
>
<WrenchIcon className={THREAD_DETAILS_PANEL_ICON_CLASS} />
<span className="min-w-0 truncate">Actions</span>
<span className={cn("min-w-0 truncate", THREAD_DETAILS_PANEL_LABEL_CLASS)}>
Actions
</span>
</ThreadDetailsControl>
<span aria-hidden="true" className={THREAD_DETAILS_PANEL_SPLIT_SEPARATOR_CLASS} />
<Menu
Expand Down Expand Up @@ -456,7 +459,7 @@ export default function ProjectScriptsControl({
<span
className={cn(
"sr-only @3xl/header-actions:not-sr-only @3xl/header-actions:ml-0.5",
isPanel && "not-sr-only ml-0.5",
isPanel && cn("not-sr-only ml-0.5", THREAD_DETAILS_PANEL_LABEL_CLASS),
)}
>
{isPanel ? "Add project script" : "Add action"}
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/chat/ComposerPrimaryActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ export const ComposerPrimaryActions = memo(function ComposerPrimaryActions({
<button
type="button"
className={cn(
"flex h-7 shrink-0 cursor-pointer items-center gap-1.5 rounded-full border px-2.5 text-xs tabular-nums outline-none transition-colors focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-64 [&_svg]:pointer-events-none [&_svg]:size-3.5",
"flex h-9 shrink-0 cursor-pointer items-center gap-1.5 rounded-full border px-2.5 text-xs tabular-nums outline-none transition-colors focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-64 sm:h-8 [&_svg]:pointer-events-none [&_svg]:size-3.5",
keepFullHistory
? "border-border text-muted-foreground hover:text-foreground"
: "border-warning/40 text-warning hover:bg-warning/8",
Expand Down
3 changes: 2 additions & 1 deletion apps/web/src/components/chat/OpenInPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import { useAtomCommand } from "~/state/use-atom-command";
import {
THREAD_DETAILS_PANEL_CHEVRON_CLASS,
THREAD_DETAILS_PANEL_ICON_CLASS,
THREAD_DETAILS_PANEL_LABEL_CLASS,
THREAD_DETAILS_PANEL_SPLIT_GROUP_CLASS,
THREAD_DETAILS_PANEL_SPLIT_SEPARATOR_CLASS,
} from "./threadDetailsPanelStyles";
Expand Down Expand Up @@ -421,7 +422,7 @@ export const OpenInPicker = memo(function OpenInPicker({
compact
? "sr-only"
: "sr-only @3xl/header-actions:not-sr-only @3xl/header-actions:ml-0.5",
isPanel && "not-sr-only ml-0 min-w-0 truncate",
isPanel && cn("not-sr-only ml-0 min-w-0 truncate", THREAD_DETAILS_PANEL_LABEL_CLASS),
)}
>
{primaryLabel}
Expand Down
5 changes: 4 additions & 1 deletion apps/web/src/components/chat/ThreadDetailsPrRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ import { Button } from "../ui/button";
import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";
import {
THREAD_DETAILS_PANEL_ICON_CLASS,
THREAD_DETAILS_PANEL_LABEL_CLASS,
THREAD_DETAILS_PANEL_SPLIT_GROUP_CLASS,
THREAD_DETAILS_PANEL_SPLIT_SEPARATOR_CLASS,
} from "./threadDetailsPanelStyles";
Expand Down Expand Up @@ -398,7 +399,9 @@ export function ThreadDetailsPrRow({
}
>
{icon}
<span className="min-w-0 flex-1 truncate text-left">{text}</span>
<span className={cn("min-w-0 flex-1 truncate text-left", THREAD_DETAILS_PANEL_LABEL_CLASS)}>
{text}
</span>
</TooltipTrigger>
{rowTooltip}
</Tooltip>
Expand Down
5 changes: 5 additions & 0 deletions apps/web/src/components/chat/threadDetailsPanelStyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ const THREAD_DETAILS_PANEL_SPLIT_GROUP_SURFACE_CLASS = `${THREAD_DETAILS_PANEL_H

export const THREAD_DETAILS_PANEL_ROW_CONTENT_CLASS = "gap-2.5 px-2.5 text-left";

// Center capitals with icons. Only clip horizontally so truncated labels retain
// accents and descenders outside the trimmed text box.
export const THREAD_DETAILS_PANEL_LABEL_CLASS =
"[text-box:trim-both_cap_alphabetic] overflow-x-clip overflow-y-visible";

const THREAD_DETAILS_PANEL_CONTROL_CLASS = `h-8 min-w-0 rounded-lg border-transparent ${THREAD_DETAILS_PANEL_ROW_CONTENT_CLASS} text-[13px] font-medium text-foreground/80`;

export const THREAD_DETAILS_PANEL_ROW_CLASS = `${THREAD_DETAILS_PANEL_CONTROL_CLASS} w-full justify-start ${THREAD_DETAILS_PANEL_ROW_SURFACE_CLASS}`;
Expand Down
10 changes: 9 additions & 1 deletion apps/web/src/components/ui/middle-truncate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export function MiddleTruncate({
value,
tail,
showTitle = true,
variant = "default",
className,
...props
}: Omit<ComponentProps<"span">, "children"> & {
Expand All @@ -24,12 +25,19 @@ export function MiddleTruncate({
tail?: number;
/** The full value on hover. Off where a tooltip already carries it. */
showTitle?: boolean;
/** Align capital letters with adjacent icons while preserving accents and descenders. */
variant?: "default" | "cap-aligned";
}) {
const split = splitForMiddleTruncate(value, tail);
return (
<span
{...(showTitle ? { title: value } : {})}
className={cn("inline-flex min-w-0 max-w-full overflow-hidden whitespace-nowrap", className)}
className={cn(
"inline-flex min-w-0 max-w-full overflow-hidden whitespace-nowrap",
variant === "cap-aligned" &&
"overflow-x-clip overflow-y-visible [&>span]:[text-box:trim-both_cap_alphabetic] [&>span]:overflow-x-clip [&>span]:overflow-y-visible",
className,
)}
{...props}
>
{split ? (
Expand Down
Loading