Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
49 changes: 29 additions & 20 deletions apps/web/src/browser/BrowserDeviceToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { useState } from "react";

import { Button } from "~/components/ui/button";
import { Input } from "~/components/ui/input";
import { Tooltip, TooltipPopup, TooltipTrigger } from "~/components/ui/tooltip";
import {
Select,
SelectGroup,
Expand Down Expand Up @@ -296,26 +297,34 @@ export function BrowserDeviceToolbar({
/>
</form>

<Button
variant="ghost"
size="icon-xs"
type="button"
aria-label={
aspectRatio === null ? "Lock viewport aspect ratio" : "Unlock viewport aspect ratio"
}
aria-pressed={aspectRatio !== null}
title={aspectRatio === null ? "Lock aspect ratio" : "Unlock aspect ratio"}
className={cn(aspectRatio !== null && "bg-accent text-foreground")}
disabled={pending || !customValid}
onPointerDown={(event) => event.preventDefault()}
onClick={toggleAspectRatio}
>
{aspectRatio === null ? (
<Unlink2 className={cn(aspectRatio !== null && "text-foreground")} />
) : (
<Link2 className={cn(aspectRatio !== null && "text-foreground")} />
)}
</Button>
<Tooltip>
<TooltipTrigger
render={
<Button
variant="ghost"
size="icon-xs"
type="button"
aria-label={
aspectRatio === null ? "Lock viewport aspect ratio" : "Unlock viewport aspect ratio"
}
aria-pressed={aspectRatio !== null}
className={cn(aspectRatio !== null && "bg-accent text-foreground")}
disabled={pending || !customValid}
onPointerDown={(event) => event.preventDefault()}
onClick={toggleAspectRatio}
/>
}
>
{aspectRatio === null ? (
<Unlink2 className={cn(aspectRatio !== null && "text-foreground")} />
) : (
<Link2 className={cn(aspectRatio !== null && "text-foreground")} />
)}
</TooltipTrigger>
<TooltipPopup side="top">
{aspectRatio === null ? "Lock aspect ratio" : "Unlock aspect ratio"}
</TooltipPopup>
</Tooltip>
<Button
variant="ghost"
size="icon-xs"
Expand Down
1 change: 0 additions & 1 deletion apps/web/src/components/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6348,7 +6348,6 @@ function ChatViewContent(props: ChatViewProps) {
>
<Button
aria-label="Scroll to end"
title="Scroll to end"
onClick={() => scrollToEnd(true)}
className="pointer-events-auto gap-1.5 rounded-full px-3 text-muted-foreground hover:text-foreground"
size="xs"
Expand Down
1 change: 0 additions & 1 deletion apps/web/src/components/ConnectionStatusDot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ export function ConnectionStatusDot({
const dot = (
<button
type="button"
title={tooltipText}
aria-label={tooltipText}
className="relative flex size-3 shrink-0 cursor-help items-center justify-center rounded-full outline-hidden"
>
Expand Down
228 changes: 121 additions & 107 deletions apps/web/src/components/DiffPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -581,117 +581,131 @@ export default function DiffPanel({
</DropdownMenuContent>
</DropdownMenu>
{selectedTurnId === null && selectedGitScope === "branch" && selectedGitSource?.baseRef && (
<div
className="flex min-w-0 max-w-full items-center gap-2 overflow-hidden text-xs text-muted-foreground"
title={`${selectedGitSource.headRef ?? "HEAD"} → ${selectedGitSource.baseRef}`}
aria-label={`Comparing ${selectedGitSource.headRef ?? "HEAD"} against ${selectedGitSource.baseRef}`}
>
<span className="min-w-0 max-w-48 truncate">{selectedGitSource.headRef ?? "HEAD"}</span>
<ArrowRightIcon className="size-3.5 shrink-0 opacity-70" />
<Combobox
items={baseRefItems}
filteredItems={filteredBaseRefItems}
value={selectedBaseRef ?? AUTOMATIC_BASE_REF}
onOpenChange={(open) => {
if (!open) setBaseRefQuery("");
}}
onValueChange={(value) => {
if (!value) return;
selectBranchBaseRef(value === AUTOMATIC_BASE_REF ? null : value);
}}
<Tooltip>
<TooltipTrigger
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
Resolved
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
Resolved
render={
<div
className="flex min-w-0 max-w-full items-center gap-2 overflow-hidden text-xs text-muted-foreground"
aria-label={`Comparing ${selectedGitSource.headRef ?? "HEAD"} against ${selectedGitSource.baseRef}`}
/>
}
>
<ComboboxTrigger
className="inline-flex min-w-0 max-w-48 items-center gap-1 overflow-hidden rounded-md px-1.5 py-1 outline-none transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring"
aria-label={`Change comparison target. Currently ${selectedGitSource.baseRef}`}
>
<span className="min-w-0 truncate">{selectedGitSource.baseRef}</span>
<ChevronDownIcon className="size-3.5 shrink-0 opacity-70" />
</ComboboxTrigger>
<ComboboxPopup
align="start"
className="w-72 min-w-0 max-w-[calc(100vw-1rem)] overflow-hidden [&>[data-slot=combobox-popup]]:min-w-0 [&>[data-slot=combobox-popup]]:overflow-hidden"
<span className="min-w-0 max-w-48 truncate">
{selectedGitSource.headRef ?? "HEAD"}
</span>
<ArrowRightIcon className="size-3.5 shrink-0 opacity-70" />
<Combobox
items={baseRefItems}
filteredItems={filteredBaseRefItems}
value={selectedBaseRef ?? AUTOMATIC_BASE_REF}
onOpenChange={(open) => {
if (!open) setBaseRefQuery("");
}}
onValueChange={(value) => {
if (!value) return;
selectBranchBaseRef(value === AUTOMATIC_BASE_REF ? null : value);
}}
>
<div className="min-w-0 shrink-0 px-3 pt-2.5">
<div className="relative -translate-y-px border-b border-border/70 pb-1.5 transition-colors focus-within:border-ring">
<SearchIcon
aria-hidden="true"
className="pointer-events-none absolute top-1.5 left-0 size-4 shrink-0 text-muted-foreground/55"
/>
<ComboboxInput
className="[&_input]:h-6.5 [&_input]:ps-5 [&_input]:font-sans [&_input]:leading-6.5"
inputClassName="rounded-none bg-transparent text-sm"
placeholder="Search refs..."
showTrigger={false}
size="sm"
unstyled
value={baseRefQuery}
onChange={(event) => setBaseRefQuery(event.target.value)}
/>
<ComboboxTrigger
className="inline-flex min-w-0 max-w-48 items-center gap-1 overflow-hidden rounded-md px-1.5 py-1 outline-none transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring"
aria-label={`Change comparison target. Currently ${selectedGitSource.baseRef}`}
>
<span className="min-w-0 truncate">{selectedGitSource.baseRef}</span>
<ChevronDownIcon className="size-3.5 shrink-0 opacity-70" />
</ComboboxTrigger>
<ComboboxPopup
align="start"
className="w-72 min-w-0 max-w-[calc(100vw-1rem)] overflow-hidden [&>[data-slot=combobox-popup]]:min-w-0 [&>[data-slot=combobox-popup]]:overflow-hidden"
>
<div className="min-w-0 shrink-0 px-3 pt-2.5">
<div className="relative -translate-y-px border-b border-border/70 pb-1.5 transition-colors focus-within:border-ring">
<SearchIcon
aria-hidden="true"
className="pointer-events-none absolute top-1.5 left-0 size-4 shrink-0 text-muted-foreground/55"
/>
<ComboboxInput
className="[&_input]:h-6.5 [&_input]:ps-5 [&_input]:font-sans [&_input]:leading-6.5"
inputClassName="rounded-none bg-transparent text-sm"
placeholder="Search refs..."
showTrigger={false}
size="sm"
unstyled
value={baseRefQuery}
onChange={(event) => setBaseRefQuery(event.target.value)}
/>
</div>
</div>
</div>
<div className="grid shrink-0 grid-cols-[1rem_minmax(0,1fr)] items-center gap-2 border-b border-border/70 ps-3 pe-6.5 pt-2 pb-1.5 font-medium text-[10px] text-muted-foreground uppercase tracking-wide">
<span aria-hidden="true" />
<div className="grid min-w-0 grid-cols-[minmax(0,1fr)_2rem] items-center">
<span>Branch</span>
<span className="text-right">Remote</span>
<div className="grid shrink-0 grid-cols-[1rem_minmax(0,1fr)] items-center gap-2 border-b border-border/70 ps-3 pe-6.5 pt-2 pb-1.5 font-medium text-[10px] text-muted-foreground uppercase tracking-wide">
<span aria-hidden="true" />
<div className="grid min-w-0 grid-cols-[minmax(0,1fr)_2rem] items-center">
<span>Branch</span>
<span className="text-right">Remote</span>
</div>
</div>
</div>
<ComboboxEmpty>No matching refs.</ComboboxEmpty>
<ComboboxList className="max-h-64 min-w-0 overflow-x-hidden">
<ComboboxItem
className="h-8 w-full min-w-0 grid-cols-[1rem_minmax(0,1fr)] py-0"
contentClassName="w-full min-w-0 overflow-hidden"
value={AUTOMATIC_BASE_REF}
>
<span className="block min-w-0 truncate">Automatic</span>
</ComboboxItem>
{baseRefChoices.map((choice) => {
const item = valueForBaseRefChoice(choice);
const hasBoth = choice.local !== null && choice.remote !== null;
const useRemote = choice.remote?.name === item;
return (
<ComboboxItem
key={choice.id}
className="h-8 w-full min-w-0 grid-cols-[1rem_minmax(0,1fr)] py-0"
contentClassName="w-full min-w-0 overflow-hidden"
value={item}
>
<div className="grid w-full min-w-0 grid-cols-[minmax(0,1fr)_2rem] items-center overflow-hidden">
<span className="block min-w-0 truncate pe-2">{choice.label}</span>
{hasBoth ? (
<div
className="flex justify-end"
onClick={(event) => event.stopPropagation()}
onPointerDown={(event) => event.stopPropagation()}
>
<Switch
aria-label={`Use remote version of ${choice.label}`}
checked={useRemote}
className="[--thumb-size:--spacing(3)]"
onCheckedChange={(checked) => {
const nextRef = checked
? choice.remote?.name
: choice.local?.name;
if (nextRef) selectBranchBaseRef(nextRef);
}}
/>
</div>
) : choice.remote ? (
<span
className="flex justify-end text-muted-foreground"
title="Remote only"
>
<CheckIcon aria-hidden="true" className="size-3" />
</span>
) : null}
</div>
</ComboboxItem>
);
})}
</ComboboxList>
</ComboboxPopup>
</Combobox>
</div>
<ComboboxEmpty>No matching refs.</ComboboxEmpty>
<ComboboxList className="max-h-64 min-w-0 overflow-x-hidden">
<ComboboxItem
className="h-8 w-full min-w-0 grid-cols-[1rem_minmax(0,1fr)] py-0"
contentClassName="w-full min-w-0 overflow-hidden"
value={AUTOMATIC_BASE_REF}
>
<span className="block min-w-0 truncate">Automatic</span>
</ComboboxItem>
{baseRefChoices.map((choice) => {
const item = valueForBaseRefChoice(choice);
const hasBoth = choice.local !== null && choice.remote !== null;
const useRemote = choice.remote?.name === item;
return (
<ComboboxItem
key={choice.id}
className="h-8 w-full min-w-0 grid-cols-[1rem_minmax(0,1fr)] py-0"
contentClassName="w-full min-w-0 overflow-hidden"
value={item}
>
<div className="grid w-full min-w-0 grid-cols-[minmax(0,1fr)_2rem] items-center overflow-hidden">
<span className="block min-w-0 truncate pe-2">{choice.label}</span>
{hasBoth ? (
<div
className="flex justify-end"
onClick={(event) => event.stopPropagation()}
onPointerDown={(event) => event.stopPropagation()}
>
<Switch
aria-label={`Use remote version of ${choice.label}`}
checked={useRemote}
className="[--thumb-size:--spacing(3)]"
onCheckedChange={(checked) => {
const nextRef = checked
? choice.remote?.name
: choice.local?.name;
if (nextRef) selectBranchBaseRef(nextRef);
}}
/>
</div>
) : choice.remote ? (
<Tooltip>
<TooltipTrigger
render={
<span className="flex justify-end text-muted-foreground">
<CheckIcon aria-hidden="true" className="size-3" />
</span>
}
/>
<TooltipPopup side="top">Remote only</TooltipPopup>
</Tooltip>
) : null}
</div>
</ComboboxItem>
);
})}
</ComboboxList>
</ComboboxPopup>
</Combobox>
</TooltipTrigger>
<TooltipPopup side="top">
{`${selectedGitSource.headRef ?? "HEAD"} → ${selectedGitSource.baseRef}`}
</TooltipPopup>
</Tooltip>
)}
</div>
<div className="flex shrink-0 items-center gap-1 [-webkit-app-region:no-drag]">
Expand Down
10 changes: 7 additions & 3 deletions apps/web/src/components/ServerUpdateAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { useAtomCommand } from "~/state/use-atom-command";
import { manualServerUpdateCommand } from "~/versionSkew";
import { Button } from "./ui/button";
import { toastManager } from "./ui/toast";
import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip";

// The wire "installing" stage is a sub-second launcher handoff, so the UI
// folds it into the download phase; everything after the handoff is the
Expand Down Expand Up @@ -45,9 +46,12 @@ export function ServerUpdateProgress({
return (
<div className="mt-1 flex min-w-0 items-center gap-2 text-xs text-destructive" role="alert">
<span className="size-1.5 shrink-0 rounded-full bg-destructive" aria-hidden="true" />
<span className="min-w-0 truncate" title={state.message}>
{state.message}
</span>
<Tooltip>
<TooltipTrigger render={<span className="min-w-0 truncate">{state.message}</span>} />
<TooltipPopup side="top" className="max-w-80">
{state.message}
</TooltipPopup>
</Tooltip>
</div>
);
}
Expand Down
Loading
Loading