diff --git a/CLAUDE.md b/CLAUDE.md index 7423c26..d6cacbc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -136,7 +136,7 @@ src/: static/: index.html: "Login-Seite (Magic-Link), JS inline" dashboard.html: "Hauptdashboard (Sidebar + Reiter-Ansicht + 8 Modals + Chat-Widget)" - studio.html: "Studio (3-Spalten-Werkstatt, online nur fuer info@ sichtbar - Gating rein clientseitig). Reiter Einstellungen ersetzt das Bearbeiten-Modal der klassischen Ansicht und enthaelt die Wahl des KI-Wegs je Fall." + studio.html: "Studio (3-Spalten-Werkstatt, online nur fuer info@ sichtbar - Gating rein clientseitig). Reiter Einstellungen ersetzt das Bearbeiten-Modal der klassischen Ansicht und enthaelt die Wahl des KI-Wegs je Fall. Spalte 3 hat genau EINEN Startpunkt (#ls-btn), darunter stehen die vier Schritte als reine Anzeige. Einzelne Bausteine lassen sich aus der Oberflaeche nicht mehr starten, der Server kann es weiterhin." css/: style.css: "AegisSight Design System (Dark/Light Theme, alle Komponenten)" studio.css: "Studio-spezifische Styles" @@ -144,7 +144,7 @@ src/: api.js: "REST-API-Client (fetch, Auth-Header, 30s Timeout, 403 -> Nur-Lese-Modus)" app.js: "Hauptlogik: ThemeManager, NotificationCenter, App-Objekt (181 KB)" components.js: "UI-Rendering: Sidebar, Faktenchecks, Toasts, Progress, Karte (geteilt mit Studio)" - studio.js: "Kompletter Studio-Controller (116 KB), enthaelt auch den Einstellungen-Reiter je Fall" + studio.js: "Kompletter Studio-Controller, enthaelt den Einstellungen-Reiter je Fall und die Lauf-Anzeige (_renderLauf, _schrittZustaende, laufKnopf)" chat.js: "Chat-Assistent Widget (Bedienfragen)" pipeline.js: "Grafische Analysepipeline (11 Schritte, Live-Animation)" layout.js: "Reiter-Umschalter je Lage (merkt letzten Reiter; enthaelt noch gridstack-Legacy-Stubs)" @@ -171,6 +171,7 @@ tests/: test_sammelaktionen.js: "Sammelaktionen der Seitenleiste im klassischen Dashboard, Auswahlmodus, Filterbeachtung, Loeschbestaetigung (Aufruf mit node)" test_falleinstellungen.js: "Einstellungen-Reiter im Studio, Formular fuellen, nur Geaendertes speichern, KI-Weg umstellen samt Warnhinweis, Abo nachladen (Aufruf mit node)" test_studio_struktur.py: "Strukturpruefung beider Oberflaechen ohne Browser, eindeutige IDs, jedes onclick zeigt auf eine vorhandene Methode, Cache-Buster, Design-Tokens" + test_lauf.js: "Lauf-Anzeige der Studio-Spalte, Zustand des Startknopfs, Kette, Schrittzustaende aus /freshness, Nachweis dass kein Schritt etwas startet (Aufruf mit node)" test_quellenausgabe.py: "Ausgabetreue des Quellenverzeichnisses: Nummern aus dem Lagebild statt Zeilennummern, keine Kappung, ein Verlag ein Name, Statistik deckungsgleich mit dem Verzeichnis" ``` diff --git a/src/static/css/studio.css b/src/static/css/studio.css index b183d15..9796bb8 100644 --- a/src/static/css/studio.css +++ b/src/static/css/studio.css @@ -1014,180 +1014,6 @@ es neu, die Zeile darunter meldet den Datenstand (aktuell / veraltet / laeuft). */ @keyframes st-spin { to { transform: rotate(360deg); } } -/* Kompletter Lauf: orchestriert alles, daher als Primaerknopf ueber den Karten */ -.run-all { - display: flex; align-items: center; justify-content: center; gap: 8px; - width: 100%; - padding: 9px 12px; - margin-bottom: 12px; - border-radius: var(--radius-md, 6px); - border: 1px solid var(--accent); - background: var(--accent); - color: #fff; - font-family: var(--font-title); - font-weight: 600; - font-size: 12.5px; - cursor: pointer; - transition: background .15s, opacity .15s; -} -.run-all:hover:not(:disabled) { background: var(--accent-hover); } -.run-all:disabled { opacity: .45; cursor: not-allowed; } -/* display:flex wuerde das hidden-Attribut ueberstimmen - explizit ausblenden */ -.run-all[hidden] { display: none; } -.run-all .ra-icon { display: inline-flex; } -.run-all .ra-state { display: none; } -.run-all.running .ra-icon { display: none; } -.run-all.running .ra-state { - display: inline-flex; align-items: center; gap: 6px; font-weight: 400; font-size: 11.5px; -} -.run-all.running .ra-state::before { - content: ''; width: 11px; height: 11px; flex-shrink: 0; - border: 2px solid currentColor; border-right-color: transparent; - border-radius: 50%; animation: st-spin .8s linear infinite; -} - -/* Die Karte selbst */ -.art-card { - background: var(--bg-card); - border: 1px solid var(--border); - border-radius: 10px; - margin-bottom: 8px; - overflow: hidden; - transition: border-color .15s; -} -.art-card:hover { border-color: var(--accent); } -.art-card:has(.studio-menu-item.active) { border-color: var(--accent); } -.art-card.busy { border-color: var(--accent); } -.art-card-soon { opacity: .6; } -.art-row { display: flex; align-items: stretch; } - -/* Kartenkopf: uebernimmt die Rolle des alten Menue-Eintrags, aber ohne eigenen Rahmen */ -.studio-menu .art-head { - flex: 1; min-width: 0; - display: flex; align-items: center; gap: 10px; - padding: 10px var(--sp-lg); - margin: 0; - background: none; - border: none; - border-radius: 0; - text-align: left; - cursor: pointer; - color: var(--text-primary); - font-family: var(--font-body); -} -.studio-menu .art-head:hover { background: var(--bg-hover); } -.studio-menu .art-head.active { background: var(--tint-accent-faint); } -.studio-menu .art-head-static { cursor: default; } -.studio-menu .art-head-static:hover { background: none; } -.art-head .mi-icon { flex-shrink: 0; width: 20px; height: 20px; display: inline-flex; align-items: center; justify-content: center; color: var(--accent); } -.art-head .mi-text { flex: 1; min-width: 0; } -.art-head .mi-title { - display: block; - font-family: var(--font-title); - font-weight: 600; - font-size: 13px; - line-height: 1.25; - white-space: normal; - overflow: visible; - text-overflow: clip; -} -.art-head .mi-meta { display: block; margin-top: 1px; font-size: 10.5px; color: var(--text-disabled); } - -/* Erzeuger-Aktion (rechnet das Ergebnis neu) */ -.art-run { - flex-shrink: 0; - width: 36px; - display: flex; align-items: center; justify-content: center; - background: none; - border: none; - border-left: 1px solid var(--border); - color: var(--text-disabled); - cursor: pointer; - transition: color .15s, background .15s; -} -.art-run:hover:not(:disabled) { color: var(--accent); background: var(--bg-hover); } -.art-run:disabled { opacity: .35; cursor: not-allowed; } - -/* Datenstand-Zeile */ -.art-note { - display: none; - align-items: center; - gap: var(--sp-md); - padding: 7px var(--sp-lg); - border-top: 1px solid var(--border); - font-size: 11px; - line-height: 1.35; -} -.art-note.show { display: flex; } -.art-note.fresh { background: var(--tint-success); color: var(--success); } -.art-note.stale { background: var(--tint-warning); color: var(--warning); } -.art-note.blocked { background: var(--bg-elevated); color: var(--text-disabled); } -.art-note.running { background: var(--tint-accent); color: var(--accent); } -.art-note .an-spin { - width: 11px; height: 11px; flex-shrink: 0; - border: 2px solid currentColor; border-right-color: transparent; - border-radius: 50%; animation: st-spin .8s linear infinite; -} -.art-note button { - margin-left: auto; - white-space: nowrap; - padding: 3px 9px; - font-size: 10.5px; - font-weight: 600; - border-radius: var(--radius-md, 6px); - border: 1px solid currentColor; - background: transparent; - color: inherit; - cursor: pointer; - font-family: var(--font-body); -} -.art-note button:hover { background: var(--tint-hover-subtle); } - -/* Unter-Ansichten: gehoeren zum selben Erzeuger (Analyse) */ -.art-subs { border-top: 1px solid var(--border); } -.studio-menu .art-sub { - display: flex; align-items: center; gap: 8px; - width: 100%; - padding: 6px var(--sp-lg); - margin: 0; - background: none; - border: none; - border-radius: 0; - cursor: pointer; - text-align: left; - color: var(--text-secondary); - font-family: var(--font-body); - font-size: 11.5px; -} -.studio-menu .art-sub:hover { background: var(--bg-hover); color: var(--text-primary); } -.studio-menu .art-sub.active { background: var(--tint-accent-faint); color: var(--accent); } -.art-sub .mi-icon { flex-shrink: 0; color: var(--text-disabled); display: inline-flex; } -.art-sub.active .mi-icon { color: var(--accent); } -.art-sub .mi-title { - flex: 1; min-width: 0; - font-family: var(--font-body); - font-weight: 500; - font-size: 11.5px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} -.art-sub .mi-meta { flex-shrink: 0; font-size: 10px; color: var(--text-disabled); } - -/* Export: Ausgabe-Aktion, kein Artefakt -> Fusszeile */ -.art-foot { margin-top: var(--sp-lg); padding-top: var(--sp-lg); border-top: 1px solid var(--border); } -.studio-menu .art-export { justify-content: center; gap: 8px; margin-bottom: 0; padding: 8px; font-size: 12px; } -.art-export .mi-title { font-family: var(--font-body); font-weight: 600; font-size: 12px; flex: 0 0 auto; } -.art-export .mi-icon { color: var(--accent); display: inline-flex; } - -/* "Sammeln" in der Quellenspalte (sein Ergebnis landet dort) */ -.collect-btn:disabled { opacity: .45; cursor: not-allowed; } -.collect-btn.running .cb-label { display: none; } -.collect-btn.running::after { - content: 'Sammelt …'; - display: inline-flex; align-items: center; -} - /* Karten-Container fuellt den Karte-Tab */ #map-container { width: 100%; @@ -1486,3 +1312,221 @@ .case-ai-tag.ai-anthropic { color: var(--text-disabled); } + +/* === Der Lauf (Spalte 3) ============================================== */ +/* Genau ein Startpunkt. Die vier Schritte darunter sind Anzeige, sie starten + nichts. Frueher standen hier Artefakt-Karten, in denen ein Klick links das + Ergebnis oeffnete und einer rechts es neu rechnete. Das lag zu nah + beieinander und kostete im falschen Fall Geld. */ + +.lauf-stand { + padding: 12px; + margin-bottom: 12px; + border: 1px solid var(--border); + border-radius: 8px; + background: var(--bg-card); +} + +/* Vollstaendig umlaufender Rahmen, kein einseitiger Streifen */ +.lauf-stand.betont { + border: 2px solid var(--sidebar-active, #C8A851); + padding: 11px; +} + +.ls-haupt { + font-size: 13px; + color: var(--text-primary); + margin-bottom: 2px; +} + +.ls-haupt.warn { color: var(--warning); } + +.ls-neben { + font-size: 11px; + color: var(--text-tertiary); +} + +.ls-btn { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + width: 100%; + margin-top: 12px; + padding: 10px 12px; + border-radius: 6px; + border: 1px solid var(--sidebar-active, #C8A851); + background: var(--sidebar-active, #C8A851); + color: #1A1A1A; + font-family: var(--font-title, inherit); + font-size: 13px; + font-weight: 600; + cursor: pointer; + transition: filter .15s, opacity .15s; +} + +.ls-btn:hover:not(:disabled) { filter: brightness(1.08); } + +.ls-btn:disabled { opacity: .45; cursor: not-allowed; } + +.ls-btn.abbrechen { + background: transparent; + border-color: var(--error); + color: var(--error); +} + +.ls-icon { display: inline-flex; flex-shrink: 0; } + +.ls-hinweis { + margin-top: 8px; + font-size: 11px; + color: var(--text-tertiary); + text-align: center; +} + +.ls-hinweis[hidden] { display: none; } + +/* --- Die Kette --- */ +.lauf-kette { + display: flex; + align-items: center; + padding: 4px 6px 8px; +} + +.kn { + width: 12px; + height: 12px; + flex-shrink: 0; + border-radius: 50%; + border: 2px solid var(--border); + background: var(--bg-elevated); +} + +.kn-fertig { background: var(--success); border-color: var(--success); } +.kn-veraltet { background: var(--warning); border-color: var(--warning); } +.kn-laeuft { + background: var(--sidebar-active, #C8A851); + border-color: var(--sidebar-active, #C8A851); + animation: kn-puls 1.4s ease-in-out infinite; +} +.kn-gesperrt { opacity: .45; } + +.kn-linie { flex: 1; height: 2px; background: var(--border); } +.kn-linie.aktiv { background: var(--success); } + +@keyframes kn-puls { 0%, 100% { opacity: 1; } 50% { opacity: .35; } } + +@media (prefers-reduced-motion: reduce) { + .kn-laeuft { animation: none; } +} + +.lauf-fortschritt { + font-size: 11px; + color: var(--text-tertiary); + text-align: center; + margin-bottom: 12px; +} + +/* --- Die Schritte --- */ +.lauf-schritte { margin-bottom: 4px; } + +.schritt { + border: 1px solid var(--border); + border-radius: 6px; + margin-bottom: 6px; + background: var(--bg-card); + overflow: hidden; +} + +.schritt-laeuft, .schritt-veraltet { + border: 2px solid var(--sidebar-active, #C8A851); +} + +.schritt-gesperrt { opacity: .55; } + +.schritt-kopf { + display: flex; + align-items: center; + gap: 10px; + width: 100%; + padding: 8px 10px; + background: none; + border: none; + color: var(--text-primary); + font-family: inherit; + text-align: left; + cursor: pointer; +} + +.schritt-kopf:hover { background: var(--tint-hover-subtle, rgba(255,255,255,.03)); } + +.schritt-nr { + flex-shrink: 0; + width: 20px; + height: 20px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 11px; + font-weight: 700; + background: var(--bg-elevated); + color: var(--text-tertiary); +} + +.schritt-fertig .schritt-nr { background: var(--tint-success); color: var(--success); } +.schritt-veraltet .schritt-nr { background: var(--tint-warning); color: var(--warning); } +.schritt-laeuft .schritt-nr { background: var(--tint-accent); color: var(--sidebar-active, #C8A851); } + +.schritt-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; } +.schritt-name { font-size: 12px; } +.schritt-stand { font-size: 11px; color: var(--text-tertiary); } +.schritt-fertig .schritt-stand { color: var(--success); } +.schritt-veraltet .schritt-stand { color: var(--warning); } + +.schritt-pfeil { + flex-shrink: 0; + color: var(--text-tertiary); + display: inline-flex; + transition: transform .18s; +} + +.schritt.offen .schritt-pfeil { transform: rotate(180deg); } + +.schritt-mehr { + display: none; + padding: 0 10px 10px 40px; + font-size: 11px; + line-height: 1.5; + color: var(--text-secondary); +} + +.schritt.offen .schritt-mehr { display: block; } + +/* --- Bereichstrenner im Menue --- */ +.menu-bereich { + display: flex; + align-items: center; + gap: 8px; + margin: 20px 0 6px; + font-size: 10px; + font-weight: 700; + letter-spacing: .08em; + text-transform: uppercase; + color: var(--text-tertiary); +} + +.menu-bereich::after { + content: ""; + flex: 1; + height: 1px; + background: var(--border); +} + +/* Untergeordnete Ansicht, gehoert zum Eintrag darueber */ +.studio-menu-item.mi-unter { + padding-left: 30px; + font-size: 12px; +} + +.studio-menu-item.mi-unter .mi-title { font-size: 12px; } diff --git a/src/static/dashboard.html b/src/static/dashboard.html index 6b57189..9c16903 100644 --- a/src/static/dashboard.html +++ b/src/static/dashboard.html @@ -13,7 +13,7 @@ - +