@annondeveloper/ui-kit 0.1.0 → 0.2.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.
Files changed (65) hide show
  1. package/dist/{chunk-5OKSXPWK.js → chunk-2DWZVHZS.js} +2 -2
  2. package/dist/chunk-2DWZVHZS.js.map +1 -0
  3. package/dist/form.d.ts +6 -6
  4. package/dist/form.js +1 -1
  5. package/dist/form.js.map +1 -1
  6. package/dist/index.d.ts +508 -52
  7. package/dist/index.js +2927 -4
  8. package/dist/index.js.map +1 -1
  9. package/dist/{select-nnBJUO8U.d.ts → select-B2wXqqSM.d.ts} +2 -2
  10. package/package.json +1 -1
  11. package/src/components/animated-counter.tsx +2 -1
  12. package/src/components/avatar.tsx +2 -1
  13. package/src/components/badge.tsx +3 -2
  14. package/src/components/button.tsx +3 -2
  15. package/src/components/card.tsx +13 -12
  16. package/src/components/checkbox.tsx +3 -2
  17. package/src/components/color-input.tsx +414 -0
  18. package/src/components/command-bar.tsx +434 -0
  19. package/src/components/confidence-bar.tsx +115 -0
  20. package/src/components/confirm-dialog.tsx +2 -1
  21. package/src/components/copy-block.tsx +229 -0
  22. package/src/components/data-table.tsx +2 -1
  23. package/src/components/diff-viewer.tsx +319 -0
  24. package/src/components/dropdown-menu.tsx +2 -1
  25. package/src/components/empty-state.tsx +2 -1
  26. package/src/components/filter-pill.tsx +2 -1
  27. package/src/components/form-input.tsx +5 -4
  28. package/src/components/heatmap-calendar.tsx +213 -0
  29. package/src/components/infinite-scroll.tsx +243 -0
  30. package/src/components/kanban-column.tsx +198 -0
  31. package/src/components/live-feed.tsx +220 -0
  32. package/src/components/log-viewer.tsx +2 -1
  33. package/src/components/metric-card.tsx +2 -1
  34. package/src/components/notification-stack.tsx +226 -0
  35. package/src/components/pipeline-stage.tsx +2 -1
  36. package/src/components/popover.tsx +2 -1
  37. package/src/components/port-status-grid.tsx +2 -1
  38. package/src/components/progress.tsx +2 -1
  39. package/src/components/radio-group.tsx +2 -1
  40. package/src/components/realtime-value.tsx +283 -0
  41. package/src/components/select.tsx +2 -1
  42. package/src/components/severity-timeline.tsx +2 -1
  43. package/src/components/sheet.tsx +2 -1
  44. package/src/components/skeleton.tsx +4 -3
  45. package/src/components/slider.tsx +2 -1
  46. package/src/components/smart-table.tsx +383 -0
  47. package/src/components/sortable-list.tsx +268 -0
  48. package/src/components/sparkline.tsx +2 -1
  49. package/src/components/status-badge.tsx +2 -1
  50. package/src/components/status-pulse.tsx +2 -1
  51. package/src/components/step-wizard.tsx +372 -0
  52. package/src/components/streaming-text.tsx +163 -0
  53. package/src/components/success-checkmark.tsx +2 -1
  54. package/src/components/tabs.tsx +2 -1
  55. package/src/components/threshold-gauge.tsx +2 -1
  56. package/src/components/time-range-selector.tsx +2 -1
  57. package/src/components/toast.tsx +2 -1
  58. package/src/components/toggle-switch.tsx +2 -1
  59. package/src/components/tooltip.tsx +2 -1
  60. package/src/components/truncated-text.tsx +2 -1
  61. package/src/components/typing-indicator.tsx +123 -0
  62. package/src/components/uptime-tracker.tsx +2 -1
  63. package/src/components/utilization-bar.tsx +2 -1
  64. package/src/utils.ts +1 -1
  65. package/dist/chunk-5OKSXPWK.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { cn, clamp } from './chunk-5OKSXPWK.js';
2
- export { Checkbox, FormInput, INPUT_CLS, LABEL_CLS, Select, TEXTAREA_CLS, ToggleSwitch, clamp, cn, defaultUtilColorMap, fmtBps, fmtBytes, fmtCompact, fmtDuration, fmtPct, fmtRelative, fmtSpeed, fmtUptime, fmtUtil, stripCidr, utilColor } from './chunk-5OKSXPWK.js';
1
+ import { cn, clamp } from './chunk-2DWZVHZS.js';
2
+ export { Checkbox, FormInput, INPUT_CLS, LABEL_CLS, Select, TEXTAREA_CLS, ToggleSwitch, clamp, cn, defaultUtilColorMap, fmtBps, fmtBytes, fmtCompact, fmtDuration, fmtPct, fmtRelative, fmtSpeed, fmtUptime, fmtUtil, stripCidr, utilColor } from './chunk-2DWZVHZS.js';
3
3
  import { forwardRef, useRef, useState, useEffect, useCallback, useMemo } from 'react';
4
- import { Loader2, Check, Copy, AlertTriangle, Search, X, Filter, List, AlignJustify, LayoutList, Columns3, Download, ChevronUp, ChevronDown, ChevronsUpDown, TrendingUp, TrendingDown, ArrowDown, ChevronRight, GripVertical, Eye, EyeOff } from 'lucide-react';
4
+ import { Loader2, Check, Copy, AlertTriangle, Search, X, Filter, List, AlignJustify, LayoutList, Columns3, Download, ChevronUp, ChevronDown, ChevronsUpDown, TrendingUp, TrendingDown, ArrowDown, ChevronRight, Play, Pause, XCircle, CheckCircle2, Info, Plus, Sparkles, Hash, Regex, ArrowUp, Minus, WifiOff, Clock, CornerDownLeft, GripVertical, ChevronLeft, Eye, EyeOff } from 'lucide-react';
5
5
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
6
6
  import * as Tooltip from '@radix-ui/react-tooltip';
7
7
  import * as AlertDialog from '@radix-ui/react-alert-dialog';
@@ -2806,7 +2806,2930 @@ function UptimeTracker({
2806
2806
  ] })
2807
2807
  ] });
2808
2808
  }
