@rslsp1/fa-app-tools 3.0.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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 = "5.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 zweistufig 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\n Die Kachel ist die Einheit, nicht die Textzeile. Der gemeldete Screenshot vom\n 2026-08-22 zeigte im Flow-Host sechs schmale Spalten mit langen Prompttexten;\n `minmax(7rem, 1fr)` plus ein quadratisches Bild h\xE4lt die Karte auch auf\n schmalen Displays greifbar. */\n.szenen-bildwahl__raster {\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__raster button {\n position: relative;\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/* Der Platz der Kachel steht, bevor das Bild da ist \u2014 sonst springt das ganze\n Raster, sobald die erste Vorschau eintrifft. */\n.szenen-bildkachel {\n display: block;\n width: 100%;\n aspect-ratio: 1;\n border-radius: var(--radius-sm);\n overflow: hidden;\n background: var(--color-bg-app);\n}\n\n.szenen-bildkachel img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n}\n\n.szenen-bildkachel__leer {\n display: block;\n width: 100%;\n height: 100%;\n background: linear-gradient(135deg, var(--color-bg-app), var(--color-bg-surface));\n}\n\n/* Wie viele Varianten das Werk hat \u2014 dieselbe Auskunft wie im LabImagePicker,\n und der Grund, warum ein Klick auf die Karte eine Galerie \xF6ffnet. */\n.szenen-bildwahl__anzahl {\n position: absolute;\n top: calc(var(--space-sm) + 0.25rem);\n right: calc(var(--space-sm) + 0.25rem);\n padding: 0.05rem 0.35rem;\n font-size: 0.6875rem;\n font-weight: 700;\n color: var(--color-text-primary);\n background: var(--color-bg-elevated);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-bildwahl__nummer {\n font-size: 0.6875rem;\n color: var(--color-text-secondary);\n}\n\n/* Zwei Zeilen Titel, dann Schluss: Der Prompt eines Werkes ist oft mehrere\n hundert Zeichen lang und w\xE4re als Prim\xE4rdarstellung eine Textw\xFCste. */\n.szenen-bildwahl__titel {\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n line-height: 1.25;\n}\n\n.szenen-bildwahl__kopf {\n display: flex;\n align-items: center;\n gap: var(--space-sm);\n flex-wrap: wrap;\n margin-top: var(--space-sm);\n}\n\n.szenen-bildwahl__werktitel {\n font-size: 0.8125rem;\n color: var(--color-text-secondary);\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\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 max-width: 100%;\n}\n\n/* Das gew\xE4hlte Bild wird best\xE4tigt, nicht nur benannt: eine kleine Vorschau\n neben dem Titel. */\n.szenen-bildwahl__gewaehlt .szenen-bildkachel {\n width: 2.5rem;\n flex: 0 0 auto;\n}\n\n.szenen-bildwahl__gewaehlt .szenen-bildwahl__titel {\n min-width: 0;\n max-width: 14rem;\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,30 @@ 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");
15517
+
15518
+ // src/lib/szenen/bildVorschau.ts
15519
+ async function ladeVorschauen(port, mediaRefs, fertig, optionen = {}) {
15520
+ const laden = port.loadPreview;
15521
+ if (!laden) return;
15522
+ const parallel = Math.max(1, optionen.parallel ?? 4);
15523
+ const lauf = optionen.lauf;
15524
+ const offen = mediaRefs.filter((ref) => !(optionen.hat?.(ref) ?? false));
15525
+ let naechster = 0;
15526
+ async function arbeiter() {
15527
+ while (naechster < offen.length) {
15528
+ if (lauf?.abgebrochen) return;
15529
+ const ref = offen[naechster];
15530
+ naechster += 1;
15531
+ const ergebnis = await laden.call(port, ref);
15532
+ if (lauf?.abgebrochen) return;
15533
+ if (ergebnis.kind === "ok") fertig(ref, ergebnis.value);
15534
+ }
15535
+ }
15536
+ await Promise.all(
15537
+ Array.from({ length: Math.min(parallel, offen.length) }, () => arbeiter())
15538
+ );
15539
+ }
15142
15540
 
15143
15541
  // src/lib/szenen/derivationExamples.ts
15144
15542
  var PROPOSAL_EXAMPLE = {
@@ -15546,7 +15944,7 @@ function validateDerivationInput(input) {
15546
15944
  if (input.policy.produces.length === 0) return "Die Regel erzeugt keinen einzigen Vorschlag.";
15547
15945
  return null;
15548
15946
  }
15549
- var isObject9 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
15947
+ var isObject10 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
15550
15948
  var MAX_PROPOSAL_TITLE_LENGTH = 120;
15551
15949
  function fallbackProposalTitle(type) {
15552
15950
  return `Rekonstruktion ${type}`;
@@ -15572,7 +15970,7 @@ function resolveJsonPointer(wurzel, pointer) {
15572
15970
  aktuell = aktuell[index];
15573
15971
  continue;
15574
15972
  }
15575
- if (!isObject9(aktuell) || !(schluessel in aktuell)) {
15973
+ if (!isObject10(aktuell) || !(schluessel in aktuell)) {
15576
15974
  return { found: false, value: void 0 };
15577
15975
  }
15578
15976
  aktuell = aktuell[schluessel];
@@ -15586,7 +15984,7 @@ function collectVersionRefs2(value) {
15586
15984
  knoten.forEach(besuchen);
15587
15985
  return;
15588
15986
  }
15589
- if (!isObject9(knoten)) return;
15987
+ if (!isObject10(knoten)) return;
15590
15988
  if (typeof knoten.element_id === "string" && typeof knoten.version_id === "string") {
15591
15989
  gefunden.push({ element_id: knoten.element_id, version_id: knoten.version_id });
15592
15990
  }
@@ -15652,7 +16050,7 @@ function assessDerivationResponse(value, input) {
15652
16050
  envelope: null,
15653
16051
  contract_ok: false
15654
16052
  });
15655
- if (!isObject9(value)) {
16053
+ if (!isObject10(value)) {
15656
16054
  befund({
15657
16055
  code: "response.not_object",
15658
16056
  severity: "error",
@@ -15670,7 +16068,7 @@ function assessDerivationResponse(value, input) {
15670
16068
  });
15671
16069
  }
15672
16070
  const rohProposals = value.proposals;
15673
- const proposalsLesbar = isObject9(rohProposals);
16071
+ const proposalsLesbar = isObject10(rohProposals);
15674
16072
  if (!proposalsLesbar) {
15675
16073
  befund({
15676
16074
  code: "envelope.proposals_missing",
@@ -15680,7 +16078,7 @@ function assessDerivationResponse(value, input) {
15680
16078
  });
15681
16079
  }
15682
16080
  const roh = proposalsLesbar ? rohProposals : {};
15683
- const rohGaps = isObject9(value.gaps) ? value.gaps : {};
16081
+ const rohGaps = isObject10(value.gaps) ? value.gaps : {};
15684
16082
  const gapsUngefragt = Object.keys(rohGaps).filter(
15685
16083
  (t) => !input.policy.produces.includes(t)
15686
16084
  );
@@ -15692,7 +16090,7 @@ function assessDerivationResponse(value, input) {
15692
16090
  pointer: "/gaps"
15693
16091
  });
15694
16092
  }
15695
- const fehlend = input.policy.produces.filter((t) => !isObject9(roh[t]));
16093
+ const fehlend = input.policy.produces.filter((t) => !isObject10(roh[t]));
15696
16094
  if (fehlend.length > 0 && proposalsLesbar) {
15697
16095
  const d = befund({
15698
16096
  code: "envelope.proposals_missing_types",
@@ -15713,10 +16111,10 @@ function assessDerivationResponse(value, input) {
15713
16111
  pointer: "/proposals"
15714
16112
  });
15715
16113
  }
15716
- const rohTitles = isObject9(value.titles) ? value.titles : {};
16114
+ const rohTitles = isObject10(value.titles) ? value.titles : {};
15717
16115
  const umgebucht = [];
15718
16116
  for (const type of input.policy.produces) {
15719
- if (!isObject9(roh[type])) continue;
16117
+ if (!isObject10(roh[type])) continue;
15720
16118
  const payload = roh[type];
15721
16119
  const eigene = [];
15722
16120
  const bezug = REFERENCE_FIELD[type];
@@ -15752,7 +16150,7 @@ function assessDerivationResponse(value, input) {
15752
16150
  const bezugPointer = bezug ? `/${bezug.field}` : null;
15753
16151
  let gapFehler = false;
15754
16152
  for (const eintrag of benannt) {
15755
- if (!isObject9(eintrag) || typeof eintrag.pointer !== "string" || typeof eintrag.reason !== "string" || !eintrag.reason.trim()) {
16153
+ if (!isObject10(eintrag) || typeof eintrag.pointer !== "string" || typeof eintrag.reason !== "string" || !eintrag.reason.trim()) {
15756
16154
  eigene.push(befund({
15757
16155
  code: "proposal.gap_entry_invalid",
15758
16156
  severity: "error",
@@ -15828,7 +16226,7 @@ function assessDerivationResponse(value, input) {
15828
16226
  });
15829
16227
  } else {
15830
16228
  for (const eintrag of value.not_derivable) {
15831
- if (!isObject9(eintrag) || typeof eintrag.field !== "string" || typeof eintrag.reason !== "string") {
16229
+ if (!isObject10(eintrag) || typeof eintrag.field !== "string" || typeof eintrag.reason !== "string") {
15832
16230
  befund({
15833
16231
  code: "envelope.not_derivable_entry_invalid",
15834
16232
  severity: "error",
@@ -15853,7 +16251,7 @@ function assessDerivationResponse(value, input) {
15853
16251
  });
15854
16252
  } else {
15855
16253
  for (const eintrag of value.conflicts) {
15856
- if (!isObject9(eintrag) || typeof eintrag.field !== "string" || typeof eintrag.from_image !== "string" || typeof eintrag.from_prompt !== "string") {
16254
+ if (!isObject10(eintrag) || typeof eintrag.field !== "string" || typeof eintrag.from_image !== "string" || typeof eintrag.from_prompt !== "string") {
15857
16255
  befund({
15858
16256
  code: "envelope.conflicts_entry_invalid",
15859
16257
  severity: "error",
@@ -15977,7 +16375,7 @@ async function runSceneDerivation(input, port, userPrompt) {
15977
16375
  }
15978
16376
 
15979
16377
  // src/features/szenen/areas/RecoveryPanel.tsx
15980
- var import_react44 = require("react");
16378
+ var import_react45 = require("react");
15981
16379
  var import_jsx_runtime43 = require("react/jsx-runtime");
15982
16380
  var Diagnosen = ({ befunde, testId }) => {
15983
16381
  if (befunde.length === 0) return null;
@@ -16028,11 +16426,11 @@ var Ausbeute = ({ bewertung }) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)
16028
16426
  ] })
16029
16427
  ] });
16030
16428
  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);
16429
+ const [arbeitskopie, setArbeitskopie] = (0, import_react45.useState)(null);
16430
+ const [bewertung, setBewertung] = (0, import_react45.useState)(null);
16431
+ const [gesichert, setGesichert] = (0, import_react45.useState)(null);
16432
+ const [meldung, setMeldung] = (0, import_react45.useState)(null);
16433
+ const [laeuft, setLaeuft] = (0, import_react45.useState)(false);
16036
16434
  if (!port.derivation) return null;
16037
16435
  if (typeof lauf.raw_output !== "string" || lauf.raw_output.length === 0) return null;
16038
16436
  const derivation = port.derivation;
@@ -16155,12 +16553,12 @@ var RecoveryPanel = ({ port, lauf }) => {
16155
16553
  };
16156
16554
 
16157
16555
  // src/features/szenen/areas/ChainActions.tsx
16158
- var import_react45 = require("react");
16556
+ var import_react46 = require("react");
16159
16557
  var import_jsx_runtime44 = require("react/jsx-runtime");
16160
16558
  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);
16559
+ const [graph, setGraph] = (0, import_react46.useState)(null);
16560
+ const [meldung, setMeldung] = (0, import_react46.useState)(null);
16561
+ const [laeuft, setLaeuft] = (0, import_react46.useState)(false);
16164
16562
  if (!port.derivation) return null;
16165
16563
  const derivation = port.derivation;
16166
16564
  const runs = port.runs;
@@ -16226,7 +16624,13 @@ var ChainActions = ({ port, runId, recoveryId }) => {
16226
16624
  ] }),
16227
16625
  shotKnoten && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
16228
16626
  /* @__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 })
16627
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
16628
+ PromptStartAction,
16629
+ {
16630
+ port,
16631
+ source: { kind: "draft_chain", shotElementId: shotKnoten.elementId }
16632
+ }
16633
+ )
16230
16634
  ] }),
16231
16635
  meldung && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { className: "szenen-error", role: "alert", "data-testid": "kette-meldung", children: meldung })
16232
16636
  ] });
@@ -16240,8 +16644,8 @@ var MODUS_TEXT = {
16240
16644
  image_prompt: "Bild und Prompt"
16241
16645
  };
16242
16646
  var BelegFeld = ({ titel, text, testId, offen }) => {
16243
- const [kopiert, setKopiert] = (0, import_react46.useState)(false);
16244
- const feld = (0, import_react46.useRef)(null);
16647
+ const [kopiert, setKopiert] = (0, import_react47.useState)(false);
16648
+ const feld = (0, import_react47.useRef)(null);
16245
16649
  async function kopieren() {
16246
16650
  try {
16247
16651
  await navigator.clipboard.writeText(text);
@@ -16336,14 +16740,64 @@ function readAsBase642(datei) {
16336
16740
  leser.readAsDataURL(datei);
16337
16741
  });
16338
16742
  }
16743
+ function useVorschauen(port, mediaRefs) {
16744
+ const [quellen, setQuellen] = (0, import_react47.useState)(() => /* @__PURE__ */ new Map());
16745
+ const gehalten = (0, import_react47.useRef)(/* @__PURE__ */ new Map());
16746
+ const schluessel = mediaRefs.join(" ");
16747
+ (0, import_react47.useEffect)(() => {
16748
+ const bestand = port.imageLibrary;
16749
+ if (!bestand?.loadPreview) return void 0;
16750
+ const refs = schluessel ? schluessel.split(" ") : [];
16751
+ const lauf = { abgebrochen: false };
16752
+ void ladeVorschauen(
16753
+ bestand,
16754
+ refs,
16755
+ (mediaRef, quelle) => {
16756
+ gehalten.current.set(mediaRef, quelle);
16757
+ setQuellen(new Map(gehalten.current));
16758
+ },
16759
+ { parallel: 4, hat: (ref) => gehalten.current.has(ref), lauf }
16760
+ );
16761
+ return () => {
16762
+ lauf.abgebrochen = true;
16763
+ };
16764
+ }, [port, schluessel]);
16765
+ return quellen;
16766
+ }
16767
+ var Kachel = ({ quelle, alt }) => /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "szenen-bildkachel", children: quelle ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("img", { src: quelle, alt, loading: "lazy" }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "szenen-bildkachel__leer", "aria-hidden": "true" }) });
16339
16768
  var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
16340
- const [suche, setSuche] = (0, import_react46.useState)("");
16341
- const [bilder] = usePortQuery(
16342
- () => port.imageLibrary.listImages({ search: suche.trim() || void 0, limit: 60 }),
16769
+ const [suche, setSuche] = (0, import_react47.useState)("");
16770
+ const [offenesWerk, setOffenesWerk] = (0, import_react47.useState)(null);
16771
+ const [items] = usePortQuery(
16772
+ () => port.imageLibrary.listItems({ search: suche.trim() || void 0, limit: 60 }),
16343
16773
  [port, suche]
16344
16774
  );
16775
+ const werk = items.status === "ready" ? items.value.find((i) => i.itemRef === offenesWerk) ?? null : null;
16776
+ const sichtbar = (0, import_react47.useMemo)(() => {
16777
+ const refs = werk ? werk.images.map((b) => b.mediaRef) : items.status === "ready" ? items.value.map((i) => i.images[0]?.mediaRef).filter((r) => Boolean(r)) : [];
16778
+ return [.../* @__PURE__ */ new Set([...refs, ...gewaehlt.map((b) => b.mediaRef)])];
16779
+ }, [items, werk, gewaehlt]);
16780
+ const quellen = useVorschauen(port, sichtbar);
16781
+ const quelleVon = (bild) => bild.thumbUrl || quellen.get(bild.mediaRef) || null;
16345
16782
  return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "szenen-bildwahl", "data-testid": "bildwahl", children: [
16346
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("label", { className: "szenen-field", children: [
16783
+ werk ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "szenen-bildwahl__kopf", children: [
16784
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
16785
+ "button",
16786
+ {
16787
+ type: "button",
16788
+ className: "szenen-bildwahl__zurueck",
16789
+ onClick: () => setOffenesWerk(null),
16790
+ "data-testid": "bildwahl-zurueck",
16791
+ children: "Zurueck zur Auswahl"
16792
+ }
16793
+ ),
16794
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "szenen-bildwahl__werktitel", "data-testid": "bildwahl-werktitel", children: [
16795
+ werk.title,
16796
+ " \xB7 ",
16797
+ werk.images.length,
16798
+ " Bilder"
16799
+ ] })
16800
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("label", { className: "szenen-field", children: [
16347
16801
  /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { children: "Bild aus dem Bestand" }),
16348
16802
  /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
16349
16803
  "input",
@@ -16351,31 +16805,70 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
16351
16805
  type: "search",
16352
16806
  value: suche,
16353
16807
  onChange: (e) => setSuche(e.target.value),
16808
+ onKeyDown: (e) => {
16809
+ if (e.key === "Enter") e.preventDefault();
16810
+ },
16354
16811
  placeholder: "Suchen \u2026",
16355
16812
  "data-testid": "bild-suche"
16356
16813
  }
16357
16814
  )
16358
16815
  ] }),
16359
- bilder.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { children: "Bilder werden geladen \u2026" }),
16360
- bilder.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { className: "szenen-error", role: "alert", children: [
16816
+ items.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { children: "Bilder werden geladen \u2026" }),
16817
+ items.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { className: "szenen-error", role: "alert", children: [
16361
16818
  "Der Bildbestand ist nicht lesbar: ",
16362
- bilder.message
16819
+ items.message
16363
16820
  ] }),
16364
- bilder.status === "ready" && bilder.value.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "szenen-empty", "data-testid": "bildwahl-leer", children: "Zu dieser Suche gibt es kein Bild. Ohne Treffer bleibt der Upload." }),
16365
- bilder.status === "ready" && bilder.value.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("ul", { className: "szenen-bildwahl__liste", children: bilder.value.map((bild) => /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
16821
+ items.status === "ready" && items.value.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "szenen-empty", "data-testid": "bildwahl-leer", children: "Zu dieser Suche gibt es kein Bild. Ohne Treffer bleibt der Upload." }),
16822
+ werk && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("ul", { className: "szenen-bildwahl__raster", "data-testid": "bildwahl-galerie", children: werk.images.map((bild, position) => /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
16366
16823
  "button",
16367
16824
  {
16368
16825
  type: "button",
16369
- onClick: () => onWaehlen(bild),
16826
+ onClick: () => {
16827
+ onWaehlen(bild);
16828
+ setOffenesWerk(null);
16829
+ },
16370
16830
  "data-testid": `bild-waehlen-${bild.mediaRef}`,
16831
+ title: bild.title,
16371
16832
  children: [
16372
- bild.thumbUrl && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("img", { src: bild.thumbUrl, alt: "" }),
16373
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { children: bild.title })
16833
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Kachel, { quelle: quelleVon(bild), alt: bild.title }),
16834
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "szenen-bildwahl__nummer", children: [
16835
+ "#",
16836
+ position + 1
16837
+ ] })
16374
16838
  ]
16375
16839
  }
16376
16840
  ) }, bild.mediaRef)) }),
