@prmichaelsen/agentbase-ux 0.0.2

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.
Files changed (61) hide show
  1. package/README.md +62 -0
  2. package/dist/__tests__/setup.d.ts +2 -0
  3. package/dist/__tests__/setup.d.ts.map +1 -0
  4. package/dist/__tests__/test-utils.d.ts +6 -0
  5. package/dist/__tests__/test-utils.d.ts.map +1 -0
  6. package/dist/components/Button.d.ts +19 -0
  7. package/dist/components/Button.d.ts.map +1 -0
  8. package/dist/components/Button.test.d.ts +2 -0
  9. package/dist/components/Button.test.d.ts.map +1 -0
  10. package/dist/components/ConfirmationModal.d.ts +17 -0
  11. package/dist/components/ConfirmationModal.d.ts.map +1 -0
  12. package/dist/components/ConfirmationModal.test.d.ts +2 -0
  13. package/dist/components/ConfirmationModal.test.d.ts.map +1 -0
  14. package/dist/components/MenuItem.d.ts +17 -0
  15. package/dist/components/MenuItem.d.ts.map +1 -0
  16. package/dist/components/MenuItem.test.d.ts +2 -0
  17. package/dist/components/MenuItem.test.d.ts.map +1 -0
  18. package/dist/components/Modal.d.ts +18 -0
  19. package/dist/components/Modal.d.ts.map +1 -0
  20. package/dist/components/Modal.test.d.ts +2 -0
  21. package/dist/components/Modal.test.d.ts.map +1 -0
  22. package/dist/components/Paginator.d.ts +14 -0
  23. package/dist/components/Paginator.d.ts.map +1 -0
  24. package/dist/components/Paginator.test.d.ts +2 -0
  25. package/dist/components/Paginator.test.d.ts.map +1 -0
  26. package/dist/components/Popover.d.ts +18 -0
  27. package/dist/components/Popover.d.ts.map +1 -0
  28. package/dist/components/SlideOverPanel.d.ts +12 -0
  29. package/dist/components/SlideOverPanel.d.ts.map +1 -0
  30. package/dist/components/SlideOverPanel.test.d.ts +2 -0
  31. package/dist/components/SlideOverPanel.test.d.ts.map +1 -0
  32. package/dist/components/Slider.d.ts +36 -0
  33. package/dist/components/Slider.d.ts.map +1 -0
  34. package/dist/components/Slider.test.d.ts +2 -0
  35. package/dist/components/Slider.test.d.ts.map +1 -0
  36. package/dist/components/ToggleSwitch.d.ts +17 -0
  37. package/dist/components/ToggleSwitch.d.ts.map +1 -0
  38. package/dist/components/ToggleSwitch.test.d.ts +2 -0
  39. package/dist/components/ToggleSwitch.test.d.ts.map +1 -0
  40. package/dist/hooks/useDebounce.d.ts +2 -0
  41. package/dist/hooks/useDebounce.d.ts.map +1 -0
  42. package/dist/hooks/useDebounce.test.d.ts +2 -0
  43. package/dist/hooks/useDebounce.test.d.ts.map +1 -0
  44. package/dist/index.d.ts +18 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +746 -0
  47. package/dist/index.js.map +7 -0
  48. package/dist/lib/theming.d.ts +61 -0
  49. package/dist/lib/theming.d.ts.map +1 -0
  50. package/dist/lib/theming.test.d.ts +2 -0
  51. package/dist/lib/theming.test.d.ts.map +1 -0
  52. package/dist/tokens.css +88 -0
  53. package/dist/utils/clipboard.d.ts +9 -0
  54. package/dist/utils/clipboard.d.ts.map +1 -0
  55. package/dist/utils/clipboard.test.d.ts +2 -0
  56. package/dist/utils/clipboard.test.d.ts.map +1 -0
  57. package/dist/utils/format-time.d.ts +6 -0
  58. package/dist/utils/format-time.d.ts.map +1 -0
  59. package/dist/utils/format-time.test.d.ts +2 -0
  60. package/dist/utils/format-time.test.d.ts.map +1 -0
  61. package/package.json +43 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/lib/theming.tsx", "../src/components/Button.tsx", "../src/components/Modal.tsx", "../src/components/ConfirmationModal.tsx", "../src/components/MenuItem.tsx", "../src/components/Paginator.tsx", "../src/components/Popover.tsx", "../src/components/Slider.tsx", "../src/components/SlideOverPanel.tsx", "../src/components/ToggleSwitch.tsx", "../src/hooks/useDebounce.ts", "../src/utils/format-time.ts", "../src/utils/clipboard.ts"],
