@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,544 @@
1
+ import {
2
+ computed,
3
+ defineComponent,
4
+ Fragment,
5
+ h,
6
+ inject,
7
+ onActivated,
8
+ onBeforeUnmount,
9
+ onDeactivated,
10
+ onErrorCaptured,
11
+ onMounted,
12
+ onUpdated,
13
+ provide,
14
+ shallowRef,
15
+ watch,
16
+ type Component,
17
+ type ComputedRef,
18
+ type InjectionKey,
19
+ type VNodeChild,
20
+ } from "vue";
21
+ import {
22
+ createActionDispatcher,
23
+ type ActionDispatcher,
24
+ type ActionHandler,
25
+ type ActionSource,
26
+ } from "../actions";
27
+ import type { Catalog } from "../catalog";
28
+ import {
29
+ evaluateCondition,
30
+ resolveProps,
31
+ type ExpressionContext,
32
+ } from "../expressions";
33
+ import {
34
+ animatePendingDots,
35
+ PENDING_DOT_STYLE,
36
+ PENDING_STYLE,
37
+ } from "../pending";
38
+ import { getAtPointer } from "../pointer";
39
+ import { rejectedPropType } from "../resolved";
40
+ import { createStateStore, type SpecStateStore } from "../state";
41
+ import { childIdsOf, repeatItems, safePointer } from "../tree";
42
+ import type { Spec, SpecElement } from "../types";
43
+ export type SpecComponentProps<
44
+ P extends Record<string, unknown> = Record<string, unknown>,
45
+ > = {
46
+ id: string;
47
+ element: SpecElement;
48
+ props: P;
49
+ /** The rendered default slot, also supplied as a native Vue default slot. */
50
+ children: VNodeChild;
51
+ /** Named slot nodes, also supplied as native Vue named slots. */
52
+ slots: Record<string, VNodeChild>;
53
+ emit(event: string, payload?: unknown): void;
54
+ setProp(name: string, value: unknown): void;
55
+ bindings: Record<string, string>;
56
+ state: SpecStateStore;
57
+ streaming: boolean;
58
+ };
59
+ export type SpecComponents = Record<string, Component>;
60
+ export type SpecPlaceholderProps = {
61
+ id: string;
62
+ reason:
63
+ | "missing"
64
+ | "pending"
65
+ | "unknown-type"
66
+ | "invalid-props"
67
+ | "cycle"
68
+ | "error";
69
+ message: string;
70
+ };
71
+ export type SpecRendererProps = {
72
+ spec: Spec;
73
+ components: SpecComponents;
74
+ catalog?: Catalog;
75
+ state?: SpecStateStore;
76
+ handlers?: Record<string, ActionHandler>;
77
+ onAction?: (
78
+ name: string,
79
+ params: Record<string, unknown>,
80
+ context: ActionSource & { state: SpecStateStore },
81
+ ) => unknown;
82
+ onError?: (error: Error, context: { elementId?: string }) => void;
83
+ streaming?: boolean;
84
+ placeholder?: Component;
85
+ };
86
+ type RenderContext = {
87
+ spec: Spec;
88
+ components: SpecComponents;
89
+ catalog: Catalog | undefined;
90
+ store: SpecStateStore;
91
+ state: Record<string, unknown>;
92
+ dispatch: ActionDispatcher;
93
+ streaming: boolean;
94
+ placeholder: Component;
95
+ invalid: (element: SpecElement) => string | undefined;
96
+ onError: SpecRendererProps["onError"];
97
+ };
98
+ const SpecContext: InjectionKey<ComputedRef<RenderContext>> =
99
+ Symbol("SpecRenderer");
100
+ type Scope = Pick<ExpressionContext, "item" | "index" | "itemPath">;
101
+ const PendingDots = defineComponent({
102
+ setup() {
103
+ const host = shallowRef<HTMLSpanElement | null>(null);
104
+ let stop: (() => void) | undefined;
105
+ const start = () => {
106
+ stop?.();
107
+ stop = host.value ? animatePendingDots(host.value) : undefined;
108
+ };
109
+ onMounted(start);
110
+ onActivated(start);
111
+ onDeactivated(() => {
112
+ stop?.();
113
+ stop = undefined;
114
+ });
115
+ onBeforeUnmount(() => {
116
+ stop?.();
117
+ stop = undefined;
118
+ });
119
+ return () =>
120
+ h(
121
+ "span",
122
+ {
123
+ ref: host,
124
+ "data-gf-spec-placeholder": "pending",
125
+ "aria-hidden": "true",
126
+ style: PENDING_STYLE,
127
+ },
128
+ Array.from({ length: 3 }, (_, key) =>
129
+ h("span", { key, style: PENDING_DOT_STYLE }),
130
+ ),
131
+ );
132
+ },
133
+ });
134
+ export const SpecPlaceholder = defineComponent(
135
+ (props: SpecPlaceholderProps) => () =>
136
+ props.reason === "pending"
137
+ ? h(PendingDots)
138
+ : h(
139
+ "div",
140
+ {
141
+ "data-gf-spec-placeholder": props.reason,
142
+ role: "note",
143
+ title: props.id + ": " + props.message,
144
+ style: {
145
+ fontSize: "12px",
146
+ padding: "4px 8px",
147
+ margin: "2px 0",
148
+ border: "1px dashed currentColor",
149
+ borderRadius: "6px",
150
+ opacity: 0.6,
151
+ },
152
+ },
153
+ props.message,
154
+ ),
155
+ { name: "SpecPlaceholder", props: ["id", "reason", "message"] },
156
+ );
157
+ type BoundaryProps = {
158
+ element: SpecElement;
159
+ onError: (error: Error) => void;
160
+ fallback: (error: Error) => VNodeChild;
161
+ };
162
+ const ElementBoundary = defineComponent(
163
+ (props: BoundaryProps, { slots }) => {
164
+ const error = shallowRef<Error | null>(null);
165
+ watch(
166
+ () => props.element,
167
+ () => {
168
+ error.value = null;
169
+ },
170
+ { flush: "sync" },
171
+ );
172
+ onErrorCaptured((cause) => {
173
+ const caught = cause instanceof Error ? cause : new Error(String(cause));
174
+ error.value = caught;
175
+ props.onError(caught);
176
+ return false;
177
+ });
178
+ return () =>
179
+ error.value ? props.fallback(error.value) : slots.default?.();
180
+ },
181
+ { name: "SpecElementBoundary", props: ["element", "onError", "fallback"] },
182
+ );
183
+ function renderChildren(
184
+ ids: readonly unknown[],
185
+ scope: Scope,
186
+ ancestors: readonly string[],
187
+ ): VNodeChild[] {
188
+ const occurrences = new Map<string, number>();
189
+ return ids.map((id) => {
190
+ if (typeof id !== "string") return null;
191
+ const occurrence = occurrences.get(id) ?? 0;
192
+ occurrences.set(id, occurrence + 1);
193
+ return h(ElementView, { key: id + ":" + occurrence, id, scope, ancestors });
194
+ });
195
+ }
196
+ const ElementView = defineComponent(
197
+ (props: { id: string; scope: Scope; ancestors: readonly string[] }) => {
198
+ const context = inject(SpecContext)!;
199
+ let last: SpecElement | undefined;
200
+ let accepted: SpecElement | undefined;
201
+ const commit = () => {
202
+ if (accepted !== undefined) last = accepted;
203
+ };
204
+ onMounted(commit);
205
+ onUpdated(commit);
206
+ return () => {
207
+ const ctx = context.value;
208
+ const { id, scope, ancestors } = props;
209
+ const Placeholder = ctx.placeholder;
210
+ const raw = Object.hasOwn(ctx.spec.elements, id)
211
+ ? ctx.spec.elements[id]
212
+ : undefined;
213
+ const element =
214
+ raw && typeof raw === "object" && typeof raw.type === "string"
215
+ ? raw
216
+ : undefined;
217
+ const invalid = element && ctx.invalid(element);
218
+ accepted = invalid ? undefined : element;
219
+ if (ancestors.includes(id))
220
+ return h(Placeholder, {
221
+ id,
222
+ reason: "cycle",
223
+ message: '"' + id + '" contains itself',
224
+ });
225
+ if (!element)
226
+ return ctx.streaming
227
+ ? null
228
+ : h(Placeholder, {
229
+ id,
230
+ reason: "missing",
231
+ message: 'Missing element "' + id + '"',
232
+ });
233
+ if (!evaluateCondition(element.visible, { ...scope, state: ctx.state }))
234
+ return null;
235
+ const Impl = Object.hasOwn(ctx.components, element.type)
236
+ ? ctx.components[element.type]
237
+ : undefined;
238
+ if (!Impl || (ctx.catalog && !ctx.catalog.component(element.type)))
239
+ return ctx.streaming
240
+ ? null
241
+ : h(Placeholder, {
242
+ id,
243
+ reason: "unknown-type",
244
+ message: 'Unknown component "' + element.type + '"',
245
+ });
246
+ if (invalid && !ctx.streaming)
247
+ return h(Placeholder, {
248
+ id,
249
+ reason: "invalid-props",
250
+ message: invalid,
251
+ });
252
+ const shown = invalid ? last : element;
253
+ if (shown?.type !== element.type) return null;
254
+ return h(
255
+ ElementBoundary,
256
+ {
257
+ element: shown,
258
+ onError: (error) => context.value.onError?.(error, { elementId: id }),
259
+ fallback: (error) =>
260
+ context.value.streaming
261
+ ? null
262
+ : h(context.value.placeholder, {
263
+ id,
264
+ reason: "error",
265
+ message: shown.type + " failed: " + error.message,
266
+ }),
267
+ },
268
+ {
269
+ default: () =>
270
+ h(ResolvedElement, {
271
+ key: shown.type,
272
+ id,
273
+ element: shown,
274
+ Impl,
275
+ scope,
276
+ ancestors: [...ancestors, id],
277
+ }),
278
+ },
279
+ );
280
+ };
281
+ },
282
+ { name: "SpecElement", props: ["id", "scope", "ancestors"] },
283
+ );
284
+ type Resolved = ReturnType<typeof resolveProps>;
285
+ const ResolvedElement = defineComponent(
286
+ (input: {
287
+ id: string;
288
+ element: SpecElement;
289
+ Impl: Component;
290
+ scope: Scope;
291
+ ancestors: readonly string[];
292
+ }) => {
293
+ const context = inject(SpecContext)!;
294
+ const resolved = computed(() =>
295
+ resolveProps(input.element.props, {
296
+ ...input.scope,
297
+ state: context.value.state,
298
+ }),
299
+ );
300
+ const rejected = computed(() =>
301
+ rejectedPropType(
302
+ context.value.catalog?.propsSchema(input.element.type),
303
+ resolved.value.props,
304
+ ),
305
+ );
306
+ let last: Resolved | undefined;
307
+ const shown = computed(() => (rejected.value ? last : resolved.value));
308
+ const commit = () => {
309
+ if (!rejected.value) last = resolved.value;
310
+ };
311
+ onMounted(commit);
312
+ onUpdated(commit);
313
+ let previousWatch: Map<string, unknown> | undefined;
314
+ watch(
315
+ () => ({
316
+ state: context.value.state,
317
+ element: input.element,
318
+ scope: input.scope,
319
+ dispatch: context.value.dispatch,
320
+ }),
321
+ () => {
322
+ const ctx = context.value;
323
+ const next = new Map<string, unknown>();
324
+ for (const path of Object.keys(input.element.watch ?? {})) {
325
+ const value = getAtPointer(ctx.state, safePointer(path));
326
+ next.set(path, value);
327
+ if (
328
+ previousWatch?.has(path) &&
329
+ !Object.is(previousWatch.get(path), value)
330
+ )
331
+ void ctx.dispatch(input.element.watch![path], {
332
+ ...input.scope,
333
+ elementId: input.id,
334
+ trigger: path,
335
+ kind: "watch",
336
+ });
337
+ }
338
+ previousWatch = next;
339
+ },
340
+ { immediate: true, flush: "post" },
341
+ );
342
+ const emit = (event: string, payload?: unknown) => {
343
+ const bound = input.element.on?.[event];
344
+ if (bound)
345
+ void context.value.dispatch(bound, {
346
+ ...input.scope,
347
+ elementId: input.id,
348
+ trigger: event,
349
+ kind: "event",
350
+ payload,
351
+ });
352
+ };
353
+ const setProp = (name: string, value: unknown) => {
354
+ const path = shown.value?.bindings[name];
355
+ if (path !== undefined) context.value.store.set(path, value, "replace");
356
+ };
357
+ return () => {
358
+ const ctx = context.value;
359
+ const { id, element, Impl, scope, ancestors } = input;
360
+ if (rejected.value && !ctx.streaming)
361
+ return h(ctx.placeholder, {
362
+ id,
363
+ reason: "invalid-props",
364
+ message: element.type + ": " + rejected.value,
365
+ });
366
+ const current = shown.value;
367
+ if (!current) return null;
368
+ const children =
369
+ element.repeat && typeof element.repeat.path === "string"
370
+ ? (repeatItems(element.repeat, ctx.state)?.map((item) =>
371
+ h(
372
+ Fragment,
373
+ { key: item.key },
374
+ renderChildren(
375
+ childIdsOf(element, "default"),
376
+ item.scope,
377
+ ancestors,
378
+ ),
379
+ ),
380
+ ) ?? null)
381
+ : renderChildren(childIdsOf(element, "default"), scope, ancestors);
382
+ const named: Record<string, VNodeChild> = {};
383
+ for (const [name, ids] of Object.entries(element.slots ?? {}))
384
+ if (Array.isArray(ids))
385
+ named[name] = renderChildren(ids, scope, ancestors);
386
+ const slots = Object.fromEntries(
387
+ Object.entries(named).map(([name, nodes]) => [name, () => nodes]),
388
+ );
389
+ return h(
390
+ Impl,
391
+ {
392
+ id,
393
+ element,
394
+ props: current.props,
395
+ children,
396
+ bindings: current.bindings,
397
+ slots: named,
398
+ emit,
399
+ setProp,
400
+ state: ctx.store,
401
+ streaming: ctx.streaming,
402
+ },
403
+ { ...slots, default: () => children },
404
+ );
405
+ };
406
+ },
407
+ {
408
+ name: "ResolvedSpecElement",
409
+ props: ["id", "element", "Impl", "scope", "ancestors"],
410
+ },
411
+ );
412
+ export const SpecRenderer = defineComponent(
413
+ (props: SpecRendererProps) => {
414
+ const emptyState: Record<string, unknown> = {};
415
+ const ownStore = createStateStore(props.spec.state ?? emptyState);
416
+ const store = computed(() => props.state ?? ownStore);
417
+ const snapshot = shallowRef(store.value.getState());
418
+ const active = shallowRef(false);
419
+ onMounted(() => {
420
+ active.value = true;
421
+ });
422
+ onActivated(() => {
423
+ active.value = true;
424
+ });
425
+ onDeactivated(() => {
426
+ active.value = false;
427
+ });
428
+ onBeforeUnmount(() => {
429
+ active.value = false;
430
+ });
431
+ watch(
432
+ [store, active],
433
+ ([current, enabled], _old, onCleanup) => {
434
+ snapshot.value = current.getState();
435
+ if (!enabled) return;
436
+ let subscribed = true;
437
+ const stop = current.subscribe(() => {
438
+ if (subscribed) snapshot.value = current.getState();
439
+ });
440
+ snapshot.value = current.getState();
441
+ onCleanup(() => {
442
+ subscribed = false;
443
+ stop();
444
+ });
445
+ },
446
+ { immediate: true, flush: "sync" },
447
+ );
448
+ watch(
449
+ () => [props.state, props.spec.state] as const,
450
+ ([external, seed]) => {
451
+ if (!external) ownStore.seed(seed ?? emptyState);
452
+ },
453
+ { flush: "pre" },
454
+ );
455
+ const dispatch = computed(() =>
456
+ createActionDispatcher({
457
+ store: store.value,
458
+ ...(props.catalog ? { catalog: props.catalog } : {}),
459
+ onAction: (name, params, context) => {
460
+ if (props.handlers && Object.hasOwn(props.handlers, name))
461
+ return props.handlers[name]!(params, context);
462
+ if (!props.onAction)
463
+ throw new Error('No handler for action "' + name + '"');
464
+ return props.onAction(name, params, context);
465
+ },
466
+ onError: (error) => {
467
+ if (props.onError) props.onError(error, {});
468
+ else console.warn("[generative-frame]", error.message);
469
+ },
470
+ }),
471
+ );
472
+ const invalid = computed(() => {
473
+ const catalog = props.catalog;
474
+ const cache = new WeakMap<SpecElement, string | null>();
475
+ return (element: SpecElement): string | undefined => {
476
+ if (!catalog) return undefined;
477
+ let message = cache.get(element);
478
+ if (message === undefined) {
479
+ const issues = catalog.validateProps(element.type, element.props);
480
+ message = issues.length
481
+ ? element.type +
482
+ ": " +
483
+ issues
484
+ .slice(0, 2)
485
+ .map(
486
+ (issue) =>
487
+ (issue.path === "/" ? "props" : issue.path.slice(1)) +
488
+ " " +
489
+ issue.message,
490
+ )
491
+ .join("; ")
492
+ : null;
493
+ cache.set(element, message);
494
+ }
495
+ return message ?? undefined;
496
+ };
497
+ });
498
+ const ctx = computed<RenderContext>(() => ({
499
+ spec: props.spec,
500
+ components: props.components,
501
+ catalog: props.catalog,
502
+ store: store.value,
503
+ state: snapshot.value,
504
+ dispatch: dispatch.value,
505
+ streaming: props.streaming ?? false,
506
+ placeholder: props.placeholder ?? SpecPlaceholder,
507
+ invalid: invalid.value,
508
+ onError: props.onError,
509
+ }));
510
+ provide(SpecContext, ctx);
511
+ return () => [
512
+ props.spec.root
513
+ ? h(ElementView, {
514
+ key: "root",
515
+ id: props.spec.root,
516
+ scope: {},
517
+ ancestors: [],
518
+ })
519
+ : null,
520
+ props.streaming
521
+ ? h(props.placeholder ?? SpecPlaceholder, {
522
+ key: "pending",
523
+ id: "",
524
+ reason: "pending",
525
+ message: "The spec is streaming",
526
+ })
527
+ : null,
528
+ ];
529
+ },
530
+ {
531
+ name: "SpecRenderer",
532
+ props: [
533
+ "spec",
534
+ "components",
535
+ "catalog",
536
+ "state",
537
+ "handlers",
538
+ "onAction",
539
+ "onError",
540
+ "streaming",
541
+ "placeholder",
542
+ ],
543
+ },
544
+ );
@@ -0,0 +1,50 @@
1
+ import { createApp, nextTick, shallowRef, type VNode } from "vue";
2
+ import {
3
+ getQueriesForElement,
4
+ fireEvent as domFireEvent,
5
+ } from "@testing-library/dom";
6
+ const disposers = new Set<() => void>();
7
+ export function cleanup() {
8
+ for (const dispose of [...disposers]) dispose();
9
+ }
10
+ export async function act(callback: () => unknown) {
11
+ await callback();
12
+ await nextTick();
13
+ }
14
+ export const fireEvent = {
15
+ async click(element: Element, options?: Record<string, unknown>) {
16
+ domFireEvent.click(element, options);
17
+ await nextTick();
18
+ },
19
+ async change(element: Element, options?: Record<string, unknown>) {
20
+ domFireEvent.change(element, options);
21
+ await nextTick();
22
+ },
23
+ };
24
+ export function render(initial: VNode) {
25
+ const vnode = shallowRef(initial);
26
+ const container = document.createElement("div");
27
+ document.body.appendChild(container);
28
+ const app = createApp({ setup: () => () => vnode.value });
29
+ app.config.warnHandler = (message, instance, trace) =>
30
+ console.warn(message, instance, trace);
31
+ app.mount(container);
32
+ let mounted = true;
33
+ const unmount = () => {
34
+ if (!mounted) return;
35
+ mounted = false;
36
+ app.unmount();
37
+ container.remove();
38
+ disposers.delete(unmount);
39
+ };
40
+ disposers.add(unmount);
41
+ return {
42
+ ...getQueriesForElement(container),
43
+ container,
44
+ unmount,
45
+ async rerender(next: VNode) {
46
+ vnode.value = next;
47
+ await nextTick();
48
+ },
49
+ };
50
+ }