@runwisp/ui 0.6.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.
Files changed (48) hide show
  1. package/package.json +4 -12
  2. package/src/lib/components/Alert.svelte +4 -4
  3. package/src/lib/components/Avatar.svelte +8 -8
  4. package/src/lib/components/Badge.svelte +23 -23
  5. package/src/lib/components/CronInput.svelte +1 -1
  6. package/src/lib/components/Dropdown.svelte +16 -3
  7. package/src/lib/components/DurationInput.svelte +4 -2
  8. package/src/lib/components/EmptyState.svelte +1 -1
  9. package/src/lib/components/FormField.svelte +4 -2
  10. package/src/lib/components/Input.svelte +4 -2
  11. package/src/lib/components/LogConsole.svelte +76 -29
  12. package/src/lib/components/Progress.svelte +4 -4
  13. package/src/lib/components/RadioGroup.svelte +3 -1
  14. package/src/lib/components/Select.svelte +1 -1
  15. package/src/lib/components/Sparkline.svelte +11 -3
  16. package/src/lib/components/TaskCard.svelte +4 -4
  17. package/src/lib/components/Textarea.svelte +2 -2
  18. package/src/lib/components/ToastContainer.svelte +4 -4
  19. package/src/lib/components/Tooltip.svelte +11 -2
  20. package/src/lib/components/dashboard/RunDetailPanel.svelte +27 -17
  21. package/src/lib/components/dashboard/RunsList.svelte +19 -2
  22. package/src/lib/components/dashboard/cron-format.test.ts +67 -0
  23. package/src/lib/components/dashboard/cron-format.ts +71 -0
  24. package/src/lib/components/dashboard/status-config.test.ts +18 -0
  25. package/src/lib/components/dashboard/status-config.ts +19 -0
  26. package/src/lib/index.ts +1 -4
  27. package/src/lib/log-console/ansi.test.ts +50 -0
  28. package/src/lib/log-console/ansi.ts +39 -0
  29. package/src/lib/theme-tokens.css +33 -0
  30. package/src/lib/theme.css +6 -0
  31. package/src/lib/utils/format.test.ts +37 -1
  32. package/src/lib/utils/format.ts +8 -6
  33. package/src/stories/Avatar.stories.svelte +1 -1
  34. package/src/stories/Breadcrumb.stories.svelte +3 -3
  35. package/src/stories/Card.stories.svelte +21 -19
  36. package/src/stories/Checkbox.stories.svelte +2 -2
  37. package/src/stories/DataGrid.stories.svelte +1 -1
  38. package/src/stories/Dropdown.stories.svelte +3 -3
  39. package/src/stories/EmptyState.stories.svelte +1 -1
  40. package/src/stories/LogConsole.stories.svelte +75 -0
  41. package/src/stories/LogConsoleDemo.svelte +60 -0
  42. package/src/stories/Logo.stories.svelte +9 -9
  43. package/src/stories/Modal.stories.svelte +12 -10
  44. package/src/stories/Progress.stories.svelte +6 -6
  45. package/src/stories/Tabs.stories.svelte +16 -12
  46. package/src/stories/Toggle.stories.svelte +2 -2
  47. package/src/stories/Tooltip.stories.svelte +5 -5
  48. package/src/lib/utils/toast.ts +0 -60
@@ -88,6 +88,27 @@
88
88
  --rw-color-warning-600: oklch(0.65 0.14 60);
89
89
  --rw-color-warning-700: oklch(0.55 0.12 55);
90
90
 
91
+ /* ANSI console palette — 16-color map for the log console, tuned for
92
+ * its always-dark surface (no .dark override needed). Hues follow the
93
+ * semantic scales: red→danger, green→success, yellow→warning,
94
+ * blue→wisp, cyan→aurora, grays→mist. */
95
+ --rw-ansi-black: var(--rw-color-mist-700);
96
+ --rw-ansi-red: var(--rw-color-danger-400);
97
+ --rw-ansi-green: var(--rw-color-success-400);
98
+ --rw-ansi-yellow: var(--rw-color-warning-400);
99
+ --rw-ansi-blue: var(--rw-color-wisp-400);
100
+ --rw-ansi-magenta: oklch(0.68 0.14 320); /* between wisp and danger hues */
101
+ --rw-ansi-cyan: var(--rw-color-aurora-400);
102
+ --rw-ansi-white: var(--rw-color-mist-300);
103
+ --rw-ansi-bright-black: var(--rw-color-mist-500);
104
+ --rw-ansi-bright-red: var(--rw-color-danger-300);
105
+ --rw-ansi-bright-green: var(--rw-color-success-300);
106
+ --rw-ansi-bright-yellow: var(--rw-color-warning-300);
107
+ --rw-ansi-bright-blue: var(--rw-color-wisp-300);
108
+ --rw-ansi-bright-magenta: oklch(0.78 0.1 320);
109
+ --rw-ansi-bright-cyan: var(--rw-color-aurora-300);
110
+ --rw-ansi-bright-white: var(--rw-color-mist-50);
111
+
91
112
  /* Light-mode semantic tokens (default) */
