@json-render/svelte 0.6.1

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 (76) hide show
  1. package/LICENSE +201 -0
  2. package/dist/CatalogRenderer.svelte +55 -0
  3. package/dist/CatalogRenderer.svelte.d.ts +20 -0
  4. package/dist/CatalogRenderer.svelte.d.ts.map +1 -0
  5. package/dist/ConfirmDialog.svelte +98 -0
  6. package/dist/ConfirmDialog.svelte.d.ts +10 -0
  7. package/dist/ConfirmDialog.svelte.d.ts.map +1 -0
  8. package/dist/ConfirmDialogManager.svelte +15 -0
  9. package/dist/ConfirmDialogManager.svelte.d.ts +4 -0
  10. package/dist/ConfirmDialogManager.svelte.d.ts.map +1 -0
  11. package/dist/ElementRenderer.svelte +147 -0
  12. package/dist/ElementRenderer.svelte.d.ts +13 -0
  13. package/dist/ElementRenderer.svelte.d.ts.map +1 -0
  14. package/dist/JsonUIProvider.svelte +68 -0
  15. package/dist/JsonUIProvider.svelte.d.ts +37 -0
  16. package/dist/JsonUIProvider.svelte.d.ts.map +1 -0
  17. package/dist/Renderer.svelte +34 -0
  18. package/dist/Renderer.svelte.d.ts +19 -0
  19. package/dist/Renderer.svelte.d.ts.map +1 -0
  20. package/dist/RendererWithProvider.test.svelte +37 -0
  21. package/dist/RendererWithProvider.test.svelte.d.ts +14 -0
  22. package/dist/RendererWithProvider.test.svelte.d.ts.map +1 -0
  23. package/dist/RepeatChildren.svelte +50 -0
  24. package/dist/RepeatChildren.svelte.d.ts +13 -0
  25. package/dist/RepeatChildren.svelte.d.ts.map +1 -0
  26. package/dist/TestButton.svelte +11 -0
  27. package/dist/TestButton.svelte.d.ts +9 -0
  28. package/dist/TestButton.svelte.d.ts.map +1 -0
  29. package/dist/TestContainer.svelte +19 -0
  30. package/dist/TestContainer.svelte.d.ts +11 -0
  31. package/dist/TestContainer.svelte.d.ts.map +1 -0
  32. package/dist/TestText.svelte +9 -0
  33. package/dist/TestText.svelte.d.ts +9 -0
  34. package/dist/TestText.svelte.d.ts.map +1 -0
  35. package/dist/catalog-types.d.ts +63 -0
  36. package/dist/catalog-types.d.ts.map +1 -0
  37. package/dist/catalog-types.js +1 -0
  38. package/dist/contexts/ActionProvider.svelte +344 -0
  39. package/dist/contexts/ActionProvider.svelte.d.ts +62 -0
  40. package/dist/contexts/ActionProvider.svelte.d.ts.map +1 -0
  41. package/dist/contexts/FunctionsContextProvider.svelte +44 -0
  42. package/dist/contexts/FunctionsContextProvider.svelte.d.ts +21 -0
  43. package/dist/contexts/FunctionsContextProvider.svelte.d.ts.map +1 -0
  44. package/dist/contexts/RepeatScopeProvider.svelte +49 -0
  45. package/dist/contexts/RepeatScopeProvider.svelte.d.ts +26 -0
  46. package/dist/contexts/RepeatScopeProvider.svelte.d.ts.map +1 -0
  47. package/dist/contexts/StateProvider.svelte +182 -0
  48. package/dist/contexts/StateProvider.svelte.d.ts +47 -0
  49. package/dist/contexts/StateProvider.svelte.d.ts.map +1 -0
  50. package/dist/contexts/ValidationProvider.svelte +198 -0
  51. package/dist/contexts/ValidationProvider.svelte.d.ts +57 -0
  52. package/dist/contexts/ValidationProvider.svelte.d.ts.map +1 -0
  53. package/dist/contexts/VisibilityProvider.svelte +82 -0
  54. package/dist/contexts/VisibilityProvider.svelte.d.ts +29 -0
  55. package/dist/contexts/VisibilityProvider.svelte.d.ts.map +1 -0
  56. package/dist/contexts/actions.test.js +156 -0
  57. package/dist/contexts/state.test.js +118 -0
  58. package/dist/contexts/visibility.test.js +71 -0
  59. package/dist/index.d.ts +18 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +30 -0
  62. package/dist/renderer.d.ts +128 -0
  63. package/dist/renderer.d.ts.map +1 -0
  64. package/dist/renderer.js +124 -0
  65. package/dist/renderer.test.js +168 -0
  66. package/dist/schema.d.ts +57 -0
  67. package/dist/schema.d.ts.map +1 -0
  68. package/dist/schema.js +81 -0
  69. package/dist/streaming.svelte.d.ts +75 -0
  70. package/dist/streaming.svelte.d.ts.map +1 -0
  71. package/dist/streaming.svelte.js +404 -0
  72. package/dist/utils.svelte.d.ts +26 -0
  73. package/dist/utils.svelte.d.ts.map +1 -0
  74. package/dist/utils.svelte.js +92 -0
  75. package/dist/utils.test.js +307 -0
  76. package/package.json +70 -0
