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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: FSL-1.1-MIT OR MIT
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { shapeAt } from "./shape.js";
|
|
3
4
|
/**
|
|
4
5
|
* Surfaces: views composed from a small catalog of native components, bound to a run's data and
|
|
5
6
|
* rendered by the runner itself — themed, on phones, no iframe and nothing to pay per run. A
|
|
@@ -9,8 +10,10 @@ import { z } from "zod";
|
|
|
9
10
|
*
|
|
10
11
|
* A surface is a flat list of components with ids; the one with id "root" is drawn, containers
|
|
11
12
|
* name their children by id. A prop may be a literal or `{ "path": "/json/pointer" }` into the
|
|
12
|
-
* surface's data. A `List` or `
|
|
13
|
-
* without a leading slash reads the row ("name"), one with a slash the whole data.
|
|
13
|
+
* surface's data. A `List`, `Table` or `Group` repeats over an array: inside a repeated row, a
|
|
14
|
+
* path without a leading slash reads the row ("name"), one with a slash the whole data. A path
|
|
15
|
+
* may turn what it reads into words: `map` looks the value up (`{ "true": "im Kühlschrank" }`),
|
|
16
|
+
* `format` sets it into a text ("{} Min").
|
|
14
17
|
*/
|
|
15
18
|
export const SURFACE_COMPONENTS = [
|
|
16
19
|
"Column",
|
|
@@ -28,9 +31,26 @@ export const SURFACE_COMPONENTS = [
|
|
|
28
31
|
"DonutChart",
|
|
29
32
|
"Actions",
|
|
30
33
|
"Button",
|
|
34
|
+
"Tabs",
|
|
35
|
+
"Card",
|
|
36
|
+
"Fold",
|
|
37
|
+
"Group",
|
|
38
|
+
"Chips",
|
|
39
|
+
"Checklist",
|
|
40
|
+
"Steps",
|
|
41
|
+
"Link",
|
|
42
|
+
"Callout",
|
|
31
43
|
];
|
|
32
44
|
/** Components that hold others by id. */
|
|
33
|
-
const CONTAINERS = new Set([
|
|
45
|
+
const CONTAINERS = new Set([
|
|
46
|
+
"Column",
|
|
47
|
+
"List",
|
|
48
|
+
"Grid",
|
|
49
|
+
"Actions",
|
|
50
|
+
"Card",
|
|
51
|
+
"Fold",
|
|
52
|
+
"Group",
|
|
53
|
+
]);
|
|
34
54
|
/** The props each component needs. */
|
|
35
55
|
const REQUIRED = {
|
|
36
56
|
Text: ["text"],
|
|
@@ -44,6 +64,15 @@ const REQUIRED = {
|
|
|
44
64
|
LineChart: ["points"],
|
|
45
65
|
DonutChart: ["slices"],
|
|
46
66
|
Button: ["label", "action"],
|
|
67
|
+
Tabs: ["tabs"],
|
|
68
|
+
Card: ["title"],
|
|
69
|
+
Fold: ["title", "children"],
|
|
70
|
+
Group: ["children", "by"],
|
|
71
|
+
Chips: ["items"],
|
|
72
|
+
Checklist: ["items"],
|
|
73
|
+
Steps: ["items"],
|
|
74
|
+
Link: ["title", "items", "value", "view"],
|
|
75
|
+
Callout: ["text"],
|
|
47
76
|
};
|
|
48
77
|
export const SURFACE_ACTIONS = ["regenerate", "open"];
|
|
49
78
|
const id = z.string().regex(/^[a-zA-Z][a-zA-Z0-9_-]{0,40}$/, "ids are letters, digits, _ and -");
|
|
@@ -57,6 +86,24 @@ export const surfaceComponentSchema = z.looseObject({
|
|
|
57
86
|
});
|
|
58
87
|
export const surfaceComponentsSchema = z.array(surfaceComponentSchema).min(1).max(200);
|
|
59
88
|
const isPath = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v) && typeof v.path === "string");
|
|
89
|
+
/**
|
|
90
|
+
* The ids a component draws: its children, the views of its tabs. A Link's view is drawn only
|
|
91
|
+
* when it is opened, so it is no part of the tree.
|
|
92
|
+
*/
|
|
93
|
+
function childIds(c) {
|
|
94
|
+
const kids = c.children;
|
|
95
|
+
const ids = Array.isArray(kids) ? [...kids] : kids ? [kids.componentId] : [];
|
|
96
|
+
const tabs = c.tabs;
|
|
97
|
+
if (Array.isArray(tabs)) {
|
|
98
|
+
for (const tab of tabs) {
|
|
99
|
+
const child = tab?.child;
|
|
100
|
+
if (typeof child === "string") {
|
|
101
|
+
ids.push(child);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return ids;
|
|
106
|
+
}
|
|
60
107
|
/** Every `{ path }` in a component's props, with whether it reads a repeated row. */
|
|
61
108
|
function pathsOf(c) {
|
|
62
109
|
const out = [];
|
|
@@ -78,12 +125,173 @@ function pathsOf(c) {
|
|
|
78
125
|
}
|
|
79
126
|
return out;
|
|
80
127
|
}
|
|
128
|
+
/** Props naming a key of the entries a component's `items` (or chart's points) holds. */
|
|
129
|
+
const ENTRY_KEYS = [
|
|
130
|
+
"labelKey",
|
|
131
|
+
"valueKey",
|
|
132
|
+
"flagKey",
|
|
133
|
+
"detailKey",
|
|
134
|
+
"noteKey",
|
|
135
|
+
"groupKey",
|
|
136
|
+
"textKey",
|
|
137
|
+
"minutesKey",
|
|
138
|
+
];
|
|
139
|
+
/** A path as parts: "/days/0/label" from the data, "meals.slot" from the entry. */
|
|
140
|
+
const partsOf = (path) => (path.startsWith("/") ? path.slice(1) : path)
|
|
141
|
+
.split(/[./]/)
|
|
142
|
+
.filter(Boolean)
|
|
143
|
+
.map((p) => p.replace(/~1/g, "/").replace(/~0/g, "~"));
|
|
144
|
+
/**
|
|
145
|
+
* The paths a surface reads, followed through the step's schema from "root": a repeated child
|
|
146
|
+
* reads an entry of the list its path names, a Link's view the entry it opens. Every path has to
|
|
147
|
+
* be in the schema, every key a component names in its entries.
|
|
148
|
+
*/
|
|
149
|
+
function shapeIssues(byId, root) {
|
|
150
|
+
const issues = [];
|
|
151
|
+
const seen = new Map();
|
|
152
|
+
/** The shape a path reads where `entry` is drawn; "any": unchecked. */
|
|
153
|
+
const resolve = (path, entry) => {
|
|
154
|
+
if (path.startsWith("/")) {
|
|
155
|
+
return shapeAt(root, partsOf(path));
|
|
156
|
+
}
|
|
157
|
+
if (entry === "any") {
|
|
158
|
+
return "any";
|
|
159
|
+
}
|
|
160
|
+
return entry ? shapeAt(entry, partsOf(path)) : undefined;
|
|
161
|
+
};
|
|
162
|
+
const say = (c, path, entry) => issues.push({
|
|
163
|
+
message: !path.startsWith("/") && !entry
|
|
164
|
+
? `"${c.id}" reads "${path}" outside a repeated entry; from the data it is "/${path.replace(/\./g, "/")}".`
|
|
165
|
+
: `"${c.id}" reads "${path}", which no data provides.`,
|
|
166
|
+
});
|
|
167
|
+
/** The entries of a list a prop binds, or undefined (and an issue) where it is no list. */
|
|
168
|
+
const entriesOf = (c, value, entry) => {
|
|
169
|
+
if (!isPath(value)) {
|
|
170
|
+
return "any";
|
|
171
|
+
}
|
|
172
|
+
const at = resolve(value.path, entry);
|
|
173
|
+
if (at === "any") {
|
|
174
|
+
return "any";
|
|
175
|
+
}
|
|
176
|
+
if (at?.kind === "list") {
|
|
177
|
+
return at.of;
|
|
178
|
+
}
|
|
179
|
+
if (at?.kind === "table") {
|
|
180
|
+
return at.columns?.length
|
|
181
|
+
? {
|
|
182
|
+
kind: "object",
|
|
183
|
+
fields: Object.fromEntries(at.columns.map((k) => [k, { kind: "text" }])),
|
|
184
|
+
}
|
|
185
|
+
: "any";
|
|
186
|
+
}
|
|
187
|
+
if (at) {
|
|
188
|
+
issues.push({ message: `"${c.id}" reads "${value.path}" as a list; it is ${at.kind}.` });
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
say(c, value.path, entry);
|
|
192
|
+
}
|
|
193
|
+
return undefined;
|
|
194
|
+
};
|
|
195
|
+
const hasKey = (shape, k) => shape === "any" || shape === undefined || shape.kind !== "object" || k in shape.fields;
|
|
196
|
+
const visit = (cid, entry) => {
|
|
197
|
+
const c = byId.get(cid);
|
|
198
|
+
const done = seen.get(cid) ?? new Set();
|
|
199
|
+
if (!c || done.has(entry)) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
done.add(entry);
|
|
203
|
+
seen.set(cid, done);
|
|
204
|
+
const props = c;
|
|
205
|
+
// What the component's items, rows or points hold, for the keys it names in them.
|
|
206
|
+
const listProp = ["items", "rows", "points", "slices"].find((k) => isPath(props[k]));
|
|
207
|
+
const items = listProp ? entriesOf(c, props[listProp], entry) : undefined;
|
|
208
|
+
for (const k of ENTRY_KEYS) {
|
|
209
|
+
const named = props[k];
|
|
210
|
+
if (typeof named === "string" && items && !hasKey(items, named)) {
|
|
211
|
+
issues.push({
|
|
212
|
+
message: `"${c.id}": "${k}" names "${named}", which its entries do not have.`,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (c.component === "Table" && Array.isArray(props.columns) && items) {
|
|
217
|
+
for (const col of props.columns) {
|
|
218
|
+
if (typeof col?.key === "string" && !hasKey(items, col.key)) {
|
|
219
|
+
issues.push({ message: `"${c.id}": no column "${col.key}" in its rows.` });
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// A Link: title, subtitle and meta read the entry it opens, the rest the entry it sits in.
|
|
224
|
+
const opened = c.component === "Link" ? items : undefined;
|
|
225
|
+
if (c.component === "Link" && typeof props.key === "string" && !hasKey(opened, props.key)) {
|
|
226
|
+
issues.push({ message: `"${c.id}": its items have no "${props.key}".` });
|
|
227
|
+
}
|
|
228
|
+
for (const [k, value] of Object.entries(props)) {
|
|
229
|
+
if (k === "id" || k === "component" || k === "children" || k === listProp) {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
const reads = c.component === "Link" && (k === "title" || k === "subtitle" || k === "meta");
|
|
233
|
+
const scope = reads ? opened : entry;
|
|
234
|
+
const paths = [];
|
|
235
|
+
const walk = (v) => {
|
|
236
|
+
if (isPath(v)) {
|
|
237
|
+
paths.push(v.path);
|
|
238
|
+
}
|
|
239
|
+
else if (Array.isArray(v)) {
|
|
240
|
+
v.forEach(walk);
|
|
241
|
+
}
|
|
242
|
+
else if (v && typeof v === "object") {
|
|
243
|
+
Object.values(v).forEach(walk);
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
walk(value);
|
|
247
|
+
for (const path of paths) {
|
|
248
|
+
if (resolve(path, scope) === undefined) {
|
|
249
|
+
say(c, path, scope);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
const kids = c.children;
|
|
254
|
+
if (Array.isArray(kids)) {
|
|
255
|
+
for (const k of kids) {
|
|
256
|
+
visit(k, entry);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
else if (kids) {
|
|
260
|
+
const of = entriesOf(c, { path: kids.path }, entry);
|
|
261
|
+
if (of !== undefined) {
|
|
262
|
+
if (c.component === "Group" && typeof props.by === "string" && !hasKey(of, props.by)) {
|
|
263
|
+
issues.push({
|
|
264
|
+
message: `"${c.id}": "by" names "${props.by}", which its entries do not have.`,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
visit(kids.componentId, of);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const tabs = props.tabs;
|
|
271
|
+
if (Array.isArray(tabs)) {
|
|
272
|
+
for (const tab of tabs) {
|
|
273
|
+
const child = tab?.child;
|
|
274
|
+
if (typeof child === "string") {
|
|
275
|
+
visit(child, entry);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (c.component === "Link" && typeof props.view === "string" && opened !== undefined) {
|
|
280
|
+
visit(props.view, opened);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
if (byId.has("root")) {
|
|
284
|
+
visit("root", undefined);
|
|
285
|
+
}
|
|
286
|
+
return issues;
|
|
287
|
+
}
|
|
81
288
|
/**
|
|
82
289
|
* What can be wrong with a surface: an unknown or missing component, no root, a child that is
|
|
83
|
-
* not there, an id twice, a loop, a prop missing, and — given the data keys a run will provide
|
|
84
|
-
*
|
|
290
|
+
* not there, an id twice, a loop, a prop missing, and — given the data keys a run will provide,
|
|
291
|
+
* or the step's schema — a path into data nothing provides.
|
|
85
292
|
*/
|
|
86
|
-
export function validateSurface(raw,
|
|
293
|
+
export function validateSurface(raw, data) {
|
|
294
|
+
const dataKeys = Array.isArray(data) ? data : undefined;
|
|
87
295
|
const parsed = surfaceComponentsSchema.safeParse(raw);
|
|
88
296
|
if (!parsed.success) {
|
|
89
297
|
return parsed.error.issues.map((i) => ({ message: `${i.path.join(".")}: ${i.message}` }));
|
|
@@ -116,7 +324,17 @@ export function validateSurface(raw, dataKeys) {
|
|
|
116
324
|
if (children && !CONTAINERS.has(c.component) && c.component !== "Table") {
|
|
117
325
|
issues.push({ message: `${c.component} "${c.id}" holds no children.` });
|
|
118
326
|
}
|
|
119
|
-
|
|
327
|
+
if (c.component === "Group" && (!children || Array.isArray(children))) {
|
|
328
|
+
issues.push({
|
|
329
|
+
message: `Group "${c.id}" repeats over a list: children { "path", "componentId" }.`,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
const tabs = c.tabs;
|
|
333
|
+
if (c.component === "Tabs" && !(Array.isArray(tabs) && tabs.length >= 2)) {
|
|
334
|
+
issues.push({ message: `Tabs "${c.id}" needs at least two tabs: [{ label, child }].` });
|
|
335
|
+
}
|
|
336
|
+
const view = c.view;
|
|
337
|
+
const ids = [...childIds(c), ...(typeof view === "string" ? [view] : [])];
|
|
120
338
|
for (const child of ids) {
|
|
121
339
|
if (!byId.has(child)) {
|
|
122
340
|
issues.push({ message: `"${c.id}" names a child "${child}" that is not there.` });
|
|
@@ -142,18 +360,15 @@ export function validateSurface(raw, dataKeys) {
|
|
|
142
360
|
}
|
|
143
361
|
seen.add(cid);
|
|
144
362
|
const c = byId.get(cid);
|
|
145
|
-
const kids =
|
|
146
|
-
? c.children
|
|
147
|
-
: c?.children
|
|
148
|
-
? [c.children.componentId]
|
|
149
|
-
: [];
|
|
363
|
+
const kids = c ? childIds(c) : [];
|
|
150
364
|
const next = new Set(stack).add(cid);
|
|
151
365
|
return kids.some((k) => loops(k, next));
|
|
152
366
|
};
|
|
153
367
|
if (byId.has("root") && loops("root", new Set())) {
|
|
154
368
|
issues.push({ message: "Components hold each other in a loop." });
|
|
369
|
+
return issues;
|
|
155
370
|
}
|
|
156
|
-
return issues;
|
|
371
|
+
return data && !Array.isArray(data) ? [...issues, ...shapeIssues(byId, data)] : issues;
|
|
157
372
|
}
|
|
158
373
|
/** A JSON pointer into data: "/suppliers/0/name". */
|
|
159
374
|
export function pointer(data, path) {
|
|
@@ -174,21 +389,38 @@ export function bound(value, data, row) {
|
|
|
174
389
|
if (!isPath(value)) {
|
|
175
390
|
return value;
|
|
176
391
|
}
|
|
177
|
-
|
|
178
|
-
|
|
392
|
+
const raw = value.path.startsWith("/")
|
|
393
|
+
? pointer(data, value.path)
|
|
394
|
+
: row === undefined
|
|
395
|
+
? undefined
|
|
396
|
+
: pointer(row, `/${value.path.replace(/\./g, "/")}`);
|
|
397
|
+
return worded(raw, value);
|
|
398
|
+
}
|
|
399
|
+
/** A value turned into words as its path says: looked up in `map`, set into `format`. */
|
|
400
|
+
export function worded(raw, spec) {
|
|
401
|
+
let v = raw;
|
|
402
|
+
const map = spec.map;
|
|
403
|
+
if (map && typeof map === "object" && !Array.isArray(map)) {
|
|
404
|
+
const key = String(v);
|
|
405
|
+
if (Object.hasOwn(map, key)) {
|
|
406
|
+
v = map[key];
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
if (typeof spec.format === "string" && v !== undefined && v !== null && v !== "") {
|
|
410
|
+
v = spec.format.replace("{}", Array.isArray(v) ? v.join(", ") : String(v));
|
|
179
411
|
}
|
|
180
|
-
return
|
|
412
|
+
return v;
|
|
181
413
|
}
|
|
182
414
|
/** What a model needs to compose a surface: the catalog, briefly. */
|
|
183
415
|
export 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.
|
|
184
416
|
Components and props (a prop may be a literal or {"path": "/key/..."} into the data):
|
|
185
|
-
- Column { children: [ids] } — vertical stack (use for root)
|
|
186
|
-
- Text { text, variant?: "h3"|"h4"|"body"|"muted" }
|
|
417
|
+
- Column { children: [ids] } — vertical stack (use for root); Column and Grid may also repeat one component per entry: children {"path", "componentId"}
|
|
418
|
+
- Text { text, variant?: "h3"|"h4"|"lead"|"body"|"muted" } — "lead": the one opening line in the wizard's voice
|
|
187
419
|
- List { children: {"path": "/rows", "componentId": "<a Row id>"} } — one Row per array entry; inside, paths without "/" read the entry ("name")
|
|
188
420
|
- Row { title, subtitle?, meta?, badge?, image? }
|
|
189
421
|
- DetailGrid { rows: [{ label, value }] } — label/value facts
|
|
190
|
-
- Badge { label, tone?: "default"|"success"|"warning" }
|
|
191
|
-
- Grid { children: [ids], columns?: 2|3|4 } — tiles side by side
|
|
422
|
+
- Badge { label, tone?: "default"|"success"|"warning"|"accent" }
|
|
423
|
+
- Grid { children: [ids], columns?: 2|3|4, min?: rem } — tiles side by side; min: the narrowest a tile gets (14 for cards, so a phone shows one)
|
|
192
424
|
- Metric { label, value, caption? } — a key figure
|
|
193
425
|
- Table { rows: {"path": "/rows"}, columns?: [{ key, label }] }
|
|
194
426
|
- Image { src: an asset id or {"path": ...}, alt? }
|
|
@@ -196,4 +428,15 @@ Components and props (a prop may be a literal or {"path": "/key/..."} into the d
|
|
|
196
428
|
- DonutChart { title?, slices: [{ label, value }] or {"path": ...}, labelKey?, valueKey? }
|
|
197
429
|
(labelKey / valueKey: the columns of bound rows that hold the label and the number, e.g. "name", "price")
|
|
198
430
|
- Actions { children: [Button ids] } / Button { label, action: "regenerate"|"open" }
|
|
431
|
+
- Tabs { tabs: [{ label, count?, child: id }] } — two to four views of one result; count may bind a list (shows its length)
|
|
432
|
+
- Card { title, eyebrow?, subtitle?, meta?: [values], image?, children?: [ids] } — one thing: a day, a recipe, an offer
|
|
433
|
+
- Fold { title, open?, children: [ids] } — details that open on a tap
|
|
434
|
+
- Group { children: {"path", "componentId"}, by: "<key>", order?: [values] } — entries under a heading per value of a key
|
|
435
|
+
- Chips { items, labelKey?, flagKey?, flag? } — short things at a glance; flagKey marks some ("bald")
|
|
436
|
+
- Checklist { items, labelKey?, detailKey?, noteKey?, noteFormat?, noteMap?, groupKey?, groupOrder?, share? } — things to get or do; ticks are kept; a yes/no note needs noteMap: {"true": "da", "false": "einkaufen"}
|
|
437
|
+
- Steps { items, textKey?, minutesKey? } — ordered work, numbered, with times
|
|
438
|
+
- Link { title, subtitle?, eyebrow?, meta?, look?: "row"|"card", items: {"path": "/recipes"}, key?: "id", value: {"path": "recipe_id"}, view: id } — 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
|
|
439
|
+
- Callout { text, title?, tone?: "tip"|"note"|"warning" }
|
|
440
|
+
A path may put its value into words: {"path": "in_fridge", "map": {"true": "da", "false": "einkaufen"}}, {"path": "minutes", "format": "{} Min"}.
|
|
441
|
+
Design 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.
|
|
199
442
|
Keep it small: what the person needs to see first, then the details. Never invent numbers: bind them by path.`;
|
|
@@ -24,13 +24,22 @@ export type Texts<K extends string> = {
|
|
|
24
24
|
};
|
|
25
25
|
/** What fills a line's gaps: words, a number (written the language's way), or a list of them. */
|
|
26
26
|
export type Gap = string | number | readonly Gap[];
|
|
27
|
+
/** The plural rules of a language: any language, by its code. */
|
|
28
|
+
export declare function pluralRules(lang: string): Intl.PluralRules;
|
|
27
29
|
/** A gap's words in a language: numbers as the language writes them, lists joined. */
|
|
28
|
-
export declare function gapText(value: Gap, lang:
|
|
30
|
+
export declare function gapText(value: Gap, lang: string): string;
|
|
29
31
|
/** Fills `{name}` gaps; a gap with nothing for it stays as it is. */
|
|
30
|
-
export declare function fillGaps(text: string, lang:
|
|
32
|
+
export declare function fillGaps(text: string, lang: string, params?: Record<string, Gap | undefined>): string;
|
|
31
33
|
/**
|
|
32
34
|
* A line of a table in a language, its gaps filled and its plural form picked by `{count}`
|
|
33
35
|
* (or `{n}`);
|
|
34
36
|
* the English line where the language lacks one.
|
|
35
37
|
*/
|
|
36
38
|
export declare function textOf<K extends string>(texts: Texts<K>, key: K, lang: Lang, params?: Record<string, Gap | undefined>): string;
|
|
39
|
+
/**
|
|
40
|
+
* A string table of one language outside `LANGS`, translated on request: lines by key, a plural
|
|
41
|
+
* line with the forms of that language's plural categories.
|
|
42
|
+
*/
|
|
43
|
+
export type TextTable = Record<string, Line>;
|
|
44
|
+
/** A line of a table, in a language: its gaps filled and its plural form picked. */
|
|
45
|
+
export declare function lineText(line: Line, lang: string, params?: Record<string, Gap | undefined>): string;
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
// SPDX-License-Identifier: FSL-1.1-MIT OR MIT
|
|
2
|
-
import {
|
|
2
|
+
import { localeOf } from "./definition.js";
|
|
3
3
|
const plurals = new Map();
|
|
4
|
+
/** The plural rules of a language: any language, by its code. */
|
|
5
|
+
export function pluralRules(lang) {
|
|
6
|
+
let rules = plurals.get(lang);
|
|
7
|
+
if (!rules) {
|
|
8
|
+
rules = new Intl.PluralRules(localeOf(lang));
|
|
9
|
+
plurals.set(lang, rules);
|
|
10
|
+
}
|
|
11
|
+
return rules;
|
|
12
|
+
}
|
|
4
13
|
function form(line, lang, count) {
|
|
5
14
|
if (typeof line === "string") {
|
|
6
15
|
return line;
|
|
@@ -8,17 +17,12 @@ function form(line, lang, count) {
|
|
|
8
17
|
if (typeof count !== "number") {
|
|
9
18
|
return line.other;
|
|
10
19
|
}
|
|
11
|
-
|
|
12
|
-
if (!rules) {
|
|
13
|
-
rules = new Intl.PluralRules(LOCALES[lang]);
|
|
14
|
-
plurals.set(lang, rules);
|
|
15
|
-
}
|
|
16
|
-
return line[rules.select(count)] ?? line.other;
|
|
20
|
+
return line[pluralRules(lang).select(count)] ?? line.other;
|
|
17
21
|
}
|
|
18
22
|
/** A gap's words in a language: numbers as the language writes them, lists joined. */
|
|
19
23
|
export function gapText(value, lang) {
|
|
20
24
|
if (typeof value === "number") {
|
|
21
|
-
return value.toLocaleString(
|
|
25
|
+
return value.toLocaleString(localeOf(lang));
|
|
22
26
|
}
|
|
23
27
|
if (Array.isArray(value)) {
|
|
24
28
|
return value.map((item) => gapText(item, lang)).join(", ");
|
|
@@ -44,3 +48,7 @@ export function textOf(texts, key, lang, params) {
|
|
|
44
48
|
}
|
|
45
49
|
return fillGaps(form(line, lang, params?.count ?? params?.n), lang, params);
|
|
46
50
|
}
|
|
51
|
+
/** A line of a table, in a language: its gaps filled and its plural form picked. */
|
|
52
|
+
export function lineText(line, lang, params) {
|
|
53
|
+
return fillGaps(form(line, lang, params?.count ?? params?.n), lang, params);
|
|
54
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { WizardDefinition } from "./definition.js";
|
|
2
|
+
/** The texts of a definition the person reads, each once, in the order they appear. */
|
|
3
|
+
export declare function wizardTexts(def: WizardDefinition): string[];
|
|
4
|
+
/** Whether a translated text keeps every placeholder and gap of its original, no more, no fewer. */
|
|
5
|
+
export declare function keepsPlaceholders(original: string, translated: string): boolean;
|
|
6
|
+
/** A translation as it is kept: each original text with its translation and who wrote it. */
|
|
7
|
+
export interface TranslationEntry {
|
|
8
|
+
text: string;
|
|
9
|
+
by: "machine" | "author";
|
|
10
|
+
}
|
|
11
|
+
export type TranslationMemory = Record<string, TranslationEntry>;
|
|
12
|
+
/** The texts of a definition a translation does not have yet. */
|
|
13
|
+
export declare function missingTexts(def: WizardDefinition, memory: TranslationMemory): string[];
|
|
14
|
+
/**
|
|
15
|
+
* The definition in the viewer's language: a copy with every text the memory has written in,
|
|
16
|
+
* and the labels of the choices beside it (`labels[value]`), the values untouched.
|
|
17
|
+
*/
|
|
18
|
+
export declare function translateDefinition(def: WizardDefinition, memory: TranslationMemory): {
|
|
19
|
+
def: WizardDefinition;
|
|
20
|
+
labels: Record<string, string>;
|
|
21
|
+
};
|
|
22
|
+
/** Whether every text of the memory came from the author, or some from a model. */
|
|
23
|
+
export declare function machineTranslated(memory: TranslationMemory, def: WizardDefinition): boolean;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A wizard translated for its viewer, on request: what the person reads is written in their
|
|
3
|
+
* language, what the wizard runs on stays as the author wrote it. Never translated: ids,
|
|
4
|
+
* branches and their statements, the instructions of AI steps, `{{…}}` placeholders, and the
|
|
5
|
+
* values of choices — a choice shows a translated label and keeps its value.
|
|
6
|
+
*/
|
|
7
|
+
/** Fields whose `options` are choices the person reads (a slot's are dates, written by the runner). */
|
|
8
|
+
function choiceField(field) {
|
|
9
|
+
return field.kind === "select" || field.kind === "multiselect";
|
|
10
|
+
}
|
|
11
|
+
/** Every text of a definition the person reads, with the way to write it back. */
|
|
12
|
+
function visitTexts(def, visit, choices) {
|
|
13
|
+
visit(def.title, (t) => {
|
|
14
|
+
def.title = t;
|
|
15
|
+
});
|
|
16
|
+
visit(def.description, (t) => {
|
|
17
|
+
def.description = t;
|
|
18
|
+
});
|
|
19
|
+
visit(def.intro, (t) => {
|
|
20
|
+
def.intro = t;
|
|
21
|
+
});
|
|
22
|
+
for (const list of def.lists ?? []) {
|
|
23
|
+
visit(list.title, (t) => {
|
|
24
|
+
list.title = t;
|
|
25
|
+
});
|
|
26
|
+
visit(list.description, (t) => {
|
|
27
|
+
list.description = t;
|
|
28
|
+
});
|
|
29
|
+
for (const column of list.columns ?? []) {
|
|
30
|
+
visit(column.name, (t) => {
|
|
31
|
+
column.name = t;
|
|
32
|
+
});
|
|
33
|
+
const options = ("format" in column
|
|
34
|
+
? column.format
|
|
35
|
+
: undefined)?.options;
|
|
36
|
+
for (const option of options ?? []) {
|
|
37
|
+
visit(option.label, (t) => {
|
|
38
|
+
option.label = t;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
for (const connection of def.connections ?? []) {
|
|
44
|
+
visit(connection.title, (t) => {
|
|
45
|
+
connection.title = t;
|
|
46
|
+
});
|
|
47
|
+
visit(connection.description, (t) => {
|
|
48
|
+
connection.description = t;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
for (const step of def.steps) {
|
|
52
|
+
visit(step.title, (t) => {
|
|
53
|
+
step.title = t;
|
|
54
|
+
});
|
|
55
|
+
visit(step.description, (t) => {
|
|
56
|
+
step.description = t;
|
|
57
|
+
});
|
|
58
|
+
if ("working" in step) {
|
|
59
|
+
visit(step.working, (t) => {
|
|
60
|
+
step.working = t;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
// What an AI step's output fields hold: the headings a review and the result show them under.
|
|
64
|
+
if (step.type === "agent") {
|
|
65
|
+
for (const field of step.output?.fields ?? []) {
|
|
66
|
+
visit(field.description, (t) => {
|
|
67
|
+
field.description = t;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (step.type === "page") {
|
|
72
|
+
visit(step.cta, (t) => {
|
|
73
|
+
step.cta = t;
|
|
74
|
+
});
|
|
75
|
+
for (const field of step.fields) {
|
|
76
|
+
visit(field.label, (t) => {
|
|
77
|
+
field.label = t;
|
|
78
|
+
});
|
|
79
|
+
visit(field.help, (t) => {
|
|
80
|
+
field.help = t;
|
|
81
|
+
});
|
|
82
|
+
visit(field.placeholder, (t) => {
|
|
83
|
+
field.placeholder = t;
|
|
84
|
+
});
|
|
85
|
+
for (const column of field.columns ?? []) {
|
|
86
|
+
visit(column.label, (t) => {
|
|
87
|
+
column.label = t;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
if (choiceField(field)) {
|
|
91
|
+
for (const option of field.options ?? []) {
|
|
92
|
+
choices(option, () => undefined);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (step.type === "result") {
|
|
98
|
+
visit(step.message, (t) => {
|
|
99
|
+
step.message = t;
|
|
100
|
+
});
|
|
101
|
+
for (const deliverable of step.deliverables) {
|
|
102
|
+
visit(deliverable.label, (t) => {
|
|
103
|
+
deliverable.label = t;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/** The texts of a definition the person reads, each once, in the order they appear. */
|
|
110
|
+
export function wizardTexts(def) {
|
|
111
|
+
const seen = new Set();
|
|
112
|
+
const add = (text) => {
|
|
113
|
+
if (text?.trim()) {
|
|
114
|
+
seen.add(text);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
visitTexts(structuredClone(def), add, add);
|
|
118
|
+
return [...seen];
|
|
119
|
+
}
|
|
120
|
+
/** `{{…}}` placeholders and `{name}` gaps: what a translation keeps exactly. */
|
|
121
|
+
const KEPT = /\{\{[^}]*\}\}|\{\w+\}/g;
|
|
122
|
+
/** Whether a translated text keeps every placeholder and gap of its original, no more, no fewer. */
|
|
123
|
+
export function keepsPlaceholders(original, translated) {
|
|
124
|
+
const of = (text) => (text.match(KEPT) ?? []).sort().join("\u0000");
|
|
125
|
+
return of(original) === of(translated);
|
|
126
|
+
}
|
|
127
|
+
/** The texts of a definition a translation does not have yet. */
|
|
128
|
+
export function missingTexts(def, memory) {
|
|
129
|
+
return wizardTexts(def).filter((text) => !memory[text]);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* The definition in the viewer's language: a copy with every text the memory has written in,
|
|
133
|
+
* and the labels of the choices beside it (`labels[value]`), the values untouched.
|
|
134
|
+
*/
|
|
135
|
+
export function translateDefinition(def, memory) {
|
|
136
|
+
const copy = structuredClone(def);
|
|
137
|
+
const labels = {};
|
|
138
|
+
visitTexts(copy, (text, write) => {
|
|
139
|
+
const entry = text ? memory[text] : undefined;
|
|
140
|
+
if (entry) {
|
|
141
|
+
write(entry.text);
|
|
142
|
+
}
|
|
143
|
+
}, (value) => {
|
|
144
|
+
const entry = value ? memory[value] : undefined;
|
|
145
|
+
if (value && entry) {
|
|
146
|
+
labels[value] = entry.text;
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
return { def: copy, labels };
|
|
150
|
+
}
|
|
151
|
+
/** Whether every text of the memory came from the author, or some from a model. */
|
|
152
|
+
export function machineTranslated(memory, def) {
|
|
153
|
+
return wizardTexts(def).some((text) => memory[text]?.by !== "author");
|
|
154
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wizards",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Make a wish. Get your wizard. It guides you step by step. Let AI do the work. Runs on your machine, on the AI subscription you already have.",
|
|
5
5
|
"license": "FSL-1.1-MIT",
|
|
6
6
|
"homepage": "https://engenty.ai",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{n as t,t as n}from"./jsx-runtime-BNakU3Ej.js";import{$n as r,At as i,Jn as a,Mt as o,Nt as s,Rt as c,Tn as l,Ut as u,Vt as d,Yt as f,jt as p}from"./ui-CeZNNuaK.js";import{t as m}from"./preload-helper-CNEI7N0N.js";import{H as h,U as g,W as _,yt as v}from"./index-93fPRTxr.js";import{c as y,f as b}from"./settings-menu-COgM8CgB.js";import{ReleaseNotes as x,loadReleases as S,releaseDate as C,releaseLabel as w}from"./ChangelogDialog-BZ8i3wt_.js";var T=e(t(),1),E=[{name:`engenty wizards`,description:`credit.engenty-wizards`,homepage:`https://github.com/engenty/wizards`,license:`FSL-1.1-MIT`},{name:`engenty`,description:`credit.engenty`,homepage:`https://github.com/engenty/engenty`,license:`FSL-1.1-MIT`}],D=[{name:`Mastra`,description:`credit.mastra`,homepage:`https://github.com/mastra-ai/mastra`,license:`Apache-2.0`},{name:`AI SDK`,description:`credit.ai-sdk`,homepage:`https://github.com/vercel/ai`,license:`Apache-2.0`},{name:`Model Context Protocol`,description:`credit.model-context-protocol`,homepage:`https://github.com/modelcontextprotocol/typescript-sdk`,license:`MIT`},{name:`Hono`,description:`credit.hono`,homepage:`https://github.com/honojs/hono`,license:`MIT`},{name:`Drizzle ORM`,description:`credit.drizzle-orm`,homepage:`https://github.com/drizzle-team/drizzle-orm`,license:`Apache-2.0`},{name:`libSQL`,description:`credit.libsql`,homepage:`https://github.com/tursodatabase/libsql`,license:`MIT`},{name:`Playwright`,description:`credit.playwright`,homepage:`https://github.com/microsoft/playwright`,license:`Apache-2.0`},{name:`agentOS`,description:`credit.agentos`,homepage:`https://github.com/rivet-dev/agentos`,license:`Apache-2.0`},{name:`React`,description:`credit.react`,homepage:`https://github.com/facebook/react`,license:`MIT`},{name:`Vite`,description:`credit.vite`,homepage:`https://github.com/vitejs/vite`,license:`MIT`},{name:`Tailwind CSS`,description:`credit.tailwind-css`,homepage:`https://github.com/tailwindlabs/tailwindcss`,license:`MIT`},{name:`React Flow`,description:`credit.react-flow`,homepage:`https://github.com/xyflow/xyflow`,license:`MIT`},{name:`TanStack Query`,description:`credit.tanstack-query`,homepage:`https://github.com/TanStack/query`,license:`MIT`},{name:`xterm.js`,description:`credit.xterm-js`,homepage:`https://github.com/xtermjs/xterm.js`,license:`MIT`},{name:`Lucide`,description:`credit.lucide`,homepage:`https://github.com/lucide-icons/lucide`,license:`ISC`},{name:`Geist & Geist Mono`,description:`credit.geist-geist-mono`,homepage:`https://github.com/vercel/geist-font`,license:`OFL-1.1`},{name:`Space Grotesk`,description:`credit.space-grotesk`,homepage:`https://github.com/floriankarsten/space-grotesk`,license:`OFL-1.1`},{name:`git-cliff`,description:`credit.git-cliff`,homepage:`https://github.com/orhun/git-cliff`,license:`MIT OR Apache-2.0`}],O=e(n(),1),k=(e,...t)=>t.some(t=>t?.toLowerCase().includes(e));function A({name:e,homepage:t,version:n,license:r,description:i}){return(0,O.jsxs)(`li`,{className:`py-2.5`,children:[(0,O.jsxs)(`div`,{className:`flex flex-wrap items-baseline gap-x-2.5 gap-y-0.5`,children:[t?(0,O.jsxs)(`a`,{href:t,target:`_blank`,rel:`noreferrer`,className:`inline-flex items-center gap-1 font-medium text-[0.875rem] text-ink underline-offset-4 hover:underline`,children:[e,(0,O.jsx)(a,{"aria-hidden":!0,className:`size-3 text-ink-4`})]}):(0,O.jsx)(`span`,{className:`font-medium text-[0.875rem] text-ink`,children:e}),n?(0,O.jsx)(`span`,{className:`text-[0.75rem] text-ink-3 tabular-nums`,children:n}):null,(0,O.jsx)(`span`,{className:`text-[0.75rem] text-ink-3`,children:r})]}),i?(0,O.jsx)(`p`,{className:`mt-0.5 text-[0.8125rem] text-ink-3`,children:i}):null]})}function j({title:e,hint:t,children:n}){return(0,O.jsxs)(`section`,{className:`mt-6`,children:[(0,O.jsx)(`h3`,{className:`border-border-soft border-b pb-2 font-display font-semibold text-[1rem]`,children:e}),t?(0,O.jsx)(`p`,{className:`mt-2 text-[0.8125rem] text-ink-3`,children:t}):null,n]})}function M({credits:e}){return(0,O.jsx)(`ul`,{className:`divide-y divide-border-soft`,children:e.map(e=>(0,O.jsx)(A,{name:e.name,homepage:e.homepage,license:e.license,description:f(e.description)},e.name))})}function N({label:e,packages:t}){return(0,O.jsxs)(`div`,{className:`mt-4`,children:[(0,O.jsx)(`h4`,{className:`font-medium text-[0.75rem] text-ink-3 tracking-wide`,children:e}),(0,O.jsx)(`ul`,{className:`divide-y divide-border-soft`,children:t.map(e=>(0,O.jsx)(A,{...e},e.name))})]})}function P({active:e}){let[t,n]=(0,T.useState)(null),[r,i]=(0,T.useState)(``);(0,T.useEffect)(()=>{if(!e||t)return;let r=!1;return m(()=>import(`./oss-credits-BJrR7BR1.js`).then(e=>{r||n(e.default)}),[]),()=>{r=!0}},[e,t]),(0,T.useEffect)(()=>{e||i(``)},[e]);let a=r.trim().toLowerCase(),o=e=>a?e.filter(e=>k(a,e.name,e.license,f(e.description))):e,l=e=>a?e.filter(e=>k(a,e.name,e.license,e.version)):e,u=o(E),d=o(D),h=t?[{id:`shared`,label:f(`about.creditsShared`),packages:l(t.shared)},...t.groups.map(e=>({id:e.id,label:e.label,packages:l(e.packages)}))].filter(e=>e.packages.length>0):[],g=u.length===0&&d.length===0&&h.length===0;return(0,O.jsxs)(O.Fragment,{children:[(0,O.jsx)(`p`,{className:`mb-4 text-[0.875rem] text-ink-3`,children:f(`about.creditsHint`)}),(0,O.jsx)(s,{type:`search`,value:r,placeholder:f(`about.search`),onChange:e=>i(e.target.value)}),u.length>0?(0,O.jsx)(j,{title:f(`about.creditsProduct`),children:(0,O.jsx)(M,{credits:u})}):null,d.length>0?(0,O.jsx)(j,{title:f(`about.creditsBuiltWith`),children:(0,O.jsx)(M,{credits:d})}):null,t===null?(0,O.jsx)(`div`,{className:`flex justify-center py-10 text-ink-3`,children:(0,O.jsx)(c,{})}):h.length>0?(0,O.jsx)(j,{title:f(`about.creditsDeps`),hint:a?void 0:f(`about.creditsDepsHint`),children:h.map(e=>(0,O.jsx)(N,{label:e.label,packages:e.packages},e.id))}):null,t!==null&&g?(0,O.jsx)(p,{children:f(`about.noMatches`)}):null]})}var F=[`new`,`earlier`,`credits`],I=e=>f(e===`new`?`about.tab.new`:e===`earlier`?`about.tab.earlier`:`about.credits`),L=e=>e===1?f(`about.change`):f(`about.changes`,{n:e});function R({open:e,onClose:t,me:n}){let a=n?.mode===`local`,{data:s,latest:p}=_(a&&e),[m,E]=(0,T.useState)(null),[D,k]=(0,T.useState)(`new`),A=b(F.indexOf(D)),j=s?.newer&&p?p:g;(0,T.useEffect)(()=>{if(!e)return;let t=!1;return E(null),S(j===`0.5.12`?void 0:j).then(e=>{t||E(e)}),()=>{t=!0}},[e,j]);let M=m?.find(e=>e.version===`v${j}`)??m?.[0]??null,N=m?.filter(e=>e!==M)??[],R=a?s?.kind??null:`cloud`;return(0,O.jsxs)(i,{open:e,onClose:t,wide:!0,height:`tall`,children:[(0,O.jsx)(`div`,{className:`-mt-2 -mr-2 sticky top-0 z-10 flex h-0 justify-end`,children:(0,O.jsx)(o,{label:f(`common.close`),onClick:t,children:(0,O.jsx)(l,{className:`size-4`})})}),(0,O.jsxs)(`div`,{className:`flex flex-col items-center pt-4 text-center`,children:[(0,O.jsx)(v,{size:96}),(0,O.jsxs)(`div`,{className:`mt-4 font-display font-semibold text-[1.5rem] tracking-tight`,children:[`engenty`,(0,O.jsx)(`span`,{className:`text-ember`,children:`.`}),(0,O.jsx)(`span`,{className:`ml-1.5 font-normal text-ink-3`,children:`wizards`})]}),(0,O.jsxs)(`div`,{className:`mt-1 text-[0.9375rem] text-ink-3 tabular-nums`,children:[f(`about.version`,{v:g}),R?` · ${f(`about.kind.${R}`)}`:``]})]}),a?(0,O.jsx)(`div`,{className:`mt-5`,children:(0,O.jsx)(h,{enabled:e})}):null,(0,O.jsx)(`div`,{className:`mt-6 flex justify-center`,children:(0,O.jsx)(d,{value:D,onChange:k,options:F.map(e=>({id:e,label:I(e)})),label:f(`about.title`)})}),(0,O.jsxs)(`div`,{className:u(`mt-5`,y(A)),children:[D===`new`?m===null?(0,O.jsx)(`div`,{className:`flex justify-center py-6 text-ink-3`,children:(0,O.jsx)(c,{})}):M?(0,O.jsxs)(O.Fragment,{children:[(0,O.jsxs)(`h3`,{className:`flex items-baseline gap-3 border-border-soft border-b pb-2 font-display font-semibold text-[1rem]`,children:[w(M),(0,O.jsx)(`span`,{className:`font-normal font-sans text-[0.8125rem] text-ink-3 tabular-nums`,children:C(M)})]}),(0,O.jsx)(`div`,{className:`mt-3`,children:(0,O.jsx)(x,{release:M})})]}):(0,O.jsx)(`p`,{className:`text-[0.875rem] text-ink-3`,children:f(`about.changelogEmpty`)}):null,D===`earlier`?(0,O.jsx)(`div`,{className:`flex flex-col divide-y divide-border-soft`,children:N.map(e=>(0,O.jsxs)(`details`,{className:`group/r py-2`,children:[(0,O.jsxs)(`summary`,{className:`flex cursor-pointer list-none items-baseline gap-3 text-[0.875rem] [&::-webkit-details-marker]:hidden`,children:[(0,O.jsx)(r,{className:`size-3.5 shrink-0 self-center text-ink-4 transition group-open/r:rotate-90`}),(0,O.jsx)(`span`,{className:`font-medium text-ink tabular-nums`,children:w(e)}),(0,O.jsx)(`span`,{className:`text-[0.8125rem] text-ink-3 tabular-nums`,children:C(e)}),(0,O.jsx)(`span`,{className:`ml-auto text-[0.8125rem] text-ink-4`,children:L(e.commits.length)})]}),(0,O.jsx)(`div`,{className:`mt-2 pl-6`,children:(0,O.jsx)(x,{release:e})})]},e.version??`unreleased`))}):null,D===`credits`?(0,O.jsx)(P,{active:!0}):null]},D)]})}export{R as AboutDialog};
|