@botiverse/raft-ui 0.13.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,22 +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
+
1
43
  ## [0.13.0](https://github.com/botiverse/raft-design/compare/v0.12.0...v0.13.0) (2026-10-21)
2
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
+
3
52
  ### Added
4
53
 
5
- - Tokens `fill-row-hover` (4% ink light, 3% dark), `fill-message-hover` (3% / 2.2%) and `fill-row-open` (5% / 4%), with utilities `row-hover-overlay` and `row-open-overlay`. ([#641](https://github.com/botiverse/raft-design/pull/641), [#643](https://github.com/botiverse/raft-design/pull/643))
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/
6
58
 
7
59
  ### Changed
8
60
 
9
- - Rows hover with their own light step instead of the press overlay (`fill-pressed`, 7.8%): messages with `fill-message-hover`; sidebar items, list, inbox, search, table and file rows with `fill-row-hover`. Hover stays lighter than selection; press is unchanged. ([#641](https://github.com/botiverse/raft-design/pull/641))
10
- - A row with its menu open (right-click, ⋯ menu, phone long-press) takes `fill-row-open`, the one marker of which row the menu belongs to; selection still wins. Comment cards hover with `fill-row-hover`. ([#643](https://github.com/botiverse/raft-design/pull/643))
11
- - Select and Combobox default `collisionPadding` to 8px. ([#642](https://github.com/botiverse/raft-design/pull/642))
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
12
73
 
13
74
  ### Fixed
14
75
 
15
- - Long values stay inside form fields on narrow screens: Field 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; SelectValue, ComboboxSelect values and chips truncate with the full value as `title`; options wrap to 2 lines then clamp; Select and Combobox popups never get wider than the screen less 8px each side. ([#642](https://github.com/botiverse/raft-design/pull/642))
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
16
80
 
17
81
  ## [0.12.0](https://github.com/botiverse/raft-design/compare/v0.11.0...v0.12.0) (2026-10-21)
18
82
 
19
- ### Breaking
83
+ ### Breaking Changes
20
84
 
21
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))
22
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: [
@@ -171,8 +171,10 @@
171
171
  .r-combobox-select__option {
172
172
  box-sizing: border-box;
173
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). */
174
176
  min-height: 36px;
175
- padding: 6px 10px;
177
+ padding: calc((36px - 1lh) / 2) 10px;
176
178
  display: flex;
177
179
  align-items: center;
178
180
  justify-content: space-between;
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, ref, title, ...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
  };
@@ -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 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, 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, 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-DP03T8gF.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"],
@@ -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"],
@@ -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: [
@@ -3795,11 +3797,12 @@ const inputGroup = tv({
3795
3797
  "[&>svg:not([class*='size-'])]:size-4.5"
3796
3798
  ],
3797
3799
  text: [
3798
- "block min-w-0 max-w-1/2 shrink-0 content-center truncate px-3",
3800
+ "flex min-w-0 max-w-1/2 shrink-0 items-center px-3",
3799
3801
  "select-none",
3800
3802
  "font-mono text-mono-code",
3801
3803
  "text-(--ig-text-fg,var(--ig-text-fg-default,var(--foreground-hint)))"
3802
3804
  ],
3805
+ textInner: ["min-w-0 truncate"],
3803
3806
  control: [
3804
3807
  "min-w-0 flex-1 bg-transparent pl-3 outline-none",
3805
3808
  "pr-(--ig-control-pr,0.75rem)",
@@ -3904,8 +3907,8 @@ function InputGroupAddonRoot({ align = "inline-start", variant = "default", clas
3904
3907
  ...props
3905
3908
  });
3906
3909
  }
3907
- function TextAddon({ align = "inline-start", variant = "default", className, ref, title, ...props }) {
3908
- const { text } = inputGroup({
3910
+ function TextAddon({ align = "inline-start", variant = "default", className, ref, title, children, ...props }) {
3911
+ const { text, textInner } = inputGroup({
3909
3912
  align,
3910
3913
  variant
3911
3914
  });
@@ -3918,7 +3921,12 @@ function TextAddon({ align = "inline-start", variant = "default", className, ref
3918
3921
  "data-variant": variant,
3919
3922
  "data-input-group-trim": variant === "default" && align === "inline-end" ? align : void 0,
3920
3923
  className: text({ className }),
3921
- ...props
3924
+ ...props,
3925
+ children: /* @__PURE__ */ jsx("span", {
3926
+ "data-slot": "input-group-text-value",
3927
+ className: textInner(),
3928
+ children
3929
+ })
3922
3930
  });
3923
3931
  }
3924
3932
  const InputGroupAddon = Object.assign(InputGroupAddonRoot, { Text: TextAddon });
@@ -4000,7 +4008,7 @@ const combobox = tv({
4000
4008
  item: [
4001
4009
  "flex w-full shrink-0 cursor-default select-none items-center justify-between overflow-hidden",
4002
4010
  "text-left outline-none transition-colors",
4003
- "min-h-9 gap-2.5 rounded-control px-2.5 py-1.5",
4011
+ "min-h-option gap-2.5 rounded-control px-2.5 py-option-inset",
4004
4012
  "font-sans text-callout font-medium text-foreground wrap-anywhere",
4005
4013
  "bg-transparent hover:bg-brand-soft data-highlighted:bg-brand-soft",
4006
4014
  "data-selected:font-bold",
@@ -4429,7 +4437,7 @@ const select = tv({
4429
4437
  item: [
4430
4438
  "flex w-full shrink-0 cursor-default select-none items-center justify-between overflow-hidden",
4431
4439
  "text-left outline-none transition-colors",
4432
- "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",
4433
4441
  "text-callout font-medium text-foreground",
4434
4442
  "bg-transparent hover:bg-brand-soft data-highlighted:bg-brand-soft",
4435
4443
  "data-selected:font-bold data-selected:**:data-[slot='select-item-text']:font-bold",
@@ -9887,7 +9895,11 @@ const notificationCenter = tv({
9887
9895
  "rounded-container bg-layer-popover text-foreground",
9888
9896
  "shadow-float ring-1 ring-line-seam"
9889
9897
  ],
9890
- 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
+ ],
9891
9903
  title: ["font-sans text-headline text-foreground"],
9892
9904
  count: ["ml-auto", "font-mono text-mono-meta text-foreground-hint"],
9893
9905
  scroller: ["min-h-0 flex-1 overflow-y-auto overscroll-contain"],
@@ -11131,7 +11143,8 @@ const panelActionBase = tv({ base: [
11131
11143
  "text-foreground",
11132
11144
  "hover:bg-fill-pressed active:bg-fill-pressed",
11133
11145
  "focus-visible:shadow-focus",
11134
- "[&>svg]:size-control-icon"
11146
+ "[&>svg]:size-control-icon",
11147
+ controlIconInset
11135
11148
  ] });
11136
11149
  const panelAction = tv({
11137
11150
  extend: panelActionBase,
@@ -11163,6 +11176,7 @@ function PanelAction({ className, render, nativeButton, ...props }) {
11163
11176
  nativeButton: nativeButton ?? defaultNativeButton(render),
11164
11177
  type: "button",
11165
11178
  "data-slot": "panel-action",
11179
+ "data-icon-inset": "",
11166
11180
  className: panelAction({ className })
11167
11181
  });
11168
11182
  }
@@ -11539,6 +11553,7 @@ function PanelActions({ className, render, ...props }) {
11539
11553
  const { actions } = panelHeader();
11540
11554
  const defaultProps = {
11541
11555
  "data-slot": "panel-actions",
11556
+ "data-icon-inset": "",
11542
11557
  className: actions({ className })
11543
11558
  };
11544
11559
  return useRender({
@@ -11554,6 +11569,7 @@ function PanelToggleAction({ className, ...props }) {
11554
11569
  ...props,
11555
11570
  type: "button",
11556
11571
  "data-slot": "panel-toggle-action",
11572
+ "data-icon-inset": "",
11557
11573
  className: panelToggleAction({ className })
11558
11574
  });
11559
11575
  }
@@ -11775,6 +11791,7 @@ function ThreadPanelActions({ className, render, ...props }) {
11775
11791
  const { actions } = threadPanel();
11776
11792
  const defaultProps = {
11777
11793
  "data-slot": "thread-panel-actions",
11794
+ "data-icon-inset": "",
11778
11795
  className: actions({ className })
11779
11796
  };
11780
11797
  return useRender({
@@ -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",
@@ -1539,6 +1596,13 @@ function assign(ref, node) {
1539
1596
  * It watches the element and its children, so a part whose text sits in a
1540
1597
  * child that truncates (a chip's label) is covered too. A `title` the caller
1541
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.
1542
1606
  */
1543
1607
  function useOverflowTitle(ref, title) {
1544
1608
  const cleanup = useRef(void 0);
@@ -3221,4 +3285,4 @@ function MessageListJumpPillDismiss({ className, children, render, nativeButton,
3221
3285
  });
3222
3286
  }
3223
3287
  //#endregion
3224
- export { MessageReactionTooltipNameText as $, 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, 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, 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 };
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
@@ -1247,7 +1247,7 @@
1247
1247
  justify-content: space-between;
1248
1248
  align-items: center;
1249
1249
  gap: 10px;
1250
- padding: 6px 10px;
1250
+ padding: calc(18px - .5lh) 10px;
1251
1251
  font-weight: 500;
1252
1252
  transition: background-color .12s, color .12s;
1253
1253
  display: flex;
package/dist/styles.css CHANGED
@@ -370,6 +370,31 @@
370
370
  --spacing-control-cta: var(--control-height-cta);
371
371
  --spacing-control-icon: var(--control-icon-size);
372
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);
373
398
 
374
399
  /* Deprecated utility names, kept until raft-ui 1.0 (see codemods/token-names.mjs). */
375
400
  --color-foreground-strong: var(--foreground);
@@ -435,6 +460,14 @@
435
460
  --shadow-raft-focus: var(--focus-ring);
436
461
  }
437
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
+
438
471
  *,
439
472
  ::before,
440
473
  ::after {
package/dist/wip.mjs CHANGED
@@ -1,4 +1,4 @@
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-DP03T8gF.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";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botiverse/raft-ui",
3
- "version": "0.13.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",