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
@@ -136,8 +136,9 @@ type AgentStep = {
136
136
  tools: ("web_search"|"web_fetch"|"browser"|"sandbox"|"image"|"http"|"pages")[]
137
137
  mcp?: string[] // ids of the project's MCP servers this step may use
138
138
  connections?: string[] // ids of wizard connections this step may read (mail → mail_search, mail_read, mail_save)
139
- output: { format: "text"|"markdown"|"json", fields?: { id, kind: "text"|"number"|"list"|"table"|"yesno"|"choice"|"score", description, columns?: string[], options?: string[] }[], surface?: boolean }
139
+ output: { format: "text"|"markdown"|"json", fields?: { id, kind: "text"|"number"|"list"|"table"|"yesno"|"choice"|"score", description, columns?: string[], options?: string[] }[], schema?: Schema, surface?: SurfaceComponent[] | true }
140
140
  // "score" answers its description on the scale in options, lowest first ("low", "medium", "high")
141
+ // fields: one level of keys (the short form). schema: nested data, below. One of the two, not both.
141
142
  each?: "steps.<id>.<key>" | "lists.<id>"
142
143
  // ONE RUN PER ENTRY (at most 20): the rows of a table output, the entries of a list output, or the
143
144
  // rows of a stored list; the instructions read {{item.<column>}}, {{index}}, {{count}}. The output is
@@ -172,9 +173,45 @@ type SurfaceStep = {
172
173
  candidates?: { id, description, components: SurfaceComponent[] (the first has the piece's id), group?, required? }[]
173
174
  groups?: { id, instructions, optional? }[]
174
175
  }
176
+ // SCHEMA (json only): nested data — lists of entries that hold lists of their own (days with meals, recipes with
177
+ // ingredients and steps). Its root is { kind: "object", label, fields: { <key>: Field } }; a Field is
178
+ // { kind: "text", label, description?, long?: true } long: paragraphs, not a line
179
+ // { kind: "number", label, description?, unit?: "Min" }
180
+ // { kind: "yesno", label, description?, yes?: "da", no?: "einkaufen" } (yes/no: the words people read)
181
+ // { kind: "choice", label, description?, options: string[] }
182
+ // { kind: "ref", label, description?, to: "<top-level list key>" } the id of an entry of that list; its entries
183
+ // need { id: { kind: "text" } }
184
+ // { kind: "list", label, description?, of: Field-without-list | { kind: "object", label, fields: { … } } }
185
+ // Labels are in the wizard's language: column heads, part titles, "add <label>" in the admin's editor. Description:
186
+ // what the model is to put there. Objects nest at most 4 deep (the root counted), at most 100 keys in all.
187
+ // The model gets it as a strict JSON Schema: every key is always filled.
188
+ // DESIGN THE VIEW FIRST, then the schema it needs: write output.surface (Tabs, Card, Fold, Group, Chips, Checklist,
189
+ // Steps, Link, Callout …) for what the person should see, then give the schema exactly the lists and keys that
190
+ // view binds — nothing it does not show or a later step does not read. One step returns the whole tree.
191
+ // Paths: {"path": "/recipes"} from the data; inside a repeated entry "title" or "meals" reads the entry; a Link's
192
+ // view reads the entry it opens. The view is checked against the schema.
193
+ // Later steps read top-level keys ({{steps.id.key}}, optionsFrom, each, conditions); a list of entries is a table
194
+ // there ("steps.id.recipes.title"). Downloads: xlsx one sheet per top-level list, csv the first list of flat
195
+ // entries, md and pdf the whole tree.
196
+ // Example (Kühlschrank-Koch): { kind: "object", label: "Menüplan", fields: {
197
+ // summary: { kind: "text", label: "Zusammenfassung", long: true },
198
+ // days: { kind: "list", label: "Tage", of: { kind: "object", label: "Tag", fields: {
199
+ // label: { kind: "text", label: "Tag" }, theme: { kind: "text", label: "Motto" },
200
+ // meals: { kind: "list", label: "Mahlzeiten", of: { kind: "object", label: "Mahlzeit", fields: {
201
+ // slot: { kind: "choice", label: "Mahlzeit", options: ["Frühstück", "Mittag", "Abend"] },
202
+ // recipe_id: { kind: "ref", label: "Rezept", to: "recipes" } } } } } } },
203
+ // recipes: { kind: "list", label: "Rezepte", of: { kind: "object", label: "Rezept", fields: {
204
+ // id: { kind: "text", label: "Kennung" }, title: { kind: "text", label: "Titel" },
205
+ // time_minutes: { kind: "number", label: "Zeit", unit: "Min" },
206
+ // steps: { kind: "list", label: "Zubereitung", of: { kind: "object", label: "Schritt", fields: {
207
+ // text: { kind: "text", label: "Schritt", long: true }, minutes: { kind: "number", label: "Dauer", unit: "Min" } } } } } } } } }
208
+ // with a view: Tabs over a Grid of day Cards (each meal a Link to its recipe) and a Grid of recipe Links whose
209
+ // view is a Card, a Fold of ingredients and the Steps.
175
210
  // A SURFACE is a native view in the runner — facts, a list of results, key figures, a chart — themed, on phones, free per run.
176
211
  // Prefer it to a widget for such views; a widget is for what the catalog cannot show (a map, an animation).
177
- // An agent step with output.format "json" can also set output.surface: true: it composes such a view of its own result.
212
+ // An agent step with output.format "json" can also carry such a view of its own result: output.surface: [components],
213
+ // written once and bound to its fields or schema by path ({"path": "/<key>"}; a step with "each": "/rows"). Prefer it to
214
+ // output.surface: true, which has a model compose a new view on every run.
178
215
  // Reviews and the result show a step's view instead of its plain table.
179
216
  type GenerateStep = {
180
217
  type: "generate"
@@ -200,7 +237,10 @@ type WidgetStep = {
200
237
  entry: string // workspace path of the widget's HTML, e.g. "weather/index.html"
201
238
  data: { [key]: string } // what the widget gets as wizard.data: key → fieldId | steps.stepId | steps.stepId.key | brand.name | facts.key | today
202
239
  sample?: string // workspace path of example data (same shape as data) for previews
203
- size?: { width, height } // design size in px, default 1280×720 (9:16 → 1080×1920)
240
+ layout?: "fixed"|"page" // fixed (default): drawn at size and scaled to the page — maps, charts, films.
241
+ // page: part of the run page, as wide as it and as tall as its content, never scaled —
242
+ // a result people scroll and use (a cook mode, a configurator, a planner)
243
+ size?: { width, height } // design size in px, default 1280×720 (9:16 → 1080×1920); exports render at it
204
244
  video?: boolean // a FILM: the timeline is rendered to an MP4 WITH SOUND when the step runs; the person sees and
205
245
  // downloads that video. data may then name generate steps: "steps.<id>" of an image / video / voice
206
246
  // step arrives as a URL (a list of URLs for a step with "each").
@@ -238,6 +278,7 @@ type ResultStep = { type: "result", message?: string, deliverables: { from: step
238
278
  // film widget (video: true)→mp4 png · film→mp4
239
279
  // widget→html png pdf mp4 json (mp4 only when the widget registers a timeline)
240
280
  // agent text/markdown→md txt docx pdf html · agent json→json csv xlsx md (xlsx: one sheet per table)
281
+ // agent json with schema→json csv xlsx md pdf
241
282
  // agent, any format→zip: the FILES the step kept with mail_save / browser_download
242
283
  // lists.<id>→xlsx csv json md · zip (a list with check.file: the kept files its rows name, plus the list)
243
284
 
@@ -252,9 +293,18 @@ A template may only use fields asked and steps run EARLIER.`;
252
293
  export const PRINCIPLES = `
253
294
  How good wizards look:
254
295
  - For non-technical end users. Plain words, friendly titles phrased as questions ("What is the post for?").
296
+ - The wizard speaks for itself: the first page invites in its own voice ("Show me your fridge."), every field's help says in
297
+ one line why it is asked ("For the amounts in the recipes"), every step that takes a while has a "working" line that
298
+ names what happens ("Looking into your fridge…"). Choices with up to five options are buttons, not a dropdown.
299
+ - The result is what people come for: design how it looks first, then the data it needs. A json step's result gets a view
300
+ (output.surface, written once) or a widget; give its data the fields only the view uses — a yes/no that picks a colour
301
+ ("in_fridge"), a key that groups ("category"), one human line (a summary, a tip, a theme per day) — and links instead of
302
+ copies (a meal names its recipe's id). Things are cards, ordered work is Steps, things to get or do are a Checklist;
303
+ never a table of things for a person — a table only for figures compared across columns (prices, dates).
255
304
  - A page whose fields depend on an earlier answer uses "when" on its fields, never one copy of the page per answer and a branch to each copy. Choices that depend on what an earlier step found use "optionsFrom", never one page per possible result.
256
305
  - Few pages, 1–5 fields each, one theme per page. Only ask what the result really needs; prefer selects with sensible defaults.
257
- - Put a review step before anything expensive (video) and before the final result, so people can correct or regenerate.
306
+ - Put a review step before anything expensive (video) and before anything written elsewhere (mail, CRM), so people can
307
+ correct or regenerate. A result people can simply make again needs no review before it.
258
308
  - Money/totals: use an "items" field with vat — the runner computes totals; documents must use {{items}} verbatim. Never let a model compute totals.
259
309
  - Facts from the web need an agent step with web_search + web_fetch BEFORE writing; documents then cite those notes.
260
310
  - Live data with a free public JSON API (weather and wind: api.open-meteo.com; sunrise/sunset is in it too) → an agent step with the http tool and model "standard" that calls the API and returns json. Far cheaper and more exact than web research; use web research only when no API exists.
@@ -295,8 +345,12 @@ one-off pages stay "generate" steps. A widget is code in the wizard's WORKSPACE
295
345
  <script src="lib/name.min.js">. Fetch reference data ONCE now (e.g. lake outlines
296
346
  from nominatim.openstreetmap.org/search?q=…&format=json&polygon_geojson=1&polygon_threshold=0.0005)
297
347
  and save it as a workspace file.
298
- - Runtime, window.wizard: data · brand {name, accent, logo, colors [{name, value}], facts {key: value}} · ai "generated"|"edited"|null · mode "view"|"export" · file(path) ·
299
- json(path) · url(path) (data URL for images/fonts) · ready() · timeline({ duration, seek, poster }).
348
+ - Runtime, window.wizard: data · brand {name, accent, logo, colors [{name, value}], facts {key: value}, theme} · ai "generated"|"edited"|null · mode "view"|"export" · file(path) ·
349
+ json(path) · url(path) (data URL for images/fonts) · ready() · timeline({ duration, seek, poster }) ·
350
+ state (what the person did last time, kept with the run) · save(state) (keep ticks, an open tab, a step reached: small JSON) ·
351
+ share(text) (the device's share sheet).
352
+ - layout "page": let the document grow with its content (no height: 100vh, no fixed body height) and lay out for phones
353
+ first; the page sets the frame's height from it. Keep what the person does with wizard.save and start from wizard.state.
300
354
  - Animation: call wizard.timeline({ duration: seconds, seek: t => draw(t) }) and drive your own
301
355
  play/pause and <input type=range> scrubber from it. seek(t) must draw time t synchronously — the MP4
302
356
  export steps through it frame by frame. In mode "export" hide all controls and do not autoplay.
@@ -308,7 +362,9 @@ one-off pages stay "generate" steps. A widget is code in the wizard's WORKSPACE
308
362
  arrive as URLs, not inlined.
309
363
  - Call wizard.ready() once the first frame is drawn. Lay out for the step's size and scale to fit
310
364
  the window on every "resize" event — the window can be 0×0 when your script first runs.
311
- - Plain HTML/CSS/JS, SVG or canvas. Calm design, one accent colour (brand.accent), legible labels.
365
+ - Plain HTML/CSS/JS, SVG or canvas. Take the look from wizard.brand.theme: accent for actions, second for meaning (done,
366
+ there, a tip), background for the ground, display for headings, body for text (system fonts behind them); dark: a dark
367
+ ground. Cards for things, generous spacing, legible labels; no table of things.
312
368
  - After writing, ALWAYS run check_widget, read its errors and look at the screenshot; fix until clean.`;
313
369
  export function connectorsLine(connectors) {
314
370
  const lines = connectors.map((c) => {
@@ -61,7 +61,7 @@ function stepFormula(step, catalog) {
61
61
  if (step.output.format === "json") {
62
62
  credits += textCredits(cls("classifier"), TOKENS.structure);
63
63
  }
64
- if (step.output.surface) {
64
+ if (step.output.surface === true) {
65
65
  credits += textCredits(cls("standard"), TOKENS.structure);
66
66
  }
67
67
  return credits;
@@ -80,6 +80,16 @@ export const setting = sqliteTable("setting", {
80
80
  key: text("key").primaryKey(),
81
81
  value: text("value", { mode: "json" }).$type().notNull(),
82
82
  });
83
+ /**
84
+ * The runner's own words in a language outside `LANGS`, translated once for the runtime:
85
+ * `source` is the hash of the English tables they were made from.
86
+ */
87
+ export const uiTranslation = sqliteTable("ui_translation", {
88
+ lang: text("lang").primaryKey(),
89
+ source: text("source").notNull(),
90
+ texts: text("texts", { mode: "json" }).$type().notNull(),
91
+ createdAt: createdAt(),
92
+ });
83
93
  // --- marketplace -----------------------------------------------------------------
84
94
  // The marketplace is an app of its own (docs/marketplace-contract.md). What this runtime keeps
85
95
  // of it for offline use: its starters and what people starred, as the marketplace exports them.
@@ -0,0 +1,6 @@
1
+ CREATE TABLE `ui_translation` (
2
+ `lang` text PRIMARY KEY NOT NULL,
3
+ `source` text NOT NULL,
4
+ `texts` text NOT NULL,
5
+ `created_at` integer DEFAULT (unixepoch() * 1000) NOT NULL
6
+ );