@haikit/client 0.8.0 → 0.9.1

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/src/hai.css CHANGED
@@ -6,226 +6,597 @@
6
6
  *
7
7
  * It deliberately does not style the inside of your surfaces. Your components
8
8
  * emit their own class names and your own stylesheet owns them — otherwise the
9
- * framework starts dictating what your UI looks like.
9
+ * framework starts dictating what your UI looks like. They can use the tokens,
10
+ * though: --hai-hover and --hai-selected are there for rows and pickers.
10
11
  *
11
- * Everything is driven by custom properties. Override them in your own sheet:
12
+ * Light and dark, following the system. Every colour is a custom property set
13
+ * here at zero specificity, so a plain `:root` rule in your own sheet wins.
14
+ * Give one value per scheme with light-dark(), behind a plain dark value for
15
+ * the browsers that fall back to dark below:
12
16
  *
13
- * :root { --hai-accent: #b07cff; --hai-bg: #101014; }
17
+ * :root { --hai-accent: #b07cff; }
18
+ * @supports (color: light-dark(#000, #fff)) {
19
+ * :root { --hai-accent: light-dark(#6d28d9, #b07cff); }
20
+ * }
21
+ *
22
+ * <html data-hai-theme="dark"> pins the page to one scheme, as does
23
+ * `mountChat({ theme: "dark" })`. The pin is on the root because the tokens
24
+ * are: a minifier that lowers light-dark() resolves them there.
14
25
  */
15
26
 
16
- :root {
17
- --hai-bg: #0d1013;
18
- --hai-panel: #14181d;
19
- --hai-panel-2: #11161c;
20
- --hai-line: #242c34;
21
- --hai-fg: #dde3ea;
22
- --hai-dim: #7c8894;
23
- --hai-accent: #6ea8fe;
24
- --hai-good: #63c49a;
25
- --hai-warn: #e0b055;
26
- --hai-bad: #e07070;
27
- --hai-radius: 10px;
28
- --hai-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
29
- --hai-sans: ui-sans-serif, system-ui, -apple-system, sans-serif;
27
+ :where(:root) {
28
+ color-scheme: light dark;
29
+
30
+ --hai-bg: light-dark(#f6f7f9, #0c0f13);
31
+ --hai-panel: light-dark(#ffffff, #141920);
32
+ --hai-panel-2: light-dark(#f9fafb, #10141a);
33
+ --hai-line: light-dark(#e1e5ea, #252c35);
34
+ --hai-fg: light-dark(#161b22, #e3e8ee);
35
+ --hai-dim: light-dark(#5b6675, #8a95a3);
36
+ --hai-accent: light-dark(#2160e0, #7aa7ff);
37
+ --hai-on-accent: light-dark(#ffffff, #0b1220);
38
+ --hai-good: light-dark(#127a4c, #5ec597);
39
+ --hai-warn: light-dark(#9a5b00, #e7b65c);
40
+ --hai-bad: light-dark(#c23338, #f07b7b);
41
+ --hai-user-bg: light-dark(#e7eefb, #1e2835);
42
+ --hai-code-bg: light-dark(#f3f5f8, #0a0d11);
43
+ --hai-shadow: 0 1px 2px light-dark(rgb(15 23 42 / .06), rgb(0 0 0 / .35));
44
+ --hai-logo-ink: light-dark(#0f1115, #f3f2ee);
45
+ --hai-logo-paper: light-dark(#f3f2ee, #0f1115);
46
+ --hai-logo-hot: #ff5a1f;
47
+
48
+ /* tints, derived — override the colour and these follow */
49
+ --hai-line-soft: color-mix(in srgb, var(--hai-line) 60%, transparent);
50
+ --hai-hover: color-mix(in srgb, var(--hai-fg) 6%, transparent);
51
+ --hai-selected: color-mix(in srgb, var(--hai-good) 15%, transparent);
52
+ --hai-accent-soft: color-mix(in srgb, var(--hai-accent) 14%, transparent);
53
+ --hai-good-soft: color-mix(in srgb, var(--hai-good) 13%, transparent);
54
+ --hai-warn-soft: color-mix(in srgb, var(--hai-warn) 12%, transparent);
55
+ --hai-bad-soft: color-mix(in srgb, var(--hai-bad) 12%, transparent);
56
+
57
+ --hai-radius: 12px;
58
+ --hai-mono: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace;
59
+ --hai-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
60
+ --hai-column: 780px;
30
61
  --hai-inspector-width: 400px;
31
62
  }
32
63
 
64
+ :where(:root[data-hai-theme="light"]) { color-scheme: light; }
65
+ :where(:root[data-hai-theme="dark"]) { color-scheme: dark; }
66
+
67
+ /* A browser without light-dark() gets the dark scheme, not no colours at all. */
68
+ @supports not (color: light-dark(#000, #fff)) {
69
+ :where(:root) {
70
+ color-scheme: dark;
71
+ --hai-bg: #0c0f13;
72
+ --hai-panel: #141920;
73
+ --hai-panel-2: #10141a;
74
+ --hai-line: #252c35;
75
+ --hai-fg: #e3e8ee;
76
+ --hai-dim: #8a95a3;
77
+ --hai-accent: #7aa7ff;
78
+ --hai-on-accent: #0b1220;
79
+ --hai-good: #5ec597;
80
+ --hai-warn: #e7b65c;
81
+ --hai-bad: #f07b7b;
82
+ --hai-user-bg: #1e2835;
83
+ --hai-code-bg: #0a0d11;
84
+ --hai-shadow: 0 1px 2px rgb(0 0 0 / .35);
85
+ --hai-logo-ink: #f3f2ee;
86
+ --hai-logo-paper: #0f1115;
87
+ }
88
+ }
89
+
90
+ /* the shell is the page */
91
+ :where(body:has(.hai-shell)) { margin: 0; }
92
+
33
93
  /* ── shell ──────────────────────────────────────────────────────────── */
34
94
  .hai-shell {
35
95
  height: 100vh;
96
+ height: 100dvh;
36
97
  display: flex;
37
98
  flex-direction: column;
38
99
  background: var(--hai-bg);
39
100
  color: var(--hai-fg);
40
- font: 14px/1.55 var(--hai-sans);
101
+ font: 14px/1.5 var(--hai-sans);
102
+ -webkit-font-smoothing: antialiased;
41
103
  }
104
+
105
+ .hai-icon { width: 16px; height: 16px; flex: none; }
106
+
42
107
  .hai-top {
108
+ flex: none;
43
109
  display: flex;
44
- align-items: baseline;
110
+ align-items: center;
45
111
  gap: 12px;
46
- padding: 10px 16px;
112
+ height: 52px;
113
+ padding: 0 10px 0 16px;
47
114
  border-bottom: 1px solid var(--hai-line);
48
115
  background: var(--hai-panel);
49
116
  }
50
- .hai-title { font-weight: 700; letter-spacing: .04em; }
51
- .hai-subtitle { color: var(--hai-dim); font-size: 12px; }
52
- .hai-model { margin-left: auto; font: 11px var(--hai-mono); color: var(--hai-warn); }
117
+ .hai-brand { display: flex; align-items: center; gap: 10px; min-width: 0; }
118
+ .hai-logo { flex: none; }
119
+ .hai-logo-ink { fill: var(--hai-logo-ink); }
120
+ .hai-logo-paper { fill: var(--hai-logo-paper); }
121
+ .hai-logo-paper-dim { fill: var(--hai-logo-paper); opacity: .5; }
122
+ .hai-logo-hot { fill: var(--hai-logo-hot); }
123
+ .hai-mark { width: 20px; height: 20px; }
124
+ .hai-title { font-size: 15px; font-weight: 650; letter-spacing: -.01em; white-space: nowrap; }
125
+ .hai-subtitle {
126
+ color: var(--hai-dim);
127
+ font-size: 13px;
128
+ padding-left: 10px;
129
+ border-left: 1px solid var(--hai-line);
130
+ overflow: hidden;
131
+ text-overflow: ellipsis;
132
+ white-space: nowrap;
133
+ }
134
+
135
+ .hai-actions { margin-left: auto; display: flex; align-items: center; gap: 4px; flex: none; }
136
+ .hai-model {
137
+ display: inline-flex;
138
+ align-items: center;
139
+ gap: 6px;
140
+ margin-right: 6px;
141
+ padding: 3px 9px;
142
+ border: 1px solid var(--hai-line);
143
+ border-radius: 999px;
144
+ font: 11.5px var(--hai-mono);
145
+ color: var(--hai-dim);
146
+ }
147
+ .hai-model::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--hai-good); }
148
+ .hai-model[hidden] { display: none; }
149
+
150
+ .hai-btn {
151
+ display: inline-flex;
152
+ align-items: center;
153
+ gap: 7px;
154
+ height: 32px;
155
+ padding: 0 10px;
156
+ border: none;
157
+ border-radius: 8px;
158
+ background: none;
159
+ color: var(--hai-dim);
160
+ font: inherit;
161
+ font-size: 13px;
162
+ font-weight: 500;
163
+ cursor: pointer;
164
+ }
165
+ .hai-btn:hover:not(:disabled) { background: var(--hai-hover); color: var(--hai-fg); }
166
+ .hai-btn:disabled { opacity: .4; cursor: default; }
167
+ .hai-btn[aria-expanded="true"] { color: var(--hai-fg); background: var(--hai-hover); }
168
+ .hai-ctx-count { font: 11.5px var(--hai-mono); color: var(--hai-dim); }
169
+ .hai-ctx-count:empty { display: none; }
170
+
171
+ .hai-icon-btn {
172
+ display: grid;
173
+ place-items: center;
174
+ width: 30px;
175
+ height: 30px;
176
+ border: none;
177
+ border-radius: 8px;
178
+ background: none;
179
+ color: var(--hai-dim);
180
+ cursor: pointer;
181
+ }
182
+ .hai-icon-btn:hover { background: var(--hai-hover); color: var(--hai-fg); }
183
+
184
+ /* clips the drawer while it waits off the right edge */
185
+ .hai-main { flex: 1; display: grid; grid-template-columns: minmax(0, 1fr); min-height: 0; position: relative; overflow: hidden; }
186
+
187
+ /* the conversation is a centred column; the scrollbar stays at the edge */
188
+ .hai-chat {
189
+ --hai-gutter: max(16px, calc((100% - var(--hai-column)) / 2));
190
+ display: flex;
191
+ flex-direction: column;
192
+ min-width: 0;
193
+ min-height: 0;
194
+ transition: margin-right .25s cubic-bezier(.2, .8, .2, 1), opacity .25s;
195
+ }
196
+ /* behind the drawer on a narrow screen, out of reach until it closes */
197
+ .hai-chat[inert] { opacity: .4; }
198
+ /* on a wide screen the chat makes room for the open drawer */
199
+ @media (min-width: 900px) {
200
+ .hai-main.inspector-open > .hai-chat { margin-right: var(--hai-inspector-width); }
201
+ }
202
+ .hai-transcript {
203
+ flex: 1;
204
+ overflow-y: auto;
205
+ padding: 28px var(--hai-gutter) 8px;
206
+ scrollbar-width: thin;
207
+ scrollbar-color: var(--hai-line) transparent;
208
+ }
53
209
 
54
- .hai-main { flex: 1; display: grid; grid-template-columns: minmax(0, 1fr); min-height: 0; }
55
- .hai-main.has-inspector { grid-template-columns: minmax(0, 1fr) var(--hai-inspector-width); }
56
- .hai-chat { display: flex; flex-direction: column; min-height: 0; }
210
+ /* ── empty state ────────────────────────────────────────────────────── */
211
+ .hai-empty { color: var(--hai-dim); }
212
+ .hai-empty p { margin: 0; max-width: 54ch; font-size: 15px; line-height: 1.6; }
213
+ .hai-empty-mark { width: 44px; height: 44px; margin-bottom: 20px; }
214
+ /* alone, it greets; after what init showed, it stays out of the way */
215
+ .hai-empty:first-child {
216
+ display: flex;
217
+ flex-direction: column;
218
+ align-items: center;
219
+ text-align: center;
220
+ padding-top: clamp(24px, 14vh, 140px);
221
+ }
222
+ .hai-empty:not(:first-child) .hai-empty-mark { display: none; }
57
223
 
58
- .hai-transcript { flex: 1; overflow-y: auto; padding: 18px 20px 8px; }
59
- .hai-empty { color: var(--hai-dim); max-width: 60ch; }
60
- .hai-suggestions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
224
+ .hai-suggestions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 20px; }
225
+ .hai-empty:first-child .hai-suggestions { justify-content: center; }
226
+ .hai-empty:not(:first-child) .hai-suggestions { margin-top: 12px; }
61
227
  .hai-suggestion {
62
- background: var(--hai-panel-2);
63
- color: var(--hai-fg);
228
+ padding: 7px 14px;
64
229
  border: 1px solid var(--hai-line);
65
230
  border-radius: 999px;
66
- padding: 5px 12px;
67
- cursor: pointer;
231
+ background: var(--hai-panel);
232
+ color: var(--hai-fg);
68
233
  font: inherit;
69
- font-size: 13px;
234
+ font-size: 13.5px;
235
+ cursor: pointer;
236
+ transition: border-color .15s, background-color .15s;
70
237
  }
71
- .hai-suggestion:hover { border-color: var(--hai-accent); }
238
+ .hai-suggestion:hover { border-color: var(--hai-accent); background: var(--hai-accent-soft); }
72
239
 
73
240
  /* ── transcript blocks ──────────────────────────────────────────────── */
74
- .hai-block { margin: 0 0 12px; max-width: 70ch; }
241
+ .hai-block { margin: 0 0 18px; }
242
+ .hai-user .hai-body,
243
+ .hai-assistant .hai-body {
244
+ white-space: pre-wrap;
245
+ overflow-wrap: anywhere;
246
+ /* each paragraph takes its own direction: a reply can mix scripts */
247
+ unicode-bidi: plaintext;
248
+ font-size: 15px;
249
+ }
250
+ .hai-assistant { max-width: 72ch; line-height: 1.65; }
251
+ .hai-user { display: flex; justify-content: flex-end; }
75
252
  .hai-user .hai-body {
76
- background: #1d2733;
77
- border: 1px solid #27333f;
78
- border-radius: var(--hai-radius);
79
- padding: 8px 12px;
80
- display: inline-block;
253
+ max-width: min(85%, 62ch);
254
+ padding: 9px 14px;
255
+ border-radius: 18px 18px 6px 18px;
256
+ background: var(--hai-user-bg);
81
257
  }
82
- .hai-assistant .hai-body { white-space: pre-wrap; }
83
- .hai-interaction {
258
+
259
+ /* an answer given in a surface — it went back to the model as the tool result */
260
+ .hai-interaction { display: flex; align-items: flex-start; gap: 10px; max-width: 72ch; font-size: 13.5px; color: var(--hai-dim); }
261
+ .hai-arrow {
262
+ flex: none;
263
+ display: grid;
264
+ place-items: center;
265
+ width: 22px;
266
+ height: 22px;
267
+ margin-top: -1px;
268
+ border-radius: 50%;
269
+ background: var(--hai-good-soft);
84
270
  color: var(--hai-good);
85
- font-size: 13px;
271
+ }
272
+ .hai-arrow .hai-icon { width: 13px; height: 13px; }
273
+
274
+ .hai-error,
275
+ .hai-expired {
86
276
  display: flex;
87
- gap: 8px;
88
- border-left: 2px solid var(--hai-good);
89
- padding-left: 10px;
277
+ align-items: flex-start;
278
+ gap: 10px;
279
+ padding: 11px 14px;
280
+ border: 1px solid;
281
+ border-radius: var(--hai-radius);
90
282
  }
91
- .hai-error { color: var(--hai-bad); font: 12px var(--hai-mono); }
283
+ .hai-error .hai-icon,
284
+ .hai-expired .hai-icon { margin-top: 2px; }
285
+ .hai-error {
286
+ border-color: color-mix(in srgb, var(--hai-bad) 35%, transparent);
287
+ background: var(--hai-bad-soft);
288
+ color: var(--hai-bad);
289
+ font: 12.5px/1.55 var(--hai-mono);
290
+ }
291
+ .hai-error .hai-body { overflow-wrap: anywhere; }
92
292
 
93
293
  /* tool provenance row — chrome for the surface directly beneath it */
94
294
  .hai-tool {
95
295
  border: 1px solid var(--hai-line);
96
- border-radius: var(--hai-radius) var(--hai-radius) 0 0;
97
- background: var(--hai-panel);
98
- max-width: none;
99
- margin-bottom: 0;
296
+ border-radius: var(--hai-radius);
297
+ background: var(--hai-panel-2);
298
+ overflow: hidden;
100
299
  }
300
+ .hai-tool:has(+ .hai-surface) { margin-bottom: 0; border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
101
301
  .hai-tool summary {
102
302
  display: flex;
103
303
  align-items: center;
104
- gap: 8px;
304
+ gap: 9px;
305
+ min-height: 36px;
105
306
  padding: 7px 12px;
307
+ box-sizing: border-box;
106
308
  cursor: pointer;
107
- font: 12px var(--hai-mono);
108
309
  list-style: none;
310
+ font-size: 12.5px;
311
+ user-select: none;
109
312
  }
110
313
  .hai-tool summary::-webkit-details-marker { display: none; }
111
- .hai-gear { color: var(--hai-dim); }
112
- .hai-tname { color: var(--hai-accent); }
113
- .hai-targs { color: var(--hai-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
114
- .hai-tstatus { color: var(--hai-dim); }
115
- .hai-status-awaiting .hai-tstatus { color: var(--hai-warn); }
314
+ .hai-tool summary:hover { background: var(--hai-hover); }
315
+ .hai-ticon { flex: none; display: grid; place-items: center; width: 16px; height: 16px; color: var(--hai-dim); }
316
+ .hai-ticon .hai-icon { width: 14px; height: 14px; }
317
+ .hai-tname { font: 500 12.5px var(--hai-mono); }
318
+ .hai-targs {
319
+ flex: 1;
320
+ min-width: 0;
321
+ overflow: hidden;
322
+ text-overflow: ellipsis;
323
+ white-space: nowrap;
324
+ font: 12px var(--hai-mono);
325
+ color: var(--hai-dim);
326
+ }
327
+ .hai-tstatus { color: var(--hai-dim); white-space: nowrap; font-variant-numeric: tabular-nums; }
328
+ .hai-chevron { width: 14px; height: 14px; color: var(--hai-dim); transition: transform .15s; }
329
+ .hai-tool[open] .hai-chevron { transform: rotate(90deg); }
330
+
331
+ .hai-status-ok .hai-ticon { color: var(--hai-good); }
116
332
  .hai-status-running .hai-tstatus { color: var(--hai-accent); }
117
- .hai-tool-detail { padding: 0 12px 10px; border-top: 1px solid var(--hai-line); }
118
- .hai-label { font: 11px var(--hai-mono); color: var(--hai-dim); margin: 8px 0 4px; }
333
+ .hai-status-awaiting .hai-tstatus { color: var(--hai-warn); }
334
+ .hai-status-error .hai-ticon,
335
+ .hai-status-error .hai-tstatus { color: var(--hai-bad); }
336
+ /* the component is the question: the turn is parked on it */
337
+ .hai-tool.hai-status-awaiting { background: var(--hai-warn-soft); }
338
+ .hai-tool.hai-status-awaiting,
339
+ .hai-tool.hai-status-awaiting + .hai-surface { border-color: color-mix(in srgb, var(--hai-warn) 45%, var(--hai-line)); }
340
+
341
+ .hai-tool-detail { padding: 0 12px 12px; border-top: 1px solid var(--hai-line); }
342
+ .hai-label { margin: 10px 0 5px; font: 11px var(--hai-mono); color: var(--hai-dim); }
343
+ .hai-code,
119
344
  .hai-digest {
120
345
  margin: 0;
121
- padding: 8px 10px;
122
- background: #0b0e11;
346
+ padding: 9px 11px;
347
+ max-height: 200px;
348
+ overflow: auto;
123
349
  border: 1px solid var(--hai-line);
124
- border-radius: 6px;
125
- font: 11.5px/1.5 var(--hai-mono);
126
- color: var(--hai-good);
350
+ border-radius: 8px;
351
+ background: var(--hai-code-bg);
352
+ font: 12px/1.55 var(--hai-mono);
127
353
  white-space: pre-wrap;
128
- max-height: 180px;
129
- overflow: auto;
354
+ overflow-wrap: anywhere;
355
+ }
356
+ .hai-digest { color: var(--hai-good); }
357
+
358
+ .hai-spinner {
359
+ width: 12px;
360
+ height: 12px;
361
+ box-sizing: border-box;
362
+ border: 2px solid var(--hai-accent-soft);
363
+ border-top-color: var(--hai-accent);
364
+ border-radius: 50%;
365
+ animation: hai-spin .8s linear infinite;
366
+ }
367
+ .hai-pulse {
368
+ flex: none;
369
+ width: 8px;
370
+ height: 8px;
371
+ border-radius: 50%;
372
+ background: var(--hai-warn);
373
+ animation: hai-pulse 1.8s ease-out infinite;
130
374
  }
131
375
 
132
376
  /* surface container — your component mounts inside this */
133
377
  .hai-surface {
134
- border: 1px solid var(--hai-line);
135
- border-top: none;
136
- border-radius: 0 0 var(--hai-radius) var(--hai-radius);
137
- background: var(--hai-panel-2);
138
- margin-bottom: 14px;
378
+ margin-bottom: 18px;
139
379
  overflow: hidden;
380
+ border: 1px solid var(--hai-line);
381
+ border-radius: var(--hai-radius);
382
+ background: var(--hai-panel);
383
+ box-shadow: var(--hai-shadow);
384
+ }
385
+ .hai-tool + .hai-surface { border-top: none; border-top-left-radius: 0; border-top-right-radius: 0; }
386
+ .hai-skeleton { display: grid; gap: 9px; padding: 14px; }
387
+ .hai-skeleton-label { margin-bottom: 2px; font: 12px var(--hai-mono); color: var(--hai-dim); }
388
+ .hai-shimmer {
389
+ height: 10px;
390
+ border-radius: 5px;
391
+ background: linear-gradient(90deg, var(--hai-hover), var(--hai-line), var(--hai-hover)) 0 0 / 200% 100%;
392
+ animation: hai-shimmer 1.4s ease-in-out infinite;
393
+ }
394
+ .hai-shimmer:nth-child(3) { width: 82%; }
395
+ .hai-shimmer:nth-child(4) { width: 60%; }
396
+ .hai-surface-error {
397
+ margin-bottom: 18px;
398
+ padding: 11px 14px;
399
+ border: 1px solid color-mix(in srgb, var(--hai-bad) 35%, transparent);
400
+ border-radius: var(--hai-radius);
401
+ background: var(--hai-bad-soft);
402
+ color: var(--hai-bad);
403
+ font: 12.5px var(--hai-mono);
140
404
  }
141
- .hai-skeleton, .hai-surface-error { padding: 14px; font: 12px var(--hai-mono); color: var(--hai-dim); }
142
- .hai-surface-error { color: var(--hai-bad); }
143
405
 
144
406
  /* out of date — the runtime has made the surface inert; this only shows it */
145
407
  .hai-surface[data-expired] { opacity: .45; filter: grayscale(.7); }
146
408
  .hai-expired {
147
- display: flex;
148
409
  flex-wrap: wrap;
149
410
  align-items: center;
150
- gap: 10px 14px;
151
- padding: 10px 12px;
152
- border: 1px solid var(--hai-warn);
153
- border-radius: var(--hai-radius);
411
+ gap: 10px 12px;
412
+ border-color: color-mix(in srgb, var(--hai-warn) 45%, transparent);
413
+ background: var(--hai-warn-soft);
154
414
  color: var(--hai-warn);
155
415
  }
416
+ .hai-expired .hai-icon { margin-top: 0; }
156
417
  .hai-expired .hai-body { flex: 1 1 30ch; }
157
418
  .hai-new {
158
- background: var(--hai-warn);
159
- color: var(--hai-bg);
419
+ padding: 7px 14px;
160
420
  border: none;
161
421
  border-radius: 8px;
162
- padding: 6px 14px;
422
+ background: var(--hai-warn);
423
+ color: var(--hai-on-accent);
163
424
  font: inherit;
164
425
  font-weight: 600;
165
426
  cursor: pointer;
166
427
  }
167
428
 
168
429
  /* ── status + composer ──────────────────────────────────────────────── */
169
- .hai-status { padding: 0 20px 4px; font-size: 12px; min-height: 18px; color: var(--hai-dim); }
430
+ .hai-status {
431
+ display: flex;
432
+ align-items: center;
433
+ gap: 8px;
434
+ min-height: 22px;
435
+ padding: 0 var(--hai-gutter) 6px;
436
+ font-size: 12.5px;
437
+ color: var(--hai-dim);
438
+ }
170
439
  .hai-status[data-status="awaiting"] { color: var(--hai-warn); }
171
- .hai-composer { display: flex; gap: 8px; padding: 10px 20px 16px; border-top: 1px solid var(--hai-line); }
440
+ .hai-dots { display: inline-flex; gap: 3px; }
441
+ .hai-dots i { width: 5px; height: 5px; border-radius: 50%; background: currentColor; animation: hai-blink 1.2s infinite both; }
442
+ .hai-dots i:nth-child(2) { animation-delay: .2s; }
443
+ .hai-dots i:nth-child(3) { animation-delay: .4s; }
444
+
445
+ .hai-composer { flex: none; padding: 0 var(--hai-gutter) 18px; }
446
+ .hai-composer-box {
447
+ display: flex;
448
+ align-items: flex-end;
449
+ gap: 8px;
450
+ padding: 7px 7px 7px 14px;
451
+ border: 1px solid var(--hai-line);
452
+ border-radius: 16px;
453
+ background: var(--hai-panel);
454
+ box-shadow: var(--hai-shadow);
455
+ transition: border-color .15s, box-shadow .15s;
456
+ }
457
+ .hai-composer-box:focus-within { border-color: var(--hai-accent); box-shadow: 0 0 0 3px var(--hai-accent-soft); }
172
458
  .hai-input {
173
459
  flex: 1;
460
+ box-sizing: border-box;
461
+ min-height: 34px;
462
+ max-height: 200px;
463
+ padding: 6px 0;
464
+ overflow-y: auto;
174
465
  resize: none;
175
- background: var(--hai-panel);
466
+ border: none;
467
+ outline: none;
468
+ background: transparent;
176
469
  color: var(--hai-fg);
177
- border: 1px solid var(--hai-line);
178
- border-radius: 8px;
179
- padding: 9px 12px;
180
470
  font: inherit;
181
- min-height: 38px;
182
- max-height: 180px;
471
+ font-size: 15px;
472
+ line-height: 1.45;
183
473
  }
184
- .hai-input:focus { outline: none; border-color: var(--hai-accent); }
185
- .hai-input:disabled { opacity: .5; }
474
+ .hai-input::placeholder { color: var(--hai-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
475
+ .hai-input:disabled { cursor: not-allowed; opacity: .6; }
186
476
  .hai-send {
187
- background: var(--hai-accent);
188
- color: var(--hai-bg);
477
+ flex: none;
478
+ display: grid;
479
+ place-items: center;
480
+ width: 34px;
481
+ height: 34px;
189
482
  border: none;
190
- border-radius: 8px;
191
- padding: 0 18px;
192
- font: inherit;
193
- font-weight: 600;
483
+ border-radius: 10px;
484
+ background: var(--hai-accent);
485
+ color: var(--hai-on-accent);
194
486
  cursor: pointer;
487
+ transition: opacity .15s;
195
488
  }
196
- .hai-send:disabled { opacity: .4; cursor: default; }
489
+ .hai-send .hai-icon { width: 18px; height: 18px; }
490
+ .hai-send:disabled { opacity: .35; cursor: default; }
197
491
 
198
- /* ── context inspector ──────────────────────────────────────────────── */
492
+ /* ── model context debug drawer ─────────────────────────────────────── */
493
+ /* Closed, it waits off the right edge, out of the tab order and the
494
+ accessibility tree; open, it slides in, over the chat on a narrow screen. */
199
495
  .hai-inspector {
200
- border-left: 1px solid var(--hai-line);
201
- background: var(--hai-panel-2);
496
+ position: absolute;
497
+ inset: 0 0 0 auto;
498
+ z-index: 10;
202
499
  display: flex;
203
500
  flex-direction: column;
501
+ width: min(100%, var(--hai-inspector-width));
204
502
  min-height: 0;
503
+ border-left: 1px solid var(--hai-line);
504
+ background: var(--hai-panel-2);
505
+ box-shadow: -16px 0 32px -12px rgb(0 0 0 / .25);
506
+ transform: translateX(100%);
507
+ visibility: hidden;
508
+ transition: transform .25s cubic-bezier(.2, .8, .2, 1), visibility 0s .25s;
205
509
  }
206
- .hai-inspector-title { padding: 10px 14px; border-bottom: 1px solid var(--hai-line); font-size: 12px; font-weight: 600; }
207
- .hai-inspector-title span { color: var(--hai-dim); font-weight: 400; }
208
- .hai-inspector-body { flex: 1; overflow-y: auto; padding: 12px 14px; }
209
- .hai-ctx-bar {
210
- display: flex;
211
- flex-direction: column;
212
- gap: 3px;
213
- margin-bottom: 14px;
214
- padding: 10px;
215
- border: 1px solid var(--hai-line);
216
- border-radius: 8px;
217
- background: var(--hai-bg);
218
- font: 11.5px var(--hai-mono);
510
+ .hai-main.inspector-open > .hai-inspector {
511
+ transform: none;
512
+ visibility: visible;
513
+ transition: transform .25s cubic-bezier(.2, .8, .2, 1), visibility 0s;
514
+ }
515
+ .hai-inspector-head { display: flex; align-items: center; gap: 8px; padding: 10px 10px 10px 16px; border-bottom: 1px solid var(--hai-line); }
516
+ .hai-inspector-title { flex: 1; display: flex; flex-direction: column; font-size: 13px; font-weight: 600; }
517
+ .hai-inspector-title span { color: var(--hai-dim); font-size: 12px; font-weight: 400; }
518
+ .hai-inspector-body {
519
+ flex: 1;
520
+ overflow-y: auto;
521
+ padding: 0 16px 16px;
522
+ scrollbar-width: thin;
523
+ scrollbar-color: var(--hai-line) transparent;
524
+ }
525
+ .hai-inspector-empty { margin: 16px 0; color: var(--hai-dim); font-size: 13px; }
526
+
527
+ /* the numbers stay in view as the context scrolls under them */
528
+ .hai-ctx {
529
+ position: sticky;
530
+ top: 0;
531
+ z-index: 1;
532
+ padding: 14px 0 12px;
533
+ border-bottom: 1px solid var(--hai-line);
534
+ background: var(--hai-panel-2);
535
+ }
536
+ .hai-ctx-headline { display: flex; align-items: baseline; gap: 8px; margin-bottom: 8px; font-size: 12.5px; color: var(--hai-dim); }
537
+ .hai-ctx-pct { font-size: 26px; font-weight: 650; letter-spacing: -.02em; color: var(--hai-good); font-variant-numeric: tabular-nums; }
538
+ .hai-meter { display: flex; height: 6px; margin-bottom: 10px; overflow: hidden; border-radius: 3px; background: var(--hai-good); }
539
+ .hai-meter-model { min-width: 2px; background: var(--hai-accent); }
540
+ .hai-ctx-stat { display: flex; justify-content: space-between; padding: 1px 0; font: 12px var(--hai-mono); }
541
+ .hai-ctx-label { display: inline-flex; align-items: center; gap: 7px; color: var(--hai-dim); }
542
+ .hai-ctx-tok { font-variant-numeric: tabular-nums; }
543
+ .hai-key { width: 8px; height: 8px; border-radius: 2px; }
544
+ .hai-key-model { background: var(--hai-accent); }
545
+ .hai-key-ui { background: var(--hai-good); }
546
+
547
+ .hai-msg { margin-top: 14px; padding-left: 11px; border-left: 2px solid var(--hai-line); }
548
+ .hai-role { margin-bottom: 4px; font: 600 10px var(--hai-mono); text-transform: uppercase; letter-spacing: .08em; color: var(--hai-dim); }
549
+ .hai-mbody { display: grid; gap: 6px; font: 12px/1.55 var(--hai-mono); white-space: pre-wrap; overflow-wrap: anywhere; }
550
+ .hai-tu,
551
+ .hai-tr { padding: 6px 8px; border-radius: 6px; }
552
+ .hai-tu { background: var(--hai-accent-soft); color: var(--hai-accent); }
553
+ .hai-tr { background: var(--hai-good-soft); color: var(--hai-good); }
554
+ .hai-kind { display: block; margin-bottom: 2px; font-size: 10.5px; opacity: .75; }
555
+
556
+ /* ── narrow screens ─────────────────────────────────────────────────── */
557
+ @media (max-width: 899.98px) {
558
+ .hai-subtitle { display: none; }
559
+ .hai-btn { padding: 0 8px; }
560
+ .hai-btn-label {
561
+ position: absolute;
562
+ width: 1px;
563
+ height: 1px;
564
+ overflow: hidden;
565
+ clip-path: inset(50%);
566
+ white-space: nowrap;
567
+ }
568
+ }
569
+ @media (max-width: 599.98px) {
570
+ .hai-model { display: none; }
571
+ .hai-transcript { padding-top: 18px; }
572
+ .hai-composer { padding-bottom: 12px; }
219
573
  }
220
- .hai-stat-dim { color: var(--hai-dim); }
221
- .hai-stat-good { color: var(--hai-good); }
222
- .hai-msg { margin-bottom: 10px; border-left: 2px solid var(--hai-line); padding-left: 9px; }
223
- .hai-role { font: 10px var(--hai-mono); text-transform: uppercase; color: var(--hai-dim); letter-spacing: .07em; }
224
- .hai-mbody { font: 11.5px/1.5 var(--hai-mono); white-space: pre-wrap; word-break: break-word; }
225
- .hai-tu { color: var(--hai-accent); }
226
- .hai-tr { color: var(--hai-good); }
227
-
228
- @media (max-width: 900px) {
229
- .hai-main.has-inspector { grid-template-columns: minmax(0, 1fr); }
230
- .hai-inspector { display: none; }
574
+
575
+ /* ── focus, motion ──────────────────────────────────────────────────── */
576
+ .hai-btn:focus-visible,
577
+ .hai-icon-btn:focus-visible,
578
+ .hai-send:focus-visible,
579
+ .hai-suggestion:focus-visible,
580
+ .hai-new:focus-visible { outline: 2px solid var(--hai-accent); outline-offset: 2px; }
581
+ .hai-tool summary:focus-visible { outline: 2px solid var(--hai-accent); outline-offset: -2px; }
582
+
583
+ @keyframes hai-spin { to { transform: rotate(360deg); } }
584
+ @keyframes hai-pulse {
585
+ 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--hai-warn) 55%, transparent); }
586
+ 100% { box-shadow: 0 0 0 7px transparent; }
587
+ }
588
+ @keyframes hai-blink { 0%, 80%, 100% { opacity: .25; } 40% { opacity: 1; } }
589
+ @keyframes hai-shimmer { from { background-position: 100% 0; } to { background-position: -100% 0; } }
590
+
591
+ @media (prefers-reduced-motion: reduce) {
592
+ .hai-spinner,
593
+ .hai-pulse,
594
+ .hai-dots i,
595
+ .hai-shimmer { animation: none; }
596
+ .hai-chevron,
597
+ .hai-suggestion,
598
+ .hai-composer-box,
599
+ .hai-chat,
600
+ .hai-inspector,
601
+ .hai-main.inspector-open > .hai-inspector { transition: none; }
231
602
  }