agentgui 1.0.854 → 1.0.856
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +33 -164
- package/lib/jsonl-parser.js +48 -0
- package/lib/jsonl-watcher.js +30 -0
- package/lib/process-message.js +41 -0
- package/lib/server-startup.js +10 -0
- package/lib/stream-event-handler.js +80 -0
- package/package.json +1 -1
- package/server.js +6 -5
- package/static/css/main.css +159 -0
- package/static/js/client.js +68 -0
- package/static/js/conversations.js +9 -0
|
@@ -1,8 +1,25 @@
|
|
|
1
|
+
<<<<<<< HEAD
|
|
1
2
|
export function createEventHandler({ queries, activeExecutions, broadcastSync, rateLimitState, batcherRef, sessionId, conversationId, messageId, content, agentId, model, subAgent, ownedSessionIds, allBlocksRef, currentSequenceRef, scheduleRetry, eagerTTS, debugLog, parseRateLimitResetTime }) {
|
|
3
|
+
=======
|
|
4
|
+
/**
|
|
5
|
+
* Minimal Claude stdout event handler.
|
|
6
|
+
*
|
|
7
|
+
* Now that JsonlParser owns all event broadcasting (streaming_start,
|
|
8
|
+
* streaming_progress, streaming_complete) via the JSONL file watcher,
|
|
9
|
+
* this handler only needs to:
|
|
10
|
+
* 1. Extract session_id → call setClaudeSessionId + registerSession so the
|
|
11
|
+
* parser links the file to the correct existing conversation/session.
|
|
12
|
+
* 2. Detect inline rate-limit messages in text/result blocks → scheduleRetry.
|
|
13
|
+
*
|
|
14
|
+
* No batcher, no broadcastSync for individual blocks.
|
|
15
|
+
*/
|
|
16
|
+
export function createEventHandler({ queries, activeExecutions, broadcastSync, rateLimitState, batcherRef, sessionId, conversationId, messageId, content, agentId, model, subAgent, getJsonlWatcher, scheduleRetry, eagerTTS, debugLog, parseRateLimitResetTime }) {
|
|
17
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
2
18
|
return function onEvent(parsed) {
|
|
3
19
|
batcherRef.eventCount++;
|
|
4
20
|
const entry = activeExecutions.get(conversationId);
|
|
5
21
|
if (entry) entry.lastActivity = Date.now();
|
|
22
|
+
<<<<<<< HEAD
|
|
6
23
|
if (parsed.session_id) {
|
|
7
24
|
ownedSessionIds.add(parsed.session_id);
|
|
8
25
|
if (!batcherRef.resumeSessionId || batcherRef.resumeSessionId !== parsed.session_id) {
|
|
@@ -25,16 +42,41 @@ export function createEventHandler({ queries, activeExecutions, broadcastSync, r
|
|
|
25
42
|
currentSequenceRef.val++;
|
|
26
43
|
batcherRef.batcher.add(sessionId, conversationId, currentSequenceRef.val, block.type || 'assistant', block);
|
|
27
44
|
broadcastSync({ type: 'streaming_progress', sessionId, conversationId, block, blockRole: 'assistant', blockIndex: allBlocksRef.val.length - 1, seq: currentSequenceRef.val, timestamp: Date.now() });
|
|
45
|
+
=======
|
|
46
|
+
|
|
47
|
+
// Register session with file watcher as soon as we see the session_id.
|
|
48
|
+
// This pre-maps claudeSessionId → (convId, dbSessionId) in JsonlParser before
|
|
49
|
+
// the 16ms file-watcher debounce fires, preventing duplicate conversation creation.
|
|
50
|
+
if (parsed.session_id) {
|
|
51
|
+
if (!batcherRef.resumeSessionId || batcherRef.resumeSessionId !== parsed.session_id) {
|
|
52
|
+
batcherRef.resumeSessionId = parsed.session_id;
|
|
53
|
+
queries.setClaudeSessionId(conversationId, parsed.session_id, sessionId);
|
|
54
|
+
try { getJsonlWatcher()?.registerSession(parsed.session_id, conversationId, sessionId); } catch (_) {}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
debugLog(`[stream] Event ${batcherRef.eventCount}: type=${parsed.type}`);
|
|
59
|
+
|
|
60
|
+
// Rate-limit detection in assistant text blocks
|
|
61
|
+
if (parsed.type === 'assistant' && parsed.message?.content) {
|
|
62
|
+
for (const block of parsed.message.content) {
|
|
63
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
28
64
|
if (block.type === 'text' && block.text) {
|
|
29
65
|
const rateLimitMatch = block.text.match(/you'?ve hit your limit|rate limit exceeded/i);
|
|
30
66
|
if (rateLimitMatch) {
|
|
31
67
|
debugLog(`[rate-limit] Detected rate limit message in stream for conv ${conversationId}`);
|
|
32
68
|
const retryAfterSec = parseRateLimitResetTime(block.text);
|
|
33
69
|
const entry2 = activeExecutions.get(conversationId);
|
|
70
|
+
<<<<<<< HEAD
|
|
34
71
|
if (entry2 && entry2.pid) { try { process.kill(entry2.pid); } catch (e) {} }
|
|
35
72
|
const existingCount = rateLimitState.get(conversationId)?.retryCount || 0;
|
|
36
73
|
if (existingCount >= 3) {
|
|
37
74
|
batcherRef.batcher.drain();
|
|
75
|
+
=======
|
|
76
|
+
if (entry2 && entry2.pid) { try { process.kill(entry2.pid); } catch (_) {} }
|
|
77
|
+
const existingCount = rateLimitState.get(conversationId)?.retryCount || 0;
|
|
78
|
+
if (existingCount >= 3) {
|
|
79
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
38
80
|
activeExecutions.delete(conversationId);
|
|
39
81
|
queries.setIsStreaming(conversationId, false);
|
|
40
82
|
const errMsg = queries.createMessage(conversationId, 'assistant', `Error: Rate limit exceeded after ${existingCount + 1} attempts. Please try again later.`);
|
|
@@ -44,7 +86,10 @@ export function createEventHandler({ queries, activeExecutions, broadcastSync, r
|
|
|
44
86
|
}
|
|
45
87
|
rateLimitState.set(conversationId, { retryAt: Date.now() + (retryAfterSec * 1000), cooldownMs: retryAfterSec * 1000, retryCount: existingCount + 1, isStreamDetected: true });
|
|
46
88
|
broadcastSync({ type: 'rate_limit_hit', sessionId, conversationId, retryAfterMs: retryAfterSec * 1000, retryAt: Date.now() + (retryAfterSec * 1000), retryCount: 1, timestamp: Date.now() });
|
|
89
|
+
<<<<<<< HEAD
|
|
47
90
|
batcherRef.batcher.drain();
|
|
91
|
+
=======
|
|
92
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
48
93
|
activeExecutions.delete(conversationId);
|
|
49
94
|
queries.setIsStreaming(conversationId, false);
|
|
50
95
|
setTimeout(() => {
|
|
@@ -57,6 +102,7 @@ export function createEventHandler({ queries, activeExecutions, broadcastSync, r
|
|
|
57
102
|
eagerTTS(block.text, conversationId, sessionId);
|
|
58
103
|
}
|
|
59
104
|
}
|
|
105
|
+
<<<<<<< HEAD
|
|
60
106
|
} else if (parsed.type === 'user' && parsed.message?.content) {
|
|
61
107
|
for (const block of parsed.message.content) {
|
|
62
108
|
if (block.type === 'tool_result') {
|
|
@@ -110,6 +156,40 @@ export function createEventHandler({ queries, activeExecutions, broadcastSync, r
|
|
|
110
156
|
broadcastSync({ type: 'streaming_progress', sessionId, conversationId, block: { type: 'usage', usage: parsed.usage }, seq: currentSequenceRef.val, timestamp: Date.now() });
|
|
111
157
|
} else if (parsed.type === 'plan') {
|
|
112
158
|
broadcastSync({ type: 'streaming_progress', sessionId, conversationId, block: { type: 'plan', entries: parsed.entries }, seq: currentSequenceRef.val, timestamp: Date.now() });
|
|
159
|
+
=======
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Rate-limit detection in result blocks
|
|
163
|
+
if (parsed.type === 'result' && parsed.result) {
|
|
164
|
+
const resultText = typeof parsed.result === 'string' ? parsed.result : JSON.stringify(parsed.result);
|
|
165
|
+
const rlMatch = resultText.match(/you'?ve hit your limit|rate limit exceeded/i);
|
|
166
|
+
if (rlMatch) {
|
|
167
|
+
debugLog(`[rate-limit] Detected rate limit in result for conv ${conversationId}`);
|
|
168
|
+
const retryAfterSec = parseRateLimitResetTime(resultText);
|
|
169
|
+
const entry3 = activeExecutions.get(conversationId);
|
|
170
|
+
if (entry3 && entry3.pid) { try { process.kill(entry3.pid); } catch (_) {} }
|
|
171
|
+
const existingCount2 = rateLimitState.get(conversationId)?.retryCount || 0;
|
|
172
|
+
if (existingCount2 >= 3) {
|
|
173
|
+
activeExecutions.delete(conversationId);
|
|
174
|
+
queries.setIsStreaming(conversationId, false);
|
|
175
|
+
const errMsg2 = queries.createMessage(conversationId, 'assistant', `Error: Rate limit exceeded after ${existingCount2 + 1} attempts. Please try again later.`);
|
|
176
|
+
broadcastSync({ type: 'message_created', conversationId, message: errMsg2, timestamp: Date.now() });
|
|
177
|
+
broadcastSync({ type: 'streaming_complete', sessionId, conversationId, interrupted: true, timestamp: Date.now() });
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
rateLimitState.set(conversationId, { retryAt: Date.now() + (retryAfterSec * 1000), cooldownMs: retryAfterSec * 1000, retryCount: existingCount2 + 1, isStreamDetected: true });
|
|
181
|
+
broadcastSync({ type: 'rate_limit_hit', sessionId, conversationId, retryAfterMs: retryAfterSec * 1000, retryAt: Date.now() + (retryAfterSec * 1000), retryCount: existingCount2 + 1, timestamp: Date.now() });
|
|
182
|
+
activeExecutions.delete(conversationId);
|
|
183
|
+
queries.setIsStreaming(conversationId, false);
|
|
184
|
+
setTimeout(() => {
|
|
185
|
+
rateLimitState.delete(conversationId);
|
|
186
|
+
broadcastSync({ type: 'rate_limit_clear', conversationId, timestamp: Date.now() });
|
|
187
|
+
scheduleRetry(conversationId, messageId, content, agentId, model, subAgent);
|
|
188
|
+
}, retryAfterSec * 1000);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (resultText) eagerTTS(resultText, conversationId, sessionId);
|
|
192
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
113
193
|
}
|
|
114
194
|
};
|
|
115
195
|
}
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -26,7 +26,7 @@ const sendWs = (ws, obj) => { if (ws.readyState === 1) ws.send(wsEncode(obj)); }
|
|
|
26
26
|
import { startAll as startACPTools, stopAll as stopACPTools, getStatus as getACPStatus, getPort as getACPPort, ensureRunning, queryModels as queryACPModels, touch as touchACP } from './lib/acp-sdk-manager.js';
|
|
27
27
|
import * as execMachine from './lib/execution-machine.js';
|
|
28
28
|
import * as toolInstallMachine from './lib/tool-install-machine.js';
|
|
29
|
-
import { _assetCache, htmlState, generateETag, warmAssetCache, serveFile as _serveFile
|
|
29
|
+
import { _assetCache, htmlState, generateETag, warmAssetCache, serveFile as _serveFile } from './lib/asset-server.js';
|
|
30
30
|
import { installGMAgentConfigs } from './lib/gm-agent-configs.js';
|
|
31
31
|
import * as toolManager from './lib/tool-manager.js';
|
|
32
32
|
import { pm2Manager } from './lib/pm2-manager.js';
|
|
@@ -53,7 +53,7 @@ const activeExecutions = new Map();
|
|
|
53
53
|
const activeScripts = new Map();
|
|
54
54
|
const messageQueues = new Map();
|
|
55
55
|
const rateLimitState = new Map();
|
|
56
|
-
|
|
56
|
+
let _jsonlWatcher = null;
|
|
57
57
|
const activeProcessesByRunId = new Map();
|
|
58
58
|
const checkpointManager = new CheckpointManager(queries);
|
|
59
59
|
const STUCK_AGENT_THRESHOLD_MS = 1800000;
|
|
@@ -121,8 +121,9 @@ const { scheduleRetry, drainMessageQueue } = createMessageQueue({ queries, messa
|
|
|
121
121
|
const { processMessageWithStreaming } = createProcessMessage({
|
|
122
122
|
queries, activeExecutions, rateLimitState, execMachine,
|
|
123
123
|
broadcastSync, runClaudeWithStreaming, cleanupExecution, checkpointManager,
|
|
124
|
-
discoveredAgents,
|
|
125
|
-
parseRateLimitResetTime, eagerTTS, touchACP,
|
|
124
|
+
discoveredAgents, STARTUP_CWD, buildSystemPrompt,
|
|
125
|
+
parseRateLimitResetTime, eagerTTS, touchACP,
|
|
126
|
+
getJsonlWatcher: () => _jsonlWatcher,
|
|
126
127
|
debugLog, logError,
|
|
127
128
|
scheduleRetry, drainMessageQueue, createEventHandler
|
|
128
129
|
});
|
|
@@ -187,7 +188,7 @@ setInterval(performDbRecovery, 300000);
|
|
|
187
188
|
|
|
188
189
|
const { onServerReady, getJsonlWatcher } = createOnServerReady({
|
|
189
190
|
queries, broadcastSync, warmAssetCache, staticDir, toolManager, discoveredAgents,
|
|
190
|
-
PORT, BASE_URL, watch,
|
|
191
|
+
PORT, BASE_URL, watch, setWatcher: (w) => { _jsonlWatcher = w; }, resumeInterruptedStreams, activeExecutions,
|
|
191
192
|
debugLog, installGMAgentConfigs, startACPTools, getACPStatus, execMachine,
|
|
192
193
|
toolInstallMachine, getSpeech, ensureModelsDownloaded, performAutoImport,
|
|
193
194
|
performAgentHealthCheck, pm2Manager, pm2Subscribers, recoverStaleSessions
|
package/static/css/main.css
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
<<<<<<< HEAD
|
|
2
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
3
|
+
|
|
4
|
+
:root {
|
|
5
|
+
--color-primary: #3b82f6;
|
|
6
|
+
--color-primary-dark: #1e40af;
|
|
7
|
+
--color-bg-primary: #ffffff;
|
|
8
|
+
--color-bg-secondary: #f9fafb;
|
|
9
|
+
--color-bg-code: #f1f5f9;
|
|
10
|
+
--color-code-text: #1e293b;
|
|
11
|
+
--color-code-border: #cbd5e1;
|
|
12
|
+
--color-thinking-bg: #f5f3ff;
|
|
13
|
+
--color-text-primary: #111827;
|
|
14
|
+
--color-text-secondary: #6b7280;
|
|
15
|
+
--color-border: #e5e7eb;
|
|
16
|
+
=======
|
|
1
17
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
|
2
18
|
|
|
3
19
|
*, *::before, *::after { box-sizing: border-box; }
|
|
@@ -16,10 +32,30 @@
|
|
|
16
32
|
--color-text-muted: #9ca3af;
|
|
17
33
|
--color-border: #e5e7eb;
|
|
18
34
|
--color-border-strong: #d1d5db;
|
|
35
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
19
36
|
--color-success: #10b981;
|
|
20
37
|
--color-error: #ef4444;
|
|
21
38
|
--color-warning: #f59e0b;
|
|
22
39
|
--color-info: #0891b2;
|
|
40
|
+
<<<<<<< HEAD
|
|
41
|
+
--sidebar-width: 300px;
|
|
42
|
+
--header-height: 52px;
|
|
43
|
+
--msg-max-width: 800px;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
html.dark {
|
|
47
|
+
--color-bg-primary: #1a1a1a;
|
|
48
|
+
--color-bg-secondary: #242424;
|
|
49
|
+
--color-text-primary: #e5e5e5;
|
|
50
|
+
--color-text-secondary: #a3a3a3;
|
|
51
|
+
--color-border: #333333;
|
|
52
|
+
--color-primary: #737373;
|
|
53
|
+
--color-primary-dark: #525252;
|
|
54
|
+
--color-bg-code: #1e293b;
|
|
55
|
+
--color-code-text: #e2e8f0;
|
|
56
|
+
--color-code-border: #334155;
|
|
57
|
+
--color-thinking-bg: #1e1a2e;
|
|
58
|
+
=======
|
|
23
59
|
--sidebar-width: 260px;
|
|
24
60
|
--header-height: 48px;
|
|
25
61
|
--msg-max-width: 800px;
|
|
@@ -51,17 +87,24 @@
|
|
|
51
87
|
--shadow-sm: 0 1px 2px rgba(0,0,0,0.3);
|
|
52
88
|
--shadow-md: 0 4px 12px rgba(0,0,0,0.4);
|
|
53
89
|
--shadow-lg: 0 8px 24px rgba(0,0,0,0.5);
|
|
90
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
54
91
|
}
|
|
55
92
|
|
|
56
93
|
html, body {
|
|
57
94
|
margin: 0;
|
|
58
95
|
padding: 0;
|
|
59
96
|
height: 100%;
|
|
97
|
+
<<<<<<< HEAD
|
|
98
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
99
|
+
background-color: var(--color-bg-primary);
|
|
100
|
+
color: var(--color-text-primary);
|
|
101
|
+
=======
|
|
60
102
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
61
103
|
background-color: var(--color-bg-primary);
|
|
62
104
|
color: var(--color-text-primary);
|
|
63
105
|
-webkit-font-smoothing: antialiased;
|
|
64
106
|
-moz-osx-font-smoothing: grayscale;
|
|
107
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
65
108
|
}
|
|
66
109
|
|
|
67
110
|
/* ===== ROOT LAYOUT: sidebar + main, full viewport ===== */
|
|
@@ -82,7 +125,10 @@
|
|
|
82
125
|
display: flex;
|
|
83
126
|
flex-direction: column;
|
|
84
127
|
background-color: var(--color-bg-secondary);
|
|
128
|
+
<<<<<<< HEAD
|
|
129
|
+
=======
|
|
85
130
|
border-right: 1px solid var(--color-border);
|
|
131
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
86
132
|
overflow: hidden;
|
|
87
133
|
transition: none !important;
|
|
88
134
|
animation: none !important;
|
|
@@ -97,26 +143,51 @@
|
|
|
97
143
|
}
|
|
98
144
|
|
|
99
145
|
.sidebar-header {
|
|
146
|
+
<<<<<<< HEAD
|
|
147
|
+
padding: 0.75rem 1rem;
|
|
148
|
+
=======
|
|
100
149
|
padding: 0.875rem 1rem 0.75rem;
|
|
150
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
101
151
|
display: flex;
|
|
102
152
|
justify-content: space-between;
|
|
103
153
|
align-items: center;
|
|
104
154
|
flex-shrink: 0;
|
|
105
155
|
min-height: var(--header-height);
|
|
156
|
+
<<<<<<< HEAD
|
|
157
|
+
=======
|
|
106
158
|
border-bottom: 1px solid var(--color-border);
|
|
159
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
107
160
|
}
|
|
108
161
|
|
|
109
162
|
.sidebar-header h2 {
|
|
110
163
|
margin: 0;
|
|
164
|
+
<<<<<<< HEAD
|
|
165
|
+
font-size: 0.875rem;
|
|
166
|
+
font-weight: 600;
|
|
167
|
+
text-transform: uppercase;
|
|
168
|
+
letter-spacing: 0.05em;
|
|
169
|
+
color: var(--color-text-secondary);
|
|
170
|
+
=======
|
|
111
171
|
font-size: 1rem;
|
|
112
172
|
font-weight: 700;
|
|
113
173
|
letter-spacing: -0.01em;
|
|
114
174
|
color: var(--color-text-primary);
|
|
175
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
115
176
|
white-space: nowrap;
|
|
116
177
|
overflow: hidden;
|
|
117
178
|
}
|
|
118
179
|
|
|
119
180
|
.sidebar-new-btn {
|
|
181
|
+
<<<<<<< HEAD
|
|
182
|
+
padding: 0.375rem 0.625rem;
|
|
183
|
+
font-size: 0.75rem;
|
|
184
|
+
background-color: var(--color-primary);
|
|
185
|
+
color: white;
|
|
186
|
+
border: none;
|
|
187
|
+
border-radius: 0.375rem;
|
|
188
|
+
cursor: pointer;
|
|
189
|
+
transition: background-color 0.2s;
|
|
190
|
+
=======
|
|
120
191
|
padding: 0.375rem 0.75rem;
|
|
121
192
|
font-size: 0.8rem;
|
|
122
193
|
font-weight: 600;
|
|
@@ -126,6 +197,7 @@
|
|
|
126
197
|
border-radius: var(--radius-md);
|
|
127
198
|
cursor: pointer;
|
|
128
199
|
transition: background-color var(--transition-fast);
|
|
200
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
129
201
|
white-space: nowrap;
|
|
130
202
|
flex-shrink: 0;
|
|
131
203
|
}
|
|
@@ -154,6 +226,8 @@
|
|
|
154
226
|
|
|
155
227
|
.sidebar-clone-btn:hover { border-color: var(--color-primary); color: var(--color-primary); }
|
|
156
228
|
|
|
229
|
+
<<<<<<< HEAD
|
|
230
|
+
=======
|
|
157
231
|
/* Sidebar overflow menu */
|
|
158
232
|
.sidebar-overflow-btn {
|
|
159
233
|
width: 28px;
|
|
@@ -205,6 +279,7 @@
|
|
|
205
279
|
.sidebar-overflow-menu-item.danger { color: var(--color-error); }
|
|
206
280
|
.sidebar-overflow-menu-wrapper { position: relative; }
|
|
207
281
|
|
|
282
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
208
283
|
.clone-input-bar {
|
|
209
284
|
display: flex;
|
|
210
285
|
align-items: center;
|
|
@@ -285,6 +360,24 @@
|
|
|
285
360
|
}
|
|
286
361
|
|
|
287
362
|
.conversation-item {
|
|
363
|
+
<<<<<<< HEAD
|
|
364
|
+
padding: 0.75rem 0.75rem;
|
|
365
|
+
margin: 0.125rem 0.5rem;
|
|
366
|
+
border-radius: 0.375rem;
|
|
367
|
+
cursor: pointer;
|
|
368
|
+
transition: background-color 0.15s;
|
|
369
|
+
border-left: 3px solid transparent;
|
|
370
|
+
user-select: none;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.conversation-item:hover { background-color: var(--color-bg-primary); }
|
|
374
|
+
|
|
375
|
+
.conversation-item.active {
|
|
376
|
+
background-color: var(--color-primary);
|
|
377
|
+
color: white;
|
|
378
|
+
border-left-color: var(--color-primary-dark);
|
|
379
|
+
}
|
|
380
|
+
=======
|
|
288
381
|
padding: 0.625rem 0.75rem;
|
|
289
382
|
margin: 0.125rem 0.5rem;
|
|
290
383
|
border-radius: var(--radius-md);
|
|
@@ -301,6 +394,7 @@
|
|
|
301
394
|
color: var(--color-primary);
|
|
302
395
|
}
|
|
303
396
|
.conversation-item.active .conversation-item-meta { color: var(--color-primary); opacity: 0.7; }
|
|
397
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
304
398
|
|
|
305
399
|
.conversation-item-title {
|
|
306
400
|
font-weight: 500;
|
|
@@ -320,7 +414,11 @@
|
|
|
320
414
|
text-overflow: ellipsis;
|
|
321
415
|
}
|
|
322
416
|
|
|
417
|
+
<<<<<<< HEAD
|
|
418
|
+
.conversation-item.active .conversation-item-meta { color: rgba(255,255,255,0.7); }
|
|
419
|
+
=======
|
|
323
420
|
/* conversation-item.active meta color handled in active block */
|
|
421
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
324
422
|
|
|
325
423
|
.conversation-streaming-badge {
|
|
326
424
|
display: inline-flex;
|
|
@@ -331,14 +429,22 @@
|
|
|
331
429
|
|
|
332
430
|
.streaming-dot {
|
|
333
431
|
display: inline-block;
|
|
432
|
+
<<<<<<< HEAD
|
|
433
|
+
width: 0.5rem;
|
|
434
|
+
height: 0.5rem;
|
|
435
|
+
=======
|
|
334
436
|
width: 0.4rem;
|
|
335
437
|
height: 0.4rem;
|
|
438
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
336
439
|
border-radius: 50%;
|
|
337
440
|
background-color: var(--color-success);
|
|
338
441
|
animation: pulse 1.5s ease-in-out infinite;
|
|
339
442
|
}
|
|
340
443
|
|
|
341
444
|
.conversation-item.active .streaming-dot {
|
|
445
|
+
<<<<<<< HEAD
|
|
446
|
+
background-color: #fff;
|
|
447
|
+
=======
|
|
342
448
|
background-color: var(--color-success);
|
|
343
449
|
}
|
|
344
450
|
|
|
@@ -362,6 +468,7 @@
|
|
|
362
468
|
@keyframes typing-bounce {
|
|
363
469
|
0%, 80%, 100% { transform: scale(0.7); opacity: 0.5; }
|
|
364
470
|
40% { transform: scale(1); opacity: 1; }
|
|
471
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
365
472
|
}
|
|
366
473
|
|
|
367
474
|
.conversation-item {
|
|
@@ -377,6 +484,8 @@
|
|
|
377
484
|
overflow: hidden;
|
|
378
485
|
}
|
|
379
486
|
|
|
487
|
+
<<<<<<< HEAD
|
|
488
|
+
=======
|
|
380
489
|
/* Date group headers in sidebar */
|
|
381
490
|
.conv-date-group-header {
|
|
382
491
|
padding: 0.5rem 1.25rem 0.25rem;
|
|
@@ -402,6 +511,7 @@
|
|
|
402
511
|
margin-right: 0.375rem;
|
|
403
512
|
}
|
|
404
513
|
|
|
514
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
405
515
|
.conversation-item.pinned { cursor: grab; }
|
|
406
516
|
.conversation-item.pinned:active { cursor: grabbing; }
|
|
407
517
|
.conversation-item-checkbox {
|
|
@@ -459,16 +569,25 @@
|
|
|
459
569
|
.conversation-item.active .conversation-item-delete,
|
|
460
570
|
.conversation-item.active .conversation-item-archive,
|
|
461
571
|
.conversation-item.active .conversation-item-export {
|
|
572
|
+
<<<<<<< HEAD
|
|
573
|
+
color: rgba(255,255,255,0.8);
|
|
574
|
+
=======
|
|
462
575
|
color: var(--color-primary);
|
|
463
576
|
opacity: 0.7;
|
|
577
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
464
578
|
}
|
|
465
579
|
|
|
466
580
|
.conversation-item.active .conversation-item-delete:hover,
|
|
467
581
|
.conversation-item.active .conversation-item-archive:hover,
|
|
468
582
|
.conversation-item.active .conversation-item-export:hover {
|
|
583
|
+
<<<<<<< HEAD
|
|
584
|
+
background-color: rgba(255,255,255,0.2);
|
|
585
|
+
color: white;
|
|
586
|
+
=======
|
|
469
587
|
background-color: rgba(var(--color-primary-rgb),0.15);
|
|
470
588
|
color: var(--color-primary);
|
|
471
589
|
opacity: 1;
|
|
590
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
472
591
|
}
|
|
473
592
|
|
|
474
593
|
.sidebar-empty {
|
|
@@ -494,11 +613,18 @@
|
|
|
494
613
|
align-items: center;
|
|
495
614
|
gap: 0.75rem;
|
|
496
615
|
padding: 0 1rem;
|
|
616
|
+
<<<<<<< HEAD
|
|
617
|
+
height: var(--header-height);
|
|
618
|
+
min-height: var(--header-height);
|
|
619
|
+
flex-shrink: 0;
|
|
620
|
+
background-color: var(--color-bg-secondary);
|
|
621
|
+
=======
|
|
497
622
|
height: 48px;
|
|
498
623
|
min-height: 48px;
|
|
499
624
|
flex-shrink: 0;
|
|
500
625
|
background: var(--color-bg-primary);
|
|
501
626
|
border-bottom: 1px solid var(--color-border);
|
|
627
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
502
628
|
}
|
|
503
629
|
|
|
504
630
|
.sidebar-toggle-btn {
|
|
@@ -528,9 +654,14 @@
|
|
|
528
654
|
}
|
|
529
655
|
|
|
530
656
|
.header-title {
|
|
657
|
+
<<<<<<< HEAD
|
|
658
|
+
font-size: 1.125rem;
|
|
659
|
+
font-weight: 600;
|
|
660
|
+
=======
|
|
531
661
|
font-size: 0.9375rem;
|
|
532
662
|
font-weight: 600;
|
|
533
663
|
letter-spacing: -0.01em;
|
|
664
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
534
665
|
margin: 0;
|
|
535
666
|
flex: 1;
|
|
536
667
|
min-width: 0;
|
|
@@ -693,6 +824,8 @@
|
|
|
693
824
|
flex-direction: column;
|
|
694
825
|
}
|
|
695
826
|
|
|
827
|
+
<<<<<<< HEAD
|
|
828
|
+
=======
|
|
696
829
|
/* ===== MESSAGE BUBBLES ===== */
|
|
697
830
|
.message-user-bubble {
|
|
698
831
|
display: flex;
|
|
@@ -771,6 +904,7 @@
|
|
|
771
904
|
.message-action-btn svg { width: 13px; height: 13px; }
|
|
772
905
|
.message-wrapper { position: relative; }
|
|
773
906
|
|
|
907
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
774
908
|
#output {
|
|
775
909
|
display: flex;
|
|
776
910
|
flex-direction: column;
|
|
@@ -1017,6 +1151,15 @@
|
|
|
1017
1151
|
white-space: nowrap;
|
|
1018
1152
|
}
|
|
1019
1153
|
|
|
1154
|
+
<<<<<<< HEAD
|
|
1155
|
+
/* --- Input area: fixed at bottom --- */
|
|
1156
|
+
.input-section {
|
|
1157
|
+
flex-shrink: 0;
|
|
1158
|
+
background-color: var(--color-bg-primary);
|
|
1159
|
+
padding: 0.75rem 1rem;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
=======
|
|
1020
1163
|
/* ===== INPUT AREA REDESIGN ===== */
|
|
1021
1164
|
.input-section {
|
|
1022
1165
|
flex-shrink: 0;
|
|
@@ -1139,6 +1282,7 @@
|
|
|
1139
1282
|
.input-send-btn:disabled { opacity: 0.4; cursor: not-allowed; transform: none; }
|
|
1140
1283
|
.input-send-btn svg { width: 16px; height: 16px; }
|
|
1141
1284
|
|
|
1285
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
1142
1286
|
.input-wrapper {
|
|
1143
1287
|
max-width: var(--msg-max-width);
|
|
1144
1288
|
margin: 0 auto;
|
|
@@ -1401,9 +1545,15 @@
|
|
|
1401
1545
|
.permanently-expanded > summary::marker { display: none; content: ''; }
|
|
1402
1546
|
|
|
1403
1547
|
/* ===== Folder Browser Modal ===== */
|
|
1548
|
+
<<<<<<< HEAD
|
|
1549
|
+
.folder-modal-overlay { display:none; position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.5); z-index:2000; align-items:center; justify-content:center; }
|
|
1550
|
+
.folder-modal-overlay.visible { display:flex; }
|
|
1551
|
+
.folder-modal { background:var(--color-bg-primary); border-radius:0.5rem; width:500px; max-width:90vw; max-height:80vh; display:flex; flex-direction:column; box-shadow:0 20px 60px rgba(0,0,0,0.3); }
|
|
1552
|
+
=======
|
|
1404
1553
|
.folder-modal-overlay { display:none; position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.5); backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px); z-index:2000; align-items:center; justify-content:center; transition:opacity 0.15s ease; animation:fadeIn 0.15s ease; }
|
|
1405
1554
|
.folder-modal-overlay.visible { display:flex; }
|
|
1406
1555
|
.folder-modal { background:var(--color-bg-primary); border-radius:var(--radius-xl); width:500px; max-width:90vw; max-height:80vh; display:flex; flex-direction:column; box-shadow:var(--shadow-lg); animation:scaleIn 0.15s ease; }
|
|
1556
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
1407
1557
|
.folder-modal-header { padding:1rem; display:flex; justify-content:space-between; align-items:center; flex-shrink:0; }
|
|
1408
1558
|
.folder-modal-header h3 { margin:0; font-size:1rem; font-weight:600; }
|
|
1409
1559
|
.folder-modal-close { background:none; border:none; font-size:1.25rem; cursor:pointer; color:var(--color-text-secondary); padding:0.25rem; line-height:1; }
|
|
@@ -1423,6 +1573,8 @@
|
|
|
1423
1573
|
.folder-modal-footer { padding:0.75rem 1rem; display:flex; justify-content:flex-end; gap:0.5rem; flex-shrink:0; }
|
|
1424
1574
|
.folder-modal-footer .btn { padding:0.5rem 1rem; font-size:0.8rem; }
|
|
1425
1575
|
|
|
1576
|
+
<<<<<<< HEAD
|
|
1577
|
+
=======
|
|
1426
1578
|
@keyframes fadeIn {
|
|
1427
1579
|
from { opacity: 0; }
|
|
1428
1580
|
to { opacity: 1; }
|
|
@@ -1432,6 +1584,7 @@
|
|
|
1432
1584
|
to { opacity: 1; transform: scale(1); }
|
|
1433
1585
|
}
|
|
1434
1586
|
|
|
1587
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
1435
1588
|
.btn { padding:0.5rem 1rem; border:none; border-radius:0.375rem; font-weight:500; cursor:pointer; transition:all 0.15s; font-size:0.875rem; }
|
|
1436
1589
|
.btn-primary { background-color:var(--color-primary); color:white; }
|
|
1437
1590
|
.btn-primary:hover:not(:disabled) { background-color:var(--color-primary-dark); }
|
|
@@ -1554,6 +1707,8 @@
|
|
|
1554
1707
|
scrollbar-color: #475569 transparent;
|
|
1555
1708
|
}
|
|
1556
1709
|
|
|
1710
|
+
<<<<<<< HEAD
|
|
1711
|
+
=======
|
|
1557
1712
|
/* Modern thin scrollbars */
|
|
1558
1713
|
.sidebar-list, #output-scroll, .message-scroll-area {
|
|
1559
1714
|
scrollbar-width: thin;
|
|
@@ -1563,6 +1718,7 @@
|
|
|
1563
1718
|
scrollbar-color: var(--color-border-strong) transparent;
|
|
1564
1719
|
}
|
|
1565
1720
|
|
|
1721
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
1566
1722
|
.voice-mic-btn {
|
|
1567
1723
|
position: absolute;
|
|
1568
1724
|
top: 4px;
|
|
@@ -1818,6 +1974,8 @@
|
|
|
1818
1974
|
.icon-sm svg { width: 1rem !important; height: 1rem !important; }
|
|
1819
1975
|
.icon-lg svg { width: 1.5rem !important; height: 1.5rem !important; }
|
|
1820
1976
|
|
|
1977
|
+
<<<<<<< HEAD
|
|
1978
|
+
=======
|
|
1821
1979
|
/* ===== STREAMING STATUS BAR ===== */
|
|
1822
1980
|
.streaming-status-bar {
|
|
1823
1981
|
max-width: var(--msg-max-width);
|
|
@@ -1942,6 +2100,7 @@
|
|
|
1942
2100
|
line-height: 1.4;
|
|
1943
2101
|
}
|
|
1944
2102
|
|
|
2103
|
+
>>>>>>> 6bfde951cbeb65ec72b73da9c23b9c8c0ba0bbc1
|
|
1945
2104
|
/* ===== STREAMING BLOCK STYLES ===== */
|
|
1946
2105
|
.block-text {
|
|
1947
2106
|
margin-bottom: 0;
|