@rslsp1/fa-app-tools 3.0.0 → 5.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.
package/dist/szenen.js CHANGED
@@ -40,6 +40,7 @@ __export(szenen_exports, {
40
40
  ELEMENT_VERSION_SCHEMA_FILE: () => ELEMENT_VERSION_SCHEMA_FILE,
41
41
  FLOW_EXECUTION_PATH: () => FLOW_EXECUTION_PATH,
42
42
  PROBE_VERSION_ID: () => PROBE_VERSION_ID,
43
+ PROMPT_RULE_EFFECT: () => PROMPT_RULE_EFFECT,
43
44
  REFERENCE_FIELD: () => REFERENCE_FIELD,
44
45
  RULES_WITHOUT_CAPABILITY_EFFECT: () => RULES_WITHOUT_CAPABILITY_EFFECT,
45
46
  RULE_SCOPES: () => RULE_SCOPES,
@@ -76,6 +77,7 @@ __export(szenen_exports, {
76
77
  checkDraftChainMeta: () => checkDraftChainMeta,
77
78
  checkElementType: () => checkElementType,
78
79
  checkImageExecutionProfile: () => checkImageExecutionProfile,
80
+ checkImageExecutionProfiles: () => checkImageExecutionProfiles,
79
81
  checkImageModelParameters: () => checkImageModelParameters,
80
82
  checkOptionalId: () => checkOptionalId,
81
83
  checkParents: () => checkParents,
@@ -124,7 +126,10 @@ __export(szenen_exports, {
124
126
  derivationPoliciesOf: () => derivationPoliciesOf,
125
127
  deriveItemFromRun: () => deriveItemFromRun,
126
128
  deriveLineage: () => deriveLineage,
129
+ describableContent: () => describableContent,
127
130
  describeTargetModelFailure: () => describeTargetModelFailure,
131
+ diagnosticOfFinding: () => diagnosticOfFinding,
132
+ diagnosticOfSchemaError: () => diagnosticOfSchemaError,
128
133
  differentialSteps: () => differentialSteps,
129
134
  draftRevisionOf: () => draftRevisionOf,
130
135
  elementStoreContractChecks: () => elementStoreContractChecks,
@@ -139,14 +144,18 @@ __export(szenen_exports, {
139
144
  firstError: () => firstError,
140
145
  formatSchemaErrors: () => formatSchemaErrors,
141
146
  freezeDraftChain: () => freezeDraftChain,
147
+ freezeSceneWorkingState: () => freezeSceneWorkingState,
142
148
  getSzenenSchemas: () => getSzenenSchemas,
149
+ gruppiereZuItems: () => gruppiereZuItems,
143
150
  hasPlacementCapability: () => hasPlacementCapability,
144
151
  isKnownElementType: () => isKnownElementType,
145
152
  isOk: () => isOk,
146
153
  isProjectionCurrent: () => isProjectionCurrent,
147
154
  isSzenenItemId: () => isSzenenItemId,
148
155
  itemIdForRun: () => itemIdForRun,
156
+ ladeVorschauen: () => ladeVorschauen,
149
157
  linkFor: () => linkFor,
158
+ locatorTeile: () => locatorTeile,
150
159
  logRevisionOf: () => logRevisionOf,
151
160
  mergeSzenenBatchOrigin: () => mergeSzenenBatchOrigin,
152
161
  noticeFor: () => noticeFor,
@@ -155,22 +164,26 @@ __export(szenen_exports, {
155
164
  parseRunResponse: () => parseRunResponse,
156
165
  preferredHeadRevisionOf: () => preferredHeadRevisionOf,
157
166
  preferredVersionId: () => preferredVersionId,
167
+ promptDiagnosticsOf: () => promptDiagnosticsOf,
158
168
  publishedHeadsOf: () => publishedHeadsOf,
159
169
  recoveriesForRun: () => recoveriesForRun,
160
170
  recoveryStatusOf: () => recoveryStatusOf,
161
171
  reduceElementEvents: () => reduceElementEvents,
162
172
  resolveArea: () => resolveArea,
163
173
  resolvePreferredHead: () => resolvePreferredHead,
174
+ resolveScenePayload: () => resolveScenePayload,
164
175
  resolveShot: () => resolveShot,
165
176
  resolveShotPayload: () => resolveShotPayload,
166
177
  resolveTargetModelOver: () => resolveTargetModelOver,
167
178
  resolverFromQueries: () => resolverFromQueries,
168
179
  roundOfRun: () => roundOfRun,
180
+ rulesWithoutPromptEffect: () => rulesWithoutPromptEffect,
169
181
  runElementStoreContract: () => runElementStoreContract,
170
182
  runPromptDerivation: () => runPromptDerivation,
171
183
  runPublishChecks: () => runPublishChecks,
172
184
  runSceneDerivation: () => runSceneDerivation,
173
185
  runStoreDifferential: () => runStoreDifferential,
186
+ sceneOnlyCapabilities: () => sceneOnlyCapabilities,
174
187
  scopeOfPath: () => scopeOfPath,
175
188
  selectableTargetModels: () => selectableTargetModels,
176
189
  showChain: () => showChain,
@@ -4254,6 +4267,96 @@ function finish(report) {
4254
4267
  };
4255
4268
  }
4256
4269
 
4270
+ // src/lib/szenen/promptOperation.ts
4271
+ var PROMPT_RULE_EFFECT = {
4272
+ "P-01": "diagnostic",
4273
+ // Kamera-ID im Rig nicht auflösbar → ohne Kamera weiter
4274
+ "P-02": "diagnostic",
4275
+ // mehrdeutige Kamera-ID → erklären, nicht sperren
4276
+ "P-03": "diagnostic",
4277
+ // Abstammungsintegrität einer camera_id — Publish-Frage
4278
+ "P-04": "diagnostic",
4279
+ // referenzierte Version nicht veröffentlicht/sichtbar
4280
+ "P-05": "diagnostic",
4281
+ // Node-Referenz nicht auflösbar → Teil auslassen
4282
+ "P-06": "diagnostic",
4283
+ // Placement-Node mehrdeutig → ohne exakte Platzierung
4284
+ "P-07": "diagnostic",
4285
+ // spatial Pattern ohne can_place → Pattern weglassen
4286
+ "P-08": "diagnostic",
4287
+ // unvollständige Pattern-Bindings
4288
+ "P-09": "not_applicable",
4289
+ // reine Publish-/Prozessregel für sequence
4290
+ "P-10": "diagnostic",
4291
+ // Zyklus: Blocker nur, wenn keine endliche Auflösung bleibt
4292
+ "P-11": "diagnostic",
4293
+ // Override-Disziplin, schon bisher advisory
4294
+ "P-12": "not_applicable",
4295
+ // Laufzeitregel der Auflösung, keine Bedingung
4296
+ "P-13": "diagnostic",
4297
+ // pattern_derived ohne Pattern-Platzierung
4298
+ "P-14": "diagnostic",
4299
+ // Bildgrößen-/Optik-Plausibilität, erklärte Absicht gewinnt
4300
+ "P-15": "diagnostic",
4301
+ // vertikaler look_at ohne up_hint — Rollwinkel ist Spezialpräzision
4302
+ "P-16": "diagnostic",
4303
+ // Placements ohne Koordinatensystem
4304
+ "P-17": "diagnostic"
4305
+ // Target Model nicht auflösbar → generischer Prompt
4306
+ };
4307
+ function rulesWithoutPromptEffect(alleRegeln) {
4308
+ return alleRegeln.filter((rule) => !(rule in PROMPT_RULE_EFFECT));
4309
+ }
4310
+ function diagnosticOfFinding(finding) {
4311
+ return {
4312
+ code: finding.rule,
4313
+ origin: "rule",
4314
+ path: finding.path,
4315
+ message: finding.message
4316
+ };
4317
+ }
4318
+ function diagnosticOfSchemaError(error, where) {
4319
+ return {
4320
+ code: "schema",
4321
+ origin: "schema",
4322
+ path: error.instancePath || "/",
4323
+ message: `${where}: ${error.message}`
4324
+ };
4325
+ }
4326
+ function promptDiagnosticsOf(checks) {
4327
+ const befunde = [];
4328
+ for (const finding of checks.findings) {
4329
+ if (PROMPT_RULE_EFFECT[finding.rule] === "not_applicable") continue;
4330
+ befunde.push(diagnosticOfFinding(finding));
4331
+ }
4332
+ return befunde;
4333
+ }
4334
+ var isObject5 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
4335
+ var asArray2 = (v) => Array.isArray(v) ? v : [];
4336
+ var istText = (v) => typeof v === "string" && v.trim().length > 0;
4337
+ function describableContent(scenePayload) {
4338
+ if (!isObject5(scenePayload)) {
4339
+ return {
4340
+ ok: false,
4341
+ message: `Die Szene ist kein Objekt, sondern ${Array.isArray(scenePayload) ? "ein Array" : typeof scenePayload}. Daraus laesst sich kein Textauftrag herstellen.`
4342
+ };
4343
+ }
4344
+ const nodes = asArray2(scenePayload.nodes);
4345
+ for (const node of nodes) {
4346
+ if (!isObject5(node)) continue;
4347
+ const semantic = isObject5(node.semantic) ? node.semantic : null;
4348
+ if (semantic && istText(semantic.description)) return { ok: true };
4349
+ if (asArray2(node.component_refs).length > 0) return { ok: true };
4350
+ }
4351
+ const environment = isObject5(scenePayload.environment) ? scenePayload.environment : null;
4352
+ if (environment && istText(environment.text)) return { ok: true };
4353
+ if (environment && asArray2(environment.component_refs).length > 0) return { ok: true };
4354
+ return {
4355
+ ok: false,
4356
+ message: nodes.length === 0 ? "Die Szene hat keine Knoten und keinen Umgebungstext. Ohne beschreibbaren Inhalt gibt es nichts, woraus ein Prompt entstehen koennte." : `Keiner der ${nodes.length} Knoten traegt eine Beschreibung oder eine Komponentenreferenz, und es gibt keinen Umgebungstext. Ein Prompt daraus waere eine leere Behauptung.`
4357
+ };
4358
+ }
4359
+
4257
4360
  // src/lib/szenen/capabilities.ts
4258
4361
  var CapabilityAnchorError = class extends Error {
4259
4362
  };
@@ -4296,9 +4399,9 @@ var FLAG_OF_SCOPE = {
4296
4399
  // die Vererbung an die beiden anderen weitergereicht.
4297
4400
  global: "can_describe_scene"
4298
4401
  };
4299
- var isObject5 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
4402
+ var isObject6 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
4300
4403
  function nodeCountOf(scenePayload) {
4301
- if (!isObject5(scenePayload)) return 0;
4404
+ if (!isObject6(scenePayload)) return 0;
4302
4405
  return Array.isArray(scenePayload.nodes) ? scenePayload.nodes.length : 0;
4303
4406
  }
4304
4407
  async function capabilityReport(input) {
@@ -4323,7 +4426,7 @@ async function capabilityReport(input) {
4323
4426
  reason: "Payload verletzt das Schema \u2014 die Publish-Regeln wurden nicht ausgefuehrt"
4324
4427
  }))
4325
4428
  } : await runPublishChecks({ type, payload: setupPayload, elementId, parents, resolver });
4326
- if (!isObject5(setupPayload)) {
4429
+ if (!isObject6(setupPayload)) {
4327
4430
  const vorgefunden = Array.isArray(setupPayload) ? "array" : typeof setupPayload;
4328
4431
  const reasons = [
4329
4432
  {
@@ -4333,10 +4436,14 @@ async function capabilityReport(input) {
4333
4436
  ];
4334
4437
  return {
4335
4438
  can_describe_scene: { state: "no", reasons },
4439
+ // Auch fuer den Promptpfad ein echter Blocker: Eine Payload, die kein
4440
+ // Objekt ist, ist technisch unlesbar (Schritt 07, Klasse A/C) — nicht
4441
+ // bloss fachlich unvollstaendig.
4336
4442
  can_render_prompt: { state: "no", reasons },
4337
4443
  can_place: { state: "no", reasons },
4338
4444
  checks,
4339
- schemaErrors
4445
+ schemaErrors,
4446
+ promptDiagnostics: []
4340
4447
  };
4341
4448
  }
4342
4449
  const blockedByScope = /* @__PURE__ */ new Map();
@@ -4372,6 +4479,8 @@ async function capabilityReport(input) {
4372
4479
  };
4373
4480
  const base = forFlag("can_describe_scene");
4374
4481
  let describe;
4482
+ let szenenPayload = null;
4483
+ let sceneVersionId = null;
4375
4484
  if (base.blocked.length > 0) {
4376
4485
  describe = { state: "no", reasons: base.blocked };
4377
4486
  } else if (schemaErrors.length > 0) {
@@ -4397,9 +4506,10 @@ async function capabilityReport(input) {
4397
4506
  ]
4398
4507
  };
4399
4508
  } else {
4400
- const sceneRef = isObject5(setupPayload.scene_ref) ? setupPayload.scene_ref : null;
4401
- const sceneVersionId = typeof sceneRef?.version_id === "string" ? sceneRef.version_id : null;
4509
+ const sceneRef = isObject6(setupPayload.scene_ref) ? setupPayload.scene_ref : null;
4510
+ sceneVersionId = typeof sceneRef?.version_id === "string" ? sceneRef.version_id : null;
4402
4511
  const scene = chainScene !== void 0 ? { payload: chainScene } : sceneVersionId ? await resolver.getVersion(sceneVersionId) : null;
4512
+ if (scene) szenenPayload = { payload: scene.payload };
4403
4513
  const nodes = nodeCountOf(scene?.payload);
4404
4514
  if (nodes === 0) {
4405
4515
  describe = {
@@ -4446,15 +4556,38 @@ async function capabilityReport(input) {
4446
4556
  if (own.unchecked.length > 0) return { state: "unknown", reasons: own.unchecked };
4447
4557
  return { state: "yes", reasons: [] };
4448
4558
  };
4559
+ const promptFlag = (() => {
4560
+ if (!szenenPayload) {
4561
+ return {
4562
+ state: "unknown",
4563
+ reasons: [
4564
+ {
4565
+ kind: "structure",
4566
+ message: `Die Szene ${sceneVersionId ?? "(ohne version_id)"} wurde nicht gelesen; ob beschreibbarer Inhalt vorliegt, ist damit offen \u2014 keine Absage.`
4567
+ }
4568
+ ]
4569
+ };
4570
+ }
4571
+ const inhalt = describableContent(szenenPayload.payload);
4572
+ if (!inhalt.ok) {
4573
+ return { state: "no", reasons: [{ kind: "structure", message: inhalt.message }] };
4574
+ }
4575
+ return { state: "yes", reasons: [] };
4576
+ })();
4577
+ const promptDiagnostics = [
4578
+ ...schemaErrors.map((e) => diagnosticOfSchemaError(e, `Scene Setup ${elementId}`)),
4579
+ ...promptDiagnosticsOf(checks)
4580
+ ];
4449
4581
  return {
4450
4582
  can_describe_scene: describe,
4451
- can_render_prompt: derived("can_render_prompt"),
4583
+ can_render_prompt: promptFlag,
4452
4584
  can_place: derived("can_place", {
4453
4585
  ok: hasPlacementCapability(setupPayload),
4454
4586
  message: "Das Scene Setup traegt kein coordinate_system und ist damit nicht platzierbar (D-65)."
4455
4587
  }),
4456
4588
  checks,
4457
- schemaErrors
4589
+ schemaErrors,
4590
+ promptDiagnostics
4458
4591
  };
4459
4592
  }
4460
4593
  function unclassifiedPublishRules() {
@@ -4462,16 +4595,37 @@ function unclassifiedPublishRules() {
4462
4595
  (rule) => !(rule in RULE_SCOPES) && !(rule in RULES_WITHOUT_CAPABILITY_EFFECT)
4463
4596
  );
4464
4597
  }
4598
+ function sceneOnlyCapabilities(scenePayload) {
4599
+ const knoten = nodeCountOf(scenePayload);
4600
+ const inhalt = describableContent(scenePayload);
4601
+ return {
4602
+ can_describe_scene: knoten > 0 ? { state: "yes", reasons: [] } : {
4603
+ state: "no",
4604
+ reasons: [{
4605
+ kind: "structure",
4606
+ message: "Die Szene hat keine Knoten. Ohne Knoten gibt es nichts zu beschreiben."
4607
+ }]
4608
+ },
4609
+ can_render_prompt: inhalt.ok ? { state: "yes", reasons: [] } : { state: "no", reasons: [{ kind: "structure", message: inhalt.message }] },
4610
+ can_place: {
4611
+ state: "no",
4612
+ reasons: [{
4613
+ kind: "structure",
4614
+ message: "Zu dieser Szene gehoert kein Scene Setup. Weltplatzierung und coordinate_system liegen ausschliesslich dort (D-39/D-65) \u2014 es gibt also keine belastbaren Koordinaten."
4615
+ }]
4616
+ }
4617
+ };
4618
+ }
4465
4619
 
4466
4620
  // src/lib/szenen/resolve.ts
4467
4621
  var ResolveTechnicalError = class extends Error {
4468
4622
  };
4469
- var isObject6 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
4470
- var asArray2 = (v) => Array.isArray(v) ? v : [];
4623
+ var isObject7 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
4624
+ var asArray3 = (v) => Array.isArray(v) ? v : [];
4471
4625
  var str = (v) => typeof v === "string" ? v : null;
4472
4626
  var num = (v) => typeof v === "number" ? v : null;
4473
4627
  async function refOf(resolver, ref, expected, path) {
4474
- const versionId = isObject6(ref) ? str(ref.version_id) : null;
4628
+ const versionId = isObject7(ref) ? str(ref.version_id) : null;
4475
4629
  if (!versionId) {
4476
4630
  return {
4477
4631
  ok: false,
@@ -4523,8 +4677,11 @@ async function refOf(resolver, ref, expected, path) {
4523
4677
  }
4524
4678
  return { ok: true, version };
4525
4679
  }
4680
+ function ausgelassen(diagnostics, code, path, message) {
4681
+ diagnostics.push({ code, origin: "resolution", path, message });
4682
+ }
4526
4683
  function entraeumlichtePlatzierung(placement, assumptions) {
4527
- if (!isObject6(placement)) return null;
4684
+ if (!isObject7(placement)) return null;
4528
4685
  if (placement.mode !== "pattern") {
4529
4686
  assumptions.push(
4530
4687
  `Die Kameraplatzierung (mode "${str(placement.mode) ?? "unbekannt"}") ist metrisch und entfaellt ohne can_place. Der Renderer darf daraus keine Position ableiten.`
@@ -4539,7 +4696,7 @@ function entraeumlichtePlatzierung(placement, assumptions) {
4539
4696
  const entfallen = [];
4540
4697
  if (placement.offsets !== void 0) entfallen.push("offsets");
4541
4698
  const height = placement.height;
4542
- if (isObject6(height)) {
4699
+ if (isObject7(height)) {
4543
4700
  if (height.mode === "anchor") {
4544
4701
  const behalteneHoehe = {
4545
4702
  mode: "anchor",
@@ -4618,142 +4775,328 @@ async function resolveShotPayload(input) {
4618
4775
  chain
4619
4776
  } = input;
4620
4777
  const assumptions = [];
4621
- const shotVersion = {
4622
- version_id: shotVersionId,
4623
- element_id: shotElementId,
4624
- payload: shotPayload
4625
- };
4626
- const shotErrors = ohneKettenReferenz(
4627
- validatePayload("shot", shotVersion.payload).errors,
4628
- chain ? "scene_setup_ref" : null
4629
- );
4630
- if (shotErrors.length > 0) {
4778
+ const diagnostics = [];
4779
+ if (!isObject7(shotPayload)) {
4631
4780
  return {
4632
4781
  kind: "rejected",
4633
4782
  failure: {
4634
4783
  class: "schema",
4635
4784
  where: "shot",
4636
- message: `Die Shot-Payload verletzt das Schema (${shotErrors.length} Befunde). Es wurde keine ihrer Referenzen aufgeloest.`,
4637
- errors: shotErrors
4785
+ message: `Die Shot-Payload ist kein Objekt, sondern ${Array.isArray(shotPayload) ? "ein Array" : typeof shotPayload}. Daraus laesst sich kein Auftrag herstellen.`,
4786
+ errors: []
4787
+ }
4788
+ };
4789
+ }
4790
+ const shot = shotPayload;
4791
+ for (const fehler4 of ohneKettenReferenz(
4792
+ validatePayload("shot", shotPayload).errors,
4793
+ chain ? "scene_setup_ref" : null
4794
+ )) {
4795
+ diagnostics.push(diagnosticOfSchemaError(fehler4, `Shot ${shotElementId}`));
4796
+ }
4797
+ let setup = null;
4798
+ let report = null;
4799
+ let canPlace = false;
4800
+ let capabilities;
4801
+ let scene;
4802
+ const nenntSetup = chain !== void 0 || shot.scene_setup_ref !== void 0;
4803
+ if (!nenntSetup) {
4804
+ return {
4805
+ kind: "rejected",
4806
+ failure: {
4807
+ class: "no_content",
4808
+ message: `Der Shot ${shotElementId} nennt kein scene_setup_ref und fuehrt keine Draft-Kette. Ohne Setup gibt es keinen Weg zu einer Szene und damit keinen beschreibbaren Inhalt.`
4638
4809
  }
4639
4810
  };
4640
4811
  }
4641
- const shot = shotVersion.payload;
4642
4812
  const setupRef = chain ? { ok: true, version: chain.setup } : await refOf(resolver, shot.scene_setup_ref, "scene_setup", "/scene_setup_ref");
4643
4813
  if (!setupRef.ok) return { kind: "rejected", failure: setupRef.failure };
4644
- const setupErrors = ohneKettenReferenz(
4645
- validatePayload("scene_setup", setupRef.version.payload).errors,
4646
- chain ? "scene_ref" : null
4647
- );
4648
- if (setupErrors.length > 0) {
4814
+ if (!isObject7(setupRef.version.payload)) {
4649
4815
  return {
4650
4816
  kind: "rejected",
4651
4817
  failure: {
4652
4818
  class: "schema",
4653
4819
  where: "scene_setup",
4654
- message: `Die Scene-Setup-Payload verletzt das Schema (${setupErrors.length} Befunde). Weder ihre Referenzen noch der Faehigkeitsbericht wurden mit einem Resolver gefahren.`,
4655
- errors: setupErrors
4820
+ message: `Die Scene-Setup-Payload ist kein Objekt, sondern ${Array.isArray(setupRef.version.payload) ? "ein Array" : typeof setupRef.version.payload}.`,
4821
+ errors: []
4656
4822
  }
4657
4823
  };
4658
4824
  }
4659
- const setup = setupRef.version.payload;
4660
- const report = await capabilityReport({
4825
+ setup = {
4826
+ element_id: setupRef.version.element_id,
4827
+ version_id: setupRef.version.version_id,
4828
+ payload: setupRef.version.payload
4829
+ };
4830
+ report = await capabilityReport({
4661
4831
  type: "scene_setup",
4662
- setupPayload: setup,
4663
- elementId: setupRef.version.element_id,
4832
+ setupPayload: setup.payload,
4833
+ elementId: setup.element_id,
4664
4834
  resolver,
4665
4835
  // Bei einer Draft-Kette kommt die Szene von hier, nicht aus `scene_ref`
4666
4836
  // (C1). Dasselbe Regelwerk, ein anderer Weg zur selben Szene.
4667
4837
  ...chain ? { chainScene: chain.scene.payload } : {}
4668
4838
  });
4669
- const gate = capabilityGate(report, "can_render_prompt");
4670
- if (gate) return { kind: "rejected", failure: gate };
4671
- const placeGate = capabilityGate(report, "can_place");
4672
- if (placeGate && placeGate.class === "capability_undetermined") {
4673
- return { kind: "rejected", failure: placeGate };
4674
- }
4675
- const canPlace = report.can_place.state === "yes";
4839
+ diagnostics.push(...report.promptDiagnostics);
4840
+ canPlace = report.can_place.state === "yes";
4676
4841
  if (!canPlace) {
4677
4842
  assumptions.push(
4678
- "can_place ist nicht erfuellt \u2014 alle Platzierungen bleiben null. Der Renderer darf daraus keine Koordinaten ableiten (D-47)."
4843
+ `can_place ist "${report.can_place.state}" \u2014 alle Platzierungen bleiben null. Der Renderer darf daraus keine Koordinaten ableiten (D-47).`
4679
4844
  );
4680
4845
  }
4681
- const sceneRef = chain ? { ok: true, version: chain.scene } : await refOf(resolver, setup.scene_ref, "scene", "/scene_ref");
4846
+ capabilities = {
4847
+ can_describe_scene: report.can_describe_scene.state,
4848
+ can_render_prompt: report.can_render_prompt.state,
4849
+ can_place: report.can_place.state
4850
+ };
4851
+ const sceneRef = chain ? { ok: true, version: chain.scene } : await refOf(resolver, setup.payload.scene_ref, "scene", "/scene_ref");
4682
4852
  if (!sceneRef.ok) return { kind: "rejected", failure: sceneRef.failure };
4683
- const scene = sceneRef.version.payload;
4684
- const cameraId = str(shot.camera_id) ?? "";
4685
- const rig = isObject6(setup.camera_rig) ? setup.camera_rig : {};
4686
- const cameras = asArray2(rig.cameras);
4687
- const cameraIndex = cameras.findIndex(
4688
- (camera2) => isObject6(camera2) && camera2.camera_id === cameraId
4689
- );
4690
- if (cameraIndex < 0) {
4853
+ if (!isObject7(sceneRef.version.payload)) {
4691
4854
  return {
4692
4855
  kind: "rejected",
4693
4856
  failure: {
4694
- class: "reference_unresolvable",
4695
- message: `Die aktive camera_id "${cameraId}" kommt im Rig der Scene-Setup-Version ${setupRef.version.version_id} nicht vor (P-01).`,
4696
- versionId: setupRef.version.version_id,
4697
- path: "/camera_id",
4698
- reason: "unknown"
4857
+ class: "schema",
4858
+ where: "scene",
4859
+ message: `Die Szenen-Payload ist kein Objekt, sondern ${Array.isArray(sceneRef.version.payload) ? "ein Array" : typeof sceneRef.version.payload}.`,
4860
+ errors: []
4699
4861
  }
4700
4862
  };
4701
4863
  }
4702
- const camera = cameras[cameraIndex];
4703
- const presetRef = await refOf(
4864
+ scene = {
4865
+ element_id: sceneRef.version.element_id,
4866
+ version_id: sceneRef.version.version_id,
4867
+ payload: sceneRef.version.payload
4868
+ };
4869
+ return komponiere({
4870
+ shot: { element_id: shotElementId, version_id: shotVersionId, payload: shot },
4871
+ setup,
4872
+ scene,
4873
+ report,
4874
+ capabilities,
4875
+ canPlace,
4704
4876
  resolver,
4705
- camera.preset_ref,
4706
- "camera_preset",
4707
- `/camera_rig/cameras/${cameraIndex}/preset_ref`
4708
- );
4709
- if (!presetRef.ok) return { kind: "rejected", failure: presetRef.failure };
4710
- let pattern = null;
4711
- const placementSpec = isObject6(camera.placement) ? camera.placement : null;
4712
- if (placementSpec && isObject6(placementSpec.pattern_ref)) {
4713
- const patternRef = await refOf(
4714
- resolver,
4715
- placementSpec.pattern_ref,
4716
- "camera_pattern",
4717
- `/camera_rig/cameras/${cameraIndex}/placement/pattern_ref`
4877
+ contextChain,
4878
+ userInstruction,
4879
+ assumptions,
4880
+ diagnostics
4881
+ });
4882
+ }
4883
+ async function resolveScenePayload(input) {
4884
+ const {
4885
+ scenePayload,
4886
+ sceneElementId,
4887
+ sceneVersionId = "",
4888
+ resolver,
4889
+ contextChain,
4890
+ userInstruction
4891
+ } = input;
4892
+ if (!isObject7(scenePayload)) {
4893
+ return {
4894
+ kind: "rejected",
4895
+ failure: {
4896
+ class: "schema",
4897
+ where: "scene",
4898
+ message: `Die Szenen-Payload ist kein Objekt, sondern ${Array.isArray(scenePayload) ? "ein Array" : typeof scenePayload}.`,
4899
+ errors: []
4900
+ }
4901
+ };
4902
+ }
4903
+ const assumptions = [
4904
+ "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)."
4905
+ ];
4906
+ const diagnostics = [];
4907
+ for (const fehler4 of validatePayload("scene", scenePayload).errors) {
4908
+ diagnostics.push(diagnosticOfSchemaError(fehler4, `Szene ${sceneElementId}`));
4909
+ }
4910
+ const faehig = sceneOnlyCapabilities(scenePayload);
4911
+ return komponiere({
4912
+ shot: null,
4913
+ setup: null,
4914
+ scene: { element_id: sceneElementId, version_id: sceneVersionId, payload: scenePayload },
4915
+ report: null,
4916
+ capabilities: {
4917
+ can_describe_scene: faehig.can_describe_scene.state,
4918
+ can_render_prompt: faehig.can_render_prompt.state,
4919
+ can_place: faehig.can_place.state
4920
+ },
4921
+ canPlace: false,
4922
+ resolver,
4923
+ contextChain,
4924
+ userInstruction,
4925
+ assumptions,
4926
+ diagnostics
4927
+ });
4928
+ }
4929
+ async function komponiere(z) {
4930
+ const {
4931
+ shot,
4932
+ setup,
4933
+ scene,
4934
+ report,
4935
+ capabilities,
4936
+ canPlace,
4937
+ resolver,
4938
+ contextChain,
4939
+ userInstruction,
4940
+ assumptions,
4941
+ diagnostics
4942
+ } = z;
4943
+ const inhalt = describableContent(scene.payload);
4944
+ if (!inhalt.ok) {
4945
+ return {
4946
+ kind: "rejected",
4947
+ failure: {
4948
+ class: "no_content",
4949
+ message: `Szene ${scene.element_id}: ${inhalt.message}`
4950
+ }
4951
+ };
4952
+ }
4953
+ const eingefroreneFaehigkeiten = {
4954
+ ...capabilities,
4955
+ can_render_prompt: "yes"
4956
+ };
4957
+ const shotOverrides = shot && isObject7(shot.payload.overrides) ? shot.payload.overrides : {};
4958
+ const settings = shot && isObject7(shot.payload.prompt_render_settings) ? shot.payload.prompt_render_settings : {};
4959
+ let camera = null;
4960
+ if (shot && setup) {
4961
+ const cameraId = str(shot.payload.camera_id);
4962
+ const rig = isObject7(setup.payload.camera_rig) ? setup.payload.camera_rig : {};
4963
+ const cameras = asArray3(rig.cameras);
4964
+ const cameraIndex = cameraId === null ? -1 : cameras.findIndex((c) => isObject7(c) && c.camera_id === cameraId);
4965
+ if (cameraId === null) {
4966
+ ausgelassen(
4967
+ diagnostics,
4968
+ "P-01",
4969
+ "/camera_id",
4970
+ `Der Shot ${shot.element_id} nennt keine camera_id. Der Prompt entsteht ohne Kameravorgabe; eine Kamera wird nicht erfunden (D-75).`
4971
+ );
4972
+ } else if (cameraIndex < 0) {
4973
+ ausgelassen(
4974
+ diagnostics,
4975
+ "P-01",
4976
+ "/camera_id",
4977
+ `Die aktive camera_id "${cameraId}" kommt im Rig der Scene-Setup-Version ${setup.version_id} nicht vor. Der Prompt entsteht ohne Kameravorgabe.`
4978
+ );
4979
+ } else {
4980
+ const rohKamera = cameras[cameraIndex];
4981
+ let preset = null;
4982
+ const presetRef = await refOf(
4983
+ resolver,
4984
+ rohKamera.preset_ref,
4985
+ "camera_preset",
4986
+ `/camera_rig/cameras/${cameraIndex}/preset_ref`
4987
+ );
4988
+ if (presetRef.ok) preset = presetRef.version.payload;
4989
+ else {
4990
+ ausgelassen(
4991
+ diagnostics,
4992
+ "P-04",
4993
+ `/camera_rig/cameras/${cameraIndex}/preset_ref`,
4994
+ `${presetRef.failure.message} Die Kamera geht ohne Preset in den Prompt.`
4995
+ );
4996
+ }
4997
+ let pattern = null;
4998
+ const placementSpec = isObject7(rohKamera.placement) ? rohKamera.placement : null;
4999
+ if (placementSpec && isObject7(placementSpec.pattern_ref)) {
5000
+ const patternRef = await refOf(
5001
+ resolver,
5002
+ placementSpec.pattern_ref,
5003
+ "camera_pattern",
5004
+ `/camera_rig/cameras/${cameraIndex}/placement/pattern_ref`
5005
+ );
5006
+ if (patternRef.ok) pattern = patternRef.version.payload;
5007
+ else {
5008
+ ausgelassen(
5009
+ diagnostics,
5010
+ "P-04",
5011
+ `/camera_rig/cameras/${cameraIndex}/placement/pattern_ref`,
5012
+ `${patternRef.failure.message} Das Kameramuster entfaellt im Prompt.`
5013
+ );
5014
+ }
5015
+ }
5016
+ const cameraPlacement = canPlace ? rohKamera.placement ?? null : entraeumlichtePlatzierung(rohKamera.placement, assumptions);
5017
+ camera = {
5018
+ camera_id: cameraId,
5019
+ name: str(rohKamera.name),
5020
+ preset,
5021
+ placement: cameraPlacement,
5022
+ orientation: rohKamera.orientation ?? null,
5023
+ // Der Shot darf Bildgroesse und Fokus ueberschreiben (D-40) — das ist
5024
+ // die oberste Stufe der Vorrangkette.
5025
+ framing: shotOverrides.framing ?? rohKamera.framing ?? null,
5026
+ focus: shotOverrides.focus !== void 0 ? shotOverrides.focus : rohKamera.focus ?? null,
5027
+ pattern
5028
+ };
5029
+ }
5030
+ } else if (shot && !setup) {
5031
+ ausgelassen(
5032
+ diagnostics,
5033
+ "P-01",
5034
+ "/camera_id",
5035
+ "Ohne Scene Setup gibt es kein Camera Rig. Der Prompt entsteht ohne Kameravorgabe."
4718
5036
  );
4719
- if (!patternRef.ok) return { kind: "rejected", failure: patternRef.failure };
4720
- pattern = patternRef.version.payload;
4721
5037
  }
4722
5038
  let shotConcept = null;
4723
- if (isObject6(shot.shot_concept_ref)) {
5039
+ if (shot && isObject7(shot.payload.shot_concept_ref)) {
4724
5040
  const conceptRef = await refOf(
4725
5041
  resolver,
4726
- shot.shot_concept_ref,
5042
+ shot.payload.shot_concept_ref,
4727
5043
  "shot_concept",
4728
5044
  "/shot_concept_ref"
4729
5045
  );
4730
- if (!conceptRef.ok) return { kind: "rejected", failure: conceptRef.failure };
4731
- shotConcept = conceptRef.version.payload;
5046
+ if (conceptRef.ok) shotConcept = conceptRef.version.payload;
5047
+ else {
5048
+ ausgelassen(
5049
+ diagnostics,
5050
+ "P-04",
5051
+ "/shot_concept_ref",
5052
+ `${conceptRef.failure.message} Das Shot Concept entfaellt im Prompt.`
5053
+ );
5054
+ }
4732
5055
  }
4733
5056
  const styles = [];
4734
5057
  for (const [ebene, refs] of [
4735
- ["scene_setup", asArray2(setup.style_refs)],
4736
- ["shot", asArray2(shot.style_refs)]
5058
+ ["scene_setup", setup ? asArray3(setup.payload.style_refs) : []],
5059
+ ["shot", shot ? asArray3(shot.payload.style_refs) : []]
4737
5060
  ]) {
4738
5061
  for (const [i, ref] of refs.entries()) {
4739
5062
  const resolved = await refOf(resolver, ref, "style", `/${ebene}/style_refs/${i}`);
4740
- if (!resolved.ok) return { kind: "rejected", failure: resolved.failure };
5063
+ if (!resolved.ok) {
5064
+ ausgelassen(
5065
+ diagnostics,
5066
+ "P-04",
5067
+ `/${ebene}/style_refs/${i}`,
5068
+ `${resolved.failure.message} Dieser Stil entfaellt im Prompt.`
5069
+ );
5070
+ continue;
5071
+ }
4741
5072
  const payload = resolved.version.payload;
4742
5073
  styles.push({
4743
- role: str(isObject6(ref) ? ref.role : null) ?? str(payload.role) ?? "unspecified",
4744
- rank: num(isObject6(ref) ? ref.rank : null) ?? 0,
4745
- weight: num(isObject6(ref) ? ref.weight : null),
5074
+ role: str(isObject7(ref) ? ref.role : null) ?? str(payload.role) ?? "unspecified",
5075
+ rank: num(isObject7(ref) ? ref.rank : null) ?? 0,
5076
+ weight: num(isObject7(ref) ? ref.weight : null),
4746
5077
  text: str(payload.text) ?? "",
4747
5078
  locale: str(payload.locale)
4748
5079
  });
4749
5080
  }
4750
5081
  }
4751
5082
  styles.sort((a, b) => a.rank - b.rank);
4752
- const settings = isObject6(shot.prompt_render_settings) ? shot.prompt_render_settings : {};
4753
5083
  const policies = [];
4754
- for (const [i, ref] of asArray2(settings.policy_refs).entries()) {
4755
- const resolved = await refOf(resolver, ref, "prompt_policy", `/prompt_render_settings/policy_refs/${i}`);
4756
- if (!resolved.ok) return { kind: "rejected", failure: resolved.failure };
5084
+ for (const [i, ref] of asArray3(settings.policy_refs).entries()) {
5085
+ const resolved = await refOf(
5086
+ resolver,
5087
+ ref,
5088
+ "prompt_policy",
5089
+ `/prompt_render_settings/policy_refs/${i}`
5090
+ );
5091
+ if (!resolved.ok) {
5092
+ ausgelassen(
5093
+ diagnostics,
5094
+ "P-04",
5095
+ `/prompt_render_settings/policy_refs/${i}`,
5096
+ `${resolved.failure.message} Diese Policy entfaellt im Prompt.`
5097
+ );
5098
+ continue;
5099
+ }
4757
5100
  const payload = resolved.version.payload;
4758
5101
  policies.push({
4759
5102
  subtype: str(payload.subtype) ?? "general",
@@ -4764,14 +5107,22 @@ async function resolveShotPayload(input) {
4764
5107
  }
4765
5108
  policies.sort((a, b) => a.rank - b.rank);
4766
5109
  const fragments = [];
4767
- for (const [i, ref] of asArray2(settings.fragment_refs).entries()) {
5110
+ for (const [i, ref] of asArray3(settings.fragment_refs).entries()) {
4768
5111
  const resolved = await refOf(
4769
5112
  resolver,
4770
5113
  ref,
4771
5114
  "prompt_fragment",
4772
5115
  `/prompt_render_settings/fragment_refs/${i}`
4773
5116
  );
4774
- if (!resolved.ok) return { kind: "rejected", failure: resolved.failure };
5117
+ if (!resolved.ok) {
5118
+ ausgelassen(
5119
+ diagnostics,
5120
+ "P-04",
5121
+ `/prompt_render_settings/fragment_refs/${i}`,
5122
+ `${resolved.failure.message} Dieses Fragment entfaellt im Prompt.`
5123
+ );
5124
+ continue;
5125
+ }
4775
5126
  const payload = resolved.version.payload;
4776
5127
  fragments.push({
4777
5128
  channel: str(payload.channel) ?? "positive",
@@ -4781,83 +5132,102 @@ async function resolveShotPayload(input) {
4781
5132
  });
4782
5133
  }
4783
5134
  fragments.sort((a, b) => a.rank - b.rank);
4784
- const key = str(settings.target_model_id);
4785
- if (!key) {
4786
- return {
4787
- kind: "rejected",
4788
- failure: {
4789
- class: "target_model_unresolvable",
4790
- message: "Der Shot nennt kein target_model_id.",
4791
- resolution: { kind: "not_found", key: "", chain: contextChain }
4792
- }
4793
- };
4794
- }
4795
- if (!resolver.resolveTargetModel) {
5135
+ let targetModelPayload = null;
5136
+ let targetModelResolution = null;
5137
+ const key = shot ? str(settings.target_model_id) : null;
5138
+ if (key === null) {
5139
+ ausgelassen(
5140
+ diagnostics,
5141
+ "P-17",
5142
+ "/prompt_render_settings/target_model_id",
5143
+ shot ? `Der Shot ${shot.element_id} nennt kein target_model_id. Der Prompt wird generisch abgeleitet, ohne Modelloptimierung.` : "Ohne Shot gibt es kein target_model_id. Der Prompt wird generisch abgeleitet, ohne Modelloptimierung."
5144
+ );
5145
+ } else if (!resolver.resolveTargetModel) {
4796
5146
  throw new ResolveTechnicalError(
4797
5147
  `Der Resolver bietet keine Zielmodellsuche an (kein ContextChainPort). Ein Lauf ohne Kontextkette wuerde ein Zielmodell melden, das nicht das gesuchte ist.`
4798
5148
  );
4799
- }
4800
- const targetModel = await resolver.resolveTargetModel(key, shotVersion.element_id);
4801
- if (targetModel.kind !== "resolved") {
4802
- return {
4803
- kind: "rejected",
4804
- failure: {
4805
- class: "target_model_unresolvable",
4806
- message: describeTargetModelFailure(targetModel),
4807
- resolution: targetModel
5149
+ } else {
5150
+ const targetModel = await resolver.resolveTargetModel(key, shot.element_id);
5151
+ if (targetModel.kind !== "resolved") {
5152
+ ausgelassen(
5153
+ diagnostics,
5154
+ "P-17",
5155
+ "/prompt_render_settings/target_model_id",
5156
+ `${describeTargetModelFailure(targetModel)} Der Prompt wird generisch abgeleitet, ohne Modelloptimierung.`
5157
+ );
5158
+ } else {
5159
+ targetModelPayload = targetModel.payload;
5160
+ targetModelResolution = {
5161
+ element_id: targetModel.elementId,
5162
+ version_id: targetModel.versionId,
5163
+ chain_index: targetModel.chainIndex,
5164
+ head: targetModel.head,
5165
+ shadowed_retired: targetModel.shadowedRetired.map((entry) => ({
5166
+ element_id: entry.elementId,
5167
+ context_id: entry.contextId
5168
+ }))
5169
+ };
5170
+ for (const uebergangen of targetModel.shadowedRetired) {
5171
+ assumptions.push(
5172
+ `Ein stillgelegtes target_model (${uebergangen.elementId}, Kontext ${uebergangen.contextId ?? "global"}) wurde uebergangen; verwendet wird ${targetModel.elementId} von Kettenglied ${targetModel.chainIndex}.`
5173
+ );
4808
5174
  }
4809
- };
4810
- }
4811
- for (const uebergangen of targetModel.shadowedRetired) {
4812
- assumptions.push(
4813
- `Ein stillgelegtes target_model (${uebergangen.elementId}, Kontext ${uebergangen.contextId ?? "global"}) wurde uebergangen; verwendet wird ${targetModel.elementId} von Kettenglied ${targetModel.chainIndex}.`
4814
- );
4815
- }
4816
- if (targetModel.head.kind === "selected" && targetModel.head.stale) {
4817
- assumptions.push(
4818
- `Die Auswahl des Zielmodell-Heads (${targetModel.versionId}) ist veraltet \u2014 es gibt neuere Heads. Die Auswahl gilt, weil sie getroffen wurde (D-36).`
4819
- );
5175
+ if (targetModel.head.kind === "selected" && targetModel.head.stale) {
5176
+ assumptions.push(
5177
+ `Die Auswahl des Zielmodell-Heads (${targetModel.versionId}) ist veraltet \u2014 es gibt neuere Heads. Die Auswahl gilt, weil sie getroffen wurde (D-36).`
5178
+ );
5179
+ }
5180
+ }
4820
5181
  }
4821
5182
  const overrides = /* @__PURE__ */ new Map();
4822
- for (const entry of asArray2(setup.node_overrides)) {
4823
- if (isObject6(entry) && typeof entry.node_id === "string") overrides.set(entry.node_id, entry);
5183
+ for (const entry of setup ? asArray3(setup.payload.node_overrides) : []) {
5184
+ if (isObject7(entry) && typeof entry.node_id === "string") overrides.set(entry.node_id, entry);
4824
5185
  }
4825
5186
  const placements = /* @__PURE__ */ new Map();
4826
- for (const entry of asArray2(setup.placements)) {
4827
- if (!isObject6(entry) || typeof entry.node_id !== "string") continue;
5187
+ for (const entry of setup ? asArray3(setup.payload.placements) : []) {
5188
+ if (!isObject7(entry) || typeof entry.node_id !== "string") continue;
4828
5189
  placements.set(entry.node_id, {
4829
- position: asArray2(entry.position),
4830
- ...entry.rotation_euler_deg ? { rotation_euler_deg: asArray2(entry.rotation_euler_deg) } : {},
4831
- ...entry.scale ? { scale: asArray2(entry.scale) } : {}
5190
+ position: asArray3(entry.position),
5191
+ ...entry.rotation_euler_deg ? { rotation_euler_deg: asArray3(entry.rotation_euler_deg) } : {},
5192
+ ...entry.scale ? { scale: asArray3(entry.scale) } : {}
4832
5193
  });
4833
5194
  }
4834
- const shotOverrides = isObject6(shot.overrides) ? shot.overrides : {};
4835
- const hidden = new Set(asArray2(shotOverrides.hidden_nodes).filter((v) => typeof v === "string"));
5195
+ const hidden = new Set(
5196
+ asArray3(shotOverrides.hidden_nodes).filter((v) => typeof v === "string")
5197
+ );
4836
5198
  const nodes = [];
4837
- for (const raw of asArray2(scene.nodes)) {
4838
- if (!isObject6(raw) || typeof raw.node_id !== "string") continue;
5199
+ for (const raw of asArray3(scene.payload.nodes)) {
5200
+ if (!isObject7(raw) || typeof raw.node_id !== "string") continue;
4839
5201
  const nodeId = raw.node_id;
4840
5202
  const contributions = [];
4841
- for (const [i, ref] of asArray2(raw.component_refs).entries()) {
5203
+ for (const [i, ref] of asArray3(raw.component_refs).entries()) {
4842
5204
  const resolved = await refOf(
4843
5205
  resolver,
4844
5206
  ref,
4845
5207
  "scene_component",
4846
5208
  `/nodes/${nodeId}/component_refs/${i}`
4847
5209
  );
4848
- if (!resolved.ok) return { kind: "rejected", failure: resolved.failure };
5210
+ if (!resolved.ok) {
5211
+ ausgelassen(
5212
+ diagnostics,
5213
+ "P-05",
5214
+ `/nodes/${nodeId}/component_refs/${i}`,
5215
+ `${resolved.failure.message} Dieser Beitrag entfaellt; der Knoten bleibt mit seinen uebrigen Beitraegen im Prompt.`
5216
+ );
5217
+ continue;
5218
+ }
4849
5219
  const payload = resolved.version.payload;
4850
5220
  contributions.push({
4851
5221
  source: "component",
4852
5222
  element_id: resolved.version.element_id,
4853
5223
  version_id: resolved.version.version_id,
4854
- role: str(isObject6(ref) ? ref.role : null),
4855
- rank: num(isObject6(ref) ? ref.rank : null) ?? 0,
5224
+ role: str(isObject7(ref) ? ref.role : null),
5225
+ rank: num(isObject7(ref) ? ref.rank : null) ?? 0,
4856
5226
  locale: str(payload.locale),
4857
5227
  text: str(payload.text) ?? ""
4858
5228
  });
4859
5229
  }
4860
- const semantic = isObject6(raw.semantic) ? raw.semantic : null;
5230
+ const semantic = isObject7(raw.semantic) ? raw.semantic : null;
4861
5231
  if (semantic && str(semantic.description)) {
4862
5232
  contributions.push({
4863
5233
  source: "node_semantic",
@@ -4871,12 +5241,12 @@ async function resolveShotPayload(input) {
4871
5241
  });
4872
5242
  }
4873
5243
  const override = overrides.get(nodeId);
4874
- const overrideSemantic = override && isObject6(override.semantic) ? override.semantic : null;
4875
- if (overrideSemantic && str(overrideSemantic.description)) {
5244
+ const overrideSemantic = override && isObject7(override.semantic) ? override.semantic : null;
5245
+ if (overrideSemantic && str(overrideSemantic.description) && setup) {
4876
5246
  contributions.push({
4877
5247
  source: "setup_override",
4878
- element_id: setupRef.version.element_id,
4879
- version_id: setupRef.version.version_id,
5248
+ element_id: setup.element_id,
5249
+ version_id: setup.version_id,
4880
5250
  role: null,
4881
5251
  rank: Number.MAX_SAFE_INTEGER,
4882
5252
  locale: null,
@@ -4885,8 +5255,8 @@ async function resolveShotPayload(input) {
4885
5255
  }
4886
5256
  contributions.sort((a, b) => a.rank - b.rank);
4887
5257
  const properties = {
4888
- ...isObject6(semantic?.properties) ? semantic.properties : {},
4889
- ...isObject6(overrideSemantic?.properties) ? overrideSemantic.properties : {}
5258
+ ...isObject7(semantic?.properties) ? semantic.properties : {},
5259
+ ...isObject7(overrideSemantic?.properties) ? overrideSemantic.properties : {}
4890
5260
  };
4891
5261
  nodes.push({
4892
5262
  node_id: nodeId,
@@ -4897,9 +5267,6 @@ async function resolveShotPayload(input) {
4897
5267
  hidden: hidden.has(nodeId)
4898
5268
  });
4899
5269
  }
4900
- const cameraPlacement = canPlace ? camera.placement ?? null : entraeumlichtePlatzierung(camera.placement, assumptions);
4901
- const framing = shotOverrides.framing ?? camera.framing;
4902
- const focus = shotOverrides.focus !== void 0 ? shotOverrides.focus : camera.focus ?? null;
4903
5270
  const shotInstruction = str(settings.user_instruction);
4904
5271
  if (userInstruction && shotInstruction && userInstruction !== shotInstruction) {
4905
5272
  assumptions.push(
@@ -4913,56 +5280,37 @@ async function resolveShotPayload(input) {
4913
5280
  kind: "resolved",
4914
5281
  report,
4915
5282
  input: {
4916
- shot: {
4917
- version_id: shotVersion.version_id,
4918
- element_id: shotVersion.element_id,
4919
- camera_id: cameraId,
5283
+ shot: shot ? {
5284
+ version_id: shot.version_id,
5285
+ element_id: shot.element_id,
5286
+ camera_id: camera ? camera.camera_id : null,
4920
5287
  shot_concept: shotConcept,
4921
5288
  // Der Aufrufer darf die im Shot hinterlegte Anweisung ueberschreiben
4922
5289
  // (§4.1). Beide Herkuenfte sind unterschiedliche Sachverhalte, deshalb
4923
5290
  // wird der Fall in `assumptions` benannt statt still ersetzt.
4924
- user_instruction: userInstruction ?? str(settings.user_instruction),
5291
+ user_instruction: userInstruction ?? shotInstruction,
4925
5292
  text_override: str(shotOverrides.text)
4926
- },
5293
+ } : null,
5294
+ setup: setup ? { version_id: setup.version_id, element_id: setup.element_id } : null,
4927
5295
  scene: {
4928
- version_id: sceneRef.version.version_id,
5296
+ element_id: scene.element_id,
5297
+ version_id: scene.version_id,
4929
5298
  nodes,
4930
- relations: asArray2(scene.relations),
4931
- environment: scene.environment ?? null,
4932
- unit_system: scene.unit_system ?? null
4933
- },
4934
- camera: {
4935
- camera_id: cameraId,
4936
- name: str(camera.name),
4937
- preset: presetRef.version.payload,
4938
- placement: cameraPlacement,
4939
- orientation: camera.orientation ?? null,
4940
- framing: framing ?? null,
4941
- focus,
4942
- pattern
5299
+ relations: asArray3(scene.payload.relations),
5300
+ environment: scene.payload.environment ?? null,
5301
+ unit_system: scene.payload.unit_system ?? null
4943
5302
  },
5303
+ camera,
4944
5304
  styles,
4945
5305
  policies,
4946
5306
  fragments,
4947
- target_model: targetModel.payload,
5307
+ target_model: targetModelPayload,
4948
5308
  localization_notes: [],
4949
5309
  assumptions,
4950
- capabilities: {
4951
- can_describe_scene: report.can_describe_scene.state,
4952
- can_render_prompt: report.can_render_prompt.state,
4953
- can_place: report.can_place.state
4954
- },
5310
+ diagnostics,
5311
+ capabilities: eingefroreneFaehigkeiten,
4955
5312
  context_chain: [...contextChain],
4956
- target_model_resolution: {
4957
- element_id: targetModel.elementId,
4958
- version_id: targetModel.versionId,
4959
- chain_index: targetModel.chainIndex,
4960
- head: targetModel.head,
4961
- shadowed_retired: targetModel.shadowedRetired.map((entry) => ({
4962
- element_id: entry.elementId,
4963
- context_id: entry.contextId
4964
- }))
4965
- }
5313
+ target_model_resolution: targetModelResolution
4966
5314
  }
4967
5315
  };
4968
5316
  }
@@ -5105,6 +5453,7 @@ function createFlowTextGenerationPort(generateText, options = {}) {
5105
5453
 
5106
5454
  // src/lib/szenen/promptRun.ts
5107
5455
  function buildSystemInstruction(input) {
5456
+ const ohneZielmodell = input.target_model === null || input.target_model === void 0;
5108
5457
  const model = input.target_model ?? {};
5109
5458
  const sprache = typeof model.prompt_language === "string" ? model.prompt_language : "en";
5110
5459
  const negativ = model.supports_negative_prompt === true;
@@ -5125,7 +5474,11 @@ function buildSystemInstruction(input) {
5125
5474
  "",
5126
5475
  `Der Prompt wird in der Sprache "${sprache}" formuliert.`
5127
5476
  ];
5128
- if (!negativ) {
5477
+ if (ohneZielmodell) {
5478
+ zeilen.push(
5479
+ 'Es ist KEIN Zielmodell angegeben. Formuliere einen generischen Prompt und richte ihn nicht an einer bestimmten Bildmodell-Syntax aus. "negative" bleibt null, weil kein Modell zugesagt hat, ein Negativfeld zu fuehren; was mit negativen Fragmenten geschehen ist, gehoert in "notes".'
5480
+ );
5481
+ } else if (!negativ) {
5129
5482
  zeilen.push(
5130
5483
  'Das Zielmodell kennt KEIN Negativ-Prompt-Feld. "negative" bleibt null; was mit negativen Fragmenten geschehen ist, gehoert in "notes".'
5131
5484
  );
@@ -5154,6 +5507,14 @@ function buildSystemInstruction(input) {
5154
5507
  'Widersprechen sie einander, nenne den Widerspruch mit beiden Quellen in "notes" \u2014',
5155
5508
  "waehle nicht still eine Seite."
5156
5509
  );
5510
+ if (input.camera === null) {
5511
+ zeilen.push(
5512
+ "",
5513
+ "Es ist KEINE Kamera angegeben. Erfinde keine Brennweite, keinen Bildausschnitt",
5514
+ "und keine Kameraposition. Wo eine Kameraaussage noetig waere, formuliere sie",
5515
+ 'als Annahme und nenne sie in "notes".'
5516
+ );
5517
+ }
5157
5518
  if (input.capabilities.can_place !== "yes") {
5158
5519
  zeilen.push(
5159
5520
  "",
@@ -5167,7 +5528,7 @@ function buildSystemInstruction(input) {
5167
5528
  function buildUserPrompt(input) {
5168
5529
  return JSON.stringify(input, null, 2);
5169
5530
  }
5170
- var isObject7 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
5531
+ var isObject8 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
5171
5532
  function parseRunResponse(raw) {
5172
5533
  const text = raw.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "").trim();
5173
5534
  if (text.length === 0) return { ok: false, message: "Die Antwort war leer." };
@@ -5181,7 +5542,7 @@ function parseRunResponse(raw) {
5181
5542
  }
5182
5543
  }
5183
5544
  function checkRunResponse(value, input) {
5184
- if (!isObject7(value)) {
5545
+ if (!isObject8(value)) {
5185
5546
  return { ok: false, message: `Die Antwort ist kein Objekt, sondern ${Array.isArray(value) ? "array" : typeof value}.` };
5186
5547
  }
5187
5548
  if (typeof value.positive !== "string" || value.positive.trim().length === 0) {
@@ -5199,11 +5560,11 @@ function checkRunResponse(value, input) {
5199
5560
  if (negative !== null && !negativErlaubt) {
5200
5561
  return {
5201
5562
  ok: false,
5202
- message: `Das Zielmodell fuehrt kein Negativ-Prompt-Feld (supports_negative_prompt = false), die Antwort liefert aber eines.`
5563
+ message: input.target_model ? `Das Zielmodell fuehrt kein Negativ-Prompt-Feld (supports_negative_prompt = false), die Antwort liefert aber eines.` : `Dieser Lauf hat kein Zielmodell (generische Ableitung); ein Negativ-Prompt-Feld ist damit nicht zugesagt, die Antwort liefert aber eines.`
5203
5564
  };
5204
5565
  }
5205
5566
  const visibility = value.visibility;
5206
- if (!isObject7(visibility)) {
5567
+ if (!isObject8(visibility)) {
5207
5568
  return { ok: false, message: '"visibility" fehlt \u2014 die Pflichtdiagnosen aus D-21.' };
5208
5569
  }
5209
5570
  if (typeof visibility.basis !== "string") {
@@ -5215,7 +5576,7 @@ function checkRunResponse(value, input) {
5215
5576
  const genannt = /* @__PURE__ */ new Set();
5216
5577
  const nodes = [];
5217
5578
  for (const eintrag of visibility.nodes) {
5218
- if (!isObject7(eintrag) || typeof eintrag.node_id !== "string" || typeof eintrag.state !== "string") {
5579
+ if (!isObject8(eintrag) || typeof eintrag.node_id !== "string" || typeof eintrag.state !== "string") {
5219
5580
  return { ok: false, message: 'Ein Eintrag in "visibility.nodes" hat keine node_id oder keinen state.' };
5220
5581
  }
5221
5582
  genannt.add(eintrag.node_id);
@@ -6109,7 +6470,7 @@ function validateDerivationInput(input) {
6109
6470
  if (input.policy.produces.length === 0) return "Die Regel erzeugt keinen einzigen Vorschlag.";
6110
6471
  return null;
6111
6472
  }
6112
- var isObject8 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
6473
+ var isObject9 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
6113
6474
  var MAX_PROPOSAL_TITLE_LENGTH = 120;
6114
6475
  function fallbackProposalTitle(type) {
6115
6476
  return `Rekonstruktion ${type}`;
@@ -6135,7 +6496,7 @@ function resolveJsonPointer(wurzel, pointer) {
6135
6496
  aktuell = aktuell[index];
6136
6497
  continue;
6137
6498
  }
6138
- if (!isObject8(aktuell) || !(schluessel in aktuell)) {
6499
+ if (!isObject9(aktuell) || !(schluessel in aktuell)) {
6139
6500
  return { found: false, value: void 0 };
6140
6501
  }
6141
6502
  aktuell = aktuell[schluessel];
@@ -6149,7 +6510,7 @@ function collectVersionRefs2(value) {
6149
6510
  knoten.forEach(besuchen);
6150
6511
  return;
6151
6512
  }
6152
- if (!isObject8(knoten)) return;
6513
+ if (!isObject9(knoten)) return;
6153
6514
  if (typeof knoten.element_id === "string" && typeof knoten.version_id === "string") {
6154
6515
  gefunden.push({ element_id: knoten.element_id, version_id: knoten.version_id });
6155
6516
  }
@@ -6215,7 +6576,7 @@ function assessDerivationResponse(value, input) {
6215
6576
  envelope: null,
6216
6577
  contract_ok: false
6217
6578
  });
6218
- if (!isObject8(value)) {
6579
+ if (!isObject9(value)) {
6219
6580
  befund({
6220
6581
  code: "response.not_object",
6221
6582
  severity: "error",
@@ -6233,7 +6594,7 @@ function assessDerivationResponse(value, input) {
6233
6594
  });
6234
6595
  }
6235
6596
  const rohProposals = value.proposals;
6236
- const proposalsLesbar = isObject8(rohProposals);
6597
+ const proposalsLesbar = isObject9(rohProposals);
6237
6598
  if (!proposalsLesbar) {
6238
6599
  befund({
6239
6600
  code: "envelope.proposals_missing",
@@ -6243,7 +6604,7 @@ function assessDerivationResponse(value, input) {
6243
6604
  });
6244
6605
  }
6245
6606
  const roh = proposalsLesbar ? rohProposals : {};
6246
- const rohGaps = isObject8(value.gaps) ? value.gaps : {};
6607
+ const rohGaps = isObject9(value.gaps) ? value.gaps : {};
6247
6608
  const gapsUngefragt = Object.keys(rohGaps).filter(
6248
6609
  (t) => !input.policy.produces.includes(t)
6249
6610
  );
@@ -6255,7 +6616,7 @@ function assessDerivationResponse(value, input) {
6255
6616
  pointer: "/gaps"
6256
6617
  });
6257
6618
  }
6258
- const fehlend = input.policy.produces.filter((t) => !isObject8(roh[t]));
6619
+ const fehlend = input.policy.produces.filter((t) => !isObject9(roh[t]));
6259
6620
  if (fehlend.length > 0 && proposalsLesbar) {
6260
6621
  const d = befund({
6261
6622
  code: "envelope.proposals_missing_types",
@@ -6276,10 +6637,10 @@ function assessDerivationResponse(value, input) {
6276
6637
  pointer: "/proposals"
6277
6638
  });
6278
6639
  }
6279
- const rohTitles = isObject8(value.titles) ? value.titles : {};
6640
+ const rohTitles = isObject9(value.titles) ? value.titles : {};
6280
6641
  const umgebucht = [];
6281
6642
  for (const type of input.policy.produces) {
6282
- if (!isObject8(roh[type])) continue;
6643
+ if (!isObject9(roh[type])) continue;
6283
6644
  const payload = roh[type];
6284
6645
  const eigene = [];
6285
6646
  const bezug = REFERENCE_FIELD[type];
@@ -6315,7 +6676,7 @@ function assessDerivationResponse(value, input) {
6315
6676
  const bezugPointer = bezug ? `/${bezug.field}` : null;
6316
6677
  let gapFehler = false;
6317
6678
  for (const eintrag of benannt) {
6318
- if (!isObject8(eintrag) || typeof eintrag.pointer !== "string" || typeof eintrag.reason !== "string" || !eintrag.reason.trim()) {
6679
+ if (!isObject9(eintrag) || typeof eintrag.pointer !== "string" || typeof eintrag.reason !== "string" || !eintrag.reason.trim()) {
6319
6680
  eigene.push(befund({
6320
6681
  code: "proposal.gap_entry_invalid",
6321
6682
  severity: "error",
@@ -6391,7 +6752,7 @@ function assessDerivationResponse(value, input) {
6391
6752
  });
6392
6753
  } else {
6393
6754
  for (const eintrag of value.not_derivable) {
6394
- if (!isObject8(eintrag) || typeof eintrag.field !== "string" || typeof eintrag.reason !== "string") {
6755
+ if (!isObject9(eintrag) || typeof eintrag.field !== "string" || typeof eintrag.reason !== "string") {
6395
6756
  befund({
6396
6757
  code: "envelope.not_derivable_entry_invalid",
6397
6758
  severity: "error",
@@ -6416,7 +6777,7 @@ function assessDerivationResponse(value, input) {
6416
6777
  });
6417
6778
  } else {
6418
6779
  for (const eintrag of value.conflicts) {
6419
- if (!isObject8(eintrag) || typeof eintrag.field !== "string" || typeof eintrag.from_image !== "string" || typeof eintrag.from_prompt !== "string") {
6780
+ if (!isObject9(eintrag) || typeof eintrag.field !== "string" || typeof eintrag.from_image !== "string" || typeof eintrag.from_prompt !== "string") {
6420
6781
  befund({
6421
6782
  code: "envelope.conflicts_entry_invalid",
6422
6783
  severity: "error",
@@ -6641,7 +7002,7 @@ var CHAIN_EDGES = {
6641
7002
  scene_ref: { from: "scene_setup", to: "scene" },
6642
7003
  scene_setup_ref: { from: "shot", to: "scene_setup" }
6643
7004
  };
6644
- function istText(wert) {
7005
+ function istText2(wert) {
6645
7006
  return typeof wert === "string" && wert.trim().length > 0;
6646
7007
  }
6647
7008
  function checkDraftChainMeta(meta, type) {
@@ -6652,14 +7013,14 @@ function checkDraftChainMeta(meta, type) {
6652
7013
  };
6653
7014
  }
6654
7015
  const m = meta;
6655
- if (!istText(m.chain_id)) return { ok: false, message: '"chain_id" fehlt oder ist leer.' };
6656
- if (!istText(m.source_run_id)) {
7016
+ if (!istText2(m.chain_id)) return { ok: false, message: '"chain_id" fehlt oder ist leer.' };
7017
+ if (!istText2(m.source_run_id)) {
6657
7018
  return {
6658
7019
  ok: false,
6659
7020
  message: '"source_run_id" fehlt oder ist leer \u2014 ohne sie ist die Herkunft eines Kettenknotens nicht rekonstruierbar (D-96/B3).'
6660
7021
  };
6661
7022
  }
6662
- if (m.source_recovery_id !== void 0 && !istText(m.source_recovery_id)) {
7023
+ if (m.source_recovery_id !== void 0 && !istText2(m.source_recovery_id)) {
6663
7024
  return { ok: false, message: '"source_recovery_id" ist gesetzt, aber leer.' };
6664
7025
  }
6665
7026
  if (!Array.isArray(m.links)) {
@@ -6684,7 +7045,7 @@ function checkDraftChainMeta(meta, type) {
6684
7045
  message: `"${l.field}" gehoert an einen ${kante.from}, nicht an einen ${type}.`
6685
7046
  };
6686
7047
  }
6687
- if (!istText(l.target_element_id) || !istText(l.target_draft_id)) {
7048
+ if (!istText2(l.target_element_id) || !istText2(l.target_draft_id)) {
6688
7049
  return {
6689
7050
  ok: false,
6690
7051
  message: `"links[${index}]" braucht target_element_id und target_draft_id.`
@@ -6739,42 +7100,199 @@ function linkFor(meta, field) {
6739
7100
  return meta?.links.find((l) => l.field === field) ?? null;
6740
7101
  }
6741
7102
 
6742
- // src/lib/szenen/freezeDraftChain.ts
6743
- function ladeKnoten(sicht, elementId, erwarteterTyp) {
6744
- const gelesen = sicht.getElement(elementId);
6745
- if (gelesen.kind !== "ok") {
6746
- return {
6747
- fehler: gelesen.message ?? `Element ${elementId} konnte nicht gelesen werden.`,
6748
- technisch: true
6749
- };
6750
- }
6751
- const snapshot = gelesen.value;
6752
- if (!snapshot) return { fehler: `Element ${elementId} ist unbekannt.` };
6753
- if (snapshot.element.type !== erwarteterTyp) {
6754
- return {
6755
- fehler: `Element ${elementId} ist vom Typ "${snapshot.element.type}", erwartet "${erwarteterTyp}".`
6756
- };
6757
- }
6758
- if (snapshot.draftFork) {
6759
- return {
6760
- fehler: `Element ${elementId} hat parallele Entwuerfe. Solange sie nicht aufgeloest sind, gibt es keinen eindeutigen Kettenknoten (D-29).`
6761
- };
6762
- }
6763
- if (!snapshot.draft) {
6764
- return { fehler: `Element ${elementId} hat keinen Entwurf.` };
6765
- }
6766
- if (!snapshot.draft.chain) {
6767
- return {
6768
- fehler: `Der Entwurf von Element ${elementId} gehoert zu keiner Draft-Kette.`
6769
- };
6770
- }
7103
+ // src/lib/szenen/contextVisibility.ts
7104
+ var isObject10 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
7105
+ function zerlegeKette(contextChain) {
6771
7106
  return {
6772
- type: erwarteterTyp,
6773
- element_id: elementId,
6774
- draft_id: snapshot.draft.draft_id,
6775
- revision: snapshot.draft.revision,
6776
- payload: snapshot.draft.payload,
6777
- chain: snapshot.draft.chain
7107
+ // Das erste Glied unveraendert, `null` eingeschlossen: `null` heisst
7108
+ // kontextlos, nicht „egal" — der Typ von `workingContextId` sagt das
7109
+ // ausdruecklich.
7110
+ workingContextId: contextChain.length > 0 ? contextChain[0] : null,
7111
+ // Gespeicherte Auswahlen tragen nie `null` als Kontext
7112
+ // (`SetPreferredHeadInput.contextId` laeuft durch `checkRequiredId`), das
7113
+ // Schlussglied der Kette kann hier also nichts treffen.
7114
+ inheritanceChain: contextChain.slice(1).filter((link) => typeof link === "string" && link.length > 0)
7115
+ };
7116
+ }
7117
+ function effectivePreferredHead(snapshot, contextChain) {
7118
+ const versionen = snapshot.versions ?? [];
7119
+ if (versionen.length === 0) return null;
7120
+ const { workingContextId, inheritanceChain } = zerlegeKette(contextChain);
7121
+ return resolvePreferredHead({
7122
+ dag: buildVersionDag(versionen),
7123
+ workingContextId,
7124
+ inheritanceChain,
7125
+ owningContextId: snapshot.element.context_id ?? void 0,
7126
+ selections: snapshot.preferredHeads ?? []
7127
+ });
7128
+ }
7129
+ function effectivePreferredHeadSafe(snapshot, contextChain) {
7130
+ try {
7131
+ const outcome = effectivePreferredHead(snapshot, contextChain);
7132
+ return outcome ? { kind: "aufgeloest", outcome } : { kind: "keine_version" };
7133
+ } catch (error) {
7134
+ if (error instanceof VersionDagError) {
7135
+ return { kind: "dag_fehler", message: error.message };
7136
+ }
7137
+ throw error;
7138
+ }
7139
+ }
7140
+ function resolutionOf(result) {
7141
+ return result && result.kind === "aufgeloest" ? result.outcome.resolution : null;
7142
+ }
7143
+ function selectedHeadVersionId(resolution) {
7144
+ return resolution && resolution.kind === "selected" ? resolution.versionId : null;
7145
+ }
7146
+ function usableHeadVersionId(resolution) {
7147
+ if (!resolution) return null;
7148
+ if (resolution.kind === "selected" || resolution.kind === "single") return resolution.versionId;
7149
+ return null;
7150
+ }
7151
+ function preferredHeadRevisionFor(snapshot, contextId) {
7152
+ const auswahl = (snapshot.preferredHeads ?? []).find((h) => h.context_id === contextId);
7153
+ return auswahl?.revision ?? 0;
7154
+ }
7155
+ async function visibleElementsAlongChain(queries, input) {
7156
+ const gesehen = /* @__PURE__ */ new Set();
7157
+ const gefunden = [];
7158
+ const kette = input.contextChain.length > 0 ? input.contextChain : [null];
7159
+ for (const link of kette) {
7160
+ const liste = await queries.listElements({ contextId: link, types: input.types });
7161
+ if (liste.kind !== "ok") return liste;
7162
+ for (const element of liste.value) {
7163
+ if (gesehen.has(element.element_id)) continue;
7164
+ gesehen.add(element.element_id);
7165
+ gefunden.push(element);
7166
+ }
7167
+ }
7168
+ return { kind: "ok", value: gefunden };
7169
+ }
7170
+ async function selectableTargetModels(queries, contextChain) {
7171
+ const selectable = [];
7172
+ const unselectable = [];
7173
+ const entschieden = /* @__PURE__ */ new Set();
7174
+ const kette = contextChain.length > 0 ? contextChain : [null];
7175
+ for (const [chainIndex, link] of kette.entries()) {
7176
+ const liste = await queries.listElements({ contextId: link, type: "target_model" });
7177
+ if (liste.kind !== "ok") return liste;
7178
+ const trefferJeKey = /* @__PURE__ */ new Map();
7179
+ const merke = (key, treffer) => {
7180
+ trefferJeKey.set(key, [...trefferJeKey.get(key) ?? [], treffer]);
7181
+ };
7182
+ for (const element of liste.value) {
7183
+ const detail = await queries.getElement(element.element_id);
7184
+ if (detail.kind !== "ok") return detail;
7185
+ const snapshot = detail.value;
7186
+ if (!snapshot) continue;
7187
+ const titel = snapshot.metadata?.title ?? element.title ?? element.element_id;
7188
+ const aufgeloest = effectivePreferredHeadSafe(snapshot, kette);
7189
+ if (aufgeloest.kind === "dag_fehler") {
7190
+ return {
7191
+ kind: "error",
7192
+ message: `Versionsgraph von ${element.element_id} ist nicht aufbaubar: ` + aufgeloest.message,
7193
+ retryable: false
7194
+ };
7195
+ }
7196
+ if (aufgeloest.kind === "keine_version") continue;
7197
+ const resolution = aufgeloest.outcome.resolution;
7198
+ const payloadVon = (versionId2) => snapshot.versions.find((v) => v.version_id === versionId2)?.payload;
7199
+ const keyVon = (versionId2) => {
7200
+ const payload2 = payloadVon(versionId2);
7201
+ return isObject10(payload2) && typeof payload2.key === "string" ? payload2.key : null;
7202
+ };
7203
+ if (resolution.kind === "fork") {
7204
+ for (const key2 of new Set(resolution.heads.map(keyVon).filter((k) => !!k))) {
7205
+ merke(key2, { art: "fork", elementId: element.element_id, title: titel, heads: resolution.heads });
7206
+ }
7207
+ continue;
7208
+ }
7209
+ const versionId = usableHeadVersionId(resolution);
7210
+ if (!versionId) continue;
7211
+ const key = keyVon(versionId);
7212
+ if (!key) continue;
7213
+ const payload = payloadVon(versionId);
7214
+ merke(key, {
7215
+ art: "aufgeloest",
7216
+ eintrag: {
7217
+ key,
7218
+ title: titel,
7219
+ elementId: element.element_id,
7220
+ versionId,
7221
+ provider: isObject10(payload) && typeof payload.provider === "string" ? payload.provider : null,
7222
+ executionProfiles: isObject10(payload) && Array.isArray(payload.execution_profiles) ? payload.execution_profiles : [],
7223
+ chainIndex
7224
+ }
7225
+ });
7226
+ }
7227
+ for (const [key, treffer] of trefferJeKey) {
7228
+ if (entschieden.has(key)) continue;
7229
+ entschieden.add(key);
7230
+ if (treffer.length > 1) {
7231
+ const namen = treffer.map((t) => t.art === "fork" ? t.title : t.eintrag.title);
7232
+ for (const t of treffer) {
7233
+ unselectable.push({
7234
+ key,
7235
+ elementId: t.art === "fork" ? t.elementId : t.eintrag.elementId,
7236
+ title: t.art === "fork" ? t.title : t.eintrag.title,
7237
+ reason: "ambiguous",
7238
+ message: `\`${key}\` ist in diesem Kontext mehrfach vergeben (${namen.join(", ")}). Die Aufl\xF6sung w\xE4hlt keinen.`
7239
+ });
7240
+ }
7241
+ continue;
7242
+ }
7243
+ const einziger = treffer[0];
7244
+ if (einziger.art === "fork") {
7245
+ unselectable.push({
7246
+ key,
7247
+ elementId: einziger.elementId,
7248
+ title: einziger.title,
7249
+ reason: "fork",
7250
+ message: `\`${key}\` liegt bei \u201E${einziger.title}" auf einem Fork (${einziger.heads.length} K\xF6pfe, keine greifende Auswahl). Erst einen bevorzugten Kopf setzen (D-36) \u2014 ein gleichnamiges Zielmodell aus einem entfernteren Kontext tritt daf\xFCr nicht ein.`
7251
+ });
7252
+ continue;
7253
+ }
7254
+ selectable.push(einziger.eintrag);
7255
+ }
7256
+ }
7257
+ return { kind: "ok", value: { selectable, unselectable } };
7258
+ }
7259
+
7260
+ // src/lib/szenen/freezeDraftChain.ts
7261
+ function ladeKnoten(sicht, elementId, erwarteterTyp) {
7262
+ const gelesen = sicht.getElement(elementId);
7263
+ if (gelesen.kind !== "ok") {
7264
+ return {
7265
+ fehler: gelesen.message ?? `Element ${elementId} konnte nicht gelesen werden.`,
7266
+ technisch: true
7267
+ };
7268
+ }
7269
+ const snapshot = gelesen.value;
7270
+ if (!snapshot) return { fehler: `Element ${elementId} ist unbekannt.` };
7271
+ if (snapshot.element.type !== erwarteterTyp) {
7272
+ return {
7273
+ fehler: `Element ${elementId} ist vom Typ "${snapshot.element.type}", erwartet "${erwarteterTyp}".`
7274
+ };
7275
+ }
7276
+ if (snapshot.draftFork) {
7277
+ return {
7278
+ fehler: `Element ${elementId} hat parallele Entwuerfe. Solange sie nicht aufgeloest sind, gibt es keinen eindeutigen Kettenknoten (D-29).`
7279
+ };
7280
+ }
7281
+ if (!snapshot.draft) {
7282
+ return { fehler: `Element ${elementId} hat keinen Entwurf.` };
7283
+ }
7284
+ if (!snapshot.draft.chain) {
7285
+ return {
7286
+ fehler: `Der Entwurf von Element ${elementId} gehoert zu keiner Draft-Kette.`
7287
+ };
7288
+ }
7289
+ return {
7290
+ type: erwarteterTyp,
7291
+ element_id: elementId,
7292
+ draft_id: snapshot.draft.draft_id,
7293
+ revision: snapshot.draft.revision,
7294
+ payload: snapshot.draft.payload,
7295
+ chain: snapshot.draft.chain
6778
7296
  };
6779
7297
  }
6780
7298
  function ausfallErgebnis(ausfall) {
@@ -6873,6 +7391,113 @@ async function freezeDraftChain(input) {
6873
7391
  ...shot.chain.source_recovery_id ? { sourceRecoveryId: shot.chain.source_recovery_id } : {}
6874
7392
  };
6875
7393
  }
7394
+ function ladeSzene(sicht, elementId, contextChain) {
7395
+ const gelesen = sicht.getElement(elementId);
7396
+ if (gelesen.kind !== "ok") {
7397
+ return {
7398
+ fehler: gelesen.message ?? `Element ${elementId} konnte nicht gelesen werden.`,
7399
+ technisch: true
7400
+ };
7401
+ }
7402
+ const snapshot = gelesen.value;
7403
+ if (!snapshot) return { fehler: `Element ${elementId} ist unbekannt.` };
7404
+ if (snapshot.element.type !== "scene") {
7405
+ return {
7406
+ fehler: `Element ${elementId} ist vom Typ "${snapshot.element.type}", erwartet "scene".`
7407
+ };
7408
+ }
7409
+ if (snapshot.draftFork) {
7410
+ return {
7411
+ fehler: `Element ${elementId} hat parallele Entwuerfe. Solange sie nicht aufgeloest sind, gibt es keinen eindeutigen Arbeitsstand (D-29).`
7412
+ };
7413
+ }
7414
+ if (snapshot.draft) {
7415
+ return {
7416
+ element_id: elementId,
7417
+ draft_id: snapshot.draft.draft_id,
7418
+ revision: snapshot.draft.revision,
7419
+ version_id: null,
7420
+ payload: snapshot.draft.payload
7421
+ };
7422
+ }
7423
+ const versionen = snapshot.versions ?? [];
7424
+ if (versionen.length === 0) {
7425
+ return {
7426
+ fehler: `Element ${elementId} hat weder einen Entwurf noch eine veroeffentlichte Version. Es gibt nichts einzufrieren.`
7427
+ };
7428
+ }
7429
+ const kopf = effectivePreferredHeadSafe(
7430
+ { element: snapshot.element, versions: versionen, preferredHeads: snapshot.preferredHeads },
7431
+ contextChain
7432
+ );
7433
+ if (kopf.kind === "dag_fehler") {
7434
+ return { fehler: `Der Versionsgraph von Element ${elementId} ist nicht aufbaubar: ${kopf.message}` };
7435
+ }
7436
+ const versionId = usableHeadVersionId(resolutionOf(kopf));
7437
+ if (!versionId) {
7438
+ return {
7439
+ fehler: `Element ${elementId} hat mehrere veroeffentlichte Koepfe und in diesem Kontext keine Auswahl. Welcher Stand gilt, entscheidet keine Reihenfolge (D-36) \u2014 bitte einen bevorzugten Kopf setzen.`
7440
+ };
7441
+ }
7442
+ const version = versionen.find((v) => v.version_id === versionId);
7443
+ if (!version) {
7444
+ return {
7445
+ fehler: `Die wirksame Version ${versionId} von Element ${elementId} steht nicht in der gelesenen Momentaufnahme.`
7446
+ };
7447
+ }
7448
+ return {
7449
+ element_id: elementId,
7450
+ draft_id: null,
7451
+ revision: null,
7452
+ version_id: versionId,
7453
+ payload: version.payload
7454
+ };
7455
+ }
7456
+ async function freezeSceneWorkingState(input) {
7457
+ const { sceneElementId, store, resolver, contextChain, userInstruction } = input;
7458
+ const a = await store.readSnapshot({ fresh: false });
7459
+ const szene = ladeSzene(a, sceneElementId, contextChain);
7460
+ if ("fehler" in szene) return ausfallErgebnis(szene);
7461
+ const b = await store.readSnapshot({ fresh: true });
7462
+ const erneut = ladeSzene(b, sceneElementId, contextChain);
7463
+ if ("fehler" in erneut) return ausfallErgebnis(erneut);
7464
+ if (erneut.draft_id !== szene.draft_id || erneut.revision !== szene.revision || erneut.version_id !== szene.version_id) {
7465
+ return {
7466
+ kind: "conflict",
7467
+ message: `Der Arbeitsstand der Szene ${sceneElementId} hat sich waehrend des Einfrierens geaendert (erwartet ${szene.draft_id ? `Entwurf ${szene.draft_id}, Revision ${szene.revision}` : `Version ${szene.version_id}`}, vorhanden ${erneut.draft_id ? `Entwurf ${erneut.draft_id}, Revision ${erneut.revision}` : `Version ${erneut.version_id}`}). Es wurde kein Modell gerufen.`,
7468
+ node: { type: "scene", element_id: sceneElementId, draft_id: szene.draft_id ?? "" },
7469
+ expectedRevision: szene.revision ?? 0,
7470
+ actualRevision: erneut.revision ?? 0
7471
+ };
7472
+ }
7473
+ const aufgeloest = await resolveScenePayload({
7474
+ scenePayload: szene.payload,
7475
+ sceneElementId: szene.element_id,
7476
+ // Ein Entwurf hat keine Version — und bekommt hier auch keine erfundene
7477
+ // (D-98/D-75).
7478
+ sceneVersionId: szene.version_id ?? "",
7479
+ resolver,
7480
+ contextChain,
7481
+ userInstruction
7482
+ });
7483
+ if (aufgeloest.kind !== "resolved") {
7484
+ return {
7485
+ kind: "rejected",
7486
+ message: aufgeloest.failure.message,
7487
+ failure: aufgeloest.failure
7488
+ };
7489
+ }
7490
+ return {
7491
+ kind: "ok",
7492
+ scene: {
7493
+ element_id: szene.element_id,
7494
+ draft_id: szene.draft_id,
7495
+ revision: szene.revision,
7496
+ version_id: szene.version_id
7497
+ },
7498
+ input: aufgeloest.input
7499
+ };
7500
+ }
6876
7501
 
6877
7502
  // src/lib/szenen/sceneDerivationRecovery.ts
6878
7503
  function recoveryStatusOf(assessment) {
@@ -6905,13 +7530,13 @@ function isSzenenItemId(itemId) {
6905
7530
  return itemId.startsWith(SZENEN_ITEM_PREFIX);
6906
7531
  }
6907
7532
  function checkSzenenBatchOrigin(candidate) {
6908
- if (!istText2(candidate.prompt_run_id)) {
7533
+ if (!istText3(candidate.prompt_run_id)) {
6909
7534
  return {
6910
7535
  kind: "rejected",
6911
7536
  failure: { field: "prompt_run_id", message: '"prompt_run_id" fehlt oder ist leer.' }
6912
7537
  };
6913
7538
  }
6914
- if (!istText2(candidate.generation_batch_id)) {
7539
+ if (!istText3(candidate.generation_batch_id)) {
6915
7540
  return {
6916
7541
  kind: "rejected",
6917
7542
  failure: {
@@ -6954,7 +7579,7 @@ function mergeSzenenBatchOrigin(bestehend, neu) {
6954
7579
  };
6955
7580
  }
6956
7581
  var DEFAULT_FSK_LEVEL = "FSK16";
6957
- var istText2 = (v) => typeof v === "string" && v.trim().length > 0;
7582
+ var istText3 = (v) => typeof v === "string" && v.trim().length > 0;
6958
7583
  function roundOfRun(run, runsOfShot) {
6959
7584
  return runsOfShot.findIndex((kandidat) => kandidat.run_id === run.run_id) + 1;
6960
7585
  }
@@ -6990,7 +7615,7 @@ function deriveItemFromRun(input) {
6990
7615
  }
6991
7616
  };
6992
7617
  }
6993
- if (!run.output || !istText2(run.output.positive)) {
7618
+ if (!run.output || !istText3(run.output.positive)) {
6994
7619
  return {
6995
7620
  kind: "rejected",
6996
7621
  failure: {
@@ -7011,8 +7636,8 @@ function deriveItemFromRun(input) {
7011
7636
  }
7012
7637
  const item_id = itemIdForRun(run.run_id);
7013
7638
  const lineage = deriveLineage(item_id, round, existingItems);
7014
- const negative = istText2(run.output.negative) ? run.output.negative : null;
7015
- const title = istText2(input.title) ? input.title : istText2(input.shotTitle) ? `${input.shotTitle} \xB7 Runde ${round}` : `Szenen-Lauf ${round}`;
7639
+ const negative = istText3(run.output.negative) ? run.output.negative : null;
7640
+ const title = istText3(input.title) ? input.title : istText3(input.shotTitle) ? `${input.shotTitle} \xB7 Runde ${round}` : `Szenen-Lauf ${round}`;
7016
7641
  return {
7017
7642
  kind: "ok",
7018
7643
  value: {
@@ -7022,9 +7647,9 @@ function deriveItemFromRun(input) {
7022
7647
  negative_prompt: negative,
7023
7648
  // `feedback` ist Pflicht (D-55) und ist genau die Erlaeuterung fuer den
7024
7649
  // Menschen — also gehoert sie in das Feld fuer Notizen.
7025
- notes: istText2(input.notes) ? input.notes : run.output.feedback,
7650
+ notes: istText3(input.notes) ? input.notes : run.output.feedback,
7026
7651
  rating: typeof input.rating === "number" ? input.rating : 0,
7027
- fsk_level: istText2(input.fskLevel) ? input.fskLevel : DEFAULT_FSK_LEVEL,
7652
+ fsk_level: istText3(input.fskLevel) ? input.fskLevel : DEFAULT_FSK_LEVEL,
7028
7653
  tags: [],
7029
7654
  szenen: {
7030
7655
  prompt_run_id: run.run_id,
@@ -7049,7 +7674,7 @@ function deriveItemFromRun(input) {
7049
7674
  }
7050
7675
 
7051
7676
  // src/lib/szenen/imageGenerationOrderStore.ts
7052
- function istText3(wert) {
7677
+ function istText4(wert) {
7053
7678
  return typeof wert === "string" && wert.trim().length > 0;
7054
7679
  }
7055
7680
  function collectImageGenerationOrders(events) {
@@ -7057,7 +7682,7 @@ function collectImageGenerationOrders(events) {
7057
7682
  for (const event of events) {
7058
7683
  if (event.type !== SZENEN_EVENT_TYPES.imageGenerationStarted) continue;
7059
7684
  const payload = event.payload;
7060
- if (!payload || !istText3(payload.generation_batch_id) || !istText3(payload.prompt_run_id)) continue;
7685
+ if (!payload || !istText4(payload.generation_batch_id) || !istText4(payload.prompt_run_id)) continue;
7061
7686
  if (byBatchId.has(payload.generation_batch_id)) continue;
7062
7687
  byBatchId.set(payload.generation_batch_id, {
7063
7688
  generation_batch_id: payload.generation_batch_id,
@@ -7071,7 +7696,7 @@ function collectImageGenerationOrders(events) {
7071
7696
  for (const event of events) {
7072
7697
  if (event.type !== SZENEN_EVENT_TYPES.imageGenerationFinished) continue;
7073
7698
  const payload = event.payload;
7074
- if (!payload || !istText3(payload.generation_batch_id)) continue;
7699
+ if (!payload || !istText4(payload.generation_batch_id)) continue;
7075
7700
  const auftrag = byBatchId.get(payload.generation_batch_id);
7076
7701
  if (!auftrag) continue;
7077
7702
  if (auftrag.status !== "started") continue;
@@ -9578,6 +10203,7 @@ function createHfImageStore(options) {
9578
10203
  const basisMs = options.leseBasisMs ?? 400;
9579
10204
  const schlafen2 = options.schlafen ?? warten;
9580
10205
  const abgelegt = /* @__PURE__ */ new Map();
10206
+ const vorschauen = /* @__PURE__ */ new Map();
9581
10207
  const bekanntePfade = /* @__PURE__ */ new Set();
9582
10208
  let gefalteterBestand = null;
9583
10209
  let laufenderAbgleich = null;
@@ -9601,7 +10227,7 @@ function createHfImageStore(options) {
9601
10227
  }
9602
10228
  return laufenderAbgleich;
9603
10229
  }
9604
- return {
10230
+ const speicher = {
9605
10231
  bestand: bestandAbgleichen,
9606
10232
  async putWithId(id, base64, mimeType, titel) {
9607
10233
  const echterTyp = typAusBytes(base64, mimeType);
@@ -9620,23 +10246,55 @@ function createHfImageStore(options) {
9620
10246
  }, []);
9621
10247
  abgelegt.set(id, { data: base64, mimeType: echterTyp });
9622
10248
  },
9623
- async resolve(id, mimeType) {
10249
+ async preview(id) {
10250
+ const bekannt = vorschauen.get(id);
10251
+ if (bekannt) return bekannt;
9624
10252
  const eben = abgelegt.get(id);
9625
- if (eben) return eben;
10253
+ if (eben) {
10254
+ const quelle2 = `data:${eben.mimeType};base64,${eben.data}`;
10255
+ vorschauen.set(id, quelle2);
10256
+ return quelle2;
10257
+ }
9626
10258
  const zustand = await bestandAbgleichen();
9627
10259
  const eintrag = zustand.metadata.find((e) => e.id === id);
9628
- const typ = eintrag?.mimeType || mimeType || null;
9629
- if (eintrag) {
9630
- const base64 = await hfLoadImageAsBase64(
10260
+ if (eintrag?.hasThumb && eintrag.filename) {
10261
+ const klein = await hfLoadImageAsBase64(
9631
10262
  id,
9632
10263
  token,
9633
10264
  void 0,
9634
10265
  eintrag.filename,
9635
10266
  void 0,
9636
10267
  eintrag.mimeType,
9637
- false
10268
+ true
9638
10269
  );
9639
- if (base64) return { data: base64, mimeType: eintrag.mimeType || "image/jpeg" };
10270
+ if (klein) {
10271
+ const quelle2 = `data:${eintrag.mimeType || "image/jpeg"};base64,${klein}`;
10272
+ vorschauen.set(id, quelle2);
10273
+ return quelle2;
10274
+ }
10275
+ }
10276
+ const voll = await speicher.resolve(id);
10277
+ const quelle = `data:${voll.mimeType};base64,${voll.data}`;
10278
+ vorschauen.set(id, quelle);
10279
+ return quelle;
10280
+ },
10281
+ async resolve(id, mimeType) {
10282
+ const eben = abgelegt.get(id);
10283
+ if (eben) return eben;
10284
+ const zustand = await bestandAbgleichen();
10285
+ const eintrag = zustand.metadata.find((e) => e.id === id);
10286
+ const typ = eintrag?.mimeType || mimeType || null;
10287
+ if (eintrag) {
10288
+ const base64 = await hfLoadImageAsBase64(
10289
+ id,
10290
+ token,
10291
+ void 0,
10292
+ eintrag.filename,
10293
+ void 0,
10294
+ eintrag.mimeType,
10295
+ false
10296
+ );
10297
+ if (base64) return { data: base64, mimeType: eintrag.mimeType || "image/jpeg" };
9640
10298
  }
9641
10299
  const pfadTyp = typ ?? "image/png";
9642
10300
  for (let versuch = 0; versuch < versuche; versuch += 1) {
@@ -9657,6 +10315,47 @@ function createHfImageStore(options) {
9657
10315
  );
9658
10316
  }
9659
10317
  };
10318
+ return speicher;
10319
+ }
10320
+
10321
+ // src/lib/szenen/libraryItems.ts
10322
+ var LOCATOR = /^(.+)-(\d+)$/;
10323
+ function locatorTeile(mediaRef) {
10324
+ const treffer = LOCATOR.exec(mediaRef);
10325
+ if (!treffer) return { stamm: mediaRef, position: null };
10326
+ return { stamm: treffer[1], position: Number(treffer[2]) };
10327
+ }
10328
+ function gruppiereZuItems(bilder) {
10329
+ const gruppen = /* @__PURE__ */ new Map();
10330
+ for (const bild of bilder) {
10331
+ const { stamm } = locatorTeile(bild.mediaRef);
10332
+ const vorhanden = gruppen.get(stamm);
10333
+ if (vorhanden) vorhanden.push(bild);
10334
+ else gruppen.set(stamm, [bild]);
10335
+ }
10336
+ const items = [...gruppen.entries()].map(([stamm, mitglieder]) => {
10337
+ const sortiert = [...mitglieder].sort((a, b) => {
10338
+ const pa = locatorTeile(a.mediaRef).position ?? 0;
10339
+ const pb = locatorTeile(b.mediaRef).position ?? 0;
10340
+ return pa - pb;
10341
+ });
10342
+ const erstes = sortiert[0];
10343
+ const itemRef = sortiert.length === 1 ? erstes.mediaRef : stamm;
10344
+ const juengstes = sortiert.reduce(
10345
+ (max, b) => Math.max(max, b.timestamp ?? 0),
10346
+ 0
10347
+ );
10348
+ return {
10349
+ item: {
10350
+ itemRef,
10351
+ title: erstes.itemTitle?.trim() || erstes.title || itemRef,
10352
+ images: sortiert.map(({ timestamp: _t, itemTitle: _i, ...bild }) => bild)
10353
+ },
10354
+ juengstes
10355
+ };
10356
+ });
10357
+ items.sort((a, b) => b.juengstes - a.juengstes);
10358
+ return items.map((e) => e.item);
9660
10359
  }
9661
10360
 
9662
10361
  // src/lib/szenen/hfImageLibrary.ts
@@ -9664,13 +10363,22 @@ function fehler(nachricht, retryable = false) {
9664
10363
  return { kind: "error", message: nachricht, retryable };
9665
10364
  }
9666
10365
  function alsBild(eintrag) {
10366
+ const { position } = locatorTeile(eintrag.id);
10367
+ const werk = eintrag.prompt?.trim() || "";
9667
10368
  return {
9668
10369
  mediaRef: eintrag.id,
9669
- // Der Prompt ist der einzige menschenlesbare Text, den ein Bild dort trägt.
9670
- // Fehlt er, ist die Kennung ehrlicher als ein erfundener Name.
9671
- title: eintrag.prompt?.trim() || eintrag.id,
10370
+ // Innerhalb eines mehrbildigen Werkes tragen alle Einträge denselben
10371
+ // Prompt — der Abgleich schreibt ihn je Bild aus derselben Library-Zeile.
10372
+ // Deshalb bekommt die Kachel dort die Position dazu, sonst stünde
10373
+ // vierzehnmal derselbe Satz.
10374
+ title: position === null ? werk || eintrag.id : `${werk || eintrag.id} \xB7 Bild ${position + 1}`,
9672
10375
  mimeType: eintrag.mimeType || "image/jpeg",
9673
- thumbUrl: null
10376
+ // Kein `thumbUrl`: Die Datei liegt auf HF hinter einem Token, und ein
10377
+ // `<img src>` käme dort nicht durch. Die Quelle entsteht über
10378
+ // `loadPreview()` — und nur für die Kacheln, die wirklich gezeigt werden.
10379
+ thumbUrl: null,
10380
+ itemTitle: werk || null,
10381
+ ...typeof eintrag.timestamp === "number" ? { timestamp: eintrag.timestamp } : {}
9674
10382
  };
9675
10383
  }
9676
10384
  function createHfImageLibrary(options) {
@@ -9681,15 +10389,14 @@ function createHfImageLibrary(options) {
9681
10389
  ...options.now ? { now: options.now } : {}
9682
10390
  });
9683
10391
  return {
9684
- async listImages(query) {
10392
+ async listItems(query) {
9685
10393
  try {
9686
10394
  const zustand = await store.bestand();
9687
10395
  const eintraege = zustand?.metadata ?? [];
10396
+ const items = gruppiereZuItems(eintraege.map(alsBild));
9688
10397
  const gesucht = query?.search?.trim().toLowerCase();
9689
- const gefiltert = gesucht ? eintraege.filter((e) => (e.prompt ?? "").toLowerCase().includes(gesucht) || e.id.toLowerCase().includes(gesucht)) : eintraege;
9690
- const sortiert = [...gefiltert].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
9691
- const begrenzt = query?.limit ? sortiert.slice(0, query.limit) : sortiert;
9692
- return { kind: "ok", value: begrenzt.map(alsBild) };
10398
+ const gefiltert = gesucht ? items.filter((i) => i.title.toLowerCase().includes(gesucht) || i.itemRef.toLowerCase().includes(gesucht)) : items;
10399
+ return { kind: "ok", value: query?.limit ? gefiltert.slice(0, query.limit) : gefiltert };
9693
10400
  } catch (error) {
9694
10401
  return fehler(
9695
10402
  `Der Bildbestand konnte nicht gelesen werden: ${error instanceof Error ? error.message : String(error)}`,
@@ -9697,6 +10404,16 @@ function createHfImageLibrary(options) {
9697
10404
  );
9698
10405
  }
9699
10406
  },
10407
+ async loadPreview(mediaRef) {
10408
+ try {
10409
+ return { kind: "ok", value: await store.preview(mediaRef) };
10410
+ } catch (error) {
10411
+ return fehler(
10412
+ `Die Vorschau von ${mediaRef} ist nicht ladbar: ${error instanceof Error ? error.message : String(error)}`,
10413
+ true
10414
+ );
10415
+ }
10416
+ },
9700
10417
  async resolveImage(mediaRef, mimeTypeHinweis) {
9701
10418
  try {
9702
10419
  const bild = await store.resolve(mediaRef, mimeTypeHinweis);
@@ -9709,10 +10426,13 @@ function createHfImageLibrary(options) {
9709
10426
  const id = newImageId();
9710
10427
  try {
9711
10428
  await store.putWithId(id, input.data, input.mime_type, input.title);
9712
- return {
9713
- kind: "ok",
9714
- value: { mediaRef: id, title: input.title, mimeType: input.mime_type, thumbUrl: null }
10429
+ const abgelegt = {
10430
+ mediaRef: id,
10431
+ title: input.title,
10432
+ mimeType: input.mime_type,
10433
+ thumbUrl: null
9715
10434
  };
10435
+ return { kind: "ok", value: abgelegt };
9716
10436
  } catch (error) {
9717
10437
  return fehler(
9718
10438
  `Das Bild konnte nicht abgelegt werden: ${error instanceof Error ? error.message : String(error)}`,
@@ -9723,180 +10443,46 @@ function createHfImageLibrary(options) {
9723
10443
  };
9724
10444
  }
9725
10445
 
9726
- // src/lib/szenen/contextVisibility.ts
9727
- var isObject9 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
9728
- function zerlegeKette(contextChain) {
9729
- return {
9730
- // Das erste Glied unveraendert, `null` eingeschlossen: `null` heisst
9731
- // kontextlos, nicht „egal" — der Typ von `workingContextId` sagt das
9732
- // ausdruecklich.
9733
- workingContextId: contextChain.length > 0 ? contextChain[0] : null,
9734
- // Gespeicherte Auswahlen tragen nie `null` als Kontext
9735
- // (`SetPreferredHeadInput.contextId` laeuft durch `checkRequiredId`), das
9736
- // Schlussglied der Kette kann hier also nichts treffen.
9737
- inheritanceChain: contextChain.slice(1).filter((link) => typeof link === "string" && link.length > 0)
9738
- };
9739
- }
9740
- function effectivePreferredHead(snapshot, contextChain) {
9741
- const versionen = snapshot.versions ?? [];
9742
- if (versionen.length === 0) return null;
9743
- const { workingContextId, inheritanceChain } = zerlegeKette(contextChain);
9744
- return resolvePreferredHead({
9745
- dag: buildVersionDag(versionen),
9746
- workingContextId,
9747
- inheritanceChain,
9748
- owningContextId: snapshot.element.context_id ?? void 0,
9749
- selections: snapshot.preferredHeads ?? []
9750
- });
9751
- }
9752
- function effectivePreferredHeadSafe(snapshot, contextChain) {
9753
- try {
9754
- const outcome = effectivePreferredHead(snapshot, contextChain);
9755
- return outcome ? { kind: "aufgeloest", outcome } : { kind: "keine_version" };
9756
- } catch (error) {
9757
- if (error instanceof VersionDagError) {
9758
- return { kind: "dag_fehler", message: error.message };
9759
- }
9760
- throw error;
9761
- }
9762
- }
9763
- function resolutionOf(result) {
9764
- return result && result.kind === "aufgeloest" ? result.outcome.resolution : null;
9765
- }
9766
- function selectedHeadVersionId(resolution) {
9767
- return resolution && resolution.kind === "selected" ? resolution.versionId : null;
9768
- }
9769
- function usableHeadVersionId(resolution) {
9770
- if (!resolution) return null;
9771
- if (resolution.kind === "selected" || resolution.kind === "single") return resolution.versionId;
9772
- return null;
9773
- }
9774
- function preferredHeadRevisionFor(snapshot, contextId) {
9775
- const auswahl = (snapshot.preferredHeads ?? []).find((h) => h.context_id === contextId);
9776
- return auswahl?.revision ?? 0;
9777
- }
9778
- async function visibleElementsAlongChain(queries, input) {
9779
- const gesehen = /* @__PURE__ */ new Set();
9780
- const gefunden = [];
9781
- const kette = input.contextChain.length > 0 ? input.contextChain : [null];
9782
- for (const link of kette) {
9783
- const liste = await queries.listElements({ contextId: link, types: input.types });
9784
- if (liste.kind !== "ok") return liste;
9785
- for (const element of liste.value) {
9786
- if (gesehen.has(element.element_id)) continue;
9787
- gesehen.add(element.element_id);
9788
- gefunden.push(element);
9789
- }
9790
- }
9791
- return { kind: "ok", value: gefunden };
9792
- }
9793
- async function selectableTargetModels(queries, contextChain) {
9794
- const selectable = [];
9795
- const unselectable = [];
9796
- const entschieden = /* @__PURE__ */ new Set();
9797
- const kette = contextChain.length > 0 ? contextChain : [null];
9798
- for (const [chainIndex, link] of kette.entries()) {
9799
- const liste = await queries.listElements({ contextId: link, type: "target_model" });
9800
- if (liste.kind !== "ok") return liste;
9801
- const trefferJeKey = /* @__PURE__ */ new Map();
9802
- const merke = (key, treffer) => {
9803
- trefferJeKey.set(key, [...trefferJeKey.get(key) ?? [], treffer]);
9804
- };
9805
- for (const element of liste.value) {
9806
- const detail = await queries.getElement(element.element_id);
9807
- if (detail.kind !== "ok") return detail;
9808
- const snapshot = detail.value;
9809
- if (!snapshot) continue;
9810
- const titel = snapshot.metadata?.title ?? element.title ?? element.element_id;
9811
- const aufgeloest = effectivePreferredHeadSafe(snapshot, kette);
9812
- if (aufgeloest.kind === "dag_fehler") {
9813
- return {
9814
- kind: "error",
9815
- message: `Versionsgraph von ${element.element_id} ist nicht aufbaubar: ` + aufgeloest.message,
9816
- retryable: false
9817
- };
9818
- }
9819
- if (aufgeloest.kind === "keine_version") continue;
9820
- const resolution = aufgeloest.outcome.resolution;
9821
- const payloadVon = (versionId2) => snapshot.versions.find((v) => v.version_id === versionId2)?.payload;
9822
- const keyVon = (versionId2) => {
9823
- const payload2 = payloadVon(versionId2);
9824
- return isObject9(payload2) && typeof payload2.key === "string" ? payload2.key : null;
9825
- };
9826
- if (resolution.kind === "fork") {
9827
- for (const key2 of new Set(resolution.heads.map(keyVon).filter((k) => !!k))) {
9828
- merke(key2, { art: "fork", elementId: element.element_id, title: titel, heads: resolution.heads });
9829
- }
9830
- continue;
9831
- }
9832
- const versionId = usableHeadVersionId(resolution);
9833
- if (!versionId) continue;
9834
- const key = keyVon(versionId);
9835
- if (!key) continue;
9836
- const payload = payloadVon(versionId);
9837
- merke(key, {
9838
- art: "aufgeloest",
9839
- eintrag: {
9840
- key,
9841
- title: titel,
9842
- elementId: element.element_id,
9843
- versionId,
9844
- provider: isObject9(payload) && typeof payload.provider === "string" ? payload.provider : null,
9845
- executionProfiles: isObject9(payload) && Array.isArray(payload.execution_profiles) ? payload.execution_profiles : [],
9846
- chainIndex
9847
- }
9848
- });
9849
- }
9850
- for (const [key, treffer] of trefferJeKey) {
9851
- if (entschieden.has(key)) continue;
9852
- entschieden.add(key);
9853
- if (treffer.length > 1) {
9854
- const namen = treffer.map((t) => t.art === "fork" ? t.title : t.eintrag.title);
9855
- for (const t of treffer) {
9856
- unselectable.push({
9857
- key,
9858
- elementId: t.art === "fork" ? t.elementId : t.eintrag.elementId,
9859
- title: t.art === "fork" ? t.title : t.eintrag.title,
9860
- reason: "ambiguous",
9861
- message: `\`${key}\` ist in diesem Kontext mehrfach vergeben (${namen.join(", ")}). Die Aufl\xF6sung w\xE4hlt keinen.`
9862
- });
9863
- }
9864
- continue;
9865
- }
9866
- const einziger = treffer[0];
9867
- if (einziger.art === "fork") {
9868
- unselectable.push({
9869
- key,
9870
- elementId: einziger.elementId,
9871
- title: einziger.title,
9872
- reason: "fork",
9873
- message: `\`${key}\` liegt bei \u201E${einziger.title}" auf einem Fork (${einziger.heads.length} K\xF6pfe, keine greifende Auswahl). Erst einen bevorzugten Kopf setzen (D-36) \u2014 ein gleichnamiges Zielmodell aus einem entfernteren Kontext tritt daf\xFCr nicht ein.`
9874
- });
9875
- continue;
9876
- }
9877
- selectable.push(einziger.eintrag);
9878
- }
9879
- }
9880
- return { kind: "ok", value: { selectable, unselectable } };
10446
+ // src/lib/szenen/bildVorschau.ts
10447
+ async function ladeVorschauen(port, mediaRefs, fertig, optionen = {}) {
10448
+ const laden = port.loadPreview;
10449
+ if (!laden) return;
10450
+ const parallel = Math.max(1, optionen.parallel ?? 4);
10451
+ const lauf = optionen.lauf;
10452
+ const offen = mediaRefs.filter((ref) => !(optionen.hat?.(ref) ?? false));
10453
+ let naechster = 0;
10454
+ async function arbeiter() {
10455
+ while (naechster < offen.length) {
10456
+ if (lauf?.abgebrochen) return;
10457
+ const ref = offen[naechster];
10458
+ naechster += 1;
10459
+ const ergebnis = await laden.call(port, ref);
10460
+ if (lauf?.abgebrochen) return;
10461
+ if (ergebnis.kind === "ok") fertig(ref, ergebnis.value);
10462
+ }
10463
+ }
10464
+ await Promise.all(
10465
+ Array.from({ length: Math.min(parallel, offen.length) }, () => arbeiter())
10466
+ );
9881
10467
  }
9882
10468
 
9883
10469
  // src/lib/szenen/imageGenerationCapability.ts
9884
- var isObject10 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
9885
- var istText4 = (v) => typeof v === "string" && v.trim().length > 0;
10470
+ var isObject11 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
10471
+ var istText5 = (v) => typeof v === "string" && v.trim().length > 0;
9886
10472
  function checkImageExecutionProfile(profile) {
9887
10473
  const befunde = [];
9888
- if (!isObject10(profile)) return ["Das Ausfuehrungsprofil ist kein Objekt."];
10474
+ if (!isObject11(profile)) return ["Das Ausfuehrungsprofil ist kein Objekt."];
9889
10475
  for (const feld of ["profile_id", "execution_path", "execution_model_id"]) {
9890
- if (!istText4(profile[feld])) befunde.push(`"${feld}" fehlt als nicht-leerer Text.`);
10476
+ if (!istText5(profile[feld])) befunde.push(`"${feld}" fehlt als nicht-leerer Text.`);
9891
10477
  }
9892
10478
  if (typeof profile.enabled !== "boolean") befunde.push('"enabled" fehlt als Boolean.');
9893
- if (!Array.isArray(profile.environments) || profile.environments.length === 0 || !profile.environments.every(istText4)) {
10479
+ if (!Array.isArray(profile.environments) || profile.environments.length === 0 || !profile.environments.every(istText5)) {
9894
10480
  befunde.push('"environments" fehlt als nicht-leere Liste von Umgebungskennungen.');
9895
10481
  }
9896
10482
  if (!Number.isInteger(profile.max_images) || profile.max_images < 1) {
9897
10483
  befunde.push(`"max_images" ist ${JSON.stringify(profile.max_images)}; erwartet eine ganze Zahl >= 1.`);
9898
10484
  }
9899
- if (!Array.isArray(profile.aspect_ratios) || !profile.aspect_ratios.every(istText4)) {
10485
+ if (!Array.isArray(profile.aspect_ratios) || !profile.aspect_ratios.every(istText5)) {
9900
10486
  befunde.push('"aspect_ratios" fehlt als Liste von Seitenverhaeltnissen (darf leer sein).');
9901
10487
  }
9902
10488
  if (typeof profile.accepts_seed !== "boolean") befunde.push('"accepts_seed" fehlt als Boolean.');
@@ -9905,10 +10491,26 @@ function checkImageExecutionProfile(profile) {
9905
10491
  }
9906
10492
  return befunde;
9907
10493
  }
10494
+ function checkImageExecutionProfiles(profiles) {
10495
+ const anzahlJeKennung = /* @__PURE__ */ new Map();
10496
+ for (const profil of profiles ?? []) {
10497
+ if (!isObject11(profil) || !istText5(profil.profile_id)) continue;
10498
+ anzahlJeKennung.set(profil.profile_id, (anzahlJeKennung.get(profil.profile_id) ?? 0) + 1);
10499
+ }
10500
+ return (profiles ?? []).map((profil) => {
10501
+ const befunde = [...checkImageExecutionProfile(profil)];
10502
+ if (isObject11(profil) && istText5(profil.profile_id) && (anzahlJeKennung.get(profil.profile_id) ?? 0) > 1) {
10503
+ befunde.push(
10504
+ `Die profile_id "${profil.profile_id}" kommt in diesem Zielmodell mehrfach vor \u2014 die Auswahl waere nicht mehr eindeutig aufloesbar.`
10505
+ );
10506
+ }
10507
+ return befunde;
10508
+ });
10509
+ }
9908
10510
  function executableImageModels(promptTargetModelKey, targets, capability) {
9909
10511
  const models = [];
9910
10512
  const invalid = [];
9911
- if (!istText4(capability?.environment_id) || !Array.isArray(capability?.execution_paths)) {
10513
+ if (!istText5(capability?.environment_id) || !Array.isArray(capability?.execution_paths)) {
9912
10514
  return {
9913
10515
  prompt_target_model_key: promptTargetModelKey,
9914
10516
  models: [],
@@ -9918,23 +10520,31 @@ function executableImageModels(promptTargetModelKey, targets, capability) {
9918
10520
  };
9919
10521
  }
9920
10522
  for (const target of targets ?? []) {
9921
- for (const roh of target.executionProfiles ?? []) {
9922
- const befunde = checkImageExecutionProfile(roh);
10523
+ const profilBefunde = checkImageExecutionProfiles(target.executionProfiles ?? []);
10524
+ for (const [index, roh] of (target.executionProfiles ?? []).entries()) {
10525
+ const befunde = profilBefunde[index] ?? [];
9923
10526
  if (befunde.length > 0) {
9924
- const kennung = isObject10(roh) && istText4(roh.profile_id) ? roh.profile_id : "(ohne profile_id)";
10527
+ const kennung = isObject11(roh) && istText5(roh.profile_id) ? roh.profile_id : "(ohne profile_id)";
9925
10528
  invalid.push(`${target.key}/${kennung}: ${befunde.join(" ")}`);
9926
10529
  continue;
9927
10530
  }
9928
10531
  const profil = roh;
9929
10532
  if (!profil.enabled) continue;
9930
10533
  if (!profil.environments.includes(capability.environment_id)) continue;
9931
- if (!capability.execution_paths.includes(profil.execution_path)) continue;
10534
+ const weg = capability.execution_paths.find((r) => r.execution_path === profil.execution_path);
10535
+ if (!weg) continue;
10536
+ if (target.provider !== weg.provider) {
10537
+ invalid.push(
10538
+ `${target.key}/${profil.profile_id}: Das Zielmodell nennt den Anbieter ${target.provider === null ? "(keinen)" : `"${target.provider}"`}, der Aufrufweg "${weg.execution_path}" fuehrt aber zu "${weg.provider}".`
10539
+ );
10540
+ continue;
10541
+ }
9932
10542
  models.push({
9933
10543
  selection_id: `${target.key}#${profil.profile_id}`,
9934
10544
  target_model_key: target.key,
9935
10545
  ...target.versionId ? { target_model_version_id: target.versionId } : {},
9936
10546
  display_name: target.title,
9937
- provider: target.provider ?? "(ohne provider)",
10547
+ provider: weg.provider,
9938
10548
  profile_id: profil.profile_id,
9939
10549
  environment_id: capability.environment_id,
9940
10550
  execution_path: profil.execution_path,
@@ -9953,7 +10563,7 @@ function executableImageModels(promptTargetModelKey, targets, capability) {
9953
10563
  models: [],
9954
10564
  default_selection_id: null,
9955
10565
  invalid_profiles: invalid,
9956
- reason: `In dieser Umgebung (${capability.environment_id}) traegt kein sichtbares Zielmodell ein aktives Ausfuehrungsprofil fuer einen Aufrufweg dieses Hosts (${capability.execution_paths.join(", ") || "keiner"}).`
10566
+ reason: `In dieser Umgebung (${capability.environment_id}) traegt kein sichtbares Zielmodell ein aktives Ausfuehrungsprofil fuer einen Aufrufweg dieses Hosts (${capability.execution_paths.map((r) => r.execution_path).join(", ") || "keiner"}).`
9957
10567
  };
9958
10568
  }
9959
10569
  const passend = models.find((m) => m.is_prompt_target);
@@ -10196,7 +10806,7 @@ function createImageGenerationPort(options) {
10196
10806
  // src/lib/szenen/memoryViewPort.ts
10197
10807
  var MEMORY_IMAGE_CAPABILITY = {
10198
10808
  environment_id: "memory",
10199
- execution_paths: ["memory"]
10809
+ execution_paths: [{ execution_path: "memory", provider: "google" }]
10200
10810
  };
10201
10811
  function ok2(value) {
10202
10812
  return { kind: "ok", value };
@@ -10323,6 +10933,44 @@ function createMemoryViewPort(options = {}) {
10323
10933
  return ok2(laeufe.filter(
10324
10934
  (r) => r.source?.kind === "draft_chain" && r.source.chain_id === chainId
10325
10935
  ));
10936
+ },
10937
+ // ── Der Lauf direkt aus einer Szene (D-135) ───────────────────────────
10938
+ //
10939
+ // Wie beim Kettenlauf bildet der Speicherport nur die **Form** ab: ein
10940
+ // Lauf mit `source.kind === 'scene_direct'`, ohne Shot und ohne
10941
+ // Versionskennungen. Ob ein Arbeitsstand einfrierbar ist, entscheidet
10942
+ // `clientRunPort` gegen einen echten Speicher — hier waere es eine zweite
10943
+ // Wahrheit.
10944
+ async startRunFromScene(input) {
10945
+ const runId = ids();
10946
+ const basis = (options.deriveRun ?? simulierterLauf)(
10947
+ { shotVersionId: "", ...input.userInstruction ? { userInstruction: input.userInstruction } : {} },
10948
+ runId
10949
+ );
10950
+ const lauf = {
10951
+ ...basis,
10952
+ shot_version_id: "",
10953
+ shot_element_id: "",
10954
+ scene_setup_version_id: "",
10955
+ camera_id: "",
10956
+ target_model_version_id: "",
10957
+ source: {
10958
+ kind: "scene_direct",
10959
+ scene: {
10960
+ element_id: input.sceneElementId,
10961
+ draft_id: `draft-${input.sceneElementId}`,
10962
+ revision: 1,
10963
+ version_id: null
10964
+ }
10965
+ }
10966
+ };
10967
+ laeufe.push(lauf);
10968
+ return done(lauf);
10969
+ },
10970
+ async listRunsForScene(sceneElementId) {
10971
+ return ok2(laeufe.filter(
10972
+ (r) => r.source?.kind === "scene_direct" && r.source.scene.element_id === sceneElementId
10973
+ ));
10326
10974
  }
10327
10975
  };
10328
10976
  const itemPort = {
@@ -10505,15 +11153,26 @@ function createMemoryViewPort(options = {}) {
10505
11153
  }
10506
11154
  };
10507
11155
  const bilder = /* @__PURE__ */ new Map();
11156
+ const werke = [];
10508
11157
  for (const bild of options.images ?? []) {
10509
11158
  bilder.set(bild.mediaRef, { bild, data: "AAAA" });
11159
+ werke.push({ itemRef: bild.mediaRef, title: bild.title, images: [bild] });
11160
+ }
11161
+ for (const werk of options.imageItems ?? []) {
11162
+ for (const bild of werk.images) bilder.set(bild.mediaRef, { bild, data: "AAAA" });
11163
+ werke.push(werk);
10510
11164
  }
11165
+ const vorschauLazy = options.imagePreviewLazy === true;
10511
11166
  const imageLibraryPort = {
10512
- async listImages(query) {
10513
- const alle = [...bilder.values()].map((e) => e.bild);
11167
+ async listItems(query) {
10514
11168
  const gesucht = query?.search?.trim().toLowerCase();
10515
- const gefiltert = gesucht ? alle.filter((b) => b.title.toLowerCase().includes(gesucht)) : alle;
10516
- return ok2(query?.limit ? gefiltert.slice(0, query.limit) : gefiltert);
11169
+ const gefiltert = gesucht ? werke.filter((w) => w.title.toLowerCase().includes(gesucht)) : werke;
11170
+ const sichtbar = query?.limit ? gefiltert.slice(0, query.limit) : gefiltert;
11171
+ if (!vorschauLazy) return ok2(sichtbar);
11172
+ return ok2(sichtbar.map((w) => ({
11173
+ ...w,
11174
+ images: w.images.map((b) => ({ ...b, thumbUrl: null }))
11175
+ })));
10517
11176
  },
10518
11177
  async resolveImage(mediaRef) {
10519
11178
  const eintrag = bilder.get(mediaRef);
@@ -10531,9 +11190,19 @@ function createMemoryViewPort(options = {}) {
10531
11190
  thumbUrl: null
10532
11191
  };
10533
11192
  bilder.set(mediaRef, { bild, data: input.data });
11193
+ werke.unshift({ itemRef: mediaRef, title: bild.title, images: [bild] });
10534
11194
  return done(bild);
10535
11195
  }
10536
11196
  };
11197
+ if (vorschauLazy) {
11198
+ imageLibraryPort.loadPreview = async (mediaRef) => {
11199
+ const eintrag = bilder.get(mediaRef);
11200
+ if (!eintrag) {
11201
+ return { kind: "error", message: `Bild ${mediaRef} ist unbekannt.`, retryable: false };
11202
+ }
11203
+ return ok2(`data:${eintrag.bild.mimeType};base64,${eintrag.data}`);
11204
+ };
11205
+ }
10537
11206
  const batches = [...options.batches ?? []];
10538
11207
  const imageGenerationEvents = createInMemoryEventChannel();
10539
11208
  const imageBatchHistoryPort = {
@@ -11408,6 +12077,23 @@ function createEventLogRunPersistence(channel) {
11408
12077
  return queryError2(error, `Laeufe der Kette ${chainId} konnten nicht gelesen werden`);
11409
12078
  }
11410
12079
  },
12080
+ async listForScene(sceneElementId) {
12081
+ try {
12082
+ const events = await channel.readEvents();
12083
+ return {
12084
+ kind: "ok",
12085
+ value: collectPromptRuns(events).filter((run) => {
12086
+ const quelle = sourceOf(run);
12087
+ return quelle.kind === "scene_direct" && quelle.scene.element_id === sceneElementId;
12088
+ })
12089
+ };
12090
+ } catch (error) {
12091
+ return queryError2(
12092
+ error,
12093
+ `Laeufe der Szene ${sceneElementId} konnten nicht gelesen werden`
12094
+ );
12095
+ }
12096
+ },
11411
12097
  async record(record) {
11412
12098
  try {
11413
12099
  await channel.appendEvent(SZENEN_EVENT_TYPES.promptRunRecorded, record);
@@ -11494,13 +12180,21 @@ function createClientRunPort(options) {
11494
12180
  return { kind: "error", message: resolved.failure.message, retryable: false };
11495
12181
  }
11496
12182
  const outcome = await runPromptDerivation(resolved.input, text);
12183
+ if (!resolved.input.shot) {
12184
+ return {
12185
+ kind: "error",
12186
+ message: `Die Aufloesung der Shot-Version ${input.shotVersionId} lieferte eine Eingabe ohne Shot. Das ist ein Programmfehler, kein Lauf.`,
12187
+ retryable: false
12188
+ };
12189
+ }
11497
12190
  const record = {
11498
12191
  run_id: newRunId(),
11499
12192
  shot_version_id: input.shotVersionId,
11500
12193
  shot_element_id: resolved.input.shot.element_id,
11501
12194
  scene_setup_version_id: resolved.input.scene.version_id,
11502
- camera_id: resolved.input.shot.camera_id,
11503
- target_model_version_id: resolved.input.target_model_resolution.version_id,
12195
+ // Leer heisst „kein Wert", nicht „Wert unbekannt" (D-75/D-135).
12196
+ camera_id: resolved.input.shot.camera_id ?? "",
12197
+ target_model_version_id: resolved.input.target_model_resolution?.version_id ?? "",
11504
12198
  provider: outcome.provider,
11505
12199
  model_id: outcome.model_id,
11506
12200
  system_prompt: outcome.system_prompt,
@@ -11572,8 +12266,8 @@ function createClientRunPort(options) {
11572
12266
  shot_version_id: "",
11573
12267
  shot_element_id: input.shotElementId,
11574
12268
  scene_setup_version_id: "",
11575
- camera_id: eingefroren.input.shot.camera_id,
11576
- target_model_version_id: eingefroren.input.target_model_resolution.version_id,
12269
+ camera_id: eingefroren.input.shot?.camera_id ?? "",
12270
+ target_model_version_id: eingefroren.input.target_model_resolution?.version_id ?? "",
11577
12271
  provider: outcome.provider,
11578
12272
  model_id: outcome.model_id,
11579
12273
  system_prompt: outcome.system_prompt,
@@ -11603,10 +12297,99 @@ function createClientRunPort(options) {
11603
12297
  retryable: true
11604
12298
  };
11605
12299
  }
11606
- },
11607
- async listRunsForChain(chainId) {
11608
- const alle = await runs.listForChain(chainId);
11609
- return alle;
12300
+ },
12301
+ async listRunsForChain(chainId) {
12302
+ const alle = await runs.listForChain(chainId);
12303
+ return alle;
12304
+ },
12305
+ // ── Der Lauf direkt aus einer Szene (D-135) ───────────────────────────
12306
+ //
12307
+ // Bewusst **derselbe** Ablauf wie oben: Kontext ermitteln, Kette
12308
+ // aufloesen, einfrieren, dann erst rufen. Was sich unterscheidet, ist
12309
+ // ausschliesslich, **was** eingefroren wird — ein Szenen-Arbeitsstand
12310
+ // statt einer Drei-Knoten-Kette.
12311
+ async startRunFromScene(input) {
12312
+ try {
12313
+ let contextId = input.contextId;
12314
+ if (contextId === void 0) {
12315
+ const element = await elements.getElement(input.sceneElementId);
12316
+ if (element.kind !== "ok") {
12317
+ return { kind: "error", message: element.message, retryable: element.retryable };
12318
+ }
12319
+ if (element.value === null) {
12320
+ return {
12321
+ kind: "error",
12322
+ message: `Element ${input.sceneElementId} ist unbekannt.`,
12323
+ retryable: false
12324
+ };
12325
+ }
12326
+ contextId = element.value.element.context_id;
12327
+ }
12328
+ const chain = await context.chainFor(contextId ?? null);
12329
+ if (chain.kind !== "ok") {
12330
+ return { kind: "error", message: chain.message, retryable: chain.retryable };
12331
+ }
12332
+ const eingefroren = await freezeSceneWorkingState({
12333
+ sceneElementId: input.sceneElementId,
12334
+ store: elements,
12335
+ resolver,
12336
+ contextChain: chain.value,
12337
+ userInstruction: input.userInstruction ?? null
12338
+ });
12339
+ if (eingefroren.kind === "conflict") {
12340
+ return {
12341
+ kind: "conflict",
12342
+ message: eingefroren.message,
12343
+ node: eingefroren.node,
12344
+ expectedRevision: eingefroren.expectedRevision,
12345
+ actualRevision: eingefroren.actualRevision
12346
+ };
12347
+ }
12348
+ if (eingefroren.kind === "error") {
12349
+ return { kind: "error", message: eingefroren.message, retryable: eingefroren.retryable };
12350
+ }
12351
+ if (eingefroren.kind !== "ok") {
12352
+ return { kind: "rejected", message: eingefroren.message };
12353
+ }
12354
+ const outcome = await runPromptDerivation(eingefroren.input, text);
12355
+ const record = {
12356
+ run_id: newRunId(),
12357
+ // Keine fingierten Kennungen (D-98/D-75). Was es nicht gibt, bleibt
12358
+ // leer — die Herkunft steht in `source`.
12359
+ shot_version_id: "",
12360
+ shot_element_id: "",
12361
+ scene_setup_version_id: "",
12362
+ camera_id: "",
12363
+ target_model_version_id: eingefroren.input.target_model_resolution?.version_id ?? "",
12364
+ provider: outcome.provider,
12365
+ model_id: outcome.model_id,
12366
+ system_prompt: outcome.system_prompt,
12367
+ input_payload: eingefroren.input,
12368
+ status: outcome.status,
12369
+ failure: outcome.status === "failed" ? outcome.failure : null,
12370
+ output: outcome.status === "succeeded" ? outcome.output : null,
12371
+ raw_output: outcome.raw_output,
12372
+ source: { kind: "scene_direct", scene: eingefroren.scene },
12373
+ created_at: now().toISOString()
12374
+ };
12375
+ return await runs.record(record);
12376
+ } catch (error) {
12377
+ return {
12378
+ kind: "error",
12379
+ message: `Prompt-Lauf aus der Szene ${input.sceneElementId} ist fehlgeschlagen: ${error instanceof Error ? error.message : String(error)}`,
12380
+ retryable: true
12381
+ };
12382
+ }
12383
+ },
12384
+ async listRunsForScene(sceneElementId) {
12385
+ try {
12386
+ return await runs.listForScene(sceneElementId);
12387
+ } catch (error) {
12388
+ return queryError2(
12389
+ error,
12390
+ `Laeufe der Szene ${sceneElementId} konnten nicht gelesen werden`
12391
+ );
12392
+ }
11610
12393
  }
11611
12394
  };
11612
12395
  }
@@ -11671,18 +12454,18 @@ function createFlowImageProvider(generateImage) {
11671
12454
  }
11672
12455
 
11673
12456
  // src/lib/szenen/imageBatchStore.ts
11674
- var istText5 = (wert) => typeof wert === "string" && wert.trim().length > 0;
12457
+ var istText6 = (wert) => typeof wert === "string" && wert.trim().length > 0;
11675
12458
  function alsBilder(rohe) {
11676
12459
  if (!Array.isArray(rohe)) return [];
11677
- return rohe.filter((b) => !!b && typeof b === "object").filter((b) => istText5(b.media_ref) && typeof b.position === "number").map((b) => ({
12460
+ return rohe.filter((b) => !!b && typeof b === "object").filter((b) => istText6(b.media_ref) && typeof b.position === "number").map((b) => ({
11678
12461
  media_ref: b.media_ref,
11679
12462
  position: b.position,
11680
- ...istText5(b.asset_id) ? { asset_id: b.asset_id } : {},
12463
+ ...istText6(b.asset_id) ? { asset_id: b.asset_id } : {},
11681
12464
  // Der bestätigte MIME-Typ (Review 2026-08-18 §5 P2) muss durch das
11682
12465
  // Rücklesen hindurch: Genau hier braucht ihn der Lesepfad, um den
11683
12466
  // Dateipfad `images/<id>.<ext>` zu bilden, statt ihn zu erraten. Nur
11684
12467
  // gesetzt, wenn er im Ereignis steht — ältere Batches tragen ihn nicht.
11685
- ...istText5(b.mime_type) ? { mime_type: b.mime_type } : {},
12468
+ ...istText6(b.mime_type) ? { mime_type: b.mime_type } : {},
11686
12469
  model: b.model ?? null,
11687
12470
  seed: b.seed ?? null,
11688
12471
  width: b.width ?? null,
@@ -11694,7 +12477,7 @@ function collectImageBatches(events) {
11694
12477
  for (const event of events) {
11695
12478
  if (event.type !== SZENEN_EVENT_TYPES.imageBatchPersisted) continue;
11696
12479
  const payload = event.payload;
11697
- if (!payload || !istText5(payload.item_id) || !istText5(payload.prompt_run_id) || !istText5(payload.generation_batch_id)) continue;
12480
+ if (!payload || !istText6(payload.item_id) || !istText6(payload.prompt_run_id) || !istText6(payload.generation_batch_id)) continue;
11698
12481
  if (payload.source_kind !== "generated_in_app" && payload.source_kind !== "external_import") continue;
11699
12482
  const bestehend = byItemId.get(payload.item_id);
11700
12483
  if (bestehend?.generation_batch_ids.includes(payload.generation_batch_id)) continue;
@@ -11705,7 +12488,7 @@ function collectImageBatches(events) {
11705
12488
  prompt_run_id: payload.prompt_run_id,
11706
12489
  generation_batch_ids: [payload.generation_batch_id],
11707
12490
  source_kind: payload.source_kind,
11708
- title: istText5(payload.title) ? payload.title : `Batch aus Lauf ${payload.prompt_run_id}`,
12491
+ title: istText6(payload.title) ? payload.title : `Batch aus Lauf ${payload.prompt_run_id}`,
11709
12492
  images: bilder
11710
12493
  });
11711
12494
  continue;
@@ -11980,7 +12763,7 @@ function createFlowSzenenViewPort(options) {
11980
12763
  ...options.now ? { now: options.now } : {}
11981
12764
  }) : void 0;
11982
12765
  const bildProvider = options.imageProvider ?? (options.generateImage ? createFlowImageProvider(options.generateImage) : void 0);
11983
- const bildFaehigkeit = options.imageCapability ?? { environment_id: "flow", execution_paths: [FLOW_EXECUTION_PATH] };
12766
+ const bildFaehigkeit = options.imageCapability ?? { environment_id: "flow", execution_paths: [{ execution_path: FLOW_EXECUTION_PATH, provider: "google" }] };
11984
12767
  const imageGeneration = bildProvider && imageBatchWrite ? createImageGenerationPort({
11985
12768
  runs: { async getRun(runId) {
11986
12769
  return runs.getRun(runId);
@@ -12132,43 +12915,85 @@ var FAILURE_CLASSES = [
12132
12915
  ];
12133
12916
  var PFLICHTFELDER = [
12134
12917
  "run_id",
12135
- "shot_version_id",
12136
- "shot_element_id",
12137
- "scene_setup_version_id",
12138
- "camera_id",
12139
- "target_model_version_id",
12140
12918
  "provider",
12141
12919
  "model_id",
12142
12920
  "system_prompt",
12143
12921
  "created_at"
12144
12922
  ];
12145
- function istText6(wert) {
12923
+ var TEXTFELDER = [
12924
+ "shot_version_id",
12925
+ "shot_element_id",
12926
+ "scene_setup_version_id",
12927
+ "camera_id",
12928
+ "target_model_version_id"
12929
+ ];
12930
+ var PFLICHT_JE_HERKUNFT = {
12931
+ published: ["shot_version_id", "shot_element_id", "scene_setup_version_id"],
12932
+ draft_chain: ["shot_element_id"],
12933
+ scene_direct: []
12934
+ };
12935
+ var VERBOTEN_JE_HERKUNFT = {
12936
+ published: [],
12937
+ draft_chain: ["shot_version_id", "scene_setup_version_id"],
12938
+ scene_direct: ["shot_version_id", "shot_element_id", "scene_setup_version_id", "camera_id"]
12939
+ };
12940
+ function istText7(wert) {
12146
12941
  return typeof wert === "string" && wert.trim().length > 0;
12147
12942
  }
12148
- function isObject11(wert) {
12943
+ function isObject12(wert) {
12149
12944
  return typeof wert === "object" && wert !== null && !Array.isArray(wert);
12150
12945
  }
12151
- var VERSIONSFELDER = ["shot_version_id", "scene_setup_version_id"];
12152
12946
  var KETTEN_TYPEN = ["scene", "scene_setup", "shot"];
12947
+ var HERKUENFTE = ["published", "draft_chain", "scene_direct"];
12153
12948
  function pruefeLaufherkunft(quelle) {
12154
- if (!isObject11(quelle)) {
12949
+ if (!isObject12(quelle)) {
12155
12950
  return { ok: false, message: '"source" muss ein Objekt sein.' };
12156
12951
  }
12952
+ if (!HERKUENFTE.includes(quelle.kind)) {
12953
+ return {
12954
+ ok: false,
12955
+ message: `"source.kind" ist unbekannt: ${JSON.stringify(quelle.kind)}. Erlaubt: ${HERKUENFTE.join(", ")}.`
12956
+ };
12957
+ }
12157
12958
  if (quelle.kind === "published") {
12158
- for (const feld of VERSIONSFELDER) {
12159
- if (!istText6(quelle[feld])) {
12959
+ for (const feld of ["shot_version_id", "scene_setup_version_id"]) {
12960
+ if (!istText7(quelle[feld])) {
12160
12961
  return { ok: false, message: `"source.${feld}" fehlt oder ist leer.` };
12161
12962
  }
12162
12963
  }
12163
12964
  return { ok: true };
12164
12965
  }
12165
- if (quelle.kind !== "draft_chain") {
12166
- return {
12167
- ok: false,
12168
- message: `"source.kind" ist unbekannt: ${JSON.stringify(quelle.kind)}. Erlaubt: published, draft_chain.`
12169
- };
12966
+ if (quelle.kind === "scene_direct") {
12967
+ const szene = quelle.scene;
12968
+ if (!isObject12(szene)) {
12969
+ return { ok: false, message: '"source.scene" muss ein Objekt sein.' };
12970
+ }
12971
+ if (!istText7(szene.element_id)) {
12972
+ return { ok: false, message: '"source.scene.element_id" fehlt oder ist leer.' };
12973
+ }
12974
+ const ausEntwurf = istText7(szene.draft_id);
12975
+ const ausVersion = istText7(szene.version_id);
12976
+ if (ausEntwurf === ausVersion) {
12977
+ return {
12978
+ ok: false,
12979
+ message: ausEntwurf ? '"source.scene" nennt draft_id und version_id zugleich. Eingefroren wurde genau ein Stand (D-135).' : '"source.scene" nennt weder draft_id noch version_id. Ohne den eingefrorenen Stand ist der Lauf nicht nachvollziehbar (D-97).'
12980
+ };
12981
+ }
12982
+ if (ausEntwurf && (typeof szene.revision !== "number" || !Number.isInteger(szene.revision))) {
12983
+ return {
12984
+ ok: false,
12985
+ message: '"source.scene.revision" muss eine ganze Zahl sein \u2014 ohne sie ist der eingefrorene Entwurfsstand nicht benennbar (D-97).'
12986
+ };
12987
+ }
12988
+ if (ausVersion && szene.revision !== null && szene.revision !== void 0) {
12989
+ return {
12990
+ ok: false,
12991
+ message: '"source.scene.revision" ist bei einer veroeffentlichten Version gesetzt. Eine Version ist unveraenderlich und hat keine Revision (D-02).'
12992
+ };
12993
+ }
12994
+ return { ok: true };
12170
12995
  }
12171
- if (!istText6(quelle.chain_id)) {
12996
+ if (!istText7(quelle.chain_id)) {
12172
12997
  return { ok: false, message: '"source.chain_id" fehlt oder ist leer.' };
12173
12998
  }
12174
12999
  if (!Array.isArray(quelle.nodes) || quelle.nodes.length === 0) {
@@ -12178,7 +13003,7 @@ function pruefeLaufherkunft(quelle) {
12178
13003
  };
12179
13004
  }
12180
13005
  for (const [index, knoten] of quelle.nodes.entries()) {
12181
- if (!isObject11(knoten)) {
13006
+ if (!isObject12(knoten)) {
12182
13007
  return { ok: false, message: `"source.nodes[${index}]" ist kein Objekt.` };
12183
13008
  }
12184
13009
  if (!KETTEN_TYPEN.includes(knoten.type)) {
@@ -12187,7 +13012,7 @@ function pruefeLaufherkunft(quelle) {
12187
13012
  message: `"source.nodes[${index}].type" ist unbekannt: ${JSON.stringify(knoten.type)}.`
12188
13013
  };
12189
13014
  }
12190
- if (!istText6(knoten.element_id) || !istText6(knoten.draft_id)) {
13015
+ if (!istText7(knoten.element_id) || !istText7(knoten.draft_id)) {
12191
13016
  return {
12192
13017
  ok: false,
12193
13018
  message: `"source.nodes[${index}]" braucht element_id und draft_id.`
@@ -12200,10 +13025,10 @@ function pruefeLaufherkunft(quelle) {
12200
13025
  };
12201
13026
  }
12202
13027
  }
12203
- if (quelle.derivation_run_id !== void 0 && !istText6(quelle.derivation_run_id)) {
13028
+ if (quelle.derivation_run_id !== void 0 && !istText7(quelle.derivation_run_id)) {
12204
13029
  return { ok: false, message: '"source.derivation_run_id" ist gesetzt, aber leer.' };
12205
13030
  }
12206
- if (quelle.derivation_recovery_id !== void 0 && !istText6(quelle.derivation_recovery_id)) {
13031
+ if (quelle.derivation_recovery_id !== void 0 && !istText7(quelle.derivation_recovery_id)) {
12207
13032
  return { ok: false, message: '"source.derivation_recovery_id" ist gesetzt, aber leer.' };
12208
13033
  }
12209
13034
  return { ok: true };
@@ -12217,25 +13042,38 @@ function checkPromptRunRecord(candidate) {
12217
13042
  }
12218
13043
  const record = candidate;
12219
13044
  const quelle = record.source;
12220
- const ausKette = isObject11(quelle) && quelle.kind === "draft_chain";
12221
13045
  if (quelle !== void 0) {
12222
13046
  const urteil2 = pruefeLaufherkunft(quelle);
12223
13047
  if (!urteil2.ok) return urteil2;
12224
13048
  }
12225
- const pflicht = ausKette ? PFLICHTFELDER.filter((f) => !VERSIONSFELDER.includes(f)) : PFLICHTFELDER;
12226
- for (const feld of pflicht) {
12227
- if (!istText6(record[feld])) {
13049
+ const herkunft = isObject12(quelle) && typeof quelle.kind === "string" ? quelle.kind : "published";
13050
+ for (const feld of PFLICHTFELDER) {
13051
+ if (!istText7(record[feld])) {
12228
13052
  return { ok: false, message: `"${feld}" fehlt oder ist leer.` };
12229
13053
  }
12230
13054
  }
12231
- if (ausKette) {
12232
- for (const feld of VERSIONSFELDER) {
12233
- if (istText6(record[feld])) {
12234
- return {
12235
- ok: false,
12236
- message: `"${feld}" ist bei einem Lauf aus einer Draft-Kette gesetzt. Dort gibt es keine Versionen, und erfundene sind unzulaessig (D-98).`
12237
- };
12238
- }
13055
+ for (const feld of TEXTFELDER) {
13056
+ if (typeof record[feld] !== "string") {
13057
+ return {
13058
+ ok: false,
13059
+ message: `"${feld}" fehlt. Wo es keinen Wert gibt, gehoert eine leere Zeichenkette hin \u2014 kein fehlendes Feld (D-135).`
13060
+ };
13061
+ }
13062
+ }
13063
+ for (const feld of PFLICHT_JE_HERKUNFT[herkunft] ?? []) {
13064
+ if (!istText7(record[feld])) {
13065
+ return {
13066
+ ok: false,
13067
+ message: `"${feld}" fehlt oder ist leer, wird bei Herkunft "${herkunft}" aber gebraucht.`
13068
+ };
13069
+ }
13070
+ }
13071
+ for (const feld of VERBOTEN_JE_HERKUNFT[herkunft] ?? []) {
13072
+ if (istText7(record[feld])) {
13073
+ return {
13074
+ ok: false,
13075
+ message: `"${feld}" ist bei einem Lauf der Herkunft "${herkunft}" gesetzt. Dort gibt es diesen Wert nicht, und erfundene sind unzulaessig (D-98/D-135).`
13076
+ };
12239
13077
  }
12240
13078
  }
12241
13079
  if (record.status !== "succeeded" && record.status !== "failed") {
@@ -12268,7 +13106,7 @@ function checkPromptRunRecord(candidate) {
12268
13106
  message: `"failure.class" ist unbekannt: ${JSON.stringify(failure.class)}. Erlaubt sind ${FAILURE_CLASSES.join(", ")}.`
12269
13107
  };
12270
13108
  }
12271
- if (!istText6(failure.message)) {
13109
+ if (!istText7(failure.message)) {
12272
13110
  return { ok: false, message: '"failure.message" fehlt oder ist leer.' };
12273
13111
  }
12274
13112
  return { ok: true };
@@ -12310,7 +13148,7 @@ function checkSceneDerivationRecord(candidate) {
12310
13148
  }
12311
13149
  const record = candidate;
12312
13150
  for (const feld of DERIVATION_PFLICHTFELDER) {
12313
- if (!istText6(record[feld])) {
13151
+ if (!istText7(record[feld])) {
12314
13152
  return { ok: false, message: `"${feld}" fehlt oder ist leer.` };
12315
13153
  }
12316
13154
  }
@@ -12365,13 +13203,13 @@ function checkSceneDerivationRecord(candidate) {
12365
13203
  return { ok: false, message: '"follows" muss ein Objekt sein.' };
12366
13204
  }
12367
13205
  const f = folgt;
12368
- if (!istText6(f.run_id)) {
13206
+ if (!istText7(f.run_id)) {
12369
13207
  return { ok: false, message: '"follows.run_id" fehlt oder ist leer.' };
12370
13208
  }
12371
13209
  if (f.run_id === record.run_id) {
12372
13210
  return { ok: false, message: '"follows.run_id" zeigt auf den Lauf selbst.' };
12373
13211
  }
12374
- if (!istText6(f.note)) {
13212
+ if (!istText7(f.note)) {
12375
13213
  return {
12376
13214
  ok: false,
12377
13215
  message: '"follows.note" fehlt oder ist leer \u2014 ohne Beanstandung ist ein Folgelauf nicht nachvollziehbar (D-89).'
@@ -12385,7 +13223,7 @@ function checkSceneDerivationRecord(candidate) {
12385
13223
  }
12386
13224
  }
12387
13225
  const reply = record.reply;
12388
- if (reply !== void 0 && !istText6(reply)) {
13226
+ if (reply !== void 0 && !istText7(reply)) {
12389
13227
  return {
12390
13228
  ok: false,
12391
13229
  message: `"reply" ist unbrauchbar: erwartet wird eine nicht leere Zeichenkette, angekommen ist ${JSON.stringify(reply)}. Ohne "reply" einreichen ist zulaessig.`
@@ -12405,7 +13243,7 @@ function checkSceneDerivationRecord(candidate) {
12405
13243
  message: `"failure.class" ist unbekannt: ${JSON.stringify(failure.class)}. Erlaubt sind ${FAILURE_CLASSES.join(", ")}.`
12406
13244
  };
12407
13245
  }
12408
- if (!istText6(failure.message)) {
13246
+ if (!istText7(failure.message)) {
12409
13247
  return { ok: false, message: '"failure.message" fehlt oder ist leer.' };
12410
13248
  }
12411
13249
  if (Array.isArray(record.proposals) && record.proposals.length > 0) {
@@ -12460,7 +13298,7 @@ function checkSceneDerivationRecord(candidate) {
12460
13298
  };
12461
13299
  }
12462
13300
  for (const [gapIndex, luecke] of gaps.entries()) {
12463
- if (!luecke || typeof luecke !== "object" || !istText6(luecke.pointer) || !istText6(luecke.reason)) {
13301
+ if (!luecke || typeof luecke !== "object" || !istText7(luecke.pointer) || !istText7(luecke.reason)) {
12464
13302
  return {
12465
13303
  ok: false,
12466
13304
  message: `"proposals[${index}].gaps[${gapIndex}]" braucht pointer und reason.`
@@ -12486,7 +13324,7 @@ function checkSceneDerivationRecoveryRecord(candidate) {
12486
13324
  }
12487
13325
  const record = candidate;
12488
13326
  for (const feld of ["recovery_id", "run_id", "created_at"]) {
12489
- if (!istText6(record[feld])) {
13327
+ if (!istText7(record[feld])) {
12490
13328
  return { ok: false, message: `"${feld}" fehlt oder ist leer.` };
12491
13329
  }
12492
13330
  }
@@ -12513,7 +13351,7 @@ function checkSceneDerivationRecoveryRecord(candidate) {
12513
13351
  return { ok: false, message: '"diagnostics" muss ein Array sein (auch ein leeres).' };
12514
13352
  }
12515
13353
  for (const [index, befund] of record.diagnostics.entries()) {
12516
- if (!befund || typeof befund !== "object" || !istText6(befund.code) || !istText6(befund.message)) {
13354
+ if (!befund || typeof befund !== "object" || !istText7(befund.code) || !istText7(befund.message)) {
12517
13355
  return {
12518
13356
  ok: false,
12519
13357
  message: `"diagnostics[${index}]" braucht code und message.`
@@ -12565,7 +13403,7 @@ function checkSceneDerivationRecoveryRecord(candidate) {
12565
13403
  }
12566
13404
  if (Array.isArray(gaps)) {
12567
13405
  for (const [gapIndex, luecke] of gaps.entries()) {
12568
- if (!luecke || typeof luecke !== "object" || !istText6(luecke.pointer) || !istText6(luecke.reason)) {
13406
+ if (!luecke || typeof luecke !== "object" || !istText7(luecke.pointer) || !istText7(luecke.reason)) {
12569
13407
  return {
12570
13408
  ok: false,
12571
13409
  message: `"proposals[${index}].gaps[${gapIndex}]" braucht pointer und reason.`
@@ -12602,9 +13440,15 @@ function checkSceneDerivationRecoveryRecord(candidate) {
12602
13440
  }
12603
13441
 
12604
13442
  // src/features/szenen/SzenenView.tsx
12605
- var import_react15 = require("react");
13443
+ var import_react16 = require("react");
12606
13444
 
12607
13445
  // src/features/szenen/szenenViewModel.ts
13446
+ var KEINE_BAUSTEINE = [
13447
+ "scene",
13448
+ "scene_setup",
13449
+ "shot"
13450
+ ];
13451
+ var BAUSTEIN_TYPES = SZENEN_ELEMENT_TYPES.filter((typ) => !KEINE_BAUSTEINE.includes(typ));
12608
13452
  var ALLE_BEREICHE = [
12609
13453
  {
12610
13454
  id: "derive",
@@ -12689,7 +13533,7 @@ function noticeFor(ergebnis, was) {
12689
13533
  }
12690
13534
 
12691
13535
  // src/features/szenen/areas/ElementsArea.tsx
12692
- var import_react10 = require("react");
13536
+ var import_react11 = require("react");
12693
13537
 
12694
13538
  // src/features/szenen/areas/usePortQuery.ts
12695
13539
  var import_react = require("react");
@@ -12723,6 +13567,32 @@ function usePortQuery(frage, abhaengigkeiten) {
12723
13567
  // src/features/szenen/areas/ElementDetail.tsx
12724
13568
  var import_react9 = require("react");
12725
13569
 
13570
+ // src/features/szenen/elementLabels.ts
13571
+ var LABELS = {
13572
+ scene_component: { singular: "Szenenbaustein", plural: "Szenenbausteine" },
13573
+ scene: { singular: "Szene", plural: "Szenen" },
13574
+ shot_concept: { singular: "Prompt-Idee", plural: "Prompt-Ideen" },
13575
+ camera_preset: { singular: "Kamera", plural: "Kameras" },
13576
+ camera_pattern: { singular: "Kamera-Muster", plural: "Kamera-Muster" },
13577
+ style: { singular: "Stil", plural: "Stile" },
13578
+ scene_setup: { singular: "Bildaufbau", plural: "Bildaufbauten" },
13579
+ shot: { singular: "Prompt", plural: "Prompts" },
13580
+ prompt_policy: { singular: "Prompt-Regel", plural: "Prompt-Regeln" },
13581
+ prompt_fragment: { singular: "Prompt-Baustein", plural: "Prompt-Bausteine" },
13582
+ target_model: { singular: "Zielmodell", plural: "Zielmodelle" },
13583
+ sequence: { singular: "Sequenz", plural: "Sequenzen" },
13584
+ scene_derivation_policy: {
13585
+ singular: "Rekonstruktions-Regel",
13586
+ plural: "Rekonstruktions-Regeln"
13587
+ }
13588
+ };
13589
+ function elementTypLabel(typ) {
13590
+ return LABELS[typ]?.singular ?? typ;
13591
+ }
13592
+ function elementTypPlural(typ) {
13593
+ return LABELS[typ]?.plural ?? typ;
13594
+ }
13595
+
12726
13596
  // src/features/szenen/schemaForm.ts
12727
13597
  var FORM_JE_REF = {
12728
13598
  "common.schema.json#/$defs/slug": "slug",
@@ -13018,7 +13888,7 @@ var SEMANTIK = {
13018
13888
  function feldSemantik(type, pfad) {
13019
13889
  return SEMANTIK[`${type}.${pfad}`] ?? null;
13020
13890
  }
13021
- var LABELS = {
13891
+ var LABELS2 = {
13022
13892
  key: "Kennung",
13023
13893
  name: "Name",
13024
13894
  text: "Text",
@@ -13063,13 +13933,13 @@ var LABELS = {
13063
13933
  properties: "Zusatzangaben"
13064
13934
  };
13065
13935
  function feldLabel(type, pfad, name) {
13066
- return feldSemantik(type, pfad)?.label ?? LABELS[name] ?? name;
13936
+ return feldSemantik(type, pfad)?.label ?? LABELS2[name] ?? name;
13067
13937
  }
13068
13938
 
13069
13939
  // src/features/szenen/areas/SchemaFields.tsx
13070
13940
  var import_jsx_runtime = require("react/jsx-runtime");
13071
- var isObject12 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
13072
- var istVersionRef = (v) => isObject12(v) && typeof v.element_id === "string" && typeof v.version_id === "string";
13941
+ var isObject13 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
13942
+ var istVersionRef = (v) => isObject13(v) && typeof v.element_id === "string" && typeof v.version_id === "string";
13073
13943
  function leseJson(wert) {
13074
13944
  if (!wert || wert === NULL_MARKER) return null;
13075
13945
  try {
@@ -13297,13 +14167,13 @@ var VersionRefListeFeld = ({ port, kontextkette, feld, wert, onChange, readOnly,
13297
14167
  }
13298
14168
  const schreiben = (neu) => onChange(
13299
14169
  feld.path,
13300
- schreibListe(neu.map((eintrag, i) => isObject12(eintrag) && mitRolle ? { ...eintrag, rank: i } : eintrag))
14170
+ schreibListe(neu.map((eintrag, i) => isObject13(eintrag) && mitRolle ? { ...eintrag, rank: i } : eintrag))
13301
14171
  );
13302
14172
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "szenen-feld__reflist", "data-testid": `reflist-${feld.path}`, children: [
13303
14173
  liste.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { "data-testid": `reflist-${feld.path}-leer`, children: "Keine Eintr\xE4ge." }),
13304
14174
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { children: liste.map((eintrag, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { "data-testid": `reflist-${feld.path}-${index}`, children: [
13305
14175
  istVersionRef(eintrag) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RefAnzeige, { port, ref_: eintrag, testId: `reflist-${feld.path}-${index}-ref` }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: JSON.stringify(eintrag) }),
13306
- mitRolle === "styleRole" && isObject12(eintrag) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
14176
+ mitRolle === "styleRole" && isObject13(eintrag) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
13307
14177
  "select",
13308
14178
  {
13309
14179
  "data-testid": `reflist-${feld.path}-${index}-role`,
@@ -13316,7 +14186,7 @@ var VersionRefListeFeld = ({ port, kontextkette, feld, wert, onChange, readOnly,
13316
14186
  ]
13317
14187
  }
13318
14188
  ),
13319
- mitRolle === "frei" && isObject12(eintrag) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
14189
+ mitRolle === "frei" && isObject13(eintrag) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
13320
14190
  "input",
13321
14191
  {
13322
14192
  "data-testid": `reflist-${feld.path}-${index}-role`,
@@ -13326,7 +14196,7 @@ var VersionRefListeFeld = ({ port, kontextkette, feld, wert, onChange, readOnly,
13326
14196
  onChange: (e) => schreiben(liste.map((x, i) => i === index ? { ...x, role: e.target.value } : x))
13327
14197
  }
13328
14198
  ),
13329
- mitGewicht && isObject12(eintrag) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
14199
+ mitGewicht && isObject13(eintrag) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
13330
14200
  "input",
13331
14201
  {
13332
14202
  "data-testid": `reflist-${feld.path}-${index}-weight`,
@@ -13557,7 +14427,7 @@ var KameraFeld = ({
13557
14427
  if (phase.status !== "ready" || !phase.value) return [];
13558
14428
  const cameras = valueAtPath(phase.value.payload, "camera_rig.cameras");
13559
14429
  if (!Array.isArray(cameras)) return [];
13560
- return cameras.filter(isObject12).map((c) => ({
14430
+ return cameras.filter(isObject13).map((c) => ({
13561
14431
  id: typeof c.camera_id === "string" ? c.camera_id : null,
13562
14432
  name: typeof c.name === "string" ? c.name : null
13563
14433
  })).filter((c) => c.id !== null);
@@ -13702,11 +14572,11 @@ var MediaRefListeFeld = ({ feld, wert, onChange, readOnly }) => {
13702
14572
  }
13703
14573
  const schreiben = (neu) => onChange(
13704
14574
  feld.path,
13705
- schreibListe(neu.map((e, i) => isObject12(e) ? { ...e, position: i } : e))
14575
+ schreibListe(neu.map((e, i) => isObject13(e) ? { ...e, position: i } : e))
13706
14576
  );
13707
14577
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "szenen-feld__liste", "data-testid": `feld-${feld.path}`, children: [
13708
14578
  liste.map((eintrag, index) => {
13709
- const objekt = isObject12(eintrag) ? eintrag : {};
14579
+ const objekt = isObject13(eintrag) ? eintrag : {};
13710
14580
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "szenen-feld__liste-zeile", children: [
13711
14581
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
13712
14582
  "input",
@@ -13715,7 +14585,7 @@ var MediaRefListeFeld = ({ feld, wert, onChange, readOnly }) => {
13715
14585
  value: typeof objekt.asset_id === "string" ? objekt.asset_id : "",
13716
14586
  readOnly,
13717
14587
  placeholder: "asset_id (UUID)",
13718
- onChange: (e) => schreiben(liste.map((x, i) => i === index ? { ...isObject12(x) ? x : {}, asset_id: e.target.value } : x))
14588
+ onChange: (e) => schreiben(liste.map((x, i) => i === index ? { ...isObject13(x) ? x : {}, asset_id: e.target.value } : x))
13719
14589
  }
13720
14590
  ),
13721
14591
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
@@ -13724,7 +14594,7 @@ var MediaRefListeFeld = ({ feld, wert, onChange, readOnly }) => {
13724
14594
  "data-testid": `feld-${feld.path}-${index}-rolle`,
13725
14595
  value: typeof objekt.role === "string" ? objekt.role : "",
13726
14596
  disabled: readOnly,
13727
- onChange: (e) => schreiben(liste.map((x, i) => i === index ? { ...isObject12(x) ? x : {}, role: e.target.value } : x)),
14597
+ onChange: (e) => schreiben(liste.map((x, i) => i === index ? { ...isObject13(x) ? x : {}, role: e.target.value } : x)),
13728
14598
  children: [
13729
14599
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: "(Rolle w\xE4hlen)" }),
13730
14600
  rollen.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: r, children: r }, r))
@@ -13767,9 +14637,10 @@ var LEERES_PROFIL = {
13767
14637
  var listeAus = (text) => text.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
13768
14638
  var AusfuehrungsprofileFeld = ({ feld, wert, onChange, readOnly }) => {
13769
14639
  const gelesen = leseListe(wert);
13770
- const profile = (gelesen ?? []).filter(isObject12);
14640
+ const profile = (gelesen ?? []).filter(isObject13);
13771
14641
  const unlesbar = gelesen === null;
13772
14642
  const schreibe = (neu) => onChange(feld.path, schreibListe(neu));
14643
+ const alleBefunde = checkImageExecutionProfiles(profile);
13773
14644
  const aendere = (index, patch) => schreibe(profile.map((p, i) => i === index ? { ...p, ...patch } : p));
13774
14645
  if (unlesbar) {
13775
14646
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", "data-testid": `feld-${feld.path}-unlesbar`, children: "Der gespeicherte Wert ist keine Profilliste. Er bleibt unver\xE4ndert stehen; bitte an der Quelle korrigieren." });
@@ -13777,7 +14648,7 @@ var AusfuehrungsprofileFeld = ({ feld, wert, onChange, readOnly }) => {
13777
14648
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "szenen-feld__profile", "data-testid": `feld-${feld.path}`, children: [
13778
14649
  profile.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { "data-testid": `feld-${feld.path}-keine`, children: "Dieses Zielmodell wird f\xFCr die Prompt-Optimierung verwendet und ist derzeit nicht direkt aus der App aufrufbar." }),
13779
14650
  profile.map((profil, index) => {
13780
- const befunde = checkImageExecutionProfile(profil);
14651
+ const befunde = alleBefunde[index] ?? [];
13781
14652
  const text = (schluessel) => typeof profil[schluessel] === "string" ? profil[schluessel] : "";
13782
14653
  const liste = (schluessel) => Array.isArray(profil[schluessel]) ? profil[schluessel].join(", ") : "";
13783
14654
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("fieldset", { "data-testid": `profil-${index}`, className: "szenen-feld__profil", children: [
@@ -14528,60 +15399,72 @@ var CapabilityPanel = ({ port, elementId, setupPayload }) => {
14528
15399
  ] });
14529
15400
  };
14530
15401
 
14531
- // src/features/szenen/areas/ChainPromptAction.tsx
15402
+ // src/features/szenen/areas/PromptStartAction.tsx
14532
15403
  var import_react6 = require("react");
14533
15404
  var import_jsx_runtime5 = require("react/jsx-runtime");
14534
- var ChainPromptAction = ({
15405
+ var PromptStartAction = ({
14535
15406
  port,
14536
- shotElementId
15407
+ source,
15408
+ hinweise = [],
15409
+ blocker = null,
15410
+ onLauf
14537
15411
  }) => {
14538
15412
  const [lauf, setLauf] = (0, import_react6.useState)(null);
14539
15413
  const [meldung, setMeldung] = (0, import_react6.useState)(null);
14540
15414
  const [ausgang, setAusgang] = (0, import_react6.useState)(null);
14541
15415
  const [laeuft, setLaeuft] = (0, import_react6.useState)(false);
14542
15416
  const runs = port.runs;
15417
+ const ausKette = source.kind === "draft_chain";
15418
+ const id = (name) => ausKette ? `kette-${name}` : `szene-${name}`;
14543
15419
  async function erzeugen() {
14544
15420
  setLaeuft(true);
14545
15421
  setMeldung(null);
14546
15422
  setAusgang(null);
14547
15423
  try {
14548
- const ergebnis = await runs.startRunFromDraft({ shotElementId });
15424
+ const ergebnis = source.kind === "draft_chain" ? await runs.startRunFromDraft({ shotElementId: source.shotElementId }) : await runs.startRunFromScene({ sceneElementId: source.sceneElementId });
14549
15425
  if (ergebnis.kind !== "ok") {
14550
15426
  setMeldung(ergebnis.kind === "unsupported" ? `Dieser Host kann "${ergebnis.capability}" nicht.` : ergebnis.message);
14551
15427
  setAusgang(ergebnis.kind === "unsupported" ? "error" : ergebnis.kind);
14552
15428
  return;
14553
15429
  }
14554
15430
  setLauf(ergebnis.value);
15431
+ onLauf?.();
14555
15432
  } finally {
14556
15433
  setLaeuft(false);
14557
15434
  }
14558
15435
  }
14559
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { className: "szenen-kette", "data-testid": "kette-prompt-aktion", children: [
14560
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h5", { children: "Prompt aus der Draft-Kette" }),
14561
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "szenen-area__lead", children: [
15436
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { className: "szenen-kette", "data-testid": id("prompt-aktion"), children: [
15437
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h5", { children: ausKette ? "Prompt aus der Draft-Kette" : "Prompt aus dieser Szene" }),
15438
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "szenen-area__lead", children: ausKette ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
14562
15439
  "Dieser Shot geh\xF6rt zu einer Draft-Kette. Der Lauf friert Szene, Aufbau und Shot in dem Stand ein, den er gerade vorfindet \u2014",
14563
15440
  " ",
14564
15441
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("strong", { children: "ohne" }),
14565
15442
  " dass etwas ver\xF6ffentlicht werden muss."
14566
- ] }),
15443
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
15444
+ "Der Lauf friert diese Szene in dem Stand ein, den er gerade vorfindet. Bildaufbau, Kamera und Zielmodell sind ",
15445
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("strong", { children: "nicht n\xF6tig" }),
15446
+ " \u2014 was fehlt, wird weggelassen und am Lauf festgehalten, nicht erg\xE4nzt."
15447
+ ] }) }),
15448
+ hinweise.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ul", { className: "szenen-detail__hint", "data-testid": id("hinweise"), role: "status", children: hinweise.map((hinweis, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("li", { children: hinweis }, i)) }),
15449
+ blocker && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "szenen-error", role: "alert", "data-testid": id("blocker"), children: blocker }),
14567
15450
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
14568
15451
  "button",
14569
15452
  {
14570
15453
  type: "button",
14571
- disabled: laeuft,
15454
+ disabled: laeuft || blocker !== null,
14572
15455
  onClick: () => void erzeugen(),
14573
- "data-testid": "kette-prompt",
14574
- children: laeuft ? "Pr\xFCft und erzeugt \u2026" : "Draft-Kette pr\xFCfen und Prompt erzeugen (kostet einen Modellaufruf)"
15456
+ "data-testid": id("prompt"),
15457
+ children: laeuft ? "Pr\xFCft und erzeugt \u2026" : ausKette ? "Draft-Kette pr\xFCfen und Prompt erzeugen (kostet einen Modellaufruf)" : "Prompt erzeugen (kostet einen Modellaufruf)"
14575
15458
  }
14576
15459
  ),
14577
- meldung && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "szenen-error", role: "alert", "data-testid": "kette-meldung", children: [
15460
+ meldung && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "szenen-error", role: "alert", "data-testid": id("meldung"), children: [
14578
15461
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: meldung }),
14579
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { "data-testid": "kette-kein-modell", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("strong", { children: "Es wurde kein Modell gerufen." }) }),
14580
- ausgang === "conflict" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { "data-testid": "kette-konflikt", children: "Ein Entwurf der Kette wurde zwischenzeitlich ge\xE4ndert. Ein erneuter Versuch friert den aktuellen Stand ein." }),
14581
- ausgang === "rejected" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { "data-testid": "kette-mangel", children: "Die Kette selbst ist noch nicht vollst\xE4ndig. Ein erneuter Versuch \xE4ndert daran nichts \u2014 die Entw\xFCrfe m\xFCssen zuerst erg\xE4nzt werden." }),
14582
- ausgang === "error" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { "data-testid": "kette-technisch", children: "Das war ein technisches Problem beim Zugriff, kein Mangel der Kette. Ein erneuter Versuch ist sinnvoll." })
15462
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { "data-testid": id("kein-modell"), children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("strong", { children: "Es wurde kein Modell gerufen." }) }),
15463
+ ausgang === "conflict" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { "data-testid": id("konflikt"), children: ausKette ? "Ein Entwurf der Kette wurde zwischenzeitlich ge\xE4ndert. Ein erneuter Versuch friert den aktuellen Stand ein." : "Die Szene wurde zwischenzeitlich ge\xE4ndert. Ein erneuter Versuch friert den aktuellen Stand ein." }),
15464
+ ausgang === "rejected" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { "data-testid": id("mangel"), children: ausKette ? "Die Kette selbst ist noch nicht vollst\xE4ndig. Ein erneuter Versuch \xE4ndert daran nichts \u2014 die Entw\xFCrfe m\xFCssen zuerst erg\xE4nzt werden." : "Aus diesem Stand l\xE4sst sich kein Auftrag herstellen. Ein erneuter Versuch \xE4ndert daran nichts \u2014 die Szene muss zuerst Inhalt bekommen." }),
15465
+ ausgang === "error" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { "data-testid": id("technisch"), children: "Das war ein technisches Problem beim Zugriff, kein fachlicher Mangel. Ein erneuter Versuch ist sinnvoll." })
14583
15466
  ] }),
14584
- lauf && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "szenen-run", "data-testid": "kette-lauf", children: [
15467
+ lauf && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "szenen-run", "data-testid": id("lauf"), children: [
14585
15468
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("h5", { children: [
14586
15469
  "Prompt-Lauf ",
14587
15470
  lauf.status === "succeeded" ? "gelungen" : "gescheitert"
@@ -14593,7 +15476,7 @@ var ChainPromptAction = ({
14593
15476
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("code", { children: lauf.model_id }),
14594
15477
  "."
14595
15478
  ] }),
14596
- lauf.status === "succeeded" && lauf.output && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("pre", { className: "szenen-proposal__payload", "data-testid": "kette-prompt-text", children: lauf.output.positive }),
15479
+ lauf.status === "succeeded" && lauf.output && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("pre", { className: "szenen-proposal__payload", "data-testid": id("prompt-text"), children: lauf.output.positive }),
14597
15480
  lauf.status === "failed" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "szenen-error", children: [
14598
15481
  lauf.failure?.class,
14599
15482
  " \u2014 ",
@@ -14763,12 +15646,27 @@ var RunImagesPanel = ({ port, lauf }) => {
14763
15646
  "Lauf ",
14764
15647
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { "data-testid": `eingang-lauf-${lauf.run_id}`, children: lauf.run_id })
14765
15648
  ] }),
14766
- quelle.kind === "published" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { "data-testid": `eingang-herkunft-${lauf.run_id}`, children: [
15649
+ quelle.kind === "published" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { "data-testid": `eingang-herkunft-${lauf.run_id}`, children: [
14767
15650
  "ver\xF6ffentlichte Shot-Version ",
14768
15651
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { children: quelle.shot_version_id })
14769
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { "data-testid": `eingang-herkunft-${lauf.run_id}`, children: [
15652
+ ] }),
15653
+ quelle.kind === "draft_chain" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { "data-testid": `eingang-herkunft-${lauf.run_id}`, children: [
14770
15654
  "Draft-Kette ",
14771
15655
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { children: quelle.chain_id })
15656
+ ] }),
15657
+ quelle.kind === "scene_direct" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { "data-testid": `eingang-herkunft-${lauf.run_id}`, children: [
15658
+ "Szene ",
15659
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { children: quelle.scene.element_id }),
15660
+ quelle.scene.draft_id ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
15661
+ " \xB7 Entwurf ",
15662
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { children: quelle.scene.draft_id }),
15663
+ " (Revision ",
15664
+ quelle.scene.revision,
15665
+ ")"
15666
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
15667
+ " \xB7 Version ",
15668
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { children: quelle.scene.version_id })
15669
+ ] })
14772
15670
  ] })
14773
15671
  ] }),
14774
15672
  quelle.kind === "draft_chain" && quelle.derivation_run_id ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
@@ -15334,9 +16232,13 @@ var RunsPanel = ({ port, source, contextId }) => {
15334
16232
  const [notiz, setNotiz] = (0, import_react8.useState)(null);
15335
16233
  const [nachladen, setNachladen] = (0, import_react8.useState)(0);
15336
16234
  const faehig = capabilitiesOf(port);
15337
- const quellSchluessel = source.kind === "published" ? source.shotVersionId : source.chainId;
16235
+ const quellSchluessel = source.kind === "published" ? source.shotVersionId : source.kind === "draft_chain" ? source.chainId : source.sceneElementId;
15338
16236
  const [phase, neuLaden] = usePortQuery(
15339
- () => source.kind === "published" ? port.runs.listRunsForShot(source.shotVersionId) : port.runs.listRunsForChain(source.chainId),
16237
+ () => {
16238
+ if (source.kind === "published") return port.runs.listRunsForShot(source.shotVersionId);
16239
+ if (source.kind === "draft_chain") return port.runs.listRunsForChain(source.chainId);
16240
+ return port.runs.listRunsForScene(source.sceneElementId);
16241
+ },
15340
16242
  [port, source.kind, quellSchluessel, nachladen]
15341
16243
  );
15342
16244
  const starten = async () => {
@@ -15375,7 +16277,7 @@ var RunsPanel = ({ port, source, contextId }) => {
15375
16277
  "L\xE4ufe konnten nicht geladen werden: ",
15376
16278
  phase.message
15377
16279
  ] }),
15378
- phase.status === "ready" && laeufe.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { "data-testid": "keine-laeufe", children: source.kind === "published" ? "F\xFCr diese Shot-Version gibt es noch keinen Lauf." : "F\xFCr diese Draft-Kette gibt es noch keinen Lauf." }),
16280
+ phase.status === "ready" && laeufe.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { "data-testid": "keine-laeufe", children: source.kind === "published" ? "F\xFCr diese Shot-Version gibt es noch keinen Lauf." : source.kind === "draft_chain" ? "F\xFCr diese Draft-Kette gibt es noch keinen Lauf." : "F\xFCr diese Szene gibt es noch keinen direkten Lauf." }),
15379
16281
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ol", { "data-testid": "runden-liste", children: laeufe.map((lauf) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
15380
16282
  RundenEintrag,
15381
16283
  {
@@ -15580,6 +16482,23 @@ var ElementDetail = ({
15580
16482
  if (!kette2) return null;
15581
16483
  return kette2.links[0]?.field ?? null;
15582
16484
  }, [schnappschuss?.draft?.chain]);
16485
+ const laufStand = (0, import_react9.useMemo)(() => {
16486
+ if (!schnappschuss || schnappschuss.element.type !== "scene") return null;
16487
+ if (schnappschuss.draft) return schnappschuss.draft.payload;
16488
+ const kopf = usableHeadVersionId(kopfAufloesung);
16489
+ if (!kopf) return null;
16490
+ return schnappschuss.versions.find((v) => v.version_id === kopf)?.payload ?? null;
16491
+ }, [schnappschuss, kopfAufloesung]);
16492
+ const szenenBlocker = (0, import_react9.useMemo)(() => {
16493
+ if (!schnappschuss || schnappschuss.element.type !== "scene") return null;
16494
+ if (laufStand === null) {
16495
+ return schnappschuss.versions.length > 0 ? "Diese Szene hat keinen Entwurf und in diesem Kontext keinen bevorzugten Kopf. Welcher Stand gilt, entscheidet keine Reihenfolge (D-36) \u2014 bitte oben eine Version als bevorzugt setzen." : "Diese Szene hat noch keinen gespeicherten Stand. Ein Lauf friert ein, was gespeichert ist \u2014 bitte zuerst den Entwurf speichern.";
16496
+ }
16497
+ const flag = sceneOnlyCapabilities(laufStand).can_render_prompt;
16498
+ if (flag.state === "yes") return null;
16499
+ const grund = flag.reasons[0];
16500
+ return grund && "message" in grund ? grund.message : "Kein Lauf moeglich.";
16501
+ }, [schnappschuss, laufStand]);
15583
16502
  const vorschau = (0, import_react9.useMemo)(() => {
15584
16503
  if (!schnappschuss) return null;
15585
16504
  try {
@@ -15713,8 +16632,7 @@ var ElementDetail = ({
15713
16632
  /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("header", { children: [
15714
16633
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { children: schnappschuss.metadata?.title ?? "(ohne Titel)" }),
15715
16634
  /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("p", { className: "szenen-detail__ids", children: [
15716
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: typ }),
15717
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("code", { children: schnappschuss.element.element_id }),
16635
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { "data-testid": "element-typ", children: elementTypLabel(typ) }),
15718
16636
  schnappschuss.element.retired && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { "data-testid": "ist-stillgelegt", children: "stillgelegt" })
15719
16637
  ] })
15720
16638
  ] }),
@@ -15892,10 +16810,13 @@ var ElementDetail = ({
15892
16810
  // voraus" gilt nur noch fuer den Einzel-Shot.
15893
16811
  draft?.chain ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
15894
16812
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
15895
- ChainPromptAction,
16813
+ PromptStartAction,
15896
16814
  {
15897
16815
  port,
15898
- shotElementId: schnappschuss.element.element_id
16816
+ source: {
16817
+ kind: "draft_chain",
16818
+ shotElementId: schnappschuss.element.element_id
16819
+ }
15899
16820
  }
15900
16821
  ),
15901
16822
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
@@ -15915,6 +16836,30 @@ var ElementDetail = ({
15915
16836
  source: { kind: "published", shotVersionId: laufVersionId }
15916
16837
  }
15917
16838
  ) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { "data-testid": "laufquelle-unklar", children: "Dieser Shot hat mehrere ver\xF6ffentlichte Versionen und keinen bevorzugten Kopf in diesem Kontext. Welche davon einen Lauf tr\xE4gt, entscheidet keine Reihenfolge \u2014 bitte oben eine Version anzeigen oder als bevorzugt setzen." })),
16839
+ typ === "scene" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
16840
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
16841
+ PromptStartAction,
16842
+ {
16843
+ port,
16844
+ source: { kind: "scene", sceneElementId: schnappschuss.element.element_id },
16845
+ hinweise: [
16846
+ "Kein Bildaufbau und keine Kamera \u2014 der Prompt wird ohne Kameravorgabe erzeugt.",
16847
+ "Kein Zielmodell \u2014 generische Promptableitung ohne Modelloptimierung.",
16848
+ schnappschuss.draft ? "Eingefroren wird der gespeicherte Entwurf. Nicht gespeicherte Aenderungen im Formular gehen nicht mit." : "Eingefroren wird die wirksame veroeffentlichte Version."
16849
+ ],
16850
+ blocker: szenenBlocker,
16851
+ onLauf: () => setNachladen((n) => n + 1)
16852
+ }
16853
+ ),
16854
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
16855
+ RunsPanel,
16856
+ {
16857
+ port,
16858
+ contextId,
16859
+ source: { kind: "scene", sceneElementId: schnappschuss.element.element_id }
16860
+ }
16861
+ )
16862
+ ] }),
15918
16863
  typ === "scene_setup" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
15919
16864
  CapabilityPanel,
15920
16865
  {
@@ -15939,6 +16884,26 @@ var ElementDetail = ({
15939
16884
  ] });
15940
16885
  };
15941
16886
 
16887
+ // src/features/szenen/areas/useSchmalesDisplay.ts
16888
+ var import_react10 = require("react");
16889
+ var SCHMALE_ANSICHT = "(max-width: 760px)";
16890
+ function messen(abfrage) {
16891
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
16892
+ return window.matchMedia(abfrage).matches;
16893
+ }
16894
+ function useSchmalesDisplay(abfrage = SCHMALE_ANSICHT) {
16895
+ const [schmal, setSchmal] = (0, import_react10.useState)(() => messen(abfrage));
16896
+ (0, import_react10.useEffect)(() => {
16897
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
16898
+ const liste = window.matchMedia(abfrage);
16899
+ const aktualisieren = () => setSchmal(liste.matches);
16900
+ aktualisieren();
16901
+ liste.addEventListener?.("change", aktualisieren);
16902
+ return () => liste.removeEventListener?.("change", aktualisieren);
16903
+ }, [abfrage]);
16904
+ return schmal;
16905
+ }
16906
+
15942
16907
  // src/features/szenen/areas/ElementsArea.tsx
15943
16908
  var import_jsx_runtime10 = require("react/jsx-runtime");
15944
16909
  var ElementsArea = ({
@@ -15947,18 +16912,31 @@ var ElementsArea = ({
15947
16912
  author = "szenen-view",
15948
16913
  restrictToTypes,
15949
16914
  allTypesLabel,
16915
+ listTitle,
15950
16916
  onSelect
15951
16917
  }) => {
15952
16918
  const auswahlTypen = restrictToTypes ?? SZENEN_ELEMENT_TYPES;
15953
- const [typ, setTyp] = (0, import_react10.useState)(
15954
- restrictToTypes && restrictToTypes.length === 1 ? restrictToTypes[0] : ""
15955
- );
15956
- const [zeigeStillgelegte, setZeigeStillgelegte] = (0, import_react10.useState)(false);
15957
- const [gewaehlt, setGewaehlt] = (0, import_react10.useState)(null);
15958
- const [neuerTitel, setNeuerTitel] = (0, import_react10.useState)("");
15959
- const [neuerTyp, setNeuerTyp] = (0, import_react10.useState)(auswahlTypen[0]);
15960
- const [notiz, setNotiz] = (0, import_react10.useState)(null);
15961
- const [anlegend, setAnlegend] = (0, import_react10.useState)(false);
16919
+ const einzelTyp = auswahlTypen.length === 1 ? auswahlTypen[0] : null;
16920
+ const gemischt = auswahlTypen.length > 1;
16921
+ const ueberschrift = listTitle ?? (einzelTyp ? elementTypPlural(einzelTyp) : "Bausteine");
16922
+ const einzahl = einzelTyp ? elementTypLabel(einzelTyp) : "Objekt";
16923
+ const [typ, setTyp] = (0, import_react11.useState)(einzelTyp ?? "");
16924
+ const [zeigeStillgelegte, setZeigeStillgelegte] = (0, import_react11.useState)(false);
16925
+ const [gewaehlt, setGewaehlt] = (0, import_react11.useState)(null);
16926
+ const [neuerTitel, setNeuerTitel] = (0, import_react11.useState)("");
16927
+ const [neuerTyp, setNeuerTyp] = (0, import_react11.useState)(auswahlTypen[0]);
16928
+ const [notiz, setNotiz] = (0, import_react11.useState)(null);
16929
+ const [anlegend, setAnlegend] = (0, import_react11.useState)(false);
16930
+ const [anlageOffen, setAnlageOffen] = (0, import_react11.useState)(false);
16931
+ const schmal = useSchmalesDisplay();
16932
+ const [ansicht, setAnsicht] = (0, import_react11.useState)("liste");
16933
+ const zeigeListe = !schmal || ansicht === "liste";
16934
+ const zeigeDetail = !schmal || ansicht === "detail";
16935
+ const waehle = (elementId) => {
16936
+ setGewaehlt(elementId);
16937
+ setAnsicht("detail");
16938
+ onSelect?.(elementId);
16939
+ };
15962
16940
  const [phase, neuLaden] = usePortQuery(
15963
16941
  () => port.elements.listElements({
15964
16942
  contextId,
@@ -15967,128 +16945,199 @@ var ElementsArea = ({
15967
16945
  }),
15968
16946
  [port, contextId, typ, zeigeStillgelegte]
15969
16947
  );
15970
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "szenen-elements", "data-testid": "bereich-elemente", children: [
15971
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "szenen-elements__filter", children: [
15972
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
15973
- "Typ",
15974
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
15975
- "select",
15976
- {
15977
- "data-testid": "filter-typ",
15978
- value: typ,
15979
- onChange: (e) => setTyp(e.target.value),
15980
- children: [
15981
- (!restrictToTypes || restrictToTypes.length > 1) && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: "", children: allTypesLabel ?? "alle Typen" }),
15982
- auswahlTypen.map((t) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: t, children: t }, t))
15983
- ]
15984
- }
15985
- )
15986
- ] }),
15987
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
15988
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
15989
- "input",
15990
- {
15991
- type: "checkbox",
15992
- "data-testid": "filter-stillgelegt",
15993
- checked: zeigeStillgelegte,
15994
- onChange: (e) => setZeigeStillgelegte(e.target.checked)
15995
- }
15996
- ),
15997
- "stillgelegte Elemente einblenden"
15998
- ] }),
15999
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", "data-testid": "neu-laden", onClick: neuLaden, children: "neu laden" })
16000
- ] }),
16001
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
16002
- "form",
16003
- {
16004
- className: "szenen-elements__create",
16005
- "data-testid": "element-anlegen",
16006
- onSubmit: async (e) => {
16007
- e.preventDefault();
16008
- if (!neuerTitel.trim()) return;
16009
- setAnlegend(true);
16010
- const ergebnis = await port.elements.createElement({
16011
- type: neuerTyp,
16012
- title: neuerTitel.trim(),
16013
- contextId
16014
- });
16015
- setNotiz(noticeFor(ergebnis, `Element \u201E${neuerTitel.trim()}" anlegen`));
16016
- setAnlegend(false);
16017
- if (ergebnis.kind === "ok") {
16018
- setNeuerTitel("");
16019
- setGewaehlt(ergebnis.value.element_id);
16020
- onSelect?.(ergebnis.value.element_id);
16021
- neuLaden();
16022
- }
16023
- },
16024
- children: [
16025
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
16026
- "input",
16948
+ const anlageSchliessen = () => {
16949
+ setAnlageOffen(false);
16950
+ setNeuerTitel("");
16951
+ };
16952
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
16953
+ "div",
16954
+ {
16955
+ className: "szenen-elements",
16956
+ "data-testid": "bereich-elemente",
16957
+ "data-ansicht": schmal ? ansicht : "beide",
16958
+ children: [
16959
+ zeigeListe && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "szenen-elements__master", "data-testid": "elemente-master", children: [
16960
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "szenen-elements__toolbar", children: [
16961
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h3", { className: "szenen-elements__heading", children: ueberschrift }),
16962
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
16963
+ "button",
16964
+ {
16965
+ type: "button",
16966
+ className: "szenen-elements__new",
16967
+ "data-testid": "neu-oeffnen",
16968
+ "aria-expanded": anlageOffen,
16969
+ onClick: () => {
16970
+ setNotiz(null);
16971
+ if (anlageOffen) anlageSchliessen();
16972
+ else setAnlageOffen(true);
16973
+ },
16974
+ children: "+ Neu"
16975
+ }
16976
+ )
16977
+ ] }),
16978
+ gemischt && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { className: "szenen-elements__scope", children: [
16979
+ "Art",
16980
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
16981
+ "select",
16982
+ {
16983
+ "data-testid": "filter-typ",
16984
+ value: typ,
16985
+ onChange: (e) => setTyp(e.target.value),
16986
+ children: [
16987
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: "", children: allTypesLabel ?? "alle Typen" }),
16988
+ auswahlTypen.map((t) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: t, children: elementTypPlural(t) }, t))
16989
+ ]
16990
+ }
16991
+ )
16992
+ ] }),
16993
+ anlageOffen && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
16994
+ "form",
16027
16995
  {
16028
- "data-testid": "neuer-titel",
16029
- value: neuerTitel,
16030
- placeholder: "Titel des neuen Elements",
16031
- onChange: (e) => setNeuerTitel(e.target.value)
16996
+ className: "szenen-elements__create",
16997
+ "data-testid": "element-anlegen",
16998
+ onSubmit: async (e) => {
16999
+ e.preventDefault();
17000
+ if (!neuerTitel.trim()) return;
17001
+ setAnlegend(true);
17002
+ const gewuenschterTyp = einzelTyp ?? neuerTyp;
17003
+ const titel = neuerTitel.trim();
17004
+ const ergebnis = await port.elements.createElement({
17005
+ type: gewuenschterTyp,
17006
+ title: titel,
17007
+ contextId
17008
+ });
17009
+ setNotiz(noticeFor(
17010
+ ergebnis,
17011
+ `${elementTypLabel(gewuenschterTyp)} \u201E${titel}" anlegen`
17012
+ ));
17013
+ setAnlegend(false);
17014
+ if (ergebnis.kind === "ok") {
17015
+ anlageSchliessen();
17016
+ waehle(ergebnis.value.element_id);
17017
+ neuLaden();
17018
+ }
17019
+ },
17020
+ children: [
17021
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { className: "szenen-elements__create-title", children: [
17022
+ einzelTyp ? `Neue ${einzahl}` : "Titel",
17023
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
17024
+ "input",
17025
+ {
17026
+ "data-testid": "neuer-titel",
17027
+ value: neuerTitel,
17028
+ autoFocus: true,
17029
+ placeholder: einzelTyp ? `Titel der neuen ${einzahl}` : "Titel",
17030
+ onChange: (e) => setNeuerTitel(e.target.value)
17031
+ }
17032
+ )
17033
+ ] }),
17034
+ gemischt && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { className: "szenen-elements__create-type", children: [
17035
+ "Art",
17036
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
17037
+ "select",
17038
+ {
17039
+ "data-testid": "neuer-typ",
17040
+ value: neuerTyp,
17041
+ onChange: (e) => setNeuerTyp(e.target.value),
17042
+ children: auswahlTypen.map((t) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: t, children: elementTypLabel(t) }, t))
17043
+ }
17044
+ )
17045
+ ] }),
17046
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "szenen-elements__create-actions", children: [
17047
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
17048
+ "button",
17049
+ {
17050
+ type: "submit",
17051
+ "data-testid": "anlegen",
17052
+ disabled: anlegend || !neuerTitel.trim(),
17053
+ children: "anlegen"
17054
+ }
17055
+ ),
17056
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
17057
+ "button",
17058
+ {
17059
+ type: "button",
17060
+ "data-testid": "anlegen-abbrechen",
17061
+ onClick: anlageSchliessen,
17062
+ children: "abbrechen"
17063
+ }
17064
+ )
17065
+ ] })
17066
+ ]
16032
17067
  }
16033
17068
  ),
16034
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
16035
- "select",
17069
+ notiz && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { "data-testid": "anlegen-notiz", "data-tone": notiz.tone, children: notiz.text }),
17070
+ phase.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { "data-testid": "elemente-laden", children: "Elemente werden geladen \u2026" }),
17071
+ phase.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { "data-testid": "elemente-fehler", role: "alert", children: [
17072
+ "Elemente konnten nicht geladen werden: ",
17073
+ phase.message,
17074
+ phase.retryable && " \u2014 ein erneuter Versuch kann helfen."
17075
+ ] }),
17076
+ phase.status === "ready" && phase.value.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { "data-testid": "elemente-leer", children: [
17077
+ typ ? `Keine ${elementTypPlural(typ)} in diesem Kontext.` : "Nichts in diesem Kontext.",
17078
+ " ",
17079
+ 'Mit \u201E+ Neu" anlegen.'
17080
+ ] }),
17081
+ phase.status === "ready" && phase.value.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("ul", { className: "szenen-elements__list", "data-testid": "elemente-liste", children: phase.value.map((element) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("li", { "data-element-id": element.element_id, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
17082
+ "button",
17083
+ {
17084
+ type: "button",
17085
+ "data-testid": `element-waehlen-${element.element_id}`,
17086
+ "aria-current": element.element_id === gewaehlt,
17087
+ className: element.element_id === gewaehlt ? "is-selected" : void 0,
17088
+ onClick: () => waehle(element.element_id),
17089
+ children: [
17090
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { className: "szenen-elements__title", children: element.title || "Unbenanntes Element" }),
17091
+ gemischt && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "szenen-elements__type", children: elementTypLabel(element.type) }),
17092
+ element.subtype && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "szenen-elements__subtype", children: element.subtype }),
17093
+ element.retired && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "szenen-elements__retired", children: "stillgelegt" })
17094
+ ]
17095
+ }
17096
+ ) }, element.element_id)) }),
17097
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("details", { className: "szenen-elements__options", "data-testid": "listen-optionen", children: [
17098
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("summary", { children: "Listenoptionen" }),
17099
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "szenen-elements__filter", children: [
17100
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
17101
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
17102
+ "input",
17103
+ {
17104
+ type: "checkbox",
17105
+ "data-testid": "filter-stillgelegt",
17106
+ checked: zeigeStillgelegte,
17107
+ onChange: (e) => setZeigeStillgelegte(e.target.checked)
17108
+ }
17109
+ ),
17110
+ "stillgelegte Elemente einblenden"
17111
+ ] }),
17112
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", "data-testid": "neu-laden", onClick: neuLaden, children: "neu laden" })
17113
+ ] })
17114
+ ] })
17115
+ ] }),
17116
+ zeigeDetail && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "szenen-elements__detail", "data-testid": "elemente-detail-spalte", children: [
17117
+ schmal && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
17118
+ "button",
16036
17119
  {
16037
- "data-testid": "neuer-typ",
16038
- value: neuerTyp,
16039
- onChange: (e) => setNeuerTyp(e.target.value),
16040
- children: auswahlTypen.map((t) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: t, children: t }, t))
17120
+ type: "button",
17121
+ className: "szenen-elements__back",
17122
+ "data-testid": "zurueck-zur-liste",
17123
+ onClick: () => setAnsicht("liste"),
17124
+ children: "\u2190 Zur\xFCck zur Liste"
16041
17125
  }
16042
17126
  ),
16043
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "submit", "data-testid": "anlegen", disabled: anlegend || !neuerTitel.trim(), children: "anlegen" })
16044
- ]
16045
- }
16046
- ),
16047
- notiz && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { "data-testid": "anlegen-notiz", "data-tone": notiz.tone, children: notiz.text }),
16048
- phase.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { "data-testid": "elemente-laden", children: "Elemente werden geladen \u2026" }),
16049
- phase.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { "data-testid": "elemente-fehler", role: "alert", children: [
16050
- "Elemente konnten nicht geladen werden: ",
16051
- phase.message,
16052
- phase.retryable && " \u2014 ein erneuter Versuch kann helfen."
16053
- ] }),
16054
- phase.status === "ready" && phase.value.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { "data-testid": "elemente-leer", children: [
16055
- "Keine Elemente in diesem Kontext",
16056
- typ ? ` mit Typ \u201E${typ}"` : "",
16057
- "."
16058
- ] }),
16059
- phase.status === "ready" && phase.value.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("ul", { className: "szenen-elements__list", "data-testid": "elemente-liste", children: phase.value.map((element) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("li", { "data-element-id": element.element_id, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
16060
- "button",
16061
- {
16062
- type: "button",
16063
- "aria-current": element.element_id === gewaehlt,
16064
- className: element.element_id === gewaehlt ? "is-selected" : void 0,
16065
- onClick: () => {
16066
- setGewaehlt(element.element_id);
16067
- onSelect?.(element.element_id);
16068
- },
16069
- children: [
16070
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { className: "szenen-elements__title", children: element.title || "Unbenanntes Element" }),
16071
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "szenen-elements__type", children: element.type }),
16072
- element.subtype && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "szenen-elements__subtype", children: element.subtype }),
16073
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "szenen-elements__technical", children: [
16074
- "ID ",
16075
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("code", { className: "szenen-elements__id", children: element.element_id })
16076
- ] }),
16077
- element.retired && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "szenen-elements__retired", children: "stillgelegt" })
16078
- ]
16079
- }
16080
- ) }, element.element_id)) }),
16081
- gewaehlt && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
16082
- ElementDetail,
16083
- {
16084
- port,
16085
- elementId: gewaehlt,
16086
- contextId,
16087
- author,
16088
- onChanged: neuLaden
16089
- }
16090
- )
16091
- ] });
17127
+ gewaehlt ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
17128
+ ElementDetail,
17129
+ {
17130
+ port,
17131
+ elementId: gewaehlt,
17132
+ contextId,
17133
+ author,
17134
+ onChanged: neuLaden
17135
+ }
17136
+ ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "szenen-elements__empty-detail", "data-testid": "keine-auswahl", children: "Nichts ausgew\xE4hlt. Ein Eintrag aus der Liste \xF6ffnet die Bearbeitung." })
17137
+ ] })
17138
+ ]
17139
+ }
17140
+ );
16092
17141
  };
16093
17142
 
16094
17143
  // src/features/szenen/areas/SceneArea.tsx
@@ -16101,14 +17150,15 @@ var SceneArea = ({ port, contextId }) => /* @__PURE__ */ (0, import_jsx_runtime1
16101
17150
  // src/features/szenen/areas/SetupArea.tsx
16102
17151
  var import_jsx_runtime12 = require("react/jsx-runtime");
16103
17152
  var SetupArea = ({ port, contextId }) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "szenen-setup", "data-testid": "bereich-setup", children: [
16104
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "szenen-area__lead", children: "Scene Setups und Camera Rigs dieses Kontexts." }),
17153
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "szenen-area__lead", children: "Bildaufbauten, Kameras und Stile dieses Kontexts." }),
16105
17154
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
16106
17155
  ElementsArea,
16107
17156
  {
16108
17157
  port,
16109
17158
  contextId,
16110
17159
  restrictToTypes: ["scene_setup", "camera_preset", "camera_pattern", "style"],
16111
- allTypesLabel: "alle Bildaufbau-Typen"
17160
+ allTypesLabel: "alle Bildaufbau-Typen",
17161
+ listTitle: "Bildaufbau"
16112
17162
  }
16113
17163
  )
16114
17164
  ] });
@@ -16116,34 +17166,35 @@ var SetupArea = ({ port, contextId }) => /* @__PURE__ */ (0, import_jsx_runtime1
16116
17166
  // src/features/szenen/areas/ShotArea.tsx
16117
17167
  var import_jsx_runtime13 = require("react/jsx-runtime");
16118
17168
  var ShotArea = ({ port, contextId }) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "szenen-shot", "data-testid": "bereich-shot", children: [
16119
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "szenen-area__lead", children: "Shots dieses Kontexts. Auswahl zeigt Runden und startet Prompt-L\xE4ufe." }),
17169
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "szenen-area__lead", children: "Prompts dieses Kontexts. Auswahl zeigt Runden und startet Prompt-L\xE4ufe." }),
16120
17170
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
16121
17171
  ElementsArea,
16122
17172
  {
16123
17173
  port,
16124
17174
  contextId,
16125
17175
  restrictToTypes: ["shot", "shot_concept", "target_model"],
16126
- allTypesLabel: "alle Prompt-Typen"
17176
+ allTypesLabel: "alle Prompt-Typen",
17177
+ listTitle: "Prompts"
16127
17178
  }
16128
17179
  )
16129
17180
  ] });
16130
17181
 
16131
17182
  // src/features/szenen/areas/MediaArea.tsx
16132
- var import_react11 = require("react");
17183
+ var import_react12 = require("react");
16133
17184
  var import_jsx_runtime14 = require("react/jsx-runtime");
16134
17185
  var leer = async () => ({ kind: "ok", value: [] });
16135
17186
  var MediaArea = ({ port }) => {
16136
17187
  const media = port.media;
16137
- const [versionInput, setVersionInput] = (0, import_react11.useState)("");
16138
- const [versionId, setVersionId] = (0, import_react11.useState)("");
16139
- const [itemInput, setItemInput] = (0, import_react11.useState)("");
16140
- const [itemId, setItemId] = (0, import_react11.useState)("");
16141
- const [assetId, setAssetId] = (0, import_react11.useState)("");
16142
- const [role, setRole] = (0, import_react11.useState)("reference");
16143
- const [position, setPosition] = (0, import_react11.useState)("0");
16144
- const [notiz, setNotiz] = (0, import_react11.useState)(null);
16145
- const [busy, setBusy] = (0, import_react11.useState)(false);
16146
- const [refresh, setRefresh] = (0, import_react11.useState)(0);
17188
+ const [versionInput, setVersionInput] = (0, import_react12.useState)("");
17189
+ const [versionId, setVersionId] = (0, import_react12.useState)("");
17190
+ const [itemInput, setItemInput] = (0, import_react12.useState)("");
17191
+ const [itemId, setItemId] = (0, import_react12.useState)("");
17192
+ const [assetId, setAssetId] = (0, import_react12.useState)("");
17193
+ const [role, setRole] = (0, import_react12.useState)("reference");
17194
+ const [position, setPosition] = (0, import_react12.useState)("0");
17195
+ const [notiz, setNotiz] = (0, import_react12.useState)(null);
17196
+ const [busy, setBusy] = (0, import_react12.useState)(false);
17197
+ const [refresh, setRefresh] = (0, import_react12.useState)(0);
16147
17198
  const [versionPhase] = usePortQuery(
16148
17199
  () => versionId ? media.listVersionMedia(versionId) : leer(),
16149
17200
  [media, versionId, refresh]
@@ -16269,10 +17320,10 @@ var MediaArea = ({ port }) => {
16269
17320
  };
16270
17321
 
16271
17322
  // src/features/szenen/areas/DeriveArea.tsx
16272
- var import_react14 = require("react");
17323
+ var import_react15 = require("react");
16273
17324
 
16274
17325
  // src/features/szenen/areas/RecoveryPanel.tsx
16275
- var import_react12 = require("react");
17326
+ var import_react13 = require("react");
16276
17327
  var import_jsx_runtime15 = require("react/jsx-runtime");
16277
17328
  var Diagnosen = ({ befunde, testId }) => {
16278
17329
  if (befunde.length === 0) return null;
@@ -16323,11 +17374,11 @@ var Ausbeute = ({ bewertung }) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)
16323
17374
  ] })
16324
17375
  ] });
16325
17376
  var RecoveryPanel = ({ port, lauf }) => {
16326
- const [arbeitskopie, setArbeitskopie] = (0, import_react12.useState)(null);
16327
- const [bewertung, setBewertung] = (0, import_react12.useState)(null);
16328
- const [gesichert, setGesichert] = (0, import_react12.useState)(null);
16329
- const [meldung, setMeldung] = (0, import_react12.useState)(null);
16330
- const [laeuft, setLaeuft] = (0, import_react12.useState)(false);
17377
+ const [arbeitskopie, setArbeitskopie] = (0, import_react13.useState)(null);
17378
+ const [bewertung, setBewertung] = (0, import_react13.useState)(null);
17379
+ const [gesichert, setGesichert] = (0, import_react13.useState)(null);
17380
+ const [meldung, setMeldung] = (0, import_react13.useState)(null);
17381
+ const [laeuft, setLaeuft] = (0, import_react13.useState)(false);
16331
17382
  if (!port.derivation) return null;
16332
17383
  if (typeof lauf.raw_output !== "string" || lauf.raw_output.length === 0) return null;
16333
17384
  const derivation = port.derivation;
@@ -16450,12 +17501,12 @@ var RecoveryPanel = ({ port, lauf }) => {
16450
17501
  };
16451
17502
 
16452
17503
  // src/features/szenen/areas/ChainActions.tsx
16453
- var import_react13 = require("react");
17504
+ var import_react14 = require("react");
16454
17505
  var import_jsx_runtime16 = require("react/jsx-runtime");
16455
17506
  var ChainActions = ({ port, runId, recoveryId }) => {
16456
- const [graph, setGraph] = (0, import_react13.useState)(null);
16457
- const [meldung, setMeldung] = (0, import_react13.useState)(null);
16458
- const [laeuft, setLaeuft] = (0, import_react13.useState)(false);
17507
+ const [graph, setGraph] = (0, import_react14.useState)(null);
17508
+ const [meldung, setMeldung] = (0, import_react14.useState)(null);
17509
+ const [laeuft, setLaeuft] = (0, import_react14.useState)(false);
16459
17510
  if (!port.derivation) return null;
16460
17511
  const derivation = port.derivation;
16461
17512
  const runs = port.runs;
@@ -16521,7 +17572,13 @@ var ChainActions = ({ port, runId, recoveryId }) => {
16521
17572
  ] }),
16522
17573
  shotKnoten && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
16523
17574
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("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".' }),
16524
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ChainPromptAction, { port, shotElementId: shotKnoten.elementId })
17575
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
17576
+ PromptStartAction,
17577
+ {
17578
+ port,
17579
+ source: { kind: "draft_chain", shotElementId: shotKnoten.elementId }
17580
+ }
17581
+ )
16525
17582
  ] }),
16526
17583
  meldung && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "szenen-error", role: "alert", "data-testid": "kette-meldung", children: meldung })
16527
17584
  ] });
@@ -16535,8 +17592,8 @@ var MODUS_TEXT = {
16535
17592
  image_prompt: "Bild und Prompt"
16536
17593
  };
16537
17594
  var BelegFeld = ({ titel, text, testId, offen }) => {
16538
- const [kopiert, setKopiert] = (0, import_react14.useState)(false);
16539
- const feld = (0, import_react14.useRef)(null);
17595
+ const [kopiert, setKopiert] = (0, import_react15.useState)(false);
17596
+ const feld = (0, import_react15.useRef)(null);
16540
17597
  async function kopieren() {
16541
17598
  try {
16542
17599
  await navigator.clipboard.writeText(text);
@@ -16631,14 +17688,64 @@ function readAsBase642(datei) {
16631
17688
  leser.readAsDataURL(datei);
16632
17689
  });
16633
17690
  }
17691
+ function useVorschauen(port, mediaRefs) {
17692
+ const [quellen, setQuellen] = (0, import_react15.useState)(() => /* @__PURE__ */ new Map());
17693
+ const gehalten = (0, import_react15.useRef)(/* @__PURE__ */ new Map());
17694
+ const schluessel = mediaRefs.join(" ");
17695
+ (0, import_react15.useEffect)(() => {
17696
+ const bestand = port.imageLibrary;
17697
+ if (!bestand?.loadPreview) return void 0;
17698
+ const refs = schluessel ? schluessel.split(" ") : [];
17699
+ const lauf = { abgebrochen: false };
17700
+ void ladeVorschauen(
17701
+ bestand,
17702
+ refs,
17703
+ (mediaRef, quelle) => {
17704
+ gehalten.current.set(mediaRef, quelle);
17705
+ setQuellen(new Map(gehalten.current));
17706
+ },
17707
+ { parallel: 4, hat: (ref) => gehalten.current.has(ref), lauf }
17708
+ );
17709
+ return () => {
17710
+ lauf.abgebrochen = true;
17711
+ };
17712
+ }, [port, schluessel]);
17713
+ return quellen;
17714
+ }
17715
+ var Kachel = ({ quelle, alt }) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "szenen-bildkachel", children: quelle ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("img", { src: quelle, alt, loading: "lazy" }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "szenen-bildkachel__leer", "aria-hidden": "true" }) });
16634
17716
  var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
16635
- const [suche, setSuche] = (0, import_react14.useState)("");
16636
- const [bilder] = usePortQuery(
16637
- () => port.imageLibrary.listImages({ search: suche.trim() || void 0, limit: 60 }),
17717
+ const [suche, setSuche] = (0, import_react15.useState)("");
17718
+ const [offenesWerk, setOffenesWerk] = (0, import_react15.useState)(null);
17719
+ const [items] = usePortQuery(
17720
+ () => port.imageLibrary.listItems({ search: suche.trim() || void 0, limit: 60 }),
16638
17721
  [port, suche]
16639
17722
  );
17723
+ const werk = items.status === "ready" ? items.value.find((i) => i.itemRef === offenesWerk) ?? null : null;
17724
+ const sichtbar = (0, import_react15.useMemo)(() => {
17725
+ const refs = werk ? werk.images.map((b) => b.mediaRef) : items.status === "ready" ? items.value.map((i) => i.images[0]?.mediaRef).filter((r) => Boolean(r)) : [];
17726
+ return [.../* @__PURE__ */ new Set([...refs, ...gewaehlt.map((b) => b.mediaRef)])];
17727
+ }, [items, werk, gewaehlt]);
17728
+ const quellen = useVorschauen(port, sichtbar);
17729
+ const quelleVon = (bild) => bild.thumbUrl || quellen.get(bild.mediaRef) || null;
16640
17730
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "szenen-bildwahl", "data-testid": "bildwahl", children: [
16641
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("label", { className: "szenen-field", children: [
17731
+ werk ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "szenen-bildwahl__kopf", children: [
17732
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
17733
+ "button",
17734
+ {
17735
+ type: "button",
17736
+ className: "szenen-bildwahl__zurueck",
17737
+ onClick: () => setOffenesWerk(null),
17738
+ "data-testid": "bildwahl-zurueck",
17739
+ children: "Zurueck zur Auswahl"
17740
+ }
17741
+ ),
17742
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "szenen-bildwahl__werktitel", "data-testid": "bildwahl-werktitel", children: [
17743
+ werk.title,
17744
+ " \xB7 ",
17745
+ werk.images.length,
17746
+ " Bilder"
17747
+ ] })
17748
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("label", { className: "szenen-field", children: [
16642
17749
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { children: "Bild aus dem Bestand" }),
16643
17750
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
16644
17751
  "input",
@@ -16646,31 +17753,70 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
16646
17753
  type: "search",
16647
17754
  value: suche,
16648
17755
  onChange: (e) => setSuche(e.target.value),
17756
+ onKeyDown: (e) => {
17757
+ if (e.key === "Enter") e.preventDefault();
17758
+ },
16649
17759
  placeholder: "Suchen \u2026",
16650
17760
  "data-testid": "bild-suche"
16651
17761
  }
16652
17762
  )
16653
17763
  ] }),
16654
- bilder.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { children: "Bilder werden geladen \u2026" }),
16655
- bilder.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("p", { className: "szenen-error", role: "alert", children: [
17764
+ items.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { children: "Bilder werden geladen \u2026" }),
17765
+ items.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("p", { className: "szenen-error", role: "alert", children: [
16656
17766
  "Der Bildbestand ist nicht lesbar: ",
16657
- bilder.message
17767
+ items.message
16658
17768
  ] }),
16659
- bilder.status === "ready" && bilder.value.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "szenen-empty", "data-testid": "bildwahl-leer", children: "Zu dieser Suche gibt es kein Bild. Ohne Treffer bleibt der Upload." }),
16660
- bilder.status === "ready" && bilder.value.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("ul", { className: "szenen-bildwahl__liste", children: bilder.value.map((bild) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
17769
+ items.status === "ready" && items.value.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "szenen-empty", "data-testid": "bildwahl-leer", children: "Zu dieser Suche gibt es kein Bild. Ohne Treffer bleibt der Upload." }),
17770
+ werk && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("ul", { className: "szenen-bildwahl__raster", "data-testid": "bildwahl-galerie", children: werk.images.map((bild, position) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
16661
17771
  "button",
16662
17772
  {
16663
17773
  type: "button",
16664
- onClick: () => onWaehlen(bild),
17774
+ onClick: () => {
17775
+ onWaehlen(bild);
17776
+ setOffenesWerk(null);
17777
+ },
16665
17778
  "data-testid": `bild-waehlen-${bild.mediaRef}`,
17779
+ title: bild.title,
16666
17780
  children: [
16667
- bild.thumbUrl && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("img", { src: bild.thumbUrl, alt: "" }),
16668
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { children: bild.title })
17781
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Kachel, { quelle: quelleVon(bild), alt: bild.title }),
17782
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "szenen-bildwahl__nummer", children: [
17783
+ "#",
17784
+ position + 1
17785
+ ] })
16669
17786
  ]
16670
17787
  }
16671
17788
  ) }, bild.mediaRef)) }),
17789
+ !werk && items.status === "ready" && items.value.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("ul", { className: "szenen-bildwahl__raster", "data-testid": "bildwahl-werke", children: items.value.map((item) => {
17790
+ const deckbild = item.images[0];
17791
+ if (!deckbild) return null;
17792
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
17793
+ "button",
17794
+ {
17795
+ type: "button",
17796
+ onClick: () => item.images.length === 1 ? onWaehlen(deckbild) : setOffenesWerk(item.itemRef),
17797
+ "data-testid": `item-waehlen-${item.itemRef}`,
17798
+ title: item.title,
17799
+ children: [
17800
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Kachel, { quelle: quelleVon(deckbild), alt: item.title }),
17801
+ item.images.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
17802
+ "span",
17803
+ {
17804
+ className: "szenen-bildwahl__anzahl",
17805
+ "data-testid": `item-anzahl-${item.itemRef}`,
17806
+ children: [
17807
+ item.images.length,
17808
+ "x"
17809
+ ]
17810
+ }
17811
+ ),
17812
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "szenen-bildwahl__titel", children: item.title })
17813
+ ]
17814
+ }
17815
+ ) }, item.itemRef);
17816
+ }) }),
16672
17817
  gewaehlt.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("ul", { className: "szenen-bildwahl__gewaehlt", "data-testid": "bilder-gewaehlt", children: gewaehlt.map((bild) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("li", { children: [
16673
- bild.title,
17818
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Kachel, { quelle: quelleVon(bild), alt: bild.title }),
17819
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "szenen-bildwahl__titel", children: bild.title }),
16674
17820
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
16675
17821
  "button",
16676
17822
  {
@@ -16690,26 +17836,29 @@ async function digestOf(datei) {
16690
17836
  return [...new Uint8Array(hash)].map((b) => b.toString(16).padStart(2, "0")).join("");
16691
17837
  }
16692
17838
  var DeriveArea = ({ port, contextId }) => {
16693
- const [policyVersionId, setPolicyVersionId] = (0, import_react14.useState)("");
16694
- const [modus, setModus] = (0, import_react14.useState)("prompt");
16695
- const [promptText, setPromptText] = (0, import_react14.useState)("");
16696
- const [dateien, setDateien] = (0, import_react14.useState)([]);
16697
- const [bestandsbilder, setBestandsbilder] = (0, import_react14.useState)([]);
16698
- const [laeuft, setLaeuft] = (0, import_react14.useState)(false);
16699
- const [lauf, setLauf] = (0, import_react14.useState)(null);
16700
- const [meldung, setMeldung] = (0, import_react14.useState)(null);
16701
- const [uebernommen, setUebernommen] = (0, import_react14.useState)({});
16702
- const [titel, setTitel] = (0, import_react14.useState)({});
16703
- const [referenzen, setReferenzen] = (0, import_react14.useState)({});
16704
- const [beanstandung, setBeanstandung] = (0, import_react14.useState)(null);
16705
- const [zusatz, setZusatz] = (0, import_react14.useState)("");
17839
+ const [policyVersionId, setPolicyVersionId] = (0, import_react15.useState)("");
17840
+ const [modus, setModus] = (0, import_react15.useState)("prompt");
17841
+ const [promptText, setPromptText] = (0, import_react15.useState)("");
17842
+ const [dateien, setDateien] = (0, import_react15.useState)([]);
17843
+ const [bestandsbilder, setBestandsbilder] = (0, import_react15.useState)([]);
17844
+ const [laeuft, setLaeuft] = (0, import_react15.useState)(false);
17845
+ const [lauf, setLauf] = (0, import_react15.useState)(null);
17846
+ const [meldung, setMeldung] = (0, import_react15.useState)(null);
17847
+ const [uebernommen, setUebernommen] = (0, import_react15.useState)({});
17848
+ const [titel, setTitel] = (0, import_react15.useState)({});
17849
+ const [referenzen, setReferenzen] = (0, import_react15.useState)({});
17850
+ const [beanstandung, setBeanstandung] = (0, import_react15.useState)(null);
17851
+ const [zusatz, setZusatz] = (0, import_react15.useState)("");
16706
17852
  const [regeln] = usePortQuery(
16707
17853
  () => port.derivation.listPolicies(contextId),
16708
17854
  [port, contextId]
16709
17855
  );
16710
17856
  const gewaehlt = regeln.status === "ready" ? regeln.value.find((r) => r.versionId === policyVersionId) ?? null : null;
16711
17857
  async function starten(folgt) {
16712
- if (!gewaehlt) return;
17858
+ if (!gewaehlt) {
17859
+ setMeldung("Bitte zuerst eine Rekonstruktionsregel waehlen \u2014 ohne sie ist kein Lauf reproduzierbar (D-03).");
17860
+ return;
17861
+ }
16713
17862
  setLaeuft(true);
16714
17863
  setMeldung(null);
16715
17864
  setLauf(null);
@@ -17086,9 +18235,9 @@ var SzenenView = ({
17086
18235
  initialArea,
17087
18236
  onAreaChange
17088
18237
  }) => {
17089
- const bereiche = (0, import_react15.useMemo)(() => areasFor(port), [port]);
17090
- const faehig = (0, import_react15.useMemo)(() => capabilitiesOf(port), [port]);
17091
- const [aktiv, setAktiv] = (0, import_react15.useState)(() => resolveArea(port, initialArea));
18238
+ const bereiche = (0, import_react16.useMemo)(() => areasFor(port), [port]);
18239
+ const faehig = (0, import_react16.useMemo)(() => capabilitiesOf(port), [port]);
18240
+ const [aktiv, setAktiv] = (0, import_react16.useState)(() => resolveArea(port, initialArea));
17092
18241
  const wechsle = (id) => {
17093
18242
  setAktiv(id);
17094
18243
  onAreaChange?.(id);
@@ -17119,7 +18268,16 @@ var SzenenView = ({
17119
18268
  )) }),
17120
18269
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("section", { className: "szenen-view__area", "aria-label": aktuellerBereich.title, children: [
17121
18270
  aktiv === "derive" && port.derivation && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DeriveArea, { port, contextId }),
17122
- aktiv === "elements" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ElementsArea, { port, contextId }),
18271
+ aktiv === "elements" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
18272
+ ElementsArea,
18273
+ {
18274
+ port,
18275
+ contextId,
18276
+ restrictToTypes: BAUSTEIN_TYPES,
18277
+ allTypesLabel: "alle Bausteine",
18278
+ listTitle: "Bausteine"
18279
+ }
18280
+ ),
17123
18281
  aktiv === "scene" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SceneArea, { port, contextId }),
17124
18282
  aktiv === "setup" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SetupArea, { port, contextId }),
17125
18283
  aktiv === "shot" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ShotArea, { port, contextId }),
@@ -17144,6 +18302,7 @@ var SzenenView = ({
17144
18302
  ELEMENT_VERSION_SCHEMA_FILE,
17145
18303
  FLOW_EXECUTION_PATH,
17146
18304
  PROBE_VERSION_ID,
18305
+ PROMPT_RULE_EFFECT,
17147
18306
  REFERENCE_FIELD,
17148
18307
  RULES_WITHOUT_CAPABILITY_EFFECT,
17149
18308
  RULE_SCOPES,
@@ -17180,6 +18339,7 @@ var SzenenView = ({
17180
18339
  checkDraftChainMeta,
17181
18340
  checkElementType,
17182
18341
  checkImageExecutionProfile,
18342
+ checkImageExecutionProfiles,
17183
18343
  checkImageModelParameters,
17184
18344
  checkOptionalId,
17185
18345
  checkParents,
@@ -17228,7 +18388,10 @@ var SzenenView = ({
17228
18388
  derivationPoliciesOf,
17229
18389
  deriveItemFromRun,
17230
18390
  deriveLineage,
18391
+ describableContent,
17231
18392
  describeTargetModelFailure,
18393
+ diagnosticOfFinding,
18394
+ diagnosticOfSchemaError,
17232
18395
  differentialSteps,
17233
18396
  draftRevisionOf,
17234
18397
  elementStoreContractChecks,
@@ -17243,14 +18406,18 @@ var SzenenView = ({
17243
18406
  firstError,
17244
18407
  formatSchemaErrors,
17245
18408
  freezeDraftChain,
18409
+ freezeSceneWorkingState,
17246
18410
  getSzenenSchemas,
18411
+ gruppiereZuItems,
17247
18412
  hasPlacementCapability,
17248
18413
  isKnownElementType,
17249
18414
  isOk,
17250
18415
  isProjectionCurrent,
17251
18416
  isSzenenItemId,
17252
18417
  itemIdForRun,
18418
+ ladeVorschauen,
17253
18419
  linkFor,
18420
+ locatorTeile,
17254
18421
  logRevisionOf,
17255
18422
  mergeSzenenBatchOrigin,
17256
18423
  noticeFor,
@@ -17259,22 +18426,26 @@ var SzenenView = ({
17259
18426
  parseRunResponse,
17260
18427
  preferredHeadRevisionOf,
17261
18428
  preferredVersionId,
18429
+ promptDiagnosticsOf,
17262
18430
  publishedHeadsOf,
17263
18431
  recoveriesForRun,
17264
18432
  recoveryStatusOf,
17265
18433
  reduceElementEvents,
17266
18434
  resolveArea,
17267
18435
  resolvePreferredHead,
18436
+ resolveScenePayload,
17268
18437
  resolveShot,
17269
18438
  resolveShotPayload,
17270
18439
  resolveTargetModelOver,
17271
18440
  resolverFromQueries,
17272
18441
  roundOfRun,
18442
+ rulesWithoutPromptEffect,
17273
18443
  runElementStoreContract,
17274
18444
  runPromptDerivation,
17275
18445
  runPublishChecks,
17276
18446
  runSceneDerivation,
17277
18447
  runStoreDifferential,
18448
+ sceneOnlyCapabilities,
17278
18449
  scopeOfPath,
17279
18450
  selectableTargetModels,
17280
18451
  showChain,