diff --git a/apps/runtime/resilience.ts b/apps/runtime/resilience.ts index 22eee4d..a8cfe40 100644 --- a/apps/runtime/resilience.ts +++ b/apps/runtime/resilience.ts @@ -31,8 +31,7 @@ const IGNORABLE_ERRORS = [ ]; function isIgnorable(err: unknown): boolean { - const msg = - err instanceof Error ? `${err.name}: ${err.message}` : String(err); + const msg = err instanceof Error ? `${err.name}: ${err.message}` : String(err); if (IGNORABLE_ERRORS.some((token) => msg.includes(token))) return true; // Check cause chain (Node.js fetch wraps network errors) if (err instanceof Error && err.cause) { @@ -93,17 +92,17 @@ function gracefulShutdown() { if (shuttingDown) return; shuttingDown = true; for (const cb of shutdownCallbacks) { - try { cb(); } catch (e) { console.error("[shutdown] callback error", e); } + try { + cb(); + } catch (e) { + console.error("[shutdown] callback error", e); + } } const mem = process.memoryUsage(); const heapMb = Math.round(mem.heapUsed / 1024 / 1024); - console.warn( - `[memory] Heap at ${heapMb}MB — initiating graceful shutdown to avoid OOM crash`, - ); - console.warn( - `[memory] Draining in-flight requests for ${GRACEFUL_DRAIN_MS}ms before exit…`, - ); + console.warn(`[memory] Heap at ${heapMb}MB — initiating graceful shutdown to avoid OOM crash`); + console.warn(`[memory] Draining in-flight requests for ${GRACEFUL_DRAIN_MS}ms before exit…`); // Give in-flight requests a few seconds to complete, then exit cleanly. // Render will restart the instance automatically. @@ -124,9 +123,7 @@ setInterval(() => { } if (rssMb > MEMORY_WARN_THRESHOLD_MB) { - console.warn( - `[memory] WARNING RSS=${rssMb}MB heap=${heapMb}MB — approaching limit`, - ); + console.warn(`[memory] WARNING RSS=${rssMb}MB heap=${heapMb}MB — approaching limit`); if (global.gc) { console.warn("[memory] Forcing garbage collection"); global.gc(); diff --git a/apps/ui/src/App.tsx b/apps/ui/src/App.tsx index 49e3c06..8cebb45 100644 --- a/apps/ui/src/App.tsx +++ b/apps/ui/src/App.tsx @@ -6,19 +6,22 @@ import { useChatKit } from "@/components/chat/chat-kit"; import { s } from "@hashbrownai/core"; import { chatTheme } from "@/lib/chat-theme"; import { useInitialSuggestions } from "./lib/suggestions"; +import { StyleProvider } from "@/lib/style-context"; export function App() { return ( - - - + + + + + ); } diff --git a/apps/ui/src/components/app-header.tsx b/apps/ui/src/components/app-header.tsx index 1b76e8d..a496fe5 100644 --- a/apps/ui/src/components/app-header.tsx +++ b/apps/ui/src/components/app-header.tsx @@ -1,4 +1,8 @@ +import { useStyle } from "@/lib/style-context"; + export function AppHeader({ title }: { title: string }) { + const { style, setStyle } = useStyle(); + return (

{title}

+
) { + return ; +} + +function AccordionItem({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AccordionTrigger({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + svg]:rotate-180", + className, + )} + {...props} + > + {children} + + + + ); +} + +function AccordionContent({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + +
{children}
+
+ ); +} + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }; diff --git a/apps/ui/src/components/ui-default/alert-dialog.tsx b/apps/ui/src/components/ui-default/alert-dialog.tsx new file mode 100644 index 0000000..c85227b --- /dev/null +++ b/apps/ui/src/components/ui-default/alert-dialog.tsx @@ -0,0 +1,179 @@ +"use client"; + +import * as React from "react"; +import { AlertDialog as AlertDialogPrimitive } from "radix-ui"; + +import { cn } from "@/lib/utils"; +import { Button } from "@/components/ui/button"; + +function AlertDialog({ ...props }: React.ComponentProps) { + return ; +} + +function AlertDialogTrigger({ + ...props +}: React.ComponentProps) { + return ; +} + +function AlertDialogPortal({ ...props }: React.ComponentProps) { + return ; +} + +function AlertDialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogContent({ + className, + size = "default", + ...props +}: React.ComponentProps & { + size?: "default" | "sm"; +}) { + return ( + + + + + ); +} + +function AlertDialogHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function AlertDialogFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function AlertDialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogMedia({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function AlertDialogAction({ + className, + variant = "default", + size = "default", + ...props +}: React.ComponentProps & + Pick, "variant" | "size">) { + return ( + + ); +} + +function AlertDialogCancel({ + className, + variant = "outline", + size = "default", + ...props +}: React.ComponentProps & + Pick, "variant" | "size">) { + return ( + + ); +} + +export { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogMedia, + AlertDialogOverlay, + AlertDialogPortal, + AlertDialogTitle, + AlertDialogTrigger, +}; diff --git a/apps/ui/src/components/ui-default/alert.tsx b/apps/ui/src/components/ui-default/alert.tsx new file mode 100644 index 0000000..55632ed --- /dev/null +++ b/apps/ui/src/components/ui-default/alert.tsx @@ -0,0 +1,60 @@ +import * as React from "react"; +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "@/lib/utils"; + +const alertVariants = cva( + "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current", + { + variants: { + variant: { + default: "bg-card text-card-foreground", + destructive: + "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + +function Alert({ + className, + variant, + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
+ ); +} + +function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function AlertDescription({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +export { Alert, AlertTitle, AlertDescription }; diff --git a/apps/ui/src/components/ui-default/aspect-ratio.tsx b/apps/ui/src/components/ui-default/aspect-ratio.tsx new file mode 100644 index 0000000..b5993d1 --- /dev/null +++ b/apps/ui/src/components/ui-default/aspect-ratio.tsx @@ -0,0 +1,9 @@ +"use client"; + +import { AspectRatio as AspectRatioPrimitive } from "radix-ui"; + +function AspectRatio({ ...props }: React.ComponentProps) { + return ; +} + +export { AspectRatio }; diff --git a/apps/ui/src/components/ui-default/avatar.tsx b/apps/ui/src/components/ui-default/avatar.tsx new file mode 100644 index 0000000..41cde35 --- /dev/null +++ b/apps/ui/src/components/ui-default/avatar.tsx @@ -0,0 +1,94 @@ +import * as React from "react"; +import { Avatar as AvatarPrimitive } from "radix-ui"; + +import { cn } from "@/lib/utils"; + +function Avatar({ + className, + size = "default", + ...props +}: React.ComponentProps & { + size?: "default" | "sm" | "lg"; +}) { + return ( + + ); +} + +function AvatarImage({ className, ...props }: React.ComponentProps) { + return ( + + ); +} + +function AvatarFallback({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) { + return ( + svg]:hidden", + "group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2", + "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2", + className, + )} + {...props} + /> + ); +} + +function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function AvatarGroupCount({ className, ...props }: React.ComponentProps<"div">) { + return ( +
svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3", + className, + )} + {...props} + /> + ); +} + +export { Avatar, AvatarImage, AvatarFallback, AvatarBadge, AvatarGroup, AvatarGroupCount }; diff --git a/apps/ui/src/components/ui-default/badge.tsx b/apps/ui/src/components/ui-default/badge.tsx new file mode 100644 index 0000000..716e827 --- /dev/null +++ b/apps/ui/src/components/ui-default/badge.tsx @@ -0,0 +1,46 @@ +import * as React from "react"; +import { cva, type VariantProps } from "class-variance-authority"; +import { Slot } from "radix-ui"; + +import { cn } from "@/lib/utils"; + +const badgeVariants = cva( + "inline-flex items-center justify-center rounded-full border border-transparent px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90", + secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", + destructive: + "bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + link: "text-primary underline-offset-4 [a&]:hover:underline", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + +function Badge({ + className, + variant = "default", + asChild = false, + ...props +}: React.ComponentProps<"span"> & VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot.Root : "span"; + + return ( + + ); +} + +export { Badge, badgeVariants }; diff --git a/apps/ui/src/components/ui-default/breadcrumb.tsx b/apps/ui/src/components/ui-default/breadcrumb.tsx new file mode 100644 index 0000000..2b4fa1e --- /dev/null +++ b/apps/ui/src/components/ui-default/breadcrumb.tsx @@ -0,0 +1,102 @@ +import * as React from "react"; +import { ChevronRight, MoreHorizontal } from "lucide-react"; +import { Slot } from "radix-ui"; + +import { cn } from "@/lib/utils"; + +function Breadcrumb({ ...props }: React.ComponentProps<"nav">) { + return