@haikit/client 0.21.0 → 0.22.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/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.1",
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,75 @@
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. The
387
+ arguments are too, in full, and set apart from the bold name by weight:
388
+ dimmed, they'd lose contrast on a darker amber with white text */
389
+ .hai-status-awaiting summary {
390
+ --hai-dim: var(--hai-on-accent);
391
+ color: var(--hai-on-accent);
392
+ }
393
+ .hai-status-awaiting .hai-ticon { background: var(--hai-accent); }
394
+ .hai-status-resolved .hai-tstatus { background: var(--hai-selected); }
395
+ .hai-tool.hai-status-expired { background: color-mix(in srgb, var(--hai-dim) 10%, var(--hai-panel-2)); }
396
+ .hai-status-expired .hai-ticon { background: color-mix(in srgb, var(--hai-dim) 60%, var(--hai-bg)); }
397
+ .hai-status-expired .hai-tstatus { background: none; }
398
+ .hai-tool.hai-status-error { background: var(--hai-bad-soft); box-shadow: 4px 4px 0 var(--hai-bad); }
399
+ /* hover over the pink, in the dark scheme, leaves dim text too faint */
400
+ .hai-status-error summary:hover .hai-targs { color: var(--hai-fg); }
401
+ .hai-status-error .hai-ticon { background: var(--hai-bad); }
402
+
403
+ .hai-tool-detail { padding: 0 14px 14px; border-top: 3px solid var(--hai-line); background: var(--hai-panel-2); }
404
+ .hai-label { margin: 12px 0 6px; font: 700 11px var(--hai-mono); color: var(--hai-dim); }
365
405
  .hai-code,
366
406
  .hai-digest {
367
407
  margin: 0;
368
408
  padding: 9px 11px;
369
409
  max-height: 200px;
370
410
  overflow: auto;
371
- border: 1px solid var(--hai-line);
372
- border-radius: 8px;
411
+ border: 2px solid var(--hai-line);
412
+ border-radius: var(--hai-radius);
373
413
  background: var(--hai-code-bg);
374
414
  font: 12px/1.55 var(--hai-mono);
375
415
  white-space: pre-wrap;
376
416
  overflow-wrap: anywhere;
377
417
  }
378
- .hai-digest { color: var(--hai-good); }
418
+ .hai-digest { background: var(--hai-good-soft); }
379
419
 
380
- .hai-spinner {
420
+ .hai-pulse {
421
+ flex: none;
381
422
  width: 12px;
382
423
  height: 12px;
383
424
  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;
425
+ border: 2px solid;
393
426
  border-radius: 50%;
394
427
  background: var(--hai-warn);
395
428
  animation: hai-pulse 1.8s ease-out infinite;
@@ -399,68 +432,79 @@
399
432
  .hai-surface {
400
433
  margin-bottom: 18px;
401
434
  overflow: hidden;
402
- border: 1px solid var(--hai-line);
435
+ border: 3px solid var(--hai-line);
403
436
  border-radius: var(--hai-radius);
404
437
  background: var(--hai-panel);
405
438
  box-shadow: var(--hai-shadow);
406
439
  }
407
440
  .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); }
441
+ /* props still arriving: hatched bars, inked first, and the label under them */
442
+ .hai-skeleton { display: grid; gap: 12px; padding: 16px; }
443
+ .hai-skeleton-label { order: 1; font: 12px var(--hai-mono); color: var(--hai-dim); }
410
444
  .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;
445
+ height: 36px;
446
+ box-sizing: border-box;
447
+ border: 2px solid color-mix(in srgb, var(--hai-dim) 50%, transparent);
448
+ background: linear-gradient(45deg,
449
+ color-mix(in srgb, var(--hai-dim) 50%, transparent) 0 12.5%, transparent 0 50%,
450
+ color-mix(in srgb, var(--hai-dim) 50%, transparent) 0 62.5%, transparent 0) 0 0 / 12px 12px;
451
+ animation: hai-shimmer 1s linear infinite;
452
+ }
453
+ .hai-shimmer:nth-child(2) {
454
+ width: 40%;
455
+ height: 16px;
456
+ border-color: var(--hai-line);
457
+ background-image: linear-gradient(45deg,
458
+ var(--hai-line) 0 12.5%, transparent 0 50%,
459
+ var(--hai-line) 0 62.5%, transparent 0);
415
460
  }
416
- .hai-shimmer:nth-child(3) { width: 82%; }
417
- .hai-shimmer:nth-child(4) { width: 60%; }
418
461
  /* a notice: something the server said outside of any request */
419
462
  .hai-notice {
420
463
  margin-bottom: 18px;
421
464
  overflow: hidden;
422
- border: 1px solid var(--hai-line);
465
+ border: 3px solid var(--hai-line);
423
466
  border-radius: var(--hai-radius);
424
467
  background: var(--hai-panel);
468
+ box-shadow: var(--hai-shadow);
425
469
  }
426
470
  .hai-notice-label {
427
471
  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);
472
+ border-bottom: 3px solid var(--hai-line);
473
+ background: var(--hai-user-bg);
474
+ font: 700 11px var(--hai-mono);
432
475
  }
