@annondeveloper/ui-kit 0.1.0 → 0.2.1

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