@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/szenen.js CHANGED
@@ -59,6 +59,7 @@ __export(szenen_exports, {
59
59
  areasFor: () => areasFor,
60
60
  assessDerivationResponse: () => assessDerivationResponse,
61
61
  batchesForPromptRun: () => batchesForPromptRun,
62
+ bildPfad: () => bildPfad,
62
63
  buildDerivationSystemInstruction: () => buildSystemInstruction2,
63
64
  buildDerivationUserPrompt: () => buildUserPrompt2,
64
65
  buildSystemInstruction: () => buildSystemInstruction,
@@ -114,6 +115,7 @@ __export(szenen_exports, {
114
115
  createHfImageBatchWrite: () => createHfImageBatchWrite,
115
116
  createHfImageGenerationBatchPersistence: () => createHfImageGenerationBatchPersistence,
116
117
  createHfImageLibrary: () => createHfImageLibrary,
118
+ createHfImageStore: () => createHfImageStore,
117
119
  createImageGenerationPort: () => createImageGenerationPort,
118
120
  createInMemoryEventChannel: () => createInMemoryEventChannel,
119
121
  createMemoryViewPort: () => createMemoryViewPort,
@@ -172,6 +174,7 @@ __export(szenen_exports, {
172
174
  showChain: () => showChain,
173
175
  simulatedAnswerFor: () => simulatedAnswerFor,
174
176
  totalModelCalls: () => totalModelCalls,
177
+ typAusBytes: () => typAusBytes,
175
178
  unclassifiedPublishRules: () => unclassifiedPublishRules,
176
179
  unsupportedReason: () => unsupportedReason,
177
180
  validateAgainst: () => validateAgainst,
@@ -7572,6 +7575,21 @@ function treeUrl(namespace, subdir = "") {
7572
7575
  var DROSSEL_VERSUCHE = 4;
7573
7576
  var DROSSEL_BASIS_MS = 400;
7574
7577
  var schlafen = (ms) => new Promise((r) => setTimeout(r, ms));
7578
+ var HF_LESE_ZEITLIMIT_MS = 2e4;
7579
+ async function holeMitZeitlimit(url, init) {
7580
+ const abbruch = new AbortController();
7581
+ const timer = setTimeout(() => abbruch.abort(), HF_LESE_ZEITLIMIT_MS);
7582
+ try {
7583
+ return await fetch(url, { ...init, signal: abbruch.signal });
7584
+ } catch (error) {
7585
+ if (abbruch.signal.aborted) {
7586
+ throw new Error(`HF-Anfrage ohne Antwort nach ${HF_LESE_ZEITLIMIT_MS}ms abgebrochen: ${url}`);
7587
+ }
7588
+ throw error;
7589
+ } finally {
7590
+ clearTimeout(timer);
7591
+ }
7592
+ }
7575
7593
  async function mitGeduld(anfrage) {
7576
7594
  let letzte = null;
7577
7595
  for (let versuch = 0; versuch < DROSSEL_VERSUCHE; versuch += 1) {
@@ -7580,13 +7598,13 @@ async function mitGeduld(anfrage) {
7580
7598
  letzte = res;
7581
7599
  if (versuch === DROSSEL_VERSUCHE - 1) break;
7582
7600
  const angesagt = Number(res.headers.get("retry-after"));
7583
- const warten = Number.isFinite(angesagt) && angesagt > 0 ? angesagt * 1e3 : DROSSEL_BASIS_MS * 2 ** versuch;
7584
- await schlafen(warten);
7601
+ const warten2 = Number.isFinite(angesagt) && angesagt > 0 ? angesagt * 1e3 : DROSSEL_BASIS_MS * 2 ** versuch;
7602
+ await schlafen(warten2);
7585
7603
  }
7586
7604
  return letzte;
7587
7605
  }
7588
7606
  async function hfListDir(namespace, subdir, token) {
7589
- const res = await mitGeduld(() => fetch(treeUrl(namespace, subdir), {
7607
+ const res = await mitGeduld(() => holeMitZeitlimit(treeUrl(namespace, subdir), {
7590
7608
  headers: { Authorization: `Bearer ${token}` }
7591
7609
  }));
7592
7610
  if (res.status === 404) return [];
@@ -7594,7 +7612,7 @@ async function hfListDir(namespace, subdir, token) {
7594
7612
  return res.json();
7595
7613
  }
7596
7614
  async function hfDownloadJsonByPath(repoPath, token) {
7597
- const res = await mitGeduld(() => fetch(
7615
+ const res = await mitGeduld(() => holeMitZeitlimit(
7598
7616
  `${HF_BASE}/datasets/${HF_REPO}/resolve/main/${repoPath}?download=true`,
7599
7617
  { headers: { Authorization: `Bearer ${token}` } }
7600
7618
  ));
@@ -7602,7 +7620,7 @@ async function hfDownloadJsonByPath(repoPath, token) {
7602
7620
  return res.json();
7603
7621
  }
7604
7622
  async function hfDownloadBinaryByPath(repoPath, token) {
7605
- const res = await mitGeduld(() => fetch(
7623
+ const res = await mitGeduld(() => holeMitZeitlimit(
7606
7624
  `${HF_BASE}/datasets/${HF_REPO}/resolve/main/${repoPath}?download=true`,
7607
7625
  { headers: { Authorization: `Bearer ${token}` } }
7608
7626
  ));
@@ -9337,6 +9355,179 @@ async function hfLoadImageAsBase64(id, token, namespace, filename, on404, mimeTy
9337
9355
  return result === "ABORT" ? null : result;
9338
9356
  }
9339
9357
 
9358
+ // src/lib/hfReducer.ts
9359
+ function applyEvent(state, event) {
9360
+ if (event.v.major > 1) return state;
9361
+ switch (event.type) {
9362
+ // SPEZ: image_added (state/spez/hf-state.spez.yaml)
9363
+ case "image_added": {
9364
+ const p = event.payload;
9365
+ if (state.metadata.some((m) => m.id === p.id)) return state;
9366
+ return { ...state, metadata: [...state.metadata, p] };
9367
+ }
9368
+ // SPEZ: task_requested (global/spez/task-queue.spez.yaml)
9369
+ case "task_requested": {
9370
+ const p = event.payload;
9371
+ if (p.expiresAt && p.expiresAt < event.ts) return state;
9372
+ const openTasks = { ...state.openTasks || {} };
9373
+ openTasks[p.taskId] = p;
9374
+ return { ...state, openTasks };
9375
+ }
9376
+ // SPEZ: task_completed (global/spez/task-queue.spez.yaml)
9377
+ case "task_completed": {
9378
+ const p = event.payload;
9379
+ if (!state.openTasks || !state.openTasks[p.taskId]) return state;
9380
+ const openTasks = { ...state.openTasks };
9381
+ delete openTasks[p.taskId];
9382
+ return { ...state, openTasks };
9383
+ }
9384
+ // SPEZ: tag_upserted (state/spez/hf-state.spez.yaml) — Legacy, wird nicht mehr erzeugt
9385
+ case "tag_upserted": {
9386
+ const p = event.payload;
9387
+ const tags = state.tags;
9388
+ const cat = tags.by_category[p.category] || [];
9389
+ const existing = cat.find((t) => t.value === p.value);
9390
+ const updated = { label: p.label, value: p.value, is_user_created: p.is_user_created, is_deleted: p.is_deleted };
9391
+ const newCat = existing ? cat.map((t) => t.value === p.value ? { ...t, ...updated } : t) : [...cat, updated];
9392
+ 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 }];
9393
+ return { ...state, tags: { by_category: { ...tags.by_category, [p.category]: newCat }, all: newAll } };
9394
+ }
9395
+ // SPEZ: thumb_added (state/spez/hf-state.spez.yaml)
9396
+ case "thumb_added": {
9397
+ const p = event.payload;
9398
+ return {
9399
+ ...state,
9400
+ metadata: state.metadata.map((m) => m.id === p.id ? { ...m, hasThumb: true } : m)
9401
+ };
9402
+ }
9403
+ // SPEZ: metadata_updated (state/spez/hf-state.spez.yaml)
9404
+ case "metadata_updated": {
9405
+ const p = event.payload;
9406
+ return {
9407
+ ...state,
9408
+ metadata: state.metadata.map((m) => m.id === p.id ? { ...m, ...p.delta } : m)
9409
+ };
9410
+ }
9411
+ // SPEZ: title_set (state/spez/hf-state.spez.yaml)
9412
+ case "title_set": {
9413
+ const p = event.payload;
9414
+ return {
9415
+ ...state,
9416
+ metadata: state.metadata.map((m) => m.id === p.id ? { ...m, titleTs: event.ts } : m)
9417
+ };
9418
+ }
9419
+ default:
9420
+ return state;
9421
+ }
9422
+ }
9423
+ function applyEvents(state, events) {
9424
+ return events.reduce(applyEvent, state);
9425
+ }
9426
+
9427
+ // src/lib/szenen/hfImageStore.ts
9428
+ var LEERER_BESTAND = {
9429
+ metadata: [],
9430
+ tags: { by_category: {}, all: [] },
9431
+ meta: { consolidatedAt: 0, version: 1 }
9432
+ };
9433
+ function endungFuer(mimeType) {
9434
+ return mimeType === "image/png" ? "png" : "jpg";
9435
+ }
9436
+ function bildPfad(id, mimeType) {
9437
+ return `images/${id}.${endungFuer(mimeType)}`;
9438
+ }
9439
+ function typAusBytes(base64, vorgabe = "image/png") {
9440
+ const kopf = base64.slice(0, 16);
9441
+ if (kopf.startsWith("iVBORw0KGgo")) return "image/png";
9442
+ if (kopf.startsWith("/9j/")) return "image/jpeg";
9443
+ if (kopf.startsWith("UklGR")) return "image/webp";
9444
+ if (kopf.startsWith("R0lGOD")) return "image/gif";
9445
+ return vorgabe;
9446
+ }
9447
+ var warten = (ms) => new Promise((r) => setTimeout(r, ms));
9448
+ function createHfImageStore(options) {
9449
+ const { namespace, token } = options;
9450
+ const now = options.now ?? (() => Date.now());
9451
+ const versuche = options.leseVersuche ?? 4;
9452
+ const basisMs = options.leseBasisMs ?? 400;
9453
+ const schlafen2 = options.schlafen ?? warten;
9454
+ const abgelegt = /* @__PURE__ */ new Map();
9455
+ const bekanntePfade = /* @__PURE__ */ new Set();
9456
+ let gefalteterBestand = null;
9457
+ let laufenderAbgleich = null;
9458
+ async function bestandLesen() {
9459
+ const grundlage = gefalteterBestand ?? await loadHFState(namespace, token) ?? LEERER_BESTAND;
9460
+ const neue = await loadPendingEvents(
9461
+ namespace,
9462
+ token,
9463
+ grundlage.meta.consolidatedAt,
9464
+ bekanntePfade
9465
+ );
9466
+ const danach = neue.filter((e) => e.ts > grundlage.meta.consolidatedAt);
9467
+ gefalteterBestand = danach.length ? applyEvents(grundlage, danach) : grundlage;
9468
+ return gefalteterBestand;
9469
+ }
9470
+ function bestandAbgleichen() {
9471
+ if (!laufenderAbgleich) {
9472
+ laufenderAbgleich = bestandLesen().finally(() => {
9473
+ laufenderAbgleich = null;
9474
+ });
9475
+ }
9476
+ return laufenderAbgleich;
9477
+ }
9478
+ return {
9479
+ bestand: bestandAbgleichen,
9480
+ async putWithId(id, base64, mimeType, titel) {
9481
+ const echterTyp = typAusBytes(base64, mimeType);
9482
+ await hfUploadImage(base64, id, token, echterTyp);
9483
+ await writeHFEvent(namespace, token, "image_added", {
9484
+ id,
9485
+ prompt: titel ?? id,
9486
+ tags: [],
9487
+ timestamp: now(),
9488
+ mimeType: echterTyp
9489
+ }, []);
9490
+ abgelegt.set(id, { data: base64, mimeType: echterTyp });
9491
+ },
9492
+ async resolve(id, mimeType) {
9493
+ const eben = abgelegt.get(id);
9494
+ if (eben) return eben;
9495
+ const zustand = await bestandAbgleichen();
9496
+ const eintrag = zustand.metadata.find((e) => e.id === id);
9497
+ const typ = eintrag?.mimeType || mimeType || null;
9498
+ if (eintrag) {
9499
+ const base64 = await hfLoadImageAsBase64(
9500
+ id,
9501
+ token,
9502
+ void 0,
9503
+ eintrag.filename,
9504
+ void 0,
9505
+ eintrag.mimeType,
9506
+ false
9507
+ );
9508
+ if (base64) return { data: base64, mimeType: eintrag.mimeType || "image/jpeg" };
9509
+ }
9510
+ const pfadTyp = typ ?? "image/png";
9511
+ for (let versuch = 0; versuch < versuche; versuch += 1) {
9512
+ if (versuch > 0) await schlafen2(basisMs * 2 ** (versuch - 1));
9513
+ const base64 = await hfLoadImageAsBase64(
9514
+ id,
9515
+ token,
9516
+ void 0,
9517
+ void 0,
9518
+ void 0,
9519
+ pfadTyp,
9520
+ false
9521
+ );
9522
+ if (base64) return { data: base64, mimeType: pfadTyp };
9523
+ }
9524
+ throw new Error(
9525
+ `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).")
9526
+ );
9527
+ }
9528
+ };
9529
+ }
9530
+
9340
9531
  // src/lib/szenen/hfImageLibrary.ts
9341
9532
  function fehler(nachricht, retryable = false) {
9342
9533
  return { kind: "error", message: nachricht, retryable };
@@ -9352,14 +9543,16 @@ function alsBild(eintrag) {
9352
9543
  };
9353
9544
  }
9354
9545
  function createHfImageLibrary(options) {
9355
- const { namespace, token } = options;
9356
9546
  const newImageId = options.newImageId ?? (() => crypto.randomUUID());
9357
- const now = options.now ?? (() => Date.now());
9358
- const frisch = /* @__PURE__ */ new Map();
9547
+ const store = options.store ?? createHfImageStore({
9548
+ namespace: options.namespace,
9549
+ token: options.token,
9550
+ ...options.now ? { now: options.now } : {}
9551
+ });
9359
9552
  return {
9360
9553
  async listImages(query) {
9361
9554
  try {
9362
- const zustand = await loadHFState(namespace, token);
9555
+ const zustand = await store.bestand();
9363
9556
  const eintraege = zustand?.metadata ?? [];
9364
9557
  const gesucht = query?.search?.trim().toLowerCase();
9365
9558
  const gefiltert = gesucht ? eintraege.filter((e) => (e.prompt ?? "").toLowerCase().includes(gesucht) || e.id.toLowerCase().includes(gesucht)) : eintraege;
@@ -9373,50 +9566,18 @@ function createHfImageLibrary(options) {
9373
9566
  );
9374
9567
  }
9375
9568
  },
9376
- async resolveImage(mediaRef) {
9377
- const eben = frisch.get(mediaRef);
9378
- if (eben) return { kind: "ok", value: { data: eben.data, mime_type: eben.mimeType } };
9569
+ async resolveImage(mediaRef, mimeTypeHinweis) {
9379
9570
  try {
9380
- const zustand = await loadHFState(namespace, token);
9381
- const eintrag = (zustand?.metadata ?? []).find((e) => e.id === mediaRef);
9382
- if (!eintrag) {
9383
- return fehler(
9384
- `Das Bild ${mediaRef} steht nicht im Bestand. Moeglicherweise hat der Abgleich es noch nicht uebertragen.`,
9385
- true
9386
- );
9387
- }
9388
- const base64 = await hfLoadImageAsBase64(
9389
- mediaRef,
9390
- token,
9391
- void 0,
9392
- eintrag.filename,
9393
- void 0,
9394
- eintrag.mimeType,
9395
- false
9396
- );
9397
- if (!base64) {
9398
- return fehler(`Das Bild ${mediaRef} ist im Bestand verzeichnet, aber nicht ladbar.`, true);
9399
- }
9400
- return { kind: "ok", value: { data: base64, mime_type: eintrag.mimeType || "image/jpeg" } };
9571
+ const bild = await store.resolve(mediaRef, mimeTypeHinweis);
9572
+ return { kind: "ok", value: { data: bild.data, mime_type: bild.mimeType } };
9401
9573
  } catch (error) {
9402
- return fehler(
9403
- `Das Bild ${mediaRef} konnte nicht geladen werden: ${error instanceof Error ? error.message : String(error)}`,
9404
- true
9405
- );
9574
+ return fehler(error instanceof Error ? error.message : String(error), true);
9406
9575
  }
9407
9576
  },
9408
9577
  async putImage(input) {
9409
9578
  const id = newImageId();
9410
9579
  try {
9411
- await hfUploadImage(input.data, id, token, input.mime_type);
9412
- await writeHFEvent(namespace, token, "image_added", {
9413
- id,
9414
- prompt: input.title,
9415
- tags: [],
9416
- timestamp: now(),
9417
- mimeType: input.mime_type
9418
- }, []);
9419
- frisch.set(id, { mimeType: input.mime_type, data: input.data });
9580
+ await store.putWithId(id, input.data, input.mime_type, input.title);
9420
9581
  return {
9421
9582
  kind: "ok",
9422
9583
  value: { mediaRef: id, title: input.title, mimeType: input.mime_type, thumbUrl: null }
@@ -11123,6 +11284,11 @@ function alsBilder(rohe) {
11123
11284
  media_ref: b.media_ref,
11124
11285
  position: b.position,
11125
11286
  ...istText4(b.asset_id) ? { asset_id: b.asset_id } : {},
11287
+ // Der bestätigte MIME-Typ (Review 2026-08-18 §5 P2) muss durch das
11288
+ // Rücklesen hindurch: Genau hier braucht ihn der Lesepfad, um den
11289
+ // Dateipfad `images/<id>.<ext>` zu bilden, statt ihn zu erraten. Nur
11290
+ // gesetzt, wenn er im Ereignis steht — ältere Batches tragen ihn nicht.
11291
+ ...istText4(b.mime_type) ? { mime_type: b.mime_type } : {},
11126
11292
  model: b.model ?? null,
11127
11293
  seed: b.seed ?? null,
11128
11294
  width: b.width ?? null,
@@ -11211,6 +11377,7 @@ async function legeBatchAb(options, input) {
11211
11377
  const { namespace, token, channel } = options;
11212
11378
  const newImageId = options.newImageId ?? ((batchId, position) => `${batchId}-${position}`);
11213
11379
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
11380
+ const store = options.store ?? createHfImageStore({ namespace, token });
11214
11381
  const itemId = itemIdForRun(input.runId);
11215
11382
  let vorhandeneEvents;
11216
11383
  try {
@@ -11229,15 +11396,14 @@ async function legeBatchAb(options, input) {
11229
11396
  for (let offset = 0; offset < input.images.length; offset++) {
11230
11397
  const bild = input.images[offset];
11231
11398
  const id = newImageId(input.generationBatchId, startPosition + offset);
11399
+ const echterTyp = typAusBytes(bild.imageBase64, bild.mimeType);
11232
11400
  try {
11233
- await hfUploadImage(bild.imageBase64, id, token, bild.mimeType);
11234
- await writeHFEvent(namespace, token, "image_added", {
11401
+ await store.putWithId(
11235
11402
  id,
11236
- prompt: input.title ?? `Batch ${input.generationBatchId}`,
11237
- tags: [],
11238
- timestamp: now().getTime(),
11239
- mimeType: bild.mimeType
11240
- }, []);
11403
+ bild.imageBase64,
11404
+ echterTyp,
11405
+ input.title ?? `Batch ${input.generationBatchId}`
11406
+ );
11241
11407
  } catch (error) {
11242
11408
  return fehler3(
11243
11409
  `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.`
@@ -11247,6 +11413,7 @@ async function legeBatchAb(options, input) {
11247
11413
  position: startPosition + offset,
11248
11414
  media_ref: id,
11249
11415
  asset_id: id,
11416
+ mime_type: echterTyp,
11250
11417
  model: bild.model ?? null,
11251
11418
  seed: bild.seed ?? null,
11252
11419
  width: bild.width ?? null,
@@ -11290,8 +11457,11 @@ function createHfImageGenerationBatchPersistence(options) {
11290
11457
  images: input.images.map((bild) => ({
11291
11458
  imageBase64: bild.imageBase64,
11292
11459
  // Flow liefert PNG-Base64 (bestehende Konvention, s. AvatarArchitectApp.tsx
11293
- // `data:image/png;base64,${base64}`); der gemeinsame Vertrag führt
11294
- // keinen MIME-Typ je Bild.
11460
+ // `data:image/png;base64,${base64}`), und der gemeinsame Vertrag führt
11461
+ // keinen MIME-Typ je Bild. Das ist deshalb nur die **Vorgabe**:
11462
+ // Entschieden wird nach den echten Bytes (Review §5 P2) — ein künftig
11463
+ // anders kodiertes Bild läge sonst unter einer Endung, unter der es
11464
+ // niemand sucht.
11295
11465
  mimeType: "image/png",
11296
11466
  model: bild.model ?? null,
11297
11467
  seed: bild.seed ?? null,
@@ -11312,6 +11482,8 @@ function createHfImageBatchWrite(options) {
11312
11482
  title: input.title,
11313
11483
  images: input.images.map((bild) => ({
11314
11484
  imageBase64: bild.imageBase64,
11485
+ // Vorgabe, kein Urteil — der Import bringt beliebig kodierte Bilder
11486
+ // mit, und der Typ wird aus den Bytes bestimmt (Review §5 P2).
11315
11487
  mimeType: "image/png",
11316
11488
  model: bild.model ?? null,
11317
11489
  seed: bild.seed ?? null,
@@ -11385,7 +11557,17 @@ function createFlowSzenenViewPort(options) {
11385
11557
  ...options.newRunId ? { newRunId: options.newRunId } : {},
11386
11558
  ...options.now ? { now: options.now } : {}
11387
11559
  });
11388
- const imageLibrary = options.imageLibrary ?? (options.namespace?.trim() && options.token?.trim() ? createHfImageLibrary({ namespace: options.namespace, token: options.token }) : void 0);
11560
+ const hatHfZugangsdaten = Boolean(options.namespace?.trim() && options.token?.trim());
11561
+ const bildSpeicher = hatHfZugangsdaten ? createHfImageStore({
11562
+ namespace: options.namespace,
11563
+ token: options.token,
11564
+ ...options.now ? { now: () => options.now().getTime() } : {}
11565
+ }) : void 0;
11566
+ const imageLibrary = options.imageLibrary ?? (bildSpeicher ? createHfImageLibrary({
11567
+ namespace: options.namespace,
11568
+ token: options.token,
11569
+ store: bildSpeicher
11570
+ }) : void 0);
11389
11571
  const derivation = createClientDerivationPort({
11390
11572
  store: elements,
11391
11573
  derivations: createEventLogDerivationPersistence(channel),
@@ -11395,11 +11577,11 @@ function createFlowSzenenViewPort(options) {
11395
11577
  ...options.now ? { now: options.now } : {}
11396
11578
  });
11397
11579
  const imageBatchHistory = createHfImageBatchHistory({ channel });
11398
- const hatHfZugangsdaten = Boolean(options.namespace?.trim() && options.token?.trim());
11399
- const imageBatchWrite = hatHfZugangsdaten ? createHfImageBatchWrite({
11580
+ const imageBatchWrite = bildSpeicher ? createHfImageBatchWrite({
11400
11581
  namespace: options.namespace,
11401
11582
  token: options.token,
11402
11583
  channel,
11584
+ store: bildSpeicher,
11403
11585
  ...options.newBatchImageId ? { newImageId: options.newBatchImageId } : {},
11404
11586
  ...options.now ? { now: options.now } : {}
11405
11587
  }) : void 0;
@@ -11413,6 +11595,9 @@ function createFlowSzenenViewPort(options) {
11413
11595
  namespace: options.namespace,
11414
11596
  token: options.token,
11415
11597
  channel,
11598
+ // Dieselbe Instanz wie der Bildbestand — sonst wäre das gerade
11599
+ // erzeugte Bild in der Laufansicht wieder unauffindbar (R1).
11600
+ store: bildSpeicher,
11416
11601
  ...options.newBatchImageId ? { newImageId: options.newBatchImageId } : {},
11417
11602
  ...options.now ? { now: options.now } : {}
11418
11603
  }),
@@ -12896,8 +13081,8 @@ var BestandsBild = ({ port, mediaRef }) => {
12896
13081
  };
12897
13082
  var BatchBild = ({ port, bild }) => {
12898
13083
  const [phase] = usePortQuery(
12899
- () => port.imageLibrary ? port.imageLibrary.resolveImage(bild.media_ref) : Promise.resolve({ kind: "ok", value: null }),
12900
- [port, bild.media_ref]
13084
+ () => port.imageLibrary ? port.imageLibrary.resolveImage(bild.media_ref, bild.mime_type ?? null) : Promise.resolve({ kind: "ok", value: null }),
13085
+ [port, bild.media_ref, bild.mime_type]
12901
13086
  );
12902
13087
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("li", { "data-testid": `batchbild-${bild.media_ref}`, children: [
12903
13088
  phase.status === "ready" && phase.value && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
@@ -14942,6 +15127,7 @@ var SzenenView = ({
14942
15127
  areasFor,
14943
15128
  assessDerivationResponse,
14944
15129
  batchesForPromptRun,
15130
+ bildPfad,
14945
15131
  buildDerivationSystemInstruction,
14946
15132
  buildDerivationUserPrompt,
14947
15133
  buildSystemInstruction,
@@ -14997,6 +15183,7 @@ var SzenenView = ({
14997
15183
  createHfImageBatchWrite,
14998
15184
  createHfImageGenerationBatchPersistence,
14999
15185
  createHfImageLibrary,
15186
+ createHfImageStore,
15000
15187
  createImageGenerationPort,
15001
15188
  createInMemoryEventChannel,
15002
15189
  createMemoryViewPort,
@@ -15055,6 +15242,7 @@ var SzenenView = ({
15055
15242
  showChain,
15056
15243
  simulatedAnswerFor,
15057
15244
  totalModelCalls,
15245
+ typAusBytes,
15058
15246
  unclassifiedPublishRules,
15059
15247
  unsupportedReason,
15060
15248
  validateAgainst,
package/dist/szenen.mjs CHANGED
@@ -26,6 +26,7 @@ import {
26
26
  areasFor,
27
27
  assessDerivationResponse,
28
28
  batchesForPromptRun,
29
+ bildPfad,
29
30
  buildSystemInstruction,
30
31
  buildSystemInstruction2,
31
32
  buildUserPrompt,
@@ -75,6 +76,7 @@ import {
75
76
  createHfImageBatchWrite,
76
77
  createHfImageGenerationBatchPersistence,
77
78
  createHfImageLibrary,
79
+ createHfImageStore,
78
80
  createImageGenerationPort,
79
81
  createSimulationSceneDerivationPort,
80
82
  createSimulationTextGenerationPort,
@@ -123,6 +125,7 @@ import {
123
125
  scopeOfPath,
124
126
  showChain,
125
127
  simulatedAnswerFor,
128
+ typAusBytes,
126
129
  unclassifiedPublishRules,
127
130
  unsupportedReason,
128
131
  validateAgainst,
@@ -130,7 +133,7 @@ import {
130
133
  validateElementVersion,
131
134
  validatePayload,
132
135
  withReference
133
- } from "./chunk-4FOEFCKM.mjs";
136
+ } from "./chunk-DGFMONWO.mjs";
134
137
  import {
135
138
  ContractViolation,
136
139
  STYLE_PAYLOAD,
@@ -144,7 +147,7 @@ import {
144
147
  preferredVersionId,
145
148
  resolvePreferredHead
146
149
  } from "./chunk-FUY2OCVC.mjs";
147
- import "./chunk-VBWQP7PO.mjs";
150
+ import "./chunk-LFPOXNOZ.mjs";
148
151
 
149
152
  // src/lib/szenen/providerCheckStore.ts
150
153
  function collectProviderChecks(events) {
@@ -1477,6 +1480,7 @@ export {
1477
1480
  areasFor,
1478
1481
  assessDerivationResponse,
1479
1482
  batchesForPromptRun,
1483
+ bildPfad,
1480
1484
  buildSystemInstruction as buildDerivationSystemInstruction,
1481
1485
  buildUserPrompt as buildDerivationUserPrompt,
1482
1486
  buildSystemInstruction2 as buildSystemInstruction,
@@ -1532,6 +1536,7 @@ export {
1532
1536
  createHfImageBatchWrite,
1533
1537
  createHfImageGenerationBatchPersistence,
1534
1538
  createHfImageLibrary,
1539
+ createHfImageStore,
1535
1540
  createImageGenerationPort,
1536
1541
  createInMemoryEventChannel,
1537
1542
  createMemoryViewPort,
@@ -1590,6 +1595,7 @@ export {
1590
1595
  showChain,
1591
1596
  simulatedAnswerFor,
1592
1597
  totalModelCalls,
1598
+ typAusBytes,
1593
1599
  unclassifiedPublishRules,
1594
1600
  unsupportedReason,
1595
1601
  validateAgainst,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rslsp1/fa-app-tools",
3
- "version": "2.0.84",
3
+ "version": "2.0.86",
4
4
  "description": "Shared tools and hooks for Fine Art flow apps",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -1,73 +0,0 @@
1
- import { T as TextGenerationPort } from './contracts-DdYOMBl9.mjs';
2
-
3
- declare function buildGenerationPrompt(hierarchyText: string, mode?: 'literal' | 'creative'): string;
4
- declare function buildFallbackPrompt(hierarchyText: string): string;
5
- declare function cleanAiResponse(text: string): string;
6
- declare function buildImageGenerationOptions(prompt: string, aspectRatio: string, model: string, referenceMediaId?: string): Record<string, any>;
7
- interface SelectedTag {
8
- label: string;
9
- value: string;
10
- category: string;
11
- }
12
- declare function buildPromptTabPayload(selectedTags: SelectedTag[], instructions: string, rules: string, treeText?: string): string;
13
- declare function parsePromptResponse(raw: string): {
14
- prompt: string;
15
- feedback: string | null;
16
- };
17
- interface FlowSdkImage {
18
- base64: string;
19
- mimeType: string;
20
- }
21
- interface FlowTextGenerationOptions {
22
- images?: FlowSdkImage[];
23
- systemInstruction?: string;
24
- responseFormat?: 'text' | 'json';
25
- thinkingLevel?: 'low' | 'medium' | 'high';
26
- }
27
- interface FlowSdk {
28
- generate: {
29
- image: (options: Record<string, any>) => Promise<{
30
- base64: string;
31
- mediaId: string;
32
- }>;
33
- text: (prompt: string, options?: {
34
- systemInstruction?: string;
35
- thinkingLevel?: string;
36
- images?: FlowSdkImage[];
37
- }) => Promise<{
38
- text: string;
39
- }>;
40
- };
41
- download: (args: {
42
- base64: string;
43
- mimeType: string;
44
- filename: string;
45
- }) => Promise<void>;
46
- media: {
47
- selectMultiple: (args: {
48
- filter: string;
49
- }) => Promise<any[]>;
50
- };
51
- }
52
- declare function createFlowServices(Flow: FlowSdk): {
53
- generateImage: (options: Record<string, any>) => Promise<{
54
- base64: string;
55
- mediaId: string;
56
- }>;
57
- generateText: (text: string, options?: FlowTextGenerationOptions) => Promise<string>;
58
- };
59
- declare function interpretSdkError(err: any): Error;
60
-
61
- type FlowGenerateText = (prompt: string, options?: FlowTextGenerationOptions) => Promise<string>;
62
- interface CreateFlowTextGenerationPortOptions {
63
- provider?: string;
64
- modelId?: string;
65
- }
66
- /**
67
- * Adapts the existing Flow SDK service to the host-neutral Szenen contract.
68
- * It deliberately does not parse or validate the response: that happens once
69
- * in `runPromptDerivation`, identically for every host.
70
- */
71
- declare function createFlowTextGenerationPort(generateText: FlowGenerateText, options?: CreateFlowTextGenerationPortOptions): TextGenerationPort;
72
-
73
- export { type CreateFlowTextGenerationPortOptions as C, type FlowGenerateText as F, type SelectedTag as S, type FlowSdk as a, type FlowTextGenerationOptions as b, buildFallbackPrompt as c, buildGenerationPrompt as d, buildImageGenerationOptions as e, buildPromptTabPayload as f, cleanAiResponse as g, createFlowServices as h, createFlowTextGenerationPort as i, interpretSdkError as j, parsePromptResponse as p };