@agent-native/toolkit 0.9.1 → 0.10.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.
Files changed (162) hide show
  1. package/agent-native.eject.json +71 -0
  2. package/dist/chat-history/ChatHistoryRail.d.ts +15 -1
  3. package/dist/chat-history/ChatHistoryRail.d.ts.map +1 -1
  4. package/dist/chat-history/ChatHistoryRail.js +26 -13
  5. package/dist/chat-history/ChatHistoryRail.js.map +1 -1
  6. package/dist/chat-history/ChatHistoryRail.spec.js +77 -1
  7. package/dist/chat-history/ChatHistoryRail.spec.js.map +1 -1
  8. package/dist/chat-history/index.d.ts +2 -1
  9. package/dist/chat-history/index.d.ts.map +1 -1
  10. package/dist/chat-history/index.js +2 -1
  11. package/dist/chat-history/index.js.map +1 -1
  12. package/dist/chat-history/useChatHistoryRailController.d.ts +26 -0
  13. package/dist/chat-history/useChatHistoryRailController.d.ts.map +1 -0
  14. package/dist/chat-history/useChatHistoryRailController.js +36 -0
  15. package/dist/chat-history/useChatHistoryRailController.js.map +1 -0
  16. package/dist/chat-history/useChatHistoryRailController.spec.d.ts +2 -0
  17. package/dist/chat-history/useChatHistoryRailController.spec.d.ts.map +1 -0
  18. package/dist/chat-history/useChatHistoryRailController.spec.js +101 -0
  19. package/dist/chat-history/useChatHistoryRailController.spec.js.map +1 -0
  20. package/dist/chat-history.css +2 -0
  21. package/dist/composer/ComposerPlusMenu.js +1 -1
  22. package/dist/composer/ComposerPlusMenu.js.map +1 -1
  23. package/dist/composer/PromptComposer.d.ts.map +1 -1
  24. package/dist/composer/PromptComposer.js +22 -4
  25. package/dist/composer/PromptComposer.js.map +1 -1
  26. package/dist/composer/useRealtimeVoiceMode.d.ts +7 -0
  27. package/dist/composer/useRealtimeVoiceMode.d.ts.map +1 -1
  28. package/dist/composer/useRealtimeVoiceMode.js +91 -19
  29. package/dist/composer/useRealtimeVoiceMode.js.map +1 -1
  30. package/dist/composer/useRealtimeVoiceMode.spec.js +58 -9
  31. package/dist/composer/useRealtimeVoiceMode.spec.js.map +1 -1
  32. package/dist/conformance/__fixtures__/css-in-js-adapter.d.ts +7 -0
  33. package/dist/conformance/__fixtures__/css-in-js-adapter.d.ts.map +1 -0
  34. package/dist/conformance/__fixtures__/css-in-js-adapter.js +84 -0
  35. package/dist/conformance/__fixtures__/css-in-js-adapter.js.map +1 -0
  36. package/dist/conformance/conformance.spec.d.ts +2 -0
  37. package/dist/conformance/conformance.spec.d.ts.map +1 -0
  38. package/dist/conformance/conformance.spec.js +58 -0
  39. package/dist/conformance/conformance.spec.js.map +1 -0
  40. package/dist/conformance/index.d.ts +4 -0
  41. package/dist/conformance/index.d.ts.map +1 -0
  42. package/dist/conformance/index.js +4 -0
  43. package/dist/conformance/index.js.map +1 -0
  44. package/dist/conformance/runner.d.ts +12 -0
  45. package/dist/conformance/runner.d.ts.map +1 -0
  46. package/dist/conformance/runner.js +441 -0
  47. package/dist/conformance/runner.js.map +1 -0
  48. package/dist/conformance/types.d.ts +26 -0
  49. package/dist/conformance/types.d.ts.map +1 -0
  50. package/dist/conformance/types.js +13 -0
  51. package/dist/conformance/types.js.map +1 -0
  52. package/dist/conformance/version.d.ts +6 -0
  53. package/dist/conformance/version.d.ts.map +1 -0
  54. package/dist/conformance/version.js +14 -0
  55. package/dist/conformance/version.js.map +1 -0
  56. package/dist/design-system/components.d.ts +64 -0
  57. package/dist/design-system/components.d.ts.map +1 -0
  58. package/dist/design-system/components.js +50 -0
  59. package/dist/design-system/components.js.map +1 -0
  60. package/dist/design-system/context.d.ts +11 -0
  61. package/dist/design-system/context.d.ts.map +1 -0
  62. package/dist/design-system/context.js +17 -0
  63. package/dist/design-system/context.js.map +1 -0
  64. package/dist/design-system/default-adapter.d.ts +3 -0
  65. package/dist/design-system/default-adapter.d.ts.map +1 -0
  66. package/dist/design-system/default-adapter.js +166 -0
  67. package/dist/design-system/default-adapter.js.map +1 -0
  68. package/dist/design-system/default-adapter.spec.d.ts +2 -0
  69. package/dist/design-system/default-adapter.spec.d.ts.map +1 -0
  70. package/dist/design-system/default-adapter.spec.js +96 -0
  71. package/dist/design-system/default-adapter.spec.js.map +1 -0
  72. package/dist/design-system/definition.d.ts +9 -0
  73. package/dist/design-system/definition.d.ts.map +1 -0
  74. package/dist/design-system/definition.js +4 -0
  75. package/dist/design-system/definition.js.map +1 -0
  76. package/dist/design-system/design-system.spec.d.ts +2 -0
  77. package/dist/design-system/design-system.spec.d.ts.map +1 -0
  78. package/dist/design-system/design-system.spec.js +121 -0
  79. package/dist/design-system/design-system.spec.js.map +1 -0
  80. package/dist/design-system/error-boundary.d.ts +18 -0
  81. package/dist/design-system/error-boundary.d.ts.map +1 -0
  82. package/dist/design-system/error-boundary.js +16 -0
  83. package/dist/design-system/error-boundary.js.map +1 -0
  84. package/dist/design-system/index.d.ts +8 -0
  85. package/dist/design-system/index.d.ts.map +1 -0
  86. package/dist/design-system/index.js +8 -0
  87. package/dist/design-system/index.js.map +1 -0
  88. package/dist/design-system/theme.d.ts +30 -0
  89. package/dist/design-system/theme.d.ts.map +1 -0
  90. package/dist/design-system/theme.js +95 -0
  91. package/dist/design-system/theme.js.map +1 -0
  92. package/dist/design-system/theme.spec.d.ts +2 -0
  93. package/dist/design-system/theme.spec.d.ts.map +1 -0
  94. package/dist/design-system/theme.spec.js +43 -0
  95. package/dist/design-system/theme.spec.js.map +1 -0
  96. package/dist/design-system/types.d.ts +288 -0
  97. package/dist/design-system/types.d.ts.map +1 -0
  98. package/dist/design-system/types.js +2 -0
  99. package/dist/design-system/types.js.map +1 -0
  100. package/dist/provider.d.ts +4 -3
  101. package/dist/provider.d.ts.map +1 -1
  102. package/dist/provider.js +46 -4
  103. package/dist/provider.js.map +1 -1
  104. package/dist/ui/button.d.ts +5 -0
  105. package/dist/ui/button.d.ts.map +1 -1
  106. package/dist/ui/button.js +25 -4
  107. package/dist/ui/button.js.map +1 -1
  108. package/dist/ui/dialog.d.ts +4 -0
  109. package/dist/ui/dialog.d.ts.map +1 -1
  110. package/dist/ui/dialog.js +2 -2
  111. package/dist/ui/dialog.js.map +1 -1
  112. package/dist/ui/popover.d.ts +1 -0
  113. package/dist/ui/popover.d.ts.map +1 -1
  114. package/dist/ui/popover.js +2 -2
  115. package/dist/ui/popover.js.map +1 -1
  116. package/dist/ui/select.d.ts +3 -1
  117. package/dist/ui/select.d.ts.map +1 -1
  118. package/dist/ui/select.js +1 -1
  119. package/dist/ui/select.js.map +1 -1
  120. package/dist/ui/sidebar.d.ts +6 -2
  121. package/dist/ui/sidebar.d.ts.map +1 -1
  122. package/dist/ui/sidebar.js +12 -15
  123. package/dist/ui/sidebar.js.map +1 -1
  124. package/dist/ui/tooltip.d.ts +3 -1
  125. package/dist/ui/tooltip.d.ts.map +1 -1
  126. package/dist/ui/tooltip.js +2 -2
  127. package/dist/ui/tooltip.js.map +1 -1
  128. package/package.json +31 -1
  129. package/src/chat-history/ChatHistoryRail.spec.tsx +174 -0
  130. package/src/chat-history/ChatHistoryRail.tsx +100 -39
  131. package/src/chat-history/index.ts +9 -0
  132. package/src/chat-history/useChatHistoryRailController.spec.tsx +131 -0
  133. package/src/chat-history/useChatHistoryRailController.ts +76 -0
  134. package/src/chat-history.css +2 -0
  135. package/src/composer/ComposerPlusMenu.tsx +11 -8
  136. package/src/composer/PromptComposer.tsx +27 -6
  137. package/src/composer/useRealtimeVoiceMode.spec.ts +82 -8
  138. package/src/composer/useRealtimeVoiceMode.tsx +107 -22
  139. package/src/conformance/__fixtures__/css-in-js-adapter.tsx +401 -0
  140. package/src/conformance/conformance.spec.tsx +83 -0
  141. package/src/conformance/index.ts +3 -0
  142. package/src/conformance/runner.tsx +710 -0
  143. package/src/conformance/types.ts +45 -0
  144. package/src/conformance/version.ts +22 -0
  145. package/src/design-system/components.tsx +146 -0
  146. package/src/design-system/context.tsx +31 -0
  147. package/src/design-system/default-adapter.spec.tsx +143 -0
  148. package/src/design-system/default-adapter.tsx +915 -0
  149. package/src/design-system/definition.ts +14 -0
  150. package/src/design-system/design-system.spec.tsx +183 -0
  151. package/src/design-system/error-boundary.tsx +37 -0
  152. package/src/design-system/index.ts +7 -0
  153. package/src/design-system/theme.spec.ts +61 -0
  154. package/src/design-system/theme.ts +145 -0
  155. package/src/design-system/types.ts +363 -0
  156. package/src/provider.tsx +98 -5
  157. package/src/ui/button.tsx +95 -6
  158. package/src/ui/dialog.tsx +48 -23
  159. package/src/ui/popover.tsx +16 -2
  160. package/src/ui/select.tsx +7 -3
  161. package/src/ui/sidebar.tsx +47 -48
  162. package/src/ui/tooltip.tsx +7 -3
