@botiverse/raft-ui 0.36.0 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,57 @@
1
+ ## [0.37.0](https://github.com/botiverse/raft-design/compare/v0.36.1...v0.37.0) (2026-10-26)
2
+
3
+ ### Design System
4
+
5
+ - **Edges fade:** content that goes on past an edge fades with a mask, not a colour gradient, so the fade works on any surface (a hovered, selected or located row, a card). Two lengths: 24px at the edge of a scrolling strip, 48px over cut-off content. `MessageListEndFade` is the one exception. ([#816](https://github.com/botiverse/raft-design/pull/816))
6
+ - Docs: https://raft-design.botiverse.build/foundations/spacing/
7
+ - **Dimmed:** deleted, offline and pending items are `opacity-dimmed` (60%); a drag origin stays 40%. Media hover is an `ink-8` overlay, not a brightness filter. ([#816](https://github.com/botiverse/raft-design/pull/816))
8
+ - Docs: https://raft-design.botiverse.build/foundations/states/
9
+ - **Tooltip:** a disabled control's reason under its name is a second line in `foreground-on-hud-muted`, 4px below. ([#813](https://github.com/botiverse/raft-design/pull/813))
10
+ - **Drag and drop:** a panel grip's hit area stays clear of a neighbouring resize seam, with its bar inside it. ([#814](https://github.com/botiverse/raft-design/pull/814))
11
+ - **Raft Chat:** the history paywall where a plan cuts a conversation's history. ([#757](https://github.com/botiverse/raft-design/pull/757))
12
+
13
+ ### Added
14
+
15
+ - Utilities `mask-fade-t`, `mask-fade-b` (48px), `mask-fade-t-edge`, `mask-fade-b-edge` (24px) and `mask-fade-x` (24px on the side named by `data-overflow="start|end|both"`); tokens `fade-edge`, `fade-content`. ([#816](https://github.com/botiverse/raft-design/pull/816))
16
+ - Token `layer-veil` (the page at 80%) for busy and loading covers, `bg-layer-veil`; `opacity-dimmed` (0.6). Both registered with `cn`'s tailwind-merge. ([#816](https://github.com/botiverse/raft-design/pull/816))
17
+
18
+ ### Changed
19
+
20
+ - **Tabs**: the overflow fade is `mask-fade-x`, 24px (was 32px). ([#816](https://github.com/botiverse/raft-design/pull/816))
21
+ - **MessageForwardedBundle**: the fold fades with `mask-fade-b` (48px, was a 40px card-colour gradient; a tight fold 24px, was 20px). ([#816](https://github.com/botiverse/raft-design/pull/816))
22
+ - Affects: overrides that targeted the fold's `::after` must drop it.
23
+ - **Composer** upload and failed covers, and the pending **MessageImageGallery** image, use `layer-veil` (were `layer-card/80`, `layer-card/90` and `opacity-70`); the pending spinner uses its default colour. ([#816](https://github.com/botiverse/raft-design/pull/816))
24
+ - **Lightbox** thumbnails dim with `layer-backdrop-nested` (40%, was `black/45`); hover 28% unchanged. ([#816](https://github.com/botiverse/raft-design/pull/816))
25
+ - Task chip and task-card busy states, and the deleted delegated avatar, use `opacity-dimmed` (same 60%). ([#816](https://github.com/botiverse/raft-design/pull/816))
26
+
27
+ ## [0.36.1](https://github.com/botiverse/raft-design/compare/v0.36.0...v0.36.1) (2026-10-26)
28
+
29
+ ### Design System
30
+
31
+ - **Drag and drop:** a panel of live content (a side section) is lifted by its grip only, so its head keeps its clicks (opening a thread, jumping to the channel). The grip shows while the head is hovered or the grip has focus, always on a device that can't hover, and its hit area is at least 24px tall and wider than the bar. ([#809](https://github.com/botiverse/raft-design/pull/809))
32
+ - Docs: https://raft-design.botiverse.build/patterns/drag-and-drop/
33
+ - **Motion:** the app's columns change without animation: collapsing or expanding the sidebar, and Focus Mode in or out, by a button or by dragging past the end. ([#811](https://github.com/botiverse/raft-design/pull/811))
34
+ - Docs: https://raft-design.botiverse.build/foundations/motion/
35
+ - **Field:** an action at the end of a field's label row (Forgot Password) comes after the control in the markup, so Tab and a screen reader reach the field first. ([#808](https://github.com/botiverse/raft-design/pull/808))
36
+ - Docs: https://raft-design.botiverse.build/components/field/
37
+
38
+ ### Fixed
39
+
40
+ - **AppShell**: the sidebar collapses and expands at once; the main area no longer slides and the sidebar no longer fades. `data-sliding` is gone. ([#811](https://github.com/botiverse/raft-design/pull/811))
41
+ - Why: Focus Mode already switched at once while the sidebar slid; a whole column sliding flickers.
42
+ - Affects: no transition on `AppShellMainSlot` (`left`) or `AppShellSidebarSlot` (opacity); no z-index on the main slot while collapsing.
43
+ - Docs: https://raft-design.botiverse.build/components/app-shell/
44
+ - **Composer**: its buttons sit 6px apart (`gap-embedded-inset`), the same as their distance from the frame. ([#810](https://github.com/botiverse/raft-design/pull/810))
45
+ - Why: the buttons were 8px apart but 6px from the edge, against Shape › Inside a frame.
46
+ - Affects: gap in the toolbar, actions and meta 8px → 6px.
47
+ - Docs: https://raft-design.botiverse.build/components/composer/
48
+ - **DragRefusalHint**: the chip stays inside the window and off the lifted copy: left of the pointer and the copy at the right edge, above the pointer at the bottom edge, 8px inside on every side. ([#807](https://github.com/botiverse/raft-design/pull/807))
49
+ - Why: near the window's edge the chip was drawn outside it, or under the copy when flipped.
50
+ - Docs: https://raft-design.botiverse.build/components/drag/
51
+ - **useDragSensors**: Escape during a drag, however it started, only cancels the drag; the keydown is stopped in the capture phase, so no app Escape handler (closing a panel, a dialog, a popover) hears it. ([#807](https://github.com/botiverse/raft-design/pull/807))
52
+ - Why: Escape cancelled the drag and also closed the panel around it.
53
+ - Docs: https://raft-design.botiverse.build/components/drag/
54
+
1
55
  ## [0.36.0](https://github.com/botiverse/raft-design/compare/v0.35.1...v0.36.0) (2026-10-26)
2
56
 
3
57
  ### Design System
package/dist/cn.mjs CHANGED
@@ -60,6 +60,14 @@ const twMerge = extendTailwindMerge({ extend: {
60
60
  "control"
61
61
  ] }],
62
62
  "font-weight": [{ font: ["field", "card-title"] }],
63
+ "mask-image": [{ "mask-fade": [
64
+ "t",
65
+ "t-edge",
66
+ "b",
67
+ "b-edge",
68
+ "x"
69
+ ] }],
70
+ opacity: [{ opacity: ["dimmed"] }],
63
71
  "bg-image": [
64
72
  { overlay: [
65
73
  "subtle",
@@ -27,8 +27,9 @@
27
27
  selected sticker, with their own value so no theme grays them.
28
28
  Washes: ink-{alpha%} (2..40) — neutral overlays, hairlines, hover fills.
29
29
  Opacity rule: layer tokens are opaque in every theme, except the two
30
- backdrops (overlays by contract); among fills only the hover tiers
31
- (fill-subtle, fill-medium, fill-frame-medium) are translucent.
30
+ backdrops and the veil (overlays by contract: a scrim under a modal
31
+ surface, a cover over something busy in place); among fills only the
32
+ hover tiers (fill-subtle, fill-medium, fill-frame-medium) are translucent.
32
33
  Interaction fills: transparent resting → ink-N directly; solid family fill →
33
34
  the family's -hover/-active pair; neutral or tinted surface → step up its ladder.
34
35
  Escape hatches: ink ramp for one-off washes; inline oklch() literals for
@@ -36,7 +37,8 @@
36
37
 
37
38
  /* Raw brand ramps (the website palette) — variables only, no utility registration. Consumed by the
38
39
  Tailwind aliases in styles.css, the brand ramps, and state families below. Then the
39
- mode-independent motion values (design/foundations/motion.md). */
40
+ mode-independent values: the fade lengths (design/foundations/spacing.md), the dimmed
41
+ opacity (design/foundations/states.md) and motion (design/foundations/motion.md). */
40
42
  :where(:root) {
41
43
  --palette-yellow-50: oklch(0.984 0.017 84.56);
42
44
  --palette-yellow-100: oklch(0.975 0.027 85.64);
@@ -137,6 +139,9 @@
137
139
  --palette-cream-800: oklch(0.396 0.082 95);
138
140
  --palette-cream-900: oklch(0.276 0.057 95.1);
139
141
  --palette-cream-950: oklch(0.19 0.039 94.7);
142
+ --fade-edge: 24px;
143
+ --fade-content: 48px;
144
+ --opacity-dimmed: 0.6;
140
145
  --motion-drag-settle-duration: 160ms;
141
146
  --motion-drag-settle-easing: cubic-bezier(0.25, 1, 0.5, 1);
142
147
  }
@@ -168,6 +173,11 @@
168
173
  --layer-popover: oklch(1 0 0);
169
174
  --layer-backdrop: oklch(0 0 0 / 0.651);
170
175
  --layer-backdrop-nested: oklch(0 0 0 / 0.4);
176
+ /* The cover over something busy or loading in place: an image uploading or
177
+ that failed, a preview being prepared. The page at 80%, so what is under it
178
+ shows through, dimmed, and a default-colour Spinner on it reads in both
179
+ modes. Translucent by contract, like the two backdrops. */
180
+ --layer-veil: oklch(1 0 0 / 0.8);
171
181
  --layer-card: oklch(1 0 0);
172
182
  --layer-hud: oklch(0.2634 0.0093 294.92);
173
183
  --layer-frame: oklch(0.8829 0.1623 91.9);
@@ -381,6 +391,7 @@
381
391
  --layer-popover: oklch(0.2802 0.0036 106.63);
382
392
  --layer-backdrop: oklch(0 0 0 / 0.6);
383
393
  --layer-backdrop-nested: oklch(0 0 0 / 0.4);
394
+ --layer-veil: oklch(0.2213 0.0038 106.71 / 0.8);
384
395
  --layer-card: oklch(0.2596 0.0037 106.65);
385
396
  --layer-hud: oklch(0.2802 0.0036 106.63);
386
397
  --layer-frame: oklch(0.1582 0.0041 106.86);
@@ -465,6 +476,7 @@
465
476
  --layer-popover: oklch(0.2802 0.0036 106.63);
466
477
  --layer-backdrop: oklch(0 0 0 / 0.6);
467
478
  --layer-backdrop-nested: oklch(0 0 0 / 0.4);
479
+ --layer-veil: oklch(0.2213 0.0038 106.71 / 0.8);
468
480
  --layer-card: oklch(0.2596 0.0037 106.65);
469
481
  --layer-hud: oklch(0.2802 0.0036 106.63);
470
482
  --layer-frame: oklch(0.1582 0.0041 106.86);
@@ -2541,6 +2541,42 @@ function MobileNavLabel({ className, render, ...props }) {
2541
2541
  });
2542
2542
  }
2543
2543
  //#endregion
2544
+ //#region src/lib/drag-escape.ts
2545
+ /** The drags in progress in each window, each with its own cancel. */
2546
+ const holds$1 = /* @__PURE__ */ new WeakMap();
2547
+ function onKeydown(event) {
2548
+ if (event.key !== "Escape" && event.code !== "Escape") return;
2549
+ const drags = holds$1.get(this);
2550
+ if (!drags || drags.size === 0) return;
2551
+ event.preventDefault();
2552
+ event.stopImmediatePropagation();
2553
+ for (const cancel of drags.values()) cancel(event);
2554
+ }
2555
+ function guard(win) {
2556
+ let drags = holds$1.get(win);
2557
+ if (!drags) {
2558
+ drags = /* @__PURE__ */ new Map();
2559
+ holds$1.set(win, drags);
2560
+ win.addEventListener("keydown", onKeydown, { capture: true });
2561
+ }
2562
+ return drags;
2563
+ }
2564
+ if (typeof window !== "undefined") guard(window);
2565
+ /** A sensor's drag has started: Escape now cancels it, and only it. */
2566
+ function holdEscape(win, sensor, cancel) {
2567
+ if (!win) return;
2568
+ guard(win).set(sensor, cancel);
2569
+ }
2570
+ /** The sensor's drag is over: Escape behaves as it does everywhere else again. */
2571
+ function releaseEscape(win, sensor) {
2572
+ if (!win) return;
2573
+ holds$1.get(win)?.delete(sensor);
2574
+ }
2575
+ /** The window an event happened in. */
2576
+ function eventWindow(event) {
2577
+ return (event?.target)?.ownerDocument?.defaultView ?? (typeof window === "undefined" ? null : window);
2578
+ }
2579
+ //#endregion
2544
2580
  //#region src/lib/touch-drag.ts
2545
2581
  /**
2546
2582
  * How a finger picks up something that can be dragged
@@ -2577,6 +2613,18 @@ function touchPoint(event) {
2577
2613
  function farther(from, to, limit) {
2578
2614
  return Math.hypot(to.x - from.x, to.y - from.y) > limit;
2579
2615
  }
2616
+ /**
2617
+ * Once a pointer sensor's drag has started, Escape cancels it and reaches
2618
+ * nothing else (`drag-escape.ts`): no app handler on the window, the document
2619
+ * or an element hears it.
2620
+ */
2621
+ function holdEscapeOnceActive(sensor) {
2622
+ const self = sensor;
2623
+ if (self.activated) holdEscape(eventWindow(self.props.event), sensor, () => self.handleCancel());
2624
+ }
2625
+ function releaseEscapeOf(sensor) {
2626
+ releaseEscape(eventWindow(sensor.props.event), sensor);
2627
+ }
2580
2628
  var HoldThenMoveTouchSensorImpl = class extends TouchSensor {
2581
2629
  static activators = TouchSensor.activators;
2582
2630
  static setup() {
@@ -2595,6 +2643,10 @@ var HoldThenMoveTouchSensorImpl = class extends TouchSensor {
2595
2643
  };
2596
2644
  holds.set(this, state);
2597
2645
  }
2646
+ handleStart() {
2647
+ super.handleStart();
2648
+ holdEscapeOnceActive(this);
2649
+ }
2598
2650
  handleMove(event) {
2599
2651
  const self = this;
2600
2652
  const state = holds.get(this);
@@ -2622,22 +2674,33 @@ var HoldThenMoveTouchSensorImpl = class extends TouchSensor {
2622
2674
  const state = holds.get(this);
2623
2675
  if (state) clearTimeout(state.timer);
2624
2676
  holds.delete(this);
2677
+ releaseEscapeOf(this);
2625
2678
  super.detach();
2626
2679
  }
2627
2680
  };
2628
2681
  /** A TouchSensor that starts a drag on the first move after a still hold (see above). */
2629
2682
  const HoldThenMoveTouchSensor = HoldThenMoveTouchSensorImpl;
2630
- /**
2631
- * dnd-kit's PointerSensor for a mouse and a pen only: a finger's pointerdown is
2632
- * left to `HoldThenMoveTouchSensor`.
2633
- */
2634
- var MouseOnlyPointerSensor = class extends PointerSensor {
2683
+ var MouseOnlyPointerSensorImpl = class extends PointerSensor {
2635
2684
  static activators = [{
2636
2685
  eventName: "onPointerDown",
2637
2686
  handler: (event, options) => event.nativeEvent.pointerType === "touch" ? false : PointerSensor.activators[0].handler(event, options)
2638
2687
  }];
2688
+ handleStart() {
2689
+ super.handleStart();
2690
+ holdEscapeOnceActive(this);
2691
+ }
2692
+ detach() {
2693
+ releaseEscapeOf(this);
2694
+ super.detach();
2695
+ }
2639
2696
  };
2640
2697
  /**
2698
+ * dnd-kit's PointerSensor for a mouse and a pen only: a finger's pointerdown is
2699
+ * left to `HoldThenMoveTouchSensor`. While it holds a drag, Escape puts the
2700
+ * item back and reaches nothing else.
2701
+ */
2702
+ const MouseOnlyPointerSensor = MouseOnlyPointerSensorImpl;
2703
+ /**
2641
2704
  * `useSensor(HoldThenMoveTouchSensor, …)` with the activation the sensor relies
2642
2705
  * on: no constraint dnd-kit would act on by itself, so only a move after the
2643
2706
  * hold starts the drag.
@@ -4182,7 +4245,7 @@ const messageItem = tv$1({
4182
4245
  "size-[19px] group-data-[size=md]/avatar:size-[19px]",
4183
4246
  "rounded-sticker outline-none",
4184
4247
  "bg-layer-canvas bg-[linear-gradient(var(--mi-row-fill,transparent),var(--mi-row-fill,transparent))]",
4185
- "data-deleted:*:data-[slot=avatar]:grayscale data-deleted:*:data-[slot=avatar]:opacity-60",
4248
+ "data-deleted:*:data-[slot=avatar]:grayscale data-deleted:*:data-[slot=avatar]:opacity-dimmed",
4186
4249
  "shadow-[0_0_0_1.5px_var(--mi-row-fill,transparent),0_0_0_1.5px_var(--layer-canvas)]",
4187
4250
  "dark:shadow-[0_0_0_3px_var(--mi-row-fill,transparent),0_0_0_3px_var(--layer-canvas)]"
4188
4251
  ],
@@ -6181,6 +6244,24 @@ const DRAG_KEYBOARD_CODES = {
6181
6244
  cancel: ["Escape"],
6182
6245
  end: ["Space"]
6183
6246
  };
6247
+ var DragKeyboardSensorImpl = class extends KeyboardSensor {
6248
+ static activators = KeyboardSensor.activators;
6249
+ attach() {
6250
+ super.attach();
6251
+ holdEscape(eventWindow(this.props.event), this, (event) => this.handleCancel(event));
6252
+ }
6253
+ detach() {
6254
+ releaseEscape(eventWindow(this.props.event), this);
6255
+ super.detach();
6256
+ }
6257
+ };
6258
+ /**
6259
+ * dnd-kit's KeyboardSensor, but Escape during the drag puts the item back and
6260
+ * reaches nothing else: no dialog, panel or popover that closes on Escape
6261
+ * hears it (`drag-escape.ts`). The pointer sensors of `useDragSensors` do the
6262
+ * same.
6263
+ */
6264
+ const DragKeyboardSensor = DragKeyboardSensorImpl;
6184
6265
  const NO_TOUCH = {};
6185
6266
  /**
6186
6267
  * The one sensor set for everything that can be dragged
@@ -6191,6 +6272,11 @@ const NO_TOUCH = {};
6191
6272
  * - the keyboard: Space picks up, the arrows move, Space drops, Escape puts
6192
6273
  * back. Enter never starts a drag.
6193
6274
  *
6275
+ * However the drag started, Escape during it only puts the item back: it is
6276
+ * taken in the capture phase on the window and stopped, so no app handler
6277
+ * (a dialog, a panel, a popover that closes on Escape) hears it. Once the drag
6278
+ * is over, Escape reaches the app again.
6279
+ *
6194
6280
  * Pass the options, or only the keyboard's `coordinateGetter`.
6195
6281
  */
6196
6282
  function useDragSensors(options = {}) {
@@ -6201,7 +6287,7 @@ function useDragSensors(options = {}) {
6201
6287
  }), [coordinateGetter]);
6202
6288
  const mouse = useMouseOnlyPointerSensor(6);
6203
6289
  const finger = useHoldThenMoveTouchSensor(touch);
6204
- const keys = useSensor(KeyboardSensor, keyboardOptions);
6290
+ const keys = useSensor(DragKeyboardSensor, keyboardOptions);
6205
6291
  return useSensors(mouse, finger, keyboard ? keys : null);
6206
6292
  }
6207
6293
  /**
@@ -6323,6 +6409,63 @@ function DropPlaceholder({ opaque = false, className, render, ...props }) {
6323
6409
  }, props, { "data-slot": "drop-placeholder" })
6324
6410
  });
6325
6411
  }
6412
+ /** The chip sits this far right of and below the pointer. */
6413
+ const HINT_OFFSET = {
6414
+ x: 14,
6415
+ y: 16
6416
+ };
6417
+ /** It stays at least this far inside the window on every side. */
6418
+ const HINT_INSET = 8;
6419
+ /** Pushed off the lifted copy, it keeps this far from it. */
6420
+ const HINT_GAP = 6;
6421
+ function overlaps(a, b) {
6422
+ return a.left < b.right && a.right > b.left && a.top < b.bottom && a.bottom > b.top;
6423
+ }
6424
+ function clamp(value, min, max) {
6425
+ return Math.max(min, Math.min(value, Math.max(min, max)));
6426
+ }
6427
+ /**
6428
+ * Where the refusal chip goes (design/patterns/drag-and-drop.md#refused-places):
6429
+ * right of and below the pointer; left of it (and of the copy) where the
6430
+ * window's right edge would cut it, above it where the bottom edge would; off
6431
+ * the lifted copy, which is drawn over it; and always at least 8px inside the
6432
+ * window.
6433
+ */
6434
+ function placeRefusalHint(point, size, lift, viewport) {
6435
+ const { width, height } = size;
6436
+ const maxX = viewport.width - HINT_INSET - width;
6437
+ const maxY = viewport.height - HINT_INSET - height;
6438
+ const fitsX = (x) => x >= HINT_INSET && x <= maxX;
6439
+ const fitsY = (y) => y >= HINT_INSET && y <= maxY;
6440
+ const right = point.x + HINT_OFFSET.x;
6441
+ const onRight = right <= maxX;
6442
+ let x = onRight ? right : Math.min(point.x, lift?.left ?? point.x) - HINT_OFFSET.x - width;
6443
+ const below = point.y + HINT_OFFSET.y;
6444
+ const onBelow = below <= maxY;
6445
+ let y = onBelow ? below : point.y - HINT_OFFSET.y - height;
6446
+ const box = (bx, by) => ({
6447
+ left: bx,
6448
+ top: by,
6449
+ right: bx + width,
6450
+ bottom: by + height
6451
+ });
6452
+ if (lift && overlaps(box(x, y), lift)) {
6453
+ const clearBelow = lift.bottom + HINT_GAP;
6454
+ const clearAbove = lift.top - HINT_GAP - height;
6455
+ const vertical = (onBelow ? [clearBelow, clearAbove] : [clearAbove, clearBelow]).find(fitsY);
6456
+ if (vertical !== void 0) y = vertical;
6457
+ else {
6458
+ const besideRight = lift.right + HINT_OFFSET.x;
6459
+ const besideLeft = lift.left - HINT_OFFSET.x - width;
6460
+ const beside = (onRight ? [besideRight, besideLeft] : [besideLeft, besideRight]).find(fitsX);
6461
+ if (beside !== void 0) x = beside;
6462
+ }
6463
+ }
6464
+ return {
6465
+ x: Math.round(clamp(x, HINT_INSET, maxX)),
6466
+ y: Math.round(clamp(y, HINT_INSET, maxY))
6467
+ };
6468
+ }
6326
6469
  /**
6327
6470
  * Shown while the hand is over a place that refuses the item
6328
6471
  * (design/patterns/drag-and-drop.md#refused-places): the reason, in a HUD
@@ -6330,6 +6473,11 @@ function DropPlaceholder({ opaque = false, className, render, ...props }) {
6330
6473
  * not-allowed cursor. Render it only while the place is refused, inside the
6331
6474
  * `DndContext`, with no placeholder anywhere. Announce the reason as well:
6332
6475
  * the chip is not read out.
6476
+ *
6477
+ * The chip sits right of and below the pointer. Near the window's right edge
6478
+ * it goes left of the pointer and of the lifted copy, near the bottom edge
6479
+ * above the pointer; it never sits under the copy, and always stays 8px inside
6480
+ * the window. Its size is measured when its content changes, not every frame.
6333
6481
  */
6334
6482
  function DragRefusalHint({ children, container, className, style, ...props }) {
6335
6483
  const { hint } = drag();
@@ -6341,20 +6489,31 @@ function DragRefusalHint({ children, container, className, style, ...props }) {
6341
6489
  if (!dragging) return;
6342
6490
  const root = document.documentElement;
6343
6491
  root.setAttribute("data-drag-refused", "");
6492
+ const node = ref.current;
6493
+ const size = {
6494
+ width: node?.offsetWidth ?? 0,
6495
+ height: node?.offsetHeight ?? 0
6496
+ };
6497
+ const observer = node && typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
6498
+ size.width = node.offsetWidth;
6499
+ size.height = node.offsetHeight;
6500
+ }) : null;
6501
+ if (node) observer?.observe(node);
6344
6502
  let frame = 0;
6345
6503
  const tick = () => {
6346
- const node = ref.current;
6347
6504
  if (node) {
6505
+ const lift = document.querySelector("[data-slot=drag-lift]")?.getBoundingClientRect();
6348
6506
  let point = dragPointer();
6349
- if (viaKeyboard || !point) {
6350
- const lift = document.querySelector("[data-slot=drag-lift]")?.getBoundingClientRect();
6351
- if (lift) point = {
6352
- x: lift.right,
6353
- y: lift.top + lift.height / 2
6354
- };
6355
- }
6507
+ if ((viaKeyboard || !point) && lift) point = {
6508
+ x: lift.right,
6509
+ y: lift.top + lift.height / 2
6510
+ };
6356
6511
  if (point) {
6357
- node.style.transform = `translate(${Math.round(point.x + 14)}px, ${Math.round(point.y + 16)}px)`;
6512
+ const at = placeRefusalHint(point, size, lift ?? null, {
6513
+ width: window.innerWidth,
6514
+ height: window.innerHeight
6515
+ });
6516
+ node.style.transform = `translate(${at.x}px, ${at.y}px)`;
6358
6517
  node.style.visibility = "visible";
6359
6518
  }
6360
6519
  }
@@ -6363,6 +6522,7 @@ function DragRefusalHint({ children, container, className, style, ...props }) {
6363
6522
  tick();
6364
6523
  return () => {
6365
6524
  root.removeAttribute("data-drag-refused");
6525
+ observer?.disconnect();
6366
6526
  cancelAnimationFrame(frame);
6367
6527
  };
6368
6528
  }, [dragging, viaKeyboard]);
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { cn } from "./cn.mjs";
2
- import { $ as MessageListDateDivider, $i as DialogContent, $n as PreviewCardContent, $r as DropdownMenuItemLabel, $t as MessageReactionTooltipNameText, Aa as controlIconInset, Ai as ListRow, An as MessageItem, Ar as PopoverPopup, At as MessageMultiSelectToolbarActions, B as useDragSiblingTransition, Bi as ListRowValue, Bn as RadioGroupItem, Bt as inlineChip, C as DropPlaceholder, Ca as withOverlayLayer, Ci as ScrollAreaCorner, Cn as MessageItemEdited, Cr as PopoverArrow, Ct as messageEmbed, Da as tv, Di as Checkbox, Dn as MessageItemAvatarSlot, Dr as PopoverContent, Dt as MessageMultiSelect, Ea as ThemeStateContext, Ei as ScrollAreaViewport, En as MessageItemSearchMatch, Er as PopoverClose, Et as MessageTextSelectionToolbarSeparator, Fi as ListRowGroup, Fn as AvatarGroup, Fr as PopoverViewport, Ft as MessageReferencePending, G as SILENT_DRAG_ANNOUNCEMENTS, Gi as TooltipTrigger, Gr as MobileNavRoot, Gt as MessageReactionGlyph, Hi as TooltipContent, Hn as InputGroupAddon, Ht as MessageReactionChip, I as DragLift, Ii as ListRowIcon, In as AvatarGroupCount, It as MessageReferenceReadMark, Jr as DropdownMenuCheckboxItemIndicator, Jt as MessageReactionQuickButton, K as createDragAnnouncements, Ki as Badge, Kr as DropdownMenu, Kt as MessageReactionPickerContent, Li as ListRowMeta, Ln as AvatarImage, Lt as MessageReferenceSuffix, Ma as headEdges, Mi as ListRowBottom, Mn as Avatar, Mr as PopoverSeparator, Na as decisionWidth, Ni as ListRowChevron, Nn as AvatarBadge, Nr as PopoverTitle, Nt as MessageReferenceIndicator, Oa as stackedActions, Oi as CheckboxIndicator, On as MessageItemContinuationTime, Or as PopoverDescription, Ot as MessageMultiSelectCheckbox, Pi as ListRowDescription, Pn as AvatarFallback, Pr as PopoverTrigger, Pt as MessageReferenceLabel, Qi as DialogClose, Qr as DropdownMenuItemCount, Qt as MessageReactionTooltipName, Ri as ListRowTitle, Rn as RadioGroup, Rt as MessageReferenceText, Sa as useParentOverlayKind, Si as ScrollAreaContent, Sn as MessageItemContent, St as MessageEmbedHeader, Ta as useTheme, Ti as ScrollAreaThumb, Tn as MessageItemFooter, Tt as MessageTextSelectionToolbarButton, U as drag, Ui as TooltipPortal, Un as InputGroupInput, Ur as MobileNavItem, Ut as MessageReactionCount, V as useLiftContainer, Vi as Tooltip, Vn as InputGroup, Vt as MessageReactionAddButton, Wi as TooltipProvider, Wn as useOverflowTitle, Wr as MobileNavLabel, Wt as MessageReactionErrorIndicator, Xi as Dialog, Xr as DropdownMenuFooter, Xt as MessageReactionTooltipContent, Yi as useMobileViewport, Yn as PreviewCard, Yr as DropdownMenuContent, Yt as MessageReactionQuickRow, Zi as DialogBody, Zn as PreviewCardArrow, Zr as DropdownMenuItem, Zt as MessageReactionTooltipMeta, _ as InboxItemTrailing, _a as Button, _i as PanelHeaderRoot, _n as MessageItemSenderButton, _t as MessageQuotedPreviewTimestamp, a as InboxItemContext, aa as DialogOverlay, ai as DropdownMenuRadioItem, an as MessageItemSavedIndicator, ar as PreviewCardPopup, at as MessageQuotedPreviewAttachment, b as useLatestRef, ba as defaultNativeButton, bi as PanelSection, bn as MessageItemAttachments, bt as MessageEmbedContent, c as InboxItemLeading, ca as DialogSurface, ci as DropdownMenuShortcut, cr as PreviewCardTitle, d as InboxItemMetadata, da as PanelAction, di as DropdownMenuTrigger, dt as MessageQuotedPreviewChannel, ea as DialogDescription, ei as DropdownMenuLabel, en as MessageReactionTooltipNames, er as PreviewCardDescription, et as MessageListEndFade, f as InboxItemSelection, fa as panelToggleAction, fi as Kbd, fn as MessageItemHeader, ft as MessageQuotedPreviewContent, g as InboxItemTitle, ga as DialogSheetReset, gi as PanelFooter, gn as MessageItemSenderBadges, gt as MessageQuotedPreviewThread, h as InboxItemTimestamp, ha as textLinkVariants, hi as PanelBody, hn as MessageItemSender, ht as MessageQuotedPreviewText, i as InboxItemContent, ia as DialogNav, ii as DropdownMenuRadioGroup, in as MessageItemReadReceipt, it as MessageQuotedPreview, ja as headEdgePull, ji as ListRowBody, jn as searchHit, jr as PopoverPortal, jt as MessageReferenceChip, k as useDragSensors, ka as useStackedActions, ki as PRESENCE_COLOR, kn as MessageItemGutter, kr as PopoverHeader, kt as MessageMultiSelectToolbar, l as InboxItemMarks, la as DialogTitle, li as DropdownMenuSubmenu, lr as PreviewCardTrigger, lt as MessageQuotedPreviewAuthorMeta, m as InboxItemSubtitle, ma as TextLinkExternalIcon, mi as Panel, mn as MessageItemMeta, mt as MessageQuotedPreviewLoading, n as InboxItemActions, ni as DropdownMenuPopup, nn as MessageItemBadge, nt as MessageListNotice, o as InboxItemHeading, oa as DialogPortal, oi as DropdownMenuRadioItemIndicator, on as MessageItemToolbar, or as PreviewCardPortal, ot as MessageQuotedPreviewAttachments, p as InboxItemSender, pa as TextLink, pi as KbdGroup, pn as MessageItemInlineStatus, pt as MessageQuotedPreviewHeader, q as dragInstructions, qr as DropdownMenuCheckboxItem, qt as MessageReactionPickerStatic, r as InboxItemBody, ra as DialogHeader, ri as DropdownMenuPortal, rn as MessageItemBadgeSeparator, rr as PreviewCardHeader, rt as MessageList, s as InboxItemHoverTarget, si as DropdownMenuSeparator, sn as MessageItemToolbarButton, sr as PreviewCardSeparator, st as MessageQuotedPreviewAuthor, t as InboxItem, ta as DialogFooter, tn as MessageReactionTooltipSeparator, tt as MessageListHistoryLimited, u as InboxItemMessage, ua as DialogTrigger, ui as DropdownMenuSubmenuTrigger, ut as MessageQuotedPreviewAuthorName, v as InboxItemTrigger, va as buttonVariants, vi as PanelScrollContent, vn as MessageItemSenderGroup, vt as MessageQuotedPreviewUnavailable, wa as useThemeScopeProps, wi as ScrollAreaScrollbar, wn as MessageItemEmbedded, wr as PopoverBackdrop, wt as MessageTextSelectionToolbar, x as Skeleton, xi as ScrollArea, xn as MessageItemBody, xr as Popover, xt as MessageEmbedFooter, ya as Spinner, yi as PanelScrollViewport, yn as MessageItemTime, yt as MessageEmbed, zi as ListRowTop, zn as RadioGroupIndicator, zt as MessageReferenceUnavailable } from "./inbox-item-DIUkRiT4.mjs";
2
+ import { $ as MessageListDateDivider, $i as DialogContent, $n as PreviewCardContent, $r as DropdownMenuItemLabel, $t as MessageReactionTooltipNameText, Aa as controlIconInset, Ai as ListRow, An as MessageItem, Ar as PopoverPopup, At as MessageMultiSelectToolbarActions, B as useDragSiblingTransition, Bi as ListRowValue, Bn as RadioGroupItem, Bt as inlineChip, C as DropPlaceholder, Ca as withOverlayLayer, Ci as ScrollAreaCorner, Cn as MessageItemEdited, Cr as PopoverArrow, Ct as messageEmbed, Da as tv, Di as Checkbox, Dn as MessageItemAvatarSlot, Dr as PopoverContent, Dt as MessageMultiSelect, Ea as ThemeStateContext, Ei as ScrollAreaViewport, En as MessageItemSearchMatch, Er as PopoverClose, Et as MessageTextSelectionToolbarSeparator, Fi as ListRowGroup, Fn as AvatarGroup, Fr as PopoverViewport, Ft as MessageReferencePending, G as SILENT_DRAG_ANNOUNCEMENTS, Gi as TooltipTrigger, Gr as MobileNavRoot, Gt as MessageReactionGlyph, Hi as TooltipContent, Hn as InputGroupAddon, Ht as MessageReactionChip, I as DragLift, Ii as ListRowIcon, In as AvatarGroupCount, It as MessageReferenceReadMark, Jr as DropdownMenuCheckboxItemIndicator, Jt as MessageReactionQuickButton, K as createDragAnnouncements, Ki as Badge, Kr as DropdownMenu, Kt as MessageReactionPickerContent, Li as ListRowMeta, Ln as AvatarImage, Lt as MessageReferenceSuffix, Ma as headEdges, Mi as ListRowBottom, Mn as Avatar, Mr as PopoverSeparator, Na as decisionWidth, Ni as ListRowChevron, Nn as AvatarBadge, Nr as PopoverTitle, Nt as MessageReferenceIndicator, Oa as stackedActions, Oi as CheckboxIndicator, On as MessageItemContinuationTime, Or as PopoverDescription, Ot as MessageMultiSelectCheckbox, Pi as ListRowDescription, Pn as AvatarFallback, Pr as PopoverTrigger, Pt as MessageReferenceLabel, Qi as DialogClose, Qr as DropdownMenuItemCount, Qt as MessageReactionTooltipName, Ri as ListRowTitle, Rn as RadioGroup, Rt as MessageReferenceText, Sa as useParentOverlayKind, Si as ScrollAreaContent, Sn as MessageItemContent, St as MessageEmbedHeader, Ta as useTheme, Ti as ScrollAreaThumb, Tn as MessageItemFooter, Tt as MessageTextSelectionToolbarButton, U as drag, Ui as TooltipPortal, Un as InputGroupInput, Ur as MobileNavItem, Ut as MessageReactionCount, V as useLiftContainer, Vi as Tooltip, Vn as InputGroup, Vt as MessageReactionAddButton, Wi as TooltipProvider, Wn as useOverflowTitle, Wr as MobileNavLabel, Wt as MessageReactionErrorIndicator, Xi as Dialog, Xr as DropdownMenuFooter, Xt as MessageReactionTooltipContent, Yi as useMobileViewport, Yn as PreviewCard, Yr as DropdownMenuContent, Yt as MessageReactionQuickRow, Zi as DialogBody, Zn as PreviewCardArrow, Zr as DropdownMenuItem, Zt as MessageReactionTooltipMeta, _ as InboxItemTrailing, _a as Button, _i as PanelHeaderRoot, _n as MessageItemSenderButton, _t as MessageQuotedPreviewTimestamp, a as InboxItemContext, aa as DialogOverlay, ai as DropdownMenuRadioItem, an as MessageItemSavedIndicator, ar as PreviewCardPopup, at as MessageQuotedPreviewAttachment, b as useLatestRef, ba as defaultNativeButton, bi as PanelSection, bn as MessageItemAttachments, bt as MessageEmbedContent, c as InboxItemLeading, ca as DialogSurface, ci as DropdownMenuShortcut, cr as PreviewCardTitle, d as InboxItemMetadata, da as PanelAction, di as DropdownMenuTrigger, dt as MessageQuotedPreviewChannel, ea as DialogDescription, ei as DropdownMenuLabel, en as MessageReactionTooltipNames, er as PreviewCardDescription, et as MessageListEndFade, f as InboxItemSelection, fa as panelToggleAction, fi as Kbd, fn as MessageItemHeader, ft as MessageQuotedPreviewContent, g as InboxItemTitle, ga as DialogSheetReset, gi as PanelFooter, gn as MessageItemSenderBadges, gt as MessageQuotedPreviewThread, h as InboxItemTimestamp, ha as textLinkVariants, hi as PanelBody, hn as MessageItemSender, ht as MessageQuotedPreviewText, i as InboxItemContent, ia as DialogNav, ii as DropdownMenuRadioGroup, in as MessageItemReadReceipt, it as MessageQuotedPreview, ja as headEdgePull, ji as ListRowBody, jn as searchHit, jr as PopoverPortal, jt as MessageReferenceChip, k as useDragSensors, ka as useStackedActions, ki as PRESENCE_COLOR, kn as MessageItemGutter, kr as PopoverHeader, kt as MessageMultiSelectToolbar, l as InboxItemMarks, la as DialogTitle, li as DropdownMenuSubmenu, lr as PreviewCardTrigger, lt as MessageQuotedPreviewAuthorMeta, m as InboxItemSubtitle, ma as TextLinkExternalIcon, mi as Panel, mn as MessageItemMeta, mt as MessageQuotedPreviewLoading, n as InboxItemActions, ni as DropdownMenuPopup, nn as MessageItemBadge, nt as MessageListNotice, o as InboxItemHeading, oa as DialogPortal, oi as DropdownMenuRadioItemIndicator, on as MessageItemToolbar, or as PreviewCardPortal, ot as MessageQuotedPreviewAttachments, p as InboxItemSender, pa as TextLink, pi as KbdGroup, pn as MessageItemInlineStatus, pt as MessageQuotedPreviewHeader, q as dragInstructions, qr as DropdownMenuCheckboxItem, qt as MessageReactionPickerStatic, r as InboxItemBody, ra as DialogHeader, ri as DropdownMenuPortal, rn as MessageItemBadgeSeparator, rr as PreviewCardHeader, rt as MessageList, s as InboxItemHoverTarget, si as DropdownMenuSeparator, sn as MessageItemToolbarButton, sr as PreviewCardSeparator, st as MessageQuotedPreviewAuthor, t as InboxItem, ta as DialogFooter, tn as MessageReactionTooltipSeparator, tt as MessageListHistoryLimited, u as InboxItemMessage, ua as DialogTrigger, ui as DropdownMenuSubmenuTrigger, ut as MessageQuotedPreviewAuthorName, v as InboxItemTrigger, va as buttonVariants, vi as PanelScrollContent, vn as MessageItemSenderGroup, vt as MessageQuotedPreviewUnavailable, wa as useThemeScopeProps, wi as ScrollAreaScrollbar, wn as MessageItemEmbedded, wr as PopoverBackdrop, wt as MessageTextSelectionToolbar, x as Skeleton, xi as ScrollArea, xn as MessageItemBody, xr as Popover, xt as MessageEmbedFooter, ya as Spinner, yi as PanelScrollViewport, yn as MessageItemTime, yt as MessageEmbed, zi as ListRowTop, zn as RadioGroupIndicator, zt as MessageReferenceUnavailable } from "./inbox-item-D7xLTDjD.mjs";
3
3
  import { Dialog as Dialog$1 } from "@base-ui/react/dialog";
4
4
  import { AlertTriangle, Bell, Check, CheckCircle2, ChevronDown, ChevronLeft, ChevronRight, CircleCheck, Copy, FileText, Image, Info, TriangleAlert, Upload, X, Zap } from "lucide-react";
5
5
  import { Children, cloneElement, createContext, createElement, isValidElement, use, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
@@ -2703,7 +2703,7 @@ const lightbox = tv({ slots: {
2703
2703
  ],
2704
2704
  thumbnailOverlay: [
2705
2705
  "pointer-events-none absolute inset-0 block transition-colors duration-150",
2706
- "bg-black/45 group-hover/lightbox-thumb:bg-black/28",
2706
+ "bg-layer-backdrop-nested group-hover/lightbox-thumb:bg-layer-backdrop-nested/70",
2707
2707
  "group-data-active/lightbox-thumb:bg-transparent"
2708
2708
  ],
2709
2709
  close: ["pointer-events-auto"]
@@ -5964,14 +5964,14 @@ const messageImageGallery = tv({
5964
5964
  slots: {
5965
5965
  root: ["@container/gallery w-full max-w-[min(360px,100%)] stack-y-1"],
5966
5966
  row: ["grid", "gap-1"],
5967
- item: [
5968
- "group/img relative overflow-hidden text-left",
5969
- "rounded-control border border-line-hairline bg-fill-muted",
5970
- "has-data-[slot=message-image-gallery-overlay]:opacity-70"
5971
- ],
5967
+ item: ["group/img relative overflow-hidden text-left", "rounded-control border border-line-hairline bg-fill-muted"],
5972
5968
  media: "block size-full ",
5973
- overlay: "absolute inset-0 flex items-center justify-center",
5974
- preview: ["absolute inset-0", "outline-none focus-visible:keyboard:inset-ring-2 focus-visible:keyboard:inset-ring-foreground"],
5969
+ overlay: "absolute inset-0 flex items-center justify-center bg-layer-veil",
5970
+ preview: [
5971
+ "absolute inset-0",
5972
+ "transition-colors duration-75 hover:bg-ink-8",
5973
+ "outline-none focus-visible:keyboard:inset-ring-2 focus-visible:keyboard:inset-ring-foreground"
5974
+ ],
5975
5975
  action: [
5976
5976
  "absolute z-10 hidden items-center justify-center",
5977
5977
  "focus:flex",
@@ -6317,12 +6317,7 @@ const messageForwardedBundle = tv({
6317
6317
  },
6318
6318
  variants: {
6319
6319
  fullWidth: { true: { root: "w-full max-w-none" } },
6320
- collapsed: { true: { items: [
6321
- "max-h-40 overflow-clip",
6322
- "after:pointer-events-none after:absolute after:inset-x-0 after:bottom-0 after:h-10 after:bg-linear-to-t",
6323
- "data-[tight-fold]:after:h-5",
6324
- "after:from-layer-card after:via-layer-card/80 after:to-layer-card/0"
6325
- ] } }
6320
+ collapsed: { true: { items: ["max-h-40 overflow-clip", "mask-fade-b data-tight-fold:mask-fade-b-edge"] } }
6326
6321
  }
6327
6322
  });
6328
6323
  const messageForwardedBundleItem = tv({
@@ -8127,9 +8122,9 @@ const composer = tv({ slots: {
8127
8122
  "font-sans text-body font-normal text-foreground",
8128
8123
  "placeholder:text-foreground-hint"
8129
8124
  ],
8130
- toolbar: ["flex items-center justify-between", "mx-[calc(var(--control-embedded-inset)-0.75rem-1px)] gap-2"],
8131
- actions: ["flex items-center gap-2"],
8132
- meta: ["flex items-center", "gap-2"],
8125
+ toolbar: ["flex items-center justify-between", "mx-[calc(var(--control-embedded-inset)-0.75rem-1px)] gap-embedded-inset"],
8126
+ actions: ["flex items-center gap-embedded-inset"],
8127
+ meta: ["flex items-center", "gap-embedded-inset"],
8133
8128
  iconButton: [
8134
8129
  "size-8 rounded-chip text-foreground-muted",
8135
8130
  "disabled:border-transparent disabled:bg-transparent",
@@ -8267,7 +8262,7 @@ const composerAttachment = tv({
8267
8262
  attachmentUploadingOverlay: [
8268
8263
  "absolute inset-0 z-10 flex flex-col items-center justify-center px-1",
8269
8264
  "text-center",
8270
- "rounded-control bg-layer-card/80",
8265
+ "rounded-control bg-layer-veil",
8271
8266
  "font-mono text-mono-meta font-bold text-foreground"
8272
8267
  ],
8273
8268
  attachmentUploadProgressBar: [
@@ -8280,7 +8275,7 @@ const composerAttachment = tv({
8280
8275
  "absolute inset-0 z-10 flex flex-col items-center justify-center px-1",
8281
8276
  "[border-width:var(--ca-overlay-border,2px)]",
8282
8277
  "text-center",
8283
- "rounded-control border-danger-strong bg-layer-card/90",
8278
+ "rounded-control border-danger-strong bg-layer-veil",
8284
8279
  "font-sans text-caption font-semibold text-danger-strong"
8285
8280
  ],
8286
8281
  attachmentRemove: [
@@ -10431,9 +10426,7 @@ const tabs = tv({
10431
10426
  "relative isolate flex w-max max-w-full",
10432
10427
  "overscroll-contain whitespace-nowrap",
10433
10428
  "scrollbar-none [&::-webkit-scrollbar]:hidden",
10434
- "data-[overflow=end]:mask-[linear-gradient(to_right,black_calc(100%-32px),transparent)]",
10435
- "data-[overflow=start]:mask-[linear-gradient(to_right,transparent,black_32px)]",
10436
- "data-[overflow=both]:mask-[linear-gradient(to_right,transparent,black_32px,black_calc(100%-32px),transparent)]"
10429
+ "mask-fade-x"
10437
10430
  ],
10438
10431
  background: ["", "hidden"],
10439
10432
  tab: [
@@ -11113,13 +11106,9 @@ function AppShellSidebarSlot({ className, id: idProp, render, ...props }) {
11113
11106
  id,
11114
11107
  inert: collapsed ? true : void 0,
11115
11108
  className: sidebarSlot({ className: cn(className, sidebar?.collapsible && [
11116
- "will-change-[opacity]",
11117
- "transition-opacity duration-160 ease-slide",
11118
- "motion-reduce:transition-none",
11119
11109
  "group-data-[state=expanded]/app-shell:relative group-data-[state=expanded]/app-shell:z-1",
11120
11110
  "group-data-[state=collapsed]/app-shell:pointer-events-none",
11121
- "group-data-[state=collapsed]/app-shell:opacity-0",
11122
- "group-data-[state=collapsed]/app-shell:duration-120"
11111
+ "group-data-[state=collapsed]/app-shell:opacity-0"
11123
11112
  ]) })
11124
11113
  };
11125
11114
  return useRender({
@@ -11155,65 +11144,17 @@ function AppShellOverlaySlot({ className, render, ...props }) {
11155
11144
  }
11156
11145
  function AppShellMainSlot({ className, overlay = false, render, ...props }) {
11157
11146
  const { mainSlot } = appShell({ overlay });
11158
- const sidebar = useOptionalAppShellSidebar();
11159
- const collapsible = sidebar?.collapsible ?? false;
11160
- const sliding = useSidebarSlide(collapsible, sidebar?.open ?? true);
11147
+ const collapsible = useOptionalAppShellSidebar()?.collapsible ?? false;
11161
11148
  const defaultProps = {
11162
11149
  "data-slot": "app-shell-main-slot",
11163
- "data-sliding": sliding.active ? "" : void 0,
11164
- className: mainSlot({ className: cn(className, collapsible && [
11165
- "absolute inset-y-0 right-0 left-0",
11166
- "data-sliding:z-2",
11167
- "transition-[left] duration-220 ease-slide",
11168
- "motion-reduce:transition-none",
11169
- "group-data-[state=collapsed]/app-shell:-left-(--app-shell-sidebar-width)"
11170
- ]) })
11150
+ className: mainSlot({ className: cn(className, collapsible && ["absolute inset-y-0 right-0 left-0", "group-data-[state=collapsed]/app-shell:-left-(--app-shell-sidebar-width)"]) })
11171
11151
  };
11172
11152
  return useRender({
11173
11153
  defaultTagName: "main",
11174
11154
  render,
11175
- ref: sliding.ref,
11176
11155
  props: mergeProps(defaultProps, props)
11177
11156
  });
11178
11157
  }
11179
- function useSidebarSlide(collapsible, open) {
11180
- const elementRef = useRef(null);
11181
- const [slide, setSlide] = useState({
11182
- active: false,
11183
- open
11184
- });
11185
- if (slide.open !== open) setSlide({
11186
- active: collapsible,
11187
- open
11188
- });
11189
- const active = slide.active;
11190
- const ref = useCallback((element) => {
11191
- elementRef.current = element;
11192
- }, []);
11193
- useEffect(() => {
11194
- if (!active) return;
11195
- const transitions = elementRef.current?.getAnimations?.().filter((animation) => "transitionProperty" in animation && animation.transitionProperty === "left" && animation.effect?.getComputedTiming().activeDuration !== 0) ?? [];
11196
- let current = true;
11197
- const settle = () => {
11198
- if (current) setSlide((state) => state.open === open ? {
11199
- active: false,
11200
- open
11201
- } : state);
11202
- };
11203
- if (transitions.length === 0) {
11204
- settle();
11205
- return;
11206
- }
11207
- Promise.allSettled(transitions.map((transition) => transition.finished)).then(settle);
11208
- return () => {
11209
- current = false;
11210
- };
11211
- }, [active, open]);
11212
- return {
11213
- active,
11214
- ref
11215
- };
11216
- }
11217
11158
  function AppShellSidebarTrigger(props) {
11218
11159
  const sidebar = useOptionalAppShellSidebar();
11219
11160
  if (!sidebar?.collapsible) return null;
@@ -12168,7 +12109,7 @@ const taskStatusStrong = tv({ variants: { status: {
12168
12109
  const taskCardStatus = tv({ base: [
12169
12110
  "inline-flex min-w-0 shrink-0 touch-manipulation items-center",
12170
12111
  "transition-[filter,opacity] duration-100 outline-none",
12171
- "data-[busy=true]:pointer-events-none data-[busy=true]:cursor-wait data-[busy=true]:opacity-60 data-[busy=true]:disabled:opacity-100",
12112
+ "data-[busy=true]:pointer-events-none data-[busy=true]:cursor-wait data-[busy=true]:opacity-dimmed data-[busy=true]:disabled:opacity-100",
12172
12113
  "h-[22px] gap-1.5 rounded-chip border-0 px-[7px] py-0",
12173
12114
  "font-sans text-[12px] leading-4 font-semibold text-foreground",
12174
12115
  "[&:not(:has([data-slot=task-card-status-label]))]:hidden",
@@ -15350,7 +15291,7 @@ function TaskChip({ status, loading = false, variant = "default", size = "defaul
15350
15291
  "data-slot": "task-chip",
15351
15292
  "data-inline-reference": "",
15352
15293
  "data-loading": loading ? "true" : void 0,
15353
- className: cn(root({ className }), loading && "pointer-events-none opacity-60"),
15294
+ className: cn(root({ className }), loading && "pointer-events-none opacity-dimmed"),
15354
15295
  children
15355
15296
  };
15356
15297
  return useRender({
package/dist/styles.css CHANGED
@@ -191,6 +191,7 @@
191
191
  --color-layer-popover: var(--layer-popover);
192
192
  --color-layer-backdrop: var(--layer-backdrop);
193
193
  --color-layer-backdrop-nested: var(--layer-backdrop-nested);
194
+ --color-layer-veil: var(--layer-veil);
194
195
  --color-layer-card: var(--layer-card);
195
196
  --color-layer-hud: var(--layer-hud);
196
197
  --color-foreground-on-hud: var(--foreground-on-hud);
@@ -663,6 +664,56 @@ textarea:-webkit-autofill:disabled {
663
664
  }
664
665
  }
665
666
 
667
+ /* Dimmed: something present but not active, deleted, offline or still
668
+ pending, at 60% (design/foundations/states.md#dimmed). Lighter than the 40%
669
+ of a drag origin, which says "it was here", not "it is here, inactive". */
670
+ @utility opacity-dimmed {
671
+ opacity: var(--opacity-dimmed);
672
+ }
673
+
674
+ /* Content fades (design/foundations/spacing.md#edges-fade): a mask, never a
675
+ colour gradient, so whatever is behind the content (a row's hover or
676
+ located fill, a card, a tint) shows through the fade unchanged.
677
+ mask-fade-t / mask-fade-b fade the top or bottom edge over fade-content
678
+ (48px), where content is cut: a fold, a glimpse. mask-fade-x fades the
679
+ sides of a strip that scrolls sideways over fade-edge (24px), on the side
680
+ its data-overflow names (start, end or both) and nowhere without it.
681
+ mask-fade-t-edge / mask-fade-b-edge take the edge length instead, for a
682
+ fold too short to give 48px to the fade. */
683
+ @utility mask-fade-t {
684
+ mask-image: linear-gradient(to bottom, transparent, oklch(0 0 0) var(--fade-content));
685
+ }
686
+
687
+ @utility mask-fade-t-edge {
688
+ mask-image: linear-gradient(to bottom, transparent, oklch(0 0 0) var(--fade-edge));
689
+ }
690
+
691
+ @utility mask-fade-b {
692
+ mask-image: linear-gradient(to top, transparent, oklch(0 0 0) var(--fade-content));
693
+ }
694
+
695
+ @utility mask-fade-b-edge {
696
+ mask-image: linear-gradient(to top, transparent, oklch(0 0 0) var(--fade-edge));
697
+ }
698
+
699
+ @utility mask-fade-x {
700
+ &[data-overflow="start"] {
701
+ mask-image: linear-gradient(to right, transparent, oklch(0 0 0) var(--fade-edge));
702
+ }
703
+ &[data-overflow="end"] {
704
+ mask-image: linear-gradient(to left, transparent, oklch(0 0 0) var(--fade-edge));
705
+ }
706
+ &[data-overflow="both"] {
707
+ mask-image: linear-gradient(
708
+ to right,
709
+ transparent,
710
+ oklch(0 0 0) var(--fade-edge),
711
+ oklch(0 0 0) calc(100% - var(--fade-edge)),
712
+ transparent
713
+ );
714
+ }
715
+ }
716
+
666
717
  /* Drag and drop (design/patterns/drag-and-drop.md). While something is
667
718
  lifted, DragLift sets data-dragging on the root, and DragRefusalHint sets
668
719
  data-drag-refused while the place under the hand refuses it: the cursor
package/dist/tokens.json CHANGED
@@ -1377,6 +1377,25 @@
1377
1377
  }
1378
1378
  }
1379
1379
  },
1380
+ "veil": {
1381
+ "$value": {
1382
+ "colorSpace": "oklch",
1383
+ "components": [1, 0, 0],
1384
+ "alpha": 0.8
1385
+ },
1386
+ "$description": "The cover over something busy or loading in place: an image uploading or that failed, a preview being prepared. The page at 80%, so what is under it shows through, dimmed, and a default-colour Spinner on it reads in both modes. Translucent by contract, like the two backdrops.",
1387
+ "$extensions": {
1388
+ "build.botiverse.raft": {
1389
+ "modes": {
1390
+ "dark": {
1391
+ "colorSpace": "oklch",
1392
+ "components": [0.2213, 0.0038, 106.71],
1393
+ "alpha": 0.8
1394
+ }
1395
+ }
1396
+ }
1397
+ }
1398
+ },
1380
1399
  "card": {
1381
1400
  "$value": {
1382
1401
  "colorSpace": "oklch",
@@ -2530,6 +2549,27 @@
2530
2549
  }
2531
2550
  }
2532
2551
  },
2552
+ "fade": {
2553
+ "$type": "dimension",
2554
+ "edge": {
2555
+ "$value": { "value": 24, "unit": "px" },
2556
+ "$description": "The fade at the edge of a strip that scrolls sideways (mask-fade-x): long enough to say more is there, short enough to leave the item under it readable.",
2557
+ "$extensions": { "build.botiverse.raft": { "scope": "root" } }
2558
+ },
2559
+ "content": {
2560
+ "$value": { "value": 48, "unit": "px" },
2561
+ "$description": "The fade where content is cut (mask-fade-t, mask-fade-b): a long message folded, a glimpse of hidden history. About two lines, so the cut reads as a fold, not a clipped line.",
2562
+ "$extensions": { "build.botiverse.raft": { "scope": "root" } }
2563
+ }
2564
+ },
2565
+ "opacity": {
2566
+ "$type": "number",
2567
+ "dimmed": {
2568
+ "$value": 0.6,
2569
+ "$description": "Something present but not active: deleted, offline or still pending (opacity-dimmed). The place a dragged item came from keeps its own 0.4 (data-drag-origin).",
2570
+ "$extensions": { "build.botiverse.raft": { "scope": "root" } }
2571
+ }
2572
+ },
2533
2573
  "motion": {
2534
2574
  "drag-settle-duration": {
2535
2575
  "$type": "duration",
package/dist/wip.d.mts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { $ as MessageListJumpPillProps, Aa as PopoverBody, As as DialogCommit, B as DragMessages, Bs as DialogContentProps, C as InboxItemProps, Co as DropdownMenuLinkItemProps, Di as PreviewCardEyebrowProps, Ei as PreviewCardEyebrow, G as dragMessages, Gs as DialogFooterNoteProps, H as DragPlace, J as MessageListJumpPill, K as MessageListJumpControls, Ls as DialogClose, Mi as PreviewCardMeta, Ms as DialogSheetContent, Ni as PreviewCardMetaProps, Ns as DialogSheetContentProps, Oi as PreviewCardFooter, Os as TextLinkProps, Ps as Dialog, Q as MessageListJumpPillGroupProps, R as DEFAULT_DRAG_MESSAGES, Sa as MobileNavServerPopoverRowProps, So as DropdownMenuLinkItem, Ta as PopoverActionsProps, U as createDragAnnouncements, V as DragNamed, W as dragInstructions, Ws as DialogFooterNote, X as MessageListJumpPillDismissProps, Y as MessageListJumpPillDismiss, Z as MessageListJumpPillGroup, _a as MobileNavServerPopoverRowBodyProps, _c as VariantProps, _i as PreviewCardActionsProps, _s as ListRowProps, _t as MessageQuotedPreviewAuthorDelegatedByProps, aa as MobileNavServerPopoverContentProps, ba as MobileNavServerPopoverRowLabel, bi as PreviewCardBody, ca as MobileNavServerPopoverDestinations, cc as DialogTrigger, da as MobileNavServerPopoverGroupProps, dn as MessageReferenceReadMarkProps, dr as MessageItemDelegatedBy, fa as MobileNavServerPopoverList, fr as MessageItemDelegatedByButton, ga as MobileNavServerPopoverRowBody, gi as PreviewCardActions, gr as MessageItemDelegatedByProps, gt as MessageQuotedPreviewAuthorDelegatedBy, ha as MobileNavServerPopoverRow, hr as MessageItemDelegatedByNameProps, ia as MobileNavServerPopoverContent, ja as PopoverBodyProps, js as DialogCommitProps, ki as PreviewCardFooterProps, la as MobileNavServerPopoverDestinationsProps, lr as MessageItemDelegatedAvatar, ma as MobileNavServerPopoverProps, mr as MessageItemDelegatedByName, na as MobileNavServerItemProps, nc as DialogSheetBack, nn as MessageReferenceIcon, oa as MobileNavServerPopoverDestination, pa as MobileNavServerPopoverListProps, pc as ButtonProps, pr as MessageItemDelegatedByButtonProps, q as MessageListJumpControlsProps, ra as MobileNavServerPopover, rc as DialogSheetBackProps, rn as MessageReferenceIconProps, sa as MobileNavServerPopoverDestinationProps, sc as DialogTitleProps, ta as MobileNavServerItem, u as InboxItemDensity, ua as MobileNavServerPopoverGroup, un as MessageReferenceReadMark, ur as MessageItemDelegatedAvatarProps, va as MobileNavServerPopoverRowDescription, vc as DataAttributes, wa as PopoverActions, xa as MobileNavServerPopoverRowLabelProps, xi as PreviewCardBodyProps, ya as MobileNavServerPopoverRowDescriptionProps, yc as StyledProps, z as DragAnnouncementSource } from "./inbox-item-B8eXIikq.mjs";
2
- import { CSSProperties, ComponentProps, ComponentPropsWithRef, PointerEvent, ReactNode, RefObject } from "react";
2
+ import { CSSProperties, ComponentProps, ComponentPropsWithRef, ReactNode, RefObject } from "react";
3
3
  import { Button } from "@base-ui/react/button";
4
4
  import { useRender } from "@base-ui/react/use-render";
5
- import { Announcements, CollisionDetection, DragEndEvent, DragOverEvent, DragStartEvent, DropAnimation, KeyboardCoordinateGetter, KeyboardSensorOptions, Modifier, PointerSensor, PointerSensorOptions, ScreenReaderInstructions, TouchSensor, TouchSensorOptions, UniqueIdentifier } from "@dnd-kit/core";
5
+ import { Announcements, CollisionDetection, DragEndEvent, DragOverEvent, DragStartEvent, DropAnimation, KeyboardCoordinateGetter, KeyboardSensorOptions, Modifier, PointerSensor, ScreenReaderInstructions, TouchSensor, TouchSensorOptions, UniqueIdentifier } from "@dnd-kit/core";
6
6
  import { Combobox } from "@base-ui/react/combobox";
7
7
  import { SortingStrategy } from "@dnd-kit/sortable";
8
8
  import { Meter as Meter$1 } from "@base-ui/react/meter";
@@ -305,14 +305,10 @@ type HoldThenMoveOptions = {
305
305
  export declare const HoldThenMoveTouchSensor: typeof TouchSensor;
306
306
  /**
307
307
  * dnd-kit's PointerSensor for a mouse and a pen only: a finger's pointerdown is
308
- * left to `HoldThenMoveTouchSensor`.
308
+ * left to `HoldThenMoveTouchSensor`. While it holds a drag, Escape puts the
309
+ * item back and reaches nothing else.
309
310
  */
310
- export declare class MouseOnlyPointerSensor extends PointerSensor {
311
- static activators: {
312
- eventName: "onPointerDown";
313
- handler: (event: PointerEvent, options: PointerSensorOptions) => boolean;
314
- }[];
315
- }
311
+ export declare const MouseOnlyPointerSensor: typeof PointerSensor;
316
312
  /**
317
313
  * `useSensor(HoldThenMoveTouchSensor, …)` with the activation the sensor relies
318
314
  * on: no constraint dnd-kit would act on by itself, so only a move after the
@@ -358,6 +354,11 @@ type DragSensorsOptions = {
358
354
  * - the keyboard: Space picks up, the arrows move, Space drops, Escape puts
359
355
  * back. Enter never starts a drag.
360
356
  *
357
+ * However the drag started, Escape during it only puts the item back: it is
358
+ * taken in the capture phase on the window and stopped, so no app handler
359
+ * (a dialog, a panel, a popover that closes on Escape) hears it. Once the drag
360
+ * is over, Escape reaches the app again.
361
+ *
361
362
  * Pass the options, or only the keyboard's `coordinateGetter`.
362
363
  */
363
364
  export declare function useDragSensors(options?: DragSensorsOptions | KeyboardCoordinateGetter): import("@dnd-kit/core").SensorDescriptor<import("@dnd-kit/core").SensorOptions>[];
@@ -539,6 +540,11 @@ type DragRefusalHintProps = Omit<ComponentPropsWithRef<"div">, "children" | "ref
539
540
  * not-allowed cursor. Render it only while the place is refused, inside the
540
541
  * `DndContext`, with no placeholder anywhere. Announce the reason as well:
541
542
  * the chip is not read out.
543
+ *
544
+ * The chip sits right of and below the pointer. Near the window's right edge
545
+ * it goes left of the pointer and of the lifted copy, near the bottom edge
546
+ * above the pointer; it never sits under the copy, and always stays 8px inside
547
+ * the window. Its size is measured when its content changes, not every frame.
542
548
  */
543
549
  export declare function DragRefusalHint({ children, container, className, style, ...props }: DragRefusalHintProps): import("react").ReactPortal | null;
544
550
  //#endregion
package/dist/wip.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { cn } from "./cn.mjs";
2
- import { $i as DialogContent, A as DRAG_AUTO_SCROLL_ZONE_PX, Ai as ListRow, B as useDragSiblingTransition, Bn as RadioGroupItem, Br as TOUCH_DRAG_TOLERANCE_PX, C as DropPlaceholder, D as listKeyboardCoordinates, Da as tv, Di as Checkbox, E as MOUSE_DRAG_DISTANCE_PX, F as DROP_GLIDE, G as SILENT_DRAG_ANNOUNCEMENTS, Gi as TooltipTrigger, Gn as measureContent, H as LIFT_CLASS, Hi as TooltipContent, Hn as InputGroupAddon, Hr as useMouseOnlyPointerSensor, I as DragLift, Ir as HoldThenMoveTouchSensor, It as MessageReferenceReadMark, J as dragMessages, Ji as DialogSheetContent, Jn as readNow, K as createDragAnnouncements, Kn as measureElement, L as ORIGIN_OPACITY, Li as ListRowMeta, Lr as MouseOnlyPointerSensor, M as DRAG_LIFT_Z_INDEX, Mi as ListRowBottom, Mt as MessageReferenceIcon, N as DRAG_SETTLE, O as useDragAcrossLists, P as DRAG_SIBLING_TRANSITION, Pi as ListRowDescription, Q as MessageListJumpPillGroup, Qi as DialogClose, Qn as PreviewCardBody, R as cancelDrag, Ri as ListRowTitle, Rn as RadioGroup, Rr as TOUCH_DRAG_DISTANCE_PX, S as DragRefusalHint, Sa as useParentOverlayKind, Sr as PopoverActions, T as DragRemeasure, Ta as useTheme, Tr as PopoverBody, Un as InputGroupInput, Vi as Tooltip, Vn as InputGroup, Vr as useHoldThenMoveTouchSensor, W as DEFAULT_DRAG_MESSAGES, Wn as useOverflowTitle, X as MessageListJumpPill, Xi as Dialog, Xn as PreviewCardActions, Y as MessageListJumpControls, Z as MessageListJumpPillDismiss, _ as InboxItemTrailing, _a as Button$1, _r as MobileNavServerPopoverRow, a as InboxItemContext, b as useLatestRef, ba as defaultNativeButton, br as MobileNavServerPopoverRowLabel, c as InboxItemLeading, cn as MessageItemDelegatedAvatar, ct as MessageQuotedPreviewAuthorDelegatedBy, dn as MessageItemDelegatedByName, dr as MobileNavServerPopover, fr as MobileNavServerPopoverContent, g as InboxItemTitle, gr as MobileNavServerPopoverList, h as InboxItemTimestamp, hr as MobileNavServerPopoverGroup, i as InboxItemContent, ir as PreviewCardMeta, j as DRAG_KEYBOARD_OFFSET_PX, ji as ListRowBody, k as useDragSensors, la as DialogTitle, ln as MessageItemDelegatedBy, mr as MobileNavServerPopoverDestinations, na as DialogFooterNote, nr as PreviewCardFooter, o as InboxItemHeading, pa as TextLink, pr as MobileNavServerPopoverDestination, q as dragInstructions, qi as DialogCommit, qn as observeTruncation, r as InboxItemBody, sa as DialogSheetBack, t as InboxItem, ti as DropdownMenuLinkItem, tr as PreviewCardEyebrow, ua as DialogTrigger, un as MessageItemDelegatedByButton, ur as MobileNavServerItem, vr as MobileNavServerPopoverRowBody, w as DRAG_KEYBOARD_CODES, wa as useThemeScopeProps, x as Skeleton, xa as OverlayLayerProvider, y as inboxItem, ya as Spinner, yr as MobileNavServerPopoverRowDescription, z as keepGrabInside, zi as ListRowTop, zr as TOUCH_DRAG_HOLD_MS } from "./inbox-item-DIUkRiT4.mjs";
2
+ import { $i as DialogContent, A as DRAG_AUTO_SCROLL_ZONE_PX, Ai as ListRow, B as useDragSiblingTransition, Bn as RadioGroupItem, Br as TOUCH_DRAG_TOLERANCE_PX, C as DropPlaceholder, D as listKeyboardCoordinates, Da as tv, Di as Checkbox, E as MOUSE_DRAG_DISTANCE_PX, F as DROP_GLIDE, G as SILENT_DRAG_ANNOUNCEMENTS, Gi as TooltipTrigger, Gn as measureContent, H as LIFT_CLASS, Hi as TooltipContent, Hn as InputGroupAddon, Hr as useMouseOnlyPointerSensor, I as DragLift, Ir as HoldThenMoveTouchSensor, It as MessageReferenceReadMark, J as dragMessages, Ji as DialogSheetContent, Jn as readNow, K as createDragAnnouncements, Kn as measureElement, L as ORIGIN_OPACITY, Li as ListRowMeta, Lr as MouseOnlyPointerSensor, M as DRAG_LIFT_Z_INDEX, Mi as ListRowBottom, Mt as MessageReferenceIcon, N as DRAG_SETTLE, O as useDragAcrossLists, P as DRAG_SIBLING_TRANSITION, Pi as ListRowDescription, Q as MessageListJumpPillGroup, Qi as DialogClose, Qn as PreviewCardBody, R as cancelDrag, Ri as ListRowTitle, Rn as RadioGroup, Rr as TOUCH_DRAG_DISTANCE_PX, S as DragRefusalHint, Sa as useParentOverlayKind, Sr as PopoverActions, T as DragRemeasure, Ta as useTheme, Tr as PopoverBody, Un as InputGroupInput, Vi as Tooltip, Vn as InputGroup, Vr as useHoldThenMoveTouchSensor, W as DEFAULT_DRAG_MESSAGES, Wn as useOverflowTitle, X as MessageListJumpPill, Xi as Dialog, Xn as PreviewCardActions, Y as MessageListJumpControls, Z as MessageListJumpPillDismiss, _ as InboxItemTrailing, _a as Button$1, _r as MobileNavServerPopoverRow, a as InboxItemContext, b as useLatestRef, ba as defaultNativeButton, br as MobileNavServerPopoverRowLabel, c as InboxItemLeading, cn as MessageItemDelegatedAvatar, ct as MessageQuotedPreviewAuthorDelegatedBy, dn as MessageItemDelegatedByName, dr as MobileNavServerPopover, fr as MobileNavServerPopoverContent, g as InboxItemTitle, gr as MobileNavServerPopoverList, h as InboxItemTimestamp, hr as MobileNavServerPopoverGroup, i as InboxItemContent, ir as PreviewCardMeta, j as DRAG_KEYBOARD_OFFSET_PX, ji as ListRowBody, k as useDragSensors, la as DialogTitle, ln as MessageItemDelegatedBy, mr as MobileNavServerPopoverDestinations, na as DialogFooterNote, nr as PreviewCardFooter, o as InboxItemHeading, pa as TextLink, pr as MobileNavServerPopoverDestination, q as dragInstructions, qi as DialogCommit, qn as observeTruncation, r as InboxItemBody, sa as DialogSheetBack, t as InboxItem, ti as DropdownMenuLinkItem, tr as PreviewCardEyebrow, ua as DialogTrigger, un as MessageItemDelegatedByButton, ur as MobileNavServerItem, vr as MobileNavServerPopoverRowBody, w as DRAG_KEYBOARD_CODES, wa as useThemeScopeProps, x as Skeleton, xa as OverlayLayerProvider, y as inboxItem, ya as Spinner, yr as MobileNavServerPopoverRowDescription, z as keepGrabInside, zi as ListRowTop, zr as TOUCH_DRAG_HOLD_MS } from "./inbox-item-D7xLTDjD.mjs";
3
3
  import { Check, ChevronDown, LoaderCircle, Plus, Search, X } from "lucide-react";
4
4
  import { createContext, use, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
5
5
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botiverse/raft-ui",
3
- "version": "0.36.0",
3
+ "version": "0.37.0",
4
4
  "description": "React components and CSS foundations for the Raft Design System.",
5
5
  "keywords": [
6
6
  "agent-skills",