@agentunion/kite 1.0.7 → 1.2.0

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.
Files changed (77) hide show
  1. package/core/event_hub/entry.py +305 -26
  2. package/core/event_hub/hub.py +8 -0
  3. package/core/event_hub/server.py +80 -17
  4. package/core/kite_log.py +241 -0
  5. package/core/launcher/entry.py +978 -284
  6. package/core/launcher/process_manager.py +456 -46
  7. package/core/registry/entry.py +272 -3
  8. package/core/registry/server.py +339 -289
  9. package/core/registry/store.py +10 -4
  10. package/extensions/agents/__init__.py +1 -0
  11. package/extensions/agents/assistant/__init__.py +1 -0
  12. package/extensions/agents/assistant/entry.py +380 -0
  13. package/extensions/agents/assistant/module.md +22 -0
  14. package/extensions/agents/assistant/server.py +236 -0
  15. package/extensions/channels/__init__.py +1 -0
  16. package/extensions/channels/acp_channel/__init__.py +1 -0
  17. package/extensions/channels/acp_channel/entry.py +380 -0
  18. package/extensions/channels/acp_channel/module.md +22 -0
  19. package/extensions/channels/acp_channel/server.py +236 -0
  20. package/extensions/event_hub_bench/entry.py +664 -379
  21. package/extensions/event_hub_bench/module.md +2 -1
  22. package/extensions/services/backup/__init__.py +1 -0
  23. package/extensions/services/backup/entry.py +380 -0
  24. package/extensions/services/backup/module.md +22 -0
  25. package/extensions/services/backup/server.py +244 -0
  26. package/extensions/services/model_service/__init__.py +1 -0
  27. package/extensions/services/model_service/entry.py +380 -0
  28. package/extensions/services/model_service/module.md +22 -0
  29. package/extensions/services/model_service/server.py +236 -0
  30. package/extensions/services/watchdog/entry.py +460 -147
  31. package/extensions/services/watchdog/module.md +3 -0
  32. package/extensions/services/watchdog/monitor.py +128 -13
  33. package/extensions/services/watchdog/server.py +75 -13
  34. package/extensions/services/web/__init__.py +1 -0
  35. package/extensions/services/web/config.yaml +149 -0
  36. package/extensions/services/web/entry.py +487 -0
  37. package/extensions/services/web/module.md +24 -0
  38. package/extensions/services/web/routes/__init__.py +1 -0
  39. package/extensions/services/web/routes/routes_call.py +189 -0
  40. package/extensions/services/web/routes/routes_config.py +512 -0
  41. package/extensions/services/web/routes/routes_contacts.py +98 -0
  42. package/extensions/services/web/routes/routes_devlog.py +99 -0
  43. package/extensions/services/web/routes/routes_phone.py +81 -0
  44. package/extensions/services/web/routes/routes_sms.py +48 -0
  45. package/extensions/services/web/routes/routes_stats.py +17 -0
  46. package/extensions/services/web/routes/routes_voicechat.py +554 -0
  47. package/extensions/services/web/routes/schemas.py +216 -0
  48. package/extensions/services/web/server.py +332 -0
  49. package/extensions/services/web/static/css/style.css +1064 -0
  50. package/extensions/services/web/static/index.html +1445 -0
  51. package/extensions/services/web/static/js/app.js +4671 -0
  52. package/extensions/services/web/vendor/__init__.py +1 -0
  53. package/extensions/services/web/vendor/bluetooth/__init__.py +0 -0
  54. package/extensions/services/web/vendor/bluetooth/audio.py +348 -0
  55. package/extensions/services/web/vendor/bluetooth/contacts.py +251 -0
  56. package/extensions/services/web/vendor/bluetooth/manager.py +395 -0
  57. package/extensions/services/web/vendor/bluetooth/sms.py +290 -0
  58. package/extensions/services/web/vendor/bluetooth/telephony.py +274 -0
  59. package/extensions/services/web/vendor/config.py +139 -0
  60. package/extensions/services/web/vendor/conversation/__init__.py +0 -0
  61. package/extensions/services/web/vendor/conversation/asr.py +936 -0
  62. package/extensions/services/web/vendor/conversation/engine.py +548 -0
  63. package/extensions/services/web/vendor/conversation/llm.py +534 -0
  64. package/extensions/services/web/vendor/conversation/mcp_tools.py +190 -0
  65. package/extensions/services/web/vendor/conversation/tts.py +322 -0
  66. package/extensions/services/web/vendor/conversation/vad.py +138 -0
  67. package/extensions/services/web/vendor/storage/__init__.py +1 -0
  68. package/extensions/services/web/vendor/storage/identity.py +312 -0
  69. package/extensions/services/web/vendor/storage/store.py +507 -0
  70. package/extensions/services/web/vendor/task/__init__.py +0 -0
  71. package/extensions/services/web/vendor/task/manager.py +864 -0
  72. package/extensions/services/web/vendor/task/models.py +45 -0
  73. package/extensions/services/web/vendor/task/webhook.py +263 -0
  74. package/extensions/services/web/vendor/tools/__init__.py +0 -0
  75. package/extensions/services/web/vendor/tools/registry.py +321 -0
  76. package/main.py +230 -90
  77. package/package.json +1 -1
