@haikit/client 0.21.0 → 0.22.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/README.md CHANGED
@@ -18,6 +18,10 @@ not style the inside of your surfaces; your components emit their own class
18
18
  names and your stylesheet owns them. Every colour is a `--hai-*` custom
19
19
  property, so override what you like.
20
20
 
21
+ The look is neo-brutalist: hard 3px borders, offset shadows, square corners,
22
+ in Space Grotesk and Space Mono. `hai.css` names those faces but doesn't load
23
+ them; link them on your page, or the system's faces stand in.
24
+
21
25
  ## The default UI
22
26
 
23
27
  The [default UI guide](https://github.com/wfoxd/haikit/blob/main/docs/default-ui.md)
@@ -51,13 +55,14 @@ sheet wins. Give a colour one value per scheme with `light-dark()`:
51
55
  ```css
52
56
  :root { --hai-accent: #b07cff; }
53
57
  @supports (color: light-dark(#000, #fff)) {
54
- :root { --hai-accent: light-dark(#6d28d9, #b07cff); }
58
+ :root { --hai-accent: light-dark(#9f7aea, #b07cff); }
55
59
  }
56
60
  ```
57
61
 
58
62
  Keep the plain value first. A browser without `light-dark()` gets hai.css's
59
63
  dark scheme, and an override it can't read would leave that token with no
60
- colour at all.
64
+ colour at all. Keep the accent light enough for ink text, too:
65
+ `--hai-on-accent` writes on it and on the amber `--hai-warn` alike.
61
66
 
62
67
  | | |
63
68
  |---|---|
@@ -65,9 +70,9 @@ colour at all.
65
70
  | `--hai-fg` `--hai-dim` | text, secondary text |
66
71
  | `--hai-line` `--hai-line-soft` | borders, row dividers |
67
72
  | `--hai-accent` `--hai-on-accent` | the accent, and text drawn on it |
68
- | `--hai-good` `--hai-warn` `--hai-bad` | status colours; each, and the accent, has a `-soft` tint for backgrounds |
73
+ | `--hai-good` `--hai-warn` `--hai-bad` | status colours; each, and the accent, has a `-soft` tint for backgrounds. `--hai-warn` is a fill: write on it in `--hai-on-accent` |
69
74
  | `--hai-hover` `--hai-selected` | row hover and picked-row backgrounds, for your surfaces |
70
- | `--hai-user-bg` `--hai-code-bg` `--hai-shadow` | user bubble, digest blocks, card shadow |
75
+ | `--hai-user-bg` `--hai-code-bg` `--hai-shadow` | user messages and notice labels, code blocks, the offset card shadow |
71
76
  | `--hai-radius` `--hai-sans` `--hai-mono` | shape and type |
72
77
  | `--hai-column` `--hai-inspector-width` | conversation width, debug drawer width |
73
78
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haikit/client",
3
- "version": "0.21.0",
3
+ "version": "0.22.0",
4
4
  "description": "The haikit browser runtime: streams tool payloads into interactive UI surfaces. Plain ESM, no build step, no dependencies.",
5
5
  "keywords": [
6
6
  "haikit",
package/src/hai.css CHANGED
@@ -9,6 +9,12 @@
9
9
  * framework starts dictating what your UI looks like. They can use the tokens,
10
10
  * though: --hai-hover and --hai-selected are there for rows and pickers.
11
11
  *
12
+ * Neo-brutalist: hard 3px ink borders, offset shadows, square corners. Amber
13
+ * means "waiting on you"; the logo's orange is the accent. The faces are Space
14
+ * Grotesk and Space Mono, which this sheet names but doesn't load: link them
15
+ * on your page, or the system's own faces stand in. The design, every token
16
+ * and state: docs/design/default-ui-tokens-and-states.pdf in HaiKIT's repo.
17
+ *
12
18
  * Light and dark, following the system. Every colour is a custom property set
13
19
  * here at zero specificity, so a plain `:root` rule in your own sheet wins.
14
20
  * Give one value per scheme with light-dark(), behind a plain dark value for
@@ -16,9 +22,12 @@
16
22
  *
17
23
  * :root { --hai-accent: #b07cff; }
18
24
  * @supports (color: light-dark(#000, #fff)) {
19
- * :root { --hai-accent: light-dark(#6d28d9, #b07cff); }
25
+ * :root { --hai-accent: light-dark(#9f7aea, #b07cff); }
20
26
  * }
21
27
  *
28
+ * Keep an accent light enough for ink: --hai-on-accent writes on it and on
29
+ * --hai-warn alike, so it can't turn white for a dark accent alone.
30
+ *
22
31
  * <html data-hai-theme="dark"> pins the page to one scheme, as does
23
32
  * `mountChat({ theme: "dark" })`. The pin is on the root because the tokens
24
33
  * are: a minifier that lowers light-dark() resolves them there.
@@ -27,36 +36,37 @@
27
36
  :where(:root) {
28
37
  color-scheme: light dark;
29
38
 
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));
39
+ --hai-bg: light-dark(#fff3c4, #0f1115);
40
+ --hai-panel: light-dark(#ffffff, #1a1d23);
41
+ --hai-panel-2: light-dark(#fffaf0, #15171c);
42
+ --hai-line: light-dark(#0f1115, #f3f2ee);
43
+ --hai-fg: light-dark(#0f1115, #f3f2ee);
44
+ --hai-dim: light-dark(#4d4a42, #bdbab1);
45
+ --hai-accent: #ff5a1f;
46
+ --hai-on-accent: #0f1115;
47
+ --hai-good: light-dark(#1fa35a, #6ee39a);
48
+ --hai-warn: #ffd23f;
49
+ --hai-bad: light-dark(#c4262b, #ff7a72);
50
+ --hai-user-bg: light-dark(#7cc6ff, #23506f);
51
+ --hai-code-bg: light-dark(#f6efdc, #0b0c0f);
52
+ --hai-selected: light-dark(#c9f5cf, #1d3b2a);
53
+ --hai-shadow: 4px 4px 0 var(--hai-line);
44
54
  --hai-logo-ink: light-dark(#0f1115, #f3f2ee);
45
55
  --hai-logo-paper: light-dark(#f3f2ee, #0f1115);
46
56
  --hai-logo-hot: #ff5a1f;
47
57
 
48
- /* tints, derived — override the colour and these follow */
58
+ /* tints, derived — override the colour and these follow. The soft ones
59
+ are solid, so a card reads the same on the page as on a panel. */
49
60
  --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;
61
+ --hai-hover: light-dark(color-mix(in srgb, var(--hai-warn) 35%, transparent), color-mix(in srgb, var(--hai-warn) 20%, transparent));
62
+ --hai-accent-soft: color-mix(in oklab, var(--hai-accent) 25%, var(--hai-panel));
63
+ --hai-good-soft: color-mix(in oklab, var(--hai-good) 25%, var(--hai-panel));
64
+ --hai-warn-soft: color-mix(in oklab, var(--hai-warn) 25%, var(--hai-panel));
65
+ --hai-bad-soft: color-mix(in oklab, var(--hai-bad) 25%, var(--hai-panel));
66
+
67
+ --hai-radius: 0;
68
+ --hai-mono: "Space Mono", ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace;
69
+ --hai-sans: "Space Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
60
70
  --hai-column: 780px;
61
71
  --hai-inspector-width: 400px;
62
72
  }
@@ -68,20 +78,18 @@
68
78
  @supports not (color: light-dark(#000, #fff)) {
69
79
  :where(:root) {
70
80
  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);
81
+ --hai-bg: #0f1115;
82
+ --hai-panel: #1a1d23;
83
+ --hai-panel-2: #15171c;
84
+ --hai-line: #f3f2ee;
85
+ --hai-fg: #f3f2ee;
86
+ --hai-dim: #bdbab1;
87
+ --hai-good: #6ee39a;
88
+ --hai-bad: #ff7a72;
89
+ --hai-user-bg: #23506f;
90
+ --hai-code-bg: #0b0c0f;
91
+ --hai-selected: #1d3b2a;
92
+ --hai-hover: color-mix(in srgb, var(--hai-warn) 20%, transparent);
85
93
  --hai-logo-ink: #f3f2ee;
86
94
  --hai-logo-paper: #0f1115;
87
95
  }
@@ -109,9 +117,9 @@
109
117
  display: flex;
110
118
  align-items: center;
111
119
  gap: 12px;
112
- height: 52px;
120
+ height: 56px;
113
121
  padding: 0 10px 0 16px;
114
- border-bottom: 1px solid var(--hai-line);
122
+ border-bottom: 3px solid var(--hai-line);
115
123
  background: var(--hai-panel);
116
124
  }
117
125
  .hai-brand { display: flex; align-items: center; gap: 10px; min-width: 0; }
@@ -120,13 +128,13 @@
120
128
  .hai-logo-paper { fill: var(--hai-logo-paper); }
121
129
  .hai-logo-paper-dim { fill: var(--hai-logo-paper); opacity: .5; }
122
130
  .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; }
131
+ .hai-mark { width: 22px; height: 22px; }
132
+ .hai-title { font-size: 16px; font-weight: 700; letter-spacing: -.01em; white-space: nowrap; }
125
133
  .hai-subtitle {
126
134
  color: var(--hai-dim);
127
135
  font-size: 13px;
128
136
  padding-left: 10px;
129
- border-left: 1px solid var(--hai-line);
137
+ border-left: 2px solid var(--hai-line);
130
138
  overflow: hidden;
131
139
  text-overflow: ellipsis;
132
140
  white-space: nowrap;
@@ -138,48 +146,55 @@
138
146
  align-items: center;
139
147
  gap: 6px;
140
148
  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);
149
+ padding: 2px 8px;
150
+ border: 2px solid var(--hai-line);
151
+ border-radius: var(--hai-radius);
152
+ font: 700 11.5px var(--hai-mono);
153
+ }
154
+ .hai-model::before {
155
+ content: "";
156
+ width: 10px;
157
+ height: 10px;
158
+ box-sizing: border-box;
159
+ border: 2px solid;
160
+ border-radius: 50%;
161
+ background: var(--hai-good);
146
162
  }
147
- .hai-model::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--hai-good); }
148
163
  .hai-model[hidden] { display: none; }
149
164
 
150
165
  .hai-btn {
151
166
  display: inline-flex;
152
167
  align-items: center;
153
168
  gap: 7px;
154
- height: 32px;
169
+ height: 34px;
155
170
  padding: 0 10px;
156
- border: none;
157
- border-radius: 8px;
171
+ border: 2px solid transparent;
172
+ border-radius: var(--hai-radius);
158
173
  background: none;
159
174
  color: var(--hai-dim);
160
175
  font: inherit;
161
176
  font-size: 13px;
162
- font-weight: 500;
177
+ font-weight: 600;
163
178
  cursor: pointer;
164
179
  }
165
- .hai-btn:hover:not(:disabled) { background: var(--hai-hover); color: var(--hai-fg); }
180
+ .hai-btn:hover:not(:disabled),
181
+ .hai-btn[aria-expanded="true"] { border-color: var(--hai-line); background: var(--hai-hover); color: var(--hai-fg); }
166
182
  .hai-btn:disabled { opacity: .4; cursor: default; }
167
- .hai-btn[aria-expanded="true"] { color: var(--hai-fg); background: var(--hai-hover); }
168
183
  .hai-ctx-count { font: 11.5px var(--hai-mono); color: var(--hai-dim); }
169
184
  .hai-ctx-count:empty { display: none; }
170
185
 
171
186
  .hai-icon-btn {
172
187
  display: grid;
173
188
  place-items: center;
174
- width: 30px;
175
- height: 30px;
176
- border: none;
177
- border-radius: 8px;
189
+ width: 32px;
190
+ height: 32px;
191
+ border: 2px solid transparent;
192
+ border-radius: var(--hai-radius);
178
193
  background: none;
179
194
  color: var(--hai-dim);
180
195
  cursor: pointer;
181
196
  }
182
- .hai-icon-btn:hover { background: var(--hai-hover); color: var(--hai-fg); }
197
+ .hai-icon-btn:hover { border-color: var(--hai-line); background: var(--hai-hover); color: var(--hai-fg); }
183
198
 
184
199
  /* Clips the drawer while it waits off the right edge. `clip`, not just
185
200
  `hidden`: a hidden box still scrolls, so bringing anything into view
@@ -213,7 +228,7 @@
213
228
  /* ── empty state ────────────────────────────────────────────────────── */
214
229
  .hai-empty { color: var(--hai-dim); }
215
230
  .hai-empty p { margin: 0; max-width: 54ch; font-size: 15px; line-height: 1.6; }
216
- .hai-empty-mark { width: 44px; height: 44px; margin-bottom: 20px; }
231
+ .hai-empty-mark { width: 64px; height: 64px; margin-bottom: 20px; }
217
232
  /* alone, it greets; after what init showed, it stays out of the way */
218
233
  .hai-empty:first-child {
219
234
  display: flex;
@@ -222,23 +237,27 @@
222
237
  text-align: center;
223
238
  padding-top: clamp(24px, 14vh, 140px);
224
239
  }
240
+ .hai-empty:first-child p { color: var(--hai-fg); font-size: 18px; font-weight: 700; line-height: 1.45; letter-spacing: -.01em; }
225
241
  .hai-empty:not(:first-child) .hai-empty-mark { display: none; }
226
242
 
227
- .hai-suggestions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 20px; }
243
+ .hai-suggestions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 24px; }
228
244
  .hai-empty:first-child .hai-suggestions { justify-content: center; }
229
245
  .hai-empty:not(:first-child) .hai-suggestions { margin-top: 12px; }
230
246
  .hai-suggestion {
231
- padding: 7px 14px;
232
- border: 1px solid var(--hai-line);
233
- border-radius: 999px;
247
+ padding: 8px 16px;
248
+ border: 3px solid var(--hai-line);
249
+ border-radius: var(--hai-radius);
234
250
  background: var(--hai-panel);
251
+ box-shadow: var(--hai-shadow);
235
252
  color: var(--hai-fg);
236
253
  font: inherit;
237
- font-size: 13.5px;
254
+ font-size: 14px;
255
+ font-weight: 500;
238
256
  cursor: pointer;
239
- transition: border-color .15s, background-color .15s;
240
257
  }
241
- .hai-suggestion:hover { border-color: var(--hai-accent); background: var(--hai-accent-soft); }
258
+ /* a solid button takes the hover tint over its own colour */
259
+ .hai-suggestion:hover,
260
+ .hai-new:hover { background-image: linear-gradient(var(--hai-hover) 0 0); }
242
261
 
243
262
  /* ── transcript blocks ──────────────────────────────────────────────── */
244
263
  .hai-block { margin: 0 0 18px; }
@@ -255,69 +274,73 @@
255
274
  .hai-user .hai-body {
256
275
  max-width: min(85%, 62ch);
257
276
  padding: 9px 14px;
258
- border-radius: 18px 18px 6px 18px;
277
+ border: 3px solid var(--hai-line);
278
+ border-radius: var(--hai-radius);
259
279
  background: var(--hai-user-bg);
280
+ box-shadow: var(--hai-shadow);
260
281
  }
261
282
 
262
283
  /* an answer given in a surface — it went back to the model as the tool result */
263
- .hai-interaction { display: flex; align-items: flex-start; gap: 10px; max-width: 72ch; font-size: 13.5px; color: var(--hai-dim); }
264
- .hai-arrow {
265
- flex: none;
266
- display: grid;
267
- place-items: center;
268
- width: 22px;
269
- height: 22px;
270
- margin-top: -1px;
271
- border-radius: 50%;
272
- background: var(--hai-good-soft);
273
- color: var(--hai-good);
274
- }
275
- .hai-arrow .hai-icon { width: 13px; height: 13px; }
284
+ .hai-interaction { display: flex; align-items: flex-start; gap: 8px; max-width: 72ch; font: 13px/1.6 var(--hai-mono); color: var(--hai-dim); }
285
+ .hai-arrow { flex: none; display: grid; place-items: center; width: 18px; height: 21px; color: var(--hai-fg); }
286
+ .hai-arrow .hai-icon { width: 15px; height: 15px; stroke-width: 2.5; }
276
287
 
277
288
  .hai-error,
278
289
  .hai-expired {
279
290
  display: flex;
280
291
  align-items: flex-start;
281
292
  gap: 10px;
282
- padding: 11px 14px;
283
- border: 1px solid;
293
+ padding: 12px 16px;
294
+ border: 3px solid var(--hai-line);
284
295
  border-radius: var(--hai-radius);
296
+ color: var(--hai-fg);
285
297
  }
286
- .hai-error .hai-icon,
287
- .hai-expired .hai-icon { margin-top: 2px; }
298
+ .hai-error .hai-icon { margin-top: 3px; }
288
299
  .hai-error {
289
- border-color: color-mix(in srgb, var(--hai-bad) 35%, transparent);
290
300
  background: var(--hai-bad-soft);
291
- color: var(--hai-bad);
292
- font: 12.5px/1.55 var(--hai-mono);
301
+ box-shadow: 4px 4px 0 var(--hai-bad);
302
+ line-height: 1.55;
293
303
  }
294
304
  .hai-error .hai-body { overflow-wrap: anywhere; }
295
305
 
296
- /* tool provenance row — chrome for the surface directly beneath it */
306
+ /* tool provenance row — chrome for the surface directly beneath it. Rows
307
+ that want something (running, awaiting you, failed) stand out on a shadow;
308
+ settled ones lie flat. */
297
309
  .hai-tool {
298
- border: 1px solid var(--hai-line);
310
+ border: 3px solid var(--hai-line);
299
311
  border-radius: var(--hai-radius);
300
312
  background: var(--hai-panel-2);
301
313
  overflow: hidden;
302
314
  }
303
315
  .hai-tool:has(+ .hai-surface) { margin-bottom: 0; border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
316
+ /* a row and its live surface are one card, under one shadow */
317
+ .hai-tool:has(+ .hai-surface:not([data-expired])) { box-shadow: var(--hai-shadow); }
304
318
  .hai-tool summary {
305
319
  display: flex;
306
320
  align-items: center;
307
- gap: 9px;
308
- min-height: 36px;
309
- padding: 7px 12px;
321
+ gap: 10px;
322
+ min-height: 44px;
323
+ padding: 8px 14px;
310
324
  box-sizing: border-box;
311
325
  cursor: pointer;
312
326
  list-style: none;
313
- font-size: 12.5px;
327
+ font-size: 13px;
314
328
  user-select: none;
315
329
  }
316
330
  .hai-tool summary::-webkit-details-marker { display: none; }
317
331
  .hai-tool summary:hover { background: var(--hai-hover); }
318
- .hai-ticon { flex: none; display: grid; place-items: center; width: 16px; height: 16px; color: var(--hai-dim); }
319
- .hai-ticon .hai-icon { width: 14px; height: 14px; }
320
- .hai-tname { font: 500 12.5px var(--hai-mono); }
332
+ /* where the call stands, as a dot; the glyph the row carries goes unshown */
333
+ .hai-ticon {
334
+ flex: none;
335
+ width: 12px;
336
+ height: 12px;
337
+ box-sizing: border-box;
338
+ border: 2px solid;
339
+ border-radius: 50%;
340
+ background: var(--hai-good);
341
+ }
342
+ .hai-ticon > * { display: none; }
343
+ .hai-tname { font: 700 13px var(--hai-mono); }
321
344
  .hai-targs {
322
345
  flex: 1;
323
346
  min-width: 0;
@@ -331,65 +354,73 @@
331
354
  max-width: 50%;
332
355
  overflow: hidden;
333
356
  text-overflow: ellipsis;
334
- color: var(--hai-dim);
357
+ padding: 1px 8px;
358
+ border: 2px solid var(--hai-line);
359
+ background: var(--hai-panel);
360
+ color: var(--hai-fg);
335
361
  white-space: nowrap;
362
+ font: 700 12px var(--hai-mono);
336
363
  font-variant-numeric: tabular-nums;
337
364
  }
338
365
  /* a running tool's progress: the message gives way before the count does */
339
366
  .hai-tprogress { display: flex; min-width: 0; white-space: pre; }
340
367
  .hai-tmessage { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
341
368
  .hai-tcount { flex: none; }
342
- /* …and a bar along the bottom edge of its row */
369
+ /* …and a bar along the bottom edge of its row, the accent edged in ink */
343
370
  .hai-tool summary:has(.hai-tbar) { position: relative; }
344
371
  .hai-tbar {
345
372
  position: absolute;
346
373
  inset: auto 0 0 0;
347
- height: 2px;
348
- background: linear-gradient(to right, var(--hai-accent) var(--hai-progress, 0%), transparent 0);
374
+ height: 7px;
375
+ background:
376
+ linear-gradient(to right, var(--hai-accent) calc(var(--hai-progress, 0%) - 3px), transparent 0) 0 3px no-repeat,
377
+ linear-gradient(to right, var(--hai-line) var(--hai-progress, 0%), transparent 0);
349
378
  }
350
- .hai-chevron { width: 14px; height: 14px; color: var(--hai-dim); transition: transform .15s; }
379
+ .hai-chevron { width: 14px; height: 14px; stroke-width: 3; transition: transform .15s; }
351
380
  .hai-tool[open] .hai-chevron { transform: rotate(90deg); }
352
381
 
353
- .hai-status-ok .hai-ticon { color: var(--hai-good); }
354
- .hai-status-running .hai-tstatus { color: var(--hai-accent); }
355
- .hai-status-awaiting .hai-tstatus { color: var(--hai-warn); }
356
- .hai-status-error .hai-ticon,
357
- .hai-status-error .hai-tstatus { color: var(--hai-bad); }
382
+ .hai-tool.hai-status-running { background: var(--hai-panel); box-shadow: var(--hai-shadow); }
383
+ .hai-status-running .hai-ticon { background: var(--hai-accent); animation: hai-blink 1.2s infinite both; }
358
384
  /* the component is the question: the turn is parked on it */
359
- .hai-tool.hai-status-awaiting { background: var(--hai-warn-soft); }
360
- .hai-tool.hai-status-awaiting,
361
- .hai-tool.hai-status-awaiting + .hai-surface { border-color: color-mix(in srgb, var(--hai-warn) 45%, var(--hai-line)); }
362
-
363
- .hai-tool-detail { padding: 0 12px 12px; border-top: 1px solid var(--hai-line); }
364
- .hai-label { margin: 10px 0 5px; font: 11px var(--hai-mono); color: var(--hai-dim); }
385
+ .hai-tool.hai-status-awaiting { background: var(--hai-warn); box-shadow: var(--hai-shadow); }
386
+ /* amber is as light in either scheme, so what's written on it is ink */
387
+ .hai-status-awaiting summary {
388
+ --hai-dim: color-mix(in srgb, var(--hai-on-accent) 70%, transparent);
389
+ color: var(--hai-on-accent);
390
+ }
391
+ .hai-status-awaiting .hai-ticon { background: var(--hai-accent); }
392
+ .hai-status-resolved .hai-tstatus { background: var(--hai-selected); }
393
+ .hai-tool.hai-status-expired { background: color-mix(in srgb, var(--hai-dim) 10%, var(--hai-panel-2)); }
394
+ .hai-status-expired .hai-ticon { background: color-mix(in srgb, var(--hai-dim) 60%, var(--hai-bg)); }
395
+ .hai-status-expired .hai-tstatus { background: none; }
396
+ .hai-tool.hai-status-error { background: var(--hai-bad-soft); box-shadow: 4px 4px 0 var(--hai-bad); }
397
+ /* hover over the pink, in the dark scheme, leaves dim text too faint */
398
+ .hai-status-error summary:hover .hai-targs { color: var(--hai-fg); }
399
+ .hai-status-error .hai-ticon { background: var(--hai-bad); }
400
+
401
+ .hai-tool-detail { padding: 0 14px 14px; border-top: 3px solid var(--hai-line); background: var(--hai-panel-2); }
402
+ .hai-label { margin: 12px 0 6px; font: 700 11px var(--hai-mono); color: var(--hai-dim); }
365
403
  .hai-code,
366
404
  .hai-digest {
367
405
  margin: 0;
368
406
  padding: 9px 11px;
369
407
  max-height: 200px;
370
408
  overflow: auto;
371
- border: 1px solid var(--hai-line);
372
- border-radius: 8px;
409
+ border: 2px solid var(--hai-line);
410
+ border-radius: var(--hai-radius);
373
411
  background: var(--hai-code-bg);
374
412
  font: 12px/1.55 var(--hai-mono);
375
413
  white-space: pre-wrap;
376
414
  overflow-wrap: anywhere;
377
415
  }
378
- .hai-digest { color: var(--hai-good); }
416
+ .hai-digest { background: var(--hai-good-soft); }
379
417
 
380
- .hai-spinner {
418
+ .hai-pulse {
419
+ flex: none;
381
420
  width: 12px;
382
421
  height: 12px;
383
422
  box-sizing: border-box;
384
- border: 2px solid var(--hai-accent-soft);
385
- border-top-color: var(--hai-accent);
386
- border-radius: 50%;
387
- animation: hai-spin .8s linear infinite;
388
- }
389
- .hai-pulse {
390
- flex: none;
391
- width: 8px;
392
- height: 8px;
423
+ border: 2px solid;
393
424
  border-radius: 50%;
394
425
  background: var(--hai-warn);
395
426
  animation: hai-pulse 1.8s ease-out infinite;
@@ -399,68 +430,79 @@
399
430
  .hai-surface {
400
431
  margin-bottom: 18px;
401
432
  overflow: hidden;
402
- border: 1px solid var(--hai-line);
433
+ border: 3px solid var(--hai-line);
403
434
  border-radius: var(--hai-radius);
404
435
  background: var(--hai-panel);
405
436
  box-shadow: var(--hai-shadow);
406
437
  }
407
438
  .hai-tool + .hai-surface { border-top: none; border-top-left-radius: 0; border-top-right-radius: 0; }
408
- .hai-skeleton { display: grid; gap: 9px; padding: 14px; }
409
- .hai-skeleton-label { margin-bottom: 2px; font: 12px var(--hai-mono); color: var(--hai-dim); }
439
+ /* props still arriving: hatched bars, inked first, and the label under them */
440
+ .hai-skeleton { display: grid; gap: 12px; padding: 16px; }
441
+ .hai-skeleton-label { order: 1; font: 12px var(--hai-mono); color: var(--hai-dim); }
410
442
  .hai-shimmer {
411
- height: 10px;
412
- border-radius: 5px;
413
- background: linear-gradient(90deg, var(--hai-hover), var(--hai-line), var(--hai-hover)) 0 0 / 200% 100%;
414
- animation: hai-shimmer 1.4s ease-in-out infinite;
443
+ height: 36px;
444
+ box-sizing: border-box;
445
+ border: 2px solid color-mix(in srgb, var(--hai-dim) 50%, transparent);
446
+ background: linear-gradient(45deg,
447
+ color-mix(in srgb, var(--hai-dim) 50%, transparent) 0 12.5%, transparent 0 50%,
448
+ color-mix(in srgb, var(--hai-dim) 50%, transparent) 0 62.5%, transparent 0) 0 0 / 12px 12px;
449
+ animation: hai-shimmer 1s linear infinite;
450
+ }
451
+ .hai-shimmer:nth-child(2) {
452
+ width: 40%;
453
+ height: 16px;
454
+ border-color: var(--hai-line);
455
+ background-image: linear-gradient(45deg,
456
+ var(--hai-line) 0 12.5%, transparent 0 50%,
457
+ var(--hai-line) 0 62.5%, transparent 0);
415
458
  }
416
- .hai-shimmer:nth-child(3) { width: 82%; }
417
- .hai-shimmer:nth-child(4) { width: 60%; }
418
459
  /* a notice: something the server said outside of any request */
419
460
  .hai-notice {
420
461
  margin-bottom: 18px;
421
462
  overflow: hidden;
422
- border: 1px solid var(--hai-line);
463
+ border: 3px solid var(--hai-line);
423
464
  border-radius: var(--hai-radius);
424
465
  background: var(--hai-panel);
466
+ box-shadow: var(--hai-shadow);
425
467
  }
426
468
  .hai-notice-label {
427
469
  padding: 6px 12px;
428
- border-bottom: 1px solid var(--hai-line);
429
- background: var(--hai-panel-2);
430
- font: 11px var(--hai-mono);
431
- color: var(--hai-dim);
470
+ border-bottom: 3px solid var(--hai-line);
471
+ background: var(--hai-user-bg);
472
+ font: 700 11px var(--hai-mono);
432
473
  }
433
- .hai-notice > .hai-surface-error { margin: 0; border: none; border-radius: 0; }
474
+ .hai-notice > .hai-surface-error { margin: 0; border: none; border-radius: 0; box-shadow: none; }
434
475
  .hai-surface-error {
435
476
  margin-bottom: 18px;
436
- padding: 11px 14px;
437
- border: 1px solid color-mix(in srgb, var(--hai-bad) 35%, transparent);
477
+ padding: 12px 16px;
478
+ border: 3px solid var(--hai-line);
438
479
  border-radius: var(--hai-radius);
439
480
  background: var(--hai-bad-soft);
440
- color: var(--hai-bad);
481
+ box-shadow: 4px 4px 0 var(--hai-bad);
441
482
  font: 12.5px var(--hai-mono);
442
483
  }
443
484
 
444
485
  /* out of date — the runtime has made the surface inert; this only shows it */
445
- .hai-surface[data-expired] { opacity: .45; filter: grayscale(.7); }
486
+ .hai-surface[data-expired] { opacity: .45; filter: grayscale(.7); box-shadow: none; }
446
487
  .hai-expired {
447
488
  flex-wrap: wrap;
448
489
  align-items: center;
449
- gap: 10px 12px;
450
- border-color: color-mix(in srgb, var(--hai-warn) 45%, transparent);
451
- background: var(--hai-warn-soft);
452
- color: var(--hai-warn);
490
+ gap: 14px;
491
+ padding-bottom: 16px;
492
+ border-style: dashed;
493
+ background: var(--hai-panel);
453
494
  }
454
- .hai-expired .hai-icon { margin-top: 0; }
455
- .hai-expired .hai-body { flex: 1 1 30ch; }
495
+ .hai-expired .hai-icon { display: none; }
496
+ .hai-expired .hai-body { flex: 1 1 100%; }
456
497
  .hai-new {
457
- padding: 7px 14px;
458
- border: none;
459
- border-radius: 8px;
460
- background: var(--hai-warn);
498
+ padding: 8px 16px;
499
+ border: 3px solid var(--hai-line);
500
+ border-radius: var(--hai-radius);
501
+ background: var(--hai-accent);
502
+ box-shadow: var(--hai-shadow);
461
503
  color: var(--hai-on-accent);
462
504
  font: inherit;
463
- font-weight: 600;
505
+ font-weight: 700;
464
506
  cursor: pointer;
465
507
  }
466
508
 
@@ -470,29 +512,35 @@
470
512
  align-items: center;
471
513
  gap: 8px;
472
514
  min-height: 22px;
473
- padding: 0 var(--hai-gutter) 6px;
474
- font-size: 12.5px;
475
- color: var(--hai-dim);
515
+ padding: 0 var(--hai-gutter) 8px;
516
+ font: 700 12.5px var(--hai-mono);
476
517
  }
477
- .hai-status[data-status="awaiting"] { color: var(--hai-warn); }
478
- .hai-dots { display: inline-flex; gap: 3px; }
479
- .hai-dots i { width: 5px; height: 5px; border-radius: 50%; background: currentColor; animation: hai-blink 1.2s infinite both; }
480
- .hai-dots i:nth-child(2) { animation-delay: .2s; }
481
- .hai-dots i:nth-child(3) { animation-delay: .4s; }
518
+ /* thinking: one accent dot, blinking */
519
+ .hai-dots { display: inline-flex; }
520
+ .hai-dots i {
521
+ width: 12px;
522
+ height: 12px;
523
+ box-sizing: border-box;
524
+ border: 2px solid;
525
+ border-radius: 50%;
526
+ background: var(--hai-accent);
527
+ animation: hai-blink 1.2s infinite both;
528
+ }
529
+ .hai-dots i + i { display: none; }
482
530
 
483
- .hai-composer { flex: none; padding: 0 var(--hai-gutter) 18px; }
531
+ .hai-composer { flex: none; padding: 0 var(--hai-gutter) 20px; }
484
532
  .hai-composer-box {
485
533
  display: flex;
486
534
  align-items: flex-end;
487
535
  gap: 8px;
488
- padding: 7px 7px 7px 14px;
489
- border: 1px solid var(--hai-line);
490
- border-radius: 16px;
536
+ padding: 6px 6px 6px 14px;
537
+ border: 3px solid var(--hai-line);
538
+ border-radius: var(--hai-radius);
491
539
  background: var(--hai-panel);
492
540
  box-shadow: var(--hai-shadow);
493
- transition: border-color .15s, box-shadow .15s;
541
+ transition: box-shadow .15s;
494
542
  }
495
- .hai-composer-box:focus-within { border-color: var(--hai-accent); box-shadow: 0 0 0 3px var(--hai-accent-soft); }
543
+ .hai-composer-box:focus-within { box-shadow: 4px 4px 0 var(--hai-accent); }
496
544
  .hai-input {
497
545
  flex: 1;
498
546
  box-sizing: border-box;
@@ -517,14 +565,14 @@
517
565
  place-items: center;
518
566
  width: 34px;
519
567
  height: 34px;
520
- border: none;
521
- border-radius: 10px;
568
+ border: 2px solid var(--hai-line);
569
+ border-radius: var(--hai-radius);
522
570
  background: var(--hai-accent);
523
571
  color: var(--hai-on-accent);
524
572
  cursor: pointer;
525
573
  transition: opacity .15s;
526
574
  }
527
- .hai-send .hai-icon { width: 18px; height: 18px; }
575
+ .hai-send .hai-icon { width: 18px; height: 18px; stroke-width: 2.5; }
528
576
  .hai-send:disabled { opacity: .35; cursor: default; }
529
577
 
530
578
  /* ── model context debug drawer ─────────────────────────────────────── */
@@ -538,9 +586,8 @@
538
586
  flex-direction: column;
539
587
  width: min(100%, var(--hai-inspector-width));
540
588
  min-height: 0;
541
- border-left: 1px solid var(--hai-line);
589
+ border-left: 3px solid var(--hai-line);
542
590
  background: var(--hai-panel-2);
543
- box-shadow: -16px 0 32px -12px rgb(0 0 0 / .25);
544
591
  transform: translateX(100%);
545
592
  visibility: hidden;
546
593
  transition: transform .25s cubic-bezier(.2, .8, .2, 1), visibility 0s .25s;
@@ -550,8 +597,8 @@
550
597
  visibility: visible;
551
598
  transition: transform .25s cubic-bezier(.2, .8, .2, 1), visibility 0s;
552
599
  }
553
- .hai-inspector-head { display: flex; align-items: center; gap: 8px; padding: 10px 10px 10px 16px; border-bottom: 1px solid var(--hai-line); }
554
- .hai-inspector-title { flex: 1; display: flex; flex-direction: column; font-size: 13px; font-weight: 600; }
600
+ .hai-inspector-head { display: flex; align-items: center; gap: 8px; padding: 10px 10px 10px 16px; border-bottom: 3px solid var(--hai-line); }
601
+ .hai-inspector-title { flex: 1; display: flex; flex-direction: column; font-size: 13px; font-weight: 700; }
555
602
  .hai-inspector-title span { color: var(--hai-dim); font-size: 12px; font-weight: 400; }
556
603
  .hai-inspector-body {
557
604
  flex: 1;
@@ -568,28 +615,28 @@
568
615
  top: 0;
569
616
  z-index: 1;
570
617
  padding: 14px 0 12px;
571
- border-bottom: 1px solid var(--hai-line);
618
+ border-bottom: 3px solid var(--hai-line);
572
619
  background: var(--hai-panel-2);
573
620
  }
574
621
  .hai-ctx-headline { display: flex; align-items: baseline; gap: 8px; margin-bottom: 8px; font-size: 12.5px; color: var(--hai-dim); }
575
- .hai-ctx-pct { font-size: 26px; font-weight: 650; letter-spacing: -.02em; color: var(--hai-good); font-variant-numeric: tabular-nums; }
576
- .hai-meter { display: flex; height: 6px; margin-bottom: 10px; overflow: hidden; border-radius: 3px; background: var(--hai-good); }
577
- .hai-meter-model { min-width: 2px; background: var(--hai-accent); }
622
+ .hai-ctx-pct { font-size: 26px; font-weight: 700; letter-spacing: -.02em; color: var(--hai-fg); font-variant-numeric: tabular-nums; }
623
+ .hai-meter { display: flex; height: 10px; margin-bottom: 10px; overflow: hidden; border: 2px solid var(--hai-line); border-radius: var(--hai-radius); background: var(--hai-good); }
624
+ .hai-meter-model { min-width: 2px; border-right: 2px solid var(--hai-line); background: var(--hai-accent); }
578
625
  .hai-ctx-stat { display: flex; justify-content: space-between; padding: 1px 0; font: 12px var(--hai-mono); }
579
626
  .hai-ctx-label { display: inline-flex; align-items: center; gap: 7px; color: var(--hai-dim); }
580
627
  .hai-ctx-tok { font-variant-numeric: tabular-nums; }
581
- .hai-key { width: 8px; height: 8px; border-radius: 2px; }
628
+ .hai-key { width: 10px; height: 10px; box-sizing: border-box; border: 2px solid var(--hai-line); border-radius: var(--hai-radius); }
582
629
  .hai-key-model { background: var(--hai-accent); }
583
630
  .hai-key-ui { background: var(--hai-good); }
584
631
 
585
- .hai-msg { margin-top: 14px; padding-left: 11px; border-left: 2px solid var(--hai-line); }
586
- .hai-role { margin-bottom: 4px; font: 600 10px var(--hai-mono); text-transform: uppercase; letter-spacing: .08em; color: var(--hai-dim); }
632
+ .hai-msg { margin-top: 14px; padding-left: 11px; border-left: 3px solid var(--hai-line); }
633
+ .hai-role { margin-bottom: 4px; font: 700 10px var(--hai-mono); text-transform: uppercase; letter-spacing: .08em; color: var(--hai-dim); }
587
634
  .hai-mbody { display: grid; gap: 6px; font: 12px/1.55 var(--hai-mono); white-space: pre-wrap; overflow-wrap: anywhere; }
588
635
  .hai-tu,
589
- .hai-tr { padding: 6px 8px; border-radius: 6px; }
590
- .hai-tu { background: var(--hai-accent-soft); color: var(--hai-accent); }
591
- .hai-tr { background: var(--hai-good-soft); color: var(--hai-good); }
592
- .hai-kind { display: block; margin-bottom: 2px; font-size: 10.5px; opacity: .75; }
636
+ .hai-tr { padding: 6px 8px; border: 2px solid var(--hai-line); border-radius: var(--hai-radius); }
637
+ .hai-tu { background: var(--hai-accent-soft); }
638
+ .hai-tr { background: var(--hai-good-soft); }
639
+ .hai-kind { display: block; margin-bottom: 2px; font-size: 10.5px; font-weight: 700; opacity: .75; }
593
640
 
594
641
  /* ── narrow screens ─────────────────────────────────────────────────── */
595
642
  @media (max-width: 899.98px) {
@@ -615,24 +662,23 @@
615
662
  .hai-icon-btn:focus-visible,
616
663
  .hai-send:focus-visible,
617
664
  .hai-suggestion:focus-visible,
618
- .hai-new:focus-visible { outline: 2px solid var(--hai-accent); outline-offset: 2px; }
619
- .hai-tool summary:focus-visible { outline: 2px solid var(--hai-accent); outline-offset: -2px; }
665
+ .hai-new:focus-visible { outline: 3px solid var(--hai-fg); outline-offset: 2px; }
666
+ /* inside the row's border, clear of it, in the row's text colour: ink on amber too */
667
+ .hai-tool summary:focus-visible { outline: 2px solid currentColor; outline-offset: -5px; }
620
668
 
621
- @keyframes hai-spin { to { transform: rotate(360deg); } }
622
669
  @keyframes hai-pulse {
623
670
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--hai-warn) 55%, transparent); }
624
671
  100% { box-shadow: 0 0 0 7px transparent; }
625
672
  }
626
673
  @keyframes hai-blink { 0%, 80%, 100% { opacity: .25; } 40% { opacity: 1; } }
627
- @keyframes hai-shimmer { from { background-position: 100% 0; } to { background-position: -100% 0; } }
674
+ @keyframes hai-shimmer { to { background-position: 12px 0; } }
628
675
 
629
676
  @media (prefers-reduced-motion: reduce) {
630
- .hai-spinner,
677
+ .hai-status-running .hai-ticon,
631
678
  .hai-pulse,
632
679
  .hai-dots i,
633
680
  .hai-shimmer { animation: none; }
634
681
  .hai-chevron,
635
- .hai-suggestion,
636
682
  .hai-composer-box,
637
683
  .hai-chat,
638
684
  .hai-inspector,
package/src/icons.js CHANGED
@@ -13,7 +13,7 @@ const PATHS = {
13
13
  close: "M6 6l12 12M18 6 6 18",
14
14
  chevron: "M9 6l6 6-6 6",
15
15
  check: "M5 12.5l4.5 4.5L19 7.5",
16
- alert: "M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18zM12 7.5v5.5M12 16.5v.01",
16
+ alert: "M4 4h16v16H4zM12 8v5M12 16.5v.01",
17
17
  clock: "M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18zM12 7v5l3.5 2",
18
18
  reply: "M5 4v7a4 4 0 0 0 4 4h10M15 11l4 4-4 4",
19
19
  };