2809
+ function formatSegments(text) {
2810
+ const segments = [];
2811
+ const regex = /(\*\*(.+?)\*\*|`([^`]+?)`)/g;
2812
+ let lastIndex = 0;
2813
+ let match;
2814
+ while ((match = regex.exec(text)) !== null) {
2815
+ if (match.index > lastIndex) {
2816
+ segments.push(text.slice(lastIndex, match.index));
2817
+ }
2818
+ if (match[2]) {
2819
+ segments.push(
2820
+ /* @__PURE__ */ jsx("strong", { className: "font-semibold", children: match[2] }, match.index)
2821
+ );
2822
+ } else if (match[3]) {
2823
+ segments.push(
2824
+ /* @__PURE__ */ jsx(
2825
+ "code",
2826
+ {
2827
+ className: "rounded px-1 py-0.5 text-[0.875em] bg-[hsl(var(--bg-overlay))] text-[hsl(var(--brand-primary))] font-mono",
2828
+ children: match[3]
2829
+ },
2830
+ match.index
2831
+ )
2832
+ );
2833
+ }
2834
+ lastIndex = match.index + match[0].length;
2835
+ }
2836
+ if (lastIndex < text.length) {
2837
+ segments.push(text.slice(lastIndex));
2838
+ }
2839
+ return segments;
2840
+ }
2841
+ function StreamingText({
2842
+ text,
2843
+ isStreaming,
2844
+ speed = 500,
2845
+ showCursor = true,
2846
+ onComplete,
2847
+ className
2848
+ }) {
2849
+ const reduced = useReducedMotion();
2850
+ const containerRef = useRef(null);
2851
+ const prevStreamingRef = useRef(isStreaming);
2852
+ const [copied, setCopied] = useState(false);
2853
+ useEffect(() => {
2854
+ if (prevStreamingRef.current && !isStreaming) {
2855
+ onComplete?.();
2856
+ }
2857
+ prevStreamingRef.current = isStreaming;
2858
+ }, [isStreaming, onComplete]);
2859
+ useEffect(() => {
2860
+ if (isStreaming && containerRef.current) {
2861
+ const el = containerRef.current;
2862
+ el.scrollTop = el.scrollHeight;
2863
+ }
2864
+ }, [text, isStreaming]);
2865
+ const handleCopy = useCallback(() => {
2866
+ void navigator.clipboard.writeText(text).then(() => {
2867
+ setCopied(true);
2868
+ setTimeout(() => setCopied(false), 2e3);
2869
+ });
2870
+ }, [text]);
2871
+ const formatted = formatSegments(text);
2872
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative", className), children: [
2873
+ /* @__PURE__ */ jsxs(
2874
+ "div",
2875
+ {
2876
+ ref: containerRef,
2877
+ className: "overflow-y-auto text-[hsl(var(--text-primary))] leading-relaxed whitespace-pre-wrap break-words",
2878
+ children: [
2879
+ formatted,
2880
+ showCursor && isStreaming && /* @__PURE__ */ jsx(
2881
+ "span",
2882
+ {
2883
+ className: "inline-block w-[2px] h-[1.1em] align-text-bottom ml-0.5 bg-[hsl(var(--brand-primary))]",
2884
+ style: reduced ? { opacity: 1 } : {
2885
+ animation: `streaming-cursor-blink ${speed}ms step-end infinite`
2886
+ }
2887
+ }
2888
+ ),
2889
+ /* @__PURE__ */ jsx(AnimatePresence, { children: showCursor && !isStreaming && text.length > 0 && /* @__PURE__ */ jsx(
2890
+ motion.span,
2891
+ {
2892
+ className: "inline-block w-[2px] h-[1.1em] align-text-bottom ml-0.5 bg-[hsl(var(--brand-primary))]",
2893
+ initial: { opacity: 1 },
2894
+ exit: { opacity: 0 },
2895
+ transition: { duration: reduced ? 0 : 0.4 }
2896
+ }
2897
+ ) })
2898
+ ]
2899
+ }
2900
+ ),
2901
+ /* @__PURE__ */ jsx(AnimatePresence, { children: !isStreaming && text.length > 0 && /* @__PURE__ */ jsx(
2902
+ motion.button,
2903
+ {
2904
+ type: "button",
2905
+ initial: { opacity: 0, scale: 0.8 },
2906
+ animate: { opacity: 1, scale: 1 },
2907
+ exit: { opacity: 0, scale: 0.8 },
2908
+ transition: { duration: reduced ? 0 : 0.2 },
2909
+ onClick: handleCopy,
2910
+ className: cn(
2911
+ "absolute top-0 right-0 p-1.5 rounded-lg",
2912
+ "text-[hsl(var(--text-tertiary))] hover:text-[hsl(var(--text-primary))]",
2913
+ "bg-[hsl(var(--bg-surface))]/80 hover:bg-[hsl(var(--bg-elevated))]",
2914
+ "transition-colors duration-150 cursor-pointer"
2915
+ ),
2916
+ "aria-label": copied ? "Copied" : "Copy to clipboard",
2917
+ children: copied ? /* @__PURE__ */ jsx(Check, { className: "size-3.5" }) : /* @__PURE__ */ jsx(Copy, { className: "size-3.5" })
2918
+ }
2919
+ ) }),
2920
+ showCursor && isStreaming && !reduced && /* @__PURE__ */ jsx("style", { children: `
2921
+ @keyframes streaming-cursor-blink {
2922
+ 0%, 100% { opacity: 1; }
2923
+ 50% { opacity: 0; }
2924
+ }
2925
+ ` })
2926
+ ] });
2927
+ }
2928
+ var DOT_SIZES = { sm: "size-1.5", md: "size-2" };
2929
+ var FONT_SIZES = { sm: "text-xs", md: "text-sm" };
2930
+ function TypingIndicator({
2931
+ label,
2932
+ variant = "dots",
2933
+ size = "md",
2934
+ className
2935
+ }) {
2936
+ const reduced = useReducedMotion();
2937
+ return /* @__PURE__ */ jsxs(
2938
+ "div",
2939
+ {
2940
+ className: cn(
2941
+ "inline-flex items-center gap-2 text-[hsl(var(--text-secondary))]",
2942
+ FONT_SIZES[size],
2943
+ className
2944
+ ),
2945
+ role: "status",
2946
+ "aria-label": label ?? "Typing",
2947
+ children: [
2948
+ variant === "dots" && /* @__PURE__ */ jsx(BouncingDots, { size, reduced: !!reduced }),
2949
+ variant === "pulse" && /* @__PURE__ */ jsx(PulseRing, { size, reduced: !!reduced }),
2950
+ variant === "text" && /* @__PURE__ */ jsx(AnimatedEllipsis, { reduced: !!reduced }),
2951
+ label && /* @__PURE__ */ jsx("span", { children: label })
2952
+ ]
2953
+ }
2954
+ );
2955
+ }
2956
+ function BouncingDots({ size, reduced }) {
2957
+ const dotClass = cn("rounded-full bg-current", DOT_SIZES[size]);
2958
+ if (reduced) {
2959
+ return /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
2960
+ /* @__PURE__ */ jsx("span", { className: dotClass }),
2961
+ /* @__PURE__ */ jsx("span", { className: dotClass }),
2962
+ /* @__PURE__ */ jsx("span", { className: dotClass })
2963
+ ] });
2964
+ }
2965
+ return /* @__PURE__ */ jsx("span", { className: "inline-flex items-center gap-1", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx(
2966
+ motion.span,
2967
+ {
2968
+ className: dotClass,
2969
+ animate: { y: [0, -4, 0] },
2970
+ transition: {
2971
+ duration: 0.6,
2972
+ repeat: Infinity,
2973
+ delay: i * 0.15,
2974
+ ease: "easeInOut"
2975
+ }
2976
+ },
2977
+ i
2978
+ )) });
2979
+ }
2980
+ function PulseRing({ size, reduced }) {
2981
+ const ringSize = size === "sm" ? "size-4" : "size-5";
2982
+ const dotSize = size === "sm" ? "size-2" : "size-2.5";
2983
+ return /* @__PURE__ */ jsxs("span", { className: cn("relative inline-flex items-center justify-center", ringSize), children: [
2984
+ !reduced && /* @__PURE__ */ jsx(
2985
+ motion.span,
2986
+ {
2987
+ className: cn("absolute inset-0 rounded-full border border-current"),
2988
+ animate: { scale: [1, 1.8], opacity: [0.6, 0] },
2989
+ transition: { duration: 1.2, repeat: Infinity, ease: "easeOut" }
2990
+ }
2991
+ ),
2992
+ /* @__PURE__ */ jsx("span", { className: cn("rounded-full bg-current", dotSize) })
2993
+ ] });
2994
+ }
2995
+ function AnimatedEllipsis({ reduced }) {
2996
+ if (reduced) {
2997
+ return /* @__PURE__ */ jsx("span", { children: "..." });
2998
+ }
2999
+ return /* @__PURE__ */ jsx("span", { className: "inline-flex w-[1.2em]", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx(
3000
+ motion.span,
3001
+ {
3002
+ animate: { opacity: [0, 1, 0] },
3003
+ transition: {
3004
+ duration: 1.2,
3005
+ repeat: Infinity,
3006
+ delay: i * 0.3,
3007
+ ease: "easeInOut"
3008
+ },
3009
+ children: "."
3010
+ },
3011
+ i
3012
+ )) });
3013
+ }
3014
+ var SIZE_CLASSES = {
3015
+ sm: "h-1.5",
3016
+ md: "h-2.5"
3017
+ };
3018
+ function getBarColor2(value, thresholds) {
3019
+ if (value < thresholds.low) return "bg-[hsl(var(--status-critical))]";
3020
+ if (value < thresholds.medium) return "bg-[hsl(var(--status-warning))]";
3021
+ return "bg-[hsl(var(--status-ok))]";
3022
+ }
3023
+ function getTextColor(value, thresholds) {
3024
+ if (value < thresholds.low) return "text-[hsl(var(--status-critical))]";
3025
+ if (value < thresholds.medium) return "text-[hsl(var(--status-warning))]";
3026
+ return "text-[hsl(var(--status-ok))]";
3027
+ }
3028
+ function ConfidenceBar({
3029
+ value,
3030
+ label,
3031
+ showPercentage = true,
3032
+ thresholds = { low: 0.3, medium: 0.7 },
3033
+ size = "md",
3034
+ className
3035
+ }) {
3036
+ const reduced = useReducedMotion();
3037
+ const [hovered, setHovered] = useState(false);
3038
+ const clamped = Math.min(1, Math.max(0, value));
3039
+ const pct = clamped * 100;
3040
+ const barColor = getBarColor2(clamped, thresholds);
3041
+ const textColor = getTextColor(clamped, thresholds);
3042
+ return /* @__PURE__ */ jsxs(
3043
+ "div",
3044
+ {
3045
+ className: cn("w-full", className),
3046
+ onMouseEnter: () => setHovered(true),
3047
+ onMouseLeave: () => setHovered(false),
3048
+ children: [
3049
+ (label || showPercentage) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-1", children: [
3050
+ label && /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-[hsl(var(--text-secondary))]", children: label }),
3051
+ showPercentage && /* @__PURE__ */ jsxs("span", { className: cn("text-xs font-medium tabular-nums", textColor), children: [
3052
+ pct.toFixed(1),
3053
+ "%"
3054
+ ] })
3055
+ ] }),
3056
+ /* @__PURE__ */ jsx(
3057
+ "div",
3058
+ {
3059
+ className: cn(
3060
+ "relative w-full overflow-hidden rounded-full bg-[hsl(var(--bg-overlay))]",
3061
+ SIZE_CLASSES[size]
3062
+ ),
3063
+ role: "meter",
3064
+ "aria-valuenow": pct,
3065
+ "aria-valuemin": 0,
3066
+ "aria-valuemax": 100,
3067
+ "aria-label": label ?? `Confidence: ${pct.toFixed(1)}%`,
3068
+ children: reduced ? /* @__PURE__ */ jsx(
3069
+ "div",
3070
+ {
3071
+ className: cn("h-full rounded-full", barColor),
3072
+ style: { width: `${pct}%` }
3073
+ }
3074
+ ) : /* @__PURE__ */ jsx(
3075
+ motion.div,
3076
+ {
3077
+ className: cn("h-full rounded-full", barColor),
3078
+ initial: { width: 0 },
3079
+ animate: { width: `${pct}%` },
3080
+ transition: { type: "spring", stiffness: 80, damping: 20 }
3081
+ }
3082
+ )
3083
+ }
3084
+ ),
3085
+ hovered && /* @__PURE__ */ jsxs("div", { className: "mt-1 text-[10px] text-[hsl(var(--text-tertiary))] tabular-nums", children: [
3086
+ clamped.toFixed(4),
3087
+ " (",
3088
+ pct.toFixed(2),
3089
+ "%)"
3090
+ ] })
3091
+ ]
3092
+ }
3093
+ );
3094
+ }
3095
+ var TYPE_BORDER = {
3096
+ info: "border-l-[hsl(var(--brand-secondary))]",
3097
+ success: "border-l-[hsl(var(--status-ok))]",
3098
+ warning: "border-l-[hsl(var(--status-warning))]",
3099
+ error: "border-l-[hsl(var(--status-critical))]"
3100
+ };
3101
+ function relativeTime(ts) {
3102
+ const diff = (Date.now() - new Date(ts).getTime()) / 1e3;
3103
+ if (diff < 5) return "now";
3104
+ if (diff < 60) return `${Math.floor(diff)}s ago`;
3105
+ if (diff < 3600) return `${Math.floor(diff / 60)}m ago`;
3106
+ if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`;
3107
+ return `${Math.floor(diff / 86400)}d ago`;
3108
+ }
3109
+ function LiveFeed({
3110
+ items,
3111
+ maxVisible = 50,
3112
+ showTimestamps = true,
3113
+ autoScroll: autoScrollProp = true,
3114
+ onItemClick,
3115
+ emptyMessage = "No events yet",
3116
+ className
3117
+ }) {
3118
+ const reduced = useReducedMotion();
3119
+ const scrollRef = useRef(null);
3120
+ const [paused, setPaused] = useState(false);
3121
+ const [userScrolled, setUserScrolled] = useState(false);
3122
+ const [newCount, setNewCount] = useState(0);
3123
+ const prevCountRef = useRef(items.length);
3124
+ useEffect(() => {
3125
+ const diff = items.length - prevCountRef.current;
3126
+ if (diff > 0 && (paused || userScrolled)) {
3127
+ setNewCount((c) => c + diff);
3128
+ }
3129
+ prevCountRef.current = items.length;
3130
+ }, [items.length, paused, userScrolled]);
3131
+ useEffect(() => {
3132
+ if (autoScrollProp && !paused && !userScrolled && scrollRef.current) {
3133
+ scrollRef.current.scrollTop = 0;
3134
+ }
3135
+ }, [items, autoScrollProp, paused, userScrolled]);
3136
+ const handleScroll = useCallback(() => {
3137
+ if (!scrollRef.current) return;
3138
+ const { scrollTop } = scrollRef.current;
3139
+ setUserScrolled(scrollTop > 40);
3140
+ }, []);
3141
+ const scrollToTop = useCallback(() => {
3142
+ if (scrollRef.current) {
3143
+ scrollRef.current.scrollTop = 0;
3144
+ }
3145
+ setUserScrolled(false);
3146
+ setNewCount(0);
3147
+ }, []);
3148
+ const togglePause = useCallback(() => {
3149
+ setPaused((p) => {
3150
+ if (p) {
3151
+ setNewCount(0);
3152
+ setUserScrolled(false);
3153
+ }
3154
+ return !p;
3155
+ });
3156
+ }, []);
3157
+ const [, setTick] = useState(0);
3158
+ useEffect(() => {
3159
+ if (!showTimestamps) return;
3160
+ const id = setInterval(() => setTick((t) => t + 1), 1e4);
3161
+ return () => clearInterval(id);
3162
+ }, [showTimestamps]);
3163
+ const visibleItems = items.slice(0, maxVisible);
3164
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative flex flex-col", className), children: [
3165
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-3 py-2 border-b border-[hsl(var(--border-subtle))]", children: [
3166
+ /* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-[hsl(var(--text-secondary))]", children: [
3167
+ items.length,
3168
+ " events"
3169
+ ] }),
3170
+ /* @__PURE__ */ jsxs(
3171
+ "button",
3172
+ {
3173
+ type: "button",
3174
+ onClick: togglePause,
3175
+ className: cn(
3176
+ "inline-flex items-center gap-1 px-2 py-1 rounded-md text-xs font-medium cursor-pointer",
3177
+ "text-[hsl(var(--text-secondary))] hover:text-[hsl(var(--text-primary))]",
3178
+ "hover:bg-[hsl(var(--bg-overlay))] transition-colors duration-150"
3179
+ ),
3180
+ "aria-label": paused ? "Resume auto-scroll" : "Pause auto-scroll",
3181
+ children: [
3182
+ paused ? /* @__PURE__ */ jsx(Play, { className: "size-3" }) : /* @__PURE__ */ jsx(Pause, { className: "size-3" }),
3183
+ paused ? "Resume" : "Pause"
3184
+ ]
3185
+ }
3186
+ )
3187
+ ] }),
3188
+ /* @__PURE__ */ jsx(
3189
+ "div",
3190
+ {
3191
+ ref: scrollRef,
3192
+ onScroll: handleScroll,
3193
+ className: "flex-1 overflow-y-auto",
3194
+ children: visibleItems.length === 0 ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-12 text-sm text-[hsl(var(--text-tertiary))]", children: emptyMessage }) : /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: visibleItems.map((item) => /* @__PURE__ */ jsx(
3195
+ motion.div,
3196
+ {
3197
+ layout: !reduced,
3198
+ initial: reduced ? void 0 : { opacity: 0, y: -12, height: 0 },
3199
+ animate: { opacity: 1, y: 0, height: "auto" },
3200
+ exit: reduced ? void 0 : { opacity: 0, height: 0 },
3201
+ transition: { duration: reduced ? 0 : 0.2 },
3202
+ children: /* @__PURE__ */ jsxs(
3203
+ "div",
3204
+ {
3205
+ role: onItemClick ? "button" : void 0,
3206
+ tabIndex: onItemClick ? 0 : void 0,
3207
+ onClick: onItemClick ? () => onItemClick(item) : void 0,
3208
+ onKeyDown: onItemClick ? (e) => {
3209
+ if (e.key === "Enter" || e.key === " ") onItemClick(item);
3210
+ } : void 0,
3211
+ className: cn(
3212
+ "flex items-start gap-3 px-3 py-2.5 border-l-2",
3213
+ "border-b border-b-[hsl(var(--border-subtle))]",
3214
+ TYPE_BORDER[item.type ?? "info"],
3215
+ onItemClick && "cursor-pointer hover:bg-[hsl(var(--bg-surface))] transition-colors duration-100"
3216
+ ),
3217
+ children: [
3218
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0 text-sm text-[hsl(var(--text-primary))]", children: item.content }),
3219
+ showTimestamps && /* @__PURE__ */ jsx(
3220
+ "span",
3221
+ {
3222
+ className: "shrink-0 text-[10px] tabular-nums text-[hsl(var(--text-tertiary))] mt-0.5",
3223
+ title: new Date(item.timestamp).toISOString(),
3224
+ children: relativeTime(item.timestamp)
3225
+ }
3226
+ )
3227
+ ]
3228
+ }
3229
+ )
3230
+ },
3231
+ item.id
3232
+ )) })
3233
+ }
3234
+ ),
3235
+ /* @__PURE__ */ jsx(AnimatePresence, { children: newCount > 0 && (userScrolled || paused) && /* @__PURE__ */ jsxs(
3236
+ motion.button,
3237
+ {
3238
+ type: "button",
3239
+ initial: reduced ? void 0 : { opacity: 0, y: 8 },
3240
+ animate: { opacity: 1, y: 0 },
3241
+ exit: reduced ? void 0 : { opacity: 0, y: 8 },
3242
+ transition: { duration: reduced ? 0 : 0.15 },
3243
+ onClick: scrollToTop,
3244
+ className: cn(
3245
+ "absolute top-12 left-1/2 -translate-x-1/2 z-10",
3246
+ "inline-flex items-center gap-1 px-3 py-1.5 rounded-full",
3247
+ "bg-[hsl(var(--brand-primary))] text-white text-xs font-medium",
3248
+ "shadow-lg cursor-pointer hover:brightness-110 transition-[filter] duration-100"
3249
+ ),
3250
+ children: [
3251
+ /* @__PURE__ */ jsx(ChevronUp, { className: "size-3" }),
3252
+ newCount,
3253
+ " new ",
3254
+ newCount === 1 ? "item" : "items"
3255
+ ]
3256
+ }
3257
+ ) })
3258
+ ] });
3259
+ }
3260
+ function computeDiff(oldLines, newLines) {
3261
+ const m = oldLines.length;
3262
+ const n = newLines.length;
3263
+ const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
3264
+ for (let i2 = 1; i2 <= m; i2++) {
3265
+ for (let j2 = 1; j2 <= n; j2++) {
3266
+ if (oldLines[i2 - 1] === newLines[j2 - 1]) {
3267
+ dp[i2][j2] = dp[i2 - 1][j2 - 1] + 1;
3268
+ } else {
3269
+ dp[i2][j2] = Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
3270
+ }
3271
+ }
3272
+ }
3273
+ const result = [];
3274
+ let i = m;
3275
+ let j = n;
3276
+ while (i > 0 || j > 0) {
3277
+ if (i > 0 && j > 0 && oldLines[i - 1] === newLines[j - 1]) {
3278
+ result.push({ type: "unchanged", content: oldLines[i - 1], oldLineNo: i, newLineNo: j });
3279
+ i--;
3280
+ j--;
3281
+ } else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) {
3282
+ result.push({ type: "added", content: newLines[j - 1], newLineNo: j });
3283
+ j--;
3284
+ } else {
3285
+ result.push({ type: "removed", content: oldLines[i - 1], oldLineNo: i });
3286
+ i--;
3287
+ }
3288
+ }
3289
+ return result.reverse();
3290
+ }
3291
+ var TYPE_BG = {
3292
+ added: "bg-[hsl(var(--status-ok))]/10",
3293
+ removed: "bg-[hsl(var(--status-critical))]/10",
3294
+ unchanged: ""
3295
+ };
3296
+ var TYPE_PREFIX = {
3297
+ added: "+",
3298
+ removed: "-",
3299
+ unchanged: " "
3300
+ };
3301
+ var TYPE_PREFIX_COLOR = {
3302
+ added: "text-[hsl(var(--status-ok))]",
3303
+ removed: "text-[hsl(var(--status-critical))]",
3304
+ unchanged: "text-[hsl(var(--text-tertiary))]"
3305
+ };
3306
+ function DiffViewer({
3307
+ oldValue,
3308
+ newValue,
3309
+ mode = "inline",
3310
+ language,
3311
+ showLineNumbers = true,
3312
+ className
3313
+ }) {
3314
+ const [expandedSections, setExpandedSections] = useState(/* @__PURE__ */ new Set());
3315
+ const diffLines = useMemo(() => {
3316
+ const oldLines = oldValue.split("\n");
3317
+ const newLines = newValue.split("\n");
3318
+ return computeDiff(oldLines, newLines);
3319
+ }, [oldValue, newValue]);
3320
+ const sections = useMemo(() => {
3321
+ const groups = [];
3322
+ let current = [];
3323
+ let currentType = null;
3324
+ let startIdx = 0;
3325
+ for (let i = 0; i < diffLines.length; i++) {
3326
+ const lineType = diffLines[i].type === "unchanged" ? "unchanged" : "changes";
3327
+ if (lineType !== currentType) {
3328
+ if (current.length > 0 && currentType !== null) {
3329
+ groups.push({ type: currentType, lines: current, startIdx });
3330
+ }
3331
+ current = [diffLines[i]];
3332
+ currentType = lineType;
3333
+ startIdx = i;
3334
+ } else {
3335
+ current.push(diffLines[i]);
3336
+ }
3337
+ }
3338
+ if (current.length > 0 && currentType !== null) {
3339
+ groups.push({ type: currentType, lines: current, startIdx });
3340
+ }
3341
+ return groups;
3342
+ }, [diffLines]);
3343
+ const toggleSection = (idx) => {
3344
+ setExpandedSections((prev) => {
3345
+ const next = new Set(prev);
3346
+ if (next.has(idx)) {
3347
+ next.delete(idx);
3348
+ } else {
3349
+ next.add(idx);
3350
+ }
3351
+ return next;
3352
+ });
3353
+ };
3354
+ if (mode === "side-by-side") {
3355
+ return /* @__PURE__ */ jsx(SideBySide, { diffLines, showLineNumbers, language, className });
3356
+ }
3357
+ return /* @__PURE__ */ jsx(
3358
+ "div",
3359
+ {
3360
+ className: cn(
3361
+ "w-full overflow-x-auto rounded-xl border border-[hsl(var(--border-subtle))] bg-[hsl(var(--bg-surface))]",
3362
+ "font-mono text-xs leading-5",
3363
+ className
3364
+ ),
3365
+ "data-language": language,
3366
+ children: sections.map((section, sIdx) => {
3367
+ if (section.type === "unchanged" && section.lines.length > 6 && !expandedSections.has(sIdx)) {
3368
+ const topContext = section.lines.slice(0, 3);
3369
+ const bottomContext = section.lines.slice(-3);
3370
+ const hiddenCount = section.lines.length - 6;
3371
+ return /* @__PURE__ */ jsxs("div", { children: [
3372
+ topContext.map((line, i) => /* @__PURE__ */ jsx(InlineLine, { line, showLineNumbers }, `${sIdx}-t-${i}`)),
3373
+ /* @__PURE__ */ jsxs(
3374
+ "button",
3375
+ {
3376
+ type: "button",
3377
+ onClick: () => toggleSection(sIdx),
3378
+ className: cn(
3379
+ "w-full px-3 py-1 text-center text-[10px]",
3380
+ "text-[hsl(var(--text-tertiary))] bg-[hsl(var(--bg-overlay))]/50",
3381
+ "hover:bg-[hsl(var(--bg-overlay))] cursor-pointer transition-colors duration-100"
3382
+ ),
3383
+ children: [
3384
+ "... ",
3385
+ hiddenCount,
3386
+ " unchanged ",
3387
+ hiddenCount === 1 ? "line" : "lines",
3388
+ " ..."
3389
+ ]
3390
+ }
3391
+ ),
3392
+ bottomContext.map((line, i) => /* @__PURE__ */ jsx(InlineLine, { line, showLineNumbers }, `${sIdx}-b-${i}`))
3393
+ ] }, sIdx);
3394
+ }
3395
+ return /* @__PURE__ */ jsxs("div", { children: [
3396
+ section.type === "unchanged" && expandedSections.has(sIdx) && /* @__PURE__ */ jsxs(
3397
+ "button",
3398
+ {
3399
+ type: "button",
3400
+ onClick: () => toggleSection(sIdx),
3401
+ className: cn(
3402
+ "w-full px-3 py-0.5 text-center text-[10px]",
3403
+ "text-[hsl(var(--text-tertiary))] bg-[hsl(var(--bg-overlay))]/30",
3404
+ "hover:bg-[hsl(var(--bg-overlay))] cursor-pointer transition-colors duration-100"
3405
+ ),
3406
+ children: [
3407
+ "collapse ",
3408
+ section.lines.length,
3409
+ " unchanged lines"
3410
+ ]
3411
+ }
3412
+ ),
3413
+ section.lines.map((line, i) => /* @__PURE__ */ jsx(InlineLine, { line, showLineNumbers }, `${sIdx}-${i}`))
3414
+ ] }, sIdx);
3415
+ })
3416
+ }
3417
+ );
3418
+ }
3419
+ function InlineLine({ line, showLineNumbers }) {
3420
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex", TYPE_BG[line.type]), children: [
3421
+ showLineNumbers && /* @__PURE__ */ jsx("span", { className: "shrink-0 w-9 px-1.5 text-right select-none text-[hsl(var(--text-tertiary))]/50 tabular-nums", children: line.oldLineNo ?? "" }),
3422
+ showLineNumbers && /* @__PURE__ */ jsx("span", { className: "shrink-0 w-9 px-1.5 text-right select-none text-[hsl(var(--text-tertiary))]/50 tabular-nums", children: line.newLineNo ?? "" }),
3423
+ /* @__PURE__ */ jsx("span", { className: cn("shrink-0 w-4 text-center select-none font-bold", TYPE_PREFIX_COLOR[line.type]), children: TYPE_PREFIX[line.type] }),
3424
+ /* @__PURE__ */ jsx("span", { className: "flex-1 px-2 whitespace-pre text-[hsl(var(--text-primary))]", children: line.content })
3425
+ ] });
3426
+ }
3427
+ function SideBySide({
3428
+ diffLines,
3429
+ showLineNumbers,
3430
+ language,
3431
+ className
3432
+ }) {
3433
+ const pairs = [];
3434
+ let i = 0;
3435
+ while (i < diffLines.length) {
3436
+ const line = diffLines[i];
3437
+ if (line.type === "unchanged") {
3438
+ pairs.push({ left: line, right: line });
3439
+ i++;
3440
+ } else if (line.type === "removed") {
3441
+ if (i + 1 < diffLines.length && diffLines[i + 1].type === "added") {
3442
+ pairs.push({ left: line, right: diffLines[i + 1] });
3443
+ i += 2;
3444
+ } else {
3445
+ pairs.push({ left: line });
3446
+ i++;
3447
+ }
3448
+ } else {
3449
+ pairs.push({ right: line });
3450
+ i++;
3451
+ }
3452
+ }
3453
+ return /* @__PURE__ */ jsx(
3454
+ "div",
3455
+ {
3456
+ className: cn(
3457
+ "w-full overflow-x-auto rounded-xl border border-[hsl(var(--border-subtle))] bg-[hsl(var(--bg-surface))]",
3458
+ "font-mono text-xs leading-5",
3459
+ className
3460
+ ),
3461
+ "data-language": language,
3462
+ children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 divide-x divide-[hsl(var(--border-subtle))]", children: [
3463
+ /* @__PURE__ */ jsx("div", { children: pairs.map((pair, idx) => /* @__PURE__ */ jsxs(
3464
+ "div",
3465
+ {
3466
+ className: cn("flex", pair.left ? TYPE_BG[pair.left.type] : ""),
3467
+ children: [
3468
+ showLineNumbers && /* @__PURE__ */ jsx("span", { className: "shrink-0 w-9 px-1.5 text-right select-none text-[hsl(var(--text-tertiary))]/50 tabular-nums", children: pair.left?.oldLineNo ?? "" }),
3469
+ /* @__PURE__ */ jsx("span", { className: "flex-1 px-2 whitespace-pre text-[hsl(var(--text-primary))]", children: pair.left?.content ?? "" })
3470
+ ]
3471
+ },
3472
+ `l-${idx}`
3473
+ )) }),
3474
+ /* @__PURE__ */ jsx("div", { children: pairs.map((pair, idx) => /* @__PURE__ */ jsxs(
3475
+ "div",
3476
+ {
3477
+ className: cn("flex", pair.right ? TYPE_BG[pair.right.type] : ""),
3478
+ children: [
3479
+ showLineNumbers && /* @__PURE__ */ jsx("span", { className: "shrink-0 w-9 px-1.5 text-right select-none text-[hsl(var(--text-tertiary))]/50 tabular-nums", children: pair.right?.newLineNo ?? "" }),
3480
+ /* @__PURE__ */ jsx("span", { className: "flex-1 px-2 whitespace-pre text-[hsl(var(--text-primary))]", children: pair.right?.content ?? "" })
3481
+ ]
3482
+ },
3483
+ `r-${idx}`
3484
+ )) })
3485
+ ] })
3486
+ }
3487
+ );
3488
+ }
3489
+ var DEFAULT_COLORS = [
3490
+ "bg-[hsl(var(--bg-overlay))]",
3491
+ "bg-[hsl(var(--status-ok))]/20",
3492
+ "bg-[hsl(var(--status-ok))]/40",
3493
+ "bg-[hsl(var(--status-ok))]/65",
3494
+ "bg-[hsl(var(--status-ok))]"
3495
+ ];
3496
+ var MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
3497
+ var DAY_LABELS = ["", "Mon", "", "Wed", "", "Fri", ""];
3498
+ function toDateKey(d) {
3499
+ return d.toISOString().slice(0, 10);
3500
+ }
3501
+ function parseDate(s) {
3502
+ const [y, m, d] = s.split("-").map(Number);
3503
+ return new Date(y, m - 1, d);
3504
+ }
3505
+ function HeatmapCalendar({
3506
+ data,
3507
+ startDate,
3508
+ endDate,
3509
+ colorScale = DEFAULT_COLORS,
3510
+ onDayClick,
3511
+ showMonthLabels = true,
3512
+ showDayLabels = true,
3513
+ tooltipFormat,
3514
+ className
3515
+ }) {
3516
+ const [hoveredDay, setHoveredDay] = useState(null);
3517
+ const { weeks, months, maxValue } = useMemo(() => {
3518
+ const end = endDate ? parseDate(endDate) : /* @__PURE__ */ new Date();
3519
+ const start = startDate ? parseDate(startDate) : new Date(end.getFullYear() - 1, end.getMonth(), end.getDate() + 1);
3520
+ const lookup = /* @__PURE__ */ new Map();
3521
+ let maxVal = 0;
3522
+ for (const d of data) {
3523
+ lookup.set(d.date, d.value);
3524
+ if (d.value > maxVal) maxVal = d.value;
3525
+ }
3526
+ const weeks2 = [];
3527
+ const monthChanges = [];
3528
+ const cursor = new Date(start);
3529
+ cursor.setDate(cursor.getDate() - cursor.getDay());
3530
+ let prevMonth = -1;
3531
+ while (cursor <= end || weeks2.length === 0) {
3532
+ const week = [];
3533
+ for (let dow = 0; dow < 7; dow++) {
3534
+ const key = toDateKey(cursor);
3535
+ if (cursor >= start && cursor <= end) {
3536
+ week.push({ date: key, value: lookup.get(key) ?? 0 });
3537
+ } else {
3538
+ week.push(null);
3539
+ }
3540
+ if (cursor.getMonth() !== prevMonth && cursor >= start && cursor <= end) {
3541
+ if (dow === 0) {
3542
+ monthChanges.push({ col: weeks2.length, label: MONTH_NAMES[cursor.getMonth()] });
3543
+ }
3544
+ prevMonth = cursor.getMonth();
3545
+ }
3546
+ cursor.setDate(cursor.getDate() + 1);
3547
+ }
3548
+ weeks2.push(week);
3549
+ }
3550
+ return { weeks: weeks2, months: monthChanges, maxValue: maxVal };
3551
+ }, [data, startDate, endDate]);
3552
+ const getColorClass = (value) => {
3553
+ if (value === 0 || maxValue === 0) return colorScale[0];
3554
+ const idx = Math.min(
3555
+ colorScale.length - 1,
3556
+ Math.ceil(value / maxValue * (colorScale.length - 1))
3557
+ );
3558
+ return colorScale[idx];
3559
+ };
3560
+ const defaultTooltip = (day) => `${day.date}: ${day.value}`;
3561
+ const formatTooltip = tooltipFormat ?? defaultTooltip;
3562
+ return /* @__PURE__ */ jsx("div", { className: cn("overflow-x-auto", className), children: /* @__PURE__ */ jsxs("div", { className: "inline-flex flex-col gap-0.5", children: [
3563
+ showMonthLabels && /* @__PURE__ */ jsx("div", { className: "flex", style: { marginLeft: showDayLabels ? "2rem" : 0 }, children: weeks.map((_, col) => {
3564
+ const monthEntry = months.find((m) => m.col === col);
3565
+ return /* @__PURE__ */ jsx(
3566
+ "div",
3567
+ {
3568
+ className: "text-[10px] text-[hsl(var(--text-tertiary))]",
3569
+ style: { width: 13, minWidth: 13 },
3570
+ children: monthEntry?.label ?? ""
3571
+ },
3572
+ `m-${col}`
3573
+ );
3574
+ }) }),
3575
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-0", children: [
3576
+ showDayLabels && /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-[2px] mr-1", children: DAY_LABELS.map((label, i) => /* @__PURE__ */ jsx(
3577
+ "div",
3578
+ {
3579
+ className: "text-[10px] text-[hsl(var(--text-tertiary))] h-[11px] flex items-center justify-end pr-1",
3580
+ style: { width: "1.75rem" },
3581
+ children: label
3582
+ },
3583
+ i
3584
+ )) }),
3585
+ weeks.map((week, wIdx) => /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-[2px]", children: week.map((day, dIdx) => /* @__PURE__ */ jsx(
3586
+ "div",
3587
+ {
3588
+ className: cn(
3589
+ "w-[11px] h-[11px] rounded-sm",
3590
+ day ? getColorClass(day.value) : "bg-transparent",
3591
+ day && onDayClick && "cursor-pointer",
3592
+ day && "hover:ring-1 hover:ring-[hsl(var(--text-tertiary))]"
3593
+ ),
3594
+ onClick: day && onDayClick ? () => onDayClick(day) : void 0,
3595
+ onMouseEnter: day ? () => setHoveredDay(day) : void 0,
3596
+ onMouseLeave: () => setHoveredDay(null),
3597
+ title: day ? formatTooltip(day) : void 0,
3598
+ role: day && onDayClick ? "button" : void 0,
3599
+ tabIndex: day && onDayClick ? 0 : void 0,
3600
+ onKeyDown: day && onDayClick ? (e) => {
3601
+ if (e.key === "Enter" || e.key === " ") onDayClick(day);
3602
+ } : void 0
3603
+ },
3604
+ dIdx
3605
+ )) }, wIdx))
3606
+ ] }),
3607
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 mt-1", style: { marginLeft: showDayLabels ? "2rem" : 0 }, children: [
3608
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] text-[hsl(var(--text-tertiary))] mr-1", children: "Less" }),
3609
+ colorScale.map((color, i) => /* @__PURE__ */ jsx("div", { className: cn("w-[11px] h-[11px] rounded-sm", color) }, i)),
3610
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] text-[hsl(var(--text-tertiary))] ml-1", children: "More" })
3611
+ ] })
3612
+ ] }) });
3613
+ }
3614
+ var TYPE_ICON = {
3615
+ info: Info,
3616
+ success: CheckCircle2,
3617
+ warning: AlertTriangle,
3618
+ error: XCircle
3619
+ };
3620
+ var TYPE_COLOR = {
3621
+ info: "border-l-[hsl(var(--brand-secondary))]",
3622
+ success: "border-l-[hsl(var(--status-ok))]",
3623
+ warning: "border-l-[hsl(var(--status-warning))]",
3624
+ error: "border-l-[hsl(var(--status-critical))]"
3625
+ };
3626
+ var TYPE_ICON_COLOR = {
3627
+ info: "text-[hsl(var(--brand-secondary))]",
3628
+ success: "text-[hsl(var(--status-ok))]",
3629
+ warning: "text-[hsl(var(--status-warning))]",
3630
+ error: "text-[hsl(var(--status-critical))]"
3631
+ };
3632
+ var POSITION_CLASSES = {
3633
+ "top-right": "top-4 right-4",
3634
+ "top-left": "top-4 left-4",
3635
+ "bottom-right": "bottom-4 right-4",
3636
+ "bottom-left": "bottom-4 left-4"
3637
+ };
3638
+ var SLIDE_FROM = {
3639
+ "top-right": { x: 80 },
3640
+ "top-left": { x: -80 },
3641
+ "bottom-right": { x: 80 },
3642
+ "bottom-left": { x: -80 }
3643
+ };
3644
+ function NotificationStack({
3645
+ notifications,
3646
+ onDismiss,
3647
+ position = "top-right",
3648
+ maxVisible = 5,
3649
+ className
3650
+ }) {
3651
+ const reduced = useReducedMotion();
3652
+ const visible = notifications.slice(0, maxVisible);
3653
+ const overflow = notifications.length - maxVisible;
3654
+ return /* @__PURE__ */ jsxs(
3655
+ "div",
3656
+ {
3657
+ className: cn(
3658
+ "fixed z-50 flex flex-col gap-2 w-[360px] max-w-[calc(100vw-2rem)]",
3659
+ POSITION_CLASSES[position],
3660
+ className
3661
+ ),
3662
+ role: "region",
3663
+ "aria-label": "Notifications",
3664
+ children: [
3665
+ /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: visible.map((notification, idx) => /* @__PURE__ */ jsx(
3666
+ motion.div,
3667
+ {
3668
+ layout: !reduced,
3669
+ initial: reduced ? { opacity: 1 } : { opacity: 0, ...SLIDE_FROM[position] },
3670
+ animate: { opacity: 1, x: 0 },
3671
+ exit: reduced ? { opacity: 0 } : { opacity: 0, ...SLIDE_FROM[position], transition: { duration: 0.15 } },
3672
+ transition: { type: "spring", stiffness: 300, damping: 30 },
3673
+ children: /* @__PURE__ */ jsx(
3674
+ NotificationCard,
3675
+ {
3676
+ notification,
3677
+ onDismiss,
3678
+ reduced: !!reduced
3679
+ }
3680
+ )
3681
+ },
3682
+ notification.id
3683
+ )) }),
3684
+ overflow > 0 && /* @__PURE__ */ jsxs("div", { className: "text-center text-xs text-[hsl(var(--text-tertiary))] py-1", children: [
3685
+ "+",
3686
+ overflow,
3687
+ " more ",
3688
+ overflow === 1 ? "notification" : "notifications"
3689
+ ] })
3690
+ ]
3691
+ }
3692
+ );
3693
+ }
3694
+ function NotificationCard({
3695
+ notification,
3696
+ onDismiss,
3697
+ reduced
3698
+ }) {
3699
+ const { id, title, message, type, action, dismissible = true, duration = 0 } = notification;
3700
+ const Icon = TYPE_ICON[type];
3701
+ const timerRef = useRef(null);
3702
+ const [progress, setProgress] = useState(100);
3703
+ const startTimeRef = useRef(Date.now());
3704
+ useEffect(() => {
3705
+ if (duration <= 0) return;
3706
+ startTimeRef.current = Date.now();
3707
+ const intervalId = setInterval(() => {
3708
+ const elapsed = Date.now() - startTimeRef.current;
3709
+ const remaining = Math.max(0, 100 - elapsed / duration * 100);
3710
+ setProgress(remaining);
3711
+ if (remaining <= 0) {
3712
+ clearInterval(intervalId);
3713
+ }
3714
+ }, 50);
3715
+ timerRef.current = setTimeout(() => {
3716
+ onDismiss(id);
3717
+ }, duration);
3718
+ return () => {
3719
+ clearInterval(intervalId);
3720
+ if (timerRef.current) clearTimeout(timerRef.current);
3721
+ };
3722
+ }, [id, duration, onDismiss]);
3723
+ const handleDismiss = useCallback(() => {
3724
+ onDismiss(id);
3725
+ }, [id, onDismiss]);
3726
+ return /* @__PURE__ */ jsxs(
3727
+ "div",
3728
+ {
3729
+ className: cn(
3730
+ "relative overflow-hidden rounded-xl border-l-[3px] shadow-lg",
3731
+ "bg-[hsl(var(--bg-elevated))] border border-[hsl(var(--border-subtle))]",
3732
+ TYPE_COLOR[type]
3733
+ ),
3734
+ children: [
3735
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 p-4", children: [
3736
+ /* @__PURE__ */ jsx(Icon, { className: cn("size-5 shrink-0 mt-0.5", TYPE_ICON_COLOR[type]) }),
3737
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
3738
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[hsl(var(--text-primary))]", children: title }),
3739
+ message && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-[hsl(var(--text-secondary))] line-clamp-2", children: message }),
3740
+ action && /* @__PURE__ */ jsx(
3741
+ "button",
3742
+ {
3743
+ type: "button",
3744
+ onClick: action.onClick,
3745
+ className: cn(
3746
+ "mt-2 text-xs font-medium cursor-pointer",
3747
+ "text-[hsl(var(--brand-primary))] hover:underline"
3748
+ ),
3749
+ children: action.label
3750
+ }
3751
+ )
3752
+ ] }),
3753
+ dismissible && /* @__PURE__ */ jsx(
3754
+ "button",
3755
+ {
3756
+ type: "button",
3757
+ onClick: handleDismiss,
3758
+ className: cn(
3759
+ "shrink-0 p-0.5 rounded cursor-pointer",
3760
+ "text-[hsl(var(--text-tertiary))] hover:text-[hsl(var(--text-primary))]",
3761
+ "hover:bg-[hsl(var(--bg-overlay))] transition-colors duration-100"
3762
+ ),
3763
+ "aria-label": "Dismiss notification",
3764
+ children: /* @__PURE__ */ jsx(X, { className: "size-3.5" })
3765
+ }
3766
+ )
3767
+ ] }),
3768
+ duration > 0 && /* @__PURE__ */ jsx("div", { className: "h-0.5 bg-[hsl(var(--bg-overlay))]", children: /* @__PURE__ */ jsx(
3769
+ "div",
3770
+ {
3771
+ className: cn("h-full transition-[width] duration-100", TYPE_COLOR[type].replace("border-l-", "bg-")),
3772
+ style: { width: `${progress}%` }
3773
+ }
3774
+ ) })
3775
+ ]
3776
+ }
3777
+ );
3778
+ }
3779
+ var TAG_COLOR_MAP = {
3780
+ brand: "bg-[hsl(var(--brand-primary))]/15 text-[hsl(var(--brand-primary))]",
3781
+ blue: "bg-[hsl(var(--brand-secondary))]/15 text-[hsl(var(--brand-secondary))]",
3782
+ green: "bg-[hsl(var(--status-ok))]/15 text-[hsl(var(--status-ok))]",
3783
+ yellow: "bg-[hsl(var(--status-warning))]/15 text-[hsl(var(--status-warning))]",
3784
+ red: "bg-[hsl(var(--status-critical))]/15 text-[hsl(var(--status-critical))]",
3785
+ orange: "bg-[hsl(var(--status-warning))]/20 text-[hsl(var(--status-warning))]",
3786
+ purple: "bg-[hsl(270,60%,60%)]/15 text-[hsl(270,60%,65%)]",
3787
+ pink: "bg-[hsl(330,60%,60%)]/15 text-[hsl(330,60%,65%)]",
3788
+ teal: "bg-[hsl(180,60%,40%)]/15 text-[hsl(180,60%,55%)]",
3789
+ gray: "bg-[hsl(var(--bg-overlay))] text-[hsl(var(--text-secondary))]"
3790
+ };
3791
+ function KanbanColumn({
3792
+ title,
3793
+ items,
3794
+ count,
3795
+ color,
3796
+ onItemClick,
3797
+ onAddItem,
3798
+ className
3799
+ }) {
3800
+ const reduced = useReducedMotion();
3801
+ const displayCount = count ?? items.length;
3802
+ return /* @__PURE__ */ jsxs(
3803
+ "div",
3804
+ {
3805
+ className: cn(
3806
+ "flex flex-col w-72 min-w-[18rem] rounded-2xl",
3807
+ "bg-[hsl(var(--bg-surface))] border border-[hsl(var(--border-subtle))]",
3808
+ className
3809
+ ),
3810
+ children: [
3811
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-[hsl(var(--border-subtle))]", children: [
3812
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3813
+ color && /* @__PURE__ */ jsx(
3814
+ "span",
3815
+ {
3816
+ className: "w-2 h-2 rounded-full shrink-0",
3817
+ style: { backgroundColor: color }
3818
+ }
3819
+ ),
3820
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-[hsl(var(--text-primary))]", children: title }),
3821
+ /* @__PURE__ */ jsx("span", { className: "inline-flex items-center justify-center min-w-[1.25rem] h-5 px-1.5 rounded-full text-[10px] font-medium tabular-nums bg-[hsl(var(--bg-overlay))] text-[hsl(var(--text-secondary))]", children: displayCount })
3822
+ ] }),
3823
+ onAddItem && /* @__PURE__ */ jsx(
3824
+ "button",
3825
+ {
3826
+ type: "button",
3827
+ onClick: onAddItem,
3828
+ className: cn(
3829
+ "p-1 rounded-lg cursor-pointer",
3830
+ "text-[hsl(var(--text-tertiary))] hover:text-[hsl(var(--text-primary))]",
3831
+ "hover:bg-[hsl(var(--bg-overlay))] transition-colors duration-150"
3832
+ ),
3833
+ "aria-label": `Add item to ${title}`,
3834
+ children: /* @__PURE__ */ jsx(Plus, { className: "size-4" })
3835
+ }
3836
+ )
3837
+ ] }),
3838
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto p-2 space-y-2", children: items.length === 0 ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-8 text-xs text-[hsl(var(--text-tertiary))]", children: "No items" }) : items.map((item, idx) => /* @__PURE__ */ jsx(
3839
+ motion.div,
3840
+ {
3841
+ initial: reduced ? void 0 : { opacity: 0, y: 8 },
3842
+ animate: { opacity: 1, y: 0 },
3843
+ transition: reduced ? { duration: 0 } : { delay: idx * 0.04, duration: 0.2 },
3844
+ children: /* @__PURE__ */ jsx(KanbanCard, { item, onClick: onItemClick })
3845
+ },
3846
+ item.id
3847
+ )) })
3848
+ ]
3849
+ }
3850
+ );
3851
+ }
3852
+ function KanbanCard({
3853
+ item,
3854
+ onClick
3855
+ }) {
3856
+ return /* @__PURE__ */ jsxs(
3857
+ "div",
3858
+ {
3859
+ role: onClick ? "button" : void 0,
3860
+ tabIndex: onClick ? 0 : void 0,
3861
+ onClick: onClick ? () => onClick(item) : void 0,
3862
+ onKeyDown: onClick ? (e) => {
3863
+ if (e.key === "Enter" || e.key === " ") onClick(item);
3864
+ } : void 0,
3865
+ className: cn(
3866
+ "rounded-xl p-3 border border-[hsl(var(--border-subtle))]",
3867
+ "bg-[hsl(var(--bg-base))]",
3868
+ onClick && "cursor-pointer hover:bg-[hsl(var(--bg-elevated))] hover:shadow-sm",
3869
+ "transition-all duration-150"
3870
+ ),
3871
+ children: [
3872
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[hsl(var(--text-primary))] line-clamp-2", children: item.title }),
3873
+ item.description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-[hsl(var(--text-secondary))] line-clamp-2", children: item.description }),
3874
+ (item.tags?.length || item.assignee) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mt-2.5 gap-2", children: [
3875
+ item.tags && item.tags.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1 min-w-0", children: item.tags.map((tag, i) => /* @__PURE__ */ jsx(
3876
+ "span",
3877
+ {
3878
+ className: cn(
3879
+ "inline-flex items-center px-1.5 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap",
3880
+ TAG_COLOR_MAP[tag.color]
3881
+ ),
3882
+ children: tag.label
3883
+ },
3884
+ i
3885
+ )) }),
3886
+ item.assignee && /* @__PURE__ */ jsx("div", { className: "shrink-0", title: item.assignee.name, children: item.assignee.avatar ? /* @__PURE__ */ jsx(
3887
+ "img",
3888
+ {
3889
+ src: item.assignee.avatar,
3890
+ alt: item.assignee.name,
3891
+ className: "size-6 rounded-full object-cover"
3892
+ }
3893
+ ) : /* @__PURE__ */ jsx("div", { className: "size-6 rounded-full bg-[hsl(var(--bg-overlay))] flex items-center justify-center text-[10px] font-semibold text-[hsl(var(--text-secondary))]", children: item.assignee.name.slice(0, 2).toUpperCase() }) })
3894
+ ] })
3895
+ ]
3896
+ }
3897
+ );
3898
+ }
3899
+ function mean(nums) {
3900
+ if (nums.length === 0) return 0;
3901
+ return nums.reduce((a, b) => a + b, 0) / nums.length;
3902
+ }
3903
+ function stdDev(nums) {
3904
+ if (nums.length < 2) return 0;
3905
+ const m = mean(nums);
3906
+ const variance = nums.reduce((sum, n) => sum + (n - m) ** 2, 0) / nums.length;
3907
+ return Math.sqrt(variance);
3908
+ }
3909
+ function analyzeColumn(columnId, columnHeader, data, getValue) {
3910
+ const suggestions = [];
3911
+ const values = data.map(getValue).filter((v) => v != null);
3912
+ if (values.length === 0) return suggestions;
3913
+ const numericValues = values.map((v) => typeof v === "number" ? v : typeof v === "string" && v !== "" && !isNaN(Number(v)) ? Number(v) : null).filter((v) => v !== null);
3914
+ if (numericValues.length > values.length * 0.7) {
3915
+ const m = mean(numericValues);
3916
+ const sd = stdDev(numericValues);
3917
+ if (sd > 0) {
3918
+ const outliers = numericValues.filter((v) => Math.abs(v - m) > 2 * sd);
3919
+ if (outliers.length > 0 && outliers.length < numericValues.length * 0.3) {
3920
+ suggestions.push({
3921
+ column: columnId,
3922
+ type: "outlier",
3923
+ label: `${outliers.length} outlier${outliers.length > 1 ? "s" : ""} in ${columnHeader}`,
3924
+ filter: () => {
3925
+ }
3926
+ });
3927
+ }
3928
+ }
3929
+ const sorted = [...numericValues].sort((a, b) => a - b);
3930
+ const median = sorted[Math.floor(sorted.length / 2)] ?? 0;
3931
+ const aboveMedian = numericValues.filter((v) => v > median);
3932
+ if (aboveMedian.length > 0 && aboveMedian.length < numericValues.length * 0.2) {
3933
+ suggestions.push({
3934
+ column: columnId,
3935
+ type: "threshold",
3936
+ label: `Top ${aboveMedian.length} high values in ${columnHeader}`,
3937
+ filter: () => {
3938
+ }
3939
+ });
3940
+ }
3941
+ } else {
3942
+ const strValues = values.map(String);
3943
+ const freq = /* @__PURE__ */ new Map();
3944
+ for (const v of strValues) {
3945
+ freq.set(v, (freq.get(v) ?? 0) + 1);
3946
+ }
3947
+ for (const [val, count] of freq) {
3948
+ if (count / strValues.length >= 0.9 && freq.size > 1) {
3949
+ const otherCount = strValues.length - count;
3950
+ suggestions.push({
3951
+ column: columnId,
3952
+ type: "pattern",
3953
+ label: `Show non-"${val.length > 20 ? val.slice(0, 20) + "\u2026" : val}" (${otherCount})`,
3954
+ filter: () => {
3955
+ }
3956
+ });
3957
+ }
3958
+ }
3959
+ if (freq.size > 5) {
3960
+ const topEntries = [...freq.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5);
3961
+ const topTotal = topEntries.reduce((s, e) => s + e[1], 0);
3962
+ suggestions.push({
3963
+ column: columnId,
3964
+ type: "top-n",
3965
+ label: `Top 5 ${columnHeader} (${Math.round(topTotal / strValues.length * 100)}%)`,
3966
+ filter: () => {
3967
+ }
3968
+ });
3969
+ }
3970
+ const minorityValues = [...freq.entries()].filter(
3971
+ ([, count]) => count / strValues.length < 0.12 && count > 0
3972
+ );
3973
+ if (minorityValues.length > 0 && minorityValues.length < freq.size) {
3974
+ const totalMinority = minorityValues.reduce((s, [, c]) => s + c, 0);
3975
+ suggestions.push({
3976
+ column: columnId,
3977
+ type: "pattern",
3978
+ label: `Rare ${columnHeader} values (${totalMinority} rows)`,
3979
+ filter: () => {
3980
+ }
3981
+ });
3982
+ }
3983
+ }
3984
+ return suggestions;
3985
+ }
3986
+ var SUGGESTION_ICONS = {
3987
+ outlier: AlertTriangle,
3988
+ "top-n": TrendingUp,
3989
+ pattern: Regex,
3990
+ threshold: Hash
3991
+ };
3992
+ var SUGGESTION_COLORS = {
3993
+ outlier: "bg-[hsl(var(--status-warning)/0.15)] text-[hsl(var(--status-warning))] border-[hsl(var(--status-warning)/0.3)]",
3994
+ "top-n": "bg-[hsl(var(--brand-primary)/0.15)] text-[hsl(var(--brand-primary))] border-[hsl(var(--brand-primary)/0.3)]",
3995
+ pattern: "bg-[hsl(var(--brand-secondary)/0.15)] text-[hsl(var(--brand-secondary))] border-[hsl(var(--brand-secondary)/0.3)]",
3996
+ threshold: "bg-[hsl(var(--status-critical)/0.15)] text-[hsl(var(--status-critical))] border-[hsl(var(--status-critical)/0.3)]"
3997
+ };
3998
+ function SmartTable({
3999
+ columns,
4000
+ data,
4001
+ onFilterSuggestion,
4002
+ maxSuggestions = 6,
4003
+ ...tableProps
4004
+ }) {
4005
+ const prefersReducedMotion = useReducedMotion();
4006
+ const [dismissed, setDismissed] = useState(/* @__PURE__ */ new Set());
4007
+ const [appliedFilter, setAppliedFilter] = useState(null);
4008
+ const [filteredData, setFilteredData] = useState(null);
4009
+ const suggestions = useMemo(() => {
4010
+ if (data.length < 3) return [];
4011
+ const allSuggestions = [];
4012
+ for (const colDef of columns) {
4013
+ const col = colDef;
4014
+ const columnId = col.accessorKey ?? col.id ?? "";
4015
+ const columnHeader = typeof col.header === "string" ? col.header : columnId;
4016
+ if (!columnId) continue;
4017
+ const getValue = col.accessorFn ? col.accessorFn : (row) => row[columnId];
4018
+ const columnSuggestions = analyzeColumn(columnId, columnHeader, data, getValue);
4019
+ for (const s of columnSuggestions) {
4020
+ s.filter = () => {
4021
+ const vals = data.map(getValue).filter((v) => v != null);
4022
+ let filtered;
4023
+ switch (s.type) {
4024
+ case "outlier": {
4025
+ const nums = vals.map((v) => typeof v === "number" ? v : Number(v)).filter((v) => !isNaN(v));
4026
+ const m = mean(nums);
4027
+ const sd = stdDev(nums);
4028
+ filtered = data.filter((row) => {
4029
+ const v = getValue(row);
4030
+ const n = typeof v === "number" ? v : Number(v);
4031
+ return !isNaN(n) && Math.abs(n - m) > 2 * sd;
4032
+ });
4033
+ break;
4034
+ }
4035
+ case "top-n": {
4036
+ const freq = /* @__PURE__ */ new Map();
4037
+ for (const v of vals) freq.set(String(v), (freq.get(String(v)) ?? 0) + 1);
4038
+ const topKeys = new Set([...freq.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map((e) => e[0]));
4039
+ filtered = data.filter((row) => topKeys.has(String(getValue(row))));
4040
+ break;
4041
+ }
4042
+ case "threshold": {
4043
+ const nums = vals.map((v) => typeof v === "number" ? v : Number(v)).filter((v) => !isNaN(v));
4044
+ const sorted = [...nums].sort((a, b) => a - b);
4045
+ const median = sorted[Math.floor(sorted.length / 2)] ?? 0;
4046
+ filtered = data.filter((row) => {
4047
+ const v = getValue(row);
4048
+ const n = typeof v === "number" ? v : Number(v);
4049
+ return !isNaN(n) && n > median;
4050
+ });
4051
+ break;
4052
+ }
4053
+ case "pattern": {
4054
+ const freq = /* @__PURE__ */ new Map();
4055
+ for (const v of vals) freq.set(String(v), (freq.get(String(v)) ?? 0) + 1);
4056
+ let dominant = "";
4057
+ let maxCount = 0;
4058
+ for (const [k, c] of freq) {
4059
+ if (c > maxCount) {
4060
+ dominant = k;
4061
+ maxCount = c;
4062
+ }
4063
+ }
4064
+ if (maxCount / vals.length >= 0.9) {
4065
+ filtered = data.filter((row) => String(getValue(row)) !== dominant);
4066
+ } else {
4067
+ const rareKeys = new Set(
4068
+ [...freq.entries()].filter(([, c]) => c / vals.length < 0.12).map((e) => e[0])
4069
+ );
4070
+ filtered = data.filter((row) => rareKeys.has(String(getValue(row))));
4071
+ }
4072
+ break;
4073
+ }
4074
+ default:
4075
+ filtered = data;
4076
+ }
4077
+ setFilteredData(filtered);
4078
+ setAppliedFilter(s.label);
4079
+ onFilterSuggestion?.(s);
4080
+ };
4081
+ }
4082
+ allSuggestions.push(...columnSuggestions);
4083
+ }
4084
+ return allSuggestions.slice(0, maxSuggestions);
4085
+ }, [data, columns, maxSuggestions, onFilterSuggestion]);
4086
+ const visibleSuggestions = suggestions.filter((s) => !dismissed.has(s.label));
4087
+ const handleDismiss = useCallback((label) => {
4088
+ setDismissed((prev) => new Set(prev).add(label));
4089
+ }, []);
4090
+ const handleClearFilter = useCallback(() => {
4091
+ setFilteredData(null);
4092
+ setAppliedFilter(null);
4093
+ }, []);
4094
+ const displayData = filteredData ?? data;
4095
+ return /* @__PURE__ */ jsxs("div", { children: [
4096
+ /* @__PURE__ */ jsx(AnimatePresence, { children: visibleSuggestions.length > 0 && !appliedFilter && /* @__PURE__ */ jsxs(
4097
+ motion.div,
4098
+ {
4099
+ initial: prefersReducedMotion ? void 0 : { opacity: 0, y: -8 },
4100
+ animate: prefersReducedMotion ? void 0 : { opacity: 1, y: 0 },
4101
+ exit: prefersReducedMotion ? void 0 : { opacity: 0, y: -8 },
4102
+ transition: { duration: 0.2 },
4103
+ className: "mb-3 flex flex-wrap items-center gap-2",
4104
+ children: [
4105
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 text-[11px] font-medium text-[hsl(var(--text-tertiary))] uppercase tracking-wider", children: [
4106
+ /* @__PURE__ */ jsx(Sparkles, { className: "h-3.5 w-3.5 text-[hsl(var(--brand-primary))]" }),
4107
+ "Suggested Filters"
4108
+ ] }),
4109
+ visibleSuggestions.map((suggestion) => {
4110
+ const Icon = SUGGESTION_ICONS[suggestion.type];
4111
+ return /* @__PURE__ */ jsxs(
4112
+ motion.button,
4113
+ {
4114
+ layout: !prefersReducedMotion,
4115
+ initial: prefersReducedMotion ? void 0 : { opacity: 0, scale: 0.9 },
4116
+ animate: prefersReducedMotion ? void 0 : { opacity: 1, scale: 1 },
4117
+ exit: prefersReducedMotion ? void 0 : { opacity: 0, scale: 0.9 },
4118
+ transition: { duration: 0.15 },
4119
+ onClick: suggestion.filter,
4120
+ className: cn(
4121
+ "group inline-flex items-center gap-1.5 rounded-full border px-3 py-1 text-[12px] font-medium",
4122
+ "transition-all hover:shadow-sm cursor-pointer",
4123
+ SUGGESTION_COLORS[suggestion.type]
4124
+ ),
4125
+ children: [
4126
+ /* @__PURE__ */ jsx(Icon, { className: "h-3 w-3" }),
4127
+ suggestion.label,
4128
+ /* @__PURE__ */ jsx(
4129
+ "span",
4130
+ {
4131
+ role: "button",
4132
+ tabIndex: 0,
4133
+ onClick: (e) => {
4134
+ e.stopPropagation();
4135
+ handleDismiss(suggestion.label);
4136
+ },
4137
+ onKeyDown: (e) => {
4138
+ if (e.key === "Enter") {
4139
+ e.stopPropagation();
4140
+ handleDismiss(suggestion.label);
4141
+ }
4142
+ },
4143
+ className: "ml-0.5 opacity-0 group-hover:opacity-100 transition-opacity rounded-full p-0.5 hover:bg-[hsl(var(--bg-overlay)/0.3)]",
4144
+ children: /* @__PURE__ */ jsx(X, { className: "h-2.5 w-2.5" })
4145
+ }
4146
+ )
4147
+ ]
4148
+ },
4149
+ suggestion.label
4150
+ );
4151
+ })
4152
+ ]
4153
+ }
4154
+ ) }),
4155
+ /* @__PURE__ */ jsx(AnimatePresence, { children: appliedFilter && /* @__PURE__ */ jsxs(
4156
+ motion.div,
4157
+ {
4158
+ initial: prefersReducedMotion ? void 0 : { opacity: 0, y: -4 },
4159
+ animate: prefersReducedMotion ? void 0 : { opacity: 1, y: 0 },
4160
+ exit: prefersReducedMotion ? void 0 : { opacity: 0, y: -4 },
4161
+ transition: { duration: 0.15 },
4162
+ className: "mb-3 flex items-center gap-2",
4163
+ children: [
4164
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-full bg-[hsl(var(--brand-primary)/0.15)] border border-[hsl(var(--brand-primary)/0.3)] px-3 py-1 text-[12px] font-medium text-[hsl(var(--brand-primary))]", children: [
4165
+ /* @__PURE__ */ jsx(Sparkles, { className: "h-3 w-3" }),
4166
+ appliedFilter,
4167
+ /* @__PURE__ */ jsxs("span", { className: "ml-1 tabular-nums text-[11px] opacity-70", children: [
4168
+ "(",
4169
+ displayData.length,
4170
+ " row",
4171
+ displayData.length !== 1 ? "s" : "",
4172
+ ")"
4173
+ ] })
4174
+ ] }),
4175
+ /* @__PURE__ */ jsxs(
4176
+ "button",
4177
+ {
4178
+ onClick: handleClearFilter,
4179
+ className: "inline-flex items-center gap-1 rounded-full px-2 py-1 text-[11px] font-medium text-[hsl(var(--text-secondary))] hover:text-[hsl(var(--text-primary))] hover:bg-[hsl(var(--bg-elevated)/0.5)] transition-colors",
4180
+ children: [
4181
+ /* @__PURE__ */ jsx(X, { className: "h-3 w-3" }),
4182
+ "Clear"
4183
+ ]
4184
+ }
4185
+ )
4186
+ ]
4187
+ }
4188
+ ) }),
4189
+ /* @__PURE__ */ jsx(
4190
+ DataTable,
4191
+ {
4192
+ columns,
4193
+ data: displayData,
4194
+ ...tableProps
4195
+ }
4196
+ )
4197
+ ] });
4198
+ }
4199
+ function easeOutCubic2(t) {
4200
+ return 1 - Math.pow(1 - t, 3);
4201
+ }
4202
+ function formatRelativeSeconds(ms) {
4203
+ const secs = Math.floor(ms / 1e3);
4204
+ if (secs < 1) return "just now";
4205
+ if (secs < 60) return `${secs}s ago`;
4206
+ if (secs < 3600) return `${Math.floor(secs / 60)}m ago`;
4207
+ return `${Math.floor(secs / 3600)}h ago`;
4208
+ }
4209
+ var SIZE_CLASSES2 = {
4210
+ sm: { value: "text-lg", label: "text-[11px]", delta: "text-[10px]", dot: "h-1.5 w-1.5", gap: "gap-1" },
4211
+ md: { value: "text-2xl", label: "text-xs", delta: "text-[11px]", dot: "h-2 w-2", gap: "gap-1.5" },
4212
+ lg: { value: "text-3xl", label: "text-sm", delta: "text-xs", dot: "h-2.5 w-2.5", gap: "gap-2" },
4213
+ xl: { value: "text-4xl", label: "text-base", delta: "text-sm", dot: "h-3 w-3", gap: "gap-2" }
4214
+ };
4215
+ function AnimatedNumber({
4216
+ value,
4217
+ format,
4218
+ duration = 400,
4219
+ animateEnabled,
4220
+ reduced,
4221
+ className
4222
+ }) {
4223
+ const prevRef = useRef(value);
4224
+ const rafRef = useRef(null);
4225
+ const [displayed, setDisplayed] = useState(value);
4226
+ useEffect(() => {
4227
+ const from = prevRef.current;
4228
+ const to = value;
4229
+ prevRef.current = value;
4230
+ if (reduced || !animateEnabled || from === to) {
4231
+ setDisplayed(to);
4232
+ return;
4233
+ }
4234
+ const start = performance.now();
4235
+ function tick(now) {
4236
+ const elapsed = now - start;
4237
+ const progress = Math.min(elapsed / duration, 1);
4238
+ const eased = easeOutCubic2(progress);
4239
+ setDisplayed(from + (to - from) * eased);
4240
+ if (progress < 1) {
4241
+ rafRef.current = requestAnimationFrame(tick);
4242
+ } else {
4243
+ setDisplayed(to);
4244
+ }
4245
+ }
4246
+ rafRef.current = requestAnimationFrame(tick);
4247
+ return () => {
4248
+ if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
4249
+ };
4250
+ }, [value, duration, reduced, animateEnabled]);
4251
+ const formatted = format ? format(displayed) : Number.isInteger(value) ? Math.round(displayed).toString() : displayed.toFixed(value.toString().split(".")[1]?.length ?? 1);
4252
+ return /* @__PURE__ */ jsx("span", { className: cn("tabular-nums", className), children: formatted });
4253
+ }
4254
+ function RealtimeValue({
4255
+ value,
4256
+ label,
4257
+ format,
4258
+ lastUpdated,
4259
+ staleAfterMs = 3e4,
4260
+ connectionState = "connected",
4261
+ previousValue,
4262
+ animate = true,
4263
+ size = "md",
4264
+ className
4265
+ }) {
4266
+ const prefersReducedMotion = useReducedMotion();
4267
+ const sizeClasses6 = SIZE_CLASSES2[size];
4268
+ const [staleness, setStaleness] = useState(0);
4269
+ useEffect(() => {
4270
+ if (!lastUpdated) return;
4271
+ const getMs = () => Date.now() - new Date(lastUpdated).getTime();
4272
+ setStaleness(getMs());
4273
+ const interval = setInterval(() => setStaleness(getMs()), 1e3);
4274
+ return () => clearInterval(interval);
4275
+ }, [lastUpdated]);
4276
+ const isStale = lastUpdated ? staleness > staleAfterMs : false;
4277
+ const isVeryStale = lastUpdated ? staleness > staleAfterMs * 2 : false;
4278
+ const freshnessColor = useMemo(() => {
4279
+ if (connectionState === "disconnected") return "bg-[hsl(var(--status-critical))]";
4280
+ if (isVeryStale) return "bg-[hsl(var(--status-critical))]";
4281
+ if (isStale) return "bg-[hsl(var(--status-warning))]";
4282
+ return "bg-[hsl(var(--status-ok))]";
4283
+ }, [connectionState, isStale, isVeryStale]);
4284
+ const delta = typeof value === "number" && previousValue !== void 0 ? value - previousValue : null;
4285
+ const deltaSign = delta !== null ? delta > 0 ? "+" : delta < 0 ? "" : "" : null;
4286
+ const isNumeric = typeof value === "number";
4287
+ return /* @__PURE__ */ jsxs(
4288
+ "div",
4289
+ {
4290
+ className: cn(
4291
+ "relative inline-flex flex-col",
4292
+ sizeClasses6.gap,
4293
+ isStale && "opacity-60",
4294
+ className
4295
+ ),
4296
+ title: lastUpdated ? `Last updated: ${new Date(lastUpdated).toLocaleString()} (${formatRelativeSeconds(staleness)})` : void 0,
4297
+ children: [
4298
+ label && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1.5", children: /* @__PURE__ */ jsx("span", { className: cn("font-medium text-[hsl(var(--text-secondary))]", sizeClasses6.label), children: label }) }),
4299
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
4300
+ /* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center", children: [
4301
+ /* @__PURE__ */ jsx("span", { className: cn("rounded-full", sizeClasses6.dot, freshnessColor) }),
4302
+ connectionState === "connected" && !isStale && /* @__PURE__ */ jsx(
4303
+ "span",
4304
+ {
4305
+ className: cn(
4306
+ "absolute rounded-full animate-ping",
4307
+ sizeClasses6.dot,
4308
+ "bg-[hsl(var(--status-ok)/0.5)]"
4309
+ ),
4310
+ style: { animationDuration: "2s" }
4311
+ }
4312
+ )
4313
+ ] }),
4314
+ /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-1.5", children: [
4315
+ isNumeric ? /* @__PURE__ */ jsx(
4316
+ AnimatedNumber,
4317
+ {
4318
+ value,
4319
+ format,
4320
+ animateEnabled: animate,
4321
+ reduced: prefersReducedMotion,
4322
+ className: cn("font-semibold text-[hsl(var(--text-primary))]", sizeClasses6.value)
4323
+ }
4324
+ ) : /* @__PURE__ */ jsx("span", { className: cn("font-semibold text-[hsl(var(--text-primary))] tabular-nums", sizeClasses6.value), children: value }),
4325
+ /* @__PURE__ */ jsxs(AnimatePresence, { children: [
4326
+ delta !== null && delta !== 0 && /* @__PURE__ */ jsxs(
4327
+ motion.span,
4328
+ {
4329
+ initial: prefersReducedMotion ? void 0 : { opacity: 0, x: -4 },
4330
+ animate: prefersReducedMotion ? void 0 : { opacity: 1, x: 0 },
4331
+ exit: prefersReducedMotion ? void 0 : { opacity: 0, x: -4 },
4332
+ transition: { duration: 0.15 },
4333
+ className: cn(
4334
+ "inline-flex items-center gap-0.5 font-medium tabular-nums",
4335
+ sizeClasses6.delta,
4336
+ delta > 0 ? "text-[hsl(var(--status-ok))]" : "text-[hsl(var(--status-critical))]"
4337
+ ),
4338
+ children: [
4339
+ delta > 0 ? /* @__PURE__ */ jsx(ArrowUp, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(ArrowDown, { className: "h-3 w-3" }),
4340
+ deltaSign,
4341
+ format ? format(Math.abs(delta)) : Math.abs(delta).toLocaleString()
4342
+ ]
4343
+ }
4344
+ ),
4345
+ delta === 0 && /* @__PURE__ */ jsxs(
4346
+ motion.span,
4347
+ {
4348
+ initial: prefersReducedMotion ? void 0 : { opacity: 0 },
4349
+ animate: prefersReducedMotion ? void 0 : { opacity: 1 },
4350
+ className: cn(
4351
+ "inline-flex items-center gap-0.5 font-medium text-[hsl(var(--text-tertiary))]",
4352
+ sizeClasses6.delta
4353
+ ),
4354
+ children: [
4355
+ /* @__PURE__ */ jsx(Minus, { className: "h-3 w-3" }),
4356
+ "0"
4357
+ ]
4358
+ }
4359
+ )
4360
+ ] })
4361
+ ] }),
4362
+ connectionState !== "connected" && /* @__PURE__ */ jsxs("span", { className: "ml-1", children: [
4363
+ connectionState === "reconnecting" && /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 text-[hsl(var(--status-warning))] animate-spin" }),
4364
+ connectionState === "disconnected" && /* @__PURE__ */ jsx(WifiOff, { className: "h-4 w-4 text-[hsl(var(--status-critical))]" })
4365
+ ] })
4366
+ ] }),
4367
+ lastUpdated && /* @__PURE__ */ jsx("span", { className: cn("text-[hsl(var(--text-tertiary))] tabular-nums", sizeClasses6.delta), children: formatRelativeSeconds(staleness) })
4368
+ ]
4369
+ }
4370
+ );
4371
+ }
4372
+ function fuzzyScore(query, target) {
4373
+ const q = query.toLowerCase();
4374
+ const t = target.toLowerCase();
4375
+ if (t === q) return 100;
4376
+ if (t.startsWith(q)) return 80;
4377
+ if (t.includes(q)) return 60;
4378
+ let qi = 0;
4379
+ let score = 0;
4380
+ for (let ti = 0; ti < t.length && qi < q.length; ti++) {
4381
+ if (t[ti] === q[qi]) {
4382
+ score += 10;
4383
+ qi++;
4384
+ }
4385
+ }
4386
+ return qi === q.length ? score : 0;
4387
+ }
4388
+ function scoreItem(query, item) {
4389
+ if (!query) return 0;
4390
+ let best = fuzzyScore(query, item.label);
4391
+ if (item.description) best = Math.max(best, fuzzyScore(query, item.description) * 0.8);
4392
+ if (item.keywords) {
4393
+ for (const kw of item.keywords) {
4394
+ best = Math.max(best, fuzzyScore(query, kw) * 0.9);
4395
+ }
4396
+ }
4397
+ return best;
4398
+ }
4399
+ function CommandBar({
4400
+ items,
4401
+ placeholder = "Type a command\u2026",
4402
+ hotkey = "k",
4403
+ onSearch,
4404
+ recentKey = "ui-kit-command-recent",
4405
+ maxRecent = 5,
4406
+ className
4407
+ }) {
4408
+ const prefersReducedMotion = useReducedMotion();
4409
+ const [open, setOpen] = useState(false);
4410
+ const [query, setQuery] = useState("");
4411
+ const [activeIndex, setActiveIndex] = useState(0);
4412
+ const [asyncResults, setAsyncResults] = useState([]);
4413
+ const [isSearching, setIsSearching] = useState(false);
4414
+ const inputRef = useRef(null);
4415
+ const listRef = useRef(null);
4416
+ const [recentIds, setRecentIds] = useState(() => {
4417
+ if (typeof window === "undefined") return [];
4418
+ try {
4419
+ return JSON.parse(localStorage.getItem(recentKey) ?? "[]");
4420
+ } catch {
4421
+ return [];
4422
+ }
4423
+ });
4424
+ const saveRecent = useCallback(
4425
+ (id) => {
4426
+ const updated = [id, ...recentIds.filter((r) => r !== id)].slice(0, maxRecent);
4427
+ setRecentIds(updated);
4428
+ try {
4429
+ localStorage.setItem(recentKey, JSON.stringify(updated));
4430
+ } catch {
4431
+ }
4432
+ },
4433
+ [recentIds, recentKey, maxRecent]
4434
+ );
4435
+ useEffect(() => {
4436
+ const handler = (e) => {
4437
+ if ((e.metaKey || e.ctrlKey) && e.key === hotkey) {
4438
+ e.preventDefault();
4439
+ setOpen((o) => !o);
4440
+ }
4441
+ if (e.key === "Escape" && open) {
4442
+ setOpen(false);
4443
+ }
4444
+ };
4445
+ document.addEventListener("keydown", handler);
4446
+ return () => document.removeEventListener("keydown", handler);
4447
+ }, [hotkey, open]);
4448
+ useEffect(() => {
4449
+ if (open) {
4450
+ setQuery("");
4451
+ setActiveIndex(0);
4452
+ setAsyncResults([]);
4453
+ requestAnimationFrame(() => inputRef.current?.focus());
4454
+ }
4455
+ }, [open]);
4456
+ useEffect(() => {
4457
+ if (open) {
4458
+ const prev = document.body.style.overflow;
4459
+ document.body.style.overflow = "hidden";
4460
+ return () => {
4461
+ document.body.style.overflow = prev;
4462
+ };
4463
+ }
4464
+ }, [open]);
4465
+ useEffect(() => {
4466
+ if (!onSearch || !query) {
4467
+ setAsyncResults([]);
4468
+ return;
4469
+ }
4470
+ setIsSearching(true);
4471
+ const timer = setTimeout(async () => {
4472
+ try {
4473
+ const results = await onSearch(query);
4474
+ setAsyncResults(results);
4475
+ } catch {
4476
+ setAsyncResults([]);
4477
+ } finally {
4478
+ setIsSearching(false);
4479
+ }
4480
+ }, 200);
4481
+ return () => clearTimeout(timer);
4482
+ }, [query, onSearch]);
4483
+ const displayItems = useMemo(() => {
4484
+ const allItems = [...items, ...asyncResults];
4485
+ if (!query) {
4486
+ const recentItems = recentIds.map((id) => allItems.find((i) => i.id === id)).filter((i) => i !== void 0);
4487
+ const rest = allItems.filter((i) => !recentIds.includes(i.id));
4488
+ return [
4489
+ ...recentItems.map((i) => ({ ...i, group: "Recent" })),
4490
+ ...rest
4491
+ ];
4492
+ }
4493
+ return allItems.map((item) => ({ item, score: scoreItem(query, item) })).filter(({ score }) => score > 0).sort((a, b) => b.score - a.score).map(({ item }) => item);
4494
+ }, [items, asyncResults, query, recentIds]);
4495
+ const groups = useMemo(() => {
4496
+ const grouped = /* @__PURE__ */ new Map();
4497
+ for (const item of displayItems) {
4498
+ const group = item.group ?? "";
4499
+ const arr = grouped.get(group);
4500
+ if (arr) arr.push(item);
4501
+ else grouped.set(group, [item]);
4502
+ }
4503
+ return grouped;
4504
+ }, [displayItems]);
4505
+ const flatItems = displayItems;
4506
+ useEffect(() => {
4507
+ setActiveIndex(0);
4508
+ }, [query]);
4509
+ const handleSelect = useCallback(
4510
+ (item) => {
4511
+ saveRecent(item.id);
4512
+ setOpen(false);
4513
+ item.onSelect();
4514
+ },
4515
+ [saveRecent]
4516
+ );
4517
+ const handleKeyDown = useCallback(
4518
+ (e) => {
4519
+ if (e.key === "ArrowDown") {
4520
+ e.preventDefault();
4521
+ setActiveIndex((i) => Math.min(i + 1, flatItems.length - 1));
4522
+ } else if (e.key === "ArrowUp") {
4523
+ e.preventDefault();
4524
+ setActiveIndex((i) => Math.max(i - 1, 0));
4525
+ } else if (e.key === "Enter") {
4526
+ e.preventDefault();
4527
+ const item = flatItems[activeIndex];
4528
+ if (item) handleSelect(item);
4529
+ }
4530
+ },
4531
+ [flatItems, activeIndex, handleSelect]
4532
+ );
4533
+ useEffect(() => {
4534
+ if (!listRef.current) return;
4535
+ const active = listRef.current.querySelector('[data-active="true"]');
4536
+ active?.scrollIntoView({ block: "nearest" });
4537
+ }, [activeIndex]);
4538
+ const isMac = typeof navigator !== "undefined" && /Mac|iPhone/.test(navigator.userAgent ?? "");
4539
+ return /* @__PURE__ */ jsx(AnimatePresence, { children: open && /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50", children: [
4540
+ /* @__PURE__ */ jsx(
4541
+ motion.div,
4542
+ {
4543
+ initial: { opacity: 0 },
4544
+ animate: { opacity: 1 },
4545
+ exit: { opacity: 0 },
4546
+ transition: prefersReducedMotion ? { duration: 0 } : { duration: 0.15 },
4547
+ className: "absolute inset-0 bg-[hsl(var(--bg-base)/0.6)] backdrop-blur-sm",
4548
+ onClick: () => setOpen(false)
4549
+ }
4550
+ ),
4551
+ /* @__PURE__ */ jsxs(
4552
+ motion.div,
4553
+ {
4554
+ initial: prefersReducedMotion ? void 0 : { opacity: 0, scale: 0.96, y: -20 },
4555
+ animate: prefersReducedMotion ? void 0 : { opacity: 1, scale: 1, y: 0 },
4556
+ exit: prefersReducedMotion ? void 0 : { opacity: 0, scale: 0.96, y: -20 },
4557
+ transition: prefersReducedMotion ? { duration: 0 } : { duration: 0.2, ease: [0.16, 1, 0.3, 1] },
4558
+ className: cn(
4559
+ "absolute left-1/2 top-[15%] -translate-x-1/2",
4560
+ "w-full max-w-lg rounded-2xl overflow-hidden",
4561
+ "border border-[hsl(var(--border-default))]",
4562
+ "bg-[hsl(var(--bg-elevated))] shadow-2xl",
4563
+ "flex flex-col max-h-[70vh]",
4564
+ className
4565
+ ),
4566
+ onKeyDown: handleKeyDown,
4567
+ children: [
4568
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-4 py-3 border-b border-[hsl(var(--border-subtle)/0.5)]", children: [
4569
+ /* @__PURE__ */ jsx(Search, { className: "h-5 w-5 text-[hsl(var(--text-tertiary))] shrink-0" }),
4570
+ /* @__PURE__ */ jsx(
4571
+ "input",
4572
+ {
4573
+ ref: inputRef,
4574
+ type: "text",
4575
+ value: query,
4576
+ onChange: (e) => setQuery(e.target.value),
4577
+ placeholder,
4578
+ className: "flex-1 bg-transparent text-[hsl(var(--text-primary))] text-sm placeholder:text-[hsl(var(--text-tertiary))] outline-none"
4579
+ }
4580
+ ),
4581
+ /* @__PURE__ */ jsx("kbd", { className: "hidden sm:inline-flex items-center gap-1 rounded-md border border-[hsl(var(--border-subtle))] bg-[hsl(var(--bg-surface))] px-1.5 py-0.5 text-[10px] text-[hsl(var(--text-tertiary))] font-mono", children: "Esc" })
4582
+ ] }),
4583
+ /* @__PURE__ */ jsxs("div", { ref: listRef, className: "flex-1 overflow-y-auto py-2", children: [
4584
+ flatItems.length === 0 && !isSearching && /* @__PURE__ */ jsx("div", { className: "px-4 py-8 text-center text-sm text-[hsl(var(--text-tertiary))]", children: query ? "No results found." : "No commands available." }),
4585
+ isSearching && flatItems.length === 0 && /* @__PURE__ */ jsxs("div", { className: "px-4 py-8 flex items-center justify-center gap-2 text-sm text-[hsl(var(--text-tertiary))]", children: [
4586
+ /* @__PURE__ */ jsx("div", { className: "h-4 w-4 rounded-full border-2 border-[hsl(var(--brand-primary))] border-t-transparent animate-spin" }),
4587
+ "Searching..."
4588
+ ] }),
4589
+ [...groups.entries()].map(([groupName, groupItems]) => {
4590
+ return /* @__PURE__ */ jsxs("div", { children: [
4591
+ groupName && /* @__PURE__ */ jsx("div", { className: "px-4 pt-2 pb-1", children: /* @__PURE__ */ jsx("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-[hsl(var(--text-tertiary))]", children: groupName }) }),
4592
+ groupItems.map((item) => {
4593
+ const globalIdx = flatItems.indexOf(item);
4594
+ const isActive = globalIdx === activeIndex;
4595
+ const Icon = item.icon;
4596
+ const isRecent = item.group === "Recent";
4597
+ return /* @__PURE__ */ jsxs(
4598
+ "button",
4599
+ {
4600
+ "data-active": isActive,
4601
+ onClick: () => handleSelect(item),
4602
+ onMouseEnter: () => setActiveIndex(globalIdx),
4603
+ className: cn(
4604
+ "w-full flex items-center gap-3 px-4 py-2.5 text-left transition-colors",
4605
+ isActive ? "bg-[hsl(var(--brand-primary)/0.1)]" : "hover:bg-[hsl(var(--bg-surface)/0.5)]"
4606
+ ),
4607
+ children: [
4608
+ Icon ? /* @__PURE__ */ jsx(Icon, { className: cn(
4609
+ "h-4 w-4 shrink-0",
4610
+ isActive ? "text-[hsl(var(--brand-primary))]" : "text-[hsl(var(--text-tertiary))]"
4611
+ ) }) : isRecent ? /* @__PURE__ */ jsx(Clock, { className: cn(
4612
+ "h-4 w-4 shrink-0",
4613
+ isActive ? "text-[hsl(var(--brand-primary))]" : "text-[hsl(var(--text-tertiary))]"
4614
+ ) }) : /* @__PURE__ */ jsx("div", { className: "h-4 w-4 shrink-0" }),
4615
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
4616
+ /* @__PURE__ */ jsx("div", { className: cn(
4617
+ "text-sm font-medium truncate",
4618
+ isActive ? "text-[hsl(var(--text-primary))]" : "text-[hsl(var(--text-primary))]"
4619
+ ), children: item.label }),
4620
+ item.description && /* @__PURE__ */ jsx("div", { className: "text-[11px] text-[hsl(var(--text-tertiary))] truncate mt-0.5", children: item.description })
4621
+ ] }),
4622
+ item.shortcut && /* @__PURE__ */ jsx("kbd", { className: "flex items-center gap-0.5 rounded-md border border-[hsl(var(--border-subtle))] bg-[hsl(var(--bg-surface))] px-1.5 py-0.5 text-[10px] text-[hsl(var(--text-tertiary))] font-mono shrink-0", children: item.shortcut }),
4623
+ isActive && /* @__PURE__ */ jsx(CornerDownLeft, { className: "h-3.5 w-3.5 text-[hsl(var(--text-tertiary))] shrink-0" })
4624
+ ]
4625
+ },
4626
+ item.id
4627
+ );
4628
+ })
4629
+ ] }, groupName || "__ungrouped");
4630
+ })
4631
+ ] }),
4632
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 px-4 py-2 border-t border-[hsl(var(--border-subtle)/0.5)] text-[10px] text-[hsl(var(--text-tertiary))]", children: [
4633
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
4634
+ /* @__PURE__ */ jsx("kbd", { className: "rounded border border-[hsl(var(--border-subtle))] bg-[hsl(var(--bg-surface))] px-1 py-0.5 font-mono", children: "\u2191\u2193" }),
4635
+ "Navigate"
4636
+ ] }),
4637
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
4638
+ /* @__PURE__ */ jsx("kbd", { className: "rounded border border-[hsl(var(--border-subtle))] bg-[hsl(var(--bg-surface))] px-1 py-0.5 font-mono", children: "\u21B5" }),
4639
+ "Select"
4640
+ ] }),
4641
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
4642
+ /* @__PURE__ */ jsx("kbd", { className: "rounded border border-[hsl(var(--border-subtle))] bg-[hsl(var(--bg-surface))] px-1 py-0.5 font-mono", children: "Esc" }),
4643
+ "Close"
4644
+ ] }),
4645
+ /* @__PURE__ */ jsxs("span", { className: "ml-auto inline-flex items-center gap-1", children: [
4646
+ /* @__PURE__ */ jsx("kbd", { className: "rounded border border-[hsl(var(--border-subtle))] bg-[hsl(var(--bg-surface))] px-1 py-0.5 font-mono", children: isMac ? "\u2318" : "Ctrl+" }),
4647
+ /* @__PURE__ */ jsx("kbd", { className: "rounded border border-[hsl(var(--border-subtle))] bg-[hsl(var(--bg-surface))] px-1 py-0.5 font-mono uppercase", children: hotkey }),
4648
+ "Toggle"
4649
+ ] })
4650
+ ] })
4651
+ ]
4652
+ }
4653
+ )
4654
+ ] }) });
4655
+ }
4656
+ function SortableList({
4657
+ items,
4658
+ onReorder,
4659
+ renderItem,
4660
+ direction = "vertical",
4661
+ className
4662
+ }) {
4663
+ const prefersReducedMotion = useReducedMotion();
4664
+ const [dragIdx, setDragIdx] = useState(null);
4665
+ const [overIdx, setOverIdx] = useState(null);
4666
+ const [kbPickedIdx, setKbPickedIdx] = useState(null);
4667
+ const containerRef = useRef(null);
4668
+ const startPos = useRef({ x: 0, y: 0 });
4669
+ const dragItemId = useRef(null);
4670
+ const handlePointerDown = useCallback(
4671
+ (index) => (e) => {
4672
+ e.preventDefault();
4673
+ if (e.button !== 0) return;
4674
+ setDragIdx(index);
4675
+ setOverIdx(index);
4676
+ dragItemId.current = items[index]?.id ?? null;
4677
+ startPos.current = { x: e.clientX, y: e.clientY };
4678
+ const handlePointerMove = (ev) => {
4679
+ if (!containerRef.current) return;
4680
+ const container = containerRef.current;
4681
+ const children = Array.from(container.children);
4682
+ for (let i = 0; i < children.length; i++) {
4683
+ const rect = children[i].getBoundingClientRect();
4684
+ const midX = rect.left + rect.width / 2;
4685
+ const midY = rect.top + rect.height / 2;
4686
+ const isOver = direction === "vertical" ? ev.clientY < midY + rect.height / 2 && ev.clientY > midY - rect.height / 2 : ev.clientX < midX + rect.width / 2 && ev.clientX > midX - rect.width / 2;
4687
+ if (isOver) {
4688
+ setOverIdx(i);
4689
+ break;
4690
+ }
4691
+ }
4692
+ };
4693
+ const handlePointerUp = () => {
4694
+ document.removeEventListener("pointermove", handlePointerMove);
4695
+ document.removeEventListener("pointerup", handlePointerUp);
4696
+ setDragIdx((prev) => {
4697
+ setOverIdx((over) => {
4698
+ if (prev !== null && over !== null && prev !== over) {
4699
+ const newItems = [...items];
4700
+ const [moved] = newItems.splice(prev, 1);
4701
+ if (moved) newItems.splice(over, 0, moved);
4702
+ setTimeout(() => onReorder(newItems), 0);
4703
+ }
4704
+ return null;
4705
+ });
4706
+ return null;
4707
+ });
4708
+ };
4709
+ document.addEventListener("pointermove", handlePointerMove);
4710
+ document.addEventListener("pointerup", handlePointerUp);
4711
+ },
4712
+ [items, onReorder, direction]
4713
+ );
4714
+ const handleKeyDown = useCallback(
4715
+ (index) => (e) => {
4716
+ if (e.key === " " || e.key === "Enter") {
4717
+ e.preventDefault();
4718
+ if (kbPickedIdx === null) {
4719
+ setKbPickedIdx(index);
4720
+ } else {
4721
+ if (kbPickedIdx !== index) {
4722
+ const newItems = [...items];
4723
+ const [moved] = newItems.splice(kbPickedIdx, 1);
4724
+ if (moved) newItems.splice(index, 0, moved);
4725
+ onReorder(newItems);
4726
+ }
4727
+ setKbPickedIdx(null);
4728
+ }
4729
+ } else if (e.key === "Escape") {
4730
+ setKbPickedIdx(null);
4731
+ } else if (kbPickedIdx !== null) {
4732
+ const isUp = direction === "vertical" ? e.key === "ArrowUp" : e.key === "ArrowLeft";
4733
+ const isDown = direction === "vertical" ? e.key === "ArrowDown" : e.key === "ArrowRight";
4734
+ if (isUp && kbPickedIdx > 0) {
4735
+ e.preventDefault();
4736
+ const newItems = [...items];
4737
+ const [moved] = newItems.splice(kbPickedIdx, 1);
4738
+ const newIdx = kbPickedIdx - 1;
4739
+ if (moved) newItems.splice(newIdx, 0, moved);
4740
+ onReorder(newItems);
4741
+ setKbPickedIdx(newIdx);
4742
+ } else if (isDown && kbPickedIdx < items.length - 1) {
4743
+ e.preventDefault();
4744
+ const newItems = [...items];
4745
+ const [moved] = newItems.splice(kbPickedIdx, 1);
4746
+ const newIdx = kbPickedIdx + 1;
4747
+ if (moved) newItems.splice(newIdx, 0, moved);
4748
+ onReorder(newItems);
4749
+ setKbPickedIdx(newIdx);
4750
+ }
4751
+ }
4752
+ },
4753
+ [items, onReorder, kbPickedIdx, direction]
4754
+ );
4755
+ const getVisualItems = useCallback(() => {
4756
+ if (dragIdx === null || overIdx === null || dragIdx === overIdx) return items;
4757
+ const visual = [...items];
4758
+ const [moved] = visual.splice(dragIdx, 1);
4759
+ if (moved) visual.splice(overIdx, 0, moved);
4760
+ return visual;
4761
+ }, [items, dragIdx, overIdx]);
4762
+ const visualItems = dragIdx !== null ? getVisualItems() : items;
4763
+ return /* @__PURE__ */ jsx(
4764
+ "div",
4765
+ {
4766
+ ref: containerRef,
4767
+ className: cn(
4768
+ "flex",
4769
+ direction === "vertical" ? "flex-col" : "flex-row flex-wrap",
4770
+ className
4771
+ ),
4772
+ role: "listbox",
4773
+ "aria-label": "Sortable list",
4774
+ children: /* @__PURE__ */ jsx(AnimatePresence, { children: visualItems.map((item, index) => {
4775
+ const isDragging = dragIdx !== null && item.id === dragItemId.current;
4776
+ const isKbPicked = kbPickedIdx !== null && items[kbPickedIdx]?.id === item.id;
4777
+ const dragHandleProps = {
4778
+ onPointerDown: handlePointerDown(items.findIndex((i) => i.id === item.id)),
4779
+ isDragging: isDragging || isKbPicked,
4780
+ onKeyDown: handleKeyDown(items.findIndex((i) => i.id === item.id)),
4781
+ tabIndex: 0,
4782
+ role: "option",
4783
+ "aria-roledescription": "sortable item"
4784
+ };
4785
+ return /* @__PURE__ */ jsxs(
4786
+ motion.div,
4787
+ {
4788
+ layout: !prefersReducedMotion,
4789
+ transition: prefersReducedMotion ? { duration: 0 } : { type: "spring", stiffness: 500, damping: 35, mass: 0.5 },
4790
+ className: cn(
4791
+ "relative",
4792
+ isDragging && "z-10 opacity-80",
4793
+ isKbPicked && "ring-2 ring-[hsl(var(--brand-primary))] rounded-lg"
4794
+ ),
4795
+ role: "option",
4796
+ "aria-selected": isKbPicked,
4797
+ children: [
4798
+ dragIdx !== null && overIdx === index && !isDragging && /* @__PURE__ */ jsx(
4799
+ "div",
4800
+ {
4801
+ className: cn(
4802
+ "absolute z-20 bg-[hsl(var(--brand-primary))] rounded-full",
4803
+ direction === "vertical" ? "left-0 right-0 -top-px h-0.5" : "top-0 bottom-0 -left-px w-0.5"
4804
+ )
4805
+ }
4806
+ ),
4807
+ renderItem(item, index, dragHandleProps)
4808
+ ]
4809
+ },
4810
+ item.id
4811
+ );
4812
+ }) })
4813
+ }
4814
+ );
4815
+ }
4816
+ function DragHandle(props) {
4817
+ return /* @__PURE__ */ jsx(
4818
+ "span",
4819
+ {
4820
+ onPointerDown: props.onPointerDown,
4821
+ onKeyDown: props.onKeyDown,
4822
+ tabIndex: props.tabIndex,
4823
+ role: props.role,
4824
+ "aria-roledescription": props["aria-roledescription"],
4825
+ className: cn(
4826
+ "inline-flex items-center justify-center p-1 rounded cursor-grab touch-none select-none",
4827
+ "text-[hsl(var(--text-disabled))] hover:text-[hsl(var(--text-secondary))] transition-colors",
4828
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[hsl(var(--brand-primary))]",
4829
+ props.isDragging && "cursor-grabbing text-[hsl(var(--brand-primary))]"
4830
+ ),
4831
+ children: /* @__PURE__ */ jsx(GripVertical, { className: "h-4 w-4" })
4832
+ }
4833
+ );
4834
+ }
4835
+ function InfiniteScroll({
4836
+ items,
4837
+ renderItem,
4838
+ loadMore,
4839
+ hasMore,
4840
+ isLoading = false,
4841
+ threshold = 200,
4842
+ itemHeight,
4843
+ emptyState,
4844
+ className
4845
+ }) {
4846
+ const prefersReducedMotion = useReducedMotion();
4847
+ const containerRef = useRef(null);
4848
+ const sentinelRef = useRef(null);
4849
+ const [showScrollTop, setShowScrollTop] = useState(false);
4850
+ const loadingRef = useRef(false);
4851
+ useEffect(() => {
4852
+ const sentinel = sentinelRef.current;
4853
+ if (!sentinel) return;
4854
+ const observer = new IntersectionObserver(
4855
+ (entries) => {
4856
+ const entry = entries[0];
4857
+ if (entry?.isIntersecting && hasMore && !isLoading && !loadingRef.current) {
4858
+ loadingRef.current = true;
4859
+ const result = loadMore();
4860
+ if (result && typeof result.then === "function") {
4861
+ result.then(() => {
4862
+ loadingRef.current = false;
4863
+ }).catch(() => {
4864
+ loadingRef.current = false;
4865
+ });
4866
+ } else {
4867
+ loadingRef.current = false;
4868
+ }
4869
+ }
4870
+ },
4871
+ {
4872
+ root: containerRef.current,
4873
+ rootMargin: `0px 0px ${threshold}px 0px`
4874
+ }
4875
+ );
4876
+ observer.observe(sentinel);
4877
+ return () => observer.disconnect();
4878
+ }, [hasMore, isLoading, loadMore, threshold]);
4879
+ useEffect(() => {
4880
+ if (!isLoading) loadingRef.current = false;
4881
+ }, [isLoading]);
4882
+ useEffect(() => {
4883
+ const container = containerRef.current;
4884
+ if (!container) return;
4885
+ const handler = () => {
4886
+ setShowScrollTop(container.scrollTop > 400);
4887
+ };
4888
+ container.addEventListener("scroll", handler, { passive: true });
4889
+ return () => container.removeEventListener("scroll", handler);
4890
+ }, []);
4891
+ const scrollToTop = useCallback(() => {
4892
+ containerRef.current?.scrollTo({ top: 0, behavior: prefersReducedMotion ? "instant" : "smooth" });
4893
+ }, [prefersReducedMotion]);
4894
+ const [scrollTop, setScrollTop] = useState(0);
4895
+ const containerHeight = useRef(0);
4896
+ useEffect(() => {
4897
+ if (!itemHeight) return;
4898
+ const container = containerRef.current;
4899
+ if (!container) return;
4900
+ containerHeight.current = container.clientHeight;
4901
+ const handler = () => {
4902
+ setScrollTop(container.scrollTop);
4903
+ containerHeight.current = container.clientHeight;
4904
+ };
4905
+ container.addEventListener("scroll", handler, { passive: true });
4906
+ return () => container.removeEventListener("scroll", handler);
4907
+ }, [itemHeight]);
4908
+ const virtualizedContent = useMemo(() => {
4909
+ if (!itemHeight) return null;
4910
+ const visibleHeight = containerHeight.current || 600;
4911
+ const buffer = 5;
4912
+ const startIdx = Math.max(0, Math.floor(scrollTop / itemHeight) - buffer);
4913
+ const endIdx = Math.min(
4914
+ items.length,
4915
+ Math.ceil((scrollTop + visibleHeight) / itemHeight) + buffer
4916
+ );
4917
+ const totalHeight = items.length * itemHeight;
4918
+ const offsetTop = startIdx * itemHeight;
4919
+ return {
4920
+ totalHeight,
4921
+ offsetTop,
4922
+ visibleItems: items.slice(startIdx, endIdx),
4923
+ startIdx
4924
+ };
4925
+ }, [items, itemHeight, scrollTop]);
4926
+ if (items.length === 0 && !isLoading && !hasMore) {
4927
+ return /* @__PURE__ */ jsx("div", { className: cn("flex items-center justify-center min-h-[200px]", className), children: emptyState ?? /* @__PURE__ */ jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-[hsl(var(--text-tertiary))]", children: "No items to display." }) }) });
4928
+ }
4929
+ return /* @__PURE__ */ jsxs(
4930
+ "div",
4931
+ {
4932
+ ref: containerRef,
4933
+ className: cn("relative overflow-y-auto", className),
4934
+ children: [
4935
+ virtualizedContent ? /* @__PURE__ */ jsx("div", { style: { height: virtualizedContent.totalHeight, position: "relative" }, children: /* @__PURE__ */ jsx("div", { style: { position: "absolute", top: virtualizedContent.offsetTop, left: 0, right: 0 }, children: virtualizedContent.visibleItems.map((item, i) => /* @__PURE__ */ jsx("div", { style: { height: itemHeight }, children: renderItem(item, virtualizedContent.startIdx + i) }, virtualizedContent.startIdx + i)) }) }) : (
4936
+ /* Non-virtualized rendering */
4937
+ items.map((item, index) => /* @__PURE__ */ jsx("div", { children: renderItem(item, index) }, index))
4938
+ ),
4939
+ isLoading && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center py-6 gap-2", children: [
4940
+ /* @__PURE__ */ jsx(Loader2, { className: "h-5 w-5 text-[hsl(var(--brand-primary))] animate-spin" }),
4941
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-[hsl(var(--text-tertiary))]", children: "Loading more..." })
4942
+ ] }),
4943
+ isLoading && items.length === 0 && /* @__PURE__ */ jsx("div", { className: "space-y-3 p-4", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ jsx("div", { className: "skeleton-shimmer h-16 rounded-xl" }, i)) }),
4944
+ !hasMore && items.length > 0 && /* @__PURE__ */ jsx("div", { className: "py-4 text-center", children: /* @__PURE__ */ jsxs("span", { className: "text-[11px] text-[hsl(var(--text-tertiary))]", children: [
4945
+ "All ",
4946
+ items.length,
4947
+ " item",
4948
+ items.length !== 1 ? "s" : "",
4949
+ " loaded"
4950
+ ] }) }),
4951
+ /* @__PURE__ */ jsx("div", { ref: sentinelRef, className: "h-px w-full", "aria-hidden": "true" }),
4952
+ /* @__PURE__ */ jsx(AnimatePresence, { children: showScrollTop && /* @__PURE__ */ jsxs(
4953
+ motion.button,
4954
+ {
4955
+ initial: prefersReducedMotion ? void 0 : { opacity: 0, scale: 0.8 },
4956
+ animate: prefersReducedMotion ? void 0 : { opacity: 1, scale: 1 },
4957
+ exit: prefersReducedMotion ? void 0 : { opacity: 0, scale: 0.8 },
4958
+ transition: { duration: 0.15 },
4959
+ onClick: scrollToTop,
4960
+ className: cn(
4961
+ "sticky bottom-4 left-1/2 -translate-x-1/2 z-10",
4962
+ "inline-flex items-center gap-1.5 rounded-full",
4963
+ "px-3 py-2 text-[11px] font-medium",
4964
+ "bg-[hsl(var(--bg-elevated))] border border-[hsl(var(--border-default))]",
4965
+ "text-[hsl(var(--text-secondary))] shadow-lg",
4966
+ "hover:bg-[hsl(var(--bg-overlay))] hover:text-[hsl(var(--text-primary))] transition-colors",
4967
+ "cursor-pointer"
4968
+ ),
4969
+ children: [
4970
+ /* @__PURE__ */ jsx(ArrowUp, { className: "h-3.5 w-3.5" }),
4971
+ "Back to top"
4972
+ ]
4973
+ }
4974
+ ) })
4975
+ ]
4976
+ }
4977
+ );
4978
+ }
4979
+ function hexToRgb(hex) {
4980
+ const clean = hex.replace("#", "");
4981
+ const full = clean.length === 3 ? clean.split("").map((c) => c + c).join("") : clean;
4982
+ const num = parseInt(full, 16);
4983
+ return { r: num >> 16 & 255, g: num >> 8 & 255, b: num & 255 };
4984
+ }
4985
+ function rgbToHex(r, g, b) {
4986
+ return "#" + [r, g, b].map((c) => Math.round(c).toString(16).padStart(2, "0")).join("");
4987
+ }
4988
+ function rgbToHsl(r, g, b) {
4989
+ const rn = r / 255, gn = g / 255, bn = b / 255;
4990
+ const max = Math.max(rn, gn, bn), min = Math.min(rn, gn, bn);
4991
+ const l = (max + min) / 2;
4992
+ if (max === min) return { h: 0, s: 0, l };
4993
+ const d = max - min;
4994
+ const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
4995
+ let h = 0;
4996
+ if (max === rn) h = ((gn - bn) / d + (gn < bn ? 6 : 0)) / 6;
4997
+ else if (max === gn) h = ((bn - rn) / d + 2) / 6;
4998
+ else h = ((rn - gn) / d + 4) / 6;
4999
+ return { h, s, l };
5000
+ }
5001
+ function hslToRgb(h, s, l) {
5002
+ if (s === 0) {
5003
+ const v = Math.round(l * 255);
5004
+ return { r: v, g: v, b: v };
5005
+ }
5006
+ const hue2rgb = (p2, q2, t) => {
5007
+ const tt = t < 0 ? t + 1 : t > 1 ? t - 1 : t;
5008
+ if (tt < 1 / 6) return p2 + (q2 - p2) * 6 * tt;
5009
+ if (tt < 1 / 2) return q2;
5010
+ if (tt < 2 / 3) return p2 + (q2 - p2) * (2 / 3 - tt) * 6;
5011
+ return p2;
5012
+ };
5013
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
5014
+ const p = 2 * l - q;
5015
+ return {
5016
+ r: Math.round(hue2rgb(p, q, h + 1 / 3) * 255),
5017
+ g: Math.round(hue2rgb(p, q, h) * 255),
5018
+ b: Math.round(hue2rgb(p, q, h - 1 / 3) * 255)
5019
+ };
5020
+ }
5021
+ function formatColor(hex, fmt) {
5022
+ if (fmt === "hex") return hex;
5023
+ const { r, g, b } = hexToRgb(hex);
5024
+ if (fmt === "rgb") return `rgb(${r}, ${g}, ${b})`;
5025
+ const { h, s, l } = rgbToHsl(r, g, b);
5026
+ return `hsl(${Math.round(h * 360)}, ${Math.round(s * 100)}%, ${Math.round(l * 100)}%)`;
5027
+ }
5028
+ var RECENT_COLORS_KEY = "ui-kit-recent-colors";
5029
+ var MAX_RECENT = 8;
5030
+ function ColorInput({
5031
+ value,
5032
+ onChange,
5033
+ label,
5034
+ presets,
5035
+ showAlpha = false,
5036
+ format = "hex",
5037
+ className
5038
+ }) {
5039
+ const prefersReducedMotion = useReducedMotion();
5040
+ const [open, setOpen] = useState(false);
5041
+ const [copied, setCopied] = useState(false);
5042
+ const [textInput, setTextInput] = useState("");
5043
+ const [alpha, setAlpha] = useState(1);
5044
+ const panelRef = useRef(null);
5045
+ const satAreaRef = useRef(null);
5046
+ const [recentColors, setRecentColors] = useState(() => {
5047
+ if (typeof window === "undefined") return [];
5048
+ try {
5049
+ return JSON.parse(localStorage.getItem(RECENT_COLORS_KEY) ?? "[]");
5050
+ } catch {
5051
+ return [];
5052
+ }
5053
+ });
5054
+ const addRecent = useCallback((color) => {
5055
+ setRecentColors((prev) => {
5056
+ const updated = [color, ...prev.filter((c) => c !== color)].slice(0, MAX_RECENT);
5057
+ try {
5058
+ localStorage.setItem(RECENT_COLORS_KEY, JSON.stringify(updated));
5059
+ } catch {
5060
+ }
5061
+ return updated;
5062
+ });
5063
+ }, []);
5064
+ const { r, g, b } = useMemo(() => hexToRgb(value), [value]);
5065
+ const hsl = useMemo(() => rgbToHsl(r, g, b), [r, g, b]);
5066
+ useEffect(() => {
5067
+ setTextInput(formatColor(value, format));
5068
+ }, [value, format]);
5069
+ useEffect(() => {
5070
+ if (!open) return;
5071
+ const handler = (e) => {
5072
+ if (panelRef.current && !panelRef.current.contains(e.target)) {
5073
+ setOpen(false);
5074
+ addRecent(value);
5075
+ }
5076
+ };
5077
+ document.addEventListener("mousedown", handler);
5078
+ return () => document.removeEventListener("mousedown", handler);
5079
+ }, [open, value, addRecent]);
5080
+ const handleSatAreaPointer = useCallback(
5081
+ (e) => {
5082
+ if (!satAreaRef.current) return;
5083
+ const rect = satAreaRef.current.getBoundingClientRect();
5084
+ const x = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
5085
+ const y = Math.max(0, Math.min(1, (e.clientY - rect.top) / rect.height));
5086
+ const s = x;
5087
+ const l = 1 - y;
5088
+ const adjustedL = 0.05 + l * 0.9;
5089
+ const rgb = hslToRgb(hsl.h, s, adjustedL);
5090
+ onChange(rgbToHex(rgb.r, rgb.g, rgb.b));
5091
+ },
5092
+ [hsl.h, onChange]
5093
+ );
5094
+ const handleSatAreaDown = useCallback(
5095
+ (e) => {
5096
+ e.preventDefault();
5097
+ handleSatAreaPointer(e);
5098
+ const move = (ev) => handleSatAreaPointer(ev);
5099
+ const up = () => {
5100
+ document.removeEventListener("pointermove", move);
5101
+ document.removeEventListener("pointerup", up);
5102
+ };
5103
+ document.addEventListener("pointermove", move);
5104
+ document.addEventListener("pointerup", up);
5105
+ },
5106
+ [handleSatAreaPointer]
5107
+ );
5108
+ const handleHueChange = useCallback(
5109
+ (e) => {
5110
+ const h = Number(e.target.value) / 360;
5111
+ const rgb = hslToRgb(h, hsl.s || 0.5, hsl.l || 0.5);
5112
+ onChange(rgbToHex(rgb.r, rgb.g, rgb.b));
5113
+ },
5114
+ [hsl.s, hsl.l, onChange]
5115
+ );
5116
+ const handleTextCommit = useCallback(() => {
5117
+ const v = textInput.trim();
5118
+ if (/^#?[0-9a-f]{3,6}$/i.test(v)) {
5119
+ const hex = v.startsWith("#") ? v : "#" + v;
5120
+ onChange(hex);
5121
+ return;
5122
+ }
5123
+ const rgbMatch = v.match(/rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/);
5124
+ if (rgbMatch) {
5125
+ onChange(rgbToHex(Number(rgbMatch[1]), Number(rgbMatch[2]), Number(rgbMatch[3])));
5126
+ return;
5127
+ }
5128
+ const hslMatch = v.match(/hsl\(\s*(\d+)\s*,\s*(\d+)%?\s*,\s*(\d+)%?\s*\)/);
5129
+ if (hslMatch) {
5130
+ const rgb = hslToRgb(Number(hslMatch[1]) / 360, Number(hslMatch[2]) / 100, Number(hslMatch[3]) / 100);
5131
+ onChange(rgbToHex(rgb.r, rgb.g, rgb.b));
5132
+ return;
5133
+ }
5134
+ setTextInput(formatColor(value, format));
5135
+ }, [textInput, value, format, onChange]);
5136
+ const handleCopy = useCallback(async () => {
5137
+ try {
5138
+ await navigator.clipboard.writeText(formatColor(value, format));
5139
+ setCopied(true);
5140
+ setTimeout(() => setCopied(false), 1500);
5141
+ } catch {
5142
+ }
5143
+ }, [value, format]);
5144
+ const markerX = hsl.s * 100;
5145
+ const markerY = (1 - (hsl.l - 0.05) / 0.9) * 100;
5146
+ return /* @__PURE__ */ jsxs("div", { ref: panelRef, className: cn("relative inline-block", className), children: [
5147
+ label && /* @__PURE__ */ jsx("label", { className: "block text-xs font-medium text-[hsl(var(--text-secondary))] mb-1.5", children: label }),
5148
+ /* @__PURE__ */ jsxs(
5149
+ "button",
5150
+ {
5151
+ onClick: () => setOpen((o) => !o),
5152
+ className: cn(
5153
+ "inline-flex items-center gap-2 rounded-lg border border-[hsl(var(--border-subtle))]",
5154
+ "bg-[hsl(var(--bg-surface))] px-3 py-2 text-sm",
5155
+ "hover:border-[hsl(var(--border-default))] transition-colors",
5156
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[hsl(var(--brand-primary))]"
5157
+ ),
5158
+ children: [
5159
+ /* @__PURE__ */ jsx(
5160
+ "span",
5161
+ {
5162
+ className: "h-5 w-5 rounded-md border border-[hsl(var(--border-subtle))]",
5163
+ style: { backgroundColor: value }
5164
+ }
5165
+ ),
5166
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-xs text-[hsl(var(--text-primary))]", children: formatColor(value, format) })
5167
+ ]
5168
+ }
5169
+ ),
5170
+ /* @__PURE__ */ jsx(AnimatePresence, { children: open && /* @__PURE__ */ jsxs(
5171
+ motion.div,
5172
+ {
5173
+ initial: prefersReducedMotion ? void 0 : { opacity: 0, scale: 0.96, y: -4 },
5174
+ animate: prefersReducedMotion ? void 0 : { opacity: 1, scale: 1, y: 0 },
5175
+ exit: prefersReducedMotion ? void 0 : { opacity: 0, scale: 0.96, y: -4 },
5176
+ transition: prefersReducedMotion ? { duration: 0 } : { duration: 0.15 },
5177
+ className: cn(
5178
+ "absolute z-50 mt-2 w-64 rounded-xl overflow-hidden",
5179
+ "border border-[hsl(var(--border-default))]",
5180
+ "bg-[hsl(var(--bg-elevated))] shadow-xl",
5181
+ "p-3"
5182
+ ),
5183
+ children: [
5184
+ /* @__PURE__ */ jsx(
5185
+ "div",
5186
+ {
5187
+ ref: satAreaRef,
5188
+ onPointerDown: handleSatAreaDown,
5189
+ className: "relative h-36 w-full rounded-lg cursor-crosshair overflow-hidden mb-3",
5190
+ style: {
5191
+ background: `linear-gradient(to top, #000, transparent),
5192
+ linear-gradient(to right, #fff, hsl(${Math.round(hsl.h * 360)}, 100%, 50%))`
5193
+ },
5194
+ children: /* @__PURE__ */ jsx(
5195
+ "div",
5196
+ {
5197
+ className: "absolute w-4 h-4 rounded-full border-2 border-white shadow-md -translate-x-1/2 -translate-y-1/2 pointer-events-none",
5198
+ style: {
5199
+ left: `${markerX}%`,
5200
+ top: `${Math.max(0, Math.min(100, markerY))}%`,
5201
+ backgroundColor: value
5202
+ }
5203
+ }
5204
+ )
5205
+ }
5206
+ ),
5207
+ /* @__PURE__ */ jsx("div", { className: "mb-3", children: /* @__PURE__ */ jsx(
5208
+ "input",
5209
+ {
5210
+ type: "range",
5211
+ min: 0,
5212
+ max: 360,
5213
+ value: Math.round(hsl.h * 360),
5214
+ onChange: handleHueChange,
5215
+ className: "w-full h-3 rounded-full appearance-none cursor-pointer",
5216
+ style: {
5217
+ background: "linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00)"
5218
+ }
5219
+ }
5220
+ ) }),
5221
+ showAlpha && /* @__PURE__ */ jsx("div", { className: "mb-3", children: /* @__PURE__ */ jsx(
5222
+ "input",
5223
+ {
5224
+ type: "range",
5225
+ min: 0,
5226
+ max: 100,
5227
+ value: Math.round(alpha * 100),
5228
+ onChange: (e) => setAlpha(Number(e.target.value) / 100),
5229
+ className: "w-full h-3 rounded-full appearance-none cursor-pointer",
5230
+ style: {
5231
+ background: `linear-gradient(to right, transparent, ${value})`
5232
+ }
5233
+ }
5234
+ ) }),
5235
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-3", children: [
5236
+ /* @__PURE__ */ jsx(
5237
+ "input",
5238
+ {
5239
+ type: "text",
5240
+ value: textInput,
5241
+ onChange: (e) => setTextInput(e.target.value),
5242
+ onBlur: handleTextCommit,
5243
+ onKeyDown: (e) => {
5244
+ if (e.key === "Enter") handleTextCommit();
5245
+ },
5246
+ className: cn(
5247
+ "flex-1 rounded-md border border-[hsl(var(--border-subtle))]",
5248
+ "bg-[hsl(var(--bg-surface))] px-2 py-1 text-xs font-mono",
5249
+ "text-[hsl(var(--text-primary))] outline-none",
5250
+ "focus:border-[hsl(var(--brand-primary))] transition-colors"
5251
+ )
5252
+ }
5253
+ ),
5254
+ /* @__PURE__ */ jsx(
5255
+ "button",
5256
+ {
5257
+ onClick: handleCopy,
5258
+ className: cn(
5259
+ "p-1.5 rounded-md transition-colors",
5260
+ "text-[hsl(var(--text-tertiary))] hover:text-[hsl(var(--text-primary))]",
5261
+ "hover:bg-[hsl(var(--bg-surface))]"
5262
+ ),
5263
+ title: "Copy color",
5264
+ children: copied ? /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5 text-[hsl(var(--status-ok))]" }) : /* @__PURE__ */ jsx(Copy, { className: "h-3.5 w-3.5" })
5265
+ }
5266
+ )
5267
+ ] }),
5268
+ presets && presets.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mb-2", children: [
5269
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-[hsl(var(--text-tertiary))] mb-1.5 block", children: "Presets" }),
5270
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: presets.map((color) => /* @__PURE__ */ jsx(
5271
+ "button",
5272
+ {
5273
+ onClick: () => {
5274
+ onChange(color);
5275
+ addRecent(color);
5276
+ },
5277
+ className: cn(
5278
+ "h-6 w-6 rounded-md border transition-all",
5279
+ value === color ? "border-[hsl(var(--brand-primary))] ring-2 ring-[hsl(var(--brand-primary)/0.3)] scale-110" : "border-[hsl(var(--border-subtle))] hover:scale-110"
5280
+ ),
5281
+ style: { backgroundColor: color },
5282
+ title: color
5283
+ },
5284
+ color
5285
+ )) })
5286
+ ] }),
5287
+ recentColors.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
5288
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-[hsl(var(--text-tertiary))] mb-1.5 block", children: "Recent" }),
5289
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: recentColors.map((color) => /* @__PURE__ */ jsx(
5290
+ "button",
5291
+ {
5292
+ onClick: () => onChange(color),
5293
+ className: cn(
5294
+ "h-6 w-6 rounded-md border border-[hsl(var(--border-subtle))]",
5295
+ "hover:scale-110 transition-transform"
5296
+ ),
5297
+ style: { backgroundColor: color },
5298
+ title: color
5299
+ },
5300
+ color
5301
+ )) })
5302
+ ] })
5303
+ ]
5304
+ }
5305
+ ) })
5306
+ ] });
5307
+ }
5308
+ var SESSION_KEY_PREFIX = "ui-kit-wizard-";
5309
+ function StepWizard({
5310
+ steps,
5311
+ onComplete,
5312
+ onStepChange,
5313
+ orientation = "horizontal",
5314
+ allowSkip = false,
5315
+ showSummary = false,
5316
+ className
5317
+ }) {
5318
+ const prefersReducedMotion = useReducedMotion();
5319
+ const sessionKey = useMemo(() => SESSION_KEY_PREFIX + steps.map((s) => s.id).join("-"), [steps]);
5320
+ const [currentStep, setCurrentStep] = useState(() => {
5321
+ if (typeof window === "undefined") return 0;
5322
+ try {
5323
+ const saved = sessionStorage.getItem(sessionKey);
5324
+ if (saved) {
5325
+ const parsed = JSON.parse(saved);
5326
+ return Math.min(parsed.step, steps.length - 1);
5327
+ }
5328
+ } catch {
5329
+ }
5330
+ return 0;
5331
+ });
5332
+ const [completed, setCompleted] = useState(() => {
5333
+ if (typeof window === "undefined") return /* @__PURE__ */ new Set();
5334
+ try {
5335
+ const saved = sessionStorage.getItem(sessionKey);
5336
+ if (saved) {
5337
+ const parsed = JSON.parse(saved);
5338
+ return new Set(parsed.completed);
5339
+ }
5340
+ } catch {
5341
+ }
5342
+ return /* @__PURE__ */ new Set();
5343
+ });
5344
+ const [isValidating, setIsValidating] = useState(false);
5345
+ const [direction, setDirection] = useState(1);
5346
+ const [isComplete, setIsComplete] = useState(false);
5347
+ const contentRef = useRef(null);
5348
+ useEffect(() => {
5349
+ try {
5350
+ sessionStorage.setItem(sessionKey, JSON.stringify({
5351
+ step: currentStep,
5352
+ completed: [...completed]
5353
+ }));
5354
+ } catch {
5355
+ }
5356
+ }, [currentStep, completed, sessionKey]);
5357
+ const totalSteps = steps.length;
5358
+ const progress = totalSteps === 0 ? 100 : Math.round(completed.size / totalSteps * 100);
5359
+ const goToStep = useCallback((idx) => {
5360
+ setDirection(idx > currentStep ? 1 : -1);
5361
+ setCurrentStep(idx);
5362
+ onStepChange?.(idx);
5363
+ }, [currentStep, onStepChange]);
5364
+ const handleNext = useCallback(async () => {
5365
+ const step = steps[currentStep];
5366
+ if (step?.validate) {
5367
+ setIsValidating(true);
5368
+ try {
5369
+ const valid = await step.validate();
5370
+ if (!valid) {
5371
+ setIsValidating(false);
5372
+ return;
5373
+ }
5374
+ } catch {
5375
+ setIsValidating(false);
5376
+ return;
5377
+ }
5378
+ setIsValidating(false);
5379
+ }
5380
+ setCompleted((prev) => new Set(prev).add(currentStep));
5381
+ if (currentStep < totalSteps - 1) {
5382
+ goToStep(currentStep + 1);
5383
+ } else {
5384
+ if (showSummary) {
5385
+ setIsComplete(true);
5386
+ }
5387
+ onComplete();
5388
+ try {
5389
+ sessionStorage.removeItem(sessionKey);
5390
+ } catch {
5391
+ }
5392
+ }
5393
+ }, [currentStep, steps, totalSteps, goToStep, onComplete, showSummary, sessionKey]);
5394
+ const handleBack = useCallback(() => {
5395
+ if (currentStep > 0) {
5396
+ goToStep(currentStep - 1);
5397
+ }
5398
+ }, [currentStep, goToStep]);
5399
+ const handleStepClick = useCallback((idx) => {
5400
+ if (completed.has(idx) || allowSkip || idx < currentStep) {
5401
+ goToStep(idx);
5402
+ }
5403
+ }, [completed, allowSkip, currentStep, goToStep]);
5404
+ useEffect(() => {
5405
+ const handler = (e) => {
5406
+ if (e.key === "Enter" && !e.shiftKey && !(e.target instanceof HTMLTextAreaElement)) {
5407
+ handleNext();
5408
+ }
5409
+ };
5410
+ document.addEventListener("keydown", handler);
5411
+ return () => document.removeEventListener("keydown", handler);
5412
+ }, [handleNext]);
5413
+ const slideVariants2 = {
5414
+ enter: (dir) => ({
5415
+ x: prefersReducedMotion ? 0 : dir > 0 ? 40 : -40,
5416
+ opacity: prefersReducedMotion ? 1 : 0
5417
+ }),
5418
+ center: { x: 0, opacity: 1 },
5419
+ exit: (dir) => ({
5420
+ x: prefersReducedMotion ? 0 : dir > 0 ? -40 : 40,
5421
+ opacity: prefersReducedMotion ? 1 : 0
5422
+ })
5423
+ };
5424
+ const isHorizontal = orientation === "horizontal";
5425
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col", className), children: [
5426
+ /* @__PURE__ */ jsx("div", { className: cn(
5427
+ "mb-6",
5428
+ isHorizontal ? "flex items-center" : "flex flex-col gap-1"
5429
+ ), children: steps.map((step, idx) => {
5430
+ const isActive = idx === currentStep;
5431
+ const isDone = completed.has(idx) || isComplete;
5432
+ const isClickable = isDone || allowSkip || idx < currentStep;
5433
+ const Icon = step.icon;
5434
+ return /* @__PURE__ */ jsxs(
5435
+ "div",
5436
+ {
5437
+ className: cn(
5438
+ isHorizontal ? "flex items-center flex-1" : "flex items-center gap-3"
5439
+ ),
5440
+ children: [
5441
+ /* @__PURE__ */ jsxs(
5442
+ "button",
5443
+ {
5444
+ onClick: () => handleStepClick(idx),
5445
+ disabled: !isClickable,
5446
+ className: cn(
5447
+ "flex items-center gap-2 group",
5448
+ isClickable ? "cursor-pointer" : "cursor-default"
5449
+ ),
5450
+ children: [
5451
+ /* @__PURE__ */ jsx(
5452
+ "div",
5453
+ {
5454
+ className: cn(
5455
+ "flex items-center justify-center rounded-full transition-all",
5456
+ "h-8 w-8 text-xs font-semibold shrink-0",
5457
+ isDone ? "bg-[hsl(var(--status-ok))] text-[hsl(var(--text-on-brand))]" : isActive ? "bg-[hsl(var(--brand-primary))] text-[hsl(var(--text-on-brand))] ring-4 ring-[hsl(var(--brand-primary)/0.2)]" : "bg-[hsl(var(--bg-overlay))] text-[hsl(var(--text-tertiary))]",
5458
+ isClickable && !isActive && !isDone && "group-hover:bg-[hsl(var(--bg-elevated))]"
5459
+ ),
5460
+ children: isDone ? /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) : Icon ? /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }) : idx + 1
5461
+ }
5462
+ ),
5463
+ /* @__PURE__ */ jsxs("div", { className: cn(
5464
+ isHorizontal ? "hidden sm:block" : "block"
5465
+ ), children: [
5466
+ /* @__PURE__ */ jsx("div", { className: cn(
5467
+ "text-xs font-medium leading-tight",
5468
+ isActive ? "text-[hsl(var(--text-primary))]" : "text-[hsl(var(--text-secondary))]"
5469
+ ), children: step.title }),
5470
+ step.description && !isHorizontal && /* @__PURE__ */ jsx("div", { className: "text-[10px] text-[hsl(var(--text-tertiary))]", children: step.description })
5471
+ ] })
5472
+ ]
5473
+ }
5474
+ ),
5475
+ isHorizontal && idx < totalSteps - 1 && /* @__PURE__ */ jsx("div", { className: cn(
5476
+ "flex-1 h-0.5 mx-2 rounded-full transition-colors",
5477
+ completed.has(idx) ? "bg-[hsl(var(--status-ok))]" : "bg-[hsl(var(--border-subtle))]"
5478
+ ) })
5479
+ ]
5480
+ },
5481
+ step.id
5482
+ );
5483
+ }) }),
5484
+ /* @__PURE__ */ jsx("div", { className: "w-full h-1 rounded-full bg-[hsl(var(--bg-overlay))] mb-4 overflow-hidden", children: /* @__PURE__ */ jsx(
5485
+ motion.div,
5486
+ {
5487
+ className: "h-full rounded-full bg-[hsl(var(--brand-primary))]",
5488
+ initial: { width: 0 },
5489
+ animate: { width: `${progress}%` },
5490
+ transition: prefersReducedMotion ? { duration: 0 } : { type: "spring", stiffness: 300, damping: 30 }
5491
+ }
5492
+ ) }),
5493
+ /* @__PURE__ */ jsx("div", { ref: contentRef, className: "relative min-h-[200px]", children: /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", custom: direction, children: isComplete && showSummary ? /* @__PURE__ */ jsxs(
5494
+ motion.div,
5495
+ {
5496
+ custom: 1,
5497
+ variants: slideVariants2,
5498
+ initial: "enter",
5499
+ animate: "center",
5500
+ exit: "exit",
5501
+ transition: prefersReducedMotion ? { duration: 0 } : { duration: 0.25 },
5502
+ className: "flex flex-col items-center justify-center py-12 text-center",
5503
+ children: [
5504
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center w-16 h-16 rounded-full bg-[hsl(var(--status-ok)/0.15)] mb-4", children: /* @__PURE__ */ jsx(Check, { className: "h-8 w-8 text-[hsl(var(--status-ok))]" }) }),
5505
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-[hsl(var(--text-primary))] mb-1", children: "All steps completed" }),
5506
+ /* @__PURE__ */ jsxs("p", { className: "text-sm text-[hsl(var(--text-secondary))]", children: [
5507
+ "All ",
5508
+ totalSteps,
5509
+ " steps have been successfully completed."
5510
+ ] })
5511
+ ]
5512
+ },
5513
+ "summary"
5514
+ ) : /* @__PURE__ */ jsx(
5515
+ motion.div,
5516
+ {
5517
+ custom: direction,
5518
+ variants: slideVariants2,
5519
+ initial: "enter",
5520
+ animate: "center",
5521
+ exit: "exit",
5522
+ transition: prefersReducedMotion ? { duration: 0 } : { duration: 0.25 },
5523
+ children: steps[currentStep]?.content
5524
+ },
5525
+ currentStep
5526
+ ) }) }),
5527
+ !isComplete && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mt-6 pt-4 border-t border-[hsl(var(--border-subtle)/0.5)]", children: [
5528
+ /* @__PURE__ */ jsxs(
5529
+ "button",
5530
+ {
5531
+ onClick: handleBack,
5532
+ disabled: currentStep === 0,
5533
+ className: cn(
5534
+ "inline-flex items-center gap-1.5 px-4 py-2 rounded-lg text-sm font-medium transition-colors",
5535
+ "border border-[hsl(var(--border-default))]",
5536
+ "text-[hsl(var(--text-primary))]",
5537
+ "hover:bg-[hsl(var(--bg-overlay))]",
5538
+ "disabled:opacity-40 disabled:pointer-events-none"
5539
+ ),
5540
+ children: [
5541
+ /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" }),
5542
+ "Back"
5543
+ ]
5544
+ }
5545
+ ),
5546
+ /* @__PURE__ */ jsxs("span", { className: "text-[11px] text-[hsl(var(--text-tertiary))] tabular-nums", children: [
5547
+ "Step ",
5548
+ currentStep + 1,
5549
+ " of ",
5550
+ totalSteps
5551
+ ] }),
5552
+ /* @__PURE__ */ jsx(
5553
+ "button",
5554
+ {
5555
+ onClick: handleNext,
5556
+ disabled: isValidating,
5557
+ className: cn(
5558
+ "inline-flex items-center gap-1.5 px-4 py-2 rounded-lg text-sm font-medium transition-colors",
5559
+ "bg-[hsl(var(--brand-primary))] text-[hsl(var(--text-on-brand))]",
5560
+ "hover:bg-[hsl(var(--brand-primary))]/90",
5561
+ "disabled:opacity-70 disabled:cursor-not-allowed"
5562
+ ),
5563
+ children: isValidating ? /* @__PURE__ */ jsxs(Fragment, { children: [
5564
+ /* @__PURE__ */ jsx("div", { className: "h-4 w-4 rounded-full border-2 border-[hsl(var(--text-on-brand))] border-t-transparent animate-spin" }),
5565
+ "Validating..."
5566
+ ] }) : currentStep === totalSteps - 1 ? /* @__PURE__ */ jsxs(Fragment, { children: [
5567
+ "Complete",
5568
+ /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" })
5569
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
5570
+ "Next",
5571
+ /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
5572
+ ] })
5573
+ }
5574
+ )
5575
+ ] })
5576
+ ] });
5577
+ }
5578
+ function CopyBlock({
5579
+ content,
5580
+ language,
5581
+ showLineNumbers = false,
5582
+ maxHeight,
5583
+ label,
5584
+ className
5585
+ }) {
5586
+ const prefersReducedMotion = useReducedMotion();
5587
+ const [copied, setCopied] = useState(false);
5588
+ const [isCollapsed, setIsCollapsed] = useState(true);
5589
+ const [needsCollapse, setNeedsCollapse] = useState(false);
5590
+ const contentRef = useRef(null);
5591
+ useEffect(() => {
5592
+ if (!maxHeight || !contentRef.current) return;
5593
+ setNeedsCollapse(contentRef.current.scrollHeight > maxHeight);
5594
+ }, [content, maxHeight]);
5595
+ const lines = useMemo(() => content.split("\n"), [content]);
5596
+ const handleCopy = useCallback(async () => {
5597
+ try {
5598
+ await navigator.clipboard.writeText(content);
5599
+ setCopied(true);
5600
+ setTimeout(() => setCopied(false), 2e3);
5601
+ } catch {
5602
+ const textarea = document.createElement("textarea");
5603
+ textarea.value = content;
5604
+ textarea.style.position = "fixed";
5605
+ textarea.style.opacity = "0";
5606
+ document.body.appendChild(textarea);
5607
+ textarea.select();
5608
+ document.execCommand("copy");
5609
+ document.body.removeChild(textarea);
5610
+ setCopied(true);
5611
+ setTimeout(() => setCopied(false), 2e3);
5612
+ }
5613
+ }, [content]);
5614
+ const shouldCollapse = maxHeight && needsCollapse && isCollapsed;
5615
+ return /* @__PURE__ */ jsxs(
5616
+ "div",
5617
+ {
5618
+ className: cn(
5619
+ "relative group rounded-xl overflow-hidden",
5620
+ "border border-[hsl(var(--border-subtle))]",
5621
+ "bg-[hsl(var(--bg-base))]",
5622
+ className
5623
+ ),
5624
+ children: [
5625
+ (label || language) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-2 border-b border-[hsl(var(--border-subtle)/0.5)] bg-[hsl(var(--bg-surface)/0.3)]", children: [
5626
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
5627
+ label && /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-[hsl(var(--text-secondary))]", children: label }),
5628
+ language && /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded-md bg-[hsl(var(--bg-overlay)/0.5)] px-1.5 py-0.5 text-[10px] font-mono text-[hsl(var(--text-tertiary))]", children: language })
5629
+ ] }),
5630
+ /* @__PURE__ */ jsx(
5631
+ "button",
5632
+ {
5633
+ onClick: handleCopy,
5634
+ className: cn(
5635
+ "inline-flex items-center gap-1 rounded-md px-2 py-1 text-[11px] font-medium transition-all",
5636
+ copied ? "text-[hsl(var(--status-ok))] bg-[hsl(var(--status-ok)/0.1)]" : "text-[hsl(var(--text-tertiary))] hover:text-[hsl(var(--text-primary))] hover:bg-[hsl(var(--bg-elevated))]"
5637
+ ),
5638
+ children: /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: copied ? /* @__PURE__ */ jsxs(
5639
+ motion.span,
5640
+ {
5641
+ initial: prefersReducedMotion ? void 0 : { scale: 0.5, opacity: 0 },
5642
+ animate: prefersReducedMotion ? void 0 : { scale: 1, opacity: 1 },
5643
+ exit: prefersReducedMotion ? void 0 : { scale: 0.5, opacity: 0 },
5644
+ transition: { duration: 0.15 },
5645
+ className: "inline-flex items-center gap-1",
5646
+ children: [
5647
+ /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5" }),
5648
+ "Copied!"
5649
+ ]
5650
+ },
5651
+ "check"
5652
+ ) : /* @__PURE__ */ jsxs(
5653
+ motion.span,
5654
+ {
5655
+ initial: prefersReducedMotion ? void 0 : { scale: 0.5, opacity: 0 },
5656
+ animate: prefersReducedMotion ? void 0 : { scale: 1, opacity: 1 },
5657
+ exit: prefersReducedMotion ? void 0 : { scale: 0.5, opacity: 0 },
5658
+ transition: { duration: 0.15 },
5659
+ className: "inline-flex items-center gap-1",
5660
+ children: [
5661
+ /* @__PURE__ */ jsx(Copy, { className: "h-3.5 w-3.5" }),
5662
+ "Copy"
5663
+ ]
5664
+ },
5665
+ "copy"
5666
+ ) })
5667
+ }
5668
+ )
5669
+ ] }),
5670
+ !label && !language && /* @__PURE__ */ jsx(
5671
+ "button",
5672
+ {
5673
+ onClick: handleCopy,
5674
+ className: cn(
5675
+ "absolute top-2 right-2 z-10 rounded-md p-1.5 transition-all",
5676
+ "opacity-0 group-hover:opacity-100",
5677
+ copied ? "text-[hsl(var(--status-ok))] bg-[hsl(var(--status-ok)/0.1)]" : "text-[hsl(var(--text-tertiary))] hover:text-[hsl(var(--text-primary))] bg-[hsl(var(--bg-elevated)/0.8)] hover:bg-[hsl(var(--bg-elevated))]"
5678
+ ),
5679
+ title: copied ? "Copied!" : "Copy to clipboard",
5680
+ children: copied ? /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(Copy, { className: "h-4 w-4" })
5681
+ }
5682
+ ),
5683
+ /* @__PURE__ */ jsx(
5684
+ "div",
5685
+ {
5686
+ className: "overflow-x-auto",
5687
+ style: shouldCollapse ? { maxHeight, overflow: "hidden" } : void 0,
5688
+ children: /* @__PURE__ */ jsx(
5689
+ "pre",
5690
+ {
5691
+ ref: contentRef,
5692
+ className: cn(
5693
+ "p-4 text-[13px] leading-relaxed font-mono",
5694
+ "text-[hsl(var(--text-primary))]",
5695
+ "whitespace-pre overflow-x-auto"
5696
+ ),
5697
+ children: showLineNumbers ? /* @__PURE__ */ jsx("table", { className: "border-collapse w-full", children: /* @__PURE__ */ jsx("tbody", { children: lines.map((line, i) => /* @__PURE__ */ jsxs("tr", { className: "hover:bg-[hsl(var(--bg-surface)/0.3)]", children: [
5698
+ /* @__PURE__ */ jsx("td", { className: "select-none text-right pr-4 text-[hsl(var(--text-disabled))] text-[11px] tabular-nums w-8 align-top", children: i + 1 }),
5699
+ /* @__PURE__ */ jsx("td", { className: "whitespace-pre", children: line })
5700
+ ] }, i)) }) }) : content
5701
+ }
5702
+ )
5703
+ }
5704
+ ),
5705
+ maxHeight && needsCollapse && /* @__PURE__ */ jsxs(Fragment, { children: [
5706
+ isCollapsed && /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 right-0 h-16 bg-gradient-to-t from-[hsl(var(--bg-base))] to-transparent pointer-events-none" }),
5707
+ /* @__PURE__ */ jsx("div", { className: "relative border-t border-[hsl(var(--border-subtle)/0.3)]", children: /* @__PURE__ */ jsx(
5708
+ "button",
5709
+ {
5710
+ onClick: () => setIsCollapsed((c) => !c),
5711
+ className: cn(
5712
+ "w-full flex items-center justify-center gap-1.5 py-2 text-[11px] font-medium",
5713
+ "text-[hsl(var(--text-secondary))] hover:text-[hsl(var(--text-primary))]",
5714
+ "hover:bg-[hsl(var(--bg-surface)/0.3)] transition-colors"
5715
+ ),
5716
+ children: isCollapsed ? /* @__PURE__ */ jsxs(Fragment, { children: [
5717
+ "Show more (",
5718
+ lines.length,
5719
+ " lines)",
5720
+ /* @__PURE__ */ jsx(ChevronDown, { className: "h-3.5 w-3.5" })
5721
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
5722
+ "Show less",
5723
+ /* @__PURE__ */ jsx(ChevronUp, { className: "h-3.5 w-3.5" })
5724
+ ] })
5725
+ }
5726
+ ) })
5727
+ ] })
5728
+ ]
5729
+ }
5730
+ );
5731
+ }
2809
5732
 
