@rslsp1/fa-app-tools 2.0.84 → 2.0.86

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/index.js CHANGED
@@ -275,6 +275,20 @@ function treeUrl(namespace, subdir = "") {
275
275
  const parts = [namespace.replace(/\/$/, ""), subdir].filter(Boolean).join("/");
276
276
  return `${HF_BASE}/api/datasets/${HF_REPO}/tree/main${parts ? "/" + parts : ""}`;
277
277
  }
278
+ async function holeMitZeitlimit(url, init) {
279
+ const abbruch = new AbortController();
280
+ const timer = setTimeout(() => abbruch.abort(), HF_LESE_ZEITLIMIT_MS);
281
+ try {
282
+ return await fetch(url, { ...init, signal: abbruch.signal });
283
+ } catch (error) {
284
+ if (abbruch.signal.aborted) {
285
+ throw new Error(`HF-Anfrage ohne Antwort nach ${HF_LESE_ZEITLIMIT_MS}ms abgebrochen: ${url}`);
286
+ }
287
+ throw error;
288
+ } finally {
289
+ clearTimeout(timer);
290
+ }
291
+ }
278
292
  async function mitGeduld(anfrage) {
279
293
  let letzte = null;
280
294
  for (let versuch = 0; versuch < DROSSEL_VERSUCHE; versuch += 1) {
@@ -283,13 +297,13 @@ async function mitGeduld(anfrage) {
283
297
  letzte = res;
284
298
  if (versuch === DROSSEL_VERSUCHE - 1) break;
285
299
  const angesagt = Number(res.headers.get("retry-after"));
286
- const warten = Number.isFinite(angesagt) && angesagt > 0 ? angesagt * 1e3 : DROSSEL_BASIS_MS * 2 ** versuch;
287
- await schlafen(warten);
300
+ const warten2 = Number.isFinite(angesagt) && angesagt > 0 ? angesagt * 1e3 : DROSSEL_BASIS_MS * 2 ** versuch;
301
+ await schlafen(warten2);
288
302
  }
289
303
  return letzte;
290
304
  }
291
305
  async function hfListDir(namespace, subdir, token) {
292
- const res = await mitGeduld(() => fetch(treeUrl(namespace, subdir), {
306
+ const res = await mitGeduld(() => holeMitZeitlimit(treeUrl(namespace, subdir), {
293
307
  headers: { Authorization: `Bearer ${token}` }
294
308
  }));
295
309
  if (res.status === 404) return [];
@@ -297,7 +311,7 @@ async function hfListDir(namespace, subdir, token) {
297
311
  return res.json();
298
312
  }
299
313
  async function hfDownloadJsonByPath(repoPath, token) {
300
- const res = await mitGeduld(() => fetch(
314
+ const res = await mitGeduld(() => holeMitZeitlimit(
301
315
  `${HF_BASE}/datasets/${HF_REPO}/resolve/main/${repoPath}?download=true`,
302
316
  { headers: { Authorization: `Bearer ${token}` } }
303
317
  ));
@@ -305,7 +319,7 @@ async function hfDownloadJsonByPath(repoPath, token) {
305
319
  return res.json();
306
320
  }
307
321
  async function hfDownloadBinaryByPath(repoPath, token) {
308
- const res = await mitGeduld(() => fetch(
322
+ const res = await mitGeduld(() => holeMitZeitlimit(
309
323
  `${HF_BASE}/datasets/${HF_REPO}/resolve/main/${repoPath}?download=true`,
310
324
  { headers: { Authorization: `Bearer ${token}` } }
311
325
  ));
@@ -417,7 +431,7 @@ async function writeHFEvent(namespace, token, type, payload, prevTs, options) {
417
431
  await hfUploadSmallFile(repoPath, JSON.stringify(event, null, 2), token, `Event: ${type}`);
418
432
  return event;
419
433
  }
420
- var DROSSEL_VERSUCHE, DROSSEL_BASIS_MS, schlafen, EVENTDOWNLOAD_PARALLELITAET, SESSION_CLIENT_ID, writeSeq, lastOrderedTs;
434
+ var DROSSEL_VERSUCHE, DROSSEL_BASIS_MS, schlafen, HF_LESE_ZEITLIMIT_MS, EVENTDOWNLOAD_PARALLELITAET, SESSION_CLIENT_ID, writeSeq, lastOrderedTs;
421
435
  var init_hfEventLog = __esm({
422
436
  "src/lib/hfEventLog.ts"() {
423
437
  "use strict";
@@ -425,6 +439,7 @@ var init_hfEventLog = __esm({
425
439
  DROSSEL_VERSUCHE = 4;
426
440
  DROSSEL_BASIS_MS = 400;
427
441
  schlafen = (ms) => new Promise((r) => setTimeout(r, ms));
442
+ HF_LESE_ZEITLIMIT_MS = 2e4;
428
443
  EVENTDOWNLOAD_PARALLELITAET = 3;
429
444
  SESSION_CLIENT_ID = `client-${crypto.randomUUID().slice(0, 8)}`;
430
445
  writeSeq = 0;
@@ -786,7 +801,7 @@ __export(index_exports, {
786
801
  module.exports = __toCommonJS(index_exports);
787
802
 
788
803
  // package.json
789
- var version = "2.0.84";
804
+ var version = "2.0.86";
790
805
 
791
806
  // src/hooks/useOnClickOutside.ts
792
807
  var import_react = require("react");
@@ -11992,8 +12007,8 @@ var BestandsBild = ({ port, mediaRef }) => {
11992
12007
  };
11993
12008
  var BatchBild = ({ port, bild }) => {
11994
12009
  const [phase] = usePortQuery(
11995
- () => port.imageLibrary ? port.imageLibrary.resolveImage(bild.media_ref) : Promise.resolve({ kind: "ok", value: null }),
11996
- [port, bild.media_ref]
12010
+ () => port.imageLibrary ? port.imageLibrary.resolveImage(bild.media_ref, bild.mime_type ?? null) : Promise.resolve({ kind: "ok", value: null }),
12011
+ [port, bild.media_ref, bild.mime_type]
11997
12012
  );
11998
12013
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("li", { "data-testid": `batchbild-${bild.media_ref}`, children: [
11999
12014
  phase.status === "ready" && phase.value && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
@@ -17423,9 +17438,113 @@ function createClientRunPort(options) {
17423
17438
  };
17424
17439
  }
17425
17440
 
17426
- // src/lib/szenen/hfImageLibrary.ts
17441
+ // src/lib/szenen/hfImageStore.ts
17427
17442
  init_hfStateService();
17428
17443
  init_hfEventLog();
17444
+ var LEERER_BESTAND = {
17445
+ metadata: [],
17446
+ tags: { by_category: {}, all: [] },
17447
+ meta: { consolidatedAt: 0, version: 1 }
17448
+ };
17449
+ function endungFuer(mimeType) {
17450
+ return mimeType === "image/png" ? "png" : "jpg";
17451
+ }
17452
+ function bildPfad(id, mimeType) {
17453
+ return `images/${id}.${endungFuer(mimeType)}`;
17454
+ }
17455
+ function typAusBytes(base64, vorgabe = "image/png") {
17456
+ const kopf = base64.slice(0, 16);
17457
+ if (kopf.startsWith("iVBORw0KGgo")) return "image/png";
17458
+ if (kopf.startsWith("/9j/")) return "image/jpeg";
17459
+ if (kopf.startsWith("UklGR")) return "image/webp";
17460
+ if (kopf.startsWith("R0lGOD")) return "image/gif";
17461
+ return vorgabe;
17462
+ }
17463
+ var warten = (ms) => new Promise((r) => setTimeout(r, ms));
17464
+ function createHfImageStore(options) {
17465
+ const { namespace, token } = options;
17466
+ const now = options.now ?? (() => Date.now());
17467
+ const versuche = options.leseVersuche ?? 4;
17468
+ const basisMs = options.leseBasisMs ?? 400;
17469
+ const schlafen2 = options.schlafen ?? warten;
17470
+ const abgelegt = /* @__PURE__ */ new Map();
17471
+ const bekanntePfade = /* @__PURE__ */ new Set();
17472
+ let gefalteterBestand = null;
17473
+ let laufenderAbgleich = null;
17474
+ async function bestandLesen() {
17475
+ const grundlage = gefalteterBestand ?? await loadHFState(namespace, token) ?? LEERER_BESTAND;
17476
+ const neue = await loadPendingEvents(
17477
+ namespace,
17478
+ token,
17479
+ grundlage.meta.consolidatedAt,
17480
+ bekanntePfade
17481
+ );
17482
+ const danach = neue.filter((e) => e.ts > grundlage.meta.consolidatedAt);
17483
+ gefalteterBestand = danach.length ? applyEvents(grundlage, danach) : grundlage;
17484
+ return gefalteterBestand;
17485
+ }
17486
+ function bestandAbgleichen() {
17487
+ if (!laufenderAbgleich) {
17488
+ laufenderAbgleich = bestandLesen().finally(() => {
17489
+ laufenderAbgleich = null;
17490
+ });
17491
+ }
17492
+ return laufenderAbgleich;
17493
+ }
17494
+ return {
17495
+ bestand: bestandAbgleichen,
17496
+ async putWithId(id, base64, mimeType, titel) {
17497
+ const echterTyp = typAusBytes(base64, mimeType);
17498
+ await hfUploadImage(base64, id, token, echterTyp);
17499
+ await writeHFEvent(namespace, token, "image_added", {
17500
+ id,
17501
+ prompt: titel ?? id,
17502
+ tags: [],
17503
+ timestamp: now(),
17504
+ mimeType: echterTyp
17505
+ }, []);
17506
+ abgelegt.set(id, { data: base64, mimeType: echterTyp });
17507
+ },
17508
+ async resolve(id, mimeType) {
17509
+ const eben = abgelegt.get(id);
17510
+ if (eben) return eben;
17511
+ const zustand = await bestandAbgleichen();
17512
+ const eintrag = zustand.metadata.find((e) => e.id === id);
17513
+ const typ = eintrag?.mimeType || mimeType || null;
17514
+ if (eintrag) {
17515
+ const base64 = await hfLoadImageAsBase64(
17516
+ id,
17517
+ token,
17518
+ void 0,
17519
+ eintrag.filename,
17520
+ void 0,
17521
+ eintrag.mimeType,
17522
+ false
17523
+ );
17524
+ if (base64) return { data: base64, mimeType: eintrag.mimeType || "image/jpeg" };
17525
+ }
17526
+ const pfadTyp = typ ?? "image/png";
17527
+ for (let versuch = 0; versuch < versuche; versuch += 1) {
17528
+ if (versuch > 0) await schlafen2(basisMs * 2 ** (versuch - 1));
17529
+ const base64 = await hfLoadImageAsBase64(
17530
+ id,
17531
+ token,
17532
+ void 0,
17533
+ void 0,
17534
+ void 0,
17535
+ pfadTyp,
17536
+ false
17537
+ );
17538
+ if (base64) return { data: base64, mimeType: pfadTyp };
17539
+ }
17540
+ throw new Error(
17541
+ `Das Bild ${id} ist auf HF nicht auffindbar. Geprueft: ${bildPfad(id, pfadTyp)} in ${versuche} Versuchen` + (eintrag ? " (im Bestand verzeichnet, aber nicht ladbar)." : " (nicht im Bestand verzeichnet).")
17542
+ );
17543
+ }
17544
+ };
17545
+ }
17546
+
17547
+ // src/lib/szenen/hfImageLibrary.ts
17429
17548
  function fehler2(nachricht, retryable = false) {
17430
17549
  return { kind: "error", message: nachricht, retryable };
17431
17550
  }
@@ -17440,14 +17559,16 @@ function alsBild(eintrag) {
17440
17559
  };
17441
17560
  }
17442
17561
  function createHfImageLibrary(options) {
17443
- const { namespace, token } = options;
17444
17562
  const newImageId = options.newImageId ?? (() => crypto.randomUUID());
17445
- const now = options.now ?? (() => Date.now());
17446
- const frisch = /* @__PURE__ */ new Map();
17563
+ const store = options.store ?? createHfImageStore({
17564
+ namespace: options.namespace,
17565
+ token: options.token,
17566
+ ...options.now ? { now: options.now } : {}
17567
+ });
17447
17568
  return {
17448
17569
  async listImages(query) {
17449
17570
  try {
17450
- const zustand = await loadHFState(namespace, token);
17571
+ const zustand = await store.bestand();
17451
17572
  const eintraege = zustand?.metadata ?? [];
17452
17573
  const gesucht = query?.search?.trim().toLowerCase();
17453
17574
  const gefiltert = gesucht ? eintraege.filter((e) => (e.prompt ?? "").toLowerCase().includes(gesucht) || e.id.toLowerCase().includes(gesucht)) : eintraege;
@@ -17461,50 +17582,18 @@ function createHfImageLibrary(options) {
17461
17582
  );
17462
17583
  }
17463
17584
  },
17464
- async resolveImage(mediaRef) {
17465
- const eben = frisch.get(mediaRef);
17466
- if (eben) return { kind: "ok", value: { data: eben.data, mime_type: eben.mimeType } };
17585
+ async resolveImage(mediaRef, mimeTypeHinweis) {
17467
17586
  try {
17468
- const zustand = await loadHFState(namespace, token);
17469
- const eintrag = (zustand?.metadata ?? []).find((e) => e.id === mediaRef);
17470
- if (!eintrag) {
17471
- return fehler2(
17472
- `Das Bild ${mediaRef} steht nicht im Bestand. Moeglicherweise hat der Abgleich es noch nicht uebertragen.`,
17473
- true
17474
- );
17475
- }
17476
- const base64 = await hfLoadImageAsBase64(
17477
- mediaRef,
17478
- token,
17479
- void 0,
17480
- eintrag.filename,
17481
- void 0,
17482
- eintrag.mimeType,
17483
- false
17484
- );
17485
- if (!base64) {
17486
- return fehler2(`Das Bild ${mediaRef} ist im Bestand verzeichnet, aber nicht ladbar.`, true);
17487
- }
17488
- return { kind: "ok", value: { data: base64, mime_type: eintrag.mimeType || "image/jpeg" } };
17587
+ const bild = await store.resolve(mediaRef, mimeTypeHinweis);
17588
+ return { kind: "ok", value: { data: bild.data, mime_type: bild.mimeType } };
17489
17589
  } catch (error) {
17490
- return fehler2(
17491
- `Das Bild ${mediaRef} konnte nicht geladen werden: ${error instanceof Error ? error.message : String(error)}`,
17492
- true
17493
- );
17590
+ return fehler2(error instanceof Error ? error.message : String(error), true);
17494
17591
  }
17495
17592
  },
17496
17593
  async putImage(input) {
17497
17594
  const id = newImageId();
17498
17595
  try {
17499
- await hfUploadImage(input.data, id, token, input.mime_type);
17500
- await writeHFEvent(namespace, token, "image_added", {
17501
- id,
17502
- prompt: input.title,
17503
- tags: [],
17504
- timestamp: now(),
17505
- mimeType: input.mime_type
17506
- }, []);
17507
- frisch.set(id, { mimeType: input.mime_type, data: input.data });
17596
+ await store.putWithId(id, input.data, input.mime_type, input.title);
17508
17597
  return {
17509
17598
  kind: "ok",
17510
17599
  value: { mediaRef: id, title: input.title, mimeType: input.mime_type, thumbUrl: null }
@@ -17631,6 +17720,11 @@ function alsBilder(rohe) {
17631
17720
  media_ref: b.media_ref,
17632
17721
  position: b.position,
17633
17722
  ...istText3(b.asset_id) ? { asset_id: b.asset_id } : {},
17723
+ // Der bestätigte MIME-Typ (Review 2026-08-18 §5 P2) muss durch das
17724
+ // Rücklesen hindurch: Genau hier braucht ihn der Lesepfad, um den
17725
+ // Dateipfad `images/<id>.<ext>` zu bilden, statt ihn zu erraten. Nur
17726
+ // gesetzt, wenn er im Ereignis steht — ältere Batches tragen ihn nicht.
17727
+ ...istText3(b.mime_type) ? { mime_type: b.mime_type } : {},
17634
17728
  model: b.model ?? null,
17635
17729
  seed: b.seed ?? null,
17636
17730
  width: b.width ?? null,
@@ -17712,8 +17806,6 @@ function createHfImageBatchHistory(options) {
17712
17806
  }
17713
17807
 
17714
17808
  // src/lib/szenen/hfImageBatchPersistence.ts
17715
- init_hfStateService();
17716
- init_hfEventLog();
17717
17809
  function fehler3(nachricht, retryable = true) {
17718
17810
  return { kind: "error", message: nachricht, retryable };
17719
17811
  }
@@ -17721,6 +17813,7 @@ async function legeBatchAb(options, input) {
17721
17813
  const { namespace, token, channel } = options;
17722
17814
  const newImageId = options.newImageId ?? ((batchId, position) => `${batchId}-${position}`);
17723
17815
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
17816
+ const store = options.store ?? createHfImageStore({ namespace, token });
17724
17817
  const itemId = itemIdForRun(input.runId);
17725
17818
  let vorhandeneEvents;
17726
17819
  try {
@@ -17739,15 +17832,14 @@ async function legeBatchAb(options, input) {
17739
17832
  for (let offset = 0; offset < input.images.length; offset++) {
17740
17833
  const bild = input.images[offset];
17741
17834
  const id = newImageId(input.generationBatchId, startPosition + offset);
17835
+ const echterTyp = typAusBytes(bild.imageBase64, bild.mimeType);
17742
17836
  try {
17743
- await hfUploadImage(bild.imageBase64, id, token, bild.mimeType);
17744
- await writeHFEvent(namespace, token, "image_added", {
17837
+ await store.putWithId(
17745
17838
  id,
17746
- prompt: input.title ?? `Batch ${input.generationBatchId}`,
17747
- tags: [],
17748
- timestamp: now().getTime(),
17749
- mimeType: bild.mimeType
17750
- }, []);
17839
+ bild.imageBase64,
17840
+ echterTyp,
17841
+ input.title ?? `Batch ${input.generationBatchId}`
17842
+ );
17751
17843
  } catch (error) {
17752
17844
  return fehler3(
17753
17845
  `Bild ${offset + 1}/${input.images.length} des Batches ${input.generationBatchId} konnte nicht im Bildbestand abgelegt werden: ${error instanceof Error ? error.message : String(error)}. Bereits abgelegte Bilder dieses Versuchs (${abgelegt.length}) bleiben im Bestand, geh\xF6ren aber zu keinem Batch, solange das Batch-Ereignis nicht geschrieben ist.`
@@ -17757,6 +17849,7 @@ async function legeBatchAb(options, input) {
17757
17849
  position: startPosition + offset,
17758
17850
  media_ref: id,
17759
17851
  asset_id: id,
17852
+ mime_type: echterTyp,
17760
17853
  model: bild.model ?? null,
17761
17854
  seed: bild.seed ?? null,
17762
17855
  width: bild.width ?? null,
@@ -17800,8 +17893,11 @@ function createHfImageGenerationBatchPersistence(options) {
17800
17893
  images: input.images.map((bild) => ({
17801
17894
  imageBase64: bild.imageBase64,
17802
17895
  // Flow liefert PNG-Base64 (bestehende Konvention, s. AvatarArchitectApp.tsx
17803
- // `data:image/png;base64,${base64}`); der gemeinsame Vertrag führt
17804
- // keinen MIME-Typ je Bild.
17896
+ // `data:image/png;base64,${base64}`), und der gemeinsame Vertrag führt
17897
+ // keinen MIME-Typ je Bild. Das ist deshalb nur die **Vorgabe**:
17898
+ // Entschieden wird nach den echten Bytes (Review §5 P2) — ein künftig
17899
+ // anders kodiertes Bild läge sonst unter einer Endung, unter der es
17900
+ // niemand sucht.
17805
17901
  mimeType: "image/png",
17806
17902
  model: bild.model ?? null,
17807
17903
  seed: bild.seed ?? null,
@@ -17822,6 +17918,8 @@ function createHfImageBatchWrite(options) {
17822
17918
  title: input.title,
17823
17919
  images: input.images.map((bild) => ({
17824
17920
  imageBase64: bild.imageBase64,
17921
+ // Vorgabe, kein Urteil — der Import bringt beliebig kodierte Bilder
17922
+ // mit, und der Typ wird aus den Bytes bestimmt (Review §5 P2).
17825
17923
  mimeType: "image/png",
17826
17924
  model: bild.model ?? null,
17827
17925
  seed: bild.seed ?? null,
@@ -18110,7 +18208,17 @@ function createFlowSzenenViewPort(options) {
18110
18208
  ...options.newRunId ? { newRunId: options.newRunId } : {},
18111
18209
  ...options.now ? { now: options.now } : {}
18112
18210
  });
18113
- const imageLibrary = options.imageLibrary ?? (options.namespace?.trim() && options.token?.trim() ? createHfImageLibrary({ namespace: options.namespace, token: options.token }) : void 0);
18211
+ const hatHfZugangsdaten = Boolean(options.namespace?.trim() && options.token?.trim());
18212
+ const bildSpeicher = hatHfZugangsdaten ? createHfImageStore({
18213
+ namespace: options.namespace,
18214
+ token: options.token,
18215
+ ...options.now ? { now: () => options.now().getTime() } : {}
18216
+ }) : void 0;
18217
+ const imageLibrary = options.imageLibrary ?? (bildSpeicher ? createHfImageLibrary({
18218
+ namespace: options.namespace,
18219
+ token: options.token,
18220
+ store: bildSpeicher
18221
+ }) : void 0);
18114
18222
  const derivation = createClientDerivationPort({
18115
18223
  store: elements,
18116
18224
  derivations: createEventLogDerivationPersistence(channel),
@@ -18120,11 +18228,11 @@ function createFlowSzenenViewPort(options) {
18120
18228
  ...options.now ? { now: options.now } : {}
18121
18229
  });
18122
18230
  const imageBatchHistory = createHfImageBatchHistory({ channel });
18123
- const hatHfZugangsdaten = Boolean(options.namespace?.trim() && options.token?.trim());
18124
- const imageBatchWrite = hatHfZugangsdaten ? createHfImageBatchWrite({
18231
+ const imageBatchWrite = bildSpeicher ? createHfImageBatchWrite({
18125
18232
  namespace: options.namespace,
18126
18233
  token: options.token,
18127
18234
  channel,
18235
+ store: bildSpeicher,
18128
18236
  ...options.newBatchImageId ? { newImageId: options.newBatchImageId } : {},
18129
18237
  ...options.now ? { now: options.now } : {}
18130
18238
  }) : void 0;
@@ -18138,6 +18246,9 @@ function createFlowSzenenViewPort(options) {
18138
18246
  namespace: options.namespace,
18139
18247
  token: options.token,
18140
18248
  channel,
18249
+ // Dieselbe Instanz wie der Bildbestand — sonst wäre das gerade
18250
+ // erzeugte Bild in der Laufansicht wieder unauffindbar (R1).
18251
+ store: bildSpeicher,
18141
18252
  ...options.newBatchImageId ? { newImageId: options.newBatchImageId } : {},
18142
18253
  ...options.now ? { now: options.now } : {}
18143
18254
  }),
package/dist/index.mjs CHANGED
@@ -1,7 +1,8 @@
1
1
  import {
2
2
  SzenenView,
3
+ applyEvents,
3
4
  createFlowSzenenViewPort
4
- } from "./chunk-4FOEFCKM.mjs";
5
+ } from "./chunk-DGFMONWO.mjs";
5
6
  import "./chunk-FUY2OCVC.mjs";
6
7
  import {
7
8
  exportProjectToZip,
@@ -27,10 +28,10 @@ import {
27
28
  setHFToken,
28
29
  tsFromEventPath,
29
30
  writeHFEvent
30
- } from "./chunk-VBWQP7PO.mjs";
31
+ } from "./chunk-LFPOXNOZ.mjs";
31
32
 
32
33
  // package.json
33
- var version = "2.0.84";
34
+ var version = "2.0.86";
34
35
 
35
36
  // src/hooks/useOnClickOutside.ts
36
37
  import { useEffect } from "react";
@@ -2308,7 +2309,7 @@ var ProjectSyncTab = ({
2308
2309
  {
2309
2310
  onClick: async () => {
2310
2311
  try {
2311
- const { hfDownloadProject: hfDownloadProject2 } = await import("./hfStateService-WKQLNDHO.mjs");
2312
+ const { hfDownloadProject: hfDownloadProject2 } = await import("./hfStateService-PZH2IDKF.mjs");
2312
2313
  const file = await hfDownloadProject2(p.path, hfToken);
2313
2314
  onHfLoad(file);
2314
2315
  } catch (e) {
@@ -2532,75 +2533,6 @@ function toPromptImages(images) {
2532
2533
  // src/hooks/useHFState.ts
2533
2534
  import { useState as useState11, useEffect as useEffect7, useRef as useRef8, useCallback } from "react";
2534
2535
 
2535
- // src/lib/hfReducer.ts
2536
- function applyEvent(state, event) {
2537
- if (event.v.major > 1) return state;
2538
- switch (event.type) {
2539
- // SPEZ: image_added (state/spez/hf-state.spez.yaml)
2540
- case "image_added": {
2541
- const p = event.payload;
2542
- if (state.metadata.some((m) => m.id === p.id)) return state;
2543
- return { ...state, metadata: [...state.metadata, p] };
2544
- }
2545
- // SPEZ: task_requested (global/spez/task-queue.spez.yaml)
2546
- case "task_requested": {
2547
- const p = event.payload;
2548
- if (p.expiresAt && p.expiresAt < event.ts) return state;
2549
- const openTasks = { ...state.openTasks || {} };
2550
- openTasks[p.taskId] = p;
2551
- return { ...state, openTasks };
2552
- }
2553
- // SPEZ: task_completed (global/spez/task-queue.spez.yaml)
2554
- case "task_completed": {
2555
- const p = event.payload;
2556
- if (!state.openTasks || !state.openTasks[p.taskId]) return state;
2557
- const openTasks = { ...state.openTasks };
2558
- delete openTasks[p.taskId];
2559
- return { ...state, openTasks };
2560
- }
2561
- // SPEZ: tag_upserted (state/spez/hf-state.spez.yaml) — Legacy, wird nicht mehr erzeugt
2562
- case "tag_upserted": {
2563
- const p = event.payload;
2564
- const tags = state.tags;
2565
- const cat = tags.by_category[p.category] || [];
2566
- const existing = cat.find((t) => t.value === p.value);
2567
- const updated = { label: p.label, value: p.value, is_user_created: p.is_user_created, is_deleted: p.is_deleted };
2568
- const newCat = existing ? cat.map((t) => t.value === p.value ? { ...t, ...updated } : t) : [...cat, updated];
2569
- const newAll = tags.all.some((t) => t.value === p.value && t.category === p.category) ? tags.all.map((t) => t.value === p.value && t.category === p.category ? { ...t, ...updated, category: p.category } : t) : [...tags.all, { ...updated, category: p.category }];
2570
- return { ...state, tags: { by_category: { ...tags.by_category, [p.category]: newCat }, all: newAll } };
2571
- }
2572
- // SPEZ: thumb_added (state/spez/hf-state.spez.yaml)
2573
- case "thumb_added": {
2574
- const p = event.payload;
2575
- return {
2576
- ...state,
2577
- metadata: state.metadata.map((m) => m.id === p.id ? { ...m, hasThumb: true } : m)
2578
- };
2579
- }
2580
- // SPEZ: metadata_updated (state/spez/hf-state.spez.yaml)
2581
- case "metadata_updated": {
2582
- const p = event.payload;
2583
- return {
2584
- ...state,
2585
- metadata: state.metadata.map((m) => m.id === p.id ? { ...m, ...p.delta } : m)
2586
- };
2587
- }
2588
- // SPEZ: title_set (state/spez/hf-state.spez.yaml)
2589
- case "title_set": {
2590
- const p = event.payload;
2591
- return {
2592
- ...state,
2593
- metadata: state.metadata.map((m) => m.id === p.id ? { ...m, titleTs: event.ts } : m)
2594
- };
2595
- }
2596
- default:
2597
- return state;
2598
- }
2599
- }
2600
- function applyEvents(state, events) {
2601
- return events.reduce(applyEvent, state);
2602
- }
2603
-
2604
2536
  // src/lib/hfDag.ts
2605
2537
  function buildDag(events) {
2606
2538
  const dag = /* @__PURE__ */ new Map();
@@ -6856,7 +6788,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
6856
6788
  onClick: async () => {
6857
6789
  setIsLoadingFromHF(true);
6858
6790
  try {
6859
- const { hfListProjects: hfListProjects2, hfDownloadProject: hfDownloadProject2 } = await import("./hfStateService-WKQLNDHO.mjs");
6791
+ const { hfListProjects: hfListProjects2, hfDownloadProject: hfDownloadProject2 } = await import("./hfStateService-PZH2IDKF.mjs");
6860
6792
  const projects = await hfListProjects2(hfToken);
6861
6793
  if (projects.length > 0) {
6862
6794
  const file = await hfDownloadProject2(projects[0].path, hfToken);