@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,628 @@
1
+ import { ScenarioPicker } from "@superxd-studio/adptr/ui";
2
+ import { useEffect, useRef, useState, type ReactNode } from "react";
3
+ import { MessageMarkdown } from "@superxd-studio/adptr/web";
4
+ import * as UI from "@superxd-studio/adptr/ui";
5
+ import * as AI from "@superxd-studio/adptr/interactions";
6
+ import { layerCopy as copy } from "./copy";
7
+
8
+ function useOwner<T extends { dispose: () => void }>(make: () => T) {
9
+ const [owner] = useState(make);
10
+ const lease = useRef(0);
11
+ useEffect(() => {
12
+ const state = lease;
13
+ const current = ++state.current;
14
+ return () => {
15
+ queueMicrotask(() => {
16
+ if (state.current === current) owner.dispose();
17
+ });
18
+ };
19
+ }, [owner]);
20
+ return owner;
21
+ }
22
+ export function Foundation({ id, mode = "ready" }: { id: string; mode?: string }) {
23
+ const [value, setValue] = useState("notes"),
24
+ [checked, setChecked] = useState(false),
25
+ [number, setNumber] = useState(1),
26
+ [open, setOpen] = useState(false),
27
+ [saved, setSaved] = useState(false),
28
+ [queue] = useState(UI.createNotificationQueue);
29
+ const disabled = mode === "disabled",
30
+ error = mode === "invalid" ? copy.error : undefined;
31
+ let node: ReactNode;
32
+ switch (id) {
33
+ case "checkbox":
34
+ node = (
35
+ <UI.Checkbox
36
+ label={copy.consent}
37
+ description={copy.consentHint}
38
+ isSelected={checked}
39
+ onChange={setChecked}
40
+ isIndeterminate={mode === "mixed"}
41
+ isDisabled={disabled}
42
+ />
43
+ );
44
+ break;
45
+ case "checkbox-group":
46
+ node = (
47
+ <UI.CheckboxGroup
48
+ label={copy.priority}
49
+ options={copy.choices}
50
+ isDisabled={disabled}
51
+ error={error}
52
+ />
53
+ );
54
+ break;
55
+ case "radio-group":
56
+ node = (
57
+ <UI.RadioGroup
58
+ label={copy.priority}
59
+ options={copy.choices}
60
+ value={value}
61
+ onChange={setValue}
62
+ isDisabled={disabled}
63
+ error={error}
64
+ />
65
+ );
66
+ break;
67
+ case "select":
68
+ node = (
69
+ <UI.Select
70
+ label={copy.priority}
71
+ options={mode === "empty" ? [] : copy.choices}
72
+ selectedKey={mode === "invalid" ? null : value}
73
+ onSelectionChange={(key) => setValue(String(key))}
74
+ isDisabled={disabled}
75
+ error={error}
76
+ />
77
+ );
78
+ break;
79
+ case "slider":
80
+ node = (
81
+ <UI.Slider
82
+ label={copy.amount}
83
+ value={number}
84
+ minValue={1}
85
+ maxValue={10}
86
+ onChange={(v) => setNumber(v as number)}
87
+ isDisabled={disabled}
88
+ />
89
+ );
90
+ break;
91
+ case "toggle":
92
+ node = (
93
+ <UI.Toggle isSelected={checked} onChange={setChecked} isDisabled={disabled}>
94
+ {copy.bold}
95
+ </UI.Toggle>
96
+ );
97
+ break;
98
+ case "form":
99
+ node = (
100
+ <UI.Form
101
+ onSubmit={(event) => {
102
+ event.preventDefault();
103
+ setSaved(true);
104
+ }}
105
+ validationErrors={mode === "invalid" ? { project: copy.required } : undefined}
106
+ >
107
+ <UI.TextField name="project" label={copy.name} isRequired isDisabled={disabled} />
108
+ <UI.Button type="submit" isDisabled={disabled}>
109
+ {copy.submit}
110
+ </UI.Button>
111
+ <p role="status" className="type-body">
112
+ {saved ? copy.saved : ""}
113
+ </p>
114
+ </UI.Form>
115
+ );
116
+ break;
117
+ case "menu":
118
+ node = (
119
+ <>
120
+ <UI.Menu
121
+ label={copy.actions}
122
+ isDisabled={disabled}
123
+ items={[
124
+ { id: "open", label: copy.open },
125
+ { id: "duplicate", label: copy.duplicate },
126
+ { id: "remove", label: copy.remove, isDisabled: true, isDestructive: true },
127
+ ]}
128
+ onAction={setValue}
129
+ />
130
+ <p role="status" className="mt-4 type-body">
131
+ {value !== "notes" ? `${copy.selected}: ${value}` : ""}
132
+ </p>
133
+ </>
134
+ );
135
+ break;
136
+ case "accordion":
137
+ node = (
138
+ <UI.Accordion
139
+ label={copy.details}
140
+ allowsMultipleExpanded
141
+ items={[
142
+ { id: "scope", title: copy.scope, content: copy.scopeText },
143
+ { id: "owner", title: copy.owner, content: copy.ownerText, isDisabled: disabled },
144
+ ]}
145
+ />
146
+ );
147
+ break;
148
+ case "tabs":
149
+ node = (
150
+ <UI.Tabs
151
+ label={copy.tabLabel}
152
+ items={[
153
+ { id: "overview", label: copy.overview, content: copy.scopeText },
154
+ {
155
+ id: "activity",
156
+ label: copy.activity,
157
+ content: copy.activityText,
158
+ isDisabled: disabled,
159
+ },
160
+ ]}
161
+ />
162
+ );
163
+ break;
164
+ case "tooltip":
165
+ node = (
166
+ <UI.Tooltip content={copy.helpText} delay={0}>
167
+ <UI.Button isDisabled={disabled}>{copy.help}</UI.Button>
168
+ </UI.Tooltip>
169
+ );
170
+ break;
171
+ case "dialog":
172
+ node = (
173
+ <UI.ModalDialog
174
+ title={copy.dialogTitle}
175
+ isOpen={open}
176
+ onOpenChange={setOpen}
177
+ trigger={<UI.Button isDisabled={disabled}>{copy.open}</UI.Button>}
178
+ >
179
+ <p className="type-body">{copy.dialogText}</p>
180
+ <UI.TextField className="mt-4" label={copy.name} />
181
+ </UI.ModalDialog>
182
+ );
183
+ break;
184
+ case "pagination":
185
+ node = (
186
+ <UI.Pagination
187
+ label={copy.page}
188
+ total={3}
189
+ page={number}
190
+ onChange={setNumber}
191
+ isDisabled={disabled}
192
+ />
193
+ );
194
+ break;
195
+ case "breadcrumbs":
196
+ node = <UI.Breadcrumbs label={copy.page} items={copy.crumbs} />;
197
+ break;
198
+ case "card":
199
+ node = (
200
+ <UI.Card>
201
+ <h3 className="type-heading text-fg-emphasis">{copy.cardTitle}</h3>
202
+ <p className="mt-4 type-body">{copy.cardText}</p>
203
+ </UI.Card>
204
+ );
205
+ break;
206
+ case "badge":
207
+ node = (
208
+ <div className="flex flex-wrap gap-3">
209
+ {(["neutral", "info", "success", "attention", "error"] as const).map((tone) => (
210
+ <UI.Badge key={tone} tone={tone}>
211
+ {copy.badgeText}
212
+ </UI.Badge>
213
+ ))}
214
+ </div>
215
+ );
216
+ break;
217
+ case "progress":
218
+ node = (
219
+ <UI.Progress label={copy.progressLabel} value={60} isIndeterminate={mode === "loading"} />
220
+ );
221
+ break;
222
+ case "separator":
223
+ node = (
224
+ <div className="flex flex-col gap-4 type-body">
225
+ <p>{copy.scopeText}</p>
226
+ <UI.Separator />
227
+ <p>{copy.ownerText}</p>
228
+ </div>
229
+ );
230
+ break;
231
+ case "aspect-ratio":
232
+ node = (
233
+ <UI.AspectRatio>
234
+ <div className="flex h-full items-center justify-center bg-surface-subtle type-body">
235
+ {copy.image}
236
+ </div>
237
+ </UI.AspectRatio>
238
+ );
239
+ break;
240
+ case "keyboard-hint":
241
+ node = (
242
+ <p className="type-body">
243
+ {copy.keyLabel} <UI.KeyboardHint>⌘ K</UI.KeyboardHint>
244
+ </p>
245
+ );
246
+ break;
247
+ case "table":
248
+ node = (
249
+ <UI.DataTable
250
+ label={copy.tableLabel}
251
+ columns={copy.columns}
252
+ rows={mode === "empty" ? [] : copy.rows}
253
+ selectionMode={mode === "selection" ? "multiple" : "none"}
254
+ />
255
+ );
256
+ break;
257
+ case "toast":
258
+ node = (
259
+ <>
260
+ <UI.Button onPress={() => queue.add(copy.notification)}>{copy.notify}</UI.Button>
261
+ <UI.ToastRegion queue={queue} />
262
+ </>
263
+ );
264
+ break;
265
+ }
266
+ return <div className="mx-auto w-full max-w-xl min-w-0">{node}</div>;
267
+ }
268
+ export function Presentation({ id }: { id: string }) {
269
+ const [value, setValue] = useState("balanced"),
270
+ [context, setContext] = useState<string[]>([]),
271
+ [chosen, setChosen] = useState("");
272
+ let node: ReactNode;
273
+ switch (id) {
274
+ case "reasoning":
275
+ node = <AI.Reasoning text={copy.explanation} />;
276
+ break;
277
+ case "tool-presentation":
278
+ node = (
279
+ <AI.ToolCall
280
+ title={copy.toolTitle}
281
+ status="completed"
282
+ input={copy.input}
283
+ output={copy.output}
284
+ />
285
+ );
286
+ break;
287
+ case "sources":
288
+ node = <AI.Sources sources={copy.sources} />;
289
+ break;
290
+ case "citation":
291
+ node = (
292
+ <p className="type-body">
293
+ {copy.sources[0].claim} <AI.Citation source={copy.sources[0]} index={1} />
294
+ </p>
295
+ );
296
+ break;
297
+ case "retrieval-inspection":
298
+ node = (
299
+ <AI.RetrievalInspection
300
+ documents={copy.sources.map((source, index) => ({ ...source, included: index === 0 }))}
301
+ />
302
+ );
303
+ break;
304
+ case "confidence":
305
+ node = <AI.Confidence claim={copy.claim} explanation={copy.confidenceText} />;
306
+ break;
307
+ case "guardrail":
308
+ node = (
309
+ <AI.Guardrail reason={copy.blocked} alternatives={copy.alternatives} onChoose={setChosen} />
310
+ );
311
+ break;
312
+ case "agent-status":
313
+ node = (
314
+ <AI.AgentStatus label={copy.toolTitle} status={AI.interactionCopy.agentStates.ready} />
315
+ );
316
+ break;
317
+ case "connection-status":
318
+ node = (
319
+ <AI.ConnectionStatus
320
+ state={chosen ? "connected" : "disconnected"}
321
+ onReconnect={() => setChosen("connected")}
322
+ />
323
+ );
324
+ break;
325
+ case "composer-controls":
326
+ node = (
327
+ <AI.ComposerTools
328
+ models={copy.modelChoices}
329
+ model={value}
330
+ onModelChange={setValue}
331
+ contexts={copy.choices}
332
+ selectedContext={context}
333
+ onContextChange={setContext}
334
+ commands={copy.commands}
335
+ onCommand={setChosen}
336
+ />
337
+ );
338
+ break;
339
+ case "quoted-reply":
340
+ node = <AI.QuotedExcerpt quote={copy.quote} onReply={() => setChosen(copy.quoteUsed)} />;
341
+ break;
342
+ case "execution-inspector":
343
+ node = (
344
+ <AI.ExecutionInspector
345
+ code={copy.code}
346
+ files={copy.files}
347
+ output={copy.output}
348
+ tests={copy.checks}
349
+ />
350
+ );
351
+ break;
352
+ case "observability":
353
+ node = <AI.Observability measurements={copy.measurements} />;
354
+ break;
355
+ }
356
+ return (
357
+ <div className="mx-auto w-full max-w-2xl min-w-0">
358
+ {node}
359
+ {chosen && (
360
+ <p role="status" className="mt-4 type-body">
361
+ {chosen}
362
+ </p>
363
+ )}
364
+ </div>
365
+ );
366
+ }
367
+ const wait = () => new Promise<void>((resolve) => setTimeout(resolve, 150));
368
+ function AgentScenario({ lost = false }: { lost?: boolean }) {
369
+ const owner = useOwner(() => {
370
+ const receipts = new Map<string, AI.AgentResult>();
371
+ return new AI.AgentController(
372
+ {
373
+ async execute(request, _signal, progress) {
374
+ const prior = receipts.get(request.requestId);
375
+ if (prior) return prior;
376
+ progress(copy.working);
377
+ await wait();
378
+ const result = {
379
+ requestId: request.requestId,
380
+ status: "completed" as const,
381
+ output: copy.stepDone,
382
+ };
383
+ receipts.set(request.requestId, result);
384
+ if (lost && receipts.size === 1) throw new Error("Reply lost");
385
+ return result;
386
+ },
387
+ async lookup(id) {
388
+ await wait();
389
+ return receipts.get(id) ?? { requestId: id, status: "not-found" };
390
+ },
391
+ },
392
+ copy.steps,
393
+ );
394
+ });
395
+ return <AI.AgentSupervision controller={owner} />;
396
+ }
397
+ function ArtifactScenario({ mode = "ready" }: { mode?: string }) {
398
+ const owner = useOwner(() => {
399
+ let record: AI.ArtifactRecord = {
400
+ id: "brief",
401
+ title: copy.artifactTitle,
402
+ revision: "v1",
403
+ content: copy.artifactContent,
404
+ canEdit: mode !== "read-only",
405
+ history: [],
406
+ };
407
+ let first = true;
408
+ const receipts = new Map<string, AI.ArtifactResult>();
409
+ return new AI.ArtifactController({
410
+ async load() {
411
+ await wait();
412
+ return record;
413
+ },
414
+ async save(request) {
415
+ const prior = receipts.get(request.requestId);
416
+ if (prior) return prior;
417
+ await wait();
418
+ if (mode === "conflict" && first) {
419
+ first = false;
420
+ record = {
421
+ ...record,
422
+ revision: "v2",
423
+ content: copy.draftChange,
424
+ history: [{ revision: "v1", content: record.content }],
425
+ };
426
+ }
427
+ let result: AI.ArtifactResult;
428
+ if (request.baseRevision !== record.revision)
429
+ result = { requestId: request.requestId, status: "conflict", record };
430
+ else {
431
+ record = {
432
+ ...record,
433
+ revision: `v${record.history.length + 2}`,
434
+ content: request.content,
435
+ history: [...record.history, { revision: record.revision, content: record.content }],
436
+ };
437
+ result = { requestId: request.requestId, status: "saved", record };
438
+ }
439
+ receipts.set(request.requestId, result);
440
+ if (mode === "lost" && first) {
441
+ first = false;
442
+ throw new Error("Reply lost");
443
+ }
444
+ return result;
445
+ },
446
+ async lookup(id) {
447
+ await wait();
448
+ return receipts.get(id) ?? { requestId: id, status: "not-found" };
449
+ },
450
+ });
451
+ });
452
+ return (
453
+ <>
454
+ <UI.Button className="mb-4" onPress={() => owner.open("brief")}>
455
+ {copy.artifactOpen}
456
+ </UI.Button>
457
+ <AI.ArtifactWorkspace controller={owner} />
458
+ </>
459
+ );
460
+ }
461
+ function FeedbackScenario({ fails = false }: { fails?: boolean }) {
462
+ const owner = useOwner(() => {
463
+ let first = true;
464
+ return new AI.FeedbackController(
465
+ {
466
+ async save(request) {
467
+ await wait();
468
+ if (fails && first) {
469
+ first = false;
470
+ throw new Error("Storage unavailable");
471
+ }
472
+ return { requestId: request.requestId, responseId: request.responseId };
473
+ },
474
+ },
475
+ copy.responseId,
476
+ );
477
+ });
478
+ return (
479
+ <div className="mx-auto w-full max-w-xl">
480
+ <p className="mb-6 type-body">{copy.response}</p>
481
+ <AI.ResponseFeedback controller={owner} />
482
+ </div>
483
+ );
484
+ }
485
+ function HistoryScenario({ fails = false }: { fails?: boolean }) {
486
+ const owner = useOwner(() => {
487
+ const records = copy.threadTitles.map((title, index) => ({
488
+ id: `thread-${index}`,
489
+ title,
490
+ archived: false,
491
+ payload: {
492
+ messages: [{ role: "assistant", text: copy.threadText[index] ?? "" }],
493
+ branches: [],
494
+ attachments: [],
495
+ },
496
+ }));
497
+ const findRecord = (id: string) => {
498
+ const record = records.find((item) => item.id === id);
499
+ if (!record) throw new Error("Unknown local conversation");
500
+ return record;
501
+ };
502
+ let first = true;
503
+ return new AI.ThreadController({
504
+ async list() {
505
+ await wait();
506
+ if (fails && first) {
507
+ first = false;
508
+ throw new Error("Storage unavailable");
509
+ }
510
+ return records.map(({ id, title, archived }) => ({ id, title, archived }));
511
+ },
512
+ async load(id) {
513
+ await wait();
514
+ return findRecord(id);
515
+ },
516
+ async rename(id, title) {
517
+ await wait();
518
+ const record = findRecord(id);
519
+ record.title = title as typeof record.title;
520
+ return { id, title, archived: record.archived };
521
+ },
522
+ async archive(id, archived) {
523
+ await wait();
524
+ const record = findRecord(id);
525
+ record.archived = archived;
526
+ return { id, title: record.title, archived };
527
+ },
528
+ });
529
+ });
530
+ return (
531
+ <div className="mx-auto w-full max-w-2xl">
532
+ <AI.ConversationHistory
533
+ controller={owner}
534
+ renderThread={(thread) => (
535
+ <UI.Card aria-label={copy.threadView}>
536
+ <h3 className="type-heading">{thread.title}</h3>
537
+ <div className="mt-4 flex flex-col gap-4">
538
+ {typeof thread.payload === "object" &&
539
+ thread.payload !== null &&
540
+ !Array.isArray(thread.payload) &&
541
+ Array.isArray(thread.payload.messages)
542
+ ? thread.payload.messages.map((message, index) =>
543
+ typeof message === "object" &&
544
+ message !== null &&
545
+ !Array.isArray(message) &&
546
+ typeof message.text === "string" ? (
547
+ <MessageMarkdown key={index} text={message.text} />
548
+ ) : null,
549
+ )
550
+ : null}
551
+ </div>
552
+ </UI.Card>
553
+ )}
554
+ />
555
+ </div>
556
+ );
557
+ }
558
+ export function AgentExample({ lost = false }: { lost?: boolean }) {
559
+ const [scenario, setScenario] = useState(lost ? "lost" : "ready");
560
+ return (
561
+ <div className="flex flex-col gap-6">
562
+ <ScenarioPicker
563
+ label={copy.scenario}
564
+ value={scenario}
565
+ onChange={setScenario}
566
+ options={[
567
+ { id: "ready", label: copy.ready, description: copy.descriptions.agent },
568
+ { id: "lost", label: copy.lost, description: copy.lostDescription },
569
+ ]}
570
+ />
571
+ <AgentScenario key={scenario} lost={scenario === "lost"} />
572
+ </div>
573
+ );
574
+ }
575
+ export function ArtifactExample({ mode = "ready" }: { mode?: string }) {
576
+ const [scenario, setScenario] = useState(mode);
577
+ return (
578
+ <div className="flex flex-col gap-6">
579
+ <ScenarioPicker
580
+ label={copy.scenario}
581
+ value={scenario}
582
+ onChange={setScenario}
583
+ options={[
584
+ { id: "ready", label: copy.ready, description: copy.descriptions.artifact },
585
+ { id: "conflict", label: copy.conflict, description: copy.conflictDescription },
586
+ { id: "lost", label: copy.lost, description: copy.lostDescription },
587
+ { id: "read-only", label: copy.readOnly, description: copy.readOnlyDescription },
588
+ ]}
589
+ />
590
+ <ArtifactScenario key={scenario} mode={scenario} />
591
+ </div>
592
+ );
593
+ }
594
+ export function FeedbackExample({ fails = false }: { fails?: boolean }) {
595
+ const [scenario, setScenario] = useState(fails ? "failed" : "ready");
596
+ return (
597
+ <div className="flex flex-col gap-6">
598
+ <ScenarioPicker
599
+ label={copy.scenario}
600
+ value={scenario}
601
+ onChange={setScenario}
602
+ options={[
603
+ { id: "ready", label: copy.ready, description: copy.descriptions.feedback },
604
+ { id: "failed", label: copy.failed, description: copy.failedDescription },
605
+ ]}
606
+ />
607
+ <FeedbackScenario key={scenario} fails={scenario === "failed"} />
608
+ </div>
609
+ );
610
+ }
611
+
612
+ export function HistoryExample() {
613
+ const [scenario, setScenario] = useState("ready");
614
+ return (
615
+ <div className="flex flex-col gap-6">
616
+ <ScenarioPicker
617
+ label={copy.scenario}
618
+ value={scenario}
619
+ onChange={setScenario}
620
+ options={[
621
+ { id: "ready", label: copy.ready, description: copy.descriptions.history },
622
+ { id: "failed", label: copy.failed, description: copy.failedDescription },
623
+ ]}
624
+ />
625
+ <HistoryScenario key={scenario} fails={scenario === "failed"} />
626
+ </div>
627
+ );
628
+ }