@@ -0,0 +1,307 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { flatToTree, buildSpecFromParts, getTextFromParts, } from "./utils.svelte.js";
3
+ import { SPEC_DATA_PART_TYPE } from "@json-render/core";
4
+ describe("flatToTree", () => {
5
+ it("converts array of elements to tree structure", () => {
6
+ const elements = [
7
+ { key: "root", type: "Container", props: {}, parentKey: undefined },
8
+ {
9
+ key: "child1",
10
+ type: "Text",
11
+ props: { text: "Hello" },
12
+ parentKey: "root",
13
+ },
14
+ ];
15
+ const spec = flatToTree(elements);
16
+ expect(spec.root).toBe("root");
17
+ expect(spec.elements["root"]).toBeDefined();
18
+ expect(spec.elements["child1"]).toBeDefined();
19
+ });
20
+ it("builds parent-child relationships", () => {
21
+ const elements = [
22
+ { key: "root", type: "Container", props: {}, parentKey: undefined },
23
+ { key: "child1", type: "Text", props: {}, parentKey: "root" },
24
+ { key: "child2", type: "Text", props: {}, parentKey: "root" },
25
+ ];
26
+ const spec = flatToTree(elements);
27
+ expect(spec.elements["root"]?.children).toEqual(["child1", "child2"]);
28
+ });
29
+ it("handles single root element", () => {
30
+ const elements = [
31
+ {
32
+ key: "only",
33
+ type: "Text",
34
+ props: { text: "Solo" },
35
+ parentKey: undefined,
36
+ },
37
+ ];
38
+ const spec = flatToTree(elements);
39
+ expect(spec.root).toBe("only");
40
+ expect(spec.elements["only"]?.children).toEqual([]);
41
+ });
42
+ it("handles deeply nested elements", () => {
43
+ const elements = [
44
+ { key: "root", type: "Container", props: {}, parentKey: undefined },
45
+ { key: "level1", type: "Container", props: {}, parentKey: "root" },
46
+ { key: "level2", type: "Container", props: {}, parentKey: "level1" },
47
+ { key: "level3", type: "Text", props: {}, parentKey: "level2" },
48
+ ];
49
+ const spec = flatToTree(elements);
50
+ expect(spec.elements["root"]?.children).toEqual(["level1"]);
51
+ expect(spec.elements["level1"]?.children).toEqual(["level2"]);
52
+ expect(spec.elements["level2"]?.children).toEqual(["level3"]);
53
+ expect(spec.elements["level3"]?.children).toEqual([]);
54
+ });
55
+ it("preserves element props", () => {
56
+ const elements = [
57
+ {
58
+ key: "root",
59
+ type: "Card",
60
+ props: { title: "Hello", value: 42 },
61
+ parentKey: undefined,
62
+ },
63
+ ];
64
+ const spec = flatToTree(elements);
65
+ expect(spec.elements["root"]?.props).toEqual({ title: "Hello", value: 42 });
66
+ });
67
+ it("preserves visibility conditions", () => {
68
+ const elements = [
69
+ {
70
+ key: "root",
71
+ type: "Container",
72
+ props: {},
73
+ parentKey: undefined,
74
+ visible: { $state: "/isVisible" },
75
+ },
76
+ ];
77
+ const spec = flatToTree(elements);
78
+ expect(spec.elements["root"]?.visible).toEqual({ $state: "/isVisible" });
79
+ });
80
+ it("handles elements with undefined parentKey as root", () => {
81
+ const elements = [
82
+ { key: "a", type: "Text", props: {}, parentKey: undefined },
83
+ ];
84
+ const spec = flatToTree(elements);
85
+ expect(spec.root).toBe("a");
86
+ });
87
+ it("handles empty elements array", () => {
88
+ const spec = flatToTree([]);
89
+ expect(spec.root).toBe("");
90
+ expect(spec.elements).toEqual({});
91
+ });
92
+ it("handles multiple children correctly", () => {
93
+ const elements = [
94
+ { key: "root", type: "Container", props: {}, parentKey: undefined },
95
+ { key: "a", type: "Text", props: {}, parentKey: "root" },
96
+ { key: "b", type: "Text", props: {}, parentKey: "root" },
97
+ { key: "c", type: "Text", props: {}, parentKey: "root" },
98
+ ];
99
+ const spec = flatToTree(elements);
100
+ expect(spec.elements["root"]?.children).toHaveLength(3);
101
+ expect(spec.elements["root"]?.children).toContain("a");
102
+ expect(spec.elements["root"]?.children).toContain("b");
103
+ expect(spec.elements["root"]?.children).toContain("c");
104
+ });
105
+ });
106
+ describe("buildSpecFromParts", () => {
107
+ it("returns null when no data-spec parts are present", () => {
108
+ const parts = [
109
+ { type: "text", text: "Hello world" },
110
+ { type: "other", data: {} },
111
+ ];
112
+ const spec = buildSpecFromParts(parts);
113
+ expect(spec).toBeNull();
114
+ });
115
+ it("builds a spec from patch parts", () => {
116
+ const parts = [
117
+ {
118
+ type: SPEC_DATA_PART_TYPE,
119
+ data: {
120
+ type: "patch",
121
+ patch: { op: "add", path: "/root", value: "main" },
122
+ },
123
+ },
124
+ {
125
+ type: SPEC_DATA_PART_TYPE,
126
+ data: {
127
+ type: "patch",
128
+ patch: {
129
+ op: "add",
130
+ path: "/elements/main",
131
+ value: { type: "Text", props: { text: "Hi" }, children: [] },
132
+ },
133
+ },
134
+ },
135
+ ];
136
+ const spec = buildSpecFromParts(parts);
137
+ expect(spec).not.toBeNull();
138
+ expect(spec?.root).toBe("main");
139
+ expect(spec?.elements["main"]?.type).toBe("Text");
140
+ });
141
+ it("handles flat spec parts", () => {
142
+ const parts = [
143
+ {
144
+ type: SPEC_DATA_PART_TYPE,
145
+ data: {
146
+ type: "flat",
147
+ spec: {
148
+ root: "root",
149
+ elements: {
150
+ root: { type: "Container", props: {}, children: [] },
151
+ },
152
+ },
153
+ },
154
+ },
155
+ ];
156
+ const spec = buildSpecFromParts(parts);
157
+ expect(spec?.root).toBe("root");
158
+ expect(spec?.elements["root"]?.type).toBe("Container");
159
+ });
160
+ it("ignores non-spec parts", () => {
161
+ const parts = [
162
+ { type: "text", text: "Some text" },
163
+ {
164
+ type: SPEC_DATA_PART_TYPE,
165
+ data: {
166
+ type: "flat",
167
+ spec: {
168
+ root: "r",
169
+ elements: { r: { type: "Text", props: {}, children: [] } },
170
+ },
171
+ },
172
+ },
173
+ { type: "tool-call", data: {} },
174
+ ];
175
+ const spec = buildSpecFromParts(parts);
176
+ expect(spec?.root).toBe("r");
177
+ });
178
+ it("applies patches incrementally", () => {
179
+ const parts = [
180
+ {
181
+ type: SPEC_DATA_PART_TYPE,
182
+ data: {
183
+ type: "patch",
184
+ patch: { op: "add", path: "/root", value: "a" },
185
+ },
186
+ },
187
+ {
188
+ type: SPEC_DATA_PART_TYPE,
189
+ data: {
190
+ type: "patch",
191
+ patch: {
192
+ op: "add",
193
+ path: "/elements/a",
194
+ value: { type: "Text", props: { n: 1 }, children: [] },
195
+ },
196
+ },
197
+ },
198
+ {
199
+ type: SPEC_DATA_PART_TYPE,
200
+ data: {
201
+ type: "patch",
202
+ patch: { op: "replace", path: "/elements/a/props/n", value: 2 },
203
+ },
204
+ },
205
+ ];
206
+ const spec = buildSpecFromParts(parts);
207
+ expect((spec?.elements["a"]?.props).n).toBe(2);
208
+ });
209
+ it("handles nested spec parts via nestedToFlat", () => {
210
+ const parts = [
211
+ {
212
+ type: SPEC_DATA_PART_TYPE,
213
+ data: {
214
+ type: "nested",
215
+ spec: {
216
+ type: "Container",
217
+ props: {},
218
+ children: [{ type: "Text", props: { t: "x" } }],
219
+ },
220
+ },
221
+ },
222
+ ];
223
+ const spec = buildSpecFromParts(parts);
224
+ expect(spec).not.toBeNull();
225
+ expect(Object.keys(spec?.elements ?? {}).length).toBeGreaterThan(0);
226
+ });
227
+ it("handles mixed patch + flat + nested parts in sequence", () => {
228
+ const parts = [
229
+ {
230
+ type: SPEC_DATA_PART_TYPE,
231
+ data: {
232
+ type: "patch",
233
+ patch: { op: "add", path: "/root", value: "initial" },
234
+ },
235
+ },
236
+ {
237
+ type: SPEC_DATA_PART_TYPE,
238
+ data: {
239
+ type: "flat",
240
+ spec: {
241
+ root: "replaced",
242
+ elements: { replaced: { type: "Box", props: {}, children: [] } },
243
+ },
244
+ },
245
+ },
246
+ ];
247
+ const spec = buildSpecFromParts(parts);
248
+ expect(spec?.root).toBe("replaced");
249
+ });
250
+ it("returns empty elements map from empty parts list", () => {
251
+ const spec = buildSpecFromParts([]);
252
+ expect(spec).toBeNull();
253
+ });
254
+ });
255
+ describe("getTextFromParts", () => {
256
+ it("extracts text from text parts", () => {
257
+ const parts = [
258
+ { type: "text", text: "Hello" },
259
+ { type: "text", text: "World" },
260
+ ];
261
+ const text = getTextFromParts(parts);
262
+ expect(text).toBe("Hello\n\nWorld");
263
+ });
264
+ it("returns empty string when no text parts", () => {
265
+ const parts = [
266
+ { type: "data", data: {} },
267
+ { type: "tool-call", data: {} },
268
+ ];
269
+ const text = getTextFromParts(parts);
270
+ expect(text).toBe("");
271
+ });
272
+ it("ignores non-text parts", () => {
273
+ const parts = [
274
+ { type: "text", text: "Keep" },
275
+ { type: "data", data: {} },
276
+ { type: "text", text: "This" },
277
+ ];
278
+ const text = getTextFromParts(parts);
279
+ expect(text).toBe("Keep\n\nThis");
280
+ });
281
+ it("trims whitespace from text parts", () => {
282
+ const parts = [
283
+ { type: "text", text: " Hello " },
284
+ { type: "text", text: "\n\nWorld\n\n" },
285
+ ];
286
+ const text = getTextFromParts(parts);
287
+ expect(text).toBe("Hello\n\nWorld");
288
+ });
289
+ it("skips empty text parts", () => {
290
+ const parts = [
291
+ { type: "text", text: "Hello" },
292
+ { type: "text", text: " " },
293
+ { type: "text", text: "World" },
294
+ ];
295
+ const text = getTextFromParts(parts);
296
+ expect(text).toBe("Hello\n\nWorld");
297
+ });
298
+ it("ignores text parts with non-string text field", () => {
299
+ const parts = [
300
+ { type: "text", text: "Valid" },
301
+ { type: "text", text: 123 },
302
+ { type: "text", text: "Also Valid" },
303
+ ];
304
+ const text = getTextFromParts(parts);
305
+ expect(text).toBe("Valid\n\nAlso Valid");
306
+ });
307
+ });
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@json-render/svelte",
3
+ "version": "0.6.1",
4
+ "license": "Apache-2.0",
5
+ "description": "Svelte 5 renderer for @json-render/core. JSON becomes Svelte components.",
6
+ "keywords": [
7
+ "json",
8
+ "ui",
9
+ "svelte",
10
+ "svelte5",
11
+ "ai",
12
+ "generative-ui",
13
+ "llm",
14
+ "renderer",
15
+ "streaming",
16
+ "components",
17
+ "runes"
18
+ ],
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/vercel-labs/json-render.git",
22
+ "directory": "packages/svelte"
23
+ },
24
+ "homepage": "https://github.com/vercel-labs/json-render#readme",
25
+ "bugs": {
26
+ "url": "https://github.com/vercel-labs/json-render/issues"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "type": "module",
32
+ "svelte": "./dist/index.js",
33
+ "main": "./dist/index.js",
34
+ "module": "./dist/index.js",
35
+ "types": "./dist/index.d.ts",
36
+ "exports": {
37
+ ".": {
38
+ "types": "./dist/index.d.ts",
39
+ "svelte": "./dist/index.js",
40
+ "default": "./dist/index.js"
41
+ },
42
+ "./schema": {
43
+ "types": "./dist/schema.d.ts",
44
+ "svelte": "./dist/schema.js",
45
+ "default": "./dist/schema.js"
46
+ }
47
+ },
48
+ "files": [
49
+ "dist"
50
+ ],
51
+ "dependencies": {
52
+ "@json-render/core": "0.11.0"
53
+ },
54
+ "devDependencies": {
55
+ "@sveltejs/package": "^2.3.0",
56
+ "@sveltejs/vite-plugin-svelte": "^6.2.4",
57
+ "svelte": "^5.0.0",
58
+ "svelte-check": "^4.0.0",
59
+ "typescript": "^5.4.5",
60
+ "@internal/typescript-config": "0.0.0"
61
+ },
62
+ "peerDependencies": {
63
+ "svelte": "^5.0.0"
64
+ },
65
+ "scripts": {
66
+ "build": "svelte-package -i src -o dist",
67
+ "dev": "svelte-package -i src -o dist --watch",
68
+ "typecheck": "svelte-check --tsconfig ./tsconfig.json"
69
+ }
70
+ }