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
@@ -190,6 +190,60 @@ export const FONT_USE_WORDS = {
190
190
  images: "images and social graphics",
191
191
  video: "video",
192
192
  };
193
+ const BACKGROUND_WORDS = /hintergrund|background|untergrund|grund|paper|papier|fläche|surface/i;
194
+ const TEXT_WORDS = /\btext|schrift|ink|typo/i;
195
+ const DISPLAY_ROLES = /head|überschrift|display|titel|title|headline/i;
196
+ const BODY_ROLES = /body|fließ|copy|lauf|text|ui|interface/i;
197
+ /** The channels of a #rrggbb colour, 0–255; null for anything else. */
198
+ function rgbOf(hex) {
199
+ const m = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(hex.trim());
200
+ return m
201
+ ? [Number.parseInt(m[1], 16), Number.parseInt(m[2], 16), Number.parseInt(m[3], 16)]
202
+ : null;
203
+ }
204
+ /** How light a colour looks, 0–1 (relative luminance). */
205
+ function luminance(hex) {
206
+ const rgb = rgbOf(hex);
207
+ if (!rgb) {
208
+ return 1;
209
+ }
210
+ const [r, g, b] = rgb.map((c) => {
211
+ const v = c / 255;
212
+ return v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
213
+ });
214
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
215
+ }
216
+ /** A colour with a hue of its own, not a grey, white or black. */
217
+ function colourful(hex) {
218
+ const rgb = rgbOf(hex);
219
+ return rgb ? Math.max(...rgb) - Math.min(...rgb) > 40 : false;
220
+ }
221
+ /**
222
+ * How a wizard's pages look, from the brand: the first standard colour is the accent, a standard
223
+ * colour named as the ground ("Hintergrund") the background, the next colourful one the second
224
+ * colour; the typeface for headings and the one for text by their roles. What the brand does not
225
+ * say stays the runner's own.
226
+ */
227
+ export function runTheme(brand) {
228
+ const standard = (brand.colors ?? []).filter((c) => !c.section && rgbOf(c.value));
229
+ const accent = standard[0]?.value ?? null;
230
+ const background = standard.find((c) => BACKGROUND_WORDS.test(c.name))?.value ?? null;
231
+ const second = standard
232
+ .slice(1)
233
+ .find((c) => c.value !== background && !TEXT_WORDS.test(c.name) && colourful(c.value))
234
+ ?.value ?? null;
235
+ const fonts = brand.fonts ?? [];
236
+ const display = fonts.find((f) => DISPLAY_ROLES.test(f.role)) ?? fonts[0];
237
+ const body = fonts.find((f) => f !== display && BODY_ROLES.test(f.role)) ?? fonts.find((f) => f !== display);
238
+ return {
239
+ accent,
240
+ second,
241
+ background,
242
+ display: display?.family ?? null,
243
+ body: body?.family ?? null,
244
+ dark: background ? luminance(background) < 0.18 : false,
245
+ };
246
+ }
193
247
  /** The font's licence as a model reads it: ", licence …, may only be used for …". */
