Show proxy status in UI

This commit is contained in:
Gregory Schier
2026-03-11 15:09:21 -07:00
parent 90365f0723
commit f51f72a332
13 changed files with 289 additions and 194 deletions

View File

@@ -1,5 +1,9 @@
import type { Color } from "@yaakapp-internal/plugins";
import classNames from "classnames";
import type { HTMLAttributes, ReactNode } from "react";
import { forwardRef } from "react";
import { Icon } from "./Icon";
import { LoadingIcon } from "./LoadingIcon";
type ButtonVariant = "border" | "solid";
type ButtonSize = "2xs" | "xs" | "sm" | "md" | "auto";
@@ -16,41 +20,43 @@ export type ButtonProps = Omit<
size?: ButtonSize;
justify?: "start" | "center";
type?: "button" | "submit";
forDropdown?: boolean;
disabled?: boolean;
title?: string;
leftSlot?: ReactNode;
rightSlot?: ReactNode;
};
function cx(...values: Array<string | false | null | undefined>) {
return values.filter(Boolean).join(" ");
}
export function Button({
isLoading,
className,
innerClassName,
children,
color,
tone,
type = "button",
justify = "center",
size = "md",
variant = "solid",
leftSlot,
rightSlot,
disabled,
title,
onClick,
...props
}: ButtonProps) {
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
{
isLoading,
className,
innerClassName,
children,
color,
tone,
forDropdown,
type = "button",
justify = "center",
size = "md",
variant = "solid",
leftSlot,
rightSlot,
disabled,
title,
onClick,
...props
}: ButtonProps,
ref,
) {
const resolvedColor = color ?? tone ?? "default";
const isDisabled = disabled || isLoading;
return (
<button
ref={ref}
type={type}
className={cx(
className={classNames(
className,
"x-theme-button",
`x-theme-button--${variant}`,
@@ -83,7 +89,8 @@ export function Button({
variant === "border" && "border",
variant === "border" &&
resolvedColor !== "custom" &&
"border-border-subtle text-text-subtle enabled:hocus:border-border enabled:hocus:bg-surface-highlight enabled:hocus:text-text outline-border-subtler",
"border-border-subtle text-text-subtle enabled:hocus:border-border " +
"enabled:hocus:bg-surface-highlight enabled:hocus:text-text outline-border-subtler",
)}
disabled={isDisabled}
onClick={onClick}
@@ -94,12 +101,12 @@ export function Button({
{...props}
>
{isLoading ? (
<div className="mr-1">...</div>
<LoadingIcon size={size === "auto" ? "md" : size} className="mr-1" />
) : leftSlot ? (
<div className="mr-2">{leftSlot}</div>
) : null}
<div
className={cx(
className={classNames(
"truncate w-full",
justify === "start" ? "text-left" : "text-center",
innerClassName,
@@ -107,7 +114,14 @@ export function Button({
>
{children}
</div>
{rightSlot ? <div className="ml-1">{rightSlot}</div> : null}
{rightSlot && <div className="ml-1">{rightSlot}</div>}
{forDropdown && (
<Icon
icon="chevron_down"
size={size === "auto" ? "md" : size}
className="ml-1 -mr-1 relative top-[0.1em]"
/>
)}
</button>
);
}
});

View File

@@ -16,3 +16,4 @@ export type { TreeHandle, TreeProps } from "./components/tree/Tree";
export type { TreeNode } from "./components/tree/common";
export type { TreeItemProps } from "./components/tree/TreeItem";
export { isSelectedFamily, selectedIdsFamily } from "./components/tree/atoms";
export { minPromiseMillis } from "./lib/minPromiseMillis";

View File

@@ -0,0 +1,16 @@
export async function minPromiseMillis<T>(promise: Promise<T>, millis = 300): Promise<T> {
const start = Date.now();
let result: T;
try {
result = await promise;
} catch (e) {
const remaining = millis - (Date.now() - start);
if (remaining > 0) await new Promise((r) => setTimeout(r, remaining));
throw e;
}
const remaining = millis - (Date.now() - start);
if (remaining > 0) await new Promise((r) => setTimeout(r, remaining));
return result;
}