@nextlyhq/ui 0.0.2-alpha.60 → 0.0.2-alpha.62

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.mjs CHANGED
@@ -998,15 +998,23 @@ var tabsListVariants = cva8(
998
998
  defaultVariants: { variant: "default" }
999
999
  }
1000
1000
  );
1001
- var TabsList = forwardRef15(({ className, variant, ...props }, ref) => /* @__PURE__ */ jsx20(
1002
- List,
1003
- {
1004
- ref,
1005
- "data-slot": "tabs-list",
1006
- className: cn(tabsListVariants({ variant }), className),
1007
- ...props
1008
- }
1009
- ));
1001
+ var TabsList = forwardRef15(({ className, variant, scrollable = false, ...props }, ref) => {
1002
+ const list = /* @__PURE__ */ jsx20(
1003
+ List,
1004
+ {
1005
+ ref,
1006
+ "data-slot": "tabs-list",
1007
+ className: cn(
1008
+ tabsListVariants({ variant }),
1009
+ scrollable && "w-max min-w-full",
1010
+ className
1011
+ ),
1012
+ ...props
1013
+ }
1014
+ );
1015
+ if (!scrollable) return list;
1016
+ return /* @__PURE__ */ jsx20("div", { "data-slot": "tabs-list-scroller", className: "w-full overflow-x-auto", children: list });
1017
+ });
1010
1018
  TabsList.displayName = List.displayName;
