@nextlyhq/ui 0.0.2-alpha.54 → 0.0.2-alpha.57

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/index.cjs CHANGED
@@ -29,8 +29,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
29
29
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
30
 
31
31
  // src/index.ts
32
- var index_exports = {};
33
- __export(index_exports, {
32
+ var src_exports = {};
33
+ __export(src_exports, {
34
34
  Accordion: () => Accordion,
35
35
  AccordionContent: () => AccordionContent,
36
36
  AccordionItem: () => AccordionItem,
@@ -65,6 +65,7 @@ __export(index_exports, {
65
65
  Collapsible: () => Collapsible,
66
66
  CollapsibleContent: () => CollapsibleContent2,
67
67
  CollapsibleTrigger: () => CollapsibleTrigger2,
68
+ ColorPicker: () => ColorPicker,
68
69
  Command: () => Command,
69
70
  CommandDialog: () => CommandDialog,
70
71
  CommandEmpty: () => CommandEmpty,
@@ -149,7 +150,10 @@ __export(index_exports, {
149
150
  SheetPortal: () => SheetPortal,
150
151
  SheetTitle: () => SheetTitle,
151
152
  SheetTrigger: () => SheetTrigger,
153
+ ShortcutProvider: () => ShortcutProvider,
154
+ ShortcutScope: () => ShortcutScope,
152
155
  Skeleton: () => Skeleton,
156
+ Slider: () => Slider,
153
157
  Spinner: () => Spinner,
154
158
  Stack: () => Stack,
155
159
  Stat: () => Stat,
@@ -183,16 +187,21 @@ __export(index_exports, {
183
187
  badgeVariants: () => badgeVariants,
184
188
  buttonVariants: () => buttonVariants,
185
189
  cardVariants: () => cardVariants,
190
+ createShortcutManager: () => createShortcutManager,
186
191
  dialogContentVariants: () => dialogContentVariants,
187
192
  inputVariants: () => inputVariants,
193
+ parseKeys: () => parseKeys,
188
194
  progressVariants: () => progressVariants,
189
195
  selectTriggerVariants: () => selectTriggerVariants,
190
196
  sheetVariants: () => sheetVariants,
191
197
  spinnerVariants: () => spinnerVariants,
192
198
  toast: () => import_sonner.toast,
193
- usePortalContainer: () => usePortalContainer
199
+ useActiveShortcuts: () => useActiveShortcuts,
200
+ usePortalContainer: () => usePortalContainer,
201
+ useShortcutManager: () => useShortcutManager,
202
+ useShortcuts: () => useShortcuts
194
203
  });
195
- module.exports = __toCommonJS(index_exports);
204
+ module.exports = __toCommonJS(src_exports);
196
205
 
197
206
  // src/components/button.tsx
198
207
  var import_react_slot = require("@radix-ui/react-slot");
@@ -216,11 +225,15 @@ var buttonVariants = (0, import_class_variance_authority.cva)(
216
225
  variant: {
217
226
  default: "bg-primary text-primary-foreground border border-transparent hover:opacity-90",
218
227
  primary: "bg-primary text-primary-foreground border border-transparent hover:opacity-90",
219
- // Solid fill uses the emphasis token so white on-color text stays AA in
220
- // dark mode (the base token is the readable text color, too light here).
221
- // Hover darkens to a deeper shade instead of opacity-90, which would
222
- // composite the fill toward the page and drop white text under 4.5:1.
223
- destructive: "bg-destructive-solid text-destructive-foreground border border-transparent hover:bg-destructive-700",
228
+ // Solid fill uses the emphasis token so on-color text stays AA in dark
229
+ // mode (the base token is the readable text color, too light here).
230
+ // Hover darkens to a deeper shade rather than opacity-90, which would
231
+ // composite the fill toward the page and drop the label under 4.5:1.
232
+ // One step, not two: the label is white in light mode and black in
233
+ // dark, so mixing the fill toward black moves it away from the label in
234
+ // one mode and into it in the other. `-600` clears both (5.92:1 light,
235
+ // 5.67:1 dark); `-700` reads at 3.70:1 against the dark label.
236
+ destructive: "bg-destructive-solid text-destructive-foreground border border-transparent hover:bg-destructive-600",
224
237
  // border-border is the decorative separator token, and it is the right
225
238
  // one here: a button is identified by its label and fill, so its edge
226
239
  // carries no meaning on its own and is not held to the 3:1 minimum that
@@ -2280,7 +2293,7 @@ var TableSkeleton = ({
2280
2293
  ] })
2281
2294
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(GrayBar, { className: "h-4 w-[60%] max-w-[120px]" }) }, colIdx)) }, rowIdx)) })
2282
2295
  ] }) }),
2283
- !hideFooter && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "table-footer border-t border-border", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-center justify-between px-2 py-4 p-4", children: [
2296
+ !hideFooter && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "table-footer border-t border-border", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-center justify-between px-2 py-4", children: [
2284
2297
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex items-center gap-2 text-sm", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(GrayBar, { className: "h-4 w-[120px]" }) }),
2285
2298
  /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-center gap-6", children: [
2286
2299
  /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-center gap-2", children: [
