Merge plan and panel toggles into one composer button

The composer had two separate mode buttons (plan + panel). Fold them into a
single #planModeBtn that cycles through every operating mode in one place:
Off -> Plan -> Fusion -> MoA -> Off. Cleaner UI, one affordance instead of two.

Plan stays transient (reset on session switch, never persisted) while
Fusion/MoA persist to the session via apiUpdateSession({multiModelMode}) and
are read server-side from ctx.MultiModelMode. The compat var _planModeActive
is kept in sync (true iff mode === 'plan') so sendMessage and modals.js read
it unchanged. No text label — icon + tint only.

- index.html: drop the separate panel button, reuse #planModeBtn
- chat.css: unified states (plan/fusion/moa tints) on #planModeBtn
- chat.js: cycleCompositeMode / initCompositeMode / _renderCompositeBtn
  replace the split plan/panel functions
- chat-page.js: single wiring block, init from session.multiModelMode
- ARCHITECTURE_QUICK.md: document the unified toggle

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MelekiandClaude Opus 4.8 committed 2026-06-19 08:11:13 +02:00
1 parent 47557c9724
commit 1b203707ec
5 files changed
+103 -20

No files matched your search

+11 -1
View File
@@ -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 */
+1 -1
View File
@@ -455,7 +455,7 @@
<input type="file" id="fileInput" accept="image/jpeg,image/png,image/gif,image/webp,application/pdf" multiple hidden>
<button id="fileBtn" class="icon-btn" title="Datei anhängen"><i class="fa fa-paperclip"></i></button>
<button id="explorerBtn" class="icon-btn" title="Workspace durchsuchen"><i class="fa fa-folder"></i></button>
<button id="planModeBtn" class="icon-btn" title="Plan-Modus umschalten"><i class="fa fa-map"></i></button>
<button id="planModeBtn" class="icon-btn" title="Plan-Modus / Panel umschalten"><i class="fa fa-map"></i></button>
<textarea id="input" placeholder="Nachricht..." rows="1"></textarea>
<button id="emojiBtn" class="icon-btn" title="Emoji einfügen"><i class="fa fa-smile-o"></i></button>
<button id="send" class="icon-btn"><i class="fa fa-paper-plane"></i></button>
+84 -10
View File
@@ -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');
}
}
}
+6 -7
View File
@@ -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