wizards 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/apps/runtime/dist/db/client.js +24 -3
  2. package/apps/runtime/dist/index.js +6 -0
  3. package/apps/runtime/dist/routes/api.js +1 -1
  4. package/apps/runtime/dist/services/cloud.js +60 -3
  5. package/apps/runtime/dist/services/files.js +50 -1
  6. package/apps/runtime/dist/services/spaces.js +84 -84
  7. package/apps/runtime/dist/update.js +3 -3
  8. package/apps/web/dist/assets/{ChangelogDialog-CbZBUMRu.js → ChangelogDialog-lGa0JKY1.js} +1 -1
  9. package/apps/web/dist/assets/{ChatPanel-zurtfj6A.js → ChatPanel-Dk_45uca.js} +1 -1
  10. package/apps/web/dist/assets/{CreditsDialog-BewZHZTR.js → CreditsDialog-BPMWo6tp.js} +1 -1
  11. package/apps/web/dist/assets/{EditorPage-B7ZOoW5h.js → EditorPage-Dt4MCcrU.js} +1 -1
  12. package/apps/web/dist/assets/{FlowPreview-DHrhPps0.js → FlowPreview-gwtUv8-X.js} +1 -1
  13. package/apps/web/dist/assets/{HomePage-CBvWxXsq.js → HomePage-Bi9FK3_1.js} +1 -1
  14. package/apps/web/dist/assets/{ImportWizard-CKjVJMaG.js → ImportWizard-D6y4bKlF.js} +1 -1
  15. package/apps/web/dist/assets/{LocalRuntime-Brkvbzzg.js → LocalRuntime-BjzPSsHr.js} +1 -1
  16. package/apps/web/dist/assets/{NewWizardPage-B3XePMlp.js → NewWizardPage-2HbCaPSt.js} +2 -2
  17. package/apps/web/dist/assets/{PublicRunner-Bn1g6HG9.js → PublicRunner-Dac_e6wg.js} +1 -1
  18. package/apps/web/dist/assets/{RunnerView-CO1Nk3jn.js → RunnerView-BC6crf1p.js} +1 -1
  19. package/apps/web/dist/assets/{SettingsPage-BIY4kHZl.js → SettingsPage-BfXxa4SU.js} +1 -1
  20. package/apps/web/dist/assets/{SetupPage-BvLwZH9i.js → SetupPage-CYKVspBJ.js} +1 -1
  21. package/apps/web/dist/assets/{SharePage-CDESJ11B.js → SharePage-DFIHsNS-.js} +1 -1
  22. package/apps/web/dist/assets/{api-9sHuNTkp.js → api-BTfKh3La.js} +1 -1
  23. package/apps/web/dist/assets/changelog-CLmll9By.js +1 -0
  24. package/apps/web/dist/assets/{index-VsRgvimv.js → index-DOpyzQiU.js} +3 -3
  25. package/apps/web/dist/assets/{meta-B4mZqEso.js → meta-BbVhbYf6.js} +1 -1
  26. package/apps/web/dist/assets/{outputs-boPx5ngv.js → outputs-n_yhxmMk.js} +1 -1
  27. package/apps/web/dist/assets/{session-CJGuo1Tv.js → session-D_Zggq_t.js} +1 -1
  28. package/apps/web/dist/assets/ui-C3bTWmdM.js +1 -0
  29. package/apps/web/dist/index.html +4 -4
  30. package/apps/web/dist/mcp/flow.html +1 -1
  31. package/package.json +1 -1
  32. package/apps/web/dist/assets/changelog-Cto0j5-V.js +0 -1
  33. package/apps/web/dist/assets/ui-iTYHqwvY.js +0 -1
@@ -1,3 +1,4 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
1
2
  import { mkdirSync } from "node:fs";
2
3
  import { dirname, join } from "node:path";
3
4
  import { fileURLToPath } from "node:url";
@@ -117,14 +118,34 @@ export async function withTenant(id, fn) {
117
118
  await openTenantDb(id);
118
119
  return inTenant(id, fn);
119
120
  }
121
+ /** The transaction the current context writes in, while `inTransaction()` runs. */
122
+ const transactions = new AsyncLocalStorage();
120
123
  /**
121
- * The current tenant's database, resolved from the context `inTenant()` set. Code that reads or
122
- * writes wizards, runs or files never names a tenant itself.
124
+ * Runs `fn` as one transaction of the current tenant's database: inside it, `db` is the
125
+ * transaction, so every write of the services called there stands or falls with the others.
126
+ * Already inside one, `fn` just joins it. Only this database takes part: the object store and
127
+ * the control database are written as before, so do those first or after.
128
+ */
129
+ export async function inTransaction(fn) {
130
+ if (transactions.getStore()) {
131
+ return fn();
132
+ }
133
+ const id = currentTenant();
134
+ const target = ready.get(id);
135
+ if (!target) {
136
+ throw new Error(`Tenant database "${id}" is not open: enter it with withTenant().`);
137
+ }
138
+ return target.transaction((tx) => transactions.run(tx, fn));
139
+ }
140
+ /**
141
+ * The current tenant's database, resolved from the context `inTenant()` set — or the
142
+ * transaction `inTransaction()` opened there. Code that reads or writes wizards, runs or files
143
+ * never names a tenant itself.
123
144
  */