@@ -2305,11 +2318,353 @@ var TableSkeleton = ({
2305
2318
  };
2306
2319
  TableSkeleton.displayName = "TableSkeleton";
2307
2320
 
2308
- // src/components/context-menu.tsx
2309
- var ContextMenuPrimitive = __toESM(require("@radix-ui/react-context-menu"), 1);
2321
+ // src/components/color-picker.tsx
2310
2322
  var import_lucide_react12 = require("lucide-react");
2311
2323
  var React9 = __toESM(require("react"), 1);
2324
+
2325
+ // src/lib/color/convert.ts
2326
+ var clamp01 = (n) => n < 0 ? 0 : n > 1 ? 1 : n;
2327
+ function normalizeHue(hue) {
2328
+ if (!Number.isFinite(hue)) return 0;
2329
+ const wrapped = hue % 360;
2330
+ return wrapped < 0 ? wrapped + 360 : wrapped;
2331
+ }
2332
+ function hsvToRgb({ h, s, v }) {
2333
+ const hue = normalizeHue(h);
2334
+ const sat = clamp01(s);
2335
+ const val = clamp01(v);
2336
+ const sector = hue / 60;
2337
+ const chroma = val * sat;
2338
+ const x = chroma * (1 - Math.abs(sector % 2 - 1));
2339
+ const base = val - chroma;
2340
+ let rgb;
2341
+ if (sector < 1) rgb = [chroma, x, 0];
2342
+ else if (sector < 2) rgb = [x, chroma, 0];
2343
+ else if (sector < 3) rgb = [0, chroma, x];
2344
+ else if (sector < 4) rgb = [0, x, chroma];
2345
+ else if (sector < 5) rgb = [x, 0, chroma];
2346
+ else rgb = [chroma, 0, x];
2347
+ return { r: rgb[0] + base, g: rgb[1] + base, b: rgb[2] + base };
2348
+ }
2349
+ function rgbToHsv({ r, g, b }) {
2350
+ const red = clamp01(r);
2351
+ const green = clamp01(g);
2352
+ const blue = clamp01(b);
2353
+ const max = Math.max(red, green, blue);
2354
+ const min = Math.min(red, green, blue);
2355
+ const chroma = max - min;
2356
+ let hue = 0;
2357
+ if (chroma !== 0) {
2358
+ if (max === red) hue = (green - blue) / chroma % 6;
2359
+ else if (max === green) hue = (blue - red) / chroma + 2;
2360
+ else hue = (red - green) / chroma + 4;
2361
+ hue *= 60;
2362
+ }
2363
+ return {
2364
+ h: normalizeHue(hue),
2365
+ s: max === 0 ? 0 : chroma / max,
2366
+ v: max
2367
+ };
2368
+ }
2369
+
2370
+ // src/lib/color/hex.ts
2371
+ var HEX = /^#?(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
2372
+ var clamp012 = (n) => n < 0 ? 0 : n > 1 ? 1 : n;
2373
+ function pair(channel) {
2374
+ const value = Number.isFinite(channel) ? clamp012(channel) : 0;
2375
+ return Math.round(value * 255).toString(16).padStart(2, "0");
2376
+ }
2377
+ function parseHex(input) {
2378
+ const text = input.trim();
2379
+ if (!HEX.test(text)) return null;
2380
+ const digits = text.replace("#", "");
2381
+ const short = digits.length < 6;
2382
+ const size = short ? 1 : 2;
2383
+ const channel = (index) => {
2384
+ const slice = digits.slice(index * size, index * size + size);
2385
+ return parseInt(short ? slice + slice : slice, 16) / 255;
2386
+ };
2387
+ const hasAlpha = digits.length === 4 || digits.length === 8;
2388
+ return {
2389
+ r: channel(0),
2390
+ g: channel(1),
2391
+ b: channel(2),
2392
+ alpha: hasAlpha ? channel(3) : 1
2393
+ };
2394
+ }
2395
+ function toHex(color, alpha = 1) {
2396
+ const opacity = Number.isFinite(alpha) ? clamp012(alpha) : 1;
2397
+ const opaque = `#${pair(color.r)}${pair(color.g)}${pair(color.b)}`;
2398
+ return opacity === 1 ? opaque : `${opaque}${pair(opacity)}`;
2399
+ }
2400
+
2401
+ // src/lib/color/picker-geometry.ts
2402
+ var clamp013 = (n) => n < 0 ? 0 : n > 1 ? 1 : n;
2403
+ function pointOnSurface(clientX, clientY, rect) {
2404
+ return {
2405
+ x: rect.width === 0 ? 0 : clamp013((clientX - rect.left) / rect.width),
2406
+ y: rect.height === 0 ? 0 : clamp013((clientY - rect.top) / rect.height)
2407
+ };
2408
+ }
2409
+ function saturationValueAt(point) {
2410
+ return { s: clamp013(point.x), v: 1 - clamp013(point.y) };
2411
+ }
2412
+ function surfacePointFor(s, v) {
2413
+ return { x: clamp013(s), y: 1 - clamp013(v) };
2414
+ }
2415
+ function hueAt(fraction) {
2416
+ const hue = clamp013(fraction) * 360;
2417
+ return hue >= 360 ? 0 : hue;
2418
+ }
2419
+ function huePosition(hue) {
2420
+ const wrapped = (hue % 360 + 360) % 360;
2421
+ return wrapped / 360;
2422
+ }
2423
+ function hueSliderValue(hue, max) {
2424
+ const step = Math.round(huePosition(hue) * (max + 1));
2425
+ return step > max ? max : step;
2426
+ }
2427
+
2428
+ // src/components/color-picker.tsx
2312
2429
  var import_jsx_runtime34 = require("react/jsx-runtime");
2430
+ function toHsva(hex) {
2431
+ const parsed = parseHex(hex);
2432
+ if (!parsed) return { h: 0, s: 0, v: 0, a: 1 };
2433
+ const { r, g, b, alpha } = parsed;
2434
+ return { ...rgbToHsv({ r, g, b }), a: alpha };
2435
+ }
2436
+ function toHexString(hsva, withAlpha) {
2437
+ return toHex(hsvToRgb(hsva), withAlpha ? hsva.a : 1);
2438
+ }
2439
+ var HUE_MAX = 359;
2440
+ function hsvaFrom(color, alpha, currentHue) {
2441
+ const hsv = rgbToHsv(color);
2442
+ return { ...hsv, h: hsv.s === 0 ? currentHue : hsv.h, a: alpha };
2443
+ }
2444
+ var SLIDER = "h-3 w-full cursor-pointer appearance-none rounded-full";
2445
+ var CHECKERBOARD = "repeating-conic-gradient(#c8c8c8 0% 25%, #ffffff 0% 50%)";
2446
+ function eyeDropperSupported() {
2447
+ return typeof window !== "undefined" && "EyeDropper" in window;
2448
+ }
2449
+ function ColorPicker({
2450
+ color,
2451
+ onColorChange,
2452
+ swatches = [],
2453
+ onSwatchSelect,
2454
+ recentColors = [],
2455
+ showAlpha = false,
2456
+ className
2457
+ }) {
2458
+ const fieldId = React9.useId();
2459
+ const surfaceRef = React9.useRef(null);
2460
+ const [hsva, setHsva] = React9.useState(() => toHsva(color));
2461
+ const [draftHex, setDraftHex] = React9.useState(null);
2462
+ const rendered = toHexString(hsva, showAlpha);
2463
+ React9.useEffect(() => {
2464
+ const incoming = parseHex(color);
2465
+ if (incoming && toHex(incoming, showAlpha ? incoming.alpha : 1) !== rendered) {
2466
+ setHsva((prev) => hsvaFrom(incoming, incoming.alpha, prev.h));
2467
+ }
2468
+ }, [color, rendered, showAlpha]);
2469
+ const commit = (next) => {
2470
+ setHsva(next);
2471
+ setDraftHex(null);
2472
+ onColorChange(toHexString(next, showAlpha));
2473
+ };
2474
+ const trackPointer = (event) => {
2475
+ const rect = surfaceRef.current?.getBoundingClientRect();
2476
+ if (!rect) return;
2477
+ const { s, v } = saturationValueAt(
2478
+ pointOnSurface(event.clientX, event.clientY, rect)
2479
+ );
2480
+ commit({ ...hsva, s, v });
2481
+ };
2482
+ const nudge = (ds, dv) => {
2483
+ const { s, v } = saturationValueAt(
2484
+ surfacePointFor(hsva.s + ds, hsva.v + dv)
2485
+ );
2486
+ commit({ ...hsva, s, v });
2487
+ };
2488
+ const handleSurfaceKey = (event) => {
2489
+ const step = event.shiftKey ? 0.1 : 0.01;
2490
+ const moves = {
2491
+ ArrowLeft: [-step, 0],
2492
+ ArrowRight: [step, 0],
2493
+ ArrowUp: [0, step],
2494
+ ArrowDown: [0, -step]
2495
+ };
2496
+ const move = moves[event.key];
2497
+ if (!move) return;
2498
+ event.preventDefault();
2499
+ nudge(move[0], move[1]);
2500
+ };
2501
+ const [canPickFromScreen, setCanPickFromScreen] = React9.useState(false);
2502
+ React9.useEffect(() => {
2503
+ setCanPickFromScreen(eyeDropperSupported());
2504
+ }, []);
2505
+ const handle = surfacePointFor(hsva.s, hsva.v);
2506
+ const hueOnly = toHex(hsvToRgb({ h: hsva.h, s: 1, v: 1 }));
2507
+ const pickFromScreen = async () => {
2508
+ const ctor = window.EyeDropper;
2509
+ if (!ctor) return;
2510
+ let sampled;
2511
+ try {
2512
+ sampled = (await new ctor().open()).sRGBHex;
2513
+ } catch {
2514
+ return;
2515
+ }
2516
+ const parsed = parseHex(sampled);
2517
+ if (parsed) commit(hsvaFrom(parsed, hsva.a, hsva.h));
2518
+ };
2519
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: cn("w-64 space-y-3", className), children: [
2520
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2521
+ "div",
2522
+ {
2523
+ ref: surfaceRef,
2524
+ role: "application",
2525
+ tabIndex: 0,
2526
+ "aria-label": `Saturation and brightness: ${Math.round(hsva.s * 100)}% saturation, ${Math.round(hsva.v * 100)}% brightness. Arrow keys adjust.`,
2527
+ className: "ring-offset-background focus-visible:ring-ring relative h-40 w-full cursor-crosshair touch-none rounded-md focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none",
2528
+ style: {
2529
+ backgroundColor: hueOnly,
2530
+ // Value on TOP of saturation. CSS paints the first layer nearest the
2531
+ // viewer, so the reverse order lets the opaque white end of the
2532
+ // saturation ramp cover the black end of the value ramp: the
2533
+ // bottom-left corner displays white while selecting black.
2534
+ backgroundImage: "linear-gradient(to top, #000, transparent), linear-gradient(to right, #fff, transparent)"
2535
+ },
2536
+ onKeyDown: handleSurfaceKey,
2537
+ onPointerDown: (event) => {
2538
+ event.currentTarget.setPointerCapture(event.pointerId);
2539
+ trackPointer(event);
2540
+ },
2541
+ onPointerMove: (event) => {
2542
+ if (event.buttons === 1) trackPointer(event);
2543
+ },
2544
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2545
+ "span",
2546
+ {
2547
+ "aria-hidden": "true",
2548
+ className: "pointer-events-none absolute size-3 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-sm ring-1 ring-black/60",
2549
+ style: { left: `${handle.x * 100}%`, top: `${handle.y * 100}%` }
2550
+ }
2551
+ )
2552
+ }
2553
+ ),
2554
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("label", { className: "sr-only", htmlFor: `${fieldId}-hue`, children: "Hue" }),
2555
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2556
+ "input",
2557
+ {
2558
+ id: `${fieldId}-hue`,
2559
+ type: "range",
2560
+ min: 0,
2561
+ max: HUE_MAX,
2562
+ step: 1,
2563
+ value: hueSliderValue(hsva.h, HUE_MAX),
2564
+ onChange: (event) => commit({ ...hsva, h: hueAt(+event.target.value / (HUE_MAX + 1)) }),
2565
+ className: SLIDER,
2566
+ style: {
2567
+ backgroundImage: "linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00)"
2568
+ }
2569
+ }
2570
+ ),
2571
+ showAlpha && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
2572
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("label", { className: "sr-only", htmlFor: `${fieldId}-alpha`, children: "Opacity" }),
2573
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2574
+ "input",
2575
+ {
2576
+ id: `${fieldId}-alpha`,
2577
+ type: "range",
2578
+ min: 0,
2579
+ max: 100,
2580
+ step: 1,
2581
+ value: Math.round(hsva.a * 100),
2582
+ onChange: (event) => commit({ ...hsva, a: +event.target.value / 100 }),
2583
+ className: SLIDER,
2584
+ style: {
2585
+ // The ramp runs to the colour being edited, over a chequerboard,
2586
+ // so the track shows what the slider actually controls. Without
2587
+ // any background this rendered as a blank 12px strip whose only
2588
+ // label was screen-reader-only.
2589
+ backgroundImage: `linear-gradient(to right, transparent, ${toHexString({ ...hsva, a: 1 }, false)}), ${CHECKERBOARD}`,
2590
+ backgroundSize: "100% 100%, 8px 8px"
2591
+ }
2592
+ }
2593
+ )
2594
+ ] }),
2595
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex items-center gap-2", children: [
2596
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("label", { className: "sr-only", htmlFor: `${fieldId}-hex`, children: "Hex colour" }),
2597
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2598
+ Input,
2599
+ {
2600
+ id: `${fieldId}-hex`,
2601
+ className: "font-mono",
2602
+ value: draftHex ?? rendered,
2603
+ onChange: (event) => {
2604
+ const text = event.target.value;
2605
+ setDraftHex(text);
2606
+ const parsed = parseHex(text);
2607
+ if (parsed) {
2608
+ setHsva(hsvaFrom(parsed, parsed.alpha, hsva.h));
2609
+ onColorChange(toHex(parsed, showAlpha ? parsed.alpha : 1));
2610
+ }
2611
+ },
2612
+ onBlur: () => setDraftHex(null)
2613
+ }
2614
+ ),
2615
+ canPickFromScreen && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2616
+ Button,
2617
+ {
2618
+ type: "button",
2619
+ variant: "outline",
2620
+ size: "icon",
2621
+ "aria-label": "Pick a colour from the screen",
2622
+ onClick: () => void pickFromScreen(),
2623
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_lucide_react12.Pipette, { className: "size-4" })
2624
+ }
2625
+ )
2626
+ ] }),
2627
+ swatches.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { children: [
2628
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { className: "text-muted-foreground mb-1 text-xs", children: "Presets" }),
2629
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "flex flex-wrap gap-1", children: swatches.map((swatch) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2630
+ "button",
2631
+ {
2632
+ type: "button",
2633
+ title: swatch.label,
2634
+ "aria-label": swatch.label,
2635
+ className: "size-6 rounded border shadow-sm",
2636
+ style: { backgroundColor: swatch.color },
2637
+ onClick: () => onSwatchSelect?.(swatch)
2638
+ },
2639
+ swatch.id
2640
+ )) })
2641
+ ] }),
2642
+ recentColors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { children: [
2643
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { className: "text-muted-foreground mb-1 text-xs", children: "Recent" }),
2644
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "flex flex-wrap gap-1", children: recentColors.map((recent) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2645
+ "button",
2646
+ {
2647
+ type: "button",
2648
+ title: recent,
2649
+ "aria-label": recent,
2650
+ className: "size-6 rounded border shadow-sm",
2651
+ style: { backgroundColor: recent },
2652
+ onClick: () => {
2653
+ const parsed = parseHex(recent);
2654
+ if (parsed) commit(hsvaFrom(parsed, parsed.alpha, hsva.h));
2655
+ }
2656
+ },
2657
+ recent
2658
+ )) })
2659
+ ] })
2660
+ ] });
2661
+ }
2662
+
2663
+ // src/components/context-menu.tsx
2664
+ var ContextMenuPrimitive = __toESM(require("@radix-ui/react-context-menu"), 1);
2665
+ var import_lucide_react13 = require("lucide-react");
2666
+ var React10 = __toESM(require("react"), 1);
2667
+ var import_jsx_runtime35 = require("react/jsx-runtime");
2313
2668
  var menuItemBase2 = "cursor-pointer transition-colors data-[highlighted]:bg-muted data-[highlighted]:text-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
