raft-ui
Version:
React UI components for Raft.
1,407 lines (1,405 loc) • 572 kB
JavaScript
import { cn } from "./cn.mjs";
import { $ as buttonVariants, A as SidebarItemIndicator, B as SidebarGroupLabel, C as PanelSection, D as SidebarItemContent, E as SidebarItemChannelIcon, F as SidebarItemSubtitle, G as ScrollArea, H as SidebarHeaderTitle, I as SidebarItemTitle, J as ScrollAreaScrollbar, K as ScrollAreaContent, L as Badge, M as SidebarItemMetaIcon, O as SidebarItemCount, P as SidebarItemStatus, Q as Button, R as SidebarBody, S as PanelScrollViewport, T as SidebarItemAside, U as SidebarLiveActivity, V as SidebarHeader, W as SidebarRoot, X as ScrollAreaViewport, Y as ScrollAreaThumb, Z as useMobileViewport, _ as Panel, a as PanelHeaderIcon, at as ThemeStateContext, b as PanelHeaderRoot, c as PanelSectionHeader, d as ConversationPanelBody, et as Spinner, f as ConversationPanelContent, g as conversationPanel, h as ConversationPanelTabs, i as PanelHeaderContent, it as ThemeContext, j as SidebarItemMeta, k as SidebarItemIcon, l as PanelStatus, m as ConversationPanelRoot, n as PanelActivity, nt as useTheme, o as PanelHeading, ot as tv, p as ConversationPanelFooter, q as ScrollAreaCorner, r as PanelHeader, rt as useThemeFamily, s as PanelMeta, t as PanelActions, tt as defaultNativeButton, u as PanelTitle, v as PanelBody, w as SidebarItem, x as PanelScrollContent, y as PanelFooter, z as SidebarContent } from "./panel-header-Dzc23jwl.mjs";
import { Dialog as Dialog$1 } from "@base-ui/react/dialog";
import { AlertTriangle, Bell, Check, CheckCircle2, ChevronDown, ChevronLeft, ChevronRight, Copy, FileText, Image, Info, Minus, Upload, X } from "lucide-react";
import { Children, Fragment, cloneElement, createContext, isValidElement, use, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
import { Button as Button$1 } from "@base-ui/react/button";
import { mergeProps } from "@base-ui/react/merge-props";
import { useRender } from "@base-ui/react/use-render";
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
import { Drawer as Drawer$1 } from "@base-ui/react/drawer";
import { useClipboard } from "foxact/use-clipboard";
import { Checkbox as Checkbox$1 } from "@base-ui/react/checkbox";
import { Collapsible } from "@base-ui/react/collapsible";
import { Avatar as Avatar$1, ContextMenu as ContextMenu$1 } from "@base-ui/react";
import { Menu } from "@base-ui/react/menu";
import { useHotkey } from "@tanstack/react-hotkeys";
import { Tooltip as Tooltip$1 } from "@base-ui/react/tooltip";
import { Popover as Popover$1 } from "@base-ui/react/popover";
import { PreviewCard as PreviewCard$1 } from "@base-ui/react/preview-card";
import { Input as Input$1 } from "@base-ui/react/input";
import { Field as Field$1 } from "@base-ui/react/field";
import { Combobox as Combobox$1 } from "@base-ui/react/combobox";
import { Select as Select$1 } from "@base-ui/react/select";
import { Radio } from "@base-ui/react/radio";
import { RadioGroup as RadioGroup$1 } from "@base-ui/react/radio-group";
import { Switch as Switch$1 } from "@base-ui/react/switch";
import { Toggle } from "@base-ui/react/toggle";
import { ToggleGroup as ToggleGroup$1 } from "@base-ui/react/toggle-group";
import { Toolbar } from "@base-ui/react/toolbar";
import { Progress as Progress$1 } from "@base-ui/react/progress";
import { DndContext, DragOverlay, KeyboardSensor, PointerSensor, TouchSensor, closestCenter, pointerWithin, useDraggable, useDroppable, useSensor, useSensors } from "@dnd-kit/core";
import { SortableContext, arrayMove, defaultAnimateLayoutChanges, horizontalListSortingStrategy, sortableKeyboardCoordinates, useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { Toast } from "@base-ui/react/toast";
import { Tabs as Tabs$1 } from "@base-ui/react/tabs";
import { Separator as Separator$1 } from "@base-ui/react/separator";
import { Group, Panel as Panel$1, Separator as Separator$2 } from "react-resizable-panels";
//#region src/lib/theme/theme-scope.ts
function resolveThemeScopeProps({ className, "data-theme": _dataTheme, ...props }, { theme, resolvedMode }) {
const modeClassName = theme === "elegant" ? resolvedMode : void 0;
const resolvedClassName = typeof className === "function" && modeClassName ? (state) => cn(className(state), modeClassName) : typeof className === "string" && modeClassName ? cn(className, modeClassName) : className ?? modeClassName;
return {
...props,
"data-theme": theme,
className: resolvedClassName
};
}
function useThemeScopeProps(props) {
return resolveThemeScopeProps(props, useTheme());
}
//#endregion
//#region src/components/alert-dialog/alert-dialog.tsx
const alertDialog = tv({
slots: {
overlay: [
"fixed inset-0 z-50",
"transition-opacity duration-150 ease-out",
"data-starting-style:opacity-0 data-ending-style:opacity-0"
],
content: [
"fixed top-1/2 left-1/2 z-50 grid w-[calc(100%-2rem)] max-w-[408px] -translate-1/2 ",
"origin-center transform-gpu outline-none transition-[opacity,transform] duration-150 ease-out",
"data-starting-style:scale-[.98] data-starting-style:opacity-0",
"data-ending-style:opacity-0"
],
header: ["grid grid-cols-[minmax(0,1fr)_max-content] items-center gap-3 min-w-0"],
body: ["min-w-0"],
footer: ["flex items-center justify-end gap-3"],
title: ["m-0 min-w-0 text-foreground"],
description: ["m-0 text-foreground-muted"]
},
variants: { theme: {
brutal: {
overlay: ["bg-layer-backdrop"],
content: ["gap-4 border-2 border-line-strong bg-layer-panel px-6 py-4 shadow-raft-xl"],
body: ["py-2 font-medium"],
title: ["font-heading text-lg leading-none font-bold uppercase"],
description: ["font-sans text-sm leading-5 text-foreground-strong/60"]
},
elegant: {
overlay: ["bg-layer-backdrop backdrop-blur-[2px]"],
content: ["overflow-clip rounded-lg bg-layer-popover shadow-raft-xl"],
header: ["border-b border-line-muted/75 bg-layer-inset px-5 py-3", "dark:border-transparent dark:bg-transparent"],
body: ["px-6 py-5 dark:pt-2.5"],
footer: ["border-t border-line-muted/75 bg-layer-inset px-5 py-3", "dark:border-transparent dark:bg-transparent"],
title: ["font-sans text-sm leading-5 font-medium tracking-[-0.005em]"],
description: ["font-sans text-sm leading-5 text-foreground-hint"]
}
} }
});
function AlertDialog(props) {
return /* @__PURE__ */ jsx(Dialog$1.Root, {
"data-slot": "alert-dialog",
...props
});
}
function AlertDialogTrigger(props) {
return /* @__PURE__ */ jsx(Dialog$1.Trigger, {
"data-slot": "alert-dialog-trigger",
...props
});
}
function AlertDialogPortal(props) {
return /* @__PURE__ */ jsx(Dialog$1.Portal, {
"data-slot": "alert-dialog-portal",
...useThemeScopeProps(props)
});
}
function AlertDialogClose({ className, variant, size, children, ...props }) {
const isBrutal = useThemeFamily() === "brutal";
return /* @__PURE__ */ jsx(Dialog$1.Close, {
"data-slot": "alert-dialog-close",
render: /* @__PURE__ */ jsx(Button, {
variant: variant ?? (isBrutal ? "outline" : "ghost"),
size: size ?? (isBrutal ? "icon-md" : "icon-sm"),
"aria-label": "Close",
className: isBrutal ? className : cn("p-0 text-foreground-placeholder hover:border-transparent hover:bg-transparent hover:text-foreground-strong", className)
}),
...props,
children: children ?? /* @__PURE__ */ jsx(X, { className: isBrutal ? "size-5" : "size-4" })
});
}
function AlertDialogOverlay({ className, ...props }) {
const theme = useThemeFamily();
const { overlay } = alertDialog({ theme });
return /* @__PURE__ */ jsx(Dialog$1.Backdrop, {
"data-slot": "alert-dialog-overlay",
className: overlay({ className }),
...props
});
}
function AlertDialogContent({ className, children, portalProps, overlay, ...props }) {
const theme = useThemeFamily();
const { content } = alertDialog({ theme });
return /* @__PURE__ */ jsxs(AlertDialogPortal, {
...portalProps,
children: [overlay === false ? null : /* @__PURE__ */ jsx(AlertDialogOverlay, { ...overlay }), /* @__PURE__ */ jsx(Dialog$1.Popup, {
"data-slot": "alert-dialog-content",
className: content({ className }),
...props,
role: "alertdialog",
children
})]
});
}
function AlertDialogHeader({ className, ...props }) {
const theme = useThemeFamily();
const { header } = alertDialog({ theme });
return /* @__PURE__ */ jsx("div", {
"data-slot": "alert-dialog-header",
className: header({ className }),
...props
});
}
function AlertDialogBody({ className, ...props }) {
const theme = useThemeFamily();
const { body } = alertDialog({ theme });
return /* @__PURE__ */ jsx("div", {
"data-slot": "alert-dialog-body",
className: body({ className }),
...props
});
}
function AlertDialogFooter({ className, ...props }) {
const theme = useThemeFamily();
const { footer } = alertDialog({ theme });
return /* @__PURE__ */ jsx("div", {
"data-slot": "alert-dialog-footer",
className: footer({ className }),
...props
});
}
function AlertDialogTitle({ className, ...props }) {
const theme = useThemeFamily();
const { title } = alertDialog({ theme });
return /* @__PURE__ */ jsx(Dialog$1.Title, {
"data-slot": "alert-dialog-title",
className: title({ className }),
...props
});
}
function AlertDialogDescription({ className, ...props }) {
const theme = useThemeFamily();
const { description } = alertDialog({ theme });
return /* @__PURE__ */ jsx(Dialog$1.Description, {
"data-slot": "alert-dialog-description",
className: description({ className }),
...props
});
}
function AlertDialogAction(props) {
return /* @__PURE__ */ jsx(Button, {
"data-slot": "alert-dialog-action",
...props
});
}
function AlertDialogCancel({ className, variant = "outline", size = "sm", ...props }) {
return /* @__PURE__ */ jsx(Dialog$1.Close, {
"data-slot": "alert-dialog-cancel",
render: /* @__PURE__ */ jsx(Button, {
className,
variant,
size
}),
...props
});
}
//#endregion
//#region src/components/dialog/dialog.tsx
const dialog = tv({
slots: {
overlay: [
"fixed inset-0 z-50",
"transition-opacity duration-150 ease-out",
"data-starting-style:opacity-0 data-ending-style:opacity-0"
],
content: [
"fixed top-1/2 left-1/2 z-50 grid w-[calc(100%-2rem)] max-w-md -translate-1/2 ",
"origin-center transform-gpu outline-none transition-[opacity,transform] duration-150 ease-out",
"data-starting-style:scale-[.98] data-starting-style:opacity-0",
"data-ending-style:opacity-0"
],
header: ["grid grid-cols-[minmax(0,1fr)_max-content] items-center gap-3 min-w-0"],
body: ["min-w-0"],
footer: ["flex items-center justify-end gap-3"],
title: ["m-0 min-w-0 text-foreground"],
description: ["m-0 text-foreground-muted"]
},
variants: { theme: {
brutal: {
overlay: ["bg-layer-backdrop"],
content: ["gap-4 border-2 border-line-strong bg-layer-panel px-6 py-4 shadow-raft-xl"],
body: ["py-2 font-medium"],
title: ["font-heading text-lg leading-none font-bold uppercase"],
description: ["font-sans text-sm leading-5 text-foreground-strong/60"]
},
elegant: {
overlay: ["bg-layer-backdrop backdrop-blur-[2px]"],
content: ["overflow-clip rounded-lg bg-layer-popover shadow-raft-xl"],
header: ["border-b border-line-muted/75 bg-layer-inset px-6 py-3", "dark:border-transparent dark:bg-transparent"],
body: ["px-6 py-5 dark:pt-2.5"],
footer: ["border-t border-line-muted/75 bg-layer-inset px-6 py-3", "dark:border-transparent dark:bg-transparent"],
title: ["font-sans text-sm leading-5 font-medium tracking-[-0.005em]"],
description: ["font-sans text-sm leading-5 text-foreground-hint"]
}
} }
});
function Dialog(props) {
return /* @__PURE__ */ jsx(Dialog$1.Root, {
"data-slot": "dialog",
...props
});
}
function DialogTrigger(props) {
return /* @__PURE__ */ jsx(Dialog$1.Trigger, {
"data-slot": "dialog-trigger",
...props
});
}
function DialogPortal(props) {
return /* @__PURE__ */ jsx(Dialog$1.Portal, {
"data-slot": "dialog-portal",
...useThemeScopeProps(props)
});
}
function DialogOverlay({ className, ...props }) {
const theme = useThemeFamily();
const { overlay } = dialog({ theme });
return /* @__PURE__ */ jsx(Dialog$1.Backdrop, {
"data-slot": "dialog-overlay",
className: overlay({ className }),
...props
});
}
function DialogContent({ className, children, portalProps, overlay, ...props }) {
const theme = useThemeFamily();
const { content } = dialog({ theme });
return /* @__PURE__ */ jsxs(DialogPortal, {
...portalProps,
children: [overlay === false ? null : /* @__PURE__ */ jsx(DialogOverlay, { ...overlay }), /* @__PURE__ */ jsx(Dialog$1.Popup, {
"data-slot": "dialog-content",
className: content({ className }),
...props,
children
})]
});
}
function DialogClose({ className, variant, size, children, render, ...props }) {
const isBrutal = useThemeFamily() === "brutal";
if (render) return /* @__PURE__ */ jsx(Dialog$1.Close, {
"data-slot": "dialog-close",
render,
...props,
children
});
return /* @__PURE__ */ jsx(Dialog$1.Close, {
"data-slot": "dialog-close",
render: /* @__PURE__ */ jsx(Button, {
variant: variant ?? (isBrutal ? "outline" : "ghost"),
size: size ?? (isBrutal ? "icon-md" : "icon-sm"),
"aria-label": "Close",
className: isBrutal ? className : cn("p-0 text-foreground-placeholder hover:border-transparent hover:bg-transparent hover:text-foreground-strong", className)
}),
...props,
children: children ?? /* @__PURE__ */ jsx(X, { className: isBrutal ? "size-5" : "size-4" })
});
}
function DialogHeader({ className, ...props }) {
const theme = useThemeFamily();
const { header } = dialog({ theme });
return /* @__PURE__ */ jsx("div", {
"data-slot": "dialog-header",
className: header({ className }),
...props
});
}
function DialogBody({ className, ...props }) {
const theme = useThemeFamily();
const { body } = dialog({ theme });
return /* @__PURE__ */ jsx("div", {
"data-slot": "dialog-body",
className: body({ className }),
...props
});
}
function DialogFooter({ className, ...props }) {
const theme = useThemeFamily();
const { footer } = dialog({ theme });
return /* @__PURE__ */ jsx("div", {
"data-slot": "dialog-footer",
className: footer({ className }),
...props
});
}
function DialogTitle({ className, ...props }) {
const theme = useThemeFamily();
const { title } = dialog({ theme });
return /* @__PURE__ */ jsx(Dialog$1.Title, {
"data-slot": "dialog-title",
className: title({ className }),
...props
});
}
function DialogDescription({ className, ...props }) {
const theme = useThemeFamily();
const { description } = dialog({ theme });
return /* @__PURE__ */ jsx(Dialog$1.Description, {
"data-slot": "dialog-description",
className: description({ className }),
...props
});
}
const drawer = tv({
extend: tv({
slots: {
overlay: [
"opacity-[max(var(--drawer-overlay-min-opacity,0),calc(1-var(--drawer-swipe-progress)))]",
"transition-opacity duration-200 ease-slide",
"data-starting-style:opacity-0 data-ending-style:pointer-events-none data-ending-style:opacity-0 data-ending-style:duration-150",
"motion-reduce:transition-none"
],
popup: [
"[--drawer-blur:2px] [--drawer-ease:var(--ease-slide)]",
"[--closed-transform:translate3d(var(--drawer-exit-x,0px),var(--drawer-exit-y,0px),0)]",
"[--drawer-stack-peek:16px] [--drawer-stack-step:0.05]",
"[--drawer-stack-progress:clamp(0,var(--drawer-swipe-progress),1)]",
"[--drawer-stack-offset:max(0px,calc((var(--nested-drawers)-var(--drawer-stack-progress))*var(--drawer-stack-peek)))]",
"[--drawer-stack-scale-base:calc(max(0,1-(var(--nested-drawers)*var(--drawer-stack-step))))]",
"[--drawer-stack-scale:clamp(0,calc(var(--drawer-stack-scale-base)+(var(--drawer-stack-step)*var(--drawer-stack-progress))),1)]",
"[--drawer-stack-shrink:calc(1-var(--drawer-stack-scale))]",
"[--drawer-stack-shrink-offset:calc(var(--drawer-stack-shrink)*var(--drawer-stack-size))]",
"origin-(--drawer-transform-origin)",
"transform-[translate3d(calc(var(--drawer-swipe-x,0px)+var(--drawer-stack-x,0px)),calc(var(--drawer-swipe-y,0px)+var(--drawer-stack-y,0px)),0)_scale(var(--drawer-stack-scale))]",
"opacity-100 blur-0",
"transition-[transform,height,opacity,filter] duration-240 ease-(--drawer-ease) will-change-transform",
"data-ending-style:duration-[calc(var(--drawer-swipe-strength,1)*180ms)]",
"data-starting-style:transform-(--closed-transform) data-starting-style:opacity-0 data-starting-style:blur-(--drawer-blur)",
"data-ending-style:transform-(--closed-transform) data-ending-style:opacity-0 data-ending-style:blur-(--drawer-blur)",
"data-swiping:duration-0 data-nested-drawer-swiping:duration-0 motion-reduce:transition-none"
],
content: ["transition-opacity duration-150 ease-slide motion-reduce:transition-none", "group-data-nested-drawer-open/drawer-popup:opacity-0 group-data-nested-drawer-swiping/drawer-popup:opacity-100"],
swipeHandle: ["transition-opacity duration-150 motion-reduce:transition-none", "group-data-nested-drawer-open/drawer-popup:opacity-0 group-data-nested-drawer-swiping/drawer-popup:opacity-100"]
},
variants: {
side: {
down: { popup: [
"[--drawer-exit-y:calc(100%+var(--drawer-inset)+2px)]",
"[--drawer-stack-y:calc(0px-var(--drawer-stack-offset)-var(--drawer-stack-shrink-offset))]",
"[--drawer-transform-origin:50%_100%]"
] },
up: { popup: [
"[--drawer-exit-y:calc(-100%-var(--drawer-inset)-2px)]",
"[--drawer-stack-y:calc(var(--drawer-stack-offset)+var(--drawer-stack-shrink-offset))]",
"[--drawer-transform-origin:50%_0%]"
] },
left: { popup: [
"[--drawer-exit-x:calc(-100%-var(--drawer-inset)-2px)]",
"[--drawer-stack-x:calc(var(--drawer-stack-offset)+var(--drawer-stack-shrink-offset))]",
"[--drawer-transform-origin:0%_50%]"
] },
right: { popup: [
"[--drawer-exit-x:calc(100%+var(--drawer-inset)+2px)]",
"[--drawer-stack-x:calc(0px-var(--drawer-stack-offset)-var(--drawer-stack-shrink-offset))]",
"[--drawer-transform-origin:100%_50%]"
] }
},
axis: {
x: { popup: ["[--drawer-stack-size:100%]", "[--drawer-swipe-x:var(--drawer-swipe-movement-x)]"] },
y: { popup: ["[--drawer-stack-size:max(0px,var(--drawer-frontmost-height,var(--drawer-height)))]", "[--drawer-swipe-y:calc(var(--drawer-snap-point-offset,0px)+var(--drawer-swipe-movement-y))]"] }
},
snapPoints: { true: { overlay: ["[--drawer-overlay-min-opacity:0.5]"] } }
}
}),
slots: {
overlay: [
"fixed inset-0 z-50 min-h-dvh select-none",
"bg-layer-backdrop",
"supports-[-webkit-touch-callout:none]:absolute"
],
viewport: ["pointer-events-none fixed inset-0 z-50 select-none"],
popup: [
"group/drawer-popup pointer-events-auto fixed z-50 flex h-(--drawer-content-height) flex-col outline-none",
"text-foreground-strong",
"[--drawer-inset:12px] [--drawer-content-height:var(--drawer-height,auto)]",
"data-nested-drawer-open:overflow-hidden"
],
content: ["flex min-h-0 flex-1 flex-col rounded-[inherit] select-text", "group-data-swiping/drawer-popup:select-none"],
swipeHandle: ["relative z-10 shrink-0 cursor-grab select-none active:cursor-grabbing", "before:absolute before:rounded-full before:bg-ink-20 before:content-['']"],
close: ["shrink-0"],
closeIcon: [],
header: ["flex flex-col items-center px-6 pt-8 pb-2 text-center"],
body: ["flex flex-1 flex-col items-center px-6 pt-4 pb-6 text-center"],
footer: ["mt-auto px-6 py-5"],
actions: ["flex w-full flex-col items-stretch gap-2.5", "**:data-[slot=button]:w-full"],
title: ["max-w-sm text-pretty text-center text-2xl leading-8 text-foreground"],
description: ["max-w-sm text-pretty text-center text-sm leading-5 text-foreground-strong/60"]
},
variants: {
theme: {
brutal: {
title: ["font-bold"],
swipeHandle: ["before:rounded-none before:bg-ink-40"],
popup: ["border-2 border-line-strong bg-layer-panel shadow-raft-xl"],
close: ["bg-layer-panel"],
closeIcon: ["size-5"]
},
elegant: {
overlay: ["backdrop-blur-[2px]"],
popup: ["overflow-clip rounded-lg bg-layer-popover shadow-raft-xl md:rounded-xl"],
close: ["p-0 text-foreground-placeholder hover:bg-transparent hover:text-foreground-strong"],
closeIcon: ["size-4"],
header: ["bg-layer-panel px-8 pt-9 pb-2", "dark:bg-transparent"],
body: ["bg-layer-panel px-8 pt-4 pb-7", "dark:bg-transparent"],
footer: ["mt-0 px-8 py-5"],
title: ["font-sans text-2xl leading-8 font-semibold"],
description: ["font-sans text-sm leading-5 text-foreground-muted"]
}
},
side: {
down: {
popup: ["[--drawer-inset:max(12px,env(safe-area-inset-bottom,0px))]", "bottom-(--drawer-inset) min-h-64 max-h-[82vh]"],
swipeHandle: ["before:top-3"]
},
up: {
popup: ["[--drawer-inset:max(12px,env(safe-area-inset-top,0px))]", "top-(--drawer-inset) max-h-[80vh]"],
swipeHandle: ["order-last", "before:bottom-3"]
},
left: {
popup: ["left-(--drawer-inset)"],
swipeHandle: ["order-last", "before:right-3"]
},
right: {
popup: ["right-(--drawer-inset)"],
swipeHandle: ["before:left-3"]
}
},
axis: {
x: {
popup: ["inset-y-3", "flex-row w-3/4 max-w-sm"],
swipeHandle: ["my-auto h-16 w-11", "before:top-1/2 before:h-12 before:w-1.5 before:-translate-y-1/2"],
header: ["flex-row justify-between gap-3 pt-6 text-left"],
body: ["items-stretch text-left"],
footer: ["py-4"],
title: ["min-w-0 max-w-none flex-1 text-left"],
description: ["max-w-none text-left"]
},
y: {
popup: ["data-nested-drawer-open:h-(--drawer-frontmost-height)", "inset-x-3 mx-auto md:max-w-md"],
swipeHandle: ["mx-auto h-11 w-16", "before:left-1/2 before:h-1.5 before:w-12 before:-translate-x-1/2"]
}
}
},
compoundVariants: [
{
theme: "brutal",
side: "right",
class: { popup: ["[--drawer-inset:20px]"] }
},
{
theme: "brutal",
side: "down",
class: { popup: ["[--drawer-inset:max(20px,env(safe-area-inset-bottom,0px))]"] }
},
{
theme: "elegant",
axis: "x",
class: { popup: ["md:max-w-md"] }
},
{
side: "down",
snapPoints: true,
class: { header: ["pt-2"] }
},
{
axis: "y",
snapPoints: true,
class: { popup: ["[--drawer-content-height:100dvh]"] }
}
]
});
//#endregion
//#region src/components/drawer/drawer.tsx
const DrawerConfigContext = createContext(null);
function useDrawerConfig() {
const context = useContext(DrawerConfigContext);
if (!context) throw new Error("Drawer parts must be used within Drawer.");
return context;
}
function Drawer({ modal = true, snapPoints, swipeDirection = "down", ...props }) {
const hasSnapPoints = snapPoints != null && snapPoints.length > 0;
const swipeAxis = swipeDirection === "down" || swipeDirection === "up" ? "y" : "x";
const contextValue = useMemo(() => ({
hasSnapPoints,
modal,
swipeAxis,
swipeDirection
}), [
hasSnapPoints,
modal,
swipeAxis,
swipeDirection
]);
return /* @__PURE__ */ jsx(DrawerConfigContext.Provider, {
value: contextValue,
children: /* @__PURE__ */ jsx(Drawer$1.Root, {
modal,
snapPoints,
swipeDirection,
...props
})
});
}
function DrawerTrigger(props) {
return /* @__PURE__ */ jsx(Drawer$1.Trigger, {
"data-slot": "drawer-trigger",
...props
});
}
function DrawerPortal(props) {
return /* @__PURE__ */ jsx(Drawer$1.Portal, {
"data-slot": "drawer-portal",
...useThemeScopeProps(props)
});
}
function DrawerOverlay({ className, ...props }) {
const { hasSnapPoints } = useDrawerConfig();
const { overlay } = drawer({
theme: useThemeFamily(),
snapPoints: hasSnapPoints
});
return /* @__PURE__ */ jsx(Drawer$1.Backdrop, {
"data-slot": "drawer-overlay",
className: overlay({ className }),
...props
});
}
function DrawerSwipeHandle() {
const { swipeAxis, swipeDirection } = useDrawerConfig();
const { swipeHandle } = drawer({
theme: useThemeFamily(),
side: swipeDirection,
axis: swipeAxis
});
return /* @__PURE__ */ jsx("div", {
"data-slot": "drawer-swipe-handle",
"aria-hidden": "true",
className: swipeHandle()
});
}
function DrawerContent({ className, children, portalProps, ...props }) {
const { hasSnapPoints, modal, swipeAxis, swipeDirection } = useDrawerConfig();
const { content, popup, viewport } = drawer({
theme: useThemeFamily(),
side: swipeDirection,
axis: swipeAxis,
snapPoints: hasSnapPoints
});
return /* @__PURE__ */ jsxs(DrawerPortal, {
...portalProps,
children: [modal === true ? /* @__PURE__ */ jsx(DrawerOverlay, {}) : null, /* @__PURE__ */ jsx(Drawer$1.Viewport, {
"data-slot": "drawer-viewport",
className: cn(viewport(), modal === true && "pointer-events-auto"),
children: /* @__PURE__ */ jsxs(Drawer$1.Popup, {
"data-slot": "drawer-popup",
className: popup({ className }),
...props,
children: [hasSnapPoints ? /* @__PURE__ */ jsx(DrawerSwipeHandle, {}) : null, /* @__PURE__ */ jsx(Drawer$1.Content, {
"data-slot": "drawer-content",
className: content(),
children
})]
})
})]
});
}
function DrawerClose({ className, variant, size, children, render, ...props }) {
const theme = useThemeFamily();
const isBrutal = theme === "brutal";
const { close, closeIcon } = drawer({ theme });
if (render) return /* @__PURE__ */ jsx(Drawer$1.Close, {
"data-slot": "drawer-close",
className,
render,
...props,
children
});
return /* @__PURE__ */ jsx(Drawer$1.Close, {
"data-slot": "drawer-close",
render: /* @__PURE__ */ jsx(Button, {
variant: variant ?? (isBrutal ? "outline" : "ghost"),
size: size ?? (isBrutal ? "icon-md" : "icon-sm"),
"aria-label": "Close"
}),
className: close({ className }),
...props,
children: children ?? /* @__PURE__ */ jsx(X, { className: closeIcon() })
});
}
function DrawerHeader({ className, render, ...props }) {
const { hasSnapPoints, swipeAxis, swipeDirection } = useDrawerConfig();
const { header } = drawer({
theme: useThemeFamily(),
side: swipeDirection,
axis: swipeAxis,
snapPoints: hasSnapPoints
});
return useRender({
defaultTagName: "div",
render,
props: mergeProps({ className: header({ className }) }, props),
state: { slot: "drawer-header" }
});
}
function DrawerFooter({ className, render, ...props }) {
const { swipeAxis } = useDrawerConfig();
const { footer } = drawer({
theme: useThemeFamily(),
axis: swipeAxis
});
return useRender({
defaultTagName: "div",
render,
props: mergeProps({ className: footer({ className }) }, props),
state: { slot: "drawer-footer" }
});
}
function DrawerBody({ className, render, ...props }) {
const { swipeAxis } = useDrawerConfig();
const { body } = drawer({
theme: useThemeFamily(),
axis: swipeAxis
});
return useRender({
defaultTagName: "div",
render,
props: mergeProps({ className: body({ className }) }, props),
state: { slot: "drawer-body" }
});
}
function DrawerActions({ className, render, children, ...props }) {
const { actions } = drawer({ theme: useThemeFamily() });
return useRender({
defaultTagName: "div",
render,
props: mergeProps({
className: actions({ className }),
children
}, props),
state: { slot: "drawer-actions" }
});
}
function DrawerTitle({ className, ...props }) {
const { swipeAxis } = useDrawerConfig();
const { title } = drawer({
theme: useThemeFamily(),
axis: swipeAxis
});
return /* @__PURE__ */ jsx(Drawer$1.Title, {
"data-slot": "drawer-title",
className: title({ className }),
...props
});
}
function DrawerDescription({ className, ...props }) {
const { swipeAxis } = useDrawerConfig();
const { description } = drawer({
theme: useThemeFamily(),
axis: swipeAxis
});
return /* @__PURE__ */ jsx(Drawer$1.Description, {
"data-slot": "drawer-description",
className: description({ className }),
...props
});
}
//#endregion
//#region src/components/card/card.recipe.ts
const card = tv({
slots: {
root: ["flex min-w-0 flex-col overflow-hidden", "text-foreground"],
header: [
"group/card-header grid min-w-0 auto-rows-min grid-cols-1 items-start gap-x-4 gap-y-1",
"has-[>[data-slot=card-leading]]:grid-cols-[auto_minmax(0,1fr)]",
"has-[>[data-slot=card-trailing]]:grid-cols-[minmax(0,1fr)_auto]",
"has-[>[data-slot=card-leading]]:has-[>[data-slot=card-trailing]]:grid-cols-[auto_minmax(0,1fr)_auto]"
],
title: ["col-start-1 row-start-1 min-w-0", "group-has-[>[data-slot=card-leading]]/card-header:col-start-2"],
leading: [
"col-start-1 row-span-2 row-start-1",
"flex shrink-0 items-center self-start",
"[&>svg]:size-4 [&>svg]:shrink-0"
],
description: ["col-start-1 row-start-2 min-w-0", "group-has-[>[data-slot=card-leading]]/card-header:col-start-2"],
trailing: [
"col-start-2 row-start-1",
"flex shrink-0 items-center self-center justify-self-end",
"group-has-[>[data-slot=card-leading]]/card-header:col-start-3"
],
content: ["min-w-0"],
footer: ["flex min-w-0 flex-wrap items-center gap-2"]
},
variants: {
theme: {
brutal: {
leading: ["min-h-5"],
title: ["font-heading text-lg leading-5 font-bold"],
description: ["font-sans text-sm leading-5 text-foreground-strong/60"],
footer: [
"flex-nowrap items-center",
"font-sans text-xs leading-4 text-foreground-muted",
"[&>svg]:size-3.5 [&>svg]:shrink-0",
"[&>:not(svg)]:min-w-0"
]
},
elegant: {
leading: ["min-h-[22px]"],
title: ["font-sans text-base leading-[22px] font-medium"],
description: ["font-sans text-xs leading-4 tracking-[0.01em] text-pretty", "text-foreground-muted"],
footer: [
"flex-nowrap items-center",
"font-sans text-xs leading-4 text-foreground-hint",
"[&>svg]:size-3.5 [&>svg]:shrink-0",
"[&>:not(svg)]:min-w-0"
]
}
},
variant: {
default: {},
inset: {}
}
},
defaultVariants: { variant: "default" },
compoundVariants: [
{
theme: "brutal",
variant: "default",
class: {
root: ["border-2 border-line-strong bg-layer-panel", "shadow-raft-md"],
header: "border-b-2 border-line-strong p-4",
content: "p-4",
footer: "border-t-2 border-line-strong bg-layer-inset px-4 py-3"
}
},
{
theme: "brutal",
variant: "inset",
class: {
root: ["border-2 border-line-strong bg-layer-panel px-2", "shadow-raft-md"],
header: "px-4 py-3",
content: "-mx-2 border-y-2 border-line-strong bg-layer-panel px-6 py-4",
footer: "px-4 py-3"
}
},
{
theme: "elegant",
variant: "default",
class: {
root: ["rounded-lg border-[0.5px] border-line-muted bg-layer-panel", "shadow-raft-xs dark:border-transparent"],
header: "p-5 has-[+[data-slot=card-content]]:pb-0",
content: "p-5",
footer: ["border-t border-line-hairline bg-layer-inset px-5 py-3.5", "dark:border-transparent dark:bg-transparent"]
}
},
{
theme: "elegant",
variant: "inset",
class: {
root: ["rounded-lg border-[0.5px] border-line-muted bg-layer-card p-[2px]", "shadow-raft-xs dark:border-transparent"],
header: "px-5 py-3.5",
content: ["overflow-clip rounded-[5.5px] border-[0.5px] border-line-hairline bg-layer-panel p-5", "dark:border-transparent dark:shadow-[inset_0_1px_0_oklch(1_0_0/0.04),0_0_0_1px_oklch(0_0_0/0.35)]"],
footer: "px-5 py-3"
}
}
]
});
//#endregion
//#region src/components/card/card.tsx
const CardVariantContext = createContext("default");
function useCardRecipe() {
return card({
theme: useThemeFamily(),
variant: use(CardVariantContext)
});
}
function Card({ className, render, variant = "default", ...props }) {
const { root } = card({
theme: useThemeFamily(),
variant
});
const element = useRender({
defaultTagName: "div",
render,
props: mergeProps({
"data-slot": "card",
"data-variant": variant,
className: root({ className })
}, props)
});
return /* @__PURE__ */ jsx(CardVariantContext, {
value: variant,
children: element
});
}
function CardHeader({ className, render, ...props }) {
const { header } = useCardRecipe();
return useRender({
defaultTagName: "div",
render,
props: mergeProps({
"data-slot": "card-header",
className: header({ className })
}, props)
});
}
function CardTitle({ className, render, ...props }) {
const { title } = useCardRecipe();
return useRender({
defaultTagName: "div",
render,
props: mergeProps({
"data-slot": "card-title",
className: title({ className })
}, props)
});
}
function CardLeading({ className, render, ...props }) {
const { leading } = useCardRecipe();
return useRender({
defaultTagName: "div",
render,
props: mergeProps({
"data-slot": "card-leading",
className: leading({ className })
}, props)
});
}
function CardDescription({ className, render, ...props }) {
const { description } = useCardRecipe();
return useRender({
defaultTagName: "div",
render,
props: mergeProps({
"data-slot": "card-description",
className: description({ className })
}, props)
});
}
function CardTrailing({ className, render, ...props }) {
const { trailing } = useCardRecipe();
return useRender({
defaultTagName: "div",
render,
props: mergeProps({
"data-slot": "card-trailing",
className: trailing({ className })
}, props)
});
}
function CardContent({ className, render, ...props }) {
const { content } = useCardRecipe();
return useRender({
defaultTagName: "div",
render,
props: mergeProps({
"data-slot": "card-content",
className: content({ className })
}, props)
});
}
function CardFooter({ className, render, ...props }) {
const { footer } = useCardRecipe();
return useRender({
defaultTagName: "div",
render,
props: mergeProps({
"data-slot": "card-footer",
className: footer({ className })
}, props)
});
}
//#endregion
//#region src/components/status/status.tsx
const STATUS_STYLE = `
@keyframes raft-status-pulse-background {
0% {
box-shadow: 0 0 0 1px color-mix(in oklch, var(--status-color) 42%, transparent);
}
70%,
100% {
box-shadow: 0 0 0 8px color-mix(in oklch, var(--status-color) 0%, transparent);
}
}
/* Pulse + attention: animate a bottom-layer ring; leave attention outline untouched. */
[data-slot="status"][data-attention][data-pulse]:not([data-pulse="false"])::before {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
pointer-events: none;
z-index: -1;
animation: raft-status-pulse-background 1.4s ease-out infinite;
}
@media (prefers-reduced-motion: reduce) {
[data-slot="status"][data-attention][data-pulse]:not([data-pulse="false"])::before {
animation: none;
}
}
`;
const status = tv({
extend: tv({
slots: { root: ["relative inline-block shrink-0 rounded-full", "bg-(--status-color)"] },
variants: {
theme: {
brutal: { root: "border border-line-strong" },
elegant: {}
},
variant: {
default: { root: "[--status-color:var(--color-gray-400)]" },
primary: { root: "[--status-color:var(--color-primary-400)]" },
information: { root: "[--status-color:var(--color-info)]" },
muted: { root: "[--status-color:var(--color-secondary-400)]" },
accent: { root: "[--status-color:var(--color-accent-400)]" },
success: { root: "[--status-color:var(--color-success)]" },
warning: { root: "[--status-color:var(--color-warning)]" },
danger: { root: "[--status-color:var(--color-danger)]" }
}
},
defaultVariants: { variant: "default" }
}),
variants: {
theme: {
brutal: {},
elegant: {}
},
size: {
xs: { root: "size-1" },
sm: { root: "size-2" },
md: { root: "size-2.5" },
lg: { root: "size-[11px]" }
},
pulse: {
true: { root: "" },
false: { root: "" }
},
attention: {
true: { root: "" },
false: { root: "" }
}
},
defaultVariants: {
size: "md",
variant: "default",
pulse: false,
attention: false
},
compoundVariants: [
{
theme: "brutal",
variant: "success",
class: { root: "[--status-color:var(--color-brutal-lime)]" }
},
{
theme: "brutal",
variant: "warning",
class: { root: "[--status-color:var(--color-warning)]" }
},
{
theme: "brutal",
pulse: true,
class: { root: "animate-pulse" }
},
{
theme: "elegant",
attention: true,
class: { root: [
"isolate overflow-visible",
"border border-[color-mix(in_oklch,var(--status-color)_58%,transparent)]",
"shadow-[inset_0_0.5px_1px_color-mix(in_oklch,oklch(1_0_0)_55%,transparent)]"
] }
},
{
theme: "elegant",
pulse: true,
attention: false,
class: { root: "animate-[raft-status-pulse-background_1.4s_ease-out_infinite] motion-reduce:animate-none" }
}
]
});
const statusStyle = /* @__PURE__ */ jsx("style", {
href: "raft-ui-status",
precedence: "default",
children: STATUS_STYLE
});
function StatusRoot({ size, variant, pulse, attention, className, render, ...props }) {
const theme = useThemeFamily();
const resolvedPulse = pulse ?? false;
const resolvedAttention = attention ?? false;
const { root } = status({
theme,
size,
variant,
pulse: resolvedPulse,
attention: resolvedAttention
});
return useRender({
defaultTagName: "span",
render,
props: mergeProps({
className: root({ className }),
children: statusStyle
}, props),
state: {
slot: "status",
variant,
pulse: resolvedPulse,
attention: resolvedAttention,
size
}
});
}
const Status = StatusRoot;
//#endregion
//#region src/components/app-rail/app-rail.recipe.ts
const appRail = tv({
slots: {
root: ["flex size-full flex-col items-center select-none"],
header: ["relative flex w-full shrink-0 items-center justify-center"],
nav: ["flex w-full flex-1 flex-col items-center gap-1.5 py-2"],
footer: ["flex w-full shrink-0 flex-col items-center justify-center gap-1.5"],
item: [
"relative inline-flex shrink-0 items-center justify-center",
"outline-none",
"disabled:pointer-events-none disabled:opacity-45",
"[&_svg]:size-4.5 [&_svg]:shrink-0"
],
itemIcon: ["relative inline-flex items-center justify-center"],
itemAttentionMask: ["pointer-events-none absolute inset-0", "[&_svg]:size-full [&_svg]:shrink-0"],
itemLabel: ["sr-only"],
itemBadge: ["absolute -right-1 -top-1 inline-flex min-w-4 items-center justify-center", "px-1 text-[10px] font-bold leading-4"],
itemIndicator: ["pointer-events-none absolute -right-1 top-[-2px]"]
},
variants: { theme: {
brutal: {
root: "w-16 border-r-2 border-black bg-primary text-foreground-strong md:[@media(max-height:600px)]:w-[50px]",
header: "h-(--shell-header-height,62px) border-b-2 border-black [@media(max-height:600px)]:h-12",
footer: "h-auto pb-2 [@media(max-height:600px)]:pb-1",
item: [
"size-10 border-2 border-transparent text-foreground-strong",
"transition-colors",
"hover:border-black hover:bg-white",
"data-[selected=true]:border-black data-[selected=true]:bg-white data-[selected=true]:shadow-raft-sm",
"focus-visible:outline-2 focus-visible:outline-solid focus-visible:outline-offset-2 focus-visible:outline-black",
"md:[@media(max-height:600px)]:size-9"
],
itemBadge: "border-2 border-black bg-brutal-pink text-black",
itemAttentionMask: "hidden"
},
elegant: {
root: "w-14 bg-layer-canvas-muted",
header: "h-(--shell-header-height,56px) border-b-0",
nav: ["px-2 pt-4 pb-2"],
footer: ["h-auto gap-1.5 px-2 pb-2", "*:data-[slot=app-rail-item]:text-foreground-muted"],
item: [
"size-auto min-w-9 p-3 rounded-md border border-transparent",
"text-[13px] text-foreground-icon [&_svg]:size-4",
"hover:bg-fill-strong/80 hover:text-foreground-strong",
"active:bg-fill-strong/80 active:text-foreground-strong",
"dark:hover:bg-ink-6 dark:active:bg-ink-6",
"data-[selected=true]:border-transparent data-[selected=true]:bg-fill-strong",
"dark:data-[selected=true]:bg-layer-card",
"data-[selected=true]:text-foreground-muted data-[selected=true]:shadow-none",
"dark:data-[selected=true]:text-foreground-strong dark:data-[selected=true]:shadow-raft-sm",
"focus-visible:ring-[0.5px] focus-visible:ring-primary-500"
],
itemBadge: [
"rounded-full border-0 bg-primary-400 text-primary-950",
"px-1 font-mono text-[10px] font-[450] leading-none tabular-nums normal-case tracking-normal",
"ring-2 ring-layer-canvas-muted"
],
itemAttentionMask: [
"text-[oklch(73.4%_0.149_89.2)] drop-shadow-[0_0_1px_oklch(88.3%_0.162_91.9/0.82)]",
"mask-[radial-gradient(circle_at_96%_12%,black_0%,black_24%,oklch(0_0_0/0.82)_38%,oklch(0_0_0/0.38)_58%,transparent_74%)]",
"[-webkit-mask-image:radial-gradient(circle_at_96%_12%,black_0%,black_24%,oklch(0_0_0/0.82)_38%,oklch(0_0_0/0.38)_58%,transparent_74%)]"
],
itemIndicator: "data-[variant=primary]:bg-[color-mix(in_srgb-linear,var(--color-primary-400)_86%,var(--ink))]"
}
} }
});
//#endregion
//#region src/components/app-rail/app-rail.tsx
function AppRailRoot({ className, render, ...props }) {
const { root } = appRail({ theme: useThemeFamily() });
return useRender({
defaultTagName: "div",
render,
props: mergeProps({
"data-slot": "app-rail-root",
className: root({ className })
}, props)
});
}
function AppRailHeader({ className, render, ...props }) {
const { header } = appRail({ theme: useThemeFamily() });
return useRender({
defaultTagName: "div",
render,
props: mergeProps({
"data-slot": "app-rail-header",
className: header({ className })
}, props)
});
}
function AppRailNav({ className, render, ...props }) {
const { nav } = appRail({ theme: useThemeFamily() });
return useRender({
defaultTagName: "nav",
render,
props: mergeProps({
"data-slot": "app-rail-nav",
className: nav({ className })
}, props)
});
}
function AppRailItem({ nativeButton = true, selected, disabled, className, render, ...props }) {
const { item } = appRail({ theme: useThemeFamily() });
const defaultProps = {
type: nativeButton ? "button" : void 0,
disabled: nativeButton ? disabled : void 0,
"aria-pressed": nativeButton ? selected : void 0,
"data-slot": "app-rail-item",
"data-selected": selected ? "true" : void 0,
"data-disabled": disabled ? "true" : void 0,
className: item({ className })
};
return useRender({
render: render ?? /* @__PURE__ */ jsx(Button$1, {}),
props: mergeProps(defaultProps, props)
});
}
function AppRailItemIcon({ className, render, ...props }) {
const { itemIcon } = appRail();
return useRender({
defaultTagName: "span",
render,
props: mergeProps({
"data-slot": "app-rail-item-icon",
className: itemIcon({ className })
}, props)
});
}
function AppRailItemAttention({ title, children }) {
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
/* @__PURE__ */ jsx(AppRailItemAttentionMask, {
"aria-hidden": true,
children
}),
/* @__PURE__ */ jsx(AppRailItemIndicator, { "aria-hidden": true }),
title ? /* @__PURE__ */ jsx("span", {
className: "sr-only",
children: title
}) : null
] });
}
function AppRailItemAttentionMask({ className, render, ...props }) {
const { itemAttentionMask } = appRail({ theme: useThemeFamily() });
return useRender({
defaultTagName: "span",
render,
props: mergeProps({
"data-slot": "app-rail-item-attention-mask",
className: itemAttentionMask({ className })
}, props)
});
}
function AppRailItemLabel({ className, render, ...props }) {
const { itemLabel } = appRail({ theme: useThemeFamily() });
return useRender({
defaultTagName: "span",
render,
props: mergeProps({
"data-slot": "app-rail-item-label",
className: itemLabel({ className })
}, props)
});
}
function AppRailItemBadge({ className, render, ...props }) {
const { itemBadge } = appRail({ theme: useThemeFamily() });
return useRender({
defaultTagName: "span",
render,
props: mergeProps({
"data-slot": "app-rail-item-badge",
className: itemBadge({ className })
}, props)
});
}
function AppRailItemIndicator({ className, size = "sm", variant, attention = true, ...props }) {
const theme = useThemeFamily();
const { itemIndicator } = appRail({ theme });
return /* @__PURE__ */ jsx(Status, {
"aria-hidden": true,
attention,
size,
variant: variant ?? (theme === "elegant" ? "primary" : "accent"),
"data-slot": "app-rail-item-indicator",
className: itemIndicator({ className }),
...props
});
}
function AppRailFooter({ className, render, ...props }) {
const { footer } = appRail({ theme: useThemeFamily() });
return useRender({
defaultTagName: "div",
render,
props: mergeProps({
"data-slot": "app-rail-footer",
className: footer({ className })
}, props)
});
}
//#endregion
//#region src/components/copyable-code/copyable-code.tsx
const copyableCode = tv({
slots: {
root: ["group/copyable-code flex w-full min-w-0 items-center gap-2"],
code: ["min-w-0 flex-1 font-mono text-xs"],
codeInner: ["block w-full min-w-0 max-w-full break-all"],
action: [
"shrink-0",
"**:data-[slot=button]:transition-[color,background-color,box-shadow]",
"**:data-[slot=button]:duration-150 [&_[data-slot=button]]:ease-out"
],
iconSwap: [
"relative inline-grid size-3",
"**:data-[slot=copyable-code-icon]:col-start-1 **:data-[slot=copyable-code-icon]:row-start-1",
"**:data-[slot=copyable-code-icon]:origin-center",
"**:data-[slot=copyable-code-icon]:transition-[opacity,filter,scale]",
"**:data-[slot=copyable-code-icon]:duration-170",
"**:data-[slot=copyable-code-icon]:ease-[cubic-bezier(0.16,1,0.3,1)]",
"**:data-[slot=copyable-code-icon]:will-change-[opacity,filter,scale]",
"motion-reduce:**:data-[slot=copyable-code-icon]:transition-none",
"data-[state=idle]:**:data-[icon=copy]:scale-100 data-[state=idle]:**:data-[icon=copy]:opacity-100 data-[state=idle]:[&_[data-icon=copy]]:blur-0",
"data-[state=idle]:**:data-[icon=check]:scale-[0.72] data-[state=idle]:**:data-[icon=check]:opacity-0 data-[state=idle]:**:data-[icon=check]:blur-[1px]",
"data-[state=copied]:**:data-[icon=check]:scale-100 data-[state=copied]:**:data-[icon=check]:opacity-100 data-[state=copied]:[&_[data-icon=check]]:blur-0",
"data-[state=copied]:**:data-[icon=copy]:scale-[0.72] data-[state=copied]:**:data-[icon=copy]:opacity-0 data-[state=copied]:**:data-[icon=copy]:blur-[1px]"
]
},
variants: {
theme: {
brutal: {
code: ["border-2 border-line-strong bg-black text-[oklch(0.88_0.18_132)] shadow-raft-sm"],
codeInner: "break-all"
},
elegant: {
code: [
"bg-layer-inset text-[oklch(0.45_0.04_74)]",
"ring-[0.5px] ring-ink-20 dark:ring-ink-10",
"dark:text-[oklch(0.82_0.06_90)]"
],
codeInner: ["border-x border-dashed border-ink-10 dark:border-ink-16 bg-layer-panel px-1", "dark:bg-[oklch(0.15_0.004_106.42)]"],
action: [
"**:data-[slot=button]:text-foreground-muted",
"**:data-[slot=button]:shadow-none",
"**:data-[slot=button]:hover:bg-transparent **:data-[slot=button]:hover:text-foreground **:data-[slot=button]:hover:before:bg-transparent"
]
}
},
size: {
sm: { code: "px-3 py-1.5 leading-5" },
md: { code: "px-3 py-2 leading-5" }
}
},
defaultVariants: { size: "md" },
compoundVariants: [{
theme: "elegant",
size: "sm",
class: {
code: "px-2 py-0",
codeInner: "py-1.5 leading-5"
}
}, {
theme: "elegant",
size: "md",
class: {
code: "px-2 py-0",
codeInner: "py-2 leading-5"
}
}]
});
const CopyableCodeContext = createContext(null);
function useCopyableCode(part) {
const context = useContext(CopyableCodeContext);
if (!context) throw new Error(`${part} must be used inside CopyableCodeRoot.`);
return context;
}
function CopyableCodeRoot({ copied, onCopy, size = "md", className, ...props }) {
const theme = useThemeFamily();
const { root } = copyableCode({
theme,
size
});
const { copied: clipboardCopied, copy } = useClipboard({ timeout: 1400 });
const effectiveCopied = copied ?? clipboardCopied;
const copyValue = useCallback(async (value) => {
await copy(value);
await onCopy?.(value);
}, [copy, onCopy]);
const context = useMemo(() => ({
copied: effectiveCopied,
copyValue,
size
}), [
copyValue,
effectiveCopied,
size
]);
return /* @__PURE__ */ jsx(CopyableCodeContext.Provider, {
value: context,
children: /* @__PURE__ */ jsx("div", {
"data-slot": "copyable-code-root",
"data-size": size,
"data-state": effectiveCopied ? "copied" : "idle",
className: root({ className }),
...props
})
});
}
function CopyableCode({ children, className, truncate, ...props }) {
const { size } = useCopyableCode("CopyableCode");
const theme = useThemeFamily();
const { code, codeInner } = copyableCode({
theme,
size
});
const scrollClassName = "overflow-x-auto overflow-y-hidden break-normal whitespace-nowrap text-clip [scrollbar-width:none] [&::-webkit-scrollbar]:hidden";
return /* @__PURE__ */ jsx("code", {
"data-slot": "copyable-code",
"data-truncate": truncate ? "true" : void 0,
className: code({ className }),
...props,
children: /* @__PURE__ */ jsx("span", {
"data-slot": "copyable-code-inner",
className: codeInner({ className: [truncate && scrollClassName] }),
children
})
});
}
function CopyableCodeAction({ children, buttonClassName, className, onClick, size, variant, ...props }) {
const { copied, copyValue } = useCopyableCode("CopyableCodeAction");
const theme = useThemeFamily();
const { action } = copyableCode({ theme });
const accessibleLabel = copied ? "Copied code" : "Copy code";
const buttonSize = size ?? (theme === "elegant" ? "icon-xs" : "icon-sm");
const buttonVariant = variant ?? (theme === "elegant" ? "ghost" : "outline");
const handleClick = async (event) => {
const value = event.currentTarget.closest("[data-slot=\"copyable-code-root\"]")?.querySelector("[data-slot=\"copyable-code\"]")?.textContent ?? "";
await copyValue(value);
onClick?.(event);
};
return /* @__PURE__ */ jsx("span", {
"data-slot": "copyable-code-action",
className: action({ className }),
children: /* @__PURE__ */ jsx(Button, {
type: "button",
variant: buttonV