@botiverse/raft-ui 0.63.1 → 0.65.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 +51 -0
- package/dist/components/activity-inbox/activity-inbox-sidebar.recipe.mjs +1 -1
- package/dist/components/activity-inbox/activity-inbox-toolbar.recipe.mjs +6 -1
- package/dist/components/alert-dialog/alert-dialog.mjs +3 -3
- package/dist/components/announcement-dialog/announcement-dialog.mjs +2 -2
- package/dist/components/app-link-card/app-link-card.d.mts +56 -0
- package/dist/components/app-link-card/app-link-card.mjs +207 -0
- package/dist/components/app-link-card/index.d.mts +2 -0
- package/dist/components/app-rail/app-rail.recipe.mjs +12 -7
- package/dist/components/app-shell/app-shell.mjs +1 -1
- package/dist/components/avatar/avatar.mjs +1 -0
- package/dist/components/badge/badge.mjs +6 -1
- package/dist/components/banner/banner.mjs +1 -1
- package/dist/components/button/button-split.d.mts +38 -0
- package/dist/components/button/button-split.mjs +126 -0
- package/dist/components/button/button.mjs +13 -8
- package/dist/components/button/index.d.mts +2 -1
- package/dist/components/code-block/code-block.recipe.mjs +1 -1
- package/dist/components/combobox/combobox.mjs +4 -4
- package/dist/components/combobox-select/combobox-select.css +6 -3
- package/dist/components/comment/comment-margin.mjs +2 -1
- package/dist/components/composer/composer-suggestion-list.mjs +1 -1
- package/dist/components/composer/composer-toolbar-label.mjs +1 -1
- package/dist/components/composer/composer.mjs +1 -1
- package/dist/components/composer/composer.recipe.mjs +38 -26
- package/dist/components/conversation-panel/conversation-panel-empty-state.mjs +1 -1
- package/dist/components/conversation-panel/conversation-panel.recipe.mjs +3 -2
- package/dist/components/dialog/dialog-sheet-context.mjs +1 -1
- package/dist/components/dialog/dialog.mjs +7 -7
- package/dist/components/drawer/drawer.mjs +3 -2
- package/dist/components/drawer/drawer.recipe.mjs +5 -3
- package/dist/components/empty-state/empty-state.mjs +1 -1
- package/dist/components/field/field.mjs +1 -1
- package/dist/components/files/files.recipe.mjs +1 -1
- package/dist/components/filter-chip/filter-chip.mjs +1 -1
- package/dist/components/form-section/form-section.mjs +1 -1
- package/dist/components/icon-tile/icon-tile.mjs +3 -3
- package/dist/components/inbox-item/inbox-item.recipe.mjs +4 -2
- package/dist/components/input/input.recipe.mjs +1 -1
- package/dist/components/input-group/input-group.mjs +1 -1
- package/dist/components/lightbox/lightbox.mjs +1 -1
- package/dist/components/list-row/list-row.recipe.mjs +2 -2
- package/dist/components/message-action-card/message-action-card-content.recipe.mjs +1 -1
- package/dist/components/message-action-card/message-action-card.recipe.mjs +1 -1
- package/dist/components/message-item/index.d.mts +2 -2
- package/dist/components/message-item/message-item-content.d.mts +14 -0
- package/dist/components/message-item/message-item-content.mjs +146 -1
- package/dist/components/message-item/message-item.recipe.d.mts +3 -0
- package/dist/components/message-item/message-item.recipe.mjs +3 -2
- package/dist/components/message-item/message-reaction.recipe.mjs +4 -3
- package/dist/components/message-list/message-list.recipe.mjs +3 -1
- package/dist/components/mode-bar/mode-bar.mjs +1 -1
- package/dist/components/notification-center/notification-center.mjs +1 -1
- package/dist/components/panel/panel-action.recipe.mjs +5 -5
- package/dist/components/panel/panel-header.recipe.mjs +10 -9
- package/dist/components/panel/panel.recipe.mjs +2 -2
- package/dist/components/picker-trigger-button/picker-trigger-button.mjs +1 -1
- package/dist/components/profile-panel/profile-panel.recipe.mjs +1 -1
- package/dist/components/scroll-area/scroll-area.css +74 -33
- package/dist/components/search/search-entity.recipe.mjs +3 -2
- package/dist/components/search/search-message.recipe.mjs +1 -0
- package/dist/components/search/search-thread.recipe.mjs +1 -1
- package/dist/components/search/search-trigger.mjs +1 -1
- package/dist/components/search/search.recipe.mjs +7 -3
- package/dist/components/segmented-control/segmented-control.mjs +9 -8
- package/dist/components/select/select.mjs +2 -2
- package/dist/components/sidebar/sidebar-item.recipe.mjs +5 -4
- package/dist/components/sidebar/sidebar-section.recipe.mjs +5 -2
- package/dist/components/sidebar/sidebar.recipe.mjs +3 -3
- package/dist/components/switch/switch.mjs +3 -3
- package/dist/components/tabs/tabs.mjs +1 -1
- package/dist/components/task-board/task-board-column.recipe.mjs +1 -1
- package/dist/components/task-list/task-list-section.recipe.mjs +1 -1
- package/dist/components/tasks/tasks-panel.recipe.mjs +4 -3
- package/dist/components/text-selection-toolbar/text-selection-toolbar.recipe.mjs +1 -1
- package/dist/components/textarea/textarea.mjs +1 -1
- package/dist/components/thread-panel/thread-panel.recipe.mjs +3 -3
- package/dist/components/toggle-group/toggle-group.mjs +1 -1
- package/dist/foundation.css +100 -5
- package/dist/lib/head-edges.mjs +38 -8
- package/dist/lib/list-cell.mjs +1 -0
- package/dist/styles.css +54 -7
- package/dist/tokens.json +260 -4
- package/dist/wip.d.mts +6 -1
- package/dist/wip.mjs +4 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,54 @@
|
|
|
1
|
+
## [0.65.0](https://github.com/botiverse/raft-design/compare/v0.64.0...v0.65.0) (2026-11-05)
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
|
|
5
|
+
- **MessageItemTrailing** (`@botiverse/raft-ui/wip`): a slot that ends a message's last line, the body's last child, such as the faces of the agents that read it. It takes no column: the last line keeps room for it and the text wraps under it only when that line is full; under a code block, a table or an image it has a line of its own.
|
|
6
|
+
- Why: the read faces kept a 63-77px column beside every message (stdrc, 2026-11-05).
|
|
7
|
+
- Docs: https://raft-design.botiverse.build/components/message-item/#trailing
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **Size chain tokens**: control, row, option and header heights are now one chain of variables in `tokens.json` and `foundation.css`, built on the existing `--control-height`, `--control-hit-inset` and `--control-embedded-size`; components read them instead of fixed sizes. New: `--row-height`, `--option-height`, `--control-air`, `--field-gap`, `--header-air`, `--header-height`, `--header-height-short`, `--bar-button-size`, `--bar-button-radius`, `--frame-item-height`, `--switch-width`, `--switch-height`, `--settings-row-inset`, `--section-gap`, `--page-inset`, `--rail-width`, `--rail-item`.
|
|
12
|
+
- Why: the desktop sizing review (stdrc, sizing option A, 2026-11-04) set one size per role; a component that hard-coded 32 or 56 drifted from the rest.
|
|
13
|
+
- Affects: nothing visible on its own; #973 and #974 set the desktop and touch values.
|
|
14
|
+
- Docs: https://raft-design.botiverse.build/foundations/spacing/
|
|
15
|
+
- **Desktop density** (with a mouse from 768px; touch is unchanged): one 34px control height for buttons, fields, selects, tabs, menu items and rows; 44px heads (34 + 2 × 5); 28px bar buttons with a 44px hit area; content 16px from a surface's edges (`--page-inset`, `--surface-inset`), 12px under a head (`--page-top`); settings cards 20 inside, 16 apart; sidebar rows and titles on two axes (glyphs at 16, text at 40) with a section's chevron at the end of its header; quick reactions a control high; empty states 16 from their sides. Also visible with a mouse: the rail is 49px wide with 36px items (was 57 and 40; `--rail-width-short`, `--rail-item-short`); a Switch is 38 × 22 (was 50 × 30); an IconTile takes its corner by size (27 chip, 35 control, 51 container) and an interactive badge is 20px high with the sticker corner; the hover toolbar is 42px (34px buttons) and floats half its height above the row; the Lightbox head is the header height.
|
|
16
|
+
- Why: stdrc's sizing review, rounds 3–9 (2026-11-04/05); the rules are in design/foundations/desktop-density.md.
|
|
17
|
+
- Affects: every desktop surface with a mouse. Apps that pad inside a raft-ui dialog, drawer or panel section with their own `md:` classes should drop them: the surface inset now replaces a caller's `px-*` again.
|
|
18
|
+
- Migrate: read `--surface-inset` / `--page-inset` instead of `px-5` for a surface's own padding; browser tests that measured 32px controls or 36px toolbars on a desktop now see 34 and 42.
|
|
19
|
+
- New variables: `--page-top`, `--rail-width-short`, `--rail-item-short` (tokens); `--surface-inset`, `--sidebar-glyph-axis`, `--sidebar-text-axis` (styles.css, next to `--column-margin`).
|
|
20
|
+
- Docs: https://raft-design.botiverse.build/foundations/desktop-density/
|
|
21
|
+
- **Touch density** (a phone width, and a touch screen at any width): 44px controls with a 10px radius (`--control-radius`; below 768px a mouse gets it too), denser rich rows (56px Activity, Inbox and person rows), a small ComboboxSelect keeps 8; icon tiles by size on touch.
|
|
22
|
+
- Why: the sizing audit's touch column (stdrc, 2026-11-04).
|
|
23
|
+
- Affects: phone widths and touch screens; a desktop with a mouse from 768px is unchanged.
|
|
24
|
+
- Docs: https://raft-design.botiverse.build/foundations/spacing/#hit-targets
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- **ScrollArea** and **InboxItem**: hover rules apply only with a pointer that can hover. On a touch screen the last thing tapped stays `:hover`, which left scrollbars shown, the thumb thick, a hidden gutter catching taps, and an Inbox row's action room open. ([#1017](https://github.com/botiverse/raft-design/pull/1017))
|
|
29
|
+
- Affects: touch screens only; mouse behavior is unchanged.
|
|
30
|
+
- **MessageActionCard**: the agent's reason is an upright quote (Space Grotesk has no italic), and a long permission list caps at 136px so the cut runs through a line of text at either description length. ([#1018](https://github.com/botiverse/raft-design/pull/1018))
|
|
31
|
+
|
|
32
|
+
## [0.64.0](https://github.com/botiverse/raft-design/compare/v0.63.1...v0.64.0) (2026-11-05)
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- **ButtonSplit**, **ButtonSplitMain** and **ButtonSplitMenu** (from `@botiverse/raft-ui/wip`): a `primary` or `default` Button with its ▾ menu, one shape in two parts. The parts touch and share one `radius-control` outline, the menu part's start border divides them, and each part hovers, presses, focuses and disables on its own; `disabled` on the group disables both. ([#1009](https://github.com/botiverse/raft-design/pull/1009))
|
|
37
|
+
- Why: Raft Chat's Members header needs Create agent with a ▾ for Take over a local session and Connect an external agent (cindyz, #triage:36bfa2f8; rule in [#1008](https://github.com/botiverse/raft-design/pull/1008)), and raft-ui only had `PanelSplitAction`, the frameless split of a panel header.
|
|
38
|
+
- Affects: nothing until used.
|
|
39
|
+
- Docs: https://raft-design.botiverse.build/components/button/#split-buttons
|
|
40
|
+
- **AppLinkCard** and its parts (from `@botiverse/raft-ui/wip`): an app link's card drawn in the message itself, after the link's paragraph, where the app's answer lists one (protocol version 3: a `card` item with `placement: "block"` in `items`). The link is never replaced: it stays a chip when the answer lists `inline`, and the author's plain link when it doesn't. `AppLinkCardHeader` (with `AppLinkCardIcon`, `AppLinkCardName` and `AppLinkCardHost`) is Raft's row naming the app; `AppLinkCardBody`, `AppLinkCardEyebrow`, `AppLinkCardTitle` and `AppLinkCardMeta` are the item, as on the hover card, and the title holds a link that opens the item; `AppLinkCardActions` holds at most two of the app's buttons and ⋯ for the rest, where an Open link goes; `AppLinkCardPlaceholder` is the card's size while a cached answer loads. 400px wide, the message's width in the phone layout. Navigation's App links now say what each item of an answer draws, in the list's order, with nothing inferred. ([#1010](https://github.com/botiverse/raft-design/pull/1010))
|
|
41
|
+
- Why: a card whose buttons matter, such as a Stamp approval, could be acted on only by hovering its chip on desktop, and not at all on a phone. The app lists every way its link is drawn, a chip, a hover card or a block card, as artin decided in #proj-raft-ui:d3528715 after the prototype (https://app.raft.build/artifacts/Q6r36on3znYNHnSbczSH3S); AppFather defined version 3 (#proj-raft-ui:46e9d07e and 8051e1a8). Tenny asked that Raft always draw the app's name and icon in its own frame.
|
|
42
|
+
- Affects: nothing until a product draws one. PreviewCard is untouched.
|
|
43
|
+
- Docs: https://raft-design.botiverse.build/components/app-link-card/ and https://raft-design.botiverse.build/patterns/navigation/#what-an-apps-answer-lists
|
|
44
|
+
|
|
45
|
+
### Fixed
|
|
46
|
+
|
|
47
|
+
- **CommentMarginItem**: a `CommentMargin` renders its cards where the browser has no `ResizeObserver` (jsdom, some embedded webviews) instead of throwing. Each card keeps the height it had when placed. ([#1007](https://github.com/botiverse/raft-design/pull/1007))
|
|
48
|
+
- Why: the same gap #996 closed in `useCommentMarginFits`; slock's Artifact viewer works around it today by not drawing the margin there (Remy, slock #13062).
|
|
49
|
+
- Affects: only environments without `ResizeObserver`; there a card that grows after it is placed doesn't push the cards below it.
|
|
50
|
+
- Docs: https://raft-design.botiverse.build/components/comment/
|
|
51
|
+
|
|
1
52
|
## [0.63.1](https://github.com/botiverse/raft-design/compare/v0.63.0...v0.63.1) (2026-11-05)
|
|
2
53
|
|
|
3
54
|
### Fixed
|
|
@@ -12,7 +12,7 @@ const activityInboxSidebar = tv({ slots: {
|
|
|
12
12
|
"py-0",
|
|
13
13
|
"[&_[data-slot=sidebar-group-label]]:mt-3 [&_[data-slot=sidebar-group-label]]:mb-0",
|
|
14
14
|
"**:data-[slot=sidebar-group-label]:flex **:data-[slot=sidebar-group-label]:items-center [&_[data-slot=sidebar-group-label]]:gap-2",
|
|
15
|
-
"[&_[data-slot=sidebar-group-label]]:px-5 [&_[data-slot=sidebar-group-label]]:py-0",
|
|
15
|
+
"[&_[data-slot=sidebar-group-label]]:px-5 [&_[data-slot=sidebar-group-label]]:py-0 md:pointer-fine:[&_[data-slot=sidebar-group-label]]:px-2",
|
|
16
16
|
"**:data-[slot=sidebar-group-label]:font-sans **:data-[slot=sidebar-group-label]:text-footnote [&_[data-slot=sidebar-group-label]]:font-bold",
|
|
17
17
|
"**:data-[slot=sidebar-group-label]:text-foreground-hint **:data-[slot=sidebar-group-label]:normal-case [&_[data-slot=sidebar-group-label]]:tracking-normal",
|
|
18
18
|
"**:data-activity-inbox-show-all:whitespace-nowrap"
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { tv } from "../../lib/tv.mjs";
|
|
2
2
|
//#region src/components/activity-inbox/activity-inbox-toolbar.recipe.ts
|
|
3
3
|
const activityInboxToolbar = tv({ slots: {
|
|
4
|
-
controls: [
|
|
4
|
+
controls: [
|
|
5
|
+
"flex shrink-0 flex-nowrap items-center gap-2",
|
|
6
|
+
"bg-layer-canvas px-(--column-margin) py-3",
|
|
7
|
+
"md:pointer-fine:py-[calc((var(--header-height)-var(--control-height))/2)]"
|
|
8
|
+
],
|
|
5
9
|
selectionToolbar: [
|
|
6
10
|
"flex min-h-11 shrink-0 flex-wrap items-center gap-2 pt-1.5 pb-[5px]",
|
|
11
|
+
"md:pointer-fine:pt-1 md:pointer-fine:pb-[5px]",
|
|
7
12
|
"border-b border-line-hairline bg-layer-canvas px-(--column-margin)",
|
|
8
13
|
"**:data-[activity-inbox-action=sort]:text-foreground-muted",
|
|
9
14
|
"**:data-[activity-inbox-action=sort]:hover:text-foreground"
|
|
@@ -22,18 +22,18 @@ const alertDialog = tv({
|
|
|
22
22
|
],
|
|
23
23
|
header: [
|
|
24
24
|
"grid grid-cols-[minmax(0,1fr)_max-content] items-center min-w-0",
|
|
25
|
-
"gap-2 px-
|
|
25
|
+
"gap-2 px-(--surface-inset) pt-(--surface-inset)",
|
|
26
26
|
headEdgePull
|
|
27
27
|
],
|
|
28
28
|
body: [
|
|
29
29
|
"min-w-0",
|
|
30
|
-
"px-
|
|
30
|
+
"px-(--surface-inset) pt-1.5 pb-(--surface-inset) first:pt-(--surface-inset)",
|
|
31
31
|
"font-sans text-subhead text-foreground-muted"
|
|
32
32
|
],
|
|
33
33
|
footer: [
|
|
34
34
|
"flex min-w-0 items-center justify-end",
|
|
35
35
|
...stackedActions,
|
|
36
|
-
"gap-2 px-
|
|
36
|
+
"gap-2 px-(--surface-inset) pb-(--surface-inset) first:pt-(--surface-inset) [[data-slot=alert-dialog-header]+&]:pt-(--surface-inset)"
|
|
37
37
|
],
|
|
38
38
|
title: ["m-0 min-w-0 text-foreground", "font-sans text-headline font-bold"],
|
|
39
39
|
description: ["m-0", "font-sans text-subhead text-foreground-muted"]
|
|
@@ -9,7 +9,7 @@ const announcement = tv({ slots: {
|
|
|
9
9
|
body: [
|
|
10
10
|
"min-h-0 max-h-[70dvh] overflow-y-auto pb-5",
|
|
11
11
|
"[&_[data-slot=announcement-picture]+[data-slot=announcement-text]]:mt-5",
|
|
12
|
-
"px-
|
|
12
|
+
"px-(--surface-inset) pt-(--surface-inset)"
|
|
13
13
|
],
|
|
14
14
|
header: ["flex min-w-0 items-center justify-between gap-3"],
|
|
15
15
|
label: ["flex min-w-0 items-center gap-2 wrap-break-word", "text-caption text-foreground-muted"],
|
|
@@ -21,7 +21,7 @@ const announcement = tv({ slots: {
|
|
|
21
21
|
"[&_ul]:list-disc [&_ol]:list-decimal [&_ul]:pl-5 [&_ol]:pl-5 [&_h1]:text-xl [&_h2]:text-lg [&_h3]:text-base [&_h1]:font-bold [&_h2]:font-bold [&_h3]:font-bold [&_pre]:max-w-full [&_pre]:overflow-x-auto",
|
|
22
22
|
"text-subhead text-foreground-muted"
|
|
23
23
|
],
|
|
24
|
-
footer: ["flex shrink-0 flex-wrap items-center justify-between gap-3 border-line-hairline py-4 @max-[28rem]/announcement:flex-col", "border-t-0 px-
|
|
24
|
+
footer: ["flex shrink-0 flex-wrap items-center justify-between gap-3 border-line-hairline py-4 @max-[28rem]/announcement:flex-col", "border-t-0 px-(--surface-inset) pt-0 pb-(--surface-inset)"],
|
|
25
25
|
action: ["ml-auto h-auto min-h-8 min-w-0 max-w-full py-1 whitespace-normal text-center font-bold @max-[28rem]/announcement:ml-0 @max-[28rem]/announcement:w-full"],
|
|
26
26
|
secondary: ["min-w-0 max-w-full whitespace-normal @max-[28rem]/announcement:w-full"]
|
|
27
27
|
} });
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ComponentPropsWithRef } from "react";
|
|
2
|
+
//#region src/components/app-link-card/app-link-card.d.ts
|
|
3
|
+
export type AppLinkCardProps = ComponentPropsWithRef<"div">;
|
|
4
|
+
/**
|
|
5
|
+
* An app link's card drawn in the message, after the link's paragraph, where
|
|
6
|
+
* the app's answer lists one (a `card` item with `placement: "block"`):
|
|
7
|
+
* Raft's header with the app's identity, the item, and buttons that work by
|
|
8
|
+
* keyboard and touch. It never replaces the link. A `group`: label it with
|
|
9
|
+
* `aria-labelledby` pointing at its `AppLinkCardTitle`, so someone who tabs
|
|
10
|
+
* to one of its buttons hears which item the button acts on.
|
|
11
|
+
*/
|
|
12
|
+
export declare function AppLinkCard({ className, ...props }: AppLinkCardProps): import("react").JSX.Element;
|
|
13
|
+
export type AppLinkCardPlaceholderProps = ComponentPropsWithRef<"div">;
|
|
14
|
+
/**
|
|
15
|
+
* The card's size while Raft fetches an answer it already knows lists a
|
|
16
|
+
* block card. Put the `AppLinkCardHeader` in it when the app is known; the item's
|
|
17
|
+
* lines and a button follow, as gray bars.
|
|
18
|
+
*/
|
|
19
|
+
export declare function AppLinkCardPlaceholder({ className, children, ...props }: AppLinkCardPlaceholderProps): import("react").JSX.Element;
|
|
20
|
+
export type AppLinkCardHeaderProps = ComponentPropsWithRef<"div">;
|
|
21
|
+
/** Raft's row at the top: `AppLinkCardIcon`, `AppLinkCardName`, then `AppLinkCardHost`. */
|
|
22
|
+
export declare function AppLinkCardHeader({ className, ...props }: AppLinkCardHeaderProps): import("react").JSX.Element;
|
|
23
|
+
export type AppLinkCardIconProps = ComponentPropsWithRef<"span">;
|
|
24
|
+
/** The app's registered icon, the image Raft stores and serves: one child at 16px. */
|
|
25
|
+
export declare function AppLinkCardIcon({ className, ...props }: AppLinkCardIconProps): import("react").JSX.Element;
|
|
26
|
+
export type AppLinkCardNameProps = ComponentPropsWithRef<"span">;
|
|
27
|
+
/** The app's name as Raft stores it. One line, truncated. */
|
|
28
|
+
export declare function AppLinkCardName({ className, ...props }: AppLinkCardNameProps): import("react").JSX.Element;
|
|
29
|
+
export type AppLinkCardHostProps = ComponentPropsWithRef<"span">;
|
|
30
|
+
/**
|
|
31
|
+
* The link's host, after a "·". When it doesn't fit it loses its start, so
|
|
32
|
+
* the end, which says where the link goes, stays.
|
|
33
|
+
*/
|
|
34
|
+
export declare function AppLinkCardHost({ className, children, ...props }: AppLinkCardHostProps): import("react").JSX.Element;
|
|
35
|
+
export type AppLinkCardBodyProps = ComponentPropsWithRef<"div">;
|
|
36
|
+
/** The item: `AppLinkCardEyebrow`, `AppLinkCardTitle`, `AppLinkCardMeta`, in that order. */
|
|
37
|
+
export declare function AppLinkCardBody({ className, ...props }: AppLinkCardBodyProps): import("react").JSX.Element;
|
|
38
|
+
export type AppLinkCardEyebrowProps = ComponentPropsWithRef<"p">;
|
|
39
|
+
/** The app's id for the item, above the title. One line, truncated. */
|
|
40
|
+
export declare function AppLinkCardEyebrow({ className, ...props }: AppLinkCardEyebrowProps): import("react").JSX.Element;
|
|
41
|
+
export type AppLinkCardTitleProps = ComponentPropsWithRef<"p">;
|
|
42
|
+
/**
|
|
43
|
+
* The app's title for the item, up to 2 lines, then an ellipsis. Put a link
|
|
44
|
+
* to the item in it: the title opens the item, in the title's ink.
|
|
45
|
+
*/
|
|
46
|
+
export declare function AppLinkCardTitle({ className, ...props }: AppLinkCardTitleProps): import("react").JSX.Element;
|
|
47
|
+
export type AppLinkCardMetaProps = ComponentPropsWithRef<"div">;
|
|
48
|
+
/** One state in a neutral Badge, then muted details. Wraps when it runs out of room. */
|
|
49
|
+
export declare function AppLinkCardMeta({ className, ...props }: AppLinkCardMetaProps): import("react").JSX.Element;
|
|
50
|
+
export type AppLinkCardActionsProps = ComponentPropsWithRef<"div">;
|
|
51
|
+
/**
|
|
52
|
+
* The app's buttons at the card's bottom end: at most two, the bordered one
|
|
53
|
+
* last, with a More menu (⋯) to their left for the rest.
|
|
54
|
+
*/
|
|
55
|
+
export declare function AppLinkCardActions({ className, ...props }: AppLinkCardActionsProps): import("react").JSX.Element;
|
|
56
|
+
//#endregion
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { tv } from "../../lib/tv.mjs";
|
|
2
|
+
import { messageEmbed } from "../message-embed/message-embed.recipe.mjs";
|
|
3
|
+
import { Skeleton } from "../skeleton/skeleton.mjs";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/app-link-card/app-link-card.tsx
|
|
6
|
+
const appLinkCard = tv({ slots: {
|
|
7
|
+
root: ["flex w-full max-w-[400px] min-w-0 flex-col max-md:max-w-none", "my-2 first:mt-0 last:mb-0"],
|
|
8
|
+
header: [
|
|
9
|
+
"flex min-w-0 items-center gap-1.5",
|
|
10
|
+
"border-b border-line-hairline px-4 py-2",
|
|
11
|
+
"font-sans text-caption text-foreground-muted"
|
|
12
|
+
],
|
|
13
|
+
icon: ["inline-flex size-4 shrink-0 items-center justify-center", "*:size-full *:shrink-0 [&>img]:rounded-[2px] [&>img]:object-cover"],
|
|
14
|
+
name: ["min-w-0 truncate", "font-semibold text-foreground"],
|
|
15
|
+
host: ["flex min-w-0 items-baseline", "before:shrink-0 before:pe-1.5 before:content-['·']"],
|
|
16
|
+
hostText: ["min-w-0 truncate"],
|
|
17
|
+
body: ["flex min-w-0 flex-col gap-1.5", "px-4 py-3 last:pb-4"],
|
|
18
|
+
eyebrow: ["m-0 truncate", "font-mono text-mono-meta text-foreground-muted"],
|
|
19
|
+
title: [
|
|
20
|
+
"m-0 line-clamp-2",
|
|
21
|
+
"font-sans text-headline font-bold text-foreground",
|
|
22
|
+
"[&_a]:rounded-[2px] [&_a]:decoration-[1.5px] [&_a]:underline-offset-[3px] [&_a]:hover:underline",
|
|
23
|
+
"[&_a]:outline-none [&_a]:focus-visible:shadow-focus"
|
|
24
|
+
],
|
|
25
|
+
meta: ["flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", "m-0 font-sans text-callout text-foreground-muted"],
|
|
26
|
+
actions: ["flex flex-wrap items-center justify-end gap-2", "px-4 pb-3 last:pb-4"],
|
|
27
|
+
placeholderLine: ["flex h-lh items-center"],
|
|
28
|
+
placeholderButton: ["h-8 md:pointer-fine:h-control w-28 rounded-control"]
|
|
29
|
+
} });
|
|
30
|
+
function frame(className) {
|
|
31
|
+
const { root } = appLinkCard();
|
|
32
|
+
return messageEmbed().root({ className: root({ className }) });
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* An app link's card drawn in the message, after the link's paragraph, where
|
|
36
|
+
* the app's answer lists one (a `card` item with `placement: "block"`):
|
|
37
|
+
* Raft's header with the app's identity, the item, and buttons that work by
|
|
38
|
+
* keyboard and touch. It never replaces the link. A `group`: label it with
|
|
39
|
+
* `aria-labelledby` pointing at its `AppLinkCardTitle`, so someone who tabs
|
|
40
|
+
* to one of its buttons hears which item the button acts on.
|
|
41
|
+
*/
|
|
42
|
+
function AppLinkCard({ className, ...props }) {
|
|
43
|
+
return /* @__PURE__ */ jsx("div", {
|
|
44
|
+
role: "group",
|
|
45
|
+
"data-slot": "app-link-card",
|
|
46
|
+
className: frame(className),
|
|
47
|
+
...props
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The card's size while Raft fetches an answer it already knows lists a
|
|
52
|
+
* block card. Put the `AppLinkCardHeader` in it when the app is known; the item's
|
|
53
|
+
* lines and a button follow, as gray bars.
|
|
54
|
+
*/
|
|
55
|
+
function AppLinkCardPlaceholder({ className, children, ...props }) {
|
|
56
|
+
const { body, eyebrow, title, meta, actions, placeholderLine, placeholderButton } = appLinkCard();
|
|
57
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
58
|
+
"data-slot": "app-link-card-placeholder",
|
|
59
|
+
"aria-busy": "true",
|
|
60
|
+
className: frame(className),
|
|
61
|
+
...props,
|
|
62
|
+
children: [
|
|
63
|
+
children,
|
|
64
|
+
/* @__PURE__ */ jsxs("div", {
|
|
65
|
+
"aria-hidden": "true",
|
|
66
|
+
className: body(),
|
|
67
|
+
children: [
|
|
68
|
+
/* @__PURE__ */ jsx("div", {
|
|
69
|
+
className: eyebrow({ className: placeholderLine() }),
|
|
70
|
+
children: /* @__PURE__ */ jsx(Skeleton, {
|
|
71
|
+
variant: "line",
|
|
72
|
+
className: "w-28"
|
|
73
|
+
})
|
|
74
|
+
}),
|
|
75
|
+
/* @__PURE__ */ jsxs("div", {
|
|
76
|
+
className: title({ className: "line-clamp-none" }),
|
|
77
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
78
|
+
className: placeholderLine(),
|
|
79
|
+
children: /* @__PURE__ */ jsx(Skeleton, {
|
|
80
|
+
variant: "line",
|
|
81
|
+
className: "w-full"
|
|
82
|
+
})
|
|
83
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
84
|
+
className: placeholderLine(),
|
|
85
|
+
children: /* @__PURE__ */ jsx(Skeleton, {
|
|
86
|
+
variant: "line",
|
|
87
|
+
className: "w-2/3"
|
|
88
|
+
})
|
|
89
|
+
})]
|
|
90
|
+
}),
|
|
91
|
+
/* @__PURE__ */ jsx("div", {
|
|
92
|
+
className: meta({ className: placeholderLine() }),
|
|
93
|
+
children: /* @__PURE__ */ jsx(Skeleton, {
|
|
94
|
+
variant: "line",
|
|
95
|
+
className: "w-40"
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
]
|
|
99
|
+
}),
|
|
100
|
+
/* @__PURE__ */ jsx("div", {
|
|
101
|
+
"aria-hidden": "true",
|
|
102
|
+
className: actions(),
|
|
103
|
+
children: /* @__PURE__ */ jsx(Skeleton, { className: placeholderButton() })
|
|
104
|
+
})
|
|
105
|
+
]
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
/** Raft's row at the top: `AppLinkCardIcon`, `AppLinkCardName`, then `AppLinkCardHost`. */
|
|
109
|
+
function AppLinkCardHeader({ className, ...props }) {
|
|
110
|
+
const { header } = appLinkCard();
|
|
111
|
+
return /* @__PURE__ */ jsx("div", {
|
|
112
|
+
"data-slot": "app-link-card-header",
|
|
113
|
+
className: header({ className }),
|
|
114
|
+
...props
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/** The app's registered icon, the image Raft stores and serves: one child at 16px. */
|
|
118
|
+
function AppLinkCardIcon({ className, ...props }) {
|
|
119
|
+
const { icon } = appLinkCard();
|
|
120
|
+
return /* @__PURE__ */ jsx("span", {
|
|
121
|
+
"data-slot": "app-link-card-icon",
|
|
122
|
+
className: icon({ className }),
|
|
123
|
+
...props
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/** The app's name as Raft stores it. One line, truncated. */
|
|
127
|
+
function AppLinkCardName({ className, ...props }) {
|
|
128
|
+
const { name } = appLinkCard();
|
|
129
|
+
return /* @__PURE__ */ jsx("span", {
|
|
130
|
+
"data-slot": "app-link-card-name",
|
|
131
|
+
className: name({ className }),
|
|
132
|
+
...props
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The link's host, after a "·". When it doesn't fit it loses its start, so
|
|
137
|
+
* the end, which says where the link goes, stays.
|
|
138
|
+
*/
|
|
139
|
+
function AppLinkCardHost({ className, children, ...props }) {
|
|
140
|
+
const { host, hostText } = appLinkCard();
|
|
141
|
+
return /* @__PURE__ */ jsx("span", {
|
|
142
|
+
"data-slot": "app-link-card-host",
|
|
143
|
+
className: host({ className }),
|
|
144
|
+
...props,
|
|
145
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
146
|
+
dir: "rtl",
|
|
147
|
+
className: hostText(),
|
|
148
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
149
|
+
dir: "ltr",
|
|
150
|
+
children
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
/** The item: `AppLinkCardEyebrow`, `AppLinkCardTitle`, `AppLinkCardMeta`, in that order. */
|
|
156
|
+
function AppLinkCardBody({ className, ...props }) {
|
|
157
|
+
const { body } = appLinkCard();
|
|
158
|
+
return /* @__PURE__ */ jsx("div", {
|
|
159
|
+
"data-slot": "app-link-card-body",
|
|
160
|
+
className: body({ className }),
|
|
161
|
+
...props
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
/** The app's id for the item, above the title. One line, truncated. */
|
|
165
|
+
function AppLinkCardEyebrow({ className, ...props }) {
|
|
166
|
+
const { eyebrow } = appLinkCard();
|
|
167
|
+
return /* @__PURE__ */ jsx("p", {
|
|
168
|
+
"data-slot": "app-link-card-eyebrow",
|
|
169
|
+
className: eyebrow({ className }),
|
|
170
|
+
...props
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* The app's title for the item, up to 2 lines, then an ellipsis. Put a link
|
|
175
|
+
* to the item in it: the title opens the item, in the title's ink.
|
|
176
|
+
*/
|
|
177
|
+
function AppLinkCardTitle({ className, ...props }) {
|
|
178
|
+
const { title } = appLinkCard();
|
|
179
|
+
return /* @__PURE__ */ jsx("p", {
|
|
180
|
+
"data-slot": "app-link-card-title",
|
|
181
|
+
className: title({ className }),
|
|
182
|
+
...props
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
/** One state in a neutral Badge, then muted details. Wraps when it runs out of room. */
|
|
186
|
+
function AppLinkCardMeta({ className, ...props }) {
|
|
187
|
+
const { meta } = appLinkCard();
|
|
188
|
+
return /* @__PURE__ */ jsx("div", {
|
|
189
|
+
"data-slot": "app-link-card-meta",
|
|
190
|
+
className: meta({ className }),
|
|
191
|
+
...props
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* The app's buttons at the card's bottom end: at most two, the bordered one
|
|
196
|
+
* last, with a More menu (⋯) to their left for the rest.
|
|
197
|
+
*/
|
|
198
|
+
function AppLinkCardActions({ className, ...props }) {
|
|
199
|
+
const { actions } = appLinkCard();
|
|
200
|
+
return /* @__PURE__ */ jsx("div", {
|
|
201
|
+
"data-slot": "app-link-card-actions",
|
|
202
|
+
className: actions({ className }),
|
|
203
|
+
...props
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
//#endregion
|
|
207
|
+
export { AppLinkCard, AppLinkCardActions, AppLinkCardBody, AppLinkCardEyebrow, AppLinkCardHeader, AppLinkCardHost, AppLinkCardIcon, AppLinkCardMeta, AppLinkCardName, AppLinkCardPlaceholder, AppLinkCardTitle };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { AppLinkCard, AppLinkCardActions, AppLinkCardActionsProps, AppLinkCardBody, AppLinkCardBodyProps, AppLinkCardEyebrow, AppLinkCardEyebrowProps, AppLinkCardHeader, AppLinkCardHeaderProps, AppLinkCardHost, AppLinkCardHostProps, AppLinkCardIcon, AppLinkCardIconProps, AppLinkCardMeta, AppLinkCardMetaProps, AppLinkCardName, AppLinkCardNameProps, AppLinkCardPlaceholder, AppLinkCardPlaceholderProps, AppLinkCardTitle, AppLinkCardTitleProps } from "./app-link-card.mjs";
|
|
2
|
+
export { AppLinkCard, AppLinkCardActions, type AppLinkCardActionsProps, AppLinkCardBody, type AppLinkCardBodyProps, AppLinkCardEyebrow, type AppLinkCardEyebrowProps, AppLinkCardHeader, type AppLinkCardHeaderProps, AppLinkCardHost, type AppLinkCardHostProps, AppLinkCardIcon, type AppLinkCardIconProps, AppLinkCardMeta, type AppLinkCardMetaProps, AppLinkCardName, type AppLinkCardNameProps, AppLinkCardPlaceholder, type AppLinkCardPlaceholderProps, AppLinkCardTitle, type AppLinkCardTitleProps };
|
|
@@ -3,35 +3,40 @@ import { tv } from "../../lib/tv.mjs";
|
|
|
3
3
|
const appRail = tv({ slots: {
|
|
4
4
|
root: [
|
|
5
5
|
"flex size-full flex-col items-center select-none",
|
|
6
|
-
"w-
|
|
6
|
+
"w-(--rail-width) border-r border-line-seam",
|
|
7
7
|
"bg-layer-frame text-foreground-on-frame",
|
|
8
|
-
"[@media(max-height:600px)]:w-
|
|
8
|
+
"[@media(max-height:600px)]:w-(--rail-width-short)"
|
|
9
9
|
],
|
|
10
10
|
header: [
|
|
11
11
|
"relative flex w-full shrink-0 items-center justify-center",
|
|
12
|
-
"h-(--shell-header-height,
|
|
13
|
-
"[@media(max-height:600px)]:h-
|
|
12
|
+
"h-(--shell-header-height,var(--header-height)) border-b border-line-seam",
|
|
13
|
+
"[@media(max-height:600px)]:h-(--header-height-short)"
|
|
14
14
|
],
|
|
15
15
|
nav: [
|
|
16
16
|
"flex w-full flex-1 flex-col items-center py-2",
|
|
17
17
|
"gap-1.5 pt-2.5 pb-2 first:pt-2",
|
|
18
18
|
"[@media(max-height:600px)]:pt-1.5 [@media(max-height:600px)]:first:pt-1.5",
|
|
19
|
+
"md:pointer-fine:gap-1 md:pointer-fine:pt-1.5 md:pointer-fine:first:pt-1.5",
|
|
19
20
|
"after:mt-auto after:h-28 after:min-h-0 after:w-12 after:shrink after:content-[''] after:frame-decor-rail"
|
|
20
21
|
],
|
|
21
|
-
footer: [
|
|
22
|
+
footer: [
|
|
23
|
+
"flex w-full shrink-0 flex-col items-center justify-center",
|
|
24
|
+
"h-auto gap-1.5 pb-2 [@media(max-height:600px)]:pb-1.5",
|
|
25
|
+
"md:pointer-fine:gap-1 md:pointer-fine:pb-1.5"
|
|
26
|
+
],
|
|
22
27
|
item: [
|
|
23
28
|
"relative inline-flex shrink-0 items-center justify-center",
|
|
24
29
|
"outline-none",
|
|
25
30
|
"disabled:pointer-events-none disabled:opacity-45",
|
|
26
31
|
"[&_svg]:shrink-0",
|
|
27
|
-
"size-
|
|
32
|
+
"size-(--rail-item) rounded-control border-[1.5px] border-transparent",
|
|
28
33
|
"text-foreground-on-frame [&_svg]:size-5",
|
|
29
34
|
"hover:bg-fill-frame-medium active:bg-fill-frame-medium",
|
|
30
35
|
"data-[selected=true]:border-line-frame-active data-[selected=true]:bg-layer-frame-active",
|
|
31
36
|
"data-[selected=true]:ring-[1.5px] data-[selected=true]:ring-sticker-edge",
|
|
32
37
|
"data-[selected=true]:text-foreground-on-frame-active",
|
|
33
38
|
"focus-visible:shadow-focus",
|
|
34
|
-
"[@media(max-height:600px)]:size-
|
|
39
|
+
"[@media(max-height:600px)]:size-(--rail-item-short)"
|
|
35
40
|
],
|
|
36
41
|
itemIcon: ["relative inline-flex items-center justify-center"],
|
|
37
42
|
itemLabel: ["sr-only"],
|
|
@@ -12,7 +12,7 @@ const appShell = tv({
|
|
|
12
12
|
"flex min-h-0 w-full overflow-visible md:overflow-clip",
|
|
13
13
|
"max-md:flex-col max-md:pt-[env(safe-area-inset-top,0px)]",
|
|
14
14
|
"font-sans",
|
|
15
|
-
"[--shell-header-height:
|
|
15
|
+
"[--shell-header-height:var(--header-height)] [@media(max-height:600px)]:[--shell-header-height:var(--header-height-short)]",
|
|
16
16
|
"bg-layer-canvas text-foreground"
|
|
17
17
|
],
|
|
18
18
|
railSlot: ["hidden h-full shrink-0 flex-col md:flex"],
|
|
@@ -64,6 +64,7 @@ const avatar = tv({
|
|
|
64
64
|
groupButton: [
|
|
65
65
|
"relative inline-flex shrink-0 cursor-pointer items-center p-1",
|
|
66
66
|
"rounded-chip border-0 bg-transparent",
|
|
67
|
+
"md:pointer-fine:h-(--bar-button-size) md:pointer-fine:justify-center md:pointer-fine:rounded-(--bar-button-radius) md:pointer-fine:py-0",
|
|
67
68
|
"transition-colors enabled:hover:bg-fill-medium enabled:active:bg-fill-medium",
|
|
68
69
|
"outline-none focus-visible:shadow-focus",
|
|
69
70
|
"disabled:cursor-default"
|
|
@@ -24,8 +24,13 @@ const badge = tv({
|
|
|
24
24
|
"[&_svg]:size-[13px]",
|
|
25
25
|
"enabled:hover:overlay-sticker-medium enabled:active:overlay-sticker-medium"
|
|
26
26
|
] },
|
|
27
|
-
soft: { root: [
|
|
27
|
+
soft: { root: [
|
|
28
|
+
"md:pointer-fine:h-5 md:pointer-fine:rounded-sticker",
|
|
29
|
+
"text-foreground",
|
|
30
|
+
"enabled:hover:overlay-medium enabled:active:overlay-medium"
|
|
31
|
+
] },
|
|
28
32
|
outline: { root: [
|
|
33
|
+
"md:pointer-fine:h-5 md:pointer-fine:rounded-sticker",
|
|
29
34
|
"font-mono text-mono-meta font-normal",
|
|
30
35
|
"bg-transparent text-foreground-muted inset-ring inset-ring-line-hairline",
|
|
31
36
|
"enabled:hover:bg-fill-medium enabled:active:bg-fill-medium"
|
|
@@ -67,7 +67,7 @@ const banner = tv({
|
|
|
67
67
|
defaultVariants: { size: "md" },
|
|
68
68
|
compoundVariants: [{
|
|
69
69
|
size: "sm",
|
|
70
|
-
class: { root: "has-[>[data-slot=banner-action],>[data-banner-close]]:py-[calc((--
|
|
70
|
+
class: { root: ["[--banner-button:--spacing(8)] md:pointer-fine:[--banner-button:var(--control-height)]", "has-[>[data-slot=banner-action],>[data-banner-close]]:py-[calc((var(--banner-button)-1lh)/2+--spacing(1))]"] }
|
|
71
71
|
}]
|
|
72
72
|
});
|
|
73
73
|
/**
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { DataAttributes } from "../../lib/data-attributes.mjs";
|
|
2
|
+
import { ButtonProps } from "./button.mjs";
|
|
3
|
+
import { ComponentProps } from "react";
|
|
4
|
+
//#region src/components/button/button-split.d.ts
|
|
5
|
+
/** The Button variants a split button takes: the filled ones and the bordered one. */
|
|
6
|
+
export type ButtonSplitVariant = "primary" | "default" | "warning" | "danger";
|
|
7
|
+
/** The Button sizes a split button takes. */
|
|
8
|
+
export type ButtonSplitSize = "md" | "lg";
|
|
9
|
+
export type ButtonSplitProps = ComponentProps<"div"> & DataAttributes & {
|
|
10
|
+
/** Disables both parts. Either part can also be disabled on its own. */
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* A Button with a menu beside it, such as Create agent and its ▾: a
|
|
15
|
+
* `ButtonSplitMain`, then a `ButtonSplitMenu` as the trigger of a DropdownMenu.
|
|
16
|
+
* The two parts touch and read as one button. A `role="group"`: name it with
|
|
17
|
+
* `aria-label`, not with the main part's name. For a frameless split in a
|
|
18
|
+
* panel header, use `PanelSplitAction`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function ButtonSplit({ disabled, className, ...props }: ButtonSplitProps): import("react").JSX.Element;
|
|
21
|
+
type ButtonSplitPartProps = Omit<ButtonProps, "variant" | "size"> & {
|
|
22
|
+
/** The Button variant; give both parts the same one. */
|
|
23
|
+
variant?: ButtonSplitVariant;
|
|
24
|
+
/** The Button size; give both parts the same one. */
|
|
25
|
+
size?: ButtonSplitSize;
|
|
26
|
+
};
|
|
27
|
+
export type ButtonSplitMainProps = ButtonSplitPartProps;
|
|
28
|
+
/** The action of a split button: a Button, rounded only on its start side. */
|
|
29
|
+
export declare function ButtonSplitMain({ variant, size, disabled, className, ...props }: ButtonSplitMainProps): import("react").JSX.Element;
|
|
30
|
+
export type ButtonSplitMenuProps = ButtonSplitPartProps;
|
|
31
|
+
/**
|
|
32
|
+
* The narrow part of a split button that opens its menu, with a chevron by
|
|
33
|
+
* default; its start border is the divider. Render it as the trigger of a
|
|
34
|
+
* DropdownMenu:
|
|
35
|
+
* `<DropdownMenuTrigger render={<ButtonSplitMenu aria-label="More ways to create an agent" />} />`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function ButtonSplitMenu({ variant, size, disabled, className, children, ...props }: ButtonSplitMenuProps): import("react").JSX.Element;
|
|
38
|
+
//#endregion
|