From 21c3e78689fef0324a85d6614be0a41e58630eb6 Mon Sep 17 00:00:00 2001 From: team 1 Date: Tue, 28 Apr 2026 08:33:13 +0200 Subject: [PATCH] optimize ui --- public/assets/js/base.js | 134 ++++++++++------------------------ public/assets/styles/base.css | 33 +++++++++ public/index.html | 7 ++ 3 files changed, 79 insertions(+), 95 deletions(-) diff --git a/public/assets/js/base.js b/public/assets/js/base.js index 859c9dc..81b4357 100644 --- a/public/assets/js/base.js +++ b/public/assets/js/base.js @@ -5,7 +5,9 @@ document.addEventListener('DOMContentLoaded', () => { const abortBtn = document.getElementById('abort'); const clearBtn = document.getElementById('clear'); const aiCloudEl = document.getElementById('ai-cloud'); + const retriexCardsToggleEl = document.getElementById('toggle-retriex-cards'); const LAST_TURN_STORAGE_KEY = 'retriex:lastCompletedTurn'; + const DETAIL_CARDS_STORAGE_KEY = 'retriex:showDetailCards'; const state = { abortRequested: false, @@ -22,6 +24,42 @@ document.addEventListener('DOMContentLoaded', () => { marked.setOptions({breaks: true}); + function setRetriexDetailCardsVisible(isVisible, persist = true) { + document.body.classList.toggle('retriex-show-detail-cards', isVisible); + + if (retriexCardsToggleEl) { + retriexCardsToggleEl.checked = isVisible; + } + + if (!persist) { + return; + } + + try { + window.localStorage?.setItem(DETAIL_CARDS_STORAGE_KEY, isVisible ? '1' : '0'); + } catch (err) { + console.debug('Could not persist detail card visibility:', err); + } + } + + function initRetriexDetailCardsToggle() { + let isVisible = false; + + try { + isVisible = window.localStorage?.getItem(DETAIL_CARDS_STORAGE_KEY) === '1'; + } catch (err) { + console.debug('Could not read detail card visibility:', err); + } + + setRetriexDetailCardsVisible(isVisible, false); + + retriexCardsToggleEl?.addEventListener('change', () => { + setRetriexDetailCardsVisible(retriexCardsToggleEl.checked, true); + }); + } + + initRetriexDetailCardsToggle(); + function renderMarkdown(text) { return DOMPurify.sanitize(marked.parse(text)); } @@ -764,8 +802,6 @@ document.addEventListener('DOMContentLoaded', () => { state.eventSource = source; let networkErrorTimer = null; - let completionWatchdogTimer = null; - let completionWatchdogInFlight = false; const clearNetworkErrorTimer = () => { if (!networkErrorTimer) { @@ -776,15 +812,6 @@ document.addEventListener('DOMContentLoaded', () => { networkErrorTimer = null; }; - const clearCompletionWatchdog = () => { - if (!completionWatchdogTimer) { - return; - } - - clearInterval(completionWatchdogTimer); - completionWatchdogTimer = null; - }; - const complete = () => { if (finished) { return; @@ -792,7 +819,6 @@ document.addEventListener('DOMContentLoaded', () => { finished = true; clearNetworkErrorTimer(); - clearCompletionWatchdog(); finishEventStream(); resolve(); }; @@ -804,73 +830,10 @@ document.addEventListener('DOMContentLoaded', () => { finished = true; clearNetworkErrorTimer(); - clearCompletionWatchdog(); finishEventStream(); reject(err); }; - const finishFromStatus = (payload) => { - if (finished || state.abortRequested || !payload || typeof payload !== 'object') { - return false; - } - - const status = String(payload.status || ''); - const serverLastEventId = Number.parseInt(String(payload.lastEventId || '0'), 10) || 0; - - // Only finalize from the side-channel when all numbered content chunks are already visible. - // This prevents cutting off the final answer if the browser missed more than just the terminal done event. - if (serverLastEventId > lastSseEventId) { - return false; - } - - if (status === 'completed') { - finalizeStream(bubble, raw); - rememberCompletedTurn(prompt, raw); - complete(); - return true; - } - - if (status === 'failed' || status === 'interrupted') { - appendError(payload.message || 'Der Antwort-Stream wurde beendet, ohne ein Abschluss-Signal an den Browser zu senden.'); - complete(); - return true; - } - - return false; - }; - - const checkCompletionStatus = async () => { - if (finished || state.abortRequested || completionWatchdogInFlight) { - return false; - } - - completionWatchdogInFlight = true; - - try { - const statusRes = await fetch(`/ask-jobs/${encodeURIComponent(jobId)}`, { - method: 'GET', - cache: 'no-store', - signal: state.abortController?.signal, - }); - - if (!statusRes.ok) { - return false; - } - - return finishFromStatus(await statusRes.json()); - } catch (err) { - if (!state.abortRequested) { - console.debug('Stream completion watchdog failed:', err); - } - - return false; - } finally { - completionWatchdogInFlight = false; - } - }; - - completionWatchdogTimer = setInterval(checkCompletionStatus, 2500); - state.completeStream = complete; state.failStream = fail; @@ -890,13 +853,6 @@ document.addEventListener('DOMContentLoaded', () => { return; } - if (event.data === '[DONE]') { - finalizeStream(bubble, raw); - rememberCompletedTurn(prompt, raw); - complete(); - return; - } - const numericEventId = Number.parseInt(event.lastEventId || '', 10); if (Number.isFinite(numericEventId) && numericEventId > 0) { @@ -931,20 +887,8 @@ document.addEventListener('DOMContentLoaded', () => { return; } - void checkCompletionStatus(); - if (source.readyState === EventSource.CLOSED) { - window.setTimeout(async () => { - if (finished || state.abortRequested) { - return; - } - - const statusCompleted = await checkCompletionStatus(); - - if (!statusCompleted && !finished) { - fail(new Error('EventSource connection closed')); - } - }, 250); + fail(new Error('EventSource connection closed')); return; } diff --git a/public/assets/styles/base.css b/public/assets/styles/base.css index 5fe4764..d5182eb 100644 --- a/public/assets/styles/base.css +++ b/public/assets/styles/base.css @@ -464,6 +464,39 @@ span.think { } } +.retriex-chat-options { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin: 0.45rem 0 0.75rem; + color: rgba(248, 249, 250, 0.76); + font-size: 0.84rem; +} + +.retriex-option-toggle { + display: inline-flex; + align-items: center; + gap: 0.45rem; + border: 1px solid rgba(134, 183, 254, 0.2); + border-radius: 999px; + padding: 0.28rem 0.65rem; + background: rgba(13, 17, 23, 0.42); + cursor: pointer; + user-select: none; +} + +.retriex-option-toggle input { + width: 0.95rem; + height: 0.95rem; + margin: 0; + accent-color: #0dcaf0; +} + +body:not(.retriex-show-detail-cards) #chat .retriex-meta-card, +body:not(.retriex-show-detail-cards) #chat .retriex-alert { + display: none !important; +} + /* RetrieX chat meta/status cards */ .retriex-meta-card, .retriex-alert { diff --git a/public/index.html b/public/index.html index 99ff93e..b9e4b7f 100644 --- a/public/index.html +++ b/public/index.html @@ -32,6 +32,13 @@
+
+ +
+