@@ -0,0 +1,1064 @@
1
+ /* AI Phone Agent — Dashboard Stylesheet */
2
+
3
+ :root {
4
+ --sidebar-bg: #1e293b;
5
+ --sidebar-hover: #334155;
6
+ --sidebar-active: #0f172a;
7
+ --sidebar-text: #94a3b8;
8
+ --sidebar-text-active: #f1f5f9;
9
+ --primary: #3b82f6;
10
+ --primary-hover: #2563eb;
11
+ --primary-light: #dbeafe;
12
+ --success: #22c55e;
13
+ --success-light: #dcfce7;
14
+ --error: #ef4444;
15
+ --error-light: #fee2e2;
16
+ --warning: #eab308;
17
+ --warning-light: #fef9c3;
18
+ --white: #ffffff;
19
+ --gray-50: #f8fafc;
20
+ --gray-100: #f1f5f9;
21
+ --gray-200: #e2e8f0;
22
+ --gray-300: #cbd5e1;
23
+ --gray-400: #94a3b8;
24
+ --gray-500: #64748b;
25
+ --gray-600: #475569;
26
+ --gray-700: #334155;
27
+ --gray-800: #1e293b;
28
+ --gray-900: #0f172a;
29
+ --sidebar-width: 240px;
30
+ --header-height: 56px;
31
+ --statusbar-height: 32px;
32
+ --radius: 8px;
33
+ --shadow: 0 1px 3px rgba(0,0,0,.1), 0 1px 2px rgba(0,0,0,.06);
34
+ --shadow-md: 0 4px 6px rgba(0,0,0,.1);
35
+ }
36
+
37
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
38
+
39
+ html, body {
40
+ height: 100%;
41
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
42
+ font-size: 14px;
43
+ color: var(--gray-800);
44
+ background: var(--gray-50);
45
+ }
46
+
47
+ /* ===== HEADER ===== */
48
+ #app-header {
49
+ position: fixed; top: 0; left: 0; right: 0;
50
+ height: var(--header-height);
51
+ background: var(--white);
52
+ border-bottom: 1px solid var(--gray-200);
53
+ display: flex; align-items: center; justify-content: space-between;
54
+ padding: 0 20px 0 0;
55
+ z-index: 100;
56
+ box-shadow: var(--shadow);
57
+ }
58
+ .header-left {
59
+ display: flex; align-items: center; gap: 12px;
60
+ padding-left: 16px;
61
+ }
62
+ .header-title {
63
+ font-size: 18px; font-weight: 600; color: var(--gray-900);
64
+ }
65
+ .header-right { display: flex; align-items: center; gap: 12px; }
66
+ .icon-btn {
67
+ background: none; border: none; font-size: 20px; cursor: pointer;
68
+ color: var(--gray-500); padding: 4px 8px; border-radius: 4px;
69
+ }
70
+ .icon-btn:hover { background: var(--gray-100); }
71
+ .bt-indicator {
72
+ display: flex; align-items: center; gap: 6px;
73
+ padding: 4px 12px; border-radius: 20px; font-size: 12px;
74
+ background: var(--error-light); color: var(--error);
75
+ }
76
+ .bt-indicator.connected { background: var(--success-light); color: var(--success); }
77
+ .bt-icon { font-weight: 700; }
78
+
79
+ /* ===== LAYOUT ===== */
80
+ #app-layout {
81
+ display: flex;
82
+ padding-top: var(--header-height);
83
+ min-height: calc(100vh - var(--statusbar-height));
84
+ }
85
+
86
+ /* ===== SIDEBAR ===== */
87
+ #sidebar {
88
+ position: fixed; top: var(--header-height); bottom: var(--statusbar-height);
89
+ left: 0; width: var(--sidebar-width);
90
+ background: var(--sidebar-bg);
91
+ overflow-y: auto; z-index: 90;
92
+ }
93
+ .nav-list { list-style: none; padding: 12px 0; }
94
+ .nav-item {
95
+ display: flex; flex-direction: column;
96
+ padding: 12px 20px;
97
+ cursor: pointer;
98
+ border-left: 3px solid transparent;
99
+ color: var(--sidebar-text);
100
+ transition: all .15s;
101
+ }
102
+ .nav-item:hover {
103
+ background: var(--sidebar-hover);
104
+ color: var(--sidebar-text-active);
105
+ }
106
+ .nav-item.active {
107
+ background: var(--sidebar-active);
108
+ border-left-color: var(--primary);
109
+ color: var(--sidebar-text-active);
110
+ }
111
+ .nav-label { font-size: 14px; font-weight: 500; }
112
+ .nav-sublabel { font-size: 11px; opacity: .6; margin-top: 2px; }
113
+
114
+ /* ===== MAIN CONTENT ===== */
115
+ #main-content {
116
+ margin-left: var(--sidebar-width);
117
+ flex: 1;
118
+ padding: 24px;
119
+ padding-bottom: calc(var(--statusbar-height) + 24px);
120
+ min-height: calc(100vh - var(--header-height) - var(--statusbar-height));
121
+ }
122
+
123
+ /* ===== PAGES ===== */
124
+ .page { display: none; }
125
+ .page.active { display: block; }
126
+
127
+ .page-title {
128
+ font-size: 22px; font-weight: 600; color: var(--gray-900);
129
+ margin-bottom: 20px;
130
+ }
131
+
132
+ .page-actions {
133
+ display: flex; align-items: center; gap: 10px;
134
+ margin-bottom: 16px; flex-wrap: wrap;
135
+ }
136
+
137
+ /* ===== STATUS BAR ===== */
138
+ #status-bar {
139
+ position: fixed; bottom: 0; left: 0; right: 0;
140
+ height: var(--statusbar-height);
141
+ background: var(--gray-100);
142
+ border-top: 1px solid var(--gray-200);
143
+ display: flex; align-items: center;
144
+ padding: 0 20px 0 calc(var(--sidebar-width) + 20px);
145
+ gap: 24px; font-size: 12px; color: var(--gray-500);
146
+ z-index: 90;
147
+ }
148
+ .status-item { display: flex; gap: 4px; }
149
+ .status-label { font-weight: 500; }
150
+
151
+ /* ===== STAT CARDS ===== */
152
+ .stats-row {
153
+ display: grid; grid-template-columns: repeat(4, 1fr);
154
+ gap: 16px; margin-bottom: 20px;
155
+ }
156
+ .stat-card {
157
+ background: var(--white); border-radius: var(--radius);
158
+ box-shadow: var(--shadow); padding: 20px; text-align: center;
159
+ }
160
+ .stat-value {
161
+ font-size: 28px; font-weight: 700; color: var(--gray-900);
162
+ margin-bottom: 4px;
163
+ }
164
+ .stat-label { font-size: 13px; color: var(--gray-500); }
165
+
166
+ /* ===== PANELS ===== */
167
+ .panel {
168
+ background: var(--white); border-radius: var(--radius);
169
+ box-shadow: var(--shadow); padding: 20px;
170
+ margin-bottom: 16px;
171
+ }
172
+ .panel-title {
173
+ font-size: 15px; font-weight: 600; color: var(--gray-700);
174
+ margin-bottom: 14px; padding-bottom: 10px;
175
+ border-bottom: 1px solid var(--gray-100);
176
+ }
177
+
178
+ /* ===== BREAKDOWN LIST ===== */
179
+ .breakdown-list { list-style: none; }
180
+ .breakdown-list li {
181
+ display: flex; justify-content: space-between; align-items: center;
182
+ padding: 8px 0; border-bottom: 1px solid var(--gray-100);
183
+ }
184
+ .breakdown-list li:last-child { border-bottom: none; }
185
+ .breakdown-label { color: var(--gray-600); }
186
+ .breakdown-value { font-weight: 600; color: var(--gray-800); }
187
+
188
+ /* ===== TABLES ===== */
189
+ .table-wrapper { overflow-x: auto; }
190
+ .data-table {
191
+ width: 100%; border-collapse: collapse; font-size: 13px;
192
+ }
193
+ .data-table th {
194
+ text-align: left; padding: 10px 12px;
195
+ background: var(--gray-50); color: var(--gray-500);
196
+ font-weight: 600; font-size: 12px; text-transform: uppercase;
197
+ letter-spacing: .03em; border-bottom: 2px solid var(--gray-200);
198
+ }
199
+ .data-table td {
200
+ padding: 10px 12px; border-bottom: 1px solid var(--gray-100);
201
+ color: var(--gray-700);
202
+ }
203
+ .data-table tbody tr:hover { background: var(--gray-50); }
204
+ .data-table .text-muted { color: var(--gray-400); font-style: italic; }
205
+
206
+ /* ===== BUTTONS ===== */
207
+ .btn {
208
+ display: inline-flex; align-items: center; justify-content: center;
209
+ padding: 8px 16px; border: none; border-radius: 6px;
210
+ font-size: 13px; font-weight: 500; cursor: pointer;
211
+ transition: all .15s; gap: 6px; white-space: nowrap;
212
+ }
213
+ .btn:disabled { opacity: .5; cursor: not-allowed; }
214
+ .btn-primary { background: var(--primary); color: var(--white); }
215
+ .btn-primary:hover:not(:disabled) { background: var(--primary-hover); }
216
+ .btn-secondary { background: var(--gray-100); color: var(--gray-700); }
217
+ .btn-secondary:hover:not(:disabled) { background: var(--gray-200); }
218
+ .btn-danger { background: var(--error); color: var(--white); }
219
+ .btn-danger:hover:not(:disabled) { background: #dc2626; }
220
+ .btn-sm { padding: 4px 10px; font-size: 12px; }
221
+ .btn-lg { padding: 10px 24px; font-size: 15px; }
222
+
223
+ /* ===== FORMS ===== */
224
+ .form-section {
225
+ background: var(--white); border-radius: var(--radius);
226
+ box-shadow: var(--shadow); padding: 20px; margin-bottom: 16px;
227
+ }
228
+ .form-title {
229
+ font-size: 16px; font-weight: 600; color: var(--gray-800);
230
+ margin-bottom: 16px;
231
+ }
232
+ .form-group { margin-bottom: 14px; }
233
+ .form-group label {
234
+ display: block; font-size: 13px; font-weight: 500;
235
+ color: var(--gray-600); margin-bottom: 5px;
236
+ }
237
+ .form-group input,
238
+ .form-group textarea,
239
+ .form-group select {
240
+ width: 100%; padding: 8px 12px;
241
+ border: 1px solid var(--gray-300); border-radius: 6px;
242
+ font-size: 13px; color: var(--gray-800);
243
+ background: var(--white); transition: border-color .15s;
244
+ }
245
+ .form-group input:focus,
246
+ .form-group textarea:focus,
247
+ .form-group select:focus {
248
+ outline: none; border-color: var(--primary);
249
+ box-shadow: 0 0 0 3px rgba(59,130,246,.15);
250
+ }
251
+ .form-group input[type="range"] {
252
+ padding: 0; border: none; box-shadow: none;
253
+ }
254
+ .form-row {
255
+ display: grid; grid-template-columns: 1fr 1fr; gap: 16px;
256
+ }
257
+ .form-actions {
258
+ display: flex; gap: 10px; margin-top: 16px;
259
+ }
260
+ .required { color: var(--error); }
261
+ .form-hint { font-size: 11px; color: var(--gray-400); margin-top: 2px; }
262
+ .input-with-btn { display: flex; gap: 8px; }
263
+ .input-with-btn select { flex: 1; }
264
+
265
+ /* ===== CONFIG AUTO-SAVE STATUS ===== */
266
+ .config-save-status {
267
+ display: inline-block;
268
+ font-size: 12px; font-weight: 400;
269
+ color: var(--gray-400);
270
+ margin-left: 12px; vertical-align: middle;
271
+ transition: opacity .3s ease;
272
+ }
273
+ .config-save-status.saving { color: var(--primary); }
274
+ .config-save-status.saved { color: var(--success); }
275
+ .config-save-status.error { color: var(--error); }
276
+ .config-save-status.fade-out { opacity: 0; }
277
+
278
+ /* ===== BASIC INFO: Tags & Owners ===== */
279
+ .tag-list {
280
+ display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; min-height: 28px;
281
+ }
282
+ .tag-item {
283
+ display: inline-flex; align-items: center; gap: 4px;
284
+ padding: 4px 10px; border-radius: 14px; font-size: 13px;
285
+ background: var(--gray-100); border: 1px solid var(--gray-200);
286
+ cursor: pointer; transition: all .15s ease;
287
+ }
288
+ .tag-item:hover { border-color: var(--primary); }
289
+ .tag-item.active {
290
+ background: var(--primary); color: #fff; border-color: var(--primary);
291
+ }
292
+ .tag-item .tag-remove {
293
+ display: inline-flex; align-items: center; justify-content: center;
294
+ width: 16px; height: 16px; border-radius: 50%; font-size: 12px; line-height: 1;
295
+ background: rgba(0,0,0,.1); cursor: pointer; border: none; padding: 0;
296
+ color: inherit;
297
+ }
298
+ .tag-item .tag-remove:hover { background: rgba(0,0,0,.25); }
299
+ .tag-input-row {
300
+ display: flex; gap: 8px;
301
+ }
302
+ .tag-input { flex: 1; }
303
+
304
+ .badge { display: inline-block; font-size: 10px; padding: 1px 6px; border-radius: 8px; vertical-align: middle; }
305
+ .badge-primary { background: var(--primary); color: #fff; }
306
+
307
+ .owner-list {
308
+ display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px;
309
+ }
310
+ .owner-item {
311
+ display: flex; align-items: center; gap: 8px;
312
+ padding: 6px 12px; border-radius: 8px; font-size: 13px;
313
+ background: var(--gray-100); border: 1px solid var(--gray-200);
314
+ }
315
+ .owner-item .owner-phone { font-weight: 600; min-width: 120px; }
316
+ .owner-item .owner-name { color: var(--gray-500); min-width: 60px; }
317
+ .owner-item .owner-note { color: var(--gray-400); flex: 1; font-size: 12px; }
318
+ .owner-item .tag-remove { margin-left: auto; flex-shrink: 0; }
319
+
320
+ .owner-add-row {
321
+ display: flex; gap: 8px;
322
+ }
323
+ .owner-input { font-size: 13px; }
324
+ .owner-input-phone { flex: 2; }
325
+ .owner-input-name { flex: 1.5; }
326
+ .owner-input-note { flex: 2; }
327
+
328
+ /* ===== HIDDEN ===== */
329
+ .hidden { display: none !important; }
330
+
331
+ /* ===== PAGINATION ===== */
332
+ .pagination {
333
+ display: flex; align-items: center; justify-content: center;
334
+ gap: 10px; padding: 16px 0;
335
+ }
336
+ .pagination-info { font-size: 13px; color: var(--gray-500); }
337
+
338
+ /* ===== SEARCH BOX ===== */
339
+ .search-box input {
340
+ padding: 6px 12px; border: 1px solid var(--gray-300);
341
+ border-radius: 6px; font-size: 13px; width: 220px;
342
+ }
343
+ .search-box input:focus {
344
+ outline: none; border-color: var(--primary);
345
+ box-shadow: 0 0 0 3px rgba(59,130,246,.15);
346
+ }
347
+
348
+ /* ===== PROVIDER TABS ===== */
349
+ .provider-tabs {
350
+ display: flex; gap: 0; margin-bottom: 16px;
351
+ border-bottom: 2px solid var(--gray-200);
352
+ }
353
+ .provider-tab {
354
+ padding: 8px 20px; background: none; border: none;
355
+ font-size: 14px; font-weight: 500; color: var(--gray-500);
356
+ cursor: pointer; border-bottom: 2px solid transparent;
357
+ margin-bottom: -2px; transition: all .15s;
358
+ }
359
+ .provider-tab:hover { color: var(--gray-700); }
360
+ .provider-tab.active {
361
+ color: var(--primary); border-bottom-color: var(--primary);
362
+ }
363
+ .provider-config { display: none; }
364
+ .provider-config.active { display: block; }
365
+
366
+ /* ===== BLUETOOTH CONNECTION CARD ===== */
367
+ .bt-connection-card { padding: 4px 0; }
368
+ .bt-status-row {
369
+ display: flex; align-items: center; gap: 10px;
370
+ margin-bottom: 16px; font-size: 16px; font-weight: 600;
371
+ }
372
+ .bt-status-indicator {
373
+ width: 12px; height: 12px; border-radius: 50%;
374
+ background: var(--error); display: inline-block;
375
+ }
376
+ .bt-status-indicator.connected { background: var(--success); }
377
+ .bt-device-info {
378
+ display: grid; grid-template-columns: 1fr 1fr;
379
+ gap: 12px; margin-bottom: 16px;
380
+ }
381
+ .bt-info-item { display: flex; flex-direction: column; gap: 2px; }
382
+ .info-label { font-size: 12px; color: var(--gray-500); font-weight: 500; }
383
+ .info-value { font-size: 14px; color: var(--gray-800); }
384
+ .bt-actions { display: flex; gap: 10px; }
385
+
386
+ /* ===== CALL DETAIL ===== */
387
+ .call-info-grid {
388
+ display: grid; grid-template-columns: repeat(3, 1fr);
389
+ gap: 16px;
390
+ }
391
+ .call-info-item { display: flex; flex-direction: column; gap: 2px; }
392
+
393
+ /* ===== TRANSCRIPT / CHAT ===== */
394
+ .transcript-container {
395
+ max-height: 500px; overflow-y: auto;
396
+ display: flex; flex-direction: column; gap: 10px;
397
+ padding: 12px 0;
398
+ }
399
+ .chat-msg {
400
+ max-width: 75%; padding: 10px 14px; border-radius: 12px;
401
+ font-size: 13px; line-height: 1.5;
402
+ }
403
+ .chat-msg.ai {
404
+ align-self: flex-end;
405
+ background: var(--primary); color: var(--white);
406
+ border-bottom-right-radius: 4px;
407
+ }
408
+ .chat-msg.human {
409
+ align-self: flex-start;
410
+ background: var(--gray-200); color: var(--gray-800);
411
+ border-bottom-left-radius: 4px;
412
+ }
413
+ .chat-msg.system {
414
+ align-self: center; text-align: center;
415
+ background: var(--gray-100); color: var(--gray-500);
416
+ font-size: 12px; border-radius: 20px;
417
+ }
418
+ .chat-msg.mcp {
419
+ align-self: center; width: 90%;
420
+ background: var(--warning-light); color: var(--gray-800);
421
+ border: 1px solid var(--warning); font-size: 12px;
422
+ }
423
+ .chat-msg .msg-role {
424
+ font-size: 11px; font-weight: 600; opacity: .7; margin-bottom: 3px;
425
+ }
426
+ .chat-msg .msg-time {
427
+ font-size: 11px; opacity: .6; margin-top: 4px;
428
+ }
429
+ .summary-content { font-size: 14px; line-height: 1.6; color: var(--gray-700); }
430
+ .text-muted { color: var(--gray-400); }
431
+ .tool-calls-container { font-size: 13px; }
432
+
433
+ /* ===== TAGS ===== */
434
+ .tag {
435
+ display: inline-block; padding: 2px 8px; border-radius: 10px;
436
+ background: var(--primary-light); color: var(--primary);
437
+ font-size: 11px; font-weight: 500; margin: 1px 2px;
438
+ }
439
+
440
+ /* ===== BADGE ===== */
441
+ .badge {
442
+ display: inline-block; padding: 2px 8px; border-radius: 10px;
443
+ font-size: 11px; font-weight: 600;
444
+ }
445
+ .badge-success { background: var(--success-light); color: var(--success); }
446
+ .badge-error { background: var(--error-light); color: var(--error); }
447
+ .badge-warning { background: var(--warning-light); color: var(--warning); }
448
+ .badge-info { background: var(--primary-light); color: var(--primary); }
449
+ .badge-gray { background: var(--gray-100); color: var(--gray-500); }
450
+
451
+ /* ===== MODAL ===== */
452
+ .modal-overlay {
453
+ position: fixed; inset: 0;
454
+ background: rgba(0,0,0,.4); backdrop-filter: blur(2px);
455
+ display: flex; align-items: center; justify-content: center;
456
+ z-index: 200;
457
+ }
458
+ .modal {
459
+ background: var(--white); border-radius: var(--radius);
460
+ box-shadow: var(--shadow-md);
461
+ width: 90%; max-width: 560px; max-height: 85vh;
462
+ display: flex; flex-direction: column;
463
+ }
464
+ .modal-header {
465
+ display: flex; align-items: center; justify-content: space-between;
466
+ padding: 16px 20px; border-bottom: 1px solid var(--gray-200);
467
+ }
468
+ .modal-title { font-size: 16px; font-weight: 600; }
469
+ .modal-close {
470
+ background: none; border: none; font-size: 22px;
471
+ cursor: pointer; color: var(--gray-400); padding: 0 4px;
472
+ }
473
+ .modal-close:hover { color: var(--gray-700); }
474
+ .modal-body { padding: 20px; overflow-y: auto; flex: 1; }
475
+ .modal-footer {
476
+ padding: 12px 20px; border-top: 1px solid var(--gray-200);
477
+ display: flex; justify-content: flex-end; gap: 8px;
478
+ }
479
+
480
+ /* ===== CHAT TEST DIALOG ===== */
481
+ .chat-test-overlay {
482
+ position: fixed; inset: 0; z-index: 250;
483
+ background: rgba(0,0,0,.45); backdrop-filter: blur(3px);
484
+ display: flex; align-items: center; justify-content: center;
485
+ }
486
+ .chat-test-overlay.hidden { display: none; }
487
+ .chat-test-dialog {
488
+ width: 760px; max-width: 95vw; height: 82vh;
489
+ background: var(--white); border-radius: 12px;
490
+ box-shadow: 0 20px 60px rgba(0,0,0,.25);
491
+ display: flex; flex-direction: column; overflow: hidden;
492
+ }
493
+ .chat-test-header {
494
+ padding: 14px 20px; border-bottom: 1px solid var(--gray-200);
495
+ display: flex; align-items: center; justify-content: space-between;
496
+ flex-shrink: 0;
497
+ }
498
+ .chat-test-header h3 { margin: 0; font-size: 16px; font-weight: 600; }
499
+ .chat-test-controls {
500
+ display: flex; align-items: center; gap: 8px;
501
+ }
502
+ .chat-test-select {
503
+ padding: 5px 10px; border: 1px solid var(--gray-300); border-radius: 6px;
504
+ font-size: 13px; background: var(--white); color: var(--gray-800);
505
+ }
506
+ .chat-test-model-select { max-width: 220px; }
507
+ .chat-test-close {
508
+ background: none; border: none; font-size: 22px; cursor: pointer;
509
+ color: var(--gray-400); padding: 0 4px; line-height: 1;
510
+ }
511
+ .chat-test-close:hover { color: var(--gray-700); }
512
+
513
+ .chat-test-messages {
514
+ flex: 1; overflow-y: auto; padding: 20px;
515
+ display: flex; flex-direction: column; gap: 12px;
516
+ background: var(--gray-50);
517
+ }
518
+ .chat-test-empty {
519
+ text-align: center; color: var(--gray-400); padding: 60px 0; font-size: 14px;
520
+ }
521
+
522
+ .chat-test-bubble {
523
+ max-width: 80%; padding: 10px 14px; border-radius: 12px;
524
+ font-size: 14px; line-height: 1.6; white-space: pre-wrap; word-break: break-word;
525
+ }
526
+ .chat-test-bubble.user {
527
+ align-self: flex-end;
528
+ background: var(--primary); color: var(--white);
529
+ border-bottom-right-radius: 4px;
530
+ }
531
+ .chat-test-bubble.assistant {
532
+ align-self: flex-start;
533
+ background: var(--white); color: var(--gray-800);
534
+ border: 1px solid var(--gray-200);
535
+ border-bottom-left-radius: 4px;
536
+ }
537
+ .chat-test-bubble.error {
538
+ align-self: center;
539
+ background: var(--danger-light, #fef2f2); color: var(--danger, #ef4444);
540
+ border: 1px solid var(--danger, #ef4444); font-size: 13px;
541
+ }
542
+ .chat-test-bubble.thinking {
543
+ align-self: flex-start;
544
+ background: var(--white); color: var(--gray-400);
545
+ border: 1px solid var(--gray-200);
546
+ border-bottom-left-radius: 4px;
547
+ font-style: italic;
548
+ }
549
+
550
+ .chat-test-footer {
551
+ padding: 14px 20px; border-top: 1px solid var(--gray-200);
552
+ display: flex; gap: 10px; align-items: flex-end;
553
+ flex-shrink: 0; background: var(--white);
554
+ }
555
+ .chat-test-input {
556
+ flex: 1; resize: none; border: 1px solid var(--gray-300); border-radius: 8px;
557
+ padding: 10px 14px; font-size: 14px; font-family: inherit;
558
+ line-height: 1.5; max-height: 120px; overflow-y: auto;
559
+ }
560
+ .chat-test-input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px rgba(79,70,229,.1); }
561
+
562
+ /* ===== TOAST ===== */
563
+ #toast-container {
564
+ position: fixed; top: 70px; right: 20px;
565
+ display: flex; flex-direction: column; gap: 8px;
566
+ z-index: 300; pointer-events: none;
567
+ }
568
+ .toast {
569
+ background: var(--white); border-radius: 8px;
570
+ box-shadow: var(--shadow-md); padding: 12px 16px;
571
+ font-size: 13px; min-width: 260px; max-width: 380px;
572
+ border-left: 4px solid var(--gray-400);
573
+ pointer-events: auto;
574
+ animation: toastIn .25s ease-out;
575
+ }
576
+ .toast.success { border-left-color: var(--success); }
577
+ .toast.error { border-left-color: var(--error); }
578
+ .toast.info { border-left-color: var(--primary); }
579
+ @keyframes toastIn { from { opacity:0; transform: translateX(40px); } to { opacity:1; transform: translateX(0); } }
580
+
581
+ /* ===== AUDIO PLAYER ===== */
582
+ #detail-audio { width: 100%; margin-top: 8px; }
583
+
584
+ /* ===== ASR TEST DIALOG ===== */
585
+ .asr-test-overlay {
586
+ position: fixed; inset: 0; z-index: 250;
587
+ background: rgba(0,0,0,.45); backdrop-filter: blur(3px);
588
+ display: flex; align-items: center; justify-content: center;
589
+ }
590
+ .asr-test-overlay.hidden { display: none; }
591
+ .asr-test-dialog {
592
+ width: 520px; max-width: 95vw; max-height: 90vh;
593
+ background: var(--white); border-radius: 12px;
594
+ box-shadow: 0 20px 60px rgba(0,0,0,.25);
595
+ display: flex; flex-direction: column; overflow: hidden;
596
+ }
597
+ .asr-test-header {
598
+ padding: 14px 20px; border-bottom: 1px solid var(--gray-200);
599
+ display: flex; align-items: center; justify-content: space-between;
600
+ flex-shrink: 0;
601
+ }
602
+ .asr-test-header h3 { margin: 0; font-size: 16px; font-weight: 600; }
603
+ .asr-test-close {
604
+ background: none; border: none; font-size: 22px; cursor: pointer;
605
+ color: var(--gray-400); padding: 0 4px; line-height: 1;
606
+ }
607
+ .asr-test-close:hover { color: var(--gray-700); }
608
+ .asr-test-body {
609
+ padding: 24px 20px; min-height: 200px;
610
+ display: flex; flex-direction: column; gap: 20px;
611
+ overflow-y: auto; flex: 1 1 auto;
612
+ }
613
+ .asr-test-status-area { text-align: center; }
614
+ .asr-test-status-text { font-size: 14px; color: var(--gray-500); }
615
+ .asr-test-status-text.recording {
616
+ color: var(--error);
617
+ animation: pulse 1.5s infinite;
618
+ }
619
+ @keyframes pulse {
620
+ 0%, 100% { opacity: 1; }
621
+ 50% { opacity: .5; }
622
+ }
623
+ .asr-test-result-area {
624
+ background: var(--gray-50); border-radius: 8px;
625
+ padding: 16px; min-height: 80px; max-height: 200px;
626
+ overflow-y: auto;
627
+ }
628
+ .asr-test-result-label {
629
+ font-size: 12px; font-weight: 500; color: var(--gray-500);
630
+ margin-bottom: 8px;
631
+ }
632
+ .asr-test-result-text {
633
+ font-size: 16px; line-height: 1.6; color: var(--gray-800);
634
+ white-space: pre-wrap; word-break: break-word; min-height: 40px;
635
+ }
636
+ .asr-test-footer {
637
+ padding: 14px 20px; border-top: 1px solid var(--gray-200);
638
+ display: flex; justify-content: center; gap: 12px;
639
+ flex-shrink: 0;
640
+ }
641
+ .asr-test-stats {
642
+ display: flex; justify-content: center; gap: 16px;
643
+ font-size: 12px; color: var(--gray-500); font-variant-numeric: tabular-nums;
644
+ }
645
+ .asr-test-stats span {
646
+ background: var(--gray-100); padding: 2px 10px; border-radius: 10px;
647
+ }
648
+ .asr-test-log {
649
+ font-family: monospace; font-size: 11px; line-height: 1.5;
650
+ color: var(--gray-600); background: var(--gray-50);
651
+ border-radius: 6px; padding: 8px 12px;
652
+ max-height: 120px; overflow-y: auto; white-space: pre-wrap;
653
+ }
654
+ .asr-test-playback {
655
+ display: flex; justify-content: center; align-items: center; gap: 10px;
656
+ }
657
+ .asr-test-play-dur { font-size: 12px; color: var(--gray-500); }
658
+
659
+ /* --- ASR Controls Bar --- */
660
+ .asr-ctrl-bar {
661
+ background: var(--gray-50); border-radius: 8px; padding: 10px 12px;
662
+ margin-bottom: 10px; display: flex; flex-direction: column; gap: 8px;
663
+ }
664
+ .asr-ctrl-row {
665
+ display: flex; align-items: center; gap: 8px;
666
+ }
667
+ .asr-ctrl-label {
668
+ font-size: 12px; color: var(--gray-500); font-weight: 500; white-space: nowrap;
669
+ }
670
+ .asr-ctrl-select {
671
+ padding: 4px 8px; border: 1px solid var(--gray-300); border-radius: 5px;
672
+ font-size: 12px; background: var(--white); color: var(--gray-800);
673
+ overflow: hidden; text-overflow: ellipsis;
674
+ }
675
+ .asr-mute-btn {
676
+ background: none; border: 1px solid var(--gray-300); border-radius: 6px;
677
+ width: 34px; height: 30px; cursor: pointer; color: var(--gray-700);
678
+ display: flex; align-items: center; justify-content: center;
679
+ transition: all .15s; flex-shrink: 0;
680
+ }
681
+ .asr-mute-btn:hover { background: var(--gray-100); }
682
+ .asr-mute-btn .mic-slash { display: none; }
683
+ .asr-mute-btn.muted {
684
+ background: var(--error-light); border-color: var(--error); color: var(--error);
685
+ }
686
+ .asr-mute-btn.muted .mic-slash { display: inline; }
687
+ .asr-mic-gain {
688
+ width: 80px; flex-shrink: 0; cursor: pointer;
689
+ }
690
+ .asr-gain-val {
691
+ font-size: 11px; color: var(--gray-500); min-width: 36px; text-align: right;
692
+ font-variant-numeric: tabular-nums;
693
+ }
694
+ .asr-agc-label {
695
+ font-size: 11px; color: var(--gray-500); white-space: nowrap; cursor: pointer;
696
+ display: flex; align-items: center; gap: 3px; margin-left: 4px;
697
+ }
698
+
699
+ /* --- ASR Status + Timer row --- */
700
+ .asr-test-status-row {
701
+ display: flex; align-items: center; justify-content: center; gap: 16px;
702
+ margin-bottom: 12px;
703
+ }
704
+ .asr-test-timer {
705
+ font-size: 20px; font-weight: 700; font-variant-numeric: tabular-nums;
706
+ color: var(--error); min-width: 56px; text-align: center;
707
+ }
708
+ .asr-test-timer:empty { display: none; }
709
+
710
+ /* --- Waveform visualization --- */
711
+ .asr-test-viz {
712
+ background: var(--gray-900); border-radius: 8px; padding: 12px;
713
+ margin-bottom: 12px;
714
+ }
715
+ #asr-test-canvas {
716
+ width: 100%; height: 100px; display: block; border-radius: 4px;
717
+ }
718
+ .asr-test-vol-row {
719
+ display: flex; align-items: center; gap: 8px; margin-top: 8px;
720
+ }
721
+ .asr-test-vol-label { font-size: 11px; color: var(--gray-400); min-width: 28px; }
722
+ .asr-test-vol-track {
723
+ flex: 1; height: 6px; background: var(--gray-700); border-radius: 3px;
724
+ overflow: hidden;
725
+ }
726
+ .asr-test-vol-bar {
727
+ height: 100%; width: 0%; border-radius: 3px;
728
+ background: linear-gradient(90deg, var(--success) 0%, var(--warning) 70%, var(--error) 100%);
729
+ transition: width 0.08s ease-out;
730
+ }
731
+ .asr-test-vol-db {
732
+ font-size: 11px; color: var(--gray-400); min-width: 50px; text-align: right;
733
+ font-variant-numeric: tabular-nums;
734
+ }
735
+
736
+ /* ===== VOICE CHAT ===== */
737
+ .vc-layout {
738
+ display: flex; gap: 16px;
739
+ height: calc(100vh - var(--header-height) - var(--statusbar-height) - 48px);
740
+ }
741
+ .vc-main {
742
+ flex: 1; display: flex; flex-direction: column; min-width: 0;
743
+ }
744
+ .vc-sidebar {
745
+ width: 260px; flex-shrink: 0; display: flex; flex-direction: column;
746
+ background: var(--white); border-radius: var(--radius);
747
+ border: 1px solid var(--gray-200); overflow: hidden;
748
+ }
749
+ .vc-sidebar-title {
750
+ padding: 10px 14px; font-size: 13px; font-weight: 600;
751
+ color: var(--gray-600); border-bottom: 1px solid var(--gray-200);
752
+ background: var(--gray-50); flex-shrink: 0;
753
+ }
754
+ .vc-log {
755
+ flex: 1; overflow-y: auto; padding: 8px 12px;
756
+ font-family: monospace; font-size: 11px; line-height: 1.6;
757
+ color: var(--gray-600); white-space: pre-wrap; word-break: break-all;
758
+ min-height: 0; max-height: 50%;
759
+ }
760
+ .vc-log-entry { margin-bottom: 2px; }
761
+ .vc-log-time { color: var(--gray-400); }
762
+ .vc-log-entry.error { color: var(--error); }
763
+ .vc-log-entry.warn { color: var(--warning, #f59e0b); }
764
+ .vc-log-entry.success { color: var(--success); }
765
+ .vc-log-entry.info { color: var(--primary); }
766
+
767
+ /* Debug records */
768
+ .vc-debug-title { cursor: default; }
769
+ .vc-debug-count {
770
+ font-weight: 400; color: var(--gray-400); font-size: 11px; margin-left: 4px;
771
+ }
772
+ .vc-debug {
773
+ flex: 1; overflow-y: auto; padding: 4px 8px;
774
+ font-family: monospace; font-size: 11px; line-height: 1.5;
775
+ }
776
+ .vc-debug-item { margin-bottom: 4px; }
777
+ .vc-debug-item summary {
778
+ cursor: pointer; padding: 3px 6px; border-radius: 4px;
779
+ color: var(--gray-600); font-size: 11px;
780
+ }
781
+ .vc-debug-item summary:hover { background: var(--gray-100); }
782
+ .vc-debug-item summary .vc-dbg-turn { font-weight: 600; color: var(--primary); }
783
+ .vc-debug-item summary .vc-dbg-time { color: var(--gray-400); margin-left: 4px; }
784
+ .vc-debug-item pre {
785
+ margin: 4px 0 0 0; padding: 6px 8px; background: var(--gray-50);
786
+ border: 1px solid var(--gray-200); border-radius: 4px;
787
+ white-space: pre-wrap; word-break: break-all; font-size: 10px;
788
+ color: var(--gray-700); max-height: 300px; overflow-y: auto;
789
+ }
790
+
791
+ .vc-status-bar {
792
+ display: flex; align-items: center; justify-content: center;
793
+ padding: 10px 0; flex-shrink: 0; gap: 16px;
794
+ }
795
+ .vc-call-timer {
796
+ font-size: 18px; font-weight: 700; color: var(--gray-500);
797
+ font-variant-numeric: tabular-nums; min-width: 50px;
798
+ }
799
+ .vc-call-timer:empty { display: none; }
800
+ .vc-status-indicator {
801
+ display: flex; align-items: center; gap: 10px;
802
+ padding: 8px 20px; border-radius: 20px;
803
+ background: var(--gray-100); font-size: 14px; font-weight: 500;
804
+ color: var(--gray-500); transition: all .3s;
805
+ }
806
+ .vc-status-indicator.listening { background: var(--success-light); color: var(--success); }
807
+ .vc-status-indicator.thinking { background: var(--primary-light); color: var(--primary); }
808
+ .vc-status-indicator.speaking { background: var(--warning-light); color: #b45309; }
809
+ .vc-pulse-dot {
810
+ width: 10px; height: 10px; border-radius: 50%;
811
+ background: var(--gray-400); display: inline-block; transition: background .3s;
812
+ }
813
+ .vc-status-indicator.listening .vc-pulse-dot { background: var(--success); animation: vcPulse 1.5s infinite; }
814
+ .vc-status-indicator.thinking .vc-pulse-dot { background: var(--primary); animation: vcPulse 1s infinite; }
815
+ .vc-status-indicator.speaking .vc-pulse-dot { background: #b45309; animation: vcPulse 0.8s infinite; }
816
+ @keyframes vcPulse {
817
+ 0%, 100% { opacity: 1; transform: scale(1); }
818
+ 50% { opacity: .4; transform: scale(1.3); }
819
+ }
820
+
821
+ /* Config info panel */
822
+ .vc-config-panel {
823
+ display: flex; gap: 16px; flex-wrap: wrap;
824
+ padding: 8px 14px; margin-bottom: 6px;
825
+ background: var(--white); border-radius: var(--radius);
826
+ border: 1px solid var(--gray-200); flex-shrink: 0; font-size: 12px;
827
+ }
828
+ .vc-config-row { display: flex; align-items: center; gap: 6px; }
829
+ .vc-config-label {
830
+ font-weight: 600; color: var(--gray-500);
831
+ padding: 1px 6px; border-radius: 4px; background: var(--gray-100);
832
+ }
833
+ .vc-config-value { color: var(--gray-700); }
834
+
835
+ /* VAD inline controls */
836
+ .vc-vad-bar {
837
+ display: flex; gap: 12px; flex-wrap: wrap;
838
+ padding: 6px 14px; margin-bottom: 6px;
839
+ background: var(--white); border-radius: var(--radius);
840
+ border: 1px solid var(--gray-200); flex-shrink: 0; font-size: 12px;
841
+ align-items: center;
842
+ }
843
+ .vc-vad-group { display: flex; align-items: center; gap: 4px; }
844
+ .vc-vad-label {
845
+ font-weight: 500; color: var(--gray-500); white-space: nowrap;
846
+ }
847
+ .vc-vad-input {
848
+ width: 70px; padding: 2px 6px; border: 1px solid var(--gray-300);
849
+ border-radius: 4px; font-size: 12px; text-align: center;
850
+ color: var(--gray-800); background: var(--white);
851
+ font-variant-numeric: tabular-nums;
852
+ }
853
+ .vc-vad-input:focus {
854
+ outline: none; border-color: var(--primary);
855
+ box-shadow: 0 0 0 2px rgba(59,130,246,.15);
856
+ }
857
+
858
+ /* Phone simulation bar */
859
+ .vc-phone-bar {
860
+ display: flex; gap: 10px; flex-wrap: wrap;
861
+ padding: 6px 14px; margin-bottom: 6px;
862
+ background: var(--white); border-radius: var(--radius);
863
+ border: 1px solid var(--gray-200); flex-shrink: 0; font-size: 12px;
864
+ align-items: center;
865
+ }
866
+ .vc-phone-group { display: flex; align-items: center; gap: 4px; }
867
+ .vc-phone-group-num { flex: 1; min-width: 120px; }
868
+ .vc-phone-group-name { flex: 1; min-width: 80px; }
869
+ .vc-phone-label { font-weight: 500; color: var(--gray-500); white-space: nowrap; }
870
+ .vc-phone-select {
871
+ padding: 2px 6px; border: 1px solid var(--gray-300); border-radius: 4px;
872
+ font-size: 12px; color: var(--gray-800); background: var(--white);
873
+ }
874
+ .vc-phone-input {
875
+ flex: 1; padding: 2px 6px; border: 1px solid var(--gray-300);
876
+ border-radius: 4px; font-size: 12px; color: var(--gray-800);
877
+ background: var(--white); min-width: 0;
878
+ }
879
+ .vc-phone-select:focus, .vc-phone-input:focus {
880
+ outline: none; border-color: var(--primary);
881
+ box-shadow: 0 0 0 2px rgba(59,130,246,.15);
882
+ }
883
+
884
+ /* System prompt */
885
+ .vc-prompt-bar { margin-bottom: 6px; flex-shrink: 0; }
886
+ .vc-prompt-label {
887
+ font-size: 12px; font-weight: 500; color: var(--gray-500);
888
+ margin-bottom: 3px; display: block;
889
+ }
890
+ .vc-prompt-input {
891
+ width: 100%; padding: 6px 10px; border: 1px solid var(--gray-300);
892
+ border-radius: 6px; font-size: 12px; color: var(--gray-800);
893
+ background: var(--white); resize: vertical; font-family: inherit; line-height: 1.5;
894
+ }
895
+ .vc-prompt-input:focus {
896
+ outline: none; border-color: var(--primary);
897
+ box-shadow: 0 0 0 3px rgba(59,130,246,.15);
898
+ }
899
+
900
+ /* Waveform */
901
+ .vc-viz {
902
+ background: var(--gray-900); border-radius: 8px; padding: 8px;
903
+ margin-bottom: 6px; flex-shrink: 0;
904
+ }
905
+ #vc-canvas { width: 100%; height: 64px; display: block; border-radius: 4px; }
906
+ .vc-vol-row { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
907
+ .vc-vol-label { font-size: 11px; color: var(--gray-400); min-width: 28px; }
908
+ .vc-vol-track {
909
+ flex: 1; height: 5px; background: var(--gray-700); border-radius: 3px; overflow: hidden;
910
+ }
911
+ .vc-vol-bar {
912
+ height: 100%; width: 0%; border-radius: 3px;
913
+ background: linear-gradient(90deg, var(--success) 0%, var(--warning) 70%, var(--error) 100%);
914
+ transition: width 0.08s ease-out;
915
+ }
916
+ .vc-vol-db {
917
+ font-size: 11px; color: var(--gray-400); min-width: 50px; text-align: right;
918
+ font-variant-numeric: tabular-nums;
919
+ }
920
+
921
+ /* Messages */
922
+ .vc-messages {
923
+ flex: 1; overflow-y: auto; padding: 16px;
924
+ display: flex; flex-direction: column; gap: 12px;
925
+ background: var(--gray-50); border-radius: var(--radius);
926
+ border: 1px solid var(--gray-200);
927
+ }
928
+ .vc-empty-hint { text-align: center; color: var(--gray-400); padding: 60px 0; font-size: 14px; }
929
+
930
+ /* Chat bubbles */
931
+ .vc-bubble {
932
+ max-width: 80%; padding: 10px 14px; border-radius: 12px;
933
+ font-size: 14px; line-height: 1.6; word-break: break-word; white-space: pre-wrap;
934
+ }
935
+ .vc-bubble.user {
936
+ align-self: flex-end; background: var(--primary); color: var(--white);
937
+ border-bottom-right-radius: 4px;
938
+ }
939
+ .vc-bubble.ai {
940
+ align-self: flex-start; background: var(--white); color: var(--gray-800);
941
+ border: 1px solid var(--gray-200); border-bottom-left-radius: 4px;
942
+ }
943
+ .vc-bubble.interim {
944
+ align-self: flex-end; background: var(--gray-200); color: var(--gray-500);
945
+ border-bottom-right-radius: 4px; font-style: italic;
946
+ }
947
+ .vc-bubble .vc-bubble-label {
948
+ font-size: 11px; font-weight: 600; opacity: .6;
949
+ margin-bottom: 3px; display: flex; justify-content: space-between;
950
+ }
951
+ .vc-bubble .vc-bubble-time { font-weight: 400; font-size: 10px; opacity: .7; }
952
+ /* Tool Call Card — collapsed */
953
+ .vc-tool-card {
954
+ align-self: flex-start; display: inline-flex; flex-direction: column;
955
+ max-width: 85%; margin: 4px 0;
956
+ }
957
+ .vc-tool-card-header {
958
+ display: inline-flex; align-items: center; gap: 8px;
959
+ background: #f8f9fb; border: 1px solid #e5e7eb; border-radius: 22px;
960
+ padding: 8px 16px; cursor: pointer; user-select: none;
961
+ transition: background .15s, box-shadow .15s; font-size: 14px; color: #374151;
962
+ }
963
+ .vc-tool-card-header:hover { background: #f0f2f5; box-shadow: 0 1px 4px rgba(0,0,0,.06); }
964
+ .vc-tool-card-icon {
965
+ width: 20px; height: 20px; flex-shrink: 0; opacity: .55;
966
+ }
967
+ .vc-tool-card-name { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
968
+ .vc-tool-card-metrics { display: flex; align-items: center; gap: 8px; margin-left: auto; font-size: 12px; color: #9ca3af; white-space: nowrap; }
969
+ .vc-tool-card-metrics .up { color: #6b7280; }
970
+ .vc-tool-card-metrics .down { color: #6b7280; }
971
+ .vc-tool-card-status { width: 20px; height: 20px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; }
972
+ .vc-tool-card-status .spinner {
973
+ width: 16px; height: 16px; border: 2px solid #d1d5db; border-top-color: #6b7280;
974
+ border-radius: 50%; animation: vcToolSpin .8s linear infinite;
975
+ }
976
+ @keyframes vcToolSpin { to { transform: rotate(360deg); } }
977
+ .vc-tool-card-status .checkmark { color: #22c55e; font-size: 18px; line-height: 1; }
978
+ .vc-tool-card-status .error-mark { color: #ef4444; font-size: 18px; line-height: 1; }
979
+
980
+ /* Tool Call Card — expanded detail */
981
+ .vc-tool-detail {
982
+ display: none; border: 1px solid #e5e7eb; border-radius: 12px;
983
+ margin-top: 6px; background: #fff; overflow: hidden;
984
+ box-shadow: 0 1px 6px rgba(0,0,0,.06); font-size: 13px; color: #374151;
985
+ }
986
+ .vc-tool-card.expanded .vc-tool-detail { display: block; }
987
+ .vc-tool-detail-header {
988
+ display: flex; align-items: center; justify-content: space-between;
989
+ padding: 12px 16px; border-bottom: 1px solid #f0f0f0;
990
+ }
991
+ .vc-tool-detail-header .title { display: flex; align-items: center; gap: 6px; font-weight: 500; color: #6b7280; }
992
+ .vc-tool-detail-header .title svg { width: 16px; height: 16px; opacity: .5; }
993
+ .vc-tool-detail-copy {
994
+ background: none; border: none; cursor: pointer; padding: 4px; color: #9ca3af; transition: color .15s;
995
+ }
996
+ .vc-tool-detail-copy:hover { color: #374151; }
997
+ .vc-tool-detail-copy svg { width: 16px; height: 16px; }
998
+ .vc-tool-detail-info { padding: 12px 16px; line-height: 1.8; color: #6b7280; font-size: 12px; }
999
+ .vc-tool-detail-info .label { color: #3b82f6; font-weight: 500; }
1000
+ .vc-tool-detail-sep { height: 1px; background: #f0f0f0; margin: 0 16px; }
1001
+ .vc-tool-detail-section { padding: 12px 16px; }
1002
+ .vc-tool-detail-section .section-title {
1003
+ display: flex; align-items: center; gap: 6px;
1004
+ font-size: 12px; font-weight: 500; color: #6b7280; margin-bottom: 8px;
1005
+ }
1006
+ .vc-tool-detail-section .section-title .arrow-up { color: #6b7280; }
1007
+ .vc-tool-detail-section .section-title .arrow-down { color: #3b82f6; }
1008
+ .vc-tool-detail-section pre {
1009
+ background: #f8f9fb; border-radius: 6px; padding: 10px 12px;
1010
+ font-family: 'Courier New', Consolas, monospace; font-size: 12px;
1011
+ white-space: pre-wrap; word-break: break-all; max-height: 200px; overflow-y: auto;
1012
+ margin: 0; color: #374151; line-height: 1.5;
1013
+ }
1014
+
1015
+ /* Controls */
1016
+ .vc-controls {
1017
+ display: flex; justify-content: center; padding: 12px 0; flex-shrink: 0; gap: 12px;
1018
+ }
1019
+ .vc-btn-call {
1020
+ background: var(--success); color: var(--white);
1021
+ padding: 12px 40px; font-size: 16px; border-radius: 30px;
1022
+ }
1023
+ .vc-btn-call:hover:not(:disabled) { background: #16a34a; }
1024
+ .vc-btn-hangup { padding: 12px 40px; font-size: 16px; border-radius: 30px; }
1025
+
1026
+ /* ===== RESPONSIVE ===== */
1027
+ @media (max-width: 768px) {
1028
+ #sidebar { display: none; }
1029
+ #main-content { margin-left: 0; }
1030
+ #status-bar { padding-left: 20px; }
1031
+ .stats-row { grid-template-columns: 1fr 1fr; }
1032
+ .form-row { grid-template-columns: 1fr; }
1033
+ .call-info-grid { grid-template-columns: 1fr 1fr; }
1034
+ .bt-device-info { grid-template-columns: 1fr; }
1035
+ .vc-layout { flex-direction: column; }
1036
+ .vc-sidebar { width: 100%; max-height: 200px; }
1037
+ .owner-add-row { flex-wrap: wrap; }
1038
+ .owner-add-row .owner-input { min-width: 0; flex-basis: 45%; }
1039
+ }
1040
+
1041
+ /* ===== TOGGLE SWITCH ===== */
1042
+ .toggle-switch-label {
1043
+ display: inline-flex; align-items: center; gap: 6px;
1044
+ cursor: pointer; font-size: 13px; color: var(--gray-600);
1045
+ user-select: none;
1046
+ }
1047
+ .toggle-switch-label input { display: none; }
1048
+ .toggle-switch {
1049
+ position: relative; width: 36px; min-width: 36px; height: 20px;
1050
+ background: var(--gray-300); border-radius: 10px;
1051
+ transition: background .2s; display: inline-block; flex-shrink: 0;
1052
+ }
1053
+ .toggle-switch::after {
1054
+ content: ''; position: absolute; top: 2px; left: 2px;
1055
+ width: 16px; height: 16px; border-radius: 50%;
1056
+ background: var(--white); box-shadow: 0 1px 2px rgba(0,0,0,.2);
1057
+ transition: transform .2s;
1058
+ }
1059
+ .toggle-switch-label input:checked + .toggle-switch {
1060
+ background: var(--primary);
1061
+ }
1062
+ .toggle-switch-label input:checked + .toggle-switch::after {
1063
+ transform: translateX(16px);
1064
+ }