@rslsp1/fa-app-tools 3.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -807,7 +807,7 @@ __export(index_exports, {
807
807
  module.exports = __toCommonJS(index_exports);
808
808
 
809
809
  // package.json
810
- var version = "3.0.0";
810
+ var version = "4.0.0";
811
811
 
812
812
  // src/hooks/useOnClickOutside.ts
813
813
  var import_react = require("react");
@@ -846,7 +846,7 @@ function useKeyboardNavigation(history, currentResult, setCurrentResult) {
846
846
  }
847
847
 
848
848
  // src/features/szenen/szenenViewStyles.generated.ts
849
- var SZENEN_VIEW_STYLES = '/* ==========================================================================\n SZENEN VIEW\n ========================================================================== */\n\n.szenen-view {\n display: grid;\n gap: var(--space-md);\n width: 100%;\n min-width: 0;\n font-family: var(--font-sans);\n padding: var(--space-md);\n}\n\n@media (max-width: 760px) {\n .szenen-view {\n padding: var(--space-sm);\n gap: var(--space-sm);\n }\n}\n\n.szenen-view__intro {\n display: flex;\n align-items: end;\n justify-content: space-between;\n gap: var(--space-md);\n padding: clamp(1rem, 3vw, 1.75rem);\n color: var(--color-text-primary);\n background:\n radial-gradient(circle at 85% 15%, rgba(45, 212, 191, 0.16), transparent 34%),\n linear-gradient(135deg, var(--color-bg-surface), var(--color-bg-elevated));\n border: 1px solid var(--color-border);\n border-radius: var(--radius-lg);\n}\n\n.szenen-view__intro h2,\n.szenen-view__intro p {\n margin: 0;\n}\n\n.szenen-view__intro h2 {\n margin-bottom: var(--space-xs);\n font-size: clamp(1.25rem, 4vw, 2rem);\n}\n\n.szenen-view__intro > div > p:last-child {\n max-width: 44rem;\n color: var(--color-text-secondary);\n}\n\n.szenen-view__eyebrow {\n margin-bottom: var(--space-xs) !important;\n color: var(--color-accent-teal, #2dd4bf);\n font-size: 0.72rem;\n font-weight: 800;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n}\n\n.szenen-view__step {\n flex: none;\n padding: 0.35rem 0.65rem;\n color: var(--color-text-primary);\n background: rgba(45, 212, 191, 0.12);\n border: 1px solid rgba(45, 212, 191, 0.35);\n border-radius: var(--radius-full);\n font-size: 0.78rem;\n font-weight: 700;\n}\n\n.szenen-view__nav {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-sm);\n padding: var(--space-sm);\n background: var(--color-bg-surface);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-md);\n}\n\n.szenen-view__nav button,\n.szenen-view button {\n min-height: 2.25rem;\n padding: 0.45rem 0.75rem;\n color: var(--color-text-secondary);\n background: var(--color-bg-elevated);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n cursor: pointer;\n}\n\n.szenen-view button:hover:not(:disabled) {\n color: var(--color-text-primary);\n background: var(--color-bg-surface-hover);\n border-color: var(--color-border-hover);\n}\n\n.szenen-view button:focus-visible,\n.szenen-view input:focus-visible,\n.szenen-view select:focus-visible,\n.szenen-view textarea:focus-visible {\n outline: 2px solid var(--color-accent-blue);\n outline-offset: 2px;\n}\n\n.szenen-view button:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.szenen-view__nav button.is-active,\n.szenen-view__nav button[aria-selected="true"] {\n color: var(--color-text-primary);\n background: var(--color-accent-blue-dim);\n border-color: var(--color-accent-blue);\n}\n\n.szenen-view__area {\n min-width: 0;\n padding: 0;\n background: transparent;\n border: none;\n border-radius: 0;\n}\n\n.szenen-view__capability-note,\n.szenen-area__lead {\n margin: 0;\n padding: var(--space-sm) var(--space-md);\n color: var(--color-text-secondary);\n background: var(--color-bg-surface);\n border-left: 3px solid var(--color-accent-purple);\n border-radius: var(--radius-sm);\n}\n\n.szenen-view__host-notes {\n color: var(--color-text-secondary);\n}\n\n.szenen-view__host-notes > summary {\n cursor: pointer;\n font-size: 0.8rem;\n}\n\n.szenen-view input:not([type="checkbox"]),\n.szenen-view select,\n.szenen-view textarea {\n width: 100%;\n min-height: 2.25rem;\n padding: 0.5rem 0.65rem;\n color: var(--color-text-primary);\n background: var(--color-bg-app);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n font: inherit;\n}\n\n.szenen-view textarea {\n min-height: 7rem;\n resize: vertical;\n font-family: var(--font-mono);\n font-size: 0.8rem;\n}\n\n.szenen-view label {\n display: grid;\n gap: var(--space-xs);\n color: var(--color-text-secondary);\n font-size: 0.85rem;\n}\n\n.szenen-view code,\n.szenen-view pre {\n font-family: var(--font-mono);\n overflow-wrap: anywhere;\n}\n\n.szenen-view h3,\n.szenen-view h4,\n.szenen-view h5 {\n margin-top: 0;\n}\n\n.szenen-view ul,\n.szenen-view ol {\n padding-left: 1.25rem;\n}\n\n.szenen-elements,\n.szenen-detail,\n.szenen-scene,\n.szenen-setup,\n.szenen-shot,\n.szenen-media,\n.szenen-runs {\n display: grid;\n gap: var(--space-md);\n min-width: 0;\n}\n\n.szenen-elements__filter,\n.szenen-elements__create,\n.szenen-detail__actions,\n.szenen-runs__start,\n.szenen-scene-editor__add,\n.szenen-scene-editor__add-relation {\n display: flex;\n flex-wrap: wrap;\n align-items: end;\n gap: var(--space-sm);\n}\n\n.szenen-elements__filter > label,\n.szenen-elements__create > input,\n.szenen-runs__start > label,\n.szenen-scene-editor__add > input {\n flex: 1 1 16rem;\n}\n\n.szenen-elements__filter > label:has(input[type="checkbox"]) {\n display: flex;\n flex: 1 1 16rem;\n flex-direction: row;\n align-items: center;\n}\n\n.szenen-elements__filter input[type="checkbox"] {\n width: 1rem;\n height: 1rem;\n accent-color: var(--color-accent-blue);\n}\n\n.szenen-elements__list {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));\n gap: var(--space-sm);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.szenen-elements__list > li > button {\n display: grid;\n grid-template-columns: auto 1fr;\n gap: var(--space-xs) var(--space-sm);\n width: 100%;\n height: 100%;\n text-align: left;\n}\n\n.szenen-elements__list > li > button.is-selected,\n.szenen-elements__list > li > button[aria-current="true"] {\n background: var(--color-accent-blue-dim);\n border-color: var(--color-accent-blue);\n}\n\n.szenen-elements__type {\n color: var(--color-accent-blue);\n font-size: 0.75rem;\n font-weight: 700;\n text-transform: uppercase;\n}\n\n.szenen-elements__title {\n color: var(--color-text-primary);\n font-size: 0.95rem;\n}\n\n.szenen-elements__technical {\n grid-column: 1 / -1;\n color: var(--color-text-tertiary);\n font-size: 0.72rem;\n}\n\n.szenen-elements__subtype,\n.szenen-elements__retired {\n color: var(--color-text-secondary);\n font-size: 0.75rem;\n}\n\n.szenen-elements__retired {\n color: var(--color-warning);\n}\n\n.szenen-elements__id {\n grid-column: 1 / -1;\n color: var(--color-text-tertiary);\n font-size: 0.72rem;\n}\n\n.szenen-detail {\n margin-top: 0;\n padding: 0;\n background: transparent;\n border: none;\n border-radius: 0;\n}\n\n@media (min-width: 761px) {\n .szenen-detail {\n padding: var(--space-md);\n background: var(--color-bg-surface);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n }\n}\n\n.szenen-detail > header,\n.szenen-detail__heads,\n.szenen-detail__draft,\n.szenen-capability {\n padding: 0;\n background: transparent;\n border: none;\n border-radius: 0;\n}\n\n/* .szenen-runs ist ein eigenst\xE4ndiger sichtbarer Bereich, beh\xE4lt Styling auf Desktop */\n@media (min-width: 761px) {\n .szenen-runs {\n padding: var(--space-md);\n background: var(--color-bg-surface);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-md);\n }\n}\n\n@media (max-width: 760px) {\n .szenen-runs {\n padding: 0;\n background: transparent;\n border: none;\n border-radius: 0;\n }\n}\n\n.szenen-detail__ids {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-sm);\n margin-bottom: 0;\n color: var(--color-text-secondary);\n}\n\n.szenen-detail__heads ul,\n.szenen-scene-editor ul,\n.szenen-runs > ol,\n.szenen-capability > ul {\n display: grid;\n gap: var(--space-sm);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.szenen-detail__heads li,\n.szenen-scene-editor li,\n.szenen-runs > ol > li,\n.szenen-capability > ul > li {\n padding: var(--space-sm);\n background: var(--color-bg-surface);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-detail__field {\n margin-bottom: var(--space-md);\n}\n\n/* Stufe 6.7.2 (D-126) \u2014 Advanced-/Debug-Fl\xE4che f\xFCr die rohe, schema-getriebene\n * Feldliste einer Szene. Gestrichelt wie die anderen \u201EAngebot statt\n * Auskunft"-Bereiche, aber ohne Akzentfarbe: sie ist kein Vorschlag, sondern\n * ein Eskalationsweg. */\n.szenen-detail__technical {\n margin-top: var(--space-md);\n padding: var(--space-sm) var(--space-md) var(--space-md);\n border: 1px dashed var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-detail__technical > summary {\n margin-bottom: var(--space-sm);\n color: var(--color-text-secondary);\n font-weight: 600;\n cursor: pointer;\n}\n\n.szenen-detail__technical[open] > summary {\n margin-bottom: var(--space-md);\n}\n\n.szenen-detail__field small,\n.szenen-capability small {\n color: var(--color-text-tertiary);\n}\n\n.szenen-detail__preview-errors,\n.szenen-detail__notice--error,\n.szenen-view [role="alert"] {\n color: #fecaca;\n background: var(--color-accent-red-dim);\n border-color: var(--color-accent-red);\n}\n\n.szenen-detail__notice {\n padding: var(--space-sm) var(--space-md);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-detail__notice--success,\n.szenen-view [data-tone="success"] {\n color: #bbf7d0;\n background: var(--color-accent-green-dim);\n border-color: var(--color-accent-green);\n}\n\n.szenen-scene-editor {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: var(--space-md);\n margin-bottom: 0;\n}\n\n.szenen-scene-editor > section {\n min-width: 0;\n padding: 0;\n background: transparent;\n border: none;\n border-radius: 0;\n}\n\n/* \u2500\u2500 Stufe 6.7.2 (D-126): Node-/Relationskarten \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n *\n * Nodes und Beziehungen sind `<details>` \u2014 dieselbe Interaktionssprache wie\n * \u201ERohantwort (Diagnose)" und \u201EBilder zu diesem Lauf" in RunsPanel.tsx. Kopf\n * (immer sichtbar) tr\xE4gt Inhalt zuerst, Technik zuletzt; Body (nur bei\n * ge\xF6ffneter Karte) tr\xE4gt die Bearbeitungscontrols. Padding wandert von der\n * `<li>` in Kopf/Body, damit die Karte keinen doppelten Rand bekommt. */\n.szenen-scene-editor li {\n padding: 0;\n overflow: hidden;\n}\n\n.szenen-node-karte__kopf,\n.szenen-relation-karte__kopf {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-xs) var(--space-sm);\n padding: var(--space-sm);\n cursor: pointer;\n list-style: none;\n}\n\n.szenen-node-karte__kopf::-webkit-details-marker,\n.szenen-relation-karte__kopf::-webkit-details-marker {\n display: none;\n}\n\n.szenen-node-karte__titel {\n flex: 1 1 12rem;\n min-width: 0;\n color: var(--color-text-primary);\n font-weight: 600;\n overflow-wrap: anywhere;\n}\n\n.szenen-node-karte__typ {\n flex: none;\n padding: 0.15rem 0.55rem;\n color: var(--color-text-secondary);\n background: var(--color-bg-app);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-full);\n font-size: 0.72rem;\n white-space: nowrap;\n}\n\n/* node_id klein, monospace, sekund\xE4r \u2014 sichtbar auch bei zugeklappter Karte,\n * aber optisch klar hinter Beschreibung und Typ. */\n.szenen-node-karte__meta {\n flex: 1 1 100%;\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem;\n color: var(--color-text-tertiary);\n font-size: 0.75rem;\n}\n\n.szenen-node-karte__meta code {\n font-size: inherit;\n}\n\n.szenen-node-karte__body,\n.szenen-relation-karte__body {\n display: grid;\n gap: var(--space-sm);\n padding: var(--space-sm) var(--space-sm) var(--space-sm);\n border-top: 1px solid var(--color-border);\n}\n\n/* \xA74 \u2014 Prim\xE4raktion und destruktive Aktion optisch getrennt: \u201ENode/Beziehung\n * entfernen" bekommt einen eigenen, warnenden Ton statt gleichgewichtig neben\n * der normalen Bearbeitung zu stehen. */\n.szenen-node-karte__entfernen,\n.szenen-relation-karte__entfernen {\n justify-self: start;\n color: #fecaca;\n background: var(--color-accent-red-dim);\n border-color: var(--color-accent-red);\n}\n\n.szenen-relation-karte__kopf {\n font-size: 0.9rem;\n}\n\n.szenen-relation-karte__kopf code {\n overflow-wrap: anywhere;\n}\n\n.szenen-relation-karte__pfeil {\n flex: none;\n color: var(--color-text-secondary);\n}\n\n/* Die Composer-\xD6ffner (\u201E+ Node hinzuf\xFCgen", \u201E+ Beziehung hinzuf\xFCgen") sind\n * Angebote, keine Ausk\xFCnfte \u2014 dasselbe gestrichelte Muster wie beim Folgelauf\n * und der Fehlerrettung (\xA73.3/\xA73.5). */\n.szenen-scene-editor__oeffnen {\n width: 100%;\n border-style: dashed;\n}\n\n.szenen-refpicker {\n display: grid;\n gap: var(--space-sm);\n min-width: 0;\n margin: var(--space-sm) 0;\n padding: var(--space-sm) 0;\n border: none;\n border-radius: 0;\n}\n\n.szenen-refpicker legend {\n padding: 0 var(--space-xs);\n color: var(--color-text-secondary);\n font-weight: 600;\n}\n\n.szenen-capability > ul > li {\n display: grid;\n grid-template-columns: minmax(10rem, 1fr) auto minmax(14rem, 2fr);\n align-items: start;\n gap: var(--space-sm);\n}\n\n.szenen-capability__state {\n padding: 0.1rem 0.5rem;\n border-radius: var(--radius-full);\n font-size: 0.75rem;\n font-weight: 700;\n text-transform: uppercase;\n}\n\n.szenen-capability__state--yes {\n color: #bbf7d0;\n background: var(--color-accent-green-dim);\n}\n\n.szenen-capability__state--no {\n color: #fecaca;\n background: var(--color-accent-red-dim);\n}\n\n.szenen-capability__state--unknown {\n color: #fef08a;\n background: rgba(234, 179, 8, 0.2);\n}\n\n.szenen-runs > ol > li {\n display: grid;\n gap: var(--space-sm);\n}\n\n.szenen-runs > ol > li > header {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-sm);\n color: var(--color-text-secondary);\n}\n\n.szenen-runs > ol > li[data-status="succeeded"] {\n border-left: 3px solid var(--color-success);\n}\n\n.szenen-runs > ol > li[data-status="failed"] {\n border-left: 3px solid var(--color-danger);\n}\n\n.szenen-runs details pre {\n max-height: 20rem;\n overflow: auto;\n padding: var(--space-sm);\n white-space: pre-wrap;\n color: var(--color-text-secondary);\n background: var(--color-bg-app);\n border-radius: var(--radius-sm);\n}\n\n.szenen-media > section {\n display: grid;\n gap: var(--space-sm);\n padding: 0;\n background: transparent;\n border: none;\n border-radius: 0;\n}\n\n.szenen-media__lookup,\n.szenen-media__attach {\n display: flex;\n flex-wrap: wrap;\n align-items: end;\n gap: var(--space-sm);\n}\n\n.szenen-media__lookup > label,\n.szenen-media__attach > label:first-child {\n flex: 1 1 18rem;\n}\n\n.szenen-media__list {\n display: grid;\n gap: var(--space-sm);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.szenen-media__list > li {\n display: grid;\n grid-template-columns: minmax(12rem, 1fr) auto auto auto;\n align-items: center;\n gap: var(--space-sm);\n padding: var(--space-sm);\n background: var(--color-bg-app);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n@media (max-width: 760px) {\n .szenen-view {\n gap: var(--space-sm);\n }\n\n .szenen-view__intro {\n align-items: start;\n flex-direction: column;\n gap: var(--space-sm);\n padding: var(--space-md);\n }\n\n .szenen-view__intro h2 {\n font-size: 1.25rem;\n }\n\n .szenen-view__intro > div > p:last-child {\n font-size: 0.9rem;\n }\n\n .szenen-view__nav {\n position: sticky;\n top: 0;\n z-index: 2;\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: var(--space-xs);\n padding: var(--space-xs);\n }\n\n .szenen-view__nav button {\n width: 100%;\n min-width: 0;\n min-height: 2.5rem;\n padding: 0.35rem 0.5rem;\n }\n\n\n .szenen-scene-editor {\n grid-template-columns: 1fr;\n }\n\n .szenen-capability > ul > li {\n grid-template-columns: 1fr;\n }\n\n .szenen-derive,\n .szenen-elements,\n .szenen-scene,\n .szenen-setup,\n .szenen-shot,\n .szenen-media,\n .szenen-runs {\n gap: var(--space-sm);\n }\n\n .szenen-elements__filter,\n .szenen-elements__create {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n align-items: center;\n gap: var(--space-sm);\n }\n\n .szenen-elements__filter > label,\n .szenen-elements__create > input {\n width: 100%;\n min-width: 0;\n flex: none;\n }\n\n .szenen-elements__filter > label:first-child,\n .szenen-elements__create > input {\n grid-column: 1 / -1;\n }\n\n .szenen-elements__filter > label:has(input[type="checkbox"]) {\n flex: none;\n }\n\n .szenen-detail__actions,\n .szenen-runs__start,\n .szenen-scene-editor__add,\n .szenen-scene-editor__add-relation {\n align-items: stretch;\n flex-direction: column;\n }\n\n .szenen-runs__start > *,\n .szenen-scene-editor__add > *,\n .szenen-scene-editor__add-relation > * {\n width: 100%;\n flex: none;\n }\n}\n\n/* \u2500\u2500 Stufe 6.5: Rekonstruktion \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n *\n * Der Bereich zeigt drei Dinge nebeneinander: den Vorschlag, das nicht\n * Ableitbare und die Widerspr\xFCche. Die Diagnosen sind deshalb bewusst nicht\n * kleiner gesetzt als der Vorschlag \u2014 wer sie \xFCbersieht, \xFCbernimmt Geratenes.\n */\n\n.szenen-derive {\n display: grid;\n gap: var(--space-md);\n}\n\n.szenen-form {\n display: grid;\n gap: var(--space-sm);\n padding: 0;\n background: transparent;\n border: none;\n border-radius: 0;\n}\n\n.szenen-field {\n display: grid;\n gap: var(--space-xs);\n}\n\n.szenen-field > span {\n color: var(--color-text-secondary);\n font-size: 0.875rem;\n}\n\n.szenen-error {\n padding: var(--space-sm) var(--space-md);\n color: var(--color-danger);\n background: var(--color-accent-red-dim);\n border: 1px solid var(--color-accent-red);\n border-radius: var(--radius-sm);\n}\n\n.szenen-empty {\n padding: var(--space-md);\n color: var(--color-text-secondary);\n background: var(--color-bg-surface);\n border: 1px dashed var(--color-border);\n border-radius: var(--radius-md);\n}\n\n.szenen-diagnose {\n padding: var(--space-md);\n background: var(--color-bg-surface);\n border: 1px solid var(--color-border);\n border-left: 3px solid var(--color-warning);\n border-radius: var(--radius-sm);\n}\n\n/* Ein Widerspruch ist der schwerste der drei Befunde: dort steht eine\n * Entscheidung aus, die das System bewusst nicht getroffen hat (D-74). */\n.szenen-diagnose--konflikt {\n border-left-color: var(--color-danger);\n}\n\n.szenen-diagnose > h4 {\n margin: 0 0 var(--space-sm);\n font-size: 0.9375rem;\n}\n\n.szenen-diagnose ul {\n display: grid;\n gap: var(--space-xs);\n margin: 0;\n padding-left: var(--space-md);\n}\n\n.szenen-proposal {\n padding: var(--space-md);\n background: var(--color-bg-surface);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-md);\n}\n\n.szenen-proposal > h4 {\n margin: 0 0 var(--space-sm);\n font-family: var(--font-mono);\n font-size: 0.9375rem;\n}\n\n.szenen-proposal__payload {\n max-height: 18rem;\n margin: 0 0 var(--space-sm);\n padding: var(--space-sm);\n overflow: auto;\n /* Ein erzeugter Prompt ist ein einziger langer Satz \u2014 ohne Umbruch\n verschwindet er nach rechts aus dem Bild. JSON-Payloads bringen ihre\n eigenen Zeilenumbrueche mit und leiden nicht darunter. */\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n background: var(--color-bg-app);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-proposal__done {\n margin: var(--space-sm) 0 0;\n color: var(--color-success);\n}\n\n/* Die Belege eines Laufs: Rohantwort und Systemprompt. Zum Lesen und Kopieren,\n nicht zum Bedienen \u2014 deshalb voll markierbar und in Monospace. */\n.szenen-beleg__text {\n width: 100%;\n margin: var(--space-sm) 0 0;\n padding: var(--space-sm);\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n line-height: 1.5;\n white-space: pre;\n background: var(--color-bg-app);\n color: var(--color-text);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n resize: vertical;\n}\n\n/* Der Folgelauf (D-87): abgesetzt, damit er nicht wie Teil des Ergebnisses\n aussieht \u2014 er ist eine Frage, keine Auskunft. */\n.szenen-folgelauf {\n margin-top: var(--space-md);\n padding: var(--space-sm) var(--space-md) var(--space-md);\n border: 1px dashed var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-folgelauf > h4 {\n margin: var(--space-sm) 0 0;\n}\n\n.szenen-run__herkunft {\n margin: 0 0 var(--space-sm);\n font-size: 0.875rem;\n color: var(--color-text-muted, inherit);\n}\n\n/* Widerspricht das Modell, ist das das Wichtigste auf der Seite. */\n.szenen-diagnose--antwort {\n border-left: 3px solid var(--color-accent, var(--color-border));\n}\n\n/* Der Bildw\xE4hler (D-92) \u2014 Kacheln wie in den Labs, aber mit dem CSS dieser\n Ansicht: eine gemeinsame Komponente \xFCber zwei Dom\xE4nengrenzen w\xE4re eine\n Kopplung, die keiner gewollt hat. */\n.szenen-bildwahl__liste {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr));\n gap: var(--space-sm);\n margin: var(--space-sm) 0 0;\n padding: 0;\n list-style: none;\n}\n\n.szenen-bildwahl__liste button {\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n width: 100%;\n padding: var(--space-sm);\n text-align: left;\n font-size: 0.8125rem;\n}\n\n.szenen-bildwahl__liste img {\n width: 100%;\n aspect-ratio: 1;\n object-fit: cover;\n border-radius: var(--radius-sm);\n}\n\n.szenen-bildwahl__gewaehlt {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-sm);\n margin: var(--space-sm) 0 0;\n padding: 0;\n list-style: none;\n}\n\n.szenen-bildwahl__gewaehlt li {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.25rem 0.5rem;\n font-size: 0.8125rem;\n background: var(--color-bg-app);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-beleg__kopf {\n display: flex;\n align-items: center;\n gap: var(--space-sm);\n margin-top: var(--space-sm);\n}\n\n.szenen-diagnose > details + details {\n margin-top: var(--space-sm);\n}\n\n.szenen-diagnose > details > summary {\n cursor: pointer;\n font-weight: 600;\n}\n\n/* \u2500\u2500 Die Fehlerrettung (Stufe 6.6, A5) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n *\n * Sie steht im Fehlerblock und muss trotzdem als Werkzeug lesbar sein, nicht\n * als weitere Fehlermeldung. Deshalb abgesetzt wie der Folgelauf \u2014 und aus\n * demselben Grund: Beides sind Angebote, keine Ausk\xFCnfte.\n *\n * Der wichtigste optische Punkt ist die Trennung von Original und\n * Arbeitskopie. Das Original bekommt den ruhigen, \u201Efesten" Hintergrund der\n * Belege; die Kopie sieht aus wie ein normales Eingabefeld, weil sie eines\n * ist. */\n.szenen-rettung {\n margin-top: var(--space-md);\n padding: var(--space-sm) var(--space-md) var(--space-md);\n border: 1px dashed var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-rettung > h4 {\n margin: var(--space-sm) 0 0;\n}\n\n.szenen-rettung textarea[readonly] {\n background: var(--color-bg-surface);\n color: var(--color-text-secondary);\n font-family: var(--font-mono, monospace);\n font-size: 0.8125rem;\n}\n\n.szenen-rettung textarea:not([readonly]) {\n font-family: var(--font-mono, monospace);\n font-size: 0.8125rem;\n}\n\n.szenen-rettung__aktionen {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-sm);\n margin: var(--space-sm) 0;\n}\n\n/* Ein Befund tr\xE4gt Code, Bezug und Meldung. Der Code steht klein und\n * monospaced davor: Er ist die Kennung, nicht die Aussage. */\n.szenen-diagnose__liste {\n display: grid;\n gap: var(--space-sm);\n margin: 0;\n padding-left: var(--space-md);\n}\n\n.szenen-diagnose__liste code {\n font-size: 0.8125rem;\n}\n\n.szenen-diagnose__liste > li[data-severity=\'warning\'] {\n color: var(--color-text-secondary);\n}\n\n/* \u2500\u2500 Die Draft-Kette (Stufe 6.6, C4) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n *\n * Der Hauptweg. Er steht nach den Einzel\xFCbernahmen und muss als das erkennbar\n * sein, was er ist: nicht eine weitere Option, sondern der k\xFCrzere Weg zum\n * selben Ziel. Deshalb durchgezogener Rahmen statt gestricheltem \u2014 er ist\n * Angebot **und** Empfehlung. */\n.szenen-kette {\n margin-top: var(--space-md);\n padding: var(--space-sm) var(--space-md) var(--space-md);\n background: var(--color-bg-surface);\n border: 1px solid var(--color-border);\n border-left: 3px solid var(--color-accent, var(--color-border));\n border-radius: var(--radius-sm);\n}\n\n.szenen-kette > h4 {\n margin: var(--space-sm) 0 0;\n}\n\n.szenen-kette ul {\n display: grid;\n gap: var(--space-xs);\n margin: 0 0 var(--space-sm);\n padding-left: var(--space-md);\n}\n\n.szenen-kette code {\n font-size: 0.8125rem;\n}\n\n/* Ein Hinweis, der eine Bedingung erklaert \u2014 nicht rot, weil er kein Fehler\n ist, aber abgesetzt, weil er eine Folge hat. */\n.szenen-detail__hint {\n margin: var(--space-sm) 0 0;\n padding: var(--space-sm);\n font-size: 0.875rem;\n color: var(--color-text-secondary);\n background: var(--color-bg-surface);\n border-left: 3px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n/* \u2500\u2500 Stufe 6.7: Bilder am Prompt-Lauf (H0/H1) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n *\n * Der Bereich tr\xE4gt zwei Dinge, die man nie verwechseln darf: den **Eingang**\n * (woraus der Prompt entstand) und den **Ausgang** (was daraus wurde). Sie\n * bekommen deshalb verschiedene Akzentkanten statt nur verschiedener\n * \xDCberschriften \u2014 wer schnell liest, sieht die Trennung, bevor er sie liest.\n */\n.szenen-bilder {\n display: grid;\n gap: var(--space-md);\n margin-top: var(--space-sm);\n}\n\n.szenen-bilder__eingang,\n.szenen-bilder__ausgang {\n display: grid;\n gap: var(--space-sm);\n padding: var(--space-sm) var(--space-md);\n background: var(--color-bg-surface);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-bilder__eingang {\n border-left: 3px solid var(--color-accent-purple, var(--color-border));\n}\n\n.szenen-bilder__ausgang {\n border-left: 3px solid var(--color-accent-teal, var(--color-border));\n}\n\n.szenen-bilder h5 {\n margin: 0;\n}\n\n.szenen-bilder__ids {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-sm);\n margin: 0;\n align-items: baseline;\n color: var(--color-text-secondary);\n font-size: 0.875rem;\n}\n\n.szenen-bilder__eingangsliste,\n.szenen-bilder__batchliste,\n.szenen-bilder__auftraege {\n display: grid;\n gap: var(--space-sm);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.szenen-bilder__eingangsliste > li,\n.szenen-bilder__batchliste > li,\n.szenen-bilder__auftraege > li {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-sm);\n padding: var(--space-sm);\n background: var(--color-bg-app);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n font-size: 0.8125rem;\n}\n\n/* Ein unterbrochener Auftrag ist der Fall, f\xFCr den der Auftragssatz existiert.\n Er wird deshalb markiert \u2014 nicht rot, denn er ist kein Fehler, sondern eine\n offene Entscheidung. */\n.szenen-bilder__auftraege > li[data-status="started"] {\n border-left: 3px solid var(--color-warning);\n}\n\n.szenen-bilder__auftraege > li[data-status="succeeded"] {\n border-left: 3px solid var(--color-success);\n}\n\n.szenen-bilder__auftraege > li[data-status="partial"] {\n border-left: 3px solid var(--color-accent-blue, var(--color-border));\n}\n\n.szenen-bilder__auftraege > li[data-status="failed"] {\n border-left: 3px solid var(--color-danger);\n}\n\n.szenen-bilder__eingangsliste img,\n.szenen-bilder__batchliste img {\n width: 4rem;\n height: 4rem;\n object-fit: cover;\n border-radius: var(--radius-sm);\n}\n\n.szenen-bilder__batch {\n display: grid;\n gap: var(--space-sm);\n padding: var(--space-sm);\n background: var(--color-bg-app);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n/* Die Aktionen stehen abgesetzt: Sie sind Angebote, keine Ausk\xFCnfte \u2014 dasselbe\n Muster wie beim Folgelauf und bei der Fehlerrettung. */\n.szenen-bilder__aktion {\n display: grid;\n gap: var(--space-sm);\n padding: var(--space-sm) var(--space-md) var(--space-md);\n border: 1px dashed var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-bilder__aktion > h5 {\n margin: var(--space-sm) 0 0;\n}\n\n.szenen-bilder__aktion ol {\n display: grid;\n gap: var(--space-sm);\n margin: 0;\n padding-left: var(--space-md);\n font-size: 0.8125rem;\n}\n\n/* ==========================================================================\n STUFE 6.10 \u2014 ELEMENTEDITOR UND VERSIONSNAVIGATION\n ========================================================================== */\n\n/* \xA74.2 \u2014 vier Sachverhalte, vier Marken. Die Marken stehen in einer eigenen\n * Spalte, damit ANGEZEIGT + BEVORZUGT nebeneinander lesbar bleiben, statt sich\n * durch den Text zu schieben. */\n.szenen-detail__heads li {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-sm);\n}\n\n.szenen-version__titel {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--space-xs);\n min-width: 0;\n margin-right: auto;\n}\n\n/* V17 \u2014 Kennungen bleiben vollst\xE4ndig sichtbar, aber optisch sekund\xE4r. */\n.szenen-version__id,\n.szenen-ref__id,\n.szenen-detail__field-key {\n color: var(--color-text-tertiary);\n font-size: 0.78rem;\n overflow-wrap: anywhere;\n}\n\n.szenen-version__marken {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-xs);\n}\n\n.szenen-version__marken > span {\n padding: 0.1rem 0.5rem;\n color: var(--color-text-primary);\n background: rgba(45, 212, 191, 0.12);\n border: 1px solid rgba(45, 212, 191, 0.35);\n border-radius: var(--radius-full);\n font-size: 0.7rem;\n font-weight: 800;\n letter-spacing: 0.08em;\n}\n\n.szenen-version--angezeigt {\n border-color: var(--color-accent-teal, #2dd4bf) !important;\n}\n\n/* V9 \u2014 der Basiswechsel ist eine Entscheidung, keine Nebenwirkung. */\n.szenen-detail__confirm {\n display: grid;\n gap: var(--space-sm);\n margin: var(--space-md) 0;\n padding: var(--space-md);\n background: var(--color-bg-surface);\n border: 1px solid var(--color-accent-amber, #f59e0b);\n border-radius: var(--radius-sm);\n}\n\n.szenen-detail__confirm p {\n margin: 0;\n}\n\n/* Untergruppen aufgeklappter Objektfelder (`prompt_render_settings`). */\n.szenen-detail__group {\n display: grid;\n gap: var(--space-sm);\n min-width: 0;\n margin: var(--space-md) 0;\n padding: var(--space-sm) var(--space-md) var(--space-md);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-detail__group > legend {\n padding: 0 var(--space-xs);\n color: var(--color-text-secondary);\n font-weight: 600;\n}\n\n.szenen-detail__field {\n display: grid;\n gap: var(--space-xs);\n min-width: 0;\n}\n\n.szenen-detail__field > span {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--space-xs);\n}\n\n/* Mehrfachauswahl und Listeneditoren. */\n.szenen-feld__mehrfach,\n.szenen-feld__liste,\n.szenen-feld__reflist,\n.szenen-feld__ref,\n.szenen-feld__zielmodell,\n.szenen-feld__kamera {\n display: grid;\n gap: var(--space-xs);\n min-width: 0;\n}\n\n.szenen-feld__mehrfach > label,\n.szenen-feld__null {\n display: flex;\n align-items: center;\n gap: var(--space-xs);\n color: var(--color-text-secondary);\n font-size: 0.85rem;\n}\n\n.szenen-feld__liste-zeile {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-xs);\n}\n\n.szenen-feld__reflist ol {\n display: grid;\n gap: var(--space-xs);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.szenen-feld__reflist li {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-xs);\n padding: var(--space-xs) var(--space-sm);\n background: var(--color-bg-surface);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n/* \xA76.1 \u2014 Titel und Typ zuerst, Kennungen daneben. */\n.szenen-ref__anzeige {\n display: inline-flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--space-xs);\n min-width: 0;\n}\n\n.szenen-ref__typ {\n color: var(--color-text-secondary);\n font-size: 0.8rem;\n}\n\n.szenen-ref__warn {\n padding: 0.1rem 0.5rem;\n color: #fecaca;\n background: var(--color-accent-red-dim);\n border-radius: var(--radius-full);\n font-size: 0.72rem;\n font-weight: 700;\n}\n\n/* \xA76.5 \u2014 abgeleitete Angaben, ausdr\xFCcklich nur Anzeige. */\n.szenen-detail__derived {\n margin-top: var(--space-md);\n padding: var(--space-sm) var(--space-md);\n background: var(--color-bg-surface);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-detail__derived dl {\n display: grid;\n grid-template-columns: minmax(10rem, auto) 1fr;\n gap: var(--space-xs) var(--space-sm);\n margin: 0;\n}\n\n.szenen-detail__derived dt {\n color: var(--color-text-secondary);\n}\n\n.szenen-detail__derived dd {\n margin: 0;\n}\n\n.szenen-feld__abgeleitet {\n margin: 0;\n color: var(--color-text-tertiary);\n font-size: 0.78rem;\n}\n';
849
+ var SZENEN_VIEW_STYLES = '/* ==========================================================================\n SZENEN VIEW\n ========================================================================== */\n\n.szenen-view {\n display: grid;\n gap: var(--space-md);\n width: 100%;\n min-width: 0;\n font-family: var(--font-sans);\n padding: var(--space-md);\n}\n\n@media (max-width: 760px) {\n .szenen-view {\n padding: var(--space-sm);\n gap: var(--space-sm);\n }\n}\n\n.szenen-view__intro {\n display: flex;\n align-items: end;\n justify-content: space-between;\n gap: var(--space-md);\n padding: clamp(1rem, 3vw, 1.75rem);\n color: var(--color-text-primary);\n background:\n radial-gradient(circle at 85% 15%, rgba(45, 212, 191, 0.16), transparent 34%),\n linear-gradient(135deg, var(--color-bg-surface), var(--color-bg-elevated));\n border: 1px solid var(--color-border);\n border-radius: var(--radius-lg);\n}\n\n.szenen-view__intro h2,\n.szenen-view__intro p {\n margin: 0;\n}\n\n.szenen-view__intro h2 {\n margin-bottom: var(--space-xs);\n font-size: clamp(1.25rem, 4vw, 2rem);\n}\n\n.szenen-view__intro > div > p:last-child {\n max-width: 44rem;\n color: var(--color-text-secondary);\n}\n\n.szenen-view__eyebrow {\n margin-bottom: var(--space-xs) !important;\n color: var(--color-accent-teal, #2dd4bf);\n font-size: 0.72rem;\n font-weight: 800;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n}\n\n.szenen-view__step {\n flex: none;\n padding: 0.35rem 0.65rem;\n color: var(--color-text-primary);\n background: rgba(45, 212, 191, 0.12);\n border: 1px solid rgba(45, 212, 191, 0.35);\n border-radius: var(--radius-full);\n font-size: 0.78rem;\n font-weight: 700;\n}\n\n.szenen-view__nav {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-sm);\n padding: var(--space-sm);\n background: var(--color-bg-surface);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-md);\n}\n\n.szenen-view__nav button,\n.szenen-view button {\n min-height: 2.25rem;\n padding: 0.45rem 0.75rem;\n color: var(--color-text-secondary);\n background: var(--color-bg-elevated);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n cursor: pointer;\n}\n\n.szenen-view button:hover:not(:disabled) {\n color: var(--color-text-primary);\n background: var(--color-bg-surface-hover);\n border-color: var(--color-border-hover);\n}\n\n.szenen-view button:focus-visible,\n.szenen-view input:focus-visible,\n.szenen-view select:focus-visible,\n.szenen-view textarea:focus-visible {\n outline: 2px solid var(--color-accent-blue);\n outline-offset: 2px;\n}\n\n.szenen-view button:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.szenen-view__nav button.is-active,\n.szenen-view__nav button[aria-selected="true"] {\n color: var(--color-text-primary);\n background: var(--color-accent-blue-dim);\n border-color: var(--color-accent-blue);\n}\n\n.szenen-view__area {\n min-width: 0;\n padding: 0;\n background: transparent;\n border: none;\n border-radius: 0;\n}\n\n.szenen-view__capability-note,\n.szenen-area__lead {\n margin: 0;\n padding: var(--space-sm) var(--space-md);\n color: var(--color-text-secondary);\n background: var(--color-bg-surface);\n border-left: 3px solid var(--color-accent-purple);\n border-radius: var(--radius-sm);\n}\n\n.szenen-view__host-notes {\n color: var(--color-text-secondary);\n}\n\n.szenen-view__host-notes > summary {\n cursor: pointer;\n font-size: 0.8rem;\n}\n\n.szenen-view input:not([type="checkbox"]),\n.szenen-view select,\n.szenen-view textarea {\n width: 100%;\n min-height: 2.25rem;\n padding: 0.5rem 0.65rem;\n color: var(--color-text-primary);\n background: var(--color-bg-app);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n font: inherit;\n}\n\n.szenen-view textarea {\n min-height: 7rem;\n resize: vertical;\n font-family: var(--font-mono);\n font-size: 0.8rem;\n}\n\n.szenen-view label {\n display: grid;\n gap: var(--space-xs);\n color: var(--color-text-secondary);\n font-size: 0.85rem;\n}\n\n.szenen-view code,\n.szenen-view pre {\n font-family: var(--font-mono);\n overflow-wrap: anywhere;\n}\n\n.szenen-view h3,\n.szenen-view h4,\n.szenen-view h5 {\n margin-top: 0;\n}\n\n.szenen-view ul,\n.szenen-view ol {\n padding-left: 1.25rem;\n}\n\n.szenen-elements,\n.szenen-detail,\n.szenen-scene,\n.szenen-setup,\n.szenen-shot,\n.szenen-media,\n.szenen-runs {\n display: grid;\n gap: var(--space-md);\n min-width: 0;\n}\n\n/* Arbeitsoberflaeche: Liste links, Bearbeitung rechts.\n *\n * Breit stehen beide Spalten nebeneinander \u2014 die Auswahl bleibt sichtbar,\n * waehrend bearbeitet wird. Schmal rendert die Komponente nur eine der beiden;\n * hier steht deshalb keine Regel, die die andere versteckt. */\n@media (min-width: 761px) {\n .szenen-elements {\n grid-template-columns: minmax(0, 22rem) minmax(0, 1fr);\n align-items: start;\n }\n\n .szenen-elements__master,\n .szenen-elements__detail {\n min-width: 0;\n }\n}\n\n.szenen-elements__master,\n.szenen-elements__detail {\n display: grid;\n gap: var(--space-sm);\n align-content: start;\n min-width: 0;\n}\n\n.szenen-elements__toolbar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: var(--space-sm);\n}\n\n.szenen-elements__heading {\n margin: 0;\n font-size: 1.05rem;\n}\n\n.szenen-elements__scope {\n display: flex;\n align-items: center;\n gap: var(--space-xs);\n min-width: 0;\n color: var(--color-text-secondary);\n font-size: 0.8rem;\n}\n\n.szenen-elements__scope > select {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.szenen-elements__new {\n flex: none;\n font-weight: 700;\n}\n\n.szenen-elements__create {\n display: grid;\n gap: var(--space-sm);\n padding: var(--space-sm);\n background: var(--color-bg-app);\n border: 1px solid var(--color-accent-blue);\n border-radius: var(--radius-sm);\n}\n\n.szenen-elements__create label {\n display: grid;\n gap: var(--space-xs);\n min-width: 0;\n}\n\n.szenen-elements__create-actions {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-sm);\n}\n\n.szenen-elements__options > summary {\n color: var(--color-text-tertiary);\n font-size: 0.78rem;\n cursor: pointer;\n}\n\n.szenen-elements__back {\n justify-self: start;\n font-weight: 700;\n}\n\n.szenen-elements__empty-detail {\n margin: 0;\n color: var(--color-text-tertiary);\n}\n\n.szenen-elements__filter,\n.szenen-detail__actions,\n.szenen-runs__start,\n.szenen-scene-editor__add,\n.szenen-scene-editor__add-relation {\n display: flex;\n flex-wrap: wrap;\n align-items: end;\n gap: var(--space-sm);\n}\n\n.szenen-elements__filter > label,\n.szenen-runs__start > label,\n.szenen-scene-editor__add > input {\n flex: 1 1 16rem;\n}\n\n.szenen-elements__filter > label:has(input[type="checkbox"]) {\n display: flex;\n flex: 1 1 16rem;\n flex-direction: row;\n align-items: center;\n}\n\n.szenen-elements__filter input[type="checkbox"] {\n width: 1rem;\n height: 1rem;\n accent-color: var(--color-accent-blue);\n}\n\n.szenen-elements__list {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));\n gap: var(--space-sm);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.szenen-elements__list > li > button {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--space-xs) var(--space-sm);\n width: 100%;\n height: 100%;\n text-align: left;\n}\n\n.szenen-elements__list > li > button > .szenen-elements__title {\n flex: 1 1 100%;\n}\n\n.szenen-elements__list > li > button.is-selected,\n.szenen-elements__list > li > button[aria-current="true"] {\n background: var(--color-accent-blue-dim);\n border-color: var(--color-accent-blue);\n}\n\n.szenen-elements__type {\n color: var(--color-accent-blue);\n font-size: 0.75rem;\n font-weight: 700;\n text-transform: uppercase;\n}\n\n.szenen-elements__title {\n color: var(--color-text-primary);\n font-size: 0.95rem;\n}\n\n.szenen-elements__subtype,\n.szenen-elements__retired {\n color: var(--color-text-secondary);\n font-size: 0.75rem;\n}\n\n.szenen-elements__retired {\n color: var(--color-warning);\n}\n\n.szenen-detail {\n margin-top: 0;\n padding: 0;\n background: transparent;\n border: none;\n border-radius: 0;\n}\n\n@media (min-width: 761px) {\n .szenen-detail {\n padding: var(--space-md);\n background: var(--color-bg-surface);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n }\n}\n\n.szenen-detail > header,\n.szenen-detail__heads,\n.szenen-detail__draft,\n.szenen-capability {\n padding: 0;\n background: transparent;\n border: none;\n border-radius: 0;\n}\n\n/* .szenen-runs ist ein eigenst\xE4ndiger sichtbarer Bereich, beh\xE4lt Styling auf Desktop */\n@media (min-width: 761px) {\n .szenen-runs {\n padding: var(--space-md);\n background: var(--color-bg-surface);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-md);\n }\n}\n\n@media (max-width: 760px) {\n .szenen-runs {\n padding: 0;\n background: transparent;\n border: none;\n border-radius: 0;\n }\n}\n\n.szenen-detail__ids {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-sm);\n margin-bottom: 0;\n color: var(--color-text-secondary);\n}\n\n.szenen-detail__heads ul,\n.szenen-scene-editor ul,\n.szenen-runs > ol,\n.szenen-capability > ul {\n display: grid;\n gap: var(--space-sm);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.szenen-detail__heads li,\n.szenen-scene-editor li,\n.szenen-runs > ol > li,\n.szenen-capability > ul > li {\n padding: var(--space-sm);\n background: var(--color-bg-surface);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-detail__field {\n margin-bottom: var(--space-md);\n}\n\n/* Stufe 6.7.2 (D-126) \u2014 Advanced-/Debug-Fl\xE4che f\xFCr die rohe, schema-getriebene\n * Feldliste einer Szene. Gestrichelt wie die anderen \u201EAngebot statt\n * Auskunft"-Bereiche, aber ohne Akzentfarbe: sie ist kein Vorschlag, sondern\n * ein Eskalationsweg. */\n.szenen-detail__technical {\n margin-top: var(--space-md);\n padding: var(--space-sm) var(--space-md) var(--space-md);\n border: 1px dashed var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-detail__technical > summary {\n margin-bottom: var(--space-sm);\n color: var(--color-text-secondary);\n font-weight: 600;\n cursor: pointer;\n}\n\n.szenen-detail__technical[open] > summary {\n margin-bottom: var(--space-md);\n}\n\n.szenen-detail__field small,\n.szenen-capability small {\n color: var(--color-text-tertiary);\n}\n\n.szenen-detail__preview-errors,\n.szenen-detail__notice--error,\n.szenen-view [role="alert"] {\n color: #fecaca;\n background: var(--color-accent-red-dim);\n border-color: var(--color-accent-red);\n}\n\n.szenen-detail__notice {\n padding: var(--space-sm) var(--space-md);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-detail__notice--success,\n.szenen-view [data-tone="success"] {\n color: #bbf7d0;\n background: var(--color-accent-green-dim);\n border-color: var(--color-accent-green);\n}\n\n.szenen-scene-editor {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: var(--space-md);\n margin-bottom: 0;\n}\n\n.szenen-scene-editor > section {\n min-width: 0;\n padding: 0;\n background: transparent;\n border: none;\n border-radius: 0;\n}\n\n/* \u2500\u2500 Stufe 6.7.2 (D-126): Node-/Relationskarten \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n *\n * Nodes und Beziehungen sind `<details>` \u2014 dieselbe Interaktionssprache wie\n * \u201ERohantwort (Diagnose)" und \u201EBilder zu diesem Lauf" in RunsPanel.tsx. Kopf\n * (immer sichtbar) tr\xE4gt Inhalt zuerst, Technik zuletzt; Body (nur bei\n * ge\xF6ffneter Karte) tr\xE4gt die Bearbeitungscontrols. Padding wandert von der\n * `<li>` in Kopf/Body, damit die Karte keinen doppelten Rand bekommt. */\n.szenen-scene-editor li {\n padding: 0;\n overflow: hidden;\n}\n\n.szenen-node-karte__kopf,\n.szenen-relation-karte__kopf {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-xs) var(--space-sm);\n padding: var(--space-sm);\n cursor: pointer;\n list-style: none;\n}\n\n.szenen-node-karte__kopf::-webkit-details-marker,\n.szenen-relation-karte__kopf::-webkit-details-marker {\n display: none;\n}\n\n.szenen-node-karte__titel {\n flex: 1 1 12rem;\n min-width: 0;\n color: var(--color-text-primary);\n font-weight: 600;\n overflow-wrap: anywhere;\n}\n\n.szenen-node-karte__typ {\n flex: none;\n padding: 0.15rem 0.55rem;\n color: var(--color-text-secondary);\n background: var(--color-bg-app);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-full);\n font-size: 0.72rem;\n white-space: nowrap;\n}\n\n/* node_id klein, monospace, sekund\xE4r \u2014 sichtbar auch bei zugeklappter Karte,\n * aber optisch klar hinter Beschreibung und Typ. */\n.szenen-node-karte__meta {\n flex: 1 1 100%;\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem;\n color: var(--color-text-tertiary);\n font-size: 0.75rem;\n}\n\n.szenen-node-karte__meta code {\n font-size: inherit;\n}\n\n.szenen-node-karte__body,\n.szenen-relation-karte__body {\n display: grid;\n gap: var(--space-sm);\n padding: var(--space-sm) var(--space-sm) var(--space-sm);\n border-top: 1px solid var(--color-border);\n}\n\n/* \xA74 \u2014 Prim\xE4raktion und destruktive Aktion optisch getrennt: \u201ENode/Beziehung\n * entfernen" bekommt einen eigenen, warnenden Ton statt gleichgewichtig neben\n * der normalen Bearbeitung zu stehen. */\n.szenen-node-karte__entfernen,\n.szenen-relation-karte__entfernen {\n justify-self: start;\n color: #fecaca;\n background: var(--color-accent-red-dim);\n border-color: var(--color-accent-red);\n}\n\n.szenen-relation-karte__kopf {\n font-size: 0.9rem;\n}\n\n.szenen-relation-karte__kopf code {\n overflow-wrap: anywhere;\n}\n\n.szenen-relation-karte__pfeil {\n flex: none;\n color: var(--color-text-secondary);\n}\n\n/* Die Composer-\xD6ffner (\u201E+ Node hinzuf\xFCgen", \u201E+ Beziehung hinzuf\xFCgen") sind\n * Angebote, keine Ausk\xFCnfte \u2014 dasselbe gestrichelte Muster wie beim Folgelauf\n * und der Fehlerrettung (\xA73.3/\xA73.5). */\n.szenen-scene-editor__oeffnen {\n width: 100%;\n border-style: dashed;\n}\n\n.szenen-refpicker {\n display: grid;\n gap: var(--space-sm);\n min-width: 0;\n margin: var(--space-sm) 0;\n padding: var(--space-sm) 0;\n border: none;\n border-radius: 0;\n}\n\n.szenen-refpicker legend {\n padding: 0 var(--space-xs);\n color: var(--color-text-secondary);\n font-weight: 600;\n}\n\n.szenen-capability > ul > li {\n display: grid;\n grid-template-columns: minmax(10rem, 1fr) auto minmax(14rem, 2fr);\n align-items: start;\n gap: var(--space-sm);\n}\n\n.szenen-capability__state {\n padding: 0.1rem 0.5rem;\n border-radius: var(--radius-full);\n font-size: 0.75rem;\n font-weight: 700;\n text-transform: uppercase;\n}\n\n.szenen-capability__state--yes {\n color: #bbf7d0;\n background: var(--color-accent-green-dim);\n}\n\n.szenen-capability__state--no {\n color: #fecaca;\n background: var(--color-accent-red-dim);\n}\n\n.szenen-capability__state--unknown {\n color: #fef08a;\n background: rgba(234, 179, 8, 0.2);\n}\n\n.szenen-runs > ol > li {\n display: grid;\n gap: var(--space-sm);\n}\n\n.szenen-runs > ol > li > header {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-sm);\n color: var(--color-text-secondary);\n}\n\n.szenen-runs > ol > li[data-status="succeeded"] {\n border-left: 3px solid var(--color-success);\n}\n\n.szenen-runs > ol > li[data-status="failed"] {\n border-left: 3px solid var(--color-danger);\n}\n\n.szenen-runs details pre {\n max-height: 20rem;\n overflow: auto;\n padding: var(--space-sm);\n white-space: pre-wrap;\n color: var(--color-text-secondary);\n background: var(--color-bg-app);\n border-radius: var(--radius-sm);\n}\n\n.szenen-media > section {\n display: grid;\n gap: var(--space-sm);\n padding: 0;\n background: transparent;\n border: none;\n border-radius: 0;\n}\n\n.szenen-media__lookup,\n.szenen-media__attach {\n display: flex;\n flex-wrap: wrap;\n align-items: end;\n gap: var(--space-sm);\n}\n\n.szenen-media__lookup > label,\n.szenen-media__attach > label:first-child {\n flex: 1 1 18rem;\n}\n\n.szenen-media__list {\n display: grid;\n gap: var(--space-sm);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.szenen-media__list > li {\n display: grid;\n grid-template-columns: minmax(12rem, 1fr) auto auto auto;\n align-items: center;\n gap: var(--space-sm);\n padding: var(--space-sm);\n background: var(--color-bg-app);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n@media (max-width: 760px) {\n .szenen-view {\n gap: var(--space-sm);\n }\n\n .szenen-view__intro {\n align-items: start;\n flex-direction: column;\n gap: var(--space-sm);\n padding: var(--space-md);\n }\n\n .szenen-view__intro h2 {\n font-size: 1.25rem;\n }\n\n .szenen-view__intro > div > p:last-child {\n font-size: 0.9rem;\n }\n\n .szenen-view__nav {\n position: sticky;\n top: 0;\n z-index: 2;\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: var(--space-xs);\n padding: var(--space-xs);\n }\n\n .szenen-view__nav button {\n width: 100%;\n min-width: 0;\n min-height: 2.5rem;\n padding: 0.35rem 0.5rem;\n }\n\n\n .szenen-scene-editor {\n grid-template-columns: 1fr;\n }\n\n .szenen-capability > ul > li {\n grid-template-columns: 1fr;\n }\n\n .szenen-derive,\n .szenen-elements,\n .szenen-scene,\n .szenen-setup,\n .szenen-shot,\n .szenen-media,\n .szenen-runs {\n gap: var(--space-sm);\n }\n\n .szenen-elements__filter {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n align-items: center;\n gap: var(--space-sm);\n }\n\n .szenen-elements__filter > label {\n width: 100%;\n min-width: 0;\n flex: none;\n }\n\n .szenen-elements__filter > label:has(input[type="checkbox"]) {\n flex: none;\n grid-column: 1 / -1;\n }\n\n /* Schmal ist die Liste eine Spalte, und die Auswahl fuehrt weiter \u2014 der\n * Eintrag ist deshalb ein Ziel fuer den Finger, keine Kachel. */\n .szenen-elements__list {\n grid-template-columns: minmax(0, 1fr);\n }\n\n .szenen-elements__list > li > button {\n min-height: 2.75rem;\n }\n\n .szenen-elements__back {\n justify-self: stretch;\n min-height: 2.5rem;\n }\n\n .szenen-detail__actions,\n .szenen-runs__start,\n .szenen-scene-editor__add,\n .szenen-scene-editor__add-relation {\n align-items: stretch;\n flex-direction: column;\n }\n\n .szenen-runs__start > *,\n .szenen-scene-editor__add > *,\n .szenen-scene-editor__add-relation > * {\n width: 100%;\n flex: none;\n }\n}\n\n/* \u2500\u2500 Stufe 6.5: Rekonstruktion \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n *\n * Der Bereich zeigt drei Dinge nebeneinander: den Vorschlag, das nicht\n * Ableitbare und die Widerspr\xFCche. Die Diagnosen sind deshalb bewusst nicht\n * kleiner gesetzt als der Vorschlag \u2014 wer sie \xFCbersieht, \xFCbernimmt Geratenes.\n */\n\n.szenen-derive {\n display: grid;\n gap: var(--space-md);\n}\n\n.szenen-form {\n display: grid;\n gap: var(--space-sm);\n padding: 0;\n background: transparent;\n border: none;\n border-radius: 0;\n}\n\n.szenen-field {\n display: grid;\n gap: var(--space-xs);\n}\n\n.szenen-field > span {\n color: var(--color-text-secondary);\n font-size: 0.875rem;\n}\n\n.szenen-error {\n padding: var(--space-sm) var(--space-md);\n color: var(--color-danger);\n background: var(--color-accent-red-dim);\n border: 1px solid var(--color-accent-red);\n border-radius: var(--radius-sm);\n}\n\n.szenen-empty {\n padding: var(--space-md);\n color: var(--color-text-secondary);\n background: var(--color-bg-surface);\n border: 1px dashed var(--color-border);\n border-radius: var(--radius-md);\n}\n\n.szenen-diagnose {\n padding: var(--space-md);\n background: var(--color-bg-surface);\n border: 1px solid var(--color-border);\n border-left: 3px solid var(--color-warning);\n border-radius: var(--radius-sm);\n}\n\n/* Ein Widerspruch ist der schwerste der drei Befunde: dort steht eine\n * Entscheidung aus, die das System bewusst nicht getroffen hat (D-74). */\n.szenen-diagnose--konflikt {\n border-left-color: var(--color-danger);\n}\n\n.szenen-diagnose > h4 {\n margin: 0 0 var(--space-sm);\n font-size: 0.9375rem;\n}\n\n.szenen-diagnose ul {\n display: grid;\n gap: var(--space-xs);\n margin: 0;\n padding-left: var(--space-md);\n}\n\n.szenen-proposal {\n padding: var(--space-md);\n background: var(--color-bg-surface);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-md);\n}\n\n.szenen-proposal > h4 {\n margin: 0 0 var(--space-sm);\n font-family: var(--font-mono);\n font-size: 0.9375rem;\n}\n\n.szenen-proposal__payload {\n max-height: 18rem;\n margin: 0 0 var(--space-sm);\n padding: var(--space-sm);\n overflow: auto;\n /* Ein erzeugter Prompt ist ein einziger langer Satz \u2014 ohne Umbruch\n verschwindet er nach rechts aus dem Bild. JSON-Payloads bringen ihre\n eigenen Zeilenumbrueche mit und leiden nicht darunter. */\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n background: var(--color-bg-app);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-proposal__done {\n margin: var(--space-sm) 0 0;\n color: var(--color-success);\n}\n\n/* Die Belege eines Laufs: Rohantwort und Systemprompt. Zum Lesen und Kopieren,\n nicht zum Bedienen \u2014 deshalb voll markierbar und in Monospace. */\n.szenen-beleg__text {\n width: 100%;\n margin: var(--space-sm) 0 0;\n padding: var(--space-sm);\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n line-height: 1.5;\n white-space: pre;\n background: var(--color-bg-app);\n color: var(--color-text);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n resize: vertical;\n}\n\n/* Der Folgelauf (D-87): abgesetzt, damit er nicht wie Teil des Ergebnisses\n aussieht \u2014 er ist eine Frage, keine Auskunft. */\n.szenen-folgelauf {\n margin-top: var(--space-md);\n padding: var(--space-sm) var(--space-md) var(--space-md);\n border: 1px dashed var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-folgelauf > h4 {\n margin: var(--space-sm) 0 0;\n}\n\n.szenen-run__herkunft {\n margin: 0 0 var(--space-sm);\n font-size: 0.875rem;\n color: var(--color-text-muted, inherit);\n}\n\n/* Widerspricht das Modell, ist das das Wichtigste auf der Seite. */\n.szenen-diagnose--antwort {\n border-left: 3px solid var(--color-accent, var(--color-border));\n}\n\n/* Der Bildw\xE4hler (D-92) \u2014 Kacheln wie in den Labs, aber mit dem CSS dieser\n Ansicht: eine gemeinsame Komponente \xFCber zwei Dom\xE4nengrenzen w\xE4re eine\n Kopplung, die keiner gewollt hat. */\n.szenen-bildwahl__liste {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr));\n gap: var(--space-sm);\n margin: var(--space-sm) 0 0;\n padding: 0;\n list-style: none;\n}\n\n.szenen-bildwahl__liste button {\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n width: 100%;\n padding: var(--space-sm);\n text-align: left;\n font-size: 0.8125rem;\n}\n\n.szenen-bildwahl__liste img {\n width: 100%;\n aspect-ratio: 1;\n object-fit: cover;\n border-radius: var(--radius-sm);\n}\n\n.szenen-bildwahl__gewaehlt {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-sm);\n margin: var(--space-sm) 0 0;\n padding: 0;\n list-style: none;\n}\n\n.szenen-bildwahl__gewaehlt li {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.25rem 0.5rem;\n font-size: 0.8125rem;\n background: var(--color-bg-app);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-beleg__kopf {\n display: flex;\n align-items: center;\n gap: var(--space-sm);\n margin-top: var(--space-sm);\n}\n\n.szenen-diagnose > details + details {\n margin-top: var(--space-sm);\n}\n\n.szenen-diagnose > details > summary {\n cursor: pointer;\n font-weight: 600;\n}\n\n/* \u2500\u2500 Die Fehlerrettung (Stufe 6.6, A5) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n *\n * Sie steht im Fehlerblock und muss trotzdem als Werkzeug lesbar sein, nicht\n * als weitere Fehlermeldung. Deshalb abgesetzt wie der Folgelauf \u2014 und aus\n * demselben Grund: Beides sind Angebote, keine Ausk\xFCnfte.\n *\n * Der wichtigste optische Punkt ist die Trennung von Original und\n * Arbeitskopie. Das Original bekommt den ruhigen, \u201Efesten" Hintergrund der\n * Belege; die Kopie sieht aus wie ein normales Eingabefeld, weil sie eines\n * ist. */\n.szenen-rettung {\n margin-top: var(--space-md);\n padding: var(--space-sm) var(--space-md) var(--space-md);\n border: 1px dashed var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-rettung > h4 {\n margin: var(--space-sm) 0 0;\n}\n\n.szenen-rettung textarea[readonly] {\n background: var(--color-bg-surface);\n color: var(--color-text-secondary);\n font-family: var(--font-mono, monospace);\n font-size: 0.8125rem;\n}\n\n.szenen-rettung textarea:not([readonly]) {\n font-family: var(--font-mono, monospace);\n font-size: 0.8125rem;\n}\n\n.szenen-rettung__aktionen {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-sm);\n margin: var(--space-sm) 0;\n}\n\n/* Ein Befund tr\xE4gt Code, Bezug und Meldung. Der Code steht klein und\n * monospaced davor: Er ist die Kennung, nicht die Aussage. */\n.szenen-diagnose__liste {\n display: grid;\n gap: var(--space-sm);\n margin: 0;\n padding-left: var(--space-md);\n}\n\n.szenen-diagnose__liste code {\n font-size: 0.8125rem;\n}\n\n.szenen-diagnose__liste > li[data-severity=\'warning\'] {\n color: var(--color-text-secondary);\n}\n\n/* \u2500\u2500 Die Draft-Kette (Stufe 6.6, C4) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n *\n * Der Hauptweg. Er steht nach den Einzel\xFCbernahmen und muss als das erkennbar\n * sein, was er ist: nicht eine weitere Option, sondern der k\xFCrzere Weg zum\n * selben Ziel. Deshalb durchgezogener Rahmen statt gestricheltem \u2014 er ist\n * Angebot **und** Empfehlung. */\n.szenen-kette {\n margin-top: var(--space-md);\n padding: var(--space-sm) var(--space-md) var(--space-md);\n background: var(--color-bg-surface);\n border: 1px solid var(--color-border);\n border-left: 3px solid var(--color-accent, var(--color-border));\n border-radius: var(--radius-sm);\n}\n\n.szenen-kette > h4 {\n margin: var(--space-sm) 0 0;\n}\n\n.szenen-kette ul {\n display: grid;\n gap: var(--space-xs);\n margin: 0 0 var(--space-sm);\n padding-left: var(--space-md);\n}\n\n.szenen-kette code {\n font-size: 0.8125rem;\n}\n\n/* Ein Hinweis, der eine Bedingung erklaert \u2014 nicht rot, weil er kein Fehler\n ist, aber abgesetzt, weil er eine Folge hat. */\n.szenen-detail__hint {\n margin: var(--space-sm) 0 0;\n padding: var(--space-sm);\n font-size: 0.875rem;\n color: var(--color-text-secondary);\n background: var(--color-bg-surface);\n border-left: 3px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n/* \u2500\u2500 Stufe 6.7: Bilder am Prompt-Lauf (H0/H1) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n *\n * Der Bereich tr\xE4gt zwei Dinge, die man nie verwechseln darf: den **Eingang**\n * (woraus der Prompt entstand) und den **Ausgang** (was daraus wurde). Sie\n * bekommen deshalb verschiedene Akzentkanten statt nur verschiedener\n * \xDCberschriften \u2014 wer schnell liest, sieht die Trennung, bevor er sie liest.\n */\n.szenen-bilder {\n display: grid;\n gap: var(--space-md);\n margin-top: var(--space-sm);\n}\n\n.szenen-bilder__eingang,\n.szenen-bilder__ausgang {\n display: grid;\n gap: var(--space-sm);\n padding: var(--space-sm) var(--space-md);\n background: var(--color-bg-surface);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-bilder__eingang {\n border-left: 3px solid var(--color-accent-purple, var(--color-border));\n}\n\n.szenen-bilder__ausgang {\n border-left: 3px solid var(--color-accent-teal, var(--color-border));\n}\n\n.szenen-bilder h5 {\n margin: 0;\n}\n\n.szenen-bilder__ids {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-sm);\n margin: 0;\n align-items: baseline;\n color: var(--color-text-secondary);\n font-size: 0.875rem;\n}\n\n.szenen-bilder__eingangsliste,\n.szenen-bilder__batchliste,\n.szenen-bilder__auftraege {\n display: grid;\n gap: var(--space-sm);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.szenen-bilder__eingangsliste > li,\n.szenen-bilder__batchliste > li,\n.szenen-bilder__auftraege > li {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-sm);\n padding: var(--space-sm);\n background: var(--color-bg-app);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n font-size: 0.8125rem;\n}\n\n/* Ein unterbrochener Auftrag ist der Fall, f\xFCr den der Auftragssatz existiert.\n Er wird deshalb markiert \u2014 nicht rot, denn er ist kein Fehler, sondern eine\n offene Entscheidung. */\n.szenen-bilder__auftraege > li[data-status="started"] {\n border-left: 3px solid var(--color-warning);\n}\n\n.szenen-bilder__auftraege > li[data-status="succeeded"] {\n border-left: 3px solid var(--color-success);\n}\n\n.szenen-bilder__auftraege > li[data-status="partial"] {\n border-left: 3px solid var(--color-accent-blue, var(--color-border));\n}\n\n.szenen-bilder__auftraege > li[data-status="failed"] {\n border-left: 3px solid var(--color-danger);\n}\n\n.szenen-bilder__eingangsliste img,\n.szenen-bilder__batchliste img {\n width: 4rem;\n height: 4rem;\n object-fit: cover;\n border-radius: var(--radius-sm);\n}\n\n.szenen-bilder__batch {\n display: grid;\n gap: var(--space-sm);\n padding: var(--space-sm);\n background: var(--color-bg-app);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n/* Die Aktionen stehen abgesetzt: Sie sind Angebote, keine Ausk\xFCnfte \u2014 dasselbe\n Muster wie beim Folgelauf und bei der Fehlerrettung. */\n.szenen-bilder__aktion {\n display: grid;\n gap: var(--space-sm);\n padding: var(--space-sm) var(--space-md) var(--space-md);\n border: 1px dashed var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-bilder__aktion > h5 {\n margin: var(--space-sm) 0 0;\n}\n\n.szenen-bilder__aktion ol {\n display: grid;\n gap: var(--space-sm);\n margin: 0;\n padding-left: var(--space-md);\n font-size: 0.8125rem;\n}\n\n/* ==========================================================================\n STUFE 6.10 \u2014 ELEMENTEDITOR UND VERSIONSNAVIGATION\n ========================================================================== */\n\n/* \xA74.2 \u2014 vier Sachverhalte, vier Marken. Die Marken stehen in einer eigenen\n * Spalte, damit ANGEZEIGT + BEVORZUGT nebeneinander lesbar bleiben, statt sich\n * durch den Text zu schieben. */\n.szenen-detail__heads li {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-sm);\n}\n\n.szenen-version__titel {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--space-xs);\n min-width: 0;\n margin-right: auto;\n}\n\n/* V17 \u2014 Kennungen bleiben vollst\xE4ndig sichtbar, aber optisch sekund\xE4r. */\n.szenen-version__id,\n.szenen-ref__id,\n.szenen-detail__field-key {\n color: var(--color-text-tertiary);\n font-size: 0.78rem;\n overflow-wrap: anywhere;\n}\n\n.szenen-version__marken {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-xs);\n}\n\n.szenen-version__marken > span {\n padding: 0.1rem 0.5rem;\n color: var(--color-text-primary);\n background: rgba(45, 212, 191, 0.12);\n border: 1px solid rgba(45, 212, 191, 0.35);\n border-radius: var(--radius-full);\n font-size: 0.7rem;\n font-weight: 800;\n letter-spacing: 0.08em;\n}\n\n.szenen-version--angezeigt {\n border-color: var(--color-accent-teal, #2dd4bf) !important;\n}\n\n/* V9 \u2014 der Basiswechsel ist eine Entscheidung, keine Nebenwirkung. */\n.szenen-detail__confirm {\n display: grid;\n gap: var(--space-sm);\n margin: var(--space-md) 0;\n padding: var(--space-md);\n background: var(--color-bg-surface);\n border: 1px solid var(--color-accent-amber, #f59e0b);\n border-radius: var(--radius-sm);\n}\n\n.szenen-detail__confirm p {\n margin: 0;\n}\n\n/* Untergruppen aufgeklappter Objektfelder (`prompt_render_settings`). */\n.szenen-detail__group {\n display: grid;\n gap: var(--space-sm);\n min-width: 0;\n margin: var(--space-md) 0;\n padding: var(--space-sm) var(--space-md) var(--space-md);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-detail__group > legend {\n padding: 0 var(--space-xs);\n color: var(--color-text-secondary);\n font-weight: 600;\n}\n\n.szenen-detail__field {\n display: grid;\n gap: var(--space-xs);\n min-width: 0;\n}\n\n.szenen-detail__field > span {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--space-xs);\n}\n\n/* Mehrfachauswahl und Listeneditoren. */\n.szenen-feld__mehrfach,\n.szenen-feld__liste,\n.szenen-feld__reflist,\n.szenen-feld__ref,\n.szenen-feld__zielmodell,\n.szenen-feld__kamera {\n display: grid;\n gap: var(--space-xs);\n min-width: 0;\n}\n\n.szenen-feld__mehrfach > label,\n.szenen-feld__null {\n display: flex;\n align-items: center;\n gap: var(--space-xs);\n color: var(--color-text-secondary);\n font-size: 0.85rem;\n}\n\n.szenen-feld__liste-zeile {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-xs);\n}\n\n.szenen-feld__reflist ol {\n display: grid;\n gap: var(--space-xs);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.szenen-feld__reflist li {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-xs);\n padding: var(--space-xs) var(--space-sm);\n background: var(--color-bg-surface);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n/* \xA76.1 \u2014 Titel und Typ zuerst, Kennungen daneben. */\n.szenen-ref__anzeige {\n display: inline-flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--space-xs);\n min-width: 0;\n}\n\n.szenen-ref__typ {\n color: var(--color-text-secondary);\n font-size: 0.8rem;\n}\n\n.szenen-ref__warn {\n padding: 0.1rem 0.5rem;\n color: #fecaca;\n background: var(--color-accent-red-dim);\n border-radius: var(--radius-full);\n font-size: 0.72rem;\n font-weight: 700;\n}\n\n/* \xA76.5 \u2014 abgeleitete Angaben, ausdr\xFCcklich nur Anzeige. */\n.szenen-detail__derived {\n margin-top: var(--space-md);\n padding: var(--space-sm) var(--space-md);\n background: var(--color-bg-surface);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-detail__derived dl {\n display: grid;\n grid-template-columns: minmax(10rem, auto) 1fr;\n gap: var(--space-xs) var(--space-sm);\n margin: 0;\n}\n\n.szenen-detail__derived dt {\n color: var(--color-text-secondary);\n}\n\n.szenen-detail__derived dd {\n margin: 0;\n}\n\n.szenen-feld__abgeleitet {\n margin: 0;\n color: var(--color-text-tertiary);\n font-size: 0.78rem;\n}\n';
850
850
 
851
851
  // src/lib/utils.ts
852
852
  var getFormattedTimestamp = () => {
@@ -2225,7 +2225,7 @@ function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMove
2225
2225
  }
2226
2226
 
2227
2227
  // src/components/AvatarArchitectApp.tsx
2228
- var import_react48 = require("react");
2228
+ var import_react49 = require("react");
2229
2229
 
2230
2230
  // src/components/PromptTab.tsx
2231
2231
  var import_react15 = require("react");
@@ -6532,7 +6532,7 @@ function FlowWorkspacePicker({
6532
6532
  }
6533
6533
 
6534
6534
  // src/features/szenen/SzenenView.tsx
6535
- var import_react47 = require("react");
6535
+ var import_react48 = require("react");
6536
6536
 
6537
6537
  // src/lib/szenen/viewPort.ts
6538
6538
  function capabilitiesOf(port) {
@@ -6556,93 +6556,6 @@ async function callOptional(capability, operation) {
6556
6556
  return operation();
6557
6557
  }
6558
6558
 
6559
- // src/features/szenen/szenenViewModel.ts
6560
- var ALLE_BEREICHE = [
6561
- {
6562
- id: "derive",
6563
- title: "0 \xB7 Rekonstruktion",
6564
- description: "Aus einem Bild oder Prompt einen Szenenentwurf vorschlagen lassen."
6565
- },
6566
- {
6567
- id: "elements",
6568
- title: "1 \xB7 Bausteine",
6569
- description: "Figuren, Orte, Stile und Regeln f\xFCr sp\xE4tere Bildprompts pflegen."
6570
- },
6571
- {
6572
- id: "scene",
6573
- title: "2 \xB7 Szene",
6574
- description: "Aus Bausteinen eine konkrete Szene und ihre Beziehungen bauen."
6575
- },
6576
- {
6577
- id: "setup",
6578
- title: "3 \xB7 Bildaufbau",
6579
- description: "Stil, Komposition und Kamera f\xFCr das sp\xE4tere Bild festlegen."
6580
- },
6581
- {
6582
- id: "shot",
6583
- title: "4 \xB7 Prompt",
6584
- description: "Bildprompt erzeugen, Varianten vergleichen und als Item speichern."
6585
- },
6586
- {
6587
- id: "media",
6588
- title: "Referenzen",
6589
- description: "Referenzmedien einer Elementversion ansehen und zuordnen."
6590
- }
6591
- ];
6592
- function areasFor(port) {
6593
- const faehig = capabilitiesOf(port);
6594
- return ALLE_BEREICHE.filter((bereich) => {
6595
- if (bereich.id === "media") return faehig.media;
6596
- if (bereich.id === "derive") return faehig.derivation;
6597
- return true;
6598
- });
6599
- }
6600
- function resolveArea(port, gewuenscht) {
6601
- const verfuegbar = areasFor(port);
6602
- if (gewuenscht && verfuegbar.some((b) => b.id === gewuenscht)) return gewuenscht;
6603
- return verfuegbar.find((b) => b.id === "elements")?.id ?? verfuegbar[0].id;
6604
- }
6605
- function noticeFor(ergebnis, was) {
6606
- switch (ergebnis.kind) {
6607
- case "ok":
6608
- return { tone: "ok", text: `${was}: erledigt.` };
6609
- case "conflict": {
6610
- const aktuell = ergebnis.actualRevision;
6611
- return {
6612
- tone: "conflict",
6613
- text: `${was}: inzwischen von anderer Seite ge\xE4ndert` + (aktuell !== void 0 ? ` (aktueller Stand: Revision ${String(aktuell)})` : "") + ". Bitte neu laden und erneut versuchen."
6614
- };
6615
- }
6616
- case "rejected": {
6617
- const fehler4 = ergebnis.errors ?? [];
6618
- const ungeprueft = ergebnis.unchecked ?? [];
6619
- return {
6620
- tone: "rejected",
6621
- text: `${was}: abgelehnt \u2014 ${fehler4.length} Befund${fehler4.length === 1 ? "" : "e"}.`,
6622
- details: [
6623
- ...fehler4.map((f) => `${f.path ?? "(ohne Pfad)"}: ${f.message ?? "ohne Meldung"}`),
6624
- // Nicht verschweigen: was nicht geprüft wurde, ist nicht in Ordnung,
6625
- // sondern ungeprüft.
6626
- ...ungeprueft.map((u) => `ungepr\xFCft: ${u}`)
6627
- ]
6628
- };
6629
- }
6630
- case "unsupported":
6631
- return {
6632
- tone: "unsupported",
6633
- text: `${was}: von diesem Host nicht angeboten (${String(ergebnis.capability ?? was)}). Das ist kein Fehler und kein vor\xFCbergehender Zustand.`
6634
- };
6635
- default:
6636
- return {
6637
- tone: "error",
6638
- text: `${was}: ${String(ergebnis.message ?? "unbekannter Fehler")}`
6639
- };
6640
- }
6641
- }
6642
-
6643
- // src/features/szenen/areas/ElementsArea.tsx
6644
- var import_react42 = require("react");
6645
-
6646
6559
  // src/lib/szenen/jsonSchema.ts
6647
6560
  var SUPPORTED_KEYWORDS = /* @__PURE__ */ new Set([
6648
6561
  "$ref",
@@ -9716,6 +9629,99 @@ function formatSchemaErrors(errors) {
9716
9629
  return errors.map((e) => `${e.instancePath || "/"} ${e.message}`).join("; ");
9717
9630
  }
9718
9631
 
9632
+ // src/features/szenen/szenenViewModel.ts
9633
+ var KEINE_BAUSTEINE = [
9634
+ "scene",
9635
+ "scene_setup",
9636
+ "shot"
9637
+ ];
9638
+ var BAUSTEIN_TYPES = SZENEN_ELEMENT_TYPES.filter((typ) => !KEINE_BAUSTEINE.includes(typ));
9639
+ var ALLE_BEREICHE = [
9640
+ {
9641
+ id: "derive",
9642
+ title: "0 \xB7 Rekonstruktion",
9643
+ description: "Aus einem Bild oder Prompt einen Szenenentwurf vorschlagen lassen."
9644
+ },
9645
+ {
9646
+ id: "elements",
9647
+ title: "1 \xB7 Bausteine",
9648
+ description: "Figuren, Orte, Stile und Regeln f\xFCr sp\xE4tere Bildprompts pflegen."
9649
+ },
9650
+ {
9651
+ id: "scene",
9652
+ title: "2 \xB7 Szene",
9653
+ description: "Aus Bausteinen eine konkrete Szene und ihre Beziehungen bauen."
9654
+ },
9655
+ {
9656
+ id: "setup",
9657
+ title: "3 \xB7 Bildaufbau",
9658
+ description: "Stil, Komposition und Kamera f\xFCr das sp\xE4tere Bild festlegen."
9659
+ },
9660
+ {
9661
+ id: "shot",
9662
+ title: "4 \xB7 Prompt",
9663
+ description: "Bildprompt erzeugen, Varianten vergleichen und als Item speichern."
9664
+ },
9665
+ {
9666
+ id: "media",
9667
+ title: "Referenzen",
9668
+ description: "Referenzmedien einer Elementversion ansehen und zuordnen."
9669
+ }
9670
+ ];
9671
+ function areasFor(port) {
9672
+ const faehig = capabilitiesOf(port);
9673
+ return ALLE_BEREICHE.filter((bereich) => {
9674
+ if (bereich.id === "media") return faehig.media;
9675
+ if (bereich.id === "derive") return faehig.derivation;
9676
+ return true;
9677
+ });
9678
+ }
9679
+ function resolveArea(port, gewuenscht) {
9680
+ const verfuegbar = areasFor(port);
9681
+ if (gewuenscht && verfuegbar.some((b) => b.id === gewuenscht)) return gewuenscht;
9682
+ return verfuegbar.find((b) => b.id === "elements")?.id ?? verfuegbar[0].id;
9683
+ }
9684
+ function noticeFor(ergebnis, was) {
9685
+ switch (ergebnis.kind) {
9686
+ case "ok":
9687
+ return { tone: "ok", text: `${was}: erledigt.` };
9688
+ case "conflict": {
9689
+ const aktuell = ergebnis.actualRevision;
9690
+ return {
9691
+ tone: "conflict",
9692
+ text: `${was}: inzwischen von anderer Seite ge\xE4ndert` + (aktuell !== void 0 ? ` (aktueller Stand: Revision ${String(aktuell)})` : "") + ". Bitte neu laden und erneut versuchen."
9693
+ };
9694
+ }
9695
+ case "rejected": {
9696
+ const fehler4 = ergebnis.errors ?? [];
9697
+ const ungeprueft = ergebnis.unchecked ?? [];
9698
+ return {
9699
+ tone: "rejected",
9700
+ text: `${was}: abgelehnt \u2014 ${fehler4.length} Befund${fehler4.length === 1 ? "" : "e"}.`,
9701
+ details: [
9702
+ ...fehler4.map((f) => `${f.path ?? "(ohne Pfad)"}: ${f.message ?? "ohne Meldung"}`),
9703
+ // Nicht verschweigen: was nicht geprüft wurde, ist nicht in Ordnung,
9704
+ // sondern ungeprüft.
9705
+ ...ungeprueft.map((u) => `ungepr\xFCft: ${u}`)
9706
+ ]
9707
+ };
9708
+ }
9709
+ case "unsupported":
9710
+ return {
9711
+ tone: "unsupported",
9712
+ text: `${was}: von diesem Host nicht angeboten (${String(ergebnis.capability ?? was)}). Das ist kein Fehler und kein vor\xFCbergehender Zustand.`
9713
+ };
9714
+ default:
9715
+ return {
9716
+ tone: "error",
9717
+ text: `${was}: ${String(ergebnis.message ?? "unbekannter Fehler")}`
9718
+ };
9719
+ }
9720
+ }
9721
+
9722
+ // src/features/szenen/areas/ElementsArea.tsx
9723
+ var import_react43 = require("react");
9724
+
9719
9725
  // src/features/szenen/areas/usePortQuery.ts
9720
9726
  var import_react33 = require("react");
9721
9727
  function usePortQuery(frage, abhaengigkeiten) {
@@ -9748,6 +9754,32 @@ function usePortQuery(frage, abhaengigkeiten) {
9748
9754
  // src/features/szenen/areas/ElementDetail.tsx
9749
9755
  var import_react41 = require("react");
9750
9756
 
9757
+ // src/features/szenen/elementLabels.ts
9758
+ var LABELS = {
9759
+ scene_component: { singular: "Szenenbaustein", plural: "Szenenbausteine" },
9760
+ scene: { singular: "Szene", plural: "Szenen" },
9761
+ shot_concept: { singular: "Prompt-Idee", plural: "Prompt-Ideen" },
9762
+ camera_preset: { singular: "Kamera", plural: "Kameras" },
9763
+ camera_pattern: { singular: "Kamera-Muster", plural: "Kamera-Muster" },
9764
+ style: { singular: "Stil", plural: "Stile" },
9765
+ scene_setup: { singular: "Bildaufbau", plural: "Bildaufbauten" },
9766
+ shot: { singular: "Prompt", plural: "Prompts" },
9767
+ prompt_policy: { singular: "Prompt-Regel", plural: "Prompt-Regeln" },
9768
+ prompt_fragment: { singular: "Prompt-Baustein", plural: "Prompt-Bausteine" },
9769
+ target_model: { singular: "Zielmodell", plural: "Zielmodelle" },
9770
+ sequence: { singular: "Sequenz", plural: "Sequenzen" },
9771
+ scene_derivation_policy: {
9772
+ singular: "Rekonstruktions-Regel",
9773
+ plural: "Rekonstruktions-Regeln"
9774
+ }
9775
+ };
9776
+ function elementTypLabel(typ) {
9777
+ return LABELS[typ]?.singular ?? typ;
9778
+ }
9779
+ function elementTypPlural(typ) {
9780
+ return LABELS[typ]?.plural ?? typ;
9781
+ }
9782
+
9751
9783
  // src/features/szenen/schemaForm.ts
9752
9784
  var FORM_JE_REF = {
9753
9785
  "common.schema.json#/$defs/slug": "slug",
@@ -10351,7 +10383,7 @@ var SEMANTIK = {
10351
10383
  function feldSemantik(type, pfad) {
10352
10384
  return SEMANTIK[`${type}.${pfad}`] ?? null;
10353
10385
  }
10354
- var LABELS = {
10386
+ var LABELS2 = {
10355
10387
  key: "Kennung",
10356
10388
  name: "Name",
10357
10389
  text: "Text",
@@ -10396,7 +10428,7 @@ var LABELS = {
10396
10428
  properties: "Zusatzangaben"
10397
10429
  };
10398
10430
  function feldLabel(type, pfad, name) {
10399
- return feldSemantik(type, pfad)?.label ?? LABELS[name] ?? name;
10431
+ return feldSemantik(type, pfad)?.label ?? LABELS2[name] ?? name;
10400
10432
  }
10401
10433
 
10402
10434
  // src/lib/szenen/imageGenerationCapability.ts
@@ -10424,6 +10456,22 @@ function checkImageExecutionProfile(profile) {
10424
10456
  }
10425
10457
  return befunde;
10426
10458
  }
10459
+ function checkImageExecutionProfiles(profiles) {
10460
+ const anzahlJeKennung = /* @__PURE__ */ new Map();
10461
+ for (const profil of profiles ?? []) {
10462
+ if (!isObject3(profil) || !istText(profil.profile_id)) continue;
10463
+ anzahlJeKennung.set(profil.profile_id, (anzahlJeKennung.get(profil.profile_id) ?? 0) + 1);
10464
+ }
10465
+ return (profiles ?? []).map((profil) => {
10466
+ const befunde = [...checkImageExecutionProfile(profil)];
10467
+ if (isObject3(profil) && istText(profil.profile_id) && (anzahlJeKennung.get(profil.profile_id) ?? 0) > 1) {
10468
+ befunde.push(
10469
+ `Die profile_id "${profil.profile_id}" kommt in diesem Zielmodell mehrfach vor \u2014 die Auswahl waere nicht mehr eindeutig aufloesbar.`
10470
+ );
10471
+ }
10472
+ return befunde;
10473
+ });
10474
+ }
10427
10475
  function executableImageModels(promptTargetModelKey, targets, capability) {
10428
10476
  const models = [];
10429
10477
  const invalid = [];
@@ -10437,8 +10485,9 @@ function executableImageModels(promptTargetModelKey, targets, capability) {
10437
10485
  };
10438
10486
  }
10439
10487
  for (const target of targets ?? []) {
10440
- for (const roh of target.executionProfiles ?? []) {
10441
- const befunde = checkImageExecutionProfile(roh);
10488
+ const profilBefunde = checkImageExecutionProfiles(target.executionProfiles ?? []);
10489
+ for (const [index, roh] of (target.executionProfiles ?? []).entries()) {
10490
+ const befunde = profilBefunde[index] ?? [];
10442
10491
  if (befunde.length > 0) {
10443
10492
  const kennung = isObject3(roh) && istText(roh.profile_id) ? roh.profile_id : "(ohne profile_id)";
10444
10493
  invalid.push(`${target.key}/${kennung}: ${befunde.join(" ")}`);
@@ -10447,13 +10496,20 @@ function executableImageModels(promptTargetModelKey, targets, capability) {
10447
10496
  const profil = roh;
10448
10497
  if (!profil.enabled) continue;
10449
10498
  if (!profil.environments.includes(capability.environment_id)) continue;
10450
- if (!capability.execution_paths.includes(profil.execution_path)) continue;
10499
+ const weg = capability.execution_paths.find((r) => r.execution_path === profil.execution_path);
10500
+ if (!weg) continue;
10501
+ if (target.provider !== weg.provider) {
10502
+ invalid.push(
10503
+ `${target.key}/${profil.profile_id}: Das Zielmodell nennt den Anbieter ${target.provider === null ? "(keinen)" : `"${target.provider}"`}, der Aufrufweg "${weg.execution_path}" fuehrt aber zu "${weg.provider}".`
10504
+ );
10505
+ continue;
10506
+ }
10451
10507
  models.push({
10452
10508
  selection_id: `${target.key}#${profil.profile_id}`,
10453
10509
  target_model_key: target.key,
10454
10510
  ...target.versionId ? { target_model_version_id: target.versionId } : {},
10455
10511
  display_name: target.title,
10456
- provider: target.provider ?? "(ohne provider)",
10512
+ provider: weg.provider,
10457
10513
  profile_id: profil.profile_id,
10458
10514
  environment_id: capability.environment_id,
10459
10515
  execution_path: profil.execution_path,
@@ -10472,7 +10528,7 @@ function executableImageModels(promptTargetModelKey, targets, capability) {
10472
10528
  models: [],
10473
10529
  default_selection_id: null,
10474
10530
  invalid_profiles: invalid,
10475
- reason: `In dieser Umgebung (${capability.environment_id}) traegt kein sichtbares Zielmodell ein aktives Ausfuehrungsprofil fuer einen Aufrufweg dieses Hosts (${capability.execution_paths.join(", ") || "keiner"}).`
10531
+ reason: `In dieser Umgebung (${capability.environment_id}) traegt kein sichtbares Zielmodell ein aktives Ausfuehrungsprofil fuer einen Aufrufweg dieses Hosts (${capability.execution_paths.map((r) => r.execution_path).join(", ") || "keiner"}).`
10476
10532
  };
10477
10533
  }
10478
10534
  const passend = models.find((m) => m.is_prompt_target);
@@ -11214,6 +11270,7 @@ var AusfuehrungsprofileFeld = ({ feld, wert, onChange, readOnly }) => {
11214
11270
  const profile = (gelesen ?? []).filter(isObject4);
11215
11271
  const unlesbar = gelesen === null;
11216
11272
  const schreibe = (neu) => onChange(feld.path, schreibListe(neu));
11273
+ const alleBefunde = checkImageExecutionProfiles(profile);
11217
11274
  const aendere = (index, patch) => schreibe(profile.map((p, i) => i === index ? { ...p, ...patch } : p));
11218
11275
  if (unlesbar) {
11219
11276
  return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { role: "alert", "data-testid": `feld-${feld.path}-unlesbar`, children: "Der gespeicherte Wert ist keine Profilliste. Er bleibt unver\xE4ndert stehen; bitte an der Quelle korrigieren." });
@@ -11221,7 +11278,7 @@ var AusfuehrungsprofileFeld = ({ feld, wert, onChange, readOnly }) => {
11221
11278
  return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "szenen-feld__profile", "data-testid": `feld-${feld.path}`, children: [
11222
11279
  profile.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { "data-testid": `feld-${feld.path}-keine`, children: "Dieses Zielmodell wird f\xFCr die Prompt-Optimierung verwendet und ist derzeit nicht direkt aus der App aufrufbar." }),
11223
11280
  profile.map((profil, index) => {
11224
- const befunde = checkImageExecutionProfile(profil);
11281
+ const befunde = alleBefunde[index] ?? [];
11225
11282
  const text = (schluessel) => typeof profil[schluessel] === "string" ? profil[schluessel] : "";
11226
11283
  const liste = (schluessel) => Array.isArray(profil[schluessel]) ? profil[schluessel].join(", ") : "";
11227
11284
  return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("fieldset", { "data-testid": `profil-${index}`, className: "szenen-feld__profil", children: [
@@ -12700,6 +12757,93 @@ function finish(report) {
12700
12757
  };
12701
12758
  }
12702
12759
 
12760
+ // src/lib/szenen/promptOperation.ts
12761
+ var PROMPT_RULE_EFFECT = {
12762
+ "P-01": "diagnostic",
12763
+ // Kamera-ID im Rig nicht auflösbar → ohne Kamera weiter
12764
+ "P-02": "diagnostic",
12765
+ // mehrdeutige Kamera-ID → erklären, nicht sperren
12766
+ "P-03": "diagnostic",
12767
+ // Abstammungsintegrität einer camera_id — Publish-Frage
12768
+ "P-04": "diagnostic",
12769
+ // referenzierte Version nicht veröffentlicht/sichtbar
12770
+ "P-05": "diagnostic",
12771
+ // Node-Referenz nicht auflösbar → Teil auslassen
12772
+ "P-06": "diagnostic",
12773
+ // Placement-Node mehrdeutig → ohne exakte Platzierung
12774
+ "P-07": "diagnostic",
12775
+ // spatial Pattern ohne can_place → Pattern weglassen
12776
+ "P-08": "diagnostic",
12777
+ // unvollständige Pattern-Bindings
12778
+ "P-09": "not_applicable",
12779
+ // reine Publish-/Prozessregel für sequence
12780
+ "P-10": "diagnostic",
12781
+ // Zyklus: Blocker nur, wenn keine endliche Auflösung bleibt
12782
+ "P-11": "diagnostic",
12783
+ // Override-Disziplin, schon bisher advisory
12784
+ "P-12": "not_applicable",
12785
+ // Laufzeitregel der Auflösung, keine Bedingung
12786
+ "P-13": "diagnostic",
12787
+ // pattern_derived ohne Pattern-Platzierung
12788
+ "P-14": "diagnostic",
12789
+ // Bildgrößen-/Optik-Plausibilität, erklärte Absicht gewinnt
12790
+ "P-15": "diagnostic",
12791
+ // vertikaler look_at ohne up_hint — Rollwinkel ist Spezialpräzision
12792
+ "P-16": "diagnostic",
12793
+ // Placements ohne Koordinatensystem
12794
+ "P-17": "diagnostic"
12795
+ // Target Model nicht auflösbar → generischer Prompt
12796
+ };
12797
+ function diagnosticOfFinding(finding) {
12798
+ return {
12799
+ code: finding.rule,
12800
+ origin: "rule",
12801
+ path: finding.path,
12802
+ message: finding.message
12803
+ };
12804
+ }
12805
+ function diagnosticOfSchemaError(error, where) {
12806
+ return {
12807
+ code: "schema",
12808
+ origin: "schema",
12809
+ path: error.instancePath || "/",
12810
+ message: `${where}: ${error.message}`
12811
+ };
12812
+ }
12813
+ function promptDiagnosticsOf(checks) {
12814
+ const befunde = [];
12815
+ for (const finding of checks.findings) {
12816
+ if (PROMPT_RULE_EFFECT[finding.rule] === "not_applicable") continue;
12817
+ befunde.push(diagnosticOfFinding(finding));
12818
+ }
12819
+ return befunde;
12820
+ }
12821
+ var isObject8 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
12822
+ var asArray2 = (v) => Array.isArray(v) ? v : [];
12823
+ var istText2 = (v) => typeof v === "string" && v.trim().length > 0;
12824
+ function describableContent(scenePayload) {
12825
+ if (!isObject8(scenePayload)) {
12826
+ return {
12827
+ ok: false,
12828
+ message: `Die Szene ist kein Objekt, sondern ${Array.isArray(scenePayload) ? "ein Array" : typeof scenePayload}. Daraus laesst sich kein Textauftrag herstellen.`
12829
+ };
12830
+ }
12831
+ const nodes = asArray2(scenePayload.nodes);
12832
+ for (const node of nodes) {
12833
+ if (!isObject8(node)) continue;
12834
+ const semantic = isObject8(node.semantic) ? node.semantic : null;
12835
+ if (semantic && istText2(semantic.description)) return { ok: true };
12836
+ if (asArray2(node.component_refs).length > 0) return { ok: true };
12837
+ }
12838
+ const environment = isObject8(scenePayload.environment) ? scenePayload.environment : null;
12839
+ if (environment && istText2(environment.text)) return { ok: true };
12840
+ if (environment && asArray2(environment.component_refs).length > 0) return { ok: true };
12841
+ return {
12842
+ ok: false,
12843
+ message: nodes.length === 0 ? "Die Szene hat keine Knoten und keinen Umgebungstext. Ohne beschreibbaren Inhalt gibt es nichts, woraus ein Prompt entstehen koennte." : `Keiner der ${nodes.length} Knoten traegt eine Beschreibung oder eine Komponentenreferenz, und es gibt keinen Umgebungstext. Ein Prompt daraus waere eine leere Behauptung.`
12844
+ };
12845
+ }
12846
+
12703
12847
  // src/lib/szenen/capabilities.ts
12704
12848
  var CapabilityAnchorError = class extends Error {
12705
12849
  };
@@ -12730,9 +12874,9 @@ var FLAG_OF_SCOPE = {
12730
12874
  // die Vererbung an die beiden anderen weitergereicht.
12731
12875
  global: "can_describe_scene"
12732
12876
  };
12733
- var isObject8 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
12877
+ var isObject9 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
12734
12878
  function nodeCountOf(scenePayload) {
12735
- if (!isObject8(scenePayload)) return 0;
12879
+ if (!isObject9(scenePayload)) return 0;
12736
12880
  return Array.isArray(scenePayload.nodes) ? scenePayload.nodes.length : 0;
12737
12881
  }
12738
12882
  async function capabilityReport(input) {
@@ -12757,7 +12901,7 @@ async function capabilityReport(input) {
12757
12901
  reason: "Payload verletzt das Schema \u2014 die Publish-Regeln wurden nicht ausgefuehrt"
12758
12902
  }))
12759
12903
  } : await runPublishChecks({ type, payload: setupPayload, elementId, parents, resolver });
12760
- if (!isObject8(setupPayload)) {
12904
+ if (!isObject9(setupPayload)) {
12761
12905
  const vorgefunden = Array.isArray(setupPayload) ? "array" : typeof setupPayload;
12762
12906
  const reasons = [
12763
12907
  {
@@ -12767,10 +12911,14 @@ async function capabilityReport(input) {
12767
12911
  ];
12768
12912
  return {
12769
12913
  can_describe_scene: { state: "no", reasons },
12914
+ // Auch fuer den Promptpfad ein echter Blocker: Eine Payload, die kein
12915
+ // Objekt ist, ist technisch unlesbar (Schritt 07, Klasse A/C) — nicht
12916
+ // bloss fachlich unvollstaendig.
12770
12917
  can_render_prompt: { state: "no", reasons },
12771
12918
  can_place: { state: "no", reasons },
12772
12919
  checks,
12773
- schemaErrors
12920
+ schemaErrors,
12921
+ promptDiagnostics: []
12774
12922
  };
12775
12923
  }
12776
12924
  const blockedByScope = /* @__PURE__ */ new Map();
@@ -12806,6 +12954,8 @@ async function capabilityReport(input) {
12806
12954
  };
12807
12955
  const base = forFlag("can_describe_scene");
12808
12956
  let describe;
12957
+ let szenenPayload = null;
12958
+ let sceneVersionId = null;
12809
12959
  if (base.blocked.length > 0) {
12810
12960
  describe = { state: "no", reasons: base.blocked };
12811
12961
  } else if (schemaErrors.length > 0) {
@@ -12831,9 +12981,10 @@ async function capabilityReport(input) {
12831
12981
  ]
12832
12982
  };
12833
12983
  } else {
12834
- const sceneRef = isObject8(setupPayload.scene_ref) ? setupPayload.scene_ref : null;
12835
- const sceneVersionId = typeof sceneRef?.version_id === "string" ? sceneRef.version_id : null;
12984
+ const sceneRef = isObject9(setupPayload.scene_ref) ? setupPayload.scene_ref : null;
12985
+ sceneVersionId = typeof sceneRef?.version_id === "string" ? sceneRef.version_id : null;
12836
12986
  const scene = chainScene !== void 0 ? { payload: chainScene } : sceneVersionId ? await resolver.getVersion(sceneVersionId) : null;
12987
+ if (scene) szenenPayload = { payload: scene.payload };
12837
12988
  const nodes = nodeCountOf(scene?.payload);
12838
12989
  if (nodes === 0) {
12839
12990
  describe = {
@@ -12880,15 +13031,59 @@ async function capabilityReport(input) {
12880
13031
  if (own.unchecked.length > 0) return { state: "unknown", reasons: own.unchecked };
12881
13032
  return { state: "yes", reasons: [] };
12882
13033
  };
13034
+ const promptFlag = (() => {
13035
+ if (!szenenPayload) {
13036
+ return {
13037
+ state: "unknown",
13038
+ reasons: [
13039
+ {
13040
+ kind: "structure",
13041
+ message: `Die Szene ${sceneVersionId ?? "(ohne version_id)"} wurde nicht gelesen; ob beschreibbarer Inhalt vorliegt, ist damit offen \u2014 keine Absage.`
13042
+ }
13043
+ ]
13044
+ };
13045
+ }
13046
+ const inhalt = describableContent(szenenPayload.payload);
13047
+ if (!inhalt.ok) {
13048
+ return { state: "no", reasons: [{ kind: "structure", message: inhalt.message }] };
13049
+ }
13050
+ return { state: "yes", reasons: [] };
13051
+ })();
13052
+ const promptDiagnostics = [
13053
+ ...schemaErrors.map((e) => diagnosticOfSchemaError(e, `Scene Setup ${elementId}`)),
13054
+ ...promptDiagnosticsOf(checks)
13055
+ ];
12883
13056
  return {
12884
13057
  can_describe_scene: describe,
12885
- can_render_prompt: derived("can_render_prompt"),
13058
+ can_render_prompt: promptFlag,
12886
13059
  can_place: derived("can_place", {
12887
13060
  ok: hasPlacementCapability(setupPayload),
12888
13061
  message: "Das Scene Setup traegt kein coordinate_system und ist damit nicht platzierbar (D-65)."
12889
13062
  }),
12890
13063
  checks,
12891
- schemaErrors
13064
+ schemaErrors,
13065
+ promptDiagnostics
13066
+ };
13067
+ }
13068
+ function sceneOnlyCapabilities(scenePayload) {
13069
+ const knoten = nodeCountOf(scenePayload);
13070
+ const inhalt = describableContent(scenePayload);
13071
+ return {
13072
+ can_describe_scene: knoten > 0 ? { state: "yes", reasons: [] } : {
13073
+ state: "no",
13074
+ reasons: [{
13075
+ kind: "structure",
13076
+ message: "Die Szene hat keine Knoten. Ohne Knoten gibt es nichts zu beschreiben."
13077
+ }]
13078
+ },
13079
+ can_render_prompt: inhalt.ok ? { state: "yes", reasons: [] } : { state: "no", reasons: [{ kind: "structure", message: inhalt.message }] },
13080
+ can_place: {
13081
+ state: "no",
13082
+ reasons: [{
13083
+ kind: "structure",
13084
+ message: "Zu dieser Szene gehoert kein Scene Setup. Weltplatzierung und coordinate_system liegen ausschliesslich dort (D-39/D-65) \u2014 es gibt also keine belastbaren Koordinaten."
13085
+ }]
13086
+ }
12892
13087
  };
12893
13088
  }
12894
13089
 
@@ -13007,60 +13202,72 @@ var CapabilityPanel = ({ port, elementId, setupPayload }) => {
13007
13202
  ] });
13008
13203
  };
13009
13204
 
13010
- // src/features/szenen/areas/ChainPromptAction.tsx
13205
+ // src/features/szenen/areas/PromptStartAction.tsx
13011
13206
  var import_react38 = require("react");
13012
13207
  var import_jsx_runtime33 = require("react/jsx-runtime");
13013
- var ChainPromptAction = ({
13208
+ var PromptStartAction = ({
13014
13209
  port,
13015
- shotElementId
13210
+ source,
13211
+ hinweise = [],
13212
+ blocker = null,
13213
+ onLauf
13016
13214
  }) => {
13017
13215
  const [lauf, setLauf] = (0, import_react38.useState)(null);
13018
13216
  const [meldung, setMeldung] = (0, import_react38.useState)(null);
13019
13217
  const [ausgang, setAusgang] = (0, import_react38.useState)(null);
13020
13218
  const [laeuft, setLaeuft] = (0, import_react38.useState)(false);
13021
13219
  const runs = port.runs;
13220
+ const ausKette = source.kind === "draft_chain";
13221
+ const id = (name) => ausKette ? `kette-${name}` : `szene-${name}`;
13022
13222
  async function erzeugen() {
13023
13223
  setLaeuft(true);
13024
13224
  setMeldung(null);
13025
13225
  setAusgang(null);
13026
13226
  try {
13027
- const ergebnis = await runs.startRunFromDraft({ shotElementId });
13227
+ const ergebnis = source.kind === "draft_chain" ? await runs.startRunFromDraft({ shotElementId: source.shotElementId }) : await runs.startRunFromScene({ sceneElementId: source.sceneElementId });
13028
13228
  if (ergebnis.kind !== "ok") {
13029
13229
  setMeldung(ergebnis.kind === "unsupported" ? `Dieser Host kann "${ergebnis.capability}" nicht.` : ergebnis.message);
13030
13230
  setAusgang(ergebnis.kind === "unsupported" ? "error" : ergebnis.kind);
13031
13231
  return;
13032
13232
  }
13033
13233
  setLauf(ergebnis.value);
13234
+ onLauf?.();
13034
13235
  } finally {
13035
13236
  setLaeuft(false);
13036
13237
  }
13037
13238
  }
13038
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("section", { className: "szenen-kette", "data-testid": "kette-prompt-aktion", children: [
13039
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("h5", { children: "Prompt aus der Draft-Kette" }),
13040
- /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("p", { className: "szenen-area__lead", children: [
13239
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("section", { className: "szenen-kette", "data-testid": id("prompt-aktion"), children: [
13240
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("h5", { children: ausKette ? "Prompt aus der Draft-Kette" : "Prompt aus dieser Szene" }),
13241
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { className: "szenen-area__lead", children: ausKette ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
13041
13242
  "Dieser Shot geh\xF6rt zu einer Draft-Kette. Der Lauf friert Szene, Aufbau und Shot in dem Stand ein, den er gerade vorfindet \u2014",
13042
13243
  " ",
13043
13244
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("strong", { children: "ohne" }),
13044
13245
  " dass etwas ver\xF6ffentlicht werden muss."
13045
- ] }),
13246
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
13247
+ "Der Lauf friert diese Szene in dem Stand ein, den er gerade vorfindet. Bildaufbau, Kamera und Zielmodell sind ",
13248
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("strong", { children: "nicht n\xF6tig" }),
13249
+ " \u2014 was fehlt, wird weggelassen und am Lauf festgehalten, nicht erg\xE4nzt."
13250
+ ] }) }),
13251
+ hinweise.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("ul", { className: "szenen-detail__hint", "data-testid": id("hinweise"), role: "status", children: hinweise.map((hinweis, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("li", { children: hinweis }, i)) }),
13252
+ blocker && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { className: "szenen-error", role: "alert", "data-testid": id("blocker"), children: blocker }),
13046
13253
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
13047
13254
  "button",
13048
13255
  {
13049
13256
  type: "button",
13050
- disabled: laeuft,
13257
+ disabled: laeuft || blocker !== null,
13051
13258
  onClick: () => void erzeugen(),
13052
- "data-testid": "kette-prompt",
13053
- children: laeuft ? "Pr\xFCft und erzeugt \u2026" : "Draft-Kette pr\xFCfen und Prompt erzeugen (kostet einen Modellaufruf)"
13259
+ "data-testid": id("prompt"),
13260
+ children: laeuft ? "Pr\xFCft und erzeugt \u2026" : ausKette ? "Draft-Kette pr\xFCfen und Prompt erzeugen (kostet einen Modellaufruf)" : "Prompt erzeugen (kostet einen Modellaufruf)"
13054
13261
  }
13055
13262
  ),
13056
- meldung && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "szenen-error", role: "alert", "data-testid": "kette-meldung", children: [
13263
+ meldung && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "szenen-error", role: "alert", "data-testid": id("meldung"), children: [
13057
13264
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { children: meldung }),
13058
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { "data-testid": "kette-kein-modell", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("strong", { children: "Es wurde kein Modell gerufen." }) }),
13059
- ausgang === "conflict" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { "data-testid": "kette-konflikt", children: "Ein Entwurf der Kette wurde zwischenzeitlich ge\xE4ndert. Ein erneuter Versuch friert den aktuellen Stand ein." }),
13060
- ausgang === "rejected" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { "data-testid": "kette-mangel", children: "Die Kette selbst ist noch nicht vollst\xE4ndig. Ein erneuter Versuch \xE4ndert daran nichts \u2014 die Entw\xFCrfe m\xFCssen zuerst erg\xE4nzt werden." }),
13061
- ausgang === "error" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { "data-testid": "kette-technisch", children: "Das war ein technisches Problem beim Zugriff, kein Mangel der Kette. Ein erneuter Versuch ist sinnvoll." })
13265
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { "data-testid": id("kein-modell"), children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("strong", { children: "Es wurde kein Modell gerufen." }) }),
13266
+ ausgang === "conflict" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { "data-testid": id("konflikt"), children: ausKette ? "Ein Entwurf der Kette wurde zwischenzeitlich ge\xE4ndert. Ein erneuter Versuch friert den aktuellen Stand ein." : "Die Szene wurde zwischenzeitlich ge\xE4ndert. Ein erneuter Versuch friert den aktuellen Stand ein." }),
13267
+ ausgang === "rejected" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { "data-testid": id("mangel"), children: ausKette ? "Die Kette selbst ist noch nicht vollst\xE4ndig. Ein erneuter Versuch \xE4ndert daran nichts \u2014 die Entw\xFCrfe m\xFCssen zuerst erg\xE4nzt werden." : "Aus diesem Stand l\xE4sst sich kein Auftrag herstellen. Ein erneuter Versuch \xE4ndert daran nichts \u2014 die Szene muss zuerst Inhalt bekommen." }),
13268
+ ausgang === "error" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { "data-testid": id("technisch"), children: "Das war ein technisches Problem beim Zugriff, kein fachlicher Mangel. Ein erneuter Versuch ist sinnvoll." })
13062
13269
  ] }),
13063
- lauf && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "szenen-run", "data-testid": "kette-lauf", children: [
13270
+ lauf && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "szenen-run", "data-testid": id("lauf"), children: [
13064
13271
  /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("h5", { children: [
13065
13272
  "Prompt-Lauf ",
13066
13273
  lauf.status === "succeeded" ? "gelungen" : "gescheitert"
@@ -13072,7 +13279,7 @@ var ChainPromptAction = ({
13072
13279
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("code", { children: lauf.model_id }),
13073
13280
  "."
13074
13281
  ] }),
13075
- lauf.status === "succeeded" && lauf.output && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("pre", { className: "szenen-proposal__payload", "data-testid": "kette-prompt-text", children: lauf.output.positive }),
13282
+ lauf.status === "succeeded" && lauf.output && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("pre", { className: "szenen-proposal__payload", "data-testid": id("prompt-text"), children: lauf.output.positive }),
13076
13283
  lauf.status === "failed" && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("p", { className: "szenen-error", children: [
13077
13284
  lauf.failure?.class,
13078
13285
  " \u2014 ",
@@ -13632,12 +13839,27 @@ var RunImagesPanel = ({ port, lauf }) => {
13632
13839
  "Lauf ",
13633
13840
  /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("code", { "data-testid": `eingang-lauf-${lauf.run_id}`, children: lauf.run_id })
13634
13841
  ] }),
13635
- quelle.kind === "published" ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { "data-testid": `eingang-herkunft-${lauf.run_id}`, children: [
13842
+ quelle.kind === "published" && /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { "data-testid": `eingang-herkunft-${lauf.run_id}`, children: [
13636
13843
  "ver\xF6ffentlichte Shot-Version ",
13637
13844
  /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("code", { children: quelle.shot_version_id })
13638
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { "data-testid": `eingang-herkunft-${lauf.run_id}`, children: [
13845
+ ] }),
13846
+ quelle.kind === "draft_chain" && /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { "data-testid": `eingang-herkunft-${lauf.run_id}`, children: [
13639
13847
  "Draft-Kette ",
13640
13848
  /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("code", { children: quelle.chain_id })
13849
+ ] }),
13850
+ quelle.kind === "scene_direct" && /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { "data-testid": `eingang-herkunft-${lauf.run_id}`, children: [
13851
+ "Szene ",
13852
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("code", { children: quelle.scene.element_id }),
13853
+ quelle.scene.draft_id ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
13854
+ " \xB7 Entwurf ",
13855
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("code", { children: quelle.scene.draft_id }),
13856
+ " (Revision ",
13857
+ quelle.scene.revision,
13858
+ ")"
13859
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
13860
+ " \xB7 Version ",
13861
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("code", { children: quelle.scene.version_id })
13862
+ ] })
13641
13863
  ] })
13642
13864
  ] }),
13643
13865
  quelle.kind === "draft_chain" && quelle.derivation_run_id ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
@@ -14203,9 +14425,13 @@ var RunsPanel = ({ port, source, contextId }) => {
14203
14425
  const [notiz, setNotiz] = (0, import_react40.useState)(null);
14204
14426
  const [nachladen, setNachladen] = (0, import_react40.useState)(0);
14205
14427
  const faehig = capabilitiesOf(port);
14206
- const quellSchluessel = source.kind === "published" ? source.shotVersionId : source.chainId;
14428
+ const quellSchluessel = source.kind === "published" ? source.shotVersionId : source.kind === "draft_chain" ? source.chainId : source.sceneElementId;
14207
14429
  const [phase, neuLaden] = usePortQuery(
14208
- () => source.kind === "published" ? port.runs.listRunsForShot(source.shotVersionId) : port.runs.listRunsForChain(source.chainId),
14430
+ () => {
14431
+ if (source.kind === "published") return port.runs.listRunsForShot(source.shotVersionId);
14432
+ if (source.kind === "draft_chain") return port.runs.listRunsForChain(source.chainId);
14433
+ return port.runs.listRunsForScene(source.sceneElementId);
14434
+ },
14209
14435
  [port, source.kind, quellSchluessel, nachladen]
14210
14436
  );
14211
14437
  const starten = async () => {
@@ -14244,7 +14470,7 @@ var RunsPanel = ({ port, source, contextId }) => {
14244
14470
  "L\xE4ufe konnten nicht geladen werden: ",
14245
14471
  phase.message
14246
14472
  ] }),
14247
- phase.status === "ready" && laeufe.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { "data-testid": "keine-laeufe", children: source.kind === "published" ? "F\xFCr diese Shot-Version gibt es noch keinen Lauf." : "F\xFCr diese Draft-Kette gibt es noch keinen Lauf." }),
14473
+ phase.status === "ready" && laeufe.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { "data-testid": "keine-laeufe", children: source.kind === "published" ? "F\xFCr diese Shot-Version gibt es noch keinen Lauf." : source.kind === "draft_chain" ? "F\xFCr diese Draft-Kette gibt es noch keinen Lauf." : "F\xFCr diese Szene gibt es noch keinen direkten Lauf." }),
14248
14474
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("ol", { "data-testid": "runden-liste", children: laeufe.map((lauf) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
14249
14475
  RundenEintrag,
14250
14476
  {
@@ -14449,6 +14675,23 @@ var ElementDetail = ({
14449
14675
  if (!kette2) return null;
14450
14676
  return kette2.links[0]?.field ?? null;
14451
14677
  }, [schnappschuss?.draft?.chain]);
14678
+ const laufStand = (0, import_react41.useMemo)(() => {
14679
+ if (!schnappschuss || schnappschuss.element.type !== "scene") return null;
14680
+ if (schnappschuss.draft) return schnappschuss.draft.payload;
14681
+ const kopf = usableHeadVersionId(kopfAufloesung);
14682
+ if (!kopf) return null;
14683
+ return schnappschuss.versions.find((v) => v.version_id === kopf)?.payload ?? null;
14684
+ }, [schnappschuss, kopfAufloesung]);
14685
+ const szenenBlocker = (0, import_react41.useMemo)(() => {
14686
+ if (!schnappschuss || schnappschuss.element.type !== "scene") return null;
14687
+ if (laufStand === null) {
14688
+ return schnappschuss.versions.length > 0 ? "Diese Szene hat keinen Entwurf und in diesem Kontext keinen bevorzugten Kopf. Welcher Stand gilt, entscheidet keine Reihenfolge (D-36) \u2014 bitte oben eine Version als bevorzugt setzen." : "Diese Szene hat noch keinen gespeicherten Stand. Ein Lauf friert ein, was gespeichert ist \u2014 bitte zuerst den Entwurf speichern.";
14689
+ }
14690
+ const flag = sceneOnlyCapabilities(laufStand).can_render_prompt;
14691
+ if (flag.state === "yes") return null;
14692
+ const grund = flag.reasons[0];
14693
+ return grund && "message" in grund ? grund.message : "Kein Lauf moeglich.";
14694
+ }, [schnappschuss, laufStand]);
14452
14695
  const vorschau = (0, import_react41.useMemo)(() => {
14453
14696
  if (!schnappschuss) return null;
14454
14697
  try {
@@ -14582,8 +14825,7 @@ var ElementDetail = ({
14582
14825
  /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("header", { children: [
14583
14826
  /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("h3", { children: schnappschuss.metadata?.title ?? "(ohne Titel)" }),
14584
14827
  /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("p", { className: "szenen-detail__ids", children: [
14585
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { children: typ }),
14586
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("code", { children: schnappschuss.element.element_id }),
14828
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { "data-testid": "element-typ", children: elementTypLabel(typ) }),
14587
14829
  schnappschuss.element.retired && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { "data-testid": "ist-stillgelegt", children: "stillgelegt" })
14588
14830
  ] })
14589
14831
  ] }),
@@ -14761,10 +15003,13 @@ var ElementDetail = ({
14761
15003
  // voraus" gilt nur noch fuer den Einzel-Shot.
14762
15004
  draft?.chain ? /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
14763
15005
  /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
14764
- ChainPromptAction,
15006
+ PromptStartAction,
14765
15007
  {
14766
15008
  port,
14767
- shotElementId: schnappschuss.element.element_id
15009
+ source: {
15010
+ kind: "draft_chain",
15011
+ shotElementId: schnappschuss.element.element_id
15012
+ }
14768
15013
  }
14769
15014
  ),
14770
15015
  /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
@@ -14784,12 +15029,36 @@ var ElementDetail = ({
14784
15029
  source: { kind: "published", shotVersionId: laufVersionId }
14785
15030
  }
14786
15031
  ) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { "data-testid": "laufquelle-unklar", children: "Dieser Shot hat mehrere ver\xF6ffentlichte Versionen und keinen bevorzugten Kopf in diesem Kontext. Welche davon einen Lauf tr\xE4gt, entscheidet keine Reihenfolge \u2014 bitte oben eine Version anzeigen oder als bevorzugt setzen." })),
14787
- typ === "scene_setup" && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
14788
- CapabilityPanel,
14789
- {
14790
- port,
14791
- elementId,
14792
- setupPayload: payloadAusFormular()
15032
+ typ === "scene" && /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
15033
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
15034
+ PromptStartAction,
15035
+ {
15036
+ port,
15037
+ source: { kind: "scene", sceneElementId: schnappschuss.element.element_id },
15038
+ hinweise: [
15039
+ "Kein Bildaufbau und keine Kamera \u2014 der Prompt wird ohne Kameravorgabe erzeugt.",
15040
+ "Kein Zielmodell \u2014 generische Promptableitung ohne Modelloptimierung.",
15041
+ schnappschuss.draft ? "Eingefroren wird der gespeicherte Entwurf. Nicht gespeicherte Aenderungen im Formular gehen nicht mit." : "Eingefroren wird die wirksame veroeffentlichte Version."
15042
+ ],
15043
+ blocker: szenenBlocker,
15044
+ onLauf: () => setNachladen((n) => n + 1)
15045
+ }
15046
+ ),
15047
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
15048
+ RunsPanel,
15049
+ {
15050
+ port,
15051
+ contextId,
15052
+ source: { kind: "scene", sceneElementId: schnappschuss.element.element_id }
15053
+ }
15054
+ )
15055
+ ] }),
15056
+ typ === "scene_setup" && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
15057
+ CapabilityPanel,
15058
+ {
15059
+ port,
15060
+ elementId,
15061
+ setupPayload: payloadAusFormular()
14793
15062
  }
14794
15063
  ),
14795
15064
  notiz && /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
@@ -14808,6 +15077,26 @@ var ElementDetail = ({
14808
15077
  ] });
14809
15078
  };
14810
15079
 
15080
+ // src/features/szenen/areas/useSchmalesDisplay.ts
15081
+ var import_react42 = require("react");
15082
+ var SCHMALE_ANSICHT = "(max-width: 760px)";
15083
+ function messen(abfrage) {
15084
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
15085
+ return window.matchMedia(abfrage).matches;
15086
+ }
15087
+ function useSchmalesDisplay(abfrage = SCHMALE_ANSICHT) {
15088
+ const [schmal, setSchmal] = (0, import_react42.useState)(() => messen(abfrage));
15089
+ (0, import_react42.useEffect)(() => {
15090
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
15091
+ const liste = window.matchMedia(abfrage);
15092
+ const aktualisieren = () => setSchmal(liste.matches);
15093
+ aktualisieren();
15094
+ liste.addEventListener?.("change", aktualisieren);
15095
+ return () => liste.removeEventListener?.("change", aktualisieren);
15096
+ }, [abfrage]);
15097
+ return schmal;
15098
+ }
15099
+
14811
15100
  // src/features/szenen/areas/ElementsArea.tsx
14812
15101
  var import_jsx_runtime38 = require("react/jsx-runtime");
14813
15102
  var ElementsArea = ({
@@ -14816,18 +15105,31 @@ var ElementsArea = ({
14816
15105
  author = "szenen-view",
14817
15106
  restrictToTypes,
14818
15107
  allTypesLabel,
15108
+ listTitle,
14819
15109
  onSelect
14820
15110
  }) => {
14821
15111
  const auswahlTypen = restrictToTypes ?? SZENEN_ELEMENT_TYPES;
14822
- const [typ, setTyp] = (0, import_react42.useState)(
14823
- restrictToTypes && restrictToTypes.length === 1 ? restrictToTypes[0] : ""
14824
- );
14825
- const [zeigeStillgelegte, setZeigeStillgelegte] = (0, import_react42.useState)(false);
14826
- const [gewaehlt, setGewaehlt] = (0, import_react42.useState)(null);
14827
- const [neuerTitel, setNeuerTitel] = (0, import_react42.useState)("");
14828
- const [neuerTyp, setNeuerTyp] = (0, import_react42.useState)(auswahlTypen[0]);
14829
- const [notiz, setNotiz] = (0, import_react42.useState)(null);
14830
- const [anlegend, setAnlegend] = (0, import_react42.useState)(false);
15112
+ const einzelTyp = auswahlTypen.length === 1 ? auswahlTypen[0] : null;
15113
+ const gemischt = auswahlTypen.length > 1;
15114
+ const ueberschrift = listTitle ?? (einzelTyp ? elementTypPlural(einzelTyp) : "Bausteine");
15115
+ const einzahl = einzelTyp ? elementTypLabel(einzelTyp) : "Objekt";
15116
+ const [typ, setTyp] = (0, import_react43.useState)(einzelTyp ?? "");
15117
+ const [zeigeStillgelegte, setZeigeStillgelegte] = (0, import_react43.useState)(false);
15118
+ const [gewaehlt, setGewaehlt] = (0, import_react43.useState)(null);
15119
+ const [neuerTitel, setNeuerTitel] = (0, import_react43.useState)("");
15120
+ const [neuerTyp, setNeuerTyp] = (0, import_react43.useState)(auswahlTypen[0]);
15121
+ const [notiz, setNotiz] = (0, import_react43.useState)(null);
15122
+ const [anlegend, setAnlegend] = (0, import_react43.useState)(false);
15123
+ const [anlageOffen, setAnlageOffen] = (0, import_react43.useState)(false);
15124
+ const schmal = useSchmalesDisplay();
15125
+ const [ansicht, setAnsicht] = (0, import_react43.useState)("liste");
15126
+ const zeigeListe = !schmal || ansicht === "liste";
15127
+ const zeigeDetail = !schmal || ansicht === "detail";
15128
+ const waehle = (elementId) => {
15129
+ setGewaehlt(elementId);
15130
+ setAnsicht("detail");
15131
+ onSelect?.(elementId);
15132
+ };
14831
15133
  const [phase, neuLaden] = usePortQuery(
14832
15134
  () => port.elements.listElements({
14833
15135
  contextId,
@@ -14836,128 +15138,199 @@ var ElementsArea = ({
14836
15138
  }),
14837
15139
  [port, contextId, typ, zeigeStillgelegte]
14838
15140
  );
14839
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "szenen-elements", "data-testid": "bereich-elemente", children: [
14840
- /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "szenen-elements__filter", children: [
14841
- /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("label", { children: [
14842
- "Typ",
14843
- /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
14844
- "select",
14845
- {
14846
- "data-testid": "filter-typ",
14847
- value: typ,
14848
- onChange: (e) => setTyp(e.target.value),
14849
- children: [
14850
- (!restrictToTypes || restrictToTypes.length > 1) && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("option", { value: "", children: allTypesLabel ?? "alle Typen" }),
14851
- auswahlTypen.map((t) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("option", { value: t, children: t }, t))
14852
- ]
14853
- }
14854
- )
14855
- ] }),
14856
- /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("label", { children: [
14857
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
14858
- "input",
14859
- {
14860
- type: "checkbox",
14861
- "data-testid": "filter-stillgelegt",
14862
- checked: zeigeStillgelegte,
14863
- onChange: (e) => setZeigeStillgelegte(e.target.checked)
14864
- }
14865
- ),
14866
- "stillgelegte Elemente einblenden"
14867
- ] }),
14868
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("button", { type: "button", "data-testid": "neu-laden", onClick: neuLaden, children: "neu laden" })
14869
- ] }),
14870
- /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
14871
- "form",
14872
- {
14873
- className: "szenen-elements__create",
14874
- "data-testid": "element-anlegen",
14875
- onSubmit: async (e) => {
14876
- e.preventDefault();
14877
- if (!neuerTitel.trim()) return;
14878
- setAnlegend(true);
14879
- const ergebnis = await port.elements.createElement({
14880
- type: neuerTyp,
14881
- title: neuerTitel.trim(),
14882
- contextId
14883
- });
14884
- setNotiz(noticeFor(ergebnis, `Element \u201E${neuerTitel.trim()}" anlegen`));
14885
- setAnlegend(false);
14886
- if (ergebnis.kind === "ok") {
14887
- setNeuerTitel("");
14888
- setGewaehlt(ergebnis.value.element_id);
14889
- onSelect?.(ergebnis.value.element_id);
14890
- neuLaden();
14891
- }
14892
- },
14893
- children: [
14894
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
14895
- "input",
15141
+ const anlageSchliessen = () => {
15142
+ setAnlageOffen(false);
15143
+ setNeuerTitel("");
15144
+ };
15145
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
15146
+ "div",
15147
+ {
15148
+ className: "szenen-elements",
15149
+ "data-testid": "bereich-elemente",
15150
+ "data-ansicht": schmal ? ansicht : "beide",
15151
+ children: [
15152
+ zeigeListe && /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "szenen-elements__master", "data-testid": "elemente-master", children: [
15153
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "szenen-elements__toolbar", children: [
15154
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("h3", { className: "szenen-elements__heading", children: ueberschrift }),
15155
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
15156
+ "button",
15157
+ {
15158
+ type: "button",
15159
+ className: "szenen-elements__new",
15160
+ "data-testid": "neu-oeffnen",
15161
+ "aria-expanded": anlageOffen,
15162
+ onClick: () => {
15163
+ setNotiz(null);
15164
+ if (anlageOffen) anlageSchliessen();
15165
+ else setAnlageOffen(true);
15166
+ },
15167
+ children: "+ Neu"
15168
+ }
15169
+ )
15170
+ ] }),
15171
+ gemischt && /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("label", { className: "szenen-elements__scope", children: [
15172
+ "Art",
15173
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
15174
+ "select",
15175
+ {
15176
+ "data-testid": "filter-typ",
15177
+ value: typ,
15178
+ onChange: (e) => setTyp(e.target.value),
15179
+ children: [
15180
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("option", { value: "", children: allTypesLabel ?? "alle Typen" }),
15181
+ auswahlTypen.map((t) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("option", { value: t, children: elementTypPlural(t) }, t))
15182
+ ]
15183
+ }
15184
+ )
15185
+ ] }),
15186
+ anlageOffen && /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
15187
+ "form",
14896
15188
  {
14897
- "data-testid": "neuer-titel",
14898
- value: neuerTitel,
14899
- placeholder: "Titel des neuen Elements",
14900
- onChange: (e) => setNeuerTitel(e.target.value)
15189
+ className: "szenen-elements__create",
15190
+ "data-testid": "element-anlegen",
15191
+ onSubmit: async (e) => {
15192
+ e.preventDefault();
15193
+ if (!neuerTitel.trim()) return;
15194
+ setAnlegend(true);
15195
+ const gewuenschterTyp = einzelTyp ?? neuerTyp;
15196
+ const titel = neuerTitel.trim();
15197
+ const ergebnis = await port.elements.createElement({
15198
+ type: gewuenschterTyp,
15199
+ title: titel,
15200
+ contextId
15201
+ });
15202
+ setNotiz(noticeFor(
15203
+ ergebnis,
15204
+ `${elementTypLabel(gewuenschterTyp)} \u201E${titel}" anlegen`
15205
+ ));
15206
+ setAnlegend(false);
15207
+ if (ergebnis.kind === "ok") {
15208
+ anlageSchliessen();
15209
+ waehle(ergebnis.value.element_id);
15210
+ neuLaden();
15211
+ }
15212
+ },
15213
+ children: [
15214
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("label", { className: "szenen-elements__create-title", children: [
15215
+ einzelTyp ? `Neue ${einzahl}` : "Titel",
15216
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
15217
+ "input",
15218
+ {
15219
+ "data-testid": "neuer-titel",
15220
+ value: neuerTitel,
15221
+ autoFocus: true,
15222
+ placeholder: einzelTyp ? `Titel der neuen ${einzahl}` : "Titel",
15223
+ onChange: (e) => setNeuerTitel(e.target.value)
15224
+ }
15225
+ )
15226
+ ] }),
15227
+ gemischt && /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("label", { className: "szenen-elements__create-type", children: [
15228
+ "Art",
15229
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
15230
+ "select",
15231
+ {
15232
+ "data-testid": "neuer-typ",
15233
+ value: neuerTyp,
15234
+ onChange: (e) => setNeuerTyp(e.target.value),
15235
+ children: auswahlTypen.map((t) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("option", { value: t, children: elementTypLabel(t) }, t))
15236
+ }
15237
+ )
15238
+ ] }),
15239
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "szenen-elements__create-actions", children: [
15240
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
15241
+ "button",
15242
+ {
15243
+ type: "submit",
15244
+ "data-testid": "anlegen",
15245
+ disabled: anlegend || !neuerTitel.trim(),
15246
+ children: "anlegen"
15247
+ }
15248
+ ),
15249
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
15250
+ "button",
15251
+ {
15252
+ type: "button",
15253
+ "data-testid": "anlegen-abbrechen",
15254
+ onClick: anlageSchliessen,
15255
+ children: "abbrechen"
15256
+ }
15257
+ )
15258
+ ] })
15259
+ ]
14901
15260
  }
14902
15261
  ),
14903
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
14904
- "select",
15262
+ notiz && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("p", { "data-testid": "anlegen-notiz", "data-tone": notiz.tone, children: notiz.text }),
15263
+ phase.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("p", { "data-testid": "elemente-laden", children: "Elemente werden geladen \u2026" }),
15264
+ phase.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("p", { "data-testid": "elemente-fehler", role: "alert", children: [
15265
+ "Elemente konnten nicht geladen werden: ",
15266
+ phase.message,
15267
+ phase.retryable && " \u2014 ein erneuter Versuch kann helfen."
15268
+ ] }),
15269
+ phase.status === "ready" && phase.value.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("p", { "data-testid": "elemente-leer", children: [
15270
+ typ ? `Keine ${elementTypPlural(typ)} in diesem Kontext.` : "Nichts in diesem Kontext.",
15271
+ " ",
15272
+ 'Mit \u201E+ Neu" anlegen.'
15273
+ ] }),
15274
+ phase.status === "ready" && phase.value.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("ul", { className: "szenen-elements__list", "data-testid": "elemente-liste", children: phase.value.map((element) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("li", { "data-element-id": element.element_id, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
15275
+ "button",
15276
+ {
15277
+ type: "button",
15278
+ "data-testid": `element-waehlen-${element.element_id}`,
15279
+ "aria-current": element.element_id === gewaehlt,
15280
+ className: element.element_id === gewaehlt ? "is-selected" : void 0,
15281
+ onClick: () => waehle(element.element_id),
15282
+ children: [
15283
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("strong", { className: "szenen-elements__title", children: element.title || "Unbenanntes Element" }),
15284
+ gemischt && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "szenen-elements__type", children: elementTypLabel(element.type) }),
15285
+ element.subtype && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "szenen-elements__subtype", children: element.subtype }),
15286
+ element.retired && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "szenen-elements__retired", children: "stillgelegt" })
15287
+ ]
15288
+ }
15289
+ ) }, element.element_id)) }),
15290
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("details", { className: "szenen-elements__options", "data-testid": "listen-optionen", children: [
15291
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("summary", { children: "Listenoptionen" }),
15292
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "szenen-elements__filter", children: [
15293
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("label", { children: [
15294
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
15295
+ "input",
15296
+ {
15297
+ type: "checkbox",
15298
+ "data-testid": "filter-stillgelegt",
15299
+ checked: zeigeStillgelegte,
15300
+ onChange: (e) => setZeigeStillgelegte(e.target.checked)
15301
+ }
15302
+ ),
15303
+ "stillgelegte Elemente einblenden"
15304
+ ] }),
15305
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("button", { type: "button", "data-testid": "neu-laden", onClick: neuLaden, children: "neu laden" })
15306
+ ] })
15307
+ ] })
15308
+ ] }),
15309
+ zeigeDetail && /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "szenen-elements__detail", "data-testid": "elemente-detail-spalte", children: [
15310
+ schmal && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
15311
+ "button",
14905
15312
  {
14906
- "data-testid": "neuer-typ",
14907
- value: neuerTyp,
14908
- onChange: (e) => setNeuerTyp(e.target.value),
14909
- children: auswahlTypen.map((t) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("option", { value: t, children: t }, t))
15313
+ type: "button",
15314
+ className: "szenen-elements__back",
15315
+ "data-testid": "zurueck-zur-liste",
15316
+ onClick: () => setAnsicht("liste"),
15317
+ children: "\u2190 Zur\xFCck zur Liste"
14910
15318
  }
14911
15319
  ),