@@ -0,0 +1,363 @@
1
+ import type {
2
+ ComponentType,
3
+ CSSProperties,
4
+ KeyboardEvent as ReactKeyboardEvent,
5
+ MouseEvent as ReactMouseEvent,
6
+ ReactElement,
7
+ ReactNode,
8
+ Ref,
9
+ RefObject,
10
+ } from "react";
11
+
12
+ export const DESIGN_SYSTEM_CONTRACT_VERSION = 1 as const;
13
+
14
+ export type DesignSystemIntent = "primary" | "neutral" | "danger";
15
+ export type DesignSystemEmphasis = "solid" | "outline" | "ghost";
16
+ export type DesignSystemSize = "compact" | "default" | "large";
17
+ export type DesignSystemTone =
18
+ | "neutral"
19
+ | "info"
20
+ | "success"
21
+ | "warning"
22
+ | "danger";
23
+ export type DesignSystemPlacement = "top" | "right" | "bottom" | "left";
24
+ export type DesignSystemAlign = "start" | "center" | "end";
25
+ export type DesignSystemKey = string | number;
26
+ export type DesignSystemPressEvent =
27
+ | ReactMouseEvent<HTMLElement>
28
+ | ReactKeyboardEvent<HTMLElement>;
29
+
30
+ /**
31
+ * Styling hooks are optional interoperability affordances. Adapters must not
32
+ * rely on either hook being present, and Toolkit views must not pass
33
+ * framework-specific utility classes through this contract.
34
+ */
35
+ export interface DesignSystemStyleProps {
36
+ className?: string;
37
+ style?: CSSProperties;
38
+ }
39
+
40
+ export interface DesignSystemAccessibleProps {
41
+ id?: string;
42
+ title?: string;
43
+ "aria-label"?: string;
44
+ "aria-labelledby"?: string;
45
+ "aria-describedby"?: string;
46
+ "aria-controls"?: string;
47
+ "aria-expanded"?: boolean;
48
+ "aria-haspopup"?: boolean | "dialog" | "grid" | "listbox" | "menu" | "tree";
49
+ "aria-pressed"?: boolean | "mixed";
50
+ }
51
+
52
+ export interface DesignSystemOverlayProps extends DesignSystemStyleProps {
53
+ portalContainer?: Element | null;
54
+ placement?: DesignSystemPlacement;
55
+ align?: DesignSystemAlign;
56
+ collisionPadding?: number;
57
+ }
58
+
59
+ export interface ActionButtonProps
60
+ extends DesignSystemStyleProps, DesignSystemAccessibleProps {
61
+ children?: ReactNode;
62
+ intent?: DesignSystemIntent;
63
+ emphasis?: DesignSystemEmphasis;
64
+ size?: DesignSystemSize;
65
+ pending?: boolean;
66
+ disabled?: boolean;
67
+ type?: "button" | "submit" | "reset";
68
+ leadingIcon?: ReactNode;
69
+ trailingIcon?: ReactNode;
70
+ onPress?: (event?: DesignSystemPressEvent) => void;
71
+ elementRef?: Ref<HTMLButtonElement>;
72
+ }
73
+
74
+ export interface IconButtonProps
75
+ extends DesignSystemStyleProps, DesignSystemAccessibleProps {
76
+ label: string;
77
+ icon: ReactNode;
78
+ intent?: DesignSystemIntent;
79
+ emphasis?: DesignSystemEmphasis;
80
+ size?: DesignSystemSize;
81
+ pending?: boolean;
82
+ disabled?: boolean;
83
+ type?: "button" | "submit" | "reset";
84
+ onPress?: (event?: DesignSystemPressEvent) => void;
85
+ elementRef?: Ref<HTMLButtonElement>;
86
+ }
87
+
88
+ export type DesignSystemTextInputType =
89
+ | "text"
90
+ | "email"
91
+ | "password"
92
+ | "search"
93
+ | "tel"
94
+ | "url";
95
+
96
+ export interface TextFieldProps
97
+ extends DesignSystemStyleProps, DesignSystemAccessibleProps {
98
+ value: string;
99
+ onChange: (value: string) => void;
100
+ label?: ReactNode;
101
+ description?: ReactNode;
102
+ errorMessage?: ReactNode;
103
+ placeholder?: string;
104
+ /** Optional native datalist id for editable fields with suggestions. */
105
+ list?: string;
106
+ name?: string;
107
+ type?: DesignSystemTextInputType;
108
+ inputMode?:
109
+ | "none"
110
+ | "text"
111
+ | "tel"
112
+ | "url"
113
+ | "email"
114
+ | "numeric"
115
+ | "decimal"
116
+ | "search";
117
+ autoComplete?: string;
118
+ autoFocus?: boolean;
119
+ required?: boolean;
120
+ readOnly?: boolean;
121
+ disabled?: boolean;
122
+ invalid?: boolean;
123
+ leadingContent?: ReactNode;
124
+ trailingContent?: ReactNode;
125
+ onBlur?: () => void;
126
+ onFocus?: () => void;
127
+ onKeyDown?: (event: ReactKeyboardEvent<HTMLElement>) => void;
128
+ inputRef?: Ref<HTMLInputElement>;
129
+ }
130
+
131
+ export interface TextAreaProps
132
+ extends DesignSystemStyleProps, DesignSystemAccessibleProps {
133
+ value: string;
134
+ onChange: (value: string) => void;
135
+ label?: ReactNode;
136
+ description?: ReactNode;
137
+ errorMessage?: ReactNode;
138
+ placeholder?: string;
139
+ name?: string;
140
+ rows?: number;
141
+ maxLength?: number;
142
+ autoFocus?: boolean;
143
+ required?: boolean;
144
+ readOnly?: boolean;
145
+ disabled?: boolean;
146
+ invalid?: boolean;
147
+ onBlur?: () => void;
148
+ onFocus?: () => void;
149
+ onKeyDown?: (event: ReactKeyboardEvent<HTMLElement>) => void;
150
+ textAreaRef?: Ref<HTMLTextAreaElement>;
151
+ }
152
+
153
+ export interface SpinnerProps
154
+ extends DesignSystemStyleProps, DesignSystemAccessibleProps {
155
+ label?: string;
156
+ size?: DesignSystemSize;
157
+ }
158
+
159
+ export interface SkeletonProps
160
+ extends DesignSystemStyleProps, DesignSystemAccessibleProps {
161
+ width?: string | number;
162
+ height?: string | number;
163
+ shape?: "line" | "rectangle" | "circle";
164
+ }
165
+
166
+ export interface StatusProps
167
+ extends DesignSystemStyleProps, DesignSystemAccessibleProps {
168
+ children: ReactNode;
169
+ tone?: DesignSystemTone;
170
+ icon?: ReactNode;
171
+ size?: DesignSystemSize;
172
+ }
173
+
174
+ export interface SurfaceProps
175
+ extends DesignSystemStyleProps, DesignSystemAccessibleProps {
176
+ children: ReactNode;
177
+ as?: "div" | "section" | "article" | "aside";
178
+ elevation?: "none" | "low" | "medium";
179
+ padding?: "none" | "compact" | "default" | "spacious";
180
+ interactive?: boolean;
181
+ onPress?: (event?: DesignSystemPressEvent) => void;
182
+ }
183
+
184
+ export interface AvatarProps
185
+ extends DesignSystemStyleProps, DesignSystemAccessibleProps {
186
+ name: string;
187
+ src?: string | null;
188
+ fallback?: ReactNode;
189
+ size?: DesignSystemSize;
190
+ status?: "online" | "offline" | "busy" | "away";
191
+ imageRef?: Ref<HTMLImageElement>;
192
+ }
193
+
194
+ export interface TooltipProps extends DesignSystemOverlayProps {
195
+ trigger: ReactElement;
196
+ content: ReactNode;
197
+ open?: boolean;
198
+ defaultOpen?: boolean;
199
+ onOpenChange?: (open: boolean) => void;
200
+ delayMs?: number;
201
+ disabled?: boolean;
202
+ }
203
+
204
+ export interface MenuItem {
205
+ id: DesignSystemKey;
206
+ label: ReactNode;
207
+ description?: ReactNode;
208
+ icon?: ReactNode;
209
+ shortcut?: ReactNode;
210
+ disabled?: boolean;
211
+ intent?: Extract<DesignSystemIntent, "neutral" | "danger">;
212
+ selected?: boolean;
213
+ children?: readonly MenuItem[];
214
+ }
215
+
216
+ export interface MenuSection {
217
+ id: DesignSystemKey;
218
+ label?: ReactNode;
219
+ items: readonly MenuItem[];
220
+ }
221
+
222
+ export interface MenuProps extends DesignSystemOverlayProps {
223
+ trigger: ReactElement;
224
+ items?: readonly MenuItem[];
225
+ sections?: readonly MenuSection[];
226
+ open?: boolean;
227
+ defaultOpen?: boolean;
228
+ onOpenChange?: (open: boolean) => void;
229
+ onAction: (id: DesignSystemKey) => void;
230
+ closeOnAction?: boolean;
231
+ }
232
+
233
+ export interface PopoverProps extends DesignSystemOverlayProps {
234
+ trigger: ReactElement;
235
+ children: ReactNode;
236
+ open?: boolean;
237
+ defaultOpen?: boolean;
238
+ onOpenChange?: (open: boolean) => void;
239
+ modal?: boolean;
240
+ dismissible?: boolean;
241
+ initialFocusRef?: RefObject<HTMLElement | null>;
242
+ restoreFocusRef?: RefObject<HTMLElement | null>;
243
+ }
244
+
245
+ export interface DialogProps extends DesignSystemOverlayProps {
246
+ open: boolean;
247
+ onOpenChange: (open: boolean) => void;
248
+ title: ReactNode;
249
+ children: ReactNode;
250
+ description?: ReactNode;
251
+ footer?: ReactNode;
252
+ trigger?: ReactElement;
253
+ size?: "small" | "medium" | "large" | "fullscreen";
254
+ dismissible?: boolean;
255
+ closeLabel?: string;
256
+ initialFocusRef?: RefObject<HTMLElement | null>;
257
+ restoreFocusRef?: RefObject<HTMLElement | null>;
258
+ }
259
+
260
+ export interface PickerOption<Value extends DesignSystemKey = string> {
261
+ value: Value;
262
+ label: ReactNode;
263
+ textValue?: string;
264
+ description?: ReactNode;
265
+ icon?: ReactNode;
266
+ disabled?: boolean;
267
+ keywords?: readonly string[];
268
+ }
269
+
270
+ export interface PickerProps<Value extends DesignSystemKey = string>
271
+ extends DesignSystemOverlayProps, DesignSystemAccessibleProps {
272
+ mode: "select" | "combobox";
273
+ options: readonly PickerOption<Value>[];
274
+ value: Value | null;
275
+ onChange: (value: Value | null) => void;
276
+ label?: ReactNode;
277
+ description?: ReactNode;
278
+ errorMessage?: ReactNode;
279
+ placeholder?: string;
280
+ searchValue?: string;
281
+ onSearchChange?: (value: string) => void;
282
+ open?: boolean;
283
+ onOpenChange?: (open: boolean) => void;
284
+ emptyContent?: ReactNode;
285
+ loadingContent?: ReactNode;
286
+ loading?: boolean;
287
+ required?: boolean;
288
+ disabled?: boolean;
289
+ invalid?: boolean;
290
+ pickerRef?: Ref<HTMLElement>;
291
+ }
292
+
293
+ export interface CheckboxProps
294
+ extends DesignSystemStyleProps, DesignSystemAccessibleProps {
295
+ checked: boolean;
296
+ onChange: (checked: boolean) => void;
297
+ label?: ReactNode;
298
+ description?: ReactNode;
299
+ indeterminate?: boolean;
300
+ required?: boolean;
301
+ disabled?: boolean;
302
+ invalid?: boolean;
303
+ inputRef?: Ref<HTMLButtonElement>;
304
+ }
305
+
306
+ export interface SwitchProps
307
+ extends DesignSystemStyleProps, DesignSystemAccessibleProps {
308
+ checked: boolean;
309
+ onChange: (checked: boolean) => void;
310
+ label?: ReactNode;
311
+ description?: ReactNode;
312
+ disabled?: boolean;
313
+ inputRef?: Ref<HTMLButtonElement>;
314
+ }
315
+
316
+ export interface TabItem<Value extends DesignSystemKey = string> {
317
+ value: Value;
318
+ label: ReactNode;
319
+ content: ReactNode;
320
+ disabled?: boolean;
321
+ icon?: ReactNode;
322
+ }
323
+
324
+ export interface TabsProps<Value extends DesignSystemKey = string>
325
+ extends DesignSystemStyleProps, DesignSystemAccessibleProps {
326
+ items: readonly TabItem<Value>[];
327
+ value: Value;
328
+ onChange: (value: Value) => void;
329
+ orientation?: "horizontal" | "vertical";
330
+ activationMode?: "automatic" | "manual";
331
+ }
332
+
333
+ export interface PickerComponent {
334
+ <Value extends DesignSystemKey = string>(
335
+ props: PickerProps<Value>,
336
+ ): ReactNode;
337
+ }
338
+
339
+ export interface TabsComponent {
340
+ <Value extends DesignSystemKey = string>(props: TabsProps<Value>): ReactNode;
341
+ }
342
+
343
+ export interface DesignSystemComponents {
344
+ ActionButton: ComponentType<ActionButtonProps>;
345
+ IconButton: ComponentType<IconButtonProps>;
346
+ TextField: ComponentType<TextFieldProps>;
347
+ TextArea: ComponentType<TextAreaProps>;
348
+ Spinner: ComponentType<SpinnerProps>;
349
+ Skeleton: ComponentType<SkeletonProps>;
350
+ Status: ComponentType<StatusProps>;
351
+ Surface: ComponentType<SurfaceProps>;
352
+ Avatar: ComponentType<AvatarProps>;
353
+ Tooltip: ComponentType<TooltipProps>;
354
+ Menu: ComponentType<MenuProps>;
355
+ Popover: ComponentType<PopoverProps>;
356
+ Dialog: ComponentType<DialogProps>;
357
+ Picker: PickerComponent;
358
+ Checkbox: ComponentType<CheckboxProps>;
359
+ Switch: ComponentType<SwitchProps>;
360
+ Tabs: TabsComponent;
361
+ }
362
+
363
+ export type DesignSystemComponentName = keyof DesignSystemComponents;
package/src/provider.tsx CHANGED
@@ -1,20 +1,33 @@
1
1
  import {
2
2
  createContext,
3
3
  useContext,
4
+ useEffect,
4
5
  useMemo,
6
+ type ElementType,
5
7
  type ReactNode,
6
8
  type ComponentType,
7
9
  } from "react";
