@rslsp1/fa-app-tools 5.1.0 → 7.0.1
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/dist/{chunk-3MUI4ZV7.mjs → chunk-B2YV4TNI.mjs} +914 -76
- package/dist/{hfEventTypes-Dkfrcd_t.d.mts → hfEventTypes-BUztJySs.d.mts} +1 -11
- package/dist/{hfEventTypes-C_PX6RFR.d.ts → hfEventTypes-fAdA-0A9.d.ts} +1 -11
- package/dist/index.d.mts +18 -49
- package/dist/index.d.ts +18 -49
- package/dist/index.js +1221 -1548
- package/dist/index.mjs +208 -671
- package/dist/szenen.d.mts +197 -4
- package/dist/szenen.d.ts +197 -4
- package/dist/szenen.js +697 -115
- package/dist/szenen.mjs +63 -4
- package/package.json +1 -1
- package/dist/chunk-NBUS7AWR.mjs +0 -418
- package/dist/chunk-UFXDXENC.mjs +0 -218
- package/dist/hfStateService-W7KO2FOH.mjs +0 -56
- package/dist/project-O4ORKXY5.mjs +0 -8
|
@@ -4,15 +4,307 @@ import {
|
|
|
4
4
|
preferredVersionId,
|
|
5
5
|
resolvePreferredHead
|
|
6
6
|
} from "./chunk-FUY2OCVC.mjs";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
|
|
8
|
+
// src/lib/hfEventTypes.ts
|
|
9
|
+
var HF_BASE = "https://huggingface.co";
|
|
10
|
+
var HF_REPO = "RolandSch/fa-app-state";
|
|
11
|
+
var CURRENT_EVENT_VERSION = { major: 1, minor: 0 };
|
|
12
|
+
var SPEICHERBARE_BILDTYPEN = ["image/png", "image/jpeg", "image/webp"];
|
|
13
|
+
function bildEndungFuer(mimeType) {
|
|
14
|
+
if (mimeType === "image/png") return "png";
|
|
15
|
+
if (mimeType === "image/webp") return "webp";
|
|
16
|
+
return "jpg";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// src/lib/hfEventLog.ts
|
|
20
|
+
function treeUrl(namespace, subdir = "") {
|
|
21
|
+
const parts = [namespace.replace(/\/$/, ""), subdir].filter(Boolean).join("/");
|
|
22
|
+
return `${HF_BASE}/api/datasets/${HF_REPO}/tree/main${parts ? "/" + parts : ""}`;
|
|
23
|
+
}
|
|
24
|
+
var DROSSEL_VERSUCHE = 4;
|
|
25
|
+
var DROSSEL_BASIS_MS = 400;
|
|
26
|
+
var schlafen = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
27
|
+
var HF_LESE_ZEITLIMIT_MS = 2e4;
|
|
28
|
+
async function holeMitZeitlimit(url, init) {
|
|
29
|
+
const abbruch = new AbortController();
|
|
30
|
+
const timer = setTimeout(() => abbruch.abort(), HF_LESE_ZEITLIMIT_MS);
|
|
31
|
+
try {
|
|
32
|
+
return await fetch(url, { ...init, signal: abbruch.signal });
|
|
33
|
+
} catch (error) {
|
|
34
|
+
if (abbruch.signal.aborted) {
|
|
35
|
+
throw new Error(`HF-Anfrage ohne Antwort nach ${HF_LESE_ZEITLIMIT_MS}ms abgebrochen: ${url}`);
|
|
36
|
+
}
|
|
37
|
+
throw error;
|
|
38
|
+
} finally {
|
|
39
|
+
clearTimeout(timer);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function mitGeduld(anfrage) {
|
|
43
|
+
let letzte = null;
|
|
44
|
+
for (let versuch = 0; versuch < DROSSEL_VERSUCHE; versuch += 1) {
|
|
45
|
+
const res = await anfrage();
|
|
46
|
+
if (res.status !== 429 && res.status < 500) return res;
|
|
47
|
+
letzte = res;
|
|
48
|
+
if (versuch === DROSSEL_VERSUCHE - 1) break;
|
|
49
|
+
const angesagt = Number(res.headers.get("retry-after"));
|
|
50
|
+
const warten2 = Number.isFinite(angesagt) && angesagt > 0 ? angesagt * 1e3 : DROSSEL_BASIS_MS * 2 ** versuch;
|
|
51
|
+
await schlafen(warten2);
|
|
52
|
+
}
|
|
53
|
+
return letzte;
|
|
54
|
+
}
|
|
55
|
+
async function hfListDir(namespace, subdir, token) {
|
|
56
|
+
const res = await mitGeduld(() => holeMitZeitlimit(treeUrl(namespace, subdir), {
|
|
57
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
58
|
+
}));
|
|
59
|
+
if (res.status === 404) return [];
|
|
60
|
+
if (!res.ok) throw new Error(`HF list failed: ${res.status} ${res.statusText}`);
|
|
61
|
+
return res.json();
|
|
62
|
+
}
|
|
63
|
+
async function hfDownloadJsonByPath(repoPath, token) {
|
|
64
|
+
const res = await mitGeduld(() => holeMitZeitlimit(
|
|
65
|
+
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/${repoPath}?download=true`,
|
|
66
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
67
|
+
));
|
|
68
|
+
if (!res.ok) throw new Error(`HF download failed: ${res.status}`);
|
|
69
|
+
return res.json();
|
|
70
|
+
}
|
|
71
|
+
async function hfDownloadBinaryByPath(repoPath, token) {
|
|
72
|
+
const res = await mitGeduld(() => holeMitZeitlimit(
|
|
73
|
+
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/${repoPath}?download=true`,
|
|
74
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
75
|
+
));
|
|
76
|
+
if (!res.ok) throw new Error(`HF download binary failed: ${res.status}`);
|
|
77
|
+
return new Uint8Array(await res.arrayBuffer());
|
|
78
|
+
}
|
|
79
|
+
function toBase64(text) {
|
|
80
|
+
const bytes = new TextEncoder().encode(text);
|
|
81
|
+
let binary = "";
|
|
82
|
+
bytes.forEach((b) => binary += String.fromCharCode(b));
|
|
83
|
+
return btoa(binary);
|
|
84
|
+
}
|
|
85
|
+
async function hfUploadSmallFile(repoPath, content, token, summary = `Update ${repoPath}`) {
|
|
86
|
+
const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
|
|
87
|
+
method: "POST",
|
|
88
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
|
|
89
|
+
body: [
|
|
90
|
+
JSON.stringify({ key: "header", value: { summary, description: "" } }),
|
|
91
|
+
JSON.stringify({ key: "file", value: { path: repoPath, encoding: "base64", content: toBase64(content) } })
|
|
92
|
+
].join("\n")
|
|
93
|
+
});
|
|
94
|
+
if (!res.ok) {
|
|
95
|
+
const err = await res.text().catch(() => "");
|
|
96
|
+
throw new Error(`HF upload failed: ${res.status} \u2014 ${err.slice(0, 200)}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function hfBatchArchive(moves, token, summary) {
|
|
100
|
+
const lines = [
|
|
101
|
+
JSON.stringify({ key: "header", value: { summary, description: "" } }),
|
|
102
|
+
...moves.flatMap(({ from, to, content }) => [
|
|
103
|
+
JSON.stringify({ key: "file", value: { path: to, encoding: "base64", content: toBase64(content) } }),
|
|
104
|
+
JSON.stringify({ key: "deletedFile", value: { path: from } })
|
|
105
|
+
])
|
|
106
|
+
];
|
|
107
|
+
const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
|
|
108
|
+
method: "POST",
|
|
109
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
|
|
110
|
+
body: lines.join("\n")
|
|
111
|
+
});
|
|
112
|
+
if (!res.ok) {
|
|
113
|
+
const err = await res.text().catch(() => "");
|
|
114
|
+
throw new Error(`HF batch archive failed: ${res.status} \u2014 ${err.slice(0, 200)}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function tsFromEventPath(repoPath) {
|
|
118
|
+
const filename = repoPath.split("/").pop() || "";
|
|
119
|
+
const base = filename.replace(/_[^_]+\.json$/, "");
|
|
120
|
+
const iso = base.replace(/T(\d{2})-(\d{2})-(\d{2})-(\d{3})Z$/, "T$1:$2:$3.$4Z");
|
|
121
|
+
const ts = Date.parse(iso);
|
|
122
|
+
return isNaN(ts) ? 0 : ts;
|
|
123
|
+
}
|
|
124
|
+
var EVENTDOWNLOAD_PARALLELITAET = 3;
|
|
125
|
+
async function mitBegrenzterParallelitaet(count, limit, task) {
|
|
126
|
+
let cursor = 0;
|
|
127
|
+
let ersterFehler;
|
|
128
|
+
let fehlerAufgetreten = false;
|
|
129
|
+
const worker = async () => {
|
|
130
|
+
while (cursor < count) {
|
|
131
|
+
const index = cursor++;
|
|
132
|
+
try {
|
|
133
|
+
await task(index);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
if (!fehlerAufgetreten) {
|
|
136
|
+
fehlerAufgetreten = true;
|
|
137
|
+
ersterFehler = error;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
await Promise.all(Array.from({ length: Math.min(limit, count) }, worker));
|
|
143
|
+
if (fehlerAufgetreten) throw ersterFehler;
|
|
144
|
+
}
|
|
145
|
+
async function loadPendingEvents(namespace, token, sinceTs, knownPaths) {
|
|
146
|
+
const files = await hfListDir(namespace, "events", token);
|
|
147
|
+
const pending = files.filter((f) => f.type === "file" && tsFromEventPath(f.path) > sinceTs && !knownPaths?.has(f.path)).sort((a, b) => a.path.localeCompare(b.path));
|
|
148
|
+
if (!pending.length) return [];
|
|
149
|
+
const raw = new Array(pending.length);
|
|
150
|
+
await mitBegrenzterParallelitaet(pending.length, EVENTDOWNLOAD_PARALLELITAET, async (index) => {
|
|
151
|
+
raw[index] = await hfDownloadJsonByPath(pending[index].path, token);
|
|
152
|
+
});
|
|
153
|
+
for (const f of pending) knownPaths?.add(f.path);
|
|
154
|
+
return raw.flatMap((e) => Array.isArray(e) ? e : [e]);
|
|
155
|
+
}
|
|
156
|
+
var SESSION_CLIENT_ID = `client-${crypto.randomUUID().slice(0, 8)}`;
|
|
157
|
+
function getSessionClientId() {
|
|
158
|
+
return SESSION_CLIENT_ID;
|
|
159
|
+
}
|
|
160
|
+
function tsToFilename(ts) {
|
|
161
|
+
return new Date(ts).toISOString().replace(/:/g, "-").replace(".", "-");
|
|
162
|
+
}
|
|
163
|
+
var writeSeq = 0;
|
|
164
|
+
var lastOrderedTs = 0;
|
|
165
|
+
async function writeHFEvent(namespace, token, type, payload, prevTs, options) {
|
|
166
|
+
const wallTs = Date.now();
|
|
167
|
+
const ts = options?.ordered ? Math.max(wallTs, lastOrderedTs) : wallTs;
|
|
168
|
+
if (options?.ordered) lastOrderedTs = ts;
|
|
169
|
+
const uuid = crypto.randomUUID().slice(0, 8);
|
|
170
|
+
const event = {
|
|
171
|
+
v: CURRENT_EVENT_VERSION,
|
|
172
|
+
type,
|
|
173
|
+
ts,
|
|
174
|
+
prevTs,
|
|
175
|
+
clientId: SESSION_CLIENT_ID,
|
|
176
|
+
payload
|
|
177
|
+
};
|
|
178
|
+
const suffix = options?.ordered ? `${String(++writeSeq % 1e6).padStart(6, "0")}${uuid}` : uuid;
|
|
179
|
+
const filename = `${tsToFilename(ts)}_${suffix}.json`;
|
|
180
|
+
const repoPath = `${namespace}events/${filename}`;
|
|
181
|
+
await hfUploadSmallFile(repoPath, JSON.stringify(event, null, 2), token, `Event: ${type}`);
|
|
182
|
+
return event;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// src/lib/hfStateService.ts
|
|
186
|
+
import JSZip from "jszip";
|
|
187
|
+
import { uploadFile } from "@huggingface/hub";
|
|
188
|
+
var HF_TOKEN_KEY = "hf-token";
|
|
189
|
+
function getHFToken() {
|
|
190
|
+
try {
|
|
191
|
+
return localStorage.getItem(HF_TOKEN_KEY);
|
|
192
|
+
} catch {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function setHFToken(token) {
|
|
197
|
+
try {
|
|
198
|
+
localStorage.setItem(HF_TOKEN_KEY, token);
|
|
199
|
+
} catch {
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
async function loadHFState(namespace, token) {
|
|
203
|
+
const files = await hfListDir(namespace, "", token);
|
|
204
|
+
const stateFiles = files.filter((f) => f.type === "file" && /^state-[\dT\-]+Z\.zip$/.test(f.path.split("/").pop() || "")).sort((a, b) => b.path.localeCompare(a.path));
|
|
205
|
+
if (!stateFiles.length) return null;
|
|
206
|
+
const zipBytes = await hfDownloadBinaryByPath(stateFiles[0].path, token);
|
|
207
|
+
const zip = await JSZip.loadAsync(zipBytes);
|
|
208
|
+
const [metadataStr, tagsStr, metaStr, openTasksStr] = await Promise.all([
|
|
209
|
+
zip.file("metadata.json")?.async("string"),
|
|
210
|
+
zip.file("tags.json")?.async("string"),
|
|
211
|
+
zip.file("state_meta.json")?.async("string"),
|
|
212
|
+
zip.file("open_tasks.json")?.async("string")
|
|
213
|
+
// optional — ältere Snapshots haben es nicht
|
|
214
|
+
]);
|
|
215
|
+
if (!metadataStr || !tagsStr || !metaStr) throw new Error("state.zip: fehlende Pflicht-Dateien");
|
|
216
|
+
return {
|
|
217
|
+
metadata: JSON.parse(metadataStr),
|
|
218
|
+
tags: JSON.parse(tagsStr),
|
|
219
|
+
meta: JSON.parse(metaStr),
|
|
220
|
+
openTasks: openTasksStr ? JSON.parse(openTasksStr) : {}
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
async function hfBootstrapFromLegacy(namespace, token, onProgress) {
|
|
224
|
+
const log = (msg) => {
|
|
225
|
+
if (onProgress) onProgress(msg);
|
|
226
|
+
};
|
|
227
|
+
log("Lese tags.json \u2026");
|
|
228
|
+
const tags = await fetch(
|
|
229
|
+
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/tags.json?download=true`,
|
|
230
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
231
|
+
).then((r) => r.ok ? r.json() : null).catch(() => null);
|
|
232
|
+
log("Lese metadata.json \u2026");
|
|
233
|
+
const metadata = await fetch(
|
|
234
|
+
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/metadata.json?download=true`,
|
|
235
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
236
|
+
).then((r) => r.ok ? r.json() : []).catch(() => []);
|
|
237
|
+
const snapshot = {
|
|
238
|
+
metadata: Array.isArray(metadata) ? metadata : [],
|
|
239
|
+
tags: tags?.by_category ? tags : { by_category: {}, all: [] },
|
|
240
|
+
meta: { consolidatedAt: Date.now(), version: 1 }
|
|
241
|
+
};
|
|
242
|
+
log(`Erstelle state.zip (${snapshot.metadata.length} Bilder, ${snapshot.tags.all?.length ?? 0} Tags) \u2026`);
|
|
243
|
+
const zip = new JSZip();
|
|
244
|
+
zip.file("metadata.json", JSON.stringify(snapshot.metadata, null, 2));
|
|
245
|
+
zip.file("tags.json", JSON.stringify(snapshot.tags, null, 2));
|
|
246
|
+
zip.file("state_meta.json", JSON.stringify(snapshot.meta, null, 2));
|
|
247
|
+
const zipBytes = await zip.generateAsync({ type: "uint8array", compression: "DEFLATE" });
|
|
248
|
+
const ts = snapshot.meta.consolidatedAt;
|
|
249
|
+
const stateFilename = `state-${new Date(ts).toISOString().replace(/:/g, "-").replace(".", "-")}.zip`;
|
|
250
|
+
const repoPath = `${namespace}${stateFilename}`;
|
|
251
|
+
log("Lade state.zip hoch \u2026");
|
|
252
|
+
await uploadFile({
|
|
253
|
+
repo: { type: "dataset", name: HF_REPO },
|
|
254
|
+
credentials: { accessToken: token },
|
|
255
|
+
file: { path: repoPath, content: new Blob([zipBytes.buffer], { type: "application/zip" }) },
|
|
256
|
+
branch: "main",
|
|
257
|
+
commitTitle: `Bootstrap: initialer State aus Legacy-Daten`
|
|
258
|
+
});
|
|
259
|
+
log(`Fertig \u2014 ${stateFilename}`);
|
|
260
|
+
}
|
|
261
|
+
async function hfUploadImage(base64, id, token, mimeType = "image/jpeg") {
|
|
262
|
+
const ext = bildEndungFuer(mimeType);
|
|
263
|
+
const binary = atob(base64);
|
|
264
|
+
const bytes = new Uint8Array(binary.length);
|
|
265
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
266
|
+
await uploadFile({
|
|
267
|
+
repo: { type: "dataset", name: HF_REPO },
|
|
268
|
+
credentials: { accessToken: token },
|
|
269
|
+
file: { path: `images/${id}.${ext}`, content: new Blob([bytes], { type: mimeType }) },
|
|
270
|
+
branch: "main",
|
|
271
|
+
commitTitle: `Add image ${id}`
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
async function hfLoadImageAsBase64(id, token, namespace, filename, on404, mimeType, hasThumb) {
|
|
275
|
+
const fetchBase64 = async (repoPath) => {
|
|
276
|
+
try {
|
|
277
|
+
const res = await fetch(
|
|
278
|
+
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/${repoPath}?download=true`,
|
|
279
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
280
|
+
);
|
|
281
|
+
if (!res.ok) {
|
|
282
|
+
if (on404 && on404()) return "ABORT";
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
const blob = await res.blob();
|
|
286
|
+
return new Promise((resolve, reject) => {
|
|
287
|
+
const reader = new FileReader();
|
|
288
|
+
reader.onload = () => resolve(reader.result.split(",")[1]);
|
|
289
|
+
reader.onerror = reject;
|
|
290
|
+
reader.readAsDataURL(blob);
|
|
291
|
+
});
|
|
292
|
+
} catch {
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
if (filename) {
|
|
297
|
+
if (hasThumb) {
|
|
298
|
+
const thumb = await fetchBase64(`thumbs/${filename}`);
|
|
299
|
+
if (thumb && thumb !== "ABORT") return thumb;
|
|
300
|
+
}
|
|
301
|
+
const result2 = await fetchBase64(`images/${filename}`);
|
|
302
|
+
return result2 === "ABORT" ? null : result2;
|
|
303
|
+
}
|
|
304
|
+
const ext = bildEndungFuer(mimeType);
|
|
305
|
+
const result = await fetchBase64(`images/${id}.${ext}`);
|
|
306
|
+
return result === "ABORT" ? null : result;
|
|
307
|
+
}
|
|
16
308
|
|
|
17
309
|
// src/lib/szenen/viewPort.ts
|
|
18
310
|
function capabilitiesOf(port) {
|
|
@@ -5225,6 +5517,133 @@ function findPromptRun(events, runId) {
|
|
|
5225
5517
|
return collectPromptRuns(events).find((run) => run.run_id === runId) ?? null;
|
|
5226
5518
|
}
|
|
5227
5519
|
|
|
5520
|
+
// src/lib/szenen/seedListe.ts
|
|
5521
|
+
var DEFAULT_SEED_VORLAGE = {
|
|
5522
|
+
name: "Default",
|
|
5523
|
+
seeds: "1; 10; 100; 1000",
|
|
5524
|
+
builtin: true
|
|
5525
|
+
};
|
|
5526
|
+
function parseSeedListe(eingabe) {
|
|
5527
|
+
if (!eingabe) return [];
|
|
5528
|
+
return eingabe.split(/[,;]/).map((teil) => teil.trim()).filter((teil) => teil.length > 0);
|
|
5529
|
+
}
|
|
5530
|
+
function formatSeedListe(tokens) {
|
|
5531
|
+
return tokens.join("; ");
|
|
5532
|
+
}
|
|
5533
|
+
var GANZZAHL = /^-?\d+$/;
|
|
5534
|
+
function ordneSeedsZu(eingabe, anzahl) {
|
|
5535
|
+
const tokens = Array.isArray(eingabe) ? [...eingabe] : parseSeedListe(eingabe);
|
|
5536
|
+
const eintraege = [];
|
|
5537
|
+
for (let position = 0; position < anzahl; position++) {
|
|
5538
|
+
const roh = tokens[position] ?? null;
|
|
5539
|
+
if (roh === null) {
|
|
5540
|
+
eintraege.push({ position, roh: null, seed: null, ungueltig: false });
|
|
5541
|
+
continue;
|
|
5542
|
+
}
|
|
5543
|
+
const zahl = GANZZAHL.test(roh) ? Number(roh) : Number.NaN;
|
|
5544
|
+
const gueltig = Number.isSafeInteger(zahl);
|
|
5545
|
+
eintraege.push({
|
|
5546
|
+
position,
|
|
5547
|
+
roh,
|
|
5548
|
+
seed: gueltig ? zahl : null,
|
|
5549
|
+
ungueltig: !gueltig
|
|
5550
|
+
});
|
|
5551
|
+
}
|
|
5552
|
+
return {
|
|
5553
|
+
eintraege,
|
|
5554
|
+
ungueltige: eintraege.filter((e) => e.ungueltig),
|
|
5555
|
+
ueberzaehlig: Math.max(0, tokens.length - anzahl)
|
|
5556
|
+
};
|
|
5557
|
+
}
|
|
5558
|
+
function seedFehlerText(ungueltige) {
|
|
5559
|
+
const teile = ungueltige.map((e) => `Bild ${e.position + 1}: "${e.roh}"`);
|
|
5560
|
+
return `Die Seedliste enth\xE4lt keine ganze Zahl an ${teile.length === 1 ? "Position" : "Positionen"} \u2014 ${teile.join(", ")}. Kein Bild wurde importiert.`;
|
|
5561
|
+
}
|
|
5562
|
+
function mitDefaultVorlage(custom) {
|
|
5563
|
+
return [DEFAULT_SEED_VORLAGE, ...custom.filter((v) => !v.builtin)];
|
|
5564
|
+
}
|
|
5565
|
+
|
|
5566
|
+
// src/lib/szenen/zipBilder.ts
|
|
5567
|
+
var BILD_ENDUNGEN = {
|
|
5568
|
+
png: "image/png",
|
|
5569
|
+
jpg: "image/jpeg",
|
|
5570
|
+
jpeg: "image/jpeg",
|
|
5571
|
+
webp: "image/webp"
|
|
5572
|
+
};
|
|
5573
|
+
var UPLOAD_MAX_BYTES = 45 * 1024 * 1024;
|
|
5574
|
+
function byteLaenge(base64) {
|
|
5575
|
+
const fuellzeichen = base64.endsWith("==") ? 2 : base64.endsWith("=") ? 1 : 0;
|
|
5576
|
+
return Math.floor(base64.length * 3 / 4) - fuellzeichen;
|
|
5577
|
+
}
|
|
5578
|
+
function angekuendigteGroesse(eintrag) {
|
|
5579
|
+
const roh = eintrag._data?.uncompressedSize;
|
|
5580
|
+
return typeof roh === "number" && Number.isFinite(roh) && roh >= 0 ? roh : null;
|
|
5581
|
+
}
|
|
5582
|
+
function bildGroesseVerdikt(archiv, pfad, bytes) {
|
|
5583
|
+
if (bytes === null || bytes <= UPLOAD_MAX_BYTES) return null;
|
|
5584
|
+
return `${archiv}: Das Bild ${pfad} ist entpackt ${mb(bytes)} MB gro\xDF \u2014 die Grenze f\xFCr eine einzelne Bilddatei liegt bei ${UPLOAD_MAX_BYTES / 1024 / 1024} MB. Kein Bild wurde importiert.`;
|
|
5585
|
+
}
|
|
5586
|
+
function mb(bytes) {
|
|
5587
|
+
return (bytes / 1024 / 1024).toFixed(1);
|
|
5588
|
+
}
|
|
5589
|
+
function istZipDatei(datei) {
|
|
5590
|
+
return /\.zip$/i.test(datei.name);
|
|
5591
|
+
}
|
|
5592
|
+
function endungVon(pfad) {
|
|
5593
|
+
const punkt = pfad.lastIndexOf(".");
|
|
5594
|
+
return punkt >= 0 ? pfad.slice(punkt + 1).toLowerCase() : "";
|
|
5595
|
+
}
|
|
5596
|
+
function istImportierbar(pfad, istVerzeichnis) {
|
|
5597
|
+
if (istVerzeichnis) return false;
|
|
5598
|
+
if (pfad.startsWith("__MACOSX/")) return false;
|
|
5599
|
+
const datei = pfad.slice(pfad.lastIndexOf("/") + 1);
|
|
5600
|
+
if (datei.startsWith(".")) return false;
|
|
5601
|
+
return endungVon(datei) in BILD_ENDUNGEN;
|
|
5602
|
+
}
|
|
5603
|
+
function nachPfad(a, b) {
|
|
5604
|
+
return a.localeCompare(b, "en", { numeric: true, sensitivity: "base" }) || (a < b ? -1 : a > b ? 1 : 0);
|
|
5605
|
+
}
|
|
5606
|
+
async function leseBilderAusZip(datei) {
|
|
5607
|
+
const name = datei.name ?? "Archiv";
|
|
5608
|
+
if (datei.size > UPLOAD_MAX_BYTES) {
|
|
5609
|
+
throw new Error(
|
|
5610
|
+
`${name} ist ${mb(datei.size)} MB gro\xDF \u2014 die Grenze liegt bei ${UPLOAD_MAX_BYTES / 1024 / 1024} MB.`
|
|
5611
|
+
);
|
|
5612
|
+
}
|
|
5613
|
+
const { default: JSZip2 } = await import("jszip");
|
|
5614
|
+
const archiv = await JSZip2.loadAsync(datei).catch((fehler4) => {
|
|
5615
|
+
throw new Error(
|
|
5616
|
+
`${name} konnte nicht als ZIP gelesen werden: ${fehler4 instanceof Error ? fehler4.message : String(fehler4)}`
|
|
5617
|
+
);
|
|
5618
|
+
});
|
|
5619
|
+
const pfade = [];
|
|
5620
|
+
archiv.forEach((pfad, eintrag) => {
|
|
5621
|
+
if (istImportierbar(pfad, eintrag.dir)) pfade.push(pfad);
|
|
5622
|
+
});
|
|
5623
|
+
pfade.sort(nachPfad);
|
|
5624
|
+
if (pfade.length === 0) {
|
|
5625
|
+
throw new Error(
|
|
5626
|
+
`${name} enth\xE4lt kein unterst\xFCtztes Bild (erwartet: ${Object.keys(BILD_ENDUNGEN).map((e) => `.${e}`).join(", ")}).`
|
|
5627
|
+
);
|
|
5628
|
+
}
|
|
5629
|
+
const bilder = [];
|
|
5630
|
+
for (const pfad of pfade) {
|
|
5631
|
+
const eintrag = archiv.file(pfad);
|
|
5632
|
+
if (!eintrag) continue;
|
|
5633
|
+
const angekuendigt = bildGroesseVerdikt(name, pfad, angekuendigteGroesse(eintrag));
|
|
5634
|
+
if (angekuendigt) throw new Error(angekuendigt);
|
|
5635
|
+
const base64 = await eintrag.async("base64");
|
|
5636
|
+
const nachgemessen = bildGroesseVerdikt(name, pfad, byteLaenge(base64));
|
|
5637
|
+
if (nachgemessen) throw new Error(nachgemessen);
|
|
5638
|
+
bilder.push({
|
|
5639
|
+
name: pfad,
|
|
5640
|
+
base64,
|
|
5641
|
+
mimeType: BILD_ENDUNGEN[endungVon(pfad)]
|
|
5642
|
+
});
|
|
5643
|
+
}
|
|
5644
|
+
return bilder;
|
|
5645
|
+
}
|
|
5646
|
+
|
|
5228
5647
|
// src/lib/szenen/bildVorschau.ts
|
|
5229
5648
|
async function ladeVorschauen(port, mediaRefs, fertig, optionen = {}) {
|
|
5230
5649
|
const laden = port.loadPreview;
|
|
@@ -6242,7 +6661,7 @@ function usePortQuery(frage, abhaengigkeiten) {
|
|
|
6242
6661
|
}
|
|
6243
6662
|
|
|
6244
6663
|
// src/features/szenen/areas/ElementDetail.tsx
|
|
6245
|
-
import { useEffect as
|
|
6664
|
+
import { useEffect as useEffect6, useMemo as useMemo2, useState as useState10 } from "react";
|
|
6246
6665
|
|
|
6247
6666
|
// src/features/szenen/elementLabels.ts
|
|
6248
6667
|
var LABELS = {
|
|
@@ -8334,10 +8753,10 @@ var VersionNavigator = ({
|
|
|
8334
8753
|
};
|
|
8335
8754
|
|
|
8336
8755
|
// src/features/szenen/areas/RunsPanel.tsx
|
|
8337
|
-
import { useState as useState9 } from "react";
|
|
8756
|
+
import { useEffect as useEffect5, useRef as useRef3, useState as useState9 } from "react";
|
|
8338
8757
|
|
|
8339
8758
|
// src/features/szenen/areas/RunImagesPanel.tsx
|
|
8340
|
-
import { useEffect as useEffect4, useRef as useRef2, useState as useState8 } from "react";
|
|
8759
|
+
import React7, { useEffect as useEffect4, useRef as useRef2, useState as useState8 } from "react";
|
|
8341
8760
|
|
|
8342
8761
|
// src/features/szenen/areas/Bildbausteine.tsx
|
|
8343
8762
|
import { useCallback, useEffect as useEffect3, useState as useState7 } from "react";
|
|
@@ -8777,6 +9196,7 @@ function useBildQuellen(port, bilder) {
|
|
|
8777
9196
|
const [stand, setStand] = useState8(() => ({ quellen: /* @__PURE__ */ new Map(), fehler: /* @__PURE__ */ new Map() }));
|
|
8778
9197
|
const gehalten = useRef2(/* @__PURE__ */ new Map());
|
|
8779
9198
|
const gescheitert = useRef2(/* @__PURE__ */ new Map());
|
|
9199
|
+
const [versuch, setVersuch] = useState8(0);
|
|
8780
9200
|
const schluessel = bilder.map((b) => `${b.media_ref}|${b.mime_type ?? ""}`).join(" ");
|
|
8781
9201
|
useEffect4(() => {
|
|
8782
9202
|
const bestand = port.imageLibrary;
|
|
@@ -8785,7 +9205,9 @@ function useBildQuellen(port, bilder) {
|
|
|
8785
9205
|
void (async () => {
|
|
8786
9206
|
for (const bild of bilder) {
|
|
8787
9207
|
if (abgebrochen) return;
|
|
8788
|
-
|
|
9208
|
+
const vorher = gescheitert.current.get(bild.media_ref);
|
|
9209
|
+
const endgueltig = vorher !== void 0 && !vorher.retryable;
|
|
9210
|
+
if (gehalten.current.has(bild.media_ref) || endgueltig) continue;
|
|
8789
9211
|
const ergebnis = await bestand.resolveImage(bild.media_ref, bild.mime_type ?? null);
|
|
8790
9212
|
if (abgebrochen) return;
|
|
8791
9213
|
if (ergebnis.kind === "ok" && ergebnis.value) {
|
|
@@ -8793,8 +9215,12 @@ function useBildQuellen(port, bilder) {
|
|
|
8793
9215
|
bild.media_ref,
|
|
8794
9216
|
`data:${ergebnis.value.mime_type};base64,${ergebnis.value.data}`
|
|
8795
9217
|
);
|
|
9218
|
+
gescheitert.current.delete(bild.media_ref);
|
|
8796
9219
|
} else if (ergebnis.kind === "error") {
|
|
8797
|
-
gescheitert.current.set(bild.media_ref,
|
|
9220
|
+
gescheitert.current.set(bild.media_ref, {
|
|
9221
|
+
message: ergebnis.message,
|
|
9222
|
+
retryable: ergebnis.retryable
|
|
9223
|
+
});
|
|
8798
9224
|
}
|
|
8799
9225
|
setStand({
|
|
8800
9226
|
quellen: new Map(gehalten.current),
|
|
@@ -8805,8 +9231,19 @@ function useBildQuellen(port, bilder) {
|
|
|
8805
9231
|
return () => {
|
|
8806
9232
|
abgebrochen = true;
|
|
8807
9233
|
};
|
|
8808
|
-
}, [port, schluessel]);
|
|
8809
|
-
|
|
9234
|
+
}, [port, schluessel, versuch]);
|
|
9235
|
+
const fehlerNachricht = /* @__PURE__ */ new Map();
|
|
9236
|
+
let wiederholbar = false;
|
|
9237
|
+
for (const [ref, f] of stand.fehler) {
|
|
9238
|
+
fehlerNachricht.set(ref, f.message);
|
|
9239
|
+
if (f.retryable) wiederholbar = true;
|
|
9240
|
+
}
|
|
9241
|
+
return {
|
|
9242
|
+
quellen: stand.quellen,
|
|
9243
|
+
fehler: fehlerNachricht,
|
|
9244
|
+
wiederholbar,
|
|
9245
|
+
erneutVersuchen: () => setVersuch((n) => n + 1)
|
|
9246
|
+
};
|
|
8810
9247
|
}
|
|
8811
9248
|
var BildDetails = ({ bild, fehler: fehler4, aufloesbar }) => /* @__PURE__ */ jsxs8("span", { className: "szenen-bilder__meta", "data-testid": `batchbild-${bild.media_ref}`, children: [
|
|
8812
9249
|
/* @__PURE__ */ jsx8("code", { children: bild.media_ref }),
|
|
@@ -8827,8 +9264,17 @@ var BildDetails = ({ bild, fehler: fehler4, aufloesbar }) => /* @__PURE__ */ jsx
|
|
|
8827
9264
|
// Serverroute (A6).
|
|
8828
9265
|
/* @__PURE__ */ jsx8("em", { "data-testid": `batchbild-unsupported-${bild.media_ref}`, children: "Dieser Host l\xF6st keine Bilder auf \u2014 nur die Metadaten sind bekannt." })
|
|
8829
9266
|
] });
|
|
9267
|
+
var HERKUNFT_TEXT = {
|
|
9268
|
+
generated_in_app: "in der App erzeugt",
|
|
9269
|
+
external_import: "extern importiert",
|
|
9270
|
+
mixed: "gemischte Herkunft"
|
|
9271
|
+
};
|
|
9272
|
+
var HERKUNFT_KURZ = {
|
|
9273
|
+
generated_in_app: "in der App erzeugt",
|
|
9274
|
+
external_import: "extern importiert"
|
|
9275
|
+
};
|
|
8830
9276
|
var BatchKachel = ({ port, batch }) => {
|
|
8831
|
-
const { quellen, fehler: fehler4 } = useBildQuellen(port, batch.images);
|
|
9277
|
+
const { quellen, fehler: fehler4, wiederholbar, erneutVersuchen } = useBildQuellen(port, batch.images);
|
|
8832
9278
|
const aufloesbar = Boolean(port.imageLibrary);
|
|
8833
9279
|
const bilder = batch.images.map((bild) => ({
|
|
8834
9280
|
id: bild.media_ref,
|
|
@@ -8854,7 +9300,7 @@ var BatchKachel = ({ port, batch }) => {
|
|
|
8854
9300
|
/* @__PURE__ */ jsxs8("header", { className: "szenen-bilder__batchkopf", children: [
|
|
8855
9301
|
/* @__PURE__ */ jsx8("strong", { children: batch.title }),
|
|
8856
9302
|
/* @__PURE__ */ jsxs8("span", { className: "szenen-bilder__herkunft", children: [
|
|
8857
|
-
batch.source_kind
|
|
9303
|
+
HERKUNFT_TEXT[batch.source_kind],
|
|
8858
9304
|
" \xB7 ",
|
|
8859
9305
|
batch.images.length,
|
|
8860
9306
|
" Bild(er)"
|
|
@@ -8868,6 +9314,16 @@ var BatchKachel = ({ port, batch }) => {
|
|
|
8868
9314
|
satzTitel: batch.title
|
|
8869
9315
|
}
|
|
8870
9316
|
),
|
|
9317
|
+
wiederholbar && /* @__PURE__ */ jsx8(
|
|
9318
|
+
"button",
|
|
9319
|
+
{
|
|
9320
|
+
type: "button",
|
|
9321
|
+
className: "szenen-bilder__retry",
|
|
9322
|
+
onClick: erneutVersuchen,
|
|
9323
|
+
"data-testid": `batch-retry-${batch.item_id}`,
|
|
9324
|
+
children: "Bilder erneut laden"
|
|
9325
|
+
}
|
|
9326
|
+
),
|
|
8871
9327
|
/* @__PURE__ */ jsxs8("details", { className: "szenen-bilder__technik", "data-testid": `batch-technik-${batch.item_id}`, children: [
|
|
8872
9328
|
/* @__PURE__ */ jsx8("summary", { children: "Technische Details" }),
|
|
8873
9329
|
/* @__PURE__ */ jsxs8("p", { className: "szenen-bilder__ids", children: [
|
|
@@ -8876,8 +9332,13 @@ var BatchKachel = ({ port, batch }) => {
|
|
|
8876
9332
|
/* @__PURE__ */ jsx8("code", { "data-testid": `batch-item-${batch.item_id}`, children: batch.item_id })
|
|
8877
9333
|
] }),
|
|
8878
9334
|
/* @__PURE__ */ jsxs8("span", { children: [
|
|
8879
|
-
"Batches
|
|
8880
|
-
|
|
9335
|
+
"Batches",
|
|
9336
|
+
" ",
|
|
9337
|
+
batch.origins.length > 0 ? batch.origins.map((h, i) => /* @__PURE__ */ jsxs8(React7.Fragment, { children: [
|
|
9338
|
+
i > 0 ? ", " : "",
|
|
9339
|
+
/* @__PURE__ */ jsx8("code", { children: h.generation_batch_id }),
|
|
9340
|
+
` (${HERKUNFT_KURZ[h.source_kind]})`
|
|
9341
|
+
] }, h.generation_batch_id)) : /* @__PURE__ */ jsx8("code", { children: batch.generation_batch_ids.join(", ") })
|
|
8881
9342
|
] })
|
|
8882
9343
|
] }),
|
|
8883
9344
|
/* @__PURE__ */ jsx8("ul", { className: "szenen-bilder__batchliste", children: batch.images.map((bild) => /* @__PURE__ */ jsx8("li", { children: /* @__PURE__ */ jsx8(
|
|
@@ -9081,21 +9542,114 @@ var ErzeugenAktion = ({ port, lauf, wahl, wahlFehler, onFertig }) => {
|
|
|
9081
9542
|
)
|
|
9082
9543
|
] });
|
|
9083
9544
|
};
|
|
9545
|
+
var DEFAULT_VORLAGE_KEY = "__default__";
|
|
9084
9546
|
var ZuordnenAktion = ({ port, lauf, onFertig }) => {
|
|
9085
9547
|
const [dateien, setDateien] = useState8([]);
|
|
9086
9548
|
const [seeds, setSeeds] = useState8({});
|
|
9549
|
+
const [zip, setZip] = useState8(null);
|
|
9550
|
+
const [zipLaedt, setZipLaedt] = useState8(false);
|
|
9551
|
+
const [seedListe, setSeedListe] = useState8("");
|
|
9552
|
+
const [vorlagen, setVorlagen] = useState8([DEFAULT_SEED_VORLAGE]);
|
|
9553
|
+
const [vorlageKey, setVorlageKey] = useState8(DEFAULT_VORLAGE_KEY);
|
|
9554
|
+
const [vorlagenStand, setVorlagenStand] = useState8(0);
|
|
9555
|
+
const [vorlageBenennen, setVorlageBenennen] = useState8(false);
|
|
9556
|
+
const [vorlageName, setVorlageName] = useState8("");
|
|
9087
9557
|
const [modell, setModell] = useState8("");
|
|
9088
9558
|
const [itemId, setItemId] = useState8("");
|
|
9089
9559
|
const [laeuft, setLaeuft] = useState8(false);
|
|
9090
9560
|
const [notiz, setNotiz] = useState8(null);
|
|
9561
|
+
const auswahlMarke = useRef2(0);
|
|
9562
|
+
const zipAktiv = zip !== null;
|
|
9563
|
+
useEffect4(() => {
|
|
9564
|
+
if (!zipAktiv) return;
|
|
9565
|
+
let abgebrochen = false;
|
|
9566
|
+
void (async () => {
|
|
9567
|
+
const gelesen = port.seedTemplates ? await port.seedTemplates.listSeedTemplates() : null;
|
|
9568
|
+
if (abgebrochen) return;
|
|
9569
|
+
setVorlagen(mitDefaultVorlage(
|
|
9570
|
+
gelesen && gelesen.kind === "ok" ? gelesen.value : []
|
|
9571
|
+
));
|
|
9572
|
+
})();
|
|
9573
|
+
return () => {
|
|
9574
|
+
abgebrochen = true;
|
|
9575
|
+
};
|
|
9576
|
+
}, [zipAktiv, port, vorlagenStand]);
|
|
9091
9577
|
if (!port.imageBatchWrite) {
|
|
9092
9578
|
return /* @__PURE__ */ jsx8("p", { "data-testid": `zuordnen-unsupported-${lauf.run_id}`, children: "Dieser Host ordnet keine externen Bilder zu \u2014 er hat keinen gesicherten Batch-Schreibweg. Die Historie oben bleibt trotzdem sichtbar." });
|
|
9093
9579
|
}
|
|
9094
|
-
const
|
|
9580
|
+
const zuordnung = zip ? ordneSeedsZu(seedListe, zip.bilder.length) : null;
|
|
9581
|
+
const dateiWahl = async (liste) => {
|
|
9582
|
+
const gewaehlt = [...liste ?? []];
|
|
9583
|
+
const warZip = zip !== null;
|
|
9584
|
+
const meine = auswahlMarke.current += 1;
|
|
9585
|
+
const veraltet = () => auswahlMarke.current !== meine;
|
|
9586
|
+
const zips = gewaehlt.filter(istZipDatei);
|
|
9587
|
+
const nurEinZip = zips.length === 1 && gewaehlt.length === 1;
|
|
9588
|
+
setNotiz(null);
|
|
9589
|
+
setSeeds({});
|
|
9590
|
+
setZip(null);
|
|
9591
|
+
setDateien(zips.length === 0 ? gewaehlt : []);
|
|
9592
|
+
setZipLaedt(nurEinZip);
|
|
9593
|
+
if (zips.length > 0 && zips.length !== gewaehlt.length) {
|
|
9594
|
+
setNotiz({
|
|
9595
|
+
tone: "error",
|
|
9596
|
+
text: `Die Auswahl enth\xE4lt ${zips.length} ZIP-Datei(en) und ${gewaehlt.length - zips.length} Bilddatei(en). Bitte entweder ein ZIP oder einzelne Bilder w\xE4hlen \u2014 nicht beides in einem Import.`
|
|
9597
|
+
});
|
|
9598
|
+
return;
|
|
9599
|
+
}
|
|
9600
|
+
if (zips.length > 1) {
|
|
9601
|
+
setNotiz({
|
|
9602
|
+
tone: "error",
|
|
9603
|
+
text: `${zips.length} ZIP-Dateien gew\xE4hlt (${zips.map((z) => z.name).join(", ")}). Ein Import nimmt genau ein ZIP.`
|
|
9604
|
+
});
|
|
9605
|
+
return;
|
|
9606
|
+
}
|
|
9607
|
+
if (!nurEinZip) return;
|
|
9608
|
+
const datei = zips[0];
|
|
9609
|
+
try {
|
|
9610
|
+
const bilder = await leseBilderAusZip(datei);
|
|
9611
|
+
if (veraltet()) return;
|
|
9612
|
+
setZip({ datei, bilder });
|
|
9613
|
+
if (!warZip) {
|
|
9614
|
+
setSeedListe(DEFAULT_SEED_VORLAGE.seeds);
|
|
9615
|
+
setVorlageKey(DEFAULT_VORLAGE_KEY);
|
|
9616
|
+
}
|
|
9617
|
+
} catch (fehler4) {
|
|
9618
|
+
if (veraltet()) return;
|
|
9619
|
+
setNotiz({
|
|
9620
|
+
tone: "error",
|
|
9621
|
+
text: fehler4 instanceof Error ? fehler4.message : String(fehler4)
|
|
9622
|
+
});
|
|
9623
|
+
}
|
|
9624
|
+
if (veraltet()) return;
|
|
9625
|
+
setZipLaedt(false);
|
|
9626
|
+
};
|
|
9627
|
+
const schreiben = async (bilder, generationBatchId, titel) => {
|
|
9095
9628
|
setLaeuft(true);
|
|
9096
9629
|
setNotiz(null);
|
|
9097
9630
|
try {
|
|
9098
|
-
const
|
|
9631
|
+
const ergebnis = await port.imageBatchWrite.importBatch({
|
|
9632
|
+
runId: lauf.run_id,
|
|
9633
|
+
generationBatchId,
|
|
9634
|
+
images: bilder,
|
|
9635
|
+
title: titel
|
|
9636
|
+
});
|
|
9637
|
+
setNotiz(
|
|
9638
|
+
ergebnis.kind === "ok" ? { tone: "ok", text: `Importiert als Item ${ergebnis.value.item_id} mit ${bilder.length} Bild(ern).` } : noticeFor(ergebnis, "Externe Bilder importieren")
|
|
9639
|
+
);
|
|
9640
|
+
} catch (fehler4) {
|
|
9641
|
+
setNotiz({
|
|
9642
|
+
tone: "error",
|
|
9643
|
+
text: `Externe Bilder importieren: ${fehler4 instanceof Error ? fehler4.message : String(fehler4)}`
|
|
9644
|
+
});
|
|
9645
|
+
}
|
|
9646
|
+
setLaeuft(false);
|
|
9647
|
+
onFertig();
|
|
9648
|
+
};
|
|
9649
|
+
const importieren = async () => {
|
|
9650
|
+
let bilder;
|
|
9651
|
+
try {
|
|
9652
|
+
bilder = [];
|
|
9099
9653
|
for (let index = 0; index < dateien.length; index++) {
|
|
9100
9654
|
const roh = (seeds[index] ?? "").trim();
|
|
9101
9655
|
const seed = roh === "" ? void 0 : Number.parseInt(roh, 10);
|
|
@@ -9108,25 +9662,42 @@ var ZuordnenAktion = ({ port, lauf, onFertig }) => {
|
|
|
9108
9662
|
...seed !== void 0 ? { seed } : {}
|
|
9109
9663
|
});
|
|
9110
9664
|
}
|
|
9111
|
-
const ergebnis = await port.imageBatchWrite.importBatch({
|
|
9112
|
-
runId: lauf.run_id,
|
|
9113
|
-
// Vor dem Aufruf gebildet und stabil (D-113): eine Wiederholung
|
|
9114
|
-
// derselben Einreichung erzeugt kein zweites Item.
|
|
9115
|
-
generationBatchId: `imp-${lauf.run_id}-${dateien.length}-${dateien.map((d) => d.size).join("_")}`,
|
|
9116
|
-
images: bilder,
|
|
9117
|
-
title: `Externer Import zu Lauf ${lauf.run_id}`
|
|
9118
|
-
});
|
|
9119
|
-
setNotiz(
|
|
9120
|
-
ergebnis.kind === "ok" ? { tone: "ok", text: `Importiert als Item ${ergebnis.value.item_id} mit ${bilder.length} Bild(ern).` } : noticeFor(ergebnis, "Externe Bilder importieren")
|
|
9121
|
-
);
|
|
9122
9665
|
} catch (fehler4) {
|
|
9123
9666
|
setNotiz({
|
|
9124
9667
|
tone: "error",
|
|
9125
9668
|
text: `Externe Bilder importieren: ${fehler4 instanceof Error ? fehler4.message : String(fehler4)}`
|
|
9126
9669
|
});
|
|
9670
|
+
return;
|
|
9127
9671
|
}
|
|
9128
|
-
|
|
9129
|
-
|
|
9672
|
+
await schreiben(
|
|
9673
|
+
bilder,
|
|
9674
|
+
// Vor dem Aufruf gebildet und stabil (D-113): eine Wiederholung
|
|
9675
|
+
// derselben Einreichung erzeugt kein zweites Item.
|
|
9676
|
+
`imp-${lauf.run_id}-${dateien.length}-${dateien.map((d) => d.size).join("_")}`,
|
|
9677
|
+
`Externer Import zu Lauf ${lauf.run_id}`
|
|
9678
|
+
);
|
|
9679
|
+
};
|
|
9680
|
+
const zipImportieren = async () => {
|
|
9681
|
+
if (!zip || !zuordnung) return;
|
|
9682
|
+
if (zuordnung.ungueltige.length > 0) {
|
|
9683
|
+
setNotiz({ tone: "error", text: seedFehlerText(zuordnung.ungueltige) });
|
|
9684
|
+
return;
|
|
9685
|
+
}
|
|
9686
|
+
const bilder = zip.bilder.map((bild, index) => {
|
|
9687
|
+
const seed = zuordnung.eintraege[index]?.seed ?? null;
|
|
9688
|
+
return {
|
|
9689
|
+
imageBase64: bild.base64,
|
|
9690
|
+
...modell.trim() ? { model: modell.trim() } : {},
|
|
9691
|
+
// Unbekannt bleibt unbekannt: kein Feld statt eines erfundenen
|
|
9692
|
+
// Werts (§6.1).
|
|
9693
|
+
...seed !== null ? { seed } : {}
|
|
9694
|
+
};
|
|
9695
|
+
});
|
|
9696
|
+
await schreiben(
|
|
9697
|
+
bilder,
|
|
9698
|
+
`imp-${lauf.run_id}-zip-${zip.datei.size}-${bilder.length}`,
|
|
9699
|
+
`Externer Import zu Lauf ${lauf.run_id} (${zip.datei.name})`
|
|
9700
|
+
);
|
|
9130
9701
|
};
|
|
9131
9702
|
const zuordnen = async () => {
|
|
9132
9703
|
setLaeuft(true);
|
|
@@ -9144,35 +9715,63 @@ var ZuordnenAktion = ({ port, lauf, onFertig }) => {
|
|
|
9144
9715
|
setLaeuft(false);
|
|
9145
9716
|
onFertig();
|
|
9146
9717
|
};
|
|
9718
|
+
const vorlageWaehlen = (key) => {
|
|
9719
|
+
setVorlageKey(key);
|
|
9720
|
+
const vorlage = key === DEFAULT_VORLAGE_KEY ? DEFAULT_SEED_VORLAGE : vorlagen.find((v) => String(v.id) === key);
|
|
9721
|
+
if (vorlage) setSeedListe(vorlage.seeds);
|
|
9722
|
+
};
|
|
9723
|
+
const vorlageSpeichern = async () => {
|
|
9724
|
+
const name = vorlageName.trim();
|
|
9725
|
+
if (!name || !seedListe.trim() || !port.seedTemplates?.saveSeedTemplate) return;
|
|
9726
|
+
const ergebnis = await port.seedTemplates.saveSeedTemplate({ name, seeds: seedListe.trim() });
|
|
9727
|
+
if (ergebnis.kind !== "ok") {
|
|
9728
|
+
setNotiz(noticeFor(ergebnis, "Seed-Vorlage speichern"));
|
|
9729
|
+
return;
|
|
9730
|
+
}
|
|
9731
|
+
setVorlageBenennen(false);
|
|
9732
|
+
setVorlageName("");
|
|
9733
|
+
if (ergebnis.value.id !== void 0) setVorlageKey(String(ergebnis.value.id));
|
|
9734
|
+
setVorlagenStand((n) => n + 1);
|
|
9735
|
+
};
|
|
9736
|
+
const vorlageLoeschen = async () => {
|
|
9737
|
+
if (vorlageKey === DEFAULT_VORLAGE_KEY || !port.seedTemplates?.deleteSeedTemplate) return;
|
|
9738
|
+
const ergebnis = await port.seedTemplates.deleteSeedTemplate(vorlageKey);
|
|
9739
|
+
if (ergebnis.kind !== "ok") {
|
|
9740
|
+
setNotiz(noticeFor(ergebnis, "Seed-Vorlage l\xF6schen"));
|
|
9741
|
+
return;
|
|
9742
|
+
}
|
|
9743
|
+
setVorlageKey(DEFAULT_VORLAGE_KEY);
|
|
9744
|
+
setVorlagenStand((n) => n + 1);
|
|
9745
|
+
};
|
|
9147
9746
|
return /* @__PURE__ */ jsxs8("div", { className: "szenen-bilder__aktion-inhalt", "data-testid": `zuordnen-${lauf.run_id}`, children: [
|
|
9148
9747
|
/* @__PURE__ */ jsxs8("label", { children: [
|
|
9149
|
-
"Bilddateien",
|
|
9748
|
+
"Bilddateien oder ein ZIP",
|
|
9150
9749
|
/* @__PURE__ */ jsx8(
|
|
9151
9750
|
"input",
|
|
9152
9751
|
{
|
|
9153
9752
|
type: "file",
|
|
9154
|
-
accept: "image
|
|
9753
|
+
accept: "image/*,.zip",
|
|
9155
9754
|
multiple: true,
|
|
9156
9755
|
"data-testid": `zuordnen-dateien-${lauf.run_id}`,
|
|
9157
9756
|
onChange: (e) => {
|
|
9158
|
-
|
|
9159
|
-
setSeeds({});
|
|
9757
|
+
void dateiWahl(e.target.files);
|
|
9160
9758
|
}
|
|
9161
9759
|
}
|
|
9162
9760
|
)
|
|
9163
9761
|
] }),
|
|
9762
|
+
zipLaedt && /* @__PURE__ */ jsx8("p", { "data-testid": `zuordnen-zip-laedt-${lauf.run_id}`, children: "ZIP wird gelesen \u2026" }),
|
|
9763
|
+
(dateien.length > 0 || zip) && /* @__PURE__ */ jsxs8("label", { children: [
|
|
9764
|
+
"Modell (f\xFCr alle Bilder, leer = unbekannt)",
|
|
9765
|
+
/* @__PURE__ */ jsx8(
|
|
9766
|
+
"input",
|
|
9767
|
+
{
|
|
9768
|
+
value: modell,
|
|
9769
|
+
"data-testid": `zuordnen-modell-${lauf.run_id}`,
|
|
9770
|
+
onChange: (e) => setModell(e.target.value)
|
|
9771
|
+
}
|
|
9772
|
+
)
|
|
9773
|
+
] }),
|
|
9164
9774
|
dateien.length > 0 && /* @__PURE__ */ jsxs8(Fragment4, { children: [
|
|
9165
|
-
/* @__PURE__ */ jsxs8("label", { children: [
|
|
9166
|
-
"Modell (f\xFCr alle Bilder, leer = unbekannt)",
|
|
9167
|
-
/* @__PURE__ */ jsx8(
|
|
9168
|
-
"input",
|
|
9169
|
-
{
|
|
9170
|
-
value: modell,
|
|
9171
|
-
"data-testid": `zuordnen-modell-${lauf.run_id}`,
|
|
9172
|
-
onChange: (e) => setModell(e.target.value)
|
|
9173
|
-
}
|
|
9174
|
-
)
|
|
9175
|
-
] }),
|
|
9176
9775
|
/* @__PURE__ */ jsx8("ol", { "data-testid": `zuordnen-vorschau-${lauf.run_id}`, children: dateien.map((datei, index) => /* @__PURE__ */ jsxs8("li", { children: [
|
|
9177
9776
|
/* @__PURE__ */ jsxs8("span", { children: [
|
|
9178
9777
|
"Position ",
|
|
@@ -9206,6 +9805,157 @@ var ZuordnenAktion = ({ port, lauf, onFertig }) => {
|
|
|
9206
9805
|
}
|
|
9207
9806
|
)
|
|
9208
9807
|
] }),
|
|
9808
|
+
zip && zuordnung && /* @__PURE__ */ jsxs8("div", { className: "szenen-bilder__zip", "data-testid": `zuordnen-zip-${lauf.run_id}`, children: [
|
|
9809
|
+
/* @__PURE__ */ jsxs8("p", { className: "szenen-bilder__zipkopf", children: [
|
|
9810
|
+
"ZIP ",
|
|
9811
|
+
/* @__PURE__ */ jsx8("code", { children: zip.datei.name }),
|
|
9812
|
+
" \u2014 ",
|
|
9813
|
+
zip.bilder.length,
|
|
9814
|
+
" Bild(er) erkannt"
|
|
9815
|
+
] }),
|
|
9816
|
+
/* @__PURE__ */ jsxs8("label", { children: [
|
|
9817
|
+
"Seeds (pro Bild ein Seed, Trenner , oder ;)",
|
|
9818
|
+
/* @__PURE__ */ jsx8(
|
|
9819
|
+
"input",
|
|
9820
|
+
{
|
|
9821
|
+
"data-testid": `zuordnen-seedliste-${lauf.run_id}`,
|
|
9822
|
+
value: seedListe,
|
|
9823
|
+
placeholder: "z. B. 1; 10; 100; 1000",
|
|
9824
|
+
onChange: (e) => setSeedListe(e.target.value)
|
|
9825
|
+
}
|
|
9826
|
+
)
|
|
9827
|
+
] }),
|
|
9828
|
+
/* @__PURE__ */ jsxs8("label", { children: [
|
|
9829
|
+
"Seed-Vorlage",
|
|
9830
|
+
/* @__PURE__ */ jsxs8(
|
|
9831
|
+
"select",
|
|
9832
|
+
{
|
|
9833
|
+
"data-testid": `zuordnen-seedvorlage-${lauf.run_id}`,
|
|
9834
|
+
value: vorlageKey,
|
|
9835
|
+
onChange: (e) => vorlageWaehlen(e.target.value),
|
|
9836
|
+
children: [
|
|
9837
|
+
/* @__PURE__ */ jsxs8("option", { value: DEFAULT_VORLAGE_KEY, children: [
|
|
9838
|
+
"Default (",
|
|
9839
|
+
DEFAULT_SEED_VORLAGE.seeds,
|
|
9840
|
+
")"
|
|
9841
|
+
] }),
|
|
9842
|
+
vorlagen.filter((v) => !v.builtin).map((v) => /* @__PURE__ */ jsxs8("option", { value: String(v.id), children: [
|
|
9843
|
+
v.name,
|
|
9844
|
+
" (",
|
|
9845
|
+
v.seeds,
|
|
9846
|
+
")"
|
|
9847
|
+
] }, String(v.id)))
|
|
9848
|
+
]
|
|
9849
|
+
}
|
|
9850
|
+
)
|
|
9851
|
+
] }),
|
|
9852
|
+
(port.seedTemplates?.saveSeedTemplate || port.seedTemplates?.deleteSeedTemplate) && /* @__PURE__ */ jsxs8("p", { className: "szenen-bilder__zipvorlagen", children: [
|
|
9853
|
+
port.seedTemplates?.saveSeedTemplate && !vorlageBenennen && /* @__PURE__ */ jsx8(
|
|
9854
|
+
"button",
|
|
9855
|
+
{
|
|
9856
|
+
type: "button",
|
|
9857
|
+
"data-testid": `zuordnen-vorlage-neu-${lauf.run_id}`,
|
|
9858
|
+
disabled: !seedListe.trim(),
|
|
9859
|
+
onClick: () => {
|
|
9860
|
+
setVorlageName("");
|
|
9861
|
+
setVorlageBenennen(true);
|
|
9862
|
+
},
|
|
9863
|
+
children: "Als Vorlage speichern"
|
|
9864
|
+
}
|
|
9865
|
+
),
|
|
9866
|
+
port.seedTemplates?.deleteSeedTemplate && /* @__PURE__ */ jsx8(
|
|
9867
|
+
"button",
|
|
9868
|
+
{
|
|
9869
|
+
type: "button",
|
|
9870
|
+
"data-testid": `zuordnen-vorlage-loeschen-${lauf.run_id}`,
|
|
9871
|
+
disabled: vorlageKey === DEFAULT_VORLAGE_KEY,
|
|
9872
|
+
onClick: () => {
|
|
9873
|
+
void vorlageLoeschen();
|
|
9874
|
+
},
|
|
9875
|
+
children: "Vorlage l\xF6schen"
|
|
9876
|
+
}
|
|
9877
|
+
)
|
|
9878
|
+
] }),
|
|
9879
|
+
vorlageBenennen && /* @__PURE__ */ jsxs8(Fragment4, { children: [
|
|
9880
|
+
/* @__PURE__ */ jsxs8("label", { children: [
|
|
9881
|
+
"Name der Vorlage",
|
|
9882
|
+
/* @__PURE__ */ jsx8(
|
|
9883
|
+
"input",
|
|
9884
|
+
{
|
|
9885
|
+
"data-testid": `zuordnen-vorlage-name-${lauf.run_id}`,
|
|
9886
|
+
value: vorlageName,
|
|
9887
|
+
onChange: (e) => setVorlageName(e.target.value)
|
|
9888
|
+
}
|
|
9889
|
+
)
|
|
9890
|
+
] }),
|
|
9891
|
+
/* @__PURE__ */ jsxs8("p", { className: "szenen-bilder__zipvorlagen", children: [
|
|
9892
|
+
/* @__PURE__ */ jsx8(
|
|
9893
|
+
"button",
|
|
9894
|
+
{
|
|
9895
|
+
type: "button",
|
|
9896
|
+
"data-testid": `zuordnen-vorlage-sichern-${lauf.run_id}`,
|
|
9897
|
+
disabled: !vorlageName.trim(),
|
|
9898
|
+
onClick: () => {
|
|
9899
|
+
void vorlageSpeichern();
|
|
9900
|
+
},
|
|
9901
|
+
children: "Speichern"
|
|
9902
|
+
}
|
|
9903
|
+
),
|
|
9904
|
+
/* @__PURE__ */ jsx8(
|
|
9905
|
+
"button",
|
|
9906
|
+
{
|
|
9907
|
+
type: "button",
|
|
9908
|
+
"data-testid": `zuordnen-vorlage-abbrechen-${lauf.run_id}`,
|
|
9909
|
+
onClick: () => setVorlageBenennen(false),
|
|
9910
|
+
children: "Abbrechen"
|
|
9911
|
+
}
|
|
9912
|
+
)
|
|
9913
|
+
] })
|
|
9914
|
+
] }),
|
|
9915
|
+
zuordnung.ueberzaehlig > 0 && /* @__PURE__ */ jsxs8("p", { "data-testid": `zuordnen-ueberzaehlig-${lauf.run_id}`, children: [
|
|
9916
|
+
zuordnung.ueberzaehlig,
|
|
9917
|
+
" Seed(s) mehr als Bilder \u2014 die \xFCberz\xE4hligen werden nicht geschrieben."
|
|
9918
|
+
] }),
|
|
9919
|
+
/* @__PURE__ */ jsx8(
|
|
9920
|
+
"ol",
|
|
9921
|
+
{
|
|
9922
|
+
className: "szenen-bilder__zipliste",
|
|
9923
|
+
"data-testid": `zuordnen-zip-vorschau-${lauf.run_id}`,
|
|
9924
|
+
children: zip.bilder.map((bild, index) => {
|
|
9925
|
+
const eintrag = zuordnung.eintraege[index];
|
|
9926
|
+
return /* @__PURE__ */ jsxs8("li", { children: [
|
|
9927
|
+
/* @__PURE__ */ jsxs8("span", { children: [
|
|
9928
|
+
"Position ",
|
|
9929
|
+
index,
|
|
9930
|
+
": ",
|
|
9931
|
+
bild.name
|
|
9932
|
+
] }),
|
|
9933
|
+
/* @__PURE__ */ jsx8(
|
|
9934
|
+
"span",
|
|
9935
|
+
{
|
|
9936
|
+
"data-testid": `zuordnen-zip-seed-${lauf.run_id}-${index}`,
|
|
9937
|
+
"data-seed": eintrag.seed === null ? "" : String(eintrag.seed),
|
|
9938
|
+
children: eintrag.ungueltig ? `Seed ung\xFCltig: "${eintrag.roh}"` : eintrag.seed === null ? "Seed unbekannt" : `Seed ${eintrag.seed}`
|
|
9939
|
+
}
|
|
9940
|
+
)
|
|
9941
|
+
] }, `${bild.name}-${index}`);
|
|
9942
|
+
})
|
|
9943
|
+
}
|
|
9944
|
+
),
|
|
9945
|
+
/* @__PURE__ */ jsxs8(
|
|
9946
|
+
"button",
|
|
9947
|
+
{
|
|
9948
|
+
type: "button",
|
|
9949
|
+
"data-testid": `zuordnen-zip-import-${lauf.run_id}`,
|
|
9950
|
+
disabled: laeuft || zipLaedt,
|
|
9951
|
+
onClick: zipImportieren,
|
|
9952
|
+
children: [
|
|
9953
|
+
zip.bilder.length,
|
|
9954
|
+
" Bild(er) aus ZIP importieren"
|
|
9955
|
+
]
|
|
9956
|
+
}
|
|
9957
|
+
)
|
|
9958
|
+
] }),
|
|
9209
9959
|
/* @__PURE__ */ jsxs8("label", { children: [
|
|
9210
9960
|
"Oder ein bestehendes Library-Item",
|
|
9211
9961
|
/* @__PURE__ */ jsx8(
|
|
@@ -9261,6 +10011,8 @@ var RunsPanel = ({ port, source, contextId }) => {
|
|
|
9261
10011
|
const [nachladen, setNachladen] = useState9(0);
|
|
9262
10012
|
const faehig = capabilitiesOf(port);
|
|
9263
10013
|
const quellSchluessel = source.kind === "published" ? source.shotVersionId : source.kind === "draft_chain" ? source.chainId : source.sceneElementId;
|
|
10014
|
+
const aktuelleQuelle = useRef3({ kind: source.kind, id: quellSchluessel });
|
|
10015
|
+
aktuelleQuelle.current = { kind: source.kind, id: quellSchluessel };
|
|
9264
10016
|
const [phase, neuLaden] = usePortQuery(
|
|
9265
10017
|
() => {
|
|
9266
10018
|
if (source.kind === "published") return port.runs.listRunsForShot(source.shotVersionId);
|
|
@@ -9271,12 +10023,16 @@ var RunsPanel = ({ port, source, contextId }) => {
|
|
|
9271
10023
|
);
|
|
9272
10024
|
const starten = async () => {
|
|
9273
10025
|
if (source.kind !== "published") return;
|
|
10026
|
+
const gestartetFuer = { kind: source.kind, id: source.shotVersionId };
|
|
9274
10027
|
setLaeuft(true);
|
|
9275
10028
|
const ergebnis = await port.runs.startRun({
|
|
9276
10029
|
shotVersionId: source.shotVersionId,
|
|
9277
10030
|
...anweisung.trim() ? { userInstruction: anweisung.trim() } : {},
|
|
9278
10031
|
contextId
|
|
9279
10032
|
});
|
|
10033
|
+
if (aktuelleQuelle.current.kind !== gestartetFuer.kind || aktuelleQuelle.current.id !== gestartetFuer.id) {
|
|
10034
|
+
return;
|
|
10035
|
+
}
|
|
9280
10036
|
setNotiz(noticeFor(ergebnis, "Prompt-Lauf starten"));
|
|
9281
10037
|
setLaeuft(false);
|
|
9282
10038
|
setNachladen((n) => n + 1);
|
|
@@ -9286,6 +10042,12 @@ var RunsPanel = ({ port, source, contextId }) => {
|
|
|
9286
10042
|
const [gewaehlteRunde, setGewaehlteRunde] = useState9(void 0);
|
|
9287
10043
|
const neueste = laeufe.length > 0 ? laeufe[laeufe.length - 1].run_id : null;
|
|
9288
10044
|
const offeneRunde = gewaehlteRunde === void 0 ? neueste : gewaehlteRunde;
|
|
10045
|
+
useEffect5(() => {
|
|
10046
|
+
setGewaehlteRunde(void 0);
|
|
10047
|
+
setAnweisung("");
|
|
10048
|
+
setNotiz(null);
|
|
10049
|
+
setLaeuft(false);
|
|
10050
|
+
}, [source.kind, quellSchluessel]);
|
|
9289
10051
|
return /* @__PURE__ */ jsxs9("div", { className: "szenen-runs", "data-testid": "laeufe-bereich", "data-shot-version": quellSchluessel, children: [
|
|
9290
10052
|
/* @__PURE__ */ jsx9("h4", { children: "Prompt-L\xE4ufe" }),
|
|
9291
10053
|
source.kind === "published" && /* @__PURE__ */ jsxs9("div", { className: "szenen-runs__start", children: [
|
|
@@ -9356,6 +10118,26 @@ var Sekundaertext = ({ titel, text, testId }) => {
|
|
|
9356
10118
|
)
|
|
9357
10119
|
] });
|
|
9358
10120
|
};
|
|
10121
|
+
var LISTE_ANFANG = 3;
|
|
10122
|
+
var SekundaerListe = ({ titel, eintraege, testId, className }) => {
|
|
10123
|
+
const [alle, setAlle] = useState9(false);
|
|
10124
|
+
const lang = eintraege.length > LISTE_ANFANG;
|
|
10125
|
+
const gezeigt = lang && !alle ? eintraege.slice(0, LISTE_ANFANG) : eintraege;
|
|
10126
|
+
return /* @__PURE__ */ jsxs9("section", { className: "szenen-runs__sekundaer", children: [
|
|
10127
|
+
/* @__PURE__ */ jsx9("h5", { children: titel }),
|
|
10128
|
+
/* @__PURE__ */ jsx9("ul", { className, "data-testid": testId, children: gezeigt.map((eintrag, i) => /* @__PURE__ */ jsx9("li", { children: eintrag }, i)) }),
|
|
10129
|
+
lang && /* @__PURE__ */ jsx9(
|
|
10130
|
+
"button",
|
|
10131
|
+
{
|
|
10132
|
+
type: "button",
|
|
10133
|
+
className: "szenen-runs__mehr",
|
|
10134
|
+
onClick: () => setAlle(!alle),
|
|
10135
|
+
"data-testid": `${testId}-mehr`,
|
|
10136
|
+
children: alle ? "weniger anzeigen" : `${eintraege.length - LISTE_ANFANG} weitere anzeigen`
|
|
10137
|
+
}
|
|
10138
|
+
)
|
|
10139
|
+
] });
|
|
10140
|
+
};
|
|
9359
10141
|
var RundenEintrag = ({ port, lauf, runde, offen, onUmschalten, itemMoeglich, onItemGespeichert }) => {
|
|
9360
10142
|
const [notiz, setNotiz] = useState9(null);
|
|
9361
10143
|
const [laeuft, setLaeuft] = useState9(false);
|
|
@@ -9441,11 +10223,23 @@ var RundenEintrag = ({ port, lauf, runde, offen, onUmschalten, itemMoeglich, onI
|
|
|
9441
10223
|
testId: `feedback-${lauf.run_id}`
|
|
9442
10224
|
}
|
|
9443
10225
|
),
|
|
9444
|
-
lauf.output.visibility.assumptions.length > 0 && /* @__PURE__ */
|
|
9445
|
-
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
|
|
10226
|
+
lauf.output.visibility.assumptions.length > 0 && /* @__PURE__ */ jsx9(
|
|
10227
|
+
SekundaerListe,
|
|
10228
|
+
{
|
|
10229
|
+
titel: "Annahmen",
|
|
10230
|
+
eintraege: lauf.output.visibility.assumptions,
|
|
10231
|
+
testId: `annahmen-${lauf.run_id}`
|
|
10232
|
+
}
|
|
10233
|
+
),
|
|
10234
|
+
lauf.output.notes.length > 0 && /* @__PURE__ */ jsx9(
|
|
10235
|
+
SekundaerListe,
|
|
10236
|
+
{
|
|
10237
|
+
titel: "Notizen",
|
|
10238
|
+
eintraege: lauf.output.notes,
|
|
10239
|
+
testId: `notizen-${lauf.run_id}`,
|
|
10240
|
+
className: "szenen-runs__notizen"
|
|
10241
|
+
}
|
|
10242
|
+
)
|
|
9449
10243
|
] }),
|
|
9450
10244
|
lauf.raw_output && /* @__PURE__ */ jsxs9("details", { "data-testid": `diagnose-${lauf.run_id}`, children: [
|
|
9451
10245
|
/* @__PURE__ */ jsx9("summary", { children: "Rohantwort (Diagnose)" }),
|
|
@@ -9533,7 +10327,7 @@ var ElementDetail = ({
|
|
|
9533
10327
|
() => schnappschuss ? fieldsForType(schnappschuss.element.type) : [],
|
|
9534
10328
|
[schnappschuss?.element.type]
|
|
9535
10329
|
);
|
|
9536
|
-
|
|
10330
|
+
useEffect6(() => {
|
|
9537
10331
|
setWahl(null);
|
|
9538
10332
|
setBasiswechsel(null);
|
|
9539
10333
|
setNotiz(null);
|
|
@@ -9564,7 +10358,7 @@ var ElementDetail = ({
|
|
|
9564
10358
|
return {};
|
|
9565
10359
|
}, [schnappschuss, angezeigt]);
|
|
9566
10360
|
const anzeigeSchluessel = angezeigt.kind === "version" ? `v:${angezeigt.versionId}` : angezeigt.kind;
|
|
9567
|
-
|
|
10361
|
+
useEffect6(() => {
|
|
9568
10362
|
if (!schnappschuss) return;
|
|
9569
10363
|
setEntwurf(formFromPayload(felder, angezeigtePayload));
|
|
9570
10364
|
}, [
|
|
@@ -9996,7 +10790,7 @@ var ElementDetail = ({
|
|
|
9996
10790
|
};
|
|
9997
10791
|
|
|
9998
10792
|
// src/features/szenen/areas/useSchmalesDisplay.ts
|
|
9999
|
-
import { useEffect as
|
|
10793
|
+
import { useEffect as useEffect7, useState as useState11 } from "react";
|
|
10000
10794
|
var SCHMALE_ANSICHT = "(max-width: 760px)";
|
|
10001
10795
|
function messen(abfrage) {
|
|
10002
10796
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
|
|
@@ -10004,7 +10798,7 @@ function messen(abfrage) {
|
|
|
10004
10798
|
}
|
|
10005
10799
|
function useSchmalesDisplay(abfrage = SCHMALE_ANSICHT) {
|
|
10006
10800
|
const [schmal, setSchmal] = useState11(() => messen(abfrage));
|
|
10007
|
-
|
|
10801
|
+
useEffect7(() => {
|
|
10008
10802
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
10009
10803
|
const liste = window.matchMedia(abfrage);
|
|
10010
10804
|
const aktualisieren = () => setSchmal(liste.matches);
|
|
@@ -10431,7 +11225,7 @@ var MediaArea = ({ port }) => {
|
|
|
10431
11225
|
};
|
|
10432
11226
|
|
|
10433
11227
|
// src/features/szenen/areas/DeriveArea.tsx
|
|
10434
|
-
import { useEffect as
|
|
11228
|
+
import { useEffect as useEffect8, useMemo as useMemo3, useRef as useRef4, useState as useState16 } from "react";
|
|
10435
11229
|
|
|
10436
11230
|
// src/features/szenen/areas/RecoveryPanel.tsx
|
|
10437
11231
|
import { useState as useState14 } from "react";
|
|
@@ -10704,7 +11498,7 @@ var MODUS_TEXT = {
|
|
|
10704
11498
|
};
|
|
10705
11499
|
var BelegFeld = ({ titel, text, testId, offen }) => {
|
|
10706
11500
|
const [kopiert, setKopiert] = useState16(false);
|
|
10707
|
-
const feld =
|
|
11501
|
+
const feld = useRef4(null);
|
|
10708
11502
|
async function kopieren() {
|
|
10709
11503
|
try {
|
|
10710
11504
|
await navigator.clipboard.writeText(text);
|
|
@@ -10803,7 +11597,7 @@ var ERSTE_KACHELN = 12;
|
|
|
10803
11597
|
function useSichtbareKacheln(alle, wurzel) {
|
|
10804
11598
|
const [gesehen, setGesehen] = useState16([]);
|
|
10805
11599
|
const schluessel = alle.join(" ");
|
|
10806
|
-
|
|
11600
|
+
useEffect8(() => {
|
|
10807
11601
|
const ziel = wurzel.current;
|
|
10808
11602
|
if (!ziel || typeof IntersectionObserver === "undefined") return void 0;
|
|
10809
11603
|
const beobachter = new IntersectionObserver((eintraege) => {
|
|
@@ -10822,10 +11616,10 @@ function useSichtbareKacheln(alle, wurzel) {
|
|
|
10822
11616
|
}
|
|
10823
11617
|
function useVorschauen(port, mediaRefs) {
|
|
10824
11618
|
const [stand, setStand] = useState16(() => ({ quellen: /* @__PURE__ */ new Map(), gescheitert: /* @__PURE__ */ new Set() }));
|
|
10825
|
-
const gehalten =
|
|
10826
|
-
const ohneBild =
|
|
11619
|
+
const gehalten = useRef4(/* @__PURE__ */ new Map());
|
|
11620
|
+
const ohneBild = useRef4(/* @__PURE__ */ new Set());
|
|
10827
11621
|
const schluessel = mediaRefs.join(" ");
|
|
10828
|
-
|
|
11622
|
+
useEffect8(() => {
|
|
10829
11623
|
const bestand = port.imageLibrary;
|
|
10830
11624
|
if (!bestand?.loadPreview) return void 0;
|
|
10831
11625
|
const refs = schluessel ? schluessel.split(" ") : [];
|
|
@@ -10865,7 +11659,7 @@ function useVorschauen(port, mediaRefs) {
|
|
|
10865
11659
|
var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
|
|
10866
11660
|
const [suche, setSuche] = useState16("");
|
|
10867
11661
|
const [fokus, setFokus] = useState16(null);
|
|
10868
|
-
const browser =
|
|
11662
|
+
const browser = useRef4(null);
|
|
10869
11663
|
const [items] = usePortQuery(
|
|
10870
11664
|
() => port.imageLibrary.listItems({ search: suche.trim() || void 0, limit: 60 }),
|
|
10871
11665
|
[port, suche]
|
|
@@ -11092,8 +11886,6 @@ var DeriveArea = ({ port, contextId }) => {
|
|
|
11092
11886
|
if (ergebnis.kind === "ok") setLauf(ergebnis.value);
|
|
11093
11887
|
else if (ergebnis.kind === "unsupported") {
|
|
11094
11888
|
setMeldung(`Dieser Host bietet \u201E${ergebnis.capability}" nicht an.`);
|
|
11095
|
-
} else if (ergebnis.kind === "rejected") {
|
|
11096
|
-
setMeldung(ergebnis.errors.map((e) => e.message).join("; "));
|
|
11097
11889
|
} else {
|
|
11098
11890
|
setMeldung(ergebnis.message);
|
|
11099
11891
|
}
|
|
@@ -11183,8 +11975,6 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
|
|
|
11183
11975
|
});
|
|
11184
11976
|
if (ergebnis.kind === "ok") {
|
|
11185
11977
|
setUebernommen((v) => ({ ...v, [type]: ergebnis.value.elementId }));
|
|
11186
|
-
} else if (ergebnis.kind === "rejected") {
|
|
11187
|
-
setMeldung(ergebnis.errors.map((e) => e.message).join("; "));
|
|
11188
11978
|
} else if (ergebnis.kind === "unsupported") {
|
|
11189
11979
|
setMeldung(`Dieser Host bietet \u201E${ergebnis.capability}" nicht an.`);
|
|
11190
11980
|
} else {
|
|
@@ -14601,7 +15391,7 @@ function createHfImageStore(options) {
|
|
|
14601
15391
|
const now = options.now ?? (() => Date.now());
|
|
14602
15392
|
const versuche = options.leseVersuche ?? 4;
|
|
14603
15393
|
const basisMs = options.leseBasisMs ?? 400;
|
|
14604
|
-
const
|
|
15394
|
+
const schlafen2 = options.schlafen ?? warten;
|
|
14605
15395
|
const abgelegt = /* @__PURE__ */ new Map();
|
|
14606
15396
|
const vorschauen = /* @__PURE__ */ new Map();
|
|
14607
15397
|
const bekanntePfade = /* @__PURE__ */ new Set();
|
|
@@ -14698,7 +15488,7 @@ function createHfImageStore(options) {
|
|
|
14698
15488
|
}
|
|
14699
15489
|
const pfadTyp = typ ?? "image/png";
|
|
14700
15490
|
for (let versuch = 0; versuch < versuche; versuch += 1) {
|
|
14701
|
-
if (versuch > 0) await
|
|
15491
|
+
if (versuch > 0) await schlafen2(basisMs * 2 ** (versuch - 1));
|
|
14702
15492
|
const base64 = await hfLoadImageAsBase64(
|
|
14703
15493
|
id,
|
|
14704
15494
|
token,
|
|
@@ -14950,11 +15740,14 @@ function findImageGenerationOrder(events, batchId) {
|
|
|
14950
15740
|
|
|
14951
15741
|
// src/lib/szenen/imageBatchStore.ts
|
|
14952
15742
|
var istText6 = (wert) => typeof wert === "string" && wert.trim().length > 0;
|
|
14953
|
-
function alsBilder(rohe) {
|
|
15743
|
+
function alsBilder(rohe, generationBatchId) {
|
|
14954
15744
|
if (!Array.isArray(rohe)) return [];
|
|
14955
15745
|
return rohe.filter((b) => !!b && typeof b === "object").filter((b) => istText6(b.media_ref) && typeof b.position === "number").map((b) => ({
|
|
14956
15746
|
media_ref: b.media_ref,
|
|
14957
15747
|
position: b.position,
|
|
15748
|
+
// Welcher Provideraufruf dieses Bild geliefert hat (§8) — aus dem
|
|
15749
|
+
// Ereignis, in dem es steht, nicht aus seiner Position abgeleitet.
|
|
15750
|
+
generation_batch_id: generationBatchId,
|
|
14958
15751
|
...istText6(b.asset_id) ? { asset_id: b.asset_id } : {},
|
|
14959
15752
|
// Der bestätigte MIME-Typ (Review 2026-08-18 §5 P2) muss durch das
|
|
14960
15753
|
// Rücklesen hindurch: Genau hier braucht ihn der Lesepfad, um den
|
|
@@ -14967,6 +15760,11 @@ function alsBilder(rohe) {
|
|
|
14967
15760
|
height: b.height ?? null
|
|
14968
15761
|
}));
|
|
14969
15762
|
}
|
|
15763
|
+
function gesamtHerkunft(origins) {
|
|
15764
|
+
const vertreten = new Set(origins.map((o) => o.source_kind));
|
|
15765
|
+
if (vertreten.size === 1) return [...vertreten][0];
|
|
15766
|
+
return "mixed";
|
|
15767
|
+
}
|
|
14970
15768
|
function collectImageBatches(events) {
|
|
14971
15769
|
const byItemId = /* @__PURE__ */ new Map();
|
|
14972
15770
|
for (const event of events) {
|
|
@@ -14976,21 +15774,29 @@ function collectImageBatches(events) {
|
|
|
14976
15774
|
if (payload.source_kind !== "generated_in_app" && payload.source_kind !== "external_import") continue;
|
|
14977
15775
|
const bestehend = byItemId.get(payload.item_id);
|
|
14978
15776
|
if (bestehend?.generation_batch_ids.includes(payload.generation_batch_id)) continue;
|
|
14979
|
-
const bilder = alsBilder(payload.images);
|
|
15777
|
+
const bilder = alsBilder(payload.images, payload.generation_batch_id);
|
|
15778
|
+
const herkunft = {
|
|
15779
|
+
generation_batch_id: payload.generation_batch_id,
|
|
15780
|
+
source_kind: payload.source_kind
|
|
15781
|
+
};
|
|
14980
15782
|
if (!bestehend) {
|
|
14981
15783
|
byItemId.set(payload.item_id, {
|
|
14982
15784
|
item_id: payload.item_id,
|
|
14983
15785
|
prompt_run_id: payload.prompt_run_id,
|
|
14984
15786
|
generation_batch_ids: [payload.generation_batch_id],
|
|
14985
15787
|
source_kind: payload.source_kind,
|
|
15788
|
+
origins: [herkunft],
|
|
14986
15789
|
title: istText6(payload.title) ? payload.title : `Batch aus Lauf ${payload.prompt_run_id}`,
|
|
14987
15790
|
images: bilder
|
|
14988
15791
|
});
|
|
14989
15792
|
continue;
|
|
14990
15793
|
}
|
|
15794
|
+
const origins = [...bestehend.origins, herkunft];
|
|
14991
15795
|
byItemId.set(payload.item_id, {
|
|
14992
15796
|
...bestehend,
|
|
14993
15797
|
generation_batch_ids: [...bestehend.generation_batch_ids, payload.generation_batch_id],
|
|
15798
|
+
origins,
|
|
15799
|
+
source_kind: gesamtHerkunft(origins),
|
|
14994
15800
|
images: [...bestehend.images, ...bilder]
|
|
14995
15801
|
});
|
|
14996
15802
|
}
|
|
@@ -15109,13 +15915,21 @@ async function legeBatchAb(options, input) {
|
|
|
15109
15915
|
`Die Batch-Metadaten fuer ${input.generationBatchId} konnten nicht protokolliert werden, obwohl die Bilder bereits im Bestand liegen: ${error instanceof Error ? error.message : String(error)}`
|
|
15110
15916
|
);
|
|
15111
15917
|
}
|
|
15918
|
+
const origins = [
|
|
15919
|
+
...bestehend?.origins ?? [],
|
|
15920
|
+
{ generation_batch_id: input.generationBatchId, source_kind: input.sourceKind }
|
|
15921
|
+
];
|
|
15112
15922
|
const zusammengefuehrt = {
|
|
15113
15923
|
item_id: itemId,
|
|
15114
15924
|
prompt_run_id: input.runId,
|
|
15115
15925
|
generation_batch_ids: bestehend ? [...bestehend.generation_batch_ids, input.generationBatchId] : [input.generationBatchId],
|
|
15116
|
-
source_kind:
|
|
15926
|
+
source_kind: gesamtHerkunft(origins),
|
|
15927
|
+
origins,
|
|
15117
15928
|
title: titel,
|
|
15118
|
-
images: [
|
|
15929
|
+
images: [
|
|
15930
|
+
...bestehend?.images ?? [],
|
|
15931
|
+
...abgelegt.map((bild) => ({ ...bild, generation_batch_id: input.generationBatchId }))
|
|
15932
|
+
]
|
|
15119
15933
|
};
|
|
15120
15934
|
return { kind: "ok", value: zusammengefuehrt };
|
|
15121
15935
|
}
|
|
@@ -15499,6 +16313,20 @@ function createFlowSzenenViewPort(options) {
|
|
|
15499
16313
|
}
|
|
15500
16314
|
|
|
15501
16315
|
export {
|
|
16316
|
+
hfListDir,
|
|
16317
|
+
hfDownloadBinaryByPath,
|
|
16318
|
+
hfUploadSmallFile,
|
|
16319
|
+
hfBatchArchive,
|
|
16320
|
+
tsFromEventPath,
|
|
16321
|
+
loadPendingEvents,
|
|
16322
|
+
getSessionClientId,
|
|
16323
|
+
writeHFEvent,
|
|
16324
|
+
getHFToken,
|
|
16325
|
+
setHFToken,
|
|
16326
|
+
loadHFState,
|
|
16327
|
+
hfBootstrapFromLegacy,
|
|
16328
|
+
hfUploadImage,
|
|
16329
|
+
hfLoadImageAsBase64,
|
|
15502
16330
|
applyEvents,
|
|
15503
16331
|
capabilitiesOf,
|
|
15504
16332
|
unsupportedReason,
|
|
@@ -15560,6 +16388,15 @@ export {
|
|
|
15560
16388
|
preferredHeadRevisionOf,
|
|
15561
16389
|
collectPromptRuns,
|
|
15562
16390
|
findPromptRun,
|
|
16391
|
+
DEFAULT_SEED_VORLAGE,
|
|
16392
|
+
parseSeedListe,
|
|
16393
|
+
formatSeedListe,
|
|
16394
|
+
ordneSeedsZu,
|
|
16395
|
+
seedFehlerText,
|
|
16396
|
+
mitDefaultVorlage,
|
|
16397
|
+
UPLOAD_MAX_BYTES,
|
|
16398
|
+
istZipDatei,
|
|
16399
|
+
leseBilderAusZip,
|
|
15563
16400
|
ladeVorschauen,
|
|
15564
16401
|
simulatedAnswerFor,
|
|
15565
16402
|
SIMULATION_PROVIDER,
|
|
@@ -15642,6 +16479,7 @@ export {
|
|
|
15642
16479
|
collectImageGenerationOrders,
|
|
15643
16480
|
ordersForPromptRun,
|
|
15644
16481
|
findImageGenerationOrder,
|
|
16482
|
+
gesamtHerkunft,
|
|
15645
16483
|
collectImageBatches,
|
|
15646
16484
|
batchesForPromptRun,
|
|
15647
16485
|
findImageBatch,
|