wizards 0.5.12 → 0.6.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 (89) hide show
  1. package/apps/runtime/dist/authoring/guide.js +63 -7
  2. package/apps/runtime/dist/credits/estimate.js +1 -1
  3. package/apps/runtime/dist/db/control-schema.js +10 -0
  4. package/apps/runtime/dist/db/migrations/control/0010_tan_snowbird.sql +6 -0
  5. package/apps/runtime/dist/db/migrations/control/meta/0010_snapshot.json +600 -0
  6. package/apps/runtime/dist/db/migrations/control/meta/_journal.json +7 -0
  7. package/apps/runtime/dist/db/migrations/tenant/0016_even_trish_tilby.sql +12 -0
  8. package/apps/runtime/dist/db/migrations/tenant/meta/0016_snapshot.json +3326 -0
  9. package/apps/runtime/dist/db/migrations/tenant/meta/_journal.json +7 -0
  10. package/apps/runtime/dist/db/schema.js +20 -0
  11. package/apps/runtime/dist/engine/requirements.js +1 -1
  12. package/apps/runtime/dist/engine/runner.js +72 -7
  13. package/apps/runtime/dist/engine/steps.js +169 -126
  14. package/apps/runtime/dist/model-errors.js +20 -7
  15. package/apps/runtime/dist/render/downloads.js +16 -3
  16. package/apps/runtime/dist/render/shaped.js +137 -0
  17. package/apps/runtime/dist/routes/runs.js +228 -25
  18. package/apps/runtime/dist/routes/studio.js +41 -3
  19. package/apps/runtime/dist/services/brand.js +2 -1
  20. package/apps/runtime/dist/services/cloud.js +1 -0
  21. package/apps/runtime/dist/services/spaces.js +1 -0
  22. package/apps/runtime/dist/services/translations.js +303 -0
  23. package/apps/runtime/dist/services/wizards.js +3 -1
  24. package/apps/runtime/dist/texts.js +2 -0
  25. package/apps/runtime/dist/widgets/runtime.js +25 -2
  26. package/apps/runtime/dist/widgets/step.js +2 -0
  27. package/apps/web/dist/assets/AboutDialog-xOyf_juP.js +1 -0
  28. package/apps/web/dist/assets/{ChangelogDialog-BZ8i3wt_.js → ChangelogDialog-vNG0Smoo.js} +1 -1
  29. package/apps/web/dist/assets/ChatView-BrDkurRe.js +10 -0
  30. package/apps/web/dist/assets/EditorPage-97U-JYKn.js +3 -0
  31. package/apps/web/dist/assets/{FlowPreview-DG23aI89.js → FlowPreview-DA0sHtr7.js} +1 -1
  32. package/apps/web/dist/assets/{Harness-CFh04TZ3.js → Harness-Y9E7o83r.js} +2 -2
  33. package/apps/web/dist/assets/{NewWizardChannels-Bmo5yRCv.js → NewWizardChannels-D6v1iDOo.js} +1 -1
  34. package/apps/web/dist/assets/NewWizardPage-BzaAPMUK.js +7 -0
  35. package/apps/web/dist/assets/OnboardingPage-9xZAkhuO.js +1 -0
  36. package/apps/web/dist/assets/ProjectPage-dQ7wwxD2.js +1 -0
  37. package/apps/web/dist/assets/PublicRunner-CKa8M0Qd.js +1 -0
  38. package/apps/web/dist/assets/{ReadOnly-C_jD7KjW.js → ReadOnly-Bqk14yPD.js} +1 -1
  39. package/apps/web/dist/assets/RunDrawer-DsfZUojZ.js +4 -0
  40. package/apps/web/dist/assets/RunnerView-D0JQ1fgM.js +1 -0
  41. package/apps/web/dist/assets/SettingsPage-Dzu6Fl6b.js +1 -0
  42. package/apps/web/dist/assets/SetupPage-D2Kl-8NR.js +1 -0
  43. package/apps/web/dist/assets/SharePage-QSZxUlPt.js +1 -0
  44. package/apps/web/dist/assets/changelog-XlAAw5Gv.js +1 -0
  45. package/apps/web/dist/assets/{esm-Dzqe3TT4.js → esm-DqRvG-PU.js} +1 -1
  46. package/apps/web/dist/assets/index-BhVJwBz7.css +1 -0
  47. package/apps/web/dist/assets/index-mwmrgVFT.js +14 -0
  48. package/apps/web/dist/assets/{lock-a8rDnyg5.js → lock-D_KjsGfU.js} +1 -1
  49. package/apps/web/dist/assets/{phone-CitA6oZn.js → phone-YRxkPQk6.js} +1 -1
  50. package/apps/web/dist/assets/{settings-menu-COgM8CgB.js → settings-menu-CQgehwEX.js} +1 -1
  51. package/apps/web/dist/assets/ui-DDUCc_4v.js +137 -0
  52. package/apps/web/dist/assets/{upload-CcKFwoPd.js → upload-DDo14e2p.js} +1 -1
  53. package/apps/web/dist/index.html +3 -3
  54. package/apps/web/dist/mcp/flow.html +58 -55
  55. package/node_modules/@engenty-wizards/plugin-sdk/dist/index.d.ts +5 -2
  56. package/node_modules/@engenty-wizards/shared/dist/definition.d.ts +206 -3
  57. package/node_modules/@engenty-wizards/shared/dist/definition.js +140 -18
  58. package/node_modules/@engenty-wizards/shared/dist/projects.d.ts +11 -0
  59. package/node_modules/@engenty-wizards/shared/dist/projects.js +54 -0
  60. package/node_modules/@engenty-wizards/shared/dist/run.d.ts +68 -4
  61. package/node_modules/@engenty-wizards/shared/dist/run.js +26 -8
  62. package/node_modules/@engenty-wizards/shared/dist/runner-texts.d.ts +546 -0
  63. package/node_modules/@engenty-wizards/shared/dist/runner-texts.js +523 -0
  64. package/node_modules/@engenty-wizards/shared/dist/runners.js +1 -1
  65. package/node_modules/@engenty-wizards/shared/dist/shape.d.ts +102 -0
  66. package/node_modules/@engenty-wizards/shared/dist/shape.js +318 -0
  67. package/node_modules/@engenty-wizards/shared/dist/surface.d.ts +33 -7
  68. package/node_modules/@engenty-wizards/shared/dist/surface.js +263 -20
  69. package/node_modules/@engenty-wizards/shared/dist/texts.d.ts +11 -2
  70. package/node_modules/@engenty-wizards/shared/dist/texts.js +16 -8
  71. package/node_modules/@engenty-wizards/shared/dist/translation.d.ts +23 -0
  72. package/node_modules/@engenty-wizards/shared/dist/translation.js +154 -0
  73. package/package.json +1 -1
  74. package/apps/web/dist/assets/AboutDialog-DvkE9J4h.js +0 -1
  75. package/apps/web/dist/assets/ChatView-DIDIR0mb.js +0 -10
  76. package/apps/web/dist/assets/EditorPage-BYysznX3.js +0 -3
  77. package/apps/web/dist/assets/NewWizardPage-4n9-ZmHt.js +0 -7
  78. package/apps/web/dist/assets/OnboardingPage-DpZv9qEO.js +0 -1
  79. package/apps/web/dist/assets/ProjectPage-IQb9RFlm.js +0 -1
  80. package/apps/web/dist/assets/PublicRunner-Da0klKj-.js +0 -1
  81. package/apps/web/dist/assets/RunDrawer-FwLK67Yn.js +0 -4
  82. package/apps/web/dist/assets/RunnerView-BBuphCkK.js +0 -1
  83. package/apps/web/dist/assets/SettingsPage-CZx52Ubk.js +0 -1
  84. package/apps/web/dist/assets/SetupPage-B-o7VhbI.js +0 -1
  85. package/apps/web/dist/assets/SharePage-k2OMGOWU.js +0 -1
  86. package/apps/web/dist/assets/changelog-9oSWVuP-.js +0 -1
  87. package/apps/web/dist/assets/index-93fPRTxr.js +0 -14
  88. package/apps/web/dist/assets/index-DaKjB676.css +0 -1
  89. package/apps/web/dist/assets/ui-CeZNNuaK.js +0 -134
