diff --git a/ARCHITECTURE_QUICK.md b/ARCHITECTURE_QUICK.md
index 7372489..953934e 100644
--- a/ARCHITECTURE_QUICK.md
+++ b/ARCHITECTURE_QUICK.md
@@ -361,7 +361,7 @@ Zusätzlich zum Worker kann eine Session ein **Panel** aus mehreren Agenten habe
**SSE-Events:** `panel_start` (mode/layers), `panel_layer_start`/`panel_layer_done` (MoA-Runden), `panel_member_start` (mit `layer`), `panel_member_text`/`panel_member_tool_use`/`panel_member_tool_end`/`panel_member_tool` (live), `panel_member_done`, `panel_done`.
-**Frontend:** `stream-ui.js` rendert pro Member eine `.panel-bubble`, gekeyt per `layer_index` (MoA: gleiche Member über mehrere Runden; aktive Runde pro Index wird bei `panel_member_start` gemerkt, da Runden sequenziell laufen). `panel_layer_start` zeigt bei >1 Runde einen `.panel-layer-divider`. History: `history.js _renderPanelMemberBubble` rendert `Tag="panel"`-Messages. Picker: `#multiModelMode`-Dropdown (Aus/Fusion/MoA) + `#moaLayers` + Mehrfach-Agenten-Picker (`_openMultiPickerModal`).
+**Frontend:** `stream-ui.js` rendert pro Member eine `.panel-bubble`, gekeyt per `layer_index` (MoA: gleiche Member über mehrere Runden; aktive Runde pro Index wird bei `panel_member_start` gemerkt, da Runden sequenziell laufen). `panel_layer_start` zeigt bei >1 Runde einen `.panel-layer-divider`. History: `history.js _renderPanelMemberBubble` rendert `Tag="panel"`-Messages. Picker: `#multiModelMode`-Dropdown (Aus/Fusion/MoA) + `#moaLayers` + Mehrfach-Agenten-Picker (`_openMultiPickerModal`). Vereinter Composer-Toggle: **ein** Button `#planModeBtn` (chat.js `cycleCompositeMode`) schaltet zyklisch Aus→Plan→Fusion→MoA durch. Plan ist transient (Compat-Var `_planModeActive`), Fusion/MoA werden sofort via `apiUpdateSession({multiModelMode})` persistiert – gleiche Quelle wie der Picker.
**Web-Search Sources:**
- `web_search_result`-Messages in DB gespeichert (Sources als JSON-Array, leere werden übersprungen)
diff --git a/wwwroot/css/chat.css b/wwwroot/css/chat.css
index de94abb..834331c 100644
--- a/wwwroot/css/chat.css
+++ b/wwwroot/css/chat.css
@@ -436,12 +436,22 @@
color: var(--dangerous);
}
-/* Plan Mode Toggle */
+/* Unified Modus-Toggle: Aus / Plan / Fusion / MoA (ein einziger #planModeBtn) */
#planModeBtn.plan-mode-active {
color: var(--primary);
background: color-mix(in srgb, var(--primary) 15%, transparent);
border-radius: var(--border-radius);
}
+#planModeBtn.panel-mode-fusion {
+ color: var(--primary);
+ background: color-mix(in srgb, var(--primary) 15%, transparent);
+ border-radius: var(--border-radius);
+}
+#planModeBtn.panel-mode-moa {
+ color: var(--primary);
+ background: color-mix(in srgb, var(--primary) 28%, transparent);
+ border-radius: var(--border-radius);
+}
/* Emoji Picker */
diff --git a/wwwroot/index.html b/wwwroot/index.html
index 0ebc534..dfa54b1 100644
--- a/wwwroot/index.html
+++ b/wwwroot/index.html
@@ -455,7 +455,7 @@
-
+
diff --git a/wwwroot/js/chat.js b/wwwroot/js/chat.js
index cae2986..1885c47 100644
--- a/wwwroot/js/chat.js
+++ b/wwwroot/js/chat.js
@@ -223,20 +223,94 @@ function handleStreamEvent(eventType, data) {
}
}
-// ── Plan Mode ──
+// ── Unified Modus-Toggle (Aus / Plan / Fusion / MoA) ──
+//
+// Ein einziger Button (#planModeBtn) schaltet zyklisch durch alle Betriebsmodi:
+// Aus → Plan-Modus → Fusion (Panel) → MoA (Panel) → Aus
+//
+// Plan-Modus ist transient (wird bei Session-Wechsel zurückgesetzt, nie in der Session
+// persistiert). Fusion/MoA werden in der Session persistiert (apiUpdateSession) und
+// server-seitig aus ctx.MultiModelMode gelesen – daher reicht ein PATCH.
+//
+// _planModeActive bleibt als Compat-Variable (sendMessage + modals.js lesen sie direkt).
-let _planModeActive = false;
+let _compositeMode = 'off'; // 'off' | 'plan' | 'fusion' | 'moa'
+let _compositeHasAgents = false;
-function togglePlanMode() {
- _planModeActive = !_planModeActive;
+// Compat-Variablen – werden durch cycleCompositeMode / initCompositeMode synchron gehalten
+let _planModeActive = false; // true genau wenn _compositeMode === 'plan'
+
+const _COMPOSITE_ORDER = ['off', 'plan', 'fusion', 'moa'];
+
+function _renderCompositeBtn() {
var btn = $('#planModeBtn');
- if (btn) {
- btn.classList.toggle('plan-mode-active', _planModeActive);
- btn.title = _planModeActive ? 'Plan-Modus deaktivieren' : 'Plan-Modus aktivieren';
- }
+ if (!btn) return;
+ btn.classList.remove('plan-mode-active', 'panel-mode-fusion', 'panel-mode-moa');
+ var icon = btn.querySelector('i');
var input = $('#input');
- if (input) {
- input.placeholder = _planModeActive ? 'Plannen...' : 'Nachricht...';
+ if (_compositeMode === 'plan') {
+ btn.classList.add('plan-mode-active');
+ if (icon) icon.className = 'fa fa-map';
+ btn.title = 'Plan-Modus aktiv · klicken → Fusion';
+ if (input) input.placeholder = 'Planen...';
+ } else if (_compositeMode === 'fusion') {
+ btn.classList.add('panel-mode-fusion');
+ if (icon) icon.className = 'fa fa-users';
+ btn.title = 'Fusion aktiv · klicken → MoA';
+ if (input) input.placeholder = 'Nachricht...';
+ } else if (_compositeMode === 'moa') {
+ btn.classList.add('panel-mode-moa');
+ if (icon) icon.className = 'fa fa-clone';
+ btn.title = 'MoA aktiv · klicken → Aus';
+ if (input) input.placeholder = 'Nachricht...';
+ } else {
+ if (icon) icon.className = 'fa fa-map';
+ btn.title = 'Plan-Modus / Panel umschalten';
+ if (input) input.placeholder = 'Nachricht...';
+ }
+}
+
+// Initialisiert den Button aus der geladenen Session (bei jedem Chat-Wechsel aufrufen).
+// Plan-Modus wird immer auf 'off' zurückgesetzt (transient).
+function initCompositeMode(panelMode, hasAgents) {
+ _compositeHasAgents = !!hasAgents;
+ _compositeMode = (panelMode === 'fusion' || panelMode === 'moa') ? panelMode : 'off';
+ _planModeActive = false;
+ _renderCompositeBtn();
+}
+
+// Schaltet Aus → Plan → Fusion → MoA → Aus durch.
+// Fusion/MoA werden sofort in die Session persistiert; Plan-Modus ist transient (kein Server-Call).
+async function cycleCompositeMode() {
+ var idx = _COMPOSITE_ORDER.indexOf(_compositeMode);
+ _compositeMode = _COMPOSITE_ORDER[(idx + 1) % _COMPOSITE_ORDER.length];
+ _planModeActive = (_compositeMode === 'plan');
+ _renderCompositeBtn();
+
+ // Nur Fusion/MoA/Off brauchen einen Server-Call (Plan ist transient)
+ if (_compositeMode !== 'plan') {
+ var sid = getCurrentSessionId();
+ if (sid) {
+ try {
+ await apiUpdateSession(sid, { multiModelMode: _compositeMode });
+ } catch (e) {
+ console.error('Modus speichern fehlgeschlagen:', e);
+ if (typeof showToast === 'function') showToast('Modus konnte nicht gespeichert werden', 'error');
+ return;
+ }
+ }
+ }
+
+ if (typeof showToast === 'function') {
+ if (_compositeMode === 'off') {
+ showToast('Modus: Aus', 'info');
+ } else if (_compositeMode === 'plan') {
+ showToast('Plan-Modus aktiv', 'info');
+ } else if (!_compositeHasAgents) {
+ showToast('Panel: ' + (_compositeMode === 'moa' ? 'MoA' : 'Fusion') + ' aktiv – aber keine Panel-Agenten konfiguriert (im Session-Bearbeiten festlegen)', 'info');
+ } else {
+ showToast('Panel: ' + (_compositeMode === 'moa' ? 'MoA' : 'Fusion') + ' aktiv', 'success');
+ }
}
}
diff --git a/wwwroot/js/pages/chat-page.js b/wwwroot/js/pages/chat-page.js
index 780141f..cff1597 100644
--- a/wwwroot/js/pages/chat-page.js
+++ b/wwwroot/js/pages/chat-page.js
@@ -572,13 +572,12 @@ registerPage('chat', {
});
}
- // Plan Mode Toggle
- const planModeBtn = document.getElementById('planModeBtn');
- if (planModeBtn) {
- // Plan-Modus bei Session-Wechsel zurücksetzen
- _planModeActive = false;
- planModeBtn.classList.remove('plan-mode-active');
- planModeBtn.addEventListener('click', togglePlanMode);
+ // Unified Modus-Toggle (Aus / Plan / Fusion / MoA) – aus der Session initialisieren
+ const compositeModeBtn = document.getElementById('planModeBtn');
+ if (compositeModeBtn) {
+ const hasPanelAgents = !!(session.panelAgentIds && String(session.panelAgentIds).trim());
+ initCompositeMode(session.multiModelMode, hasPanelAgents);
+ compositeModeBtn.addEventListener('click', cycleCompositeMode);
}
// File Upload