433
- .hai-notice > .hai-surface-error { margin: 0; border: none; border-radius: 0; }
476
+ .hai-notice > .hai-surface-error { margin: 0; border: none; border-radius: 0; box-shadow: none; }
434
477
  .hai-surface-error {
435
478
  margin-bottom: 18px;
436
- padding: 11px 14px;
437
- border: 1px solid color-mix(in srgb, var(--hai-bad) 35%, transparent);
479
+ padding: 12px 16px;
480
+ border: 3px solid var(--hai-line);
438
481
  border-radius: var(--hai-radius);
439
482
  background: var(--hai-bad-soft);
440
- color: var(--hai-bad);
483
+ box-shadow: 4px 4px 0 var(--hai-bad);
441
484
  font: 12.5px var(--hai-mono);
442
485
  }
443
486
 
444
487
  /* out of date — the runtime has made the surface inert; this only shows it */
445
- .hai-surface[data-expired] { opacity: .45; filter: grayscale(.7); }
488
+ .hai-surface[data-expired] { opacity: .45; filter: grayscale(.7); box-shadow: none; }
446
489
  .hai-expired {
447
490
  flex-wrap: wrap;
448
491
  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);
492
+ gap: 14px;
493
+ padding-bottom: 16px;
494
+ border-style: dashed;
495
+ background: var(--hai-panel);
453
496
  }
454
- .hai-expired .hai-icon { margin-top: 0; }
455
- .hai-expired .hai-body { flex: 1 1 30ch; }
497
+ .hai-expired .hai-icon { display: none; }
498
+ .hai-expired .hai-body { flex: 1 1 100%; }
456
499
  .hai-new {
457
- padding: 7px 14px;
458
- border: none;
459
- border-radius: 8px;
460
- background: var(--hai-warn);
500
+ padding: 8px 16px;
501
+ border: 3px solid var(--hai-line);
502
+ border-radius: var(--hai-radius);
503
+ background: var(--hai-accent);
504
+ box-shadow: var(--hai-shadow);
461
505
  color: var(--hai-on-accent);
462
506
  font: inherit;
463
- font-weight: 600;
507
+ font-weight: 700;
464
508
  cursor: pointer;
465
509
  }
466
510
 
@@ -470,29 +514,35 @@
470
514
  align-items: center;
471
515
  gap: 8px;
472
516
  min-height: 22px;
473
- padding: 0 var(--hai-gutter) 6px;
474
- font-size: 12.5px;
475
- color: var(--hai-dim);
517
+ padding: 0 var(--hai-gutter) 8px;
518
+ font: 700 12.5px var(--hai-mono);
476
519
  }
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; }
520
+ /* thinking: one accent dot, blinking */
521
+ .hai-dots { display: inline-flex; }
522
+ .hai-dots i {
523
+ width: 12px;
524
+ height: 12px;
525
+ box-sizing: border-box;
526
+ border: 2px solid;
527
+ border-radius: 50%;
528
+ background: var(--hai-accent);
529
+ animation: hai-blink 1.2s infinite both;
530
+ }
531
+ .hai-dots i + i { display: none; }
482
532
 
483
- .hai-composer { flex: none; padding: 0 var(--hai-gutter) 18px; }
533
+ .hai-composer { flex: none; padding: 0 var(--hai-gutter) 20px; }
484
534
  .hai-composer-box {
485
535
  display: flex;
486
536
  align-items: flex-end;
487
537
  gap: 8px;
488
- padding: 7px 7px 7px 14px;
489
- border: 1px solid var(--hai-line);
490
- border-radius: 16px;
538
+ padding: 6px 6px 6px 14px;
539
+ border: 3px solid var(--hai-line);
540
+ border-radius: var(--hai-radius);
491
541
  background: var(--hai-panel);
492
542
  box-shadow: var(--hai-shadow);
493
- transition: border-color .15s, box-shadow .15s;
543
+ transition: box-shadow .15s;
494
544
  }
495
- .hai-composer-box:focus-within { border-color: var(--hai-accent); box-shadow: 0 0 0 3px var(--hai-accent-soft); }
545
+ .hai-composer-box:focus-within { box-shadow: 4px 4px 0 var(--hai-accent); }
496
546
  .hai-input {
497
547
  flex: 1;
498
548
  box-sizing: border-box;
@@ -517,14 +567,14 @@
517
567
  place-items: center;
518
568
  width: 34px;
519
569
  height: 34px;
520
- border: none;
521
- border-radius: 10px;
570
+ border: 2px solid var(--hai-line);
571
+ border-radius: var(--hai-radius);
522
572
  background: var(--hai-accent);
523
573
  color: var(--hai-on-accent);
524
574
  cursor: pointer;
525
575
  transition: opacity .15s;
526
576
  }
527
- .hai-send .hai-icon { width: 18px; height: 18px; }
577
+ .hai-send .hai-icon { width: 18px; height: 18px; stroke-width: 2.5; }
528
578
  .hai-send:disabled { opacity: .35; cursor: default; }
529
579
 