124
145
  export const db = new Proxy({}, {
125
146
  get(_target, prop) {
126
147
  const id = currentTenant();
127
- const target = ready.get(id);
148
+ const target = transactions.getStore() ?? ready.get(id);
128
149
  if (!target) {
129
150
  throw new Error(`Tenant database "${id}" is not open: enter it with withTenant().`);
130
151
  }
@@ -68,6 +68,12 @@ async function purge() {
68
68
  }
69
69
  void purge().catch((err) => console.error("[retention]", err));
70
70
  setInterval(() => void purge().catch((err) => console.error("[retention]", err)), 3600_000).unref();
71
+ // A runtime that runs alone sends again what did not arrive in its account's cloud.
72
+ if (!managed) {
73
+ const { retryFailedSends } = await import("./services/cloud.js");
74
+ const retry = () => withTenant(LOCAL_TENANT, () => retryFailedSends("local")).catch((err) => console.error("[cloud] retry failed:", err));
75
+ setInterval(() => void retry(), 60_000).unref();
76
+ }
71
77
  async function shutdown(code = 0) {
72
78
  clearRunning(env.dataDir);
73
79
  server.close();
@@ -82,7 +82,7 @@ export const apiRoutes = new Hono()
82
82
  return limited;
83
83
  }
84
84
  const body = syncWizardSchema.parse(await c.req.json());
85
- return withTenant(who.tenantId, async () => c.json(await syncWizard(who.userId, c.req.param("spaceId"), c.req.param("wizardId"), body)));
85
+ return withTenant(who.tenantId, async () => c.json(await syncWizard(c.req.param("spaceId"), c.req.param("wizardId"), body)));
86
86
  })
87
87
  .patch("/spaces/:spaceId/wizards/:wizardId", async (c) => {
88
88
  const who = await caller(c, ["wizards:publish"]);
@@ -54,9 +54,23 @@ function refusal(answer, fallback) {
54
54
  : fallback;
55
55
  return new ServiceError("refused", message, {
56
56
  reason: answer.status === 401 ? "signed_out" : typeof reason === "string" ? reason : "refused",
57
+ status: answer.status,
57
58
  ...(Array.isArray(rest.spaces) ? { spaces: rest.spaces } : {}),
58
59
  });
59
60
  }
61
+ /** A try is made again by itself when the cloud, not what was sent, was the trouble. */
62
+ function worthAnotherTry(err) {
63
+ if (!(err instanceof ServiceError)) {
64
+ return true;
65
+ }
66
+ const status = typeof err.data.status === "number" ? err.data.status : 0;
67
+ return err.data.reason === "unreachable" || status >= 500 || status === 429;
68
+ }
69
+ /** A minute, then twice as long each time, up to an hour. */
70
+ function nextTry(tries, from) {
71
+ const minutes = Math.min(60, 2 ** (tries - 1));
72
+ return new Date(from.getTime() + minutes * 60_000).toISOString();
73
+ }
60
74
  /** The project as the cloud keeps it: its name, what it says about itself, its logo. */
61
75
  async function spaceOf(userId, projectId) {
62
76
  const project = await ownedProject(userId, projectId);
@@ -125,12 +139,16 @@ export async function syncToCloud(userId, wizardId) {
125
139
  return state;
126
140
  }
127
141
  catch (err) {
142
+ const now = new Date();
143
+ const tries = (before.error?.tries ?? 0) + 1;
128
144
  const failed = {
129
145
  copy: before.copy,
130
146
  error: {
131
147
  message: err instanceof ServiceError ? err.message : "Das Senden hat nicht geklappt.",
132
148
  reason: err instanceof ServiceError ? err.data.reason : undefined,
133
- at: new Date().toISOString(),
149
+ at: now.toISOString(),
150
+ tries,
151
+ again: worthAnotherTry(err) ? nextTry(tries, now) : null,
134
152
  },
135
153
  };
136
154
  await writeSetting(key(w.id), failed);
@@ -177,6 +195,37 @@ export async function removeFromCloud(userId, wizardId) {
177
195
  }
178
196
  await call("DELETE", `/spaces/${w.projectId}/wizards/${w.id}`).catch((err) => console.error("[cloud] copy not removed:", err.message));
179
197
  }
198
+ /**
199
+ * Sends again what did not arrive and is due for another try. Called every minute by the
200
+ * runtime that runs alone; a wizard that is gone meanwhile is forgotten.
201
+ */
202
+ export async function retryFailedSends(userId) {
203
+ if (!(await cloudLinked())) {
204
+ return;
205
+ }
206
+ const now = new Date().toISOString();
207
+ const rows = await controlDb
208
+ .select({ key: control.setting.key, value: control.setting.value })
209
+ .from(control.setting)
210
+ .where(like(control.setting.key, "cloud:%"));
211
+ for (const row of rows) {
212
+ const state = row.value;
213
+ if (!state.error?.again || state.error.again > now) {
214
+ continue;
215
+ }
216
+ const wizardId = row.key.slice("cloud:".length);
217
+ const w = await ownedWizard(userId, wizardId).catch(() => null);
218
+ if (!w?.publishedVersion) {
219
+ await deleteSetting(row.key);
220
+ continue;
221
+ }
222
+ const sent = await syncToCloud(userId, wizardId);
223
+ // Something the person has to see to (signed out, no room) holds for every wizard.
224
+ if (sent.error && !sent.error.again) {
225
+ break;
226
+ }
227
+ }
228
+ }
180
229
  /** The account was unlinked: what this runtime kept of its cloud belongs to no account now. */
181
230
  export async function forgetCloud() {
182
231
  await controlDb.delete(control.setting).where(like(control.setting.key, "cloud:%"));
@@ -189,12 +238,20 @@ export async function syncAllToCloud(userId) {
189
238
  .where(and(eq(schema.wizard.tenantId, currentTenant()), isNotNull(schema.wizard.publishedVersion)));
190
239
  let sent = 0;
191
240
  let failed = 0;
192
- for (const { id } of wizards) {
241
+ for (const [i, { id }] of wizards.entries()) {
193
242
  const state = await syncToCloud(userId, id);
194
243
  if (state.error) {
195
244
  failed++;
196
- // A refusal that holds for every wizard (no room, signed out) is not asked again.
245
+ // A refusal that holds for every wizard (no room, signed out) is not asked again; a cloud
246
+ // out of reach neither, but the rest is then tried again later, with this one.
197
247
  if (state.error.reason && state.error.reason !== "refused") {
248
+ for (const rest of wizards.slice(i + 1)) {
249
+ failed++;
250
+ await writeSetting(key(rest.id), {
251
+ copy: (await cloudState(rest.id)).copy,
252
+ error: state.error,
253
+ });
254
+ }
198
255
  break;
199
256
  }
200
257
  }
@@ -1,5 +1,5 @@
1
1
  import { cleanPath, isTextMime, mimeForPath, WORKSPACE_LIMITS, } from "@engenty-wizards/shared/workspace";
2
- import { and, asc, eq } from "drizzle-orm";
2
+ import { and, asc, eq, inArray } from "drizzle-orm";
3
3
  import { db, schema } from "../db/client.js";
4
4
  import { getBlob, putBlob } from "../files/blobs.js";
5
5
  import { emitDraftChanged, filesChanged } from "./draft-events.js";
@@ -76,6 +76,55 @@ export async function writeFile(userId, wizardId, rawPath, content, mime) {
76
76
  emitDraftChanged(filesChanged(w, path));
77
77
  return file;
78
78
  }
79
+ /**
80
+ * Keeps the bytes of files that are about to become a wizard's workspace, checked against the
81
+ * workspace limits, and says how their rows look — without touching the wizard yet. Bytes that
82
+ * nothing comes to refer to take no harm.
83
+ */
84
+ export async function keepFiles(files) {
85
+ if (files.length > WORKSPACE_LIMITS.files) {
86
+ throw new ServiceError("invalid", `A workspace holds up to ${WORKSPACE_LIMITS.files} files.`);
87
+ }
88
+ let total = 0;
89
+ const kept = [];
90
+ for (const file of files) {
91
+ const path = requirePath(file.path);
92
+ if (file.data.byteLength > WORKSPACE_LIMITS.fileBytes) {
93
+ throw new ServiceError("invalid", `${path} is larger than 5 MB.`);
94
+ }
95
+ total += file.data.byteLength;
96
+ if (total > WORKSPACE_LIMITS.totalBytes) {
97
+ throw new ServiceError("invalid", "The workspace would be larger than 25 MB.");
98
+ }
99
+ kept.push({
100
+ path,
101
+ hash: await putBlob(file.data),
102
+ mime: file.mime && file.mime !== "application/octet-stream" ? file.mime : mimeForPath(path),
103
+ size: file.data.byteLength,
104
+ });
105
+ }
106
+ return kept;
107
+ }
108
+ /** Makes a wizard's workspace exactly `files` (kept with keepFiles): rows only, no bytes. */
109
+ export async function replaceFiles(wizardId, files) {
110
+ const paths = files.map((f) => f.path);
111
+ const before = await draftFiles(wizardId);
112
+ const gone = before.filter((f) => !paths.includes(f.path)).map((f) => f.path);
113
+ if (gone.length) {
114
+ await db
115
+ .delete(schema.wizardFile)
116
+ .where(and(eq(schema.wizardFile.wizardId, wizardId), inArray(schema.wizardFile.path, gone)));
117
+ }
118
+ for (const file of files) {
119
+ await db
120
+ .insert(schema.wizardFile)
121
+ .values({ wizardId, ...file })
122
+ .onConflictDoUpdate({
123
+ target: [schema.wizardFile.wizardId, schema.wizardFile.path],
124
+ set: { hash: file.hash, mime: file.mime, size: file.size, updatedAt: new Date() },
125
+ });
126
+ }
127
+ }
79
128
  export async function deleteFile(userId, wizardId, rawPath) {
80
129
  const w = await writableWizard(userId, wizardId);
81
130
  const path = requirePath(rawPath);
@@ -4,7 +4,7 @@ import { WORKSPACE_LIMITS } from "@engenty-wizards/shared/workspace";
4
4
  import { and, count, eq, inArray, sql } from "drizzle-orm";
5
5
  import { nanoid } from "nanoid";
6
6
  import { z } from "zod";
7
- import { db, schema } from "../db/client.js";
7
+ import { db, inTransaction, schema } from "../db/client.js";
8
8
  import { missingModels, unavoidableSteps } from "../engine/requirements.js";
9
9
  import { env } from "../env.js";
10
10
  import { codeOf, putLink } from "../tenants/control.js";
@@ -12,7 +12,7 @@ import { currentTenant } from "../tenants/tenant.js";
12
12
  import { asSync, mayBuild } from "./access.js";
13
13
  import { emitDraftChanged } from "./draft-events.js";
14
14
  import { notFound, ServiceError } from "./errors.js";
15
- import { draftFiles, writeFile } from "./files.js";
15
+ import { keepFiles, replaceFiles } from "./files.js";
16
16
  import { addProjectFile, projectFiles, removeProjectFile } from "./project-files.js";
17
17
  import { brandColorSchema, projectFactSchema, projectLimit, removeProject, } from "./projects.js";
18
18
  import { deleteWizard, draftIssues, parseDraft, shareUrl } from "./wizards.js";
@@ -164,7 +164,10 @@ async function requireWizardRoom(spaceId) {
164
164
  }
165
165
  }
166
166
  const hashOf = (data) => createHash("sha256").update(data).digest("hex");
167
- /** The project's logo follows the install's: kept when it is the same picture, else replaced. */
167
+ /**
168
+ * The project's logo follows the install's: kept when it is the same picture, else replaced —
169
+ * the new one is stored before the old one goes, so a store that fails leaves the old one.
170
+ */
168
171
  async function syncLogo(projectId, logo) {
169
172
  const here = await projectFiles(projectId, "logo");
170
173
  const data = logo ? Buffer.from(logo.data, "base64") : null;
@@ -172,9 +175,6 @@ async function syncLogo(projectId, logo) {
172
175
  if (mark && here.length === 1 && here[0].source === mark) {
173
176
  return;
174
177
  }
175
- for (const old of here) {
176
- await removeProjectFile(projectId, old.id);
177
- }
178
178
  if (logo && data && mark) {
179
179
  await addProjectFile(projectId, {
180
180
  kind: "logo",
@@ -186,8 +186,11 @@ async function syncLogo(projectId, logo) {
186
186
  source: mark,
187
187
  });
188
188
  }
189
+ for (const old of here) {
190
+ await removeProjectFile(projectId, old.id);
191
+ }
189
192
  }
190
- /** Makes or updates the synced project: its name, what it says about itself, its logo. */
193
+ /** Makes or updates the synced project: its name and what it says about itself (the logo: syncLogo). */
191
194
  async function syncSpace(spaceId, space) {
192
195
  const existing = await syncedProject(spaceId);
193
196
  if (existing && existing.origin !== "local") {
@@ -215,7 +218,6 @@ async function syncSpace(spaceId, space) {
215
218
  ...values,
216
219
  });
217
220
  }
218
- await syncLogo(spaceId, space.logo);
219
221
  const project = await syncedProject(spaceId);
220
222
  if (!project) {
221
223
  throw notFound();
@@ -225,86 +227,84 @@ async function syncSpace(spaceId, space) {
225
227
  /**
226
228
  * Takes a wizard a local install published: the project it is in, the wizard under its id, its
227
229
  * workspace, and the version — published here when a run of it can finish. Sending the same
228
- * wizard again updates the copy; its link stays.
230
+ * wizard again updates the copy; its link stays. The files' bytes are kept first; then the
231
+ * project, the wizard, its workspace and the version are written in one transaction, so an
232
+ * install whose sending broke off finds nothing half done and sends again.
229
233
  */
230
- export async function syncWizard(userId, rawSpaceId, rawWizardId, input) {
234
+ export async function syncWizard(rawSpaceId, rawWizardId, input) {
231
235
  const spaceId = syncedId.parse(rawSpaceId);
232
236
  const wizardId = syncedId.parse(rawWizardId);
233
- const paths = input.files.map((f) => f.path);
234
237
  // The shape is checked before anything is written; a wrong one leaves nothing behind.
235
- const { draft } = parseDraft(input.definition, paths);
238
+ const { draft } = parseDraft(input.definition, input.files.map((f) => f.path));
236
239
  return asSync(async () => {
237
- const project = await syncSpace(spaceId, input.space);
238
- const existing = await db.query.wizard.findFirst({
239
- where: and(eq(schema.wizard.id, wizardId), eq(schema.wizard.tenantId, currentTenant())),
240
- });
241
- if (existing && existing.projectId !== spaceId) {
242
- throw new ServiceError("refused", "Ein Wizard mit dieser Kennung gehört hier woanders hin.", {
243
- reason: "id_taken",
240
+ const files = await keepFiles(input.files.map((f) => ({ path: f.path, mime: f.mime, data: Buffer.from(f.data, "base64") })));
241
+ const taken = await inTransaction(async () => {
242
+ const project = await syncSpace(spaceId, input.space);
243
+ const existing = await db.query.wizard.findFirst({
244
+ where: and(eq(schema.wizard.id, wizardId), eq(schema.wizard.tenantId, currentTenant())),
244
245
  });
245
- }
246
- const settings = {
247
- title: draft.title,
248
- draft,
249
- shareEnabled: input.shareEnabled,
250
- ...(input.dailyRunLimit ? { dailyRunLimit: input.dailyRunLimit } : {}),
251
- };
252
- if (!existing) {
253
- await requireWizardRoom(spaceId);
254
- }
255
- let shareToken = existing?.shareToken;
256
- if (existing) {
257
- await db
258
- .update(schema.wizard)
259
- .set({ ...settings, revision: sql `${schema.wizard.revision} + 1`, updatedAt: new Date() })
260
- .where(eq(schema.wizard.id, wizardId));
261
- }
262
- else {
263
- // The link is made here: a token an install brought could be one another tenant holds.
264
- shareToken = nanoid(14);
265
- await db.insert(schema.wizard).values({
266
- id: wizardId,
267
- projectId: spaceId,
268
- tenantId: currentTenant(),
269
- shareToken,
270
- dailyRunLimit: env.limits.defaultDailyRuns,
271
- ...settings,
272
- });
273
- await putLink(shareToken, "wizard", wizardId);
274
- }
275
- // The workspace becomes what was sent: files that are gone there go here.
276
- const before = await draftFiles(wizardId);
277
- const gone = before.filter((f) => !paths.includes(f.path)).map((f) => f.path);
278
- if (gone.length) {
279
- await db
280
- .delete(schema.wizardFile)
281
- .where(and(eq(schema.wizardFile.wizardId, wizardId), inArray(schema.wizardFile.path, gone)));
282
- }
283
- for (const file of input.files) {
284
- await writeFile(userId, wizardId, file.path, Buffer.from(file.data, "base64"), file.mime);
285
- }
286
- const row = await db.query.wizard.findFirst({ where: eq(schema.wizard.id, wizardId) });
287
- if (!row) {
288
- throw notFound();
289
- }
290
- const problems = await serverProblems(draft, await draftIssues(row), project.mcpServers.map((s) => s.id));
291
- const runnable = !problems.some((p) => p.blocking);
292
- let publishedVersion = row.publishedVersion;
293
- if (runnable) {
294
- const files = await draftFiles(wizardId);
295
- await db
296
- .insert(schema.wizardVersion)
297
- .values({ id: nanoid(12), wizardId, version: input.version, definition: draft, files })
298
- .onConflictDoUpdate({
299
- target: [schema.wizardVersion.wizardId, schema.wizardVersion.version],
300
- set: { definition: draft, files },
301
- });
302
- await db
303
- .update(schema.wizard)
304
- .set({ publishedVersion: input.version })
305
- .where(eq(schema.wizard.id, wizardId));
306
- publishedVersion = input.version;
307
- }
246
+ if (existing && existing.projectId !== spaceId) {
247
+ throw new ServiceError("refused", "Ein Wizard mit dieser Kennung gehört hier woanders hin.", {
248
+ reason: "id_taken",
249
+ });
250
+ }
251
+ const settings = {
252
+ title: draft.title,
253
+ draft,
254
+ shareEnabled: input.shareEnabled,
255
+ ...(input.dailyRunLimit ? { dailyRunLimit: input.dailyRunLimit } : {}),
256
+ };
257
+ if (!existing) {
258
+ await requireWizardRoom(spaceId);
259
+ }
260
+ if (existing) {
261
+ await db
262
+ .update(schema.wizard)
263
+ .set({ ...settings, revision: sql `${schema.wizard.revision} + 1`, updatedAt: new Date() })
264
+ .where(eq(schema.wizard.id, wizardId));
265
+ }
266
+ else {
267
+ await db.insert(schema.wizard).values({
268
+ id: wizardId,
269
+ projectId: spaceId,
270
+ tenantId: currentTenant(),
271
+ // The link is made here: a token an install brought could be one another tenant holds.
272
+ shareToken: nanoid(14),
273
+ dailyRunLimit: env.limits.defaultDailyRuns,
274
+ ...settings,
275
+ });
276
+ }
277
+ // The workspace becomes what was sent: files that are gone there go here.
278
+ await replaceFiles(wizardId, files);
279
+ const row = await db.query.wizard.findFirst({ where: eq(schema.wizard.id, wizardId) });
280
+ if (!row) {
281
+ throw notFound();
282
+ }
283
+ const problems = await serverProblems(draft, await draftIssues(row), project.mcpServers.map((s) => s.id));
284
+ const runnable = !problems.some((p) => p.blocking);
285
+ let publishedVersion = row.publishedVersion;
286
+ if (runnable) {
287
+ await db
288
+ .insert(schema.wizardVersion)
289
+ .values({ id: nanoid(12), wizardId, version: input.version, definition: draft, files })
290
+ .onConflictDoUpdate({
291
+ target: [schema.wizardVersion.wizardId, schema.wizardVersion.version],
292
+ set: { definition: draft, files },
293
+ });
294
+ await db
295
+ .update(schema.wizard)
296
+ .set({ publishedVersion: input.version })
297
+ .where(eq(schema.wizard.id, wizardId));
298
+ publishedVersion = input.version;
299
+ }
300
+ // Last, since it reaches the object store: a store that fails takes everything back.
301
+ await syncLogo(spaceId, input.space.logo);
302
+ return { row, problems, runnable, publishedVersion };
303
+ });
304
+ const { row, problems, runnable, publishedVersion } = taken;
305
+ // The public link lives in the control database, outside the transaction: set on every
306
+ // sync, so one that failed to get there once is there after the next.
307
+ await putLink(row.shareToken, "wizard", wizardId);
308
308
  emitDraftChanged({
309
309
  wizardId,
310
310
  kind: "draft",
@@ -316,8 +316,8 @@ export async function syncWizard(userId, rawSpaceId, rawWizardId, input) {
316
316
  });
317
317
  return {
318
318
  wizardId,
319
- shareUrl: shareUrl(shareToken ?? row.shareToken),
320
- code: await codeOf(shareToken ?? row.shareToken),
319
+ shareUrl: shareUrl(row.shareToken),
320
+ code: await codeOf(row.shareToken),
321
321
  shareEnabled: row.shareEnabled,
322
322
  publishedVersion,
323
323
  runnable,
@@ -4,8 +4,8 @@ import { join } from "node:path";
4
4
  import { installedByScript, isCheckout, layout, packageRoot, packageVersion } from "./cli/home.js";
5
5
  const RELEASES_URL = process.env.ENGENTY_WIZARDS_RELEASES_URL?.trim() ||
6
6
  "https://api.github.com/repos/engenty/engenty-wizards/releases/latest";
7
- const FOUND_FOR_MS = 6 * 60 * 60_000;
8
- const MISSED_FOR_MS = 60 * 60_000;
7
+ /** How long an answer from GitHub holds, found or not, before the runtime asks again. */
8
+ const CHECK_EVERY_MS = 60 * 60_000;
9
9
  /**
10
10
  * The exit code with which the runtime asks the command that started it for a restart: after an
11
11
  * update, that command starts the new version (cli/start.ts).
@@ -47,7 +47,7 @@ async function newestRelease() {
47
47
  return { latest: null, url: null };
48
48
  }
49
49
  const age = cached ? Date.now() - cached.at : Number.POSITIVE_INFINITY;
50
- if (cached && age < (cached.latest ? FOUND_FOR_MS : MISSED_FOR_MS)) {
50
+ if (cached && age < CHECK_EVERY_MS) {
51
51
  return cached;
52
52
  }
53
53
  let found = { latest: null, url: null };
@@ -1 +1 @@
1
- import{a as e,n as t,t as n}from"./jsx-runtime-D3jfb0Ew.js";import{t as r}from"./preload-helper-CNEI7N0N.js";import{a as i,d as a,h as o,i as s,s as c,y as l}from"./ui-iTYHqwvY.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/,``):l(`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}function _({open:e,onClose:t}){let[n,_]=(0,u.useState)(null),[v,y]=(0,u.useState)(``);(0,u.useEffect)(()=>{if(!e||n)return;let t=!1;return r(()=>import(`./changelog-Cto0j5-V.js`).then(e=>{t||_(e.default)}),[]),()=>{t=!0}},[e,n]),(0,u.useEffect)(()=>{e||y(``)},[e]);let b=n?g(n,v):[];return(0,d.jsxs)(s,{open:e,onClose:t,title:l(`about.changelog`),wide:!0,children:[(0,d.jsx)(`p`,{className:`-mt-3 mb-4 text-[14px] text-ink-3`,children:l(`about.changelogHint`)}),n===null?(0,d.jsx)(`div`,{className:`flex justify-center py-10 text-ink-3`,children:(0,d.jsx)(a,{})}):n.length===0?(0,d.jsx)(i,{children:l(`about.changelogEmpty`)}):(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(c,{type:`search`,value:v,placeholder:l(`about.search`),onChange:e=>y(e.target.value)}),b.length===0?(0,d.jsx)(i,{children:l(`about.noMatches`)}):null,b.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-[16px]`,children:[p(e),(0,d.jsx)(`span`,{className:`font-normal font-sans text-[13px] text-ink-3 tabular-nums`,children:m(e)})]}),(0,d.jsx)(`ul`,{className:`mt-3 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-[14px]`,children:[(0,d.jsx)(`span`,{className:o(`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-[12px] 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-[12px] text-rose`,children:l(`about.breaking`)}):null]})]},e.id)})})]},e.version??`unreleased`))]})]})}export{_ as ChangelogDialog};
1
+ import{a as e,n as t,t as n}from"./jsx-runtime-D3jfb0Ew.js";import{t as r}from"./preload-helper-CNEI7N0N.js";import{a as i,d as a,h as o,i as s,s as c,y as l}from"./ui-C3bTWmdM.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/,``):l(`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}function _({open:e,onClose:t}){let[n,_]=(0,u.useState)(null),[v,y]=(0,u.useState)(``);(0,u.useEffect)(()=>{if(!e||n)return;let t=!1;return r(()=>import(`./changelog-CLmll9By.js`).then(e=>{t||_(e.default)}),[]),()=>{t=!0}},[e,n]),(0,u.useEffect)(()=>{e||y(``)},[e]);let b=n?g(n,v):[];return(0,d.jsxs)(s,{open:e,onClose:t,title:l(`about.changelog`),wide:!0,children:[(0,d.jsx)(`p`,{className:`-mt-3 mb-4 text-[14px] text-ink-3`,children:l(`about.changelogHint`)}),n===null?(0,d.jsx)(`div`,{className:`flex justify-center py-10 text-ink-3`,children:(0,d.jsx)(a,{})}):n.length===0?(0,d.jsx)(i,{children:l(`about.changelogEmpty`)}):(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(c,{type:`search`,value:v,placeholder:l(`about.search`),onChange:e=>y(e.target.value)}),b.length===0?(0,d.jsx)(i,{children:l(`about.noMatches`)}):null,b.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-[16px]`,children:[p(e),(0,d.jsx)(`span`,{className:`font-normal font-sans text-[13px] text-ink-3 tabular-nums`,children:m(e)})]}),(0,d.jsx)(`ul`,{className:`mt-3 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-[14px]`,children:[(0,d.jsx)(`span`,{className:o(`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-[12px] 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-[12px] text-rose`,children:l(`about.breaking`)}):null]})]},e.id)})})]},e.version??`unreleased`))]})]})}export{_ as ChangelogDialog};
@@ -1 +1 @@
1
- import{a as e,n as t,t as n}from"./jsx-runtime-D3jfb0Ew.js";import{c as r,d as i,i as a,n as o,nt as s}from"./api-9sHuNTkp.js";import{t as c}from"./createLucideIcon-q3MZMmnf.js";import{t as l}from"./plug-CLjyCoHB.js";import{C as u,D as d,b as f,r as p,x as m}from"./outputs-boPx5ngv.js";import{r as h}from"./wand-sparkles-CKxArWPY.js";import{t as g}from"./plus-Du-Jx8aI.js";import{t as _}from"./mic-Bvz4mA2j.js";import{_ as v,d as y,h as b,o as x,y as S}from"./ui-iTYHqwvY.js";import{l as C}from"./index-VsRgvimv.js";var w={name:`chevron-up`,size:24,node:[[`path`,{d:`m18 15-6-6-6 6`,key:`153udz`}]]};w.node;var T=c(w),E={name:`lock`,size:24,node:[[`rect`,{width:`18`,height:`11`,x:`3`,y:`11`,rx:`2`,ry:`2`,key:`1w4ew1`}],[`path`,{d:`M7 11V7a5 5 0 0 1 10 0v4`,key:`fwvmzm`}]]};E.node;var D=c(E),O=e(t(),1),k=!1;function A(){if(!(typeof window>`u`||k))return window.SpeechRecognition??window.webkitSpeechRecognition}var j=typeof navigator<`u`&&!!navigator.mediaDevices?.getUserMedia&&typeof MediaRecorder<`u`;function M(e,t){let n=e.trimEnd(),r=t.trim();return n&&r?`${n} ${r}`:n||r}function N(e,t){let[n,r]=(0,O.useState)(!1),[i,a]=(0,O.useState)(!1),[s,c]=(0,O.useState)(null),l=(0,O.useRef)(e);l.current=e;let u=(0,O.useRef)(t);u.current=t;let d=(0,O.useRef)(null);(0,O.useEffect)(()=>()=>d.current?.stop(),[]);let f=async()=>{let e=l.current,t;try{t=await navigator.mediaDevices.getUserMedia({audio:!0})}catch{c(`denied`);return}let n=[],i=new MediaRecorder(t);i.ondataavailable=e=>e.data.size&&n.push(e.data),i.onstop=async()=>{for(let e of t.getTracks())e.stop();d.current=null,r(!1);let s=(i.mimeType||`audio/webm`).split(`;`)[0],l=new File(n,`dictation`,{type:s});if(l.size){a(!0);try{let{text:t}=await o.upload(`/api/studio/transcribe`,l);t.trim()&&u.current(M(e,t))}catch(e){c(e.message)}finally{a(!1)}}},d.current={stop:()=>i.state!==`inactive`&&i.stop()},i.start(),r(!0)},p=e=>{let t=l.current,n=``,i=new e;i.continuous=!0,i.interimResults=!0,i.lang=v===`de`?`de-DE`:`en-US`,i.onresult=e=>{let r=``;for(let t=Math.max(0,e.resultIndex);t<e.results.length;t++){let i=e.results[t],a=i[0]?.transcript??``;i.isFinal?n=M(n,a):r+=a}u.current(M(t,M(n,r)))},i.onerror=e=>{if(e.error!==`aborted`&&e.error!==`no-speech`){if(e.error===`not-allowed`){c(`denied`);return}k=!0,j&&!n?(i.onend=null,d.current=null,f()):c(e.error)}},i.onend=()=>{d.current=null,r(!1)},d.current={stop:()=>i.stop()},i.start(),r(!0)};return{supported:!!A()||j,listening:n,processing:i,error:s,toggle:()=>{if(i)return;if(d.current){d.current.stop();return}c(null);let e=A();e?p(e):f()}}}var P=e(n(),1),F=/\.(png|jpe?g|gif|webp|avif|svg)$/i;function I(e){let t=e.filter(e=>e.type.startsWith(`image/`));return t.length?Object.fromEntries(t.map(e=>[e.name,URL.createObjectURL(e)])):void 0}function L(e){let t=e.match(/(?:^|\n\n)(?:Angehängt|Attached): ([^\n]+)$/);return t?{text:e.slice(0,t.index).trim(),names:t[1].split(`, `).filter(Boolean)}:{text:e,names:[]}}function R(e,t){if(e.name&&e.name!==`image.png`)return e;let n=e.type.split(`/`)[1]?.replace(`jpeg`,`jpg`)||`png`,r=new Date().toISOString().slice(0,19).replace(/[-:]/g,``).replace(`T`,`-`);return new File([e],`paste-${r}${t?`-${t+1}`:``}.${n}`,{type:e.type})}function z({url:e,label:t,children:n}){return(0,P.jsxs)(`div`,{className:`group relative`,title:t,children:[(0,P.jsx)(`img`,{src:e,alt:t,width:64,height:64,className:`size-16 rounded-lg bg-paper-2 object-cover ring-1 ring-border`}),n]})}function B({names:e,previews:t,fileUrl:n}){let r=e.filter(e=>t?.[e]||n&&F.test(e)),i=e.filter(e=>!r.includes(e));return(0,P.jsxs)(`div`,{className:`flex flex-col items-end gap-1.5`,children:[r.map(e=>{let r=t?.[e]??n?.(e)??``;return(0,P.jsx)(`a`,{href:r,target:`_blank`,rel:`noreferrer`,title:e,children:(0,P.jsx)(`img`,{src:r,alt:e,className:`max-h-60 w-auto max-w-full rounded-xl bg-paper-2 object-contain ring-1 ring-border`})},e)}),i.length?(0,P.jsx)(`div`,{className:`flex flex-wrap justify-end gap-1.5`,children:i.map(e=>(0,P.jsxs)(`span`,{className:`inline-flex max-w-full items-center gap-1.5 rounded-full bg-paper-2 px-2.5 py-1 text-[12px] text-ink-2`,children:[(0,P.jsx)(u,{className:`size-3 shrink-0 text-ink-4`}),(0,P.jsx)(`span`,{className:`truncate`,children:e})]},e))}):null]})}function V(e,t){let n=s(),[i,o]=(0,O.useState)([]),[c,l]=(0,O.useState)(`idle`),[u,d]=(0,O.useState)(null),[f,p]=(0,O.useState)(null),[m,h]=(0,O.useState)(null),[g,_]=(0,O.useState)(null),v=(0,O.useRef)(null);return(0,O.useEffect)(()=>{if(e){if(v.current!==e.id){v.current=e.id,o(e.messages);return}c===`idle`&&o(t=>e.messages.length>=t.length?e.messages:t)}},[e,c]),{messages:i,phase:c,activity:f,thought:m,startedAt:g,error:u,send:async(i,s=[])=>{if(!e||!(i.trim()||s.length)||c!==`idle`)return!1;if(d(null),_(Date.now()),h(null),s.length){l(`thinking`);try{for(let t of s){let n=await fetch(r(`/api/studio/wizards/${e.id}/files/${encodeURIComponent(t.name)}`),{method:`PUT`,credentials:`include`,headers:{"content-type":t.type||`application/octet-stream`},body:t});if(!n.ok){let e=await n.json().catch(()=>({}));throw Error(e.error??n.statusText)}}}catch(t){return d(t.message),l(`idle`),_(null),await n.invalidateQueries({queryKey:[`wizard`,e.id]}),!1}let t=`${S(`editor.attached`)}: ${s.map(e=>e.name).join(`, `)}`;i=i.trim()?`${i}\n\n${t}`:t}let u=`p-${Date.now()}`;o(e=>[...e,{id:`u-${Date.now()}`,role:`user`,content:i,previews:I(s)},{id:u,role:`assistant`,content:``,pending:!0}]),l(`thinking`),p(null);let f=!1;try{await a(`/api/studio/wizards/${e.id}/chat`,{message:i},(e,n)=>{if(e===`text`){let e=JSON.parse(n);o(t=>t.map(t=>t.id===u?{...t,content:t.content+e}:t))}else if(e===`activity`)p(JSON.parse(n));else if(e===`thought`)h(JSON.parse(n));else if(e===`building`)l(`building`);else if(e===`done`){f=!0;let e=JSON.parse(n);o(t=>t.map(t=>t.id===u?{...t,content:e.reply,changed:e.changed,pending:!1}:t)),e.changed&&t(e.draft,e.revision)}else e===`error`&&(f=!0,d(JSON.parse(n).message))}),f||d(S(`editor.chatBroken`))}catch(e){d(e.message)}finally{p(null),h(null),_(null),o(e=>e.filter(e=>!(e.id===u&&!e.content)).map(e=>({...e,pending:!1}))),l(`idle`),await n.invalidateQueries({queryKey:[`wizard`,e.id]}),await n.invalidateQueries({queryKey:[`me`]})}return!0},fileUrl:t=>r(`/api/studio/wizards/${e?.id}/files/${encodeURIComponent(t)}`)}}function H(e){let[t,n]=(0,O.useState)(()=>Date.now());return(0,O.useEffect)(()=>{if(e===null)return;n(Date.now());let t=setInterval(()=>n(Date.now()),1e3);return()=>clearInterval(t)},[e]),e===null?null:Math.max(0,Math.floor((t-e)/1e3))}function U(e){return e<60?`${e} s`:`${Math.floor(e/60)}:${String(e%60).padStart(2,`0`)} min`}function W({chat:e,thought:t,className:n,spinner:r=`size-3.5`}){let i=H(e.startedAt);return(0,P.jsxs)(`div`,{className:b(`min-w-0`,n),children:[(0,P.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2`,children:[(0,P.jsx)(y,{className:b(`shrink-0`,r)}),(0,P.jsx)(`span`,{className:`min-w-0 truncate`,children:e.activity??(e.phase===`building`?S(`editor.building`):S(`editor.thinking`))}),i===null?null:(0,P.jsx)(`span`,{className:`shrink-0 text-ink-4 tabular-nums`,children:U(i)})]}),t&&e.thought?(0,P.jsx)(`p`,{className:`mt-0.5 truncate pl-5.5 text-[12px] text-ink-4`,children:e.thought}):null]})}function G({message:e,fileUrl:t}){let{text:n,names:r}=L(e.content);return(0,P.jsxs)(`div`,{className:`ml-8 flex flex-col items-end gap-1.5 self-end`,children:[r.length?(0,P.jsx)(B,{names:r,previews:e.previews,fileUrl:t}):null,n?(0,P.jsx)(`div`,{className:`rounded-xl rounded-br-md bg-paper-2 px-4 py-2.5 text-[14px] leading-relaxed`,children:(0,P.jsx)(`span`,{className:`whitespace-pre-wrap`,children:n})}):null]})}function K({chat:e,avatar:t,hello:n=S(`editor.chatHello`),placeholder:r=S(`editor.composer`),changedLabel:a=`Ablauf aktualisiert`,compact:o,intro:s,closed:c}){let[v,w]=(0,O.useState)(!1),[E,D]=(0,O.useState)(``),[k,A]=(0,O.useState)([]),j=(0,O.useRef)(null),M=(0,O.useRef)(null),F=(0,O.useRef)(null),I=N(E,D);(0,O.useEffect)(()=>{j.current?.scrollTo({top:j.current.scrollHeight,behavior:`smooth`})},[e.messages,e.phase]),(0,O.useEffect)(()=>{let e=M.current;e&&(e.style.height=`auto`,e.style.height=`${Math.min(e.scrollHeight,200)}px`)},[E]);let L=(E.trim()||k.length>0)&&e.phase===`idle`,B=e=>A(t=>[...t.filter(t=>!e.some(e=>e.name===t.name)),...e]),[V,H]=(0,O.useState)(new Map);(0,O.useEffect)(()=>{let e=new Map(k.filter(e=>e.type.startsWith(`image/`)).map(e=>[e,URL.createObjectURL(e)]));return H(e),()=>{for(let t of e.values())URL.revokeObjectURL(t)}},[k]);let U=async()=>{if(!L)return;I.listening&&I.toggle();let t={text:E,files:k};D(``),A([]),await e.send(t.text.trim(),t.files)||(D(t.text),A(t.files))},K=!o||v,q=e.messages.at(-1);return(0,P.jsxs)(`div`,{className:`flex h-full min-h-0 flex-col`,children:[o&&(q||e.error)?(0,P.jsxs)(`button`,{type:`button`,"aria-expanded":v,"aria-label":S(`editor.thread`),onClick:()=>w(e=>!e),className:`flex items-center gap-2 px-4 pt-2.5 pb-0.5 text-left text-[13px] text-ink-3 transition hover:text-ink`,children:[!(e.error||v)&&q?.pending?(0,P.jsx)(W,{chat:e,className:`flex-1`}):(0,P.jsx)(`span`,{className:b(`min-w-0 flex-1 truncate`,e.error&&`text-rose`),children:e.error??(v?S(`editor.thread`):q?.content)}),v?(0,P.jsx)(h,{className:`size-4 shrink-0`}):(0,P.jsx)(T,{className:`size-4 shrink-0`})]}):null,(0,P.jsxs)(`div`,{ref:j,className:b(`min-h-0 flex-1 overflow-y-auto px-5 py-5`,!K&&`hidden`),children:[e.messages.length===0&&!c?s??(0,P.jsxs)(`div`,{className:`flex items-start gap-3`,children:[(0,P.jsx)(i,{kind:t,size:32,interactive:!1}),(0,P.jsx)(`p`,{className:`pt-1 text-[14px] text-ink-2 leading-relaxed`,children:n})]}):null,(0,P.jsx)(`div`,{className:`flex flex-col gap-4`,children:e.messages.map(n=>n.role===`user`?(0,P.jsx)(G,{message:n,fileUrl:e.fileUrl},n.id):(0,P.jsxs)(`div`,{className:`flex items-start gap-3`,children:[(0,P.jsx)(`div`,{className:`shrink-0`,children:(0,P.jsx)(i,{kind:t,size:28,interactive:!1})}),(0,P.jsxs)(`div`,{className:`min-w-0 pt-0.5 text-[14px]`,children:[n.source===`mcp`?(0,P.jsxs)(`div`,{className:`mb-1 inline-flex items-center gap-1.5 text-[12px] text-ink-3`,children:[(0,P.jsx)(l,{className:`size-3`}),` `,S(`editor.viaClient`,{client:n.client??`MCP`})]}):null,n.content?(0,P.jsx)(p,{text:n.content,className:`text-[14px]`}):null,n.pending?(0,P.jsx)(W,{chat:e,thought:!0,className:`mt-1 text-[13px] text-ink-3`}):null,n.changed?(0,P.jsxs)(`div`,{className:`mt-2 inline-flex items-center gap-1.5 rounded-full bg-ember-tint px-2.5 py-0.5 text-[12px] text-ember-strong`,children:[(0,P.jsx)(m,{className:`size-3`}),` `,a]}):null]})]},n.id))}),e.error?(0,P.jsx)(`div`,{className:`mt-4 rounded-lg bg-rose-tint px-3 py-2 text-[13px] text-rose`,children:e.error}):null]}),c?(0,P.jsx)(`p`,{className:`m-3 rounded-xl bg-paper-2 px-4 py-3 text-[13px] text-ink-2 leading-relaxed`,children:c}):(0,P.jsxs)(`div`,{className:o?`p-2`:`p-3`,children:[(0,P.jsxs)(`div`,{className:`rounded-xl bg-card p-1.5 shadow-soft ring-1 ring-border focus-within:ring-focus`,children:[V.size?(0,P.jsx)(`div`,{className:`flex flex-wrap gap-2 px-1 pt-1 pb-1.5`,children:k.map((e,t)=>{let n=V.get(e);return n?(0,P.jsx)(z,{url:n,label:e.name,children:(0,P.jsx)(`button`,{type:`button`,"aria-label":S(`editor.detach`),onClick:()=>A(e=>e.filter((e,n)=>n!==t)),className:`-top-1.5 -right-1.5 absolute flex size-5 items-center justify-center rounded-full bg-ink text-paper opacity-0 shadow-soft transition focus-visible:opacity-100 group-hover:opacity-100`,children:(0,P.jsx)(C,{className:`size-3`})})},`${e.name}-${t}`):null})}):null,k.some(e=>!V.has(e))?(0,P.jsx)(`ul`,{className:`flex flex-wrap gap-1.5 px-1 pt-1 pb-1.5`,children:k.map((e,t)=>V.has(e)?null:(0,P.jsxs)(`li`,{className:`inline-flex max-w-full items-center gap-1.5 rounded-full bg-paper-2 py-1 pr-1 pl-2.5 text-[12px] text-ink-2`,children:[(0,P.jsx)(u,{className:`size-3 shrink-0 text-ink-4`}),(0,P.jsx)(`span`,{className:`truncate`,children:e.name}),(0,P.jsx)(`button`,{type:`button`,"aria-label":S(`editor.detach`),onClick:()=>A(e=>e.filter((e,n)=>n!==t)),className:`inline-flex size-5 shrink-0 items-center justify-center rounded-full text-ink-3 hover:bg-paper-3 hover:text-ink`,children:(0,P.jsx)(C,{className:`size-3`})})]},`${e.name}-${t}`))}):null,(0,P.jsxs)(`div`,{className:`flex items-end gap-1`,children:[(0,P.jsx)(x,{label:S(`editor.attach`),onClick:()=>F.current?.click(),children:(0,P.jsx)(g,{className:`size-4`})}),(0,P.jsx)(`input`,{ref:F,type:`file`,multiple:!0,hidden:!0,onChange:e=>{let t=Array.from(e.target.files??[]);e.target.value=``,B(t)}}),(0,P.jsx)(`textarea`,{ref:M,rows:1,value:E,onChange:e=>D(e.target.value),onPaste:e=>{let t=Array.from(e.clipboardData.files);t.length&&(e.preventDefault(),B(t.map(R)))},onKeyDown:e=>{e.key===`Enter`&&!e.shiftKey&&(e.preventDefault(),U())},placeholder:I.listening?S(`editor.listening`):r,className:`max-h-[200px] min-h-[40px] flex-1 resize-none bg-transparent px-1.5 py-2 text-[14px] outline-none placeholder:text-ink-4`}),I.supported?(0,P.jsx)(x,{label:I.processing?S(`editor.transcribing`):I.listening?S(`editor.voiceStop`):S(`editor.voice`),"aria-pressed":I.listening,disabled:I.processing,onClick:I.toggle,className:b(I.listening&&`bg-rose-tint text-rose hover:bg-rose-tint hover:text-rose`),children:I.processing?(0,P.jsx)(y,{className:`size-4`}):I.listening?(0,P.jsx)(f,{className:`size-3.5 animate-pulse-dot fill-current`}):(0,P.jsx)(_,{className:`size-4`})}):null,(0,P.jsx)(`button`,{type:`button`,onClick:()=>void U(),disabled:!L,className:b(`inline-flex size-9 shrink-0 items-center justify-center rounded-full transition`,L?`bg-primary text-primary-foreground`:`bg-paper-2 text-ink-4`),"aria-label":`Send`,children:(0,P.jsx)(d,{className:`size-4`})})]})]}),I.error?(0,P.jsx)(`p`,{className:`mt-2 px-1 text-[12px] text-rose`,children:I.error===`denied`?S(`editor.micDenied`):I.error}):null]})]})}export{D as a,V as i,W as n,I as r,K as t};
1
+ import{a as e,n as t,t as n}from"./jsx-runtime-D3jfb0Ew.js";import{c as r,d as i,i as a,n as o,nt as s}from"./api-BTfKh3La.js";import{t as c}from"./createLucideIcon-q3MZMmnf.js";import{t as l}from"./plug-CLjyCoHB.js";import{C as u,D as d,b as f,r as p,x as m}from"./outputs-n_yhxmMk.js";import{r as h}from"./wand-sparkles-CKxArWPY.js";import{t as g}from"./plus-Du-Jx8aI.js";import{t as _}from"./mic-Bvz4mA2j.js";import{_ as v,d as y,h as b,o as x,y as S}from"./ui-C3bTWmdM.js";import{l as C}from"./index-DOpyzQiU.js";var w={name:`chevron-up`,size:24,node:[[`path`,{d:`m18 15-6-6-6 6`,key:`153udz`}]]};w.node;var T=c(w),E={name:`lock`,size:24,node:[[`rect`,{width:`18`,height:`11`,x:`3`,y:`11`,rx:`2`,ry:`2`,key:`1w4ew1`}],[`path`,{d:`M7 11V7a5 5 0 0 1 10 0v4`,key:`fwvmzm`}]]};E.node;var D=c(E),O=e(t(),1),k=!1;function A(){if(!(typeof window>`u`||k))return window.SpeechRecognition??window.webkitSpeechRecognition}var j=typeof navigator<`u`&&!!navigator.mediaDevices?.getUserMedia&&typeof MediaRecorder<`u`;function M(e,t){let n=e.trimEnd(),r=t.trim();return n&&r?`${n} ${r}`:n||r}function N(e,t){let[n,r]=(0,O.useState)(!1),[i,a]=(0,O.useState)(!1),[s,c]=(0,O.useState)(null),l=(0,O.useRef)(e);l.current=e;let u=(0,O.useRef)(t);u.current=t;let d=(0,O.useRef)(null);(0,O.useEffect)(()=>()=>d.current?.stop(),[]);let f=async()=>{let e=l.current,t;try{t=await navigator.mediaDevices.getUserMedia({audio:!0})}catch{c(`denied`);return}let n=[],i=new MediaRecorder(t);i.ondataavailable=e=>e.data.size&&n.push(e.data),i.onstop=async()=>{for(let e of t.getTracks())e.stop();d.current=null,r(!1);let s=(i.mimeType||`audio/webm`).split(`;`)[0],l=new File(n,`dictation`,{type:s});if(l.size){a(!0);try{let{text:t}=await o.upload(`/api/studio/transcribe`,l);t.trim()&&u.current(M(e,t))}catch(e){c(e.message)}finally{a(!1)}}},d.current={stop:()=>i.state!==`inactive`&&i.stop()},i.start(),r(!0)},p=e=>{let t=l.current,n=``,i=new e;i.continuous=!0,i.interimResults=!0,i.lang=v===`de`?`de-DE`:`en-US`,i.onresult=e=>{let r=``;for(let t=Math.max(0,e.resultIndex);t<e.results.length;t++){let i=e.results[t],a=i[0]?.transcript??``;i.isFinal?n=M(n,a):r+=a}u.current(M(t,M(n,r)))},i.onerror=e=>{if(e.error!==`aborted`&&e.error!==`no-speech`){if(e.error===`not-allowed`){c(`denied`);return}k=!0,j&&!n?(i.onend=null,d.current=null,f()):c(e.error)}},i.onend=()=>{d.current=null,r(!1)},d.current={stop:()=>i.stop()},i.start(),r(!0)};return{supported:!!A()||j,listening:n,processing:i,error:s,toggle:()=>{if(i)return;if(d.current){d.current.stop();return}c(null);let e=A();e?p(e):f()}}}var P=e(n(),1),F=/\.(png|jpe?g|gif|webp|avif|svg)$/i;function I(e){let t=e.filter(e=>e.type.startsWith(`image/`));return t.length?Object.fromEntries(t.map(e=>[e.name,URL.createObjectURL(e)])):void 0}function L(e){let t=e.match(/(?:^|\n\n)(?:Angehängt|Attached): ([^\n]+)$/);return t?{text:e.slice(0,t.index).trim(),names:t[1].split(`, `).filter(Boolean)}:{text:e,names:[]}}function R(e,t){if(e.name&&e.name!==`image.png`)return e;let n=e.type.split(`/`)[1]?.replace(`jpeg`,`jpg`)||`png`,r=new Date().toISOString().slice(0,19).replace(/[-:]/g,``).replace(`T`,`-`);return new File([e],`paste-${r}${t?`-${t+1}`:``}.${n}`,{type:e.type})}function z({url:e,label:t,children:n}){return(0,P.jsxs)(`div`,{className:`group relative`,title:t,children:[(0,P.jsx)(`img`,{src:e,alt:t,width:64,height:64,className:`size-16 rounded-lg bg-paper-2 object-cover ring-1 ring-border`}),n]})}function B({names:e,previews:t,fileUrl:n}){let r=e.filter(e=>t?.[e]||n&&F.test(e)),i=e.filter(e=>!r.includes(e));return(0,P.jsxs)(`div`,{className:`flex flex-col items-end gap-1.5`,children:[r.map(e=>{let r=t?.[e]??n?.(e)??``;return(0,P.jsx)(`a`,{href:r,target:`_blank`,rel:`noreferrer`,title:e,children:(0,P.jsx)(`img`,{src:r,alt:e,className:`max-h-60 w-auto max-w-full rounded-xl bg-paper-2 object-contain ring-1 ring-border`})},e)}),i.length?(0,P.jsx)(`div`,{className:`flex flex-wrap justify-end gap-1.5`,children:i.map(e=>(0,P.jsxs)(`span`,{className:`inline-flex max-w-full items-center gap-1.5 rounded-full bg-paper-2 px-2.5 py-1 text-[12px] text-ink-2`,children:[(0,P.jsx)(u,{className:`size-3 shrink-0 text-ink-4`}),(0,P.jsx)(`span`,{className:`truncate`,children:e})]},e))}):null]})}function V(e,t){let n=s(),[i,o]=(0,O.useState)([]),[c,l]=(0,O.useState)(`idle`),[u,d]=(0,O.useState)(null),[f,p]=(0,O.useState)(null),[m,h]=(0,O.useState)(null),[g,_]=(0,O.useState)(null),v=(0,O.useRef)(null);return(0,O.useEffect)(()=>{if(e){if(v.current!==e.id){v.current=e.id,o(e.messages);return}c===`idle`&&o(t=>e.messages.length>=t.length?e.messages:t)}},[e,c]),{messages:i,phase:c,activity:f,thought:m,startedAt:g,error:u,send:async(i,s=[])=>{if(!e||!(i.trim()||s.length)||c!==`idle`)return!1;if(d(null),_(Date.now()),h(null),s.length){l(`thinking`);try{for(let t of s){let n=await fetch(r(`/api/studio/wizards/${e.id}/files/${encodeURIComponent(t.name)}`),{method:`PUT`,credentials:`include`,headers:{"content-type":t.type||`application/octet-stream`},body:t});if(!n.ok){let e=await n.json().catch(()=>({}));throw Error(e.error??n.statusText)}}}catch(t){return d(t.message),l(`idle`),_(null),await n.invalidateQueries({queryKey:[`wizard`,e.id]}),!1}let t=`${S(`editor.attached`)}: ${s.map(e=>e.name).join(`, `)}`;i=i.trim()?`${i}\n\n${t}`:t}let u=`p-${Date.now()}`;o(e=>[...e,{id:`u-${Date.now()}`,role:`user`,content:i,previews:I(s)},{id:u,role:`assistant`,content:``,pending:!0}]),l(`thinking`),p(null);let f=!1;try{await a(`/api/studio/wizards/${e.id}/chat`,{message:i},(e,n)=>{if(e===`text`){let e=JSON.parse(n);o(t=>t.map(t=>t.id===u?{...t,content:t.content+e}:t))}else if(e===`activity`)p(JSON.parse(n));else if(e===`thought`)h(JSON.parse(n));else if(e===`building`)l(`building`);else if(e===`done`){f=!0;let e=JSON.parse(n);o(t=>t.map(t=>t.id===u?{...t,content:e.reply,changed:e.changed,pending:!1}:t)),e.changed&&t(e.draft,e.revision)}else e===`error`&&(f=!0,d(JSON.parse(n).message))}),f||d(S(`editor.chatBroken`))}catch(e){d(e.message)}finally{p(null),h(null),_(null),o(e=>e.filter(e=>!(e.id===u&&!e.content)).map(e=>({...e,pending:!1}))),l(`idle`),await n.invalidateQueries({queryKey:[`wizard`,e.id]}),await n.invalidateQueries({queryKey:[`me`]})}return!0},fileUrl:t=>r(`/api/studio/wizards/${e?.id}/files/${encodeURIComponent(t)}`)}}function H(e){let[t,n]=(0,O.useState)(()=>Date.now());return(0,O.useEffect)(()=>{if(e===null)return;n(Date.now());let t=setInterval(()=>n(Date.now()),1e3);return()=>clearInterval(t)},[e]),e===null?null:Math.max(0,Math.floor((t-e)/1e3))}function U(e){return e<60?`${e} s`:`${Math.floor(e/60)}:${String(e%60).padStart(2,`0`)} min`}function W({chat:e,thought:t,className:n,spinner:r=`size-3.5`}){let i=H(e.startedAt);return(0,P.jsxs)(`div`,{className:b(`min-w-0`,n),children:[(0,P.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2`,children:[(0,P.jsx)(y,{className:b(`shrink-0`,r)}),(0,P.jsx)(`span`,{className:`min-w-0 truncate`,children:e.activity??(e.phase===`building`?S(`editor.building`):S(`editor.thinking`))}),i===null?null:(0,P.jsx)(`span`,{className:`shrink-0 text-ink-4 tabular-nums`,children:U(i)})]}),t&&e.thought?(0,P.jsx)(`p`,{className:`mt-0.5 truncate pl-5.5 text-[12px] text-ink-4`,children:e.thought}):null]})}function G({message:e,fileUrl:t}){let{text:n,names:r}=L(e.content);return(0,P.jsxs)(`div`,{className:`ml-8 flex flex-col items-end gap-1.5 self-end`,children:[r.length?(0,P.jsx)(B,{names:r,previews:e.previews,fileUrl:t}):null,n?(0,P.jsx)(`div`,{className:`rounded-xl rounded-br-md bg-paper-2 px-4 py-2.5 text-[14px] leading-relaxed`,children:(0,P.jsx)(`span`,{className:`whitespace-pre-wrap`,children:n})}):null]})}function K({chat:e,avatar:t,hello:n=S(`editor.chatHello`),placeholder:r=S(`editor.composer`),changedLabel:a=`Ablauf aktualisiert`,compact:o,intro:s,closed:c}){let[v,w]=(0,O.useState)(!1),[E,D]=(0,O.useState)(``),[k,A]=(0,O.useState)([]),j=(0,O.useRef)(null),M=(0,O.useRef)(null),F=(0,O.useRef)(null),I=N(E,D);(0,O.useEffect)(()=>{j.current?.scrollTo({top:j.current.scrollHeight,behavior:`smooth`})},[e.messages,e.phase]),(0,O.useEffect)(()=>{let e=M.current;e&&(e.style.height=`auto`,e.style.height=`${Math.min(e.scrollHeight,200)}px`)},[E]);let L=(E.trim()||k.length>0)&&e.phase===`idle`,B=e=>A(t=>[...t.filter(t=>!e.some(e=>e.name===t.name)),...e]),[V,H]=(0,O.useState)(new Map);(0,O.useEffect)(()=>{let e=new Map(k.filter(e=>e.type.startsWith(`image/`)).map(e=>[e,URL.createObjectURL(e)]));return H(e),()=>{for(let t of e.values())URL.revokeObjectURL(t)}},[k]);let U=async()=>{if(!L)return;I.listening&&I.toggle();let t={text:E,files:k};D(``),A([]),await e.send(t.text.trim(),t.files)||(D(t.text),A(t.files))},K=!o||v,q=e.messages.at(-1);return(0,P.jsxs)(`div`,{className:`flex h-full min-h-0 flex-col`,children:[o&&(q||e.error)?(0,P.jsxs)(`button`,{type:`button`,"aria-expanded":v,"aria-label":S(`editor.thread`),onClick:()=>w(e=>!e),className:`flex items-center gap-2 px-4 pt-2.5 pb-0.5 text-left text-[13px] text-ink-3 transition hover:text-ink`,children:[!(e.error||v)&&q?.pending?(0,P.jsx)(W,{chat:e,className:`flex-1`}):(0,P.jsx)(`span`,{className:b(`min-w-0 flex-1 truncate`,e.error&&`text-rose`),children:e.error??(v?S(`editor.thread`):q?.content)}),v?(0,P.jsx)(h,{className:`size-4 shrink-0`}):(0,P.jsx)(T,{className:`size-4 shrink-0`})]}):null,(0,P.jsxs)(`div`,{ref:j,className:b(`min-h-0 flex-1 overflow-y-auto px-5 py-5`,!K&&`hidden`),children:[e.messages.length===0&&!c?s??(0,P.jsxs)(`div`,{className:`flex items-start gap-3`,children:[(0,P.jsx)(i,{kind:t,size:32,interactive:!1}),(0,P.jsx)(`p`,{className:`pt-1 text-[14px] text-ink-2 leading-relaxed`,children:n})]}):null,(0,P.jsx)(`div`,{className:`flex flex-col gap-4`,children:e.messages.map(n=>n.role===`user`?(0,P.jsx)(G,{message:n,fileUrl:e.fileUrl},n.id):(0,P.jsxs)(`div`,{className:`flex items-start gap-3`,children:[(0,P.jsx)(`div`,{className:`shrink-0`,children:(0,P.jsx)(i,{kind:t,size:28,interactive:!1})}),(0,P.jsxs)(`div`,{className:`min-w-0 pt-0.5 text-[14px]`,children:[n.source===`mcp`?(0,P.jsxs)(`div`,{className:`mb-1 inline-flex items-center gap-1.5 text-[12px] text-ink-3`,children:[(0,P.jsx)(l,{className:`size-3`}),` `,S(`editor.viaClient`,{client:n.client??`MCP`})]}):null,n.content?(0,P.jsx)(p,{text:n.content,className:`text-[14px]`}):null,n.pending?(0,P.jsx)(W,{chat:e,thought:!0,className:`mt-1 text-[13px] text-ink-3`}):null,n.changed?(0,P.jsxs)(`div`,{className:`mt-2 inline-flex items-center gap-1.5 rounded-full bg-ember-tint px-2.5 py-0.5 text-[12px] text-ember-strong`,children:[(0,P.jsx)(m,{className:`size-3`}),` `,a]}):null]})]},n.id))}),e.error?(0,P.jsx)(`div`,{className:`mt-4 rounded-lg bg-rose-tint px-3 py-2 text-[13px] text-rose`,children:e.error}):null]}),c?(0,P.jsx)(`p`,{className:`m-3 rounded-xl bg-paper-2 px-4 py-3 text-[13px] text-ink-2 leading-relaxed`,children:c}):(0,P.jsxs)(`div`,{className:o?`p-2`:`p-3`,children:[(0,P.jsxs)(`div`,{className:`rounded-xl bg-card p-1.5 shadow-soft ring-1 ring-border focus-within:ring-focus`,children:[V.size?(0,P.jsx)(`div`,{className:`flex flex-wrap gap-2 px-1 pt-1 pb-1.5`,children:k.map((e,t)=>{let n=V.get(e);return n?(0,P.jsx)(z,{url:n,label:e.name,children:(0,P.jsx)(`button`,{type:`button`,"aria-label":S(`editor.detach`),onClick:()=>A(e=>e.filter((e,n)=>n!==t)),className:`-top-1.5 -right-1.5 absolute flex size-5 items-center justify-center rounded-full bg-ink text-paper opacity-0 shadow-soft transition focus-visible:opacity-100 group-hover:opacity-100`,children:(0,P.jsx)(C,{className:`size-3`})})},`${e.name}-${t}`):null})}):null,k.some(e=>!V.has(e))?(0,P.jsx)(`ul`,{className:`flex flex-wrap gap-1.5 px-1 pt-1 pb-1.5`,children:k.map((e,t)=>V.has(e)?null:(0,P.jsxs)(`li`,{className:`inline-flex max-w-full items-center gap-1.5 rounded-full bg-paper-2 py-1 pr-1 pl-2.5 text-[12px] text-ink-2`,children:[(0,P.jsx)(u,{className:`size-3 shrink-0 text-ink-4`}),(0,P.jsx)(`span`,{className:`truncate`,children:e.name}),(0,P.jsx)(`button`,{type:`button`,"aria-label":S(`editor.detach`),onClick:()=>A(e=>e.filter((e,n)=>n!==t)),className:`inline-flex size-5 shrink-0 items-center justify-center rounded-full text-ink-3 hover:bg-paper-3 hover:text-ink`,children:(0,P.jsx)(C,{className:`size-3`})})]},`${e.name}-${t}`))}):null,(0,P.jsxs)(`div`,{className:`flex items-end gap-1`,children:[(0,P.jsx)(x,{label:S(`editor.attach`),onClick:()=>F.current?.click(),children:(0,P.jsx)(g,{className:`size-4`})}),(0,P.jsx)(`input`,{ref:F,type:`file`,multiple:!0,hidden:!0,onChange:e=>{let t=Array.from(e.target.files??[]);e.target.value=``,B(t)}}),(0,P.jsx)(`textarea`,{ref:M,rows:1,value:E,onChange:e=>D(e.target.value),onPaste:e=>{let t=Array.from(e.clipboardData.files);t.length&&(e.preventDefault(),B(t.map(R)))},onKeyDown:e=>{e.key===`Enter`&&!e.shiftKey&&(e.preventDefault(),U())},placeholder:I.listening?S(`editor.listening`):r,className:`max-h-[200px] min-h-[40px] flex-1 resize-none bg-transparent px-1.5 py-2 text-[14px] outline-none placeholder:text-ink-4`}),I.supported?(0,P.jsx)(x,{label:I.processing?S(`editor.transcribing`):I.listening?S(`editor.voiceStop`):S(`editor.voice`),"aria-pressed":I.listening,disabled:I.processing,onClick:I.toggle,className:b(I.listening&&`bg-rose-tint text-rose hover:bg-rose-tint hover:text-rose`),children:I.processing?(0,P.jsx)(y,{className:`size-4`}):I.listening?(0,P.jsx)(f,{className:`size-3.5 animate-pulse-dot fill-current`}):(0,P.jsx)(_,{className:`size-4`})}):null,(0,P.jsx)(`button`,{type:`button`,onClick:()=>void U(),disabled:!L,className:b(`inline-flex size-9 shrink-0 items-center justify-center rounded-full transition`,L?`bg-primary text-primary-foreground`:`bg-paper-2 text-ink-4`),"aria-label":`Send`,children:(0,P.jsx)(d,{className:`size-4`})})]})]}),I.error?(0,P.jsx)(`p`,{className:`mt-2 px-1 text-[12px] text-rose`,children:I.error===`denied`?S(`editor.micDenied`):I.error}):null]})]})}export{D as a,V as i,W as n,I as r,K as t};
@@ -1 +1 @@
1
- import{a as e,n as t,t as n}from"./jsx-runtime-D3jfb0Ew.js";import{t as r}from"./preload-helper-CNEI7N0N.js";import{t as i}from"./external-link-MAL7vJYj.js";import{_ as a,a as o,d as s,i as c,s as l,y as u}from"./ui-iTYHqwvY.js";var d=e(t(),1),f=[{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/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`}],p=[{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`}],m=e(n(),1),h=(e,...t)=>t.some(t=>t?.toLowerCase().includes(e));function g({name:e,homepage:t,version:n,license:r,description:a}){return(0,m.jsxs)(`li`,{className:`py-2.5`,children:[(0,m.jsxs)(`div`,{className:`flex flex-wrap items-baseline gap-x-2.5 gap-y-0.5`,children:[t?(0,m.jsxs)(`a`,{href:t,target:`_blank`,rel:`noreferrer`,className:`inline-flex items-center gap-1 font-medium text-[14px] text-ink underline-offset-4 hover:underline`,children:[e,(0,m.jsx)(i,{"aria-hidden":!0,className:`size-3 text-ink-4`})]}):(0,m.jsx)(`span`,{className:`font-medium text-[14px] text-ink`,children:e}),n?(0,m.jsx)(`span`,{className:`text-[12px] text-ink-3 tabular-nums`,children:n}):null,(0,m.jsx)(`span`,{className:`text-[12px] text-ink-3`,children:r})]}),a?(0,m.jsx)(`p`,{className:`mt-0.5 text-[13px] text-ink-3`,children:a}):null]})}function _({title:e,hint:t,children:n}){return(0,m.jsxs)(`section`,{className:`mt-6`,children:[(0,m.jsx)(`h3`,{className:`border-border-soft border-b pb-2 font-display font-semibold text-[16px]`,children:e}),t?(0,m.jsx)(`p`,{className:`mt-2 text-[13px] text-ink-3`,children:t}):null,n]})}function v({credits:e}){return(0,m.jsx)(`ul`,{className:`divide-y divide-border-soft`,children:e.map(e=>(0,m.jsx)(g,{name:e.name,homepage:e.homepage,license:e.license,description:e.description[a]},e.name))})}function y({label:e,packages:t}){return(0,m.jsxs)(`div`,{className:`mt-4`,children:[(0,m.jsx)(`h4`,{className:`font-medium text-[12px] text-ink-3 tracking-wide`,children:e}),(0,m.jsx)(`ul`,{className:`divide-y divide-border-soft`,children:t.map(e=>(0,m.jsx)(g,{...e},e.name))})]})}function b({open:e,onClose:t}){let[n,i]=(0,d.useState)(null),[a,g]=(0,d.useState)(``);(0,d.useEffect)(()=>{if(!e||n)return;let t=!1;return r(()=>import(`./oss-credits-Hu4LieLA.js`).then(e=>{t||i(e.default)}),[]),()=>{t=!0}},[e,n]),(0,d.useEffect)(()=>{e||g(``)},[e]);let b=a.trim().toLowerCase(),x=e=>b?e.filter(e=>h(b,e.name,e.license,e.description.de,e.description.en)):e,S=e=>b?e.filter(e=>h(b,e.name,e.license,e.version)):e,C=x(f),w=x(p),T=n?[{id:`shared`,label:u(`about.creditsShared`),packages:S(n.shared)},...n.groups.map(e=>({id:e.id,label:e.label,packages:S(e.packages)}))].filter(e=>e.packages.length>0):[],E=C.length===0&&w.length===0&&T.length===0;return(0,m.jsxs)(c,{open:e,onClose:t,title:u(`about.credits`),wide:!0,children:[(0,m.jsx)(`p`,{className:`-mt-3 mb-4 text-[14px] text-ink-3`,children:u(`about.creditsHint`)}),(0,m.jsx)(l,{type:`search`,value:a,placeholder:u(`about.search`),onChange:e=>g(e.target.value)}),C.length>0?(0,m.jsx)(_,{title:u(`about.creditsProduct`),children:(0,m.jsx)(v,{credits:C})}):null,w.length>0?(0,m.jsx)(_,{title:u(`about.creditsBuiltWith`),children:(0,m.jsx)(v,{credits:w})}):null,n===null?(0,m.jsx)(`div`,{className:`flex justify-center py-10 text-ink-3`,children:(0,m.jsx)(s,{})}):T.length>0?(0,m.jsx)(_,{title:u(`about.creditsDeps`),hint:b?void 0:u(`about.creditsDepsHint`),children:T.map(e=>(0,m.jsx)(y,{label:e.label,packages:e.packages},e.id))}):null,n!==null&&E?(0,m.jsx)(o,{children:u(`about.noMatches`)}):null]})}export{b as CreditsDialog};
1
+ import{a as e,n as t,t as n}from"./jsx-runtime-D3jfb0Ew.js";import{t as r}from"./preload-helper-CNEI7N0N.js";import{t as i}from"./external-link-MAL7vJYj.js";import{_ as a,a as o,d as s,i as c,s as l,y as u}from"./ui-C3bTWmdM.js";var d=e(t(),1),f=[{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/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`}],p=[{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`}],m=e(n(),1),h=(e,...t)=>t.some(t=>t?.toLowerCase().includes(e));function g({name:e,homepage:t,version:n,license:r,description:a}){return(0,m.jsxs)(`li`,{className:`py-2.5`,children:[(0,m.jsxs)(`div`,{className:`flex flex-wrap items-baseline gap-x-2.5 gap-y-0.5`,children:[t?(0,m.jsxs)(`a`,{href:t,target:`_blank`,rel:`noreferrer`,className:`inline-flex items-center gap-1 font-medium text-[14px] text-ink underline-offset-4 hover:underline`,children:[e,(0,m.jsx)(i,{"aria-hidden":!0,className:`size-3 text-ink-4`})]}):(0,m.jsx)(`span`,{className:`font-medium text-[14px] text-ink`,children:e}),n?(0,m.jsx)(`span`,{className:`text-[12px] text-ink-3 tabular-nums`,children:n}):null,(0,m.jsx)(`span`,{className:`text-[12px] text-ink-3`,children:r})]}),a?(0,m.jsx)(`p`,{className:`mt-0.5 text-[13px] text-ink-3`,children:a}):null]})}function _({title:e,hint:t,children:n}){return(0,m.jsxs)(`section`,{className:`mt-6`,children:[(0,m.jsx)(`h3`,{className:`border-border-soft border-b pb-2 font-display font-semibold text-[16px]`,children:e}),t?(0,m.jsx)(`p`,{className:`mt-2 text-[13px] text-ink-3`,children:t}):null,n]})}function v({credits:e}){return(0,m.jsx)(`ul`,{className:`divide-y divide-border-soft`,children:e.map(e=>(0,m.jsx)(g,{name:e.name,homepage:e.homepage,license:e.license,description:e.description[a]},e.name))})}function y({label:e,packages:t}){return(0,m.jsxs)(`div`,{className:`mt-4`,children:[(0,m.jsx)(`h4`,{className:`font-medium text-[12px] text-ink-3 tracking-wide`,children:e}),(0,m.jsx)(`ul`,{className:`divide-y divide-border-soft`,children:t.map(e=>(0,m.jsx)(g,{...e},e.name))})]})}function b({open:e,onClose:t}){let[n,i]=(0,d.useState)(null),[a,g]=(0,d.useState)(``);(0,d.useEffect)(()=>{if(!e||n)return;let t=!1;return r(()=>import(`./oss-credits-Hu4LieLA.js`).then(e=>{t||i(e.default)}),[]),()=>{t=!0}},[e,n]),(0,d.useEffect)(()=>{e||g(``)},[e]);let b=a.trim().toLowerCase(),x=e=>b?e.filter(e=>h(b,e.name,e.license,e.description.de,e.description.en)):e,S=e=>b?e.filter(e=>h(b,e.name,e.license,e.version)):e,C=x(f),w=x(p),T=n?[{id:`shared`,label:u(`about.creditsShared`),packages:S(n.shared)},...n.groups.map(e=>({id:e.id,label:e.label,packages:S(e.packages)}))].filter(e=>e.packages.length>0):[],E=C.length===0&&w.length===0&&T.length===0;return(0,m.jsxs)(c,{open:e,onClose:t,title:u(`about.credits`),wide:!0,children:[(0,m.jsx)(`p`,{className:`-mt-3 mb-4 text-[14px] text-ink-3`,children:u(`about.creditsHint`)}),(0,m.jsx)(l,{type:`search`,value:a,placeholder:u(`about.search`),onChange:e=>g(e.target.value)}),C.length>0?(0,m.jsx)(_,{title:u(`about.creditsProduct`),children:(0,m.jsx)(v,{credits:C})}):null,w.length>0?(0,m.jsx)(_,{title:u(`about.creditsBuiltWith`),children:(0,m.jsx)(v,{credits:w})}):null,n===null?(0,m.jsx)(`div`,{className:`flex justify-center py-10 text-ink-3`,children:(0,m.jsx)(s,{})}):T.length>0?(0,m.jsx)(_,{title:u(`about.creditsDeps`),hint:b?void 0:u(`about.creditsDepsHint`),children:T.map(e=>(0,m.jsx)(y,{label:e.label,packages:e.packages},e.id))}):null,n!==null&&E?(0,m.jsx)(o,{children:u(`about.noMatches`)}):null]})}export{b as CreditsDialog};