wizards 0.3.5 → 0.3.7
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/agents/studio-assistant.js +285 -31
- package/apps/runtime/dist/agents/website.js +256 -34
- package/apps/runtime/dist/db/schema.js +1 -1
- package/apps/runtime/dist/docs/user/test-and-share.md +21 -11
- package/apps/runtime/dist/documents/parse.js +14 -0
- package/apps/runtime/dist/engine/steps.js +42 -6
- package/apps/runtime/dist/engine/template.js +50 -3
- package/apps/runtime/dist/film/project.js +54 -4
- package/apps/runtime/dist/routes/studio.js +52 -3
- package/apps/runtime/dist/services/brand.js +6 -3
- package/apps/runtime/dist/services/project-files.js +59 -3
- package/apps/runtime/dist/services/projects.js +150 -2
- package/apps/runtime/dist/services/wizards.js +25 -0
- package/apps/web/dist/assets/AboutDialog-C0dN86zd.js +1 -0
- package/apps/web/dist/assets/{ChangelogDialog-VEBTlC3f.js → ChangelogDialog-Cv81t7tj.js} +1 -1
- package/apps/web/dist/assets/ChatView-uogJ3nht.js +10 -0
- package/apps/web/dist/assets/EditorPage-n8YuTh6j.js +3 -0
- package/apps/web/dist/assets/{FlowPreview-86xWWfdk.js → FlowPreview-D4dp1MWS.js} +1 -1
- package/apps/web/dist/assets/{Harness-CF6H2TYJ.js → Harness-v7r0e6vK.js} +2 -2
- package/apps/web/dist/assets/NewWizardChannels-Be-7VhBi.js +1 -0
- package/apps/web/dist/assets/NewWizardPage-C8hPvrK8.js +7 -0
- package/apps/web/dist/assets/ProjectPage-B686ArYy.js +1 -0
- package/apps/web/dist/assets/PublicRunner-DeCpVfZP.js +1 -0
- package/apps/web/dist/assets/{ReadOnly-mhKW496R.js → ReadOnly-B-UCgnqW.js} +1 -1
- package/apps/web/dist/assets/RunDrawer-C6pzi8AJ.js +4 -0
- package/apps/web/dist/assets/{RunnerView-CTRFgyu5.js → RunnerView-Deenymq0.js} +1 -1
- package/apps/web/dist/assets/SettingsPage-CK6L0yEe.js +1 -0
- package/apps/web/dist/assets/SetupPage-9CH3qXdH.js +1 -0
- package/apps/web/dist/assets/SharePage-B778xUFo.js +1 -0
- package/apps/web/dist/assets/changelog-DCJo2mbx.js +1 -0
- package/apps/web/dist/assets/{circle-alert-CYs622_r.js → circle-alert-DLvC9ePT.js} +1 -1
- package/apps/web/dist/assets/{esm-B52LIMhP.js → esm-BJskogLD.js} +1 -1
- package/apps/web/dist/assets/index-D6hUM8NU.css +1 -0
- package/apps/web/dist/assets/index-Df6UxElH.js +12 -0
- package/apps/web/dist/assets/{lock-BvSWZOin.js → lock-DR7NakPn.js} +1 -1
- package/apps/web/dist/assets/{phone-CI2Flerg.js → phone-BORh3jfv.js} +1 -1
- package/apps/web/dist/assets/{settings-menu-BOOPxcJH.js → settings-menu-DV5D91TD.js} +1 -1
- package/apps/web/dist/assets/{ui-CMdSOoUZ.js → ui-BOCXzzHi.js} +1 -1
- package/apps/web/dist/assets/{upload-Cz9HgBT9.js → upload-B3yv8gsk.js} +1 -1
- package/apps/web/dist/index.html +3 -8
- package/apps/web/dist/mcp/flow.html +2 -2
- package/node_modules/@engenty-wizards/plugin-sdk/dist/index.d.ts +3 -3
- package/node_modules/@engenty-wizards/shared/dist/changes.d.ts +22 -0
- package/node_modules/@engenty-wizards/shared/dist/changes.js +61 -0
- package/node_modules/@engenty-wizards/shared/dist/definition.d.ts +8 -8
- package/node_modules/@engenty-wizards/shared/dist/marketplace.d.ts +1 -1
- package/node_modules/@engenty-wizards/shared/dist/projects.d.ts +138 -3
- package/node_modules/@engenty-wizards/shared/dist/projects.js +153 -4
- package/node_modules/@engenty-wizards/shared/dist/wizard-package.d.ts +1 -1
- package/package.json +1 -1
- package/apps/web/dist/assets/AboutDialog-D1N4-3FE.js +0 -1
- package/apps/web/dist/assets/ChatView-DSRUEpb6.js +0 -10
- package/apps/web/dist/assets/EditorPage-UkaHDklX.js +0 -3
- package/apps/web/dist/assets/Marketplace-C-Vz3yEk.js +0 -2
- package/apps/web/dist/assets/NewWizardChannels-Cc7gDitg.js +0 -1
- package/apps/web/dist/assets/NewWizardPage-sY5q-l99.js +0 -7
- package/apps/web/dist/assets/ProjectPage-iSdpJAZS.js +0 -2
- package/apps/web/dist/assets/PublicRunner-BgRXMUxS.js +0 -1
- package/apps/web/dist/assets/RunDrawer-B0gJzdxH.js +0 -4
- package/apps/web/dist/assets/SettingsPage-CBF8i6j6.js +0 -1
- package/apps/web/dist/assets/SetupPage-Fpz9aSLN.js +0 -1
- package/apps/web/dist/assets/SharePage-Dv3Csh4A.js +0 -1
- package/apps/web/dist/assets/brand-BamUjXVr.js +0 -2
- package/apps/web/dist/assets/changelog-ViHoNW7e.js +0 -1
- package/apps/web/dist/assets/index-Bdx1Qity.js +0 -11
- package/apps/web/dist/assets/index-CDdJDojq.css +0 -1
- package/apps/web/dist/assets/session-BEHF6g4Y.js +0 -1
- package/apps/web/dist/assets/useMutation-DKyQXgyj.js +0 -1
- package/apps/web/dist/assets/volume-2-CyQuz1nn.js +0 -1
|
@@ -3,8 +3,9 @@ import { existsSync } from "node:fs";
|
|
|
3
3
|
import { cp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
4
4
|
import { dirname, join, normalize } from "node:path";
|
|
5
5
|
import { dataRef, FILM_SIZES } from "@engenty-wizards/shared/definition";
|
|
6
|
+
import { gradientCss, ruleLines, textStyleLine } from "@engenty-wizards/shared/projects";
|
|
6
7
|
import { unzipSync } from "fflate";
|
|
7
|
-
import { renderTemplate, resolveRef } from "../engine/template.js";
|
|
8
|
+
import { logoSectionLine, renderTemplate, resolveRef } from "../engine/template.js";
|
|
8
9
|
import { env } from "../env.js";
|
|
9
10
|
import { getBlob } from "../files/blobs.js";
|
|
10
11
|
import { extFor, loadAsset } from "../files/storage.js";
|
|
@@ -128,23 +129,72 @@ export async function prepareFilmFolder(step, ctx) {
|
|
|
128
129
|
// The space's brand.
|
|
129
130
|
const b = ctx.scope.brand;
|
|
130
131
|
const logos = [];
|
|
132
|
+
const patterns = [];
|
|
133
|
+
for (const p of b.patterns ?? []) {
|
|
134
|
+
const f = ctx.projectFiles.find((x) => x.id === p.file);
|
|
135
|
+
try {
|
|
136
|
+
if (f) {
|
|
137
|
+
const { data } = await projectFileContent(f.projectId, f.id);
|
|
138
|
+
const name = `patterns/${safeName(f.name.replace(/\.[^.]+$/, ""))}.${extFor(f.mime) === "bin" ? (f.name.split(".").pop() ?? "bin") : extFor(f.mime)}`;
|
|
139
|
+
await put(join(root, "brand"), name, data);
|
|
140
|
+
patterns.push({ file: name, name: p.name, tile: Boolean(p.tile) });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// a pattern less
|
|
145
|
+
}
|
|
146
|
+
}
|
|
131
147
|
for (const f of ctx.projectFiles.filter((p) => p.kind === "logo")) {
|
|
132
148
|
try {
|
|
133
149
|
const { data } = await projectFileContent(f.projectId, f.id);
|
|
134
150
|
const name = `logos/${safeName(f.name.replace(/\.[^.]+$/, ""))}.${extFor(f.mime) === "bin" ? (f.name.split(".").pop() ?? "bin") : extFor(f.mime)}`;
|
|
135
151
|
await put(join(root, "brand"), name, data);
|
|
136
|
-
|
|
152
|
+
const section = logoSectionLine(b.logoSections, f.id);
|
|
153
|
+
logos.push({
|
|
154
|
+
file: name,
|
|
155
|
+
...(section ? { section } : {}),
|
|
156
|
+
...(f.description ? { description: f.description } : {}),
|
|
157
|
+
});
|
|
137
158
|
}
|
|
138
159
|
catch {
|
|
139
160
|
// a logo less
|
|
140
161
|
}
|
|
141
162
|
}
|
|
142
163
|
await put(join(root, "brand"), "brand.json", JSON.stringify({
|
|
143
|
-
note: "The space's brand. Colours are named by what they are for; the first is the accent.",
|
|
164
|
+
note: "The space's brand. Colours are named by what they are for; the first is the accent. A logo's or a colour's section says when to use it; colorRules, logoRules and typeRules, where there are any, say how colours, logos and type may be used; text is set in the fonts.",
|
|
144
165
|
name: b.name ?? "",
|
|
145
166
|
about: b.about ?? "",
|
|
146
|
-
colors: (b.colors ?? []).map((c) =>
|
|
167
|
+
colors: (b.colors ?? []).map((c) => {
|
|
168
|
+
const s = b.colorSections?.find((x) => x.id === c.section);
|
|
169
|
+
return { name: c.name, value: c.value, ...(s ? { section: s.title } : {}) };
|
|
170
|
+
}),
|
|
171
|
+
...(b.gradients?.length
|
|
172
|
+
? {
|
|
173
|
+
gradients: b.gradients.map((g) => ({
|
|
174
|
+
name: g.name,
|
|
175
|
+
section: b.colorSections?.find((x) => x.id === g.section)?.title,
|
|
176
|
+
css: gradientCss(g),
|
|
177
|
+
})),
|
|
178
|
+
}
|
|
179
|
+
: {}),
|
|
180
|
+
...(patterns.length ? { patterns } : {}),
|
|
147
181
|
logos,
|
|
182
|
+
...(b.colorRules?.length ? { colorRules: ruleLines(b.colorRules).split("\n") } : {}),
|
|
183
|
+
...(b.fonts?.length
|
|
184
|
+
? {
|
|
185
|
+
fonts: b.fonts.map(({ family, role, weights }) => ({ family, role, weights })),
|
|
186
|
+
}
|
|
187
|
+
: {}),
|
|
188
|
+
...(b.textStyles?.length
|
|
189
|
+
? {
|
|
190
|
+
textStyles: b.textStyles.map((s) => ({
|
|
191
|
+
name: s.name,
|
|
192
|
+
style: textStyleLine(s, b.fonts ?? []),
|
|
193
|
+
})),
|
|
194
|
+
}
|
|
195
|
+
: {}),
|
|
196
|
+
...(b.typeRules?.length ? { typeRules: ruleLines(b.typeRules).split("\n") } : {}),
|
|
197
|
+
...(b.logoRules?.length ? { logoRules: ruleLines(b.logoRules).split("\n") } : {}),
|
|
148
198
|
facts: (b.facts ?? []).map((f) => `${f.label}: ${f.value}`),
|
|
149
199
|
}, null, 1));
|
|
150
200
|
// What the run made before.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mkdirSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { ASSISTANT_LAYOUTS, ASSISTANT_PAGE_BYTES, assistantTurnSchema, } from "@engenty-wizards/shared/assistant";
|
|
4
|
-
import { MODEL_CLASSES, TEXT_CLASSES } from "@engenty-wizards/shared/definition";
|
|
4
|
+
import { MODEL_CLASSES, TEXT_CLASSES, } from "@engenty-wizards/shared/definition";
|
|
5
5
|
import { CATEGORY_TYPES, whereSchema } from "@engenty-wizards/shared/knowledge";
|
|
6
6
|
import { PROJECT_FILE_KINDS } from "@engenty-wizards/shared/projects";
|
|
7
7
|
import { isCreditsRef } from "@engenty-wizards/shared/providers";
|
|
@@ -13,6 +13,7 @@ import { nanoid } from "nanoid";
|
|
|
13
13
|
import { z } from "zod";
|
|
14
14
|
import { runStudioAssistant } from "../agents/studio-assistant.js";
|
|
15
15
|
import { chatEngine, setChatEngine, subscriptionClients } from "../agents/subscription.js";
|
|
16
|
+
import { scanColors, scanFonts, scanImages, scanLogos } from "../agents/website.js";
|
|
16
17
|
import { answerBrowser, awaitBrowser } from "../assistant/browser.js";
|
|
17
18
|
import { accountOverview, linkedAccount, startLink, unlink } from "../auth/account.js";
|
|
18
19
|
import { createLocalKey, deleteLocalKey, listLocalKeys } from "../auth/keys.js";
|
|
@@ -45,7 +46,7 @@ import { categoryContents, listKnowledge, resolvePath, valueContents, } from "..
|
|
|
45
46
|
import { writeSummary } from "../services/knowledge-model.js";
|
|
46
47
|
import { exportWizard, importWizard } from "../services/package.js";
|
|
47
48
|
import { profileSchema, saveProfile, userProfile } from "../services/profile.js";
|
|
48
|
-
import { addProjectFile, fileView, orderProjectFiles, projectFile, projectFileContent, projectFiles, reindexProjectFile, removeProjectFile, updateProjectFile, } from "../services/project-files.js";
|
|
49
|
+
import { addProjectFile, addProjectFileFromUrl, fileView, orderProjectFiles, projectFile, projectFileContent, projectFiles, reindexProjectFile, removeProjectFile, updateProjectFile, } from "../services/project-files.js";
|
|
49
50
|
import { documentText, queueIndex, searchKnowledge } from "../services/project-index.js";
|
|
50
51
|
import { createProject, deleteProject, listProjects, maskedServers, ownedProject, projectLimitOrNull, projectPatchSchema, updateProject, } from "../services/projects.js";
|
|
51
52
|
import { runnersOf, wizardRunners } from "../services/runners.js";
|
|
@@ -53,7 +54,7 @@ import { listResults, listRuns, startTestRun } from "../services/runs.js";
|
|
|
53
54
|
import { addCategoryValue, categoryRow, categoryValue, deleteCategory, deleteCategoryValue, mergeCategoryValues, orderCategoryValues, putCategory, renameCategoryValue, setItemCategory, updateCategory, } from "../services/space-categories.js";
|
|
54
55
|
import { addTableRow, createPage, createTable, deletePage, deleteTable, deleteTableRows, getPage, getTable, listSpaceData, orderSubPages, updatePage, updateTable, updateTableRow, } from "../services/space-data.js";
|
|
55
56
|
import { draftWidgetPreview } from "../services/widgets.js";
|
|
56
|
-
import { createWizard, deleteWizard, duplicateWizard, listWizards, ownedWizard, rotateShareLink, updateWizardSettings, wizardMessages, wizardState, writableWizard, writeDraft, } from "../services/wizards.js";
|
|
57
|
+
import { createWizard, deleteWizard, draftChanges, duplicateWizard, listWizards, ownedWizard, rotateShareLink, updateWizardSettings, wizardMessages, wizardState, writableWizard, writeDraft, } from "../services/wizards.js";
|
|
57
58
|
import { readSetting, writeSetting } from "../settings.js";
|
|
58
59
|
import { codeOf } from "../tenants/control.js";
|
|
59
60
|
import { currentTenant } from "../tenants/tenant.js";
|
|
@@ -452,6 +453,41 @@ export const studio = new Hono()
|
|
|
452
453
|
data: new Uint8Array(await file.arrayBuffer()),
|
|
453
454
|
});
|
|
454
455
|
return c.json(fileView(row));
|
|
456
|
+
})
|
|
457
|
+
.post("/projects/:id/files/url", async (c) => {
|
|
458
|
+
const project = await ownedProject(c.get("user").id, c.req.param("id"));
|
|
459
|
+
const input = z
|
|
460
|
+
.object({ url: z.string().min(1).max(500_000), kind: z.enum(PROJECT_FILE_KINDS) })
|
|
461
|
+
.parse(await c.req.json());
|
|
462
|
+
const url = /^(https?:\/\/|data:)/i.test(input.url) ? input.url : `https://${input.url}`;
|
|
463
|
+
try {
|
|
464
|
+
return c.json({ file: fileView(await addProjectFileFromUrl(project.id, { ...input, url })) });
|
|
465
|
+
}
|
|
466
|
+
catch (err) {
|
|
467
|
+
// A web page where a logo or an asset was asked for: its logos or pictures, to pick from.
|
|
468
|
+
if (err instanceof ServiceError && err.data.reason === "page") {
|
|
469
|
+
const page = String(err.data.url ?? url);
|
|
470
|
+
const scan = input.kind === "logo" ? scanLogos : scanImages;
|
|
471
|
+
return c.json({ candidates: await scan(page) });
|
|
472
|
+
}
|
|
473
|
+
throw err;
|
|
474
|
+
}
|
|
475
|
+
})
|
|
476
|
+
.post("/projects/:id/colors/scan", async (c) => {
|
|
477
|
+
await ownedProject(c.get("user").id, c.req.param("id"));
|
|
478
|
+
const { url } = z.object({ url: z.string().min(1).max(2000) }).parse(await c.req.json());
|
|
479
|
+
const colors = await scanColors(url).catch(() => {
|
|
480
|
+
throw new ServiceError("invalid", "Die Seite ist nicht erreichbar.");
|
|
481
|
+
});
|
|
482
|
+
return c.json({ colors });
|
|
483
|
+
})
|
|
484
|
+
.post("/projects/:id/fonts/scan", async (c) => {
|
|
485
|
+
await ownedProject(c.get("user").id, c.req.param("id"));
|
|
486
|
+
const { url } = z.object({ url: z.string().min(1).max(2000) }).parse(await c.req.json());
|
|
487
|
+
const fonts = await scanFonts(url).catch(() => {
|
|
488
|
+
throw new ServiceError("invalid", "Die Seite ist nicht erreichbar.");
|
|
489
|
+
});
|
|
490
|
+
return c.json({ fonts });
|
|
455
491
|
})
|
|
456
492
|
.put("/projects/:id/files/order", async (c) => {
|
|
457
493
|
const project = await ownedProject(c.get("user").id, c.req.param("id"));
|
|
@@ -891,6 +927,19 @@ export const studio = new Hono()
|
|
|
891
927
|
})
|
|
892
928
|
// With an account linked the published version also goes to its cloud: `cloud` says how that went.
|
|
893
929
|
.post("/wizards/:id/publish", async (c) => c.json(await publishAndSync(c.get("user").id, c.req.param("id"))))
|
|
930
|
+
// What publishing the draft would change: the steps and files, its last test, the cost of a run before and after.
|
|
931
|
+
.get("/wizards/:id/changes", async (c) => {
|
|
932
|
+
const w = await ownedWizard(c.get("user").id, c.req.param("id"));
|
|
933
|
+
const { published, ...found } = await draftChanges(w);
|
|
934
|
+
const cost = async (def) => estimateRun(w.id, null, def, await stepsWithoutModel(def));
|
|
935
|
+
const [before, after] = published ? await Promise.all([cost(published), cost(w.draft)]) : [];
|
|
936
|
+
return c.json({
|
|
937
|
+
...found,
|
|
938
|
+
cost: before?.available && after?.available
|
|
939
|
+
? { before: before.credits, after: after.credits }
|
|
940
|
+
: null,
|
|
941
|
+
});
|
|
942
|
+
})
|
|
894
943
|
// What a run of the draft is expected to cost, per step and in total.
|
|
895
944
|
.get("/wizards/:id/estimate", async (c) => {
|
|
896
945
|
const w = await ownedWizard(c.get("user").id, c.req.param("id"));
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
import { mainLogo } from "@engenty-wizards/shared/projects";
|
|
1
2
|
import { and, asc, eq } from "drizzle-orm";
|
|
2
3
|
import { db, schema } from "../db/client.js";
|
|
3
|
-
/** The project's
|
|
4
|
+
/** The project's main logo, the first standard one: what end users, documents and widgets get. */
|
|
4
5
|
export async function mainLogoId(projectId) {
|
|
5
|
-
const
|
|
6
|
+
const p = await db.query.project.findFirst({ where: eq(schema.project.id, projectId) });
|
|
7
|
+
const logos = await db.query.projectFile.findMany({
|
|
8
|
+
columns: { id: true },
|
|
6
9
|
where: and(eq(schema.projectFile.projectId, projectId), eq(schema.projectFile.kind, "logo")),
|
|
7
10
|
orderBy: [asc(schema.projectFile.position), asc(schema.projectFile.createdAt)],
|
|
8
11
|
});
|
|
9
|
-
return
|
|
12
|
+
return mainLogo(logos, p?.brand.logoSections)?.id ?? null;
|
|
10
13
|
}
|
|
11
14
|
/** What end users see of the project: its title, accent and logo — never the project name. */
|
|
12
15
|
export async function brandView(projectId) {
|
|
@@ -7,6 +7,7 @@ import { loadAsset, removeAsset, replaceAsset, saveAsset } from "../files/storag
|
|
|
7
7
|
import { embeddingModel, textModel } from "../models.js";
|
|
8
8
|
import { spaceFileChanged } from "../plugins/events.js";
|
|
9
9
|
import { dropLinks, putLink } from "../tenants/control.js";
|
|
10
|
+
import { safeFetch } from "../tools/net-guard.js";
|
|
10
11
|
import { requireWritableProject } from "./access.js";
|
|
11
12
|
import { notFound, ServiceError } from "./errors.js";
|
|
12
13
|
import { convertDocument, fillCategories } from "./knowledge-model.js";
|
|
@@ -58,12 +59,17 @@ export async function projectFileContent(projectId, fileId) {
|
|
|
58
59
|
}
|
|
59
60
|
return { row, data: found.data };
|
|
60
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Assets are what wizards put into what they make: pictures, clips, recordings, and files to
|
|
64
|
+
* hand on as they are — PDFs, fonts, Lottie animations (JSON), 3D models.
|
|
65
|
+
*/
|
|
66
|
+
const ASSET_TYPES = /^((image|video|audio|font)\/|application\/(pdf|json)$|model\/gltf)/;
|
|
61
67
|
function accepts(kind, mime) {
|
|
62
|
-
if (kind === "logo") {
|
|
68
|
+
if (kind === "logo" || kind === "pattern") {
|
|
63
69
|
return mime.startsWith("image/");
|
|
64
70
|
}
|
|
65
71
|
if (kind === "asset") {
|
|
66
|
-
return
|
|
72
|
+
return ASSET_TYPES.test(mime);
|
|
67
73
|
}
|
|
68
74
|
return true;
|
|
69
75
|
}
|
|
@@ -73,7 +79,9 @@ export async function addProjectFile(projectId, input) {
|
|
|
73
79
|
if (!accepts(input.kind, mime)) {
|
|
74
80
|
throw new ServiceError("invalid", input.kind === "logo"
|
|
75
81
|
? "Ein Logo ist ein Bild (PNG, SVG, JPEG, WebP)."
|
|
76
|
-
:
|
|
82
|
+
: input.kind === "pattern"
|
|
83
|
+
? "Ein Muster ist ein Bild (SVG, PNG, JPEG, WebP)."
|
|
84
|
+
: "Hierher gehören Bilder, Videos, Audio, PDFs, Schriften, Lottie und 3D-Modelle. Texte zum Nachschlagen bitte zu den Dokumenten.");
|
|
77
85
|
}
|
|
78
86
|
const limit = projectFileLimit(input.kind, mime);
|
|
79
87
|
if (!input.data.byteLength || input.data.byteLength > limit) {
|
|
@@ -120,6 +128,54 @@ export async function addProjectFile(projectId, input) {
|
|
|
120
128
|
}
|
|
121
129
|
return row;
|
|
122
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Fetches a file from a public web address into the project, as an upload of it would add it.
|
|
133
|
+
* A drawing a page holds inline comes as a `data:image/svg+xml` address. A web page where a
|
|
134
|
+
* picture was asked for is refused with the reason `page`.
|
|
135
|
+
*/
|
|
136
|
+
export async function addProjectFileFromUrl(projectId, input) {
|
|
137
|
+
const { url, kind, signal } = input;
|
|
138
|
+
const inline = url.match(/^data:image\/svg\+xml;base64,([A-Za-z0-9+/=]+)$/);
|
|
139
|
+
if (inline) {
|
|
140
|
+
return addProjectFile(projectId, {
|
|
141
|
+
kind,
|
|
142
|
+
name: `${kind}.svg`,
|
|
143
|
+
mime: "image/svg+xml",
|
|
144
|
+
data: new Uint8Array(Buffer.from(inline[1], "base64")),
|
|
145
|
+
description: input.description,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
const res = await safeFetch(url, {
|
|
149
|
+
headers: { "user-agent": "Mozilla/5.0 (compatible; engenty-wizards/0.1)" },
|
|
150
|
+
signal: AbortSignal.any([AbortSignal.timeout(60_000), ...(signal ? [signal] : [])]),
|
|
151
|
+
}).catch(() => {
|
|
152
|
+
throw new ServiceError("invalid", "Die Adresse ist nicht erreichbar.");
|
|
153
|
+
});
|
|
154
|
+
if (!res.ok) {
|
|
155
|
+
throw new ServiceError("invalid", `Die Adresse antwortete ${res.status}.`);
|
|
156
|
+
}
|
|
157
|
+
const mime = (res.headers.get("content-type") ?? "").split(";")[0].trim().toLowerCase();
|
|
158
|
+
if (mime === "text/html" && kind !== "document") {
|
|
159
|
+
await res.body?.cancel();
|
|
160
|
+
throw new ServiceError("invalid", "Unter der Adresse steht eine Webseite, kein Bild.", {
|
|
161
|
+
reason: "page",
|
|
162
|
+
url: res.url,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
if (Number(res.headers.get("content-length") ?? 0) > projectFileLimit(kind, mime)) {
|
|
166
|
+
throw new ServiceError("invalid", "Die Datei ist zu groß.");
|
|
167
|
+
}
|
|
168
|
+
const data = new Uint8Array(await res.arrayBuffer());
|
|
169
|
+
const name = decodeURIComponent(new URL(res.url).pathname.split("/").pop() || kind);
|
|
170
|
+
return addProjectFile(projectId, {
|
|
171
|
+
kind,
|
|
172
|
+
name,
|
|
173
|
+
mime,
|
|
174
|
+
data,
|
|
175
|
+
description: input.description,
|
|
176
|
+
source: res.url,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
123
179
|
// --- reading, describing, indexing -------------------------------------------------
|
|
124
180
|
/** Files are worked through two at a time: each costs model calls. */
|
|
125
181
|
let running = 0;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FACT_TYPES, PROJECT_LIMITS } from "@engenty-wizards/shared/projects";
|
|
1
|
+
import { COLOR_SECTION_KINDS, cleanAssetSections, cleanLogoSections, FACT_TYPES, FONT_WEIGHTS, GRADIENT_TYPES, orderColors, PROJECT_LIMITS, RULE_MARKS, } from "@engenty-wizards/shared/projects";
|
|
2
2
|
import { and, count, eq, isNull } from "drizzle-orm";
|
|
3
3
|
import { nanoid } from "nanoid";
|
|
4
4
|
import { z } from "zod";
|
|
@@ -18,9 +18,66 @@ export const mcpServerSchema = z.object({
|
|
|
18
18
|
url: z.string().url(),
|
|
19
19
|
headers: z.record(z.string(), z.string()).optional(),
|
|
20
20
|
});
|
|
21
|
+
const sectionId = z.string().regex(/^[A-Za-z0-9_-]{1,40}$/);
|
|
21
22
|
export const brandColorSchema = z.object({
|
|
22
23
|
name: z.string().max(120),
|
|
23
24
|
value: z.string().regex(/^#[0-9a-fA-F]{6}$/),
|
|
25
|
+
section: sectionId.optional(),
|
|
26
|
+
});
|
|
27
|
+
export const colorSectionSchema = z.object({
|
|
28
|
+
id: sectionId,
|
|
29
|
+
title: z.string().max(80),
|
|
30
|
+
description: z.string().max(400),
|
|
31
|
+
kind: z.enum(COLOR_SECTION_KINDS).optional(),
|
|
32
|
+
});
|
|
33
|
+
const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/);
|
|
34
|
+
export const gradientSchema = z.object({
|
|
35
|
+
id: sectionId,
|
|
36
|
+
name: z.string().max(80),
|
|
37
|
+
section: sectionId,
|
|
38
|
+
type: z.enum(GRADIENT_TYPES),
|
|
39
|
+
angle: z.number().min(-360).max(720),
|
|
40
|
+
stops: z
|
|
41
|
+
.array(z.object({ color: hexColor, at: z.number().min(0).max(100) }))
|
|
42
|
+
.min(2)
|
|
43
|
+
.max(12),
|
|
44
|
+
});
|
|
45
|
+
export const patternSchema = z.object({
|
|
46
|
+
id: sectionId,
|
|
47
|
+
name: z.string().max(80),
|
|
48
|
+
section: sectionId,
|
|
49
|
+
file: z.string().max(40),
|
|
50
|
+
tile: z.boolean().optional(),
|
|
51
|
+
});
|
|
52
|
+
const rulesSchema = z
|
|
53
|
+
.array(z.object({ text: z.string().max(200), mark: z.enum(RULE_MARKS).optional() }))
|
|
54
|
+
.max(PROJECT_LIMITS.logoRules);
|
|
55
|
+
export const brandFontSchema = z.object({
|
|
56
|
+
id: sectionId,
|
|
57
|
+
family: z.string().max(80),
|
|
58
|
+
role: z.string().max(200),
|
|
59
|
+
weights: z.array(z.number().int().min(100).max(900)).max(FONT_WEIGHTS.length + 3),
|
|
60
|
+
});
|
|
61
|
+
export const textStyleSchema = z.object({
|
|
62
|
+
id: sectionId,
|
|
63
|
+
name: z.string().max(60),
|
|
64
|
+
font: sectionId.optional(),
|
|
65
|
+
size: z.number().min(6).max(400),
|
|
66
|
+
weight: z.number().int().min(100).max(900),
|
|
67
|
+
lineHeight: z.number().min(50).max(300),
|
|
68
|
+
tracking: z.number().min(-20).max(50),
|
|
69
|
+
uppercase: z.boolean().optional(),
|
|
70
|
+
});
|
|
71
|
+
export const assetSectionSchema = z.object({
|
|
72
|
+
id: sectionId,
|
|
73
|
+
title: z.string().max(80),
|
|
74
|
+
assets: z.array(z.string().max(40)).max(PROJECT_LIMITS.files.asset),
|
|
75
|
+
});
|
|
76
|
+
export const logoSectionSchema = z.object({
|
|
77
|
+
id: sectionId,
|
|
78
|
+
title: z.string().max(80),
|
|
79
|
+
description: z.string().max(400),
|
|
80
|
+
logos: z.array(z.string().max(40)).max(PROJECT_LIMITS.files.logo),
|
|
24
81
|
});
|
|
25
82
|
export const projectFactSchema = z.object({
|
|
26
83
|
key: z.string().regex(/^[a-z0-9_]{1,40}$/),
|
|
@@ -35,6 +92,16 @@ export const projectPatchSchema = z.object({
|
|
|
35
92
|
name: z.string().max(120).optional(),
|
|
36
93
|
about: z.string().max(8000).optional(),
|
|
37
94
|
colors: z.array(brandColorSchema).max(PROJECT_LIMITS.colors).optional(),
|
|
95
|
+
colorSections: z.array(colorSectionSchema).max(PROJECT_LIMITS.logoSections).optional(),
|
|
96
|
+
gradients: z.array(gradientSchema).max(PROJECT_LIMITS.gradients).optional(),
|
|
97
|
+
patterns: z.array(patternSchema).max(PROJECT_LIMITS.files.pattern).optional(),
|
|
98
|
+
colorRules: rulesSchema.optional(),
|
|
99
|
+
assetSections: z.array(assetSectionSchema).max(PROJECT_LIMITS.logoSections).optional(),
|
|
100
|
+
fonts: z.array(brandFontSchema).max(PROJECT_LIMITS.fonts).optional(),
|
|
101
|
+
textStyles: z.array(textStyleSchema).max(PROJECT_LIMITS.textStyles).optional(),
|
|
102
|
+
typeRules: rulesSchema.optional(),
|
|
103
|
+
logoSections: z.array(logoSectionSchema).max(PROJECT_LIMITS.logoSections).optional(),
|
|
104
|
+
logoRules: rulesSchema.optional(),
|
|
38
105
|
})
|
|
39
106
|
.optional(),
|
|
40
107
|
facts: z.array(projectFactSchema).max(PROJECT_LIMITS.facts).optional(),
|
|
@@ -182,7 +249,7 @@ export async function updateProject(userId, projectId, patch) {
|
|
|
182
249
|
.update(schema.project)
|
|
183
250
|
.set({
|
|
184
251
|
...(patch.name ? { name: patch.name } : {}),
|
|
185
|
-
...(patch.brand ? { brand:
|
|
252
|
+
...(patch.brand ? { brand: brandAfter(p.brand, patch.brand) } : {}),
|
|
186
253
|
...(patch.facts ? { facts: patch.facts } : {}),
|
|
187
254
|
...(mcpServers ? { mcpServers } : {}),
|
|
188
255
|
updatedAt: new Date(),
|
|
@@ -190,11 +257,92 @@ export async function updateProject(userId, projectId, patch) {
|
|
|
190
257
|
.where(eq(schema.project.id, p.id));
|
|
191
258
|
spaceChanged("space.updated", p.id);
|
|
192
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* The brand with the patch on it. Sections and rules are optional: a section without title and
|
|
262
|
+
* logos and rules without words are not kept.
|
|
263
|
+
*/
|
|
264
|
+
function brandAfter(brand, patch) {
|
|
265
|
+
const { logoSections, logoRules, colors, colorSections, colorRules, gradients, patterns, assetSections, fonts, textStyles, typeRules, ...rest } = {
|
|
266
|
+
...brand,
|
|
267
|
+
...patch,
|
|
268
|
+
};
|
|
269
|
+
// A rule is its words and its sign; a bullet is the sign of none.
|
|
270
|
+
const rows = (list) => (list ?? [])
|
|
271
|
+
.map(({ text, mark }) => ({
|
|
272
|
+
text: text.trim(),
|
|
273
|
+
...(mark && mark !== "bullet" ? { mark } : {}),
|
|
274
|
+
}))
|
|
275
|
+
.filter((r) => r.text);
|
|
276
|
+
const used = new Set([...(colors ?? []), ...(gradients ?? []), ...(patterns ?? [])].map((c) => c.section));
|
|
277
|
+
const colorGroups = (colorSections ?? [])
|
|
278
|
+
.map((s) => ({
|
|
279
|
+
...s,
|
|
280
|
+
title: s.title.trim(),
|
|
281
|
+
description: s.description.trim(),
|
|
282
|
+
...(s.kind && s.kind !== "colors" ? { kind: s.kind } : { kind: undefined }),
|
|
283
|
+
}))
|
|
284
|
+
.filter((s) => s.title || used.has(s.id));
|
|
285
|
+
// A gradient or a pattern lives in a section of its kind; colours only in colour sections.
|
|
286
|
+
const kindOf = (id) => colorGroups.find((s) => s.id === id)?.kind ?? "colors";
|
|
287
|
+
const inKind = (id, kind) => colorGroups.some((s) => s.id === id && s.kind === kind);
|
|
288
|
+
const fills = (gradients ?? [])
|
|
289
|
+
.filter((g) => inKind(g.section, "gradients"))
|
|
290
|
+
.map((g) => ({ ...g, name: g.name.trim() }));
|
|
291
|
+
const tiles = (patterns ?? [])
|
|
292
|
+
.filter((p) => inKind(p.section, "patterns"))
|
|
293
|
+
.map((p) => ({ ...p, name: p.name.trim() }));
|
|
294
|
+
const swatches = colors?.map((c) => c.section && kindOf(c.section) !== "colors" ? { name: c.name, value: c.value } : c);
|
|
295
|
+
const sections = cleanLogoSections((logoSections ?? []).map((s) => ({
|
|
296
|
+
...s,
|
|
297
|
+
title: s.title.trim(),
|
|
298
|
+
description: s.description.trim(),
|
|
299
|
+
}))).filter((s) => s.title || s.logos.length);
|
|
300
|
+
const assetGroups = cleanAssetSections((assetSections ?? []).map((s) => ({ ...s, title: s.title.trim() }))).filter((s) => s.title || s.assets.length);
|
|
301
|
+
const typefaces = (fonts ?? [])
|
|
302
|
+
.map((f) => ({ ...f, family: f.family.trim(), role: f.role.trim() }))
|
|
303
|
+
.filter((f) => f.family);
|
|
304
|
+
const styles = (textStyles ?? [])
|
|
305
|
+
.map((s) => ({
|
|
306
|
+
...s,
|
|
307
|
+
name: s.name.trim(),
|
|
308
|
+
font: typefaces.some((f) => f.id === s.font) ? s.font : undefined,
|
|
309
|
+
}))
|
|
310
|
+
.filter((s) => s.name);
|
|
311
|
+
const typeRows = rows(typeRules);
|
|
312
|
+
const rules = rows(logoRules);
|
|
313
|
+
const colourRules = rows(colorRules);
|
|
314
|
+
return {
|
|
315
|
+
...rest,
|
|
316
|
+
...(swatches ? { colors: orderColors(swatches, colorGroups) } : {}),
|
|
317
|
+
...(colorGroups.length
|
|
318
|
+
? { colorSections: colorGroups.map(({ kind, ...s }) => (kind ? { ...s, kind } : s)) }
|
|
319
|
+
: {}),
|
|
320
|
+
...(fills.length ? { gradients: fills } : {}),
|
|
321
|
+
...(tiles.length ? { patterns: tiles } : {}),
|
|
322
|
+
...(colourRules.length ? { colorRules: colourRules } : {}),
|
|
323
|
+
...(assetGroups.length ? { assetSections: assetGroups } : {}),
|
|
324
|
+
...(typefaces.length ? { fonts: typefaces } : {}),
|
|
325
|
+
...(styles.length ? { textStyles: styles } : {}),
|
|
326
|
+
...(typeRows.length ? { typeRules: typeRows } : {}),
|
|
327
|
+
...(sections.length ? { logoSections: sections } : {}),
|
|
328
|
+
...(rules.length ? { logoRules: rules } : {}),
|
|
329
|
+
};
|
|
330
|
+
}
|
|
193
331
|
export function projectProfile(p) {
|
|
194
332
|
return {
|
|
195
333
|
name: p.brand.name ?? "",
|
|
196
334
|
about: p.brand.about ?? "",
|
|
197
335
|
colors: p.brand.colors ?? [],
|
|
336
|
+
colorSections: p.brand.colorSections ?? [],
|
|
337
|
+
gradients: p.brand.gradients ?? [],
|
|
338
|
+
patterns: p.brand.patterns ?? [],
|
|
339
|
+
colorRules: p.brand.colorRules ?? [],
|
|
340
|
+
logoSections: p.brand.logoSections ?? [],
|
|
341
|
+
assetSections: p.brand.assetSections ?? [],
|
|
342
|
+
fonts: p.brand.fonts ?? [],
|
|
343
|
+
textStyles: p.brand.textStyles ?? [],
|
|
344
|
+
typeRules: p.brand.typeRules ?? [],
|
|
345
|
+
logoRules: p.brand.logoRules ?? [],
|
|
198
346
|
facts: p.facts.filter((f) => f.value.trim()),
|
|
199
347
|
};
|
|
200
348
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { versionChanges } from "@engenty-wizards/shared/changes";
|
|
1
2
|
import { pluginToolOf, shapeIssues, validateWizard, wizardSchema, } from "@engenty-wizards/shared/definition";
|
|
2
3
|
import { and, desc, eq, sql } from "drizzle-orm";
|
|
3
4
|
import { nanoid } from "nanoid";
|
|
@@ -159,6 +160,30 @@ export async function wizardState(w) {
|
|
|
159
160
|
studioUrl: studioUrl(w.id),
|
|
160
161
|
};
|
|
161
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* What "Änderungen live schalten" says: the version people run, what the draft changes of it,
|
|
165
|
+
* and the latest test run of exactly this draft and its files.
|
|
166
|
+
*/
|
|
167
|
+
export async function draftChanges(w) {
|
|
168
|
+
const published = await publishedVersion(w);
|
|
169
|
+
const files = await draftFiles(w.id);
|
|
170
|
+
const draft = JSON.stringify(w.draft);
|
|
171
|
+
const tests = await db.query.run.findMany({
|
|
172
|
+
where: and(eq(schema.run.wizardId, w.id), eq(schema.run.mode, "test")),
|
|
173
|
+
orderBy: [desc(schema.run.createdAt)],
|
|
174
|
+
columns: { id: true, definition: true, files: true, createdAt: true, status: true },
|
|
175
|
+
limit: 20,
|
|
176
|
+
});
|
|
177
|
+
const tested = tests.find((r) => JSON.stringify(r.definition) === draft && sameFiles(r.files, files));
|
|
178
|
+
return {
|
|
179
|
+
version: published?.version ?? null,
|
|
180
|
+
published: published?.definition ?? null,
|
|
181
|
+
changes: published ? versionChanges(published, { definition: w.draft, files }) : [],
|
|
182
|
+
tested: tested
|
|
183
|
+
? { runId: tested.id, at: tested.createdAt.toISOString(), status: tested.status }
|
|
184
|
+
: null,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
162
187
|
export async function wizardMessages(wizardId) {
|
|
163
188
|
return db.query.wizardMessage.findMany({
|
|
164
189
|
where: eq(schema.wizardMessage.wizardId, wizardId),
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{n as t,t as n}from"./jsx-runtime-BNakU3Ej.js";import{t as r}from"./logo-C4V2_3Ar.js";import{t as i}from"./preload-helper-CNEI7N0N.js";import{Ct as a,Dt as o,Tt as s,Zt as c,bt as l,ft as u,ht as d,jt as f,kt as p,mt as m,nn as h,pt as g}from"./ui-BOCXzzHi.js";import{ft as _,pt as v,wt as y}from"./index-Df6UxElH.js";import{c as b,f as x}from"./settings-menu-DV5D91TD.js";import{ReleaseNotes as S,loadReleases as C,releaseDate as w,releaseLabel as T}from"./ChangelogDialog-Cv81t7tj.js";var E=e(t(),1),D=[{name:`engenty wizards`,description:{de:`KI-Wizards aus einer Beschreibung, mit einem Link geteilt.`,en:`AI wizards built from a description, shared with a link.`},homepage:`https://github.com/engenty/wizards`,license:`FSL-1.1-MIT`},{name:`engenty`,description:{de:`Konnektoren, Datentabellen, Suche und die Engenties stammen aus dem engenty-Framework.`,en:`Connectors, data tables, search and the engenties come from the engenty framework.`},homepage:`https://github.com/engenty/engenty`,license:`FSL-1.1-MIT`}],O=[{name:`Mastra`,description:{de:`Agent-Framework hinter den KI-Schritten.`,en:`Agent framework behind the AI steps.`},homepage:`https://github.com/mastra-ai/mastra`,license:`Apache-2.0`},{name:`AI SDK`,description:{de:`Modell-Aufrufe für Text, Bild, Video und Sprache.`,en:`Model calls for text, image, video and speech.`},homepage:`https://github.com/vercel/ai`,license:`Apache-2.0`},{name:`Model Context Protocol`,description:{de:`MCP-Server zum Bauen von Wizards, MCP-Client für Werkzeuge der Schritte.`,en:`MCP server for building wizards, MCP client for the steps' tools.`},homepage:`https://github.com/modelcontextprotocol/typescript-sdk`,license:`MIT`},{name:`Hono`,description:{de:`HTTP-API der Runtime.`,en:`The runtime's HTTP API.`},homepage:`https://github.com/honojs/hono`,license:`MIT`},{name:`Drizzle ORM`,description:{de:`Schema und Migrationen der Datenbanken.`,en:`Schema and migrations of the databases.`},homepage:`https://github.com/drizzle-team/drizzle-orm`,license:`Apache-2.0`},{name:`libSQL`,description:{de:`Eine Datenbank je Mandant, lokal als Datei oder bei Turso.`,en:`One database per tenant, a local file or at Turso.`},homepage:`https://github.com/tursodatabase/libsql`,license:`MIT`},{name:`Playwright`,description:{de:`Browser-Schritte sowie PDF- und PNG-Export.`,en:`Browser steps and the PDF and PNG export.`},homepage:`https://github.com/microsoft/playwright`,license:`Apache-2.0`},{name:`agentOS`,description:{de:`Sandbox für Code-Schritte ohne Docker.`,en:`Sandbox for code steps without Docker.`},homepage:`https://github.com/rivet-dev/agentos`,license:`Apache-2.0`},{name:`React`,description:{de:`UI-Bibliothek der Web-App.`,en:`UI library of the web app.`},homepage:`https://github.com/facebook/react`,license:`MIT`},{name:`Vite`,description:{de:`Build-Werkzeug der Web-App.`,en:`Build tool of the web app.`},homepage:`https://github.com/vitejs/vite`,license:`MIT`},{name:`Tailwind CSS`,description:{de:`Styling der Oberfläche.`,en:`Styling of the interface.`},homepage:`https://github.com/tailwindlabs/tailwindcss`,license:`MIT`},{name:`React Flow`,description:{de:`Das Diagramm der Schritte im Editor.`,en:`The diagram of steps in the editor.`},homepage:`https://github.com/xyflow/xyflow`,license:`MIT`},{name:`TanStack Query`,description:{de:`Server-Daten in der Web-App.`,en:`Server data in the web app.`},homepage:`https://github.com/TanStack/query`,license:`MIT`},{name:`xterm.js`,description:{de:`Das Terminal auf der Seite, in dem sich KI-Clients anmelden.`,en:`The terminal on the page where AI clients sign in.`},homepage:`https://github.com/xtermjs/xterm.js`,license:`MIT`},{name:`Lucide`,description:{de:`Icons.`,en:`Icons.`},homepage:`https://github.com/lucide-icons/lucide`,license:`ISC`},{name:`Geist & Geist Mono`,description:{de:`Schrift der Oberfläche.`,en:`Typeface of the interface.`},homepage:`https://github.com/vercel/geist-font`,license:`OFL-1.1`},{name:`Space Grotesk`,description:{de:`Schrift der Überschriften.`,en:`Typeface of the headings.`},homepage:`https://github.com/floriankarsten/space-grotesk`,license:`OFL-1.1`},{name:`git-cliff`,description:{de:`Erzeugt den Changelog aus den Commits.`,en:`Generates the changelog from the commits.`},homepage:`https://github.com/orhun/git-cliff`,license:`MIT OR Apache-2.0`}],k=e(n(),1),A=(e,...t)=>t.some(t=>t?.toLowerCase().includes(e));function j({name:e,homepage:t,version:n,license:r,description:i}){return(0,k.jsxs)(`li`,{className:`py-2.5`,children:[(0,k.jsxs)(`div`,{className:`flex flex-wrap items-baseline gap-x-2.5 gap-y-0.5`,children:[t?(0,k.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,k.jsx)(c,{"aria-hidden":!0,className:`size-3 text-ink-4`})]}):(0,k.jsx)(`span`,{className:`font-medium text-[0.875rem] text-ink`,children:e}),n?(0,k.jsx)(`span`,{className:`text-[0.75rem] text-ink-3 tabular-nums`,children:n}):null,(0,k.jsx)(`span`,{className:`text-[0.75rem] text-ink-3`,children:r})]}),i?(0,k.jsx)(`p`,{className:`mt-0.5 text-[0.8125rem] text-ink-3`,children:i}):null]})}function M({title:e,hint:t,children:n}){return(0,k.jsxs)(`section`,{className:`mt-6`,children:[(0,k.jsx)(`h3`,{className:`border-border-soft border-b pb-2 font-display font-semibold text-[1rem]`,children:e}),t?(0,k.jsx)(`p`,{className:`mt-2 text-[0.8125rem] text-ink-3`,children:t}):null,n]})}function N({credits:e}){return(0,k.jsx)(`ul`,{className:`divide-y divide-border-soft`,children:e.map(e=>(0,k.jsx)(j,{name:e.name,homepage:e.homepage,license:e.license,description:e.description[o]},e.name))})}function P({label:e,packages:t}){return(0,k.jsxs)(`div`,{className:`mt-4`,children:[(0,k.jsx)(`h4`,{className:`font-medium text-[0.75rem] text-ink-3 tracking-wide`,children:e}),(0,k.jsx)(`ul`,{className:`divide-y divide-border-soft`,children:t.map(e=>(0,k.jsx)(j,{...e},e.name))})]})}function F({active:e}){let[t,n]=(0,E.useState)(null),[r,a]=(0,E.useState)(``);(0,E.useEffect)(()=>{if(!e||t)return;let r=!1;return i(()=>import(`./oss-credits-BQQX0kvk.js`).then(e=>{r||n(e.default)}),[]),()=>{r=!0}},[e,t]),(0,E.useEffect)(()=>{e||a(``)},[e]);let o=r.trim().toLowerCase(),s=e=>o?e.filter(e=>A(o,e.name,e.license,e.description.de,e.description.en)):e,c=e=>o?e.filter(e=>A(o,e.name,e.license,e.version)):e,u=s(D),f=s(O),m=t?[{id:`shared`,label:p(`about.creditsShared`),packages:c(t.shared)},...t.groups.map(e=>({id:e.id,label:e.label,packages:c(e.packages)}))].filter(e=>e.packages.length>0):[],h=u.length===0&&f.length===0&&m.length===0;return(0,k.jsxs)(k.Fragment,{children:[(0,k.jsx)(`p`,{className:`mb-4 text-[0.875rem] text-ink-3`,children:p(`about.creditsHint`)}),(0,k.jsx)(d,{type:`search`,value:r,placeholder:p(`about.search`),onChange:e=>a(e.target.value)}),u.length>0?(0,k.jsx)(M,{title:p(`about.creditsProduct`),children:(0,k.jsx)(N,{credits:u})}):null,f.length>0?(0,k.jsx)(M,{title:p(`about.creditsBuiltWith`),children:(0,k.jsx)(N,{credits:f})}):null,t===null?(0,k.jsx)(`div`,{className:`flex justify-center py-10 text-ink-3`,children:(0,k.jsx)(l,{})}):m.length>0?(0,k.jsx)(M,{title:p(`about.creditsDeps`),hint:o?void 0:p(`about.creditsDepsHint`),children:m.map(e=>(0,k.jsx)(P,{label:e.label,packages:e.packages},e.id))}):null,t!==null&&h?(0,k.jsx)(g,{children:p(`about.noMatches`)}):null]})}var I=[`new`,`earlier`,`credits`],L=e=>p(e===`new`?`about.tab.new`:e===`earlier`?`about.tab.earlier`:`about.credits`),R=e=>e===1?p(`about.change`):p(`about.changes`,{n:e});function z({open:e,onClose:t,me:n}){let i=n?.mode===`local`,{data:o,latest:c}=v(i&&e),[d,g]=(0,E.useState)(null),[D,O]=(0,E.useState)(`new`),A=x(I.indexOf(D)),j=o?.newer&&c?c:y;(0,E.useEffect)(()=>{if(!e)return;let t=!1;return g(null),C(j===`0.3.7`?void 0:j).then(e=>{t||g(e)}),()=>{t=!0}},[e,j]);let M=d?.find(e=>e.version===`v${j}`)??d?.[0]??null,N=d?.filter(e=>e!==M)??[],P=i?o?.kind??null:`cloud`;return(0,k.jsxs)(u,{open:e,onClose:t,wide:!0,height:`tall`,children:[(0,k.jsx)(`div`,{className:`-mt-2 -mr-2 sticky top-0 z-10 flex h-0 justify-end`,children:(0,k.jsx)(m,{label:p(`common.close`),onClick:t,children:(0,k.jsx)(f,{className:`size-4`})})}),(0,k.jsxs)(`div`,{className:`flex flex-col items-center pt-4 text-center`,children:[(0,k.jsx)(r,{size:96}),(0,k.jsxs)(`div`,{className:`mt-4 font-display font-semibold text-[1.5rem] tracking-tight`,children:[`engenty`,(0,k.jsx)(`span`,{className:`text-ember`,children:`.`}),(0,k.jsx)(`span`,{className:`ml-1.5 font-normal text-ink-3`,children:`wizards`})]}),(0,k.jsxs)(`div`,{className:`mt-1 text-[0.9375rem] text-ink-3 tabular-nums`,children:[p(`about.version`,{v:y}),P?` · ${p(`about.kind.${P}`)}`:``]})]}),i?(0,k.jsx)(`div`,{className:`mt-5`,children:(0,k.jsx)(_,{enabled:e})}):null,(0,k.jsx)(`div`,{className:`mt-6 flex justify-center`,children:(0,k.jsx)(a,{value:D,onChange:O,options:I.map(e=>({id:e,label:L(e)})),label:p(`about.title`)})}),(0,k.jsxs)(`div`,{className:s(`mt-5`,b(A)),children:[D===`new`?d===null?(0,k.jsx)(`div`,{className:`flex justify-center py-6 text-ink-3`,children:(0,k.jsx)(l,{})}):M?(0,k.jsxs)(k.Fragment,{children:[(0,k.jsxs)(`h3`,{className:`flex items-baseline gap-3 border-border-soft border-b pb-2 font-display font-semibold text-[1rem]`,children:[T(M),(0,k.jsx)(`span`,{className:`font-normal font-sans text-[0.8125rem] text-ink-3 tabular-nums`,children:w(M)})]}),(0,k.jsx)(`div`,{className:`mt-3`,children:(0,k.jsx)(S,{release:M})})]}):(0,k.jsx)(`p`,{className:`text-[0.875rem] text-ink-3`,children:p(`about.changelogEmpty`)}):null,D===`earlier`?(0,k.jsx)(`div`,{className:`flex flex-col divide-y divide-border-soft`,children:N.map(e=>(0,k.jsxs)(`details`,{className:`group/r py-2`,children:[(0,k.jsxs)(`summary`,{className:`flex cursor-pointer list-none items-baseline gap-3 text-[0.875rem] [&::-webkit-details-marker]:hidden`,children:[(0,k.jsx)(h,{className:`size-3.5 shrink-0 self-center text-ink-4 transition group-open/r:rotate-90`}),(0,k.jsx)(`span`,{className:`font-medium text-ink tabular-nums`,children:T(e)}),(0,k.jsx)(`span`,{className:`text-[0.8125rem] text-ink-3 tabular-nums`,children:w(e)}),(0,k.jsx)(`span`,{className:`ml-auto text-[0.8125rem] text-ink-4`,children:R(e.commits.length)})]}),(0,k.jsx)(`div`,{className:`mt-2 pl-6`,children:(0,k.jsx)(S,{release:e})})]},e.version??`unreleased`))}):null,D===`credits`?(0,k.jsx)(F,{active:!0}):null]},D)]})}export{z as AboutDialog};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{n as t,t as n}from"./jsx-runtime-BNakU3Ej.js";import{t as r}from"./preload-helper-CNEI7N0N.js";import{Tt as i,bt as a,ft as o,ht as s,kt as c,pt as l}from"./ui-
|
|
1
|
+
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{n as t,t as n}from"./jsx-runtime-BNakU3Ej.js";import{t as r}from"./preload-helper-CNEI7N0N.js";import{Tt as i,bt as a,ft as o,ht as s,kt as c,pt as l}from"./ui-BOCXzzHi.js";var u=e(t(),1),d=e(n(),1);function f(e){switch(e.group){case`Added`:return{label:`NEW`,dot:`bg-moss`};case`Fixed`:return{label:`FIXED`,dot:`bg-amber`};case`Changed`:return{label:`CHANGED`,dot:`bg-cobalt`};case`Performance`:return{label:`PERFORMANCE`,dot:`bg-ember`};case`Deploy`:return{label:`DEPLOY`,dot:`bg-ink-4`};case`Docs`:return{label:`DOCS`,dot:`bg-ink-4`};default:return{label:`OTHER`,dot:`bg-ink-4`}}}var p=e=>e.version?e.version.replace(/^v/,``):c(`about.unreleased`),m=e=>e.timestamp?new Date(e.timestamp*1e3).toISOString().slice(0,10):``,h=e=>e.charAt(0).toUpperCase()+e.slice(1);function g(e,t){let n=t.trim().toLowerCase();if(!n)return e;let r=[];for(let t of e){if(`${p(t)} ${m(t)}`.toLowerCase().includes(n)){r.push(t);continue}let e=t.commits.filter(e=>`${f(e).label} ${e.scope??``} ${e.message}`.toLowerCase().includes(n));e.length>0&&r.push({...t,commits:e})}return r}async function _(e){if(e)try{let t=await fetch(`https://raw.githubusercontent.com/engenty/engenty-wizards/v${e}/apps/web/src/about/changelog.json`,{signal:AbortSignal.timeout(5e3)});if(t.ok)return await t.json()}catch{}return(await r(async()=>{let{default:e}=await import(`./changelog-DCJo2mbx.js`);return{default:e}},[])).default}function v({release:e}){return(0,d.jsx)(`ul`,{className:`flex flex-col gap-2`,children:e.commits.map(e=>{let t=f(e);return(0,d.jsxs)(`li`,{className:`flex items-start gap-2.5 text-[0.875rem]`,children:[(0,d.jsx)(`span`,{className:i(`mt-[7px] size-1.5 shrink-0 rounded-full`,t.dot)}),(0,d.jsxs)(`span`,{className:`min-w-0 text-ink-2`,children:[(0,d.jsxs)(`span`,{className:`font-semibold text-[0.75rem] text-ink tracking-wide`,children:[t.label,e.scope?` [${e.scope}]`:``]}),` `,h(e.message),e.breaking?(0,d.jsx)(`span`,{className:`ml-1.5 rounded-full bg-rose-tint px-1.5 py-0.5 font-medium text-[0.75rem] text-rose`,children:c(`about.breaking`)}):null]})]},e.id)})})}function y({open:e,onClose:t,version:n}){let[r,i]=(0,u.useState)(null),[f,h]=(0,u.useState)(``);(0,u.useEffect)(()=>{if(!e||r)return;let t=!1;return _(n).then(e=>{t||i(e)}),()=>{t=!0}},[e,r,n]),(0,u.useEffect)(()=>{e||h(``)},[e]);let y=r?g(r,f):[];return(0,d.jsxs)(o,{open:e,onClose:t,title:c(`about.changelog`),wide:!0,height:`tall`,children:[(0,d.jsx)(`p`,{className:`-mt-3 mb-4 text-[0.875rem] text-ink-3`,children:c(`about.changelogHint`)}),r===null?(0,d.jsx)(`div`,{className:`flex justify-center py-10 text-ink-3`,children:(0,d.jsx)(a,{})}):r.length===0?(0,d.jsx)(l,{children:c(`about.changelogEmpty`)}):(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(s,{type:`search`,value:f,placeholder:c(`about.search`),onChange:e=>h(e.target.value)}),y.length===0?(0,d.jsx)(l,{children:c(`about.noMatches`)}):null,y.map(e=>(0,d.jsxs)(`section`,{className:`mt-6`,children:[(0,d.jsxs)(`h3`,{className:`flex items-baseline gap-3 border-border-soft border-b pb-2 font-display font-semibold text-[1rem]`,children:[p(e),(0,d.jsx)(`span`,{className:`font-normal font-sans text-[0.8125rem] text-ink-3 tabular-nums`,children:m(e)})]}),(0,d.jsx)(`div`,{className:`mt-3`,children:(0,d.jsx)(v,{release:e})})]},e.version??`unreleased`))]})]})}export{y as ChangelogDialog,v as ReleaseNotes,_ as loadReleases,m as releaseDate,p as releaseLabel};
|