@ulearning/ai-generative-frame 0.0.0-stage → 1.0.0-next.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 (323) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +233 -2
  3. package/dist/assistant-ui/entry.d.ts +34 -0
  4. package/dist/assistant-ui/entry.d.ts.map +1 -0
  5. package/dist/assistant-ui/entry.js +50 -0
  6. package/dist/assistant-ui/entry.js.map +1 -0
  7. package/dist/assistant-ui/extension.d.ts +18 -0
  8. package/dist/assistant-ui/extension.d.ts.map +1 -0
  9. package/dist/assistant-ui/extension.js +0 -0
  10. package/dist/assistant-ui/history.d.ts +15 -0
  11. package/dist/assistant-ui/history.d.ts.map +1 -0
  12. package/dist/assistant-ui/history.js +39 -0
  13. package/dist/assistant-ui/history.js.map +1 -0
  14. package/dist/assistant-ui/modelContext.d.ts +45 -0
  15. package/dist/assistant-ui/modelContext.d.ts.map +1 -0
  16. package/dist/assistant-ui/modelContext.js +88 -0
  17. package/dist/assistant-ui/modelContext.js.map +1 -0
  18. package/dist/assistant-ui/snapshotSaver.d.ts +11 -0
  19. package/dist/assistant-ui/snapshotSaver.d.ts.map +1 -0
  20. package/dist/assistant-ui/snapshotSaver.js +83 -0
  21. package/dist/assistant-ui/snapshotSaver.js.map +1 -0
  22. package/dist/assistant-ui/tool-calls.d.ts +21 -0
  23. package/dist/assistant-ui/tool-calls.d.ts.map +1 -0
  24. package/dist/assistant-ui/tool-calls.js +65 -0
  25. package/dist/assistant-ui/tool-calls.js.map +1 -0
  26. package/dist/assistant-ui/toolkit.d.ts +96 -0
  27. package/dist/assistant-ui/toolkit.d.ts.map +1 -0
  28. package/dist/assistant-ui/toolkit.js +456 -0
  29. package/dist/assistant-ui/toolkit.js.map +1 -0
  30. package/dist/assistant-ui/useSharedContext.d.ts +14 -0
  31. package/dist/assistant-ui/useSharedContext.d.ts.map +1 -0
  32. package/dist/assistant-ui/useSharedContext.js +27 -0
  33. package/dist/assistant-ui/useSharedContext.js.map +1 -0
  34. package/dist/assistant-ui.d.ts +3 -0
  35. package/dist/assistant-ui.d.ts.map +1 -0
  36. package/dist/assistant-ui.js +1 -0
  37. package/dist/bootstrap.d.ts +23 -0
  38. package/dist/bootstrap.d.ts.map +1 -0
  39. package/dist/bootstrap.js +78 -0
  40. package/dist/bootstrap.js.map +1 -0
  41. package/dist/csp.d.ts +25 -0
  42. package/dist/csp.d.ts.map +1 -0
  43. package/dist/csp.js +78 -0
  44. package/dist/csp.js.map +1 -0
  45. package/dist/index.d.ts +6 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +5 -0
  48. package/dist/json-schema.d.ts +70 -0
  49. package/dist/json-schema.d.ts.map +1 -0
  50. package/dist/json-schema.js +163 -0
  51. package/dist/json-schema.js.map +1 -0
  52. package/dist/preview.d.ts +30 -0
  53. package/dist/preview.d.ts.map +1 -0
  54. package/dist/preview.js +60 -0
  55. package/dist/preview.js.map +1 -0
  56. package/dist/prompts/guidance.d.ts +41 -0
  57. package/dist/prompts/guidance.d.ts.map +1 -0
  58. package/dist/prompts/guidance.js +268 -0
  59. package/dist/prompts/guidance.js.map +1 -0
  60. package/dist/prompts.d.ts +2 -0
  61. package/dist/prompts.d.ts.map +1 -0
  62. package/dist/prompts.js +1 -0
  63. package/dist/protocol.d.ts +114 -0
  64. package/dist/protocol.d.ts.map +1 -0
  65. package/dist/protocol.js +49 -0
  66. package/dist/protocol.js.map +1 -0
  67. package/dist/repair/repair.d.ts +72 -0
  68. package/dist/repair/repair.d.ts.map +1 -0
  69. package/dist/repair/repair.js +103 -0
  70. package/dist/repair/repair.js.map +1 -0
  71. package/dist/repair.d.ts +2 -0
  72. package/dist/repair.d.ts.map +1 -0
  73. package/dist/repair.js +1 -0
  74. package/dist/rpc.d.ts +60 -0
  75. package/dist/rpc.d.ts.map +1 -0
  76. package/dist/rpc.js +127 -0
  77. package/dist/rpc.js.map +1 -0
  78. package/dist/runtime/diagnostics.d.ts +17 -0
  79. package/dist/runtime/diagnostics.d.ts.map +1 -0
  80. package/dist/runtime/diagnostics.js +128 -0
  81. package/dist/runtime/diagnostics.js.map +1 -0
  82. package/dist/runtime/generated.d.ts +2 -0
  83. package/dist/runtime/generated.d.ts.map +1 -0
  84. package/dist/runtime/generated.js +5 -0
  85. package/dist/runtime/generated.js.map +1 -0
  86. package/dist/runtime/index.d.ts +7 -0
  87. package/dist/runtime/index.d.ts.map +1 -0
  88. package/dist/runtime/index.js +264 -0
  89. package/dist/runtime/index.js.map +1 -0
  90. package/dist/runtime/morph.d.ts +51 -0
  91. package/dist/runtime/morph.d.ts.map +1 -0
  92. package/dist/runtime/morph.js +251 -0
  93. package/dist/runtime/morph.js.map +1 -0
  94. package/dist/runtime/screenshot.d.ts +23 -0
  95. package/dist/runtime/screenshot.d.ts.map +1 -0
  96. package/dist/runtime/screenshot.js +188 -0
  97. package/dist/runtime/screenshot.js.map +1 -0
  98. package/dist/runtime/size.d.ts +10 -0
  99. package/dist/runtime/size.d.ts.map +1 -0
  100. package/dist/runtime/size.js +21 -0
  101. package/dist/runtime/size.js.map +1 -0
  102. package/dist/runtime/storage.d.ts +4 -0
  103. package/dist/runtime/storage.d.ts.map +1 -0
  104. package/dist/runtime/storage.js +38 -0
  105. package/dist/runtime/storage.js.map +1 -0
  106. package/dist/spec/actions.d.ts +40 -0
  107. package/dist/spec/actions.d.ts.map +1 -0
  108. package/dist/spec/actions.js +53 -0
  109. package/dist/spec/actions.js.map +1 -0
  110. package/dist/spec/assistant-ui/thread.d.ts +66 -0
  111. package/dist/spec/assistant-ui/thread.d.ts.map +1 -0
  112. package/dist/spec/assistant-ui/thread.js +252 -0
  113. package/dist/spec/assistant-ui/thread.js.map +1 -0
  114. package/dist/spec/assistant-ui/toolkit.d.ts +33 -0
  115. package/dist/spec/assistant-ui/toolkit.d.ts.map +1 -0
  116. package/dist/spec/assistant-ui/toolkit.js +142 -0
  117. package/dist/spec/assistant-ui/toolkit.js.map +1 -0
  118. package/dist/spec/assistant-ui/web.d.ts +6 -0
  119. package/dist/spec/assistant-ui/web.d.ts.map +1 -0
  120. package/dist/spec/assistant-ui/web.js +48 -0
  121. package/dist/spec/assistant-ui/web.js.map +1 -0
  122. package/dist/spec/assistant-ui.d.ts +3 -0
  123. package/dist/spec/assistant-ui.d.ts.map +1 -0
  124. package/dist/spec/assistant-ui.js +1 -0
  125. package/dist/spec/calc.d.ts +8 -0
  126. package/dist/spec/calc.d.ts.map +1 -0
  127. package/dist/spec/calc.js +498 -0
  128. package/dist/spec/calc.js.map +1 -0
  129. package/dist/spec/catalog.d.ts +52 -0
  130. package/dist/spec/catalog.d.ts.map +1 -0
  131. package/dist/spec/catalog.js +104 -0
  132. package/dist/spec/catalog.js.map +1 -0
  133. package/dist/spec/expressions.d.ts +45 -0
  134. package/dist/spec/expressions.d.ts.map +1 -0
  135. package/dist/spec/expressions.js +199 -0
  136. package/dist/spec/expressions.js.map +1 -0
  137. package/dist/spec/patch.d.ts +20 -0
  138. package/dist/spec/patch.d.ts.map +1 -0
  139. package/dist/spec/patch.js +77 -0
  140. package/dist/spec/patch.js.map +1 -0
  141. package/dist/spec/pending.d.ts +15 -0
  142. package/dist/spec/pending.d.ts.map +1 -0
  143. package/dist/spec/pending.js +33 -0
  144. package/dist/spec/pending.js.map +1 -0
  145. package/dist/spec/pointer.d.ts +17 -0
  146. package/dist/spec/pointer.d.ts.map +1 -0
  147. package/dist/spec/pointer.js +88 -0
  148. package/dist/spec/pointer.js.map +1 -0
  149. package/dist/spec/prompt.d.ts +29 -0
  150. package/dist/spec/prompt.d.ts.map +1 -0
  151. package/dist/spec/prompt.js +145 -0
  152. package/dist/spec/prompt.js.map +1 -0
  153. package/dist/spec/render-spec.d.ts +50 -0
  154. package/dist/spec/render-spec.d.ts.map +1 -0
  155. package/dist/spec/render-spec.js +104 -0
  156. package/dist/spec/render-spec.js.map +1 -0
  157. package/dist/spec/resolve.d.ts +39 -0
  158. package/dist/spec/resolve.d.ts.map +1 -0
  159. package/dist/spec/resolve.js +51 -0
  160. package/dist/spec/resolve.js.map +1 -0
  161. package/dist/spec/resolved.d.ts +3 -0
  162. package/dist/spec/resolved.d.ts.map +1 -0
  163. package/dist/spec/resolved.js +36 -0
  164. package/dist/spec/resolved.js.map +1 -0
  165. package/dist/spec/state.d.ts +34 -0
  166. package/dist/spec/state.d.ts.map +1 -0
  167. package/dist/spec/state.js +111 -0
  168. package/dist/spec/state.js.map +1 -0
  169. package/dist/spec/stream.d.ts +50 -0
  170. package/dist/spec/stream.d.ts.map +1 -0
  171. package/dist/spec/stream.js +168 -0
  172. package/dist/spec/stream.js.map +1 -0
  173. package/dist/spec/tools.d.ts +3 -0
  174. package/dist/spec/tools.d.ts.map +1 -0
  175. package/dist/spec/tools.js +3 -0
  176. package/dist/spec/tree.d.ts +21 -0
  177. package/dist/spec/tree.d.ts.map +1 -0
  178. package/dist/spec/tree.js +27 -0
  179. package/dist/spec/tree.js.map +1 -0
  180. package/dist/spec/types.d.ts +66 -0
  181. package/dist/spec/types.d.ts.map +1 -0
  182. package/dist/spec/types.js +9 -0
  183. package/dist/spec/types.js.map +1 -0
  184. package/dist/spec/validate.d.ts +32 -0
  185. package/dist/spec/validate.d.ts.map +1 -0
  186. package/dist/spec/validate.js +264 -0
  187. package/dist/spec/validate.js.map +1 -0
  188. package/dist/spec/vue/SpecRenderer.d.ts +43 -0
  189. package/dist/spec/vue/SpecRenderer.d.ts.map +1 -0
  190. package/dist/spec/vue/SpecRenderer.js +357 -0
  191. package/dist/spec/vue/SpecRenderer.js.map +1 -0
  192. package/dist/spec/vue/useSpecStream.d.ts +19 -0
  193. package/dist/spec/vue/useSpecStream.d.ts.map +1 -0
  194. package/dist/spec/vue/useSpecStream.js +115 -0
  195. package/dist/spec/vue/useSpecStream.js.map +1 -0
  196. package/dist/spec/vue.d.ts +3 -0
  197. package/dist/spec/vue.d.ts.map +1 -0
  198. package/dist/spec/vue.js +3 -0
  199. package/dist/spec.d.ts +12 -0
  200. package/dist/spec.d.ts.map +1 -0
  201. package/dist/spec.js +8 -0
  202. package/dist/theme.d.ts +38 -0
  203. package/dist/theme.d.ts.map +1 -0
  204. package/dist/theme.js +370 -0
  205. package/dist/theme.js.map +1 -0
  206. package/dist/tools/define.d.ts +43 -0
  207. package/dist/tools/define.d.ts.map +1 -0
  208. package/dist/tools/define.js +31 -0
  209. package/dist/tools/define.js.map +1 -0
  210. package/dist/tools/edits.d.ts +19 -0
  211. package/dist/tools/edits.d.ts.map +1 -0
  212. package/dist/tools/edits.js +55 -0
  213. package/dist/tools/edits.js.map +1 -0
  214. package/dist/tools/registry.d.ts +19 -0
  215. package/dist/tools/registry.d.ts.map +1 -0
  216. package/dist/tools/registry.js +33 -0
  217. package/dist/tools/registry.js.map +1 -0
  218. package/dist/tools/tools.d.ts +88 -0
  219. package/dist/tools/tools.d.ts.map +1 -0
  220. package/dist/tools/tools.js +235 -0
  221. package/dist/tools/tools.js.map +1 -0
  222. package/dist/tools.d.ts +5 -0
  223. package/dist/tools.d.ts.map +1 -0
  224. package/dist/tools.js +5 -0
  225. package/dist/version.d.ts +2 -0
  226. package/dist/version.d.ts.map +1 -0
  227. package/dist/version.js +5 -0
  228. package/dist/version.js.map +1 -0
  229. package/dist/vue/Widget.d.ts +16 -0
  230. package/dist/vue/Widget.d.ts.map +1 -0
  231. package/dist/vue/Widget.js +58 -0
  232. package/dist/vue/Widget.js.map +1 -0
  233. package/dist/vue/sync.d.ts +19 -0
  234. package/dist/vue/sync.d.ts.map +1 -0
  235. package/dist/vue/sync.js +31 -0
  236. package/dist/vue/sync.js.map +1 -0
  237. package/dist/vue/themeStore.d.ts +23 -0
  238. package/dist/vue/themeStore.d.ts.map +1 -0
  239. package/dist/vue/themeStore.js +115 -0
  240. package/dist/vue/themeStore.js.map +1 -0
  241. package/dist/vue/useThemeTokens.d.ts +6 -0
  242. package/dist/vue/useThemeTokens.d.ts.map +1 -0
  243. package/dist/vue/useThemeTokens.js +49 -0
  244. package/dist/vue/useThemeTokens.js.map +1 -0
  245. package/dist/vue/useWidget.d.ts +10 -0
  246. package/dist/vue/useWidget.d.ts.map +1 -0
  247. package/dist/vue/useWidget.js +81 -0
  248. package/dist/vue/useWidget.js.map +1 -0
  249. package/dist/vue.d.ts +5 -0
  250. package/dist/vue.d.ts.map +1 -0
  251. package/dist/vue.js +4 -0
  252. package/dist/widget.d.ts +129 -0
  253. package/dist/widget.d.ts.map +1 -0
  254. package/dist/widget.js +655 -0
  255. package/dist/widget.js.map +1 -0
  256. package/package.json +125 -3
  257. package/src/assistant-ui/entry.ts +124 -0
  258. package/src/assistant-ui/extension.ts +19 -0
  259. package/src/assistant-ui/history.ts +63 -0
  260. package/src/assistant-ui/modelContext.ts +162 -0
  261. package/src/assistant-ui/snapshotSaver.ts +116 -0
  262. package/src/assistant-ui/tool-calls.ts +115 -0
  263. package/src/assistant-ui/toolkit.tsx +747 -0
  264. package/src/assistant-ui/useSharedContext.ts +40 -0
  265. package/src/assistant-ui.ts +14 -0
  266. package/src/bootstrap.ts +112 -0
  267. package/src/csp.ts +96 -0
  268. package/src/index.ts +44 -0
  269. package/src/json-schema.ts +353 -0
  270. package/src/preview.ts +120 -0
  271. package/src/prompts/guidance.ts +369 -0
  272. package/src/prompts.ts +8 -0
  273. package/src/protocol.ts +141 -0
  274. package/src/repair/repair.ts +202 -0
  275. package/src/repair.ts +11 -0
  276. package/src/rpc.ts +228 -0
  277. package/src/runtime/diagnostics.ts +165 -0
  278. package/src/runtime/generated.ts +2 -0
  279. package/src/runtime/index.ts +343 -0
  280. package/src/runtime/morph.ts +352 -0
  281. package/src/runtime/screenshot.ts +288 -0
  282. package/src/runtime/size.ts +17 -0
  283. package/src/runtime/storage.ts +49 -0
  284. package/src/spec/actions.ts +111 -0
  285. package/src/spec/assistant-ui/thread.ts +381 -0
  286. package/src/spec/assistant-ui/toolkit.tsx +295 -0
  287. package/src/spec/assistant-ui/web.tsx +63 -0
  288. package/src/spec/assistant-ui.ts +2 -0
  289. package/src/spec/calc.ts +598 -0
  290. package/src/spec/catalog.ts +196 -0
  291. package/src/spec/expressions.ts +301 -0
  292. package/src/spec/patch.ts +112 -0
  293. package/src/spec/pending.ts +36 -0
  294. package/src/spec/pointer.ts +170 -0
  295. package/src/spec/prompt.ts +243 -0
  296. package/src/spec/render-spec.ts +144 -0
  297. package/src/spec/resolve.ts +123 -0
  298. package/src/spec/resolved.ts +53 -0
  299. package/src/spec/state.ts +193 -0
  300. package/src/spec/stream.ts +230 -0
  301. package/src/spec/tools.ts +17 -0
  302. package/src/spec/tree.ts +37 -0
  303. package/src/spec/types.ts +51 -0
  304. package/src/spec/validate.ts +429 -0
  305. package/src/spec/vue/SpecRenderer.tsx +544 -0
  306. package/src/spec/vue/__tests__/harness.ts +50 -0
  307. package/src/spec/vue/useSpecStream.ts +162 -0
  308. package/src/spec/vue.ts +13 -0
  309. package/src/spec.ts +55 -0
  310. package/src/theme.ts +423 -0
  311. package/src/tools/define.ts +65 -0
  312. package/src/tools/edits.ts +73 -0
  313. package/src/tools/registry.ts +44 -0
  314. package/src/tools/tools.ts +361 -0
  315. package/src/tools.ts +32 -0
  316. package/src/version.ts +4 -0
  317. package/src/vue/Widget.tsx +74 -0
  318. package/src/vue/sync.ts +36 -0
  319. package/src/vue/themeStore.ts +157 -0
  320. package/src/vue/useThemeTokens.ts +71 -0
  321. package/src/vue/useWidget.ts +117 -0
  322. package/src/vue.ts +8 -0
  323. package/src/widget.ts +1080 -0
