Replace mode toggle cycle with a dropdown menu
The composite mode control (#planModeBtn) now opens a dropdown menu instead of cycling, following the emoji-picker pattern: each mode is an entry with icon + label + description, the active one marked with a check. Direct jumps between modes are now possible, so selecting plan/ultracode resets the persisted panel mode to avoid running both the panel and the CLI worker. Unified the active button states. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
d5854a3f4b
commit
5b1802efee
4 files changed
+205
-77
No files matched your search
+67
-8
@@ -439,23 +439,82 @@
|
|||||||
/* Unified Modus-Toggle: Aus / Plan / Fusion / MoA (ein einziger #planModeBtn) */
|
/* Unified Modus-Toggle: Aus / Plan / Fusion / MoA (ein einziger #planModeBtn) */
|
||||||
#planModeBtn.plan-mode-active {
|
#planModeBtn.plan-mode-active {
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
background: color-mix(in srgb, var(--primary) 15%, transparent);
|
background: color-mix(in srgb, var(--primary) 25%, transparent);
|
||||||
border-radius: var(--border-radius);
|
|
||||||
}
|
}
|
||||||
#planModeBtn.panel-mode-fusion {
|
#planModeBtn.panel-mode-fusion {
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
background: color-mix(in srgb, var(--primary) 15%, transparent);
|
background: color-mix(in srgb, var(--primary) 25%, transparent);
|
||||||
border-radius: var(--border-radius);
|
|
||||||
}
|
}
|
||||||
#planModeBtn.panel-mode-moa {
|
#planModeBtn.panel-mode-moa {
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
background: color-mix(in srgb, var(--primary) 28%, transparent);
|
background: color-mix(in srgb, var(--primary) 25%, transparent);
|
||||||
border-radius: var(--border-radius);
|
|
||||||
}
|
}
|
||||||
#planModeBtn.ultracode-active {
|
#planModeBtn.ultracode-active {
|
||||||
color: var(--info);
|
color: var(--primary);
|
||||||
background: color-mix(in srgb, var(--info) 18%, transparent);
|
background: color-mix(in srgb, var(--primary) 25%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modus-Menü (Dropdown: Aus / Plan / Ultracode / Fusion / MoA) */
|
||||||
|
|
||||||
|
.composite-menu {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 10000;
|
||||||
|
background: var(--f2-bg);
|
||||||
|
border: var(--border-width) solid var(--f2-border);
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-shadow: 0 4px 16px rgb(0 0 0 / 0.25);
|
||||||
|
min-width: 16em;
|
||||||
|
padding-block: calc(var(--badge-spacing) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.composite-menu-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--badge-spacing);
|
||||||
|
padding: var(--menu-item-padding);
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
border-radius: unset;
|
||||||
|
text-align: left;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.composite-menu-item:hover {
|
||||||
|
background: var(--f3-bg);
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.composite-menu-item.active {
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.composite-menu-icon {
|
||||||
|
width: 1.3em;
|
||||||
|
text-align: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.composite-menu-text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.composite-menu-desc {
|
||||||
|
font-size: var(--font-size);
|
||||||
|
opacity: var(--semi-transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.composite-menu-check {
|
||||||
|
margin-left: auto;
|
||||||
|
flex-shrink: 0;
|
||||||
|
color: var(--primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Emoji Picker */
|
/* Emoji Picker */
|
||||||
|
|||||||
+1
-1
@@ -458,7 +458,7 @@
|
|||||||
<input type="file" id="fileInput" accept="image/jpeg,image/png,image/gif,image/webp,application/pdf" multiple hidden>
|
<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="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="explorerBtn" class="icon-btn" title="Workspace durchsuchen"><i class="fa fa-folder"></i></button>
|
||||||
<button id="planModeBtn" class="icon-btn" title="Plan-Modus / Panel umschalten"><i class="fa fa-map"></i></button>
|
<button id="planModeBtn" class="icon-btn" title="Modus wählen"><i class="fa fa-ban"></i></button>
|
||||||
<textarea id="input" placeholder="Nachricht..." rows="1"></textarea>
|
<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="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>
|
<button id="send" class="icon-btn"><i class="fa fa-paper-plane"></i></button>
|
||||||
|
|||||||
+131
-66
@@ -217,65 +217,70 @@ function handleStreamEvent(eventType, data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Unified Modus-Toggle (Aus / Plan / Fusion / MoA) ──
|
// ── Unified Modus-Menü (Aus / Plan / Ultracode / Fusion / MoA) ──
|
||||||
//
|
//
|
||||||
// Ein einziger Button (#planModeBtn) schaltet zyklisch durch alle Betriebsmodi:
|
// Der Button (#planModeBtn) öffnet ein Dropdown-Menü (Muster wie der Emoji-Picker), aus dem
|
||||||
// Aus → Plan-Modus → Fusion (Panel) → MoA (Panel) → Aus
|
// der Betriebsmodus direkt gewählt wird. Der Button zeigt weiterhin das Symbol des aktiven
|
||||||
|
// Modus; im Menü ist der aktive Eintrag markiert.
|
||||||
//
|
//
|
||||||
// Plan-Modus ist transient (wird bei Session-Wechsel zurückgesetzt, nie in der Session
|
// Plan-Modus und Ultracode sind transient (pro Request, nicht in der Session persistiert) und
|
||||||
// persistiert). Fusion/MoA werden in der Session persistiert (apiUpdateSession) und
|
// laufen ohne Panel → beim Wählen wird multiModelMode der Session auf 'off' gesetzt, damit kein
|
||||||
// server-seitig aus ctx.MultiModelMode gelesen – daher reicht ein PATCH.
|
// zuvor gewähltes Fusion/MoA-Panel nebenherläuft (das Menü erlaubt direkte Sprünge, anders als
|
||||||
|
// der frühere Zyklus, der immer durch 'off' lief). Fusion/MoA werden in der Session persistiert
|
||||||
|
// und server-seitig aus ctx.MultiModelMode gelesen.
|
||||||
//
|
//
|
||||||
// _planModeActive bleibt als Compat-Variable (sendMessage + modals.js lesen sie direkt).
|
// _planModeActive / _ultracodeActive bleiben als Compat-Variablen (sendMessage + modals.js lesen
|
||||||
|
// sie direkt).
|
||||||
|
|
||||||
|
// Metadaten je Modus – eine Quelle für Button-Render UND Menü-Aufbau: Reihenfolge im Menü,
|
||||||
|
// Icon (Button + Menü), Label, Button-Aktiv-Klasse, Input-Placeholder, kurze Beschreibung.
|
||||||
|
const _COMPOSITE_MODES = [
|
||||||
|
{ mode: 'off', icon: 'fa-ban', label: 'Aus', btnClass: null, placeholder: 'Nachricht...', desc: 'Normaler Einzel-Agent' },
|
||||||
|
{ mode: 'plan', icon: 'fa-map', label: 'Plan-Modus', btnClass: 'plan-mode-active', placeholder: 'Planen...', desc: 'Nur analysieren & planen (read-only)' },
|
||||||
|
{ mode: 'ultracode', icon: 'fa-sitemap', label: 'Ultracode', btnClass: 'ultracode-active', placeholder: 'Ultracode – als Workflow...', desc: 'Dynamischer Workflow (mehrere Subagenten)' },
|
||||||
|
{ mode: 'fusion', icon: 'fa-users', label: 'Fusion', btnClass: 'panel-mode-fusion', placeholder: 'Nachricht...', desc: 'Panel: Antworten fusionieren' },
|
||||||
|
{ mode: 'moa', icon: 'fa-clone', label: 'MoA', btnClass: 'panel-mode-moa', placeholder: 'Nachricht...', desc: 'Panel: Mixture-of-Agents' }
|
||||||
|
];
|
||||||
|
|
||||||
let _compositeMode = 'off'; // 'off' | 'plan' | 'ultracode' | 'fusion' | 'moa'
|
let _compositeMode = 'off'; // 'off' | 'plan' | 'ultracode' | 'fusion' | 'moa'
|
||||||
let _compositeHasAgents = false;
|
let _compositeHasAgents = false;
|
||||||
|
|
||||||
// Compat-Variablen – werden durch cycleCompositeMode / initCompositeMode synchron gehalten
|
// Compat-Variablen – werden durch setCompositeMode / initCompositeMode synchron gehalten
|
||||||
let _planModeActive = false; // true genau wenn _compositeMode === 'plan'
|
let _planModeActive = false; // true genau wenn _compositeMode === 'plan'
|
||||||
let _ultracodeActive = false; // true genau wenn _compositeMode === 'ultracode' (transient, pro Request)
|
let _ultracodeActive = false; // true genau wenn _compositeMode === 'ultracode' (transient, pro Request)
|
||||||
|
|
||||||
const _COMPOSITE_ORDER = ['off', 'plan', 'ultracode', 'fusion', 'moa'];
|
// Offenes Modus-Dropdown + Outside-Click-Handler (wie beim Emoji-Picker).
|
||||||
|
let _compositeMenuEl = null;
|
||||||
|
let _compositeMenuCloseHandler = null;
|
||||||
|
|
||||||
// Serialisierte Modus-Persistenz. Schnelles Durchklicken (off→plan→fusion→moa) feuert sonst
|
// Serialisierte Modus-Persistenz. Schnell hintereinander gewählte Modi feuern sonst ÜBERLAPPENDE
|
||||||
// ÜBERLAPPENDE PATCHes, deren Commit-Reihenfolge nicht garantiert ist → der zuletzt committete
|
// PATCHes, deren Commit-Reihenfolge nicht garantiert ist → ein Zwischenwert kann den letzten
|
||||||
// Zwischenwert 'fusion' kann 'moa' überschreiben. Und ein direkt danach gesendeter Chat-Request
|
// überschreiben. Und ein direkt danach gesendeter Chat-Request lädt die Session, BEVOR der Modus
|
||||||
// lädt die Session, BEVOR 'moa' committed ist → das Backend liest den alten Modus (Fusion → 1 Runde
|
// committed ist → das Backend liest den alten Modus. Die Kette garantiert (a) Reihenfolge (letzte
|
||||||
// statt MoA). Die Kette garantiert (a) Reihenfolge (letzter Klick gewinnt) und (b) dass sendMessage
|
// Wahl gewinnt) und (b) dass sendMessage das offene PATCH abwarten kann. Sie rejected nie (Fehler
|
||||||
// das offene PATCH abwarten kann. Die Kette rejected nie (Fehler werden hier geschluckt).
|
// werden hier geschluckt).
|
||||||
let _modePersist = Promise.resolve();
|
let _modePersist = Promise.resolve();
|
||||||
|
|
||||||
|
function _compositeMeta(mode) {
|
||||||
|
for (var i = 0; i < _COMPOSITE_MODES.length; i++) {
|
||||||
|
if (_COMPOSITE_MODES[i].mode === mode) return _COMPOSITE_MODES[i];
|
||||||
|
}
|
||||||
|
return _COMPOSITE_MODES[0];
|
||||||
|
}
|
||||||
|
|
||||||
function _renderCompositeBtn() {
|
function _renderCompositeBtn() {
|
||||||
var btn = $('#planModeBtn');
|
var btn = $('#planModeBtn');
|
||||||
if (!btn) return;
|
if (!btn) return;
|
||||||
btn.classList.remove('plan-mode-active', 'ultracode-active', 'panel-mode-fusion', 'panel-mode-moa');
|
btn.classList.remove('plan-mode-active', 'ultracode-active', 'panel-mode-fusion', 'panel-mode-moa');
|
||||||
|
var meta = _compositeMeta(_compositeMode);
|
||||||
var icon = btn.querySelector('i');
|
var icon = btn.querySelector('i');
|
||||||
|
if (icon) icon.className = 'fa ' + meta.icon;
|
||||||
|
if (meta.btnClass) btn.classList.add(meta.btnClass);
|
||||||
|
btn.title = _compositeMode === 'off'
|
||||||
|
? 'Modus wählen (aktuell: Aus)'
|
||||||
|
: 'Modus: ' + meta.label + ' · klicken zum Wechseln';
|
||||||
var input = $('#input');
|
var input = $('#input');
|
||||||
if (_compositeMode === 'plan') {
|
if (input) input.placeholder = meta.placeholder;
|
||||||
btn.classList.add('plan-mode-active');
|
|
||||||
if (icon) icon.className = 'fa fa-map';
|
|
||||||
btn.title = 'Plan-Modus aktiv · klicken → Ultracode';
|
|
||||||
if (input) input.placeholder = 'Planen...';
|
|
||||||
} else if (_compositeMode === 'ultracode') {
|
|
||||||
btn.classList.add('ultracode-active');
|
|
||||||
if (icon) icon.className = 'fa fa-sitemap';
|
|
||||||
btn.title = 'Ultracode aktiv (dynamischer Workflow) · klicken → Fusion';
|
|
||||||
if (input) input.placeholder = 'Ultracode – als Workflow...';
|
|
||||||
} 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).
|
// Initialisiert den Button aus der geladenen Session (bei jedem Chat-Wechsel aufrufen).
|
||||||
@@ -288,46 +293,106 @@ function initCompositeMode(panelMode, hasAgents) {
|
|||||||
_renderCompositeBtn();
|
_renderCompositeBtn();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schaltet Aus → Plan → Fusion → MoA → Aus durch.
|
// Setzt den Modus direkt (aus dem Menü gewählt).
|
||||||
// Fusion/MoA werden sofort in die Session persistiert; Plan-Modus ist transient (kein Server-Call).
|
// Persistiert das EFFEKTIVE Panel der Session (off/fusion/moa): für plan/ultracode also 'off',
|
||||||
async function cycleCompositeMode() {
|
// damit kein zuvor gewähltes Panel nebenherläuft. Plan/Ultracode selbst sind transient (Request-Flags).
|
||||||
var idx = _COMPOSITE_ORDER.indexOf(_compositeMode);
|
async function setCompositeMode(mode) {
|
||||||
_compositeMode = _COMPOSITE_ORDER[(idx + 1) % _COMPOSITE_ORDER.length];
|
if (mode === _compositeMode) return; // schon aktiv → nichts tun
|
||||||
_planModeActive = (_compositeMode === 'plan');
|
if (_compositeMeta(mode).mode !== mode) return; // unbekannter Modus → ignorieren
|
||||||
_ultracodeActive = (_compositeMode === 'ultracode');
|
|
||||||
|
_compositeMode = mode;
|
||||||
|
_planModeActive = (mode === 'plan');
|
||||||
|
_ultracodeActive = (mode === 'ultracode');
|
||||||
_renderCompositeBtn();
|
_renderCompositeBtn();
|
||||||
|
|
||||||
// Nur Fusion/MoA/Off brauchen einen Server-Call (Plan + Ultracode sind transient pro Request).
|
// Effektiver Panel-Modus: nur Fusion/MoA aktivieren ein Panel, alles andere 'off'.
|
||||||
// Auf die serialisierte Kette legen, damit aufeinanderfolgende Klicks in Reihenfolge committen
|
// Auf die serialisierte Kette legen, damit aufeinanderfolgende Wahlen in Reihenfolge committen
|
||||||
// (letzter Klick gewinnt) und sendMessage das offene PATCH abwarten kann.
|
// (letzte gewinnt) und sendMessage das offene PATCH abwarten kann.
|
||||||
if (_compositeMode !== 'plan' && _compositeMode !== 'ultracode') {
|
var panelMode = (mode === 'fusion' || mode === 'moa') ? mode : 'off';
|
||||||
var sid = getCurrentSessionId();
|
var sid = getCurrentSessionId();
|
||||||
if (sid) {
|
if (sid) {
|
||||||
var targetMode = _compositeMode;
|
_modePersist = _modePersist
|
||||||
_modePersist = _modePersist
|
.then(function () { return apiUpdateSession(sid, { multiModelMode: panelMode }); })
|
||||||
.then(function () { return apiUpdateSession(sid, { multiModelMode: targetMode }); })
|
.catch(function (e) {
|
||||||
.catch(function (e) {
|
console.error('Modus speichern fehlgeschlagen:', e);
|
||||||
console.error('Modus speichern fehlgeschlagen:', e);
|
if (typeof showToast === 'function') showToast('Modus konnte nicht gespeichert werden', 'error');
|
||||||
if (typeof showToast === 'function') showToast('Modus konnte nicht gespeichert werden', 'error');
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof showToast === 'function') {
|
if (typeof showToast === 'function') {
|
||||||
if (_compositeMode === 'off') {
|
if (mode === 'off') {
|
||||||
showToast('Modus: Aus', 'info');
|
showToast('Modus: Aus', 'info');
|
||||||
} else if (_compositeMode === 'plan') {
|
} else if (mode === 'plan') {
|
||||||
showToast('Plan-Modus aktiv', 'info');
|
showToast('Plan-Modus aktiv', 'info');
|
||||||
} else if (_compositeMode === 'ultracode') {
|
} else if (mode === 'ultracode') {
|
||||||
showToast('Ultracode aktiv – diese Anfrage läuft als dynamischer Workflow (mehr Tokens/Zeit)', 'info');
|
showToast('Ultracode aktiv – diese Anfrage läuft als dynamischer Workflow (mehr Tokens/Zeit)', 'info');
|
||||||
} else if (!_compositeHasAgents) {
|
} else if (!_compositeHasAgents) {
|
||||||
showToast('Panel: ' + (_compositeMode === 'moa' ? 'MoA' : 'Fusion') + ' aktiv – aber keine Panel-Agenten konfiguriert (im Session-Bearbeiten festlegen)', 'info');
|
showToast('Panel: ' + (mode === 'moa' ? 'MoA' : 'Fusion') + ' aktiv – aber keine Panel-Agenten konfiguriert (im Session-Bearbeiten festlegen)', 'info');
|
||||||
} else {
|
} else {
|
||||||
showToast('Panel: ' + (_compositeMode === 'moa' ? 'MoA' : 'Fusion') + ' aktiv', 'success');
|
showToast('Panel: ' + (mode === 'moa' ? 'MoA' : 'Fusion') + ' aktiv', 'success');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Modus-Dropdown-Menü (Muster wie der Emoji-Picker) ──
|
||||||
|
|
||||||
|
function _buildCompositeMenu() {
|
||||||
|
var menu = document.createElement('div');
|
||||||
|
menu.className = 'composite-menu';
|
||||||
|
_COMPOSITE_MODES.forEach(function (m) {
|
||||||
|
var active = (m.mode === _compositeMode);
|
||||||
|
var item = document.createElement('button');
|
||||||
|
item.className = 'composite-menu-item' + (active ? ' active' : '');
|
||||||
|
item.innerHTML =
|
||||||
|
'<i class="fa ' + m.icon + ' composite-menu-icon"></i>' +
|
||||||
|
'<span class="composite-menu-text">' +
|
||||||
|
'<span class="composite-menu-label">' + m.label + '</span>' +
|
||||||
|
'<span class="composite-menu-desc">' + m.desc + '</span>' +
|
||||||
|
'</span>' +
|
||||||
|
(active ? '<i class="fa fa-check composite-menu-check"></i>' : '');
|
||||||
|
item.addEventListener('click', function (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
closeCompositeMenu();
|
||||||
|
setCompositeMode(m.mode);
|
||||||
|
});
|
||||||
|
menu.appendChild(item);
|
||||||
|
});
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleCompositeMenu(anchor) {
|
||||||
|
if (_compositeMenuEl) { closeCompositeMenu(); return; }
|
||||||
|
|
||||||
|
var menu = _buildCompositeMenu();
|
||||||
|
document.body.appendChild(menu);
|
||||||
|
_compositeMenuEl = menu;
|
||||||
|
|
||||||
|
// Positionierung: über dem Button (Input-Leiste ist unten), linksbündig zum Button.
|
||||||
|
var rect = anchor.getBoundingClientRect();
|
||||||
|
var h = menu.offsetHeight;
|
||||||
|
var w = menu.offsetWidth;
|
||||||
|
var top = rect.top - h - 6;
|
||||||
|
var left = rect.left;
|
||||||
|
if (top < 8) top = rect.bottom + 6; // kein Platz oben → unten
|
||||||
|
if (left + w > window.innerWidth - 8) left = window.innerWidth - w - 8; // rechts abgeschnitten
|
||||||
|
if (left < 8) left = 8;
|
||||||
|
menu.style.top = top + 'px';
|
||||||
|
menu.style.left = left + 'px';
|
||||||
|
|
||||||
|
_compositeMenuCloseHandler = function (e) {
|
||||||
|
if (!menu.contains(e.target) && e.target !== anchor && !anchor.contains(e.target)) closeCompositeMenu();
|
||||||
|
};
|
||||||
|
setTimeout(function () { document.addEventListener('click', _compositeMenuCloseHandler); }, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeCompositeMenu() {
|
||||||
|
if (_compositeMenuEl) { _compositeMenuEl.remove(); _compositeMenuEl = null; }
|
||||||
|
if (_compositeMenuCloseHandler) {
|
||||||
|
document.removeEventListener('click', _compositeMenuCloseHandler);
|
||||||
|
_compositeMenuCloseHandler = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── Chat State ──
|
// ── Chat State ──
|
||||||
|
|
||||||
let isStreaming = false;
|
let isStreaming = false;
|
||||||
|
|||||||
@@ -572,12 +572,16 @@ registerPage('chat', {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unified Modus-Toggle (Aus / Plan / Fusion / MoA) – aus der Session initialisieren
|
// Unified Modus-Menü (Aus / Plan / Ultracode / Fusion / MoA) – aus der Session initialisieren.
|
||||||
|
// Klick öffnet ein Dropdown (wie der Emoji-Picker), aus dem der Modus direkt gewählt wird.
|
||||||
const compositeModeBtn = document.getElementById('planModeBtn');
|
const compositeModeBtn = document.getElementById('planModeBtn');
|
||||||
if (compositeModeBtn) {
|
if (compositeModeBtn) {
|
||||||
const hasPanelAgents = !!(session.panelAgentIds && String(session.panelAgentIds).trim());
|
const hasPanelAgents = !!(session.panelAgentIds && String(session.panelAgentIds).trim());
|
||||||
initCompositeMode(session.multiModelMode, hasPanelAgents);
|
initCompositeMode(session.multiModelMode, hasPanelAgents);
|
||||||
compositeModeBtn.addEventListener('click', cycleCompositeMode);
|
compositeModeBtn.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
toggleCompositeMenu(compositeModeBtn);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// File Upload
|
// File Upload
|
||||||
|
|||||||
Reference in new issue
Block a user