@rslsp1/fa-app-tools 7.2.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -96,9 +96,9 @@ function bildEndungFuer(mimeType) {
96
96
  }
97
97
 
98
98
  // src/lib/hfEventLog.ts
99
- function treeUrl(namespace, subdir = "") {
99
+ function treeUrl(namespace, subdir = "", revision = "main") {
100
100
  const parts = [namespace.replace(/\/$/, ""), subdir].filter(Boolean).join("/");
101
- return `${HF_BASE}/api/datasets/${HF_REPO}/tree/main${parts ? "/" + parts : ""}`;
101
+ return `${HF_BASE}/api/datasets/${HF_REPO}/tree/${encodeURIComponent(revision)}${parts ? "/" + parts : ""}`;
102
102
  }
103
103
  var DROSSEL_VERSUCHE = 4;
104
104
  var DROSSEL_BASIS_MS = 400;
@@ -131,8 +131,8 @@ async function mitGeduld(anfrage) {
131
131
  }
132
132
  return letzte;
133
133
  }
134
- async function hfListDir(namespace, subdir, token) {
135
- const res = await mitGeduld(() => holeMitZeitlimit(treeUrl(namespace, subdir), {
134
+ async function hfListDir(namespace, subdir, token, revision = "main") {
135
+ const res = await mitGeduld(() => holeMitZeitlimit(treeUrl(namespace, subdir, revision), {
136
136
  headers: { Authorization: `Bearer ${token}` }
137
137
  }));
138
138
  if (res.status === 404) return [];
@@ -161,17 +161,46 @@ function toBase64(text) {
161
161
  bytes.forEach((b) => binary += String.fromCharCode(b));
162
162
  return btoa(binary);
163
163
  }
164
- async function hfUploadSmallFile(repoPath, content, token, summary = `Update ${repoPath}`) {
164
+ var HfPreconditionFailedError = class extends Error {
165
+ constructor(message) {
166
+ super(message);
167
+ this.name = "HfPreconditionFailed";
168
+ }
169
+ };
170
+ async function hfRepoHead(token, revision = "main") {
171
+ const res = await mitGeduld(() => holeMitZeitlimit(
172
+ `${HF_BASE}/api/datasets/${HF_REPO}/revision/${encodeURIComponent(revision)}`,
173
+ { headers: { Authorization: `Bearer ${token}` } }
174
+ ));
175
+ if (!res.ok) {
176
+ throw new Error(`HF revision failed: ${res.status} ${res.statusText} (${revision})`);
177
+ }
178
+ const daten = await res.json();
179
+ if (typeof daten.sha !== "string" || daten.sha.length === 0) {
180
+ throw new Error(
181
+ `HF revision ${revision}: die Antwort traegt kein "sha". Erhaltene Felder: ${Object.keys(daten || {}).join(", ") || "(keine)"}.`
182
+ );
183
+ }
184
+ return daten.sha;
185
+ }
186
+ async function hfUploadSmallFile(repoPath, content, token, summary = `Update ${repoPath}`, options) {
187
+ const kopf = { summary, description: "" };
188
+ if (options?.parentCommit) kopf.parentCommit = options.parentCommit;
165
189
  const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
166
190
  method: "POST",
167
191
  headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
168
192
  body: [
169
- JSON.stringify({ key: "header", value: { summary, description: "" } }),
193
+ JSON.stringify({ key: "header", value: kopf }),
170
194
  JSON.stringify({ key: "file", value: { path: repoPath, encoding: "base64", content: toBase64(content) } })
171
195
  ].join("\n")
172
196
  });
173
197
  if (!res.ok) {
174
198
  const err = await res.text().catch(() => "");
199
+ if (options?.parentCommit && (res.status === 412 || res.status === 409)) {
200
+ throw new HfPreconditionFailedError(
201
+ `HF commit abgelehnt: Der Branch hat sich seit ${options.parentCommit} bewegt (${res.status}). Es wurde nichts geschrieben.`
202
+ );
203
+ }
175
204
  throw new Error(`HF upload failed: ${res.status} \u2014 ${err.slice(0, 200)}`);
176
205
  }
177
206
  }
@@ -221,8 +250,8 @@ async function mitBegrenzterParallelitaet(count, limit, task) {
221
250
  await Promise.all(Array.from({ length: Math.min(limit, count) }, worker));
222
251
  if (fehlerAufgetreten) throw ersterFehler;
223
252
  }
224
- async function loadPendingEvents(namespace, token, sinceTs, knownPaths) {
225
- const files = await hfListDir(namespace, "events", token);
253
+ async function loadPendingEvents(namespace, token, sinceTs, knownPaths, revision = "main") {
254
+ const files = await hfListDir(namespace, "events", token, revision);
226
255
  const pending = files.filter((f) => f.type === "file" && tsFromEventPath(f.path) > sinceTs && !knownPaths?.has(f.path)).sort((a, b) => a.path.localeCompare(b.path));
227
256
  if (!pending.length) return [];
228
257
  const raw = new Array(pending.length);
@@ -267,7 +296,13 @@ async function writeHFEvent(namespace, token, type, payload, prevTs, options) {
267
296
  const suffix = options?.ordered ? `${String(++writeSeq % 1e6).padStart(6, "0")}${uuid}` : uuid;
268
297
  const filename = `${tsToFilename(ts)}_${suffix}.json`;
269
298
  const repoPath = `${namespace}events/${filename}`;
270
- await hfUploadSmallFile(repoPath, JSON.stringify(event, null, 2), token, `Event: ${type}`);
299
+ await hfUploadSmallFile(
300
+ repoPath,
301
+ JSON.stringify(event, null, 2),
302
+ token,
303
+ `Event: ${type}`,
304
+ options?.parentCommit ? { parentCommit: options.parentCommit } : void 0
305
+ );
271
306
  return event;
272
307
  }
273
308
 
@@ -407,7 +442,11 @@ function capabilitiesOf(port) {
407
442
  imageBatchHistory: typeof port.imageBatchHistory?.listBatchesForPromptRun === "function",
408
443
  imageOrderHistory: typeof port.imageBatchHistory?.listOrdersForPromptRun === "function",
409
444
  imageBatchWrite: typeof port.imageBatchWrite?.importBatch === "function",
410
- imageGeneration: typeof port.imageGeneration?.startFromPromptRun === "function"
445
+ imageGeneration: typeof port.imageGeneration?.startFromPromptRun === "function",
446
+ imageJobs: typeof port.imageJobs?.claimAndRun === "function",
447
+ // P1-A: die herkunftsuebergreifende Prompt-Sicht. Ein Host ohne sie
448
+ // verliert nur diesen Bereich — nicht die drei bestehenden Einstiege.
449
+ promptOverview: typeof port.runs.listAllRuns === "function"
411
450
  };
412
451
  }
413
452
  function unsupportedReason(capability) {
@@ -3494,37 +3533,52 @@ function formatSchemaErrors(errors) {
3494
3533
  }
3495
3534
 
3496
3535
  // src/features/szenen/szenenViewModel.ts
3497
- var KEINE_BAUSTEINE = [
3498
- "scene",
3536
+ var BAUSTEIN_TYPES = [
3537
+ "scene_component",
3538
+ "style",
3539
+ "shot_concept",
3540
+ "prompt_fragment",
3541
+ "prompt_policy"
3542
+ ];
3543
+ var ERWEITERT_REIHENFOLGE = [
3499
3544
  "scene_setup",
3500
- "shot"
3545
+ "shot",
3546
+ "camera_preset",
3547
+ "camera_pattern",
3548
+ "target_model",
3549
+ "scene_derivation_policy",
3550
+ "sequence"
3501
3551
  ];
3502
- var BAUSTEIN_TYPES = SZENEN_ELEMENT_TYPES.filter((typ) => !KEINE_BAUSTEINE.includes(typ));
3552
+ var ERWEITERT_TYPES = (() => {
3553
+ const gehoertHierher = (typ) => typ !== "scene" && !BAUSTEIN_TYPES.includes(typ);
3554
+ const bekannt = ERWEITERT_REIHENFOLGE.filter(
3555
+ (typ) => SZENEN_ELEMENT_TYPES.includes(typ) && gehoertHierher(typ)
3556
+ );
3557
+ const rest = SZENEN_ELEMENT_TYPES.filter(
3558
+ (typ) => gehoertHierher(typ) && !bekannt.includes(typ)
3559
+ );
3560
+ return [...bekannt, ...rest];
3561
+ })();
3503
3562
  var ALLE_BEREICHE = [
3504
3563
  {
3505
- id: "derive",
3506
- title: "0 \xB7 Rekonstruktion",
3507
- description: "Aus einem Bild oder Prompt einen Szenenentwurf vorschlagen lassen."
3508
- },
3509
- {
3510
- id: "elements",
3511
- title: "1 \xB7 Bausteine",
3512
- description: "Figuren, Orte, Stile und Regeln f\xFCr sp\xE4tere Bildprompts pflegen."
3564
+ id: "scene",
3565
+ title: "Szenen",
3566
+ description: "Szenen anlegen, aus Bild oder Text ableiten und daraus Prompts starten."
3513
3567
  },
3514
3568
  {
3515
- id: "scene",
3516
- title: "2 \xB7 Szene",
3517
- description: "Aus Bausteinen eine konkrete Szene und ihre Beziehungen bauen."
3569
+ id: "prompts",
3570
+ title: "Prompts",
3571
+ description: "Alle erzeugten Prompts mit ihren Bildern \u2014 unabhaengig davon, ueber welchen Weg sie entstanden sind."
3518
3572
  },
3519
3573
  {
3520
- id: "setup",
3521
- title: "3 \xB7 Bildaufbau",
3522
- description: "Stil, Komposition und Kamera f\xFCr das sp\xE4tere Bild festlegen."
3574
+ id: "elements",
3575
+ title: "Bausteine",
3576
+ description: "Figuren, Orte, Stile, Prompt-Bausteine und Prompt-Regeln pflegen."
3523
3577
  },
3524
3578
  {
3525
- id: "shot",
3526
- title: "4 \xB7 Prompt",
3527
- description: "Bildprompt erzeugen, Varianten vergleichen und als Item speichern."
3579
+ id: "advanced",
3580
+ title: "Erweitert",
3581
+ description: "Optionale Pr\xE4zisierung: Bildaufbau, Shot, Kameras, Zielmodelle, Regeln, Sequenzen."
3528
3582
  },
3529
3583
  {
3530
3584
  id: "media",
@@ -3534,16 +3588,12 @@ var ALLE_BEREICHE = [
3534
3588
  ];
3535
3589
  function areasFor(port) {
3536
3590
  const faehig = capabilitiesOf(port);
3537
- return ALLE_BEREICHE.filter((bereich) => {
3538
- if (bereich.id === "media") return faehig.media;
3539
- if (bereich.id === "derive") return faehig.derivation;
3540
- return true;
3541
- });
3591
+ return ALLE_BEREICHE.filter((bereich) => bereich.id !== "media" || faehig.media);
3542
3592
  }
3543
3593
  function resolveArea(port, gewuenscht) {
3544
3594
  const verfuegbar = areasFor(port);
3545
3595
  if (gewuenscht && verfuegbar.some((b) => b.id === gewuenscht)) return gewuenscht;
3546
- return verfuegbar.find((b) => b.id === "elements")?.id ?? verfuegbar[0].id;
3596
+ return verfuegbar.find((b) => b.id === "scene")?.id ?? verfuegbar[0].id;
3547
3597
  }
3548
3598
  function noticeFor(ergebnis, was) {
3549
3599
  switch (ergebnis.kind) {
@@ -3557,13 +3607,13 @@ function noticeFor(ergebnis, was) {
3557
3607
  };
3558
3608
  }
3559
3609
  case "rejected": {
3560
- const fehler4 = ergebnis.errors ?? [];
3610
+ const fehler5 = ergebnis.errors ?? [];
3561
3611
  const ungeprueft = ergebnis.unchecked ?? [];
3562
3612
  return {
3563
3613
  tone: "rejected",
3564
- text: `${was}: abgelehnt \u2014 ${fehler4.length} Befund${fehler4.length === 1 ? "" : "e"}.`,
3614
+ text: `${was}: abgelehnt \u2014 ${fehler5.length} Befund${fehler5.length === 1 ? "" : "e"}.`,
3565
3615
  details: [
3566
- ...fehler4.map((f) => `${f.path ?? "(ohne Pfad)"}: ${f.message ?? "ohne Meldung"}`),
3616
+ ...fehler5.map((f) => `${f.path ?? "(ohne Pfad)"}: ${f.message ?? "ohne Meldung"}`),
3567
3617
  // Nicht verschweigen: was nicht geprüft wurde, ist nicht in Ordnung,
3568
3618
  // sondern ungeprüft.
3569
3619
  ...ungeprueft.map((u) => `ungepr\xFCft: ${u}`)
@@ -5068,158 +5118,6 @@ function resolverFromQueries(queries, contexts) {
5068
5118
  var isProjectionCurrent = (state) => state.status === "current";
5069
5119
  var isOk = (result) => result.kind === "ok";
5070
5120
 
5071
- // src/lib/szenen/itemDerivation.ts
5072
- var SZENEN_ITEM_PREFIX = "szn-";
5073
- function itemIdForRun(runId) {
5074
- return `${SZENEN_ITEM_PREFIX}${runId}`;
5075
- }
5076
- function isSzenenItemId(itemId) {
5077
- return itemId.startsWith(SZENEN_ITEM_PREFIX);
5078
- }
5079
- function checkSzenenBatchOrigin(candidate) {
5080
- if (!istText3(candidate.prompt_run_id)) {
5081
- return {
5082
- kind: "rejected",
5083
- failure: { field: "prompt_run_id", message: '"prompt_run_id" fehlt oder ist leer.' }
5084
- };
5085
- }
5086
- if (!istText3(candidate.generation_batch_id)) {
5087
- return {
5088
- kind: "rejected",
5089
- failure: {
5090
- field: "generation_batch_id",
5091
- message: '"generation_batch_id" fehlt oder ist leer.'
5092
- }
5093
- };
5094
- }
5095
- if (candidate.source_kind !== "generated_in_app" && candidate.source_kind !== "external_import") {
5096
- return {
5097
- kind: "rejected",
5098
- failure: {
5099
- field: "source_kind",
5100
- message: `"source_kind" ist unbekannt: ${JSON.stringify(candidate.source_kind)}. Erlaubt: generated_in_app, external_import.`
5101
- }
5102
- };
5103
- }
5104
- const round = typeof candidate.round === "number" && Number.isInteger(candidate.round) ? candidate.round : void 0;
5105
- return {
5106
- kind: "ok",
5107
- value: {
5108
- prompt_run_id: candidate.prompt_run_id,
5109
- generation_batch_ids: [candidate.generation_batch_id],
5110
- source_kind: candidate.source_kind,
5111
- ...round !== void 0 ? { round } : {}
5112
- }
5113
- };
5114
- }
5115
- function mergeSzenenBatchOrigin(bestehend, neu) {
5116
- if (!bestehend) return neu;
5117
- const ids = [...bestehend.generation_batch_ids];
5118
- for (const id of neu.generation_batch_ids) {
5119
- if (!ids.includes(id)) ids.push(id);
5120
- }
5121
- return {
5122
- prompt_run_id: bestehend.prompt_run_id,
5123
- generation_batch_ids: ids,
5124
- source_kind: bestehend.source_kind,
5125
- ...bestehend.round !== void 0 ? { round: bestehend.round } : {}
5126
- };
5127
- }
5128
- var DEFAULT_FSK_LEVEL = "FSK16";
5129
- var istText3 = (v) => typeof v === "string" && v.trim().length > 0;
5130
- function roundOfRun(run, runsOfShot) {
5131
- return runsOfShot.findIndex((kandidat) => kandidat.run_id === run.run_id) + 1;
5132
- }
5133
- function deriveLineage(ownItemId, ownRound, existingItems) {
5134
- const kandidaten = existingItems.filter((item) => item.valid && item.item_id !== ownItemId).sort((a, b) => a.sequence - b.sequence);
5135
- const elternteil = kandidaten[kandidaten.length - 1];
5136
- if (!elternteil) {
5137
- return {
5138
- parent_item_id: null,
5139
- lineage_root_id: ownItemId,
5140
- generation: 0,
5141
- out_of_order: false
5142
- };
5143
- }
5144
- return {
5145
- parent_item_id: elternteil.item_id,
5146
- lineage_root_id: elternteil.lineage_root_id,
5147
- generation: elternteil.generation + 1,
5148
- // Der Elternteil kommt aus einer spaeteren Runde: rueckwaerts gespeichert.
5149
- // Kein Fehler, aber nichts, was stillschweigend passieren darf.
5150
- out_of_order: elternteil.round >= ownRound
5151
- };
5152
- }
5153
- function deriveItemFromRun(input) {
5154
- const { run, runsOfShot, existingItems, savedAt } = input;
5155
- if (run.status === "failed") {
5156
- return {
5157
- kind: "rejected",
5158
- failure: {
5159
- class: "run_failed",
5160
- message: `Der Lauf ${run.run_id} ist gescheitert (${run.failure ? run.failure.class : "unbekannt"}) und traegt keinen Prompt. Aus einem Fehlerlauf entsteht kein Item.`,
5161
- run_failure: run.failure
5162
- }
5163
- };
5164
- }
5165
- if (!run.output || !istText3(run.output.positive)) {
5166
- return {
5167
- kind: "rejected",
5168
- failure: {
5169
- class: "run_without_output",
5170
- message: `Der Lauf ${run.run_id} ist als erfolgreich protokolliert, traegt aber keinen positiven Prompt. Das ist ein Datenfehler im Lauf, kein Item.`
5171
- }
5172
- };
5173
- }
5174
- const round = roundOfRun(run, runsOfShot);
5175
- if (round === 0) {
5176
- return {
5177
- kind: "rejected",
5178
- failure: {
5179
- class: "run_not_among_siblings",
5180
- message: `Der Lauf ${run.run_id} ist unter den ${runsOfShot.length} Laeufen des Shots ${run.shot_version_id} nicht enthalten. Ohne seinen Platz gaebe es keine Rundennummer.`
5181
- }
5182
- };
5183
- }
5184
- const item_id = itemIdForRun(run.run_id);
5185
- const lineage = deriveLineage(item_id, round, existingItems);
5186
- const negative = istText3(run.output.negative) ? run.output.negative : null;
5187
- const title = istText3(input.title) ? input.title : istText3(input.shotTitle) ? `${input.shotTitle} \xB7 Runde ${round}` : `Szenen-Lauf ${round}`;
5188
- return {
5189
- kind: "ok",
5190
- value: {
5191
- item_id,
5192
- title,
5193
- content: run.output.positive,
5194
- negative_prompt: negative,
5195
- // `feedback` ist Pflicht (D-55) und ist genau die Erlaeuterung fuer den
5196
- // Menschen — also gehoert sie in das Feld fuer Notizen.
5197
- notes: istText3(input.notes) ? input.notes : run.output.feedback,
5198
- rating: typeof input.rating === "number" ? input.rating : 0,
5199
- fsk_level: istText3(input.fskLevel) ? input.fskLevel : DEFAULT_FSK_LEVEL,
5200
- tags: [],
5201
- szenen: {
5202
- prompt_run_id: run.run_id,
5203
- shot_version_id: run.shot_version_id,
5204
- shot_element_id: run.shot_element_id,
5205
- scene_setup_version_id: run.scene_setup_version_id,
5206
- camera_id: run.camera_id,
5207
- target_model_version_id: run.target_model_version_id,
5208
- provider: run.provider,
5209
- model_id: run.model_id,
5210
- run_created_at: run.created_at,
5211
- round,
5212
- created_from_draft_id: null,
5213
- saved_at: savedAt
5214
- },
5215
- lineage,
5216
- // Bestandsformat: `LibraryTab` baut den Baum aus `metadata.relationships`.
5217
- // Ein Szenen-Item ohne diese Kante waere im Baum unsichtbar.
5218
- relationships: lineage.parent_item_id ? [{ type: "parent", id: lineage.parent_item_id, role: "source_A" }] : []
5219
- }
5220
- };
5221
- }
5222
-
5223
5121
  // src/lib/szenen/elementEvents.ts
5224
5122
  var SZENEN_EVENT_PREFIX = "szenen.";
5225
5123
  var SZENEN_EVENT_TYPES = {
@@ -5337,7 +5235,60 @@ var SZENEN_EVENT_TYPES = {
5337
5235
  * Der Elementreducer ignoriert auch diesen Typ (`default:`-Zweig); gelesen
5338
5236
  * wird mit `collectImageBatches` (`imageBatchStore.ts`).
5339
5237
  */
5340
- imageBatchPersisted: "szenen.image_batch_persisted"
5238
+ imageBatchPersisted: "szenen.image_batch_persisted",
5239
+ /**
5240
+ * Ein vorbereiteter Promptvorgang — der Durchstich „Szene → ChatGPT → Lauf".
5241
+ *
5242
+ * Der bestehende Promptpfad ruft das Modell selbst
5243
+ * (`runPromptDerivation()`); ein aufrufendes Chatmodell formuliert dagegen
5244
+ * **zwischen** zwei Werkzeugaufrufen. Damit zerfaellt der eine Vorgang in
5245
+ * zwei, und der zweite braucht genau den Stand, den der erste eingefroren
5246
+ * hat — sonst loeste der Abschluss die inzwischen geaenderte Szene neu auf
5247
+ * und der Lauf behauptete eine Herkunft, die er nicht hat (D-22).
5248
+ *
5249
+ * Deshalb ein eigenes Ereignis und **kein** fluechtiger Prozesszustand: Der
5250
+ * Snapshot ueberlebt Neustart, Hostwechsel und Sitzungsende, weil er im
5251
+ * selben autoritativen Log liegt wie der Lauf, der daraus entsteht.
5252
+ *
5253
+ * Der Elementreducer ignoriert diesen Typ (`default:`-Zweig); gelesen wird
5254
+ * mit `collectPromptOperations` (`scenePromptOperation.ts`).
5255
+ */
5256
+ promptOperationPrepared: "szenen.prompt_operation_prepared",
5257
+ /**
5258
+ * Ein Bildauftrag wartet auf einen ausfuehrenden Flow-Host.
5259
+ *
5260
+ * Die vorhandene Auftragshistorie (`imageGenerationStarted`) beginnt
5261
+ * **unmittelbar vor** dem Provideraufruf und kennt deshalb keinen wartenden
5262
+ * Auftrag. Genau der entsteht hier: Ein Chatmodell beauftragt ueber MCP ein
5263
+ * Bild, der Auftrag wird dauerhaft abgelegt, und der Werkzeugaufruf endet
5264
+ * sofort mit seiner Kennung — er haelt keine Antwort minutenlang offen.
5265
+ *
5266
+ * Traegt **keinen** Prompt: Der steht im referenzierten Lauf, und ihn hier
5267
+ * zu kopieren waere eine zweite Wahrheit ueber denselben eingefrorenen Text
5268
+ * (D-103).
5269
+ */
5270
+ imageJobRequested: "szenen.image_job_requested",
5271
+ /**
5272
+ * Ein Flow-Host beansprucht einen wartenden Bildauftrag.
5273
+ *
5274
+ * Ein append-only Log kennt kein Lock: Zwei Tabs koennen beide anhaengen.
5275
+ * Deshalb ist die Uebernahme **nicht** der Append, sondern das Ergebnis
5276
+ * einer deterministischen Schiedsregel darueber (`winningClaimOf`) — der
5277
+ * erste Anspruch in Logreihenfolge gewinnt, und jeder Host liest nach
5278
+ * seinem Append erneut, bevor er einen bezahlten Provideraufruf startet.
5279
+ * Ein unterlegener Anspruch bleibt sichtbar stehen; er wird nicht geloescht,
5280
+ * weil das Log das nicht kann und weil der Versuch ein Beleg ist.
5281
+ */
5282
+ imageJobClaimed: "szenen.image_job_claimed",
5283
+ /**
5284
+ * Der Bildauftrag ist erledigt — mit dem Ausgang, den der Host gesehen hat.
5285
+ *
5286
+ * Getrennt von `imageGenerationFinished`: Jenes gehoert zum bezahlten
5287
+ * Provideraufruf, dieses zur Zustellung. Ein Auftrag kann fehlschlagen,
5288
+ * **bevor** ein Provider gerufen wurde (abgelehntes Profil), und dann gibt
5289
+ * es kein `imageGenerationFinished`, das den Wartenden erloesen wuerde.
5290
+ */
5291
+ imageJobSettled: "szenen.image_job_settled"
5341
5292
  };
5342
5293
  function sortForReplay(events) {
5343
5294
  return [...events].sort((a, b) => {
@@ -5605,53 +5556,290 @@ function collectPromptRuns(events) {
5605
5556
  function findPromptRun(events, runId) {
5606
5557
  return collectPromptRuns(events).find((run) => run.run_id === runId) ?? null;
5607
5558
  }
5608
-
5609
- // src/lib/szenen/seedListe.ts
5610
- var DEFAULT_SEED_VORLAGE = {
5611
- name: "Default",
5612
- seeds: "1; 10; 100; 1000",
5613
- builtin: true
5614
- };
5615
- function parseSeedListe(eingabe) {
5616
- if (!eingabe) return [];
5617
- return eingabe.split(/[,;]/).map((teil) => teil.trim()).filter((teil) => teil.length > 0);
5618
- }
5619
- function formatSeedListe(tokens) {
5620
- return tokens.join("; ");
5559
+ var istKennung = (wert) => typeof wert === "string" && wert.trim().length > 0;
5560
+ function groupOf(run) {
5561
+ const quelle = sourceOf(run);
5562
+ if (quelle.kind === "published") {
5563
+ return istKennung(quelle.shot_version_id) ? {
5564
+ kind: "published",
5565
+ key: `published:${quelle.shot_version_id}`,
5566
+ shot_version_id: quelle.shot_version_id
5567
+ } : {
5568
+ kind: "unresolved",
5569
+ key: `run:${run.run_id}`,
5570
+ reason: `Der Lauf ${run.run_id} gilt als ver\xF6ffentlichte Herkunft, tr\xE4gt aber keine shot_version_id. Er steht deshalb f\xFCr sich; eine Zuordnung w\xE4re geraten.`
5571
+ };
5572
+ }
5573
+ if (quelle.kind === "draft_chain") {
5574
+ return istKennung(quelle.chain_id) ? { kind: "draft_chain", key: `chain:${quelle.chain_id}`, chain_id: quelle.chain_id } : {
5575
+ kind: "unresolved",
5576
+ key: `run:${run.run_id}`,
5577
+ reason: `Der Lauf ${run.run_id} stammt aus einer Draft-Kette ohne chain_id. Er steht deshalb f\xFCr sich; eine Zuordnung w\xE4re geraten.`
5578
+ };
5579
+ }
5580
+ const elementId = quelle.scene?.element_id;
5581
+ return istKennung(elementId) ? { kind: "scene_direct", key: `scene:${elementId}`, scene_element_id: elementId } : {
5582
+ kind: "unresolved",
5583
+ key: `run:${run.run_id}`,
5584
+ reason: `Der Lauf ${run.run_id} entstand direkt aus einer Szene, nennt aber kein Szenen-Element. Er steht deshalb f\xFCr sich; eine Zuordnung w\xE4re geraten.`
5585
+ };
5621
5586
  }
5622
- var GANZZAHL = /^-?\d+$/;
5623
- function ordneSeedsZu(eingabe, anzahl) {
5624
- const tokens = Array.isArray(eingabe) ? [...eingabe] : parseSeedListe(eingabe);
5625
- const eintraege = [];
5626
- for (let position = 0; position < anzahl; position++) {
5627
- const roh = tokens[position] ?? null;
5628
- if (roh === null) {
5629
- eintraege.push({ position, roh: null, seed: null, ungueltig: false });
5587
+ function groupKeyOf(run) {
5588
+ return groupOf(run).key;
5589
+ }
5590
+ function siblingsOf(run, alle) {
5591
+ const schluessel = groupKeyOf(run);
5592
+ return alle.filter((kandidat) => groupKeyOf(kandidat) === schluessel);
5593
+ }
5594
+ function namingElementOf(run) {
5595
+ const quelle = sourceOf(run);
5596
+ if (quelle.kind === "scene_direct") {
5597
+ return istKennung(quelle.scene?.element_id) ? quelle.scene.element_id : null;
5598
+ }
5599
+ if (quelle.kind === "draft_chain") {
5600
+ const szene = (quelle.nodes ?? []).find((knoten) => knoten?.type === "scene");
5601
+ if (szene && istKennung(szene.element_id)) return szene.element_id;
5602
+ const shot = (quelle.nodes ?? []).find((knoten) => knoten?.type === "shot");
5603
+ if (shot && istKennung(shot.element_id)) return shot.element_id;
5604
+ }
5605
+ return istKennung(run.shot_element_id) ? run.shot_element_id : null;
5606
+ }
5607
+ function contextOfRun(run) {
5608
+ const kette = run.input_payload?.context_chain;
5609
+ if (!Array.isArray(kette) || kette.length === 0) return void 0;
5610
+ const erstes = kette[0];
5611
+ if (erstes === null) return null;
5612
+ return istKennung(erstes) ? erstes : void 0;
5613
+ }
5614
+ function selectPromptRuns(alle, filter = {}) {
5615
+ const status = filter.status ?? "succeeded";
5616
+ const nachKontext = filter.contextId !== void 0;
5617
+ const runs = [];
5618
+ let hiddenByStatus = 0;
5619
+ let hiddenByContext = 0;
5620
+ let withoutContext = 0;
5621
+ for (const run of alle) {
5622
+ if (status !== "all" && run.status !== status) {
5623
+ hiddenByStatus += 1;
5630
5624
  continue;
5631
5625
  }
5632
- const zahl = GANZZAHL.test(roh) ? Number(roh) : Number.NaN;
5633
- const gueltig = Number.isSafeInteger(zahl);
5634
- eintraege.push({
5635
- position,
5636
- roh,
5637
- seed: gueltig ? zahl : null,
5638
- ungueltig: !gueltig
5639
- });
5626
+ if (nachKontext) {
5627
+ const kontext = contextOfRun(run);
5628
+ if (kontext === void 0) {
5629
+ withoutContext += 1;
5630
+ continue;
5631
+ }
5632
+ if (kontext !== filter.contextId) {
5633
+ hiddenByContext += 1;
5634
+ continue;
5635
+ }
5636
+ }
5637
+ runs.push(run);
5640
5638
  }
5641
- return {
5642
- eintraege,
5643
- ungueltige: eintraege.filter((e) => e.ungueltig),
5644
- ueberzaehlig: Math.max(0, tokens.length - anzahl)
5645
- };
5639
+ return { runs, hiddenByStatus, hiddenByContext, withoutContext };
5646
5640
  }
5647
- function seedFehlerText(ungueltige) {
5648
- const teile = ungueltige.map((e) => `Bild ${e.position + 1}: "${e.roh}"`);
5649
- return `Die Seedliste enth\xE4lt keine ganze Zahl an ${teile.length === 1 ? "Position" : "Positionen"} \u2014 ${teile.join(", ")}. Kein Bild wurde importiert.`;
5641
+
5642
+ // src/lib/szenen/itemDerivation.ts
5643
+ var SZENEN_ITEM_PREFIX = "szn-";
5644
+ function itemIdForRun(runId) {
5645
+ return `${SZENEN_ITEM_PREFIX}${runId}`;
5650
5646
  }
5651
- function mitDefaultVorlage(custom) {
5652
- return [DEFAULT_SEED_VORLAGE, ...custom.filter((v) => !v.builtin)];
5647
+ function isSzenenItemId(itemId) {
5648
+ return itemId.startsWith(SZENEN_ITEM_PREFIX);
5653
5649
  }
5654
-
5650
+ function checkSzenenBatchOrigin(candidate) {
5651
+ if (!istText3(candidate.prompt_run_id)) {
5652
+ return {
5653
+ kind: "rejected",
5654
+ failure: { field: "prompt_run_id", message: '"prompt_run_id" fehlt oder ist leer.' }
5655
+ };
5656
+ }
5657
+ if (!istText3(candidate.generation_batch_id)) {
5658
+ return {
5659
+ kind: "rejected",
5660
+ failure: {
5661
+ field: "generation_batch_id",
5662
+ message: '"generation_batch_id" fehlt oder ist leer.'
5663
+ }
5664
+ };
5665
+ }
5666
+ if (candidate.source_kind !== "generated_in_app" && candidate.source_kind !== "external_import") {
5667
+ return {
5668
+ kind: "rejected",
5669
+ failure: {
5670
+ field: "source_kind",
5671
+ message: `"source_kind" ist unbekannt: ${JSON.stringify(candidate.source_kind)}. Erlaubt: generated_in_app, external_import.`
5672
+ }
5673
+ };
5674
+ }
5675
+ const round = typeof candidate.round === "number" && Number.isInteger(candidate.round) ? candidate.round : void 0;
5676
+ return {
5677
+ kind: "ok",
5678
+ value: {
5679
+ prompt_run_id: candidate.prompt_run_id,
5680
+ generation_batch_ids: [candidate.generation_batch_id],
5681
+ source_kind: candidate.source_kind,
5682
+ ...round !== void 0 ? { round } : {}
5683
+ }
5684
+ };
5685
+ }
5686
+ function mergeSzenenBatchOrigin(bestehend, neu) {
5687
+ if (!bestehend) return neu;
5688
+ const ids = [...bestehend.generation_batch_ids];
5689
+ for (const id of neu.generation_batch_ids) {
5690
+ if (!ids.includes(id)) ids.push(id);
5691
+ }
5692
+ return {
5693
+ prompt_run_id: bestehend.prompt_run_id,
5694
+ generation_batch_ids: ids,
5695
+ source_kind: bestehend.source_kind,
5696
+ ...bestehend.round !== void 0 ? { round: bestehend.round } : {}
5697
+ };
5698
+ }
5699
+ var DEFAULT_FSK_LEVEL = "FSK16";
5700
+ var istText3 = (v) => typeof v === "string" && v.trim().length > 0;
5701
+ function roundOfRun(run, runsOfShot) {
5702
+ return runsOfShot.findIndex((kandidat) => kandidat.run_id === run.run_id) + 1;
5703
+ }
5704
+ function deriveLineage(ownItemId, ownRound, existingItems) {
5705
+ const kandidaten = existingItems.filter((item) => item.valid && item.item_id !== ownItemId).sort((a, b) => a.sequence - b.sequence);
5706
+ const elternteil = kandidaten[kandidaten.length - 1];
5707
+ if (!elternteil) {
5708
+ return {
5709
+ parent_item_id: null,
5710
+ lineage_root_id: ownItemId,
5711
+ generation: 0,
5712
+ out_of_order: false
5713
+ };
5714
+ }
5715
+ return {
5716
+ parent_item_id: elternteil.item_id,
5717
+ lineage_root_id: elternteil.lineage_root_id,
5718
+ generation: elternteil.generation + 1,
5719
+ // Der Elternteil kommt aus einer spaeteren Runde: rueckwaerts gespeichert.
5720
+ // Kein Fehler, aber nichts, was stillschweigend passieren darf.
5721
+ out_of_order: elternteil.round >= ownRound
5722
+ };
5723
+ }
5724
+ function deriveItemFromRun(input) {
5725
+ const { run, runsOfShot, existingItems, savedAt } = input;
5726
+ if (run.status === "failed") {
5727
+ return {
5728
+ kind: "rejected",
5729
+ failure: {
5730
+ class: "run_failed",
5731
+ message: `Der Lauf ${run.run_id} ist gescheitert (${run.failure ? run.failure.class : "unbekannt"}) und traegt keinen Prompt. Aus einem Fehlerlauf entsteht kein Item.`,
5732
+ run_failure: run.failure
5733
+ }
5734
+ };
5735
+ }
5736
+ if (!run.output || !istText3(run.output.positive)) {
5737
+ return {
5738
+ kind: "rejected",
5739
+ failure: {
5740
+ class: "run_without_output",
5741
+ message: `Der Lauf ${run.run_id} ist als erfolgreich protokolliert, traegt aber keinen positiven Prompt. Das ist ein Datenfehler im Lauf, kein Item.`
5742
+ }
5743
+ };
5744
+ }
5745
+ const round = roundOfRun(run, runsOfShot);
5746
+ if (round === 0) {
5747
+ return {
5748
+ kind: "rejected",
5749
+ failure: {
5750
+ class: "run_not_among_siblings",
5751
+ // Regel 11: Die Meldung nennt die **Bezugsgruppe**, nicht ein Feld,
5752
+ // das bei einem shotlosen Lauf leer ist. „des Shots ''" war keine
5753
+ // Auskunft (P1-B).
5754
+ message: `Der Lauf ${run.run_id} ist unter den ${runsOfShot.length} Laeufen seiner Bezugsgruppe ${groupKeyOf(run)} nicht enthalten. Ohne seinen Platz gaebe es keine Rundennummer.`
5755
+ }
5756
+ };
5757
+ }
5758
+ const item_id = itemIdForRun(run.run_id);
5759
+ const lineage = deriveLineage(item_id, round, existingItems);
5760
+ const negative = istText3(run.output.negative) ? run.output.negative : null;
5761
+ const title = istText3(input.title) ? input.title : istText3(input.shotTitle) ? `${input.shotTitle} \xB7 Runde ${round}` : `Szenen-Lauf ${round}`;
5762
+ return {
5763
+ kind: "ok",
5764
+ value: {
5765
+ item_id,
5766
+ title,
5767
+ content: run.output.positive,
5768
+ negative_prompt: negative,
5769
+ // `feedback` ist Pflicht (D-55) und ist genau die Erlaeuterung fuer den
5770
+ // Menschen — also gehoert sie in das Feld fuer Notizen.
5771
+ notes: istText3(input.notes) ? input.notes : run.output.feedback,
5772
+ rating: typeof input.rating === "number" ? input.rating : 0,
5773
+ fsk_level: istText3(input.fskLevel) ? input.fskLevel : DEFAULT_FSK_LEVEL,
5774
+ tags: [],
5775
+ szenen: {
5776
+ prompt_run_id: run.run_id,
5777
+ shot_version_id: run.shot_version_id,
5778
+ shot_element_id: run.shot_element_id,
5779
+ scene_setup_version_id: run.scene_setup_version_id,
5780
+ camera_id: run.camera_id,
5781
+ target_model_version_id: run.target_model_version_id,
5782
+ provider: run.provider,
5783
+ model_id: run.model_id,
5784
+ run_created_at: run.created_at,
5785
+ round,
5786
+ created_from_draft_id: null,
5787
+ saved_at: savedAt
5788
+ },
5789
+ lineage,
5790
+ // Bestandsformat: `LibraryTab` baut den Baum aus `metadata.relationships`.
5791
+ // Ein Szenen-Item ohne diese Kante waere im Baum unsichtbar.
5792
+ relationships: lineage.parent_item_id ? [{ type: "parent", id: lineage.parent_item_id, role: "source_A" }] : []
5793
+ }
5794
+ };
5795
+ }
5796
+
5797
+ // src/lib/szenen/seedListe.ts
5798
+ var DEFAULT_SEED_VORLAGE = {
5799
+ name: "Default",
5800
+ seeds: "1; 10; 100; 1000",
5801
+ builtin: true
5802
+ };
5803
+ function parseSeedListe(eingabe) {
5804
+ if (!eingabe) return [];
5805
+ return eingabe.split(/[,;]/).map((teil) => teil.trim()).filter((teil) => teil.length > 0);
5806
+ }
5807
+ function formatSeedListe(tokens) {
5808
+ return tokens.join("; ");
5809
+ }
5810
+ var GANZZAHL = /^-?\d+$/;
5811
+ function ordneSeedsZu(eingabe, anzahl) {
5812
+ const tokens = Array.isArray(eingabe) ? [...eingabe] : parseSeedListe(eingabe);
5813
+ const eintraege = [];
5814
+ for (let position = 0; position < anzahl; position++) {
5815
+ const roh = tokens[position] ?? null;
5816
+ if (roh === null) {
5817
+ eintraege.push({ position, roh: null, seed: null, ungueltig: false });
5818
+ continue;
5819
+ }
5820
+ const zahl = GANZZAHL.test(roh) ? Number(roh) : Number.NaN;
5821
+ const gueltig = Number.isSafeInteger(zahl);
5822
+ eintraege.push({
5823
+ position,
5824
+ roh,
5825
+ seed: gueltig ? zahl : null,
5826
+ ungueltig: !gueltig
5827
+ });
5828
+ }
5829
+ return {
5830
+ eintraege,
5831
+ ungueltige: eintraege.filter((e) => e.ungueltig),
5832
+ ueberzaehlig: Math.max(0, tokens.length - anzahl)
5833
+ };
5834
+ }
5835
+ function seedFehlerText(ungueltige) {
5836
+ const teile = ungueltige.map((e) => `Bild ${e.position + 1}: "${e.roh}"`);
5837
+ return `Die Seedliste enth\xE4lt keine ganze Zahl an ${teile.length === 1 ? "Position" : "Positionen"} \u2014 ${teile.join(", ")}. Kein Bild wurde importiert.`;
5838
+ }
5839
+ function mitDefaultVorlage(custom) {
5840
+ return [DEFAULT_SEED_VORLAGE, ...custom.filter((v) => !v.builtin)];
5841
+ }
5842
+
5655
5843
  // src/lib/szenen/zipBilder.ts
5656
5844
  var BILD_ENDUNGEN = {
5657
5845
  png: "image/png",
@@ -5700,9 +5888,9 @@ async function leseBilderAusZip(datei) {
5700
5888
  );
5701
5889
  }
5702
5890
  const { default: JSZip2 } = await import("jszip");
5703
- const archiv = await JSZip2.loadAsync(datei).catch((fehler4) => {
5891
+ const archiv = await JSZip2.loadAsync(datei).catch((fehler5) => {
5704
5892
  throw new Error(
5705
- `${name} konnte nicht als ZIP gelesen werden: ${fehler4 instanceof Error ? fehler4.message : String(fehler4)}`
5893
+ `${name} konnte nicht als ZIP gelesen werden: ${fehler5 instanceof Error ? fehler5.message : String(fehler5)}`
5706
5894
  );
5707
5895
  });
5708
5896
  const pfade = [];
@@ -6715,10 +6903,10 @@ async function runSceneDerivation(input, port, userPrompt) {
6715
6903
  }
6716
6904
 
6717
6905
  // src/features/szenen/SzenenView.tsx
6718
- import { useMemo as useMemo4, useState as useState17 } from "react";
6906
+ import { useMemo as useMemo5, useState as useState20 } from "react";
6719
6907
 
6720
6908
  // src/features/szenen/areas/ElementsArea.tsx
6721
- import { useState as useState12 } from "react";
6909
+ import { useState as useState13 } from "react";
6722
6910
 
6723
6911
  // src/features/szenen/areas/usePortQuery.ts
6724
6912
  import { useEffect, useRef, useState } from "react";
@@ -6750,7 +6938,7 @@ function usePortQuery(frage, abhaengigkeiten) {
6750
6938
  }
6751
6939
 
6752
6940
  // src/features/szenen/areas/ElementDetail.tsx
6753
- import { useEffect as useEffect6, useMemo as useMemo2, useState as useState10 } from "react";
6941
+ import { useEffect as useEffect7, useMemo as useMemo2, useState as useState11 } from "react";
6754
6942
 
6755
6943
  // src/features/szenen/elementLabels.ts
6756
6944
  var LABELS = {
@@ -8581,7 +8769,7 @@ var ERKLAERUNG = {
8581
8769
  };
8582
8770
  var CapabilityPanel = ({ port, elementId, setupPayload }) => {
8583
8771
  const [bericht, setBericht] = useState5(null);
8584
- const [fehler4, setFehler] = useState5(null);
8772
+ const [fehler5, setFehler] = useState5(null);
8585
8773
  useEffect2(() => {
8586
8774
  let abgemeldet = false;
8587
8775
  setBericht(null);
@@ -8603,10 +8791,10 @@ var CapabilityPanel = ({ port, elementId, setupPayload }) => {
8603
8791
  abgemeldet = true;
8604
8792
  };
8605
8793
  }, [port, elementId, setupPayload]);
8606
- if (fehler4) {
8794
+ if (fehler5) {
8607
8795
  return /* @__PURE__ */ jsxs4("p", { "data-testid": "capability-fehler", role: "alert", children: [
8608
8796
  "F\xE4higkeitsbericht nicht erstellbar: ",
8609
- fehler4
8797
+ fehler5
8610
8798
  ] });
8611
8799
  }
8612
8800
  if (!bericht) return /* @__PURE__ */ jsx4("p", { "data-testid": "capability-laden", children: "F\xE4higkeitsbericht wird erstellt \u2026" });
@@ -8842,13 +9030,57 @@ var VersionNavigator = ({
8842
9030
  };
8843
9031
 
8844
9032
  // src/features/szenen/areas/RunsPanel.tsx
8845
- import { useEffect as useEffect5, useRef as useRef3, useState as useState9 } from "react";
9033
+ import { useEffect as useEffect6, useRef as useRef3, useState as useState10 } from "react";
9034
+
9035
+ // src/features/szenen/areas/useLaufTitel.ts
9036
+ import { useEffect as useEffect3, useState as useState7 } from "react";
9037
+ function useLaufTitel(port, laeufe) {
9038
+ const [titelJeElement, setTitelJeElement] = useState7(
9039
+ () => /* @__PURE__ */ new Map()
9040
+ );
9041
+ const gebraucht = [...new Set(
9042
+ laeufe.map(namingElementOf).filter((id) => id !== null)
9043
+ )].sort();
9044
+ const schluessel = gebraucht.join(",");
9045
+ useEffect3(() => {
9046
+ let abgemeldet = false;
9047
+ const offen = gebraucht.filter((id) => !titelJeElement.has(id));
9048
+ if (offen.length === 0) return void 0;
9049
+ void (async () => {
9050
+ const gelesen = /* @__PURE__ */ new Map();
9051
+ for (const id of offen) {
9052
+ try {
9053
+ const ergebnis = await port.elements.getElement(id);
9054
+ gelesen.set(
9055
+ id,
9056
+ ergebnis.kind === "ok" && ergebnis.value ? ergebnis.value.metadata?.title ?? null : null
9057
+ );
9058
+ } catch {
9059
+ gelesen.set(id, null);
9060
+ }
9061
+ }
9062
+ if (abgemeldet) return;
9063
+ setTitelJeElement((bisher) => new Map([...bisher, ...gelesen]));
9064
+ })();
9065
+ return () => {
9066
+ abgemeldet = true;
9067
+ };
9068
+ }, [port, schluessel]);
9069
+ const jeLauf = /* @__PURE__ */ new Map();
9070
+ for (const lauf of laeufe) {
9071
+ const elementId = namingElementOf(lauf);
9072
+ if (!elementId) continue;
9073
+ const titel = titelJeElement.get(elementId);
9074
+ if (titel) jeLauf.set(lauf.run_id, titel);
9075
+ }
9076
+ return jeLauf;
9077
+ }
8846
9078
 
8847
9079
  // src/features/szenen/areas/RunImagesPanel.tsx
8848
- import React7, { useEffect as useEffect4, useRef as useRef2, useState as useState8 } from "react";
9080
+ import React7, { useEffect as useEffect5, useRef as useRef2, useState as useState9 } from "react";
8849
9081
 
8850
9082
  // src/features/szenen/areas/Bildbausteine.tsx
8851
- import { useCallback, useEffect as useEffect3, useState as useState7 } from "react";
9083
+ import { useCallback, useEffect as useEffect4, useState as useState8 } from "react";
8852
9084
  import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
8853
9085
  var Kachel = ({
8854
9086
  quelle,
@@ -8869,7 +9101,7 @@ var BildViewer = ({
8869
9101
  if (anzahl === 0) return;
8870
9102
  onWechseln((position + schritt + anzahl) % anzahl);
8871
9103
  }, [anzahl, position, onWechseln]);
8872
- useEffect3(() => {
9104
+ useEffect4(() => {
8873
9105
  const taste = (e) => {
8874
9106
  if (e.key === "Escape") onSchliessen();
8875
9107
  else if (e.key === "ArrowLeft") blaettern(-1);
@@ -8952,8 +9184,8 @@ var BildViewer = ({
8952
9184
  );
8953
9185
  };
8954
9186
  var BildGalerie = ({ bilder, testId, satzTitel }) => {
8955
- const [offen, setOffen] = useState7(null);
8956
- useEffect3(() => {
9187
+ const [offen, setOffen] = useState8(null);
9188
+ useEffect4(() => {
8957
9189
  setOffen((vorher) => vorher !== null && vorher >= bilder.length ? null : vorher);
8958
9190
  }, [bilder.length]);
8959
9191
  if (bilder.length === 0) return null;
@@ -9009,10 +9241,11 @@ var AUFTRAG_TEXT = {
9009
9241
  };
9010
9242
  var RunImagesPanel = ({ port, lauf }) => {
9011
9243
  const faehig = capabilitiesOf(port);
9012
- const [nachladen, setNachladen] = useState8(0);
9244
+ const [nachladen, setNachladen] = useState9(0);
9013
9245
  const neuLesen = () => setNachladen((n) => n + 1);
9014
- const [erzeugenOffen, setErzeugenOffen] = useState8(null);
9015
- const [zuordnenOffen, setZuordnenOffen] = useState8(null);
9246
+ const [erzeugenOffen, setErzeugenOffen] = useState9(null);
9247
+ const [zuordnenOffen, setZuordnenOffen] = useState9(null);
9248
+ const [chatAuftraegeOffen, setChatAuftraegeOffen] = useState9(null);
9016
9249
  const quelle = sourceOf(lauf);
9017
9250
  const [wahlPhase] = usePortQuery(
9018
9251
  () => port.imageGeneration ? port.imageGeneration.imageModelChoiceForRun(lauf.run_id) : Promise.resolve({ kind: "ok", value: null }),
@@ -9027,10 +9260,17 @@ var RunImagesPanel = ({ port, lauf }) => {
9027
9260
  () => port.imageBatchHistory ? port.imageBatchHistory.listBatchesForPromptRun(lauf.run_id) : Promise.resolve({ kind: "ok", value: [] }),
9028
9261
  [port, lauf.run_id, nachladen]
9029
9262
  );
9263
+ const [chatAuftraegePhase] = usePortQuery(
9264
+ () => port.imageJobs ? port.imageJobs.listJobs(lauf.run_id) : Promise.resolve({ kind: "ok", value: [] }),
9265
+ [port, lauf.run_id, nachladen]
9266
+ );
9030
9267
  const auftraege = auftraegePhase.status === "ready" ? auftraegePhase.value : [];
9031
9268
  const batches = batchesPhase.status === "ready" ? batchesPhase.value : [];
9269
+ const chatAuftraege = chatAuftraegePhase.status === "ready" ? chatAuftraegePhase.value : [];
9270
+ const offeneChatAuftraege = chatAuftraege.filter((a) => a.status === "pending").length;
9032
9271
  const auffaellige = auftraege.filter((a) => a.status !== "succeeded");
9033
9272
  const erzeugenAuf = erzeugenOffen ?? batches.length === 0;
9273
+ const chatAuftraegeAuf = chatAuftraegeOffen ?? offeneChatAuftraege > 0;
9034
9274
  return /* @__PURE__ */ jsxs8("div", { className: "szenen-bilder", "data-testid": `bilder-${lauf.run_id}`, children: [
9035
9275
  /* @__PURE__ */ jsxs8("section", { className: "szenen-bilder__eingang", "data-testid": `eingang-${lauf.run_id}`, children: [
9036
9276
  /* @__PURE__ */ jsx8("h5", { children: "Eingang \u2014 woraus dieser Prompt entstand" }),
@@ -9151,6 +9391,43 @@ var RunImagesPanel = ({ port, lauf }) => {
9151
9391
  }
9152
9392
  )
9153
9393
  ] }),
9394
+ faehig.imageJobs && /* @__PURE__ */ jsxs8(
9395
+ "details",
9396
+ {
9397
+ className: "szenen-bilder__aktion",
9398
+ "data-testid": `auftraege-chat-bereich-${lauf.run_id}`,
9399
+ open: chatAuftraegeAuf,
9400
+ children: [
9401
+ /* @__PURE__ */ jsxs8(
9402
+ "summary",
9403
+ {
9404
+ onClick: (e) => {
9405
+ e.preventDefault();
9406
+ setChatAuftraegeOffen(!chatAuftraegeAuf);
9407
+ },
9408
+ children: [
9409
+ "Bildauftraege aus dem Chat",
9410
+ offeneChatAuftraege > 0 ? ` (${offeneChatAuftraege} wartend)` : ""
9411
+ ]
9412
+ }
9413
+ ),
9414
+ /* @__PURE__ */ jsx8(
9415
+ ChatAuftraegeAktion,
9416
+ {
9417
+ port,
9418
+ lauf,
9419
+ auftraege: chatAuftraege,
9420
+ fehler: chatAuftraegePhase.status === "error" ? chatAuftraegePhase.message : null,
9421
+ laedt: chatAuftraegePhase.status === "loading",
9422
+ onFertig: () => {
9423
+ setChatAuftraegeOffen(true);
9424
+ neuLesen();
9425
+ }
9426
+ }
9427
+ )
9428
+ ]
9429
+ }
9430
+ ),
9154
9431
  /* @__PURE__ */ jsxs8(
9155
9432
  "details",
9156
9433
  {
@@ -9282,12 +9559,12 @@ var BestandsBild = ({ port, mediaRef }) => {
9282
9559
  ] });
9283
9560
  };
9284
9561
  function useBildQuellen(port, bilder) {
9285
- const [stand, setStand] = useState8(() => ({ quellen: /* @__PURE__ */ new Map(), fehler: /* @__PURE__ */ new Map() }));
9562
+ const [stand, setStand] = useState9(() => ({ quellen: /* @__PURE__ */ new Map(), fehler: /* @__PURE__ */ new Map() }));
9286
9563
  const gehalten = useRef2(/* @__PURE__ */ new Map());
9287
9564
  const gescheitert = useRef2(/* @__PURE__ */ new Map());
9288
- const [versuch, setVersuch] = useState8(0);
9565
+ const [versuch, setVersuch] = useState9(0);
9289
9566
  const schluessel = bilder.map((b) => `${b.media_ref}|${b.mime_type ?? ""}`).join(" ");
9290
- useEffect4(() => {
9567
+ useEffect5(() => {
9291
9568
  const bestand = port.imageLibrary;
9292
9569
  if (!bestand) return void 0;
9293
9570
  let abgebrochen = false;
@@ -9334,7 +9611,7 @@ function useBildQuellen(port, bilder) {
9334
9611
  erneutVersuchen: () => setVersuch((n) => n + 1)
9335
9612
  };
9336
9613
  }
9337
- var BildDetails = ({ bild, fehler: fehler4, aufloesbar }) => /* @__PURE__ */ jsxs8("span", { className: "szenen-bilder__meta", "data-testid": `batchbild-${bild.media_ref}`, children: [
9614
+ var BildDetails = ({ bild, fehler: fehler5, aufloesbar }) => /* @__PURE__ */ jsxs8("span", { className: "szenen-bilder__meta", "data-testid": `batchbild-${bild.media_ref}`, children: [
9338
9615
  /* @__PURE__ */ jsx8("code", { children: bild.media_ref }),
9339
9616
  /* @__PURE__ */ jsxs8("span", { children: [
9340
9617
  "Position ",
@@ -9347,7 +9624,7 @@ var BildDetails = ({ bild, fehler: fehler4, aufloesbar }) => /* @__PURE__ */ jsx
9347
9624
  "\xD7",
9348
9625
  bild.height
9349
9626
  ] }) : null,
9350
- fehler4 && /* @__PURE__ */ jsx8("em", { "data-testid": `batchbild-fehler-${bild.media_ref}`, children: fehler4 }),
9627
+ fehler5 && /* @__PURE__ */ jsx8("em", { "data-testid": `batchbild-fehler-${bild.media_ref}`, children: fehler5 }),
9351
9628
  !aufloesbar && // Capability-Grenze, kein Fehler: Dieser Host kann Batches lesen,
9352
9629
  // aber keine Bilder auflösen (E). Kein erfundener Fallback auf eine
9353
9630
  // Serverroute (A6).
@@ -9363,18 +9640,18 @@ var HERKUNFT_KURZ = {
9363
9640
  external_import: "extern importiert"
9364
9641
  };
9365
9642
  var BatchKachel = ({ port, batch }) => {
9366
- const { quellen, fehler: fehler4, wiederholbar, erneutVersuchen } = useBildQuellen(port, batch.images);
9643
+ const { quellen, fehler: fehler5, wiederholbar, erneutVersuchen } = useBildQuellen(port, batch.images);
9367
9644
  const aufloesbar = Boolean(port.imageLibrary);
9368
9645
  const bilder = batch.images.map((bild) => ({
9369
9646
  id: bild.media_ref,
9370
9647
  quelle: quellen.get(bild.media_ref) ?? null,
9371
9648
  titel: `Bild ${bild.position + 1}`,
9372
- ...fehler4.get(bild.media_ref) ? { leerText: "Nicht ladbar" } : {},
9649
+ ...fehler5.get(bild.media_ref) ? { leerText: "Nicht ladbar" } : {},
9373
9650
  details: /* @__PURE__ */ jsx8(
9374
9651
  BildDetails,
9375
9652
  {
9376
9653
  bild,
9377
- fehler: fehler4.get(bild.media_ref) ?? null,
9654
+ fehler: fehler5.get(bild.media_ref) ?? null,
9378
9655
  aufloesbar
9379
9656
  }
9380
9657
  )
@@ -9434,7 +9711,7 @@ var BatchKachel = ({ port, batch }) => {
9434
9711
  BildDetails,
9435
9712
  {
9436
9713
  bild,
9437
- fehler: fehler4.get(bild.media_ref) ?? null,
9714
+ fehler: fehler5.get(bild.media_ref) ?? null,
9438
9715
  aufloesbar
9439
9716
  }
9440
9717
  ) }, bild.media_ref)) })
@@ -9452,11 +9729,11 @@ var ProfilBefunde = ({ wahl, runId }) => {
9452
9729
  ] });
9453
9730
  };
9454
9731
  var ErzeugenAktion = ({ port, lauf, wahl, wahlFehler, onFertig }) => {
9455
- const [modellWahl, setModellWahl] = useState8(null);
9456
- const [anzahlText, setAnzahlText] = useState8("1");
9457
- const [seitenverhaeltnis, setSeitenverhaeltnis] = useState8("");
9458
- const [laeuft, setLaeuft] = useState8(false);
9459
- const [notiz, setNotiz] = useState8(null);
9732
+ const [modellWahl, setModellWahl] = useState9(null);
9733
+ const [anzahlText, setAnzahlText] = useState9("1");
9734
+ const [seitenverhaeltnis, setSeitenverhaeltnis] = useState9("");
9735
+ const [laeuft, setLaeuft] = useState9(false);
9736
+ const [notiz, setNotiz] = useState9(null);
9460
9737
  if (!port.imageGeneration) {
9461
9738
  return /* @__PURE__ */ jsx8("p", { "data-testid": `erzeugen-unsupported-${lauf.run_id}`, children: "Dieser Host erzeugt keine Bilder \u2014 entweder fehlt der Providerzugang oder der gesicherte Speicherweg. Externe Bilder lassen sich trotzdem zuordnen." });
9462
9739
  }
@@ -9633,23 +9910,23 @@ var ErzeugenAktion = ({ port, lauf, wahl, wahlFehler, onFertig }) => {
9633
9910
  };
9634
9911
  var DEFAULT_VORLAGE_KEY = "__default__";
9635
9912
  var ZuordnenAktion = ({ port, lauf, onFertig }) => {
9636
- const [dateien, setDateien] = useState8([]);
9637
- const [seeds, setSeeds] = useState8({});
9638
- const [zip, setZip] = useState8(null);
9639
- const [zipLaedt, setZipLaedt] = useState8(false);
9640
- const [seedListe, setSeedListe] = useState8("");
9641
- const [vorlagen, setVorlagen] = useState8([DEFAULT_SEED_VORLAGE]);
9642
- const [vorlageKey, setVorlageKey] = useState8(DEFAULT_VORLAGE_KEY);
9643
- const [vorlagenStand, setVorlagenStand] = useState8(0);
9644
- const [vorlageBenennen, setVorlageBenennen] = useState8(false);
9645
- const [vorlageName, setVorlageName] = useState8("");
9646
- const [modell, setModell] = useState8("");
9647
- const [itemId, setItemId] = useState8("");
9648
- const [laeuft, setLaeuft] = useState8(false);
9649
- const [notiz, setNotiz] = useState8(null);
9913
+ const [dateien, setDateien] = useState9([]);
9914
+ const [seeds, setSeeds] = useState9({});
9915
+ const [zip, setZip] = useState9(null);
9916
+ const [zipLaedt, setZipLaedt] = useState9(false);
9917
+ const [seedListe, setSeedListe] = useState9("");
9918
+ const [vorlagen, setVorlagen] = useState9([DEFAULT_SEED_VORLAGE]);
9919
+ const [vorlageKey, setVorlageKey] = useState9(DEFAULT_VORLAGE_KEY);
9920
+ const [vorlagenStand, setVorlagenStand] = useState9(0);
9921
+ const [vorlageBenennen, setVorlageBenennen] = useState9(false);
9922
+ const [vorlageName, setVorlageName] = useState9("");
9923
+ const [modell, setModell] = useState9("");
9924
+ const [itemId, setItemId] = useState9("");
9925
+ const [laeuft, setLaeuft] = useState9(false);
9926
+ const [notiz, setNotiz] = useState9(null);
9650
9927
  const auswahlMarke = useRef2(0);
9651
9928
  const zipAktiv = zip !== null;
9652
- useEffect4(() => {
9929
+ useEffect5(() => {
9653
9930
  if (!zipAktiv) return;
9654
9931
  let abgebrochen = false;
9655
9932
  void (async () => {
@@ -9703,11 +9980,11 @@ var ZuordnenAktion = ({ port, lauf, onFertig }) => {
9703
9980
  setSeedListe(DEFAULT_SEED_VORLAGE.seeds);
9704
9981
  setVorlageKey(DEFAULT_VORLAGE_KEY);
9705
9982
  }
9706
- } catch (fehler4) {
9983
+ } catch (fehler5) {
9707
9984
  if (veraltet()) return;
9708
9985
  setNotiz({
9709
9986
  tone: "error",
9710
- text: fehler4 instanceof Error ? fehler4.message : String(fehler4)
9987
+ text: fehler5 instanceof Error ? fehler5.message : String(fehler5)
9711
9988
  });
9712
9989
  }
9713
9990
  if (veraltet()) return;
@@ -9726,10 +10003,10 @@ var ZuordnenAktion = ({ port, lauf, onFertig }) => {
9726
10003
  setNotiz(
9727
10004
  ergebnis.kind === "ok" ? { tone: "ok", text: `Importiert als Item ${ergebnis.value.item_id} mit ${bilder.length} Bild(ern).` } : noticeFor(ergebnis, "Externe Bilder importieren")
9728
10005
  );
9729
- } catch (fehler4) {
10006
+ } catch (fehler5) {
9730
10007
  setNotiz({
9731
10008
  tone: "error",
9732
- text: `Externe Bilder importieren: ${fehler4 instanceof Error ? fehler4.message : String(fehler4)}`
10009
+ text: `Externe Bilder importieren: ${fehler5 instanceof Error ? fehler5.message : String(fehler5)}`
9733
10010
  });
9734
10011
  }
9735
10012
  setLaeuft(false);
@@ -9751,10 +10028,10 @@ var ZuordnenAktion = ({ port, lauf, onFertig }) => {
9751
10028
  ...seed !== void 0 ? { seed } : {}
9752
10029
  });
9753
10030
  }
9754
- } catch (fehler4) {
10031
+ } catch (fehler5) {
9755
10032
  setNotiz({
9756
10033
  tone: "error",
9757
- text: `Externe Bilder importieren: ${fehler4 instanceof Error ? fehler4.message : String(fehler4)}`
10034
+ text: `Externe Bilder importieren: ${fehler5 instanceof Error ? fehler5.message : String(fehler5)}`
9758
10035
  });
9759
10036
  return;
9760
10037
  }
@@ -10090,14 +10367,109 @@ function readAsBase64(datei) {
10090
10367
  leser.readAsDataURL(datei);
10091
10368
  });
10092
10369
  }
10093
-
10094
- // src/features/szenen/areas/RunsPanel.tsx
10095
- import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
10096
- var RunsPanel = ({ port, source, contextId }) => {
10097
- const [anweisung, setAnweisung] = useState9("");
10098
- const [laeuft, setLaeuft] = useState9(false);
10370
+ var CHAT_AUFTRAG_TEXT = {
10371
+ pending: "wartet auf \xDCbernahme",
10372
+ // Wie bei `started` (D-119): laufend und abgebrochen sind aus dem Log
10373
+ // nicht unterscheidbar, und es gibt keinen automatischen Retry, der die
10374
+ // Unterscheidung bräuchte.
10375
+ claimed: "\xFCbernommen (l\xE4uft oder unterbrochen)",
10376
+ succeeded: "erfolgreich",
10377
+ partial: "teilweise",
10378
+ failed: "fehlgeschlagen",
10379
+ rejected: "abgelehnt \u2014 es wurde nichts ausgef\xFChrt"
10380
+ };
10381
+ var ChatAuftraegeAktion = ({
10382
+ port,
10383
+ lauf,
10384
+ auftraege,
10385
+ fehler: fehler5,
10386
+ laedt,
10387
+ onFertig
10388
+ }) => {
10389
+ const [laufend, setLaufend] = useState9(null);
10099
10390
  const [notiz, setNotiz] = useState9(null);
10100
- const [nachladen, setNachladen] = useState9(0);
10391
+ async function uebernehmen(jobId) {
10392
+ if (!port.imageJobs) return;
10393
+ setLaufend(jobId);
10394
+ setNotiz(null);
10395
+ try {
10396
+ const ergebnis = await port.imageJobs.claimAndRun(jobId);
10397
+ if (ergebnis.kind === "ok") {
10398
+ setNotiz({
10399
+ tone: ergebnis.job.status === "succeeded" ? "ok" : "rejected",
10400
+ text: `Auftrag ${jobId}: ${CHAT_AUFTRAG_TEXT[ergebnis.job.status]}.` + (ergebnis.job.diagnostic ? ` ${ergebnis.job.diagnostic}` : "")
10401
+ });
10402
+ } else if (ergebnis.kind === "lost") {
10403
+ setNotiz({ tone: "rejected", text: ergebnis.message });
10404
+ } else {
10405
+ setNotiz({ tone: "error", text: ergebnis.message });
10406
+ }
10407
+ } catch (e) {
10408
+ setNotiz({
10409
+ tone: "error",
10410
+ text: `Die \xDCbernahme von Auftrag ${jobId} ist technisch gescheitert: ` + (e instanceof Error ? e.message : String(e))
10411
+ });
10412
+ } finally {
10413
+ setLaufend(null);
10414
+ onFertig();
10415
+ }
10416
+ }
10417
+ return /* @__PURE__ */ jsxs8("div", { className: "szenen-bilder__aktion-inhalt", "data-testid": `chat-auftraege-${lauf.run_id}`, children: [
10418
+ /* @__PURE__ */ jsx8("p", { className: "szenen-area__lead", children: "Auftr\xE4ge, die \xFCber den MCP aus dem Chat kamen. Der Prompt wird nicht kopiert \u2014 ausgef\xFChrt wird der eingefrorene Prompt dieses Laufs." }),
10419
+ fehler5 && /* @__PURE__ */ jsxs8("p", { role: "alert", "data-testid": `chat-auftraege-fehler-${lauf.run_id}`, children: [
10420
+ "Bildauftr\xE4ge aus dem Chat konnten nicht gelesen werden: ",
10421
+ fehler5
10422
+ ] }),
10423
+ !fehler5 && !laedt && auftraege.length === 0 && /* @__PURE__ */ jsx8("p", { "data-testid": `chat-auftraege-leer-${lauf.run_id}`, children: "F\xFCr diesen Lauf liegt kein Auftrag aus dem Chat vor." }),
10424
+ notiz && /* @__PURE__ */ jsx8(
10425
+ "p",
10426
+ {
10427
+ role: notiz.tone === "error" ? "alert" : void 0,
10428
+ "data-testid": `chat-auftraege-notiz-${lauf.run_id}`,
10429
+ children: notiz.text
10430
+ }
10431
+ ),
10432
+ /* @__PURE__ */ jsx8("ul", { className: "szenen-bilder__auftraege", children: auftraege.map((auftrag) => /* @__PURE__ */ jsxs8("li", { "data-testid": `chat-auftrag-${auftrag.job_id}`, children: [
10433
+ /* @__PURE__ */ jsx8("span", { children: CHAT_AUFTRAG_TEXT[auftrag.status] }),
10434
+ " \u2014 ",
10435
+ /* @__PURE__ */ jsxs8("span", { children: [
10436
+ auftrag.image_count,
10437
+ " ",
10438
+ auftrag.image_count === 1 ? "Bild" : "Bilder",
10439
+ " mit",
10440
+ " ",
10441
+ /* @__PURE__ */ jsx8("code", { children: auftrag.execution_target_model_key }),
10442
+ " / ",
10443
+ /* @__PURE__ */ jsx8("code", { children: auftrag.execution_profile_id }),
10444
+ auftrag.aspect_ratio ? ` im Format ${auftrag.aspect_ratio}` : ""
10445
+ ] }),
10446
+ auftrag.diagnostic && /* @__PURE__ */ jsxs8("span", { children: [
10447
+ " ",
10448
+ auftrag.diagnostic
10449
+ ] }),
10450
+ /* @__PURE__ */ jsx8("br", {}),
10451
+ /* @__PURE__ */ jsx8("code", { className: "szenen-bilder__auftragskennung", children: auftrag.job_id }),
10452
+ auftrag.status === "pending" && /* @__PURE__ */ jsx8(
10453
+ "button",
10454
+ {
10455
+ type: "button",
10456
+ disabled: laufend !== null,
10457
+ onClick: () => void uebernehmen(auftrag.job_id),
10458
+ "data-testid": `chat-auftrag-uebernehmen-${auftrag.job_id}`,
10459
+ children: laufend === auftrag.job_id ? "\xDCbernahme l\xE4uft\u2026" : "\xDCbernehmen und ausf\xFChren (kostet einen Bildaufruf)"
10460
+ }
10461
+ )
10462
+ ] }, auftrag.job_id)) })
10463
+ ] });
10464
+ };
10465
+
10466
+ // src/features/szenen/areas/RunsPanel.tsx
10467
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
10468
+ var RunsPanel = ({ port, source, contextId }) => {
10469
+ const [anweisung, setAnweisung] = useState10("");
10470
+ const [laeuft, setLaeuft] = useState10(false);
10471
+ const [notiz, setNotiz] = useState10(null);
10472
+ const [nachladen, setNachladen] = useState10(0);
10101
10473
  const faehig = capabilitiesOf(port);
10102
10474
  const quellSchluessel = source.kind === "published" ? source.shotVersionId : source.kind === "draft_chain" ? source.chainId : source.sceneElementId;
10103
10475
  const aktuelleQuelle = useRef3({ kind: source.kind, id: quellSchluessel });
@@ -10128,10 +10500,11 @@ var RunsPanel = ({ port, source, contextId }) => {
10128
10500
  neuLaden();
10129
10501
  };
10130
10502
  const laeufe = phase.status === "ready" ? phase.value : [];
10131
- const [gewaehlteRunde, setGewaehlteRunde] = useState9(void 0);
10503
+ const titel = useLaufTitel(port, laeufe);
10504
+ const [gewaehlteRunde, setGewaehlteRunde] = useState10(void 0);
10132
10505
  const neueste = laeufe.length > 0 ? laeufe[laeufe.length - 1].run_id : null;
10133
10506
  const offeneRunde = gewaehlteRunde === void 0 ? neueste : gewaehlteRunde;
10134
- useEffect5(() => {
10507
+ useEffect6(() => {
10135
10508
  setGewaehlteRunde(void 0);
10136
10509
  setAnweisung("");
10137
10510
  setNotiz(null);
@@ -10166,6 +10539,7 @@ var RunsPanel = ({ port, source, contextId }) => {
10166
10539
  port,
10167
10540
  lauf,
10168
10541
  runde: roundOfRun(lauf, laeufe),
10542
+ titel: titel.get(lauf.run_id) ?? null,
10169
10543
  offen: lauf.run_id === offeneRunde,
10170
10544
  onUmschalten: () => setGewaehlteRunde(
10171
10545
  lauf.run_id === offeneRunde ? null : lauf.run_id
@@ -10190,7 +10564,7 @@ var STATUS_TEXT = {
10190
10564
  };
10191
10565
  var LANGER_TEXT = 240;
10192
10566
  var Sekundaertext = ({ titel, text, testId }) => {
10193
- const [ganz, setGanz] = useState9(false);
10567
+ const [ganz, setGanz] = useState10(false);
10194
10568
  const lang = text.length > LANGER_TEXT;
10195
10569
  return /* @__PURE__ */ jsxs9("section", { className: "szenen-runs__sekundaer", children: [
10196
10570
  /* @__PURE__ */ jsx9("h5", { children: titel }),
@@ -10209,7 +10583,7 @@ var Sekundaertext = ({ titel, text, testId }) => {
10209
10583
  };
10210
10584
  var LISTE_ANFANG = 3;
10211
10585
  var SekundaerListe = ({ titel, eintraege, testId, className }) => {
10212
- const [alle, setAlle] = useState9(false);
10586
+ const [alle, setAlle] = useState10(false);
10213
10587
  const lang = eintraege.length > LISTE_ANFANG;
10214
10588
  const gezeigt = lang && !alle ? eintraege.slice(0, LISTE_ANFANG) : eintraege;
10215
10589
  return /* @__PURE__ */ jsxs9("section", { className: "szenen-runs__sekundaer", children: [
@@ -10227,10 +10601,10 @@ var SekundaerListe = ({ titel, eintraege, testId, className }) => {
10227
10601
  )
10228
10602
  ] });
10229
10603
  };
10230
- var RundenEintrag = ({ port, lauf, runde, offen, onUmschalten, itemMoeglich, onItemGespeichert }) => {
10231
- const [notiz, setNotiz] = useState9(null);
10232
- const [laeuft, setLaeuft] = useState9(false);
10233
- const [nachladen, setNachladen] = useState9(0);
10604
+ var RundenEintrag = ({ port, lauf, runde, titel, offen, onUmschalten, itemMoeglich, onItemGespeichert }) => {
10605
+ const [notiz, setNotiz] = useState10(null);
10606
+ const [laeuft, setLaeuft] = useState10(false);
10607
+ const [nachladen, setNachladen] = useState10(0);
10234
10608
  const [itemPhase] = usePortQuery(
10235
10609
  () => offen && port.items ? port.items.getItemForRun(lauf.run_id) : Promise.resolve({ kind: "ok", value: null }),
10236
10610
  [port, lauf.run_id, nachladen, offen]
@@ -10258,10 +10632,7 @@ var RundenEintrag = ({ port, lauf, runde, offen, onUmschalten, itemMoeglich, onI
10258
10632
  "aria-expanded": offen,
10259
10633
  "data-testid": `runde-umschalten-${lauf.run_id}`,
10260
10634
  children: [
10261
- /* @__PURE__ */ jsxs9("span", { className: "szenen-runs__marke", children: [
10262
- "Runde ",
10263
- runde
10264
- ] }),
10635
+ /* @__PURE__ */ jsx9("span", { className: "szenen-runs__marke", "data-testid": `titel-${lauf.run_id}`, children: titel ? `${titel} \xB7 Runde ${runde}` : `Runde ${runde}` }),
10265
10636
  /* @__PURE__ */ jsx9(
10266
10637
  "span",
10267
10638
  {
@@ -10385,16 +10756,16 @@ var ElementDetail = ({
10385
10756
  author,
10386
10757
  onChanged
10387
10758
  }) => {
10388
- const [nachladen, setNachladen] = useState10(0);
10759
+ const [nachladen, setNachladen] = useState11(0);
10389
10760
  const [phase] = usePortQuery(
10390
10761
  () => port.elements.getElement(elementId),
10391
10762
  [port, elementId, nachladen]
10392
10763
  );
10393
- const [entwurf, setEntwurf] = useState10({});
10394
- const [notiz, setNotiz] = useState10(null);
10395
- const [laeuft, setLaeuft] = useState10(false);
10396
- const [wahl, setWahl] = useState10(null);
10397
- const [basiswechsel, setBasiswechsel] = useState10(null);
10764
+ const [entwurf, setEntwurf] = useState11({});
10765
+ const [notiz, setNotiz] = useState11(null);
10766
+ const [laeuft, setLaeuft] = useState11(false);
10767
+ const [wahl, setWahl] = useState11(null);
10768
+ const [basiswechsel, setBasiswechsel] = useState11(null);
10398
10769
  const [kettenPhase] = usePortQuery(
10399
10770
  () => port.context.chainFor(contextId),
10400
10771
  [port, contextId]
@@ -10416,7 +10787,7 @@ var ElementDetail = ({
10416
10787
  () => schnappschuss ? fieldsForType(schnappschuss.element.type) : [],
10417
10788
  [schnappschuss?.element.type]
10418
10789
  );
10419
- useEffect6(() => {
10790
+ useEffect7(() => {
10420
10791
  setWahl(null);
10421
10792
  setBasiswechsel(null);
10422
10793
  setNotiz(null);
@@ -10447,7 +10818,7 @@ var ElementDetail = ({
10447
10818
  return {};
10448
10819
  }, [schnappschuss, angezeigt]);
10449
10820
  const anzeigeSchluessel = angezeigt.kind === "version" ? `v:${angezeigt.versionId}` : angezeigt.kind;
10450
- useEffect6(() => {
10821
+ useEffect7(() => {
10451
10822
  if (!schnappschuss) return;
10452
10823
  setEntwurf(formFromPayload(felder, angezeigtePayload));
10453
10824
  }, [
@@ -10879,15 +11250,15 @@ var ElementDetail = ({
10879
11250
  };
10880
11251
 
10881
11252
  // src/features/szenen/areas/useSchmalesDisplay.ts
10882
- import { useEffect as useEffect7, useState as useState11 } from "react";
11253
+ import { useEffect as useEffect8, useState as useState12 } from "react";
10883
11254
  var SCHMALE_ANSICHT = "(max-width: 760px)";
10884
11255
  function messen(abfrage) {
10885
11256
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
10886
11257
  return window.matchMedia(abfrage).matches;
10887
11258
  }
10888
11259
  function useSchmalesDisplay(abfrage = SCHMALE_ANSICHT) {
10889
- const [schmal, setSchmal] = useState11(() => messen(abfrage));
10890
- useEffect7(() => {
11260
+ const [schmal, setSchmal] = useState12(() => messen(abfrage));
11261
+ useEffect8(() => {
10891
11262
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
10892
11263
  const liste = window.matchMedia(abfrage);
10893
11264
  const aktualisieren = () => setSchmal(liste.matches);
@@ -10914,16 +11285,16 @@ var ElementsArea = ({
10914
11285
  const gemischt = auswahlTypen.length > 1;
10915
11286
  const ueberschrift = listTitle ?? (einzelTyp ? elementTypPlural(einzelTyp) : "Bausteine");
10916
11287
  const einzahl = einzelTyp ? elementTypLabel(einzelTyp) : "Objekt";
10917
- const [typ, setTyp] = useState12(einzelTyp ?? "");
10918
- const [zeigeStillgelegte, setZeigeStillgelegte] = useState12(false);
10919
- const [gewaehlt, setGewaehlt] = useState12(null);
10920
- const [neuerTitel, setNeuerTitel] = useState12("");
10921
- const [neuerTyp, setNeuerTyp] = useState12(auswahlTypen[0]);
10922
- const [notiz, setNotiz] = useState12(null);
10923
- const [anlegend, setAnlegend] = useState12(false);
10924
- const [anlageOffen, setAnlageOffen] = useState12(false);
11288
+ const [typ, setTyp] = useState13(einzelTyp ?? "");
11289
+ const [zeigeStillgelegte, setZeigeStillgelegte] = useState13(false);
11290
+ const [gewaehlt, setGewaehlt] = useState13(null);
11291
+ const [neuerTitel, setNeuerTitel] = useState13("");
11292
+ const [neuerTyp, setNeuerTyp] = useState13(auswahlTypen[0]);
11293
+ const [notiz, setNotiz] = useState13(null);
11294
+ const [anlegend, setAnlegend] = useState13(false);
11295
+ const [anlageOffen, setAnlageOffen] = useState13(false);
10925
11296
  const schmal = useSchmalesDisplay();
10926
- const [ansicht, setAnsicht] = useState12("liste");
11297
+ const [ansicht, setAnsicht] = useState13("liste");
10927
11298
  const zeigeListe = !schmal || ansicht === "liste";
10928
11299
  const zeigeDetail = !schmal || ansicht === "detail";
10929
11300
  const waehle = (elementId) => {
@@ -11135,235 +11506,59 @@ var ElementsArea = ({
11135
11506
  };
11136
11507
 
11137
11508
  // src/features/szenen/areas/SceneArea.tsx
11138
- import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
11139
- var SceneArea = ({ port, contextId }) => /* @__PURE__ */ jsxs12("div", { className: "szenen-scene", "data-testid": "bereich-szene", children: [
11140
- /* @__PURE__ */ jsx12("p", { className: "szenen-area__lead", children: "Szenen dieses Kontexts. Auswahl \xF6ffnet die Bearbeitung der Nodes und Beziehungen." }),
11141
- /* @__PURE__ */ jsx12(ElementsArea, { port, contextId, restrictToTypes: ["scene"] })
11142
- ] });
11143
-
11144
- // src/features/szenen/areas/SetupArea.tsx
11145
- import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
11146
- var SetupArea = ({ port, contextId }) => /* @__PURE__ */ jsxs13("div", { className: "szenen-setup", "data-testid": "bereich-setup", children: [
11147
- /* @__PURE__ */ jsx13("p", { className: "szenen-area__lead", children: "Bildaufbauten, Kameras und Stile dieses Kontexts." }),
11148
- /* @__PURE__ */ jsx13(
11149
- ElementsArea,
11150
- {
11151
- port,
11152
- contextId,
11153
- restrictToTypes: ["scene_setup", "camera_preset", "camera_pattern", "style"],
11154
- allTypesLabel: "alle Bildaufbau-Typen",
11155
- listTitle: "Bildaufbau"
11156
- }
11157
- )
11158
- ] });
11159
-
11160
- // src/features/szenen/areas/ShotArea.tsx
11161
- import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
11162
- var ShotArea = ({ port, contextId }) => /* @__PURE__ */ jsxs14("div", { className: "szenen-shot", "data-testid": "bereich-shot", children: [
11163
- /* @__PURE__ */ jsx14("p", { className: "szenen-area__lead", children: "Prompts dieses Kontexts. Auswahl zeigt Runden und startet Prompt-L\xE4ufe." }),
11164
- /* @__PURE__ */ jsx14(
11165
- ElementsArea,
11166
- {
11167
- port,
11168
- contextId,
11169
- restrictToTypes: ["shot", "shot_concept", "target_model"],
11170
- allTypesLabel: "alle Prompt-Typen",
11171
- listTitle: "Prompts"
11172
- }
11173
- )
11174
- ] });
11175
-
11176
- // src/features/szenen/areas/MediaArea.tsx
11177
- import { useState as useState13 } from "react";
11178
- import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
11179
- var leer = async () => ({ kind: "ok", value: [] });
11180
- var MediaArea = ({ port }) => {
11181
- const media = port.media;
11182
- const [versionInput, setVersionInput] = useState13("");
11183
- const [versionId, setVersionId] = useState13("");
11184
- const [itemInput, setItemInput] = useState13("");
11185
- const [itemId, setItemId] = useState13("");
11186
- const [assetId, setAssetId] = useState13("");
11187
- const [role, setRole] = useState13("reference");
11188
- const [position, setPosition] = useState13("0");
11189
- const [notiz, setNotiz] = useState13(null);
11190
- const [busy, setBusy] = useState13(false);
11191
- const [refresh, setRefresh] = useState13(0);
11192
- const [versionPhase] = usePortQuery(
11193
- () => versionId ? media.listVersionMedia(versionId) : leer(),
11194
- [media, versionId, refresh]
11195
- );
11196
- const [itemPhase] = usePortQuery(
11197
- () => itemId ? media.listItemMedia(itemId) : Promise.resolve({ kind: "ok", value: [] }),
11198
- [media, itemId, refresh]
11199
- );
11200
- const zuordnen = async () => {
11201
- if (!versionInput.trim() || !assetId.trim()) {
11202
- setNotiz({ tone: "error", text: "Version-ID und Asset-ID sind erforderlich." });
11203
- return;
11204
- }
11205
- const parsedPosition = Number(position);
11206
- if (!Number.isInteger(parsedPosition) || parsedPosition < 0) {
11207
- setNotiz({ tone: "error", text: `Position muss eine nicht-negative Ganzzahl sein, war ${JSON.stringify(position)}.` });
11208
- return;
11209
- }
11210
- setBusy(true);
11211
- const result = await media.attachVersionMedium({
11212
- versionId: versionInput.trim(),
11213
- assetId: assetId.trim(),
11214
- role,
11215
- position: parsedPosition
11216
- });
11217
- setNotiz(noticeFor(result, "Referenzmedium zuordnen"));
11218
- if (result.kind === "ok") {
11219
- setVersionId(versionInput.trim());
11220
- setRefresh((value) => value + 1);
11221
- }
11222
- setBusy(false);
11223
- };
11224
- const entfernen = async (medium) => {
11225
- setBusy(true);
11226
- const result = await media.detachVersionMedium({
11227
- versionId,
11228
- assetId: medium.assetId,
11229
- role: medium.role
11230
- });
11231
- setNotiz(noticeFor(result, "Referenzmedium entfernen"));
11232
- if (result.kind === "ok") setRefresh((value) => value + 1);
11233
- setBusy(false);
11234
- };
11235
- const versionMedien = versionPhase.status === "ready" ? versionPhase.value : [];
11236
- const itemMedien = itemPhase.status === "ready" ? itemPhase.value : [];
11237
- return /* @__PURE__ */ jsxs15("div", { className: "szenen-media", "data-testid": "bereich-medien", children: [
11238
- /* @__PURE__ */ jsx15("p", { className: "szenen-area__lead", children: "Bereits vorhandene Medien-Assets einer Elementversion zuordnen. Hochgeladen wird hier nichts \u2014 Medien entstehen \xFCber den bestehenden Speicherweg aus Stufe 5." }),
11239
- /* @__PURE__ */ jsxs15("section", { children: [
11240
- /* @__PURE__ */ jsx15("h3", { children: "Version" }),
11241
- /* @__PURE__ */ jsxs15("div", { className: "szenen-media__lookup", children: [
11242
- /* @__PURE__ */ jsxs15("label", { children: [
11243
- "Version-ID",
11244
- /* @__PURE__ */ jsx15("input", { value: versionInput, onChange: (event) => setVersionInput(event.target.value) })
11245
- ] }),
11246
- /* @__PURE__ */ jsx15("button", { type: "button", onClick: () => setVersionId(versionInput.trim()), children: "Medien laden" })
11247
- ] }),
11248
- /* @__PURE__ */ jsxs15("div", { className: "szenen-media__attach", children: [
11249
- /* @__PURE__ */ jsxs15("label", { children: [
11250
- "Asset-ID",
11251
- /* @__PURE__ */ jsx15("input", { value: assetId, onChange: (event) => setAssetId(event.target.value) })
11252
- ] }),
11253
- /* @__PURE__ */ jsxs15("label", { children: [
11254
- "Rolle",
11255
- /* @__PURE__ */ jsxs15("select", { value: role, onChange: (event) => setRole(event.target.value), children: [
11256
- /* @__PURE__ */ jsx15("option", { value: "reference", children: "reference" }),
11257
- /* @__PURE__ */ jsx15("option", { value: "mask", children: "mask" }),
11258
- /* @__PURE__ */ jsx15("option", { value: "palette", children: "palette" }),
11259
- /* @__PURE__ */ jsx15("option", { value: "example", children: "example" })
11260
- ] })
11261
- ] }),
11262
- /* @__PURE__ */ jsxs15("label", { children: [
11263
- "Position",
11264
- /* @__PURE__ */ jsx15("input", { type: "number", min: "0", value: position, onChange: (event) => setPosition(event.target.value) })
11265
- ] }),
11266
- /* @__PURE__ */ jsx15("button", { type: "button", disabled: busy, onClick: zuordnen, children: "zuordnen" })
11267
- ] }),
11268
- versionPhase.status === "loading" && versionId && /* @__PURE__ */ jsx15("p", { children: "Versionsmedien werden geladen \u2026" }),
11269
- versionPhase.status === "error" && /* @__PURE__ */ jsxs15("p", { role: "alert", children: [
11270
- "Versionsmedien konnten nicht geladen werden: ",
11271
- versionPhase.message
11272
- ] }),
11273
- versionPhase.status === "ready" && versionId && versionMedien.length === 0 && /* @__PURE__ */ jsxs15("p", { children: [
11274
- "Version ",
11275
- versionId,
11276
- " hat keine Medien."
11277
- ] }),
11278
- /* @__PURE__ */ jsx15("ul", { className: "szenen-media__list", children: versionMedien.map((medium) => /* @__PURE__ */ jsxs15("li", { children: [
11279
- /* @__PURE__ */ jsx15("code", { children: medium.assetId }),
11280
- /* @__PURE__ */ jsxs15("span", { children: [
11281
- medium.role,
11282
- " \xB7 Position ",
11283
- medium.position
11284
- ] }),
11285
- medium.url && /* @__PURE__ */ jsx15("a", { href: medium.url, target: "_blank", rel: "noreferrer", children: "\xF6ffnen" }),
11286
- /* @__PURE__ */ jsx15("button", { type: "button", disabled: busy, onClick: () => entfernen(medium), children: "entfernen" })
11287
- ] }, `${medium.assetId}:${medium.role}`)) })
11288
- ] }),
11289
- /* @__PURE__ */ jsxs15("section", { children: [
11290
- /* @__PURE__ */ jsx15("h3", { children: "Item-Medien" }),
11291
- /* @__PURE__ */ jsxs15("div", { className: "szenen-media__lookup", children: [
11292
- /* @__PURE__ */ jsxs15("label", { children: [
11293
- "Item-ID",
11294
- /* @__PURE__ */ jsx15("input", { value: itemInput, onChange: (event) => setItemInput(event.target.value) })
11295
- ] }),
11296
- /* @__PURE__ */ jsx15("button", { type: "button", onClick: () => setItemId(itemInput.trim()), children: "Item-Medien laden" })
11297
- ] }),
11298
- itemPhase.status === "error" && /* @__PURE__ */ jsxs15("p", { role: "alert", children: [
11299
- "Item-Medien konnten nicht geladen werden: ",
11300
- itemPhase.message
11301
- ] }),
11302
- /* @__PURE__ */ jsx15("ul", { className: "szenen-media__list", children: itemMedien.map((medium) => /* @__PURE__ */ jsxs15("li", { children: [
11303
- /* @__PURE__ */ jsx15("code", { children: medium.assetId }),
11304
- /* @__PURE__ */ jsxs15("span", { children: [
11305
- medium.role,
11306
- " \xB7 Position ",
11307
- medium.position
11308
- ] }),
11309
- medium.url && /* @__PURE__ */ jsx15("a", { href: medium.url, target: "_blank", rel: "noreferrer", children: "\xF6ffnen" })
11310
- ] }, `${medium.assetId}:${medium.role}:${medium.position}`)) })
11311
- ] }),
11312
- notiz && /* @__PURE__ */ jsx15("p", { role: notiz.tone === "error" ? "alert" : "status", "data-tone": notiz.tone, children: notiz.text })
11313
- ] });
11314
- };
11509
+ import { useState as useState17 } from "react";
11315
11510
 
11316
11511
  // src/features/szenen/areas/DeriveArea.tsx
11317
- import { useEffect as useEffect8, useMemo as useMemo3, useRef as useRef4, useState as useState16 } from "react";
11512
+ import { useEffect as useEffect9, useMemo as useMemo3, useRef as useRef4, useState as useState16 } from "react";
11318
11513
 
11319
11514
  // src/features/szenen/areas/RecoveryPanel.tsx
11320
11515
  import { useState as useState14 } from "react";
11321
- import { Fragment as Fragment6, jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
11516
+ import { Fragment as Fragment6, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
11322
11517
  var Diagnosen = ({ befunde, testId }) => {
11323
11518
  if (befunde.length === 0) return null;
11324
- return /* @__PURE__ */ jsx16("ul", { className: "szenen-diagnose__liste", "data-testid": testId, children: befunde.map((d, i) => /* @__PURE__ */ jsxs16("li", { "data-severity": d.severity, children: [
11325
- /* @__PURE__ */ jsx16("code", { children: d.code }),
11326
- d.proposal_type && /* @__PURE__ */ jsxs16(Fragment6, { children: [
11519
+ return /* @__PURE__ */ jsx12("ul", { className: "szenen-diagnose__liste", "data-testid": testId, children: befunde.map((d, i) => /* @__PURE__ */ jsxs12("li", { "data-severity": d.severity, children: [
11520
+ /* @__PURE__ */ jsx12("code", { children: d.code }),
11521
+ d.proposal_type && /* @__PURE__ */ jsxs12(Fragment6, { children: [
11327
11522
  " \xB7 ",
11328
- /* @__PURE__ */ jsx16("strong", { children: d.proposal_type })
11523
+ /* @__PURE__ */ jsx12("strong", { children: d.proposal_type })
11329
11524
  ] }),
11330
- d.pointer && /* @__PURE__ */ jsxs16(Fragment6, { children: [
11525
+ d.pointer && /* @__PURE__ */ jsxs12(Fragment6, { children: [
11331
11526
  " \xB7 ",
11332
- /* @__PURE__ */ jsx16("code", { children: d.pointer })
11527
+ /* @__PURE__ */ jsx12("code", { children: d.pointer })
11333
11528
  ] }),
11334
- typeof d.line === "number" && /* @__PURE__ */ jsxs16(Fragment6, { children: [
11529
+ typeof d.line === "number" && /* @__PURE__ */ jsxs12(Fragment6, { children: [
11335
11530
  " \xB7 Zeile ",
11336
11531
  d.line,
11337
11532
  ", Spalte ",
11338
11533
  d.column
11339
11534
  ] }),
11340
- /* @__PURE__ */ jsx16("div", { children: d.message })
11535
+ /* @__PURE__ */ jsx12("div", { children: d.message })
11341
11536
  ] }, i)) });
11342
11537
  };
11343
- var Ausbeute = ({ bewertung }) => /* @__PURE__ */ jsxs16("div", { className: "szenen-diagnose", "data-testid": "rettung-ergebnis", children: [
11344
- /* @__PURE__ */ jsxs16("p", { "data-testid": "rettung-status", children: [
11538
+ var Ausbeute = ({ bewertung }) => /* @__PURE__ */ jsxs12("div", { className: "szenen-diagnose", "data-testid": "rettung-ergebnis", children: [
11539
+ /* @__PURE__ */ jsxs12("p", { "data-testid": "rettung-status", children: [
11345
11540
  bewertung.status === "usable" && "Diese Fassung ist vollst\xE4ndig verwertbar.",
11346
11541
  bewertung.status === "partial" && "Diese Fassung ist teilweise verwertbar \u2014 nicht alles kommt durch.",
11347
11542
  bewertung.status === "invalid" && "Aus dieser Fassung ist nichts zu \xFCbernehmen."
11348
11543
  ] }),
11349
- bewertung.proposals.length > 0 && /* @__PURE__ */ jsxs16(Fragment6, { children: [
11350
- /* @__PURE__ */ jsx16("h5", { children: "Verwertbar" }),
11351
- /* @__PURE__ */ jsx16("ul", { "data-testid": "rettung-verwertbar", children: bewertung.proposals.map((p) => /* @__PURE__ */ jsxs16("li", { "data-testid": `rettung-verwertbar-${p.type}`, children: [
11352
- /* @__PURE__ */ jsx16("strong", { children: p.type }),
11544
+ bewertung.proposals.length > 0 && /* @__PURE__ */ jsxs12(Fragment6, { children: [
11545
+ /* @__PURE__ */ jsx12("h5", { children: "Verwertbar" }),
11546
+ /* @__PURE__ */ jsx12("ul", { "data-testid": "rettung-verwertbar", children: bewertung.proposals.map((p) => /* @__PURE__ */ jsxs12("li", { "data-testid": `rettung-verwertbar-${p.type}`, children: [
11547
+ /* @__PURE__ */ jsx12("strong", { children: p.type }),
11353
11548
  " \u2014 ",
11354
11549
  p.status === "complete" ? "vollst\xE4ndig" : "teilweise; vor dem Ver\xF6ffentlichen zu erg\xE4nzen",
11355
- p.status === "partial" && p.gaps && p.gaps.length > 0 && /* @__PURE__ */ jsx16("ul", { children: p.gaps.map((l, i) => /* @__PURE__ */ jsxs16("li", { children: [
11356
- /* @__PURE__ */ jsx16("code", { children: l.pointer }),
11550
+ p.status === "partial" && p.gaps && p.gaps.length > 0 && /* @__PURE__ */ jsx12("ul", { children: p.gaps.map((l, i) => /* @__PURE__ */ jsxs12("li", { children: [
11551
+ /* @__PURE__ */ jsx12("code", { children: l.pointer }),
11357
11552
  ": ",
11358
11553
  l.reason
11359
11554
  ] }, i)) })
11360
11555
  ] }, p.type)) })
11361
11556
  ] }),
11362
- bewertung.rejected.length > 0 && /* @__PURE__ */ jsxs16(Fragment6, { children: [
11363
- /* @__PURE__ */ jsx16("h5", { children: "Nicht verwertbar" }),
11364
- /* @__PURE__ */ jsx16("ul", { "data-testid": "rettung-abgelehnt", children: bewertung.rejected.map((r) => /* @__PURE__ */ jsxs16("li", { "data-testid": `rettung-abgelehnt-${r.type}`, children: [
11365
- /* @__PURE__ */ jsx16("strong", { children: r.type }),
11366
- /* @__PURE__ */ jsx16(Diagnosen, { befunde: r.diagnostics, testId: `rettung-grund-${r.type}` })
11557
+ bewertung.rejected.length > 0 && /* @__PURE__ */ jsxs12(Fragment6, { children: [
11558
+ /* @__PURE__ */ jsx12("h5", { children: "Nicht verwertbar" }),
11559
+ /* @__PURE__ */ jsx12("ul", { "data-testid": "rettung-abgelehnt", children: bewertung.rejected.map((r) => /* @__PURE__ */ jsxs12("li", { "data-testid": `rettung-abgelehnt-${r.type}`, children: [
11560
+ /* @__PURE__ */ jsx12("strong", { children: r.type }),
11561
+ /* @__PURE__ */ jsx12(Diagnosen, { befunde: r.diagnostics, testId: `rettung-grund-${r.type}` })
11367
11562
  ] }, r.type)) })
11368
11563
  ] })
11369
11564
  ] });
@@ -11408,16 +11603,16 @@ var RecoveryPanel = ({ port, lauf }) => {
11408
11603
  setLaeuft(false);
11409
11604
  }
11410
11605
  }
11411
- return /* @__PURE__ */ jsxs16("section", { className: "szenen-rettung", "data-testid": "rettung", children: [
11412
- /* @__PURE__ */ jsx16("h4", { children: "Antwort retten" }),
11413
- /* @__PURE__ */ jsxs16("p", { className: "szenen-area__lead", children: [
11606
+ return /* @__PURE__ */ jsxs12("section", { className: "szenen-rettung", "data-testid": "rettung", children: [
11607
+ /* @__PURE__ */ jsx12("h4", { children: "Antwort retten" }),
11608
+ /* @__PURE__ */ jsxs12("p", { className: "szenen-area__lead", children: [
11414
11609
  "Der Lauf ist bezahlt und seine Antwort liegt vor. Sie l\xE4sst sich hier pr\xFCfen und korrigieren \u2014 ",
11415
- /* @__PURE__ */ jsx16("strong", { children: "ohne" }),
11610
+ /* @__PURE__ */ jsx12("strong", { children: "ohne" }),
11416
11611
  " einen weiteren Modellaufruf. Das Original bleibt unver\xE4ndert."
11417
11612
  ] }),
11418
- /* @__PURE__ */ jsxs16("label", { className: "szenen-field", children: [
11419
- /* @__PURE__ */ jsx16("span", { children: "Original der Modellantwort (unver\xE4nderlich)" }),
11420
- /* @__PURE__ */ jsx16(
11613
+ /* @__PURE__ */ jsxs12("label", { className: "szenen-field", children: [
11614
+ /* @__PURE__ */ jsx12("span", { children: "Original der Modellantwort (unver\xE4nderlich)" }),
11615
+ /* @__PURE__ */ jsx12(
11421
11616
  "textarea",
11422
11617
  {
11423
11618
  readOnly: true,
@@ -11428,12 +11623,12 @@ var RecoveryPanel = ({ port, lauf }) => {
11428
11623
  }
11429
11624
  )
11430
11625
  ] }),
11431
- /* @__PURE__ */ jsxs16("label", { className: "szenen-field", children: [
11432
- /* @__PURE__ */ jsxs16("span", { children: [
11626
+ /* @__PURE__ */ jsxs12("label", { className: "szenen-field", children: [
11627
+ /* @__PURE__ */ jsxs12("span", { children: [
11433
11628
  "Arbeitskopie",
11434
11629
  bearbeitet ? " (bearbeitet)" : ""
11435
11630
  ] }),
11436
- /* @__PURE__ */ jsx16(
11631
+ /* @__PURE__ */ jsx12(
11437
11632
  "textarea",
11438
11633
  {
11439
11634
  rows: 8,
@@ -11443,8 +11638,8 @@ var RecoveryPanel = ({ port, lauf }) => {
11443
11638
  }
11444
11639
  )
11445
11640
  ] }),
11446
- /* @__PURE__ */ jsxs16("div", { className: "szenen-rettung__aktionen", children: [
11447
- /* @__PURE__ */ jsx16(
11641
+ /* @__PURE__ */ jsxs12("div", { className: "szenen-rettung__aktionen", children: [
11642
+ /* @__PURE__ */ jsx12(
11448
11643
  "button",
11449
11644
  {
11450
11645
  type: "button",
@@ -11454,7 +11649,7 @@ var RecoveryPanel = ({ port, lauf }) => {
11454
11649
  children: "Arbeitskopie pr\xFCfen (kein Modellaufruf)"
11455
11650
  }
11456
11651
  ),
11457
- /* @__PURE__ */ jsx16(
11652
+ /* @__PURE__ */ jsx12(
11458
11653
  "button",
11459
11654
  {
11460
11655
  type: "button",
@@ -11464,7 +11659,7 @@ var RecoveryPanel = ({ port, lauf }) => {
11464
11659
  children: "Arbeitsfassung speichern"
11465
11660
  }
11466
11661
  ),
11467
- arbeitskopie !== null && /* @__PURE__ */ jsx16(
11662
+ arbeitskopie !== null && /* @__PURE__ */ jsx12(
11468
11663
  "button",
11469
11664
  {
11470
11665
  type: "button",
@@ -11479,15 +11674,15 @@ var RecoveryPanel = ({ port, lauf }) => {
11479
11674
  }
11480
11675
  )
11481
11676
  ] }),
11482
- meldung && /* @__PURE__ */ jsx16("p", { className: "szenen-error", role: "alert", "data-testid": "rettung-meldung", children: meldung }),
11483
- bewertung && /* @__PURE__ */ jsx16(Ausbeute, { bewertung }),
11484
- bewertung && bewertung.diagnostics.length > 0 && /* @__PURE__ */ jsxs16("div", { className: "szenen-diagnose", children: [
11485
- /* @__PURE__ */ jsx16("h5", { children: "Befunde an dieser Fassung" }),
11486
- /* @__PURE__ */ jsx16(Diagnosen, { befunde: bewertung.diagnostics, testId: "rettung-befunde" })
11677
+ meldung && /* @__PURE__ */ jsx12("p", { className: "szenen-error", role: "alert", "data-testid": "rettung-meldung", children: meldung }),
11678
+ bewertung && /* @__PURE__ */ jsx12(Ausbeute, { bewertung }),
11679
+ bewertung && bewertung.diagnostics.length > 0 && /* @__PURE__ */ jsxs12("div", { className: "szenen-diagnose", children: [
11680
+ /* @__PURE__ */ jsx12("h5", { children: "Befunde an dieser Fassung" }),
11681
+ /* @__PURE__ */ jsx12(Diagnosen, { befunde: bewertung.diagnostics, testId: "rettung-befunde" })
11487
11682
  ] }),
11488
- gesichert && /* @__PURE__ */ jsxs16("p", { "data-testid": "rettung-gesichert", children: [
11683
+ gesichert && /* @__PURE__ */ jsxs12("p", { "data-testid": "rettung-gesichert", children: [
11489
11684
  "Gespeichert als Arbeitsfassung ",
11490
- /* @__PURE__ */ jsx16("code", { children: gesichert.recovery_id }),
11685
+ /* @__PURE__ */ jsx12("code", { children: gesichert.recovery_id }),
11491
11686
  gesichert.edited ? " (bearbeitet)" : " (unver\xE4ndert)",
11492
11687
  ". Das Original des Laufs ist davon unber\xFChrt."
11493
11688
  ] })
@@ -11496,7 +11691,7 @@ var RecoveryPanel = ({ port, lauf }) => {
11496
11691
 
11497
11692
  // src/features/szenen/areas/ChainActions.tsx
11498
11693
  import { useState as useState15 } from "react";
11499
- import { Fragment as Fragment7, jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
11694
+ import { Fragment as Fragment7, jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
11500
11695
  var ChainActions = ({ port, runId, recoveryId }) => {
11501
11696
  const [graph, setGraph] = useState15(null);
11502
11697
  const [meldung, setMeldung] = useState15(null);
@@ -11522,14 +11717,14 @@ var ChainActions = ({ port, runId, recoveryId }) => {
11522
11717
  setLaeuft(false);
11523
11718
  }
11524
11719
  }
11525
- return /* @__PURE__ */ jsxs17("section", { className: "szenen-kette", "data-testid": "kette", children: [
11526
- /* @__PURE__ */ jsx17("h4", { children: "Draft-Kette" }),
11527
- /* @__PURE__ */ jsxs17("p", { className: "szenen-area__lead", children: [
11720
+ return /* @__PURE__ */ jsxs13("section", { className: "szenen-kette", "data-testid": "kette", children: [
11721
+ /* @__PURE__ */ jsx13("h4", { children: "Draft-Kette" }),
11722
+ /* @__PURE__ */ jsxs13("p", { className: "szenen-area__lead", children: [
11528
11723
  "Szene, Aufbau und Shot entstehen als zusammenh\xE4ngende Entw\xFCrfe. Daraus l\xE4sst sich sofort ein Prompt erzeugen \u2014 ",
11529
- /* @__PURE__ */ jsx17("strong", { children: "ohne" }),
11724
+ /* @__PURE__ */ jsx13("strong", { children: "ohne" }),
11530
11725
  " vorher etwas zu ver\xF6ffentlichen."
11531
11726
  ] }),
11532
- !graph && /* @__PURE__ */ jsx17(
11727
+ !graph && /* @__PURE__ */ jsx13(
11533
11728
  "button",
11534
11729
  {
11535
11730
  type: "button",
@@ -11539,34 +11734,34 @@ var ChainActions = ({ port, runId, recoveryId }) => {
11539
11734
  children: "Als Draft-Kette \xFCbernehmen"
11540
11735
  }
11541
11736
  ),
11542
- graph && /* @__PURE__ */ jsxs17("div", { "data-testid": "kette-ergebnis", children: [
11543
- /* @__PURE__ */ jsxs17("p", { children: [
11737
+ graph && /* @__PURE__ */ jsxs13("div", { "data-testid": "kette-ergebnis", children: [
11738
+ /* @__PURE__ */ jsxs13("p", { children: [
11544
11739
  "Kette ",
11545
- /* @__PURE__ */ jsx17("code", { children: graph.chainId }),
11740
+ /* @__PURE__ */ jsx13("code", { children: graph.chainId }),
11546
11741
  " angelegt. Kein Knoten ist ver\xF6ffentlicht."
11547
11742
  ] }),
11548
- /* @__PURE__ */ jsx17("ul", { "data-testid": "kette-knoten", children: graph.created.map((k) => /* @__PURE__ */ jsxs17("li", { "data-testid": `kette-knoten-${k.type}`, children: [
11549
- /* @__PURE__ */ jsx17("strong", { children: k.type }),
11743
+ /* @__PURE__ */ jsx13("ul", { "data-testid": "kette-knoten", children: graph.created.map((k) => /* @__PURE__ */ jsxs13("li", { "data-testid": `kette-knoten-${k.type}`, children: [
11744
+ /* @__PURE__ */ jsx13("strong", { children: k.type }),
11550
11745
  " \u2014 Element ",
11551
- /* @__PURE__ */ jsx17("code", { children: k.elementId }),
11746
+ /* @__PURE__ */ jsx13("code", { children: k.elementId }),
11552
11747
  ", Entwurf ",
11553
- /* @__PURE__ */ jsx17("code", { children: k.draftId }),
11748
+ /* @__PURE__ */ jsx13("code", { children: k.draftId }),
11554
11749
  " (Revision ",
11555
11750
  k.revision,
11556
11751
  ")"
11557
11752
  ] }, k.type)) }),
11558
- graph.missing.length > 0 && /* @__PURE__ */ jsxs17("div", { className: "szenen-diagnose", "data-testid": "kette-fehlend", children: [
11559
- /* @__PURE__ */ jsx17("h5", { children: "Unvollst\xE4ndig erzeugt" }),
11560
- /* @__PURE__ */ jsx17("ul", { children: graph.missing.map((m) => /* @__PURE__ */ jsxs17("li", { children: [
11561
- /* @__PURE__ */ jsx17("strong", { children: m.type }),
11753
+ graph.missing.length > 0 && /* @__PURE__ */ jsxs13("div", { className: "szenen-diagnose", "data-testid": "kette-fehlend", children: [
11754
+ /* @__PURE__ */ jsx13("h5", { children: "Unvollst\xE4ndig erzeugt" }),
11755
+ /* @__PURE__ */ jsx13("ul", { children: graph.missing.map((m) => /* @__PURE__ */ jsxs13("li", { children: [
11756
+ /* @__PURE__ */ jsx13("strong", { children: m.type }),
11562
11757
  ": ",
11563
11758
  m.reason
11564
11759
  ] }, m.type)) })
11565
11760
  ] })
11566
11761
  ] }),
11567
- shotKnoten && /* @__PURE__ */ jsxs17(Fragment7, { children: [
11568
- /* @__PURE__ */ jsx17("p", { children: 'Die Entw\xFCrfe lassen sich jetzt unter \u201E1 \xB7 Bausteine" \xE4ndern. Der Prompt-Lauf friert den Stand ein, den er dabei vorfindet \u2014 dieselbe Aktion steht auch am Shot selbst, unter \u201E4 \xB7 Prompt".' }),
11569
- /* @__PURE__ */ jsx17(
11762
+ shotKnoten && /* @__PURE__ */ jsxs13(Fragment7, { children: [
11763
+ /* @__PURE__ */ jsx13("p", { children: 'Die Entw\xFCrfe lassen sich jetzt unter \u201E1 \xB7 Bausteine" \xE4ndern. Der Prompt-Lauf friert den Stand ein, den er dabei vorfindet \u2014 dieselbe Aktion steht auch am Shot selbst, unter \u201E4 \xB7 Prompt".' }),
11764
+ /* @__PURE__ */ jsx13(
11570
11765
  PromptStartAction,
11571
11766
  {
11572
11767
  port,
@@ -11574,12 +11769,12 @@ var ChainActions = ({ port, runId, recoveryId }) => {
11574
11769
  }
11575
11770
  )
11576
11771
  ] }),
11577
- meldung && /* @__PURE__ */ jsx17("p", { className: "szenen-error", role: "alert", "data-testid": "kette-meldung", children: meldung })
11772
+ meldung && /* @__PURE__ */ jsx13("p", { className: "szenen-error", role: "alert", "data-testid": "kette-meldung", children: meldung })
11578
11773
  ] });
11579
11774
  };
11580
11775
 
11581
11776
  // src/features/szenen/areas/DeriveArea.tsx
11582
- import { Fragment as Fragment8, jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
11777
+ import { Fragment as Fragment8, jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
11583
11778
  var MODUS_TEXT = {
11584
11779
  prompt: "Nur Prompt",
11585
11780
  image: "Nur Bild",
@@ -11597,15 +11792,15 @@ var BelegFeld = ({ titel, text, testId, offen }) => {
11597
11792
  setKopiert(false);
11598
11793
  }
11599
11794
  }
11600
- return /* @__PURE__ */ jsxs18("details", { open: offen, "data-testid": `belege-${testId}`, children: [
11601
- /* @__PURE__ */ jsxs18("summary", { children: [
11795
+ return /* @__PURE__ */ jsxs14("details", { open: offen, "data-testid": `belege-${testId}`, children: [
11796
+ /* @__PURE__ */ jsxs14("summary", { children: [
11602
11797
  titel,
11603
11798
  " (",
11604
11799
  text.length,
11605
11800
  " Zeichen)"
11606
11801
  ] }),
11607
- /* @__PURE__ */ jsxs18("div", { className: "szenen-beleg__kopf", children: [
11608
- /* @__PURE__ */ jsx18(
11802
+ /* @__PURE__ */ jsxs14("div", { className: "szenen-beleg__kopf", children: [
11803
+ /* @__PURE__ */ jsx14(
11609
11804
  "button",
11610
11805
  {
11611
11806
  type: "button",
@@ -11614,9 +11809,9 @@ var BelegFeld = ({ titel, text, testId, offen }) => {
11614
11809
  children: "Kopieren"
11615
11810
  }
11616
11811
  ),
11617
- kopiert && /* @__PURE__ */ jsx18("span", { "data-testid": `kopiert-${testId}`, children: "Kopiert." })
11812
+ kopiert && /* @__PURE__ */ jsx14("span", { "data-testid": `kopiert-${testId}`, children: "Kopiert." })
11618
11813
  ] }),
11619
- /* @__PURE__ */ jsx18(
11814
+ /* @__PURE__ */ jsx14(
11620
11815
  "textarea",
11621
11816
  {
11622
11817
  ref: feld,
@@ -11630,8 +11825,8 @@ var BelegFeld = ({ titel, text, testId, offen }) => {
11630
11825
  )
11631
11826
  ] });
11632
11827
  };
11633
- var Belege = ({ lauf }) => /* @__PURE__ */ jsxs18("div", { className: "szenen-diagnose", "data-testid": "belege", children: [
11634
- lauf.raw_output ? /* @__PURE__ */ jsx18(
11828
+ var Belege = ({ lauf }) => /* @__PURE__ */ jsxs14("div", { className: "szenen-diagnose", "data-testid": "belege", children: [
11829
+ lauf.raw_output ? /* @__PURE__ */ jsx14(
11635
11830
  BelegFeld,
11636
11831
  {
11637
11832
  titel: "Rohantwort des Modells",
@@ -11642,9 +11837,9 @@ var Belege = ({ lauf }) => /* @__PURE__ */ jsxs18("div", { className: "szenen-di
11642
11837
  ) : (
11643
11838
  /* Kein Text ist auch eine Auskunft: Der Provider hat nichts geliefert,
11644
11839
  das Modell hat also nicht „falsch geantwortet". */
11645
- /* @__PURE__ */ jsx18("p", { "data-testid": "roh-antwort-fehlt", children: "Es gibt keine Rohantwort \u2014 der Lauf ist gescheitert, bevor ein Text ankam (etwa Netz, Sperre oder Providerfehler)." })
11840
+ /* @__PURE__ */ jsx14("p", { "data-testid": "roh-antwort-fehlt", children: "Es gibt keine Rohantwort \u2014 der Lauf ist gescheitert, bevor ein Text ankam (etwa Netz, Sperre oder Providerfehler)." })
11646
11841
  ),
11647
- /* @__PURE__ */ jsx18(
11842
+ /* @__PURE__ */ jsx14(
11648
11843
  BelegFeld,
11649
11844
  {
11650
11845
  titel: "Systemprompt dieses Laufs",
@@ -11656,18 +11851,18 @@ var Belege = ({ lauf }) => /* @__PURE__ */ jsxs18("div", { className: "szenen-di
11656
11851
  ] });
11657
11852
  var Herkunft = ({ lauf }) => {
11658
11853
  if (!lauf.follows) return null;
11659
- return /* @__PURE__ */ jsxs18("p", { className: "szenen-run__herkunft", "data-testid": "folgt-auf", children: [
11854
+ return /* @__PURE__ */ jsxs14("p", { className: "szenen-run__herkunft", "data-testid": "folgt-auf", children: [
11660
11855
  "Folgt auf Lauf ",
11661
- /* @__PURE__ */ jsx18("code", { children: lauf.follows.run_id }),
11856
+ /* @__PURE__ */ jsx14("code", { children: lauf.follows.run_id }),
11662
11857
  ".",
11663
11858
  lauf.status === "succeeded" && " Im Folgelauf gelungen."
11664
11859
  ] });
11665
11860
  };
11666
11861
  var Widerspruch = ({ lauf }) => {
11667
11862
  if (!lauf.reply) return null;
11668
- return /* @__PURE__ */ jsxs18("div", { className: "szenen-diagnose szenen-diagnose--antwort", "data-testid": "modell-antwort", children: [
11669
- /* @__PURE__ */ jsx18("h4", { children: "Das Modell antwortet auf die Beanstandung" }),
11670
- /* @__PURE__ */ jsx18("p", { children: lauf.reply })
11863
+ return /* @__PURE__ */ jsxs14("div", { className: "szenen-diagnose szenen-diagnose--antwort", "data-testid": "modell-antwort", children: [
11864
+ /* @__PURE__ */ jsx14("h4", { children: "Das Modell antwortet auf die Beanstandung" }),
11865
+ /* @__PURE__ */ jsx14("p", { children: lauf.reply })
11671
11866
  ] });
11672
11867
  };
11673
11868
  function readAsBase642(datei) {
@@ -11686,7 +11881,7 @@ var ERSTE_KACHELN = 12;
11686
11881
  function useSichtbareKacheln(alle, wurzel) {
11687
11882
  const [gesehen, setGesehen] = useState16([]);
11688
11883
  const schluessel = alle.join(" ");
11689
- useEffect8(() => {
11884
+ useEffect9(() => {
11690
11885
  const ziel = wurzel.current;
11691
11886
  if (!ziel || typeof IntersectionObserver === "undefined") return void 0;
11692
11887
  const beobachter = new IntersectionObserver((eintraege) => {
@@ -11708,7 +11903,7 @@ function useVorschauen(port, mediaRefs) {
11708
11903
  const gehalten = useRef4(/* @__PURE__ */ new Map());
11709
11904
  const ohneBild = useRef4(/* @__PURE__ */ new Set());
11710
11905
  const schluessel = mediaRefs.join(" ");
11711
- useEffect8(() => {
11906
+ useEffect9(() => {
11712
11907
  const bestand = port.imageLibrary;
11713
11908
  if (!bestand?.loadPreview) return void 0;
11714
11909
  const refs = schluessel ? schluessel.split(" ") : [];
@@ -11774,10 +11969,10 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
11774
11969
  if (item.images.length === 1) onWaehlen(deckbild);
11775
11970
  else setFokus(item.itemRef === fokus ? null : item.itemRef);
11776
11971
  }
11777
- return /* @__PURE__ */ jsxs18("div", { className: "szenen-bildwahl", "data-testid": "bildwahl", children: [
11778
- /* @__PURE__ */ jsxs18("label", { className: "szenen-field", children: [
11779
- /* @__PURE__ */ jsx18("span", { children: "Bild aus dem Bestand" }),
11780
- /* @__PURE__ */ jsx18(
11972
+ return /* @__PURE__ */ jsxs14("div", { className: "szenen-bildwahl", "data-testid": "bildwahl", children: [
11973
+ /* @__PURE__ */ jsxs14("label", { className: "szenen-field", children: [
11974
+ /* @__PURE__ */ jsx14("span", { children: "Bild aus dem Bestand" }),
11975
+ /* @__PURE__ */ jsx14(
11781
11976
  "input",
11782
11977
  {
11783
11978
  type: "search",
@@ -11791,23 +11986,23 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
11791
11986
  }
11792
11987
  )
11793
11988
  ] }),
11794
- items.status === "loading" && /* @__PURE__ */ jsx18("p", { children: "Bilder werden geladen \u2026" }),
11795
- items.status === "error" && /* @__PURE__ */ jsxs18("p", { className: "szenen-error", role: "alert", children: [
11989
+ items.status === "loading" && /* @__PURE__ */ jsx14("p", { children: "Bilder werden geladen \u2026" }),
11990
+ items.status === "error" && /* @__PURE__ */ jsxs14("p", { className: "szenen-error", role: "alert", children: [
11796
11991
  "Der Bildbestand ist nicht lesbar: ",
11797
11992
  items.message
11798
11993
  ] }),
11799
- items.status === "ready" && werke.length === 0 && /* @__PURE__ */ jsx18("p", { className: "szenen-empty", "data-testid": "bildwahl-leer", children: "Zu dieser Suche gibt es kein Bild. Ohne Treffer bleibt der Upload." }),
11800
- werke.length > 0 && /* @__PURE__ */ jsx18(
11994
+ items.status === "ready" && werke.length === 0 && /* @__PURE__ */ jsx14("p", { className: "szenen-empty", "data-testid": "bildwahl-leer", children: "Zu dieser Suche gibt es kein Bild. Ohne Treffer bleibt der Upload." }),
11995
+ werke.length > 0 && /* @__PURE__ */ jsx14(
11801
11996
  "div",
11802
11997
  {
11803
11998
  className: "szenen-bildwahl__browser",
11804
11999
  ref: browser,
11805
12000
  "data-testid": "bildwahl-werke",
11806
- children: /* @__PURE__ */ jsx18("ul", { className: "szenen-bildwahl__raster", children: werke.map((item) => {
12001
+ children: /* @__PURE__ */ jsx14("ul", { className: "szenen-bildwahl__raster", children: werke.map((item) => {
11807
12002
  const deckbild = item.images[0];
11808
12003
  if (!deckbild) return null;
11809
12004
  const fokussiert = item.itemRef === fokus;
11810
- return /* @__PURE__ */ jsx18("li", { "data-media-ref": deckbild.mediaRef, children: /* @__PURE__ */ jsxs18(
12005
+ return /* @__PURE__ */ jsx14("li", { "data-media-ref": deckbild.mediaRef, children: /* @__PURE__ */ jsxs14(
11811
12006
  "button",
11812
12007
  {
11813
12008
  type: "button",
@@ -11817,7 +12012,7 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
11817
12012
  "data-testid": `item-waehlen-${item.itemRef}`,
11818
12013
  title: item.title,
11819
12014
  children: [
11820
- /* @__PURE__ */ jsx18(
12015
+ /* @__PURE__ */ jsx14(
11821
12016
  Kachel,
11822
12017
  {
11823
12018
  quelle: quelleVon(deckbild),
@@ -11825,7 +12020,7 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
11825
12020
  ...gescheitert.has(deckbild.mediaRef) ? { leerText: "Vorschau fehlt" } : {}
11826
12021
  }
11827
12022
  ),
11828
- item.images.length > 1 && /* @__PURE__ */ jsxs18(
12023
+ item.images.length > 1 && /* @__PURE__ */ jsxs14(
11829
12024
  "span",
11830
12025
  {
11831
12026
  className: "szenen-bildwahl__anzahl",
@@ -11836,22 +12031,22 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
11836
12031
  ]
11837
12032
  }
11838
12033
  ),
11839
- /* @__PURE__ */ jsx18("span", { className: "szenen-bildwahl__kacheltitel", children: item.title })
12034
+ /* @__PURE__ */ jsx14("span", { className: "szenen-bildwahl__kacheltitel", children: item.title })
11840
12035
  ]
11841
12036
  }
11842
12037
  ) }, item.itemRef);
11843
12038
  }) })
11844
12039
  }
11845
12040
  ),
11846
- werk && /* @__PURE__ */ jsxs18("section", { className: "szenen-bildwahl__inspector", "data-testid": "bildwahl-galerie", children: [
11847
- /* @__PURE__ */ jsxs18("header", { className: "szenen-bildwahl__inspektorkopf", children: [
11848
- /* @__PURE__ */ jsxs18("span", { className: "szenen-bildwahl__werktitel", "data-testid": "bildwahl-werktitel", children: [
12041
+ werk && /* @__PURE__ */ jsxs14("section", { className: "szenen-bildwahl__inspector", "data-testid": "bildwahl-galerie", children: [
12042
+ /* @__PURE__ */ jsxs14("header", { className: "szenen-bildwahl__inspektorkopf", children: [
12043
+ /* @__PURE__ */ jsxs14("span", { className: "szenen-bildwahl__werktitel", "data-testid": "bildwahl-werktitel", children: [
11849
12044
  werk.title,
11850
12045
  " \xB7 ",
11851
12046
  werk.images.length,
11852
12047
  " Bilder"
11853
12048
  ] }),
11854
- /* @__PURE__ */ jsx18(
12049
+ /* @__PURE__ */ jsx14(
11855
12050
  "button",
11856
12051
  {
11857
12052
  type: "button",
@@ -11862,7 +12057,7 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
11862
12057
  }
11863
12058
  )
11864
12059
  ] }),
11865
- /* @__PURE__ */ jsx18("ul", { className: "szenen-bildwahl__streifen", children: werk.images.map((bild, position) => /* @__PURE__ */ jsx18("li", { children: /* @__PURE__ */ jsxs18(
12060
+ /* @__PURE__ */ jsx14("ul", { className: "szenen-bildwahl__streifen", children: werk.images.map((bild, position) => /* @__PURE__ */ jsx14("li", { children: /* @__PURE__ */ jsxs14(
11866
12061
  "button",
11867
12062
  {
11868
12063
  type: "button",
@@ -11872,7 +12067,7 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
11872
12067
  "data-testid": `bild-waehlen-${bild.mediaRef}`,
11873
12068
  title: bild.title,
11874
12069
  children: [
11875
- /* @__PURE__ */ jsx18(
12070
+ /* @__PURE__ */ jsx14(
11876
12071
  Kachel,
11877
12072
  {
11878
12073
  quelle: quelleVon(bild),
@@ -11880,7 +12075,7 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
11880
12075
  ...gescheitert.has(bild.mediaRef) ? { leerText: "Vorschau fehlt" } : {}
11881
12076
  }
11882
12077
  ),
11883
- /* @__PURE__ */ jsxs18("span", { className: "szenen-bildwahl__nummer", children: [
12078
+ /* @__PURE__ */ jsxs14("span", { className: "szenen-bildwahl__nummer", children: [
11884
12079
  "#",
11885
12080
  position + 1
11886
12081
  ] })
@@ -11888,10 +12083,10 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
11888
12083
  }
11889
12084
  ) }, bild.mediaRef)) })
11890
12085
  ] }),
11891
- gewaehlt.length > 0 && /* @__PURE__ */ jsx18("ul", { className: "szenen-bildwahl__gewaehlt", "data-testid": "bilder-gewaehlt", children: gewaehlt.map((bild) => /* @__PURE__ */ jsxs18("li", { children: [
11892
- /* @__PURE__ */ jsx18(Kachel, { quelle: quelleVon(bild), alt: bild.title }),
11893
- /* @__PURE__ */ jsx18("span", { className: "szenen-bildwahl__kacheltitel", children: bild.title }),
11894
- /* @__PURE__ */ jsx18(
12086
+ gewaehlt.length > 0 && /* @__PURE__ */ jsx14("ul", { className: "szenen-bildwahl__gewaehlt", "data-testid": "bilder-gewaehlt", children: gewaehlt.map((bild) => /* @__PURE__ */ jsxs14("li", { children: [
12087
+ /* @__PURE__ */ jsx14(Kachel, { quelle: quelleVon(bild), alt: bild.title }),
12088
+ /* @__PURE__ */ jsx14("span", { className: "szenen-bildwahl__kacheltitel", children: bild.title }),
12089
+ /* @__PURE__ */ jsx14(
11895
12090
  "button",
11896
12091
  {
11897
12092
  type: "button",
@@ -11927,6 +12122,15 @@ var DeriveArea = ({ port, contextId }) => {
11927
12122
  () => port.derivation.listPolicies(contextId),
11928
12123
  [port, contextId]
11929
12124
  );
12125
+ const sortiert = useMemo3(() => regeln.status === "ready" ? [...regeln.value].sort((a, b) => {
12126
+ const szeneZuerst = Number(b.produces.includes("scene")) - Number(a.produces.includes("scene"));
12127
+ return szeneZuerst !== 0 ? szeneZuerst : a.title.localeCompare(b.title, "de");
12128
+ }) : [], [regeln]);
12129
+ useEffect9(() => {
12130
+ if (policyVersionId) return;
12131
+ const fuerSzene = sortiert.find((r) => r.produces.includes("scene"));
12132
+ if (fuerSzene) setPolicyVersionId(fuerSzene.versionId);
12133
+ }, [sortiert, policyVersionId]);
11930
12134
  const gewaehlt = regeln.status === "ready" ? regeln.value.find((r) => r.versionId === policyVersionId) ?? null : null;
11931
12135
  async function starten(folgt) {
11932
12136
  if (!gewaehlt) {
@@ -12001,12 +12205,12 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
12001
12205
  const text = beanstandung ?? vorgabeBeanstandung(satz);
12002
12206
  const bilderFehlen = satz.input.images.some((bild) => bild.kind !== "library");
12003
12207
  const bereit = Boolean(text.trim()) && !bilderFehlen && !laeuft;
12004
- return /* @__PURE__ */ jsxs18("div", { className: "szenen-folgelauf", "data-testid": "folgelauf", children: [
12005
- /* @__PURE__ */ jsx18("h4", { children: "Korrektur- oder Erg\xE4nzungslauf" }),
12006
- /* @__PURE__ */ jsx18("p", { className: "szenen-area__lead", children: satz.status === "failed" ? "Das Modell bekommt seine Antwort und die Beanstandung noch einmal vorgelegt." : "Das Ergebnis steht. Wenn etwas offen blieb, kann das Modell nachbessern." }),
12007
- /* @__PURE__ */ jsxs18("label", { className: "szenen-field", children: [
12008
- /* @__PURE__ */ jsx18("span", { children: "Beanstandung" }),
12009
- /* @__PURE__ */ jsx18(
12208
+ return /* @__PURE__ */ jsxs14("div", { className: "szenen-folgelauf", "data-testid": "folgelauf", children: [
12209
+ /* @__PURE__ */ jsx14("h4", { children: "Korrektur- oder Erg\xE4nzungslauf" }),
12210
+ /* @__PURE__ */ jsx14("p", { className: "szenen-area__lead", children: satz.status === "failed" ? "Das Modell bekommt seine Antwort und die Beanstandung noch einmal vorgelegt." : "Das Ergebnis steht. Wenn etwas offen blieb, kann das Modell nachbessern." }),
12211
+ /* @__PURE__ */ jsxs14("label", { className: "szenen-field", children: [
12212
+ /* @__PURE__ */ jsx14("span", { children: "Beanstandung" }),
12213
+ /* @__PURE__ */ jsx14(
12010
12214
  "textarea",
12011
12215
  {
12012
12216
  rows: 4,
@@ -12016,9 +12220,9 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
12016
12220
  }
12017
12221
  )
12018
12222
  ] }),
12019
- /* @__PURE__ */ jsxs18("label", { className: "szenen-field", children: [
12020
- /* @__PURE__ */ jsx18("span", { children: "Zus\xE4tzliche Anweisung (optional)" }),
12021
- /* @__PURE__ */ jsx18(
12223
+ /* @__PURE__ */ jsxs14("label", { className: "szenen-field", children: [
12224
+ /* @__PURE__ */ jsx14("span", { children: "Zus\xE4tzliche Anweisung (optional)" }),
12225
+ /* @__PURE__ */ jsx14(
12022
12226
  "textarea",
12023
12227
  {
12024
12228
  rows: 2,
@@ -12029,8 +12233,8 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
12029
12233
  }
12030
12234
  )
12031
12235
  ] }),
12032
- bilderFehlen && /* @__PURE__ */ jsx18("p", { className: "szenen-error", "data-testid": "folgelauf-ohne-bild", children: "Die Bilder dieses Laufs wurden nicht im Bestand abgelegt \u2014 er ist \xE4lter als diese M\xF6glichkeit. Ein Folgelauf ohne sie k\xF6nnte ein fehlendes Pflichtfeld nur erfinden. Starten Sie einen neuen Lauf mit dem Referenzbild; dessen Bilder bleiben dann erhalten." }),
12033
- /* @__PURE__ */ jsx18(
12236
+ bilderFehlen && /* @__PURE__ */ jsx14("p", { className: "szenen-error", "data-testid": "folgelauf-ohne-bild", children: "Die Bilder dieses Laufs wurden nicht im Bestand abgelegt \u2014 er ist \xE4lter als diese M\xF6glichkeit. Ein Folgelauf ohne sie k\xF6nnte ein fehlendes Pflichtfeld nur erfinden. Starten Sie einen neuen Lauf mit dem Referenzbild; dessen Bilder bleiben dann erhalten." }),
12237
+ /* @__PURE__ */ jsx14(
12034
12238
  "button",
12035
12239
  {
12036
12240
  type: "button",
@@ -12070,19 +12274,19 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
12070
12274
  setMeldung(ergebnis.message);
12071
12275
  }
12072
12276
  }
12073
- return /* @__PURE__ */ jsxs18("div", { className: "szenen-derive", "data-testid": "bereich-rekonstruktion", children: [
12074
- /* @__PURE__ */ jsx18("p", { className: "szenen-area__lead", children: "Aus einem Referenzbild, einem Prompt oder beidem einen Szenenentwurf vorschlagen lassen. Der Vorschlag wird nie ver\xF6ffentlicht \u2014 er wird ein Entwurf, den Sie pr\xFCfen und \xE4ndern." }),
12075
- regeln.status === "loading" && /* @__PURE__ */ jsx18("p", { children: "Regeln werden geladen \u2026" }),
12076
- regeln.status === "error" && /* @__PURE__ */ jsxs18("p", { className: "szenen-error", role: "alert", children: [
12277
+ return /* @__PURE__ */ jsxs14("div", { className: "szenen-derive", "data-testid": "bereich-rekonstruktion", children: [
12278
+ /* @__PURE__ */ jsx14("p", { className: "szenen-area__lead", children: "Aus einem Referenzbild, einem Prompt oder beidem einen Szenenentwurf vorschlagen lassen. Der Vorschlag wird nie ver\xF6ffentlicht \u2014 er wird ein Entwurf, den Sie pr\xFCfen und \xE4ndern." }),
12279
+ regeln.status === "loading" && /* @__PURE__ */ jsx14("p", { children: "Regeln werden geladen \u2026" }),
12280
+ regeln.status === "error" && /* @__PURE__ */ jsxs14("p", { className: "szenen-error", role: "alert", children: [
12077
12281
  "Die Regeln konnten nicht gelesen werden: ",
12078
12282
  regeln.message
12079
12283
  ] }),
12080
- regeln.status === "ready" && regeln.value.length === 0 && /* @__PURE__ */ jsxs18("p", { className: "szenen-empty", children: [
12284
+ regeln.status === "ready" && regeln.value.length === 0 && /* @__PURE__ */ jsxs14("p", { className: "szenen-empty", children: [
12081
12285
  "In diesem Kontext ist keine Rekonstruktionsregel (",
12082
- /* @__PURE__ */ jsx18("code", { children: "scene_derivation_policy" }),
12083
- ') ver\xF6ffentlicht. Ohne sie ist kein Lauf reproduzierbar \u2014 legen Sie hier eine unter \u201E1 \xB7 Bausteine" an, oder wechseln Sie in den Kontext, in dem Ihre Regel liegt. Regeln aus anderen Kontexten stehen hier bewusst nicht zur Auswahl.'
12286
+ /* @__PURE__ */ jsx14("code", { children: "scene_derivation_policy" }),
12287
+ ') ver\xF6ffentlicht. Ohne sie ist kein Lauf reproduzierbar \u2014 legen Sie eine unter \u201EErweitert" an, oder wechseln Sie in den Kontext, in dem Ihre Regel liegt. Regeln aus anderen Kontexten stehen hier bewusst nicht zur Auswahl.'
12084
12288
  ] }),
12085
- regeln.status === "ready" && regeln.value.length > 0 && /* @__PURE__ */ jsxs18(
12289
+ regeln.status === "ready" && regeln.value.length > 0 && /* @__PURE__ */ jsxs14(
12086
12290
  "form",
12087
12291
  {
12088
12292
  className: "szenen-form",
@@ -12091,17 +12295,17 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
12091
12295
  void starten();
12092
12296
  },
12093
12297
  children: [
12094
- /* @__PURE__ */ jsxs18("label", { className: "szenen-field", children: [
12095
- /* @__PURE__ */ jsx18("span", { children: "Regel" }),
12096
- /* @__PURE__ */ jsxs18(
12298
+ /* @__PURE__ */ jsxs14("label", { className: "szenen-field", children: [
12299
+ /* @__PURE__ */ jsx14("span", { children: "Regel" }),
12300
+ /* @__PURE__ */ jsxs14(
12097
12301
  "select",
12098
12302
  {
12099
12303
  value: policyVersionId,
12100
12304
  onChange: (e) => setPolicyVersionId(e.target.value),
12101
12305
  "data-testid": "regel-auswahl",
12102
12306
  children: [
12103
- /* @__PURE__ */ jsx18("option", { value: "", children: "\u2014 bitte w\xE4hlen \u2014" }),
12104
- regeln.value.map((regel) => /* @__PURE__ */ jsxs18("option", { value: regel.versionId, children: [
12307
+ /* @__PURE__ */ jsx14("option", { value: "", children: "\u2014 bitte w\xE4hlen \u2014" }),
12308
+ sortiert.map((regel) => /* @__PURE__ */ jsxs14("option", { value: regel.versionId, children: [
12105
12309
  regel.title,
12106
12310
  " (",
12107
12311
  regel.produces.join(", "),
@@ -12111,21 +12315,21 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
12111
12315
  }
12112
12316
  )
12113
12317
  ] }),
12114
- /* @__PURE__ */ jsxs18("label", { className: "szenen-field", children: [
12115
- /* @__PURE__ */ jsx18("span", { children: "Eingang" }),
12116
- /* @__PURE__ */ jsx18(
12318
+ /* @__PURE__ */ jsxs14("label", { className: "szenen-field", children: [
12319
+ /* @__PURE__ */ jsx14("span", { children: "Eingang" }),
12320
+ /* @__PURE__ */ jsx14(
12117
12321
  "select",
12118
12322
  {
12119
12323
  value: modus,
12120
12324
  onChange: (e) => setModus(e.target.value),
12121
12325
  "data-testid": "modus-auswahl",
12122
- children: (gewaehlt ? gewaehlt.inputModes : ["prompt", "image", "image_prompt"]).map((m) => /* @__PURE__ */ jsx18("option", { value: m, children: MODUS_TEXT[m] }, m))
12326
+ children: (gewaehlt ? gewaehlt.inputModes : ["prompt", "image", "image_prompt"]).map((m) => /* @__PURE__ */ jsx14("option", { value: m, children: MODUS_TEXT[m] }, m))
12123
12327
  }
12124
12328
  )
12125
12329
  ] }),
12126
- modus !== "image" && /* @__PURE__ */ jsxs18("label", { className: "szenen-field", children: [
12127
- /* @__PURE__ */ jsx18("span", { children: "Referenzprompt" }),
12128
- /* @__PURE__ */ jsx18(
12330
+ modus !== "image" && /* @__PURE__ */ jsxs14("label", { className: "szenen-field", children: [
12331
+ /* @__PURE__ */ jsx14("span", { children: "Referenzprompt" }),
12332
+ /* @__PURE__ */ jsx14(
12129
12333
  "textarea",
12130
12334
  {
12131
12335
  value: promptText,
@@ -12135,8 +12339,8 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
12135
12339
  }
12136
12340
  )
12137
12341
  ] }),
12138
- modus !== "prompt" && /* @__PURE__ */ jsxs18(Fragment8, { children: [
12139
- port.imageLibrary && /* @__PURE__ */ jsx18(
12342
+ modus !== "prompt" && /* @__PURE__ */ jsxs14(Fragment8, { children: [
12343
+ port.imageLibrary && /* @__PURE__ */ jsx14(
12140
12344
  BildWaehler,
12141
12345
  {
12142
12346
  port,
@@ -12145,9 +12349,9 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
12145
12349
  onEntfernen: (ref) => setBestandsbilder((v) => v.filter((b) => b.mediaRef !== ref))
12146
12350
  }
12147
12351
  ),
12148
- /* @__PURE__ */ jsxs18("label", { className: "szenen-field", children: [
12149
- /* @__PURE__ */ jsx18("span", { children: port.imageLibrary ? "Oder hochladen" : "Referenzbild" }),
12150
- /* @__PURE__ */ jsx18(
12352
+ /* @__PURE__ */ jsxs14("label", { className: "szenen-field", children: [
12353
+ /* @__PURE__ */ jsx14("span", { children: port.imageLibrary ? "Oder hochladen" : "Referenzbild" }),
12354
+ /* @__PURE__ */ jsx14(
12151
12355
  "input",
12152
12356
  {
12153
12357
  type: "file",
@@ -12157,40 +12361,40 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
12157
12361
  "data-testid": "bild-eingabe"
12158
12362
  }
12159
12363
  ),
12160
- port.imageLibrary && /* @__PURE__ */ jsx18("small", { "data-testid": "upload-hinweis", children: "Hochgeladene Bilder werden im Bestand abgelegt \u2014 nur so ist sp\xE4ter ein Folgelauf damit m\xF6glich." })
12364
+ port.imageLibrary && /* @__PURE__ */ jsx14("small", { "data-testid": "upload-hinweis", children: "Hochgeladene Bilder werden im Bestand abgelegt \u2014 nur so ist sp\xE4ter ein Folgelauf damit m\xF6glich." })
12161
12365
  ] })
12162
12366
  ] }),
12163
- /* @__PURE__ */ jsx18("button", { type: "submit", disabled: !gewaehlt || laeuft, "data-testid": "lauf-starten", children: laeuft ? "Rekonstruiert \u2026" : "Rekonstruieren" })
12367
+ /* @__PURE__ */ jsx14("button", { type: "submit", disabled: !gewaehlt || laeuft, "data-testid": "lauf-starten", children: laeuft ? "Rekonstruiert \u2026" : "Rekonstruieren" })
12164
12368
  ]
12165
12369
  }
12166
12370
  ),
12167
- meldung && /* @__PURE__ */ jsx18("p", { className: "szenen-error", role: "alert", "data-testid": "derive-meldung", children: meldung }),
12168
- lauf && lauf.status === "failed" && /* @__PURE__ */ jsxs18("section", { className: "szenen-run szenen-run--failed", "data-testid": "lauf-fehler", children: [
12169
- /* @__PURE__ */ jsx18("h3", { children: "Der Lauf ist gescheitert" }),
12170
- /* @__PURE__ */ jsxs18("p", { children: [
12171
- /* @__PURE__ */ jsx18("strong", { children: lauf.failure?.class }),
12371
+ meldung && /* @__PURE__ */ jsx14("p", { className: "szenen-error", role: "alert", "data-testid": "derive-meldung", children: meldung }),
12372
+ lauf && lauf.status === "failed" && /* @__PURE__ */ jsxs14("section", { className: "szenen-run szenen-run--failed", "data-testid": "lauf-fehler", children: [
12373
+ /* @__PURE__ */ jsx14("h3", { children: "Der Lauf ist gescheitert" }),
12374
+ /* @__PURE__ */ jsxs14("p", { children: [
12375
+ /* @__PURE__ */ jsx14("strong", { children: lauf.failure?.class }),
12172
12376
  " \u2014 ",
12173
12377
  lauf.failure?.message
12174
12378
  ] }),
12175
- lauf.failure?.diagnostics && lauf.failure.diagnostics.length > 0 && /* @__PURE__ */ jsxs18("div", { className: "szenen-diagnose", "data-testid": "lauf-diagnosen", children: [
12176
- /* @__PURE__ */ jsx18("h4", { children: "Befunde" }),
12177
- /* @__PURE__ */ jsx18(Diagnosen, { befunde: lauf.failure.diagnostics, testId: "lauf-befunde" })
12379
+ lauf.failure?.diagnostics && lauf.failure.diagnostics.length > 0 && /* @__PURE__ */ jsxs14("div", { className: "szenen-diagnose", "data-testid": "lauf-diagnosen", children: [
12380
+ /* @__PURE__ */ jsx14("h4", { children: "Befunde" }),
12381
+ /* @__PURE__ */ jsx14(Diagnosen, { befunde: lauf.failure.diagnostics, testId: "lauf-befunde" })
12178
12382
  ] }),
12179
- /* @__PURE__ */ jsx18(Herkunft, { lauf }),
12180
- /* @__PURE__ */ jsx18(Widerspruch, { lauf }),
12181
- /* @__PURE__ */ jsx18(Belege, { lauf }),
12182
- /* @__PURE__ */ jsx18(RecoveryPanel, { port, lauf }),
12383
+ /* @__PURE__ */ jsx14(Herkunft, { lauf }),
12384
+ /* @__PURE__ */ jsx14(Widerspruch, { lauf }),
12385
+ /* @__PURE__ */ jsx14(Belege, { lauf }),
12386
+ /* @__PURE__ */ jsx14(RecoveryPanel, { port, lauf }),
12183
12387
  folgeblock(lauf)
12184
12388
  ] }),
12185
- lauf && lauf.status === "succeeded" && /* @__PURE__ */ jsxs18("section", { className: "szenen-run", "data-testid": "lauf-ergebnis", children: [
12186
- /* @__PURE__ */ jsx18("h3", { children: "Vorschlag" }),
12187
- /* @__PURE__ */ jsx18(Herkunft, { lauf }),
12188
- /* @__PURE__ */ jsx18(Widerspruch, { lauf }),
12189
- /* @__PURE__ */ jsx18("p", { className: "szenen-run__feedback", children: lauf.feedback }),
12190
- lauf.conflicts.length > 0 && /* @__PURE__ */ jsxs18("div", { className: "szenen-diagnose szenen-diagnose--konflikt", "data-testid": "konflikte", children: [
12191
- /* @__PURE__ */ jsx18("h4", { children: "Widerspr\xFCche zwischen Bild und Prompt" }),
12192
- /* @__PURE__ */ jsx18("ul", { children: lauf.conflicts.map((k, i) => /* @__PURE__ */ jsxs18("li", { children: [
12193
- /* @__PURE__ */ jsx18("code", { children: k.field }),
12389
+ lauf && lauf.status === "succeeded" && /* @__PURE__ */ jsxs14("section", { className: "szenen-run", "data-testid": "lauf-ergebnis", children: [
12390
+ /* @__PURE__ */ jsx14("h3", { children: "Vorschlag" }),
12391
+ /* @__PURE__ */ jsx14(Herkunft, { lauf }),
12392
+ /* @__PURE__ */ jsx14(Widerspruch, { lauf }),
12393
+ /* @__PURE__ */ jsx14("p", { className: "szenen-run__feedback", children: lauf.feedback }),
12394
+ lauf.conflicts.length > 0 && /* @__PURE__ */ jsxs14("div", { className: "szenen-diagnose szenen-diagnose--konflikt", "data-testid": "konflikte", children: [
12395
+ /* @__PURE__ */ jsx14("h4", { children: "Widerspr\xFCche zwischen Bild und Prompt" }),
12396
+ /* @__PURE__ */ jsx14("ul", { children: lauf.conflicts.map((k, i) => /* @__PURE__ */ jsxs14("li", { children: [
12397
+ /* @__PURE__ */ jsx14("code", { children: k.field }),
12194
12398
  ": Bild sagt \u201E",
12195
12399
  k.from_image,
12196
12400
  '", Prompt sagt \u201E',
@@ -12198,31 +12402,31 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
12198
12402
  '". Nichts davon wurde \xFCbernommen.'
12199
12403
  ] }, i)) })
12200
12404
  ] }),
12201
- lauf.not_derivable.length > 0 && /* @__PURE__ */ jsxs18("div", { className: "szenen-diagnose", "data-testid": "nicht-ableitbar", children: [
12202
- /* @__PURE__ */ jsx18("h4", { children: "Nicht ableitbar" }),
12203
- /* @__PURE__ */ jsx18("ul", { children: lauf.not_derivable.map((n, i) => /* @__PURE__ */ jsxs18("li", { children: [
12204
- /* @__PURE__ */ jsx18("code", { children: n.field }),
12405
+ lauf.not_derivable.length > 0 && /* @__PURE__ */ jsxs14("div", { className: "szenen-diagnose", "data-testid": "nicht-ableitbar", children: [
12406
+ /* @__PURE__ */ jsx14("h4", { children: "Nicht ableitbar" }),
12407
+ /* @__PURE__ */ jsx14("ul", { children: lauf.not_derivable.map((n, i) => /* @__PURE__ */ jsxs14("li", { children: [
12408
+ /* @__PURE__ */ jsx14("code", { children: n.field }),
12205
12409
  ": ",
12206
12410
  n.reason
12207
12411
  ] }, i)) })
12208
12412
  ] }),
12209
- lauf.assumptions.length > 0 && /* @__PURE__ */ jsxs18("div", { className: "szenen-diagnose", "data-testid": "annahmen", children: [
12210
- /* @__PURE__ */ jsx18("h4", { children: "Erschlossen, nicht belegt" }),
12211
- /* @__PURE__ */ jsx18("ul", { children: lauf.assumptions.map((a, i) => /* @__PURE__ */ jsx18("li", { children: a }, i)) })
12413
+ lauf.assumptions.length > 0 && /* @__PURE__ */ jsxs14("div", { className: "szenen-diagnose", "data-testid": "annahmen", children: [
12414
+ /* @__PURE__ */ jsx14("h4", { children: "Erschlossen, nicht belegt" }),
12415
+ /* @__PURE__ */ jsx14("ul", { children: lauf.assumptions.map((a, i) => /* @__PURE__ */ jsx14("li", { children: a }, i)) })
12212
12416
  ] }),
12213
12417
  lauf.proposals.map((vorschlag) => {
12214
12418
  const bezug = REFERENCE_FIELD[vorschlag.type];
12215
12419
  const fertig = uebernommen[vorschlag.type];
12216
- return /* @__PURE__ */ jsxs18(
12420
+ return /* @__PURE__ */ jsxs14(
12217
12421
  "article",
12218
12422
  {
12219
12423
  className: "szenen-proposal",
12220
12424
  "data-testid": `vorschlag-${vorschlag.type}`,
12221
12425
  children: [
12222
- /* @__PURE__ */ jsx18("h4", { children: vorschlag.type }),
12223
- !fertig && /* @__PURE__ */ jsxs18("label", { className: "szenen-field", children: [
12224
- /* @__PURE__ */ jsx18("span", { children: "Titel" }),
12225
- /* @__PURE__ */ jsx18(
12426
+ /* @__PURE__ */ jsx14("h4", { children: vorschlag.type }),
12427
+ !fertig && /* @__PURE__ */ jsxs14("label", { className: "szenen-field", children: [
12428
+ /* @__PURE__ */ jsx14("span", { children: "Titel" }),
12429
+ /* @__PURE__ */ jsx14(
12226
12430
  "input",
12227
12431
  {
12228
12432
  type: "text",
@@ -12236,9 +12440,9 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
12236
12440
  }
12237
12441
  )
12238
12442
  ] }),
12239
- /* @__PURE__ */ jsx18("pre", { className: "szenen-proposal__payload", children: JSON.stringify(vorschlag.payload, null, 2) }),
12240
- bezug && !fertig && /* @__PURE__ */ jsxs18(Fragment8, { children: [
12241
- /* @__PURE__ */ jsx18(
12443
+ /* @__PURE__ */ jsx14("pre", { className: "szenen-proposal__payload", children: JSON.stringify(vorschlag.payload, null, 2) }),
12444
+ bezug && !fertig && /* @__PURE__ */ jsxs14(Fragment8, { children: [
12445
+ /* @__PURE__ */ jsx14(
12242
12446
  ReferencePicker,
12243
12447
  {
12244
12448
  port,
@@ -12252,30 +12456,30 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
12252
12456
  }))
12253
12457
  }
12254
12458
  ),
12255
- referenzen[vorschlag.type] ? /* @__PURE__ */ jsxs18("p", { "data-testid": `referenz-gewaehlt-${vorschlag.type}`, children: [
12459
+ referenzen[vorschlag.type] ? /* @__PURE__ */ jsxs14("p", { "data-testid": `referenz-gewaehlt-${vorschlag.type}`, children: [
12256
12460
  "Zeigt auf Version ",
12257
- /* @__PURE__ */ jsx18("code", { children: referenzen[vorschlag.type] }),
12461
+ /* @__PURE__ */ jsx14("code", { children: referenzen[vorschlag.type] }),
12258
12462
  "."
12259
12463
  ] }) : (
12260
12464
  /* Der Grund steht **vor** der Handlung. Den Nutzer erst
12261
12465
  auf einen Knopf drücken zu lassen, um ihm dann eine
12262
12466
  Ablehnung zu zeigen, ist keine Auskunft. */
12263
- /* @__PURE__ */ jsxs18("p", { "data-testid": `referenz-fehlt-${vorschlag.type}`, children: [
12467
+ /* @__PURE__ */ jsxs14("p", { "data-testid": `referenz-fehlt-${vorschlag.type}`, children: [
12264
12468
  "Diese ",
12265
- /* @__PURE__ */ jsx18("strong", { children: "Einzel\xFCbernahme" }),
12469
+ /* @__PURE__ */ jsx14("strong", { children: "Einzel\xFCbernahme" }),
12266
12470
  " braucht eine ver\xF6ffentlichte ",
12267
12471
  bezug.refersTo,
12268
12472
  "-Version, auf die der Entwurf zeigt (D-03) \u2014 sie ist der Weg in einen bestehenden, l\xE4ngst ver\xF6ffentlichten Zusammenhang. Wollen Sie aus diesem Lauf erst einmal einen Prompt ausprobieren, nehmen Sie unten die ",
12269
- /* @__PURE__ */ jsx18("strong", { children: "Draft-Kette" }),
12473
+ /* @__PURE__ */ jsx14("strong", { children: "Draft-Kette" }),
12270
12474
  ": Dort entstehen Szene, Aufbau und Shot zusammenh\xE4ngend, und es muss nichts ver\xF6ffentlicht werden."
12271
12475
  ] })
12272
12476
  )
12273
12477
  ] }),
12274
- fertig ? /* @__PURE__ */ jsxs18("p", { className: "szenen-proposal__done", children: [
12478
+ fertig ? /* @__PURE__ */ jsxs14("p", { className: "szenen-proposal__done", children: [
12275
12479
  "Als Entwurf \xFCbernommen: ",
12276
- /* @__PURE__ */ jsx18("code", { children: fertig }),
12480
+ /* @__PURE__ */ jsx14("code", { children: fertig }),
12277
12481
  ". Er ist noch nicht ver\xF6ffentlicht."
12278
- ] }) : /* @__PURE__ */ jsx18(
12482
+ ] }) : /* @__PURE__ */ jsx14(
12279
12483
  "button",
12280
12484
  {
12281
12485
  type: "button",
@@ -12290,13 +12494,337 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
12290
12494
  vorschlag.type
12291
12495
  );
12292
12496
  }),
12293
- /* @__PURE__ */ jsx18(ChainActions, { port, runId: lauf.run_id }),
12294
- /* @__PURE__ */ jsx18(Belege, { lauf }),
12497
+ /* @__PURE__ */ jsx14(ChainActions, { port, runId: lauf.run_id }),
12498
+ /* @__PURE__ */ jsx14(Belege, { lauf }),
12295
12499
  folgeblock(lauf)
12296
12500
  ] })
12297
12501
  ] });
12298
12502
  };
12299
12503
 
12504
+ // src/features/szenen/areas/SceneArea.tsx
12505
+ import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
12506
+ var SceneArea = ({ port, contextId }) => {
12507
+ const kannAbleiten = capabilitiesOf(port).derivation;
12508
+ const [ableiten, setAbleiten] = useState17(false);
12509
+ const zeigeDerive = kannAbleiten && ableiten;
12510
+ return /* @__PURE__ */ jsxs15("div", { className: "szenen-scene", "data-testid": "bereich-szene", children: [
12511
+ /* @__PURE__ */ jsxs15("div", { className: "szenen-elements__toolbar", children: [
12512
+ /* @__PURE__ */ jsx15("p", { className: "szenen-area__lead", children: "Szenen dieses Kontexts. Auswahl \xF6ffnet die Bearbeitung der Nodes und Beziehungen und startet Prompt-L\xE4ufe." }),
12513
+ kannAbleiten && /* @__PURE__ */ jsx15(
12514
+ "button",
12515
+ {
12516
+ type: "button",
12517
+ className: "szenen-elements__new",
12518
+ "data-testid": "neu-aus-bild-text",
12519
+ "aria-expanded": ableiten,
12520
+ onClick: () => setAbleiten((offen) => !offen),
12521
+ children: ableiten ? "Zur\xFCck zu den Szenen" : "+ Neu aus Bild/Text"
12522
+ }
12523
+ )
12524
+ ] }),
12525
+ !kannAbleiten && /* @__PURE__ */ jsx15("p", { className: "szenen-view__capability-note", "data-testid": "hinweis-keine-rekonstruktion", children: "Aus Bild oder Text l\xE4sst sich in diesem Host keine Szene ableiten." }),
12526
+ zeigeDerive ? /* @__PURE__ */ jsx15(DeriveArea, { port, contextId }) : /* @__PURE__ */ jsx15(ElementsArea, { port, contextId, restrictToTypes: ["scene"] })
12527
+ ] });
12528
+ };
12529
+
12530
+ // src/features/szenen/areas/AdvancedArea.tsx
12531
+ import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
12532
+ var AdvancedArea = ({ port, contextId }) => /* @__PURE__ */ jsxs16("div", { className: "szenen-erweitert", "data-testid": "bereich-erweitert", children: [
12533
+ /* @__PURE__ */ jsx16("p", { className: "szenen-area__lead", children: "Optionale Pr\xE4zisierung: Bildaufbau und Shot, Kameras und Stile der Aufnahme, Zielmodelle, Rekonstruktionsregeln und Sequenzen. F\xFCr den normalen Weg von der Szene zum Bild wird hier nichts gebraucht." }),
12534
+ /* @__PURE__ */ jsx16(
12535
+ ElementsArea,
12536
+ {
12537
+ port,
12538
+ contextId,
12539
+ restrictToTypes: ERWEITERT_TYPES,
12540
+ allTypesLabel: "alle erweiterten Typen",
12541
+ listTitle: "Erweitert"
12542
+ }
12543
+ )
12544
+ ] });
12545
+
12546
+ // src/features/szenen/areas/PromptsArea.tsx
12547
+ import { useMemo as useMemo4, useState as useState18 } from "react";
12548
+ import { Fragment as Fragment9, jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
12549
+ var HERKUNFT_TEXT2 = {
12550
+ published: "aus ver\xF6ffentlichtem Shot",
12551
+ draft_chain: "aus Draft-Kette",
12552
+ scene_direct: "direkt aus Szene",
12553
+ unresolved: "Herkunft unvollst\xE4ndig"
12554
+ };
12555
+ var PromptsArea = ({ port, contextId }) => {
12556
+ const faehig = capabilitiesOf(port);
12557
+ const [nachladen, setNachladen] = useState18(0);
12558
+ const [offen, setOffen] = useState18(null);
12559
+ const [alleKontexte, setAlleKontexte] = useState18(false);
12560
+ const [mitFehllaeufen, setMitFehllaeufen] = useState18(false);
12561
+ const [herkunft, setHerkunft] = useState18("alle");
12562
+ const [phase] = usePortQuery(
12563
+ () => port.runs.listAllRuns ? port.runs.listAllRuns({ status: "all" }) : Promise.resolve({
12564
+ kind: "ok",
12565
+ value: { runs: [], hiddenByStatus: 0, hiddenByContext: 0, withoutContext: 0 }
12566
+ }),
12567
+ [port, nachladen]
12568
+ );
12569
+ const alle = phase.status === "ready" ? phase.value.runs : [];
12570
+ const sicht = useMemo4(() => selectPromptRuns(alle, {
12571
+ status: mitFehllaeufen ? "all" : "succeeded",
12572
+ ...alleKontexte ? {} : { contextId }
12573
+ }), [alle, mitFehllaeufen, alleKontexte, contextId]);
12574
+ const titel = useLaufTitel(port, alle);
12575
+ const gezeigt = useMemo4(
12576
+ () => herkunft === "alle" ? sicht.runs : sicht.runs.filter((lauf) => groupOf(lauf).kind === herkunft),
12577
+ [sicht.runs, herkunft]
12578
+ );
12579
+ if (!faehig.promptOverview) {
12580
+ return /* @__PURE__ */ jsx17("div", { className: "szenen-prompts", "data-testid": "prompts-bereich", children: /* @__PURE__ */ jsx17("p", { "data-testid": "hinweis-keine-promptsicht", children: "Dieser Host bietet keine herkunfts\xFCbergreifende Prompt-Liste an. Prompt-L\xE4ufe bleiben im Detail ihrer Szene bzw. ihres Shots erreichbar \u2014 es ist kein Fehler und kein vor\xFCbergehender Zustand." }) });
12581
+ }
12582
+ return /* @__PURE__ */ jsxs17("div", { className: "szenen-prompts", "data-testid": "prompts-bereich", children: [
12583
+ /* @__PURE__ */ jsxs17("div", { className: "szenen-prompts__toolbar", children: [
12584
+ /* @__PURE__ */ jsx17("h3", { className: "szenen-prompts__heading", children: "Prompts" }),
12585
+ /* @__PURE__ */ jsxs17("label", { className: "szenen-prompts__filter", children: [
12586
+ "Herkunft",
12587
+ /* @__PURE__ */ jsxs17(
12588
+ "select",
12589
+ {
12590
+ "data-testid": "prompts-herkunft",
12591
+ value: herkunft,
12592
+ onChange: (e) => setHerkunft(e.target.value),
12593
+ children: [
12594
+ /* @__PURE__ */ jsx17("option", { value: "alle", children: "alle" }),
12595
+ /* @__PURE__ */ jsx17("option", { value: "published", children: HERKUNFT_TEXT2.published }),
12596
+ /* @__PURE__ */ jsx17("option", { value: "draft_chain", children: HERKUNFT_TEXT2.draft_chain }),
12597
+ /* @__PURE__ */ jsx17("option", { value: "scene_direct", children: HERKUNFT_TEXT2.scene_direct }),
12598
+ /* @__PURE__ */ jsx17("option", { value: "unresolved", children: HERKUNFT_TEXT2.unresolved })
12599
+ ]
12600
+ }
12601
+ )
12602
+ ] }),
12603
+ /* @__PURE__ */ jsxs17("label", { className: "szenen-prompts__filter", children: [
12604
+ /* @__PURE__ */ jsx17(
12605
+ "input",
12606
+ {
12607
+ type: "checkbox",
12608
+ "data-testid": "prompts-alle-kontexte",
12609
+ checked: alleKontexte,
12610
+ onChange: (e) => setAlleKontexte(e.target.checked)
12611
+ }
12612
+ ),
12613
+ "Alle Kontexte"
12614
+ ] }),
12615
+ /* @__PURE__ */ jsxs17("label", { className: "szenen-prompts__filter", children: [
12616
+ /* @__PURE__ */ jsx17(
12617
+ "input",
12618
+ {
12619
+ type: "checkbox",
12620
+ "data-testid": "prompts-mit-fehllaeufen",
12621
+ checked: mitFehllaeufen,
12622
+ onChange: (e) => setMitFehllaeufen(e.target.checked)
12623
+ }
12624
+ ),
12625
+ "Fehll\xE4ufe zeigen"
12626
+ ] })
12627
+ ] }),
12628
+ phase.status === "loading" && /* @__PURE__ */ jsx17("p", { "data-testid": "prompts-laden", children: "Prompts werden geladen \u2026" }),
12629
+ phase.status === "error" && /* @__PURE__ */ jsxs17("p", { "data-testid": "prompts-fehler", role: "alert", children: [
12630
+ "Prompts konnten nicht geladen werden: ",
12631
+ phase.message
12632
+ ] }),
12633
+ phase.status === "ready" && /* @__PURE__ */ jsxs17(Fragment9, { children: [
12634
+ gezeigt.length === 0 && /* @__PURE__ */ jsx17("p", { "data-testid": "keine-prompts", children: alle.length === 0 ? "Es gibt noch keinen Prompt-Lauf." : `Kein Prompt passt zu dieser Auswahl \u2014 von ${alle.length} vorhandenen L\xE4ufen bleibt keiner \xFCbrig.` }),
12635
+ /* @__PURE__ */ jsx17("ol", { "data-testid": "prompts-liste", children: gezeigt.map((lauf) => /* @__PURE__ */ jsx17(
12636
+ PromptEintrag,
12637
+ {
12638
+ port,
12639
+ lauf,
12640
+ alle,
12641
+ titel: titel.get(lauf.run_id) ?? null,
12642
+ offen: lauf.run_id === offen,
12643
+ onUmschalten: () => setOffen(
12644
+ lauf.run_id === offen ? null : lauf.run_id
12645
+ ),
12646
+ itemMoeglich: faehig.saveRunAsItem,
12647
+ onItemGespeichert: () => setNachladen((n) => n + 1)
12648
+ },
12649
+ lauf.run_id
12650
+ )) }),
12651
+ (sicht.hiddenByContext > 0 || sicht.withoutContext > 0 || sicht.hiddenByStatus > 0) && /* @__PURE__ */ jsxs17("p", { className: "szenen-prompts__ausgeblendet", "data-testid": "prompts-ausgeblendet", children: [
12652
+ sicht.hiddenByStatus > 0 && `${sicht.hiddenByStatus} Fehllauf/-l\xE4ufe ausgeblendet. `,
12653
+ sicht.hiddenByContext > 0 && `${sicht.hiddenByContext} Lauf/L\xE4ufe geh\xF6ren zu einem anderen Kontext. `,
12654
+ sicht.withoutContext > 0 && `${sicht.withoutContext} Lauf/L\xE4ufe nennen keinen Kontext und werden keinem zugeordnet \u2014 \u201EAlle Kontexte" zeigt sie. `
12655
+ ] })
12656
+ ] })
12657
+ ] });
12658
+ };
12659
+ var PromptEintrag = ({ port, lauf, alle, titel, offen, onUmschalten, itemMoeglich, onItemGespeichert }) => {
12660
+ const gruppe = groupOf(lauf);
12661
+ const runde = roundOfRun(lauf, siblingsOf(lauf, alle));
12662
+ return /* @__PURE__ */ jsxs17("li", { className: "szenen-prompts__eintrag", "data-testid": `prompt-${lauf.run_id}`, children: [
12663
+ /* @__PURE__ */ jsxs17("p", { className: "szenen-prompts__herkunft", "data-testid": `herkunft-${lauf.run_id}`, children: [
12664
+ /* @__PURE__ */ jsx17("span", { "data-herkunft": gruppe.kind, children: HERKUNFT_TEXT2[gruppe.kind] }),
12665
+ gruppe.kind === "unresolved" && // D-75: Was nicht belegbar ist, wird benannt und nicht
12666
+ // durch eine plausible Kennung ersetzt.
12667
+ /* @__PURE__ */ jsxs17("span", { className: "szenen-prompts__hinweis", children: [
12668
+ " \u2014 ",
12669
+ gruppe.reason
12670
+ ] })
12671
+ ] }),
12672
+ /* @__PURE__ */ jsx17("ol", { className: "szenen-prompts__lauf", children: /* @__PURE__ */ jsx17(
12673
+ RundenEintrag,
12674
+ {
12675
+ port,
12676
+ lauf,
12677
+ runde,
12678
+ titel,
12679
+ offen,
12680
+ onUmschalten,
12681
+ itemMoeglich,
12682
+ onItemGespeichert
12683
+ }
12684
+ ) })
12685
+ ] });
12686
+ };
12687
+
12688
+ // src/features/szenen/areas/MediaArea.tsx
12689
+ import { useState as useState19 } from "react";
12690
+ import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
12691
+ var leer = async () => ({ kind: "ok", value: [] });
12692
+ var MediaArea = ({ port }) => {
12693
+ const media = port.media;
12694
+ const [versionInput, setVersionInput] = useState19("");
12695
+ const [versionId, setVersionId] = useState19("");
12696
+ const [itemInput, setItemInput] = useState19("");
12697
+ const [itemId, setItemId] = useState19("");
12698
+ const [assetId, setAssetId] = useState19("");
12699
+ const [role, setRole] = useState19("reference");
12700
+ const [position, setPosition] = useState19("0");
12701
+ const [notiz, setNotiz] = useState19(null);
12702
+ const [busy, setBusy] = useState19(false);
12703
+ const [refresh, setRefresh] = useState19(0);
12704
+ const [versionPhase] = usePortQuery(
12705
+ () => versionId ? media.listVersionMedia(versionId) : leer(),
12706
+ [media, versionId, refresh]
12707
+ );
12708
+ const [itemPhase] = usePortQuery(
12709
+ () => itemId ? media.listItemMedia(itemId) : Promise.resolve({ kind: "ok", value: [] }),
12710
+ [media, itemId, refresh]
12711
+ );
12712
+ const zuordnen = async () => {
12713
+ if (!versionInput.trim() || !assetId.trim()) {
12714
+ setNotiz({ tone: "error", text: "Version-ID und Asset-ID sind erforderlich." });
12715
+ return;
12716
+ }
12717
+ const parsedPosition = Number(position);
12718
+ if (!Number.isInteger(parsedPosition) || parsedPosition < 0) {
12719
+ setNotiz({ tone: "error", text: `Position muss eine nicht-negative Ganzzahl sein, war ${JSON.stringify(position)}.` });
12720
+ return;
12721
+ }
12722
+ setBusy(true);
12723
+ const result = await media.attachVersionMedium({
12724
+ versionId: versionInput.trim(),
12725
+ assetId: assetId.trim(),
12726
+ role,
12727
+ position: parsedPosition
12728
+ });
12729
+ setNotiz(noticeFor(result, "Referenzmedium zuordnen"));
12730
+ if (result.kind === "ok") {
12731
+ setVersionId(versionInput.trim());
12732
+ setRefresh((value) => value + 1);
12733
+ }
12734
+ setBusy(false);
12735
+ };
12736
+ const entfernen = async (medium) => {
12737
+ setBusy(true);
12738
+ const result = await media.detachVersionMedium({
12739
+ versionId,
12740
+ assetId: medium.assetId,
12741
+ role: medium.role
12742
+ });
12743
+ setNotiz(noticeFor(result, "Referenzmedium entfernen"));
12744
+ if (result.kind === "ok") setRefresh((value) => value + 1);
12745
+ setBusy(false);
12746
+ };
12747
+ const versionMedien = versionPhase.status === "ready" ? versionPhase.value : [];
12748
+ const itemMedien = itemPhase.status === "ready" ? itemPhase.value : [];
12749
+ return /* @__PURE__ */ jsxs18("div", { className: "szenen-media", "data-testid": "bereich-medien", children: [
12750
+ /* @__PURE__ */ jsx18("p", { className: "szenen-area__lead", children: "Bereits vorhandene Medien-Assets einer Elementversion zuordnen. Hochgeladen wird hier nichts \u2014 Medien entstehen \xFCber den bestehenden Speicherweg aus Stufe 5." }),
12751
+ /* @__PURE__ */ jsxs18("section", { children: [
12752
+ /* @__PURE__ */ jsx18("h3", { children: "Version" }),
12753
+ /* @__PURE__ */ jsxs18("div", { className: "szenen-media__lookup", children: [
12754
+ /* @__PURE__ */ jsxs18("label", { children: [
12755
+ "Version-ID",
12756
+ /* @__PURE__ */ jsx18("input", { value: versionInput, onChange: (event) => setVersionInput(event.target.value) })
12757
+ ] }),
12758
+ /* @__PURE__ */ jsx18("button", { type: "button", onClick: () => setVersionId(versionInput.trim()), children: "Medien laden" })
12759
+ ] }),
12760
+ /* @__PURE__ */ jsxs18("div", { className: "szenen-media__attach", children: [
12761
+ /* @__PURE__ */ jsxs18("label", { children: [
12762
+ "Asset-ID",
12763
+ /* @__PURE__ */ jsx18("input", { value: assetId, onChange: (event) => setAssetId(event.target.value) })
12764
+ ] }),
12765
+ /* @__PURE__ */ jsxs18("label", { children: [
12766
+ "Rolle",
12767
+ /* @__PURE__ */ jsxs18("select", { value: role, onChange: (event) => setRole(event.target.value), children: [
12768
+ /* @__PURE__ */ jsx18("option", { value: "reference", children: "reference" }),
12769
+ /* @__PURE__ */ jsx18("option", { value: "mask", children: "mask" }),
12770
+ /* @__PURE__ */ jsx18("option", { value: "palette", children: "palette" }),
12771
+ /* @__PURE__ */ jsx18("option", { value: "example", children: "example" })
12772
+ ] })
12773
+ ] }),
12774
+ /* @__PURE__ */ jsxs18("label", { children: [
12775
+ "Position",
12776
+ /* @__PURE__ */ jsx18("input", { type: "number", min: "0", value: position, onChange: (event) => setPosition(event.target.value) })
12777
+ ] }),
12778
+ /* @__PURE__ */ jsx18("button", { type: "button", disabled: busy, onClick: zuordnen, children: "zuordnen" })
12779
+ ] }),
12780
+ versionPhase.status === "loading" && versionId && /* @__PURE__ */ jsx18("p", { children: "Versionsmedien werden geladen \u2026" }),
12781
+ versionPhase.status === "error" && /* @__PURE__ */ jsxs18("p", { role: "alert", children: [
12782
+ "Versionsmedien konnten nicht geladen werden: ",
12783
+ versionPhase.message
12784
+ ] }),
12785
+ versionPhase.status === "ready" && versionId && versionMedien.length === 0 && /* @__PURE__ */ jsxs18("p", { children: [
12786
+ "Version ",
12787
+ versionId,
12788
+ " hat keine Medien."
12789
+ ] }),
12790
+ /* @__PURE__ */ jsx18("ul", { className: "szenen-media__list", children: versionMedien.map((medium) => /* @__PURE__ */ jsxs18("li", { children: [
12791
+ /* @__PURE__ */ jsx18("code", { children: medium.assetId }),
12792
+ /* @__PURE__ */ jsxs18("span", { children: [
12793
+ medium.role,
12794
+ " \xB7 Position ",
12795
+ medium.position
12796
+ ] }),
12797
+ medium.url && /* @__PURE__ */ jsx18("a", { href: medium.url, target: "_blank", rel: "noreferrer", children: "\xF6ffnen" }),
12798
+ /* @__PURE__ */ jsx18("button", { type: "button", disabled: busy, onClick: () => entfernen(medium), children: "entfernen" })
12799
+ ] }, `${medium.assetId}:${medium.role}`)) })
12800
+ ] }),
12801
+ /* @__PURE__ */ jsxs18("section", { children: [
12802
+ /* @__PURE__ */ jsx18("h3", { children: "Item-Medien" }),
12803
+ /* @__PURE__ */ jsxs18("div", { className: "szenen-media__lookup", children: [
12804
+ /* @__PURE__ */ jsxs18("label", { children: [
12805
+ "Item-ID",
12806
+ /* @__PURE__ */ jsx18("input", { value: itemInput, onChange: (event) => setItemInput(event.target.value) })
12807
+ ] }),
12808
+ /* @__PURE__ */ jsx18("button", { type: "button", onClick: () => setItemId(itemInput.trim()), children: "Item-Medien laden" })
12809
+ ] }),
12810
+ itemPhase.status === "error" && /* @__PURE__ */ jsxs18("p", { role: "alert", children: [
12811
+ "Item-Medien konnten nicht geladen werden: ",
12812
+ itemPhase.message
12813
+ ] }),
12814
+ /* @__PURE__ */ jsx18("ul", { className: "szenen-media__list", children: itemMedien.map((medium) => /* @__PURE__ */ jsxs18("li", { children: [
12815
+ /* @__PURE__ */ jsx18("code", { children: medium.assetId }),
12816
+ /* @__PURE__ */ jsxs18("span", { children: [
12817
+ medium.role,
12818
+ " \xB7 Position ",
12819
+ medium.position
12820
+ ] }),
12821
+ medium.url && /* @__PURE__ */ jsx18("a", { href: medium.url, target: "_blank", rel: "noreferrer", children: "\xF6ffnen" })
12822
+ ] }, `${medium.assetId}:${medium.role}:${medium.position}`)) })
12823
+ ] }),
12824
+ notiz && /* @__PURE__ */ jsx18("p", { role: notiz.tone === "error" ? "alert" : "status", "data-tone": notiz.tone, children: notiz.text })
12825
+ ] });
12826
+ };
12827
+
12300
12828
  // src/features/szenen/SzenenView.tsx
12301
12829
  import { jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
12302
12830
  var SzenenView = ({
@@ -12305,9 +12833,9 @@ var SzenenView = ({
12305
12833
  initialArea,
12306
12834
  onAreaChange
12307
12835
  }) => {
12308
- const bereiche = useMemo4(() => areasFor(port), [port]);
12309
- const faehig = useMemo4(() => capabilitiesOf(port), [port]);
12310
- const [aktiv, setAktiv] = useState17(() => resolveArea(port, initialArea));
12836
+ const bereiche = useMemo5(() => areasFor(port), [port]);
12837
+ const faehig = useMemo5(() => capabilitiesOf(port), [port]);
12838
+ const [aktiv, setAktiv] = useState20(() => resolveArea(port, initialArea));
12311
12839
  const wechsle = (id) => {
12312
12840
  setAktiv(id);
12313
12841
  onAreaChange?.(id);
@@ -12318,7 +12846,7 @@ var SzenenView = ({
12318
12846
  /* @__PURE__ */ jsxs19("div", { children: [
12319
12847
  /* @__PURE__ */ jsx19("p", { className: "szenen-view__eyebrow", children: "Vom Motiv zum Bild" }),
12320
12848
  /* @__PURE__ */ jsx19("h2", { children: "Bildprompt aus einer Szene" }),
12321
- /* @__PURE__ */ jsx19("p", { children: "Bausteine w\xE4hlen, Szene und Bildaufbau festlegen und daraus einen wiederholbaren Prompt erzeugen." })
12849
+ /* @__PURE__ */ jsx19("p", { children: 'Eine Szene anlegen oder aus Bild bzw. Text ableiten und daraus einen Prompt starten. Alle so entstandenen Prompts stehen mit ihren Bildern unter \u201EPrompts". Bausteine und Erweitertes pr\xE4zisieren den Weg, wenn er pr\xE4ziser werden soll.' })
12322
12850
  ] }),
12323
12851
  /* @__PURE__ */ jsx19("span", { className: "szenen-view__step", children: aktuellerBereich.title })
12324
12852
  ] }),
@@ -12337,7 +12865,8 @@ var SzenenView = ({
12337
12865
  bereich.id
12338
12866
  )) }),
12339
12867
  /* @__PURE__ */ jsxs19("section", { className: "szenen-view__area", "aria-label": aktuellerBereich.title, children: [
12340
- aktiv === "derive" && port.derivation && /* @__PURE__ */ jsx19(DeriveArea, { port, contextId }),
12868
+ aktiv === "scene" && /* @__PURE__ */ jsx19(SceneArea, { port, contextId }),
12869
+ aktiv === "prompts" && /* @__PURE__ */ jsx19(PromptsArea, { port, contextId }),
12341
12870
  aktiv === "elements" && /* @__PURE__ */ jsx19(
12342
12871
  ElementsArea,
12343
12872
  {
@@ -12348,9 +12877,7 @@ var SzenenView = ({
12348
12877
  listTitle: "Bausteine"
12349
12878
  }
12350
12879
  ),
12351
- aktiv === "scene" && /* @__PURE__ */ jsx19(SceneArea, { port, contextId }),
12352
- aktiv === "setup" && /* @__PURE__ */ jsx19(SetupArea, { port, contextId }),
12353
- aktiv === "shot" && /* @__PURE__ */ jsx19(ShotArea, { port, contextId }),
12880
+ aktiv === "advanced" && /* @__PURE__ */ jsx19(AdvancedArea, { port, contextId }),
12354
12881
  aktiv === "media" && port.media && /* @__PURE__ */ jsx19(MediaArea, { port, contextId })
12355
12882
  ] }),
12356
12883
  (!faehig.saveRunAsItem || !faehig.media) && /* @__PURE__ */ jsxs19("details", { className: "szenen-view__host-notes", children: [
@@ -13796,7 +14323,7 @@ function createHfEventChannel(options) {
13796
14323
  let syncInFlight = null;
13797
14324
  let snapshot = null;
13798
14325
  let snapshotGeladen = ohneSnapshot;
13799
- const refresh = async () => {
14326
+ const refresh = async (revision = "main") => {
13800
14327
  if (!snapshotGeladen) {
13801
14328
  snapshot = await loadLatestSzenenSnapshot(namespace, token);
13802
14329
  snapshotGeladen = true;
@@ -13804,7 +14331,7 @@ function createHfEventChannel(options) {
13804
14331
  knownPaths.add(`${namespace}events/${name}`);
13805
14332
  }
13806
14333
  }
13807
- const neue = await loadPendingEvents(namespace, token, sinceTs, knownPaths);
14334
+ const neue = await loadPendingEvents(namespace, token, sinceTs, knownPaths, revision);
13808
14335
  if (neue.length) cachedRawEvents = [...cachedRawEvents, ...neue];
13809
14336
  };
13810
14337
  const sync = () => {
@@ -13815,16 +14342,34 @@ function createHfEventChannel(options) {
13815
14342
  }
13816
14343
  return syncInFlight;
13817
14344
  };
14345
+ const sicht = () => {
14346
+ const pending = cachedRawEvents.filter((event) => typeof event.type === "string" && event.type.startsWith(SZENEN_EVENT_PREFIX)).map((event) => ({
14347
+ type: event.type,
14348
+ payload: event.payload,
14349
+ clientId: event.clientId,
14350
+ ts: event.ts
14351
+ }));
14352
+ return mergeSnapshotAndPending(snapshot, pending).events;
14353
+ };
13818
14354
  return {
13819
14355
  async readEvents() {
13820
14356
  await sync();
13821
- const pending = cachedRawEvents.filter((event) => typeof event.type === "string" && event.type.startsWith(SZENEN_EVENT_PREFIX)).map((event) => ({
13822
- type: event.type,
13823
- payload: event.payload,
13824
- clientId: event.clientId,
13825
- ts: event.ts
13826
- }));
13827
- return mergeSnapshotAndPending(snapshot, pending).events;
14357
+ return sicht();
14358
+ },
14359
+ async readHead() {
14360
+ return hfRepoHead(token);
14361
+ },
14362
+ async readEventsAt(revision) {
14363
+ await refresh(revision);
14364
+ return sicht();
14365
+ },
14366
+ async appendEventAt(type, payload, parentCommit) {
14367
+ if (!type.startsWith(SZENEN_EVENT_PREFIX)) {
14368
+ throw new Error(
14369
+ `Event-Typ "${type}" gehoert nicht zur Domain szenen. Der geteilte Kanal wird ueber das Praefix "${SZENEN_EVENT_PREFIX}" getrennt.`
14370
+ );
14371
+ }
14372
+ await writeHFEvent(namespace, token, type, payload, [], { ordered: true, parentCommit });
13828
14373
  },
13829
14374
  async appendEvent(type, payload) {
13830
14375
  if (!type.startsWith(SZENEN_EVENT_PREFIX)) {
@@ -14008,11 +14553,11 @@ async function resolveShotPayload(input) {
14008
14553
  };
14009
14554
  }
14010
14555
  const shot = shotPayload;
14011
- for (const fehler4 of ohneKettenReferenz(
14556
+ for (const fehler5 of ohneKettenReferenz(
14012
14557
  validatePayload("shot", shotPayload).errors,
14013
14558
  chain ? "scene_setup_ref" : null
14014
14559
  )) {
14015
- diagnostics.push(diagnosticOfSchemaError(fehler4, `Shot ${shotElementId}`));
14560
+ diagnostics.push(diagnosticOfSchemaError(fehler5, `Shot ${shotElementId}`));
14016
14561
  }
14017
14562
  let setup = null;
14018
14563
  let report = null;
@@ -14124,8 +14669,8 @@ async function resolveScenePayload(input) {
14124
14669
  "Kein Scene Setup, kein Shot: Der Lauf entsteht aus der Szene allein. Bildaufbau, Kamera und Zielmodell wurden nicht ergaenzt, sondern sind nicht vorhanden (D-135)."
14125
14670
  ];
14126
14671
  const diagnostics = [];
14127
- for (const fehler4 of validatePayload("scene", scenePayload).errors) {
14128
- diagnostics.push(diagnosticOfSchemaError(fehler4, `Szene ${sceneElementId}`));
14672
+ for (const fehler5 of validatePayload("scene", scenePayload).errors) {
14673
+ diagnostics.push(diagnosticOfSchemaError(fehler5, `Szene ${sceneElementId}`));
14129
14674
  }
14130
14675
  const faehig = sceneOnlyCapabilities(scenePayload);
14131
14676
  return komponiere({
@@ -15048,8 +15593,13 @@ function createEventLogRunPersistence(channel) {
15048
15593
  // Nur veroeffentlichte Laeufe. Ein Draft-Lauf hat eine **leere**
15049
15594
  // shot_version_id (D-98) und wuerde bei der Suche nach '' sonst
15050
15595
  // faelschlich mitkommen — eine stille Vermischung zweier Herkuenfte.
15596
+ //
15597
+ // Seit P1-B faellt die Entscheidung nicht mehr hier, sondern in
15598
+ // `groupKeyOf()`: dieselbe Regel, aus der auch die Geschwister und
15599
+ // damit die Rundennummer entstehen. Vorher stand sie hier **und**
15600
+ // — anders — in `domainService.getPromptRunWithSiblings()`.
15051
15601
  value: collectPromptRuns(events).filter(
15052
- (run) => sourceOf(run).kind === "published" && run.shot_version_id === shotVersionId
15602
+ (run) => groupKeyOf(run) === `published:${shotVersionId}`
15053
15603
  )
15054
15604
  };
15055
15605
  } catch (error) {
@@ -15071,10 +15621,9 @@ function createEventLogRunPersistence(channel) {
15071
15621
  const events = await channel.readEvents();
15072
15622
  return {
15073
15623
  kind: "ok",
15074
- value: collectPromptRuns(events).filter((run) => {
15075
- const quelle = sourceOf(run);
15076
- return quelle.kind === "draft_chain" && quelle.chain_id === chainId;
15077
- })
15624
+ value: collectPromptRuns(events).filter(
15625
+ (run) => groupKeyOf(run) === `chain:${chainId}`
15626
+ )
15078
15627
  };
15079
15628
  } catch (error) {
15080
15629
  return queryError(error, `Laeufe der Kette ${chainId} konnten nicht gelesen werden`);
@@ -15085,10 +15634,9 @@ function createEventLogRunPersistence(channel) {
15085
15634
  const events = await channel.readEvents();
15086
15635
  return {
15087
15636
  kind: "ok",
15088
- value: collectPromptRuns(events).filter((run) => {
15089
- const quelle = sourceOf(run);
15090
- return quelle.kind === "scene_direct" && quelle.scene.element_id === sceneElementId;
15091
- })
15637
+ value: collectPromptRuns(events).filter(
15638
+ (run) => groupKeyOf(run) === `scene:${sceneElementId}`
15639
+ )
15092
15640
  };
15093
15641
  } catch (error) {
15094
15642
  return queryError(
@@ -15097,6 +15645,16 @@ function createEventLogRunPersistence(channel) {
15097
15645
  );
15098
15646
  }
15099
15647
  },
15648
+ // P1-A: dieselbe Faltung, nur ohne Herkunftsfilter. Sie las schon bisher
15649
+ // in jedem der drei Fälle den **vollen** Log — hier fällt lediglich das
15650
+ // Aussieben weg.
15651
+ async listAll() {
15652
+ try {
15653
+ return { kind: "ok", value: collectPromptRuns(await channel.readEvents()) };
15654
+ } catch (error) {
15655
+ return queryError(error, "Die Prompt-Laeufe konnten nicht gelesen werden");
15656
+ }
15657
+ },
15100
15658
  async record(record) {
15101
15659
  try {
15102
15660
  await channel.appendEvent(SZENEN_EVENT_TYPES.promptRunRecorded, record);
@@ -15393,7 +15951,25 @@ function createClientRunPort(options) {
15393
15951
  `Laeufe der Szene ${sceneElementId} konnten nicht gelesen werden`
15394
15952
  );
15395
15953
  }
15396
- }
15954
+ },
15955
+ // ── Die gemeinsame Prompt-Sicht (P1-A) ────────────────────────────────
15956
+ //
15957
+ // Sie wird nur angeboten, wenn die **Persistenz** aufzählen kann. Ein
15958
+ // Host, dessen Speicher das nicht hergibt, bekommt hier keine Operation —
15959
+ // und die Ansicht nennt den Grund, statt eine leere Liste zu zeigen (§8).
15960
+ // Der Filter läuft über `selectPromptRuns()`, dieselbe Funktion, die auch
15961
+ // der Server benutzt; zwei Bezugsrahmen wären zwei Wahrheiten.
15962
+ ...typeof runs.listAll === "function" ? {
15963
+ async listAllRuns(filter) {
15964
+ try {
15965
+ const alle = await runs.listAll();
15966
+ if (alle.kind !== "ok") return alle;
15967
+ return { kind: "ok", value: selectPromptRuns(alle.value, filter) };
15968
+ } catch (error) {
15969
+ return queryError(error, "Die Prompt-Laeufe konnten nicht gelesen werden");
15970
+ }
15971
+ }
15972
+ } : {}
15397
15973
  };
15398
15974
  }
15399
15975
 
@@ -15646,6 +16222,44 @@ function gruppiereZuItems(bilder) {
15646
16222
  items.sort((a, b) => b.juengstes - a.juengstes);
15647
16223
  return items.map((e) => e.item);
15648
16224
  }
16225
+ function locatorAus(itemId, position, anzahl) {
16226
+ return anzahl === 1 ? itemId : `${itemId}-${position}`;
16227
+ }
16228
+ function mimeTypAus(dateiname) {
16229
+ const n = dateiname.toLowerCase();
16230
+ if (n.endsWith(".png")) return "image/png";
16231
+ if (n.endsWith(".webp")) return "image/webp";
16232
+ return "image/jpeg";
16233
+ }
16234
+ function passt(item, suche) {
16235
+ const f = suche.trim().toLowerCase();
16236
+ if (!f) return true;
16237
+ return (item.title ?? "").toLowerCase().includes(f) || item.id.toLowerCase().includes(f);
16238
+ }
16239
+ function itemsAusBestand(items, options = {}) {
16240
+ const suche = options.search ?? "";
16241
+ const werke = [];
16242
+ const passende = items.filter((i) => i && typeof i.id === "string" && i.id.length > 0 && passt(i, suche)).sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0) || (a.id < b.id ? 1 : a.id > b.id ? -1 : 0));
16243
+ for (const item of passende) {
16244
+ const galerie = Array.isArray(item.gallery) ? item.gallery : [];
16245
+ const titel = item.title || item.id;
16246
+ const bilder = [];
16247
+ galerie.forEach((eintrag, position) => {
16248
+ const datei = eintrag?.file;
16249
+ if (!datei) return;
16250
+ bilder.push({
16251
+ mediaRef: locatorAus(item.id, position, galerie.length),
16252
+ title: galerie.length === 1 ? titel : `${titel} \xB7 Bild ${position + 1}`,
16253
+ mimeType: mimeTypAus(String(datei)),
16254
+ thumbUrl: options.thumbUrl ? options.thumbUrl(String(datei)) : null
16255
+ });
16256
+ });
16257
+ if (bilder.length === 0) continue;
16258
+ werke.push({ itemRef: item.id, title: titel, images: bilder });
16259
+ if (options.limit && werke.length >= options.limit) break;
16260
+ }
16261
+ return werke;
16262
+ }
15649
16263
 
15650
16264
  // src/lib/szenen/hfImageLibrary.ts
15651
16265
  function fehler2(nachricht, retryable = false) {
@@ -15776,8 +16390,8 @@ function createFlowImageProvider(generateImage) {
15776
16390
  // Kein Seed: weder als Eingabe dokumentiert noch als Ausgabe
15777
16391
  // geliefert — nichts wird erfunden (D-107).
15778
16392
  });
15779
- } catch (fehler4) {
15780
- ersterFehler = fehler4 instanceof Error ? fehler4.message : String(fehler4);
16393
+ } catch (fehler5) {
16394
+ ersterFehler = fehler5 instanceof Error ? fehler5.message : String(fehler5);
15781
16395
  break;
15782
16396
  }
15783
16397
  }
@@ -16306,6 +16920,252 @@ function createImageGenerationPort(options) {
16306
16920
  };
16307
16921
  }
16308
16922
 
16923
+ // src/lib/szenen/imageJobStore.ts
16924
+ var istText7 = (wert) => typeof wert === "string" && wert.trim().length > 0;
16925
+ var SETTLED_STATUS = ["succeeded", "partial", "failed", "rejected"];
16926
+ function collectImageJobs(events) {
16927
+ const nachId = /* @__PURE__ */ new Map();
16928
+ for (const event of events) {
16929
+ if (event.type !== SZENEN_EVENT_TYPES.imageJobRequested) continue;
16930
+ const payload = event.payload;
16931
+ if (!payload || !istText7(payload.job_id) || !istText7(payload.prompt_run_id)) continue;
16932
+ if (nachId.has(payload.job_id)) continue;
16933
+ nachId.set(payload.job_id, {
16934
+ job_id: payload.job_id,
16935
+ prompt_run_id: payload.prompt_run_id,
16936
+ execution_target_model_key: payload.execution_target_model_key ?? "",
16937
+ execution_profile_id: payload.execution_profile_id ?? "",
16938
+ image_count: typeof payload.image_count === "number" ? payload.image_count : 0,
16939
+ ...payload.aspect_ratio ? { aspect_ratio: payload.aspect_ratio } : {},
16940
+ requested_by: payload.requested_by ?? "",
16941
+ store_id: payload.store_id ?? "",
16942
+ created_at: payload.created_at ?? "",
16943
+ status: "pending",
16944
+ claim_attempts: []
16945
+ });
16946
+ }
16947
+ for (const event of events) {
16948
+ if (event.type !== SZENEN_EVENT_TYPES.imageJobClaimed) continue;
16949
+ const payload = event.payload;
16950
+ if (!payload || !istText7(payload.job_id) || !istText7(payload.claim_id)) continue;
16951
+ const auftrag = nachId.get(payload.job_id);
16952
+ if (!auftrag) continue;
16953
+ const anspruch = {
16954
+ job_id: payload.job_id,
16955
+ claim_id: payload.claim_id,
16956
+ client_id: payload.client_id ?? "",
16957
+ claimed_at: payload.claimed_at ?? ""
16958
+ };
16959
+ nachId.set(payload.job_id, {
16960
+ ...auftrag,
16961
+ claim: auftrag.claim ?? anspruch,
16962
+ claim_attempts: [...auftrag.claim_attempts, anspruch],
16963
+ status: auftrag.status === "pending" ? "claimed" : auftrag.status
16964
+ });
16965
+ }
16966
+ for (const event of events) {
16967
+ if (event.type !== SZENEN_EVENT_TYPES.imageJobSettled) continue;
16968
+ const payload = event.payload;
16969
+ if (!payload || !istText7(payload.job_id)) continue;
16970
+ const auftrag = nachId.get(payload.job_id);
16971
+ if (!auftrag) continue;
16972
+ if (!auftrag.claim || auftrag.claim.claim_id !== payload.claim_id) continue;
16973
+ if (auftrag.status !== "claimed") continue;
16974
+ if (!SETTLED_STATUS.includes(payload.status)) continue;
16975
+ nachId.set(payload.job_id, {
16976
+ ...auftrag,
16977
+ status: payload.status,
16978
+ ...payload.generation_batch_id ? { generation_batch_id: payload.generation_batch_id } : {},
16979
+ ...payload.item_id ? { item_id: payload.item_id } : {},
16980
+ ...typeof payload.produced_count === "number" ? { produced_count: payload.produced_count } : {},
16981
+ ...payload.diagnostic ? { diagnostic: payload.diagnostic } : {},
16982
+ settled_at: payload.settled_at ?? ""
16983
+ });
16984
+ }
16985
+ return [...nachId.values()];
16986
+ }
16987
+ function jobsForPromptRun(events, runId) {
16988
+ return collectImageJobs(events).filter((j) => j.prompt_run_id === runId);
16989
+ }
16990
+ function findImageJob(events, jobId) {
16991
+ return collectImageJobs(events).find((j) => j.job_id === jobId) ?? null;
16992
+ }
16993
+ function claimWon(job, claimId) {
16994
+ return Boolean(job && job.claim && job.claim.claim_id === claimId);
16995
+ }
16996
+ function generationBatchIdOf(jobId) {
16997
+ return `job-${jobId}`;
16998
+ }
16999
+
17000
+ // src/lib/szenen/imageJobPort.ts
17001
+ var ANSPRUCH_VERSUCHE = 4;
17002
+ function fehler4(error, aktion) {
17003
+ return {
17004
+ message: `${aktion}: ${error instanceof Error ? error.message : String(error)}`
17005
+ };
17006
+ }
17007
+ function createImageJobPort(options) {
17008
+ const { channel, imageGeneration, storeId, clientId } = options;
17009
+ const newClaimId = options.newClaimId ?? (() => crypto.randomUUID());
17010
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
17011
+ return {
17012
+ async listJobs(runId) {
17013
+ try {
17014
+ return { kind: "ok", value: jobsForPromptRun(await channel.readEvents(), runId) };
17015
+ } catch (error) {
17016
+ return {
17017
+ kind: "error",
17018
+ ...fehler4(error, `Die Bildauftraege des Laufs ${runId} konnten nicht gelesen werden`),
17019
+ retryable: true
17020
+ };
17021
+ }
17022
+ },
17023
+ async claimAndRun(jobId) {
17024
+ let letzterGrund = "";
17025
+ for (let versuch = 1; versuch <= ANSPRUCH_VERSUCHE; versuch += 1) {
17026
+ let kopf;
17027
+ try {
17028
+ kopf = await channel.readHead();
17029
+ } catch (error) {
17030
+ return {
17031
+ kind: "error",
17032
+ ...fehler4(error, `Der Kopf-Commit des Bestands war nicht lesbar; Bildauftrag ${jobId} wurde nicht uebernommen`),
17033
+ retryable: true
17034
+ };
17035
+ }
17036
+ let auftrag;
17037
+ try {
17038
+ auftrag = findImageJob(await channel.readEventsAt(kopf), jobId);
17039
+ } catch (error) {
17040
+ return {
17041
+ kind: "error",
17042
+ ...fehler4(error, `Der Bildauftrag ${jobId} konnte nicht gelesen werden`),
17043
+ retryable: true
17044
+ };
17045
+ }
17046
+ if (!auftrag) {
17047
+ return {
17048
+ kind: "rejected",
17049
+ message: `Der Bildauftrag ${jobId} steht nicht im autoritativen Log dieses Bestands.`
17050
+ };
17051
+ }
17052
+ if (auftrag.store_id !== storeId) {
17053
+ return {
17054
+ kind: "rejected",
17055
+ message: `Der Bildauftrag ${jobId} gilt fuer den Bestand "${auftrag.store_id}", dieser Host arbeitet auf "${storeId}". Nutzer, Umgebung und Bestand muessen zusammenpassen.`
17056
+ };
17057
+ }
17058
+ if (auftrag.status !== "pending") {
17059
+ return {
17060
+ kind: "lost",
17061
+ job: auftrag,
17062
+ message: `Der Bildauftrag ${jobId} ist bereits "${auftrag.status}"` + (auftrag.claim ? ` (Client ${auftrag.claim.client_id})` : "") + ". Er wird nicht erneut ausgefuehrt \u2014 ein zweiter Lauf waere ein zweites Mal bezahlte Rechenzeit."
17063
+ };
17064
+ }
17065
+ const anspruch = {
17066
+ job_id: jobId,
17067
+ claim_id: newClaimId(),
17068
+ client_id: clientId,
17069
+ claimed_at: now().toISOString()
17070
+ };
17071
+ try {
17072
+ await channel.appendEventAt(
17073
+ SZENEN_EVENT_TYPES.imageJobClaimed,
17074
+ anspruch,
17075
+ kopf
17076
+ );
17077
+ } catch (error) {
17078
+ if (istPreconditionFehler(error)) {
17079
+ letzterGrund = error instanceof Error ? error.message : String(error);
17080
+ continue;
17081
+ }
17082
+ return {
17083
+ kind: "error",
17084
+ ...fehler4(error, `Der Anspruch auf Bildauftrag ${jobId} konnte nicht angehaengt werden`),
17085
+ retryable: true
17086
+ };
17087
+ }
17088
+ const gewonnen = {
17089
+ ...auftrag,
17090
+ status: "claimed",
17091
+ claim: anspruch,
17092
+ claim_attempts: [...auftrag.claim_attempts, anspruch]
17093
+ };
17094
+ return await fuehreAus(gewonnen, anspruch, jobId);
17095
+ }
17096
+ return {
17097
+ kind: "error",
17098
+ message: `Der Bildauftrag ${jobId} liess sich in ${ANSPRUCH_VERSUCHE} Versuchen nicht exklusiv uebernehmen \u2014 der Bestand wird gerade von anderer Seite beschrieben (${letzterGrund}). Es wurde nichts ausgefuehrt und nichts bezahlt; ein neuer Versuch ist gefahrlos.`,
17099
+ retryable: true
17100
+ };
17101
+ }
17102
+ };
17103
+ async function fuehreAus(gewonnen, anspruch, jobId) {
17104
+ const ergebnis = await imageGeneration.startFromPromptRun({
17105
+ runId: gewonnen.prompt_run_id,
17106
+ generationBatchId: generationBatchIdOf(jobId),
17107
+ executionTargetModelKey: gewonnen.execution_target_model_key,
17108
+ executionProfileId: gewonnen.execution_profile_id,
17109
+ imageCount: gewonnen.image_count,
17110
+ ...gewonnen.aspect_ratio ? { aspectRatio: gewonnen.aspect_ratio } : {}
17111
+ });
17112
+ const abschluss = {
17113
+ job_id: jobId,
17114
+ claim_id: anspruch.claim_id,
17115
+ ...abschlussFelder(ergebnis, jobId),
17116
+ settled_at: now().toISOString()
17117
+ };
17118
+ try {
17119
+ await channel.appendEvent(SZENEN_EVENT_TYPES.imageJobSettled, abschluss);
17120
+ } catch {
17121
+ }
17122
+ const gelesen = await channel.readEvents().catch(() => null);
17123
+ return {
17124
+ kind: "ok",
17125
+ job: (gelesen && findImageJob(gelesen, jobId)) ?? {
17126
+ ...gewonnen,
17127
+ status: abschluss.status,
17128
+ ...abschluss.generation_batch_id ? { generation_batch_id: abschluss.generation_batch_id } : {},
17129
+ ...abschluss.diagnostic ? { diagnostic: abschluss.diagnostic } : {},
17130
+ settled_at: abschluss.settled_at
17131
+ }
17132
+ };
17133
+ }
17134
+ }
17135
+ function istPreconditionFehler(error) {
17136
+ return Boolean(error && typeof error === "object" && error.name === "HfPreconditionFailed");
17137
+ }
17138
+ function abschlussFelder(ergebnis, jobId) {
17139
+ const batchId = generationBatchIdOf(jobId);
17140
+ switch (ergebnis.kind) {
17141
+ case "succeeded":
17142
+ return {
17143
+ status: "succeeded",
17144
+ generation_batch_id: batchId,
17145
+ item_id: ergebnis.batch.item_id,
17146
+ produced_count: ergebnis.batch.images.length
17147
+ };
17148
+ case "partial":
17149
+ return {
17150
+ status: "partial",
17151
+ generation_batch_id: batchId,
17152
+ item_id: ergebnis.batch.item_id,
17153
+ produced_count: ergebnis.produced,
17154
+ diagnostic: `Angefordert ${ergebnis.requested}, geliefert ${ergebnis.produced}.`
17155
+ };
17156
+ case "failed":
17157
+ return { status: "failed", produced_count: 0, diagnostic: ergebnis.message };
17158
+ case "unsupported":
17159
+ return {
17160
+ status: "rejected",
17161
+ produced_count: 0,
17162
+ diagnostic: `Zielmodell "${ergebnis.targetModelKey}" ist in dieser Umgebung nicht ausfuehrbar: ${ergebnis.reason}`
17163
+ };
17164
+ default:
17165
+ return { status: "rejected", produced_count: 0, diagnostic: ergebnis.message };
17166
+ }
17167
+ }
17168
+
16309
17169
  // src/lib/szenen/flowViewPort.ts
16310
17170
  var defaultContext = {
16311
17171
  async chainFor(contextId) {
@@ -16404,10 +17264,21 @@ function createFlowSzenenViewPort(options) {
16404
17264
  },
16405
17265
  ...options.now ? { now: options.now } : {}
16406
17266
  }) : void 0;
17267
+ const kanalMitRevision = typeof channel.readHead === "function" && typeof channel.readEventsAt === "function" && typeof channel.appendEventAt === "function";
17268
+ const imageJobs = imageGeneration && hatHfZugangsdaten && kanalMitRevision ? createImageJobPort({
17269
+ channel,
17270
+ imageGeneration,
17271
+ // Dieselbe Form, in der der Server seinen Bestand kennt (`storeId`).
17272
+ storeId: `hf:${options.namespace}`,
17273
+ clientId: options.imageJobClientId ?? crypto.randomUUID(),
17274
+ ...options.newImageJobClaimId ? { newClaimId: options.newImageJobClaimId } : {},
17275
+ ...options.now ? { now: options.now } : {}
17276
+ }) : void 0;
16407
17277
  const port = { elements, context, runs, derivation, imageBatchHistory };
16408
17278
  if (imageLibrary) port.imageLibrary = imageLibrary;
16409
17279
  if (imageBatchWrite) port.imageBatchWrite = imageBatchWrite;
16410
17280
  if (imageGeneration) port.imageGeneration = imageGeneration;
17281
+ if (imageJobs) port.imageJobs = imageJobs;
16411
17282
  return port;
16412
17283
  }
16413
17284
 
@@ -16479,22 +17350,29 @@ export {
16479
17350
  resolverFromQueries,
16480
17351
  isProjectionCurrent,
16481
17352
  isOk,
16482
- SZENEN_ITEM_PREFIX,
16483
- itemIdForRun,
16484
- isSzenenItemId,
16485
- checkSzenenBatchOrigin,
16486
- mergeSzenenBatchOrigin,
16487
- roundOfRun,
16488
- deriveLineage,
16489
- deriveItemFromRun,
16490
17353
  SZENEN_EVENT_PREFIX,
16491
17354
  SZENEN_EVENT_TYPES,
16492
17355
  reduceElementEvents,
16493
17356
  draftRevisionOf,
16494
17357
  logRevisionOf,
16495
17358
  preferredHeadRevisionOf,
17359
+ sourceOf,
16496
17360
  collectPromptRuns,
16497
17361
  findPromptRun,
17362
+ groupOf,
17363
+ groupKeyOf,
17364
+ siblingsOf,
17365
+ namingElementOf,
17366
+ contextOfRun,
17367
+ selectPromptRuns,
17368
+ SZENEN_ITEM_PREFIX,
17369
+ itemIdForRun,
17370
+ isSzenenItemId,
17371
+ checkSzenenBatchOrigin,
17372
+ mergeSzenenBatchOrigin,
17373
+ roundOfRun,
17374
+ deriveLineage,
17375
+ deriveItemFromRun,
16498
17376
  DEFAULT_SEED_VORLAGE,
16499
17377
  parseSeedListe,
16500
17378
  formatSeedListe,
@@ -16580,6 +17458,9 @@ export {
16580
17458
  createHfImageStore,
16581
17459
  locatorTeile,
16582
17460
  gruppiereZuItems,
17461
+ locatorAus,
17462
+ mimeTypAus,
17463
+ itemsAusBestand,
16583
17464
  createHfImageLibrary,
16584
17465
  FLOW_EXECUTION_PATH,
16585
17466
  createFlowImageProvider,
@@ -16594,5 +17475,11 @@ export {
16594
17475
  createHfImageGenerationBatchPersistence,
16595
17476
  createHfImageBatchWrite,
16596
17477
  createImageGenerationPort,
17478
+ collectImageJobs,
17479
+ jobsForPromptRun,
17480
+ findImageJob,
17481
+ claimWon,
17482
+ generationBatchIdOf,
17483
+ createImageJobPort,
16597
17484
  createFlowSzenenViewPort
16598
17485
  };