let-them-talk 3.3.3 → 3.4.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.
- package/CHANGELOG.md +48 -0
- package/LICENSE +1 -1
- package/README.md +17 -9
- package/cli.js +103 -2
- package/dashboard.html +640 -124
- package/dashboard.js +342 -3
- package/package.json +2 -1
- package/server.js +1 -1
package/dashboard.html
CHANGED
|
@@ -5,55 +5,72 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Let Them Talk</title>
|
|
7
7
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect rx='20' width='100' height='100' fill='%230d1117'/><path d='M20 30 Q20 20 30 20 H70 Q80 20 80 30 V55 Q80 65 70 65 H55 L40 80 V65 H30 Q20 65 20 55Z' fill='%2358a6ff'/><circle cx='38' cy='42' r='5' fill='%230d1117'/><circle cx='55' cy='42' r='5' fill='%230d1117'/></svg>">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
9
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
10
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
8
11
|
<style>
|
|
9
12
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
10
13
|
|
|
11
14
|
:root {
|
|
12
|
-
--bg: #
|
|
13
|
-
--surface: #
|
|
14
|
-
--surface-2: #
|
|
15
|
-
--surface-3: #
|
|
16
|
-
--border:
|
|
17
|
-
--border-light:
|
|
18
|
-
--text: #
|
|
19
|
-
--text-dim: #
|
|
20
|
-
--text-muted: #
|
|
21
|
-
--accent: #
|
|
22
|
-
--accent-dim: rgba(
|
|
23
|
-
--
|
|
24
|
-
--green
|
|
25
|
-
--
|
|
26
|
-
--red
|
|
27
|
-
--
|
|
28
|
-
--orange
|
|
29
|
-
--
|
|
30
|
-
--purple
|
|
31
|
-
--
|
|
15
|
+
--bg: #080b12;
|
|
16
|
+
--surface: #0f1318;
|
|
17
|
+
--surface-2: #161c24;
|
|
18
|
+
--surface-3: #1e2530;
|
|
19
|
+
--border: rgba(255, 255, 255, 0.06);
|
|
20
|
+
--border-light: rgba(255, 255, 255, 0.1);
|
|
21
|
+
--text: #f0f4f8;
|
|
22
|
+
--text-dim: #94a3b8;
|
|
23
|
+
--text-muted: #5a6578;
|
|
24
|
+
--accent: #6c8aff;
|
|
25
|
+
--accent-dim: rgba(108, 138, 255, 0.12);
|
|
26
|
+
--accent-glow: rgba(108, 138, 255, 0.25);
|
|
27
|
+
--green: #34d399;
|
|
28
|
+
--green-dim: rgba(52, 211, 153, 0.12);
|
|
29
|
+
--red: #f87171;
|
|
30
|
+
--red-dim: rgba(248, 113, 113, 0.12);
|
|
31
|
+
--orange: #fbbf24;
|
|
32
|
+
--orange-dim: rgba(251, 191, 36, 0.12);
|
|
33
|
+
--purple: #c084fc;
|
|
34
|
+
--purple-dim: rgba(192, 132, 252, 0.12);
|
|
35
|
+
--yellow: #fbbf24;
|
|
32
36
|
--sidebar-w: 280px;
|
|
33
37
|
--header-h: 56px;
|
|
38
|
+
--glow: 0 0 20px rgba(108, 138, 255, 0.08);
|
|
39
|
+
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3);
|
|
40
|
+
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4);
|
|
41
|
+
--shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.5);
|
|
42
|
+
--gradient-accent: linear-gradient(135deg, #6c8aff, #a78bfa);
|
|
43
|
+
--gradient-surface: linear-gradient(180deg, rgba(255,255,255,0.03) 0%, transparent 100%);
|
|
34
44
|
}
|
|
35
45
|
|
|
36
46
|
[data-theme="light"] {
|
|
37
|
-
--bg: #
|
|
47
|
+
--bg: #f0f2f5;
|
|
38
48
|
--surface: #ffffff;
|
|
39
|
-
--surface-2: #
|
|
40
|
-
--surface-3: #
|
|
41
|
-
--border:
|
|
42
|
-
--border-light:
|
|
43
|
-
--text: #
|
|
44
|
-
--text-dim: #
|
|
45
|
-
--text-muted: #
|
|
46
|
-
--accent: #
|
|
47
|
-
--accent-dim: rgba(
|
|
48
|
-
--
|
|
49
|
-
--green
|
|
50
|
-
--
|
|
51
|
-
--red
|
|
52
|
-
--
|
|
53
|
-
--orange
|
|
54
|
-
--
|
|
55
|
-
--purple
|
|
56
|
-
--
|
|
49
|
+
--surface-2: #f7f8fa;
|
|
50
|
+
--surface-3: #eef0f4;
|
|
51
|
+
--border: rgba(0, 0, 0, 0.08);
|
|
52
|
+
--border-light: rgba(0, 0, 0, 0.12);
|
|
53
|
+
--text: #111827;
|
|
54
|
+
--text-dim: #4b5563;
|
|
55
|
+
--text-muted: #9ca3af;
|
|
56
|
+
--accent: #4f6eff;
|
|
57
|
+
--accent-dim: rgba(79, 110, 255, 0.08);
|
|
58
|
+
--accent-glow: rgba(79, 110, 255, 0.15);
|
|
59
|
+
--green: #059669;
|
|
60
|
+
--green-dim: rgba(5, 150, 105, 0.08);
|
|
61
|
+
--red: #dc2626;
|
|
62
|
+
--red-dim: rgba(220, 38, 38, 0.08);
|
|
63
|
+
--orange: #d97706;
|
|
64
|
+
--orange-dim: rgba(217, 119, 6, 0.08);
|
|
65
|
+
--purple: #7c3aed;
|
|
66
|
+
--purple-dim: rgba(124, 58, 237, 0.08);
|
|
67
|
+
--yellow: #d97706;
|
|
68
|
+
--glow: 0 0 20px rgba(79, 110, 255, 0.05);
|
|
69
|
+
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08);
|
|
70
|
+
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
71
|
+
--shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.12);
|
|
72
|
+
--gradient-accent: linear-gradient(135deg, #4f6eff, #7c3aed);
|
|
73
|
+
--gradient-surface: linear-gradient(180deg, rgba(0,0,0,0.01) 0%, transparent 100%);
|
|
57
74
|
}
|
|
58
75
|
|
|
59
76
|
.theme-toggle {
|
|
@@ -84,16 +101,20 @@
|
|
|
84
101
|
}
|
|
85
102
|
|
|
86
103
|
body {
|
|
87
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
|
|
104
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
|
|
88
105
|
background: var(--bg);
|
|
89
106
|
color: var(--text);
|
|
90
107
|
min-height: 100vh;
|
|
91
108
|
overflow: hidden;
|
|
109
|
+
-webkit-font-smoothing: antialiased;
|
|
110
|
+
-moz-osx-font-smoothing: grayscale;
|
|
92
111
|
}
|
|
93
112
|
|
|
94
113
|
/* ===== HEADER ===== */
|
|
95
114
|
.header {
|
|
96
|
-
background:
|
|
115
|
+
background: rgba(15, 19, 24, 0.8);
|
|
116
|
+
backdrop-filter: blur(16px) saturate(180%);
|
|
117
|
+
-webkit-backdrop-filter: blur(16px) saturate(180%);
|
|
97
118
|
border-bottom: 1px solid var(--border);
|
|
98
119
|
padding: 0 20px;
|
|
99
120
|
height: var(--header-h);
|
|
@@ -107,6 +128,10 @@
|
|
|
107
128
|
z-index: 100;
|
|
108
129
|
}
|
|
109
130
|
|
|
131
|
+
[data-theme="light"] .header {
|
|
132
|
+
background: rgba(255, 255, 255, 0.85);
|
|
133
|
+
}
|
|
134
|
+
|
|
110
135
|
.header-left {
|
|
111
136
|
display: flex;
|
|
112
137
|
align-items: center;
|
|
@@ -115,12 +140,13 @@
|
|
|
115
140
|
|
|
116
141
|
.logo {
|
|
117
142
|
font-size: 18px;
|
|
118
|
-
font-weight:
|
|
119
|
-
background:
|
|
143
|
+
font-weight: 800;
|
|
144
|
+
background: var(--gradient-accent);
|
|
120
145
|
-webkit-background-clip: text;
|
|
121
146
|
-webkit-text-fill-color: transparent;
|
|
122
147
|
background-clip: text;
|
|
123
148
|
white-space: nowrap;
|
|
149
|
+
letter-spacing: -0.3px;
|
|
124
150
|
}
|
|
125
151
|
|
|
126
152
|
.header-stats {
|
|
@@ -163,6 +189,7 @@
|
|
|
163
189
|
height: 6px;
|
|
164
190
|
border-radius: 50%;
|
|
165
191
|
background: var(--green);
|
|
192
|
+
box-shadow: 0 0 6px var(--green);
|
|
166
193
|
animation: pulse 2s infinite;
|
|
167
194
|
}
|
|
168
195
|
|
|
@@ -176,19 +203,21 @@
|
|
|
176
203
|
color: var(--text);
|
|
177
204
|
border: 1px solid var(--border);
|
|
178
205
|
padding: 6px 12px;
|
|
179
|
-
border-radius:
|
|
206
|
+
border-radius: 8px;
|
|
180
207
|
cursor: pointer;
|
|
181
208
|
font-size: 12px;
|
|
182
209
|
font-weight: 500;
|
|
183
|
-
transition: all 0.
|
|
210
|
+
transition: all 0.2s ease;
|
|
184
211
|
white-space: nowrap;
|
|
212
|
+
letter-spacing: 0.01em;
|
|
185
213
|
}
|
|
186
214
|
|
|
187
|
-
.btn:hover { border-color: var(--border-light); background: var(--surface-3); }
|
|
215
|
+
.btn:hover { border-color: var(--border-light); background: var(--surface-3); transform: translateY(-1px); box-shadow: var(--shadow-sm); }
|
|
216
|
+
.btn:active { transform: translateY(0); }
|
|
188
217
|
.btn-danger { color: var(--red); }
|
|
189
|
-
.btn-danger:hover { background: var(--red-dim); border-color: var(--red); }
|
|
218
|
+
.btn-danger:hover { background: var(--red-dim); border-color: var(--red); box-shadow: 0 0 12px rgba(248, 113, 113, 0.15); }
|
|
190
219
|
.btn-primary { color: var(--accent); }
|
|
191
|
-
.btn-primary:hover { background: var(--accent-dim); border-color: var(--accent); }
|
|
220
|
+
.btn-primary:hover { background: var(--accent-dim); border-color: var(--accent); box-shadow: 0 0 12px var(--accent-glow); }
|
|
192
221
|
|
|
193
222
|
.mobile-toggle {
|
|
194
223
|
display: none;
|
|
@@ -212,6 +241,7 @@
|
|
|
212
241
|
width: var(--sidebar-w);
|
|
213
242
|
min-width: var(--sidebar-w);
|
|
214
243
|
background: var(--surface);
|
|
244
|
+
background-image: var(--gradient-surface);
|
|
215
245
|
border-right: 1px solid var(--border);
|
|
216
246
|
display: flex;
|
|
217
247
|
flex-direction: column;
|
|
@@ -256,13 +286,16 @@
|
|
|
256
286
|
/* ===== AGENT CARDS ===== */
|
|
257
287
|
.agent-card {
|
|
258
288
|
background: var(--surface-2);
|
|
289
|
+
background-image: var(--gradient-surface);
|
|
259
290
|
border: 1px solid var(--border);
|
|
260
|
-
border-radius:
|
|
291
|
+
border-radius: 10px;
|
|
261
292
|
padding: 10px 12px;
|
|
262
293
|
margin-bottom: 6px;
|
|
263
|
-
transition: all 0.
|
|
294
|
+
transition: all 0.25s ease;
|
|
264
295
|
}
|
|
265
296
|
|
|
297
|
+
.agent-card:hover { border-color: var(--border-light); box-shadow: var(--shadow-sm); }
|
|
298
|
+
|
|
266
299
|
.agent-card.sleeping {
|
|
267
300
|
border-color: var(--orange);
|
|
268
301
|
border-left: 3px solid var(--orange);
|
|
@@ -360,7 +393,7 @@
|
|
|
360
393
|
display: inline-block;
|
|
361
394
|
}
|
|
362
395
|
|
|
363
|
-
.listen-dot.on { background: var(--green); box-shadow: 0 0
|
|
396
|
+
.listen-dot.on { background: var(--green); box-shadow: 0 0 8px var(--green); }
|
|
364
397
|
.listen-dot.off { background: var(--red); }
|
|
365
398
|
|
|
366
399
|
@keyframes pulseAlert {
|
|
@@ -428,15 +461,16 @@
|
|
|
428
461
|
background: var(--surface-2);
|
|
429
462
|
color: var(--text);
|
|
430
463
|
border: 1px solid var(--border);
|
|
431
|
-
border-radius:
|
|
464
|
+
border-radius: 8px;
|
|
432
465
|
padding: 7px 10px;
|
|
433
466
|
font-size: 12px;
|
|
434
467
|
cursor: pointer;
|
|
435
468
|
outline: none;
|
|
436
469
|
margin-bottom: 6px;
|
|
470
|
+
transition: all 0.2s ease;
|
|
437
471
|
}
|
|
438
472
|
|
|
439
|
-
.project-select:focus { border-color: var(--accent); }
|
|
473
|
+
.project-select:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-dim); }
|
|
440
474
|
|
|
441
475
|
.project-actions {
|
|
442
476
|
display: flex;
|
|
@@ -530,12 +564,12 @@
|
|
|
530
564
|
display: flex;
|
|
531
565
|
gap: 10px;
|
|
532
566
|
padding: 10px 14px;
|
|
533
|
-
border-radius:
|
|
534
|
-
transition:
|
|
567
|
+
border-radius: 10px;
|
|
568
|
+
transition: all 0.2s ease;
|
|
569
|
+
position: relative;
|
|
535
570
|
}
|
|
536
571
|
|
|
537
|
-
.message {
|
|
538
|
-
.message:hover { background: var(--surface); }
|
|
572
|
+
.message:hover { background: var(--surface); box-shadow: var(--glow); }
|
|
539
573
|
|
|
540
574
|
/* ===== REACTIONS ===== */
|
|
541
575
|
.msg-actions {
|
|
@@ -570,12 +604,14 @@
|
|
|
570
604
|
top: -4px;
|
|
571
605
|
right: 56px;
|
|
572
606
|
background: var(--surface-2);
|
|
573
|
-
|
|
574
|
-
|
|
607
|
+
backdrop-filter: blur(12px);
|
|
608
|
+
-webkit-backdrop-filter: blur(12px);
|
|
609
|
+
border: 1px solid var(--border-light);
|
|
610
|
+
border-radius: 10px;
|
|
575
611
|
padding: 4px;
|
|
576
612
|
gap: 2px;
|
|
577
613
|
z-index: 20;
|
|
578
|
-
box-shadow:
|
|
614
|
+
box-shadow: var(--shadow-lg);
|
|
579
615
|
}
|
|
580
616
|
|
|
581
617
|
.react-picker.open { display: flex; }
|
|
@@ -701,9 +737,10 @@
|
|
|
701
737
|
|
|
702
738
|
.badge {
|
|
703
739
|
font-size: 9px;
|
|
704
|
-
padding:
|
|
705
|
-
border-radius:
|
|
740
|
+
padding: 2px 6px;
|
|
741
|
+
border-radius: 6px;
|
|
706
742
|
font-weight: 600;
|
|
743
|
+
letter-spacing: 0.03em;
|
|
707
744
|
}
|
|
708
745
|
|
|
709
746
|
.badge-ack { background: var(--green-dim); color: var(--green); }
|
|
@@ -737,16 +774,17 @@
|
|
|
737
774
|
}
|
|
738
775
|
|
|
739
776
|
.msg-content pre {
|
|
740
|
-
background: var(--
|
|
777
|
+
background: var(--bg);
|
|
741
778
|
border: 1px solid var(--border);
|
|
742
|
-
border-radius:
|
|
743
|
-
padding:
|
|
744
|
-
margin:
|
|
779
|
+
border-radius: 8px;
|
|
780
|
+
padding: 14px 16px;
|
|
781
|
+
margin: 8px 0;
|
|
745
782
|
overflow-x: auto;
|
|
746
783
|
font-size: 12px;
|
|
747
|
-
line-height: 1.
|
|
784
|
+
line-height: 1.6;
|
|
748
785
|
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', monospace;
|
|
749
786
|
position: relative;
|
|
787
|
+
box-shadow: inset 0 1px 3px rgba(0,0,0,0.2);
|
|
750
788
|
}
|
|
751
789
|
|
|
752
790
|
.msg-content pre code {
|
|
@@ -816,7 +854,8 @@
|
|
|
816
854
|
.msg-input-bar {
|
|
817
855
|
border-top: 1px solid var(--border);
|
|
818
856
|
background: var(--surface);
|
|
819
|
-
|
|
857
|
+
background-image: var(--gradient-surface);
|
|
858
|
+
padding: 14px 20px;
|
|
820
859
|
display: flex;
|
|
821
860
|
gap: 8px;
|
|
822
861
|
align-items: flex-end;
|
|
@@ -840,14 +879,15 @@
|
|
|
840
879
|
background: var(--surface-2);
|
|
841
880
|
color: var(--text);
|
|
842
881
|
border: 1px solid var(--border);
|
|
843
|
-
border-radius:
|
|
882
|
+
border-radius: 8px;
|
|
844
883
|
padding: 7px 10px;
|
|
845
884
|
font-size: 12px;
|
|
846
885
|
cursor: pointer;
|
|
847
886
|
outline: none;
|
|
887
|
+
transition: all 0.2s ease;
|
|
848
888
|
}
|
|
849
889
|
|
|
850
|
-
.input-target select:focus { border-color: var(--accent); }
|
|
890
|
+
.input-target select:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-dim); }
|
|
851
891
|
|
|
852
892
|
.input-msg {
|
|
853
893
|
flex: 1;
|
|
@@ -867,7 +907,7 @@
|
|
|
867
907
|
background: var(--surface-2);
|
|
868
908
|
color: var(--text);
|
|
869
909
|
border: 1px solid var(--border);
|
|
870
|
-
border-radius:
|
|
910
|
+
border-radius: 8px;
|
|
871
911
|
padding: 7px 10px;
|
|
872
912
|
font-size: 12px;
|
|
873
913
|
font-family: inherit;
|
|
@@ -875,26 +915,30 @@
|
|
|
875
915
|
height: 34px;
|
|
876
916
|
max-height: 80px;
|
|
877
917
|
outline: none;
|
|
918
|
+
transition: all 0.2s ease;
|
|
878
919
|
}
|
|
879
920
|
|
|
880
|
-
.input-msg textarea:focus { border-color: var(--accent); }
|
|
921
|
+
.input-msg textarea:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-dim); }
|
|
881
922
|
|
|
882
923
|
.send-btn {
|
|
883
|
-
background: var(--accent);
|
|
924
|
+
background: var(--gradient-accent);
|
|
884
925
|
color: #fff;
|
|
885
926
|
border: none;
|
|
886
|
-
border-radius:
|
|
887
|
-
padding: 7px
|
|
927
|
+
border-radius: 8px;
|
|
928
|
+
padding: 7px 20px;
|
|
888
929
|
font-size: 12px;
|
|
889
930
|
font-weight: 600;
|
|
890
931
|
cursor: pointer;
|
|
891
932
|
white-space: nowrap;
|
|
892
|
-
transition:
|
|
933
|
+
transition: all 0.2s ease;
|
|
893
934
|
height: 34px;
|
|
935
|
+
box-shadow: 0 2px 8px var(--accent-glow);
|
|
936
|
+
letter-spacing: 0.02em;
|
|
894
937
|
}
|
|
895
938
|
|
|
896
|
-
.send-btn:hover {
|
|
897
|
-
.send-btn:
|
|
939
|
+
.send-btn:hover { transform: translateY(-1px); box-shadow: 0 4px 16px var(--accent-glow); }
|
|
940
|
+
.send-btn:active { transform: translateY(0); }
|
|
941
|
+
.send-btn:disabled { opacity: 0.4; cursor: not-allowed; transform: none; box-shadow: none; }
|
|
898
942
|
|
|
899
943
|
/* ===== EMPTY STATE ===== */
|
|
900
944
|
.empty-state {
|
|
@@ -912,25 +956,27 @@
|
|
|
912
956
|
.empty-sub { font-size: 12px; color: var(--text-muted); }
|
|
913
957
|
|
|
914
958
|
/* ===== MESSAGE FLASH ===== */
|
|
915
|
-
.message-new { animation: flashIn 0.
|
|
959
|
+
.message-new { animation: flashIn 0.8s ease-out; }
|
|
916
960
|
|
|
917
961
|
@keyframes flashIn {
|
|
918
|
-
|
|
919
|
-
|
|
962
|
+
0% { background: var(--accent-dim); box-shadow: inset 0 0 20px var(--accent-glow); }
|
|
963
|
+
100% { background: transparent; box-shadow: none; }
|
|
920
964
|
}
|
|
921
965
|
|
|
922
966
|
/* ===== SCROLLBAR ===== */
|
|
923
|
-
::-webkit-scrollbar { width:
|
|
967
|
+
::-webkit-scrollbar { width: 5px; }
|
|
924
968
|
::-webkit-scrollbar-track { background: transparent; }
|
|
925
|
-
::-webkit-scrollbar-thumb { background:
|
|
926
|
-
::-webkit-scrollbar-thumb:hover { background:
|
|
969
|
+
::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.08); border-radius: 4px; }
|
|
970
|
+
::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.15); }
|
|
971
|
+
[data-theme="light"] ::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.1); }
|
|
972
|
+
[data-theme="light"] ::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,0.2); }
|
|
927
973
|
|
|
928
974
|
/* ===== SCROLL TO BOTTOM ===== */
|
|
929
975
|
.scroll-bottom {
|
|
930
976
|
position: absolute;
|
|
931
977
|
bottom: 80px;
|
|
932
978
|
right: 24px;
|
|
933
|
-
background: var(--accent);
|
|
979
|
+
background: var(--gradient-accent);
|
|
934
980
|
color: #fff;
|
|
935
981
|
border: none;
|
|
936
982
|
border-radius: 50%;
|
|
@@ -941,13 +987,13 @@
|
|
|
941
987
|
justify-content: center;
|
|
942
988
|
cursor: pointer;
|
|
943
989
|
font-size: 16px;
|
|
944
|
-
box-shadow: 0
|
|
990
|
+
box-shadow: 0 4px 16px var(--accent-glow);
|
|
945
991
|
z-index: 10;
|
|
946
|
-
transition:
|
|
992
|
+
transition: all 0.25s ease;
|
|
947
993
|
}
|
|
948
994
|
|
|
949
995
|
.scroll-bottom.visible { display: flex; }
|
|
950
|
-
.scroll-bottom:hover {
|
|
996
|
+
.scroll-bottom:hover { transform: translateY(-2px); box-shadow: 0 6px 24px var(--accent-glow); }
|
|
951
997
|
|
|
952
998
|
.scroll-bottom .new-count {
|
|
953
999
|
position: absolute;
|
|
@@ -981,16 +1027,43 @@
|
|
|
981
1027
|
background: var(--surface-2);
|
|
982
1028
|
color: var(--text);
|
|
983
1029
|
border: 1px solid var(--border);
|
|
984
|
-
border-radius:
|
|
985
|
-
padding:
|
|
1030
|
+
border-radius: 8px;
|
|
1031
|
+
padding: 7px 12px;
|
|
986
1032
|
font-size: 12px;
|
|
987
1033
|
font-family: inherit;
|
|
988
1034
|
outline: none;
|
|
1035
|
+
transition: all 0.2s ease;
|
|
989
1036
|
}
|
|
990
1037
|
|
|
991
|
-
.search-input:focus { border-color: var(--accent); }
|
|
1038
|
+
.search-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-dim); }
|
|
992
1039
|
.search-input::placeholder { color: var(--text-muted); }
|
|
993
1040
|
|
|
1041
|
+
.compact-toggle {
|
|
1042
|
+
background: var(--surface-2);
|
|
1043
|
+
border: 1px solid var(--border);
|
|
1044
|
+
border-radius: 6px;
|
|
1045
|
+
color: var(--text-dim);
|
|
1046
|
+
font-size: 11px;
|
|
1047
|
+
padding: 4px 8px;
|
|
1048
|
+
cursor: pointer;
|
|
1049
|
+
white-space: nowrap;
|
|
1050
|
+
transition: background 0.15s, color 0.15s;
|
|
1051
|
+
}
|
|
1052
|
+
.compact-toggle:hover { background: var(--surface-3); color: var(--text); }
|
|
1053
|
+
.compact-toggle.active { background: var(--accent-dim); color: var(--accent); border-color: var(--accent); }
|
|
1054
|
+
|
|
1055
|
+
/* Compact mode styles */
|
|
1056
|
+
.messages-area.compact-mode { gap: 0; padding: 8px 12px; }
|
|
1057
|
+
.messages-area.compact-mode .message { padding: 3px 8px; gap: 6px; }
|
|
1058
|
+
.messages-area.compact-mode .msg-avatar,
|
|
1059
|
+
.messages-area.compact-mode .msg-avatar-img { display: none; }
|
|
1060
|
+
.messages-area.compact-mode .msg-header { margin-bottom: 0; display: inline; }
|
|
1061
|
+
.messages-area.compact-mode .msg-body { display: inline; }
|
|
1062
|
+
.messages-area.compact-mode .msg-from { font-size: 12px; }
|
|
1063
|
+
.messages-area.compact-mode .msg-time { font-size: 9px; }
|
|
1064
|
+
.messages-area.compact-mode .msg-content { display: inline; font-size: 12px; }
|
|
1065
|
+
.messages-area.compact-mode .msg-content p { display: inline; margin: 0; }
|
|
1066
|
+
|
|
994
1067
|
.search-count {
|
|
995
1068
|
font-size: 11px;
|
|
996
1069
|
color: var(--text-muted);
|
|
@@ -1076,6 +1149,7 @@
|
|
|
1076
1149
|
gap: 16px;
|
|
1077
1150
|
font-size: 10px;
|
|
1078
1151
|
color: var(--text-muted);
|
|
1152
|
+
letter-spacing: 0.02em;
|
|
1079
1153
|
}
|
|
1080
1154
|
|
|
1081
1155
|
.app-footer a { color: var(--text-dim); text-decoration: none; }
|
|
@@ -1177,17 +1251,18 @@
|
|
|
1177
1251
|
.view-tab {
|
|
1178
1252
|
flex: 1;
|
|
1179
1253
|
text-align: center;
|
|
1180
|
-
padding: 8px;
|
|
1254
|
+
padding: 10px 8px;
|
|
1181
1255
|
font-size: 12px;
|
|
1182
1256
|
font-weight: 600;
|
|
1183
|
-
color: var(--text-
|
|
1257
|
+
color: var(--text-muted);
|
|
1184
1258
|
cursor: pointer;
|
|
1185
1259
|
border-bottom: 2px solid transparent;
|
|
1186
|
-
transition: all 0.
|
|
1260
|
+
transition: all 0.2s ease;
|
|
1261
|
+
letter-spacing: 0.02em;
|
|
1187
1262
|
}
|
|
1188
1263
|
|
|
1189
|
-
.view-tab:hover { color: var(--text); }
|
|
1190
|
-
.view-tab.active { color: var(--accent); border-bottom-color: var(--accent); }
|
|
1264
|
+
.view-tab:hover { color: var(--text); background: rgba(255,255,255,0.02); }
|
|
1265
|
+
.view-tab.active { color: var(--accent); border-bottom-color: var(--accent); background: var(--accent-dim); }
|
|
1191
1266
|
|
|
1192
1267
|
/* ===== LAN BADGE ===== */
|
|
1193
1268
|
.lan-badge {
|
|
@@ -1231,13 +1306,14 @@
|
|
|
1231
1306
|
|
|
1232
1307
|
.phone-modal {
|
|
1233
1308
|
background: var(--surface);
|
|
1234
|
-
|
|
1235
|
-
border
|
|
1309
|
+
background-image: var(--gradient-surface);
|
|
1310
|
+
border: 1px solid var(--border-light);
|
|
1311
|
+
border-radius: 16px;
|
|
1236
1312
|
padding: 28px;
|
|
1237
1313
|
width: 360px;
|
|
1238
1314
|
max-width: 90vw;
|
|
1239
1315
|
text-align: center;
|
|
1240
|
-
box-shadow: 0
|
|
1316
|
+
box-shadow: var(--shadow-lg), 0 0 60px rgba(0,0,0,0.3);
|
|
1241
1317
|
position: relative;
|
|
1242
1318
|
}
|
|
1243
1319
|
|
|
@@ -1411,14 +1487,19 @@
|
|
|
1411
1487
|
|
|
1412
1488
|
.task-card {
|
|
1413
1489
|
background: var(--surface-2);
|
|
1490
|
+
background-image: var(--gradient-surface);
|
|
1414
1491
|
border: 1px solid var(--border);
|
|
1415
|
-
border-radius:
|
|
1492
|
+
border-radius: 8px;
|
|
1416
1493
|
padding: 10px;
|
|
1417
1494
|
margin-bottom: 6px;
|
|
1418
|
-
transition:
|
|
1495
|
+
transition: all 0.2s ease;
|
|
1419
1496
|
}
|
|
1420
1497
|
|
|
1421
|
-
.task-card:hover { border-color: var(--border-light); }
|
|
1498
|
+
.task-card:hover { border-color: var(--border-light); transform: translateY(-1px); box-shadow: var(--shadow-sm); }
|
|
1499
|
+
.task-card[draggable="true"] { cursor: grab; }
|
|
1500
|
+
.task-card[draggable="true"]:active { cursor: grabbing; }
|
|
1501
|
+
.task-card.dragging { opacity: 0.4; transform: scale(0.95); }
|
|
1502
|
+
.kanban-col.drag-over { background: var(--surface-2); border-radius: 8px; outline: 2px dashed var(--accent); outline-offset: -2px; transition: background 0.15s, outline 0.15s; }
|
|
1422
1503
|
|
|
1423
1504
|
.task-title {
|
|
1424
1505
|
font-size: 13px;
|
|
@@ -1945,11 +2026,14 @@
|
|
|
1945
2026
|
position: fixed;
|
|
1946
2027
|
z-index: 300;
|
|
1947
2028
|
background: var(--surface);
|
|
1948
|
-
|
|
1949
|
-
border
|
|
1950
|
-
|
|
2029
|
+
background-image: var(--gradient-surface);
|
|
2030
|
+
border: 1px solid var(--border-light);
|
|
2031
|
+
border-radius: 14px;
|
|
2032
|
+
padding: 18px;
|
|
1951
2033
|
width: 260px;
|
|
1952
|
-
box-shadow: 0
|
|
2034
|
+
box-shadow: var(--shadow-lg), 0 0 40px rgba(0,0,0,0.2);
|
|
2035
|
+
backdrop-filter: blur(8px);
|
|
2036
|
+
-webkit-backdrop-filter: blur(8px);
|
|
1953
2037
|
}
|
|
1954
2038
|
|
|
1955
2039
|
.profile-popup.open { display: block; }
|
|
@@ -2201,6 +2285,122 @@
|
|
|
2201
2285
|
|
|
2202
2286
|
.launch-area.visible { display: block; }
|
|
2203
2287
|
|
|
2288
|
+
/* ===== STATS VIEW ===== */
|
|
2289
|
+
.stats-area {
|
|
2290
|
+
flex: 1;
|
|
2291
|
+
overflow-y: auto;
|
|
2292
|
+
padding: 20px;
|
|
2293
|
+
display: none;
|
|
2294
|
+
}
|
|
2295
|
+
.stats-area.visible { display: block; }
|
|
2296
|
+
|
|
2297
|
+
.stats-grid {
|
|
2298
|
+
display: grid;
|
|
2299
|
+
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
2300
|
+
gap: 12px;
|
|
2301
|
+
margin-bottom: 20px;
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
.stat-card {
|
|
2305
|
+
background: var(--surface);
|
|
2306
|
+
border: 1px solid var(--border);
|
|
2307
|
+
border-radius: 8px;
|
|
2308
|
+
padding: 14px;
|
|
2309
|
+
}
|
|
2310
|
+
.stat-card-label {
|
|
2311
|
+
font-size: 10px;
|
|
2312
|
+
text-transform: uppercase;
|
|
2313
|
+
letter-spacing: 0.5px;
|
|
2314
|
+
color: var(--text-muted);
|
|
2315
|
+
margin-bottom: 4px;
|
|
2316
|
+
}
|
|
2317
|
+
.stat-card-value {
|
|
2318
|
+
font-size: 24px;
|
|
2319
|
+
font-weight: 700;
|
|
2320
|
+
color: var(--text);
|
|
2321
|
+
}
|
|
2322
|
+
.stat-card-sub {
|
|
2323
|
+
font-size: 11px;
|
|
2324
|
+
color: var(--text-dim);
|
|
2325
|
+
margin-top: 2px;
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2328
|
+
.stats-section {
|
|
2329
|
+
background: var(--surface);
|
|
2330
|
+
border: 1px solid var(--border);
|
|
2331
|
+
border-radius: 8px;
|
|
2332
|
+
padding: 16px;
|
|
2333
|
+
margin-bottom: 16px;
|
|
2334
|
+
}
|
|
2335
|
+
.stats-section-title {
|
|
2336
|
+
font-size: 12px;
|
|
2337
|
+
font-weight: 700;
|
|
2338
|
+
text-transform: uppercase;
|
|
2339
|
+
letter-spacing: 0.5px;
|
|
2340
|
+
color: var(--text-dim);
|
|
2341
|
+
margin-bottom: 12px;
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2344
|
+
.stats-bar-row {
|
|
2345
|
+
display: flex;
|
|
2346
|
+
align-items: center;
|
|
2347
|
+
gap: 8px;
|
|
2348
|
+
margin-bottom: 6px;
|
|
2349
|
+
}
|
|
2350
|
+
.stats-bar-label {
|
|
2351
|
+
font-size: 12px;
|
|
2352
|
+
color: var(--text);
|
|
2353
|
+
min-width: 80px;
|
|
2354
|
+
text-align: right;
|
|
2355
|
+
}
|
|
2356
|
+
.stats-bar-track {
|
|
2357
|
+
flex: 1;
|
|
2358
|
+
height: 18px;
|
|
2359
|
+
background: var(--surface-2);
|
|
2360
|
+
border-radius: 4px;
|
|
2361
|
+
overflow: hidden;
|
|
2362
|
+
}
|
|
2363
|
+
.stats-bar-fill {
|
|
2364
|
+
height: 100%;
|
|
2365
|
+
border-radius: 4px;
|
|
2366
|
+
transition: width 0.3s;
|
|
2367
|
+
display: flex;
|
|
2368
|
+
align-items: center;
|
|
2369
|
+
padding-left: 6px;
|
|
2370
|
+
font-size: 10px;
|
|
2371
|
+
font-weight: 600;
|
|
2372
|
+
color: #fff;
|
|
2373
|
+
white-space: nowrap;
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
.stats-hour-chart {
|
|
2377
|
+
display: flex;
|
|
2378
|
+
align-items: flex-end;
|
|
2379
|
+
gap: 2px;
|
|
2380
|
+
height: 80px;
|
|
2381
|
+
}
|
|
2382
|
+
.stats-hour-bar {
|
|
2383
|
+
flex: 1;
|
|
2384
|
+
background: var(--accent);
|
|
2385
|
+
border-radius: 2px 2px 0 0;
|
|
2386
|
+
min-width: 4px;
|
|
2387
|
+
opacity: 0.7;
|
|
2388
|
+
transition: opacity 0.15s;
|
|
2389
|
+
position: relative;
|
|
2390
|
+
}
|
|
2391
|
+
.stats-hour-bar:hover { opacity: 1; }
|
|
2392
|
+
.stats-hour-labels {
|
|
2393
|
+
display: flex;
|
|
2394
|
+
gap: 2px;
|
|
2395
|
+
margin-top: 4px;
|
|
2396
|
+
}
|
|
2397
|
+
.stats-hour-labels span {
|
|
2398
|
+
flex: 1;
|
|
2399
|
+
text-align: center;
|
|
2400
|
+
font-size: 8px;
|
|
2401
|
+
color: var(--text-muted);
|
|
2402
|
+
}
|
|
2403
|
+
|
|
2204
2404
|
.launch-panel {
|
|
2205
2405
|
max-width: 560px;
|
|
2206
2406
|
margin: 0 auto;
|
|
@@ -2459,6 +2659,7 @@
|
|
|
2459
2659
|
<div id="export-menu" style="display:none;position:absolute;right:0;top:100%;margin-top:4px;background:var(--surface-2);border:1px solid var(--border);border-radius:6px;overflow:hidden;z-index:200;min-width:160px">
|
|
2460
2660
|
<div style="padding:7px 12px;font-size:12px;cursor:pointer;transition:background 0.1s" onmouseover="this.style.background='var(--surface-3)'" onmouseout="this.style.background=''" onclick="exportShareableHTML();toggleExportMenu()">HTML (shareable)</div>
|
|
2461
2661
|
<div style="padding:7px 12px;font-size:12px;cursor:pointer;transition:background 0.1s" onmouseover="this.style.background='var(--surface-3)'" onmouseout="this.style.background=''" onclick="exportConversation();toggleExportMenu()">Markdown (.md)</div>
|
|
2662
|
+
<div style="padding:7px 12px;font-size:12px;cursor:pointer;transition:background 0.1s" onmouseover="this.style.background='var(--surface-3)'" onmouseout="this.style.background=''" onclick="exportJSON();toggleExportMenu()">JSON (.json)</div>
|
|
2462
2663
|
</div>
|
|
2463
2664
|
</div>
|
|
2464
2665
|
<button class="btn btn-danger" onclick="doReset()">Reset</button>
|
|
@@ -2479,6 +2680,21 @@
|
|
|
2479
2680
|
</div>
|
|
2480
2681
|
</div>
|
|
2481
2682
|
|
|
2683
|
+
<!-- v3.4: EDIT MESSAGE MODAL -->
|
|
2684
|
+
<div class="phone-modal-overlay" id="edit-modal-overlay" onclick="if(event.target===this)closeEditModal()">
|
|
2685
|
+
<div class="phone-modal" style="width:500px;text-align:left">
|
|
2686
|
+
<button class="phone-modal-close" onclick="closeEditModal()">×</button>
|
|
2687
|
+
<h3>Edit Message</h3>
|
|
2688
|
+
<input type="hidden" id="edit-msg-id">
|
|
2689
|
+
<textarea id="edit-msg-content" style="width:100%;min-height:120px;background:var(--surface-2);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:12px;font-size:13px;font-family:inherit;resize:vertical;outline:none;margin:12px 0 8px"></textarea>
|
|
2690
|
+
<div id="edit-history" style="max-height:150px;overflow-y:auto;margin-bottom:12px"></div>
|
|
2691
|
+
<div style="display:flex;gap:8px;justify-content:flex-end">
|
|
2692
|
+
<button class="btn" onclick="closeEditModal()">Cancel</button>
|
|
2693
|
+
<button class="btn btn-primary" onclick="saveEditMessage()" style="background:var(--accent);color:#fff;border-color:var(--accent)">Save Edit</button>
|
|
2694
|
+
</div>
|
|
2695
|
+
</div>
|
|
2696
|
+
</div>
|
|
2697
|
+
|
|
2482
2698
|
<!-- APP LAYOUT -->
|
|
2483
2699
|
<div class="app">
|
|
2484
2700
|
<div class="sidebar-overlay" id="sidebar-overlay" onclick="toggleSidebar()"></div>
|
|
@@ -2566,11 +2782,13 @@
|
|
|
2566
2782
|
<div class="view-tab" id="tab-workspaces" onclick="switchView('workspaces')">Workspaces</div>
|
|
2567
2783
|
<div class="view-tab" id="tab-workflows" onclick="switchView('workflows')">Workflows</div>
|
|
2568
2784
|
<div class="view-tab" id="tab-launch" onclick="switchView('launch')">Launch</div>
|
|
2785
|
+
<div class="view-tab" id="tab-stats" onclick="switchView('stats')">Stats</div>
|
|
2569
2786
|
</div>
|
|
2570
2787
|
<div class="branch-tabs" id="branch-tabs"></div>
|
|
2571
2788
|
<div class="search-bar" id="search-bar">
|
|
2572
2789
|
<input class="search-input" id="search-input" placeholder="Search messages... ( / )" oninput="onSearch()">
|
|
2573
2790
|
<span class="search-count" id="search-count"></span>
|
|
2791
|
+
<button class="compact-toggle" id="compact-toggle" onclick="toggleCompactMode()" title="Toggle compact view">Compact</button>
|
|
2574
2792
|
</div>
|
|
2575
2793
|
<div class="pinned-section" id="pinned-section">
|
|
2576
2794
|
<div class="pinned-header" onclick="togglePinnedSection()"><span>Pinned Messages</span><span class="pinned-toggle" id="pinned-toggle">Hide</span></div>
|
|
@@ -2581,6 +2799,7 @@
|
|
|
2581
2799
|
<div class="workspaces-area" id="workspaces-area"></div>
|
|
2582
2800
|
<div class="workflows-area" id="workflows-area"></div>
|
|
2583
2801
|
<div class="launch-area" id="launch-area"></div>
|
|
2802
|
+
<div class="stats-area" id="stats-area"></div>
|
|
2584
2803
|
<button class="scroll-bottom" id="scroll-bottom" onclick="scrollToBottom()">↓<span class="new-count" id="new-msg-count" style="display:none">0</span></button>
|
|
2585
2804
|
<div class="typing-bar" id="typing-bar"></div>
|
|
2586
2805
|
|
|
@@ -2602,7 +2821,7 @@
|
|
|
2602
2821
|
</div>
|
|
2603
2822
|
</div>
|
|
2604
2823
|
<div class="app-footer">
|
|
2605
|
-
<span>Let Them Talk v3.
|
|
2824
|
+
<span>Let Them Talk v3.4.0</span>
|
|
2606
2825
|
</div>
|
|
2607
2826
|
<div class="profile-popup" id="profile-popup" onclick="event.stopPropagation()">
|
|
2608
2827
|
<div class="profile-popup-header">
|
|
@@ -3205,6 +3424,7 @@ function renderMessages(messages) {
|
|
|
3205
3424
|
var badges = '';
|
|
3206
3425
|
if (m.acked) badges += '<span class="badge badge-ack">ACK</span>';
|
|
3207
3426
|
if (m.thread_id) badges += '<span class="badge badge-thread">Thread</span>';
|
|
3427
|
+
if (m.edited) badges += '<span class="badge" style="background:var(--orange-dim);color:var(--orange)" title="Edited ' + (m.edited_at ? new Date(m.edited_at).toLocaleString() : '') + '">edited</span>';
|
|
3208
3428
|
|
|
3209
3429
|
var msgAvatarHtml = getMsgAvatar(m.from, color);
|
|
3210
3430
|
|
|
@@ -3330,6 +3550,28 @@ function scrollToBottom() {
|
|
|
3330
3550
|
document.getElementById('new-msg-count').style.display = 'none';
|
|
3331
3551
|
}
|
|
3332
3552
|
|
|
3553
|
+
// ==================== COMPACT MODE ====================
|
|
3554
|
+
|
|
3555
|
+
var compactMode = localStorage.getItem('compactMode') === 'true';
|
|
3556
|
+
|
|
3557
|
+
function initCompactMode() {
|
|
3558
|
+
var area = document.getElementById('messages');
|
|
3559
|
+
var btn = document.getElementById('compact-toggle');
|
|
3560
|
+
if (compactMode) {
|
|
3561
|
+
area.classList.add('compact-mode');
|
|
3562
|
+
btn.classList.add('active');
|
|
3563
|
+
}
|
|
3564
|
+
}
|
|
3565
|
+
|
|
3566
|
+
function toggleCompactMode() {
|
|
3567
|
+
compactMode = !compactMode;
|
|
3568
|
+
localStorage.setItem('compactMode', compactMode);
|
|
3569
|
+
var area = document.getElementById('messages');
|
|
3570
|
+
var btn = document.getElementById('compact-toggle');
|
|
3571
|
+
area.classList.toggle('compact-mode', compactMode);
|
|
3572
|
+
btn.classList.toggle('active', compactMode);
|
|
3573
|
+
}
|
|
3574
|
+
|
|
3333
3575
|
// ==================== SEARCH ====================
|
|
3334
3576
|
|
|
3335
3577
|
var searchQuery = '';
|
|
@@ -3425,16 +3667,106 @@ function buildMsgActions(msgId) {
|
|
|
3425
3667
|
var starClass = isBookmarked ? ' active' : '';
|
|
3426
3668
|
var pinClass = isPinned ? ' active' : '';
|
|
3427
3669
|
|
|
3670
|
+
var msg = cachedHistory.find(function(m) { return m.id === msgId; });
|
|
3671
|
+
var isDeletable = msg && (msg.from === 'dashboard' || msg.from === 'Dashboard' || msg.from === 'system' || msg.from === '__system__');
|
|
3672
|
+
var deleteBtn = isDeletable ? '<button class="msg-action-btn" onclick="deleteMessage(\'' + msgId + '\')" title="Delete" style="color:var(--red,#e74c3c)">🗑️</button>' : '';
|
|
3673
|
+
|
|
3428
3674
|
return '<div class="msg-actions">' +
|
|
3429
3675
|
'<button class="msg-action-btn" onclick="toggleReactPicker(\'' + msgId + '\')" title="React">😀</button>' +
|
|
3430
3676
|
'<button class="msg-action-btn' + pinClass + '" onclick="togglePin(\'' + msgId + '\')" title="Pin">\ud83d\udccc</button>' +
|
|
3431
3677
|
'<button class="msg-action-btn' + starClass + '" onclick="toggleBookmark(\'' + msgId + '\')" title="Bookmark">' + starChar + '</button>' +
|
|
3678
|
+
'<button class="msg-action-btn" onclick="copyMessage(\'' + msgId + '\', this)" title="Copy">📋</button>' +
|
|
3679
|
+
'<button class="msg-action-btn" onclick="openEditMessage(\'' + msgId + '\')" title="Edit">✏️</button>' +
|
|
3680
|
+
deleteBtn +
|
|
3432
3681
|
'<div class="react-picker" id="react-' + msgId + '">' +
|
|
3433
3682
|
REACTION_EMOJIS.map(function(e) { return '<button class="react-emoji" onclick="addReaction(\'' + msgId + '\',\'' + e + '\')">' + e + '</button>'; }).join('') +
|
|
3434
3683
|
'</div>' +
|
|
3435
3684
|
'</div>';
|
|
3436
3685
|
}
|
|
3437
3686
|
|
|
3687
|
+
function copyMessage(msgId, btn) {
|
|
3688
|
+
var msg = cachedHistory.find(function(m) { return m.id === msgId; });
|
|
3689
|
+
if (!msg) return;
|
|
3690
|
+
navigator.clipboard.writeText(msg.content).then(function() {
|
|
3691
|
+
var orig = btn.innerHTML;
|
|
3692
|
+
btn.innerHTML = '✅';
|
|
3693
|
+
btn.title = 'Copied!';
|
|
3694
|
+
setTimeout(function() { btn.innerHTML = orig; btn.title = 'Copy'; }, 1500);
|
|
3695
|
+
});
|
|
3696
|
+
}
|
|
3697
|
+
|
|
3698
|
+
function deleteMessage(msgId) {
|
|
3699
|
+
var msg = cachedHistory.find(function(m) { return m.id === msgId; });
|
|
3700
|
+
if (!msg) return;
|
|
3701
|
+
var preview = msg.content.substring(0, 60) + (msg.content.length > 60 ? '...' : '');
|
|
3702
|
+
if (!confirm('Delete this message from ' + msg.from + '?\n\n"' + preview + '"')) return;
|
|
3703
|
+
var pq = activeProject ? '?project=' + encodeURIComponent(activeProject) : '';
|
|
3704
|
+
fetch('/api/message' + pq, {
|
|
3705
|
+
method: 'DELETE',
|
|
3706
|
+
headers: { 'Content-Type': 'application/json' },
|
|
3707
|
+
body: JSON.stringify({ id: msgId })
|
|
3708
|
+
}).then(function(r) { return r.json(); }).then(function(data) {
|
|
3709
|
+
if (data.error) {
|
|
3710
|
+
console.error('Delete failed: ' + data.error);
|
|
3711
|
+
} else {
|
|
3712
|
+
cachedHistory = cachedHistory.filter(function(m) { return m.id !== msgId; });
|
|
3713
|
+
lastMessageCount = 0;
|
|
3714
|
+
renderMessages(cachedHistory);
|
|
3715
|
+
}
|
|
3716
|
+
}).catch(function(err) {
|
|
3717
|
+
console.error('Delete failed: ' + err.message);
|
|
3718
|
+
});
|
|
3719
|
+
}
|
|
3720
|
+
|
|
3721
|
+
// ==================== v3.4: MESSAGE EDIT ====================
|
|
3722
|
+
|
|
3723
|
+
function openEditMessage(msgId) {
|
|
3724
|
+
var msg = cachedHistory.find(function(m) { return m.id === msgId; });
|
|
3725
|
+
if (!msg) return;
|
|
3726
|
+
var overlay = document.getElementById('edit-modal-overlay');
|
|
3727
|
+
document.getElementById('edit-msg-id').value = msgId;
|
|
3728
|
+
document.getElementById('edit-msg-content').value = msg.content;
|
|
3729
|
+
var historyEl = document.getElementById('edit-history');
|
|
3730
|
+
if (msg.edit_history && msg.edit_history.length > 0) {
|
|
3731
|
+
historyEl.innerHTML = '<div style="font-size:10px;color:var(--text-muted);margin-bottom:4px">Edit history (' + msg.edit_history.length + ' edits):</div>' +
|
|
3732
|
+
msg.edit_history.map(function(h) {
|
|
3733
|
+
return '<div style="font-size:11px;color:var(--text-dim);padding:4px 8px;background:var(--surface-2);border-radius:4px;margin-bottom:2px">' +
|
|
3734
|
+
'<span style="color:var(--text-muted)">' + new Date(h.edited_at).toLocaleTimeString() + '</span> ' +
|
|
3735
|
+
escapeHtml(h.content.substring(0, 100)) + (h.content.length > 100 ? '...' : '') + '</div>';
|
|
3736
|
+
}).join('');
|
|
3737
|
+
historyEl.style.display = 'block';
|
|
3738
|
+
} else {
|
|
3739
|
+
historyEl.style.display = 'none';
|
|
3740
|
+
}
|
|
3741
|
+
overlay.classList.add('open');
|
|
3742
|
+
document.getElementById('edit-msg-content').focus();
|
|
3743
|
+
}
|
|
3744
|
+
|
|
3745
|
+
function closeEditModal() {
|
|
3746
|
+
document.getElementById('edit-modal-overlay').classList.remove('open');
|
|
3747
|
+
}
|
|
3748
|
+
|
|
3749
|
+
function saveEditMessage() {
|
|
3750
|
+
var msgId = document.getElementById('edit-msg-id').value;
|
|
3751
|
+
var content = document.getElementById('edit-msg-content').value.trim();
|
|
3752
|
+
if (!content) return;
|
|
3753
|
+
var pq = activeProject ? '?project=' + encodeURIComponent(activeProject) : '';
|
|
3754
|
+
fetch('/api/message' + pq, {
|
|
3755
|
+
method: 'PUT',
|
|
3756
|
+
headers: { 'Content-Type': 'application/json' },
|
|
3757
|
+
body: JSON.stringify({ id: msgId, content: content })
|
|
3758
|
+
}).then(function(r) { return r.json(); }).then(function(data) {
|
|
3759
|
+
if (data.success) {
|
|
3760
|
+
closeEditModal();
|
|
3761
|
+
poll();
|
|
3762
|
+
} else {
|
|
3763
|
+
alert('Edit failed: ' + (data.error || 'Unknown error'));
|
|
3764
|
+
}
|
|
3765
|
+
}).catch(function(err) {
|
|
3766
|
+
alert('Edit failed: ' + err.message);
|
|
3767
|
+
});
|
|
3768
|
+
}
|
|
3769
|
+
|
|
3438
3770
|
function buildReactionsHtml(msgId) {
|
|
3439
3771
|
if (!reactions[msgId]) return '';
|
|
3440
3772
|
var emojis = Object.keys(reactions[msgId]);
|
|
@@ -3512,6 +3844,8 @@ document.addEventListener('keydown', function(e) {
|
|
|
3512
3844
|
if (e.key === '4') { switchView('workflows'); return; }
|
|
3513
3845
|
// 5 — Launch tab
|
|
3514
3846
|
if (e.key === '5') { switchView('launch'); return; }
|
|
3847
|
+
// 6 — Stats tab
|
|
3848
|
+
if (e.key === '6') { switchView('stats'); return; }
|
|
3515
3849
|
});
|
|
3516
3850
|
|
|
3517
3851
|
// ==================== RELATIVE TIMESTAMPS ====================
|
|
@@ -3531,7 +3865,7 @@ function relativeTime(ts) {
|
|
|
3531
3865
|
// ==================== COPY TO CLIPBOARD ====================
|
|
3532
3866
|
|
|
3533
3867
|
function copyText(el) {
|
|
3534
|
-
var text = el.getAttribute('data-text').replace(/"/g, '"');
|
|
3868
|
+
var text = el.getAttribute('data-text').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&');
|
|
3535
3869
|
navigator.clipboard.writeText(text).then(function() {
|
|
3536
3870
|
el.classList.add('copied');
|
|
3537
3871
|
el.querySelector('.copy-hint').textContent = 'copied!';
|
|
@@ -3740,6 +4074,21 @@ function exportConversation() {
|
|
|
3740
4074
|
URL.revokeObjectURL(url);
|
|
3741
4075
|
}
|
|
3742
4076
|
|
|
4077
|
+
function exportJSON() {
|
|
4078
|
+
if (!cachedHistory.length) return;
|
|
4079
|
+
var data = cachedHistory.map(function(m) {
|
|
4080
|
+
return { id: m.id, from: m.from, to: m.to, content: m.content, timestamp: m.timestamp, thread_id: m.thread_id || null };
|
|
4081
|
+
});
|
|
4082
|
+
var json = JSON.stringify(data, null, 2);
|
|
4083
|
+
var blob = new Blob([json], { type: 'application/json' });
|
|
4084
|
+
var url = URL.createObjectURL(blob);
|
|
4085
|
+
var a = document.createElement('a');
|
|
4086
|
+
a.href = url;
|
|
4087
|
+
a.download = 'conversation-' + new Date().toISOString().slice(0, 10) + '.json';
|
|
4088
|
+
a.click();
|
|
4089
|
+
URL.revokeObjectURL(url);
|
|
4090
|
+
}
|
|
4091
|
+
|
|
3743
4092
|
// ==================== VIEW SWITCHING ====================
|
|
3744
4093
|
|
|
3745
4094
|
var activeView = 'messages';
|
|
@@ -3751,16 +4100,19 @@ function switchView(view) {
|
|
|
3751
4100
|
document.getElementById('tab-workspaces').classList.toggle('active', view === 'workspaces');
|
|
3752
4101
|
document.getElementById('tab-workflows').classList.toggle('active', view === 'workflows');
|
|
3753
4102
|
document.getElementById('tab-launch').classList.toggle('active', view === 'launch');
|
|
4103
|
+
document.getElementById('tab-stats').classList.toggle('active', view === 'stats');
|
|
3754
4104
|
document.getElementById('messages').style.display = view === 'messages' ? 'flex' : 'none';
|
|
3755
4105
|
document.getElementById('tasks-area').classList.toggle('visible', view === 'tasks');
|
|
3756
4106
|
document.getElementById('workspaces-area').classList.toggle('visible', view === 'workspaces');
|
|
3757
4107
|
document.getElementById('workflows-area').classList.toggle('visible', view === 'workflows');
|
|
3758
4108
|
document.getElementById('launch-area').classList.toggle('visible', view === 'launch');
|
|
4109
|
+
document.getElementById('stats-area').classList.toggle('visible', view === 'stats');
|
|
3759
4110
|
document.getElementById('search-bar').style.display = view === 'messages' ? 'flex' : 'none';
|
|
3760
4111
|
if (view === 'tasks') fetchTasks();
|
|
3761
4112
|
if (view === 'workspaces') fetchWorkspaces();
|
|
3762
4113
|
if (view === 'workflows') fetchWorkflows();
|
|
3763
4114
|
if (view === 'launch') renderLaunchPanel();
|
|
4115
|
+
if (view === 'stats') fetchStats();
|
|
3764
4116
|
// Auto-close sidebar on mobile after view switch
|
|
3765
4117
|
if (isMobile) closeSidebar();
|
|
3766
4118
|
}
|
|
@@ -3824,7 +4176,7 @@ function renderTasks() {
|
|
|
3824
4176
|
for (var c = 0; c < cols.length; c++) {
|
|
3825
4177
|
var col = cols[c];
|
|
3826
4178
|
var tasks = groups[col.key] || [];
|
|
3827
|
-
html += '<div class="kanban-col">';
|
|
4179
|
+
html += '<div class="kanban-col" data-status="' + col.key + '" ondragover="onKanbanDragOver(event)" ondragleave="onKanbanDragLeave(event)" ondrop="onKanbanDrop(event)">';
|
|
3828
4180
|
html += '<div class="kanban-title ' + col.key + '">' + col.label + '<span class="kanban-count">' + tasks.length + '</span></div>';
|
|
3829
4181
|
for (var j = 0; j < tasks.length; j++) {
|
|
3830
4182
|
html += buildTaskCard(tasks[j]);
|
|
@@ -3850,7 +4202,7 @@ function buildTaskCard(t) {
|
|
|
3850
4202
|
'<option value="blocked"' + (t.status === 'blocked' ? ' selected' : '') + '>Blocked</option>' +
|
|
3851
4203
|
'</select>';
|
|
3852
4204
|
|
|
3853
|
-
return '<div class="task-card">' +
|
|
4205
|
+
return '<div class="task-card" draggable="true" data-task-id="' + t.id + '" ondragstart="onTaskDragStart(event)" ondragend="onTaskDragEnd(event)">' +
|
|
3854
4206
|
'<div class="task-title">' + escapeHtml(t.title || 'Untitled') + '</div>' +
|
|
3855
4207
|
(t.description ? '<div class="task-desc">' + escapeHtml(t.description) + '</div>' : '') +
|
|
3856
4208
|
'<div class="task-footer">' +
|
|
@@ -3871,6 +4223,135 @@ function updateTaskStatus(taskId, newStatus) {
|
|
|
3871
4223
|
}).catch(function(e) { console.error('Task update failed:', e); });
|
|
3872
4224
|
}
|
|
3873
4225
|
|
|
4226
|
+
// ==================== KANBAN DRAG-AND-DROP ====================
|
|
4227
|
+
|
|
4228
|
+
var draggedTaskId = null;
|
|
4229
|
+
|
|
4230
|
+
function onTaskDragStart(e) {
|
|
4231
|
+
draggedTaskId = e.target.getAttribute('data-task-id');
|
|
4232
|
+
e.dataTransfer.effectAllowed = 'move';
|
|
4233
|
+
e.dataTransfer.setData('text/plain', draggedTaskId);
|
|
4234
|
+
e.target.classList.add('dragging');
|
|
4235
|
+
}
|
|
4236
|
+
|
|
4237
|
+
function onTaskDragEnd(e) {
|
|
4238
|
+
e.target.classList.remove('dragging');
|
|
4239
|
+
draggedTaskId = null;
|
|
4240
|
+
var cols = document.querySelectorAll('.kanban-col');
|
|
4241
|
+
for (var i = 0; i < cols.length; i++) cols[i].classList.remove('drag-over');
|
|
4242
|
+
}
|
|
4243
|
+
|
|
4244
|
+
function onKanbanDragOver(e) {
|
|
4245
|
+
e.preventDefault();
|
|
4246
|
+
e.dataTransfer.dropEffect = 'move';
|
|
4247
|
+
var col = e.currentTarget;
|
|
4248
|
+
if (!col.classList.contains('drag-over')) col.classList.add('drag-over');
|
|
4249
|
+
}
|
|
4250
|
+
|
|
4251
|
+
function onKanbanDragLeave(e) {
|
|
4252
|
+
if (!e.currentTarget.contains(e.relatedTarget)) {
|
|
4253
|
+
e.currentTarget.classList.remove('drag-over');
|
|
4254
|
+
}
|
|
4255
|
+
}
|
|
4256
|
+
|
|
4257
|
+
function onKanbanDrop(e) {
|
|
4258
|
+
e.preventDefault();
|
|
4259
|
+
var col = e.currentTarget;
|
|
4260
|
+
col.classList.remove('drag-over');
|
|
4261
|
+
var taskId = e.dataTransfer.getData('text/plain');
|
|
4262
|
+
var newStatus = col.getAttribute('data-status');
|
|
4263
|
+
if (taskId && newStatus) {
|
|
4264
|
+
updateTaskStatus(taskId, newStatus);
|
|
4265
|
+
}
|
|
4266
|
+
}
|
|
4267
|
+
|
|
4268
|
+
// ==================== STATS VIEW ====================
|
|
4269
|
+
|
|
4270
|
+
var AGENT_COLORS = ['#58a6ff', '#f78166', '#7ee787', '#d2a8ff', '#ffa657', '#ff7b72', '#79c0ff', '#56d364'];
|
|
4271
|
+
|
|
4272
|
+
function fetchStats() {
|
|
4273
|
+
var pq = activeProject ? '?project=' + encodeURIComponent(activeProject) : '';
|
|
4274
|
+
fetch('/api/stats' + pq).then(function(r) { return r.json(); }).then(function(data) {
|
|
4275
|
+
renderStats(data);
|
|
4276
|
+
}).catch(function(e) { console.error('Stats fetch failed:', e); });
|
|
4277
|
+
}
|
|
4278
|
+
|
|
4279
|
+
function renderStats(data) {
|
|
4280
|
+
var el = document.getElementById('stats-area');
|
|
4281
|
+
if (!data || !data.total_messages) {
|
|
4282
|
+
el.innerHTML = '<div class="tasks-empty">No messages yet. Stats will appear once agents start talking.</div>';
|
|
4283
|
+
return;
|
|
4284
|
+
}
|
|
4285
|
+
|
|
4286
|
+
var agentNames = Object.keys(data.agents);
|
|
4287
|
+
var maxMessages = 0;
|
|
4288
|
+
for (var i = 0; i < agentNames.length; i++) {
|
|
4289
|
+
if (data.agents[agentNames[i]].messages > maxMessages) maxMessages = data.agents[agentNames[i]].messages;
|
|
4290
|
+
}
|
|
4291
|
+
|
|
4292
|
+
// Overview cards
|
|
4293
|
+
var html = '<div class="stats-grid">';
|
|
4294
|
+
html += '<div class="stat-card"><div class="stat-card-label">Total Messages</div><div class="stat-card-value">' + data.total_messages + '</div></div>';
|
|
4295
|
+
html += '<div class="stat-card"><div class="stat-card-label">Busiest Agent</div><div class="stat-card-value" style="font-size:18px">' + escapeHtml(data.busiest_agent || '-') + '</div></div>';
|
|
4296
|
+
html += '<div class="stat-card"><div class="stat-card-label">Velocity</div><div class="stat-card-value">' + data.velocity_per_min + '</div><div class="stat-card-sub">msgs/min (last 10m)</div></div>';
|
|
4297
|
+
html += '<div class="stat-card"><div class="stat-card-label">Active Agents</div><div class="stat-card-value">' + agentNames.length + '</div></div>';
|
|
4298
|
+
html += '</div>';
|
|
4299
|
+
|
|
4300
|
+
// Per-agent message bars
|
|
4301
|
+
html += '<div class="stats-section"><div class="stats-section-title">Messages per Agent</div>';
|
|
4302
|
+
for (var j = 0; j < agentNames.length; j++) {
|
|
4303
|
+
var name = agentNames[j];
|
|
4304
|
+
var agent = data.agents[name];
|
|
4305
|
+
var pct = maxMessages ? Math.round((agent.messages / maxMessages) * 100) : 0;
|
|
4306
|
+
var color = AGENT_COLORS[j % AGENT_COLORS.length];
|
|
4307
|
+
html += '<div class="stats-bar-row">';
|
|
4308
|
+
html += '<div class="stats-bar-label">' + escapeHtml(name) + '</div>';
|
|
4309
|
+
html += '<div class="stats-bar-track"><div class="stats-bar-fill" style="width:' + pct + '%;background:' + color + '">' + agent.messages + '</div></div>';
|
|
4310
|
+
html += '</div>';
|
|
4311
|
+
}
|
|
4312
|
+
html += '</div>';
|
|
4313
|
+
|
|
4314
|
+
// Per-agent response times
|
|
4315
|
+
var hasResponseTimes = false;
|
|
4316
|
+
for (var k = 0; k < agentNames.length; k++) {
|
|
4317
|
+
if (data.agents[agentNames[k]].avg_response_ms) { hasResponseTimes = true; break; }
|
|
4318
|
+
}
|
|
4319
|
+
if (hasResponseTimes) {
|
|
4320
|
+
html += '<div class="stats-section"><div class="stats-section-title">Avg Response Time</div>';
|
|
4321
|
+
for (var l = 0; l < agentNames.length; l++) {
|
|
4322
|
+
var aName = agentNames[l];
|
|
4323
|
+
var aData = data.agents[aName];
|
|
4324
|
+
if (!aData.avg_response_ms) continue;
|
|
4325
|
+
var secs = (aData.avg_response_ms / 1000).toFixed(1);
|
|
4326
|
+
var maxBar = 60000;
|
|
4327
|
+
var rtPct = Math.min(Math.round((aData.avg_response_ms / maxBar) * 100), 100);
|
|
4328
|
+
var rtColor = AGENT_COLORS[l % AGENT_COLORS.length];
|
|
4329
|
+
html += '<div class="stats-bar-row">';
|
|
4330
|
+
html += '<div class="stats-bar-label">' + escapeHtml(aName) + '</div>';
|
|
4331
|
+
html += '<div class="stats-bar-track"><div class="stats-bar-fill" style="width:' + rtPct + '%;background:' + rtColor + '">' + secs + 's</div></div>';
|
|
4332
|
+
html += '</div>';
|
|
4333
|
+
}
|
|
4334
|
+
html += '</div>';
|
|
4335
|
+
}
|
|
4336
|
+
|
|
4337
|
+
// Hourly distribution chart
|
|
4338
|
+
var maxHour = Math.max.apply(null, data.hour_distribution);
|
|
4339
|
+
html += '<div class="stats-section"><div class="stats-section-title">Activity by Hour</div>';
|
|
4340
|
+
html += '<div class="stats-hour-chart">';
|
|
4341
|
+
for (var h = 0; h < 24; h++) {
|
|
4342
|
+
var hPct = maxHour ? Math.round((data.hour_distribution[h] / maxHour) * 100) : 0;
|
|
4343
|
+
html += '<div class="stats-hour-bar" style="height:' + Math.max(hPct, 2) + '%" title="' + h + ':00 — ' + data.hour_distribution[h] + ' msgs"></div>';
|
|
4344
|
+
}
|
|
4345
|
+
html += '</div>';
|
|
4346
|
+
html += '<div class="stats-hour-labels">';
|
|
4347
|
+
for (var hh = 0; hh < 24; hh++) {
|
|
4348
|
+
html += '<span>' + (hh % 6 === 0 ? hh + 'h' : '') + '</span>';
|
|
4349
|
+
}
|
|
4350
|
+
html += '</div></div>';
|
|
4351
|
+
|
|
4352
|
+
el.innerHTML = html;
|
|
4353
|
+
}
|
|
4354
|
+
|
|
3874
4355
|
// ==================== EXPORT MENU ====================
|
|
3875
4356
|
|
|
3876
4357
|
function toggleExportMenu() {
|
|
@@ -4292,6 +4773,7 @@ function poll() {
|
|
|
4292
4773
|
if (activeView === 'tasks') fetchTasks();
|
|
4293
4774
|
if (activeView === 'workspaces') fetchWorkspaces();
|
|
4294
4775
|
if (activeView === 'workflows') fetchWorkflows();
|
|
4776
|
+
if (activeView === 'stats') fetchStats();
|
|
4295
4777
|
}).catch(function(e) {
|
|
4296
4778
|
console.error('Poll failed:', e);
|
|
4297
4779
|
document.getElementById('conn-detail').textContent = ' ERR: ' + e.message;
|
|
@@ -4300,7 +4782,7 @@ function poll() {
|
|
|
4300
4782
|
|
|
4301
4783
|
function doReset() {
|
|
4302
4784
|
if (!confirm('Clear all messages, agents, and history?')) return;
|
|
4303
|
-
fetch('/api/reset', { method: 'POST' }).then(function() {
|
|
4785
|
+
fetch('/api/reset' + projectParam(), { method: 'POST' }).then(function() {
|
|
4304
4786
|
lastMessageCount = 0;
|
|
4305
4787
|
activeThread = null;
|
|
4306
4788
|
cachedHistory = [];
|
|
@@ -4862,10 +5344,6 @@ function renderLaunchPanel() {
|
|
|
4862
5344
|
templateOpts += '<option value="' + escapeHtml(templates[i].name) + '">' + escapeHtml(templates[i].name) + ' — ' + escapeHtml(templates[i].description) + '</option>';
|
|
4863
5345
|
}
|
|
4864
5346
|
|
|
4865
|
-
var activeProject = '';
|
|
4866
|
-
var projSelect = document.getElementById('project-selector');
|
|
4867
|
-
if (projSelect && projSelect.value) activeProject = projSelect.value;
|
|
4868
|
-
|
|
4869
5347
|
el.innerHTML =
|
|
4870
5348
|
'<div class="launch-panel">' +
|
|
4871
5349
|
'<h3>Launch Agent Terminal</h3>' +
|
|
@@ -4899,11 +5377,16 @@ function renderLaunchPanel() {
|
|
|
4899
5377
|
'</div>' +
|
|
4900
5378
|
'<div class="launch-result" id="launch-result"></div>' +
|
|
4901
5379
|
'</div>';
|
|
5380
|
+
renderConversationTemplates(el);
|
|
4902
5381
|
}).catch(function() {
|
|
4903
5382
|
el.innerHTML = '<div class="launch-panel"><h3>Launch Agent Terminal</h3><p style="color:var(--text-dim)">Failed to load templates.</p></div>';
|
|
4904
5383
|
});
|
|
4905
5384
|
}
|
|
4906
5385
|
|
|
5386
|
+
// ==================== v3.4: CONVERSATION TEMPLATES ====================
|
|
5387
|
+
function renderConversationTemplates(p){fetch('/api/conversation-templates').then(function(r){return r.json()}).then(function(t){if(!t.length)return;var s=document.createElement('div');s.className='launch-panel';s.style.marginTop='20px';var h='<h3 style="margin-bottom:4px">Conversation Templates</h3><p style="font-size:12px;color:var(--text-dim);margin-bottom:16px">Pre-built multi-agent workflows. Click to see agent prompts.</p><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px">';for(var i=0;i<t.length;i++){var c=t[i];var n=c.agents.map(function(a){return a.name}).join(', ');h+='<div style="background:var(--surface-2);border:1px solid var(--border);border-radius:8px;padding:14px;cursor:pointer;transition:all 0.15s" onclick="showConvTemplate(\''+escapeHtml(c.id)+'\')" onmouseover="this.style.borderColor=\'var(--accent)\'" onmouseout="this.style.borderColor=\'var(--border)\'"><div style="font-weight:600;font-size:13px;margin-bottom:4px">'+escapeHtml(c.name)+'</div><div style="font-size:11px;color:var(--text-dim);margin-bottom:8px">'+escapeHtml(c.description)+'</div><div style="font-size:10px;color:var(--text-muted)">Agents: '+escapeHtml(n)+'</div>'+(c.workflow?'<div style="font-size:10px;color:var(--purple);margin-top:4px">Workflow: '+escapeHtml(c.workflow.steps.join(' \u2192 '))+'</div>':'')+'</div>'}h+='</div><div id="conv-template-detail" style="margin-top:16px"></div>';s.innerHTML=h;p.appendChild(s)}).catch(function(){})}
|
|
5388
|
+
function showConvTemplate(tid){var pq=activeProject?'?project='+encodeURIComponent(activeProject):'';fetch('/api/conversation-templates/launch'+pq,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({template_id:tid})}).then(function(r){return r.json()}).then(function(d){if(d.error){alert(d.error);return}var el=document.getElementById('conv-template-detail');var h='<div style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:16px"><div style="font-weight:700;font-size:15px;margin-bottom:4px">'+escapeHtml(d.template.name)+'</div><div style="font-size:12px;color:var(--text-dim);margin-bottom:12px">'+escapeHtml(d.template.description)+'</div>';if(d.template.workflow){h+='<div style="display:flex;gap:6px;align-items:center;margin-bottom:14px;flex-wrap:wrap">';var st=d.template.workflow.steps;for(var s=0;s<st.length;s++){if(s>0)h+='<span style="color:var(--text-muted);font-size:12px">\u2192</span>';h+='<span style="background:var(--purple-dim);color:var(--purple);padding:3px 10px;border-radius:12px;font-size:11px;font-weight:600">'+escapeHtml(st[s])+'</span>'}h+='</div>'}h+='<div style="font-weight:600;font-size:12px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:8px">Agent Prompts (copy each into a separate terminal)</div>';for(var i=0;i<d.instructions.length;i++){var inst=d.instructions[i];h+='<div style="margin-bottom:10px"><div style="display:flex;align-items:center;gap:8px;margin-bottom:4px"><span style="font-weight:600;font-size:13px">'+escapeHtml(inst.agent_name)+'</span><span class="role-badge">'+escapeHtml(inst.role)+'</span></div><div class="copy-block" onclick="copyText(this)" data-text="'+inst.prompt.replace(/&/g,'&').replace(/"/g,'"').replace(/</g,'<').replace(/>/g,'>')+'"><span class="copy-hint">click to copy</span><span style="font-size:11px;color:var(--text-dim)">'+escapeHtml(inst.prompt.substring(0,200))+(inst.prompt.length>200?'...':'')+'</span></div></div>'}h+='</div>';el.innerHTML=h;el.scrollIntoView({behavior:'smooth',block:'nearest'})})}
|
|
5389
|
+
|
|
4907
5390
|
function selectCli(cli) {
|
|
4908
5391
|
selectedCli = cli;
|
|
4909
5392
|
var btns = document.querySelectorAll('.cli-btn');
|
|
@@ -4980,6 +5463,27 @@ function copyLaunchPrompt() {
|
|
|
4980
5463
|
// ==================== SSE + POLLING ====================
|
|
4981
5464
|
|
|
4982
5465
|
var sseConnected = false;
|
|
5466
|
+
var sseRetryDelay = 1000;
|
|
5467
|
+
var sseRetryTimer = null;
|
|
5468
|
+
var ssePollFallback = null;
|
|
5469
|
+
|
|
5470
|
+
function setConnStatus(status) {
|
|
5471
|
+
var dot = document.querySelector('.conn-dot');
|
|
5472
|
+
var label = document.getElementById('conn-label');
|
|
5473
|
+
if (status === 'live') {
|
|
5474
|
+
dot.style.background = 'var(--green)';
|
|
5475
|
+
dot.style.animation = 'pulse 2s infinite';
|
|
5476
|
+
label.textContent = 'Live (SSE)';
|
|
5477
|
+
} else if (status === 'reconnecting') {
|
|
5478
|
+
dot.style.background = 'var(--yellow, #f0ad4e)';
|
|
5479
|
+
dot.style.animation = 'pulse 0.8s infinite';
|
|
5480
|
+
label.textContent = 'Reconnecting...';
|
|
5481
|
+
} else if (status === 'poll') {
|
|
5482
|
+
dot.style.background = 'var(--green)';
|
|
5483
|
+
dot.style.animation = 'pulse 2s infinite';
|
|
5484
|
+
label.textContent = 'Live (poll)';
|
|
5485
|
+
}
|
|
5486
|
+
}
|
|
4983
5487
|
|
|
4984
5488
|
function initSSE() {
|
|
4985
5489
|
try {
|
|
@@ -4991,20 +5495,29 @@ function initSSE() {
|
|
|
4991
5495
|
};
|
|
4992
5496
|
eventSource.onopen = function() {
|
|
4993
5497
|
sseConnected = true;
|
|
4994
|
-
|
|
5498
|
+
sseRetryDelay = 1000;
|
|
5499
|
+
if (sseRetryTimer) { clearTimeout(sseRetryTimer); sseRetryTimer = null; }
|
|
5500
|
+
if (ssePollFallback) { clearInterval(ssePollFallback); ssePollFallback = null; }
|
|
5501
|
+
setConnStatus('live');
|
|
4995
5502
|
};
|
|
4996
5503
|
eventSource.onerror = function() {
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
setInterval(poll, POLL_INTERVAL);
|
|
5504
|
+
sseConnected = false;
|
|
5505
|
+
eventSource.close();
|
|
5506
|
+
console.warn('SSE disconnected, retrying in ' + sseRetryDelay + 'ms');
|
|
5507
|
+
setConnStatus('reconnecting');
|
|
5508
|
+
if (!ssePollFallback) {
|
|
5509
|
+
ssePollFallback = setInterval(poll, POLL_INTERVAL);
|
|
5003
5510
|
}
|
|
5511
|
+
sseRetryTimer = setTimeout(function() {
|
|
5512
|
+
initSSE();
|
|
5513
|
+
}, sseRetryDelay);
|
|
5514
|
+
sseRetryDelay = Math.min(sseRetryDelay * 2, 30000);
|
|
5004
5515
|
};
|
|
5005
5516
|
} catch (e) {
|
|
5006
|
-
|
|
5007
|
-
|
|
5517
|
+
setConnStatus('poll');
|
|
5518
|
+
if (!ssePollFallback) {
|
|
5519
|
+
ssePollFallback = setInterval(poll, POLL_INTERVAL);
|
|
5520
|
+
}
|
|
5008
5521
|
}
|
|
5009
5522
|
}
|
|
5010
5523
|
|
|
@@ -5020,6 +5533,9 @@ function initSSE() {
|
|
|
5020
5533
|
}
|
|
5021
5534
|
})();
|
|
5022
5535
|
|
|
5536
|
+
// Init UI preferences
|
|
5537
|
+
initCompactMode();
|
|
5538
|
+
|
|
5023
5539
|
// Load projects first, then poll (so auto-select works before first data fetch)
|
|
5024
5540
|
loadProjects().then(function() {
|
|
5025
5541
|
console.log('[LTT] init: projects loaded, starting poll + SSE');
|