@@ -0,0 +1,318 @@
1
+ // SPDX-License-Identifier: FSL-1.1-MIT OR MIT
2
+ import { z } from "zod";
3
+ /**
4
+ * What an agent step's json output holds, in the kinds wizards already use: `output.schema`.
5
+ * Text, numbers, yes/no, a choice, an entry of another list by its id (`ref`), and lists — of
6
+ * these or of objects (entries) with lists of their own — every one with a label. The runtime builds the model's JSON Schema from
7
+ * it, the admin shows and edits the data by it, downloads and views read it.
8
+ *
9
+ * `output.fields`, the short form, compiles to the same shape (`shapeOfFields`); two kinds exist
10
+ * only there: `score` (a decision's levels) and `table` (rows of text or numbers, columns named
11
+ * or picked by the model).
12
+ */
13
+ /** Objects nest at most this deep, the step's own object counted. */
14
+ export const MAX_SHAPE_DEPTH = 4;
15
+ /** At most this many keys in all objects together. */
16
+ export const MAX_SHAPE_KEYS = 100;
17
+ const key = z
18
+ .string()
19
+ .regex(/^[a-zA-Z][a-zA-Z0-9_]{0,40}$/, "keys are letters, digits and _ and start with a letter");
20
+ const named = {
21
+ label: z.string().min(1).optional(),
22
+ description: z.string().optional(),
23
+ };
24
+ const leaves = [
25
+ z.object({ kind: z.literal("text"), ...named, long: z.boolean().optional() }),
26
+ z.object({ kind: z.literal("number"), ...named, unit: z.string().optional() }),
27
+ z.object({
28
+ kind: z.literal("yesno"),
29
+ ...named,
30
+ yes: z.string().optional(),
31
+ no: z.string().optional(),
32
+ }),
33
+ z.object({
34
+ kind: z.literal("choice"),
35
+ ...named,
36
+ options: z.array(z.string().min(1)).min(2).max(40),
37
+ }),
38
+ z.object({ kind: z.literal("ref"), ...named, to: key }),
39
+ ];
40
+ const LEAF_KINDS = "text, number, yesno, choice, ref";
41
+ /**
42
+ * The shape of an object `depth` deep, built level by level instead of recursively: JSON Schema
43
+ * of the definition takes no $ref, and the depth is part of the schema. Objects are the entries
44
+ * of lists (and the step's own); a field holding one object would only add a level.
45
+ */
46
+ function objectAt(depth) {
47
+ const deeper = depth < MAX_SHAPE_DEPTH ? objectAt(depth + 1) : undefined;
48
+ const of = deeper
49
+ ? z.discriminatedUnion("kind", [...leaves, deeper], {
50
+ error: `A list holds ${LEAF_KINDS} or object.`,
51
+ })
52
+ : z.discriminatedUnion("kind", [...leaves], {
53
+ error: `A list this deep holds ${LEAF_KINDS}: objects nest at most ${MAX_SHAPE_DEPTH} deep.`,
54
+ });
55
+ const list = z.object({ kind: z.literal("list"), ...named, of });
56
+ const field = z.discriminatedUnion("kind", [...leaves, list], {
57
+ error: `"kind" is one of ${LEAF_KINDS}, list.`,
58
+ });
59
+ return z.object({
60
+ kind: z.literal("object"),
61
+ ...named,
62
+ fields: z
63
+ .record(key, field)
64
+ .refine((f) => Object.keys(f).length > 0, { message: "An object has fields." }),
65
+ });
66
+ }
67
+ /** Every object of a shape with where it sits, the shape's own first. */
68
+ function objectsOf(shape, at = []) {
69
+ const out = [{ shape, at }];
70
+ for (const [k, f] of Object.entries(shape.fields)) {
71
+ if (f.kind === "list" && f.of.kind === "object") {
72
+ out.push(...objectsOf(f.of, [...at, "fields", k, "of"]));
73
+ }
74
+ }
75
+ return out;
76
+ }
77
+ /** The list a `ref` points into: a top-level list of objects with an `id`. Null when it is fine. */
78
+ function refProblem(root, to) {
79
+ const target = root.fields[to];
80
+ if (target?.kind !== "list" || target.of.kind !== "object") {
81
+ return `"ref" to "${to}": that is no list of objects at the top of the schema.`;
82
+ }
83
+ return target.of.fields.id?.kind === "text"
84
+ ? null
85
+ : `"ref" to "${to}": its entries need an "id" text field to be pointed at.`;
86
+ }
87
+ /** `output.schema`: what the step returns. */
88
+ export const outputShapeSchema = objectAt(1).superRefine((shape, ctx) => {
89
+ const objects = objectsOf(shape);
90
+ const keys = objects.reduce((n, o) => n + Object.keys(o.shape.fields).length, 0);
91
+ if (keys > MAX_SHAPE_KEYS) {
92
+ ctx.addIssue({
93
+ code: "custom",
94
+ message: `A schema has at most ${MAX_SHAPE_KEYS} keys in all; this one has ${keys}.`,
95
+ });
96
+ }
97
+ for (const o of objects) {
98
+ for (const [k, f] of Object.entries(o.shape.fields)) {
99
+ const ref = f.kind === "ref" ? f : f.kind === "list" && f.of.kind === "ref" ? f.of : null;
100
+ const problem = ref ? refProblem(shape, ref.to) : null;
101
+ if (problem) {
102
+ ctx.addIssue({ code: "custom", message: problem, path: [...o.at, "fields", k] });
103
+ }
104
+ }
105
+ }
106
+ });
107
+ /** The short form as a shape: a field per key, a list of text, a table as it is. */
108
+ export function shapeOfFields(fields) {
109
+ const one = (f) => {
110
+ const d = f.description ? { description: f.description } : {};
111
+ switch (f.kind) {
112
+ case "number":
113
+ return { kind: "number", ...d };
114
+ case "list":
115
+ return { kind: "list", ...d, of: { kind: "text" } };
116
+ case "yesno":
117
+ return { kind: "yesno", ...d };
118
+ case "choice":
119
+ return { kind: "choice", ...d, options: f.options ?? [] };
120
+ case "score":
121
+ return { kind: "score", ...d, options: f.options ?? [] };
122
+ case "table":
123
+ return { kind: "table", ...d, ...(f.columns?.length ? { columns: f.columns } : {}) };
124
+ default:
125
+ return { kind: "text", ...d };
126
+ }
127
+ };
128
+ return { kind: "object", fields: Object.fromEntries(fields.map((f) => [f.id, one(f)])) };
129
+ }
130
+ /** The key of an entry's title: its first short text other than its `id`. */
131
+ export function titleKey(shape) {
132
+ return Object.entries(shape.fields).find(([k, f]) => k !== "id" && f.kind === "text" && !f.long)?.[0];
133
+ }
134
+ /** What people read for a key. */
135
+ export function labelOf(key, shape) {
136
+ return shape.label ?? key;
137
+ }
138
+ /**
139
+ * What the model reads about a value beside its title: its description (without label: its key),
140
+ * its unit, a score's order, where a ref points. Undefined where the title says it all.
141
+ */
142
+ function about(k, s) {
143
+ const base = s.description ?? (s.label ? "" : k);
144
+ const more = s.kind === "number" && s.unit
145
+ ? `in ${s.unit}`
146
+ : s.kind === "score"
147
+ ? "lowest to highest"
148
+ : s.kind === "ref"
149
+ ? `the id of an entry of "${s.to}"`
150
+ : "";
151
+ return (base && more ? `${base} (${more})` : base || more) || undefined;
152
+ }
153
+ const CELL = z.union([z.string(), z.number(), z.null()]);
154
+ /** The data a shape describes, as Zod: what a model's answer is checked against. */
155
+ export function shapeZod(shape, k = "data") {
156
+ const description = about(k, shape);
157
+ const meta = {
158
+ ...(shape.label ? { title: shape.label } : {}),
159
+ ...(description ? { description } : {}),
160
+ };
161
+ switch (shape.kind) {
162
+ case "text":
163
+ case "ref":
164
+ return z.string().meta(meta);
165
+ case "number":
166
+ return z.number().meta(meta);
167
+ case "yesno":
168
+ return z.boolean().meta(meta);
169
+ case "choice":
170
+ case "score":
171
+ return z.enum(shape.options).meta(meta);
172
+ case "list":
173
+ return z.array(shapeZod(shape.of, k)).meta(meta);
174
+ case "object":
175
+ return z
176
+ .object(Object.fromEntries(Object.entries(shape.fields).map(([n, f]) => [n, shapeZod(f, n)])))
177
+ .meta(meta);
178
+ case "table":
179
+ return (shape.columns?.length
180
+ ? z.array(z.object(Object.fromEntries(shape.columns.map((c) => [c, CELL]))))
181
+ : // Free columns come as columns + rows, which a strict schema takes; stored as records.
182
+ z.object({ columns: z.array(z.string()), rows: z.array(z.array(CELL)) })).meta(meta);
183
+ }
184
+ }
185
+ /**
186
+ * The data as JSON Schema (draft 2020-12): every key required, no other keys — what a model's
187
+ * structured output takes, and what developers read.
188
+ */
189
+ export function shapeJsonSchema(shape, title) {
190
+ const schema = z.toJSONSchema(shapeZod(shape), { target: "draft-2020-12" });
191
+ return title && !shape.label ? { ...schema, title } : schema;
192
+ }
193
+ /** A table cell as a table takes it: a list joined by spaces, anything else as text. */
194
+ function fitCell(value) {
195
+ if (value === null || value === undefined) {
196
+ return null;
197
+ }
198
+ if (typeof value === "string" || typeof value === "number") {
199
+ return value;
200
+ }
201
+ if (Array.isArray(value) && value.every((v) => typeof v === "string" || typeof v === "number")) {
202
+ return value.join(" ");
203
+ }
204
+ return typeof value === "boolean" ? String(value) : JSON.stringify(value);
205
+ }
206
+ const isRecord = (v) => Boolean(v) && typeof v === "object" && !Array.isArray(v);
207
+ /**
208
+ * Data a model wrote itself, fitted to the shape before it is checked: a number where text
209
+ * belongs as text, a number written as text as a number; in a table a cell's list joined and a
210
+ * missing column empty. Lists stay lists everywhere else.
211
+ */
212
+ export function fitData(shape, value) {
213
+ switch (shape.kind) {
214
+ case "text":
215
+ case "ref":
216
+ return typeof value === "number" ? String(value) : value;
217
+ case "number":
218
+ return typeof value === "string" && value.trim() && Number.isFinite(Number(value))
219
+ ? Number(value)
220
+ : value;
221
+ case "list":
222
+ return Array.isArray(value) ? value.map((v) => fitData(shape.of, v)) : value;
223
+ case "object":
224
+ if (!isRecord(value)) {
225
+ return value;
226
+ }
227
+ return {
228
+ ...value,
229
+ ...Object.fromEntries(Object.entries(shape.fields)
230
+ .filter(([k]) => k in value)
231
+ .map(([k, f]) => [k, fitData(f, value[k])])),
232
+ };
233
+ case "table": {
234
+ const columns = shape.columns;
235
+ return columns?.length && Array.isArray(value)
236
+ ? value.map((row) => isRecord(row) ? Object.fromEntries(columns.map((c) => [c, fitCell(row[c])])) : row)
237
+ : value;
238
+ }
239
+ default:
240
+ return value;
241
+ }
242
+ }
243
+ /** Tables with free columns come back as columns + rows and are stored as records. */
244
+ export function recordsOf(shape, value) {
245
+ if (shape.kind === "table" && !shape.columns?.length && isRecord(value)) {
246
+ const t = value;
247
+ if (t.columns && t.rows) {
248
+ const columns = t.columns;
249
+ return t.rows.map((row) => Object.fromEntries(columns.map((c, i) => [c, row[i] ?? null])));
250
+ }
251
+ }
252
+ if (shape.kind === "object" && isRecord(value)) {
253
+ return Object.fromEntries(Object.entries(value).map(([k, v]) => {
254
+ const f = shape.fields[k];
255
+ return [k, f ? recordsOf(f, v) : v];
256
+ }));
257
+ }
258
+ if (shape.kind === "list" && Array.isArray(value)) {
259
+ return value.map((v) => recordsOf(shape.of, v));
260
+ }
261
+ return value;
262
+ }
263
+ /** What a value of a shape is, in a word or two: "list of Rezept", "choice: a | b". */
264
+ function kindLine(s) {
265
+ switch (s.kind) {
266
+ case "choice":
267
+ case "score":
268
+ return `${s.kind}: ${s.options.join(" | ")}`;
269
+ case "ref":
270
+ return `ref → /${s.to}[].id`;
271
+ case "table":
272
+ return s.columns?.length ? `table: ${s.columns.join(", ")}` : "table";
273
+ case "list":
274
+ return s.of.kind === "object" ? "list of entries" : `list of ${s.of.kind}`;
275
+ default:
276
+ return s.kind;
277
+ }
278
+ }
279
+ /** The shape as lines of paths a model binds to: "/recipes[]/title (text): Titel". */
280
+ export function shapeLines(shape, at = "") {
281
+ const out = [];
282
+ for (const [k, f] of Object.entries(shape.fields)) {
283
+ const path = `${at}/${k}`;
284
+ const words = [f.label, f.description].filter(Boolean).join(" — ");
285
+ out.push(`- ${path} (${kindLine(f)})${words ? `: ${words}` : ""}`);
286
+ if (f.kind === "list" && f.of.kind === "object") {
287
+ out.push(...shapeLines(f.of, `${path}[]`));
288
+ }
289
+ }
290
+ return out;
291
+ }
292
+ /**
293
+ * The shape at a path: keys of objects, an index (digits) into a list. `any` where the data can
294
+ * hold anything (a table's row), undefined where the shape has nothing.
295
+ */
296
+ export function shapeAt(shape, path) {
297
+ let at = shape;
298
+ for (const part of path) {
299
+ if (at.kind === "object") {
300
+ const next = at.fields[part];
301
+ if (!next) {
302
+ return undefined;
303
+ }
304
+ at = next;
305
+ }
306
+ else if (at.kind === "list" && /^\d+$/.test(part)) {
307
+ at = at.of;
308
+ }
309
+ else if (at.kind === "table") {
310
+ // A row, and in it whatever columns the model wrote.
311
+ return /^\d+$/.test(part) ? "any" : undefined;
312
+ }
313
+ else {
314
+ return undefined;
315
+ }
316
+ }
317
+ return at;
318
+ }
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { type ObjectShape } from "./shape.js";
2
3
  /**
3
4
  * Surfaces: views composed from a small catalog of native components, bound to a run's data and
4
5
  * rendered by the runner itself — themed, on phones, no iframe and nothing to pay per run. A
@@ -8,10 +9,12 @@ import { z } from "zod";
8
9
  *
9
10
  * A surface is a flat list of components with ids; the one with id "root" is drawn, containers
10
11
  * name their children by id. A prop may be a literal or `{ "path": "/json/pointer" }` into the
11
- * surface's data. A `List` or `Table` repeats over an array: inside a repeated row, a path
12
- * without a leading slash reads the row ("name"), one with a slash the whole data.
12
+ * surface's data. A `List`, `Table` or `Group` repeats over an array: inside a repeated row, a
13
+ * path without a leading slash reads the row ("name"), one with a slash the whole data. A path
14
+ * may turn what it reads into words: `map` looks the value up (`{ "true": "im Kühlschrank" }`),
15
+ * `format` sets it into a text ("{} Min").
13
16
  */