92
113
  /* Surfaces */
93
114
  --rw-surface: var(--rw-color-mist-50);
@@ -136,6 +157,12 @@
136
157
  --rw-on-info: white;
137
158
  --rw-info-soft: var(--rw-color-aurora-50);
138
159
  --rw-info-soft-text: var(--rw-color-aurora-700);
160
+ /* Soft callout borders — sit on the matching *-soft surface */
161
+ --rw-primary-soft-border: var(--rw-color-wisp-200);
162
+ --rw-danger-soft-border: var(--rw-color-danger-200);
163
+ --rw-success-soft-border: var(--rw-color-success-200);
164
+ --rw-warning-soft-border: var(--rw-color-warning-200);
165
+ --rw-info-soft-border: var(--rw-color-aurora-200);
139
166
  /* Glass */
140
167
  --rw-glass: oklch(1 0 0 / 0.8);
141
168
  --rw-glass-blur: blur(12px);
@@ -189,6 +216,12 @@
189
216
  --rw-on-info: white;
190
217
  --rw-info-soft: oklch(0.2 0.04 180);
191
218
  --rw-info-soft-text: var(--rw-color-aurora-300);
219
+ /* Soft callout borders */
220
+ --rw-primary-soft-border: oklch(0.32 0.06 270);
221
+ --rw-danger-soft-border: oklch(0.32 0.06 355);
222
+ --rw-success-soft-border: oklch(0.32 0.06 165);
223
+ --rw-warning-soft-border: oklch(0.32 0.06 85);
224
+ --rw-info-soft-border: oklch(0.32 0.06 180);
192
225
  /* Glass */
193
226
  --rw-glass: oklch(0.12 0.012 280 / 0.9);
194
227
  }
package/src/lib/theme.css CHANGED
@@ -201,6 +201,12 @@
201
201
  --color-on-info: var(--rw-on-info);
202
202
  --color-info-soft: var(--rw-info-soft);
203
203
  --color-info-soft-text: var(--rw-info-soft-text);
204
+ /* Soft callout borders */
205
+ --color-primary-soft-border: var(--rw-primary-soft-border);
206
+ --color-danger-soft-border: var(--rw-danger-soft-border);
207
+ --color-success-soft-border: var(--rw-success-soft-border);
208
+ --color-warning-soft-border: var(--rw-warning-soft-border);
209
+ --color-info-soft-border: var(--rw-info-soft-border);
204
210
 
205
211
  /* Transition durations */
206
212
  --duration-fast: 100ms;
@@ -2,7 +2,12 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import { describe, expect, it } from "vitest";
5
- import { formatBytes, formatDuration, formatRelativeTimeWithAbsolute } from "./format.js";
5
+ import {
6
+ formatBytes,
7
+ formatDuration,
8
+ formatRelativeTime,
9
+ formatRelativeTimeWithAbsolute,
10
+ } from "./format.js";
6
11
 