14912
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("button", { type: "submit", "data-testid": "anlegen", disabled: anlegend || !neuerTitel.trim(), children: "anlegen" })
14913
- ]
14914
- }
14915
- ),
14916
- notiz && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("p", { "data-testid": "anlegen-notiz", "data-tone": notiz.tone, children: notiz.text }),
14917
- phase.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("p", { "data-testid": "elemente-laden", children: "Elemente werden geladen \u2026" }),
14918
- phase.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("p", { "data-testid": "elemente-fehler", role: "alert", children: [
14919
- "Elemente konnten nicht geladen werden: ",
14920
- phase.message,
14921
- phase.retryable && " \u2014 ein erneuter Versuch kann helfen."
14922
- ] }),
14923
- phase.status === "ready" && phase.value.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("p", { "data-testid": "elemente-leer", children: [
14924
- "Keine Elemente in diesem Kontext",
14925
- typ ? ` mit Typ \u201E${typ}"` : "",
14926
- "."
14927
- ] }),
14928
- phase.status === "ready" && phase.value.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("ul", { className: "szenen-elements__list", "data-testid": "elemente-liste", children: phase.value.map((element) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("li", { "data-element-id": element.element_id, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
14929
- "button",
14930
- {
14931
- type: "button",
14932
- "aria-current": element.element_id === gewaehlt,
14933
- className: element.element_id === gewaehlt ? "is-selected" : void 0,
14934
- onClick: () => {
14935
- setGewaehlt(element.element_id);
14936
- onSelect?.(element.element_id);
14937
- },
14938
- children: [
14939
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("strong", { className: "szenen-elements__title", children: element.title || "Unbenanntes Element" }),
14940
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "szenen-elements__type", children: element.type }),
14941
- element.subtype && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "szenen-elements__subtype", children: element.subtype }),
14942
- /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("span", { className: "szenen-elements__technical", children: [
14943
- "ID ",
14944
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("code", { className: "szenen-elements__id", children: element.element_id })
14945
- ] }),
14946
- element.retired && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "szenen-elements__retired", children: "stillgelegt" })
14947
- ]
14948
- }
14949
- ) }, element.element_id)) }),
14950
- gewaehlt && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
14951
- ElementDetail,
14952
- {
14953
- port,
14954
- elementId: gewaehlt,
14955
- contextId,
14956
- author,
14957
- onChanged: neuLaden
14958
- }
14959
- )
14960
- ] });
15320
+ gewaehlt ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
15321
+ ElementDetail,
15322
+ {
15323
+ port,
15324
+ elementId: gewaehlt,
15325
+ contextId,
15326
+ author,
15327
+ onChanged: neuLaden
15328
+ }
15329
+ ) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("p", { className: "szenen-elements__empty-detail", "data-testid": "keine-auswahl", children: "Nichts ausgew\xE4hlt. Ein Eintrag aus der Liste \xF6ffnet die Bearbeitung." })
15330
+ ] })
15331
+ ]
15332
+ }
15333
+ );
14961
15334
  };