2314
2669
  var menuSurface = "z-50 max-h-[var(--radix-context-menu-content-available-height)] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-lg border border-border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 origin-[--radix-context-menu-content-transform-origin]";
2315
2670
  var ContextMenu = ContextMenuPrimitive.Root;
@@ -2317,7 +2672,7 @@ var ContextMenuTrigger = ContextMenuPrimitive.Trigger;
2317
2672
  var ContextMenuGroup = ContextMenuPrimitive.Group;
2318
2673
  var ContextMenuSub = ContextMenuPrimitive.Sub;
2319
2674
  var ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
2320
- var ContextMenuSubTrigger = React9.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
2675
+ var ContextMenuSubTrigger = React10.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
2321
2676
  ContextMenuPrimitive.SubTrigger,
2322
2677
  {
2323
2678
  ref,
@@ -2330,14 +2685,14 @@ var ContextMenuSubTrigger = React9.forwardRef(({ className, inset, children, ...
2330
2685
  ...props,
2331
2686
  children: [
2332
2687
  children,
2333
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_lucide_react12.ChevronRight, { className: "ml-auto" })
2688
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_lucide_react13.ChevronRight, { className: "ml-auto" })
2334
2689
  ]
2335
2690
  }
2336
2691
  ));
2337
2692
  ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName;
2338
- var ContextMenuSubContent = React9.forwardRef(({ className, ...props }, ref) => {
2693
+ var ContextMenuSubContent = React10.forwardRef(({ className, ...props }, ref) => {
2339
2694
  const portalContainer = usePortalContainer();
2340
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ContextMenuPrimitive.Portal, { container: portalContainer, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2695
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ContextMenuPrimitive.Portal, { container: portalContainer, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2341
2696
  ContextMenuPrimitive.SubContent,
2342
2697
  {
2343
2698
  ref,
@@ -2347,9 +2702,9 @@ var ContextMenuSubContent = React9.forwardRef(({ className, ...props }, ref) =>
2347
2702
  ) });
2348
2703
  });
2349
2704
  ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName;
2350
- var ContextMenuContent = React9.forwardRef(({ className, ...props }, ref) => {
2705
+ var ContextMenuContent = React10.forwardRef(({ className, ...props }, ref) => {
2351
2706
  const portalContainer = usePortalContainer();
2352
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ContextMenuPrimitive.Portal, { container: portalContainer, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2707
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ContextMenuPrimitive.Portal, { container: portalContainer, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2353
2708
  ContextMenuPrimitive.Content,
2354
2709
  {
2355
2710
  ref,
@@ -2359,7 +2714,7 @@ var ContextMenuContent = React9.forwardRef(({ className, ...props }, ref) => {
2359
2714
  ) });
2360
2715
  });
2361
2716
  ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName;
2362
- var ContextMenuItem = React9.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2717
+ var ContextMenuItem = React10.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2363
2718
  ContextMenuPrimitive.Item,
2364
2719
  {
2365
2720
  ref,
@@ -2373,7 +2728,7 @@ var ContextMenuItem = React9.forwardRef(({ className, inset, ...props }, ref) =>
2373
2728
  }
2374
2729
  ));
2375
2730
  ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName;
2376
- var ContextMenuCheckboxItem = React9.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
2731
+ var ContextMenuCheckboxItem = React10.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
2377
2732
  ContextMenuPrimitive.CheckboxItem,
2378
2733
  {
2379
2734
  ref,
@@ -2385,13 +2740,13 @@ var ContextMenuCheckboxItem = React9.forwardRef(({ className, children, checked,
2385
2740
  checked,
2386
2741
  ...props,
2387
2742
  children: [
2388
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ContextMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_lucide_react12.Check, { className: "h-4 w-4" }) }) }),
2743
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ContextMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_lucide_react13.Check, { className: "h-4 w-4" }) }) }),
2389
2744
  children
2390
2745
  ]
2391
2746
  }
2392
2747
  ));
2393
2748
  ContextMenuCheckboxItem.displayName = ContextMenuPrimitive.CheckboxItem.displayName;
2394
- var ContextMenuRadioItem = React9.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
2749
+ var ContextMenuRadioItem = React10.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
2395
2750
  ContextMenuPrimitive.RadioItem,
2396
2751
  {
2397
2752
  ref,
@@ -2402,13 +2757,13 @@ var ContextMenuRadioItem = React9.forwardRef(({ className, children, ...props },
2402
2757
  ),
2403
2758
  ...props,
2404
2759
  children: [
2405
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ContextMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_lucide_react12.Circle, { className: "h-2 w-2 fill-current" }) }) }),
2760
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ContextMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_lucide_react13.Circle, { className: "h-2 w-2 fill-current" }) }) }),
2406
2761
  children
2407
2762
  ]
2408
2763
  }
2409
2764
  ));
2410
2765
  ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName;
2411
- var ContextMenuLabel = React9.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2766
+ var ContextMenuLabel = React10.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2412
2767
  ContextMenuPrimitive.Label,
2413
2768
  {
2414
2769
  ref,
@@ -2421,7 +2776,7 @@ var ContextMenuLabel = React9.forwardRef(({ className, inset, ...props }, ref) =
2421
2776
  }
2422
2777
  ));
2423
2778
  ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName;
2424
- var ContextMenuSeparator = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2779
+ var ContextMenuSeparator = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2425
2780
  ContextMenuPrimitive.Separator,
2426
2781
  {
2427
2782
  ref,
@@ -2433,7 +2788,7 @@ ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName;
2433
2788
  var ContextMenuShortcut = ({
2434
2789
  className,
2435
2790
  ...props
2436
- }) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2791
+ }) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2437
2792
  "span",
