@annondeveloper/ui-kit 0.2.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.
package/dist/form.js CHANGED
@@ -131,8 +131,7 @@ function RHFToggleSwitch({
131
131
  {
132
132
  enabled: !!field.value,
133
133
  onChange: field.onChange,
134
- disabled,
135
- label
134
+ disabled
136
135
  }
137
136
  ),
138
137
  label && /* @__PURE__ */ jsx("span", { className: "text-sm text-[hsl(var(--text-primary))]", children: label })
package/dist/form.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/form-integration.tsx"],"names":[],"mappings":"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","file":"form.js","sourcesContent":["'use client'\n\nimport type React from 'react'\nimport { Controller, type Control, type FieldValues, type Path, type RegisterOptions } from 'react-hook-form'\nimport { FormInput, INPUT_CLS } from './components/form-input'\nimport { Select, type SelectOption } from './components/select'\nimport { Checkbox } from './components/checkbox'\nimport { ToggleSwitch } from './components/toggle-switch'\nimport { cn } from './utils'\n\n// ── Shared error display ──────────────────────────────────────────────────\n\nfunction FieldError({ message }: { message?: string }) {\n if (!message) return null\n return (\n <p className=\"mt-1 text-xs text-[hsl(var(--status-critical))]\">{message}</p>\n )\n}\n\n// ── RHFFormInput ──────────────────────────────────────────────────────────\n\nexport interface RHFFormInputProps<T extends FieldValues> {\n control: Control<T>\n name: Path<T>\n rules?: RegisterOptions<T, Path<T>>\n label: string\n type?: string\n placeholder?: string\n required?: boolean\n disabled?: boolean\n hint?: string\n className?: string\n autoComplete?: string\n}\n\n/**\n * FormInput wrapper for react-hook-form.\n * Shows validation errors from fieldState.error automatically.\n */\nexport function RHFFormInput<T extends FieldValues>({\n control, name, rules, label, type = 'text',\n placeholder, required, disabled, hint, className, autoComplete,\n}: RHFFormInputProps<T>): React.JSX.Element {\n return (\n <Controller\n control={control}\n name={name}\n rules={rules}\n render={({ field, fieldState }) => (\n <div className={cn('space-y-1.5', className)}>\n <FormInput\n label={label}\n value={field.value ?? ''}\n onChange={field.onChange}\n type={type}\n placeholder={placeholder}\n required={required}\n disabled={disabled}\n hint={fieldState.error ? undefined : hint}\n autoComplete={autoComplete}\n />\n <FieldError message={fieldState.error?.message} />\n </div>\n )}\n />\n )\n}\n\n// ── RHFSelect ─────────────────────────────────────────────────────────────\n\nexport interface RHFSelectProps<T extends FieldValues> {\n control: Control<T>\n name: Path<T>\n rules?: RegisterOptions<T, Path<T>>\n options: SelectOption[]\n placeholder?: string\n disabled?: boolean\n className?: string\n label?: string\n}\n\n/**\n * Select wrapper for react-hook-form.\n * Shows validation errors from fieldState.error automatically.\n */\nexport function RHFSelect<T extends FieldValues>({\n control, name, rules, options, placeholder, disabled, className, label,\n}: RHFSelectProps<T>): React.JSX.Element {\n return (\n <Controller\n control={control}\n name={name}\n rules={rules}\n render={({ field, fieldState }) => (\n <div className={cn('space-y-1.5', className)}>\n {label && (\n <label className=\"mb-1.5 block text-xs font-medium uppercase tracking-wider text-[hsl(var(--text-secondary))]\">\n {label}\n </label>\n )}\n <Select\n value={field.value ?? ''}\n onValueChange={field.onChange}\n options={options}\n placeholder={placeholder}\n disabled={disabled}\n className={fieldState.error ? 'border-[hsl(var(--status-critical))]' : undefined}\n />\n <FieldError message={fieldState.error?.message} />\n </div>\n )}\n />\n )\n}\n\n// ── RHFCheckbox ───────────────────────────────────────────────────────────\n\nexport interface RHFCheckboxProps<T extends FieldValues> {\n control: Control<T>\n name: Path<T>\n rules?: RegisterOptions<T, Path<T>>\n label?: string\n disabled?: boolean\n className?: string\n}\n\n/**\n * Checkbox wrapper for react-hook-form.\n * Shows validation errors from fieldState.error automatically.\n */\nexport function RHFCheckbox<T extends FieldValues>({\n control, name, rules, label, disabled, className,\n}: RHFCheckboxProps<T>): React.JSX.Element {\n return (\n <Controller\n control={control}\n name={name}\n rules={rules}\n render={({ field, fieldState }) => (\n <div className={cn('space-y-1', className)}>\n <div className=\"flex items-center gap-2\">\n <Checkbox\n checked={!!field.value}\n onChange={(e) => field.onChange(e.target.checked)}\n disabled={disabled}\n />\n {label && (\n <span className=\"text-sm text-[hsl(var(--text-primary))]\">{label}</span>\n )}\n </div>\n <FieldError message={fieldState.error?.message} />\n </div>\n )}\n />\n )\n}\n\n// ── RHFToggleSwitch ───────────────────────────────────────────────────────\n\nexport interface RHFToggleSwitchProps<T extends FieldValues> {\n control: Control<T>\n name: Path<T>\n rules?: RegisterOptions<T, Path<T>>\n label?: string\n disabled?: boolean\n className?: string\n}\n\n/**\n * ToggleSwitch wrapper for react-hook-form.\n * Shows validation errors from fieldState.error automatically.\n */\nexport function RHFToggleSwitch<T extends FieldValues>({\n control, name, rules, label, disabled, className,\n}: RHFToggleSwitchProps<T>): React.JSX.Element {\n return (\n <Controller\n control={control}\n name={name}\n rules={rules}\n render={({ field, fieldState }) => (\n <div className={cn('space-y-1', className)}>\n <div className=\"flex items-center gap-2\">\n <ToggleSwitch\n enabled={!!field.value}\n onChange={field.onChange}\n disabled={disabled}\n label={label}\n />\n {label && (\n <span className=\"text-sm text-[hsl(var(--text-primary))]\">{label}</span>\n )}\n </div>\n <FieldError message={fieldState.error?.message} />\n </div>\n )}\n />\n )\n}\n"]}
1
+ {"version":3,"sources":["../src/form-integration.tsx"],"names":[],"mappings":"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","file":"form.js","sourcesContent":["'use client'\n\nimport type React from 'react'\nimport { Controller, type Control, type FieldValues, type Path, type RegisterOptions } from 'react-hook-form'\nimport { FormInput, INPUT_CLS } from './components/form-input'\nimport { Select, type SelectOption } from './components/select'\nimport { Checkbox } from './components/checkbox'\nimport { ToggleSwitch } from './components/toggle-switch'\nimport { cn } from './utils'\n\n// ── Shared error display ──────────────────────────────────────────────────\n\nfunction FieldError({ message }: { message?: string }) {\n if (!message) return null\n return (\n <p className=\"mt-1 text-xs text-[hsl(var(--status-critical))]\">{message}</p>\n )\n}\n\n// ── RHFFormInput ──────────────────────────────────────────────────────────\n\nexport interface RHFFormInputProps<T extends FieldValues> {\n control: Control<T>\n name: Path<T>\n rules?: RegisterOptions<T, Path<T>>\n label: string\n type?: string\n placeholder?: string\n required?: boolean\n disabled?: boolean\n hint?: string\n className?: string\n autoComplete?: string\n}\n\n/**\n * FormInput wrapper for react-hook-form.\n * Shows validation errors from fieldState.error automatically.\n */\nexport function RHFFormInput<T extends FieldValues>({\n control, name, rules, label, type = 'text',\n placeholder, required, disabled, hint, className, autoComplete,\n}: RHFFormInputProps<T>): React.JSX.Element {\n return (\n <Controller\n control={control}\n name={name}\n rules={rules}\n render={({ field, fieldState }) => (\n <div className={cn('space-y-1.5', className)}>\n <FormInput\n label={label}\n value={field.value ?? ''}\n onChange={field.onChange}\n type={type}\n placeholder={placeholder}\n required={required}\n disabled={disabled}\n hint={fieldState.error ? undefined : hint}\n autoComplete={autoComplete}\n />\n <FieldError message={fieldState.error?.message} />\n </div>\n )}\n />\n )\n}\n\n// ── RHFSelect ─────────────────────────────────────────────────────────────\n\nexport interface RHFSelectProps<T extends FieldValues> {\n control: Control<T>\n name: Path<T>\n rules?: RegisterOptions<T, Path<T>>\n options: SelectOption[]\n placeholder?: string\n disabled?: boolean\n className?: string\n label?: string\n}\n\n/**\n * Select wrapper for react-hook-form.\n * Shows validation errors from fieldState.error automatically.\n */\nexport function RHFSelect<T extends FieldValues>({\n control, name, rules, options, placeholder, disabled, className, label,\n}: RHFSelectProps<T>): React.JSX.Element {\n return (\n <Controller\n control={control}\n name={name}\n rules={rules}\n render={({ field, fieldState }) => (\n <div className={cn('space-y-1.5', className)}>\n {label && (\n <label className=\"mb-1.5 block text-xs font-medium uppercase tracking-wider text-[hsl(var(--text-secondary))]\">\n {label}\n </label>\n )}\n <Select\n value={field.value ?? ''}\n onValueChange={field.onChange}\n options={options}\n placeholder={placeholder}\n disabled={disabled}\n className={fieldState.error ? 'border-[hsl(var(--status-critical))]' : undefined}\n />\n <FieldError message={fieldState.error?.message} />\n </div>\n )}\n />\n )\n}\n\n// ── RHFCheckbox ───────────────────────────────────────────────────────────\n\nexport interface RHFCheckboxProps<T extends FieldValues> {\n control: Control<T>\n name: Path<T>\n rules?: RegisterOptions<T, Path<T>>\n label?: string\n disabled?: boolean\n className?: string\n}\n\n/**\n * Checkbox wrapper for react-hook-form.\n * Shows validation errors from fieldState.error automatically.\n */\nexport function RHFCheckbox<T extends FieldValues>({\n control, name, rules, label, disabled, className,\n}: RHFCheckboxProps<T>): React.JSX.Element {\n return (\n <Controller\n control={control}\n name={name}\n rules={rules}\n render={({ field, fieldState }) => (\n <div className={cn('space-y-1', className)}>\n <div className=\"flex items-center gap-2\">\n <Checkbox\n checked={!!field.value}\n onChange={(e) => field.onChange(e.target.checked)}\n disabled={disabled}\n />\n {label && (\n <span className=\"text-sm text-[hsl(var(--text-primary))]\">{label}</span>\n )}\n </div>\n <FieldError message={fieldState.error?.message} />\n </div>\n )}\n />\n )\n}\n\n// ── RHFToggleSwitch ───────────────────────────────────────────────────────\n\nexport interface RHFToggleSwitchProps<T extends FieldValues> {\n control: Control<T>\n name: Path<T>\n rules?: RegisterOptions<T, Path<T>>\n label?: string\n disabled?: boolean\n className?: string\n}\n\n/**\n * ToggleSwitch wrapper for react-hook-form.\n * Shows validation errors from fieldState.error automatically.\n */\nexport function RHFToggleSwitch<T extends FieldValues>({\n control, name, rules, label, disabled, className,\n}: RHFToggleSwitchProps<T>): React.JSX.Element {\n return (\n <Controller\n control={control}\n name={name}\n rules={rules}\n render={({ field, fieldState }) => (\n <div className={cn('space-y-1', className)}>\n <div className=\"flex items-center gap-2\">\n <ToggleSwitch\n enabled={!!field.value}\n onChange={field.onChange}\n disabled={disabled}\n />\n {label && (\n <span className=\"text-sm text-[hsl(var(--text-primary))]\">{label}</span>\n )}\n </div>\n <FieldError message={fieldState.error?.message} />\n </div>\n )}\n />\n )\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1300,6 +1300,8 @@ interface InfiniteScrollProps<T> {
1300
1300
  itemHeight?: number;
1301
1301
  /** Content to display when items array is empty. */
1302
1302
  emptyState?: React.ReactNode;
1303
+ /** Function to derive a stable key for each item. Falls back to index. */
1304
+ getItemKey?: (item: T, index: number) => string | number;
1303
1305
  /** Additional class name for the scroll container. */
1304
1306
  className?: string;
1305
1307
  }
@@ -1308,7 +1310,7 @@ interface InfiniteScrollProps<T> {
1308
1310
  * Supports optional height-based virtualization, loading indicators, scroll-to-top,
1309
1311
  * empty states, and skeleton placeholders. No scroll event listeners used.
1310
1312
  */
1311
- declare function InfiniteScroll<T>({ items, renderItem, loadMore, hasMore, isLoading, threshold, itemHeight, emptyState, className, }: InfiniteScrollProps<T>): React.JSX.Element;
1313
+ declare function InfiniteScroll<T>({ items, renderItem, loadMore, hasMore, isLoading, threshold, itemHeight, getItemKey, emptyState, className, }: InfiniteScrollProps<T>): React.JSX.Element;
1312
1314
 
1313
1315
  /** Props for the ColorInput component. */
1314
1316
  interface ColorInputProps {
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { cn, clamp } from './chunk-2DWZVHZS.js';
1
+ import { cn, Select, clamp } from './chunk-2DWZVHZS.js';
2
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
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';
@@ -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
  {
@@ -2850,6 +2851,10 @@ function StreamingText({
2850
2851
  const containerRef = useRef(null);
2851
2852
  const prevStreamingRef = useRef(isStreaming);
2852
2853
  const [copied, setCopied] = useState(false);
2854
+ const copyTimerRef = useRef(null);
2855
+ useEffect(() => () => {
2856
+ if (copyTimerRef.current) clearTimeout(copyTimerRef.current);
2857
+ }, []);
2853
2858
  useEffect(() => {
2854
2859
  if (prevStreamingRef.current && !isStreaming) {
2855
2860
  onComplete?.();
@@ -2865,7 +2870,8 @@ function StreamingText({
2865
2870
  const handleCopy = useCallback(() => {
2866
2871
  void navigator.clipboard.writeText(text).then(() => {
2867
2872
  setCopied(true);
2868
- setTimeout(() => setCopied(false), 2e3);
2873
+ if (copyTimerRef.current) clearTimeout(copyTimerRef.current);
2874
+ copyTimerRef.current = setTimeout(() => setCopied(false), 2e3);
2869
2875
  });
2870
2876
  }, [text]);
2871
2877
  const formatted = formatSegments(text);
@@ -2883,7 +2889,8 @@ function StreamingText({
2883
2889
  className: "inline-block w-[2px] h-[1.1em] align-text-bottom ml-0.5 bg-[hsl(var(--brand-primary))]",
2884
2890
  style: reduced ? { opacity: 1 } : {
2885
2891
  animation: `streaming-cursor-blink ${speed}ms step-end infinite`
2886
- }
2892
+ },
2893
+ "aria-hidden": "true"
2887
2894
  }
2888
2895
  ),
2889
2896
  /* @__PURE__ */ jsx(AnimatePresence, { children: showCursor && !isStreaming && text.length > 0 && /* @__PURE__ */ jsx(
@@ -2916,13 +2923,7 @@ function StreamingText({
2916
2923
  "aria-label": copied ? "Copied" : "Copy to clipboard",
2917
2924
  children: copied ? /* @__PURE__ */ jsx(Check, { className: "size-3.5" }) : /* @__PURE__ */ jsx(Copy, { className: "size-3.5" })
2918
2925
  }
2919
- ) }),
2920
- showCursor && isStreaming && !reduced && /* @__PURE__ */ jsx("style", { children: `
2921
- @keyframes streaming-cursor-blink {
2922
- 0%, 100% { opacity: 1; }
2923
- 50% { opacity: 0; }
2924
- }
2925
- ` })
2926
+ ) })
2926
2927
  ] });
2927
2928
  }
2928
2929
  var DOT_SIZES = { sm: "size-1.5", md: "size-2" };
@@ -3191,6 +3192,8 @@ function LiveFeed({
3191
3192
  ref: scrollRef,
3192
3193
  onScroll: handleScroll,
3193
3194
  className: "flex-1 overflow-y-auto",
3195
+ "aria-live": "polite",
3196
+ "aria-atomic": "false",
3194
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(
3195
3198
  motion.div,
3196
3199
  {
@@ -3257,7 +3260,25 @@ function LiveFeed({
3257
3260
  ) })
3258
3261
  ] });
3259
3262
  }
3263
+ var MAX_LINES = 2e3;
3260
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
+ }
3261
3282
  const m = oldLines.length;
3262
3283
  const n = newLines.length;
3263
3284
  const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
@@ -3499,8 +3520,13 @@ function toDateKey(d) {
3499
3520
  return d.toISOString().slice(0, 10);
3500
3521
  }
3501
3522
  function parseDate(s) {
3523
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(s)) {
3524
+ return /* @__PURE__ */ new Date();
3525
+ }
3502
3526
  const [y, m, d] = s.split("-").map(Number);
3503
- return new Date(y, m - 1, d);
3527
+ const date = new Date(y, m - 1, d);
3528
+ if (isNaN(date.getTime())) return /* @__PURE__ */ new Date();
3529
+ return date;
3504
3530
  }
3505
3531
  function HeatmapCalendar({
3506
3532
  data,
@@ -3629,6 +3655,12 @@ var TYPE_ICON_COLOR = {
3629
3655
  warning: "text-[hsl(var(--status-warning))]",
3630
3656
  error: "text-[hsl(var(--status-critical))]"
3631
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
+ };
3632
3664
  var POSITION_CLASSES = {
3633
3665
  "top-right": "top-4 right-4",
3634
3666
  "top-left": "top-4 left-4",
@@ -3768,7 +3800,7 @@ function NotificationCard({
3768
3800
  duration > 0 && /* @__PURE__ */ jsx("div", { className: "h-0.5 bg-[hsl(var(--bg-overlay))]", children: /* @__PURE__ */ jsx(
3769
3801
  "div",
3770
3802
  {
3771
- className: cn("h-full transition-[width] duration-100", TYPE_COLOR[type].replace("border-l-", "bg-")),
3803
+ className: cn("h-full transition-[width] duration-100", TYPE_PROGRESS_BG[type]),
3772
3804
  style: { width: `${progress}%` }
3773
3805
  }
3774
3806
  ) })
@@ -4416,7 +4448,8 @@ function CommandBar({
4416
4448
  const [recentIds, setRecentIds] = useState(() => {
4417
4449
  if (typeof window === "undefined") return [];
4418
4450
  try {
4419
- return JSON.parse(localStorage.getItem(recentKey) ?? "[]");
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) : [];
4420
4453
  } catch {
4421
4454
  return [];
4422
4455
  }
@@ -4667,12 +4700,16 @@ function SortableList({
4667
4700
  const containerRef = useRef(null);
4668
4701
  const startPos = useRef({ x: 0, y: 0 });
4669
4702
  const dragItemId = useRef(null);
4703
+ const dragIdxRef = useRef(null);
4704
+ const overIdxRef = useRef(null);
4670
4705
  const handlePointerDown = useCallback(
4671
4706
  (index) => (e) => {
4672
4707
  e.preventDefault();
4673
4708
  if (e.button !== 0) return;
4674
4709
  setDragIdx(index);
4675
4710
  setOverIdx(index);
4711
+ dragIdxRef.current = index;
4712
+ overIdxRef.current = index;
4676
4713
  dragItemId.current = items[index]?.id ?? null;
4677
4714
  startPos.current = { x: e.clientX, y: e.clientY };
4678
4715
  const handlePointerMove = (ev) => {
@@ -4685,6 +4722,7 @@ function SortableList({
4685
4722
  const midY = rect.top + rect.height / 2;
4686
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;
4687
4724
  if (isOver) {
4725
+ overIdxRef.current = i;
4688
4726
  setOverIdx(i);
4689
4727
  break;
4690
4728
  }
@@ -4693,18 +4731,18 @@ function SortableList({
4693
4731
  const handlePointerUp = () => {
4694
4732
  document.removeEventListener("pointermove", handlePointerMove);
4695
4733
  document.removeEventListener("pointerup", handlePointerUp);
4696
- setDragIdx((prev) => {
4697
- setOverIdx((over) => {
4698
- if (prev !== null && over !== null && prev !== over) {
4699
- const newItems = [...items];
4700
- const [moved] = newItems.splice(prev, 1);
4701
- if (moved) newItems.splice(over, 0, moved);
4702
- setTimeout(() => onReorder(newItems), 0);
4703
- }
4704
- return null;
4705
- });
4706
- return null;
4707
- });
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);
4708
4746
  };
4709
4747
  document.addEventListener("pointermove", handlePointerMove);
4710
4748
  document.addEventListener("pointerup", handlePointerUp);
@@ -4840,6 +4878,7 @@ function InfiniteScroll({
4840
4878
  isLoading = false,
4841
4879
  threshold = 200,
4842
4880
  itemHeight,
4881
+ getItemKey,
4843
4882
  emptyState,
4844
4883
  className
4845
4884
  }) {
@@ -4848,6 +4887,10 @@ function InfiniteScroll({
4848
4887
  const sentinelRef = useRef(null);
4849
4888
  const [showScrollTop, setShowScrollTop] = useState(false);
4850
4889
  const loadingRef = useRef(false);
4890
+ const loadMoreRef = useRef(loadMore);
4891
+ useEffect(() => {
4892
+ loadMoreRef.current = loadMore;
4893
+ }, [loadMore]);
4851
4894
  useEffect(() => {
4852
4895
  const sentinel = sentinelRef.current;
4853
4896
  if (!sentinel) return;
@@ -4856,7 +4899,7 @@ function InfiniteScroll({
4856
4899
  const entry = entries[0];
4857
4900
  if (entry?.isIntersecting && hasMore && !isLoading && !loadingRef.current) {
4858
4901
  loadingRef.current = true;
4859
- const result = loadMore();
4902
+ const result = loadMoreRef.current();
4860
4903
  if (result && typeof result.then === "function") {
4861
4904
  result.then(() => {
4862
4905
  loadingRef.current = false;
@@ -4875,7 +4918,7 @@ function InfiniteScroll({
4875
4918
  );
4876
4919
  observer.observe(sentinel);
4877
4920
  return () => observer.disconnect();
4878
- }, [hasMore, isLoading, loadMore, threshold]);
4921
+ }, [hasMore, isLoading, threshold]);
4879
4922
  useEffect(() => {
4880
4923
  if (!isLoading) loadingRef.current = false;
4881
4924
  }, [isLoading]);
@@ -4934,7 +4977,7 @@ function InfiniteScroll({
4934
4977
  children: [
4935
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)) }) }) : (
4936
4979
  /* Non-virtualized rendering */
4937
- items.map((item, index) => /* @__PURE__ */ jsx("div", { children: renderItem(item, index) }, index))
4980
+ items.map((item, index) => /* @__PURE__ */ jsx("div", { children: renderItem(item, index) }, getItemKey ? getItemKey(item, index) : index))
4938
4981
  ),
4939
4982
  isLoading && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center py-6 gap-2", children: [
4940
4983
  /* @__PURE__ */ jsx(Loader2, { className: "h-5 w-5 text-[hsl(var(--brand-primary))] animate-spin" }),
@@ -5025,6 +5068,9 @@ function formatColor(hex, fmt) {
5025
5068
  const { h, s, l } = rgbToHsl(r, g, b);
5026
5069
  return `hsl(${Math.round(h * 360)}, ${Math.round(s * 100)}%, ${Math.round(l * 100)}%)`;
5027
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
+ }
5028
5074
  var RECENT_COLORS_KEY = "ui-kit-recent-colors";
5029
5075
  var MAX_RECENT = 8;
5030
5076
  function ColorInput({
@@ -5037,16 +5083,23 @@ function ColorInput({
5037
5083
  className
5038
5084
  }) {
5039
5085
  const prefersReducedMotion = useReducedMotion();
5086
+ const isValidHex = /^#[0-9a-f]{3,8}$/i.test(value);
5087
+ const safeValue = isValidHex ? value : "#000000";
5040
5088
  const [open, setOpen] = useState(false);
5041
5089
  const [copied, setCopied] = useState(false);
5042
5090
  const [textInput, setTextInput] = useState("");
5043
5091
  const [alpha, setAlpha] = useState(1);
5044
5092
  const panelRef = useRef(null);
5045
5093
  const satAreaRef = useRef(null);
5094
+ const copyTimerRef = useRef(null);
5095
+ useEffect(() => () => {
5096
+ if (copyTimerRef.current) clearTimeout(copyTimerRef.current);
5097
+ }, []);
5046
5098
  const [recentColors, setRecentColors] = useState(() => {
5047
5099
  if (typeof window === "undefined") return [];
5048
5100
  try {
5049
- return JSON.parse(localStorage.getItem(RECENT_COLORS_KEY) ?? "[]");
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) : [];
5050
5103
  } catch {
5051
5104
  return [];
5052
5105
  }
@@ -5061,11 +5114,11 @@ function ColorInput({
5061
5114
  return updated;
5062
5115
  });
5063
5116
  }, []);
5064
- const { r, g, b } = useMemo(() => hexToRgb(value), [value]);
5117
+ const { r, g, b } = useMemo(() => hexToRgb(safeValue), [safeValue]);
5065
5118
  const hsl = useMemo(() => rgbToHsl(r, g, b), [r, g, b]);
5066
5119
  useEffect(() => {
5067
- setTextInput(formatColor(value, format));
5068
- }, [value, format]);
5120
+ setTextInput(formatColor(safeValue, format));
5121
+ }, [safeValue, format]);
5069
5122
  useEffect(() => {
5070
5123
  if (!open) return;
5071
5124
  const handler = (e) => {
@@ -5131,16 +5184,17 @@ function ColorInput({
5131
5184
  onChange(rgbToHex(rgb.r, rgb.g, rgb.b));
5132
5185
  return;
5133
5186
  }
5134
- setTextInput(formatColor(value, format));
5135
- }, [textInput, value, format, onChange]);
5187
+ setTextInput(formatColor(safeValue, format));
5188
+ }, [textInput, safeValue, format, onChange]);
5136
5189
  const handleCopy = useCallback(async () => {
5137
5190
  try {
5138
- await navigator.clipboard.writeText(formatColor(value, format));
5191
+ await navigator.clipboard.writeText(formatColor(safeValue, format));
5139
5192
  setCopied(true);
5140
- setTimeout(() => setCopied(false), 1500);
5193
+ if (copyTimerRef.current) clearTimeout(copyTimerRef.current);
5194
+ copyTimerRef.current = setTimeout(() => setCopied(false), 1500);
5141
5195
  } catch {
5142
5196
  }
5143
- }, [value, format]);
5197
+ }, [safeValue, format]);
5144
5198
  const markerX = hsl.s * 100;
5145
5199
  const markerY = (1 - (hsl.l - 0.05) / 0.9) * 100;
5146
5200
  return /* @__PURE__ */ jsxs("div", { ref: panelRef, className: cn("relative inline-block", className), children: [
@@ -5160,7 +5214,7 @@ function ColorInput({
5160
5214
  "span",
5161
5215
  {
5162
5216
  className: "h-5 w-5 rounded-md border border-[hsl(var(--border-subtle))]",
5163
- style: { backgroundColor: value }
5217
+ style: { backgroundColor: isSafeColor(safeValue) ? safeValue : void 0 }
5164
5218
  }
5165
5219
  ),
5166
5220
  /* @__PURE__ */ jsx("span", { className: "font-mono text-xs text-[hsl(var(--text-primary))]", children: formatColor(value, format) })
@@ -5198,7 +5252,7 @@ function ColorInput({
5198
5252
  style: {
5199
5253
  left: `${markerX}%`,
5200
5254
  top: `${Math.max(0, Math.min(100, markerY))}%`,
5201
- backgroundColor: value
5255
+ backgroundColor: isSafeColor(safeValue) ? safeValue : void 0
5202
5256
  }
5203
5257
  }
5204
5258
  )
@@ -5228,7 +5282,7 @@ function ColorInput({
5228
5282
  onChange: (e) => setAlpha(Number(e.target.value) / 100),
5229
5283
  className: "w-full h-3 rounded-full appearance-none cursor-pointer",
5230
5284
  style: {
5231
- background: `linear-gradient(to right, transparent, ${value})`
5285
+ background: `linear-gradient(to right, transparent, ${isSafeColor(safeValue) ? safeValue : "#000"})`
5232
5286
  }
5233
5287
  }
5234
5288
  ) }),
@@ -5278,7 +5332,7 @@ function ColorInput({
5278
5332
  "h-6 w-6 rounded-md border transition-all",
5279
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"
5280
5334
  ),
5281
- style: { backgroundColor: color },
5335
+ style: { backgroundColor: isSafeColor(color) ? color : void 0 },
5282
5336
  title: color
5283
5337
  },
5284
5338
  color
@@ -5294,7 +5348,7 @@ function ColorInput({
5294
5348
  "h-6 w-6 rounded-md border border-[hsl(var(--border-subtle))]",
5295
5349
  "hover:scale-110 transition-transform"
5296
5350
  ),
5297
- style: { backgroundColor: color },
5351
+ style: { backgroundColor: isSafeColor(color) ? color : void 0 },
5298
5352
  title: color
5299
5353
  },
5300
5354
  color
@@ -5345,6 +5399,8 @@ function StepWizard({
5345
5399
  const [direction, setDirection] = useState(1);
5346
5400
  const [isComplete, setIsComplete] = useState(false);
5347
5401
  const contentRef = useRef(null);
5402
+ const wizardRef = useRef(null);
5403
+ const validatingRef = useRef(false);
5348
5404
  useEffect(() => {
5349
5405
  try {
5350
5406
  sessionStorage.setItem(sessionKey, JSON.stringify({
@@ -5362,20 +5418,25 @@ function StepWizard({
5362
5418
  onStepChange?.(idx);
5363
5419
  }, [currentStep, onStepChange]);
5364
5420
  const handleNext = useCallback(async () => {
5421
+ if (validatingRef.current) return;
5365
5422
  const step = steps[currentStep];
5366
5423
  if (step?.validate) {
5424
+ validatingRef.current = true;
5367
5425
  setIsValidating(true);
5368
5426
  try {
5369
5427
  const valid = await step.validate();
5370
5428
  if (!valid) {
5371
5429
  setIsValidating(false);
5430
+ validatingRef.current = false;
5372
5431
  return;
5373
5432
  }
5374
5433
  } catch {
5375
5434
  setIsValidating(false);
5435
+ validatingRef.current = false;
5376
5436
  return;
5377
5437
  }
5378
5438
  setIsValidating(false);
5439
+ validatingRef.current = false;
5379
5440
  }
5380
5441
  setCompleted((prev) => new Set(prev).add(currentStep));
5381
5442
  if (currentStep < totalSteps - 1) {
@@ -5403,6 +5464,7 @@ function StepWizard({
5403
5464
  }, [completed, allowSkip, currentStep, goToStep]);
5404
5465
  useEffect(() => {
5405
5466
  const handler = (e) => {
5467
+ if (!wizardRef.current?.contains(e.target)) return;
5406
5468
  if (e.key === "Enter" && !e.shiftKey && !(e.target instanceof HTMLTextAreaElement)) {
5407
5469
  handleNext();
5408
5470
  }
@@ -5422,7 +5484,7 @@ function StepWizard({
5422
5484
  })
5423
5485
  };
5424
5486
  const isHorizontal = orientation === "horizontal";
5425
- return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col", className), children: [
5487
+ return /* @__PURE__ */ jsxs("div", { ref: wizardRef, className: cn("flex flex-col", className), children: [
5426
5488
  /* @__PURE__ */ jsx("div", { className: cn(
5427
5489
  "mb-6",
5428
5490
  isHorizontal ? "flex items-center" : "flex flex-col gap-1"
@@ -5588,6 +5650,10 @@ function CopyBlock({
5588
5650
  const [isCollapsed, setIsCollapsed] = useState(true);
5589
5651
  const [needsCollapse, setNeedsCollapse] = useState(false);
5590
5652
  const contentRef = useRef(null);
5653
+ const copyTimerRef = useRef(null);
5654
+ useEffect(() => () => {
5655
+ if (copyTimerRef.current) clearTimeout(copyTimerRef.current);
5656
+ }, []);
5591
5657
  useEffect(() => {
5592
5658
  if (!maxHeight || !contentRef.current) return;
5593
5659
  setNeedsCollapse(contentRef.current.scrollHeight > maxHeight);
@@ -5597,18 +5663,10 @@ function CopyBlock({
5597
5663
  try {
5598
5664
  await navigator.clipboard.writeText(content);
5599
5665
  setCopied(true);
5600
- setTimeout(() => setCopied(false), 2e3);
5666
+ if (copyTimerRef.current) clearTimeout(copyTimerRef.current);
5667
+ copyTimerRef.current = setTimeout(() => setCopied(false), 2e3);
5601
5668
  } catch {
5602
- const textarea = document.createElement("textarea");
5603
- textarea.value = content;
5604
- textarea.style.position = "fixed";
5605
- textarea.style.opacity = "0";
5606
- document.body.appendChild(textarea);
5607
- textarea.select();
5608
- document.execCommand("copy");
5609
- document.body.removeChild(textarea);
5610
- setCopied(true);
5611
- setTimeout(() => setCopied(false), 2e3);
5669
+ console.warn("Clipboard API not available");
5612
5670
  }
5613
5671
  }, [content]);
5614
5672
  const shouldCollapse = maxHeight && needsCollapse && isCollapsed;