16841
+ !werk && items.status === "ready" && items.value.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("ul", { className: "szenen-bildwahl__raster", "data-testid": "bildwahl-werke", children: items.value.map((item) => {
16842
+ const deckbild = item.images[0];
16843
+ if (!deckbild) return null;
16844
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
16845
+ "button",
16846
+ {
16847
+ type: "button",
16848
+ onClick: () => item.images.length === 1 ? onWaehlen(deckbild) : setOffenesWerk(item.itemRef),
16849
+ "data-testid": `item-waehlen-${item.itemRef}`,
16850
+ title: item.title,
16851
+ children: [
16852
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Kachel, { quelle: quelleVon(deckbild), alt: item.title }),
16853
+ item.images.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
16854
+ "span",
16855
+ {
16856
+ className: "szenen-bildwahl__anzahl",
16857
+ "data-testid": `item-anzahl-${item.itemRef}`,
16858
+ children: [
16859
+ item.images.length,
16860
+ "x"
16861
+ ]
16862
+ }
16863
+ ),
16864
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "szenen-bildwahl__titel", children: item.title })
16865
+ ]
16866
+ }
16867
+ ) }, item.itemRef);
16868
+ }) }),
16377
16869
  gewaehlt.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("ul", { className: "szenen-bildwahl__gewaehlt", "data-testid": "bilder-gewaehlt", children: gewaehlt.map((bild) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("li", { children: [
16378
- bild.title,
16870
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Kachel, { quelle: quelleVon(bild), alt: bild.title }),
16871
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "szenen-bildwahl__titel", children: bild.title }),
16379
16872
  /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
16380
16873
  "button",
16381
16874
  {
@@ -16395,26 +16888,29 @@ async function digestOf(datei) {
16395
16888
  return [...new Uint8Array(hash)].map((b) => b.toString(16).padStart(2, "0")).join("");
16396
16889
  }
16397
16890
  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)("");
16891
+ const [policyVersionId, setPolicyVersionId] = (0, import_react47.useState)("");
16892
+ const [modus, setModus] = (0, import_react47.useState)("prompt");
16893
+ const [promptText, setPromptText] = (0, import_react47.useState)("");
16894
+ const [dateien, setDateien] = (0, import_react47.useState)([]);
16895
+ const [bestandsbilder, setBestandsbilder] = (0, import_react47.useState)([]);
16896
+ const [laeuft, setLaeuft] = (0, import_react47.useState)(false);
16897
+ const [lauf, setLauf] = (0, import_react47.useState)(null);
16898
+ const [meldung, setMeldung] = (0, import_react47.useState)(null);
16899
+ const [uebernommen, setUebernommen] = (0, import_react47.useState)({});
16900
+ const [titel, setTitel] = (0, import_react47.useState)({});
16901
+ const [referenzen, setReferenzen] = (0, import_react47.useState)({});
16902
+ const [beanstandung, setBeanstandung] = (0, import_react47.useState)(null);
16903
+ const [zusatz, setZusatz] = (0, import_react47.useState)("");
16411
16904
  const [regeln] = usePortQuery(
16412
16905
  () => port.derivation.listPolicies(contextId),
16413
16906
  [port, contextId]
16414
16907
  );
16415
16908
  const gewaehlt = regeln.status === "ready" ? regeln.value.find((r) => r.versionId === policyVersionId) ?? null : null;
16416
16909
  async function starten(folgt) {
16417
- if (!gewaehlt) return;
16910
+ if (!gewaehlt) {
16911
+ setMeldung("Bitte zuerst eine Rekonstruktionsregel waehlen \u2014 ohne sie ist kein Lauf reproduzierbar (D-03).");
16912
+ return;
16913
+ }
16418
16914
  setLaeuft(true);
16419
16915
  setMeldung(null);
16420
16916
  setLauf(null);
@@ -16791,9 +17287,9 @@ var SzenenView = ({
16791
17287
  initialArea,
16792
17288
  onAreaChange
16793
17289
  }) => {
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));
17290
+ const bereiche = (0, import_react48.useMemo)(() => areasFor(port), [port]);
17291
+ const faehig = (0, import_react48.useMemo)(() => capabilitiesOf(port), [port]);
17292
+ const [aktiv, setAktiv] = (0, import_react48.useState)(() => resolveArea(port, initialArea));
16797
17293
  const wechsle = (id) => {
16798
17294
  setAktiv(id);
16799
17295
  onAreaChange?.(id);
@@ -16824,7 +17320,16 @@ var SzenenView = ({
16824
17320
  )) }),
16825
17321
  /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("section", { className: "szenen-view__area", "aria-label": aktuellerBereich.title, children: [
16826
17322
  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 }),
17323
+ aktiv === "elements" && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17324
+ ElementsArea,
17325
+ {
17326
+ port,
17327
+ contextId,
17328
+ restrictToTypes: BAUSTEIN_TYPES,
17329
+ allTypesLabel: "alle Bausteine",
17330
+ listTitle: "Bausteine"
17331
+ }
17332
+ ),
16828
17333
  aktiv === "scene" && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(SceneArea, { port, contextId }),
16829
17334
  aktiv === "setup" && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(SetupArea, { port, contextId }),
16830
17335
  aktiv === "shot" && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ShotArea, { port, contextId }),
@@ -16839,6 +17344,17 @@ var SzenenView = ({
16839
17344
  };
16840
17345
 
16841
17346
  // src/lib/szenen/flowTextGenerationPort.ts
17347
+ function alsFlowTextZugang(sdk) {
17348
+ return async (prompt, options) => {
17349
+ const antwort = await sdk(prompt, options);
17350
+ if (typeof antwort?.text !== "string") {
17351
+ throw new Error(
17352
+ `Flow.generate.text lieferte kein { text: string }, sondern ${antwort === null || antwort === void 0 ? String(antwort) : typeof antwort}: ${JSON.stringify(antwort)?.slice(0, 200)}`
17353
+ );
17354
+ }
17355
+ return antwort.text;
17356
+ };
17357
+ }
16842
17358
  function createFlowTextGenerationPort(generateText, options = {}) {
16843
17359
  const provider = options.provider ?? "flow";
16844
17360
  const modelId = options.modelId ?? "flow.generate.text";
@@ -17602,7 +18118,7 @@ var CHAIN_EDGES = {
17602
18118
  scene_ref: { from: "scene_setup", to: "scene" },
17603
18119
  scene_setup_ref: { from: "shot", to: "scene_setup" }
17604
18120
  };
17605
- function istText2(wert) {
18121
+ function istText3(wert) {
17606
18122
  return typeof wert === "string" && wert.trim().length > 0;
17607
18123
  }
17608
18124
  function checkDraftChainMeta(meta, type) {
@@ -17613,14 +18129,14 @@ function checkDraftChainMeta(meta, type) {
17613
18129
  };
17614
18130
  }
17615
18131
  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)) {
18132
+ if (!istText3(m.chain_id)) return { ok: false, message: '"chain_id" fehlt oder ist leer.' };
18133
+ if (!istText3(m.source_run_id)) {
17618
18134
  return {
17619
18135
  ok: false,
17620
18136
  message: '"source_run_id" fehlt oder ist leer \u2014 ohne sie ist die Herkunft eines Kettenknotens nicht rekonstruierbar (D-96/B3).'
17621
18137
  };
17622
18138
  }
17623
- if (m.source_recovery_id !== void 0 && !istText2(m.source_recovery_id)) {
18139
+ if (m.source_recovery_id !== void 0 && !istText3(m.source_recovery_id)) {
17624
18140
  return { ok: false, message: '"source_recovery_id" ist gesetzt, aber leer.' };
17625
18141
  }
17626
18142
  if (!Array.isArray(m.links)) {
@@ -17645,7 +18161,7 @@ function checkDraftChainMeta(meta, type) {
17645
18161
  message: `"${l.field}" gehoert an einen ${kante.from}, nicht an einen ${type}.`
17646
18162
  };
17647
18163
  }
17648
- if (!istText2(l.target_element_id) || !istText2(l.target_draft_id)) {
18164
+ if (!istText3(l.target_element_id) || !istText3(l.target_draft_id)) {
17649
18165
  return {
17650
18166
  ok: false,
17651
18167
  message: `"links[${index}]" braucht target_element_id und target_draft_id.`
@@ -18292,12 +18808,12 @@ function createHfEventChannel(options) {
18292
18808
  // src/lib/szenen/resolve.ts
18293
18809
  var ResolveTechnicalError = class extends Error {
18294
18810
  };
18295
- var isObject10 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
18296
- var asArray2 = (v) => Array.isArray(v) ? v : [];
18811
+ var isObject11 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
18812
+ var asArray3 = (v) => Array.isArray(v) ? v : [];
18297
18813
  var str = (v) => typeof v === "string" ? v : null;
18298
18814
  var num = (v) => typeof v === "number" ? v : null;
18299
18815
  async function refOf(resolver, ref, expected, path) {
18300
- const versionId = isObject10(ref) ? str(ref.version_id) : null;
18816
+ const versionId = isObject11(ref) ? str(ref.version_id) : null;
18301
18817
  if (!versionId) {
18302
18818
  return {
18303
18819
  ok: false,
@@ -18349,8 +18865,11 @@ async function refOf(resolver, ref, expected, path) {
18349
18865
  }
18350
18866
  return { ok: true, version: version2 };
18351
18867
  }
18868
+ function ausgelassen(diagnostics, code, path, message) {
18869
+ diagnostics.push({ code, origin: "resolution", path, message });
18870
+ }
18352
18871
  function entraeumlichtePlatzierung(placement, assumptions) {
18353
- if (!isObject10(placement)) return null;
18872
+ if (!isObject11(placement)) return null;
18354
18873
  if (placement.mode !== "pattern") {
18355
18874
  assumptions.push(
18356
18875
  `Die Kameraplatzierung (mode "${str(placement.mode) ?? "unbekannt"}") ist metrisch und entfaellt ohne can_place. Der Renderer darf daraus keine Position ableiten.`
@@ -18365,7 +18884,7 @@ function entraeumlichtePlatzierung(placement, assumptions) {
18365
18884
  const entfallen = [];
18366
18885
  if (placement.offsets !== void 0) entfallen.push("offsets");
18367
18886
  const height = placement.height;
18368
- if (isObject10(height)) {
18887
+ if (isObject11(height)) {
18369
18888
  if (height.mode === "anchor") {
18370
18889
  const behalteneHoehe = {
18371
18890
  mode: "anchor",
@@ -18444,142 +18963,328 @@ async function resolveShotPayload(input) {
18444
18963
  chain
18445
18964
  } = input;
18446
18965
  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) {
18966
+ const diagnostics = [];
18967
+ if (!isObject11(shotPayload)) {
18457
18968
  return {
18458
18969
  kind: "rejected",
18459
18970
  failure: {
18460
18971
  class: "schema",
18461
18972
  where: "shot",
18462
- message: `Die Shot-Payload verletzt das Schema (${shotErrors.length} Befunde). Es wurde keine ihrer Referenzen aufgeloest.`,
18463
- errors: shotErrors
18973
+ message: `Die Shot-Payload ist kein Objekt, sondern ${Array.isArray(shotPayload) ? "ein Array" : typeof shotPayload}. Daraus laesst sich kein Auftrag herstellen.`,
18974
+ errors: []
18975
+ }
18976
+ };
18977
+ }
18978
+ const shot = shotPayload;
18979
+ for (const fehler4 of ohneKettenReferenz(
18980
+ validatePayload("shot", shotPayload).errors,
18981
+ chain ? "scene_setup_ref" : null
18982
+ )) {
18983
+ diagnostics.push(diagnosticOfSchemaError(fehler4, `Shot ${shotElementId}`));
18984
+ }
18985
+ let setup = null;
18986
+ let report = null;
18987
+ let canPlace = false;
18988
+ let capabilities;
18989
+ let scene;
18990
+ const nenntSetup = chain !== void 0 || shot.scene_setup_ref !== void 0;
18991
+ if (!nenntSetup) {
18992
+ return {
18993
+ kind: "rejected",
18994
+ failure: {
18995
+ class: "no_content",
18996
+ 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
18997
  }
18465
18998
  };
18466
18999
  }
18467
- const shot = shotVersion.payload;
18468
19000
  const setupRef = chain ? { ok: true, version: chain.setup } : await refOf(resolver, shot.scene_setup_ref, "scene_setup", "/scene_setup_ref");
18469
19001
  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) {
19002
+ if (!isObject11(setupRef.version.payload)) {
18475
19003
  return {
18476
19004
  kind: "rejected",
18477
19005
  failure: {
18478
19006
  class: "schema",
18479
19007
  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
19008
+ message: `Die Scene-Setup-Payload ist kein Objekt, sondern ${Array.isArray(setupRef.version.payload) ? "ein Array" : typeof setupRef.version.payload}.`,
19009
+ errors: []
18482
19010
  }
18483
19011
  };
18484
19012
  }
18485
- const setup = setupRef.version.payload;
18486
- const report = await capabilityReport({
19013
+ setup = {
19014
+ element_id: setupRef.version.element_id,
19015
+ version_id: setupRef.version.version_id,
19016
+ payload: setupRef.version.payload
19017
+ };
19018
+ report = await capabilityReport({
18487
19019
  type: "scene_setup",
18488
- setupPayload: setup,
18489
- elementId: setupRef.version.element_id,
19020
+ setupPayload: setup.payload,
19021
+ elementId: setup.element_id,
18490
19022
  resolver,
18491
19023
  // Bei einer Draft-Kette kommt die Szene von hier, nicht aus `scene_ref`
18492
19024
  // (C1). Dasselbe Regelwerk, ein anderer Weg zur selben Szene.
18493
19025
  ...chain ? { chainScene: chain.scene.payload } : {}
18494
19026
  });
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";
19027
+ diagnostics.push(...report.promptDiagnostics);
19028
+ canPlace = report.can_place.state === "yes";
18502
19029
  if (!canPlace) {
18503
19030
  assumptions.push(
18504
- "can_place ist nicht erfuellt \u2014 alle Platzierungen bleiben null. Der Renderer darf daraus keine Koordinaten ableiten (D-47)."
19031
+ `can_place ist "${report.can_place.state}" \u2014 alle Platzierungen bleiben null. Der Renderer darf daraus keine Koordinaten ableiten (D-47).`
18505
19032
  );
18506
19033
  }
18507
- const sceneRef = chain ? { ok: true, version: chain.scene } : await refOf(resolver, setup.scene_ref, "scene", "/scene_ref");
19034
+ capabilities = {
19035
+ can_describe_scene: report.can_describe_scene.state,
19036
+ can_render_prompt: report.can_render_prompt.state,
19037
+ can_place: report.can_place.state
19038
+ };
19039
+ const sceneRef = chain ? { ok: true, version: chain.scene } : await refOf(resolver, setup.payload.scene_ref, "scene", "/scene_ref");
18508
19040
  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) {
19041
+ if (!isObject11(sceneRef.version.payload)) {
18517
19042
  return {
18518
19043
  kind: "rejected",
18519
19044
  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"
19045
+ class: "schema",
19046
+ where: "scene",
19047
+ message: `Die Szenen-Payload ist kein Objekt, sondern ${Array.isArray(sceneRef.version.payload) ? "ein Array" : typeof sceneRef.version.payload}.`,
19048
+ errors: []
18525
19049
  }
18526
19050
  };
18527
19051
  }
18528
- const camera = cameras[cameraIndex];
18529
- const presetRef = await refOf(
19052
+ scene = {
19053
+ element_id: sceneRef.version.element_id,
19054
+ version_id: sceneRef.version.version_id,
19055
+ payload: sceneRef.version.payload
19056
+ };
19057
+ return komponiere({
19058
+ shot: { element_id: shotElementId, version_id: shotVersionId, payload: shot },
19059
+ setup,
19060
+ scene,
19061
+ report,
19062
+ capabilities,
19063
+ canPlace,
18530
19064
  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`
18544
- );
18545
- if (!patternRef.ok) return { kind: "rejected", failure: patternRef.failure };
18546
- pattern = patternRef.version.payload;
18547
- }
18548
- let shotConcept = null;
18549
- if (isObject10(shot.shot_concept_ref)) {
18550
- const conceptRef = await refOf(
18551
- resolver,
18552
- shot.shot_concept_ref,
18553
- "shot_concept",
18554
- "/shot_concept_ref"
18555
- );
18556
- if (!conceptRef.ok) return { kind: "rejected", failure: conceptRef.failure };
18557
- shotConcept = conceptRef.version.payload;
19065
+ contextChain,
19066
+ userInstruction,
19067
+ assumptions,
19068
+ diagnostics
19069
+ });
19070
+ }
19071
+ async function resolveScenePayload(input) {
19072
+ const {
19073
+ scenePayload,
19074
+ sceneElementId,
19075
+ sceneVersionId = "",
19076
+ resolver,
19077
+ contextChain,
19078
+ userInstruction
19079
+ } = input;
19080
+ if (!isObject11(scenePayload)) {
19081
+ return {
19082
+ kind: "rejected",
19083
+ failure: {
19084
+ class: "schema",
19085
+ where: "scene",
19086
+ message: `Die Szenen-Payload ist kein Objekt, sondern ${Array.isArray(scenePayload) ? "ein Array" : typeof scenePayload}.`,
19087
+ errors: []
19088
+ }
19089
+ };
18558
19090
  }
18559
- const styles = [];
18560
- for (const [ebene, refs] of [
18561
- ["scene_setup", asArray2(setup.style_refs)],
18562
- ["shot", asArray2(shot.style_refs)]
18563
- ]) {
18564
- for (const [i, ref] of refs.entries()) {
18565
- const resolved = await refOf(resolver, ref, "style", `/${ebene}/style_refs/${i}`);
18566
- if (!resolved.ok) return { kind: "rejected", failure: resolved.failure };
19091
+ const assumptions = [
19092
+ "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)."
19093
+ ];
19094
+ const diagnostics = [];
19095
+ for (const fehler4 of validatePayload("scene", scenePayload).errors) {
19096
+ diagnostics.push(diagnosticOfSchemaError(fehler4, `Szene ${sceneElementId}`));
19097
+ }
19098
+ const faehig = sceneOnlyCapabilities(scenePayload);
19099
+ return komponiere({
19100
+ shot: null,
19101
+ setup: null,
19102
+ scene: { element_id: sceneElementId, version_id: sceneVersionId, payload: scenePayload },
19103
+ report: null,
19104
+ capabilities: {
19105
+ can_describe_scene: faehig.can_describe_scene.state,
19106
+ can_render_prompt: faehig.can_render_prompt.state,
19107
+ can_place: faehig.can_place.state
19108
+ },
19109
+ canPlace: false,
19110
+ resolver,
19111
+ contextChain,
19112
+ userInstruction,
19113
+ assumptions,
19114
+ diagnostics
19115
+ });
19116
+ }
19117
+ async function komponiere(z) {
19118
+ const {
19119
+ shot,
19120
+ setup,
19121
+ scene,
19122
+ report,
19123
+ capabilities,
19124
+ canPlace,
19125
+ resolver,
19126
+ contextChain,
19127
+ userInstruction,
19128
+ assumptions,
19129
+ diagnostics
19130
+ } = z;
19131
+ const inhalt = describableContent(scene.payload);
19132
+ if (!inhalt.ok) {
19133
+ return {
19134
+ kind: "rejected",
19135
+ failure: {
19136
+ class: "no_content",
19137
+ message: `Szene ${scene.element_id}: ${inhalt.message}`
19138
+ }
19139
+ };
19140
+ }
19141
+ const eingefroreneFaehigkeiten = {
19142
+ ...capabilities,
19143
+ can_render_prompt: "yes"
19144
+ };
19145
+ const shotOverrides = shot && isObject11(shot.payload.overrides) ? shot.payload.overrides : {};
19146
+ const settings = shot && isObject11(shot.payload.prompt_render_settings) ? shot.payload.prompt_render_settings : {};
19147
+ let camera = null;
19148
+ if (shot && setup) {
19149
+ const cameraId = str(shot.payload.camera_id);
19150
+ const rig = isObject11(setup.payload.camera_rig) ? setup.payload.camera_rig : {};
19151
+ const cameras = asArray3(rig.cameras);
19152
+ const cameraIndex = cameraId === null ? -1 : cameras.findIndex((c) => isObject11(c) && c.camera_id === cameraId);
19153
+ if (cameraId === null) {
19154
+ ausgelassen(
19155
+ diagnostics,
19156
+ "P-01",
19157
+ "/camera_id",
19158
+ `Der Shot ${shot.element_id} nennt keine camera_id. Der Prompt entsteht ohne Kameravorgabe; eine Kamera wird nicht erfunden (D-75).`
19159
+ );
19160
+ } else if (cameraIndex < 0) {
19161
+ ausgelassen(
19162
+ diagnostics,
19163
+ "P-01",
19164
+ "/camera_id",
19165
+ `Die aktive camera_id "${cameraId}" kommt im Rig der Scene-Setup-Version ${setup.version_id} nicht vor. Der Prompt entsteht ohne Kameravorgabe.`
19166
+ );
19167
+ } else {
19168
+ const rohKamera = cameras[cameraIndex];
19169
+ let preset = null;
19170
+ const presetRef = await refOf(
19171
+ resolver,
19172
+ rohKamera.preset_ref,
19173
+ "camera_preset",
19174
+ `/camera_rig/cameras/${cameraIndex}/preset_ref`
19175
+ );
19176
+ if (presetRef.ok) preset = presetRef.version.payload;
19177
+ else {
19178
+ ausgelassen(
19179
+ diagnostics,
19180
+ "P-04",
19181
+ `/camera_rig/cameras/${cameraIndex}/preset_ref`,
19182
+ `${presetRef.failure.message} Die Kamera geht ohne Preset in den Prompt.`
19183
+ );
19184
+ }
19185
+ let pattern = null;
19186
+ const placementSpec = isObject11(rohKamera.placement) ? rohKamera.placement : null;
19187
+ if (placementSpec && isObject11(placementSpec.pattern_ref)) {
19188
+ const patternRef = await refOf(
19189
+ resolver,
19190
+ placementSpec.pattern_ref,
19191
+ "camera_pattern",
19192
+ `/camera_rig/cameras/${cameraIndex}/placement/pattern_ref`
19193
+ );
19194
+ if (patternRef.ok) pattern = patternRef.version.payload;
19195
+ else {
19196
+ ausgelassen(
19197
+ diagnostics,
19198
+ "P-04",
19199
+ `/camera_rig/cameras/${cameraIndex}/placement/pattern_ref`,
19200
+ `${patternRef.failure.message} Das Kameramuster entfaellt im Prompt.`
19201
+ );
19202
+ }
19203
+ }
19204
+ const cameraPlacement = canPlace ? rohKamera.placement ?? null : entraeumlichtePlatzierung(rohKamera.placement, assumptions);
19205
+ camera = {
19206
+ camera_id: cameraId,
19207
+ name: str(rohKamera.name),
19208
+ preset,
19209
+ placement: cameraPlacement,
19210
+ orientation: rohKamera.orientation ?? null,
19211
+ // Der Shot darf Bildgroesse und Fokus ueberschreiben (D-40) — das ist
19212
+ // die oberste Stufe der Vorrangkette.
19213
+ framing: shotOverrides.framing ?? rohKamera.framing ?? null,
19214
+ focus: shotOverrides.focus !== void 0 ? shotOverrides.focus : rohKamera.focus ?? null,
19215
+ pattern
19216
+ };
19217
+ }
19218
+ } else if (shot && !setup) {
19219
+ ausgelassen(
19220
+ diagnostics,
19221
+ "P-01",
19222
+ "/camera_id",
19223
+ "Ohne Scene Setup gibt es kein Camera Rig. Der Prompt entsteht ohne Kameravorgabe."
19224
+ );
19225
+ }
19226
+ let shotConcept = null;
19227
+ if (shot && isObject11(shot.payload.shot_concept_ref)) {
19228
+ const conceptRef = await refOf(
19229
+ resolver,
19230
+ shot.payload.shot_concept_ref,
19231
+ "shot_concept",
19232
+ "/shot_concept_ref"
19233
+ );
19234
+ if (conceptRef.ok) shotConcept = conceptRef.version.payload;
19235
+ else {
19236
+ ausgelassen(
19237
+ diagnostics,
19238
+ "P-04",
19239
+ "/shot_concept_ref",
19240
+ `${conceptRef.failure.message} Das Shot Concept entfaellt im Prompt.`
19241
+ );
19242
+ }
19243
+ }
19244
+ const styles = [];
19245
+ for (const [ebene, refs] of [
19246
+ ["scene_setup", setup ? asArray3(setup.payload.style_refs) : []],
19247
+ ["shot", shot ? asArray3(shot.payload.style_refs) : []]
19248
+ ]) {
19249
+ for (const [i, ref] of refs.entries()) {
19250
+ const resolved = await refOf(resolver, ref, "style", `/${ebene}/style_refs/${i}`);
19251
+ if (!resolved.ok) {
19252
+ ausgelassen(
19253
+ diagnostics,
19254
+ "P-04",
19255
+ `/${ebene}/style_refs/${i}`,
19256
+ `${resolved.failure.message} Dieser Stil entfaellt im Prompt.`
19257
+ );
19258
+ continue;
19259
+ }
18567
19260
  const payload = resolved.version.payload;
18568
19261
  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),
19262
+ role: str(isObject11(ref) ? ref.role : null) ?? str(payload.role) ?? "unspecified",
19263
+ rank: num(isObject11(ref) ? ref.rank : null) ?? 0,
19264
+ weight: num(isObject11(ref) ? ref.weight : null),
18572
19265
  text: str(payload.text) ?? "",
18573
19266
  locale: str(payload.locale)
18574
19267
  });
18575
19268
  }
18576
19269
  }
18577
19270
  styles.sort((a, b) => a.rank - b.rank);
18578
- const settings = isObject10(shot.prompt_render_settings) ? shot.prompt_render_settings : {};
18579
19271
  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 };
19272
+ for (const [i, ref] of asArray3(settings.policy_refs).entries()) {
19273
+ const resolved = await refOf(
19274
+ resolver,
19275
+ ref,
19276
+ "prompt_policy",
19277
+ `/prompt_render_settings/policy_refs/${i}`
19278
+ );
19279
+ if (!resolved.ok) {
19280
+ ausgelassen(
19281
+ diagnostics,
19282
+ "P-04",
19283
+ `/prompt_render_settings/policy_refs/${i}`,
19284
+ `${resolved.failure.message} Diese Policy entfaellt im Prompt.`
19285
+ );
19286
+ continue;
19287
+ }
18583
19288
  const payload = resolved.version.payload;
18584
19289
  policies.push({
18585
19290
  subtype: str(payload.subtype) ?? "general",
@@ -18590,14 +19295,22 @@ async function resolveShotPayload(input) {
18590
19295
  }
18591
19296
  policies.sort((a, b) => a.rank - b.rank);
18592
19297
  const fragments = [];
18593
- for (const [i, ref] of asArray2(settings.fragment_refs).entries()) {
19298
+ for (const [i, ref] of asArray3(settings.fragment_refs).entries()) {
18594
19299
  const resolved = await refOf(
18595
19300
  resolver,
18596
19301
  ref,
18597
19302
  "prompt_fragment",
18598
19303
  `/prompt_render_settings/fragment_refs/${i}`
18599
19304
  );
18600
- if (!resolved.ok) return { kind: "rejected", failure: resolved.failure };
19305
+ if (!resolved.ok) {
19306
+ ausgelassen(
19307
+ diagnostics,
19308
+ "P-04",
19309
+ `/prompt_render_settings/fragment_refs/${i}`,
19310
+ `${resolved.failure.message} Dieses Fragment entfaellt im Prompt.`
19311
+ );
19312
+ continue;
19313
+ }
18601
19314
  const payload = resolved.version.payload;
18602
19315
  fragments.push({
18603
19316
  channel: str(payload.channel) ?? "positive",
@@ -18607,83 +19320,102 @@ async function resolveShotPayload(input) {
18607
19320
  });
18608
19321
  }
18609
19322
  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) {
19323
+ let targetModelPayload = null;
19324
+ let targetModelResolution = null;
19325
+ const key = shot ? str(settings.target_model_id) : null;
19326
+ if (key === null) {
19327
+ ausgelassen(
19328
+ diagnostics,
19329
+ "P-17",
19330
+ "/prompt_render_settings/target_model_id",
19331
+ 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."
19332
+ );
19333
+ } else if (!resolver.resolveTargetModel) {
18622
19334
  throw new ResolveTechnicalError(
18623
19335
  `Der Resolver bietet keine Zielmodellsuche an (kein ContextChainPort). Ein Lauf ohne Kontextkette wuerde ein Zielmodell melden, das nicht das gesuchte ist.`
18624
19336
  );
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
19337
+ } else {
19338
+ const targetModel = await resolver.resolveTargetModel(key, shot.element_id);
19339
+ if (targetModel.kind !== "resolved") {
19340
+ ausgelassen(
19341
+ diagnostics,
19342
+ "P-17",
19343
+ "/prompt_render_settings/target_model_id",
19344
+ `${describeTargetModelFailure(targetModel)} Der Prompt wird generisch abgeleitet, ohne Modelloptimierung.`
19345
+ );
19346
+ } else {
19347
+ targetModelPayload = targetModel.payload;
19348
+ targetModelResolution = {
19349
+ element_id: targetModel.elementId,
19350
+ version_id: targetModel.versionId,
19351
+ chain_index: targetModel.chainIndex,
19352
+ head: targetModel.head,
19353
+ shadowed_retired: targetModel.shadowedRetired.map((entry) => ({
19354
+ element_id: entry.elementId,
19355
+ context_id: entry.contextId
19356
+ }))
19357
+ };
19358
+ for (const uebergangen of targetModel.shadowedRetired) {
19359
+ assumptions.push(
19360
+ `Ein stillgelegtes target_model (${uebergangen.elementId}, Kontext ${uebergangen.contextId ?? "global"}) wurde uebergangen; verwendet wird ${targetModel.elementId} von Kettenglied ${targetModel.chainIndex}.`
19361
+ );
18634
19362
  }
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
- );
19363
+ if (targetModel.head.kind === "selected" && targetModel.head.stale) {
19364
+ assumptions.push(
19365
+ `Die Auswahl des Zielmodell-Heads (${targetModel.versionId}) ist veraltet \u2014 es gibt neuere Heads. Die Auswahl gilt, weil sie getroffen wurde (D-36).`
19366
+ );
19367
+ }
19368
+ }
18646
19369
  }
18647
19370
  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);
19371
+ for (const entry of setup ? asArray3(setup.payload.node_overrides) : []) {
19372
+ if (isObject11(entry) && typeof entry.node_id === "string") overrides.set(entry.node_id, entry);
18650
19373
  }
18651
19374
  const placements = /* @__PURE__ */ new Map();
18652
- for (const entry of asArray2(setup.placements)) {
18653
- if (!isObject10(entry) || typeof entry.node_id !== "string") continue;
19375
+ for (const entry of setup ? asArray3(setup.payload.placements) : []) {
19376
+ if (!isObject11(entry) || typeof entry.node_id !== "string") continue;
18654
19377
  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) } : {}
19378
+ position: asArray3(entry.position),
19379
+ ...entry.rotation_euler_deg ? { rotation_euler_deg: asArray3(entry.rotation_euler_deg) } : {},
19380
+ ...entry.scale ? { scale: asArray3(entry.scale) } : {}
18658
19381
  });
18659
19382
  }
18660
- const shotOverrides = isObject10(shot.overrides) ? shot.overrides : {};
18661
- const hidden = new Set(asArray2(shotOverrides.hidden_nodes).filter((v) => typeof v === "string"));
19383
+ const hidden = new Set(
19384
+ asArray3(shotOverrides.hidden_nodes).filter((v) => typeof v === "string")
19385
+ );
18662
19386
  const nodes = [];
18663
- for (const raw of asArray2(scene.nodes)) {
18664
- if (!isObject10(raw) || typeof raw.node_id !== "string") continue;
19387
+ for (const raw of asArray3(scene.payload.nodes)) {
19388
+ if (!isObject11(raw) || typeof raw.node_id !== "string") continue;
18665
19389
  const nodeId = raw.node_id;
18666
19390
  const contributions = [];
18667
- for (const [i, ref] of asArray2(raw.component_refs).entries()) {
19391
+ for (const [i, ref] of asArray3(raw.component_refs).entries()) {
18668
19392
  const resolved = await refOf(
18669
19393
  resolver,
18670
19394
  ref,
18671
19395
  "scene_component",
18672
19396
  `/nodes/${nodeId}/component_refs/${i}`
18673
19397
  );
18674
- if (!resolved.ok) return { kind: "rejected", failure: resolved.failure };
19398
+ if (!resolved.ok) {
19399
+ ausgelassen(
19400
+ diagnostics,
19401
+ "P-05",
19402
+ `/nodes/${nodeId}/component_refs/${i}`,
19403
+ `${resolved.failure.message} Dieser Beitrag entfaellt; der Knoten bleibt mit seinen uebrigen Beitraegen im Prompt.`
19404
+ );
19405
+ continue;
19406
+ }
18675
19407
  const payload = resolved.version.payload;
18676
19408
  contributions.push({
18677
19409
  source: "component",
18678
19410
  element_id: resolved.version.element_id,
18679
19411
  version_id: resolved.version.version_id,
18680
- role: str(isObject10(ref) ? ref.role : null),
18681
- rank: num(isObject10(ref) ? ref.rank : null) ?? 0,
19412
+ role: str(isObject11(ref) ? ref.role : null),
19413
+ rank: num(isObject11(ref) ? ref.rank : null) ?? 0,
18682
19414
  locale: str(payload.locale),
18683
19415
  text: str(payload.text) ?? ""
18684
19416
  });
18685
19417
  }
18686
- const semantic = isObject10(raw.semantic) ? raw.semantic : null;
19418
+ const semantic = isObject11(raw.semantic) ? raw.semantic : null;
18687
19419
  if (semantic && str(semantic.description)) {
18688
19420
  contributions.push({
18689
19421
  source: "node_semantic",
@@ -18697,12 +19429,12 @@ async function resolveShotPayload(input) {
18697
19429
  });
18698
19430
  }
18699
19431
  const override = overrides.get(nodeId);
18700
- const overrideSemantic = override && isObject10(override.semantic) ? override.semantic : null;
18701
- if (overrideSemantic && str(overrideSemantic.description)) {
19432
+ const overrideSemantic = override && isObject11(override.semantic) ? override.semantic : null;
19433
+ if (overrideSemantic && str(overrideSemantic.description) && setup) {
18702
19434
  contributions.push({
18703
19435
  source: "setup_override",
18704
- element_id: setupRef.version.element_id,
18705
- version_id: setupRef.version.version_id,
19436
+ element_id: setup.element_id,
19437
+ version_id: setup.version_id,
18706
19438
  role: null,
18707
19439
  rank: Number.MAX_SAFE_INTEGER,
18708
19440
  locale: null,
@@ -18711,8 +19443,8 @@ async function resolveShotPayload(input) {
18711
19443
  }
18712
19444
  contributions.sort((a, b) => a.rank - b.rank);
18713
19445
  const properties = {
18714
- ...isObject10(semantic?.properties) ? semantic.properties : {},
18715
- ...isObject10(overrideSemantic?.properties) ? overrideSemantic.properties : {}
19446
+ ...isObject11(semantic?.properties) ? semantic.properties : {},
19447
+ ...isObject11(overrideSemantic?.properties) ? overrideSemantic.properties : {}
18716
19448
  };
18717
19449
  nodes.push({
18718
19450
  node_id: nodeId,
@@ -18723,9 +19455,6 @@ async function resolveShotPayload(input) {
18723
19455
  hidden: hidden.has(nodeId)
18724
19456
  });
18725
19457
  }
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
19458
  const shotInstruction = str(settings.user_instruction);
18730
19459
  if (userInstruction && shotInstruction && userInstruction !== shotInstruction) {
18731
19460
  assumptions.push(
@@ -18739,92 +19468,40 @@ async function resolveShotPayload(input) {
18739
19468
  kind: "resolved",
18740
19469
  report,
18741
19470
  input: {
18742
- shot: {
18743
- version_id: shotVersion.version_id,
18744
- element_id: shotVersion.element_id,
18745
- camera_id: cameraId,
19471
+ shot: shot ? {
19472
+ version_id: shot.version_id,
19473
+ element_id: shot.element_id,
19474
+ camera_id: camera ? camera.camera_id : null,
18746
19475
  shot_concept: shotConcept,
18747
19476
  // Der Aufrufer darf die im Shot hinterlegte Anweisung ueberschreiben
18748
19477
  // (§4.1). Beide Herkuenfte sind unterschiedliche Sachverhalte, deshalb
18749
19478
  // wird der Fall in `assumptions` benannt statt still ersetzt.
18750
- user_instruction: userInstruction ?? str(settings.user_instruction),
19479
+ user_instruction: userInstruction ?? shotInstruction,
18751
19480
  text_override: str(shotOverrides.text)
18752
- },
19481
+ } : null,
19482
+ setup: setup ? { version_id: setup.version_id, element_id: setup.element_id } : null,
18753
19483
  scene: {
18754
- version_id: sceneRef.version.version_id,
19484
+ element_id: scene.element_id,
19485
+ version_id: scene.version_id,
18755
19486
  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
19487
+ relations: asArray3(scene.payload.relations),
19488
+ environment: scene.payload.environment ?? null,
19489
+ unit_system: scene.payload.unit_system ?? null
18769
19490
  },
19491
+ camera,
18770
19492
  styles,
18771
19493
  policies,
18772
19494
  fragments,
18773
- target_model: targetModel.payload,
19495
+ target_model: targetModelPayload,
18774
19496
  localization_notes: [],
18775
19497
  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
- },
19498
+ diagnostics,
19499
+ capabilities: eingefroreneFaehigkeiten,
18781
19500
  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
- }
19501
+ target_model_resolution: targetModelResolution
18792
19502
  }
18793
19503
  };
18794
19504
  }
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
19505
 
18829
19506
  // src/lib/szenen/freezeDraftChain.ts
18830
19507
  function ladeKnoten(sicht, elementId, erwarteterTyp) {
@@ -18960,9 +19637,117 @@ async function freezeDraftChain(input) {
18960
19637
  ...shot.chain.source_recovery_id ? { sourceRecoveryId: shot.chain.source_recovery_id } : {}
18961
19638
  };
18962
19639
  }
19640
+ function ladeSzene(sicht, elementId, contextChain) {
19641
+ const gelesen = sicht.getElement(elementId);
19642
+ if (gelesen.kind !== "ok") {
19643
+ return {
19644
+ fehler: gelesen.message ?? `Element ${elementId} konnte nicht gelesen werden.`,
19645
+ technisch: true
19646
+ };
19647
+ }
19648
+ const snapshot = gelesen.value;
19649
+ if (!snapshot) return { fehler: `Element ${elementId} ist unbekannt.` };
19650
+ if (snapshot.element.type !== "scene") {
19651
+ return {
19652
+ fehler: `Element ${elementId} ist vom Typ "${snapshot.element.type}", erwartet "scene".`
19653
+ };
19654
+ }
19655
+ if (snapshot.draftFork) {
19656
+ return {
19657
+ fehler: `Element ${elementId} hat parallele Entwuerfe. Solange sie nicht aufgeloest sind, gibt es keinen eindeutigen Arbeitsstand (D-29).`
19658
+ };
19659
+ }
19660
+ if (snapshot.draft) {
19661
+ return {
19662
+ element_id: elementId,
19663
+ draft_id: snapshot.draft.draft_id,
19664
+ revision: snapshot.draft.revision,
19665
+ version_id: null,
19666
+ payload: snapshot.draft.payload
19667
+ };
19668
+ }
19669
+ const versionen = snapshot.versions ?? [];
19670
+ if (versionen.length === 0) {
19671
+ return {
19672
+ fehler: `Element ${elementId} hat weder einen Entwurf noch eine veroeffentlichte Version. Es gibt nichts einzufrieren.`
19673
+ };
19674
+ }
19675
+ const kopf = effectivePreferredHeadSafe(
19676
+ { element: snapshot.element, versions: versionen, preferredHeads: snapshot.preferredHeads },
19677
+ contextChain
19678
+ );
19679
+ if (kopf.kind === "dag_fehler") {
19680
+ return { fehler: `Der Versionsgraph von Element ${elementId} ist nicht aufbaubar: ${kopf.message}` };
19681
+ }
19682
+ const versionId = usableHeadVersionId(resolutionOf(kopf));
19683
+ if (!versionId) {
19684
+ return {
19685
+ 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.`
19686
+ };
19687
+ }
19688
+ const version2 = versionen.find((v) => v.version_id === versionId);
19689
+ if (!version2) {
19690
+ return {
19691
+ fehler: `Die wirksame Version ${versionId} von Element ${elementId} steht nicht in der gelesenen Momentaufnahme.`
19692
+ };
19693
+ }
19694
+ return {
19695
+ element_id: elementId,
19696
+ draft_id: null,
19697
+ revision: null,
19698
+ version_id: versionId,
19699
+ payload: version2.payload
19700
+ };
19701
+ }
19702
+ async function freezeSceneWorkingState(input) {
19703
+ const { sceneElementId, store, resolver, contextChain, userInstruction } = input;
19704
+ const a = await store.readSnapshot({ fresh: false });
19705
+ const szene = ladeSzene(a, sceneElementId, contextChain);
19706
+ if ("fehler" in szene) return ausfallErgebnis(szene);
19707
+ const b = await store.readSnapshot({ fresh: true });
19708
+ const erneut = ladeSzene(b, sceneElementId, contextChain);
19709
+ if ("fehler" in erneut) return ausfallErgebnis(erneut);
19710
+ if (erneut.draft_id !== szene.draft_id || erneut.revision !== szene.revision || erneut.version_id !== szene.version_id) {
19711
+ return {
19712
+ kind: "conflict",
19713
+ 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.`,
19714
+ node: { type: "scene", element_id: sceneElementId, draft_id: szene.draft_id ?? "" },
19715
+ expectedRevision: szene.revision ?? 0,
19716
+ actualRevision: erneut.revision ?? 0
19717
+ };
19718
+ }
19719
+ const aufgeloest = await resolveScenePayload({
19720
+ scenePayload: szene.payload,
19721
+ sceneElementId: szene.element_id,
19722
+ // Ein Entwurf hat keine Version — und bekommt hier auch keine erfundene
19723
+ // (D-98/D-75).
19724
+ sceneVersionId: szene.version_id ?? "",
19725
+ resolver,
19726
+ contextChain,
19727
+ userInstruction
19728
+ });
19729
+ if (aufgeloest.kind !== "resolved") {
19730
+ return {
19731
+ kind: "rejected",
19732
+ message: aufgeloest.failure.message,
19733
+ failure: aufgeloest.failure
19734
+ };
19735
+ }
19736
+ return {
19737
+ kind: "ok",
19738
+ scene: {
19739
+ element_id: szene.element_id,
19740
+ draft_id: szene.draft_id,
19741
+ revision: szene.revision,
19742
+ version_id: szene.version_id
19743
+ },
19744
+ input: aufgeloest.input
19745
+ };
19746
+ }
18963
19747
 
18964
19748
  // src/lib/szenen/promptRun.ts
18965
19749
  function buildSystemInstruction2(input) {
19750
+ const ohneZielmodell = input.target_model === null || input.target_model === void 0;
18966
19751
  const model = input.target_model ?? {};
18967
19752
  const sprache = typeof model.prompt_language === "string" ? model.prompt_language : "en";
18968
19753
  const negativ = model.supports_negative_prompt === true;
@@ -18983,7 +19768,11 @@ function buildSystemInstruction2(input) {
18983
19768
  "",
18984
19769
  `Der Prompt wird in der Sprache "${sprache}" formuliert.`
18985
19770
  ];
18986
- if (!negativ) {
19771
+ if (ohneZielmodell) {
19772
+ zeilen.push(
19773
+ '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".'
19774
+ );
19775
+ } else if (!negativ) {
18987
19776
  zeilen.push(
18988
19777
  'Das Zielmodell kennt KEIN Negativ-Prompt-Feld. "negative" bleibt null; was mit negativen Fragmenten geschehen ist, gehoert in "notes".'
18989
19778
  );
@@ -19012,6 +19801,14 @@ function buildSystemInstruction2(input) {
19012
19801
  'Widersprechen sie einander, nenne den Widerspruch mit beiden Quellen in "notes" \u2014',
19013
19802
  "waehle nicht still eine Seite."
19014
19803
  );
19804
+ if (input.camera === null) {
19805
+ zeilen.push(
19806
+ "",
19807
+ "Es ist KEINE Kamera angegeben. Erfinde keine Brennweite, keinen Bildausschnitt",
19808
+ "und keine Kameraposition. Wo eine Kameraaussage noetig waere, formuliere sie",
19809
+ 'als Annahme und nenne sie in "notes".'
19810
+ );
19811
+ }
19015
19812
  if (input.capabilities.can_place !== "yes") {
19016
19813
  zeilen.push(
19017
19814
  "",
@@ -19025,7 +19822,7 @@ function buildSystemInstruction2(input) {
19025
19822
  function buildUserPrompt2(input) {
19026
19823
  return JSON.stringify(input, null, 2);
19027
19824
  }
19028
- var isObject11 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
19825
+ var isObject12 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
19029
19826
  function parseRunResponse(raw) {
19030
19827
  const text = raw.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "").trim();
19031
19828
  if (text.length === 0) return { ok: false, message: "Die Antwort war leer." };
@@ -19039,7 +19836,7 @@ function parseRunResponse(raw) {
19039
19836
  }
19040
19837
  }
19041
19838
  function checkRunResponse(value, input) {
19042
- if (!isObject11(value)) {
19839
+ if (!isObject12(value)) {
19043
19840
  return { ok: false, message: `Die Antwort ist kein Objekt, sondern ${Array.isArray(value) ? "array" : typeof value}.` };
19044
19841
  }
19045
19842
  if (typeof value.positive !== "string" || value.positive.trim().length === 0) {
@@ -19057,11 +19854,11 @@ function checkRunResponse(value, input) {
19057
19854
  if (negative !== null && !negativErlaubt) {
19058
19855
  return {
19059
19856
  ok: false,
19060
- message: `Das Zielmodell fuehrt kein Negativ-Prompt-Feld (supports_negative_prompt = false), die Antwort liefert aber eines.`
19857
+ 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
19858
  };
19062
19859
  }
19063
19860
  const visibility = value.visibility;
19064
- if (!isObject11(visibility)) {
19861
+ if (!isObject12(visibility)) {
19065
19862
  return { ok: false, message: '"visibility" fehlt \u2014 die Pflichtdiagnosen aus D-21.' };
19066
19863
  }
19067
19864
  if (typeof visibility.basis !== "string") {
@@ -19073,7 +19870,7 @@ function checkRunResponse(value, input) {
19073
19870
  const genannt = /* @__PURE__ */ new Set();
19074
19871
  const nodes = [];
19075
19872
  for (const eintrag of visibility.nodes) {
19076
- if (!isObject11(eintrag) || typeof eintrag.node_id !== "string" || typeof eintrag.state !== "string") {
19873
+ if (!isObject12(eintrag) || typeof eintrag.node_id !== "string" || typeof eintrag.state !== "string") {
19077
19874
  return { ok: false, message: 'Ein Eintrag in "visibility.nodes" hat keine node_id oder keinen state.' };
19078
19875
  }
19079
19876
  genannt.add(eintrag.node_id);
@@ -19218,6 +20015,23 @@ function createEventLogRunPersistence(channel) {
19218
20015
  return queryError(error, `Laeufe der Kette ${chainId} konnten nicht gelesen werden`);
19219
20016
  }
19220
20017
  },
20018
+ async listForScene(sceneElementId) {
20019
+ try {
20020
+ const events = await channel.readEvents();
20021
+ return {
20022
+ kind: "ok",
20023
+ value: collectPromptRuns(events).filter((run) => {
20024
+ const quelle = sourceOf(run);
20025
+ return quelle.kind === "scene_direct" && quelle.scene.element_id === sceneElementId;
20026
+ })
20027
+ };
20028
+ } catch (error) {
20029
+ return queryError(
20030
+ error,
20031
+ `Laeufe der Szene ${sceneElementId} konnten nicht gelesen werden`
20032
+ );
20033
+ }
20034
+ },
19221
20035
  async record(record) {
19222
20036
  try {
19223
20037
  await channel.appendEvent(SZENEN_EVENT_TYPES.promptRunRecorded, record);
@@ -19304,13 +20118,21 @@ function createClientRunPort(options) {
19304
20118
  return { kind: "error", message: resolved.failure.message, retryable: false };
19305
20119
  }
19306
20120
  const outcome = await runPromptDerivation(resolved.input, text);
20121
+ if (!resolved.input.shot) {
20122
+ return {
20123
+ kind: "error",
20124
+ message: `Die Aufloesung der Shot-Version ${input.shotVersionId} lieferte eine Eingabe ohne Shot. Das ist ein Programmfehler, kein Lauf.`,
20125
+ retryable: false
20126
+ };
20127
+ }
19307
20128
  const record = {
19308
20129
  run_id: newRunId(),
19309
20130
  shot_version_id: input.shotVersionId,
19310
20131
  shot_element_id: resolved.input.shot.element_id,
19311
20132
  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,
20133
+ // Leer heisst „kein Wert", nicht „Wert unbekannt" (D-75/D-135).
20134
+ camera_id: resolved.input.shot.camera_id ?? "",
20135
+ target_model_version_id: resolved.input.target_model_resolution?.version_id ?? "",
19314
20136
  provider: outcome.provider,
19315
20137
  model_id: outcome.model_id,
19316
20138
  system_prompt: outcome.system_prompt,
@@ -19382,8 +20204,8 @@ function createClientRunPort(options) {
19382
20204
  shot_version_id: "",
19383
20205
  shot_element_id: input.shotElementId,
19384
20206
  scene_setup_version_id: "",
19385
- camera_id: eingefroren.input.shot.camera_id,
19386
- target_model_version_id: eingefroren.input.target_model_resolution.version_id,
20207
+ camera_id: eingefroren.input.shot?.camera_id ?? "",
20208
+ target_model_version_id: eingefroren.input.target_model_resolution?.version_id ?? "",
19387
20209
  provider: outcome.provider,
19388
20210
  model_id: outcome.model_id,
19389
20211
  system_prompt: outcome.system_prompt,
@@ -19417,6 +20239,95 @@ function createClientRunPort(options) {
19417
20239
  async listRunsForChain(chainId) {
19418
20240
  const alle = await runs.listForChain(chainId);
19419
20241
  return alle;
20242
+ },
20243
+ // ── Der Lauf direkt aus einer Szene (D-135) ───────────────────────────
20244
+ //
20245
+ // Bewusst **derselbe** Ablauf wie oben: Kontext ermitteln, Kette
20246
+ // aufloesen, einfrieren, dann erst rufen. Was sich unterscheidet, ist
20247
+ // ausschliesslich, **was** eingefroren wird — ein Szenen-Arbeitsstand
20248
+ // statt einer Drei-Knoten-Kette.
20249
+ async startRunFromScene(input) {
20250
+ try {
20251
+ let contextId = input.contextId;
20252
+ if (contextId === void 0) {
20253
+ const element = await elements.getElement(input.sceneElementId);
20254
+ if (element.kind !== "ok") {
20255
+ return { kind: "error", message: element.message, retryable: element.retryable };
20256
+ }
20257
+ if (element.value === null) {
20258
+ return {
20259
+ kind: "error",
20260
+ message: `Element ${input.sceneElementId} ist unbekannt.`,
20261
+ retryable: false
20262
+ };
20263
+ }
20264
+ contextId = element.value.element.context_id;
20265
+ }
20266
+ const chain = await context.chainFor(contextId ?? null);
20267
+ if (chain.kind !== "ok") {
20268
+ return { kind: "error", message: chain.message, retryable: chain.retryable };
20269
+ }
20270
+ const eingefroren = await freezeSceneWorkingState({
20271
+ sceneElementId: input.sceneElementId,
20272
+ store: elements,
20273
+ resolver,
20274
+ contextChain: chain.value,
20275
+ userInstruction: input.userInstruction ?? null
20276
+ });
20277
+ if (eingefroren.kind === "conflict") {
20278
+ return {
20279
+ kind: "conflict",
20280
+ message: eingefroren.message,
20281
+ node: eingefroren.node,
20282
+ expectedRevision: eingefroren.expectedRevision,
20283
+ actualRevision: eingefroren.actualRevision
20284
+ };
20285
+ }
20286
+ if (eingefroren.kind === "error") {
20287
+ return { kind: "error", message: eingefroren.message, retryable: eingefroren.retryable };
20288
+ }
20289
+ if (eingefroren.kind !== "ok") {
20290
+ return { kind: "rejected", message: eingefroren.message };
20291
+ }
20292
+ const outcome = await runPromptDerivation(eingefroren.input, text);
20293
+ const record = {
20294
+ run_id: newRunId(),
20295
+ // Keine fingierten Kennungen (D-98/D-75). Was es nicht gibt, bleibt
20296
+ // leer — die Herkunft steht in `source`.
20297
+ shot_version_id: "",
20298
+ shot_element_id: "",
20299
+ scene_setup_version_id: "",
20300
+ camera_id: "",
20301
+ target_model_version_id: eingefroren.input.target_model_resolution?.version_id ?? "",
20302
+ provider: outcome.provider,
20303
+ model_id: outcome.model_id,
20304
+ system_prompt: outcome.system_prompt,
20305
+ input_payload: eingefroren.input,
20306
+ status: outcome.status,
20307
+ failure: outcome.status === "failed" ? outcome.failure : null,
20308
+ output: outcome.status === "succeeded" ? outcome.output : null,
20309
+ raw_output: outcome.raw_output,
20310
+ source: { kind: "scene_direct", scene: eingefroren.scene },
20311
+ created_at: now().toISOString()
20312
+ };
20313
+ return await runs.record(record);
20314
+ } catch (error) {
20315
+ return {
20316
+ kind: "error",
20317
+ message: `Prompt-Lauf aus der Szene ${input.sceneElementId} ist fehlgeschlagen: ${error instanceof Error ? error.message : String(error)}`,
20318
+ retryable: true
20319
+ };
20320
+ }
20321
+ },
20322
+ async listRunsForScene(sceneElementId) {
20323
+ try {
20324
+ return await runs.listForScene(sceneElementId);
20325
+ } catch (error) {
20326
+ return queryError(
20327
+ error,
20328
+ `Laeufe der Szene ${sceneElementId} konnten nicht gelesen werden`
20329
+ );
20330
+ }
19420
20331
  }
19421
20332
  };
19422
20333
  }
@@ -19450,6 +20361,7 @@ function createHfImageStore(options) {
19450
20361
  const basisMs = options.leseBasisMs ?? 400;
19451
20362
  const schlafen2 = options.schlafen ?? warten;
19452
20363
  const abgelegt = /* @__PURE__ */ new Map();
20364
+ const vorschauen = /* @__PURE__ */ new Map();
19453
20365
  const bekanntePfade = /* @__PURE__ */ new Set();
19454
20366
  let gefalteterBestand = null;
19455
20367
  let laufenderAbgleich = null;
@@ -19473,7 +20385,7 @@ function createHfImageStore(options) {
19473
20385
  }
19474
20386
  return laufenderAbgleich;
19475
20387
  }
19476
- return {
20388
+ const speicher = {
19477
20389
  bestand: bestandAbgleichen,
19478
20390
  async putWithId(id, base64, mimeType, titel) {
19479
20391
  const echterTyp = typAusBytes(base64, mimeType);
@@ -19492,6 +20404,38 @@ function createHfImageStore(options) {
19492
20404
  }, []);
19493
20405
  abgelegt.set(id, { data: base64, mimeType: echterTyp });
19494
20406
  },
20407
+ async preview(id) {
20408
+ const bekannt = vorschauen.get(id);
20409
+ if (bekannt) return bekannt;
20410
+ const eben = abgelegt.get(id);
20411
+ if (eben) {
20412
+ const quelle2 = `data:${eben.mimeType};base64,${eben.data}`;
20413
+ vorschauen.set(id, quelle2);
20414
+ return quelle2;
20415
+ }
20416
+ const zustand = await bestandAbgleichen();
20417
+ const eintrag = zustand.metadata.find((e) => e.id === id);
20418
+ if (eintrag?.hasThumb && eintrag.filename) {
20419
+ const klein = await hfLoadImageAsBase64(
20420
+ id,
20421
+ token,
20422
+ void 0,
20423
+ eintrag.filename,
20424
+ void 0,
20425
+ eintrag.mimeType,
20426
+ true
20427
+ );
20428
+ if (klein) {
20429
+ const quelle2 = `data:${eintrag.mimeType || "image/jpeg"};base64,${klein}`;
20430
+ vorschauen.set(id, quelle2);
20431
+ return quelle2;
20432
+ }
20433
+ }
20434
+ const voll = await speicher.resolve(id);
20435
+ const quelle = `data:${voll.mimeType};base64,${voll.data}`;
20436
+ vorschauen.set(id, quelle);
20437
+ return quelle;
20438
+ },
19495
20439
  async resolve(id, mimeType) {
19496
20440
  const eben = abgelegt.get(id);
19497
20441
  if (eben) return eben;
@@ -19529,6 +20473,47 @@ function createHfImageStore(options) {
19529
20473
  );
19530
20474
  }
19531
20475
  };
20476
+ return speicher;
20477
+ }
20478
+
20479
+ // src/lib/szenen/libraryItems.ts
20480
+ var LOCATOR = /^(.+)-(\d+)$/;
20481
+ function locatorTeile(mediaRef) {
20482
+ const treffer = LOCATOR.exec(mediaRef);
20483
+ if (!treffer) return { stamm: mediaRef, position: null };
20484
+ return { stamm: treffer[1], position: Number(treffer[2]) };
20485
+ }
20486
+ function gruppiereZuItems(bilder) {
20487
+ const gruppen = /* @__PURE__ */ new Map();
20488
+ for (const bild of bilder) {
20489
+ const { stamm } = locatorTeile(bild.mediaRef);
20490
+ const vorhanden = gruppen.get(stamm);
20491
+ if (vorhanden) vorhanden.push(bild);
20492
+ else gruppen.set(stamm, [bild]);
20493
+ }
20494
+ const items = [...gruppen.entries()].map(([stamm, mitglieder]) => {
20495
+ const sortiert = [...mitglieder].sort((a, b) => {
20496
+ const pa = locatorTeile(a.mediaRef).position ?? 0;
20497
+ const pb = locatorTeile(b.mediaRef).position ?? 0;
20498
+ return pa - pb;
20499
+ });
20500
+ const erstes = sortiert[0];
20501
+ const itemRef = sortiert.length === 1 ? erstes.mediaRef : stamm;
20502
+ const juengstes = sortiert.reduce(
20503
+ (max, b) => Math.max(max, b.timestamp ?? 0),
20504
+ 0
20505
+ );
20506
+ return {
20507
+ item: {
20508
+ itemRef,
20509
+ title: erstes.itemTitle?.trim() || erstes.title || itemRef,
20510
+ images: sortiert.map(({ timestamp: _t, itemTitle: _i, ...bild }) => bild)
20511
+ },
20512
+ juengstes
20513
+ };
20514
+ });
20515
+ items.sort((a, b) => b.juengstes - a.juengstes);
20516
+ return items.map((e) => e.item);
19532
20517
  }
19533
20518
 
19534
20519
  // src/lib/szenen/hfImageLibrary.ts
@@ -19536,13 +20521,22 @@ function fehler2(nachricht, retryable = false) {
19536
20521
  return { kind: "error", message: nachricht, retryable };
19537
20522
  }
19538
20523
  function alsBild(eintrag) {
20524
+ const { position } = locatorTeile(eintrag.id);
20525
+ const werk = eintrag.prompt?.trim() || "";
19539
20526
  return {
19540
20527
  mediaRef: eintrag.id,
19541
- // Der Prompt ist der einzige menschenlesbare Text, den ein Bild dort trägt.
19542
- // Fehlt er, ist die Kennung ehrlicher als ein erfundener Name.
19543
- title: eintrag.prompt?.trim() || eintrag.id,
20528
+ // Innerhalb eines mehrbildigen Werkes tragen alle Einträge denselben
20529
+ // Prompt — der Abgleich schreibt ihn je Bild aus derselben Library-Zeile.
20530
+ // Deshalb bekommt die Kachel dort die Position dazu, sonst stünde
20531
+ // vierzehnmal derselbe Satz.
20532
+ title: position === null ? werk || eintrag.id : `${werk || eintrag.id} \xB7 Bild ${position + 1}`,
19544
20533
  mimeType: eintrag.mimeType || "image/jpeg",
19545
- thumbUrl: null
20534
+ // Kein `thumbUrl`: Die Datei liegt auf HF hinter einem Token, und ein
20535
+ // `<img src>` käme dort nicht durch. Die Quelle entsteht über
20536
+ // `loadPreview()` — und nur für die Kacheln, die wirklich gezeigt werden.
20537
+ thumbUrl: null,
20538
+ itemTitle: werk || null,
20539
+ ...typeof eintrag.timestamp === "number" ? { timestamp: eintrag.timestamp } : {}
19546
20540
  };
19547
20541
  }
19548
20542
  function createHfImageLibrary(options) {
@@ -19553,15 +20547,14 @@ function createHfImageLibrary(options) {
19553
20547
  ...options.now ? { now: options.now } : {}
19554
20548
  });
19555
20549
  return {
19556
- async listImages(query) {
20550
+ async listItems(query) {
19557
20551
  try {
19558
20552
  const zustand = await store.bestand();
19559
20553
  const eintraege = zustand?.metadata ?? [];
20554
+ const items = gruppiereZuItems(eintraege.map(alsBild));
19560
20555
  const gesucht = query?.search?.trim().toLowerCase();
19561
- const gefiltert = gesucht ? eintraege.filter((e) => (e.prompt ?? "").toLowerCase().includes(gesucht) || e.id.toLowerCase().includes(gesucht)) : eintraege;
19562
- const sortiert = [...gefiltert].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
19563
- const begrenzt = query?.limit ? sortiert.slice(0, query.limit) : sortiert;
19564
- return { kind: "ok", value: begrenzt.map(alsBild) };
20556
+ const gefiltert = gesucht ? items.filter((i) => i.title.toLowerCase().includes(gesucht) || i.itemRef.toLowerCase().includes(gesucht)) : items;
20557
+ return { kind: "ok", value: query?.limit ? gefiltert.slice(0, query.limit) : gefiltert };
19565
20558
  } catch (error) {
19566
20559
  return fehler2(
19567
20560
  `Der Bildbestand konnte nicht gelesen werden: ${error instanceof Error ? error.message : String(error)}`,
@@ -19569,6 +20562,16 @@ function createHfImageLibrary(options) {
19569
20562
  );
19570
20563
  }
19571
20564
  },
20565
+ async loadPreview(mediaRef) {
20566
+ try {
20567
+ return { kind: "ok", value: await store.preview(mediaRef) };
20568
+ } catch (error) {
20569
+ return fehler2(
20570
+ `Die Vorschau von ${mediaRef} ist nicht ladbar: ${error instanceof Error ? error.message : String(error)}`,
20571
+ true
20572
+ );
20573
+ }
20574
+ },
19572
20575
  async resolveImage(mediaRef, mimeTypeHinweis) {
19573
20576
  try {
19574
20577
  const bild = await store.resolve(mediaRef, mimeTypeHinweis);
@@ -19581,10 +20584,13 @@ function createHfImageLibrary(options) {
19581
20584
  const id = newImageId();
19582
20585
  try {
19583
20586
  await store.putWithId(id, input.data, input.mime_type, input.title);
19584
- return {
19585
- kind: "ok",
19586
- value: { mediaRef: id, title: input.title, mimeType: input.mime_type, thumbUrl: null }
20587
+ const abgelegt = {
20588
+ mediaRef: id,
20589
+ title: input.title,
20590
+ mimeType: input.mime_type,
20591
+ thumbUrl: null
19587
20592
  };
20593
+ return { kind: "ok", value: abgelegt };
19588
20594
  } catch (error) {
19589
20595
  return fehler2(
19590
20596
  `Das Bild konnte nicht abgelegt werden: ${error instanceof Error ? error.message : String(error)}`,
@@ -19655,7 +20661,7 @@ function createFlowImageProvider(generateImage) {
19655
20661
  }
19656
20662
 
19657
20663
  // src/lib/szenen/imageGenerationOrderStore.ts
19658
- function istText3(wert) {
20664
+ function istText4(wert) {
19659
20665
  return typeof wert === "string" && wert.trim().length > 0;
19660
20666
  }
19661
20667
  function collectImageGenerationOrders(events) {
@@ -19663,7 +20669,7 @@ function collectImageGenerationOrders(events) {
19663
20669
  for (const event of events) {
19664
20670
  if (event.type !== SZENEN_EVENT_TYPES.imageGenerationStarted) continue;
19665
20671
  const payload = event.payload;
19666
- if (!payload || !istText3(payload.generation_batch_id) || !istText3(payload.prompt_run_id)) continue;
20672
+ if (!payload || !istText4(payload.generation_batch_id) || !istText4(payload.prompt_run_id)) continue;
19667
20673
  if (byBatchId.has(payload.generation_batch_id)) continue;
19668
20674
  byBatchId.set(payload.generation_batch_id, {
19669
20675
  generation_batch_id: payload.generation_batch_id,
@@ -19677,7 +20683,7 @@ function collectImageGenerationOrders(events) {
19677
20683
  for (const event of events) {
19678
20684
  if (event.type !== SZENEN_EVENT_TYPES.imageGenerationFinished) continue;
19679
20685
  const payload = event.payload;
19680
- if (!payload || !istText3(payload.generation_batch_id)) continue;
20686
+ if (!payload || !istText4(payload.generation_batch_id)) continue;
19681
20687
  const auftrag = byBatchId.get(payload.generation_batch_id);
19682
20688
  if (!auftrag) continue;
19683
20689
  if (auftrag.status !== "started") continue;
@@ -19698,18 +20704,18 @@ function ordersForPromptRun(events, runId) {
19698
20704
  }
19699
20705
 
19700
20706
  // src/lib/szenen/imageBatchStore.ts
19701
- var istText4 = (wert) => typeof wert === "string" && wert.trim().length > 0;
20707
+ var istText5 = (wert) => typeof wert === "string" && wert.trim().length > 0;
19702
20708
  function alsBilder(rohe) {
19703
20709
  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) => ({
20710
+ return rohe.filter((b) => !!b && typeof b === "object").filter((b) => istText5(b.media_ref) && typeof b.position === "number").map((b) => ({
19705
20711
  media_ref: b.media_ref,
19706
20712
  position: b.position,
19707
- ...istText4(b.asset_id) ? { asset_id: b.asset_id } : {},
20713
+ ...istText5(b.asset_id) ? { asset_id: b.asset_id } : {},
19708
20714
  // Der bestätigte MIME-Typ (Review 2026-08-18 §5 P2) muss durch das
19709
20715
  // Rücklesen hindurch: Genau hier braucht ihn der Lesepfad, um den
19710
20716
  // Dateipfad `images/<id>.<ext>` zu bilden, statt ihn zu erraten. Nur
19711
20717
  // gesetzt, wenn er im Ereignis steht — ältere Batches tragen ihn nicht.
19712
- ...istText4(b.mime_type) ? { mime_type: b.mime_type } : {},
20718
+ ...istText5(b.mime_type) ? { mime_type: b.mime_type } : {},
19713
20719
  model: b.model ?? null,
19714
20720
  seed: b.seed ?? null,
19715
20721
  width: b.width ?? null,
@@ -19721,7 +20727,7 @@ function collectImageBatches(events) {
19721
20727
  for (const event of events) {
19722
20728
  if (event.type !== SZENEN_EVENT_TYPES.imageBatchPersisted) continue;
19723
20729
  const payload = event.payload;
19724
- if (!payload || !istText4(payload.item_id) || !istText4(payload.prompt_run_id) || !istText4(payload.generation_batch_id)) continue;
20730
+ if (!payload || !istText5(payload.item_id) || !istText5(payload.prompt_run_id) || !istText5(payload.generation_batch_id)) continue;
19725
20731
  if (payload.source_kind !== "generated_in_app" && payload.source_kind !== "external_import") continue;
19726
20732
  const bestehend = byItemId.get(payload.item_id);
19727
20733
  if (bestehend?.generation_batch_ids.includes(payload.generation_batch_id)) continue;
@@ -19732,7 +20738,7 @@ function collectImageBatches(events) {
19732
20738
  prompt_run_id: payload.prompt_run_id,
19733
20739
  generation_batch_ids: [payload.generation_batch_id],
19734
20740
  source_kind: payload.source_kind,
19735
- title: istText4(payload.title) ? payload.title : `Batch aus Lauf ${payload.prompt_run_id}`,
20741
+ title: istText5(payload.title) ? payload.title : `Batch aus Lauf ${payload.prompt_run_id}`,
19736
20742
  images: bilder
19737
20743
  });
19738
20744
  continue;
@@ -20209,7 +21215,7 @@ function createFlowSzenenViewPort(options) {
20209
21215
  ...options.now ? { now: options.now } : {}
20210
21216
  }) : void 0;
20211
21217
  const bildProvider = options.imageProvider ?? (options.generateImage ? createFlowImageProvider(options.generateImage) : void 0);
20212
- const bildFaehigkeit = options.imageCapability ?? { environment_id: "flow", execution_paths: [FLOW_EXECUTION_PATH] };
21218
+ const bildFaehigkeit = options.imageCapability ?? { environment_id: "flow", execution_paths: [{ execution_path: FLOW_EXECUTION_PATH, provider: "google" }] };
20213
21219
  const imageGeneration = bildProvider && imageBatchWrite ? createImageGenerationPort({
20214
21220
  runs: { async getRun(runId) {
20215
21221
  return runs.getRun(runId);
@@ -20301,7 +21307,7 @@ function resolveBootstrapTarget(input) {
20301
21307
  // src/components/AvatarArchitectApp.tsx
20302
21308
  var import_jsx_runtime47 = require("react/jsx-runtime");
20303
21309
  function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, initialHfToken, hfNamespace, allowDevNamespace, requireWorkspaceSelection = false, serverBaseUrl, onFetchServerProjects, onServerSave, onServerLoad, onServerDelete }) {
20304
- (0, import_react48.useEffect)(() => {
21310
+ (0, import_react49.useEffect)(() => {
20305
21311
  const id = "flow-styles";
20306
21312
  if (!document.getElementById(id)) {
20307
21313
  const style = document.createElement("style");
@@ -20310,36 +21316,36 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20310
21316
  document.head.appendChild(style);
20311
21317
  }
20312
21318
  }, []);
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)(() => {
21319
+ const [showStart, setShowStart] = (0, import_react49.useState)(true);
21320
+ const [showSzenen, setShowSzenen] = (0, import_react49.useState)(false);
21321
+ const [layoutChoice, setLayoutChoice] = (0, import_react49.useState)(() => {
20316
21322
  try {
20317
21323
  return localStorage.getItem("aa-layout") || null;
20318
21324
  } catch {
20319
21325
  return null;
20320
21326
  }
20321
21327
  });
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)(() => {
21328
+ const [projectLoaded, setProjectLoaded] = (0, import_react49.useState)(false);
21329
+ const [hfToken, setHfToken] = (0, import_react49.useState)(initialHfToken || "");
21330
+ const [hfTokenInput, setHfTokenInput] = (0, import_react49.useState)(initialHfToken || "");
21331
+ const [isLoadingFromHF, setIsLoadingFromHF] = (0, import_react49.useState)(false);
21332
+ const [hfNamespaceLocal, setHfNamespaceLocal] = (0, import_react49.useState)(() => {
20327
21333
  try {
20328
21334
  return normalizeSelectableNamespace(localStorage.getItem("aa-hf-namespace"));
20329
21335
  } catch {
20330
21336
  return DEFAULT_NAMESPACE;
20331
21337
  }
20332
21338
  });
20333
- const [hfNamespaceFromServer, setHfNamespaceFromServer] = (0, import_react48.useState)(null);
20334
- const [serverProbeFailed, setServerProbeFailed] = (0, import_react48.useState)(false);
20335
- (0, import_react48.useEffect)(() => {
21339
+ const [hfNamespaceFromServer, setHfNamespaceFromServer] = (0, import_react49.useState)(null);
21340
+ const [serverProbeFailed, setServerProbeFailed] = (0, import_react49.useState)(false);
21341
+ (0, import_react49.useEffect)(() => {
20336
21342
  if (hfNamespace !== void 0 || !serverBaseUrl) return;
20337
21343
  fetch(`${serverBaseUrl}/api/status`).then((r) => r.json()).then((d) => {
20338
21344
  if (typeof d.hfNamespace === "string" && d.hfNamespace) setHfNamespaceFromServer(d.hfNamespace);
20339
21345
  else setServerProbeFailed(true);
20340
21346
  }).catch(() => setServerProbeFailed(true));
20341
21347
  }, [hfNamespace, serverBaseUrl]);
20342
- const [targetConfirmed, setTargetConfirmed] = (0, import_react48.useState)(false);
21348
+ const [targetConfirmed, setTargetConfirmed] = (0, import_react49.useState)(false);
20343
21349
  const isDialogTarget = hfNamespace === void 0 && hfNamespaceFromServer === null && (!serverBaseUrl || serverProbeFailed);
20344
21350
  const isProbingServer = hfNamespace === void 0 && !!serverBaseUrl && hfNamespaceFromServer === null && !serverProbeFailed;
20345
21351
  const bootstrapTarget = resolveBootstrapTarget({
@@ -20350,8 +21356,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20350
21356
  isStartDialogOpen: isDialogTarget && !targetConfirmed
20351
21357
  });
20352
21358
  const effectiveNamespace = bootstrapTarget.namespace ?? "";
20353
- const kontextRef = (0, import_react48.useRef)(null);
20354
- const szenenPort = (0, import_react48.useMemo)(() => hfToken && effectiveNamespace ? createFlowSzenenViewPort({
21359
+ const kontextRef = (0, import_react49.useRef)(null);
21360
+ const szenenPort = (0, import_react49.useMemo)(() => hfToken && effectiveNamespace ? createFlowSzenenViewPort({
20355
21361
  generateText: onGeneratePrompt,
20356
21362
  token: hfToken,
20357
21363
  namespace: effectiveNamespace,
@@ -20373,10 +21379,10 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20373
21379
  const tagBaseResult = useTagBase(hfToken, effectiveNamespace);
20374
21380
  const szenenKontextId = tagBaseResult.selectedContextId === null ? null : String(tagBaseResult.selectedContextId);
20375
21381
  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)([]);
21382
+ const [imageUploadStatus, setImageUploadStatus] = (0, import_react49.useState)(/* @__PURE__ */ new Map());
21383
+ const [bootstrapLog, setBootstrapLog] = (0, import_react49.useState)([]);
21384
+ const [isBootstrapping, setIsBootstrapping] = (0, import_react49.useState)(false);
21385
+ const [hfMissingImages, setHfMissingImages] = (0, import_react49.useState)([]);
20380
21386
  const syncTopSlot = /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_jsx_runtime47.Fragment, { children: [
20381
21387
  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
21388
  "\u26A0 ",
@@ -20426,7 +21432,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20426
21432
  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
21433
  ] })
20428
21434
  ] });
20429
- const wsInputRef = (0, import_react48.useRef)(null);
21435
+ const wsInputRef = (0, import_react49.useRef)(null);
20430
21436
  const startApp = (choice) => {
20431
21437
  try {
20432
21438
  localStorage.setItem("aa-layout", choice);
@@ -20436,18 +21442,18 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20436
21442
  setTargetConfirmed(true);
20437
21443
  setShowStart(false);
20438
21444
  };
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)(() => {
21445
+ const [nodes, setNodes] = (0, import_react49.useState)([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
21446
+ const [edges, setEdges] = (0, import_react49.useState)([]);
21447
+ const [history, setHistory] = (0, import_react49.useState)([]);
21448
+ const [galleryItems, setGalleryItems] = (0, import_react49.useState)([]);
21449
+ const galleryItemsRef = (0, import_react49.useRef)([]);
21450
+ (0, import_react49.useEffect)(() => {
20445
21451
  galleryItemsRef.current = galleryItems;
20446
21452
  }, [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) => {
21453
+ const hfImageNotFoundRef = (0, import_react49.useRef)(/* @__PURE__ */ new Map());
21454
+ const [galleryVisibleCount, setGalleryVisibleCount] = (0, import_react49.useState)(20);
21455
+ const [historyVisibleCount, setHistoryVisibleCount] = (0, import_react49.useState)(20);
21456
+ const loadThumbnailsForEntries = (0, import_react49.useCallback)(async (entries) => {
20451
21457
  for (const entry of entries) {
20452
21458
  if (galleryItemsRef.current.find((g) => g.id === entry.id)?.base64) continue;
20453
21459
  if (hfImageNotFoundRef.current.has(entry.id)) continue;
@@ -20466,7 +21472,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20466
21472
  }
20467
21473
  }
20468
21474
  }, [hfToken, effectiveNamespace]);
20469
- (0, import_react48.useEffect)(() => {
21475
+ (0, import_react49.useEffect)(() => {
20470
21476
  if (!hfState) return;
20471
21477
  if (hfState.tags?.by_category) setWorkspaceTags(hfState.tags);
20472
21478
  const hfIds = new Set(hfState.metadata.map((m) => m.id));
@@ -20493,36 +21499,36 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20493
21499
  return [...localOnly, ...merged].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
20494
21500
  });
20495
21501
  }, [hfState]);
20496
- (0, import_react48.useEffect)(() => {
21502
+ (0, import_react49.useEffect)(() => {
20497
21503
  if (!hfState) return;
20498
21504
  const sortedMeta = [...hfState.metadata].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
20499
21505
  const skeletons = sortedMeta.map((m) => ({ id: m.id, prompt: m.prompt, timestamp: m.timestamp, status: "done", nodeId: m.id, tags: m.tags || [] }));
20500
21506
  const repIds = new Set(groupByPrompt(skeletons).slice(0, galleryVisibleCount).map((g) => g.representative.id));
20501
21507
  loadThumbnailsForEntries(sortedMeta.filter((e) => repIds.has(e.id)));
20502
21508
  }, [hfState, galleryVisibleCount, loadThumbnailsForEntries]);
20503
- (0, import_react48.useEffect)(() => {
21509
+ (0, import_react49.useEffect)(() => {
20504
21510
  if (!hfState) return;
20505
21511
  const sortedMeta = [...hfState.metadata].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
20506
21512
  loadThumbnailsForEntries(sortedMeta.slice(0, historyVisibleCount));
20507
21513
  }, [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)(() => {
21514
+ const [activePrompt, setActivePrompt] = (0, import_react49.useState)("");
21515
+ const [isSynthesizing, setIsSynthesizing] = (0, import_react49.useState)(false);
21516
+ const [activeGenerationsCount, setActiveGenerationsCount] = (0, import_react49.useState)(0);
21517
+ const [currentResult, setCurrentResult] = (0, import_react49.useState)(null);
21518
+ const [focusedNodeId, setFocusedNodeId] = (0, import_react49.useState)(null);
21519
+ const [leftTab, setLeftTab] = (0, import_react49.useState)("prompt");
21520
+ const [promptFeedback, setPromptFeedback] = (0, import_react49.useState)(null);
21521
+ const [lastPromptPayload, setLastPromptPayload] = (0, import_react49.useState)(null);
21522
+ const [isPromptTabGenerating, setIsPromptTabGenerating] = (0, import_react49.useState)(false);
21523
+ const [activeTab, setActiveTab] = (0, import_react49.useState)("history");
21524
+ const [mobileTab, setMobileTab] = (0, import_react49.useState)("stage");
21525
+ const [middlePanel, setMiddlePanel] = (0, import_react49.useState)("stage");
21526
+ const [recentLabItems, setRecentLabItems] = (0, import_react49.useState)([]);
21527
+ const [aspectRatio, setAspectRatio] = (0, import_react49.useState)("1:1");
21528
+ const [selectedModel, setSelectedModel] = (0, import_react49.useState)("\u{1F34C} Nano Banana Pro");
21529
+ const [seed, setSeed] = (0, import_react49.useState)(Math.floor(Math.random() * 1e6));
21530
+ const [seedMode, setSeedMode] = (0, import_react49.useState)("random");
21531
+ const [imageCount, setImageCount] = (0, import_react49.useState)(() => {
20526
21532
  try {
20527
21533
  const v = parseInt(localStorage.getItem("aa-image-count") || "", 10);
20528
21534
  return v >= 1 && v <= 8 ? v : 4;
@@ -20544,45 +21550,45 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20544
21550
  { label: "4 Bilder", value: "4" },
20545
21551
  { label: "8 Bilder", value: "8" }
20546
21552
  ];
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)(() => {
21553
+ const [isLeftCollapsed, setIsLeftCollapsed] = (0, import_react49.useState)(false);
21554
+ const [isRightCollapsed, setIsRightCollapsed] = (0, import_react49.useState)(false);
21555
+ const [leftPanelWidth, setLeftPanelWidth] = (0, import_react49.useState)(() => {
20550
21556
  try {
20551
21557
  return parseInt(localStorage.getItem("aa-left-width") || "260", 10);
20552
21558
  } catch {
20553
21559
  return 260;
20554
21560
  }
20555
21561
  });
20556
- const [rightPanelWidth, setRightPanelWidth] = (0, import_react48.useState)(() => {
21562
+ const [rightPanelWidth, setRightPanelWidth] = (0, import_react49.useState)(() => {
20557
21563
  try {
20558
21564
  return parseInt(localStorage.getItem("aa-right-width") || "320", 10);
20559
21565
  } catch {
20560
21566
  return 320;
20561
21567
  }
20562
21568
  });
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)(() => {
21569
+ const [isPromptCollapsed, setIsPromptCollapsed] = (0, import_react49.useState)(false);
21570
+ const [projectActionState, setProjectActionState] = (0, import_react49.useState)("idle");
21571
+ const syncServerDataRef = (0, import_react49.useRef)(null);
21572
+ const [workspaceTags, setWorkspaceTags] = (0, import_react49.useState)(null);
21573
+ const [serverProjects, setServerProjects] = (0, import_react49.useState)([]);
21574
+ const [isLoadingFromServer, setIsLoadingFromServer] = (0, import_react49.useState)(false);
21575
+ const [highContrast, setHighContrast] = (0, import_react49.useState)(() => {
20570
21576
  try {
20571
21577
  return localStorage.getItem("aa-contrast") === "high";
20572
21578
  } catch {
20573
21579
  return false;
20574
21580
  }
20575
21581
  });
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);
21582
+ const [activeReferenceId, setActiveReferenceId] = (0, import_react49.useState)(null);
21583
+ const [activeReferenceThumbnail, setActiveReferenceThumbnail] = (0, import_react49.useState)(null);
21584
+ const [isScanningImage, setIsScanningImage] = (0, import_react49.useState)(false);
21585
+ const [touchStartX, setTouchStartX] = (0, import_react49.useState)(null);
21586
+ const [isFullscreen, setIsFullscreen] = (0, import_react49.useState)(false);
21587
+ const [zoomScale, setZoomScale] = (0, import_react49.useState)(1);
21588
+ const [zoomOffset, setZoomOffset] = (0, import_react49.useState)({ x: 0, y: 0 });
21589
+ const lastPinchDist = (0, import_react49.useRef)(null);
21590
+ const lastTapTime = (0, import_react49.useRef)(0);
21591
+ const dragStart = (0, import_react49.useRef)(null);
20586
21592
  const openFullscreen = () => {
20587
21593
  setIsFullscreen(true);
20588
21594
  setZoomScale(1);
@@ -20645,7 +21651,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20645
21651
  setActiveReferenceId(null);
20646
21652
  setActiveReferenceThumbnail(null);
20647
21653
  };
20648
- const labServices = (0, import_react48.useMemo)(() => {
21654
+ const labServices = (0, import_react49.useMemo)(() => {
20649
21655
  const available = groupGenerationsToLabItems([...galleryItems, ...history]);
20650
21656
  return {
20651
21657
  availableItems: available,
@@ -20725,14 +21731,14 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20725
21731
  setIsScanningImage(false);
20726
21732
  }
20727
21733
  };
20728
- const currentIndex = (0, import_react48.useMemo)(() => history.findIndex((h) => h.id === currentResult?.id), [history, currentResult]);
20729
- const goToPrev = (0, import_react48.useCallback)(() => {
21734
+ const currentIndex = (0, import_react49.useMemo)(() => history.findIndex((h) => h.id === currentResult?.id), [history, currentResult]);
21735
+ const goToPrev = (0, import_react49.useCallback)(() => {
20730
21736
  if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
20731
21737
  }, [currentIndex, history]);
20732
- const goToNext = (0, import_react48.useCallback)(() => {
21738
+ const goToNext = (0, import_react49.useCallback)(() => {
20733
21739
  if (currentIndex < history.length - 1) setCurrentResult(history[currentIndex + 1]);
20734
21740
  }, [currentIndex, history]);
20735
- const handleGallerySelect = (0, import_react48.useCallback)((g) => {
21741
+ const handleGallerySelect = (0, import_react49.useCallback)((g) => {
20736
21742
  setCurrentResult(g);
20737
21743
  setMobileTab("stage");
20738
21744
  if (g.hasThumb && g.filename && hfToken && !g.fullBase64) {
@@ -20745,7 +21751,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20745
21751
  });
20746
21752
  }
20747
21753
  }, [hfToken, effectiveNamespace]);
20748
- const handleTitleSet = (0, import_react48.useCallback)((id) => {
21754
+ const handleTitleSet = (0, import_react49.useCallback)((id) => {
20749
21755
  const ts = Date.now();
20750
21756
  setGalleryItems((prev) => prev.map((g) => g.id === id ? { ...g, titleTs: ts } : g));
20751
21757
  setHistory((prev) => prev.map((g) => g.id === id ? { ...g, titleTs: ts } : g));
@@ -20754,13 +21760,13 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20754
21760
  });
20755
21761
  }
20756
21762
  }, [hfToken, effectiveNamespace, hfWriteEvent]);
20757
- const currentGroup = (0, import_react48.useMemo)(() => {
21763
+ const currentGroup = (0, import_react49.useMemo)(() => {
20758
21764
  if (!currentResult?.prompt) return [];
20759
21765
  const groups = groupByPrompt(galleryItems.filter((g) => g.status === "done" && !!g.base64));
20760
21766
  const group = groups.find((gr) => gr.prompt === currentResult.prompt);
20761
21767
  return group ? group.items : [];
20762
21768
  }, [galleryItems, currentResult?.prompt]);
20763
- (0, import_react48.useEffect)(() => {
21769
+ (0, import_react49.useEffect)(() => {
20764
21770
  if (!currentResult?.prompt || !hfToken || !effectiveNamespace || !hfState) return;
20765
21771
  const siblings = galleryItemsRef.current.filter(
20766
21772
  (g) => g.prompt === currentResult.prompt && !g.base64 && !hfImageNotFoundRef.current.has(g.id)
@@ -20799,7 +21805,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20799
21805
  }
20800
21806
  ) : null;
20801
21807
  useKeyboardNavigation(history, currentResult, setCurrentResult);
20802
- const getSubtreeFormat = (0, import_react48.useCallback)((nodeId, depth = 0) => {
21808
+ const getSubtreeFormat = (0, import_react49.useCallback)((nodeId, depth = 0) => {
20803
21809
  const node = nodes.find((n) => n.id === nodeId);
20804
21810
  if (!node) return "";
20805
21811
  const childrenIds = edges.filter((e) => e.source === nodeId).map((e) => e.target);
@@ -20807,7 +21813,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
20807
21813
  return `${indent}- ${node.data.label || "(unbenannt)"}
20808
21814
  ` + childrenIds.map((id) => getSubtreeFormat(id, depth + 1)).join("");
20809
21815
  }, [nodes, edges]);
20810
- const activePath = (0, import_react48.useMemo)(() => {
21816
+ const activePath = (0, import_react49.useMemo)(() => {
20811
21817
  if (!focusedNodeId) return /* @__PURE__ */ new Set();
20812
21818
  const path = /* @__PURE__ */ new Set([focusedNodeId]);
20813
21819
  let currId = focusedNodeId;
@@ -21173,7 +22179,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
21173
22179
  setTimeout(() => setProjectActionState("idle"), 4e3);
21174
22180
  }
21175
22181
  };
21176
- (0, import_react48.useEffect)(() => {
22182
+ (0, import_react49.useEffect)(() => {
21177
22183
  if (activeTab === "setup" || activeTab === "sync") fetchServerProjects();
21178
22184
  }, [activeTab]);
21179
22185
  const mergeWorkspaceTags = (local, remote) => {
@@ -22205,7 +23211,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
22205
23211
  }
22206
23212
 
22207
23213
  // src/components/FaApp.tsx
22208
- var import_react49 = require("react");
23214
+ var import_react50 = require("react");
22209
23215
  var import_jsx_runtime48 = require("react/jsx-runtime");
22210
23216
  function FaApp({
22211
23217
  onGenerateImage,
@@ -22225,18 +23231,18 @@ function FaApp({
22225
23231
  onServerDelete,
22226
23232
  buildInfo
22227
23233
  }) {
22228
- const [hfNamespace, setHfNamespace] = (0, import_react49.useState)(void 0);
22229
- (0, import_react49.useEffect)(() => {
23234
+ const [hfNamespace, setHfNamespace] = (0, import_react50.useState)(void 0);
23235
+ (0, import_react50.useEffect)(() => {
22230
23236
  if (!serverBaseUrl) return;
22231
23237
  fetch(`${serverBaseUrl}/api/status`).then((r) => r.json()).then((d) => {
22232
23238
  if (typeof d.hfNamespace === "string") setHfNamespace(d.hfNamespace);
22233
23239
  }).catch(() => {
22234
23240
  });
22235
23241
  }, [serverBaseUrl]);
22236
- const wrappedPrompt = async (text, options) => {
22237
- const result = await onGeneratePrompt(text, options);
22238
- return result.text;
22239
- };
23242
+ const wrappedPrompt = (0, import_react50.useMemo)(
23243
+ () => alsFlowTextZugang(onGeneratePrompt),
23244
+ [onGeneratePrompt]
23245
+ );
22240
23246
  return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
22241
23247
  AvatarArchitectApp,
22242
23248
  {
@@ -22491,15 +23497,15 @@ function createInMemoryWorkspacePersistence(now = () => Date.now()) {
22491
23497
  }
22492
23498
 
22493
23499
  // node_modules/zustand/esm/react.mjs
22494
- var import_react50 = __toESM(require("react"), 1);
23500
+ var import_react51 = __toESM(require("react"), 1);
22495
23501
  var identity = (arg) => arg;
22496
23502
  function useStore(api, selector = identity) {
22497
- const slice = import_react50.default.useSyncExternalStore(
23503
+ const slice = import_react51.default.useSyncExternalStore(
22498
23504
  api.subscribe,
22499
- import_react50.default.useCallback(() => selector(api.getState()), [api, selector]),
22500
- import_react50.default.useCallback(() => selector(api.getInitialState()), [api, selector])
23505
+ import_react51.default.useCallback(() => selector(api.getState()), [api, selector]),
23506
+ import_react51.default.useCallback(() => selector(api.getInitialState()), [api, selector])
22501
23507
  );
22502
- import_react50.default.useDebugValue(slice);
23508
+ import_react51.default.useDebugValue(slice);
22503
23509
  return slice;
22504
23510
  }
22505
23511