2438
2793
  {
2439
2794
  className: cn(
@@ -2446,13 +2801,13 @@ var ContextMenuShortcut = ({
2446
2801
  ContextMenuShortcut.displayName = "ContextMenuShortcut";
2447
2802
 
2448
2803
  // src/components/resizable.tsx
2449
- var import_lucide_react13 = require("lucide-react");
2804
+ var import_lucide_react14 = require("lucide-react");
2450
2805
  var ResizablePrimitive = __toESM(require("react-resizable-panels"), 1);
2451
- var import_jsx_runtime35 = require("react/jsx-runtime");
2806
+ var import_jsx_runtime36 = require("react/jsx-runtime");
2452
2807
  var ResizablePanelGroup = ({
2453
2808
  className,
2454
2809
  ...props
2455
- }) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2810
+ }) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2456
2811
  ResizablePrimitive.Group,
2457
2812
  {
2458
2813
  className: cn("h-full w-full", className),
@@ -2465,7 +2820,7 @@ var ResizableHandle = ({
2465
2820
  className,
2466
2821
  children,
2467
2822
  ...props
2468
- }) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
2823
+ }) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
2469
2824
  ResizablePrimitive.Separator,
2470
2825
  {
2471
2826
  className: cn(
@@ -2485,7 +2840,7 @@ var ResizableHandle = ({
2485
2840
  ),
2486
2841
  ...props,
2487
2842
  children: [
2488
- withGrip ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "z-10 flex h-4 w-3 items-center justify-center rounded-sm border border-border bg-border", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_lucide_react13.GripVertical, { className: "h-2.5 w-2.5 text-muted-foreground" }) }) : null,
2843
+ withGrip ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "z-10 flex h-4 w-3 items-center justify-center rounded-sm border border-border bg-border", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_lucide_react14.GripVertical, { className: "h-2.5 w-2.5 text-muted-foreground" }) }) : null,
2489
2844
  children
2490
2845
  ]
2491
2846
  }
