@sableui/react 0.13.1 → 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/AGENTS.md +2 -2
- package/CHANGELOG.md +132 -0
- package/CONTRIBUTING.md +7 -5
- package/DESIGN.md +8 -3
- package/bin/sable.mjs +17 -1
- package/dist/blocks/approval-block.js +93 -0
- package/dist/blocks/approval-block.meta.js +23 -0
- package/dist/blocks/index.d.ts +17 -2
- package/dist/blocks/index.js +4 -0
- package/dist/components/accordion/accordion.meta.js +10 -3
- package/dist/components/area-chart/area-chart.meta.js +10 -3
- package/dist/components/attachment/attachment.meta.js +26 -0
- package/dist/components/badge/badge.meta.js +2 -1
- package/dist/components/banner/banner.meta.js +4 -2
- package/dist/components/bar-chart/bar-chart.meta.js +11 -4
- package/dist/components/bottom-nav/bottom-nav.js +3 -1
- package/dist/components/bottom-nav/bottom-nav.meta.js +32 -0
- package/dist/components/breadcrumbs/breadcrumbs.meta.js +26 -0
- package/dist/components/button/button.js +21 -4
- package/dist/components/button/button.meta.js +7 -7
- package/dist/components/button-group/button-group.meta.js +2 -1
- package/dist/components/calendar/calendar.js +18 -1
- package/dist/components/calendar/calendar.meta.js +38 -0
- package/dist/components/carousel/carousel.js +6 -4
- package/dist/components/carousel/carousel.meta.js +42 -0
- package/dist/components/chat-composer/chat-composer.js +21 -21
- package/dist/components/chat-composer/chat-composer.meta.js +38 -0
- package/dist/components/chat-composer/use-eased-growth.js +70 -0
- package/dist/components/checkbox/checkbox-mark.js +8 -1
- package/dist/components/checkbox/checkbox.js +3 -1
- package/dist/components/checkbox/checkbox.meta.js +34 -1
- package/dist/components/checkbox-card/checkbox-card.js +3 -0
- package/dist/components/checkbox-card/checkbox-card.meta.js +32 -0
- package/dist/components/code-block/code-block.js +14 -3
- package/dist/components/code-block/code-block.meta.js +28 -1
- package/dist/components/collapsible/collapsible.meta.js +34 -1
- package/dist/components/color-field/color-field.meta.js +26 -0
- package/dist/components/combobox/combobox.meta.js +48 -2
- package/dist/components/command-menu/command-menu.js +8 -3
- package/dist/components/command-menu/command-menu.meta.js +44 -3
- package/dist/components/confirm-button/confirm-button.js +179 -0
- package/dist/components/confirm-button/confirm-button.meta.js +143 -0
- package/dist/components/confirm-button/index.js +7 -0
- package/dist/components/context-card/context-card.meta.js +2 -1
- package/dist/components/data-table/data-table.js +6 -2
- package/dist/components/data-table/data-table.meta.js +32 -0
- package/dist/components/dialog/dialog.meta.js +6 -3
- package/dist/components/disclosure-picker/disclosure-picker.meta.js +2 -1
- package/dist/components/entity/entity.meta.js +26 -0
- package/dist/components/feedback/feedback.js +113 -47
- package/dist/components/feedback/feedback.meta.js +49 -4
- package/dist/components/inline-status/inline-status.meta.js +2 -1
- package/dist/components/input-group/input-group.meta.js +27 -1
- package/dist/components/line-chart/line-chart.meta.js +10 -3
- package/dist/components/menu/menu.meta.js +2 -1
- package/dist/components/menubar/menubar.meta.js +2 -1
- package/dist/components/meter/meter.meta.js +2 -1
- package/dist/components/middle-truncate/middle-truncate.meta.js +1 -1
- package/dist/components/note/note.meta.js +34 -1
- package/dist/components/notification-badge/notification-badge.js +17 -5
- package/dist/components/notification-badge/notification-badge.meta.js +34 -2
- package/dist/components/number-ticker/index.js +7 -0
- package/dist/components/number-ticker/number-ticker.js +246 -0
- package/dist/components/number-ticker/number-ticker.meta.js +159 -0
- package/dist/components/otp-field/otp-field.js +23 -3
- package/dist/components/otp-field/otp-field.meta.js +23 -2
- package/dist/components/page-loader/page-loader.meta.js +2 -1
- package/dist/components/panel-group/panel-group.meta.js +26 -0
- package/dist/components/panel-section/panel-section.js +37 -10
- package/dist/components/panel-section/panel-section.meta.js +32 -0
- package/dist/components/pie-chart/pie-chart.js +20 -11
- package/dist/components/pie-chart/pie-chart.meta.js +17 -4
- package/dist/components/pill/pill.meta.js +34 -1
- package/dist/components/popover/popover.meta.js +4 -2
- package/dist/components/progress/progress.meta.js +2 -1
- package/dist/components/radar-chart/radar-chart.js +4 -3
- package/dist/components/radar-chart/radar-chart.meta.js +10 -3
- package/dist/components/radial-chart/radial-chart.js +22 -22
- package/dist/components/radial-chart/radial-chart.meta.js +17 -4
- package/dist/components/radio/radio.meta.js +28 -1
- package/dist/components/radio-card/radio-card.meta.js +38 -0
- package/dist/components/relative-time/relative-time.meta.js +32 -0
- package/dist/components/scroller/scroller.meta.js +32 -0
- package/dist/components/scrub-field/scrub-field.meta.js +32 -0
- package/dist/components/search-input/search-input.js +9 -7
- package/dist/components/search-input/search-input.meta.js +26 -0
- package/dist/components/segmented-bar/segmented-bar.meta.js +2 -1
- package/dist/components/select/select.meta.js +4 -2
- package/dist/components/sheet/sheet.meta.js +4 -2
- package/dist/components/show-more/show-more.js +1 -4
- package/dist/components/show-more/show-more.meta.js +38 -0
- package/dist/components/side-nav/side-nav.js +21 -1
- package/dist/components/side-nav/side-nav.meta.js +34 -1
- package/dist/components/side-nav/side-nav.variants.js +3 -1
- package/dist/components/sidebar/sidebar.js +31 -8
- package/dist/components/sidebar/sidebar.meta.js +38 -0
- package/dist/components/skeleton/skeleton.meta.js +2 -1
- package/dist/components/slider/slider.meta.js +26 -0
- package/dist/components/sparkline/sparkline.js +7 -4
- package/dist/components/sparkline/sparkline.meta.js +9 -2
- package/dist/components/spinner/spinner.meta.js +2 -1
- package/dist/components/stepper/stepper.meta.js +4 -2
- package/dist/components/switch/switch.meta.js +2 -1
- package/dist/components/table/table.meta.js +28 -1
- package/dist/components/tabs/tabs.js +27 -8
- package/dist/components/tabs/tabs.meta.js +50 -5
- package/dist/components/theme-selector/theme-selector.meta.js +32 -0
- package/dist/components/theme-switcher/theme-switcher.js +4 -1
- package/dist/components/theme-switcher/theme-switcher.meta.js +42 -0
- package/dist/components/thinking-orb/thinking-orb.meta.js +4 -2
- package/dist/components/toast/toast.meta.js +2 -1
- package/dist/components/toggle/toggle.js +28 -5
- package/dist/components/toggle/toggle.meta.js +46 -3
- package/dist/components/tool-call/index.js +8 -0
- package/dist/components/tool-call/tool-call.js +117 -0
- package/dist/components/tool-call/tool-call.meta.js +147 -0
- package/dist/components/tool-call/tool-call.variants.js +33 -0
- package/dist/components/toolbar/toolbar.meta.js +2 -1
- package/dist/components/tooltip/index.js +3 -1
- package/dist/components/tooltip/tooltip.js +84 -0
- package/dist/components/tooltip/tooltip.meta.js +18 -2
- package/dist/components/tree/tree.js +71 -6
- package/dist/components/tree/tree.meta.js +32 -0
- package/dist/components/video/video.meta.js +32 -0
- package/dist/file-hashes.json +139 -122
- package/dist/foundations/motion.js +49 -3
- package/dist/index.d.ts +616 -46
- package/dist/index.js +4 -1
- package/dist/lib/chart/cartesian.js +9 -10
- package/dist/lib/chart/chart-center.js +30 -0
- package/dist/lib/chart/chart-frame.js +15 -8
- package/dist/lib/chart/dim.js +13 -0
- package/dist/lib/chart/format.js +7 -1
- package/dist/lib/chart/motion.js +6 -4
- package/dist/lib/highlight-glide.js +68 -10
- package/dist/lib/taxonomy.js +3 -0
- package/dist/{meta-DVG8ceze.d.ts → meta-DX5r5wQL.d.ts} +8 -0
- package/dist/motion/MotionProvider.js +45 -3
- package/dist/motion/Reveal.js +1 -1
- package/dist/motion/TextReveal.js +1 -1
- package/dist/motion/index.js +15 -2
- package/dist/motion/useAutoSize.js +42 -0
- package/dist/motion/useShake.js +25 -0
- package/dist/patterns/index.d.ts +1 -1
- package/dist/styles.css +298 -5
- package/llms/blocks/approval-block.md +128 -0
- package/llms/components/accordion.md +4 -3
- package/llms/components/area-chart.md +4 -3
- package/llms/components/attachment.md +7 -0
- package/llms/components/badge.md +1 -1
- package/llms/components/banner.md +2 -2
- package/llms/components/bar-chart.md +5 -4
- package/llms/components/bottom-nav.md +8 -0
- package/llms/components/breadcrumbs.md +7 -0
- package/llms/components/button-group.md +1 -1
- package/llms/components/button.md +6 -6
- package/llms/components/calendar.md +9 -0
- package/llms/components/carousel.md +17 -0
- package/llms/components/chat-composer.md +9 -0
- package/llms/components/checkbox-card.md +8 -0
- package/llms/components/checkbox.md +9 -1
- package/llms/components/code-block.md +8 -0
- package/llms/components/collapsible.md +9 -1
- package/llms/components/color-field.md +7 -0
- package/llms/components/combobox.md +12 -2
- package/llms/components/command-menu.md +12 -3
- package/llms/components/confirm-button.md +92 -0
- package/llms/components/context-card.md +1 -1
- package/llms/components/data-table.md +8 -0
- package/llms/components/dialog.md +3 -3
- package/llms/components/disclosure-picker.md +1 -1
- package/llms/components/entity.md +7 -0
- package/llms/components/feedback.md +22 -3
- package/llms/components/inline-status.md +1 -1
- package/llms/components/input-group.md +8 -1
- package/llms/components/line-chart.md +4 -3
- package/llms/components/menu.md +1 -1
- package/llms/components/menubar.md +1 -1
- package/llms/components/meter.md +1 -1
- package/llms/components/middle-truncate.md +1 -1
- package/llms/components/note.md +9 -1
- package/llms/components/notification-badge.md +10 -2
- package/llms/components/number-ticker.md +86 -0
- package/llms/components/otp-field.md +15 -1
- package/llms/components/page-loader.md +1 -1
- package/llms/components/panel-group.md +7 -0
- package/llms/components/panel-section.md +8 -0
- package/llms/components/pie-chart.md +6 -4
- package/llms/components/pill.md +9 -1
- package/llms/components/popover.md +2 -2
- package/llms/components/progress.md +1 -1
- package/llms/components/radar-chart.md +4 -3
- package/llms/components/radial-chart.md +6 -4
- package/llms/components/radio-card.md +9 -0
- package/llms/components/radio.md +8 -1
- package/llms/components/relative-time.md +8 -0
- package/llms/components/scroller.md +8 -0
- package/llms/components/scrub-field.md +8 -0
- package/llms/components/search-input.md +7 -0
- package/llms/components/segmented-bar.md +1 -1
- package/llms/components/select.md +2 -2
- package/llms/components/sheet.md +2 -2
- package/llms/components/show-more.md +9 -0
- package/llms/components/side-nav.md +9 -1
- package/llms/components/sidebar.md +9 -0
- package/llms/components/skeleton.md +1 -1
- package/llms/components/slider.md +7 -0
- package/llms/components/sparkline.md +3 -2
- package/llms/components/spinner.md +1 -1
- package/llms/components/stepper.md +2 -2
- package/llms/components/switch.md +1 -1
- package/llms/components/table.md +8 -1
- package/llms/components/tabs.md +22 -4
- package/llms/components/theme-selector.md +8 -0
- package/llms/components/theme-switcher.md +17 -0
- package/llms/components/thinking-orb.md +2 -2
- package/llms/components/toast.md +1 -1
- package/llms/components/toggle.md +20 -3
- package/llms/components/tool-call.md +115 -0
- package/llms/components/toolbar.md +1 -1
- package/llms/components/tooltip.md +12 -2
- package/llms/components/tree.md +8 -0
- package/llms/components/video.md +8 -0
- package/llms/foundations/motion.md +48 -3
- package/llms/reference/utilities.md +23 -1
- package/llms/tasks/reviewing-motion.md +14 -0
- package/llms-full.txt +1004 -98
- package/llms.txt +16 -11
- package/package.json +1 -1
- package/registry.json +2708 -187
- package/targets/ios.json +27 -0
|
@@ -3,19 +3,38 @@ import { Tabs as Base } from "@base-ui/react/tabs";
|
|
|
3
3
|
import { createContext, useContext } from "react";
|
|
4
4
|
import { cn } from "../../lib/cn.js";
|
|
5
5
|
import { trimLabel } from "../../lib/trim-label.js";
|
|
6
|
-
import {
|
|
7
|
-
|
|
6
|
+
import { motionAttribute } from "../../motion/MotionProvider.js";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
function Tabs({ motion, ...props }) {
|
|
9
|
+
return /* @__PURE__ */ jsx(Base.Root, { "data-motion": motionAttribute(motion), ...props });
|
|
10
|
+
}
|
|
11
|
+
Tabs.displayName = "Tabs";
|
|
8
12
|
var TabsVariantContext = createContext("underline");
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
var UNDERLINE_HANDOFF = "[&:has(>[data-tabs-indicator]:not([hidden]))>[role=tab]]:border-transparent";
|
|
14
|
+
var PILL_HANDOFF = "[&:has(>[data-tabs-indicator]:not([hidden]))>[role=tab]]:bg-transparent";
|
|
15
|
+
var INDICATOR = "pointer-events-none absolute left-0 w-(--active-tab-width) translate-x-(--active-tab-left) motion-glide";
|
|
16
|
+
var INDICATOR_UNDERLINE = "-bottom-px h-0.5 bg-foreground";
|
|
17
|
+
var INDICATOR_PILL = "top-0 h-(--active-tab-height) translate-y-(--active-tab-top) rounded-[var(--button-radius,9999px)] bg-muted";
|
|
18
|
+
function TabsList({ className, variant = "underline", children, ...props }) {
|
|
19
|
+
return /* @__PURE__ */ jsx(TabsVariantContext.Provider, { value: variant, children: /* @__PURE__ */ jsxs(
|
|
11
20
|
Base.List,
|
|
12
21
|
{
|
|
13
22
|
"data-variant": variant,
|
|
14
23
|
className: cn(
|
|
15
|
-
variant === "underline" ? "inline-flex items-center gap-1 border-b border-border" : "inline-flex items-center gap-1",
|
|
24
|
+
variant === "underline" ? cn("relative inline-flex items-center gap-1 border-b border-border", UNDERLINE_HANDOFF) : cn("relative inline-flex items-center gap-1", PILL_HANDOFF),
|
|
16
25
|
className
|
|
17
26
|
),
|
|
18
|
-
...props
|
|
27
|
+
...props,
|
|
28
|
+
children: [
|
|
29
|
+
/* @__PURE__ */ jsx(
|
|
30
|
+
Base.Indicator,
|
|
31
|
+
{
|
|
32
|
+
"data-tabs-indicator": "",
|
|
33
|
+
className: cn(INDICATOR, variant === "underline" ? INDICATOR_UNDERLINE : INDICATOR_PILL)
|
|
34
|
+
}
|
|
35
|
+
),
|
|
36
|
+
children
|
|
37
|
+
]
|
|
19
38
|
}
|
|
20
39
|
) });
|
|
21
40
|
}
|
|
@@ -25,7 +44,7 @@ function TabsTrigger({ className, children, ...props }) {
|
|
|
25
44
|
Base.Tab,
|
|
26
45
|
{
|
|
27
46
|
className: cn(
|
|
28
|
-
"cursor-pointer text-label-lg text-muted-foreground motion-interactive hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring data-[active]:text-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
47
|
+
"relative cursor-pointer text-label-lg text-muted-foreground motion-interactive hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring data-[active]:text-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
29
48
|
variant === "underline" ? (
|
|
30
49
|
// On the control ladder like the pill variant (it was py-2 plus
|
|
31
50
|
// the label's line, ~36px — no rung).
|
|
@@ -43,7 +62,7 @@ function TabsPanel({ className, ...props }) {
|
|
|
43
62
|
Base.Panel,
|
|
44
63
|
{
|
|
45
64
|
className: cn(
|
|
46
|
-
"pt-4 text-foreground focus-visible:outline-none",
|
|
65
|
+
"pt-4 text-foreground focus-visible:outline-none not-data-[activation-direction=none]:animate-fade-in data-[ending-style]:hidden",
|
|
47
66
|
className
|
|
48
67
|
),
|
|
49
68
|
...props
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/components/tabs/tabs.meta.ts
|
|
2
2
|
var tabsMeta = {
|
|
3
3
|
name: "Tabs",
|
|
4
|
-
summary: "Tabs over Base UI Tabs (roving focus, arrow-key nav, ARIA tablist wiring) in two styles: underline (page sections) or pill (compact switchers). Tabs (Root) + TabsList + TabsTrigger + TabsPanel.",
|
|
4
|
+
summary: "Tabs over Base UI Tabs (roving focus, arrow-key nav, ARIA tablist wiring) in two styles: underline (page sections) or pill (compact switchers), with one indicator that glides to the active tab. Tabs (Root) + TabsList + TabsTrigger + TabsPanel.",
|
|
5
5
|
status: "stable",
|
|
6
6
|
keywords: [
|
|
7
7
|
"tabs",
|
|
@@ -26,11 +26,13 @@ var tabsMeta = {
|
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
guidance: "dont",
|
|
29
|
-
description: "Do not use Tabs for unrelated pages or primary navigation that needs distinct URLs."
|
|
29
|
+
description: "Do not use Tabs for unrelated pages or primary navigation that needs distinct URLs.",
|
|
30
|
+
instead: ["SideNav", "NavigationMenu"]
|
|
30
31
|
},
|
|
31
32
|
{
|
|
32
33
|
guidance: "dont",
|
|
33
|
-
description: "Do not use tabs for sequential steps. Tabs are peer views the user returns to in any order; a process with an order needs a stepper, or the user will skip ahead."
|
|
34
|
+
description: "Do not use tabs for sequential steps. Tabs are peer views the user returns to in any order; a process with an order needs a stepper, or the user will skip ahead.",
|
|
35
|
+
instead: ["Stepper"]
|
|
34
36
|
}
|
|
35
37
|
],
|
|
36
38
|
anatomy: [
|
|
@@ -69,13 +71,19 @@ var tabsMeta = {
|
|
|
69
71
|
type: "Base UI Tabs.Root props (value, defaultValue, onValueChange, orientation)",
|
|
70
72
|
default: "defaultValue: 0",
|
|
71
73
|
description: "Controls the active tab. Uncontrolled via defaultValue; controlled via value + onValueChange."
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "motion",
|
|
77
|
+
type: '"full" | "subtle" | "off"',
|
|
78
|
+
default: '"full"',
|
|
79
|
+
description: "On Tabs: how much motion this component uses: `full` (default) inherits the app's policy, `subtle` keeps feedback and drops the glide, `off` stills it. It can only turn motion down \u2014 it never overrides a reader's reduce-motion setting."
|
|
72
80
|
}
|
|
73
81
|
],
|
|
74
82
|
subcomponents: [
|
|
75
83
|
{
|
|
76
84
|
name: "TabsList",
|
|
77
85
|
type: 'Base UI Tabs.List props (activateOnFocus, loopFocus) + variant?: "underline" | "pill" + className',
|
|
78
|
-
description: `The tablist row. variant="underline" (default) draws a divider rule beneath the triggers; variant="pill" drops the rule and fills the active trigger, which takes the theme's button shape (--button-radius). Triggers inherit the variant.`
|
|
86
|
+
description: `The tablist row. variant="underline" (default) draws a divider rule beneath the triggers; variant="pill" drops the rule and fills the active trigger, which takes the theme's button shape (--button-radius). Triggers inherit the variant. It renders the gliding indicator (Base UI Tabs.Indicator) itself, so do not add one.`
|
|
79
87
|
},
|
|
80
88
|
{
|
|
81
89
|
name: "TabsTrigger",
|
|
@@ -94,11 +102,44 @@ var tabsMeta = {
|
|
|
94
102
|
}
|
|
95
103
|
],
|
|
96
104
|
tokensUsed: ["border", "muted", "muted-foreground", "foreground", "ring"],
|
|
105
|
+
motion: [
|
|
106
|
+
{
|
|
107
|
+
slot: "arrival",
|
|
108
|
+
recipe: "none",
|
|
109
|
+
part: "Root",
|
|
110
|
+
why: "Tabs arrive with the section they organise. An entrance of their own would announce navigation chrome louder than the content it navigates."
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
slot: "response",
|
|
114
|
+
recipe: "motion-interactive",
|
|
115
|
+
part: "TabsTrigger",
|
|
116
|
+
why: "The label steps from muted to foreground under the pointer; the ease is what makes that read as an answer rather than a repaint."
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
slot: "transition",
|
|
120
|
+
recipe: "motion-glide",
|
|
121
|
+
part: "Indicator",
|
|
122
|
+
why: `One underline (or pill) travels to the new tab and stretches to its width. A mark that teleports makes the reader re-find the selection on every click; one that moves is tracked without looking. Expressive: motion="subtle" lands it in place. Each tab's own mark is the fallback until the indicator has measured, so the server render is already right.`
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
slot: "transition",
|
|
126
|
+
recipe: "animate-fade-in",
|
|
127
|
+
part: "TabsPanel",
|
|
128
|
+
why: 'The incoming panel fades in, opacity only, so a switch between two similar panels still registers. Skipped on first render (activation direction "none") so a page of tabs does not fade in on load, and the outgoing panel leaves layout at once so a quick second click never stacks both.'
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
slot: "activity",
|
|
132
|
+
recipe: "none",
|
|
133
|
+
part: "Root",
|
|
134
|
+
why: "Switching tabs is synchronous. A panel that loads data shows its own Skeleton or Spinner inside the panel, where the wait is."
|
|
135
|
+
}
|
|
136
|
+
],
|
|
97
137
|
a11y: [
|
|
98
138
|
"Base UI provides the tablist/tab/tabpanel roles, aria-selected, and roving tabindex.",
|
|
99
139
|
"Arrow keys move between tabs; Home/End jump to first/last; panels are linked via aria-controls.",
|
|
100
140
|
"Active tab styling reads from the data-active attribute; disabled tabs from data-disabled. Disabled tabs are skipped by arrow-key navigation.",
|
|
101
|
-
"Keyboard focus shows a ring on the focused trigger in both variants; the panel remains focusable."
|
|
141
|
+
"Keyboard focus shows a ring on the focused trigger in both variants; the panel remains focusable.",
|
|
142
|
+
'The gliding indicator is role="presentation" decoration; aria-selected on the tab carries the state.'
|
|
102
143
|
],
|
|
103
144
|
examples: [
|
|
104
145
|
{
|
|
@@ -112,6 +153,10 @@ var tabsMeta = {
|
|
|
112
153
|
{
|
|
113
154
|
label: "With a disabled tab",
|
|
114
155
|
code: '<Tabs defaultValue="overview"><TabsList><TabsTrigger value="overview">Overview</TabsTrigger><TabsTrigger value="billing" disabled>Billing</TabsTrigger></TabsList><TabsPanel value="overview">Overview content</TabsPanel></Tabs>'
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
label: "Subtle motion",
|
|
159
|
+
code: '<Tabs defaultValue="table" motion="subtle"><TabsList><TabsTrigger value="table">Table</TabsTrigger><TabsTrigger value="chart">Chart</TabsTrigger></TabsList><TabsPanel value="table">Rows</TabsPanel><TabsPanel value="chart">Trend</TabsPanel></Tabs>'
|
|
115
160
|
}
|
|
116
161
|
]
|
|
117
162
|
};
|
|
@@ -117,6 +117,38 @@ var themeSelectorMeta = {
|
|
|
117
117
|
"muted-foreground",
|
|
118
118
|
"ring"
|
|
119
119
|
],
|
|
120
|
+
motion: [
|
|
121
|
+
{
|
|
122
|
+
slot: "arrival",
|
|
123
|
+
recipe: "none",
|
|
124
|
+
part: "Root",
|
|
125
|
+
why: "It sits in a settings panel or a popover, which owns the entrance."
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
slot: "response",
|
|
129
|
+
recipe: "motion-interactive",
|
|
130
|
+
part: "Option (list)",
|
|
131
|
+
why: "List rows ease their hover fill like Menu and Select rows, so a theme list beside a menu reads as the same kind of row."
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
slot: "response",
|
|
135
|
+
recipe: "motion-interactive",
|
|
136
|
+
part: "Name (grid)",
|
|
137
|
+
why: "In the grid, the theme's name brightens on hover, eased. The preview tile's outline steps rather than easing \u2014 outline is not in the recipe's transition list \u2014 so the name carries the motion."
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
slot: "transition",
|
|
141
|
+
recipe: "motion-interactive",
|
|
142
|
+
part: "Check (list)",
|
|
143
|
+
why: "The check fades into the chosen row and out of the old one, so the choice reads as moving, at the same moment the whole interface repaints in the new theme."
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
slot: "activity",
|
|
147
|
+
recipe: "none",
|
|
148
|
+
part: "Root",
|
|
149
|
+
why: "Applying a theme is synchronous: the whole interface repaints at once, and that repaint is the feedback."
|
|
150
|
+
}
|
|
151
|
+
],
|
|
120
152
|
a11y: [
|
|
121
153
|
'The container is role="radiogroup" with an aria-label; each card is a role="radio" button with aria-checked reflecting the selected theme.',
|
|
122
154
|
"Standard radio keyboard: arrow keys move the check \u2014 calling onValueChange on each press \u2014 and Space checks the focused option. That instant switching is right for a free, reversible choice; where a switch has a cost, use a DisclosurePicker list (a listbox: arrows move, Enter commits) with ThemePreview tiles instead.",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// src/components/theme-switcher/theme-switcher.tsx
|
|
4
4
|
import { Radio as RadioBase } from "@base-ui/react/radio";
|
|
5
5
|
import { Icon } from "../../icon/index.js";
|
|
6
|
+
import { motionAttribute } from "../../motion/MotionProvider.js";
|
|
6
7
|
import { cn } from "../../lib/cn.js";
|
|
7
8
|
import {
|
|
8
9
|
SEGMENTED_INNER_RADIUS,
|
|
@@ -21,6 +22,7 @@ function ThemeSwitcher({
|
|
|
21
22
|
value,
|
|
22
23
|
onValueChange,
|
|
23
24
|
label = "Theme",
|
|
25
|
+
motion,
|
|
24
26
|
className,
|
|
25
27
|
ref,
|
|
26
28
|
...props
|
|
@@ -31,6 +33,7 @@ function ThemeSwitcher({
|
|
|
31
33
|
{
|
|
32
34
|
ref,
|
|
33
35
|
"aria-label": label,
|
|
36
|
+
"data-motion": motionAttribute(motion),
|
|
34
37
|
value,
|
|
35
38
|
onValueChange: (next) => onValueChange(next),
|
|
36
39
|
className: cn(
|
|
@@ -54,7 +57,7 @@ function ThemeSwitcher({
|
|
|
54
57
|
{
|
|
55
58
|
"aria-hidden": true,
|
|
56
59
|
className: cn(
|
|
57
|
-
"pointer-events-none absolute start-0.5 top-0.5 size-7 bg-accent
|
|
60
|
+
"pointer-events-none absolute start-0.5 top-0.5 size-7 bg-accent motion-glide",
|
|
58
61
|
SEGMENTED_INNER_RADIUS,
|
|
59
62
|
"[translate:calc(var(--segment-index)*var(--segment-step))_0] rtl:[translate:calc(var(--segment-index)*var(--segment-step)*-1)_0]",
|
|
60
63
|
activeIndex < 0 && "opacity-0"
|
|
@@ -58,6 +58,12 @@ var themeSwitcherMeta = {
|
|
|
58
58
|
default: '"Theme"',
|
|
59
59
|
description: "Accessible label for the radiogroup."
|
|
60
60
|
},
|
|
61
|
+
{
|
|
62
|
+
name: "motion",
|
|
63
|
+
type: '"full" | "subtle" | "off"',
|
|
64
|
+
default: '"full"',
|
|
65
|
+
description: "How much motion this component uses: `full` (default) inherits the app's policy, `subtle` keeps feedback and drops the glide, `off` stills it. It can only turn motion down \u2014 it never overrides a reader's reduce-motion setting."
|
|
66
|
+
},
|
|
61
67
|
{
|
|
62
68
|
name: "className",
|
|
63
69
|
type: "string",
|
|
@@ -73,6 +79,38 @@ var themeSwitcherMeta = {
|
|
|
73
79
|
"accent-foreground",
|
|
74
80
|
"ring"
|
|
75
81
|
],
|
|
82
|
+
motion: [
|
|
83
|
+
{
|
|
84
|
+
slot: "arrival",
|
|
85
|
+
recipe: "none",
|
|
86
|
+
part: "Root",
|
|
87
|
+
why: "It arrives with the header or settings row it lives in; a theme control that announces itself competes with the page it themes."
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
slot: "response",
|
|
91
|
+
recipe: "motion-interactive",
|
|
92
|
+
part: "Option",
|
|
93
|
+
why: "The icon steps from muted to foreground under the pointer, which is what tells an icon-only segment it is a control."
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
slot: "response",
|
|
97
|
+
recipe: "motion-press",
|
|
98
|
+
part: "Option",
|
|
99
|
+
why: "A settle under the press confirms the tap on a 28px target before the theme change repaints the page around it."
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
slot: "transition",
|
|
103
|
+
recipe: "motion-glide",
|
|
104
|
+
part: "Active pill",
|
|
105
|
+
why: 'One pill slides to the chosen mode, stepping by option + gap on the spacing scale, so light \u2192 dark reads as the same control moving, not three icons trading a fill. Expressive: motion="subtle" lands it in place.'
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
slot: "activity",
|
|
109
|
+
recipe: "none",
|
|
110
|
+
part: "Root",
|
|
111
|
+
why: "Changing the theme is synchronous; the repaint is the feedback."
|
|
112
|
+
}
|
|
113
|
+
],
|
|
76
114
|
a11y: [
|
|
77
115
|
'The container is role="radiogroup" with an aria-label; each option is role="radio" with aria-checked reflecting the selected value.',
|
|
78
116
|
"Each icon button carries an aria-label (Light / Dark / System) plus a title tooltip, since the visual is icon-only.",
|
|
@@ -91,6 +129,10 @@ var themeSwitcherMeta = {
|
|
|
91
129
|
{
|
|
92
130
|
label: "Light selected",
|
|
93
131
|
code: '<ThemeSwitcher value="light" onValueChange={setTheme} />'
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
label: "Subtle motion",
|
|
135
|
+
code: '<ThemeSwitcher value={theme} onValueChange={setTheme} motion="subtle" />'
|
|
94
136
|
}
|
|
95
137
|
]
|
|
96
138
|
};
|
|
@@ -31,11 +31,13 @@ var thinkingOrbMeta = {
|
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
33
|
guidance: "dont",
|
|
34
|
-
description: "Do not put one on every row of a list or in a repeated skeleton. Each orb runs its own animation frame loop painting hundreds of arcs; use Skeleton for content-shaped waits and Spinner inside buttons."
|
|
34
|
+
description: "Do not put one on every row of a list or in a repeated skeleton. Each orb runs its own animation frame loop painting hundreds of arcs; use Skeleton for content-shaped waits and Spinner inside buttons.",
|
|
35
|
+
instead: ["Skeleton", "Spinner"]
|
|
35
36
|
},
|
|
36
37
|
{
|
|
37
38
|
guidance: "dont",
|
|
38
|
-
description: "Do not use it for a determinate wait. It carries no sense of how far along anything is \u2014 reach for Progress when a percentage exists."
|
|
39
|
+
description: "Do not use it for a determinate wait. It carries no sense of how far along anything is \u2014 reach for Progress when a percentage exists.",
|
|
40
|
+
instead: ["Progress"]
|
|
39
41
|
},
|
|
40
42
|
{
|
|
41
43
|
guidance: "dont",
|
|
@@ -33,7 +33,8 @@ var toastMeta = {
|
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
guidance: "dont",
|
|
36
|
-
description: "Use Toast for critical blockers or persistent outages. Use Banner or Dialog when the user must notice and act."
|
|
36
|
+
description: "Use Toast for critical blockers or persistent outages. Use Banner or Dialog when the user must notice and act.",
|
|
37
|
+
instead: ["Banner", "Dialog"]
|
|
37
38
|
},
|
|
38
39
|
{
|
|
39
40
|
guidance: "dont",
|
|
@@ -3,8 +3,10 @@ import { Toggle as BaseToggle } from "@base-ui/react/toggle";
|
|
|
3
3
|
import { ToggleGroup as BaseToggleGroup } from "@base-ui/react/toggle-group";
|
|
4
4
|
import { createContext, useContext } from "react";
|
|
5
5
|
import { cn } from "../../lib/cn.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { HighlightGlide } from "../../lib/highlight-glide.js";
|
|
7
|
+
import { SEGMENTED_INNER_RADIUS, SEGMENTED_ITEM, SEGMENTED_TRACK } from "../../lib/segmented.js";
|
|
8
|
+
import { motionAttribute } from "../../motion/MotionProvider.js";
|
|
9
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
10
|
var ToggleAppearanceContext = createContext("default");
|
|
9
11
|
function Toggle({ className, ...props }) {
|
|
10
12
|
const appearance = useContext(ToggleAppearanceContext);
|
|
@@ -31,17 +33,38 @@ function Toggle({ className, ...props }) {
|
|
|
31
33
|
);
|
|
32
34
|
}
|
|
33
35
|
Toggle.displayName = "Toggle";
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
var SEGMENTED_GLIDE_HANDOFF = "[&[data-glide]>[data-pressed]]:bg-transparent";
|
|
37
|
+
function ToggleGroup({
|
|
38
|
+
className,
|
|
39
|
+
appearance = "default",
|
|
40
|
+
motion,
|
|
41
|
+
children,
|
|
42
|
+
...props
|
|
43
|
+
}) {
|
|
44
|
+
const glide = appearance === "segmented" && !props.multiple;
|
|
45
|
+
return /* @__PURE__ */ jsx(ToggleAppearanceContext.Provider, { value: appearance, children: /* @__PURE__ */ jsxs(
|
|
36
46
|
BaseToggleGroup,
|
|
37
47
|
{
|
|
38
48
|
"data-appearance": appearance,
|
|
49
|
+
"data-motion": motionAttribute(motion),
|
|
39
50
|
className: cn(
|
|
40
51
|
appearance === "segmented" ? SEGMENTED_TRACK : "inline-flex items-center gap-1",
|
|
52
|
+
glide && SEGMENTED_GLIDE_HANDOFF,
|
|
41
53
|
className
|
|
42
54
|
),
|
|
43
55
|
...props,
|
|
44
|
-
"aria-orientation": props.role === "toolbar" ? props.orientation ?? "horizontal" : void 0
|
|
56
|
+
"aria-orientation": props.role === "toolbar" ? props.orientation ?? "horizontal" : void 0,
|
|
57
|
+
children: [
|
|
58
|
+
glide && /* @__PURE__ */ jsx(
|
|
59
|
+
HighlightGlide,
|
|
60
|
+
{
|
|
61
|
+
follow: "data-pressed",
|
|
62
|
+
timing: "selection",
|
|
63
|
+
className: cn(SEGMENTED_INNER_RADIUS, "bg-accent motion-glide")
|
|
64
|
+
}
|
|
65
|
+
),
|
|
66
|
+
children
|
|
67
|
+
]
|
|
45
68
|
}
|
|
46
69
|
) });
|
|
47
70
|
}
|
|
@@ -31,7 +31,8 @@ var toggleMeta = {
|
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
33
|
guidance: "dont",
|
|
34
|
-
description: "Do not use Toggle for persistent application settings; use Switch."
|
|
34
|
+
description: "Do not use Toggle for persistent application settings; use Switch.",
|
|
35
|
+
instead: ["Switch"]
|
|
35
36
|
},
|
|
36
37
|
{
|
|
37
38
|
guidance: "dont",
|
|
@@ -68,7 +69,7 @@ var toggleMeta = {
|
|
|
68
69
|
name: "appearance",
|
|
69
70
|
type: '"default" | "segmented"',
|
|
70
71
|
default: '"default"',
|
|
71
|
-
description: "On ToggleGroup: `default` is a loose row of toggles; `segmented` draws the capsule segmented control (one track, the pressed option filled) shared with ThemeSwitcher. Toggles inside inherit it."
|
|
72
|
+
description: "On ToggleGroup: `default` is a loose row of toggles; `segmented` draws the capsule segmented control (one track, the pressed option filled) shared with ThemeSwitcher. Single-select segmented draws ONE fill that glides to the pressed option and resizes to its label; multi-select keeps a fill per pressed option. Toggles inside inherit it."
|
|
72
73
|
},
|
|
73
74
|
{
|
|
74
75
|
name: "multiple",
|
|
@@ -76,6 +77,12 @@ var toggleMeta = {
|
|
|
76
77
|
default: "false",
|
|
77
78
|
description: "On ToggleGroup: allow several toggles pressed at once. The default is single-select, which behaves like a radio group."
|
|
78
79
|
},
|
|
80
|
+
{
|
|
81
|
+
name: "motion",
|
|
82
|
+
type: '"full" | "subtle" | "off"',
|
|
83
|
+
default: '"full"',
|
|
84
|
+
description: "On ToggleGroup: how much motion this component uses: `full` (default) inherits the app's policy, `subtle` keeps feedback and drops the glide, `off` stills it. It can only turn motion down \u2014 it never overrides a reader's reduce-motion setting."
|
|
85
|
+
},
|
|
79
86
|
{
|
|
80
87
|
name: "Toggle",
|
|
81
88
|
type: "Base UI Toggle props (pressed, defaultPressed, onPressedChange, disabled, value) + className",
|
|
@@ -86,7 +93,7 @@ var toggleMeta = {
|
|
|
86
93
|
subcomponents: [
|
|
87
94
|
{
|
|
88
95
|
name: "ToggleGroup",
|
|
89
|
-
type: 'Base UI ToggleGroup props (value, defaultValue, onValueChange, multiple, disabled, orientation, loopFocus) + appearance?: "default" | "segmented" + className',
|
|
96
|
+
type: 'Base UI ToggleGroup props (value, defaultValue, onValueChange, multiple, disabled, orientation, loopFocus) + appearance?: "default" | "segmented" + motion?: "full" | "subtle" | "off" + className',
|
|
90
97
|
description: "Groups child Toggles and shares pressed state across them \u2014 each Toggle inside carries a value. Defaults to multiple: false (single-select) \u2014 the fit for view-mode switches and segmented controls; pass multiple to allow several pressed at once, e.g. a row of formatting toggles."
|
|
91
98
|
},
|
|
92
99
|
{
|
|
@@ -99,6 +106,38 @@ var toggleMeta = {
|
|
|
99
106
|
appearance: ["default", "segmented"]
|
|
100
107
|
},
|
|
101
108
|
tokensUsed: ["muted-foreground", "foreground", "accent", "accent-foreground", "card", "border", "ring"],
|
|
109
|
+
motion: [
|
|
110
|
+
{
|
|
111
|
+
slot: "arrival",
|
|
112
|
+
recipe: "none",
|
|
113
|
+
part: "Toggle",
|
|
114
|
+
why: "Toggles arrive with the toolbar or header they sit in. Animating each one in would make a row of controls ripple for no reason."
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
slot: "response",
|
|
118
|
+
recipe: "motion-interactive",
|
|
119
|
+
part: "Toggle",
|
|
120
|
+
why: "Hover and the pressed fill ease rather than flip, so pressing reads as the same button changing state, not a different button appearing."
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
slot: "response",
|
|
124
|
+
recipe: "motion-press",
|
|
125
|
+
part: "Toggle",
|
|
126
|
+
why: "The small settle under the finger confirms the press landed before the state change is read \u2014 the difference matters most on touch, where there is no hover."
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
slot: "transition",
|
|
130
|
+
recipe: "motion-glide",
|
|
131
|
+
part: "Segmented fill",
|
|
132
|
+
why: 'In a single-select segmented group one fill travels to the chosen option and stretches to its label, so a view switch reads as one control now pointing elsewhere. Measured, because labels differ in width. Expressive: motion="subtle" lands it in place.'
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
slot: "activity",
|
|
136
|
+
recipe: "none",
|
|
137
|
+
part: "Toggle",
|
|
138
|
+
why: "A toggle commits instantly. Work that follows from the new state shows where its result appears, not on the control."
|
|
139
|
+
}
|
|
140
|
+
],
|
|
102
141
|
a11y: [
|
|
103
142
|
"Base UI sets aria-pressed on each Toggle and manages the pressed/disabled states.",
|
|
104
143
|
"ToggleGroup provides arrow-key roving focus across its items; loopFocus wraps at the ends.",
|
|
@@ -118,6 +157,10 @@ var toggleMeta = {
|
|
|
118
157
|
label: "Segmented view switch",
|
|
119
158
|
code: '<ToggleGroup appearance="segmented" defaultValue={["list"]} aria-label="View"><Toggle value="list" aria-label="List view"><Icon name="list" /></Toggle><Toggle value="grid" aria-label="Grid view"><Icon name="grid" /></Toggle></ToggleGroup>'
|
|
120
159
|
},
|
|
160
|
+
{
|
|
161
|
+
label: "Segmented with labels, subtle motion",
|
|
162
|
+
code: '<ToggleGroup appearance="segmented" motion="subtle" defaultValue={["week"]} aria-label="Range"><Toggle value="day">Day</Toggle><Toggle value="week">Week</Toggle><Toggle value="month">Month</Toggle></ToggleGroup>'
|
|
163
|
+
},
|
|
121
164
|
{
|
|
122
165
|
label: "Multi-select group",
|
|
123
166
|
code: '<ToggleGroup multiple defaultValue={["bold"]}><Toggle value="bold">Bold</Toggle><Toggle value="italic">Italic</Toggle></ToggleGroup>'
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// src/components/tool-call/tool-call.tsx
|
|
2
|
+
import { useRef } from "react";
|
|
3
|
+
import { Icon } from "../../icon/index.js";
|
|
4
|
+
import { cn } from "../../lib/cn.js";
|
|
5
|
+
import { useTypeTier } from "../../lib/density.js";
|
|
6
|
+
import { CodeBlock } from "../code-block/index.js";
|
|
7
|
+
import {
|
|
8
|
+
Collapsible,
|
|
9
|
+
CollapsibleIndicator,
|
|
10
|
+
CollapsiblePanel,
|
|
11
|
+
CollapsibleTrigger
|
|
12
|
+
} from "../collapsible/index.js";
|
|
13
|
+
import { Spinner } from "../spinner/index.js";
|
|
14
|
+
import { toolCallHeaderVariants, toolCallVariants } from "./tool-call.variants.js";
|
|
15
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
16
|
+
var TOOL_CALL_STATUS_LABELS = {
|
|
17
|
+
running: "Running",
|
|
18
|
+
awaiting: "Needs approval",
|
|
19
|
+
complete: "Done",
|
|
20
|
+
error: "Failed",
|
|
21
|
+
denied: "Denied"
|
|
22
|
+
};
|
|
23
|
+
var STATUSES = Object.keys(TOOL_CALL_STATUS_LABELS);
|
|
24
|
+
function formatData(value) {
|
|
25
|
+
if (typeof value === "string") return value;
|
|
26
|
+
try {
|
|
27
|
+
return JSON.stringify(value, null, 2) ?? String(value);
|
|
28
|
+
} catch {
|
|
29
|
+
return String(value);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function ToolCall({
|
|
33
|
+
name,
|
|
34
|
+
title,
|
|
35
|
+
status = "running",
|
|
36
|
+
statusLabel,
|
|
37
|
+
input,
|
|
38
|
+
output,
|
|
39
|
+
error,
|
|
40
|
+
children,
|
|
41
|
+
defaultOpen = false,
|
|
42
|
+
open,
|
|
43
|
+
onOpenChange,
|
|
44
|
+
className,
|
|
45
|
+
...props
|
|
46
|
+
}) {
|
|
47
|
+
const tier = useTypeTier();
|
|
48
|
+
const word = statusLabel ?? TOOL_CALL_STATUS_LABELS[status];
|
|
49
|
+
const mountedStatus = useRef(status).current;
|
|
50
|
+
const announcement = status === mountedStatus ? "" : `${typeof title === "string" ? title : name}: ${word}`;
|
|
51
|
+
const hasInput = input !== void 0;
|
|
52
|
+
const hasOutput = children !== void 0 || output !== void 0;
|
|
53
|
+
const hasDetails = hasInput || hasOutput;
|
|
54
|
+
const row = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
55
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "motion-icon-swap size-4 shrink-0", children: STATUSES.map((s) => /* @__PURE__ */ jsx("span", { "data-active": s === status, className: "inline-flex", children: s === "running" ? /* @__PURE__ */ jsx(
|
|
56
|
+
Spinner,
|
|
57
|
+
{
|
|
58
|
+
size: "sm",
|
|
59
|
+
role: "none",
|
|
60
|
+
"aria-label": void 0,
|
|
61
|
+
className: cn("text-muted-foreground", status !== "running" && "[&_svg]:[animation-play-state:paused]")
|
|
62
|
+
}
|
|
63
|
+
) : /* @__PURE__ */ jsx(Icon, { name: GLYPHS[s].name, className: GLYPHS[s].className }) }, s)) }),
|
|
64
|
+
/* @__PURE__ */ jsxs("span", { className: cn("flex min-w-0 flex-1 items-baseline gap-2", tier.body), children: [
|
|
65
|
+
/* @__PURE__ */ jsx("span", { className: cn("truncate text-foreground", !title && "font-mono"), children: title ?? name }),
|
|
66
|
+
title ? /* @__PURE__ */ jsx("span", { className: cn("hidden truncate font-mono text-muted-foreground sm:inline", tier.secondary), children: name }) : null
|
|
67
|
+
] }),
|
|
68
|
+
/* @__PURE__ */ jsx("span", { className: cn("shrink-0 text-muted-foreground", tier.secondary, status === "error" && "text-destructive"), children: word }),
|
|
69
|
+
hasDetails ? /* @__PURE__ */ jsx(CollapsibleIndicator, {}) : null
|
|
70
|
+
] });
|
|
71
|
+
const errorLine = status === "error" && error ? /* @__PURE__ */ jsx("p", { className: cn("px-3 pb-2 text-destructive", tier.supporting), children: error }) : null;
|
|
72
|
+
const live = /* @__PURE__ */ jsx("span", { role: "status", className: "sr-only", children: announcement });
|
|
73
|
+
if (!hasDetails) {
|
|
74
|
+
return /* @__PURE__ */ jsxs("div", { "data-status": status, className: cn(toolCallVariants({ status }), className), ...props, children: [
|
|
75
|
+
/* @__PURE__ */ jsx("div", { className: toolCallHeaderVariants({ interactive: false }), children: row }),
|
|
76
|
+
errorLine,
|
|
77
|
+
live
|
|
78
|
+
] });
|
|
79
|
+
}
|
|
80
|
+
return /* @__PURE__ */ jsxs(
|
|
81
|
+
Collapsible,
|
|
82
|
+
{
|
|
83
|
+
"data-status": status,
|
|
84
|
+
defaultOpen,
|
|
85
|
+
open,
|
|
86
|
+
onOpenChange: onOpenChange ? (next) => onOpenChange(next) : void 0,
|
|
87
|
+
className: cn(toolCallVariants({ status }), className),
|
|
88
|
+
...props,
|
|
89
|
+
children: [
|
|
90
|
+
/* @__PURE__ */ jsx(CollapsibleTrigger, { className: cn("group/collapsible", toolCallHeaderVariants({ interactive: true })), children: row }),
|
|
91
|
+
errorLine,
|
|
92
|
+
/* @__PURE__ */ jsx(CollapsiblePanel, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 border-t border-border p-3", children: [
|
|
93
|
+
hasInput ? /* @__PURE__ */ jsx(Detail, { label: "Input", tier: tier.label, code: formatData(input) }) : null,
|
|
94
|
+
children !== void 0 ? children : output !== void 0 ? /* @__PURE__ */ jsx(Detail, { label: "Output", tier: tier.label, code: formatData(output) }) : null
|
|
95
|
+
] }) }),
|
|
96
|
+
live
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
ToolCall.displayName = "ToolCall";
|
|
102
|
+
var GLYPHS = {
|
|
103
|
+
awaiting: { name: "clock", className: "text-tone-warning" },
|
|
104
|
+
complete: { name: "check", className: "text-muted-foreground" },
|
|
105
|
+
error: { name: "warning", className: "text-destructive" },
|
|
106
|
+
denied: { name: "xmark", className: "text-muted-foreground" }
|
|
107
|
+
};
|
|
108
|
+
function Detail({ label, tier, code }) {
|
|
109
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-1.5", children: [
|
|
110
|
+
/* @__PURE__ */ jsx("span", { className: cn("text-muted-foreground", tier), children: label }),
|
|
111
|
+
/* @__PURE__ */ jsx(CodeBlock, { code })
|
|
112
|
+
] });
|
|
113
|
+
}
|
|
114
|
+
export {
|
|
115
|
+
TOOL_CALL_STATUS_LABELS,
|
|
116
|
+
ToolCall
|
|
117
|
+
};
|