14
- export declare const SURFACE_COMPONENTS: readonly ["Column", "Text", "List", "Row", "DetailGrid", "Badge", "Grid", "Metric", "Table", "Image", "BarChart", "LineChart", "DonutChart", "Actions", "Button"];
17
+ export declare const SURFACE_COMPONENTS: readonly ["Column", "Text", "List", "Row", "DetailGrid", "Badge", "Grid", "Metric", "Table", "Image", "BarChart", "LineChart", "DonutChart", "Actions", "Button", "Tabs", "Card", "Fold", "Group", "Chips", "Checklist", "Steps", "Link", "Callout"];
15
18
  export type SurfaceComponentName = (typeof SURFACE_COMPONENTS)[number];
16
19
  export declare const SURFACE_ACTIONS: readonly ["regenerate", "open"];
17
20
  /** One component: its id, its kind, its children (ids, or a template repeated over an array). */
@@ -22,16 +25,25 @@ export declare const surfaceComponentSchema: z.ZodObject<{
22
25
  Badge: "Badge";
23
26
  BarChart: "BarChart";
24
27
  Button: "Button";
28
+ Callout: "Callout";
29
+ Card: "Card";
30
+ Checklist: "Checklist";
31
+ Chips: "Chips";
25
32
  Column: "Column";
26
33
  DetailGrid: "DetailGrid";
27
34
  DonutChart: "DonutChart";
35
+ Fold: "Fold";
28
36
  Grid: "Grid";
37
+ Group: "Group";
29
38
  Image: "Image";
30
39
  LineChart: "LineChart";
40
+ Link: "Link";
31
41
  List: "List";
32
42
  Metric: "Metric";
33
43
  Row: "Row";
44
+ Steps: "Steps";
34
45
  Table: "Table";
46
+ Tabs: "Tabs";
35
47
  Text: "Text";
36
48
  }>;
