@rslsp1/fa-app-tools 10.3.2 → 10.3.3

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.
@@ -8,14 +8,19 @@ import {
8
8
  CAMERA_ASPECT_RATIOS,
9
9
  STAGE_CATALOGUE,
10
10
  checkCameraAspectRatioChanges,
11
+ distinctPreviewColor,
12
+ facingRotationZ,
11
13
  frozenCameraAspectRatio,
14
+ normalizePreviewColor,
15
+ previewColorConflicts,
16
+ previewColorLegibility,
12
17
  roundMeters,
13
18
  stageColor,
14
19
  stageColors,
15
20
  stageFrame,
16
21
  stageTypeLabel,
17
22
  withStagePositions
18
- } from "./chunk-TFWIMFNF.mjs";
23
+ } from "./chunk-GKLCL2VQ.mjs";
19
24
 
20
25
  // src/lib/szenen/szenenSnapshot.ts
21
26
  var SZENEN_SNAPSHOT_SCHEMA_VERSION = 1;
@@ -442,6 +447,98 @@ async function hfLoadImageAsBase64(id, token, namespace, filename, on404, mimeTy
442
447
  return result === "ABORT" ? null : result;
443
448
  }
444
449
 
450
+ // src/lib/szenen/stageMigration.ts
451
+ var STAGE_MIGRATION_ID = "stage_floor_center_v1";
452
+ var STAGE_MIGRATION_EVENT = "szenen.stage_convention_migrated";
453
+ function migrateNode(node, report, source) {
454
+ if (!node.proxy_3d || node.proxy_3d.origin === "floor_center") return structuredClone(node);
455
+ const proxy = node.proxy_3d, d = proxy.dimensions;
456
+ if (!Array.isArray(d) || d.length !== 3 || d.some((v) => !Number.isFinite(v) || v <= 0)) throw new Error(`${source}/${node.node_id}: ung\xFCltige Proxy-Ma\xDFe.`);
457
+ let shape;
458
+ if (node.label === "Mensch" || node.type === "subject") shape = { primitive: "humanoid_proxy", pose_ref: "standing", morph: { build: 0.5, gender: 0.5 } };
459
+ else if (node.label === "Stuhl") shape = { primitive: "furniture_proxy", variant: "chair" };
460
+ else if (node.label === "Tisch" || node.type === "surface") shape = { primitive: "furniture_proxy", variant: "table" };
461
+ else if (node.label === "Lampe" || node.type === "light") shape = { primitive: "furniture_proxy", variant: "floor_lamp" };
462
+ else {
463
+ shape = { primitive: "box" };
464
+ report.remaining_boxes.push(`${source}/${node.node_id}`);
465
+ }
466
+ const anchors = node.anchors && Object.fromEntries(Object.entries(node.anchors).map(([key, value2]) => {
467
+ const a = value2;
468
+ return [key, [a[0], a[1], a[2] + d[2] / 2]];
469
+ }));
470
+ return { ...node, proxy_3d: { ...proxy, dimensions: [...d], ...shape, origin: "floor_center" }, ...anchors ? { anchors } : {} };
471
+ }
472
+ function prepareStageMigration(state) {
473
+ const report = { remaining_boxes: [], changed_camera_views: [], placements: 0 };
474
+ const entries = [];
475
+ const records = (snapshot) => [
476
+ ...snapshot.versions.map((v) => ({ element_id: v.element_id, version_id: v.version_id, before: v.payload })),
477
+ ...snapshot.draft ? [{
478
+ element_id: snapshot.element.element_id,
479
+ draft_id: snapshot.draft.draft_id,
480
+ revision: snapshot.draft.revision,
481
+ before: snapshot.draft.payload
482
+ }] : []
483
+ ];
484
+ for (const snapshot of state.elements.values()) {
485
+ if (!["scene", "scene_setup"].includes(snapshot.element.type)) continue;
486
+ if (snapshot.draftFork) throw new Error(`Migration: Entwurfskonflikt an ${snapshot.element.element_id} zuerst aufl\xF6sen.`);
487
+ for (const record of records(snapshot)) {
488
+ const source = record.element_id + "/" + ("version_id" in record ? record.version_id : record.draft_id);
489
+ let after;
490
+ if (snapshot.element.type === "scene") {
491
+ if (record.before.nodes === void 0) continue;
492
+ if (!Array.isArray(record.before.nodes)) throw new Error(`${source}: nodes ist keine Liste.`);
493
+ after = { ...record.before, nodes: record.before.nodes.map((n) => migrateNode(n, report, source)) };
494
+ } else {
495
+ const p = record.before;
496
+ const scene = state.elements.get(p.scene_ref?.element_id);
497
+ const link = "draft_id" in record && snapshot.draft?.chain?.links.find((l) => l.field === "scene_ref");
498
+ const scenePayload = link && scene?.draft?.draft_id === link.target_draft_id ? scene.draft.payload : scene?.versions.find((v) => v.version_id === p.scene_ref?.version_id)?.payload;
499
+ if (!scenePayload && (p.placements?.length || p.node_overrides?.some((n) => n.proxy_3d))) throw new Error(`${source}: referenzierte Szene fehlt.`);
500
+ const placements = (p.placements ?? []).map((placement) => {
501
+ const raw = scenePayload?.nodes.find((n) => n.node_id === placement.node_id);
502
+ const override = p.node_overrides?.find((n) => n.node_id === placement.node_id);
503
+ const proxy = override?.proxy_3d ?? raw?.proxy_3d;
504
+ if (!proxy || proxy.origin === "floor_center") return placement;
505
+ if (!Number.isFinite(proxy.dimensions?.[2]) || proxy.dimensions[2] <= 0) throw new Error(`${source}/${placement.node_id}: H\xF6he fehlt.`);
506
+ report.placements++;
507
+ return { ...placement, position: [placement.position[0], placement.position[1], placement.position[2] - proxy.dimensions[2] / 2] };
508
+ });
509
+ for (const camera of p.camera_rig?.cameras ?? []) {
510
+ if (camera.orientation?.mode === "explicit" && camera.orientation.rotation_euler_deg.filter((n) => n !== 0).length >= 2)
511
+ report.changed_camera_views.push(`${source}/${camera.camera_id}`);
512
+ }
513
+ after = {
514
+ ...p,
515
+ ...p.placements ? { placements } : {},
516
+ ...p.node_overrides ? { node_overrides: p.node_overrides.map((n) => {
517
+ const raw = scenePayload?.nodes.find((x) => x.node_id === n.node_id);
518
+ return n.proxy_3d ? { ...n, proxy_3d: migrateNode({ ...raw, ...n }, report, source).proxy_3d } : n;
519
+ }) } : {}
520
+ };
521
+ }
522
+ if (JSON.stringify(after) !== JSON.stringify(record.before)) entries.push({ ...record, after });
523
+ }
524
+ }
525
+ return { migration_id: STAGE_MIGRATION_ID, entries, report };
526
+ }
527
+ function applyStageMigration(state, migration) {
528
+ if (migration.migration_id !== STAGE_MIGRATION_ID) throw new Error("Unbekannte B\xFChnenmigration.");
529
+ for (const entry of migration.entries) {
530
+ const snapshot = state.elements.get(entry.element_id);
531
+ if (!snapshot) throw new Error(`B\xFChnenmigration: Element ${entry.element_id} fehlt.`);
532
+ if (entry.version_id) {
533
+ const version = snapshot.versions.find((v) => v.version_id === entry.version_id);
534
+ if (!version) throw new Error(`B\xFChnenmigration: Version ${entry.version_id} fehlt.`);
535
+ if (JSON.stringify(version.payload) === JSON.stringify(entry.before)) version.payload = structuredClone(entry.after);
536
+ } else if (snapshot.draft && snapshot.draft.draft_id === entry.draft_id && snapshot.draft.revision === entry.revision && JSON.stringify(snapshot.draft.payload) === JSON.stringify(entry.before)) {
537
+ snapshot.draft = { ...snapshot.draft, payload: structuredClone(entry.after), revision: snapshot.draft.revision + 1 };
538
+ }
539
+ }
540
+ }
541
+
445
542
  // src/lib/szenen/elementEvents.ts
446
543
  var SZENEN_EVENT_PREFIX = "szenen.";
