@rslsp1/fa-app-tools 2.0.81 → 2.0.83

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
@@ -786,7 +786,7 @@ __export(index_exports, {
786
786
  module.exports = __toCommonJS(index_exports);
787
787
 
788
788
  // package.json
789
- var version = "2.0.81";
789
+ var version = "2.0.83";
790
790
 
791
791
  // src/hooks/useOnClickOutside.ts
792
792
  var import_react = require("react");
@@ -3692,6 +3692,21 @@ function validateTagBaseSelection(base, userId, contextId) {
3692
3692
  // src/hooks/useTagBase.ts
3693
3693
  var USER_ID_KEY = "aa-user-id";
3694
3694
  var CONTEXT_ID_KEY = "aa-context-id";
3695
+ var IDENTITY_NAMESPACE_KEY = "aa-identity-namespace";
3696
+ function readStoredNamespace() {
3697
+ try {
3698
+ return localStorage.getItem(IDENTITY_NAMESPACE_KEY);
3699
+ } catch {
3700
+ return null;
3701
+ }
3702
+ }
3703
+ function writeStoredNamespace(namespace) {
3704
+ try {
3705
+ if (namespace === null) localStorage.removeItem(IDENTITY_NAMESPACE_KEY);
3706
+ else localStorage.setItem(IDENTITY_NAMESPACE_KEY, namespace);
3707
+ } catch {
3708
+ }
3709
+ }
3695
3710
  function readStoredId(key) {
3696
3711
  try {
3697
3712
  const v = localStorage.getItem(key);
@@ -3728,6 +3743,16 @@ function useTagBase(token, namespace) {
3728
3743
  }, [selectedContextId]);
3729
3744
  const load = (0, import_react19.useCallback)(async () => {
3730
3745
  if (!token || !namespace) return;
3746
+ if (readStoredNamespace() !== namespace) {
3747
+ selectedUserIdRef.current = null;
3748
+ selectedContextIdRef.current = null;
3749
+ setSelectedUserId(null);
3750
+ setSelectedContextId(null);
3751
+ writeStoredId(USER_ID_KEY, null);
3752
+ writeStoredId(CONTEXT_ID_KEY, null);
3753
+ writeStoredNamespace(namespace);
3754
+ setPickerMessage(null);
3755
+ }
3731
3756
  setIsLoading(true);
3732
3757
  setError(null);
3733
3758
  try {
@@ -3766,16 +3791,19 @@ function useTagBase(token, namespace) {
3766
3791
  setSelectedContextId(null);
3767
3792
  writeStoredId(USER_ID_KEY, userId);
3768
3793
  writeStoredId(CONTEXT_ID_KEY, null);
3769
- }, []);
3794
+ writeStoredNamespace(namespace);
3795
+ }, [namespace]);
3770
3796
  const selectContext = (0, import_react19.useCallback)((contextId) => {
3771
3797
  setSelectedContextId(contextId);
3772
3798
  writeStoredId(CONTEXT_ID_KEY, contextId);
3773
- }, []);
3799
+ writeStoredNamespace(namespace);
3800
+ }, [namespace]);
3774
3801
  const resetSelection = (0, import_react19.useCallback)(() => {
3775
3802
  setSelectedUserId(null);
3776
3803
  setSelectedContextId(null);
3777
3804
  writeStoredId(USER_ID_KEY, null);
3778
3805
  writeStoredId(CONTEXT_ID_KEY, null);
3806
+ writeStoredNamespace(null);
3779
3807
  setPickerMessage(null);
3780
3808
  }, []);
3781
3809
  const validation = tagBase ? validateTagBaseSelection(tagBase, selectedUserId, selectedContextId) : { valid: false, reason: null };
@@ -6565,13 +6593,13 @@ function noticeFor(ergebnis, was) {
6565
6593
  };
6566
6594
  }
6567
6595
  case "rejected": {
6568
- const fehler3 = ergebnis.errors ?? [];
6596
+ const fehler4 = ergebnis.errors ?? [];
6569
6597
  const ungeprueft = ergebnis.unchecked ?? [];
6570
6598
  return {
6571
6599
  tone: "rejected",
6572
- text: `${was}: abgelehnt \u2014 ${fehler3.length} Befund${fehler3.length === 1 ? "" : "e"}.`,
6600
+ text: `${was}: abgelehnt \u2014 ${fehler4.length} Befund${fehler4.length === 1 ? "" : "e"}.`,
6573
6601
  details: [
6574
- ...fehler3.map((f) => `${f.path ?? "(ohne Pfad)"}: ${f.message ?? "ohne Meldung"}`),
6602
+ ...fehler4.map((f) => `${f.path ?? "(ohne Pfad)"}: ${f.message ?? "ohne Meldung"}`),
6575
6603
  // Nicht verschweigen: was nicht geprüft wurde, ist nicht in Ordnung,
6576
6604
  // sondern ungeprüft.
6577
6605
  ...ungeprueft.map((u) => `ungepr\xFCft: ${u}`)
@@ -11266,7 +11294,7 @@ var ERKLAERUNG = {
11266
11294
  };
11267
11295
  var CapabilityPanel = ({ port, elementId, setupPayload }) => {
11268
11296
  const [bericht, setBericht] = (0, import_react36.useState)(null);
11269
- const [fehler3, setFehler] = (0, import_react36.useState)(null);
11297
+ const [fehler4, setFehler] = (0, import_react36.useState)(null);
11270
11298
  (0, import_react36.useEffect)(() => {
11271
11299
  let abgemeldet = false;
11272
11300
  setBericht(null);
@@ -11288,10 +11316,10 @@ var CapabilityPanel = ({ port, elementId, setupPayload }) => {
11288
11316
  abgemeldet = true;
11289
11317
  };
11290
11318
  }, [port, elementId, setupPayload]);
11291
- if (fehler3) {
11319
+ if (fehler4) {
11292
11320
  return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("p", { "data-testid": "capability-fehler", role: "alert", children: [
11293
11321
  "F\xE4higkeitsbericht nicht erstellbar: ",
11294
- fehler3
11322
+ fehler4
11295
11323
  ] });
11296
11324
  }
11297
11325
  if (!bericht) return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("p", { "data-testid": "capability-laden", children: "F\xE4higkeitsbericht wird erstellt \u2026" });
@@ -11401,6 +11429,10 @@ var ChainPromptAction = ({
11401
11429
  var import_react39 = require("react");
11402
11430
 
11403
11431
  // src/lib/szenen/itemDerivation.ts
11432
+ var SZENEN_ITEM_PREFIX = "szn-";
11433
+ function itemIdForRun(runId) {
11434
+ return `${SZENEN_ITEM_PREFIX}${runId}`;
11435
+ }
11404
11436
  function roundOfRun(run, runsOfShot) {
11405
11437
  return runsOfShot.findIndex((kandidat) => kandidat.run_id === run.run_id) + 1;
11406
11438
  }
@@ -11502,7 +11534,30 @@ var SZENEN_EVENT_TYPES = {
11502
11534
  * ebenfalls keine Bilddaten — nur Status, Zaehlung und die Item-Kennung,
11503
11535
  * falls eine entstand.
11504
11536
  */
11505
- imageGenerationFinished: "szenen.image_generation_finished"
11537
+ imageGenerationFinished: "szenen.image_generation_finished",
11538
+ /**
11539
+ * Ein Bild-Batch ist kanonisch gespeichert — Stufe 6.9.2, Gate D
11540
+ * (D-6.9.2-6).
11541
+ *
11542
+ * Erscheint erst, **nachdem** jedes Bild des Batches im Bildbestand liegt
11543
+ * (`hfUploadImage` + `image_added`, dieselbe Reihenfolge wie
11544
+ * `hfImageLibrary.putImage`) — kein Bild gilt als vorhanden, bevor sein
11545
+ * Upload bestätigt ist (R3). Trägt bewusst **keine Bilddaten**, nur den
11546
+ * vollständigen Metadatennachweis, den `listBatchesForPromptRun()` /
11547
+ * `getBatch()` ohne MySQL rekonstruieren können müssen: `item_id`,
11548
+ * `prompt_run_id`, `generation_batch_id`, `source_kind`, je Bild
11549
+ * `position`, `media_ref`, `asset_id`, `model`, `seed`, `width`/`height`.
11550
+ *
11551
+ * Additiv wie jedes Ereignis dieser Domain: ein zweiter Batch desselben
11552
+ * Laufs ist ein **weiteres** Ereignis mit derselben `item_id`, nicht eine
11553
+ * Änderung am ersten (D-113/D-121 — ein Lauf bleibt ein Item). Gilt für
11554
+ * `generated_in_app` **und** `external_import` — nur `source_kind`
11555
+ * unterscheidet die beiden Schreiber (D-116/D-117/D-120).
11556
+ *
11557
+ * Der Elementreducer ignoriert auch diesen Typ (`default:`-Zweig); gelesen
11558
+ * wird mit `collectImageBatches` (`imageBatchStore.ts`).
11559
+ */
11560
+ imageBatchPersisted: "szenen.image_batch_persisted"
11506
11561
  };
11507
11562
  function sortForReplay(events) {
11508
11563
  return [...events].sort((a, b) => {
@@ -12151,10 +12206,10 @@ var ZuordnenAktion = ({ port, lauf, onFertig }) => {
12151
12206
  setNotiz(
12152
12207
  ergebnis.kind === "ok" ? { tone: "ok", text: `Importiert als Item ${ergebnis.value.item_id} mit ${bilder.length} Bild(ern).` } : noticeFor(ergebnis, "Externe Bilder importieren")
12153
12208
  );
12154
- } catch (fehler3) {
12209
+ } catch (fehler4) {
12155
12210
  setNotiz({
12156
12211
  tone: "error",
12157
- text: `Externe Bilder importieren: ${fehler3 instanceof Error ? fehler3.message : String(fehler3)}`
12212
+ text: `Externe Bilder importieren: ${fehler4 instanceof Error ? fehler4.message : String(fehler4)}`
12158
12213
  });
12159
12214
  }
12160
12215
  setLaeuft(false);
@@ -17464,6 +17519,531 @@ function createHfImageLibrary(options) {
17464
17519
  };
17465
17520
  }
17466
17521
 
17522
+ // src/lib/szenen/flowImageProvider.ts
17523
+ var FLOW_MODEL_DISPLAY_NAMES = {
17524
+ "imagen-4.0-generate-001": "Imagen 4"
17525
+ };
17526
+ function flowImageProviderHasAccess(model) {
17527
+ return model.callable_model_id in FLOW_MODEL_DISPLAY_NAMES;
17528
+ }
17529
+ function istNichtleererText(wert) {
17530
+ return typeof wert === "string" && wert.trim().length > 0;
17531
+ }
17532
+ function createFlowImageProvider(generateImage) {
17533
+ return {
17534
+ async generate(input) {
17535
+ const modelDisplayName = FLOW_MODEL_DISPLAY_NAMES[input.callableModelId];
17536
+ if (!modelDisplayName) {
17537
+ throw new Error(
17538
+ `Der Flow-Host kennt keinen Flow-Display-Namen zu "${input.callableModelId}". Bekannt sind: ${Object.keys(FLOW_MODEL_DISPLAY_NAMES).join(", ")}. Es wird kein anderes Modell ersatzweise gerufen (D-115/D-6.9.2-3).`
17539
+ );
17540
+ }
17541
+ if (istNichtleererText(input.negativePrompt)) {
17542
+ throw new Error(
17543
+ "Flow.generate.image kennt keinen dokumentierten Parameter f\xFCr einen negativen Prompt. Der Auftrag wird deshalb als fehlgeschlagen protokolliert, statt den negativen Prompt still zu verlieren (D-6.9.2-3)."
17544
+ );
17545
+ }
17546
+ const erzeugt = [];
17547
+ let ersterFehler = null;
17548
+ for (let position = 0; position < input.imageCount; position++) {
17549
+ try {
17550
+ const antwort = await generateImage({
17551
+ prompt: input.positivePrompt,
17552
+ modelDisplayName,
17553
+ ...input.aspectRatio ? { aspectRatio: input.aspectRatio } : {}
17554
+ });
17555
+ if (!antwort || typeof antwort.base64 !== "string" || antwort.base64.length === 0) {
17556
+ throw new Error("Flow.generate.image hat kein Bild geliefert.");
17557
+ }
17558
+ erzeugt.push({
17559
+ imageBase64: antwort.base64,
17560
+ // Das tatsaechlich gerufene Modell, nicht der Flow-Display-Name —
17561
+ // im Bestand soll stehen, was bezahlt wurde (wie reactImageProvider.ts).
17562
+ model: input.callableModelId
17563
+ // Kein Seed: weder als Eingabe dokumentiert noch als Ausgabe
17564
+ // geliefert — nichts wird erfunden (D-107).
17565
+ });
17566
+ } catch (fehler4) {
17567
+ ersterFehler = fehler4 instanceof Error ? fehler4.message : String(fehler4);
17568
+ break;
17569
+ }
17570
+ }
17571
+ if (erzeugt.length === 0) {
17572
+ throw new Error(
17573
+ ersterFehler ?? `Flow.generate.image hat zu ${input.callableModelId} kein verwertbares Bild geliefert.`
17574
+ );
17575
+ }
17576
+ return { images: erzeugt };
17577
+ }
17578
+ };
17579
+ }
17580
+
17581
+ // src/lib/szenen/imageGenerationOrderStore.ts
17582
+ function istText2(wert) {
17583
+ return typeof wert === "string" && wert.trim().length > 0;
17584
+ }
17585
+ function collectImageGenerationOrders(events) {
17586
+ const byBatchId = /* @__PURE__ */ new Map();
17587
+ for (const event of events) {
17588
+ if (event.type !== SZENEN_EVENT_TYPES.imageGenerationStarted) continue;
17589
+ const payload = event.payload;
17590
+ if (!payload || !istText2(payload.generation_batch_id) || !istText2(payload.prompt_run_id)) continue;
17591
+ if (byBatchId.has(payload.generation_batch_id)) continue;
17592
+ byBatchId.set(payload.generation_batch_id, {
17593
+ generation_batch_id: payload.generation_batch_id,
17594
+ prompt_run_id: payload.prompt_run_id,
17595
+ source_kind: "generated_in_app",
17596
+ request: payload.request,
17597
+ created_at: payload.created_at ?? "",
17598
+ status: "started"
17599
+ });
17600
+ }
17601
+ for (const event of events) {
17602
+ if (event.type !== SZENEN_EVENT_TYPES.imageGenerationFinished) continue;
17603
+ const payload = event.payload;
17604
+ if (!payload || !istText2(payload.generation_batch_id)) continue;
17605
+ const auftrag = byBatchId.get(payload.generation_batch_id);
17606
+ if (!auftrag) continue;
17607
+ if (auftrag.status !== "started") continue;
17608
+ if (payload.status !== "succeeded" && payload.status !== "partial" && payload.status !== "failed") continue;
17609
+ byBatchId.set(payload.generation_batch_id, {
17610
+ ...auftrag,
17611
+ status: payload.status,
17612
+ ...payload.item_id ? { item_id: payload.item_id } : {},
17613
+ produced_count: payload.produced_count ?? 0,
17614
+ ...payload.diagnostic ? { diagnostic: payload.diagnostic } : {},
17615
+ finished_at: payload.finished_at ?? ""
17616
+ });
17617
+ }
17618
+ return [...byBatchId.values()];
17619
+ }
17620
+ function ordersForPromptRun(events, runId) {
17621
+ return collectImageGenerationOrders(events).filter((o) => o.prompt_run_id === runId);
17622
+ }
17623
+
17624
+ // src/lib/szenen/imageBatchStore.ts
17625
+ var istText3 = (wert) => typeof wert === "string" && wert.trim().length > 0;
17626
+ function alsBilder(rohe) {
17627
+ if (!Array.isArray(rohe)) return [];
17628
+ return rohe.filter((b) => !!b && typeof b === "object").filter((b) => istText3(b.media_ref) && typeof b.position === "number").map((b) => ({
17629
+ media_ref: b.media_ref,
17630
+ position: b.position,
17631
+ ...istText3(b.asset_id) ? { asset_id: b.asset_id } : {},
17632
+ model: b.model ?? null,
17633
+ seed: b.seed ?? null,
17634
+ width: b.width ?? null,
17635
+ height: b.height ?? null
17636
+ }));
17637
+ }
17638
+ function collectImageBatches(events) {
17639
+ const byItemId = /* @__PURE__ */ new Map();
17640
+ for (const event of events) {
17641
+ if (event.type !== SZENEN_EVENT_TYPES.imageBatchPersisted) continue;
17642
+ const payload = event.payload;
17643
+ if (!payload || !istText3(payload.item_id) || !istText3(payload.prompt_run_id) || !istText3(payload.generation_batch_id)) continue;
17644
+ if (payload.source_kind !== "generated_in_app" && payload.source_kind !== "external_import") continue;
17645
+ const bestehend = byItemId.get(payload.item_id);
17646
+ if (bestehend?.generation_batch_ids.includes(payload.generation_batch_id)) continue;
17647
+ const bilder = alsBilder(payload.images);
17648
+ if (!bestehend) {
17649
+ byItemId.set(payload.item_id, {
17650
+ item_id: payload.item_id,
17651
+ prompt_run_id: payload.prompt_run_id,
17652
+ generation_batch_ids: [payload.generation_batch_id],
17653
+ source_kind: payload.source_kind,
17654
+ title: istText3(payload.title) ? payload.title : `Batch aus Lauf ${payload.prompt_run_id}`,
17655
+ images: bilder
17656
+ });
17657
+ continue;
17658
+ }
17659
+ byItemId.set(payload.item_id, {
17660
+ ...bestehend,
17661
+ generation_batch_ids: [...bestehend.generation_batch_ids, payload.generation_batch_id],
17662
+ images: [...bestehend.images, ...bilder]
17663
+ });
17664
+ }
17665
+ return [...byItemId.values()];
17666
+ }
17667
+ function batchesForPromptRun(events, runId) {
17668
+ return collectImageBatches(events).filter((b) => b.prompt_run_id === runId);
17669
+ }
17670
+ function findImageBatch(events, batchId) {
17671
+ return collectImageBatches(events).find((b) => b.generation_batch_ids.includes(batchId)) ?? null;
17672
+ }
17673
+
17674
+ // src/lib/szenen/hfImageBatchHistory.ts
17675
+ function lesefehler(aktion, error) {
17676
+ return {
17677
+ kind: "error",
17678
+ message: `${aktion}: ${error instanceof Error ? error.message : String(error)}`,
17679
+ retryable: true
17680
+ };
17681
+ }
17682
+ function createHfImageBatchHistory(options) {
17683
+ const { channel } = options;
17684
+ return {
17685
+ async listBatchesForPromptRun(runId) {
17686
+ try {
17687
+ const events = await channel.readEvents();
17688
+ return { kind: "ok", value: batchesForPromptRun(events, runId) };
17689
+ } catch (error) {
17690
+ return lesefehler(`Batches fuer Lauf ${runId} konnten nicht gelesen werden`, error);
17691
+ }
17692
+ },
17693
+ async getBatch(batchId) {
17694
+ try {
17695
+ const events = await channel.readEvents();
17696
+ return { kind: "ok", value: findImageBatch(events, batchId) };
17697
+ } catch (error) {
17698
+ return lesefehler(`Batch ${batchId} konnte nicht gelesen werden`, error);
17699
+ }
17700
+ },
17701
+ async listOrdersForPromptRun(runId) {
17702
+ try {
17703
+ const events = await channel.readEvents();
17704
+ return { kind: "ok", value: ordersForPromptRun(events, runId) };
17705
+ } catch (error) {
17706
+ return lesefehler(`Bildgenerierungsauftraege fuer Lauf ${runId} konnten nicht gelesen werden`, error);
17707
+ }
17708
+ }
17709
+ };
17710
+ }
17711
+
17712
+ // src/lib/szenen/hfImageBatchPersistence.ts
17713
+ init_hfStateService();
17714
+ init_hfEventLog();
17715
+ function fehler3(nachricht, retryable = true) {
17716
+ return { kind: "error", message: nachricht, retryable };
17717
+ }
17718
+ async function legeBatchAb(options, input) {
17719
+ const { namespace, token, channel } = options;
17720
+ const newImageId = options.newImageId ?? ((batchId, position) => `${batchId}-${position}`);
17721
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
17722
+ const itemId = itemIdForRun(input.runId);
17723
+ let vorhandeneEvents;
17724
+ try {
17725
+ vorhandeneEvents = await channel.readEvents();
17726
+ } catch (error) {
17727
+ return fehler3(
17728
+ `Bild-Batches von Lauf ${input.runId} konnten vor dem Ablegen nicht gelesen werden: ${error instanceof Error ? error.message : String(error)}`
17729
+ );
17730
+ }
17731
+ const bestehend = batchesForPromptRun(vorhandeneEvents, input.runId)[0] ?? null;
17732
+ if (bestehend?.generation_batch_ids.includes(input.generationBatchId)) {
17733
+ return { kind: "ok", value: bestehend };
17734
+ }
17735
+ const startPosition = bestehend?.images.length ?? 0;
17736
+ const abgelegt = [];
17737
+ for (let offset = 0; offset < input.images.length; offset++) {
17738
+ const bild = input.images[offset];
17739
+ const id = newImageId(input.generationBatchId, startPosition + offset);
17740
+ try {
17741
+ await hfUploadImage(bild.imageBase64, id, token, bild.mimeType);
17742
+ await writeHFEvent(namespace, token, "image_added", {
17743
+ id,
17744
+ prompt: input.title ?? `Batch ${input.generationBatchId}`,
17745
+ tags: [],
17746
+ timestamp: now().getTime(),
17747
+ mimeType: bild.mimeType
17748
+ }, []);
17749
+ } catch (error) {
17750
+ return fehler3(
17751
+ `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.`
17752
+ );
17753
+ }
17754
+ abgelegt.push({
17755
+ position: startPosition + offset,
17756
+ media_ref: id,
17757
+ asset_id: id,
17758
+ model: bild.model ?? null,
17759
+ seed: bild.seed ?? null,
17760
+ width: bild.width ?? null,
17761
+ height: bild.height ?? null
17762
+ });
17763
+ }
17764
+ const titel = bestehend?.title ?? (input.title?.trim() || `Batch aus Lauf ${input.runId}`);
17765
+ const payload = {
17766
+ item_id: itemId,
17767
+ prompt_run_id: input.runId,
17768
+ generation_batch_id: input.generationBatchId,
17769
+ source_kind: input.sourceKind,
17770
+ title: titel,
17771
+ images: abgelegt,
17772
+ created_at: now().toISOString()
17773
+ };
17774
+ try {
17775
+ await channel.appendEvent(SZENEN_EVENT_TYPES.imageBatchPersisted, payload);
17776
+ } catch (error) {
17777
+ return fehler3(
17778
+ `Die Batch-Metadaten fuer ${input.generationBatchId} konnten nicht protokolliert werden, obwohl die Bilder bereits im Bestand liegen: ${error instanceof Error ? error.message : String(error)}`
17779
+ );
17780
+ }
17781
+ const zusammengefuehrt = {
17782
+ item_id: itemId,
17783
+ prompt_run_id: input.runId,
17784
+ generation_batch_ids: bestehend ? [...bestehend.generation_batch_ids, input.generationBatchId] : [input.generationBatchId],
17785
+ source_kind: bestehend?.source_kind ?? input.sourceKind,
17786
+ title: titel,
17787
+ images: [...bestehend?.images ?? [], ...abgelegt]
17788
+ };
17789
+ return { kind: "ok", value: zusammengefuehrt };
17790
+ }
17791
+ function createHfImageGenerationBatchPersistence(options) {
17792
+ return {
17793
+ async persist(input) {
17794
+ return legeBatchAb(options, {
17795
+ runId: input.runId,
17796
+ generationBatchId: input.generationBatchId,
17797
+ sourceKind: "generated_in_app",
17798
+ images: input.images.map((bild) => ({
17799
+ imageBase64: bild.imageBase64,
17800
+ // Flow liefert PNG-Base64 (bestehende Konvention, s. AvatarArchitectApp.tsx
17801
+ // `data:image/png;base64,${base64}`); der gemeinsame Vertrag führt
17802
+ // keinen MIME-Typ je Bild.
17803
+ mimeType: "image/png",
17804
+ model: bild.model ?? null,
17805
+ seed: bild.seed ?? null,
17806
+ width: bild.width ?? null,
17807
+ height: bild.height ?? null
17808
+ }))
17809
+ });
17810
+ }
17811
+ };
17812
+ }
17813
+ function createHfImageBatchWrite(options) {
17814
+ return {
17815
+ async importBatch(input) {
17816
+ return legeBatchAb(options, {
17817
+ runId: input.runId,
17818
+ generationBatchId: input.generationBatchId,
17819
+ sourceKind: "external_import",
17820
+ title: input.title,
17821
+ images: input.images.map((bild) => ({
17822
+ imageBase64: bild.imageBase64,
17823
+ mimeType: "image/png",
17824
+ model: bild.model ?? null,
17825
+ seed: bild.seed ?? null,
17826
+ width: bild.width ?? null,
17827
+ height: bild.height ?? null
17828
+ }))
17829
+ });
17830
+ },
17831
+ async attachLibraryItem(input) {
17832
+ let events;
17833
+ try {
17834
+ events = await options.channel.readEvents();
17835
+ } catch (error) {
17836
+ return {
17837
+ kind: "error",
17838
+ message: `Batches konnten nicht gelesen werden: ${error instanceof Error ? error.message : String(error)}`,
17839
+ retryable: true
17840
+ };
17841
+ }
17842
+ const item = collectImageBatches(events).find((b) => b.item_id === input.itemId);
17843
+ if (!item) {
17844
+ return { kind: "error", message: `Item ${input.itemId} ist unbekannt.`, retryable: false };
17845
+ }
17846
+ if (item.prompt_run_id !== input.runId) {
17847
+ return {
17848
+ kind: "conflict",
17849
+ message: `Item ${input.itemId} geh\xF6rt bereits zu Lauf ${item.prompt_run_id}.`,
17850
+ existingPromptRunId: item.prompt_run_id
17851
+ };
17852
+ }
17853
+ return { kind: "ok", value: item };
17854
+ }
17855
+ };
17856
+ }
17857
+
17858
+ // src/lib/szenen/imageGenerationCapability.ts
17859
+ var isObject9 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
17860
+ var IMAGE_MODEL_CATALOGUE = {
17861
+ imagen_4: {
17862
+ callable_model_id: "imagen-4.0-generate-001",
17863
+ provider: "google",
17864
+ max_images: 4,
17865
+ aspect_ratios: ["1:1", "3:4", "4:3", "9:16", "16:9"],
17866
+ supports_seed: true
17867
+ },
17868
+ imagen_3: {
17869
+ callable_model_id: "imagen-3.0-generate-001",
17870
+ provider: "google",
17871
+ max_images: 4,
17872
+ aspect_ratios: ["1:1", "3:4", "4:3", "9:16", "16:9"],
17873
+ supports_seed: true
17874
+ }
17875
+ };
17876
+ function callableImageModelFor(targetModelKey) {
17877
+ return IMAGE_MODEL_CATALOGUE[targetModelKey] ?? null;
17878
+ }
17879
+ function targetModelKeyOf(targetModel) {
17880
+ if (!isObject9(targetModel)) return null;
17881
+ return typeof targetModel.key === "string" && targetModel.key.length > 0 ? targetModel.key : null;
17882
+ }
17883
+ function capabilityForTargetModel(targetModel, hasAccess) {
17884
+ const key = targetModelKeyOf(targetModel);
17885
+ if (!key) {
17886
+ return {
17887
+ kind: "unsupported",
17888
+ target_model_key: "(unbekannt)",
17889
+ reason: 'Der eingefrorene Lauf tr\xE4gt kein "target_model.key" \u2014 es gibt nichts zuzuordnen.'
17890
+ };
17891
+ }
17892
+ const modell = callableImageModelFor(key);
17893
+ if (!modell) {
17894
+ return {
17895
+ kind: "unsupported",
17896
+ target_model_key: key,
17897
+ reason: `F\xFCr das Zielmodell "${key}" ist in diesem Host kein Bildpfad katalogisiert.`
17898
+ };
17899
+ }
17900
+ if (!hasAccess(modell)) {
17901
+ return {
17902
+ kind: "unsupported",
17903
+ target_model_key: key,
17904
+ reason: `Dieser Host hat keinen Zugang zu ${modell.callable_model_id} (${modell.provider}).`
17905
+ };
17906
+ }
17907
+ return {
17908
+ kind: "supported",
17909
+ target_model_key: key,
17910
+ provider: modell.provider,
17911
+ callable_model_id: modell.callable_model_id,
17912
+ max_images: modell.max_images,
17913
+ aspect_ratios: modell.aspect_ratios,
17914
+ supports_seed: modell.supports_seed
17915
+ };
17916
+ }
17917
+
17918
+ // src/lib/szenen/imageGenerationPort.ts
17919
+ function queryError2(error, action) {
17920
+ return {
17921
+ kind: "error",
17922
+ message: `${action}: ${error instanceof Error ? error.message : String(error)}`,
17923
+ retryable: true
17924
+ };
17925
+ }
17926
+ function createImageGenerationPort(options) {
17927
+ const { runs, provider, batches, events, hasAccess } = options;
17928
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
17929
+ async function readRun(runId) {
17930
+ try {
17931
+ return await runs.getRun(runId);
17932
+ } catch (error) {
17933
+ return queryError2(error, `Lauf ${runId} konnte nicht gelesen werden`);
17934
+ }
17935
+ }
17936
+ return {
17937
+ async capabilityForRun(runId) {
17938
+ const gelesen = await readRun(runId);
17939
+ if (gelesen.kind !== "ok") return gelesen;
17940
+ if (!gelesen.value) {
17941
+ return { kind: "error", message: `Lauf ${runId} ist unbekannt.`, retryable: false };
17942
+ }
17943
+ const targetModel = gelesen.value.input_payload?.target_model;
17944
+ return { kind: "ok", value: capabilityForTargetModel(targetModel, hasAccess) };
17945
+ },
17946
+ async startFromPromptRun(input) {
17947
+ const gelesen = await readRun(input.runId);
17948
+ if (gelesen.kind !== "ok") return { kind: "rejected", message: gelesen.message };
17949
+ if (!gelesen.value) {
17950
+ return { kind: "rejected", message: `Lauf ${input.runId} ist unbekannt.` };
17951
+ }
17952
+ const run = gelesen.value;
17953
+ if (run.status !== "succeeded" || !run.output) {
17954
+ return {
17955
+ kind: "rejected",
17956
+ message: `Lauf ${input.runId} ist nicht erfolgreich \u2014 ein Bildauftrag braucht einen eingefrorenen Prompt (D-103).`
17957
+ };
17958
+ }
17959
+ const targetModel = run.input_payload?.target_model;
17960
+ const capability = capabilityForTargetModel(targetModel, hasAccess);
17961
+ if (capability.kind !== "supported") {
17962
+ return { kind: "unsupported", targetModelKey: capability.target_model_key, reason: capability.reason };
17963
+ }
17964
+ const startedPayload = {
17965
+ generation_batch_id: input.generationBatchId,
17966
+ prompt_run_id: input.runId,
17967
+ source_kind: "generated_in_app",
17968
+ request: {
17969
+ image_count: input.imageCount,
17970
+ ...input.aspectRatio ? { aspect_ratio: input.aspectRatio } : {},
17971
+ ...input.seeds ? { seeds: input.seeds } : {},
17972
+ callable_model_id: capability.callable_model_id
17973
+ },
17974
+ created_at: now().toISOString()
17975
+ };
17976
+ try {
17977
+ await events.appendEvent(SZENEN_EVENT_TYPES.imageGenerationStarted, startedPayload);
17978
+ } catch (error) {
17979
+ return {
17980
+ kind: "failed",
17981
+ message: `Der Auftrag ${input.generationBatchId} konnte nicht protokolliert werden: ${error instanceof Error ? error.message : String(error)}`
17982
+ };
17983
+ }
17984
+ let produced = [];
17985
+ let providerFehler = null;
17986
+ try {
17987
+ const antwort = await provider.generate({
17988
+ callableModelId: capability.callable_model_id,
17989
+ provider: capability.provider,
17990
+ positivePrompt: run.output.positive,
17991
+ negativePrompt: run.output.negative,
17992
+ imageCount: input.imageCount,
17993
+ ...input.aspectRatio ? { aspectRatio: input.aspectRatio } : {},
17994
+ ...input.seeds ? { seeds: input.seeds } : {}
17995
+ });
17996
+ produced = antwort.images;
17997
+ } catch (error) {
17998
+ providerFehler = error instanceof Error ? error.message : String(error);
17999
+ }
18000
+ if (providerFehler || produced.length === 0) {
18001
+ const finishedPayload2 = {
18002
+ generation_batch_id: input.generationBatchId,
18003
+ status: "failed",
18004
+ produced_count: 0,
18005
+ diagnostic: providerFehler ?? "Der Provider hat kein verwertbares Bild geliefert.",
18006
+ finished_at: now().toISOString()
18007
+ };
18008
+ await events.appendEvent(SZENEN_EVENT_TYPES.imageGenerationFinished, finishedPayload2).catch(() => {
18009
+ });
18010
+ return {
18011
+ kind: "failed",
18012
+ message: finishedPayload2.diagnostic
18013
+ };
18014
+ }
18015
+ const persistiert = await batches.persist({
18016
+ runId: input.runId,
18017
+ generationBatchId: input.generationBatchId,
18018
+ images: produced
18019
+ });
18020
+ if (persistiert.kind !== "ok") {
18021
+ const finishedPayload2 = {
18022
+ generation_batch_id: input.generationBatchId,
18023
+ status: "failed",
18024
+ produced_count: 0,
18025
+ diagnostic: persistiert.kind === "error" ? persistiert.message : `Batch ${input.generationBatchId} konnte nicht gespeichert werden (${persistiert.kind}).`,
18026
+ finished_at: now().toISOString()
18027
+ };
18028
+ await events.appendEvent(SZENEN_EVENT_TYPES.imageGenerationFinished, finishedPayload2).catch(() => {
18029
+ });
18030
+ return { kind: "failed", message: finishedPayload2.diagnostic };
18031
+ }
18032
+ const istPartial = produced.length < input.imageCount;
18033
+ const finishedPayload = {
18034
+ generation_batch_id: input.generationBatchId,
18035
+ status: istPartial ? "partial" : "succeeded",
18036
+ item_id: persistiert.value.item_id,
18037
+ produced_count: produced.length,
18038
+ finished_at: now().toISOString()
18039
+ };
18040
+ await events.appendEvent(SZENEN_EVENT_TYPES.imageGenerationFinished, finishedPayload).catch(() => {
18041
+ });
18042
+ return istPartial ? { kind: "partial", batch: persistiert.value, requested: input.imageCount, produced: produced.length } : { kind: "succeeded", batch: persistiert.value };
18043
+ }
18044
+ };
18045
+ }
18046
+
17467
18047
  // src/lib/szenen/flowViewPort.ts
17468
18048
  var defaultContext = {
17469
18049
  async chainFor(contextId) {
@@ -17511,11 +18091,90 @@ function createFlowSzenenViewPort(options) {
17511
18091
  ...options.newRunId ? { newRunId: options.newRunId } : {},
17512
18092
  ...options.now ? { now: options.now } : {}
17513
18093
  });
17514
- const port = { elements, context, runs, derivation };
18094
+ const imageBatchHistory = createHfImageBatchHistory({ channel });
18095
+ const hatHfZugangsdaten = Boolean(options.namespace?.trim() && options.token?.trim());
18096
+ const imageBatchWrite = hatHfZugangsdaten ? createHfImageBatchWrite({
18097
+ namespace: options.namespace,
18098
+ token: options.token,
18099
+ channel,
18100
+ ...options.newBatchImageId ? { newImageId: options.newBatchImageId } : {},
18101
+ ...options.now ? { now: options.now } : {}
18102
+ }) : void 0;
18103
+ const bildProvider = options.imageProvider ?? (options.generateImage ? createFlowImageProvider(options.generateImage) : void 0);
18104
+ const imageGeneration = bildProvider && imageBatchWrite ? createImageGenerationPort({
18105
+ runs: { async getRun(runId) {
18106
+ return runs.getRun(runId);
18107
+ } },
18108
+ provider: bildProvider,
18109
+ batches: createHfImageGenerationBatchPersistence({
18110
+ namespace: options.namespace,
18111
+ token: options.token,
18112
+ channel,
18113
+ ...options.newBatchImageId ? { newImageId: options.newBatchImageId } : {},
18114
+ ...options.now ? { now: options.now } : {}
18115
+ }),
18116
+ events: channel,
18117
+ hasAccess: options.imageProviderHasAccess ?? flowImageProviderHasAccess,
18118
+ ...options.now ? { now: options.now } : {}
18119
+ }) : void 0;
18120
+ const port = { elements, context, runs, derivation, imageBatchHistory };
17515
18121
  if (imageLibrary) port.imageLibrary = imageLibrary;
18122
+ if (imageBatchWrite) port.imageBatchWrite = imageBatchWrite;
18123
+ if (imageGeneration) port.imageGeneration = imageGeneration;
17516
18124
  return port;
17517
18125
  }
17518
18126
 
18127
+ // src/lib/flowBootstrapTarget.ts
18128
+ var PROD_NAMESPACE = "app.art-by-rolands.de/";
18129
+ var DEV_NAMESPACE = "dev-app.art-by-rolands.de/";
18130
+ var SELECTABLE_NAMESPACES = [PROD_NAMESPACE, DEV_NAMESPACE];
18131
+ var DEFAULT_NAMESPACE = PROD_NAMESPACE;
18132
+ function environmentForNamespace(namespace) {
18133
+ if (!namespace) return null;
18134
+ if (namespace === DEV_NAMESPACE) return "dev";
18135
+ if (namespace === PROD_NAMESPACE) return "prod";
18136
+ return null;
18137
+ }
18138
+ function normalizeSelectableNamespace(namespace) {
18139
+ return namespace && SELECTABLE_NAMESPACES.includes(namespace) ? namespace : DEFAULT_NAMESPACE;
18140
+ }
18141
+ function resolveBootstrapTarget(input) {
18142
+ const { hostNamespace, serverNamespace, isProbingServer = false } = input;
18143
+ if (hostNamespace) {
18144
+ return {
18145
+ source: "host",
18146
+ phase: "ready",
18147
+ ready: true,
18148
+ namespace: hostNamespace,
18149
+ environment: environmentForNamespace(hostNamespace)
18150
+ };
18151
+ }
18152
+ if (serverNamespace) {
18153
+ return {
18154
+ source: "server",
18155
+ phase: "ready",
18156
+ ready: true,
18157
+ namespace: serverNamespace,
18158
+ environment: environmentForNamespace(serverNamespace)
18159
+ };
18160
+ }
18161
+ if (isProbingServer) {
18162
+ return { source: "server", phase: "probing", ready: false, namespace: null, environment: null };
18163
+ }
18164
+ const { selectedNamespace, isStartDialogOpen = false } = input;
18165
+ if (isStartDialogOpen) {
18166
+ return { source: "dialog", phase: "awaiting-choice", ready: false, namespace: null, environment: null };
18167
+ }
18168
+ const namespace = normalizeSelectableNamespace(selectedNamespace);
18169
+ return {
18170
+ source: "dialog",
18171
+ phase: "ready",
18172
+ ready: true,
18173
+ namespace,
18174
+ environment: environmentForNamespace(namespace)
18175
+ };
18176
+ }
18177
+
17519
18178
  // src/components/AvatarArchitectApp.tsx
17520
18179
  var import_jsx_runtime45 = require("react/jsx-runtime");
17521
18180
  function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, initialHfToken, hfNamespace, allowDevNamespace, requireWorkspaceSelection = false, serverBaseUrl, onFetchServerProjects, onServerSave, onServerLoad, onServerDelete }) {
@@ -17542,31 +18201,38 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
17542
18201
  const [hfTokenInput, setHfTokenInput] = (0, import_react47.useState)(initialHfToken || "");
17543
18202
  const [isLoadingFromHF, setIsLoadingFromHF] = (0, import_react47.useState)(false);
17544
18203
  const [hfNamespaceLocal, setHfNamespaceLocal] = (0, import_react47.useState)(() => {
17545
- const KNOWN = ["app.art-by-rolands.de/", "dev-app.art-by-rolands.de/"];
17546
- const DEFAULT = "app.art-by-rolands.de/";
17547
18204
  try {
17548
- const stored = localStorage.getItem("aa-hf-namespace");
17549
- if (stored && KNOWN.includes(stored)) return stored;
17550
- localStorage.setItem("aa-hf-namespace", DEFAULT);
17551
- return DEFAULT;
18205
+ return normalizeSelectableNamespace(localStorage.getItem("aa-hf-namespace"));
17552
18206
  } catch {
17553
- return DEFAULT;
18207
+ return DEFAULT_NAMESPACE;
17554
18208
  }
17555
18209
  });
17556
18210
  const [hfNamespaceFromServer, setHfNamespaceFromServer] = (0, import_react47.useState)(null);
18211
+ const [serverProbeFailed, setServerProbeFailed] = (0, import_react47.useState)(false);
17557
18212
  (0, import_react47.useEffect)(() => {
17558
18213
  if (hfNamespace !== void 0 || !serverBaseUrl) return;
17559
18214
  fetch(`${serverBaseUrl}/api/status`).then((r) => r.json()).then((d) => {
17560
18215
  if (typeof d.hfNamespace === "string" && d.hfNamespace) setHfNamespaceFromServer(d.hfNamespace);
17561
- }).catch(() => {
17562
- });
18216
+ else setServerProbeFailed(true);
18217
+ }).catch(() => setServerProbeFailed(true));
17563
18218
  }, [hfNamespace, serverBaseUrl]);
17564
- const effectiveNamespace = hfNamespace ?? hfNamespaceFromServer ?? hfNamespaceLocal;
17565
- const szenenPort = (0, import_react47.useMemo)(() => hfToken ? createFlowSzenenViewPort({
18219
+ const [targetConfirmed, setTargetConfirmed] = (0, import_react47.useState)(false);
18220
+ const isDialogTarget = hfNamespace === void 0 && hfNamespaceFromServer === null && (!serverBaseUrl || serverProbeFailed);
18221
+ const isProbingServer = hfNamespace === void 0 && !!serverBaseUrl && hfNamespaceFromServer === null && !serverProbeFailed;
18222
+ const bootstrapTarget = resolveBootstrapTarget({
18223
+ hostNamespace: hfNamespace,
18224
+ serverNamespace: hfNamespaceFromServer,
18225
+ isProbingServer,
18226
+ selectedNamespace: hfNamespaceLocal,
18227
+ isStartDialogOpen: isDialogTarget && !targetConfirmed
18228
+ });
18229
+ const effectiveNamespace = bootstrapTarget.namespace ?? "";
18230
+ const szenenPort = (0, import_react47.useMemo)(() => hfToken && effectiveNamespace ? createFlowSzenenViewPort({
17566
18231
  generateText: onGeneratePrompt,
17567
18232
  token: hfToken,
17568
- namespace: effectiveNamespace
17569
- }) : null, [hfToken, effectiveNamespace, onGeneratePrompt]);
18233
+ namespace: effectiveNamespace,
18234
+ generateImage: onGenerateImage
18235
+ }) : null, [hfToken, effectiveNamespace, onGeneratePrompt, onGenerateImage]);
17570
18236
  const {
17571
18237
  state: hfState,
17572
18238
  isLoading: isHfRefreshing,
@@ -17640,6 +18306,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
17640
18306
  } catch {
17641
18307
  }
17642
18308
  setLayoutChoice(choice);
18309
+ setTargetConfirmed(true);
17643
18310
  setShowStart(false);
17644
18311
  };
17645
18312
  const [nodes, setNodes] = (0, import_react47.useState)([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
@@ -18401,45 +19068,6 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
18401
19068
  const all = Object.entries(merged).flatMap(([cat, tags]) => tags.map((t) => ({ ...t, category: cat })));
18402
19069
  return { by_category: merged, all };
18403
19070
  };
18404
- if (requireWorkspaceSelection) {
18405
- if (!hfToken) {
18406
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("main", { className: "flow-workspace-picker", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("section", { className: "flow-workspace-picker__card", role: "alert", children: "HF-Token fehlt \u2014 User und Context koennen nicht aus der Tag-Base geladen werden." }) });
18407
- }
18408
- if (tagBaseResult.isLoading) {
18409
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("main", { className: "flow-workspace-picker", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("section", { className: "flow-workspace-picker__card", children: [
18410
- "Lade User und Contexts aus ",
18411
- effectiveNamespace,
18412
- "\u2026"
18413
- ] }) });
18414
- }
18415
- if (tagBaseResult.error) {
18416
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("main", { className: "flow-workspace-picker", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("section", { className: "flow-workspace-picker__card", role: "alert", children: [
18417
- "Tag-Base ",
18418
- effectiveNamespace,
18419
- " konnte nicht geladen werden: ",
18420
- tagBaseResult.error
18421
- ] }) });
18422
- }
18423
- if (!tagBaseResult.tagBase) {
18424
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("main", { className: "flow-workspace-picker", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("section", { className: "flow-workspace-picker__card", role: "alert", children: [
18425
- "Im Namespace ",
18426
- effectiveNamespace,
18427
- " fehlt tags-state-*.zip. Der Server muss zuerst die Tag-Base erzeugen."
18428
- ] }) });
18429
- }
18430
- if (tagBaseResult.needsPicker) {
18431
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
18432
- FlowWorkspacePicker,
18433
- {
18434
- tagBase: tagBaseResult.tagBase,
18435
- selectedUserId: tagBaseResult.selectedUserId,
18436
- message: tagBaseResult.pickerMessage,
18437
- onSelectUser: tagBaseResult.selectUser,
18438
- onSelectContext: tagBaseResult.selectContext
18439
- }
18440
- );
18441
- }
18442
- }
18443
19071
  if (isFullscreen && currentResult?.base64) {
18444
19072
  const fsRaw = currentResult.fullBase64 || currentResult.base64;
18445
19073
  const fsBase64 = fsRaw.startsWith("data:") ? fsRaw : `data:image/png;base64,${fsRaw}`;
@@ -18583,7 +19211,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
18583
19211
  }
18584
19212
  )
18585
19213
  ] }),
18586
- !hfNamespace && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center gap-3 w-full", children: [
19214
+ isDialogTarget && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center gap-3 w-full", children: [
18587
19215
  /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "text-white/25 text-[10px] uppercase tracking-widest font-bold flex-shrink-0", children: "State:" }),
