@botiverse/raft-ui 0.12.0 → 0.14.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,6 +1,86 @@
1
+ ## [0.14.0](https://github.com/botiverse/raft-design/compare/v0.13.0...v0.14.0) (2026-10-21)
2
+
3
+ ### Design System
4
+
5
+ - **Option height**: an option in any popup list is as tall as its content, and the space above and below its text is constant. ([#647](https://github.com/botiverse/raft-design/pull/647))
6
+ - Why: options had a fixed 6px above and below whatever their content, which is less than a one-line 36px row leaves around its text, so a two-line option (a wrapped label, or a name with an id under it) looked more cramped than its one-line neighbors.
7
+ - Affects: space above and below an option 6px → 7px with a mouse (22px line), 8px on a touch screen (20px line); one line stays 36px; a second line adds only its own height. On short windows (600px tall or less) the menus' extra `py-1` is removed.
8
+ - Docs: https://raft-design.botiverse.build/patterns/forms/#option-height
9
+ - **The ends of a header**: the icon buttons at both ends of every header (panels, threads, phone top bars, drawers, sheets, a dialog's ×, the sidebar) put their icon box on the content margin, and their empty hit area hangs outward. ([#650](https://github.com/botiverse/raft-design/pull/650))
10
+ - Why: a 44px button on a touch screen was placed by its own edge, so Back sat 11px inside the content line while the ⋮ at the other end stayed on it, and the two ends stopped mirroring.
11
+ - Affects: icon box on the margin, 16px on a phone and 20px on a desktop and in a dialog, at every button size (32px or 44px); a phone's top bar moves its icon boxes from 14px to 16px from the edge.
12
+ - Docs: https://raft-design.botiverse.build/foundations/alignment/#the-ends-of-a-header
13
+
14
+ - **A tab's own controls**: when a tab's content has controls (a view switch, filters, search, a sort), they go in one pinned toolbar row under the tabs, the same row the Tasks tab draws: filters and search on the left, a view switch on the right; a tab with no controls has no row. ([#652](https://github.com/botiverse/raft-design/pull/652))
15
+ - Why: a view switch sat directly on the divider under the tabs, and each tab placed its controls differently, so a switch under the selected tab read as a second row of tabs.
16
+ - Affects: the row has the content margin at its sides (20px desktop, 16px phone) and a `line-seam` below it.
17
+ - Docs: https://raft-design.botiverse.build/components/tabs/#a-tabs-own-controls
18
+
19
+ ### Added
20
+
21
+ - **Utilities** `py-option-inset` and `min-h-option`, the constant option padding and the floor for an option with no line of text. ([#647](https://github.com/botiverse/raft-design/pull/647))
22
+ - Why: a product that builds its own option row keeps the same padding as raft-ui's.
23
+ - Affects: `py-option-inset` = (36px − one line) / 2; `min-h-option` 36px.
24
+ - Docs: https://raft-design.botiverse.build/patterns/forms/#option-height
25
+
26
+ ### Changed
27
+
28
+ - **Select, Combobox, ComboboxSelect, DropdownMenu and ContextMenu options**: their padding is `py-option-inset` instead of a fixed 6px. ([#647](https://github.com/botiverse/raft-design/pull/647))
29
+ - Why: see Option height above.
30
+ - Affects: padding 6px → 7px (mouse) / 8px (touch); two-line options 2px taller with a mouse, 4px on a touch screen; one-line options unchanged at 36px.
31
+ - Docs: https://raft-design.botiverse.build/patterns/forms/#option-height
32
+ - **PanelHeader, ThreadPanel, Drawer, Dialog, AlertDialog, Lightbox, NotificationCenter and Sidebar headers**: frameless icon buttons at the ends align by their icon box on the content margin. ([#650](https://github.com/botiverse/raft-design/pull/650))
33
+ - Why: see The ends of a header above.
34
+ - Affects: edge icon boxes at 16px (phone) / 20px (desktop, dialog) from the edge at every button size.
35
+ - Docs: https://raft-design.botiverse.build/foundations/alignment/#the-ends-of-a-header
36
+
37
+ ### Fixed
38
+
39
+ - **InputGroup**: a text addon is centered with flex, so older iOS Safari no longer sets its text at the top; long text still truncates. ([#646](https://github.com/botiverse/raft-design/pull/646))
40
+ - Why: the previous centering needed Safari 17.4.
41
+ - Docs: https://raft-design.botiverse.build/components/input-group/
42
+
43
+ ## [0.13.0](https://github.com/botiverse/raft-design/compare/v0.12.0...v0.13.0) (2026-10-21)
44
+
45
+ ### Design System
46
+
47
+ - **Row hover**: rows hover with their own lightest visible step, not the press overlay; a row whose menu is open is marked with one slightly stronger step. ([#641](https://github.com/botiverse/raft-design/pull/641), [#643](https://github.com/botiverse/raft-design/pull/643))
48
+ - Why: the press fill made hovered messages and list rows read as grey and heavy, and on a phone a long-press menu left no mark of which row it belonged to.
49
+ - Affects: hover `fill-pressed` 7.8% → `fill-row-hover` 4% (dark 3%), messages `fill-message-hover` 3% (dark 2.2%), menu-open `fill-row-open` 5% (dark 4%). Press and selection are unchanged; hover stays lighter than selection.
50
+ - Docs: https://raft-design.botiverse.build/foundations/states/#row-hover
51
+
52
+ ### Added
53
+
54
+ - **Tokens** `fill-row-hover`, `fill-message-hover` and `fill-row-open`, with utilities `row-hover-overlay` and `row-open-overlay` for rows on a card or a tinted surface. ([#641](https://github.com/botiverse/raft-design/pull/641), [#643](https://github.com/botiverse/raft-design/pull/643))
55
+ - Why: rows need a hover step lighter than press, and rows on a non-transparent surface need it as an overlay so the surface shows through.
56
+ - Affects: `fill-row-hover` 4% / 3%, `fill-message-hover` 3% / 2.2%, `fill-row-open` 5% / 4% (ink, light / dark).
57
+ - Docs: https://raft-design.botiverse.build/foundations/tokens/
58
+
59
+ ### Changed
60
+
61
+ - **Message, SidebarItem, List, Inbox, Search, Table and File rows**: hover with `fill-row-hover` (messages with `fill-message-hover`) instead of `fill-pressed`. ([#641](https://github.com/botiverse/raft-design/pull/641))
62
+ - Why: see Row hover above.
63
+ - Affects: hover background 7.8% → 4% (messages 3%).
64
+ - Docs: https://raft-design.botiverse.build/foundations/states/#row-hover
65
+ - **Rows with a menu open** (right-click, ⋯ menu, phone long-press): take `fill-row-open` while the menu is open; selection still wins. **Comment cards** hover with `fill-row-hover`. ([#643](https://github.com/botiverse/raft-design/pull/643))
66
+ - Why: the open menu needs one marker of which row it belongs to, including on a phone where there is no hover.
67
+ - Affects: menu-open background `fill-row-open` 5% / 4%.
68
+ - Docs: https://raft-design.botiverse.build/foundations/states/#row-hover
69
+ - **Select, Combobox**: `collisionPadding` defaults to 8px. ([#642](https://github.com/botiverse/raft-design/pull/642))
70
+ - Why: popups touched the screen edge on phones.
71
+ - Affects: popup inset from the screen edge, 8px.
72
+ - Docs: https://raft-design.botiverse.build/patterns/forms/#long-values-in-fields
73
+
74
+ ### Fixed
75
+
76
+ - **Field, FieldItem, InputGroup, SelectValue, ComboboxSelect, chips and options**: long values stay inside form fields on narrow screens. Labels, descriptions and errors wrap. A FieldItem label next to a checkbox, radio or switch shrinks. An InputGroup text prefix takes at most half the field and truncates. Values and chips truncate, with the full value as `title`. Options wrap to 2 lines, then clamp. Select and Combobox popups are never wider than the screen less 8px on each side. ([#642](https://github.com/botiverse/raft-design/pull/642))
77
+ - Why: long names and values could push fields and popups past the screen edge on phones, so every form field, picker and option list was checked and fixed together.
78
+ - Affects: InputGroup prefix `max-w-1/2`; options clamp at 2 lines; popups are never wider than the screen less 8px on each side, and on a desktop at most the wider of the trigger and `24rem`.
79
+ - Docs: https://raft-design.botiverse.build/patterns/forms/#long-values-in-fields
80
+
1
81
  ## [0.12.0](https://github.com/botiverse/raft-design/compare/v0.11.0...v0.12.0) (2026-10-21)
2
82
 
3
- ### Breaking
83
+ ### Breaking Changes
4
84
 
5
85
  - `ComboboxSelect` always takes typing on a phone, as on a desktop: tapping the field focuses it and typing filters; `create` and remote search work in the field. The phone-only pick-only mode and bottom sheet from 0.10.5 are gone, with their props `sheetThreshold`, `sheetTitle` and `messages.search` / `done` / `close`. A pick-only field uses `Select`. The list still picks its side once and doesn't flip while the keyboard opens. ([#635](https://github.com/botiverse/raft-design/pull/635))
6
86
 
package/dist/cn.mjs CHANGED
@@ -6,7 +6,10 @@ const twMerge = extendTailwindMerge({ extend: {
6
6
  "control",
7
7
  "control-cta",
8
8
  "control-icon",
9
- "control-hit"
9
+ "control-hit",
10
+ "option",
11
+ "option-inset",
12
+ "icon-inset"
10
13
  ] },
11
14
  classGroups: {
12
15
  shadow: [{ shadow: [
@@ -77,6 +77,15 @@
77
77
  text-overflow: ellipsis;
78
78
  white-space: nowrap;
79
79
  }
80
+ /* An option wraps onto a second line before it is cut off, so two long values
81
+ that share a beginning can still be told apart (forms.md#long-values-in-fields). */
82
+ .r-combobox-select__option .r-combobox-select__text {
83
+ display: -webkit-box;
84
+ -webkit-box-orient: vertical;
85
+ -webkit-line-clamp: 2;
86
+ white-space: normal;
87
+ overflow-wrap: anywhere;
88
+ }
80
89
  .r-combobox-select__actions {
81
90
  display: inline-flex;
82
91
  align-items: center;
@@ -162,8 +171,10 @@
162
171
  .r-combobox-select__option {
163
172
  box-sizing: border-box;
164
173
  width: 100%;
174
+ /* The option inset (styles.css, --spacing-option-inset): one line is 36px,
175
+ and a second line adds only its own height (forms.md#option-height). */
165
176
  min-height: 36px;
166
- padding: 0 10px;
177
+ padding: calc((36px - 1lh) / 2) 10px;
167
178
  display: flex;
168
179
  align-items: center;
169
180
  justify-content: space-between;
@@ -11,7 +11,10 @@
11
11
  hairline < seam < strong for line
12
12
  layer = places on the elevation ladder; fill = neutral interaction fills
13
13
  (resting/hover/active/disabled surfaces): fill-muted and fill-strong are
14
- opaque, the -pressed overlays are translucent ink laid over a surface.
14
+ opaque, the -pressed and -hover overlays are translucent ink laid over a
15
+ surface (a row hovers with fill-row-hover or fill-message-hover, and a
16
+ row with its menu open takes fill-row-open, all lighter than any
17
+ selection tint; press keeps fill-pressed).
15
18
  Families: {family}(-{slot})
16
19
  family = brand | attention | info | success | warning | danger | inactive
17
20
  slot = -strong (deep shade: readable text, rings) > bare (solid fill)
@@ -21,8 +24,8 @@
21
24
  an opted-in family always gets both)
22
25
  Washes: ink-{alpha%} (2..40) — neutral overlays, hairlines, hover fills.
23
26
  Opacity rule: layer tokens are opaque in every theme, except the two
24
- backdrops (overlays by contract); among fills only the -pressed overlays are
25
- translucent.
27
+ backdrops (overlays by contract); among fills only the -pressed and -hover
28
+ overlays are translucent.
26
29
  Interaction fills: transparent resting → ink-N directly; solid family fill →
27
30
  the family's -hover/-active pair; neutral or tinted surface → step up its ladder.
28
31
  Escape hatches: ink ramp for one-off washes; inline oklch() literals for
@@ -168,6 +171,19 @@
168
171
  --fill-strong: oklch(0.936 0.0006 17.62);
169
172
  --fill-frame-pressed: oklch(0.1813 0.005 17.62 / 0.122);
170
173
  --fill-pressed: oklch(0.1813 0.005 17.62 / 0.078);
174
+ /* Hover on a whole row (sidebar, list, inbox, search result, table, file row):
175
+ one perceptible step (OKLab dE about 2.8 in both modes), below every
176
+ selection tint, so text keeps its contrast. */
177
+ --fill-row-hover: oklch(0.1813 0.005 17.62 / 0.04);
178
+ /* Hover on a message: the lightest perceptible step (OKLab dE about 2.1 in
179
+ both modes), because a message is a large block someone reads with the
180
+ pointer resting on it. */
181
+ --fill-message-hover: oklch(0.1813 0.005 17.62 / 0.03);
182
+ /* Any row with its menu open (right-click, its menu button, press and hold):
183
+ one step over a hover (OKLab dE 3.4 to 4.3 on every row surface), because on
184
+ a phone, which has no hover, it is the only sign of which row the menu acts
185
+ on. Still under info-soft and brand-soft on every surface. */
186
+ --fill-row-open: oklch(0.1813 0.005 17.62 / 0.05);
171
187
  --line-strong: oklch(0.1813 0.005 17.62);
172
188
  --line-seam: oklch(0.1813 0.005 17.62 / 0.451);
173
189
  --line-hairline: oklch(0.1813 0.005 17.62 / 0.149);
@@ -326,6 +342,9 @@
326
342
  --fill-strong: oklch(0.3474 0.0051 106.66);
327
343
  --fill-frame-pressed: oklch(0.9843 0.004 106.47 / 0.078);
328
344
  --fill-pressed: oklch(0.9843 0.004 106.47 / 0.078);
345
+ --fill-row-hover: oklch(0.9843 0.004 106.47 / 0.03);
346
+ --fill-message-hover: oklch(0.9843 0.004 106.47 / 0.022);
347
+ --fill-row-open: oklch(0.9843 0.004 106.47 / 0.04);
329
348
  --line-strong: oklch(0.9211 0.004 106.48);
330
349
  --line-seam: oklch(0.9843 0.004 106.47 / 0.259);
331
350
  --line-hairline: oklch(0.9843 0.004 106.47 / 0.102);
@@ -407,6 +426,9 @@
407
426
  --fill-strong: oklch(0.3474 0.0051 106.66);
408
427
  --fill-frame-pressed: oklch(0.9843 0.004 106.47 / 0.078);
409
428
  --fill-pressed: oklch(0.9843 0.004 106.47 / 0.078);
429
+ --fill-row-hover: oklch(0.9843 0.004 106.47 / 0.03);
430
+ --fill-message-hover: oklch(0.9843 0.004 106.47 / 0.022);
431
+ --fill-row-open: oklch(0.9843 0.004 106.47 / 0.04);
410
432
  --line-strong: oklch(0.9211 0.004 106.48);
411
433
  --line-seam: oklch(0.9843 0.004 106.47 / 0.259);
412
434
  --line-hairline: oklch(0.9843 0.004 106.47 / 0.102);
package/dist/index.d.mts CHANGED
@@ -1201,6 +1201,7 @@ declare const inputGroup: import("tailwind-variants/lite").TVReturnType<{
1201
1201
  root: string[];
1202
1202
  addon: string[];
1203
1203
  text: string[];
1204
+ textInner: string[];
1204
1205
  control: string[];
1205
1206
  }, undefined, {
1206
1207
  align: {
@@ -1224,6 +1225,7 @@ declare const inputGroup: import("tailwind-variants/lite").TVReturnType<{
1224
1225
  root: string[];
1225
1226
  addon: string[];
1226
1227
  text: string[];
1228
+ textInner: string[];
1227
1229
  control: string[];
1228
1230
  }, import("tailwind-variants/lite").TVReturnTypeLike<{
1229
1231
  align: {
@@ -1247,6 +1249,7 @@ declare const inputGroup: import("tailwind-variants/lite").TVReturnType<{
1247
1249
  root: string[];
1248
1250
  addon: string[];
1249
1251
  text: string[];
1252
+ textInner: string[];
1250
1253
  control: string[];
1251
1254
  }>>;
1252
1255
  type InputGroupProps = ComponentPropsWithRef<"div">;
@@ -1254,7 +1257,7 @@ export declare function InputGroup({ className, ...props }: InputGroupProps): im
1254
1257
  type InputGroupAddonProps = ComponentPropsWithRef<"div"> & Pick<VariantProps<typeof inputGroup>, "align" | "variant">;
1255
1258
  declare function InputGroupAddonRoot({ align, variant, className, ...props }: InputGroupAddonProps): import("react").JSX.Element;
1256
1259
  type InputGroupAddonTextProps = ComponentPropsWithRef<"span"> & Pick<VariantProps<typeof inputGroup>, "align" | "variant">;
1257
- declare function TextAddon({ align, variant, className, ...props }: InputGroupAddonTextProps): import("react").JSX.Element;
1260
+ declare function TextAddon({ align, variant, className, ref, title, children, ...props }: InputGroupAddonTextProps): import("react").JSX.Element;
1258
1261
  export declare const InputGroupAddon: typeof InputGroupAddonRoot & {
1259
1262
  Text: typeof TextAddon;
1260
1263
  };
@@ -1315,7 +1318,7 @@ export declare function ComboboxCollection(props: ComboboxCollectionProps): impo
1315
1318
  type ComboboxChipsProps = StyledProps<Combobox$1.Chips.Props>;
1316
1319
  export declare function ComboboxChips({ className, ...props }: ComboboxChipsProps): import("react").JSX.Element;
1317
1320
  type ComboboxChipProps = StyledProps<Combobox$1.Chip.Props>;
1318
- export declare function ComboboxChip({ className, ...props }: ComboboxChipProps): import("react").JSX.Element;
1321
+ export declare function ComboboxChip({ className, ref, title, ...props }: ComboboxChipProps): import("react").JSX.Element;
1319
1322
  type ComboboxChipRemoveProps = StyledProps<Combobox$1.ChipRemove.Props>;
1320
1323
  export declare function ComboboxChipRemove({ className, children, ...props }: ComboboxChipRemoveProps): import("react").JSX.Element;
1321
1324
  //#endregion
@@ -1337,7 +1340,7 @@ export declare function Select<Value, Multiple extends boolean | undefined = fal
1337
1340
  type SelectTriggerProps = StyledProps<Select$1.Trigger.Props> & DataAttributes$1;
1338
1341
  export declare function SelectTrigger({ className, children, render, "data-invalid": dataInvalid, ...props }: SelectTriggerProps): import("react").JSX.Element;
1339
1342
  type SelectValueProps = StyledProps<Select$1.Value.Props>;
1340
- export declare function SelectValue({ className, ...props }: SelectValueProps): import("react").JSX.Element;
1343
+ export declare function SelectValue({ className, ref, title, ...props }: SelectValueProps): import("react").JSX.Element;
1341
1344
  type SelectIconProps = StyledProps<Select$1.Icon.Props>;
1342
1345
  export declare function SelectIcon({ className, children, ...props }: SelectIconProps): import("react").JSX.Element;
1343
1346
  type SelectPortalProps = Select$1.Portal.Props;
@@ -1357,7 +1360,7 @@ export declare function SelectItemLeading({ className, ...props }: SelectItemLea
1357
1360
  type SelectItemIndicatorProps = StyledProps<Select$1.ItemIndicator.Props>;
1358
1361
  export declare function SelectItemIndicator({ className, children, ...props }: SelectItemIndicatorProps): import("react").JSX.Element;
1359
1362
  type SelectItemTextProps = StyledProps<Select$1.ItemText.Props>;
1360
- export declare function SelectItemText({ className, id, ...props }: SelectItemTextProps): import("react").JSX.Element;
1363
+ export declare function SelectItemText({ className, id, ref, title, ...props }: SelectItemTextProps): import("react").JSX.Element;
1361
1364
  type SelectItemDescriptionProps = StyledProps<ComponentPropsWithRef<"span">>;
1362
1365
  /**
1363
1366
  * A second line under an option's label: what it means, not its name again.
@@ -3320,7 +3323,7 @@ declare const notificationCenter: import("tailwind-variants/lite").TVReturnType<
3320
3323
  triggerBell: string[];
3321
3324
  triggerDot: string[];
3322
3325
  popup: string[];
3323
- header: string[];
3326
+ header: (string | string[])[];
3324
3327
  title: string[];
3325
3328
  count: string[];
3326
3329
  scroller: string[];
@@ -3379,7 +3382,7 @@ declare const notificationCenter: import("tailwind-variants/lite").TVReturnType<
3379
3382
  triggerBell: string[];
3380
3383
  triggerDot: string[];
3381
3384
  popup: string[];
3382
- header: string[];
3385
+ header: (string | string[])[];
3383
3386
  title: string[];
3384
3387
  count: string[];
3385
3388
  scroller: string[];
@@ -3438,7 +3441,7 @@ declare const notificationCenter: import("tailwind-variants/lite").TVReturnType<
3438
3441
  triggerBell: string[];
3439
3442
  triggerDot: string[];
3440
3443
  popup: string[];
3441
- header: string[];
3444
+ header: (string | string[])[];
3442
3445
  title: string[];
3443
3446
  count: string[];
3444
3447
  scroller: string[];
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { cn } from "./cn.mjs";
2
- import { $ as MessageReactionTooltipNameText, $t as DropdownMenuShortcut, A as MessageTextSelectionToolbarButton, An as DialogClose, At as MessageItem, B as MessageReferenceUnavailable, Bn as DialogTrigger, Bt as DropdownMenuCheckboxItemIndicator, C as MessageQuotedPreviewUnavailable, Cn as TooltipContent, Ct as MessageItemEdited, D as MessageEmbedHeader, Dn as Badge, Dt as MessageItemAvatarSlot, E as MessageEmbedFooter, En as TooltipTrigger, Et as MessageItemSearchMatch, F as MessageReferenceChip, Fn as DialogNav, Ft as AvatarGroup, G as MessageReactionGlyph, Gn as useParentOverlayKind, Gt as DropdownMenuItemLabel, H as MessageReactionChip, Hn as buttonVariants, Ht as DropdownMenuFooter, I as MessageReferenceIndicator, In as DialogOverlay, It as AvatarGroupCount, J as MessageReactionQuickButton, Jn as useThemeScopeProps, Jt as DropdownMenuPortal, K as MessageReactionPickerContent, Kn as withOverlayLayer, Kt as DropdownMenuLabel, L as MessageReferenceLabel, Ln as DialogPortal, Lt as AvatarImage, M as MessageMultiSelect, Mn as DialogDescription, Mt as Avatar, N as MessageMultiSelectCheckbox, Nn as DialogFooter, Nt as AvatarBadge, O as messageEmbed, On as Dialog, Ot as MessageItemContinuationTime, P as MessageMultiSelectToolbar, Pn as DialogHeader, Pt as AvatarFallback, Q as MessageReactionTooltipName, Qt as DropdownMenuSeparator, R as MessageReferencePending, Rn as DialogSurface, Rt as DropdownMenu, S as MessageQuotedPreviewTimestamp, Sn as Tooltip, St as MessageItemContent, T as MessageEmbedContent, Tn as TooltipProvider, Tt as MessageItemFooter, U as MessageReactionCount, Un as Spinner, Ut as DropdownMenuItem, V as MessageReactionAddButton, Vn as Button, Vt as DropdownMenuContent, W as MessageReactionErrorIndicator, Wn as defaultNativeButton, Wt as DropdownMenuItemCount, X as MessageReactionTooltipContent, Xn as ThemeStateContext, Xt as DropdownMenuRadioItem, Y as MessageReactionQuickRow, Yn as useTheme, Yt as DropdownMenuRadioGroup, Z as MessageReactionTooltipMeta, Zn as tv, Zt as DropdownMenuRadioItemIndicator, _ as MessageQuotedPreviewContent, _n as ScrollAreaViewport, _t as MessageItemSenderButton, a as MessageListDateDivider, an as Panel, at as MessageItemSavedIndicator, b as MessageQuotedPreviewText, bn as CheckboxIndicator, bt as MessageItemAttachments, c as MessageList, cn as PanelHeaderRoot, d as MessageQuotedPreviewAttachments, dn as PanelSection, en as DropdownMenuSubmenu, et as MessageReactionTooltipNames, f as MessageQuotedPreviewAuthor, fn as ScrollArea, ft as MessageItemHeader, g as MessageQuotedPreviewChannel, gn as ScrollAreaThumb, gt as MessageItemSenderBadges, h as MessageQuotedPreviewAuthorName, hn as ScrollAreaScrollbar, ht as MessageItemSender, in as KbdGroup, it as MessageItemReadReceipt, j as MessageTextSelectionToolbarSeparator, jn as DialogContent, jt as searchHit, k as MessageTextSelectionToolbar, kn as DialogBody, kt as MessageItemGutter, l as MessageQuotedPreview, ln as PanelScrollContent, m as MessageQuotedPreviewAuthorMeta, mn as ScrollAreaCorner, mt as MessageItemMeta, nn as DropdownMenuTrigger, nt as MessageItemBadge, o as MessageListHistoryLimited, on as PanelBody, ot as MessageItemToolbar, pn as ScrollAreaContent, pt as MessageItemInlineStatus, q as MessageReactionPickerStatic, qn as resolveThemeScopeProps, qt as DropdownMenuPopup, rn as Kbd, rt as MessageItemBadgeSeparator, s as MessageListNotice, sn as PanelFooter, st as MessageItemToolbarButton, tn as DropdownMenuSubmenuTrigger, tt as MessageReactionTooltipSeparator, u as MessageQuotedPreviewAttachment, un as PanelScrollViewport, v as MessageQuotedPreviewHeader, vn as useMobileViewport, vt as MessageItemSenderGroup, w as MessageEmbed, wn as TooltipPortal, wt as MessageItemEmbedded, x as MessageQuotedPreviewThread, xn as PRESENCE_COLOR, xt as MessageItemBody, y as MessageQuotedPreviewLoading, yn as Checkbox, yt as MessageItemTime, z as MessageReferenceText, zn as DialogTitle, zt as DropdownMenuCheckboxItem } from "./message-list-CxawdwMF.mjs";
2
+ import { $ as MessageReactionTooltipNameText, $n as controlIconInset, $t as DropdownMenuSeparator, A as MessageTextSelectionToolbarButton, An as DialogBody, At as MessageItem, B as MessageReferenceUnavailable, Bn as DialogTitle, Bt as DropdownMenuCheckboxItem, C as MessageQuotedPreviewUnavailable, Cn as Tooltip, Ct as MessageItemEdited, D as MessageEmbedHeader, Dn as TooltipTrigger, Dt as MessageItemAvatarSlot, E as MessageEmbedFooter, En as TooltipProvider, Et as MessageItemSearchMatch, F as MessageReferenceChip, Fn as DialogHeader, Ft as AvatarGroup, G as MessageReactionGlyph, Gn as defaultNativeButton, Gt as DropdownMenuItemCount, H as MessageReactionChip, Hn as Button, Ht as DropdownMenuContent, I as MessageReferenceIndicator, In as DialogNav, It as AvatarGroupCount, J as MessageReactionQuickButton, Jn as resolveThemeScopeProps, Jt as DropdownMenuPopup, K as MessageReactionPickerContent, Kn as useParentOverlayKind, Kt as DropdownMenuItemLabel, L as MessageReferenceLabel, Ln as DialogOverlay, Lt as AvatarImage, M as MessageMultiSelect, Mn as DialogContent, Mt as Avatar, N as MessageMultiSelectCheckbox, Nn as DialogDescription, Nt as AvatarBadge, O as messageEmbed, On as Badge, Ot as MessageItemContinuationTime, P as MessageMultiSelectToolbar, Pn as DialogFooter, Pt as AvatarFallback, Q as MessageReactionTooltipName, Qn as tv, Qt as DropdownMenuRadioItemIndicator, R as MessageReferencePending, Rn as DialogPortal, Rt as useOverflowTitle, S as MessageQuotedPreviewTimestamp, Sn as PRESENCE_COLOR, St as MessageItemContent, T as MessageEmbedContent, Tn as TooltipPortal, Tt as MessageItemFooter, U as MessageReactionCount, Un as buttonVariants, Ut as DropdownMenuFooter, V as MessageReactionAddButton, Vn as DialogTrigger, Vt as DropdownMenuCheckboxItemIndicator, W as MessageReactionErrorIndicator, Wn as Spinner, Wt as DropdownMenuItem, X as MessageReactionTooltipContent, Xn as useTheme, Xt as DropdownMenuRadioGroup, Y as MessageReactionQuickRow, Yn as useThemeScopeProps, Yt as DropdownMenuPortal, Z as MessageReactionTooltipMeta, Zn as ThemeStateContext, Zt as DropdownMenuRadioItem, _ as MessageQuotedPreviewContent, _n as ScrollAreaThumb, _t as MessageItemSenderButton, a as MessageListDateDivider, an as KbdGroup, at as MessageItemSavedIndicator, b as MessageQuotedPreviewText, bn as Checkbox, bt as MessageItemAttachments, c as MessageList, cn as PanelFooter, d as MessageQuotedPreviewAttachments, dn as PanelScrollViewport, en as DropdownMenuShortcut, er as headEdgePull, et as MessageReactionTooltipNames, f as MessageQuotedPreviewAuthor, fn as PanelSection, ft as MessageItemHeader, g as MessageQuotedPreviewChannel, gn as ScrollAreaScrollbar, gt as MessageItemSenderBadges, h as MessageQuotedPreviewAuthorName, hn as ScrollAreaCorner, ht as MessageItemSender, in as Kbd, it as MessageItemReadReceipt, j as MessageTextSelectionToolbarSeparator, jn as DialogClose, jt as searchHit, k as MessageTextSelectionToolbar, kn as Dialog, kt as MessageItemGutter, l as MessageQuotedPreview, ln as PanelHeaderRoot, m as MessageQuotedPreviewAuthorMeta, mn as ScrollAreaContent, mt as MessageItemMeta, nn as DropdownMenuSubmenuTrigger, nt as MessageItemBadge, o as MessageListHistoryLimited, on as Panel, ot as MessageItemToolbar, pn as ScrollArea, pt as MessageItemInlineStatus, q as MessageReactionPickerStatic, qn as withOverlayLayer, qt as DropdownMenuLabel, rn as DropdownMenuTrigger, rt as MessageItemBadgeSeparator, s as MessageListNotice, sn as PanelBody, st as MessageItemToolbarButton, tn as DropdownMenuSubmenu, tr as headEdges, tt as MessageReactionTooltipSeparator, u as MessageQuotedPreviewAttachment, un as PanelScrollContent, v as MessageQuotedPreviewHeader, vn as ScrollAreaViewport, vt as MessageItemSenderGroup, w as MessageEmbed, wn as TooltipContent, wt as MessageItemEmbedded, x as MessageQuotedPreviewThread, xn as CheckboxIndicator, xt as MessageItemBody, y as MessageQuotedPreviewLoading, yn as useMobileViewport, yt as MessageItemTime, z as MessageReferenceText, zn as DialogSurface, zt as DropdownMenu } from "./message-list-YsCZpkr7.mjs";
3
3
  import { Dialog as Dialog$1 } from "@base-ui/react/dialog";
4
4
  import { AlertTriangle, Bell, Check, CheckCircle2, ChevronDown, ChevronLeft, ChevronRight, Copy, FileText, Image, Info, 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";
@@ -40,7 +40,11 @@ const alertDialog = tv({
40
40
  "overflow-clip rounded-dialog bg-layer-popover text-foreground",
41
41
  "shadow-float ring-1 ring-line-seam"
42
42
  ],
43
- header: ["grid grid-cols-[minmax(0,1fr)_max-content] items-center min-w-0", "gap-2 px-5 pt-5"],
43
+ header: [
44
+ "grid grid-cols-[minmax(0,1fr)_max-content] items-center min-w-0",
45
+ "gap-2 px-5 pt-5",
46
+ headEdgePull
47
+ ],
44
48
  body: [
45
49
  "min-w-0",
46
50
  "px-5 pt-1.5 pb-5 first:pt-5",
@@ -81,7 +85,7 @@ function AlertDialogClose({ className, variant, size, children, "aria-label": ar
81
85
  variant: variant ?? "ghost",
82
86
  size: size ?? "icon-md",
83
87
  "aria-label": ariaLabel ?? "Close",
84
- className: cn("-my-1.5 -mr-1.5", className)
88
+ className: cn("-my-1.5", className)
85
89
  }),
86
90
  ...props,
87
91
  children: children ?? /* @__PURE__ */ jsx(X, { className: "size-4.5" })
@@ -172,30 +176,21 @@ function AlertDialogCancel({ className, variant, size, ...props }) {
172
176
  //#region src/components/panel/panel-header.recipe.ts
173
177
  /**
174
178
  * Where a head's edge controls sit, shared by every head (panel, section,
175
- * drawer, thread). Phone: a filled thing (a sticker, a pill) puts its shape
176
- * on 16. A ghost button has no shape, so its empty 44px box overhangs, and
177
- * the two edge buttons mirror each other: the last action (6px padding,
178
- * `panelHeadActions`' -3px) and a leading Back (16px padding, -13px) both sit
179
- * 3px from their edge. Their 22px icon frames are then 14px in and every
180
- * glyph's centre 25px in, whatever the glyph (an arrow, a gear, a narrow ⋮);
181
- * aligning ink instead would move each button by its glyph's own side
182
- * bearing. A Button straight in the head (ghost ones are PanelActions) is a
183
- * filled control, so its shape ends on the content edge: 20 / 16.
179
+ * drawer, thread): see lib/head-edges.ts. Both sides pad by the content
180
+ * margin, 20px on a desktop and 16px on a phone, so a filled thing (a sticker,
181
+ * a pill, a filled Button) puts its shape on it, and the first and last
182
+ * frameless icon button are pulled out by their icon inset so their icon box
183
+ * lands on it: 20px on a desktop and on a touch screen with the desktop
184
+ * layout, 16px on a phone, mirrored at the two ends.
184
185
  */
185
- const panelHeadEdges = [
186
- "pr-3 pl-5 max-md:pr-1.5 max-md:pl-4",
187
- "max-md:[&>[data-slot=panel-action]:first-child]:-ml-[13px]",
188
- "[&>[data-slot=button]:last-child]:mr-2 max-md:[&>[data-slot=button]:last-child]:mr-2.5"
189
- ];
186
+ const panelHeadEdges = headEdges;
190
187
  /**
191
- * A head's trailing actions. The last glyph ends on the head's edge: 20px on
192
- * desktop (12px of padding, 1px here, the 32px button's 7px glyph inset); on
193
- * a phone 14px in (44px buttons, 11px inset), mirroring a leading Back. The
194
- * margin and gap follow the button's size, not the layout: a touch screen
195
- * from 768px has the desktop head with 44px buttons, so -3px puts its last
196
- * glyph on 20px too.
188
+ * A head's trailing actions, 2px apart (4px on a desktop). The group declares
189
+ * its PanelActions' icon inset, so the head pulls the group, not its last
190
+ * button: an action hidden at some width (a thread's Close below 1024px) then
191
+ * leaves the next one on the edge.
197
192
  */
198
- const panelHeadActions = "flex shrink-0 items-center gap-0.5 mr-[-3px] md:pointer-fine:mr-px md:pointer-fine:gap-1";
193
+ const panelHeadActions = ["flex shrink-0 items-center gap-0.5 md:pointer-fine:gap-1", controlIconInset];
199
194
  /**
200
195
  * `header` and `sectionHeader` share one bar, expressed through
201
196
  * `compoundSlots`: a 56px row (the shell header height) with a seam under it.
@@ -336,7 +331,7 @@ const drawer = tv({
336
331
  "before:rounded-[1px] before:bg-line-seam"
337
332
  ],
338
333
  close: ["shrink-0"],
339
- closeIcon: ["size-5.5"],
334
+ closeIcon: ["size-control-icon"],
340
335
  header: ["flex flex-col", "items-stretch gap-1 px-4 pt-4 pb-2 text-left"],
341
336
  body: ["flex flex-1 flex-col", "items-stretch px-4 pt-2 pb-4 text-left"],
342
337
  footer: ["mt-auto py-5", "px-4 pt-3 pb-4"],
@@ -1056,9 +1051,9 @@ const listRow = tv({
1056
1051
  },
1057
1052
  interactive: {
1058
1053
  true: { root: [
1059
- "not-disabled:not-aria-disabled:not-data-selected:hover:press-overlay",
1054
+ "not-disabled:not-aria-disabled:not-data-selected:hover:row-hover-overlay",
1060
1055
  "not-disabled:not-aria-disabled:not-data-selected:active:press-overlay",
1061
- "not-data-selected:data-popup-open:press-overlay"
1056
+ "not-data-selected:data-popup-open:row-open-overlay not-disabled:not-aria-disabled:not-data-selected:data-popup-open:hover:row-open-overlay"
1062
1057
  ] },
1063
1058
  false: {}
1064
1059
  },
@@ -1662,6 +1657,7 @@ const sidebarRecipe = tv({ slots: {
1662
1657
  "h-(--shell-header-height,56px) shrink-0 gap-2 border-b border-line-seam px-5",
1663
1658
  "bg-layer-sidebar text-foreground",
1664
1659
  "max-md:bg-layer-frame max-md:px-4 max-md:text-foreground-on-frame",
1660
+ headEdgePull,
1665
1661
  "[@media(max-height:600px)]:h-12"
1666
1662
  ],
1667
1663
  sidebarHeaderMask: ["pointer-events-none absolute inset-x-0 top-0 -z-10 hidden"],
@@ -1972,7 +1968,7 @@ const sidebarItemRecipe = tv({
1972
1968
  },
1973
1969
  variants: {
1974
1970
  interactive: {
1975
- true: { sidebarItem: ["hover:bg-fill-pressed active:bg-fill-pressed not-data-active:data-popup-open:bg-fill-pressed", "focus-visible:shadow-focus"] },
1971
+ true: { sidebarItem: ["hover:bg-fill-row-hover active:bg-fill-pressed not-data-active:data-popup-open:bg-fill-row-open", "focus-visible:shadow-focus"] },
1976
1972
  false: { sidebarItem: "cursor-default" }
1977
1973
  },
1978
1974
  wrap: {
@@ -2626,9 +2622,9 @@ const contextMenu = tv({ slots: {
2626
2622
  "shadow-float ring-1 ring-line-seam"
2627
2623
  ],
2628
2624
  item: [
2629
- "flex w-full cursor-default select-none items-center [@media(max-height:600px)]:py-1",
2625
+ "flex w-full cursor-default select-none items-center",
2630
2626
  "text-left outline-none transition-colors",
2631
- "min-h-9 gap-2.5 rounded-control px-2.5 py-1.5",
2627
+ "min-h-option gap-2.5 rounded-control px-2.5 py-option-inset",
2632
2628
  "font-sans text-callout font-medium text-foreground",
2633
2629
  "hover:bg-brand-soft data-highlighted:bg-brand-soft",
2634
2630
  "data-checked:font-bold",
@@ -2639,7 +2635,7 @@ const contextMenu = tv({ slots: {
2639
2635
  radioItem: [
2640
2636
  "flex w-full cursor-default select-none items-center",
2641
2637
  "text-left outline-none transition-colors",
2642
- "min-h-9 gap-2.5 rounded-control px-2.5 py-1.5",
2638
+ "min-h-option gap-2.5 rounded-control px-2.5 py-option-inset",
2643
2639
  "font-sans text-callout font-medium text-foreground",
2644
2640
  "hover:bg-brand-soft data-highlighted:bg-brand-soft",
2645
2641
  "data-checked:font-bold",
@@ -2648,9 +2644,9 @@ const contextMenu = tv({ slots: {
2648
2644
  "[&>svg]:text-foreground-muted data-disabled:[&>svg]:text-foreground-disabled"
2649
2645
  ],
2650
2646
  submenuTrigger: [
2651
- "flex w-full cursor-default select-none items-center [@media(max-height:600px)]:py-1",
2647
+ "flex w-full cursor-default select-none items-center",
2652
2648
  "text-left outline-none transition-colors",
2653
- "min-h-9 gap-2.5 rounded-control px-2.5 py-1.5",
2649
+ "min-h-option gap-2.5 rounded-control px-2.5 py-option-inset",
2654
2650
  "font-sans text-callout font-medium text-foreground",
2655
2651
  "hover:bg-brand-soft data-highlighted:bg-brand-soft data-popup-open:bg-brand-soft",
2656
2652
  "data-disabled:pointer-events-none data-disabled:text-foreground-disabled",
@@ -2888,7 +2884,8 @@ const lightbox = tv({ slots: {
2888
2884
  "pt-[calc(env(safe-area-inset-top,0px)+--spacing(3))]",
2889
2885
  "pl-[calc(env(safe-area-inset-left,0px)+--spacing(4))]",
2890
2886
  "pr-[calc(env(safe-area-inset-right,0px)+--spacing(4))]",
2891
- "bg-transparent shadow-none"
2887
+ "bg-transparent shadow-none",
2888
+ headEdgePull
2892
2889
  ],
2893
2890
  toolbar: [
2894
2891
  "pointer-events-auto shrink-0 flex items-center gap-3",
@@ -2896,7 +2893,8 @@ const lightbox = tv({ slots: {
2896
2893
  "pl-[calc(16px+env(safe-area-inset-left,0px))] pr-[calc(16px+env(safe-area-inset-right,0px))]",
2897
2894
  "**:data-lightbox-close:static **:data-lightbox-close:inset-auto",
2898
2895
  "**:data-lightbox-close:top-auto **:data-lightbox-close:right-auto",
2899
- "border-b border-line-seam bg-layer-canvas shadow-none"
2896
+ "border-b border-line-seam bg-layer-canvas shadow-none",
2897
+ headEdgePull
2900
2898
  ],
2901
2899
  headerMeta: ["flex min-w-0 flex-1 items-center gap-1.5"],
2902
2900
  headerLabel: [
@@ -2905,7 +2903,11 @@ const lightbox = tv({ slots: {
2905
2903
  "font-sans text-caption font-semibold normal-case tracking-normal text-foreground-muted"
2906
2904
  ],
2907
2905
  title: ["min-w-0 flex-1 truncate", "py-0 font-sans text-headline font-bold text-foreground"],
2908
- actions: ["flex shrink-0 items-center", "gap-1"],
2906
+ actions: [
2907
+ "flex shrink-0 items-center",
2908
+ "gap-1",
2909
+ "[&>[data-icon-inset]:last-child]:-mr-icon-inset"
2910
+ ],
2909
2911
  stage: ["pointer-events-none relative flex min-h-0 flex-1 items-center justify-center overflow-hidden"],
2910
2912
  media: ["pointer-events-auto max-h-[80vh] max-w-[86vw] border-0 bg-transparent shadow-none"],
2911
2913
  navigationButton: [
@@ -3686,17 +3688,26 @@ const field = tv({
3686
3688
  root: ["group/field flex flex-col gap-1.5"],
3687
3689
  item: [
3688
3690
  "group/field-item flex items-start gap-2",
3691
+ "*:min-w-0",
3689
3692
  "*:data-[slot=checkbox]:mt-[calc((1lh-1.25rem)/2)]",
3690
3693
  "*:data-[slot=radio-group-item]:mt-[calc((1lh-1.25rem)/2)]",
3691
3694
  "*:data-[slot=switch]:mt-[calc((1lh-1.875rem)/2)]"
3692
3695
  ],
3693
3696
  label: [
3694
- "block",
3697
+ "block wrap-anywhere",
3695
3698
  "group-data-disabled/field:text-foreground-disabled",
3696
3699
  "font-sans font-semibold text-foreground-muted"
3697
3700
  ],
3698
- description: ["group-data-disabled/field:text-foreground-disabled", "text-caption text-foreground-hint"],
3699
- error: ["group-data-disabled/field:text-foreground-disabled", "text-caption font-semibold text-danger-strong"]
3701
+ description: [
3702
+ "wrap-anywhere",
3703
+ "group-data-disabled/field:text-foreground-disabled",
3704
+ "text-caption text-foreground-hint"
3705
+ ],
3706
+ error: [
3707
+ "wrap-anywhere",
3708
+ "group-data-disabled/field:text-foreground-disabled",
3709
+ "text-caption font-semibold text-danger-strong"
3710
+ ]
3700
3711
  },
3701
3712
  variants: { size: {
3702
3713
  sm: {
@@ -3786,11 +3797,12 @@ const inputGroup = tv({
3786
3797
  "[&>svg:not([class*='size-'])]:size-4.5"
3787
3798
  ],
3788
3799
  text: [
3789
- "flex shrink-0 items-center px-3",
3790
- "select-none whitespace-nowrap",
3800
+ "flex min-w-0 max-w-1/2 shrink-0 items-center px-3",
3801
+ "select-none",
3791
3802
  "font-mono text-mono-code",
3792
3803
  "text-(--ig-text-fg,var(--ig-text-fg-default,var(--foreground-hint)))"
3793
3804
  ],
3805
+ textInner: ["min-w-0 truncate"],
3794
3806
  control: [
3795
3807
  "min-w-0 flex-1 bg-transparent pl-3 outline-none",
3796
3808
  "pr-(--ig-control-pr,0.75rem)",
@@ -3895,18 +3907,26 @@ function InputGroupAddonRoot({ align = "inline-start", variant = "default", clas
3895
3907
  ...props
3896
3908
  });
3897
3909
  }
3898
- function TextAddon({ align = "inline-start", variant = "default", className, ...props }) {
3899
- const { text } = inputGroup({
3910
+ function TextAddon({ align = "inline-start", variant = "default", className, ref, title, children, ...props }) {
3911
+ const { text, textInner } = inputGroup({
3900
3912
  align,
3901
3913
  variant
3902
3914
  });
3915
+ const overflowRef = useOverflowTitle(ref, title);
3903
3916
  return /* @__PURE__ */ jsx("span", {
3917
+ ref: overflowRef,
3918
+ title,
3904
3919
  "data-slot": "input-group-text",
3905
3920
  "data-align": align,
3906
3921
  "data-variant": variant,
3907
3922
  "data-input-group-trim": variant === "default" && align === "inline-end" ? align : void 0,
3908
3923
  className: text({ className }),
3909
- ...props
3924
+ ...props,
3925
+ children: /* @__PURE__ */ jsx("span", {
3926
+ "data-slot": "input-group-text-value",
3927
+ className: textInner(),
3928
+ children
3929
+ })
3910
3930
  });
3911
3931
  }
3912
3932
  const InputGroupAddon = Object.assign(InputGroupAddonRoot, { Text: TextAddon });
@@ -3977,6 +3997,7 @@ const combobox = tv({
3977
3997
  positioner: ["isolate z-50 outline-none"],
3978
3998
  content: [
3979
3999
  "h-auto max-h-(--available-height) min-w-[220px] overflow-hidden",
4000
+ "max-w-[min(var(--available-width),max(var(--anchor-width),24rem))]",
3980
4001
  "outline-none",
3981
4002
  "rounded-container bg-layer-popover text-foreground",
3982
4003
  "shadow-float ring-1 ring-line-seam"
@@ -3987,8 +4008,8 @@ const combobox = tv({
3987
4008
  item: [
3988
4009
  "flex w-full shrink-0 cursor-default select-none items-center justify-between overflow-hidden",
3989
4010
  "text-left outline-none transition-colors",
3990
- "min-h-9 gap-2.5 rounded-control px-2.5 py-0",
3991
- "font-sans text-callout font-medium text-foreground",
4011
+ "min-h-option gap-2.5 rounded-control px-2.5 py-option-inset",
4012
+ "font-sans text-callout font-medium text-foreground wrap-anywhere",
3992
4013
  "bg-transparent hover:bg-brand-soft data-highlighted:bg-brand-soft",
3993
4014
  "data-selected:font-bold",
3994
4015
  "data-disabled:pointer-events-none data-disabled:text-foreground-disabled",
@@ -4016,12 +4037,13 @@ const combobox = tv({
4016
4037
  "transition-shadow duration-90 ease-out has-focus-visible:shadow-focus"
4017
4038
  ],
4018
4039
  chip: [
4019
- "inline-flex items-center",
4040
+ "inline-flex min-w-0 max-w-full items-center overflow-hidden",
4041
+ "[&>:not([data-slot=combobox-chip-remove])]:min-w-0 [&>:not([data-slot=combobox-chip-remove])]:truncate",
4020
4042
  "h-7 gap-1.5 rounded-chip bg-fill-muted pr-0.5 pl-2",
4021
4043
  "text-footnote font-semibold text-foreground"
4022
4044
  ],
4023
4045
  chipRemove: [
4024
- "inline-flex items-center justify-center outline-none transition-colors",
4046
+ "inline-flex shrink-0 items-center justify-center outline-none transition-colors",
4025
4047
  "size-[22px] rounded-sticker text-foreground-hint",
4026
4048
  "hover:bg-fill-pressed active:bg-fill-pressed focus-visible:shadow-focus",
4027
4049
  "[&>svg]:size-3.5"
@@ -4202,7 +4224,7 @@ function ComboboxSeparator({ className, ...props }) {
4202
4224
  ...props
4203
4225
  });
4204
4226
  }
4205
- function ComboboxContent({ align = "start", alignOffset = 0, anchor, arrowPadding, collisionAvoidance, collisionBoundary, collisionPadding, disableAnchorTracking = true, positionMethod = "fixed", side = "bottom", sideOffset, sticky, portalProps, className, initialFocus = false, finalFocus = false, ...props }) {
4227
+ function ComboboxContent({ align = "start", alignOffset = 0, anchor, arrowPadding, collisionAvoidance, collisionBoundary, collisionPadding = 8, disableAnchorTracking = true, positionMethod = "fixed", side = "bottom", sideOffset, sticky, portalProps, className, initialFocus = false, finalFocus = false, ...props }) {
4206
4228
  const { positioner, content } = combobox();
4207
4229
  return /* @__PURE__ */ jsx(ComboboxPortal, {
4208
4230
  ...portalProps,
@@ -4322,9 +4344,12 @@ function ComboboxChips({ className, ...props }) {
4322
4344
  ...props
4323
4345
  });
4324
4346
  }
4325
- function ComboboxChip({ className, ...props }) {
4347
+ function ComboboxChip({ className, ref, title, ...props }) {
4326
4348
  const { chip } = combobox();
4349
+ const overflowRef = useOverflowTitle(ref, title);
4327
4350
  return /* @__PURE__ */ jsx(Combobox$1.Chip, {
4351
+ ref: overflowRef,
4352
+ title,
4328
4353
  "data-slot": "combobox-chip",
4329
4354
  className: chip({ className }),
4330
4355
  ...props
@@ -4403,6 +4428,7 @@ const select = tv({
4403
4428
  positioner: ["isolate z-50 outline-none"],
4404
4429
  content: [
4405
4430
  "relative max-h-(--available-height) min-w-[max(var(--anchor-width),220px)] overflow-hidden",
4431
+ "max-w-[min(var(--available-width),max(var(--anchor-width),24rem))]",
4406
4432
  "outline-none",
4407
4433
  "rounded-container bg-layer-popover p-1.5 text-foreground",
4408
4434
  "shadow-float ring-1 ring-line-seam"
@@ -4411,7 +4437,7 @@ const select = tv({
4411
4437
  item: [
4412
4438
  "flex w-full shrink-0 cursor-default select-none items-center justify-between overflow-hidden",
4413
4439
  "text-left outline-none transition-colors",
4414
- "min-h-9 flex-wrap gap-x-2.5 gap-y-0.5 rounded-control px-2.5 py-1.5",
4440
+ "min-h-option flex-wrap gap-x-2.5 gap-y-0.5 rounded-control px-2.5 py-option-inset",
4415
4441
  "text-callout font-medium text-foreground",
4416
4442
  "bg-transparent hover:bg-brand-soft data-highlighted:bg-brand-soft",
4417
4443
  "data-selected:font-bold data-selected:**:data-[slot='select-item-text']:font-bold",
@@ -4429,9 +4455,9 @@ const select = tv({
4429
4455
  "[&>svg:not([class*='stroke-'])]:stroke-2"
4430
4456
  ],
4431
4457
  itemIndicator: ["pointer-events-none ml-auto flex shrink-0 items-center justify-center", "text-foreground [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:stroke-2 [&>svg_*]:stroke-2"],
4432
- itemText: ["min-w-0 flex-1 truncate"],
4458
+ itemText: ["min-w-0 flex-1 line-clamp-2 wrap-anywhere"],
4433
4459
  itemDescription: [
4434
- "order-last min-w-0 basis-full",
4460
+ "order-last min-w-0 basis-full wrap-anywhere",
4435
4461
  "[[data-slot=select-item-leading]~&]:ps-[calc(--spacing(4)+--spacing(2.5))]",
4436
4462
  "font-sans text-caption font-normal text-foreground-hint"
4437
4463
  ],
@@ -4510,9 +4536,12 @@ function SelectTrigger({ className, children, render, "data-invalid": dataInvali
4510
4536
  })
4511
4537
  });
4512
4538
  }
4513
- function SelectValue({ className, ...props }) {
4539
+ function SelectValue({ className, ref, title, ...props }) {
4514
4540
  const { value } = select();
4541
+ const overflowRef = useOverflowTitle(ref, title);
4515
4542
  return /* @__PURE__ */ jsx(Select$1.Value, {
4543
+ ref: overflowRef,
4544
+ title,
4516
4545
  "data-slot": "select-value",
4517
4546
  className: value({ className }),
4518
4547
  ...props
@@ -4545,7 +4574,7 @@ function SelectPopup({ className, ...props }) {
4545
4574
  ...props
4546
4575
  });
4547
4576
  }
4548
- function SelectContent({ align = "start", alignItemWithTrigger = false, alignOffset = 0, anchor, arrowPadding, collisionAvoidance, collisionBoundary, collisionPadding, disableAnchorTracking = true, positionMethod = "fixed", side = "bottom", sideOffset, sticky, portalProps, className, ...props }) {
4577
+ function SelectContent({ align = "start", alignItemWithTrigger = false, alignOffset = 0, anchor, arrowPadding, collisionAvoidance, collisionBoundary, collisionPadding = 8, disableAnchorTracking = true, positionMethod = "fixed", side = "bottom", sideOffset, sticky, portalProps, className, ...props }) {
4549
4578
  const { positioner, content } = select();
4550
4579
  const positionerNode = /* @__PURE__ */ jsx(Select$1.Positioner, {
4551
4580
  align,
@@ -4623,8 +4652,9 @@ function SelectItemIndicator({ className, children, ...props }) {
4623
4652
  children: children ?? defaultIndicator
4624
4653
  });
4625
4654
  }
4626
- function SelectItemText({ className, id, ...props }) {
4655
+ function SelectItemText({ className, id, ref, title, ...props }) {
4627
4656
  const { itemText } = select();
4657
+ const overflowRef = useOverflowTitle(ref, title);
4628
4658
  const generatedId = useId();
4629
4659
  const textId = id ?? generatedId;
4630
4660
  const setTextId = useContext(SelectItemPartsContext)?.setTextId;
@@ -4633,6 +4663,8 @@ function SelectItemText({ className, id, ...props }) {
4633
4663
  return () => setTextId?.(void 0);
4634
4664
  }, [setTextId, textId]);
4635
4665
  return /* @__PURE__ */ jsx(Select$1.ItemText, {
4666
+ ref: overflowRef,
4667
+ title,
4636
4668
  id: textId,
4637
4669
  "data-slot": "select-item-text",
4638
4670
  className: itemText({ className }),
@@ -8063,8 +8095,13 @@ function OtterIndicator({ status, className, ...props }) {
8063
8095
  const table = tv({ slots: {
8064
8096
  root: ["w-full border-collapse caption-bottom", "font-sans text-callout text-foreground"],
8065
8097
  header: [],
8066
- body: ["[&>tr:last-child>td]:border-b-0 [&>tr:last-child>th]:border-b-0", "[&>tr]:hover:bg-fill-pressed"],
8067
- row: ["aria-selected:bg-info-soft aria-selected:hover:bg-info-soft", "data-[selected=true]:bg-info-soft data-[selected=true]:hover:bg-info-soft"],
8098
+ body: ["[&>tr:last-child>td]:border-b-0 [&>tr:last-child>th]:border-b-0", "[&>tr]:hover:bg-fill-row-hover"],
8099
+ row: [
8100
+ "not-aria-selected:not-data-[selected=true]:data-popup-open:bg-fill-row-open",
8101
+ "not-aria-selected:not-data-[selected=true]:has-data-popup-open:bg-fill-row-open",
8102
+ "aria-selected:bg-info-soft aria-selected:hover:bg-info-soft",
8103
+ "data-[selected=true]:bg-info-soft data-[selected=true]:hover:bg-info-soft"
8104
+ ],
8068
8105
  head: [
8069
8106
  "text-left whitespace-nowrap",
8070
8107
  "border-b border-line-hairline px-3 py-2",
@@ -8425,7 +8462,8 @@ const files = tv({ slots: {
8425
8462
  "group/file-row flex w-full min-w-0 gap-3 px-3",
8426
8463
  "transition-colors",
8427
8464
  "items-center rounded-container border border-line-hairline bg-layer-card py-2.5 pr-1.5 pl-2.5 shadow-none",
8428
- "[@media(hover:hover)]:has-[[data-slot=file-row-open]:hover]:press-overlay has-[[data-slot=file-row-open]:active]:press-overlay",
8465
+ "[@media(hover:hover)]:has-[[data-slot=file-row-open]:hover]:row-hover-overlay has-[[data-slot=file-row-open]:active]:press-overlay",
8466
+ "has-data-popup-open:row-open-overlay",
8429
8467
  "has-[[data-slot=file-row-open]:focus-visible]:shadow-focus"
8430
8468
  ],
8431
8469
  rowOpen: [
@@ -9857,7 +9895,11 @@ const notificationCenter = tv({
9857
9895
  "rounded-container bg-layer-popover text-foreground",
9858
9896
  "shadow-float ring-1 ring-line-seam"
9859
9897
  ],
9860
- header: ["flex items-center px-4", "min-h-13 gap-2 border-b border-line-seam py-0 pr-2 pl-4"],
9898
+ header: [
9899
+ "flex items-center px-4",
9900
+ "min-h-13 gap-2 border-b border-line-seam py-0",
9901
+ headEdgePull
9902
+ ],
9861
9903
  title: ["font-sans text-headline text-foreground"],
9862
9904
  count: ["ml-auto", "font-mono text-mono-meta text-foreground-hint"],
9863
9905
  scroller: ["min-h-0 flex-1 overflow-y-auto overscroll-contain"],
@@ -11101,7 +11143,8 @@ const panelActionBase = tv({ base: [
11101
11143
  "text-foreground",
11102
11144
  "hover:bg-fill-pressed active:bg-fill-pressed",
11103
11145
  "focus-visible:shadow-focus",
11104
- "[&>svg]:size-control-icon"
11146
+ "[&>svg]:size-control-icon",
11147
+ controlIconInset
11105
11148
  ] });
11106
11149
  const panelAction = tv({
11107
11150
  extend: panelActionBase,
@@ -11133,6 +11176,7 @@ function PanelAction({ className, render, nativeButton, ...props }) {
11133
11176
  nativeButton: nativeButton ?? defaultNativeButton(render),
11134
11177
  type: "button",
11135
11178
  "data-slot": "panel-action",
11179
+ "data-icon-inset": "",
11136
11180
  className: panelAction({ className })
11137
11181
  });
11138
11182
  }
@@ -11509,6 +11553,7 @@ function PanelActions({ className, render, ...props }) {
11509
11553
  const { actions } = panelHeader();
11510
11554
  const defaultProps = {
11511
11555
  "data-slot": "panel-actions",
11556
+ "data-icon-inset": "",
11512
11557
  className: actions({ className })
11513
11558
  };
11514
11559
  return useRender({
@@ -11524,6 +11569,7 @@ function PanelToggleAction({ className, ...props }) {
11524
11569
  ...props,
11525
11570
  type: "button",
11526
11571
  "data-slot": "panel-toggle-action",
11572
+ "data-icon-inset": "",
11527
11573
  className: panelToggleAction({ className })
11528
11574
  });
11529
11575
  }
@@ -11745,6 +11791,7 @@ function ThreadPanelActions({ className, render, ...props }) {
11745
11791
  const { actions } = threadPanel();
11746
11792
  const defaultProps = {
11747
11793
  "data-slot": "thread-panel-actions",
11794
+ "data-icon-inset": "",
11748
11795
  className: actions({ className })
11749
11796
  };
11750
11797
  return useRender({
@@ -15716,7 +15763,8 @@ const inboxItem = tv({
15716
15763
  "[[data-slot=inbox-item]+&]:before:block has-data-[slot=inbox-item-selection]:before:left-11",
15717
15764
  "has-data-[slot=inbox-item-leading]:before:left-16",
15718
15765
  "has-data-[slot=inbox-item-selection]:has-data-[slot=inbox-item-leading]:before:left-23",
15719
- "hover:bg-fill-pressed",
15766
+ "hover:bg-fill-row-hover",
15767
+ "not-data-[active=true]:data-popup-open:bg-fill-row-open not-data-[active=true]:has-data-popup-open:bg-fill-row-open",
15720
15768
  "data-[active=true]:bg-info-soft data-[active=true]:hover:bg-info-soft",
15721
15769
  "data-disabled:pointer-events-none data-disabled:hover:bg-transparent",
15722
15770
  "has-[[data-slot=inbox-item-open]:focus-visible]:keyboard:inset-ring-2 has-[[data-slot=inbox-item-open]:focus-visible]:keyboard:inset-ring-foreground",
@@ -16201,7 +16249,8 @@ const searchEntity = tv({ slots: {
16201
16249
  "relative rounded-none border-0 bg-transparent px-4 py-3 text-foreground shadow-none",
16202
16250
  "max-md:min-h-16",
16203
16251
  "not-first:before:absolute not-first:before:top-0 not-first:before:right-0 not-first:before:left-[55px] not-first:before:border-t not-first:before:border-line-hairline",
16204
- "hover:bg-fill-pressed active:bg-fill-pressed",
16252
+ "hover:bg-fill-row-hover active:bg-fill-pressed",
16253
+ "not-data-[selected=true]:data-popup-open:bg-fill-row-open",
16205
16254
  "disabled:cursor-default disabled:hover:bg-transparent disabled:active:bg-transparent",
16206
16255
  "data-[selected=true]:bg-brand-soft data-[selected=true]:hover:bg-brand-soft",
16207
16256
  "outline-none focus-visible:keyboard:inset-ring-2 focus-visible:keyboard:inset-ring-foreground"
@@ -16340,7 +16389,8 @@ const searchMessage = tv({ slots: {
16340
16389
  "w-full text-left transition-colors",
16341
16390
  "relative block rounded-none border-0 bg-transparent px-4 py-3 text-foreground shadow-none",
16342
16391
  "not-first:before:absolute not-first:before:top-0 not-first:before:right-0 not-first:before:left-4 not-first:before:border-t not-first:before:border-line-hairline",
16343
- "hover:bg-fill-pressed active:bg-fill-pressed",
16392
+ "hover:bg-fill-row-hover active:bg-fill-pressed",
16393
+ "not-data-[selected=true]:data-popup-open:bg-fill-row-open",
16344
16394
  "disabled:cursor-default disabled:hover:bg-transparent disabled:active:bg-transparent",
16345
16395
  "data-[selected=true]:bg-brand-soft data-[selected=true]:hover:bg-brand-soft",
16346
16396
  "outline-none focus-visible:keyboard:inset-ring-2 focus-visible:keyboard:inset-ring-foreground"
@@ -14,6 +14,37 @@ import { Avatar } from "@base-ui/react";
14
14
  import { Menu } from "@base-ui/react/menu";
15
15
  import { Toggle } from "@base-ui/react/toggle";
16
16
  import { Toolbar } from "@base-ui/react/toolbar";
17
+ //#region src/lib/head-edges.ts
18
+ /**
19
+ * The ends of a header (design/foundations/alignment.md#the-ends-of-a-header).
20
+ *
21
+ * A head pads both sides by its column's content margin, so a shape (a
22
+ * sticker, a filled button) and text land on the margin as they are. A
23
+ * frameless icon button declares how far its icon box sits inside its edge,
24
+ * `--icon-inset` = (button - icon) / 2, and the head pulls its first and last
25
+ * child outward by that much: the icon box lands on the margin and the
26
+ * button's empty hit area hangs past it, toward the edge. The two ends mirror,
27
+ * and the line holds at every button size (32px with an 18px icon on a
28
+ * desktop, 44px with a 22px icon on a phone and a touch screen) without a
29
+ * number per size or per breakpoint. A title, a sticker or a filled button
30
+ * declares no inset and stays on the margin.
31
+ *
32
+ * An element that declares an inset also carries `data-icon-inset`, which
33
+ * anchors the head's `:first-child` / `:last-child` (packages/ui/CLAUDE.md,
34
+ * Selector Cost). A group of actions declares its buttons' inset itself, so
35
+ * the pull holds when its last action is hidden at some width.
36
+ */
37
+ /** The pull alone, for a head whose padding is already its margin (a dialog's 20px). */
38
+ const headEdgePull = ["[&>[data-icon-inset]:first-child]:-ml-icon-inset", "[&>[data-icon-inset]:last-child]:-mr-icon-inset"];
39
+ /** A panel's head: the margin, 20px on a desktop and 16px on a phone, and the pull. */
40
+ const headEdges = ["px-5 max-md:px-4", ...headEdgePull];
41
+ /**
42
+ * The inset of a control-height icon button (a PanelAction, a Button at
43
+ * `icon-lg`): 7px on a desktop, 11px on a phone and on a touch screen. Written
44
+ * as the arithmetic so it follows the control tokens wherever they switch.
45
+ */
46
+ const controlIconInset = "[--icon-inset:calc((var(--control-height)-var(--control-icon-size))/2)]";
47
+ //#endregion
17
48
  //#region src/lib/tv.ts
18
49
  const tv$1 = ((options) => {
19
50
  const recipe = tv(options);
@@ -300,6 +331,20 @@ const buttonVariants = tv$1({
300
331
  ],
301
332
  class: { root: ["disabled:border-transparent disabled:bg-transparent", "aria-disabled:border-transparent aria-disabled:bg-transparent"] }
302
333
  },
334
+ {
335
+ variant: ["ghost", "on-frame"],
336
+ size: "icon-lg",
337
+ class: { root: controlIconInset }
338
+ },
339
+ {
340
+ variant: "ghost",
341
+ size: [
342
+ "icon-xs",
343
+ "icon-sm",
344
+ "icon-md"
345
+ ],
346
+ class: { root: "[--icon-inset:calc((--spacing(8)-18px)/2)]" }
347
+ },
303
348
  {
304
349
  variant: "on-frame",
305
350
  size: [
@@ -316,10 +361,17 @@ const buttonVariants = tv$1({
316
361
  "icon-sm",
317
362
  "icon-md"
318
363
  ],
319
- class: { root: "size-9 after:-inset-[calc(--spacing(1)+1.5px)] [&_svg:not([class*='size-'])]:size-5" }
364
+ class: { root: ["size-9 after:-inset-[calc(--spacing(1)+1.5px)] [&_svg:not([class*='size-'])]:size-5", "[--icon-inset:calc((--spacing(9)-20px)/2)]"] }
320
365
  }
321
366
  ]
322
367
  });
368
+ const FRAMELESS = /* @__PURE__ */ new Set(["ghost", "on-frame"]);
369
+ const ICON_SIZES = /* @__PURE__ */ new Set([
370
+ "icon-xs",
371
+ "icon-sm",
372
+ "icon-md",
373
+ "icon-lg"
374
+ ]);
323
375
  function Button$1({ variant, size, loading = false, loadingLabel = "Loading", disabled, className, children, render, nativeButton, ...props }) {
324
376
  const { root, content, loadingIndicator } = buttonVariants({
325
377
  variant,
@@ -330,6 +382,7 @@ function Button$1({ variant, size, loading = false, loadingLabel = "Loading", di
330
382
  render,
331
383
  nativeButton: nativeButton ?? defaultNativeButton(render),
332
384
  "data-slot": "button",
385
+ "data-icon-inset": FRAMELESS.has(variant ?? "default") && ICON_SIZES.has(size ?? "md") ? "" : void 0,
333
386
  className: root({ className }),
334
387
  "data-loading": loading ? "true" : void 0,
335
388
  disabled: disabled || loading,
@@ -357,7 +410,11 @@ const dialog = tv$1({
357
410
  "overflow-clip rounded-dialog bg-layer-popover text-foreground",
358
411
  "shadow-float ring-1 ring-line-seam"
359
412
  ],
360
- header: ["grid shrink-0 grid-cols-[minmax(0,1fr)_max-content] items-center min-w-0", "gap-2 px-5 pt-5"],
413
+ header: [
414
+ "grid shrink-0 grid-cols-[minmax(0,1fr)_max-content] items-center min-w-0",
415
+ "gap-2 px-5 pt-5",
416
+ headEdgePull
417
+ ],
361
418
  body: [
362
419
  "min-w-0 flex-1 overflow-y-auto",
363
420
  "px-5 pt-1.5 pb-5 first:pt-5",
@@ -468,7 +525,7 @@ function DialogClose({ className, variant, size, children, render, "aria-label":
468
525
  variant: variant ?? "ghost",
469
526
  size: size ?? "icon-md",
470
527
  "aria-label": ariaLabel ?? "Close",
471
- className: cn("-my-1.5 -mr-1.5", className)
528
+ className: cn("-my-1.5", className)
472
529
  }),
473
530
  ...props,
474
531
  children: children ?? /* @__PURE__ */ jsx(X, { className: "size-4.5" })
@@ -1253,9 +1310,9 @@ const dropdownMenu = tv$1({ slots: {
1253
1310
  "shadow-float ring-1 ring-line-seam"
1254
1311
  ],
1255
1312
  item: [
1256
- "flex w-full cursor-default select-none items-center [@media(max-height:600px)]:py-1",
1313
+ "flex w-full cursor-default select-none items-center",
1257
1314
  "text-left outline-none transition-colors",
1258
- "min-h-9 gap-2.5 rounded-control px-2.5 py-1.5",
1315
+ "min-h-option gap-2.5 rounded-control px-2.5 py-option-inset",
1259
1316
  "font-sans text-callout font-medium text-foreground",
1260
1317
  "hover:bg-brand-soft data-highlighted:bg-brand-soft",
1261
1318
  "data-checked:font-bold data-[selected]:font-bold data-[emphasis=strong]:font-bold",
@@ -1266,7 +1323,7 @@ const dropdownMenu = tv$1({ slots: {
1266
1323
  radioItem: [
1267
1324
  "flex w-full cursor-default select-none items-center",
1268
1325
  "text-left outline-none transition-colors",
1269
- "min-h-9 gap-2.5 rounded-control px-2.5 py-1.5",
1326
+ "min-h-option gap-2.5 rounded-control px-2.5 py-option-inset",
1270
1327
  "font-sans text-callout font-medium text-foreground",
1271
1328
  "hover:bg-brand-soft data-highlighted:bg-brand-soft",
1272
1329
  "data-checked:font-bold",
@@ -1275,9 +1332,9 @@ const dropdownMenu = tv$1({ slots: {
1275
1332
  "[&>svg]:text-foreground-muted data-disabled:[&>svg]:text-foreground-disabled"
1276
1333
  ],
1277
1334
  submenuTrigger: [
1278
- "flex w-full cursor-default select-none items-center [@media(max-height:600px)]:py-1",
1335
+ "flex w-full cursor-default select-none items-center",
1279
1336
  "text-left outline-none transition-colors",
1280
- "min-h-9 gap-2.5 rounded-control px-2.5 py-1.5",
1337
+ "min-h-option gap-2.5 rounded-control px-2.5 py-option-inset",
1281
1338
  "font-sans text-callout font-medium text-foreground",
1282
1339
  "hover:bg-brand-soft data-highlighted:bg-brand-soft data-popup-open:bg-brand-soft",
1283
1340
  "data-disabled:pointer-events-none data-disabled:text-foreground-disabled",
@@ -1524,6 +1581,59 @@ function DropdownMenuCheckboxItemIndicator({ className, ...props }) {
1524
1581
  });
1525
1582
  }
1526
1583
  //#endregion
1584
+ //#region src/lib/use-overflow-title.ts
1585
+ function cut(element) {
1586
+ return element.scrollWidth > element.clientWidth + 1 || element.scrollHeight > element.clientHeight + 1;
1587
+ }
1588
+ function assign(ref, node) {
1589
+ if (typeof ref === "function") ref(node);
1590
+ else if (ref) ref.current = node;
1591
+ }
1592
+ /**
1593
+ * Gives a value that is cut off (an ellipsis, a clamp) its whole text as its
1594
+ * `title`, and takes it away again when the value fits: a value that shows in
1595
+ * full needs no tooltip repeating it (design/patterns/forms.md#long-values-in-fields).
1596
+ * It watches the element and its children, so a part whose text sits in a
1597
+ * child that truncates (a chip's label) is covered too. A `title` the caller
1598
+ * passes wins, and is never touched.
1599
+ *
1600
+ * Pass a stable `ref`: a ref object, or a callback ref made with `useCallback`
1601
+ * (or defined outside the component). The returned callback ref changes
1602
+ * whenever `ref` does, and React then detaches and re-attaches it, so an inline
1603
+ * `ref={(node) => ...}` disconnects and rebuilds both observers on every render.
1604
+ * The title stays right, but each render pays for new observers and a fresh
1605
+ * measurement.
1606
+ */
1607
+ function useOverflowTitle(ref, title) {
1608
+ const cleanup = useRef(void 0);
1609
+ const own = title !== void 0;
1610
+ return useCallback((node) => {
1611
+ cleanup.current?.();
1612
+ cleanup.current = void 0;
1613
+ assign(ref, node);
1614
+ const view = node?.ownerDocument.defaultView;
1615
+ if (!node || own || !view || !("ResizeObserver" in view)) return;
1616
+ const update = () => {
1617
+ const text = node.textContent?.trim() ?? "";
1618
+ if (text && (cut(node) || Array.from(node.children).some(cut))) node.title = text;
1619
+ else node.removeAttribute("title");
1620
+ };
1621
+ const resize = new view.ResizeObserver(update);
1622
+ resize.observe(node);
1623
+ for (const child of Array.from(node.children)) resize.observe(child);
1624
+ const mutation = new view.MutationObserver(update);
1625
+ mutation.observe(node, {
1626
+ childList: true,
1627
+ characterData: true,
1628
+ subtree: true
1629
+ });
1630
+ cleanup.current = () => {
1631
+ resize.disconnect();
1632
+ mutation.disconnect();
1633
+ };
1634
+ }, [own, ref]);
1635
+ }
1636
+ //#endregion
1527
1637
  //#region src/components/avatar/avatar.tsx
1528
1638
  const avatar = tv$1({
1529
1639
  slots: {
@@ -1817,7 +1927,8 @@ const messageItem = tv$1({
1817
1927
  "min-h-0 px-2 py-1 border-t-8 border-transparent bg-clip-padding",
1818
1928
  "hover:[--mi-ct-color:var(--foreground-hint)] has-data-popup-open:[--mi-ct-color:var(--foreground-hint)]",
1819
1929
  "bg-(--mi-row-fill) [--mi-row-fill:initial]",
1820
- "hover:[--mi-row-fill:var(--fill-pressed)] data-popup-open:[--mi-row-fill:var(--fill-pressed)] has-data-popup-open:[--mi-row-fill:var(--fill-pressed)]",
1930
+ "hover:[--mi-row-fill:var(--fill-message-hover)]",
1931
+ "data-popup-open:[--mi-row-fill:var(--fill-row-open)] has-data-popup-open:[--mi-row-fill:var(--fill-row-open)]",
1821
1932
  "has-data-[slot=message-item-search-match]:[--mi-row-fill:var(--info-soft)] has-data-[slot=message-item-search-match]:hover:[--mi-row-fill:var(--info-soft)]",
1822
1933
  "has-[[data-slot=message-multi-select-checkbox][data-checked]]:[--mi-row-fill:var(--info-soft)]",
1823
1934
  "has-[[data-slot=message-multi-select-checkbox][data-checked]]:hover:[--mi-row-fill:var(--info-soft)]"
@@ -3174,4 +3285,4 @@ function MessageListJumpPillDismiss({ className, children, render, nativeButton,
3174
3285
  });
3175
3286
  }
3176
3287
  //#endregion
3177
- export { MessageReactionTooltipNameText as $, DropdownMenuShortcut as $t, MessageTextSelectionToolbarButton as A, DialogClose as An, MessageItem as At, MessageReferenceUnavailable as B, DialogTrigger as Bn, DropdownMenuCheckboxItemIndicator as Bt, MessageQuotedPreviewUnavailable as C, TooltipContent as Cn, MessageItemEdited as Ct, MessageEmbedHeader as D, Badge as Dn, MessageItemAvatarSlot as Dt, MessageEmbedFooter as E, TooltipTrigger as En, MessageItemSearchMatch as Et, MessageReferenceChip as F, DialogNav as Fn, AvatarGroup as Ft, MessageReactionGlyph as G, useParentOverlayKind as Gn, DropdownMenuItemLabel as Gt, MessageReactionChip as H, buttonVariants as Hn, DropdownMenuFooter as Ht, MessageReferenceIndicator as I, DialogOverlay as In, AvatarGroupCount as It, MessageReactionQuickButton as J, useThemeScopeProps as Jn, DropdownMenuPortal as Jt, MessageReactionPickerContent as K, withOverlayLayer as Kn, DropdownMenuLabel as Kt, MessageReferenceLabel as L, DialogPortal as Ln, AvatarImage as Lt, MessageMultiSelect as M, DialogDescription as Mn, Avatar$1 as Mt, MessageMultiSelectCheckbox as N, DialogFooter as Nn, AvatarBadge as Nt, messageEmbed as O, Dialog$1 as On, MessageItemContinuationTime as Ot, MessageMultiSelectToolbar as P, DialogHeader as Pn, AvatarFallback as Pt, MessageReactionTooltipName as Q, DropdownMenuSeparator as Qt, MessageReferencePending as R, DialogSurface as Rn, DropdownMenu as Rt, MessageQuotedPreviewTimestamp as S, Tooltip$1 as Sn, MessageItemContent as St, MessageEmbedContent as T, TooltipProvider as Tn, MessageItemFooter as Tt, MessageReactionCount as U, Spinner as Un, DropdownMenuItem as Ut, MessageReactionAddButton as V, Button$1 as Vn, DropdownMenuContent as Vt, MessageReactionErrorIndicator as W, defaultNativeButton as Wn, DropdownMenuItemCount as Wt, MessageReactionTooltipContent as X, ThemeStateContext as Xn, DropdownMenuRadioItem as Xt, MessageReactionQuickRow as Y, useTheme as Yn, DropdownMenuRadioGroup as Yt, MessageReactionTooltipMeta as Z, tv$1 as Zn, DropdownMenuRadioItemIndicator as Zt, MessageQuotedPreviewContent as _, ScrollAreaViewport as _n, MessageItemSenderButton as _t, MessageListDateDivider as a, Panel as an, MessageItemSavedIndicator as at, MessageQuotedPreviewText as b, CheckboxIndicator as bn, MessageItemAttachments as bt, MessageList as c, PanelHeaderRoot as cn, MessageItemDelegatedAvatar as ct, MessageQuotedPreviewAttachments as d, PanelSection as dn, MessageItemDelegatedByName as dt, DropdownMenuSubmenu as en, MessageReactionTooltipNames as et, MessageQuotedPreviewAuthor as f, ScrollArea$1 as fn, MessageItemHeader as ft, MessageQuotedPreviewChannel as g, ScrollAreaThumb as gn, MessageItemSenderBadges as gt, MessageQuotedPreviewAuthorName as h, ScrollAreaScrollbar as hn, MessageItemSender as ht, MessageListJumpPillGroup as i, KbdGroup as in, MessageItemReadReceipt as it, MessageTextSelectionToolbarSeparator as j, DialogContent as jn, searchHit as jt, MessageTextSelectionToolbar as k, DialogBody as kn, MessageItemGutter as kt, MessageQuotedPreview as l, PanelScrollContent as ln, MessageItemDelegatedBy as lt, MessageQuotedPreviewAuthorMeta as m, ScrollAreaCorner as mn, MessageItemMeta as mt, MessageListJumpPill as n, DropdownMenuTrigger as nn, MessageItemBadge as nt, MessageListHistoryLimited as o, PanelBody as on, MessageItemToolbar as ot, MessageQuotedPreviewAuthorDelegatedBy as p, ScrollAreaContent as pn, MessageItemInlineStatus as pt, MessageReactionPickerStatic as q, resolveThemeScopeProps as qn, DropdownMenuPopup as qt, MessageListJumpPillDismiss as r, Kbd as rn, MessageItemBadgeSeparator as rt, MessageListNotice as s, PanelFooter as sn, MessageItemToolbarButton as st, MessageListJumpControls as t, DropdownMenuSubmenuTrigger as tn, MessageReactionTooltipSeparator as tt, MessageQuotedPreviewAttachment as u, PanelScrollViewport as un, MessageItemDelegatedByButton as ut, MessageQuotedPreviewHeader as v, useMobileViewport as vn, MessageItemSenderGroup as vt, MessageEmbed as w, TooltipPortal as wn, MessageItemEmbedded as wt, MessageQuotedPreviewThread as x, PRESENCE_COLOR as xn, MessageItemBody as xt, MessageQuotedPreviewLoading as y, Checkbox$1 as yn, MessageItemTime as yt, MessageReferenceText as z, DialogTitle as zn, DropdownMenuCheckboxItem as zt };
3288
+ export { MessageReactionTooltipNameText as $, controlIconInset as $n, DropdownMenuSeparator as $t, MessageTextSelectionToolbarButton as A, DialogBody as An, MessageItem as At, MessageReferenceUnavailable as B, DialogTitle as Bn, DropdownMenuCheckboxItem as Bt, MessageQuotedPreviewUnavailable as C, Tooltip$1 as Cn, MessageItemEdited as Ct, MessageEmbedHeader as D, TooltipTrigger as Dn, MessageItemAvatarSlot as Dt, MessageEmbedFooter as E, TooltipProvider as En, MessageItemSearchMatch as Et, MessageReferenceChip as F, DialogHeader as Fn, AvatarGroup as Ft, MessageReactionGlyph as G, defaultNativeButton as Gn, DropdownMenuItemCount as Gt, MessageReactionChip as H, Button$1 as Hn, DropdownMenuContent as Ht, MessageReferenceIndicator as I, DialogNav as In, AvatarGroupCount as It, MessageReactionQuickButton as J, resolveThemeScopeProps as Jn, DropdownMenuPopup as Jt, MessageReactionPickerContent as K, useParentOverlayKind as Kn, DropdownMenuItemLabel as Kt, MessageReferenceLabel as L, DialogOverlay as Ln, AvatarImage as Lt, MessageMultiSelect as M, DialogContent as Mn, Avatar$1 as Mt, MessageMultiSelectCheckbox as N, DialogDescription as Nn, AvatarBadge as Nt, messageEmbed as O, Badge as On, MessageItemContinuationTime as Ot, MessageMultiSelectToolbar as P, DialogFooter as Pn, AvatarFallback as Pt, MessageReactionTooltipName as Q, tv$1 as Qn, DropdownMenuRadioItemIndicator as Qt, MessageReferencePending as R, DialogPortal as Rn, useOverflowTitle as Rt, MessageQuotedPreviewTimestamp as S, PRESENCE_COLOR as Sn, MessageItemContent as St, MessageEmbedContent as T, TooltipPortal as Tn, MessageItemFooter as Tt, MessageReactionCount as U, buttonVariants as Un, DropdownMenuFooter as Ut, MessageReactionAddButton as V, DialogTrigger as Vn, DropdownMenuCheckboxItemIndicator as Vt, MessageReactionErrorIndicator as W, Spinner as Wn, DropdownMenuItem as Wt, MessageReactionTooltipContent as X, useTheme as Xn, DropdownMenuRadioGroup as Xt, MessageReactionQuickRow as Y, useThemeScopeProps as Yn, DropdownMenuPortal as Yt, MessageReactionTooltipMeta as Z, ThemeStateContext as Zn, DropdownMenuRadioItem as Zt, MessageQuotedPreviewContent as _, ScrollAreaThumb as _n, MessageItemSenderButton as _t, MessageListDateDivider as a, KbdGroup as an, MessageItemSavedIndicator as at, MessageQuotedPreviewText as b, Checkbox$1 as bn, MessageItemAttachments as bt, MessageList as c, PanelFooter as cn, MessageItemDelegatedAvatar as ct, MessageQuotedPreviewAttachments as d, PanelScrollViewport as dn, MessageItemDelegatedByName as dt, DropdownMenuShortcut as en, headEdgePull as er, MessageReactionTooltipNames as et, MessageQuotedPreviewAuthor as f, PanelSection as fn, MessageItemHeader as ft, MessageQuotedPreviewChannel as g, ScrollAreaScrollbar as gn, MessageItemSenderBadges as gt, MessageQuotedPreviewAuthorName as h, ScrollAreaCorner as hn, MessageItemSender as ht, MessageListJumpPillGroup as i, Kbd as in, MessageItemReadReceipt as it, MessageTextSelectionToolbarSeparator as j, DialogClose as jn, searchHit as jt, MessageTextSelectionToolbar as k, Dialog$1 as kn, MessageItemGutter as kt, MessageQuotedPreview as l, PanelHeaderRoot as ln, MessageItemDelegatedBy as lt, MessageQuotedPreviewAuthorMeta as m, ScrollAreaContent as mn, MessageItemMeta as mt, MessageListJumpPill as n, DropdownMenuSubmenuTrigger as nn, MessageItemBadge as nt, MessageListHistoryLimited as o, Panel as on, MessageItemToolbar as ot, MessageQuotedPreviewAuthorDelegatedBy as p, ScrollArea$1 as pn, MessageItemInlineStatus as pt, MessageReactionPickerStatic as q, withOverlayLayer as qn, DropdownMenuLabel as qt, MessageListJumpPillDismiss as r, DropdownMenuTrigger as rn, MessageItemBadgeSeparator as rt, MessageListNotice as s, PanelBody as sn, MessageItemToolbarButton as st, MessageListJumpControls as t, DropdownMenuSubmenu as tn, headEdges as tr, MessageReactionTooltipSeparator as tt, MessageQuotedPreviewAttachment as u, PanelScrollContent as un, MessageItemDelegatedByButton as ut, MessageQuotedPreviewHeader as v, ScrollAreaViewport as vn, MessageItemSenderGroup as vt, MessageEmbed as w, TooltipContent as wn, MessageItemEmbedded as wt, MessageQuotedPreviewThread as x, CheckboxIndicator as xn, MessageItemBody as xt, MessageQuotedPreviewLoading as y, useMobileViewport as yn, MessageItemTime as yt, MessageReferenceText as z, DialogSurface as zn, DropdownMenu as zt };
package/dist/style.css CHANGED
@@ -1138,6 +1138,14 @@
1138
1138
  overflow: hidden;
1139
1139
  }
1140
1140
 
1141
+ .r-combobox-select__option .r-combobox-select__text {
1142
+ -webkit-line-clamp: 2;
1143
+ white-space: normal;
1144
+ overflow-wrap: anywhere;
1145
+ -webkit-box-orient: vertical;
1146
+ display: -webkit-box;
1147
+ }
1148
+
1141
1149
  .r-combobox-select__actions {
1142
1150
  flex: none;
1143
1151
  align-items: center;
@@ -1239,7 +1247,7 @@
1239
1247
  justify-content: space-between;
1240
1248
  align-items: center;
1241
1249
  gap: 10px;
1242
- padding: 0 10px;
1250
+ padding: calc(18px - .5lh) 10px;
1243
1251
  font-weight: 500;
1244
1252
  transition: background-color .12s, color .12s;
1245
1253
  display: flex;
package/dist/styles.css CHANGED
@@ -283,6 +283,9 @@
283
283
  --color-foreground-on-frame-active: var(--foreground-on-frame-active);
284
284
  --color-fill-frame-pressed: var(--fill-frame-pressed);
285
285
  --color-fill-pressed: var(--fill-pressed);
286
+ --color-fill-row-hover: var(--fill-row-hover);
287
+ --color-fill-message-hover: var(--fill-message-hover);
288
+ --color-fill-row-open: var(--fill-row-open);
286
289
  --color-layer-code: var(--layer-code);
287
290
  --color-foreground-link: var(--foreground-link);
288
291
  --color-sticker-ink: var(--sticker-ink);
@@ -367,6 +370,31 @@
367
370
  --spacing-control-cta: var(--control-height-cta);
368
371
  --spacing-control-icon: var(--control-icon-size);
369
372
  --spacing-control-hit: var(--control-hit-inset);
373
+ /* ─── Option metric axis ────────────────────────────────────────────────────
374
+ An option in a popup list (a Select, Combobox or ComboboxSelect option, a
375
+ menu item) is as tall as its content, with the same space above and below
376
+ its text whatever that content is (design/patterns/forms.md#option-height).
377
+ `py-option-inset` is that space: what a 36px row leaves around one line of the
378
+ option's own type (`1lh`, so 7px around a 22px line, 8px around 20px, 6px
379
+ around 24px). One line is exactly 36px; a second line, wrapped or a
380
+ description, adds its own height and nothing else. `min-h-option` is the
381
+ floor for an option with no text line, such as an empty one.
382
+
383
+ Consumers: `SelectItem`, `ComboboxItem`, the menu items of `DropdownMenu`
384
+ and `ContextMenu`, and `.r-combobox-select__option` (which writes the same
385
+ calc in its CSS). */
386
+ --spacing-option: 2.25rem;
387
+ --spacing-option-inset: calc((2.25rem - 1lh) / 2);
388
+ /* ─── Icon inset ────────────────────────────────────────────────────────────
389
+ How far a frameless icon button's icon box sits inside its edge:
390
+ (button - icon) / 2. 7px for a 32px button with an 18px icon, 11px for a
391
+ 44px one with a 22px icon. The button declares it (`--icon-inset`, see
392
+ lib/head-edges.ts) and a head pulls its first and last child outward by
393
+ it (`-ml-icon-inset`, `-mr-icon-inset`), so the icon box, not the button,
394
+ lands on the head's content margin at every button size
395
+ (design/foundations/alignment.md#the-ends-of-a-header). A shape or text
396
+ declares nothing and stays on the margin. */
397
+ --spacing-icon-inset: var(--icon-inset);
370
398
 
371
399
  /* Deprecated utility names, kept until raft-ui 1.0 (see codemods/token-names.mjs). */
372
400
  --color-foreground-strong: var(--foreground);
@@ -432,6 +460,14 @@
432
460
  --shadow-raft-focus: var(--focus-ring);
433
461
  }
434
462
 
463
+ /* Not inherited, so a wrapper never takes its button's inset: only the element
464
+ that declares it moves. */
465
+ @property --icon-inset {
466
+ syntax: "<length>";
467
+ inherits: false;
468
+ initial-value: 0px;
469
+ }
470
+
435
471
  *,
436
472
  ::before,
437
473
  ::after {
@@ -490,6 +526,18 @@ body {
490
526
  background-image: linear-gradient(var(--fill-pressed), var(--fill-pressed));
491
527
  }
492
528
 
529
+ /* A row laid over a solid surface (a list row on a card, a file row) hovers
530
+ with the lighter row step, as an overlay too, so press (press-overlay)
531
+ replaces it instead of stacking on it. */
532
+ @utility row-hover-overlay {
533
+ background-image: linear-gradient(var(--fill-row-hover), var(--fill-row-hover));
534
+ }
535
+
536
+ /* The same row with its menu open: fill-row-open, as an overlay. */
537
+ @utility row-open-overlay {
538
+ background-image: linear-gradient(var(--fill-row-open), var(--fill-row-open));
539
+ }
540
+
493
541
  @utility press-overlay-sticker {
494
542
  background-image: linear-gradient(var(--sticker-pressed), var(--sticker-pressed));
495
543
  }
package/dist/tokens.json CHANGED
@@ -1527,6 +1527,63 @@
1527
1527
  }
1528
1528
  }
1529
1529
  }
1530
+ },
1531
+ "row-hover": {
1532
+ "$description": "Hover on a whole row (sidebar, list, inbox, search result, table, file row): one perceptible step (OKLab dE about 2.8 in both modes), below every selection tint, so text keeps its contrast.",
1533
+ "$value": {
1534
+ "colorSpace": "oklch",
1535
+ "components": [0.1813, 0.005, 17.62],
1536
+ "alpha": 0.04
1537
+ },
1538
+ "$extensions": {
1539
+ "build.botiverse.raft": {
1540
+ "modes": {
1541
+ "dark": {
1542
+ "colorSpace": "oklch",
1543
+ "components": [0.9843, 0.004, 106.47],
1544
+ "alpha": 0.03
1545
+ }
1546
+ }
1547
+ }
1548
+ }
1549
+ },
1550
+ "message-hover": {
1551
+ "$description": "Hover on a message: the lightest perceptible step (OKLab dE about 2.1 in both modes), because a message is a large block someone reads with the pointer resting on it.",
1552
+ "$value": {
1553
+ "colorSpace": "oklch",
1554
+ "components": [0.1813, 0.005, 17.62],
1555
+ "alpha": 0.03
1556
+ },
1557
+ "$extensions": {
1558
+ "build.botiverse.raft": {
1559
+ "modes": {
1560
+ "dark": {
1561
+ "colorSpace": "oklch",
1562
+ "components": [0.9843, 0.004, 106.47],
1563
+ "alpha": 0.022
1564
+ }
1565
+ }
1566
+ }
1567
+ }
1568
+ },
1569
+ "row-open": {
1570
+ "$description": "Any row with its menu open (right-click, its menu button, press and hold): one step over a hover (OKLab dE 3.4 to 4.3 on every row surface), because on a phone, which has no hover, it is the only sign of which row the menu acts on. Still under info-soft and brand-soft on every surface.",
1571
+ "$value": {
1572
+ "colorSpace": "oklch",
1573
+ "components": [0.1813, 0.005, 17.62],
1574
+ "alpha": 0.05
1575
+ },
1576
+ "$extensions": {
1577
+ "build.botiverse.raft": {
1578
+ "modes": {
1579
+ "dark": {
1580
+ "colorSpace": "oklch",
1581
+ "components": [0.9843, 0.004, 106.47],
1582
+ "alpha": 0.04
1583
+ }
1584
+ }
1585
+ }
1586
+ }
1530
1587
  }
1531
1588
  },
1532
1589
  "line": {
package/dist/wip.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { An as DialogClose, Bn as DialogTrigger, Jn as useThemeScopeProps, On as Dialog, Vn as Button$1, Wn as defaultNativeButton, Yn as useTheme, Zn as tv, ct as MessageItemDelegatedAvatar, dt as MessageItemDelegatedByName, i as MessageListJumpPillGroup, jn as DialogContent, lt as MessageItemDelegatedBy, n as MessageListJumpPill, p as MessageQuotedPreviewAuthorDelegatedBy, r as MessageListJumpPillDismiss, t as MessageListJumpControls, ut as MessageItemDelegatedByButton, zn as DialogTitle } from "./message-list-CxawdwMF.mjs";
1
+ import { Bn as DialogTitle, Gn as defaultNativeButton, Hn as Button$1, Mn as DialogContent, Qn as tv, Rt as useOverflowTitle, Vn as DialogTrigger, Xn as useTheme, Yn as useThemeScopeProps, ct as MessageItemDelegatedAvatar, dt as MessageItemDelegatedByName, i as MessageListJumpPillGroup, jn as DialogClose, kn as Dialog, lt as MessageItemDelegatedBy, n as MessageListJumpPill, p as MessageQuotedPreviewAuthorDelegatedBy, r as MessageListJumpPillDismiss, t as MessageListJumpControls, ut as MessageItemDelegatedByButton } from "./message-list-YsCZpkr7.mjs";
2
2
  import { Check, ChevronDown, LoaderCircle, Plus, X } from "lucide-react";
3
3
  import { createContext, use, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
4
4
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -70,19 +70,21 @@ const defaultMessages = {
70
70
  remove: (label) => `Remove ${label}`
71
71
  };
72
72
  const defaultRenderCreateLabel = (query) => `Create “${query}”`;
73
+ /** An option's or a chip's text, with its whole value as the title when it is cut off. */
74
+ function OverflowText({ children }) {
75
+ return /* @__PURE__ */ jsx("span", {
76
+ ref: useOverflowTitle(),
77
+ className: "r-combobox-select__text",
78
+ children
79
+ });
80
+ }
73
81
  function recordLabel(record, renderLabel) {
74
82
  return record.option === void 0 ? record.label : renderLabel?.(record.option) ?? record.label;
75
83
  }
76
84
  function renderOptionContent({ record, selected, renderLabel, renderOption, renderCreateLabel }) {
77
- if (record.createQuery !== void 0) return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Plus, { "aria-hidden": true }), /* @__PURE__ */ jsx("span", {
78
- className: "r-combobox-select__text",
79
- children: renderCreateLabel(record.label)
80
- })] });
85
+ if (record.createQuery !== void 0) return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Plus, { "aria-hidden": true }), /* @__PURE__ */ jsx(OverflowText, { children: renderCreateLabel(record.label) })] });
81
86
  const content = record.option === void 0 ? record.label : renderOption?.(record.option, { selected }) ?? recordLabel(record, renderLabel);
82
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
83
- className: "r-combobox-select__text",
84
- children: content
85
- }), /* @__PURE__ */ jsx(Combobox.ItemIndicator, {
87
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(OverflowText, { children: content }), /* @__PURE__ */ jsx(Combobox.ItemIndicator, {
86
88
  className: "r-combobox-select__indicator",
87
89
  children: /* @__PURE__ */ jsx(Check, { "aria-hidden": true })
88
90
  })] });
@@ -96,10 +98,7 @@ function renderSelectedChip({ record, index, tagLimit, strings, renderLabel, ren
96
98
  "data-overflow": overflow || void 0,
97
99
  "aria-label": record.label,
98
100
  "aria-description": strings.chipDescription,
99
- children: [/* @__PURE__ */ jsx("span", {
100
- className: "r-combobox-select__text",
101
- children: content
102
- }), /* @__PURE__ */ jsx(Combobox.ChipRemove, {
101
+ children: [/* @__PURE__ */ jsx(OverflowText, { children: content }), /* @__PURE__ */ jsx(Combobox.ChipRemove, {
103
102
  className: "r-combobox-select__remove",
104
103
  "aria-label": strings.remove(record.label),
105
104
  children: /* @__PURE__ */ jsx(X, { "aria-hidden": true })
@@ -472,6 +471,7 @@ function ComboboxSelect({ options, multiple, value, defaultValue, onValueChange,
472
471
  children: /* @__PURE__ */ jsx(Combobox.Positioner, {
473
472
  sideOffset: 4,
474
473
  align: "start",
474
+ collisionPadding: 8,
475
475
  ...phone ? {
476
476
  side: listSide,
477
477
  collisionAvoidance: PHONE_LIST_COLLISION
@@ -634,7 +634,7 @@ const comment = tv({
634
634
  cardTrigger: [
635
635
  "absolute -inset-px z-10 cursor-pointer",
636
636
  "rounded-container border-0 bg-transparent p-0",
637
- "hover:bg-fill-pressed active:bg-fill-pressed",
637
+ "hover:bg-fill-row-hover active:bg-fill-pressed",
638
638
  "outline-none focus-visible:shadow-focus"
639
639
  ],
640
640
  cardQuote: ["sr-only"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botiverse/raft-ui",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "React components and CSS foundations for the Raft Design System.",
5
5
  "keywords": [
6
6
  "agent-skills",