@@ -2493,9 +2848,9 @@ var ResizableHandle = ({
2493
2848
 
2494
2849
  // src/components/tree-view.tsx
2495
2850
  var import_react_virtual = require("@tanstack/react-virtual");
2496
- var import_lucide_react14 = require("lucide-react");
2497
- var React10 = __toESM(require("react"), 1);
2498
- var import_jsx_runtime36 = require("react/jsx-runtime");
2851
+ var import_lucide_react15 = require("lucide-react");
2852
+ var React11 = __toESM(require("react"), 1);
2853
+ var import_jsx_runtime37 = require("react/jsx-runtime");
2499
2854
  var ROW_HEIGHT = 28;
2500
2855
  var INDENT_PER_LEVEL = 12;
2501
2856
  function textOf(node) {
@@ -2536,13 +2891,13 @@ function flatten(nodes, expanded) {
2536
2891
  return rows;
2537
2892
  }
2538
2893
  function useControllable(controlled, fallback) {
2539
- const [uncontrolled, setUncontrolled] = React10.useState(fallback);
2894
+ const [uncontrolled, setUncontrolled] = React11.useState(fallback);
2540
2895
  return [
2541
2896
  controlled === void 0 ? uncontrolled : controlled,
2542
2897
  setUncontrolled
2543
2898
  ];
2544
2899
  }
2545
- var TreeView = React10.forwardRef(
2900
+ var TreeView = React11.forwardRef(
2546
2901
  ({
2547
2902
  nodes,
2548
2903
  expandedIds,
@@ -2557,8 +2912,8 @@ var TreeView = React10.forwardRef(
2557
2912
  "aria-describedby": ariaDescribedBy,
2558
2913
  ...props
2559
2914
  }, forwardedRef) => {
2560
- const scrollRef = React10.useRef(null);
2561
- const attachScroll = React10.useCallback(
2915
+ const scrollRef = React11.useRef(null);
2916
+ const attachScroll = React11.useCallback(
2562
2917
  (node) => {
2563
2918
  scrollRef.current = node;
2564
2919
  if (typeof forwardedRef === "function") forwardedRef(node);
@@ -2572,7 +2927,7 @@ var TreeView = React10.forwardRef(
2572
2927
  expandedIds === void 0 ? void 0 : [...expandedIds],
2573
2928
  [...defaultExpandedIds ?? []]
2574
2929
  );
2575
- const expanded = React10.useMemo(
2930
+ const expanded = React11.useMemo(
2576
2931
  () => new Set(expandedIds ?? expandedState),
2577
2932
  [expandedIds, expandedState]
2578
2933
  );
@@ -2580,11 +2935,11 @@ var TreeView = React10.forwardRef(
2580
2935
  selectedId === void 0 ? void 0 : selectedId,
2581
2936
  defaultSelectedId ?? null
2582
2937
  );
2583
- const rows = React10.useMemo(
2938
+ const rows = React11.useMemo(
2584
2939
  () => flatten(nodes, expanded),
2585
2940
  [nodes, expanded]
2586
2941
  );
2587
- const [activeId, setActiveId] = React10.useState(null);
2942
+ const [activeId, setActiveId] = React11.useState(null);
2588
2943
  const activeIndex = Math.max(
2589
2944
  0,
2590
2945
  rows.findIndex((row) => row.node.id === (activeId ?? selected))
@@ -2628,7 +2983,7 @@ var TreeView = React10.forwardRef(
2628
2983
  }
2629
2984
  return from;
2630
2985
  };
2631
- const typeahead = React10.useRef({ query: "", at: 0 });
2986
+ const typeahead = React11.useRef({ query: "", at: 0 });
2632
2987
  const onKeyDown = (event) => {
2633
2988
  const index = activeIndex;
2634
2989
  const row = rows[index];
@@ -2725,13 +3080,13 @@ var TreeView = React10.forwardRef(
2725
3080
  const virtualItems = virtualizer.getVirtualItems();
2726
3081
  const usable = (index) => rows[index]?.node.disabled !== true;
2727
3082
  const tabStopIndex = virtualItems.some((item) => item.index === activeIndex) && usable(activeIndex) ? activeIndex : virtualItems.find((item) => usable(item.index))?.index ?? -1;
2728
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
3083
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2729
3084
  "div",
2730
3085
  {
2731
3086
  ref: attachScroll,
2732
3087
  className: cn("overflow-auto", className),
2733
3088
  ...props,
2734
- children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
3089
+ children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2735
3090
  "div",
2736
3091
  {
2737
3092
  role: "tree",
@@ -2744,7 +3099,7 @@ var TreeView = React10.forwardRef(
2744
3099
  const row = rows[item.index];
2745
3100
  if (row === void 0) return null;
2746
3101
  const isSelected = selected === row.node.id;
2747
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
3102
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
2748
3103
  "div",
2749
3104
  {
2750
3105
  "data-tree-index": item.index,
@@ -2774,7 +3129,7 @@ var TreeView = React10.forwardRef(
2774
3129
  paddingLeft: 4 + row.level * INDENT_PER_LEVEL
2775
3130
  },
2776
3131
  children: [
2777
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
3132
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2778
3133
  "span",
2779
3134
  {
2780
3135
  "aria-hidden": "true",
@@ -2784,8 +3139,8 @@ var TreeView = React10.forwardRef(
2784
3139
  event.stopPropagation();
2785
3140
  setExpansion(row.node.id, !expanded.has(row.node.id));
2786
3141
  },
2787
- children: row.hasChildren ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2788
- import_lucide_react14.ChevronRight,
3142
+ children: row.hasChildren ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
3143
+ import_lucide_react15.ChevronRight,
2789
3144
  {
2790
3145
  className: cn(
2791
3146
  "size-3.5 text-muted-foreground transition-transform",
@@ -2795,8 +3150,8 @@ var TreeView = React10.forwardRef(
2795
3150
  ) : null
2796
3151
  }
2797
3152
  ),
2798
- row.node.icon !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "flex size-4 shrink-0 items-center justify-center text-muted-foreground", children: row.node.icon }) : null,
2799
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "truncate", children: row.node.label })
3153
+ row.node.icon !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "flex size-4 shrink-0 items-center justify-center text-muted-foreground", children: row.node.icon }) : null,
3154
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "truncate", children: row.node.label })
2800
3155
  ]
2801
3156
  },
2802
3157
  row.node.id
@@ -2809,6 +3164,837 @@ var TreeView = React10.forwardRef(
2809
3164
  }
2810
3165
  );
2811
3166
  TreeView.displayName = "TreeView";
3167
+
3168
+ // src/components/slider.tsx
3169
+ var SliderPrimitive = __toESM(require("@radix-ui/react-slider"), 1);
3170
+ var React12 = __toESM(require("react"), 1);
3171
+
3172
+ // src/lib/dev-warn.ts
3173
+ var emitted = /* @__PURE__ */ new Set();
3174
+ var SPEAKING_ENVIRONMENTS = /* @__PURE__ */ new Set(["development", "test"]);
3175
+ function isDevelopmentRuntime() {
3176
+ if (typeof process === "undefined") return false;
3177
+ const env = process?.env?.NODE_ENV;
3178
+ return env !== void 0 && SPEAKING_ENVIRONMENTS.has(env);
3179
+ }
3180
+ function devWarnOnce(condition, message) {
3181
+ if (condition) return;
3182
+ if (!isDevelopmentRuntime()) return;
3183
+ if (emitted.has(message)) return;
3184
+ emitted.add(message);
3185
+ console.warn(`[@nextlyhq/ui] ${message}`);
3186
+ }
3187
+
3188
+ // src/components/slider.tsx
3189
+ var import_jsx_runtime38 = (
3190
+ // `aria-label`/`aria-labelledby` are destructured out above rather than
3191
+ // spread here: left on the root they would be a second, roleless copy
3192
+ // of a name only the thumb is read for.
3193
+ require("react/jsx-runtime")
3194
+ );
3195
+ function thumbCount(value, defaultValue) {
3196
+ return Math.max(1, value?.length ?? defaultValue?.length ?? 1);
3197
+ }
3198
+ function hasAccessibleName(value) {
3199
+ return value !== void 0 && value.trim() !== "";
3200
+ }
3201
+ var Slider = React12.forwardRef(
3202
+ ({
3203
+ className,
3204
+ value,
3205
+ defaultValue,
3206
+ thumbs,
3207
+ orientation = "horizontal",
3208
+ "aria-label": ariaLabel,
3209
+ "aria-labelledby": ariaLabelledBy,
3210
+ ...props
3211
+ }, ref) => {
3212
+ const initialUncontrolledCount = React12.useRef(
3213
+ thumbCount(void 0, defaultValue)
3214
+ ).current;
3215
+ const count = value?.length ?? initialUncontrolledCount;
3216
+ const isEmptyDefault = defaultValue !== void 0 && defaultValue.length === 0;
3217
+ const isEmptyControlled = value !== void 0 && value.length === 0;
3218
+ devWarnOnce(
3219
+ !isEmptyDefault && !isEmptyControlled,
3220
+ "Slider: `value`/`defaultValue` must hold one number per thumb, and an empty array holds none \u2014 the control has nothing to slide. An empty `defaultValue` falls back to `min`; an empty `value` renders nothing at all, because a controlled slider cannot be given a value without taking state the caller owns. Render nothing until the value is loaded rather than passing `[]`."
3221
+ );
3222
+ if (isEmptyControlled) return null;
3223
+ const isNamed = (index) => {
3224
+ const own = thumbs?.[index];
3225
+ if (hasAccessibleName(own?.["aria-label"])) return true;
3226
+ if (hasAccessibleName(own?.["aria-labelledby"])) return true;
3227
+ return count === 1 && (hasAccessibleName(ariaLabel) || hasAccessibleName(ariaLabelledBy));
3228
+ };
3229
+ devWarnOnce(
3230
+ Array.from({ length: count }).every((_, i) => isNamed(i)),
3231
+ "Slider: every thumb needs an accessible name. A single thumb may take it from the root's `aria-label`/`aria-labelledby`; a range needs one `thumbs` entry per thumb, because the root's name is not inherited and two thumbs sharing one name are announced identically."
3232
+ );
3233
+ const ariaFor = (index) => {
3234
+ const supplied = thumbs?.[index] ?? {};
3235
+ const ownLabel = hasAccessibleName(supplied["aria-label"]) ? supplied["aria-label"] : void 0;
3236
+ const ownLabelledBy = hasAccessibleName(supplied["aria-labelledby"]) ? supplied["aria-labelledby"] : void 0;
3237
+ if (count !== 1) {
3238
+ return {
3239
+ ...supplied,
3240
+ "aria-label": ownLabel,
3241
+ "aria-labelledby": ownLabelledBy
3242
+ };
3243
+ }
3244
+ const namesItself = ownLabel !== void 0 || ownLabelledBy !== void 0;
3245
+ return {
3246
+ "aria-label": namesItself ? ownLabel : ariaLabel,
3247
+ "aria-labelledby": namesItself ? ownLabelledBy : ariaLabelledBy,
3248
+ "aria-valuetext": supplied["aria-valuetext"],
3249
+ "aria-describedby": supplied["aria-describedby"]
3250
+ };
3251
+ };
3252
+ const isVertical = orientation === "vertical";
3253
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
3254
+ SliderPrimitive.Root,
3255
+ {
3256
+ ref,
3257
+ className: cn(
3258
+ "relative flex touch-none select-none items-center",
3259
+ // WCAG 2.5.8 wants a 24px target. Padding alone does not reach it: the
3260
+ // thumb is absolutely positioned, so the cross-axis size is the 6px
3261
+ // track plus the padding — 22px with `py-2`. An explicit minimum
3262
+ // states the target rather than leaving it to arithmetic that moves
3263
+ // whenever the track thickness does.
3264
+ isVertical ? (
3265
+ // A vertical slider needs a LENGTH, and it cannot inherit one:
3266
+ // `h-full` inside an auto-height parent resolves to zero, leaving
3267
+ // a control with no track to drag along. A concrete default is
3268
+ // usable everywhere and, being a plain utility, is replaced by a
3269
+ // caller's own `h-*` — including `h-full`, for the fill-the-parent
3270
+ // case this default gives up.
3271
+ "h-44 min-w-6 flex-col px-2"
3272
+ ) : "min-h-6 w-full py-2",
3273
+ "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
3274
+ className
3275
+ ),
3276
+ orientation,
3277
+ value,
3278
+ defaultValue: isEmptyDefault ? void 0 : defaultValue,
3279
+ ...props,
3280
+ children: [
3281
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
3282
+ SliderPrimitive.Track,
3283
+ {
3284
+ className: cn(
3285
+ "bg-secondary relative grow overflow-hidden rounded-full",
3286
+ isVertical ? "h-full w-1.5" : "h-1.5 w-full"
3287
+ ),
3288
+ children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
3289
+ SliderPrimitive.Range,
3290
+ {
3291
+ className: cn(
3292
+ "bg-primary absolute",
3293
+ isVertical ? "w-full" : "h-full"
3294
+ )
3295
+ }
3296
+ )
3297
+ }
3298
+ ),
3299
+ Array.from({ length: count }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
3300
+ SliderPrimitive.Thumb,
3301
+ {
3302
+ ...ariaFor(i),
3303
+ className: cn(
3304
+ "border-primary bg-background block h-4 w-4 rounded-full border-2",
3305
+ "ring-offset-background transition-colors",
3306
+ "focus-visible:ring-ring focus-visible:outline-none focus-visible:ring-2",
3307
+ "focus-visible:ring-offset-2",
3308
+ "disabled:pointer-events-none disabled:opacity-50"
3309
+ )
3310
+ },
3311
+ i
3312
+ ))
3313
+ ]
3314
+ }
3315
+ );
3316
+ }
3317
+ );
3318
+ Slider.displayName = SliderPrimitive.Root.displayName;
3319
+
3320
+ // src/lib/shortcuts/react.tsx
3321
+ var React13 = __toESM(require("react"), 1);
3322
+
3323
+ // src/lib/shortcuts/key-spec.ts
3324
+ function normalizeKey(key) {
3325
+ return [...key].length === 1 ? key.toLowerCase() : key;
3326
+ }
3327
+ function shiftIsMeaningful(key) {
3328
+ if (key.length > 1) return true;
3329
+ if (key === " ") return true;
3330
+ return /[\p{L}\p{N}]/u.test(key);
3331
+ }
3332
+ function parseKeys(spec) {
3333
+ const steps = spec.trim().split(/\s+/).filter(Boolean);
3334
+ if (steps.length === 0) {
3335
+ throw new Error(`Shortcut spec is empty: ${JSON.stringify(spec)}`);
3336
+ }
3337
+ return steps.map((step) => parseChord(step, spec));
3338
+ }
3339
+ function parseChord(step, spec) {
3340
+ const trailingPlusIsKey = step.length > 2 && step.endsWith("++");
3341
+ const body = trailingPlusIsKey ? step.slice(0, -1) : step;
3342
+ const parts = step === "+" ? ["+"] : body.split("+").filter(Boolean);
3343
+ let mod = false;
3344
+ let ctrl = false;
3345
+ let meta = false;
3346
+ let alt = false;
3347
+ let shift = false;
3348
+ let key;
3349
+ for (const raw of parts) {
3350
+ switch (raw.toLowerCase()) {
3351
+ case "mod":
3352
+ mod = true;
3353
+ break;
3354
+ case "ctrl":
3355
+ case "control":
3356
+ ctrl = true;
3357
+ break;
3358
+ case "meta":
3359
+ case "cmd":
3360
+ case "command":
3361
+ meta = true;
3362
+ break;
3363
+ case "alt":
3364
+ case "option":
3365
+ alt = true;
3366
+ break;
3367
+ case "shift":
3368
+ shift = true;
3369
+ break;
3370
+ case "space":
3371
+ key = " ";
3372
+ break;
3373
+ default:
3374
+ if (key !== void 0) {
3375
+ throw new Error(
3376
+ `Shortcut step "${step}" names two keys ("${key}" and "${raw}") in ${JSON.stringify(spec)}`
3377
+ );
3378
+ }
3379
+ key = raw;
3380
+ }
3381
+ }
3382
+ if (trailingPlusIsKey) {
3383
+ if (key !== void 0) {
3384
+ throw new Error(
3385
+ `Shortcut step has more than one key: ${JSON.stringify(step)} in ${JSON.stringify(spec)}`
3386
+ );
3387
+ }
3388
+ key = "+";
3389
+ }
3390
+ if (key === void 0) {
3391
+ throw new Error(
3392
+ `Shortcut step "${step}" names modifiers but no key, in ${JSON.stringify(spec)}`
3393
+ );
3394
+ }
3395
+ return { key: normalizeKey(key), mod, ctrl, meta, alt, shift };
3396
+ }
3397
+ function chordMatches(chord, key, state, isApple) {
3398
+ if (normalizeKey(key) !== chord.key) return false;
3399
+ const wantsCtrl = chord.ctrl || chord.mod && !isApple;
3400
+ const wantsMeta = chord.meta || chord.mod && isApple;
3401
+ if (state.metaKey !== wantsMeta) return false;
3402
+ const altGraph = state.getModifierState?.("AltGraph") ?? false;
3403
+ const synthetic = altGraph && [...chord.key].length === 1 && !wantsCtrl && !chord.alt;
3404
+ if (!synthetic) {
3405
+ if (state.ctrlKey !== wantsCtrl) return false;
3406
+ if (state.altKey !== chord.alt) return false;
3407
+ }
3408
+ if (shiftIsMeaningful(chord.key) && state.shiftKey !== chord.shift)
3409
+ return false;
3410
+ return true;
3411
+ }
3412
+ function detectApplePlatform() {
3413
+ if (typeof navigator === "undefined") return false;
3414
+ const candidate = navigator;
3415
+ const platform = candidate.userAgentData?.platform ?? navigator.platform ?? "";
3416
+ return /mac|iphone|ipad|ipod/i.test(platform);
3417
+ }
3418
+
3419
+ // src/lib/shortcuts/manager.ts
3420
+ var DEFAULT_SEQUENCE_TIMEOUT_MS = 1e3;
3421
+ function signature(event) {
3422
+ return event.code || event.key;
3423
+ }
3424
+ function eventTarget(event) {
3425
+ const path = event.composedPath?.();
3426
+ return path && path.length > 0 ? path[0] ?? null : event.target;
3427
+ }
3428
+ function asElement(target) {
3429
+ if (target === null || typeof target !== "object") return null;
3430
+ const node = target;
3431
+ if (node.nodeType !== 1 || typeof node.tagName !== "string") return null;
3432
+ return target;
3433
+ }
3434
+ function inputType(element) {
3435
+ if (element.tagName !== "INPUT") return "";
3436
+ const value = element.type;
3437
+ return typeof value === "string" ? value.toLowerCase() : "";
3438
+ }
3439
+ function controlOwnsKey(target, event) {
3440
+ if (event.ctrlKey || event.metaKey || event.altKey) return false;
3441
+ const element = asElement(target);
3442
+ if (!element) return false;
3443
+ const tag = element.tagName;
3444
+ const type = inputType(element);
3445
+ if (tag === "BUTTON" || type === "button" || type === "submit" || type === "reset" || type === "image") {
3446
+ return event.key === " " || event.key === "Enter";
3447
+ }
3448
+ if (tag === "A" && element.getAttribute("href") !== null) {
3449
+ return event.key === "Enter";
3450
+ }
3451
+ if (tag === "SUMMARY") return event.key === " " || event.key === "Enter";
3452
+ if (type === "checkbox") return event.key === " ";
3453
+ if (type === "color") return event.key === " " || event.key === "Enter";
3454
+ if (type === "file") return event.key === " " || event.key === "Enter";
3455
+ if (type === "range") {
3456
+ return event.key.startsWith("Arrow") || RANGE_KEYS.has(event.key);
3457
+ }
3458
+ if (type === "radio") {
3459
+ return event.key === " " || event.key.startsWith("Arrow");
3460
+ }
3461
+ return false;
3462
+ }
3463
+ function isTypingTarget(target) {
3464
+ const element = asElement(target);
3465
+ if (!element) return false;
3466
+ if (element.isContentEditable) return true;
3467
+ const tag = element.tagName;
3468
+ if (tag === "TEXTAREA") return true;
3469
+ if (tag === "SELECT") return true;
3470
+ if (tag === "INPUT") {
3471
+ return !NON_TEXT_INPUT_TYPES.has(inputType(element));
3472
+ }
3473
+ const role = element.getAttribute("role");
3474
+ return role !== null && TYPE_AHEAD_ROLES.has(role);
3475
+ }
3476
+ var NON_TEXT_INPUT_TYPES = /* @__PURE__ */ new Set([
3477
+ "button",
3478
+ "checkbox",
3479
+ "color",
3480
+ "file",
3481
+ "hidden",
3482
+ "image",
3483
+ "radio",
3484
+ "range",
3485
+ "reset",
3486
+ "submit"
3487
+ ]);
3488
+ function firesWhileTyping(prepared) {
3489
+ const explicit = prepared.binding.whenTyping;
3490
+ if (explicit !== void 0) return explicit;
3491
+ const first = prepared.keys[0];
3492
+ if (first === void 0) return false;
3493
+ return first.mod || first.ctrl || first.meta || first.alt || first.key === "Escape";
3494
+ }
3495
+ function createShortcutManager(options = {}) {
3496
+ const isApple = options.isApple ?? detectApplePlatform();
3497
+ const sequenceTimeoutMs = options.sequenceTimeoutMs ?? DEFAULT_SEQUENCE_TIMEOUT_MS;
3498
+ const now = options.now ?? (() => Date.now());
3499
+ const layers = /* @__PURE__ */ new Set();
3500
+ let nextSequence = 0;
3501
+ let pendingAt = null;
3502
+ let pendingLayer = null;
3503
+ const consumedPresses = /* @__PURE__ */ new Map();
3504
+ let pendingKey = null;
3505
+ function layerShape(bindings, options2) {
3506
+ const keys = bindings.map((b) => b.binding.keys).join("\0");
3507
+ return `${keys}${options2.depth}${options2.blocking === true}${options2.enabled !== false}`;
3508
+ }
3509
+ function blocking() {
3510
+ return ordered().some((layer) => layer.options.blocking === true);
3511
+ }
3512
+ function abandonSequence() {
3513
+ pendingAt = null;
3514
+ pressedEvents.length = 0;
3515
+ pendingLayer = null;
3516
+ pendingKey = null;
3517
+ }
3518
+ function prepare(bindings) {
3519
+ return bindings.map((binding) => ({
3520
+ binding,
3521
+ keys: parseKeys(binding.keys)
3522
+ }));
3523
+ }
3524
+ function ordered() {
3525
+ return [...layers].filter((layer) => layer.options.enabled !== false).sort(
3526
+ (a, b) => b.options.depth - a.options.depth || b.sequence - a.sequence
3527
+ );
3528
+ }
3529
+ function matchDepth(prepared, pressed) {
3530
+ if (pressed.length > prepared.keys.length) return "none";
3531
+ for (let i = 0; i < pressed.length; i++) {
3532
+ const chord = prepared.keys[i];
3533
+ const event = pressed[i];
3534
+ if (chord === void 0 || event === void 0) return "none";
3535
+ if (!chordMatches(chord, event.key, event, isApple)) return "none";
3536
+ }
3537
+ return pressed.length === prepared.keys.length ? "exact" : "prefix";
3538
+ }
3539
+ function fire(prepared, event, invoke) {
3540
+ if (prepared.binding.preventDefault !== false) event.preventDefault();
3541
+ if (invoke) prepared.binding.run(event);
3542
+ }
3543
+ function insertsText(event, typing) {
3544
+ if (event.key === "Tab")
3545
+ return !event.ctrlKey && !event.metaKey && !event.altKey;
3546
+ if (!typing) return false;
3547
+ const altGraph = event.getModifierState?.("AltGraph") ?? false;
3548
+ if (!altGraph && (event.ctrlKey || event.metaKey)) {
3549
+ const letter = event.key.length === 1 ? event.key.toLowerCase() : event.key;
3550
+ if (letter === "z" && event.shiftKey) return !event.altKey;
3551
+ if (letter === REDO_LETTER)
3552
+ return !isApple && !event.shiftKey && !event.altKey;
3553
+ if (EDITING_NAVIGATION.has(event.key)) return !event.altKey || isApple;
3554
+ if (event.shiftKey || event.altKey) return false;
3555
+ return EDITING_LETTERS.has(letter);
3556
+ }
3557
+ if (!altGraph && event.altKey) {
3558
+ if ((event.key === "ArrowDown" || event.key === "ArrowUp") && asElement(eventTarget(event))?.tagName === "SELECT") {
3559
+ return true;
3560
+ }
3561
+ if (!isApple) return false;
3562
+ if (EDITING_NAVIGATION.has(event.key)) return true;
3563
+ }
3564
+ if (event.key === "Dead" || event.key === "Process") return true;
3565
+ if ([...event.key].length === 1) return true;
3566
+ if (AMBIGUOUS_KEYS.has(event.key)) return targetOwnsAmbiguousKey(event);
3567
+ return FIELD_KEYS.has(event.key);
3568
+ }
3569
+ function offer(pressed, event, typing, invoke) {
3570
+ for (const layer of ordered()) {
3571
+ const mayMatch = pressed.length <= 1 || pendingLayer === null || layer === pendingLayer;
3572
+ if (mayMatch) {
3573
+ let prefixed = false;
3574
+ for (const prepared of layer.bindings) {
3575
+ if (typing && !firesWhileTyping(prepared)) continue;
3576
+ if (prepared.binding.when && !prepared.binding.when()) continue;
3577
+ const depth = matchDepth(prepared, pressed);
3578
+ if (depth === "exact") {
3579
+ fire(prepared, event, invoke);
3580
+ return "fired";
3581
+ }
3582
+ if (depth === "prefix") prefixed = true;
3583
+ }
3584
+ if (prefixed) {
3585
+ pendingLayer = layer;
3586
+ event.preventDefault();
3587
+ return "pending";
3588
+ }
3589
+ }
3590
+ if (layer.options.blocking) return "blocked";
3591
+ }
3592
+ return "none";
3593
+ }
3594
+ function warnOnPrefixConflicts(prepared, layerName) {
3595
+ const resolved = (chord) => {
3596
+ const ctrl = chord.ctrl || chord.mod && !isApple;
3597
+ const meta = chord.meta || chord.mod && isApple;
3598
+ const shift = shiftIsMeaningful(chord.key) ? chord.shift : false;
3599
+ return `${chord.key}\0${ctrl}${meta}${chord.alt}${shift}`;
3600
+ };
3601
+ const sameChord = (a, b) => resolved(a) === resolved(b);
3602
+ for (const short of prepared) {
3603
+ for (const long of prepared) {
3604
+ if (short === long || short.keys.length >= long.keys.length) continue;
3605
+ if (short.binding.when !== void 0) continue;
3606
+ if (!firesWhileTyping(short) && firesWhileTyping(long)) continue;
3607
+ if (short.keys.every((chord, i) => sameChord(chord, long.keys[i]))) {
3608
+ devWarnOnce(
3609
+ false,
3610
+ `shortcuts: in layer "${layerName}", "${short.binding.keys}" is a prefix of "${long.binding.keys}", so the longer one can never fire. Bind one or the other.`
3611
+ );
3612
+ }
3613
+ }
3614
+ }
3615
+ }
3616
+ const watchers = /* @__PURE__ */ new Set();
3617
+ let snapshot = null;
3618
+ function computeSnapshot() {
3619
+ return ordered().flatMap(
3620
+ (layer) => layer.bindings.map((prepared) => ({
3621
+ keys: prepared.binding.keys,
3622
+ description: prepared.binding.description,
3623
+ layer: layer.options.name
3624
+ }))
3625
+ );
3626
+ }
3627
+ function sameShortcuts(a, b) {
3628
+ return a.length === b.length && a.every(
3629
+ (entry, index) => entry.keys === b[index]?.keys && entry.description === b[index]?.description && entry.layer === b[index]?.layer
3630
+ );
3631
+ }
3632
+ function changed() {
3633
+ const previous = snapshot;
3634
+ snapshot = null;
3635
+ if (watchers.size === 0) return;
3636
+ const next = computeSnapshot();
3637
+ if (previous && sameShortcuts(previous, next)) {
3638
+ snapshot = previous;
3639
+ return;
3640
+ }
3641
+ snapshot = next;
3642
+ for (const watcher of watchers) watcher();
3643
+ }
3644
+ const pressedEvents = [];
3645
+ function runOffer(pressed, event, typing) {
3646
+ try {
3647
+ return offer(pressed, event, typing, true);
3648
+ } catch (error) {
3649
+ abandonSequence();
3650
+ consumedPresses.delete(signature(event));
3651
+ throw error;
3652
+ }
3653
+ }
3654
+ function handle(event) {
3655
+ if (typeof event.key !== "string") return false;
3656
+ if (event.defaultPrevented) {
3657
+ abandonSequence();
3658
+ return true;
3659
+ }
3660
+ if (event.isComposing) {
3661
+ abandonSequence();
3662
+ return true;
3663
+ }
3664
+ if (MODIFIER_KEYS.has(event.key)) return blocking();
3665
+ if (controlOwnsKey(eventTarget(event), event)) {
3666
+ abandonSequence();
3667
+ return true;
3668
+ }
3669
+ const typing = isTypingTarget(eventTarget(event));
3670
+ if (event.repeat) {
3671
+ if (pendingKey !== signature(event)) {
3672
+ abandonSequence();
3673
+ }
3674
+ const held = consumedPresses.get(signature(event));
3675
+ if (held) {
3676
+ if (held.prevented) {
3677
+ event.preventDefault();
3678
+ } else {
3679
+ const still = offer([event], event, typing, false);
3680
+ if (still !== "fired" && !insertsText(event, typing)) {
3681
+ event.preventDefault();
3682
+ }
3683
+ }
3684
+ return true;
3685
+ }
3686
+ const repeated = offer([event], event, typing, false);
3687
+ if (repeated === "blocked" && !insertsText(event, typing)) {
3688
+ event.preventDefault();
3689
+ }
3690
+ return repeated !== "none";
3691
+ }
3692
+ if (pendingAt !== null && now() - pendingAt > sequenceTimeoutMs) {
3693
+ abandonSequence();
3694
+ }
3695
+ pressedEvents.push(event);
3696
+ let outcome = runOffer(pressedEvents, event, typing);
3697
+ if (pressedEvents.length > 1 && (outcome === "none" || outcome === "blocked")) {
3698
+ abandonSequence();
3699
+ pressedEvents.push(event);
3700
+ outcome = runOffer(pressedEvents, event, typing);
3701
+ }
3702
+ if (outcome === "pending") {
3703
+ pendingAt = now();
3704
+ pendingKey = signature(event);
3705
+ consumedPresses.set(signature(event), {
3706
+ prevented: event.defaultPrevented
3707
+ });
3708
+ return true;
3709
+ }
3710
+ abandonSequence();
3711
+ if (outcome === "blocked") {
3712
+ if (!insertsText(event, typing)) event.preventDefault();
3713
+ }
3714
+ const consumed = outcome === "fired" || outcome === "blocked";
3715
+ if (consumed) {
3716
+ consumedPresses.set(signature(event), {
3717
+ prevented: event.defaultPrevented
3718
+ });
3719
+ } else {
3720
+ consumedPresses.delete(signature(event));
3721
+ }
3722
+ return consumed;
3723
+ }
3724
+ return {
3725
+ register(bindings, layerOptions) {
3726
+ const prepared = prepare(bindings);
3727
+ const layer = {
3728
+ options: layerOptions,
3729
+ bindings: prepared,
3730
+ sequence: nextSequence++,
3731
+ shape: layerShape(prepared, layerOptions)
3732
+ };
3733
+ warnOnPrefixConflicts(layer.bindings, layerOptions.name);
3734
+ layers.add(layer);
3735
+ changed();
3736
+ return {
3737
+ update(nextBindings, nextOptions) {
3738
+ layer.bindings = prepare(nextBindings);
3739
+ layer.options = nextOptions;
3740
+ warnOnPrefixConflicts(layer.bindings, nextOptions.name);
3741
+ const shape = layerShape(layer.bindings, nextOptions);
3742
+ const shapeChanged = shape !== layer.shape;
3743
+ layer.shape = shape;
3744
+ if (shapeChanged && pendingLayer === layer) abandonSequence();
3745
+ changed();
3746
+ },
3747
+ dispose() {
3748
+ layers.delete(layer);
3749
+ changed();
3750
+ if (pendingLayer === layer) abandonSequence();
3751
+ }
3752
+ };
3753
+ },
3754
+ handle,
3755
+ attach(target) {
3756
+ const listener = (event) => {
3757
+ try {
3758
+ if (handle(event)) event.stopPropagation();
3759
+ } catch (error) {
3760
+ event.stopPropagation();
3761
+ throw error;
3762
+ }
3763
+ };
3764
+ target.addEventListener("keydown", listener);
3765
+ return () => {
3766
+ target.removeEventListener("keydown", listener);
3767
+ abandonSequence();
3768
+ consumedPresses.clear();
3769
+ };
3770
+ },
3771
+ subscribe(onChange) {
3772
+ watchers.add(onChange);
3773
+ return () => {
3774
+ watchers.delete(onChange);
3775
+ };
3776
+ },
3777
+ activeBindings() {
3778
+ if (snapshot) return snapshot;
3779
+ snapshot = computeSnapshot();
3780
+ return snapshot;
3781
+ }
3782
+ };
3783
+ }
3784
+ var MODIFIER_KEYS = /* @__PURE__ */ new Set([
3785
+ "Control",
3786
+ "Meta",
3787
+ "Alt",
3788
+ "Shift",
3789
+ // A dedicated AltGraph key reports its own keydown before the character-producing one. Without
3790
+ // it here, pressing AltGraph mid-sequence abandons the sequence before the character that would
3791
+ // have completed it ever arrives.
3792
+ "AltGraph"
3793
+ ]);
3794
+ var TYPE_AHEAD_ROLES = /* @__PURE__ */ new Set([
3795
+ "textbox",
3796
+ "combobox",
3797
+ "listbox",
3798
+ // The focused element inside an open listbox is the OPTION, and it is what the event reports;
3799
+ // the listbox itself is only its ancestor.
3800
+ "option",
3801
+ "menu",
3802
+ "menuitem",
3803
+ "menuitemcheckbox",
3804
+ "menuitemradio"
3805
+ ]);
3806
+ var RANGE_KEYS = /* @__PURE__ */ new Set(["Home", "End", "PageUp", "PageDown"]);
3807
+ var EDITING_LETTERS = /* @__PURE__ */ new Set(["a", "c", "v", "x", "z"]);
3808
+ var REDO_LETTER = "y";
3809
+ var EDITING_NAVIGATION = /* @__PURE__ */ new Set([
3810
+ "Insert",
3811
+ "ArrowLeft",
3812
+ "ArrowRight",
3813
+ "ArrowUp",
3814
+ "ArrowDown",
3815
+ "Home",
3816
+ "End",
3817
+ "Backspace",
3818
+ "Delete"
3819
+ ]);
3820
+ var AMBIGUOUS_KEYS = /* @__PURE__ */ new Set(["Enter", "PageUp", "PageDown"]);
3821
+ function targetOwnsAmbiguousKey(event) {
3822
+ const element = asElement(eventTarget(event));
3823
+ if (element === null) return false;
3824
+ const multiline = element.tagName === "TEXTAREA" || element.isContentEditable;
3825
+ if (event.key === "Enter") return multiline;
3826
+ return multiline || element.tagName === "SELECT";
3827
+ }
3828
+ var FIELD_KEYS = /* @__PURE__ */ new Set([
3829
+ "Backspace",
3830
+ "Delete",
3831
+ "ArrowUp",
3832
+ "ArrowDown",
3833
+ "ArrowLeft",
3834
+ "ArrowRight",
3835
+ "Home",
3836
+ "End",
3837
+ // Shift+Insert pastes and carries no ctrl or meta, so it arrives here rather than at the
3838
+ // chord branch where Ctrl+Insert is recognised.
3839
+ "Insert",
3840
+ // Tab moves focus, and a blocking layer must NOT take it. The documented case for blocking is
3841
+ // a modal, whose focus trap only calls `preventDefault()` at the first and last tabbable
3842
+ // element — ordinary moves between the controls inside it rely on the browser default.
3843
+ // Suppressing every Tab therefore pinned focus to one control in exactly the situation
3844
+ // blocking exists to serve. A layer that genuinely wants Tab binds it.
3845
+ "Tab"
3846
+ ]);
3847
+
3848
+ // src/lib/shortcuts/react.tsx
3849
+ var import_jsx_runtime39 = require("react/jsx-runtime");
3850
+ var ShortcutContext = React13.createContext(null);
3851
+ var ownersByTarget = /* @__PURE__ */ new WeakMap();
3852
+ var useIsomorphicLayoutEffect = typeof document === "undefined" ? React13.useEffect : React13.useLayoutEffect;
3853
+ function optionsFingerprint(options) {
3854
+ return [
3855
+ options.isApple ?? "auto",
3856
+ options.sequenceTimeoutMs ?? "default",
3857
+ options.now ? "clock" : "no-clock"
3858
+ ].join("\0");
3859
+ }
3860
+ function ShortcutProvider({
3861
+ children,
3862
+ target,
3863
+ ...managerOptions
3864
+ }) {
3865
+ const parent = React13.useContext(ShortcutContext);
3866
+ const resolvedTarget = target === null ? null : target ?? (typeof document === "undefined" ? null : document);
3867
+ const nestedOnSameTarget = parent !== null && parent.target === resolvedTarget;
3868
+ const optionsRef = React13.useRef(managerOptions);
3869
+ const ownManagers = React13.useRef(/* @__PURE__ */ new WeakMap());
3870
+ const ownDetached = React13.useRef(null);
3871
+ const detached = React13.useMemo(() => {
3872
+ if (resolvedTarget === null) {
3873
+ ownDetached.current ??= createShortcutManager(optionsRef.current);
3874
+ return ownDetached.current;
3875
+ }
3876
+ const existing = ownManagers.current.get(resolvedTarget);
3877
+ if (existing) return existing;
3878
+ const created = createShortcutManager(optionsRef.current);
3879
+ ownManagers.current.set(resolvedTarget, created);
3880
+ return created;
3881
+ }, [resolvedTarget]);
3882
+ let owner = resolvedTarget === null ? null : ownersByTarget.get(resolvedTarget);
3883
+ const fingerprint = optionsFingerprint(optionsRef.current);
3884
+ if (resolvedTarget !== null && (!owner || owner.retired)) {
3885
+ owner = {
3886
+ manager: detached,
3887
+ providers: 0,
3888
+ retired: false,
3889
+ options: fingerprint
3890
+ };
3891
+ ownersByTarget.set(resolvedTarget, owner);
3892
+ }
3893
+ const adoptedDiffers = Boolean(owner) && owner?.options !== fingerprint || nestedOnSameTarget && parent !== null && parent.options !== fingerprint;
3894
+ devWarnOnce(
3895
+ !adoptedDiffers,
3896
+ "ShortcutProvider: another provider is already listening on this target with different options, so the ones passed here are being ignored. Managers are shared per target; give the providers matching options, or a target of their own."
3897
+ );
3898
+ const manager = nestedOnSameTarget && parent ? parent.manager : owner ? owner.manager : detached;
3899
+ useIsomorphicLayoutEffect(() => {
3900
+ if (resolvedTarget === null) return;
3901
+ let entry = ownersByTarget.get(resolvedTarget);
3902
+ if (!entry) {
3903
+ entry = {
3904
+ manager,
3905
+ providers: 0,
3906
+ retired: false,
3907
+ options: optionsFingerprint(optionsRef.current)
3908
+ };
3909
+ ownersByTarget.set(resolvedTarget, entry);
3910
+ }
3911
+ const owned = entry;
3912
+ owned.providers += 1;
3913
+ owned.retired = false;
3914
+ if (owned.providers === 1) {
3915
+ owned.detach = owned.manager.attach(resolvedTarget);
3916
+ }
3917
+ return () => {
3918
+ owned.providers -= 1;
3919
+ if (owned.providers === 0) {
3920
+ owned.detach?.();
3921
+ owned.detach = void 0;
3922
+ owned.retired = true;
3923
+ }
3924
+ };
3925
+ }, [resolvedTarget, manager]);
3926
+ const depth = nestedOnSameTarget && parent ? parent.depth : 0;
3927
+ const value = React13.useMemo(
3928
+ () => ({ manager, depth, target: resolvedTarget, options: fingerprint }),
3929
+ [manager, depth, resolvedTarget, fingerprint]
3930
+ );
3931
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ShortcutContext.Provider, { value, children });
3932
+ }
3933
+ function ShortcutScope({
3934
+ children
3935
+ }) {
3936
+ const parent = React13.useContext(ShortcutContext);
3937
+ if (!parent) {
3938
+ throw new Error("ShortcutScope must be rendered inside a ShortcutProvider");
3939
+ }
3940
+ const value = React13.useMemo(
3941
+ () => ({
3942
+ manager: parent.manager,
3943
+ depth: parent.depth + 1,
3944
+ target: parent.target,
3945
+ // Inherited: a scope raises precedence, it does not build a manager, so the options in force
3946
+ // are still the ones the provider above it used.
3947
+ options: parent.options
3948
+ }),
3949
+ [parent.manager, parent.depth, parent.target, parent.options]
3950
+ );
3951
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ShortcutContext.Provider, { value, children });
3952
+ }
3953
+ function useShortcuts(bindings, options) {
3954
+ const context = React13.useContext(ShortcutContext);
3955
+ if (!context) {
3956
+ throw new Error("useShortcuts must be called inside a ShortcutProvider");
3957
+ }
3958
+ const { manager, depth } = context;
3959
+ const registration = React13.useRef(null);
3960
+ const latest = React13.useRef({ bindings, options });
3961
+ useIsomorphicLayoutEffect(() => {
3962
+ registration.current = manager.register([], {
3963
+ name: latest.current.options.name,
3964
+ depth
3965
+ });
3966
+ return () => {
3967
+ registration.current?.dispose();
3968
+ registration.current = null;
3969
+ };
3970
+ }, [manager, depth]);
3971
+ useIsomorphicLayoutEffect(() => {
3972
+ latest.current = { bindings, options };
3973
+ registration.current?.update(bindings, {
3974
+ name: options.name,
3975
+ depth,
3976
+ enabled: options.enabled,
3977
+ blocking: options.blocking
3978
+ });
3979
+ });
3980
+ }
3981
+ function useShortcutManager() {
3982
+ const context = React13.useContext(ShortcutContext);
3983
+ if (!context) {
3984
+ throw new Error(
3985
+ "useShortcutManager must be called inside a ShortcutProvider"
3986
+ );
3987
+ }
3988
+ return context.manager;
3989
+ }
3990
+ function useActiveShortcuts() {
3991
+ const manager = useShortcutManager();
3992
+ return React13.useSyncExternalStore(
3993
+ manager.subscribe,
3994
+ manager.activeBindings,
3995
+ manager.activeBindings
3996
+ );
3997
+ }
2812
3998
  // Annotate the CommonJS export names for ESM import in node:
2813
3999
  0 && (module.exports = {
2814
4000
  Accordion,
@@ -2845,6 +4031,7 @@ TreeView.displayName = "TreeView";
2845
4031
  Collapsible,
2846
4032
  CollapsibleContent,
2847
4033
  CollapsibleTrigger,
4034
+ ColorPicker,
2848
4035
  Command,
2849
4036
  CommandDialog,
2850
4037
  CommandEmpty,
@@ -2929,7 +4116,10 @@ TreeView.displayName = "TreeView";
2929
4116
  SheetPortal,
2930
4117
  SheetTitle,
2931
4118
  SheetTrigger,
4119
+ ShortcutProvider,
4120
+ ShortcutScope,
2932
4121
  Skeleton,
4122
+ Slider,
2933
4123
  Spinner,
2934
4124
  Stack,
2935
4125
  Stat,
@@ -2963,13 +4153,18 @@ TreeView.displayName = "TreeView";
2963
4153
  badgeVariants,
2964
4154
  buttonVariants,
2965
4155
  cardVariants,
4156
+ createShortcutManager,
2966
4157
  dialogContentVariants,
2967
4158
  inputVariants,
4159
+ parseKeys,
2968
4160
  progressVariants,
2969
4161
  selectTriggerVariants,
2970
4162
  sheetVariants,
2971
4163
  spinnerVariants,
2972
4164
  toast,
2973
- usePortalContainer
4165
+ useActiveShortcuts,
4166
+ usePortalContainer,
4167
+ useShortcutManager,
4168
+ useShortcuts
2974
4169
  });
2975
4170
  //# sourceMappingURL=index.cjs.map