@runwisp/ui 0.12.0 → 0.14.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.
Files changed (60) hide show
  1. package/package.json +40 -45
  2. package/src/astro/CTABand.astro +19 -12
  3. package/src/astro/Hero.astro +3 -3
  4. package/src/astro/SiteFooter.astro +4 -4
  5. package/src/astro/SiteHeader.astro +4 -4
  6. package/src/lib/assets/runwisp-logo.svg +1 -8
  7. package/src/lib/components/AccordionItem.svelte +4 -10
  8. package/src/lib/components/Alert.svelte +11 -11
  9. package/src/lib/components/Avatar.svelte +1 -1
  10. package/src/lib/components/Badge.svelte +13 -13
  11. package/src/lib/components/Breadcrumb.svelte +3 -3
  12. package/src/lib/components/Button.svelte +2 -2
  13. package/src/lib/components/Card.svelte +4 -4
  14. package/src/lib/components/Checkbox.svelte +12 -10
  15. package/src/lib/components/CodeBlock.svelte +7 -13
  16. package/src/lib/components/CronInput.svelte +12 -11
  17. package/src/lib/components/DataGrid.svelte +14 -11
  18. package/src/lib/components/Drawer.svelte +9 -6
  19. package/src/lib/components/Dropdown.svelte +10 -14
  20. package/src/lib/components/DurationInput.svelte +10 -14
  21. package/src/lib/components/EmptyState.svelte +1 -1
  22. package/src/lib/components/ErrorState.svelte +4 -4
  23. package/src/lib/components/FeatureCard.svelte +4 -4
  24. package/src/lib/components/FormField.svelte +11 -6
  25. package/src/lib/components/Heading.svelte +4 -1
  26. package/src/lib/components/Input.svelte +10 -12
  27. package/src/lib/components/Kbd.svelte +1 -1
  28. package/src/lib/components/Link.svelte +2 -3
  29. package/src/lib/components/LinkButton.svelte +2 -2
  30. package/src/lib/components/LogConsole.svelte +22 -21
  31. package/src/lib/components/Logo.svelte +3 -1
  32. package/src/lib/components/Modal.svelte +9 -8
  33. package/src/lib/components/PageContainer.svelte +1 -1
  34. package/src/lib/components/PageHeader.svelte +1 -1
  35. package/src/lib/components/Pagination.svelte +4 -4
  36. package/src/lib/components/Popover.svelte +5 -7
  37. package/src/lib/components/Progress.svelte +6 -4
  38. package/src/lib/components/Prose.svelte +4 -4
  39. package/src/lib/components/Radio.svelte +11 -10
  40. package/src/lib/components/RadioGroup.svelte +3 -5
  41. package/src/lib/components/Select.svelte +21 -31
  42. package/src/lib/components/Separator.svelte +4 -1
  43. package/src/lib/components/Skeleton.svelte +6 -6
  44. package/src/lib/components/StatusIndicator.svelte +7 -15
  45. package/src/lib/components/Table.svelte +1 -1
  46. package/src/lib/components/TableCell.svelte +1 -1
  47. package/src/lib/components/TableRow.svelte +1 -5
  48. package/src/lib/components/Tabs.svelte +10 -11
  49. package/src/lib/components/TaskCard.svelte +7 -4
  50. package/src/lib/components/Textarea.svelte +10 -11
  51. package/src/lib/components/ThemeToggle.svelte +1 -1
  52. package/src/lib/components/ToastContainer.svelte +3 -3
  53. package/src/lib/components/Toggle.svelte +2 -3
  54. package/src/lib/components/Tooltip.svelte +8 -9
  55. package/src/lib/components/button-styles.ts +17 -18
  56. package/src/lib/components/dashboard/RunDetailPanel.svelte +61 -49
  57. package/src/lib/components/dashboard/RunFilterPopover.svelte +17 -11
  58. package/src/lib/components/dashboard/RunsList.svelte +26 -21
  59. package/src/lib/theme-tokens.css +179 -114
  60. package/src/lib/theme.css +49 -35
@@ -169,7 +169,7 @@
169
169
  </script>
170
170
 
171
171
  <div
