@superxd-studio/adptr 0.5.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 (221) hide show
  1. package/GENERATIVE.md +101 -0
  2. package/INTERACTIONS.md +51 -0
  3. package/LICENSE +21 -0
  4. package/README.md +57 -0
  5. package/REUSE.md +170 -0
  6. package/REVIEW.md +68 -0
  7. package/THIRD-PARTY-NOTICES.md +28 -0
  8. package/ai-sdk/index.js +96 -0
  9. package/assets/ibm-plex-mono-cyrillic-400-normal.woff2 +0 -0
  10. package/assets/ibm-plex-mono-cyrillic-500-normal.woff2 +0 -0
  11. package/assets/ibm-plex-mono-cyrillic-ext-400-normal.woff2 +0 -0
  12. package/assets/ibm-plex-mono-cyrillic-ext-500-normal.woff2 +0 -0
  13. package/assets/ibm-plex-mono-latin-400-normal.woff2 +0 -0
  14. package/assets/ibm-plex-mono-latin-500-normal.woff2 +0 -0
  15. package/assets/ibm-plex-mono-latin-ext-400-normal.woff2 +0 -0
  16. package/assets/ibm-plex-mono-latin-ext-500-normal.woff2 +0 -0
  17. package/assets/ibm-plex-mono-vietnamese-400-normal.woff2 +0 -0
  18. package/assets/ibm-plex-mono-vietnamese-500-normal.woff2 +0 -0
  19. package/assets/instrument-sans-latin-ext-wght-normal.woff2 +0 -0
  20. package/assets/instrument-sans-latin-wght-normal.woff2 +0 -0
  21. package/assistant-ui/index.js +139 -0
  22. package/chunks/callout-DKMHSnjc.js +342 -0
  23. package/chunks/canvas-BJ15-XxE.js +598 -0
  24. package/chunks/controller-Cfpg0C7v.js +180 -0
  25. package/chunks/core-BCqVRLUy.js +312 -0
  26. package/chunks/text-field-BLtoaZFn.js +41 -0
  27. package/chunks/types-BGF7r1my.js +115 -0
  28. package/chunks/ui-9DyNsYXY.js +967 -0
  29. package/core/index.js +4 -0
  30. package/custom/index.js +64 -0
  31. package/examples/ai-sdk-backend/package.json +11 -0
  32. package/examples/ai-sdk-backend/server.mjs +316 -0
  33. package/examples/custom-backend/copy.mjs +7 -0
  34. package/examples/custom-backend/server.mjs +118 -0
  35. package/examples/generative/specs.json +2094 -0
  36. package/examples/review-backend/copy.mjs +27 -0
  37. package/examples/review-backend/package.json +11 -0
  38. package/examples/review-backend/server.mjs +276 -0
  39. package/examples/web-ai-sdk/index.html +12 -0
  40. package/examples/web-ai-sdk/package.json +21 -0
  41. package/examples/web-ai-sdk/src/copy.ts +1 -0
  42. package/examples/web-ai-sdk/src/main.tsx +64 -0
  43. package/examples/web-ai-sdk/tsconfig.json +13 -0
  44. package/examples/web-generative/index.html +12 -0
  45. package/examples/web-generative/package.json +23 -0
  46. package/examples/web-generative/src/copy.ts +42 -0
  47. package/examples/web-generative/src/main.tsx +74 -0
  48. package/examples/web-generative/tsconfig.json +14 -0
  49. package/examples/web-independent/index.html +12 -0
  50. package/examples/web-independent/package.json +20 -0
  51. package/examples/web-independent/src/main.tsx +32 -0
  52. package/examples/web-independent/tsconfig.json +14 -0
  53. package/examples/web-interactions/index.html +12 -0
  54. package/examples/web-interactions/package.json +21 -0
  55. package/examples/web-interactions/src/copy.ts +215 -0
  56. package/examples/web-interactions/src/examples.tsx +628 -0
  57. package/examples/web-interactions/src/main.tsx +116 -0
  58. package/examples/web-interactions/tsconfig.json +14 -0
  59. package/examples/web-review/index.html +12 -0
  60. package/examples/web-review/package.json +20 -0
  61. package/examples/web-review/src/main.tsx +59 -0
  62. package/examples/web-review/tsconfig.json +14 -0
  63. package/fonts.css +117 -0
  64. package/generative/index.js +1560 -0
  65. package/interactions/index.js +1591 -0
  66. package/langgraph-review/index.js +36 -0
  67. package/licenses/ai-sdk.txt +13 -0
  68. package/licenses/assistant-ui-generative.txt +21 -0
  69. package/licenses/assistant-ui.txt +23 -0
  70. package/licenses/ibm-plex-mono.txt +93 -0
  71. package/licenses/instrument-sans.txt +93 -0
  72. package/licenses/internationalized-date.txt +201 -0
  73. package/licenses/langgraph.txt +21 -0
  74. package/licenses/lucide.txt +43 -0
  75. package/licenses/react-markdown.txt +21 -0
  76. package/licenses/remark-gfm.txt +22 -0
  77. package/licenses/zod.txt +21 -0
  78. package/package.json +138 -0
  79. package/review/index.js +31 -0
  80. package/source/components/avatar.tsx +50 -0
  81. package/source/components/button.tsx +155 -0
  82. package/source/components/callout.tsx +65 -0
  83. package/source/components/canvas/connector.tsx +14 -0
  84. package/source/components/canvas/copy.ts +34 -0
  85. package/source/components/canvas/index.tsx +257 -0
  86. package/source/components/canvas/recorder.ts +36 -0
  87. package/source/components/date-time-field.tsx +77 -0
  88. package/source/components/flow-motion.tsx +109 -0
  89. package/source/components/icons.tsx +275 -0
  90. package/source/components/responsive-overlay.tsx +91 -0
  91. package/source/components/scenario-picker/copy.ts +5 -0
  92. package/source/components/scenario-picker/index.tsx +142 -0
  93. package/source/components/scroll-area.tsx +59 -0
  94. package/source/components/search-picker.tsx +127 -0
  95. package/source/components/segmented-control.tsx +123 -0
  96. package/source/components/skeleton.tsx +11 -0
  97. package/source/components/spinner.tsx +19 -0
  98. package/source/components/superxd-arrow.tsx +22 -0
  99. package/source/components/switch.tsx +45 -0
  100. package/source/components/text-field.tsx +81 -0
  101. package/source/components/thinking.tsx +16 -0
  102. package/source/components/ui/copy.ts +12 -0
  103. package/source/components/ui/display.tsx +276 -0
  104. package/source/components/ui/navigation.tsx +346 -0
  105. package/source/components/ui/portal-theme.tsx +28 -0
  106. package/source/components/ui/selection.tsx +336 -0
  107. package/source/lib/cn.ts +23 -0
  108. package/source/reusable/ai-sdk/index.ts +114 -0
  109. package/source/reusable/assistant-ui/index.tsx +266 -0
  110. package/source/reusable/core/attachments.ts +163 -0
  111. package/source/reusable/core/index.ts +417 -0
  112. package/source/reusable/custom/index.ts +90 -0
  113. package/source/reusable/generative/actions.ts +188 -0
  114. package/source/reusable/generative/copy.ts +47 -0
  115. package/source/reusable/generative/data.tsx +126 -0
  116. package/source/reusable/generative/index.tsx +517 -0
  117. package/source/reusable/generative/schema.ts +327 -0
  118. package/source/reusable/generative/timeline.tsx +214 -0
  119. package/source/reusable/generative/vocabulary.tsx +847 -0
  120. package/source/reusable/interactions/agent.ts +259 -0
  121. package/source/reusable/interactions/artifact.ts +236 -0
  122. package/source/reusable/interactions/context.tsx +38 -0
  123. package/source/reusable/interactions/copy.ts +117 -0
  124. package/source/reusable/interactions/feedback.ts +73 -0
  125. package/source/reusable/interactions/index.ts +11 -0
  126. package/source/reusable/interactions/presentation.tsx +483 -0
  127. package/source/reusable/interactions/store.ts +74 -0
  128. package/source/reusable/interactions/threads.ts +126 -0
  129. package/source/reusable/interactions/web.tsx +574 -0
  130. package/source/reusable/langgraph-review/index.ts +73 -0
  131. package/source/reusable/review/controller.ts +263 -0
  132. package/source/reusable/review/index.ts +47 -0
  133. package/source/reusable/review/types.ts +189 -0
  134. package/source/reusable/ui/index.ts +25 -0
  135. package/source/reusable/uploads/index.ts +48 -0
  136. package/source/reusable/web/attachments/copy.ts +23 -0
  137. package/source/reusable/web/attachments/index.tsx +233 -0
  138. package/source/reusable/web/copy.ts +22 -0
  139. package/source/reusable/web/index.tsx +470 -0
  140. package/source/reusable/web/message/copy.ts +26 -0
  141. package/source/reusable/web/message/index.tsx +388 -0
  142. package/source/reusable/web/message/streaming.ts +64 -0
  143. package/source/reusable/web/message/styles.css +21 -0
  144. package/source/reusable/web/review/copy.ts +78 -0
  145. package/source/reusable/web/review/flow.ts +24 -0
  146. package/source/reusable/web/review/index.tsx +506 -0
  147. package/source/reusable/web/styles.ts +2 -0
  148. package/source/styles/library.css +612 -0
  149. package/source/styles/theme.css +228 -0
  150. package/styles.css +3 -0
  151. package/styles.d.ts +1 -0
  152. package/types/components/avatar.d.ts +14 -0
  153. package/types/components/button.d.ts +36 -0
  154. package/types/components/callout.d.ts +19 -0
  155. package/types/components/canvas/connector.d.ts +2 -0
  156. package/types/components/canvas/copy.d.ts +34 -0
  157. package/types/components/canvas/index.d.ts +33 -0
  158. package/types/components/canvas/recorder.d.ts +10 -0
  159. package/types/components/date-time-field.d.ts +11 -0
  160. package/types/components/flow-motion.d.ts +38 -0
  161. package/types/components/icons.d.ts +138 -0
  162. package/types/components/responsive-overlay.d.ts +15 -0
  163. package/types/components/scenario-picker/copy.d.ts +5 -0
  164. package/types/components/scenario-picker/index.d.ts +14 -0
  165. package/types/components/scroll-area.d.ts +14 -0
  166. package/types/components/search-picker.d.ts +27 -0
  167. package/types/components/segmented-control.d.ts +23 -0
  168. package/types/components/skeleton.d.ts +4 -0
  169. package/types/components/spinner.d.ts +4 -0
  170. package/types/components/superxd-arrow.d.ts +5 -0
  171. package/types/components/switch.d.ts +8 -0
  172. package/types/components/text-field.d.ts +19 -0
  173. package/types/components/thinking.d.ts +8 -0
  174. package/types/components/ui/copy.d.ts +12 -0
  175. package/types/components/ui/display.d.ts +50 -0
  176. package/types/components/ui/navigation.d.ts +63 -0
  177. package/types/components/ui/portal-theme.d.ts +8 -0
  178. package/types/components/ui/selection.d.ts +37 -0
  179. package/types/lib/cn.d.ts +3 -0
  180. package/types/reusable/ai-sdk/index.d.ts +15 -0
  181. package/types/reusable/assistant-ui/index.d.ts +12 -0
  182. package/types/reusable/core/attachments.d.ts +54 -0
  183. package/types/reusable/core/index.d.ts +107 -0
  184. package/types/reusable/custom/index.d.ts +14 -0
  185. package/types/reusable/generative/actions.d.ts +45 -0
  186. package/types/reusable/generative/copy.d.ts +48 -0
  187. package/types/reusable/generative/data.d.ts +4 -0
  188. package/types/reusable/generative/index.d.ts +30 -0
  189. package/types/reusable/generative/schema.d.ts +303 -0
  190. package/types/reusable/generative/timeline.d.ts +21 -0
  191. package/types/reusable/generative/vocabulary.d.ts +34 -0
  192. package/types/reusable/interactions/agent.d.ts +98 -0
  193. package/types/reusable/interactions/artifact.d.ts +118 -0
  194. package/types/reusable/interactions/context.d.ts +130 -0
  195. package/types/reusable/interactions/copy.d.ts +115 -0
  196. package/types/reusable/interactions/feedback.d.ts +34 -0
  197. package/types/reusable/interactions/index.d.ts +10 -0
  198. package/types/reusable/interactions/presentation.d.ts +97 -0
  199. package/types/reusable/interactions/store.d.ts +29 -0
  200. package/types/reusable/interactions/threads.d.ts +38 -0
  201. package/types/reusable/interactions/web.d.ts +22 -0
  202. package/types/reusable/langgraph-review/index.d.ts +5 -0
  203. package/types/reusable/review/controller.d.ts +52 -0
  204. package/types/reusable/review/index.d.ts +12 -0
  205. package/types/reusable/review/types.d.ts +61 -0
  206. package/types/reusable/ui/index.d.ts +19 -0
  207. package/types/reusable/uploads/index.d.ts +11 -0
  208. package/types/reusable/web/attachments/copy.d.ts +25 -0
  209. package/types/reusable/web/attachments/index.d.ts +24 -0
  210. package/types/reusable/web/copy.d.ts +24 -0
  211. package/types/reusable/web/index.d.ts +41 -0
  212. package/types/reusable/web/message/copy.d.ts +27 -0
  213. package/types/reusable/web/message/index.d.ts +56 -0
  214. package/types/reusable/web/message/streaming.d.ts +27 -0
  215. package/types/reusable/web/review/copy.d.ts +72 -0
  216. package/types/reusable/web/review/flow.d.ts +3 -0
  217. package/types/reusable/web/review/index.d.ts +38 -0
  218. package/types/reusable/web/styles.d.ts +2 -0
  219. package/ui/index.js +4 -0
  220. package/uploads/index.js +34 -0
  221. package/web/index.js +931 -0