1011
1019
  var TABS_TRIGGER_SIZES = {
1012
1020
  default: "text-sm",
@@ -1756,7 +1764,11 @@ SheetDescription.displayName = DialogPrimitive2.Description.displayName;
1756
1764
 
1757
1765
  // src/components/command.tsx
1758
1766
  import * as DialogPrimitive3 from "@radix-ui/react-dialog";
1759
- import { Command as CommandPrimitive, defaultFilter } from "cmdk";
1767
+ import {
1768
+ Command as CommandPrimitive,
1769
+ defaultFilter,
1770
+ useCommandState as useCommandStatePrimitive
1771
+ } from "cmdk";
1760
1772
  import { Search } from "lucide-react";
1761
1773
  import { forwardRef as forwardRef22 } from "react";
1762
1774
  import { jsx as jsx27, jsxs as jsxs9 } from "react/jsx-runtime";
@@ -1983,6 +1995,7 @@ var CommandShortcut = forwardRef22(
1983
1995
  );
1984
1996
  CommandShortcut.displayName = "CommandShortcut";
1985
1997
  var commandDefaultFilter = defaultFilter;
1998
+ var useCommandHighlight = () => useCommandStatePrimitive((state) => state.value);
1986
1999
 
1987
2000
  // src/components/spinner.tsx
1988
2001
  import { cva as cva12 } from "class-variance-authority";
@@ -2257,7 +2270,7 @@ TableSkeleton.displayName = "TableSkeleton";
2257
2270
 
2258
2271
  // src/components/color-picker.tsx
2259
2272
  import { Pipette } from "lucide-react";
2260
- import * as React9 from "react";
2273
+ import * as React10 from "react";
2261
2274
 
2262
2275
  // src/lib/color/convert.ts
2263
2276
  var clamp01 = (n) => n < 0 ? 0 : n > 1 ? 1 : n;
@@ -2362,6 +2375,10 @@ function hueSliderValue(hue, max) {
2362
2375
  return step > max ? max : step;
2363
2376
  }
2364
2377
 
2378
+ // src/lib/isomorphic-layout-effect.ts
2379
+ import * as React9 from "react";
2380
+ var useIsomorphicLayoutEffect = typeof document === "undefined" ? React9.useEffect : React9.useLayoutEffect;
2381
+
2365
2382
  // src/components/color-picker.tsx
2366
2383
  import { Fragment as Fragment2, jsx as jsx34, jsxs as jsxs13 } from "react/jsx-runtime";
2367
2384
  function toHsva(hex) {
@@ -2383,6 +2400,9 @@ var CHECKERBOARD = "repeating-conic-gradient(#c8c8c8 0% 25%, #ffffff 0% 50%)";
2383
2400
  function eyeDropperSupported() {
2384
2401
  return typeof window !== "undefined" && "EyeDropper" in window;
2385
2402
  }
2403
+ function isNode(value) {
2404
+ return value !== null && typeof value.nodeType === "number";
2405
+ }
2386
2406
  function ColorPicker({
2387
2407
  color,
2388
2408
  onColorChange,
@@ -2392,20 +2412,100 @@ function ColorPicker({
2392
2412
  showAlpha = false,
2393
2413
  className
2394
2414
  }) {
2395
- const fieldId = React9.useId();
2396
- const surfaceRef = React9.useRef(null);
2397
- const [hsva, setHsva] = React9.useState(() => toHsva(color));
2398
- const [draftHex, setDraftHex] = React9.useState(null);
2415
+ const fieldId = React10.useId();
2416
+ const rootRef = React10.useRef(null);
2417
+ const insidePress = React10.useRef(false);
2418
+ const owedFinish = React10.useRef(false);
2419
+ const pendingTap = React10.useRef(null);
2420
+ const latestCommit = React10.useRef(() => void 0);
2421
+ const surfaceRef = React10.useRef(null);
2422
+ const [hsva, setHsva] = React10.useState(() => toHsva(color));
2423
+ const [draftHex, setDraftHex] = React10.useState(null);
2424
+ const draftRef = React10.useRef(null);
2399
2425
  const rendered = toHexString(hsva, showAlpha);
2400
- React9.useEffect(() => {
2426
+ useIsomorphicLayoutEffect(() => {
2401
2427
  const incoming = parseHex(color);
2402
2428
  if (incoming && toHex(incoming, showAlpha ? incoming.alpha : 1) !== rendered) {
2429
+ dropDraft();
2403
2430
  setHsva((prev) => hsvaFrom(incoming, incoming.alpha, prev.h));
2404
2431
  }
2405
2432
  }, [color, rendered, showAlpha]);
2433
+ const dropDraft = () => {
2434
+ owedFinish.current = false;
2435
+ draftRef.current = null;
2436
+ setDraftHex(null);
2437
+ };
2438
+ const commitDraft = () => {
2439
+ owedFinish.current = false;
2440
+ const text = draftRef.current;
2441
+ draftRef.current = null;
2442
+ if (text === null) return;
2443
+ setDraftHex(null);
2444
+ const parsed = parseHex(text);
2445
+ if (!parsed) return;
2446
+ setHsva((prev) => hsvaFrom(parsed, parsed.alpha, prev.h));
2447
+ onColorChange(toHex(parsed, showAlpha ? parsed.alpha : 1));
2448
+ };
2449
+ useIsomorphicLayoutEffect(() => {
2450
+ const root = rootRef.current;
2451
+ if (root === null) return;
2452
+ latestCommit.current = commitDraft;
2453
+ const onPointerDown = (event) => {
2454
+ pendingTap.current?.();
2455
+ pendingTap.current = null;
2456
+ const path = typeof event.composedPath === "function" ? event.composedPath() : [];
2457
+ const inside = path.includes(root) || isNode(event.target) && root.contains(event.target);
2458
+ insidePress.current = true;
2459
+ if (inside) return;
2460
+ const pointerType = event.pointerType;
2461
+ if (pointerType === "touch") {
2462
+ const onClick = () => {
2463
+ owner.removeEventListener("click", onClick, true);
2464
+ pendingTap.current = null;
2465
+ latestCommit.current();
2466
+ };
2467
+ owner.addEventListener("click", onClick, true);
2468
+ pendingTap.current = () => {
2469
+ owner.removeEventListener("click", onClick, true);
2470
+ };
2471
+ return;
2472
+ }
2473
+ commitDraft();
2474
+ };
2475
+ const owner = root.ownerDocument;
2476
+ const onPointerCancel = () => {
2477
+ insidePress.current = false;
2478
+ pendingTap.current?.();
2479
+ pendingTap.current = null;
2480
+ };
2481
+ const onPointerUp = () => {
2482
+ insidePress.current = false;
2483
+ };
2484
+ const onClickResolve = () => {
2485
+ if (!owedFinish.current) return;
2486
+ latestCommit.current();
2487
+ };
2488
+ owner.addEventListener("pointerdown", onPointerDown, true);
2489
+ owner.addEventListener("pointercancel", onPointerCancel, true);
2490
+ owner.addEventListener("pointerup", onPointerUp, true);
2491
+ owner.addEventListener("click", onClickResolve, false);
2492
+ return () => {
2493
+ owner.removeEventListener("pointerdown", onPointerDown, true);
2494
+ owner.removeEventListener("pointercancel", onPointerCancel, true);
2495
+ owner.removeEventListener("pointerup", onPointerUp, true);
2496
+ owner.removeEventListener("click", onClickResolve, false);
2497
+ };
2498
+ });
2499
+ useIsomorphicLayoutEffect(
2500
+ () => () => {
2501
+ pendingTap.current?.();
2502
+ pendingTap.current = null;
2503
+ },
2504
+ []
2505
+ );
2406
2506
  const commit = (next) => {
2407
2507
  setHsva(next);
2408
- setDraftHex(null);
2508
+ dropDraft();
2409
2509
  onColorChange(toHexString(next, showAlpha));
2410
2510
  };
2411
2511
  const trackPointer = (event) => {
@@ -2435,8 +2535,8 @@ function ColorPicker({
2435
2535
  event.preventDefault();
2436
2536
  nudge(move[0], move[1]);
2437
2537
  };
2438
- const [canPickFromScreen, setCanPickFromScreen] = React9.useState(false);
2439
- React9.useEffect(() => {
2538
+ const [canPickFromScreen, setCanPickFromScreen] = React10.useState(false);
2539
+ React10.useEffect(() => {
2440
2540
  setCanPickFromScreen(eyeDropperSupported());
2441
2541
  }, []);
2442
2542
  const handle = surfacePointFor(hsva.s, hsva.v);
@@ -2444,163 +2544,187 @@ function ColorPicker({
2444
2544
  const pickFromScreen = async () => {
2445
2545
  const ctor = window.EyeDropper;
2446
2546
  if (!ctor) return;
2547
+ const owed = owedFinish.current;
2548
+ owedFinish.current = false;
2447
2549
  let sampled;
2448
2550
  try {
2449
2551
  sampled = (await new ctor().open()).sRGBHex;
2450
2552
  } catch {
2553
+ if (owed) commitDraft();
2451
2554
  return;
2452
2555
  }
2453
2556
  const parsed = parseHex(sampled);
2454
2557
  if (parsed) commit(hsvaFrom(parsed, hsva.a, hsva.h));
2455
2558
  };
2456
- return /* @__PURE__ */ jsxs13("div", { className: cn("w-64 space-y-3", className), children: [
2457
- /* @__PURE__ */ jsx34(
2458
- "div",
2459
- {
2460
- ref: surfaceRef,
2461
- role: "application",
2462
- tabIndex: 0,
2463
- "aria-label": `Saturation and brightness: ${Math.round(hsva.s * 100)}% saturation, ${Math.round(hsva.v * 100)}% brightness. Arrow keys adjust.`,
2464
- 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",
2465
- style: {
2466
- backgroundColor: hueOnly,
2467
- // Value on TOP of saturation. CSS paints the first layer nearest the
2468
- // viewer, so the reverse order lets the opaque white end of the
2469
- // saturation ramp cover the black end of the value ramp: the
2470
- // bottom-left corner displays white while selecting black.
2471
- backgroundImage: "linear-gradient(to top, #000, transparent), linear-gradient(to right, #fff, transparent)"
2472
- },
2473
- onKeyDown: handleSurfaceKey,
2474
- onPointerDown: (event) => {
2475
- event.currentTarget.setPointerCapture(event.pointerId);
2476
- trackPointer(event);
2477
- },
2478
- onPointerMove: (event) => {
2479
- if (event.buttons === 1) trackPointer(event);
2480
- },
2481
- children: /* @__PURE__ */ jsx34(
2482
- "span",
2483
- {
2484
- "aria-hidden": "true",
2485
- 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",
2486
- style: { left: `${handle.x * 100}%`, top: `${handle.y * 100}%` }
2487
- }
2488
- )
2489
- }
2490
- ),
2491
- /* @__PURE__ */ jsx34("label", { className: "sr-only", htmlFor: `${fieldId}-hue`, children: "Hue" }),
2492
- /* @__PURE__ */ jsx34(
2493
- "input",
2494
- {
2495
- id: `${fieldId}-hue`,
2496
- type: "range",
2497
- min: 0,
2498
- max: HUE_MAX,
2499
- step: 1,
2500
- value: hueSliderValue(hsva.h, HUE_MAX),
2501
- onChange: (event) => commit({ ...hsva, h: hueAt(+event.target.value / (HUE_MAX + 1)) }),
2502
- className: SLIDER,
2503
- style: {
2504
- backgroundImage: "linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00)"
2559
+ return /* @__PURE__ */ jsxs13(
2560
+ "div",
2561
+ {
2562
+ ref: rootRef,
2563
+ className: cn("w-64 space-y-3", className),
2564
+ onBlur: (event) => {
2565
+ const next = event.relatedTarget;
2566
+ const root = rootRef.current;
2567
+ if (root !== null && isNode(next) && root.contains(next)) return;
2568
+ if (next === null && insidePress.current) {
2569
+ owedFinish.current = true;
2570
+ return;
2505
2571
  }
2506
- }
2507
- ),
2508
- showAlpha && /* @__PURE__ */ jsxs13(Fragment2, { children: [
2509
- /* @__PURE__ */ jsx34("label", { className: "sr-only", htmlFor: `${fieldId}-alpha`, children: "Opacity" }),
2510
- /* @__PURE__ */ jsx34(
2511
- "input",
2512
- {
2513
- id: `${fieldId}-alpha`,
2514
- type: "range",
2515
- min: 0,
2516
- max: 100,
2517
- step: 1,
2518
- value: Math.round(hsva.a * 100),
2519
- onChange: (event) => commit({ ...hsva, a: +event.target.value / 100 }),
2520
- className: SLIDER,
2521
- style: {
2522
- // The ramp runs to the colour being edited, over a chequerboard,
2523
- // so the track shows what the slider actually controls. Without
2524
- // any background this rendered as a blank 12px strip whose only
2525
- // label was screen-reader-only.
2526
- backgroundImage: `linear-gradient(to right, transparent, ${toHexString({ ...hsva, a: 1 }, false)}), ${CHECKERBOARD}`,
2527
- backgroundSize: "100% 100%, 8px 8px"
2572
+ commitDraft();
2573
+ },
2574
+ children: [
2575
+ /* @__PURE__ */ jsx34(
2576
+ "div",
2577
+ {
2578
+ ref: surfaceRef,
2579
+ role: "application",
2580
+ tabIndex: 0,
2581
+ "aria-label": `Saturation and brightness: ${Math.round(hsva.s * 100)}% saturation, ${Math.round(hsva.v * 100)}% brightness. Arrow keys adjust.`,
2582
+ 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",
2583
+ style: {
2584
+ backgroundColor: hueOnly,
2585
+ // Value on TOP of saturation. CSS paints the first layer nearest the
2586
+ // viewer, so the reverse order lets the opaque white end of the
2587
+ // saturation ramp cover the black end of the value ramp: the
2588
+ // bottom-left corner displays white while selecting black.
2589
+ backgroundImage: "linear-gradient(to top, #000, transparent), linear-gradient(to right, #fff, transparent)"
2590
+ },
2591
+ onKeyDown: handleSurfaceKey,
2592
+ onPointerDown: (event) => {
2593
+ event.currentTarget.setPointerCapture(event.pointerId);
2594
+ trackPointer(event);
2595
+ },
2596
+ onPointerMove: (event) => {
2597
+ if (event.buttons === 1) trackPointer(event);
2598
+ },
2599
+ children: /* @__PURE__ */ jsx34(
2600
+ "span",
2601
+ {
2602
+ "aria-hidden": "true",
2603
+ 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",
2604
+ style: { left: `${handle.x * 100}%`, top: `${handle.y * 100}%` }
2605
+ }
2606
+ )
2528
2607
  }
2529
- }
2530
- )
2531
- ] }),
2532
- /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2", children: [
2533
- /* @__PURE__ */ jsx34("label", { className: "sr-only", htmlFor: `${fieldId}-hex`, children: "Hex colour" }),
2534
- /* @__PURE__ */ jsx34(
2535
- Input,
2536
- {
2537
- id: `${fieldId}-hex`,
2538
- className: "font-mono",
2539
- value: draftHex ?? rendered,
2540
- onChange: (event) => {
2541
- const text = event.target.value;
2542
- setDraftHex(text);
2543
- const parsed = parseHex(text);
2544
- if (parsed) {
2545
- setHsva(hsvaFrom(parsed, parsed.alpha, hsva.h));
2546
- onColorChange(toHex(parsed, showAlpha ? parsed.alpha : 1));
2608
+ ),
2609
+ /* @__PURE__ */ jsx34("label", { className: "sr-only", htmlFor: `${fieldId}-hue`, children: "Hue" }),
2610
+ /* @__PURE__ */ jsx34(
2611
+ "input",
2612
+ {
2613
+ id: `${fieldId}-hue`,
2614
+ type: "range",
2615
+ min: 0,
2616
+ max: HUE_MAX,
2617
+ step: 1,
2618
+ value: hueSliderValue(hsva.h, HUE_MAX),
2619
+ onChange: (event) => commit({ ...hsva, h: hueAt(+event.target.value / (HUE_MAX + 1)) }),
2620
+ className: SLIDER,
2621
+ style: {
2622
+ backgroundImage: "linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00)"
2547
2623
  }
2548
- },
2549
- onBlur: () => setDraftHex(null)
2550
- }
2551
- ),
2552
- canPickFromScreen && /* @__PURE__ */ jsx34(
2553
- Button,
2554
- {
2555
- type: "button",
2556
- variant: "outline",
2557
- size: "icon",
2558
- "aria-label": "Pick a colour from the screen",
2559
- onClick: () => void pickFromScreen(),
2560
- children: /* @__PURE__ */ jsx34(Pipette, { className: "size-4" })
2561
- }
2562
- )
2563
- ] }),
2564
- swatches.length > 0 && /* @__PURE__ */ jsxs13("div", { children: [
2565
- /* @__PURE__ */ jsx34("p", { className: "text-muted-foreground mb-1 text-xs", children: "Presets" }),
2566
- /* @__PURE__ */ jsx34("div", { className: "flex flex-wrap gap-1", children: swatches.map((swatch) => /* @__PURE__ */ jsx34(
2567
- "button",
2568
- {
2569
- type: "button",
2570
- title: swatch.label,
2571
- "aria-label": swatch.label,
2572
- className: "size-6 rounded border shadow-sm",
2573
- style: { backgroundColor: swatch.color },
2574
- onClick: () => onSwatchSelect?.(swatch)
2575
- },
2576
- swatch.id
2577
- )) })
2578
- ] }),
2579
- recentColors.length > 0 && /* @__PURE__ */ jsxs13("div", { children: [
2580
- /* @__PURE__ */ jsx34("p", { className: "text-muted-foreground mb-1 text-xs", children: "Recent" }),
2581
- /* @__PURE__ */ jsx34("div", { className: "flex flex-wrap gap-1", children: recentColors.map((recent) => /* @__PURE__ */ jsx34(
2582
- "button",
2583
- {
2584
- type: "button",
2585
- title: recent,
2586
- "aria-label": recent,
2587
- className: "size-6 rounded border shadow-sm",
2588
- style: { backgroundColor: recent },
2589
- onClick: () => {
2590
- const parsed = parseHex(recent);
2591
- if (parsed) commit(hsvaFrom(parsed, parsed.alpha, hsva.h));
2592
2624
  }
2593
- },
2594
- recent
2595
- )) })
2596
- ] })
2597
- ] });
2625
+ ),
2626
+ showAlpha && /* @__PURE__ */ jsxs13(Fragment2, { children: [
2627
+ /* @__PURE__ */ jsx34("label", { className: "sr-only", htmlFor: `${fieldId}-alpha`, children: "Opacity" }),
2628
+ /* @__PURE__ */ jsx34(
2629
+ "input",
2630
+ {
2631
+ id: `${fieldId}-alpha`,
2632
+ type: "range",
2633
+ min: 0,
2634
+ max: 100,
2635
+ step: 1,
2636
+ value: Math.round(hsva.a * 100),
2637
+ onChange: (event) => commit({ ...hsva, a: +event.target.value / 100 }),
2638
+ className: SLIDER,
2639
+ style: {
2640
+ // The ramp runs to the colour being edited, over a chequerboard,
2641
+ // so the track shows what the slider actually controls. Without
2642
+ // any background this rendered as a blank 12px strip whose only
2643
+ // label was screen-reader-only.
2644
+ backgroundImage: `linear-gradient(to right, transparent, ${toHexString({ ...hsva, a: 1 }, false)}), ${CHECKERBOARD}`,
2645
+ backgroundSize: "100% 100%, 8px 8px"
2646
+ }
2647
+ }
2648
+ )
2649
+ ] }),
2650
+ /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2", children: [
2651
+ /* @__PURE__ */ jsx34("label", { className: "sr-only", htmlFor: `${fieldId}-hex`, children: "Hex colour" }),
2652
+ /* @__PURE__ */ jsx34(
2653
+ Input,
2654
+ {
2655
+ id: `${fieldId}-hex`,
2656
+ className: "font-mono",
2657
+ value: draftHex ?? rendered,
2658
+ onChange: (event) => {
2659
+ draftRef.current = event.target.value;
2660
+ setDraftHex(event.target.value);
2661
+ },
2662
+ onKeyDown: (event) => {
2663
+ if (event.key !== "Enter") return;
2664
+ if (event.nativeEvent.isComposing || event.keyCode === 229) return;
2665
+ event.preventDefault();
2666
+ commitDraft();
2667
+ }
2668
+ }
2669
+ ),
2670
+ canPickFromScreen && /* @__PURE__ */ jsx34(
2671
+ Button,
2672
+ {
2673
+ type: "button",
2674
+ variant: "outline",
2675
+ size: "icon",
2676
+ "aria-label": "Pick a colour from the screen",
2677
+ onClick: () => void pickFromScreen(),
2678
+ children: /* @__PURE__ */ jsx34(Pipette, { className: "size-4" })
2679
+ }
2680
+ )
2681
+ ] }),
2682
+ swatches.length > 0 && /* @__PURE__ */ jsxs13("div", { children: [
2683
+ /* @__PURE__ */ jsx34("p", { className: "text-muted-foreground mb-1 text-xs", children: "Presets" }),
2684
+ /* @__PURE__ */ jsx34("div", { className: "flex flex-wrap gap-1", children: swatches.map((swatch) => /* @__PURE__ */ jsx34(
2685
+ "button",
2686
+ {
2687
+ type: "button",
2688
+ title: swatch.label,
2689
+ "aria-label": swatch.label,
2690
+ className: "size-6 rounded border shadow-sm",
2691
+ style: { backgroundColor: swatch.color },
2692
+ onClick: () => {
2693
+ if (onSwatchSelect === void 0) return;
2694
+ dropDraft();
2695
+ onSwatchSelect(swatch);
2696
+ }
2697
+ },
2698
+ swatch.id
2699
+ )) })
2700
+ ] }),
2701
+ recentColors.length > 0 && /* @__PURE__ */ jsxs13("div", { children: [
2702
+ /* @__PURE__ */ jsx34("p", { className: "text-muted-foreground mb-1 text-xs", children: "Recent" }),
2703
+ /* @__PURE__ */ jsx34("div", { className: "flex flex-wrap gap-1", children: recentColors.map((recent) => /* @__PURE__ */ jsx34(
2704
+ "button",
2705
+ {
2706
+ type: "button",
2707
+ title: recent,
2708
+ "aria-label": recent,
2709
+ className: "size-6 rounded border shadow-sm",
2710
+ style: { backgroundColor: recent },
2711
+ onClick: () => {
2712
+ const parsed = parseHex(recent);
2713
+ if (parsed) commit(hsvaFrom(parsed, parsed.alpha, hsva.h));
2714
+ }
2715
+ },
2716
+ recent
2717
+ )) })
2718
+ ] })
2719
+ ]
2720
+ }
2721
+ );
2598
2722
  }
2599
2723
 
2600
2724
  // src/components/context-menu.tsx
2601
2725
  import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
2602
2726
  import { Check as Check4, ChevronRight as ChevronRight2, Circle as Circle2 } from "lucide-react";
2603
- import * as React10 from "react";
2727
+ import * as React11 from "react";
2604
2728
  import { jsx as jsx35, jsxs as jsxs14 } from "react/jsx-runtime";
2605
2729
  var menuItemBase2 = "cursor-pointer transition-colors data-[highlighted]:bg-muted data-[highlighted]:text-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
2606
2730
  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]";
@@ -2609,7 +2733,7 @@ var ContextMenuTrigger = ContextMenuPrimitive.Trigger;
2609
2733
  var ContextMenuGroup = ContextMenuPrimitive.Group;
2610
2734
  var ContextMenuSub = ContextMenuPrimitive.Sub;
2611
2735
  var ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
2612
- var ContextMenuSubTrigger = React10.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsxs14(
2736
+ var ContextMenuSubTrigger = React11.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsxs14(
2613
2737
  ContextMenuPrimitive.SubTrigger,
2614
2738
  {
2615
2739
  ref,
@@ -2627,7 +2751,7 @@ var ContextMenuSubTrigger = React10.forwardRef(({ className, inset, children, ..
2627
2751
  }
2628
2752
  ));
2629
2753
  ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName;
2630
- var ContextMenuSubContent = React10.forwardRef(({ className, ...props }, ref) => {
2754
+ var ContextMenuSubContent = React11.forwardRef(({ className, ...props }, ref) => {
2631
2755
  const portalContainer = usePortalContainer();
2632
2756
  return /* @__PURE__ */ jsx35(ContextMenuPrimitive.Portal, { container: portalContainer, children: /* @__PURE__ */ jsx35(
2633
2757
  ContextMenuPrimitive.SubContent,
@@ -2639,7 +2763,7 @@ var ContextMenuSubContent = React10.forwardRef(({ className, ...props }, ref) =>
2639
2763
  ) });
2640
2764
  });
2641
2765
  ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName;
2642
- var ContextMenuContent = React10.forwardRef(({ className, ...props }, ref) => {
2766
+ var ContextMenuContent = React11.forwardRef(({ className, ...props }, ref) => {
2643
2767
  const portalContainer = usePortalContainer();
2644
2768
  return /* @__PURE__ */ jsx35(ContextMenuPrimitive.Portal, { container: portalContainer, children: /* @__PURE__ */ jsx35(
2645
2769
  ContextMenuPrimitive.Content,
@@ -2651,7 +2775,7 @@ var ContextMenuContent = React10.forwardRef(({ className, ...props }, ref) => {
2651
2775
  ) });
2652
2776
  });
2653
2777
  ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName;
2654
- var ContextMenuItem = React10.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx35(
2778
+ var ContextMenuItem = React11.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx35(
2655
2779
  ContextMenuPrimitive.Item,
2656
2780
  {
2657
2781
  ref,
@@ -2665,7 +2789,7 @@ var ContextMenuItem = React10.forwardRef(({ className, inset, ...props }, ref) =
2665
2789
  }
2666
2790
  ));
2667
2791
  ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName;
2668
- var ContextMenuCheckboxItem = React10.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ jsxs14(
2792
+ var ContextMenuCheckboxItem = React11.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ jsxs14(
2669
2793
  ContextMenuPrimitive.CheckboxItem,
2670
2794
  {
2671
2795
  ref,
@@ -2683,7 +2807,7 @@ var ContextMenuCheckboxItem = React10.forwardRef(({ className, children, checked
2683
2807
  }
2684
2808
  ));
2685
2809
  ContextMenuCheckboxItem.displayName = ContextMenuPrimitive.CheckboxItem.displayName;
2686
- var ContextMenuRadioItem = React10.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs14(
2810
+ var ContextMenuRadioItem = React11.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs14(
2687
2811
  ContextMenuPrimitive.RadioItem,
2688
2812
  {
2689
2813
  ref,
@@ -2700,7 +2824,7 @@ var ContextMenuRadioItem = React10.forwardRef(({ className, children, ...props }
2700
2824
  }
2701
2825
  ));
2702
2826
  ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName;
2703
- var ContextMenuLabel = React10.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx35(
2827
+ var ContextMenuLabel = React11.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx35(
2704
2828
  ContextMenuPrimitive.Label,
2705
2829
  {
2706
2830
  ref,
@@ -2713,7 +2837,7 @@ var ContextMenuLabel = React10.forwardRef(({ className, inset, ...props }, ref)
2713
2837
  }
2714
2838
  ));
2715
2839
  ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName;
2716
- var ContextMenuSeparator = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx35(
2840
+ var ContextMenuSeparator = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx35(
2717
2841
  ContextMenuPrimitive.Separator,
2718
2842
  {
2719
2843
  ref,
@@ -2786,7 +2910,7 @@ var ResizableHandle = ({
2786
2910
  // src/components/tree-view.tsx
2787
2911
  import { useVirtualizer } from "@tanstack/react-virtual";
2788
2912
  import { ChevronRight as ChevronRight3 } from "lucide-react";
2789
- import * as React11 from "react";
2913
+ import * as React12 from "react";
2790
2914
  import { jsx as jsx37, jsxs as jsxs16 } from "react/jsx-runtime";
2791
2915
  var ROW_HEIGHT = 28;
2792
2916
  var INDENT_PER_LEVEL = 12;
@@ -2828,19 +2952,28 @@ function flatten(nodes, expanded) {
2828
2952
  return rows;
2829
2953
  }
2830
2954
  function useControllable(controlled, fallback) {
2831
- const [uncontrolled, setUncontrolled] = React11.useState(fallback);
2955
+ const [uncontrolled, setUncontrolled] = React12.useState(fallback);
2832
2956
  return [
2833
2957
  controlled === void 0 ? uncontrolled : controlled,
2834
2958
  setUncontrolled
2835
2959
  ];
2836
2960
  }
2837
- var TreeView = React11.forwardRef(
2961
+ function treeSelectionMode(modifiers) {
2962
+ if (modifiers.shiftKey) return "extend";
2963
+ if (modifiers.metaKey || modifiers.ctrlKey) return "toggle";
2964
+ return "replace";
2965
+ }
2966
+ function isRowSelected(id, primary, selectedIds) {
2967
+ return selectedIds === void 0 ? primary === id : selectedIds.includes(id);
2968
+ }
2969
+ var TreeView = React12.forwardRef(
2838
2970
  ({
2839
2971
  nodes,
2840
2972
  expandedIds,
2841
2973
  defaultExpandedIds,
2842
2974
  onExpandedChange,
2843
2975
  selectedId,
2976
+ selectedIds,
2844
2977
  defaultSelectedId,
2845
2978
  onSelectedChange,
2846
2979
  className,
@@ -2849,8 +2982,8 @@ var TreeView = React11.forwardRef(
2849
2982
  "aria-describedby": ariaDescribedBy,
2850
2983
  ...props
2851
2984
  }, forwardedRef) => {
2852
- const scrollRef = React11.useRef(null);
2853
- const attachScroll = React11.useCallback(
2985
+ const scrollRef = React12.useRef(null);
2986
+ const attachScroll = React12.useCallback(
2854
2987
  (node) => {
2855
2988
  scrollRef.current = node;
2856
2989
  if (typeof forwardedRef === "function") forwardedRef(node);
@@ -2864,7 +2997,7 @@ var TreeView = React11.forwardRef(
2864
2997
  expandedIds === void 0 ? void 0 : [...expandedIds],
2865
2998
  [...defaultExpandedIds ?? []]
2866
2999
  );
2867
- const expanded = React11.useMemo(
3000
+ const expanded = React12.useMemo(
2868
3001
  () => new Set(expandedIds ?? expandedState),
2869
3002
  [expandedIds, expandedState]
2870
3003
  );
@@ -2872,11 +3005,11 @@ var TreeView = React11.forwardRef(
2872
3005
  selectedId === void 0 ? void 0 : selectedId,
2873
3006
  defaultSelectedId ?? null
2874
3007
  );
2875
- const rows = React11.useMemo(
3008
+ const rows = React12.useMemo(
2876
3009
  () => flatten(nodes, expanded),
2877
3010
  [nodes, expanded]
2878
3011
  );
2879
- const [activeId, setActiveId] = React11.useState(null);
3012
+ const [activeId, setActiveId] = React12.useState(null);
2880
3013
  const activeIndex = Math.max(
2881
3014
  0,
2882
3015
  rows.findIndex((row) => row.node.id === (activeId ?? selected))
@@ -2898,9 +3031,9 @@ var TreeView = React11.forwardRef(
2898
3031
  else next.delete(id);
2899
3032
  commitExpanded(next);
2900
3033
  };
2901
- const choose = (id) => {
3034
+ const choose = (id, mode = "replace") => {
2902
3035
  if (selectedId === void 0) setSelected(id);
2903
- onSelectedChange?.(id);
3036
+ onSelectedChange?.(id, mode);
2904
3037
  };
2905
3038
  const focusRow = (index) => {
2906
3039
  const row = rows[index];
@@ -2920,11 +3053,12 @@ var TreeView = React11.forwardRef(
2920
3053
  }
2921
3054
  return from;
2922
3055
  };
2923
- const typeahead = React11.useRef({ query: "", at: 0 });
3056
+ const typeahead = React12.useRef({ query: "", at: 0 });
2924
3057
  const onKeyDown = (event) => {
2925
3058
  const index = activeIndex;
2926
3059
  const row = rows[index];
2927
3060
  if (row === void 0) return;
3061
+ if (event.altKey) return;
2928
3062
  switch (event.key) {
2929
3063
  case "ArrowDown":
2930
3064
  event.preventDefault();
@@ -2979,7 +3113,9 @@ var TreeView = React11.forwardRef(
2979
3113
  case "Enter":
2980
3114
  case " ":
2981
3115
  event.preventDefault();
2982
- if (row.node.disabled !== true) choose(row.node.id);
3116
+ if (row.node.disabled !== true) {
3117
+ choose(row.node.id, treeSelectionMode(event));
3118
+ }
2983
3119
  return;
2984
3120
  case "*": {
2985
3121
  event.preventDefault();
@@ -3027,6 +3163,7 @@ var TreeView = React11.forwardRef(
3027
3163
  "div",
3028
3164
  {
3029
3165
  role: "tree",
3166
+ "aria-multiselectable": selectedIds === void 0 ? void 0 : true,
3030
3167
  "aria-label": ariaLabel,
3031
3168
  "aria-labelledby": ariaLabelledBy,
3032
3169
  "aria-describedby": ariaDescribedBy,
@@ -3035,7 +3172,12 @@ var TreeView = React11.forwardRef(
3035
3172
  children: virtualItems.map((item) => {
3036
3173
  const row = rows[item.index];
3037
3174
  if (row === void 0) return null;
3038
- const isSelected = selected === row.node.id;
3175
+ const isSelected = isRowSelected(
3176
+ row.node.id,
3177
+ selected,
3178
+ selectedIds
3179
+ );
3180
+ const isPrimary = selected === row.node.id;
3039
3181
  return /* @__PURE__ */ jsxs16(
3040
3182
  "div",
3041
3183
  {
@@ -3049,16 +3191,21 @@ var TreeView = React11.forwardRef(
3049
3191
  "aria-disabled": row.node.disabled === true ? true : void 0,
3050
3192
  tabIndex: item.index === tabStopIndex ? 0 : -1,
3051
3193
  onFocus: () => setActiveId(row.node.id),
3052
- onClick: () => {
3194
+ onClick: (event) => {
3053
3195
  if (row.node.disabled === true) return;
3054
3196
  setActiveId(row.node.id);
3055
- choose(row.node.id);
3197
+ choose(row.node.id, treeSelectionMode(event));
3056
3198
  },
3057
3199
  className: cn(
3058
3200
  "absolute left-0 flex w-full select-none items-center gap-1 rounded-sm pr-2 text-sm outline-none",
3059
3201
  "focus-visible:ring-1 focus-visible:ring-ring",
3060
3202
  row.node.disabled === true ? "pointer-events-none opacity-50" : "cursor-pointer",
3061
- isSelected ? "bg-muted text-foreground" : "hover:bg-muted/50"
3203
+ isSelected ? "bg-muted text-foreground" : "hover:bg-muted/50",
3204
+ // WEIGHT, not a second colour. Every row here is selected and
3205
+ // an action applies to all of them; what a reader needs is
3206
+ // which one the detail panel describes, and a second colour
3207
+ // would have to mean a second kind of selected.
3208
+ isSelected && isPrimary && "font-medium"
3062
3209
  ),
3063
3210
  style: {
3064
3211
  height: item.size,
@@ -3104,7 +3251,7 @@ TreeView.displayName = "TreeView";
3104
3251
 
3105
3252
  // src/components/slider.tsx
3106
3253
  import * as SliderPrimitive from "@radix-ui/react-slider";
3107
- import * as React12 from "react";
3254
+ import * as React13 from "react";
3108
3255
 
3109
3256
  // src/lib/dev-warn.ts
3110
3257
  var emitted = /* @__PURE__ */ new Set();
@@ -3130,7 +3277,7 @@ function thumbCount(value, defaultValue) {
3130
3277
  function hasAccessibleName(value) {
3131
3278
  return value !== void 0 && value.trim() !== "";
3132
3279
  }
3133
- var Slider = React12.forwardRef(
3280
+ var Slider = React13.forwardRef(
3134
3281
  ({
3135
3282
  className,
3136
3283
  value,
@@ -3141,7 +3288,7 @@ var Slider = React12.forwardRef(
3141
3288
  "aria-labelledby": ariaLabelledBy,
3142
3289
  ...props
3143
3290
  }, ref) => {
3144
- const initialUncontrolledCount = React12.useRef(
3291
+ const initialUncontrolledCount = React13.useRef(
3145
3292
  thumbCount(void 0, defaultValue)
3146
3293
  ).current;
3147
3294
  const count = value?.length ?? initialUncontrolledCount;
@@ -3255,7 +3402,7 @@ var Slider = React12.forwardRef(
3255
3402
  Slider.displayName = SliderPrimitive.Root.displayName;
3256
3403
 
3257
3404
  // src/lib/shortcuts/react.tsx
3258
- import * as React13 from "react";
3405
+ import * as React14 from "react";
3259
3406
 
3260
3407
  // src/lib/shortcuts/key-spec.ts
3261
3408
  function normalizeKey(key) {
@@ -3784,9 +3931,8 @@ var FIELD_KEYS = /* @__PURE__ */ new Set([
3784
3931
 
3785
3932
  // src/lib/shortcuts/react.tsx
3786
3933
  import { jsx as jsx39 } from "react/jsx-runtime";
3787
- var ShortcutContext = React13.createContext(null);
3934
+ var ShortcutContext = React14.createContext(null);
3788
3935
  var ownersByTarget = /* @__PURE__ */ new WeakMap();
3789
- var useIsomorphicLayoutEffect = typeof document === "undefined" ? React13.useEffect : React13.useLayoutEffect;
3790
3936
  function optionsFingerprint(options) {
3791
3937
  return [
3792
3938
  options.isApple ?? "auto",
@@ -3799,13 +3945,13 @@ function ShortcutProvider({
3799
3945
  target,
3800
3946
  ...managerOptions
3801
3947
  }) {
3802
- const parent = React13.useContext(ShortcutContext);
3948
+ const parent = React14.useContext(ShortcutContext);
3803
3949
  const resolvedTarget = target === null ? null : target ?? (typeof document === "undefined" ? null : document);
3804
3950
  const nestedOnSameTarget = parent !== null && parent.target === resolvedTarget;
3805
- const optionsRef = React13.useRef(managerOptions);
3806
- const ownManagers = React13.useRef(/* @__PURE__ */ new WeakMap());
3807
- const ownDetached = React13.useRef(null);
3808
- const detached = React13.useMemo(() => {
3951
+ const optionsRef = React14.useRef(managerOptions);
3952
+ const ownManagers = React14.useRef(/* @__PURE__ */ new WeakMap());
3953
+ const ownDetached = React14.useRef(null);
3954
+ const detached = React14.useMemo(() => {
3809
3955
  if (resolvedTarget === null) {
3810
3956
  ownDetached.current ??= createShortcutManager(optionsRef.current);
3811
3957
  return ownDetached.current;
@@ -3861,7 +4007,7 @@ function ShortcutProvider({
3861
4007
  };
3862
4008
  }, [resolvedTarget, manager]);
3863
4009
  const depth = nestedOnSameTarget && parent ? parent.depth : 0;
3864
- const value = React13.useMemo(
4010
+ const value = React14.useMemo(
3865
4011
  () => ({ manager, depth, target: resolvedTarget, options: fingerprint }),
3866
4012
  [manager, depth, resolvedTarget, fingerprint]
3867
4013
  );
@@ -3870,11 +4016,11 @@ function ShortcutProvider({
3870
4016
  function ShortcutScope({
3871
4017
  children
3872
4018
  }) {
3873
- const parent = React13.useContext(ShortcutContext);
4019
+ const parent = React14.useContext(ShortcutContext);
3874
4020
  if (!parent) {
3875
4021
  throw new Error("ShortcutScope must be rendered inside a ShortcutProvider");
3876
4022
  }
3877
- const value = React13.useMemo(
4023
+ const value = React14.useMemo(
3878
4024
  () => ({
3879
4025
  manager: parent.manager,
3880
4026
  depth: parent.depth + 1,
@@ -3888,13 +4034,13 @@ function ShortcutScope({
3888
4034
  return /* @__PURE__ */ jsx39(ShortcutContext.Provider, { value, children });
3889
4035
  }
3890
4036
  function useShortcuts(bindings, options) {
3891
- const context = React13.useContext(ShortcutContext);
4037
+ const context = React14.useContext(ShortcutContext);
3892
4038
  if (!context) {
3893
4039
  throw new Error("useShortcuts must be called inside a ShortcutProvider");
3894
4040
  }
3895
4041
  const { manager, depth } = context;
3896
- const registration = React13.useRef(null);
3897
- const latest = React13.useRef({ bindings, options });
4042
+ const registration = React14.useRef(null);
4043
+ const latest = React14.useRef({ bindings, options });
3898
4044
  useIsomorphicLayoutEffect(() => {
3899
4045
  registration.current = manager.register([], {
3900
4046
  name: latest.current.options.name,
@@ -3917,7 +4063,7 @@ function useShortcuts(bindings, options) {
3917
4063
  });
3918
4064
  }
3919
4065
  function useShortcutManager() {
3920
- const context = React13.useContext(ShortcutContext);
4066
+ const context = React14.useContext(ShortcutContext);
3921
4067
  if (!context) {
3922
4068
  throw new Error(
3923
4069
  "useShortcutManager must be called inside a ShortcutProvider"
@@ -3927,7 +4073,7 @@ function useShortcutManager() {
3927
4073
  }
3928
4074
  function useActiveShortcuts() {
3929
4075
  const manager = useShortcutManager();
3930
- return React13.useSyncExternalStore(
4076
+ return React14.useSyncExternalStore(
3931
4077
  manager.subscribe,
3932
4078
  manager.activeBindings,
3933
4079
  manager.activeBindings
@@ -4054,29 +4200,12 @@ function FormSection({
4054
4200
  ),
4055
4201
  description ? /* @__PURE__ */ jsx41("p", { className: "text-sm text-muted-foreground", children: description }) : null
4056
4202
  ] }),
4057
- /* @__PURE__ */ jsx41(Card, { className: "overflow-hidden", children: /* @__PURE__ */ jsx41("div", { className: "divide-y divide-foreground/10 px-6", children }) })
4203
+ /* @__PURE__ */ jsx41(Card, { className: "overflow-hidden", children: /* @__PURE__ */ jsx41("div", { className: "nx-form-section-rows divide-y divide-foreground/10 px-6", children }) })
4058
4204
  ] });
4059
4205
  }
4060
4206
 
4061
- // src/components/form-layout.tsx
4062
- import { cva as cva14 } from "class-variance-authority";
4207
+ // src/components/form-actions.tsx
4063
4208
  import { jsx as jsx42, jsxs as jsxs20 } from "react/jsx-runtime";
4064
- var measure = cva14("mx-auto w-full px-6 py-8", {
4065
- variants: {
4066
- width: {
4067
- form: "max-w-[56rem]",
4068
- wide: "max-w-[72rem]"
4069
- }
4070
- },
4071
- defaultVariants: { width: "form" }
4072
- });
4073
- function FormLayout({
4074
- width = "form",
4075
- className,
4076
- children
4077
- }) {
4078
- return /* @__PURE__ */ jsx42("div", { className: cn(measure({ width }), className), children });
4079
- }
4080
4209
  function FormActions({ dirty, className, children }) {
4081
4210
  return /* @__PURE__ */ jsxs20(
4082
4211
  "div",
@@ -4099,6 +4228,127 @@ function FormActions({ dirty, className, children }) {
4099
4228
  }
4100
4229
  );
4101
4230
  }
4231
+
4232
+ // src/components/page-shell.tsx
4233
+ import {
4234
+ createContext as createContext5,
4235
+ forwardRef as forwardRef28,
4236
+ useCallback as useCallback2,
4237
+ useContext as useContext5,
4238
+ useEffect as useEffect4,
4239
+ useRef as useRef5
4240
+ } from "react";
4241
+ import { jsx as jsx43 } from "react/jsx-runtime";
4242
+ function hasBareTextChild(shell) {
4243
+ return Array.from(shell.childNodes).some(
4244
+ (node) => node.nodeType === Node.TEXT_NODE && node.textContent?.trim() !== ""
4245
+ );
4246
+ }
4247
+ var InsideShell = createContext5(false);
4248
+ function hasDisplayContentsChild(shell) {
4249
+ return Array.from(shell.children).some(
4250
+ (child) => getComputedStyle(child).display === "contents"
4251
+ );
4252
+ }
4253
+ var SHELL_MEASURE = {
4254
+ form: "var(--nx-measure-form)",
4255
+ wide: "var(--nx-measure-wide)",
4256
+ full: "100%"
4257
+ };
4258
+ var PageShell = forwardRef28(
4259
+ ({ width = "form", className, children, style, ...props }, ref) => {
4260
+ const shellRef = useRef5(null);
4261
+ const attachRef = useCallback2(
4262
+ (node) => {
4263
+ shellRef.current = node;
4264
+ if (typeof ref === "function") return ref(node);
4265
+ if (ref) ref.current = node;
4266
+ },
4267
+ [ref]
4268
+ );
4269
+ const nested = useContext5(InsideShell);
4270
+ useEffect4(() => {
4271
+ if (!isDevelopmentRuntime()) return;
4272
+ const shell = shellRef.current;
4273
+ if (!shell) return;
4274
+ devWarnOnce(
4275
+ !nested,
4276
+ "PageShell: an ancestor already renders a PageShell. The two grids each add a pair of gutter tracks, so this page's content is inset twice. Remove this shell, or move the outer one."
4277
+ );
4278
+ const check = () => {
4279
+ devWarnOnce(
4280
+ !hasBareTextChild(shell),
4281
+ "PageShell: a direct child renders as bare text. CSS Grid wraps it in an anonymous grid item, which no selector can reach, so it is placed in the gutter rather than the content column and sits outside the page's measure. Wrap it in an element \u2014 a `<p>`, or the section it belongs to."
4282
+ );
4283
+ devWarnOnce(
4284
+ !hasDisplayContentsChild(shell),
4285
+ "PageShell: a direct child has `display: contents`, so it generates no grid item and the content-column rule has nothing to apply to. Its own children are promoted into this grid, match no selector, and are placed from the gutter onward. Give the child a box, or move `display: contents` inside it."
4286
+ );
4287
+ };
4288
+ check();
4289
+ const observer = new MutationObserver(check);
4290
+ observer.observe(shell, { childList: true });
4291
+ return () => {
4292
+ observer.disconnect();
4293
+ };
4294
+ });
4295
+ const shellStyle = {
4296
+ ...style,
4297
+ "--nx-shell-measure": SHELL_MEASURE[width]
4298
+ };
4299
+ return /* @__PURE__ */ jsx43(InsideShell.Provider, { value: true, children: /* @__PURE__ */ jsx43(
4300
+ "div",
4301
+ {
4302
+ ref: attachRef,
4303
+ "data-slot": "page-shell",
4304
+ style: shellStyle,
4305
+ className: cn("nx-page-shell py-8", className),
4306
+ ...props,
4307
+ children
4308
+ }
4309
+ ) });
4310
+ }
4311
+ );
4312
+ PageShell.displayName = "PageShell";
4313
+ var Bleed = forwardRef28(
4314
+ ({ className, children, ...props }, ref) => /* @__PURE__ */ jsx43(
4315
+ "div",
4316
+ {
4317
+ ref,
4318
+ "data-slot": "bleed",
4319
+ className: cn("nx-bleed", className),
4320
+ ...props,
4321
+ children
4322
+ }
4323
+ )
4324
+ );
4325
+ Bleed.displayName = "Bleed";
4326
+
4327
+ // src/components/page-header.tsx
4328
+ import { forwardRef as forwardRef29 } from "react";
4329
+ import { jsx as jsx44, jsxs as jsxs21 } from "react/jsx-runtime";
4330
+ var PageHeader = forwardRef29(
4331
+ ({ title, description, breadcrumbs, actions, className, ...props }, ref) => /* @__PURE__ */ jsxs21(
4332
+ "header",
4333
+ {
4334
+ ref,
4335
+ "data-slot": "page-header",
4336
+ className: cn("mb-8", className),
4337
+ ...props,
4338
+ children: [
4339
+ breadcrumbs ? /* @__PURE__ */ jsx44("div", { className: "mb-6", children: breadcrumbs }) : null,
4340
+ /* @__PURE__ */ jsxs21("div", { className: "flex flex-col justify-between gap-4 sm:flex-row sm:items-center", children: [
4341
+ /* @__PURE__ */ jsxs21("div", { className: "space-y-1", children: [
4342
+ /* @__PURE__ */ jsx44("h1", { className: "text-xl font-semibold tracking-tight", children: title }),
4343
+ description ? /* @__PURE__ */ jsx44("p", { className: "mt-1 text-sm font-normal text-muted-foreground", children: description }) : null
4344
+ ] }),
4345
+ actions ? /* @__PURE__ */ jsx44("div", { className: "flex items-center gap-3", children: actions }) : null
4346
+ ] })
4347
+ ]
4348
+ }
4349
+ )
4350
+ );
4351
+ PageHeader.displayName = "PageHeader";
4102
4352
  export {
4103
4353
  Accordion,
4104
4354
  AccordionContent,
@@ -4122,6 +4372,7 @@ export {
4122
4372
  AvatarFallback,
4123
4373
  AvatarImage,
4124
4374
  Badge,
4375
+ Bleed,
4125
4376
  Button,
4126
4377
  Card,
4127
4378
  CardAction,
@@ -4186,11 +4437,12 @@ export {
4186
4437
  FieldShell,
4187
4438
  FormActions,
4188
4439
  FormLabelWithTooltip,
4189
- FormLayout,
4190
4440
  FormSection,
4191
4441
  Grid,
4192
4442
  Input,
4193
4443
  Label,
4444
+ PageHeader,
4445
+ PageShell,
4194
4446
  Popover,
4195
4447
  PopoverAnchor,
4196
4448
  PopoverContent,
@@ -4202,6 +4454,7 @@ export {
4202
4454
  ResizableHandle,
4203
4455
  ResizablePanel,
4204
4456
  ResizablePanelGroup,
4457
+ SHELL_MEASURE,
4205
4458
  Select,
4206
4459
  SelectContent,
4207
4460
  SelectGroup,
@@ -4260,8 +4513,10 @@ export {
4260
4513
  badgeVariants,
4261
4514
  buttonVariants,
4262
4515
  cardVariants,
4516
+ chordMatches,
4263
4517
  commandDefaultFilter,
4264
4518
  createShortcutManager,
4519
+ detectApplePlatform,
4265
4520
  dialogContentVariants,
4266
4521
  inputVariants,
4267
4522
  parseKeys,
@@ -4271,6 +4526,7 @@ export {
4271
4526
  spinnerVariants,
4272
4527
  toast,
4273
4528
  useActiveShortcuts,
4529
+ useCommandHighlight,
4274
4530
  usePortalContainer,
4275
4531
  useShortcutManager,
4276
4532
  useShortcuts