194
248
  export function fontLicenseWords(f) {
195
249
  return [
@@ -1,7 +1,7 @@
1
- import type { CardOption, Format, Lang, ModelClass, Step, WizardDefinition } from "./definition.js";
1
+ import { type CardOption, type Format, type Lang, type ModelClass, type Step, type WizardDefinition } from "./definition.js";
2
2
  import type { ConnectionView, ListDef, ListRow } from "./store.js";
3
3
  import type { Surface } from "./surface.js";
4
- import { type Gap, type Texts } from "./texts.js";
4
+ import { type Gap, type Texts, type TextTable } from "./texts.js";
5
5
  export type RunStatus = "waiting_input" | "running" | "done" | "failed" | "cancelled";
6
6
  export interface AssetRef {
7
7
  id: string;
@@ -23,6 +23,11 @@ export interface StepOutput {
23
23
  /** A view from the surface catalog and the data it is bound to. */
24
24
  surface?: Surface;
25
25
  assets?: AssetRef[];
26
+ /**
27
+ * What the person did in the step's view, kept with the run: a checklist's ticks by component
28
+ * id, a widget's own state (`wizard.save`).
29
+ */
30
+ view?: Record<string, unknown>;
26
31
  /** Widgets: what loading it with this run's data showed. */
27
32
  widget?: {
28
33
  /** Seconds of animation the widget registered for video export; null = a still widget. */
@@ -61,6 +66,11 @@ export interface RunState {
61
66
  * pages stay as they are; the copy goes with the run.
62
67
  */
63
68
  memory?: Record<string, string>;
69
+ /**
70
+ * Finished results translated on request, by language and step: the text the person reads in
71
+ * place of the step's own (`StepOutput.text`); the step's output stays as it was made.
72
+ */
73
+ translated?: Record<string, Record<string, string>>;
64
74
  }
65
75
  /** What a decision over a step's `ask` branches found. */
66
76
  export interface BranchDecision {
@@ -172,6 +182,7 @@ declare const NOTES_EN: {
172
182
  };
173
183
  readonly videoRendering: "Rendering the video – this usually takes 1–3 minutes.";
174
184
  readonly photo: "Looking at photo {n} of {total}";
185
+ readonly found: "{label}: {name}";
175
186
  readonly listen: "Listening to “{label}”…";
176
187
  readonly listenFailed: "“{label}” couldn’t be transcribed.";
177
188
  readonly filmCutting: "Cutting the film – this takes about a minute.";
@@ -225,6 +236,9 @@ declare const NOTES_EN: {
225
236
  readonly filmFix: "The film didn’t render – Claude Code is fixing it";
226
237
  readonly filmResume: "Picking the film up where it stopped";
227
238
  readonly wizardUnavailable: "This wizard isn’t available right now.";
239
+ readonly translationFailed: "This wizard can’t be translated right now. It’s shown as it was written.";
240
+ readonly translationOff: "This wizard is only offered as it was written.";
241
+ readonly translationPlaceholder: "The translation of “{text}” needs every placeholder in curly brackets the original has.";
228
242
  readonly wizardLimit: "This wizard has reached its limit for today. Try again tomorrow.";
229
243
  readonly confirmHuman: "Confirm that you’re a person.";
230
244
  readonly tooManyRuns: "You’ve started a lot of runs just now. Try again in an hour.";
@@ -465,8 +479,11 @@ export type Said = string | RunNote;
465
479
  export declare function note(code: RunNoteCode, params?: Record<string, NoteGap | undefined>): RunNote;
466
480
  /** What is said, in a language. */
467
481
  export declare function saidText(said: Said, lang: Lang): string;
468
- /** A note in a language, its gaps filled; English where the language lacks the line. */
469
- export declare function noteText(note: RunNote, lang: Lang): string;
482
+ /**
483
+ * A note in a language, its gaps filled; English where the language lacks the line. A language
484
+ * outside `LANGS` reads its lines from `table`, the notes translated on request.
485
+ */
486
+ export declare function noteText(note: RunNote, lang: string, table?: TextTable): string;
470
487
  export interface RunEvent {
471
488
  id: number;
472
489
  at: string;
@@ -486,6 +503,12 @@ export interface RunView {
486
503
  wizard: Pick<WizardDefinition, "title" | "description" | "avatar" | "intro">;
487
504
  /** The language the wizard speaks: what is read aloud, and in which voice. */
488
505
  lang: Lang;
506
+ /**
507
+ * The run in the viewer's language (a wizard translated on request): every text of the view
508
+ * is written in it, the choices keep their values and show `labels[value]`. Null: the
509
+ * wizard's own words.
510
+ */
511
+ translation: RunTranslation | null;
489
512
  /** A live conversation can be started on this run: the runtime has a model for it and the wizard has live voice or video on (always in a test). */
490
513
  talk: boolean;
491
514
  /** The step the person is on (interactive) or the one running. */
@@ -539,6 +562,17 @@ export interface RunView {
539
562
  /** When the run and its shared link are deleted; null = kept. */
540
563
  expiresAt: string | null;
541
564
  }
565
+ /** How a run in the viewer's language shows. */
566
+ export interface RunTranslation {
567
+ /** The language, by its code ("fr"): any language, not only one of `LANGS`. */
568
+ lang: string;
569
+ /** What a choice shows, by its value. */
570
+ labels: Record<string, string>;
571
+ /** A model wrote some of it; false where the author wrote every text by hand. */
572
+ machine: boolean;
573
+ /** Results shown in this language, by step (`RunState.translated`). */
574
+ results: Record<string, string>;
575
+ }
542
576
  /** A shared result: what `/s/<token>` shows. Never the person's answers. */
543
577
  export interface ShareView {
544
578
  token: string;
@@ -556,6 +590,22 @@ export interface BrandView {
556
590
  name: string;
557
591
  accent: string | null;
558
592
  logoUrl: string | null;
593
+ /** How the pages look: the run page sets its tokens from it. */
594
+ theme: RunTheme;
595
+ }
596
+ /** How a wizard's pages look, taken from its space's brand (`runTheme`). Null: the runner's own. */
597
+ export interface RunTheme {
598
+ /** Buttons and marks. */
599
+ accent: string | null;
600
+ /** What is there, done or good to know: ticks, tips, group headings. */
601
+ second: string | null;
602
+ /** The ground the pages stand on; its hue tints paper and ink. */
603
+ background: string | null;
604
+ /** Typefaces as CSS names them. */
605
+ display: string | null;
606
+ body: string | null;
607
+ /** The brand stands on a dark ground. */
608
+ dark: boolean;
559
609
  }
560
610
  export interface PublicWizard {
561
611
  token: string;
@@ -577,6 +627,20 @@ export interface PublicWizard {
577
627
  kind: "page" | "channel";
578
628
  url: string | null;
579
629
  }[];
630
+ /** The language the wizard is written in: the page opens in it. */
631
+ lang: Lang;
632
+ /** The author offers the wizard in the viewer's language, translated on request. */
633
+ translatable: boolean;
634
+ /** The page in the viewer's language, where it was asked for (`?lang=`); null: the original. */
635
+ translation: Omit<RunTranslation, "results"> | null;
636
+ }
637
+ /** The runner's own words in a language outside `LANGS`: `GET /api/public/texts/:lang`. */
638
+ export interface UiTexts {
639
+ lang: string;
640
+ /** The runner's table (`packages/shared/src/runner-texts.ts`). */
641
+ runner: TextTable;
642
+ /** The run notes (`RUN_NOTES`). */
643
+ notes: TextTable;
580
644
  }
581
645
  /** What a step is expected to cost, in credits. `high` is the 90th percentile once measured. */
582
646
  export interface StepEstimate {
@@ -1,4 +1,6 @@
1
- import { textOf } from "./texts.js";
1
+ // SPDX-License-Identifier: FSL-1.1-MIT OR MIT
2
+ import { isLang, } from "./definition.js";
3
+ import { lineText, textOf } from "./texts.js";
2
4
  /**
3
5
  * What a step reports while it works, in words for each language: the runtime writes the English
4
6
  * line into the run's log, a runner shows it in the language the person reads. The runtime's
@@ -18,6 +20,8 @@ const NOTES_EN = {
18
20
  },
19
21
  videoRendering: "Rendering the video – this usually takes 1–3 minutes.",
20
22
  photo: "Looking at photo {n} of {total}",
23
+ // An entry a step has just written, while it writes the rest: "Rezepte: Shakshuka mit Feta".
24
+ found: "{label}: {name}",
21
25
  listen: "Listening to “{label}”…",
22
26
  listenFailed: "“{label}” couldn’t be transcribed.",
23
27
  filmCutting: "Cutting the film – this takes about a minute.",
@@ -65,6 +69,9 @@ const NOTES_EN = {
65
69
  filmFix: "The film didn’t render – Claude Code is fixing it",
66
70
  filmResume: "Picking the film up where it stopped",
67
71
  wizardUnavailable: "This wizard isn’t available right now.",
72
+ translationFailed: "This wizard can’t be translated right now. It’s shown as it was written.",
73
+ translationOff: "This wizard is only offered as it was written.",
74
+ translationPlaceholder: "The translation of “{text}” needs every placeholder in curly brackets the original has.",
68
75
  wizardLimit: "This wizard has reached its limit for today. Try again tomorrow.",
69
76
  confirmHuman: "Confirm that you’re a person.",
70
77
  tooManyRuns: "You’ve started a lot of runs just now. Try again in an hour.",
@@ -303,6 +310,7 @@ const NOTES_DE = {
303
310
  },
304
311
  videoRendering: "Das Video wird gerendert – das dauert meist 1–3 Minuten.",
305
312
  photo: "Sieht sich Foto {n} von {total} an",
313
+ found: "{label}: {name}",
306
314
  listen: "Hört „{label}“ an …",
307
315
  listenFailed: "„{label}“ konnte nicht verschriftlicht werden.",
308
316
  filmCutting: "Der Film wird geschnitten – das dauert etwa eine Minute.",
@@ -350,6 +358,9 @@ const NOTES_DE = {
350
358
  filmFix: "Der Film ließ sich nicht rendern – Claude Code bessert nach",
351
359
  filmResume: "Macht dort weiter, wo der Film stehen geblieben ist",
352
360
  wizardUnavailable: "Dieser Wizard ist gerade nicht verfügbar.",
361
+ translationFailed: "Dieser Wizard lässt sich gerade nicht übersetzen. Du siehst ihn so, wie er geschrieben wurde.",
362
+ translationOff: "Diesen Wizard gibt es nur so, wie er geschrieben wurde.",
363
+ translationPlaceholder: "Die Übersetzung von „{text}“ braucht jeden Platzhalter in geschweiften Klammern, den das Original hat.",
353
364
  wizardLimit: "Für heute ist das Limit dieses Wizards erreicht. Versuch es morgen wieder.",
354
365
  confirmHuman: "Bestätige kurz, dass du ein Mensch bist.",
355
366
  tooManyRuns: "Du hast gerade viele Läufe gestartet. Versuch es in einer Stunde wieder.",
@@ -578,23 +589,30 @@ export function note(code, params) {
578
589
  export function saidText(said, lang) {
579
590
  return typeof said === "string" ? said : noteText(said, lang);
580
591
  }
581
- function gapIn(value, lang) {
592
+ function gapIn(value, lang, table) {
582
593
  if (Array.isArray(value)) {
583
- return value.map((item) => gapIn(item, lang));
594
+ return value.map((item) => gapIn(item, lang, table));
584
595
  }
585
596
  if (typeof value === "object" && value !== null && "code" in value) {
586
- return noteText(value, lang);
597
+ return noteText(value, lang, table);
587
598
  }
588
599
  return value;
589
600
  }
590
- /** A note in a language, its gaps filled; English where the language lacks the line. */
591
- export function noteText(note, lang) {
601
+ /**
602
+ * A note in a language, its gaps filled; English where the language lacks the line. A language
603
+ * outside `LANGS` reads its lines from `table`, the notes translated on request.
604
+ */
605
+ export function noteText(note, lang, table) {
592
606
  if (!(note.code in NOTES_EN)) {
593
607
  return "";
594
608
  }
595
609
  const params = {};
596
610
  for (const [key, value] of Object.entries(note.params ?? {})) {
597
- params[key] = value === undefined ? undefined : gapIn(value, lang);
611
+ params[key] = value === undefined ? undefined : gapIn(value, lang, table);
612
+ }
613
+ const own = table?.[note.code];
614
+ if (own !== undefined) {
615
+ return lineText(own, lang, params);
598
616
  }
599
- return textOf(RUN_NOTES, note.code, lang, params);
617
+ return textOf(RUN_NOTES, note.code, isLang(lang) ? lang : "en", params);
600
618
  }