@ia-ccun/code-agent-claw 0.0.3 → 0.0.4
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/bin/cli.js +20 -0
- package/dist/public/index.html +42 -21
- package/package.json +1 -1
- package/public/index.html +42 -21
- package/package/.claude/plan/aicode-ui-npm.md +0 -477
- package/package/README.md +0 -108
- package/package/bin/cli.js +0 -16
- package/package/config/default.json +0 -18
- package/package/package.json +0 -43
- package/package/public/aicode.svg +0 -1
- package/package/public/index-v3.html +0 -1757
- package/package/public/index.html +0 -1818
- package/package/public/index_v3.html +0 -1757
- package/package/public/juejin.css +0 -143
- package/package/src/config.ts +0 -239
- package/package/src/index.ts +0 -40
- package/package/src/server/index.ts +0 -103
- package/package/src/server/routes.ts +0 -342
- package/package/src/server/websocket.ts +0 -82
- package/package/src/services/agent-rpc.ts +0 -397
- package/package/src/types/index.ts +0 -60
- package/package/src/utils/logger.ts +0 -13
- package/package/tsconfig.json +0 -19
|
@@ -1,1757 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="zh-CN">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>AICode Agent</title>
|
|
7
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
-
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
10
|
-
<link rel="stylesheet" href="juejin.css">
|
|
11
|
-
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
12
|
-
<style>
|
|
13
|
-
/* ========================================
|
|
14
|
-
ANTHROPIC STYLE - AI Agent Console
|
|
15
|
-
Warm earth tones with terracotta accents
|
|
16
|
-
======================================== */
|
|
17
|
-
|
|
18
|
-
:root {
|
|
19
|
-
/* Anthropic Design System - Warm earth palette */
|
|
20
|
-
--primary: #f0eee6;
|
|
21
|
-
--primary-light: #ffffff;
|
|
22
|
-
--primary-dark: #e8e6dc;
|
|
23
|
-
--secondary: #3d3d3a;
|
|
24
|
-
--accent: #d97757;
|
|
25
|
-
--accent-light: rgba(217, 119, 87, 0.15);
|
|
26
|
-
--muted: #b0aea5;
|
|
27
|
-
|
|
28
|
-
/* Backgrounds */
|
|
29
|
-
--bg-primary: #f0eee6;
|
|
30
|
-
--bg-secondary: #ffffff;
|
|
31
|
-
--bg-tertiary: #e8e6dc;
|
|
32
|
-
--bg-elevated: #ffffff;
|
|
33
|
-
--bg-hover: #e8e6dc;
|
|
34
|
-
--bg-code: #f5f4f0;
|
|
35
|
-
|
|
36
|
-
/* Borders */
|
|
37
|
-
--border-subtle: #e0e0dc;
|
|
38
|
-
--border-default: #d0cec5;
|
|
39
|
-
--border-active: #d97757;
|
|
40
|
-
|
|
41
|
-
/* Accent - terracotta */
|
|
42
|
-
--accent-primary: #d97757;
|
|
43
|
-
--accent-secondary: #c45d3d;
|
|
44
|
-
--accent-glow: rgba(217, 119, 87, 0.2);
|
|
45
|
-
--accent-subtle: rgba(217, 119, 87, 0.1);
|
|
46
|
-
|
|
47
|
-
/* Text */
|
|
48
|
-
--text-primary: #3d3d3a;
|
|
49
|
-
--text-secondary: #5a5a55;
|
|
50
|
-
--text-muted: #87867f;
|
|
51
|
-
--text-accent: #d97757;
|
|
52
|
-
|
|
53
|
-
/* Status colors */
|
|
54
|
-
--success: #4a9d6d;
|
|
55
|
-
--warning: #c9852d;
|
|
56
|
-
--error: #c94a4a;
|
|
57
|
-
|
|
58
|
-
/* Spacing */
|
|
59
|
-
--space-xs: 4px;
|
|
60
|
-
--space-sm: 8px;
|
|
61
|
-
--space-md: 12px;
|
|
62
|
-
--space-lg: 16px;
|
|
63
|
-
--space-xl: 24px;
|
|
64
|
-
--space-2xl: 32px;
|
|
65
|
-
|
|
66
|
-
/* Typography - Inter for clean readability */
|
|
67
|
-
--font-display: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
68
|
-
--font-mono: 'JetBrains Mono', 'SF Mono', Consolas, monospace;
|
|
69
|
-
|
|
70
|
-
/* Effects */
|
|
71
|
-
--shadow-sm: 0 1px 3px rgba(61, 61, 58, 0.08);
|
|
72
|
-
--shadow-md: 0 4px 12px rgba(61, 61, 58, 0.1);
|
|
73
|
-
--shadow-glow: 0 0 20px var(--accent-glow);
|
|
74
|
-
--radius-sm: 6px;
|
|
75
|
-
--radius-md: 10px;
|
|
76
|
-
--radius-lg: 14px;
|
|
77
|
-
--radius-xl: 20px;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/* Dark theme - preserve dark aesthetic with terracotta accent */
|
|
81
|
-
body.dark {
|
|
82
|
-
--primary: #1a1a18;
|
|
83
|
-
--primary-light: #252523;
|
|
84
|
-
--primary-dark: #141413;
|
|
85
|
-
--secondary: #b0aea5;
|
|
86
|
-
--accent: #d97757;
|
|
87
|
-
--muted: #6a6a65;
|
|
88
|
-
|
|
89
|
-
--bg-primary: #141413;
|
|
90
|
-
--bg-secondary: #1a1a18;
|
|
91
|
-
--bg-tertiary: #222220;
|
|
92
|
-
--bg-elevated: #2a2a28;
|
|
93
|
-
--bg-hover: #323230;
|
|
94
|
-
--bg-code: #1e1e1c;
|
|
95
|
-
|
|
96
|
-
--border-subtle: #2a2a28;
|
|
97
|
-
--border-default: #3a3a38;
|
|
98
|
-
|
|
99
|
-
--text-primary: #f0eee6;
|
|
100
|
-
--text-secondary: #b0aea5;
|
|
101
|
-
--text-muted: #6a6a65;
|
|
102
|
-
--text-accent: #d97757;
|
|
103
|
-
|
|
104
|
-
--accent-glow: rgba(217, 119, 87, 0.25);
|
|
105
|
-
--accent-subtle: rgba(217, 119, 87, 0.12);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
* {
|
|
109
|
-
box-sizing: border-box;
|
|
110
|
-
margin: 0;
|
|
111
|
-
padding: 0;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
body {
|
|
115
|
-
font-family: var(--font-display);
|
|
116
|
-
background: var(--bg-primary);
|
|
117
|
-
min-height: 100vh;
|
|
118
|
-
color: var(--text-primary);
|
|
119
|
-
line-height: 1.5;
|
|
120
|
-
-webkit-font-smoothing: antialiased;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/* Background texture - subtle warmth */
|
|
124
|
-
body::before {
|
|
125
|
-
content: '';
|
|
126
|
-
position: fixed;
|
|
127
|
-
top: 0;
|
|
128
|
-
left: 0;
|
|
129
|
-
right: 0;
|
|
130
|
-
bottom: 0;
|
|
131
|
-
background:
|
|
132
|
-
radial-gradient(ellipse at 0% 0%, var(--accent-subtle) 0%, transparent 60%),
|
|
133
|
-
radial-gradient(ellipse at 100% 100%, rgba(217, 119, 87, 0.05) 0%, transparent 50%);
|
|
134
|
-
pointer-events: none;
|
|
135
|
-
z-index: 0;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
.container {
|
|
139
|
-
max-width: 1400px;
|
|
140
|
-
margin: 0 auto;
|
|
141
|
-
height: 100vh;
|
|
142
|
-
display: flex;
|
|
143
|
-
flex-direction: column;
|
|
144
|
-
position: relative;
|
|
145
|
-
z-index: 1;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/* ========================================
|
|
149
|
-
HEADER
|
|
150
|
-
======================================== */
|
|
151
|
-
.header {
|
|
152
|
-
display: flex;
|
|
153
|
-
align-items: center;
|
|
154
|
-
justify-content: space-between;
|
|
155
|
-
padding: var(--space-lg) var(--space-xl);
|
|
156
|
-
background: var(--bg-secondary);
|
|
157
|
-
border-bottom: 1px solid var(--border-subtle);
|
|
158
|
-
animation: slideDown 0.4s ease-out;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
@keyframes slideDown {
|
|
162
|
-
from { opacity: 0; transform: translateY(-10px); }
|
|
163
|
-
to { opacity: 1; transform: translateY(0); }
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
.header-left {
|
|
167
|
-
display: flex;
|
|
168
|
-
align-items: center;
|
|
169
|
-
gap: var(--space-md);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
.logo {
|
|
173
|
-
display: flex;
|
|
174
|
-
align-items: center;
|
|
175
|
-
gap: var(--space-sm);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
.logo-icon {
|
|
179
|
-
width: 32px;
|
|
180
|
-
height: 32px;
|
|
181
|
-
border-radius: var(--radius-md);
|
|
182
|
-
box-shadow: var(--shadow-glow);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
.logo-text {
|
|
186
|
-
font-size: 1.25rem;
|
|
187
|
-
font-weight: 700;
|
|
188
|
-
letter-spacing: -0.02em;
|
|
189
|
-
background: linear-gradient(135deg, var(--text-primary) 60%, var(--accent-primary) 100%);
|
|
190
|
-
-webkit-background-clip: text;
|
|
191
|
-
-webkit-text-fill-color: transparent;
|
|
192
|
-
background-clip: text;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
.header-right {
|
|
196
|
-
display: flex;
|
|
197
|
-
align-items: center;
|
|
198
|
-
gap: var(--space-md);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/* 配置弹窗 */
|
|
202
|
-
.modal-overlay {
|
|
203
|
-
display: none;
|
|
204
|
-
position: fixed;
|
|
205
|
-
top: 0;
|
|
206
|
-
left: 0;
|
|
207
|
-
width: 100%;
|
|
208
|
-
height: 100%;
|
|
209
|
-
background: rgba(0, 0, 0, 0.6);
|
|
210
|
-
z-index: 1000;
|
|
211
|
-
justify-content: center;
|
|
212
|
-
align-items: center;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
.modal-overlay.show {
|
|
216
|
-
display: flex;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
.modal {
|
|
220
|
-
background: var(--bg-secondary);
|
|
221
|
-
border: 1px solid var(--border-default);
|
|
222
|
-
padding: 32px;
|
|
223
|
-
width: 90%;
|
|
224
|
-
max-width: 500px;
|
|
225
|
-
border-radius: 12px;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
.modal h2 {
|
|
229
|
-
color: var(--text-primary);
|
|
230
|
-
font-size: 1.25rem;
|
|
231
|
-
margin-bottom: 16px;
|
|
232
|
-
padding-bottom: 16px;
|
|
233
|
-
border-bottom: 1px solid var(--border-subtle);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
.modal .config-tips {
|
|
237
|
-
color: var(--text-secondary);
|
|
238
|
-
font-size: 0.8rem;
|
|
239
|
-
margin-top: 16px;
|
|
240
|
-
padding: 12px;
|
|
241
|
-
background: var(--bg-secondary);
|
|
242
|
-
border-radius: 6px;
|
|
243
|
-
border: 1px solid var(--border-subtle);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
.modal .config-tips p {
|
|
247
|
-
margin-bottom: 8px;
|
|
248
|
-
color: var(--text-primary);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
.modal .config-tips ul {
|
|
252
|
-
margin: 0;
|
|
253
|
-
padding-left: 20px;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
.modal .config-tips li {
|
|
257
|
-
margin-bottom: 4px;
|
|
258
|
-
color: var(--text-secondary);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
.modal .config-tips code {
|
|
262
|
-
background: var(--bg-tertiary);
|
|
263
|
-
padding: 2px 6px;
|
|
264
|
-
border-radius: 4px;
|
|
265
|
-
font-family: 'JetBrains Mono', monospace;
|
|
266
|
-
font-size: 0.75rem;
|
|
267
|
-
color: var(--accent-green);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
.modal label .field-tip {
|
|
271
|
-
font-size: 0.7rem;
|
|
272
|
-
color: var(--text-muted);
|
|
273
|
-
font-weight: 400;
|
|
274
|
-
margin-left: 8px;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
.modal .form-group {
|
|
278
|
-
margin-bottom: 16px;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
.modal label {
|
|
282
|
-
display: block;
|
|
283
|
-
margin-bottom: 8px;
|
|
284
|
-
color: var(--text-secondary);
|
|
285
|
-
font-size: 0.75rem;
|
|
286
|
-
font-weight: 500;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
.modal input[type="text"], .modal select {
|
|
290
|
-
width: 100%;
|
|
291
|
-
padding: 10px;
|
|
292
|
-
background: var(--bg-primary);
|
|
293
|
-
border: 1px solid var(--border-default);
|
|
294
|
-
border-radius: 6px;
|
|
295
|
-
font-size: 0.875rem;
|
|
296
|
-
color: var(--text-primary);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
.modal input[type="text"]:focus, .modal select:focus {
|
|
300
|
-
outline: none;
|
|
301
|
-
border-color: var(--accent);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
.modal input[type="checkbox"] {
|
|
305
|
-
margin-right: 8px;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
.modal .btn-group {
|
|
309
|
-
display: flex;
|
|
310
|
-
gap: 12px;
|
|
311
|
-
margin-top: 24px;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
.modal .btn-primary {
|
|
315
|
-
padding: 10px 20px;
|
|
316
|
-
background: var(--accent);
|
|
317
|
-
color: white;
|
|
318
|
-
border: none;
|
|
319
|
-
border-radius: 6px;
|
|
320
|
-
cursor: pointer;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
.modal .btn-secondary {
|
|
324
|
-
padding: 10px 20px;
|
|
325
|
-
background: transparent;
|
|
326
|
-
border: 1px solid var(--border-default);
|
|
327
|
-
border-radius: 6px;
|
|
328
|
-
cursor: pointer;
|
|
329
|
-
color: var(--text-secondary);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
.modal .error-message {
|
|
333
|
-
color: #ef4444;
|
|
334
|
-
font-size: 0.75rem;
|
|
335
|
-
margin-top: 8px;
|
|
336
|
-
display: none;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
.modal .error-message.show {
|
|
340
|
-
display: block;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
.modal .loading {
|
|
344
|
-
color: var(--text-muted);
|
|
345
|
-
font-size: 0.75rem;
|
|
346
|
-
margin-top: 8px;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/* Status indicator */
|
|
350
|
-
.status-badge {
|
|
351
|
-
display: flex;
|
|
352
|
-
align-items: center;
|
|
353
|
-
gap: var(--space-sm);
|
|
354
|
-
padding: var(--space-sm) var(--space-md);
|
|
355
|
-
background: var(--bg-tertiary);
|
|
356
|
-
border: 1px solid var(--border-subtle);
|
|
357
|
-
border-radius: 20px;
|
|
358
|
-
font-size: 0.75rem;
|
|
359
|
-
color: var(--text-secondary);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
.status-dot {
|
|
363
|
-
width: 8px;
|
|
364
|
-
height: 8px;
|
|
365
|
-
border-radius: 50%;
|
|
366
|
-
background: var(--text-muted);
|
|
367
|
-
transition: all 0.3s ease;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
.status-dot.active {
|
|
371
|
-
background: var(--success);
|
|
372
|
-
box-shadow: 0 0 8px rgba(34, 197, 94, 0.5);
|
|
373
|
-
animation: pulse 2s ease-in-out infinite;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
@keyframes pulse {
|
|
377
|
-
0%, 100% { opacity: 1; }
|
|
378
|
-
50% { opacity: 0.6; }
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/* Action buttons */
|
|
382
|
-
.action-btn {
|
|
383
|
-
width: 36px;
|
|
384
|
-
height: 36px;
|
|
385
|
-
border-radius: var(--radius-md);
|
|
386
|
-
background: var(--bg-tertiary);
|
|
387
|
-
border: 1px solid var(--border-subtle);
|
|
388
|
-
color: var(--text-secondary);
|
|
389
|
-
cursor: pointer;
|
|
390
|
-
display: flex;
|
|
391
|
-
align-items: center;
|
|
392
|
-
justify-content: center;
|
|
393
|
-
font-size: 1rem;
|
|
394
|
-
transition: all 0.2s ease;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
.action-btn:hover {
|
|
398
|
-
background: var(--bg-hover);
|
|
399
|
-
color: var(--text-primary);
|
|
400
|
-
border-color: var(--border-default);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
.action-btn.active {
|
|
404
|
-
background: var(--accent-subtle);
|
|
405
|
-
border-color: var(--accent-primary);
|
|
406
|
-
color: var(--accent-primary);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
/* ========================================
|
|
410
|
-
MAIN CONTENT - Two Panel Layout
|
|
411
|
-
======================================== */
|
|
412
|
-
.main-content {
|
|
413
|
-
flex: 1;
|
|
414
|
-
display: grid;
|
|
415
|
-
grid-template-columns: 1fr 380px;
|
|
416
|
-
gap: 0;
|
|
417
|
-
min-height: 0;
|
|
418
|
-
overflow: hidden;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
/* Left Panel - Messages */
|
|
422
|
-
.messages-panel {
|
|
423
|
-
display: flex;
|
|
424
|
-
flex-direction: column;
|
|
425
|
-
border-right: 1px solid var(--border-subtle);
|
|
426
|
-
animation: fadeIn 0.5s ease-out 0.1s both;
|
|
427
|
-
min-height: 0;
|
|
428
|
-
overflow-y: auto;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
@keyframes fadeIn {
|
|
432
|
-
from { opacity: 0; }
|
|
433
|
-
to { opacity: 1; }
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/* Tabs */
|
|
437
|
-
.panel-tabs {
|
|
438
|
-
display: flex;
|
|
439
|
-
gap: var(--space-xs);
|
|
440
|
-
padding: var(--space-md) var(--space-lg);
|
|
441
|
-
background: var(--bg-secondary);
|
|
442
|
-
border-bottom: 1px solid var(--border-subtle);
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
.panel-tab {
|
|
446
|
-
padding: var(--space-sm) var(--space-lg);
|
|
447
|
-
background: transparent;
|
|
448
|
-
border: none;
|
|
449
|
-
color: var(--text-muted);
|
|
450
|
-
font-family: var(--font-mono);
|
|
451
|
-
font-size: 0.75rem;
|
|
452
|
-
font-weight: 500;
|
|
453
|
-
cursor: pointer;
|
|
454
|
-
border-radius: var(--radius-sm);
|
|
455
|
-
transition: all 0.2s ease;
|
|
456
|
-
position: relative;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
.panel-tab:hover {
|
|
460
|
-
color: var(--text-secondary);
|
|
461
|
-
background: var(--bg-hover);
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
.panel-tab.active {
|
|
465
|
-
color: var(--accent-primary);
|
|
466
|
-
background: var(--accent-subtle);
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
.panel-tab.active::after {
|
|
470
|
-
content: '';
|
|
471
|
-
position: absolute;
|
|
472
|
-
bottom: -13px;
|
|
473
|
-
left: 50%;
|
|
474
|
-
transform: translateX(-50%);
|
|
475
|
-
width: 20px;
|
|
476
|
-
height: 2px;
|
|
477
|
-
background: var(--accent-primary);
|
|
478
|
-
border-radius: 1px;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
/* Panel content */
|
|
482
|
-
.panel-content {
|
|
483
|
-
flex: 1;
|
|
484
|
-
overflow-y: auto;
|
|
485
|
-
padding: var(--space-lg);
|
|
486
|
-
display: flex;
|
|
487
|
-
flex-direction: column;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
.panel-content.hidden {
|
|
491
|
-
display: none;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
/* Empty state */
|
|
495
|
-
.empty-state {
|
|
496
|
-
height: 100%;
|
|
497
|
-
display: flex;
|
|
498
|
-
flex-direction: column;
|
|
499
|
-
align-items: center;
|
|
500
|
-
justify-content: center;
|
|
501
|
-
color: var(--text-muted);
|
|
502
|
-
gap: var(--space-md);
|
|
503
|
-
padding: var(--space-2xl);
|
|
504
|
-
text-align: center;
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
.empty-logo {
|
|
508
|
-
width: 80px;
|
|
509
|
-
height: 80px;
|
|
510
|
-
margin-bottom: var(--space-lg);
|
|
511
|
-
opacity: 0.9;
|
|
512
|
-
animation: float 3s ease-in-out infinite;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
@keyframes float {
|
|
516
|
-
0%, 100% { transform: translateY(0); }
|
|
517
|
-
50% { transform: translateY(-8px); }
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
.empty-title {
|
|
521
|
-
font-size: 1.5rem;
|
|
522
|
-
font-weight: 700;
|
|
523
|
-
color: var(--text-primary);
|
|
524
|
-
margin-bottom: var(--space-sm);
|
|
525
|
-
letter-spacing: -0.02em;
|
|
526
|
-
overflow: hidden;
|
|
527
|
-
white-space: nowrap;
|
|
528
|
-
display: inline-block;
|
|
529
|
-
max-width: 320px;
|
|
530
|
-
animation:
|
|
531
|
-
typing 1.5s steps(17) forwards,
|
|
532
|
-
delete-typing 1.5s steps(17) 2s forwards,
|
|
533
|
-
show-again 0.3s steps(17) 3.5s forwards;
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
@keyframes typing {
|
|
537
|
-
from { max-width: 0; }
|
|
538
|
-
to { max-width: 320px; }
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
@keyframes delete-typing {
|
|
542
|
-
from { max-width: 320px; }
|
|
543
|
-
to { max-width: 0; }
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
@keyframes show-again {
|
|
547
|
-
from { max-width: 0; opacity: 0; }
|
|
548
|
-
to { max-width: 320px; opacity: 1; }
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
.empty-desc {
|
|
552
|
-
font-size: 0.875rem;
|
|
553
|
-
color: var(--text-secondary);
|
|
554
|
-
margin-bottom: var(--space-xl);
|
|
555
|
-
opacity: 0;
|
|
556
|
-
animation: fadeIn 0.5s ease 2.5s forwards;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
.empty-features {
|
|
560
|
-
display: flex;
|
|
561
|
-
flex-direction: column;
|
|
562
|
-
gap: var(--space-md);
|
|
563
|
-
width: 100%;
|
|
564
|
-
max-width: 280px;
|
|
565
|
-
opacity: 0;
|
|
566
|
-
animation: fadeIn 0.5s ease 3s forwards;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
.feature-item {
|
|
570
|
-
display: flex;
|
|
571
|
-
align-items: center;
|
|
572
|
-
gap: var(--space-md);
|
|
573
|
-
padding: var(--space-md) var(--space-lg);
|
|
574
|
-
background: var(--bg-tertiary);
|
|
575
|
-
border: 1px solid var(--border-subtle);
|
|
576
|
-
border-radius: var(--radius-md);
|
|
577
|
-
font-size: 0.85rem;
|
|
578
|
-
color: var(--text-secondary);
|
|
579
|
-
transition: all 0.2s ease;
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
.feature-item:hover {
|
|
583
|
-
border-color: var(--accent-primary);
|
|
584
|
-
background: var(--accent-subtle);
|
|
585
|
-
transform: translateX(4px);
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
.feature-icon {
|
|
589
|
-
font-size: 1.1rem;
|
|
590
|
-
flex-shrink: 0;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
.empty-hint {
|
|
594
|
-
margin-top: var(--space-xl);
|
|
595
|
-
font-size: 0.75rem;
|
|
596
|
-
color: var(--text-muted);
|
|
597
|
-
padding: var(--space-sm) var(--space-md);
|
|
598
|
-
background: var(--bg-tertiary);
|
|
599
|
-
border-radius: var(--radius-sm);
|
|
600
|
-
border: 1px dashed var(--border-default);
|
|
601
|
-
opacity: 0;
|
|
602
|
-
animation: fadeIn 0.5s ease 3.5s forwards;
|
|
603
|
-
text-align: center;
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
.empty-icon {
|
|
607
|
-
font-size: 3rem;
|
|
608
|
-
opacity: 0.3;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
.empty-text {
|
|
612
|
-
font-size: 0.875rem;
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
/* Output items */
|
|
616
|
-
.output-item {
|
|
617
|
-
margin-bottom: var(--space-lg);
|
|
618
|
-
padding: var(--space-lg);
|
|
619
|
-
background: var(--bg-secondary);
|
|
620
|
-
border: 1px solid var(--border-subtle);
|
|
621
|
-
border-radius: var(--radius-md);
|
|
622
|
-
animation: slideUp 0.3s ease-out;
|
|
623
|
-
max-width: 85%;
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
/* AI messages - left aligned */
|
|
627
|
-
.output-item.assistant {
|
|
628
|
-
align-self: flex-start;
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
/* User messages - right aligned */
|
|
632
|
-
.output-item.user {
|
|
633
|
-
align-self: flex-end;
|
|
634
|
-
margin-left: auto;
|
|
635
|
-
border-color: var(--accent-subtle);
|
|
636
|
-
background: var(--bg-tertiary);
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
@keyframes slideUp {
|
|
640
|
-
from { opacity: 0; transform: translateY(10px); }
|
|
641
|
-
to { opacity: 1; transform: translateY(0); }
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
.output-item:hover {
|
|
645
|
-
border-color: var(--border-default);
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
.output-header {
|
|
649
|
-
display: flex;
|
|
650
|
-
align-items: center;
|
|
651
|
-
justify-content: space-between;
|
|
652
|
-
margin-bottom: var(--space-md);
|
|
653
|
-
padding-bottom: var(--space-sm);
|
|
654
|
-
border-bottom: 1px solid var(--border-subtle);
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
.output-meta {
|
|
658
|
-
display: flex;
|
|
659
|
-
align-items: center;
|
|
660
|
-
gap: var(--space-sm);
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
.output-role {
|
|
664
|
-
font-family: var(--font-mono);
|
|
665
|
-
font-size: 0.7rem;
|
|
666
|
-
font-weight: 600;
|
|
667
|
-
text-transform: uppercase;
|
|
668
|
-
letter-spacing: 0.05em;
|
|
669
|
-
padding: 2px 8px;
|
|
670
|
-
background: var(--bg-tertiary);
|
|
671
|
-
border-radius: var(--radius-sm);
|
|
672
|
-
color: var(--accent-primary);
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
.output-item.user .output-role {
|
|
676
|
-
color: var(--success);
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
.output-time {
|
|
680
|
-
font-family: var(--font-mono);
|
|
681
|
-
font-size: 0.65rem;
|
|
682
|
-
color: var(--text-muted);
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
.output-actions {
|
|
686
|
-
display: flex;
|
|
687
|
-
align-items: center;
|
|
688
|
-
gap: var(--space-xs);
|
|
689
|
-
opacity: 0;
|
|
690
|
-
transition: opacity 0.2s ease;
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
.output-item:hover .output-actions {
|
|
694
|
-
opacity: 1;
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
.copy-btn {
|
|
698
|
-
width: 22px;
|
|
699
|
-
height: 22px;
|
|
700
|
-
border-radius: var(--radius-sm);
|
|
701
|
-
background: transparent;
|
|
702
|
-
border: none;
|
|
703
|
-
color: var(--text-muted);
|
|
704
|
-
cursor: pointer;
|
|
705
|
-
display: flex;
|
|
706
|
-
align-items: center;
|
|
707
|
-
justify-content: center;
|
|
708
|
-
transition: all 0.2s ease;
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
.copy-btn:hover {
|
|
712
|
-
background: var(--accent-subtle);
|
|
713
|
-
color: var(--accent-primary);
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
.copy-btn.copied {
|
|
717
|
-
color: var(--success);
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
.copy-btn svg {
|
|
721
|
-
width: 14px;
|
|
722
|
-
height: 14px;
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
.output-content {
|
|
726
|
-
font-family: var(--font-mono);
|
|
727
|
-
font-size: 0.8rem;
|
|
728
|
-
line-height: 1.7;
|
|
729
|
-
color: var(--text-secondary);
|
|
730
|
-
white-space: pre-wrap;
|
|
731
|
-
word-break: break-word;
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
/* Event log */
|
|
735
|
-
.event-log {
|
|
736
|
-
font-family: var(--font-mono);
|
|
737
|
-
font-size: 0.7rem;
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
.event-item {
|
|
741
|
-
padding: var(--space-sm) 0;
|
|
742
|
-
border-bottom: 1px solid var(--border-subtle);
|
|
743
|
-
display: flex;
|
|
744
|
-
gap: var(--space-md);
|
|
745
|
-
animation: fadeIn 0.2s ease-out;
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
.event-time {
|
|
749
|
-
color: var(--text-muted);
|
|
750
|
-
flex-shrink: 0;
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
.event-type {
|
|
754
|
-
color: var(--accent-primary);
|
|
755
|
-
flex-shrink: 0;
|
|
756
|
-
min-width: 120px;
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
.event-data {
|
|
760
|
-
color: var(--text-muted);
|
|
761
|
-
overflow: hidden;
|
|
762
|
-
text-overflow: ellipsis;
|
|
763
|
-
white-space: nowrap;
|
|
764
|
-
max-width: 800px;
|
|
765
|
-
position: relative;
|
|
766
|
-
cursor: pointer;
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
.event-data:hover {
|
|
770
|
-
white-space: normal;
|
|
771
|
-
word-break: break-all;
|
|
772
|
-
max-width: 1200px;
|
|
773
|
-
background: var(--bg-tertiary);
|
|
774
|
-
padding: 4px 8px;
|
|
775
|
-
border-radius: var(--radius-sm);
|
|
776
|
-
margin: -4px -8px;
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
.event-actions {
|
|
780
|
-
display: none;
|
|
781
|
-
margin-left: auto;
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
.event-item:hover .event-actions {
|
|
785
|
-
display: flex;
|
|
786
|
-
gap: var(--space-xs);
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
.event-item {
|
|
790
|
-
padding: var(--space-sm) 0;
|
|
791
|
-
border-bottom: 1px solid var(--border-subtle);
|
|
792
|
-
display: flex;
|
|
793
|
-
gap: var(--space-md);
|
|
794
|
-
align-items: flex-start;
|
|
795
|
-
animation: fadeIn 0.2s ease-out;
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
/* ========================================
|
|
799
|
-
RIGHT PANEL - Input
|
|
800
|
-
======================================== */
|
|
801
|
-
.input-panel {
|
|
802
|
-
display: flex;
|
|
803
|
-
flex-direction: column;
|
|
804
|
-
padding: var(--space-lg);
|
|
805
|
-
gap: var(--space-lg);
|
|
806
|
-
background: var(--bg-secondary);
|
|
807
|
-
animation: fadeIn 0.5s ease-out 0.2s both;
|
|
808
|
-
height: calc(100vh - 65px);
|
|
809
|
-
flex-shrink: 0;
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
/* Cards */
|
|
813
|
-
.card {
|
|
814
|
-
background: var(--bg-tertiary);
|
|
815
|
-
border: 1px solid var(--border-subtle);
|
|
816
|
-
border-radius: var(--radius-md);
|
|
817
|
-
padding: var(--space-lg);
|
|
818
|
-
transition: all 0.2s ease;
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
.card:hover {
|
|
822
|
-
border-color: var(--border-default);
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
.card-title {
|
|
826
|
-
font-family: var(--font-mono);
|
|
827
|
-
font-size: 0.65rem;
|
|
828
|
-
font-weight: 600;
|
|
829
|
-
text-transform: uppercase;
|
|
830
|
-
letter-spacing: 0.1em;
|
|
831
|
-
color: var(--text-muted);
|
|
832
|
-
margin-bottom: var(--space-md);
|
|
833
|
-
display: flex;
|
|
834
|
-
align-items: center;
|
|
835
|
-
gap: var(--space-sm);
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
.card-title::before {
|
|
839
|
-
content: '';
|
|
840
|
-
width: 8px;
|
|
841
|
-
height: 8px;
|
|
842
|
-
background: var(--accent-primary);
|
|
843
|
-
border-radius: 2px;
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
/* Quick questions */
|
|
847
|
-
.quick-questions {
|
|
848
|
-
display: flex;
|
|
849
|
-
flex-wrap: wrap;
|
|
850
|
-
gap: var(--space-sm);
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
.quick-question {
|
|
854
|
-
padding: var(--space-sm) var(--space-md);
|
|
855
|
-
background: var(--bg-elevated);
|
|
856
|
-
border: 1px solid var(--border-subtle);
|
|
857
|
-
border-radius: var(--radius-sm);
|
|
858
|
-
font-family: var(--font-mono);
|
|
859
|
-
font-size: 0.7rem;
|
|
860
|
-
color: var(--text-secondary);
|
|
861
|
-
cursor: pointer;
|
|
862
|
-
transition: all 0.2s ease;
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
.quick-question:hover {
|
|
866
|
-
background: var(--accent-subtle);
|
|
867
|
-
border-color: var(--accent-primary);
|
|
868
|
-
color: var(--accent-primary);
|
|
869
|
-
transform: translateY(-1px);
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
/* Message type selector */
|
|
873
|
-
.msg-type-group {
|
|
874
|
-
display: flex;
|
|
875
|
-
flex-wrap: wrap;
|
|
876
|
-
gap: var(--space-xs);
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
.msg-type-group input[type="radio"] {
|
|
880
|
-
display: none;
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
.msg-type-group label {
|
|
884
|
-
padding: var(--space-sm) var(--space-md);
|
|
885
|
-
background: var(--bg-elevated);
|
|
886
|
-
border: 1px solid var(--border-subtle);
|
|
887
|
-
border-radius: var(--radius-sm);
|
|
888
|
-
font-family: var(--font-mono);
|
|
889
|
-
font-size: 0.7rem;
|
|
890
|
-
color: var(--text-secondary);
|
|
891
|
-
cursor: pointer;
|
|
892
|
-
transition: all 0.2s ease;
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
.msg-type-group label:hover {
|
|
896
|
-
border-color: var(--border-default);
|
|
897
|
-
color: var(--text-primary);
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
.msg-type-group input[type="radio"]:checked + label {
|
|
901
|
-
background: var(--accent-subtle);
|
|
902
|
-
border-color: var(--accent-primary);
|
|
903
|
-
color: var(--accent-primary);
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
/* Type-specific colors */
|
|
907
|
-
#type-prompt:checked + label { border-color: var(--accent-primary); color: var(--accent-primary); }
|
|
908
|
-
#type-steer:checked + label { border-color: var(--warning); color: var(--warning); }
|
|
909
|
-
#type-follow:checked + label { border-color: var(--success); color: var(--success); }
|
|
910
|
-
#type-abort:checked + label { border-color: var(--error); color: var(--error); }
|
|
911
|
-
|
|
912
|
-
/* Input area */
|
|
913
|
-
.input-card {
|
|
914
|
-
flex: 1;
|
|
915
|
-
display: flex;
|
|
916
|
-
flex-direction: column;
|
|
917
|
-
min-height: 200px;
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
.input-wrapper {
|
|
921
|
-
flex: 1;
|
|
922
|
-
position: relative;
|
|
923
|
-
display: flex;
|
|
924
|
-
flex-direction: column;
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
textarea {
|
|
928
|
-
flex: 1;
|
|
929
|
-
width: 100%;
|
|
930
|
-
padding: var(--space-md);
|
|
931
|
-
background: var(--bg-elevated);
|
|
932
|
-
border: 1px solid var(--border-subtle);
|
|
933
|
-
border-radius: var(--radius-md);
|
|
934
|
-
font-family: var(--font-mono);
|
|
935
|
-
font-size: 0.8rem;
|
|
936
|
-
color: var(--text-primary);
|
|
937
|
-
resize: none;
|
|
938
|
-
transition: all 0.2s ease;
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
textarea:focus {
|
|
942
|
-
outline: none;
|
|
943
|
-
border-color: var(--accent-primary);
|
|
944
|
-
box-shadow: 0 0 0 3px var(--accent-glow);
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
textarea::placeholder {
|
|
948
|
-
color: var(--text-muted);
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
.input-actions {
|
|
952
|
-
display: flex;
|
|
953
|
-
justify-content: flex-end;
|
|
954
|
-
margin-top: var(--space-md);
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
.btn-send {
|
|
958
|
-
padding: var(--space-sm) var(--space-xl);
|
|
959
|
-
background: var(--accent-primary);
|
|
960
|
-
border: none;
|
|
961
|
-
border-radius: var(--radius-md);
|
|
962
|
-
font-family: var(--font-mono);
|
|
963
|
-
font-size: 0.75rem;
|
|
964
|
-
font-weight: 600;
|
|
965
|
-
color: var(--bg-primary);
|
|
966
|
-
cursor: pointer;
|
|
967
|
-
transition: all 0.2s ease;
|
|
968
|
-
display: flex;
|
|
969
|
-
align-items: center;
|
|
970
|
-
gap: var(--space-sm);
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
.btn-send:hover {
|
|
974
|
-
background: var(--accent-secondary);
|
|
975
|
-
transform: translateY(-1px);
|
|
976
|
-
box-shadow: var(--shadow-glow);
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
.btn-send:disabled {
|
|
980
|
-
background: var(--bg-hover);
|
|
981
|
-
color: var(--text-muted);
|
|
982
|
-
cursor: not-allowed;
|
|
983
|
-
transform: none;
|
|
984
|
-
box-shadow: none;
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
/* Loading animation */
|
|
988
|
-
.loading {
|
|
989
|
-
display: inline-flex;
|
|
990
|
-
gap: 4px;
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
.loading span {
|
|
994
|
-
width: 6px;
|
|
995
|
-
height: 6px;
|
|
996
|
-
background: var(--accent-primary);
|
|
997
|
-
border-radius: 50%;
|
|
998
|
-
animation: bounce 1.4s infinite ease-in-out;
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
.loading span:nth-child(1) { animation-delay: -0.32s; }
|
|
1002
|
-
.loading span:nth-child(2) { animation-delay: -0.16s; }
|
|
1003
|
-
|
|
1004
|
-
@keyframes bounce {
|
|
1005
|
-
0%, 80%, 100% { transform: scale(0); }
|
|
1006
|
-
40% { transform: scale(1); }
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
/* Cursor blink */
|
|
1010
|
-
.cursor {
|
|
1011
|
-
display: inline-block;
|
|
1012
|
-
width: 2px;
|
|
1013
|
-
height: 1em;
|
|
1014
|
-
background: var(--accent-primary);
|
|
1015
|
-
margin-left: 2px;
|
|
1016
|
-
animation: blink 1s infinite;
|
|
1017
|
-
vertical-align: text-bottom;
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
@keyframes blink {
|
|
1021
|
-
0%, 50% { opacity: 1; }
|
|
1022
|
-
51%, 100% { opacity: 0; }
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
/* Scrollbar */
|
|
1026
|
-
::-webkit-scrollbar {
|
|
1027
|
-
width: 6px;
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
::-webkit-scrollbar-track {
|
|
1031
|
-
background: transparent;
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
::-webkit-scrollbar-thumb {
|
|
1035
|
-
background: var(--border-default);
|
|
1036
|
-
border-radius: 3px;
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
::-webkit-scrollbar-thumb:hover {
|
|
1040
|
-
background: var(--text-muted);
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
/* Responsive */
|
|
1044
|
-
@media (max-width: 900px) {
|
|
1045
|
-
.main-content {
|
|
1046
|
-
grid-template-columns: 1fr;
|
|
1047
|
-
grid-template-rows: 1fr 1fr;
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
.messages-panel {
|
|
1051
|
-
border-right: none;
|
|
1052
|
-
border-bottom: 1px solid var(--border-subtle);
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
</style>
|
|
1056
|
-
</head>
|
|
1057
|
-
<body class="light">
|
|
1058
|
-
<div class="container">
|
|
1059
|
-
<!-- Header -->
|
|
1060
|
-
<header class="header">
|
|
1061
|
-
<div class="header-left">
|
|
1062
|
-
<div class="logo">
|
|
1063
|
-
<img class="logo-icon" src="aicode.svg" alt="AICode">
|
|
1064
|
-
<span class="logo-text">AICode Agent</span>
|
|
1065
|
-
</div>
|
|
1066
|
-
<div class="status-badge">
|
|
1067
|
-
<div class="status-dot" id="statusDot"></div>
|
|
1068
|
-
<span id="statusText">Connecting...</span>
|
|
1069
|
-
<span id="clientCount" style="margin-left: 8px; color: var(--text-muted); font-size: 0.75rem;"></span>
|
|
1070
|
-
</div>
|
|
1071
|
-
</div>
|
|
1072
|
-
<div class="header-right">
|
|
1073
|
-
<button class="action-btn" id="wideBtn" onclick="toggleWide()" title="Toggle Width">
|
|
1074
|
-
<span id="wideIcon">⤢</span>
|
|
1075
|
-
</button>
|
|
1076
|
-
<button class="action-btn" onclick="openConfigModal()" title="Settings">
|
|
1077
|
-
<span>⚙</span>
|
|
1078
|
-
</button>
|
|
1079
|
-
<button class="action-btn" onclick="toggleTheme()" title="Toggle Theme">
|
|
1080
|
-
<span id="themeIcon">☀</span>
|
|
1081
|
-
</button>
|
|
1082
|
-
</div>
|
|
1083
|
-
</header>
|
|
1084
|
-
|
|
1085
|
-
<!-- 配置弹窗 -->
|
|
1086
|
-
<div class="modal-overlay" id="configModal">
|
|
1087
|
-
<div class="modal">
|
|
1088
|
-
<h2>Agent Configuration</h2>
|
|
1089
|
-
<div class="form-group">
|
|
1090
|
-
<label>aicode Command Path</label>
|
|
1091
|
-
<input type="text" id="configCommand" placeholder="aicode">
|
|
1092
|
-
</div>
|
|
1093
|
-
<div class="form-group">
|
|
1094
|
-
<label>Provider</label>
|
|
1095
|
-
<select id="configProvider" onchange="onProviderChange()">
|
|
1096
|
-
<option value="">Select Provider</option>
|
|
1097
|
-
</select>
|
|
1098
|
-
</div>
|
|
1099
|
-
<div class="form-group">
|
|
1100
|
-
<label>Model</label>
|
|
1101
|
-
<select id="configModel">
|
|
1102
|
-
<option value="">Select Model</option>
|
|
1103
|
-
</select>
|
|
1104
|
-
</div>
|
|
1105
|
-
<div class="form-group">
|
|
1106
|
-
<label>
|
|
1107
|
-
<input type="checkbox" id="configNoSession" checked>
|
|
1108
|
-
No Session Mode
|
|
1109
|
-
</label>
|
|
1110
|
-
</div>
|
|
1111
|
-
<div class="form-group">
|
|
1112
|
-
<label>Session Directory</label>
|
|
1113
|
-
<input type="text" id="configSessionDir" placeholder="~/.aicode-cli/agent/sessions">
|
|
1114
|
-
</div>
|
|
1115
|
-
<div class="form-group">
|
|
1116
|
-
<label>Working Directory</label>
|
|
1117
|
-
<input type="text" id="configWorkingDir" placeholder="~/.aicode-cli">
|
|
1118
|
-
</div>
|
|
1119
|
-
<div class="error-message" id="configError"></div>
|
|
1120
|
-
<div class="loading" id="configLoading" style="display: none;">Initializing agent...</div>
|
|
1121
|
-
<div class="btn-group">
|
|
1122
|
-
<button class="btn-primary" onclick="saveConfig()">Initialize</button>
|
|
1123
|
-
<button class="btn-secondary" onclick="closeConfigModal()">Cancel</button>
|
|
1124
|
-
</div>
|
|
1125
|
-
<div class="config-tips">
|
|
1126
|
-
<p>💡 提示:</p>
|
|
1127
|
-
<ul>
|
|
1128
|
-
<li>Provider/Model 参考 <code>~/.aicode-cli/agent/models.json</code></li>
|
|
1129
|
-
<li>Command 路径可用 <code>whereis aicode</code> 获取</li>
|
|
1130
|
-
</ul>
|
|
1131
|
-
</div>
|
|
1132
|
-
</div>
|
|
1133
|
-
</div>
|
|
1134
|
-
|
|
1135
|
-
<!-- Main Content -->
|
|
1136
|
-
<main class="main-content">
|
|
1137
|
-
<!-- Left Panel: Messages -->
|
|
1138
|
-
<section class="messages-panel">
|
|
1139
|
-
<div class="panel-tabs">
|
|
1140
|
-
<button class="panel-tab active" onclick="switchMsgTab('response')">Response</button>
|
|
1141
|
-
<button class="panel-tab" onclick="switchMsgTab('events')">Events</button>
|
|
1142
|
-
</div>
|
|
1143
|
-
|
|
1144
|
-
<!-- Response Tab -->
|
|
1145
|
-
<div id="responseTab" class="panel-content">
|
|
1146
|
-
<div class="empty-state" id="emptyState">
|
|
1147
|
-
<img class="empty-logo" src="aicode.svg" alt="AICode">
|
|
1148
|
-
<div class="empty-title">你好,我是 AICode Agent</div>
|
|
1149
|
-
<div class="empty-desc">我可以帮助你完成以下任务:</div>
|
|
1150
|
-
<div class="empty-features">
|
|
1151
|
-
<div class="feature-item"><span class="feature-icon">📝</span> 代码编写与调试</div>
|
|
1152
|
-
<div class="feature-item"><span class="feature-icon">🔍</span> 代码审查与优化</div>
|
|
1153
|
-
<div class="feature-item"><span class="feature-icon">💡</span> 技术问题解答</div>
|
|
1154
|
-
<div class="feature-item"><span class="feature-icon">🔄</span> Git 操作辅助</div>
|
|
1155
|
-
</div>
|
|
1156
|
-
<div class="empty-hint">在右侧输入框中输入你的问题或任务</div>
|
|
1157
|
-
</div>
|
|
1158
|
-
</div>
|
|
1159
|
-
|
|
1160
|
-
<!-- Events Tab -->
|
|
1161
|
-
<div id="eventsTab" class="panel-content hidden">
|
|
1162
|
-
<div class="event-log" id="eventLog">
|
|
1163
|
-
<div class="empty-state">
|
|
1164
|
-
<div class="empty-text">No events yet</div>
|
|
1165
|
-
</div>
|
|
1166
|
-
</div>
|
|
1167
|
-
</div>
|
|
1168
|
-
</section>
|
|
1169
|
-
|
|
1170
|
-
<!-- Right Panel: Input -->
|
|
1171
|
-
<aside class="input-panel">
|
|
1172
|
-
<!-- Quick Questions Card -->
|
|
1173
|
-
<div class="card">
|
|
1174
|
-
<div class="card-title">Quick Commands</div>
|
|
1175
|
-
<div class="quick-questions">
|
|
1176
|
-
<span class="quick-question" onclick="setPrompt('你好,请介绍一下你自己')">👋 自我介绍</span>
|
|
1177
|
-
<span class="quick-question" onclick="setPrompt('帮我写一个 Hello World')">Hello World</span>
|
|
1178
|
-
<span class="quick-question" onclick="setPrompt('解释什么是 RPC 协议')">RPC 协议</span>
|
|
1179
|
-
<span class="quick-question" onclick="setPrompt('介绍下你的技能')">你有哪些技能</span>
|
|
1180
|
-
</div>
|
|
1181
|
-
</div>
|
|
1182
|
-
|
|
1183
|
-
<!-- Message Type Card -->
|
|
1184
|
-
<div class="card">
|
|
1185
|
-
<div class="card-title">Message Type</div>
|
|
1186
|
-
<div class="msg-type-group">
|
|
1187
|
-
<input type="radio" id="type-prompt" name="msgType" value="prompt" checked onchange="onMsgTypeChange()">
|
|
1188
|
-
<label for="type-prompt">Prompt</label>
|
|
1189
|
-
|
|
1190
|
-
<input type="radio" id="type-steer" name="msgType" value="steer" onchange="onMsgTypeChange()">
|
|
1191
|
-
<label for="type-steer">Steer</label>
|
|
1192
|
-
|
|
1193
|
-
<input type="radio" id="type-follow" name="msgType" value="follow_up" onchange="onMsgTypeChange()">
|
|
1194
|
-
<label for="type-follow">Follow</label>
|
|
1195
|
-
|
|
1196
|
-
<input type="radio" id="type-abort" name="msgType" value="abort" onchange="onMsgTypeChange()">
|
|
1197
|
-
<label for="type-abort">Abort</label>
|
|
1198
|
-
</div>
|
|
1199
|
-
</div>
|
|
1200
|
-
|
|
1201
|
-
<!-- Input Card -->
|
|
1202
|
-
<div class="card input-card">
|
|
1203
|
-
<div class="card-title">Input</div>
|
|
1204
|
-
<div class="input-wrapper">
|
|
1205
|
-
<textarea id="promptInput" placeholder="Enter your message..." onkeydown="handleKeyDown(event)"></textarea>
|
|
1206
|
-
</div>
|
|
1207
|
-
<div class="input-actions">
|
|
1208
|
-
<button class="btn-send" onclick="sendPrompt()" id="sendBtn">
|
|
1209
|
-
<span>Send</span>
|
|
1210
|
-
<span>↵</span>
|
|
1211
|
-
</button>
|
|
1212
|
-
</div>
|
|
1213
|
-
</div>
|
|
1214
|
-
</aside>
|
|
1215
|
-
</main>
|
|
1216
|
-
</div>
|
|
1217
|
-
|
|
1218
|
-
<script>
|
|
1219
|
-
const API_BASE = '';
|
|
1220
|
-
let messages = [];
|
|
1221
|
-
|
|
1222
|
-
// Configure marked
|
|
1223
|
-
marked.setOptions({
|
|
1224
|
-
breaks: true,
|
|
1225
|
-
gfm: true
|
|
1226
|
-
});
|
|
1227
|
-
let isStreaming = false;
|
|
1228
|
-
let lastRenderTime = 0;
|
|
1229
|
-
const RENDER_THROTTLE = 100;
|
|
1230
|
-
let events = [];
|
|
1231
|
-
|
|
1232
|
-
// Initialize
|
|
1233
|
-
window.onload = function() {
|
|
1234
|
-
checkStatus();
|
|
1235
|
-
connectWebSocket();
|
|
1236
|
-
setInterval(checkStatus, 30000);
|
|
1237
|
-
document.getElementById('promptInput').focus();
|
|
1238
|
-
};
|
|
1239
|
-
|
|
1240
|
-
// Toggle theme
|
|
1241
|
-
function toggleTheme() {
|
|
1242
|
-
document.body.classList.toggle('light');
|
|
1243
|
-
const icon = document.getElementById('themeIcon');
|
|
1244
|
-
icon.textContent = document.body.classList.contains('light') ? '☾' : '☀';
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
// Toggle width
|
|
1248
|
-
function toggleWide() {
|
|
1249
|
-
document.body.classList.toggle('wide');
|
|
1250
|
-
const container = document.querySelector('.container');
|
|
1251
|
-
if (document.body.classList.contains('wide')) {
|
|
1252
|
-
container.style.maxWidth = '100%';
|
|
1253
|
-
} else {
|
|
1254
|
-
container.style.maxWidth = '1400px';
|
|
1255
|
-
}
|
|
1256
|
-
document.getElementById('wideBtn').classList.toggle('active');
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
|
-
// 全局变量存储 providers 数据
|
|
1260
|
-
let availableProviders = [];
|
|
1261
|
-
|
|
1262
|
-
// 打开配置弹窗
|
|
1263
|
-
async function openConfigModal() {
|
|
1264
|
-
const modal = document.getElementById('configModal');
|
|
1265
|
-
const errorEl = document.getElementById('configError');
|
|
1266
|
-
errorEl.classList.remove('show');
|
|
1267
|
-
|
|
1268
|
-
try {
|
|
1269
|
-
const res = await fetch('/api/aicode/config');
|
|
1270
|
-
const data = await res.json();
|
|
1271
|
-
|
|
1272
|
-
if (data.success && data.data) {
|
|
1273
|
-
const config = data.data.config;
|
|
1274
|
-
const models = data.data.models;
|
|
1275
|
-
|
|
1276
|
-
// 填充 providers 下拉框
|
|
1277
|
-
const providerSelect = document.getElementById('configProvider');
|
|
1278
|
-
providerSelect.innerHTML = '<option value="">Select Provider</option>';
|
|
1279
|
-
if (models && models.providers) {
|
|
1280
|
-
availableProviders = models.providers;
|
|
1281
|
-
models.providers.forEach(p => {
|
|
1282
|
-
const option = document.createElement('option');
|
|
1283
|
-
option.value = p.id;
|
|
1284
|
-
option.textContent = p.name || p.id;
|
|
1285
|
-
providerSelect.appendChild(option);
|
|
1286
|
-
});
|
|
1287
|
-
}
|
|
1288
|
-
|
|
1289
|
-
// 设置当前值
|
|
1290
|
-
document.getElementById('configCommand').value = config.command || '';
|
|
1291
|
-
document.getElementById('configNoSession').checked = config.noSession !== false;
|
|
1292
|
-
document.getElementById('configSessionDir').value = config.sessionDir || '~/.aicode-cli/agent/sessions';
|
|
1293
|
-
document.getElementById('configWorkingDir').value = config.workingDir || '~/.aicode-cli';
|
|
1294
|
-
|
|
1295
|
-
// 选择 provider 后填充 models
|
|
1296
|
-
if (config.provider) {
|
|
1297
|
-
providerSelect.value = config.provider;
|
|
1298
|
-
onProviderChange();
|
|
1299
|
-
// 选择 model
|
|
1300
|
-
if (config.model) {
|
|
1301
|
-
document.getElementById('configModel').value = config.model;
|
|
1302
|
-
}
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
} catch (e) {
|
|
1306
|
-
document.getElementById('configCommand').value = '';
|
|
1307
|
-
document.getElementById('configProvider').innerHTML = '<option value="minimax-custom">minimax-custom</option>';
|
|
1308
|
-
document.getElementById('configModel').innerHTML = '<option value="MiniMax-M2.5">MiniMax-M2.5</option>';
|
|
1309
|
-
document.getElementById('configNoSession').checked = true;
|
|
1310
|
-
document.getElementById('configSessionDir').value = '~/.aicode-cli/agent/sessions';
|
|
1311
|
-
document.getElementById('configWorkingDir').value = '~/.aicode-cli';
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
modal.classList.add('show');
|
|
1315
|
-
}
|
|
1316
|
-
|
|
1317
|
-
// Provider 变化时更新 Model 下拉框
|
|
1318
|
-
function onProviderChange() {
|
|
1319
|
-
const providerId = document.getElementById('configProvider').value;
|
|
1320
|
-
const modelSelect = document.getElementById('configModel');
|
|
1321
|
-
modelSelect.innerHTML = '<option value="">Select Model</option>';
|
|
1322
|
-
|
|
1323
|
-
if (providerId && availableProviders) {
|
|
1324
|
-
const provider = availableProviders.find(p => p.id === providerId);
|
|
1325
|
-
if (provider && provider.models) {
|
|
1326
|
-
provider.models.forEach(m => {
|
|
1327
|
-
const option = document.createElement('option');
|
|
1328
|
-
option.value = m.id;
|
|
1329
|
-
option.textContent = m.name || m.id;
|
|
1330
|
-
modelSelect.appendChild(option);
|
|
1331
|
-
});
|
|
1332
|
-
}
|
|
1333
|
-
}
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
|
-
// 关闭配置弹窗
|
|
1337
|
-
function closeConfigModal() {
|
|
1338
|
-
document.getElementById('configModal').classList.remove('show');
|
|
1339
|
-
}
|
|
1340
|
-
|
|
1341
|
-
// 保存配置并初始化
|
|
1342
|
-
async function saveConfig() {
|
|
1343
|
-
const errorEl = document.getElementById('configError');
|
|
1344
|
-
const loadingEl = document.getElementById('configLoading');
|
|
1345
|
-
const config = {
|
|
1346
|
-
command: document.getElementById('configCommand').value || 'aicode',
|
|
1347
|
-
provider: document.getElementById('configProvider').value || 'minimax-custom',
|
|
1348
|
-
model: document.getElementById('configModel').value || 'MiniMax-M2.5',
|
|
1349
|
-
noSession: document.getElementById('configNoSession').checked,
|
|
1350
|
-
sessionDir: document.getElementById('configSessionDir').value || '~/.aicode-cli/agent/sessions',
|
|
1351
|
-
workingDir: document.getElementById('configWorkingDir').value || '~/.aicode-cli'
|
|
1352
|
-
};
|
|
1353
|
-
|
|
1354
|
-
errorEl.classList.remove('show');
|
|
1355
|
-
loadingEl.style.display = 'block';
|
|
1356
|
-
|
|
1357
|
-
try {
|
|
1358
|
-
const res = await fetch('/api/aicode/config', {
|
|
1359
|
-
method: 'POST',
|
|
1360
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1361
|
-
body: JSON.stringify(config)
|
|
1362
|
-
});
|
|
1363
|
-
|
|
1364
|
-
const data = await res.json();
|
|
1365
|
-
|
|
1366
|
-
if (data.success) {
|
|
1367
|
-
closeConfigModal();
|
|
1368
|
-
checkStatus();
|
|
1369
|
-
connectWebSocket();
|
|
1370
|
-
} else {
|
|
1371
|
-
errorEl.textContent = data.message || 'Failed to initialize agent';
|
|
1372
|
-
errorEl.classList.add('show');
|
|
1373
|
-
}
|
|
1374
|
-
} catch (e) {
|
|
1375
|
-
errorEl.textContent = 'Request failed: ' + e.message;
|
|
1376
|
-
errorEl.classList.add('show');
|
|
1377
|
-
} finally {
|
|
1378
|
-
loadingEl.style.display = 'none';
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
|
-
// 检查状态
|
|
1383
|
-
async function checkStatus() {
|
|
1384
|
-
try {
|
|
1385
|
-
const res = await fetch('/api/aicode/status');
|
|
1386
|
-
const data = await res.json();
|
|
1387
|
-
const statusDot = document.getElementById('statusDot');
|
|
1388
|
-
const statusText = document.getElementById('statusText');
|
|
1389
|
-
|
|
1390
|
-
if (data.success && data.data && data.data.running) {
|
|
1391
|
-
statusDot.classList.add('active');
|
|
1392
|
-
statusText.textContent = 'Connected';
|
|
1393
|
-
} else {
|
|
1394
|
-
statusDot.classList.remove('active');
|
|
1395
|
-
statusText.textContent = 'Not Connected';
|
|
1396
|
-
openConfigModal();
|
|
1397
|
-
}
|
|
1398
|
-
} catch (e) {
|
|
1399
|
-
console.error('Check status error:', e);
|
|
1400
|
-
}
|
|
1401
|
-
}
|
|
1402
|
-
|
|
1403
|
-
// 初始化时检查状态
|
|
1404
|
-
checkStatus();
|
|
1405
|
-
|
|
1406
|
-
// Switch message tab
|
|
1407
|
-
function switchMsgTab(tab) {
|
|
1408
|
-
document.querySelectorAll('.panel-tab').forEach(t => t.classList.remove('active'));
|
|
1409
|
-
document.querySelector(`.panel-tab[onclick="switchMsgTab('${tab}')"]`).classList.add('active');
|
|
1410
|
-
document.getElementById('responseTab').classList.toggle('hidden', tab !== 'response');
|
|
1411
|
-
document.getElementById('eventsTab').classList.toggle('hidden', tab !== 'events');
|
|
1412
|
-
}
|
|
1413
|
-
|
|
1414
|
-
// Check health
|
|
1415
|
-
async function checkHealth() {
|
|
1416
|
-
try {
|
|
1417
|
-
const res = await fetch(`${API_BASE}/api/aicode/health`);
|
|
1418
|
-
const data = await res.json();
|
|
1419
|
-
const statusDot = document.getElementById('statusDot');
|
|
1420
|
-
const statusText = document.getElementById('statusText');
|
|
1421
|
-
|
|
1422
|
-
if (data.running) {
|
|
1423
|
-
statusDot.classList.add('active');
|
|
1424
|
-
statusText.textContent = 'Connected';
|
|
1425
|
-
} else {
|
|
1426
|
-
statusDot.classList.remove('active');
|
|
1427
|
-
statusText.textContent = 'Disconnected';
|
|
1428
|
-
}
|
|
1429
|
-
} catch (e) {
|
|
1430
|
-
document.getElementById('statusText').textContent = 'Error';
|
|
1431
|
-
}
|
|
1432
|
-
}
|
|
1433
|
-
|
|
1434
|
-
// Get message type
|
|
1435
|
-
function getMsgType() {
|
|
1436
|
-
return document.querySelector('input[name="msgType"]:checked').value;
|
|
1437
|
-
}
|
|
1438
|
-
|
|
1439
|
-
// On message type change
|
|
1440
|
-
function onMsgTypeChange() {
|
|
1441
|
-
const msgType = getMsgType();
|
|
1442
|
-
const input = document.getElementById('promptInput');
|
|
1443
|
-
const placeholder = {
|
|
1444
|
-
'prompt': 'Enter your message...',
|
|
1445
|
-
'steer': 'Enter steer message (interrupts current operation)...',
|
|
1446
|
-
'follow_up': 'Enter follow-up message...',
|
|
1447
|
-
'abort': 'Confirm abort?'
|
|
1448
|
-
};
|
|
1449
|
-
input.placeholder = placeholder[msgType] || 'Enter your message...';
|
|
1450
|
-
|
|
1451
|
-
if (msgType === 'abort') {
|
|
1452
|
-
input.value = '';
|
|
1453
|
-
input.disabled = true;
|
|
1454
|
-
} else {
|
|
1455
|
-
input.disabled = false;
|
|
1456
|
-
}
|
|
1457
|
-
}
|
|
1458
|
-
|
|
1459
|
-
// Set prompt
|
|
1460
|
-
function setPrompt(text) {
|
|
1461
|
-
document.getElementById('promptInput').value = text;
|
|
1462
|
-
document.getElementById('promptInput').focus();
|
|
1463
|
-
}
|
|
1464
|
-
|
|
1465
|
-
// Handle key down
|
|
1466
|
-
function handleKeyDown(event) {
|
|
1467
|
-
if (event.key === 'Enter' && !event.shiftKey) {
|
|
1468
|
-
event.preventDefault();
|
|
1469
|
-
sendPrompt();
|
|
1470
|
-
}
|
|
1471
|
-
}
|
|
1472
|
-
|
|
1473
|
-
// Send prompt
|
|
1474
|
-
async function sendPrompt() {
|
|
1475
|
-
const msgType = getMsgType();
|
|
1476
|
-
const input = document.getElementById('promptInput');
|
|
1477
|
-
const message = input.value.trim();
|
|
1478
|
-
|
|
1479
|
-
if (msgType !== 'abort' && !message) return;
|
|
1480
|
-
if (isStreaming) return;
|
|
1481
|
-
|
|
1482
|
-
// Add user message
|
|
1483
|
-
const displayMsg = msgType === 'abort' ? '[Abort]' : message;
|
|
1484
|
-
addMessage('user', `[${msgType}] ${displayMsg}`);
|
|
1485
|
-
|
|
1486
|
-
// Add AI placeholder
|
|
1487
|
-
addMessage('assistant', '');
|
|
1488
|
-
|
|
1489
|
-
if (msgType !== 'abort') {
|
|
1490
|
-
input.value = '';
|
|
1491
|
-
}
|
|
1492
|
-
|
|
1493
|
-
isStreaming = true;
|
|
1494
|
-
setButtonsDisabled(true);
|
|
1495
|
-
|
|
1496
|
-
try {
|
|
1497
|
-
let endpoint = `${API_BASE}/api/aicode/${msgType}`;
|
|
1498
|
-
const res = await fetch(endpoint, {
|
|
1499
|
-
method: 'POST',
|
|
1500
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1501
|
-
body: msgType === 'abort' ? JSON.stringify({}) : JSON.stringify({ message })
|
|
1502
|
-
});
|
|
1503
|
-
|
|
1504
|
-
const data = await res.json();
|
|
1505
|
-
if (!data.success) {
|
|
1506
|
-
updateLastMessage(`Error: ${data.message}`, false);
|
|
1507
|
-
} else if (msgType === 'abort') {
|
|
1508
|
-
updateLastMessage('✓ Abort command sent', false);
|
|
1509
|
-
}
|
|
1510
|
-
} catch (e) {
|
|
1511
|
-
updateLastMessage(`Request failed: ${e.message}`, false);
|
|
1512
|
-
}
|
|
1513
|
-
|
|
1514
|
-
isStreaming = false;
|
|
1515
|
-
setButtonsDisabled(false);
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1518
|
-
// Add message
|
|
1519
|
-
function addMessage(role, content) {
|
|
1520
|
-
messages.push({
|
|
1521
|
-
role: role,
|
|
1522
|
-
content: content,
|
|
1523
|
-
time: new Date().toLocaleTimeString()
|
|
1524
|
-
});
|
|
1525
|
-
renderMessages();
|
|
1526
|
-
}
|
|
1527
|
-
|
|
1528
|
-
// Render messages
|
|
1529
|
-
function renderMessages() {
|
|
1530
|
-
const section = document.getElementById('responseTab');
|
|
1531
|
-
const empty = document.getElementById('emptyState');
|
|
1532
|
-
|
|
1533
|
-
const existingEmpty = section.querySelector('#emptyState');
|
|
1534
|
-
section.innerHTML = '';
|
|
1535
|
-
if (existingEmpty) {
|
|
1536
|
-
section.appendChild(existingEmpty);
|
|
1537
|
-
} else {
|
|
1538
|
-
section.appendChild(empty);
|
|
1539
|
-
}
|
|
1540
|
-
|
|
1541
|
-
if (messages.length === 0) {
|
|
1542
|
-
empty.style.display = 'flex';
|
|
1543
|
-
return;
|
|
1544
|
-
}
|
|
1545
|
-
|
|
1546
|
-
empty.style.display = 'none';
|
|
1547
|
-
|
|
1548
|
-
messages.forEach((msg, index) => {
|
|
1549
|
-
const div = document.createElement('div');
|
|
1550
|
-
div.className = `output-item ${msg.role}`;
|
|
1551
|
-
div.dataset.index = index;
|
|
1552
|
-
|
|
1553
|
-
const roleLabel = msg.role === 'user' ? 'You' : 'AI';
|
|
1554
|
-
div.innerHTML = `
|
|
1555
|
-
<div class="output-header">
|
|
1556
|
-
<div class="output-meta">
|
|
1557
|
-
<span class="output-role">${roleLabel}</span>
|
|
1558
|
-
<span class="output-time">${msg.time}</span>
|
|
1559
|
-
</div>
|
|
1560
|
-
<div class="output-actions">
|
|
1561
|
-
<button class="copy-btn" onclick="copyMessage(this, '${msg.role}')" title="Copy">
|
|
1562
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1563
|
-
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
|
1564
|
-
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
|
1565
|
-
</svg>
|
|
1566
|
-
</button>
|
|
1567
|
-
</div>
|
|
1568
|
-
</div>
|
|
1569
|
-
<pre class="output-content">${escapeHtml(msg.content)}</pre>
|
|
1570
|
-
`;
|
|
1571
|
-
section.appendChild(div);
|
|
1572
|
-
});
|
|
1573
|
-
|
|
1574
|
-
scrollToBottom();
|
|
1575
|
-
}
|
|
1576
|
-
|
|
1577
|
-
// Update last message
|
|
1578
|
-
function updateLastMessage(content, showCursor = false) {
|
|
1579
|
-
const section = document.getElementById('responseTab');
|
|
1580
|
-
const msgElements = section.querySelectorAll('.output-item.assistant');
|
|
1581
|
-
|
|
1582
|
-
if (msgElements.length === 0) return;
|
|
1583
|
-
|
|
1584
|
-
const lastMsg = msgElements[msgElements.length - 1];
|
|
1585
|
-
const outputContent = lastMsg.querySelector('.output-content');
|
|
1586
|
-
|
|
1587
|
-
if (outputContent) {
|
|
1588
|
-
outputContent.innerHTML = escapeHtml(content) + (showCursor ? '<span class="cursor"></span>' : '');
|
|
1589
|
-
scrollToBottom();
|
|
1590
|
-
}
|
|
1591
|
-
}
|
|
1592
|
-
|
|
1593
|
-
// Scroll to bottom
|
|
1594
|
-
function scrollToBottom() {
|
|
1595
|
-
const section = document.getElementById('responseTab');
|
|
1596
|
-
section.scrollTop = section.scrollHeight;
|
|
1597
|
-
}
|
|
1598
|
-
|
|
1599
|
-
// Escape HTML
|
|
1600
|
-
function escapeHtml(text) {
|
|
1601
|
-
const div = document.createElement('div');
|
|
1602
|
-
div.textContent = text;
|
|
1603
|
-
return div.innerHTML;
|
|
1604
|
-
}
|
|
1605
|
-
|
|
1606
|
-
// Copy message to clipboard
|
|
1607
|
-
function copyMessage(btn, role) {
|
|
1608
|
-
const outputItem = btn.closest('.output-item');
|
|
1609
|
-
const content = outputItem.querySelector('.output-content').textContent;
|
|
1610
|
-
|
|
1611
|
-
navigator.clipboard.writeText(content).then(() => {
|
|
1612
|
-
btn.classList.add('copied');
|
|
1613
|
-
btn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1614
|
-
<polyline points="20 6 9 17 4 12"></polyline>
|
|
1615
|
-
</svg>`;
|
|
1616
|
-
|
|
1617
|
-
setTimeout(() => {
|
|
1618
|
-
btn.classList.remove('copied');
|
|
1619
|
-
btn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1620
|
-
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
|
1621
|
-
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
|
1622
|
-
</svg>`;
|
|
1623
|
-
}, 1500);
|
|
1624
|
-
});
|
|
1625
|
-
}
|
|
1626
|
-
|
|
1627
|
-
// Set buttons disabled
|
|
1628
|
-
function setButtonsDisabled(disabled) {
|
|
1629
|
-
document.getElementById('sendBtn').disabled = disabled;
|
|
1630
|
-
}
|
|
1631
|
-
|
|
1632
|
-
// Add event to log
|
|
1633
|
-
function addEvent(type, data) {
|
|
1634
|
-
events.push({
|
|
1635
|
-
type: type,
|
|
1636
|
-
data: data,
|
|
1637
|
-
time: new Date().toLocaleTimeString()
|
|
1638
|
-
});
|
|
1639
|
-
// 只保留最后 40 个
|
|
1640
|
-
if (events.length > 40) {
|
|
1641
|
-
events = events.slice(-40);
|
|
1642
|
-
}
|
|
1643
|
-
renderEvents();
|
|
1644
|
-
}
|
|
1645
|
-
|
|
1646
|
-
// Render events (倒序显示,最新的在最上方)
|
|
1647
|
-
function renderEvents() {
|
|
1648
|
-
const log = document.getElementById('eventLog');
|
|
1649
|
-
if (events.length === 0) {
|
|
1650
|
-
log.innerHTML = '<div class="empty-state"><div class="empty-text">No events yet</div></div>';
|
|
1651
|
-
return;
|
|
1652
|
-
}
|
|
1653
|
-
|
|
1654
|
-
// 倒序显示
|
|
1655
|
-
const reversedEvents = [...events].reverse();
|
|
1656
|
-
log.innerHTML = reversedEvents.map((evt, index) => {
|
|
1657
|
-
const fullData = JSON.stringify(evt.data);
|
|
1658
|
-
const shortData = fullData.substring(0, 300);
|
|
1659
|
-
return `
|
|
1660
|
-
<div class="event-item">
|
|
1661
|
-
<span class="event-time">${evt.time}</span>
|
|
1662
|
-
<span class="event-type">${evt.type}</span>
|
|
1663
|
-
<span class="event-data" data-full="${escapeHtml(fullData)}">${escapeHtml(shortData)}</span>
|
|
1664
|
-
<div class="event-actions">
|
|
1665
|
-
<button class="copy-btn" onclick="copyEventData(this, ${index})" title="Copy">
|
|
1666
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1667
|
-
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
|
1668
|
-
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
|
1669
|
-
</svg>
|
|
1670
|
-
</button>
|
|
1671
|
-
</div>
|
|
1672
|
-
</div>
|
|
1673
|
-
`;
|
|
1674
|
-
}).join('');
|
|
1675
|
-
|
|
1676
|
-
scrollToBottom();
|
|
1677
|
-
}
|
|
1678
|
-
|
|
1679
|
-
// Copy event data (倒序后需要反向计算索引)
|
|
1680
|
-
function copyEventData(btn, index) {
|
|
1681
|
-
const actualIndex = events.length - 1 - index;
|
|
1682
|
-
const evt = events[actualIndex];
|
|
1683
|
-
const fullData = JSON.stringify(evt.data, null, 2);
|
|
1684
|
-
|
|
1685
|
-
navigator.clipboard.writeText(fullData).then(() => {
|
|
1686
|
-
btn.classList.add('copied');
|
|
1687
|
-
btn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1688
|
-
<polyline points="20 6 9 17 4 12"></polyline>
|
|
1689
|
-
</svg>`;
|
|
1690
|
-
|
|
1691
|
-
setTimeout(() => {
|
|
1692
|
-
btn.classList.remove('copied');
|
|
1693
|
-
btn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1694
|
-
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
|
1695
|
-
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
|
1696
|
-
</svg>`;
|
|
1697
|
-
}, 1500);
|
|
1698
|
-
});
|
|
1699
|
-
}
|
|
1700
|
-
|
|
1701
|
-
// WebSocket
|
|
1702
|
-
let ws;
|
|
1703
|
-
function connectWebSocket() {
|
|
1704
|
-
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
1705
|
-
const wsUrl = `${protocol}//${window.location.host}/ws/agent`;
|
|
1706
|
-
|
|
1707
|
-
ws = new WebSocket(wsUrl);
|
|
1708
|
-
|
|
1709
|
-
ws.onmessage = (event) => {
|
|
1710
|
-
try {
|
|
1711
|
-
const data = JSON.parse(event.data);
|
|
1712
|
-
// 处理客户端连接数消息
|
|
1713
|
-
if (data.type === 'client_count') {
|
|
1714
|
-
document.getElementById('clientCount').textContent = `Clients: ${data.count}`;
|
|
1715
|
-
return;
|
|
1716
|
-
}
|
|
1717
|
-
handleEvent(data);
|
|
1718
|
-
} catch (e) {
|
|
1719
|
-
console.error('Parse error:', e);
|
|
1720
|
-
}
|
|
1721
|
-
};
|
|
1722
|
-
|
|
1723
|
-
ws.onclose = () => {
|
|
1724
|
-
setTimeout(connectWebSocket, 3000);
|
|
1725
|
-
};
|
|
1726
|
-
}
|
|
1727
|
-
|
|
1728
|
-
// Handle event
|
|
1729
|
-
function handleEvent(event) {
|
|
1730
|
-
addEvent(event.type, event);
|
|
1731
|
-
|
|
1732
|
-
if (event.type === 'message_update' && event.messageType === 'text_delta') {
|
|
1733
|
-
let lastMsg = messages[messages.length - 1];
|
|
1734
|
-
if (!lastMsg || lastMsg.role !== 'assistant') {
|
|
1735
|
-
messages.push({ role: 'assistant', content: '', time: new Date().toLocaleTimeString() });
|
|
1736
|
-
lastMsg = messages[messages.length - 1];
|
|
1737
|
-
}
|
|
1738
|
-
lastMsg.content += event.delta;
|
|
1739
|
-
|
|
1740
|
-
const now = Date.now();
|
|
1741
|
-
if (now - lastRenderTime > RENDER_THROTTLE) {
|
|
1742
|
-
lastRenderTime = now;
|
|
1743
|
-
updateLastMessage(lastMsg.content, true);
|
|
1744
|
-
}
|
|
1745
|
-
} else if (event.type === 'agent_end') {
|
|
1746
|
-
const lastMsg = messages[messages.length - 1];
|
|
1747
|
-
if (lastMsg && lastMsg.role === 'assistant') {
|
|
1748
|
-
if (event.usage) {
|
|
1749
|
-
lastMsg.content += `\n\nTokens: ${event.usage.completionTokens || 0} (completion) / ${event.usage.promptTokens || 0} (prompt)`;
|
|
1750
|
-
}
|
|
1751
|
-
updateLastMessage(lastMsg.content, false);
|
|
1752
|
-
}
|
|
1753
|
-
}
|
|
1754
|
-
}
|
|
1755
|
-
</script>
|
|
1756
|
-
</body>
|
|
1757
|
-
</html>
|