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
2 changes: 1 addition & 1 deletion apps/web/src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1374,7 +1374,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
if (!showSnoozeButton) setSnoozeMenuOpen(false);
}, [showSnoozeButton]);
const handlePrClick = useCallback(
(event: ReactMouseEvent<HTMLAnchorElement>) => {
(event: ReactMouseEvent<HTMLElement>) => {
const url = pr?.url ?? currentLinkedPr?.url;
if (!url) return;
const openedInRightPanel = openPrLink(
Expand Down
52 changes: 27 additions & 25 deletions apps/web/src/components/ThreadStatusIndicators.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
} from "@t3tools/shared/threadPullRequests";
import { FolderGit2Icon, TerminalIcon } from "lucide-react";
import { useMemo, type MouseEvent } from "react";
import { buttonVariants, InlineButton } from "./ui/button";
import { Button, InlineButton } from "./ui/button";
import { cn } from "../lib/utils";
import { useEnvironment, usePrimaryEnvironmentId } from "../state/environments";
import { EnvironmentMachineIcon } from "./EnvironmentMachineIcon";
Expand Down Expand Up @@ -207,50 +207,52 @@ export function ThreadPullRequestBadgeControl({
url?: string | undefined;
status: PrStatusIndicator | null;
onOpenStack: () => void;
onOpenPullRequest: (event: MouseEvent<HTMLAnchorElement>) => void;
onOpenPullRequest: (event: MouseEvent<HTMLElement>) => void;
}) {
const presentation = resolveThreadPullRequestBadgePresentation({ badge, number, url, status });
if (presentation === null) return null;
const isStack = badge?.kind === "stack";
const className = cn(
variant === "ghost"
? buttonVariants({ variant: "ghost", size: "xs" })
: "inline-flex shrink-0 cursor-pointer items-center gap-0.5 whitespace-nowrap border-b border-transparent hover:border-current focus-visible:outline-2 focus-visible:outline-ring",
"text-xs tabular-nums",
variant === "ghost" &&
"font-normal text-xs! active:scale-100 [--control-icon-color:currentColor]",
presentation.toneClassName,
);
const content = (
<>
<presentation.Icon aria-hidden className="size-3 shrink-0" />
{presentation.text}
</>
);
const linkProps = isStack
? {
onClick: (event: MouseEvent<HTMLElement>) => {
event.preventDefault();
event.stopPropagation();
onOpenStack();
},
}
: { onClick: onOpenPullRequest };
const element = isStack ? (
<button type="button" />
) : (
<a href={url} target="_blank" rel="noopener noreferrer" />
);
return (
<Tooltip>
<TooltipTrigger
render={
isStack ? (
<InlineButton
className={className}
variant === "ghost" ? (
<Button
render={element}
variant="ghost"
size="xs"
className={presentation.toneClassName}
aria-label={presentation.label}
onPointerDown={(event) => event.stopPropagation()}
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
onOpenStack();
}}
{...linkProps}
/>
) : (
<a
href={url}
target="_blank"
rel="noopener noreferrer"
className={className}
<InlineButton
render={element}
className={presentation.toneClassName}
aria-label={presentation.label}
onPointerDown={(event) => event.stopPropagation()}
onClick={onOpenPullRequest}
{...linkProps}
/>
)
}
Expand Down
5 changes: 1 addition & 4 deletions apps/web/src/components/pullRequest/PullRequestGhosts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,7 @@ export function PullRequestDetailGhost({
<span className="min-w-0 truncate font-medium">{seed.repository}</span>
<InlineButton
onClick={() => void readLocalApi()?.shell.openExternal(seed.url)}
className={cn(
"font-medium underline-offset-2 hover:underline",
statePresentation?.toneClassName,
)}
className={statePresentation?.toneClassName}
aria-label={`Open pull request #${seed.number} on host`}
>
#{seed.number}
Expand Down
3 changes: 2 additions & 1 deletion apps/web/src/components/settings/ExpandableText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ export function ExpandableText({
<InlineButton
aria-expanded={expanded}
aria-controls={textId}
className="mt-1 text-[11px] font-medium text-foreground/70 underline-offset-2 hover:text-foreground hover:underline"
tone="muted"
className="mt-1"
onClick={() => setExpanded((value) => !value)}
>
{expanded ? "Show less" : expandLabel}
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/components/settings/ProcessSignalActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function ProcessSignalActions({
<InlineButton
disabled={disabled}
aria-label="Send SIGINT"
className="text-[11px] font-medium text-muted-foreground underline-offset-2 hover:text-foreground hover:underline"
tone="muted"
onClick={() => onSignal("SIGINT")}
>
INT
Expand All @@ -34,7 +34,7 @@ export function ProcessSignalActions({
<InlineButton
disabled={disabled}
aria-label="Send SIGKILL"
className="text-[11px] font-medium text-destructive underline-offset-2 hover:underline"
tone="destructive"
onClick={() => onSignal("SIGKILL")}
>
KILL
Expand Down
10 changes: 2 additions & 8 deletions apps/web/src/components/settings/SettingInheritance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -286,10 +286,7 @@ export function SettingInheritance({
<div className="flex items-center justify-between gap-3 px-2 text-xs text-muted-foreground">
<span>Overridden by</span>
{onClearOverrides ? (
<InlineButton
className="font-medium text-foreground underline-offset-2 hover:underline"
onClick={() => onClearOverrides(overriding)}
>
<InlineButton onClick={() => onClearOverrides(overriding)}>
Reset {overriding.length === 1 ? "it" : "all"}
</InlineButton>
) : null}
Expand All @@ -300,10 +297,7 @@ export function SettingInheritance({
key={project.projectId}
className="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-x-3 px-2 py-1"
>
<InlineButton
className="min-w-0 justify-start text-left text-foreground underline-offset-2 hover:underline"
onClick={project.open}
>
<InlineButton className="min-w-0 justify-start" onClick={project.open}>
<span className="truncate">{project.label}</span>
</InlineButton>
<span className="max-w-32 truncate text-muted-foreground tabular-nums">
Expand Down
6 changes: 1 addition & 5 deletions apps/web/src/components/sidebar/SidebarThreadUndoNotice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@ export function SidebarThreadUndoNotice() {
<Alert role="status" variant="sidebar">
<AlertDescription>
{notice.action} {notice.count} thread{notice.count === 1 ? "" : "s"},{" "}
<InlineButton
underline
onClick={undoLatestThreadAction}
className="hover:text-sidebar-foreground"
>
<InlineButton onClick={undoLatestThreadAction}>
{shortcut ? `${shortcut} to undo` : "Undo"}
</InlineButton>
</AlertDescription>
Expand Down
43 changes: 28 additions & 15 deletions apps/web/src/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,22 +97,35 @@ function Button({ className, variant, size, render, ...props }: ButtonProps) {

export { Button, buttonVariants };

/** An inline action that keeps the geometry of surrounding text or a graph node. */
const inlineButtonVariants = cva(
"inline-flex shrink-0 cursor-pointer items-center gap-0.5 whitespace-nowrap font-medium underline-offset-2 hover:underline focus-visible:outline-2 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-64",
{
defaultVariants: { tone: "default" },
variants: {
tone: {
default: "text-foreground",
muted: "text-muted-foreground hover:text-foreground",
destructive: "text-destructive/80 hover:text-destructive",
},
},
},
);

/** An inline text action that keeps the size of the surrounding text and underlines on hover. */
export function InlineButton({
className,
underline = false,
tone,
render,
...props
}: React.ComponentProps<"button"> & { underline?: boolean }) {
return (
<button
type="button"
data-slot="inline-button"
className={cn(
"inline-flex shrink-0 cursor-pointer items-center gap-0.5 whitespace-nowrap focus-visible:outline-2 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-64",
underline && "border-b border-transparent hover:border-current",
className,
)}
{...props}
/>
);
}: useRender.ComponentProps<"button"> & VariantProps<typeof inlineButtonVariants>) {
const defaultProps = {
className: cn(inlineButtonVariants({ tone }), className),
"data-slot": "inline-button",
type: render ? undefined : ("button" as const),
};
return useRender({
defaultTagName: "button",
props: mergeProps<"button">(defaultProps, props),
render,
Comment thread
juliusmarminge marked this conversation as resolved.
});
}
2 changes: 1 addition & 1 deletion scripts/lint-restyle-ceiling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as NodeURL from "node:url";
// components/ui exports are migrated to variants (see vite.config.ts). This gate keeps the
// count from growing: CI fails when findings exceed the ceiling. Lower the ceiling when you
// migrate a file, and delete this script when the rule becomes an error.
export const RESTYLE_CEILING = 865;
export const RESTYLE_CEILING = 826;

const RULE = "shadcn(no-restyle)";

Expand Down
Loading