@runwisp/ui 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -12
- package/src/lib/components/Alert.svelte +4 -4
- package/src/lib/components/Avatar.svelte +8 -8
- package/src/lib/components/Badge.svelte +23 -23
- package/src/lib/components/CronInput.svelte +1 -1
- package/src/lib/components/Dropdown.svelte +16 -3
- package/src/lib/components/DurationInput.svelte +4 -2
- package/src/lib/components/EmptyState.svelte +1 -1
- package/src/lib/components/FormField.svelte +4 -2
- package/src/lib/components/Input.svelte +4 -2
- package/src/lib/components/LogConsole.svelte +22 -29
- package/src/lib/components/Progress.svelte +4 -4
- package/src/lib/components/RadioGroup.svelte +3 -1
- package/src/lib/components/Select.svelte +1 -1
- package/src/lib/components/Sparkline.svelte +11 -3
- package/src/lib/components/TaskCard.svelte +4 -4
- package/src/lib/components/Textarea.svelte +2 -2
- package/src/lib/components/ToastContainer.svelte +4 -4
- package/src/lib/components/Tooltip.svelte +11 -2
- package/src/lib/components/dashboard/RunDetailPanel.svelte +24 -17
- package/src/lib/components/dashboard/RunsList.svelte +19 -2
- package/src/lib/components/dashboard/cron-format.test.ts +67 -0
- package/src/lib/components/dashboard/cron-format.ts +71 -0
- package/src/lib/components/dashboard/status-config.test.ts +18 -0
- package/src/lib/components/dashboard/status-config.ts +19 -0
- package/src/lib/index.ts +1 -4
- package/src/lib/log-console/ansi.test.ts +50 -0
- package/src/lib/log-console/ansi.ts +39 -0
- package/src/lib/theme-tokens.css +33 -0
- package/src/lib/theme.css +6 -0
- package/src/lib/utils/format.test.ts +37 -1
- package/src/lib/utils/format.ts +8 -6
- package/src/stories/Avatar.stories.svelte +1 -1
- package/src/stories/Breadcrumb.stories.svelte +3 -3
- package/src/stories/Card.stories.svelte +21 -19
- package/src/stories/Checkbox.stories.svelte +2 -2
- package/src/stories/DataGrid.stories.svelte +1 -1
- package/src/stories/Dropdown.stories.svelte +3 -3
- package/src/stories/EmptyState.stories.svelte +1 -1
- package/src/stories/LogConsole.stories.svelte +75 -0
- package/src/stories/LogConsoleDemo.svelte +60 -0
- package/src/stories/Logo.stories.svelte +9 -9
- package/src/stories/Modal.stories.svelte +12 -10
- package/src/stories/Progress.stories.svelte +6 -6
- package/src/stories/Tabs.stories.svelte +16 -12
- package/src/stories/Toggle.stories.svelte +2 -2
- package/src/stories/Tooltip.stories.svelte +5 -5
- package/src/lib/utils/toast.ts +0 -60
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runwisp/ui",
|
|
3
3
|
"description": "Svelte 5 UI component library for RunWisp dashboards and admin interfaces.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.8.0",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -15,14 +15,6 @@
|
|
|
15
15
|
"publishConfig": {
|
|
16
16
|
"access": "public"
|
|
17
17
|
},
|
|
18
|
-
"scripts": {
|
|
19
|
-
"dev": "storybook dev -p 6006 --no-open",
|
|
20
|
-
"build": "echo '@runwisp/ui: no build step (raw source)'",
|
|
21
|
-
"check": "bun run check:types && bun run check:lint",
|
|
22
|
-
"check:types": "svelte-check --tsconfig ./tsconfig.json",
|
|
23
|
-
"check:lint": "prettier --check . && eslint .",
|
|
24
|
-
"format": "prettier --write ."
|
|
25
|
-
},
|
|
26
18
|
"files": [
|
|
27
19
|
"src"
|
|
28
20
|
],
|
|
@@ -58,8 +50,8 @@
|
|
|
58
50
|
"@chromatic-com/storybook": "^4.1.3",
|
|
59
51
|
"@eslint/compat": "^1.4.0",
|
|
60
52
|
"@eslint/js": "^9.39.1",
|
|
61
|
-
"@runwisp/eslint-config": "0.
|
|
62
|
-
"@runwisp/typescript-config": "0.
|
|
53
|
+
"@runwisp/eslint-config": "0.8.0",
|
|
54
|
+
"@runwisp/typescript-config": "0.8.0",
|
|
63
55
|
"@storybook/addon-a11y": "^10.1.9",
|
|
64
56
|
"@storybook/addon-docs": "^10.1.9",
|
|
65
57
|
"@storybook/addon-svelte-csf": "^5.0.10",
|
|
@@ -107,7 +99,7 @@
|
|
|
107
99
|
"@floating-ui/dom": "^1.7.4",
|
|
108
100
|
"@fontsource/tasa-orbiter": "^5.2.2",
|
|
109
101
|
"@lucide/svelte": "^0.561.0",
|
|
110
|
-
"@runwisp/common": "0.
|
|
102
|
+
"@runwisp/common": "0.8.0",
|
|
111
103
|
"@tanstack/svelte-virtual": "^3.13.25",
|
|
112
104
|
"ansi-to-html": "^0.7.2",
|
|
113
105
|
"cron-parser": "^5.4.0",
|
|
@@ -35,25 +35,25 @@
|
|
|
35
35
|
> = {
|
|
36
36
|
info: {
|
|
37
37
|
bg: "bg-info-soft",
|
|
38
|
-
border: "border-
|
|
38
|
+
border: "border-info-soft-border",
|
|
39
39
|
icon: Info,
|
|
40
40
|
iconColor: "text-info",
|
|
41
41
|
},
|
|
42
42
|
success: {
|
|
43
43
|
bg: "bg-success-soft",
|
|
44
|
-
border: "border-success-
|
|
44
|
+
border: "border-success-soft-border",
|
|
45
45
|
icon: CircleCheckBig,
|
|
46
46
|
iconColor: "text-success-surface",
|
|
47
47
|
},
|
|
48
48
|
warning: {
|
|
49
49
|
bg: "bg-warning-soft",
|
|
50
|
-
border: "border-warning-
|
|
50
|
+
border: "border-warning-soft-border",
|
|
51
51
|
icon: TriangleAlert,
|
|
52
52
|
iconColor: "text-warning-surface",
|
|
53
53
|
},
|
|
54
54
|
danger: {
|
|
55
55
|
bg: "bg-danger-soft",
|
|
56
|
-
border: "border-danger-
|
|
56
|
+
border: "border-danger-soft-border",
|
|
57
57
|
icon: CircleX,
|
|
58
58
|
iconColor: "text-danger-surface",
|
|
59
59
|
},
|
|
@@ -30,10 +30,10 @@
|
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
const statusClasses: Record<string, string> = {
|
|
33
|
-
online: "bg-success-
|
|
33
|
+
online: "bg-success-surface",
|
|
34
34
|
offline: "bg-on-surface-faint",
|
|
35
|
-
busy: "bg-danger-
|
|
36
|
-
away: "bg-warning-
|
|
35
|
+
busy: "bg-danger-surface",
|
|
36
|
+
away: "bg-warning-surface",
|
|
37
37
|
};
|
|
38
38
|
|
|
39
39
|
const statusSizes: Record<string, string> = {
|
|
@@ -46,11 +46,11 @@
|
|
|
46
46
|
|
|
47
47
|
// Generate a consistent color based on name
|
|
48
48
|
const colors = [
|
|
49
|
-
"bg-
|
|
50
|
-
"bg-
|
|
51
|
-
"bg-success-
|
|
52
|
-
"bg-warning-
|
|
53
|
-
"bg-danger-
|
|
49
|
+
"bg-primary",
|
|
50
|
+
"bg-info",
|
|
51
|
+
"bg-success-surface",
|
|
52
|
+
"bg-warning-surface",
|
|
53
|
+
"bg-danger-surface",
|
|
54
54
|
];
|
|
55
55
|
|
|
56
56
|
const colorIndex = $derived(
|
|
@@ -35,28 +35,28 @@
|
|
|
35
35
|
|
|
36
36
|
const variantClasses: Record<BadgeVariant, { solid: string; outline: string }> = {
|
|
37
37
|
default: {
|
|
38
|
-
solid: "bg-
|
|
39
|
-
outline: "bg-transparent border border-
|
|
38
|
+
solid: "bg-surface-sunken text-on-surface-muted",
|
|
39
|
+
outline: "bg-transparent border border-outline-hover text-on-surface-muted",
|
|
40
40
|
},
|
|
41
41
|
primary: {
|
|
42
|
-
solid: "bg-
|
|
43
|
-
outline: "bg-transparent border border-wisp-300 text-
|
|
42
|
+
solid: "bg-primary-soft text-primary-soft-text",
|
|
43
|
+
outline: "bg-transparent border border-wisp-300 text-primary-soft-text",
|
|
44
44
|
},
|
|
45
45
|
success: {
|
|
46
|
-
solid: "bg-success-
|
|
47
|
-
outline: "bg-transparent border border-success-300 text-success-
|
|
46
|
+
solid: "bg-success-soft text-success-soft-text",
|
|
47
|
+
outline: "bg-transparent border border-success-300 text-success-soft-text",
|
|
48
48
|
},
|
|
49
49
|
warning: {
|
|
50
|
-
solid: "bg-warning-
|
|
51
|
-
outline: "bg-transparent border border-warning-400 text-warning-
|
|
50
|
+
solid: "bg-warning-soft text-warning-soft-text",
|
|
51
|
+
outline: "bg-transparent border border-warning-400 text-warning-soft-text",
|
|
52
52
|
},
|
|
53
53
|
danger: {
|
|
54
|
-
solid: "bg-danger-
|
|
55
|
-
outline: "bg-transparent border border-danger-300 text-danger-
|
|
54
|
+
solid: "bg-danger-soft text-danger-soft-text",
|
|
55
|
+
outline: "bg-transparent border border-danger-300 text-danger-soft-text",
|
|
56
56
|
},
|
|
57
57
|
info: {
|
|
58
|
-
solid: "bg-
|
|
59
|
-
outline: "bg-transparent border border-aurora-300 text-
|
|
58
|
+
solid: "bg-info-soft text-info-soft-text",
|
|
59
|
+
outline: "bg-transparent border border-aurora-300 text-info-soft-text",
|
|
60
60
|
},
|
|
61
61
|
};
|
|
62
62
|
|
|
@@ -68,20 +68,20 @@
|
|
|
68
68
|
|
|
69
69
|
const dotColors: Record<BadgeVariant, string> = {
|
|
70
70
|
default: "bg-mist-500",
|
|
71
|
-
primary: "bg-
|
|
72
|
-
success: "bg-success-
|
|
73
|
-
warning: "bg-warning-
|
|
74
|
-
danger: "bg-danger-
|
|
75
|
-
info: "bg-
|
|
71
|
+
primary: "bg-primary",
|
|
72
|
+
success: "bg-success-surface",
|
|
73
|
+
warning: "bg-warning-surface",
|
|
74
|
+
danger: "bg-danger-surface",
|
|
75
|
+
info: "bg-info",
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
const indicatorTextColors: Record<BadgeVariant, string> = {
|
|
79
|
-
default: "text-
|
|
80
|
-
primary: "text-
|
|
81
|
-
success: "text-success-
|
|
82
|
-
warning: "text-warning-
|
|
83
|
-
danger: "text-danger-
|
|
84
|
-
info: "text-
|
|
79
|
+
default: "text-on-surface-muted",
|
|
80
|
+
primary: "text-primary",
|
|
81
|
+
success: "text-success-soft-text",
|
|
82
|
+
warning: "text-warning-soft-text",
|
|
83
|
+
danger: "text-danger-soft-text",
|
|
84
|
+
info: "text-info",
|
|
85
85
|
};
|
|
86
86
|
|
|
87
87
|
const spinnerSizeClasses: Record<BadgeSize, string> = {
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
<!-- Live Feedback -->
|
|
89
89
|
<div
|
|
90
90
|
class="flex items-start gap-3 rounded-md border p-3 transition-colors duration-200
|
|
91
|
-
{isError ? 'border-danger-
|
|
91
|
+
{isError ? 'border-danger-soft-border bg-danger-soft' : 'border-outline bg-surface-sunken'}"
|
|
92
92
|
>
|
|
93
93
|
<Info size={18} class="mt-0.5 {isError ? 'text-danger-surface' : 'text-primary'}" />
|
|
94
94
|
<div>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import type { Snippet } from "svelte";
|
|
6
6
|
import type { Component } from "svelte";
|
|
7
7
|
import { tick, onDestroy } from "svelte";
|
|
8
|
-
import { EllipsisVertical } from "@lucide/svelte";
|
|
8
|
+
import { EllipsisVertical, Check } from "@lucide/svelte";
|
|
9
9
|
import { scale } from "svelte/transition";
|
|
10
10
|
import { quintOut } from "svelte/easing";
|
|
11
11
|
import { computePosition, autoUpdate, flip, shift, offset } from "@floating-ui/dom";
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
danger?: boolean;
|
|
19
19
|
disabled?: boolean;
|
|
20
20
|
divider?: boolean;
|
|
21
|
+
selected?: boolean;
|
|
21
22
|
title?: string;
|
|
22
23
|
}
|
|
23
24
|
|
|
@@ -26,10 +27,18 @@
|
|
|
26
27
|
trigger?: Snippet;
|
|
27
28
|
children?: Snippet;
|
|
28
29
|
align?: "left" | "right";
|
|
30
|
+
triggerLabel?: string;
|
|
29
31
|
class?: string;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
let {
|
|
34
|
+
let {
|
|
35
|
+
items = [],
|
|
36
|
+
trigger,
|
|
37
|
+
children,
|
|
38
|
+
align = "right",
|
|
39
|
+
triggerLabel = "Options",
|
|
40
|
+
class: className = "",
|
|
41
|
+
}: Props = $props();
|
|
33
42
|
|
|
34
43
|
let open = $state(false);
|
|
35
44
|
let triggerEl = $state<HTMLElement | null>(null);
|
|
@@ -116,7 +125,8 @@
|
|
|
116
125
|
toggle(!open);
|
|
117
126
|
}}
|
|
118
127
|
bind:this={triggerEl}
|
|
119
|
-
aria-label=
|
|
128
|
+
aria-label={triggerLabel}
|
|
129
|
+
aria-expanded={open}
|
|
120
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"
|
|
121
131
|
>
|
|
122
132
|
{#if trigger}
|
|
@@ -186,6 +196,9 @@
|
|
|
186
196
|
<Icon size={16} class="opacity-70" />
|
|
187
197
|
{/if}
|
|
188
198
|
{item.label}
|
|
199
|
+
{#if item.selected}
|
|
200
|
+
<Check size={16} class="ml-auto text-primary" />
|
|
201
|
+
{/if}
|
|
189
202
|
</button>
|
|
190
203
|
{/if}
|
|
191
204
|
{/each}
|
|
@@ -147,7 +147,7 @@
|
|
|
147
147
|
|
|
148
148
|
{#if error}
|
|
149
149
|
<div
|
|
150
|
-
class="pointer-events-none absolute top-1/2 right-3 -translate-y-1/2 text-danger-
|
|
150
|
+
class="pointer-events-none absolute top-1/2 right-3 -translate-y-1/2 text-danger-soft-text"
|
|
151
151
|
>
|
|
152
152
|
<CircleAlert size={18} />
|
|
153
153
|
</div>
|
|
@@ -161,7 +161,9 @@
|
|
|
161
161
|
{parsedHuman}
|
|
162
162
|
</p>
|
|
163
163
|
{:else if error}
|
|
164
|
-
<p
|
|
164
|
+
<p
|
|
165
|
+
class="animate-in slide-in-from-top-1 fade-in text-sm text-danger-soft-text duration-200"
|
|
166
|
+
>
|
|
165
167
|
{error}
|
|
166
168
|
</p>
|
|
167
169
|
{:else if hint}
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
<label for={generatedId} class="block text-sm font-medium text-on-surface-muted">
|
|
33
33
|
{label}
|
|
34
34
|
{#if required}
|
|
35
|
-
<span class="text-danger-
|
|
35
|
+
<span class="text-danger-soft-text">*</span>
|
|
36
36
|
{/if}
|
|
37
37
|
</label>
|
|
38
38
|
{/if}
|
|
@@ -44,7 +44,9 @@
|
|
|
44
44
|
{@render children()}
|
|
45
45
|
|
|
46
46
|
{#if error}
|
|
47
|
-
<p
|
|
47
|
+
<p
|
|
48
|
+
class="animate-in slide-in-from-top-1 fade-in text-sm text-danger-soft-text duration-200"
|
|
49
|
+
>
|
|
48
50
|
{error}
|
|
49
51
|
</p>
|
|
50
52
|
{:else if hint}
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
|
|
92
92
|
{#if error}
|
|
93
93
|
<div
|
|
94
|
-
class="pointer-events-none absolute top-1/2 right-3 -translate-y-1/2 text-danger-
|
|
94
|
+
class="pointer-events-none absolute top-1/2 right-3 -translate-y-1/2 text-danger-soft-text"
|
|
95
95
|
>
|
|
96
96
|
<CircleAlert size={18} />
|
|
97
97
|
</div>
|
|
@@ -105,7 +105,9 @@
|
|
|
105
105
|
</div>
|
|
106
106
|
|
|
107
107
|
{#if error}
|
|
108
|
-
<p
|
|
108
|
+
<p
|
|
109
|
+
class="animate-in slide-in-from-top-1 fade-in text-sm text-danger-soft-text duration-200"
|
|
110
|
+
>
|
|
109
111
|
{error}
|
|
110
112
|
</p>
|
|
111
113
|
{:else if hint}
|
|
@@ -3,19 +3,12 @@
|
|
|
3
3
|
|
|
4
4
|
<script lang="ts">
|
|
5
5
|
import { untrack } from "svelte";
|
|
6
|
-
import
|
|
6
|
+
import { ansiLineToHtml } from "../log-console/ansi.js";
|
|
7
7
|
import { LogCache } from "../log-console/LogCache.svelte.js";
|
|
8
8
|
import { LogFetcher } from "../log-console/LogFetcher.svelte.js";
|
|
9
9
|
import type { FetchLogsFn, LogEvent } from "../log-console/types.js";
|
|
10
10
|
import { formatBytes } from "../utils/format.js";
|
|
11
11
|
|
|
12
|
-
// Convert ANSI escape sequences in a single log line to safe HTML spans.
|
|
13
|
-
// A fresh converter per call ensures no colour state leaks between
|
|
14
|
-
// independently virtualised lines.
|
|
15
|
-
function ansiLineToHtml(text: string): string {
|
|
16
|
-
return new Convert({ escapeXML: true }).toHtml(text);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
12
|
interface Props {
|
|
20
13
|
fetchLogs?: FetchLogsFn;
|
|
21
14
|
chunkSize?: number;
|
|
@@ -256,13 +249,13 @@
|
|
|
256
249
|
</script>
|
|
257
250
|
|
|
258
251
|
<div
|
|
259
|
-
class="log-console relative flex h-full w-full flex-col overflow-hidden bg-
|
|
252
|
+
class="log-console relative flex h-full w-full flex-col overflow-hidden bg-mist-950 font-mono text-sm {className}"
|
|
260
253
|
>
|
|
261
254
|
<div bind:this={containerEl} class="flex-1 overflow-auto" onscroll={onScroll}>
|
|
262
255
|
<div class="relative" style="height: {totalHeight}px; min-height: 100%;">
|
|
263
256
|
{#if cache.firstAvailableLine > 0}
|
|
264
257
|
<div
|
|
265
|
-
class="absolute right-0 left-0 flex items-center bg-
|
|
258
|
+
class="absolute right-0 left-0 flex items-center bg-warning-700/20 text-warning-400"
|
|
266
259
|
style="top: 0px; height: {lineHeight}px;"
|
|
267
260
|
>
|
|
268
261
|
<div
|
|
@@ -280,24 +273,24 @@
|
|
|
280
273
|
|
|
281
274
|
{#each renderedLines as line (line.num)}
|
|
282
275
|
<div
|
|
283
|
-
class="log-line absolute right-0 left-0 flex items-center hover:bg-
|
|
276
|
+
class="log-line absolute right-0 left-0 flex items-center hover:bg-mist-900/50 {flashLine ===
|
|
284
277
|
line.num
|
|
285
278
|
? 'log-line--flash'
|
|
286
279
|
: ''}"
|
|
287
280
|
style="top: {lineTop(line.num)}px; height: {lineHeight}px;"
|
|
288
281
|
>
|
|
289
282
|
<div
|
|
290
|
-
class="flex-shrink-0 pr-3 text-right text-
|
|
283
|
+
class="flex-shrink-0 pr-3 text-right text-mist-500 select-none"
|
|
291
284
|
style="width: {gutterWidth}px;"
|
|
292
285
|
>
|
|
293
286
|
{line.num + 1}
|
|
294
287
|
</div>
|
|
295
|
-
<div class="flex-1 truncate pr-4 text-
|
|
288
|
+
<div class="flex-1 truncate pr-4 text-mist-200">
|
|
296
289
|
{#if line.text !== undefined}
|
|
297
290
|
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
298
291
|
<span class="whitespace-pre">{@html ansiLineToHtml(line.text)}</span>
|
|
299
292
|
{:else}
|
|
300
|
-
<span class="text-
|
|
293
|
+
<span class="text-mist-600 italic">Loading...</span>
|
|
301
294
|
{/if}
|
|
302
295
|
</div>
|
|
303
296
|
</div>
|
|
@@ -312,7 +305,7 @@
|
|
|
312
305
|
class="flex-shrink-0 pr-3 text-right select-none"
|
|
313
306
|
style="width: {gutterWidth}px;"
|
|
314
307
|
></div>
|
|
315
|
-
<div class="flex items-center gap-1 text-
|
|
308
|
+
<div class="flex items-center gap-1 text-mist-400">
|
|
316
309
|
<span class="dot" style="animation-delay: 0ms;"></span>
|
|
317
310
|
<span class="dot" style="animation-delay: 150ms;"></span>
|
|
318
311
|
<span class="dot" style="animation-delay: 300ms;"></span>
|
|
@@ -329,7 +322,7 @@
|
|
|
329
322
|
)}px; height: {lineHeight}px;"
|
|
330
323
|
>
|
|
331
324
|
<div
|
|
332
|
-
class="flex-shrink-0 pr-3 text-right text-
|
|
325
|
+
class="flex-shrink-0 pr-3 text-right text-mist-700 opacity-0 select-none"
|
|
333
326
|
style="width: {gutterWidth}px;"
|
|
334
327
|
>
|
|
335
328
|
~
|
|
@@ -339,16 +332,16 @@
|
|
|
339
332
|
{/if}
|
|
340
333
|
|
|
341
334
|
{#if cache.totalLines === 0 && !fetcher?.isFetching}
|
|
342
|
-
<div class="absolute inset-0 flex items-center justify-center text-
|
|
335
|
+
<div class="absolute inset-0 flex items-center justify-center text-mist-500">
|
|
343
336
|
<div class="text-center">
|
|
344
337
|
<div class="mb-1 text-lg">No output yet</div>
|
|
345
|
-
<div class="text-sm text-
|
|
338
|
+
<div class="text-sm text-mist-600">Waiting for logs...</div>
|
|
346
339
|
</div>
|
|
347
340
|
</div>
|
|
348
341
|
{/if}
|
|
349
342
|
|
|
350
343
|
{#if cache.totalLines === 0 && fetcher?.isFetching}
|
|
351
|
-
<div class="absolute inset-0 flex items-center justify-center text-
|
|
344
|
+
<div class="absolute inset-0 flex items-center justify-center text-mist-500">
|
|
352
345
|
<div class="flex items-center gap-2">
|
|
353
346
|
<span class="dot" style="animation-delay: 0ms;"></span>
|
|
354
347
|
<span class="dot" style="animation-delay: 150ms;"></span>
|
|
@@ -363,11 +356,11 @@
|
|
|
363
356
|
{#if userScrolledUp && cache.totalLines > 0}
|
|
364
357
|
<button
|
|
365
358
|
class="absolute right-4 bottom-12 z-10 flex items-center gap-2 rounded-full border
|
|
366
|
-
border-
|
|
359
|
+
border-mist-700 bg-mist-800
|
|
367
360
|
px-3 py-1.5 text-xs
|
|
368
|
-
font-medium text-
|
|
361
|
+
font-medium text-mist-200 shadow-lg
|
|
369
362
|
transition-all duration-200 hover:scale-105
|
|
370
|
-
hover:bg-
|
|
363
|
+
hover:bg-mist-700 active:scale-95"
|
|
371
364
|
onclick={enableAutoScroll}
|
|
372
365
|
>
|
|
373
366
|
<svg
|
|
@@ -384,20 +377,20 @@
|
|
|
384
377
|
{/if}
|
|
385
378
|
|
|
386
379
|
<div
|
|
387
|
-
class="flex flex-shrink-0 items-center justify-between border-t border-
|
|
388
|
-
bg-
|
|
380
|
+
class="flex flex-shrink-0 items-center justify-between border-t border-mist-800
|
|
381
|
+
bg-mist-900/80 px-3 py-1.5 text-xs text-mist-400"
|
|
389
382
|
>
|
|
390
383
|
<div class="flex items-center gap-3">
|
|
391
384
|
{#if isStreaming}
|
|
392
|
-
<div class="flex items-center gap-1.5 text-
|
|
385
|
+
<div class="flex items-center gap-1.5 text-info">
|
|
393
386
|
<div class="h-1.5 w-1.5 animate-pulse rounded-full bg-aurora-400"></div>
|
|
394
387
|
Streaming
|
|
395
388
|
</div>
|
|
396
389
|
{:else if cache.finished}
|
|
397
|
-
<span class="text-
|
|
390
|
+
<span class="text-mist-500">Stream ended</span>
|
|
398
391
|
{/if}
|
|
399
392
|
{#if fetcher?.isFetching}
|
|
400
|
-
<span class="text-
|
|
393
|
+
<span class="text-mist-500">Fetching...</span>
|
|
401
394
|
{/if}
|
|
402
395
|
</div>
|
|
403
396
|
<div class="flex items-center gap-4">
|
|
@@ -406,7 +399,7 @@
|
|
|
406
399
|
<span>{formatBytes(cache.totalBytes)}</span>
|
|
407
400
|
{/if}
|
|
408
401
|
{#if isAutoScroll}
|
|
409
|
-
<span class="text-
|
|
402
|
+
<span class="text-info">Auto-scroll</span>
|
|
410
403
|
{/if}
|
|
411
404
|
</div>
|
|
412
405
|
</div>
|
|
@@ -423,7 +416,7 @@
|
|
|
423
416
|
|
|
424
417
|
@keyframes log-line-flash {
|
|
425
418
|
0% {
|
|
426
|
-
background-color:
|
|
419
|
+
background-color: oklch(0.8 0.18 70 / 0.45);
|
|
427
420
|
}
|
|
428
421
|
100% {
|
|
429
422
|
background-color: transparent;
|
|
@@ -28,10 +28,10 @@
|
|
|
28
28
|
const percentage = $derived(Math.min(Math.max((value / max) * 100, 0), 100));
|
|
29
29
|
|
|
30
30
|
const variantClasses: Record<ProgressVariant, string> = {
|
|
31
|
-
default: "bg-
|
|
32
|
-
success: "bg-success-
|
|
33
|
-
warning: "bg-warning-
|
|
34
|
-
danger: "bg-danger-
|
|
31
|
+
default: "bg-primary",
|
|
32
|
+
success: "bg-success-surface",
|
|
33
|
+
warning: "bg-warning-surface",
|
|
34
|
+
danger: "bg-danger-surface",
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
const sizeClasses: Record<ProgressSize, string> = {
|
|
@@ -44,7 +44,9 @@
|
|
|
44
44
|
</div>
|
|
45
45
|
|
|
46
46
|
{#if error}
|
|
47
|
-
<p
|
|
47
|
+
<p
|
|
48
|
+
class="animate-in slide-in-from-top-1 fade-in text-sm text-danger-soft-text duration-200"
|
|
49
|
+
>
|
|
48
50
|
{error}
|
|
49
51
|
</p>
|
|
50
52
|
{:else if hint}
|
|
@@ -6,14 +6,19 @@
|
|
|
6
6
|
data: number[];
|
|
7
7
|
color?: string;
|
|
8
8
|
fillColor?: string;
|
|
9
|
+
fillOpacity?: number;
|
|
9
10
|
height?: number;
|
|
10
11
|
class?: string;
|
|
11
12
|
}
|
|
12
13
|
|
|
14
|
+
// `currentColor` by default so the caller can theme us with a Tailwind
|
|
15
|
+
// text-* class — that's the only way to flip on .dark without burning a
|
|
16
|
+
// hardcoded hex in here.
|
|
13
17
|
let {
|
|
14
18
|
data,
|
|
15
|
-
color = "
|
|
19
|
+
color = "currentColor",
|
|
16
20
|
fillColor,
|
|
21
|
+
fillOpacity = 0.12,
|
|
17
22
|
height = 40,
|
|
18
23
|
class: className = "",
|
|
19
24
|
}: Props = $props();
|
|
@@ -42,7 +47,10 @@
|
|
|
42
47
|
return `${pathD} L${lastX.toFixed(1)},${height} L0,${height} Z`;
|
|
43
48
|
});
|
|
44
49
|
|
|
45
|
-
|
|
50
|
+
// Use `fill-opacity` rather than appending "18" to a hex — that trick
|
|
51
|
+
// breaks for `currentColor` / `oklch()` / `var(...)` values.
|
|
52
|
+
let resolvedFill = $derived(fillColor ?? color);
|
|
53
|
+
let resolvedFillOpacity = $derived(fillColor === undefined ? fillOpacity : 1);
|
|
46
54
|
</script>
|
|
47
55
|
|
|
48
56
|
<svg
|
|
@@ -52,7 +60,7 @@
|
|
|
52
60
|
style="height: {height}px"
|
|
53
61
|
>
|
|
54
62
|
{#if data.length >= 2}
|
|
55
|
-
<path d={fillD} fill={resolvedFill} />
|
|
63
|
+
<path d={fillD} fill={resolvedFill} fill-opacity={resolvedFillOpacity} />
|
|
56
64
|
<path d={pathD} fill="none" stroke={color} stroke-width="1.5" stroke-linejoin="round" />
|
|
57
65
|
{/if}
|
|
58
66
|
</svg>
|
|
@@ -16,15 +16,15 @@
|
|
|
16
16
|
let { accent = "wisp", onclick, children, class: className = "" }: Props = $props();
|
|
17
17
|
|
|
18
18
|
const accentClasses: Record<TaskCardAccent, string> = {
|
|
19
|
-
danger: "hover:border-danger-
|
|
20
|
-
wisp: "hover:border-
|
|
21
|
-
aurora: "hover:border-
|
|
19
|
+
danger: "hover:border-danger-soft-border",
|
|
20
|
+
wisp: "hover:border-primary-soft-border",
|
|
21
|
+
aurora: "hover:border-info-soft-border",
|
|
22
22
|
};
|
|
23
23
|
</script>
|
|
24
24
|
|
|
25
25
|
<button
|
|
26
26
|
type="button"
|
|
27
|
-
class="group w-full rounded-lg border border-
|
|
27
|
+
class="group w-full rounded-lg border border-outline-faint bg-surface-raised p-3 text-left transition-all hover:shadow-sm {accentClasses[
|
|
28
28
|
accent
|
|
29
29
|
]} {className}"
|
|
30
30
|
{onclick}
|
|
@@ -67,14 +67,14 @@
|
|
|
67
67
|
></textarea>
|
|
68
68
|
|
|
69
69
|
{#if error}
|
|
70
|
-
<div class="pointer-events-none absolute top-3 right-3 text-danger-
|
|
70
|
+
<div class="pointer-events-none absolute top-3 right-3 text-danger-soft-text">
|
|
71
71
|
<CircleAlert size={18} />
|
|
72
72
|
</div>
|
|
73
73
|
{/if}
|
|
74
74
|
</div>
|
|
75
75
|
|
|
76
76
|
{#if error}
|
|
77
|
-
<p class="text-sm text-danger-
|
|
77
|
+
<p class="text-sm text-danger-soft-text">{error}</p>
|
|
78
78
|
{:else if hint}
|
|
79
79
|
<p class="text-sm text-on-surface-muted">{hint}</p>
|
|
80
80
|
{/if}
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
function getColorClasses(type: Toast["type"]) {
|
|
27
27
|
switch (type) {
|
|
28
28
|
case "success":
|
|
29
|
-
return "border-success-
|
|
29
|
+
return "border-success-soft-border text-success-soft-text";
|
|
30
30
|
case "error":
|
|
31
|
-
return "border-danger-
|
|
31
|
+
return "border-danger-soft-border text-danger-soft-text";
|
|
32
32
|
case "warning":
|
|
33
|
-
return "border-warning-
|
|
33
|
+
return "border-warning-soft-border text-warning-soft-text";
|
|
34
34
|
case "info":
|
|
35
|
-
return "border-
|
|
35
|
+
return "border-primary-soft-border text-primary-soft-text";
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -11,9 +11,17 @@
|
|
|
11
11
|
position?: TooltipPosition;
|
|
12
12
|
children: Snippet;
|
|
13
13
|
class?: string;
|
|
14
|
+
/** Wrap long content across lines instead of one nowrap line. */
|
|
15
|
+
wide?: boolean;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
let {
|
|
18
|
+
let {
|
|
19
|
+
content,
|
|
20
|
+
position = "top",
|
|
21
|
+
children,
|
|
22
|
+
class: className = "",
|
|
23
|
+
wide = false,
|
|
24
|
+
}: Props = $props();
|
|
17
25
|
|
|
18
26
|
const positionClasses: Record<TooltipPosition, string> = {
|
|
19
27
|
top: "bottom-full left-1/2 -translate-x-1/2 mb-2",
|
|
@@ -39,7 +47,8 @@
|
|
|
39
47
|
pointer-events-none invisible rounded-lg
|
|
40
48
|
bg-mist-800 px-2.5 py-1.5 text-xs
|
|
41
49
|
font-medium
|
|
42
|
-
|
|
50
|
+
{wide ? 'w-max max-w-xs whitespace-normal' : 'whitespace-nowrap'}
|
|
51
|
+
text-white opacity-0 transition-opacity
|
|
43
52
|
duration-150 group-hover:visible
|
|
44
53
|
group-hover:opacity-100
|
|
45
54
|
"
|