530
580
  /* ── model context debug drawer ─────────────────────────────────────── */
@@ -538,9 +588,8 @@
538
588
  flex-direction: column;
539
589
  width: min(100%, var(--hai-inspector-width));
540
590
  min-height: 0;
541
- border-left: 1px solid var(--hai-line);
591
+ border-left: 3px solid var(--hai-line);
542
592
  background: var(--hai-panel-2);
543
- box-shadow: -16px 0 32px -12px rgb(0 0 0 / .25);
544
593
  transform: translateX(100%);
545
594
  visibility: hidden;
546
595
  transition: transform .25s cubic-bezier(.2, .8, .2, 1), visibility 0s .25s;
@@ -550,8 +599,8 @@
550
599
  visibility: visible;
551
600
  transition: transform .25s cubic-bezier(.2, .8, .2, 1), visibility 0s;
552
601
  }
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; }
602
+ .hai-inspector-head { display: flex; align-items: center; gap: 8px; padding: 10px 10px 10px 16px; border-bottom: 3px solid var(--hai-line); }
603
+ .hai-inspector-title { flex: 1; display: flex; flex-direction: column; font-size: 13px; font-weight: 700; }
555
604
  .hai-inspector-title span { color: var(--hai-dim); font-size: 12px; font-weight: 400; }
556
605
  .hai-inspector-body {
557
606
  flex: 1;
@@ -568,28 +617,28 @@
568
617
  top: 0;
569
618
  z-index: 1;
570
619
  padding: 14px 0 12px;
571
- border-bottom: 1px solid var(--hai-line);
620
+ border-bottom: 3px solid var(--hai-line);
572
621
  background: var(--hai-panel-2);
573
622
  }
574
623
  .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); }
624
+ .hai-ctx-pct { font-size: 26px; font-weight: 700; letter-spacing: -.02em; color: var(--hai-fg); font-variant-numeric: tabular-nums; }
625
+ .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); }
626
+ .hai-meter-model { min-width: 2px; border-right: 2px solid var(--hai-line); background: var(--hai-accent); }
578
627
  .hai-ctx-stat { display: flex; justify-content: space-between; padding: 1px 0; font: 12px var(--hai-mono); }
579
628
  .hai-ctx-label { display: inline-flex; align-items: center; gap: 7px; color: var(--hai-dim); }
580
629
  .hai-ctx-tok { font-variant-numeric: tabular-nums; }
581
- .hai-key { width: 8px; height: 8px; border-radius: 2px; }
630
+ .hai-key { width: 10px; height: 10px; box-sizing: border-box; border: 2px solid var(--hai-line); border-radius: var(--hai-radius); }
582
631
  .hai-key-model { background: var(--hai-accent); }
583
632
  .hai-key-ui { background: var(--hai-good); }
584
633
 
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); }
634
+ .hai-msg { margin-top: 14px; padding-left: 11px; border-left: 3px solid var(--hai-line); }
635
+ .hai-role { margin-bottom: 4px; font: 700 10px var(--hai-mono); text-transform: uppercase; letter-spacing: .08em; color: var(--hai-dim); }
587
636
  .hai-mbody { display: grid; gap: 6px; font: 12px/1.55 var(--hai-mono); white-space: pre-wrap; overflow-wrap: anywhere; }
588
637
  .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; }
638
+ .hai-tr { padding: 6px 8px; border: 2px solid var(--hai-line); border-radius: var(--hai-radius); }
639
+ .hai-tu { background: var(--hai-accent-soft); }
640
+ .hai-tr { background: var(--hai-good-soft); }
641
+ .hai-kind { display: block; margin-bottom: 2px; font-size: 10.5px; font-weight: 700; opacity: .75; }
593
642
 
594
643
  /* ── narrow screens ─────────────────────────────────────────────────── */
595
644
  @media (max-width: 899.98px) {
@@ -615,24 +664,23 @@
615
664
  .hai-icon-btn:focus-visible,
616
665
  .hai-send:focus-visible,
617
666
  .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; }
667
+ .hai-new:focus-visible { outline: 3px solid var(--hai-fg); outline-offset: 2px; }
668
+ /* inside the row's border, clear of it, in the row's text colour: ink on amber too */
669
+ .hai-tool summary:focus-visible { outline: 2px solid currentColor; outline-offset: -5px; }
620
670
 
621
- @keyframes hai-spin { to { transform: rotate(360deg); } }
622
671
  @keyframes hai-pulse {
623
672
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--hai-warn) 55%, transparent); }
624
673
  100% { box-shadow: 0 0 0 7px transparent; }
625
674
  }
626
675
  @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; } }
676
+ @keyframes hai-shimmer { to { background-position: 12px 0; } }
628
677
 
629
678
  @media (prefers-reduced-motion: reduce) {
630
- .hai-spinner,
679
+ .hai-status-running .hai-ticon,
631
680
  .hai-pulse,
632
681
  .hai-dots i,
633
682
  .hai-shimmer { animation: none; }
634
683
  .hai-chevron,
635
- .hai-suggestion,
636
684
  .hai-composer-box,
637
685
  .hai-chat,
638
686
  .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
  };