7
12
  describe("formatBytes", () => {
8
13
  it("formats 0 bytes", () => {
@@ -85,4 +90,35 @@ describe("formatRelativeTimeWithAbsolute", () => {
85
90
  expect(r).toMatch(/\([^)]*\d[^)]*\)$/);
86
91
  expect(r).not.toMatch(/\(\d{1,2}:\d{2}\)$/);
87
92
  });
93
+ it("computes the relative part against the supplied now", () => {
94
+ const date = new Date("2024-01-01T12:00:00Z");
95
+ const twoHoursBefore = formatRelativeTimeWithAbsolute(
96
+ date,
97
+ new Date("2024-01-01T10:00:00Z"),
98
+ );
99
+ const oneHourBefore = formatRelativeTimeWithAbsolute(
100
+ date,
101
+ new Date("2024-01-01T11:00:00Z"),
102
+ );
103
+ expect(twoHoursBefore).toMatch(/^in 2 hours/);
104
+ expect(oneHourBefore).toMatch(/^in 1 hour/);
105
+ });
106
+ it("picks the day-boundary branch against the supplied now", () => {
107
+ const date = new Date("2024-01-01T12:00:00Z");
108
+ // 2 days after the date → calendar-date suffix, not HH:MM.
109
+ const r = formatRelativeTimeWithAbsolute(date, new Date("2024-01-03T12:00:00Z"));
110
+ expect(r).not.toMatch(/\(\d{1,2}:\d{2}\)$/);
111
+ });
112
+ });
113
+
114
+ describe("formatRelativeTime", () => {
115
+ it("advances as the supplied now advances", () => {
116
+ const date = new Date("2024-01-01T12:00:00Z");
117
+ expect(formatRelativeTime(date, new Date("2024-01-01T14:00:00Z"))).toBe(
118
+ "about 2 hours ago",
119
+ );
120
+ expect(formatRelativeTime(date, new Date("2024-01-01T15:00:00Z"))).toBe(
121
+ "about 3 hours ago",
122
+ );
123
+ });
88
124
  });
@@ -1,7 +1,7 @@
1
1
  // SPDX-FileCopyrightText: PoppyCake, s.r.o.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import { formatDistanceToNow } from "date-fns";
4
+ import { formatDistance } from "date-fns";
5
5
 