@@ -0,0 +1,202 @@
1
+ import type { ConsoleEntry, WidgetError } from "../protocol";
2
+ import { applyWidgetEdits, type WidgetEdit } from "../tools/edits";
3
+
4
+ /** What one render of a widget produced; `previewWidget` returns a compatible shape. */
5
+ export type RenderReport = {
6
+ errors: WidgetError[];
7
+ console: ConsoleEntry[];
8
+ blank: boolean;
9
+ height?: number;
10
+ screenshot?: string;
11
+ };
12
+
13
+ export type RepairFeedback = {
14
+ ok: boolean;
15
+ round: number;
16
+ errors: WidgetError[];
17
+ /** Warnings and errors from the console, most recent last. */
18
+ console: ConsoleEntry[];
19
+ blank: boolean;
20
+ height?: number;
21
+ screenshot?: string;
22
+ /** The feedback as text for a tool result or follow-up message. */
23
+ text: string;
24
+ };
25
+
26
+ export type GenerateContext = {
27
+ round: number;
28
+ /** The code from the previous round; undefined in the first round. */
29
+ previousCode: string | undefined;
30
+ /** Feedback on the previous round; undefined in the first round. */
31
+ feedback: RepairFeedback | undefined;
32
+ };
33
+
34
+ /** Either complete new code or exact replacements on the previous round's code. */
35
+ export type GenerateResult = string | { edits: WidgetEdit[] };
36
+
37
+ export type RepairRound = {
38
+ round: number;
39
+ code: string;
40
+ report: RenderReport;
41
+ feedback: RepairFeedback;
42
+ };
43
+
44
+ export type RepairLoopOptions = {
45
+ generate(context: GenerateContext): Promise<GenerateResult>;
46
+ render(code: string): Promise<RenderReport>;
47
+ /** Rounds including the first generation. Defaults to 3. */
48
+ maxRounds?: number;
49
+ /** Decides whether a render is good enough. Defaults to no errors and not blank. */
50
+ accept?(report: RenderReport): boolean;
51
+ /** Console entries carried into feedback. Defaults to 20. */
52
+ consoleLimit?: number;
53
+ /** Forward the screenshot to `generate`. Defaults to true. */
54
+ includeScreenshot?: boolean;
55
+ };
56
+
57
+ export type RepairLoopResult = {
58
+ ok: boolean;
59
+ code: string;
60
+ report: RenderReport;
61
+ rounds: RepairRound[];
62
+ };
63
+
64
+ const defaultAccept = (report: RenderReport) =>
65
+ report.errors.length === 0 && !report.blank;
66
+
67
+ const describeError = (error: WidgetError) => {
68
+ const location =
69
+ error.line !== undefined
70
+ ? ` (line ${error.line}${error.column !== undefined ? `:${error.column}` : ""})`
71
+ : "";
72
+ const source =
73
+ error.source && error.kind !== "error" ? ` [${error.source}]` : "";
74
+ return `- ${error.kind}: ${error.message}${location}${source}`;
75
+ };
76
+
77
+ /** Turns a render report into structured, model-readable feedback. */
78
+ export function buildRepairFeedback(
79
+ report: RenderReport,
80
+ options: {
81
+ round?: number;
82
+ ok?: boolean;
83
+ consoleLimit?: number;
84
+ includeScreenshot?: boolean;
85
+ } = {},
86
+ ): RepairFeedback {
87
+ const ok = options.ok ?? defaultAccept(report);
88
+ const consoleExcerpt = report.console
89
+ .filter((entry) => entry.level === "warn" || entry.level === "error")
90
+ .slice(-(options.consoleLimit ?? 20));
91
+
92
+ const lines: string[] = [];
93
+ if (ok) {
94
+ lines.push("The widget rendered without errors.");
95
+ } else {
96
+ lines.push("The widget did not render cleanly. Fix the causes below.");
97
+ }
98
+ if (report.errors.length > 0) {
99
+ lines.push(
100
+ "",
101
+ `Errors (${report.errors.length}):`,
102
+ ...report.errors.map(describeError),
103
+ );
104
+ }
105
+ if (report.blank) {
106
+ lines.push(
107
+ "",
108
+ "The widget rendered blank: nothing visible ended up on screen. Check that content is not hidden, that script-drawn areas have an explicit height, and that scripts ran without errors.",
109
+ );
110
+ }
111
+ if (consoleExcerpt.length > 0) {
112
+ lines.push(
113
+ "",
114
+ "Console:",
115
+ ...consoleExcerpt.map((entry) => `- ${entry.level}: ${entry.message}`),
116
+ );
117
+ }
118
+ if (report.height !== undefined) {
119
+ lines.push("", `Rendered height: ${report.height}px.`);
120
+ }
121
+
122
+ const includeScreenshot = options.includeScreenshot ?? true;
123
+ return {
124
+ ok,
125
+ round: options.round ?? 1,
126
+ errors: report.errors,
127
+ console: consoleExcerpt,
128
+ blank: report.blank,
129
+ ...(report.height !== undefined ? { height: report.height } : {}),
130
+ ...(includeScreenshot && report.screenshot
131
+ ? { screenshot: report.screenshot }
132
+ : {}),
133
+ text: lines.join("\n"),
134
+ };
135
+ }
136
+
137
+ /**
138
+ * Generates a widget, renders it, and feeds structured feedback back into
139
+ * generation until a render is accepted or the round budget runs out. Returns
140
+ * the best attempt: the last accepted one, otherwise the one with the fewest
141
+ * errors.
142
+ */
143
+ export async function repairLoop(
144
+ options: RepairLoopOptions,
145
+ ): Promise<RepairLoopResult> {
146
+ const maxRounds = Math.max(1, options.maxRounds ?? 3);
147
+ const accept = options.accept ?? defaultAccept;
148
+ const rounds: RepairRound[] = [];
149
+ let previousCode: string | undefined;
150
+ let feedback: RepairFeedback | undefined;
151
+
152
+ for (let round = 1; round <= maxRounds; round++) {
153
+ const generated = await options.generate({ round, previousCode, feedback });
154
+ let code: string;
155
+ let report: RenderReport;
156
+ if (typeof generated === "string") {
157
+ code = generated;
158
+ report = await options.render(code);
159
+ } else {
160
+ const applied = applyWidgetEdits(previousCode ?? "", generated.edits);
161
+ if (applied.ok) {
162
+ code = applied.code;
163
+ report = await options.render(code);
164
+ } else {
165
+ code = previousCode ?? "";
166
+ report = {
167
+ errors: [
168
+ {
169
+ kind: "error",
170
+ message: `Edits were not applied: ${applied.error}`,
171
+ },
172
+ ],
173
+ console: [],
174
+ blank: false,
175
+ };
176
+ }
177
+ }
178
+
179
+ const ok = accept(report);
180
+ feedback = buildRepairFeedback(report, {
181
+ round,
182
+ ok,
183
+ ...(options.consoleLimit !== undefined
184
+ ? { consoleLimit: options.consoleLimit }
185
+ : {}),
186
+ ...(options.includeScreenshot !== undefined
187
+ ? { includeScreenshot: options.includeScreenshot }
188
+ : {}),
189
+ });
190
+ rounds.push({ round, code, report, feedback });
191
+ previousCode = code;
192
+ if (ok) return { ok: true, code, report, rounds };
193
+ }
194
+
195
+ const best = rounds.reduce((a, b) =>
196
+ b.report.errors.length + (b.report.blank ? 1 : 0) <
197
+ a.report.errors.length + (a.report.blank ? 1 : 0)
198
+ ? b
199
+ : a,
200
+ );
201
+ return { ok: false, code: best.code, report: best.report, rounds };
202
+ }
package/src/repair.ts ADDED
@@ -0,0 +1,11 @@
1
+ export {
2
+ repairLoop,
3
+ buildRepairFeedback,
4
+ type GenerateContext,
5
+ type GenerateResult,
6
+ type RenderReport,
7
+ type RepairFeedback,
8
+ type RepairLoopOptions,
9
+ type RepairLoopResult,
10
+ type RepairRound,
11
+ } from "./repair/repair";
package/src/rpc.ts ADDED
@@ -0,0 +1,228 @@
1
+ export type JsonRpcId = string | number;
2
+
3
+ export type JsonRpcRequest = {
4
+ jsonrpc: "2.0";
5
+ id: JsonRpcId;
6
+ method: string;
7
+ params?: unknown;
8
+ };
9
+
10
+ export type JsonRpcNotification = {
11
+ jsonrpc: "2.0";
12
+ method: string;
13
+ params?: unknown;
14
+ };
15
+
16
+ export type JsonRpcResponse = {
17
+ jsonrpc: "2.0";
18
+ id: JsonRpcId;
19
+ result?: unknown;
20
+ error?: { code: number; message: string; data?: unknown };
21
+ };
22
+
23
+ export type JsonRpcMessage =
24
+ | JsonRpcRequest
25
+ | JsonRpcNotification
26
+ | JsonRpcResponse;
27
+
28
+ export const RPC_ERROR = {
29
+ parseError: -32700,
30
+ invalidRequest: -32600,
31
+ methodNotFound: -32601,
32
+ invalidParams: -32602,
33
+ internalError: -32603,
34
+ } as const;
35
+
36
+ export class RpcError extends Error {
37
+ readonly code: number;
38
+ readonly data: unknown;
39
+
40
+ constructor(code: number, message: string, data?: unknown) {
41
+ super(message);
42
+ this.name = "RpcError";
43
+ this.code = code;
44
+ this.data = data;
45
+ }
46
+ }
47
+
48
+ /** The subset of `MessagePort` the peer uses. */
49
+ export type RpcEndpoint = {
50
+ postMessage(message: unknown): void;
51
+ addEventListener(
52
+ type: "message",
53
+ listener: (event: MessageEvent) => void,
54
+ ): void;
55
+ removeEventListener(
56
+ type: "message",
57
+ listener: (event: MessageEvent) => void,
58
+ ): void;
59
+ start?(): void;
60
+ };
61
+
62
+ export type RpcHandlers = {
63
+ /** Resolves to the result; throw an `RpcError` to answer with a specific code. */
64
+ onRequest?: (method: string, params: unknown) => unknown;
65
+ onNotification?: (method: string, params: unknown) => void;
66
+ };
67
+
68
+ export type RpcPeer = {
69
+ request(
70
+ method: string,
71
+ params?: unknown,
72
+ options?: { timeoutMs?: number },
73
+ ): Promise<unknown>;
74
+ notify(method: string, params?: unknown): void;
75
+ /** Feeds a message received outside the endpoint, such as a window message. */
76
+ receive(message: unknown, reply?: (message: JsonRpcMessage) => void): void;
77
+ dispose(): void;
78
+ };
79
+
80
+ const isRecord = (value: unknown): value is Record<string, unknown> =>
81
+ typeof value === "object" && value !== null;
82
+
83
+ export function isJsonRpcMessage(value: unknown): value is JsonRpcMessage {
84
+ return isRecord(value) && value["jsonrpc"] === "2.0";
85
+ }
86
+
87
+ const errorPayload = (error: unknown) => {
88
+ if (error instanceof RpcError) {
89
+ return {
90
+ code: error.code,
91
+ message: error.message,
92
+ ...(error.data !== undefined ? { data: error.data } : {}),
93
+ };
94
+ }
95
+ return {
96
+ code: RPC_ERROR.internalError,
97
+ message: error instanceof Error ? error.message : String(error),
98
+ };
99
+ };
100
+
101
+ /** A symmetric JSON-RPC 2.0 peer: both sides can send requests and notifications. */
102
+ export function createRpcPeer(
103
+ endpoint: RpcEndpoint,
104
+ handlers: RpcHandlers = {},
105
+ ): RpcPeer {
106
+ let nextId = 1;
107
+ let disposed = false;
108
+ const pending = new Map<
109
+ JsonRpcId,
110
+ {
111
+ resolve: (value: unknown) => void;
112
+ reject: (error: unknown) => void;
113
+ timer: ReturnType<typeof setTimeout> | undefined;
114
+ }
115
+ >();
116
+
117
+ const post = (message: JsonRpcMessage) => {
118
+ if (disposed) return;
119
+ endpoint.postMessage(message);
120
+ };
121
+
122
+ const handleRequest = async (
123
+ request: JsonRpcRequest,
124
+ reply: (message: JsonRpcMessage) => void,
125
+ ) => {
126
+ try {
127
+ if (!handlers.onRequest) {
128
+ throw new RpcError(
129
+ RPC_ERROR.methodNotFound,
130
+ `Unknown method: ${request.method}`,
131
+ );
132
+ }
133
+ const result = await handlers.onRequest(request.method, request.params);
134
+ reply({ jsonrpc: "2.0", id: request.id, result: result ?? null });
135
+ } catch (error) {
136
+ reply({ jsonrpc: "2.0", id: request.id, error: errorPayload(error) });
137
+ }
138
+ };
139
+
140
+ const receive = (
141
+ message: unknown,
142
+ reply: (message: JsonRpcMessage) => void = post,
143
+ ) => {
144
+ if (disposed || !isJsonRpcMessage(message)) return;
145
+ const hasId =
146
+ "id" in message &&
147
+ (typeof message.id === "string" || typeof message.id === "number");
148
+
149
+ if ("method" in message && typeof message.method === "string") {
150
+ if (hasId) {
151
+ void handleRequest(message as JsonRpcRequest, reply);
152
+ } else {
153
+ try {
154
+ handlers.onNotification?.(message.method, message.params);
155
+ } catch {
156
+ // A failing notification handler has nobody to answer to.
157
+ }
158
+ }
159
+ return;
160
+ }
161
+
162
+ if (!hasId) return;
163
+ const response = message as JsonRpcResponse;
164
+ const entry = pending.get(response.id);
165
+ if (!entry) return;
166
+ pending.delete(response.id);
167
+ if (entry.timer !== undefined) clearTimeout(entry.timer);
168
+ if (response.error) {
169
+ entry.reject(
170
+ new RpcError(
171
+ response.error.code,
172
+ response.error.message,
173
+ response.error.data,
174
+ ),
175
+ );
176
+ } else {
177
+ entry.resolve(response.result);
178
+ }
179
+ };
180
+
181
+ const onMessage = (event: MessageEvent) => receive(event.data);
182
+ endpoint.addEventListener("message", onMessage);
183
+ endpoint.start?.();
184
+
185
+ return {
186
+ request(method, params, options) {
187
+ if (disposed) {
188
+ return Promise.reject(new Error("RPC peer is disposed"));
189
+ }
190
+ const id = nextId++;
191
+ return new Promise((resolve, reject) => {
192
+ const timeoutMs = options?.timeoutMs;
193
+ const timer =
194
+ timeoutMs === undefined
195
+ ? undefined
196
+ : setTimeout(() => {
197
+ pending.delete(id);
198
+ reject(new Error(`${method} timed out after ${timeoutMs}ms`));
199
+ }, timeoutMs);
200
+ pending.set(id, { resolve, reject, timer });
201
+ post({
202
+ jsonrpc: "2.0",
203
+ id,
204
+ method,
205
+ ...(params !== undefined ? { params } : {}),
206
+ });
207
+ });
208
+ },
209
+ notify(method, params) {
210
+ post({
211
+ jsonrpc: "2.0",
212
+ method,
213
+ ...(params !== undefined ? { params } : {}),
214
+ });
215
+ },
216
+ receive,
217
+ dispose() {
218
+ if (disposed) return;
219
+ disposed = true;
220
+ endpoint.removeEventListener("message", onMessage);
221
+ for (const entry of pending.values()) {
222
+ if (entry.timer !== undefined) clearTimeout(entry.timer);
223
+ entry.reject(new Error("RPC peer is disposed"));
224
+ }
225
+ pending.clear();
226
+ },
227
+ };
228
+ }
@@ -0,0 +1,165 @@
1
+ import type { ConsoleEntry, ConsoleLevel, WidgetError } from "../protocol";
2
+
3
+ const MAX_CONSOLE = 200;
4
+ const MAX_ERRORS = 50;
5
+ const MAX_MESSAGE = 1000;
6
+
7
+ const LEVELS: readonly ConsoleLevel[] = [
8
+ "log",
9
+ "info",
10
+ "warn",
11
+ "error",
12
+ "debug",
13
+ ];
14
+
15
+ const truncate = (text: string) =>
16
+ text.length > MAX_MESSAGE ? `${text.slice(0, MAX_MESSAGE)}…` : text;
17
+
18
+ export const formatValue = (value: unknown): string => {
19
+ if (typeof value === "string") return value;
20
+ if (value instanceof Error)
21
+ return value.stack ?? `${value.name}: ${value.message}`;
22
+ if (typeof value === "object" && value !== null) {
23
+ if (typeof Node !== "undefined" && value instanceof Node) {
24
+ return `<${value.nodeName.toLowerCase()}>`;
25
+ }
26
+ try {
27
+ return JSON.stringify(value);
28
+ } catch {
29
+ return String(value);
30
+ }
31
+ }
32
+ return String(value);
33
+ };
34
+
35
+ export type Diagnostics = {
36
+ readonly errors: WidgetError[];
37
+ readonly console: ConsoleEntry[];
38
+ record(error: WidgetError): void;
39
+ dispose(): void;
40
+ };
41
+
42
+ export type DiagnosticsOptions = {
43
+ onError?: (error: WidgetError) => void;
44
+ onConsole?: (entry: ConsoleEntry) => void;
45
+ };
46
+
47
+ /** Captures errors, console output, failed resources, and CSP violations in a window. */
48
+ export function installDiagnostics(
49
+ win: Window & typeof globalThis,
50
+ options: DiagnosticsOptions = {},
51
+ ): Diagnostics {
52
+ const errors: WidgetError[] = [];
53
+ const consoleEntries: ConsoleEntry[] = [];
54
+
55
+ const record = (error: WidgetError) => {
56
+ const normalized: WidgetError = {
57
+ ...error,
58
+ message: truncate(error.message),
59
+ };
60
+ if (errors.length < MAX_ERRORS) errors.push(normalized);
61
+ options.onError?.(normalized);
62
+ };
63
+
64
+ const onError = (event: ErrorEvent | Event) => {
65
+ const target = event.target;
66
+ if (target && target !== win && target instanceof win.Element) {
67
+ const src =
68
+ target.getAttribute("src") ?? target.getAttribute("href") ?? undefined;
69
+ record({
70
+ kind: "resource",
71
+ message: `Failed to load <${target.localName}>${src ? ` ${src}` : ""}`,
72
+ ...(src ? { source: src } : {}),
73
+ });
74
+ return;
75
+ }
76
+ const e = event as ErrorEvent;
77
+ const stack = e.error instanceof Error ? e.error.stack : undefined;
78
+ record({
79
+ kind: "error",
80
+ message: e.message || formatValue(e.error),
81
+ ...(e.filename ? { source: e.filename } : {}),
82
+ ...(e.lineno ? { line: e.lineno } : {}),
83
+ ...(e.colno ? { column: e.colno } : {}),
84
+ ...(stack ? { stack: truncate(stack) } : {}),
85
+ });
86
+ };
87
+
88
+ const onRejection = (event: PromiseRejectionEvent) => {
89
+ const reason: unknown = event.reason;
90
+ const stack = reason instanceof Error ? reason.stack : undefined;
91
+ record({
92
+ kind: "unhandledrejection",
93
+ message: reason instanceof Error ? reason.message : formatValue(reason),
94
+ ...(stack ? { stack: truncate(stack) } : {}),
95
+ });
96
+ };
97
+
98
+ const onViolation = (event: SecurityPolicyViolationEvent) => {
99
+ record({
100
+ kind: "csp",
101
+ message: `Blocked by Content Security Policy (${event.effectiveDirective}): ${event.blockedURI || "inline"}`,
102
+ ...(event.blockedURI ? { source: event.blockedURI } : {}),
103
+ ...(event.lineNumber ? { line: event.lineNumber } : {}),
104
+ });
105
+ };
106
+
107
+ win.addEventListener("error", onError, true);
108
+ win.addEventListener("unhandledrejection", onRejection);
109
+ win.document.addEventListener("securitypolicyviolation", onViolation);
110
+
111
+ const originals = new Map<ConsoleLevel, (...args: unknown[]) => void>();
112
+ for (const level of LEVELS) {
113
+ const original = win.console[level];
114
+ originals.set(level, original);
115
+ win.console[level] = (...args: unknown[]) => {
116
+ const entry: ConsoleEntry = {
117
+ level,
118
+ message: truncate(args.map(formatValue).join(" ")),
119
+ };
120
+ consoleEntries.push(entry);
121
+ if (consoleEntries.length > MAX_CONSOLE) consoleEntries.shift();
122
+ options.onConsole?.(entry);
123
+ original.apply(win.console, args);
124
+ };
125
+ }
126
+
127
+ return {
128
+ errors,
129
+ console: consoleEntries,
130
+ record,
131
+ dispose() {
132
+ win.removeEventListener("error", onError, true);
133
+ win.removeEventListener("unhandledrejection", onRejection);
134
+ win.document.removeEventListener("securitypolicyviolation", onViolation);
135
+ for (const [level, original] of originals) win.console[level] = original;
136
+ },
137
+ };
138
+ }
139
+
140
+ const VISUAL_SELECTOR =
141
+ "svg, canvas, img, video, picture, iframe, input, button, select, textarea, progress, meter";
142
+
143
+ /** True when the widget rendered nothing a person could see. */
144
+ export function isBlankRender(root: HTMLElement): boolean {
145
+ const rect = root.getBoundingClientRect();
146
+ if (rect.height < 2 || rect.width < 2) return true;
147
+ if ((root.textContent ?? "").trim().length > 0) return false;
148
+ for (const el of Array.from(root.querySelectorAll(VISUAL_SELECTOR))) {
149
+ const r = el.getBoundingClientRect();
150
+ if (r.width >= 2 && r.height >= 2) return false;
151
+ }
152
+ for (const el of Array.from(root.querySelectorAll("*"))) {
153
+ const style = root.ownerDocument.defaultView?.getComputedStyle(el);
154
+ if (!style) continue;
155
+ const painted =
156
+ (style.backgroundColor !== "rgba(0, 0, 0, 0)" &&
157
+ style.backgroundColor !== "transparent") ||
158
+ style.backgroundImage !== "none" ||
159
+ parseFloat(style.borderTopWidth) > 0;
160
+ if (!painted) continue;
161
+ const r = el.getBoundingClientRect();
162
+ if (r.width >= 2 && r.height >= 2) return false;
163
+ }
164
+ return true;
165
+ }
@@ -0,0 +1,2 @@
1
+ // Generated by scripts/build-runtime.mjs from src/runtime. Do not edit by hand.
2
+ export const runtimeSource: string = "(function(){let e={write:`genframe/write`,end:`genframe/end`,replace:`genframe/replace`,screenshot:`genframe/screenshot`,inspect:`genframe/inspect`,clearStorage:`genframe/clear-storage`,hostContextChanged:`ui/notifications/host-context-changed`,toolInputPartial:`ui/notifications/tool-input-partial`,toolInput:`ui/notifications/tool-input`,toolResult:`ui/notifications/tool-result`,ready:`genframe/ready`,initialize:`ui/initialize`,initialized:`ui/notifications/initialized`,sizeChanged:`ui/notifications/size-changed`,openLink:`ui/open-link`,message:`ui/message`,callTool:`tools/call`,requestDisplayMode:`ui/request-display-mode`,updateModelContext:`ui/update-model-context`,copyText:`genframe/copy-text`,widgetState:`genframe/widget-state`,log:`genframe/log`,error:`genframe/error`},t=/^<svg[\\s>/]/i;function n(e){let n=e.trimStart();if(n.startsWith(`<?xml`)){let e=n.indexOf(`>`);if(e===-1)return`html`;n=n.slice(e+1).trimStart()}for(;n.startsWith(`<!--`);){let e=n.indexOf(`-->`,4);if(e===-1)return`html`;n=n.slice(e+3).trimStart()}return t.test(n)?`svg`:`html`}let r={parseError:-32700,invalidRequest:-32600,methodNotFound:-32601,invalidParams:-32602,internalError:-32603};var i=class extends Error{code;data;constructor(e,t,n){super(t),this.name=`RpcError`,this.code=e,this.data=n}};let a=e=>typeof e==`object`&&!!e;function o(e){return a(e)&&e.jsonrpc===`2.0`}let s=e=>e instanceof i?{code:e.code,message:e.message,...e.data===void 0?{}:{data:e.data}}:{code:r.internalError,message:e instanceof Error?e.message:String(e)};function c(e,t={}){let n=1,a=!1,c=/* @__PURE__ */ new Map,l=t=>{a||e.postMessage(t)},u=async(e,n)=>{try{if(!t.onRequest)throw new i(r.methodNotFound,`Unknown method: ${e.method}`);let a=await t.onRequest(e.method,e.params);n({jsonrpc:`2.0`,id:e.id,result:a??null})}catch(t){n({jsonrpc:`2.0`,id:e.id,error:s(t)})}},d=(e,n=l)=>{if(a||!o(e))return;let r=`id`in e&&(typeof e.id==`string`||typeof e.id==`number`);if(`method`in e&&typeof e.method==`string`){if(r)u(e,n);else try{t.onNotification?.(e.method,e.params)}catch{}return}if(!r)return;let s=e,d=c.get(s.id);d&&(c.delete(s.id),d.timer!==void 0&&clearTimeout(d.timer),s.error?d.reject(new i(s.error.code,s.error.message,s.error.data)):d.resolve(s.result))},f=e=>d(e.data);return e.addEventListener(`message`,f),e.start?.(),{request(e,t,r){if(a)return Promise.reject(/* @__PURE__ */ Error(`RPC peer is disposed`));let i=n++;return new Promise((n,a)=>{let o=r?.timeoutMs,s=o===void 0?void 0:setTimeout(()=>{c.delete(i),a(/* @__PURE__ */ Error(`${e} timed out after ${o}ms`))},o);c.set(i,{resolve:n,reject:a,timer:s}),l({jsonrpc:`2.0`,id:i,method:e,...t===void 0?{}:{params:t}})})},notify(e,t){l({jsonrpc:`2.0`,method:e,...t===void 0?{}:{params:t}})},receive:d,dispose(){if(!a){a=!0,e.removeEventListener(`message`,f);for(let e of c.values())e.timer!==void 0&&clearTimeout(e.timer),e.reject(/* @__PURE__ */ Error(`RPC peer is disposed`));c.clear()}}}}let l=`1.0.0-next.0`,u=1e3,d=[`log`,`info`,`warn`,`error`,`debug`],f=e=>e.length>u?`${e.slice(0,u)}…`:e,p=e=>{if(typeof e==`string`)return e;if(e instanceof Error)return e.stack??`${e.name}: ${e.message}`;if(typeof e==`object`&&e){if(typeof Node<`u`&&e instanceof Node)return`<${e.nodeName.toLowerCase()}>`;try{return JSON.stringify(e)}catch{return String(e)}}return String(e)};function m(e,t={}){let n=[],r=[],i=e=>{let r={...e,message:f(e.message)};n.length<50&&n.push(r),t.onError?.(r)},a=t=>{let n=t.target;if(n&&n!==e&&n instanceof e.Element){let e=n.getAttribute(`src`)??n.getAttribute(`href`)??void 0;i({kind:`resource`,message:`Failed to load <${n.localName}>${e?` ${e}`:``}`,...e?{source:e}:{}});return}let r=t,a=r.error instanceof Error?r.error.stack:void 0;i({kind:`error`,message:r.message||p(r.error),...r.filename?{source:r.filename}:{},...r.lineno?{line:r.lineno}:{},...r.colno?{column:r.colno}:{},...a?{stack:f(a)}:{}})},o=e=>{let t=e.reason,n=t instanceof Error?t.stack:void 0;i({kind:`unhandledrejection`,message:t instanceof Error?t.message:p(t),...n?{stack:f(n)}:{}})},s=e=>{i({kind:`csp`,message:`Blocked by Content Security Policy (${e.effectiveDirective}): ${e.blockedURI||`inline`}`,...e.blockedURI?{source:e.blockedURI}:{},...e.lineNumber?{line:e.lineNumber}:{}})};e.addEventListener(`error`,a,!0),e.addEventListener(`unhandledrejection`,o),e.document.addEventListener(`securitypolicyviolation`,s);let c=/* @__PURE__ */ new Map;for(let n of d){let i=e.console[n];c.set(n,i),e.console[n]=(...a)=>{let o={level:n,message:f(a.map(p).join(` `))};r.push(o),r.length>200&&r.shift(),t.onConsole?.(o),i.apply(e.console,a)}}return{errors:n,console:r,record:i,dispose(){e.removeEventListener(`error`,a,!0),e.removeEventListener(`unhandledrejection`,o),e.document.removeEventListener(`securitypolicyviolation`,s);for(let[t,n]of c)e.console[t]=n}}}function h(e){let t=e.getBoundingClientRect();if(t.height<2||t.width<2)return!0;if((e.textContent??``).trim().length>0)return!1;for(let t of Array.from(e.querySelectorAll(`svg, canvas, img, video, picture, iframe, input, button, select, textarea, progress, meter`))){let e=t.getBoundingClientRect();if(e.width>=2&&e.height>=2)return!1}for(let t of Array.from(e.querySelectorAll(`*`))){let n=e.ownerDocument.defaultView?.getComputedStyle(t);if(!n||!(n.backgroundColor!==`rgba(0, 0, 0, 0)`&&n.backgroundColor!==`transparent`||n.backgroundImage!==`none`||parseFloat(n.borderTopWidth)>0))continue;let r=t.getBoundingClientRect();if(r.width>=2&&r.height>=2)return!1}return!0}let g=/* @__PURE__ */ new Set([`style`,`script`,`meta`,`link`,`title`,`base`]);function _(e){let t=e.toLowerCase().lastIndexOf(`<style`);if(t===-1)return e;let n=e.charAt(t+6);return n!==``&&!/[\\s>/]/.test(n)||e.toLowerCase().includes(`</style`,t)?e:e.slice(0,t)}function v(e,t){let n=e.createElement(`template`);n.innerHTML=t;let r=n.content,i=[];for(let t of Array.from(r.querySelectorAll(`script`)))t.replaceWith(e.createComment(`gf-script`)),i.push(t);return{fragment:r,scripts:i}}let y=(e,t)=>{if(e.nodeType!==t.nodeType)return!1;if(e.nodeType!==Node.ELEMENT_NODE)return!0;let n=e,r=t;return n.localName===r.localName&&n.namespaceURI===r.namespaceURI&&n.getAttribute(`id`)===r.getAttribute(`id`)},b=(e,t)=>{for(let n of Array.from(t.attributes))if(e.getAttributeNS(n.namespaceURI,n.localName)!==n.value)try{n.namespaceURI?e.setAttributeNS(n.namespaceURI,n.name,n.value):e.setAttribute(n.name,n.value)}catch{}for(let n of Array.from(e.attributes))t.hasAttributeNS(n.namespaceURI,n.localName)||e.removeAttributeNS(n.namespaceURI,n.localName)},x=(e,t,n)=>{if(e.nodeType!==Node.ELEMENT_NODE){e.nodeValue!==t.nodeValue&&(e.nodeValue=t.nodeValue);return}let r=e,i=t;b(r,i),r instanceof HTMLTemplateElement&&i instanceof HTMLTemplateElement?r.innerHTML!==i.innerHTML&&r.content.replaceChildren(i.content):S(r,i,n)};function S(e,t,n){let r=Array.from(t.childNodes),i=e.firstChild;for(let t=0;t<r.length;t++){let a=r[t];if(!i){e.appendChild(a),n.onAdded(a);continue}if(y(i,a)){x(i,a,n),i=i.nextSibling;continue}let o=r[t+1];if(o&&y(i,o)){e.insertBefore(a,i),n.onAdded(a);continue}let s=i.nextSibling;s&&y(s,a)?(i.remove(),x(s,a,n),i=s.nextSibling):(e.replaceChild(a,i),n.onAdded(a),i=s)}for(;i;){let e=i.nextSibling;i.remove(),i=e}}let C=e=>{let t=!1,n=()=>{t||(t=!0,cancelAnimationFrame(r),clearTimeout(i),e())},r=requestAnimationFrame(n),i=setTimeout(n,50);return()=>{t=!0,cancelAnimationFrame(r),clearTimeout(i)}},w=(e=()=>{})=>(t,n)=>{let r=t.ownerDocument.createElementNS(n.namespaceURI??`http://www.w3.org/1999/xhtml`,`script`);for(let e of Array.from(n.attributes))try{r.setAttribute(e.name,e.value)}catch{}r.textContent=n.textContent;let i=r.getAttribute(`src`)??r.getAttribute(`href`);return i?new Promise(n=>{let a=setTimeout(()=>{e(`Script timed out: ${i}`,i),n()},15e3);r.addEventListener(`load`,()=>{clearTimeout(a),n()}),r.addEventListener(`error`,()=>{clearTimeout(a),e(`Script failed to load: ${i}`,i),n()}),t.replaceWith(r)}):(t.replaceWith(r),Promise.resolve())},T=e=>{if(e.nodeType!==Node.ELEMENT_NODE)return;let t=e;g.has(t.localName)||typeof t.animate!=`function`||t.animate([{opacity:0},{opacity:1}],{duration:200,easing:`ease-out`})};function E(e){let{root:t}=e,r=t.ownerDocument,i=e.schedule??C,a=e.runScript??w(e.onScriptError),o=``,s=`html`,c=!1,l=!1,u,d,f=[],p={onAdded(t){e.animate?.()&&T(t)}},m=i=>{u?.(),u=void 0;let a=i?o:_(o);if(a===d)return;d=a,s=n(o),t.dataset.kind=s;let c=v(r,a);f=c.scripts,S(t,c.fragment,p),e.onRender?.(s)},h=async()=>{let e=r.createTreeWalker(t,NodeFilter.SHOW_COMMENT),n=[];for(let t=e.nextNode();t;t=e.nextNode())t.nodeValue===`gf-script`&&n.push(t);let i=f;for(let e=0;e<n.length&&e<i.length;e++)await a(n[e],i[e]);i.length>0&&!l&&(l=!0,r.dispatchEvent(new Event(`DOMContentLoaded`,{bubbles:!0})),r.defaultView?.dispatchEvent(new Event(`load`)))};return{get source(){return o},get kind(){return s},get ended(){return c},get scriptsRan(){return l},write(e){if(c)throw Error(`Cannot write after end; use replace`);e&&(o+=e,u??=i(()=>{u=void 0,m(!1)}))},async end(){c||(c=!0,m(!0),await h())},async replace(e){c=!0,o=e,m(!0),await h()},flush(){u&&m(c)}}}let D=`http://www.w3.org/1999/xhtml`,O=`http://www.w3.org/2000/svg`,k=4096,A=/* @__PURE__ */ new Set([`script`,`noscript`,`template`]),j=e=>{let t=/* @__PURE__ */ new Map,n,r,i,a=()=>{if(n)return;i=e.createElement(`div`),i.setAttribute(`aria-hidden`,`true`),i.style.cssText=`all:initial;position:absolute;left:-99999px;top:0;visibility:hidden;contain:strict;width:0;height:0;`;let t=i.attachShadow({mode:`closed`});n=e.createElement(`div`),r=e.createElementNS(O,`svg`),n.appendChild(r),t.appendChild(n),e.body.appendChild(i)};return{get(i){let o=`${i.namespaceURI}|${i.localName}`,s=t.get(o);if(s)return s;a();let c=e.createElementNS(i.namespaceURI??D,i.localName);(i.namespaceURI===O?r:n).appendChild(c);let l=e.defaultView.getComputedStyle(c);s=/* @__PURE__ */ new Map;for(let e=0;e<l.length;e++){let t=l.item(e);s.set(t,l.getPropertyValue(t))}return c.remove(),t.set(o,s),s},dispose(){i?.remove()}}},M=(e,t,n)=>{let r=document.createElement(`canvas`);return r.width=Math.max(1,t),r.height=Math.max(1,n),r.getContext(`2d`).drawImage(e,0,0,t,n),r.toDataURL(`image/png`)},N=(e,t,n)=>{if(e.nodeType===Node.TEXT_NODE)return e.cloneNode();if(e.nodeType!==Node.ELEMENT_NODE)return null;let r=e;if(A.has(r.localName))return null;let i=r.ownerDocument.defaultView,a=i.getComputedStyle(r),o;if(r instanceof i.HTMLCanvasElement){o=r.ownerDocument.createElement(`img`);try{o.setAttribute(`src`,r.toDataURL(`image/png`))}catch{}}else if(r instanceof i.HTMLImageElement&&r.complete&&r.naturalWidth>0&&!r.src.startsWith(`data:`)){o=r.cloneNode(!1);try{o.setAttribute(`src`,M(r,r.naturalWidth,r.naturalHeight))}catch{}}else o=r.cloneNode(!1);r instanceof i.HTMLInputElement?r.type===`checkbox`||r.type===`radio`?r.checked?o.setAttribute(`checked`,``):o.removeAttribute(`checked`):o.setAttribute(`value`,r.value):r instanceof i.HTMLTextAreaElement?o.textContent=r.value:r instanceof i.HTMLSelectElement&&o.setAttribute(`data-gf-value`,r.value);let s=t.get(r),c=[];for(let e=0;e<a.length;e++){let t=a.item(e);if(t.startsWith(`--`))continue;let r=a.getPropertyValue(t);(r!==s.get(t)||n&&r!==n.getPropertyValue(t))&&c.push(`${t}:${r}`)}if((a.animationName!==`none`||a.transitionProperty!==`none`)&&c.push(`animation:none`,`transition:none`),o.setAttribute(`style`,c.join(`;`)),!(r instanceof i.HTMLTextAreaElement))for(let e of Array.from(r.childNodes)){let n=N(e,t,a);n&&o.appendChild(n)}if(r instanceof i.HTMLSelectElement)for(let e of Array.from(o.querySelectorAll(`option`)))(e.getAttribute(`value`)===r.value||e.textContent===r.value)&&e.setAttribute(`selected`,``);return o},P=e=>{let t=[];for(let n of Array.from(e.styleSheets)){let e;try{e=n.cssRules}catch{continue}for(let n of Array.from(e))n.constructor.name===`CSSFontFaceRule`&&!/url\\((?!\\s*[\"']?data:)/i.test(n.cssText)&&t.push(n.cssText)}return t.join(`\n`)},F=e=>{let t=e.getBoundingClientRect(),n=Math.max(1,Math.ceil(t.width));return{layoutWidth:n,width:Math.max(n,Math.ceil(e.scrollWidth)),height:Math.max(1,Math.ceil(Math.max(t.height,e.scrollHeight)))}},I=e=>new Promise((t,n)=>{let r=new Image;r.onload=()=>t(r),r.onerror=()=>n(/* @__PURE__ */ Error(`The serialized widget failed to rasterize`)),r.src=e});async function L(e,t={}){let n=e.ownerDocument,r=n.defaultView;await n.fonts?.ready;let{layoutWidth:i,width:a,height:o}=F(e),s=j(n),c;try{c=N(e,s,r.getComputedStyle(n.body))}finally{s.dispose()}let l=n.createElementNS(D,`div`);l.setAttribute(`style`,`width:${a}px;height:${o}px;overflow:hidden;`),c&&l.appendChild(c),c instanceof Element&&c.setAttribute(`style`,`${c.getAttribute(`style`)??``};width:${i}px;margin:0;`);let u=P(n),d=new XMLSerializer().serializeToString(l),f=`<svg xmlns=\"${O}\" width=\"${a}\" height=\"${o}\" viewBox=\"0 0 ${a} ${o}\"><foreignObject x=\"0\" y=\"0\" width=\"100%\" height=\"100%\">`+(u?`<style xmlns=\"${D}\">${u.replace(/</g,`\\\\3c `)}</style>`:``)+d+`</foreignObject></svg>`,p=await I(`data:image/svg+xml;charset=utf-8,${encodeURIComponent(f)}`),m=t.scale??Math.min(r.devicePixelRatio||1,2),h=Math.max(.1,Math.min(m,k/a,k/o)),g=n.createElement(`canvas`);g.width=Math.round(a*h),g.height=Math.round(o*h);let _=g.getContext(`2d`);if(!_)throw Error(`Canvas 2D context is unavailable`);let v=t.background??r.getComputedStyle(n.documentElement).getPropertyValue(`--color-background`).trim();return v&&(_.fillStyle=v,_.fillRect(0,0,g.width,g.height)),_.scale(h,h),_.drawImage(p,0,0,a,o),{dataUrl:g.toDataURL(`image/png`),width:a,height:o}}function R(e){let t=e.getBoundingClientRect();return{width:Math.ceil(t.width),height:Math.ceil(t.height)}}function z(e){let t=R(e);return t.width>0?t:void 0}async function B(e){let t=[],n=async(e,n)=>{try{await n()!==!1&&t.push(e)}catch{}};return await n(`localStorage`,()=>e.localStorage.clear()),await n(`sessionStorage`,()=>e.sessionStorage.clear()),await n(`indexedDB`,async()=>{let t=e.indexedDB;if(typeof t?.databases!=`function`)return!1;let n=await t.databases();return await Promise.all(n.map(({name:e})=>e&&new Promise(n=>{let r=t.deleteDatabase(e);r.onsuccess=r.onerror=r.onblocked=()=>n()}))),!0}),await n(`cacheStorage`,async()=>{if(!e.caches)return!1;for(let t of await e.caches.keys())await e.caches.delete(t);return!0}),await n(`cookies`,()=>{for(let t of e.document.cookie.split(`;`)){let n=t.split(`=`)[0]?.trim();n&&(e.document.cookie=`${n}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`)}}),{cleared:t}}let V=/^--[\\w-]+$/;function H(t=window){let n=t.document,a=n.getElementById(`gf-config`),o=JSON.parse(a?.textContent??`{}`);a?.remove();let s=n.getElementById(`gf-root`)??n.body,u,d={},f=null,p=null,g=null,_={width:0,height:0},v=m(t,{onError:t=>u?.notify(e.error,t),onConsole:t=>{(t.level===`warn`||t.level===`error`)&&u?.notify(e.log,t)}}),y=t.matchMedia?.(`(prefers-reduced-motion: reduce)`),b=E({root:s,animate:()=>o.animate!==!1&&!y?.matches,onScriptError:(e,t)=>v.record({kind:`script`,message:e,...t?{source:t}:{}})}),x=()=>R(s),S,C=()=>{S=void 0;let t=z(s);if(!t)return;let r=d.containerDimensions?.maxHeight;n.documentElement.style.overflowY=typeof r==`number`&&t.height>r?`auto`:`hidden`,(t.width!==_.width||t.height!==_.height)&&(_=t,u?.notify(e.sizeChanged,t))},w=()=>{S??=t.requestAnimationFrame(C)};typeof t.ResizeObserver==`function`&&new t.ResizeObserver(w).observe(s);let T=e=>{let r=d.theme;d={...d,...e};let i=n.documentElement.style,a=!1;(e.theme===`light`||e.theme===`dark`)&&(i.colorScheme=e.theme,n.documentElement.dataset.theme=e.theme,a=e.theme!==r);let o=e.styles?.variables;if(o&&typeof o==`object`){for(let[e,t]of Object.entries(o))V.test(e)&&typeof t==`string`&&i.setProperty(e,t);a=!0}a&&t.dispatchEvent(new CustomEvent(`themechange`,{detail:{theme:d.theme,variables:d.styles?.variables}}))},D=()=>{if(!u)throw Error(`generative-frame is not connected to its host`);return u},O=t=>D().request(e.message,{role:`user`,content:[{type:`text`,text:String(t)}]}),k=t=>D().request(e.openLink,{url:String(t)}),A=(t,n)=>D().request(e.callTool,{name:t,...n===void 0?{}:{arguments:n}}),j=t=>D().request(e.requestDisplayMode,{mode:t}),M=t=>D().request(e.updateModelContext,t),N=async t=>{await D().request(e.copyText,{text:String(t)})},P={version:l,get theme(){return d.theme},get context(){return d},get toolInput(){return f},get toolOutput(){return p},get state(){return g},sendPrompt:O,openLink:k,callTool:A,requestDisplayMode:j,updateModelContext:M,copyText:N,setState:t=>{g=t,D().notify(e.widgetState,{state:t})},on(e,n){return t.addEventListener(e,n),()=>t.removeEventListener(e,n)}},F=t;F.genframe=P,F.sendPrompt=O,F.openLink=k,t.navigator.clipboard?Object.defineProperty(t.navigator.clipboard,\"writeText\",{configurable:!0,value:N}):Object.defineProperty(t.navigator,\"clipboard\",{configurable:!0,value:{writeText:N}}),t.open=(e=>(e!==void 0&&k(String(e)).catch(()=>{}),null)),n.addEventListener(`click`,e=>{if(e.defaultPrevented||!(e.target instanceof t.Element))return;let n=e.target.closest(`a[href]`);if(!n)return;let r=n.getAttribute(`href`)??``;if(r.startsWith(`#`))return;let i;try{i=new URL(r,o.hostOrigin)}catch{return}(i.protocol===`http:`||i.protocol===`https:`)&&(e.preventDefault(),k(i.href).catch(()=>{}))},!0);let I=()=>({size:x(),blank:h(s),errorCount:v.errors.length}),H=()=>({kind:b.kind,ended:b.ended,size:x(),blank:b.ended?h(s):!1,errors:[...v.errors],console:[...v.console]}),U=async(n,a)=>{let o=a??{};switch(n){case e.end:return await b.end(),w(),I();case e.replace:if(typeof o.code!=`string`)throw new i(r.invalidParams,`replace requires a string 'code'`);return await b.replace(o.code),w(),I();case e.screenshot:return b.flush(),L(s,o);case e.inspect:return b.flush(),H();case e.clearStorage:return B(t);default:throw new i(r.methodNotFound,`Unknown method: ${n}`)}},W=(n,r)=>{let i=r??{};switch(n){case e.write:typeof i.chunk==`string`&&b.write(i.chunk);return;case e.hostContextChanged:T(i);return;case e.toolInputPartial:case e.toolInput:{let r=i.arguments;f=r&&typeof r==`object`?r:{},t.dispatchEvent(new CustomEvent(`toolinput`,{detail:{arguments:f,partial:n===e.toolInputPartial}}));return}case e.toolResult:p=i.structuredContent===void 0?i:i.structuredContent,t.dispatchEvent(new CustomEvent(`toolresult`,{detail:i}));return;default:return}},G=(t,n)=>{u=c(t,{onRequest:U,onNotification:W}),g=n.state??null,T(n.context??{}),u.notify(e.ready,{version:l}),w();for(let t of v.errors)u.notify(e.error,t)},K=0,q=()=>{K++,K>100&&t.clearInterval(Y),t.parent.postMessage({type:`genframe:ready`},o.hostOrigin)},J=e=>{if(e.source!==t.parent||e.origin!==o.hostOrigin)return;let n=e.data;n?.type===`genframe:init`&&e.ports[0]&&(t.removeEventListener(`message`,J),t.clearInterval(Y),G(e.ports[0],n))};t.addEventListener(`message`,J);let Y=t.setInterval(q,100);q()}H()})();";