14962
15335
 
14963
15336
  // src/features/szenen/areas/SceneArea.tsx
@@ -14970,14 +15343,15 @@ var SceneArea = ({ port, contextId }) => /* @__PURE__ */ (0, import_jsx_runtime3
14970
15343
  // src/features/szenen/areas/SetupArea.tsx
14971
15344
  var import_jsx_runtime40 = require("react/jsx-runtime");
14972
15345
  var SetupArea = ({ port, contextId }) => /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "szenen-setup", "data-testid": "bereich-setup", children: [
14973
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "szenen-area__lead", children: "Scene Setups und Camera Rigs dieses Kontexts." }),
15346
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "szenen-area__lead", children: "Bildaufbauten, Kameras und Stile dieses Kontexts." }),
14974
15347
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
14975
15348
  ElementsArea,
14976
15349
  {
14977
15350
  port,
14978
15351
  contextId,
14979
15352
  restrictToTypes: ["scene_setup", "camera_preset", "camera_pattern", "style"],
14980
- allTypesLabel: "alle Bildaufbau-Typen"
15353
+ allTypesLabel: "alle Bildaufbau-Typen",
15354
+ listTitle: "Bildaufbau"
14981
15355
  }
14982
15356
  )
14983
15357
  ] });
@@ -14985,34 +15359,35 @@ var SetupArea = ({ port, contextId }) => /* @__PURE__ */ (0, import_jsx_runtime4
14985
15359
  // src/features/szenen/areas/ShotArea.tsx
14986
15360
  var import_jsx_runtime41 = require("react/jsx-runtime");
14987
15361
  var ShotArea = ({ port, contextId }) => /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "szenen-shot", "data-testid": "bereich-shot", children: [
14988
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "szenen-area__lead", children: "Shots dieses Kontexts. Auswahl zeigt Runden und startet Prompt-L\xE4ufe." }),
15362
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "szenen-area__lead", children: "Prompts dieses Kontexts. Auswahl zeigt Runden und startet Prompt-L\xE4ufe." }),
14989
15363
  /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
