@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/{chunk-HUGUH4VB.mjs → chunk-2ADFDRFX.mjs} +1325 -804
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1114 -607
- package/dist/index.mjs +3 -3
- package/dist/szenen.d.mts +364 -8
- package/dist/szenen.d.ts +364 -8
- package/dist/szenen.js +1067 -529
- package/dist/szenen.mjs +30 -2
- package/package.json +1 -1
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 = "
|
|
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 = (
|
|
256
|
+
const sanitize = (str3) => (str3 || "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
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
|
|
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
|
|
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
|
|
9142
|
-
"
|
|
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
|
|
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: "
|
|
9150
|
-
title: "
|
|
9151
|
-
description: "
|
|
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: "
|
|
9160
|
-
title: "
|
|
9161
|
-
description: "
|
|
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: "
|
|
9165
|
-
title: "
|
|
9166
|
-
description: "
|
|
9182
|
+
id: "elements",
|
|
9183
|
+
title: "Bausteine",
|
|
9184
|
+
description: "Figuren, Orte, Stile, Prompt-Bausteine und Prompt-Regeln pflegen."
|
|
9167
9185
|
},
|
|
9168
9186
|
{
|
|
9169
|
-
id: "
|
|
9170
|
-
title: "
|
|
9171
|
-
description: "
|
|
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 === "
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
13647
|
-
(0,
|
|
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,
|
|
13843
|
+
const [nachladen, setNachladen] = (0, import_react41.useState)(0);
|
|
13704
13844
|
const neuLesen = () => setNachladen((n) => n + 1);
|
|
13705
|
-
const [erzeugenOffen, setErzeugenOffen] = (0,
|
|
13706
|
-
const [zuordnenOffen, setZuordnenOffen] = (0,
|
|
13707
|
-
const [chatAuftraegeOffen, setChatAuftraegeOffen] = (0,
|
|
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,
|
|
14022
|
-
const gehalten = (0,
|
|
14023
|
-
const gescheitert = (0,
|
|
14024
|
-
const [versuch, setVersuch] = (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,
|
|
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)(
|
|
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,
|
|
14192
|
-
const [anzahlText, setAnzahlText] = (0,
|
|
14193
|
-
const [seitenverhaeltnis, setSeitenverhaeltnis] = (0,
|
|
14194
|
-
const [laeuft, setLaeuft] = (0,
|
|
14195
|
-
const [notiz, setNotiz] = (0,
|
|
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,
|
|
14373
|
-
const [seeds, setSeeds] = (0,
|
|
14374
|
-
const [zip, setZip] = (0,
|
|
14375
|
-
const [zipLaedt, setZipLaedt] = (0,
|
|
14376
|
-
const [seedListe, setSeedListe] = (0,
|
|
14377
|
-
const [vorlagen, setVorlagen] = (0,
|
|
14378
|
-
const [vorlageKey, setVorlageKey] = (0,
|
|
14379
|
-
const [vorlagenStand, setVorlagenStand] = (0,
|
|
14380
|
-
const [vorlageBenennen, setVorlageBenennen] = (0,
|
|
14381
|
-
const [vorlageName, setVorlageName] = (0,
|
|
14382
|
-
const [modell, setModell] = (0,
|
|
14383
|
-
const [itemId, setItemId] = (0,
|
|
14384
|
-
const [laeuft, setLaeuft] = (0,
|
|
14385
|
-
const [notiz, setNotiz] = (0,
|
|
14386
|
-
const auswahlMarke = (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,
|
|
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,
|
|
14849
|
-
const [notiz, setNotiz] = (0,
|
|
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,
|
|
14929
|
-
const [laeuft, setLaeuft] = (0,
|
|
14930
|
-
const [notiz, setNotiz] = (0,
|
|
14931
|
-
const [nachladen, setNachladen] = (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,
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
15063
|
-
const [laeuft, setLaeuft] = (0,
|
|
15064
|
-
const [nachladen, setNachladen] = (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.
|
|
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,
|
|
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,
|
|
15225
|
-
const [notiz, setNotiz] = (0,
|
|
15226
|
-
const [laeuft, setLaeuft] = (0,
|
|
15227
|
-
const [wahl, setWahl] = (0,
|
|
15228
|
-
const [basiswechsel, setBasiswechsel] = (0,
|
|
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,
|
|
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,
|
|
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,
|
|
15385
|
+
const felder = (0, import_react43.useMemo)(
|
|
15247
15386
|
() => schnappschuss ? fieldsForType(schnappschuss.element.type) : [],
|
|
15248
15387
|
[schnappschuss?.element.type]
|
|
15249
15388
|
);
|
|
15250
|
-
(0,
|
|
15389
|
+
(0, import_react43.useEffect)(() => {
|
|
15251
15390
|
setWahl(null);
|
|
15252
15391
|
setBasiswechsel(null);
|
|
15253
15392
|
setNotiz(null);
|
|
15254
15393
|
}, [elementId]);
|
|
15255
|
-
const automatisch = (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
15721
|
-
(0,
|
|
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,
|
|
15749
|
-
const [zeigeStillgelegte, setZeigeStillgelegte] = (0,
|
|
15750
|
-
const [gewaehlt, setGewaehlt] = (0,
|
|
15751
|
-
const [neuerTitel, setNeuerTitel] = (0,
|
|
15752
|
-
const [neuerTyp, setNeuerTyp] = (0,
|
|
15753
|
-
const [notiz, setNotiz] = (0,
|
|
15754
|
-
const [anlegend, setAnlegend] = (0,
|
|
15755
|
-
const [anlageOffen, setAnlageOffen] = (0,
|
|
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,
|
|
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
|
|
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
|
|
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,
|
|
17016
|
-
/* @__PURE__ */ (0,
|
|
17017
|
-
d.proposal_type && /* @__PURE__ */ (0,
|
|
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,
|
|
16982
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("strong", { children: d.proposal_type })
|
|
17020
16983
|
] }),
|
|
17021
|
-
d.pointer && /* @__PURE__ */ (0,
|
|
16984
|
+
d.pointer && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
|
|
17022
16985
|
" \xB7 ",
|
|
17023
|
-
/* @__PURE__ */ (0,
|
|
16986
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("code", { children: d.pointer })
|
|
17024
16987
|
] }),
|
|
17025
|
-
typeof d.line === "number" && /* @__PURE__ */ (0,
|
|
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,
|
|
16994
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { children: d.message })
|
|
17032
16995
|
] }, i)) });
|
|
17033
16996
|
};
|
|
17034
|
-
var Ausbeute = ({ bewertung }) => /* @__PURE__ */ (0,
|
|
17035
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17041
|
-
/* @__PURE__ */ (0,
|
|
17042
|
-
/* @__PURE__ */ (0,
|
|
17043
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17047
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17054
|
-
/* @__PURE__ */ (0,
|
|
17055
|
-
/* @__PURE__ */ (0,
|
|
17056
|
-
/* @__PURE__ */ (0,
|
|
17057
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17103
|
-
/* @__PURE__ */ (0,
|
|
17104
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
17110
|
-
/* @__PURE__ */ (0,
|
|
17111
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17123
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
17138
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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,
|
|
17174
|
-
bewertung && /* @__PURE__ */ (0,
|
|
17175
|
-
bewertung && bewertung.diagnostics.length > 0 && /* @__PURE__ */ (0,
|
|
17176
|
-
/* @__PURE__ */ (0,
|
|
17177
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17142
|
+
gesichert && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("p", { "data-testid": "rettung-gesichert", children: [
|
|
17180
17143
|
"Gespeichert als Arbeitsfassung ",
|
|
17181
|
-
/* @__PURE__ */ (0,
|
|
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
|
|
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,
|
|
17217
|
-
/* @__PURE__ */ (0,
|
|
17218
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17183
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("strong", { children: "ohne" }),
|
|
17221
17184
|
" vorher etwas zu ver\xF6ffentlichen."
|
|
17222
17185
|
] }),
|
|
17223
|
-
!graph && /* @__PURE__ */ (0,
|
|
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,
|
|
17234
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17199
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("code", { children: graph.chainId }),
|
|
17237
17200
|
" angelegt. Kein Knoten ist ver\xF6ffentlicht."
|
|
17238
17201
|
] }),
|
|
17239
|
-
/* @__PURE__ */ (0,
|
|
17240
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17205
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("code", { children: k.elementId }),
|
|
17243
17206
|
", Entwurf ",
|
|
17244
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17250
|
-
/* @__PURE__ */ (0,
|
|
17251
|
-
/* @__PURE__ */ (0,
|
|
17252
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17259
|
-
/* @__PURE__ */ (0,
|
|
17260
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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
|
|
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,
|
|
17292
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17299
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17271
|
+
kopiert && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { "data-testid": `kopiert-${testId}`, children: "Kopiert." })
|
|
17309
17272
|
] }),
|
|
17310
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17325
|
-
lauf.raw_output ? /* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
17360
|
-
/* @__PURE__ */ (0,
|
|
17361
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17469
|
-
/* @__PURE__ */ (0,
|
|
17470
|
-
/* @__PURE__ */ (0,
|
|
17471
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17486
|
-
items.status === "error" && /* @__PURE__ */ (0,
|
|
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,
|
|
17491
|
-
werke.length > 0 && /* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
17538
|
-
/* @__PURE__ */ (0,
|
|
17539
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
17583
|
-
/* @__PURE__ */ (0,
|
|
17584
|
-
/* @__PURE__ */ (0,
|
|
17585
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17696
|
-
/* @__PURE__ */ (0,
|
|
17697
|
-
/* @__PURE__ */ (0,
|
|
17698
|
-
/* @__PURE__ */ (0,
|
|
17699
|
-
/* @__PURE__ */ (0,
|
|
17700
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17711
|
-
/* @__PURE__ */ (0,
|
|
17712
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17724
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17765
|
-
/* @__PURE__ */ (0,
|
|
17766
|
-
regeln.status === "loading" && /* @__PURE__ */ (0,
|
|
17767
|
-
regeln.status === "error" && /* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
17774
|
-
') ver\xF6ffentlicht. Ohne sie ist kein Lauf reproduzierbar \u2014 legen Sie
|
|
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,
|
|
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,
|
|
17786
|
-
/* @__PURE__ */ (0,
|
|
17787
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17795
|
-
|
|
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,
|
|
17806
|
-
/* @__PURE__ */ (0,
|
|
17807
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
17818
|
-
/* @__PURE__ */ (0,
|
|
17819
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17830
|
-
port.imageLibrary && /* @__PURE__ */ (0,
|
|
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,
|
|
17840
|
-
/* @__PURE__ */ (0,
|
|
17841
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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,
|
|
17859
|
-
lauf && lauf.status === "failed" && /* @__PURE__ */ (0,
|
|
17860
|
-
/* @__PURE__ */ (0,
|
|
17861
|
-
/* @__PURE__ */ (0,
|
|
17862
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17867
|
-
/* @__PURE__ */ (0,
|
|
17868
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17871
|
-
/* @__PURE__ */ (0,
|
|
17872
|
-
/* @__PURE__ */ (0,
|
|
17873
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17877
|
-
/* @__PURE__ */ (0,
|
|
17878
|
-
/* @__PURE__ */ (0,
|
|
17879
|
-
/* @__PURE__ */ (0,
|
|
17880
|
-
/* @__PURE__ */ (0,
|
|
17881
|
-
lauf.conflicts.length > 0 && /* @__PURE__ */ (0,
|
|
17882
|
-
/* @__PURE__ */ (0,
|
|
17883
|
-
/* @__PURE__ */ (0,
|
|
17884
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17893
|
-
/* @__PURE__ */ (0,
|
|
17894
|
-
/* @__PURE__ */ (0,
|
|
17895
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17901
|
-
/* @__PURE__ */ (0,
|
|
17902
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
17914
|
-
!fertig && /* @__PURE__ */ (0,
|
|
17915
|
-
/* @__PURE__ */ (0,
|
|
17916
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
17931
|
-
bezug && !fertig && /* @__PURE__ */ (0,
|
|
17932
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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,
|
|
17926
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("p", { "data-testid": `referenz-fehlt-${vorschlag.type}`, children: [
|
|
17955
17927
|
"Diese ",
|
|
17956
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
17937
|
+
fertig ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("p", { className: "szenen-proposal__done", children: [
|
|
17966
17938
|
"Als Entwurf \xFCbernommen: ",
|
|
17967
|
-
/* @__PURE__ */ (0,
|
|
17939
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("code", { children: fertig }),
|
|
17968
17940
|
". Er ist noch nicht ver\xF6ffentlicht."
|
|
17969
|
-
] }) : /* @__PURE__ */ (0,
|
|
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,
|
|
17985
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
18000
|
-
const faehig = (0,
|
|
18001
|
-
const [aktiv, setAktiv] = (0,
|
|
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:
|
|
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 === "
|
|
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 === "
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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) =>
|
|
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(
|
|
20746
|
-
|
|
20747
|
-
|
|
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(
|
|
20760
|
-
|
|
20761
|
-
|
|
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,
|
|
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,
|
|
22331
|
-
const [layoutChoice, setLayoutChoice] = (0,
|
|
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,
|
|
22339
|
-
const [hfTokenInput, setHfTokenInput] = (0,
|
|
22340
|
-
const [hfNamespaceLocal, setHfNamespaceLocal] = (0,
|
|
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,
|
|
22348
|
-
const [serverProbeFailed, setServerProbeFailed] = (0,
|
|
22349
|
-
(0,
|
|
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,
|
|
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,
|
|
22368
|
-
const szenenPort = (0,
|
|
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,
|
|
22391
|
-
const [bootstrapLog, setBootstrapLog] = (0,
|
|
22392
|
-
const [isBootstrapping, setIsBootstrapping] = (0,
|
|
22393
|
-
const [hfMissingImages, setHfMissingImages] = (0,
|
|
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,
|
|
22453
|
-
const [edges, setEdges] = (0,
|
|
22454
|
-
const [history, setHistory] = (0,
|
|
22455
|
-
const [galleryItems, setGalleryItems] = (0,
|
|
22456
|
-
const galleryItemsRef = (0,
|
|
22457
|
-
(0,
|
|
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,
|
|
22461
|
-
const [galleryVisibleCount, setGalleryVisibleCount] = (0,
|
|
22462
|
-
const [historyVisibleCount, setHistoryVisibleCount] = (0,
|
|
22463
|
-
const loadThumbnailsForEntries = (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
22522
|
-
const [isSynthesizing, setIsSynthesizing] = (0,
|
|
22523
|
-
const [activeGenerationsCount, setActiveGenerationsCount] = (0,
|
|
22524
|
-
const [currentResult, setCurrentResult] = (0,
|
|
22525
|
-
const [focusedNodeId, setFocusedNodeId] = (0,
|
|
22526
|
-
const [leftTab, setLeftTab] = (0,
|
|
22527
|
-
const [promptFeedback, setPromptFeedback] = (0,
|
|
22528
|
-
const [lastPromptPayload, setLastPromptPayload] = (0,
|
|
22529
|
-
const [isPromptTabGenerating, setIsPromptTabGenerating] = (0,
|
|
22530
|
-
const [activeTab, setActiveTab] = (0,
|
|
22531
|
-
const [mobileTab, setMobileTab] = (0,
|
|
22532
|
-
const [middlePanel, setMiddlePanel] = (0,
|
|
22533
|
-
const [recentLabItems, setRecentLabItems] = (0,
|
|
22534
|
-
const [aspectRatio, setAspectRatio] = (0,
|
|
22535
|
-
const [selectedModel, setSelectedModel] = (0,
|
|
22536
|
-
const [seed, setSeed] = (0,
|
|
22537
|
-
const [seedMode, setSeedMode] = (0,
|
|
22538
|
-
const [imageCount, setImageCount] = (0,
|
|
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,
|
|
22561
|
-
const [isRightCollapsed, setIsRightCollapsed] = (0,
|
|
22562
|
-
const [leftPanelWidth, setLeftPanelWidth] = (0,
|
|
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,
|
|
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,
|
|
22577
|
-
const [workspaceTags, setWorkspaceTags] = (0,
|
|
22578
|
-
const [highContrast, setHighContrast] = (0,
|
|
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,
|
|
22586
|
-
const [activeReferenceThumbnail, setActiveReferenceThumbnail] = (0,
|
|
22587
|
-
const [isScanningImage, setIsScanningImage] = (0,
|
|
22588
|
-
const [touchStartX, setTouchStartX] = (0,
|
|
22589
|
-
const [isFullscreen, setIsFullscreen] = (0,
|
|
22590
|
-
const [zoomScale, setZoomScale] = (0,
|
|
22591
|
-
const [zoomOffset, setZoomOffset] = (0,
|
|
22592
|
-
const lastPinchDist = (0,
|
|
22593
|
-
const lastTapTime = (0,
|
|
22594
|
-
const dragStart = (0,
|
|
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,
|
|
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,
|
|
22738
|
-
const goToPrev = (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
23988
|
-
(0,
|
|
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,
|
|
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
|
|
24756
|
+
var import_react55 = __toESM(require("react"), 1);
|
|
24250
24757
|
var identity = (arg) => arg;
|
|
24251
24758
|
function useStore(api, selector = identity) {
|
|
24252
|
-
const slice =
|
|
24759
|
+
const slice = import_react55.default.useSyncExternalStore(
|
|
24253
24760
|
api.subscribe,
|
|
24254
|
-
|
|
24255
|
-
|
|
24761
|
+
import_react55.default.useCallback(() => selector(api.getState()), [api, selector]),
|
|
24762
|
+
import_react55.default.useCallback(() => selector(api.getInitialState()), [api, selector])
|
|
24256
24763
|
);
|
|
24257
|
-
|
|
24764
|
+
import_react55.default.useDebugValue(slice);
|
|
24258
24765
|
return slice;
|
|
24259
24766
|
}
|
|
24260
24767
|
|