37
49
  children: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodObject<{
@@ -47,16 +59,25 @@ export declare const surfaceComponentsSchema: z.ZodArray<z.ZodObject<{
47
59
  Badge: "Badge";
48
60
  BarChart: "BarChart";
49
61
  Button: "Button";
62
+ Callout: "Callout";
63
+ Card: "Card";
64
+ Checklist: "Checklist";
65
+ Chips: "Chips";
50
66
  Column: "Column";
51
67
  DetailGrid: "DetailGrid";
52
68
  DonutChart: "DonutChart";
69
+ Fold: "Fold";
53
70
  Grid: "Grid";
71
+ Group: "Group";
54
72
  Image: "Image";
55
73
  LineChart: "LineChart";
74
+ Link: "Link";
56
75
  List: "List";
57
76
  Metric: "Metric";
58
77
  Row: "Row";
78
+ Steps: "Steps";
59
79
  Table: "Table";
80
+ Tabs: "Tabs";
60
81
  Text: "Text";
61
82
  }>;
62
83
  children: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodObject<{
@@ -74,10 +95,10 @@ export interface SurfaceIssue {
74
95
  }
75
96
  /**
76
97
  * What can be wrong with a surface: an unknown or missing component, no root, a child that is
77
- * not there, an id twice, a loop, a prop missing, and — given the data keys a run will provide —
78
- * an absolute path into data nothing provides.
98
+ * not there, an id twice, a loop, a prop missing, and — given the data keys a run will provide,
99
+ * or the step's schema — a path into data nothing provides.
79
100
  */
80
- export declare function validateSurface(raw: unknown, dataKeys?: string[]): SurfaceIssue[];
101
+ export declare function validateSurface(raw: unknown, data?: string[] | ObjectShape): SurfaceIssue[];
81
102
  /** A JSON pointer into data: "/suppliers/0/name". */
82
103
  export declare function pointer(data: unknown, path: string): unknown;
83
104
  /**
@@ -85,5 +106,10 @@ export declare function pointer(data: unknown, path: string): unknown;
85
106
  * from the row a List or Table repeats over.
86
107
  */
87
108
  export declare function bound(value: unknown, data: unknown, row?: unknown): unknown;
109
+ /** A value turned into words as its path says: looked up in `map`, set into `format`. */
110
+ export declare function worded(raw: unknown, spec: {
111
+ map?: unknown;
112
+ format?: unknown;
113
+ }): unknown;
88
114
  /** What a model needs to compose a surface: the catalog, briefly. */
89
- export declare const SURFACE_GUIDE = "A surface is a flat JSON array of components; the one with id \"root\" is drawn. Containers list their children by id.\nComponents and props (a prop may be a literal or {\"path\": \"/key/...\"} into the data):\n- Column { children: [ids] } \u2014 vertical stack (use for root)\n- Text { text, variant?: \"h3\"|\"h4\"|\"body\"|\"muted\" }\n- List { children: {\"path\": \"/rows\", \"componentId\": \"<a Row id>\"} } \u2014 one Row per array entry; inside, paths without \"/\" read the entry (\"name\")\n- Row { title, subtitle?, meta?, badge?, image? }\n- DetailGrid { rows: [{ label, value }] } \u2014 label/value facts\n- Badge { label, tone?: \"default\"|\"success\"|\"warning\" }\n- Grid { children: [ids], columns?: 2|3|4 } \u2014 tiles side by side\n- Metric { label, value, caption? } \u2014 a key figure\n- Table { rows: {\"path\": \"/rows\"}, columns?: [{ key, label }] }\n- Image { src: an asset id or {\"path\": ...}, alt? }\n- BarChart / LineChart { title?, points: [{ label, value }] or {\"path\": ...}, labelKey?, valueKey? }\n- DonutChart { title?, slices: [{ label, value }] or {\"path\": ...}, labelKey?, valueKey? }\n (labelKey / valueKey: the columns of bound rows that hold the label and the number, e.g. \"name\", \"price\")\n- Actions { children: [Button ids] } / Button { label, action: \"regenerate\"|\"open\" }\nKeep it small: what the person needs to see first, then the details. Never invent numbers: bind them by path.";
115
+ export declare const SURFACE_GUIDE = "A surface is a flat JSON array of components; the one with id \"root\" is drawn. Containers list their children by id.\nComponents and props (a prop may be a literal or {\"path\": \"/key/...\"} into the data):\n- Column { children: [ids] } \u2014 vertical stack (use for root); Column and Grid may also repeat one component per entry: children {\"path\", \"componentId\"}\n- Text { text, variant?: \"h3\"|\"h4\"|\"lead\"|\"body\"|\"muted\" } \u2014 \"lead\": the one opening line in the wizard's voice\n- List { children: {\"path\": \"/rows\", \"componentId\": \"<a Row id>\"} } \u2014 one Row per array entry; inside, paths without \"/\" read the entry (\"name\")\n- Row { title, subtitle?, meta?, badge?, image? }\n- DetailGrid { rows: [{ label, value }] } \u2014 label/value facts\n- Badge { label, tone?: \"default\"|\"success\"|\"warning\"|\"accent\" }\n- Grid { children: [ids], columns?: 2|3|4, min?: rem } \u2014 tiles side by side; min: the narrowest a tile gets (14 for cards, so a phone shows one)\n- Metric { label, value, caption? } \u2014 a key figure\n- Table { rows: {\"path\": \"/rows\"}, columns?: [{ key, label }] }\n- Image { src: an asset id or {\"path\": ...}, alt? }\n- BarChart / LineChart { title?, points: [{ label, value }] or {\"path\": ...}, labelKey?, valueKey? }\n- DonutChart { title?, slices: [{ label, value }] or {\"path\": ...}, labelKey?, valueKey? }\n (labelKey / valueKey: the columns of bound rows that hold the label and the number, e.g. \"name\", \"price\")\n- Actions { children: [Button ids] } / Button { label, action: \"regenerate\"|\"open\" }\n- Tabs { tabs: [{ label, count?, child: id }] } \u2014 two to four views of one result; count may bind a list (shows its length)\n- Card { title, eyebrow?, subtitle?, meta?: [values], image?, children?: [ids] } \u2014 one thing: a day, a recipe, an offer\n- Fold { title, open?, children: [ids] } \u2014 details that open on a tap\n- Group { children: {\"path\", \"componentId\"}, by: \"<key>\", order?: [values] } \u2014 entries under a heading per value of a key\n- Chips { items, labelKey?, flagKey?, flag? } \u2014 short things at a glance; flagKey marks some (\"bald\")\n- Checklist { items, labelKey?, detailKey?, noteKey?, noteFormat?, noteMap?, groupKey?, groupOrder?, share? } \u2014 things to get or do; ticks are kept; a yes/no note needs noteMap: {\"true\": \"da\", \"false\": \"einkaufen\"}\n- Steps { items, textKey?, minutesKey? } \u2014 ordered work, numbered, with times\n- Link { title, subtitle?, eyebrow?, meta?, look?: \"row\"|\"card\", items: {\"path\": \"/recipes\"}, key?: \"id\", value: {\"path\": \"recipe_id\"}, view: id } \u2014 opens the entry of items whose key is value in a sheet, drawn by view; title, subtitle and meta read that entry, eyebrow the row the Link sits in\n- Callout { text, title?, tone?: \"tip\"|\"note\"|\"warning\" }\nA path may put its value into words: {\"path\": \"in_fridge\", \"map\": {\"true\": \"da\", \"false\": \"einkaufen\"}}, {\"path\": \"minutes\", \"format\": \"{} Min\"}.\nDesign it like an app, not a report: lead with a headline, the key figure or the chips; one Card per thing, never a Table of things; ordered work is Steps, things to get are a Checklist, sets are grouped by their key. A Table only for figures people compare across columns.\nKeep it small: what the person needs to see first, then the details. Never invent numbers: bind them by path.";