@agent-native/toolkit 0.9.1 → 0.10.1

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 (194) 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 +1 -0
  24. package/dist/composer/PromptComposer.d.ts.map +1 -1
  25. package/dist/composer/PromptComposer.js +26 -5
  26. package/dist/composer/PromptComposer.js.map +1 -1
  27. package/dist/composer/PromptComposer.spec.js +8 -1
  28. package/dist/composer/PromptComposer.spec.js.map +1 -1
  29. package/dist/composer/RealtimeVoiceMode.d.ts.map +1 -1
  30. package/dist/composer/RealtimeVoiceMode.js +56 -11
  31. package/dist/composer/RealtimeVoiceMode.js.map +1 -1
  32. package/dist/composer/RealtimeVoiceMode.spec.js +59 -7
  33. package/dist/composer/RealtimeVoiceMode.spec.js.map +1 -1
  34. package/dist/composer/useRealtimeVoiceMode.d.ts +7 -0
  35. package/dist/composer/useRealtimeVoiceMode.d.ts.map +1 -1
  36. package/dist/composer/useRealtimeVoiceMode.js +110 -6
  37. package/dist/composer/useRealtimeVoiceMode.js.map +1 -1
  38. package/dist/composer/useRealtimeVoiceMode.spec.js +74 -2
  39. package/dist/composer/useRealtimeVoiceMode.spec.js.map +1 -1
  40. package/dist/conformance/__fixtures__/css-in-js-adapter.d.ts +7 -0
  41. package/dist/conformance/__fixtures__/css-in-js-adapter.d.ts.map +1 -0
  42. package/dist/conformance/__fixtures__/css-in-js-adapter.js +84 -0
  43. package/dist/conformance/__fixtures__/css-in-js-adapter.js.map +1 -0
  44. package/dist/conformance/conformance.spec.d.ts +2 -0
  45. package/dist/conformance/conformance.spec.d.ts.map +1 -0
  46. package/dist/conformance/conformance.spec.js +87 -0
  47. package/dist/conformance/conformance.spec.js.map +1 -0
  48. package/dist/conformance/index.d.ts +4 -0
  49. package/dist/conformance/index.d.ts.map +1 -0
  50. package/dist/conformance/index.js +4 -0
  51. package/dist/conformance/index.js.map +1 -0
  52. package/dist/conformance/runner.d.ts +12 -0
  53. package/dist/conformance/runner.d.ts.map +1 -0
  54. package/dist/conformance/runner.js +515 -0
  55. package/dist/conformance/runner.js.map +1 -0
  56. package/dist/conformance/types.d.ts +26 -0
  57. package/dist/conformance/types.d.ts.map +1 -0
  58. package/dist/conformance/types.js +13 -0
  59. package/dist/conformance/types.js.map +1 -0
  60. package/dist/conformance/version.d.ts +6 -0
  61. package/dist/conformance/version.d.ts.map +1 -0
  62. package/dist/conformance/version.js +14 -0
  63. package/dist/conformance/version.js.map +1 -0
  64. package/dist/design-system/components.d.ts +64 -0
  65. package/dist/design-system/components.d.ts.map +1 -0
  66. package/dist/design-system/components.js +50 -0
  67. package/dist/design-system/components.js.map +1 -0
  68. package/dist/design-system/context.d.ts +11 -0
  69. package/dist/design-system/context.d.ts.map +1 -0
  70. package/dist/design-system/context.js +17 -0
  71. package/dist/design-system/context.js.map +1 -0
  72. package/dist/design-system/default-adapter.d.ts +3 -0
  73. package/dist/design-system/default-adapter.d.ts.map +1 -0
  74. package/dist/design-system/default-adapter.js +166 -0
  75. package/dist/design-system/default-adapter.js.map +1 -0
  76. package/dist/design-system/default-adapter.spec.d.ts +2 -0
  77. package/dist/design-system/default-adapter.spec.d.ts.map +1 -0
  78. package/dist/design-system/default-adapter.spec.js +96 -0
  79. package/dist/design-system/default-adapter.spec.js.map +1 -0
  80. package/dist/design-system/definition.d.ts +9 -0
  81. package/dist/design-system/definition.d.ts.map +1 -0
  82. package/dist/design-system/definition.js +4 -0
  83. package/dist/design-system/definition.js.map +1 -0
  84. package/dist/design-system/design-system.spec.d.ts +2 -0
  85. package/dist/design-system/design-system.spec.d.ts.map +1 -0
  86. package/dist/design-system/design-system.spec.js +121 -0
  87. package/dist/design-system/design-system.spec.js.map +1 -0
  88. package/dist/design-system/error-boundary.d.ts +18 -0
  89. package/dist/design-system/error-boundary.d.ts.map +1 -0
  90. package/dist/design-system/error-boundary.js +16 -0
  91. package/dist/design-system/error-boundary.js.map +1 -0
  92. package/dist/design-system/index.d.ts +8 -0
  93. package/dist/design-system/index.d.ts.map +1 -0
  94. package/dist/design-system/index.js +8 -0
  95. package/dist/design-system/index.js.map +1 -0
  96. package/dist/design-system/theme.d.ts +30 -0
  97. package/dist/design-system/theme.d.ts.map +1 -0
  98. package/dist/design-system/theme.js +95 -0
  99. package/dist/design-system/theme.js.map +1 -0
  100. package/dist/design-system/theme.spec.d.ts +2 -0
  101. package/dist/design-system/theme.spec.d.ts.map +1 -0
  102. package/dist/design-system/theme.spec.js +43 -0
  103. package/dist/design-system/theme.spec.js.map +1 -0
  104. package/dist/design-system/types.d.ts +288 -0
  105. package/dist/design-system/types.d.ts.map +1 -0
  106. package/dist/design-system/types.js +2 -0
  107. package/dist/design-system/types.js.map +1 -0
  108. package/dist/editor/SharedRichEditor.d.ts.map +1 -1
  109. package/dist/editor/SharedRichEditor.js +3 -2
  110. package/dist/editor/SharedRichEditor.js.map +1 -1
  111. package/dist/editor/SlashCommandMenu.d.ts +8 -0
  112. package/dist/editor/SlashCommandMenu.d.ts.map +1 -1
  113. package/dist/editor/SlashCommandMenu.js +6 -0
  114. package/dist/editor/SlashCommandMenu.js.map +1 -1
  115. package/dist/editor/SlashCommandMenu.spec.d.ts +2 -0
  116. package/dist/editor/SlashCommandMenu.spec.d.ts.map +1 -0
  117. package/dist/editor/SlashCommandMenu.spec.js +20 -0
  118. package/dist/editor/SlashCommandMenu.spec.js.map +1 -0
  119. package/dist/editor/index.d.ts +1 -1
  120. package/dist/editor/index.d.ts.map +1 -1
  121. package/dist/editor/index.js +1 -1
  122. package/dist/editor/index.js.map +1 -1
  123. package/dist/provider.d.ts +4 -3
  124. package/dist/provider.d.ts.map +1 -1
  125. package/dist/provider.js +46 -4
  126. package/dist/provider.js.map +1 -1
  127. package/dist/styles.css +52 -23
  128. package/dist/ui/button.d.ts +5 -0
  129. package/dist/ui/button.d.ts.map +1 -1
  130. package/dist/ui/button.js +25 -4
  131. package/dist/ui/button.js.map +1 -1
  132. package/dist/ui/dialog.d.ts +4 -0
  133. package/dist/ui/dialog.d.ts.map +1 -1
  134. package/dist/ui/dialog.js +2 -2
  135. package/dist/ui/dialog.js.map +1 -1
  136. package/dist/ui/popover.d.ts +1 -0
  137. package/dist/ui/popover.d.ts.map +1 -1
  138. package/dist/ui/popover.js +2 -2
  139. package/dist/ui/popover.js.map +1 -1
  140. package/dist/ui/select.d.ts +3 -1
  141. package/dist/ui/select.d.ts.map +1 -1
  142. package/dist/ui/select.js +1 -1
  143. package/dist/ui/select.js.map +1 -1
  144. package/dist/ui/sidebar.d.ts +6 -2
  145. package/dist/ui/sidebar.d.ts.map +1 -1
  146. package/dist/ui/sidebar.js +12 -15
  147. package/dist/ui/sidebar.js.map +1 -1
  148. package/dist/ui/tooltip.d.ts +3 -1
  149. package/dist/ui/tooltip.d.ts.map +1 -1
  150. package/dist/ui/tooltip.js +2 -2
  151. package/dist/ui/tooltip.js.map +1 -1
  152. package/package.json +31 -1
  153. package/src/chat-history/ChatHistoryRail.spec.tsx +174 -0
  154. package/src/chat-history/ChatHistoryRail.tsx +100 -39
  155. package/src/chat-history/index.ts +9 -0
  156. package/src/chat-history/useChatHistoryRailController.spec.tsx +131 -0
  157. package/src/chat-history/useChatHistoryRailController.ts +76 -0
  158. package/src/chat-history.css +2 -0
  159. package/src/composer/ComposerPlusMenu.tsx +11 -8
  160. package/src/composer/PromptComposer.spec.ts +12 -1
  161. package/src/composer/PromptComposer.tsx +32 -7
  162. package/src/composer/RealtimeVoiceMode.spec.tsx +117 -9
  163. package/src/composer/RealtimeVoiceMode.tsx +119 -31
  164. package/src/composer/useRealtimeVoiceMode.spec.ts +101 -1
  165. package/src/composer/useRealtimeVoiceMode.tsx +124 -9
  166. package/src/conformance/__fixtures__/css-in-js-adapter.tsx +401 -0
  167. package/src/conformance/conformance.spec.tsx +143 -0
  168. package/src/conformance/index.ts +3 -0
  169. package/src/conformance/runner.tsx +862 -0
  170. package/src/conformance/types.ts +45 -0
  171. package/src/conformance/version.ts +22 -0
  172. package/src/design-system/components.tsx +146 -0
  173. package/src/design-system/context.tsx +31 -0
  174. package/src/design-system/default-adapter.spec.tsx +143 -0
  175. package/src/design-system/default-adapter.tsx +915 -0
  176. package/src/design-system/definition.ts +14 -0
  177. package/src/design-system/design-system.spec.tsx +183 -0
  178. package/src/design-system/error-boundary.tsx +37 -0
  179. package/src/design-system/index.ts +7 -0
  180. package/src/design-system/theme.spec.ts +61 -0
  181. package/src/design-system/theme.ts +145 -0
  182. package/src/design-system/types.ts +363 -0
  183. package/src/editor/SharedRichEditor.tsx +13 -2
  184. package/src/editor/SlashCommandMenu.spec.ts +33 -0
  185. package/src/editor/SlashCommandMenu.tsx +20 -0
  186. package/src/editor/index.ts +2 -0
  187. package/src/provider.tsx +98 -5
  188. package/src/styles.css +52 -23
  189. package/src/ui/button.tsx +95 -6
  190. package/src/ui/dialog.tsx +48 -23
  191. package/src/ui/popover.tsx +16 -2
  192. package/src/ui/select.tsx +7 -3
  193. package/src/ui/sidebar.tsx +47 -48
  194. 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;
