@runwisp/ui 0.13.0 → 0.15.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 (69) hide show
  1. package/package.json +39 -47
  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/actions/portal.ts +0 -3
  7. package/src/lib/assets/runwisp-logo.svg +1 -8
  8. package/src/lib/components/AccordionItem.svelte +4 -10
  9. package/src/lib/components/Alert.svelte +11 -11
  10. package/src/lib/components/Avatar.svelte +1 -1
  11. package/src/lib/components/Badge.svelte +13 -13
  12. package/src/lib/components/Breadcrumb.svelte +3 -3
  13. package/src/lib/components/Button.svelte +2 -2
  14. package/src/lib/components/Card.svelte +4 -4
  15. package/src/lib/components/Checkbox.svelte +12 -10
  16. package/src/lib/components/CodeBlock.svelte +7 -13
  17. package/src/lib/components/CronInput.svelte +12 -15
  18. package/src/lib/components/DataGrid.svelte +14 -13
  19. package/src/lib/components/Drawer.svelte +9 -9
  20. package/src/lib/components/Dropdown.svelte +12 -20
  21. package/src/lib/components/DurationInput.svelte +10 -14
  22. package/src/lib/components/EmptyState.svelte +1 -1
  23. package/src/lib/components/ErrorState.svelte +4 -4
  24. package/src/lib/components/FeatureCard.svelte +4 -4
  25. package/src/lib/components/FormField.svelte +4 -6
  26. package/src/lib/components/Heading.svelte +4 -1
  27. package/src/lib/components/Input.svelte +10 -12
  28. package/src/lib/components/Kbd.svelte +1 -1
  29. package/src/lib/components/Link.svelte +2 -3
  30. package/src/lib/components/LinkButton.svelte +2 -2
  31. package/src/lib/components/LogConsole.svelte +22 -21
  32. package/src/lib/components/Logo.svelte +3 -1
  33. package/src/lib/components/Modal.svelte +9 -11
  34. package/src/lib/components/PageContainer.svelte +1 -1
  35. package/src/lib/components/PageHeader.svelte +1 -1
  36. package/src/lib/components/Pagination.svelte +4 -4
  37. package/src/lib/components/Popover.svelte +5 -7
  38. package/src/lib/components/Progress.svelte +6 -4
  39. package/src/lib/components/Prose.svelte +4 -4
  40. package/src/lib/components/Radio.svelte +11 -10
  41. package/src/lib/components/RadioGroup.svelte +3 -5
  42. package/src/lib/components/Select.svelte +23 -36
  43. package/src/lib/components/Separator.svelte +4 -1
  44. package/src/lib/components/Skeleton.svelte +6 -6
  45. package/src/lib/components/StatusIndicator.svelte +7 -18
  46. package/src/lib/components/Table.svelte +1 -1
  47. package/src/lib/components/TableCell.svelte +1 -1
  48. package/src/lib/components/TableRow.svelte +1 -5
  49. package/src/lib/components/Tabs.svelte +10 -13
  50. package/src/lib/components/TaskCard.svelte +7 -4
  51. package/src/lib/components/Textarea.svelte +10 -11
  52. package/src/lib/components/ThemeToggle.svelte +1 -1
  53. package/src/lib/components/ToastContainer.svelte +3 -8
  54. package/src/lib/components/Toggle.svelte +2 -3
  55. package/src/lib/components/Tooltip.svelte +12 -10
  56. package/src/lib/components/button-styles.ts +17 -18
  57. package/src/lib/components/dashboard/RunDetailPanel.svelte +160 -249
  58. package/src/lib/components/dashboard/RunFilterPopover.svelte +31 -25
  59. package/src/lib/components/dashboard/RunsList.svelte +55 -50
  60. package/src/lib/components/dashboard/run-filters.test.ts +31 -33
  61. package/src/lib/components/dashboard/run-filters.ts +33 -33
  62. package/src/lib/components/dashboard/run-helpers.test.ts +42 -17
  63. package/src/lib/components/dashboard/run-helpers.ts +53 -17
  64. package/src/lib/components/dashboard/status-config.ts +9 -8
  65. package/src/lib/theme-tokens.css +179 -114
  66. package/src/lib/theme.css +49 -35
  67. package/src/lib/utils/error.ts +0 -3
  68. package/src/lib/utils/format.test.ts +2 -6
  69. package/src/lib/utils/format.ts +28 -3
