@rslsp1/fa-app-tools 6.1.0 → 7.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,15 +4,396 @@ import {
4
4
  preferredVersionId,
5
5
  resolvePreferredHead
6
6
  } from "./chunk-FUY2OCVC.mjs";
7
- import {
8
- SPEICHERBARE_BILDTYPEN,
9
- bildEndungFuer,
10
- hfLoadImageAsBase64,
11
- hfUploadImage,
12
- loadHFState,
13
- loadPendingEvents,
14
- writeHFEvent
15
- } from "./chunk-NBUS7AWR.mjs";
7
+
8
+ // src/lib/szenen/szenenSnapshot.ts
9
+ var SZENEN_SNAPSHOT_SCHEMA_VERSION = 1;
10
+ var SZENEN_SNAPSHOT_PREFIX = "szenen-state-";
11
+ var SZENEN_SNAPSHOT_FILE = "szenen-state.json";
12
+ function szenenSnapshotFilename(maxTs) {
13
+ const stamp = new Date(maxTs).toISOString().replace(/:/g, "-").replace(".", "-");
14
+ return `${SZENEN_SNAPSHOT_PREFIX}${stamp}.json`;
15
+ }
16
+ function istSzenenSnapshotDatei(name) {
17
+ return name.startsWith(SZENEN_SNAPSHOT_PREFIX) && name.endsWith(".json");
18
+ }
19
+ var maxTsOf = (events) => {
20
+ let max = 0;
21
+ for (const event of events) if (event.ts > max) max = event.ts;
22
+ return max;
23
+ };
24
+ function buildSzenenSnapshot(base, neue, neuePfade = []) {
25
+ const events = [...base?.events ?? [], ...neue];
26
+ const paths = [...base?.consumed.paths ?? [], ...neuePfade.map(basisname)];
27
+ return {
28
+ schema_version: SZENEN_SNAPSHOT_SCHEMA_VERSION,
29
+ consumed: { count: events.length, max_ts: maxTsOf(events), paths },
30
+ events
31
+ };
32
+ }
33
+ function basisname(pfad) {
34
+ return pfad.split("/").pop() ?? pfad;
35
+ }
36
+ function aufgenommeneDateien(snapshot) {
37
+ return new Set(snapshot ? snapshot.consumed.paths : []);
38
+ }
39
+ function parseSzenenSnapshot(raw, herkunft) {
40
+ if (!raw || typeof raw !== "object") {
41
+ throw new Error(`Szenen-Snapshot ${herkunft}: kein Objekt, sondern ${typeof raw}.`);
42
+ }
43
+ const obj = raw;
44
+ if (obj.schema_version !== SZENEN_SNAPSHOT_SCHEMA_VERSION) {
45
+ throw new Error(
46
+ `Szenen-Snapshot ${herkunft}: schema_version ${String(obj.schema_version)} wird nicht gelesen, erwartet ${SZENEN_SNAPSHOT_SCHEMA_VERSION}.`
47
+ );
48
+ }
49
+ if (!Array.isArray(obj.events)) {
50
+ throw new Error(`Szenen-Snapshot ${herkunft}: "events" fehlt oder ist kein Array.`);
51
+ }
52
+ const events = obj.events;
53
+ for (const [i, event] of events.entries()) {
54
+ if (!event || typeof event.type !== "string" || typeof event.ts !== "number") {
55
+ throw new Error(
56
+ `Szenen-Snapshot ${herkunft}: Ereignis ${i} hat keinen Typ oder keinen Zeitstempel.`
57
+ );
58
+ }
59
+ }
60
+ const consumed = obj.consumed;
61
+ if (!consumed || typeof consumed.count !== "number" || typeof consumed.max_ts !== "number") {
62
+ throw new Error(`Szenen-Snapshot ${herkunft}: "consumed" fehlt oder ist unvollstaendig.`);
63
+ }
64
+ if (!Array.isArray(consumed.paths)) {
65
+ throw new Error(
66
+ `Szenen-Snapshot ${herkunft}: "consumed.paths" fehlt. Ohne die aufgenommenen Dateinamen laesst sich nicht entscheiden, welche Datei in events/ noch aussteht.`
67
+ );
68
+ }
69
+ if (consumed.count !== events.length) {
70
+ throw new Error(
71
+ `Szenen-Snapshot ${herkunft}: consumed.count ${consumed.count} passt nicht zu ${events.length} Ereignis(sen) in der Datei \u2014 die Datei ist unvollstaendig.`
72
+ );
73
+ }
74
+ const echt = maxTsOf(events);
75
+ if (consumed.max_ts !== echt) {
76
+ throw new Error(
77
+ `Szenen-Snapshot ${herkunft}: consumed.max_ts ${consumed.max_ts} passt nicht zum groessten Zeitstempel ${echt} in der Datei.`
78
+ );
79
+ }
80
+ return { schema_version: SZENEN_SNAPSHOT_SCHEMA_VERSION, consumed, events };
81
+ }
82
+ function mergeSnapshotAndPending(snapshot, pending, bereitsImSnapshot = 0) {
83
+ if (!snapshot) return { events: pending, bereitsImSnapshot };
84
+ return { events: [...snapshot.events, ...pending], bereitsImSnapshot };
85
+ }
86
+
87
+ // src/lib/hfEventTypes.ts
88
+ var HF_BASE = "https://huggingface.co";
89
+ var HF_REPO = "RolandSch/fa-app-state";
90
+ var CURRENT_EVENT_VERSION = { major: 1, minor: 0 };
91
+ var SPEICHERBARE_BILDTYPEN = ["image/png", "image/jpeg", "image/webp"];
92
+ function bildEndungFuer(mimeType) {
93
+ if (mimeType === "image/png") return "png";
94
+ if (mimeType === "image/webp") return "webp";
95
+ return "jpg";
96
+ }
97
+
98
+ // src/lib/hfEventLog.ts
99
+ function treeUrl(namespace, subdir = "") {
100
+ const parts = [namespace.replace(/\/$/, ""), subdir].filter(Boolean).join("/");
101
+ return `${HF_BASE}/api/datasets/${HF_REPO}/tree/main${parts ? "/" + parts : ""}`;
102
+ }
103
+ var DROSSEL_VERSUCHE = 4;
104
+ var DROSSEL_BASIS_MS = 400;
105
+ var schlafen = (ms) => new Promise((r) => setTimeout(r, ms));
106
+ var HF_LESE_ZEITLIMIT_MS = 2e4;
107
+ async function holeMitZeitlimit(url, init) {
108
+ const abbruch = new AbortController();
109
+ const timer = setTimeout(() => abbruch.abort(), HF_LESE_ZEITLIMIT_MS);
110
+ try {
111
+ return await fetch(url, { ...init, signal: abbruch.signal });
112
+ } catch (error) {
113
+ if (abbruch.signal.aborted) {
114
+ throw new Error(`HF-Anfrage ohne Antwort nach ${HF_LESE_ZEITLIMIT_MS}ms abgebrochen: ${url}`);
115
+ }
116
+ throw error;
117
+ } finally {
118
+ clearTimeout(timer);
119
+ }
120
+ }
121
+ async function mitGeduld(anfrage) {
122
+ let letzte = null;
123
+ for (let versuch = 0; versuch < DROSSEL_VERSUCHE; versuch += 1) {
124
+ const res = await anfrage();
125
+ if (res.status !== 429 && res.status < 500) return res;
126
+ letzte = res;
127
+ if (versuch === DROSSEL_VERSUCHE - 1) break;
128
+ const angesagt = Number(res.headers.get("retry-after"));
129
+ const warten2 = Number.isFinite(angesagt) && angesagt > 0 ? angesagt * 1e3 : DROSSEL_BASIS_MS * 2 ** versuch;
130
+ await schlafen(warten2);
131
+ }
132
+ return letzte;
133
+ }
134
+ async function hfListDir(namespace, subdir, token) {
135
+ const res = await mitGeduld(() => holeMitZeitlimit(treeUrl(namespace, subdir), {
136
+ headers: { Authorization: `Bearer ${token}` }
137
+ }));
138
+ if (res.status === 404) return [];
139
+ if (!res.ok) throw new Error(`HF list failed: ${res.status} ${res.statusText}`);
140
+ return res.json();
141
+ }
142
+ async function hfDownloadJsonByPath(repoPath, token) {
143
+ const res = await mitGeduld(() => holeMitZeitlimit(
144
+ `${HF_BASE}/datasets/${HF_REPO}/resolve/main/${repoPath}?download=true`,
145
+ { headers: { Authorization: `Bearer ${token}` } }
146
+ ));
147
+ if (!res.ok) throw new Error(`HF download failed: ${res.status}`);
148
+ return res.json();
149
+ }
150
+ async function hfDownloadBinaryByPath(repoPath, token) {
151
+ const res = await mitGeduld(() => holeMitZeitlimit(
152
+ `${HF_BASE}/datasets/${HF_REPO}/resolve/main/${repoPath}?download=true`,
153
+ { headers: { Authorization: `Bearer ${token}` } }
154
+ ));
155
+ if (!res.ok) throw new Error(`HF download binary failed: ${res.status}`);
156
+ return new Uint8Array(await res.arrayBuffer());
157
+ }
158
+ function toBase64(text) {
159
+ const bytes = new TextEncoder().encode(text);
160
+ let binary = "";
161
+ bytes.forEach((b) => binary += String.fromCharCode(b));
162
+ return btoa(binary);
163
+ }
164
+ async function hfUploadSmallFile(repoPath, content, token, summary = `Update ${repoPath}`) {
165
+ const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
166
+ method: "POST",
167
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
168
+ body: [
169
+ JSON.stringify({ key: "header", value: { summary, description: "" } }),
170
+ JSON.stringify({ key: "file", value: { path: repoPath, encoding: "base64", content: toBase64(content) } })
171
+ ].join("\n")
172
+ });
173
+ if (!res.ok) {
174
+ const err = await res.text().catch(() => "");
175
+ throw new Error(`HF upload failed: ${res.status} \u2014 ${err.slice(0, 200)}`);
176
+ }
177
+ }
178
+ async function hfBatchArchive(moves, token, summary) {
179
+ const lines = [
180
+ JSON.stringify({ key: "header", value: { summary, description: "" } }),
181
+ ...moves.flatMap(({ from, to, content }) => [
182
+ JSON.stringify({ key: "file", value: { path: to, encoding: "base64", content: toBase64(content) } }),
183
+ JSON.stringify({ key: "deletedFile", value: { path: from } })
184
+ ])
185
+ ];
186
+ const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
187
+ method: "POST",
188
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
189
+ body: lines.join("\n")
190
+ });
191
+ if (!res.ok) {
192
+ const err = await res.text().catch(() => "");
193
+ throw new Error(`HF batch archive failed: ${res.status} \u2014 ${err.slice(0, 200)}`);
194
+ }
195
+ }
196
+ function tsFromEventPath(repoPath) {
197
+ const filename = repoPath.split("/").pop() || "";
198
+ const base = filename.replace(/_[^_]+\.json$/, "");
199
+ const iso = base.replace(/T(\d{2})-(\d{2})-(\d{2})-(\d{3})Z$/, "T$1:$2:$3.$4Z");
200
+ const ts = Date.parse(iso);
201
+ return isNaN(ts) ? 0 : ts;
202
+ }
203
+ var EVENTDOWNLOAD_PARALLELITAET = 3;
204
+ async function mitBegrenzterParallelitaet(count, limit, task) {
205
+ let cursor = 0;
206
+ let ersterFehler;
207
+ let fehlerAufgetreten = false;
208
+ const worker = async () => {
209
+ while (cursor < count) {
210
+ const index = cursor++;
211
+ try {
212
+ await task(index);
213
+ } catch (error) {
214
+ if (!fehlerAufgetreten) {
215
+ fehlerAufgetreten = true;
216
+ ersterFehler = error;
217
+ }
218
+ }
219
+ }
220
+ };
221
+ await Promise.all(Array.from({ length: Math.min(limit, count) }, worker));
222
+ if (fehlerAufgetreten) throw ersterFehler;
223
+ }
224
+ async function loadPendingEvents(namespace, token, sinceTs, knownPaths) {
225
+ const files = await hfListDir(namespace, "events", token);
226
+ const pending = files.filter((f) => f.type === "file" && tsFromEventPath(f.path) > sinceTs && !knownPaths?.has(f.path)).sort((a, b) => a.path.localeCompare(b.path));
227
+ if (!pending.length) return [];
228
+ const raw = new Array(pending.length);
229
+ await mitBegrenzterParallelitaet(pending.length, EVENTDOWNLOAD_PARALLELITAET, async (index) => {
230
+ raw[index] = await hfDownloadJsonByPath(pending[index].path, token);
231
+ });
232
+ for (const f of pending) knownPaths?.add(f.path);
233
+ return raw.flatMap((e) => Array.isArray(e) ? e : [e]);
234
+ }
235
+ async function findLatestSzenenSnapshotPath(namespace, token) {
236
+ const files = await hfListDir(namespace, "", token);
237
+ const treffer = files.filter((f) => f.type === "file" && istSzenenSnapshotDatei(f.path.split("/").pop() || "")).map((f) => f.path).sort((a, b) => b.localeCompare(a));
238
+ return treffer[0] ?? null;
239
+ }
240
+ async function loadLatestSzenenSnapshot(namespace, token) {
241
+ const pfad = await findLatestSzenenSnapshotPath(namespace, token);
242
+ if (!pfad) return null;
243
+ return parseSzenenSnapshot(await hfDownloadJsonByPath(pfad, token), pfad);
244
+ }
245
+ var SESSION_CLIENT_ID = `client-${crypto.randomUUID().slice(0, 8)}`;
246
+ function getSessionClientId() {
247
+ return SESSION_CLIENT_ID;
248
+ }
249
+ function tsToFilename(ts) {
250
+ return new Date(ts).toISOString().replace(/:/g, "-").replace(".", "-");
251
+ }
252
+ var writeSeq = 0;
253
+ var lastOrderedTs = 0;
254
+ async function writeHFEvent(namespace, token, type, payload, prevTs, options) {
255
+ const wallTs = Date.now();
256
+ const ts = options?.ordered ? Math.max(wallTs, lastOrderedTs) : wallTs;
257
+ if (options?.ordered) lastOrderedTs = ts;
258
+ const uuid = crypto.randomUUID().slice(0, 8);
259
+ const event = {
260
+ v: CURRENT_EVENT_VERSION,
261
+ type,
262
+ ts,
263
+ prevTs,
264
+ clientId: SESSION_CLIENT_ID,
265
+ payload
266
+ };
267
+ const suffix = options?.ordered ? `${String(++writeSeq % 1e6).padStart(6, "0")}${uuid}` : uuid;
268
+ const filename = `${tsToFilename(ts)}_${suffix}.json`;
269
+ const repoPath = `${namespace}events/${filename}`;
270
+ await hfUploadSmallFile(repoPath, JSON.stringify(event, null, 2), token, `Event: ${type}`);
271
+ return event;
272
+ }
273
+
274
+ // src/lib/hfStateService.ts
275
+ import JSZip from "jszip";
276
+ import { uploadFile } from "@huggingface/hub";
277
+ var HF_TOKEN_KEY = "hf-token";
278
+ function getHFToken() {
279
+ try {
280
+ return localStorage.getItem(HF_TOKEN_KEY);
281
+ } catch {
282
+ return null;
283
+ }
284
+ }
285
+ function setHFToken(token) {
286
+ try {
287
+ localStorage.setItem(HF_TOKEN_KEY, token);
288
+ } catch {
289
+ }
290
+ }
291
+ async function loadHFState(namespace, token) {
292
+ const files = await hfListDir(namespace, "", token);
293
+ 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));
294
+ if (!stateFiles.length) return null;
295
+ const zipBytes = await hfDownloadBinaryByPath(stateFiles[0].path, token);
296
+ const zip = await JSZip.loadAsync(zipBytes);
297
+ const [metadataStr, tagsStr, metaStr, openTasksStr] = await Promise.all([
298
+ zip.file("metadata.json")?.async("string"),
299
+ zip.file("tags.json")?.async("string"),
300
+ zip.file("state_meta.json")?.async("string"),
301
+ zip.file("open_tasks.json")?.async("string")
302
+ // optional — ältere Snapshots haben es nicht
303
+ ]);
304
+ if (!metadataStr || !tagsStr || !metaStr) throw new Error("state.zip: fehlende Pflicht-Dateien");
305
+ return {
306
+ metadata: JSON.parse(metadataStr),
307
+ tags: JSON.parse(tagsStr),
308
+ meta: JSON.parse(metaStr),
309
+ openTasks: openTasksStr ? JSON.parse(openTasksStr) : {}
310
+ };
311
+ }
312
+ async function hfBootstrapFromLegacy(namespace, token, onProgress) {
313
+ const log = (msg) => {
314
+ if (onProgress) onProgress(msg);
315
+ };
316
+ log("Lese tags.json \u2026");
317
+ const tags = await fetch(
318
+ `${HF_BASE}/datasets/${HF_REPO}/resolve/main/tags.json?download=true`,
319
+ { headers: { Authorization: `Bearer ${token}` } }
320
+ ).then((r) => r.ok ? r.json() : null).catch(() => null);
321
+ log("Lese metadata.json \u2026");
322
+ const metadata = await fetch(
323
+ `${HF_BASE}/datasets/${HF_REPO}/resolve/main/metadata.json?download=true`,
324
+ { headers: { Authorization: `Bearer ${token}` } }
325
+ ).then((r) => r.ok ? r.json() : []).catch(() => []);
326
+ const snapshot = {
327
+ metadata: Array.isArray(metadata) ? metadata : [],
328
+ tags: tags?.by_category ? tags : { by_category: {}, all: [] },
329
+ meta: { consolidatedAt: Date.now(), version: 1 }
330
+ };
331
+ log(`Erstelle state.zip (${snapshot.metadata.length} Bilder, ${snapshot.tags.all?.length ?? 0} Tags) \u2026`);
332
+ const zip = new JSZip();
333
+ zip.file("metadata.json", JSON.stringify(snapshot.metadata, null, 2));
334
+ zip.file("tags.json", JSON.stringify(snapshot.tags, null, 2));
335
+ zip.file("state_meta.json", JSON.stringify(snapshot.meta, null, 2));
336
+ const zipBytes = await zip.generateAsync({ type: "uint8array", compression: "DEFLATE" });
337
+ const ts = snapshot.meta.consolidatedAt;
338
+ const stateFilename = `state-${new Date(ts).toISOString().replace(/:/g, "-").replace(".", "-")}.zip`;
339
+ const repoPath = `${namespace}${stateFilename}`;
340
+ log("Lade state.zip hoch \u2026");
341
+ await uploadFile({
342
+ repo: { type: "dataset", name: HF_REPO },
343
+ credentials: { accessToken: token },
344
+ file: { path: repoPath, content: new Blob([zipBytes.buffer], { type: "application/zip" }) },
345
+ branch: "main",
346
+ commitTitle: `Bootstrap: initialer State aus Legacy-Daten`
347
+ });
348
+ log(`Fertig \u2014 ${stateFilename}`);
349
+ }
350
+ async function hfUploadImage(base64, id, token, mimeType = "image/jpeg") {
351
+ const ext = bildEndungFuer(mimeType);
352
+ const binary = atob(base64);
353
+ const bytes = new Uint8Array(binary.length);
354
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
355
+ await uploadFile({
356
+ repo: { type: "dataset", name: HF_REPO },
357
+ credentials: { accessToken: token },
358
+ file: { path: `images/${id}.${ext}`, content: new Blob([bytes], { type: mimeType }) },
359
+ branch: "main",
360
+ commitTitle: `Add image ${id}`
361
+ });
362
+ }
363
+ async function hfLoadImageAsBase64(id, token, namespace, filename, on404, mimeType, hasThumb) {
364
+ const fetchBase64 = async (repoPath) => {
365
+ try {
366
+ const res = await fetch(
367
+ `${HF_BASE}/datasets/${HF_REPO}/resolve/main/${repoPath}?download=true`,
368
+ { headers: { Authorization: `Bearer ${token}` } }
369
+ );
370
+ if (!res.ok) {
371
+ if (on404 && on404()) return "ABORT";
372
+ return null;
373
+ }
374
+ const blob = await res.blob();
375
+ return new Promise((resolve, reject) => {
376
+ const reader = new FileReader();
377
+ reader.onload = () => resolve(reader.result.split(",")[1]);
378
+ reader.onerror = reject;
379
+ reader.readAsDataURL(blob);
380
+ });
381
+ } catch {
382
+ return null;
383
+ }
384
+ };
385
+ if (filename) {
386
+ if (hasThumb) {
387
+ const thumb = await fetchBase64(`thumbs/${filename}`);
388
+ if (thumb && thumb !== "ABORT") return thumb;
389
+ }
390
+ const result2 = await fetchBase64(`images/${filename}`);
391
+ return result2 === "ABORT" ? null : result2;
392
+ }
393
+ const ext = bildEndungFuer(mimeType);
394
+ const result = await fetchBase64(`images/${id}.${ext}`);
395
+ return result === "ABORT" ? null : result;
396
+ }
16
397
 