2810
- export { AnimatedCounter, Avatar, Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ConfirmDialog, DataTable, DropdownMenu, EmptyState, FilterPill, LogViewer, MetricCard, PipelineStage, Popover, PortStatusGrid, Progress, RadioGroup, SeverityTimeline, Sheet, Skeleton, SkeletonCard, SkeletonText, Slider, Sparkline, StatusBadge, StatusPulse, SuccessCheckmark, Tabs, ThresholdGauge, TimeRangeSelector, Toaster, Tooltip2 as Tooltip, TruncatedText, UptimeTracker, UtilizationBar, createBadgeVariant, defaultPulseConfigMap, defaultStatusMap };
5733
+ export { AnimatedCounter, Avatar, Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ColorInput, CommandBar, ConfidenceBar, ConfirmDialog, CopyBlock, DataTable, DiffViewer, DragHandle, DropdownMenu, EmptyState, FilterPill, HeatmapCalendar, InfiniteScroll, KanbanColumn, LiveFeed, LogViewer, MetricCard, NotificationStack, PipelineStage, Popover, PortStatusGrid, Progress, RadioGroup, RealtimeValue, SeverityTimeline, Sheet, Skeleton, SkeletonCard, SkeletonText, Slider, SmartTable, SortableList, Sparkline, StatusBadge, StatusPulse, StepWizard, StreamingText, SuccessCheckmark, Tabs, ThresholdGauge, TimeRangeSelector, Toaster, Tooltip2 as Tooltip, TruncatedText, TypingIndicator, UptimeTracker, UtilizationBar, createBadgeVariant, defaultPulseConfigMap, defaultStatusMap };
2811
5734
  //# sourceMappingURL=index.js.map
2812
5735
  //# sourceMappingURL=index.js.map