@asharca/ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,67 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useId, useMemo, useState } from 'react';
4
+ import { Bot, ChevronRight, MessageSquare, Pencil, Plus, Trash2, } from 'lucide-react';
5
+ import { IconButton, SearchInput } from "./Controls.js";
6
+ import { SidebarActionRail } from "./Sidebar.js";
7
+ export const conversationSidebarDefaultLabels = {
8
+ groups: 'Assistants',
9
+ searchPlaceholder: 'Search assistants and conversations',
10
+ clearSearch: 'Clear search',
11
+ newGroup: 'New assistant',
12
+ newConversation: 'New conversation',
13
+ untitledConversation: 'New conversation',
14
+ noGroups: 'No assistants yet.',
15
+ noConversations: 'No conversations yet.',
16
+ noResults: 'No matching assistants or conversations.',
17
+ renameConversation: 'Rename conversation',
18
+ deleteConversation: 'Delete conversation',
19
+ showConversations: 'Show conversations',
20
+ hideConversations: 'Hide conversations',
21
+ };
22
+ function cx(...classes) {
23
+ return classes.filter(Boolean).join(' ');
24
+ }
25
+ export function ConversationSidebar({ groups, activeGroupId, activeConversationId, labels: labelsOverride, className, onSelectGroup, onSelectConversation, onCreateGroup, onCreateConversation, onRenameConversation, onDeleteConversation, }) {
26
+ const labels = Object.assign(Object.assign({}, conversationSidebarDefaultLabels), labelsOverride);
27
+ const controlsId = useId();
28
+ const [query, setQuery] = useState('');
29
+ const [collapsedGroupIds, setCollapsedGroupIds] = useState(() => new Set());
30
+ const needle = query.trim().toLocaleLowerCase();
31
+ const visibleGroups = useMemo(() => groups.flatMap((group) => {
32
+ if (!needle)
33
+ return [{ group, conversations: group.conversations }];
34
+ if (group.name.toLocaleLowerCase().includes(needle)) {
35
+ return [{ group, conversations: group.conversations }];
36
+ }
37
+ const conversations = group.conversations.filter((conversation) => ((conversation.title || labels.untitledConversation).toLocaleLowerCase().includes(needle)));
38
+ return conversations.length ? [{ group, conversations }] : [];
39
+ }), [groups, labels.untitledConversation, needle]);
40
+ function toggleGroup(groupId) {
41
+ setCollapsedGroupIds((current) => {
42
+ const next = new Set(current);
43
+ if (next.has(groupId))
44
+ next.delete(groupId);
45
+ else
46
+ next.add(groupId);
47
+ return next;
48
+ });
49
+ }
50
+ return (_jsxs("aside", { "aria-label": labels.groups, "data-chat-ui": "conversation-sidebar", className: cx('flex h-full min-h-0 flex-col overflow-hidden bg-background p-1.5 text-foreground', className), children: [_jsx("div", { className: "shrink-0 px-0.5", children: _jsx(SearchInput, { value: query, onChange: (event) => setQuery(event.target.value), onClear: () => setQuery(''), label: labels.searchPlaceholder, clearLabel: labels.clearSearch, placeholder: labels.searchPlaceholder, controlSize: "sm", "data-chat-ui": "sidebar-search", className: "h-7 rounded-full border-0 bg-muted/70 text-[11px] focus:ring-1 focus:ring-brand/35" }) }), _jsxs("div", { className: "mt-2 min-h-0 flex-1 overflow-y-auto", children: [_jsxs("div", { className: "flex h-8 items-center justify-between px-2.5", children: [_jsx("p", { className: "truncate text-xs font-medium text-muted-foreground", children: labels.groups }), onCreateGroup ? (_jsx(IconButton, { icon: _jsx(Plus, { className: "size-3.5" }), label: labels.newGroup, size: "sm", variant: "ghost", onClick: onCreateGroup, className: "min-h-6 w-6 shrink-0 rounded-md text-muted-foreground hover:bg-muted hover:text-foreground" })) : null] }), _jsx("ul", { children: visibleGroups.map(({ group, conversations }, index) => {
51
+ const expanded = Boolean(needle) || !collapsedGroupIds.has(group.id);
52
+ const groupControlsId = `${controlsId}-group-${index}`;
53
+ const selectedGroup = group.id === activeGroupId
54
+ || group.conversations.some((conversation) => conversation.id === activeConversationId);
55
+ const groupIdentity = (_jsxs(_Fragment, { children: [_jsx("span", { className: "flex size-6 shrink-0 items-center justify-center rounded-full bg-background text-muted-foreground", children: _jsx(Bot, { className: "size-3.5" }) }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: group.name })] }));
56
+ return (_jsxs("li", { className: "py-0.5", "data-chat-ui": "sidebar-group", children: [_jsxs("div", { className: cx('group group/sidebar-group flex h-8 min-w-0 items-center gap-1.5 rounded-lg px-1.5 transition-colors', selectedGroup
57
+ ? 'bg-muted text-foreground'
58
+ : 'text-foreground/80 hover:bg-muted/60 hover:text-foreground'), children: [onSelectGroup ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: () => onSelectGroup(group), "aria-current": group.id === activeGroupId ? 'page' : undefined, className: "flex h-8 min-w-0 flex-1 items-center gap-1.5 text-left text-[13px]", children: groupIdentity }), _jsx(IconButton, { icon: _jsx(ChevronRight, { className: cx('size-3.5 transition-transform', expanded && 'rotate-90') }), label: `${expanded ? labels.hideConversations : labels.showConversations}: ${group.name}`, size: "sm", variant: "ghost", "aria-expanded": expanded, "aria-controls": groupControlsId, onClick: () => toggleGroup(group.id), className: "-ml-1.5 hidden min-h-6 w-6 shrink-0 rounded-md text-muted-foreground group-hover:flex group-has-[:focus-visible]:flex group-has-data-[state=open]:flex hover:bg-background hover:text-foreground" })] })) : (_jsxs("button", { type: "button", "aria-expanded": expanded, "aria-controls": groupControlsId, onClick: () => toggleGroup(group.id), className: "flex h-8 min-w-0 flex-1 items-center gap-1.5 text-left text-[13px]", children: [groupIdentity, _jsx("span", { "aria-hidden": "true", className: "-ml-1.5 hidden size-6 shrink-0 items-center justify-center text-muted-foreground group-hover:flex group-has-[:focus-visible]:flex group-has-data-[state=open]:flex", children: _jsx(ChevronRight, { className: cx('size-3.5 transition-transform', expanded && 'rotate-90') }) })] })), onCreateConversation ? (_jsx(SidebarActionRail, { hasLeadingSlot: true, revealOnCellFocus: true, children: _jsx(IconButton, { icon: _jsx(Plus, { className: "size-3.5" }), label: `${labels.newConversation}: ${group.name}`, size: "sm", variant: "ghost", onClick: () => onCreateConversation(group), title: labels.newConversation, className: "min-h-6 w-6 shrink-0 rounded-md text-muted-foreground hover:bg-background hover:text-foreground" }) })) : null] }), expanded ? (_jsx("ul", { id: groupControlsId, className: "ml-4 py-0.5 pl-1", children: conversations.length ? conversations.map((conversation) => {
59
+ const title = conversation.title || labels.untitledConversation;
60
+ const selected = conversation.id === activeConversationId;
61
+ const hasActions = Boolean(onRenameConversation || onDeleteConversation);
62
+ return (_jsx("li", { className: "relative py-0.5", "data-chat-ui": "sidebar-conversation", children: _jsxs("div", { className: cx('group group/sidebar-conversation flex h-8 min-w-0 items-center gap-1.5 rounded-lg px-2 transition-colors', selected
63
+ ? 'bg-muted font-medium text-foreground'
64
+ : 'text-foreground/75 hover:bg-muted/60 hover:text-foreground'), children: [_jsxs("button", { type: "button", disabled: conversation.disabled, onClick: () => onSelectConversation(conversation, group), "aria-current": selected ? 'page' : undefined, title: title, className: "flex h-full min-w-0 flex-1 items-center gap-1.5 text-left text-[13px] disabled:cursor-not-allowed disabled:opacity-50", children: [_jsx(MessageSquare, { className: "size-3 shrink-0 text-muted-foreground" }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: title }), conversation.meta ? (_jsx("span", { "aria-hidden": "true", className: "shrink-0", children: conversation.meta })) : null] }), hasActions ? (_jsxs(SidebarActionRail, { active: conversation.deleting, children: [onRenameConversation ? (_jsx(IconButton, { icon: _jsx(Pencil, { className: "size-3" }), label: `${labels.renameConversation}: ${title}`, size: "sm", variant: "ghost", onClick: () => onRenameConversation(conversation, group), title: labels.renameConversation, className: "min-h-6 w-6 rounded-md text-muted-foreground hover:bg-background hover:text-foreground" })) : null, onDeleteConversation ? (_jsx(IconButton, { icon: _jsx(Trash2, { className: "size-3" }), label: `${labels.deleteConversation}: ${title}`, size: "sm", variant: "ghost", loading: conversation.deleting, disabled: conversation.deleting, onClick: () => onDeleteConversation(conversation, group), title: labels.deleteConversation, className: "min-h-6 w-6 rounded-md text-muted-foreground hover:bg-background hover:text-destructive" })) : null] })) : null] }) }, conversation.id));
65
+ }) : (_jsx("li", { className: "flex h-8 items-center px-2 text-xs text-muted-foreground", children: labels.noConversations })) })) : null] }, group.id));
66
+ }) }), !visibleGroups.length ? (_jsx("p", { className: "px-3 py-8 text-center text-xs text-muted-foreground", children: needle ? labels.noResults : labels.noGroups })) : null] })] }));
67
+ }
@@ -0,0 +1,9 @@
1
+ import { Dialog as DialogPrimitive } from 'radix-ui';
2
+ export declare const Dialog: import("react").FC<DialogPrimitive.DialogProps>;
3
+ export declare const DialogTrigger: import("react").ForwardRefExoticComponent<DialogPrimitive.DialogTriggerProps & import("react").RefAttributes<HTMLButtonElement>>;
4
+ export declare const DialogPortal: import("react").FC<DialogPrimitive.DialogPortalProps>;
5
+ export declare const DialogClose: import("react").ForwardRefExoticComponent<DialogPrimitive.DialogCloseProps & import("react").RefAttributes<HTMLButtonElement>>;
6
+ export declare const DialogOverlay: import("react").ForwardRefExoticComponent<Omit<DialogPrimitive.DialogOverlayProps & import("react").RefAttributes<HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
7
+ export declare const DialogContent: import("react").ForwardRefExoticComponent<Omit<DialogPrimitive.DialogContentProps & import("react").RefAttributes<HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
8
+ export declare const DialogTitle: import("react").ForwardRefExoticComponent<Omit<DialogPrimitive.DialogTitleProps & import("react").RefAttributes<HTMLHeadingElement>, "ref"> & import("react").RefAttributes<HTMLHeadingElement>>;
9
+ export declare const DialogDescription: import("react").ForwardRefExoticComponent<Omit<DialogPrimitive.DialogDescriptionProps & import("react").RefAttributes<HTMLParagraphElement>, "ref"> & import("react").RefAttributes<HTMLParagraphElement>>;
package/dist/Dialog.js ADDED
@@ -0,0 +1,35 @@
1
+ 'use client';
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ import { jsx as _jsx } from "react/jsx-runtime";
14
+ import { forwardRef, } from 'react';
15
+ import { Dialog as DialogPrimitive } from 'radix-ui';
16
+ export const Dialog = DialogPrimitive.Root;
17
+ export const DialogTrigger = DialogPrimitive.Trigger;
18
+ export const DialogPortal = DialogPrimitive.Portal;
19
+ export const DialogClose = DialogPrimitive.Close;
20
+ export const DialogOverlay = forwardRef(function DialogOverlay(_a, ref) {
21
+ var { className } = _a, props = __rest(_a, ["className"]);
22
+ return (_jsx(DialogPrimitive.Overlay, Object.assign({}, props, { ref: ref, "data-toolplane-ui": "dialog-overlay", className: `fixed inset-0 z-50 bg-black/50 ${className !== null && className !== void 0 ? className : ''}`.trim() })));
23
+ });
24
+ export const DialogContent = forwardRef(function DialogContent(_a, ref) {
25
+ var { className } = _a, props = __rest(_a, ["className"]);
26
+ return (_jsx(DialogPrimitive.Content, Object.assign({}, props, { ref: ref, "data-toolplane-ui": "dialog-content", className: `fixed inset-x-4 top-1/2 z-50 grid max-h-[calc(100dvh-2rem)] -translate-y-1/2 gap-4 overflow-y-auto rounded-xl border bg-card p-5 text-card-foreground shadow-xl sm:left-1/2 sm:right-auto sm:w-full sm:max-w-lg sm:-translate-x-1/2 sm:p-6 ${className !== null && className !== void 0 ? className : ''}`.trim() })));
27
+ });
28
+ export const DialogTitle = forwardRef(function DialogTitle(_a, ref) {
29
+ var { className } = _a, props = __rest(_a, ["className"]);
30
+ return (_jsx(DialogPrimitive.Title, Object.assign({}, props, { ref: ref, "data-toolplane-ui": "dialog-title", className: `text-lg font-semibold leading-none tracking-tight ${className !== null && className !== void 0 ? className : ''}`.trim() })));
31
+ });
32
+ export const DialogDescription = forwardRef(function DialogDescription(_a, ref) {
33
+ var { className } = _a, props = __rest(_a, ["className"]);
34
+ return (_jsx(DialogPrimitive.Description, Object.assign({}, props, { ref: ref, "data-toolplane-ui": "dialog-description", className: `text-sm text-muted-foreground ${className !== null && className !== void 0 ? className : ''}`.trim() })));
35
+ });
@@ -0,0 +1,22 @@
1
+ import type { ComponentPropsWithoutRef, ReactNode } from 'react';
2
+ export type FeedbackTone = 'neutral' | 'brand' | 'success' | 'warning' | 'danger';
3
+ export type BadgeProps = ComponentPropsWithoutRef<'span'> & {
4
+ tone?: FeedbackTone;
5
+ };
6
+ export declare function Badge({ className, tone, ...props }: BadgeProps): import("react").JSX.Element;
7
+ export type StatusBadgeProps = Omit<BadgeProps, 'children'> & {
8
+ appearance?: 'badge' | 'plain';
9
+ dot?: boolean;
10
+ dotClassName?: string;
11
+ label: ReactNode;
12
+ };
13
+ export declare function StatusBadge({ appearance, className, dot, dotClassName, label, tone, ...props }: StatusBadgeProps): import("react").JSX.Element;
14
+ export type AlertTone = 'info' | 'success' | 'warning' | 'danger';
15
+ export type AlertProps = ComponentPropsWithoutRef<'div'> & {
16
+ tone?: AlertTone;
17
+ };
18
+ export declare function Alert({ className, role, tone, ...props }: AlertProps): import("react").JSX.Element;
19
+ export type SpinnerProps = ComponentPropsWithoutRef<'span'> & {
20
+ label?: string;
21
+ };
22
+ export declare function Spinner({ className, label, ...props }: SpinnerProps): import("react").JSX.Element;
@@ -0,0 +1,48 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
+ import { Loader2 } from 'lucide-react';
14
+ function cx(...classes) {
15
+ return classes.filter(Boolean).join(' ');
16
+ }
17
+ const badgeTones = {
18
+ neutral: 'bg-muted text-muted-foreground',
19
+ brand: 'bg-brand-soft text-accent-foreground',
20
+ success: 'bg-emerald-500/12 text-emerald-700 dark:text-emerald-300',
21
+ warning: 'bg-amber-500/14 text-amber-700 dark:text-amber-300',
22
+ danger: 'bg-red-500/12 text-red-700 dark:text-red-300',
23
+ };
24
+ export function Badge(_a) {
25
+ var { className, tone = 'neutral' } = _a, props = __rest(_a, ["className", "tone"]);
26
+ return (_jsx("span", Object.assign({ "data-toolplane-ui": "badge", "data-tone": tone }, props, { className: cx('inline-flex min-h-5 items-center gap-1.5 rounded px-2 py-0.5 text-[11px] font-semibold', badgeTones[tone], className) })));
27
+ }
28
+ export function StatusBadge(_a) {
29
+ var { appearance = 'badge', className, dot = true, dotClassName, label, tone = 'neutral' } = _a, props = __rest(_a, ["appearance", "className", "dot", "dotClassName", "label", "tone"]);
30
+ if (appearance === 'plain') {
31
+ return (_jsxs("span", Object.assign({}, props, { "data-toolplane-ui": "status-badge", "data-tone": tone, className: cx('inline-flex items-center gap-2 text-sm text-muted-foreground', className), children: [dot ? _jsx("span", { "aria-hidden": "true", className: cx('size-2 rounded-full bg-current', dotClassName) }) : null, label] })));
32
+ }
33
+ return (_jsxs(Badge, Object.assign({}, props, { tone: tone, className: className, "data-toolplane-ui": "status-badge", children: [dot ? _jsx("span", { "aria-hidden": "true", className: cx('size-1.5 rounded-full bg-current', dotClassName) }) : null, label] })));
34
+ }
35
+ const alertTones = {
36
+ info: 'border-sky-200 bg-sky-50 text-sky-800 dark:border-sky-500/30 dark:bg-sky-500/10 dark:text-sky-200',
37
+ success: 'border-emerald-200 bg-emerald-50 text-emerald-800 dark:border-emerald-500/30 dark:bg-emerald-500/10 dark:text-emerald-200',
38
+ warning: 'border-amber-200 bg-amber-50 text-amber-800 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-200',
39
+ danger: 'border-red-200 bg-red-50 text-red-800 dark:border-red-500/30 dark:bg-red-500/10 dark:text-red-200',
40
+ };
41
+ export function Alert(_a) {
42
+ var { className, role, tone = 'info' } = _a, props = __rest(_a, ["className", "role", "tone"]);
43
+ return (_jsx("div", Object.assign({}, props, { "data-toolplane-ui": "alert", "data-tone": tone, role: role !== null && role !== void 0 ? role : (tone === 'danger' ? 'alert' : 'status'), className: cx('rounded-md border px-4 py-3 text-sm', alertTones[tone], className) })));
44
+ }
45
+ export function Spinner(_a) {
46
+ var { className, label } = _a, props = __rest(_a, ["className", "label"]);
47
+ return (_jsxs("span", Object.assign({}, props, { "data-toolplane-ui": "spinner", role: label ? 'status' : undefined, "aria-hidden": label ? undefined : true, className: cx('inline-flex items-center gap-2', className), children: [_jsx(Loader2, { "aria-hidden": "true", className: "size-4 animate-spin" }), label ? _jsx("span", { className: "sr-only", children: label }) : null] })));
48
+ }
@@ -0,0 +1,38 @@
1
+ import { type ReactNode } from 'react';
2
+ export type SubmitButtonProps = {
3
+ children?: ReactNode;
4
+ className?: string;
5
+ pendingLabel?: string;
6
+ savedLabel?: string;
7
+ flash?: boolean;
8
+ error?: string | boolean | null;
9
+ disabled?: boolean;
10
+ ariaLabel?: string;
11
+ title?: string;
12
+ };
13
+ export declare function SubmitButton({ children, className, pendingLabel, savedLabel, flash, error, disabled, ariaLabel, title, }: SubmitButtonProps): import("react").JSX.Element;
14
+ export type ConfirmSubmitButtonProps = {
15
+ triggerLabel: ReactNode;
16
+ triggerAriaLabel?: string;
17
+ triggerTitle?: string;
18
+ confirmLabel: ReactNode;
19
+ cancelLabel: ReactNode;
20
+ prompt: ReactNode;
21
+ pendingLabel?: ReactNode;
22
+ disabled?: boolean;
23
+ className?: string;
24
+ triggerClassName?: string;
25
+ confirmClassName?: string;
26
+ cancelClassName?: string;
27
+ promptClassName?: string;
28
+ };
29
+ export declare function ConfirmSubmitButton({ triggerLabel, triggerAriaLabel, triggerTitle, confirmLabel, cancelLabel, prompt, pendingLabel, disabled, className, triggerClassName, confirmClassName, cancelClassName, promptClassName, }: ConfirmSubmitButtonProps): import("react").JSX.Element;
30
+ export type CopyButtonProps = {
31
+ text: string;
32
+ label?: string;
33
+ copiedLabel?: string;
34
+ failedLabel?: string;
35
+ className?: string;
36
+ iconOnly?: boolean;
37
+ };
38
+ export declare function CopyButton({ text, label, copiedLabel, failedLabel, className, iconOnly, }: CopyButtonProps): import("react").JSX.Element;
package/dist/Forms.js ADDED
@@ -0,0 +1,99 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useEffect, useRef, useState, } from 'react';
4
+ import { useFormStatus } from 'react-dom';
5
+ import { Check, Copy, Loader2 } from 'lucide-react';
6
+ export function SubmitButton({ children = 'Save', className, pendingLabel = 'Saving…', savedLabel = 'Saved', flash = true, error, disabled = false, ariaLabel, title, }) {
7
+ const { pending } = useFormStatus();
8
+ const [justSaved, setJustSaved] = useState(false);
9
+ const wasPending = useRef(pending);
10
+ useEffect(() => {
11
+ if (flash && wasPending.current && !pending && !error) {
12
+ setJustSaved(true);
13
+ const timeout = setTimeout(() => setJustSaved(false), 1600);
14
+ wasPending.current = pending;
15
+ return () => clearTimeout(timeout);
16
+ }
17
+ wasPending.current = pending;
18
+ }, [pending, error, flash]);
19
+ return (_jsx("button", { type: "submit", disabled: pending || disabled, "aria-busy": pending, "aria-disabled": disabled || undefined, "aria-label": ariaLabel, title: title, "data-toolplane-ui": "submit-button", className: `${className !== null && className !== void 0 ? className : ''} disabled:opacity-70 ${pending ? 'cursor-wait' : 'disabled:cursor-not-allowed'}`, children: _jsxs("span", { className: "inline-flex items-center gap-1.5", children: [pending ? (_jsx(Loader2, { "aria-hidden": "true", className: "size-3.5 animate-spin" })) : justSaved ? (_jsx(Check, { "aria-hidden": "true", className: "size-3.5" })) : null, pending ? pendingLabel : justSaved ? savedLabel : children] }) }));
20
+ }
21
+ export function ConfirmSubmitButton({ triggerLabel, triggerAriaLabel, triggerTitle, confirmLabel, cancelLabel, prompt, pendingLabel, disabled = false, className = 'items-center', triggerClassName = 'ui-button-secondary', confirmClassName = 'ui-button-primary', cancelClassName = 'ui-button-ghost', promptClassName = 'text-sm text-muted-foreground', }) {
22
+ const [confirming, setConfirming] = useState(false);
23
+ const { pending } = useFormStatus();
24
+ const triggerRef = useRef(null);
25
+ const confirmRef = useRef(null);
26
+ const restoreTriggerFocus = useRef(false);
27
+ const wasPending = useRef(false);
28
+ useEffect(() => {
29
+ var _a, _b;
30
+ if (confirming) {
31
+ (_a = confirmRef.current) === null || _a === void 0 ? void 0 : _a.focus();
32
+ }
33
+ else if (restoreTriggerFocus.current) {
34
+ restoreTriggerFocus.current = false;
35
+ (_b = triggerRef.current) === null || _b === void 0 ? void 0 : _b.focus();
36
+ }
37
+ }, [confirming]);
38
+ useEffect(() => {
39
+ if (pending) {
40
+ wasPending.current = true;
41
+ return;
42
+ }
43
+ if (!wasPending.current)
44
+ return;
45
+ wasPending.current = false;
46
+ restoreTriggerFocus.current = true;
47
+ setConfirming(false);
48
+ }, [pending]);
49
+ function cancelConfirmation() {
50
+ restoreTriggerFocus.current = true;
51
+ setConfirming(false);
52
+ }
53
+ return (_jsx("span", { "data-toolplane-ui": "confirm-submit-button", className: `inline-flex flex-wrap gap-2 ${className}`, children: confirming ? (_jsxs(_Fragment, { children: [_jsx("span", { className: promptClassName, children: prompt }), _jsx("button", { ref: confirmRef, type: "submit", disabled: disabled || pending, "aria-busy": pending, "data-toolplane-ui": "confirm-submit-confirm", className: `${confirmClassName} disabled:cursor-wait disabled:opacity-70`, children: pending ? pendingLabel !== null && pendingLabel !== void 0 ? pendingLabel : confirmLabel : confirmLabel }), _jsx("button", { type: "button", disabled: disabled || pending, onClick: cancelConfirmation, "data-toolplane-ui": "confirm-submit-cancel", className: `${cancelClassName} disabled:cursor-wait disabled:opacity-70`, children: cancelLabel })] })) : (_jsx("button", { ref: triggerRef, type: "button", disabled: disabled || pending, onClick: () => setConfirming(true), "aria-label": triggerAriaLabel, title: triggerTitle, "data-toolplane-ui": "confirm-submit-trigger", className: `${triggerClassName} disabled:cursor-not-allowed disabled:opacity-60`, children: triggerLabel })) }));
54
+ }
55
+ export function CopyButton({ text, label = 'Copy', copiedLabel = 'Copied', failedLabel = 'Copy failed', className, iconOnly = false, }) {
56
+ const [status, setStatus] = useState('idle');
57
+ const resetTimeout = useRef(null);
58
+ const statusLabel = status === 'copied'
59
+ ? copiedLabel
60
+ : status === 'failed'
61
+ ? failedLabel
62
+ : label;
63
+ useEffect(() => () => {
64
+ if (resetTimeout.current)
65
+ clearTimeout(resetTimeout.current);
66
+ }, []);
67
+ async function copy() {
68
+ let success = false;
69
+ try {
70
+ await Promise.race([
71
+ navigator.clipboard.writeText(text),
72
+ new Promise((_, reject) => {
73
+ setTimeout(() => reject(new Error('Clipboard write timed out.')), 500);
74
+ }),
75
+ ]);
76
+ success = true;
77
+ }
78
+ catch (_a) {
79
+ const textarea = document.createElement('textarea');
80
+ textarea.value = text;
81
+ textarea.readOnly = true;
82
+ textarea.style.position = 'fixed';
83
+ textarea.style.left = '-9999px';
84
+ textarea.style.top = '0';
85
+ textarea.style.opacity = '0';
86
+ document.body.appendChild(textarea);
87
+ textarea.focus();
88
+ textarea.select();
89
+ textarea.setSelectionRange(0, textarea.value.length);
90
+ success = document.execCommand('copy');
91
+ textarea.remove();
92
+ }
93
+ setStatus(success ? 'copied' : 'failed');
94
+ if (resetTimeout.current)
95
+ clearTimeout(resetTimeout.current);
96
+ resetTimeout.current = setTimeout(() => setStatus('idle'), 1500);
97
+ }
98
+ return (_jsxs("button", { type: "button", onClick: copy, "aria-label": iconOnly ? statusLabel : undefined, title: iconOnly ? statusLabel : undefined, "data-toolplane-ui": "copy-button", className: className !== null && className !== void 0 ? className : 'inline-flex h-9 items-center gap-1.5 rounded-md border border-zinc-200 px-3 text-sm font-medium text-zinc-700 transition-colors hover:bg-zinc-100 dark:border-zinc-700 dark:text-zinc-200 dark:hover:bg-zinc-800', children: [status === 'copied' ? (_jsx(Check, { "aria-hidden": "true", className: "size-4" })) : (_jsx(Copy, { "aria-hidden": "true", className: "size-4" })), iconOnly ? _jsx("span", { className: "sr-only", "aria-live": "polite", children: statusLabel }) : statusLabel] }));
99
+ }
@@ -0,0 +1,73 @@
1
+ import type { ComponentPropsWithoutRef, ComponentType, HTMLAttributes, ReactNode } from 'react';
2
+ export type PageProps = ComponentPropsWithoutRef<'main'> & {
3
+ as?: 'div' | 'main';
4
+ };
5
+ export declare function Page({ as: Component, className, ...props }: PageProps): import("react").JSX.Element;
6
+ export type PageHeaderProps = Omit<ComponentPropsWithoutRef<'header'>, 'title'> & {
7
+ actions?: ReactNode;
8
+ back?: ReactNode;
9
+ description?: ReactNode;
10
+ meta?: ReactNode;
11
+ title: ReactNode;
12
+ };
13
+ export declare function PageHeader({ actions, back, className, description, meta, title, ...props }: PageHeaderProps): import("react").JSX.Element;
14
+ export type ToolbarProps = ComponentPropsWithoutRef<'div'> & {
15
+ actions?: ReactNode;
16
+ };
17
+ export declare function Toolbar({ actions, children, className, ...props }: ToolbarProps): import("react").JSX.Element;
18
+ export type SectionProps = Omit<ComponentPropsWithoutRef<'section'>, 'title'> & {
19
+ actions?: ReactNode;
20
+ count?: number;
21
+ title: ReactNode;
22
+ };
23
+ export declare function Section({ actions, children, className, count, title, ...props }: SectionProps): import("react").JSX.Element;
24
+ export type PanelTone = 'default' | 'danger';
25
+ export type PanelHeaderPresentation = 'muted' | 'bordered';
26
+ export type PanelProps = Omit<ComponentPropsWithoutRef<'section'>, 'title'> & {
27
+ actions?: ReactNode;
28
+ bodyClassName?: string;
29
+ description?: ReactNode;
30
+ headerPresentation?: PanelHeaderPresentation;
31
+ padded?: boolean;
32
+ title: ReactNode;
33
+ tone?: PanelTone;
34
+ };
35
+ export declare function Panel({ actions, bodyClassName, children, className, description, headerPresentation, padded, title, tone, ...props }: PanelProps): import("react").JSX.Element;
36
+ type Icon = ComponentType<{
37
+ className?: string;
38
+ }>;
39
+ export type EmptyStateProps = Omit<ComponentPropsWithoutRef<'div'>, 'title'> & {
40
+ actions?: ReactNode;
41
+ description?: ReactNode;
42
+ icon?: Icon;
43
+ title?: ReactNode;
44
+ };
45
+ export declare function EmptyState({ actions, children, className, description, icon: IconComponent, title, ...props }: EmptyStateProps): import("react").JSX.Element;
46
+ export type DataTableHeader = {
47
+ align?: 'left' | 'right';
48
+ className?: string;
49
+ colSpan?: number;
50
+ label?: ReactNode;
51
+ };
52
+ export type DataTableProps = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
53
+ children: ReactNode;
54
+ headers: readonly DataTableHeader[];
55
+ label?: string;
56
+ minWidth?: string;
57
+ panel?: boolean;
58
+ tableClassName?: string;
59
+ };
60
+ export declare function DataTable({ children, className, headers, label, minWidth, panel, tableClassName, ...props }: DataTableProps): import("react").JSX.Element;
61
+ export type EntityProps = Omit<ComponentPropsWithoutRef<'div'>, 'title'> & {
62
+ description?: ReactNode;
63
+ initials?: string;
64
+ mono?: boolean;
65
+ title: ReactNode;
66
+ };
67
+ export declare function Entity({ className, description, initials, mono, title, ...props }: EntityProps): import("react").JSX.Element;
68
+ export type CardProps = ComponentPropsWithoutRef<'div'> & {
69
+ muted?: boolean;
70
+ padded?: boolean;
71
+ };
72
+ export declare function Card({ className, muted, padded, ...props }: CardProps): import("react").JSX.Element;
73
+ export {};
package/dist/Layout.js ADDED
@@ -0,0 +1,53 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
+ function cx(...classes) {
14
+ return classes.filter(Boolean).join(' ');
15
+ }
16
+ export function Page(_a) {
17
+ var { as: Component = 'main', className } = _a, props = __rest(_a, ["as", "className"]);
18
+ return (_jsx(Component, Object.assign({}, props, { "data-toolplane-ui": "page", className: cx('ui-page space-y-5', className) })));
19
+ }
20
+ export function PageHeader(_a) {
21
+ var { actions, back, className, description, meta, title } = _a, props = __rest(_a, ["actions", "back", "className", "description", "meta", "title"]);
22
+ return (_jsxs("header", Object.assign({}, props, { "data-toolplane-ui": "page-header", className: cx('space-y-3', className), children: [back, _jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2 gap-y-1", children: [_jsx("h1", { className: "text-2xl font-bold text-foreground [text-wrap:balance]", children: title }), meta ? _jsx("span", { className: "text-sm font-medium text-muted-foreground", children: meta }) : null] }), description ? (_jsx("div", { className: "mt-1 max-w-3xl text-sm text-muted-foreground [text-wrap:pretty]", children: description })) : null] }), actions ? _jsx("div", { className: "flex shrink-0 flex-wrap items-center gap-2", children: actions }) : null] })] })));
23
+ }
24
+ export function Toolbar(_a) {
25
+ var { actions, children, className } = _a, props = __rest(_a, ["actions", "children", "className"]);
26
+ return (_jsxs("div", Object.assign({}, props, { "data-toolplane-ui": "toolbar", className: cx('flex flex-wrap items-center justify-between gap-3', className), children: [children ? _jsx("div", { className: "min-w-0", children: children }) : _jsx("span", {}), actions ? _jsx("div", { className: "flex flex-wrap items-center gap-2", children: actions }) : null] })));
27
+ }
28
+ export function Section(_a) {
29
+ var { actions, children, className, count, title } = _a, props = __rest(_a, ["actions", "children", "className", "count", "title"]);
30
+ return (_jsxs("section", Object.assign({}, props, { "data-toolplane-ui": "section", className: className, children: [_jsxs("div", { className: "mb-3 flex items-center justify-between gap-3", children: [_jsxs("h2", { className: "text-sm font-semibold text-foreground", children: [title, typeof count === 'number' ? (_jsxs("span", { className: "ml-1.5 font-normal text-muted-foreground", children: ["(", count, ")"] })) : null] }), actions ? _jsx("div", { className: "flex items-center gap-2", children: actions }) : null] }), children] })));
31
+ }
32
+ export function Panel(_a) {
33
+ var { actions, bodyClassName, children, className, description, headerPresentation = 'muted', padded = true, title, tone = 'default' } = _a, props = __rest(_a, ["actions", "bodyClassName", "children", "className", "description", "headerPresentation", "padded", "title", "tone"]);
34
+ const danger = tone === 'danger';
35
+ const bordered = headerPresentation === 'bordered';
36
+ return (_jsxs("section", Object.assign({}, props, { "data-toolplane-ui": "panel", "data-tone": tone, className: cx('ui-panel overflow-hidden', danger && 'ui-panel-danger', className), children: [_jsxs("div", { className: cx('flex items-center justify-between gap-3 px-5 py-3.5', bordered && 'min-h-14 border-b', bordered && (danger ? 'border-red-100 dark:border-red-500/20' : 'border-border'), !bordered && (danger ? 'bg-red-500/5' : 'bg-muted/25')), children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h2", { className: cx('text-sm font-semibold', danger ? 'text-red-700 dark:text-red-400' : 'text-foreground'), children: title }), description ? _jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: description }) : null] }), actions ? _jsx("div", { className: "shrink-0", children: actions }) : null] }), _jsx("div", { className: cx(padded && 'px-5 py-5', bodyClassName), children: children })] })));
37
+ }
38
+ export function EmptyState(_a) {
39
+ var { actions, children, className, description, icon: IconComponent, title } = _a, props = __rest(_a, ["actions", "children", "className", "description", "icon", "title"]);
40
+ return (_jsxs("div", Object.assign({}, props, { "data-toolplane-ui": "empty-state", className: cx('ui-empty', className), children: [IconComponent ? _jsx(IconComponent, { className: "mb-3 size-8 text-muted-foreground" }) : null, title ? _jsx("h2", { className: "text-lg font-semibold text-foreground", children: title }) : null, description ? (_jsx("div", { className: cx('text-sm text-muted-foreground', Boolean(title) && 'mt-1'), children: description })) : null, children ? _jsx("div", { className: "mt-6 w-full", children: children }) : null, actions ? _jsx("div", { className: "mt-5 flex flex-wrap items-center justify-center gap-2", children: actions }) : null] })));
41
+ }
42
+ export function DataTable(_a) {
43
+ var { children, className, headers, label, minWidth = '40rem', panel = true, tableClassName } = _a, props = __rest(_a, ["children", "className", "headers", "label", "minWidth", "panel", "tableClassName"]);
44
+ return (_jsx("div", Object.assign({}, props, { "data-toolplane-ui": "data-table", role: label ? 'region' : undefined, "aria-label": label, tabIndex: label ? 0 : undefined, className: cx(panel && 'ui-panel', 'relative overflow-x-auto overscroll-x-contain focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring', className), children: _jsxs("table", { className: cx('ui-table', tableClassName), style: { minWidth }, children: [_jsx("thead", { children: _jsx("tr", { children: headers.map((header, index) => (_jsx("th", { scope: "col", colSpan: header.colSpan, className: cx('px-4 py-3 font-medium', header.align === 'right' && 'text-right', header.className), children: header.label }, index))) }) }), _jsx("tbody", { className: "divide-y divide-border", children: children })] }) })));
45
+ }
46
+ export function Entity(_a) {
47
+ var { className, description, initials, mono = false, title } = _a, props = __rest(_a, ["className", "description", "initials", "mono", "title"]);
48
+ return (_jsxs("div", Object.assign({}, props, { "data-toolplane-ui": "entity", className: cx('flex min-w-0 items-center gap-3', className), children: [initials ? (_jsx("span", { className: "grid size-8 shrink-0 place-items-center rounded-md border border-border bg-muted/45 text-[11px] font-bold text-muted-foreground", children: initials.slice(0, 2).toUpperCase() })) : null, _jsxs("span", { className: "min-w-0", children: [_jsx("span", { className: cx('block truncate font-semibold text-foreground', mono && 'font-mono text-[13px]'), children: title }), description ? _jsx("span", { className: "block truncate text-xs text-muted-foreground", children: description }) : null] })] })));
49
+ }
50
+ export function Card(_a) {
51
+ var { className, muted = false, padded = true } = _a, props = __rest(_a, ["className", "muted", "padded"]);
52
+ return (_jsx("div", Object.assign({}, props, { "data-toolplane-ui": "card", className: cx(muted ? 'ui-panel-muted' : 'ui-panel', padded && 'p-5', className) })));
53
+ }
@@ -0,0 +1 @@
1
+ export default function MermaidAssistantText(): import("react").JSX.Element;
@@ -0,0 +1,15 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { StreamdownTextPrimitive } from '@assistant-ui/react-streamdown';
4
+ import { code } from '@streamdown/code';
5
+ import { mermaid } from '@streamdown/mermaid';
6
+ import remarkBreaks from 'remark-breaks';
7
+ import { defaultRemarkPlugins } from 'streamdown';
8
+ const plugins = { code, mermaid };
9
+ const remarkPlugins = [...Object.values(defaultRemarkPlugins), remarkBreaks];
10
+ export default function MermaidAssistantText() {
11
+ return (_jsx(StreamdownTextPrimitive, { plugins: plugins, remarkPlugins: remarkPlugins, mermaid: { config: { securityLevel: 'strict' } }, linkSafety: { enabled: true }, security: {
12
+ allowedProtocols: ['http', 'https', 'mailto'],
13
+ allowDataImages: false,
14
+ }, className: "space-y-2 [&_li]:my-0.5 [&_ol]:my-1 [&_ol]:list-decimal [&_ol]:pl-5 [&_p]:my-1 [&_pre]:my-2 [&_ul]:my-1 [&_ul]:list-disc [&_ul]:pl-5" }));
15
+ }
@@ -0,0 +1,39 @@
1
+ import type { ButtonHTMLAttributes, ComponentPropsWithoutRef, ReactNode } from 'react';
2
+ export type ChipProps = ComponentPropsWithoutRef<'span'> & {
3
+ active?: boolean;
4
+ asChild?: boolean;
5
+ };
6
+ export declare function Chip({ active, asChild, className, ...props }: ChipProps): import("react").JSX.Element;
7
+ export type TabListProps = ComponentPropsWithoutRef<'div'> & {
8
+ label: string;
9
+ navigation?: boolean;
10
+ };
11
+ export declare function TabList({ className, label, navigation, ...props }: TabListProps): import("react").JSX.Element;
12
+ export type NavigationTabsProps = ComponentPropsWithoutRef<'nav'> & {
13
+ contentClassName?: string;
14
+ };
15
+ export declare function NavigationTabs({ children, className, contentClassName, ...props }: NavigationTabsProps): import("react").JSX.Element;
16
+ export type BreadcrumbsProps = ComponentPropsWithoutRef<'nav'>;
17
+ export declare function Breadcrumbs({ 'aria-label': ariaLabel, children, className, ...props }: BreadcrumbsProps): import("react").JSX.Element;
18
+ export type BreadcrumbItemProps = ComponentPropsWithoutRef<'li'> & {
19
+ current?: boolean;
20
+ separator?: ReactNode;
21
+ };
22
+ export declare function BreadcrumbItem({ children, className, current, separator, ...props }: BreadcrumbItemProps): import("react").JSX.Element;
23
+ export type TabProps = ButtonHTMLAttributes<HTMLButtonElement> & {
24
+ asChild?: boolean;
25
+ count?: number;
26
+ current?: boolean;
27
+ navigation?: boolean;
28
+ };
29
+ export declare function Tab({ asChild, children, className, count, current, navigation, type, ...props }: TabProps): import("react").JSX.Element;
30
+ export type TabPanelProps = ComponentPropsWithoutRef<'div'> & {
31
+ current?: boolean;
32
+ };
33
+ export declare function TabPanel({ className, current, ...props }: TabPanelProps): import("react").JSX.Element;
34
+ export type PaginationProps = Omit<ComponentPropsWithoutRef<'nav'>, 'children'> & {
35
+ next?: ReactNode;
36
+ previous?: ReactNode;
37
+ summary: ReactNode;
38
+ };
39
+ export declare function Pagination({ className, next, previous, summary, ...props }: PaginationProps): import("react").JSX.Element;
@@ -0,0 +1,60 @@
1
+ 'use client';
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
+ import { Slot } from 'radix-ui';
15
+ function cx(...classes) {
16
+ return classes.filter(Boolean).join(' ');
17
+ }
18
+ export function Chip(_a) {
19
+ var { active = false, asChild = false, className } = _a, props = __rest(_a, ["active", "asChild", "className"]);
20
+ const Component = asChild ? Slot.Root : 'span';
21
+ return (_jsx(Component, Object.assign({}, props, { "data-toolplane-ui": "chip", "data-active": active || undefined, className: cx('ui-chip', active && 'ui-chip-active', className) })));
22
+ }
23
+ export function TabList(_a) {
24
+ var { className, label, navigation = false } = _a, props = __rest(_a, ["className", "label", "navigation"]);
25
+ const classes = cx('inline-flex max-w-full items-center gap-1 overflow-x-auto rounded-xl bg-muted p-1 ring-1 ring-border/70', className);
26
+ if (navigation) {
27
+ return (_jsx("nav", Object.assign({}, props, { "aria-label": label, "data-toolplane-ui": "tab-list", className: classes })));
28
+ }
29
+ return (_jsx("div", Object.assign({}, props, { "data-toolplane-ui": "tab-list", role: "tablist", "aria-label": label, className: classes })));
30
+ }
31
+ export function NavigationTabs(_a) {
32
+ var { children, className, contentClassName } = _a, props = __rest(_a, ["children", "className", "contentClassName"]);
33
+ return (_jsx("nav", Object.assign({}, props, { "data-toolplane-ui": "navigation-tabs", className: cx('max-w-full overflow-x-auto pb-1', className), children: _jsx("div", { className: cx('inline-flex min-w-max items-center gap-1 rounded-xl bg-muted p-1 ring-1 ring-border/70', contentClassName), children: children }) })));
34
+ }
35
+ export function Breadcrumbs(_a) {
36
+ var { 'aria-label': ariaLabel = 'Breadcrumb', children, className } = _a, props = __rest(_a, ['aria-label', "children", "className"]);
37
+ return (_jsx("nav", Object.assign({}, props, { "aria-label": ariaLabel, "data-toolplane-ui": "breadcrumbs", className: cx('min-w-0 overflow-hidden', className), children: _jsx("ol", { className: "flex min-w-0 items-center gap-2 overflow-hidden", children: children }) })));
38
+ }
39
+ export function BreadcrumbItem(_a) {
40
+ var { children, className, current = false, separator } = _a, props = __rest(_a, ["children", "className", "current", "separator"]);
41
+ return (_jsxs("li", Object.assign({}, props, { className: cx('flex min-w-0 items-center gap-2', className), children: [separator ? _jsx("span", { "aria-hidden": "true", className: "shrink-0 text-muted-foreground/55", children: separator }) : null, _jsx("span", { "aria-current": current ? 'page' : undefined, className: cx('truncate', current ? 'text-muted-foreground' : 'font-semibold text-foreground'), children: children })] })));
42
+ }
43
+ export function Tab(_a) {
44
+ var { asChild = false, children, className, count, current = false, navigation = false, type = 'button' } = _a, props = __rest(_a, ["asChild", "children", "className", "count", "current", "navigation", "type"]);
45
+ const classes = cx('inline-flex shrink-0 items-center gap-1.5 rounded-lg px-4 py-1.5 text-sm font-medium transition-colors', current
46
+ ? 'bg-background text-foreground shadow-sm ring-1 ring-border/60'
47
+ : 'text-muted-foreground hover:bg-accent/60 hover:text-foreground', className);
48
+ if (asChild) {
49
+ return (_jsx(Slot.Root, Object.assign({}, props, { "data-toolplane-ui": "tab", "data-current": current || undefined, role: navigation ? undefined : 'tab', "aria-current": navigation && current ? 'page' : undefined, "aria-selected": navigation ? undefined : current, className: classes, children: children })));
50
+ }
51
+ return (_jsxs("button", Object.assign({}, props, { type: type, "data-toolplane-ui": "tab", "data-current": current || undefined, role: navigation ? undefined : 'tab', "aria-current": navigation && current ? 'page' : undefined, "aria-selected": navigation ? undefined : current, className: classes, children: [children, typeof count === 'number' ? (_jsx("span", { className: "text-muted-foreground/70", children: count })) : null] })));
52
+ }
53
+ export function TabPanel(_a) {
54
+ var { className, current = false } = _a, props = __rest(_a, ["className", "current"]);
55
+ return (_jsx("div", Object.assign({}, props, { "data-toolplane-ui": "tab-panel", role: "tabpanel", hidden: !current, className: className })));
56
+ }
57
+ export function Pagination(_a) {
58
+ var { className, next, previous, summary } = _a, props = __rest(_a, ["className", "next", "previous", "summary"]);
59
+ return (_jsxs("nav", Object.assign({}, props, { "data-toolplane-ui": "pagination", className: cx('flex flex-col gap-3 text-sm text-muted-foreground sm:flex-row sm:items-center sm:justify-between', className), children: [_jsx("span", { children: summary }), _jsxs("div", { className: "flex gap-2", children: [previous, next] })] })));
60
+ }