@wonanut/dsh-agent-lab 0.0.0-stage → 0.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.
@@ -0,0 +1,596 @@
1
+ /* ─────────────────────────────────────────────────────────────
2
+ * Agent 实验室 · UI kit
3
+ * Shared styles, icons and primitives for the page, terminal and inspector.
4
+ * This file is concatenated into the Client factory by build.mjs.
5
+ * ───────────────────────────────────────────────────────────── */
6
+
7
+ const AL_CSS = `
8
+ .al-root {
9
+ --al-bg: var(--dsw-alias-bg-base);
10
+ --al-layer: var(--dsw-alias-bg-layer-1);
11
+ --al-layer2: var(--dsw-alias-bg-layer-2);
12
+ --al-text: var(--dsw-alias-label-primary);
13
+ --al-sub: var(--dsw-alias-label-secondary);
14
+ --al-ter: var(--dsw-alias-label-tertiary, var(--dsw-alias-label-secondary));
15
+ --al-border: var(--dsw-alias-border-l1);
16
+ --al-border2: var(--dsw-alias-border-l2);
17
+ --al-brand: var(--dsw-alias-brand-primary);
18
+ --al-danger: var(--dsw-alias-state-error-primary);
19
+ --al-good: var(--dsw-alias-state-success-primary);
20
+ --al-warn: var(--dsw-alias-state-warn-primary);
21
+ --al-accent: #5b7cfa;
22
+ --al-accent-soft: color-mix(in srgb, var(--al-accent) 12%, transparent);
23
+ --al-accent-glow: color-mix(in srgb, var(--al-accent) 30%, transparent);
24
+ --al-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
25
+ position: relative;
26
+ display: flex;
27
+ flex-direction: column;
28
+ width: 100%;
29
+ height: 100%;
30
+ min-height: 100%;
31
+ overflow: hidden;
32
+ color: var(--al-text);
33
+ background:
34
+ radial-gradient(1200px 520px at 6% -10%, var(--al-accent-soft), transparent 60%),
35
+ radial-gradient(900px 480px at 100% 0%, color-mix(in srgb, var(--al-brand) 6%, transparent), transparent 55%),
36
+ var(--al-bg);
37
+ font-size: 14px;
38
+ line-height: 1.6;
39
+ -webkit-font-smoothing: antialiased;
40
+ }
41
+ .al-root, .al-root * { box-sizing: border-box; }
42
+ .al-root :where(button, input, textarea, select) { font: inherit; color: inherit; }
43
+ .al-root button { cursor: pointer; }
44
+ .al-root ::selection { background: var(--al-accent-glow); }
45
+ .al-root ::-webkit-scrollbar { width: 10px; height: 10px; }
46
+ .al-root ::-webkit-scrollbar-thumb { background: var(--al-border2); border: 3px solid transparent; border-radius: 999px; background-clip: content-box; }
47
+ .al-root ::-webkit-scrollbar-thumb:hover { background: var(--al-ter); background-clip: content-box; }
48
+ .al-root ::-webkit-scrollbar-track { background: transparent; }
49
+
50
+ .al-inner {
51
+ width: min(1480px, 100%);
52
+ margin: 0 auto;
53
+ padding: calc(var(--dsh-frame-top-clearance, 0px) + 22px) 28px 20px;
54
+ display: flex;
55
+ flex-direction: column;
56
+ gap: 14px;
57
+ flex: 1;
58
+ min-height: 0;
59
+ }
60
+
61
+ /* ── header ─────────────────────────────────────────────── */
62
+ .al-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; flex: none; }
63
+ .al-brand { display: flex; align-items: center; gap: 13px; min-width: 0; }
64
+ .al-logo {
65
+ width: 46px; height: 46px; flex: none; border-radius: 14px;
66
+ display: grid; place-items: center; color: #fff;
67
+ background: linear-gradient(145deg, var(--al-accent), color-mix(in srgb, var(--al-accent) 34%, #0d1020));
68
+ box-shadow: 0 10px 24px -12px var(--al-accent-glow), inset 0 1px 0 rgba(255, 255, 255, .28);
69
+ position: relative; overflow: hidden;
70
+ }
71
+ .al-logo::after {
72
+ content: ''; position: absolute; inset: -45% -12% auto auto; width: 74%; height: 92%;
73
+ background: radial-gradient(circle, rgba(255, 255, 255, .42), transparent 66%);
74
+ transform: rotate(18deg);
75
+ }
76
+ .al-logo svg { position: relative; z-index: 1; }
77
+ .al-title-row { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
78
+ .al-title { margin: 0; font-size: 21px; line-height: 1.25; font-weight: 680; letter-spacing: -.02em; }
79
+ .al-sub { margin: 3px 0 0; color: var(--al-sub); font-size: 12.5px; max-width: 760px; }
80
+ .al-head-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; justify-content: flex-end; flex: none; }
81
+
82
+ /* ── buttons ────────────────────────────────────────────── */
83
+ .al-btn {
84
+ display: inline-flex; align-items: center; justify-content: center; gap: 6px;
85
+ height: 34px; padding: 0 13px; border-radius: 10px;
86
+ border: 1px solid transparent; background: transparent; color: var(--al-text);
87
+ font-size: 12.5px; font-weight: 560; white-space: nowrap; user-select: none;
88
+ transition: transform .15s ease, background .15s ease, border-color .15s ease, box-shadow .15s ease, opacity .15s ease, color .15s ease;
89
+ }
90
+ .al-btn:hover { transform: translateY(-1px); }
91
+ .al-btn:active { transform: translateY(0); }
92
+ .al-btn:disabled { opacity: .42; cursor: not-allowed; transform: none; }
93
+ .al-btn-sm { height: 28px; padding: 0 9px; font-size: 12px; border-radius: 8px; gap: 4px; }
94
+ .al-btn-primary {
95
+ color: #fff !important;
96
+ background: linear-gradient(135deg, var(--al-accent), color-mix(in srgb, var(--al-accent) 78%, #101322));
97
+ box-shadow: 0 8px 18px -10px var(--al-accent-glow), inset 0 1px 0 rgba(255, 255, 255, .22);
98
+ }
99
+ .al-btn-primary span, .al-btn-primary svg { color: #fff !important; }
100
+ .al-btn-primary:hover { box-shadow: 0 12px 22px -10px var(--al-accent-glow), inset 0 1px 0 rgba(255, 255, 255, .28); }
101
+ .al-btn-soft { color: var(--al-accent); background: var(--al-accent-soft); }
102
+ .al-btn-soft:hover { background: color-mix(in srgb, var(--al-accent) 18%, transparent); }
103
+ .al-btn-ghost { color: var(--al-sub); border-color: var(--al-border); background: var(--al-layer); }
104
+ .al-btn-ghost:hover { color: var(--al-text); border-color: var(--al-border2); background: var(--al-layer2); }
105
+ .al-btn-danger { color: var(--al-danger); border-color: color-mix(in srgb, var(--al-danger) 26%, transparent); background: transparent; }
106
+ .al-btn-danger:hover { background: color-mix(in srgb, var(--al-danger) 9%, transparent); }
107
+ .al-iconbtn {
108
+ width: 28px; height: 28px; display: inline-grid; place-items: center; flex: none;
109
+ border: 1px solid transparent; background: transparent; border-radius: 8px; color: var(--al-ter);
110
+ transition: background .15s ease, color .15s ease, border-color .15s ease, opacity .15s ease;
111
+ }
112
+ .al-iconbtn:hover { background: var(--al-layer2); color: var(--al-text); border-color: var(--al-border); }
113
+ .al-iconbtn.danger:hover { color: var(--al-danger); background: color-mix(in srgb, var(--al-danger) 10%, transparent); border-color: color-mix(in srgb, var(--al-danger) 22%, transparent); }
114
+ .al-iconbtn:disabled { opacity: .35; cursor: not-allowed; }
115
+
116
+ /* ── chips ──────────────────────────────────────────────── */
117
+ .al-chip {
118
+ display: inline-flex; align-items: center; gap: 4px;
119
+ height: 23px; padding: 0 8px; border-radius: 999px;
120
+ border: 1px solid var(--al-border); background: color-mix(in srgb, var(--al-layer) 70%, transparent);
121
+ color: var(--al-sub); font-size: 11.5px; font-weight: 520; white-space: nowrap;
122
+ }
123
+ .al-chip svg { flex: none; }
124
+ .al-chip-brand { color: var(--al-accent); border-color: color-mix(in srgb, var(--al-accent) 24%, transparent); background: var(--al-accent-soft); }
125
+ .al-chip-good { color: var(--al-good); border-color: color-mix(in srgb, var(--al-good) 26%, transparent); background: color-mix(in srgb, var(--al-good) 10%, transparent); }
126
+ .al-chip-warn { color: var(--al-warn); border-color: color-mix(in srgb, var(--al-warn) 26%, transparent); background: color-mix(in srgb, var(--al-warn) 10%, transparent); }
127
+ .al-chip-danger { color: var(--al-danger); border-color: color-mix(in srgb, var(--al-danger) 26%, transparent); background: color-mix(in srgb, var(--al-danger) 9%, transparent); }
128
+ .al-chip-violet { color: #a78bfa; border-color: rgba(167, 139, 250, .3); background: rgba(167, 139, 250, .1); }
129
+
130
+ /* ── model mode switch (page header) ─────────────────────── */
131
+ .al-model-switch {
132
+ display: inline-flex; align-items: center; gap: 2px; padding: 3px;
133
+ border: 1px solid var(--al-border); border-radius: 10px;
134
+ background: color-mix(in srgb, var(--al-layer) 82%, transparent);
135
+ box-shadow: 0 1px 0 rgba(255, 255, 255, .04) inset;
136
+ }
137
+ .al-model-switch .al-ms-label {
138
+ padding: 0 5px; font-size: 10px; color: var(--al-ter); white-space: nowrap; letter-spacing: .02em;
139
+ }
140
+ .al-model-switch button {
141
+ height: 26px; padding: 0 9px; border: 0; border-radius: 7px; background: transparent;
142
+ color: var(--al-sub); font-size: 11.5px; font-weight: 560; white-space: nowrap;
143
+ transition: background .15s ease, color .15s ease;
144
+ }
145
+ .al-model-switch button:hover { background: var(--al-layer2); color: var(--al-text); }
146
+ .al-model-switch button.on { background: var(--al-accent-soft); color: var(--al-accent); }
147
+ .al-model-switch button.real.on { background: rgba(124, 58, 237, .13); color: #7c3aed; }
148
+ .al-model-switch button:disabled { opacity: .45; cursor: not-allowed; }
149
+ @media (max-width: 720px) {
150
+ .al-model-switch .al-ms-label { display: none; }
151
+ }
152
+
153
+ /* ── tabs ───────────────────────────────────────────────── */
154
+ .al-tabs {
155
+ display: flex; align-items: center; gap: 4px; flex: none;
156
+ padding: 4px; border: 1px solid var(--al-border); border-radius: 12px;
157
+ background: color-mix(in srgb, var(--al-layer) 72%, transparent);
158
+ backdrop-filter: blur(10px); width: fit-content; max-width: 100%;
159
+ overflow-x: auto; scrollbar-width: none;
160
+ }
161
+ .al-tabs::-webkit-scrollbar { display: none; }
162
+ .al-tab {
163
+ display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px;
164
+ border: 0; border-radius: 8px; background: transparent; color: var(--al-sub);
165
+ font-size: 12.5px; font-weight: 560; white-space: nowrap;
166
+ transition: background .15s ease, color .15s ease, box-shadow .15s ease;
167
+ }
168
+ .al-tab:hover { color: var(--al-text); background: color-mix(in srgb, var(--al-layer2) 70%, transparent); }
169
+ .al-tab.on { color: var(--al-text); background: var(--al-layer); box-shadow: 0 1px 3px rgba(15, 23, 42, .1), 0 0 0 1px var(--al-border); }
170
+ .al-tab.on svg { color: var(--al-accent); }
171
+ .al-tab-badge {
172
+ min-width: 16px; height: 16px; padding: 0 4px; border-radius: 999px;
173
+ display: inline-grid; place-items: center; font-size: 10px; font-weight: 650;
174
+ background: var(--al-layer2); color: var(--al-ter);
175
+ }
176
+ .al-tab.on .al-tab-badge { background: var(--al-accent-soft); color: var(--al-accent); }
177
+
178
+ /* ── tab panes ──────────────────────────────────────────── */
179
+ .al-tabpane { flex: 1; min-height: 0; display: flex; flex-direction: column; }
180
+ .al-tabpane.scroll { overflow-y: auto; overflow-x: hidden; display: block; padding-bottom: 10px; }
181
+
182
+ /* ── experience layout ──────────────────────────────────── */
183
+ .al-lab {
184
+ flex: 1; min-height: 0; display: grid;
185
+ grid-template-columns: minmax(206px, 232px) minmax(0, 1fr) minmax(318px, 352px);
186
+ grid-template-areas: "sessions tui inspector";
187
+ gap: 14px;
188
+ }
189
+
190
+ .al-lab.collapsed { grid-template-columns: 40px minmax(0, 1fr) minmax(318px, 352px); }
191
+ .al-sessions-rail {
192
+ grid-area: sessions; min-height: 0; width: 100%;
193
+ display: flex; flex-direction: column; align-items: center; gap: 8px;
194
+ padding: 10px 0; border: 1px solid var(--al-border); border-radius: 15px;
195
+ background: color-mix(in srgb, var(--al-layer) 86%, transparent);
196
+ color: var(--al-ter); cursor: pointer;
197
+ transition: background .15s ease, color .15s ease, border-color .15s ease;
198
+ }
199
+ .al-sessions-rail:hover { color: var(--al-accent); border-color: color-mix(in srgb, var(--al-accent) 28%, transparent); background: var(--al-accent-soft); }
200
+ .al-sessions-rail .al-rail-text {
201
+ writing-mode: vertical-rl; letter-spacing: .22em; font-size: 10.5px;
202
+ font-weight: 560; color: inherit; margin-top: 2px;
203
+ }
204
+ .al-sessions-rail .al-rail-icon { display: grid; place-items: center; width: 26px; height: 26px; border-radius: 8px; background: var(--al-layer2); color: var(--al-sub); }
205
+ .al-sessions-rail:hover .al-rail-icon { background: var(--al-layer); color: var(--al-accent); }
206
+ @media (max-width: 1180px) {
207
+ .al-lab.collapsed { grid-template-columns: minmax(0, 1fr) !important; grid-template-areas: "sessions" "tui" "inspector" !important; }
208
+ .al-sessions-rail { flex-direction: row; justify-content: center; height: 38px; padding: 0 12px; }
209
+ .al-sessions-rail .al-rail-text { writing-mode: horizontal-tb; letter-spacing: .08em; margin-top: 0; }
210
+ }
211
+
212
+ /* ── panels ─────────────────────────────────────────────── */
213
+ .al-panel {
214
+ display: flex; flex-direction: column; min-height: 0; min-width: 0;
215
+ border: 1px solid var(--al-border); border-radius: 15px;
216
+ background: color-mix(in srgb, var(--al-layer) 86%, transparent);
217
+ box-shadow: 0 12px 34px -28px rgba(15, 23, 42, .32);
218
+ overflow: hidden;
219
+ }
220
+ .al-panel-head {
221
+ display: flex; align-items: center; justify-content: space-between; gap: 8px;
222
+ min-height: 42px; padding: 7px 10px 7px 13px; border-bottom: 1px solid var(--al-border);
223
+ flex: none; background: color-mix(in srgb, var(--al-layer) 55%, transparent);
224
+ }
225
+ .al-panel-title { display: flex; align-items: center; gap: 7px; font-size: 12.5px; font-weight: 640; letter-spacing: -.01em; min-width: 0; }
226
+ .al-panel-title svg { color: var(--al-ter); flex: none; }
227
+ .al-panel-sub { font-size: 11px; color: var(--al-ter); font-weight: 450; }
228
+ .al-panel-body { flex: 1; min-height: 0; overflow-y: auto; }
229
+ .al-panel-foot {
230
+ flex: none; display: flex; align-items: center; justify-content: space-between; gap: 8px;
231
+ padding: 8px 12px; border-top: 1px solid var(--al-border);
232
+ font-size: 11px; color: var(--al-ter); background: color-mix(in srgb, var(--al-layer) 55%, transparent);
233
+ }
234
+
235
+ /* ── sessions ───────────────────────────────────────────── */
236
+ .al-sessions-panel { grid-area: sessions; }
237
+ .al-session-list { padding: 4px 6px 6px; }
238
+ .al-session {
239
+ position: relative; width: 100%; display: flex; flex-direction: column; align-items: stretch; gap: 3px;
240
+ margin: 3px 0; padding: 8px 9px; border-radius: 10px;
241
+ border: 1px solid transparent; background: transparent; text-align: left;
242
+ transition: background .15s ease, border-color .15s ease;
243
+ }
244
+ .al-session:hover { background: var(--al-layer2); }
245
+ .al-session.on { background: var(--al-accent-soft); border-color: color-mix(in srgb, var(--al-accent) 30%, transparent); }
246
+ .al-session.on::before {
247
+ content: ''; position: absolute; left: -6px; top: 9px; bottom: 9px; width: 3px;
248
+ border-radius: 999px; background: var(--al-accent);
249
+ }
250
+ .al-session-top { display: flex; align-items: flex-start; gap: 6px; }
251
+ .al-session-title {
252
+ flex: 1; min-width: 0; font-size: 12.5px; font-weight: 560; line-height: 1.4;
253
+ display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; word-break: break-word;
254
+ }
255
+ .al-session.on .al-session-title { color: var(--al-text); }
256
+ .al-session-meta { display: flex; align-items: center; gap: 7px; font-size: 10.5px; color: var(--al-ter); }
257
+ .al-session-del { opacity: 0; transition: opacity .15s ease; }
258
+ .al-session:hover .al-session-del, .al-session:focus-within .al-session-del { opacity: 1; }
259
+ .al-session-del.confirm { opacity: 1; color: var(--al-danger); }
260
+
261
+ /* ── common list rows / stats ───────────────────────────── */
262
+ .al-empty {
263
+ display: flex; flex-direction: column; align-items: center; justify-content: center;
264
+ gap: 8px; padding: 26px 20px; text-align: center; color: var(--al-sub);
265
+ }
266
+ .al-empty-icon {
267
+ width: 40px; height: 40px; border-radius: 12px; display: grid; place-items: center;
268
+ background: var(--al-accent-soft); color: var(--al-accent); margin-bottom: 2px;
269
+ }
270
+ .al-empty-title { font-size: 13px; font-weight: 620; color: var(--al-text); }
271
+ .al-empty-body { font-size: 11.5px; line-height: 1.65; max-width: 250px; color: var(--al-sub); }
272
+ .al-stat-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; padding: 8px 10px; }
273
+ .al-stat {
274
+ display: flex; flex-direction: column; gap: 1px; padding: 6px 8px; border-radius: 8px;
275
+ background: color-mix(in srgb, var(--al-layer2) 66%, transparent);
276
+ }
277
+ .al-stat b { font-size: 14px; font-weight: 660; font-variant-numeric: tabular-nums; line-height: 1.2; }
278
+ .al-stat span { font-size: 10px; color: var(--al-ter); }
279
+
280
+ /* ── json / code ────────────────────────────────────────── */
281
+ .al-json {
282
+ margin: 0; padding: 9px 10px; border-radius: 9px; overflow: auto;
283
+ background: color-mix(in srgb, var(--al-layer2) 80%, transparent);
284
+ border: 1px solid var(--al-border);
285
+ font-family: var(--al-mono); font-size: 11px; line-height: 1.55;
286
+ color: var(--al-text); white-space: pre; tab-size: 2; max-height: 260px;
287
+ }
288
+ .al-json-key { color: #8b5cf6; }
289
+ .al-json-str { color: #059669; }
290
+ .al-json-num { color: #2563eb; }
291
+ .al-json-bool { color: #d97706; }
292
+ .al-json-null { color: var(--al-ter); }
293
+ .al-code-inline {
294
+ font-family: var(--al-mono); font-size: 12px; padding: 1px 5px; border-radius: 5px;
295
+ background: var(--al-layer2); border: 1px solid var(--al-border); white-space: nowrap;
296
+ }
297
+ .al-kbd {
298
+ display: inline-flex; align-items: center; height: 19px; padding: 0 5px; border-radius: 5px;
299
+ border: 1px solid var(--al-border2); background: var(--al-layer); box-shadow: 0 1px 0 var(--al-border2);
300
+ font-family: var(--al-mono); font-size: 10px; color: var(--al-sub); white-space: nowrap;
301
+ }
302
+
303
+ /* ── token bar ──────────────────────────────────────────── */
304
+ .al-tokenbar { display: flex; width: 100%; border-radius: 999px; overflow: hidden; background: var(--al-layer2); }
305
+ .al-tokenbar > span { display: block; height: 100%; transition: width .3s ease; }
306
+ .al-tokenbar-legend { display: flex; flex-wrap: wrap; gap: 4px 10px; margin-top: 7px; }
307
+ .al-legend-item { display: inline-flex; align-items: center; gap: 4px; font-size: 10.5px; color: var(--al-ter); }
308
+ .al-legend-dot { width: 7px; height: 7px; border-radius: 2px; flex: none; }
309
+
310
+ /* ── toast ──────────────────────────────────────────────── */
311
+ .al-toast {
312
+ position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%);
313
+ z-index: 40; display: flex; align-items: center; gap: 7px;
314
+ padding: 9px 14px; border-radius: 10px;
315
+ background: var(--al-text); color: var(--al-bg);
316
+ font-size: 12px; font-weight: 540;
317
+ box-shadow: 0 14px 34px -12px rgba(15, 23, 42, .45);
318
+ animation: al-toast-in .18s ease;
319
+ }
320
+ .al-toast svg { color: inherit; }
321
+ @keyframes al-toast-in { from { opacity: 0; transform: translate(-50%, 6px); } to { opacity: 1; transform: translate(-50%, 0); } }
322
+
323
+ /* ── responsive ─────────────────────────────────────────── */
324
+ @media (max-width: 1240px) {
325
+ .al-inner { padding-left: 20px; padding-right: 20px; }
326
+ }
327
+ @media (max-width: 1180px) {
328
+ .al-root { overflow-y: auto; }
329
+ .al-inner { height: auto; min-height: 100%; }
330
+ .al-lab {
331
+ height: auto !important;
332
+ grid-template-columns: minmax(0, 1fr) !important;
333
+ grid-template-areas: "sessions" "tui" "inspector";
334
+ }
335
+ .al-session-list { display: flex; gap: 6px; overflow-x: auto; padding: 8px 10px 10px; }
336
+ .al-session { min-width: 190px; max-width: 260px; margin: 0; flex: none; }
337
+ .al-session.on::before { left: 9px; right: 9px; top: auto; bottom: 0; width: auto; height: 2px; }
338
+ .al-inspector-panel { height: 560px; }
339
+ .al-tabpane { overflow: visible; flex: none; }
340
+ .al-tabpane.scroll { overflow: visible; }
341
+ }
342
+ @media (max-width: 720px) {
343
+ .al-inner { padding: calc(var(--dsh-frame-top-clearance, 0px) + 14px) 12px 16px; gap: 10px; }
344
+ .al-head { flex-direction: column; gap: 10px; }
345
+ .al-head-actions { width: 100%; justify-content: flex-start; }
346
+ .al-title { font-size: 18px; }
347
+ .al-logo { width: 40px; height: 40px; border-radius: 12px; }
348
+ .al-sub { font-size: 11.5px; }
349
+ }
350
+ `;
351
+
352
+ const AL_ICONS = {
353
+ terminal: ['M4 5.5h16v13H4z', 'M7.5 9.5l2.5 2.5-2.5 2.5', 'M13 14.5h3.5'],
354
+ spark: ['M12 3.2l1.7 5 5 1.8-5 1.8-1.7 5-1.7-5-5-1.8 5-1.8z'],
355
+ plus: ['M12 5v14', 'M5 12h14'],
356
+ trash: ['M4.5 6.5h15', 'M9 6.5V4.5h6v2', 'M6.5 6.5l1 13h9l1-13', 'M10 10v6', 'M14 10v6'],
357
+ send: ['M4.5 11.5l15-6.5-6.5 15-2-6-6.5-2.5z'],
358
+ copy: ['M9 9h10v10H9z', 'M5 15V5h10'],
359
+ check: ['M5 12.5l4.5 4.5L19 7.5'],
360
+ x: ['M6 6l12 12', 'M18 6L6 18'],
361
+ refresh: ['M19.5 12a7.5 7.5 0 1 1-2.2-5.3', 'M19.5 4.5V9H15'],
362
+ book: ['M5 4.5h9a3 3 0 0 1 3 3V19.5H8a3 3 0 0 1-3-3z', 'M5 16.5h12'],
363
+ code: ['M8.5 8.5L5 12l3.5 3.5', 'M15.5 8.5L19 12l-3.5 3.5', 'M13.5 5.5l-3 13'],
364
+ cpu: ['M7 7h10v10H7z', 'M9.5 3.5v3', 'M14.5 3.5v3', 'M9.5 17.5v3', 'M14.5 17.5v3', 'M3.5 9.5h3', 'M3.5 14.5h3', 'M17.5 9.5h3', 'M17.5 14.5h3'],
365
+ brain: ['M9.5 4.5a3 3 0 0 0-3 3 3 3 0 0 0-1.5 5.6A3 3 0 0 0 8 19.5h1.5z', 'M14.5 4.5a3 3 0 0 1 3 3 3 3 0 0 1 1.5 5.6A3 3 0 0 1 16 19.5h-1.5z', 'M9.5 4.5v15', 'M14.5 4.5v15'],
366
+ layers: ['M12 3.5l8.5 4.5-8.5 4.5L3.5 8z', 'M3.5 12.5L12 17l8.5-4.5', 'M3.5 16.5L12 21l8.5-4.5'],
367
+ compress: ['M5 12h14', 'M9 8l-4 4 4 4', 'M15 8l4 4-4 4'],
368
+ memory: ['M6.5 5h11v14h-11z', 'M9.5 8.5h5', 'M9.5 12h5', 'M9.5 15.5h3', 'M6.5 8H4', 'M6.5 16H4', 'M20 8h-2.5', 'M20 16h-2.5'],
369
+ search: ['M10.8 17.6a6.8 6.8 0 1 0 0-13.6 6.8 6.8 0 0 0 0 13.6z', 'M15.8 15.8L20 20'],
370
+ clock: ['M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18z', 'M12 7v5.2l3.2 2'],
371
+ tools: ['M14.5 6.5a4 4 0 0 0-5.3 5.3L4 17l3 3 5.2-5.2a4 4 0 0 0 5.3-5.3l-2.4 2.4-2.1-2.1z'],
372
+ arrowRight: ['M5 12h13', 'M13 7l5 5-5 5'],
373
+ play: ['M8 5.5l10 6.5-10 6.5z'],
374
+ pause: ['M9 5.5v13', 'M15 5.5v13'],
375
+ info: ['M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18z', 'M12 11v5.5', 'M12 7.6v.1'],
376
+ alert: ['M12 3.8l9 15.4H3z', 'M12 9.5v4', 'M12 16.6v.1'],
377
+ dots: ['M6 12h.1', 'M12 12h.1', 'M18 12h.1'],
378
+ external: ['M14 5h5v5', 'M19 5l-7.5 7.5', 'M18.5 14v5.5h-13v-13H11'],
379
+ bolt: ['M13 3.5L5.5 13.5H11l-1 7L18.5 10H13z'],
380
+ key: ['M14 5.5a4.5 4.5 0 1 1-4.4 5.5L4 16.6V20h3.4l1.5-1.5H11v-2h2l1.6-1.6A4.5 4.5 0 0 1 14 5.5z', 'M16.4 8.6v.1'],
381
+ route: ['M5 6.5h4.5a4 4 0 0 1 4 4v3a4 4 0 0 0 4 4H20', 'M17 14.5l3 3-3 3', 'M7.5 3.5l-3 3 3 3'],
382
+ smile: ['M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18z', 'M8.5 10h.1', 'M15.5 10h.1', 'M8.5 14.5a4.2 4.2 0 0 0 7 0'],
383
+ chevron: ['M8.5 10l3.5 3.5 3.5-3.5'],
384
+ chevronRight: ['M10 8.5l3.5 3.5-3.5 3.5'],
385
+ chevronLeft: ['M14 8.5L10.5 12l3.5 3.5'],
386
+ stop: ['M7 7h10v10H7z'],
387
+ wand: ['M4 20l10-10', 'M14.5 4.5l1 2.4 2.4 1-2.4 1-1 2.4-1-2.4-2.4-1 2.4-1z', 'M19 13.5l.7 1.7 1.7.7-1.7.7-.7 1.7-.7-1.7-1.7-.7 1.7-.7z'],
388
+ };
389
+
390
+ function Icon(props) {
391
+ const name = (props && props.name) || 'terminal';
392
+ const size = (props && props.size) || 16;
393
+ const strokeWidth = (props && props.strokeWidth) || 1.7;
394
+ const paths = AL_ICONS[name] || AL_ICONS.terminal;
395
+ return e(
396
+ 'svg',
397
+ {
398
+ width: size, height: size, viewBox: '0 0 24 24', fill: 'none',
399
+ stroke: 'currentColor', strokeWidth, strokeLinecap: 'round', strokeLinejoin: 'round',
400
+ 'aria-hidden': 'true', focusable: 'false',
401
+ },
402
+ paths.map((d, index) => e('path', { key: index, d })),
403
+ );
404
+ }
405
+
406
+ function Btn(props) {
407
+ const variant = props.variant || 'ghost';
408
+ const size = props.size === 'sm' ? ' al-btn-sm' : '';
409
+ return e(
410
+ 'button',
411
+ {
412
+ type: 'button',
413
+ className: 'al-btn al-btn-' + variant + size + (props.className ? ' ' + props.className : ''),
414
+ onClick: props.onClick,
415
+ disabled: props.disabled,
416
+ title: props.title,
417
+ 'aria-label': props.title || undefined,
418
+ },
419
+ props.icon ? e(Icon, { name: props.icon, size: size ? 13 : 14 }) : null,
420
+ props.children,
421
+ );
422
+ }
423
+
424
+ function IconBtn(props) {
425
+ return e(
426
+ 'button',
427
+ {
428
+ type: 'button',
429
+ className: 'al-iconbtn' + (props.danger ? ' danger' : '') + (props.className ? ' ' + props.className : ''),
430
+ onClick: props.onClick,
431
+ disabled: props.disabled,
432
+ title: props.title,
433
+ 'aria-label': props.title || '操作',
434
+ },
435
+ e(Icon, { name: props.icon, size: props.size || 15 }),
436
+ );
437
+ }
438
+
439
+ function Chip(props) {
440
+ const tone = props.tone || 'neutral';
441
+ const toneClass = tone === 'neutral' ? '' : ' al-chip-' + tone;
442
+ return e(
443
+ 'span',
444
+ { className: 'al-chip' + toneClass + (props.className ? ' ' + props.className : ''), title: props.title },
445
+ props.icon ? e(Icon, { name: props.icon, size: 12 }) : null,
446
+ props.children,
447
+ );
448
+ }
449
+
450
+ function Empty(props) {
451
+ return e(
452
+ 'div',
453
+ { className: 'al-empty' },
454
+ e('div', { className: 'al-empty-icon' }, e(Icon, { name: props.icon || 'spark', size: 19 })),
455
+ props.title ? e('div', { className: 'al-empty-title' }, props.title) : null,
456
+ props.body ? e('div', { className: 'al-empty-body' }, props.body) : null,
457
+ props.action || null,
458
+ );
459
+ }
460
+
461
+ function prettyJson(value) {
462
+ try {
463
+ const text = JSON.stringify(value, null, 2);
464
+ return text === undefined ? String(value) : text;
465
+ } catch (error) {
466
+ return String(value);
467
+ }
468
+ }
469
+
470
+ const AL_JSON_TOKEN = /("(?:\\.|[^"\\])*")(\s*:)?|\b(true|false|null)\b|(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g;
471
+
472
+ function highlightJson(text) {
473
+ const nodes = [];
474
+ let last = 0;
475
+ let index = 0;
476
+ let match;
477
+ AL_JSON_TOKEN.lastIndex = 0;
478
+ while ((match = AL_JSON_TOKEN.exec(text)) !== null) {
479
+ if (match.index > last) nodes.push(text.slice(last, match.index));
480
+ const key = match[1];
481
+ if (key && match[2]) {
482
+ nodes.push(e('span', { key: index++, className: 'al-json-key' }, key));
483
+ nodes.push(match[2]);
484
+ } else if (key) {
485
+ nodes.push(e('span', { key: index++, className: 'al-json-str' }, key));
486
+ } else if (match[3]) {
487
+ const cls = match[3] === 'null' ? 'al-json-null' : 'al-json-bool';
488
+ nodes.push(e('span', { key: index++, className: cls }, match[3]));
489
+ } else if (match[4]) {
490
+ nodes.push(e('span', { key: index++, className: 'al-json-num' }, match[4]));
491
+ }
492
+ last = AL_JSON_TOKEN.lastIndex;
493
+ }
494
+ if (last < text.length) nodes.push(text.slice(last));
495
+ return nodes;
496
+ }
497
+
498
+ function JsonView(props) {
499
+ const text = useMemo(() => prettyJson(props.value), [props.value]);
500
+ return e('pre', { className: 'al-json', style: props.style, title: props.title || 'JSON' }, highlightJson(text));
501
+ }
502
+
503
+ function copyText(text) {
504
+ const value = String(text == null ? '' : text);
505
+ if (typeof navigator !== 'undefined' && navigator.clipboard && navigator.clipboard.writeText) {
506
+ return navigator.clipboard.writeText(value).catch(() => legacyCopy(value));
507
+ }
508
+ return legacyCopy(value);
509
+ }
510
+
511
+ function legacyCopy(value) {
512
+ return new Promise((resolve, reject) => {
513
+ try {
514
+ const area = document.createElement('textarea');
515
+ area.value = value;
516
+ area.style.position = 'fixed';
517
+ area.style.opacity = '0';
518
+ area.style.pointerEvents = 'none';
519
+ document.body.appendChild(area);
520
+ area.select();
521
+ const ok = document.execCommand('copy');
522
+ document.body.removeChild(area);
523
+ ok ? resolve(true) : reject(new Error('copy failed'));
524
+ } catch (error) {
525
+ reject(error);
526
+ }
527
+ });
528
+ }
529
+
530
+ function useCopy() {
531
+ const [copied, setCopied] = useState(false);
532
+ const timer = useRef(null);
533
+ useEffect(() => () => { if (timer.current) clearTimeout(timer.current); }, []);
534
+ const copy = useCallback((text) => {
535
+ copyText(text).then(
536
+ () => {
537
+ setCopied(true);
538
+ if (timer.current) clearTimeout(timer.current);
539
+ timer.current = setTimeout(() => setCopied(false), 1600);
540
+ },
541
+ () => setCopied(false),
542
+ );
543
+ }, []);
544
+ return { copied, copy };
545
+ }
546
+
547
+ const AL_SEGMENT_COLORS = {
548
+ system: '#8b5cf6',
549
+ summary: '#f59e0b',
550
+ memory: '#14b8a6',
551
+ recent: '#3b82f6',
552
+ input: '#22c55e',
553
+ };
554
+
555
+ function TokenBar(props) {
556
+ const segments = props.segments || [];
557
+ const limit = props.limit || 0;
558
+ const height = props.height || 8;
559
+ return e(
560
+ 'div',
561
+ null,
562
+ e(
563
+ 'div',
564
+ { className: 'al-tokenbar', style: { height } },
565
+ segments.map((segment) =>
566
+ e('span', {
567
+ key: segment.id,
568
+ style: {
569
+ width: (limit > 0 ? Math.min(100, (segment.tokens / limit) * 100) : 0) + '%',
570
+ background: AL_SEGMENT_COLORS[segment.id] || 'var(--al-ter)',
571
+ },
572
+ title: segment.label + ' · ' + segment.tokens + ' tok',
573
+ }),
574
+ ),
575
+ ),
576
+ props.showLegend === false
577
+ ? null
578
+ : e(
579
+ 'div',
580
+ { className: 'al-tokenbar-legend' },
581
+ segments.map((segment) =>
582
+ e(
583
+ 'span',
584
+ { key: segment.id, className: 'al-legend-item' },
585
+ e('span', { className: 'al-legend-dot', style: { background: AL_SEGMENT_COLORS[segment.id] || 'var(--al-ter)' } }),
586
+ segment.label,
587
+ e('span', { style: { fontVariantNumeric: 'tabular-nums' } }, String(segment.tokens)),
588
+ ),
589
+ ),
590
+ ),
591
+ );
592
+ }
593
+
594
+ function Stat(props) {
595
+ return e('div', { className: 'al-stat' }, e('b', null, props.value), e('span', null, props.label));
596
+ }