feat(queue): Diagnostic spiegelt Pro-Kontext-Queue + Rueckfrage + Drafts

Diagnostic bekommt denselben Queue-Automaten wie die App: anstellen statt
abbrechen, awaiting_reply pausiert die Queue (naechste Eingabe beantwortet die
Rueckfrage), Queue-Banner mit loeschbaren Eintraegen ueber dem Eingabefeld,
und Pro-Kontext-Textfeld-Entwuerfe (Feldinhalt bleibt beim Kontextwechsel
erhalten). rvs_chat-Handler liest awaiting_reply aus der Bridge-Payload.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-12 09:41:24 +02:00
co-authored by Claude Opus 4.8
parent 8a0670a3d2
commit c9a1d80696
+89 -7
View File
@@ -320,6 +320,7 @@
</div>
<div id="diag-pending-attachments" style="display:none;padding:6px 10px;background:#1E1E2E;border-radius:6px 6px 0 0;margin-bottom:-4px;display:flex;gap:6px;flex-wrap:wrap;align-items:center;">
</div>
<div id="diag-queue-banner" style="display:none;padding:6px 10px;background:#2A2410;border:1px solid #4A3F14;border-radius:6px;margin-bottom:6px;"></div>
<div class="input-row">
<label class="btn secondary" style="padding:6px 10px;cursor:pointer;font-size:14px;" title="Datei anhaengen">
&#x1F4CE;
@@ -1425,6 +1426,12 @@
let diagQueueStatus = {};
let diagProjectsCache = [];
let diagShowHiddenStrip = false; // versteckte Projekte im Streifen zeigen?
// ── Pro-Kontext-Queue + Zustandsautomat (spiegelt die App) ──
const diagCtxStates = {}; // pid → 'idle' | 'running' | 'awaiting_reply'
const diagCtxQueues = {}; // pid → [{id,text}]
const diagDrafts = JSON.parse(localStorage.getItem('diag_ctx_drafts') || '{}');
let diagQid = 0;
const diagState = (pid) => diagCtxStates[pid] || 'idle';
function updateChatVisibilityByFocus() {
for (const box of [chatBox, document.getElementById('chat-box-fs')]) {
@@ -1438,10 +1445,22 @@
}
function switchDiagFocus(id) {
focusedContextId = id || '';
const nextId = id || '';
// Pro-Kontext-Textfeld-Entwuerfe: Feldinhalt dem verlassenen Kontext
// zuordnen, Entwurf des neuen laden.
if (nextId !== focusedContextId) {
const input = document.getElementById('chat-input');
if (input) {
diagDrafts[focusedContextId] = input.value;
input.value = diagDrafts[nextId] || '';
}
localStorage.setItem('diag_ctx_drafts', JSON.stringify(diagDrafts));
}
focusedContextId = nextId;
localStorage.setItem('diag_focused_context_id', focusedContextId);
updateChatVisibilityByFocus();
renderContextStrip();
renderDiagQueue();
}
function renderContextStrip() {
@@ -1983,6 +2002,15 @@
projectId: p.projectId || '',
answeredBy: p.answeredBy || '',
});
// ── Pro-Kontext-Queue-Automat: auf ARIAs Antwort reagieren ──
if (sender === 'aria') {
const apid = p.projectId || '';
const st = diagState(apid);
if (st === 'running' || st === 'awaiting_reply') {
if (p.awaiting_reply) { diagCtxStates[apid] = 'awaiting_reply'; renderDiagQueue(); }
else advanceDiagQueue(apid);
}
}
return;
}
if (msg.type === 'chat_message_deleted') {
@@ -2179,16 +2207,64 @@
renderDiagPending();
}
// ── Pro-Kontext-Queue: Helfer (spiegelt die App) ──
function actuallySendDiag(pid, text) {
addChat('sent', text, 'via RVS', { projectId: pid });
send({ action: 'test_rvs', text, projectId: pid });
diagCtxStates[pid] = 'running';
renderDiagQueue();
}
function advanceDiagQueue(pid) {
const q = diagCtxQueues[pid] || [];
if (q.length === 0) { diagCtxStates[pid] = 'idle'; renderDiagQueue(); return; }
const next = q.shift();
actuallySendDiag(pid, next.text);
}
function enqueueOrSendDiag(pid, text) {
if (diagState(pid) === 'running') {
(diagCtxQueues[pid] = diagCtxQueues[pid] || []).push({ id: 'q' + (++diagQid), text });
renderDiagQueue();
} else {
// idle ODER awaiting_reply → sofort (bei awaiting = Antwort auf Rueckfrage).
actuallySendDiag(pid, text);
}
}
function removeDiagQueuedFocused(id) {
const pid = focusedContextId;
diagCtxQueues[pid] = (diagCtxQueues[pid] || []).filter(it => it.id !== id);
renderDiagQueue();
}
function renderDiagQueue() {
const el = document.getElementById('diag-queue-banner');
if (!el) return;
const st = diagState(focusedContextId);
const q = diagCtxQueues[focusedContextId] || [];
if (st !== 'awaiting_reply' && q.length === 0) { el.style.display = 'none'; el.innerHTML = ''; return; }
el.style.display = 'block';
let html = '';
if (st === 'awaiting_reply') {
html += '<div style="color:#FFD60A;font-weight:600;margin-bottom:4px;">&#x2753; ARIA fragt nach &mdash; deine Eingabe beantwortet das</div>';
}
if (q.length) {
html += q.map(it =>
'<span style="display:inline-block;background:#1E1E2E;border:1px solid #4A3F14;border-radius:10px;padding:2px 8px;margin:2px;font-size:12px;color:#FFD60A;">&#x23F8; ' +
escapeHtml(it.text.slice(0, 40)) +
' <a href="#" style="color:#FF6B6B;text-decoration:none;" onclick="removeDiagQueuedFocused(\'' + it.id + '\');return false;">&#x2715;</a></span>'
).join('');
}
el.innerHTML = html;
}
function testRVS() {
const input = document.getElementById('chat-input');
const text = input.value.trim();
if (!text && diagPendingFiles.length === 0) return;
if (diagPendingFiles.length > 0) sendDiagAttachments();
if (text) {
// Multi-Threading: mit fokussierter Kontext-ID senden.
// Bridge routet an /chat body.project_id — Brain queued per Kontext.
addChat('sent', text, 'via RVS', { projectId: focusedContextId });
send({ action: 'test_rvs', text, projectId: focusedContextId });
// Draft dieses Kontexts leeren + Queue-Automat entscheidet senden/anstellen.
diagDrafts[focusedContextId] = '';
localStorage.setItem('diag_ctx_drafts', JSON.stringify(diagDrafts));
enqueueOrSendDiag(focusedContextId, text);
}
input.value = '';
}
@@ -2675,8 +2751,8 @@
const input = document.getElementById('chat-input-fs');
const text = input.value.trim();
if (!text) return;
addChat('sent', text, 'via RVS');
send({ action: 'test_rvs', text });
// Ueber denselben Queue-Automaten wie der Haupt-Chat (fokussierter Kontext).
enqueueOrSendDiag(focusedContextId, text);
input.value = '';
}
// Escape schliesst Vollbild-Chat
@@ -3650,6 +3726,12 @@
}
document.getElementById('chat-input').addEventListener('keydown', (e) => chatInputKeydown(e, testRVS));
document.getElementById('chat-input-fs').addEventListener('keydown', (e) => chatInputKeydown(e, testRVSFS));
// Entwurf des zuletzt fokussierten Kontexts ins Feld laden + Queue-Banner init.
(function initDiagDraft() {
const input = document.getElementById('chat-input');
if (input && diagDrafts[focusedContextId]) input.value = diagDrafts[focusedContextId];
renderDiagQueue();
})();
// Escape schliesst Lightbox
document.addEventListener('keydown', (e) => {