feat: typing indicator mit agent-name und random phrases
- typing-phrases.js in index.html eingebunden - startTypingIndicator() / stopTypingIndicator() in chat.js - Agent-Name via setCurrentAgentName() aus chat-page.js gesetzt - Phrase wechselt alle 3 Sekunden zufällig - Cleanup bei destroy() und finally-Block
This commit is contained in:
1 parent
c5f2ed54fd
commit
f941eae00a
5 files changed
+73
-5
No files matched your search
@@ -46,7 +46,7 @@
|
|||||||
- [x] ~~Claude (Sub-)Agents~~ – Nicht umsetzbar: Claude Code Sub-Agents sind ein CLI-Feature, nicht über die Messages API verfügbar. Eigenes Sub-Agent-System (Preparer/Review/Finalizer) deckt den Use-Case bereits ab.
|
- [x] ~~Claude (Sub-)Agents~~ – Nicht umsetzbar: Claude Code Sub-Agents sind ein CLI-Feature, nicht über die Messages API verfügbar. Eigenes Sub-Agent-System (Preparer/Review/Finalizer) deckt den Use-Case bereits ab.
|
||||||
|
|
||||||
## Features aus Devy übernehmen
|
## Features aus Devy übernehmen
|
||||||
- [ ] 💬 Typing-Indicator - Animierter Indikator statt statischem "denkt nach..."
|
- [ ] 💬 Typing-Indicator - Animierter Indikator statt statischem "Streaming..."
|
||||||
- [ ] 🎨 Chat-Hintergründe - Benutzerdefinierte Hintergründe pro Session
|
- [ ] 🎨 Chat-Hintergründe - Benutzerdefinierte Hintergründe pro Session
|
||||||
- [x] 👤 User-Management - Admin-only: Benutzer CRUD, Login-as-User, Default-Pfade
|
- [x] 👤 User-Management - Admin-only: Benutzer CRUD, Login-as-User, Default-Pfade
|
||||||
- [x] 🖥️ Windows Desktop App - WPF + WebView2 Wrapper mit konfigurierbarer URL
|
- [x] 🖥️ Windows Desktop App - WPF + WebView2 Wrapper mit konfigurierbarer URL
|
||||||
|
|||||||
@@ -616,6 +616,7 @@
|
|||||||
<script src="js/reply.js"></script>
|
<script src="js/reply.js"></script>
|
||||||
<script src="js/files.js"></script>
|
<script src="js/files.js"></script>
|
||||||
<script src="js/file-explorer.js"></script>
|
<script src="js/file-explorer.js"></script>
|
||||||
|
<script src="js/typing-phrases.js"></script>
|
||||||
<script src="js/chat.js"></script>
|
<script src="js/chat.js"></script>
|
||||||
<script src="js/history.js"></script>
|
<script src="js/history.js"></script>
|
||||||
<script src="js/archive.js"></script>
|
<script src="js/archive.js"></script>
|
||||||
|
|||||||
+36
-3
@@ -161,6 +161,38 @@ let _currentToolBox = null; // Aktuell laufende Tool-Box (für Stream-Events)
|
|||||||
let _chatAbandoned = false; // true wenn User weg-navigiert während Stream läuft
|
let _chatAbandoned = false; // true wenn User weg-navigiert während Stream läuft
|
||||||
let _abortController = null; // AbortController für laufenden Stream
|
let _abortController = null; // AbortController für laufenden Stream
|
||||||
|
|
||||||
|
// ── Typing Indicator ──
|
||||||
|
|
||||||
|
let _currentAgentName = null;
|
||||||
|
let _typingInterval = null;
|
||||||
|
|
||||||
|
function setCurrentAgentName(name) {
|
||||||
|
_currentAgentName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _randomTypingPhrase() {
|
||||||
|
return TYPING_PHRASES[Math.floor(Math.random() * TYPING_PHRASES.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
function _buildTypingHtml(phrase) {
|
||||||
|
var name = _currentAgentName || 'Agent';
|
||||||
|
return '<span><i class="fa fa-hourglass-half"></i> ' + esc(name) + ' ' + esc(phrase) + '...</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function startTypingIndicator() {
|
||||||
|
setMetaHtml(_buildTypingHtml(_randomTypingPhrase()));
|
||||||
|
_typingInterval = setInterval(function() {
|
||||||
|
setMetaHtml(_buildTypingHtml(_randomTypingPhrase()));
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopTypingIndicator() {
|
||||||
|
if (_typingInterval) {
|
||||||
|
clearInterval(_typingInterval);
|
||||||
|
_typingInterval = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function clearChat() {
|
async function clearChat() {
|
||||||
const archive = await apiArchive(getCurrentSessionId());
|
const archive = await apiArchive(getCurrentSessionId());
|
||||||
if (!archive) return;
|
if (!archive) return;
|
||||||
@@ -213,7 +245,7 @@ async function _runStream(fetchPromise) {
|
|||||||
_currentToolBox = null;
|
_currentToolBox = null;
|
||||||
_chatAbandoned = false;
|
_chatAbandoned = false;
|
||||||
_stopClickCount = 0;
|
_stopClickCount = 0;
|
||||||
setMetaHtml('<span><i class="fa fa-hourglass-half"></i> Streaming...</span>');
|
startTypingIndicator();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const resp = await fetchPromise(_abortController.signal);
|
const resp = await fetchPromise(_abortController.signal);
|
||||||
@@ -250,7 +282,7 @@ async function _runStream(fetchPromise) {
|
|||||||
try {
|
try {
|
||||||
const status = await apiIsStreaming(sid);
|
const status = await apiIsStreaming(sid);
|
||||||
if (status.streaming) {
|
if (status.streaming) {
|
||||||
setMetaHtml('<span><i class="fa fa-hourglass-half"></i> Streaming... (Verbindung wiederhergestellt)</span>');
|
startTypingIndicator();
|
||||||
startStreamingPoll(sid, sendBtn, status.chainId);
|
startStreamingPoll(sid, sendBtn, status.chainId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -259,6 +291,7 @@ async function _runStream(fetchPromise) {
|
|||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (_chatAbandoned) return;
|
if (_chatAbandoned) return;
|
||||||
|
stopTypingIndicator();
|
||||||
if (!isStreaming || !_streamingPollInterval) {
|
if (!isStreaming || !_streamingPollInterval) {
|
||||||
sendBtn.disabled = false;
|
sendBtn.disabled = false;
|
||||||
sendBtn.hidden = false;
|
sendBtn.hidden = false;
|
||||||
@@ -269,7 +302,7 @@ async function _runStream(fetchPromise) {
|
|||||||
_abortController = null;
|
_abortController = null;
|
||||||
// Meta zurücksetzen falls kein done-Event kam (z.B. nach Stop)
|
// Meta zurücksetzen falls kein done-Event kam (z.B. nach Stop)
|
||||||
var metaEl = $('#meta');
|
var metaEl = $('#meta');
|
||||||
if (metaEl && metaEl.textContent.includes('Streaming')) {
|
if (metaEl && (metaEl.textContent.includes('Streaming') || metaEl.textContent.includes('...'))) {
|
||||||
setMeta(_stopClickCount > 0 ? 'Gestoppt' : 'Bereit');
|
setMeta(_stopClickCount > 0 ? 'Gestoppt' : 'Bereit');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -202,9 +202,12 @@ registerPage('chat', {
|
|||||||
if (session.agentId) {
|
if (session.agentId) {
|
||||||
try {
|
try {
|
||||||
const agent = await apiGetAgent(session.agentId);
|
const agent = await apiGetAgent(session.agentId);
|
||||||
if (agent && agent.customCommands) {
|
if (agent) {
|
||||||
|
setCurrentAgentName(agent.name || null);
|
||||||
|
if (agent.customCommands) {
|
||||||
agentCmds = JSON.parse(agent.customCommands) || [];
|
agentCmds = JSON.parse(agent.customCommands) || [];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
}
|
}
|
||||||
let sessionCmds = [];
|
let sessionCmds = [];
|
||||||
@@ -390,6 +393,8 @@ registerPage('chat', {
|
|||||||
// Stream nicht abbrechen (Backend soll weiterlaufen),
|
// Stream nicht abbrechen (Backend soll weiterlaufen),
|
||||||
// aber Frontend-Processing stoppen
|
// aber Frontend-Processing stoppen
|
||||||
_chatAbandoned = true;
|
_chatAbandoned = true;
|
||||||
|
stopTypingIndicator();
|
||||||
|
setCurrentAgentName(null);
|
||||||
closeEmojiPicker();
|
closeEmojiPicker();
|
||||||
if (this._closeMenu) {
|
if (this._closeMenu) {
|
||||||
document.removeEventListener('click', this._closeMenu);
|
document.removeEventListener('click', this._closeMenu);
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
const TYPING_PHRASES = [
|
||||||
|
'denkt si wos',
|
||||||
|
'grübelt',
|
||||||
|
'schaut deppat drein',
|
||||||
|
'brunzt',
|
||||||
|
'scheißt an Kropfn',
|
||||||
|
'krotzt si am Hirn',
|
||||||
|
'hoit sei Goschn',
|
||||||
|
'is am Schmäh fian',
|
||||||
|
'raunzt leise vor si hi',
|
||||||
|
'fladert a Idee',
|
||||||
|
'schleicht si boid',
|
||||||
|
'red mit si söba',
|
||||||
|
'brettlt durch die Daten',
|
||||||
|
'sudert',
|
||||||
|
'bockts nimma',
|
||||||
|
'schaut obs eh passt',
|
||||||
|
'mocht a Hetz',
|
||||||
|
'wuzelt si durch',
|
||||||
|
'hod an Bickn',
|
||||||
|
'zwickt si',
|
||||||
|
'draht Dahma',
|
||||||
|
'schnackselt mit der API',
|
||||||
|
'is gonz narrisch',
|
||||||
|
'hackelt',
|
||||||
|
'sudert übers Wetter',
|
||||||
|
'fetzt si mitn Code',
|
||||||
|
'is am Wurschteln',
|
||||||
|
];
|
||||||
Reference in new issue
Block a user