@@ -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}
@@ -82,25 +82,27 @@
82
82
  class="
83
83
  relative z-10 w-full {sizeClasses[size]}
84
84
  flex max-h-[90vh] flex-col
85
- rounded-2xl bg-surface-overlay shadow-xl
85
+ rounded-[4px] border border-outline bg-surface-overlay shadow-lg
86
86
  {className}
87
87
  "
88
88
  role="dialog"
89
89
  aria-modal="true"
90
90
  aria-labelledby={title ? "modal-title" : undefined}
91
91
  >
92
- <!-- Header -->
93
92
  {#if header}
94
- <div class="border-b border-outline-faint px-6 py-4">
93
+ <div class="border-b border-outline px-6 py-4">
95
94
  {@render header()}
96
95
  </div>
97
96
  {:else if title || closable}
98
97
  <div
99
- class="flex items-start justify-between gap-4 border-b border-outline-faint px-6 py-4"
98
+ class="flex items-start justify-between gap-4 border-b border-outline px-6 py-4"
100
99
  >
101
100
  <div>
102
101
  {#if title}
103
- <h2 id="modal-title" class="text-lg font-semibold text-on-surface">
102
+ <h2
103
+ id="modal-title"
104
+ class="font-mono text-lg font-semibold text-on-surface"
105
+ >
104
106
  {title}
105
107
  </h2>
106
108
  {/if}
@@ -111,7 +113,7 @@
111
113
  {#if closable}
112
114
  <button
113
115
  onclick={handleClose}
114
- class="-m-2 shrink-0 rounded-lg p-2 text-on-surface-faint transition-colors hover:bg-surface-sunken hover:text-on-surface-muted"
116
+ class="-m-2 shrink-0 rounded-[3px] p-2 text-on-surface-faint hover:bg-surface-sunken hover:text-on-surface-muted"
115
117
  aria-label="Close"
116
118
  >
117
119
  <X size={20} />
@@ -120,18 +122,14 @@
120
122
  </div>
121
123
  {/if}
122
124
 
123
- <!-- Body -->
124
125
  {#if children}
125
126
  <div class="flex-1 overflow-y-auto px-6 py-4">
126
127
  {@render children()}
127
128
  </div>
128
129
  {/if}
129
130
 
130
- <!-- Footer -->
131
131
  {#if footer}
132
- <div
133
- class="rounded-b-2xl border-t border-outline-faint bg-surface-sunken/50 px-6 py-4"
134
- >
132
+ <div class="rounded-b-[4px] border-t border-outline bg-surface-sunken px-6 py-4">
135
133
  {@render footer()}
136
134
  </div>
137
135
  {/if}
@@ -33,6 +33,6 @@
33
33
  };
34
34
  </script>
35
35
 
36
- <div class="animate-in fade-in duration-300 {variantClasses[variant]} {className}">
36
+ <div class="{variantClasses[variant]} {className}">
37
37
  {@render children()}
38
38
  </div>
@@ -16,7 +16,7 @@
16
16
 
17
17
  <div class="flex shrink-0 flex-wrap items-start justify-between gap-3 px-1 {className}">
18
18
  <div class="min-w-0">
19
- <h1 class="text-2xl font-bold tracking-tight text-on-surface">{title}</h1>
19
+ <h1 class="font-mono text-2xl font-bold tracking-tight text-on-surface">{title}</h1>
20
20
  {#if subtitle}
21
21
  <p class="mt-0.5 text-sm text-on-surface-muted">{subtitle}</p>
22
22
  {/if}
@@ -43,7 +43,7 @@
43
43
  </script>
44
44
 
45
45
  <div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between {className}">
46
- <div class="flex items-center gap-4 text-sm text-on-surface-muted">
46
+ <div class="flex items-center gap-4 font-mono text-sm text-on-surface-muted">
47
47
  {#if totalItems === 0}
48
48
  <span>No results</span>
49
49
  {:else}
@@ -57,8 +57,8 @@
57
57
  value={pageSize}
58
58
  onchange={handlePageSizeChange}
59
59
  class="
60
- appearance-none rounded-lg border border-outline bg-surface-raised py-1 pr-7 pl-2 text-sm
61
- font-medium text-on-surface-muted shadow-sm focus:border-ring focus:ring-1 focus:ring-ring focus:outline-none
60
+ appearance-none rounded-[3px] border border-outline bg-surface-raised py-1 pr-7 pl-2 font-mono
61
+ text-sm text-on-surface-muted shadow-sm hover:border-outline-hover focus:border-ring focus:ring-1 focus:ring-ring focus:outline-none
62
62
  "
63
63
  >
64
64
  {#each pageSizeOptions as size (size)}
@@ -81,7 +81,7 @@
81
81
  disabled={safePage <= 1}
82
82
  onclick={() => goTo(safePage - 1)}>Prev</Button
83
83
  >
84
- <div class="min-w-[4rem] text-center text-xs text-on-surface-muted">
84
+ <div class="min-w-[4rem] text-center font-mono text-xs text-on-surface-muted">
85
85
  Page {safePage} of {totalPages}
86
86
  </div>
87
87
  <Button
@@ -6,8 +6,7 @@
6
6
  import type { Placement } from "@floating-ui/dom";
7
7
  import { onDestroy } from "svelte";
8
8
  import { computePosition, autoUpdate, flip, shift, offset } from "@floating-ui/dom";
9
- import { scale, fade } from "svelte/transition";
10
- import { quintOut } from "svelte/easing";
9
+ import { fade } from "svelte/transition";
11
10
  import { portal } from "../actions/portal.js";
12
11
 
13
12
  interface Props {
@@ -139,20 +138,19 @@
139
138
  <div
140
139
  use:portal
141
140
  transition:fade={{ duration: 120 }}
142
- class="fixed inset-0 z-[9998] bg-on-surface/40"
141
+ class="fixed inset-0 z-[9998] bg-backdrop"
143
142
  aria-hidden="true"
144
143
  ></div>
145
144
  {/if}
146
145
  <div
147
146
  use:portal
148
147
  bind:this={contentEl}
149
- transition:scale={{ duration: 150, start: 0.95, easing: quintOut }}
150
148
  class="
151
149
  z-[9999] max-h-[80vh] overflow-y-auto
152
- border border-outline bg-surface-overlay shadow-xl shadow-on-surface/5
150
+ border border-outline bg-surface-overlay shadow-lg
153
151
  {asSheet
154
- ? 'fixed inset-x-0 bottom-0 max-h-[85vh] rounded-t-2xl border-x-0 border-b-0 p-5'
155
- : 'rounded-xl p-4'}
152
+ ? 'fixed inset-x-0 bottom-0 max-h-[85vh] rounded-t-[4px] border-x-0 border-b-0 p-5'
153
+ : 'rounded-[4px] p-4'}
156
154
  "
157
155
  >
158
156
  {#if children}
@@ -45,19 +45,21 @@
45
45
  {#if showLabel || label}
46
46
  <div class="mb-1.5 flex items-center justify-between">
47
47
  {#if label}
48
- <span class="text-sm font-medium text-on-surface-muted">{label}</span>
48
+ <span class="font-mono text-sm text-on-surface-muted">{label}</span>
49
49
  {/if}
50
50
  {#if showLabel}
51
- <span class="text-sm text-on-surface-muted">{Math.round(percentage)}%</span>
51
+ <span class="font-mono text-sm text-on-surface-muted"
52
+ >{Math.round(percentage)}%</span
53
+ >
52
54
  {/if}
53
55
  </div>
54
56
  {/if}
55
57
 
56
- <div class="w-full {sizeClasses[size]} overflow-hidden rounded-full bg-outline">
58
+ <div class="w-full {sizeClasses[size]} overflow-hidden rounded-full bg-surface-sunken">
57
59
  <div
58
60
  class="{sizeClasses[size]} {variantClasses[
59
61
  variant
60
- ]} rounded-full transition-all duration-300"
62
+ ]} rounded-full transition-[width] duration-200"
61
63
  style="width: {percentage}%"
62
64
  role="progressbar"
63
65
  aria-valuenow={value}
@@ -28,10 +28,10 @@
28
28
  prose-headings:text-on-surface
29
29
  prose-p:text-on-surface-muted prose-a:text-primary prose-a:no-underline
30
30
  hover:prose-a:underline
31
- prose-blockquote:border-outline prose-blockquote:text-on-surface-muted prose-strong:text-on-surface prose-code:rounded prose-code:bg-surface-sunken
32
- prose-code:px-1 prose-code:py-0.5 prose-code:text-on-surface
33
- prose-pre:border prose-pre:border-outline
34
- prose-pre:bg-surface-sunken
31
+ prose-blockquote:border-outline prose-blockquote:text-on-surface-muted prose-strong:text-on-surface prose-code:rounded-[3px] prose-code:bg-surface-sunken
32
+ prose-code:px-1 prose-code:py-0.5 prose-code:font-mono prose-code:text-on-surface
33
+ prose-pre:rounded-[4px] prose-pre:border prose-pre:border-outline
34
+ prose-pre:bg-surface-sunken prose-pre:font-mono
35
35
  prose-li:text-on-surface-muted
36
36
  prose-th:text-on-surface
37
37
  prose-td:text-on-surface-muted
@@ -49,21 +49,20 @@
49
49
  checked={isChecked}
50
50
  onchange={() => (groupValue = value)}
51
51
  class="
52
- peer {sizeClasses[size].radio} duration-fast
52
+ peer {sizeClasses[size].radio}
53
53
  cursor-pointer appearance-none rounded-full
54
- border-2 border-outline-hover
55
- bg-surface-raised transition-colors
56
- checked:border-primary checked:bg-primary
57
- hover:border-on-surface-faint
54
+ border border-outline
55
+ bg-surface-raised checked:border-primary checked:bg-primary
56
+ hover:border-outline-hover
58
57
  checked:hover:border-primary-hover checked:hover:bg-primary-hover
59
- focus:ring-2 focus:ring-ring/20
60
- focus:ring-offset-0 focus:outline-none
61
- disabled:cursor-not-allowed disabled:hover:border-outline-hover
58
+ focus:ring-2 focus:ring-ring focus:ring-offset-2
59
+ focus:outline-none
60
+ disabled:cursor-not-allowed disabled:hover:border-outline
62
61
  disabled:checked:hover:bg-primary
63
62
  "
64
63
  />
65
64
  <div
66
- class="duration-fast pointer-events-none absolute rounded-full bg-on-primary opacity-0 transition-opacity peer-checked:opacity-100 {sizeClasses[
65
+ class="pointer-events-none absolute rounded-full bg-on-primary opacity-0 peer-checked:opacity-100 {sizeClasses[
67
66
  size
68
67
  ].dot}"
69
68
  ></div>
@@ -72,7 +71,9 @@
72
71
  {#if label || description}
73
72
  <div class="flex flex-col gap-0.5">
74
73
  {#if label}
75
- <span class="{sizeClasses[size].text} font-medium text-on-surface">{label}</span>
74
+ <span class="{sizeClasses[size].text} font-mono font-medium text-on-surface"
75
+ >{label}</span
76
+ >
76
77
  {/if}
77
78
  {#if description}
78
79
  <span class="text-sm text-on-surface-muted">{description}</span>
@@ -36,7 +36,7 @@
36
36
 
37
37
  <fieldset class="space-y-3 {className}" role="radiogroup" aria-label={label}>
38
38
  {#if label}
39
- <legend class="text-sm font-medium text-on-surface-muted">{label}</legend>
39
+ <legend class="font-mono text-xs font-medium text-on-surface-muted">{label}</legend>
40
40
  {/if}
41
41
 
42
42
  <div class={orientationClasses[orientation]}>
@@ -44,12 +44,10 @@
44
44
  </div>
45
45
 
46
46
  {#if error}
47
- <p
48
- class="animate-in slide-in-from-top-1 fade-in text-sm text-danger-soft-text duration-200"
49
- >
47
+ <p class="font-sans text-xs text-danger-soft-text">
50
48
  {error}
51
49
  </p>
52
50
  {:else if hint}
53
- <p class="text-sm text-on-surface-muted">{hint}</p>
51
+ <p class="font-sans text-xs text-on-surface-muted">{hint}</p>
54
52
  {/if}
55
53
  </fieldset>
@@ -18,7 +18,6 @@
18
18
  <script lang="ts">
19
19
  import { tick, onDestroy } from "svelte";
20
20
  import { ChevronDown, Check, Search } from "@lucide/svelte";
21
- import { fade } from "svelte/transition";
22
21
  import { computePosition, autoUpdate, flip, shift, offset } from "@floating-ui/dom";
23
22
  import { generateUlid } from "@runwisp/common";
24
23
 
@@ -54,7 +53,7 @@
54
53
  id = `select-${generateUlid()}`,
55
54
  name,
56
55
  onchange,
57
- onsearch, // New prop
56
+ onsearch,
58
57
  }: Props & { onsearch?: (query: string) => void } = $props();
59
58
 
60
59
  let isOpen = $state(false);
@@ -157,7 +156,7 @@
157
156
  value = option.value;
158
157
  onchange?.(value);
159
158
  isOpen = false;
160
- cleanupFloating?.(); // Cleanup immediately on select
159
+ cleanupFloating?.();
161
160
  }
162
161
 
163
162
  function handleKeydown(e: KeyboardEvent) {
@@ -231,18 +230,17 @@
231
230
  };
232
231
 
233
232
  const triggerBase = `
234
- relative w-full text-left cursor-pointer
235
- rounded-lg border bg-surface-raised
236
- transition-all duration-normal ease-out
237
- flex items-center justify-between
238
- focus:outline-none focus:ring-4 focus:ring-ring/10 focus:border-ring
233
+ relative w-full text-left cursor-pointer font-mono
234
+ rounded-[3px] border bg-surface-raised
235
+ flex items-center justify-between
236
+ focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:border-ring
239
237
  `;
240
238
 
241
239
  const borderClass = $derived(
242
240
  error
243
- ? "border-danger-300 ring-danger-500/20"
241
+ ? "border-danger-surface"
244
242
  : isOpen
245
- ? "border-ring ring-4 ring-ring/10"
243
+ ? "border-ring ring-2 ring-ring ring-offset-2"
246
244
  : "border-outline hover:border-outline-hover shadow-sm hover:shadow-md",
247
245
  );
248
246
  </script>
@@ -251,7 +249,7 @@
251
249
 
252
250
  <div class="relative w-full {className}">
253
251
  {#if label}
254
- <label for={id} class="mb-1.5 block text-sm font-medium text-on-surface-muted">
252
+ <label for={id} class="mb-1.5 block font-mono text-xs font-medium text-on-surface-muted">
255
253
  {label}
256
254
  </label>
257
255
  {/if}
@@ -292,30 +290,19 @@
292
290
  <div
293
291
  class="pointer-events-none absolute top-1/2 right-3 -translate-y-1/2 text-on-surface-faint"
294
292
  >
295
- <ChevronDown
296
- size={16}
297
- class="transition-transform duration-200 {isOpen ? 'rotate-180' : ''}"
298
- />
293
+ <ChevronDown size={16} class={isOpen ? "rotate-180" : ""} />
299
294
  </div>
300
295
  </button>
301
296
 
302
- <!-- Hidden input for form submission if needed -->
303
297
  {#if name}
304
298
  <input type="hidden" {name} {value} />
305
299
  {/if}
306
300
 
307
- <!-- Dropdown Menu -->
308
301
  {#if isOpen}
309
- <div
310
- use:portal
311
- bind:this={menuEl}
312
- transition:fade={{ duration: 100 }}
313
- class="z-[9999] min-w-[200px]"
314
- >
302
+ <div use:portal bind:this={menuEl} class="z-[9999] min-w-[200px]">
315
303
  <div
316
304
  class="
317
- overflow-hidden rounded-xl border border-outline bg-surface-overlay shadow-xl
318
- ring-1 shadow-on-surface/5 ring-black/5
305
+ overflow-hidden rounded-[4px] border border-outline bg-surface-overlay shadow-md
319
306
  "
320
307
  >
321
308
  {#if searchable}
@@ -331,8 +318,8 @@
331
318
  onkeydown={handleKeydown}
332
319
  placeholder="Search..."
333
320
  class="
334
- w-full rounded-lg border border-outline bg-surface-raised py-1.5 pr-3 pl-8 text-xs
335
- text-on-surface placeholder:text-on-surface-faint focus:border-ring focus:ring-1
321
+ w-full rounded-[3px] border border-outline bg-surface-raised py-1.5 pr-3 pl-8 font-mono text-xs
322
+ text-on-surface placeholder:text-on-surface-faint focus:border-ring focus:ring-2
336
323
  focus:ring-ring focus:outline-none
337
324
  "
338
325
  />
@@ -341,7 +328,7 @@
341
328
  {/if}
342
329
 
343
330
  <div
344
- class="scrollbar-thin scrollbar-thumb-mist-200 scrollbar-track-transparent max-h-60 overflow-x-hidden overflow-y-auto p-1"
331
+ class="max-h-60 scrollbar-thin scrollbar-thumb-mist-200 scrollbar-track-transparent overflow-x-hidden overflow-y-auto p-1"
345
332
  >
346
333
  {#if flattenedVisibleOptions.length === 0}
347
334
  <div class="px-4 py-8 text-center text-sm text-on-surface-muted">
@@ -363,7 +350,7 @@
363
350
 
364
351
  {#each Object.entries(groupedOptions.groups) as [groupName, groupOptions] (groupName)}
365
352
  <div
366
- class="sticky top-0 z-10 mt-1 bg-surface-sunken/50 px-2 py-1.5 text-xs font-semibold tracking-wider text-on-surface-faint uppercase backdrop-blur-sm first:mt-0"
353
+ class="sticky top-0 z-10 mt-1 bg-surface-sunken/50 px-2 py-1.5 font-mono text-xs font-semibold tracking-wider text-on-surface-faint uppercase backdrop-blur-sm first:mt-0"
367
354
  >
368
355
  {groupName}
369
356
  </div>
@@ -385,17 +372,16 @@
385
372
  {/if}
386
373
 
387
374
  {#if error}
388
- <p class="animate-in slide-in-from-top-1 fade-in mt-1.5 text-sm text-danger-soft-text">
375
+ <p class="mt-1.5 font-sans text-xs text-danger-soft-text">
389
376
  {error}
390
377
  </p>
391
378
  {:else if hint}
392
- <p class="mt-1.5 text-xs text-on-surface-muted">
379
+ <p class="mt-1.5 font-sans text-xs text-on-surface-muted">
393
380
  {hint}
394
381
  </p>
395
382
  {/if}
396
383
  </div>
397
384
 
398
- <!-- Helper Component for items -->
399
385
  {#snippet OptionItem({
400
386
  option,
401
387
  active,
@@ -416,8 +402,9 @@
416
402
  {onclick}
417
403
  onmouseenter={onsemiactive}
418
404
  class="
419
- flex w-full items-start gap-3 rounded-lg px-2.5 py-2 text-left transition-colors
420
- {active ? 'bg-surface-sunken' : 'hover:bg-surface-sunken/50'}
405
+ flex w-full items-start gap-3 rounded-[3px] px-2.5 py-2 text-left {active
406
+ ? 'bg-surface-sunken'
407
+ : 'hover:bg-surface-sunken/50'}
421
408
  {selected ? 'bg-primary-soft/50 font-medium text-on-surface' : 'text-on-surface-muted'}
422
409
  {option.disabled
423
410
  ? 'pointer-events-none cursor-not-allowed opacity-50'
@@ -433,14 +420,14 @@
433
420
 
434
421
  <div class="min-w-0 flex-1">
435
422
  <div class="flex items-center justify-between gap-2">
436
- <span class="block truncate text-sm font-medium">{option.label}</span>
423
+ <span class="block truncate font-mono text-sm font-medium">{option.label}</span>
437
424
  {#if selected}
438
425
  <Check size={14} class="shrink-0 text-primary" />
439
426
  {/if}
440
427
  </div>
441
428
  {#if option.description}
442
429
  <div
443
- class="mt-0.5 line-clamp-2 text-xs leading-normal text-wrap text-on-surface-muted"
430
+ class="mt-0.5 line-clamp-2 font-sans text-xs leading-normal text-wrap text-on-surface-muted"
444
431
  >
445
432
  {option.description}
446
433
  </div>
@@ -22,7 +22,10 @@
22
22
  {:else if label}
23
23
  <div role="separator" class="flex items-center gap-3 {className}">
24
24
  <div class="h-px flex-1 bg-outline"></div>
25
- <span class="text-xs font-medium text-on-surface-faint select-none">{label}</span>
25
+ <span
26
+ class="font-mono text-xs font-medium tracking-wider text-on-surface-faint uppercase select-none"
27
+ >{label}</span
28
+ >
26
29
  <div class="h-px flex-1 bg-outline"></div>
27
30
  </div>
28
31
  {:else}
@@ -30,7 +30,7 @@
30
30
 
31
31
  {#if shape === "circle"}
32
32
  <div
33
- class="animate-pulse rounded-full bg-outline {className}"
33
+ class="animate-pulse rounded-full bg-surface-sunken {className}"
34
34
  style="width: {width ?? '2.5rem'}; height: {height ?? width ?? '2.5rem'}"
35
35
  role="status"
36
36
  aria-label="Loading"
@@ -39,7 +39,7 @@
39
39
  </div>
40
40
  {:else if shape === "rect"}
41
41
  <div
42
- class="animate-pulse rounded-lg bg-outline {className}"
42
+ class="animate-pulse rounded-[4px] bg-surface-sunken {className}"
43
43
  style="width: {width ?? '100%'}; height: {height ?? '2rem'}"
44
44
  role="status"
45
45
  aria-label="Loading"
@@ -50,8 +50,8 @@
50
50
  <div class="animate-pulse space-y-6 p-6 {className}" role="status" aria-label="Loading content">
51
51
  {#if header}
52
52
  <div class="space-y-3">
53
- <div class="h-7 w-48 rounded-lg bg-outline"></div>
54
- <div class="h-4 w-72 rounded bg-outline-faint"></div>
53
+ <div class="h-7 w-48 rounded-[4px] bg-surface-sunken"></div>
54
+ <div class="h-4 w-72 rounded-[3px] bg-surface-sunken"></div>
55
55
  </div>
56
56
  {/if}
57
57
 
@@ -59,11 +59,11 @@
59
59
  {#each Array.from({ length: rows }, (_, i) => i) as i (i)}
60
60
  <div class="space-y-2">
61
61
  <div
62
- class="h-4 rounded bg-outline-faint"
62
+ class="h-4 rounded-[3px] bg-surface-sunken"
63
63
  style="width: {70 + ((i * 17) % 30)}%"
64
64
  ></div>
65
65
  <div
66
- class="h-4 rounded bg-surface-sunken"
66
+ class="h-4 rounded-[3px] bg-surface-sunken"
67
67
  style="width: {40 + ((i * 23) % 40)}%"
68
68
  ></div>
69
69
  </div>