polish: refine sidebar activity indicators, add placeholder token, and tidy search field (#8606)
Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com>
This commit is contained in:
@@ -122,6 +122,7 @@
|
||||
--text-success: var(--color-green-300);
|
||||
--text-warning: var(--color-yellow-200);
|
||||
--text-info: var(--color-blue-200);
|
||||
--text-placeholder: var(--color-gray-400);
|
||||
|
||||
--ring: color-mix(in srgb, var(--border-strong) 20%, transparent);
|
||||
|
||||
@@ -304,6 +305,7 @@
|
||||
--text-success: var(--color-green-100);
|
||||
--text-warning: var(--color-yellow-100);
|
||||
--text-info: var(--color-blue-100);
|
||||
--text-placeholder: var(--color-gray-600);
|
||||
|
||||
--ring: color-mix(in srgb, var(--border-strong) 20%, transparent);
|
||||
|
||||
@@ -356,6 +358,7 @@
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-placeholder: var(--text-placeholder);
|
||||
--color-accent: var(--accent);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-destructive: var(--destructive);
|
||||
@@ -407,6 +410,7 @@
|
||||
--color-text-success: var(--text-success);
|
||||
--color-text-warning: var(--text-warning);
|
||||
--color-text-info: var(--text-info);
|
||||
--color-text-placeholder: var(--text-placeholder);
|
||||
|
||||
/* alpha variants */
|
||||
--color-dark-10: var(--dark-10);
|
||||
|
||||
@@ -3,25 +3,22 @@ import { describe, expect, it } from "vitest";
|
||||
import { SessionActivityIndicator } from "./SessionActivityIndicator";
|
||||
|
||||
describe("SessionActivityIndicator", () => {
|
||||
it("renders a brand-colored inline spinner for running sessions", () => {
|
||||
it("renders an inline spinner for running sessions", () => {
|
||||
render(<SessionActivityIndicator isRunning />);
|
||||
|
||||
expect(screen.getByLabelText(/chat active/i)).toHaveClass("text-brand");
|
||||
expect(screen.getByLabelText(/chat active/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders a brand-colored inline dot for unread sessions", () => {
|
||||
it("renders an inline dot for unread sessions", () => {
|
||||
render(<SessionActivityIndicator hasUnread />);
|
||||
|
||||
expect(screen.getByLabelText(/unread messages/i)).toHaveClass("bg-brand");
|
||||
expect(screen.getByLabelText(/unread messages/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders an overlay spinner variant for running sessions", () => {
|
||||
const { container } = render(
|
||||
<SessionActivityIndicator isRunning variant="overlay" />,
|
||||
);
|
||||
render(<SessionActivityIndicator isRunning variant="overlay" />);
|
||||
|
||||
expect(screen.getByLabelText(/chat active/i)).toBeInTheDocument();
|
||||
expect(container.querySelector(".text-brand")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("renders nothing when the session is idle and read", () => {
|
||||
|
||||
@@ -21,24 +21,32 @@ export function SessionActivityIndicator({
|
||||
role="status"
|
||||
aria-label="Chat active"
|
||||
className={cn(
|
||||
"absolute -right-1 -top-1 flex h-3.5 w-3.5 items-center justify-center rounded-full border border-background bg-background shadow-sm",
|
||||
"absolute -right-1 -top-1 flex h-3.5 w-3.5 items-center justify-center rounded-full border border-background bg-background shadow-sm transition-opacity duration-200 ease-out animate-in fade-in-0",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<Loader2
|
||||
aria-hidden="true"
|
||||
className="h-2.5 w-2.5 animate-spin text-brand"
|
||||
className="h-2.5 w-2.5 animate-spin text-text-info"
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Loader2
|
||||
<span
|
||||
role="status"
|
||||
aria-label="Chat active"
|
||||
className={cn("h-3 w-3 shrink-0 animate-spin text-brand", className)}
|
||||
/>
|
||||
className={cn(
|
||||
"inline-flex h-3 w-3 shrink-0 items-center justify-center animate-in fade-in-0 duration-200 ease-out",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<Loader2
|
||||
aria-hidden="true"
|
||||
className="h-3 w-3 animate-spin text-text-info"
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -52,7 +60,7 @@ export function SessionActivityIndicator({
|
||||
role="status"
|
||||
aria-label="Unread messages"
|
||||
className={cn(
|
||||
"absolute -right-0.5 -top-0.5 h-2.5 w-2.5 shrink-0 rounded-full border border-background bg-brand",
|
||||
"absolute -right-0.5 -top-0.5 h-2 w-2 shrink-0 rounded-full border border-background bg-background-info transition-opacity duration-200 ease-out animate-in fade-in-0",
|
||||
className,
|
||||
)}
|
||||
/>
|
||||
@@ -63,7 +71,10 @@ export function SessionActivityIndicator({
|
||||
<span
|
||||
role="status"
|
||||
aria-label="Unread messages"
|
||||
className={cn("h-2 w-2 shrink-0 rounded-full bg-brand", className)}
|
||||
className={cn(
|
||||
"h-1.5 w-1.5 shrink-0 rounded-full bg-background-info transition-opacity duration-200 ease-out animate-in fade-in-0",
|
||||
className,
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ function CommandInput({
|
||||
<CommandPrimitive.Input
|
||||
data-slot="command-input"
|
||||
className={cn(
|
||||
"placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden focus-visible:ring-0 focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"placeholder:text-placeholder flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden focus-visible:ring-0 focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { cn } from "@/shared/lib/cn";
|
||||
|
||||
const variantStyles = {
|
||||
default: [
|
||||
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground border-input hover:border-border-input-hover focus-visible:border-ring focus-visible:ring-0 focus-visible:ring-offset-0 flex h-9 w-full min-w-0 rounded-input border bg-transparent px-3 py-1 text-base transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
"file:text-foreground placeholder:text-placeholder selection:bg-primary selection:text-primary-foreground border-input hover:border-border-input-hover focus-visible:border-ring focus-visible:ring-0 focus-visible:ring-offset-0 flex h-9 w-full min-w-0 rounded-input border bg-transparent px-3 py-1 text-base transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
],
|
||||
ghost: [
|
||||
|
||||
@@ -7,7 +7,7 @@ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
||||
<textarea
|
||||
data-slot="textarea"
|
||||
className={cn(
|
||||
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-0 focus-visible:ring-offset-0 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base transition-[color,box-shadow] outline-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
"border-input placeholder:text-placeholder focus-visible:border-ring focus-visible:ring-0 focus-visible:ring-offset-0 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base transition-[color,box-shadow] outline-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
Reference in New Issue
Block a user