@rslsp1/fa-app-tools 7.2.0 → 8.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-5QA6DTJ6.mjs → chunk-VVGASAYT.mjs} +1620 -733
- package/dist/index.d.mts +26 -4
- package/dist/index.d.ts +26 -4
- package/dist/index.js +1487 -660
- package/dist/index.mjs +3 -3
- package/dist/szenen.d.mts +661 -9
- package/dist/szenen.d.ts +661 -9
- package/dist/szenen.js +1619 -578
- package/dist/szenen.mjs +163 -2
- package/package.json +6 -2
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 = "8.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 = () => {
|
|
@@ -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");
|
|
@@ -2495,9 +2495,9 @@ function mergeSnapshotAndPending(snapshot, pending, bereitsImSnapshot = 0) {
|
|
|
2495
2495
|
}
|
|
2496
2496
|
|
|
2497
2497
|
// src/lib/hfEventLog.ts
|
|
2498
|
-
function treeUrl(namespace, subdir = "") {
|
|
2498
|
+
function treeUrl(namespace, subdir = "", revision = "main") {
|
|
2499
2499
|
const parts = [namespace.replace(/\/$/, ""), subdir].filter(Boolean).join("/");
|
|
2500
|
-
return `${HF_BASE}/api/datasets/${HF_REPO}/tree
|
|
2500
|
+
return `${HF_BASE}/api/datasets/${HF_REPO}/tree/${encodeURIComponent(revision)}${parts ? "/" + parts : ""}`;
|
|
2501
2501
|
}
|
|
2502
2502
|
var DROSSEL_VERSUCHE = 4;
|
|
2503
2503
|
var DROSSEL_BASIS_MS = 400;
|
|
@@ -2530,8 +2530,8 @@ async function mitGeduld(anfrage) {
|
|
|
2530
2530
|
}
|
|
2531
2531
|
return letzte;
|
|
2532
2532
|
}
|
|
2533
|
-
async function hfListDir(namespace, subdir, token) {
|
|
2534
|
-
const res = await mitGeduld(() => holeMitZeitlimit(treeUrl(namespace, subdir), {
|
|
2533
|
+
async function hfListDir(namespace, subdir, token, revision = "main") {
|
|
2534
|
+
const res = await mitGeduld(() => holeMitZeitlimit(treeUrl(namespace, subdir, revision), {
|
|
2535
2535
|
headers: { Authorization: `Bearer ${token}` }
|
|
2536
2536
|
}));
|
|
2537
2537
|
if (res.status === 404) return [];
|
|
@@ -2560,17 +2560,46 @@ function toBase64(text) {
|
|
|
2560
2560
|
bytes.forEach((b) => binary += String.fromCharCode(b));
|
|
2561
2561
|
return btoa(binary);
|
|
2562
2562
|
}
|
|
2563
|
-
|
|
2563
|
+
var HfPreconditionFailedError = class extends Error {
|
|
2564
|
+
constructor(message) {
|
|
2565
|
+
super(message);
|
|
2566
|
+
this.name = "HfPreconditionFailed";
|
|
2567
|
+
}
|
|
2568
|
+
};
|
|
2569
|
+
async function hfRepoHead(token, revision = "main") {
|
|
2570
|
+
const res = await mitGeduld(() => holeMitZeitlimit(
|
|
2571
|
+
`${HF_BASE}/api/datasets/${HF_REPO}/revision/${encodeURIComponent(revision)}`,
|
|
2572
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
2573
|
+
));
|
|
2574
|
+
if (!res.ok) {
|
|
2575
|
+
throw new Error(`HF revision failed: ${res.status} ${res.statusText} (${revision})`);
|
|
2576
|
+
}
|
|
2577
|
+
const daten = await res.json();
|
|
2578
|
+
if (typeof daten.sha !== "string" || daten.sha.length === 0) {
|
|
2579
|
+
throw new Error(
|
|
2580
|
+
`HF revision ${revision}: die Antwort traegt kein "sha". Erhaltene Felder: ${Object.keys(daten || {}).join(", ") || "(keine)"}.`
|
|
2581
|
+
);
|
|
2582
|
+
}
|
|
2583
|
+
return daten.sha;
|
|
2584
|
+
}
|
|
2585
|
+
async function hfUploadSmallFile(repoPath, content, token, summary = `Update ${repoPath}`, options) {
|
|
2586
|
+
const kopf = { summary, description: "" };
|
|
2587
|
+
if (options?.parentCommit) kopf.parentCommit = options.parentCommit;
|
|
2564
2588
|
const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
|
|
2565
2589
|
method: "POST",
|
|
2566
2590
|
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
|
|
2567
2591
|
body: [
|
|
2568
|
-
JSON.stringify({ key: "header", value:
|
|
2592
|
+
JSON.stringify({ key: "header", value: kopf }),
|
|
2569
2593
|
JSON.stringify({ key: "file", value: { path: repoPath, encoding: "base64", content: toBase64(content) } })
|
|
2570
2594
|
].join("\n")
|
|
2571
2595
|
});
|
|
2572
2596
|
if (!res.ok) {
|
|
2573
2597
|
const err = await res.text().catch(() => "");
|
|
2598
|
+
if (options?.parentCommit && (res.status === 412 || res.status === 409)) {
|
|
2599
|
+
throw new HfPreconditionFailedError(
|
|
2600
|
+
`HF commit abgelehnt: Der Branch hat sich seit ${options.parentCommit} bewegt (${res.status}). Es wurde nichts geschrieben.`
|
|
2601
|
+
);
|
|
2602
|
+
}
|
|
2574
2603
|
throw new Error(`HF upload failed: ${res.status} \u2014 ${err.slice(0, 200)}`);
|
|
2575
2604
|
}
|
|
2576
2605
|
}
|
|
@@ -2620,8 +2649,8 @@ async function mitBegrenzterParallelitaet(count, limit, task) {
|
|
|
2620
2649
|
await Promise.all(Array.from({ length: Math.min(limit, count) }, worker));
|
|
2621
2650
|
if (fehlerAufgetreten) throw ersterFehler;
|
|
2622
2651
|
}
|
|
2623
|
-
async function loadPendingEvents(namespace, token, sinceTs, knownPaths) {
|
|
2624
|
-
const files = await hfListDir(namespace, "events", token);
|
|
2652
|
+
async function loadPendingEvents(namespace, token, sinceTs, knownPaths, revision = "main") {
|
|
2653
|
+
const files = await hfListDir(namespace, "events", token, revision);
|
|
2625
2654
|
const pending = files.filter((f) => f.type === "file" && tsFromEventPath(f.path) > sinceTs && !knownPaths?.has(f.path)).sort((a, b) => a.path.localeCompare(b.path));
|
|
2626
2655
|
if (!pending.length) return [];
|
|
2627
2656
|
const raw = new Array(pending.length);
|
|
@@ -2666,7 +2695,13 @@ async function writeHFEvent(namespace, token, type, payload, prevTs, options) {
|
|
|
2666
2695
|
const suffix = options?.ordered ? `${String(++writeSeq % 1e6).padStart(6, "0")}${uuid}` : uuid;
|
|
2667
2696
|
const filename = `${tsToFilename(ts)}_${suffix}.json`;
|
|
2668
2697
|
const repoPath = `${namespace}events/${filename}`;
|
|
2669
|
-
await hfUploadSmallFile(
|
|
2698
|
+
await hfUploadSmallFile(
|
|
2699
|
+
repoPath,
|
|
2700
|
+
JSON.stringify(event, null, 2),
|
|
2701
|
+
token,
|
|
2702
|
+
`Event: ${type}`,
|
|
2703
|
+
options?.parentCommit ? { parentCommit: options.parentCommit } : void 0
|
|
2704
|
+
);
|
|
2670
2705
|
return event;
|
|
2671
2706
|
}
|
|
2672
2707
|
|
|
@@ -6004,7 +6039,7 @@ function FlowWorkspacePicker({
|
|
|
6004
6039
|
}
|
|
6005
6040
|
|
|
6006
6041
|
// src/features/szenen/SzenenView.tsx
|
|
6007
|
-
var
|
|
6042
|
+
var import_react52 = require("react");
|
|
6008
6043
|
|
|
6009
6044
|
// src/lib/szenen/viewPort.ts
|
|
6010
6045
|
function capabilitiesOf(port) {
|
|
@@ -6018,7 +6053,11 @@ function capabilitiesOf(port) {
|
|
|
6018
6053
|
imageBatchHistory: typeof port.imageBatchHistory?.listBatchesForPromptRun === "function",
|
|
6019
6054
|
imageOrderHistory: typeof port.imageBatchHistory?.listOrdersForPromptRun === "function",
|
|
6020
6055
|
imageBatchWrite: typeof port.imageBatchWrite?.importBatch === "function",
|
|
6021
|
-
imageGeneration: typeof port.imageGeneration?.startFromPromptRun === "function"
|
|
6056
|
+
imageGeneration: typeof port.imageGeneration?.startFromPromptRun === "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"
|
|
6022
6061
|
};
|
|
6023
6062
|
}
|
|
6024
6063
|
async function callOptional(capability, operation) {
|
|
@@ -9102,37 +9141,52 @@ function formatSchemaErrors(errors) {
|
|
|
9102
9141
|
}
|
|
9103
9142
|
|
|
9104
9143
|
// src/features/szenen/szenenViewModel.ts
|
|
9105
|
-
var
|
|
9106
|
-
"
|
|
9144
|
+
var BAUSTEIN_TYPES = [
|
|
9145
|
+
"scene_component",
|
|
9146
|
+
"style",
|
|
9147
|
+
"shot_concept",
|
|
9148
|
+
"prompt_fragment",
|
|
9149
|
+
"prompt_policy"
|
|
9150
|
+
];
|
|
9151
|
+
var ERWEITERT_REIHENFOLGE = [
|
|
9107
9152
|
"scene_setup",
|
|
9108
|
-
"shot"
|
|
9153
|
+
"shot",
|
|
9154
|
+
"camera_preset",
|
|
9155
|
+
"camera_pattern",
|
|
9156
|
+
"target_model",
|
|
9157
|
+
"scene_derivation_policy",
|
|
9158
|
+
"sequence"
|
|
9109
9159
|
];
|
|
9110
|
-
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
|
+
})();
|
|
9111
9170
|
var ALLE_BEREICHE = [
|
|
9112
9171
|
{
|
|
9113
|
-
id: "
|
|
9114
|
-
title: "
|
|
9115
|
-
description: "
|
|
9116
|
-
},
|
|
9117
|
-
{
|
|
9118
|
-
id: "elements",
|
|
9119
|
-
title: "1 \xB7 Bausteine",
|
|
9120
|
-
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."
|
|
9121
9175
|
},
|
|
9122
9176
|
{
|
|
9123
|
-
id: "
|
|
9124
|
-
title: "
|
|
9125
|
-
description: "
|
|
9177
|
+
id: "prompts",
|
|
9178
|
+
title: "Prompts",
|
|
9179
|
+
description: "Alle erzeugten Prompts mit ihren Bildern \u2014 unabhaengig davon, ueber welchen Weg sie entstanden sind."
|
|
9126
9180
|
},
|
|
9127
9181
|
{
|
|
9128
|
-
id: "
|
|
9129
|
-
title: "
|
|
9130
|
-
description: "
|
|
9182
|
+
id: "elements",
|
|
9183
|
+
title: "Bausteine",
|
|
9184
|
+
description: "Figuren, Orte, Stile, Prompt-Bausteine und Prompt-Regeln pflegen."
|
|
9131
9185
|
},
|
|
9132
9186
|
{
|
|
9133
|
-
id: "
|
|
9134
|
-
title: "
|
|
9135
|
-
description: "
|
|
9187
|
+
id: "advanced",
|
|
9188
|
+
title: "Erweitert",
|
|
9189
|
+
description: "Optionale Pr\xE4zisierung: Bildaufbau, Shot, Kameras, Zielmodelle, Regeln, Sequenzen."
|
|
9136
9190
|
},
|
|
9137
9191
|
{
|
|
9138
9192
|
id: "media",
|
|
@@ -9142,16 +9196,12 @@ var ALLE_BEREICHE = [
|
|
|
9142
9196
|
];
|
|
9143
9197
|
function areasFor(port) {
|
|
9144
9198
|
const faehig = capabilitiesOf(port);
|
|
9145
|
-
return ALLE_BEREICHE.filter((bereich) =>
|
|
9146
|
-
if (bereich.id === "media") return faehig.media;
|
|
9147
|
-
if (bereich.id === "derive") return faehig.derivation;
|
|
9148
|
-
return true;
|
|
9149
|
-
});
|
|
9199
|
+
return ALLE_BEREICHE.filter((bereich) => bereich.id !== "media" || faehig.media);
|
|
9150
9200
|
}
|
|
9151
9201
|
function resolveArea(port, gewuenscht) {
|
|
9152
9202
|
const verfuegbar = areasFor(port);
|
|
9153
9203
|
if (gewuenscht && verfuegbar.some((b) => b.id === gewuenscht)) return gewuenscht;
|
|
9154
|
-
return verfuegbar.find((b) => b.id === "
|
|
9204
|
+
return verfuegbar.find((b) => b.id === "scene")?.id ?? verfuegbar[0].id;
|
|
9155
9205
|
}
|
|
9156
9206
|
function noticeFor(ergebnis, was) {
|
|
9157
9207
|
switch (ergebnis.kind) {
|
|
@@ -9165,13 +9215,13 @@ function noticeFor(ergebnis, was) {
|
|
|
9165
9215
|
};
|
|
9166
9216
|
}
|
|
9167
9217
|
case "rejected": {
|
|
9168
|
-
const
|
|
9218
|
+
const fehler5 = ergebnis.errors ?? [];
|
|
9169
9219
|
const ungeprueft = ergebnis.unchecked ?? [];
|
|
9170
9220
|
return {
|
|
9171
9221
|
tone: "rejected",
|
|
9172
|
-
text: `${was}: abgelehnt \u2014 ${
|
|
9222
|
+
text: `${was}: abgelehnt \u2014 ${fehler5.length} Befund${fehler5.length === 1 ? "" : "e"}.`,
|
|
9173
9223
|
details: [
|
|
9174
|
-
...
|
|
9224
|
+
...fehler5.map((f) => `${f.path ?? "(ohne Pfad)"}: ${f.message ?? "ohne Meldung"}`),
|
|
9175
9225
|
// Nicht verschweigen: was nicht geprüft wurde, ist nicht in Ordnung,
|
|
9176
9226
|
// sondern ungeprüft.
|
|
9177
9227
|
...ungeprueft.map((u) => `ungepr\xFCft: ${u}`)
|
|
@@ -9192,7 +9242,7 @@ function noticeFor(ergebnis, was) {
|
|
|
9192
9242
|
}
|
|
9193
9243
|
|
|
9194
9244
|
// src/features/szenen/areas/ElementsArea.tsx
|
|
9195
|
-
var
|
|
9245
|
+
var import_react45 = require("react");
|
|
9196
9246
|
|
|
9197
9247
|
// src/features/szenen/areas/usePortQuery.ts
|
|
9198
9248
|
var import_react33 = require("react");
|
|
@@ -9224,7 +9274,7 @@ function usePortQuery(frage, abhaengigkeiten) {
|
|
|
9224
9274
|
}
|
|
9225
9275
|
|
|
9226
9276
|
// src/features/szenen/areas/ElementDetail.tsx
|
|
9227
|
-
var
|
|
9277
|
+
var import_react43 = require("react");
|
|
9228
9278
|
|
|
9229
9279
|
// src/features/szenen/elementLabels.ts
|
|
9230
9280
|
var LABELS = {
|
|
@@ -12671,7 +12721,7 @@ var ERKLAERUNG = {
|
|
|
12671
12721
|
};
|
|
12672
12722
|
var CapabilityPanel = ({ port, elementId, setupPayload }) => {
|
|
12673
12723
|
const [bericht, setBericht] = (0, import_react37.useState)(null);
|
|
12674
|
-
const [
|
|
12724
|
+
const [fehler5, setFehler] = (0, import_react37.useState)(null);
|
|
12675
12725
|
(0, import_react37.useEffect)(() => {
|
|
12676
12726
|
let abgemeldet = false;
|
|
12677
12727
|
setBericht(null);
|
|
@@ -12693,10 +12743,10 @@ var CapabilityPanel = ({ port, elementId, setupPayload }) => {
|
|
|
12693
12743
|
abgemeldet = true;
|
|
12694
12744
|
};
|
|
12695
12745
|
}, [port, elementId, setupPayload]);
|
|
12696
|
-
if (
|
|
12746
|
+
if (fehler5) {
|
|
12697
12747
|
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("p", { "data-testid": "capability-fehler", role: "alert", children: [
|
|
12698
12748
|
"F\xE4higkeitsbericht nicht erstellbar: ",
|
|
12699
|
-
|
|
12749
|
+
fehler5
|
|
12700
12750
|
] });
|
|
12701
12751
|
}
|
|
12702
12752
|
if (!bericht) return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { "data-testid": "capability-laden", children: "F\xE4higkeitsbericht wird erstellt \u2026" });
|
|
@@ -12932,19 +12982,7 @@ var VersionNavigator = ({
|
|
|
12932
12982
|
};
|
|
12933
12983
|
|
|
12934
12984
|
// src/features/szenen/areas/RunsPanel.tsx
|
|
12935
|
-
var
|
|
12936
|
-
|
|
12937
|
-
// src/lib/szenen/itemDerivation.ts
|
|
12938
|
-
var SZENEN_ITEM_PREFIX = "szn-";
|
|
12939
|
-
function itemIdForRun(runId) {
|
|
12940
|
-
return `${SZENEN_ITEM_PREFIX}${runId}`;
|
|
12941
|
-
}
|
|
12942
|
-
function roundOfRun(run, runsOfShot) {
|
|
12943
|
-
return runsOfShot.findIndex((kandidat) => kandidat.run_id === run.run_id) + 1;
|
|
12944
|
-
}
|
|
12945
|
-
|
|
12946
|
-
// src/features/szenen/areas/RunImagesPanel.tsx
|
|
12947
|
-
var import_react40 = __toESM(require("react"));
|
|
12985
|
+
var import_react42 = require("react");
|
|
12948
12986
|
|
|
12949
12987
|
// src/lib/szenen/elementEvents.ts
|
|
12950
12988
|
var SZENEN_EVENT_PREFIX = "szenen.";
|
|
@@ -13063,7 +13101,60 @@ var SZENEN_EVENT_TYPES = {
|
|
|
13063
13101
|
* Der Elementreducer ignoriert auch diesen Typ (`default:`-Zweig); gelesen
|
|
13064
13102
|
* wird mit `collectImageBatches` (`imageBatchStore.ts`).
|
|
13065
13103
|
*/
|
|
13066
|
-
imageBatchPersisted: "szenen.image_batch_persisted"
|
|
13104
|
+
imageBatchPersisted: "szenen.image_batch_persisted",
|
|
13105
|
+
/**
|
|
13106
|
+
* Ein vorbereiteter Promptvorgang — der Durchstich „Szene → ChatGPT → Lauf".
|
|
13107
|
+
*
|
|
13108
|
+
* Der bestehende Promptpfad ruft das Modell selbst
|
|
13109
|
+
* (`runPromptDerivation()`); ein aufrufendes Chatmodell formuliert dagegen
|
|
13110
|
+
* **zwischen** zwei Werkzeugaufrufen. Damit zerfaellt der eine Vorgang in
|
|
13111
|
+
* zwei, und der zweite braucht genau den Stand, den der erste eingefroren
|
|
13112
|
+
* hat — sonst loeste der Abschluss die inzwischen geaenderte Szene neu auf
|
|
13113
|
+
* und der Lauf behauptete eine Herkunft, die er nicht hat (D-22).
|
|
13114
|
+
*
|
|
13115
|
+
* Deshalb ein eigenes Ereignis und **kein** fluechtiger Prozesszustand: Der
|
|
13116
|
+
* Snapshot ueberlebt Neustart, Hostwechsel und Sitzungsende, weil er im
|
|
13117
|
+
* selben autoritativen Log liegt wie der Lauf, der daraus entsteht.
|
|
13118
|
+
*
|
|
13119
|
+
* Der Elementreducer ignoriert diesen Typ (`default:`-Zweig); gelesen wird
|
|
13120
|
+
* mit `collectPromptOperations` (`scenePromptOperation.ts`).
|
|
13121
|
+
*/
|
|
13122
|
+
promptOperationPrepared: "szenen.prompt_operation_prepared",
|
|
13123
|
+
/**
|
|
13124
|
+
* Ein Bildauftrag wartet auf einen ausfuehrenden Flow-Host.
|
|
13125
|
+
*
|
|
13126
|
+
* Die vorhandene Auftragshistorie (`imageGenerationStarted`) beginnt
|
|
13127
|
+
* **unmittelbar vor** dem Provideraufruf und kennt deshalb keinen wartenden
|
|
13128
|
+
* Auftrag. Genau der entsteht hier: Ein Chatmodell beauftragt ueber MCP ein
|
|
13129
|
+
* Bild, der Auftrag wird dauerhaft abgelegt, und der Werkzeugaufruf endet
|
|
13130
|
+
* sofort mit seiner Kennung — er haelt keine Antwort minutenlang offen.
|
|
13131
|
+
*
|
|
13132
|
+
* Traegt **keinen** Prompt: Der steht im referenzierten Lauf, und ihn hier
|
|
13133
|
+
* zu kopieren waere eine zweite Wahrheit ueber denselben eingefrorenen Text
|
|
13134
|
+
* (D-103).
|
|
13135
|
+
*/
|
|
13136
|
+
imageJobRequested: "szenen.image_job_requested",
|
|
13137
|
+
/**
|
|
13138
|
+
* Ein Flow-Host beansprucht einen wartenden Bildauftrag.
|
|
13139
|
+
*
|
|
13140
|
+
* Ein append-only Log kennt kein Lock: Zwei Tabs koennen beide anhaengen.
|
|
13141
|
+
* Deshalb ist die Uebernahme **nicht** der Append, sondern das Ergebnis
|
|
13142
|
+
* einer deterministischen Schiedsregel darueber (`winningClaimOf`) — der
|
|
13143
|
+
* erste Anspruch in Logreihenfolge gewinnt, und jeder Host liest nach
|
|
13144
|
+
* seinem Append erneut, bevor er einen bezahlten Provideraufruf startet.
|
|
13145
|
+
* Ein unterlegener Anspruch bleibt sichtbar stehen; er wird nicht geloescht,
|
|
13146
|
+
* weil das Log das nicht kann und weil der Versuch ein Beleg ist.
|
|
13147
|
+
*/
|
|
13148
|
+
imageJobClaimed: "szenen.image_job_claimed",
|
|
13149
|
+
/**
|
|
13150
|
+
* Der Bildauftrag ist erledigt — mit dem Ausgang, den der Host gesehen hat.
|
|
13151
|
+
*
|
|
13152
|
+
* Getrennt von `imageGenerationFinished`: Jenes gehoert zum bezahlten
|
|
13153
|
+
* Provideraufruf, dieses zur Zustellung. Ein Auftrag kann fehlschlagen,
|
|
13154
|
+
* **bevor** ein Provider gerufen wurde (abgelehntes Profil), und dann gibt
|
|
13155
|
+
* es kein `imageGenerationFinished`, das den Wartenden erloesen wuerde.
|
|
13156
|
+
*/
|
|
13157
|
+
imageJobSettled: "szenen.image_job_settled"
|
|
13067
13158
|
};
|
|
13068
13159
|
function sortForReplay(events) {
|
|
13069
13160
|
return [...events].sort((a, b) => {
|
|
@@ -13324,6 +13415,144 @@ function collectPromptRuns(events) {
|
|
|
13324
13415
|
function findPromptRun(events, runId) {
|
|
13325
13416
|
return collectPromptRuns(events).find((run) => run.run_id === runId) ?? null;
|
|
13326
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"));
|
|
13327
13556
|
|
|
13328
13557
|
// src/lib/szenen/seedListe.ts
|
|
13329
13558
|
var DEFAULT_SEED_VORLAGE = {
|
|
@@ -13416,9 +13645,9 @@ async function leseBilderAusZip(datei) {
|
|
|
13416
13645
|
);
|
|
13417
13646
|
}
|
|
13418
13647
|
const { default: JSZip3 } = await import("jszip");
|
|
13419
|
-
const archiv = await JSZip3.loadAsync(datei).catch((
|
|
13648
|
+
const archiv = await JSZip3.loadAsync(datei).catch((fehler5) => {
|
|
13420
13649
|
throw new Error(
|
|
13421
|
-
`${name} konnte nicht als ZIP gelesen werden: ${
|
|
13650
|
+
`${name} konnte nicht als ZIP gelesen werden: ${fehler5 instanceof Error ? fehler5.message : String(fehler5)}`
|
|
13422
13651
|
);
|
|
13423
13652
|
});
|
|
13424
13653
|
const pfade = [];
|
|
@@ -13450,7 +13679,7 @@ async function leseBilderAusZip(datei) {
|
|
|
13450
13679
|
}
|
|
13451
13680
|
|
|
13452
13681
|
// src/features/szenen/areas/Bildbausteine.tsx
|
|
13453
|
-
var
|
|
13682
|
+
var import_react40 = require("react");
|
|
13454
13683
|
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
13455
13684
|
var Kachel = ({
|
|
13456
13685
|
quelle,
|
|
@@ -13467,11 +13696,11 @@ var BildViewer = ({
|
|
|
13467
13696
|
}) => {
|
|
13468
13697
|
const anzahl = bilder.length;
|
|
13469
13698
|
const bild = bilder[position];
|
|
13470
|
-
const blaettern = (0,
|
|
13699
|
+
const blaettern = (0, import_react40.useCallback)((schritt) => {
|
|
13471
13700
|
if (anzahl === 0) return;
|
|
13472
13701
|
onWechseln((position + schritt + anzahl) % anzahl);
|
|
13473
13702
|
}, [anzahl, position, onWechseln]);
|
|
13474
|
-
(0,
|
|
13703
|
+
(0, import_react40.useEffect)(() => {
|
|
13475
13704
|
const taste = (e) => {
|
|
13476
13705
|
if (e.key === "Escape") onSchliessen();
|
|
13477
13706
|
else if (e.key === "ArrowLeft") blaettern(-1);
|
|
@@ -13554,8 +13783,8 @@ var BildViewer = ({
|
|
|
13554
13783
|
);
|
|
13555
13784
|
};
|
|
13556
13785
|
var BildGalerie = ({ bilder, testId, satzTitel }) => {
|
|
13557
|
-
const [offen, setOffen] = (0,
|
|
13558
|
-
(0,
|
|
13786
|
+
const [offen, setOffen] = (0, import_react40.useState)(null);
|
|
13787
|
+
(0, import_react40.useEffect)(() => {
|
|
13559
13788
|
setOffen((vorher) => vorher !== null && vorher >= bilder.length ? null : vorher);
|
|
13560
13789
|
}, [bilder.length]);
|
|
13561
13790
|
if (bilder.length === 0) return null;
|
|
@@ -13611,10 +13840,11 @@ var AUFTRAG_TEXT = {
|
|
|
13611
13840
|
};
|
|
13612
13841
|
var RunImagesPanel = ({ port, lauf }) => {
|
|
13613
13842
|
const faehig = capabilitiesOf(port);
|
|
13614
|
-
const [nachladen, setNachladen] = (0,
|
|
13843
|
+
const [nachladen, setNachladen] = (0, import_react41.useState)(0);
|
|
13615
13844
|
const neuLesen = () => setNachladen((n) => n + 1);
|
|
13616
|
-
const [erzeugenOffen, setErzeugenOffen] = (0,
|
|
13617
|
-
const [zuordnenOffen, setZuordnenOffen] = (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);
|
|
13618
13848
|
const quelle = sourceOf(lauf);
|
|
13619
13849
|
const [wahlPhase] = usePortQuery(
|
|
13620
13850
|
() => port.imageGeneration ? port.imageGeneration.imageModelChoiceForRun(lauf.run_id) : Promise.resolve({ kind: "ok", value: null }),
|
|
@@ -13629,10 +13859,17 @@ var RunImagesPanel = ({ port, lauf }) => {
|
|
|
13629
13859
|
() => port.imageBatchHistory ? port.imageBatchHistory.listBatchesForPromptRun(lauf.run_id) : Promise.resolve({ kind: "ok", value: [] }),
|
|
13630
13860
|
[port, lauf.run_id, nachladen]
|
|
13631
13861
|
);
|
|
13862
|
+
const [chatAuftraegePhase] = usePortQuery(
|
|
13863
|
+
() => port.imageJobs ? port.imageJobs.listJobs(lauf.run_id) : Promise.resolve({ kind: "ok", value: [] }),
|
|
13864
|
+
[port, lauf.run_id, nachladen]
|
|
13865
|
+
);
|
|
13632
13866
|
const auftraege = auftraegePhase.status === "ready" ? auftraegePhase.value : [];
|
|
13633
13867
|
const batches = batchesPhase.status === "ready" ? batchesPhase.value : [];
|
|
13868
|
+
const chatAuftraege = chatAuftraegePhase.status === "ready" ? chatAuftraegePhase.value : [];
|
|
13869
|
+
const offeneChatAuftraege = chatAuftraege.filter((a) => a.status === "pending").length;
|
|
13634
13870
|
const auffaellige = auftraege.filter((a) => a.status !== "succeeded");
|
|
13635
13871
|
const erzeugenAuf = erzeugenOffen ?? batches.length === 0;
|
|
13872
|
+
const chatAuftraegeAuf = chatAuftraegeOffen ?? offeneChatAuftraege > 0;
|
|
13636
13873
|
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "szenen-bilder", "data-testid": `bilder-${lauf.run_id}`, children: [
|
|
13637
13874
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("section", { className: "szenen-bilder__eingang", "data-testid": `eingang-${lauf.run_id}`, children: [
|
|
13638
13875
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("h5", { children: "Eingang \u2014 woraus dieser Prompt entstand" }),
|
|
@@ -13753,6 +13990,43 @@ var RunImagesPanel = ({ port, lauf }) => {
|
|
|
13753
13990
|
}
|
|
13754
13991
|
)
|
|
13755
13992
|
] }),
|
|
13993
|
+
faehig.imageJobs && /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
13994
|
+
"details",
|
|
13995
|
+
{
|
|
13996
|
+
className: "szenen-bilder__aktion",
|
|
13997
|
+
"data-testid": `auftraege-chat-bereich-${lauf.run_id}`,
|
|
13998
|
+
open: chatAuftraegeAuf,
|
|
13999
|
+
children: [
|
|
14000
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
14001
|
+
"summary",
|
|
14002
|
+
{
|
|
14003
|
+
onClick: (e) => {
|
|
14004
|
+
e.preventDefault();
|
|
14005
|
+
setChatAuftraegeOffen(!chatAuftraegeAuf);
|
|
14006
|
+
},
|
|
14007
|
+
children: [
|
|
14008
|
+
"Bildauftraege aus dem Chat",
|
|
14009
|
+
offeneChatAuftraege > 0 ? ` (${offeneChatAuftraege} wartend)` : ""
|
|
14010
|
+
]
|
|
14011
|
+
}
|
|
14012
|
+
),
|
|
14013
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
14014
|
+
ChatAuftraegeAktion,
|
|
14015
|
+
{
|
|
14016
|
+
port,
|
|
14017
|
+
lauf,
|
|
14018
|
+
auftraege: chatAuftraege,
|
|
14019
|
+
fehler: chatAuftraegePhase.status === "error" ? chatAuftraegePhase.message : null,
|
|
14020
|
+
laedt: chatAuftraegePhase.status === "loading",
|
|
14021
|
+
onFertig: () => {
|
|
14022
|
+
setChatAuftraegeOffen(true);
|
|
14023
|
+
neuLesen();
|
|
14024
|
+
}
|
|
14025
|
+
}
|
|
14026
|
+
)
|
|
14027
|
+
]
|
|
14028
|
+
}
|
|
14029
|
+
),
|
|
13756
14030
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
13757
14031
|
"details",
|
|
13758
14032
|
{
|
|
@@ -13884,12 +14158,12 @@ var BestandsBild = ({ port, mediaRef }) => {
|
|
|
13884
14158
|
] });
|
|
13885
14159
|
};
|
|
13886
14160
|
function useBildQuellen(port, bilder) {
|
|
13887
|
-
const [stand, setStand] = (0,
|
|
13888
|
-
const gehalten = (0,
|
|
13889
|
-
const gescheitert = (0,
|
|
13890
|
-
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);
|
|
13891
14165
|
const schluessel = bilder.map((b) => `${b.media_ref}|${b.mime_type ?? ""}`).join(" ");
|
|
13892
|
-
(0,
|
|
14166
|
+
(0, import_react41.useEffect)(() => {
|
|
13893
14167
|
const bestand = port.imageLibrary;
|
|
13894
14168
|
if (!bestand) return void 0;
|
|
13895
14169
|
let abgebrochen = false;
|
|
@@ -13936,7 +14210,7 @@ function useBildQuellen(port, bilder) {
|
|
|
13936
14210
|
erneutVersuchen: () => setVersuch((n) => n + 1)
|
|
13937
14211
|
};
|
|
13938
14212
|
}
|
|
13939
|
-
var BildDetails = ({ bild, fehler:
|
|
14213
|
+
var BildDetails = ({ bild, fehler: fehler5, aufloesbar }) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { className: "szenen-bilder__meta", "data-testid": `batchbild-${bild.media_ref}`, children: [
|
|
13940
14214
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("code", { children: bild.media_ref }),
|
|
13941
14215
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { children: [
|
|
13942
14216
|
"Position ",
|
|
@@ -13949,7 +14223,7 @@ var BildDetails = ({ bild, fehler: fehler4, aufloesbar }) => /* @__PURE__ */ (0,
|
|
|
13949
14223
|
"\xD7",
|
|
13950
14224
|
bild.height
|
|
13951
14225
|
] }) : null,
|
|
13952
|
-
|
|
14226
|
+
fehler5 && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("em", { "data-testid": `batchbild-fehler-${bild.media_ref}`, children: fehler5 }),
|
|
13953
14227
|
!aufloesbar && // Capability-Grenze, kein Fehler: Dieser Host kann Batches lesen,
|
|
13954
14228
|
// aber keine Bilder auflösen (E). Kein erfundener Fallback auf eine
|
|
13955
14229
|
// Serverroute (A6).
|
|
@@ -13965,18 +14239,18 @@ var HERKUNFT_KURZ = {
|
|
|
13965
14239
|
external_import: "extern importiert"
|
|
13966
14240
|
};
|
|
13967
14241
|
var BatchKachel = ({ port, batch }) => {
|
|
13968
|
-
const { quellen, fehler:
|
|
14242
|
+
const { quellen, fehler: fehler5, wiederholbar, erneutVersuchen } = useBildQuellen(port, batch.images);
|
|
13969
14243
|
const aufloesbar = Boolean(port.imageLibrary);
|
|
13970
14244
|
const bilder = batch.images.map((bild) => ({
|
|
13971
14245
|
id: bild.media_ref,
|
|
13972
14246
|
quelle: quellen.get(bild.media_ref) ?? null,
|
|
13973
14247
|
titel: `Bild ${bild.position + 1}`,
|
|
13974
|
-
...
|
|
14248
|
+
...fehler5.get(bild.media_ref) ? { leerText: "Nicht ladbar" } : {},
|
|
13975
14249
|
details: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
13976
14250
|
BildDetails,
|
|
13977
14251
|
{
|
|
13978
14252
|
bild,
|
|
13979
|
-
fehler:
|
|
14253
|
+
fehler: fehler5.get(bild.media_ref) ?? null,
|
|
13980
14254
|
aufloesbar
|
|
13981
14255
|
}
|
|
13982
14256
|
)
|
|
@@ -14025,7 +14299,7 @@ var BatchKachel = ({ port, batch }) => {
|
|
|
14025
14299
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { children: [
|
|
14026
14300
|
"Batches",
|
|
14027
14301
|
" ",
|
|
14028
|
-
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: [
|
|
14029
14303
|
i > 0 ? ", " : "",
|
|
14030
14304
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("code", { children: h.generation_batch_id }),
|
|
14031
14305
|
` (${HERKUNFT_KURZ[h.source_kind]})`
|
|
@@ -14036,7 +14310,7 @@ var BatchKachel = ({ port, batch }) => {
|
|
|
14036
14310
|
BildDetails,
|
|
14037
14311
|
{
|
|
14038
14312
|
bild,
|
|
14039
|
-
fehler:
|
|
14313
|
+
fehler: fehler5.get(bild.media_ref) ?? null,
|
|
14040
14314
|
aufloesbar
|
|
14041
14315
|
}
|
|
14042
14316
|
) }, bild.media_ref)) })
|
|
@@ -14054,11 +14328,11 @@ var ProfilBefunde = ({ wahl, runId }) => {
|
|
|
14054
14328
|
] });
|
|
14055
14329
|
};
|
|
14056
14330
|
var ErzeugenAktion = ({ port, lauf, wahl, wahlFehler, onFertig }) => {
|
|
14057
|
-
const [modellWahl, setModellWahl] = (0,
|
|
14058
|
-
const [anzahlText, setAnzahlText] = (0,
|
|
14059
|
-
const [seitenverhaeltnis, setSeitenverhaeltnis] = (0,
|
|
14060
|
-
const [laeuft, setLaeuft] = (0,
|
|
14061
|
-
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);
|
|
14062
14336
|
if (!port.imageGeneration) {
|
|
14063
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." });
|
|
14064
14338
|
}
|
|
@@ -14235,23 +14509,23 @@ var ErzeugenAktion = ({ port, lauf, wahl, wahlFehler, onFertig }) => {
|
|
|
14235
14509
|
};
|
|
14236
14510
|
var DEFAULT_VORLAGE_KEY = "__default__";
|
|
14237
14511
|
var ZuordnenAktion = ({ port, lauf, onFertig }) => {
|
|
14238
|
-
const [dateien, setDateien] = (0,
|
|
14239
|
-
const [seeds, setSeeds] = (0,
|
|
14240
|
-
const [zip, setZip] = (0,
|
|
14241
|
-
const [zipLaedt, setZipLaedt] = (0,
|
|
14242
|
-
const [seedListe, setSeedListe] = (0,
|
|
14243
|
-
const [vorlagen, setVorlagen] = (0,
|
|
14244
|
-
const [vorlageKey, setVorlageKey] = (0,
|
|
14245
|
-
const [vorlagenStand, setVorlagenStand] = (0,
|
|
14246
|
-
const [vorlageBenennen, setVorlageBenennen] = (0,
|
|
14247
|
-
const [vorlageName, setVorlageName] = (0,
|
|
14248
|
-
const [modell, setModell] = (0,
|
|
14249
|
-
const [itemId, setItemId] = (0,
|
|
14250
|
-
const [laeuft, setLaeuft] = (0,
|
|
14251
|
-
const [notiz, setNotiz] = (0,
|
|
14252
|
-
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);
|
|
14253
14527
|
const zipAktiv = zip !== null;
|
|
14254
|
-
(0,
|
|
14528
|
+
(0, import_react41.useEffect)(() => {
|
|
14255
14529
|
if (!zipAktiv) return;
|
|
14256
14530
|
let abgebrochen = false;
|
|
14257
14531
|
void (async () => {
|
|
@@ -14305,11 +14579,11 @@ var ZuordnenAktion = ({ port, lauf, onFertig }) => {
|
|
|
14305
14579
|
setSeedListe(DEFAULT_SEED_VORLAGE.seeds);
|
|
14306
14580
|
setVorlageKey(DEFAULT_VORLAGE_KEY);
|
|
14307
14581
|
}
|
|
14308
|
-
} catch (
|
|
14582
|
+
} catch (fehler5) {
|
|
14309
14583
|
if (veraltet()) return;
|
|
14310
14584
|
setNotiz({
|
|
14311
14585
|
tone: "error",
|
|
14312
|
-
text:
|
|
14586
|
+
text: fehler5 instanceof Error ? fehler5.message : String(fehler5)
|
|
14313
14587
|
});
|
|
14314
14588
|
}
|
|
14315
14589
|
if (veraltet()) return;
|
|
@@ -14328,10 +14602,10 @@ var ZuordnenAktion = ({ port, lauf, onFertig }) => {
|
|
|
14328
14602
|
setNotiz(
|
|
14329
14603
|
ergebnis.kind === "ok" ? { tone: "ok", text: `Importiert als Item ${ergebnis.value.item_id} mit ${bilder.length} Bild(ern).` } : noticeFor(ergebnis, "Externe Bilder importieren")
|
|
14330
14604
|
);
|
|
14331
|
-
} catch (
|
|
14605
|
+
} catch (fehler5) {
|
|
14332
14606
|
setNotiz({
|
|
14333
14607
|
tone: "error",
|
|
14334
|
-
text: `Externe Bilder importieren: ${
|
|
14608
|
+
text: `Externe Bilder importieren: ${fehler5 instanceof Error ? fehler5.message : String(fehler5)}`
|
|
14335
14609
|
});
|
|
14336
14610
|
}
|
|
14337
14611
|
setLaeuft(false);
|
|
@@ -14353,10 +14627,10 @@ var ZuordnenAktion = ({ port, lauf, onFertig }) => {
|
|
|
14353
14627
|
...seed !== void 0 ? { seed } : {}
|
|
14354
14628
|
});
|
|
14355
14629
|
}
|
|
14356
|
-
} catch (
|
|
14630
|
+
} catch (fehler5) {
|
|
14357
14631
|
setNotiz({
|
|
14358
14632
|
tone: "error",
|
|
14359
|
-
text: `Externe Bilder importieren: ${
|
|
14633
|
+
text: `Externe Bilder importieren: ${fehler5 instanceof Error ? fehler5.message : String(fehler5)}`
|
|
14360
14634
|
});
|
|
14361
14635
|
return;
|
|
14362
14636
|
}
|
|
@@ -14692,17 +14966,112 @@ function readAsBase64(datei) {
|
|
|
14692
14966
|
leser.readAsDataURL(datei);
|
|
14693
14967
|
});
|
|
14694
14968
|
}
|
|
14969
|
+
var CHAT_AUFTRAG_TEXT = {
|
|
14970
|
+
pending: "wartet auf \xDCbernahme",
|
|
14971
|
+
// Wie bei `started` (D-119): laufend und abgebrochen sind aus dem Log
|
|
14972
|
+
// nicht unterscheidbar, und es gibt keinen automatischen Retry, der die
|
|
14973
|
+
// Unterscheidung bräuchte.
|
|
14974
|
+
claimed: "\xFCbernommen (l\xE4uft oder unterbrochen)",
|
|
14975
|
+
succeeded: "erfolgreich",
|
|
14976
|
+
partial: "teilweise",
|
|
14977
|
+
failed: "fehlgeschlagen",
|
|
14978
|
+
rejected: "abgelehnt \u2014 es wurde nichts ausgef\xFChrt"
|
|
14979
|
+
};
|
|
14980
|
+
var ChatAuftraegeAktion = ({
|
|
14981
|
+
port,
|
|
14982
|
+
lauf,
|
|
14983
|
+
auftraege,
|
|
14984
|
+
fehler: fehler5,
|
|
14985
|
+
laedt,
|
|
14986
|
+
onFertig
|
|
14987
|
+
}) => {
|
|
14988
|
+
const [laufend, setLaufend] = (0, import_react41.useState)(null);
|
|
14989
|
+
const [notiz, setNotiz] = (0, import_react41.useState)(null);
|
|
14990
|
+
async function uebernehmen(jobId) {
|
|
14991
|
+
if (!port.imageJobs) return;
|
|
14992
|
+
setLaufend(jobId);
|
|
14993
|
+
setNotiz(null);
|
|
14994
|
+
try {
|
|
14995
|
+
const ergebnis = await port.imageJobs.claimAndRun(jobId);
|
|
14996
|
+
if (ergebnis.kind === "ok") {
|
|
14997
|
+
setNotiz({
|
|
14998
|
+
tone: ergebnis.job.status === "succeeded" ? "ok" : "rejected",
|
|
14999
|
+
text: `Auftrag ${jobId}: ${CHAT_AUFTRAG_TEXT[ergebnis.job.status]}.` + (ergebnis.job.diagnostic ? ` ${ergebnis.job.diagnostic}` : "")
|
|
15000
|
+
});
|
|
15001
|
+
} else if (ergebnis.kind === "lost") {
|
|
15002
|
+
setNotiz({ tone: "rejected", text: ergebnis.message });
|
|
15003
|
+
} else {
|
|
15004
|
+
setNotiz({ tone: "error", text: ergebnis.message });
|
|
15005
|
+
}
|
|
15006
|
+
} catch (e) {
|
|
15007
|
+
setNotiz({
|
|
15008
|
+
tone: "error",
|
|
15009
|
+
text: `Die \xDCbernahme von Auftrag ${jobId} ist technisch gescheitert: ` + (e instanceof Error ? e.message : String(e))
|
|
15010
|
+
});
|
|
15011
|
+
} finally {
|
|
15012
|
+
setLaufend(null);
|
|
15013
|
+
onFertig();
|
|
15014
|
+
}
|
|
15015
|
+
}
|
|
15016
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "szenen-bilder__aktion-inhalt", "data-testid": `chat-auftraege-${lauf.run_id}`, children: [
|
|
15017
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "szenen-area__lead", children: "Auftr\xE4ge, die \xFCber den MCP aus dem Chat kamen. Der Prompt wird nicht kopiert \u2014 ausgef\xFChrt wird der eingefrorene Prompt dieses Laufs." }),
|
|
15018
|
+
fehler5 && /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("p", { role: "alert", "data-testid": `chat-auftraege-fehler-${lauf.run_id}`, children: [
|
|
15019
|
+
"Bildauftr\xE4ge aus dem Chat konnten nicht gelesen werden: ",
|
|
15020
|
+
fehler5
|
|
15021
|
+
] }),
|
|
15022
|
+
!fehler5 && !laedt && auftraege.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { "data-testid": `chat-auftraege-leer-${lauf.run_id}`, children: "F\xFCr diesen Lauf liegt kein Auftrag aus dem Chat vor." }),
|
|
15023
|
+
notiz && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
15024
|
+
"p",
|
|
15025
|
+
{
|
|
15026
|
+
role: notiz.tone === "error" ? "alert" : void 0,
|
|
15027
|
+
"data-testid": `chat-auftraege-notiz-${lauf.run_id}`,
|
|
15028
|
+
children: notiz.text
|
|
15029
|
+
}
|
|
15030
|
+
),
|
|
15031
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("ul", { className: "szenen-bilder__auftraege", children: auftraege.map((auftrag) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("li", { "data-testid": `chat-auftrag-${auftrag.job_id}`, children: [
|
|
15032
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { children: CHAT_AUFTRAG_TEXT[auftrag.status] }),
|
|
15033
|
+
" \u2014 ",
|
|
15034
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { children: [
|
|
15035
|
+
auftrag.image_count,
|
|
15036
|
+
" ",
|
|
15037
|
+
auftrag.image_count === 1 ? "Bild" : "Bilder",
|
|
15038
|
+
" mit",
|
|
15039
|
+
" ",
|
|
15040
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("code", { children: auftrag.execution_target_model_key }),
|
|
15041
|
+
" / ",
|
|
15042
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("code", { children: auftrag.execution_profile_id }),
|
|
15043
|
+
auftrag.aspect_ratio ? ` im Format ${auftrag.aspect_ratio}` : ""
|
|
15044
|
+
] }),
|
|
15045
|
+
auftrag.diagnostic && /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { children: [
|
|
15046
|
+
" ",
|
|
15047
|
+
auftrag.diagnostic
|
|
15048
|
+
] }),
|
|
15049
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("br", {}),
|
|
15050
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("code", { className: "szenen-bilder__auftragskennung", children: auftrag.job_id }),
|
|
15051
|
+
auftrag.status === "pending" && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
15052
|
+
"button",
|
|
15053
|
+
{
|
|
15054
|
+
type: "button",
|
|
15055
|
+
disabled: laufend !== null,
|
|
15056
|
+
onClick: () => void uebernehmen(auftrag.job_id),
|
|
15057
|
+
"data-testid": `chat-auftrag-uebernehmen-${auftrag.job_id}`,
|
|
15058
|
+
children: laufend === auftrag.job_id ? "\xDCbernahme l\xE4uft\u2026" : "\xDCbernehmen und ausf\xFChren (kostet einen Bildaufruf)"
|
|
15059
|
+
}
|
|
15060
|
+
)
|
|
15061
|
+
] }, auftrag.job_id)) })
|
|
15062
|
+
] });
|
|
15063
|
+
};
|
|
14695
15064
|
|
|
14696
15065
|
// src/features/szenen/areas/RunsPanel.tsx
|
|
14697
15066
|
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
14698
15067
|
var RunsPanel = ({ port, source, contextId }) => {
|
|
14699
|
-
const [anweisung, setAnweisung] = (0,
|
|
14700
|
-
const [laeuft, setLaeuft] = (0,
|
|
14701
|
-
const [notiz, setNotiz] = (0,
|
|
14702
|
-
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);
|
|
14703
15072
|
const faehig = capabilitiesOf(port);
|
|
14704
15073
|
const quellSchluessel = source.kind === "published" ? source.shotVersionId : source.kind === "draft_chain" ? source.chainId : source.sceneElementId;
|
|
14705
|
-
const aktuelleQuelle = (0,
|
|
15074
|
+
const aktuelleQuelle = (0, import_react42.useRef)({ kind: source.kind, id: quellSchluessel });
|
|
14706
15075
|
aktuelleQuelle.current = { kind: source.kind, id: quellSchluessel };
|
|
14707
15076
|
const [phase, neuLaden] = usePortQuery(
|
|
14708
15077
|
() => {
|
|
@@ -14730,10 +15099,11 @@ var RunsPanel = ({ port, source, contextId }) => {
|
|
|
14730
15099
|
neuLaden();
|
|
14731
15100
|
};
|
|
14732
15101
|
const laeufe = phase.status === "ready" ? phase.value : [];
|
|
14733
|
-
const
|
|
15102
|
+
const titel = useLaufTitel(port, laeufe);
|
|
15103
|
+
const [gewaehlteRunde, setGewaehlteRunde] = (0, import_react42.useState)(void 0);
|
|
14734
15104
|
const neueste = laeufe.length > 0 ? laeufe[laeufe.length - 1].run_id : null;
|
|
14735
15105
|
const offeneRunde = gewaehlteRunde === void 0 ? neueste : gewaehlteRunde;
|
|
14736
|
-
(0,
|
|
15106
|
+
(0, import_react42.useEffect)(() => {
|
|
14737
15107
|
setGewaehlteRunde(void 0);
|
|
14738
15108
|
setAnweisung("");
|
|
14739
15109
|
setNotiz(null);
|
|
@@ -14768,6 +15138,7 @@ var RunsPanel = ({ port, source, contextId }) => {
|
|
|
14768
15138
|
port,
|
|
14769
15139
|
lauf,
|
|
14770
15140
|
runde: roundOfRun(lauf, laeufe),
|
|
15141
|
+
titel: titel.get(lauf.run_id) ?? null,
|
|
14771
15142
|
offen: lauf.run_id === offeneRunde,
|
|
14772
15143
|
onUmschalten: () => setGewaehlteRunde(
|
|
14773
15144
|
lauf.run_id === offeneRunde ? null : lauf.run_id
|
|
@@ -14792,7 +15163,7 @@ var STATUS_TEXT = {
|
|
|
14792
15163
|
};
|
|
14793
15164
|
var LANGER_TEXT = 240;
|
|
14794
15165
|
var Sekundaertext = ({ titel, text, testId }) => {
|
|
14795
|
-
const [ganz, setGanz] = (0,
|
|
15166
|
+
const [ganz, setGanz] = (0, import_react42.useState)(false);
|
|
14796
15167
|
const lang = text.length > LANGER_TEXT;
|
|
14797
15168
|
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("section", { className: "szenen-runs__sekundaer", children: [
|
|
14798
15169
|
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("h5", { children: titel }),
|
|
@@ -14811,7 +15182,7 @@ var Sekundaertext = ({ titel, text, testId }) => {
|
|
|
14811
15182
|
};
|
|
14812
15183
|
var LISTE_ANFANG = 3;
|
|
14813
15184
|
var SekundaerListe = ({ titel, eintraege, testId, className }) => {
|
|
14814
|
-
const [alle, setAlle] = (0,
|
|
15185
|
+
const [alle, setAlle] = (0, import_react42.useState)(false);
|
|
14815
15186
|
const lang = eintraege.length > LISTE_ANFANG;
|
|
14816
15187
|
const gezeigt = lang && !alle ? eintraege.slice(0, LISTE_ANFANG) : eintraege;
|
|
14817
15188
|
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("section", { className: "szenen-runs__sekundaer", children: [
|
|
@@ -14829,10 +15200,10 @@ var SekundaerListe = ({ titel, eintraege, testId, className }) => {
|
|
|
14829
15200
|
)
|
|
14830
15201
|
] });
|
|
14831
15202
|
};
|
|
14832
|
-
var RundenEintrag = ({ port, lauf, runde, offen, onUmschalten, itemMoeglich, onItemGespeichert }) => {
|
|
14833
|
-
const [notiz, setNotiz] = (0,
|
|
14834
|
-
const [laeuft, setLaeuft] = (0,
|
|
14835
|
-
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);
|
|
14836
15207
|
const [itemPhase] = usePortQuery(
|
|
14837
15208
|
() => offen && port.items ? port.items.getItemForRun(lauf.run_id) : Promise.resolve({ kind: "ok", value: null }),
|
|
14838
15209
|
[port, lauf.run_id, nachladen, offen]
|
|
@@ -14860,10 +15231,7 @@ var RundenEintrag = ({ port, lauf, runde, offen, onUmschalten, itemMoeglich, onI
|
|
|
14860
15231
|
"aria-expanded": offen,
|
|
14861
15232
|
"data-testid": `runde-umschalten-${lauf.run_id}`,
|
|
14862
15233
|
children: [
|
|
14863
|
-
/* @__PURE__ */ (0, import_jsx_runtime37.
|
|
14864
|
-
"Runde ",
|
|
14865
|
-
runde
|
|
14866
|
-
] }),
|
|
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}` }),
|
|
14867
15235
|
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
14868
15236
|
"span",
|
|
14869
15237
|
{
|
|
@@ -14987,43 +15355,43 @@ var ElementDetail = ({
|
|
|
14987
15355
|
author,
|
|
14988
15356
|
onChanged
|
|
14989
15357
|
}) => {
|
|
14990
|
-
const [nachladen, setNachladen] = (0,
|
|
15358
|
+
const [nachladen, setNachladen] = (0, import_react43.useState)(0);
|
|
14991
15359
|
const [phase] = usePortQuery(
|
|
14992
15360
|
() => port.elements.getElement(elementId),
|
|
14993
15361
|
[port, elementId, nachladen]
|
|
14994
15362
|
);
|
|
14995
|
-
const [entwurf, setEntwurf] = (0,
|
|
14996
|
-
const [notiz, setNotiz] = (0,
|
|
14997
|
-
const [laeuft, setLaeuft] = (0,
|
|
14998
|
-
const [wahl, setWahl] = (0,
|
|
14999
|
-
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);
|
|
15000
15368
|
const [kettenPhase] = usePortQuery(
|
|
15001
15369
|
() => port.context.chainFor(contextId),
|
|
15002
15370
|
[port, contextId]
|
|
15003
15371
|
);
|
|
15004
|
-
const kontextkette = (0,
|
|
15372
|
+
const kontextkette = (0, import_react43.useMemo)(() => {
|
|
15005
15373
|
if (kettenPhase.status === "ready") return { status: "ready", chain: kettenPhase.value };
|
|
15006
15374
|
if (kettenPhase.status === "error") return { status: "error", message: kettenPhase.message };
|
|
15007
15375
|
return { status: "loading" };
|
|
15008
15376
|
}, [kettenPhase]);
|
|
15009
15377
|
const kette = kontextkette.status === "ready" ? kontextkette.chain : null;
|
|
15010
15378
|
const schnappschuss = phase.status === "ready" ? phase.value : null;
|
|
15011
|
-
const kopfErgebnis = (0,
|
|
15379
|
+
const kopfErgebnis = (0, import_react43.useMemo)(
|
|
15012
15380
|
() => schnappschuss && kette ? effectivePreferredHeadSafe(schnappschuss, kette) : null,
|
|
15013
15381
|
[schnappschuss, kette]
|
|
15014
15382
|
);
|
|
15015
15383
|
const kopfAufloesung = resolutionOf(kopfErgebnis);
|
|
15016
15384
|
const bevorzugteVersion = selectedHeadVersionId(kopfAufloesung);
|
|
15017
|
-
const felder = (0,
|
|
15385
|
+
const felder = (0, import_react43.useMemo)(
|
|
15018
15386
|
() => schnappschuss ? fieldsForType(schnappschuss.element.type) : [],
|
|
15019
15387
|
[schnappschuss?.element.type]
|
|
15020
15388
|
);
|
|
15021
|
-
(0,
|
|
15389
|
+
(0, import_react43.useEffect)(() => {
|
|
15022
15390
|
setWahl(null);
|
|
15023
15391
|
setBasiswechsel(null);
|
|
15024
15392
|
setNotiz(null);
|
|
15025
15393
|
}, [elementId]);
|
|
15026
|
-
const automatisch = (0,
|
|
15394
|
+
const automatisch = (0, import_react43.useMemo)(() => {
|
|
15027
15395
|
if (!schnappschuss) return { kind: "keiner" };
|
|
15028
15396
|
if (schnappschuss.draft) return { kind: "draft" };
|
|
15029
15397
|
if (bevorzugteVersion) return { kind: "version", versionId: bevorzugteVersion };
|
|
@@ -15033,14 +15401,14 @@ var ElementDetail = ({
|
|
|
15033
15401
|
if (schnappschuss.versions.length === 0) return { kind: "draft" };
|
|
15034
15402
|
return { kind: "keiner" };
|
|
15035
15403
|
}, [schnappschuss, bevorzugteVersion]);
|
|
15036
|
-
const angezeigt = (0,
|
|
15404
|
+
const angezeigt = (0, import_react43.useMemo)(() => {
|
|
15037
15405
|
if (!wahl) return automatisch;
|
|
15038
15406
|
if (wahl.kind === "version" && !schnappschuss?.versions.some((v) => v.version_id === wahl.versionId)) {
|
|
15039
15407
|
return automatisch;
|
|
15040
15408
|
}
|
|
15041
15409
|
return wahl;
|
|
15042
15410
|
}, [wahl, automatisch, schnappschuss]);
|
|
15043
|
-
const angezeigtePayload = (0,
|
|
15411
|
+
const angezeigtePayload = (0, import_react43.useMemo)(() => {
|
|
15044
15412
|
if (!schnappschuss) return {};
|
|
15045
15413
|
if (angezeigt.kind === "draft") return schnappschuss.draft?.payload ?? {};
|
|
15046
15414
|
if (angezeigt.kind === "version") {
|
|
@@ -15049,7 +15417,7 @@ var ElementDetail = ({
|
|
|
15049
15417
|
return {};
|
|
15050
15418
|
}, [schnappschuss, angezeigt]);
|
|
15051
15419
|
const anzeigeSchluessel = angezeigt.kind === "version" ? `v:${angezeigt.versionId}` : angezeigt.kind;
|
|
15052
|
-
(0,
|
|
15420
|
+
(0, import_react43.useEffect)(() => {
|
|
15053
15421
|
if (!schnappschuss) return;
|
|
15054
15422
|
setEntwurf(formFromPayload(felder, angezeigtePayload));
|
|
15055
15423
|
}, [
|
|
@@ -15062,7 +15430,7 @@ var ElementDetail = ({
|
|
|
15062
15430
|
const bearbeitbar = angezeigt.kind === "draft";
|
|
15063
15431
|
const setzeFeld = (pfad, wert) => setEntwurf((vorher) => ({ ...vorher, [pfad]: wert }));
|
|
15064
15432
|
const payloadAusFormular = () => payloadFromForm(felder, entwurf);
|
|
15065
|
-
const strukturPayload = (0,
|
|
15433
|
+
const strukturPayload = (0, import_react43.useMemo)(() => {
|
|
15066
15434
|
const lies = (name) => {
|
|
15067
15435
|
try {
|
|
15068
15436
|
const roh = JSON.parse(entwurf[name] || "[]");
|
|
@@ -15073,19 +15441,19 @@ var ElementDetail = ({
|
|
|
15073
15441
|
};
|
|
15074
15442
|
return { nodes: lies("nodes"), relations: lies("relations") };
|
|
15075
15443
|
}, [entwurf.nodes, entwurf.relations]);
|
|
15076
|
-
const kettenfeld = (0,
|
|
15444
|
+
const kettenfeld = (0, import_react43.useMemo)(() => {
|
|
15077
15445
|
const kette2 = schnappschuss?.draft?.chain;
|
|
15078
15446
|
if (!kette2) return null;
|
|
15079
15447
|
return kette2.links[0]?.field ?? null;
|
|
15080
15448
|
}, [schnappschuss?.draft?.chain]);
|
|
15081
|
-
const laufStand = (0,
|
|
15449
|
+
const laufStand = (0, import_react43.useMemo)(() => {
|
|
15082
15450
|
if (!schnappschuss || schnappschuss.element.type !== "scene") return null;
|
|
15083
15451
|
if (schnappschuss.draft) return schnappschuss.draft.payload;
|
|
15084
15452
|
const kopf = usableHeadVersionId(kopfAufloesung);
|
|
15085
15453
|
if (!kopf) return null;
|
|
15086
15454
|
return schnappschuss.versions.find((v) => v.version_id === kopf)?.payload ?? null;
|
|
15087
15455
|
}, [schnappschuss, kopfAufloesung]);
|
|
15088
|
-
const szenenBlocker = (0,
|
|
15456
|
+
const szenenBlocker = (0, import_react43.useMemo)(() => {
|
|
15089
15457
|
if (!schnappschuss || schnappschuss.element.type !== "scene") return null;
|
|
15090
15458
|
if (laufStand === null) {
|
|
15091
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.";
|
|
@@ -15095,7 +15463,7 @@ var ElementDetail = ({
|
|
|
15095
15463
|
const grund = flag.reasons[0];
|
|
15096
15464
|
return grund && "message" in grund ? grund.message : "Kein Lauf moeglich.";
|
|
15097
15465
|
}, [schnappschuss, laufStand]);
|
|
15098
|
-
const vorschau = (0,
|
|
15466
|
+
const vorschau = (0, import_react43.useMemo)(() => {
|
|
15099
15467
|
if (!schnappschuss) return null;
|
|
15100
15468
|
try {
|
|
15101
15469
|
const roh = validatePayload(schnappschuss.element.type, payloadAusFormular());
|
|
@@ -15481,15 +15849,15 @@ var ElementDetail = ({
|
|
|
15481
15849
|
};
|
|
15482
15850
|
|
|
15483
15851
|
// src/features/szenen/areas/useSchmalesDisplay.ts
|
|
15484
|
-
var
|
|
15852
|
+
var import_react44 = require("react");
|
|
15485
15853
|
var SCHMALE_ANSICHT = "(max-width: 760px)";
|
|
15486
15854
|
function messen(abfrage) {
|
|
15487
15855
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
|
|
15488
15856
|
return window.matchMedia(abfrage).matches;
|
|
15489
15857
|
}
|
|
15490
15858
|
function useSchmalesDisplay(abfrage = SCHMALE_ANSICHT) {
|
|
15491
|
-
const [schmal, setSchmal] = (0,
|
|
15492
|
-
(0,
|
|
15859
|
+
const [schmal, setSchmal] = (0, import_react44.useState)(() => messen(abfrage));
|
|
15860
|
+
(0, import_react44.useEffect)(() => {
|
|
15493
15861
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
15494
15862
|
const liste = window.matchMedia(abfrage);
|
|
15495
15863
|
const aktualisieren = () => setSchmal(liste.matches);
|
|
@@ -15516,16 +15884,16 @@ var ElementsArea = ({
|
|
|
15516
15884
|
const gemischt = auswahlTypen.length > 1;
|
|
15517
15885
|
const ueberschrift = listTitle ?? (einzelTyp ? elementTypPlural(einzelTyp) : "Bausteine");
|
|
15518
15886
|
const einzahl = einzelTyp ? elementTypLabel(einzelTyp) : "Objekt";
|
|
15519
|
-
const [typ, setTyp] = (0,
|
|
15520
|
-
const [zeigeStillgelegte, setZeigeStillgelegte] = (0,
|
|
15521
|
-
const [gewaehlt, setGewaehlt] = (0,
|
|
15522
|
-
const [neuerTitel, setNeuerTitel] = (0,
|
|
15523
|
-
const [neuerTyp, setNeuerTyp] = (0,
|
|
15524
|
-
const [notiz, setNotiz] = (0,
|
|
15525
|
-
const [anlegend, setAnlegend] = (0,
|
|
15526
|
-
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);
|
|
15527
15895
|
const schmal = useSchmalesDisplay();
|
|
15528
|
-
const [ansicht, setAnsicht] = (0,
|
|
15896
|
+
const [ansicht, setAnsicht] = (0, import_react45.useState)("liste");
|
|
15529
15897
|
const zeigeListe = !schmal || ansicht === "liste";
|
|
15530
15898
|
const zeigeDetail = !schmal || ansicht === "detail";
|
|
15531
15899
|
const waehle = (elementId) => {
|
|
@@ -15737,204 +16105,28 @@ var ElementsArea = ({
|
|
|
15737
16105
|
};
|
|
15738
16106
|
|
|
15739
16107
|
// src/features/szenen/areas/SceneArea.tsx
|
|
15740
|
-
var
|
|
15741
|
-
var SceneArea = ({ port, contextId }) => /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "szenen-scene", "data-testid": "bereich-szene", children: [
|
|
15742
|
-
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "szenen-area__lead", children: "Szenen dieses Kontexts. Auswahl \xF6ffnet die Bearbeitung der Nodes und Beziehungen." }),
|
|
15743
|
-
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ElementsArea, { port, contextId, restrictToTypes: ["scene"] })
|
|
15744
|
-
] });
|
|
16108
|
+
var import_react49 = require("react");
|
|
15745
16109
|
|
|
15746
|
-
// src/features/szenen/areas/
|
|
15747
|
-
var
|
|
15748
|
-
var SetupArea = ({ port, contextId }) => /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "szenen-setup", "data-testid": "bereich-setup", children: [
|
|
15749
|
-
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "szenen-area__lead", children: "Bildaufbauten, Kameras und Stile dieses Kontexts." }),
|
|
15750
|
-
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
15751
|
-
ElementsArea,
|
|
15752
|
-
{
|
|
15753
|
-
port,
|
|
15754
|
-
contextId,
|
|
15755
|
-
restrictToTypes: ["scene_setup", "camera_preset", "camera_pattern", "style"],
|
|
15756
|
-
allTypesLabel: "alle Bildaufbau-Typen",
|
|
15757
|
-
listTitle: "Bildaufbau"
|
|
15758
|
-
}
|
|
15759
|
-
)
|
|
15760
|
-
] });
|
|
16110
|
+
// src/features/szenen/areas/DeriveArea.tsx
|
|
16111
|
+
var import_react48 = require("react");
|
|
15761
16112
|
|
|
15762
|
-
// src/
|
|
15763
|
-
|
|
15764
|
-
|
|
15765
|
-
|
|
15766
|
-
|
|
15767
|
-
|
|
15768
|
-
|
|
15769
|
-
|
|
15770
|
-
|
|
15771
|
-
|
|
15772
|
-
|
|
15773
|
-
|
|
15774
|
-
|
|
15775
|
-
|
|
15776
|
-
|
|
15777
|
-
|
|
15778
|
-
|
|
15779
|
-
var import_react45 = require("react");
|
|
15780
|
-
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
15781
|
-
var leer = async () => ({ kind: "ok", value: [] });
|
|
15782
|
-
var MediaArea = ({ port }) => {
|
|
15783
|
-
const media = port.media;
|
|
15784
|
-
const [versionInput, setVersionInput] = (0, import_react45.useState)("");
|
|
15785
|
-
const [versionId, setVersionId] = (0, import_react45.useState)("");
|
|
15786
|
-
const [itemInput, setItemInput] = (0, import_react45.useState)("");
|
|
15787
|
-
const [itemId, setItemId] = (0, import_react45.useState)("");
|
|
15788
|
-
const [assetId, setAssetId] = (0, import_react45.useState)("");
|
|
15789
|
-
const [role, setRole] = (0, import_react45.useState)("reference");
|
|
15790
|
-
const [position, setPosition] = (0, import_react45.useState)("0");
|
|
15791
|
-
const [notiz, setNotiz] = (0, import_react45.useState)(null);
|
|
15792
|
-
const [busy, setBusy] = (0, import_react45.useState)(false);
|
|
15793
|
-
const [refresh, setRefresh] = (0, import_react45.useState)(0);
|
|
15794
|
-
const [versionPhase] = usePortQuery(
|
|
15795
|
-
() => versionId ? media.listVersionMedia(versionId) : leer(),
|
|
15796
|
-
[media, versionId, refresh]
|
|
15797
|
-
);
|
|
15798
|
-
const [itemPhase] = usePortQuery(
|
|
15799
|
-
() => itemId ? media.listItemMedia(itemId) : Promise.resolve({ kind: "ok", value: [] }),
|
|
15800
|
-
[media, itemId, refresh]
|
|
15801
|
-
);
|
|
15802
|
-
const zuordnen = async () => {
|
|
15803
|
-
if (!versionInput.trim() || !assetId.trim()) {
|
|
15804
|
-
setNotiz({ tone: "error", text: "Version-ID und Asset-ID sind erforderlich." });
|
|
15805
|
-
return;
|
|
15806
|
-
}
|
|
15807
|
-
const parsedPosition = Number(position);
|
|
15808
|
-
if (!Number.isInteger(parsedPosition) || parsedPosition < 0) {
|
|
15809
|
-
setNotiz({ tone: "error", text: `Position muss eine nicht-negative Ganzzahl sein, war ${JSON.stringify(position)}.` });
|
|
15810
|
-
return;
|
|
15811
|
-
}
|
|
15812
|
-
setBusy(true);
|
|
15813
|
-
const result = await media.attachVersionMedium({
|
|
15814
|
-
versionId: versionInput.trim(),
|
|
15815
|
-
assetId: assetId.trim(),
|
|
15816
|
-
role,
|
|
15817
|
-
position: parsedPosition
|
|
15818
|
-
});
|
|
15819
|
-
setNotiz(noticeFor(result, "Referenzmedium zuordnen"));
|
|
15820
|
-
if (result.kind === "ok") {
|
|
15821
|
-
setVersionId(versionInput.trim());
|
|
15822
|
-
setRefresh((value) => value + 1);
|
|
15823
|
-
}
|
|
15824
|
-
setBusy(false);
|
|
15825
|
-
};
|
|
15826
|
-
const entfernen = async (medium) => {
|
|
15827
|
-
setBusy(true);
|
|
15828
|
-
const result = await media.detachVersionMedium({
|
|
15829
|
-
versionId,
|
|
15830
|
-
assetId: medium.assetId,
|
|
15831
|
-
role: medium.role
|
|
15832
|
-
});
|
|
15833
|
-
setNotiz(noticeFor(result, "Referenzmedium entfernen"));
|
|
15834
|
-
if (result.kind === "ok") setRefresh((value) => value + 1);
|
|
15835
|
-
setBusy(false);
|
|
15836
|
-
};
|
|
15837
|
-
const versionMedien = versionPhase.status === "ready" ? versionPhase.value : [];
|
|
15838
|
-
const itemMedien = itemPhase.status === "ready" ? itemPhase.value : [];
|
|
15839
|
-
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "szenen-media", "data-testid": "bereich-medien", children: [
|
|
15840
|
-
/* @__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." }),
|
|
15841
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("section", { children: [
|
|
15842
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("h3", { children: "Version" }),
|
|
15843
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "szenen-media__lookup", children: [
|
|
15844
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("label", { children: [
|
|
15845
|
-
"Version-ID",
|
|
15846
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("input", { value: versionInput, onChange: (event) => setVersionInput(event.target.value) })
|
|
15847
|
-
] }),
|
|
15848
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("button", { type: "button", onClick: () => setVersionId(versionInput.trim()), children: "Medien laden" })
|
|
15849
|
-
] }),
|
|
15850
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "szenen-media__attach", children: [
|
|
15851
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("label", { children: [
|
|
15852
|
-
"Asset-ID",
|
|
15853
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("input", { value: assetId, onChange: (event) => setAssetId(event.target.value) })
|
|
15854
|
-
] }),
|
|
15855
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("label", { children: [
|
|
15856
|
-
"Rolle",
|
|
15857
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("select", { value: role, onChange: (event) => setRole(event.target.value), children: [
|
|
15858
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("option", { value: "reference", children: "reference" }),
|
|
15859
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("option", { value: "mask", children: "mask" }),
|
|
15860
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("option", { value: "palette", children: "palette" }),
|
|
15861
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("option", { value: "example", children: "example" })
|
|
15862
|
-
] })
|
|
15863
|
-
] }),
|
|
15864
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("label", { children: [
|
|
15865
|
-
"Position",
|
|
15866
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("input", { type: "number", min: "0", value: position, onChange: (event) => setPosition(event.target.value) })
|
|
15867
|
-
] }),
|
|
15868
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("button", { type: "button", disabled: busy, onClick: zuordnen, children: "zuordnen" })
|
|
15869
|
-
] }),
|
|
15870
|
-
versionPhase.status === "loading" && versionId && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { children: "Versionsmedien werden geladen \u2026" }),
|
|
15871
|
-
versionPhase.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("p", { role: "alert", children: [
|
|
15872
|
-
"Versionsmedien konnten nicht geladen werden: ",
|
|
15873
|
-
versionPhase.message
|
|
15874
|
-
] }),
|
|
15875
|
-
versionPhase.status === "ready" && versionId && versionMedien.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("p", { children: [
|
|
15876
|
-
"Version ",
|
|
15877
|
-
versionId,
|
|
15878
|
-
" hat keine Medien."
|
|
15879
|
-
] }),
|
|
15880
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { className: "szenen-media__list", children: versionMedien.map((medium) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("li", { children: [
|
|
15881
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("code", { children: medium.assetId }),
|
|
15882
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { children: [
|
|
15883
|
-
medium.role,
|
|
15884
|
-
" \xB7 Position ",
|
|
15885
|
-
medium.position
|
|
15886
|
-
] }),
|
|
15887
|
-
medium.url && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("a", { href: medium.url, target: "_blank", rel: "noreferrer", children: "\xF6ffnen" }),
|
|
15888
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("button", { type: "button", disabled: busy, onClick: () => entfernen(medium), children: "entfernen" })
|
|
15889
|
-
] }, `${medium.assetId}:${medium.role}`)) })
|
|
15890
|
-
] }),
|
|
15891
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("section", { children: [
|
|
15892
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("h3", { children: "Item-Medien" }),
|
|
15893
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "szenen-media__lookup", children: [
|
|
15894
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("label", { children: [
|
|
15895
|
-
"Item-ID",
|
|
15896
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("input", { value: itemInput, onChange: (event) => setItemInput(event.target.value) })
|
|
15897
|
-
] }),
|
|
15898
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("button", { type: "button", onClick: () => setItemId(itemInput.trim()), children: "Item-Medien laden" })
|
|
15899
|
-
] }),
|
|
15900
|
-
itemPhase.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("p", { role: "alert", children: [
|
|
15901
|
-
"Item-Medien konnten nicht geladen werden: ",
|
|
15902
|
-
itemPhase.message
|
|
15903
|
-
] }),
|
|
15904
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { className: "szenen-media__list", children: itemMedien.map((medium) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("li", { children: [
|
|
15905
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("code", { children: medium.assetId }),
|
|
15906
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { children: [
|
|
15907
|
-
medium.role,
|
|
15908
|
-
" \xB7 Position ",
|
|
15909
|
-
medium.position
|
|
15910
|
-
] }),
|
|
15911
|
-
medium.url && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("a", { href: medium.url, target: "_blank", rel: "noreferrer", children: "\xF6ffnen" })
|
|
15912
|
-
] }, `${medium.assetId}:${medium.role}:${medium.position}`)) })
|
|
15913
|
-
] }),
|
|
15914
|
-
notiz && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { role: notiz.tone === "error" ? "alert" : "status", "data-tone": notiz.tone, children: notiz.text })
|
|
15915
|
-
] });
|
|
15916
|
-
};
|
|
15917
|
-
|
|
15918
|
-
// src/features/szenen/areas/DeriveArea.tsx
|
|
15919
|
-
var import_react48 = require("react");
|
|
15920
|
-
|
|
15921
|
-
// src/lib/szenen/bildVorschau.ts
|
|
15922
|
-
async function ladeVorschauen(port, mediaRefs, fertig, optionen = {}) {
|
|
15923
|
-
const laden = port.loadPreview;
|
|
15924
|
-
if (!laden) return;
|
|
15925
|
-
const parallel = Math.max(1, optionen.parallel ?? 4);
|
|
15926
|
-
const lauf = optionen.lauf;
|
|
15927
|
-
const offen = mediaRefs.filter((ref) => !(optionen.hat?.(ref) ?? false));
|
|
15928
|
-
let naechster = 0;
|
|
15929
|
-
async function arbeiter() {
|
|
15930
|
-
while (naechster < offen.length) {
|
|
15931
|
-
if (lauf?.abgebrochen) return;
|
|
15932
|
-
const ref = offen[naechster];
|
|
15933
|
-
naechster += 1;
|
|
15934
|
-
const ergebnis = await laden.call(port, ref);
|
|
15935
|
-
if (lauf?.abgebrochen) return;
|
|
15936
|
-
if (ergebnis.kind === "ok") fertig(ref, ergebnis.value);
|
|
15937
|
-
else optionen.gescheitert?.(ref, ergebnis.message);
|
|
16113
|
+
// src/lib/szenen/bildVorschau.ts
|
|
16114
|
+
async function ladeVorschauen(port, mediaRefs, fertig, optionen = {}) {
|
|
16115
|
+
const laden = port.loadPreview;
|
|
16116
|
+
if (!laden) return;
|
|
16117
|
+
const parallel = Math.max(1, optionen.parallel ?? 4);
|
|
16118
|
+
const lauf = optionen.lauf;
|
|
16119
|
+
const offen = mediaRefs.filter((ref) => !(optionen.hat?.(ref) ?? false));
|
|
16120
|
+
let naechster = 0;
|
|
16121
|
+
async function arbeiter() {
|
|
16122
|
+
while (naechster < offen.length) {
|
|
16123
|
+
if (lauf?.abgebrochen) return;
|
|
16124
|
+
const ref = offen[naechster];
|
|
16125
|
+
naechster += 1;
|
|
16126
|
+
const ergebnis = await laden.call(port, ref);
|
|
16127
|
+
if (lauf?.abgebrochen) return;
|
|
16128
|
+
if (ergebnis.kind === "ok") fertig(ref, ergebnis.value);
|
|
16129
|
+
else optionen.gescheitert?.(ref, ergebnis.message);
|
|
15938
16130
|
}
|
|
15939
16131
|
}
|
|
15940
16132
|
await Promise.all(
|
|
@@ -16780,52 +16972,52 @@ async function runSceneDerivation(input, port, userPrompt) {
|
|
|
16780
16972
|
|
|
16781
16973
|
// src/features/szenen/areas/RecoveryPanel.tsx
|
|
16782
16974
|
var import_react46 = require("react");
|
|
16783
|
-
var
|
|
16975
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
16784
16976
|
var Diagnosen = ({ befunde, testId }) => {
|
|
16785
16977
|
if (befunde.length === 0) return null;
|
|
16786
|
-
return /* @__PURE__ */ (0,
|
|
16787
|
-
/* @__PURE__ */ (0,
|
|
16788
|
-
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: [
|
|
16789
16981
|
" \xB7 ",
|
|
16790
|
-
/* @__PURE__ */ (0,
|
|
16982
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("strong", { children: d.proposal_type })
|
|
16791
16983
|
] }),
|
|
16792
|
-
d.pointer && /* @__PURE__ */ (0,
|
|
16984
|
+
d.pointer && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
|
|
16793
16985
|
" \xB7 ",
|
|
16794
|
-
/* @__PURE__ */ (0,
|
|
16986
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("code", { children: d.pointer })
|
|
16795
16987
|
] }),
|
|
16796
|
-
typeof d.line === "number" && /* @__PURE__ */ (0,
|
|
16988
|
+
typeof d.line === "number" && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
|
|
16797
16989
|
" \xB7 Zeile ",
|
|
16798
16990
|
d.line,
|
|
16799
16991
|
", Spalte ",
|
|
16800
16992
|
d.column
|
|
16801
16993
|
] }),
|
|
16802
|
-
/* @__PURE__ */ (0,
|
|
16994
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { children: d.message })
|
|
16803
16995
|
] }, i)) });
|
|
16804
16996
|
};
|
|
16805
|
-
var Ausbeute = ({ bewertung }) => /* @__PURE__ */ (0,
|
|
16806
|
-
/* @__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: [
|
|
16807
16999
|
bewertung.status === "usable" && "Diese Fassung ist vollst\xE4ndig verwertbar.",
|
|
16808
17000
|
bewertung.status === "partial" && "Diese Fassung ist teilweise verwertbar \u2014 nicht alles kommt durch.",
|
|
16809
17001
|
bewertung.status === "invalid" && "Aus dieser Fassung ist nichts zu \xFCbernehmen."
|
|
16810
17002
|
] }),
|
|
16811
|
-
bewertung.proposals.length > 0 && /* @__PURE__ */ (0,
|
|
16812
|
-
/* @__PURE__ */ (0,
|
|
16813
|
-
/* @__PURE__ */ (0,
|
|
16814
|
-
/* @__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 }),
|
|
16815
17007
|
" \u2014 ",
|
|
16816
17008
|
p.status === "complete" ? "vollst\xE4ndig" : "teilweise; vor dem Ver\xF6ffentlichen zu erg\xE4nzen",
|
|
16817
|
-
p.status === "partial" && p.gaps && p.gaps.length > 0 && /* @__PURE__ */ (0,
|
|
16818
|
-
/* @__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 }),
|
|
16819
17011
|
": ",
|
|
16820
17012
|
l.reason
|
|
16821
17013
|
] }, i)) })
|
|
16822
17014
|
] }, p.type)) })
|
|
16823
17015
|
] }),
|
|
16824
|
-
bewertung.rejected.length > 0 && /* @__PURE__ */ (0,
|
|
16825
|
-
/* @__PURE__ */ (0,
|
|
16826
|
-
/* @__PURE__ */ (0,
|
|
16827
|
-
/* @__PURE__ */ (0,
|
|
16828
|
-
/* @__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}` })
|
|
16829
17021
|
] }, r.type)) })
|
|
16830
17022
|
] })
|
|
16831
17023
|
] });
|
|
@@ -16870,16 +17062,16 @@ var RecoveryPanel = ({ port, lauf }) => {
|
|
|
16870
17062
|
setLaeuft(false);
|
|
16871
17063
|
}
|
|
16872
17064
|
}
|
|
16873
|
-
return /* @__PURE__ */ (0,
|
|
16874
|
-
/* @__PURE__ */ (0,
|
|
16875
|
-
/* @__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: [
|
|
16876
17068
|
"Der Lauf ist bezahlt und seine Antwort liegt vor. Sie l\xE4sst sich hier pr\xFCfen und korrigieren \u2014 ",
|
|
16877
|
-
/* @__PURE__ */ (0,
|
|
17069
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("strong", { children: "ohne" }),
|
|
16878
17070
|
" einen weiteren Modellaufruf. Das Original bleibt unver\xE4ndert."
|
|
16879
17071
|
] }),
|
|
16880
|
-
/* @__PURE__ */ (0,
|
|
16881
|
-
/* @__PURE__ */ (0,
|
|
16882
|
-
/* @__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)(
|
|
16883
17075
|
"textarea",
|
|
16884
17076
|
{
|
|
16885
17077
|
readOnly: true,
|
|
@@ -16890,12 +17082,12 @@ var RecoveryPanel = ({ port, lauf }) => {
|
|
|
16890
17082
|
}
|
|
16891
17083
|
)
|
|
16892
17084
|
] }),
|
|
16893
|
-
/* @__PURE__ */ (0,
|
|
16894
|
-
/* @__PURE__ */ (0,
|
|
17085
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("label", { className: "szenen-field", children: [
|
|
17086
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { children: [
|
|
16895
17087
|
"Arbeitskopie",
|
|
16896
17088
|
bearbeitet ? " (bearbeitet)" : ""
|
|
16897
17089
|
] }),
|
|
16898
|
-
/* @__PURE__ */ (0,
|
|
17090
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
16899
17091
|
"textarea",
|
|
16900
17092
|
{
|
|
16901
17093
|
rows: 8,
|
|
@@ -16905,8 +17097,8 @@ var RecoveryPanel = ({ port, lauf }) => {
|
|
|
16905
17097
|
}
|
|
16906
17098
|
)
|
|
16907
17099
|
] }),
|
|
16908
|
-
/* @__PURE__ */ (0,
|
|
16909
|
-
/* @__PURE__ */ (0,
|
|
17100
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "szenen-rettung__aktionen", children: [
|
|
17101
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
16910
17102
|
"button",
|
|
16911
17103
|
{
|
|
16912
17104
|
type: "button",
|
|
@@ -16916,7 +17108,7 @@ var RecoveryPanel = ({ port, lauf }) => {
|
|
|
16916
17108
|
children: "Arbeitskopie pr\xFCfen (kein Modellaufruf)"
|
|
16917
17109
|
}
|
|
16918
17110
|
),
|
|
16919
|
-
/* @__PURE__ */ (0,
|
|
17111
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
16920
17112
|
"button",
|
|
16921
17113
|
{
|
|
16922
17114
|
type: "button",
|
|
@@ -16926,7 +17118,7 @@ var RecoveryPanel = ({ port, lauf }) => {
|
|
|
16926
17118
|
children: "Arbeitsfassung speichern"
|
|
16927
17119
|
}
|
|
16928
17120
|
),
|
|
16929
|
-
arbeitskopie !== null && /* @__PURE__ */ (0,
|
|
17121
|
+
arbeitskopie !== null && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
16930
17122
|
"button",
|
|
16931
17123
|
{
|
|
16932
17124
|
type: "button",
|
|
@@ -16941,15 +17133,15 @@ var RecoveryPanel = ({ port, lauf }) => {
|
|
|
16941
17133
|
}
|
|
16942
17134
|
)
|
|
16943
17135
|
] }),
|
|
16944
|
-
meldung && /* @__PURE__ */ (0,
|
|
16945
|
-
bewertung && /* @__PURE__ */ (0,
|
|
16946
|
-
bewertung && bewertung.diagnostics.length > 0 && /* @__PURE__ */ (0,
|
|
16947
|
-
/* @__PURE__ */ (0,
|
|
16948
|
-
/* @__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" })
|
|
16949
17141
|
] }),
|
|
16950
|
-
gesichert && /* @__PURE__ */ (0,
|
|
17142
|
+
gesichert && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("p", { "data-testid": "rettung-gesichert", children: [
|
|
16951
17143
|
"Gespeichert als Arbeitsfassung ",
|
|
16952
|
-
/* @__PURE__ */ (0,
|
|
17144
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("code", { children: gesichert.recovery_id }),
|
|
16953
17145
|
gesichert.edited ? " (bearbeitet)" : " (unver\xE4ndert)",
|
|
16954
17146
|
". Das Original des Laufs ist davon unber\xFChrt."
|
|
16955
17147
|
] })
|
|
@@ -16958,7 +17150,7 @@ var RecoveryPanel = ({ port, lauf }) => {
|
|
|
16958
17150
|
|
|
16959
17151
|
// src/features/szenen/areas/ChainActions.tsx
|
|
16960
17152
|
var import_react47 = require("react");
|
|
16961
|
-
var
|
|
17153
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
16962
17154
|
var ChainActions = ({ port, runId, recoveryId }) => {
|
|
16963
17155
|
const [graph, setGraph] = (0, import_react47.useState)(null);
|
|
16964
17156
|
const [meldung, setMeldung] = (0, import_react47.useState)(null);
|
|
@@ -16984,14 +17176,14 @@ var ChainActions = ({ port, runId, recoveryId }) => {
|
|
|
16984
17176
|
setLaeuft(false);
|
|
16985
17177
|
}
|
|
16986
17178
|
}
|
|
16987
|
-
return /* @__PURE__ */ (0,
|
|
16988
|
-
/* @__PURE__ */ (0,
|
|
16989
|
-
/* @__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: [
|
|
16990
17182
|
"Szene, Aufbau und Shot entstehen als zusammenh\xE4ngende Entw\xFCrfe. Daraus l\xE4sst sich sofort ein Prompt erzeugen \u2014 ",
|
|
16991
|
-
/* @__PURE__ */ (0,
|
|
17183
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("strong", { children: "ohne" }),
|
|
16992
17184
|
" vorher etwas zu ver\xF6ffentlichen."
|
|
16993
17185
|
] }),
|
|
16994
|
-
!graph && /* @__PURE__ */ (0,
|
|
17186
|
+
!graph && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
16995
17187
|
"button",
|
|
16996
17188
|
{
|
|
16997
17189
|
type: "button",
|
|
@@ -17001,34 +17193,34 @@ var ChainActions = ({ port, runId, recoveryId }) => {
|
|
|
17001
17193
|
children: "Als Draft-Kette \xFCbernehmen"
|
|
17002
17194
|
}
|
|
17003
17195
|
),
|
|
17004
|
-
graph && /* @__PURE__ */ (0,
|
|
17005
|
-
/* @__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: [
|
|
17006
17198
|
"Kette ",
|
|
17007
|
-
/* @__PURE__ */ (0,
|
|
17199
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("code", { children: graph.chainId }),
|
|
17008
17200
|
" angelegt. Kein Knoten ist ver\xF6ffentlicht."
|
|
17009
17201
|
] }),
|
|
17010
|
-
/* @__PURE__ */ (0,
|
|
17011
|
-
/* @__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 }),
|
|
17012
17204
|
" \u2014 Element ",
|
|
17013
|
-
/* @__PURE__ */ (0,
|
|
17205
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("code", { children: k.elementId }),
|
|
17014
17206
|
", Entwurf ",
|
|
17015
|
-
/* @__PURE__ */ (0,
|
|
17207
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("code", { children: k.draftId }),
|
|
17016
17208
|
" (Revision ",
|
|
17017
17209
|
k.revision,
|
|
17018
17210
|
")"
|
|
17019
17211
|
] }, k.type)) }),
|
|
17020
|
-
graph.missing.length > 0 && /* @__PURE__ */ (0,
|
|
17021
|
-
/* @__PURE__ */ (0,
|
|
17022
|
-
/* @__PURE__ */ (0,
|
|
17023
|
-
/* @__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 }),
|
|
17024
17216
|
": ",
|
|
17025
17217
|
m.reason
|
|
17026
17218
|
] }, m.type)) })
|
|
17027
17219
|
] })
|
|
17028
17220
|
] }),
|
|
17029
|
-
shotKnoten && /* @__PURE__ */ (0,
|
|
17030
|
-
/* @__PURE__ */ (0,
|
|
17031
|
-
/* @__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)(
|
|
17032
17224
|
PromptStartAction,
|
|
17033
17225
|
{
|
|
17034
17226
|
port,
|
|
@@ -17036,12 +17228,12 @@ var ChainActions = ({ port, runId, recoveryId }) => {
|
|
|
17036
17228
|
}
|
|
17037
17229
|
)
|
|
17038
17230
|
] }),
|
|
17039
|
-
meldung && /* @__PURE__ */ (0,
|
|
17231
|
+
meldung && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "szenen-error", role: "alert", "data-testid": "kette-meldung", children: meldung })
|
|
17040
17232
|
] });
|
|
17041
17233
|
};
|
|
17042
17234
|
|
|
17043
17235
|
// src/features/szenen/areas/DeriveArea.tsx
|
|
17044
|
-
var
|
|
17236
|
+
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
17045
17237
|
var MODUS_TEXT = {
|
|
17046
17238
|
prompt: "Nur Prompt",
|
|
17047
17239
|
image: "Nur Bild",
|
|
@@ -17059,15 +17251,15 @@ var BelegFeld = ({ titel, text, testId, offen }) => {
|
|
|
17059
17251
|
setKopiert(false);
|
|
17060
17252
|
}
|
|
17061
17253
|
}
|
|
17062
|
-
return /* @__PURE__ */ (0,
|
|
17063
|
-
/* @__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: [
|
|
17064
17256
|
titel,
|
|
17065
17257
|
" (",
|
|
17066
17258
|
text.length,
|
|
17067
17259
|
" Zeichen)"
|
|
17068
17260
|
] }),
|
|
17069
|
-
/* @__PURE__ */ (0,
|
|
17070
|
-
/* @__PURE__ */ (0,
|
|
17261
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "szenen-beleg__kopf", children: [
|
|
17262
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
17071
17263
|
"button",
|
|
17072
17264
|
{
|
|
17073
17265
|
type: "button",
|
|
@@ -17076,9 +17268,9 @@ var BelegFeld = ({ titel, text, testId, offen }) => {
|
|
|
17076
17268
|
children: "Kopieren"
|
|
17077
17269
|
}
|
|
17078
17270
|
),
|
|
17079
|
-
kopiert && /* @__PURE__ */ (0,
|
|
17271
|
+
kopiert && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { "data-testid": `kopiert-${testId}`, children: "Kopiert." })
|
|
17080
17272
|
] }),
|
|
17081
|
-
/* @__PURE__ */ (0,
|
|
17273
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
17082
17274
|
"textarea",
|
|
17083
17275
|
{
|
|
17084
17276
|
ref: feld,
|
|
@@ -17092,8 +17284,8 @@ var BelegFeld = ({ titel, text, testId, offen }) => {
|
|
|
17092
17284
|
)
|
|
17093
17285
|
] });
|
|
17094
17286
|
};
|
|
17095
|
-
var Belege = ({ lauf }) => /* @__PURE__ */ (0,
|
|
17096
|
-
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)(
|
|
17097
17289
|
BelegFeld,
|
|
17098
17290
|
{
|
|
17099
17291
|
titel: "Rohantwort des Modells",
|
|
@@ -17104,9 +17296,9 @@ var Belege = ({ lauf }) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div",
|
|
|
17104
17296
|
) : (
|
|
17105
17297
|
/* Kein Text ist auch eine Auskunft: Der Provider hat nichts geliefert,
|
|
17106
17298
|
das Modell hat also nicht „falsch geantwortet". */
|
|
17107
|
-
/* @__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)." })
|
|
17108
17300
|
),
|
|
17109
|
-
/* @__PURE__ */ (0,
|
|
17301
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
17110
17302
|
BelegFeld,
|
|
17111
17303
|
{
|
|
17112
17304
|
titel: "Systemprompt dieses Laufs",
|
|
@@ -17118,18 +17310,18 @@ var Belege = ({ lauf }) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div",
|
|
|
17118
17310
|
] });
|
|
17119
17311
|
var Herkunft = ({ lauf }) => {
|
|
17120
17312
|
if (!lauf.follows) return null;
|
|
17121
|
-
return /* @__PURE__ */ (0,
|
|
17313
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("p", { className: "szenen-run__herkunft", "data-testid": "folgt-auf", children: [
|
|
17122
17314
|
"Folgt auf Lauf ",
|
|
17123
|
-
/* @__PURE__ */ (0,
|
|
17315
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("code", { children: lauf.follows.run_id }),
|
|
17124
17316
|
".",
|
|
17125
17317
|
lauf.status === "succeeded" && " Im Folgelauf gelungen."
|
|
17126
17318
|
] });
|
|
17127
17319
|
};
|
|
17128
17320
|
var Widerspruch = ({ lauf }) => {
|
|
17129
17321
|
if (!lauf.reply) return null;
|
|
17130
|
-
return /* @__PURE__ */ (0,
|
|
17131
|
-
/* @__PURE__ */ (0,
|
|
17132
|
-
/* @__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 })
|
|
17133
17325
|
] });
|
|
17134
17326
|
};
|
|
17135
17327
|
function readAsBase642(datei) {
|
|
@@ -17236,10 +17428,10 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
|
|
|
17236
17428
|
if (item.images.length === 1) onWaehlen(deckbild);
|
|
17237
17429
|
else setFokus(item.itemRef === fokus ? null : item.itemRef);
|
|
17238
17430
|
}
|
|
17239
|
-
return /* @__PURE__ */ (0,
|
|
17240
|
-
/* @__PURE__ */ (0,
|
|
17241
|
-
/* @__PURE__ */ (0,
|
|
17242
|
-
/* @__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)(
|
|
17243
17435
|
"input",
|
|
17244
17436
|
{
|
|
17245
17437
|
type: "search",
|
|
@@ -17253,23 +17445,23 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
|
|
|
17253
17445
|
}
|
|
17254
17446
|
)
|
|
17255
17447
|
] }),
|
|
17256
|
-
items.status === "loading" && /* @__PURE__ */ (0,
|
|
17257
|
-
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: [
|
|
17258
17450
|
"Der Bildbestand ist nicht lesbar: ",
|
|
17259
17451
|
items.message
|
|
17260
17452
|
] }),
|
|
17261
|
-
items.status === "ready" && werke.length === 0 && /* @__PURE__ */ (0,
|
|
17262
|
-
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)(
|
|
17263
17455
|
"div",
|
|
17264
17456
|
{
|
|
17265
17457
|
className: "szenen-bildwahl__browser",
|
|
17266
17458
|
ref: browser,
|
|
17267
17459
|
"data-testid": "bildwahl-werke",
|
|
17268
|
-
children: /* @__PURE__ */ (0,
|
|
17460
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("ul", { className: "szenen-bildwahl__raster", children: werke.map((item) => {
|
|
17269
17461
|
const deckbild = item.images[0];
|
|
17270
17462
|
if (!deckbild) return null;
|
|
17271
17463
|
const fokussiert = item.itemRef === fokus;
|
|
17272
|
-
return /* @__PURE__ */ (0,
|
|
17464
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("li", { "data-media-ref": deckbild.mediaRef, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
17273
17465
|
"button",
|
|
17274
17466
|
{
|
|
17275
17467
|
type: "button",
|
|
@@ -17279,7 +17471,7 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
|
|
|
17279
17471
|
"data-testid": `item-waehlen-${item.itemRef}`,
|
|
17280
17472
|
title: item.title,
|
|
17281
17473
|
children: [
|
|
17282
|
-
/* @__PURE__ */ (0,
|
|
17474
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
17283
17475
|
Kachel,
|
|
17284
17476
|
{
|
|
17285
17477
|
quelle: quelleVon(deckbild),
|
|
@@ -17287,7 +17479,7 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
|
|
|
17287
17479
|
...gescheitert.has(deckbild.mediaRef) ? { leerText: "Vorschau fehlt" } : {}
|
|
17288
17480
|
}
|
|
17289
17481
|
),
|
|
17290
|
-
item.images.length > 1 && /* @__PURE__ */ (0,
|
|
17482
|
+
item.images.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
17291
17483
|
"span",
|
|
17292
17484
|
{
|
|
17293
17485
|
className: "szenen-bildwahl__anzahl",
|
|
@@ -17298,22 +17490,22 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
|
|
|
17298
17490
|
]
|
|
17299
17491
|
}
|
|
17300
17492
|
),
|
|
17301
|
-
/* @__PURE__ */ (0,
|
|
17493
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "szenen-bildwahl__kacheltitel", children: item.title })
|
|
17302
17494
|
]
|
|
17303
17495
|
}
|
|
17304
17496
|
) }, item.itemRef);
|
|
17305
17497
|
}) })
|
|
17306
17498
|
}
|
|
17307
17499
|
),
|
|
17308
|
-
werk && /* @__PURE__ */ (0,
|
|
17309
|
-
/* @__PURE__ */ (0,
|
|
17310
|
-
/* @__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: [
|
|
17311
17503
|
werk.title,
|
|
17312
17504
|
" \xB7 ",
|
|
17313
17505
|
werk.images.length,
|
|
17314
17506
|
" Bilder"
|
|
17315
17507
|
] }),
|
|
17316
|
-
/* @__PURE__ */ (0,
|
|
17508
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
17317
17509
|
"button",
|
|
17318
17510
|
{
|
|
17319
17511
|
type: "button",
|
|
@@ -17324,7 +17516,7 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
|
|
|
17324
17516
|
}
|
|
17325
17517
|
)
|
|
17326
17518
|
] }),
|
|
17327
|
-
/* @__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)(
|
|
17328
17520
|
"button",
|
|
17329
17521
|
{
|
|
17330
17522
|
type: "button",
|
|
@@ -17334,7 +17526,7 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
|
|
|
17334
17526
|
"data-testid": `bild-waehlen-${bild.mediaRef}`,
|
|
17335
17527
|
title: bild.title,
|
|
17336
17528
|
children: [
|
|
17337
|
-
/* @__PURE__ */ (0,
|
|
17529
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
17338
17530
|
Kachel,
|
|
17339
17531
|
{
|
|
17340
17532
|
quelle: quelleVon(bild),
|
|
@@ -17342,7 +17534,7 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
|
|
|
17342
17534
|
...gescheitert.has(bild.mediaRef) ? { leerText: "Vorschau fehlt" } : {}
|
|
17343
17535
|
}
|
|
17344
17536
|
),
|
|
17345
|
-
/* @__PURE__ */ (0,
|
|
17537
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "szenen-bildwahl__nummer", children: [
|
|
17346
17538
|
"#",
|
|
17347
17539
|
position + 1
|
|
17348
17540
|
] })
|
|
@@ -17350,10 +17542,10 @@ var BildWaehler = ({ port, gewaehlt, onWaehlen, onEntfernen }) => {
|
|
|
17350
17542
|
}
|
|
17351
17543
|
) }, bild.mediaRef)) })
|
|
17352
17544
|
] }),
|
|
17353
|
-
gewaehlt.length > 0 && /* @__PURE__ */ (0,
|
|
17354
|
-
/* @__PURE__ */ (0,
|
|
17355
|
-
/* @__PURE__ */ (0,
|
|
17356
|
-
/* @__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)(
|
|
17357
17549
|
"button",
|
|
17358
17550
|
{
|
|
17359
17551
|
type: "button",
|
|
@@ -17389,6 +17581,15 @@ var DeriveArea = ({ port, contextId }) => {
|
|
|
17389
17581
|
() => port.derivation.listPolicies(contextId),
|
|
17390
17582
|
[port, contextId]
|
|
17391
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]);
|
|
17392
17593
|
const gewaehlt = regeln.status === "ready" ? regeln.value.find((r) => r.versionId === policyVersionId) ?? null : null;
|
|
17393
17594
|
async function starten(folgt) {
|
|
17394
17595
|
if (!gewaehlt) {
|
|
@@ -17463,12 +17664,12 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
|
|
|
17463
17664
|
const text = beanstandung ?? vorgabeBeanstandung(satz);
|
|
17464
17665
|
const bilderFehlen = satz.input.images.some((bild) => bild.kind !== "library");
|
|
17465
17666
|
const bereit = Boolean(text.trim()) && !bilderFehlen && !laeuft;
|
|
17466
|
-
return /* @__PURE__ */ (0,
|
|
17467
|
-
/* @__PURE__ */ (0,
|
|
17468
|
-
/* @__PURE__ */ (0,
|
|
17469
|
-
/* @__PURE__ */ (0,
|
|
17470
|
-
/* @__PURE__ */ (0,
|
|
17471
|
-
/* @__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)(
|
|
17472
17673
|
"textarea",
|
|
17473
17674
|
{
|
|
17474
17675
|
rows: 4,
|
|
@@ -17478,9 +17679,9 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
|
|
|
17478
17679
|
}
|
|
17479
17680
|
)
|
|
17480
17681
|
] }),
|
|
17481
|
-
/* @__PURE__ */ (0,
|
|
17482
|
-
/* @__PURE__ */ (0,
|
|
17483
|
-
/* @__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)(
|
|
17484
17685
|
"textarea",
|
|
17485
17686
|
{
|
|
17486
17687
|
rows: 2,
|
|
@@ -17491,8 +17692,8 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
|
|
|
17491
17692
|
}
|
|
17492
17693
|
)
|
|
17493
17694
|
] }),
|
|
17494
|
-
bilderFehlen && /* @__PURE__ */ (0,
|
|
17495
|
-
/* @__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)(
|
|
17496
17697
|
"button",
|
|
17497
17698
|
{
|
|
17498
17699
|
type: "button",
|
|
@@ -17532,19 +17733,19 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
|
|
|
17532
17733
|
setMeldung(ergebnis.message);
|
|
17533
17734
|
}
|
|
17534
17735
|
}
|
|
17535
|
-
return /* @__PURE__ */ (0,
|
|
17536
|
-
/* @__PURE__ */ (0,
|
|
17537
|
-
regeln.status === "loading" && /* @__PURE__ */ (0,
|
|
17538
|
-
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: [
|
|
17539
17740
|
"Die Regeln konnten nicht gelesen werden: ",
|
|
17540
17741
|
regeln.message
|
|
17541
17742
|
] }),
|
|
17542
|
-
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: [
|
|
17543
17744
|
"In diesem Kontext ist keine Rekonstruktionsregel (",
|
|
17544
|
-
/* @__PURE__ */ (0,
|
|
17545
|
-
') 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.'
|
|
17546
17747
|
] }),
|
|
17547
|
-
regeln.status === "ready" && regeln.value.length > 0 && /* @__PURE__ */ (0,
|
|
17748
|
+
regeln.status === "ready" && regeln.value.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
17548
17749
|
"form",
|
|
17549
17750
|
{
|
|
17550
17751
|
className: "szenen-form",
|
|
@@ -17553,17 +17754,17 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
|
|
|
17553
17754
|
void starten();
|
|
17554
17755
|
},
|
|
17555
17756
|
children: [
|
|
17556
|
-
/* @__PURE__ */ (0,
|
|
17557
|
-
/* @__PURE__ */ (0,
|
|
17558
|
-
/* @__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)(
|
|
17559
17760
|
"select",
|
|
17560
17761
|
{
|
|
17561
17762
|
value: policyVersionId,
|
|
17562
17763
|
onChange: (e) => setPolicyVersionId(e.target.value),
|
|
17563
17764
|
"data-testid": "regel-auswahl",
|
|
17564
17765
|
children: [
|
|
17565
|
-
/* @__PURE__ */ (0,
|
|
17566
|
-
|
|
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: [
|
|
17567
17768
|
regel.title,
|
|
17568
17769
|
" (",
|
|
17569
17770
|
regel.produces.join(", "),
|
|
@@ -17573,21 +17774,21 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
|
|
|
17573
17774
|
}
|
|
17574
17775
|
)
|
|
17575
17776
|
] }),
|
|
17576
|
-
/* @__PURE__ */ (0,
|
|
17577
|
-
/* @__PURE__ */ (0,
|
|
17578
|
-
/* @__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)(
|
|
17579
17780
|
"select",
|
|
17580
17781
|
{
|
|
17581
17782
|
value: modus,
|
|
17582
17783
|
onChange: (e) => setModus(e.target.value),
|
|
17583
17784
|
"data-testid": "modus-auswahl",
|
|
17584
|
-
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))
|
|
17585
17786
|
}
|
|
17586
17787
|
)
|
|
17587
17788
|
] }),
|
|
17588
|
-
modus !== "image" && /* @__PURE__ */ (0,
|
|
17589
|
-
/* @__PURE__ */ (0,
|
|
17590
|
-
/* @__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)(
|
|
17591
17792
|
"textarea",
|
|
17592
17793
|
{
|
|
17593
17794
|
value: promptText,
|
|
@@ -17597,8 +17798,8 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
|
|
|
17597
17798
|
}
|
|
17598
17799
|
)
|
|
17599
17800
|
] }),
|
|
17600
|
-
modus !== "prompt" && /* @__PURE__ */ (0,
|
|
17601
|
-
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)(
|
|
17602
17803
|
BildWaehler,
|
|
17603
17804
|
{
|
|
17604
17805
|
port,
|
|
@@ -17607,9 +17808,9 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
|
|
|
17607
17808
|
onEntfernen: (ref) => setBestandsbilder((v) => v.filter((b) => b.mediaRef !== ref))
|
|
17608
17809
|
}
|
|
17609
17810
|
),
|
|
17610
|
-
/* @__PURE__ */ (0,
|
|
17611
|
-
/* @__PURE__ */ (0,
|
|
17612
|
-
/* @__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)(
|
|
17613
17814
|
"input",
|
|
17614
17815
|
{
|
|
17615
17816
|
type: "file",
|
|
@@ -17619,40 +17820,40 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
|
|
|
17619
17820
|
"data-testid": "bild-eingabe"
|
|
17620
17821
|
}
|
|
17621
17822
|
),
|
|
17622
|
-
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." })
|
|
17623
17824
|
] })
|
|
17624
17825
|
] }),
|
|
17625
|
-
/* @__PURE__ */ (0,
|
|
17826
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "submit", disabled: !gewaehlt || laeuft, "data-testid": "lauf-starten", children: laeuft ? "Rekonstruiert \u2026" : "Rekonstruieren" })
|
|
17626
17827
|
]
|
|
17627
17828
|
}
|
|
17628
17829
|
),
|
|
17629
|
-
meldung && /* @__PURE__ */ (0,
|
|
17630
|
-
lauf && lauf.status === "failed" && /* @__PURE__ */ (0,
|
|
17631
|
-
/* @__PURE__ */ (0,
|
|
17632
|
-
/* @__PURE__ */ (0,
|
|
17633
|
-
/* @__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 }),
|
|
17634
17835
|
" \u2014 ",
|
|
17635
17836
|
lauf.failure?.message
|
|
17636
17837
|
] }),
|
|
17637
|
-
lauf.failure?.diagnostics && lauf.failure.diagnostics.length > 0 && /* @__PURE__ */ (0,
|
|
17638
|
-
/* @__PURE__ */ (0,
|
|
17639
|
-
/* @__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" })
|
|
17640
17841
|
] }),
|
|
17641
|
-
/* @__PURE__ */ (0,
|
|
17642
|
-
/* @__PURE__ */ (0,
|
|
17643
|
-
/* @__PURE__ */ (0,
|
|
17644
|
-
/* @__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 }),
|
|
17645
17846
|
folgeblock(lauf)
|
|
17646
17847
|
] }),
|
|
17647
|
-
lauf && lauf.status === "succeeded" && /* @__PURE__ */ (0,
|
|
17648
|
-
/* @__PURE__ */ (0,
|
|
17649
|
-
/* @__PURE__ */ (0,
|
|
17650
|
-
/* @__PURE__ */ (0,
|
|
17651
|
-
/* @__PURE__ */ (0,
|
|
17652
|
-
lauf.conflicts.length > 0 && /* @__PURE__ */ (0,
|
|
17653
|
-
/* @__PURE__ */ (0,
|
|
17654
|
-
/* @__PURE__ */ (0,
|
|
17655
|
-
/* @__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 }),
|
|
17656
17857
|
": Bild sagt \u201E",
|
|
17657
17858
|
k.from_image,
|
|
17658
17859
|
'", Prompt sagt \u201E',
|
|
@@ -17660,31 +17861,31 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
|
|
|
17660
17861
|
'". Nichts davon wurde \xFCbernommen.'
|
|
17661
17862
|
] }, i)) })
|
|
17662
17863
|
] }),
|
|
17663
|
-
lauf.not_derivable.length > 0 && /* @__PURE__ */ (0,
|
|
17664
|
-
/* @__PURE__ */ (0,
|
|
17665
|
-
/* @__PURE__ */ (0,
|
|
17666
|
-
/* @__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 }),
|
|
17667
17868
|
": ",
|
|
17668
17869
|
n.reason
|
|
17669
17870
|
] }, i)) })
|
|
17670
17871
|
] }),
|
|
17671
|
-
lauf.assumptions.length > 0 && /* @__PURE__ */ (0,
|
|
17672
|
-
/* @__PURE__ */ (0,
|
|
17673
|
-
/* @__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)) })
|
|
17674
17875
|
] }),
|
|
17675
17876
|
lauf.proposals.map((vorschlag) => {
|
|
17676
17877
|
const bezug = REFERENCE_FIELD[vorschlag.type];
|
|
17677
17878
|
const fertig = uebernommen[vorschlag.type];
|
|
17678
|
-
return /* @__PURE__ */ (0,
|
|
17879
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
17679
17880
|
"article",
|
|
17680
17881
|
{
|
|
17681
17882
|
className: "szenen-proposal",
|
|
17682
17883
|
"data-testid": `vorschlag-${vorschlag.type}`,
|
|
17683
17884
|
children: [
|
|
17684
|
-
/* @__PURE__ */ (0,
|
|
17685
|
-
!fertig && /* @__PURE__ */ (0,
|
|
17686
|
-
/* @__PURE__ */ (0,
|
|
17687
|
-
/* @__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)(
|
|
17688
17889
|
"input",
|
|
17689
17890
|
{
|
|
17690
17891
|
type: "text",
|
|
@@ -17698,9 +17899,9 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
|
|
|
17698
17899
|
}
|
|
17699
17900
|
)
|
|
17700
17901
|
] }),
|
|
17701
|
-
/* @__PURE__ */ (0,
|
|
17702
|
-
bezug && !fertig && /* @__PURE__ */ (0,
|
|
17703
|
-
/* @__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)(
|
|
17704
17905
|
ReferencePicker,
|
|
17705
17906
|
{
|
|
17706
17907
|
port,
|
|
@@ -17714,30 +17915,30 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
|
|
|
17714
17915
|
}))
|
|
17715
17916
|
}
|
|
17716
17917
|
),
|
|
17717
|
-
referenzen[vorschlag.type] ? /* @__PURE__ */ (0,
|
|
17918
|
+
referenzen[vorschlag.type] ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("p", { "data-testid": `referenz-gewaehlt-${vorschlag.type}`, children: [
|
|
17718
17919
|
"Zeigt auf Version ",
|
|
17719
|
-
/* @__PURE__ */ (0,
|
|
17920
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("code", { children: referenzen[vorschlag.type] }),
|
|
17720
17921
|
"."
|
|
17721
17922
|
] }) : (
|
|
17722
17923
|
/* Der Grund steht **vor** der Handlung. Den Nutzer erst
|
|
17723
17924
|
auf einen Knopf drücken zu lassen, um ihm dann eine
|
|
17724
17925
|
Ablehnung zu zeigen, ist keine Auskunft. */
|
|
17725
|
-
/* @__PURE__ */ (0,
|
|
17926
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("p", { "data-testid": `referenz-fehlt-${vorschlag.type}`, children: [
|
|
17726
17927
|
"Diese ",
|
|
17727
|
-
/* @__PURE__ */ (0,
|
|
17928
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("strong", { children: "Einzel\xFCbernahme" }),
|
|
17728
17929
|
" braucht eine ver\xF6ffentlichte ",
|
|
17729
17930
|
bezug.refersTo,
|
|
17730
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 ",
|
|
17731
|
-
/* @__PURE__ */ (0,
|
|
17932
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("strong", { children: "Draft-Kette" }),
|
|
17732
17933
|
": Dort entstehen Szene, Aufbau und Shot zusammenh\xE4ngend, und es muss nichts ver\xF6ffentlicht werden."
|
|
17733
17934
|
] })
|
|
17734
17935
|
)
|
|
17735
17936
|
] }),
|
|
17736
|
-
fertig ? /* @__PURE__ */ (0,
|
|
17937
|
+
fertig ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("p", { className: "szenen-proposal__done", children: [
|
|
17737
17938
|
"Als Entwurf \xFCbernommen: ",
|
|
17738
|
-
/* @__PURE__ */ (0,
|
|
17939
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("code", { children: fertig }),
|
|
17739
17940
|
". Er ist noch nicht ver\xF6ffentlicht."
|
|
17740
|
-
] }) : /* @__PURE__ */ (0,
|
|
17941
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
17741
17942
|
"button",
|
|
17742
17943
|
{
|
|
17743
17944
|
type: "button",
|
|
@@ -17752,13 +17953,337 @@ ${teile.map((t) => `- ${t}`).join("\n")}`;
|
|
|
17752
17953
|
vorschlag.type
|
|
17753
17954
|
);
|
|
17754
17955
|
}),
|
|
17755
|
-
/* @__PURE__ */ (0,
|
|
17756
|
-
/* @__PURE__ */ (0,
|
|
17956
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ChainActions, { port, runId: lauf.run_id }),
|
|
17957
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Belege, { lauf }),
|
|
17757
17958
|
folgeblock(lauf)
|
|
17758
17959
|
] })
|
|
17759
17960
|
] });
|
|
17760
17961
|
};
|
|
17761
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
|
+
|
|
17762
18287
|
// src/features/szenen/SzenenView.tsx
|
|
17763
18288
|
var import_jsx_runtime47 = require("react/jsx-runtime");
|
|
17764
18289
|
var SzenenView = ({
|
|
@@ -17767,9 +18292,9 @@ var SzenenView = ({
|
|
|
17767
18292
|
initialArea,
|
|
17768
18293
|
onAreaChange
|
|
17769
18294
|
}) => {
|
|
17770
|
-
const bereiche = (0,
|
|
17771
|
-
const faehig = (0,
|
|
17772
|
-
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));
|
|
17773
18298
|
const wechsle = (id) => {
|
|
17774
18299
|
setAktiv(id);
|
|
17775
18300
|
onAreaChange?.(id);
|
|
@@ -17780,7 +18305,7 @@ var SzenenView = ({
|
|
|
17780
18305
|
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { children: [
|
|
17781
18306
|
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "szenen-view__eyebrow", children: "Vom Motiv zum Bild" }),
|
|
17782
18307
|
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("h2", { children: "Bildprompt aus einer Szene" }),
|
|
17783
|
-
/* @__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.' })
|
|
17784
18309
|
] }),
|
|
17785
18310
|
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "szenen-view__step", children: aktuellerBereich.title })
|
|
17786
18311
|
] }),
|
|
@@ -17799,7 +18324,8 @@ var SzenenView = ({
|
|
|
17799
18324
|
bereich.id
|
|
17800
18325
|
)) }),
|
|
17801
18326
|
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("section", { className: "szenen-view__area", "aria-label": aktuellerBereich.title, children: [
|
|
17802
|
-
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 }),
|
|
17803
18329
|
aktiv === "elements" && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
17804
18330
|
ElementsArea,
|
|
17805
18331
|
{
|
|
@@ -17810,9 +18336,7 @@ var SzenenView = ({
|
|
|
17810
18336
|
listTitle: "Bausteine"
|
|
17811
18337
|
}
|
|
17812
18338
|
),
|
|
17813
|
-
aktiv === "
|
|
17814
|
-
aktiv === "setup" && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(SetupArea, { port, contextId }),
|
|
17815
|
-
aktiv === "shot" && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ShotArea, { port, contextId }),
|
|
18339
|
+
aktiv === "advanced" && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(AdvancedArea, { port, contextId }),
|
|
17816
18340
|
aktiv === "media" && port.media && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(MediaArea, { port, contextId })
|
|
17817
18341
|
] }),
|
|
17818
18342
|
(!faehig.saveRunAsItem || !faehig.media) && /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("details", { className: "szenen-view__host-notes", children: [
|
|
@@ -19253,7 +19777,7 @@ function createHfEventChannel(options) {
|
|
|
19253
19777
|
let syncInFlight = null;
|
|
19254
19778
|
let snapshot = null;
|
|
19255
19779
|
let snapshotGeladen = ohneSnapshot;
|
|
19256
|
-
const refresh = async () => {
|
|
19780
|
+
const refresh = async (revision = "main") => {
|
|
19257
19781
|
if (!snapshotGeladen) {
|
|
19258
19782
|
snapshot = await loadLatestSzenenSnapshot(namespace, token);
|
|
19259
19783
|
snapshotGeladen = true;
|
|
@@ -19261,7 +19785,7 @@ function createHfEventChannel(options) {
|
|
|
19261
19785
|
knownPaths.add(`${namespace}events/${name}`);
|
|
19262
19786
|
}
|
|
19263
19787
|
}
|
|
19264
|
-
const neue = await loadPendingEvents(namespace, token, sinceTs, knownPaths);
|
|
19788
|
+
const neue = await loadPendingEvents(namespace, token, sinceTs, knownPaths, revision);
|
|
19265
19789
|
if (neue.length) cachedRawEvents = [...cachedRawEvents, ...neue];
|
|
19266
19790
|
};
|
|
19267
19791
|
const sync = () => {
|
|
@@ -19272,16 +19796,34 @@ function createHfEventChannel(options) {
|
|
|
19272
19796
|
}
|
|
19273
19797
|
return syncInFlight;
|
|
19274
19798
|
};
|
|
19799
|
+
const sicht = () => {
|
|
19800
|
+
const pending = cachedRawEvents.filter((event) => typeof event.type === "string" && event.type.startsWith(SZENEN_EVENT_PREFIX)).map((event) => ({
|
|
19801
|
+
type: event.type,
|
|
19802
|
+
payload: event.payload,
|
|
19803
|
+
clientId: event.clientId,
|
|
19804
|
+
ts: event.ts
|
|
19805
|
+
}));
|
|
19806
|
+
return mergeSnapshotAndPending(snapshot, pending).events;
|
|
19807
|
+
};
|
|
19275
19808
|
return {
|
|
19276
19809
|
async readEvents() {
|
|
19277
19810
|
await sync();
|
|
19278
|
-
|
|
19279
|
-
|
|
19280
|
-
|
|
19281
|
-
|
|
19282
|
-
|
|
19283
|
-
|
|
19284
|
-
|
|
19811
|
+
return sicht();
|
|
19812
|
+
},
|
|
19813
|
+
async readHead() {
|
|
19814
|
+
return hfRepoHead(token);
|
|
19815
|
+
},
|
|
19816
|
+
async readEventsAt(revision) {
|
|
19817
|
+
await refresh(revision);
|
|
19818
|
+
return sicht();
|
|
19819
|
+
},
|
|
19820
|
+
async appendEventAt(type, payload, parentCommit) {
|
|
19821
|
+
if (!type.startsWith(SZENEN_EVENT_PREFIX)) {
|
|
19822
|
+
throw new Error(
|
|
19823
|
+
`Event-Typ "${type}" gehoert nicht zur Domain szenen. Der geteilte Kanal wird ueber das Praefix "${SZENEN_EVENT_PREFIX}" getrennt.`
|
|
19824
|
+
);
|
|
19825
|
+
}
|
|
19826
|
+
await writeHFEvent(namespace, token, type, payload, [], { ordered: true, parentCommit });
|
|
19285
19827
|
},
|
|
19286
19828
|
async appendEvent(type, payload) {
|
|
19287
19829
|
if (!type.startsWith(SZENEN_EVENT_PREFIX)) {
|
|
@@ -19465,11 +20007,11 @@ async function resolveShotPayload(input) {
|
|
|
19465
20007
|
};
|
|
19466
20008
|
}
|
|
19467
20009
|
const shot = shotPayload;
|
|
19468
|
-
for (const
|
|
20010
|
+
for (const fehler5 of ohneKettenReferenz(
|
|
19469
20011
|
validatePayload("shot", shotPayload).errors,
|
|
19470
20012
|
chain ? "scene_setup_ref" : null
|
|
19471
20013
|
)) {
|
|
19472
|
-
diagnostics.push(diagnosticOfSchemaError(
|
|
20014
|
+
diagnostics.push(diagnosticOfSchemaError(fehler5, `Shot ${shotElementId}`));
|
|
19473
20015
|
}
|
|
19474
20016
|
let setup = null;
|
|
19475
20017
|
let report = null;
|
|
@@ -19581,8 +20123,8 @@ async function resolveScenePayload(input) {
|
|
|
19581
20123
|
"Kein Scene Setup, kein Shot: Der Lauf entsteht aus der Szene allein. Bildaufbau, Kamera und Zielmodell wurden nicht ergaenzt, sondern sind nicht vorhanden (D-135)."
|
|
19582
20124
|
];
|
|
19583
20125
|
const diagnostics = [];
|
|
19584
|
-
for (const
|
|
19585
|
-
diagnostics.push(diagnosticOfSchemaError(
|
|
20126
|
+
for (const fehler5 of validatePayload("scene", scenePayload).errors) {
|
|
20127
|
+
diagnostics.push(diagnosticOfSchemaError(fehler5, `Szene ${sceneElementId}`));
|
|
19586
20128
|
}
|
|
19587
20129
|
const faehig = sceneOnlyCapabilities(scenePayload);
|
|
19588
20130
|
return komponiere({
|
|
@@ -20472,8 +21014,13 @@ function createEventLogRunPersistence(channel) {
|
|
|
20472
21014
|
// Nur veroeffentlichte Laeufe. Ein Draft-Lauf hat eine **leere**
|
|
20473
21015
|
// shot_version_id (D-98) und wuerde bei der Suche nach '' sonst
|
|
20474
21016
|
// faelschlich mitkommen — eine stille Vermischung zweier Herkuenfte.
|
|
21017
|
+
//
|
|
21018
|
+
// Seit P1-B faellt die Entscheidung nicht mehr hier, sondern in
|
|
21019
|
+
// `groupKeyOf()`: dieselbe Regel, aus der auch die Geschwister und
|
|
21020
|
+
// damit die Rundennummer entstehen. Vorher stand sie hier **und**
|
|
21021
|
+
// — anders — in `domainService.getPromptRunWithSiblings()`.
|
|
20475
21022
|
value: collectPromptRuns(events).filter(
|
|
20476
|
-
(run) =>
|
|
21023
|
+
(run) => groupKeyOf(run) === `published:${shotVersionId}`
|
|
20477
21024
|
)
|
|
20478
21025
|
};
|
|
20479
21026
|
} catch (error) {
|
|
@@ -20495,10 +21042,9 @@ function createEventLogRunPersistence(channel) {
|
|
|
20495
21042
|
const events = await channel.readEvents();
|
|
20496
21043
|
return {
|
|
20497
21044
|
kind: "ok",
|
|
20498
|
-
value: collectPromptRuns(events).filter(
|
|
20499
|
-
|
|
20500
|
-
|
|
20501
|
-
})
|
|
21045
|
+
value: collectPromptRuns(events).filter(
|
|
21046
|
+
(run) => groupKeyOf(run) === `chain:${chainId}`
|
|
21047
|
+
)
|
|
20502
21048
|
};
|
|
20503
21049
|
} catch (error) {
|
|
20504
21050
|
return queryError(error, `Laeufe der Kette ${chainId} konnten nicht gelesen werden`);
|
|
@@ -20509,10 +21055,9 @@ function createEventLogRunPersistence(channel) {
|
|
|
20509
21055
|
const events = await channel.readEvents();
|
|
20510
21056
|
return {
|
|
20511
21057
|
kind: "ok",
|
|
20512
|
-
value: collectPromptRuns(events).filter(
|
|
20513
|
-
|
|
20514
|
-
|
|
20515
|
-
})
|
|
21058
|
+
value: collectPromptRuns(events).filter(
|
|
21059
|
+
(run) => groupKeyOf(run) === `scene:${sceneElementId}`
|
|
21060
|
+
)
|
|
20516
21061
|
};
|
|
20517
21062
|
} catch (error) {
|
|
20518
21063
|
return queryError(
|
|
@@ -20521,6 +21066,16 @@ function createEventLogRunPersistence(channel) {
|
|
|
20521
21066
|
);
|
|
20522
21067
|
}
|
|
20523
21068
|
},
|
|
21069
|
+
// P1-A: dieselbe Faltung, nur ohne Herkunftsfilter. Sie las schon bisher
|
|
21070
|
+
// in jedem der drei Fälle den **vollen** Log — hier fällt lediglich das
|
|
21071
|
+
// Aussieben weg.
|
|
21072
|
+
async listAll() {
|
|
21073
|
+
try {
|
|
21074
|
+
return { kind: "ok", value: collectPromptRuns(await channel.readEvents()) };
|
|
21075
|
+
} catch (error) {
|
|
21076
|
+
return queryError(error, "Die Prompt-Laeufe konnten nicht gelesen werden");
|
|
21077
|
+
}
|
|
21078
|
+
},
|
|
20524
21079
|
async record(record) {
|
|
20525
21080
|
try {
|
|
20526
21081
|
await channel.appendEvent(SZENEN_EVENT_TYPES.promptRunRecorded, record);
|
|
@@ -20817,7 +21372,25 @@ function createClientRunPort(options) {
|
|
|
20817
21372
|
`Laeufe der Szene ${sceneElementId} konnten nicht gelesen werden`
|
|
20818
21373
|
);
|
|
20819
21374
|
}
|
|
20820
|
-
}
|
|
21375
|
+
},
|
|
21376
|
+
// ── Die gemeinsame Prompt-Sicht (P1-A) ────────────────────────────────
|
|
21377
|
+
//
|
|
21378
|
+
// Sie wird nur angeboten, wenn die **Persistenz** aufzählen kann. Ein
|
|
21379
|
+
// Host, dessen Speicher das nicht hergibt, bekommt hier keine Operation —
|
|
21380
|
+
// und die Ansicht nennt den Grund, statt eine leere Liste zu zeigen (§8).
|
|
21381
|
+
// Der Filter läuft über `selectPromptRuns()`, dieselbe Funktion, die auch
|
|
21382
|
+
// der Server benutzt; zwei Bezugsrahmen wären zwei Wahrheiten.
|
|
21383
|
+
...typeof runs.listAll === "function" ? {
|
|
21384
|
+
async listAllRuns(filter) {
|
|
21385
|
+
try {
|
|
21386
|
+
const alle = await runs.listAll();
|
|
21387
|
+
if (alle.kind !== "ok") return alle;
|
|
21388
|
+
return { kind: "ok", value: selectPromptRuns(alle.value, filter) };
|
|
21389
|
+
} catch (error) {
|
|
21390
|
+
return queryError(error, "Die Prompt-Laeufe konnten nicht gelesen werden");
|
|
21391
|
+
}
|
|
21392
|
+
}
|
|
21393
|
+
} : {}
|
|
20821
21394
|
};
|
|
20822
21395
|
}
|
|
20823
21396
|
|
|
@@ -21131,8 +21704,8 @@ function createFlowImageProvider(generateImage) {
|
|
|
21131
21704
|
// Kein Seed: weder als Eingabe dokumentiert noch als Ausgabe
|
|
21132
21705
|
// geliefert — nichts wird erfunden (D-107).
|
|
21133
21706
|
});
|
|
21134
|
-
} catch (
|
|
21135
|
-
ersterFehler =
|
|
21707
|
+
} catch (fehler5) {
|
|
21708
|
+
ersterFehler = fehler5 instanceof Error ? fehler5.message : String(fehler5);
|
|
21136
21709
|
break;
|
|
21137
21710
|
}
|
|
21138
21711
|
}
|
|
@@ -21658,6 +22231,249 @@ function createImageGenerationPort(options) {
|
|
|
21658
22231
|
};
|
|
21659
22232
|
}
|
|
21660
22233
|
|
|
22234
|
+
// src/lib/szenen/imageJobStore.ts
|
|
22235
|
+
var istText6 = (wert) => typeof wert === "string" && wert.trim().length > 0;
|
|
22236
|
+
var SETTLED_STATUS = ["succeeded", "partial", "failed", "rejected"];
|
|
22237
|
+
function collectImageJobs(events) {
|
|
22238
|
+
const nachId = /* @__PURE__ */ new Map();
|
|
22239
|
+
for (const event of events) {
|
|
22240
|
+
if (event.type !== SZENEN_EVENT_TYPES.imageJobRequested) continue;
|
|
22241
|
+
const payload = event.payload;
|
|
22242
|
+
if (!payload || !istText6(payload.job_id) || !istText6(payload.prompt_run_id)) continue;
|
|
22243
|
+
if (nachId.has(payload.job_id)) continue;
|
|
22244
|
+
nachId.set(payload.job_id, {
|
|
22245
|
+
job_id: payload.job_id,
|
|
22246
|
+
prompt_run_id: payload.prompt_run_id,
|
|
22247
|
+
execution_target_model_key: payload.execution_target_model_key ?? "",
|
|
22248
|
+
execution_profile_id: payload.execution_profile_id ?? "",
|
|
22249
|
+
image_count: typeof payload.image_count === "number" ? payload.image_count : 0,
|
|
22250
|
+
...payload.aspect_ratio ? { aspect_ratio: payload.aspect_ratio } : {},
|
|
22251
|
+
requested_by: payload.requested_by ?? "",
|
|
22252
|
+
store_id: payload.store_id ?? "",
|
|
22253
|
+
created_at: payload.created_at ?? "",
|
|
22254
|
+
status: "pending",
|
|
22255
|
+
claim_attempts: []
|
|
22256
|
+
});
|
|
22257
|
+
}
|
|
22258
|
+
for (const event of events) {
|
|
22259
|
+
if (event.type !== SZENEN_EVENT_TYPES.imageJobClaimed) continue;
|
|
22260
|
+
const payload = event.payload;
|
|
22261
|
+
if (!payload || !istText6(payload.job_id) || !istText6(payload.claim_id)) continue;
|
|
22262
|
+
const auftrag = nachId.get(payload.job_id);
|
|
22263
|
+
if (!auftrag) continue;
|
|
22264
|
+
const anspruch = {
|
|
22265
|
+
job_id: payload.job_id,
|
|
22266
|
+
claim_id: payload.claim_id,
|
|
22267
|
+
client_id: payload.client_id ?? "",
|
|
22268
|
+
claimed_at: payload.claimed_at ?? ""
|
|
22269
|
+
};
|
|
22270
|
+
nachId.set(payload.job_id, {
|
|
22271
|
+
...auftrag,
|
|
22272
|
+
claim: auftrag.claim ?? anspruch,
|
|
22273
|
+
claim_attempts: [...auftrag.claim_attempts, anspruch],
|
|
22274
|
+
status: auftrag.status === "pending" ? "claimed" : auftrag.status
|
|
22275
|
+
});
|
|
22276
|
+
}
|
|
22277
|
+
for (const event of events) {
|
|
22278
|
+
if (event.type !== SZENEN_EVENT_TYPES.imageJobSettled) continue;
|
|
22279
|
+
const payload = event.payload;
|
|
22280
|
+
if (!payload || !istText6(payload.job_id)) continue;
|
|
22281
|
+
const auftrag = nachId.get(payload.job_id);
|
|
22282
|
+
if (!auftrag) continue;
|
|
22283
|
+
if (!auftrag.claim || auftrag.claim.claim_id !== payload.claim_id) continue;
|
|
22284
|
+
if (auftrag.status !== "claimed") continue;
|
|
22285
|
+
if (!SETTLED_STATUS.includes(payload.status)) continue;
|
|
22286
|
+
nachId.set(payload.job_id, {
|
|
22287
|
+
...auftrag,
|
|
22288
|
+
status: payload.status,
|
|
22289
|
+
...payload.generation_batch_id ? { generation_batch_id: payload.generation_batch_id } : {},
|
|
22290
|
+
...payload.item_id ? { item_id: payload.item_id } : {},
|
|
22291
|
+
...typeof payload.produced_count === "number" ? { produced_count: payload.produced_count } : {},
|
|
22292
|
+
...payload.diagnostic ? { diagnostic: payload.diagnostic } : {},
|
|
22293
|
+
settled_at: payload.settled_at ?? ""
|
|
22294
|
+
});
|
|
22295
|
+
}
|
|
22296
|
+
return [...nachId.values()];
|
|
22297
|
+
}
|
|
22298
|
+
function jobsForPromptRun(events, runId) {
|
|
22299
|
+
return collectImageJobs(events).filter((j) => j.prompt_run_id === runId);
|
|
22300
|
+
}
|
|
22301
|
+
function findImageJob(events, jobId) {
|
|
22302
|
+
return collectImageJobs(events).find((j) => j.job_id === jobId) ?? null;
|
|
22303
|
+
}
|
|
22304
|
+
function generationBatchIdOf(jobId) {
|
|
22305
|
+
return `job-${jobId}`;
|
|
22306
|
+
}
|
|
22307
|
+
|
|
22308
|
+
// src/lib/szenen/imageJobPort.ts
|
|
22309
|
+
var ANSPRUCH_VERSUCHE = 4;
|
|
22310
|
+
function fehler4(error, aktion) {
|
|
22311
|
+
return {
|
|
22312
|
+
message: `${aktion}: ${error instanceof Error ? error.message : String(error)}`
|
|
22313
|
+
};
|
|
22314
|
+
}
|
|
22315
|
+
function createImageJobPort(options) {
|
|
22316
|
+
const { channel, imageGeneration, storeId, clientId } = options;
|
|
22317
|
+
const newClaimId = options.newClaimId ?? (() => crypto.randomUUID());
|
|
22318
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
22319
|
+
return {
|
|
22320
|
+
async listJobs(runId) {
|
|
22321
|
+
try {
|
|
22322
|
+
return { kind: "ok", value: jobsForPromptRun(await channel.readEvents(), runId) };
|
|
22323
|
+
} catch (error) {
|
|
22324
|
+
return {
|
|
22325
|
+
kind: "error",
|
|
22326
|
+
...fehler4(error, `Die Bildauftraege des Laufs ${runId} konnten nicht gelesen werden`),
|
|
22327
|
+
retryable: true
|
|
22328
|
+
};
|
|
22329
|
+
}
|
|
22330
|
+
},
|
|
22331
|
+
async claimAndRun(jobId) {
|
|
22332
|
+
let letzterGrund = "";
|
|
22333
|
+
for (let versuch = 1; versuch <= ANSPRUCH_VERSUCHE; versuch += 1) {
|
|
22334
|
+
let kopf;
|
|
22335
|
+
try {
|
|
22336
|
+
kopf = await channel.readHead();
|
|
22337
|
+
} catch (error) {
|
|
22338
|
+
return {
|
|
22339
|
+
kind: "error",
|
|
22340
|
+
...fehler4(error, `Der Kopf-Commit des Bestands war nicht lesbar; Bildauftrag ${jobId} wurde nicht uebernommen`),
|
|
22341
|
+
retryable: true
|
|
22342
|
+
};
|
|
22343
|
+
}
|
|
22344
|
+
let auftrag;
|
|
22345
|
+
try {
|
|
22346
|
+
auftrag = findImageJob(await channel.readEventsAt(kopf), jobId);
|
|
22347
|
+
} catch (error) {
|
|
22348
|
+
return {
|
|
22349
|
+
kind: "error",
|
|
22350
|
+
...fehler4(error, `Der Bildauftrag ${jobId} konnte nicht gelesen werden`),
|
|
22351
|
+
retryable: true
|
|
22352
|
+
};
|
|
22353
|
+
}
|
|
22354
|
+
if (!auftrag) {
|
|
22355
|
+
return {
|
|
22356
|
+
kind: "rejected",
|
|
22357
|
+
message: `Der Bildauftrag ${jobId} steht nicht im autoritativen Log dieses Bestands.`
|
|
22358
|
+
};
|
|
22359
|
+
}
|
|
22360
|
+
if (auftrag.store_id !== storeId) {
|
|
22361
|
+
return {
|
|
22362
|
+
kind: "rejected",
|
|
22363
|
+
message: `Der Bildauftrag ${jobId} gilt fuer den Bestand "${auftrag.store_id}", dieser Host arbeitet auf "${storeId}". Nutzer, Umgebung und Bestand muessen zusammenpassen.`
|
|
22364
|
+
};
|
|
22365
|
+
}
|
|
22366
|
+
if (auftrag.status !== "pending") {
|
|
22367
|
+
return {
|
|
22368
|
+
kind: "lost",
|
|
22369
|
+
job: auftrag,
|
|
22370
|
+
message: `Der Bildauftrag ${jobId} ist bereits "${auftrag.status}"` + (auftrag.claim ? ` (Client ${auftrag.claim.client_id})` : "") + ". Er wird nicht erneut ausgefuehrt \u2014 ein zweiter Lauf waere ein zweites Mal bezahlte Rechenzeit."
|
|
22371
|
+
};
|
|
22372
|
+
}
|
|
22373
|
+
const anspruch = {
|
|
22374
|
+
job_id: jobId,
|
|
22375
|
+
claim_id: newClaimId(),
|
|
22376
|
+
client_id: clientId,
|
|
22377
|
+
claimed_at: now().toISOString()
|
|
22378
|
+
};
|
|
22379
|
+
try {
|
|
22380
|
+
await channel.appendEventAt(
|
|
22381
|
+
SZENEN_EVENT_TYPES.imageJobClaimed,
|
|
22382
|
+
anspruch,
|
|
22383
|
+
kopf
|
|
22384
|
+
);
|
|
22385
|
+
} catch (error) {
|
|
22386
|
+
if (istPreconditionFehler(error)) {
|
|
22387
|
+
letzterGrund = error instanceof Error ? error.message : String(error);
|
|
22388
|
+
continue;
|
|
22389
|
+
}
|
|
22390
|
+
return {
|
|
22391
|
+
kind: "error",
|
|
22392
|
+
...fehler4(error, `Der Anspruch auf Bildauftrag ${jobId} konnte nicht angehaengt werden`),
|
|
22393
|
+
retryable: true
|
|
22394
|
+
};
|
|
22395
|
+
}
|
|
22396
|
+
const gewonnen = {
|
|
22397
|
+
...auftrag,
|
|
22398
|
+
status: "claimed",
|
|
22399
|
+
claim: anspruch,
|
|
22400
|
+
claim_attempts: [...auftrag.claim_attempts, anspruch]
|
|
22401
|
+
};
|
|
22402
|
+
return await fuehreAus(gewonnen, anspruch, jobId);
|
|
22403
|
+
}
|
|
22404
|
+
return {
|
|
22405
|
+
kind: "error",
|
|
22406
|
+
message: `Der Bildauftrag ${jobId} liess sich in ${ANSPRUCH_VERSUCHE} Versuchen nicht exklusiv uebernehmen \u2014 der Bestand wird gerade von anderer Seite beschrieben (${letzterGrund}). Es wurde nichts ausgefuehrt und nichts bezahlt; ein neuer Versuch ist gefahrlos.`,
|
|
22407
|
+
retryable: true
|
|
22408
|
+
};
|
|
22409
|
+
}
|
|
22410
|
+
};
|
|
22411
|
+
async function fuehreAus(gewonnen, anspruch, jobId) {
|
|
22412
|
+
const ergebnis = await imageGeneration.startFromPromptRun({
|
|
22413
|
+
runId: gewonnen.prompt_run_id,
|
|
22414
|
+
generationBatchId: generationBatchIdOf(jobId),
|
|
22415
|
+
executionTargetModelKey: gewonnen.execution_target_model_key,
|
|
22416
|
+
executionProfileId: gewonnen.execution_profile_id,
|
|
22417
|
+
imageCount: gewonnen.image_count,
|
|
22418
|
+
...gewonnen.aspect_ratio ? { aspectRatio: gewonnen.aspect_ratio } : {}
|
|
22419
|
+
});
|
|
22420
|
+
const abschluss = {
|
|
22421
|
+
job_id: jobId,
|
|
22422
|
+
claim_id: anspruch.claim_id,
|
|
22423
|
+
...abschlussFelder(ergebnis, jobId),
|
|
22424
|
+
settled_at: now().toISOString()
|
|
22425
|
+
};
|
|
22426
|
+
try {
|
|
22427
|
+
await channel.appendEvent(SZENEN_EVENT_TYPES.imageJobSettled, abschluss);
|
|
22428
|
+
} catch {
|
|
22429
|
+
}
|
|
22430
|
+
const gelesen = await channel.readEvents().catch(() => null);
|
|
22431
|
+
return {
|
|
22432
|
+
kind: "ok",
|
|
22433
|
+
job: (gelesen && findImageJob(gelesen, jobId)) ?? {
|
|
22434
|
+
...gewonnen,
|
|
22435
|
+
status: abschluss.status,
|
|
22436
|
+
...abschluss.generation_batch_id ? { generation_batch_id: abschluss.generation_batch_id } : {},
|
|
22437
|
+
...abschluss.diagnostic ? { diagnostic: abschluss.diagnostic } : {},
|
|
22438
|
+
settled_at: abschluss.settled_at
|
|
22439
|
+
}
|
|
22440
|
+
};
|
|
22441
|
+
}
|
|
22442
|
+
}
|
|
22443
|
+
function istPreconditionFehler(error) {
|
|
22444
|
+
return Boolean(error && typeof error === "object" && error.name === "HfPreconditionFailed");
|
|
22445
|
+
}
|
|
22446
|
+
function abschlussFelder(ergebnis, jobId) {
|
|
22447
|
+
const batchId = generationBatchIdOf(jobId);
|
|
22448
|
+
switch (ergebnis.kind) {
|
|
22449
|
+
case "succeeded":
|
|
22450
|
+
return {
|
|
22451
|
+
status: "succeeded",
|
|
22452
|
+
generation_batch_id: batchId,
|
|
22453
|
+
item_id: ergebnis.batch.item_id,
|
|
22454
|
+
produced_count: ergebnis.batch.images.length
|
|
22455
|
+
};
|
|
22456
|
+
case "partial":
|
|
22457
|
+
return {
|
|
22458
|
+
status: "partial",
|
|
22459
|
+
generation_batch_id: batchId,
|
|
22460
|
+
item_id: ergebnis.batch.item_id,
|
|
22461
|
+
produced_count: ergebnis.produced,
|
|
22462
|
+
diagnostic: `Angefordert ${ergebnis.requested}, geliefert ${ergebnis.produced}.`
|
|
22463
|
+
};
|
|
22464
|
+
case "failed":
|
|
22465
|
+
return { status: "failed", produced_count: 0, diagnostic: ergebnis.message };
|
|
22466
|
+
case "unsupported":
|
|
22467
|
+
return {
|
|
22468
|
+
status: "rejected",
|
|
22469
|
+
produced_count: 0,
|
|
22470
|
+
diagnostic: `Zielmodell "${ergebnis.targetModelKey}" ist in dieser Umgebung nicht ausfuehrbar: ${ergebnis.reason}`
|
|
22471
|
+
};
|
|
22472
|
+
default:
|
|
22473
|
+
return { status: "rejected", produced_count: 0, diagnostic: ergebnis.message };
|
|
22474
|
+
}
|
|
22475
|
+
}
|
|
22476
|
+
|
|
21661
22477
|
// src/lib/szenen/flowViewPort.ts
|
|
21662
22478
|
var defaultContext = {
|
|
21663
22479
|
async chainFor(contextId) {
|
|
@@ -21756,10 +22572,21 @@ function createFlowSzenenViewPort(options) {
|
|
|
21756
22572
|
},
|
|
21757
22573
|
...options.now ? { now: options.now } : {}
|
|
21758
22574
|
}) : void 0;
|
|
22575
|
+
const kanalMitRevision = typeof channel.readHead === "function" && typeof channel.readEventsAt === "function" && typeof channel.appendEventAt === "function";
|
|
22576
|
+
const imageJobs = imageGeneration && hatHfZugangsdaten && kanalMitRevision ? createImageJobPort({
|
|
22577
|
+
channel,
|
|
22578
|
+
imageGeneration,
|
|
22579
|
+
// Dieselbe Form, in der der Server seinen Bestand kennt (`storeId`).
|
|
22580
|
+
storeId: `hf:${options.namespace}`,
|
|
22581
|
+
clientId: options.imageJobClientId ?? crypto.randomUUID(),
|
|
22582
|
+
...options.newImageJobClaimId ? { newClaimId: options.newImageJobClaimId } : {},
|
|
22583
|
+
...options.now ? { now: options.now } : {}
|
|
22584
|
+
}) : void 0;
|
|
21759
22585
|
const port = { elements, context, runs, derivation, imageBatchHistory };
|
|
21760
22586
|
if (imageLibrary) port.imageLibrary = imageLibrary;
|
|
21761
22587
|
if (imageBatchWrite) port.imageBatchWrite = imageBatchWrite;
|
|
21762
22588
|
if (imageGeneration) port.imageGeneration = imageGeneration;
|
|
22589
|
+
if (imageJobs) port.imageJobs = imageJobs;
|
|
21763
22590
|
return port;
|
|
21764
22591
|
}
|
|
21765
22592
|
|
|
@@ -21817,7 +22644,7 @@ function resolveBootstrapTarget(input) {
|
|
|
21817
22644
|
// src/components/AvatarArchitectApp.tsx
|
|
21818
22645
|
var import_jsx_runtime48 = require("react/jsx-runtime");
|
|
21819
22646
|
function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, initialHfToken, hfNamespace, allowDevNamespace, requireWorkspaceSelection = false, serverBaseUrl }) {
|
|
21820
|
-
(0,
|
|
22647
|
+
(0, import_react53.useEffect)(() => {
|
|
21821
22648
|
const id = "flow-styles";
|
|
21822
22649
|
if (!document.getElementById(id)) {
|
|
21823
22650
|
const style = document.createElement("style");
|
|
@@ -21826,33 +22653,33 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
21826
22653
|
document.head.appendChild(style);
|
|
21827
22654
|
}
|
|
21828
22655
|
}, []);
|
|
21829
|
-
const [showStart, setShowStart] = (0,
|
|
21830
|
-
const [layoutChoice, setLayoutChoice] = (0,
|
|
22656
|
+
const [showStart, setShowStart] = (0, import_react53.useState)(true);
|
|
22657
|
+
const [layoutChoice, setLayoutChoice] = (0, import_react53.useState)(() => {
|
|
21831
22658
|
try {
|
|
21832
22659
|
return localStorage.getItem("aa-layout") || null;
|
|
21833
22660
|
} catch {
|
|
21834
22661
|
return null;
|
|
21835
22662
|
}
|
|
21836
22663
|
});
|
|
21837
|
-
const [hfToken, setHfToken] = (0,
|
|
21838
|
-
const [hfTokenInput, setHfTokenInput] = (0,
|
|
21839
|
-
const [hfNamespaceLocal, setHfNamespaceLocal] = (0,
|
|
22664
|
+
const [hfToken, setHfToken] = (0, import_react53.useState)(initialHfToken || "");
|
|
22665
|
+
const [hfTokenInput, setHfTokenInput] = (0, import_react53.useState)(initialHfToken || "");
|
|
22666
|
+
const [hfNamespaceLocal, setHfNamespaceLocal] = (0, import_react53.useState)(() => {
|
|
21840
22667
|
try {
|
|
21841
22668
|
return normalizeSelectableNamespace(localStorage.getItem("aa-hf-namespace"));
|
|
21842
22669
|
} catch {
|
|
21843
22670
|
return DEFAULT_NAMESPACE;
|
|
21844
22671
|
}
|
|
21845
22672
|
});
|
|
21846
|
-
const [hfNamespaceFromServer, setHfNamespaceFromServer] = (0,
|
|
21847
|
-
const [serverProbeFailed, setServerProbeFailed] = (0,
|
|
21848
|
-
(0,
|
|
22673
|
+
const [hfNamespaceFromServer, setHfNamespaceFromServer] = (0, import_react53.useState)(null);
|
|
22674
|
+
const [serverProbeFailed, setServerProbeFailed] = (0, import_react53.useState)(false);
|
|
22675
|
+
(0, import_react53.useEffect)(() => {
|
|
21849
22676
|
if (hfNamespace !== void 0 || !serverBaseUrl) return;
|
|
21850
22677
|
fetch(`${serverBaseUrl}/api/status`).then((r) => r.json()).then((d) => {
|
|
21851
22678
|
if (typeof d.hfNamespace === "string" && d.hfNamespace) setHfNamespaceFromServer(d.hfNamespace);
|
|
21852
22679
|
else setServerProbeFailed(true);
|
|
21853
22680
|
}).catch(() => setServerProbeFailed(true));
|
|
21854
22681
|
}, [hfNamespace, serverBaseUrl]);
|
|
21855
|
-
const [targetConfirmed, setTargetConfirmed] = (0,
|
|
22682
|
+
const [targetConfirmed, setTargetConfirmed] = (0, import_react53.useState)(false);
|
|
21856
22683
|
const isDialogTarget = hfNamespace === void 0 && hfNamespaceFromServer === null && (!serverBaseUrl || serverProbeFailed);
|
|
21857
22684
|
const isProbingServer = hfNamespace === void 0 && !!serverBaseUrl && hfNamespaceFromServer === null && !serverProbeFailed;
|
|
21858
22685
|
const bootstrapTarget = resolveBootstrapTarget({
|
|
@@ -21863,8 +22690,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
21863
22690
|
isStartDialogOpen: isDialogTarget && !targetConfirmed
|
|
21864
22691
|
});
|
|
21865
22692
|
const effectiveNamespace = bootstrapTarget.namespace ?? "";
|
|
21866
|
-
const kontextRef = (0,
|
|
21867
|
-
const szenenPort = (0,
|
|
22693
|
+
const kontextRef = (0, import_react53.useRef)(null);
|
|
22694
|
+
const szenenPort = (0, import_react53.useMemo)(() => hfToken && effectiveNamespace ? createFlowSzenenViewPort({
|
|
21868
22695
|
generateText: onGeneratePrompt,
|
|
21869
22696
|
token: hfToken,
|
|
21870
22697
|
namespace: effectiveNamespace,
|
|
@@ -21886,10 +22713,10 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
21886
22713
|
const tagBaseResult = useTagBase(hfToken, effectiveNamespace);
|
|
21887
22714
|
const szenenKontextId = tagBaseResult.selectedContextId === null ? null : String(tagBaseResult.selectedContextId);
|
|
21888
22715
|
kontextRef.current = szenenKontextId;
|
|
21889
|
-
const [imageUploadStatus, setImageUploadStatus] = (0,
|
|
21890
|
-
const [bootstrapLog, setBootstrapLog] = (0,
|
|
21891
|
-
const [isBootstrapping, setIsBootstrapping] = (0,
|
|
21892
|
-
const [hfMissingImages, setHfMissingImages] = (0,
|
|
22716
|
+
const [imageUploadStatus, setImageUploadStatus] = (0, import_react53.useState)(/* @__PURE__ */ new Map());
|
|
22717
|
+
const [bootstrapLog, setBootstrapLog] = (0, import_react53.useState)([]);
|
|
22718
|
+
const [isBootstrapping, setIsBootstrapping] = (0, import_react53.useState)(false);
|
|
22719
|
+
const [hfMissingImages, setHfMissingImages] = (0, import_react53.useState)([]);
|
|
21893
22720
|
const syncTopSlot = /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_jsx_runtime48.Fragment, { children: [
|
|
21894
22721
|
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: [
|
|
21895
22722
|
"\u26A0 ",
|
|
@@ -21948,18 +22775,18 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
21948
22775
|
setTargetConfirmed(true);
|
|
21949
22776
|
setShowStart(false);
|
|
21950
22777
|
};
|
|
21951
|
-
const [nodes, setNodes] = (0,
|
|
21952
|
-
const [edges, setEdges] = (0,
|
|
21953
|
-
const [history, setHistory] = (0,
|
|
21954
|
-
const [galleryItems, setGalleryItems] = (0,
|
|
21955
|
-
const galleryItemsRef = (0,
|
|
21956
|
-
(0,
|
|
22778
|
+
const [nodes, setNodes] = (0, import_react53.useState)([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
|
|
22779
|
+
const [edges, setEdges] = (0, import_react53.useState)([]);
|
|
22780
|
+
const [history, setHistory] = (0, import_react53.useState)([]);
|
|
22781
|
+
const [galleryItems, setGalleryItems] = (0, import_react53.useState)([]);
|
|
22782
|
+
const galleryItemsRef = (0, import_react53.useRef)([]);
|
|
22783
|
+
(0, import_react53.useEffect)(() => {
|
|
21957
22784
|
galleryItemsRef.current = galleryItems;
|
|
21958
22785
|
}, [galleryItems]);
|
|
21959
|
-
const hfImageNotFoundRef = (0,
|
|
21960
|
-
const [galleryVisibleCount, setGalleryVisibleCount] = (0,
|
|
21961
|
-
const [historyVisibleCount, setHistoryVisibleCount] = (0,
|
|
21962
|
-
const loadThumbnailsForEntries = (0,
|
|
22786
|
+
const hfImageNotFoundRef = (0, import_react53.useRef)(/* @__PURE__ */ new Map());
|
|
22787
|
+
const [galleryVisibleCount, setGalleryVisibleCount] = (0, import_react53.useState)(20);
|
|
22788
|
+
const [historyVisibleCount, setHistoryVisibleCount] = (0, import_react53.useState)(20);
|
|
22789
|
+
const loadThumbnailsForEntries = (0, import_react53.useCallback)(async (entries) => {
|
|
21963
22790
|
for (const entry of entries) {
|
|
21964
22791
|
if (galleryItemsRef.current.find((g) => g.id === entry.id)?.base64) continue;
|
|
21965
22792
|
if (hfImageNotFoundRef.current.has(entry.id)) continue;
|
|
@@ -21978,7 +22805,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
21978
22805
|
}
|
|
21979
22806
|
}
|
|
21980
22807
|
}, [hfToken, effectiveNamespace]);
|
|
21981
|
-
(0,
|
|
22808
|
+
(0, import_react53.useEffect)(() => {
|
|
21982
22809
|
if (!hfState) return;
|
|
21983
22810
|
if (hfState.tags?.by_category) setWorkspaceTags(hfState.tags);
|
|
21984
22811
|
const hfIds = new Set(hfState.metadata.map((m) => m.id));
|
|
@@ -22005,36 +22832,36 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
22005
22832
|
return [...localOnly, ...merged].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
|
|
22006
22833
|
});
|
|
22007
22834
|
}, [hfState]);
|
|
22008
|
-
(0,
|
|
22835
|
+
(0, import_react53.useEffect)(() => {
|
|
22009
22836
|
if (!hfState) return;
|
|
22010
22837
|
const sortedMeta = [...hfState.metadata].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
|
|
22011
22838
|
const skeletons = sortedMeta.map((m) => ({ id: m.id, prompt: m.prompt, timestamp: m.timestamp, status: "done", nodeId: m.id, tags: m.tags || [] }));
|
|
22012
22839
|
const repIds = new Set(groupByPrompt(skeletons).slice(0, galleryVisibleCount).map((g) => g.representative.id));
|
|
22013
22840
|
loadThumbnailsForEntries(sortedMeta.filter((e) => repIds.has(e.id)));
|
|
22014
22841
|
}, [hfState, galleryVisibleCount, loadThumbnailsForEntries]);
|
|
22015
|
-
(0,
|
|
22842
|
+
(0, import_react53.useEffect)(() => {
|
|
22016
22843
|
if (!hfState) return;
|
|
22017
22844
|
const sortedMeta = [...hfState.metadata].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
|
|
22018
22845
|
loadThumbnailsForEntries(sortedMeta.slice(0, historyVisibleCount));
|
|
22019
22846
|
}, [hfState, historyVisibleCount, loadThumbnailsForEntries]);
|
|
22020
|
-
const [activePrompt, setActivePrompt] = (0,
|
|
22021
|
-
const [isSynthesizing, setIsSynthesizing] = (0,
|
|
22022
|
-
const [activeGenerationsCount, setActiveGenerationsCount] = (0,
|
|
22023
|
-
const [currentResult, setCurrentResult] = (0,
|
|
22024
|
-
const [focusedNodeId, setFocusedNodeId] = (0,
|
|
22025
|
-
const [leftTab, setLeftTab] = (0,
|
|
22026
|
-
const [promptFeedback, setPromptFeedback] = (0,
|
|
22027
|
-
const [lastPromptPayload, setLastPromptPayload] = (0,
|
|
22028
|
-
const [isPromptTabGenerating, setIsPromptTabGenerating] = (0,
|
|
22029
|
-
const [activeTab, setActiveTab] = (0,
|
|
22030
|
-
const [mobileTab, setMobileTab] = (0,
|
|
22031
|
-
const [middlePanel, setMiddlePanel] = (0,
|
|
22032
|
-
const [recentLabItems, setRecentLabItems] = (0,
|
|
22033
|
-
const [aspectRatio, setAspectRatio] = (0,
|
|
22034
|
-
const [selectedModel, setSelectedModel] = (0,
|
|
22035
|
-
const [seed, setSeed] = (0,
|
|
22036
|
-
const [seedMode, setSeedMode] = (0,
|
|
22037
|
-
const [imageCount, setImageCount] = (0,
|
|
22847
|
+
const [activePrompt, setActivePrompt] = (0, import_react53.useState)("");
|
|
22848
|
+
const [isSynthesizing, setIsSynthesizing] = (0, import_react53.useState)(false);
|
|
22849
|
+
const [activeGenerationsCount, setActiveGenerationsCount] = (0, import_react53.useState)(0);
|
|
22850
|
+
const [currentResult, setCurrentResult] = (0, import_react53.useState)(null);
|
|
22851
|
+
const [focusedNodeId, setFocusedNodeId] = (0, import_react53.useState)(null);
|
|
22852
|
+
const [leftTab, setLeftTab] = (0, import_react53.useState)("prompt");
|
|
22853
|
+
const [promptFeedback, setPromptFeedback] = (0, import_react53.useState)(null);
|
|
22854
|
+
const [lastPromptPayload, setLastPromptPayload] = (0, import_react53.useState)(null);
|
|
22855
|
+
const [isPromptTabGenerating, setIsPromptTabGenerating] = (0, import_react53.useState)(false);
|
|
22856
|
+
const [activeTab, setActiveTab] = (0, import_react53.useState)("history");
|
|
22857
|
+
const [mobileTab, setMobileTab] = (0, import_react53.useState)("stage");
|
|
22858
|
+
const [middlePanel, setMiddlePanel] = (0, import_react53.useState)("stage");
|
|
22859
|
+
const [recentLabItems, setRecentLabItems] = (0, import_react53.useState)([]);
|
|
22860
|
+
const [aspectRatio, setAspectRatio] = (0, import_react53.useState)("1:1");
|
|
22861
|
+
const [selectedModel, setSelectedModel] = (0, import_react53.useState)("\u{1F34C} Nano Banana Pro");
|
|
22862
|
+
const [seed, setSeed] = (0, import_react53.useState)(Math.floor(Math.random() * 1e6));
|
|
22863
|
+
const [seedMode, setSeedMode] = (0, import_react53.useState)("random");
|
|
22864
|
+
const [imageCount, setImageCount] = (0, import_react53.useState)(() => {
|
|
22038
22865
|
try {
|
|
22039
22866
|
const v = parseInt(localStorage.getItem("aa-image-count") || "", 10);
|
|
22040
22867
|
return v >= 1 && v <= 8 ? v : 4;
|
|
@@ -22056,41 +22883,41 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
22056
22883
|
{ label: "4 Bilder", value: "4" },
|
|
22057
22884
|
{ label: "8 Bilder", value: "8" }
|
|
22058
22885
|
];
|
|
22059
|
-
const [isLeftCollapsed, setIsLeftCollapsed] = (0,
|
|
22060
|
-
const [isRightCollapsed, setIsRightCollapsed] = (0,
|
|
22061
|
-
const [leftPanelWidth, setLeftPanelWidth] = (0,
|
|
22886
|
+
const [isLeftCollapsed, setIsLeftCollapsed] = (0, import_react53.useState)(false);
|
|
22887
|
+
const [isRightCollapsed, setIsRightCollapsed] = (0, import_react53.useState)(false);
|
|
22888
|
+
const [leftPanelWidth, setLeftPanelWidth] = (0, import_react53.useState)(() => {
|
|
22062
22889
|
try {
|
|
22063
22890
|
return parseInt(localStorage.getItem("aa-left-width") || "260", 10);
|
|
22064
22891
|
} catch {
|
|
22065
22892
|
return 260;
|
|
22066
22893
|
}
|
|
22067
22894
|
});
|
|
22068
|
-
const [rightPanelWidth, setRightPanelWidth] = (0,
|
|
22895
|
+
const [rightPanelWidth, setRightPanelWidth] = (0, import_react53.useState)(() => {
|
|
22069
22896
|
try {
|
|
22070
22897
|
return parseInt(localStorage.getItem("aa-right-width") || "320", 10);
|
|
22071
22898
|
} catch {
|
|
22072
22899
|
return 320;
|
|
22073
22900
|
}
|
|
22074
22901
|
});
|
|
22075
|
-
const [isPromptCollapsed, setIsPromptCollapsed] = (0,
|
|
22076
|
-
const [workspaceTags, setWorkspaceTags] = (0,
|
|
22077
|
-
const [highContrast, setHighContrast] = (0,
|
|
22902
|
+
const [isPromptCollapsed, setIsPromptCollapsed] = (0, import_react53.useState)(false);
|
|
22903
|
+
const [workspaceTags, setWorkspaceTags] = (0, import_react53.useState)(null);
|
|
22904
|
+
const [highContrast, setHighContrast] = (0, import_react53.useState)(() => {
|
|
22078
22905
|
try {
|
|
22079
22906
|
return localStorage.getItem("aa-contrast") === "high";
|
|
22080
22907
|
} catch {
|
|
22081
22908
|
return false;
|
|
22082
22909
|
}
|
|
22083
22910
|
});
|
|
22084
|
-
const [activeReferenceId, setActiveReferenceId] = (0,
|
|
22085
|
-
const [activeReferenceThumbnail, setActiveReferenceThumbnail] = (0,
|
|
22086
|
-
const [isScanningImage, setIsScanningImage] = (0,
|
|
22087
|
-
const [touchStartX, setTouchStartX] = (0,
|
|
22088
|
-
const [isFullscreen, setIsFullscreen] = (0,
|
|
22089
|
-
const [zoomScale, setZoomScale] = (0,
|
|
22090
|
-
const [zoomOffset, setZoomOffset] = (0,
|
|
22091
|
-
const lastPinchDist = (0,
|
|
22092
|
-
const lastTapTime = (0,
|
|
22093
|
-
const dragStart = (0,
|
|
22911
|
+
const [activeReferenceId, setActiveReferenceId] = (0, import_react53.useState)(null);
|
|
22912
|
+
const [activeReferenceThumbnail, setActiveReferenceThumbnail] = (0, import_react53.useState)(null);
|
|
22913
|
+
const [isScanningImage, setIsScanningImage] = (0, import_react53.useState)(false);
|
|
22914
|
+
const [touchStartX, setTouchStartX] = (0, import_react53.useState)(null);
|
|
22915
|
+
const [isFullscreen, setIsFullscreen] = (0, import_react53.useState)(false);
|
|
22916
|
+
const [zoomScale, setZoomScale] = (0, import_react53.useState)(1);
|
|
22917
|
+
const [zoomOffset, setZoomOffset] = (0, import_react53.useState)({ x: 0, y: 0 });
|
|
22918
|
+
const lastPinchDist = (0, import_react53.useRef)(null);
|
|
22919
|
+
const lastTapTime = (0, import_react53.useRef)(0);
|
|
22920
|
+
const dragStart = (0, import_react53.useRef)(null);
|
|
22094
22921
|
const openFullscreen = () => {
|
|
22095
22922
|
setIsFullscreen(true);
|
|
22096
22923
|
setZoomScale(1);
|
|
@@ -22153,7 +22980,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
22153
22980
|
setActiveReferenceId(null);
|
|
22154
22981
|
setActiveReferenceThumbnail(null);
|
|
22155
22982
|
};
|
|
22156
|
-
const labServices = (0,
|
|
22983
|
+
const labServices = (0, import_react53.useMemo)(() => {
|
|
22157
22984
|
const available = groupGenerationsToLabItems([...galleryItems, ...history]);
|
|
22158
22985
|
return {
|
|
22159
22986
|
availableItems: available,
|
|
@@ -22233,14 +23060,14 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
22233
23060
|
setIsScanningImage(false);
|
|
22234
23061
|
}
|
|
22235
23062
|
};
|
|
22236
|
-
const currentIndex = (0,
|
|
22237
|
-
const goToPrev = (0,
|
|
23063
|
+
const currentIndex = (0, import_react53.useMemo)(() => history.findIndex((h) => h.id === currentResult?.id), [history, currentResult]);
|
|
23064
|
+
const goToPrev = (0, import_react53.useCallback)(() => {
|
|
22238
23065
|
if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
|
|
22239
23066
|
}, [currentIndex, history]);
|
|
22240
|
-
const goToNext = (0,
|
|
23067
|
+
const goToNext = (0, import_react53.useCallback)(() => {
|
|
22241
23068
|
if (currentIndex < history.length - 1) setCurrentResult(history[currentIndex + 1]);
|
|
22242
23069
|
}, [currentIndex, history]);
|
|
22243
|
-
const handleGallerySelect = (0,
|
|
23070
|
+
const handleGallerySelect = (0, import_react53.useCallback)((g) => {
|
|
22244
23071
|
setCurrentResult(g);
|
|
22245
23072
|
setMobileTab("stage");
|
|
22246
23073
|
if (g.hasThumb && g.filename && hfToken && !g.fullBase64) {
|
|
@@ -22253,7 +23080,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
22253
23080
|
});
|
|
22254
23081
|
}
|
|
22255
23082
|
}, [hfToken, effectiveNamespace]);
|
|
22256
|
-
const handleTitleSet = (0,
|
|
23083
|
+
const handleTitleSet = (0, import_react53.useCallback)((id) => {
|
|
22257
23084
|
const ts = Date.now();
|
|
22258
23085
|
setGalleryItems((prev) => prev.map((g) => g.id === id ? { ...g, titleTs: ts } : g));
|
|
22259
23086
|
setHistory((prev) => prev.map((g) => g.id === id ? { ...g, titleTs: ts } : g));
|
|
@@ -22262,13 +23089,13 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
22262
23089
|
});
|
|
22263
23090
|
}
|
|
22264
23091
|
}, [hfToken, effectiveNamespace, hfWriteEvent]);
|
|
22265
|
-
const currentGroup = (0,
|
|
23092
|
+
const currentGroup = (0, import_react53.useMemo)(() => {
|
|
22266
23093
|
if (!currentResult?.prompt) return [];
|
|
22267
23094
|
const groups = groupByPrompt(galleryItems.filter((g) => g.status === "done" && !!g.base64));
|
|
22268
23095
|
const group = groups.find((gr) => gr.prompt === currentResult.prompt);
|
|
22269
23096
|
return group ? group.items : [];
|
|
22270
23097
|
}, [galleryItems, currentResult?.prompt]);
|
|
22271
|
-
(0,
|
|
23098
|
+
(0, import_react53.useEffect)(() => {
|
|
22272
23099
|
if (!currentResult?.prompt || !hfToken || !effectiveNamespace || !hfState) return;
|
|
22273
23100
|
const siblings = galleryItemsRef.current.filter(
|
|
22274
23101
|
(g) => g.prompt === currentResult.prompt && !g.base64 && !hfImageNotFoundRef.current.has(g.id)
|
|
@@ -22307,7 +23134,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
22307
23134
|
}
|
|
22308
23135
|
) : null;
|
|
22309
23136
|
useKeyboardNavigation(history, currentResult, setCurrentResult);
|
|
22310
|
-
const getSubtreeFormat = (0,
|
|
23137
|
+
const getSubtreeFormat = (0, import_react53.useCallback)((nodeId, depth = 0) => {
|
|
22311
23138
|
const node = nodes.find((n) => n.id === nodeId);
|
|
22312
23139
|
if (!node) return "";
|
|
22313
23140
|
const childrenIds = edges.filter((e) => e.source === nodeId).map((e) => e.target);
|
|
@@ -22315,7 +23142,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
22315
23142
|
return `${indent}- ${node.data.label || "(unbenannt)"}
|
|
22316
23143
|
` + childrenIds.map((id) => getSubtreeFormat(id, depth + 1)).join("");
|
|
22317
23144
|
}, [nodes, edges]);
|
|
22318
|
-
const activePath = (0,
|
|
23145
|
+
const activePath = (0, import_react53.useMemo)(() => {
|
|
22319
23146
|
if (!focusedNodeId) return /* @__PURE__ */ new Set();
|
|
22320
23147
|
const path = /* @__PURE__ */ new Set([focusedNodeId]);
|
|
22321
23148
|
let currId = focusedNodeId;
|
|
@@ -23467,7 +24294,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
23467
24294
|
}
|
|
23468
24295
|
|
|
23469
24296
|
// src/components/FaApp.tsx
|
|
23470
|
-
var
|
|
24297
|
+
var import_react54 = require("react");
|
|
23471
24298
|
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
23472
24299
|
function FaApp({
|
|
23473
24300
|
onGenerateImage,
|
|
@@ -23483,15 +24310,15 @@ function FaApp({
|
|
|
23483
24310
|
serverBaseUrl,
|
|
23484
24311
|
buildInfo
|
|
23485
24312
|
}) {
|
|
23486
|
-
const [hfNamespace, setHfNamespace] = (0,
|
|
23487
|
-
(0,
|
|
24313
|
+
const [hfNamespace, setHfNamespace] = (0, import_react54.useState)(void 0);
|
|
24314
|
+
(0, import_react54.useEffect)(() => {
|
|
23488
24315
|
if (!serverBaseUrl) return;
|
|
23489
24316
|
fetch(`${serverBaseUrl}/api/status`).then((r) => r.json()).then((d) => {
|
|
23490
24317
|
if (typeof d.hfNamespace === "string") setHfNamespace(d.hfNamespace);
|
|
23491
24318
|
}).catch(() => {
|
|
23492
24319
|
});
|
|
23493
24320
|
}, [serverBaseUrl]);
|
|
23494
|
-
const wrappedPrompt = (0,
|
|
24321
|
+
const wrappedPrompt = (0, import_react54.useMemo)(
|
|
23495
24322
|
() => alsFlowTextZugang(onGeneratePrompt),
|
|
23496
24323
|
[onGeneratePrompt]
|
|
23497
24324
|
);
|
|
@@ -23745,15 +24572,15 @@ function createInMemoryWorkspacePersistence(now = () => Date.now()) {
|
|
|
23745
24572
|
}
|
|
23746
24573
|
|
|
23747
24574
|
// node_modules/zustand/esm/react.mjs
|
|
23748
|
-
var
|
|
24575
|
+
var import_react55 = __toESM(require("react"), 1);
|
|
23749
24576
|
var identity = (arg) => arg;
|
|
23750
24577
|
function useStore(api, selector = identity) {
|
|
23751
|
-
const slice =
|
|
24578
|
+
const slice = import_react55.default.useSyncExternalStore(
|
|
23752
24579
|
api.subscribe,
|
|
23753
|
-
|
|
23754
|
-
|
|
24580
|
+
import_react55.default.useCallback(() => selector(api.getState()), [api, selector]),
|
|
24581
|
+
import_react55.default.useCallback(() => selector(api.getInitialState()), [api, selector])
|
|
23755
24582
|
);
|
|
23756
|
-
|
|
24583
|
+
import_react55.default.useDebugValue(slice);
|
|
23757
24584
|
return slice;
|
|
23758
24585
|
}
|
|
23759
24586
|
|