172
- class="relative flex flex-col rounded-xl border border-outline bg-surface-raised shadow-sm {className}"
172
+ class="relative flex flex-col rounded-[4px] border border-outline bg-surface-raised shadow-sm {className}"
173
173
  >
174
174
  {#if filterable}
175
175
  <div class="border-b border-outline p-3">
@@ -188,7 +188,7 @@
188
188
  {/if}
189
189
 
190
190
  <div
191
- class="relative min-h-0 flex-1 rounded-t-xl rounded-b-xl {wrapperClass ??
191
+ class="relative min-h-0 flex-1 rounded-[4px] {wrapperClass ??
192
192
  (stickyHeader ? '' : 'overflow-x-auto')}"
193
193
  >
194
194
  {#if loading}
@@ -196,7 +196,7 @@
196
196
  class="absolute inset-0 z-10 flex items-center justify-center bg-surface-raised/50 backdrop-blur-[1px]"
197
197
  >
198
198
  <div
199
- class="flex items-center gap-3 rounded-full border border-outline-faint bg-surface-raised px-4 py-2 shadow-lg"
199
+ class="flex items-center gap-3 rounded-full border border-outline bg-surface-raised px-4 py-2 shadow-md"
200
200
  >
201
201
  <svg class="h-5 w-5 animate-spin text-primary" viewBox="0 0 24 24" fill="none">
202
202
  <circle
@@ -213,7 +213,7 @@
213
213
  d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
214
214
  ></path>
215
215
  </svg>
216
- <span class="text-sm font-medium text-on-surface-muted">Loading...</span>
216
+ <span class="font-mono text-sm text-on-surface-muted">Loading...</span>
217
217
  </div>
218
218
  </div>
219
219
  {/if}
@@ -239,7 +239,7 @@
239
239
  <th
240
240
  class="
241
241
  group px-4 {compact ? 'py-2' : 'py-3'}
242
- font-medium text-on-surface-muted
242
+ font-mono text-xs tracking-wide text-on-surface-faint uppercase
243
243
  {column.align === 'center' ? 'text-center' : ''}
244
244
  {column.align === 'right' ? 'text-right' : ''}
245
245
  "
@@ -249,7 +249,7 @@
249
249
  <button
250
250
  onclick={() => handleSort(column.key as string)}
251
251
  class="
252
- inline-flex items-center gap-1 transition-colors group-hover:text-on-surface
252
+ inline-flex items-center gap-1 group-hover:text-on-surface
253
253
  {sortKey === column.key ? 'text-on-surface' : ''}
254
254
  "
255
255
  >
@@ -263,7 +263,7 @@
263
263
  {:else}
264
264
  <ArrowUpDown
265
265
  size={14}
266
- class="text-on-surface-faint opacity-0 transition-opacity group-hover:opacity-100"
266
+ class="text-on-surface-faint opacity-0 group-hover:opacity-100"
267
267
  />
268
268
  {/if}
269
269
  </button>
@@ -295,13 +295,12 @@
295
295
  {#each pagedData as row, idx (idx)}
296
296
  <tr
297
297
  class="
298
- border-b border-outline-faint last:border-b-0
298
+ border-b border-outline last:border-b-0
299
299
  {striped && idx % 2 === 1 ? 'bg-surface-sunken/30' : ''}
300
300
  {hoverable ? 'hover:bg-surface-sunken' : ''}
301
301
  {isSelected(row) ? 'bg-primary-soft/30' : ''}
302
302
  {onRowClick ? 'cursor-pointer' : ''}
303
- transition-colors duration-200
304
- "
303
+ "
305
304
  onclick={() => onRowClick?.(row)}
306
305
  >
307
306
  {#if selectable}
@@ -329,7 +328,11 @@
329
328
  {#if column.render}
330
329
  {@render column.render(row)}
331
330
  {:else}
332
- {dlv(row, column.key as string) ?? "-"}
331
+ <!-- Raw field value: a token, so mono. Snippet-rendered
332
+ cells choose their own voice. -->
333
+ <span class="font-mono"
334
+ >{dlv(row, column.key as string) ?? "-"}</span
335
+ >
333
336
  {/if}
334
337
  </td>
335
338
  {/each}
@@ -100,7 +100,7 @@
100
100
  <div
101
101
  class="
102
102
  absolute z-10 flex flex-col
103
- bg-surface-overlay shadow-xl
103
+ border border-outline bg-surface-overlay shadow-lg
104
104
  {positionClasses[side]}
105
105
  {className}
106
106
  "
@@ -112,16 +112,19 @@
112
112
  >
113
113
  <!-- Header -->
114
114
  {#if header}
115
- <div class="border-b border-outline-faint px-6 py-4">
115
+ <div class="border-b border-outline px-6 py-4">
116
116
  {@render header()}
117
117
  </div>
118
118
  {:else if title || closable}
119
119
  <div
120
- class="flex items-start justify-between gap-4 border-b border-outline-faint px-6 py-4"
120
+ class="flex items-start justify-between gap-4 border-b border-outline px-6 py-4"
121
121
  >
122
122
  <div>
123
123
  {#if title}
124
- <h2 id="drawer-title" class="text-lg font-semibold text-on-surface">
124
+ <h2
125
+ id="drawer-title"
126
+ class="font-mono text-lg font-semibold text-on-surface"
127
+ >
125
128
  {title}
126
129
  </h2>
127
130
  {/if}
@@ -129,7 +132,7 @@
129
132
  {#if closable}
130
133
  <button
131
134
  onclick={handleClose}
132
- class="-m-2 shrink-0 rounded-lg p-2 text-on-surface-faint transition-colors hover:bg-surface-sunken hover:text-on-surface-muted"
135
+ class="-m-2 shrink-0 rounded-[3px] p-2 text-on-surface-faint hover:bg-surface-sunken hover:text-on-surface-muted"
133
136
  aria-label="Close"
134
137
  >
135
138
  <X size={20} />
@@ -147,7 +150,7 @@
147
150
 
148
151
  <!-- Footer -->
149
152
  {#if footer}
150
- <div class="border-t border-outline-faint bg-surface-sunken/50 px-6 py-4">
153
+ <div class="border-t border-outline bg-surface-sunken px-6 py-4">
151
154
  {@render footer()}
152
155
  </div>
153
156
  {/if}
@@ -6,8 +6,6 @@
6
6
  import type { Component } from "svelte";
7
7
  import { tick, onDestroy } from "svelte";
8
8
  import { EllipsisVertical, Check } from "@lucide/svelte";
9
- import { scale } from "svelte/transition";
10
- import { quintOut } from "svelte/easing";
11
9
  import { computePosition, autoUpdate, flip, shift, offset } from "@floating-ui/dom";
12
10
 
13
11
  interface MenuItem {
@@ -127,7 +125,7 @@
127
125
  bind:this={triggerEl}
128
126
  aria-label={triggerLabel}
129
127
  aria-expanded={open}
130
- class="rounded-lg p-2 text-on-surface-muted transition-colors hover:bg-surface-sunken hover:text-on-surface focus:ring-2 focus:ring-outline focus:outline-none active:bg-surface-sunken"
128
+ class="rounded-[3px] p-2 text-on-surface-muted hover:bg-surface-sunken hover:text-on-surface focus:ring-2 focus:ring-outline focus:outline-none active:bg-surface-sunken"
131
129
  >
132
130
  {#if trigger}
133
131
  {@render trigger()}
@@ -141,12 +139,11 @@
141
139
  use:portal
142
140
  bind:this={menuEl}
143
141
  role="menu"
144
- transition:scale={{ duration: 150, start: 0.95, easing: quintOut }}
145
142
  class="
146
143
  z-[9999]
147
144
  min-w-48 overflow-y-auto
148
- rounded-xl
149
- border border-outline bg-surface-overlay py-1 shadow-xl shadow-on-surface/5
145
+ rounded-[4px]
146
+ border border-outline bg-surface-overlay py-1 shadow-lg
150
147
  "
151
148
  style="transform-origin: {transformOrigin};"
152
149
  >
@@ -155,20 +152,19 @@
155
152
  {:else}
156
153
  {#each items as item, idx (idx)}
157
154
  {#if item.divider}
158
- <hr class="my-1 border-outline-faint" />
155
+ <hr class="my-1 border-outline" />
159
156
  {:else if item.href}
160
157
  <a
161
158
  href={item.href}
162
159
  title={item.title}
163
160
  role="menuitem"
164
161
  class="
165
- mx-1 flex items-center gap-3 rounded-lg px-3 py-2 text-sm
162
+ mx-1 flex items-center gap-3 rounded-[3px] px-3 py-2 font-mono text-sm
166
163
  {item.danger
167
164
  ? 'text-danger-soft-text hover:bg-danger-soft'
168
- : 'text-on-surface-muted hover:bg-surface-sunken'}
165
+ : 'text-on-surface-muted hover:bg-surface-sunken hover:text-on-surface'}
169
166
  {item.disabled ? 'cursor-not-allowed opacity-50' : ''}
170
- transition-colors
171
- "
167
+ "
172
168
  >
173
169
  {#if item.icon}
174
170
  {@const Icon = item.icon}
@@ -183,11 +179,11 @@
183
179
  title={item.title}
184
180
  role="menuitem"
185
181
  class="
186
- mx-1 box-border flex w-full max-w-[calc(100%-8px)] items-center gap-3 rounded-lg px-3 py-2 text-left text-sm
182
+ mx-1 box-border flex w-full max-w-[calc(100%-8px)] items-center gap-3 rounded-[3px] px-3 py-2 text-left font-mono text-sm
187
183
  {item.danger
188
184
  ? 'text-danger-soft-text hover:bg-danger-soft'
189
- : 'text-on-surface-muted hover:bg-surface-sunken'}
190
- transition-colors disabled:cursor-not-allowed
185
+ : 'text-on-surface-muted hover:bg-surface-sunken hover:text-on-surface'}
186
+ disabled:cursor-not-allowed
191
187
  disabled:opacity-50
192
188
  "
193
189
  >
@@ -46,15 +46,15 @@
46
46
  };
47
47
 
48
48
  const inputClasses = `
49
- w-full rounded-lg border
49
+ w-full rounded-[3px] border font-mono
50
50
  bg-surface-raised text-on-surface placeholder:text-on-surface-faint
51
- focus:outline-none focus:ring-4 focus:ring-ring/10 focus:border-ring
51
+ focus:outline-none focus:border-ring focus:ring-2 focus:ring-ring focus:ring-offset-2
52
52
  disabled:bg-surface-sunken disabled:text-on-surface-muted disabled:cursor-not-allowed
53
- transition-all duration-normal ease-in-out
54
- `;
53
+ `;
55
54
 
56
55
  const normalBorder = "border-outline hover:border-outline-hover shadow-sm";
57
- const errorBorder = "border-danger-400 focus:border-danger-500 focus:ring-danger-500/20";
56
+ const errorBorder =
57
+ "border-danger-surface focus:border-danger-surface focus:ring-danger-surface";
58
58
 
59
59
  function formatMs(msValue: number): string {
60
60
  if (typeof msValue !== "number" || isNaN(msValue)) return "";
@@ -113,14 +113,14 @@
113
113
 
114
114
  <div class="space-y-1.5 {className}">
115
115
  {#if label}
116
- <label for={inputId} class="block text-sm font-medium text-on-surface-muted">
116
+ <label for={inputId} class="block font-mono text-xs font-medium text-on-surface-muted">
117
117
  {label}
118
118
  </label>
119
119
  {/if}
120
120
 
121
121
  <div class="group relative">
122
122
  <div
123
- class="pointer-events-none absolute top-1/2 left-3 -translate-y-1/2 text-on-surface-faint transition-colors group-focus-within:text-primary"
123
+ class="pointer-events-none absolute top-1/2 left-3 -translate-y-1/2 text-on-surface-faint group-focus-within:text-primary"
124
124
  >
125
125
  <Clock size={16} />
126
126
  </div>
@@ -155,18 +155,14 @@
155
155
  </div>
156
156
 
157
157
  {#if parsedHuman && isFocused}
158
- <p
159
- class="animate-in slide-in-from-top-1 fade-in text-xs font-medium text-primary duration-200"
160
- >
158
+ <p class="font-sans text-xs font-medium text-primary">
161
159
  {parsedHuman}
162
160
  </p>
163
161
  {:else if error}
164
- <p
165
- class="animate-in slide-in-from-top-1 fade-in text-sm text-danger-soft-text duration-200"
166
- >
162
+ <p class="font-sans text-xs text-danger-soft-text">
167
163
  {error}
168
164
  </p>
169
165
  {:else if hint}
170
- <p class="text-sm text-on-surface-muted">{hint}</p>
166
+ <p class="font-sans text-xs text-on-surface-muted">{hint}</p>
171
167
  {/if}
172
168
  </div>
@@ -32,7 +32,7 @@
32
32
  <Icon size={iconSize} class="text-on-surface-faint" />
33
33
  </div>
34
34
 
35
- <h3 class="mb-1 text-lg font-semibold text-on-surface">{title}</h3>
35
+ <h3 class="mb-1 font-mono text-lg font-semibold text-on-surface">{title}</h3>
36
36
 
37
37
  {#if description}
38
38
  <p class="mb-6 max-w-sm text-sm text-on-surface-muted">{description}</p>
@@ -41,14 +41,14 @@
41
41
 
42
42
  const IconComponent = $derived(icon ?? (variant === "disconnected" ? WifiOff : TriangleAlert));
43
43
 
44
- const iconBg = $derived(variant === "disconnected" ? "bg-danger-soft" : "bg-warning-soft");
44
+ const iconBg = $derived(variant === "disconnected" ? "bg-warning-soft" : "bg-danger-soft");
45
45
  const iconColor = $derived(
46
- variant === "disconnected" ? "text-danger-surface" : "text-warning-surface",
46
+ variant === "disconnected" ? "text-warning-soft-text" : "text-danger-soft-text",
47
47
  );
48
48
  </script>
49
49
 
50
50
  <div
51
- class="flex flex-col items-center justify-center gap-4 rounded-xl border border-outline bg-surface-raised px-6 py-12 text-center {className}"
51
+ class="flex flex-col items-center justify-center gap-4 rounded-[4px] border border-outline bg-surface-raised px-6 py-12 text-center {className}"
52
52
  role="alert"
53
53
  >
54
54
  <div class="rounded-full {iconBg} p-3">
@@ -56,7 +56,7 @@
56
56
  </div>
57
57
 
58
58
  <div class="space-y-1">
59
- <h3 class="text-lg font-semibold text-on-surface">{defaultTitle}</h3>
59
+ <h3 class="font-mono text-lg font-semibold text-on-surface">{defaultTitle}</h3>
60
60
  <p class="max-w-sm text-sm text-on-surface-muted">{message}</p>
61
61
  </div>
62
62
 
@@ -15,19 +15,19 @@
15
15
  </script>
16
16
 
17
17
  <article
18
- class="overflow-hidden rounded-xl border border-outline bg-surface-raised p-6 shadow-sm transition-colors hover:border-outline-hover {className}"
18
+ class="overflow-hidden rounded-[4px] border border-outline bg-surface-raised p-6 hover:border-outline-hover {className}"
19
19
  >
20
20
  <div class="flex flex-col gap-3">
21
21
  {#if Icon}
22
22
  <span
23
- class="inline-flex h-10 w-10 items-center justify-center rounded-lg bg-primary-soft text-primary-soft-text"
23
+ class="inline-flex h-10 w-10 items-center justify-center rounded-[3px] border border-primary-soft-border bg-primary-soft text-primary-soft-text"
24
24
  >
25
25
  <Icon size={20} />
26
26
  </span>
27
27
  {/if}
28
- <h3 class="text-lg font-semibold text-on-surface">{title}</h3>
28
+ <h3 class="font-mono text-base text-primary">{title}</h3>
29
29
  {#if children}
30
- <div class="text-sm leading-relaxed text-on-surface-muted">
30
+ <div class="font-sans text-sm leading-relaxed text-on-surface-muted">
31
31
  {@render children()}
32
32
  </div>
33
33
  {/if}
@@ -12,6 +12,11 @@
12
12
  required?: boolean;
13
13
  children: Snippet;
14
14
  class?: string;
15
+ // The id of the control this field labels. Pass the SAME id to the wrapped
16
+ // input/select so the <label for> actually points at it — otherwise the
17
+ // label is dead (clicking it does nothing; screen readers can't associate
18
+ // it). Falls back to a generated id when the field has no focusable target.
19
+ id?: string;
15
20
  }
16
21
 
17
22
  let {
@@ -22,14 +27,16 @@
22
27
  required = false,
23
28
  children,
24
29
  class: className = "",
30
+ id,
25
31
  }: Props = $props();
26
32
 
27
33
  const generatedId = `field-${Math.random().toString(36).slice(2, 10)}`;
34
+ const fieldId = $derived(id ?? generatedId);
28
35
  </script>
29
36
 
30
37
  <div class="space-y-1.5 {className}">
31
38
  {#if label}
32
- <label for={generatedId} class="block text-sm font-medium text-on-surface-muted">
39
+ <label for={fieldId} class="block font-mono text-xs font-medium text-on-surface-muted">
33
40
  {label}
34
41
  {#if required}
35
42
  <span class="text-danger-soft-text">*</span>
@@ -38,18 +45,16 @@
38
45
  {/if}
39
46
 
40
47
  {#if description}
41
- <p class="text-sm text-on-surface-faint">{description}</p>
48
+ <p class="font-sans text-xs text-on-surface-faint">{description}</p>
42
49
  {/if}
43
50
 
44
51
  {@render children()}
45
52
 
46
53
  {#if error}
47
- <p
48
- class="animate-in slide-in-from-top-1 fade-in text-sm text-danger-soft-text duration-200"
49
- >
54
+ <p class="font-sans text-xs text-danger-soft-text">
50
55
  {error}
51
56
  </p>
52
57
  {:else if hint}
53
- <p class="text-sm text-on-surface-muted">{hint}</p>
58
+ <p class="font-sans text-xs text-on-surface-muted">{hint}</p>
54
59
  {/if}
55
60
  </div>
@@ -40,6 +40,9 @@
40
40
  const tag = $derived(`h${level}` as const);
41
41
  </script>
42
42
 
43
- <svelte:element this={tag} class="text-on-surface {sizeClasses[resolvedSize]} {className}">
43
+ <svelte:element
44
+ this={tag}
45
+ class="font-mono tracking-tight text-on-surface {sizeClasses[resolvedSize]} {className}"
46
+ >
44
47
  {@render children()}
45
48
  </svelte:element>
@@ -43,20 +43,20 @@
43
43
  };
44
44
 
45
45
  const inputClasses = `
46
- w-full rounded-lg border
46
+ w-full rounded-[3px] border font-mono
47
47
  bg-surface-raised text-on-surface placeholder:text-on-surface-faint
48
- focus:outline-none focus:ring-4 focus:ring-ring/10 focus:border-ring
48
+ focus:outline-none focus:border-ring focus:ring-2 focus:ring-ring focus:ring-offset-2
49
49
  disabled:bg-surface-sunken disabled:text-on-surface-muted disabled:cursor-not-allowed
50
- transition-all duration-normal ease-in-out
51
- `;
50
+ `;
52
51
 
53
52
  const normalBorder = "border-outline hover:border-outline-hover shadow-sm";
54
- const errorBorder = "border-danger-400 focus:border-danger-500 focus:ring-danger-500/20";
53
+ const errorBorder =
54
+ "border-danger-surface focus:border-danger-surface focus:ring-danger-surface";
55
55
  </script>
56
56
 
57
57
  <div class="space-y-1.5 {className}">
58
58
  {#if label}
59
- <label for={inputId} class="block text-sm font-medium text-on-surface-muted">
59
+ <label for={inputId} class="block font-mono text-xs font-medium text-on-surface-muted">
60
60
  {label}
61
61
  </label>
62
62
  {/if}
@@ -64,7 +64,7 @@
64
64
  <div class="group relative">
65
65
  {#if leadingIcon}
66
66
  <div
67
- class="pointer-events-none absolute top-1/2 left-3 -translate-y-1/2 text-on-surface-faint transition-colors group-focus-within:text-on-surface-muted"
67
+ class="pointer-events-none absolute top-1/2 left-3 -translate-y-1/2 text-on-surface-faint group-focus-within:text-on-surface-muted"
68
68
  >
69
69
  {@render leadingIcon()}
70
70
  </div>
@@ -97,7 +97,7 @@
97
97
  </div>
98
98
  {:else if trailingIcon}
99
99
  <div
100
- class="pointer-events-none absolute top-1/2 right-3 -translate-y-1/2 text-on-surface-faint transition-colors group-focus-within:text-on-surface-muted"
100
+ class="pointer-events-none absolute top-1/2 right-3 -translate-y-1/2 text-on-surface-faint group-focus-within:text-on-surface-muted"
101
101
  >
102
102
  {@render trailingIcon()}
103
103
  </div>
@@ -105,12 +105,10 @@
105
105
  </div>
106
106
 
107
107
  {#if error}
108
- <p
109
- class="animate-in slide-in-from-top-1 fade-in text-sm text-danger-soft-text duration-200"
110
- >
108
+ <p class="font-sans text-xs text-danger-soft-text">
111
109
  {error}
112
110
  </p>
113
111
  {:else if hint}
114
- <p class="text-sm text-on-surface-muted">{hint}</p>
112
+ <p class="font-sans text-xs text-on-surface-muted">{hint}</p>
115
113
  {/if}
116
114
  </div>
@@ -16,7 +16,7 @@
16
16
  min-w-[1.5em] px-1.5 py-0.5
17
17
  text-xs font-mono font-medium
18
18
  bg-surface-sunken text-on-surface-muted
19
- border border-outline rounded-md
19
+ border border-outline rounded-[3px]
20
20
  shadow-sm
21
21
  `;
22
22
  </script>
@@ -27,9 +27,8 @@
27
27
 
28
28
  const baseClasses = `
29
29
  inline-flex items-center gap-1
30
- transition-colors duration-normal ease-out
31
- focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring/10 focus-visible:border-ring
32
- rounded-sm
30
+ focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2
31
+ rounded-[3px]
33
32
  `;
34
33
 
35
34
  const variantClasses: Record<LinkVariant, string> = {
@@ -40,7 +40,7 @@
40
40
  {...restProps}
41
41
  >
42
42
  {#if icon}
43
- <span class="shrink-0 transition-transform group-active:scale-95">
43
+ <span class="shrink-0 group-active:scale-95">
44
44
  {@render icon()}
45
45
  </span>
46
46
  {/if}
@@ -48,7 +48,7 @@
48
48
  {@render children()}
49
49
  {/if}
50
50
  {#if iconRight}
51
- <span class="shrink-0 transition-transform group-active:scale-95">
51
+ <span class="shrink-0 group-active:scale-95">
52
52
  {@render iconRight()}
53
53
  </span>
54
54
  {/if}
@@ -564,8 +564,8 @@
564
564
  >
565
565
  {#if cache.firstAvailableLine > 0}
566
566
  <div
567
- class="absolute right-0 left-0 flex items-center bg-warning-700/20 text-warning-400"
568
- style="top: 0px; height: {lineHeight}px;"
567
+ class="absolute right-0 left-0 flex items-center text-term-warn"
568
+ style="top: 0px; height: {lineHeight}px; background-color: color-mix(in srgb, var(--rw-term-warn) 15%, transparent);"
569
569
  >
570
570
  <div
571
571
  class="sticky left-0 z-10 flex-shrink-0 pr-3 text-right select-none"
@@ -585,10 +585,7 @@
585
585
  <div
586
586
  class="log-line absolute right-0 left-0 flex {wrap
587
587
  ? 'items-start'
588
- : 'items-center'} hover:bg-[rgb(255_255_255_/_0.025)] {flashLine ===
589
- line.num
590
- ? 'log-line--flash'
591
- : ''}"
588
+ : 'items-center'} {flashLine === line.num ? 'log-line--flash' : ''}"
592
589
  style="top: {lineTop(line.num)}px; height: {lineHeightPx(line.num)}px;"
593
590
  >
594
591
  <div
@@ -783,12 +780,11 @@
783
780
 
784
781
  {#if userScrolledUp && cache.totalLines > 0}
785
782
  <button
786
- class="absolute right-4 bottom-12 z-10 flex items-center gap-2 rounded-full border
787
- border-[rgb(255_255_255_/_0.1)] bg-[var(--rw-con-panel)]
783
+ class="absolute right-4 bottom-12 z-10 flex items-center gap-2 rounded-[3px] border
784
+ border-[var(--rw-con-gutter)] bg-[var(--rw-con-panel)]
788
785
  px-3 py-1.5 text-xs
789
- font-medium text-[var(--rw-con-text)] shadow-lg
790
- transition-all duration-200 hover:scale-105
791
- hover:bg-[rgb(255_255_255_/_0.06)] active:scale-95"
786
+ text-[var(--rw-con-text)]
787
+ hover:border-term-teal hover:text-term-teal active:translate-y-[1px]"
792
788
  onclick={enableAutoScroll}
793
789
  >
794
790
  <svg
@@ -805,8 +801,8 @@
805
801
  {/if}
806
802
 
807
803
  <div
808
- class="flex flex-shrink-0 items-center justify-between border-t border-[rgb(255_255_255_/_0.06)]
809
- bg-[var(--rw-con-panel)] px-3.5 py-2 text-[11px] text-[var(--rw-con-dim)]"
804
+ class="flex flex-shrink-0 items-center justify-between border-t border-[var(--rw-con-gutter)]
805
+ bg-[var(--rw-con-panel)] px-3.5 py-2 text-[11px] tracking-wide text-[var(--rw-con-dim)]"
810
806
  >
811
807
  <div class="flex items-center gap-3">
812
808
  {#if isStreaming}
@@ -827,7 +823,7 @@
827
823
  <span>{formatBytes(cache.totalBytes)}</span>
828
824
  {/if}
829
825
  {#if isAutoScroll}
830
- <span class="inline-flex items-center gap-1.5 text-[#7fe0b0]">
826
+ <span class="inline-flex items-center gap-1.5 text-term-ok">
831
827
  <svg
832
828
  class="h-3 w-3"
833
829
  viewBox="0 0 24 24"
@@ -857,15 +853,21 @@
857
853
  way the approved design closes a finished capture. */
858
854
  .sentinel--muted {
859
855
  color: var(--rw-con-gutter);
860
- border-top-color: rgb(255 255 255 / 0.08);
856
+ border-top-color: var(--rw-con-gutter);
861
857
  }
862
858
  .sentinel--warn {
863
- color: #e5a552;
864
- border-top-color: rgb(229 165 82 / 0.3);
859
+ color: var(--rw-term-warn);
860
+ border-top-color: color-mix(in srgb, var(--rw-term-warn) 30%, transparent);
861
+ }
862
+
863
+ /* Subtle row hover on the fixed-dark console surface — a faint lift of the
864
+ console text colour, never a theme-flipping fill. */
865
+ .log-line:hover {
866
+ background-color: color-mix(in srgb, var(--rw-con-text) 4%, transparent);
865
867
  }
866
868
 
867
869
  .log-line--flash {
868
- animation: log-line-flash 1.5s ease-out;
870
+ animation: log-line-flash 1.5s;
869
871
  }
870
872
 
871
873
  .frame-toggle {
@@ -875,12 +877,11 @@
875
877
  background: transparent;
876
878
  border: none;
877
879
  padding: 0;
878
- transition: color 120ms ease;
879
880
  }
880
881
 
881
882
  @keyframes log-line-flash {
882
883
  0% {
883
- background-color: oklch(0.8 0.18 70 / 0.45);
884
+ background-color: color-mix(in srgb, var(--rw-term-warn) 45%, transparent);
884
885
  }
885
886
  100% {
886
887
  background-color: transparent;
@@ -924,7 +925,7 @@
924
925
  height: 4px;
925
926
  border-radius: 50%;
926
927
  background-color: currentColor;
927
- animation: dot-pulse 1s ease-in-out infinite;
928
+ animation: dot-pulse 1s infinite;
928
929
  }
929
930
 
930
931
  @keyframes dot-pulse {
@@ -20,7 +20,9 @@
20
20
  };
21
21
  </script>
22
22
 
23
- <span class="inline-flex {sizeClasses[size]} {className}" aria-hidden="true">
23
+ <!-- The mark is flat currentColor (matching the website's brand mark), so the
24
+ brand teal comes from text-primary and any caller can override it. -->
25
+ <span class="inline-flex text-primary {sizeClasses[size]} {className}" aria-hidden="true">
24
26
  <!-- SVG body is a static, repo-vendored asset imported via Vite's ?raw loader. -->
25
27
  <!-- eslint-disable-next-line svelte/no-at-html-tags -->
26
28
  {@html logoSvg}