4
+ "sourcesContent": ["import { createContext, useContext, type ReactNode } from 'react'\n\n/** Pre-composed Tailwind class strings for component roles.\n * Components consume these via `useTheme()` \u2014 they never reference\n * raw color classes directly. */\nexport interface Theme {\n // Layout\n page: string\n sidebar: string\n card: string\n elevated: string\n hover: string\n active: string\n border: string\n borderSubtle: string\n input: string\n inputFocus: string\n\n // Text\n textPrimary: string\n textSecondary: string\n textMuted: string\n\n // Buttons\n buttonPrimary: string\n buttonSecondary: string\n buttonGhost: string\n buttonDanger: string\n buttonSuccess: string\n buttonWarning: string\n\n // Overlay\n overlay: string\n\n // Toggle\n toggleOn: string\n toggleOff: string\n\n // Slider\n sliderTrack: string\n sliderFill: string\n\n // Tabs\n tabActive: string\n tabInactive: string\n\n // Menu\n menuItem: string\n menuItemHover: string\n menuItemDanger: string\n\n // Messages / Chat\n messageSelf: string\n messageOther: string\n messageAgent: string\n messageSystem: string\n\n // Status\n statusOnline: string\n statusOffline: string\n statusAway: string\n\n // Badges\n badgeDefault: string\n badgeSuccess: string\n badgeWarning: string\n badgeDanger: string\n badgeInfo: string\n\n // Notifications\n notificationBadge: string\n notificationUnread: string\n notificationRead: string\n}\n\nconst darkTheme: Theme = {\n // Layout\n page: 'bg-bg-page text-text-primary',\n sidebar: 'bg-bg-sidebar border-r border-border-default',\n card: 'bg-bg-card border border-border-default rounded-lg',\n elevated: 'bg-bg-elevated',\n hover: 'hover:bg-bg-hover',\n active: 'bg-bg-active',\n border: 'border border-border-default',\n borderSubtle: 'border border-border-subtle',\n input:\n 'bg-bg-input border border-border-default text-text-primary placeholder:text-text-muted',\n inputFocus: 'focus:border-brand-primary focus:ring-1 focus:ring-brand-primary/30',\n\n // Text\n textPrimary: 'text-text-primary',\n textSecondary: 'text-text-secondary',\n textMuted: 'text-text-muted',\n\n // Buttons\n buttonPrimary: 'bg-brand-primary text-white hover:bg-brand-primary/90',\n buttonSecondary: 'bg-brand-secondary text-white hover:bg-brand-secondary/90',\n buttonGhost:\n 'bg-transparent text-text-secondary hover:bg-bg-hover hover:text-text-primary',\n buttonDanger: 'bg-brand-danger text-white hover:bg-brand-danger/90',\n buttonSuccess: 'bg-brand-success text-white hover:bg-brand-success/90',\n buttonWarning: 'bg-brand-warning text-white hover:bg-brand-warning/90',\n\n // Overlay\n overlay: 'bg-black/50 backdrop-blur-sm',\n\n // Toggle\n toggleOn: 'bg-brand-primary',\n toggleOff: 'bg-bg-elevated',\n\n // Slider\n sliderTrack: 'bg-bg-elevated',\n sliderFill: 'bg-brand-primary',\n\n // Tabs\n tabActive: 'bg-brand-primary text-white',\n tabInactive: 'text-text-secondary hover:bg-bg-hover hover:text-text-primary',\n\n // Menu\n menuItem: 'text-text-secondary hover:text-text-primary hover:bg-bg-hover',\n menuItemHover: 'bg-bg-hover text-text-primary',\n menuItemDanger: 'text-brand-danger hover:bg-brand-danger/10',\n\n // Messages\n messageSelf: 'bg-brand-primary/20 border-l-2 border-brand-primary/50',\n messageOther: 'bg-bg-card',\n messageAgent: 'bg-brand-accent/10 border-l-2 border-brand-accent/50',\n messageSystem: 'bg-bg-elevated text-text-muted text-sm italic',\n\n // Status\n statusOnline: 'bg-brand-success',\n statusOffline: 'bg-text-muted',\n statusAway: 'bg-brand-warning',\n\n // Badges\n badgeDefault: 'bg-bg-elevated text-text-secondary',\n badgeSuccess: 'bg-brand-success/15 text-brand-success',\n badgeWarning: 'bg-brand-warning/15 text-brand-warning',\n badgeDanger: 'bg-brand-danger/15 text-brand-danger',\n badgeInfo: 'bg-brand-info/15 text-brand-info',\n\n // Notifications\n notificationBadge: 'bg-brand-danger text-white',\n notificationUnread: 'bg-brand-primary/5',\n notificationRead: 'bg-bg-card',\n}\n\nconst lightTheme: Theme = {\n // Layout\n page: 'bg-bg-page text-text-primary',\n sidebar: 'bg-bg-sidebar border-r border-border-default',\n card: 'bg-bg-card border border-border-default rounded-lg shadow-sm',\n elevated: 'bg-bg-elevated',\n hover: 'hover:bg-bg-hover',\n active: 'bg-bg-active',\n border: 'border border-border-default',\n borderSubtle: 'border border-border-subtle',\n input:\n 'bg-bg-input border border-border-default text-text-primary placeholder:text-text-muted',\n inputFocus: 'focus:border-brand-primary focus:ring-1 focus:ring-brand-primary/30',\n\n // Text\n textPrimary: 'text-text-primary',\n textSecondary: 'text-text-secondary',\n textMuted: 'text-text-muted',\n\n // Buttons\n buttonPrimary: 'bg-brand-primary text-white hover:bg-brand-primary/90',\n buttonSecondary: 'bg-brand-secondary text-white hover:bg-brand-secondary/90',\n buttonGhost:\n 'bg-transparent text-text-secondary hover:bg-bg-hover hover:text-text-primary',\n buttonDanger: 'bg-brand-danger text-white hover:bg-brand-danger/90',\n buttonSuccess: 'bg-brand-success text-white hover:bg-brand-success/90',\n buttonWarning: 'bg-brand-warning text-white hover:bg-brand-warning/90',\n\n // Overlay\n overlay: 'bg-black/40 backdrop-blur-sm',\n\n // Toggle\n toggleOn: 'bg-brand-primary',\n toggleOff: 'bg-border-default',\n\n // Slider\n sliderTrack: 'bg-bg-elevated',\n sliderFill: 'bg-brand-primary',\n\n // Tabs\n tabActive: 'bg-brand-primary text-white',\n tabInactive: 'text-text-secondary hover:bg-bg-hover hover:text-text-primary',\n\n // Menu\n menuItem: 'text-text-secondary hover:text-text-primary hover:bg-bg-hover',\n menuItemHover: 'bg-bg-hover text-text-primary',\n menuItemDanger: 'text-brand-danger hover:bg-brand-danger/10',\n\n // Messages\n messageSelf: 'bg-brand-primary/10 border-l-2 border-brand-primary/40',\n messageOther: 'bg-bg-card',\n messageAgent: 'bg-brand-accent/5 border-l-2 border-brand-accent/40',\n messageSystem: 'bg-bg-elevated text-text-muted text-sm italic',\n\n // Status\n statusOnline: 'bg-brand-success',\n statusOffline: 'bg-text-muted',\n statusAway: 'bg-brand-warning',\n\n // Badges\n badgeDefault: 'bg-bg-elevated text-text-secondary',\n badgeSuccess: 'bg-brand-success/15 text-brand-success',\n badgeWarning: 'bg-brand-warning/15 text-brand-warning',\n badgeDanger: 'bg-brand-danger/15 text-brand-danger',\n badgeInfo: 'bg-brand-info/15 text-brand-info',\n\n // Notifications\n notificationBadge: 'bg-brand-danger text-white',\n notificationUnread: 'bg-brand-primary/5',\n notificationRead: 'bg-bg-card',\n}\n\nexport const themes = { dark: darkTheme, light: lightTheme } as const\nexport type ThemeName = keyof typeof themes\n\nconst ThemingContext = createContext<Theme>(darkTheme)\n\nexport function ThemingProvider({\n theme = 'dark',\n children,\n}: {\n theme?: ThemeName\n children: ReactNode\n}) {\n return (\n <ThemingContext.Provider value={themes[theme]}>\n <div data-theme={theme} className={themes[theme].page}>\n {children}\n </div>\n </ThemingContext.Provider>\n )\n}\n\nexport function useTheme(): Theme {\n return useContext(ThemingContext)\n}\n", "/**\n * Button \u2014 Themeable button with variant and size support.\n *\n * Uses useTheme() for all color classes. Layout utilities (padding,\n * font-weight, rounded, flex) are applied directly.\n */\n\nimport { type ReactNode, type ButtonHTMLAttributes } from 'react'\nimport { useTheme } from '../lib/theming'\n\nexport type ButtonVariant = 'primary' | 'secondary' | 'danger' | 'success' | 'ghost'\nexport type ButtonSize = 'sm' | 'md' | 'lg'\n\ninterface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: ButtonVariant\n size?: ButtonSize\n icon?: ReactNode\n children: ReactNode\n fullWidth?: boolean\n}\n\nconst sizeStyles: Record<ButtonSize, string> = {\n sm: 'px-3 py-1.5 text-sm',\n md: 'px-4 py-2 text-base',\n lg: 'px-6 py-3 text-lg',\n}\n\nexport function Button({\n variant = 'primary',\n size = 'md',\n icon,\n children,\n fullWidth = false,\n className = '',\n disabled = false,\n ...props\n}: ButtonProps) {\n const t = useTheme()\n\n const variantStyles: Record<ButtonVariant, string> = {\n primary: t.buttonPrimary,\n secondary: t.buttonSecondary,\n danger: t.buttonDanger,\n success: t.buttonSuccess,\n ghost: t.buttonGhost,\n }\n\n const combinedClassName = [\n 'font-medium rounded-lg transition-all flex items-center justify-center gap-2',\n variantStyles[variant],\n sizeStyles[size],\n fullWidth ? 'w-full' : '',\n disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer',\n className,\n ].filter(Boolean).join(' ')\n\n return (\n <button className={combinedClassName} disabled={disabled} {...props}>\n {icon && <span className=\"flex-shrink-0\">{icon}</span>}\n {children}\n </button>\n )\n}\n", "/**\n * Modal \u2014 Themeable portal-based modal with escape/backdrop dismiss.\n *\n * Supports persistent mode (no dismiss), configurable max-width,\n * and iOS safe-area-inset-top.\n */\n\nimport { useEffect } from 'react'\nimport { createPortal } from 'react-dom'\nimport { useTheme } from '../lib/theming'\n\ninterface ModalProps {\n isOpen: boolean\n onClose: () => void\n children: React.ReactNode\n title?: string\n style?: React.CSSProperties\n maxWidth?: 'sm' | 'md' | 'lg' | 'xl' | '2xl'\n persistent?: boolean\n}\n\nconst maxWidthClasses = {\n sm: 'max-w-sm',\n md: 'max-w-md',\n lg: 'max-w-lg',\n xl: 'max-w-xl',\n '2xl': 'max-w-2xl',\n}\n\nexport function Modal({\n isOpen,\n onClose,\n children,\n title,\n style,\n maxWidth = 'md',\n persistent = false,\n}: ModalProps) {\n const t = useTheme()\n\n useEffect(() => {\n if (persistent) return\n const handleEscapeKey = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && isOpen) onClose()\n }\n document.addEventListener('keydown', handleEscapeKey)\n if (isOpen) {\n document.body.style.overflow = 'hidden'\n } else {\n document.body.style.overflow = ''\n }\n return () => {\n document.removeEventListener('keydown', handleEscapeKey)\n document.body.style.overflow = ''\n }\n }, [isOpen, onClose, persistent])\n\n const handleBackdropClick = (e: React.MouseEvent<HTMLDivElement>) => {\n if (e.target === e.currentTarget) onClose()\n }\n\n if (!isOpen) return null\n if (typeof document === 'undefined') return null\n\n return createPortal(\n <div\n className={`fixed inset-0 z-[55] flex items-center justify-center overflow-hidden ${t.overlay}`}\n onClick={persistent ? undefined : handleBackdropClick}\n style={{ width: '100vw', height: '100vh', top: 0, left: 0, margin: 0, paddingTop: 'env(safe-area-inset-top)' }}\n >\n <div\n className={`relative max-h-[90vh] w-full ${maxWidthClasses[maxWidth]} overflow-auto rounded-xl ${t.card} p-6 shadow-xl m-4`}\n onClick={(e) => e.stopPropagation()}\n style={{ margin: '1rem', ...(style || {}) }}\n >\n {!persistent && (\n <button\n type=\"button\"\n className={`absolute top-4 right-4 z-10 inline-flex h-8 w-8 items-center justify-center rounded-full ${t.textMuted} ${t.hover} transition-colors`}\n onClick={onClose}\n aria-label=\"Close modal\"\n >\n <svg className=\"h-5 w-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n )}\n {title && (\n <div className=\"mb-4 pr-8\">\n <h3 className={`text-xl font-bold ${t.textPrimary}`}>{title}</h3>\n </div>\n )}\n <div>{children}</div>\n </div>\n </div>,\n document.body,\n )\n}\n", "/**\n * ConfirmationModal \u2014 Themeable confirmation dialog with danger/warning/info variants.\n */\n\nimport { Modal } from './Modal'\nimport { useTheme } from '../lib/theming'\n\ninterface ConfirmationModalProps {\n isOpen: boolean\n onClose: () => void\n onConfirm: () => void\n title: string\n message: string | React.ReactNode\n confirmText?: string\n cancelText?: string\n variant?: 'danger' | 'warning' | 'info'\n isLoading?: boolean\n}\n\nconst iconPaths: Record<string, string> = {\n danger: 'M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z',\n warning: 'M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z',\n info: 'M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z',\n}\n\nexport function ConfirmationModal({\n isOpen,\n onClose,\n onConfirm,\n title,\n message,\n confirmText = 'Confirm',\n cancelText = 'Cancel',\n variant = 'danger',\n isLoading = false,\n}: ConfirmationModalProps) {\n const t = useTheme()\n\n const variantStyles: Record<string, { icon: string; button: string }> = {\n danger: { icon: 'bg-brand-danger', button: t.buttonDanger },\n warning: { icon: 'bg-brand-warning', button: t.buttonWarning },\n info: { icon: 'bg-brand-info', button: t.buttonSecondary },\n }\n\n const styles = variantStyles[variant]\n\n return (\n <Modal isOpen={isOpen} onClose={isLoading ? () => {} : onClose}>\n <div className=\"w-full\">\n <div className=\"mb-6 flex justify-center\">\n <div className={`flex h-16 w-16 items-center justify-center rounded-full ${styles.icon}`}>\n <svg className=\"h-8 w-8 text-white\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d={iconPaths[variant]} />\n </svg>\n </div>\n </div>\n <h3 className={`mb-4 text-xl font-bold text-center ${t.textPrimary}`}>{title}</h3>\n <div className={`mb-6 text-center whitespace-pre-line ${t.textSecondary}`}>{message}</div>\n <div className=\"flex gap-3\">\n <button\n onClick={onClose}\n disabled={isLoading}\n className={`flex-1 px-4 py-2 rounded-lg transition-colors ${t.buttonGhost} ${t.border} disabled:opacity-50 disabled:cursor-not-allowed`}\n >\n {cancelText}\n </button>\n <button\n onClick={onConfirm}\n disabled={isLoading}\n className={`flex-1 px-4 py-2 rounded-lg transition-colors ${styles.button} disabled:opacity-50 disabled:cursor-not-allowed`}\n >\n {isLoading ? 'Processing...' : confirmText}\n </button>\n </div>\n </div>\n </Modal>\n )\n}\n", "/**\n * MenuItem \u2014 Themeable menu item button with icon, label, and loading state.\n */\n\nimport type { LucideIcon } from 'lucide-react'\nimport { Loader2 } from 'lucide-react'\nimport { useTheme } from '../lib/theming'\n\ninterface MenuItemProps {\n icon: LucideIcon\n label: string\n onClick: () => void\n disabled?: boolean\n danger?: boolean\n loading?: boolean\n className?: string\n suffix?: React.ReactNode\n}\n\nexport function MenuItem({ icon: Icon, label, onClick, disabled, danger, loading, className, suffix }: MenuItemProps) {\n const t = useTheme()\n const colorClasses = danger ? t.menuItemDanger : t.menuItem\n\n return (\n <button\n onClick={onClick}\n disabled={disabled || loading}\n className={`w-full flex items-center gap-2 px-3 py-2 text-sm rounded-lg transition-colors ${colorClasses} disabled:opacity-50${className ? ` ${className}` : ''}`}\n >\n {loading ? <Loader2 className=\"w-3.5 h-3.5 animate-spin\" /> : <Icon className=\"w-3.5 h-3.5\" />}\n {label}\n {suffix && <span className=\"ml-auto\">{suffix}</span>}\n </button>\n )\n}\n", "/**\n * Paginator \u2014 Themeable page navigation with editable current page input.\n *\n * Pattern: |< < 3 4 [X] 5 6 > >|\n */\n\nimport { useState, useRef, useEffect, useCallback } from 'react'\nimport { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react'\nimport { useTheme } from '../lib/theming'\n\ninterface PaginatorProps {\n currentPage: number\n totalPages: number\n onPageChange: (page: number) => void\n siblings?: number\n}\n\nexport function Paginator({\n currentPage,\n totalPages,\n onPageChange,\n siblings = 2,\n}: PaginatorProps) {\n const t = useTheme()\n const [editValue, setEditValue] = useState(String(currentPage))\n const [editing, setEditing] = useState(false)\n const inputRef = useRef<HTMLInputElement>(null)\n\n useEffect(() => {\n if (!editing) setEditValue(String(currentPage))\n }, [currentPage, editing])\n\n const clamp = useCallback((page: number) => Math.max(1, Math.min(totalPages, page)), [totalPages])\n\n const commitEdit = useCallback(() => {\n setEditing(false)\n const parsed = parseInt(editValue, 10)\n if (!isNaN(parsed)) {\n const clamped = clamp(parsed)\n onPageChange(clamped)\n setEditValue(String(clamped))\n } else {\n setEditValue(String(currentPage))\n }\n }, [editValue, clamp, onPageChange, currentPage])\n\n const handleKeyDown = useCallback((e: React.KeyboardEvent) => {\n if (e.key === 'Enter') {\n commitEdit()\n inputRef.current?.blur()\n } else if (e.key === 'Escape') {\n setEditing(false)\n setEditValue(String(currentPage))\n inputRef.current?.blur()\n }\n }, [commitEdit, currentPage])\n\n const pages: number[] = []\n const start = Math.max(1, currentPage - siblings)\n const end = Math.min(totalPages, currentPage + siblings)\n for (let i = start; i <= end; i++) {\n if (i !== currentPage) pages.push(i)\n }\n const before = pages.filter((p) => p < currentPage)\n const after = pages.filter((p) => p > currentPage)\n\n const navBtn = `p-1 ${t.textMuted} ${t.hover} disabled:opacity-30 disabled:cursor-not-allowed transition-colors rounded`\n const pageBtn = `px-1.5 py-0.5 text-xs rounded transition-colors ${t.textMuted} ${t.hover}`\n\n if (totalPages <= 1) return null\n\n return (\n <div className=\"flex items-center justify-center gap-0.5\">\n <button onClick={() => onPageChange(1)} disabled={currentPage <= 1} className={navBtn} aria-label=\"First page\">\n <ChevronsLeft className=\"w-4 h-4\" />\n </button>\n <button onClick={() => onPageChange(clamp(currentPage - 1))} disabled={currentPage <= 1} className={navBtn} aria-label=\"Previous page\">\n <ChevronLeft className=\"w-4 h-4\" />\n </button>\n {before.map((p) => (\n <button key={p} onClick={() => onPageChange(p)} className={pageBtn}>{p}</button>\n ))}\n <input\n ref={inputRef}\n type=\"text\"\n inputMode=\"numeric\"\n value={editing ? editValue : String(currentPage)}\n onChange={(e) => setEditValue(e.target.value)}\n onFocus={() => { setEditing(true); setTimeout(() => inputRef.current?.select(), 0) }}\n onBlur={commitEdit}\n onKeyDown={handleKeyDown}\n className={`w-8 text-center text-xs font-medium rounded-md py-0.5 outline-none ${t.tabActive} ${t.inputFocus}`}\n aria-label=\"Current page\"\n />\n {after.map((p) => (\n <button key={p} onClick={() => onPageChange(p)} className={pageBtn}>{p}</button>\n ))}\n <button onClick={() => onPageChange(clamp(currentPage + 1))} disabled={currentPage >= totalPages} className={navBtn} aria-label=\"Next page\">\n <ChevronRight className=\"w-4 h-4\" />\n </button>\n <button onClick={() => onPageChange(totalPages)} disabled={currentPage >= totalPages} className={navBtn} aria-label=\"Last page\">\n <ChevronsRight className=\"w-4 h-4\" />\n </button>\n </div>\n )\n}\n", "/**\n * Popover \u2014 Themeable positioned popover with auto viewport adjustment.\n *\n * Renders in a portal. Supports above/below anchor positioning with\n * automatic flipping when insufficient viewport space.\n */\n\nimport { useEffect, useRef, useState, type ReactNode, type RefObject } from 'react'\nimport { createPortal } from 'react-dom'\nimport { useTheme } from '../lib/theming'\n\ninterface PopoverProps {\n open: boolean\n anchorRef: RefObject<HTMLElement | null>\n onClose: () => void\n children: ReactNode\n className?: string\n anchor?: 'above' | 'below'\n}\n\nexport function Popover({ open, anchorRef, onClose, children, className, anchor = 'below' }: PopoverProps) {\n const t = useTheme()\n const popoverRef = useRef<HTMLDivElement>(null)\n const [adjustedStyle, setAdjustedStyle] = useState<React.CSSProperties>({})\n\n useEffect(() => {\n if (!open) return\n function handlePointerDown(e: PointerEvent | TouchEvent) {\n const target = e.target as Node\n if (\n popoverRef.current && !popoverRef.current.contains(target) &&\n anchorRef.current && !anchorRef.current.contains(target)\n ) {\n onClose()\n }\n }\n function handleKeyDown(e: KeyboardEvent) {\n if (e.key === 'Escape') onClose()\n }\n document.addEventListener('pointerdown', handlePointerDown)\n document.addEventListener('touchstart', handlePointerDown)\n document.addEventListener('keydown', handleKeyDown)\n return () => {\n document.removeEventListener('pointerdown', handlePointerDown)\n document.removeEventListener('touchstart', handlePointerDown)\n document.removeEventListener('keydown', handleKeyDown)\n }\n }, [open, onClose, anchorRef])\n\n useEffect(() => {\n if (!open || !popoverRef.current || !anchorRef.current) return\n const anchorRect = anchorRef.current.getBoundingClientRect()\n const popoverRect = popoverRef.current.getBoundingClientRect()\n const padding = 16\n const style: React.CSSProperties = { position: 'absolute', zIndex: 50 }\n\n let left = anchorRect.left + window.scrollX\n if (left + popoverRect.width > window.innerWidth - padding) {\n left = Math.max(padding, window.innerWidth - popoverRect.width - padding)\n }\n if (left < padding) left = padding\n style.left = left\n\n const spaceBelow = window.innerHeight - anchorRect.bottom\n const spaceAbove = anchorRect.top\n\n if (anchor === 'below') {\n if (spaceBelow >= popoverRect.height + padding || spaceBelow >= spaceAbove) {\n style.top = anchorRect.bottom + window.scrollY\n } else {\n style.bottom = document.documentElement.scrollHeight - anchorRect.top - window.scrollY\n }\n } else {\n if (spaceAbove >= popoverRect.height + padding || spaceAbove >= spaceBelow) {\n style.bottom = document.documentElement.scrollHeight - anchorRect.top - window.scrollY\n } else {\n style.top = anchorRect.bottom + window.scrollY\n }\n }\n setAdjustedStyle(style)\n }, [open, anchor, anchorRef])\n\n if (!open || !anchorRef.current) return null\n\n const rect = anchorRef.current.getBoundingClientRect()\n const initialStyle: React.CSSProperties = {\n position: 'absolute',\n left: rect.left + window.scrollX,\n zIndex: 50,\n visibility: adjustedStyle.left != null ? 'visible' : 'hidden',\n ...(anchor === 'above'\n ? { bottom: document.documentElement.scrollHeight - rect.top - window.scrollY }\n : { top: rect.bottom + window.scrollY }),\n }\n\n const style = adjustedStyle.left != null ? adjustedStyle : initialStyle\n const defaultClasses = `${t.elevated} ${t.border} shadow-lg rounded-lg`\n\n return createPortal(\n <div ref={popoverRef} style={style} className={className ?? defaultClasses}>\n {children}\n </div>,\n document.body,\n )\n}\n", "/**\n * Slider \u2014 Themeable range slider with gradient fill.\n *\n * Two modes:\n * - Continuous: provide min, max, step, value\n * - Discrete: provide options array, value snaps to option values\n *\n * Requires the `slider-styled` CSS class from tokens.css.\n * Fill gradient uses CSS custom properties from the theme.\n */\n\nimport type React from 'react'\nimport { useTheme } from '../lib/theming'\n\ninterface SliderBaseProps {\n disabled?: boolean\n className?: string\n}\n\ninterface ContinuousSliderProps extends SliderBaseProps {\n options?: never\n labels?: never\n min: number\n max: number\n step: number\n value: number\n onChange: (value: number) => void\n}\n\ninterface DiscreteSliderProps<T extends number | string> extends SliderBaseProps {\n options: T[]\n labels?: boolean | ((option: T) => string)\n min?: never\n max?: never\n step?: never\n value: T\n onChange: (value: T) => void\n}\n\nexport type SliderProps<T extends number | string = number> =\n | ContinuousSliderProps\n | DiscreteSliderProps<T>\n\nfunction fillStyle(value: number, min: number, max: number): React.CSSProperties {\n const pct = max === min ? 0 : ((value - min) / (max - min)) * 100\n return {\n background: `linear-gradient(90deg, var(--color-brand-primary) 0%, var(--color-brand-secondary) ${pct}%, var(--color-bg-elevated) ${pct}%)`,\n }\n}\n\nexport function Slider<T extends number | string = number>(props: SliderProps<T>) {\n const t = useTheme()\n const { disabled, className } = props\n\n if (props.options) {\n const { options, labels, value, onChange } = props\n const index = options.indexOf(value)\n const currentIndex = index === -1 ? 0 : index\n\n const getLabel = (option: T): string => {\n if (typeof labels === 'function') return labels(option)\n return String(option)\n }\n\n return (\n <div className={className}>\n <input\n type=\"range\"\n min={0}\n max={options.length - 1}\n step={1}\n value={currentIndex}\n onChange={(e) => onChange(options[parseInt(e.target.value)])}\n className=\"w-full slider-styled\"\n style={fillStyle(currentIndex, 0, options.length - 1)}\n disabled={disabled}\n />\n {labels && (\n <div className={`flex justify-between text-xs mt-1 ${t.textMuted}`}>\n {options.map((option) => (\n <span key={String(option)}>{getLabel(option)}</span>\n ))}\n </div>\n )}\n </div>\n )\n }\n\n const { min, max, step, value, onChange } = props\n\n return (\n <div className={className}>\n <input\n type=\"range\"\n min={min}\n max={max}\n step={step}\n value={value}\n onChange={(e) => onChange(parseFloat(e.target.value))}\n className=\"w-full slider-styled\"\n style={fillStyle(value, min, max)}\n disabled={disabled}\n />\n </div>\n )\n}\n", "/**\n * SlideOverPanel \u2014 Themeable right-side slide-over overlay with backdrop.\n */\n\nimport { useEffect, useState, type ReactNode } from 'react'\nimport { useTheme } from '../lib/theming'\n\ninterface SlideOverPanelProps {\n open: boolean\n onClose: () => void\n children: ReactNode\n}\n\nexport function SlideOverPanel({ open, onClose, children }: SlideOverPanelProps) {\n const t = useTheme()\n const [mounted, setMounted] = useState(false)\n const [visible, setVisible] = useState(false)\n\n useEffect(() => {\n if (open) {\n setMounted(true)\n requestAnimationFrame(() => {\n requestAnimationFrame(() => setVisible(true))\n })\n } else {\n setVisible(false)\n const timer = setTimeout(() => setMounted(false), 200)\n return () => clearTimeout(timer)\n }\n }, [open])\n\n if (!mounted) return null\n\n return (\n <>\n <div\n className={`fixed inset-0 z-20 transition-opacity duration-200 ${t.overlay} ${\n visible ? 'opacity-100' : 'opacity-0'\n }`}\n onClick={onClose}\n />\n <div\n className={`fixed top-0 right-0 bottom-0 w-72 ${t.sidebar} overflow-y-auto z-30 transition-transform duration-200 ${\n visible ? 'translate-x-0' : 'translate-x-full'\n }`}\n style={{ paddingTop: 'env(safe-area-inset-top)' }}\n >\n {children}\n </div>\n </>\n )\n}\n", "/**\n * ToggleSwitch \u2014 Themeable iOS-style toggle.\n *\n * Uses useTheme() for on/off track colors and text.\n */\n\nimport { type ButtonHTMLAttributes } from 'react'\nimport { useTheme } from '../lib/theming'\n\nexport type ToggleSwitchSize = 'sm' | 'md' | 'lg'\n\ninterface ToggleSwitchProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'size' | 'type' | 'onChange'> {\n checked: boolean\n onChange: (checked: boolean) => void\n size?: ToggleSwitchSize\n label?: string\n description?: string\n}\n\nconst sizeConfig: Record<ToggleSwitchSize, {\n track: string\n knob: string\n knobTranslate: string\n}> = {\n sm: { track: 'w-9 h-5', knob: 'w-4 h-4', knobTranslate: 'translate-x-4' },\n md: { track: 'w-11 h-6', knob: 'w-5 h-5', knobTranslate: 'translate-x-5' },\n lg: { track: 'w-14 h-7', knob: 'w-6 h-6', knobTranslate: 'translate-x-7' },\n}\n\nexport function ToggleSwitch({\n checked,\n onChange,\n size = 'md',\n label,\n description,\n disabled = false,\n id,\n ...props\n}: ToggleSwitchProps) {\n const t = useTheme()\n const config = sizeConfig[size]\n\n const handleClick = () => {\n if (!disabled) onChange(!checked)\n }\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === ' ' || e.key === 'Enter') {\n e.preventDefault()\n if (!disabled) onChange(!checked)\n }\n }\n\n const toggle = (\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n aria-label={label}\n id={id}\n disabled={disabled}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n className={[\n 'relative inline-flex items-center rounded-full transition-all duration-200 flex-shrink-0',\n 'focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-primary focus-visible:ring-offset-2',\n config.track,\n checked ? t.toggleOn : t.toggleOff,\n disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer',\n ].join(' ')}\n {...props}\n >\n <span\n className={[\n 'inline-flex items-center justify-center rounded-full bg-white shadow-sm transition-transform duration-200',\n config.knob,\n checked ? config.knobTranslate : 'translate-x-0.5',\n ].join(' ')}\n />\n </button>\n )\n\n if (!label) return toggle\n\n return (\n <div className={['flex items-center justify-between gap-3', disabled ? 'opacity-50' : ''].join(' ')}>\n <div className=\"flex flex-col min-w-0\">\n <label\n htmlFor={id}\n className={[\n 'text-sm font-medium',\n t.textPrimary,\n disabled ? 'cursor-not-allowed' : 'cursor-pointer',\n ].join(' ')}\n onClick={handleClick}\n >\n {label}\n </label>\n {description && (\n <span className={`text-xs mt-0.5 ${t.textMuted}`}>{description}</span>\n )}\n </div>\n {toggle}\n </div>\n )\n}\n", "import { useState, useEffect } from 'react'\n\nexport function useDebounce<T>(value: T, delay: number): T {\n const [debouncedValue, setDebouncedValue] = useState<T>(value)\n\n useEffect(() => {\n const handler = setTimeout(() => {\n setDebouncedValue(value)\n }, delay)\n\n return () => {\n clearTimeout(handler)\n }\n }, [value, delay])\n\n return debouncedValue\n}\n", "/**\n * Time formatting utilities.\n */\n\nexport function formatExactTime(dateString: string): string {\n const date = new Date(dateString)\n const time = date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true })\n const weekday = date.toLocaleDateString('en-US', { weekday: 'short' })\n const month = date.getMonth() + 1\n const day = date.getDate()\n const year = date.getFullYear().toString().slice(-2)\n return `${time} ${weekday} ${month}/${day}/${year}`\n}\n\nexport function getRelativeTime(dateString: string): string {\n const date = new Date(dateString)\n const now = new Date()\n const diffMs = now.getTime() - date.getTime()\n const diffMins = Math.floor(diffMs / 60000)\n const diffHours = Math.floor(diffMs / 3600000)\n const diffDays = Math.floor(diffMs / 86400000)\n\n if (diffMins < 1) return 'Just now'\n if (diffMins < 60) return `${diffMins}m ago`\n if (diffHours < 24) return `${diffHours}h ago`\n if (diffDays < 7) return `${diffDays}d ago`\n if (diffDays < 30) {\n const weeks = Math.floor(diffDays / 7)\n return `${weeks}w ago`\n }\n return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })\n}\n", "/**\n * Share a URL via the native share sheet (mobile) or copy to clipboard (desktop).\n */\nexport async function shareOrCopyUrl(url: string): Promise<'shared' | 'copied' | 'cancelled' | 'failed'> {\n try {\n if (navigator.share) {\n await navigator.share({ url })\n return 'shared'\n }\n } catch (err) {\n if ((err as DOMException)?.name === 'AbortError') return 'cancelled'\n }\n const copied = await copyToClipboard(url)\n return copied ? 'copied' : 'failed'\n}\n\n/**\n * Copy text to the clipboard with fallback for insecure contexts.\n */\nexport async function copyToClipboard(text: string): Promise<boolean> {\n try {\n if (navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(text)\n return true\n }\n } catch { /* fall through to fallback */ }\n\n const textarea = document.createElement('textarea')\n textarea.value = text\n textarea.style.position = 'fixed'\n textarea.style.opacity = '0'\n document.body.appendChild(textarea)\n textarea.select()\n try {\n document.execCommand('copy')\n return true\n } catch {\n return false\n } finally {\n document.body.removeChild(textarea)\n }\n}\n"],
5
+ "mappings": ";AAAA,SAAS,eAAe,kBAAkC;AAyOpD;AA9JN,IAAM,YAAmB;AAAA;AAAA,EAEvB,MAAM;AAAA,EACN,SAAS;AAAA,EACT,MAAM;AAAA,EACN,UAAU;AAAA,EACV,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,OACE;AAAA,EACF,YAAY;AAAA;AAAA,EAGZ,aAAa;AAAA,EACb,eAAe;AAAA,EACf,WAAW;AAAA;AAAA,EAGX,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,aACE;AAAA,EACF,cAAc;AAAA,EACd,eAAe;AAAA,EACf,eAAe;AAAA;AAAA,EAGf,SAAS;AAAA;AAAA,EAGT,UAAU;AAAA,EACV,WAAW;AAAA;AAAA,EAGX,aAAa;AAAA,EACb,YAAY;AAAA;AAAA,EAGZ,WAAW;AAAA,EACX,aAAa;AAAA;AAAA,EAGb,UAAU;AAAA,EACV,eAAe;AAAA,EACf,gBAAgB;AAAA;AAAA,EAGhB,aAAa;AAAA,EACb,cAAc;AAAA,EACd,cAAc;AAAA,EACd,eAAe;AAAA;AAAA,EAGf,cAAc;AAAA,EACd,eAAe;AAAA,EACf,YAAY;AAAA;AAAA,EAGZ,cAAc;AAAA,EACd,cAAc;AAAA,EACd,cAAc;AAAA,EACd,aAAa;AAAA,EACb,WAAW;AAAA;AAAA,EAGX,mBAAmB;AAAA,EACnB,oBAAoB;AAAA,EACpB,kBAAkB;AACpB;AAEA,IAAM,aAAoB;AAAA;AAAA,EAExB,MAAM;AAAA,EACN,SAAS;AAAA,EACT,MAAM;AAAA,EACN,UAAU;AAAA,EACV,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,OACE;AAAA,EACF,YAAY;AAAA;AAAA,EAGZ,aAAa;AAAA,EACb,eAAe;AAAA,EACf,WAAW;AAAA;AAAA,EAGX,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,aACE;AAAA,EACF,cAAc;AAAA,EACd,eAAe;AAAA,EACf,eAAe;AAAA;AAAA,EAGf,SAAS;AAAA;AAAA,EAGT,UAAU;AAAA,EACV,WAAW;AAAA;AAAA,EAGX,aAAa;AAAA,EACb,YAAY;AAAA;AAAA,EAGZ,WAAW;AAAA,EACX,aAAa;AAAA;AAAA,EAGb,UAAU;AAAA,EACV,eAAe;AAAA,EACf,gBAAgB;AAAA;AAAA,EAGhB,aAAa;AAAA,EACb,cAAc;AAAA,EACd,cAAc;AAAA,EACd,eAAe;AAAA;AAAA,EAGf,cAAc;AAAA,EACd,eAAe;AAAA,EACf,YAAY;AAAA;AAAA,EAGZ,cAAc;AAAA,EACd,cAAc;AAAA,EACd,cAAc;AAAA,EACd,aAAa;AAAA,EACb,WAAW;AAAA;AAAA,EAGX,mBAAmB;AAAA,EACnB,oBAAoB;AAAA,EACpB,kBAAkB;AACpB;AAEO,IAAM,SAAS,EAAE,MAAM,WAAW,OAAO,WAAW;AAG3D,IAAM,iBAAiB,cAAqB,SAAS;AAE9C,SAAS,gBAAgB;AAAA,EAC9B,QAAQ;AAAA,EACR;AACF,GAGG;AACD,SACE,oBAAC,eAAe,UAAf,EAAwB,OAAO,OAAO,KAAK,GAC1C,8BAAC,SAAI,cAAY,OAAO,WAAW,OAAO,KAAK,EAAE,MAC9C,UACH,GACF;AAEJ;AAEO,SAAS,WAAkB;AAChC,SAAO,WAAW,cAAc;AAClC;;;ACzLI,SACW,OAAAA,MADX;AApCJ,IAAM,aAAyC;AAAA,EAC7C,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAEO,SAAS,OAAO;AAAA,EACrB,UAAU;AAAA,EACV,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,GAAG;AACL,GAAgB;AACd,QAAM,IAAI,SAAS;AAEnB,QAAM,gBAA+C;AAAA,IACnD,SAAS,EAAE;AAAA,IACX,WAAW,EAAE;AAAA,IACb,QAAQ,EAAE;AAAA,IACV,SAAS,EAAE;AAAA,IACX,OAAO,EAAE;AAAA,EACX;AAEA,QAAM,oBAAoB;AAAA,IACxB;AAAA,IACA,cAAc,OAAO;AAAA,IACrB,WAAW,IAAI;AAAA,IACf,YAAY,WAAW;AAAA,IACvB,WAAW,kCAAkC;AAAA,IAC7C;AAAA,EACF,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAE1B,SACE,qBAAC,YAAO,WAAW,mBAAmB,UAAqB,GAAG,OAC3D;AAAA,YAAQ,gBAAAA,KAAC,UAAK,WAAU,iBAAiB,gBAAK;AAAA,IAC9C;AAAA,KACH;AAEJ;;;ACvDA,SAAS,iBAAiB;AAC1B,SAAS,oBAAoB;AA8DvB,SAaQ,OAAAC,MAbR,QAAAC,aAAA;AAjDN,IAAM,kBAAkB;AAAA,EACtB,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,OAAO;AACT;AAEO,SAAS,MAAM;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,aAAa;AACf,GAAe;AACb,QAAM,IAAI,SAAS;AAEnB,YAAU,MAAM;AACd,QAAI,WAAY;AAChB,UAAM,kBAAkB,CAAC,UAAyB;AAChD,UAAI,MAAM,QAAQ,YAAY,OAAQ,SAAQ;AAAA,IAChD;AACA,aAAS,iBAAiB,WAAW,eAAe;AACpD,QAAI,QAAQ;AACV,eAAS,KAAK,MAAM,WAAW;AAAA,IACjC,OAAO;AACL,eAAS,KAAK,MAAM,WAAW;AAAA,IACjC;AACA,WAAO,MAAM;AACX,eAAS,oBAAoB,WAAW,eAAe;AACvD,eAAS,KAAK,MAAM,WAAW;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,QAAQ,SAAS,UAAU,CAAC;AAEhC,QAAM,sBAAsB,CAAC,MAAwC;AACnE,QAAI,EAAE,WAAW,EAAE,cAAe,SAAQ;AAAA,EAC5C;AAEA,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,OAAO,aAAa,YAAa,QAAO;AAE5C,SAAO;AAAA,IACL,gBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,yEAAyE,EAAE,OAAO;AAAA,QAC7F,SAAS,aAAa,SAAY;AAAA,QAClC,OAAO,EAAE,OAAO,SAAS,QAAQ,SAAS,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,YAAY,2BAA2B;AAAA,QAE7G,0BAAAC;AAAA,UAAC;AAAA;AAAA,YACC,WAAW,gCAAgC,gBAAgB,QAAQ,CAAC,6BAA6B,EAAE,IAAI;AAAA,YACvG,SAAS,CAAC,MAAM,EAAE,gBAAgB;AAAA,YAClC,OAAO,EAAE,QAAQ,QAAQ,GAAI,SAAS,CAAC,EAAG;AAAA,YAEzC;AAAA,eAAC,cACA,gBAAAD;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,WAAW,4FAA4F,EAAE,SAAS,IAAI,EAAE,KAAK;AAAA,kBAC7H,SAAS;AAAA,kBACT,cAAW;AAAA,kBAEX,0BAAAA,KAAC,SAAI,WAAU,WAAU,MAAK,QAAO,SAAQ,aAAY,QAAO,gBAC9D,0BAAAA,KAAC,UAAK,eAAc,SAAQ,gBAAe,SAAQ,aAAa,GAAG,GAAE,wBAAuB,GAC9F;AAAA;AAAA,cACF;AAAA,cAED,SACC,gBAAAA,KAAC,SAAI,WAAU,aACb,0BAAAA,KAAC,QAAG,WAAW,qBAAqB,EAAE,WAAW,IAAK,iBAAM,GAC9D;AAAA,cAEF,gBAAAA,KAAC,SAAK,UAAS;AAAA;AAAA;AAAA,QACjB;AAAA;AAAA,IACF;AAAA,IACA,SAAS;AAAA,EACX;AACF;;;AC7Cc,gBAAAE,MAMN,QAAAC,aANM;AAjCd,IAAM,YAAoC;AAAA,EACxC,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,MAAM;AACR;AAEO,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,aAAa;AAAA,EACb,UAAU;AAAA,EACV,YAAY;AACd,GAA2B;AACzB,QAAM,IAAI,SAAS;AAEnB,QAAM,gBAAkE;AAAA,IACtE,QAAQ,EAAE,MAAM,mBAAmB,QAAQ,EAAE,aAAa;AAAA,IAC1D,SAAS,EAAE,MAAM,oBAAoB,QAAQ,EAAE,cAAc;AAAA,IAC7D,MAAM,EAAE,MAAM,iBAAiB,QAAQ,EAAE,gBAAgB;AAAA,EAC3D;AAEA,QAAM,SAAS,cAAc,OAAO;AAEpC,SACE,gBAAAD,KAAC,SAAM,QAAgB,SAAS,YAAY,MAAM;AAAA,EAAC,IAAI,SACrD,0BAAAC,MAAC,SAAI,WAAU,UACb;AAAA,oBAAAD,KAAC,SAAI,WAAU,4BACb,0BAAAA,KAAC,SAAI,WAAW,2DAA2D,OAAO,IAAI,IACpF,0BAAAA,KAAC,SAAI,WAAU,sBAAqB,MAAK,QAAO,QAAO,gBAAe,SAAQ,aAC5E,0BAAAA,KAAC,UAAK,eAAc,SAAQ,gBAAe,SAAQ,aAAa,GAAG,GAAG,UAAU,OAAO,GAAG,GAC5F,GACF,GACF;AAAA,IACA,gBAAAA,KAAC,QAAG,WAAW,sCAAsC,EAAE,WAAW,IAAK,iBAAM;AAAA,IAC7E,gBAAAA,KAAC,SAAI,WAAW,wCAAwC,EAAE,aAAa,IAAK,mBAAQ;AAAA,IACpF,gBAAAC,MAAC,SAAI,WAAU,cACb;AAAA,sBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,UAAU;AAAA,UACV,WAAW,iDAAiD,EAAE,WAAW,IAAI,EAAE,MAAM;AAAA,UAEpF;AAAA;AAAA,MACH;AAAA,MACA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,UAAU;AAAA,UACV,WAAW,iDAAiD,OAAO,MAAM;AAAA,UAExE,sBAAY,kBAAkB;AAAA;AAAA,MACjC;AAAA,OACF;AAAA,KACF,GACF;AAEJ;;;ACxEA,SAAS,eAAe;AAmBpB,SAKa,OAAAE,MALb,QAAAC,aAAA;AALG,SAAS,SAAS,EAAE,MAAM,MAAM,OAAO,SAAS,UAAU,QAAQ,SAAS,WAAW,OAAO,GAAkB;AACpH,QAAM,IAAI,SAAS;AACnB,QAAM,eAAe,SAAS,EAAE,iBAAiB,EAAE;AAEnD,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,UAAU,YAAY;AAAA,MACtB,WAAW,iFAAiF,YAAY,uBAAuB,YAAY,IAAI,SAAS,KAAK,EAAE;AAAA,MAE9J;AAAA,kBAAU,gBAAAD,KAAC,WAAQ,WAAU,4BAA2B,IAAK,gBAAAA,KAAC,QAAK,WAAU,eAAc;AAAA,QAC3F;AAAA,QACA,UAAU,gBAAAA,KAAC,UAAK,WAAU,WAAW,kBAAO;AAAA;AAAA;AAAA,EAC/C;AAEJ;;;AC5BA,SAAS,UAAU,QAAQ,aAAAE,YAAW,mBAAmB;AACzD,SAAS,aAAa,cAAc,cAAc,qBAAqB;AAiEnE,SAEI,OAAAC,MAFJ,QAAAC,aAAA;AAvDG,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AACb,GAAmB;AACjB,QAAM,IAAI,SAAS;AACnB,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,OAAO,WAAW,CAAC;AAC9D,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAC5C,QAAM,WAAW,OAAyB,IAAI;AAE9C,EAAAC,WAAU,MAAM;AACd,QAAI,CAAC,QAAS,cAAa,OAAO,WAAW,CAAC;AAAA,EAChD,GAAG,CAAC,aAAa,OAAO,CAAC;AAEzB,QAAM,QAAQ,YAAY,CAAC,SAAiB,KAAK,IAAI,GAAG,KAAK,IAAI,YAAY,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC;AAEjG,QAAM,aAAa,YAAY,MAAM;AACnC,eAAW,KAAK;AAChB,UAAM,SAAS,SAAS,WAAW,EAAE;AACrC,QAAI,CAAC,MAAM,MAAM,GAAG;AAClB,YAAM,UAAU,MAAM,MAAM;AAC5B,mBAAa,OAAO;AACpB,mBAAa,OAAO,OAAO,CAAC;AAAA,IAC9B,OAAO;AACL,mBAAa,OAAO,WAAW,CAAC;AAAA,IAClC;AAAA,EACF,GAAG,CAAC,WAAW,OAAO,cAAc,WAAW,CAAC;AAEhD,QAAM,gBAAgB,YAAY,CAAC,MAA2B;AAC5D,QAAI,EAAE,QAAQ,SAAS;AACrB,iBAAW;AACX,eAAS,SAAS,KAAK;AAAA,IACzB,WAAW,EAAE,QAAQ,UAAU;AAC7B,iBAAW,KAAK;AAChB,mBAAa,OAAO,WAAW,CAAC;AAChC,eAAS,SAAS,KAAK;AAAA,IACzB;AAAA,EACF,GAAG,CAAC,YAAY,WAAW,CAAC;AAE5B,QAAM,QAAkB,CAAC;AACzB,QAAM,QAAQ,KAAK,IAAI,GAAG,cAAc,QAAQ;AAChD,QAAM,MAAM,KAAK,IAAI,YAAY,cAAc,QAAQ;AACvD,WAAS,IAAI,OAAO,KAAK,KAAK,KAAK;AACjC,QAAI,MAAM,YAAa,OAAM,KAAK,CAAC;AAAA,EACrC;AACA,QAAM,SAAS,MAAM,OAAO,CAAC,MAAM,IAAI,WAAW;AAClD,QAAM,QAAQ,MAAM,OAAO,CAAC,MAAM,IAAI,WAAW;AAEjD,QAAM,SAAS,OAAO,EAAE,SAAS,IAAI,EAAE,KAAK;AAC5C,QAAM,UAAU,mDAAmD,EAAE,SAAS,IAAI,EAAE,KAAK;AAEzF,MAAI,cAAc,EAAG,QAAO;AAE5B,SACE,gBAAAD,MAAC,SAAI,WAAU,4CACb;AAAA,oBAAAD,KAAC,YAAO,SAAS,MAAM,aAAa,CAAC,GAAG,UAAU,eAAe,GAAG,WAAW,QAAQ,cAAW,cAChG,0BAAAA,KAAC,gBAAa,WAAU,WAAU,GACpC;AAAA,IACA,gBAAAA,KAAC,YAAO,SAAS,MAAM,aAAa,MAAM,cAAc,CAAC,CAAC,GAAG,UAAU,eAAe,GAAG,WAAW,QAAQ,cAAW,iBACrH,0BAAAA,KAAC,eAAY,WAAU,WAAU,GACnC;AAAA,IACC,OAAO,IAAI,CAAC,MACX,gBAAAA,KAAC,YAAe,SAAS,MAAM,aAAa,CAAC,GAAG,WAAW,SAAU,eAAxD,CAA0D,CACxE;AAAA,IACD,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,MAAK;AAAA,QACL,WAAU;AAAA,QACV,OAAO,UAAU,YAAY,OAAO,WAAW;AAAA,QAC/C,UAAU,CAAC,MAAM,aAAa,EAAE,OAAO,KAAK;AAAA,QAC5C,SAAS,MAAM;AAAE,qBAAW,IAAI;AAAG,qBAAW,MAAM,SAAS,SAAS,OAAO,GAAG,CAAC;AAAA,QAAE;AAAA,QACnF,QAAQ;AAAA,QACR,WAAW;AAAA,QACX,WAAW,sEAAsE,EAAE,SAAS,IAAI,EAAE,UAAU;AAAA,QAC5G,cAAW;AAAA;AAAA,IACb;AAAA,IACC,MAAM,IAAI,CAAC,MACV,gBAAAA,KAAC,YAAe,SAAS,MAAM,aAAa,CAAC,GAAG,WAAW,SAAU,eAAxD,CAA0D,CACxE;AAAA,IACD,gBAAAA,KAAC,YAAO,SAAS,MAAM,aAAa,MAAM,cAAc,CAAC,CAAC,GAAG,UAAU,eAAe,YAAY,WAAW,QAAQ,cAAW,aAC9H,0BAAAA,KAAC,gBAAa,WAAU,WAAU,GACpC;AAAA,IACA,gBAAAA,KAAC,YAAO,SAAS,MAAM,aAAa,UAAU,GAAG,UAAU,eAAe,YAAY,WAAW,QAAQ,cAAW,aAClH,0BAAAA,KAAC,iBAAc,WAAU,WAAU,GACrC;AAAA,KACF;AAEJ;;;AClGA,SAAS,aAAAG,YAAW,UAAAC,SAAQ,YAAAC,iBAAgD;AAC5E,SAAS,gBAAAC,qBAAoB;AA2FzB,gBAAAC,YAAA;AA/EG,SAAS,QAAQ,EAAE,MAAM,WAAW,SAAS,UAAU,WAAW,SAAS,QAAQ,GAAiB;AACzG,QAAM,IAAI,SAAS;AACnB,QAAM,aAAaC,QAAuB,IAAI;AAC9C,QAAM,CAAC,eAAe,gBAAgB,IAAIC,UAA8B,CAAC,CAAC;AAE1E,EAAAC,WAAU,MAAM;AACd,QAAI,CAAC,KAAM;AACX,aAAS,kBAAkB,GAA8B;AACvD,YAAM,SAAS,EAAE;AACjB,UACE,WAAW,WAAW,CAAC,WAAW,QAAQ,SAAS,MAAM,KACzD,UAAU,WAAW,CAAC,UAAU,QAAQ,SAAS,MAAM,GACvD;AACA,gBAAQ;AAAA,MACV;AAAA,IACF;AACA,aAAS,cAAc,GAAkB;AACvC,UAAI,EAAE,QAAQ,SAAU,SAAQ;AAAA,IAClC;AACA,aAAS,iBAAiB,eAAe,iBAAiB;AAC1D,aAAS,iBAAiB,cAAc,iBAAiB;AACzD,aAAS,iBAAiB,WAAW,aAAa;AAClD,WAAO,MAAM;AACX,eAAS,oBAAoB,eAAe,iBAAiB;AAC7D,eAAS,oBAAoB,cAAc,iBAAiB;AAC5D,eAAS,oBAAoB,WAAW,aAAa;AAAA,IACvD;AAAA,EACF,GAAG,CAAC,MAAM,SAAS,SAAS,CAAC;AAE7B,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,QAAQ,CAAC,WAAW,WAAW,CAAC,UAAU,QAAS;AACxD,UAAM,aAAa,UAAU,QAAQ,sBAAsB;AAC3D,UAAM,cAAc,WAAW,QAAQ,sBAAsB;AAC7D,UAAM,UAAU;AAChB,UAAMC,SAA6B,EAAE,UAAU,YAAY,QAAQ,GAAG;AAEtE,QAAI,OAAO,WAAW,OAAO,OAAO;AACpC,QAAI,OAAO,YAAY,QAAQ,OAAO,aAAa,SAAS;AAC1D,aAAO,KAAK,IAAI,SAAS,OAAO,aAAa,YAAY,QAAQ,OAAO;AAAA,IAC1E;AACA,QAAI,OAAO,QAAS,QAAO;AAC3B,IAAAA,OAAM,OAAO;AAEb,UAAM,aAAa,OAAO,cAAc,WAAW;AACnD,UAAM,aAAa,WAAW;AAE9B,QAAI,WAAW,SAAS;AACtB,UAAI,cAAc,YAAY,SAAS,WAAW,cAAc,YAAY;AAC1E,QAAAA,OAAM,MAAM,WAAW,SAAS,OAAO;AAAA,MACzC,OAAO;AACL,QAAAA,OAAM,SAAS,SAAS,gBAAgB,eAAe,WAAW,MAAM,OAAO;AAAA,MACjF;AAAA,IACF,OAAO;AACL,UAAI,cAAc,YAAY,SAAS,WAAW,cAAc,YAAY;AAC1E,QAAAA,OAAM,SAAS,SAAS,gBAAgB,eAAe,WAAW,MAAM,OAAO;AAAA,MACjF,OAAO;AACL,QAAAA,OAAM,MAAM,WAAW,SAAS,OAAO;AAAA,MACzC;AAAA,IACF;AACA,qBAAiBA,MAAK;AAAA,EACxB,GAAG,CAAC,MAAM,QAAQ,SAAS,CAAC;AAE5B,MAAI,CAAC,QAAQ,CAAC,UAAU,QAAS,QAAO;AAExC,QAAM,OAAO,UAAU,QAAQ,sBAAsB;AACrD,QAAM,eAAoC;AAAA,IACxC,UAAU;AAAA,IACV,MAAM,KAAK,OAAO,OAAO;AAAA,IACzB,QAAQ;AAAA,IACR,YAAY,cAAc,QAAQ,OAAO,YAAY;AAAA,IACrD,GAAI,WAAW,UACX,EAAE,QAAQ,SAAS,gBAAgB,eAAe,KAAK,MAAM,OAAO,QAAQ,IAC5E,EAAE,KAAK,KAAK,SAAS,OAAO,QAAQ;AAAA,EAC1C;AAEA,QAAM,QAAQ,cAAc,QAAQ,OAAO,gBAAgB;AAC3D,QAAM,iBAAiB,GAAG,EAAE,QAAQ,IAAI,EAAE,MAAM;AAEhD,SAAOC;AAAA,IACL,gBAAAL,KAAC,SAAI,KAAK,YAAY,OAAc,WAAW,aAAa,gBACzD,UACH;AAAA,IACA,SAAS;AAAA,EACX;AACF;;;ACvCM,SACE,OAAAM,MADF,QAAAC,aAAA;AAtBN,SAAS,UAAU,OAAe,KAAa,KAAkC;AAC/E,QAAM,MAAM,QAAQ,MAAM,KAAM,QAAQ,QAAQ,MAAM,OAAQ;AAC9D,SAAO;AAAA,IACL,YAAY,sFAAsF,GAAG,+BAA+B,GAAG;AAAA,EACzI;AACF;AAEO,SAAS,OAA2C,OAAuB;AAChF,QAAM,IAAI,SAAS;AACnB,QAAM,EAAE,UAAU,UAAU,IAAI;AAEhC,MAAI,MAAM,SAAS;AACjB,UAAM,EAAE,SAAS,QAAQ,OAAAC,QAAO,UAAAC,UAAS,IAAI;AAC7C,UAAM,QAAQ,QAAQ,QAAQD,MAAK;AACnC,UAAM,eAAe,UAAU,KAAK,IAAI;AAExC,UAAM,WAAW,CAAC,WAAsB;AACtC,UAAI,OAAO,WAAW,WAAY,QAAO,OAAO,MAAM;AACtD,aAAO,OAAO,MAAM;AAAA,IACtB;AAEA,WACE,gBAAAD,MAAC,SAAI,WACH;AAAA,sBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK,QAAQ,SAAS;AAAA,UACtB,MAAM;AAAA,UACN,OAAO;AAAA,UACP,UAAU,CAAC,MAAMG,UAAS,QAAQ,SAAS,EAAE,OAAO,KAAK,CAAC,CAAC;AAAA,UAC3D,WAAU;AAAA,UACV,OAAO,UAAU,cAAc,GAAG,QAAQ,SAAS,CAAC;AAAA,UACpD;AAAA;AAAA,MACF;AAAA,MACC,UACC,gBAAAH,KAAC,SAAI,WAAW,qCAAqC,EAAE,SAAS,IAC7D,kBAAQ,IAAI,CAAC,WACZ,gBAAAA,KAAC,UAA2B,mBAAS,MAAM,KAAhC,OAAO,MAAM,CAAqB,CAC9C,GACH;AAAA,OAEJ;AAAA,EAEJ;AAEA,QAAM,EAAE,KAAK,KAAK,MAAM,OAAO,SAAS,IAAI;AAE5C,SACE,gBAAAA,KAAC,SAAI,WACH,0BAAAA;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU,CAAC,MAAM,SAAS,WAAW,EAAE,OAAO,KAAK,CAAC;AAAA,MACpD,WAAU;AAAA,MACV,OAAO,UAAU,OAAO,KAAK,GAAG;AAAA,MAChC;AAAA;AAAA,EACF,GACF;AAEJ;;;ACrGA,SAAS,aAAAI,YAAW,YAAAC,iBAAgC;AA8BhD,mBACE,OAAAC,MADF,QAAAC,aAAA;AArBG,SAAS,eAAe,EAAE,MAAM,SAAS,SAAS,GAAwB;AAC/E,QAAM,IAAI,SAAS;AACnB,QAAM,CAAC,SAAS,UAAU,IAAIC,UAAS,KAAK;AAC5C,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,KAAK;AAE5C,EAAAC,WAAU,MAAM;AACd,QAAI,MAAM;AACR,iBAAW,IAAI;AACf,4BAAsB,MAAM;AAC1B,8BAAsB,MAAM,WAAW,IAAI,CAAC;AAAA,MAC9C,CAAC;AAAA,IACH,OAAO;AACL,iBAAW,KAAK;AAChB,YAAM,QAAQ,WAAW,MAAM,WAAW,KAAK,GAAG,GAAG;AACrD,aAAO,MAAM,aAAa,KAAK;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,IAAI,CAAC;AAET,MAAI,CAAC,QAAS,QAAO;AAErB,SACE,gBAAAF,MAAA,YACE;AAAA,oBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,sDAAsD,EAAE,OAAO,IACxE,UAAU,gBAAgB,WAC5B;AAAA,QACA,SAAS;AAAA;AAAA,IACX;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,qCAAqC,EAAE,OAAO,2DACvD,UAAU,kBAAkB,kBAC9B;AAAA,QACA,OAAO,EAAE,YAAY,2BAA2B;AAAA,QAE/C;AAAA;AAAA,IACH;AAAA,KACF;AAEJ;;;ACqBM,gBAAAI,OAcA,QAAAC,aAdA;AArDN,IAAM,aAID;AAAA,EACH,IAAI,EAAE,OAAO,WAAW,MAAM,WAAW,eAAe,gBAAgB;AAAA,EACxE,IAAI,EAAE,OAAO,YAAY,MAAM,WAAW,eAAe,gBAAgB;AAAA,EACzE,IAAI,EAAE,OAAO,YAAY,MAAM,WAAW,eAAe,gBAAgB;AAC3E;AAEO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA,GAAG;AACL,GAAsB;AACpB,QAAM,IAAI,SAAS;AACnB,QAAM,SAAS,WAAW,IAAI;AAE9B,QAAM,cAAc,MAAM;AACxB,QAAI,CAAC,SAAU,UAAS,CAAC,OAAO;AAAA,EAClC;AAEA,QAAM,gBAAgB,CAAC,MAA2B;AAChD,QAAI,EAAE,QAAQ,OAAO,EAAE,QAAQ,SAAS;AACtC,QAAE,eAAe;AACjB,UAAI,CAAC,SAAU,UAAS,CAAC,OAAO;AAAA,IAClC;AAAA,EACF;AAEA,QAAM,SACJ,gBAAAD;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,MAAK;AAAA,MACL,gBAAc;AAAA,MACd,cAAY;AAAA,MACZ;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT,WAAW;AAAA,MACX,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA,OAAO;AAAA,QACP,UAAU,EAAE,WAAW,EAAE;AAAA,QACzB,WAAW,kCAAkC;AAAA,MAC/C,EAAE,KAAK,GAAG;AAAA,MACT,GAAG;AAAA,MAEJ,0BAAAA;AAAA,QAAC;AAAA;AAAA,UACC,WAAW;AAAA,YACT;AAAA,YACA,OAAO;AAAA,YACP,UAAU,OAAO,gBAAgB;AAAA,UACnC,EAAE,KAAK,GAAG;AAAA;AAAA,MACZ;AAAA;AAAA,EACF;AAGF,MAAI,CAAC,MAAO,QAAO;AAEnB,SACE,gBAAAC,MAAC,SAAI,WAAW,CAAC,2CAA2C,WAAW,eAAe,EAAE,EAAE,KAAK,GAAG,GAChG;AAAA,oBAAAA,MAAC,SAAI,WAAU,yBACb;AAAA,sBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,WAAW;AAAA,YACT;AAAA,YACA,EAAE;AAAA,YACF,WAAW,uBAAuB;AAAA,UACpC,EAAE,KAAK,GAAG;AAAA,UACV,SAAS;AAAA,UAER;AAAA;AAAA,MACH;AAAA,MACC,eACC,gBAAAA,MAAC,UAAK,WAAW,kBAAkB,EAAE,SAAS,IAAK,uBAAY;AAAA,OAEnE;AAAA,IACC;AAAA,KACH;AAEJ;;;ACzGA,SAAS,YAAAE,WAAU,aAAAC,kBAAiB;AAE7B,SAAS,YAAe,OAAU,OAAkB;AACzD,QAAM,CAAC,gBAAgB,iBAAiB,IAAID,UAAY,KAAK;AAE7D,EAAAC,WAAU,MAAM;AACd,UAAM,UAAU,WAAW,MAAM;AAC/B,wBAAkB,KAAK;AAAA,IACzB,GAAG,KAAK;AAER,WAAO,MAAM;AACX,mBAAa,OAAO;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,OAAO,KAAK,CAAC;AAEjB,SAAO;AACT;;;ACZO,SAAS,gBAAgB,YAA4B;AAC1D,QAAM,OAAO,IAAI,KAAK,UAAU;AAChC,QAAM,OAAO,KAAK,mBAAmB,SAAS,EAAE,MAAM,WAAW,QAAQ,WAAW,QAAQ,KAAK,CAAC;AAClG,QAAM,UAAU,KAAK,mBAAmB,SAAS,EAAE,SAAS,QAAQ,CAAC;AACrE,QAAM,QAAQ,KAAK,SAAS,IAAI;AAChC,QAAM,MAAM,KAAK,QAAQ;AACzB,QAAM,OAAO,KAAK,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE;AACnD,SAAO,GAAG,IAAI,IAAI,OAAO,IAAI,KAAK,IAAI,GAAG,IAAI,IAAI;AACnD;AAEO,SAAS,gBAAgB,YAA4B;AAC1D,QAAM,OAAO,IAAI,KAAK,UAAU;AAChC,QAAM,MAAM,oBAAI,KAAK;AACrB,QAAM,SAAS,IAAI,QAAQ,IAAI,KAAK,QAAQ;AAC5C,QAAM,WAAW,KAAK,MAAM,SAAS,GAAK;AAC1C,QAAM,YAAY,KAAK,MAAM,SAAS,IAAO;AAC7C,QAAM,WAAW,KAAK,MAAM,SAAS,KAAQ;AAE7C,MAAI,WAAW,EAAG,QAAO;AACzB,MAAI,WAAW,GAAI,QAAO,GAAG,QAAQ;AACrC,MAAI,YAAY,GAAI,QAAO,GAAG,SAAS;AACvC,MAAI,WAAW,EAAG,QAAO,GAAG,QAAQ;AACpC,MAAI,WAAW,IAAI;AACjB,UAAM,QAAQ,KAAK,MAAM,WAAW,CAAC;AACrC,WAAO,GAAG,KAAK;AAAA,EACjB;AACA,SAAO,KAAK,mBAAmB,SAAS,EAAE,OAAO,SAAS,KAAK,WAAW,MAAM,UAAU,CAAC;AAC7F;;;AC5BA,eAAsB,eAAe,KAAoE;AACvG,MAAI;AACF,QAAI,UAAU,OAAO;AACnB,YAAM,UAAU,MAAM,EAAE,IAAI,CAAC;AAC7B,aAAO;AAAA,IACT;AAAA,EACF,SAAS,KAAK;AACZ,QAAK,KAAsB,SAAS,aAAc,QAAO;AAAA,EAC3D;AACA,QAAM,SAAS,MAAM,gBAAgB,GAAG;AACxC,SAAO,SAAS,WAAW;AAC7B;AAKA,eAAsB,gBAAgB,MAAgC;AACpE,MAAI;AACF,QAAI,UAAU,WAAW,WAAW;AAClC,YAAM,UAAU,UAAU,UAAU,IAAI;AACxC,aAAO;AAAA,IACT;AAAA,EACF,QAAQ;AAAA,EAAiC;AAEzC,QAAM,WAAW,SAAS,cAAc,UAAU;AAClD,WAAS,QAAQ;AACjB,WAAS,MAAM,WAAW;AAC1B,WAAS,MAAM,UAAU;AACzB,WAAS,KAAK,YAAY,QAAQ;AAClC,WAAS,OAAO;AAChB,MAAI;AACF,aAAS,YAAY,MAAM;AAC3B,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT,UAAE;AACA,aAAS,KAAK,YAAY,QAAQ;AAAA,EACpC;AACF;",
6
+ "names": ["jsx", "jsx", "jsxs", "jsx", "jsxs", "jsx", "jsxs", "useEffect", "jsx", "jsxs", "useEffect", "useEffect", "useRef", "useState", "createPortal", "jsx", "useRef", "useState", "useEffect", "style", "createPortal", "jsx", "jsxs", "value", "onChange", "useEffect", "useState", "jsx", "jsxs", "useState", "useEffect", "jsx", "jsxs", "useState", "useEffect"]
7
+ }
@@ -0,0 +1,61 @@
1
+ import { type ReactNode } from 'react';
2
+ /** Pre-composed Tailwind class strings for component roles.
3
+ * Components consume these via `useTheme()` — they never reference
4
+ * raw color classes directly. */
5
+ export interface Theme {
6
+ page: string;
7
+ sidebar: string;
8
+ card: string;
9
+ elevated: string;
10
+ hover: string;
11
+ active: string;
12
+ border: string;
13
+ borderSubtle: string;
14
+ input: string;
15
+ inputFocus: string;
16
+ textPrimary: string;
17
+ textSecondary: string;
18
+ textMuted: string;
19
+ buttonPrimary: string;
20
+ buttonSecondary: string;
21
+ buttonGhost: string;
22
+ buttonDanger: string;
23
+ buttonSuccess: string;
24
+ buttonWarning: string;
25
+ overlay: string;
26
+ toggleOn: string;
27
+ toggleOff: string;
28
+ sliderTrack: string;
29
+ sliderFill: string;
30
+ tabActive: string;
31
+ tabInactive: string;
32
+ menuItem: string;
33
+ menuItemHover: string;
34
+ menuItemDanger: string;
35
+ messageSelf: string;
36
+ messageOther: string;
37
+ messageAgent: string;
38
+ messageSystem: string;
39
+ statusOnline: string;
40
+ statusOffline: string;
41
+ statusAway: string;
42
+ badgeDefault: string;
43
+ badgeSuccess: string;
44
+ badgeWarning: string;
45
+ badgeDanger: string;
46
+ badgeInfo: string;
47
+ notificationBadge: string;
48
+ notificationUnread: string;
49
+ notificationRead: string;
50
+ }
51
+ export declare const themes: {
52
+ readonly dark: Theme;
53
+ readonly light: Theme;
54
+ };
55
+ export type ThemeName = keyof typeof themes;
56
+ export declare function ThemingProvider({ theme, children, }: {
57
+ theme?: ThemeName;
58
+ children: ReactNode;
59
+ }): import("react/jsx-runtime").JSX.Element;
60
+ export declare function useTheme(): Theme;
61
+ //# sourceMappingURL=theming.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theming.d.ts","sourceRoot":"","sources":["../../src/lib/theming.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAEjE;;kCAEkC;AAClC,MAAM,WAAW,KAAK;IAEpB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAGlB,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IAGjB,aAAa,EAAE,MAAM,CAAA;IACrB,eAAe,EAAE,MAAM,CAAA;IACvB,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;IAGrB,OAAO,EAAE,MAAM,CAAA;IAGf,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IAGjB,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAGlB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IAGnB,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE,MAAM,CAAA;IAGtB,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IAGrB,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;IAGlB,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IAGjB,iBAAiB,EAAE,MAAM,CAAA;IACzB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,gBAAgB,EAAE,MAAM,CAAA;CACzB;AAkJD,eAAO,MAAM,MAAM;;;CAAkD,CAAA;AACrE,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,MAAM,CAAA;AAI3C,wBAAgB,eAAe,CAAC,EAC9B,KAAc,EACd,QAAQ,GACT,EAAE;IACD,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,QAAQ,EAAE,SAAS,CAAA;CACpB,2CAQA;AAED,wBAAgB,QAAQ,IAAI,KAAK,CAEhC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=theming.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theming.test.d.ts","sourceRoot":"","sources":["../../src/lib/theming.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,88 @@
1
+ @import 'tailwindcss';
2
+
3
+ @theme {
4
+ /* ─── Palette colors — constant across all themes ─── */
5
+ --color-brand-primary: #7C3AED;
6
+ --color-brand-secondary: #2563EB;
7
+ --color-brand-accent: #06B6D4;
8
+ --color-brand-success: #22C55E;
9
+ --color-brand-warning: #F59E0B;
10
+ --color-brand-danger: #EF4444;
11
+ --color-brand-info: #3B82F6;
12
+
13
+ /* ─── Contextual tokens — dark theme defaults ───
14
+ Tailwind v4 generates utility classes from these
15
+ (bg-bg-page, text-text-primary, border-border-default, etc.)
16
+ Light theme overrides below via [data-theme='light']. */
17
+
18
+ /* Backgrounds */
19
+ --color-bg-page: #0f172a;
20
+ --color-bg-card: #1e293b;
21
+ --color-bg-sidebar: #0f172a;
22
+ --color-bg-elevated: #334155;
23
+ --color-bg-hover: #334155;
24
+ --color-bg-active: #475569;
25
+ --color-bg-input: #1e293b;
26
+
27
+ /* Text */
28
+ --color-text-primary: #f8fafc;
29
+ --color-text-secondary: #94a3b8;
30
+ --color-text-muted: #64748b;
31
+ --color-text-inverse: #0f172a;
32
+
33
+ /* Borders */
34
+ --color-border-default: #334155;
35
+ --color-border-subtle: #1e293b;
36
+ --color-border-strong: #475569;
37
+ }
38
+
39
+ /* ─── Light theme overrides ─── */
40
+ [data-theme='light'] {
41
+ --color-bg-page: #ffffff;
42
+ --color-bg-card: #ffffff;
43
+ --color-bg-sidebar: #f8fafc;
44
+ --color-bg-elevated: #f1f5f9;
45
+ --color-bg-hover: #f1f5f9;
46
+ --color-bg-active: #e2e8f0;
47
+ --color-bg-input: #ffffff;
48
+ --color-text-primary: #0f172a;
49
+ --color-text-secondary: #475569;
50
+ --color-text-muted: #94a3b8;
51
+ --color-text-inverse: #f8fafc;
52
+ --color-border-default: #e2e8f0;
53
+ --color-border-subtle: #f1f5f9;
54
+ --color-border-strong: #cbd5e1;
55
+ }
56
+
57
+ /* ─── Slider range input styling ─── */
58
+ .slider-styled {
59
+ -webkit-appearance: none;
60
+ appearance: none;
61
+ height: 6px;
62
+ border-radius: 9999px;
63
+ outline: none;
64
+ cursor: pointer;
65
+ }
66
+ .slider-styled::-webkit-slider-thumb {
67
+ -webkit-appearance: none;
68
+ appearance: none;
69
+ width: 16px;
70
+ height: 16px;
71
+ border-radius: 9999px;
72
+ background: white;
73
+ box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.3);
74
+ cursor: pointer;
75
+ }
76
+ .slider-styled::-moz-range-thumb {
77
+ width: 16px;
78
+ height: 16px;
79
+ border-radius: 9999px;
80
+ background: white;
81
+ box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.3);
82
+ cursor: pointer;
83
+ border: none;
84
+ }
85
+ .slider-styled:disabled {
86
+ opacity: 0.5;
87
+ cursor: not-allowed;
88
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Share a URL via the native share sheet (mobile) or copy to clipboard (desktop).
3
+ */
4
+ export declare function shareOrCopyUrl(url: string): Promise<'shared' | 'copied' | 'cancelled' | 'failed'>;
5
+ /**
6
+ * Copy text to the clipboard with fallback for insecure contexts.
7
+ */
8
+ export declare function copyToClipboard(text: string): Promise<boolean>;
9
+ //# sourceMappingURL=clipboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../../src/utils/clipboard.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,CAAC,CAWvG;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAsBpE"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=clipboard.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.test.d.ts","sourceRoot":"","sources":["../../src/utils/clipboard.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Time formatting utilities.
3
+ */
4
+ export declare function formatExactTime(dateString: string): string;
5
+ export declare function getRelativeTime(dateString: string): string;
6
+ //# sourceMappingURL=format-time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-time.d.ts","sourceRoot":"","sources":["../../src/utils/format-time.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAQ1D;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAiB1D"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=format-time.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-time.test.d.ts","sourceRoot":"","sources":["../../src/utils/format-time.test.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@prmichaelsen/agentbase-ux",
3
+ "version": "0.0.2",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.js",
10
+ "types": "./dist/index.d.ts"
11
+ },
12
+ "./tokens.css": "./dist/tokens.css"
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "scripts": {
18
+ "build": "node esbuild.build.js",
19
+ "typecheck": "tsc --noEmit",
20
+ "test": "vitest run",
21
+ "test:watch": "vitest",
22
+ "clean": "rm -rf dist",
23
+ "prepublishOnly": "npm run clean && npm run build"
24
+ },
25
+ "peerDependencies": {
26
+ "react": "^18.0.0 || ^19.0.0",
27
+ "react-dom": "^18.0.0 || ^19.0.0"
28
+ },
29
+ "dependencies": {
30
+ "lucide-react": "^0.460.0"
31
+ },
32
+ "devDependencies": {
33
+ "@testing-library/jest-dom": "^6.9.1",
34
+ "@testing-library/react": "^16.3.2",
35
+ "@testing-library/user-event": "^14.6.1",
36
+ "@types/react": "^19.0.0",
37
+ "@types/react-dom": "^19.0.0",
38
+ "esbuild": "^0.25.0",
39
+ "jsdom": "^29.0.0",
40
+ "typescript": "^5.7.0",
41
+ "vitest": "^4.1.0"
42
+ }
43
+ }