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.
- package/apps/runtime/dist/authoring/guide.js +63 -7
- package/apps/runtime/dist/credits/estimate.js +1 -1
- package/apps/runtime/dist/db/control-schema.js +10 -0
- package/apps/runtime/dist/db/migrations/control/0010_tan_snowbird.sql +6 -0
- package/apps/runtime/dist/db/migrations/control/meta/0010_snapshot.json +600 -0
- package/apps/runtime/dist/db/migrations/control/meta/_journal.json +7 -0
- package/apps/runtime/dist/db/migrations/tenant/0016_even_trish_tilby.sql +12 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0016_snapshot.json +3326 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/_journal.json +7 -0
- package/apps/runtime/dist/db/schema.js +20 -0
- package/apps/runtime/dist/engine/requirements.js +1 -1
- package/apps/runtime/dist/engine/runner.js +72 -7
- package/apps/runtime/dist/engine/steps.js +169 -126
- package/apps/runtime/dist/model-errors.js +20 -7
- package/apps/runtime/dist/render/downloads.js +16 -3
- package/apps/runtime/dist/render/shaped.js +137 -0
- package/apps/runtime/dist/routes/runs.js +228 -25
- package/apps/runtime/dist/routes/studio.js +41 -3
- package/apps/runtime/dist/services/brand.js +2 -1
- package/apps/runtime/dist/services/cloud.js +1 -0
- package/apps/runtime/dist/services/spaces.js +1 -0
- package/apps/runtime/dist/services/translations.js +303 -0
- package/apps/runtime/dist/services/wizards.js +3 -1
- package/apps/runtime/dist/texts.js +2 -0
- package/apps/runtime/dist/widgets/runtime.js +25 -2
- package/apps/runtime/dist/widgets/step.js +2 -0
- package/apps/web/dist/assets/AboutDialog-xOyf_juP.js +1 -0
- package/apps/web/dist/assets/{ChangelogDialog-BZ8i3wt_.js → ChangelogDialog-vNG0Smoo.js} +1 -1
- package/apps/web/dist/assets/ChatView-BrDkurRe.js +10 -0
- package/apps/web/dist/assets/EditorPage-97U-JYKn.js +3 -0
- package/apps/web/dist/assets/{FlowPreview-DG23aI89.js → FlowPreview-DA0sHtr7.js} +1 -1
- package/apps/web/dist/assets/{Harness-CFh04TZ3.js → Harness-Y9E7o83r.js} +2 -2
- package/apps/web/dist/assets/{NewWizardChannels-Bmo5yRCv.js → NewWizardChannels-D6v1iDOo.js} +1 -1
- package/apps/web/dist/assets/NewWizardPage-BzaAPMUK.js +7 -0
- package/apps/web/dist/assets/OnboardingPage-9xZAkhuO.js +1 -0
- package/apps/web/dist/assets/ProjectPage-dQ7wwxD2.js +1 -0
- package/apps/web/dist/assets/PublicRunner-CKa8M0Qd.js +1 -0
- package/apps/web/dist/assets/{ReadOnly-C_jD7KjW.js → ReadOnly-Bqk14yPD.js} +1 -1
- package/apps/web/dist/assets/RunDrawer-DsfZUojZ.js +4 -0
- package/apps/web/dist/assets/RunnerView-D0JQ1fgM.js +1 -0
- package/apps/web/dist/assets/SettingsPage-Dzu6Fl6b.js +1 -0
- package/apps/web/dist/assets/SetupPage-D2Kl-8NR.js +1 -0
- package/apps/web/dist/assets/SharePage-QSZxUlPt.js +1 -0
- package/apps/web/dist/assets/changelog-XlAAw5Gv.js +1 -0
- package/apps/web/dist/assets/{esm-Dzqe3TT4.js → esm-DqRvG-PU.js} +1 -1
- package/apps/web/dist/assets/index-BhVJwBz7.css +1 -0
- package/apps/web/dist/assets/index-mwmrgVFT.js +14 -0
- package/apps/web/dist/assets/{lock-a8rDnyg5.js → lock-D_KjsGfU.js} +1 -1
- package/apps/web/dist/assets/{phone-CitA6oZn.js → phone-YRxkPQk6.js} +1 -1
- package/apps/web/dist/assets/{settings-menu-COgM8CgB.js → settings-menu-CQgehwEX.js} +1 -1
- package/apps/web/dist/assets/ui-DDUCc_4v.js +137 -0
- package/apps/web/dist/assets/{upload-CcKFwoPd.js → upload-DDo14e2p.js} +1 -1
- package/apps/web/dist/index.html +3 -3
- package/apps/web/dist/mcp/flow.html +58 -55
- package/node_modules/@engenty-wizards/plugin-sdk/dist/index.d.ts +5 -2
- package/node_modules/@engenty-wizards/shared/dist/definition.d.ts +206 -3
- package/node_modules/@engenty-wizards/shared/dist/definition.js +140 -18
- package/node_modules/@engenty-wizards/shared/dist/projects.d.ts +11 -0
- package/node_modules/@engenty-wizards/shared/dist/projects.js +54 -0
- package/node_modules/@engenty-wizards/shared/dist/run.d.ts +68 -4
- package/node_modules/@engenty-wizards/shared/dist/run.js +26 -8
- package/node_modules/@engenty-wizards/shared/dist/runner-texts.d.ts +546 -0
- package/node_modules/@engenty-wizards/shared/dist/runner-texts.js +523 -0
- package/node_modules/@engenty-wizards/shared/dist/runners.js +1 -1
- package/node_modules/@engenty-wizards/shared/dist/shape.d.ts +102 -0
- package/node_modules/@engenty-wizards/shared/dist/shape.js +318 -0
- package/node_modules/@engenty-wizards/shared/dist/surface.d.ts +33 -7
- package/node_modules/@engenty-wizards/shared/dist/surface.js +263 -20
- package/node_modules/@engenty-wizards/shared/dist/texts.d.ts +11 -2
- package/node_modules/@engenty-wizards/shared/dist/texts.js +16 -8
- package/node_modules/@engenty-wizards/shared/dist/translation.d.ts +23 -0
- package/node_modules/@engenty-wizards/shared/dist/translation.js +154 -0
- package/package.json +1 -1
- package/apps/web/dist/assets/AboutDialog-DvkE9J4h.js +0 -1
- package/apps/web/dist/assets/ChatView-DIDIR0mb.js +0 -10
- package/apps/web/dist/assets/EditorPage-BYysznX3.js +0 -3
- package/apps/web/dist/assets/NewWizardPage-4n9-ZmHt.js +0 -7
- package/apps/web/dist/assets/OnboardingPage-DpZv9qEO.js +0 -1
- package/apps/web/dist/assets/ProjectPage-IQb9RFlm.js +0 -1
- package/apps/web/dist/assets/PublicRunner-Da0klKj-.js +0 -1
- package/apps/web/dist/assets/RunDrawer-FwLK67Yn.js +0 -4
- package/apps/web/dist/assets/RunnerView-BBuphCkK.js +0 -1
- package/apps/web/dist/assets/SettingsPage-CZx52Ubk.js +0 -1
- package/apps/web/dist/assets/SetupPage-B-o7VhbI.js +0 -1
- package/apps/web/dist/assets/SharePage-k2OMGOWU.js +0 -1
- package/apps/web/dist/assets/changelog-9oSWVuP-.js +0 -1
- package/apps/web/dist/assets/index-93fPRTxr.js +0 -14
- package/apps/web/dist/assets/index-DaKjB676.css +0 -1
- 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
|
|
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
|
-
/**
|
|
469
|
-
|
|
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
|
-
|
|
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
|
-
/**
|
|
591
|
-
|
|
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
|
}
|