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.
- package/apps/runtime/dist/db/client.js +24 -3
- package/apps/runtime/dist/index.js +6 -0
- package/apps/runtime/dist/routes/api.js +1 -1
- package/apps/runtime/dist/services/cloud.js +60 -3
- package/apps/runtime/dist/services/files.js +50 -1
- package/apps/runtime/dist/services/spaces.js +84 -84
- package/apps/runtime/dist/update.js +3 -3
- package/apps/web/dist/assets/{ChangelogDialog-CbZBUMRu.js → ChangelogDialog-lGa0JKY1.js} +1 -1
- package/apps/web/dist/assets/{ChatPanel-zurtfj6A.js → ChatPanel-Dk_45uca.js} +1 -1
- package/apps/web/dist/assets/{CreditsDialog-BewZHZTR.js → CreditsDialog-BPMWo6tp.js} +1 -1
- package/apps/web/dist/assets/{EditorPage-B7ZOoW5h.js → EditorPage-Dt4MCcrU.js} +1 -1
- package/apps/web/dist/assets/{FlowPreview-DHrhPps0.js → FlowPreview-gwtUv8-X.js} +1 -1
- package/apps/web/dist/assets/{HomePage-CBvWxXsq.js → HomePage-Bi9FK3_1.js} +1 -1
- package/apps/web/dist/assets/{ImportWizard-CKjVJMaG.js → ImportWizard-D6y4bKlF.js} +1 -1
- package/apps/web/dist/assets/{LocalRuntime-Brkvbzzg.js → LocalRuntime-BjzPSsHr.js} +1 -1
- package/apps/web/dist/assets/{NewWizardPage-B3XePMlp.js → NewWizardPage-2HbCaPSt.js} +2 -2
- package/apps/web/dist/assets/{PublicRunner-Bn1g6HG9.js → PublicRunner-Dac_e6wg.js} +1 -1
- package/apps/web/dist/assets/{RunnerView-CO1Nk3jn.js → RunnerView-BC6crf1p.js} +1 -1
- package/apps/web/dist/assets/{SettingsPage-BIY4kHZl.js → SettingsPage-BfXxa4SU.js} +1 -1
- package/apps/web/dist/assets/{SetupPage-BvLwZH9i.js → SetupPage-CYKVspBJ.js} +1 -1
- package/apps/web/dist/assets/{SharePage-CDESJ11B.js → SharePage-DFIHsNS-.js} +1 -1
- package/apps/web/dist/assets/{api-9sHuNTkp.js → api-BTfKh3La.js} +1 -1
- package/apps/web/dist/assets/changelog-CLmll9By.js +1 -0
- package/apps/web/dist/assets/{index-VsRgvimv.js → index-DOpyzQiU.js} +3 -3
- package/apps/web/dist/assets/{meta-B4mZqEso.js → meta-BbVhbYf6.js} +1 -1
- package/apps/web/dist/assets/{outputs-boPx5ngv.js → outputs-n_yhxmMk.js} +1 -1
- package/apps/web/dist/assets/{session-CJGuo1Tv.js → session-D_Zggq_t.js} +1 -1
- package/apps/web/dist/assets/ui-C3bTWmdM.js +1 -0
- package/apps/web/dist/index.html +4 -4
- package/apps/web/dist/mcp/flow.html +1 -1
- package/package.json +1 -1
- package/apps/web/dist/assets/changelog-Cto0j5-V.js +0 -1
- 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
|
-
*
|
|
122
|
-
*
|
|
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(
|
|
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:
|
|
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 {
|
|
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
|
-
/**
|
|
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
|
|
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(
|
|
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,
|
|
238
|
+
const { draft } = parseDraft(input.definition, input.files.map((f) => f.path));
|
|
236
239
|
return asSync(async () => {
|
|
237
|
-
const
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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(
|
|
320
|
-
code: await codeOf(
|
|
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
|
-
|
|
8
|
-
const
|
|
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 <
|
|
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-
|
|
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-
|
|
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};
|