14990
15364
  ElementsArea,
14991
15365
  {
14992
15366
  port,
14993
15367
  contextId,
14994
15368
  restrictToTypes: ["shot", "shot_concept", "target_model"],
14995
- allTypesLabel: "alle Prompt-Typen"
15369
+ allTypesLabel: "alle Prompt-Typen",
15370
+ listTitle: "Prompts"
14996
15371
  }
14997
15372
  )
14998
15373
  ] });
14999
15374
 
15000
15375
  // src/features/szenen/areas/MediaArea.tsx
15001
- var import_react43 = require("react");
15376
+ var import_react44 = require("react");
15002
15377
  var import_jsx_runtime42 = require("react/jsx-runtime");
15003
15378
  var leer = async () => ({ kind: "ok", value: [] });
15004
15379
  var MediaArea = ({ port }) => {
15005
15380
  const media = port.media;
15006
- const [versionInput, setVersionInput] = (0, import_react43.useState)("");
15007
- const [versionId, setVersionId] = (0, import_react43.useState)("");
15008
- const [itemInput, setItemInput] = (0, import_react43.useState)("");
15009
- const [itemId, setItemId] = (0, import_react43.useState)("");
15010
- const [assetId, setAssetId] = (0, import_react43.useState)("");
15011
- const [role, setRole] = (0, import_react43.useState)("reference");
15012
- const [position, setPosition] = (0, import_react43.useState)("0");
15013
- const [notiz, setNotiz] = (0, import_react43.useState)(null);
15014
- const [busy, setBusy] = (0, import_react43.useState)(false);
15015
- const [refresh, setRefresh] = (0, import_react43.useState)(0);
15381
+ const [versionInput, setVersionInput] = (0, import_react44.useState)("");
15382
+ const [versionId, setVersionId] = (0, import_react44.useState)("");
15383
+ const [itemInput, setItemInput] = (0, import_react44.useState)("");
15384
+ const [itemId, setItemId] = (0, import_react44.useState)("");
15385
+ const [assetId, setAssetId] = (0, import_react44.useState)("");
15386
+ const [role, setRole] = (0, import_react44.useState)("reference");
15387
+ const [position, setPosition] = (0, import_react44.useState)("0");
15388
+ const [notiz, setNotiz] = (0, import_react44.useState)(null);
15389
+ const [busy, setBusy] = (0, import_react44.useState)(false);
15390
+ const [refresh, setRefresh] = (0, import_react44.useState)(0);
15016
15391
  const [versionPhase] = usePortQuery(
15017
15392
  () => versionId ? media.listVersionMedia(versionId) : leer(),
15018
15393
  [media, versionId, refresh]
@@ -15138,7 +15513,7 @@ var MediaArea = ({ port }) => {
15138
15513
  };
15139
15514
 
15140
15515
  // src/features/szenen/areas/DeriveArea.tsx
15141
- var import_react46 = require("react");
15516
+ var import_react47 = require("react");
15142
15517
 
15143
15518
  // src/lib/szenen/derivationExamples.ts
15144
15519
  var PROPOSAL_EXAMPLE = {
@@ -15546,7 +15921,7 @@ function validateDerivationInput(input) {
15546
15921
  if (input.policy.produces.length === 0) return "Die Regel erzeugt keinen einzigen Vorschlag.";
15547
15922
  return null;
15548
15923
  }
15549
- var isObject9 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
15924
+ var isObject10 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
15550
15925
  var MAX_PROPOSAL_TITLE_LENGTH = 120;
15551
15926
  function fallbackProposalTitle(type) {
15552
15927
  return `Rekonstruktion ${type}`;
@@ -15572,7 +15947,7 @@ function resolveJsonPointer(wurzel, pointer) {
15572
15947
  aktuell = aktuell[index];
15573
15948
  continue;
15574
15949
  }
15575
- if (!isObject9(aktuell) || !(schluessel in aktuell)) {
15950
+ if (!isObject10(aktuell) || !(schluessel in aktuell)) {
15576
15951
  return { found: false, value: void 0 };
15577
15952
  }
15578
15953
  aktuell = aktuell[schluessel];
@@ -15586,7 +15961,7 @@ function collectVersionRefs2(value) {
15586
15961
  knoten.forEach(besuchen);
15587
15962
  return;
15588
15963
  }
15589
- if (!isObject9(knoten)) return;
15964
+ if (!isObject10(knoten)) return;
15590
15965
  if (typeof knoten.element_id === "string" && typeof knoten.version_id === "string") {
15591
15966
  gefunden.push({ element_id: knoten.element_id, version_id: knoten.version_id });
15592
15967
  }
@@ -15652,7 +16027,7 @@ function assessDerivationResponse(value, input) {
15652
16027
  envelope: null,
15653
16028
  contract_ok: false
15654
16029
  });
15655
- if (!isObject9(value)) {
16030
+ if (!isObject10(value)) {
15656
16031
  befund({
15657
16032
  code: "response.not_object",
15658
16033
  severity: "error",
@@ -15670,7 +16045,7 @@ function assessDerivationResponse(value, input) {
15670
16045
  });
15671
16046
  }
15672
16047
  const rohProposals = value.proposals;
15673
- const proposalsLesbar = isObject9(rohProposals);
16048
+ const proposalsLesbar = isObject10(rohProposals);
15674
16049
  if (!proposalsLesbar) {
15675
16050
  befund({
15676
16051
  code: "envelope.proposals_missing",
@@ -15680,7 +16055,7 @@ function assessDerivationResponse(value, input) {
15680
16055
  });
15681
16056
  }
15682
16057
  const roh = proposalsLesbar ? rohProposals : {};
15683
- const rohGaps = isObject9(value.gaps) ? value.gaps : {};
16058
+ const rohGaps = isObject10(value.gaps) ? value.gaps : {};
15684
16059
  const gapsUngefragt = Object.keys(rohGaps).filter(
15685
16060
  (t) => !input.policy.produces.includes(t)
15686
16061
  );
@@ -15692,7 +16067,7 @@ function assessDerivationResponse(value, input) {
15692
16067
  pointer: "/gaps"
15693
16068
  });
15694
16069
  }
15695
- const fehlend = input.policy.produces.filter((t) => !isObject9(roh[t]));
16070
+ const fehlend = input.policy.produces.filter((t) => !isObject10(roh[t]));
15696
16071
  if (fehlend.length > 0 && proposalsLesbar) {
15697
16072
  const d = befund({
15698
16073
  code: "envelope.proposals_missing_types",
@@ -15713,10 +16088,10 @@ function assessDerivationResponse(value, input) {
15713
16088
  pointer: "/proposals"
15714
16089
  });
15715
16090
  }
15716
- const rohTitles = isObject9(value.titles) ? value.titles : {};
16091
+ const rohTitles = isObject10(value.titles) ? value.titles : {};
15717
16092
  const umgebucht = [];
15718
16093
  for (const type of input.policy.produces) {
15719
- if (!isObject9(roh[type])) continue;
16094
+ if (!isObject10(roh[type])) continue;
15720
16095
  const payload = roh[type];
15721
16096
  const eigene = [];
15722
16097
  const bezug = REFERENCE_FIELD[type];
@@ -15752,7 +16127,7 @@ function assessDerivationResponse(value, input) {
15752
16127
  const bezugPointer = bezug ? `/${bezug.field}` : null;
15753
16128
  let gapFehler = false;
15754
16129
  for (const eintrag of benannt) {
15755
- if (!isObject9(eintrag) || typeof eintrag.pointer !== "string" || typeof eintrag.reason !== "string" || !eintrag.reason.trim()) {
16130
+ if (!isObject10(eintrag) || typeof eintrag.pointer !== "string" || typeof eintrag.reason !== "string" || !eintrag.reason.trim()) {
15756
16131
  eigene.push(befund({
15757
16132
  code: "proposal.gap_entry_invalid",
15758
16133
  severity: "error",
@@ -15828,7 +16203,7 @@ function assessDerivationResponse(value, input) {
15828
16203
  });
15829
16204
  } else {
15830
16205
  for (const eintrag of value.not_derivable) {
15831
- if (!isObject9(eintrag) || typeof eintrag.field !== "string" || typeof eintrag.reason !== "string") {
16206
+ if (!isObject10(eintrag) || typeof eintrag.field !== "string" || typeof eintrag.reason !== "string") {
15832
16207
  befund({
15833
16208
  code: "envelope.not_derivable_entry_invalid",
15834
16209
  severity: "error",
@@ -15853,7 +16228,7 @@ function assessDerivationResponse(value, input) {
15853
16228
  });
15854
16229
  } else {
15855
16230
  for (const eintrag of value.conflicts) {
15856
- if (!isObject9(eintrag) || typeof eintrag.field !== "string" || typeof eintrag.from_image !== "string" || typeof eintrag.from_prompt !== "string") {
16231
+ if (!isObject10(eintrag) || typeof eintrag.field !== "string" || typeof eintrag.from_image !== "string" || typeof eintrag.from_prompt !== "string") {
15857
16232
  befund({
15858
16233
  code: "envelope.conflicts_entry_invalid",
15859
16234
  severity: "error",
@@ -15977,7 +16352,7 @@ async function runSceneDerivation(input, port, userPrompt) {
15977
16352
  }
15978
16353
 
15979
16354
  // src/features/szenen/areas/RecoveryPanel.tsx
15980
- var import_react44 = require("react");
16355
+ var import_react45 = require("react");
15981
16356
  var import_jsx_runtime43 = require("react/jsx-runtime");
15982
16357
  var Diagnosen = ({ befunde, testId }) => {
15983
16358
  if (befunde.length === 0) return null;
@@ -16028,11 +16403,11 @@ var Ausbeute = ({ bewertung }) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)
16028
16403
  ] })
16029
16404
  ] });
16030
16405
  var RecoveryPanel = ({ port, lauf }) => {
16031
- const [arbeitskopie, setArbeitskopie] = (0, import_react44.useState)(null);
16032
- const [bewertung, setBewertung] = (0, import_react44.useState)(null);
16033
- const [gesichert, setGesichert] = (0, import_react44.useState)(null);
16034
- const [meldung, setMeldung] = (0, import_react44.useState)(null);
16035
- const [laeuft, setLaeuft] = (0, import_react44.useState)(false);
16406
+ const [arbeitskopie, setArbeitskopie] = (0, import_react45.useState)(null);
16407
+ const [bewertung, setBewertung] = (0, import_react45.useState)(null);
16408
+ const [gesichert, setGesichert] = (0, import_react45.useState)(null);
16409
+ const [meldung, setMeldung] = (0, import_react45.useState)(null);
16410
+ const [laeuft, setLaeuft] = (0, import_react45.useState)(false);
16036
16411
  if (!port.derivation) return null;
16037
16412
  if (typeof lauf.raw_output !== "string" || lauf.raw_output.length === 0) return null;
16038
16413
  const derivation = port.derivation;
@@ -16155,12 +16530,12 @@ var RecoveryPanel = ({ port, lauf }) => {
16155
16530
  };
16156
16531
 
16157
16532
  // src/features/szenen/areas/ChainActions.tsx
16158
- var import_react45 = require("react");
16533
+ var import_react46 = require("react");
16159
16534
  var import_jsx_runtime44 = require("react/jsx-runtime");
16160
16535
  var ChainActions = ({ port, runId, recoveryId }) => {
16161
- const [graph, setGraph] = (0, import_react45.useState)(null);
16162
- const [meldung, setMeldung] = (0, import_react45.useState)(null);
16163
- const [laeuft, setLaeuft] = (0, import_react45.useState)(false);
16536
+ const [graph, setGraph] = (0, import_react46.useState)(null);
16537
+ const [meldung, setMeldung] = (0, import_react46.useState)(null);
16538
+ const [laeuft, setLaeuft] = (0, import_react46.useState)(false);
16164
16539
  if (!port.derivation) return null;
16165
16540
  const derivation = port.derivation;
16166
16541
  const runs = port.runs;
@@ -16226,7 +16601,13 @@ var ChainActions = ({ port, runId, recoveryId }) => {
16226
16601
  ] }),
16227
16602
  shotKnoten && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
16228
16603
  /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { children: 'Die Entw\xFCrfe lassen sich jetzt unter \u201E1 \xB7 Bausteine" \xE4ndern. Der Prompt-Lauf friert den Stand ein, den er dabei vorfindet \u2014 dieselbe Aktion steht auch am Shot selbst, unter \u201E4 \xB7 Prompt".' }),
16229
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ChainPromptAction, { port, shotElementId: shotKnoten.elementId })
16604
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
16605
+ PromptStartAction,
16606
+ {
16607
+ port,
16608
+ source: { kind: "draft_chain", shotElementId: shotKnoten.elementId }
16609
+ }
16610
+ )
16230
16611
  ] }),
16231
16612
  meldung && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { className: "szenen-error", role: "alert", "data-testid": "kette-meldung", children: meldung })
16232
16613
  ] });
@@ -16240,8 +16621,8 @@ var MODUS_TEXT = {
16240
16621
  image_prompt: "Bild und Prompt"
16241
16622
  };
16242
16623
  var BelegFeld = ({ titel, text, testId, offen }) => {
16243
- const [kopiert, setKopiert] = (0, import_react46.useState)(false);
16244
- const feld = (0, import_react46.useRef)(null);
16624
+ const [kopiert, setKopiert] = (0, import_react47.useState)(false);
16625
+ const feld = (0, import_react47.useRef)(null);
16245
16626
  async function kopieren() {
16246
16627
  try {
16247
16628
  await navigator.clipboard.writeText(text);
@@ -16337,7 +16718,7 @@ function readAsBase642(datei) {
16337
16718
  });
16338
16719
  }
16339
16720
  var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
16340
- const [suche, setSuche] = (0, import_react46.useState)("");
16721
+ const [suche, setSuche] = (0, import_react47.useState)("");
16341
16722
  const [bilder] = usePortQuery(
16342
16723
  () => port.imageLibrary.listImages({ search: suche.trim() || void 0, limit: 60 }),
16343
16724
  [port, suche]
@@ -16395,19 +16776,19 @@ async function digestOf(datei) {
16395
16776
  return [...new Uint8Array(hash)].map((b) => b.toString(16).padStart(2, "0")).join("");
16396
16777
  }
16397
16778
  var DeriveArea = ({ port, contextId }) => {
16398
- const [policyVersionId, setPolicyVersionId] = (0, import_react46.useState)("");
16399
- const [modus, setModus] = (0, import_react46.useState)("prompt");
16400
- const [promptText, setPromptText] = (0, import_react46.useState)("");
16401
- const [dateien, setDateien] = (0, import_react46.useState)([]);
16402
- const [bestandsbilder, setBestandsbilder] = (0, import_react46.useState)([]);
16403
- const [laeuft, setLaeuft] = (0, import_react46.useState)(false);
16404
- const [lauf, setLauf] = (0, import_react46.useState)(null);
16405
- const [meldung, setMeldung] = (0, import_react46.useState)(null);
16406
- const [uebernommen, setUebernommen] = (0, import_react46.useState)({});
16407
- const [titel, setTitel] = (0, import_react46.useState)({});
16408
- const [referenzen, setReferenzen] = (0, import_react46.useState)({});
16409
- const [beanstandung, setBeanstandung] = (0, import_react46.useState)(null);
16410
- const [zusatz, setZusatz] = (0, import_react46.useState)("");
16779
+ const [policyVersionId, setPolicyVersionId] = (0, import_react47.useState)("");
16780
+ const [modus, setModus] = (0, import_react47.useState)("prompt");
16781
+ const [promptText, setPromptText] = (0, import_react47.useState)("");
16782
+ const [dateien, setDateien] = (0, import_react47.useState)([]);
16783
+ const [bestandsbilder, setBestandsbilder] = (0, import_react47.useState)([]);
16784
+ const [laeuft, setLaeuft] = (0, import_react47.useState)(false);
16785
+ const [lauf, setLauf] = (0, import_react47.useState)(null);
16786
+ const [meldung, setMeldung] = (0, import_react47.useState)(null);
16787
+ const [uebernommen, setUebernommen] = (0, import_react47.useState)({});
16788
+ const [titel, setTitel] = (0, import_react47.useState)({});
16789
+ const [referenzen, setReferenzen] = (0, import_react47.useState)({});
16790
+ const [beanstandung, setBeanstandung] = (0, import_react47.useState)(null);
16791
+ const [zusatz, setZusatz] = (0, import_react47.useState)("");
16411
16792
  const [regeln] = usePortQuery(
16412
16793
  () => port.derivation.listPolicies(contextId),
16413
16794
  [port, contextId]
@@ -16791,9 +17172,9 @@ var SzenenView = ({
16791
17172
  initialArea,
16792
17173
  onAreaChange
16793
17174
  }) => {
16794
- const bereiche = (0, import_react47.useMemo)(() => areasFor(port), [port]);
16795
- const faehig = (0, import_react47.useMemo)(() => capabilitiesOf(port), [port]);
16796
- const [aktiv, setAktiv] = (0, import_react47.useState)(() => resolveArea(port, initialArea));
17175
+ const bereiche = (0, import_react48.useMemo)(() => areasFor(port), [port]);
17176
+ const faehig = (0, import_react48.useMemo)(() => capabilitiesOf(port), [port]);
17177
+ const [aktiv, setAktiv] = (0, import_react48.useState)(() => resolveArea(port, initialArea));
16797
17178
  const wechsle = (id) => {
16798
17179
  setAktiv(id);
16799
17180
  onAreaChange?.(id);
@@ -16824,7 +17205,16 @@ var SzenenView = ({
16824
17205
  )) }),
16825
17206
  /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("section", { className: "szenen-view__area", "aria-label": aktuellerBereich.title, children: [
16826
17207
  aktiv === "derive" && port.derivation && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(DeriveArea, { port, contextId }),
16827
- aktiv === "elements" && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ElementsArea, { port, contextId }),
17208
+ aktiv === "elements" && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17209
+ ElementsArea,
17210
+ {
17211
+ port,
17212
+ contextId,
17213
+ restrictToTypes: BAUSTEIN_TYPES,
17214
+ allTypesLabel: "alle Bausteine",
17215
+ listTitle: "Bausteine"
17216
+ }
17217
+ ),
16828
17218
  aktiv === "scene" && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(SceneArea, { port, contextId }),
16829
17219
  aktiv === "setup" && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(SetupArea, { port, contextId }),
16830
17220
  aktiv === "shot" && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ShotArea, { port, contextId }),
@@ -17602,7 +17992,7 @@ var CHAIN_EDGES = {
17602
17992
  scene_ref: { from: "scene_setup", to: "scene" },
17603
17993
  scene_setup_ref: { from: "shot", to: "scene_setup" }
17604
17994
  };
17605
- function istText2(wert) {
17995
+ function istText3(wert) {
17606
17996
  return typeof wert === "string" && wert.trim().length > 0;
17607
17997
  }
17608
17998
  function checkDraftChainMeta(meta, type) {
@@ -17613,14 +18003,14 @@ function checkDraftChainMeta(meta, type) {
17613
18003
  };
17614
18004
  }
17615
18005
  const m = meta;
17616
- if (!istText2(m.chain_id)) return { ok: false, message: '"chain_id" fehlt oder ist leer.' };
17617
- if (!istText2(m.source_run_id)) {
18006
+ if (!istText3(m.chain_id)) return { ok: false, message: '"chain_id" fehlt oder ist leer.' };
18007
+ if (!istText3(m.source_run_id)) {
17618
18008
  return {
17619
18009
  ok: false,
17620
18010
  message: '"source_run_id" fehlt oder ist leer \u2014 ohne sie ist die Herkunft eines Kettenknotens nicht rekonstruierbar (D-96/B3).'
17621
18011
  };
17622
18012
  }
17623
- if (m.source_recovery_id !== void 0 && !istText2(m.source_recovery_id)) {
18013
+ if (m.source_recovery_id !== void 0 && !istText3(m.source_recovery_id)) {
17624
18014
  return { ok: false, message: '"source_recovery_id" ist gesetzt, aber leer.' };
17625
18015
  }
17626
18016
  if (!Array.isArray(m.links)) {
@@ -17645,7 +18035,7 @@ function checkDraftChainMeta(meta, type) {
17645
18035
  message: `"${l.field}" gehoert an einen ${kante.from}, nicht an einen ${type}.`
17646
18036
  };
17647
18037
  }
17648
- if (!istText2(l.target_element_id) || !istText2(l.target_draft_id)) {
18038
+ if (!istText3(l.target_element_id) || !istText3(l.target_draft_id)) {
17649
18039
  return {
17650
18040
  ok: false,
17651
18041
  message: `"links[${index}]" braucht target_element_id und target_draft_id.`
@@ -18292,12 +18682,12 @@ function createHfEventChannel(options) {
18292
18682
  // src/lib/szenen/resolve.ts
18293
18683
  var ResolveTechnicalError = class extends Error {
18294
18684
  };
18295
- var isObject10 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
18296
- var asArray2 = (v) => Array.isArray(v) ? v : [];
18685
+ var isObject11 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
18686
+ var asArray3 = (v) => Array.isArray(v) ? v : [];
18297
18687
  var str = (v) => typeof v === "string" ? v : null;
18298
18688
  var num = (v) => typeof v === "number" ? v : null;
18299
18689
  async function refOf(resolver, ref, expected, path) {
18300
- const versionId = isObject10(ref) ? str(ref.version_id) : null;
18690
+ const versionId = isObject11(ref) ? str(ref.version_id) : null;
18301
18691
  if (!versionId) {
18302
18692
  return {
18303
18693
  ok: false,
@@ -18349,8 +18739,11 @@ async function refOf(resolver, ref, expected, path) {
18349
18739
  }
18350
18740
  return { ok: true, version: version2 };
18351
18741
  }
18742
+ function ausgelassen(diagnostics, code, path, message) {
18743
+ diagnostics.push({ code, origin: "resolution", path, message });
18744
+ }
18352
18745
  function entraeumlichtePlatzierung(placement, assumptions) {
18353
- if (!isObject10(placement)) return null;
18746
+ if (!isObject11(placement)) return null;
18354
18747
  if (placement.mode !== "pattern") {
18355
18748
  assumptions.push(
18356
18749
  `Die Kameraplatzierung (mode "${str(placement.mode) ?? "unbekannt"}") ist metrisch und entfaellt ohne can_place. Der Renderer darf daraus keine Position ableiten.`
@@ -18365,7 +18758,7 @@ function entraeumlichtePlatzierung(placement, assumptions) {
18365
18758
  const entfallen = [];
18366
18759
  if (placement.offsets !== void 0) entfallen.push("offsets");
18367
18760
  const height = placement.height;
18368
- if (isObject10(height)) {
18761
+ if (isObject11(height)) {
18369
18762
  if (height.mode === "anchor") {
18370
18763
  const behalteneHoehe = {
18371
18764
  mode: "anchor",
@@ -18444,142 +18837,328 @@ async function resolveShotPayload(input) {
18444
18837
  chain
18445
18838
  } = input;
18446
18839
  const assumptions = [];
18447
- const shotVersion = {
18448
- version_id: shotVersionId,
18449
- element_id: shotElementId,
18450
- payload: shotPayload
18451
- };
18452
- const shotErrors = ohneKettenReferenz(
18453
- validatePayload("shot", shotVersion.payload).errors,
18454
- chain ? "scene_setup_ref" : null
18455
- );
18456
- if (shotErrors.length > 0) {
18840
+ const diagnostics = [];
18841
+ if (!isObject11(shotPayload)) {
18457
18842
  return {
18458
18843
  kind: "rejected",
18459
18844
  failure: {
18460
18845
  class: "schema",
18461
18846
  where: "shot",
18462
- message: `Die Shot-Payload verletzt das Schema (${shotErrors.length} Befunde). Es wurde keine ihrer Referenzen aufgeloest.`,
18463
- errors: shotErrors
18847
+ message: `Die Shot-Payload ist kein Objekt, sondern ${Array.isArray(shotPayload) ? "ein Array" : typeof shotPayload}. Daraus laesst sich kein Auftrag herstellen.`,
18848
+ errors: []
18849
+ }
18850
+ };
18851
+ }
18852
+ const shot = shotPayload;
18853
+ for (const fehler4 of ohneKettenReferenz(
18854
+ validatePayload("shot", shotPayload).errors,
18855
+ chain ? "scene_setup_ref" : null
18856
+ )) {
18857
+ diagnostics.push(diagnosticOfSchemaError(fehler4, `Shot ${shotElementId}`));
18858
+ }
18859
+ let setup = null;
18860
+ let report = null;
18861
+ let canPlace = false;
18862
+ let capabilities;
18863
+ let scene;
18864
+ const nenntSetup = chain !== void 0 || shot.scene_setup_ref !== void 0;
18865
+ if (!nenntSetup) {
18866
+ return {
18867
+ kind: "rejected",
18868
+ failure: {
18869
+ class: "no_content",
18870
+ message: `Der Shot ${shotElementId} nennt kein scene_setup_ref und fuehrt keine Draft-Kette. Ohne Setup gibt es keinen Weg zu einer Szene und damit keinen beschreibbaren Inhalt.`
18464
18871
  }
18465
18872
  };
18466
18873
  }
18467
- const shot = shotVersion.payload;
18468
18874
  const setupRef = chain ? { ok: true, version: chain.setup } : await refOf(resolver, shot.scene_setup_ref, "scene_setup", "/scene_setup_ref");
18469
18875
  if (!setupRef.ok) return { kind: "rejected", failure: setupRef.failure };
18470
- const setupErrors = ohneKettenReferenz(
18471
- validatePayload("scene_setup", setupRef.version.payload).errors,
18472
- chain ? "scene_ref" : null
18473
- );
18474
- if (setupErrors.length > 0) {
18876
+ if (!isObject11(setupRef.version.payload)) {
18475
18877
  return {
18476
18878
  kind: "rejected",
18477
18879
  failure: {
18478
18880
  class: "schema",
18479
18881
  where: "scene_setup",
18480
- message: `Die Scene-Setup-Payload verletzt das Schema (${setupErrors.length} Befunde). Weder ihre Referenzen noch der Faehigkeitsbericht wurden mit einem Resolver gefahren.`,
18481
- errors: setupErrors
18882
+ message: `Die Scene-Setup-Payload ist kein Objekt, sondern ${Array.isArray(setupRef.version.payload) ? "ein Array" : typeof setupRef.version.payload}.`,
18883
+ errors: []
18482
18884
  }
18483
18885
  };
18484
18886
  }
18485
- const setup = setupRef.version.payload;
18486
- const report = await capabilityReport({
18887
+ setup = {
18888
+ element_id: setupRef.version.element_id,
18889
+ version_id: setupRef.version.version_id,
18890
+ payload: setupRef.version.payload
18891
+ };
18892
+ report = await capabilityReport({
18487
18893
  type: "scene_setup",
18488
- setupPayload: setup,
18489
- elementId: setupRef.version.element_id,
18894
+ setupPayload: setup.payload,
18895
+ elementId: setup.element_id,
18490
18896
  resolver,
18491
18897
  // Bei einer Draft-Kette kommt die Szene von hier, nicht aus `scene_ref`
18492
18898
  // (C1). Dasselbe Regelwerk, ein anderer Weg zur selben Szene.
18493
18899
  ...chain ? { chainScene: chain.scene.payload } : {}
18494
18900
  });
18495
- const gate = capabilityGate(report, "can_render_prompt");
18496
- if (gate) return { kind: "rejected", failure: gate };
18497
- const placeGate = capabilityGate(report, "can_place");
18498
- if (placeGate && placeGate.class === "capability_undetermined") {
18499
- return { kind: "rejected", failure: placeGate };
18500
- }
18501
- const canPlace = report.can_place.state === "yes";
18901
+ diagnostics.push(...report.promptDiagnostics);
18902
+ canPlace = report.can_place.state === "yes";
18502
18903
  if (!canPlace) {
18503
18904
  assumptions.push(
18504
- "can_place ist nicht erfuellt \u2014 alle Platzierungen bleiben null. Der Renderer darf daraus keine Koordinaten ableiten (D-47)."
18905
+ `can_place ist "${report.can_place.state}" \u2014 alle Platzierungen bleiben null. Der Renderer darf daraus keine Koordinaten ableiten (D-47).`
18505
18906
  );
18506
18907
  }
18507
- const sceneRef = chain ? { ok: true, version: chain.scene } : await refOf(resolver, setup.scene_ref, "scene", "/scene_ref");
18908
+ capabilities = {
18909
+ can_describe_scene: report.can_describe_scene.state,
18910
+ can_render_prompt: report.can_render_prompt.state,
18911
+ can_place: report.can_place.state
18912
+ };
18913
+ const sceneRef = chain ? { ok: true, version: chain.scene } : await refOf(resolver, setup.payload.scene_ref, "scene", "/scene_ref");
18508
18914
  if (!sceneRef.ok) return { kind: "rejected", failure: sceneRef.failure };
18509
- const scene = sceneRef.version.payload;
18510
- const cameraId = str(shot.camera_id) ?? "";
18511
- const rig = isObject10(setup.camera_rig) ? setup.camera_rig : {};
18512
- const cameras = asArray2(rig.cameras);
18513
- const cameraIndex = cameras.findIndex(
18514
- (camera2) => isObject10(camera2) && camera2.camera_id === cameraId
18515
- );
18516
- if (cameraIndex < 0) {
18915
+ if (!isObject11(sceneRef.version.payload)) {
18517
18916
  return {
18518
18917
  kind: "rejected",
18519
18918
  failure: {
18520
- class: "reference_unresolvable",
18521
- message: `Die aktive camera_id "${cameraId}" kommt im Rig der Scene-Setup-Version ${setupRef.version.version_id} nicht vor (P-01).`,
18522
- versionId: setupRef.version.version_id,
18523
- path: "/camera_id",
18524
- reason: "unknown"
18919
+ class: "schema",
18920
+ where: "scene",
18921
+ message: `Die Szenen-Payload ist kein Objekt, sondern ${Array.isArray(sceneRef.version.payload) ? "ein Array" : typeof sceneRef.version.payload}.`,
18922
+ errors: []
18525
18923
  }
18526
18924
  };
18527
18925
  }
18528
- const camera = cameras[cameraIndex];
18529
- const presetRef = await refOf(
18926
+ scene = {
18927
+ element_id: sceneRef.version.element_id,
18928
+ version_id: sceneRef.version.version_id,
18929
+ payload: sceneRef.version.payload
18930
+ };
18931
+ return komponiere({
18932
+ shot: { element_id: shotElementId, version_id: shotVersionId, payload: shot },
18933
+ setup,
18934
+ scene,
18935
+ report,
18936
+ capabilities,
18937
+ canPlace,
18530
18938
  resolver,
18531
- camera.preset_ref,
18532
- "camera_preset",
18533
- `/camera_rig/cameras/${cameraIndex}/preset_ref`
18534
- );
18535
- if (!presetRef.ok) return { kind: "rejected", failure: presetRef.failure };
18536
- let pattern = null;
18537
- const placementSpec = isObject10(camera.placement) ? camera.placement : null;
18538
- if (placementSpec && isObject10(placementSpec.pattern_ref)) {
18539
- const patternRef = await refOf(
18540
- resolver,
18541
- placementSpec.pattern_ref,
18542
- "camera_pattern",
18543
- `/camera_rig/cameras/${cameraIndex}/placement/pattern_ref`
18939
+ contextChain,
18940
+ userInstruction,
18941
+ assumptions,
18942
+ diagnostics
18943
+ });
18944
+ }
18945
+ async function resolveScenePayload(input) {
18946
+ const {
18947
+ scenePayload,
18948
+ sceneElementId,
18949
+ sceneVersionId = "",
18950
+ resolver,
18951
+ contextChain,
18952
+ userInstruction
18953
+ } = input;
18954
+ if (!isObject11(scenePayload)) {
18955
+ return {
18956
+ kind: "rejected",
18957
+ failure: {
18958
+ class: "schema",
18959
+ where: "scene",
18960
+ message: `Die Szenen-Payload ist kein Objekt, sondern ${Array.isArray(scenePayload) ? "ein Array" : typeof scenePayload}.`,
18961
+ errors: []
18962
+ }
18963
+ };
18964
+ }
18965
+ const assumptions = [
18966
+ "Kein Scene Setup, kein Shot: Der Lauf entsteht aus der Szene allein. Bildaufbau, Kamera und Zielmodell wurden nicht ergaenzt, sondern sind nicht vorhanden (D-135)."
18967
+ ];
18968
+ const diagnostics = [];
18969
+ for (const fehler4 of validatePayload("scene", scenePayload).errors) {
18970
+ diagnostics.push(diagnosticOfSchemaError(fehler4, `Szene ${sceneElementId}`));
18971
+ }
18972
+ const faehig = sceneOnlyCapabilities(scenePayload);
18973
+ return komponiere({
18974
+ shot: null,
18975
+ setup: null,
18976
+ scene: { element_id: sceneElementId, version_id: sceneVersionId, payload: scenePayload },
18977
+ report: null,
18978
+ capabilities: {
18979
+ can_describe_scene: faehig.can_describe_scene.state,
18980
+ can_render_prompt: faehig.can_render_prompt.state,
18981
+ can_place: faehig.can_place.state
18982
+ },
18983
+ canPlace: false,
18984
+ resolver,
18985
+ contextChain,
18986
+ userInstruction,
18987
+ assumptions,
18988
+ diagnostics
18989
+ });
18990
+ }
18991
+ async function komponiere(z) {
18992
+ const {
18993
+ shot,
18994
+ setup,
18995
+ scene,
18996
+ report,
18997
+ capabilities,
18998
+ canPlace,
18999
+ resolver,
19000
+ contextChain,
19001
+ userInstruction,
19002
+ assumptions,
19003
+ diagnostics
19004
+ } = z;
19005
+ const inhalt = describableContent(scene.payload);
19006
+ if (!inhalt.ok) {
19007
+ return {
19008
+ kind: "rejected",
19009
+ failure: {
19010
+ class: "no_content",
19011
+ message: `Szene ${scene.element_id}: ${inhalt.message}`
19012
+ }
19013
+ };
19014
+ }
19015
+ const eingefroreneFaehigkeiten = {
19016
+ ...capabilities,
19017
+ can_render_prompt: "yes"
19018
+ };
19019
+ const shotOverrides = shot && isObject11(shot.payload.overrides) ? shot.payload.overrides : {};
19020
+ const settings = shot && isObject11(shot.payload.prompt_render_settings) ? shot.payload.prompt_render_settings : {};
19021
+ let camera = null;
19022
+ if (shot && setup) {
19023
+ const cameraId = str(shot.payload.camera_id);
19024
+ const rig = isObject11(setup.payload.camera_rig) ? setup.payload.camera_rig : {};
19025
+ const cameras = asArray3(rig.cameras);
19026
+ const cameraIndex = cameraId === null ? -1 : cameras.findIndex((c) => isObject11(c) && c.camera_id === cameraId);
19027
+ if (cameraId === null) {
19028
+ ausgelassen(
19029
+ diagnostics,
19030
+ "P-01",
19031
+ "/camera_id",
19032
+ `Der Shot ${shot.element_id} nennt keine camera_id. Der Prompt entsteht ohne Kameravorgabe; eine Kamera wird nicht erfunden (D-75).`
19033
+ );
19034
+ } else if (cameraIndex < 0) {
19035
+ ausgelassen(
19036
+ diagnostics,
19037
+ "P-01",
19038
+ "/camera_id",
19039
+ `Die aktive camera_id "${cameraId}" kommt im Rig der Scene-Setup-Version ${setup.version_id} nicht vor. Der Prompt entsteht ohne Kameravorgabe.`
19040
+ );
19041
+ } else {
19042
+ const rohKamera = cameras[cameraIndex];
19043
+ let preset = null;
19044
+ const presetRef = await refOf(
19045
+ resolver,
19046
+ rohKamera.preset_ref,
19047
+ "camera_preset",
19048
+ `/camera_rig/cameras/${cameraIndex}/preset_ref`
19049
+ );
19050
+ if (presetRef.ok) preset = presetRef.version.payload;
19051
+ else {
19052
+ ausgelassen(
19053
+ diagnostics,
19054
+ "P-04",
19055
+ `/camera_rig/cameras/${cameraIndex}/preset_ref`,
19056
+ `${presetRef.failure.message} Die Kamera geht ohne Preset in den Prompt.`
19057
+ );
19058
+ }
19059
+ let pattern = null;
19060
+ const placementSpec = isObject11(rohKamera.placement) ? rohKamera.placement : null;
19061
+ if (placementSpec && isObject11(placementSpec.pattern_ref)) {
19062
+ const patternRef = await refOf(
19063
+ resolver,
19064
+ placementSpec.pattern_ref,
19065
+ "camera_pattern",
19066
+ `/camera_rig/cameras/${cameraIndex}/placement/pattern_ref`
19067
+ );
19068
+ if (patternRef.ok) pattern = patternRef.version.payload;
19069
+ else {
19070
+ ausgelassen(
19071
+ diagnostics,
19072
+ "P-04",
19073
+ `/camera_rig/cameras/${cameraIndex}/placement/pattern_ref`,
19074
+ `${patternRef.failure.message} Das Kameramuster entfaellt im Prompt.`
19075
+ );
19076
+ }
19077
+ }
19078
+ const cameraPlacement = canPlace ? rohKamera.placement ?? null : entraeumlichtePlatzierung(rohKamera.placement, assumptions);
19079
+ camera = {
19080
+ camera_id: cameraId,
19081
+ name: str(rohKamera.name),
19082
+ preset,
19083
+ placement: cameraPlacement,
19084
+ orientation: rohKamera.orientation ?? null,
19085
+ // Der Shot darf Bildgroesse und Fokus ueberschreiben (D-40) — das ist
19086
+ // die oberste Stufe der Vorrangkette.
19087
+ framing: shotOverrides.framing ?? rohKamera.framing ?? null,
19088
+ focus: shotOverrides.focus !== void 0 ? shotOverrides.focus : rohKamera.focus ?? null,
19089
+ pattern
19090
+ };
19091
+ }
19092
+ } else if (shot && !setup) {
19093
+ ausgelassen(
19094
+ diagnostics,
19095
+ "P-01",
19096
+ "/camera_id",
19097
+ "Ohne Scene Setup gibt es kein Camera Rig. Der Prompt entsteht ohne Kameravorgabe."
18544
19098
  );
18545
- if (!patternRef.ok) return { kind: "rejected", failure: patternRef.failure };
18546
- pattern = patternRef.version.payload;
18547
19099
  }
18548
19100
  let shotConcept = null;
18549
- if (isObject10(shot.shot_concept_ref)) {
19101
+ if (shot && isObject11(shot.payload.shot_concept_ref)) {
18550
19102
  const conceptRef = await refOf(
18551
19103
  resolver,
18552
- shot.shot_concept_ref,
19104
+ shot.payload.shot_concept_ref,
18553
19105
  "shot_concept",
18554
19106
  "/shot_concept_ref"
18555
19107
  );
18556
- if (!conceptRef.ok) return { kind: "rejected", failure: conceptRef.failure };
18557
- shotConcept = conceptRef.version.payload;
19108
+ if (conceptRef.ok) shotConcept = conceptRef.version.payload;
19109
+ else {
19110
+ ausgelassen(
19111
+ diagnostics,
19112
+ "P-04",
19113
+ "/shot_concept_ref",
19114
+ `${conceptRef.failure.message} Das Shot Concept entfaellt im Prompt.`
19115
+ );
19116
+ }
18558
19117
  }
18559
19118
  const styles = [];
18560
19119
  for (const [ebene, refs] of [
18561
- ["scene_setup", asArray2(setup.style_refs)],
18562
- ["shot", asArray2(shot.style_refs)]
19120
+ ["scene_setup", setup ? asArray3(setup.payload.style_refs) : []],
19121
+ ["shot", shot ? asArray3(shot.payload.style_refs) : []]
18563
19122
  ]) {
18564
19123
  for (const [i, ref] of refs.entries()) {
18565
19124
  const resolved = await refOf(resolver, ref, "style", `/${ebene}/style_refs/${i}`);
18566
- if (!resolved.ok) return { kind: "rejected", failure: resolved.failure };
19125
+ if (!resolved.ok) {
19126
+ ausgelassen(
19127
+ diagnostics,
19128
+ "P-04",
19129
+ `/${ebene}/style_refs/${i}`,
19130
+ `${resolved.failure.message} Dieser Stil entfaellt im Prompt.`
19131
+ );
19132
+ continue;
19133
+ }
18567
19134
  const payload = resolved.version.payload;
18568
19135
  styles.push({
18569
- role: str(isObject10(ref) ? ref.role : null) ?? str(payload.role) ?? "unspecified",
18570
- rank: num(isObject10(ref) ? ref.rank : null) ?? 0,
18571
- weight: num(isObject10(ref) ? ref.weight : null),
19136
+ role: str(isObject11(ref) ? ref.role : null) ?? str(payload.role) ?? "unspecified",
19137
+ rank: num(isObject11(ref) ? ref.rank : null) ?? 0,
19138
+ weight: num(isObject11(ref) ? ref.weight : null),
18572
19139
  text: str(payload.text) ?? "",
18573
19140
  locale: str(payload.locale)
18574
19141
  });
18575
19142
  }
18576
19143
  }
18577
19144
  styles.sort((a, b) => a.rank - b.rank);
18578
- const settings = isObject10(shot.prompt_render_settings) ? shot.prompt_render_settings : {};
18579
19145
  const policies = [];
18580
- for (const [i, ref] of asArray2(settings.policy_refs).entries()) {
18581
- const resolved = await refOf(resolver, ref, "prompt_policy", `/prompt_render_settings/policy_refs/${i}`);
18582
- if (!resolved.ok) return { kind: "rejected", failure: resolved.failure };
19146
+ for (const [i, ref] of asArray3(settings.policy_refs).entries()) {
19147
+ const resolved = await refOf(
19148
+ resolver,
19149
+ ref,
19150
+ "prompt_policy",
19151
+ `/prompt_render_settings/policy_refs/${i}`
19152
+ );
19153
+ if (!resolved.ok) {
19154
+ ausgelassen(
19155
+ diagnostics,
19156
+ "P-04",
19157
+ `/prompt_render_settings/policy_refs/${i}`,
19158
+ `${resolved.failure.message} Diese Policy entfaellt im Prompt.`
19159
+ );
19160
+ continue;
19161
+ }
18583
19162
  const payload = resolved.version.payload;
18584
19163
  policies.push({
18585
19164
  subtype: str(payload.subtype) ?? "general",
@@ -18590,14 +19169,22 @@ async function resolveShotPayload(input) {
18590
19169
  }
18591
19170
  policies.sort((a, b) => a.rank - b.rank);
18592
19171
  const fragments = [];
18593
- for (const [i, ref] of asArray2(settings.fragment_refs).entries()) {
19172
+ for (const [i, ref] of asArray3(settings.fragment_refs).entries()) {
18594
19173
  const resolved = await refOf(
18595
19174
  resolver,
18596
19175
  ref,
18597
19176
  "prompt_fragment",
18598
19177
  `/prompt_render_settings/fragment_refs/${i}`
18599
19178
  );
18600
- if (!resolved.ok) return { kind: "rejected", failure: resolved.failure };
19179
+ if (!resolved.ok) {
19180
+ ausgelassen(
19181
+ diagnostics,
19182
+ "P-04",
19183
+ `/prompt_render_settings/fragment_refs/${i}`,
19184
+ `${resolved.failure.message} Dieses Fragment entfaellt im Prompt.`
19185
+ );
19186
+ continue;
19187
+ }
18601
19188
  const payload = resolved.version.payload;
18602
19189
  fragments.push({
18603
19190
  channel: str(payload.channel) ?? "positive",
@@ -18607,83 +19194,102 @@ async function resolveShotPayload(input) {
18607
19194
  });
18608
19195
  }
18609
19196
  fragments.sort((a, b) => a.rank - b.rank);
18610
- const key = str(settings.target_model_id);
18611
- if (!key) {
18612
- return {
18613
- kind: "rejected",
18614
- failure: {
18615
- class: "target_model_unresolvable",
18616
- message: "Der Shot nennt kein target_model_id.",
18617
- resolution: { kind: "not_found", key: "", chain: contextChain }
18618
- }
18619
- };
18620
- }
18621
- if (!resolver.resolveTargetModel) {
19197
+ let targetModelPayload = null;
19198
+ let targetModelResolution = null;
19199
+ const key = shot ? str(settings.target_model_id) : null;
19200
+ if (key === null) {
19201
+ ausgelassen(
19202
+ diagnostics,
19203
+ "P-17",
19204
+ "/prompt_render_settings/target_model_id",
19205
+ shot ? `Der Shot ${shot.element_id} nennt kein target_model_id. Der Prompt wird generisch abgeleitet, ohne Modelloptimierung.` : "Ohne Shot gibt es kein target_model_id. Der Prompt wird generisch abgeleitet, ohne Modelloptimierung."
19206
+ );
19207
+ } else if (!resolver.resolveTargetModel) {
18622
19208
  throw new ResolveTechnicalError(
18623
19209
  `Der Resolver bietet keine Zielmodellsuche an (kein ContextChainPort). Ein Lauf ohne Kontextkette wuerde ein Zielmodell melden, das nicht das gesuchte ist.`
18624
19210
  );
18625
- }
18626
- const targetModel = await resolver.resolveTargetModel(key, shotVersion.element_id);
18627
- if (targetModel.kind !== "resolved") {
18628
- return {
18629
- kind: "rejected",
18630
- failure: {
18631
- class: "target_model_unresolvable",
18632
- message: describeTargetModelFailure(targetModel),
18633
- resolution: targetModel
19211
+ } else {
19212
+ const targetModel = await resolver.resolveTargetModel(key, shot.element_id);
19213
+ if (targetModel.kind !== "resolved") {
19214
+ ausgelassen(
19215
+ diagnostics,
19216
+ "P-17",
19217
+ "/prompt_render_settings/target_model_id",
19218
+ `${describeTargetModelFailure(targetModel)} Der Prompt wird generisch abgeleitet, ohne Modelloptimierung.`
19219
+ );
19220
+ } else {
19221
+ targetModelPayload = targetModel.payload;
19222
+ targetModelResolution = {
19223
+ element_id: targetModel.elementId,
19224
+ version_id: targetModel.versionId,
19225
+ chain_index: targetModel.chainIndex,
19226
+ head: targetModel.head,
19227
+ shadowed_retired: targetModel.shadowedRetired.map((entry) => ({
19228
+ element_id: entry.elementId,
19229
+ context_id: entry.contextId
19230
+ }))
19231
+ };
19232
+ for (const uebergangen of targetModel.shadowedRetired) {
19233
+ assumptions.push(
19234
+ `Ein stillgelegtes target_model (${uebergangen.elementId}, Kontext ${uebergangen.contextId ?? "global"}) wurde uebergangen; verwendet wird ${targetModel.elementId} von Kettenglied ${targetModel.chainIndex}.`
19235
+ );
18634
19236
  }
18635
- };
18636
- }
18637
- for (const uebergangen of targetModel.shadowedRetired) {
18638
- assumptions.push(
18639
- `Ein stillgelegtes target_model (${uebergangen.elementId}, Kontext ${uebergangen.contextId ?? "global"}) wurde uebergangen; verwendet wird ${targetModel.elementId} von Kettenglied ${targetModel.chainIndex}.`
18640
- );
18641
- }
18642
- if (targetModel.head.kind === "selected" && targetModel.head.stale) {
18643
- assumptions.push(
18644
- `Die Auswahl des Zielmodell-Heads (${targetModel.versionId}) ist veraltet \u2014 es gibt neuere Heads. Die Auswahl gilt, weil sie getroffen wurde (D-36).`
18645
- );
19237
+ if (targetModel.head.kind === "selected" && targetModel.head.stale) {
19238
+ assumptions.push(
19239
+ `Die Auswahl des Zielmodell-Heads (${targetModel.versionId}) ist veraltet \u2014 es gibt neuere Heads. Die Auswahl gilt, weil sie getroffen wurde (D-36).`
19240
+ );
19241
+ }
19242
+ }
18646
19243
  }
18647
19244
  const overrides = /* @__PURE__ */ new Map();
18648
- for (const entry of asArray2(setup.node_overrides)) {
18649
- if (isObject10(entry) && typeof entry.node_id === "string") overrides.set(entry.node_id, entry);
19245
+ for (const entry of setup ? asArray3(setup.payload.node_overrides) : []) {
19246
+ if (isObject11(entry) && typeof entry.node_id === "string") overrides.set(entry.node_id, entry);
18650
19247
  }
18651
19248
  const placements = /* @__PURE__ */ new Map();
18652
- for (const entry of asArray2(setup.placements)) {
18653
- if (!isObject10(entry) || typeof entry.node_id !== "string") continue;
19249
+ for (const entry of setup ? asArray3(setup.payload.placements) : []) {
19250
+ if (!isObject11(entry) || typeof entry.node_id !== "string") continue;
18654
19251
  placements.set(entry.node_id, {
18655
- position: asArray2(entry.position),
18656
- ...entry.rotation_euler_deg ? { rotation_euler_deg: asArray2(entry.rotation_euler_deg) } : {},
18657
- ...entry.scale ? { scale: asArray2(entry.scale) } : {}
19252
+ position: asArray3(entry.position),
19253
+ ...entry.rotation_euler_deg ? { rotation_euler_deg: asArray3(entry.rotation_euler_deg) } : {},
19254
+ ...entry.scale ? { scale: asArray3(entry.scale) } : {}
18658
19255
  });
18659
19256
  }
18660
- const shotOverrides = isObject10(shot.overrides) ? shot.overrides : {};
18661
- const hidden = new Set(asArray2(shotOverrides.hidden_nodes).filter((v) => typeof v === "string"));
19257
+ const hidden = new Set(
19258
+ asArray3(shotOverrides.hidden_nodes).filter((v) => typeof v === "string")
19259
+ );
18662
19260
  const nodes = [];
18663
- for (const raw of asArray2(scene.nodes)) {
18664
- if (!isObject10(raw) || typeof raw.node_id !== "string") continue;
19261
+ for (const raw of asArray3(scene.payload.nodes)) {
19262
+ if (!isObject11(raw) || typeof raw.node_id !== "string") continue;
18665
19263
  const nodeId = raw.node_id;
18666
19264
  const contributions = [];
18667
- for (const [i, ref] of asArray2(raw.component_refs).entries()) {
19265
+ for (const [i, ref] of asArray3(raw.component_refs).entries()) {
18668
19266
  const resolved = await refOf(
18669
19267
  resolver,
18670
19268
  ref,
18671
19269
  "scene_component",
18672
19270
  `/nodes/${nodeId}/component_refs/${i}`
18673
19271
  );
18674
- if (!resolved.ok) return { kind: "rejected", failure: resolved.failure };
19272
+ if (!resolved.ok) {
19273
+ ausgelassen(
19274
+ diagnostics,
19275
+ "P-05",
19276
+ `/nodes/${nodeId}/component_refs/${i}`,
19277
+ `${resolved.failure.message} Dieser Beitrag entfaellt; der Knoten bleibt mit seinen uebrigen Beitraegen im Prompt.`
19278
+ );
19279
+ continue;
19280
+ }
18675
19281
  const payload = resolved.version.payload;
18676
19282
  contributions.push({
18677
19283
  source: "component",
18678
19284
  element_id: resolved.version.element_id,
18679
19285
  version_id: resolved.version.version_id,
18680
- role: str(isObject10(ref) ? ref.role : null),
18681
- rank: num(isObject10(ref) ? ref.rank : null) ?? 0,
19286
+ role: str(isObject11(ref) ? ref.role : null),
19287
+ rank: num(isObject11(ref) ? ref.rank : null) ?? 0,
18682
19288
  locale: str(payload.locale),
18683
19289
  text: str(payload.text) ?? ""
18684
19290
  });
18685
19291
  }
18686
- const semantic = isObject10(raw.semantic) ? raw.semantic : null;
19292
+ const semantic = isObject11(raw.semantic) ? raw.semantic : null;
18687
19293
  if (semantic && str(semantic.description)) {
18688
19294
  contributions.push({
18689
19295
  source: "node_semantic",
@@ -18697,12 +19303,12 @@ async function resolveShotPayload(input) {
18697
19303
  });
18698
19304
  }
18699
19305
  const override = overrides.get(nodeId);
18700
- const overrideSemantic = override && isObject10(override.semantic) ? override.semantic : null;
18701
- if (overrideSemantic && str(overrideSemantic.description)) {
19306
+ const overrideSemantic = override && isObject11(override.semantic) ? override.semantic : null;
19307
+ if (overrideSemantic && str(overrideSemantic.description) && setup) {
18702
19308
  contributions.push({
18703
19309
  source: "setup_override",
18704
- element_id: setupRef.version.element_id,
18705
- version_id: setupRef.version.version_id,
19310
+ element_id: setup.element_id,
19311
+ version_id: setup.version_id,
18706
19312
  role: null,
18707
19313
  rank: Number.MAX_SAFE_INTEGER,
18708
19314
  locale: null,
@@ -18711,8 +19317,8 @@ async function resolveShotPayload(input) {
18711
19317
  }
18712
19318
  contributions.sort((a, b) => a.rank - b.rank);
18713
19319
  const properties = {
18714
- ...isObject10(semantic?.properties) ? semantic.properties : {},
18715
- ...isObject10(overrideSemantic?.properties) ? overrideSemantic.properties : {}
19320
+ ...isObject11(semantic?.properties) ? semantic.properties : {},
19321
+ ...isObject11(overrideSemantic?.properties) ? overrideSemantic.properties : {}
18716
19322
  };
18717
19323
  nodes.push({
18718
19324
  node_id: nodeId,
@@ -18723,9 +19329,6 @@ async function resolveShotPayload(input) {
18723
19329
  hidden: hidden.has(nodeId)
18724
19330
  });
18725
19331
  }
18726
- const cameraPlacement = canPlace ? camera.placement ?? null : entraeumlichtePlatzierung(camera.placement, assumptions);
18727
- const framing = shotOverrides.framing ?? camera.framing;
18728
- const focus = shotOverrides.focus !== void 0 ? shotOverrides.focus : camera.focus ?? null;
18729
19332
  const shotInstruction = str(settings.user_instruction);
18730
19333
  if (userInstruction && shotInstruction && userInstruction !== shotInstruction) {
18731
19334
  assumptions.push(
@@ -18739,92 +19342,40 @@ async function resolveShotPayload(input) {
18739
19342
  kind: "resolved",
18740
19343
  report,
18741
19344
  input: {
18742
- shot: {
18743
- version_id: shotVersion.version_id,
18744
- element_id: shotVersion.element_id,
18745
- camera_id: cameraId,
19345
+ shot: shot ? {
19346
+ version_id: shot.version_id,
19347
+ element_id: shot.element_id,
19348
+ camera_id: camera ? camera.camera_id : null,
18746
19349
  shot_concept: shotConcept,
18747
19350
  // Der Aufrufer darf die im Shot hinterlegte Anweisung ueberschreiben
18748
19351
  // (§4.1). Beide Herkuenfte sind unterschiedliche Sachverhalte, deshalb
18749
19352
  // wird der Fall in `assumptions` benannt statt still ersetzt.
18750
- user_instruction: userInstruction ?? str(settings.user_instruction),
19353
+ user_instruction: userInstruction ?? shotInstruction,
18751
19354
  text_override: str(shotOverrides.text)
18752
- },
19355
+ } : null,
19356
+ setup: setup ? { version_id: setup.version_id, element_id: setup.element_id } : null,
18753
19357
  scene: {
18754
- version_id: sceneRef.version.version_id,
19358
+ element_id: scene.element_id,
19359
+ version_id: scene.version_id,
18755
19360
  nodes,
18756
- relations: asArray2(scene.relations),
18757
- environment: scene.environment ?? null,
18758
- unit_system: scene.unit_system ?? null
18759
- },
18760
- camera: {
18761
- camera_id: cameraId,
18762
- name: str(camera.name),
18763
- preset: presetRef.version.payload,
18764
- placement: cameraPlacement,
18765
- orientation: camera.orientation ?? null,
18766
- framing: framing ?? null,
18767
- focus,
18768
- pattern
19361
+ relations: asArray3(scene.payload.relations),
19362
+ environment: scene.payload.environment ?? null,
19363
+ unit_system: scene.payload.unit_system ?? null
18769
19364
  },
19365
+ camera,
18770
19366
  styles,
18771
19367
  policies,
18772
19368
  fragments,
18773
- target_model: targetModel.payload,
19369
+ target_model: targetModelPayload,
18774
19370
  localization_notes: [],
18775
19371
  assumptions,
18776
- capabilities: {
18777
- can_describe_scene: report.can_describe_scene.state,
18778
- can_render_prompt: report.can_render_prompt.state,
18779
- can_place: report.can_place.state
18780
- },
19372
+ diagnostics,
19373
+ capabilities: eingefroreneFaehigkeiten,
18781
19374
  context_chain: [...contextChain],
18782
- target_model_resolution: {
18783
- element_id: targetModel.elementId,
18784
- version_id: targetModel.versionId,
18785
- chain_index: targetModel.chainIndex,
18786
- head: targetModel.head,
18787
- shadowed_retired: targetModel.shadowedRetired.map((entry) => ({
18788
- element_id: entry.elementId,
18789
- context_id: entry.contextId
18790
- }))
18791
- }
19375
+ target_model_resolution: targetModelResolution
18792
19376
  }
18793
19377
  };
18794
19378
  }
18795
- function capabilityGate(report, flag) {
18796
- const value = report[flag];
18797
- if (value.state === "yes") return null;
18798
- if (value.state === "no") {
18799
- const findings = value.reasons.filter((reason) => reason.kind === "blocked").map((reason) => reason.finding);
18800
- return {
18801
- class: "capability_blocked",
18802
- flag,
18803
- message: `${flag} ist "no" \u2014 der Aufbau erfuellt die Voraussetzung nachweislich nicht. ` + (findings.length > 0 ? findings.map((f) => `${f.rule}: ${f.message}`).join(" | ") : value.reasons.map(describeReason).join(" | ")),
18804
- findings
18805
- };
18806
- }
18807
- return {
18808
- class: "capability_undetermined",
18809
- flag,
18810
- message: `${flag} ist "unknown" \u2014 es liess sich nicht feststellen, ob die Voraussetzung erfuellt ist. Ein Lauf wuerde eine unbestimmte Annahme einfrieren (D-22). ` + value.reasons.map(describeReason).join(" | "),
18811
- reasons: value.reasons
18812
- };
18813
- }
18814
- function describeReason(reason) {
18815
- switch (reason.kind) {
18816
- case "schema":
18817
- return `Schemafehler unter ${reason.error.instancePath || "/"}: ${reason.error.message}`;
18818
- case "blocked":
18819
- return `${reason.finding.rule} bei ${reason.finding.path || "/"}: ${reason.finding.message}`;
18820
- case "unchecked":
18821
- return `${reason.rule} nicht geprueft: ${reason.reason}`;
18822
- case "structure":
18823
- return reason.message;
18824
- case "inherited":
18825
- return reason.message;
18826
- }
18827
- }
18828
19379
 
18829
19380
  // src/lib/szenen/freezeDraftChain.ts
18830
19381
  function ladeKnoten(sicht, elementId, erwarteterTyp) {
@@ -18960,9 +19511,117 @@ async function freezeDraftChain(input) {
18960
19511
  ...shot.chain.source_recovery_id ? { sourceRecoveryId: shot.chain.source_recovery_id } : {}
18961
19512
  };
18962
19513
  }
19514
+ function ladeSzene(sicht, elementId, contextChain) {
19515
+ const gelesen = sicht.getElement(elementId);
19516
+ if (gelesen.kind !== "ok") {
19517
+ return {
19518
+ fehler: gelesen.message ?? `Element ${elementId} konnte nicht gelesen werden.`,
19519
+ technisch: true
19520
+ };
19521
+ }
19522
+ const snapshot = gelesen.value;
19523
+ if (!snapshot) return { fehler: `Element ${elementId} ist unbekannt.` };
19524
+ if (snapshot.element.type !== "scene") {
19525
+ return {
19526
+ fehler: `Element ${elementId} ist vom Typ "${snapshot.element.type}", erwartet "scene".`
19527
+ };
19528
+ }
19529
+ if (snapshot.draftFork) {
19530
+ return {
19531
+ fehler: `Element ${elementId} hat parallele Entwuerfe. Solange sie nicht aufgeloest sind, gibt es keinen eindeutigen Arbeitsstand (D-29).`
19532
+ };
19533
+ }
19534
+ if (snapshot.draft) {
19535
+ return {
19536
+ element_id: elementId,
19537
+ draft_id: snapshot.draft.draft_id,
19538
+ revision: snapshot.draft.revision,
19539
+ version_id: null,
19540
+ payload: snapshot.draft.payload
19541
+ };
19542
+ }
19543
+ const versionen = snapshot.versions ?? [];
19544
+ if (versionen.length === 0) {
19545
+ return {
19546
+ fehler: `Element ${elementId} hat weder einen Entwurf noch eine veroeffentlichte Version. Es gibt nichts einzufrieren.`
19547
+ };
19548
+ }
19549
+ const kopf = effectivePreferredHeadSafe(
19550
+ { element: snapshot.element, versions: versionen, preferredHeads: snapshot.preferredHeads },
19551
+ contextChain
19552
+ );
19553
+ if (kopf.kind === "dag_fehler") {
19554
+ return { fehler: `Der Versionsgraph von Element ${elementId} ist nicht aufbaubar: ${kopf.message}` };
19555
+ }
19556
+ const versionId = usableHeadVersionId(resolutionOf(kopf));
19557
+ if (!versionId) {
19558
+ return {
19559
+ fehler: `Element ${elementId} hat mehrere veroeffentlichte Koepfe und in diesem Kontext keine Auswahl. Welcher Stand gilt, entscheidet keine Reihenfolge (D-36) \u2014 bitte einen bevorzugten Kopf setzen.`
19560
+ };
19561
+ }
19562
+ const version2 = versionen.find((v) => v.version_id === versionId);
19563
+ if (!version2) {
19564
+ return {
19565
+ fehler: `Die wirksame Version ${versionId} von Element ${elementId} steht nicht in der gelesenen Momentaufnahme.`
19566
+ };
19567
+ }
19568
+ return {
19569
+ element_id: elementId,
19570
+ draft_id: null,
19571
+ revision: null,
19572
+ version_id: versionId,
19573
+ payload: version2.payload
19574
+ };
19575
+ }
19576
+ async function freezeSceneWorkingState(input) {
19577
+ const { sceneElementId, store, resolver, contextChain, userInstruction } = input;
19578
+ const a = await store.readSnapshot({ fresh: false });
19579
+ const szene = ladeSzene(a, sceneElementId, contextChain);
19580
+ if ("fehler" in szene) return ausfallErgebnis(szene);
19581
+ const b = await store.readSnapshot({ fresh: true });
19582
+ const erneut = ladeSzene(b, sceneElementId, contextChain);
19583
+ if ("fehler" in erneut) return ausfallErgebnis(erneut);
19584
+ if (erneut.draft_id !== szene.draft_id || erneut.revision !== szene.revision || erneut.version_id !== szene.version_id) {
19585
+ return {
19586
+ kind: "conflict",
19587
+ message: `Der Arbeitsstand der Szene ${sceneElementId} hat sich waehrend des Einfrierens geaendert (erwartet ${szene.draft_id ? `Entwurf ${szene.draft_id}, Revision ${szene.revision}` : `Version ${szene.version_id}`}, vorhanden ${erneut.draft_id ? `Entwurf ${erneut.draft_id}, Revision ${erneut.revision}` : `Version ${erneut.version_id}`}). Es wurde kein Modell gerufen.`,
19588
+ node: { type: "scene", element_id: sceneElementId, draft_id: szene.draft_id ?? "" },
19589
+ expectedRevision: szene.revision ?? 0,
19590
+ actualRevision: erneut.revision ?? 0
19591
+ };
19592
+ }
19593
+ const aufgeloest = await resolveScenePayload({
19594
+ scenePayload: szene.payload,
19595
+ sceneElementId: szene.element_id,
19596
+ // Ein Entwurf hat keine Version — und bekommt hier auch keine erfundene
19597
+ // (D-98/D-75).
19598
+ sceneVersionId: szene.version_id ?? "",
19599
+ resolver,
19600
+ contextChain,
19601
+ userInstruction
19602
+ });
19603
+ if (aufgeloest.kind !== "resolved") {
19604
+ return {
19605
+ kind: "rejected",
19606
+ message: aufgeloest.failure.message,
19607
+ failure: aufgeloest.failure
19608
+ };
19609
+ }
19610
+ return {
19611
+ kind: "ok",
19612
+ scene: {
19613
+ element_id: szene.element_id,
19614
+ draft_id: szene.draft_id,
19615
+ revision: szene.revision,
19616
+ version_id: szene.version_id
19617
+ },
19618
+ input: aufgeloest.input
19619
+ };
19620
+ }
18963
19621
 
18964
19622
  // src/lib/szenen/promptRun.ts
18965
19623
  function buildSystemInstruction2(input) {
19624
+ const ohneZielmodell = input.target_model === null || input.target_model === void 0;
18966
19625
  const model = input.target_model ?? {};
18967
19626
  const sprache = typeof model.prompt_language === "string" ? model.prompt_language : "en";
18968
19627
  const negativ = model.supports_negative_prompt === true;
@@ -18983,7 +19642,11 @@ function buildSystemInstruction2(input) {
18983
19642
  "",
18984
19643
  `Der Prompt wird in der Sprache "${sprache}" formuliert.`
18985
19644
  ];
18986
- if (!negativ) {
19645
+ if (ohneZielmodell) {
19646
+ zeilen.push(
19647
+ 'Es ist KEIN Zielmodell angegeben. Formuliere einen generischen Prompt und richte ihn nicht an einer bestimmten Bildmodell-Syntax aus. "negative" bleibt null, weil kein Modell zugesagt hat, ein Negativfeld zu fuehren; was mit negativen Fragmenten geschehen ist, gehoert in "notes".'
19648
+ );
19649
+ } else if (!negativ) {
18987
19650
  zeilen.push(
18988
19651
  'Das Zielmodell kennt KEIN Negativ-Prompt-Feld. "negative" bleibt null; was mit negativen Fragmenten geschehen ist, gehoert in "notes".'
18989
19652
  );
@@ -19012,6 +19675,14 @@ function buildSystemInstruction2(input) {
19012
19675
  'Widersprechen sie einander, nenne den Widerspruch mit beiden Quellen in "notes" \u2014',
19013
19676
  "waehle nicht still eine Seite."
19014
19677
  );
19678
+ if (input.camera === null) {
19679
+ zeilen.push(
19680
+ "",
19681
+ "Es ist KEINE Kamera angegeben. Erfinde keine Brennweite, keinen Bildausschnitt",
19682
+ "und keine Kameraposition. Wo eine Kameraaussage noetig waere, formuliere sie",
19683
+ 'als Annahme und nenne sie in "notes".'
19684
+ );
19685
+ }
19015
19686
  if (input.capabilities.can_place !== "yes") {
19016
19687
  zeilen.push(
19017
19688
  "",
@@ -19025,7 +19696,7 @@ function buildSystemInstruction2(input) {
19025
19696
  function buildUserPrompt2(input) {
19026
19697
  return JSON.stringify(input, null, 2);
19027
19698
  }
19028
- var isObject11 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
19699
+ var isObject12 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
19029
19700
  function parseRunResponse(raw) {
19030
19701
  const text = raw.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "").trim();
19031
19702
  if (text.length === 0) return { ok: false, message: "Die Antwort war leer." };
@@ -19039,7 +19710,7 @@ function parseRunResponse(raw) {
19039
19710
  }
19040
19711
  }
19041
19712
  function checkRunResponse(value, input) {
19042
- if (!isObject11(value)) {
19713
+ if (!isObject12(value)) {
19043
19714
  return { ok: false, message: `Die Antwort ist kein Objekt, sondern ${Array.isArray(value) ? "array" : typeof value}.` };
19044
19715
  }
19045
19716
  if (typeof value.positive !== "string" || value.positive.trim().length === 0) {
@@ -19057,11 +19728,11 @@ function checkRunResponse(value, input) {
19057
19728
  if (negative !== null && !negativErlaubt) {
19058
19729
  return {
19059
19730
  ok: false,
19060
- message: `Das Zielmodell fuehrt kein Negativ-Prompt-Feld (supports_negative_prompt = false), die Antwort liefert aber eines.`
19731
+ message: input.target_model ? `Das Zielmodell fuehrt kein Negativ-Prompt-Feld (supports_negative_prompt = false), die Antwort liefert aber eines.` : `Dieser Lauf hat kein Zielmodell (generische Ableitung); ein Negativ-Prompt-Feld ist damit nicht zugesagt, die Antwort liefert aber eines.`
19061
19732
  };
19062
19733
  }
19063
19734
  const visibility = value.visibility;
19064
- if (!isObject11(visibility)) {
19735
+ if (!isObject12(visibility)) {
19065
19736
  return { ok: false, message: '"visibility" fehlt \u2014 die Pflichtdiagnosen aus D-21.' };
19066
19737
  }
19067
19738
  if (typeof visibility.basis !== "string") {
@@ -19073,7 +19744,7 @@ function checkRunResponse(value, input) {
19073
19744
  const genannt = /* @__PURE__ */ new Set();
19074
19745
  const nodes = [];
19075
19746
  for (const eintrag of visibility.nodes) {
19076
- if (!isObject11(eintrag) || typeof eintrag.node_id !== "string" || typeof eintrag.state !== "string") {
19747
+ if (!isObject12(eintrag) || typeof eintrag.node_id !== "string" || typeof eintrag.state !== "string") {
19077
19748
  return { ok: false, message: 'Ein Eintrag in "visibility.nodes" hat keine node_id oder keinen state.' };
19078
19749
  }
19079
19750
  genannt.add(eintrag.node_id);
@@ -19218,6 +19889,23 @@ function createEventLogRunPersistence(channel) {
19218
19889
  return queryError(error, `Laeufe der Kette ${chainId} konnten nicht gelesen werden`);
19219
19890
  }
19220
19891
  },
19892
+ async listForScene(sceneElementId) {
19893
+ try {
19894
+ const events = await channel.readEvents();
19895
+ return {
19896
+ kind: "ok",
19897
+ value: collectPromptRuns(events).filter((run) => {
19898
+ const quelle = sourceOf(run);
19899
+ return quelle.kind === "scene_direct" && quelle.scene.element_id === sceneElementId;
19900
+ })
19901
+ };
19902
+ } catch (error) {
19903
+ return queryError(
19904
+ error,
19905
+ `Laeufe der Szene ${sceneElementId} konnten nicht gelesen werden`
19906
+ );
19907
+ }
19908
+ },
19221
19909
  async record(record) {
19222
19910
  try {
19223
19911
  await channel.appendEvent(SZENEN_EVENT_TYPES.promptRunRecorded, record);
@@ -19304,13 +19992,21 @@ function createClientRunPort(options) {
19304
19992
  return { kind: "error", message: resolved.failure.message, retryable: false };
19305
19993
  }
19306
19994
  const outcome = await runPromptDerivation(resolved.input, text);
19995
+ if (!resolved.input.shot) {
19996
+ return {
19997
+ kind: "error",
19998
+ message: `Die Aufloesung der Shot-Version ${input.shotVersionId} lieferte eine Eingabe ohne Shot. Das ist ein Programmfehler, kein Lauf.`,
19999
+ retryable: false
20000
+ };
20001
+ }
19307
20002
  const record = {
19308
20003
  run_id: newRunId(),
19309
20004
  shot_version_id: input.shotVersionId,
19310
20005
  shot_element_id: resolved.input.shot.element_id,
19311
20006
  scene_setup_version_id: resolved.input.scene.version_id,
19312
- camera_id: resolved.input.shot.camera_id,
19313
- target_model_version_id: resolved.input.target_model_resolution.version_id,
20007
+ // Leer heisst „kein Wert", nicht „Wert unbekannt" (D-75/D-135).
20008
+ camera_id: resolved.input.shot.camera_id ?? "",
20009
+ target_model_version_id: resolved.input.target_model_resolution?.version_id ?? "",
19314
20010
  provider: outcome.provider,
19315
20011
  model_id: outcome.model_id,
19316
20012
  system_prompt: outcome.system_prompt,
@@ -19382,8 +20078,8 @@ function createClientRunPort(options) {
19382
20078
  shot_version_id: "",
19383
20079
  shot_element_id: input.shotElementId,
19384
20080
  scene_setup_version_id: "",
19385
- camera_id: eingefroren.input.shot.camera_id,
19386
- target_model_version_id: eingefroren.input.target_model_resolution.version_id,
20081
+ camera_id: eingefroren.input.shot?.camera_id ?? "",
20082
+ target_model_version_id: eingefroren.input.target_model_resolution?.version_id ?? "",
19387
20083
  provider: outcome.provider,
19388
20084
  model_id: outcome.model_id,
19389
20085
  system_prompt: outcome.system_prompt,
@@ -19417,6 +20113,95 @@ function createClientRunPort(options) {
19417
20113
  async listRunsForChain(chainId) {
19418
20114
  const alle = await runs.listForChain(chainId);
19419
20115
  return alle;
20116
+ },
20117
+ // ── Der Lauf direkt aus einer Szene (D-135) ───────────────────────────
20118
+ //
20119
+ // Bewusst **derselbe** Ablauf wie oben: Kontext ermitteln, Kette
20120
+ // aufloesen, einfrieren, dann erst rufen. Was sich unterscheidet, ist
20121
+ // ausschliesslich, **was** eingefroren wird — ein Szenen-Arbeitsstand
20122
+ // statt einer Drei-Knoten-Kette.
20123
+ async startRunFromScene(input) {
20124
+ try {
20125
+ let contextId = input.contextId;
20126
+ if (contextId === void 0) {
20127
+ const element = await elements.getElement(input.sceneElementId);
20128
+ if (element.kind !== "ok") {
20129
+ return { kind: "error", message: element.message, retryable: element.retryable };
20130
+ }
20131
+ if (element.value === null) {
20132
+ return {
20133
+ kind: "error",
20134
+ message: `Element ${input.sceneElementId} ist unbekannt.`,
20135
+ retryable: false
20136
+ };
20137
+ }
20138
+ contextId = element.value.element.context_id;
20139
+ }
20140
+ const chain = await context.chainFor(contextId ?? null);
20141
+ if (chain.kind !== "ok") {
20142
+ return { kind: "error", message: chain.message, retryable: chain.retryable };
20143
+ }
20144
+ const eingefroren = await freezeSceneWorkingState({
20145
+ sceneElementId: input.sceneElementId,
20146
+ store: elements,
20147
+ resolver,
20148
+ contextChain: chain.value,
20149
+ userInstruction: input.userInstruction ?? null
20150
+ });
20151
+ if (eingefroren.kind === "conflict") {
20152
+ return {
20153
+ kind: "conflict",
20154
+ message: eingefroren.message,
20155
+ node: eingefroren.node,
20156
+ expectedRevision: eingefroren.expectedRevision,
20157
+ actualRevision: eingefroren.actualRevision
20158
+ };
20159
+ }
20160
+ if (eingefroren.kind === "error") {
20161
+ return { kind: "error", message: eingefroren.message, retryable: eingefroren.retryable };
20162
+ }
20163
+ if (eingefroren.kind !== "ok") {
20164
+ return { kind: "rejected", message: eingefroren.message };
20165
+ }
20166
+ const outcome = await runPromptDerivation(eingefroren.input, text);
20167
+ const record = {
20168
+ run_id: newRunId(),
20169
+ // Keine fingierten Kennungen (D-98/D-75). Was es nicht gibt, bleibt
20170
+ // leer — die Herkunft steht in `source`.
20171
+ shot_version_id: "",
20172
+ shot_element_id: "",
20173
+ scene_setup_version_id: "",
20174
+ camera_id: "",
20175
+ target_model_version_id: eingefroren.input.target_model_resolution?.version_id ?? "",
20176
+ provider: outcome.provider,
20177
+ model_id: outcome.model_id,
20178
+ system_prompt: outcome.system_prompt,
20179
+ input_payload: eingefroren.input,
20180
+ status: outcome.status,
20181
+ failure: outcome.status === "failed" ? outcome.failure : null,
20182
+ output: outcome.status === "succeeded" ? outcome.output : null,
20183
+ raw_output: outcome.raw_output,
20184
+ source: { kind: "scene_direct", scene: eingefroren.scene },
20185
+ created_at: now().toISOString()
20186
+ };
20187
+ return await runs.record(record);
20188
+ } catch (error) {
20189
+ return {
20190
+ kind: "error",
20191
+ message: `Prompt-Lauf aus der Szene ${input.sceneElementId} ist fehlgeschlagen: ${error instanceof Error ? error.message : String(error)}`,
20192
+ retryable: true
20193
+ };
20194
+ }
20195
+ },
20196
+ async listRunsForScene(sceneElementId) {
20197
+ try {
20198
+ return await runs.listForScene(sceneElementId);
20199
+ } catch (error) {
20200
+ return queryError(
20201
+ error,
20202
+ `Laeufe der Szene ${sceneElementId} konnten nicht gelesen werden`
20203
+ );
20204
+ }
19420
20205
  }
19421
20206
  };
19422
20207
  }
@@ -19655,7 +20440,7 @@ function createFlowImageProvider(generateImage) {
19655
20440
  }
19656
20441
 
19657
20442
  // src/lib/szenen/imageGenerationOrderStore.ts
19658
- function istText3(wert) {
20443
+ function istText4(wert) {
19659
20444
  return typeof wert === "string" && wert.trim().length > 0;
19660
20445
  }
19661
20446
  function collectImageGenerationOrders(events) {
@@ -19663,7 +20448,7 @@ function collectImageGenerationOrders(events) {
19663
20448
  for (const event of events) {
19664
20449
  if (event.type !== SZENEN_EVENT_TYPES.imageGenerationStarted) continue;
19665
20450
  const payload = event.payload;
19666
- if (!payload || !istText3(payload.generation_batch_id) || !istText3(payload.prompt_run_id)) continue;
20451
+ if (!payload || !istText4(payload.generation_batch_id) || !istText4(payload.prompt_run_id)) continue;
19667
20452
  if (byBatchId.has(payload.generation_batch_id)) continue;
19668
20453
  byBatchId.set(payload.generation_batch_id, {
19669
20454
  generation_batch_id: payload.generation_batch_id,
@@ -19677,7 +20462,7 @@ function collectImageGenerationOrders(events) {
19677
20462
  for (const event of events) {
19678
20463
  if (event.type !== SZENEN_EVENT_TYPES.imageGenerationFinished) continue;
19679
20464
  const payload = event.payload;
19680
- if (!payload || !istText3(payload.generation_batch_id)) continue;
20465
+ if (!payload || !istText4(payload.generation_batch_id)) continue;
19681
20466
  const auftrag = byBatchId.get(payload.generation_batch_id);
19682
20467
  if (!auftrag) continue;
19683
20468
  if (auftrag.status !== "started") continue;
@@ -19698,18 +20483,18 @@ function ordersForPromptRun(events, runId) {
19698
20483
  }
19699
20484
 
19700
20485
  // src/lib/szenen/imageBatchStore.ts
19701
- var istText4 = (wert) => typeof wert === "string" && wert.trim().length > 0;
20486
+ var istText5 = (wert) => typeof wert === "string" && wert.trim().length > 0;
19702
20487
  function alsBilder(rohe) {
19703
20488
  if (!Array.isArray(rohe)) return [];
19704
- return rohe.filter((b) => !!b && typeof b === "object").filter((b) => istText4(b.media_ref) && typeof b.position === "number").map((b) => ({
20489
+ return rohe.filter((b) => !!b && typeof b === "object").filter((b) => istText5(b.media_ref) && typeof b.position === "number").map((b) => ({
19705
20490
  media_ref: b.media_ref,
19706
20491
  position: b.position,
19707
- ...istText4(b.asset_id) ? { asset_id: b.asset_id } : {},
20492
+ ...istText5(b.asset_id) ? { asset_id: b.asset_id } : {},
19708
20493
  // Der bestätigte MIME-Typ (Review 2026-08-18 §5 P2) muss durch das
19709
20494
  // Rücklesen hindurch: Genau hier braucht ihn der Lesepfad, um den
19710
20495
  // Dateipfad `images/<id>.<ext>` zu bilden, statt ihn zu erraten. Nur
19711
20496
  // gesetzt, wenn er im Ereignis steht — ältere Batches tragen ihn nicht.
19712
- ...istText4(b.mime_type) ? { mime_type: b.mime_type } : {},
20497
+ ...istText5(b.mime_type) ? { mime_type: b.mime_type } : {},
19713
20498
  model: b.model ?? null,
19714
20499
  seed: b.seed ?? null,
19715
20500
  width: b.width ?? null,
@@ -19721,7 +20506,7 @@ function collectImageBatches(events) {
19721
20506
  for (const event of events) {
19722
20507
  if (event.type !== SZENEN_EVENT_TYPES.imageBatchPersisted) continue;
19723
20508
  const payload = event.payload;
19724
- if (!payload || !istText4(payload.item_id) || !istText4(payload.prompt_run_id) || !istText4(payload.generation_batch_id)) continue;
20509
+ if (!payload || !istText5(payload.item_id) || !istText5(payload.prompt_run_id) || !istText5(payload.generation_batch_id)) continue;
19725
20510
  if (payload.source_kind !== "generated_in_app" && payload.source_kind !== "external_import") continue;
19726
20511
  const bestehend = byItemId.get(payload.item_id);
19727
20512
  if (bestehend?.generation_batch_ids.includes(payload.generation_batch_id)) continue;
@@ -19732,7 +20517,7 @@ function collectImageBatches(events) {
19732
20517
  prompt_run_id: payload.prompt_run_id,
19733
20518
  generation_batch_ids: [payload.generation_batch_id],
19734
20519
  source_kind: payload.source_kind,
19735
- title: istText4(payload.title) ? payload.title : `Batch aus Lauf ${payload.prompt_run_id}`,
20520
+ title: istText5(payload.title) ? payload.title : `Batch aus Lauf ${payload.prompt_run_id}`,
19736
20521
  images: bilder
19737
20522
  });
19738
20523
  continue;
@@ -20209,7 +20994,7 @@ function createFlowSzenenViewPort(options) {
20209
20994
  ...options.now ? { now: options.now } : {}
20210
20995
  }) : void 0;
20211
20996
  const bildProvider = options.imageProvider ?? (options.generateImage ? createFlowImageProvider(options.generateImage) : void 0);
20212
- const bildFaehigkeit = options.imageCapability ?? { environment_id: "flow", execution_paths: [FLOW_EXECUTION_PATH] };
20997
+ const bildFaehigkeit = options.imageCapability ?? { environment_id: "flow", execution_paths: [{ execution_path: FLOW_EXECUTION_PATH, provider: "google" }] };
20213
20998
  const imageGeneration = bildProvider && imageBatchWrite ? createImageGenerationPort({
20214
20999
  runs: { async getRun(runId) {
20215
21000
  return runs.getRun(runId);
@@ -20301,7 +21086,7 @@ function resolveBootstrapTarget(input) {
20301
21086
  // src/components/AvatarArchitectApp.tsx
20302
21087
  var import_jsx_runtime47 = require("react/jsx-runtime");
20303
21088
  function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, initialHfToken, hfNamespace, allowDevNamespace, requireWorkspaceSelection = false, serverBaseUrl, onFetchServerProjects, onServerSave, onServerLoad, onServerDelete }) {
20304
- (0, import_react48.useEffect)(() => {
21089
+ (0, import_react49.useEffect)(() => {
20305
21090
  const id = "flow-styles";
20306
21091
  if (!document.getElementById(id)) {
20307
21092
  const style = document.createElement("style");
@@ -20310,36 +21095,36 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20310
21095
  document.head.appendChild(style);
20311
21096
  }
20312
21097
  }, []);
20313
- const [showStart, setShowStart] = (0, import_react48.useState)(true);
20314
- const [showSzenen, setShowSzenen] = (0, import_react48.useState)(false);
20315
- const [layoutChoice, setLayoutChoice] = (0, import_react48.useState)(() => {
21098
+ const [showStart, setShowStart] = (0, import_react49.useState)(true);
21099
+ const [showSzenen, setShowSzenen] = (0, import_react49.useState)(false);
21100
+ const [layoutChoice, setLayoutChoice] = (0, import_react49.useState)(() => {
20316
21101
  try {
20317
21102
  return localStorage.getItem("aa-layout") || null;
20318
21103
  } catch {
20319
21104
  return null;
20320
21105
  }
20321
21106
  });
20322
- const [projectLoaded, setProjectLoaded] = (0, import_react48.useState)(false);
20323
- const [hfToken, setHfToken] = (0, import_react48.useState)(initialHfToken || "");
20324
- const [hfTokenInput, setHfTokenInput] = (0, import_react48.useState)(initialHfToken || "");
20325
- const [isLoadingFromHF, setIsLoadingFromHF] = (0, import_react48.useState)(false);
20326
- const [hfNamespaceLocal, setHfNamespaceLocal] = (0, import_react48.useState)(() => {
21107
+ const [projectLoaded, setProjectLoaded] = (0, import_react49.useState)(false);
21108
+ const [hfToken, setHfToken] = (0, import_react49.useState)(initialHfToken || "");
21109
+ const [hfTokenInput, setHfTokenInput] = (0, import_react49.useState)(initialHfToken || "");
21110
+ const [isLoadingFromHF, setIsLoadingFromHF] = (0, import_react49.useState)(false);
21111
+ const [hfNamespaceLocal, setHfNamespaceLocal] = (0, import_react49.useState)(() => {
20327
21112
  try {
20328
21113
  return normalizeSelectableNamespace(localStorage.getItem("aa-hf-namespace"));
20329
21114
  } catch {
20330
21115
  return DEFAULT_NAMESPACE;
20331
21116
  }
20332
21117
  });
20333
- const [hfNamespaceFromServer, setHfNamespaceFromServer] = (0, import_react48.useState)(null);
20334
- const [serverProbeFailed, setServerProbeFailed] = (0, import_react48.useState)(false);
20335
- (0, import_react48.useEffect)(() => {
21118
+ const [hfNamespaceFromServer, setHfNamespaceFromServer] = (0, import_react49.useState)(null);
21119
+ const [serverProbeFailed, setServerProbeFailed] = (0, import_react49.useState)(false);
21120
+ (0, import_react49.useEffect)(() => {
20336
21121
  if (hfNamespace !== void 0 || !serverBaseUrl) return;
20337
21122
  fetch(`${serverBaseUrl}/api/status`).then((r) => r.json()).then((d) => {
20338
21123
  if (typeof d.hfNamespace === "string" && d.hfNamespace) setHfNamespaceFromServer(d.hfNamespace);
20339
21124
  else setServerProbeFailed(true);
20340
21125
  }).catch(() => setServerProbeFailed(true));
20341
21126
  }, [hfNamespace, serverBaseUrl]);
20342
- const [targetConfirmed, setTargetConfirmed] = (0, import_react48.useState)(false);
21127
+ const [targetConfirmed, setTargetConfirmed] = (0, import_react49.useState)(false);
20343
21128
  const isDialogTarget = hfNamespace === void 0 && hfNamespaceFromServer === null && (!serverBaseUrl || serverProbeFailed);
20344
21129
  const isProbingServer = hfNamespace === void 0 && !!serverBaseUrl && hfNamespaceFromServer === null && !serverProbeFailed;
20345
21130
  const bootstrapTarget = resolveBootstrapTarget({
@@ -20350,8 +21135,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20350
21135
  isStartDialogOpen: isDialogTarget && !targetConfirmed
20351
21136
  });
20352
21137
  const effectiveNamespace = bootstrapTarget.namespace ?? "";
20353
- const kontextRef = (0, import_react48.useRef)(null);
20354
- const szenenPort = (0, import_react48.useMemo)(() => hfToken && effectiveNamespace ? createFlowSzenenViewPort({
21138
+ const kontextRef = (0, import_react49.useRef)(null);
21139
+ const szenenPort = (0, import_react49.useMemo)(() => hfToken && effectiveNamespace ? createFlowSzenenViewPort({
20355
21140
  generateText: onGeneratePrompt,
20356
21141
  token: hfToken,
20357
21142
  namespace: effectiveNamespace,
@@ -20373,10 +21158,10 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20373
21158
  const tagBaseResult = useTagBase(hfToken, effectiveNamespace);
20374
21159
  const szenenKontextId = tagBaseResult.selectedContextId === null ? null : String(tagBaseResult.selectedContextId);
20375
21160
  kontextRef.current = szenenKontextId;
20376
- const [imageUploadStatus, setImageUploadStatus] = (0, import_react48.useState)(/* @__PURE__ */ new Map());
20377
- const [bootstrapLog, setBootstrapLog] = (0, import_react48.useState)([]);
20378
- const [isBootstrapping, setIsBootstrapping] = (0, import_react48.useState)(false);
20379
- const [hfMissingImages, setHfMissingImages] = (0, import_react48.useState)([]);
21161
+ const [imageUploadStatus, setImageUploadStatus] = (0, import_react49.useState)(/* @__PURE__ */ new Map());
21162
+ const [bootstrapLog, setBootstrapLog] = (0, import_react49.useState)([]);
21163
+ const [isBootstrapping, setIsBootstrapping] = (0, import_react49.useState)(false);
21164
+ const [hfMissingImages, setHfMissingImages] = (0, import_react49.useState)([]);
20380
21165
  const syncTopSlot = /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_jsx_runtime47.Fragment, { children: [
20381
21166
  localOnlyCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { style: { background: "rgba(234,179,8,0.15)", border: "1px solid rgba(234,179,8,0.3)", padding: "4px 10px", fontSize: 11, color: "#fbbf24", borderRadius: 4, marginBottom: 4 }, children: [
20382
21167
  "\u26A0 ",
@@ -20426,7 +21211,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20426
21211
  bootstrapLog.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { style: { marginTop: 6, fontFamily: "monospace", fontSize: 10, color: "#78716c", lineHeight: 1.6 }, children: bootstrapLog.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { children: l }, i)) })
20427
21212
  ] })
20428
21213
  ] });
20429
- const wsInputRef = (0, import_react48.useRef)(null);
21214
+ const wsInputRef = (0, import_react49.useRef)(null);
20430
21215
  const startApp = (choice) => {
20431
21216
  try {
20432
21217
  localStorage.setItem("aa-layout", choice);
@@ -20436,18 +21221,18 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20436
21221
  setTargetConfirmed(true);
20437
21222
  setShowStart(false);
20438
21223
  };
20439
- const [nodes, setNodes] = (0, import_react48.useState)([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
20440
- const [edges, setEdges] = (0, import_react48.useState)([]);
20441
- const [history, setHistory] = (0, import_react48.useState)([]);
20442
- const [galleryItems, setGalleryItems] = (0, import_react48.useState)([]);
20443
- const galleryItemsRef = (0, import_react48.useRef)([]);
20444
- (0, import_react48.useEffect)(() => {
21224
+ const [nodes, setNodes] = (0, import_react49.useState)([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
21225
+ const [edges, setEdges] = (0, import_react49.useState)([]);
21226
+ const [history, setHistory] = (0, import_react49.useState)([]);
21227
+ const [galleryItems, setGalleryItems] = (0, import_react49.useState)([]);
21228
+ const galleryItemsRef = (0, import_react49.useRef)([]);
21229
+ (0, import_react49.useEffect)(() => {
20445
21230
  galleryItemsRef.current = galleryItems;
20446
21231
  }, [galleryItems]);
20447
- const hfImageNotFoundRef = (0, import_react48.useRef)(/* @__PURE__ */ new Map());
20448
- const [galleryVisibleCount, setGalleryVisibleCount] = (0, import_react48.useState)(20);
20449
- const [historyVisibleCount, setHistoryVisibleCount] = (0, import_react48.useState)(20);
20450
- const loadThumbnailsForEntries = (0, import_react48.useCallback)(async (entries) => {
21232
+ const hfImageNotFoundRef = (0, import_react49.useRef)(/* @__PURE__ */ new Map());
21233
+ const [galleryVisibleCount, setGalleryVisibleCount] = (0, import_react49.useState)(20);
21234
+ const [historyVisibleCount, setHistoryVisibleCount] = (0, import_react49.useState)(20);
21235
+ const loadThumbnailsForEntries = (0, import_react49.useCallback)(async (entries) => {
20451
21236
  for (const entry of entries) {
20452
21237
  if (galleryItemsRef.current.find((g) => g.id === entry.id)?.base64) continue;
20453
21238
  if (hfImageNotFoundRef.current.has(entry.id)) continue;
@@ -20466,7 +21251,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20466
21251
  }
20467
21252
  }
20468
21253
  }, [hfToken, effectiveNamespace]);
20469
- (0, import_react48.useEffect)(() => {
21254
+ (0, import_react49.useEffect)(() => {
20470
21255
  if (!hfState) return;
20471
21256
  if (hfState.tags?.by_category) setWorkspaceTags(hfState.tags);
20472
21257
  const hfIds = new Set(hfState.metadata.map((m) => m.id));
@@ -20493,36 +21278,36 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20493
21278
  return [...localOnly, ...merged].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
20494
21279
  });
20495
21280
  }, [hfState]);
20496
- (0, import_react48.useEffect)(() => {
21281
+ (0, import_react49.useEffect)(() => {
20497
21282
  if (!hfState) return;
20498
21283
  const sortedMeta = [...hfState.metadata].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
20499
21284
  const skeletons = sortedMeta.map((m) => ({ id: m.id, prompt: m.prompt, timestamp: m.timestamp, status: "done", nodeId: m.id, tags: m.tags || [] }));
20500
21285
  const repIds = new Set(groupByPrompt(skeletons).slice(0, galleryVisibleCount).map((g) => g.representative.id));
20501
21286
  loadThumbnailsForEntries(sortedMeta.filter((e) => repIds.has(e.id)));
20502
21287
  }, [hfState, galleryVisibleCount, loadThumbnailsForEntries]);
20503
- (0, import_react48.useEffect)(() => {
21288
+ (0, import_react49.useEffect)(() => {
20504
21289
  if (!hfState) return;
20505
21290
  const sortedMeta = [...hfState.metadata].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
20506
21291
  loadThumbnailsForEntries(sortedMeta.slice(0, historyVisibleCount));
20507
21292
  }, [hfState, historyVisibleCount, loadThumbnailsForEntries]);
20508
- const [activePrompt, setActivePrompt] = (0, import_react48.useState)("");
20509
- const [isSynthesizing, setIsSynthesizing] = (0, import_react48.useState)(false);
20510
- const [activeGenerationsCount, setActiveGenerationsCount] = (0, import_react48.useState)(0);
20511
- const [currentResult, setCurrentResult] = (0, import_react48.useState)(null);
20512
- const [focusedNodeId, setFocusedNodeId] = (0, import_react48.useState)(null);
20513
- const [leftTab, setLeftTab] = (0, import_react48.useState)("prompt");
20514
- const [promptFeedback, setPromptFeedback] = (0, import_react48.useState)(null);
20515
- const [lastPromptPayload, setLastPromptPayload] = (0, import_react48.useState)(null);
20516
- const [isPromptTabGenerating, setIsPromptTabGenerating] = (0, import_react48.useState)(false);
20517
- const [activeTab, setActiveTab] = (0, import_react48.useState)("history");
20518
- const [mobileTab, setMobileTab] = (0, import_react48.useState)("stage");
20519
- const [middlePanel, setMiddlePanel] = (0, import_react48.useState)("stage");
20520
- const [recentLabItems, setRecentLabItems] = (0, import_react48.useState)([]);
20521
- const [aspectRatio, setAspectRatio] = (0, import_react48.useState)("1:1");
20522
- const [selectedModel, setSelectedModel] = (0, import_react48.useState)("\u{1F34C} Nano Banana Pro");
20523
- const [seed, setSeed] = (0, import_react48.useState)(Math.floor(Math.random() * 1e6));
20524
- const [seedMode, setSeedMode] = (0, import_react48.useState)("random");
20525
- const [imageCount, setImageCount] = (0, import_react48.useState)(() => {
21293
+ const [activePrompt, setActivePrompt] = (0, import_react49.useState)("");
21294
+ const [isSynthesizing, setIsSynthesizing] = (0, import_react49.useState)(false);
21295
+ const [activeGenerationsCount, setActiveGenerationsCount] = (0, import_react49.useState)(0);
21296
+ const [currentResult, setCurrentResult] = (0, import_react49.useState)(null);
21297
+ const [focusedNodeId, setFocusedNodeId] = (0, import_react49.useState)(null);
21298
+ const [leftTab, setLeftTab] = (0, import_react49.useState)("prompt");
21299
+ const [promptFeedback, setPromptFeedback] = (0, import_react49.useState)(null);
21300
+ const [lastPromptPayload, setLastPromptPayload] = (0, import_react49.useState)(null);
21301
+ const [isPromptTabGenerating, setIsPromptTabGenerating] = (0, import_react49.useState)(false);
21302
+ const [activeTab, setActiveTab] = (0, import_react49.useState)("history");
21303
+ const [mobileTab, setMobileTab] = (0, import_react49.useState)("stage");
21304
+ const [middlePanel, setMiddlePanel] = (0, import_react49.useState)("stage");
21305
+ const [recentLabItems, setRecentLabItems] = (0, import_react49.useState)([]);
21306
+ const [aspectRatio, setAspectRatio] = (0, import_react49.useState)("1:1");
21307
+ const [selectedModel, setSelectedModel] = (0, import_react49.useState)("\u{1F34C} Nano Banana Pro");
21308
+ const [seed, setSeed] = (0, import_react49.useState)(Math.floor(Math.random() * 1e6));
21309
+ const [seedMode, setSeedMode] = (0, import_react49.useState)("random");
21310
+ const [imageCount, setImageCount] = (0, import_react49.useState)(() => {
20526
21311
  try {
20527
21312
  const v = parseInt(localStorage.getItem("aa-image-count") || "", 10);
20528
21313
  return v >= 1 && v <= 8 ? v : 4;
@@ -20544,45 +21329,45 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20544
21329
  { label: "4 Bilder", value: "4" },
20545
21330
  { label: "8 Bilder", value: "8" }
20546
21331
  ];
20547
- const [isLeftCollapsed, setIsLeftCollapsed] = (0, import_react48.useState)(false);
20548
- const [isRightCollapsed, setIsRightCollapsed] = (0, import_react48.useState)(false);
20549
- const [leftPanelWidth, setLeftPanelWidth] = (0, import_react48.useState)(() => {
21332
+ const [isLeftCollapsed, setIsLeftCollapsed] = (0, import_react49.useState)(false);
21333
+ const [isRightCollapsed, setIsRightCollapsed] = (0, import_react49.useState)(false);
21334
+ const [leftPanelWidth, setLeftPanelWidth] = (0, import_react49.useState)(() => {
20550
21335
  try {
20551
21336
  return parseInt(localStorage.getItem("aa-left-width") || "260", 10);
20552
21337
  } catch {
20553
21338
  return 260;
20554
21339
  }
20555
21340
  });
20556
- const [rightPanelWidth, setRightPanelWidth] = (0, import_react48.useState)(() => {
21341
+ const [rightPanelWidth, setRightPanelWidth] = (0, import_react49.useState)(() => {
20557
21342
  try {
20558
21343
  return parseInt(localStorage.getItem("aa-right-width") || "320", 10);
20559
21344
  } catch {
20560
21345
  return 320;
20561
21346
  }
20562
21347
  });
20563
- const [isPromptCollapsed, setIsPromptCollapsed] = (0, import_react48.useState)(false);
20564
- const [projectActionState, setProjectActionState] = (0, import_react48.useState)("idle");
20565
- const syncServerDataRef = (0, import_react48.useRef)(null);
20566
- const [workspaceTags, setWorkspaceTags] = (0, import_react48.useState)(null);
20567
- const [serverProjects, setServerProjects] = (0, import_react48.useState)([]);
20568
- const [isLoadingFromServer, setIsLoadingFromServer] = (0, import_react48.useState)(false);
20569
- const [highContrast, setHighContrast] = (0, import_react48.useState)(() => {
21348
+ const [isPromptCollapsed, setIsPromptCollapsed] = (0, import_react49.useState)(false);
21349
+ const [projectActionState, setProjectActionState] = (0, import_react49.useState)("idle");
21350
+ const syncServerDataRef = (0, import_react49.useRef)(null);
21351
+ const [workspaceTags, setWorkspaceTags] = (0, import_react49.useState)(null);
21352
+ const [serverProjects, setServerProjects] = (0, import_react49.useState)([]);
21353
+ const [isLoadingFromServer, setIsLoadingFromServer] = (0, import_react49.useState)(false);
21354
+ const [highContrast, setHighContrast] = (0, import_react49.useState)(() => {
20570
21355
  try {
20571
21356
  return localStorage.getItem("aa-contrast") === "high";
20572
21357
  } catch {
20573
21358
  return false;
20574
21359
  }
20575
21360
  });
20576
- const [activeReferenceId, setActiveReferenceId] = (0, import_react48.useState)(null);
20577
- const [activeReferenceThumbnail, setActiveReferenceThumbnail] = (0, import_react48.useState)(null);
20578
- const [isScanningImage, setIsScanningImage] = (0, import_react48.useState)(false);
20579
- const [touchStartX, setTouchStartX] = (0, import_react48.useState)(null);
20580
- const [isFullscreen, setIsFullscreen] = (0, import_react48.useState)(false);
20581
- const [zoomScale, setZoomScale] = (0, import_react48.useState)(1);
20582
- const [zoomOffset, setZoomOffset] = (0, import_react48.useState)({ x: 0, y: 0 });
20583
- const lastPinchDist = (0, import_react48.useRef)(null);
20584
- const lastTapTime = (0, import_react48.useRef)(0);
20585
- const dragStart = (0, import_react48.useRef)(null);
21361
+ const [activeReferenceId, setActiveReferenceId] = (0, import_react49.useState)(null);
21362
+ const [activeReferenceThumbnail, setActiveReferenceThumbnail] = (0, import_react49.useState)(null);
21363
+ const [isScanningImage, setIsScanningImage] = (0, import_react49.useState)(false);
21364
+ const [touchStartX, setTouchStartX] = (0, import_react49.useState)(null);
21365
+ const [isFullscreen, setIsFullscreen] = (0, import_react49.useState)(false);
21366
+ const [zoomScale, setZoomScale] = (0, import_react49.useState)(1);
21367
+ const [zoomOffset, setZoomOffset] = (0, import_react49.useState)({ x: 0, y: 0 });
21368
+ const lastPinchDist = (0, import_react49.useRef)(null);
21369
+ const lastTapTime = (0, import_react49.useRef)(0);
21370
+ const dragStart = (0, import_react49.useRef)(null);
20586
21371
  const openFullscreen = () => {
20587
21372
  setIsFullscreen(true);
20588
21373
  setZoomScale(1);
@@ -20645,7 +21430,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20645
21430
  setActiveReferenceId(null);
20646
21431
  setActiveReferenceThumbnail(null);
20647
21432
  };
20648
- const labServices = (0, import_react48.useMemo)(() => {
21433
+ const labServices = (0, import_react49.useMemo)(() => {
20649
21434
  const available = groupGenerationsToLabItems([...galleryItems, ...history]);
20650
21435
  return {
20651
21436
  availableItems: available,
@@ -20725,14 +21510,14 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20725
21510
  setIsScanningImage(false);
20726
21511
  }
20727
21512
  };
20728
- const currentIndex = (0, import_react48.useMemo)(() => history.findIndex((h) => h.id === currentResult?.id), [history, currentResult]);
20729
- const goToPrev = (0, import_react48.useCallback)(() => {
21513
+ const currentIndex = (0, import_react49.useMemo)(() => history.findIndex((h) => h.id === currentResult?.id), [history, currentResult]);
21514
+ const goToPrev = (0, import_react49.useCallback)(() => {
20730
21515
  if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
20731
21516
  }, [currentIndex, history]);
20732
- const goToNext = (0, import_react48.useCallback)(() => {
21517
+ const goToNext = (0, import_react49.useCallback)(() => {
20733
21518
  if (currentIndex < history.length - 1) setCurrentResult(history[currentIndex + 1]);
20734
21519
  }, [currentIndex, history]);
20735
- const handleGallerySelect = (0, import_react48.useCallback)((g) => {
21520
+ const handleGallerySelect = (0, import_react49.useCallback)((g) => {
20736
21521
  setCurrentResult(g);
20737
21522
  setMobileTab("stage");
20738
21523
  if (g.hasThumb && g.filename && hfToken && !g.fullBase64) {
@@ -20745,7 +21530,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20745
21530
  });
20746
21531
  }
20747
21532
  }, [hfToken, effectiveNamespace]);
20748
- const handleTitleSet = (0, import_react48.useCallback)((id) => {
21533
+ const handleTitleSet = (0, import_react49.useCallback)((id) => {
20749
21534
  const ts = Date.now();
20750
21535
  setGalleryItems((prev) => prev.map((g) => g.id === id ? { ...g, titleTs: ts } : g));
20751
21536
  setHistory((prev) => prev.map((g) => g.id === id ? { ...g, titleTs: ts } : g));
@@ -20754,13 +21539,13 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20754
21539
  });
20755
21540
  }
20756
21541
  }, [hfToken, effectiveNamespace, hfWriteEvent]);
20757
- const currentGroup = (0, import_react48.useMemo)(() => {
21542
+ const currentGroup = (0, import_react49.useMemo)(() => {
20758
21543
  if (!currentResult?.prompt) return [];
20759
21544
  const groups = groupByPrompt(galleryItems.filter((g) => g.status === "done" && !!g.base64));
20760
21545
  const group = groups.find((gr) => gr.prompt === currentResult.prompt);
20761
21546
  return group ? group.items : [];
20762
21547
  }, [galleryItems, currentResult?.prompt]);
20763
- (0, import_react48.useEffect)(() => {
21548
+ (0, import_react49.useEffect)(() => {
20764
21549
  if (!currentResult?.prompt || !hfToken || !effectiveNamespace || !hfState) return;
20765
21550
  const siblings = galleryItemsRef.current.filter(
20766
21551
  (g) => g.prompt === currentResult.prompt && !g.base64 && !hfImageNotFoundRef.current.has(g.id)
@@ -20799,7 +21584,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20799
21584
  }
20800
21585
  ) : null;
20801
21586
  useKeyboardNavigation(history, currentResult, setCurrentResult);
20802
- const getSubtreeFormat = (0, import_react48.useCallback)((nodeId, depth = 0) => {
21587
+ const getSubtreeFormat = (0, import_react49.useCallback)((nodeId, depth = 0) => {
20803
21588
  const node = nodes.find((n) => n.id === nodeId);
20804
21589
  if (!node) return "";
20805
21590
  const childrenIds = edges.filter((e) => e.source === nodeId).map((e) => e.target);
@@ -20807,7 +21592,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20807
21592
  return `${indent}- ${node.data.label || "(unbenannt)"}
20808
21593
  ` + childrenIds.map((id) => getSubtreeFormat(id, depth + 1)).join("");
20809
21594
  }, [nodes, edges]);
20810
- const activePath = (0, import_react48.useMemo)(() => {
21595
+ const activePath = (0, import_react49.useMemo)(() => {
20811
21596
  if (!focusedNodeId) return /* @__PURE__ */ new Set();
20812
21597
  const path = /* @__PURE__ */ new Set([focusedNodeId]);
20813
21598
  let currId = focusedNodeId;
@@ -21173,7 +21958,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
21173
21958
  setTimeout(() => setProjectActionState("idle"), 4e3);
21174
21959
  }
21175
21960
  };
21176
- (0, import_react48.useEffect)(() => {
21961
+ (0, import_react49.useEffect)(() => {
21177
21962
  if (activeTab === "setup" || activeTab === "sync") fetchServerProjects();
21178
21963
  }, [activeTab]);
21179
21964
  const mergeWorkspaceTags = (local, remote) => {
@@ -22205,7 +22990,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
22205
22990
  }
22206
22991
 
22207
22992
  // src/components/FaApp.tsx
22208
- var import_react49 = require("react");
22993
+ var import_react50 = require("react");
22209
22994
  var import_jsx_runtime48 = require("react/jsx-runtime");
22210
22995
  function FaApp({
22211
22996
  onGenerateImage,
@@ -22225,8 +23010,8 @@ function FaApp({
22225
23010
  onServerDelete,
22226
23011
  buildInfo
22227
23012
  }) {
22228
- const [hfNamespace, setHfNamespace] = (0, import_react49.useState)(void 0);
22229
- (0, import_react49.useEffect)(() => {
23013
+ const [hfNamespace, setHfNamespace] = (0, import_react50.useState)(void 0);
23014
+ (0, import_react50.useEffect)(() => {
22230
23015
  if (!serverBaseUrl) return;
22231
23016
  fetch(`${serverBaseUrl}/api/status`).then((r) => r.json()).then((d) => {
22232
23017
  if (typeof d.hfNamespace === "string") setHfNamespace(d.hfNamespace);
@@ -22491,15 +23276,15 @@ function createInMemoryWorkspacePersistence(now = () => Date.now()) {
22491
23276
  }
22492
23277
 
22493
23278
  // node_modules/zustand/esm/react.mjs
22494
- var import_react50 = __toESM(require("react"), 1);
23279
+ var import_react51 = __toESM(require("react"), 1);
22495
23280
  var identity = (arg) => arg;
22496
23281
  function useStore(api, selector = identity) {
22497
- const slice = import_react50.default.useSyncExternalStore(
23282
+ const slice = import_react51.default.useSyncExternalStore(
22498
23283
  api.subscribe,
22499
- import_react50.default.useCallback(() => selector(api.getState()), [api, selector]),
22500
- import_react50.default.useCallback(() => selector(api.getInitialState()), [api, selector])
23284
+ import_react51.default.useCallback(() => selector(api.getState()), [api, selector]),
23285
+ import_react51.default.useCallback(() => selector(api.getInitialState()), [api, selector])
22501
23286
  );
22502
- import_react50.default.useDebugValue(slice);
23287
+ import_react51.default.useDebugValue(slice);
22503
23288
  return slice;
22504
23289
  }
22505
23290