@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,27 @@
1
+ export const reviewFixture = {
2
+ title: "Make the welcome email clearer",
3
+ destination: "Welcome email",
4
+ changes: [
5
+ {
6
+ id: "subject",
7
+ title: "A clearer subject",
8
+ before: "Your journey begins here",
9
+ after: "Your workspace is ready",
10
+ reason: "Tell people what they can do next.",
11
+ },
12
+ {
13
+ id: "opening",
14
+ title: "A more useful opening",
15
+ before: "We’re delighted to welcome you to the platform.",
16
+ after: "Your workspace is ready. Add your first project when you’re ready.",
17
+ reason: "Keep the welcome warm and make the next step clear.",
18
+ },
19
+ {
20
+ id: "action",
21
+ title: "A specific action",
22
+ before: "Get started",
23
+ after: "Create your first project",
24
+ reason: "Describe where the action takes people.",
25
+ },
26
+ ],
27
+ };
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "superxd-local-review-backend",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "start": "node server.mjs"
7
+ },
8
+ "dependencies": {
9
+ "@langchain/langgraph": "1.4.18"
10
+ }
11
+ }
@@ -0,0 +1,276 @@
1
+ import { createServer } from "node:http";
2
+ import { randomUUID, randomBytes, timingSafeEqual } from "node:crypto";
3
+ import { pathToFileURL } from "node:url";
4
+ import { realpathSync } from "node:fs";
5
+ import { setTimeout as delay } from "node:timers/promises";
6
+ import { parseReviewDecision } from "../../review/index.js";
7
+ import { createLangGraphReviewDriver } from "../../langgraph-review/index.js";
8
+ import { reviewFixture } from "./copy.mjs";
9
+
10
+ const scenarios = new Set([
11
+ "normal",
12
+ "load-fails",
13
+ "send-fails",
14
+ "lost-response",
15
+ "conflict",
16
+ "partial",
17
+ "blocked",
18
+ "empty",
19
+ "slow",
20
+ ]);
21
+ const origins = new Set([
22
+ "http://127.0.0.1:5190",
23
+ "http://127.0.0.1:5300",
24
+ "http://127.0.0.1:5301",
25
+ ]);
26
+ const clone = (value) => structuredClone(value);
27
+ const fault = (code) => Object.assign(new Error("Review rejected"), { code });
28
+ /** Loopback fixture only. No models, emails, real documents, accounts or durable storage. */
29
+ export function createReviewServer({ wait = 120, now = Date.now } = {}) {
30
+ const sessions = new Map();
31
+ async function body(request) {
32
+ let size = 0;
33
+ const chunks = [];
34
+ for await (const chunk of request) {
35
+ size += chunk.length;
36
+ if (size > 8000) throw fault(413);
37
+ chunks.push(chunk);
38
+ }
39
+ try {
40
+ return JSON.parse(Buffer.concat(chunks).toString("utf8"));
41
+ } catch {
42
+ throw fault(400);
43
+ }
44
+ }
45
+ function authorize(request, session) {
46
+ const value = request.headers.authorization;
47
+ if (!session || session.expires <= now() || typeof value !== "string") throw fault(401);
48
+ const supplied = Buffer.from(value),
49
+ expected = Buffer.from(`Bearer ${session.token}`);
50
+ if (supplied.length !== expected.length || !timingSafeEqual(supplied, expected))
51
+ throw fault(401);
52
+ }
53
+ function validate(session, decision) {
54
+ const r = session.record;
55
+ if (session.expires <= now()) throw fault(401);
56
+ if (!r.proposal.canDecide) throw fault(403);
57
+ if (
58
+ r.proposal.id !== decision.proposalId ||
59
+ r.proposal.revision !== decision.revision ||
60
+ !["pending", "partial"].includes(r.outcome)
61
+ )
62
+ throw fault(409);
63
+ if (decision.changeIds.some((id) => !r.remainingIds.includes(id))) throw fault(400);
64
+ // Preconditions come from the server's stored proposal, never client text or arbitrary tool args.
65
+ if (
66
+ decision.changeIds.some(
67
+ (id) => session.document[id] !== r.proposal.changes.find((c) => c.id === id)?.before,
68
+ )
69
+ )
70
+ throw fault(409);
71
+ }
72
+ function commit(session, decision) {
73
+ validate(session, decision);
74
+ const r = clone(session.record);
75
+ const appliedIds = [];
76
+ if (decision.choice === "approve") {
77
+ for (const id of decision.changeIds) {
78
+ if (session.scenario === "partial" && !session.partial && appliedIds.length === 1) break;
79
+ const change = r.proposal.changes.find((c) => c.id === id);
80
+ session.document[id] = change.after;
81
+ appliedIds.push(id);
82
+ session.applyCount++;
83
+ }
84
+ session.partial = true;
85
+ }
86
+ r.appliedIds.push(...appliedIds);
87
+ r.remainingIds =
88
+ decision.choice === "approve"
89
+ ? decision.changeIds.filter((id) => !appliedIds.includes(id))
90
+ : [];
91
+ r.outcome =
92
+ decision.choice === "deny"
93
+ ? r.appliedIds.length
94
+ ? "complete"
95
+ : "denied"
96
+ : r.remainingIds.length
97
+ ? "partial"
98
+ : "complete";
99
+ r.proposal.revision++;
100
+ const receipt = {
101
+ requestId: decision.requestId,
102
+ choice: decision.choice,
103
+ appliedIds,
104
+ record: r,
105
+ };
106
+ // One synchronous commit includes the result and its idempotency receipt. Never evict active receipts.
107
+ session.record = r;
108
+ session.receipts.set(decision.requestId, {
109
+ fingerprint: JSON.stringify(decision),
110
+ receipt: clone(receipt),
111
+ });
112
+ return Promise.resolve(receipt);
113
+ }
114
+ async function pause(session) {
115
+ session.thread = `${session.id}-${session.record.proposal.revision}`;
116
+ await session.driver.start(clone(session.record.proposal), session.thread);
117
+ }
118
+ const server = createServer(async (request, response) => {
119
+ function json(code, value) {
120
+ if (!response.destroyed)
121
+ response
122
+ .writeHead(code, {
123
+ "content-type": "application/json",
124
+ "cache-control": "no-store",
125
+ "x-content-type-options": "nosniff",
126
+ })
127
+ .end(JSON.stringify(value));
128
+ }
129
+ try {
130
+ if (request.headers.host !== `127.0.0.1:${request.socket.localPort}`) throw fault(403);
131
+ const origin = request.headers.origin;
132
+ if (origin && !origins.has(origin)) throw fault(403);
133
+ if (origin) response.setHeader("access-control-allow-origin", origin);
134
+ response.setHeader("vary", "Origin");
135
+ if (request.method === "OPTIONS") {
136
+ response
137
+ .writeHead(204, {
138
+ "access-control-allow-methods": "GET,POST",
139
+ "access-control-allow-headers": "content-type,authorization",
140
+ })
141
+ .end();
142
+ return;
143
+ }
144
+ if (request.method === "GET" && request.url === "/health") {
145
+ json(200, { ready: true });
146
+ return;
147
+ }
148
+ if (request.method === "POST" && request.url === "/sessions") {
149
+ const input = await body(request);
150
+ if (!input || Object.keys(input).join() !== "scenario" || !scenarios.has(input.scenario))
151
+ throw fault(400);
152
+ // Expired sessions already reject every request; removing them preserves active receipts.
153
+ for (const [id, session] of sessions) if (session.expires <= now()) sessions.delete(id);
154
+ // Keep the fixture bounded and refuse new work instead of dropping replay protection.
155
+ if (sessions.size >= 200) throw fault(503);
156
+ const changes = input.scenario === "empty" ? [] : clone(reviewFixture.changes);
157
+ const session = {
158
+ id: randomUUID(),
159
+ token: randomBytes(32).toString("hex"),
160
+ scenario: input.scenario,
161
+ expires: now() + 30 * 60_000,
162
+ record: {
163
+ proposal: {
164
+ id: randomUUID(),
165
+ revision: 1,
166
+ title: reviewFixture.title,
167
+ destination: reviewFixture.destination,
168
+ canDecide: input.scenario !== "blocked",
169
+ changes,
170
+ },
171
+ appliedIds: [],
172
+ remainingIds: changes.map((c) => c.id),
173
+ outcome: "pending",
174
+ },
175
+ document: Object.fromEntries(changes.map((c) => [c.id, c.before])),
176
+ applyCount: 0,
177
+ receipts: new Map(),
178
+ queue: Promise.resolve(),
179
+ failed: false,
180
+ partial: false,
181
+ lost: false,
182
+ stale: false,
183
+ loadFailed: false,
184
+ };
185
+ session.driver = createLangGraphReviewDriver((decision) => commit(session, decision));
186
+ sessions.set(session.id, session);
187
+ try {
188
+ await pause(session);
189
+ } catch (error) {
190
+ sessions.delete(session.id);
191
+ throw error;
192
+ }
193
+ json(201, { id: session.id, token: session.token });
194
+ return;
195
+ }
196
+ const match = /^\/reviews\/([\w-]+)(?:\/(decisions|document|receipts\/([\w-]+)))?$/.exec(
197
+ request.url ?? "",
198
+ );
199
+ if (!match) throw fault(404);
200
+ const session = sessions.get(match[1]);
201
+ authorize(request, session);
202
+ const action = match[2];
203
+ if (request.method === "POST" && action === "decisions") {
204
+ let decision;
205
+ try {
206
+ decision = parseReviewDecision(await body(request));
207
+ } catch (error) {
208
+ throw error.code ? error : fault(400);
209
+ }
210
+ const process = async () => {
211
+ const seen = session.receipts.get(decision.requestId);
212
+ if (seen) {
213
+ if (seen.fingerprint !== JSON.stringify(decision)) throw fault(409);
214
+ return clone(seen.receipt);
215
+ }
216
+ if (session.scenario === "conflict" && !session.stale) {
217
+ session.stale = true;
218
+ session.record.proposal.revision++;
219
+ session.record.proposal.changes[0].after = "Your new workspace is ready";
220
+ await pause(session);
221
+ throw fault(409);
222
+ }
223
+ validate(session, decision);
224
+ if (session.scenario === "send-fails" && !session.failed) {
225
+ session.failed = true;
226
+ throw fault(503);
227
+ }
228
+ await delay(session.scenario === "slow" ? wait * 12 : wait);
229
+ const receipt = await session.driver.resume(decision, session.thread);
230
+ if (session.record.outcome === "partial") await pause(session);
231
+ if (session.scenario === "lost-response" && !session.lost) {
232
+ session.lost = true;
233
+ response
234
+ .writeHead(200, { "content-type": "application/json", "cache-control": "no-store" })
235
+ .end('{"receipt":');
236
+ return null;
237
+ }
238
+ return receipt;
239
+ };
240
+ const job = session.queue.then(process);
241
+ session.queue = job.then(
242
+ () => {},
243
+ () => {},
244
+ );
245
+ const result = await job;
246
+ if (result) json(200, result);
247
+ return;
248
+ }
249
+ if (request.method !== "GET") throw fault(405);
250
+ await session.queue;
251
+ if (!action) {
252
+ if (session.scenario === "load-fails" && !session.loadFailed) {
253
+ session.loadFailed = true;
254
+ throw fault(503);
255
+ }
256
+ await delay(wait);
257
+ json(200, clone(session.record));
258
+ } else if (action === "document")
259
+ json(200, { fields: clone(session.document), applyCount: session.applyCount });
260
+ else if (action.startsWith("receipts/"))
261
+ json(200, {
262
+ record: clone(session.record),
263
+ receipt: clone(session.receipts.get(match[3])?.receipt ?? null),
264
+ });
265
+ else throw fault(404);
266
+ } catch (error) {
267
+ json(error.code ?? 500, { unavailable: true });
268
+ }
269
+ });
270
+ return server;
271
+ }
272
+ if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) {
273
+ createReviewServer().listen(5194, "127.0.0.1", () =>
274
+ console.log("Local review example ready at http://127.0.0.1:5194"),
275
+ );
276
+ }
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>SuperXD conversation with files</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "superxd-sdk-consumer",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "vite --host 127.0.0.1 --port 5300",
7
+ "build": "tsc --noEmit && vite build"
8
+ },
9
+ "dependencies": {
10
+ "@superxd-studio/adptr": "file:../..",
11
+ "ai": "7.0.126",
12
+ "react": "19.3.0",
13
+ "react-dom": "19.3.0"
14
+ },
15
+ "devDependencies": {
16
+ "@types/react": "^19.3.0",
17
+ "@types/react-dom": "^19.3.0",
18
+ "typescript": "~6.0",
19
+ "vite": "^8.3.1"
20
+ }
21
+ }
@@ -0,0 +1 @@
1
+ export const exampleCopy = { description: "PNG, JPEG, PDF or text · up to 4 files · 5 MB each" };
@@ -0,0 +1,64 @@
1
+ import { useEffect, useState } from "react";
2
+ import { createRoot } from "react-dom/client";
3
+ import { AttachmentController, ChatController } from "@superxd-studio/adptr/core";
4
+ import { createAiSdkChatAdapter } from "@superxd-studio/adptr/ai-sdk";
5
+ import { createHttpUploadAdapter } from "@superxd-studio/adptr/uploads";
6
+ import {
7
+ Chat,
8
+ AttachmentPicker,
9
+ useAttachments,
10
+ useChatController,
11
+ } from "@superxd-studio/adptr/web";
12
+ import "@superxd-studio/adptr/styles.css";
13
+ import "@superxd-studio/adptr/fonts.css";
14
+ import { exampleCopy } from "./copy";
15
+
16
+ function App() {
17
+ const [controller] = useState(
18
+ () =>
19
+ new ChatController(createAiSdkChatAdapter({ endpoint: "http://127.0.0.1:5193/chat" }), () =>
20
+ crypto.randomUUID(),
21
+ ),
22
+ );
23
+ const [uploads] = useState(
24
+ () =>
25
+ new AttachmentController(
26
+ createHttpUploadAdapter({
27
+ endpoint: "http://127.0.0.1:5193/uploads",
28
+ releaseEndpoint: (id) => `http://127.0.0.1:5193/files/${id}`,
29
+ }),
30
+ {
31
+ maxFiles: 4,
32
+ maxBytes: 5_000_000,
33
+ mediaTypes: ["image/png", "image/jpeg", "application/pdf", "text/plain"],
34
+ },
35
+ () => crypto.randomUUID(),
36
+ ),
37
+ );
38
+ useEffect(
39
+ () => () => {
40
+ controller.dispose();
41
+ uploads.dispose();
42
+ },
43
+ [controller, uploads],
44
+ );
45
+ const snapshot = useChatController(controller);
46
+ const queue = useAttachments(uploads);
47
+ return (
48
+ <main className="mx-auto max-w-3xl p-6">
49
+ <Chat
50
+ snapshot={snapshot}
51
+ actions={controller}
52
+ draftFiles={queue.items.flatMap((item) => (item.file ? [item.file] : []))}
53
+ isAttachmentBlocked={queue.items.some((item) => item.status !== "ready")}
54
+ onSent={uploads.transfer}
55
+ attachmentPicker={
56
+ <AttachmentPicker controller={uploads} description={exampleCopy.description} />
57
+ }
58
+ />
59
+ </main>
60
+ );
61
+ }
62
+ const root = document.getElementById("root");
63
+ if (!root) throw new Error("Missing root");
64
+ createRoot(root).render(<App />);
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["ES2023", "DOM", "DOM.Iterable"],
5
+ "module": "ESNext",
6
+ "moduleResolution": "Bundler",
7
+ "jsx": "react-jsx",
8
+ "strict": true,
9
+ "skipLibCheck": true,
10
+ "noEmit": true
11
+ },
12
+ "include": ["src"]
13
+ }
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>SuperXD structured response consumer</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "superxd-generative-consumer",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "vite --host 127.0.0.1 --port 5302",
7
+ "build": "tsc --noEmit && vite build"
8
+ },
9
+ "dependencies": {
10
+ "@superxd-studio/adptr": "file:../..",
11
+ "@assistant-ui/react": "0.15.22",
12
+ "@assistant-ui/react-generative-ui": "0.0.22",
13
+ "react-aria-components": "^1.21.1",
14
+ "react": "^19.3.0",
15
+ "react-dom": "^19.3.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/react": "^19.3.0",
19
+ "@types/react-dom": "^19.3.0",
20
+ "typescript": "~6.0",
21
+ "vite": "^8.3.1"
22
+ }
23
+ }
@@ -0,0 +1,42 @@
1
+ export const copy = {
2
+ title: "Your task. Your decision.",
3
+ note: "Local example. Nothing is sent to a service.",
4
+ replace: "Update the supplied response",
5
+ reset: "Open a fresh response",
6
+ hostLabel: "Create this task",
7
+ hostDescription: "Save the reviewed title in this local example.",
8
+ receipt: "Task saved in this local example.",
9
+ taskTitle: "Task title",
10
+ initial: "Prepare the design review",
11
+ updated: "A different suggested title",
12
+ submit: "Create task",
13
+ trend: "Tasks this week",
14
+ };
15
+ export function taskSpec(title: string) {
16
+ return {
17
+ $type: "Card",
18
+ asForm: true,
19
+ title: copy.title,
20
+ confirm: { label: copy.submit, $action: { type: "create_task" } },
21
+ children: [
22
+ { $type: "Input", name: "title", label: copy.taskTitle, defaultValue: title, required: true },
23
+ ],
24
+ };
25
+ }
26
+ export const trendSpec = {
27
+ $type: "Card",
28
+ title: copy.trend,
29
+ children: [
30
+ {
31
+ $type: "Chart",
32
+ label: copy.trend,
33
+ variant: "bar",
34
+ showAxis: true,
35
+ data: [
36
+ { label: "Monday", value: 2 },
37
+ { label: "Tuesday", value: 3 },
38
+ { label: "Wednesday", value: 4 },
39
+ ],
40
+ },
41
+ ],
42
+ };
@@ -0,0 +1,74 @@
1
+ import { useState } from "react";
2
+ import { createRoot } from "react-dom/client";
3
+ import { Button } from "react-aria-components";
4
+ import {
5
+ StructuredResponse,
6
+ type GenerativeActions,
7
+ type GenerativeReceipt,
8
+ } from "@superxd-studio/adptr/generative";
9
+ import "@superxd-studio/adptr/styles.css";
10
+ import "@superxd-studio/adptr/fonts.css";
11
+ import { copy, taskSpec, trendSpec } from "./copy";
12
+
13
+ function App() {
14
+ const [suggested, setSuggested] = useState<string>(copy.initial);
15
+ const [response, setResponse] = useState(0);
16
+ const [actions] = useState<GenerativeActions>(() => {
17
+ const receipts = new Map<string, GenerativeReceipt>();
18
+ return {
19
+ create_task: {
20
+ kind: "mutate",
21
+ label: copy.hostLabel,
22
+ description: copy.hostDescription,
23
+ execute: async (request, signal) => {
24
+ if (signal.aborted) throw new Error("Aborted");
25
+ const prior = receipts.get(request.key);
26
+ if (prior) return prior;
27
+ const receipt: GenerativeReceipt = {
28
+ key: request.key,
29
+ type: request.action.type,
30
+ state: "complete",
31
+ message: copy.receipt,
32
+ };
33
+ receipts.set(request.key, receipt);
34
+ // Model a lost reply after recording a receipt. Lookup recovers without another write.
35
+ throw new Error("Local reply lost");
36
+ },
37
+ lookup: async (request, signal) =>
38
+ signal.aborted ? null : (receipts.get(request.key) ?? null),
39
+ },
40
+ };
41
+ });
42
+ return (
43
+ <main className="mx-auto flex max-w-2xl flex-col gap-6 bg-surface-muted p-6">
44
+ <h1 className="type-title text-fg-emphasis">{copy.title}</h1>
45
+ <p className="type-body text-fg-subtle">{copy.note}</p>
46
+ <div className="flex flex-wrap gap-3">
47
+ <Button
48
+ className="rounded-control border border-line-input bg-surface px-control-md-x py-control-y type-body text-fg data-focus-visible:focus-ring"
49
+ onPress={() => setSuggested(copy.updated)}
50
+ >
51
+ {copy.replace}
52
+ </Button>
53
+ <Button
54
+ className="rounded-control border border-line-input bg-surface px-control-md-x py-control-y type-body text-fg data-focus-visible:focus-ring"
55
+ onPress={() => {
56
+ setResponse((n) => n + 1);
57
+ setSuggested(copy.initial);
58
+ }}
59
+ >
60
+ {copy.reset}
61
+ </Button>
62
+ </div>
63
+ <StructuredResponse
64
+ responseId={`task-${response}`}
65
+ spec={taskSpec(suggested)}
66
+ actions={actions}
67
+ />
68
+ <StructuredResponse responseId="trend" spec={trendSpec} />
69
+ </main>
70
+ );
71
+ }
72
+ const root = document.getElementById("root");
73
+ if (!root) throw new Error("Missing root");
74
+ createRoot(root).render(<App />);
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["ES2023", "DOM", "DOM.Iterable"],
5
+ "module": "ESNext",
6
+ "moduleResolution": "Bundler",
7
+ "jsx": "react-jsx",
8
+ "strict": true,
9
+ "skipLibCheck": true,
10
+ "noEmit": true,
11
+ "allowArbitraryExtensions": true
12
+ },
13
+ "include": ["src"]
14
+ }
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>SuperXD independent consumer</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "superxd-independent-consumer",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "vite --host 127.0.0.1 --port 5300",
7
+ "build": "tsc --noEmit && vite build"
8
+ },
9
+ "dependencies": {
10
+ "@superxd-studio/adptr": "file:../..",
11
+ "react": "^19.3.0",
12
+ "react-dom": "^19.3.0"
13
+ },
14
+ "devDependencies": {
15
+ "@types/react": "^19.3.0",
16
+ "@types/react-dom": "^19.3.0",
17
+ "typescript": "~6.0",
18
+ "vite": "^8.3.1"
19
+ }
20
+ }
@@ -0,0 +1,32 @@
1
+ import { useEffect, useState } from "react";
2
+ import { createRoot } from "react-dom/client";
3
+ import { ChatController } from "@superxd-studio/adptr/core";
4
+ import { createHttpChatAdapter } from "@superxd-studio/adptr/custom";
5
+ import { Chat, useChatController } from "@superxd-studio/adptr/web";
6
+ import "@superxd-studio/adptr/styles.css";
7
+ import "@superxd-studio/adptr/fonts.css";
8
+
9
+ function App() {
10
+ const [controller] = useState(() => {
11
+ const session = crypto.randomUUID();
12
+ return new ChatController(
13
+ createHttpChatAdapter({
14
+ endpoint: "http://127.0.0.1:5192/chat",
15
+ conversationActions: true,
16
+ headers: () => ({ "x-demo-format": "rich", "x-demo-session": session }),
17
+ }),
18
+ () => crypto.randomUUID(),
19
+ );
20
+ });
21
+ const snapshot = useChatController(controller);
22
+ useEffect(() => () => controller.dispose(), [controller]);
23
+ return (
24
+ <main className="mx-auto max-w-3xl p-6">
25
+ <h1 className="type-title text-fg-emphasis">Your product. Your backend.</h1>
26
+ <Chat snapshot={snapshot} actions={controller} />
27
+ </main>
28
+ );
29
+ }
30
+ const root = document.getElementById("root");
31
+ if (!root) throw new Error("Missing root");
32
+ createRoot(root).render(<App />);