447
544
  var SZENEN_EVENT_TYPES = {
@@ -827,7 +924,9 @@ function reduceElementEvents(events) {
827
924
  for (const [elementId, snapshot] of snapshots) {
828
925
  elements.set(elementId, finalize(elementId, snapshot));
829
926
  }
830
- return { elements, findings };
927
+ const state = { elements, findings };
928
+ for (const event of ordered) if (event.type === STAGE_MIGRATION_EVENT) applyStageMigration(state, event.payload);
929
+ return state;
831
930
  }
832
931
  var highestRevision = (byRevision) => [...byRevision.keys()].reduce((max, revision) => revision > max ? revision : max, 0);
833
932
  function finalize(elementId, snapshot) {
@@ -2021,7 +2120,7 @@ function applySchema(schema, instance, path, baseId, ctx) {
2021
2120
  }
2022
2121
  function applyKeywords(schema, instance, path, baseId, ctx) {
2023
2122
  let valid = true;
2024
- const fail3 = (keyword, message) => {
2123
+ const fail4 = (keyword, message) => {
2025
2124
  ctx.errors.push({ instancePath: path, message, keyword });
2026
2125
  valid = false;
2027
2126
  };
@@ -2032,24 +2131,24 @@ function applyKeywords(schema, instance, path, baseId, ctx) {
2032
2131
  if ("type" in schema) {
2033
2132
  const expected = Array.isArray(schema.type) ? schema.type : [schema.type];
2034
2133
  if (!expected.some((t) => matchesType(instance, t))) {
2035
- fail3("type", `muss ${expected.join(" oder ")} sein, ist ${jsonTypeOf(instance)}`);
2134
+ fail4("type", `muss ${expected.join(" oder ")} sein, ist ${jsonTypeOf(instance)}`);
2036
2135
  }
2037
2136
  }
2038
2137
  if ("const" in schema && !deepEqual(instance, schema.const)) {
2039
- fail3("const", `muss gleich ${JSON.stringify(schema.const)} sein`);
2138
+ fail4("const", `muss gleich ${JSON.stringify(schema.const)} sein`);
2040
2139
  }
2041
2140
  if (Array.isArray(schema.enum) && !schema.enum.some((option) => deepEqual(instance, option))) {
2042
- fail3("enum", `muss einer der erlaubten Werte sein: ${schema.enum.map((v) => JSON.stringify(v)).join(", ")}`);
2141
+ fail4("enum", `muss einer der erlaubten Werte sein: ${schema.enum.map((v) => JSON.stringify(v)).join(", ")}`);
2043
2142
  }
2044
2143
  if (typeof instance === "string") {
2045
2144
  if (typeof schema.minLength === "number" && instance.length < schema.minLength) {
2046
- fail3("minLength", `muss mindestens ${schema.minLength} Zeichen haben`);
2145
+ fail4("minLength", `muss mindestens ${schema.minLength} Zeichen haben`);
2047
2146
  }
2048
2147
  if (typeof schema.maxLength === "number" && instance.length > schema.maxLength) {
2049
- fail3("maxLength", `darf hoechstens ${schema.maxLength} Zeichen haben`);
2148
+ fail4("maxLength", `darf hoechstens ${schema.maxLength} Zeichen haben`);
2050
2149
  }
2051
2150
  if (typeof schema.pattern === "string" && !new RegExp(schema.pattern, "u").test(instance)) {
2052
- fail3("pattern", `muss dem Muster ${schema.pattern} entsprechen`);
2151
+ fail4("pattern", `muss dem Muster ${schema.pattern} entsprechen`);
2053
2152
  }
2054
2153
  if (typeof schema.format === "string") {
2055
2154
  const check = FORMATS[schema.format];
@@ -2058,40 +2157,40 @@ function applyKeywords(schema, instance, path, baseId, ctx) {
2058
2157
  `Format "${schema.format}" ist in jsonSchema.ts nicht implementiert.`
2059
2158
  );
2060
2159
  }
2061
- if (!check(instance)) fail3("format", `muss dem Format ${schema.format} entsprechen`);
2160
+ if (!check(instance)) fail4("format", `muss dem Format ${schema.format} entsprechen`);
2062
2161
  }
2063
2162
  }
2064
2163
  if (typeof instance === "number") {
2065
2164
  if (typeof schema.minimum === "number" && instance < schema.minimum) {
2066
- fail3("minimum", `muss >= ${schema.minimum} sein`);
2165
+ fail4("minimum", `muss >= ${schema.minimum} sein`);
2067
2166
  }
2068
2167
  if (typeof schema.maximum === "number" && instance > schema.maximum) {
2069
- fail3("maximum", `muss <= ${schema.maximum} sein`);
2168
+ fail4("maximum", `muss <= ${schema.maximum} sein`);
2070
2169
  }
2071
2170
  if (typeof schema.exclusiveMinimum === "number" && instance <= schema.exclusiveMinimum) {
2072
- fail3("exclusiveMinimum", `muss > ${schema.exclusiveMinimum} sein`);
2171
+ fail4("exclusiveMinimum", `muss > ${schema.exclusiveMinimum} sein`);
2073
2172
  }
2074
2173
  if (typeof schema.exclusiveMaximum === "number" && instance >= schema.exclusiveMaximum) {
2075
- fail3("exclusiveMaximum", `muss < ${schema.exclusiveMaximum} sein`);
2174
+ fail4("exclusiveMaximum", `muss < ${schema.exclusiveMaximum} sein`);
2076
2175
  }
2077
2176
  if (typeof schema.multipleOf === "number" && schema.multipleOf > 0) {
2078
2177
  const quotient = instance / schema.multipleOf;
2079
- if (!Number.isInteger(quotient)) fail3("multipleOf", `muss ein Vielfaches von ${schema.multipleOf} sein`);
2178
+ if (!Number.isInteger(quotient)) fail4("multipleOf", `muss ein Vielfaches von ${schema.multipleOf} sein`);
2080
2179
  }
2081
2180
  }
2082
2181
  if (Array.isArray(instance)) {
2083
2182
  if (typeof schema.minItems === "number" && instance.length < schema.minItems) {
2084
- fail3("minItems", `muss mindestens ${schema.minItems} Eintraege haben`);
2183
+ fail4("minItems", `muss mindestens ${schema.minItems} Eintraege haben`);
2085
2184
  }
2086
2185
  if (typeof schema.maxItems === "number" && instance.length > schema.maxItems) {
2087
- fail3("maxItems", `darf hoechstens ${schema.maxItems} Eintraege haben`);
2186
+ fail4("maxItems", `darf hoechstens ${schema.maxItems} Eintraege haben`);
2088
2187
  }
2089
2188
  if (schema.uniqueItems === true) {
2090
2189
  const gesehen = /* @__PURE__ */ new Set();
2091
2190
  for (const eintrag of instance) {
2092
2191
  const schluessel = canonicalJson(eintrag);
2093
2192
  if (gesehen.has(schluessel)) {
2094
- fail3("uniqueItems", "darf keine doppelten Eintraege enthalten");
2193
+ fail4("uniqueItems", "darf keine doppelten Eintraege enthalten");
2095
2194
  break;
2096
2195
  }
2097
2196
  gesehen.add(schluessel);
@@ -2102,7 +2201,7 @@ function applyKeywords(schema, instance, path, baseId, ctx) {
2102
2201
  const trifft = instance.some(
2103
2202
  (eintrag, i) => applySchema(schema.contains, eintrag, `${path}/${i}`, baseId, { ...ctx, errors: probe })
2104
2203
  );
2105
- if (!trifft) fail3("contains", "muss mindestens einen passenden Eintrag enthalten");
2204
+ if (!trifft) fail4("contains", "muss mindestens einen passenden Eintrag enthalten");
2106
2205
  }
2107
2206
  if ("items" in schema) {
2108
2207
  for (let i = 0; i < instance.length; i++) {
@@ -2115,10 +2214,10 @@ function applyKeywords(schema, instance, path, baseId, ctx) {
2115
2214
  if (isObject2(instance)) {
2116
2215
  const keys = Object.keys(instance);
2117
2216
  if (typeof schema.minProperties === "number" && keys.length < schema.minProperties) {
2118
- fail3("minProperties", `muss mindestens ${schema.minProperties} Eigenschaften haben`);
2217
+ fail4("minProperties", `muss mindestens ${schema.minProperties} Eigenschaften haben`);
2119
2218
  }
2120
2219
  if (typeof schema.maxProperties === "number" && keys.length > schema.maxProperties) {
2121
- fail3("maxProperties", `darf hoechstens ${schema.maxProperties} Eigenschaften haben`);
2220
+ fail4("maxProperties", `darf hoechstens ${schema.maxProperties} Eigenschaften haben`);
2122
2221
  }
2123
2222
  if (Array.isArray(schema.required)) {
2124
2223
  for (const name of schema.required) {
@@ -2148,7 +2247,7 @@ function applyKeywords(schema, instance, path, baseId, ctx) {
2148
2247
  if (known.has(name)) continue;
2149
2248
  const childPath = `${path}/${escapePointer(name)}`;
2150
2249
  if (schema.additionalProperties === false) {
2151
- fail3("additionalProperties", `unbekannte Eigenschaft "${name}"`);
2250
+ fail4("additionalProperties", `unbekannte Eigenschaft "${name}"`);
2152
2251
  } else if (schema.additionalProperties !== true) {
2153
2252
  if (!applySchema(schema.additionalProperties, instance[name], childPath, baseId, ctx)) {
2154
2253
  valid = false;
@@ -2167,7 +2266,7 @@ function applyKeywords(schema, instance, path, baseId, ctx) {
2167
2266
  const anyValid = schema.anyOf.some(
2168
2267
  (branch) => applySchema(branch, instance, path, baseId, { ...ctx, errors: probe })
2169
2268
  );
2170
- if (!anyValid) fail3("anyOf", "passt auf keine der erlaubten Varianten");
2269
+ if (!anyValid) fail4("anyOf", "passt auf keine der erlaubten Varianten");
2171
2270
  }
2172
2271
  if (Array.isArray(schema.oneOf)) {
2173
2272
  const probe = [];
@@ -2175,7 +2274,7 @@ function applyKeywords(schema, instance, path, baseId, ctx) {
2175
2274
  (branch) => applySchema(branch, instance, path, baseId, { ...ctx, errors: probe })
2176
2275
  ).length;
2177
2276
  if (matches !== 1) {
2178
- fail3(
2277
+ fail4(
2179
2278
  "oneOf",
2180
2279
  matches === 0 ? "passt auf keine der erlaubten Varianten" : `passt auf ${matches} Varianten, erlaubt ist genau eine`
2181
2280
  );
@@ -2184,7 +2283,7 @@ function applyKeywords(schema, instance, path, baseId, ctx) {
2184
2283
  if ("not" in schema) {
2185
2284
  const probe = [];
2186
2285
  if (applySchema(schema.not, instance, path, baseId, { ...ctx, errors: probe })) {
2187
- fail3("not", "darf hier nicht zutreffen");
2286
+ fail4("not", "darf hier nicht zutreffen");
2188
2287
  }
2189
2288
  }
2190
2289
  if ("if" in schema) {
@@ -2711,6 +2810,34 @@ var SZENEN_SCHEMAS = {
2711
2810
  "string",
2712
2811
  "null"
2713
2812
  ]
2813
+ },
2814
+ "variant": {
2815
+ "type": "string"
2816
+ },
2817
+ "origin": {
2818
+ "type": "string",
2819
+ "const": "floor_center"
2820
+ },
2821
+ "morph": {
2822
+ "type": "object",
2823
+ "properties": {
2824
+ "build": {
2825
+ "type": "number",
2826
+ "minimum": 0,
2827
+ "maximum": 1
2828
+ },
2829
+ "gender": {
2830
+ "type": "number",
2831
+ "minimum": 0,
2832
+ "maximum": 1
2833
+ }
2834
+ },
2835
+ "additionalProperties": false
2836
+ },
2837
+ "preview_color": {
2838
+ "description": "Feste Markerfarbe des Proxys in Editor und Kamera-Preview (#rrggbb). Reiner Kompositionsmarker zur Zuordnung von Proxy und Objekt \u2014 KEINE Angabe zur realen Farbe oder zum Material des Objekts. Fehlt sie, vergibt der Editor eine unterscheidbare Ersatzfarbe.",
2839
+ "type": "string",
2840
+ "pattern": "^#[0-9a-f]{6}$"
2714
2841
  }
2715
2842
  },
2716
2843
  "required": [
@@ -8509,6 +8636,219 @@ function sameResponseAs(run, response) {
8509
8636
  return (run.raw_output ?? "") === response;
8510
8637
  }
8511
8638
 
8639
+ // src/lib/szenen/promptRunVariant.ts
8640
+ var CAMERA_PREVIEW_ASSET_ROLE = "camera_preview";
8641
+ var VARIANT_VISIBILITY_BASIS = "variant_unassessed";
8642
+ var VARIANT_NODE_STATE = "unassessed";
8643
+ var VARIANT_SYSTEM_PROMPT = "Promptvariante eines bestehenden Laufs. Es wurde keine Renderer-Anweisung ausgefuehrt und kein Modell vom Server gerufen; der Prompttext stammt unveraendert vom MCP-Aufrufer. Eingefrorener Stand und Eingangsbilder sind die des Quelllaufs.";
8644
+ var VARIANT_ASSUMPTION = "Die Sichtbarkeit der Szenenknoten wurde fuer diesen Prompttext nicht beurteilt.";
8645
+ var istText7 = (wert) => typeof wert === "string" && wert.trim().length > 0;
8646
+ var isObject12 = (wert) => typeof wert === "object" && wert !== null && !Array.isArray(wert);
8647
+ function unbeurteilteKnoten(input) {
8648
+ return input.scene.nodes.map((node) => ({
8649
+ node_id: node.node_id,
8650
+ state: VARIANT_NODE_STATE,
8651
+ reason: "Promptvariante \u2014 keine Sichtbarkeitsbeurteilung fuer diesen Text.",
8652
+ assumption: true
8653
+ }));
8654
+ }
8655
+ function checkVariantInputAssets(assets, cameraPreview) {
8656
+ if (!Array.isArray(assets)) {
8657
+ return { ok: false, message: '"variant_of.input_assets" muss eine Liste sein.' };
8658
+ }
8659
+ const gesehen = /* @__PURE__ */ new Set();
8660
+ for (const [index, asset] of assets.entries()) {
8661
+ if (!isObject12(asset)) {
8662
+ return { ok: false, message: `"variant_of.input_assets[${index}]" ist kein Objekt.` };
8663
+ }
8664
+ if (!istText7(asset.asset_id)) {
8665
+ return { ok: false, message: `"variant_of.input_assets[${index}].asset_id" fehlt.` };
8666
+ }
8667
+ if (asset.role !== CAMERA_PREVIEW_ASSET_ROLE) {
8668
+ return {
8669
+ ok: false,
8670
+ message: `"variant_of.input_assets[${index}].role" ist unbekannt: ${JSON.stringify(asset.role)}. Erlaubt: ${CAMERA_PREVIEW_ASSET_ROLE}.`
8671
+ };
8672
+ }
8673
+ if (asset.position !== index) {
8674
+ return {
8675
+ ok: false,
8676
+ message: `"variant_of.input_assets[${index}].position" ist ${JSON.stringify(asset.position)}; die Eingangsbilder sind lueckenlos ab 0 geordnet.`
8677
+ };
8678
+ }
8679
+ if (asset.prompt_token !== null && !istText7(asset.prompt_token)) {
8680
+ return {
8681
+ ok: false,
8682
+ message: `"variant_of.input_assets[${index}].prompt_token" muss Text oder null sein.`
8683
+ };
8684
+ }
8685
+ if (typeof asset.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(asset.sha256)) {
8686
+ return { ok: false, message: `"variant_of.input_assets[${index}].sha256" ist ungueltig.` };
8687
+ }
8688
+ const herkunft = asset.origin;
8689
+ if (!isObject12(herkunft) || !istText7(herkunft.run_id) || !istText7(herkunft.item_id)) {
8690
+ return {
8691
+ ok: false,
8692
+ message: `"variant_of.input_assets[${index}].origin" braucht run_id und item_id.`
8693
+ };
8694
+ }
8695
+ if (gesehen.has(asset.asset_id)) {
8696
+ return {
8697
+ ok: false,
8698
+ message: `Asset ${asset.asset_id} steht mehrfach in "variant_of.input_assets".`
8699
+ };
8700
+ }
8701
+ gesehen.add(asset.asset_id);
8702
+ }
8703
+ const previews = assets.filter((asset) => asset.role === CAMERA_PREVIEW_ASSET_ROLE);
8704
+ if (cameraPreview) {
8705
+ if (previews.length !== 1) {
8706
+ return {
8707
+ ok: false,
8708
+ message: `Der Lauf traegt ein Kamera-Preview, die Eingangsbilder nennen aber ${previews.length} davon. Erwartet ist genau das eingefrorene Preview.`
8709
+ };
8710
+ }
8711
+ if (previews[0].sha256 !== cameraPreview.sha256) {
8712
+ return {
8713
+ ok: false,
8714
+ message: "Das Eingangsbild passt nicht zum eingefrorenen Kamera-Preview (sha256 weicht ab)."
8715
+ };
8716
+ }
8717
+ } else if (previews.length > 0) {
8718
+ return {
8719
+ ok: false,
8720
+ message: "Die Eingangsbilder nennen ein Kamera-Preview, der Lauf traegt aber keines."
8721
+ };
8722
+ }
8723
+ return { ok: true };
8724
+ }
8725
+ function checkVariantBlock(record) {
8726
+ const block = record.variant_of;
8727
+ if (block === void 0) return { ok: true };
8728
+ if (!isObject12(block)) return { ok: false, message: '"variant_of" muss ein Objekt sein.' };
8729
+ if (!istText7(block.source_run_id)) {
8730
+ return { ok: false, message: '"variant_of.source_run_id" fehlt oder ist leer.' };
8731
+ }
8732
+ if (block.source_run_id === record.run_id) {
8733
+ return { ok: false, message: "Ein Lauf kann nicht Variante seiner selbst sein." };
8734
+ }
8735
+ if (!istText7(block.operation_id) || block.operation_id !== record.operation_id) {
8736
+ return {
8737
+ ok: false,
8738
+ message: '"variant_of.operation_id" fehlt oder widerspricht "operation_id" des Laufs.'
8739
+ };
8740
+ }
8741
+ if (record.provider !== MCP_CLIENT_PROVIDER || record.status !== "succeeded") {
8742
+ return {
8743
+ ok: false,
8744
+ message: `Ein Variantenlauf ist ein erfolgreicher Lauf mit provider "${MCP_CLIENT_PROVIDER}".`
8745
+ };
8746
+ }
8747
+ return checkVariantInputAssets(
8748
+ block.input_assets,
8749
+ record.camera_preview
8750
+ );
8751
+ }
8752
+ function buildPromptRunVariantRecord(eingabe) {
8753
+ const now = eingabe.now ?? (() => /* @__PURE__ */ new Date());
8754
+ const quelle = eingabe.source;
8755
+ if (!istText7(eingabe.runId) || !istText7(eingabe.operationId)) {
8756
+ return { kind: "rejected", message: "Ein Variantenlauf braucht run_id und operation_id." };
8757
+ }
8758
+ if (!quelle || quelle.status !== "succeeded" || !quelle.output || !quelle.input_payload) {
8759
+ return {
8760
+ kind: "rejected",
8761
+ message: "Nur ein erfolgreicher Lauf mit eingefrorener Eingabe kann Quelle einer Variante sein."
8762
+ };
8763
+ }
8764
+ if (quelle.run_id === eingabe.runId) {
8765
+ return { kind: "rejected", message: "Die Variante braucht eine eigene run_id." };
8766
+ }
8767
+ const eingabeSnapshot = structuredClone(quelle.input_payload);
8768
+ const modell = eingabeSnapshot.target_model ?? {};
8769
+ if (typeof modell.max_prompt_chars === "number" && eingabe.positive.length > modell.max_prompt_chars) {
8770
+ return {
8771
+ kind: "rejected",
8772
+ message: `Der Prompt hat ${eingabe.positive.length} Zeichen; das eingefrorene Zielmodell erlaubt hoechstens ${modell.max_prompt_chars}.`
8773
+ };
8774
+ }
8775
+ const assets = checkVariantInputAssets(eingabe.inputAssets, quelle.camera_preview);
8776
+ if (!assets.ok) return { kind: "rejected", message: assets.message };
8777
+ const notiz = istText7(eingabe.note) ? eingabe.note.trim() : null;
8778
+ const geprueft = checkRunResponse({
8779
+ positive: eingabe.positive,
8780
+ negative: eingabe.negative ?? null,
8781
+ feedback: `Promptvariante von Lauf ${quelle.run_id}; Snapshot und Eingangsbilder unveraendert uebernommen.`,
8782
+ visibility: {
8783
+ basis: VARIANT_VISIBILITY_BASIS,
8784
+ nodes: unbeurteilteKnoten(eingabeSnapshot),
8785
+ assumptions: [VARIANT_ASSUMPTION]
8786
+ },
8787
+ notes: notiz === null ? [] : [`Notiz zur Variante: ${notiz}`]
8788
+ }, eingabeSnapshot);
8789
+ if (!geprueft.ok) return { kind: "rejected", message: geprueft.message };
8790
+ const output = {
8791
+ ...geprueft.output,
8792
+ // **Unveraendert** — `checkRunResponse` liefert den Text ohnehin woertlich.
8793
+ positive: eingabe.positive
8794
+ };
8795
+ const origin = {
8796
+ host: "mcp",
8797
+ ...istText7(eingabe.declaredModelId) ? { declared_model_id: eingabe.declaredModelId } : {},
8798
+ renderer_instruction_was_system_prompt: false
8799
+ };
8800
+ const record = {
8801
+ run_id: eingabe.runId,
8802
+ // Woertlich aus dem Quelllauf: dieselbe Herkunft, dieselben Kennungen.
8803
+ shot_version_id: quelle.shot_version_id,
8804
+ shot_element_id: quelle.shot_element_id,
8805
+ scene_setup_version_id: quelle.scene_setup_version_id,
8806
+ camera_id: quelle.camera_id,
8807
+ target_model_version_id: quelle.target_model_version_id,
8808
+ provider: MCP_CLIENT_PROVIDER,
8809
+ model_id: UNVERIFIED_MODEL_ID,
8810
+ system_prompt: VARIANT_SYSTEM_PROMPT,
8811
+ input_payload: eingabeSnapshot,
8812
+ status: "succeeded",
8813
+ failure: null,
8814
+ output,
8815
+ raw_output: JSON.stringify({
8816
+ positive: eingabe.positive,
8817
+ negative: output.negative,
8818
+ note: notiz
8819
+ }),
8820
+ ...quelle.source ? { source: structuredClone(quelle.source) } : {},
8821
+ ...quelle.camera_preview ? { camera_preview: structuredClone(quelle.camera_preview) } : {},
8822
+ operation_id: eingabe.operationId,
8823
+ execution_origin: origin,
8824
+ variant_of: {
8825
+ source_run_id: quelle.run_id,
8826
+ operation_id: eingabe.operationId,
8827
+ input_assets: eingabe.inputAssets.map((asset) => structuredClone(asset))
8828
+ },
8829
+ created_at: now().toISOString()
8830
+ };
8831
+ return { kind: "ok", record };
8832
+ }
8833
+ function variantOf(run) {
8834
+ const block = run.variant_of;
8835
+ if (!isObject12(block) || !istText7(block.source_run_id)) return null;
8836
+ return block;
8837
+ }
8838
+ function sameVariantRequest(run, anfrage) {
8839
+ const block = variantOf(run);
8840
+ if (!block || block.source_run_id !== anfrage.sourceRunId || !run.output) return false;
8841
+ const negativ = istText7(anfrage.negative) ? anfrage.negative : null;
8842
+ const notiz = istText7(anfrage.note) ? anfrage.note.trim() : null;
8843
+ let gespeichert = {};
8844
+ try {
8845
+ gespeichert = JSON.parse(run.raw_output ?? "{}");
8846
+ } catch {
8847
+ return false;
8848
+ }
8849
+ return run.output.positive === anfrage.positive && (run.output.negative ?? null) === negativ && (gespeichert.note ?? null) === notiz;
8850
+ }
8851
+
8512
8852
  // src/lib/szenen/seedListe.ts
8513
8853
  var DEFAULT_SEED_VORLAGE = {
8514
8854
  name: "Default",
@@ -8636,6 +8976,398 @@ async function leseBilderAusZip(datei) {
8636
8976
  return bilder;
8637
8977
  }
8638
8978
 
8979
+ // src/lib/szenen/stageOperations.ts
8980
+ var NODE_TYPES = ["subject", "object", "surface", "light", "camera_target", "group", "other"];
8981
+ var PROXY_PRIMITIVES = ["humanoid_proxy", "box", "cylinder", "sphere", "plane", "room_shell", "furniture_proxy"];
8982
+ var SLUG = /^[a-z][a-z0-9_]{0,62}$/;
8983
+ var Abgelehnt = class extends Error {
8984
+ };
8985
+ var fail2 = (message) => {
8986
+ throw new Abgelehnt(message);
8987
+ };
8988
+ function vec3(value2, label, positive = false) {
8989
+ if (!Array.isArray(value2) || value2.length !== 3 || value2.some((n) => typeof n !== "number" || !Number.isFinite(n))) {
8990
+ fail2(`${label}: drei endliche Zahlen erforderlich.`);
8991
+ }
8992
+ if (positive && value2.some((n) => n <= 0)) fail2(`${label}: alle Werte m\xFCssen positiv sein.`);
8993
+ return [...value2];
8994
+ }
8995
+ function farbe(value2, label) {
8996
+ const hex = normalizePreviewColor(value2);
8997
+ if (!hex) fail2(`${label}: Farbe als RGB-Hexwert #rrggbb angeben, angekommen ist ${JSON.stringify(value2)}.`);
8998
+ return hex;
8999
+ }
9000
+ function checkProxyShape(proxy) {
9001
+ if (proxy.primitive === "furniture_proxy" && !["chair", "table", "floor_lamp"].includes(proxy.variant)) fail2("M\xF6bel ben\xF6tigen variant: chair, table oder floor_lamp.");
9002
+ if (proxy.pose_ref != null && !["standing", "sitting", "walking", "arms_crossed", "hand_on_chin"].includes(proxy.pose_ref)) fail2("Unbekannte Personenpose.");
9003
+ if (proxy.morph && Object.entries(proxy.morph).some(([key, v]) => !["build", "gender"].includes(key) || typeof v !== "number" || !Number.isFinite(v) || v < 0 || v > 1)) fail2("Morph-Regler ben\xF6tigen endliche Werte zwischen 0 und 1.");
9004
+ }
9005
+ var hiddenOf = (shot) => new Set(Array.isArray(shot?.overrides?.hidden_nodes) ? shot.overrides.hidden_nodes : []);
9006
+ function previewColorNodes(scene, setup, shot) {
9007
+ const hidden = hiddenOf(shot);
9008
+ const overrides = new Map((setup?.node_overrides ?? []).map((o) => [o.node_id, o]));
9009
+ return (Array.isArray(scene.nodes) ? scene.nodes : []).filter((n) => !hidden.has(n.node_id)).map((n) => {
9010
+ const o = overrides.get(n.node_id);
9011
+ return { node_id: n.node_id, type: n.type, proxy_3d: (o && o.proxy_3d !== void 0 ? o.proxy_3d : n.proxy_3d) ?? null };
9012
+ });
9013
+ }
9014
+ function pinPreviewColors(scene, setup, shot) {
9015
+ const nodes = Array.isArray(scene.nodes) ? scene.nodes : [];
9016
+ const colors = stageColors(previewColorNodes(scene, setup, shot));
9017
+ const taken = Object.values(colors).map((c) => c.hex);
9018
+ return {
9019
+ ...scene,
9020
+ nodes: nodes.map((n) => {
9021
+ if (!n.proxy_3d || normalizePreviewColor(n.proxy_3d.preview_color) || setup && proxyUeberschrieben(setup, n.node_id)) return n;
9022
+ let hex = colors[n.node_id]?.hex;
9023
+ if (!hex) {
9024
+ hex = distinctPreviewColor(n.type, taken);
9025
+ taken.push(hex);
9026
+ }
9027
+ return { ...n, proxy_3d: { ...n.proxy_3d, preview_color: hex } };
9028
+ })
9029
+ };
9030
+ }
9031
+ function fremdeVerweise(nodeId, setup, shot) {
9032
+ const out = [];
9033
+ for (const c of setup.camera_rig?.cameras ?? []) {
9034
+ if (c.orientation?.target?.node_ref === nodeId) out.push(`Kamera ${c.camera_id} blickt auf ihn`);
9035
+ if (c.framing?.target?.node_ref === nodeId) out.push(`Kamera ${c.camera_id} rahmt ihn`);
9036
+ if (c.focus?.node_ref === nodeId) out.push(`Kamera ${c.camera_id} fokussiert ihn`);
9037
+ }
9038
+ if (shot) {
9039
+ if (shot.overrides?.focus?.node_ref === nodeId) out.push("der Shot fokussiert ihn");
9040
+ if (shot.overrides?.framing?.target?.node_ref === nodeId) out.push("der Shot rahmt ihn");
9041
+ if (hiddenOf(shot).has(nodeId)) out.push("der Shot blendet ihn aus");
9042
+ }
9043
+ return out;
9044
+ }
9045
+ function proxyUeberschrieben(setup, nodeId) {
9046
+ return (setup.node_overrides ?? []).some((o) => o.node_id === nodeId && o.proxy_3d !== void 0);
9047
+ }
9048
+ function neueNodeId(nodes, type) {
9049
+ const basis = SLUG.test(type) ? type : "node";
9050
+ for (let i = 1; ; i++) {
9051
+ const id = `${basis}_${i}`;
9052
+ if (!nodes.some((n) => n.node_id === id)) return id;
9053
+ }
9054
+ }
9055
+ function applyStageOperations(input, operations) {
9056
+ let scene = structuredClone(input.scene);
9057
+ let setup = structuredClone(input.setup);
9058
+ let shot = input.shot ? structuredClone(input.shot) : null;
9059
+ const changed = { scene: false, setup: false, shot: false };
9060
+ const created = [];
9061
+ let adds = 0, removes = 0;
9062
+ let index = null;
9063
+ try {
9064
+ if (!Array.isArray(scene.nodes)) fail2("Die Szene hat keine Node-Liste.");
9065
+ if (!Array.isArray(setup.camera_rig?.cameras)) fail2("Der Aufbau hat kein Kamera-Rig.");
9066
+ if (!Array.isArray(operations) || operations.length === 0) fail2("Mindestens eine Operation angeben.");
9067
+ const vorher = JSON.stringify(scene);
9068
+ scene = pinPreviewColors(scene, setup, shot);
9069
+ if (JSON.stringify(scene) !== vorher) changed.scene = true;
9070
+ for (const [i, operation] of operations.entries()) {
9071
+ index = i;
9072
+ const nodes = scene.nodes;
9073
+ const findNode = (id) => {
9074
+ const node = nodes.find((n) => n.node_id === id);
9075
+ if (!node) fail2(`Node ${JSON.stringify(id)} gibt es in der Szene nicht.`);
9076
+ return node;
9077
+ };
9078
+ switch (operation.op) {
9079
+ case "add_node": {
9080
+ adds++;
9081
+ if (!NODE_TYPES.includes(operation.type)) fail2(`Unbekannter Node-Typ ${JSON.stringify(operation.type)}.`);
9082
+ if (typeof operation.description !== "string" || !operation.description.trim()) fail2("add_node braucht eine semantische Beschreibung (description).");
9083
+ const id = operation.node_id ?? neueNodeId(nodes, operation.type);
9084
+ if (!SLUG.test(id)) fail2(`node_id ${JSON.stringify(id)} ist kein g\xFCltiger Bezeichner (Kleinbuchstaben, Ziffern, Unterstrich).`);
9085
+ if (nodes.some((n) => n.node_id === id)) fail2(`node_id ${id} ist bereits vergeben.`);
9086
+ const catalogueLabel = operation.label?.replace(/\s+\d+$/, "");
9087
+ const vorlage = STAGE_CATALOGUE.find((e) => e.label === catalogueLabel && e.type === operation.type) ?? (operation.label && operation.type === "object" ? void 0 : STAGE_CATALOGUE.find((e) => e.type === operation.type));
9088
+ const dimensions = operation.dimensions ? vec3(operation.dimensions, "dimensions", true) : [...vorlage?.dimensions ?? [0.5, 0.5, 0.5]];
9089
+ const primitive = operation.primitive ?? vorlage?.primitive ?? "box";
9090
+ if (!PROXY_PRIMITIVES.includes(primitive)) fail2(`Unbekanntes Primitive ${JSON.stringify(primitive)}.`);
9091
+ scene = pinPreviewColors(scene, setup, shot);
9092
+ const taken = Object.values(stageColors(previewColorNodes(scene, setup, shot))).map((c) => c.hex);
9093
+ const color = operation.preview_color !== void 0 ? farbe(operation.preview_color, "preview_color") : distinctPreviewColor(operation.type, taken);
9094
+ const node = {
9095
+ node_id: id,
9096
+ type: operation.type,
9097
+ ...operation.label ? { label: operation.label } : {},
9098
+ semantic: { description: operation.description, ...operation.properties ? { properties: operation.properties } : {} },
9099
+ proxy_3d: {
9100
+ primitive,
9101
+ dimensions,
9102
+ preview_color: color,
9103
+ origin: "floor_center",
9104
+ ...primitive === "furniture_proxy" ? { variant: operation.variant ?? (vorlage && "variant" in vorlage ? vorlage.variant : void 0) } : {},
9105
+ ...primitive === "humanoid_proxy" ? { pose_ref: operation.pose_ref ?? "standing", morph: operation.morph ?? { build: 0.5, gender: 0.5 } } : {}
9106
+ }
9107
+ };
9108
+ scene = { ...scene, unit_system: "blender_z_up_meters", nodes: [...scene.nodes, node] };
9109
+ checkProxyShape(node.proxy_3d);
9110
+ const position = operation.position ? vec3(operation.position, "position") : [0, 0, 0];
9111
+ setup = {
9112
+ ...setup,
9113
+ coordinate_system: "blender_z_up_meters",
9114
+ placements: [...setup.placements ?? [], {
9115
+ node_id: id,
9116
+ position,
9117
+ ...operation.rotation_euler_deg ? { rotation_euler_deg: vec3(operation.rotation_euler_deg, "rotation_euler_deg") } : {},
9118
+ ...operation.scale ? { scale: vec3(operation.scale, "scale", true) } : {}
9119
+ }]
9120
+ };
9121
+ changed.scene = changed.setup = true;
9122
+ created.push(id);
9123
+ break;
9124
+ }
9125
+ case "update_node": {
9126
+ const node = findNode(operation.node_id);
9127
+ const next = { ...node };
9128
+ if (operation.type !== void 0) {
9129
+ if (!NODE_TYPES.includes(operation.type)) fail2(`Unbekannter Node-Typ ${JSON.stringify(operation.type)}.`);
9130
+ next.type = operation.type;
9131
+ }
9132
+ if (operation.label !== void 0) {
9133
+ if (operation.label === null || operation.label === "") delete next.label;
9134
+ else next.label = operation.label;
9135
+ }
9136
+ if (operation.description !== void 0 || operation.properties !== void 0) {
9137
+ if (operation.description !== void 0 && !String(operation.description).trim()) fail2("description darf nicht leer sein.");
9138
+ next.semantic = {
9139
+ ...node.semantic,
9140
+ ...operation.description !== void 0 ? { description: operation.description } : {},
9141
+ ...operation.properties !== void 0 ? { properties: operation.properties } : {}
9142
+ };
9143
+ }
9144
+ if (operation.primitive !== void 0 || operation.dimensions !== void 0 || operation.preview_color !== void 0 || operation.variant !== void 0 || operation.pose_ref !== void 0 || operation.morph !== void 0) {
9145
+ if (proxyUeberschrieben(setup, node.node_id)) {
9146
+ fail2(`Node ${node.node_id} hat im Aufbau eine proxy_3d-\xDCberschreibung; Geometrie und Farbe wirken dort, nicht in der Szene.`);
9147
+ }
9148
+ const proxy = { ...node.proxy_3d ?? {} };
9149
+ if (!node.proxy_3d && operation.dimensions === void 0) fail2(`Node ${node.node_id} hat keine Proxy-Geometrie; zuerst dimensions setzen.`);
9150
+ if (operation.primitive !== void 0) {
9151
+ if (!PROXY_PRIMITIVES.includes(operation.primitive)) fail2(`Unbekanntes Primitive ${JSON.stringify(operation.primitive)}.`);
9152
+ proxy.primitive = operation.primitive;
9153
+ }
9154
+ proxy.primitive ?? (proxy.primitive = "box");
9155
+ if (operation.dimensions !== void 0) proxy.dimensions = vec3(operation.dimensions, "dimensions", true);
9156
+ if (operation.variant !== void 0) proxy.variant = operation.variant;
9157
+ if (operation.pose_ref !== void 0) proxy.pose_ref = operation.pose_ref;
9158
+ if (operation.morph !== void 0) proxy.morph = { ...proxy.morph, ...operation.morph };
9159
+ if (operation.preview_color === null) delete proxy.preview_color;
9160
+ else if (operation.preview_color !== void 0) proxy.preview_color = farbe(operation.preview_color, "preview_color");
9161
+ checkProxyShape(proxy);
9162
+ next.proxy_3d = proxy;
9163
+ }
9164
+ scene = { ...scene, nodes: nodes.map((n) => n === node ? next : n) };
9165
+ changed.scene = true;
9166
+ break;
9167
+ }
9168
+ case "place_node": {
9169
+ findNode(operation.node_id);
9170
+ const placements = setup.placements ?? [];
9171
+ const vorhanden = placements.find((p) => p.node_id === operation.node_id);
9172
+ if (!vorhanden && !operation.position) fail2(`Node ${operation.node_id} ist noch nicht platziert; position angeben.`);
9173
+ const next = {
9174
+ ...vorhanden ?? { node_id: operation.node_id },
9175
+ ...operation.position ? { position: vec3(operation.position, "position") } : {},
9176
+ ...operation.rotation_euler_deg ? { rotation_euler_deg: vec3(operation.rotation_euler_deg, "rotation_euler_deg") } : {},
9177
+ ...operation.scale ? { scale: vec3(operation.scale, "scale", true) } : {}
9178
+ };
9179
+ setup = {
9180
+ ...setup,
9181
+ coordinate_system: setup.coordinate_system ?? "blender_z_up_meters",
9182
+ placements: vorhanden ? placements.map((p) => p === vorhanden ? next : p) : [...placements, next]
9183
+ };
9184
+ changed.setup = true;
9185
+ break;
9186
+ }
9187
+ case "remove_node": {
9188
+ removes++;
9189
+ findNode(operation.node_id);
9190
+ const verweise = fremdeVerweise(operation.node_id, setup, shot);
9191
+ if (verweise.length) fail2(`Node ${operation.node_id} wird noch verwendet: ${verweise.join("; ")}. Zuerst diese Bez\xFCge \xE4ndern.`);
9192
+ scene = pinPreviewColors(scene, setup, shot);
9193
+ scene = {
9194
+ ...scene,
9195
+ nodes: scene.nodes.filter((n) => n.node_id !== operation.node_id),
9196
+ relations: (scene.relations ?? []).filter((r) => r.from !== operation.node_id && r.to !== operation.node_id)
9197
+ };
9198
+ setup = {
9199
+ ...setup,
9200
+ ...setup.placements ? { placements: setup.placements.filter((p) => p.node_id !== operation.node_id) } : {},
9201
+ ...setup.node_overrides ? { node_overrides: setup.node_overrides.filter((o) => o.node_id !== operation.node_id) } : {}
9202
+ };
9203
+ changed.scene = changed.setup = true;
9204
+ break;
9205
+ }
9206
+ case "add_camera": {
9207
+ const camera = operation.camera;
9208
+ if (!camera || typeof camera !== "object" || !SLUG.test(camera.camera_id)) fail2("add_camera braucht ein Kameraobjekt mit g\xFCltiger camera_id.");
9209
+ if (setup.camera_rig.cameras.some((c) => c.camera_id === camera.camera_id)) fail2(`camera_id ${camera.camera_id} ist bereits vergeben.`);
9210
+ if (typeof camera.aspect_ratio !== "string") fail2("Eine neue Kamera braucht ein explizites aspect_ratio.");
9211
+ for (const ref of [camera.orientation?.target?.node_ref, camera.framing?.target?.node_ref, camera.focus?.node_ref]) {
9212
+ if (ref !== void 0) findNode(ref);
9213
+ }
9214
+ setup = { ...setup, camera_rig: { ...setup.camera_rig, cameras: [...setup.camera_rig.cameras, structuredClone(camera)] } };
9215
+ changed.setup = true;
9216
+ break;
9217
+ }
9218
+ case "update_camera": {
9219
+ const cameras = setup.camera_rig.cameras;
9220
+ const camera = cameras.find((c) => c.camera_id === operation.camera_id);
9221
+ if (!camera) fail2(`Kamera ${operation.camera_id} gibt es im Aufbau nicht.`);
9222
+ if (!operation.changes || typeof operation.changes !== "object") fail2("update_camera braucht changes.");
9223
+ if ("camera_id" in operation.changes && operation.changes.camera_id !== operation.camera_id) fail2("camera_id ist unver\xE4nderlich.");
9224
+ const next = { ...camera, ...structuredClone(operation.changes) };
9225
+ for (const ref of [next.orientation?.target?.node_ref, next.framing?.target?.node_ref, next.focus?.node_ref]) {
9226
+ if (ref !== void 0) findNode(ref);
9227
+ }
9228
+ setup = { ...setup, camera_rig: { ...setup.camera_rig, cameras: cameras.map((c) => c === camera ? next : c) } };
9229
+ changed.setup = true;
9230
+ break;
9231
+ }
9232
+ case "set_shot_camera": {
9233
+ if (!shot) fail2("Ohne Shot-Entwurf gibt es keine aktive Kamera zu setzen.");
9234
+ if (!setup.camera_rig.cameras.some((c) => c.camera_id === operation.camera_id)) fail2(`Kamera ${operation.camera_id} gibt es im Aufbau nicht.`);
9235
+ shot = { ...shot, camera_id: operation.camera_id };
9236
+ changed.shot = true;
9237
+ break;
9238
+ }
9239
+ default:
9240
+ fail2(`Unbekannte Operation ${JSON.stringify(operation.op)}.`);
9241
+ }
9242
+ }
9243
+ index = null;
9244
+ if (adds && removes) fail2("Hinzuf\xFCgen und Entfernen bitte in getrennten Aufrufen: F\xFCr beides zugleich gibt es keine Schreibreihenfolge ohne verwaisten Zwischenstand.");
9245
+ const ids = new Set(scene.nodes.map((n) => n.node_id));
9246
+ const waise = (setup.placements ?? []).filter((p) => !ids.has(p.node_id)).map((p) => p.node_id);
9247
+ if (waise.length) fail2(`Placements ohne Node: ${waise.join(", ")}.`);
9248
+ if (shot && !setup.camera_rig.cameras.some((c) => c.camera_id === shot.camera_id)) fail2(`Die aktive Kamera ${shot.camera_id} fehlt im Aufbau.`);
9249
+ } catch (e) {
9250
+ if (e instanceof Abgelehnt) return { kind: "rejected", message: e.message, operation_index: index };
9251
+ throw e;
9252
+ }
9253
+ const order = removes ? ["setup", "scene", "shot"] : ["scene", "setup", "shot"];
9254
+ return {
9255
+ kind: "ok",
9256
+ payloads: { scene, setup, shot },
9257
+ changed,
9258
+ created_node_ids: created,
9259
+ write_order: order.filter((k) => changed[k]),
9260
+ color_conflicts: previewColorConflicts(previewColorNodes(scene, setup, shot))
9261
+ };
9262
+ }
9263
+
9264
+ // src/lib/szenen/stageChain.ts
9265
+ function stageValue(r) {
9266
+ if (r.kind !== "ok" || r.value == null) {
9267
+ throw new Error(r.message ?? (r.kind === "conflict" ? "Der Entwurf wurde parallel ge\xE4ndert; die letzte \xC4nderung wurde nicht gespeichert." : `Operation fehlgeschlagen: ${r.kind}`));
9268
+ }
9269
+ return r.value;
9270
+ }
9271
+ async function lies(elements, id) {
9272
+ const gelesen = stageValue(await elements.getElement(id));
9273
+ return gelesen;
9274
+ }
9275
+ async function mitKette(elements, type, chainId) {
9276
+ const liste = stageValue(await elements.listElements({ type }));
9277
+ const treffer = [];
9278
+ for (const eintrag of liste) {
9279
+ const s = await lies(elements, eintrag.element_id);
9280
+ if (s.draft?.chain?.chain_id === chainId) treffer.push(s);
9281
+ }
9282
+ return treffer;
9283
+ }
9284
+ async function findStageDraftChain(elements, sceneElementId) {
9285
+ const scene = await lies(elements, sceneElementId);
9286
+ if (scene.element.type !== "scene") throw new Error(`Element ${sceneElementId} ist keine Szene.`);
9287
+ if (scene.draftFork) throw new Error("Die Szene hat konkurrierende Entw\xFCrfe. Bitte zuerst aufl\xF6sen.");
9288
+ const chainId = scene.draft?.chain?.chain_id;
9289
+ if (!chainId) return null;
9290
+ const setups = await mitKette(elements, "scene_setup", chainId);
9291
+ const shots = await mitKette(elements, "shot", chainId);
9292
+ if (setups.length !== 1 || shots.length !== 1) {
9293
+ throw new Error(`Die Draft-Kette ${chainId} ist unvollst\xE4ndig oder mehrdeutig (${setups.length} Aufbau-, ${shots.length} Shot-Entw\xFCrfe).`);
9294
+ }
9295
+ const link = setups[0].draft.chain.links.find((l) => l.field === "scene_ref");
9296
+ if (link?.target_draft_id !== scene.draft.draft_id) throw new Error("Die gespeicherte Draft-Kette passt nicht zur Szene.");
9297
+ const shotLink = shots[0].draft.chain.links.find((l) => l.field === "scene_setup_ref");
9298
+ if (shotLink?.target_draft_id !== setups[0].draft.draft_id) throw new Error("Der Shot-Entwurf zeigt nicht auf den Aufbau dieser Kette.");
9299
+ return { chain_id: chainId, scene, setup: setups[0], shot: shots[0] };
9300
+ }
9301
+ async function openStageDraftChain(elements, input) {
9302
+ const { sceneElementId, setupVersionId, contextId } = input;
9303
+ const setup = await lies(elements, input.setupElementId);
9304
+ if (setup.element.type !== "scene_setup") throw new Error(`Element ${input.setupElementId} ist kein Scene Setup.`);
9305
+ if (setup.draftFork) throw new Error("Der Aufbau hat konkurrierende Entw\xFCrfe. Bitte zuerst aufl\xF6sen.");
9306
+ if (setupVersionId && setup.draft) throw new Error("F\xFCr diesen Aufbau existiert bereits ein Entwurf. Bitte diesen explizit ausw\xE4hlen.");
9307
+ const setupPayload = setupVersionId ? setup.versions.find((v) => v.version_id === setupVersionId)?.payload : setup.draft?.payload;
9308
+ if (!setupPayload) throw new Error("Der gew\xE4hlte Stand ist nicht mehr vorhanden.");
9309
+ const scene = await lies(elements, sceneElementId);
9310
+ if (scene.draftFork) throw new Error("Die Szene hat konkurrierende Entw\xFCrfe.");
9311
+ const existingChain = setup.draft?.chain;
9312
+ if (existingChain) {
9313
+ const link = existingChain.links.find((l) => l.field === "scene_ref");
9314
+ if (link?.target_draft_id !== scene.draft?.draft_id || scene.draft?.chain?.chain_id !== existingChain.chain_id) {
9315
+ throw new Error("Die gespeicherte Draft-Kette passt nicht zur Szene.");
9316
+ }
9317
+ } else if (scene.draft?.chain) {
9318
+ throw new Error("Die Szene geh\xF6rt bereits zu einer anderen Draft-Kette.");
9319
+ }
9320
+ const ref = setupPayload.scene_ref;
9321
+ if (!existingChain && ref?.element_id && ref.element_id !== sceneElementId) {
9322
+ throw new Error("Der Aufbau geh\xF6rt zu einer anderen Szene.");
9323
+ }
9324
+ const scenePayload = scene.draft?.payload ?? scene.versions.find((v) => v.version_id === ref?.version_id)?.payload;
9325
+ if (!scenePayload) throw new Error("Die referenzierte Szene ist nicht vorhanden.");
9326
+ const chainId = existingChain?.chain_id ?? (input.newChainId ?? (() => crypto.randomUUID()))();
9327
+ const chain = existingChain ?? { chain_id: chainId, origin: "stage", links: [] };
9328
+ let sceneDraft = scene.draft;
9329
+ if (!existingChain) {
9330
+ sceneDraft = stageValue(await elements.writeDraft({
9331
+ elementId: sceneElementId,
9332
+ payload: scenePayload,
9333
+ expectedRevision: scene.draft?.revision ?? 0,
9334
+ baseVersionId: scene.draft?.base_version_id ?? ref?.version_id ?? null,
9335
+ chain: { ...chain, links: [] }
9336
+ }));
9337
+ }
9338
+ let setupDraft = setup.draft;
9339
+ if (!existingChain) {
9340
+ setupDraft = stageValue(await elements.writeDraft({
9341
+ elementId: setup.element.element_id,
9342
+ payload: setupPayload,
9343
+ expectedRevision: setup.draft?.revision ?? 0,
9344
+ baseVersionId: setupVersionId,
9345
+ chain: { ...chain, links: [{ field: "scene_ref", target_element_id: sceneElementId, target_draft_id: sceneDraft.draft_id }] }
9346
+ }));
9347
+ }
9348
+ const shots = await mitKette(elements, "shot", chainId);
9349
+ let shot = shots[0] ?? null;
9350
+ if (!shot) {
9351
+ const created = stageValue(await elements.createElement({ type: "shot", contextId, title: "3D-Kameraansicht" }));
9352
+ const cameraId = setupPayload.camera_rig?.cameras?.[0]?.camera_id;
9353
+ if (!cameraId) throw new Error("Der Aufbau hat keine Kamera.");
9354
+ const shotPayload = { camera_id: cameraId, ...input.targetModelId ? { prompt_render_settings: { target_model_id: input.targetModelId } } : {} };
9355
+ stageValue(await elements.writeDraft({
9356
+ elementId: created.element_id,
9357
+ payload: shotPayload,
9358
+ expectedRevision: 0,
9359
+ chain: { ...chain, links: [{ field: "scene_setup_ref", target_element_id: setup.element.element_id, target_draft_id: setupDraft.draft_id }] }
9360
+ }));
9361
+ shot = await lies(elements, created.element_id);
9362
+ }
9363
+ return {
9364
+ chain_id: chainId,
9365
+ scene: await lies(elements, sceneElementId),
9366
+ setup: await lies(elements, setup.element.element_id),
9367
+ shot
9368
+ };
9369
+ }
9370
+
8639
9371
  // src/lib/szenen/textGenerationPort.ts
8640
9372
  function simulatedAnswerFor(request) {
8641
9373
  let input;
@@ -9140,7 +9872,7 @@ function validateDerivationInput(input) {
9140
9872
  if (input.policy.produces.length === 0) return "Die Regel erzeugt keinen einzigen Vorschlag.";
9141
9873
  return null;
9142
9874
  }
9143
- var isObject12 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
9875
+ var isObject13 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
9144
9876
  var MAX_PROPOSAL_TITLE_LENGTH = 120;
9145
9877
  function fallbackProposalTitle(type) {
9146
9878
  return `Rekonstruktion ${type}`;
@@ -9157,7 +9889,7 @@ function pruefeNodeLabels(payload) {
9157
9889
  if (!Array.isArray(payload.nodes)) return { payload, hinweise: [] };
9158
9890
  const hinweise = [];
9159
9891
  const nodes = payload.nodes.map((node, i) => {
9160
- if (!isObject12(node) || typeof node.node_id !== "string") return node;
9892
+ if (!isObject13(node) || typeof node.node_id !== "string") return node;
9161
9893
  const abgeleitet = kurznameAusNodeId(node.node_id);
9162
9894
  if (!("label" in node)) {
9163
9895
  hinweise.push({
@@ -9190,7 +9922,7 @@ function resolveJsonPointer(wurzel, pointer) {
9190
9922
  aktuell = aktuell[index];
9191
9923
  continue;
9192
9924
  }
9193
- if (!isObject12(aktuell) || !(schluessel in aktuell)) {
9925
+ if (!isObject13(aktuell) || !(schluessel in aktuell)) {
9194
9926
  return { found: false, value: void 0 };
9195
9927
  }
9196
9928
  aktuell = aktuell[schluessel];
@@ -9204,7 +9936,7 @@ function collectVersionRefs2(value2) {
9204
9936
  knoten.forEach(besuchen);
9205
9937
  return;
9206
9938
  }
9207
- if (!isObject12(knoten)) return;
9939
+ if (!isObject13(knoten)) return;
9208
9940
  if (typeof knoten.element_id === "string" && typeof knoten.version_id === "string") {
9209
9941
  gefunden.push({ element_id: knoten.element_id, version_id: knoten.version_id });
9210
9942
  }
@@ -9270,7 +10002,7 @@ function assessDerivationResponse(value2, input) {
9270
10002
  envelope: null,
9271
10003
  contract_ok: false
9272
10004
  });
9273
- if (!isObject12(value2)) {
10005
+ if (!isObject13(value2)) {
9274
10006
  befund({
9275
10007
  code: "response.not_object",
9276
10008
  severity: "error",
@@ -9288,7 +10020,7 @@ function assessDerivationResponse(value2, input) {
9288
10020
  });
9289
10021
  }
9290
10022
  const rohProposals = value2.proposals;
9291
- const proposalsLesbar = isObject12(rohProposals);
10023
+ const proposalsLesbar = isObject13(rohProposals);
9292
10024
  if (!proposalsLesbar) {
9293
10025
  befund({
9294
10026
  code: "envelope.proposals_missing",
@@ -9298,7 +10030,7 @@ function assessDerivationResponse(value2, input) {
9298
10030
  });
9299
10031
  }
9300
10032
  const roh = proposalsLesbar ? rohProposals : {};
9301
- const rohGaps = isObject12(value2.gaps) ? value2.gaps : {};
10033
+ const rohGaps = isObject13(value2.gaps) ? value2.gaps : {};
9302
10034
  const gapsUngefragt = Object.keys(rohGaps).filter(
9303
10035
  (t) => !input.policy.produces.includes(t)
9304
10036
  );
@@ -9310,7 +10042,7 @@ function assessDerivationResponse(value2, input) {
9310
10042
  pointer: "/gaps"
9311
10043
  });
9312
10044
  }
9313
- const fehlend = input.policy.produces.filter((t) => !isObject12(roh[t]));
10045
+ const fehlend = input.policy.produces.filter((t) => !isObject13(roh[t]));
9314
10046
  if (fehlend.length > 0 && proposalsLesbar) {
9315
10047
  const d = befund({
9316
10048
  code: "envelope.proposals_missing_types",
@@ -9331,11 +10063,11 @@ function assessDerivationResponse(value2, input) {
9331
10063
  pointer: "/proposals"
9332
10064
  });
9333
10065
  }
9334
- const rohTitles = isObject12(value2.titles) ? value2.titles : {};
10066
+ const rohTitles = isObject13(value2.titles) ? value2.titles : {};
9335
10067
  const umgebucht = [];
9336
10068
  const ohneLabel = [];
9337
10069
  for (const type of input.policy.produces) {
9338
- if (!isObject12(roh[type])) continue;
10070
+ if (!isObject13(roh[type])) continue;
9339
10071
  const labelPruefung = type === "scene" ? pruefeNodeLabels(roh[type]) : { payload: roh[type], hinweise: [] };
9340
10072
  const payload = labelPruefung.payload;
9341
10073
  const eigene = [];
@@ -9372,7 +10104,7 @@ function assessDerivationResponse(value2, input) {
9372
10104
  const bezugPointer = bezug ? `/${bezug.field}` : null;
9373
10105
  let gapFehler = false;
9374
10106
  for (const eintrag of benannt) {
9375
- if (!isObject12(eintrag) || typeof eintrag.pointer !== "string" || typeof eintrag.reason !== "string" || !eintrag.reason.trim()) {
10107
+ if (!isObject13(eintrag) || typeof eintrag.pointer !== "string" || typeof eintrag.reason !== "string" || !eintrag.reason.trim()) {
9376
10108
  eigene.push(befund({
9377
10109
  code: "proposal.gap_entry_invalid",
9378
10110
  severity: "error",
@@ -9449,7 +10181,7 @@ function assessDerivationResponse(value2, input) {
9449
10181
  });
9450
10182
  } else {
9451
10183
  for (const eintrag of value2.not_derivable) {
9452
- if (!isObject12(eintrag) || typeof eintrag.field !== "string" || typeof eintrag.reason !== "string") {
10184
+ if (!isObject13(eintrag) || typeof eintrag.field !== "string" || typeof eintrag.reason !== "string") {
9453
10185
  befund({
9454
10186
  code: "envelope.not_derivable_entry_invalid",
9455
10187
  severity: "error",
@@ -9474,7 +10206,7 @@ function assessDerivationResponse(value2, input) {
9474
10206
  });
9475
10207
  } else {
9476
10208
  for (const eintrag of value2.conflicts) {
9477
- if (!isObject12(eintrag) || typeof eintrag.field !== "string" || typeof eintrag.from_image !== "string" || typeof eintrag.from_prompt !== "string") {
10209
+ if (!isObject13(eintrag) || typeof eintrag.field !== "string" || typeof eintrag.from_image !== "string" || typeof eintrag.from_prompt !== "string") {
9478
10210
  befund({
9479
10211
  code: "envelope.conflicts_entry_invalid",
9480
10212
  severity: "error",
@@ -10314,9 +11046,46 @@ function usePagedPortQuery(seite, abhaengigkeiten, limit = LOAD_MORE_PAGE_SIZE)
10314
11046
  const [mehrFehler, setMehrFehler] = useState6(null);
10315
11047
  const [token, setToken] = useState6(0);
10316
11048
  const sequenz = useRef4(0);
11049
+ const seitenRef = useRef4([]);
11050
+ seitenRef.current = seiten;
11051
+ const zuletzt = useRef4(null);
10317
11052
  useEffect5(() => {
10318
11053
  const meine = sequenz.current += 1;
10319
11054
  let abgemeldet = false;
11055
+ const vorher = zuletzt.current;
11056
+ zuletzt.current = { deps: abhaengigkeiten, token, limit };
11057
+ const nurNeuLaden = vorher !== null && vorher.token !== token && vorher.limit === limit && vorher.deps.length === abhaengigkeiten.length && vorher.deps.every((d, i) => Object.is(d, abhaengigkeiten[i])) && seitenRef.current.length > 0;
11058
+ const fehler5 = (e) => e instanceof Error ? e.message : String(e);
11059
+ if (nurNeuLaden) {
11060
+ const anzahl = seitenRef.current.length;
11061
+ setMehrFehler(null);
11062
+ void (async () => {
11063
+ const neu = [];
11064
+ let cursor = null;
11065
+ try {
11066
+ while (neu.length < anzahl) {
11067
+ const ergebnis = await seite(cursor, limit);
11068
+ if (abgemeldet || meine !== sequenz.current) return;
11069
+ if (ergebnis.kind !== "ok") {
11070
+ setMehrFehler(ergebnis.message);
11071
+ return;
11072
+ }
11073
+ neu.push(ergebnis.value);
11074
+ if (!ergebnis.value.hasMore) break;
11075
+ cursor = ergebnis.value.nextCursor;
11076
+ }
11077
+ } catch (e) {
11078
+ if (!abgemeldet && meine === sequenz.current) setMehrFehler(fehler5(e));
11079
+ return;
11080
+ }
11081
+ setLaedtMehr(false);
11082
+ setPhase({ status: "ready", value: neu[0] });
11083
+ setSeiten(neu);
11084
+ })();
11085
+ return () => {
11086
+ abgemeldet = true;
11087
+ };
11088
+ }
10320
11089
  setPhase({ status: "loading" });
10321
11090
  setSeiten([]);
10322
11091
  setLaedtMehr(false);
@@ -10337,7 +11106,7 @@ function usePagedPortQuery(seite, abhaengigkeiten, limit = LOAD_MORE_PAGE_SIZE)
10337
11106
  if (abgemeldet || meine !== sequenz.current) return;
10338
11107
  setPhase({
10339
11108
  status: "error",
10340
- message: e instanceof Error ? e.message : String(e),
11109
+ message: fehler5(e),
10341
11110
  retryable: true
10342
11111
  });
10343
11112
  });
@@ -10655,7 +11424,7 @@ function ortVon(instanz) {
10655
11424
 
10656
11425
  // src/features/szenen/areas/SceneEditor.tsx
10657
11426
  import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
10658
- var NODE_TYPES = [
11427
+ var NODE_TYPES2 = [
10659
11428
  "subject",
10660
11429
  "object",
10661
11430
  "surface",
@@ -10807,7 +11576,7 @@ var SceneEditor = ({
10807
11576
  "data-testid": "neuer-node-typ",
10808
11577
  value: neuerTyp,
10809
11578
  onChange: (e) => setNeuerTyp(e.target.value),
10810
- children: NODE_TYPES.map((t) => /* @__PURE__ */ jsx6("option", { value: t, children: NODE_TYPE_LABELS[t] }, t))
11579
+ children: NODE_TYPES2.map((t) => /* @__PURE__ */ jsx6("option", { value: t, children: NODE_TYPE_LABELS[t] }, t))
10811
11580
  }
10812
11581
  ),
10813
11582
  /* @__PURE__ */ jsx6(
@@ -10961,7 +11730,7 @@ var NodeFormular = ({
10961
11730
  value: node.type,
10962
11731
  disabled: readOnly,
10963
11732
  onChange: (e) => aendere({ ...node, type: e.target.value }),
10964
- children: NODE_TYPES.map((t) => /* @__PURE__ */ jsx6("option", { value: t, children: NODE_TYPE_LABELS[t] }, t))
11733
+ children: NODE_TYPES2.map((t) => /* @__PURE__ */ jsx6("option", { value: t, children: NODE_TYPE_LABELS[t] }, t))
10965
11734
  }
10966
11735
  )
10967
11736
  ] }),
@@ -11997,8 +12766,8 @@ function feldLabel(type, pfad, name) {
11997
12766
 
11998
12767
  // src/features/szenen/areas/SchemaFields.tsx
11999
12768
  import { Fragment as Fragment4, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
12000
- var isObject13 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
12001
- var istVersionRef = (v) => isObject13(v) && typeof v.element_id === "string" && typeof v.version_id === "string";
12769
+ var isObject14 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
12770
+ var istVersionRef = (v) => isObject14(v) && typeof v.element_id === "string" && typeof v.version_id === "string";
12002
12771
  function leseJson(wert) {
12003
12772
  if (!wert || wert === NULL_MARKER) return null;
12004
12773
  try {
@@ -12226,13 +12995,13 @@ var VersionRefListeFeld = ({ port, kontextkette, feld: feld2, wert, onChange, re
12226
12995
  }
12227
12996
  const schreiben = (neu) => onChange(
12228
12997
  feld2.path,
12229
- schreibListe(neu.map((eintrag, i) => isObject13(eintrag) && mitRolle2 ? { ...eintrag, rank: i } : eintrag))
12998
+ schreibListe(neu.map((eintrag, i) => isObject14(eintrag) && mitRolle2 ? { ...eintrag, rank: i } : eintrag))
12230
12999
  );
12231
13000
  return /* @__PURE__ */ jsxs10("div", { className: "szenen-feld__reflist", "data-testid": `reflist-${feld2.path}`, children: [
12232
13001
  liste.length === 0 && /* @__PURE__ */ jsx11("p", { "data-testid": `reflist-${feld2.path}-leer`, children: "Keine Eintr\xE4ge." }),
12233
13002
  /* @__PURE__ */ jsx11("ol", { children: liste.map((eintrag, index) => /* @__PURE__ */ jsxs10("li", { "data-testid": `reflist-${feld2.path}-${index}`, children: [
12234
13003
  istVersionRef(eintrag) ? /* @__PURE__ */ jsx11(RefAnzeige, { port, ref_: eintrag, testId: `reflist-${feld2.path}-${index}-ref` }) : /* @__PURE__ */ jsx11("code", { children: JSON.stringify(eintrag) }),
12235
- mitRolle2 === "styleRole" && isObject13(eintrag) && /* @__PURE__ */ jsxs10(
13004
+ mitRolle2 === "styleRole" && isObject14(eintrag) && /* @__PURE__ */ jsxs10(
12236
13005
  "select",
12237
13006
  {
12238
13007
  "data-testid": `reflist-${feld2.path}-${index}-role`,
@@ -12245,7 +13014,7 @@ var VersionRefListeFeld = ({ port, kontextkette, feld: feld2, wert, onChange, re
12245
13014
  ]
12246
13015
  }
12247
13016
  ),
12248
- mitRolle2 === "frei" && isObject13(eintrag) && /* @__PURE__ */ jsx11(
13017
+ mitRolle2 === "frei" && isObject14(eintrag) && /* @__PURE__ */ jsx11(
12249
13018
  "input",
12250
13019
  {
12251
13020
  "data-testid": `reflist-${feld2.path}-${index}-role`,
@@ -12255,7 +13024,7 @@ var VersionRefListeFeld = ({ port, kontextkette, feld: feld2, wert, onChange, re
12255
13024
  onChange: (e) => schreiben(liste.map((x, i) => i === index ? { ...x, role: e.target.value } : x))
12256
13025
  }
12257
13026
  ),
12258
- mitGewicht && isObject13(eintrag) && /* @__PURE__ */ jsx11(
13027
+ mitGewicht && isObject14(eintrag) && /* @__PURE__ */ jsx11(
12259
13028
  "input",
12260
13029
  {
12261
13030
  "data-testid": `reflist-${feld2.path}-${index}-weight`,
@@ -12453,7 +13222,7 @@ var KameraFeld = ({
12453
13222
  if (phase.status !== "ready" || !phase.value) return [];
12454
13223
  const cameras = valueAtPath(phase.value.payload, "camera_rig.cameras");
12455
13224
  if (!Array.isArray(cameras)) return [];
12456
- return cameras.filter(isObject13).map((c) => ({
13225
+ return cameras.filter(isObject14).map((c) => ({
12457
13226
  id: typeof c.camera_id === "string" ? c.camera_id : null,
12458
13227
  name: typeof c.name === "string" ? c.name : null
12459
13228
  })).filter((c) => c.id !== null);
@@ -12598,11 +13367,11 @@ var MediaRefListeFeld = ({ feld: feld2, wert, onChange, readOnly }) => {
12598
13367
  }
12599
13368
  const schreiben = (neu) => onChange(
12600
13369
  feld2.path,
12601
- schreibListe(neu.map((e, i) => isObject13(e) ? { ...e, position: i } : e))
13370
+ schreibListe(neu.map((e, i) => isObject14(e) ? { ...e, position: i } : e))
12602
13371
  );
12603
13372
  return /* @__PURE__ */ jsxs10("div", { className: "szenen-feld__liste", "data-testid": `feld-${feld2.path}`, children: [
12604
13373
  liste.map((eintrag, index) => {
12605
- const objekt = isObject13(eintrag) ? eintrag : {};
13374
+ const objekt = isObject14(eintrag) ? eintrag : {};
12606
13375
  return /* @__PURE__ */ jsxs10("div", { className: "szenen-feld__liste-zeile", children: [
12607
13376
  /* @__PURE__ */ jsx11(
12608
13377
  "input",
@@ -12611,7 +13380,7 @@ var MediaRefListeFeld = ({ feld: feld2, wert, onChange, readOnly }) => {
12611
13380
  value: typeof objekt.asset_id === "string" ? objekt.asset_id : "",
12612
13381
  readOnly,
12613
13382
  placeholder: "asset_id (UUID)",
12614
- onChange: (e) => schreiben(liste.map((x, i) => i === index ? { ...isObject13(x) ? x : {}, asset_id: e.target.value } : x))
13383
+ onChange: (e) => schreiben(liste.map((x, i) => i === index ? { ...isObject14(x) ? x : {}, asset_id: e.target.value } : x))
12615
13384
  }
12616
13385
  ),
12617
13386
  /* @__PURE__ */ jsxs10(
@@ -12620,7 +13389,7 @@ var MediaRefListeFeld = ({ feld: feld2, wert, onChange, readOnly }) => {
12620
13389
  "data-testid": `feld-${feld2.path}-${index}-rolle`,
12621
13390
  value: typeof objekt.role === "string" ? objekt.role : "",
12622
13391
  disabled: readOnly,
12623
- onChange: (e) => schreiben(liste.map((x, i) => i === index ? { ...isObject13(x) ? x : {}, role: e.target.value } : x)),
13392
+ onChange: (e) => schreiben(liste.map((x, i) => i === index ? { ...isObject14(x) ? x : {}, role: e.target.value } : x)),
12624
13393
  children: [
12625
13394
  /* @__PURE__ */ jsx11("option", { value: "", children: "(Rolle w\xE4hlen)" }),
12626
13395
  rollen.map((r) => /* @__PURE__ */ jsx11("option", { value: r, children: r }, r))
@@ -12663,7 +13432,7 @@ var LEERES_PROFIL = {
12663
13432
  var listeAus = (text2) => text2.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
12664
13433
  var AusfuehrungsprofileFeld = ({ feld: feld2, wert, onChange, readOnly }) => {
12665
13434
  const gelesen = leseListe(wert);
12666
- const profile = (gelesen ?? []).filter(isObject13);
13435
+ const profile = (gelesen ?? []).filter(isObject14);
12667
13436
  const unlesbar = gelesen === null;
12668
13437
  const schreibe = (neu) => onChange(feld2.path, schreibListe(neu));
12669
13438
  const alleBefunde = checkImageExecutionProfiles(profile);
@@ -15419,6 +16188,7 @@ var STATUS_TEXT = {
15419
16188
  failed: "fehlgeschlagen"
15420
16189
  };
15421
16190
  function herkunftChip(lauf) {
16191
+ if (variantOf(lauf)) return "Variante \xFCber MCP";
15422
16192
  if (lauf.provider === MANUAL_PROVIDER) return "manuell eingetragen";
15423
16193
  if (lauf.provider === MCP_CLIENT_PROVIDER) return "\xFCber MCP";
15424
16194
  return lauf.model_id;
@@ -15464,6 +16234,7 @@ var LaufInhalt = ({
15464
16234
  onItemGespeichert();
15465
16235
  };
15466
16236
  const handeingabe = manualEntryOf(lauf);
16237
+ const variante = variantOf(lauf);
15467
16238
  const id = lauf.run_id;
15468
16239
  const promptUndTags = /* @__PURE__ */ jsxs18(Fragment9, { children: [
15469
16240
  lauf.status === "succeeded" && lauf.output && /* @__PURE__ */ jsx20(
@@ -15530,6 +16301,23 @@ var LaufInhalt = ({
15530
16301
  /* @__PURE__ */ jsx20("summary", { children: "Erl\xE4uterung des Modells" }),
15531
16302
  /* @__PURE__ */ jsx20("p", { "data-testid": `feedback-${id}`, children: lauf.output.feedback })
15532
16303
  ] }),
16304
+ variante && /* @__PURE__ */ jsxs18("section", { className: "szenen-lauf__handeingabe", "data-testid": `variante-${id}`, children: [
16305
+ /* @__PURE__ */ jsx20("h5", { children: "Promptvariante" }),
16306
+ /* @__PURE__ */ jsxs18("p", { children: [
16307
+ "Snapshot und Eingangsbilder aus Lauf",
16308
+ " ",
16309
+ /* @__PURE__ */ jsx20("code", { children: variante.source_run_id }),
16310
+ "; der Prompttext stammt unver\xE4ndert vom MCP-Aufrufer."
16311
+ ] }),
16312
+ variante.input_assets.map((asset) => /* @__PURE__ */ jsxs18("p", { "data-testid": `variante-asset-${id}-${asset.position}`, children: [
16313
+ "Eingang ",
16314
+ asset.position + 1,
16315
+ asset.prompt_token ? ` (${asset.prompt_token})` : "",
16316
+ ":",
16317
+ " ",
16318
+ /* @__PURE__ */ jsx20("code", { children: asset.asset_id })
16319
+ ] }, asset.asset_id))
16320
+ ] }),
15533
16321
  handeingabe && /* @__PURE__ */ jsxs18("section", { className: "szenen-lauf__handeingabe", "data-testid": `handeingabe-${id}`, children: [
15534
16322
  /* @__PURE__ */ jsx20("h5", { children: "Manuell erfasst" }),
15535
16323
  /* @__PURE__ */ jsxs18("p", { "data-testid": `handeingabe-von-${id}`, children: [
@@ -16658,12 +17446,12 @@ var ElementSheet = ({
16658
17446
  import { useEffect as useEffect15, useMemo as useMemo6, useRef as useRef11, useState as useState21 } from "react";
16659
17447
 
16660
17448
  // src/lib/szenen/bausteinKatalog.ts
16661
- var isObject14 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
17449
+ var isObject15 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
16662
17450
  var text = (v) => typeof v === "string" && v.trim() !== "" ? v : null;
16663
17451
  function kachelAssetId(payload) {
16664
- const refs = isObject14(payload) && Array.isArray(payload.media_refs) ? payload.media_refs : [];
17452
+ const refs = isObject15(payload) && Array.isArray(payload.media_refs) ? payload.media_refs : [];
16665
17453
  for (const rolle of ["example", "reference"]) {
16666
- const treffer = refs.find((r) => isObject14(r) && r.role === rolle && typeof r.asset_id === "string");
17454
+ const treffer = refs.find((r) => isObject15(r) && r.role === rolle && typeof r.asset_id === "string");
16667
17455
  if (treffer) return treffer.asset_id;
16668
17456
  }
16669
17457
  return null;
@@ -16706,7 +17494,7 @@ async function bausteinKatalog(queries, kette) {
16706
17494
  katalog.push({ ...basis, titel, zustand: { art: "fork" } });
16707
17495
  continue;
16708
17496
  }
16709
- const payload = isObject14(version.payload) ? version.payload : {};
17497
+ const payload = isObject15(version.payload) ? version.payload : {};
16710
17498
  katalog.push({
16711
17499
  element_id: stammsatz.element_id,
16712
17500
  titel,
@@ -17037,7 +17825,7 @@ var BausteinSheet = ({
17037
17825
  import React21, { useEffect as useEffect17, useState as useState23, useRef as useRef12, useReducer } from "react";
17038
17826
 
17039
17827
  // src/features/szenen/areas/loadStageEngine.ts
17040
- var loadStageEngine = () => import("./StageEngine-3MOBM2WX.mjs");
17828
+ var loadStageEngine = () => import("./StageEngine-PYWITIIY.mjs");
17041
17829
 
17042
17830
  // src/features/szenen/areas/StagePromptSettings.tsx
17043
17831
  import { useEffect as useEffect16, useState as useState22 } from "react";
@@ -17050,9 +17838,13 @@ function StagePromptSettings({ port, contextId, forElementId, target, settings,
17050
17838
  const [inspection, setInspection] = useState22(null);
17051
17839
  const [inspectionError, setInspectionError] = useState22("");
17052
17840
  const [previewImage, setPreviewImage] = useState22("");
17841
+ const [request, setRequest] = useState22(0);
17842
+ const [rendering, setRendering] = useState22(false);
17843
+ const [renderedBasis, setRenderedBasis] = useState22(null);
17053
17844
  const inputFingerprint = JSON.stringify(input);
17054
17845
  const settingsFingerprint = JSON.stringify(settings);
17055
17846
  const [instruction, setInstruction] = useState22(settings.user_instruction ?? "");
17847
+ const basis = JSON.stringify([inputFingerprint, settingsFingerprint, instruction, target, forElementId, contextId, tags]);
17056
17848
  useEffect16(() => setInstruction(settings.user_instruction ?? ""), [settings.user_instruction]);
17057
17849
  useEffect16(() => {
17058
17850
  let live = true;
@@ -17110,10 +17902,10 @@ function StagePromptSettings({ port, contextId, forElementId, target, settings,
17110
17902
  };
17111
17903
  }, [port, contextId, forElementId, target, settings.reference_sentence_tag, tags]);
17112
17904
  useEffect16(() => {
17905
+ if (!request) return;
17113
17906
  let live = true;
17114
- setInspection(null);
17115
- setPreviewImage("");
17116
17907
  setInspectionError("");
17908
+ setRendering(true);
17117
17909
  const timer = setTimeout(() => {
17118
17910
  void (async () => {
17119
17911
  try {
@@ -17152,17 +17944,22 @@ function StagePromptSettings({ port, contextId, forElementId, target, settings,
17152
17944
  if (live) {
17153
17945
  setInspection(prepared);
17154
17946
  setPreviewImage(`data:${preview.mime_type};base64,${preview.data}`);
17947
+ setRenderedBasis(basis);
17155
17948
  }
17156
17949
  } catch (e) {
17157
17950
  if (live) setInspectionError(String(e));
17951
+ } finally {
17952
+ if (live) setRendering(false);
17158
17953
  }
17159
17954
  })();
17160
- }, 250);
17955
+ }, 50);
17161
17956
  return () => {
17162
17957
  live = false;
17163
17958
  clearTimeout(timer);
17959
+ setRendering(false);
17164
17960
  };
17165
- }, [port, contextId, forElementId, target, inputFingerprint, settingsFingerprint, instruction, tags, previewRenderer]);
17961
+ }, [request]);
17962
+ const stale = !!inspection && renderedBasis !== basis;
17166
17963
  const selected = settings.prompt_tags ?? [];
17167
17964
  const groups = ["Inhalt", "Stil", "Regeln", "Anweisungen"];
17168
17965
  const missing = selected.filter((s) => !tags.some((t) => same(t, s)));
@@ -17243,8 +18040,11 @@ function StagePromptSettings({ port, contextId, forElementId, target, settings,
17243
18040
  /* @__PURE__ */ jsx27("p", { className: "szenen-3d__referenz", "aria-live": "polite", children: reference }),
17244
18041
  /* @__PURE__ */ jsxs25("div", { className: "szenen-3d__gemini", "aria-label": "Gemini-Eingabe vor der Erzeugung", children: [
17245
18042
  /* @__PURE__ */ jsx27("h5", { children: "Das erh\xE4lt Gemini" }),
18043
+ /* @__PURE__ */ jsx27("button", { type: "button", disabled: rendering || !input || !previewRenderer, onClick: () => setRequest((n) => n + 1), children: inspection ? "Preview aktualisieren" : "Preview erzeugen" }),
18044
+ rendering && /* @__PURE__ */ jsx27("p", { role: "status", children: "Preview wird gerendert \u2026" }),
17246
18045
  inspectionError && /* @__PURE__ */ jsx27("p", { role: "alert", children: inspectionError }),
17247
- !inspection && !inspectionError && /* @__PURE__ */ jsx27("p", { children: "Vorschau wird vorbereitet \u2026" }),
18046
+ !inspection && !inspectionError && !rendering && /* @__PURE__ */ jsx27("p", { className: "szenen-3d__hinweis", children: "Noch kein Preview. Das Rendern dauert auf dem Handy einige Sekunden; beim Ableiten des Prompts wird es ohnehin neu erzeugt." }),
18047
+ stale && !rendering && /* @__PURE__ */ jsx27("p", { role: "status", className: "szenen-3d__hinweis", children: "Veraltet: B\xFChne oder Einstellungen haben sich seit diesem Preview ge\xE4ndert." }),
17248
18048
  inspection && /* @__PURE__ */ jsxs25(Fragment11, { children: [
17249
18049
  previewImage && /* @__PURE__ */ jsx27("img", { src: previewImage, alt: "Referenzbild f\xFCr Gemini" }),
17250
18050
  /* @__PURE__ */ jsx27("p", { children: "Gemini erh\xE4lt dieses Preview und die folgende Objektzuordnung. Bildverweise sind derzeit Asset-Kennungen; Objektbilder werden noch nicht mitgesendet." }),
@@ -17285,7 +18085,9 @@ var value = (r) => {
17285
18085
  return r.value;
17286
18086
  };
17287
18087
  var errorText = (e) => e instanceof Error ? e.message : String(e);
17288
- var VIEWS = [["top", "Draufsicht"], ["side", "Seitenansicht"], ["camera", "Kamera"]];
18088
+ var VIEWS = [["top", "Draufsicht"], ["side", "Seitenansicht"], ["camera", "Kamera"], ["free", "Freie Ansicht"]];
18089
+ var ANCHORS_BASIC = [["center", "Mitte"], ["base", "Boden"]];
18090
+ var ANCHORS_HUMAN = [...ANCHORS_BASIC, ["head", "Kopf"], ["eyes", "Augen"], ["face", "Gesicht"], ["hands", "H\xE4nde"], ["left_shoulder", "Linke Schulter"], ["right_shoulder", "Rechte Schulter"]];
17289
18091
  var StageBoundary = class extends React21.Component {
17290
18092
  constructor() {
17291
18093
  super(...arguments);
@@ -17333,17 +18135,122 @@ function MeterField({ label, axis, value: current, min, step, disabled, onCommit
17333
18135
  )
17334
18136
  ] });
17335
18137
  }
18138
+ function MorphField({ label, value: stored, onCommit }) {
18139
+ const [pending, setPending] = useState23(stored), last = useRef12(stored);
18140
+ useEffect17(() => {
18141
+ setPending(stored);
18142
+ last.current = stored;
18143
+ }, [stored]);
18144
+ const commit = (value2) => {
18145
+ if (value2 !== last.current) {
18146
+ last.current = value2;
18147
+ onCommit(value2);
18148
+ }
18149
+ };
18150
+ return /* @__PURE__ */ jsxs26("label", { className: "szenen-3d__morph", children: [
18151
+ label,
18152
+ /* @__PURE__ */ jsx28(
18153
+ "input",
18154
+ {
18155
+ "aria-label": label,
18156
+ type: "range",
18157
+ min: "0",
18158
+ max: "1",
18159
+ step: ".01",
18160
+ value: pending,
18161
+ onChange: (e) => setPending(Number(e.currentTarget.value)),
18162
+ onPointerUp: (e) => commit(Number(e.currentTarget.value)),
18163
+ onPointerCancel: () => setPending(stored),
18164
+ onKeyUp: (e) => commit(Number(e.currentTarget.value)),
18165
+ onBlur: (e) => commit(Number(e.currentTarget.value))
18166
+ }
18167
+ )
18168
+ ] });
18169
+ }
18170
+ function ColorField({ hex, explicit, disabled, onCommit, onReset }) {
18171
+ const [pending, setPending] = useState23(hex);
18172
+ const [text2, setText] = useState23(hex);
18173
+ useEffect17(() => {
18174
+ setPending(hex);
18175
+ setText(hex);
18176
+ }, [hex]);
18177
+ const changed = pending !== hex;
18178
+ const discard = () => {
18179
+ setPending(hex);
18180
+ setText(hex);
18181
+ };
18182
+ const commit = () => {
18183
+ if (changed) onCommit(pending);
18184
+ };
18185
+ return /* @__PURE__ */ jsxs26("div", { className: "szenen-3d__farbe", children: [
18186
+ /* @__PURE__ */ jsx28(
18187
+ "input",
18188
+ {
18189
+ type: "color",
18190
+ value: pending,
18191
+ disabled,
18192
+ "aria-label": "Markerfarbe w\xE4hlen",
18193
+ onChange: (e) => {
18194
+ const next = normalizePreviewColor(e.currentTarget.value);
18195
+ if (next) {
18196
+ setPending(next);
18197
+ setText(next);
18198
+ }
18199
+ }
18200
+ }
18201
+ ),
18202
+ /* @__PURE__ */ jsx28(
18203
+ "input",
18204
+ {
18205
+ type: "text",
18206
+ value: text2,
18207
+ disabled,
18208
+ inputMode: "text",
18209
+ autoComplete: "off",
18210
+ spellCheck: false,
18211
+ maxLength: 7,
18212
+ "aria-label": "Markerfarbe als Hexwert",
18213
+ onChange: (e) => {
18214
+ const raw = e.currentTarget.value;
18215
+ setText(raw);
18216
+ const next = normalizePreviewColor(raw.startsWith("#") ? raw : `#${raw}`);
18217
+ if (next) setPending(next);
18218
+ },
18219
+ onKeyDown: (e) => {
18220
+ if (e.key === "Enter") {
18221
+ e.preventDefault();
18222
+ commit();
18223
+ } else if (e.key === "Escape") discard();
18224
+ }
18225
+ }
18226
+ ),
18227
+ changed ? /* @__PURE__ */ jsxs26(Fragment12, { children: [
18228
+ /* @__PURE__ */ jsx28("button", { type: "button", className: "szenen-3d__farbe-uebernehmen", disabled, onClick: commit, children: "Farbe \xFCbernehmen" }),
18229
+ /* @__PURE__ */ jsx28("button", { type: "button", disabled, onClick: discard, children: "Verwerfen" })
18230
+ ] }) : explicit ? /* @__PURE__ */ jsx28("button", { type: "button", disabled, onClick: onReset, children: "Standardfarbe" }) : /* @__PURE__ */ jsx28("span", { className: "szenen-3d__farbe-auto", children: "automatisch" })
18231
+ ] });
18232
+ }
17336
18233
  function StageWorkspace({ port, sceneElementId, contextId, onClose, onChanged, embedded = false }) {
17337
18234
  const [engine, setEngine] = useState23(null);
17338
18235
  const [choices, setChoices] = useState23([]);
17339
18236
  const [choice, setChoice] = useState23("");
17340
18237
  const [working, setWorking] = useState23(null);
17341
18238
  const [shown, setShown] = useState23(null);
18239
+ const shownRef = useRef12(null);
17342
18240
  const previewRef = useRef12(null);
17343
18241
  const [input, setInput] = useState23(null);
18242
+ const resolvedRef = useRef12(null), exportSources = useRef12(null);
17344
18243
  const [view, setView] = useState23("top");
17345
18244
  const [selected, setSelected] = useState23(null);
18245
+ const [objectTarget, setObjectTarget] = useState23("");
18246
+ const [sichtbar, setSichtbar] = useState23({ nodes: /* @__PURE__ */ new Set(), walls: /* @__PURE__ */ new Set(), floors: /* @__PURE__ */ new Set(), ground: true, camera: true });
18247
+ const [blatt, setBlatt] = useState23(false);
18248
+ const [vollbild, setVollbild] = useState23(false);
18249
+ const [kameraBlatt, setKameraBlatt] = useState23(false);
18250
+ const buehne = useRef12(null);
18251
+ const [entfernen, setEntfernen] = useState23(null);
17346
18252
  const [message, setMessage] = useState23("B\xFChne wird geladen \u2026");
18253
+ const [vorab, setVorab] = useState23([]);
17347
18254
  const [busy, setBusy] = useState23(false);
17348
18255
  const [pending, setPending] = useState23(0);
17349
18256
  const [targets, setTargets] = useState23([]);
@@ -17373,12 +18280,16 @@ function StageWorkspace({ port, sceneElementId, contextId, onClose, onChanged, e
17373
18280
  chain: { scene: { element_id: w.scene.element.element_id, version_id: "", payload: w.scenePayload }, setup: { element_id: w.setup.element.element_id, version_id: "", payload: w.setupPayload } }
17374
18281
  });
17375
18282
  if (result.kind !== "resolved") throw new Error(result.failure.message);
17376
- setInput(withCameraPreviewMetadata(result.input, w.scenePayload));
18283
+ const resolved = withCameraPreviewMetadata(result.input, w.scenePayload);
18284
+ resolvedRef.current = resolved;
18285
+ exportSources.current = Object.fromEntries(["scene", "setup", "shot"].map((key) => [key, { element_id: w[key].element.element_id, version_id: null, draft: { draft_id: w[key].draft.draft_id, revision: w[key].draft.revision } }]));
18286
+ setInput(resolved);
18287
+ return resolved;
17377
18288
  }
17378
18289
  async function reload(w) {
17379
18290
  const [scene, setup, shot] = await Promise.all([read(w.scene.element.element_id), read(w.setup.element.element_id), read(w.shot.element.element_id)]);
17380
18291
  if (!scene.draft || !setup.draft || !shot.draft) throw new Error("Ein B\xFChnenentwurf fehlt. Bitte den Aufbau erneut \xF6ffnen.");
17381
- await resolve({ scene, setup, shot, scenePayload: scene.draft.payload, setupPayload: setup.draft.payload, shotPayload: shot.draft.payload });
18292
+ return resolve({ scene, setup, shot, scenePayload: scene.draft.payload, setupPayload: setup.draft.payload, shotPayload: shot.draft.payload });
17382
18293
  }
17383
18294
  async function act(operation) {
17384
18295
  if (busy) return;
@@ -17398,8 +18309,8 @@ function StageWorkspace({ port, sceneElementId, contextId, onClose, onChanged, e
17398
18309
  setPending((n) => n + 1);
17399
18310
  setMessage("");
17400
18311
  queue.current = queue.current.then(async () => {
17401
- if (generation !== epoch.current) return;
17402
18312
  try {
18313
+ if (generation !== epoch.current) return;
17403
18314
  if (!workingRef.current && previewRef.current) await open(previewRef.current);
17404
18315
  const w = workingRef.current;
17405
18316
  if (!w) return;
@@ -17424,6 +18335,10 @@ function StageWorkspace({ port, sceneElementId, contextId, onClose, onChanged, e
17424
18335
  });
17425
18336
  }
17426
18337
  async function write(w, which, payload) {
18338
+ const pinned = pinPreviewColors(which === "scene" ? payload : w.scenePayload, w.setupPayload, w.shotPayload);
18339
+ if (which === "scene") payload = pinned;
18340
+ else if (JSON.stringify(pinned) !== JSON.stringify(w.scenePayload))
18341
+ value(await port.elements.writeDraft({ elementId: w.scene.element.element_id, payload: pinned, expectedRevision: w.scene.draft.revision }));
17427
18342
  const s = w[which];
17428
18343
  value(await port.elements.writeDraft({ elementId: s.element.element_id, payload, expectedRevision: s.draft.revision }));
17429
18344
  await reload(w);
@@ -17452,7 +18367,11 @@ function StageWorkspace({ port, sceneElementId, contextId, onClose, onChanged, e
17452
18367
  let live = true;
17453
18368
  void (async () => {
17454
18369
  try {
17455
- const loaded = await loadStageEngine();
18370
+ const engineLaden = loadStageEngine();
18371
+ const szeneFrueh = await read(sceneElementId);
18372
+ const szenePayloadFrueh = szeneFrueh.draft?.payload ?? szeneFrueh.versions[szeneFrueh.versions.length - 1]?.payload;
18373
+ if (live && Array.isArray(szenePayloadFrueh?.nodes)) setVorab(szenePayloadFrueh.nodes);
18374
+ const loaded = await engineLaden;
17456
18375
  const chain = value(await port.context.chainFor(contextId));
17457
18376
  const list = value(await port.elements.listElements({ type: "scene_setup" })).filter((e) => chain.includes(e.context_id));
17458
18377
  const snapshots = await Promise.all(list.map((e) => read(e.element_id)));
@@ -17496,6 +18415,32 @@ function StageWorkspace({ port, sceneElementId, contextId, onClose, onChanged, e
17496
18415
  live = false;
17497
18416
  };
17498
18417
  }, [port, sceneElementId, contextId]);
18418
+ useEffect17(() => {
18419
+ if (selected && blatt) setKameraBlatt(false);
18420
+ }, [selected, blatt]);
18421
+ useEffect17(() => {
18422
+ if (selected && (sichtbar.nodes.has(selected) || sichtbar.walls.has(selected) && sichtbar.floors.has(selected))) setSelected(null);
18423
+ }, [selected, sichtbar]);
18424
+ useEffect17(() => {
18425
+ if (!vollbild) return;
18426
+ const el = buehne.current, root = document.documentElement, overflow = root.style.overflow;
18427
+ root.style.overflow = "hidden";
18428
+ let api = false;
18429
+ const left = () => {
18430
+ if (api && !document.fullscreenElement) setVollbild(false);
18431
+ };
18432
+ document.addEventListener("fullscreenchange", left);
18433
+ if (el?.requestFullscreen && !document.fullscreenElement) el.requestFullscreen({ navigationUI: "hide" }).then(() => {
18434
+ api = true;
18435
+ }, () => {
18436
+ });
18437
+ return () => {
18438
+ document.removeEventListener("fullscreenchange", left);
18439
+ root.style.overflow = overflow;
18440
+ if (document.fullscreenElement === el) void document.exitFullscreen().catch(() => {
18441
+ });
18442
+ };
18443
+ }, [vollbild]);
17499
18444
  async function preview(c) {
17500
18445
  const scene = await read(sceneElementId);
17501
18446
  const scenePayload = scene.draft?.payload ?? scene.versions.find((v) => v.version_id === c.payload.scene_ref?.version_id)?.payload;
@@ -17513,53 +18458,49 @@ function StageWorkspace({ port, sceneElementId, contextId, onClose, onChanged, e
17513
18458
  chain: { scene: { element_id: sceneElementId, version_id: "", payload: scenePayload }, setup: { element_id: c.setup.element.element_id, version_id: c.versionId ?? "", payload: c.payload } }
17514
18459
  });
17515
18460
  if (result.kind !== "resolved") throw new Error(result.failure.message);
18461
+ resolvedRef.current = withCameraPreviewMetadata(result.input, scenePayload);
18462
+ exportSources.current = {
18463
+ scene: { element_id: sceneElementId, version_id: scene.draft ? null : c.payload.scene_ref.version_id, draft: scene.draft ? { draft_id: scene.draft.draft_id, revision: scene.draft.revision } : null },
18464
+ setup: { element_id: c.setup.element.element_id, version_id: c.versionId, draft: c.versionId ? null : { draft_id: c.setup.draft.draft_id, revision: c.setup.draft.revision } },
18465
+ shot: { element_id: null, version_id: null, synthetic: true }
18466
+ };
18467
+ shownRef.current = { scenePayload, setupPayload: c.payload, shotPayload };
17516
18468
  previewRef.current = c;
17517
18469
  setChoice(c.key);
17518
- setShown({ scenePayload, setupPayload: c.payload, shotPayload });
18470
+ setShown(shownRef.current);
17519
18471
  setInput(withCameraPreviewMetadata(result.input, scenePayload));
17520
18472
  }
17521
18473
  async function open(c) {
17522
- const setup = await read(c.setup.element.element_id);
17523
- if (setup.draftFork) throw new Error("Der Aufbau hat konkurrierende Entw\xFCrfe. Bitte zuerst aufl\xF6sen.");
17524
- if (c.versionId && setup.draft) throw new Error("F\xFCr diesen Aufbau existiert bereits ein Entwurf. Bitte diesen explizit ausw\xE4hlen.");
17525
- const setupPayload = c.versionId ? setup.versions.find((v) => v.version_id === c.versionId)?.payload : setup.draft?.payload;
17526
- if (!setupPayload) throw new Error("Der gew\xE4hlte Stand ist nicht mehr vorhanden.");
17527
- const scene = await read(sceneElementId);
17528
- if (scene.draftFork) throw new Error("Die Szene hat konkurrierende Entw\xFCrfe.");
17529
- const existingChain = setup.draft?.chain;
17530
- if (existingChain) {
17531
- const link = existingChain.links.find((l) => l.field === "scene_ref");
17532
- if (link?.target_draft_id !== scene.draft?.draft_id || scene.draft?.chain?.chain_id !== existingChain.chain_id) throw new Error("Die gespeicherte Draft-Kette passt nicht zur Szene.");
17533
- } else if (scene.draft?.chain) throw new Error("Die Szene geh\xF6rt bereits zu einer anderen Draft-Kette.");
17534
- const ref = setupPayload.scene_ref;
17535
- const scenePayload = scene.draft?.payload ?? scene.versions.find((v) => v.version_id === ref?.version_id)?.payload;
17536
- if (!scenePayload) throw new Error("Die referenzierte Szene ist nicht vorhanden.");
17537
- const chainId = existingChain?.chain_id ?? crypto.randomUUID();
17538
- const chain = existingChain ?? { chain_id: chainId, origin: "stage", links: [] };
17539
- let sceneDraft = scene.draft;
17540
- if (!existingChain) sceneDraft = value(await port.elements.writeDraft({ elementId: sceneElementId, payload: scenePayload, expectedRevision: scene.draft?.revision ?? 0, baseVersionId: scene.draft?.base_version_id ?? ref?.version_id ?? null, chain: { ...chain, links: [] } }));
17541
- let setupDraft = setup.draft;
17542
- if (!existingChain) setupDraft = value(await port.elements.writeDraft({ elementId: setup.element.element_id, payload: setupPayload, expectedRevision: setup.draft?.revision ?? 0, baseVersionId: c.versionId, chain: { ...chain, links: [{ field: "scene_ref", target_element_id: sceneElementId, target_draft_id: sceneDraft.draft_id }] } }));
17543
- const shots = value(await port.elements.listElements({ type: "shot" }));
17544
- let shot = null;
17545
- for (const entry of shots) {
17546
- const s = await read(entry.element_id);
17547
- if (s.draft?.chain?.chain_id === chainId) {
17548
- shot = s;
17549
- break;
17550
- }
17551
- }
17552
- if (!shot) {
17553
- const created = value(await port.elements.createElement({ type: "shot", contextId, title: "3D-Kameraansicht" }));
17554
- const cameraId = setupPayload.camera_rig?.cameras?.[0]?.camera_id;
17555
- if (!cameraId) throw new Error("Der Aufbau hat keine Kamera.");
17556
- const shotPayload = { camera_id: cameraId, ...target ? { prompt_render_settings: { target_model_id: target } } : {} };
17557
- value(await port.elements.writeDraft({ elementId: created.element_id, payload: shotPayload, expectedRevision: 0, chain: { ...chain, links: [{ field: "scene_setup_ref", target_element_id: setup.element.element_id, target_draft_id: setupDraft.draft_id }] } }));
17558
- shot = await read(created.element_id);
17559
- }
17560
- await reload({ scene, setup, shot, scenePayload, setupPayload, shotPayload: shot.draft.payload });
18474
+ const state = await openStageDraftChain(port.elements, { sceneElementId, setupElementId: c.setup.element.element_id, setupVersionId: c.versionId, contextId, targetModelId: target || null });
18475
+ await reload({ scene: state.scene, setup: state.setup, shot: state.shot, scenePayload: state.scene.draft.payload, setupPayload: state.setup.draft.payload, shotPayload: state.shot.draft.payload });
17561
18476
  previewRef.current = null;
17562
18477
  }
18478
+ async function apply(w, operations) {
18479
+ const result = applyStageOperations({ scene: w.scenePayload, setup: w.setupPayload, shot: w.shotPayload }, operations);
18480
+ if (result.kind !== "ok") throw new Error(result.message);
18481
+ for (const which of result.write_order) {
18482
+ const s = w[which];
18483
+ value(await port.elements.writeDraft({ elementId: s.element.element_id, payload: result.payloads[which], expectedRevision: s.draft.revision }));
18484
+ }
18485
+ await reload(w);
18486
+ return result;
18487
+ }
18488
+ const rotate = (id, rotation) => enqueue(async (w) => {
18489
+ await apply(w, [{ op: "place_node", node_id: id, rotation_euler_deg: rotation }]);
18490
+ });
18491
+ const face = (id, targetId) => enqueue(async (w) => {
18492
+ const saved = await resolve(w), source = saved.scene.nodes.find((n) => n.node_id === id)?.placement;
18493
+ const targetPosition = targetId ? saved.scene.nodes.find((n) => n.node_id === targetId)?.placement?.position : saved.camera?.placement?.position;
18494
+ if (!source || !targetPosition) throw new Error("Quelle oder Ziel ist nicht platziert.");
18495
+ const rotation = [...source.rotation_euler_deg ?? [0, 0, 0]];
18496
+ rotation[2] = facingRotationZ(source.position, targetPosition);
18497
+ await apply(w, [{ op: "place_node", node_id: id, rotation_euler_deg: rotation }]);
18498
+ });
18499
+ const support = (id, targetId) => enqueue(async (w) => {
18500
+ if (!engine) throw new Error("B\xFChne ist noch nicht geladen.");
18501
+ const saved = await resolve(w), position = await engine.stageSupportPosition(saved, id, targetId);
18502
+ await apply(w, [{ op: "place_node", node_id: id, position }]);
18503
+ });
17563
18504
  const display = input && withStagePositions(input, overlay.current.nodes, overlay.current.camera && overlay.current.camera.id === input.camera?.camera_id ? overlay.current.camera.position : null);
17564
18505
  const base = working ?? shown;
17565
18506
  const nodes = base?.scenePayload.nodes ?? [];
@@ -17571,6 +18512,200 @@ function StageWorkspace({ port, sceneElementId, contextId, onClose, onChanged, e
17571
18512
  const cameraPosition = (display?.camera?.camera_id === camera?.camera_id ? display?.camera?.placement?.position : null) ?? camera?.placement?.position;
17572
18513
  const lookAt = camera?.orientation?.mode === "look_at" ? camera.orientation.target?.node_ref : null;
17573
18514
  const title = choices.find((c) => c.key === choice)?.title;
18515
+ const visibleNodes = display?.scene.nodes.filter((n) => !n.hidden) ?? [];
18516
+ const colors = stageColors(visibleNodes);
18517
+ const conflicts = previewColorConflicts(visibleNodes);
18518
+ const umschalten = (key, id) => setSichtbar((v) => {
18519
+ const next = new Set(v[key]);
18520
+ if (!next.delete(id)) next.add(id);
18521
+ return { ...v, [key]: next };
18522
+ });
18523
+ const ausgeblendet = (sichtbar.ground ? 0 : 1) + (sichtbar.camera ? 0 : 1) + nodes.reduce((sum, n) => sum + [sichtbar.nodes, sichtbar.walls, sichtbar.floors].filter((set) => set.has(n.node_id)).length, 0);
18524
+ const anchorsFor = (n) => n?.proxy_3d?.primitive === "humanoid_proxy" ? ANCHORS_HUMAN : ANCHORS_BASIC;
18525
+ const lookNode = lookAt ? nodes.find((n) => n.node_id === lookAt) : void 0;
18526
+ const setLook = (nodeRef, anchor) => edit("setup", (p) => withCamera(p, camera.camera_id, (c) => ({ ...c, orientation: {
18527
+ ...c.orientation?.mode === "look_at" ? c.orientation : {},
18528
+ mode: "look_at",
18529
+ target: { node_ref: nodeRef, anchor }
18530
+ } })));
18531
+ const cameraFields = base ? /* @__PURE__ */ jsxs26(Fragment12, { children: [
18532
+ /* @__PURE__ */ jsxs26("label", { children: [
18533
+ "Kamera",
18534
+ /* @__PURE__ */ jsx28("select", { disabled: busy, value: base.shotPayload.camera_id ?? "", onChange: (e) => {
18535
+ const id = e.target.value;
18536
+ edit("shot", (p) => ({ ...p, camera_id: id }));
18537
+ }, children: base.setupPayload.camera_rig.cameras.map((c) => /* @__PURE__ */ jsx28("option", { value: c.camera_id, children: c.name ?? c.camera_id }, c.camera_id)) })
18538
+ ] }),
18539
+ camera && /* @__PURE__ */ jsxs26("label", { children: [
18540
+ "Kameraformat",
18541
+ /* @__PURE__ */ jsxs26("select", { disabled: busy, value: camera.aspect_ratio ?? "", onChange: (e) => {
18542
+ const ratio = e.target.value;
18543
+ edit("setup", (p) => withCamera(p, camera.camera_id, (c) => ({ ...c, aspect_ratio: ratio })));
18544
+ }, children: [
18545
+ /* @__PURE__ */ jsx28("option", { value: "", children: "Explizit w\xE4hlen \u2026" }),
18546
+ CAMERA_ASPECT_RATIOS.map((r) => /* @__PURE__ */ jsx28("option", { children: r }, r)),
18547
+ camera.aspect_ratio && !CAMERA_ASPECT_RATIOS.includes(camera.aspect_ratio) && /* @__PURE__ */ jsx28("option", { children: camera.aspect_ratio })
18548
+ ] })
18549
+ ] }),
18550
+ camera?.placement?.mode === "absolute" && /* @__PURE__ */ jsxs26("fieldset", { disabled: busy, className: "szenen-3d__werte", children: [
18551
+ /* @__PURE__ */ jsx28("span", { className: "szenen-3d__zeilenkopf", children: "Position (m)" }),
18552
+ ["X", "Y", "Z"].map((axis, index) => /* @__PURE__ */ jsx28(MeterField, { label: "Kameraposition", axis, step: ".1", value: cameraPosition?.[index], onCommit: (n) => edit("setup", (p) => withCamera(p, camera.camera_id, (c) => {
18553
+ const position = [...c.placement.position];
18554
+ position[index] = n;
18555
+ return { ...c, placement: { mode: "absolute", position } };
18556
+ })) }, axis)),
18557
+ camera.orientation?.mode === "explicit" && /* @__PURE__ */ jsxs26(Fragment12, { children: [
18558
+ /* @__PURE__ */ jsx28("span", { className: "szenen-3d__zeilenkopf", children: "Drehung (\xB0)" }),
18559
+ ["X", "Y", "Z"].map((axis, index) => /* @__PURE__ */ jsx28(MeterField, { label: "Kameraausrichtung", axis, step: "1", value: camera.orientation.rotation_euler_deg[index], onCommit: (n) => edit("setup", (p) => withCamera(p, camera.camera_id, (c) => {
18560
+ const rotation_euler_deg = [...c.orientation.rotation_euler_deg];
18561
+ rotation_euler_deg[index] = n;
18562
+ return { ...c, orientation: { mode: "explicit", rotation_euler_deg } };
18563
+ })) }, axis))
18564
+ ] })
18565
+ ] }),
18566
+ camera && /* @__PURE__ */ jsxs26("label", { children: [
18567
+ "Blickziel",
18568
+ /* @__PURE__ */ jsxs26("select", { "aria-label": "Blickziel", disabled: busy, value: lookAt ?? "", onChange: (e) => {
18569
+ const id = e.target.value;
18570
+ if (!id) return;
18571
+ const keep = camera.orientation?.target?.anchor;
18572
+ setLook(id, anchorsFor(nodes.find((n) => n.node_id === id)).some(([k]) => k === keep) ? keep : "center");
18573
+ }, children: [
18574
+ !lookAt && /* @__PURE__ */ jsx28("option", { value: "", children: camera.orientation?.mode === "explicit" ? "Feste Drehung (unten)" : "Ausw\xE4hlen \u2026" }),
18575
+ visibleNodes.filter((n) => n.placement).map((n) => /* @__PURE__ */ jsx28("option", { value: n.node_id, children: labels[n.node_id] }, n.node_id))
18576
+ ] })
18577
+ ] }),
18578
+ camera && lookAt && /* @__PURE__ */ jsxs26("label", { children: [
18579
+ "Blickpunkt",
18580
+ /* @__PURE__ */ jsx28("select", { "aria-label": "Blickpunkt", disabled: busy, value: camera.orientation.target?.anchor ?? "center", onChange: (e) => setLook(lookAt, e.target.value), children: anchorsFor(lookNode).map(([key, title2]) => /* @__PURE__ */ jsx28("option", { value: key, children: title2 }, key)) })
18581
+ ] }),
18582
+ lookAt && /* @__PURE__ */ jsxs26("p", { className: "szenen-3d__hinweis", children: [
18583
+ "Blickt auf ",
18584
+ labels[lookAt] ?? lookAt,
18585
+ ". Kamera in Drauf- oder Seitenansicht ziehen, um sie zu versetzen; antippen \xF6ffnet diese Einstellungen."
18586
+ ] })
18587
+ ] }) : null;
18588
+ const objectSheet = node ? /* @__PURE__ */ jsxs26("div", { className: `szenen-3d__objektblatt${blatt ? "" : " is-zu"}`, role: "region", "aria-label": "Gew\xE4hltes Objekt", children: [
18589
+ /* @__PURE__ */ jsx28("button", { type: "button", className: "szenen-3d__blatt-schliessen", onClick: () => setSelected(null), children: "Objektblatt schlie\xDFen" }),
18590
+ /* @__PURE__ */ jsxs26("fieldset", { disabled: busy, className: "szenen-3d__werte", children: [
18591
+ /* @__PURE__ */ jsxs26("legend", { children: [
18592
+ labels[node.node_id],
18593
+ " ",
18594
+ /* @__PURE__ */ jsx28("span", { children: stageTypeLabel(node.type) })
18595
+ ] }),
18596
+ colors[node.node_id] && /* @__PURE__ */ jsxs26(Fragment12, { children: [
18597
+ /* @__PURE__ */ jsx28("span", { className: "szenen-3d__zeilenkopf", children: "Markerfarbe" }),
18598
+ /* @__PURE__ */ jsx28(
18599
+ ColorField,
18600
+ {
18601
+ hex: colors[node.node_id].hex,
18602
+ explicit: colors[node.node_id].explicit,
18603
+ disabled: busy,
18604
+ onCommit: (hex) => enqueue(async (w) => {
18605
+ await apply(w, [{ op: "update_node", node_id: node.node_id, preview_color: hex }]);
18606
+ }),
18607
+ onReset: () => enqueue(async (w) => {
18608
+ await apply(w, [{ op: "update_node", node_id: node.node_id, preview_color: null }]);
18609
+ })
18610
+ }
18611
+ ),
18612
+ previewColorLegibility(colors[node.node_id].hex) && /* @__PURE__ */ jsx28("p", { role: "status", className: "szenen-3d__farbe-hinweis", children: previewColorLegibility(colors[node.node_id].hex) === "too_dark" ? "Sehr dunkle Markerfarbe: Das Bildmodell kann den Block als Schatten oder dunkle Kleidung lesen. Eine kr\xE4ftige, mittelhelle Farbe ist eindeutiger." : "Sehr helle Markerfarbe: Der Block hebt sich kaum vom hellen Preview-Hintergrund ab." }),
18613
+ conflicts.filter((c) => c.node_ids.includes(node.node_id)).map((c) => {
18614
+ const other = c.node_ids[0] === node.node_id ? c.node_ids[1] : c.node_ids[0];
18615
+ return /* @__PURE__ */ jsxs26("p", { role: "status", className: "szenen-3d__farbe-hinweis", children: [
18616
+ c.identical ? "Gleiche Farbe wie" : "Schwer unterscheidbar von",
18617
+ " ",
18618
+ labels[other] ?? other,
18619
+ " (",
18620
+ colors[other]?.hex,
18621
+ ")."
18622
+ ] }, other);
18623
+ })
18624
+ ] }),
18625
+ /* @__PURE__ */ jsx28("span", { className: "szenen-3d__zeilenkopf", children: "Gr\xF6\xDFe (m)" }),
18626
+ ["X", "Y", "Z"].map((axis, index) => /* @__PURE__ */ jsx28(MeterField, { label: "Gr\xF6\xDFe", axis, step: ".05", min: 0.01, value: node.proxy_3d?.dimensions?.[index], onCommit: (n) => edit("scene", (p) => ({ ...p, nodes: p.nodes.map((x) => {
18627
+ if (x.node_id !== node.node_id) return x;
18628
+ const d = [...x.proxy_3d?.dimensions ?? [1, 1, 1]];
18629
+ d[index] = n;
18630
+ return { ...x, proxy_3d: { ...x.proxy_3d, primitive: x.proxy_3d?.primitive ?? "box", dimensions: d } };
18631
+ }) })) }, axis)),
18632
+ placement ? /* @__PURE__ */ jsxs26(Fragment12, { children: [
18633
+ /* @__PURE__ */ jsx28("span", { className: "szenen-3d__zeilenkopf", children: "Position (m)" }),
18634
+ ["X", "Y", "Z"].map((axis, index) => /* @__PURE__ */ jsx28(MeterField, { label: "Position", axis, step: ".05", value: nodePosition?.[index], onCommit: (n) => edit("setup", (p) => ({ ...p, placements: p.placements.map((x) => {
18635
+ if (x.node_id !== node.node_id) return x;
18636
+ const q = [...x.position];
18637
+ q[index] = n;
18638
+ return { ...x, position: q };
18639
+ }) })) }, axis))
18640
+ ] }) : /* @__PURE__ */ jsx28("button", { type: "button", onClick: () => edit("setup", (p) => ({ ...p, coordinate_system: "blender_z_up_meters", placements: [...p.placements ?? [], { node_id: node.node_id, position: [0, 0, 0] }] })), children: "Am Ursprung platzieren" }),
18641
+ placement && /* @__PURE__ */ jsxs26(Fragment12, { children: [
18642
+ /* @__PURE__ */ jsx28("span", { className: "szenen-3d__zeilenkopf", children: "Drehung (\xB0)" }),
18643
+ /* @__PURE__ */ jsx28(
18644
+ MeterField,
18645
+ {
18646
+ label: "Objektdrehung",
18647
+ axis: "Z",
18648
+ step: "15",
18649
+ value: placement.rotation_euler_deg?.[2] ?? 0,
18650
+ onCommit: (z) => enqueue(async (w) => {
18651
+ const q = w.setupPayload.placements.find((p) => p.node_id === node.node_id);
18652
+ if (!q) throw new Error("Objekt ist nicht mehr platziert.");
18653
+ await apply(w, [{ op: "place_node", node_id: node.node_id, rotation_euler_deg: [q.rotation_euler_deg?.[0] ?? 0, q.rotation_euler_deg?.[1] ?? 0, z] }]);
18654
+ })
18655
+ }
18656
+ ),
18657
+ /* @__PURE__ */ jsxs26("div", { className: "szenen-3d__objekt-aktionen", children: [
18658
+ /* @__PURE__ */ jsx28("button", { type: "button", onClick: () => face(node.node_id), children: "Zur Kamera drehen" }),
18659
+ /* @__PURE__ */ jsx28("button", { type: "button", onClick: () => support(node.node_id), children: "Auf Boden" })
18660
+ ] }),
18661
+ /* @__PURE__ */ jsxs26("label", { className: "szenen-3d__zeilenkopf", children: [
18662
+ "Zielobjekt",
18663
+ /* @__PURE__ */ jsxs26("select", { "aria-label": "Zielobjekt", value: objectTarget, onChange: (e) => setObjectTarget(e.target.value), children: [
18664
+ /* @__PURE__ */ jsx28("option", { value: "", children: "Ausw\xE4hlen \u2026" }),
18665
+ visibleNodes.filter((n) => n.node_id !== node.node_id && n.placement).map((n) => /* @__PURE__ */ jsx28("option", { value: n.node_id, children: labels[n.node_id] }, n.node_id))
18666
+ ] })
18667
+ ] }),
18668
+ /* @__PURE__ */ jsxs26("div", { className: "szenen-3d__objekt-aktionen", children: [
18669
+ /* @__PURE__ */ jsx28("button", { type: "button", disabled: !objectTarget || objectTarget === node.node_id, onClick: () => face(node.node_id, objectTarget), children: "Zu Objekt drehen" }),
18670
+ /* @__PURE__ */ jsx28("button", { type: "button", disabled: !objectTarget || objectTarget === node.node_id, onClick: () => support(node.node_id, objectTarget), children: "Auf Objekt setzen" })
18671
+ ] })
18672
+ ] }),
18673
+ node.proxy_3d?.primitive === "humanoid_proxy" && /* @__PURE__ */ jsxs26(Fragment12, { children: [
18674
+ /* @__PURE__ */ jsx28("div", { className: "szenen-3d__pose", role: "group", "aria-label": "Pose", children: [["standing", "Stehen"], ["sitting", "Sitzen"], ["walking", "Gehen"], ["arms_crossed", "Arme verschr\xE4nkt"], ["hand_on_chin", "Hand am Kinn"]].map(([pose, label]) => /* @__PURE__ */ jsx28("button", { type: "button", "aria-pressed": (node.proxy_3d.pose_ref ?? "standing") === pose, onClick: () => enqueue(async (w) => {
18675
+ await apply(w, [{ op: "update_node", node_id: node.node_id, pose_ref: pose }]);
18676
+ }), children: label }, pose)) }),
18677
+ ["build", "gender"].map((key) => /* @__PURE__ */ jsx28(
18678
+ MorphField,
18679
+ {
18680
+ label: key === "build" ? "Statur" : "Geschlecht",
18681
+ value: node.proxy_3d.morph?.[key] ?? 0.5,
18682
+ onCommit: (v) => enqueue(async (w) => {
18683
+ const current = w.scenePayload.nodes.find((n) => n.node_id === node.node_id);
18684
+ if (!current) throw new Error("Person fehlt.");
18685
+ await apply(w, [{ op: "update_node", node_id: node.node_id, morph: { ...current.proxy_3d.morph, [key]: v } }]);
18686
+ })
18687
+ },
18688
+ key
18689
+ ))
18690
+ ] }),
18691
+ entfernen === node.node_id ? /* @__PURE__ */ jsxs26("div", { className: "szenen-3d__entfernen", role: "group", "aria-label": "Entfernen best\xE4tigen", children: [
18692
+ /* @__PURE__ */ jsxs26("span", { children: [
18693
+ "\u201E",
18694
+ labels[node.node_id],
18695
+ "\u201C aus der Szene entfernen?"
18696
+ ] }),
18697
+ /* @__PURE__ */ jsx28("button", { type: "button", className: "szenen-3d__gefahr", onClick: () => {
18698
+ const id = node.node_id;
18699
+ setEntfernen(null);
18700
+ enqueue(async (w) => {
18701
+ await apply(w, [{ op: "remove_node", node_id: id }]);
18702
+ setSelected(null);
18703
+ });
18704
+ }, children: "Entfernen" }),
18705
+ /* @__PURE__ */ jsx28("button", { type: "button", onClick: () => setEntfernen(null), children: "Abbrechen" })
18706
+ ] }) : /* @__PURE__ */ jsx28("button", { type: "button", onClick: () => setEntfernen(node.node_id), children: "Objekt entfernen" })
18707
+ ] })
18708
+ ] }) : null;
17574
18709
  return /* @__PURE__ */ jsxs26("section", { className: `szenen-3d${embedded ? " szenen-3d--eingebettet" : ""}`, "aria-label": "3D-B\xFChne", "data-testid": "szenen-3d", children: [
17575
18710
  /* @__PURE__ */ jsxs26("header", { className: "szenen-3d__kopf", children: [
17576
18711
  !embedded && /* @__PURE__ */ jsx28("h4", { children: "3D-B\xFChne" }),
@@ -17589,120 +18724,171 @@ function StageWorkspace({ port, sceneElementId, contextId, onClose, onChanged, e
17589
18724
  title.replace(/ · (Entwurf|Version \S+)$/, "")
17590
18725
  ] }),
17591
18726
  base && /* @__PURE__ */ jsx28("span", { className: "szenen-3d__sync", "data-state": pending ? "speichert" : working ? "gespeichert" : "ansicht", "aria-live": "polite", children: pending ? "Speichert \u2026" : working ? "Gespeichert" : "Erste \xC4nderung legt Entwurf an" }),
18727
+ base && engine && /* @__PURE__ */ jsx28("button", { type: "button", disabled: busy, onClick: () => void act(async () => {
18728
+ await queue.current;
18729
+ let payloads;
18730
+ if (workingRef.current) {
18731
+ await reload(workingRef.current);
18732
+ const w = workingRef.current;
18733
+ payloads = w;
18734
+ } else {
18735
+ const c = previewRef.current;
18736
+ if (!c) throw new Error("Bitte einen Aufbau w\xE4hlen.");
18737
+ const setup = await read(c.setup.element.element_id), payload = c.versionId ? setup.versions.find((v) => v.version_id === c.versionId)?.payload : setup.draft?.payload;
18738
+ if (!payload) throw new Error("Der gew\xE4hlte Aufbau fehlt.");
18739
+ await preview({ ...c, setup, payload });
18740
+ payloads = shownRef.current;
18741
+ }
18742
+ if (!resolvedRef.current || !exportSources.current) throw new Error("Der gespeicherte Exportstand fehlt.");
18743
+ const result = await engine.exportStageScene(resolvedRef.current, { payloads: { scene: payloads.scenePayload, setup: payloads.setupPayload, shot: payloads.shotPayload }, sources: exportSources.current }, workingRef.current?.shot.element.title ?? `scene_${resolvedRef.current.camera.camera_id}`);
18744
+ const bytes = new Uint8Array(result.data), url = URL.createObjectURL(new Blob([bytes], { type: "application/zip" }));
18745
+ const link = document.createElement("a");
18746
+ link.href = url;
18747
+ link.download = result.filename;
18748
+ document.body.appendChild(link);
18749
+ link.click();
18750
+ link.remove();
18751
+ setTimeout(() => URL.revokeObjectURL(url), 1e3);
18752
+ setMessage("Szene f\xFCr Blender exportiert.");
18753
+ }), children: "Szene exportieren" }),
17592
18754
  onClose && /* @__PURE__ */ jsx28("button", { type: "button", disabled: busy, onClick: onClose, children: "Schlie\xDFen" })
17593
18755
  ] }),
17594
18756
  message && /* @__PURE__ */ jsx28("p", { role: "status", className: "szenen-3d__meldung", children: message }),
17595
- !base ? null : /* @__PURE__ */ jsxs26(Fragment12, { children: [
18757
+ !base ? /* @__PURE__ */ jsxs26("div", { className: "szenen-3d__geruest", "aria-hidden": "true", children: [
17596
18758
  /* @__PURE__ */ jsxs26("div", { className: "szenen-3d__leiste", children: [
17597
- /* @__PURE__ */ jsx28("div", { className: "szenen-studio__umschalter", role: "group", "aria-label": "3D-Ansicht", children: VIEWS.map(([v, title2]) => /* @__PURE__ */ jsx28("button", { type: "button", "aria-pressed": view === v, onClick: () => setView(v), children: title2 }, v)) }),
17598
- /* @__PURE__ */ jsxs26("div", { className: "szenen-3d__katalog", role: "group", "aria-label": "Objektkatalog", children: [
18759
+ /* @__PURE__ */ jsx28("div", { className: "szenen-studio__umschalter", children: VIEWS.map(([v, title2]) => /* @__PURE__ */ jsx28("button", { type: "button", disabled: true, tabIndex: -1, children: title2 }, v)) }),
18760
+ /* @__PURE__ */ jsxs26("div", { className: "szenen-3d__katalog", children: [
17599
18761
  /* @__PURE__ */ jsx28("span", { children: "Hinzuf\xFCgen" }),
17600
- STAGE_CATALOGUE.map((entry) => /* @__PURE__ */ jsxs26("button", { type: "button", disabled: busy, onClick: () => enqueue(async (w) => {
17601
- const id = `node_${crypto.randomUUID().replace(/-/g, "")}`;
17602
- await write(w, "scene", { ...w.scenePayload, unit_system: "blender_z_up_meters", nodes: [...w.scenePayload.nodes, { node_id: id, label: entry.label, type: entry.type, semantic: { description: entry.label }, proxy_3d: { primitive: "box", dimensions: [...entry.dimensions] } }] });
17603
- const setup = await read(w.setup.element.element_id);
17604
- const p = setup.draft.payload;
17605
- value(await port.elements.writeDraft({ elementId: setup.element.element_id, expectedRevision: setup.draft.revision, payload: { ...p, coordinate_system: "blender_z_up_meters", placements: [...p.placements ?? [], { node_id: id, position: [0, 0, entry.dimensions[2] / 2] }] } }));
17606
- await reload(workingRef.current);
17607
- setSelected(id);
17608
- }), children: [
17609
- /* @__PURE__ */ jsx28("i", { style: { background: stageColor(entry.type) }, "aria-hidden": "true" }),
18762
+ STAGE_CATALOGUE.map((entry) => /* @__PURE__ */ jsxs26("button", { type: "button", disabled: true, tabIndex: -1, children: [
18763
+ /* @__PURE__ */ jsx28("i", { style: { background: stageColor(entry.type) } }),
17610
18764
  "+ ",
17611
18765
  entry.label
17612
18766
  ] }, entry.label))
17613
18767
  ] })
17614
18768
  ] }),
17615
- engine && display && /* @__PURE__ */ jsx28(StageBoundary, { resetKey: input, children: /* @__PURE__ */ jsx28(
17616
- engine.StageEngine,
17617
- {
17618
- input: display,
17619
- view,
17620
- selected,
17621
- labels,
17622
- onSelect: setSelected,
17623
- onMove: (id, p) => move((o) => {
17624
- o.nodes.set(id, p);
17625
- }),
17626
- onMoveCamera: camera?.placement?.mode === "absolute" ? (p) => move((o) => {
17627
- o.camera = { id: camera.camera_id, position: p };
17628
- }) : void 0
17629
- }
17630
- ) }),
18769
+ /* @__PURE__ */ jsx28("div", { className: "szenen-3d__viewport szenen-3d__viewport--laden" }),
18770
+ /* @__PURE__ */ jsxs26("div", { className: "szenen-3d__inspektor", children: [
18771
+ /* @__PURE__ */ jsxs26("section", { className: "szenen-3d__karte", children: [
18772
+ /* @__PURE__ */ jsx28("h5", { children: "Elemente" }),
18773
+ /* @__PURE__ */ jsx28("div", { className: "szenen-3d__elemente", children: vorab.map((n) => /* @__PURE__ */ jsxs26("button", { type: "button", disabled: true, tabIndex: -1, children: [
18774
+ /* @__PURE__ */ jsx28("i", { style: { background: stageColors(vorab)[n.node_id]?.hex ?? stageColor(n.type) } }),
18775
+ n.label ?? n.semantic?.description ?? n.node_id
18776
+ ] }, n.node_id)) }),
18777
+ /* @__PURE__ */ jsx28("p", { className: "szenen-3d__hinweis", children: "Element in der B\xFChne oder hier ausw\xE4hlen." })
18778
+ ] }),
18779
+ /* @__PURE__ */ jsxs26("section", { className: "szenen-3d__karte", children: [
18780
+ /* @__PURE__ */ jsx28("h5", { children: "Aktive Kamera" }),
18781
+ /* @__PURE__ */ jsx28("p", { className: "szenen-3d__hinweis", children: "Kamera wird geladen \u2026" })
18782
+ ] })
18783
+ ] })
18784
+ ] }) : /* @__PURE__ */ jsxs26(Fragment12, { children: [
18785
+ /* @__PURE__ */ jsxs26("div", { ref: buehne, className: `szenen-3d__buehne${vollbild ? " is-vollbild" : ""}`, children: [
18786
+ /* @__PURE__ */ jsxs26("div", { className: "szenen-3d__leiste", children: [
18787
+ /* @__PURE__ */ jsx28("div", { className: "szenen-studio__umschalter", role: "group", "aria-label": "3D-Ansicht", children: VIEWS.map(([v, title2]) => /* @__PURE__ */ jsx28("button", { type: "button", "aria-pressed": view === v, onClick: () => setView(v), children: title2 }, v)) }),
18788
+ /* @__PURE__ */ jsxs26("div", { className: "szenen-3d__katalog", role: "group", "aria-label": "Objektkatalog", children: [
18789
+ /* @__PURE__ */ jsx28("span", { children: "Hinzuf\xFCgen" }),
18790
+ STAGE_CATALOGUE.map((entry) => /* @__PURE__ */ jsxs26("button", { type: "button", disabled: busy, onClick: () => enqueue(async (w) => {
18791
+ const result = await apply(w, [{ op: "add_node", type: entry.type, label: entry.label, description: entry.label, dimensions: [...entry.dimensions] }]);
18792
+ setSelected(result.created_node_ids[0]);
18793
+ }), children: [
18794
+ /* @__PURE__ */ jsx28("i", { style: { background: stageColor(entry.type) }, "aria-hidden": "true" }),
18795
+ "+ ",
18796
+ entry.label
18797
+ ] }, entry.label))
18798
+ ] }),
18799
+ /* @__PURE__ */ jsxs26("div", { className: "szenen-3d__ansichtsoptionen", role: "group", "aria-label": "Anzeige", children: [
18800
+ /* @__PURE__ */ jsxs26("details", { className: "szenen-3d__sichtbar", children: [
18801
+ /* @__PURE__ */ jsxs26("summary", { children: [
18802
+ "Sichtbarkeit",
18803
+ ausgeblendet ? ` \xB7 ${ausgeblendet} aus` : ""
18804
+ ] }),
18805
+ /* @__PURE__ */ jsxs26("div", { className: "szenen-3d__sichtbar-liste", role: "group", "aria-label": "Sichtbarkeit", children: [
18806
+ /* @__PURE__ */ jsxs26("label", { children: [
18807
+ /* @__PURE__ */ jsx28("input", { type: "checkbox", checked: sichtbar.ground, onChange: () => setSichtbar((v) => ({ ...v, ground: !v.ground })) }),
18808
+ "Grundfl\xE4che"
18809
+ ] }),
18810
+ /* @__PURE__ */ jsxs26("label", { children: [
18811
+ /* @__PURE__ */ jsx28("input", { type: "checkbox", checked: sichtbar.camera, onChange: () => setSichtbar((v) => ({ ...v, camera: !v.camera })) }),
18812
+ "Kamerasymbol"
18813
+ ] }),
18814
+ nodes.map((n) => n.proxy_3d?.primitive === "room_shell" ? /* @__PURE__ */ jsxs26(React21.Fragment, { children: [
18815
+ /* @__PURE__ */ jsxs26("label", { children: [
18816
+ /* @__PURE__ */ jsx28("input", { type: "checkbox", checked: !sichtbar.floors.has(n.node_id), onChange: () => umschalten("floors", n.node_id) }),
18817
+ /* @__PURE__ */ jsx28("i", { style: { background: colors[n.node_id]?.hex ?? stageColor(n.type) }, "aria-hidden": "true" }),
18818
+ labels[n.node_id],
18819
+ " \xB7 Boden"
18820
+ ] }),
18821
+ /* @__PURE__ */ jsxs26("label", { children: [
18822
+ /* @__PURE__ */ jsx28("input", { type: "checkbox", checked: !sichtbar.walls.has(n.node_id), onChange: () => umschalten("walls", n.node_id) }),
18823
+ /* @__PURE__ */ jsx28("i", { style: { background: colors[n.node_id]?.hex ?? stageColor(n.type) }, "aria-hidden": "true" }),
18824
+ labels[n.node_id],
18825
+ " \xB7 W\xE4nde"
18826
+ ] })
18827
+ ] }, n.node_id) : /* @__PURE__ */ jsxs26("label", { children: [
18828
+ /* @__PURE__ */ jsx28("input", { type: "checkbox", checked: !sichtbar.nodes.has(n.node_id), onChange: () => umschalten("nodes", n.node_id) }),
18829
+ /* @__PURE__ */ jsx28("i", { style: { background: colors[n.node_id]?.hex ?? stageColor(n.type) }, "aria-hidden": "true" }),
18830
+ labels[n.node_id]
18831
+ ] }, n.node_id)),
18832
+ /* @__PURE__ */ jsx28("button", { type: "button", disabled: !ausgeblendet, onClick: () => setSichtbar({ nodes: /* @__PURE__ */ new Set(), walls: /* @__PURE__ */ new Set(), floors: /* @__PURE__ */ new Set(), ground: true, camera: true }), children: "Alle einblenden" })
18833
+ ] })
18834
+ ] }),
18835
+ /* @__PURE__ */ jsx28("button", { type: "button", className: "szenen-3d__blatt-schalter", "aria-pressed": blatt, onClick: () => setBlatt((b) => !b), children: "Objektblatt" }),
18836
+ vollbild && /* @__PURE__ */ jsx28("button", { type: "button", "aria-pressed": kameraBlatt, onClick: () => {
18837
+ setSelected(null);
18838
+ setKameraBlatt((k) => !k);
18839
+ }, children: "Kamera" }),
18840
+ /* @__PURE__ */ jsx28("button", { type: "button", "aria-pressed": vollbild, onClick: () => {
18841
+ setVollbild((v) => !v);
18842
+ setKameraBlatt(false);
18843
+ }, children: vollbild ? "Vollbild beenden" : "Vollbild" })
18844
+ ] })
18845
+ ] }),
18846
+ engine && display ? /* @__PURE__ */ jsx28(StageBoundary, { resetKey: input, children: /* @__PURE__ */ jsx28(
18847
+ engine.StageEngine,
18848
+ {
18849
+ input: display,
18850
+ view,
18851
+ selected,
18852
+ labels,
18853
+ onSelect: setSelected,
18854
+ onMove: (id, p) => move((o) => {
18855
+ o.nodes.set(id, p);
18856
+ }),
18857
+ onRotate: rotate,
18858
+ onMoveCamera: camera?.placement?.mode === "absolute" ? (p) => move((o) => {
18859
+ o.camera = { id: camera.camera_id, position: p };
18860
+ }) : void 0,
18861
+ visibility: sichtbar,
18862
+ onSelectCamera: blatt ? () => {
18863
+ setSelected(null);
18864
+ setKameraBlatt(true);
18865
+ } : void 0
18866
+ }
18867
+ ) }) : /* @__PURE__ */ jsx28("div", { className: "szenen-3d__viewport szenen-3d__viewport--laden", "aria-hidden": "true" }),
18868
+ vollbild && blatt && objectSheet,
18869
+ vollbild && kameraBlatt && !(blatt && objectSheet) && /* @__PURE__ */ jsxs26("div", { className: "szenen-3d__objektblatt szenen-3d__kamerablatt", role: "region", "aria-label": "Kamera", children: [
18870
+ /* @__PURE__ */ jsx28("button", { type: "button", className: "szenen-3d__blatt-schliessen", onClick: () => setKameraBlatt(false), children: "Kamerablatt schlie\xDFen" }),
18871
+ /* @__PURE__ */ jsxs26("fieldset", { disabled: busy, className: "szenen-3d__werte", children: [
18872
+ /* @__PURE__ */ jsx28("legend", { children: "Kamera" }),
18873
+ cameraFields
18874
+ ] })
18875
+ ] })
18876
+ ] }),
17631
18877
  /* @__PURE__ */ jsxs26("div", { className: "szenen-3d__inspektor", children: [
17632
18878
  /* @__PURE__ */ jsxs26("section", { className: "szenen-3d__karte", children: [
17633
18879
  /* @__PURE__ */ jsx28("h5", { children: "Elemente" }),
17634
18880
  /* @__PURE__ */ jsx28("div", { className: "szenen-3d__elemente", role: "group", "aria-label": "Element w\xE4hlen", children: nodes.map((n) => /* @__PURE__ */ jsxs26("button", { type: "button", "aria-pressed": n.node_id === selected, onClick: () => setSelected(n.node_id === selected ? null : n.node_id), children: [
17635
- /* @__PURE__ */ jsx28("i", { style: { background: stageColors(display?.scene.nodes.filter((n2) => !n2.hidden) ?? [])[n.node_id]?.hex ?? stageColor(n.type) }, "aria-hidden": "true" }),
18881
+ /* @__PURE__ */ jsx28("i", { style: { background: colors[n.node_id]?.hex ?? stageColor(n.type) }, "aria-hidden": "true" }),
17636
18882
  labels[n.node_id]
17637
18883
  ] }, n.node_id)) }),
17638
- node ? /* @__PURE__ */ jsxs26("fieldset", { disabled: busy, className: "szenen-3d__werte", children: [
17639
- /* @__PURE__ */ jsxs26("legend", { children: [
17640
- labels[node.node_id],
17641
- " ",
17642
- /* @__PURE__ */ jsx28("span", { children: stageTypeLabel(node.type) })
17643
- ] }),
17644
- /* @__PURE__ */ jsx28("span", { className: "szenen-3d__zeilenkopf", children: "Gr\xF6\xDFe (m)" }),
17645
- ["X", "Y", "Z"].map((axis, index) => /* @__PURE__ */ jsx28(MeterField, { label: "Gr\xF6\xDFe", axis, step: ".05", min: 0.01, value: node.proxy_3d?.dimensions?.[index], onCommit: (n) => edit("scene", (p) => ({ ...p, nodes: p.nodes.map((x) => {
17646
- if (x.node_id !== node.node_id) return x;
17647
- const d = [...x.proxy_3d?.dimensions ?? [1, 1, 1]];
17648
- d[index] = n;
17649
- return { ...x, proxy_3d: { primitive: x.proxy_3d?.primitive ?? "box", dimensions: d } };
17650
- }) })) }, axis)),
17651
- placement ? /* @__PURE__ */ jsxs26(Fragment12, { children: [
17652
- /* @__PURE__ */ jsx28("span", { className: "szenen-3d__zeilenkopf", children: "Position (m)" }),
17653
- ["X", "Y", "Z"].map((axis, index) => /* @__PURE__ */ jsx28(MeterField, { label: "Position", axis, step: ".05", value: nodePosition?.[index], onCommit: (n) => edit("setup", (p) => ({ ...p, placements: p.placements.map((x) => {
17654
- if (x.node_id !== node.node_id) return x;
17655
- const q = [...x.position];
17656
- q[index] = n;
17657
- return { ...x, position: q };
17658
- }) })) }, axis))
17659
- ] }) : /* @__PURE__ */ jsx28("button", { type: "button", onClick: () => edit("setup", (p) => ({ ...p, coordinate_system: "blender_z_up_meters", placements: [...p.placements ?? [], { node_id: node.node_id, position: [0, 0, 0] }] })), children: "Am Ursprung platzieren" })
17660
- ] }) : /* @__PURE__ */ jsxs26("p", { className: "szenen-3d__hinweis", children: [
18884
+ objectSheet && !vollbild ? objectSheet : /* @__PURE__ */ jsxs26("p", { className: "szenen-3d__hinweis", children: [
17661
18885
  "Element in der B\xFChne oder hier ausw\xE4hlen.",
17662
18886
  /* @__PURE__ */ jsx28("span", { className: "szenen-3d__nur-maus", children: " Pfeiltasten verschieben in 5-cm-Schritten, mit Shift in 50 cm." })
17663
18887
  ] })
17664
18888
  ] }),
17665
18889
  /* @__PURE__ */ jsxs26("section", { className: "szenen-3d__karte", children: [
17666
18890
  /* @__PURE__ */ jsx28("h5", { children: "Aktive Kamera" }),
17667
- /* @__PURE__ */ jsxs26("label", { children: [
17668
- "Kamera",
17669
- /* @__PURE__ */ jsx28("select", { disabled: busy, value: base.shotPayload.camera_id ?? "", onChange: (e) => {
17670
- const id = e.target.value;
17671
- edit("shot", (p) => ({ ...p, camera_id: id }));
17672
- }, children: base.setupPayload.camera_rig.cameras.map((c) => /* @__PURE__ */ jsx28("option", { value: c.camera_id, children: c.name ?? c.camera_id }, c.camera_id)) })
17673
- ] }),
17674
- camera && /* @__PURE__ */ jsxs26("label", { children: [
17675
- "Kameraformat",
17676
- /* @__PURE__ */ jsxs26("select", { disabled: busy, value: camera.aspect_ratio ?? "", onChange: (e) => {
17677
- const ratio = e.target.value;
17678
- edit("setup", (p) => withCamera(p, camera.camera_id, (c) => ({ ...c, aspect_ratio: ratio })));
17679
- }, children: [
17680
- /* @__PURE__ */ jsx28("option", { value: "", children: "Explizit w\xE4hlen \u2026" }),
17681
- CAMERA_ASPECT_RATIOS.map((r) => /* @__PURE__ */ jsx28("option", { children: r }, r)),
17682
- camera.aspect_ratio && !CAMERA_ASPECT_RATIOS.includes(camera.aspect_ratio) && /* @__PURE__ */ jsx28("option", { children: camera.aspect_ratio })
17683
- ] })
17684
- ] }),
17685
- camera?.placement?.mode === "absolute" && /* @__PURE__ */ jsxs26("fieldset", { disabled: busy, className: "szenen-3d__werte", children: [
17686
- /* @__PURE__ */ jsx28("span", { className: "szenen-3d__zeilenkopf", children: "Position (m)" }),
17687
- ["X", "Y", "Z"].map((axis, index) => /* @__PURE__ */ jsx28(MeterField, { label: "Kameraposition", axis, step: ".1", value: cameraPosition?.[index], onCommit: (n) => edit("setup", (p) => withCamera(p, camera.camera_id, (c) => {
17688
- const position = [...c.placement.position];
17689
- position[index] = n;
17690
- return { ...c, placement: { mode: "absolute", position } };
17691
- })) }, axis)),
17692
- camera.orientation?.mode === "explicit" && /* @__PURE__ */ jsxs26(Fragment12, { children: [
17693
- /* @__PURE__ */ jsx28("span", { className: "szenen-3d__zeilenkopf", children: "Drehung (\xB0)" }),
17694
- ["X", "Y", "Z"].map((axis, index) => /* @__PURE__ */ jsx28(MeterField, { label: "Kameraausrichtung", axis, step: "1", value: camera.orientation.rotation_euler_deg[index], onCommit: (n) => edit("setup", (p) => withCamera(p, camera.camera_id, (c) => {
17695
- const rotation_euler_deg = [...c.orientation.rotation_euler_deg];
17696
- rotation_euler_deg[index] = n;
17697
- return { ...c, orientation: { mode: "explicit", rotation_euler_deg } };
17698
- })) }, axis))
17699
- ] })
17700
- ] }),
17701
- lookAt && /* @__PURE__ */ jsxs26("p", { className: "szenen-3d__hinweis", children: [
17702
- "Blickt auf ",
17703
- labels[lookAt] ?? lookAt,
17704
- ". Kamera in Drauf- oder Seitenansicht ziehen, um sie zu versetzen."
17705
- ] })
18891
+ vollbild && kameraBlatt ? null : cameraFields
17706
18892
  ] })
17707
18893
  ] }),
17708
18894
  /* @__PURE__ */ jsxs26("div", { className: "szenen-3d__abschluss", children: [
@@ -17760,7 +18946,9 @@ function StageWorkspace({ port, sceneElementId, contextId, onClose, onChanged, e
17760
18946
  setRefresh((r) => r + 1);
17761
18947
  }), children: "Kamera-Preview nachspeichern" }),
17762
18948
  !port.runs.supportsCameraPreview && /* @__PURE__ */ jsx28("p", { className: "szenen-3d__hinweis", children: "Dieser Host kann das Kamera-Preview noch nicht am Run-Item speichern." }),
17763
- working && /* @__PURE__ */ jsx28("button", { type: "button", className: "szenen-3d__nebenaktion", disabled: busy, onClick: () => enqueue((w) => reload(w)), children: "Gespeicherten Stand neu laden" })
18949
+ working && /* @__PURE__ */ jsx28("button", { type: "button", className: "szenen-3d__nebenaktion", disabled: busy, onClick: () => enqueue(async (w) => {
18950
+ await reload(w);
18951
+ }), children: "Gespeicherten Stand neu laden" })
17764
18952
  ] }),
17765
18953
  working && /* @__PURE__ */ jsx28(RunsPanel, { port, contextId, source: { kind: "draft_chain", chainId: working.shot.draft.chain.chain_id } }, refresh)
17766
18954
  ] })
@@ -17915,7 +19103,7 @@ var ElementDetail = ({
17915
19103
  const setzeFeld = (pfad, wert) => setEntwurf((vorher) => ({ ...vorher, [pfad]: wert }));
17916
19104
  const payloadAusFormular = () => payloadFromForm(felder, entwurf);
17917
19105
  const strukturPayload = useMemo7(() => {
17918
- const lies = (name) => {
19106
+ const lies2 = (name) => {
17919
19107
  try {
17920
19108
  const roh = JSON.parse(entwurf[name] || "[]");
17921
19109
  return Array.isArray(roh) ? roh : [];
@@ -17923,7 +19111,7 @@ var ElementDetail = ({
17923
19111
  return [];
17924
19112
  }
17925
19113
  };
17926
- return { nodes: lies("nodes"), relations: lies("relations") };
19114
+ return { nodes: lies2("nodes"), relations: lies2("relations") };
17927
19115
  }, [entwurf.nodes, entwurf.relations]);
17928
19116
  const umgebungsRefs = useMemo7(() => {
17929
19117
  try {
@@ -22465,11 +23653,11 @@ function createClientDerivationPort(options) {
22465
23653
  }
22466
23654
 
22467
23655
  // src/lib/szenen/inputChecks.ts
22468
- var fail2 = (message) => ({ kind: "error", message, retryable: false });
23656
+ var fail3 = (message) => ({ kind: "error", message, retryable: false });
22469
23657
  var KNOWN_TYPES = new Set(SZENEN_ELEMENT_TYPES);
22470
23658
  function checkRevision(value2, field) {
22471
23659
  if (typeof value2 !== "number" || !Number.isInteger(value2) || value2 < 0) {
22472
- return fail2(
23660
+ return fail3(
22473
23661
  `${field} muss eine ganze Zahl >= 0 sein, war ${JSON.stringify(value2)}. Ohne belastbare Revision ist die Konfliktregel (\xA73.1) nicht anwendbar.`
22474
23662
  );
22475
23663
  }
@@ -22477,7 +23665,7 @@ function checkRevision(value2, field) {
22477
23665
  }
22478
23666
  function checkElementType(value2) {
22479
23667
  if (typeof value2 !== "string" || !KNOWN_TYPES.has(value2)) {
22480
- return fail2(
23668
+ return fail3(
22481
23669
  `Unbekannter Elementtyp ${JSON.stringify(value2)}. Erlaubt: ${SZENEN_ELEMENT_TYPES.join(", ")}. Der Typ ist nach Anlage unveraenderlich (D-02) \u2014 ein falsch angelegtes Element laesst sich nur noch stilllegen.`
22482
23670
  );
22483
23671
  }
@@ -22485,20 +23673,20 @@ function checkElementType(value2) {
22485
23673
  }
22486
23674
  function checkTitle(value2) {
22487
23675
  if (typeof value2 !== "string" || value2.trim().length === 0) {
22488
- return fail2("title muss ein nicht leerer Text sein.");
23676
+ return fail3("title muss ein nicht leerer Text sein.");
22489
23677
  }
22490
23678
  return null;
22491
23679
  }
22492
23680
  function checkOptionalId(value2, field) {
22493
23681
  if (value2 === null || value2 === void 0) return null;
22494
23682
  if (typeof value2 !== "string" || value2.trim().length === 0) {
22495
- return fail2(`${field} muss ein nicht leerer Text sein, wenn es gesetzt ist.`);
23683
+ return fail3(`${field} muss ein nicht leerer Text sein, wenn es gesetzt ist.`);
22496
23684
  }
22497
23685
  return null;
22498
23686
  }
22499
23687
  function checkRequiredId(value2, field) {
22500
23688
  if (typeof value2 !== "string" || value2.trim().length === 0) {
22501
- return fail2(`${field} ist erforderlich.`);
23689
+ return fail3(`${field} ist erforderlich.`);
22502
23690
  }
22503
23691
  return null;
22504
23692
  }
@@ -22506,17 +23694,17 @@ var RELATIONS = /* @__PURE__ */ new Set(["derived_from", "merged_from"]);
22506
23694
  function checkParents(value2) {
22507
23695
  if (value2 === void 0 || value2 === null) return null;
22508
23696
  if (!Array.isArray(value2)) {
22509
- return fail2(`parents muss ein Array sein, war ${typeof value2}.`);
23697
+ return fail3(`parents muss ein Array sein, war ${typeof value2}.`);
22510
23698
  }
22511
23699
  for (const [i, entry] of value2.entries()) {
22512
23700
  if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
22513
- return fail2(`parents[${i}] muss ein Objekt sein.`);
23701
+ return fail3(`parents[${i}] muss ein Objekt sein.`);
22514
23702
  }
22515
23703
  const parent = entry;
22516
23704
  const idError = checkRequiredId(parent.parent_version_id, `parents[${i}].parent_version_id`);
22517
23705
  if (idError) return idError;
22518
23706
  if (typeof parent.relation !== "string" || !RELATIONS.has(parent.relation)) {
22519
- return fail2(
23707
+ return fail3(
22520
23708
  `parents[${i}].relation muss "derived_from" oder "merged_from" sein, war ${JSON.stringify(parent.relation)}.`
22521
23709
  );
22522
23710
  }
@@ -22524,7 +23712,7 @@ function checkParents(value2) {
22524
23712
  return null;
22525
23713
  }
22526
23714
  function checkPayloadPresent(value2) {
22527
- if (value2 === void 0) return fail2("payload ist erforderlich.");
23715
+ if (value2 === void 0) return fail3("payload ist erforderlich.");
22528
23716
  return null;
22529
23717
  }
22530
23718
  function firstError(...checks) {
@@ -22568,7 +23756,7 @@ function checkSetPreferredHeadInput(input) {
22568
23756
  }
22569
23757
  function checkCoverLength(value2) {
22570
23758
  if (typeof value2 === "string" && value2.length > 512) {
22571
- return fail2("cover_media_ref darf hoechstens 512 Zeichen lang sein.");
23759
+ return fail3("cover_media_ref darf hoechstens 512 Zeichen lang sein.");
22572
23760
  }
22573
23761
  return null;
22574
23762
  }
@@ -24147,7 +25335,7 @@ function createFlowImageProvider(generateImage) {
24147
25335
  }
24148
25336
 
24149
25337
  // src/lib/szenen/imageGenerationOrderStore.ts
24150
- function istText7(wert) {
25338
+ function istText8(wert) {
24151
25339
  return typeof wert === "string" && wert.trim().length > 0;
24152
25340
  }
24153
25341
  function collectImageGenerationOrders(events) {
@@ -24155,7 +25343,7 @@ function collectImageGenerationOrders(events) {
24155
25343
  for (const event of events) {
24156
25344
  if (event.type !== SZENEN_EVENT_TYPES.imageGenerationStarted) continue;
24157
25345
  const payload = event.payload;
24158
- if (!payload || !istText7(payload.generation_batch_id) || !istText7(payload.prompt_run_id)) continue;
25346
+ if (!payload || !istText8(payload.generation_batch_id) || !istText8(payload.prompt_run_id)) continue;
24159
25347
  if (byBatchId.has(payload.generation_batch_id)) continue;
24160
25348
  byBatchId.set(payload.generation_batch_id, {
24161
25349
  generation_batch_id: payload.generation_batch_id,
@@ -24169,7 +25357,7 @@ function collectImageGenerationOrders(events) {
24169
25357
  for (const event of events) {
24170
25358
  if (event.type !== SZENEN_EVENT_TYPES.imageGenerationFinished) continue;
24171
25359
  const payload = event.payload;
24172
- if (!payload || !istText7(payload.generation_batch_id)) continue;
25360
+ if (!payload || !istText8(payload.generation_batch_id)) continue;
24173
25361
  const auftrag = byBatchId.get(payload.generation_batch_id);
24174
25362
  if (!auftrag) continue;
24175
25363
  if (auftrag.status !== "started") continue;
@@ -24239,13 +25427,13 @@ function isSzenenItemId(itemId) {
24239
25427
  return itemId.startsWith(SZENEN_ITEM_PREFIX);
24240
25428
  }
24241
25429
  function checkSzenenBatchOrigin(candidate) {
24242
- if (!istText8(candidate.prompt_run_id)) {
25430
+ if (!istText9(candidate.prompt_run_id)) {
24243
25431
  return {
24244
25432
  kind: "rejected",
24245
25433
  failure: { field: "prompt_run_id", message: '"prompt_run_id" fehlt oder ist leer.' }
24246
25434
  };
24247
25435
  }
24248
- if (!istText8(candidate.generation_batch_id)) {
25436
+ if (!istText9(candidate.generation_batch_id)) {
24249
25437
  return {
24250
25438
  kind: "rejected",
24251
25439
  failure: {
@@ -24288,7 +25476,7 @@ function mergeSzenenBatchOrigin(bestehend, neu) {
24288
25476
  };
24289
25477
  }
24290
25478
  var DEFAULT_FSK_LEVEL = "FSK16";
24291
- var istText8 = (v) => typeof v === "string" && v.trim().length > 0;
25479
+ var istText9 = (v) => typeof v === "string" && v.trim().length > 0;
24292
25480
  function roundOfRun(run, runsOfShot) {
24293
25481
  return runsOfShot.findIndex((kandidat) => kandidat.run_id === run.run_id) + 1;
24294
25482
  }
@@ -24324,7 +25512,7 @@ function deriveItemFromRun(input) {
24324
25512
  }
24325
25513
  };
24326
25514
  }
24327
- if (!run.output || !istText8(run.output.positive)) {
25515
+ if (!run.output || !istText9(run.output.positive)) {
24328
25516
  return {
24329
25517
  kind: "rejected",
24330
25518
  failure: {
@@ -24348,8 +25536,8 @@ function deriveItemFromRun(input) {
24348
25536
  }
24349
25537
  const item_id = itemIdForRun(run.run_id);
24350
25538
  const lineage = deriveLineage(item_id, round, existingItems);
24351
- const negative = istText8(run.output.negative) ? run.output.negative : null;
24352
- const title = istText8(input.title) ? input.title : istText8(input.shotTitle) ? `${input.shotTitle} \xB7 Runde ${round}` : `Szenen-Lauf ${round}`;
25539
+ const negative = istText9(run.output.negative) ? run.output.negative : null;
25540
+ const title = istText9(input.title) ? input.title : istText9(input.shotTitle) ? `${input.shotTitle} \xB7 Runde ${round}` : `Szenen-Lauf ${round}`;
24353
25541
  return {
24354
25542
  kind: "ok",
24355
25543
  value: {
@@ -24359,9 +25547,9 @@ function deriveItemFromRun(input) {
24359
25547
  negative_prompt: negative,
24360
25548
  // `feedback` ist Pflicht (D-55) und ist genau die Erlaeuterung fuer den
24361
25549
  // Menschen — also gehoert sie in das Feld fuer Notizen.
24362
- notes: istText8(input.notes) ? input.notes : run.output.feedback,
25550
+ notes: istText9(input.notes) ? input.notes : run.output.feedback,
24363
25551
  rating: typeof input.rating === "number" ? input.rating : 0,
24364
- fsk_level: istText8(input.fskLevel) ? input.fskLevel : DEFAULT_FSK_LEVEL,
25552
+ fsk_level: istText9(input.fskLevel) ? input.fskLevel : DEFAULT_FSK_LEVEL,
24365
25553
  tags: [],
24366
25554
  szenen: {
24367
25555
  prompt_run_id: run.run_id,
@@ -24783,14 +25971,14 @@ function createImageGenerationPort(options) {
24783
25971
  }
24784
25972
 
24785
25973
  // src/lib/szenen/imageJobStore.ts
24786
- var istText9 = (wert) => typeof wert === "string" && wert.trim().length > 0;
25974
+ var istText10 = (wert) => typeof wert === "string" && wert.trim().length > 0;
24787
25975
  var SETTLED_STATUS = ["succeeded", "partial", "failed", "rejected"];
24788
25976
  function collectImageJobs(events) {
24789
25977
  const nachId = /* @__PURE__ */ new Map();
24790
25978
  for (const event of events) {
24791
25979
  if (event.type !== SZENEN_EVENT_TYPES.imageJobRequested) continue;
24792
25980
  const payload = event.payload;
24793
- if (!payload || !istText9(payload.job_id) || !istText9(payload.prompt_run_id)) continue;
25981
+ if (!payload || !istText10(payload.job_id) || !istText10(payload.prompt_run_id)) continue;
24794
25982
  if (nachId.has(payload.job_id)) continue;
24795
25983
  nachId.set(payload.job_id, {
24796
25984
  job_id: payload.job_id,
@@ -24809,7 +25997,7 @@ function collectImageJobs(events) {
24809
25997
  for (const event of events) {
24810
25998
  if (event.type !== SZENEN_EVENT_TYPES.imageJobClaimed) continue;
24811
25999
  const payload = event.payload;
24812
- if (!payload || !istText9(payload.job_id) || !istText9(payload.claim_id)) continue;
26000
+ if (!payload || !istText10(payload.job_id) || !istText10(payload.claim_id)) continue;
24813
26001
  const auftrag = nachId.get(payload.job_id);
24814
26002
  if (!auftrag) continue;
24815
26003
  const anspruch = {
@@ -24828,7 +26016,7 @@ function collectImageJobs(events) {
24828
26016
  for (const event of events) {
24829
26017
  if (event.type !== SZENEN_EVENT_TYPES.imageJobSettled) continue;
24830
26018
  const payload = event.payload;
24831
- if (!payload || !istText9(payload.job_id)) continue;
26019
+ if (!payload || !istText10(payload.job_id)) continue;
24832
26020
  const auftrag = nachId.get(payload.job_id);
24833
26021
  if (!auftrag) continue;
24834
26022
  if (!auftrag.claim || auftrag.claim.claim_id !== payload.claim_id) continue;
@@ -24870,33 +26058,33 @@ function istOffen(job) {
24870
26058
  return job.status === "pending";
24871
26059
  }
24872
26060
  var PROMPT_AUSZUG_ZEICHEN = 160;
24873
- var istText10 = (wert) => typeof wert === "string" && wert.trim().length > 0;
26061
+ var istText11 = (wert) => typeof wert === "string" && wert.trim().length > 0;
24874
26062
  function titelJeElement(events) {
24875
26063
  const titel = /* @__PURE__ */ new Map();
24876
26064
  for (const event of events) {
24877
26065
  if (event.type === SZENEN_EVENT_TYPES.elementCreated) {
24878
26066
  const p = event.payload;
24879
- if (p && istText10(p.element_id) && istText10(p.title)) titel.set(p.element_id, p.title);
26067
+ if (p && istText11(p.element_id) && istText11(p.title)) titel.set(p.element_id, p.title);
24880
26068
  } else if (event.type === SZENEN_EVENT_TYPES.metadataRevised) {
24881
26069
  const p = event.payload;
24882
- if (p && istText10(p.element_id) && istText10(p.title)) titel.set(p.element_id, p.title);
26070
+ if (p && istText11(p.element_id) && istText11(p.title)) titel.set(p.element_id, p.title);
24883
26071
  }
24884
26072
  }
24885
26073
  return titel;
24886
26074
  }
24887
26075
  function benennendesElement(run) {
24888
26076
  const quelle = run.source;
24889
- if (quelle?.kind === "scene_direct" && istText10(quelle.scene?.element_id)) {
26077
+ if (quelle?.kind === "scene_direct" && istText11(quelle.scene?.element_id)) {
24890
26078
  return quelle.scene.element_id;
24891
26079
  }
24892
26080
  if (quelle?.kind === "draft_chain") {
24893
26081
  const knoten = quelle.nodes ?? [];
24894
- const szene = knoten.find((k) => k?.type === "scene" && istText10(k.element_id));
26082
+ const szene = knoten.find((k) => k?.type === "scene" && istText11(k.element_id));
24895
26083
  if (szene) return szene.element_id;
24896
- const shot = knoten.find((k) => k?.type === "shot" && istText10(k.element_id));
26084
+ const shot = knoten.find((k) => k?.type === "shot" && istText11(k.element_id));
24897
26085
  if (shot) return shot.element_id;
24898
26086
  }
24899
- return istText10(run.shot_element_id) ? run.shot_element_id : null;
26087
+ return istText11(run.shot_element_id) ? run.shot_element_id : null;
24900
26088
  }
24901
26089
  function kuerze(text2, zeichen) {
24902
26090
  const sauber = text2.replace(/\s+/g, " ").trim();
@@ -24921,7 +26109,7 @@ function herkunftFuer(job, laeufe, elementTitel, bilderJeLauf2) {
24921
26109
  run_id: job.prompt_run_id,
24922
26110
  szene_titel: elementId ? elementTitel.get(elementId) ?? null : null,
24923
26111
  szene_element_id: elementId,
24924
- prompt_auszug: istText10(positiv) ? kuerze(positiv, PROMPT_AUSZUG_ZEICHEN) : null,
26112
+ prompt_auszug: istText11(positiv) ? kuerze(positiv, PROMPT_AUSZUG_ZEICHEN) : null,
24925
26113
  lauf_gefunden: true,
24926
26114
  bilder_im_lauf
24927
26115
  };
@@ -24943,7 +26131,7 @@ function selectImageJobs(events, storeId, query = {}) {
24943
26131
  const getroffen = status === "all" ? alle : alle.filter((j) => GRUPPEN[status].includes(j.status));
24944
26132
  const geordnet = sortiere(getroffen, logIndex, status === "pending");
24945
26133
  let ab = 0;
24946
- if (istText10(query.cursor)) {
26134
+ if (istText11(query.cursor)) {
24947
26135
  const stelle = geordnet.findIndex((j) => j.job_id === query.cursor);
24948
26136
  if (stelle < 0) {
24949
26137
  throw new Error(
@@ -24972,8 +26160,8 @@ function bildRefAus(roh, batchId) {
24972
26160
  media_ref: roh.media_ref,
24973
26161
  position: roh.position,
24974
26162
  generation_batch_id: batchId,
24975
- ...istText10(roh.asset_id) ? { asset_id: roh.asset_id } : {},
24976
- ...istText10(roh.mime_type) ? { mime_type: roh.mime_type } : {},
26163
+ ...istText11(roh.asset_id) ? { asset_id: roh.asset_id } : {},
26164
+ ...istText11(roh.mime_type) ? { mime_type: roh.mime_type } : {},
24977
26165
  model: roh.model ?? null,
24978
26166
  seed: roh.seed ?? null,
24979
26167
  width: roh.width ?? null,
@@ -24988,10 +26176,10 @@ function bilderJeLauf(events, jobs) {
24988
26176
  for (const event of events) {
24989
26177
  if (event.type !== SZENEN_EVENT_TYPES.imageBatchPersisted) continue;
24990
26178
  const p = event.payload;
24991
- if (!p || !istText10(p.item_id) || !istText10(p.prompt_run_id) || !istText10(p.generation_batch_id)) continue;
26179
+ if (!p || !istText11(p.item_id) || !istText11(p.prompt_run_id) || !istText11(p.generation_batch_id)) continue;
24992
26180
  if (gesehen.has(p.generation_batch_id)) continue;
24993
26181
  gesehen.add(p.generation_batch_id);
24994
- const roh = (Array.isArray(p.images) ? p.images : []).filter((b) => !!b && istText10(b.media_ref) && typeof b.position === "number").sort((a, b) => a.position - b.position);
26182
+ const roh = (Array.isArray(p.images) ? p.images : []).filter((b) => !!b && istText11(b.media_ref) && typeof b.position === "number").sort((a, b) => a.position - b.position);
24995
26183
  if (roh.length === 0) continue;
24996
26184
  const job = jobJeBatch.get(p.generation_batch_id) ?? null;
24997
26185
  const liste = jeLauf.get(p.prompt_run_id) ?? [];
@@ -25004,7 +26192,7 @@ function bilderJeLauf(events, jobs) {
25004
26192
  job_id: job?.job_id ?? null,
25005
26193
  position_im_batch: einzeln.position + 1,
25006
26194
  anzahl_im_batch: roh.length,
25007
- gespeichert_am: istText10(p.created_at) ? p.created_at : null,
26195
+ gespeichert_am: istText11(p.created_at) ? p.created_at : null,
25008
26196
  execution_target_model_key: job?.execution_target_model_key ?? null
25009
26197
  });
25010
26198
  }
@@ -25494,6 +26682,9 @@ export {
25494
26682
  hfUploadImage,
25495
26683
  hfLoadImageAsBase64,
25496
26684
  applyEvents,
26685
+ STAGE_MIGRATION_ID,
26686
+ STAGE_MIGRATION_EVENT,
26687
+ prepareStageMigration,
25497
26688
  SZENEN_EVENT_PREFIX,
25498
26689
  SZENEN_EVENT_TYPES,
25499
26690
  reduceElementEvents,
@@ -25621,6 +26812,12 @@ export {
25621
26812
  completePromptOperation,
25622
26813
  runOfOperation,
25623
26814
  sameResponseAs,
26815
+ CAMERA_PREVIEW_ASSET_ROLE,
26816
+ checkVariantInputAssets,
26817
+ checkVariantBlock,
26818
+ buildPromptRunVariantRecord,
26819
+ variantOf,
26820
+ sameVariantRequest,
25624
26821
  DEFAULT_SEED_VORLAGE,
25625
26822
  parseSeedListe,
25626
26823
  formatSeedListe,
@@ -25630,6 +26827,13 @@ export {
25630
26827
  UPLOAD_MAX_BYTES,
25631
26828
  istZipDatei,
25632
26829
  leseBilderAusZip,
26830
+ NODE_TYPES,
26831
+ PROXY_PRIMITIVES,
26832
+ previewColorNodes,
26833
+ pinPreviewColors,
26834
+ applyStageOperations,
26835
+ findStageDraftChain,
26836
+ openStageDraftChain,
25633
26837
  simulatedAnswerFor,
25634
26838
  SIMULATION_PROVIDER,
25635
26839
  createSimulationTextGenerationPort,