@@ -0,0 +1,336 @@
1
+ import { PortalTheme } from "./portal-theme";
2
+ import { createContext, useContext, useId, type ReactNode } from "react";
3
+ import {
4
+ Checkbox as AriaCheckbox,
5
+ CheckboxGroup as AriaCheckboxGroup,
6
+ RadioGroup as AriaRadioGroup,
7
+ Radio,
8
+ Label,
9
+ Text,
10
+ FieldError,
11
+ Select as AriaSelect,
12
+ SelectValue,
13
+ ListBox,
14
+ ListBoxItem,
15
+ Popover,
16
+ Slider as AriaSlider,
17
+ SliderTrack,
18
+ SliderThumb,
19
+ SliderOutput,
20
+ ToggleButton as AriaToggleButton,
21
+ Form as AriaForm,
22
+ type CheckboxProps,
23
+ type CheckboxGroupProps,
24
+ type RadioGroupProps,
25
+ type SelectProps,
26
+ type SliderProps,
27
+ type ToggleButtonProps,
28
+ type FormProps,
29
+ } from "react-aria-components";
30
+ import { TextField as ExistingTextField, type TextFieldProps } from "../text-field";
31
+ import { Button, buttonStyles } from "../button";
32
+ import { IconCheck, IconChevronDown } from "../icons";
33
+ import { cn } from "../../lib/cn";
34
+ import { uiCopy } from "./copy";
35
+
36
+ const field = "flex min-w-0 flex-col gap-2";
37
+ const hint = "type-caption font-normal text-fg-subtle";
38
+ export interface Choice {
39
+ id: string;
40
+ label: string;
41
+ description?: string;
42
+ isDisabled?: boolean;
43
+ }
44
+
45
+ export function Checkbox({
46
+ label,
47
+ description,
48
+ className,
49
+ ...props
50
+ }: Omit<CheckboxProps, "children"> & { label: ReactNode; description?: ReactNode }) {
51
+ const id = useId();
52
+ return (
53
+ <AriaCheckbox
54
+ {...props}
55
+ aria-describedby={description ? `${id}-hint` : props["aria-describedby"]}
56
+ className={cn(
57
+ "group inline-flex min-h-control-md cursor-pointer items-start gap-3 rounded-control py-2 outline-none data-disabled:cursor-default data-disabled:opacity-50 data-focus-visible:focus-ring",
58
+ typeof className === "string" && className,
59
+ )}
60
+ >
61
+ {({ isSelected, isIndeterminate }) => (
62
+ <>
63
+ <span
64
+ aria-hidden="true"
65
+ className="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-cell border border-line-input bg-surface transition-[background-color,border-color,scale] duration-(--duration-settle) ease-settle group-data-indeterminate:border-brand group-data-indeterminate:bg-brand group-data-indeterminate:text-on-brand group-data-pressed:scale-(--press-depth) group-data-pressed:duration-(--duration-snap) group-data-pressed:ease-snap group-data-selected:border-brand group-data-selected:bg-brand group-data-selected:text-on-brand"
66
+ >
67
+ {/* The box presses in; the tick grows and settles in, like the radio dot */}
68
+ {isIndeterminate ? (
69
+ <span>−</span>
70
+ ) : (
71
+ <IconCheck
72
+ className={cn(
73
+ "size-3.5 transition-[scale] duration-(--duration-settle) ease-settle",
74
+ isSelected ? "scale-100" : "scale-0",
75
+ )}
76
+ />
77
+ )}
78
+ </span>
79
+ <span className="min-w-0">
80
+ <span className="block type-label text-fg-emphasis">{label}</span>
81
+ {description ? (
82
+ <span id={`${id}-hint`} className={cn("mt-1 block", hint)}>
83
+ {description}
84
+ </span>
85
+ ) : null}
86
+ </span>
87
+ </>
88
+ )}
89
+ </AriaCheckbox>
90
+ );
91
+ }
92
+ export function CheckboxGroup({
93
+ label,
94
+ description,
95
+ error,
96
+ options,
97
+ className,
98
+ ...props
99
+ }: Omit<CheckboxGroupProps, "children"> & {
100
+ label: ReactNode;
101
+ description?: ReactNode;
102
+ error?: ReactNode;
103
+ options: readonly Choice[];
104
+ }) {
105
+ return (
106
+ <AriaCheckboxGroup
107
+ {...props}
108
+ isInvalid={Boolean(error) || props.isInvalid}
109
+ className={cn(field, typeof className === "string" && className)}
110
+ >
111
+ <Label className="type-label text-fg-emphasis">{label}</Label>
112
+ {options.map((option) => (
113
+ <Checkbox
114
+ key={option.id}
115
+ value={option.id}
116
+ label={option.label}
117
+ description={option.description}
118
+ isDisabled={option.isDisabled}
119
+ />
120
+ ))}
121
+ {description && (
122
+ <Text slot="description" className={hint}>
123
+ {description}
124
+ </Text>
125
+ )}
126
+ <FieldError className="type-caption text-error">{error}</FieldError>
127
+ </AriaCheckboxGroup>
128
+ );
129
+ }
130
+ export function RadioGroup({
131
+ label,
132
+ description,
133
+ error,
134
+ options,
135
+ className,
136
+ ...props
137
+ }: Omit<RadioGroupProps, "children"> & {
138
+ label: ReactNode;
139
+ description?: ReactNode;
140
+ error?: ReactNode;
141
+ options: readonly Choice[];
142
+ }) {
143
+ return (
144
+ <AriaRadioGroup
145
+ {...props}
146
+ isInvalid={Boolean(error) || props.isInvalid}
147
+ className={cn(field, typeof className === "string" && className)}
148
+ >
149
+ <Label className="type-label text-fg-emphasis">{label}</Label>
150
+ {options.map((option) => (
151
+ <Radio
152
+ key={option.id}
153
+ value={option.id}
154
+ isDisabled={option.isDisabled}
155
+ className="group flex min-h-control-md cursor-pointer items-start gap-3 rounded-control py-2 outline-none data-disabled:opacity-50 data-focus-visible:focus-ring"
156
+ >
157
+ <span
158
+ aria-hidden="true"
159
+ className="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-pill border border-line-input bg-surface transition-[border-color,scale] duration-(--duration-settle) ease-settle group-data-pressed:scale-(--press-depth) group-data-pressed:duration-(--duration-snap) group-data-selected:border-brand"
160
+ >
161
+ {/* The dot settles into place: a small spring, from nothing to full size */}
162
+ <span className="size-2.5 scale-0 rounded-pill bg-brand transition-[scale] duration-(--duration-settle) ease-settle group-data-selected:scale-100" />
163
+ </span>
164
+ <span>
165
+ <span className="block type-label text-fg-emphasis">{option.label}</span>
166
+ {option.description && (
167
+ <Text slot="description" className={cn("mt-1 block", hint)}>
168
+ {option.description}
169
+ </Text>
170
+ )}
171
+ </span>
172
+ </Radio>
173
+ ))}
174
+ {description && (
175
+ <Text slot="description" className={hint}>
176
+ {description}
177
+ </Text>
178
+ )}
179
+ <FieldError className="type-caption text-error">{error}</FieldError>
180
+ </AriaRadioGroup>
181
+ );
182
+ }
183
+ export function Select({
184
+ label,
185
+ description,
186
+ error,
187
+ options,
188
+ className,
189
+ placeholder = uiCopy.choose,
190
+ ...props
191
+ }: Omit<SelectProps<Choice>, "children" | "items"> & {
192
+ label: ReactNode;
193
+ description?: ReactNode;
194
+ error?: ReactNode;
195
+ options: readonly Choice[];
196
+ }) {
197
+ return (
198
+ <PortalTheme>
199
+ {(theme) => (
200
+ <AriaSelect
201
+ {...props}
202
+ placeholder={placeholder}
203
+ isInvalid={Boolean(error) || props.isInvalid}
204
+ disabledKeys={options.filter((o) => o.isDisabled).map((o) => o.id)}
205
+ className={cn(field, typeof className === "string" && className)}
206
+ >
207
+ <Label className="type-label text-fg-emphasis">{label}</Label>
208
+ <Button className="group/trigger w-full justify-between text-left whitespace-normal">
209
+ {/* Only the chosen text, on one line, not the option's whole list layout */}
210
+ <SelectValue className="min-w-0 break-words data-placeholder:text-fg-subtle">
211
+ {({ isPlaceholder, selectedText, defaultChildren }) =>
212
+ isPlaceholder ? defaultChildren : selectedText
213
+ }
214
+ </SelectValue>
215
+ {/* Turns with the settle spring as the list opens, like the accordion */}
216
+ <IconChevronDown className="transition-[rotate] duration-(--duration-settle) ease-settle group-aria-expanded/trigger:rotate-180 motion-reduce:transition-none" />
217
+ </Button>
218
+ {description && (
219
+ <Text slot="description" className={hint}>
220
+ {description}
221
+ </Text>
222
+ )}
223
+ <FieldError className="type-caption text-error">{error}</FieldError>
224
+ <Popover
225
+ style={theme.style}
226
+ placement="bottom start"
227
+ className={cn(
228
+ theme.className,
229
+ "z-50 w-(--trigger-width) max-w-[calc(100vw-2rem)] origin-(--trigger-anchor-point) rounded-surface border border-line-subtle bg-surface p-1 text-fg shadow-popover outline-none data-entering:animate-pop-in data-exiting:animate-pop-out",
230
+ )}
231
+ >
232
+ <ListBox
233
+ items={options}
234
+ className="max-h-72 overflow-y-auto outline-none"
235
+ renderEmptyState={() => (
236
+ <p className="p-3 type-body text-fg-subtle">{uiCopy.empty}</p>
237
+ )}
238
+ >
239
+ {(item) => (
240
+ <ListBoxItem
241
+ id={item.id}
242
+ textValue={item.label}
243
+ className="group flex cursor-pointer items-center gap-3 rounded-cell p-3 outline-none data-disabled:opacity-50 data-focused:bg-surface-subtle data-selected:bg-surface-muted"
244
+ >
245
+ <span className="min-w-0 flex-1">
246
+ <Text slot="label" className="block type-label">
247
+ {item.label}
248
+ </Text>
249
+ {item.description && (
250
+ <Text slot="description" className={cn("mt-1 block", hint)}>
251
+ {item.description}
252
+ </Text>
253
+ )}
254
+ </span>
255
+ <IconCheck className="invisible group-data-selected:visible" />
256
+ </ListBoxItem>
257
+ )}
258
+ </ListBox>
259
+ </Popover>
260
+ </AriaSelect>
261
+ )}
262
+ </PortalTheme>
263
+ );
264
+ }
265
+ export function Slider({
266
+ label,
267
+ className,
268
+ ...props
269
+ }: Omit<SliderProps, "children"> & { label: ReactNode }) {
270
+ return (
271
+ <AriaSlider
272
+ {...props}
273
+ className={cn(
274
+ "flex w-full min-w-0 flex-col gap-3 data-disabled:opacity-50",
275
+ typeof className === "string" && className,
276
+ )}
277
+ >
278
+ <div className="flex justify-between gap-4">
279
+ <Label className="type-label text-fg-emphasis">{label}</Label>
280
+ <SliderOutput className="type-caption text-fg-subtle numeric" />
281
+ </div>
282
+ <SliderTrack className="relative h-6 w-full rounded-control bg-surface-subtle">
283
+ {({ state }) => (
284
+ <>
285
+ {state.values.map((_, index) => (
286
+ <SliderThumb
287
+ key={index}
288
+ index={index}
289
+ aria-label={
290
+ Array.isArray(props.value) || Array.isArray(props.defaultValue)
291
+ ? `${typeof label === "string" ? label : "Value"} ${index + 1}`
292
+ : undefined
293
+ }
294
+ // Grows from the point React Aria centres it on, so it stays centred while dragged
295
+ className="top-1/2 size-6 origin-top-left rounded-pill border border-line-emphasis bg-surface shadow-control transition-[scale,border-color] duration-(--duration-settle) ease-settle outline-none data-dragging:scale-110 data-dragging:border-brand data-focus-visible:focus-ring"
296
+ />
297
+ ))}
298
+ </>
299
+ )}
300
+ </SliderTrack>
301
+ </AriaSlider>
302
+ );
303
+ }
304
+ export function Toggle({ className, ...props }: ToggleButtonProps) {
305
+ return (
306
+ <AriaToggleButton
307
+ {...props}
308
+ className={cn(
309
+ buttonStyles({ size: "sm" }),
310
+ "data-selected:border-brand data-selected:bg-brand data-selected:text-on-brand",
311
+ typeof className === "string" && className,
312
+ )}
313
+ />
314
+ );
315
+ }
316
+ const FormValidation = createContext<"native" | "aria" | undefined>(undefined);
317
+ export function TextField(props: TextFieldProps) {
318
+ const context = useContext(FormValidation);
319
+ return (
320
+ <ExistingTextField
321
+ {...props}
322
+ validationBehavior={props.validationBehavior ?? context ?? "aria"}
323
+ />
324
+ );
325
+ }
326
+ export function Form({ className, children, validationBehavior = "native", ...props }: FormProps) {
327
+ return (
328
+ <AriaForm
329
+ {...props}
330
+ validationBehavior={validationBehavior}
331
+ className={cn("flex min-w-0 flex-col gap-4", typeof className === "string" && className)}
332
+ >
333
+ <FormValidation.Provider value={validationBehavior}>{children}</FormValidation.Provider>
334
+ </AriaForm>
335
+ );
336
+ }
@@ -0,0 +1,23 @@
1
+ import { clsx, type ClassValue } from "clsx";
2
+ import { extendTailwindMerge } from "tailwind-merge";
3
+
4
+ // Teaches tailwind-merge the library's own token names, so a className passed
5
+ // in from outside (say "rounded-none") cleanly replaces the component's own.
6
+ const merge = extendTailwindMerge<"type-role">({
7
+ extend: {
8
+ theme: {
9
+ radius: ["control", "cell", "surface", "pill"],
10
+ shadow: ["control", "popover", "dialog"],
11
+ ease: ["standard", "exit"],
12
+ animate: ["fade-in", "rise-in", "slide-in", "dialog-in", "pulse-soft"],
13
+ },
14
+ classGroups: {
15
+ "type-role": ["type-title", "type-heading", "type-body", "type-label", "type-caption"],
16
+ },
17
+ },
18
+ });
19
+
20
+ /** Joins class names and resolves Tailwind conflicts (last one wins). */
21
+ export function cn(...inputs: ClassValue[]): string {
22
+ return merge(clsx(inputs));
23
+ }
@@ -0,0 +1,114 @@
1
+ import { DefaultChatTransport, readUIMessageStream, type UIMessage, type UIMessageChunk } from "ai";
2
+ import {
3
+ ChatInterrupted,
4
+ type ChatAdapter,
5
+ type ChatEvent,
6
+ type ChatMessage,
7
+ } from "../core/index.js";
8
+
9
+ export interface AiSdkChatOptions {
10
+ endpoint: string;
11
+ /** Opt into editing, regeneration and versions. Restart is controlled separately by retry. */
12
+ conversationActions?: boolean;
13
+ /** Allow Restart as a new run. Defaults to true; disable for endpoints without replay safety. */
14
+ retry?: boolean;
15
+ /** Stable conversation identity; requestId remains a distinct per-run identifier. */
16
+ chatId?: string;
17
+ headers?: () => HeadersInit | Promise<HeadersInit>;
18
+ credentials?: RequestCredentials;
19
+ fetch?: typeof globalThis.fetch;
20
+ }
21
+
22
+ function toMessage(message: ChatMessage): UIMessage {
23
+ const retained = message.metadata?.aiSdk as
24
+ { metadata?: unknown; parts?: UIMessage["parts"] } | undefined;
25
+ return {
26
+ id: message.id,
27
+ role: message.role,
28
+ metadata: { provider: retained?.metadata, superxdFiles: message.files ?? [] },
29
+ parts: [
30
+ ...(message.text ? [{ type: "text" as const, text: message.text }] : []),
31
+ ...(message.files ?? []).map((file) => ({
32
+ type: "file" as const,
33
+ mediaType: file.mediaType,
34
+ filename: file.name,
35
+ url: file.url,
36
+ })),
37
+ ...(retained?.parts ?? []).filter((part) => part.type !== "text" && part.type !== "file"),
38
+ ],
39
+ };
40
+ }
41
+
42
+ /** SDK owns protocol parsing only; SuperXD or the host owns the conversation. */
43
+ export function createAiSdkChatAdapter(options: AiSdkChatOptions): ChatAdapter {
44
+ const chatId = options.chatId ?? globalThis.crypto.randomUUID();
45
+ const transport = new DefaultChatTransport<UIMessage>({
46
+ api: options.endpoint,
47
+ headers: async () => new Headers(await options.headers?.()),
48
+ credentials: options.credentials ?? "same-origin",
49
+ fetch: options.fetch,
50
+ });
51
+ return {
52
+ capabilities: {
53
+ send: true,
54
+ cancel: true,
55
+ retry: options.retry ?? true,
56
+ attachments: true,
57
+ ...(options.conversationActions ? { edit: true, regenerate: true, branches: true } : {}),
58
+ },
59
+ async *run(request, signal): AsyncGenerator<ChatEvent> {
60
+ const stream = await transport.sendMessages({
61
+ trigger: "submit-message",
62
+ chatId,
63
+ messageId: request.messages.at(-1)?.id,
64
+ messages: request.messages.map(toMessage),
65
+ abortSignal: signal,
66
+ body: { requestId: request.requestId },
67
+ });
68
+ let finished = false;
69
+ let remoteAbort = false;
70
+ const observed = stream.pipeThrough(
71
+ new TransformStream<UIMessageChunk, UIMessageChunk>({
72
+ transform(chunk, controller) {
73
+ if (JSON.stringify(chunk).length > 200_000) throw new Error("SDK part too large");
74
+ if (chunk.type.startsWith("tool-"))
75
+ throw new Error("Tool capabilities are not supported by this slice");
76
+ if (chunk.type === "finish") finished = true;
77
+ if (chunk.type === "abort") remoteAbort = true;
78
+ controller.enqueue(chunk);
79
+ },
80
+ }),
81
+ );
82
+ let previous = "";
83
+ try {
84
+ for await (const message of readUIMessageStream({
85
+ stream: observed,
86
+ terminateOnError: true,
87
+ })) {
88
+ if (signal.aborted) return;
89
+ const text = message.parts
90
+ .filter((part) => part.type === "text")
91
+ .map((part) => part.text)
92
+ .join("");
93
+ if (text.length > 100_000) throw new Error("Response too large");
94
+ if (text !== previous) {
95
+ yield text.startsWith(previous)
96
+ ? { type: "text", text: text.slice(previous.length) }
97
+ : { type: "replace-text", text };
98
+ previous = text;
99
+ }
100
+ const metadata = { aiSdk: { metadata: message.metadata, parts: message.parts } };
101
+ if (JSON.stringify(metadata).length > 200_000) throw new Error("Metadata too large");
102
+ yield { type: "metadata", metadata };
103
+ }
104
+ if (signal.aborted) return;
105
+ if (!finished || remoteAbort) throw new ChatInterrupted();
106
+ yield { type: "finish" };
107
+ } catch (error) {
108
+ if (signal.aborted) return;
109
+ if (error instanceof TypeError) throw new ChatInterrupted();
110
+ throw error;
111
+ }
112
+ },
113
+ };
114
+ }