@rslsp1/fa-app-tools 7.4.0 → 9.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
@@ -132,7 +132,7 @@ __export(index_exports, {
132
132
  module.exports = __toCommonJS(index_exports);
133
133
 
134
134
  // package.json
135
- var version = "7.4.0";
135
+ var version = "9.0.0";
136
136
 
137
137
  // src/hooks/useOnClickOutside.ts
138
138
  var import_react = require("react");
@@ -171,7 +171,7 @@ function useKeyboardNavigation(history, currentResult, setCurrentResult) {
171
171
  }
172
172
 
173
173
  // src/features/szenen/szenenViewStyles.generated.ts
174
- 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: center;\n justify-content: space-between;\n gap: var(--space-sm);\n padding: clamp(0.6rem, 1.2vw, 0.9rem) clamp(0.75rem, 1.6vw, 1.1rem);\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: 0.1rem;\n font-size: clamp(1rem, 1.9vw, 1.3rem);\n line-height: 1.2;\n}\n\n.szenen-view__intro > div > p:last-child {\n max-width: 44rem;\n color: var(--color-text-secondary);\n font-size: 0.82rem;\n line-height: 1.35;\n}\n\n.szenen-view__eyebrow {\n margin-bottom: 0.1rem !important;\n color: var(--color-accent-teal, #2dd4bf);\n font-size: 0.62rem;\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.25rem 0.6rem;\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.72rem;\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/* `box-sizing` steht hier und nicht beim Host: Die React-App bringt in\n * `master.css` einen globalen `* { box-sizing: border-box }` mit, Flow **nicht**\n * \u2014 dort ragte `width: 100%` plus Innenabstand um die Randbreite aus dem\n * Elternelement heraus und erzeugte auf ~390 px einen horizontalen Scroll. Die\n * gemeinsame Ansicht darf sich auf keinen Host-Reset verlassen. */\n.szenen-view input:not([type="checkbox"]),\n.szenen-view select,\n.szenen-view textarea {\n box-sizing: border-box;\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\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/* B1 \u2014 die geschlossene Runde. Vorher stand die `run_id` als erstes und war\n die einzige Information, die niemand liest; jetzt beantwortet die Kopfzeile\n welche Runde, ob sie durchging, mit welchem Modell und was ungef\xE4hr\n herauskam. */\n.szenen-runs__kopf {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--space-sm);\n width: 100%;\n padding: var(--space-xs) 0;\n text-align: left;\n color: var(--color-text-secondary);\n background: transparent;\n border: none;\n cursor: pointer;\n}\n\n.szenen-runs__marke {\n font-weight: 700;\n color: var(--color-text-primary);\n}\n\n.szenen-runs__status {\n padding: 0.05rem 0.4rem;\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n border-radius: var(--radius-sm);\n background: var(--color-bg-app);\n border: 1px solid var(--color-border);\n}\n\n.szenen-runs__status[data-status="succeeded"] {\n border-color: var(--color-success);\n}\n\n.szenen-runs__status[data-status="failed"] {\n border-color: var(--color-danger);\n}\n\n.szenen-runs__modell {\n font-size: 0.75rem;\n}\n\n/* Der Auszug ist die Inhaltsvorschau der Runde. Er bekommt eine eigene Zeile,\n damit er nicht mit Marke und Modell um dieselbe Zeile ringt. */\n.szenen-runs__auszug {\n flex: 1 1 100%;\n min-width: 0;\n color: var(--color-text-tertiary);\n font-size: 0.8125rem;\n}\n\n/* B2 \u2014 ein Fehler steht ausserhalb der aufklappbaren Fl\xE4che: Wer eine rote\n Runde sieht, soll nicht erst klicken m\xFCssen, um zu erfahren, woran sie\n gescheitert ist. */\n.szenen-runs__fehler {\n display: grid;\n gap: var(--space-xs);\n padding: var(--space-sm);\n border-radius: var(--radius-sm);\n}\n\n.szenen-runs__fehler p {\n margin: 0;\n}\n\n.szenen-runs__inhalt {\n display: grid;\n gap: var(--space-sm);\n min-width: 0;\n}\n\n.szenen-runs__prompt > h5,\n.szenen-runs__sekundaer > h5 {\n margin: 0 0 var(--space-xs);\n}\n\n.szenen-runs__prompt > p {\n margin: 0;\n white-space: pre-wrap;\n}\n\n.szenen-runs__prompt--negativ > p,\n.szenen-runs__sekundaer > p {\n margin: 0;\n color: var(--color-text-secondary);\n white-space: pre-wrap;\n}\n\n.szenen-runs__mehr {\n justify-self: start;\n margin-top: var(--space-xs);\n padding: 0;\n color: var(--color-accent-teal, var(--color-text-secondary));\n background: transparent;\n border: none;\n font-size: 0.75rem;\n text-decoration: underline;\n cursor: pointer;\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 /* C1 \u2014 auf 390 px bleiben drei Kacheln je Reihe gross genug zum Erkennen\n und klein genug, dass vier Bilder eine Reihe sind und keine Seite. */\n .szenen-bildgalerie {\n grid-template-columns: repeat(auto-fill, minmax(4.5rem, 1fr));\n }\n\n /* Der Viewer bekommt den ganzen Bildschirm; die Bl\xE4tterkn\xF6pfe bleiben am\n Rand, wo der Daumen sie erreicht. */\n .szenen-bildviewer {\n padding: var(--space-xs);\n }\n\n .szenen-bildviewer__blaettern {\n width: 2.25rem;\n height: 4rem;\n }\n\n .szenen-view__intro {\n align-items: start;\n flex-direction: column;\n gap: var(--space-xs);\n padding: var(--space-sm);\n }\n\n .szenen-view__intro h2 {\n font-size: 1.05rem;\n }\n\n .szenen-view__intro > div > p:last-child {\n font-size: 0.78rem;\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 Browser und Inspector, dem Labs-Tab nachgebildet,\n aber mit dem CSS dieser Ansicht: eine gemeinsame Komponente \xFCber zwei\n Dom\xE4nengrenzen w\xE4re eine Kopplung, die keiner gewollt hat.\n\n Der Review vom 2026-08-22 (R12.1/R12.2) hat den Ma\xDFstab gesetzt: Der Bestand\n ist ein **Browser**, kein Seiteninhalt. Vierzig Werke d\xFCrfen die Szenenseite\n nicht meterlang machen, und zw\xF6lf Varianten eines Werkes keine zw\xF6lf\n Bildschirmseiten. Deshalb steht hier alles unter zwei Regeln \u2014 begrenzte\n Fl\xE4che mit eigenem Scroll, und Kacheln statt Karten. */\n\n/* \u2500\u2500 Ebene 1: der Browser \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\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.szenen-bildwahl {\n /* Dieselbe Begr\xFCndung wie beim Inspector: Als Grid-Element w\xFCrde der\n W\xE4hler sonst so breit wie sein breitester Inhalt. */\n min-width: 0;\n}\n\n.szenen-bildwahl__browser {\n /* Die eigene Scrollfl\xE4che ist der ganze Punkt: Der Bestand w\xE4chst nach\n innen, nicht nach unten. Dieselbe Gr\xF6\xDFenordnung wie `max-h-[300px]` im\n Labs-Browser. */\n max-height: 19rem;\n overflow-y: auto;\n overscroll-behavior: contain;\n margin-top: 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.szenen-bildwahl__raster {\n display: grid;\n /* Vier Spalten auf schmalen Displays \u2014 der gemeldete Screenshot zeigte eine\n einzige Spalte gro\xDFer Karten. Breiter wird automatisch dichter. */\n grid-template-columns: repeat(auto-fill, minmax(4.5rem, 1fr));\n gap: var(--space-sm);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.szenen-bildwahl__karte {\n position: relative;\n display: block;\n width: 100%;\n padding: 0;\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n overflow: hidden;\n background: var(--color-bg-surface);\n text-align: left;\n cursor: pointer;\n}\n\n/* Das fokussierte Werk ist klar markiert \u2014 sonst wei\xDF niemand, wessen Galerie\n der Inspector unten zeigt. */\n.szenen-bildwahl__karte.is-fokus {\n border-color: var(--color-accent, var(--color-text-primary));\n box-shadow: 0 0 0 2px var(--color-accent, var(--color-text-primary));\n}\n\n/* \u2500\u2500 Die Kachel selbst \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n/* Der Platz steht, bevor das Bild da ist \u2014 sonst springt das Raster, sobald\n die erste Vorschau eintrifft. */\n.szenen-bildkachel {\n display: block;\n width: 100%;\n aspect-ratio: 1;\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: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n background: linear-gradient(135deg, var(--color-bg-app), var(--color-bg-surface));\n}\n\n/* R12.3: Eine fehlende Vorschau bleibt kachelgro\xDF. Vorher fiel die Karte auf\n ihren Prompttext zur\xFCck \u2014 und ein Prompt hat im echten Bestand mehrere\n hundert Zeichen. */\n.szenen-bildkachel__zeichen {\n font-size: 0.875rem;\n font-weight: 700;\n color: var(--color-text-secondary);\n opacity: 0.6;\n}\n\n/* Wie viele Varianten das Werk hat \u2014 klein und \xFCberlagernd, nie als\n dominanter Text. */\n.szenen-bildwahl__anzahl {\n position: absolute;\n top: 0.15rem;\n right: 0.15rem;\n padding: 0.05rem 0.3rem;\n font-size: 0.625rem;\n font-weight: 700;\n line-height: 1.4;\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/* Eine Zeile, dann Schluss. Der Prompt eines Werkes ist oft mehrere hundert\n Zeichen lang und w\xE4re als Prim\xE4rdarstellung genau die Textw\xFCste, gegen die\n der Review antritt. */\n.szenen-bildwahl__kacheltitel {\n display: block;\n padding: 0.15rem 0.3rem;\n font-size: 0.625rem;\n line-height: 1.3;\n color: var(--color-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* \u2500\u2500 Ebene 2: der Inspector \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\u2500\u2500\u2500\u2500 */\n\n.szenen-bildwahl__inspector {\n /* Ohne `min-width: 0` gewinnt der Inhalt: Der W\xE4hler ist Element eines\n Grids (`.szenen-form`), und dort ist `min-width: auto` die Vorgabe. Der\n Streifen unten ist rund 1000 px breit \u2014 gemessen am 2026-08-22 im echten\n Browser zog er die Auswahl von 374 px auf 1088 px auf und schob die\n ganze Seite waagerecht heraus, statt selbst zu scrollen. */\n min-width: 0;\n margin-top: var(--space-sm);\n padding: var(--space-sm);\n background: var(--color-bg-surface);\n /* Sichtbar abgesetzt vom Browser: zwei Ebenen, nicht eine lange Liste. */\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-bildwahl__inspektorkopf {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--space-sm);\n margin-bottom: var(--space-sm);\n}\n\n.szenen-bildwahl__werktitel {\n font-size: 0.75rem;\n font-weight: 600;\n color: var(--color-text-primary);\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.szenen-bildwahl__zurueck {\n flex: 0 0 auto;\n font-size: 0.6875rem;\n padding: 0.15rem 0.5rem;\n}\n\n/* Der Streifen: waagerecht scrollend, feste kleine Kacheln \u2014 zw\xF6lf Varianten\n bleiben ein \xDCberblick statt zw\xF6lf Bildschirmseiten. */\n.szenen-bildwahl__streifen {\n /* Die Breite kommt vom Elter, nie vom Inhalt \u2014 sonst scrollt hier nichts. */\n max-width: 100%;\n display: flex;\n gap: var(--space-sm);\n margin: 0;\n padding: 0 0 0.25rem;\n list-style: none;\n overflow-x: auto;\n overscroll-behavior-x: contain;\n}\n\n.szenen-bildwahl__streifen > li {\n flex: 0 0 auto;\n width: 5rem;\n}\n\n.szenen-bildwahl__variante {\n position: relative;\n display: block;\n width: 100%;\n padding: 0;\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n overflow: hidden;\n background: var(--color-bg-app);\n cursor: pointer;\n}\n\n.szenen-bildwahl__variante.is-gewaehlt {\n border-color: var(--color-accent, var(--color-text-primary));\n box-shadow: 0 0 0 2px var(--color-accent, var(--color-text-primary));\n}\n\n.szenen-bildwahl__nummer {\n position: absolute;\n bottom: 0.15rem;\n left: 0.15rem;\n padding: 0.05rem 0.3rem;\n font-size: 0.625rem;\n line-height: 1.4;\n color: var(--color-text-primary);\n background: var(--color-bg-elevated);\n border-radius: var(--radius-sm);\n}\n\n/* \u2500\u2500 Das Gew\xE4hlte \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\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.25rem;\n flex: 0 0 auto;\n border-radius: var(--radius-sm);\n}\n\n.szenen-bildwahl__gewaehlt .szenen-bildwahl__kacheltitel {\n min-width: 0;\n max-width: 12rem;\n font-size: 0.75rem;\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/* Das Eingangsbild benutzt seit C3 dieselbe Kachel wie W\xE4hler und Galerie.\n Sie ist von Haus aus so breit wie ihr Platz \u2014 in dieser Zeile bekommt sie\n deshalb eine feste Gr\xF6sse, sonst sch\xF6be sie die Metadaten heraus. */\n.szenen-bilder__eingangsliste .szenen-bildkachel {\n flex: 0 0 auto;\n width: 4rem;\n border-radius: var(--radius-sm);\n}\n\n.szenen-bilder__batch {\n display: grid;\n gap: var(--space-sm);\n min-width: 0;\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.szenen-bilder__batchkopf {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n justify-content: space-between;\n gap: var(--space-sm);\n}\n\n.szenen-bilder__herkunft {\n color: var(--color-text-tertiary);\n font-size: 0.75rem;\n}\n\n/* Review-Befund \u2014 ein transienter Bildfehler ist wiederholbar, kein\n endg\xFCltiges \u201Efehlt". Dezent wie ein Angebot, nicht wie ein Fehlerzustand. */\n.szenen-bilder__retry {\n justify-self: start;\n padding: 0.15rem 0.5rem;\n font-size: 0.75rem;\n color: var(--color-text-secondary);\n background: var(--color-bg-app);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n cursor: pointer;\n}\n\n/* C4 \u2014 die Provenienz vollst\xE4ndig, aber sekund\xE4r. */\n.szenen-bilder__technik > summary {\n color: var(--color-text-secondary);\n font-size: 0.75rem;\n cursor: pointer;\n}\n\n.szenen-bilder__meta {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--space-xs);\n font-size: 0.75rem;\n color: var(--color-text-secondary);\n}\n\n/* C5 \u2014 der Verlauf verdichtet, seine Ausreisser nicht. */\n.szenen-bilder__verlauf > summary {\n color: var(--color-text-secondary);\n font-size: 0.8125rem;\n cursor: pointer;\n}\n\n.szenen-bilder__verlauf[open] > summary {\n margin-bottom: var(--space-sm);\n}\n\n.szenen-bilder__auffaellig {\n margin: 0;\n padding: var(--space-sm);\n border-radius: var(--radius-sm);\n font-size: 0.8125rem;\n}\n\n.szenen-bilder__auftragskennung {\n flex-basis: 100%;\n color: var(--color-text-tertiary);\n font-size: 0.6875rem;\n}\n\n/* \u2500\u2500 C1/C2 \u2014 Galerie und grosse Ansicht \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/* Das Raster w\xE4chst mit dem Platz: auf 390 px drei bis vier Kacheln je Reihe,\n auf dem Desktop entsprechend mehr. Vorher war jedes Bild eine Zeile aus\n Daumennagel plus vier Metadatenfeldern \u2014 vier Bilder ergaben vier Textzeilen\n und das Protokoll war die Hauptsache. */\n.szenen-bildgalerie {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(5.5rem, 1fr));\n gap: var(--space-sm);\n min-width: 0;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.szenen-bildgalerie__kachel {\n position: relative;\n display: block;\n width: 100%;\n padding: 0;\n overflow: hidden;\n background: var(--color-bg-surface);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n cursor: pointer;\n}\n\n.szenen-bildgalerie__kachel:hover,\n.szenen-bildgalerie__kachel:focus-visible {\n border-color: var(--color-accent-teal, var(--color-text-primary));\n}\n\n.szenen-bildgalerie__nummer {\n position: absolute;\n bottom: 0.15rem;\n left: 0.15rem;\n padding: 0.05rem 0.3rem;\n font-size: 0.625rem;\n line-height: 1.4;\n color: var(--color-text-primary);\n background: var(--color-bg-elevated);\n border-radius: var(--radius-sm);\n}\n\n/* Die grosse Ansicht liegt \xFCber allem und bringt ihren eigenen Grund mit \u2014\n `position: fixed` bezieht sich im Flow-Canvas auf dessen Rahmen, in React auf\n das Fenster; in beiden F\xE4llen genau das Gew\xFCnschte. */\n.szenen-bildviewer {\n position: fixed;\n inset: 0;\n z-index: 1000;\n display: grid;\n grid-template-rows: auto 1fr auto;\n gap: var(--space-sm);\n padding: var(--space-sm);\n /* Deckend, nicht get\xF6nt: Bei 0.92 schimmerte der Laufbereich durch und man\n las Prompttext quer \xFCber dem Bild \u2014 im Browser gemessen. */\n background: #000;\n}\n\n/* Titel, Z\xE4hlung, Schliessen \u2014 drei Spalten, damit ein langer Werktitel die\n Z\xE4hlung nicht in die n\xE4chste Zeile und \xFCber das Bild schiebt. */\n.szenen-bildviewer__kopf {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto auto;\n align-items: center;\n gap: var(--space-sm);\n color: var(--color-text-primary);\n}\n\n.szenen-bildviewer__titel {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 600;\n}\n\n.szenen-bildviewer__zaehler {\n color: var(--color-text-secondary);\n font-size: 0.8125rem;\n}\n\n.szenen-bildviewer__buehne {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--space-sm);\n min-height: 0;\n min-width: 0;\n}\n\n/* Der Rahmen nimmt den Platz, der zwischen den Bl\xE4tterkn\xF6pfen \xFCbrig ist \u2014\n `min-width: 0`, damit er auch wirklich schrumpft statt sie hinauszuschieben. */\n.szenen-bildviewer__rahmen {\n flex: 1 1 auto;\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 0;\n min-height: 0;\n height: 100%;\n}\n\n.szenen-bildviewer__bild {\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n}\n\n.szenen-bildviewer__leer {\n color: var(--color-text-secondary);\n text-align: center;\n}\n\n/* Die Bl\xE4tterkn\xF6pfe sind daumengross und liegen an den R\xE4ndern \u2014 auf einem\n Telefon ist der Rand erreichbar, die Bildmitte nicht. */\n.szenen-bildviewer__blaettern {\n flex: 0 0 auto;\n width: 2.75rem;\n height: 3.5rem;\n font-size: 1.75rem;\n line-height: 1;\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 cursor: pointer;\n}\n\n.szenen-bildviewer__details {\n max-height: 8rem;\n overflow-y: auto;\n color: var(--color-text-secondary);\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/* D \u2014 die Aktionen stehen abgesetzt und standardm\xE4ssig kompakt: Sie sind\n Angebote, keine Ausk\xFCnfte. Vorher hielten beide Formulare dauerhaft offen und\n machten aus jedem Lauf zwei zus\xE4tzliche Bildschirmseiten. */\n.szenen-bilder__aktion {\n min-width: 0;\n padding: var(--space-sm) var(--space-md);\n border: 1px dashed var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-bilder__aktion > summary {\n color: var(--color-text-primary);\n font-weight: 600;\n cursor: pointer;\n}\n\n.szenen-bilder__aktion[open] > summary {\n margin-bottom: var(--space-sm);\n}\n\n.szenen-bilder__aktion-inhalt {\n display: grid;\n gap: var(--space-sm);\n min-width: 0;\n padding-bottom: var(--space-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/* Regelwerk-UX Schritt 16 \u2014 der ZIP-Sammelimport.\n *\n * \xA78: Der gemeldete Einsatz ist mobil. Deshalb **ein** Seedfeld statt einer\n * gestapelten Eingabe je Bild, und die Zuordnung Bild -> Seed als umbrechende\n * Zeile statt als Tabelle: Eine Tabelle waere auf 390 px abgeschnitten, und\n * abgeschnitten heisst hier, dass der Nutzer die Zuordnung nicht mehr pruefen\n * kann \u2014 genau die Pruefung, wegen der die Vorschau existiert. */\n.szenen-bilder__zip {\n display: grid;\n gap: var(--space-sm);\n min-width: 0;\n}\n\n.szenen-bilder__zipkopf {\n margin: 0;\n overflow-wrap: anywhere;\n}\n\n.szenen-bilder__zipvorlagen {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-xs);\n margin: 0;\n}\n\n/* Eine laengere Liste bleibt scrollbar, statt die Sektion und damit den\n * Import-Knopf aus dem Blick zu schieben (\xA78, letzter Punkt). */\n.szenen-bilder__zipliste {\n max-height: 16rem;\n overflow-y: auto;\n}\n\n.szenen-bilder__zipliste > li {\n display: flex;\n flex-wrap: wrap;\n justify-content: space-between;\n gap: var(--space-xs);\n min-width: 0;\n}\n\n.szenen-bilder__zipliste > li > span:first-child {\n overflow-wrap: anywhere;\n}\n\n.szenen-bilder__zipliste > li > span:last-child {\n color: var(--color-text-secondary);\n white-space: nowrap;\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 von Objektfeldern (`prompt_render_settings`, `overrides`,\n `blender_render_settings`) \u2014 seit A1/A2 auf- und zuklappbar. Eine optionale\n Gruppe startet zu: Blender und `overrides` f\xFCllten auf einem 390-px-Display\n sonst mehrere Bildschirmseiten, obwohl die meisten Shots sie nie anfassen. */\n.szenen-detail__group {\n min-width: 0;\n margin: var(--space-md) 0;\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__group > summary {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--space-xs);\n color: var(--color-text-secondary);\n font-weight: 600;\n cursor: pointer;\n}\n\n.szenen-detail__group[open] > summary {\n margin-bottom: var(--space-sm);\n}\n\n/* A2 \u2014 dass eine zugeklappte Gruppe schon Werte tr\xE4gt, muss man sehen, ohne\n sie zu \xF6ffnen. Sonst \xFCbersieht man eine gesetzte Blender-Ausgabe genau so\n lange, bis sie im Ergebnis auftaucht. */\n.szenen-detail__group-status {\n padding: 0.05rem 0.4rem;\n font-size: 0.6875rem;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--color-text-tertiary);\n background: var(--color-bg-app);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-detail__group[data-konfiguriert="ja"] > summary .szenen-detail__group-status {\n color: var(--color-text-primary);\n border-color: var(--color-accent-teal, var(--color-border));\n}\n\n.szenen-detail__group-inhalt {\n display: grid;\n gap: var(--space-sm);\n min-width: 0;\n padding-bottom: var(--space-sm);\n}\n\n/* A3 \u2014 die Kernaussage steht am Feld, die Begr\xFCndung dahinter. Nicht als\n Tooltip: Auf einem Telefon gibt es kein Hover, und was dort unerreichbar ist,\n ist gestrichen \u2014 nur ohne dass es jemand entschieden h\xE4tte. */\n.szenen-detail__warum > summary {\n color: var(--color-text-tertiary);\n font-size: 0.75rem;\n cursor: pointer;\n}\n\n.szenen-detail__warum > small {\n display: block;\n padding-top: var(--space-xs);\n color: var(--color-text-tertiary);\n}\n\n.szenen-detail__fieldrow {\n display: grid;\n gap: var(--space-xs);\n min-width: 0;\n margin-bottom: var(--space-md);\n}\n\n/* A4 \u2014 der technische Feldname bleibt lesbar, tritt aber hinter das fachliche\n Label zur\xFCck. */\n.szenen-detail__field-label {\n color: var(--color-text-primary);\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';
174
+ 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: center;\n justify-content: space-between;\n gap: var(--space-sm);\n padding: clamp(0.6rem, 1.2vw, 0.9rem) clamp(0.75rem, 1.6vw, 1.1rem);\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: 0.1rem;\n font-size: clamp(1rem, 1.9vw, 1.3rem);\n line-height: 1.2;\n}\n\n.szenen-view__intro > div > p:last-child {\n max-width: 44rem;\n color: var(--color-text-secondary);\n font-size: 0.82rem;\n line-height: 1.35;\n}\n\n.szenen-view__eyebrow {\n margin-bottom: 0.1rem !important;\n color: var(--color-accent-teal, #2dd4bf);\n font-size: 0.62rem;\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.25rem 0.6rem;\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.72rem;\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/* `box-sizing` steht hier und nicht beim Host: Die React-App bringt in\n * `master.css` einen globalen `* { box-sizing: border-box }` mit, Flow **nicht**\n * \u2014 dort ragte `width: 100%` plus Innenabstand um die Randbreite aus dem\n * Elternelement heraus und erzeugte auf ~390 px einen horizontalen Scroll. Die\n * gemeinsame Ansicht darf sich auf keinen Host-Reset verlassen. */\n.szenen-view input:not([type="checkbox"]),\n.szenen-view select,\n.szenen-view textarea {\n box-sizing: border-box;\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-erweitert,\n.szenen-prompts,\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\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/* B1 \u2014 die geschlossene Runde. Vorher stand die `run_id` als erstes und war\n die einzige Information, die niemand liest; jetzt beantwortet die Kopfzeile\n welche Runde, ob sie durchging, mit welchem Modell und was ungef\xE4hr\n herauskam. */\n.szenen-runs__kopf {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--space-sm);\n width: 100%;\n padding: var(--space-xs) 0;\n text-align: left;\n color: var(--color-text-secondary);\n background: transparent;\n border: none;\n cursor: pointer;\n}\n\n.szenen-runs__marke {\n font-weight: 700;\n color: var(--color-text-primary);\n}\n\n.szenen-runs__status {\n padding: 0.05rem 0.4rem;\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n border-radius: var(--radius-sm);\n background: var(--color-bg-app);\n border: 1px solid var(--color-border);\n}\n\n.szenen-runs__status[data-status="succeeded"] {\n border-color: var(--color-success);\n}\n\n.szenen-runs__status[data-status="failed"] {\n border-color: var(--color-danger);\n}\n\n.szenen-runs__modell {\n font-size: 0.75rem;\n}\n\n/* Der Auszug ist die Inhaltsvorschau der Runde. Er bekommt eine eigene Zeile,\n damit er nicht mit Marke und Modell um dieselbe Zeile ringt. */\n.szenen-runs__auszug {\n flex: 1 1 100%;\n min-width: 0;\n color: var(--color-text-tertiary);\n font-size: 0.8125rem;\n}\n\n/* B2 \u2014 ein Fehler steht ausserhalb der aufklappbaren Fl\xE4che: Wer eine rote\n Runde sieht, soll nicht erst klicken m\xFCssen, um zu erfahren, woran sie\n gescheitert ist. */\n.szenen-runs__fehler {\n display: grid;\n gap: var(--space-xs);\n padding: var(--space-sm);\n border-radius: var(--radius-sm);\n}\n\n.szenen-runs__fehler p {\n margin: 0;\n}\n\n.szenen-runs__inhalt {\n display: grid;\n gap: var(--space-sm);\n min-width: 0;\n}\n\n.szenen-runs__prompt > h5,\n.szenen-runs__sekundaer > h5 {\n margin: 0 0 var(--space-xs);\n}\n\n.szenen-runs__prompt > p {\n margin: 0;\n white-space: pre-wrap;\n}\n\n.szenen-runs__prompt--negativ > p,\n.szenen-runs__sekundaer > p {\n margin: 0;\n color: var(--color-text-secondary);\n white-space: pre-wrap;\n}\n\n.szenen-runs__mehr {\n justify-self: start;\n margin-top: var(--space-xs);\n padding: 0;\n color: var(--color-accent-teal, var(--color-text-secondary));\n background: transparent;\n border: none;\n font-size: 0.75rem;\n text-decoration: underline;\n cursor: pointer;\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 /* C1 \u2014 auf 390 px bleiben drei Kacheln je Reihe gross genug zum Erkennen\n und klein genug, dass vier Bilder eine Reihe sind und keine Seite. */\n .szenen-bildgalerie {\n grid-template-columns: repeat(auto-fill, minmax(4.5rem, 1fr));\n }\n\n /* Der Viewer bekommt den ganzen Bildschirm; die Bl\xE4tterkn\xF6pfe bleiben am\n Rand, wo der Daumen sie erreicht. */\n .szenen-bildviewer {\n padding: var(--space-xs);\n }\n\n .szenen-bildviewer__blaettern {\n width: 2.25rem;\n height: 4rem;\n }\n\n .szenen-view__intro {\n align-items: start;\n flex-direction: column;\n gap: var(--space-xs);\n padding: var(--space-sm);\n }\n\n .szenen-view__intro h2 {\n font-size: 1.05rem;\n }\n\n .szenen-view__intro > div > p:last-child {\n font-size: 0.78rem;\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 Browser und Inspector, dem Labs-Tab nachgebildet,\n aber mit dem CSS dieser Ansicht: eine gemeinsame Komponente \xFCber zwei\n Dom\xE4nengrenzen w\xE4re eine Kopplung, die keiner gewollt hat.\n\n Der Review vom 2026-08-22 (R12.1/R12.2) hat den Ma\xDFstab gesetzt: Der Bestand\n ist ein **Browser**, kein Seiteninhalt. Vierzig Werke d\xFCrfen die Szenenseite\n nicht meterlang machen, und zw\xF6lf Varianten eines Werkes keine zw\xF6lf\n Bildschirmseiten. Deshalb steht hier alles unter zwei Regeln \u2014 begrenzte\n Fl\xE4che mit eigenem Scroll, und Kacheln statt Karten. */\n\n/* \u2500\u2500 Ebene 1: der Browser \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\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.szenen-bildwahl {\n /* Dieselbe Begr\xFCndung wie beim Inspector: Als Grid-Element w\xFCrde der\n W\xE4hler sonst so breit wie sein breitester Inhalt. */\n min-width: 0;\n}\n\n.szenen-bildwahl__browser {\n /* Die eigene Scrollfl\xE4che ist der ganze Punkt: Der Bestand w\xE4chst nach\n innen, nicht nach unten. Dieselbe Gr\xF6\xDFenordnung wie `max-h-[300px]` im\n Labs-Browser. */\n max-height: 19rem;\n overflow-y: auto;\n overscroll-behavior: contain;\n margin-top: 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.szenen-bildwahl__raster {\n display: grid;\n /* Vier Spalten auf schmalen Displays \u2014 der gemeldete Screenshot zeigte eine\n einzige Spalte gro\xDFer Karten. Breiter wird automatisch dichter. */\n grid-template-columns: repeat(auto-fill, minmax(4.5rem, 1fr));\n gap: var(--space-sm);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.szenen-bildwahl__karte {\n position: relative;\n display: block;\n width: 100%;\n padding: 0;\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n overflow: hidden;\n background: var(--color-bg-surface);\n text-align: left;\n cursor: pointer;\n}\n\n/* Das fokussierte Werk ist klar markiert \u2014 sonst wei\xDF niemand, wessen Galerie\n der Inspector unten zeigt. */\n.szenen-bildwahl__karte.is-fokus {\n border-color: var(--color-accent, var(--color-text-primary));\n box-shadow: 0 0 0 2px var(--color-accent, var(--color-text-primary));\n}\n\n/* \u2500\u2500 Die Kachel selbst \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n/* Der Platz steht, bevor das Bild da ist \u2014 sonst springt das Raster, sobald\n die erste Vorschau eintrifft. */\n.szenen-bildkachel {\n display: block;\n width: 100%;\n aspect-ratio: 1;\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: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n background: linear-gradient(135deg, var(--color-bg-app), var(--color-bg-surface));\n}\n\n/* R12.3: Eine fehlende Vorschau bleibt kachelgro\xDF. Vorher fiel die Karte auf\n ihren Prompttext zur\xFCck \u2014 und ein Prompt hat im echten Bestand mehrere\n hundert Zeichen. */\n.szenen-bildkachel__zeichen {\n font-size: 0.875rem;\n font-weight: 700;\n color: var(--color-text-secondary);\n opacity: 0.6;\n}\n\n/* Wie viele Varianten das Werk hat \u2014 klein und \xFCberlagernd, nie als\n dominanter Text. */\n.szenen-bildwahl__anzahl {\n position: absolute;\n top: 0.15rem;\n right: 0.15rem;\n padding: 0.05rem 0.3rem;\n font-size: 0.625rem;\n font-weight: 700;\n line-height: 1.4;\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/* Eine Zeile, dann Schluss. Der Prompt eines Werkes ist oft mehrere hundert\n Zeichen lang und w\xE4re als Prim\xE4rdarstellung genau die Textw\xFCste, gegen die\n der Review antritt. */\n.szenen-bildwahl__kacheltitel {\n display: block;\n padding: 0.15rem 0.3rem;\n font-size: 0.625rem;\n line-height: 1.3;\n color: var(--color-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* \u2500\u2500 Ebene 2: der Inspector \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\u2500\u2500\u2500\u2500 */\n\n.szenen-bildwahl__inspector {\n /* Ohne `min-width: 0` gewinnt der Inhalt: Der W\xE4hler ist Element eines\n Grids (`.szenen-form`), und dort ist `min-width: auto` die Vorgabe. Der\n Streifen unten ist rund 1000 px breit \u2014 gemessen am 2026-08-22 im echten\n Browser zog er die Auswahl von 374 px auf 1088 px auf und schob die\n ganze Seite waagerecht heraus, statt selbst zu scrollen. */\n min-width: 0;\n margin-top: var(--space-sm);\n padding: var(--space-sm);\n background: var(--color-bg-surface);\n /* Sichtbar abgesetzt vom Browser: zwei Ebenen, nicht eine lange Liste. */\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-bildwahl__inspektorkopf {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--space-sm);\n margin-bottom: var(--space-sm);\n}\n\n.szenen-bildwahl__werktitel {\n font-size: 0.75rem;\n font-weight: 600;\n color: var(--color-text-primary);\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.szenen-bildwahl__zurueck {\n flex: 0 0 auto;\n font-size: 0.6875rem;\n padding: 0.15rem 0.5rem;\n}\n\n/* Der Streifen: waagerecht scrollend, feste kleine Kacheln \u2014 zw\xF6lf Varianten\n bleiben ein \xDCberblick statt zw\xF6lf Bildschirmseiten. */\n.szenen-bildwahl__streifen {\n /* Die Breite kommt vom Elter, nie vom Inhalt \u2014 sonst scrollt hier nichts. */\n max-width: 100%;\n display: flex;\n gap: var(--space-sm);\n margin: 0;\n padding: 0 0 0.25rem;\n list-style: none;\n overflow-x: auto;\n overscroll-behavior-x: contain;\n}\n\n.szenen-bildwahl__streifen > li {\n flex: 0 0 auto;\n width: 5rem;\n}\n\n.szenen-bildwahl__variante {\n position: relative;\n display: block;\n width: 100%;\n padding: 0;\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n overflow: hidden;\n background: var(--color-bg-app);\n cursor: pointer;\n}\n\n.szenen-bildwahl__variante.is-gewaehlt {\n border-color: var(--color-accent, var(--color-text-primary));\n box-shadow: 0 0 0 2px var(--color-accent, var(--color-text-primary));\n}\n\n.szenen-bildwahl__nummer {\n position: absolute;\n bottom: 0.15rem;\n left: 0.15rem;\n padding: 0.05rem 0.3rem;\n font-size: 0.625rem;\n line-height: 1.4;\n color: var(--color-text-primary);\n background: var(--color-bg-elevated);\n border-radius: var(--radius-sm);\n}\n\n/* \u2500\u2500 Das Gew\xE4hlte \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\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.25rem;\n flex: 0 0 auto;\n border-radius: var(--radius-sm);\n}\n\n.szenen-bildwahl__gewaehlt .szenen-bildwahl__kacheltitel {\n min-width: 0;\n max-width: 12rem;\n font-size: 0.75rem;\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/* Das Eingangsbild benutzt seit C3 dieselbe Kachel wie W\xE4hler und Galerie.\n Sie ist von Haus aus so breit wie ihr Platz \u2014 in dieser Zeile bekommt sie\n deshalb eine feste Gr\xF6sse, sonst sch\xF6be sie die Metadaten heraus. */\n.szenen-bilder__eingangsliste .szenen-bildkachel {\n flex: 0 0 auto;\n width: 4rem;\n border-radius: var(--radius-sm);\n}\n\n.szenen-bilder__batch {\n display: grid;\n gap: var(--space-sm);\n min-width: 0;\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.szenen-bilder__batchkopf {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n justify-content: space-between;\n gap: var(--space-sm);\n}\n\n.szenen-bilder__herkunft {\n color: var(--color-text-tertiary);\n font-size: 0.75rem;\n}\n\n/* Review-Befund \u2014 ein transienter Bildfehler ist wiederholbar, kein\n endg\xFCltiges \u201Efehlt". Dezent wie ein Angebot, nicht wie ein Fehlerzustand. */\n.szenen-bilder__retry {\n justify-self: start;\n padding: 0.15rem 0.5rem;\n font-size: 0.75rem;\n color: var(--color-text-secondary);\n background: var(--color-bg-app);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n cursor: pointer;\n}\n\n/* C4 \u2014 die Provenienz vollst\xE4ndig, aber sekund\xE4r. */\n.szenen-bilder__technik > summary {\n color: var(--color-text-secondary);\n font-size: 0.75rem;\n cursor: pointer;\n}\n\n.szenen-bilder__meta {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--space-xs);\n font-size: 0.75rem;\n color: var(--color-text-secondary);\n}\n\n/* C5 \u2014 der Verlauf verdichtet, seine Ausreisser nicht. */\n.szenen-bilder__verlauf > summary {\n color: var(--color-text-secondary);\n font-size: 0.8125rem;\n cursor: pointer;\n}\n\n.szenen-bilder__verlauf[open] > summary {\n margin-bottom: var(--space-sm);\n}\n\n.szenen-bilder__auffaellig {\n margin: 0;\n padding: var(--space-sm);\n border-radius: var(--radius-sm);\n font-size: 0.8125rem;\n}\n\n.szenen-bilder__auftragskennung {\n flex-basis: 100%;\n color: var(--color-text-tertiary);\n font-size: 0.6875rem;\n}\n\n/* \u2500\u2500 C1/C2 \u2014 Galerie und grosse Ansicht \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/* Das Raster w\xE4chst mit dem Platz: auf 390 px drei bis vier Kacheln je Reihe,\n auf dem Desktop entsprechend mehr. Vorher war jedes Bild eine Zeile aus\n Daumennagel plus vier Metadatenfeldern \u2014 vier Bilder ergaben vier Textzeilen\n und das Protokoll war die Hauptsache. */\n.szenen-bildgalerie {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(5.5rem, 1fr));\n gap: var(--space-sm);\n min-width: 0;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.szenen-bildgalerie__kachel {\n position: relative;\n display: block;\n width: 100%;\n padding: 0;\n overflow: hidden;\n background: var(--color-bg-surface);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n cursor: pointer;\n}\n\n.szenen-bildgalerie__kachel:hover,\n.szenen-bildgalerie__kachel:focus-visible {\n border-color: var(--color-accent-teal, var(--color-text-primary));\n}\n\n.szenen-bildgalerie__nummer {\n position: absolute;\n bottom: 0.15rem;\n left: 0.15rem;\n padding: 0.05rem 0.3rem;\n font-size: 0.625rem;\n line-height: 1.4;\n color: var(--color-text-primary);\n background: var(--color-bg-elevated);\n border-radius: var(--radius-sm);\n}\n\n/* Die grosse Ansicht liegt \xFCber allem und bringt ihren eigenen Grund mit \u2014\n `position: fixed` bezieht sich im Flow-Canvas auf dessen Rahmen, in React auf\n das Fenster; in beiden F\xE4llen genau das Gew\xFCnschte. */\n.szenen-bildviewer {\n position: fixed;\n inset: 0;\n z-index: 1000;\n display: grid;\n grid-template-rows: auto 1fr auto;\n gap: var(--space-sm);\n padding: var(--space-sm);\n /* Deckend, nicht get\xF6nt: Bei 0.92 schimmerte der Laufbereich durch und man\n las Prompttext quer \xFCber dem Bild \u2014 im Browser gemessen. */\n background: #000;\n}\n\n/* Titel, Z\xE4hlung, Schliessen \u2014 drei Spalten, damit ein langer Werktitel die\n Z\xE4hlung nicht in die n\xE4chste Zeile und \xFCber das Bild schiebt. */\n.szenen-bildviewer__kopf {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto auto;\n align-items: center;\n gap: var(--space-sm);\n color: var(--color-text-primary);\n}\n\n.szenen-bildviewer__titel {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 600;\n}\n\n.szenen-bildviewer__zaehler {\n color: var(--color-text-secondary);\n font-size: 0.8125rem;\n}\n\n.szenen-bildviewer__buehne {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--space-sm);\n min-height: 0;\n min-width: 0;\n}\n\n/* Der Rahmen nimmt den Platz, der zwischen den Bl\xE4tterkn\xF6pfen \xFCbrig ist \u2014\n `min-width: 0`, damit er auch wirklich schrumpft statt sie hinauszuschieben. */\n.szenen-bildviewer__rahmen {\n flex: 1 1 auto;\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 0;\n min-height: 0;\n height: 100%;\n}\n\n.szenen-bildviewer__bild {\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n}\n\n.szenen-bildviewer__leer {\n color: var(--color-text-secondary);\n text-align: center;\n}\n\n/* Die Bl\xE4tterkn\xF6pfe sind daumengross und liegen an den R\xE4ndern \u2014 auf einem\n Telefon ist der Rand erreichbar, die Bildmitte nicht. */\n.szenen-bildviewer__blaettern {\n flex: 0 0 auto;\n width: 2.75rem;\n height: 3.5rem;\n font-size: 1.75rem;\n line-height: 1;\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 cursor: pointer;\n}\n\n.szenen-bildviewer__details {\n max-height: 8rem;\n overflow-y: auto;\n color: var(--color-text-secondary);\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/* D \u2014 die Aktionen stehen abgesetzt und standardm\xE4ssig kompakt: Sie sind\n Angebote, keine Ausk\xFCnfte. Vorher hielten beide Formulare dauerhaft offen und\n machten aus jedem Lauf zwei zus\xE4tzliche Bildschirmseiten. */\n.szenen-bilder__aktion {\n min-width: 0;\n padding: var(--space-sm) var(--space-md);\n border: 1px dashed var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-bilder__aktion > summary {\n color: var(--color-text-primary);\n font-weight: 600;\n cursor: pointer;\n}\n\n.szenen-bilder__aktion[open] > summary {\n margin-bottom: var(--space-sm);\n}\n\n.szenen-bilder__aktion-inhalt {\n display: grid;\n gap: var(--space-sm);\n min-width: 0;\n padding-bottom: var(--space-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/* Regelwerk-UX Schritt 16 \u2014 der ZIP-Sammelimport.\n *\n * \xA78: Der gemeldete Einsatz ist mobil. Deshalb **ein** Seedfeld statt einer\n * gestapelten Eingabe je Bild, und die Zuordnung Bild -> Seed als umbrechende\n * Zeile statt als Tabelle: Eine Tabelle waere auf 390 px abgeschnitten, und\n * abgeschnitten heisst hier, dass der Nutzer die Zuordnung nicht mehr pruefen\n * kann \u2014 genau die Pruefung, wegen der die Vorschau existiert. */\n.szenen-bilder__zip {\n display: grid;\n gap: var(--space-sm);\n min-width: 0;\n}\n\n.szenen-bilder__zipkopf {\n margin: 0;\n overflow-wrap: anywhere;\n}\n\n.szenen-bilder__zipvorlagen {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-xs);\n margin: 0;\n}\n\n/* Eine laengere Liste bleibt scrollbar, statt die Sektion und damit den\n * Import-Knopf aus dem Blick zu schieben (\xA78, letzter Punkt). */\n.szenen-bilder__zipliste {\n max-height: 16rem;\n overflow-y: auto;\n}\n\n.szenen-bilder__zipliste > li {\n display: flex;\n flex-wrap: wrap;\n justify-content: space-between;\n gap: var(--space-xs);\n min-width: 0;\n}\n\n.szenen-bilder__zipliste > li > span:first-child {\n overflow-wrap: anywhere;\n}\n\n.szenen-bilder__zipliste > li > span:last-child {\n color: var(--color-text-secondary);\n white-space: nowrap;\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 von Objektfeldern (`prompt_render_settings`, `overrides`,\n `blender_render_settings`) \u2014 seit A1/A2 auf- und zuklappbar. Eine optionale\n Gruppe startet zu: Blender und `overrides` f\xFCllten auf einem 390-px-Display\n sonst mehrere Bildschirmseiten, obwohl die meisten Shots sie nie anfassen. */\n.szenen-detail__group {\n min-width: 0;\n margin: var(--space-md) 0;\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__group > summary {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--space-xs);\n color: var(--color-text-secondary);\n font-weight: 600;\n cursor: pointer;\n}\n\n.szenen-detail__group[open] > summary {\n margin-bottom: var(--space-sm);\n}\n\n/* A2 \u2014 dass eine zugeklappte Gruppe schon Werte tr\xE4gt, muss man sehen, ohne\n sie zu \xF6ffnen. Sonst \xFCbersieht man eine gesetzte Blender-Ausgabe genau so\n lange, bis sie im Ergebnis auftaucht. */\n.szenen-detail__group-status {\n padding: 0.05rem 0.4rem;\n font-size: 0.6875rem;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--color-text-tertiary);\n background: var(--color-bg-app);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-sm);\n}\n\n.szenen-detail__group[data-konfiguriert="ja"] > summary .szenen-detail__group-status {\n color: var(--color-text-primary);\n border-color: var(--color-accent-teal, var(--color-border));\n}\n\n.szenen-detail__group-inhalt {\n display: grid;\n gap: var(--space-sm);\n min-width: 0;\n padding-bottom: var(--space-sm);\n}\n\n/* A3 \u2014 die Kernaussage steht am Feld, die Begr\xFCndung dahinter. Nicht als\n Tooltip: Auf einem Telefon gibt es kein Hover, und was dort unerreichbar ist,\n ist gestrichen \u2014 nur ohne dass es jemand entschieden h\xE4tte. */\n.szenen-detail__warum > summary {\n color: var(--color-text-tertiary);\n font-size: 0.75rem;\n cursor: pointer;\n}\n\n.szenen-detail__warum > small {\n display: block;\n padding-top: var(--space-xs);\n color: var(--color-text-tertiary);\n}\n\n.szenen-detail__fieldrow {\n display: grid;\n gap: var(--space-xs);\n min-width: 0;\n margin-bottom: var(--space-md);\n}\n\n/* A4 \u2014 der technische Feldname bleibt lesbar, tritt aber hinter das fachliche\n Label zur\xFCck. */\n.szenen-detail__field-label {\n color: var(--color-text-primary);\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\n/* Bereich `Prompts` \u2014 die herkunftsuebergreifende Sicht (P1-A).\n *\n * Die Eintraege selbst rendert `RundenEintrag` und tragen damit die\n * `szenen-runs__*`-Regeln. Hier stehen nur Rahmen und Kennzeichen; eine zweite\n * Laufdarstellung gibt es nicht, also braucht sie auch kein zweites Aussehen. */\n.szenen-prompts__toolbar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-sm);\n}\n\n.szenen-prompts__heading {\n margin: 0;\n font-size: 1.05rem;\n margin-right: auto;\n}\n\n.szenen-prompts__filter {\n display: flex;\n align-items: center;\n gap: var(--space-xs);\n color: var(--color-text-secondary);\n font-size: 0.8rem;\n}\n\n.szenen-prompts__eintrag {\n list-style: none;\n margin-bottom: var(--space-md);\n}\n\n.szenen-prompts__lauf {\n padding-left: 0;\n}\n\n.szenen-prompts__herkunft {\n margin: 0 0 var(--space-xs);\n color: var(--color-text-secondary);\n font-size: 0.78rem;\n}\n\n.szenen-prompts__hinweis {\n font-style: italic;\n}\n\n.szenen-prompts__ausgeblendet {\n color: var(--color-text-secondary);\n font-size: 0.8rem;\n}\n';
175
175
 
176
176
  // src/lib/utils.ts
177
177
  var getFormattedTimestamp = () => {
@@ -253,7 +253,7 @@ function indexOfBytes(buffer, search, start = 0) {
253
253
  return -1;
254
254
  }
255
255
  function createXMPPacket(prompt, seed, model, id, tags = [], hierarchy) {
256
- const sanitize = (str2) => (str2 || "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
256
+ const sanitize = (str3) => (str3 || "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
257
257
  const escPrompt = sanitize(prompt);
258
258
  const escModel = sanitize(model || "AI Model");
259
259
  const escHierarchy = sanitize(hierarchy || "");
@@ -1656,7 +1656,7 @@ function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMove
1656
1656
  }
1657
1657
 
1658
1658
  // src/components/AvatarArchitectApp.tsx
1659
- var import_react50 = require("react");
1659
+ var import_react53 = require("react");
1660
1660
 
1661
1661
  // src/components/PromptTab.tsx
1662
1662
  var import_react15 = require("react");
@@ -6039,7 +6039,7 @@ function FlowWorkspacePicker({
6039
6039
  }
6040
6040
 
6041
6041
  // src/features/szenen/SzenenView.tsx
6042
- var import_react49 = require("react");
6042
+ var import_react52 = require("react");
6043
6043
 
6044
6044
  // src/lib/szenen/viewPort.ts
6045
6045
  function capabilitiesOf(port) {
@@ -6054,7 +6054,10 @@ function capabilitiesOf(port) {
6054
6054
  imageOrderHistory: typeof port.imageBatchHistory?.listOrdersForPromptRun === "function",
6055
6055
  imageBatchWrite: typeof port.imageBatchWrite?.importBatch === "function",
6056
6056
  imageGeneration: typeof port.imageGeneration?.startFromPromptRun === "function",
6057
- imageJobs: typeof port.imageJobs?.claimAndRun === "function"
6057
+ imageJobs: typeof port.imageJobs?.claimAndRun === "function",
6058
+ // P1-A: die herkunftsuebergreifende Prompt-Sicht. Ein Host ohne sie
6059
+ // verliert nur diesen Bereich — nicht die drei bestehenden Einstiege.
6060
+ promptOverview: typeof port.runs.listAllRuns === "function"
6058
6061
  };
6059
6062
  }
6060
6063
  async function callOptional(capability, operation) {
@@ -9138,37 +9141,52 @@ function formatSchemaErrors(errors) {
9138
9141
  }
9139
9142
 
9140
9143
  // src/features/szenen/szenenViewModel.ts
9141
- var KEINE_BAUSTEINE = [
9142
- "scene",
9144
+ var BAUSTEIN_TYPES = [
9145
+ "scene_component",
9146
+ "style",
9147
+ "shot_concept",
9148
+ "prompt_fragment",
9149
+ "prompt_policy"
9150
+ ];
9151
+ var ERWEITERT_REIHENFOLGE = [
9143
9152
  "scene_setup",
9144
- "shot"
9153
+ "shot",
9154
+ "camera_preset",
9155
+ "camera_pattern",
9156
+ "target_model",
9157
+ "scene_derivation_policy",
9158
+ "sequence"
9145
9159
  ];
9146
- var BAUSTEIN_TYPES = SZENEN_ELEMENT_TYPES.filter((typ) => !KEINE_BAUSTEINE.includes(typ));
9160
+ var ERWEITERT_TYPES = (() => {
9161
+ const gehoertHierher = (typ) => typ !== "scene" && !BAUSTEIN_TYPES.includes(typ);
9162
+ const bekannt = ERWEITERT_REIHENFOLGE.filter(
9163
+ (typ) => SZENEN_ELEMENT_TYPES.includes(typ) && gehoertHierher(typ)
9164
+ );
9165
+ const rest = SZENEN_ELEMENT_TYPES.filter(
9166
+ (typ) => gehoertHierher(typ) && !bekannt.includes(typ)
9167
+ );
9168
+ return [...bekannt, ...rest];
9169
+ })();
9147
9170
  var ALLE_BEREICHE = [
9148
9171
  {
9149
- id: "derive",
9150
- title: "0 \xB7 Rekonstruktion",
9151
- description: "Aus einem Bild oder Prompt einen Szenenentwurf vorschlagen lassen."
9152
- },
9153
- {
9154
- id: "elements",
9155
- title: "1 \xB7 Bausteine",
9156
- description: "Figuren, Orte, Stile und Regeln f\xFCr sp\xE4tere Bildprompts pflegen."
9172
+ id: "scene",
9173
+ title: "Szenen",
9174
+ description: "Szenen anlegen, aus Bild oder Text ableiten und daraus Prompts starten."
9157
9175
  },
9158
9176
  {
9159
- id: "scene",
9160
- title: "2 \xB7 Szene",
9161
- description: "Aus Bausteinen eine konkrete Szene und ihre Beziehungen bauen."
9177
+ id: "prompts",
9178
+ title: "Prompts",
9179
+ description: "Alle erzeugten Prompts mit ihren Bildern \u2014 unabhaengig davon, ueber welchen Weg sie entstanden sind."
9162
9180
  },
9163
9181
  {
9164
- id: "setup",
9165
- title: "3 \xB7 Bildaufbau",
9166
- description: "Stil, Komposition und Kamera f\xFCr das sp\xE4tere Bild festlegen."
9182
+ id: "elements",
9183
+ title: "Bausteine",
9184
+ description: "Figuren, Orte, Stile, Prompt-Bausteine und Prompt-Regeln pflegen."
9167
9185
  },
9168
9186
  {
9169
- id: "shot",
9170
- title: "4 \xB7 Prompt",
9171
- description: "Bildprompt erzeugen, Varianten vergleichen und als Item speichern."
9187
+ id: "advanced",
9188
+ title: "Erweitert",
9189
+ description: "Optionale Pr\xE4zisierung: Bildaufbau, Shot, Kameras, Zielmodelle, Regeln, Sequenzen."
9172
9190
  },
9173
9191
  {
9174
9192
  id: "media",
@@ -9178,16 +9196,12 @@ var ALLE_BEREICHE = [
9178
9196
  ];
9179
9197
  function areasFor(port) {
9180
9198
  const faehig = capabilitiesOf(port);
9181
- return ALLE_BEREICHE.filter((bereich) => {
9182
- if (bereich.id === "media") return faehig.media;
9183
- if (bereich.id === "derive") return faehig.derivation;
9184
- return true;
9185
- });
9199
+ return ALLE_BEREICHE.filter((bereich) => bereich.id !== "media" || faehig.media);
9186
9200
  }
9187
9201
  function resolveArea(port, gewuenscht) {
9188
9202
  const verfuegbar = areasFor(port);
9189
9203
  if (gewuenscht && verfuegbar.some((b) => b.id === gewuenscht)) return gewuenscht;
9190
- return verfuegbar.find((b) => b.id === "elements")?.id ?? verfuegbar[0].id;
9204
+ return verfuegbar.find((b) => b.id === "scene")?.id ?? verfuegbar[0].id;
9191
9205
  }
9192
9206
  function noticeFor(ergebnis, was) {
9193
9207
  switch (ergebnis.kind) {
@@ -9228,7 +9242,7 @@ function noticeFor(ergebnis, was) {
9228
9242
  }
9229
9243
 
9230
9244
  // src/features/szenen/areas/ElementsArea.tsx
9231
- var import_react44 = require("react");
9245
+ var import_react45 = require("react");
9232
9246
 
9233
9247
  // src/features/szenen/areas/usePortQuery.ts
9234
9248
  var import_react33 = require("react");
@@ -9260,7 +9274,7 @@ function usePortQuery(frage, abhaengigkeiten) {
9260
9274
  }
9261
9275
 
9262
9276
  // src/features/szenen/areas/ElementDetail.tsx
9263
- var import_react42 = require("react");
9277
+ var import_react43 = require("react");
9264
9278
 
9265
9279
  // src/features/szenen/elementLabels.ts
9266
9280
  var LABELS = {
@@ -12968,19 +12982,7 @@ var VersionNavigator = ({
12968
12982
  };
12969
12983
 
12970
12984
  // src/features/szenen/areas/RunsPanel.tsx
12971
- var import_react41 = require("react");
12972
-
12973
- // src/lib/szenen/itemDerivation.ts
12974
- var SZENEN_ITEM_PREFIX = "szn-";
12975
- function itemIdForRun(runId) {
12976
- return `${SZENEN_ITEM_PREFIX}${runId}`;
12977
- }
12978
- function roundOfRun(run, runsOfShot) {
12979
- return runsOfShot.findIndex((kandidat) => kandidat.run_id === run.run_id) + 1;
12980
- }
12981
-
12982
- // src/features/szenen/areas/RunImagesPanel.tsx
12983
- var import_react40 = __toESM(require("react"));
12985
+ var import_react42 = require("react");
12984
12986
 
12985
12987
  // src/lib/szenen/elementEvents.ts
12986
12988
  var SZENEN_EVENT_PREFIX = "szenen.";
@@ -13413,6 +13415,144 @@ function collectPromptRuns(events) {
13413
13415
  function findPromptRun(events, runId) {
13414
13416
  return collectPromptRuns(events).find((run) => run.run_id === runId) ?? null;
13415
13417
  }
13418
+ var istKennung = (wert) => typeof wert === "string" && wert.trim().length > 0;
13419
+ function groupOf(run) {
13420
+ const quelle = sourceOf(run);
13421
+ if (quelle.kind === "published") {
13422
+ return istKennung(quelle.shot_version_id) ? {
13423
+ kind: "published",
13424
+ key: `published:${quelle.shot_version_id}`,
13425
+ shot_version_id: quelle.shot_version_id
13426
+ } : {
13427
+ kind: "unresolved",
13428
+ key: `run:${run.run_id}`,
13429
+ reason: `Der Lauf ${run.run_id} gilt als ver\xF6ffentlichte Herkunft, tr\xE4gt aber keine shot_version_id. Er steht deshalb f\xFCr sich; eine Zuordnung w\xE4re geraten.`
13430
+ };
13431
+ }
13432
+ if (quelle.kind === "draft_chain") {
13433
+ return istKennung(quelle.chain_id) ? { kind: "draft_chain", key: `chain:${quelle.chain_id}`, chain_id: quelle.chain_id } : {
13434
+ kind: "unresolved",
13435
+ key: `run:${run.run_id}`,
13436
+ reason: `Der Lauf ${run.run_id} stammt aus einer Draft-Kette ohne chain_id. Er steht deshalb f\xFCr sich; eine Zuordnung w\xE4re geraten.`
13437
+ };
13438
+ }
13439
+ const elementId = quelle.scene?.element_id;
13440
+ return istKennung(elementId) ? { kind: "scene_direct", key: `scene:${elementId}`, scene_element_id: elementId } : {
13441
+ kind: "unresolved",
13442
+ key: `run:${run.run_id}`,
13443
+ reason: `Der Lauf ${run.run_id} entstand direkt aus einer Szene, nennt aber kein Szenen-Element. Er steht deshalb f\xFCr sich; eine Zuordnung w\xE4re geraten.`
13444
+ };
13445
+ }
13446
+ function groupKeyOf(run) {
13447
+ return groupOf(run).key;
13448
+ }
13449
+ function siblingsOf(run, alle) {
13450
+ const schluessel = groupKeyOf(run);
13451
+ return alle.filter((kandidat) => groupKeyOf(kandidat) === schluessel);
13452
+ }
13453
+ function namingElementOf(run) {
13454
+ const quelle = sourceOf(run);
13455
+ if (quelle.kind === "scene_direct") {
13456
+ return istKennung(quelle.scene?.element_id) ? quelle.scene.element_id : null;
13457
+ }
13458
+ if (quelle.kind === "draft_chain") {
13459
+ const szene = (quelle.nodes ?? []).find((knoten) => knoten?.type === "scene");
13460
+ if (szene && istKennung(szene.element_id)) return szene.element_id;
13461
+ const shot = (quelle.nodes ?? []).find((knoten) => knoten?.type === "shot");
13462
+ if (shot && istKennung(shot.element_id)) return shot.element_id;
13463
+ }
13464
+ return istKennung(run.shot_element_id) ? run.shot_element_id : null;
13465
+ }
13466
+ function contextOfRun(run) {
13467
+ const kette = run.input_payload?.context_chain;
13468
+ if (!Array.isArray(kette) || kette.length === 0) return void 0;
13469
+ const erstes = kette[0];
13470
+ if (erstes === null) return null;
13471
+ return istKennung(erstes) ? erstes : void 0;
13472
+ }
13473
+ function selectPromptRuns(alle, filter = {}) {
13474
+ const status = filter.status ?? "succeeded";
13475
+ const nachKontext = filter.contextId !== void 0;
13476
+ const runs = [];
13477
+ let hiddenByStatus = 0;
13478
+ let hiddenByContext = 0;
13479
+ let withoutContext = 0;
13480
+ for (const run of alle) {
13481
+ if (status !== "all" && run.status !== status) {
13482
+ hiddenByStatus += 1;
13483
+ continue;
13484
+ }
13485
+ if (nachKontext) {
13486
+ const kontext = contextOfRun(run);
13487
+ if (kontext === void 0) {
13488
+ withoutContext += 1;
13489
+ continue;
13490
+ }
13491
+ if (kontext !== filter.contextId) {
13492
+ hiddenByContext += 1;
13493
+ continue;
13494
+ }
13495
+ }
13496
+ runs.push(run);
13497
+ }
13498
+ return { runs, hiddenByStatus, hiddenByContext, withoutContext };
13499
+ }
13500
+
13501
+ // src/lib/szenen/itemDerivation.ts
13502
+ var SZENEN_ITEM_PREFIX = "szn-";
13503
+ function itemIdForRun(runId) {
13504
+ return `${SZENEN_ITEM_PREFIX}${runId}`;
13505
+ }
13506
+ function roundOfRun(run, runsOfShot) {
13507
+ return runsOfShot.findIndex((kandidat) => kandidat.run_id === run.run_id) + 1;
13508
+ }
13509
+
13510
+ // src/features/szenen/areas/useLaufTitel.ts
13511
+ var import_react39 = require("react");
13512
+ function useLaufTitel(port, laeufe) {
13513
+ const [titelJeElement, setTitelJeElement] = (0, import_react39.useState)(
13514
+ () => /* @__PURE__ */ new Map()
13515
+ );
13516
+ const gebraucht = [...new Set(
13517
+ laeufe.map(namingElementOf).filter((id) => id !== null)
13518
+ )].sort();
13519
+ const schluessel = gebraucht.join(",");
13520
+ (0, import_react39.useEffect)(() => {
13521
+ let abgemeldet = false;
13522
+ const offen = gebraucht.filter((id) => !titelJeElement.has(id));
13523
+ if (offen.length === 0) return void 0;
13524
+ void (async () => {
13525
+ const gelesen = /* @__PURE__ */ new Map();
13526
+ for (const id of offen) {
13527
+ try {
13528
+ const ergebnis = await port.elements.getElement(id);
13529
+ gelesen.set(
13530
+ id,
13531
+ ergebnis.kind === "ok" && ergebnis.value ? ergebnis.value.metadata?.title ?? null : null
13532
+ );
13533
+ } catch {
13534
+ gelesen.set(id, null);
13535
+ }
13536
+ }
13537
+ if (abgemeldet) return;
13538
+ setTitelJeElement((bisher) => new Map([...bisher, ...gelesen]));
13539
+ })();
13540
+ return () => {
13541
+ abgemeldet = true;
13542
+ };
13543
+ }, [port, schluessel]);
13544
+ const jeLauf = /* @__PURE__ */ new Map();
13545
+ for (const lauf of laeufe) {
13546
+ const elementId = namingElementOf(lauf);
13547
+ if (!elementId) continue;
13548
+ const titel = titelJeElement.get(elementId);
13549
+ if (titel) jeLauf.set(lauf.run_id, titel);
13550
+ }
13551
+ return jeLauf;
13552
+ }
13553
+
13554
+ // src/features/szenen/areas/RunImagesPanel.tsx
13555
+ var import_react41 = __toESM(require("react"));
13416
13556
 
13417
13557
  // src/lib/szenen/seedListe.ts
13418
13558
  var DEFAULT_SEED_VORLAGE = {
@@ -13539,7 +13679,7 @@ async function leseBilderAusZip(datei) {
13539
13679
  }
13540
13680
 
13541
13681
  // src/features/szenen/areas/Bildbausteine.tsx
13542
- var import_react39 = require("react");
13682
+ var import_react40 = require("react");
13543
13683
  var import_jsx_runtime35 = require("react/jsx-runtime");
13544
13684
  var Kachel = ({
13545
13685
  quelle,
@@ -13556,11 +13696,11 @@ var BildViewer = ({
13556
13696
  }) => {
13557
13697
  const anzahl = bilder.length;
13558
13698
  const bild = bilder[position];
13559
- const blaettern = (0, import_react39.useCallback)((schritt) => {
13699
+ const blaettern = (0, import_react40.useCallback)((schritt) => {
13560
13700
  if (anzahl === 0) return;
13561
13701
  onWechseln((position + schritt + anzahl) % anzahl);
13562
13702
  }, [anzahl, position, onWechseln]);
13563
- (0, import_react39.useEffect)(() => {
13703
+ (0, import_react40.useEffect)(() => {
13564
13704
  const taste = (e) => {
13565
13705
  if (e.key === "Escape") onSchliessen();
13566
13706
  else if (e.key === "ArrowLeft") blaettern(-1);
@@ -13643,8 +13783,8 @@ var BildViewer = ({
13643
13783
  );
13644
13784
  };
13645
13785
  var BildGalerie = ({ bilder, testId, satzTitel }) => {
13646
- const [offen, setOffen] = (0, import_react39.useState)(null);
13647
- (0, import_react39.useEffect)(() => {
13786
+ const [offen, setOffen] = (0, import_react40.useState)(null);
13787
+ (0, import_react40.useEffect)(() => {
13648
13788
  setOffen((vorher) => vorher !== null && vorher >= bilder.length ? null : vorher);
13649
13789
  }, [bilder.length]);
13650
13790
  if (bilder.length === 0) return null;
@@ -13700,11 +13840,11 @@ var AUFTRAG_TEXT = {
13700
13840
  };
13701
13841
  var RunImagesPanel = ({ port, lauf }) => {
13702
13842
  const faehig = capabilitiesOf(port);
13703
- const [nachladen, setNachladen] = (0, import_react40.useState)(0);
13843
+ const [nachladen, setNachladen] = (0, import_react41.useState)(0);
13704
13844
  const neuLesen = () => setNachladen((n) => n + 1);
13705
- const [erzeugenOffen, setErzeugenOffen] = (0, import_react40.useState)(null);
13706
- const [zuordnenOffen, setZuordnenOffen] = (0, import_react40.useState)(null);
13707
- const [chatAuftraegeOffen, setChatAuftraegeOffen] = (0, import_react40.useState)(null);
13845
+ const [erzeugenOffen, setErzeugenOffen] = (0, import_react41.useState)(null);
13846
+ const [zuordnenOffen, setZuordnenOffen] = (0, import_react41.useState)(null);
13847
+ const [chatAuftraegeOffen, setChatAuftraegeOffen] = (0, import_react41.useState)(null);
13708
13848
  const quelle = sourceOf(lauf);
13709
13849
  const [wahlPhase] = usePortQuery(
13710
13850
  () => port.imageGeneration ? port.imageGeneration.imageModelChoiceForRun(lauf.run_id) : Promise.resolve({ kind: "ok", value: null }),
@@ -14018,12 +14158,12 @@ var BestandsBild = ({ port, mediaRef }) => {
14018
14158
  ] });
14019
14159
  };
14020
14160
  function useBildQuellen(port, bilder) {
14021
- const [stand, setStand] = (0, import_react40.useState)(() => ({ quellen: /* @__PURE__ */ new Map(), fehler: /* @__PURE__ */ new Map() }));
14022
- const gehalten = (0, import_react40.useRef)(/* @__PURE__ */ new Map());
14023
- const gescheitert = (0, import_react40.useRef)(/* @__PURE__ */ new Map());
14024
- const [versuch, setVersuch] = (0, import_react40.useState)(0);
14161
+ const [stand, setStand] = (0, import_react41.useState)(() => ({ quellen: /* @__PURE__ */ new Map(), fehler: /* @__PURE__ */ new Map() }));
14162
+ const gehalten = (0, import_react41.useRef)(/* @__PURE__ */ new Map());
14163
+ const gescheitert = (0, import_react41.useRef)(/* @__PURE__ */ new Map());
14164
+ const [versuch, setVersuch] = (0, import_react41.useState)(0);
14025
14165
  const schluessel = bilder.map((b) => `${b.media_ref}|${b.mime_type ?? ""}`).join(" ");
14026
- (0, import_react40.useEffect)(() => {
14166
+ (0, import_react41.useEffect)(() => {
14027
14167
  const bestand = port.imageLibrary;
14028
14168
  if (!bestand) return void 0;
14029
14169
  let abgebrochen = false;
@@ -14159,7 +14299,7 @@ var BatchKachel = ({ port, batch }) => {
14159
14299
  /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { children: [
14160
14300
  "Batches",
14161
14301
  " ",
14162
- batch.origins.length > 0 ? batch.origins.map((h, i) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_react40.default.Fragment, { children: [
14302
+ batch.origins.length > 0 ? batch.origins.map((h, i) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_react41.default.Fragment, { children: [
14163
14303
  i > 0 ? ", " : "",
14164
14304
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("code", { children: h.generation_batch_id }),
14165
14305
  ` (${HERKUNFT_KURZ[h.source_kind]})`
@@ -14188,11 +14328,11 @@ var ProfilBefunde = ({ wahl, runId }) => {
14188
14328
  ] });
14189
14329
  };
14190
14330
  var ErzeugenAktion = ({ port, lauf, wahl, wahlFehler, onFertig }) => {
14191
- const [modellWahl, setModellWahl] = (0, import_react40.useState)(null);
14192
- const [anzahlText, setAnzahlText] = (0, import_react40.useState)("1");
14193
- const [seitenverhaeltnis, setSeitenverhaeltnis] = (0, import_react40.useState)("");
14194
- const [laeuft, setLaeuft] = (0, import_react40.useState)(false);
14195
- const [notiz, setNotiz] = (0, import_react40.useState)(null);
14331
+ const [modellWahl, setModellWahl] = (0, import_react41.useState)(null);
14332
+ const [anzahlText, setAnzahlText] = (0, import_react41.useState)("1");
14333
+ const [seitenverhaeltnis, setSeitenverhaeltnis] = (0, import_react41.useState)("");
14334
+ const [laeuft, setLaeuft] = (0, import_react41.useState)(false);
14335
+ const [notiz, setNotiz] = (0, import_react41.useState)(null);
14196
14336
  if (!port.imageGeneration) {
14197
14337
  return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { "data-testid": `erzeugen-unsupported-${lauf.run_id}`, children: "Dieser Host erzeugt keine Bilder \u2014 entweder fehlt der Providerzugang oder der gesicherte Speicherweg. Externe Bilder lassen sich trotzdem zuordnen." });
14198
14338
  }
@@ -14369,23 +14509,23 @@ var ErzeugenAktion = ({ port, lauf, wahl, wahlFehler, onFertig }) => {
14369
14509
  };
14370
14510
  var DEFAULT_VORLAGE_KEY = "__default__";
14371
14511
  var ZuordnenAktion = ({ port, lauf, onFertig }) => {
14372
- const [dateien, setDateien] = (0, import_react40.useState)([]);
14373
- const [seeds, setSeeds] = (0, import_react40.useState)({});
14374
- const [zip, setZip] = (0, import_react40.useState)(null);
14375
- const [zipLaedt, setZipLaedt] = (0, import_react40.useState)(false);
14376
- const [seedListe, setSeedListe] = (0, import_react40.useState)("");
14377
- const [vorlagen, setVorlagen] = (0, import_react40.useState)([DEFAULT_SEED_VORLAGE]);
14378
- const [vorlageKey, setVorlageKey] = (0, import_react40.useState)(DEFAULT_VORLAGE_KEY);
14379
- const [vorlagenStand, setVorlagenStand] = (0, import_react40.useState)(0);
14380
- const [vorlageBenennen, setVorlageBenennen] = (0, import_react40.useState)(false);
14381
- const [vorlageName, setVorlageName] = (0, import_react40.useState)("");
14382
- const [modell, setModell] = (0, import_react40.useState)("");
14383
- const [itemId, setItemId] = (0, import_react40.useState)("");
14384
- const [laeuft, setLaeuft] = (0, import_react40.useState)(false);
14385
- const [notiz, setNotiz] = (0, import_react40.useState)(null);
14386
- const auswahlMarke = (0, import_react40.useRef)(0);
14512
+ const [dateien, setDateien] = (0, import_react41.useState)([]);
14513
+ const [seeds, setSeeds] = (0, import_react41.useState)({});
14514
+ const [zip, setZip] = (0, import_react41.useState)(null);
14515
+ const [zipLaedt, setZipLaedt] = (0, import_react41.useState)(false);
14516
+ const [seedListe, setSeedListe] = (0, import_react41.useState)("");
14517
+ const [vorlagen, setVorlagen] = (0, import_react41.useState)([DEFAULT_SEED_VORLAGE]);
14518
+ const [vorlageKey, setVorlageKey] = (0, import_react41.useState)(DEFAULT_VORLAGE_KEY);
14519
+ const [vorlagenStand, setVorlagenStand] = (0, import_react41.useState)(0);
14520
+ const [vorlageBenennen, setVorlageBenennen] = (0, import_react41.useState)(false);
14521
+ const [vorlageName, setVorlageName] = (0, import_react41.useState)("");
14522
+ const [modell, setModell] = (0, import_react41.useState)("");
14523
+ const [itemId, setItemId] = (0, import_react41.useState)("");
14524
+ const [laeuft, setLaeuft] = (0, import_react41.useState)(false);
14525
+ const [notiz, setNotiz] = (0, import_react41.useState)(null);
14526
+ const auswahlMarke = (0, import_react41.useRef)(0);
14387
14527
  const zipAktiv = zip !== null;
14388
- (0, import_react40.useEffect)(() => {
14528
+ (0, import_react41.useEffect)(() => {
14389
14529
  if (!zipAktiv) return;
14390
14530
  let abgebrochen = false;
14391
14531
  void (async () => {
@@ -14845,8 +14985,8 @@ var ChatAuftraegeAktion = ({
14845
14985
  laedt,
14846
14986
  onFertig
14847
14987
  }) => {
14848
- const [laufend, setLaufend] = (0, import_react40.useState)(null);
14849
- const [notiz, setNotiz] = (0, import_react40.useState)(null);
14988
+ const [laufend, setLaufend] = (0, import_react41.useState)(null);
14989
+ const [notiz, setNotiz] = (0, import_react41.useState)(null);
14850
14990
  async function uebernehmen(jobId) {
14851
14991
  if (!port.imageJobs) return;
14852
14992
  setLaufend(jobId);
@@ -14925,13 +15065,13 @@ var ChatAuftraegeAktion = ({
14925
15065
  // src/features/szenen/areas/RunsPanel.tsx
14926
15066
  var import_jsx_runtime37 = require("react/jsx-runtime");
14927
15067
  var RunsPanel = ({ port, source, contextId }) => {
14928
- const [anweisung, setAnweisung] = (0, import_react41.useState)("");
14929
- const [laeuft, setLaeuft] = (0, import_react41.useState)(false);
14930
- const [notiz, setNotiz] = (0, import_react41.useState)(null);
14931
- const [nachladen, setNachladen] = (0, import_react41.useState)(0);
15068
+ const [anweisung, setAnweisung] = (0, import_react42.useState)("");
15069
+ const [laeuft, setLaeuft] = (0, import_react42.useState)(false);
15070
+ const [notiz, setNotiz] = (0, import_react42.useState)(null);
15071
+ const [nachladen, setNachladen] = (0, import_react42.useState)(0);
14932
15072
  const faehig = capabilitiesOf(port);
14933
15073
  const quellSchluessel = source.kind === "published" ? source.shotVersionId : source.kind === "draft_chain" ? source.chainId : source.sceneElementId;
14934
- const aktuelleQuelle = (0, import_react41.useRef)({ kind: source.kind, id: quellSchluessel });
15074
+ const aktuelleQuelle = (0, import_react42.useRef)({ kind: source.kind, id: quellSchluessel });
14935
15075
  aktuelleQuelle.current = { kind: source.kind, id: quellSchluessel };
14936
15076
  const [phase, neuLaden] = usePortQuery(
14937
15077
  () => {
@@ -14959,10 +15099,11 @@ var RunsPanel = ({ port, source, contextId }) => {
14959
15099
  neuLaden();
14960
15100
  };
14961
15101
  const laeufe = phase.status === "ready" ? phase.value : [];
14962
- const [gewaehlteRunde, setGewaehlteRunde] = (0, import_react41.useState)(void 0);
15102
+ const titel = useLaufTitel(port, laeufe);
15103
+ const [gewaehlteRunde, setGewaehlteRunde] = (0, import_react42.useState)(void 0);
14963
15104
  const neueste = laeufe.length > 0 ? laeufe[laeufe.length - 1].run_id : null;
14964
15105
  const offeneRunde = gewaehlteRunde === void 0 ? neueste : gewaehlteRunde;
14965
- (0, import_react41.useEffect)(() => {
15106
+ (0, import_react42.useEffect)(() => {
14966
15107
  setGewaehlteRunde(void 0);
14967
15108
  setAnweisung("");
14968
15109
  setNotiz(null);
@@ -14997,6 +15138,7 @@ var RunsPanel = ({ port, source, contextId }) => {
14997
15138
  port,
14998
15139
  lauf,
14999
15140
  runde: roundOfRun(lauf, laeufe),
15141
+ titel: titel.get(lauf.run_id) ?? null,
15000
15142
  offen: lauf.run_id === offeneRunde,
15001
15143
  onUmschalten: () => setGewaehlteRunde(
15002
15144
  lauf.run_id === offeneRunde ? null : lauf.run_id
@@ -15021,7 +15163,7 @@ var STATUS_TEXT = {
15021
15163
  };
15022
15164
  var LANGER_TEXT = 240;
15023
15165
  var Sekundaertext = ({ titel, text, testId }) => {
15024
- const [ganz, setGanz] = (0, import_react41.useState)(false);
15166
+ const [ganz, setGanz] = (0, import_react42.useState)(false);
15025
15167
  const lang = text.length > LANGER_TEXT;
15026
15168
  return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("section", { className: "szenen-runs__sekundaer", children: [
15027
15169
  /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("h5", { children: titel }),
@@ -15040,7 +15182,7 @@ var Sekundaertext = ({ titel, text, testId }) => {
15040
15182
  };
15041
15183
  var LISTE_ANFANG = 3;
15042
15184
  var SekundaerListe = ({ titel, eintraege, testId, className }) => {
15043
- const [alle, setAlle] = (0, import_react41.useState)(false);
15185
+ const [alle, setAlle] = (0, import_react42.useState)(false);
15044
15186
  const lang = eintraege.length > LISTE_ANFANG;
15045
15187
  const gezeigt = lang && !alle ? eintraege.slice(0, LISTE_ANFANG) : eintraege;
15046
15188
  return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("section", { className: "szenen-runs__sekundaer", children: [
@@ -15058,10 +15200,10 @@ var SekundaerListe = ({ titel, eintraege, testId, className }) => {
15058
15200
  )
15059
15201
  ] });
15060
15202
  };
15061
- var RundenEintrag = ({ port, lauf, runde, offen, onUmschalten, itemMoeglich, onItemGespeichert }) => {
15062
- const [notiz, setNotiz] = (0, import_react41.useState)(null);
15063
- const [laeuft, setLaeuft] = (0, import_react41.useState)(false);
15064
- const [nachladen, setNachladen] = (0, import_react41.useState)(0);
15203
+ var RundenEintrag = ({ port, lauf, runde, titel, offen, onUmschalten, itemMoeglich, onItemGespeichert }) => {
15204
+ const [notiz, setNotiz] = (0, import_react42.useState)(null);
15205
+ const [laeuft, setLaeuft] = (0, import_react42.useState)(false);
15206
+ const [nachladen, setNachladen] = (0, import_react42.useState)(0);
15065
15207
  const [itemPhase] = usePortQuery(
15066
15208
  () => offen && port.items ? port.items.getItemForRun(lauf.run_id) : Promise.resolve({ kind: "ok", value: null }),
15067
15209
  [port, lauf.run_id, nachladen, offen]
@@ -15089,10 +15231,7 @@ var RundenEintrag = ({ port, lauf, runde, offen, onUmschalten, itemMoeglich, onI
15089
15231
  "aria-expanded": offen,
15090
15232
  "data-testid": `runde-umschalten-${lauf.run_id}`,
15091
15233
  children: [
15092
- /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { className: "szenen-runs__marke", children: [
15093
- "Runde ",
15094
- runde
15095
- ] }),
15234
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "szenen-runs__marke", "data-testid": `titel-${lauf.run_id}`, children: titel ? `${titel} \xB7 Runde ${runde}` : `Runde ${runde}` }),
15096
15235
  /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
15097
15236
  "span",
15098
15237
  {
@@ -15216,43 +15355,43 @@ var ElementDetail = ({
15216
15355
  author,
15217
15356
  onChanged
15218
15357
  }) => {
15219
- const [nachladen, setNachladen] = (0, import_react42.useState)(0);
15358
+ const [nachladen, setNachladen] = (0, import_react43.useState)(0);
15220
15359
  const [phase] = usePortQuery(
15221
15360
  () => port.elements.getElement(elementId),
15222
15361
  [port, elementId, nachladen]
15223
15362
  );
15224
- const [entwurf, setEntwurf] = (0, import_react42.useState)({});
15225
- const [notiz, setNotiz] = (0, import_react42.useState)(null);
15226
- const [laeuft, setLaeuft] = (0, import_react42.useState)(false);
15227
- const [wahl, setWahl] = (0, import_react42.useState)(null);
15228
- const [basiswechsel, setBasiswechsel] = (0, import_react42.useState)(null);
15363
+ const [entwurf, setEntwurf] = (0, import_react43.useState)({});
15364
+ const [notiz, setNotiz] = (0, import_react43.useState)(null);
15365
+ const [laeuft, setLaeuft] = (0, import_react43.useState)(false);
15366
+ const [wahl, setWahl] = (0, import_react43.useState)(null);
15367
+ const [basiswechsel, setBasiswechsel] = (0, import_react43.useState)(null);
15229
15368
  const [kettenPhase] = usePortQuery(
15230
15369
  () => port.context.chainFor(contextId),
15231
15370
  [port, contextId]
15232
15371
  );
15233
- const kontextkette = (0, import_react42.useMemo)(() => {
15372
+ const kontextkette = (0, import_react43.useMemo)(() => {
15234
15373
  if (kettenPhase.status === "ready") return { status: "ready", chain: kettenPhase.value };
15235
15374
  if (kettenPhase.status === "error") return { status: "error", message: kettenPhase.message };
15236
15375
  return { status: "loading" };
15237
15376
  }, [kettenPhase]);
15238
15377
  const kette = kontextkette.status === "ready" ? kontextkette.chain : null;
15239
15378
  const schnappschuss = phase.status === "ready" ? phase.value : null;
15240
- const kopfErgebnis = (0, import_react42.useMemo)(
15379
+ const kopfErgebnis = (0, import_react43.useMemo)(
15241
15380
  () => schnappschuss && kette ? effectivePreferredHeadSafe(schnappschuss, kette) : null,
15242
15381
  [schnappschuss, kette]
15243
15382
  );
15244
15383
  const kopfAufloesung = resolutionOf(kopfErgebnis);
15245
15384
  const bevorzugteVersion = selectedHeadVersionId(kopfAufloesung);
15246
- const felder = (0, import_react42.useMemo)(
15385
+ const felder = (0, import_react43.useMemo)(
15247
15386
  () => schnappschuss ? fieldsForType(schnappschuss.element.type) : [],
15248
15387
  [schnappschuss?.element.type]
15249
15388
  );
15250
- (0, import_react42.useEffect)(() => {
15389
+ (0, import_react43.useEffect)(() => {
15251
15390
  setWahl(null);
15252
15391
  setBasiswechsel(null);
15253
15392
  setNotiz(null);
15254
15393
  }, [elementId]);
15255
- const automatisch = (0, import_react42.useMemo)(() => {
15394
+ const automatisch = (0, import_react43.useMemo)(() => {
15256
15395
  if (!schnappschuss) return { kind: "keiner" };
15257
15396
  if (schnappschuss.draft) return { kind: "draft" };
15258
15397
  if (bevorzugteVersion) return { kind: "version", versionId: bevorzugteVersion };
@@ -15262,14 +15401,14 @@ var ElementDetail = ({
15262
15401
  if (schnappschuss.versions.length === 0) return { kind: "draft" };
15263
15402
  return { kind: "keiner" };
15264
15403
  }, [schnappschuss, bevorzugteVersion]);
15265
- const angezeigt = (0, import_react42.useMemo)(() => {
15404
+ const angezeigt = (0, import_react43.useMemo)(() => {
15266
15405
  if (!wahl) return automatisch;
15267
15406
  if (wahl.kind === "version" && !schnappschuss?.versions.some((v) => v.version_id === wahl.versionId)) {
15268
15407
  return automatisch;
15269
15408
  }
15270
15409
  return wahl;
15271
15410
  }, [wahl, automatisch, schnappschuss]);
15272
- const angezeigtePayload = (0, import_react42.useMemo)(() => {
15411
+ const angezeigtePayload = (0, import_react43.useMemo)(() => {
15273
15412
  if (!schnappschuss) return {};
15274
15413
  if (angezeigt.kind === "draft") return schnappschuss.draft?.payload ?? {};
15275
15414
  if (angezeigt.kind === "version") {
@@ -15278,7 +15417,7 @@ var ElementDetail = ({
15278
15417
  return {};
15279
15418
  }, [schnappschuss, angezeigt]);
15280
15419
  const anzeigeSchluessel = angezeigt.kind === "version" ? `v:${angezeigt.versionId}` : angezeigt.kind;
15281
- (0, import_react42.useEffect)(() => {
15420
+ (0, import_react43.useEffect)(() => {
15282
15421
  if (!schnappschuss) return;
15283
15422
  setEntwurf(formFromPayload(felder, angezeigtePayload));
15284
15423
  }, [
@@ -15291,7 +15430,7 @@ var ElementDetail = ({
15291
15430
  const bearbeitbar = angezeigt.kind === "draft";
15292
15431
  const setzeFeld = (pfad, wert) => setEntwurf((vorher) => ({ ...vorher, [pfad]: wert }));
15293
15432
  const payloadAusFormular = () => payloadFromForm(felder, entwurf);
15294
- const strukturPayload = (0, import_react42.useMemo)(() => {
15433
+ const strukturPayload = (0, import_react43.useMemo)(() => {
15295
15434
  const lies = (name) => {
15296
15435
  try {
15297
15436
  const roh = JSON.parse(entwurf[name] || "[]");
@@ -15302,19 +15441,19 @@ var ElementDetail = ({
15302
15441
  };
15303
15442
  return { nodes: lies("nodes"), relations: lies("relations") };
15304
15443
  }, [entwurf.nodes, entwurf.relations]);
15305
- const kettenfeld = (0, import_react42.useMemo)(() => {
15444
+ const kettenfeld = (0, import_react43.useMemo)(() => {
15306
15445
  const kette2 = schnappschuss?.draft?.chain;
15307
15446
  if (!kette2) return null;
15308
15447
  return kette2.links[0]?.field ?? null;
15309
15448
  }, [schnappschuss?.draft?.chain]);
15310
- const laufStand = (0, import_react42.useMemo)(() => {
15449
+ const laufStand = (0, import_react43.useMemo)(() => {
15311
15450
  if (!schnappschuss || schnappschuss.element.type !== "scene") return null;
15312
15451
  if (schnappschuss.draft) return schnappschuss.draft.payload;
15313
15452
  const kopf = usableHeadVersionId(kopfAufloesung);
15314
15453
  if (!kopf) return null;
15315
15454
  return schnappschuss.versions.find((v) => v.version_id === kopf)?.payload ?? null;
15316
15455
  }, [schnappschuss, kopfAufloesung]);
15317
- const szenenBlocker = (0, import_react42.useMemo)(() => {
15456
+ const szenenBlocker = (0, import_react43.useMemo)(() => {
15318
15457
  if (!schnappschuss || schnappschuss.element.type !== "scene") return null;
15319
15458
  if (laufStand === null) {
15320
15459
  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.";
@@ -15324,7 +15463,7 @@ var ElementDetail = ({
15324
15463
  const grund = flag.reasons[0];
15325
15464
  return grund && "message" in grund ? grund.message : "Kein Lauf moeglich.";
15326
15465
  }, [schnappschuss, laufStand]);
15327
- const vorschau = (0, import_react42.useMemo)(() => {
15466
+ const vorschau = (0, import_react43.useMemo)(() => {
15328
15467
  if (!schnappschuss) return null;
15329
15468
  try {
15330
15469
  const roh = validatePayload(schnappschuss.element.type, payloadAusFormular());
@@ -15710,15 +15849,15 @@ var ElementDetail = ({
15710
15849
  };
15711
15850
 
15712
15851
  // src/features/szenen/areas/useSchmalesDisplay.ts
15713
- var import_react43 = require("react");
15852
+ var import_react44 = require("react");
15714
15853
  var SCHMALE_ANSICHT = "(max-width: 760px)";
15715
15854
  function messen(abfrage) {
15716
15855
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
15717
15856
  return window.matchMedia(abfrage).matches;
15718
15857
  }
15719
15858
  function useSchmalesDisplay(abfrage = SCHMALE_ANSICHT) {
15720
- const [schmal, setSchmal] = (0, import_react43.useState)(() => messen(abfrage));
15721
- (0, import_react43.useEffect)(() => {
15859
+ const [schmal, setSchmal] = (0, import_react44.useState)(() => messen(abfrage));
15860
+ (0, import_react44.useEffect)(() => {
15722
15861
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
15723
15862
  const liste = window.matchMedia(abfrage);
15724
15863
  const aktualisieren = () => setSchmal(liste.matches);
@@ -15745,16 +15884,16 @@ var ElementsArea = ({
15745
15884
  const gemischt = auswahlTypen.length > 1;
15746
15885
  const ueberschrift = listTitle ?? (einzelTyp ? elementTypPlural(einzelTyp) : "Bausteine");
15747
15886
  const einzahl = einzelTyp ? elementTypLabel(einzelTyp) : "Objekt";
15748
- const [typ, setTyp] = (0, import_react44.useState)(einzelTyp ?? "");
15749
- const [zeigeStillgelegte, setZeigeStillgelegte] = (0, import_react44.useState)(false);
15750
- const [gewaehlt, setGewaehlt] = (0, import_react44.useState)(null);
15751
- const [neuerTitel, setNeuerTitel] = (0, import_react44.useState)("");
15752
- const [neuerTyp, setNeuerTyp] = (0, import_react44.useState)(auswahlTypen[0]);
15753
- const [notiz, setNotiz] = (0, import_react44.useState)(null);
15754
- const [anlegend, setAnlegend] = (0, import_react44.useState)(false);
15755
- const [anlageOffen, setAnlageOffen] = (0, import_react44.useState)(false);
15887
+ const [typ, setTyp] = (0, import_react45.useState)(einzelTyp ?? "");
15888
+ const [zeigeStillgelegte, setZeigeStillgelegte] = (0, import_react45.useState)(false);
15889
+ const [gewaehlt, setGewaehlt] = (0, import_react45.useState)(null);
15890
+ const [neuerTitel, setNeuerTitel] = (0, import_react45.useState)("");
15891
+ const [neuerTyp, setNeuerTyp] = (0, import_react45.useState)(auswahlTypen[0]);
15892
+ const [notiz, setNotiz] = (0, import_react45.useState)(null);
15893
+ const [anlegend, setAnlegend] = (0, import_react45.useState)(false);
15894
+ const [anlageOffen, setAnlageOffen] = (0, import_react45.useState)(false);
15756
15895
  const schmal = useSchmalesDisplay();
15757
- const [ansicht, setAnsicht] = (0, import_react44.useState)("liste");
15896
+ const [ansicht, setAnsicht] = (0, import_react45.useState)("liste");
15758
15897
  const zeigeListe = !schmal || ansicht === "liste";
15759
15898
  const zeigeDetail = !schmal || ansicht === "detail";
15760
15899
  const waehle = (elementId) => {
@@ -15966,183 +16105,7 @@ var ElementsArea = ({
15966
16105
  };
15967
16106
 
15968
16107
  // src/features/szenen/areas/SceneArea.tsx
15969
- var import_jsx_runtime40 = require("react/jsx-runtime");
15970
- var SceneArea = ({ port, contextId }) => /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "szenen-scene", "data-testid": "bereich-szene", children: [
15971
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "szenen-area__lead", children: "Szenen dieses Kontexts. Auswahl \xF6ffnet die Bearbeitung der Nodes und Beziehungen." }),
15972
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ElementsArea, { port, contextId, restrictToTypes: ["scene"] })
15973
- ] });
15974
-
15975
- // src/features/szenen/areas/SetupArea.tsx
15976
- var import_jsx_runtime41 = require("react/jsx-runtime");
15977
- var SetupArea = ({ port, contextId }) => /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "szenen-setup", "data-testid": "bereich-setup", children: [
15978
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "szenen-area__lead", children: "Bildaufbauten, Kameras und Stile dieses Kontexts." }),
15979
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
15980
- ElementsArea,
15981
- {
15982
- port,
15983
- contextId,
15984
- restrictToTypes: ["scene_setup", "camera_preset", "camera_pattern", "style"],
15985
- allTypesLabel: "alle Bildaufbau-Typen",
15986
- listTitle: "Bildaufbau"
15987
- }
15988
- )
15989
- ] });
15990
-
15991
- // src/features/szenen/areas/ShotArea.tsx
15992
- var import_jsx_runtime42 = require("react/jsx-runtime");
15993
- var ShotArea = ({ port, contextId }) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "szenen-shot", "data-testid": "bereich-shot", children: [
15994
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "szenen-area__lead", children: "Prompts dieses Kontexts. Auswahl zeigt Runden und startet Prompt-L\xE4ufe." }),
15995
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
15996
- ElementsArea,
15997
- {
15998
- port,
15999
- contextId,
16000
- restrictToTypes: ["shot", "shot_concept", "target_model"],
16001
- allTypesLabel: "alle Prompt-Typen",
16002
- listTitle: "Prompts"
16003
- }
16004
- )
16005
- ] });
16006
-
16007
- // src/features/szenen/areas/MediaArea.tsx
16008
- var import_react45 = require("react");
16009
- var import_jsx_runtime43 = require("react/jsx-runtime");
16010
- var leer = async () => ({ kind: "ok", value: [] });
16011
- var MediaArea = ({ port }) => {
16012
- const media = port.media;
16013
- const [versionInput, setVersionInput] = (0, import_react45.useState)("");
16014
- const [versionId, setVersionId] = (0, import_react45.useState)("");
16015
- const [itemInput, setItemInput] = (0, import_react45.useState)("");
16016
- const [itemId, setItemId] = (0, import_react45.useState)("");
16017
- const [assetId, setAssetId] = (0, import_react45.useState)("");
16018
- const [role, setRole] = (0, import_react45.useState)("reference");
16019
- const [position, setPosition] = (0, import_react45.useState)("0");
16020
- const [notiz, setNotiz] = (0, import_react45.useState)(null);
16021
- const [busy, setBusy] = (0, import_react45.useState)(false);
16022
- const [refresh, setRefresh] = (0, import_react45.useState)(0);
16023
- const [versionPhase] = usePortQuery(
16024
- () => versionId ? media.listVersionMedia(versionId) : leer(),
16025
- [media, versionId, refresh]
16026
- );
16027
- const [itemPhase] = usePortQuery(
16028
- () => itemId ? media.listItemMedia(itemId) : Promise.resolve({ kind: "ok", value: [] }),
16029
- [media, itemId, refresh]
16030
- );
16031
- const zuordnen = async () => {
16032
- if (!versionInput.trim() || !assetId.trim()) {
16033
- setNotiz({ tone: "error", text: "Version-ID und Asset-ID sind erforderlich." });
16034
- return;
16035
- }
16036
- const parsedPosition = Number(position);
16037
- if (!Number.isInteger(parsedPosition) || parsedPosition < 0) {
16038
- setNotiz({ tone: "error", text: `Position muss eine nicht-negative Ganzzahl sein, war ${JSON.stringify(position)}.` });
16039
- return;
16040
- }
16041
- setBusy(true);
16042
- const result = await media.attachVersionMedium({
16043
- versionId: versionInput.trim(),
16044
- assetId: assetId.trim(),
16045
- role,
16046
- position: parsedPosition
16047
- });
16048
- setNotiz(noticeFor(result, "Referenzmedium zuordnen"));
16049
- if (result.kind === "ok") {
16050
- setVersionId(versionInput.trim());
16051
- setRefresh((value) => value + 1);
16052
- }
16053
- setBusy(false);
16054
- };
16055
- const entfernen = async (medium) => {
16056
- setBusy(true);
16057
- const result = await media.detachVersionMedium({
16058
- versionId,
16059
- assetId: medium.assetId,
16060
- role: medium.role
16061
- });
16062
- setNotiz(noticeFor(result, "Referenzmedium entfernen"));
16063
- if (result.kind === "ok") setRefresh((value) => value + 1);
16064
- setBusy(false);
16065
- };
16066
- const versionMedien = versionPhase.status === "ready" ? versionPhase.value : [];
16067
- const itemMedien = itemPhase.status === "ready" ? itemPhase.value : [];
16068
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "szenen-media", "data-testid": "bereich-medien", children: [
16069
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "szenen-area__lead", children: "Bereits vorhandene Medien-Assets einer Elementversion zuordnen. Hochgeladen wird hier nichts \u2014 Medien entstehen \xFCber den bestehenden Speicherweg aus Stufe 5." }),
16070
- /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("section", { children: [
16071
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("h3", { children: "Version" }),
16072
- /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "szenen-media__lookup", children: [
16073
- /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("label", { children: [
16074
- "Version-ID",
16075
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("input", { value: versionInput, onChange: (event) => setVersionInput(event.target.value) })
16076
- ] }),
16077
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("button", { type: "button", onClick: () => setVersionId(versionInput.trim()), children: "Medien laden" })
16078
- ] }),
16079
- /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "szenen-media__attach", children: [
16080
- /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("label", { children: [
16081
- "Asset-ID",
16082
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("input", { value: assetId, onChange: (event) => setAssetId(event.target.value) })
16083
- ] }),
16084
- /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("label", { children: [
16085
- "Rolle",
16086
- /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("select", { value: role, onChange: (event) => setRole(event.target.value), children: [
16087
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("option", { value: "reference", children: "reference" }),
16088
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("option", { value: "mask", children: "mask" }),
16089
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("option", { value: "palette", children: "palette" }),
16090
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("option", { value: "example", children: "example" })
16091
- ] })
16092
- ] }),
16093
- /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("label", { children: [
16094
- "Position",
16095
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("input", { type: "number", min: "0", value: position, onChange: (event) => setPosition(event.target.value) })
16096
- ] }),
16097
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("button", { type: "button", disabled: busy, onClick: zuordnen, children: "zuordnen" })
16098
- ] }),
16099
- versionPhase.status === "loading" && versionId && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { children: "Versionsmedien werden geladen \u2026" }),
16100
- versionPhase.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("p", { role: "alert", children: [
16101
- "Versionsmedien konnten nicht geladen werden: ",
16102
- versionPhase.message
16103
- ] }),
16104
- versionPhase.status === "ready" && versionId && versionMedien.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("p", { children: [
16105
- "Version ",
16106
- versionId,
16107
- " hat keine Medien."
16108
- ] }),
16109
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { className: "szenen-media__list", children: versionMedien.map((medium) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("li", { children: [
16110
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("code", { children: medium.assetId }),
16111
- /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { children: [
16112
- medium.role,
16113
- " \xB7 Position ",
16114
- medium.position
16115
- ] }),
16116
- medium.url && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("a", { href: medium.url, target: "_blank", rel: "noreferrer", children: "\xF6ffnen" }),
16117
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("button", { type: "button", disabled: busy, onClick: () => entfernen(medium), children: "entfernen" })
16118
- ] }, `${medium.assetId}:${medium.role}`)) })
16119
- ] }),
16120
- /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("section", { children: [
16121
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("h3", { children: "Item-Medien" }),
16122
- /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "szenen-media__lookup", children: [
16123
- /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("label", { children: [
16124
- "Item-ID",
16125
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("input", { value: itemInput, onChange: (event) => setItemInput(event.target.value) })
16126
- ] }),
16127
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("button", { type: "button", onClick: () => setItemId(itemInput.trim()), children: "Item-Medien laden" })
16128
- ] }),
16129
- itemPhase.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("p", { role: "alert", children: [
16130
- "Item-Medien konnten nicht geladen werden: ",
16131
- itemPhase.message
16132
- ] }),
16133
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { className: "szenen-media__list", children: itemMedien.map((medium) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("li", { children: [
16134
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("code", { children: medium.assetId }),
16135
- /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { children: [
16136
- medium.role,
16137
- " \xB7 Position ",
16138
- medium.position
16139
- ] }),
16140
- medium.url && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("a", { href: medium.url, target: "_blank", rel: "noreferrer", children: "\xF6ffnen" })
16141
- ] }, `${medium.assetId}:${medium.role}:${medium.position}`)) })
16142
- ] }),
16143
- notiz && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { role: notiz.tone === "error" ? "alert" : "status", "data-tone": notiz.tone, children: notiz.text })
16144
- ] });
16145
- };
16108
+ var import_react49 = require("react");
16146
16109
 
16147
16110
  // src/features/szenen/areas/DeriveArea.tsx
16148
16111
  var import_react48 = require("react");
@@ -17009,52 +16972,52 @@ async function runSceneDerivation(input, port, userPrompt) {
17009
16972
 
17010
16973
  // src/features/szenen/areas/RecoveryPanel.tsx
17011
16974
  var import_react46 = require("react");
17012
- var import_jsx_runtime44 = require("react/jsx-runtime");
16975
+ var import_jsx_runtime40 = require("react/jsx-runtime");
17013
16976
  var Diagnosen = ({ befunde, testId }) => {
17014
16977
  if (befunde.length === 0) return null;
17015
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("ul", { className: "szenen-diagnose__liste", "data-testid": testId, children: befunde.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("li", { "data-severity": d.severity, children: [
17016
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("code", { children: d.code }),
17017
- d.proposal_type && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
16978
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("ul", { className: "szenen-diagnose__liste", "data-testid": testId, children: befunde.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("li", { "data-severity": d.severity, children: [
16979
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("code", { children: d.code }),
16980
+ d.proposal_type && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
17018
16981
  " \xB7 ",
17019
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("strong", { children: d.proposal_type })
16982
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("strong", { children: d.proposal_type })
17020
16983
  ] }),
17021
- d.pointer && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
16984
+ d.pointer && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
17022
16985
  " \xB7 ",
17023
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("code", { children: d.pointer })
16986
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("code", { children: d.pointer })
17024
16987
  ] }),
17025
- typeof d.line === "number" && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
16988
+ typeof d.line === "number" && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
17026
16989
  " \xB7 Zeile ",
17027
16990
  d.line,
17028
16991
  ", Spalte ",
17029
16992
  d.column
17030
16993
  ] }),
17031
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { children: d.message })
16994
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { children: d.message })
17032
16995
  ] }, i)) });
17033
16996
  };
17034
- var Ausbeute = ({ bewertung }) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "szenen-diagnose", "data-testid": "rettung-ergebnis", children: [
17035
- /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("p", { "data-testid": "rettung-status", children: [
16997
+ var Ausbeute = ({ bewertung }) => /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "szenen-diagnose", "data-testid": "rettung-ergebnis", children: [
16998
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("p", { "data-testid": "rettung-status", children: [
17036
16999
  bewertung.status === "usable" && "Diese Fassung ist vollst\xE4ndig verwertbar.",
17037
17000
  bewertung.status === "partial" && "Diese Fassung ist teilweise verwertbar \u2014 nicht alles kommt durch.",
17038
17001
  bewertung.status === "invalid" && "Aus dieser Fassung ist nichts zu \xFCbernehmen."
17039
17002
  ] }),
17040
- bewertung.proposals.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
17041
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("h5", { children: "Verwertbar" }),
17042
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("ul", { "data-testid": "rettung-verwertbar", children: bewertung.proposals.map((p) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("li", { "data-testid": `rettung-verwertbar-${p.type}`, children: [
17043
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("strong", { children: p.type }),
17003
+ bewertung.proposals.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
17004
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("h5", { children: "Verwertbar" }),
17005
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("ul", { "data-testid": "rettung-verwertbar", children: bewertung.proposals.map((p) => /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("li", { "data-testid": `rettung-verwertbar-${p.type}`, children: [
17006
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("strong", { children: p.type }),
17044
17007
  " \u2014 ",
17045
17008
  p.status === "complete" ? "vollst\xE4ndig" : "teilweise; vor dem Ver\xF6ffentlichen zu erg\xE4nzen",
17046
- p.status === "partial" && p.gaps && p.gaps.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("ul", { children: p.gaps.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("li", { children: [
17047
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("code", { children: l.pointer }),
17009
+ p.status === "partial" && p.gaps && p.gaps.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("ul", { children: p.gaps.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("li", { children: [
17010
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("code", { children: l.pointer }),
17048
17011
  ": ",
17049
17012
  l.reason
17050
17013
  ] }, i)) })
17051
17014
  ] }, p.type)) })
17052
17015
  ] }),
17053
- bewertung.rejected.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
17054
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("h5", { children: "Nicht verwertbar" }),
17055
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("ul", { "data-testid": "rettung-abgelehnt", children: bewertung.rejected.map((r) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("li", { "data-testid": `rettung-abgelehnt-${r.type}`, children: [
17056
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("strong", { children: r.type }),
17057
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Diagnosen, { befunde: r.diagnostics, testId: `rettung-grund-${r.type}` })
17016
+ bewertung.rejected.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
17017
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("h5", { children: "Nicht verwertbar" }),
17018
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("ul", { "data-testid": "rettung-abgelehnt", children: bewertung.rejected.map((r) => /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("li", { "data-testid": `rettung-abgelehnt-${r.type}`, children: [
17019
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("strong", { children: r.type }),
17020
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Diagnosen, { befunde: r.diagnostics, testId: `rettung-grund-${r.type}` })
17058
17021
  ] }, r.type)) })
17059
17022
  ] })
17060
17023
  ] });
@@ -17099,16 +17062,16 @@ var RecoveryPanel = ({ port, lauf }) => {
17099
17062
  setLaeuft(false);
17100
17063
  }
17101
17064
  }
17102
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("section", { className: "szenen-rettung", "data-testid": "rettung", children: [
17103
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("h4", { children: "Antwort retten" }),
17104
- /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("p", { className: "szenen-area__lead", children: [
17065
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("section", { className: "szenen-rettung", "data-testid": "rettung", children: [
17066
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("h4", { children: "Antwort retten" }),
17067
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("p", { className: "szenen-area__lead", children: [
17105
17068
  "Der Lauf ist bezahlt und seine Antwort liegt vor. Sie l\xE4sst sich hier pr\xFCfen und korrigieren \u2014 ",
17106
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("strong", { children: "ohne" }),
17069
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("strong", { children: "ohne" }),
17107
17070
  " einen weiteren Modellaufruf. Das Original bleibt unver\xE4ndert."
17108
17071
  ] }),
17109
- /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("label", { className: "szenen-field", children: [
17110
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { children: "Original der Modellantwort (unver\xE4nderlich)" }),
17111
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
17072
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("label", { className: "szenen-field", children: [
17073
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { children: "Original der Modellantwort (unver\xE4nderlich)" }),
17074
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
17112
17075
  "textarea",
17113
17076
  {
17114
17077
  readOnly: true,
@@ -17119,12 +17082,12 @@ var RecoveryPanel = ({ port, lauf }) => {
17119
17082
  }
17120
17083
  )
17121
17084
  ] }),
17122
- /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("label", { className: "szenen-field", children: [
17123
- /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { children: [
17085
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("label", { className: "szenen-field", children: [
17086
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { children: [
17124
17087
  "Arbeitskopie",
17125
17088
  bearbeitet ? " (bearbeitet)" : ""
17126
17089
  ] }),
17127
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
17090
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
17128
17091
  "textarea",
17129
17092
  {
17130
17093
  rows: 8,
@@ -17134,8 +17097,8 @@ var RecoveryPanel = ({ port, lauf }) => {
17134
17097
  }
17135
17098
  )
17136
17099
  ] }),
17137
- /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "szenen-rettung__aktionen", children: [
17138
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
17100
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "szenen-rettung__aktionen", children: [
17101
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
17139
17102
  "button",
17140
17103
  {
17141
17104
  type: "button",
@@ -17145,7 +17108,7 @@ var RecoveryPanel = ({ port, lauf }) => {
17145
17108
  children: "Arbeitskopie pr\xFCfen (kein Modellaufruf)"
17146
17109
  }
17147
17110
  ),
17148
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
17111
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
17149
17112
  "button",
17150
17113
  {
17151
17114
  type: "button",
@@ -17155,7 +17118,7 @@ var RecoveryPanel = ({ port, lauf }) => {
17155
17118
  children: "Arbeitsfassung speichern"
17156
17119
  }
17157
17120
  ),
17158
- arbeitskopie !== null && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
17121
+ arbeitskopie !== null && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
17159
17122
  "button",
17160
17123
  {
17161
17124
  type: "button",
@@ -17170,15 +17133,15 @@ var RecoveryPanel = ({ port, lauf }) => {
17170
17133
  }
17171
17134
  )
17172
17135
  ] }),
17173
- meldung && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { className: "szenen-error", role: "alert", "data-testid": "rettung-meldung", children: meldung }),
17174
- bewertung && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Ausbeute, { bewertung }),
17175
- bewertung && bewertung.diagnostics.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "szenen-diagnose", children: [
17176
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("h5", { children: "Befunde an dieser Fassung" }),
17177
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Diagnosen, { befunde: bewertung.diagnostics, testId: "rettung-befunde" })
17136
+ meldung && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "szenen-error", role: "alert", "data-testid": "rettung-meldung", children: meldung }),
17137
+ bewertung && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Ausbeute, { bewertung }),
17138
+ bewertung && bewertung.diagnostics.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "szenen-diagnose", children: [
17139
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("h5", { children: "Befunde an dieser Fassung" }),
17140
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Diagnosen, { befunde: bewertung.diagnostics, testId: "rettung-befunde" })
17178
17141
  ] }),
17179
- gesichert && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("p", { "data-testid": "rettung-gesichert", children: [
17142
+ gesichert && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("p", { "data-testid": "rettung-gesichert", children: [
17180
17143
  "Gespeichert als Arbeitsfassung ",
17181
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("code", { children: gesichert.recovery_id }),
17144
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("code", { children: gesichert.recovery_id }),
17182
17145
  gesichert.edited ? " (bearbeitet)" : " (unver\xE4ndert)",
17183
17146
  ". Das Original des Laufs ist davon unber\xFChrt."
17184
17147
  ] })
@@ -17187,7 +17150,7 @@ var RecoveryPanel = ({ port, lauf }) => {
17187
17150
 
17188
17151
  // src/features/szenen/areas/ChainActions.tsx
17189
17152
  var import_react47 = require("react");
17190
- var import_jsx_runtime45 = require("react/jsx-runtime");
17153
+ var import_jsx_runtime41 = require("react/jsx-runtime");
17191
17154
  var ChainActions = ({ port, runId, recoveryId }) => {
17192
17155
  const [graph, setGraph] = (0, import_react47.useState)(null);
17193
17156
  const [meldung, setMeldung] = (0, import_react47.useState)(null);
@@ -17213,14 +17176,14 @@ var ChainActions = ({ port, runId, recoveryId }) => {
17213
17176
  setLaeuft(false);
17214
17177
  }
17215
17178
  }
17216
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("section", { className: "szenen-kette", "data-testid": "kette", children: [
17217
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h4", { children: "Draft-Kette" }),
17218
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { className: "szenen-area__lead", children: [
17179
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("section", { className: "szenen-kette", "data-testid": "kette", children: [
17180
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("h4", { children: "Draft-Kette" }),
17181
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("p", { className: "szenen-area__lead", children: [
17219
17182
  "Szene, Aufbau und Shot entstehen als zusammenh\xE4ngende Entw\xFCrfe. Daraus l\xE4sst sich sofort ein Prompt erzeugen \u2014 ",
17220
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("strong", { children: "ohne" }),
17183
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("strong", { children: "ohne" }),
17221
17184
  " vorher etwas zu ver\xF6ffentlichen."
17222
17185
  ] }),
17223
- !graph && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
17186
+ !graph && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
17224
17187
  "button",
17225
17188
  {
17226
17189
  type: "button",
@@ -17230,34 +17193,34 @@ var ChainActions = ({ port, runId, recoveryId }) => {
17230
17193
  children: "Als Draft-Kette \xFCbernehmen"
17231
17194
  }
17232
17195
  ),
17233
- graph && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { "data-testid": "kette-ergebnis", children: [
17234
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { children: [
17196
+ graph && /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { "data-testid": "kette-ergebnis", children: [
17197
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("p", { children: [
17235
17198
  "Kette ",
17236
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("code", { children: graph.chainId }),
17199
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("code", { children: graph.chainId }),
17237
17200
  " angelegt. Kein Knoten ist ver\xF6ffentlicht."
17238
17201
  ] }),
17239
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("ul", { "data-testid": "kette-knoten", children: graph.created.map((k) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("li", { "data-testid": `kette-knoten-${k.type}`, children: [
17240
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("strong", { children: k.type }),
17202
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("ul", { "data-testid": "kette-knoten", children: graph.created.map((k) => /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("li", { "data-testid": `kette-knoten-${k.type}`, children: [
17203
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("strong", { children: k.type }),
17241
17204
  " \u2014 Element ",
17242
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("code", { children: k.elementId }),
17205
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("code", { children: k.elementId }),
17243
17206
  ", Entwurf ",
17244
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("code", { children: k.draftId }),
17207
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("code", { children: k.draftId }),
17245
17208
  " (Revision ",
17246
17209
  k.revision,
17247
17210
  ")"
17248
17211
  ] }, k.type)) }),
17249
- graph.missing.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "szenen-diagnose", "data-testid": "kette-fehlend", children: [
17250
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h5", { children: "Unvollst\xE4ndig erzeugt" }),
17251
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("ul", { children: graph.missing.map((m) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("li", { children: [
17252
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("strong", { children: m.type }),
17212
+ graph.missing.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "szenen-diagnose", "data-testid": "kette-fehlend", children: [
17213
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("h5", { children: "Unvollst\xE4ndig erzeugt" }),
17214
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("ul", { children: graph.missing.map((m) => /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("li", { children: [
17215
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("strong", { children: m.type }),
17253
17216
  ": ",
17254
17217
  m.reason
17255
17218
  ] }, m.type)) })
17256
17219
  ] })
17257
17220
  ] }),
17258
- shotKnoten && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_jsx_runtime45.Fragment, { children: [
17259
- /* @__PURE__ */ (0, import_jsx_runtime45.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".' }),
17260
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
17221
+ shotKnoten && /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
17222
+ /* @__PURE__ */ (0, import_jsx_runtime41.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".' }),
17223
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
17261
17224
  PromptStartAction,
17262
17225
  {
17263
17226
  port,
@@ -17265,12 +17228,12 @@ var ChainActions = ({ port, runId, recoveryId }) => {
17265
17228
  }
17266
17229
  )
17267
17230
  ] }),
17268
- meldung && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "szenen-error", role: "alert", "data-testid": "kette-meldung", children: meldung })
17231
+ meldung && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "szenen-error", role: "alert", "data-testid": "kette-meldung", children: meldung })
17269
17232
  ] });
17270
17233
  };
17271
17234
 
17272
17235
  // src/features/szenen/areas/DeriveArea.tsx
17273
- var import_jsx_runtime46 = require("react/jsx-runtime");
17236
+ var import_jsx_runtime42 = require("react/jsx-runtime");
17274
17237
  var MODUS_TEXT = {
17275
17238
  prompt: "Nur Prompt",
17276
17239
  image: "Nur Bild",
@@ -17288,15 +17251,15 @@ var BelegFeld = ({ titel, text, testId, offen }) => {
17288
17251
  setKopiert(false);
17289
17252
  }
17290
17253
  }
17291
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("details", { open: offen, "data-testid": `belege-${testId}`, children: [
17292
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("summary", { children: [
17254
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("details", { open: offen, "data-testid": `belege-${testId}`, children: [
17255
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("summary", { children: [
17293
17256
  titel,
17294
17257
  " (",
17295
17258
  text.length,
17296
17259
  " Zeichen)"
17297
17260
  ] }),
17298
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "szenen-beleg__kopf", children: [
17299
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17261
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "szenen-beleg__kopf", children: [
17262
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
17300
17263
  "button",
17301
17264
  {
17302
17265
  type: "button",
@@ -17305,9 +17268,9 @@ var BelegFeld = ({ titel, text, testId, offen }) => {
17305
17268
  children: "Kopieren"
17306
17269
  }
17307
17270
  ),
17308
- kopiert && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { "data-testid": `kopiert-${testId}`, children: "Kopiert." })
17271
+ kopiert && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { "data-testid": `kopiert-${testId}`, children: "Kopiert." })
17309
17272
  ] }),
17310
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17273
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
17311
17274
  "textarea",
17312
17275
  {
17313
17276
  ref: feld,
@@ -17321,8 +17284,8 @@ var BelegFeld = ({ titel, text, testId, offen }) => {
17321
17284
  )
17322
17285
  ] });
17323
17286
  };
17324
- var Belege = ({ lauf }) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "szenen-diagnose", "data-testid": "belege", children: [
17325
- lauf.raw_output ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17287
+ var Belege = ({ lauf }) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "szenen-diagnose", "data-testid": "belege", children: [
17288
+ lauf.raw_output ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
17326
17289
  BelegFeld,
17327
17290
  {
17328
17291
  titel: "Rohantwort des Modells",
@@ -17333,9 +17296,9 @@ var Belege = ({ lauf }) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div",
17333
17296
  ) : (
17334
17297
  /* Kein Text ist auch eine Auskunft: Der Provider hat nichts geliefert,
17335
17298
  das Modell hat also nicht „falsch geantwortet". */
17336
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { "data-testid": "roh-antwort-fehlt", children: "Es gibt keine Rohantwort \u2014 der Lauf ist gescheitert, bevor ein Text ankam (etwa Netz, Sperre oder Providerfehler)." })
17299
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { "data-testid": "roh-antwort-fehlt", children: "Es gibt keine Rohantwort \u2014 der Lauf ist gescheitert, bevor ein Text ankam (etwa Netz, Sperre oder Providerfehler)." })
17337
17300
  ),
17338
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17301
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
17339
17302
  BelegFeld,
17340
17303
  {
17341
17304
  titel: "Systemprompt dieses Laufs",
@@ -17347,18 +17310,18 @@ var Belege = ({ lauf }) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div",
17347
17310
  ] });
17348
17311
  var Herkunft = ({ lauf }) => {
17349
17312
  if (!lauf.follows) return null;
17350
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("p", { className: "szenen-run__herkunft", "data-testid": "folgt-auf", children: [
17313
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("p", { className: "szenen-run__herkunft", "data-testid": "folgt-auf", children: [
17351
17314
  "Folgt auf Lauf ",
17352
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("code", { children: lauf.follows.run_id }),
17315
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("code", { children: lauf.follows.run_id }),
17353
17316
  ".",
17354
17317
  lauf.status === "succeeded" && " Im Folgelauf gelungen."
17355
17318
  ] });
17356
17319
  };
17357
17320
  var Widerspruch = ({ lauf }) => {
17358
17321
  if (!lauf.reply) return null;
17359
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "szenen-diagnose szenen-diagnose--antwort", "data-testid": "modell-antwort", children: [
17360
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("h4", { children: "Das Modell antwortet auf die Beanstandung" }),
17361
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { children: lauf.reply })
17322
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "szenen-diagnose szenen-diagnose--antwort", "data-testid": "modell-antwort", children: [
17323
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("h4", { children: "Das Modell antwortet auf die Beanstandung" }),
17324
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { children: lauf.reply })
17362
17325
  ] });
17363
17326
  };
17364
17327
  function readAsBase642(datei) {
@@ -17465,10 +17428,10 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
17465
17428
  if (item.images.length === 1) onWaehlen(deckbild);
17466
17429
  else setFokus(item.itemRef === fokus ? null : item.itemRef);
17467
17430
  }
17468
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "szenen-bildwahl", "data-testid": "bildwahl", children: [
17469
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("label", { className: "szenen-field", children: [
17470
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { children: "Bild aus dem Bestand" }),
17471
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17431
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "szenen-bildwahl", "data-testid": "bildwahl", children: [
17432
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("label", { className: "szenen-field", children: [
17433
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { children: "Bild aus dem Bestand" }),
17434
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
17472
17435
  "input",
17473
17436
  {
17474
17437
  type: "search",
@@ -17482,23 +17445,23 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
17482
17445
  }
17483
17446
  )
17484
17447
  ] }),
17485
- items.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { children: "Bilder werden geladen \u2026" }),
17486
- items.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("p", { className: "szenen-error", role: "alert", children: [
17448
+ items.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { children: "Bilder werden geladen \u2026" }),
17449
+ items.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("p", { className: "szenen-error", role: "alert", children: [
17487
17450
  "Der Bildbestand ist nicht lesbar: ",
17488
17451
  items.message
17489
17452
  ] }),
17490
- items.status === "ready" && werke.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "szenen-empty", "data-testid": "bildwahl-leer", children: "Zu dieser Suche gibt es kein Bild. Ohne Treffer bleibt der Upload." }),
17491
- werke.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17453
+ items.status === "ready" && werke.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "szenen-empty", "data-testid": "bildwahl-leer", children: "Zu dieser Suche gibt es kein Bild. Ohne Treffer bleibt der Upload." }),
17454
+ werke.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
17492
17455
  "div",
17493
17456
  {
17494
17457
  className: "szenen-bildwahl__browser",
17495
17458
  ref: browser,
17496
17459
  "data-testid": "bildwahl-werke",
17497
- children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("ul", { className: "szenen-bildwahl__raster", children: werke.map((item) => {
17460
+ children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("ul", { className: "szenen-bildwahl__raster", children: werke.map((item) => {
17498
17461
  const deckbild = item.images[0];
17499
17462
  if (!deckbild) return null;
17500
17463
  const fokussiert = item.itemRef === fokus;
17501
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("li", { "data-media-ref": deckbild.mediaRef, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
17464
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("li", { "data-media-ref": deckbild.mediaRef, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
17502
17465
  "button",
17503
17466
  {
17504
17467
  type: "button",
@@ -17508,7 +17471,7 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
17508
17471
  "data-testid": `item-waehlen-${item.itemRef}`,
17509
17472
  title: item.title,
17510
17473
  children: [
17511
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17474
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
17512
17475
  Kachel,
17513
17476
  {
17514
17477
  quelle: quelleVon(deckbild),
@@ -17516,7 +17479,7 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
17516
17479
  ...gescheitert.has(deckbild.mediaRef) ? { leerText: "Vorschau fehlt" } : {}
17517
17480
  }
17518
17481
  ),
17519
- item.images.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
17482
+ item.images.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
17520
17483
  "span",
17521
17484
  {
17522
17485
  className: "szenen-bildwahl__anzahl",
@@ -17527,22 +17490,22 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
17527
17490
  ]
17528
17491
  }
17529
17492
  ),
17530
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "szenen-bildwahl__kacheltitel", children: item.title })
17493
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "szenen-bildwahl__kacheltitel", children: item.title })
17531
17494
  ]
17532
17495
  }
17533
17496
  ) }, item.itemRef);
17534
17497
  }) })
17535
17498
  }
17536
17499
  ),
17537
- werk && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("section", { className: "szenen-bildwahl__inspector", "data-testid": "bildwahl-galerie", children: [
17538
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("header", { className: "szenen-bildwahl__inspektorkopf", children: [
17539
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("span", { className: "szenen-bildwahl__werktitel", "data-testid": "bildwahl-werktitel", children: [
17500
+ werk && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("section", { className: "szenen-bildwahl__inspector", "data-testid": "bildwahl-galerie", children: [
17501
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("header", { className: "szenen-bildwahl__inspektorkopf", children: [
17502
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "szenen-bildwahl__werktitel", "data-testid": "bildwahl-werktitel", children: [
17540
17503
  werk.title,
17541
17504
  " \xB7 ",
17542
17505
  werk.images.length,
17543
17506
  " Bilder"
17544
17507
  ] }),
17545
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17508
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
17546
17509
  "button",
17547
17510
  {
17548
17511
  type: "button",
@@ -17553,7 +17516,7 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
17553
17516
  }
17554
17517
  )
17555
17518
  ] }),
17556
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("ul", { className: "szenen-bildwahl__streifen", children: werk.images.map((bild, position) => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
17519
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("ul", { className: "szenen-bildwahl__streifen", children: werk.images.map((bild, position) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
17557
17520
  "button",
17558
17521
  {
17559
17522
  type: "button",
@@ -17563,7 +17526,7 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
17563
17526
  "data-testid": `bild-waehlen-${bild.mediaRef}`,
17564
17527
  title: bild.title,
17565
17528
  children: [
17566
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17529
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
17567
17530
  Kachel,
17568
17531
  {
17569
17532
  quelle: quelleVon(bild),
@@ -17571,7 +17534,7 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
17571
17534
  ...gescheitert.has(bild.mediaRef) ? { leerText: "Vorschau fehlt" } : {}
17572
17535
  }
17573
17536
  ),
17574
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("span", { className: "szenen-bildwahl__nummer", children: [
17537
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "szenen-bildwahl__nummer", children: [
17575
17538
  "#",
17576
17539
  position + 1
17577
17540
  ] })
@@ -17579,10 +17542,10 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
17579
17542
  }
17580
17543
  ) }, bild.mediaRef)) })
17581
17544
  ] }),
17582
- gewaehlt.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("ul", { className: "szenen-bildwahl__gewaehlt", "data-testid": "bilder-gewaehlt", children: gewaehlt.map((bild) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("li", { children: [
17583
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Kachel, { quelle: quelleVon(bild), alt: bild.title }),
17584
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "szenen-bildwahl__kacheltitel", children: bild.title }),
17585
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17545
+ gewaehlt.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("ul", { className: "szenen-bildwahl__gewaehlt", "data-testid": "bilder-gewaehlt", children: gewaehlt.map((bild) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("li", { children: [
17546
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Kachel, { quelle: quelleVon(bild), alt: bild.title }),
17547
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "szenen-bildwahl__kacheltitel", children: bild.title }),
17548
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
17586
17549
  "button",
17587
17550
  {
17588
17551
  type: "button",
@@ -17618,6 +17581,15 @@ var DeriveArea = ({ port, contextId }) => {
17618
17581
  () => port.derivation.listPolicies(contextId),
17619
17582
  [port, contextId]
17620
17583
  );
17584
+ const sortiert = (0, import_react48.useMemo)(() => regeln.status === "ready" ? [...regeln.value].sort((a, b) => {
17585
+ const szeneZuerst = Number(b.produces.includes("scene")) - Number(a.produces.includes("scene"));
17586
+ return szeneZuerst !== 0 ? szeneZuerst : a.title.localeCompare(b.title, "de");
17587
+ }) : [], [regeln]);
17588
+ (0, import_react48.useEffect)(() => {
17589
+ if (policyVersionId) return;
17590
+ const fuerSzene = sortiert.find((r) => r.produces.includes("scene"));
17591
+ if (fuerSzene) setPolicyVersionId(fuerSzene.versionId);
17592
+ }, [sortiert, policyVersionId]);
17621
17593
  const gewaehlt = regeln.status === "ready" ? regeln.value.find((r) => r.versionId === policyVersionId) ?? null : null;
17622
17594
  async function starten(folgt) {
17623
17595
  if (!gewaehlt) {
@@ -17692,12 +17664,12 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
17692
17664
  const text = beanstandung ?? vorgabeBeanstandung(satz);
17693
17665
  const bilderFehlen = satz.input.images.some((bild) => bild.kind !== "library");
17694
17666
  const bereit = Boolean(text.trim()) && !bilderFehlen && !laeuft;
17695
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "szenen-folgelauf", "data-testid": "folgelauf", children: [
17696
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("h4", { children: "Korrektur- oder Erg\xE4nzungslauf" }),
17697
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "szenen-area__lead", children: satz.status === "failed" ? "Das Modell bekommt seine Antwort und die Beanstandung noch einmal vorgelegt." : "Das Ergebnis steht. Wenn etwas offen blieb, kann das Modell nachbessern." }),
17698
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("label", { className: "szenen-field", children: [
17699
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { children: "Beanstandung" }),
17700
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17667
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "szenen-folgelauf", "data-testid": "folgelauf", children: [
17668
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("h4", { children: "Korrektur- oder Erg\xE4nzungslauf" }),
17669
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "szenen-area__lead", children: satz.status === "failed" ? "Das Modell bekommt seine Antwort und die Beanstandung noch einmal vorgelegt." : "Das Ergebnis steht. Wenn etwas offen blieb, kann das Modell nachbessern." }),
17670
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("label", { className: "szenen-field", children: [
17671
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { children: "Beanstandung" }),
17672
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
17701
17673
  "textarea",
17702
17674
  {
17703
17675
  rows: 4,
@@ -17707,9 +17679,9 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
17707
17679
  }
17708
17680
  )
17709
17681
  ] }),
17710
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("label", { className: "szenen-field", children: [
17711
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { children: "Zus\xE4tzliche Anweisung (optional)" }),
17712
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17682
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("label", { className: "szenen-field", children: [
17683
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { children: "Zus\xE4tzliche Anweisung (optional)" }),
17684
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
17713
17685
  "textarea",
17714
17686
  {
17715
17687
  rows: 2,
@@ -17720,8 +17692,8 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
17720
17692
  }
17721
17693
  )
17722
17694
  ] }),
17723
- bilderFehlen && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "szenen-error", "data-testid": "folgelauf-ohne-bild", children: "Die Bilder dieses Laufs wurden nicht im Bestand abgelegt \u2014 er ist \xE4lter als diese M\xF6glichkeit. Ein Folgelauf ohne sie k\xF6nnte ein fehlendes Pflichtfeld nur erfinden. Starten Sie einen neuen Lauf mit dem Referenzbild; dessen Bilder bleiben dann erhalten." }),
17724
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17695
+ bilderFehlen && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "szenen-error", "data-testid": "folgelauf-ohne-bild", children: "Die Bilder dieses Laufs wurden nicht im Bestand abgelegt \u2014 er ist \xE4lter als diese M\xF6glichkeit. Ein Folgelauf ohne sie k\xF6nnte ein fehlendes Pflichtfeld nur erfinden. Starten Sie einen neuen Lauf mit dem Referenzbild; dessen Bilder bleiben dann erhalten." }),
17696
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
17725
17697
  "button",
17726
17698
  {
17727
17699
  type: "button",
@@ -17761,19 +17733,19 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
17761
17733
  setMeldung(ergebnis.message);
17762
17734
  }
17763
17735
  }
17764
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "szenen-derive", "data-testid": "bereich-rekonstruktion", children: [
17765
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "szenen-area__lead", children: "Aus einem Referenzbild, einem Prompt oder beidem einen Szenenentwurf vorschlagen lassen. Der Vorschlag wird nie ver\xF6ffentlicht \u2014 er wird ein Entwurf, den Sie pr\xFCfen und \xE4ndern." }),
17766
- regeln.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { children: "Regeln werden geladen \u2026" }),
17767
- regeln.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("p", { className: "szenen-error", role: "alert", children: [
17736
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "szenen-derive", "data-testid": "bereich-rekonstruktion", children: [
17737
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "szenen-area__lead", children: "Aus einem Referenzbild, einem Prompt oder beidem einen Szenenentwurf vorschlagen lassen. Der Vorschlag wird nie ver\xF6ffentlicht \u2014 er wird ein Entwurf, den Sie pr\xFCfen und \xE4ndern." }),
17738
+ regeln.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { children: "Regeln werden geladen \u2026" }),
17739
+ regeln.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("p", { className: "szenen-error", role: "alert", children: [
17768
17740
  "Die Regeln konnten nicht gelesen werden: ",
17769
17741
  regeln.message
17770
17742
  ] }),
17771
- regeln.status === "ready" && regeln.value.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("p", { className: "szenen-empty", children: [
17743
+ regeln.status === "ready" && regeln.value.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("p", { className: "szenen-empty", children: [
17772
17744
  "In diesem Kontext ist keine Rekonstruktionsregel (",
17773
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("code", { children: "scene_derivation_policy" }),
17774
- ') ver\xF6ffentlicht. Ohne sie ist kein Lauf reproduzierbar \u2014 legen Sie hier eine unter \u201E1 \xB7 Bausteine" an, oder wechseln Sie in den Kontext, in dem Ihre Regel liegt. Regeln aus anderen Kontexten stehen hier bewusst nicht zur Auswahl.'
17745
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("code", { children: "scene_derivation_policy" }),
17746
+ ') ver\xF6ffentlicht. Ohne sie ist kein Lauf reproduzierbar \u2014 legen Sie eine unter \u201EErweitert" an, oder wechseln Sie in den Kontext, in dem Ihre Regel liegt. Regeln aus anderen Kontexten stehen hier bewusst nicht zur Auswahl.'
17775
17747
  ] }),
17776
- regeln.status === "ready" && regeln.value.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
17748
+ regeln.status === "ready" && regeln.value.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
17777
17749
  "form",
17778
17750
  {
17779
17751
  className: "szenen-form",
@@ -17782,17 +17754,17 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
17782
17754
  void starten();
17783
17755
  },
17784
17756
  children: [
17785
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("label", { className: "szenen-field", children: [
17786
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { children: "Regel" }),
17787
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
17757
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("label", { className: "szenen-field", children: [
17758
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { children: "Regel" }),
17759
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
17788
17760
  "select",
17789
17761
  {
17790
17762
  value: policyVersionId,
17791
17763
  onChange: (e) => setPolicyVersionId(e.target.value),
17792
17764
  "data-testid": "regel-auswahl",
17793
17765
  children: [
17794
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("option", { value: "", children: "\u2014 bitte w\xE4hlen \u2014" }),
17795
- regeln.value.map((regel) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("option", { value: regel.versionId, children: [
17766
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: "", children: "\u2014 bitte w\xE4hlen \u2014" }),
17767
+ sortiert.map((regel) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("option", { value: regel.versionId, children: [
17796
17768
  regel.title,
17797
17769
  " (",
17798
17770
  regel.produces.join(", "),
@@ -17802,21 +17774,21 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
17802
17774
  }
17803
17775
  )
17804
17776
  ] }),
17805
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("label", { className: "szenen-field", children: [
17806
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { children: "Eingang" }),
17807
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17777
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("label", { className: "szenen-field", children: [
17778
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { children: "Eingang" }),
17779
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
17808
17780
  "select",
17809
17781
  {
17810
17782
  value: modus,
17811
17783
  onChange: (e) => setModus(e.target.value),
17812
17784
  "data-testid": "modus-auswahl",
17813
- children: (gewaehlt ? gewaehlt.inputModes : ["prompt", "image", "image_prompt"]).map((m) => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("option", { value: m, children: MODUS_TEXT[m] }, m))
17785
+ children: (gewaehlt ? gewaehlt.inputModes : ["prompt", "image", "image_prompt"]).map((m) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: m, children: MODUS_TEXT[m] }, m))
17814
17786
  }
17815
17787
  )
17816
17788
  ] }),
17817
- modus !== "image" && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("label", { className: "szenen-field", children: [
17818
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { children: "Referenzprompt" }),
17819
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17789
+ modus !== "image" && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("label", { className: "szenen-field", children: [
17790
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { children: "Referenzprompt" }),
17791
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
17820
17792
  "textarea",
17821
17793
  {
17822
17794
  value: promptText,
@@ -17826,8 +17798,8 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
17826
17798
  }
17827
17799
  )
17828
17800
  ] }),
17829
- modus !== "prompt" && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
17830
- port.imageLibrary && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17801
+ modus !== "prompt" && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
17802
+ port.imageLibrary && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
17831
17803
  BildWaehler,
17832
17804
  {
17833
17805
  port,
@@ -17836,9 +17808,9 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
17836
17808
  onEntfernen: (ref) => setBestandsbilder((v) => v.filter((b) => b.mediaRef !== ref))
17837
17809
  }
17838
17810
  ),
17839
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("label", { className: "szenen-field", children: [
17840
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { children: port.imageLibrary ? "Oder hochladen" : "Referenzbild" }),
17841
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17811
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("label", { className: "szenen-field", children: [
17812
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { children: port.imageLibrary ? "Oder hochladen" : "Referenzbild" }),
17813
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
17842
17814
  "input",
17843
17815
  {
17844
17816
  type: "file",
@@ -17848,40 +17820,40 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
17848
17820
  "data-testid": "bild-eingabe"
17849
17821
  }
17850
17822
  ),
17851
- port.imageLibrary && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("small", { "data-testid": "upload-hinweis", children: "Hochgeladene Bilder werden im Bestand abgelegt \u2014 nur so ist sp\xE4ter ein Folgelauf damit m\xF6glich." })
17823
+ port.imageLibrary && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("small", { "data-testid": "upload-hinweis", children: "Hochgeladene Bilder werden im Bestand abgelegt \u2014 nur so ist sp\xE4ter ein Folgelauf damit m\xF6glich." })
17852
17824
  ] })
17853
17825
  ] }),
17854
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "submit", disabled: !gewaehlt || laeuft, "data-testid": "lauf-starten", children: laeuft ? "Rekonstruiert \u2026" : "Rekonstruieren" })
17826
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "submit", disabled: !gewaehlt || laeuft, "data-testid": "lauf-starten", children: laeuft ? "Rekonstruiert \u2026" : "Rekonstruieren" })
17855
17827
  ]
17856
17828
  }
17857
17829
  ),
17858
- meldung && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "szenen-error", role: "alert", "data-testid": "derive-meldung", children: meldung }),
17859
- lauf && lauf.status === "failed" && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("section", { className: "szenen-run szenen-run--failed", "data-testid": "lauf-fehler", children: [
17860
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("h3", { children: "Der Lauf ist gescheitert" }),
17861
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("p", { children: [
17862
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("strong", { children: lauf.failure?.class }),
17830
+ meldung && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "szenen-error", role: "alert", "data-testid": "derive-meldung", children: meldung }),
17831
+ lauf && lauf.status === "failed" && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("section", { className: "szenen-run szenen-run--failed", "data-testid": "lauf-fehler", children: [
17832
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("h3", { children: "Der Lauf ist gescheitert" }),
17833
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("p", { children: [
17834
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("strong", { children: lauf.failure?.class }),
17863
17835
  " \u2014 ",
17864
17836
  lauf.failure?.message
17865
17837
  ] }),
17866
- lauf.failure?.diagnostics && lauf.failure.diagnostics.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "szenen-diagnose", "data-testid": "lauf-diagnosen", children: [
17867
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("h4", { children: "Befunde" }),
17868
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Diagnosen, { befunde: lauf.failure.diagnostics, testId: "lauf-befunde" })
17838
+ lauf.failure?.diagnostics && lauf.failure.diagnostics.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "szenen-diagnose", "data-testid": "lauf-diagnosen", children: [
17839
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("h4", { children: "Befunde" }),
17840
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Diagnosen, { befunde: lauf.failure.diagnostics, testId: "lauf-befunde" })
17869
17841
  ] }),
17870
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Herkunft, { lauf }),
17871
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Widerspruch, { lauf }),
17872
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Belege, { lauf }),
17873
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(RecoveryPanel, { port, lauf }),
17842
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Herkunft, { lauf }),
17843
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Widerspruch, { lauf }),
17844
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Belege, { lauf }),
17845
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(RecoveryPanel, { port, lauf }),
17874
17846
  folgeblock(lauf)
17875
17847
  ] }),
17876
- lauf && lauf.status === "succeeded" && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("section", { className: "szenen-run", "data-testid": "lauf-ergebnis", children: [
17877
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("h3", { children: "Vorschlag" }),
17878
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Herkunft, { lauf }),
17879
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Widerspruch, { lauf }),
17880
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "szenen-run__feedback", children: lauf.feedback }),
17881
- lauf.conflicts.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "szenen-diagnose szenen-diagnose--konflikt", "data-testid": "konflikte", children: [
17882
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("h4", { children: "Widerspr\xFCche zwischen Bild und Prompt" }),
17883
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("ul", { children: lauf.conflicts.map((k, i) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("li", { children: [
17884
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("code", { children: k.field }),
17848
+ lauf && lauf.status === "succeeded" && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("section", { className: "szenen-run", "data-testid": "lauf-ergebnis", children: [
17849
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("h3", { children: "Vorschlag" }),
17850
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Herkunft, { lauf }),
17851
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Widerspruch, { lauf }),
17852
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "szenen-run__feedback", children: lauf.feedback }),
17853
+ lauf.conflicts.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "szenen-diagnose szenen-diagnose--konflikt", "data-testid": "konflikte", children: [
17854
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("h4", { children: "Widerspr\xFCche zwischen Bild und Prompt" }),
17855
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("ul", { children: lauf.conflicts.map((k, i) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("li", { children: [
17856
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("code", { children: k.field }),
17885
17857
  ": Bild sagt \u201E",
17886
17858
  k.from_image,
17887
17859
  '", Prompt sagt \u201E',
@@ -17889,31 +17861,31 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
17889
17861
  '". Nichts davon wurde \xFCbernommen.'
17890
17862
  ] }, i)) })
17891
17863
  ] }),
17892
- lauf.not_derivable.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "szenen-diagnose", "data-testid": "nicht-ableitbar", children: [
17893
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("h4", { children: "Nicht ableitbar" }),
17894
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("ul", { children: lauf.not_derivable.map((n, i) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("li", { children: [
17895
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("code", { children: n.field }),
17864
+ lauf.not_derivable.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "szenen-diagnose", "data-testid": "nicht-ableitbar", children: [
17865
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("h4", { children: "Nicht ableitbar" }),
17866
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("ul", { children: lauf.not_derivable.map((n, i) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("li", { children: [
17867
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("code", { children: n.field }),
17896
17868
  ": ",
17897
17869
  n.reason
17898
17870
  ] }, i)) })
17899
17871
  ] }),
17900
- lauf.assumptions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "szenen-diagnose", "data-testid": "annahmen", children: [
17901
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("h4", { children: "Erschlossen, nicht belegt" }),
17902
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("ul", { children: lauf.assumptions.map((a, i) => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("li", { children: a }, i)) })
17872
+ lauf.assumptions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "szenen-diagnose", "data-testid": "annahmen", children: [
17873
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("h4", { children: "Erschlossen, nicht belegt" }),
17874
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("ul", { children: lauf.assumptions.map((a, i) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("li", { children: a }, i)) })
17903
17875
  ] }),
17904
17876
  lauf.proposals.map((vorschlag) => {
17905
17877
  const bezug = REFERENCE_FIELD[vorschlag.type];
17906
17878
  const fertig = uebernommen[vorschlag.type];
17907
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
17879
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
17908
17880
  "article",
17909
17881
  {
17910
17882
  className: "szenen-proposal",
17911
17883
  "data-testid": `vorschlag-${vorschlag.type}`,
17912
17884
  children: [
17913
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("h4", { children: vorschlag.type }),
17914
- !fertig && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("label", { className: "szenen-field", children: [
17915
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { children: "Titel" }),
17916
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17885
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("h4", { children: vorschlag.type }),
17886
+ !fertig && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("label", { className: "szenen-field", children: [
17887
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { children: "Titel" }),
17888
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
17917
17889
  "input",
17918
17890
  {
17919
17891
  type: "text",
@@ -17927,9 +17899,9 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
17927
17899
  }
17928
17900
  )
17929
17901
  ] }),
17930
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("pre", { className: "szenen-proposal__payload", children: JSON.stringify(vorschlag.payload, null, 2) }),
17931
- bezug && !fertig && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
17932
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17902
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("pre", { className: "szenen-proposal__payload", children: JSON.stringify(vorschlag.payload, null, 2) }),
17903
+ bezug && !fertig && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
17904
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
17933
17905
  ReferencePicker,
17934
17906
  {
17935
17907
  port,
@@ -17943,30 +17915,30 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
17943
17915
  }))
17944
17916
  }
17945
17917
  ),
17946
- referenzen[vorschlag.type] ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("p", { "data-testid": `referenz-gewaehlt-${vorschlag.type}`, children: [
17918
+ referenzen[vorschlag.type] ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("p", { "data-testid": `referenz-gewaehlt-${vorschlag.type}`, children: [
17947
17919
  "Zeigt auf Version ",
17948
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("code", { children: referenzen[vorschlag.type] }),
17920
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("code", { children: referenzen[vorschlag.type] }),
17949
17921
  "."
17950
17922
  ] }) : (
17951
17923
  /* Der Grund steht **vor** der Handlung. Den Nutzer erst
17952
17924
  auf einen Knopf drücken zu lassen, um ihm dann eine
17953
17925
  Ablehnung zu zeigen, ist keine Auskunft. */
17954
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("p", { "data-testid": `referenz-fehlt-${vorschlag.type}`, children: [
17926
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("p", { "data-testid": `referenz-fehlt-${vorschlag.type}`, children: [
17955
17927
  "Diese ",
17956
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("strong", { children: "Einzel\xFCbernahme" }),
17928
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("strong", { children: "Einzel\xFCbernahme" }),
17957
17929
  " braucht eine ver\xF6ffentlichte ",
17958
17930
  bezug.refersTo,
17959
17931
  "-Version, auf die der Entwurf zeigt (D-03) \u2014 sie ist der Weg in einen bestehenden, l\xE4ngst ver\xF6ffentlichten Zusammenhang. Wollen Sie aus diesem Lauf erst einmal einen Prompt ausprobieren, nehmen Sie unten die ",
17960
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("strong", { children: "Draft-Kette" }),
17932
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("strong", { children: "Draft-Kette" }),
17961
17933
  ": Dort entstehen Szene, Aufbau und Shot zusammenh\xE4ngend, und es muss nichts ver\xF6ffentlicht werden."
17962
17934
  ] })
17963
17935
  )
17964
17936
  ] }),
17965
- fertig ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("p", { className: "szenen-proposal__done", children: [
17937
+ fertig ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("p", { className: "szenen-proposal__done", children: [
17966
17938
  "Als Entwurf \xFCbernommen: ",
17967
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("code", { children: fertig }),
17939
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("code", { children: fertig }),
17968
17940
  ". Er ist noch nicht ver\xF6ffentlicht."
17969
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17941
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
17970
17942
  "button",
17971
17943
  {
17972
17944
  type: "button",
@@ -17981,13 +17953,337 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
17981
17953
  vorschlag.type
17982
17954
  );
17983
17955
  }),
17984
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ChainActions, { port, runId: lauf.run_id }),
17985
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Belege, { lauf }),
17956
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ChainActions, { port, runId: lauf.run_id }),
17957
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Belege, { lauf }),
17986
17958
  folgeblock(lauf)
17987
17959
  ] })
17988
17960
  ] });
17989
17961
  };
17990
17962
 
17963
+ // src/features/szenen/areas/SceneArea.tsx
17964
+ var import_jsx_runtime43 = require("react/jsx-runtime");
17965
+ var SceneArea = ({ port, contextId }) => {
17966
+ const kannAbleiten = capabilitiesOf(port).derivation;
17967
+ const [ableiten, setAbleiten] = (0, import_react49.useState)(false);
17968
+ const zeigeDerive = kannAbleiten && ableiten;
17969
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "szenen-scene", "data-testid": "bereich-szene", children: [
17970
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "szenen-elements__toolbar", children: [
17971
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "szenen-area__lead", children: "Szenen dieses Kontexts. Auswahl \xF6ffnet die Bearbeitung der Nodes und Beziehungen und startet Prompt-L\xE4ufe." }),
17972
+ kannAbleiten && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
17973
+ "button",
17974
+ {
17975
+ type: "button",
17976
+ className: "szenen-elements__new",
17977
+ "data-testid": "neu-aus-bild-text",
17978
+ "aria-expanded": ableiten,
17979
+ onClick: () => setAbleiten((offen) => !offen),
17980
+ children: ableiten ? "Zur\xFCck zu den Szenen" : "+ Neu aus Bild/Text"
17981
+ }
17982
+ )
17983
+ ] }),
17984
+ !kannAbleiten && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "szenen-view__capability-note", "data-testid": "hinweis-keine-rekonstruktion", children: "Aus Bild oder Text l\xE4sst sich in diesem Host keine Szene ableiten." }),
17985
+ zeigeDerive ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(DeriveArea, { port, contextId }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ElementsArea, { port, contextId, restrictToTypes: ["scene"] })
17986
+ ] });
17987
+ };
17988
+
17989
+ // src/features/szenen/areas/AdvancedArea.tsx
17990
+ var import_jsx_runtime44 = require("react/jsx-runtime");
17991
+ var AdvancedArea = ({ port, contextId }) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "szenen-erweitert", "data-testid": "bereich-erweitert", children: [
17992
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { className: "szenen-area__lead", children: "Optionale Pr\xE4zisierung: Bildaufbau und Shot, Kameras und Stile der Aufnahme, Zielmodelle, Rekonstruktionsregeln und Sequenzen. F\xFCr den normalen Weg von der Szene zum Bild wird hier nichts gebraucht." }),
17993
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
17994
+ ElementsArea,
17995
+ {
17996
+ port,
17997
+ contextId,
17998
+ restrictToTypes: ERWEITERT_TYPES,
17999
+ allTypesLabel: "alle erweiterten Typen",
18000
+ listTitle: "Erweitert"
18001
+ }
18002
+ )
18003
+ ] });
18004
+
18005
+ // src/features/szenen/areas/PromptsArea.tsx
18006
+ var import_react50 = require("react");
18007
+ var import_jsx_runtime45 = require("react/jsx-runtime");
18008
+ var HERKUNFT_TEXT2 = {
18009
+ published: "aus ver\xF6ffentlichtem Shot",
18010
+ draft_chain: "aus Draft-Kette",
18011
+ scene_direct: "direkt aus Szene",
18012
+ unresolved: "Herkunft unvollst\xE4ndig"
18013
+ };
18014
+ var PromptsArea = ({ port, contextId }) => {
18015
+ const faehig = capabilitiesOf(port);
18016
+ const [nachladen, setNachladen] = (0, import_react50.useState)(0);
18017
+ const [offen, setOffen] = (0, import_react50.useState)(null);
18018
+ const [alleKontexte, setAlleKontexte] = (0, import_react50.useState)(false);
18019
+ const [mitFehllaeufen, setMitFehllaeufen] = (0, import_react50.useState)(false);
18020
+ const [herkunft, setHerkunft] = (0, import_react50.useState)("alle");
18021
+ const [phase] = usePortQuery(
18022
+ () => port.runs.listAllRuns ? port.runs.listAllRuns({ status: "all" }) : Promise.resolve({
18023
+ kind: "ok",
18024
+ value: { runs: [], hiddenByStatus: 0, hiddenByContext: 0, withoutContext: 0 }
18025
+ }),
18026
+ [port, nachladen]
18027
+ );
18028
+ const alle = phase.status === "ready" ? phase.value.runs : [];
18029
+ const sicht = (0, import_react50.useMemo)(() => selectPromptRuns(alle, {
18030
+ status: mitFehllaeufen ? "all" : "succeeded",
18031
+ ...alleKontexte ? {} : { contextId }
18032
+ }), [alle, mitFehllaeufen, alleKontexte, contextId]);
18033
+ const titel = useLaufTitel(port, alle);
18034
+ const gezeigt = (0, import_react50.useMemo)(
18035
+ () => herkunft === "alle" ? sicht.runs : sicht.runs.filter((lauf) => groupOf(lauf).kind === herkunft),
18036
+ [sicht.runs, herkunft]
18037
+ );
18038
+ if (!faehig.promptOverview) {
18039
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "szenen-prompts", "data-testid": "prompts-bereich", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { "data-testid": "hinweis-keine-promptsicht", children: "Dieser Host bietet keine herkunfts\xFCbergreifende Prompt-Liste an. Prompt-L\xE4ufe bleiben im Detail ihrer Szene bzw. ihres Shots erreichbar \u2014 es ist kein Fehler und kein vor\xFCbergehender Zustand." }) });
18040
+ }
18041
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "szenen-prompts", "data-testid": "prompts-bereich", children: [
18042
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "szenen-prompts__toolbar", children: [
18043
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h3", { className: "szenen-prompts__heading", children: "Prompts" }),
18044
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("label", { className: "szenen-prompts__filter", children: [
18045
+ "Herkunft",
18046
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
18047
+ "select",
18048
+ {
18049
+ "data-testid": "prompts-herkunft",
18050
+ value: herkunft,
18051
+ onChange: (e) => setHerkunft(e.target.value),
18052
+ children: [
18053
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("option", { value: "alle", children: "alle" }),
18054
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("option", { value: "published", children: HERKUNFT_TEXT2.published }),
18055
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("option", { value: "draft_chain", children: HERKUNFT_TEXT2.draft_chain }),
18056
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("option", { value: "scene_direct", children: HERKUNFT_TEXT2.scene_direct }),
18057
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("option", { value: "unresolved", children: HERKUNFT_TEXT2.unresolved })
18058
+ ]
18059
+ }
18060
+ )
18061
+ ] }),
18062
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("label", { className: "szenen-prompts__filter", children: [
18063
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
18064
+ "input",
18065
+ {
18066
+ type: "checkbox",
18067
+ "data-testid": "prompts-alle-kontexte",
18068
+ checked: alleKontexte,
18069
+ onChange: (e) => setAlleKontexte(e.target.checked)
18070
+ }
18071
+ ),
18072
+ "Alle Kontexte"
18073
+ ] }),
18074
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("label", { className: "szenen-prompts__filter", children: [
18075
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
18076
+ "input",
18077
+ {
18078
+ type: "checkbox",
18079
+ "data-testid": "prompts-mit-fehllaeufen",
18080
+ checked: mitFehllaeufen,
18081
+ onChange: (e) => setMitFehllaeufen(e.target.checked)
18082
+ }
18083
+ ),
18084
+ "Fehll\xE4ufe zeigen"
18085
+ ] })
18086
+ ] }),
18087
+ phase.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { "data-testid": "prompts-laden", children: "Prompts werden geladen \u2026" }),
18088
+ phase.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { "data-testid": "prompts-fehler", role: "alert", children: [
18089
+ "Prompts konnten nicht geladen werden: ",
18090
+ phase.message
18091
+ ] }),
18092
+ phase.status === "ready" && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_jsx_runtime45.Fragment, { children: [
18093
+ gezeigt.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { "data-testid": "keine-prompts", children: alle.length === 0 ? "Es gibt noch keinen Prompt-Lauf." : `Kein Prompt passt zu dieser Auswahl \u2014 von ${alle.length} vorhandenen L\xE4ufen bleibt keiner \xFCbrig.` }),
18094
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("ol", { "data-testid": "prompts-liste", children: gezeigt.map((lauf) => /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
18095
+ PromptEintrag,
18096
+ {
18097
+ port,
18098
+ lauf,
18099
+ alle,
18100
+ titel: titel.get(lauf.run_id) ?? null,
18101
+ offen: lauf.run_id === offen,
18102
+ onUmschalten: () => setOffen(
18103
+ lauf.run_id === offen ? null : lauf.run_id
18104
+ ),
18105
+ itemMoeglich: faehig.saveRunAsItem,
18106
+ onItemGespeichert: () => setNachladen((n) => n + 1)
18107
+ },
18108
+ lauf.run_id
18109
+ )) }),
18110
+ (sicht.hiddenByContext > 0 || sicht.withoutContext > 0 || sicht.hiddenByStatus > 0) && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { className: "szenen-prompts__ausgeblendet", "data-testid": "prompts-ausgeblendet", children: [
18111
+ sicht.hiddenByStatus > 0 && `${sicht.hiddenByStatus} Fehllauf/-l\xE4ufe ausgeblendet. `,
18112
+ sicht.hiddenByContext > 0 && `${sicht.hiddenByContext} Lauf/L\xE4ufe geh\xF6ren zu einem anderen Kontext. `,
18113
+ sicht.withoutContext > 0 && `${sicht.withoutContext} Lauf/L\xE4ufe nennen keinen Kontext und werden keinem zugeordnet \u2014 \u201EAlle Kontexte" zeigt sie. `
18114
+ ] })
18115
+ ] })
18116
+ ] });
18117
+ };
18118
+ var PromptEintrag = ({ port, lauf, alle, titel, offen, onUmschalten, itemMoeglich, onItemGespeichert }) => {
18119
+ const gruppe = groupOf(lauf);
18120
+ const runde = roundOfRun(lauf, siblingsOf(lauf, alle));
18121
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("li", { className: "szenen-prompts__eintrag", "data-testid": `prompt-${lauf.run_id}`, children: [
18122
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { className: "szenen-prompts__herkunft", "data-testid": `herkunft-${lauf.run_id}`, children: [
18123
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { "data-herkunft": gruppe.kind, children: HERKUNFT_TEXT2[gruppe.kind] }),
18124
+ gruppe.kind === "unresolved" && // D-75: Was nicht belegbar ist, wird benannt und nicht
18125
+ // durch eine plausible Kennung ersetzt.
18126
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "szenen-prompts__hinweis", children: [
18127
+ " \u2014 ",
18128
+ gruppe.reason
18129
+ ] })
18130
+ ] }),
18131
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("ol", { className: "szenen-prompts__lauf", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
18132
+ RundenEintrag,
18133
+ {
18134
+ port,
18135
+ lauf,
18136
+ runde,
18137
+ titel,
18138
+ offen,
18139
+ onUmschalten,
18140
+ itemMoeglich,
18141
+ onItemGespeichert
18142
+ }
18143
+ ) })
18144
+ ] });
18145
+ };
18146
+
18147
+ // src/features/szenen/areas/MediaArea.tsx
18148
+ var import_react51 = require("react");
18149
+ var import_jsx_runtime46 = require("react/jsx-runtime");
18150
+ var leer = async () => ({ kind: "ok", value: [] });
18151
+ var MediaArea = ({ port }) => {
18152
+ const media = port.media;
18153
+ const [versionInput, setVersionInput] = (0, import_react51.useState)("");
18154
+ const [versionId, setVersionId] = (0, import_react51.useState)("");
18155
+ const [itemInput, setItemInput] = (0, import_react51.useState)("");
18156
+ const [itemId, setItemId] = (0, import_react51.useState)("");
18157
+ const [assetId, setAssetId] = (0, import_react51.useState)("");
18158
+ const [role, setRole] = (0, import_react51.useState)("reference");
18159
+ const [position, setPosition] = (0, import_react51.useState)("0");
18160
+ const [notiz, setNotiz] = (0, import_react51.useState)(null);
18161
+ const [busy, setBusy] = (0, import_react51.useState)(false);
18162
+ const [refresh, setRefresh] = (0, import_react51.useState)(0);
18163
+ const [versionPhase] = usePortQuery(
18164
+ () => versionId ? media.listVersionMedia(versionId) : leer(),
18165
+ [media, versionId, refresh]
18166
+ );
18167
+ const [itemPhase] = usePortQuery(
18168
+ () => itemId ? media.listItemMedia(itemId) : Promise.resolve({ kind: "ok", value: [] }),
18169
+ [media, itemId, refresh]
18170
+ );
18171
+ const zuordnen = async () => {
18172
+ if (!versionInput.trim() || !assetId.trim()) {
18173
+ setNotiz({ tone: "error", text: "Version-ID und Asset-ID sind erforderlich." });
18174
+ return;
18175
+ }
18176
+ const parsedPosition = Number(position);
18177
+ if (!Number.isInteger(parsedPosition) || parsedPosition < 0) {
18178
+ setNotiz({ tone: "error", text: `Position muss eine nicht-negative Ganzzahl sein, war ${JSON.stringify(position)}.` });
18179
+ return;
18180
+ }
18181
+ setBusy(true);
18182
+ const result = await media.attachVersionMedium({
18183
+ versionId: versionInput.trim(),
18184
+ assetId: assetId.trim(),
18185
+ role,
18186
+ position: parsedPosition
18187
+ });
18188
+ setNotiz(noticeFor(result, "Referenzmedium zuordnen"));
18189
+ if (result.kind === "ok") {
18190
+ setVersionId(versionInput.trim());
18191
+ setRefresh((value) => value + 1);
18192
+ }
18193
+ setBusy(false);
18194
+ };
18195
+ const entfernen = async (medium) => {
18196
+ setBusy(true);
18197
+ const result = await media.detachVersionMedium({
18198
+ versionId,
18199
+ assetId: medium.assetId,
18200
+ role: medium.role
18201
+ });
18202
+ setNotiz(noticeFor(result, "Referenzmedium entfernen"));
18203
+ if (result.kind === "ok") setRefresh((value) => value + 1);
18204
+ setBusy(false);
18205
+ };
18206
+ const versionMedien = versionPhase.status === "ready" ? versionPhase.value : [];
18207
+ const itemMedien = itemPhase.status === "ready" ? itemPhase.value : [];
18208
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "szenen-media", "data-testid": "bereich-medien", children: [
18209
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "szenen-area__lead", children: "Bereits vorhandene Medien-Assets einer Elementversion zuordnen. Hochgeladen wird hier nichts \u2014 Medien entstehen \xFCber den bestehenden Speicherweg aus Stufe 5." }),
18210
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("section", { children: [
18211
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("h3", { children: "Version" }),
18212
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "szenen-media__lookup", children: [
18213
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("label", { children: [
18214
+ "Version-ID",
18215
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("input", { value: versionInput, onChange: (event) => setVersionInput(event.target.value) })
18216
+ ] }),
18217
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", onClick: () => setVersionId(versionInput.trim()), children: "Medien laden" })
18218
+ ] }),
18219
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "szenen-media__attach", children: [
18220
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("label", { children: [
18221
+ "Asset-ID",
18222
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("input", { value: assetId, onChange: (event) => setAssetId(event.target.value) })
18223
+ ] }),
18224
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("label", { children: [
18225
+ "Rolle",
18226
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("select", { value: role, onChange: (event) => setRole(event.target.value), children: [
18227
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("option", { value: "reference", children: "reference" }),
18228
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("option", { value: "mask", children: "mask" }),
18229
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("option", { value: "palette", children: "palette" }),
18230
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("option", { value: "example", children: "example" })
18231
+ ] })
18232
+ ] }),
18233
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("label", { children: [
18234
+ "Position",
18235
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("input", { type: "number", min: "0", value: position, onChange: (event) => setPosition(event.target.value) })
18236
+ ] }),
18237
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", disabled: busy, onClick: zuordnen, children: "zuordnen" })
18238
+ ] }),
18239
+ versionPhase.status === "loading" && versionId && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { children: "Versionsmedien werden geladen \u2026" }),
18240
+ versionPhase.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("p", { role: "alert", children: [
18241
+ "Versionsmedien konnten nicht geladen werden: ",
18242
+ versionPhase.message
18243
+ ] }),
18244
+ versionPhase.status === "ready" && versionId && versionMedien.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("p", { children: [
18245
+ "Version ",
18246
+ versionId,
18247
+ " hat keine Medien."
18248
+ ] }),
18249
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("ul", { className: "szenen-media__list", children: versionMedien.map((medium) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("li", { children: [
18250
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("code", { children: medium.assetId }),
18251
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("span", { children: [
18252
+ medium.role,
18253
+ " \xB7 Position ",
18254
+ medium.position
18255
+ ] }),
18256
+ medium.url && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("a", { href: medium.url, target: "_blank", rel: "noreferrer", children: "\xF6ffnen" }),
18257
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", disabled: busy, onClick: () => entfernen(medium), children: "entfernen" })
18258
+ ] }, `${medium.assetId}:${medium.role}`)) })
18259
+ ] }),
18260
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("section", { children: [
18261
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("h3", { children: "Item-Medien" }),
18262
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "szenen-media__lookup", children: [
18263
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("label", { children: [
18264
+ "Item-ID",
18265
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("input", { value: itemInput, onChange: (event) => setItemInput(event.target.value) })
18266
+ ] }),
18267
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", onClick: () => setItemId(itemInput.trim()), children: "Item-Medien laden" })
18268
+ ] }),
18269
+ itemPhase.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("p", { role: "alert", children: [
18270
+ "Item-Medien konnten nicht geladen werden: ",
18271
+ itemPhase.message
18272
+ ] }),
18273
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("ul", { className: "szenen-media__list", children: itemMedien.map((medium) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("li", { children: [
18274
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("code", { children: medium.assetId }),
18275
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("span", { children: [
18276
+ medium.role,
18277
+ " \xB7 Position ",
18278
+ medium.position
18279
+ ] }),
18280
+ medium.url && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("a", { href: medium.url, target: "_blank", rel: "noreferrer", children: "\xF6ffnen" })
18281
+ ] }, `${medium.assetId}:${medium.role}:${medium.position}`)) })
18282
+ ] }),
18283
+ notiz && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { role: notiz.tone === "error" ? "alert" : "status", "data-tone": notiz.tone, children: notiz.text })
18284
+ ] });
18285
+ };
18286
+
17991
18287
  // src/features/szenen/SzenenView.tsx
17992
18288
  var import_jsx_runtime47 = require("react/jsx-runtime");
17993
18289
  var SzenenView = ({
@@ -17996,9 +18292,9 @@ var SzenenView = ({
17996
18292
  initialArea,
17997
18293
  onAreaChange
17998
18294
  }) => {
17999
- const bereiche = (0, import_react49.useMemo)(() => areasFor(port), [port]);
18000
- const faehig = (0, import_react49.useMemo)(() => capabilitiesOf(port), [port]);
18001
- const [aktiv, setAktiv] = (0, import_react49.useState)(() => resolveArea(port, initialArea));
18295
+ const bereiche = (0, import_react52.useMemo)(() => areasFor(port), [port]);
18296
+ const faehig = (0, import_react52.useMemo)(() => capabilitiesOf(port), [port]);
18297
+ const [aktiv, setAktiv] = (0, import_react52.useState)(() => resolveArea(port, initialArea));
18002
18298
  const wechsle = (id) => {
18003
18299
  setAktiv(id);
18004
18300
  onAreaChange?.(id);
@@ -18009,7 +18305,7 @@ var SzenenView = ({
18009
18305
  /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { children: [
18010
18306
  /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "szenen-view__eyebrow", children: "Vom Motiv zum Bild" }),
18011
18307
  /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("h2", { children: "Bildprompt aus einer Szene" }),
18012
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { children: "Bausteine w\xE4hlen, Szene und Bildaufbau festlegen und daraus einen wiederholbaren Prompt erzeugen." })
18308
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { children: 'Eine Szene anlegen oder aus Bild bzw. Text ableiten und daraus einen Prompt starten. Alle so entstandenen Prompts stehen mit ihren Bildern unter \u201EPrompts". Bausteine und Erweitertes pr\xE4zisieren den Weg, wenn er pr\xE4ziser werden soll.' })
18013
18309
  ] }),
18014
18310
  /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "szenen-view__step", children: aktuellerBereich.title })
18015
18311
  ] }),
@@ -18028,7 +18324,8 @@ var SzenenView = ({
18028
18324
  bereich.id
18029
18325
  )) }),
18030
18326
  /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("section", { className: "szenen-view__area", "aria-label": aktuellerBereich.title, children: [
18031
- aktiv === "derive" && port.derivation && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(DeriveArea, { port, contextId }),
18327
+ aktiv === "scene" && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(SceneArea, { port, contextId }),
18328
+ aktiv === "prompts" && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(PromptsArea, { port, contextId }),
18032
18329
  aktiv === "elements" && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
18033
18330
  ElementsArea,
18034
18331
  {
@@ -18039,9 +18336,7 @@ var SzenenView = ({
18039
18336
  listTitle: "Bausteine"
18040
18337
  }
18041
18338
  ),
18042
- aktiv === "scene" && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(SceneArea, { port, contextId }),
18043
- aktiv === "setup" && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(SetupArea, { port, contextId }),
18044
- aktiv === "shot" && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ShotArea, { port, contextId }),
18339
+ aktiv === "advanced" && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(AdvancedArea, { port, contextId }),
18045
18340
  aktiv === "media" && port.media && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(MediaArea, { port, contextId })
18046
18341
  ] }),
18047
18342
  (!faehig.saveRunAsItem || !faehig.media) && /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("details", { className: "szenen-view__host-notes", children: [
@@ -19107,6 +19402,15 @@ function createHfElementStore(options) {
19107
19402
  return {
19108
19403
  getElement(elementId) {
19109
19404
  return { kind: "ok", value: state.elements.get(elementId) ?? null };
19405
+ },
19406
+ // Aus **derselben** gefalteten Sicht wie `getElement` und ohne
19407
+ // weiteren Netzzugriff: Nur so faellt die Policy-Auswahl des
19408
+ // Szene-Direktlaufs in dieselbe Konsistenzgrenze wie das Einfrieren
19409
+ // der Szene (Schritt 03 §3). Stillgelegte Elemente sind nicht dabei —
19410
+ // Stilllegen ist Ausblenden (D-04), wie bei `listElements`.
19411
+ listElements(filter) {
19412
+ const gefunden = [...state.elements.values()].map((snapshot) => snapshot.element).filter((element) => !element.retired && element.type === filter.type && (element.context_id ?? null) === (filter.contextId ?? null));
19413
+ return { kind: "ok", value: gefunden };
19110
19414
  }
19111
19415
  };
19112
19416
  } catch (error) {
@@ -19114,6 +19418,9 @@ function createHfElementStore(options) {
19114
19418
  return {
19115
19419
  getElement() {
19116
19420
  return { kind: "error", message };
19421
+ },
19422
+ listElements() {
19423
+ return { kind: "error", message };
19117
19424
  }
19118
19425
  };
19119
19426
  }
@@ -19811,7 +20118,8 @@ async function resolveScenePayload(input) {
19811
20118
  sceneVersionId = "",
19812
20119
  resolver,
19813
20120
  contextChain,
19814
- userInstruction
20121
+ userInstruction,
20122
+ autoPolicies
19815
20123
  } = input;
19816
20124
  if (!isObject11(scenePayload)) {
19817
20125
  return {
@@ -19846,6 +20154,7 @@ async function resolveScenePayload(input) {
19846
20154
  resolver,
19847
20155
  contextChain,
19848
20156
  userInstruction,
20157
+ autoPolicies,
19849
20158
  assumptions,
19850
20159
  diagnostics
19851
20160
  });
@@ -19861,6 +20170,7 @@ async function komponiere(z) {
19861
20170
  resolver,
19862
20171
  contextChain,
19863
20172
  userInstruction,
20173
+ autoPolicies,
19864
20174
  assumptions,
19865
20175
  diagnostics
19866
20176
  } = z;
@@ -20023,6 +20333,11 @@ async function komponiere(z) {
20023
20333
  }
20024
20334
  const payload = resolved.version.payload;
20025
20335
  policies.push({
20336
+ // Die Herkunft steht in der aufgeloesten Version, nicht in der Referenz:
20337
+ // `element_id` gehoert zur Version und wird hier nicht aus dem `ref`
20338
+ // uebernommen, der nur eine `version_id` fuehrt.
20339
+ element_id: resolved.version.element_id,
20340
+ version_id: resolved.version.version_id,
20026
20341
  subtype: str(payload.subtype) ?? "general",
20027
20342
  rank: num(payload.rank) ?? 0,
20028
20343
  text: str(payload.text) ?? "",
@@ -20030,6 +20345,16 @@ async function komponiere(z) {
20030
20345
  });
20031
20346
  }
20032
20347
  policies.sort((a, b) => a.rank - b.rank);
20348
+ for (const auto of autoPolicies ?? []) {
20349
+ policies.push({
20350
+ element_id: auto.element_id,
20351
+ version_id: auto.version_id,
20352
+ subtype: auto.subtype,
20353
+ rank: auto.rank,
20354
+ text: auto.text,
20355
+ locale: auto.locale
20356
+ });
20357
+ }
20033
20358
  const fragments = [];
20034
20359
  for (const [i, ref] of asArray3(settings.fragment_refs).entries()) {
20035
20360
  const resolved = await refOf(
@@ -20233,12 +20558,120 @@ async function komponiere(z) {
20233
20558
  assumptions,
20234
20559
  diagnostics,
20235
20560
  capabilities: eingefroreneFaehigkeiten,
20561
+ // Die eine kanonische Stelle: die Anweisung des Aufrufers, sonst die im
20562
+ // Shot hinterlegte. Beim Szene-Direktlauf gibt es nur die erste — und
20563
+ // genau sie fehlte dem Renderer bisher (Schritt 03 §7).
20564
+ user_instruction: userInstruction ?? shotInstruction,
20236
20565
  context_chain: [...contextChain],
20237
20566
  target_model_resolution: targetModelResolution
20238
20567
  }
20239
20568
  };
20240
20569
  }
20241
20570
 
20571
+ // src/lib/szenen/scenePolicies.ts
20572
+ var GENERAL_POLICY_SUBTYPE = "general";
20573
+ var isObject12 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
20574
+ var str2 = (v) => typeof v === "string" && v.trim().length > 0 ? v : null;
20575
+ var num2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
20576
+ function sortScenePolicies(policies) {
20577
+ return [...policies].sort((a, b) => a.rank - b.rank || a.chain_index - b.chain_index || (a.element_id < b.element_id ? -1 : a.element_id > b.element_id ? 1 : 0));
20578
+ }
20579
+ function scenePolicyFingerprint(policies) {
20580
+ return policies.map((p) => `${p.element_id}@${p.version_id}`).join(",");
20581
+ }
20582
+ function resolveGeneralScenePolicies(snapshot, contextChain) {
20583
+ if (typeof snapshot.listElements !== "function") {
20584
+ return {
20585
+ kind: "error",
20586
+ message: "Die Momentaufnahme liefert keine Elementliste. Allgemeine Kontext-Policies eines Szene-Direktlaufs lassen sich damit nicht bestimmen; eine leere Policy-Liste waere eine Behauptung ueber den Bestand, die niemand geprueft hat.",
20587
+ retryable: false
20588
+ };
20589
+ }
20590
+ const kette = contextChain.length > 0 ? contextChain : [null];
20591
+ const gesehen = /* @__PURE__ */ new Set();
20592
+ const gefunden = [];
20593
+ for (const [chainIndex, link] of kette.entries()) {
20594
+ const liste = snapshot.listElements({ contextId: link, type: "prompt_policy" });
20595
+ if (liste.kind !== "ok" || !liste.value) {
20596
+ return {
20597
+ kind: "error",
20598
+ message: liste.message ?? `Die prompt_policy-Elemente des Kontexts ${link ?? "global"} konnten nicht gelesen werden.`,
20599
+ retryable: true
20600
+ };
20601
+ }
20602
+ for (const element of liste.value) {
20603
+ if (gesehen.has(element.element_id)) continue;
20604
+ gesehen.add(element.element_id);
20605
+ const gelesen = snapshot.getElement(element.element_id);
20606
+ if (gelesen.kind !== "ok") {
20607
+ return {
20608
+ kind: "error",
20609
+ message: gelesen.message ?? `Element ${element.element_id} konnte nicht gelesen werden.`,
20610
+ retryable: true
20611
+ };
20612
+ }
20613
+ const stand = gelesen.value;
20614
+ if (!stand) {
20615
+ return {
20616
+ kind: "error",
20617
+ message: `Element ${element.element_id} steht in der Elementliste des Kontexts ${link ?? "global"}, aber nicht in derselben Momentaufnahme.`,
20618
+ retryable: false
20619
+ };
20620
+ }
20621
+ const versionen = stand.versions ?? [];
20622
+ const payloadVon = (versionId2) => {
20623
+ const payload2 = versionen.find((v) => v.version_id === versionId2)?.payload;
20624
+ return isObject12(payload2) ? payload2 : null;
20625
+ };
20626
+ const istGeneral = (versionId2) => str2(payloadVon(versionId2)?.subtype) === GENERAL_POLICY_SUBTYPE;
20627
+ const kopf = effectivePreferredHeadSafe(
20628
+ { element: stand.element, versions: versionen, preferredHeads: stand.preferredHeads },
20629
+ kette
20630
+ );
20631
+ if (kopf.kind === "dag_fehler") {
20632
+ return {
20633
+ kind: "error",
20634
+ message: `Der Versionsgraph der Policy ${element.element_id} ist nicht aufbaubar: ${kopf.message}`,
20635
+ retryable: false
20636
+ };
20637
+ }
20638
+ if (kopf.kind === "keine_version") continue;
20639
+ const resolution = kopf.outcome.resolution;
20640
+ if (resolution.kind === "fork") {
20641
+ const relevant = resolution.heads.filter(istGeneral);
20642
+ if (relevant.length === 0) continue;
20643
+ return {
20644
+ kind: "rejected",
20645
+ message: `Die Policy ${element.element_id} hat ${resolution.heads.length} veroeffentlichte Koepfe und in diesem Kontext keine greifende Auswahl. Mindestens einer davon ist eine allgemeine Regel (${relevant.join(", ")}). Welche gilt, entscheidet keine Reihenfolge (D-36) \u2014 bitte einen bevorzugten Kopf setzen. Es wurde kein Modell gerufen.`,
20646
+ element_id: element.element_id,
20647
+ heads: resolution.heads
20648
+ };
20649
+ }
20650
+ const versionId = usableHeadVersionId(resolutionOf(kopf));
20651
+ if (!versionId) continue;
20652
+ const payload = payloadVon(versionId);
20653
+ if (!payload) {
20654
+ return {
20655
+ kind: "error",
20656
+ message: `Die wirksame Version ${versionId} der Policy ${element.element_id} steht nicht mit lesbarer Payload in der Momentaufnahme.`,
20657
+ retryable: false
20658
+ };
20659
+ }
20660
+ if (str2(payload.subtype) !== GENERAL_POLICY_SUBTYPE) continue;
20661
+ gefunden.push({
20662
+ element_id: element.element_id,
20663
+ version_id: versionId,
20664
+ subtype: GENERAL_POLICY_SUBTYPE,
20665
+ rank: num2(payload.rank) ?? 0,
20666
+ text: str2(payload.text) ?? "",
20667
+ locale: str2(payload.locale),
20668
+ chain_index: chainIndex
20669
+ });
20670
+ }
20671
+ }
20672
+ return { kind: "ok", policies: sortScenePolicies(gefunden) };
20673
+ }
20674
+
20242
20675
  // src/lib/szenen/freezeDraftChain.ts
20243
20676
  function ladeKnoten(sicht, elementId, erwarteterTyp) {
20244
20677
  const gelesen = sicht.getElement(elementId);
@@ -20440,9 +20873,19 @@ async function freezeSceneWorkingState(input) {
20440
20873
  const a = await store.readSnapshot({ fresh: false });
20441
20874
  const szene = ladeSzene(a, sceneElementId, contextChain);
20442
20875
  if ("fehler" in szene) return ausfallErgebnis(szene);
20876
+ const policiesA = resolveGeneralScenePolicies(a, contextChain);
20877
+ if (policiesA.kind === "error") {
20878
+ return { kind: "error", message: policiesA.message, retryable: policiesA.retryable };
20879
+ }
20880
+ if (policiesA.kind !== "ok") return { kind: "rejected", message: policiesA.message };
20443
20881
  const b = await store.readSnapshot({ fresh: true });
20444
20882
  const erneut = ladeSzene(b, sceneElementId, contextChain);
20445
20883
  if ("fehler" in erneut) return ausfallErgebnis(erneut);
20884
+ const policiesB = resolveGeneralScenePolicies(b, contextChain);
20885
+ if (policiesB.kind === "error") {
20886
+ return { kind: "error", message: policiesB.message, retryable: policiesB.retryable };
20887
+ }
20888
+ if (policiesB.kind !== "ok") return { kind: "rejected", message: policiesB.message };
20446
20889
  if (erneut.draft_id !== szene.draft_id || erneut.revision !== szene.revision || erneut.version_id !== szene.version_id) {
20447
20890
  return {
20448
20891
  kind: "conflict",
@@ -20452,6 +20895,17 @@ async function freezeSceneWorkingState(input) {
20452
20895
  actualRevision: erneut.revision ?? 0
20453
20896
  };
20454
20897
  }
20898
+ const vorher = scenePolicyFingerprint(policiesA.policies);
20899
+ const nachher = scenePolicyFingerprint(policiesB.policies);
20900
+ if (vorher !== nachher) {
20901
+ return {
20902
+ kind: "conflict",
20903
+ message: `Die wirksamen allgemeinen Policies der Szene ${sceneElementId} haben sich waehrend des Einfrierens geaendert (erwartet [${vorher || "\u2014"}], vorhanden [${nachher || "\u2014"}]). Es wurde kein Modell gerufen.`,
20904
+ node: { type: "scene", element_id: sceneElementId, draft_id: szene.draft_id ?? "" },
20905
+ expectedRevision: szene.revision ?? 0,
20906
+ actualRevision: erneut.revision ?? 0
20907
+ };
20908
+ }
20455
20909
  const aufgeloest = await resolveScenePayload({
20456
20910
  scenePayload: szene.payload,
20457
20911
  sceneElementId: szene.element_id,
@@ -20460,7 +20914,10 @@ async function freezeSceneWorkingState(input) {
20460
20914
  sceneVersionId: szene.version_id ?? "",
20461
20915
  resolver,
20462
20916
  contextChain,
20463
- userInstruction
20917
+ userInstruction,
20918
+ // Der aus A gebildete und gegen B bestaetigte Satz — kein dritter
20919
+ // Leseweg nach dem Freeze.
20920
+ autoPolicies: policiesA.policies
20464
20921
  });
20465
20922
  if (aufgeloest.kind !== "resolved") {
20466
20923
  return {
@@ -20477,7 +20934,8 @@ async function freezeSceneWorkingState(input) {
20477
20934
  revision: szene.revision,
20478
20935
  version_id: szene.version_id
20479
20936
  },
20480
- input: aufgeloest.input
20937
+ input: aufgeloest.input,
20938
+ policies: policiesA.policies
20481
20939
  };
20482
20940
  }
20483
20941
 
@@ -20537,6 +20995,24 @@ function buildSystemInstruction2(input) {
20537
20995
  'Widersprechen sie einander, nenne den Widerspruch mit beiden Quellen in "notes" \u2014',
20538
20996
  "waehle nicht still eine Seite."
20539
20997
  );
20998
+ if (input.policies.length > 0) {
20999
+ zeilen.push(
21000
+ "",
21001
+ 'In "policies" stehen verbindliche Transformationsregeln fuer die Formulierung.',
21002
+ "Setze sie um, ohne ihren Wortlaut in den Prompt zu uebernehmen; sie sind",
21003
+ "Anweisung an dich, nicht Prompttext. Bei gleichem Rang gilt die Reihenfolge",
21004
+ "der Liste."
21005
+ );
21006
+ }
21007
+ if (input.user_instruction !== null && input.user_instruction !== void 0) {
21008
+ zeilen.push(
21009
+ "",
21010
+ '"user_instruction" ist eine ZUSAETZLICHE Arbeitsanweisung fuer genau diesen Lauf.',
21011
+ "Sie tritt zu den Policies hinzu und hebt keine davon auf. Widerspricht sie einer",
21012
+ 'Policy erkennbar, nenne den Widerspruch mit beiden Quellen in "feedback" bzw.',
21013
+ '"notes" \u2014 waehle nicht still eine Seite.'
21014
+ );
21015
+ }
20540
21016
  if (input.camera === null) {
20541
21017
  zeilen.push(
20542
21018
  "",
@@ -20558,7 +21034,7 @@ function buildSystemInstruction2(input) {
20558
21034
  function buildUserPrompt2(input) {
20559
21035
  return JSON.stringify(input, null, 2);
20560
21036
  }
20561
- var isObject12 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
21037
+ var isObject13 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
20562
21038
  function parseRunResponse(raw) {
20563
21039
  const text = raw.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "").trim();
20564
21040
  if (text.length === 0) return { ok: false, message: "Die Antwort war leer." };
@@ -20572,7 +21048,7 @@ function parseRunResponse(raw) {
20572
21048
  }
20573
21049
  }
20574
21050
  function checkRunResponse(value, input) {
20575
- if (!isObject12(value)) {
21051
+ if (!isObject13(value)) {
20576
21052
  return { ok: false, message: `Die Antwort ist kein Objekt, sondern ${Array.isArray(value) ? "array" : typeof value}.` };
20577
21053
  }
20578
21054
  if (typeof value.positive !== "string" || value.positive.trim().length === 0) {
@@ -20594,7 +21070,7 @@ function checkRunResponse(value, input) {
20594
21070
  };
20595
21071
  }
20596
21072
  const visibility = value.visibility;
20597
- if (!isObject12(visibility)) {
21073
+ if (!isObject13(visibility)) {
20598
21074
  return { ok: false, message: '"visibility" fehlt \u2014 die Pflichtdiagnosen aus D-21.' };
20599
21075
  }
20600
21076
  if (typeof visibility.basis !== "string") {
@@ -20606,7 +21082,7 @@ function checkRunResponse(value, input) {
20606
21082
  const genannt = /* @__PURE__ */ new Set();
20607
21083
  const nodes = [];
20608
21084
  for (const eintrag of visibility.nodes) {
20609
- if (!isObject12(eintrag) || typeof eintrag.node_id !== "string" || typeof eintrag.state !== "string") {
21085
+ if (!isObject13(eintrag) || typeof eintrag.node_id !== "string" || typeof eintrag.state !== "string") {
20610
21086
  return { ok: false, message: 'Ein Eintrag in "visibility.nodes" hat keine node_id oder keinen state.' };
20611
21087
  }
20612
21088
  genannt.add(eintrag.node_id);
@@ -20719,8 +21195,13 @@ function createEventLogRunPersistence(channel) {
20719
21195
  // Nur veroeffentlichte Laeufe. Ein Draft-Lauf hat eine **leere**
20720
21196
  // shot_version_id (D-98) und wuerde bei der Suche nach '' sonst
20721
21197
  // faelschlich mitkommen — eine stille Vermischung zweier Herkuenfte.
21198
+ //
21199
+ // Seit P1-B faellt die Entscheidung nicht mehr hier, sondern in
21200
+ // `groupKeyOf()`: dieselbe Regel, aus der auch die Geschwister und
21201
+ // damit die Rundennummer entstehen. Vorher stand sie hier **und**
21202
+ // — anders — in `domainService.getPromptRunWithSiblings()`.
20722
21203
  value: collectPromptRuns(events).filter(
20723
- (run) => sourceOf(run).kind === "published" && run.shot_version_id === shotVersionId
21204
+ (run) => groupKeyOf(run) === `published:${shotVersionId}`
20724
21205
  )
20725
21206
  };
20726
21207
  } catch (error) {
@@ -20742,10 +21223,9 @@ function createEventLogRunPersistence(channel) {
20742
21223
  const events = await channel.readEvents();
20743
21224
  return {
20744
21225
  kind: "ok",
20745
- value: collectPromptRuns(events).filter((run) => {
20746
- const quelle = sourceOf(run);
20747
- return quelle.kind === "draft_chain" && quelle.chain_id === chainId;
20748
- })
21226
+ value: collectPromptRuns(events).filter(
21227
+ (run) => groupKeyOf(run) === `chain:${chainId}`
21228
+ )
20749
21229
  };
20750
21230
  } catch (error) {
20751
21231
  return queryError(error, `Laeufe der Kette ${chainId} konnten nicht gelesen werden`);
@@ -20756,10 +21236,9 @@ function createEventLogRunPersistence(channel) {
20756
21236
  const events = await channel.readEvents();
20757
21237
  return {
20758
21238
  kind: "ok",
20759
- value: collectPromptRuns(events).filter((run) => {
20760
- const quelle = sourceOf(run);
20761
- return quelle.kind === "scene_direct" && quelle.scene.element_id === sceneElementId;
20762
- })
21239
+ value: collectPromptRuns(events).filter(
21240
+ (run) => groupKeyOf(run) === `scene:${sceneElementId}`
21241
+ )
20763
21242
  };
20764
21243
  } catch (error) {
20765
21244
  return queryError(
@@ -20768,6 +21247,16 @@ function createEventLogRunPersistence(channel) {
20768
21247
  );
20769
21248
  }
20770
21249
  },
21250
+ // P1-A: dieselbe Faltung, nur ohne Herkunftsfilter. Sie las schon bisher
21251
+ // in jedem der drei Fälle den **vollen** Log — hier fällt lediglich das
21252
+ // Aussieben weg.
21253
+ async listAll() {
21254
+ try {
21255
+ return { kind: "ok", value: collectPromptRuns(await channel.readEvents()) };
21256
+ } catch (error) {
21257
+ return queryError(error, "Die Prompt-Laeufe konnten nicht gelesen werden");
21258
+ }
21259
+ },
20771
21260
  async record(record) {
20772
21261
  try {
20773
21262
  await channel.appendEvent(SZENEN_EVENT_TYPES.promptRunRecorded, record);
@@ -21064,7 +21553,25 @@ function createClientRunPort(options) {
21064
21553
  `Laeufe der Szene ${sceneElementId} konnten nicht gelesen werden`
21065
21554
  );
21066
21555
  }
21067
- }
21556
+ },
21557
+ // ── Die gemeinsame Prompt-Sicht (P1-A) ────────────────────────────────
21558
+ //
21559
+ // Sie wird nur angeboten, wenn die **Persistenz** aufzählen kann. Ein
21560
+ // Host, dessen Speicher das nicht hergibt, bekommt hier keine Operation —
21561
+ // und die Ansicht nennt den Grund, statt eine leere Liste zu zeigen (§8).
21562
+ // Der Filter läuft über `selectPromptRuns()`, dieselbe Funktion, die auch
21563
+ // der Server benutzt; zwei Bezugsrahmen wären zwei Wahrheiten.
21564
+ ...typeof runs.listAll === "function" ? {
21565
+ async listAllRuns(filter) {
21566
+ try {
21567
+ const alle = await runs.listAll();
21568
+ if (alle.kind !== "ok") return alle;
21569
+ return { kind: "ok", value: selectPromptRuns(alle.value, filter) };
21570
+ } catch (error) {
21571
+ return queryError(error, "Die Prompt-Laeufe konnten nicht gelesen werden");
21572
+ }
21573
+ }
21574
+ } : {}
21068
21575
  };
21069
21576
  }
21070
21577
 
@@ -22318,7 +22825,7 @@ function resolveBootstrapTarget(input) {
22318
22825
  // src/components/AvatarArchitectApp.tsx
22319
22826
  var import_jsx_runtime48 = require("react/jsx-runtime");
22320
22827
  function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, initialHfToken, hfNamespace, allowDevNamespace, requireWorkspaceSelection = false, serverBaseUrl }) {
22321
- (0, import_react50.useEffect)(() => {
22828
+ (0, import_react53.useEffect)(() => {
22322
22829
  const id = "flow-styles";
22323
22830
  if (!document.getElementById(id)) {
22324
22831
  const style = document.createElement("style");
@@ -22327,33 +22834,33 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
22327
22834
  document.head.appendChild(style);
22328
22835
  }
22329
22836
  }, []);
22330
- const [showStart, setShowStart] = (0, import_react50.useState)(true);
22331
- const [layoutChoice, setLayoutChoice] = (0, import_react50.useState)(() => {
22837
+ const [showStart, setShowStart] = (0, import_react53.useState)(true);
22838
+ const [layoutChoice, setLayoutChoice] = (0, import_react53.useState)(() => {
22332
22839
  try {
22333
22840
  return localStorage.getItem("aa-layout") || null;
22334
22841
  } catch {
22335
22842
  return null;
22336
22843
  }
22337
22844
  });
22338
- const [hfToken, setHfToken] = (0, import_react50.useState)(initialHfToken || "");
22339
- const [hfTokenInput, setHfTokenInput] = (0, import_react50.useState)(initialHfToken || "");
22340
- const [hfNamespaceLocal, setHfNamespaceLocal] = (0, import_react50.useState)(() => {
22845
+ const [hfToken, setHfToken] = (0, import_react53.useState)(initialHfToken || "");
22846
+ const [hfTokenInput, setHfTokenInput] = (0, import_react53.useState)(initialHfToken || "");
22847
+ const [hfNamespaceLocal, setHfNamespaceLocal] = (0, import_react53.useState)(() => {
22341
22848
  try {
22342
22849
  return normalizeSelectableNamespace(localStorage.getItem("aa-hf-namespace"));
22343
22850
  } catch {
22344
22851
  return DEFAULT_NAMESPACE;
22345
22852
  }
22346
22853
  });
22347
- const [hfNamespaceFromServer, setHfNamespaceFromServer] = (0, import_react50.useState)(null);
22348
- const [serverProbeFailed, setServerProbeFailed] = (0, import_react50.useState)(false);
22349
- (0, import_react50.useEffect)(() => {
22854
+ const [hfNamespaceFromServer, setHfNamespaceFromServer] = (0, import_react53.useState)(null);
22855
+ const [serverProbeFailed, setServerProbeFailed] = (0, import_react53.useState)(false);
22856
+ (0, import_react53.useEffect)(() => {
22350
22857
  if (hfNamespace !== void 0 || !serverBaseUrl) return;
22351
22858
  fetch(`${serverBaseUrl}/api/status`).then((r) => r.json()).then((d) => {
22352
22859
  if (typeof d.hfNamespace === "string" && d.hfNamespace) setHfNamespaceFromServer(d.hfNamespace);
22353
22860
  else setServerProbeFailed(true);
22354
22861
  }).catch(() => setServerProbeFailed(true));
22355
22862
  }, [hfNamespace, serverBaseUrl]);
22356
- const [targetConfirmed, setTargetConfirmed] = (0, import_react50.useState)(false);
22863
+ const [targetConfirmed, setTargetConfirmed] = (0, import_react53.useState)(false);
22357
22864
  const isDialogTarget = hfNamespace === void 0 && hfNamespaceFromServer === null && (!serverBaseUrl || serverProbeFailed);
22358
22865
  const isProbingServer = hfNamespace === void 0 && !!serverBaseUrl && hfNamespaceFromServer === null && !serverProbeFailed;
22359
22866
  const bootstrapTarget = resolveBootstrapTarget({
@@ -22364,8 +22871,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
22364
22871
  isStartDialogOpen: isDialogTarget && !targetConfirmed
22365
22872
  });
22366
22873
  const effectiveNamespace = bootstrapTarget.namespace ?? "";
22367
- const kontextRef = (0, import_react50.useRef)(null);
22368
- const szenenPort = (0, import_react50.useMemo)(() => hfToken && effectiveNamespace ? createFlowSzenenViewPort({
22874
+ const kontextRef = (0, import_react53.useRef)(null);
22875
+ const szenenPort = (0, import_react53.useMemo)(() => hfToken && effectiveNamespace ? createFlowSzenenViewPort({
22369
22876
  generateText: onGeneratePrompt,
22370
22877
  token: hfToken,
22371
22878
  namespace: effectiveNamespace,
@@ -22387,10 +22894,10 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
22387
22894
  const tagBaseResult = useTagBase(hfToken, effectiveNamespace);
22388
22895
  const szenenKontextId = tagBaseResult.selectedContextId === null ? null : String(tagBaseResult.selectedContextId);
22389
22896
  kontextRef.current = szenenKontextId;
22390
- const [imageUploadStatus, setImageUploadStatus] = (0, import_react50.useState)(/* @__PURE__ */ new Map());
22391
- const [bootstrapLog, setBootstrapLog] = (0, import_react50.useState)([]);
22392
- const [isBootstrapping, setIsBootstrapping] = (0, import_react50.useState)(false);
22393
- const [hfMissingImages, setHfMissingImages] = (0, import_react50.useState)([]);
22897
+ const [imageUploadStatus, setImageUploadStatus] = (0, import_react53.useState)(/* @__PURE__ */ new Map());
22898
+ const [bootstrapLog, setBootstrapLog] = (0, import_react53.useState)([]);
22899
+ const [isBootstrapping, setIsBootstrapping] = (0, import_react53.useState)(false);
22900
+ const [hfMissingImages, setHfMissingImages] = (0, import_react53.useState)([]);
22394
22901
  const syncTopSlot = /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_jsx_runtime48.Fragment, { children: [
22395
22902
  localOnlyCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime48.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: [
22396
22903
  "\u26A0 ",
@@ -22449,18 +22956,18 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
22449
22956
  setTargetConfirmed(true);
22450
22957
  setShowStart(false);
22451
22958
  };
22452
- const [nodes, setNodes] = (0, import_react50.useState)([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
22453
- const [edges, setEdges] = (0, import_react50.useState)([]);
22454
- const [history, setHistory] = (0, import_react50.useState)([]);
22455
- const [galleryItems, setGalleryItems] = (0, import_react50.useState)([]);
22456
- const galleryItemsRef = (0, import_react50.useRef)([]);
22457
- (0, import_react50.useEffect)(() => {
22959
+ const [nodes, setNodes] = (0, import_react53.useState)([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
22960
+ const [edges, setEdges] = (0, import_react53.useState)([]);
22961
+ const [history, setHistory] = (0, import_react53.useState)([]);
22962
+ const [galleryItems, setGalleryItems] = (0, import_react53.useState)([]);
22963
+ const galleryItemsRef = (0, import_react53.useRef)([]);
22964
+ (0, import_react53.useEffect)(() => {
22458
22965
  galleryItemsRef.current = galleryItems;
22459
22966
  }, [galleryItems]);
22460
- const hfImageNotFoundRef = (0, import_react50.useRef)(/* @__PURE__ */ new Map());
22461
- const [galleryVisibleCount, setGalleryVisibleCount] = (0, import_react50.useState)(20);
22462
- const [historyVisibleCount, setHistoryVisibleCount] = (0, import_react50.useState)(20);
22463
- const loadThumbnailsForEntries = (0, import_react50.useCallback)(async (entries) => {
22967
+ const hfImageNotFoundRef = (0, import_react53.useRef)(/* @__PURE__ */ new Map());
22968
+ const [galleryVisibleCount, setGalleryVisibleCount] = (0, import_react53.useState)(20);
22969
+ const [historyVisibleCount, setHistoryVisibleCount] = (0, import_react53.useState)(20);
22970
+ const loadThumbnailsForEntries = (0, import_react53.useCallback)(async (entries) => {
22464
22971
  for (const entry of entries) {
22465
22972
  if (galleryItemsRef.current.find((g) => g.id === entry.id)?.base64) continue;
22466
22973
  if (hfImageNotFoundRef.current.has(entry.id)) continue;
@@ -22479,7 +22986,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
22479
22986
  }
22480
22987
  }
22481
22988
  }, [hfToken, effectiveNamespace]);
22482
- (0, import_react50.useEffect)(() => {
22989
+ (0, import_react53.useEffect)(() => {
22483
22990
  if (!hfState) return;
22484
22991
  if (hfState.tags?.by_category) setWorkspaceTags(hfState.tags);
22485
22992
  const hfIds = new Set(hfState.metadata.map((m) => m.id));
@@ -22506,36 +23013,36 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
22506
23013
  return [...localOnly, ...merged].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
22507
23014
  });
22508
23015
  }, [hfState]);
22509
- (0, import_react50.useEffect)(() => {
23016
+ (0, import_react53.useEffect)(() => {
22510
23017
  if (!hfState) return;
22511
23018
  const sortedMeta = [...hfState.metadata].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
22512
23019
  const skeletons = sortedMeta.map((m) => ({ id: m.id, prompt: m.prompt, timestamp: m.timestamp, status: "done", nodeId: m.id, tags: m.tags || [] }));
22513
23020
  const repIds = new Set(groupByPrompt(skeletons).slice(0, galleryVisibleCount).map((g) => g.representative.id));
22514
23021
  loadThumbnailsForEntries(sortedMeta.filter((e) => repIds.has(e.id)));
22515
23022
  }, [hfState, galleryVisibleCount, loadThumbnailsForEntries]);
22516
- (0, import_react50.useEffect)(() => {
23023
+ (0, import_react53.useEffect)(() => {
22517
23024
  if (!hfState) return;
22518
23025
  const sortedMeta = [...hfState.metadata].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
22519
23026
  loadThumbnailsForEntries(sortedMeta.slice(0, historyVisibleCount));
22520
23027
  }, [hfState, historyVisibleCount, loadThumbnailsForEntries]);
22521
- const [activePrompt, setActivePrompt] = (0, import_react50.useState)("");
22522
- const [isSynthesizing, setIsSynthesizing] = (0, import_react50.useState)(false);
22523
- const [activeGenerationsCount, setActiveGenerationsCount] = (0, import_react50.useState)(0);
22524
- const [currentResult, setCurrentResult] = (0, import_react50.useState)(null);
22525
- const [focusedNodeId, setFocusedNodeId] = (0, import_react50.useState)(null);
22526
- const [leftTab, setLeftTab] = (0, import_react50.useState)("prompt");
22527
- const [promptFeedback, setPromptFeedback] = (0, import_react50.useState)(null);
22528
- const [lastPromptPayload, setLastPromptPayload] = (0, import_react50.useState)(null);
22529
- const [isPromptTabGenerating, setIsPromptTabGenerating] = (0, import_react50.useState)(false);
22530
- const [activeTab, setActiveTab] = (0, import_react50.useState)("history");
22531
- const [mobileTab, setMobileTab] = (0, import_react50.useState)("stage");
22532
- const [middlePanel, setMiddlePanel] = (0, import_react50.useState)("stage");
22533
- const [recentLabItems, setRecentLabItems] = (0, import_react50.useState)([]);
22534
- const [aspectRatio, setAspectRatio] = (0, import_react50.useState)("1:1");
22535
- const [selectedModel, setSelectedModel] = (0, import_react50.useState)("\u{1F34C} Nano Banana Pro");
22536
- const [seed, setSeed] = (0, import_react50.useState)(Math.floor(Math.random() * 1e6));
22537
- const [seedMode, setSeedMode] = (0, import_react50.useState)("random");
22538
- const [imageCount, setImageCount] = (0, import_react50.useState)(() => {
23028
+ const [activePrompt, setActivePrompt] = (0, import_react53.useState)("");
23029
+ const [isSynthesizing, setIsSynthesizing] = (0, import_react53.useState)(false);
23030
+ const [activeGenerationsCount, setActiveGenerationsCount] = (0, import_react53.useState)(0);
23031
+ const [currentResult, setCurrentResult] = (0, import_react53.useState)(null);
23032
+ const [focusedNodeId, setFocusedNodeId] = (0, import_react53.useState)(null);
23033
+ const [leftTab, setLeftTab] = (0, import_react53.useState)("prompt");
23034
+ const [promptFeedback, setPromptFeedback] = (0, import_react53.useState)(null);
23035
+ const [lastPromptPayload, setLastPromptPayload] = (0, import_react53.useState)(null);
23036
+ const [isPromptTabGenerating, setIsPromptTabGenerating] = (0, import_react53.useState)(false);
23037
+ const [activeTab, setActiveTab] = (0, import_react53.useState)("history");
23038
+ const [mobileTab, setMobileTab] = (0, import_react53.useState)("stage");
23039
+ const [middlePanel, setMiddlePanel] = (0, import_react53.useState)("stage");
23040
+ const [recentLabItems, setRecentLabItems] = (0, import_react53.useState)([]);
23041
+ const [aspectRatio, setAspectRatio] = (0, import_react53.useState)("1:1");
23042
+ const [selectedModel, setSelectedModel] = (0, import_react53.useState)("\u{1F34C} Nano Banana Pro");
23043
+ const [seed, setSeed] = (0, import_react53.useState)(Math.floor(Math.random() * 1e6));
23044
+ const [seedMode, setSeedMode] = (0, import_react53.useState)("random");
23045
+ const [imageCount, setImageCount] = (0, import_react53.useState)(() => {
22539
23046
  try {
22540
23047
  const v = parseInt(localStorage.getItem("aa-image-count") || "", 10);
22541
23048
  return v >= 1 && v <= 8 ? v : 4;
@@ -22557,41 +23064,41 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
22557
23064
  { label: "4 Bilder", value: "4" },
22558
23065
  { label: "8 Bilder", value: "8" }
22559
23066
  ];
22560
- const [isLeftCollapsed, setIsLeftCollapsed] = (0, import_react50.useState)(false);
22561
- const [isRightCollapsed, setIsRightCollapsed] = (0, import_react50.useState)(false);
22562
- const [leftPanelWidth, setLeftPanelWidth] = (0, import_react50.useState)(() => {
23067
+ const [isLeftCollapsed, setIsLeftCollapsed] = (0, import_react53.useState)(false);
23068
+ const [isRightCollapsed, setIsRightCollapsed] = (0, import_react53.useState)(false);
23069
+ const [leftPanelWidth, setLeftPanelWidth] = (0, import_react53.useState)(() => {
22563
23070
  try {
22564
23071
  return parseInt(localStorage.getItem("aa-left-width") || "260", 10);
22565
23072
  } catch {
22566
23073
  return 260;
22567
23074
  }
22568
23075
  });
22569
- const [rightPanelWidth, setRightPanelWidth] = (0, import_react50.useState)(() => {
23076
+ const [rightPanelWidth, setRightPanelWidth] = (0, import_react53.useState)(() => {
22570
23077
  try {
22571
23078
  return parseInt(localStorage.getItem("aa-right-width") || "320", 10);
22572
23079
  } catch {
22573
23080
  return 320;
22574
23081
  }
22575
23082
  });
22576
- const [isPromptCollapsed, setIsPromptCollapsed] = (0, import_react50.useState)(false);
22577
- const [workspaceTags, setWorkspaceTags] = (0, import_react50.useState)(null);
22578
- const [highContrast, setHighContrast] = (0, import_react50.useState)(() => {
23083
+ const [isPromptCollapsed, setIsPromptCollapsed] = (0, import_react53.useState)(false);
23084
+ const [workspaceTags, setWorkspaceTags] = (0, import_react53.useState)(null);
23085
+ const [highContrast, setHighContrast] = (0, import_react53.useState)(() => {
22579
23086
  try {
22580
23087
  return localStorage.getItem("aa-contrast") === "high";
22581
23088
  } catch {
22582
23089
  return false;
22583
23090
  }
22584
23091
  });
22585
- const [activeReferenceId, setActiveReferenceId] = (0, import_react50.useState)(null);
22586
- const [activeReferenceThumbnail, setActiveReferenceThumbnail] = (0, import_react50.useState)(null);
22587
- const [isScanningImage, setIsScanningImage] = (0, import_react50.useState)(false);
22588
- const [touchStartX, setTouchStartX] = (0, import_react50.useState)(null);
22589
- const [isFullscreen, setIsFullscreen] = (0, import_react50.useState)(false);
22590
- const [zoomScale, setZoomScale] = (0, import_react50.useState)(1);
22591
- const [zoomOffset, setZoomOffset] = (0, import_react50.useState)({ x: 0, y: 0 });
22592
- const lastPinchDist = (0, import_react50.useRef)(null);
22593
- const lastTapTime = (0, import_react50.useRef)(0);
22594
- const dragStart = (0, import_react50.useRef)(null);
23092
+ const [activeReferenceId, setActiveReferenceId] = (0, import_react53.useState)(null);
23093
+ const [activeReferenceThumbnail, setActiveReferenceThumbnail] = (0, import_react53.useState)(null);
23094
+ const [isScanningImage, setIsScanningImage] = (0, import_react53.useState)(false);
23095
+ const [touchStartX, setTouchStartX] = (0, import_react53.useState)(null);
23096
+ const [isFullscreen, setIsFullscreen] = (0, import_react53.useState)(false);
23097
+ const [zoomScale, setZoomScale] = (0, import_react53.useState)(1);
23098
+ const [zoomOffset, setZoomOffset] = (0, import_react53.useState)({ x: 0, y: 0 });
23099
+ const lastPinchDist = (0, import_react53.useRef)(null);
23100
+ const lastTapTime = (0, import_react53.useRef)(0);
23101
+ const dragStart = (0, import_react53.useRef)(null);
22595
23102
  const openFullscreen = () => {
22596
23103
  setIsFullscreen(true);
22597
23104
  setZoomScale(1);
@@ -22654,7 +23161,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
22654
23161
  setActiveReferenceId(null);
22655
23162
  setActiveReferenceThumbnail(null);
22656
23163
  };
22657
- const labServices = (0, import_react50.useMemo)(() => {
23164
+ const labServices = (0, import_react53.useMemo)(() => {
22658
23165
  const available = groupGenerationsToLabItems([...galleryItems, ...history]);
22659
23166
  return {
22660
23167
  availableItems: available,
@@ -22734,14 +23241,14 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
22734
23241
  setIsScanningImage(false);
22735
23242
  }
22736
23243
  };
22737
- const currentIndex = (0, import_react50.useMemo)(() => history.findIndex((h) => h.id === currentResult?.id), [history, currentResult]);
22738
- const goToPrev = (0, import_react50.useCallback)(() => {
23244
+ const currentIndex = (0, import_react53.useMemo)(() => history.findIndex((h) => h.id === currentResult?.id), [history, currentResult]);
23245
+ const goToPrev = (0, import_react53.useCallback)(() => {
22739
23246
  if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
22740
23247
  }, [currentIndex, history]);
22741
- const goToNext = (0, import_react50.useCallback)(() => {
23248
+ const goToNext = (0, import_react53.useCallback)(() => {
22742
23249
  if (currentIndex < history.length - 1) setCurrentResult(history[currentIndex + 1]);
22743
23250
  }, [currentIndex, history]);
22744
- const handleGallerySelect = (0, import_react50.useCallback)((g) => {
23251
+ const handleGallerySelect = (0, import_react53.useCallback)((g) => {
22745
23252
  setCurrentResult(g);
22746
23253
  setMobileTab("stage");
22747
23254
  if (g.hasThumb && g.filename && hfToken && !g.fullBase64) {
@@ -22754,7 +23261,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
22754
23261
  });
22755
23262
  }
22756
23263
  }, [hfToken, effectiveNamespace]);
22757
- const handleTitleSet = (0, import_react50.useCallback)((id) => {
23264
+ const handleTitleSet = (0, import_react53.useCallback)((id) => {
22758
23265
  const ts = Date.now();
22759
23266
  setGalleryItems((prev) => prev.map((g) => g.id === id ? { ...g, titleTs: ts } : g));
22760
23267
  setHistory((prev) => prev.map((g) => g.id === id ? { ...g, titleTs: ts } : g));
@@ -22763,13 +23270,13 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
22763
23270
  });
22764
23271
  }
22765
23272
  }, [hfToken, effectiveNamespace, hfWriteEvent]);
22766
- const currentGroup = (0, import_react50.useMemo)(() => {
23273
+ const currentGroup = (0, import_react53.useMemo)(() => {
22767
23274
  if (!currentResult?.prompt) return [];
22768
23275
  const groups = groupByPrompt(galleryItems.filter((g) => g.status === "done" && !!g.base64));
22769
23276
  const group = groups.find((gr) => gr.prompt === currentResult.prompt);
22770
23277
  return group ? group.items : [];
22771
23278
  }, [galleryItems, currentResult?.prompt]);
22772
- (0, import_react50.useEffect)(() => {
23279
+ (0, import_react53.useEffect)(() => {
22773
23280
  if (!currentResult?.prompt || !hfToken || !effectiveNamespace || !hfState) return;
22774
23281
  const siblings = galleryItemsRef.current.filter(
22775
23282
  (g) => g.prompt === currentResult.prompt && !g.base64 && !hfImageNotFoundRef.current.has(g.id)
@@ -22808,7 +23315,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
22808
23315
  }
22809
23316
  ) : null;
22810
23317
  useKeyboardNavigation(history, currentResult, setCurrentResult);
22811
- const getSubtreeFormat = (0, import_react50.useCallback)((nodeId, depth = 0) => {
23318
+ const getSubtreeFormat = (0, import_react53.useCallback)((nodeId, depth = 0) => {
22812
23319
  const node = nodes.find((n) => n.id === nodeId);
22813
23320
  if (!node) return "";
22814
23321
  const childrenIds = edges.filter((e) => e.source === nodeId).map((e) => e.target);
@@ -22816,7 +23323,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
22816
23323
  return `${indent}- ${node.data.label || "(unbenannt)"}
22817
23324
  ` + childrenIds.map((id) => getSubtreeFormat(id, depth + 1)).join("");
22818
23325
  }, [nodes, edges]);
22819
- const activePath = (0, import_react50.useMemo)(() => {
23326
+ const activePath = (0, import_react53.useMemo)(() => {
22820
23327
  if (!focusedNodeId) return /* @__PURE__ */ new Set();
22821
23328
  const path = /* @__PURE__ */ new Set([focusedNodeId]);
22822
23329
  let currId = focusedNodeId;
@@ -23968,7 +24475,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
23968
24475
  }
23969
24476
 
23970
24477
  // src/components/FaApp.tsx
23971
- var import_react51 = require("react");
24478
+ var import_react54 = require("react");
23972
24479
  var import_jsx_runtime49 = require("react/jsx-runtime");
23973
24480
  function FaApp({
23974
24481
  onGenerateImage,
@@ -23984,15 +24491,15 @@ function FaApp({
23984
24491
  serverBaseUrl,
23985
24492
  buildInfo
23986
24493
  }) {
23987
- const [hfNamespace, setHfNamespace] = (0, import_react51.useState)(void 0);
23988
- (0, import_react51.useEffect)(() => {
24494
+ const [hfNamespace, setHfNamespace] = (0, import_react54.useState)(void 0);
24495
+ (0, import_react54.useEffect)(() => {
23989
24496
  if (!serverBaseUrl) return;
23990
24497
  fetch(`${serverBaseUrl}/api/status`).then((r) => r.json()).then((d) => {
23991
24498
  if (typeof d.hfNamespace === "string") setHfNamespace(d.hfNamespace);
23992
24499
  }).catch(() => {
23993
24500
  });
23994
24501
  }, [serverBaseUrl]);
23995
- const wrappedPrompt = (0, import_react51.useMemo)(
24502
+ const wrappedPrompt = (0, import_react54.useMemo)(
23996
24503
  () => alsFlowTextZugang(onGeneratePrompt),
23997
24504
  [onGeneratePrompt]
23998
24505
  );
@@ -24246,15 +24753,15 @@ function createInMemoryWorkspacePersistence(now = () => Date.now()) {
24246
24753
  }
24247
24754
 
24248
24755
  // node_modules/zustand/esm/react.mjs
24249
- var import_react52 = __toESM(require("react"), 1);
24756
+ var import_react55 = __toESM(require("react"), 1);
24250
24757
  var identity = (arg) => arg;
24251
24758
  function useStore(api, selector = identity) {
24252
- const slice = import_react52.default.useSyncExternalStore(
24759
+ const slice = import_react55.default.useSyncExternalStore(
24253
24760
  api.subscribe,
24254
- import_react52.default.useCallback(() => selector(api.getState()), [api, selector]),
24255
- import_react52.default.useCallback(() => selector(api.getInitialState()), [api, selector])
24761
+ import_react55.default.useCallback(() => selector(api.getState()), [api, selector]),
24762
+ import_react55.default.useCallback(() => selector(api.getInitialState()), [api, selector])
24256
24763
  );
24257
- import_react52.default.useDebugValue(slice);
24764
+ import_react55.default.useDebugValue(slice);
24258
24765
  return slice;
24259
24766
  }
24260
24767