18588
19216
  ["app.art-by-rolands.de/", "dev-app.art-by-rolands.de/"].map((ns, i) => /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
18589
19217
  "button",
@@ -18668,7 +19296,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
18668
19296
  "button",
18669
19297
  {
18670
19298
  type: "button",
18671
- disabled: !szenenPort,
19299
+ disabled: !szenenPort || !bootstrapTarget.ready,
18672
19300
  onClick: () => setShowSzenen(true),
18673
19301
  children: [
18674
19302
  /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "material-symbols-outlined", children: "movie" }),
@@ -18703,6 +19331,48 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
18703
19331
  ] })
18704
19332
  ] });
18705
19333
  }
19334
+ if (requireWorkspaceSelection) {
19335
+ if (!bootstrapTarget.ready) {
19336
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("main", { className: "flow-workspace-picker", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("section", { className: "flow-workspace-picker__card", children: "Zielraum wird bestimmt\u2026" }) });
19337
+ }
19338
+ if (!hfToken) {
19339
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("main", { className: "flow-workspace-picker", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("section", { className: "flow-workspace-picker__card", role: "alert", children: "HF-Token fehlt \u2014 User und Context koennen nicht aus der Tag-Base geladen werden." }) });
19340
+ }
19341
+ if (tagBaseResult.isLoading) {
19342
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("main", { className: "flow-workspace-picker", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("section", { className: "flow-workspace-picker__card", children: [
19343
+ "Lade User und Contexts aus ",
19344
+ effectiveNamespace,
19345
+ "\u2026"
19346
+ ] }) });
19347
+ }
19348
+ if (tagBaseResult.error) {
19349
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("main", { className: "flow-workspace-picker", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("section", { className: "flow-workspace-picker__card", role: "alert", children: [
19350
+ "Tag-Base ",
19351
+ effectiveNamespace,
19352
+ " konnte nicht geladen werden: ",
19353
+ tagBaseResult.error
19354
+ ] }) });
19355
+ }
19356
+ if (!tagBaseResult.tagBase) {
19357
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("main", { className: "flow-workspace-picker", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("section", { className: "flow-workspace-picker__card", role: "alert", children: [
19358
+ "Im Namespace ",
19359
+ effectiveNamespace,
19360
+ " fehlt tags-state-*.zip. Der Server muss zuerst die Tag-Base erzeugen."
19361
+ ] }) });
19362
+ }
19363
+ if (tagBaseResult.needsPicker) {
19364
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
19365
+ FlowWorkspacePicker,
19366
+ {
19367
+ tagBase: tagBaseResult.tagBase,
19368
+ selectedUserId: tagBaseResult.selectedUserId,
19369
+ message: tagBaseResult.pickerMessage,
19370
+ onSelectUser: tagBaseResult.selectUser,
19371
+ onSelectContext: tagBaseResult.selectContext
19372
+ }
19373
+ );
19374
+ }
19375
+ }
18706
19376
  if (layoutChoice === "mobile" || layoutChoice === "mobile-desktop") {
18707
19377
  const mdMode = layoutChoice === "mobile-desktop";
18708
19378
  const mdScale = mdMode ? window.innerWidth / 430 : 1;