17
398
  // src/lib/szenen/viewPort.ts
18
399
  function capabilitiesOf(port) {
@@ -5318,8 +5699,8 @@ async function leseBilderAusZip(datei) {
5318
5699
  `${name} ist ${mb(datei.size)} MB gro\xDF \u2014 die Grenze liegt bei ${UPLOAD_MAX_BYTES / 1024 / 1024} MB.`
5319
5700
  );
5320
5701
  }
5321
- const { default: JSZip } = await import("jszip");
5322
- const archiv = await JSZip.loadAsync(datei).catch((fehler4) => {
5702
+ const { default: JSZip2 } = await import("jszip");
5703
+ const archiv = await JSZip2.loadAsync(datei).catch((fehler4) => {
5323
5704
  throw new Error(
5324
5705
  `${name} konnte nicht als ZIP gelesen werden: ${fehler4 instanceof Error ? fehler4.message : String(fehler4)}`
5325
5706
  );
@@ -13409,11 +13790,20 @@ function createHfElementStore(options) {
13409
13790
 
13410
13791
  // src/lib/szenen/hfEventChannel.ts
13411
13792
  function createHfEventChannel(options) {
13412
- const { namespace, token, sinceTs = 0 } = options;
13793
+ const { namespace, token, sinceTs = 0, ohneSnapshot = false } = options;
13413
13794
  const knownPaths = /* @__PURE__ */ new Set();
13414
13795
  let cachedRawEvents = [];
13415
13796
  let syncInFlight = null;
13797
+ let snapshot = null;
13798
+ let snapshotGeladen = ohneSnapshot;
13416
13799
  const refresh = async () => {
13800
+ if (!snapshotGeladen) {
13801
+ snapshot = await loadLatestSzenenSnapshot(namespace, token);
13802
+ snapshotGeladen = true;
13803
+ for (const name of aufgenommeneDateien(snapshot)) {
13804
+ knownPaths.add(`${namespace}events/${name}`);
13805
+ }
13806
+ }
13417
13807
  const neue = await loadPendingEvents(namespace, token, sinceTs, knownPaths);
13418
13808
  if (neue.length) cachedRawEvents = [...cachedRawEvents, ...neue];
13419
13809
  };
@@ -13428,12 +13818,13 @@ function createHfEventChannel(options) {
13428
13818
  return {
13429
13819
  async readEvents() {
13430
13820
  await sync();
13431
- return cachedRawEvents.filter((event) => typeof event.type === "string" && event.type.startsWith(SZENEN_EVENT_PREFIX)).map((event) => ({
13821
+ const pending = cachedRawEvents.filter((event) => typeof event.type === "string" && event.type.startsWith(SZENEN_EVENT_PREFIX)).map((event) => ({
13432
13822
  type: event.type,
13433
13823
  payload: event.payload,
13434
13824
  clientId: event.clientId,
13435
13825
  ts: event.ts
13436
13826
  }));
13827
+ return mergeSnapshotAndPending(snapshot, pending).events;
13437
13828
  },
13438
13829
  async appendEvent(type, payload) {
13439
13830
  if (!type.startsWith(SZENEN_EVENT_PREFIX)) {
@@ -15099,7 +15490,7 @@ function createHfImageStore(options) {
15099
15490
  const now = options.now ?? (() => Date.now());
15100
15491
  const versuche = options.leseVersuche ?? 4;
15101
15492
  const basisMs = options.leseBasisMs ?? 400;
15102
- const schlafen = options.schlafen ?? warten;
15493
+ const schlafen2 = options.schlafen ?? warten;
15103
15494
  const abgelegt = /* @__PURE__ */ new Map();
15104
15495
  const vorschauen = /* @__PURE__ */ new Map();
15105
15496
  const bekanntePfade = /* @__PURE__ */ new Set();
@@ -15196,7 +15587,7 @@ function createHfImageStore(options) {
15196
15587
  }
15197
15588
  const pfadTyp = typ ?? "image/png";
15198
15589
  for (let versuch = 0; versuch < versuche; versuch += 1) {
15199
- if (versuch > 0) await schlafen(basisMs * 2 ** (versuch - 1));
15590
+ if (versuch > 0) await schlafen2(basisMs * 2 ** (versuch - 1));
15200
15591
  const base64 = await hfLoadImageAsBase64(
15201
15592
  id,
15202
15593
  token,
@@ -16021,6 +16412,28 @@ function createFlowSzenenViewPort(options) {
16021
16412
  }
16022
16413
 
16023
16414
  export {
16415
+ SZENEN_SNAPSHOT_SCHEMA_VERSION,
16416
+ SZENEN_SNAPSHOT_PREFIX,
16417
+ SZENEN_SNAPSHOT_FILE,
16418
+ szenenSnapshotFilename,
16419
+ istSzenenSnapshotDatei,
16420
+ buildSzenenSnapshot,
16421
+ parseSzenenSnapshot,
16422
+ mergeSnapshotAndPending,
16423
+ hfListDir,
16424
+ hfDownloadBinaryByPath,
16425
+ hfUploadSmallFile,
16426
+ hfBatchArchive,
16427
+ tsFromEventPath,
16428
+ loadPendingEvents,
16429
+ getSessionClientId,
16430
+ writeHFEvent,
16431
+ getHFToken,
16432
+ setHFToken,
16433
+ loadHFState,
16434
+ hfBootstrapFromLegacy,
16435
+ hfUploadImage,
16436
+ hfLoadImageAsBase64,
16024
16437
  applyEvents,
16025
16438
  capabilitiesOf,
16026
16439
  unsupportedReason,
@@ -43,16 +43,6 @@ interface ProjectSettings {
43
43
  seed: number;
44
44
  seedMode: 'random' | 'fixed';
45
45
  }
46
- interface ProjectMeta {
47
- id: string;
48
- name: string;
49
- saved_at: string;
50
- size: number;
51
- }
52
- interface SyncDiff {
53
- localOnly: Generation[];
54
- serverOnly: Generation[];
55
- }
56
46
  interface LabFrame {
57
47
  id: string;
58
48
  base64: string;
@@ -238,4 +228,4 @@ interface TaskRequestedPayload {
238
228
  expiresAt?: number;
239
229
  }
240
230
 
241
- export { type CreateFlowTextGenerationPortOptions as C, type FlowGenerateText as F, type Generation as G, type HFEvent as H, type ImageAddedPayload as I, type LabFrame as L, type MetadataUpdatedPayload as M, type ProjectMeta as P, type SelectedLabImage as S, type TagOption as T, type WorkspaceTags as W, type FlowSdk as a, type FlowTextGenerationOptions as b, type HFEventVersion as c, type HFFileInfo as d, type HFMetadataEntry as e, type HFStateMeta as f, type HFStateSnapshot as g, type LabItem as h, type LabServices as i, type ProjectSettings as j, type SelectedTag as k, type SyncDiff as l, type TagUpsertedPayload as m, buildFallbackPrompt as n, buildGenerationPrompt as o, buildImageGenerationOptions as p, buildPromptTabPayload as q, cleanAiResponse as r, createFlowServices as s, createFlowTextGenerationPort as t, interpretSdkError as u, parsePromptResponse as v };
231
+ export { type CreateFlowTextGenerationPortOptions as C, type FlowGenerateText as F, type Generation as G, type HFEvent as H, type ImageAddedPayload as I, type LabFrame as L, type MetadataUpdatedPayload as M, type ProjectSettings as P, type SelectedLabImage as S, type TagOption as T, type WorkspaceTags as W, type FlowSdk as a, type FlowTextGenerationOptions as b, type HFEventVersion as c, type HFFileInfo as d, type HFMetadataEntry as e, type HFStateMeta as f, type HFStateSnapshot as g, type LabItem as h, type LabServices as i, type SelectedTag as j, type TagUpsertedPayload as k, buildFallbackPrompt as l, buildGenerationPrompt as m, buildImageGenerationOptions as n, buildPromptTabPayload as o, cleanAiResponse as p, createFlowServices as q, createFlowTextGenerationPort as r, interpretSdkError as s, parsePromptResponse as t };
@@ -43,16 +43,6 @@ interface ProjectSettings {
43
43
  seed: number;
44
44
  seedMode: 'random' | 'fixed';
45
45
  }
46
- interface ProjectMeta {
47
- id: string;
48
- name: string;
49
- saved_at: string;
50
- size: number;
51
- }
52
- interface SyncDiff {
53
- localOnly: Generation[];
54
- serverOnly: Generation[];
55
- }
56
46
  interface LabFrame {
57
47
  id: string;
58
48
  base64: string;
@@ -238,4 +228,4 @@ interface TaskRequestedPayload {
238
228
  expiresAt?: number;
239
229
  }
240
230
 
241
- export { type CreateFlowTextGenerationPortOptions as C, type FlowGenerateText as F, type Generation as G, type HFEvent as H, type ImageAddedPayload as I, type LabFrame as L, type MetadataUpdatedPayload as M, type ProjectMeta as P, type SelectedLabImage as S, type TagOption as T, type WorkspaceTags as W, type FlowSdk as a, type FlowTextGenerationOptions as b, type HFEventVersion as c, type HFFileInfo as d, type HFMetadataEntry as e, type HFStateMeta as f, type HFStateSnapshot as g, type LabItem as h, type LabServices as i, type ProjectSettings as j, type SelectedTag as k, type SyncDiff as l, type TagUpsertedPayload as m, buildFallbackPrompt as n, buildGenerationPrompt as o, buildImageGenerationOptions as p, buildPromptTabPayload as q, cleanAiResponse as r, createFlowServices as s, createFlowTextGenerationPort as t, interpretSdkError as u, parsePromptResponse as v };
231
+ export { type CreateFlowTextGenerationPortOptions as C, type FlowGenerateText as F, type Generation as G, type HFEvent as H, type ImageAddedPayload as I, type LabFrame as L, type MetadataUpdatedPayload as M, type ProjectSettings as P, type SelectedLabImage as S, type TagOption as T, type WorkspaceTags as W, type FlowSdk as a, type FlowTextGenerationOptions as b, type HFEventVersion as c, type HFFileInfo as d, type HFMetadataEntry as e, type HFStateMeta as f, type HFStateSnapshot as g, type LabItem as h, type LabServices as i, type SelectedTag as j, type TagUpsertedPayload as k, buildFallbackPrompt as l, buildGenerationPrompt as m, buildImageGenerationOptions as n, buildPromptTabPayload as o, cleanAiResponse as p, createFlowServices as q, createFlowTextGenerationPort as r, interpretSdkError as s, parsePromptResponse as t };