6
6
  export function formatBytes(bytes: number): string {
7
7
  const units: [string, ...string[]] = ["B", "KB", "MB", "GB", "TB", "PB"];
@@ -16,14 +16,16 @@ export function formatBytes(bytes: number): string {
16
16
  return `${new Intl.NumberFormat("en", { maximumFractionDigits: digits }).format(value)} ${unit}`;
17
17
  }
18
18
 
19
- export function formatRelativeTime(dateStr: string | Date): string {
20
- return formatDistanceToNow(new Date(dateStr), { addSuffix: true });
19
+ export function formatRelativeTime(dateStr: string | Date, now: Date = new Date()): string {
20
+ return formatDistance(new Date(dateStr), now, { addSuffix: true });
21
21
  }
22
22
 
23
- export function formatRelativeTimeWithAbsolute(dateStr: string | Date): string {
23
+ export function formatRelativeTimeWithAbsolute(
24
+ dateStr: string | Date,
25
+ now: Date = new Date(),
26
+ ): string {
24
27
  const date = new Date(dateStr);
25
- const relative = formatDistanceToNow(date, { addSuffix: true }).replace("about ", "");
26
- const now = new Date();
28
+ const relative = formatDistance(date, now, { addSuffix: true }).replace("about ", "");
27
29
  const diffMs = Math.abs(now.getTime() - date.getTime());
28
30
  const diffDays = diffMs / (1000 * 60 * 60 * 24);
29
31
 
@@ -68,7 +68,7 @@
68
68
  <Avatar name="Charlie Brown" size="md" />
69
69
  <Avatar name="Diana Ross" size="md" />
70
70
  <div
71
- class="flex h-10 w-10 items-center justify-center rounded-full bg-mist-200 text-sm font-medium text-mist-600 ring-2 ring-white"
71
+ class="flex h-10 w-10 items-center justify-center rounded-full bg-mist-200 text-sm font-medium text-on-surface-muted ring-2 ring-white"
72
72
  >
73
73
  +5
74
74
  </div>
@@ -49,7 +49,7 @@
49
49
  />
50
50
 
51
51
  <Story name="In Page Context" asChild>
52
- <div class="rounded-xl border border-mist-200 bg-white p-6">
52
+ <div class="rounded-xl border border-outline bg-white p-6">
53
53
  <Breadcrumb
54
54
  items={[
55
55
  { label: "Dashboard", href: "/" },
@@ -58,7 +58,7 @@
58
58
  ]}
59
59
  class="mb-4"
60
60
  />
61
- <h1 class="text-2xl font-semibold text-mist-900">backup-database</h1>
62
- <p class="mt-1 text-mist-500">Daily database backup task</p>
61
+ <h1 class="text-2xl font-semibold text-on-surface">backup-database</h1>
62
+ <p class="mt-1 text-on-surface-muted">Daily database backup task</p>
63
63
  </div>
64
64
  </Story>
@@ -25,26 +25,28 @@
25
25
  </script>
26
26
 
27
27
  <Story name="Default" args={{}}>
28
- <h3 class="mb-2 text-lg font-semibold text-mist-900">Card Title</h3>
29
- <p class="text-mist-600">This is a basic card component with default padding and styling.</p>
28
+ <h3 class="mb-2 text-lg font-semibold text-on-surface">Card Title</h3>
29
+ <p class="text-on-surface-muted">
30
+ This is a basic card component with default padding and styling.
31
+ </p>
30
32
  </Story>
31
33
 
32
34
  <Story name="With Header and Footer" asChild>
33
35
  <Card>
34
36
  {#snippet header()}
35
37
  <div class="flex items-center justify-between">
36
- <h3 class="font-semibold text-mist-900">Task Configuration</h3>
38
+ <h3 class="font-semibold text-on-surface">Task Configuration</h3>
37
39
  <Badge variant="success" dot>Active</Badge>
38
40
  </div>
39
41
  {/snippet}
40
42
 
41
43
  <div class="space-y-3">
42
- <p class="text-sm text-mist-600">
44
+ <p class="text-sm text-on-surface-muted">
43
45
  Configure your scheduled task settings and execution parameters.
44
46
  </p>
45
47
  <div class="text-sm">
46
- <span class="text-mist-500">Schedule:</span>
47
- <span class="ml-2 font-mono text-mist-800">*/5 * * * *</span>
48
+ <span class="text-on-surface-muted">Schedule:</span>
49
+ <span class="ml-2 font-mono text-on-surface">*/5 * * * *</span>
48
50
  </div>
49
51
  </div>
50
52
 
@@ -60,16 +62,16 @@
60
62
  <Story name="Hoverable" asChild>
61
63
  <div class="grid max-w-3xl grid-cols-3 gap-4">
62
64
  <Card hover>
63
- <h4 class="mb-1 font-semibold text-mist-900">Total Tasks</h4>
64
- <p class="text-2xl font-bold text-wisp-600">156</p>
65
+ <h4 class="mb-1 font-semibold text-on-surface">Total Tasks</h4>
66
+ <p class="text-2xl font-bold text-primary">156</p>
65
67
  </Card>
66
68
  <Card hover>
67
- <h4 class="mb-1 font-semibold text-mist-900">Active</h4>
68
- <p class="text-2xl font-bold text-success-600">142</p>
69
+ <h4 class="mb-1 font-semibold text-on-surface">Active</h4>
70
+ <p class="text-2xl font-bold text-success-soft-text">142</p>
69
71
  </Card>
70
72
  <Card hover>
71
- <h4 class="mb-1 font-semibold text-mist-900">Failed</h4>
72
- <p class="text-2xl font-bold text-danger-600">3</p>
73
+ <h4 class="mb-1 font-semibold text-on-surface">Failed</h4>
74
+ <p class="text-2xl font-bold text-danger-soft-text">3</p>
73
75
  </Card>
74
76
  </div>
75
77
  </Story>
@@ -77,13 +79,13 @@
77
79
  <Story name="Different Padding" asChild>
78
80
  <div class="max-w-md space-y-4">
79
81
  <Card padding="sm">
80
- <p class="text-sm text-mist-600">Small padding card</p>
82
+ <p class="text-sm text-on-surface-muted">Small padding card</p>
81
83
  </Card>
82
84
  <Card padding="md">
83
- <p class="text-sm text-mist-600">Medium padding card (default)</p>
85
+ <p class="text-sm text-on-surface-muted">Medium padding card (default)</p>
84
86
  </Card>
85
87
  <Card padding="lg">
86
- <p class="text-sm text-mist-600">Large padding card</p>
88
+ <p class="text-sm text-on-surface-muted">Large padding card</p>
87
89
  </Card>
88
90
  </div>
89
91
  </Story>
@@ -91,16 +93,16 @@
91
93
  <Story name="Different Shadows" asChild>
92
94
  <div class="flex gap-4">
93
95
  <Card shadow="none">
94
- <p class="text-sm text-mist-600">No shadow</p>
96
+ <p class="text-sm text-on-surface-muted">No shadow</p>
95
97
  </Card>
96
98
  <Card shadow="sm">
97
- <p class="text-sm text-mist-600">Small shadow</p>
99
+ <p class="text-sm text-on-surface-muted">Small shadow</p>
98
100
  </Card>
99
101
  <Card shadow="md">
100
- <p class="text-sm text-mist-600">Medium shadow</p>
102
+ <p class="text-sm text-on-surface-muted">Medium shadow</p>
101
103
  </Card>
102
104
  <Card shadow="lg">
103
- <p class="text-sm text-mist-600">Large shadow</p>
105
+ <p class="text-sm text-on-surface-muted">Large shadow</p>
104
106
  </Card>
105
107
  </div>
106
108
  </Story>
@@ -53,8 +53,8 @@
53
53
  </Story>
54
54
 
55
55
  <Story name="Settings Example" asChild>
56
- <div class="max-w-md space-y-4 rounded-xl border border-mist-200 bg-white p-4">
57
- <h3 class="font-semibold text-mist-900">Notification Preferences</h3>
56
+ <div class="max-w-md space-y-4 rounded-xl border border-outline bg-white p-4">
57
+ <h3 class="font-semibold text-on-surface">Notification Preferences</h3>
58
58
  <div class="space-y-3">
59
59
  <Checkbox
60
60
  label="Task completion alerts"
@@ -81,7 +81,7 @@
81
81
  {/snippet}
82
82
 
83
83
  {#snippet durationCell(/** @type {any} */ row)}
84
- <span class="font-mono text-mist-700">{row.duration}</span>
84
+ <span class="font-mono text-on-surface-muted">{row.duration}</span>
85
85
  {/snippet}
86
86
 
87
87
  <Story name="Basic" args={{ columns, data: tasks }} />
@@ -69,11 +69,11 @@
69
69
  />
70
70
 
71
71
  <Story name="In Context" asChild>
72
- <div class="max-w-md rounded-xl border border-mist-200 bg-white p-4">
72
+ <div class="max-w-md rounded-xl border border-outline bg-white p-4">
73
73
  <div class="flex items-center justify-between">
74
74
  <div>
75
- <h3 class="font-semibold text-mist-900">backup-database</h3>
76
- <p class="text-sm text-mist-500">Daily at 2:00 AM</p>
75
+ <h3 class="font-semibold text-on-surface">backup-database</h3>
76
+ <p class="text-sm text-on-surface-muted">Daily at 2:00 AM</p>
77
77
  </div>
78
78
  <Dropdown items={taskActions} />
79
79
  </div>
@@ -60,7 +60,7 @@
60
60
  </Story>
61
61
 
62
62
  <Story name="In Card Context" asChild>
63
- <div class="max-w-lg rounded-xl border border-mist-200 bg-white p-6">
63
+ <div class="max-w-lg rounded-xl border border-outline bg-white p-6">
64
64
  <EmptyState
65
65
  icon={Calendar}
66
66
  title="No upcoming executions"
@@ -0,0 +1,75 @@
1
+ <!-- SPDX-FileCopyrightText: PoppyCake, s.r.o. -->
2
+ <!-- SPDX-License-Identifier: Apache-2.0 -->
3
+
4
+ <script module>
5
+ import { defineMeta } from "@storybook/addon-svelte-csf";
6
+ import LogConsole from "$lib/components/LogConsole.svelte";
7
+ import LogConsoleDemo from "./LogConsoleDemo.svelte";
8
+
9
+ const { Story } = defineMeta({
10
+ title: "Dashboard/LogConsole",
11
+ component: LogConsole,
12
+ tags: ["autodocs"],
13
+ parameters: {
14
+ backgrounds: { default: "dark" },
15
+ },
16
+ });
17
+
18
+ const ESC = "\x1b[";
19
+
20
+ const SAMPLE_LOG = [
21
+ `${ESC}90m2026-06-05T03:00:00+02:00${ESC}0m ${ESC}1mbackup-database${ESC}0m starting`,
22
+ `${ESC}36m[info]${ESC}0m connecting to postgres://localhost:5432/app`,
23
+ `${ESC}36m[info]${ESC}0m dumping schema ${ESC}1mpublic${ESC}0m (12 tables)`,
24
+ ` ${ESC}32m✓${ESC}0m users ${ESC}90m(184,221 rows, 48 MB)${ESC}0m`,
25
+ ` ${ESC}32m✓${ESC}0m sessions ${ESC}90m(902,114 rows, 210 MB)${ESC}0m`,
26
+ ` ${ESC}32m✓${ESC}0m invoices ${ESC}90m(45,002 rows, 12 MB)${ESC}0m`,
27
+ `${ESC}33m[warn]${ESC}0m table ${ESC}1maudit_log${ESC}0m exceeds 1 GB — consider partitioning`,
28
+ ` ${ESC}32m✓${ESC}0m audit_log ${ESC}90m(7,212,330 rows, 1.2 GB)${ESC}0m`,
29
+ `${ESC}31m[error]${ESC}0m checksum mismatch on chunk 7, retrying ${ESC}90m(attempt 1/3)${ESC}0m`,
30
+ ` ${ESC}32m✓${ESC}0m retry succeeded`,
31
+ `${ESC}36m[info]${ESC}0m compressing with ${ESC}35mzstd -19${ESC}0m`,
32
+ `${ESC}36m[info]${ESC}0m uploading to ${ESC}34ms3://backups/app/2026-06-05.tar.zst${ESC}0m`,
33
+ `${ESC}32m[done]${ESC}0m backup completed in ${ESC}1m42.7s${ESC}0m ${ESC}90m(exit 0)${ESC}0m`,
34
+ ];
35
+
36
+ const PALETTE_DEMO = [
37
+ "Standard foreground (30–37):",
38
+ `${ESC}30mblack${ESC}0m ${ESC}31mred${ESC}0m ${ESC}32mgreen${ESC}0m ${ESC}33myellow${ESC}0m ${ESC}34mblue${ESC}0m ${ESC}35mmagenta${ESC}0m ${ESC}36mcyan${ESC}0m ${ESC}37mwhite${ESC}0m`,
39
+ "",
40
+ "Bright foreground (90–97):",
41
+ `${ESC}90mblack${ESC}0m ${ESC}91mred${ESC}0m ${ESC}92mgreen${ESC}0m ${ESC}93myellow${ESC}0m ${ESC}94mblue${ESC}0m ${ESC}95mmagenta${ESC}0m ${ESC}96mcyan${ESC}0m ${ESC}97mwhite${ESC}0m`,
42
+ "",
43
+ "Bold + colour:",
44
+ `${ESC}1;31mbold red${ESC}0m ${ESC}1;32mbold green${ESC}0m ${ESC}1;34mbold blue${ESC}0m ${ESC}1;36mbold cyan${ESC}0m`,
45
+ "",
46
+ "Backgrounds (41–47):",
47
+ `${ESC}41m red ${ESC}0m ${ESC}42m green ${ESC}0m ${ESC}43m yellow ${ESC}0m ${ESC}44m blue ${ESC}0m ${ESC}45m magenta ${ESC}0m ${ESC}46m cyan ${ESC}0m`,
48
+ "",
49
+ "Underline + colour:",
50
+ `${ESC}4;36munderlined cyan link${ESC}0m and ${ESC}4;33munderlined yellow${ESC}0m`,
51
+ ];
52
+
53
+ const STREAM_TICKS = [
54
+ `${ESC}36m[info]${ESC}0m processed batch ${ESC}1m#42${ESC}0m ${ESC}90m(1,024 records)${ESC}0m`,
55
+ `${ESC}32m✓${ESC}0m health check passed`,
56
+ `${ESC}33m[warn]${ESC}0m slow query detected ${ESC}90m(812ms)${ESC}0m`,
57
+ `${ESC}36m[info]${ESC}0m flushing write buffer`,
58
+ ];
59
+ </script>
60
+
61
+ <Story name="Sample Run Log" asChild>
62
+ <LogConsoleDemo lines={SAMPLE_LOG} />
63
+ </Story>
64
+
65
+ <Story name="ANSI Palette" asChild>
66
+ <LogConsoleDemo lines={PALETTE_DEMO} />
67
+ </Story>
68
+
69
+ <Story name="Streaming" asChild>
70
+ <LogConsoleDemo lines={SAMPLE_LOG.slice(0, 5)} finished={false} streamLines={STREAM_TICKS} />
71
+ </Story>
72
+
73
+ <Story name="Truncated" asChild>
74
+ <LogConsoleDemo lines={SAMPLE_LOG.slice(6)} firstAvailableLine={1500} />
75
+ </Story>
@@ -0,0 +1,60 @@
1
+ <!-- SPDX-FileCopyrightText: PoppyCake, s.r.o. -->
2
+ <!-- SPDX-License-Identifier: Apache-2.0 -->
3
+
4
+ <!-- Storybook-only harness: LogConsole is seeded by its parent calling
5
+ onStream(), so this wrapper feeds it a canned LogEvent on mount and,
6
+ optionally, keeps appending lines to demo live streaming. -->
7
+ <script lang="ts">
8
+ import { onMount } from "svelte";
9
+ import LogConsole from "$lib/components/LogConsole.svelte";
10
+
11
+ interface Props {
12
+ lines: string[];
13
+ finished?: boolean;
14
+ firstAvailableLine?: number;
15
+ streamLines?: string[];
16
+ height?: string;
17
+ }
18
+
19
+ let {
20
+ lines,
21
+ finished = true,
22
+ firstAvailableLine = 0,
23
+ streamLines = [],
24
+ height = "24rem",
25
+ }: Props = $props();
26
+
27
+ let logConsole = $state<LogConsole | null>(null);
28
+
29
+ function toEvent(slice: Record<number, string>, total: number): void {
30
+ logConsole?.onStream({
31
+ lines: slice,
32
+ sizeLines: total,
33
+ sizeBytes: Object.values(slice).reduce((n, l) => n + l.length + 1, 0),
34
+ finished,
35
+ firstAvailableLine,
36
+ });
37
+ }
38
+
39
+ onMount(() => {
40
+ const slice: Record<number, string> = {};
41
+ lines.forEach((text, i) => {
42
+ slice[firstAvailableLine + i] = text;
43
+ });
44
+ toEvent(slice, firstAvailableLine + lines.length);
45
+
46
+ if (finished || streamLines.length === 0) return;
47
+
48
+ let next = firstAvailableLine + lines.length;
49
+ const timer = setInterval(() => {
50
+ const text = streamLines[(next - lines.length) % streamLines.length] ?? "";
51
+ toEvent({ [next]: text }, next + 1);
52
+ next += 1;
53
+ }, 700);
54
+ return () => clearInterval(timer);
55
+ });
56
+ </script>
57
+
58
+ <div class="overflow-hidden rounded-xl border border-outline" style="height: {height};">
59
+ <LogConsole bind:this={logConsole} />
60
+ </div>
@@ -21,26 +21,26 @@
21
21
  <Story name="Default" args={{}} />
22
22
 
23
23
  <Story name="Sizes" asChild>
24
- <div class="flex items-center gap-8 rounded-lg bg-mist-50 p-8">
24
+ <div class="flex items-center gap-8 rounded-lg bg-surface-sunken p-8">
25
25
  <div class="flex flex-col items-center gap-2">
26
26
  <Logo size="xs" />
27
- <span class="text-xs text-mist-600">XS</span>
27
+ <span class="text-xs text-on-surface-muted">XS</span>
28
28
  </div>
29
29
  <div class="flex flex-col items-center gap-2">
30
30
  <Logo size="sm" />
31
- <span class="text-xs text-mist-600">SM</span>
31
+ <span class="text-xs text-on-surface-muted">SM</span>
32
32
  </div>
33
33
  <div class="flex flex-col items-center gap-2">
34
34
  <Logo size="md" />
35
- <span class="text-xs text-mist-600">MD</span>
35
+ <span class="text-xs text-on-surface-muted">MD</span>
36
36
  </div>
37
37
  <div class="flex flex-col items-center gap-2">
38
38
  <Logo size="lg" />
39
- <span class="text-xs text-mist-600">LG</span>
39
+ <span class="text-xs text-on-surface-muted">LG</span>
40
40
  </div>
41
41
  <div class="flex flex-col items-center gap-2">
42
42
  <Logo size="xl" />
43
- <span class="text-xs text-mist-600">XL</span>
43
+ <span class="text-xs text-on-surface-muted">XL</span>
44
44
  </div>
45
45
  </div>
46
46
  </Story>
@@ -62,9 +62,9 @@
62
62
  </Story>
63
63
 
64
64
  <Story name="With Custom Class" asChild>
65
- <div class="flex items-center gap-4 rounded-lg bg-mist-50 p-8">
66
- <Logo size="lg" class="text-wisp-600" />
67
- <span class="text-lg font-semibold text-mist-900">Custom Color</span>
65
+ <div class="flex items-center gap-4 rounded-lg bg-surface-sunken p-8">
66
+ <Logo size="lg" class="text-primary" />
67
+ <span class="text-lg font-semibold text-on-surface">Custom Color</span>
68
68
  </div>
69
69
  </Story>
70
70
 
@@ -38,7 +38,9 @@
38
38
  title="Modal Title"
39
39
  description="This is a description of the modal."
40
40
  >
41
- <p class="text-mist-600">This is the modal content. You can put any content here.</p>
41
+ <p class="text-on-surface-muted">
42
+ This is the modal content. You can put any content here.
43
+ </p>
42
44
  </Modal>
43
45
  </Story>
44
46
 
@@ -62,7 +64,7 @@
62
64
  <Story name="Confirmation Dialog" asChild>
63
65
  <Button variant="danger" onclick={() => (confirmOpen = true)}>Delete Task</Button>
64
66
  <Modal bind:open={confirmOpen} title="Delete Task?" size="sm">
65
- <p class="text-mist-600">
67
+ <p class="text-on-surface-muted">
66
68
  Are you sure you want to delete <strong>backup-database</strong>? This action cannot be
67
69
  undone.
68
70
  </p>
@@ -81,15 +83,15 @@
81
83
  <div
82
84
  class="max-h-96 overflow-auto rounded-lg bg-mist-900 p-4 font-mono text-sm text-mist-100"
83
85
  >
84
- <p class="text-aurora-400">Starting task execution...</p>
85
- <p class="text-mist-300">Connecting to database...</p>
86
- <p class="text-mist-300">Connection established</p>
87
- <p class="text-mist-300">Running backup query...</p>
88
- <p class="text-mist-300">Processing 1,234 records...</p>
89
- <p class="text-mist-300">Compressing backup file...</p>
90
- <p class="text-mist-300">Uploading to storage...</p>
86
+ <p class="text-info">Starting task execution...</p>
87
+ <p class="text-on-surface-faint">Connecting to database...</p>
88
+ <p class="text-on-surface-faint">Connection established</p>
89
+ <p class="text-on-surface-faint">Running backup query...</p>
90
+ <p class="text-on-surface-faint">Processing 1,234 records...</p>
91
+ <p class="text-on-surface-faint">Compressing backup file...</p>
92
+ <p class="text-on-surface-faint">Uploading to storage...</p>
91
93
  <p class="text-success-400">Task completed successfully</p>
92
- <p class="text-mist-500">Total execution time: 90s</p>
94
+ <p class="text-on-surface-muted">Total execution time: 90s</p>
93
95
  </div>
94
96
  {#snippet footer()}
95
97
  <div class="flex justify-between">
@@ -44,8 +44,8 @@
44
44
  </Story>
45
45
 
46
46
  <Story name="Usage Example" asChild>
47
- <div class="max-w-md space-y-4 rounded-xl border border-mist-200 bg-white p-4">
48
- <h3 class="font-semibold text-mist-900">Resource Usage</h3>
47
+ <div class="max-w-md space-y-4 rounded-xl border border-outline bg-white p-4">
48
+ <h3 class="font-semibold text-on-surface">Resource Usage</h3>
49
49
  <Progress value={45} label="CPU Usage" showLabel />
50
50
  <Progress value={72} label="Memory" showLabel variant="warning" />
51
51
  <Progress value={23} label="Storage" showLabel variant="success" />
@@ -54,12 +54,12 @@
54
54
  </Story>
55
55
 
56
56
  <Story name="Task Execution Progress" asChild>
57
- <div class="max-w-md space-y-4 rounded-xl border border-mist-200 bg-white p-4">
57
+ <div class="max-w-md space-y-4 rounded-xl border border-outline bg-white p-4">
58
58
  <div class="flex items-center justify-between">
59
- <h3 class="font-semibold text-mist-900">backup-database</h3>
60
- <span class="text-sm text-mist-500">Running...</span>
59
+ <h3 class="font-semibold text-on-surface">backup-database</h3>
60
+ <span class="text-sm text-on-surface-muted">Running...</span>
61
61
  </div>
62
62
  <Progress value={67} showLabel />
63
- <p class="text-sm text-mist-500">Processing 847 of 1,234 records</p>
63
+ <p class="text-sm text-on-surface-muted">Processing 847 of 1,234 records</p>
64
64
  </div>
65
65
  </Story>