8
10
 
11
+ import {
12
+ DesignSystemContext,
13
+ LegacyButtonRenderContext,
14
+ type DesignSystemContextValue,
15
+ } from "./design-system/context.js";
16
+ import type { DesignSystemDefinition } from "./design-system/definition.js";
17
+ import type {
18
+ ActionButtonProps,
19
+ DesignSystemComponents,
20
+ } from "./design-system/types.js";
9
21
  import type { ButtonProps } from "./ui/button.js";
10
22
 
11
23
  export interface ToolkitComponents {
12
24
  Button?: ComponentType<ButtonProps>;
13
25
  }
14
26
 
15
- interface ToolkitProviderProps {
27
+ export interface ToolkitProviderProps {
16
28
  children: ReactNode;
17
29
  components?: ToolkitComponents;
30
+ designSystem?: DesignSystemDefinition;
18
31
  }
19
32
 
20
33
  const ToolkitComponentsContext = createContext<ToolkitComponents>({});
@@ -22,17 +35,97 @@ const ToolkitComponentsContext = createContext<ToolkitComponents>({});
22
35
  export function ToolkitProvider({
23
36
  children,
24
37
  components,
38
+ designSystem,
25
39
  }: ToolkitProviderProps) {
26
40
  const parent = useContext(ToolkitComponentsContext);
27
- const value = useMemo(
41
+ const parentDesignSystem = useContext(DesignSystemContext);
42
+ const value = useMemo<ToolkitComponents>(
28
43
  () => ({ ...parent, ...components }),
29
44
  [parent, components],
30
45
  );
46
+ const mergedDefinition = useMemo<DesignSystemDefinition | undefined>(() => {
47
+ if (!designSystem) return parentDesignSystem.definition;
48
+ return {
49
+ ...parentDesignSystem.definition,
50
+ ...designSystem,
51
+ components: {
52
+ ...parentDesignSystem.definition?.components,
53
+ ...designSystem.components,
54
+ },
55
+ };
56
+ }, [designSystem, parentDesignSystem.definition]);
57
+ const legacyActionButton = useMemo<
58
+ DesignSystemComponents["ActionButton"] | undefined
59
+ >(() => {
60
+ if (!value.Button) return parentDesignSystem.legacyActionButton;
61
+ const LegacyButton = value.Button as ElementType<
62
+ ButtonProps & { ref?: ActionButtonProps["elementRef"] }
63
+ >;
64
+ return function LegacyActionButton({
65
+ children,
66
+ intent,
67
+ emphasis,
68
+ size,
69
+ pending,
70
+ leadingIcon,
71
+ trailingIcon,
72
+ onPress,
73
+ elementRef,
74
+ ...props
75
+ }: ActionButtonProps) {
76
+ const variant =
77
+ emphasis === "ghost"
78
+ ? "ghost"
79
+ : emphasis === "outline"
80
+ ? "outline"
81
+ : intent === "danger"
82
+ ? "destructive"
83
+ : intent === "primary"
84
+ ? "default"
85
+ : "secondary";
86
+ const legacySize =
87
+ size === "compact" ? "sm" : size === "large" ? "lg" : "default";
88
+ return (
89
+ <LegacyButtonRenderContext.Provider value={true}>
90
+ <LegacyButton
91
+ {...props}
92
+ ref={elementRef}
93
+ variant={variant}
94
+ size={legacySize}
95
+ disabled={props.disabled || pending}
96
+ aria-busy={pending || undefined}
97
+ onClick={(event) => onPress?.(event)}
98
+ >
99
+ {leadingIcon}
100
+ {children}
101
+ {trailingIcon}
102
+ </LegacyButton>
103
+ </LegacyButtonRenderContext.Provider>
104
+ );
105
+ };
106
+ }, [parentDesignSystem.legacyActionButton, value.Button]);
107
+ const designSystemValue = useMemo<DesignSystemContextValue>(
108
+ () => ({ definition: mergedDefinition, legacyActionButton }),
109
+ [legacyActionButton, mergedDefinition],
110
+ );
111
+
112
+ useEffect(() => {
113
+ if (!mergedDefinition?.components?.ActionButton || !value.Button) return;
114
+ const isDevelopment =
115
+ (import.meta as ImportMeta & { env?: { DEV?: boolean } }).env?.DEV ??
116
+ true;
117
+ if (!isDevelopment) return;
118
+ console.warn(
119
+ "[agent-native] ToolkitProvider received both designSystem.components.ActionButton and legacy components.Button. ActionButton takes precedence.",
120
+ );
121
+ }, [mergedDefinition?.components?.ActionButton, value.Button]);
31
122
 
32
123
  return (
33
- <ToolkitComponentsContext.Provider value={value}>
34
- {children}
35
- </ToolkitComponentsContext.Provider>
124
+ <DesignSystemContext.Provider value={designSystemValue}>
125
+ <ToolkitComponentsContext.Provider value={value}>
126
+ {children}
127
+ </ToolkitComponentsContext.Provider>
128
+ </DesignSystemContext.Provider>
36
129
  );
37
130
  }
38
131
 
package/src/ui/button.tsx CHANGED
@@ -2,6 +2,15 @@ import { Slot } from "@radix-ui/react-slot";
2
2
  import { cva, type VariantProps } from "class-variance-authority";
3
3
  import * as React from "react";
4
4
 
5
+ import {
6
+ LegacyButtonRenderContext,
7
+ useDesignSystemComponent,
8
+ } from "../design-system/context.js";
9
+ import { DesignSystemErrorBoundary } from "../design-system/error-boundary.js";
10
+ import type {
11
+ DesignSystemEmphasis,
12
+ DesignSystemIntent,
13
+ } from "../design-system/types.js";
5
14
  import { useToolkitComponent } from "../provider.js";
6
15
  import { cn } from "../utils.js";
7
16
 
@@ -39,10 +48,25 @@ export interface ButtonProps
39
48
  React.ButtonHTMLAttributes<HTMLButtonElement>,
40
49
  VariantProps<typeof buttonVariants> {
41
50
  asChild?: boolean;
51
+ /** Semantic meaning forwarded to a registered design-system ActionButton. */
52
+ intent?: DesignSystemIntent;
53
+ /** Semantic prominence forwarded independently of the default visual variant. */
54
+ emphasis?: DesignSystemEmphasis;
42
55
  }
43
56
 
44
57
  const ButtonBase = React.forwardRef<HTMLButtonElement, ButtonProps>(
45
- ({ className, variant, size, asChild = false, ...props }, ref) => {
58
+ (
59
+ {
60
+ className,
61
+ variant,
62
+ size,
63
+ asChild = false,
64
+ intent: _intent,
65
+ emphasis: _emphasis,
66
+ ...props
67
+ },
68
+ ref,
69
+ ) => {
46
70
  const Comp = asChild ? Slot : "button";
47
71
  return (
48
72
  <Comp
@@ -58,19 +82,84 @@ ButtonBase.displayName = "ButtonBase";
58
82
  const ButtonOverrideRenderContext = React.createContext(false);
59
83
 
60
84
  const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
61
- (props, ref) => {
85
+ ({ variant, size, asChild = false, intent, emphasis, ...props }, ref) => {
86
+ const DesignSystemActionButton = useDesignSystemComponent("ActionButton");
62
87
  const Override = useToolkitComponent("Button");
63
88
  const isRenderingOverride = React.useContext(ButtonOverrideRenderContext);
89
+ const isRenderingLegacyButton = React.useContext(LegacyButtonRenderContext);
90
+ const fallback = (
91
+ <ButtonBase
92
+ ref={ref}
93
+ variant={variant}
94
+ size={size}
95
+ asChild={asChild}
96
+ {...props}
97
+ />
98
+ );
99
+ if (DesignSystemActionButton && !asChild && !isRenderingLegacyButton) {
100
+ const semanticIntent =
101
+ intent ??
102
+ (variant === "destructive"
103
+ ? "danger"
104
+ : variant === "default"
105
+ ? "primary"
106
+ : "neutral");
107
+ const semanticEmphasis =
108
+ emphasis ??
109
+ (variant === "outline"
110
+ ? "outline"
111
+ : variant === "ghost" || variant === "link"
112
+ ? "ghost"
113
+ : "solid");
114
+ const semanticSize =
115
+ size === "sm" ? "compact" : size === "lg" ? "large" : "default";
116
+ return (
117
+ <DesignSystemErrorBoundary component="ActionButton" fallback={fallback}>
118
+ <DesignSystemActionButton
119
+ className={props.className}
120
+ style={props.style}
121
+ id={props.id}
122
+ aria-label={props["aria-label"]}
123
+ aria-labelledby={props["aria-labelledby"]}
124
+ aria-describedby={props["aria-describedby"]}
125
+ aria-controls={props["aria-controls"]}
126
+ elementRef={ref}
127
+ type={props.type}
128
+ disabled={props.disabled}
129
+ intent={semanticIntent}
130
+ emphasis={semanticEmphasis}
131
+ size={semanticSize}
132
+ onPress={(event) =>
133
+ props.onClick?.(
134
+ event as React.MouseEvent<HTMLButtonElement, MouseEvent>,
135
+ )
136
+ }
137
+ >
138
+ {props.children}
139
+ </DesignSystemActionButton>
140
+ </DesignSystemErrorBoundary>
141
+ );
142
+ }
64
143
  if (Override && Override !== Button && !isRenderingOverride) {
65
144
  const OverrideButton = Override as React.ElementType<ButtonProps>;
66
145
  return (
67
- <ButtonOverrideRenderContext.Provider value={true}>
68
- <OverrideButton ref={ref} {...props} />
69
- </ButtonOverrideRenderContext.Provider>
146
+ <DesignSystemErrorBoundary component="ActionButton" fallback={fallback}>
147
+ <ButtonOverrideRenderContext.Provider value={true}>
148
+ <OverrideButton
149
+ ref={ref}
150
+ variant={variant}
151
+ size={size}
152
+ asChild={asChild}
153
+ intent={intent}
154
+ emphasis={emphasis}
155
+ {...props}
156
+ />
157
+ </ButtonOverrideRenderContext.Provider>
158
+ </DesignSystemErrorBoundary>
70
159
  );
71
160
  }
72
161
 
73
- return <ButtonBase ref={ref} {...props} />;
162
+ return fallback;
74
163
  },
75
164
  );
76
165
  Button.displayName = "Button";