@@ -14,7 +14,12 @@ import {
14
14
  type SharedEditorFeatures,
15
15
  } from "./extensions.js";
16
16
  import type { ImageUploadFn } from "./ImageExtension.js";
17
- import { SlashCommandMenu, type SlashCommandItem } from "./SlashCommandMenu.js";
17
+ import {
18
+ DEFAULT_SLASH_COMMANDS,
19
+ filterSlashCommandItems,
20
+ SlashCommandMenu,
21
+ type SlashCommandItem,
22
+ } from "./SlashCommandMenu.js";
18
23
  import {
19
24
  useCollabReconcile,
20
25
  getEditorMarkdown,
@@ -200,6 +205,12 @@ export function SharedRichEditor({
200
205
 
201
206
  const collab = !!ydoc;
202
207
 
208
+ const effectiveSlashItems = useMemo(
209
+ () =>
210
+ filterSlashCommandItems(slashItems ?? DEFAULT_SLASH_COMMANDS, features),
211
+ [features, slashItems],
212
+ );
213
+
203
214
  // The collab hook needs the editor, but useEditor's `onUpdate` needs the
204
215
  // hook's guards. Break the cycle with a ref: `onUpdate` reads the guards
205
216
  // through `guardsRef`, which is populated right after the hook runs below.
@@ -313,7 +324,7 @@ export function SharedRichEditor({
313
324
  <BubbleToolbar editor={editor} buildItems={buildBubbleItems} />
314
325
  ) : null}
315
326
  {editable ? (
316
- <SlashCommandMenu editor={editor} items={slashItems} />
327
+ <SlashCommandMenu editor={editor} items={effectiveSlashItems} />
317
328
  ) : null}
318
329
  <EditorContent editor={editor} />
319
330
  </div>
@@ -0,0 +1,33 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import {
4
+ DEFAULT_SLASH_COMMANDS,
5
+ filterSlashCommandItems,
6
+ } from "./SlashCommandMenu.js";
7
+
8
+ describe("filterSlashCommandItems", () => {
9
+ it("hides commands whose editor extensions are disabled", () => {
10
+ const commands = filterSlashCommandItems(DEFAULT_SLASH_COMMANDS, {
11
+ codeBlock: false,
12
+ tables: false,
13
+ tasks: false,
14
+ });
15
+
16
+ expect(commands.map((command) => command.title)).not.toEqual(
17
+ expect.arrayContaining(["Code block", "Table", "To-do list"]),
18
+ );
19
+ });
20
+
21
+ it("keeps commands enabled when their feature is unspecified", () => {
22
+ const commands = filterSlashCommandItems(DEFAULT_SLASH_COMMANDS, {
23
+ tasks: false,
24
+ });
25
+
26
+ expect(commands.map((command) => command.title)).toEqual(
27
+ expect.arrayContaining(["Code block", "Table"]),
28
+ );
29
+ expect(commands.map((command) => command.title)).not.toContain(
30
+ "To-do list",
31
+ );
32
+ });
33
+ });
@@ -22,9 +22,26 @@ export interface SlashCommandItem {
22
22
  searchText?: string;
23
23
  /** Short text glyph shown in the menu (T, H1, tbl, …). */
24
24
  icon: string;
25
+ /** Hide this command when the shared editor feature is disabled. */
26
+ requires?: "tables" | "tasks" | "codeBlock";
25
27
  action: (editor: Editor) => void;
26
28
  }
27
29
 
30
+ export interface SlashCommandFeatureFlags {
31
+ tables?: boolean;
32
+ tasks?: boolean;
33
+ codeBlock?: boolean;
34
+ }
35
+
36
+ export function filterSlashCommandItems(
37
+ items: readonly SlashCommandItem[],
38
+ features?: SlashCommandFeatureFlags,
39
+ ): SlashCommandItem[] {
40
+ return items.filter(
41
+ (item) => !item.requires || features?.[item.requires] !== false,
42
+ );
43
+ }
44
+
28
45
  /**
29
46
  * The default block commands — Plan's current set. Apps pass their own `items`
30
47
  * (typically `[...DEFAULT_SLASH_COMMANDS, ...extra]`) to extend it.
@@ -73,6 +90,7 @@ export const DEFAULT_SLASH_COMMANDS: SlashCommandItem[] = [
73
90
  title: "To-do list",
74
91
  description: "Checklist items",
75
92
  icon: "[]",
93
+ requires: "tasks",
76
94
  action: (editor) => editor.chain().focus().toggleTaskList().run(),
77
95
  },
78
96
  {
@@ -85,6 +103,7 @@ export const DEFAULT_SLASH_COMMANDS: SlashCommandItem[] = [
85
103
  title: "Code block",
86
104
  description: "Code snippet",
87
105
  icon: "<>",
106
+ requires: "codeBlock",
88
107
  action: (editor) => editor.chain().focus().toggleCodeBlock().run(),
89
108
  },
90
109
  {
@@ -97,6 +116,7 @@ export const DEFAULT_SLASH_COMMANDS: SlashCommandItem[] = [
97
116
  title: "Table",
98
117
  description: "Three by three table",
99
118
  icon: "tbl",
119
+ requires: "tables",
100
120
  action: (editor) =>
101
121
  editor
102
122
  .chain()
@@ -25,8 +25,10 @@ export {
25
25
  SlashCommandMenu,
26
26
  DEFAULT_SLASH_COMMANDS,
27
27
  createImageSlashCommand,
28
+ filterSlashCommandItems,
28
29
  type SlashCommandItem,
29
30
  type SlashCommandMenuProps,
31
+ type SlashCommandFeatureFlags,
30
32
  } from "./SlashCommandMenu.js";
31
33
  export {
32
34
  SharedImage,
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