@runwisp/ui 1.5.0 → 1.7.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 (70) hide show
  1. package/package.json +5 -7
  2. package/src/lib/actions/focusTrap.ts +3 -3
  3. package/src/lib/actions/fold-to-fit.test.ts +36 -0
  4. package/src/lib/actions/fold-to-fit.ts +89 -0
  5. package/src/lib/actions/modal-dialog.test.ts +47 -0
  6. package/src/lib/actions/modal-dialog.ts +48 -0
  7. package/src/lib/components/AccordionItem.svelte +16 -5
  8. package/src/lib/components/Badge.svelte +4 -25
  9. package/src/lib/components/Button.svelte +2 -9
  10. package/src/lib/components/CodeBlock.svelte +7 -6
  11. package/src/lib/components/DataGrid.svelte +5 -5
  12. package/src/lib/components/Drawer.svelte +22 -30
  13. package/src/lib/components/DurationInput.svelte +6 -25
  14. package/src/lib/components/FilterBar.svelte +3 -7
  15. package/src/lib/components/FilterPill.svelte +2 -2
  16. package/src/lib/components/FormField.svelte +1 -1
  17. package/src/lib/components/Link.svelte +9 -4
  18. package/src/lib/components/LogConsole.svelte +101 -424
  19. package/src/lib/components/Modal.svelte +7 -30
  20. package/src/lib/components/SearchInput.svelte +1 -1
  21. package/src/lib/components/Select.svelte +3 -2
  22. package/src/lib/components/Sparkline.svelte +2 -2
  23. package/src/lib/components/Spinner.svelte +10 -5
  24. package/src/lib/components/StatusDot.svelte +1 -1
  25. package/src/lib/components/StatusIndicator.svelte +37 -104
  26. package/src/lib/components/Tabs.svelte +15 -1
  27. package/src/lib/components/Tooltip.svelte +1 -1
  28. package/src/lib/components/dashboard/RunActions.svelte +156 -0
  29. package/src/lib/components/dashboard/RunConsoleFrame.svelte +319 -0
  30. package/src/lib/components/dashboard/RunDetailPanel.svelte +137 -725
  31. package/src/lib/components/dashboard/RunFacts.svelte +119 -0
  32. package/src/lib/components/dashboard/RunFilterChips.svelte +52 -0
  33. package/src/lib/components/dashboard/RunFilterPopover.svelte +9 -10
  34. package/src/lib/components/dashboard/RunHeaderLine.svelte +295 -0
  35. package/src/lib/components/dashboard/RunListSkeleton.svelte +43 -0
  36. package/src/lib/components/dashboard/RunRow.svelte +202 -0
  37. package/src/lib/components/dashboard/RunsList.svelte +267 -545
  38. package/src/lib/components/dashboard/bulk-selection.svelte.test.ts +42 -0
  39. package/src/lib/components/dashboard/bulk-selection.svelte.ts +50 -0
  40. package/src/lib/components/dashboard/cron-format.test.ts +10 -1
  41. package/src/lib/components/dashboard/cron-format.ts +7 -3
  42. package/src/lib/components/dashboard/run-filters.test.ts +62 -2
  43. package/src/lib/components/dashboard/run-filters.ts +104 -99
  44. package/src/lib/components/dashboard/run-helpers.test.ts +131 -12
  45. package/src/lib/components/dashboard/run-helpers.ts +150 -44
  46. package/src/lib/components/dashboard/status-config.ts +18 -0
  47. package/src/lib/components/dashboard/types.ts +6 -0
  48. package/src/lib/components/data-grid-selection.ts +2 -9
  49. package/src/lib/components/filter-spec.ts +3 -3
  50. package/src/lib/index.ts +13 -2
  51. package/src/lib/log-console/AnsiLine.svelte +12 -0
  52. package/src/lib/log-console/FrameHistoryBlock.svelte +57 -0
  53. package/src/lib/log-console/LogCache.svelte.ts +1 -1
  54. package/src/lib/log-console/LogConsoleFooter.svelte +88 -0
  55. package/src/lib/log-console/ansi.ts +1 -1
  56. package/src/lib/log-console/frame-history.svelte.test.ts +47 -0
  57. package/src/lib/log-console/frame-history.svelte.ts +71 -0
  58. package/src/lib/log-console/wrap-layout.svelte.test.ts +92 -0
  59. package/src/lib/log-console/wrap-layout.svelte.ts +188 -0
  60. package/src/lib/theme-tokens.css +21 -21
  61. package/src/lib/theme.css +13 -13
  62. package/src/lib/utils/format.test.ts +125 -3
  63. package/src/lib/utils/format.ts +101 -8
  64. package/src/lib/utils/toast.svelte.ts +2 -3
  65. package/src/lib/utils/typing-target.ts +10 -0
  66. package/src/stories/Accordion.stories.svelte +60 -1
  67. package/src/stories/CodeBlock.stories.svelte +13 -0
  68. package/src/stories/Link.stories.svelte +25 -1
  69. package/src/stories/LogConsole.stories.svelte +2 -2
  70. package/src/stories/Tabs.stories.svelte +17 -0
@@ -3,11 +3,32 @@
3
3
 
4
4
  <script module>
5
5
  import { defineMeta } from "@storybook/addon-svelte-csf";
6
- import { LifeBuoy, Trash } from "@lucide/svelte";
6
+ import { LifeBuoy, Pencil, Trash } from "@lucide/svelte";
7
7
  import Accordion from "$lib/components/Accordion.svelte";
8
8
  import AccordionItem from "$lib/components/AccordionItem.svelte";
9
9
  import Toggle from "$lib/components/Toggle.svelte";
10
10
 
11
+ const blocks = [
12
+ {
13
+ label: "Node.js 22",
14
+ enabled: true,
15
+ frameClass: "border-outline bg-surface-raised",
16
+ run: "RUN apk add --no-cache nodejs npm",
17
+ },
18
+ {
19
+ label: "Python 3.12",
20
+ enabled: false,
21
+ frameClass: "border-outline-faint bg-surface",
22
+ run: "RUN apk add --no-cache python3",
23
+ },
24
+ {
25
+ label: "Go 1.27 (stale)",
26
+ enabled: true,
27
+ frameClass: "border-warning-soft-border bg-warning-soft/30",
28
+ run: "RUN apk add --no-cache go",
29
+ },
30
+ ];
31
+
11
32
  const { Story } = defineMeta({
12
33
  title: "Navigation/Accordion",
13
34
  component: Accordion,
@@ -59,3 +80,41 @@
59
80
  <pre class="font-mono text-xs">RUN apk add --no-cache nodejs npm</pre>
60
81
  </AccordionItem>
61
82
  </Story>
83
+
84
+ <!-- `leading` sits before the title and never toggles the panel; `frameClass`
85
+ tints the frame by state. -->
86
+ <Story name="Framed with leading control and tint" asChild>
87
+ <div class="flex max-w-lg flex-col gap-2">
88
+ {#each blocks as block (block.label)}
89
+ <AccordionItem
90
+ variant="framed"
91
+ frameClass={block.frameClass}
92
+ class={block.enabled ? "" : "opacity-60"}
93
+ >
94
+ {#snippet leading()}
95
+ <Toggle checked={block.enabled} size="sm" />
96
+ {/snippet}
97
+ {#snippet title()}
98
+ <span class="block truncate">{block.label}</span>
99
+ {/snippet}
100
+ {#snippet actions()}
101
+ <button
102
+ type="button"
103
+ aria-label="Edit"
104
+ class="rounded-[3px] p-1 text-on-surface-faint hover:bg-surface-sunken hover:text-on-surface"
105
+ >
106
+ <Pencil size={14} />
107
+ </button>
108
+ <button
109
+ type="button"
110
+ aria-label="Remove"
111
+ class="rounded-[3px] p-1 text-on-surface-faint hover:bg-danger-soft hover:text-danger-surface"
112
+ >
113
+ <Trash size={14} />
114
+ </button>
115
+ {/snippet}
116
+ <pre class="font-mono text-xs">{block.run}</pre>
117
+ </AccordionItem>
118
+ {/each}
119
+ </div>
120
+ </Story>
@@ -7,6 +7,8 @@
7
7
 
8
8
  const helloTask = '[tasks.hello]\ncron = "*/5 * * * *"\nrun = "echo hello"';
9
9
  const backupTask = '[tasks.backup]\ncron = "0 3 * * *"\nkeep_runs = 60';
10
+ const longLine =
11
+ "export RUNWISP_STATION_TOKEN=rw_live_8f2c1d9e4b7a6053c1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4";
10
12
 
11
13
  const { Story } = defineMeta({
12
14
  title: "Data Display/CodeBlock",
@@ -40,6 +42,17 @@
40
42
  <CodeBlock variant="surface" size="sm" copyable={false} code={helloTask} />
41
43
  </Story>
42
44
 
45
+ <!-- A line wider than the block scrolls without running under the copy button. -->
46
+ <Story name="Long line" asChild>
47
+ <div class="flex max-w-md flex-col gap-3">
48
+ <CodeBlock code={longLine} />
49
+ <CodeBlock code={longLine} size="sm" prompt="$" />
50
+ <CodeBlock code={longLine} variant="surface" />
51
+ <CodeBlock code={longLine} variant="surface" size="sm" />
52
+ <CodeBlock code="runwisp station" size="sm" />
53
+ </div>
54
+ </Story>
55
+
43
56
  <!-- Rich children render the markup; `code` keeps the copied text plain. -->
44
57
  <Story name="Highlighted children" asChild>
45
58
  <!-- No whitespace between the line spans: inside <pre> it would render as blank lines. -->
@@ -10,7 +10,10 @@
10
10
  component: Link,
11
11
  tags: ["autodocs"],
12
12
  argTypes: {
13
- variant: { control: "select", options: ["default", "muted", "primary", "danger"] },
13
+ variant: {
14
+ control: "select",
15
+ options: ["default", "muted", "primary", "danger", "unstyled"],
16
+ },
14
17
  underline: { control: "select", options: ["always", "hover", "none"] },
15
18
  },
16
19
  });
@@ -36,3 +39,24 @@
36
39
  Task configuration docs →
37
40
  </Link>
38
41
  </Story>
42
+
43
+ <!-- `unstyled` adds nothing beyond `class`: the row brings its own layout and colours. -->
44
+ <Story name="Unstyled nav row" asChild>
45
+ <nav class="w-64 space-y-0.5">
46
+ <Link
47
+ href="#tasks"
48
+ variant="unstyled"
49
+ aria-current="page"
50
+ class="flex items-center gap-3 rounded-sm bg-primary-soft px-3 py-2 font-mono text-sm text-primary-soft-text"
51
+ >
52
+ Tasks
53
+ </Link>
54
+ <Link
55
+ href="#runners"
56
+ variant="unstyled"
57
+ class="flex items-center gap-3 rounded-sm px-3 py-2 font-mono text-sm text-on-surface-muted hover:bg-surface-sunken hover:text-primary"
58
+ >
59
+ Runners
60
+ </Link>
61
+ </nav>
62
+ </Story>
@@ -24,7 +24,7 @@
24
24
  ` ${ESC}32m✓${ESC}0m users ${ESC}90m(184,221 rows, 48 MB)${ESC}0m`,
25
25
  ` ${ESC}32m✓${ESC}0m sessions ${ESC}90m(902,114 rows, 210 MB)${ESC}0m`,
26
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`,
27
+ `${ESC}33m[warn]${ESC}0m table ${ESC}1maudit_log${ESC}0m exceeds 1 GB, consider partitioning`,
28
28
  ` ${ESC}32m✓${ESC}0m audit_log ${ESC}90m(7,212,330 rows, 1.2 GB)${ESC}0m`,
29
29
  `${ESC}31m[error]${ESC}0m checksum mismatch on chunk 7, retrying ${ESC}90m(attempt 1/3)${ESC}0m`,
30
30
  ` ${ESC}32m✓${ESC}0m retry succeeded`,
@@ -53,7 +53,7 @@
53
53
  const LONG_LINES = [
54
54
  `${ESC}36m[info]${ESC}0m starting request trace`,
55
55
  `${ESC}90m2026-06-05T03:00:00Z${ESC}0m GET /api/v1/tasks?include=runs,logs,schedule&limit=100&offset=0 → ${ESC}32m200${ESC}0m ${ESC}90m{"trace":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","upstream":"https://internal.example.com/very/long/path/segment/that/keeps/going/and/going","latency_ms":1287,"bytes":204812}${ESC}0m`,
56
- `${ESC}31m[error]${ESC}0m panic: runtime error: index out of range [12] with length 8 — goroutine 42 [running]: main.process(0xc0000b4000, 0x1f4) /home/runner/work/app/app/internal/pipeline/process.go:317 +0x4a5`,
56
+ `${ESC}31m[error]${ESC}0m panic: runtime error: index out of range [12] with length 8, goroutine 42 [running]: main.process(0xc0000b4000, 0x1f4) /home/runner/work/app/app/internal/pipeline/process.go:317 +0x4a5`,
57
57
  " short line in between",
58
58
  `tab-separated\tcolumns\there\tshould\tstay\taligned\tand\textend\tfar\tpast\tthe\tviewport\tedge\tto\tprove\thorizontal\tscroll`,
59
59
  `${ESC}32m[done]${ESC}0m ${"=".repeat(400)}|END`,
@@ -32,6 +32,14 @@
32
32
  { id: "settings", label: "Settings", icon: Settings },
33
33
  ];
34
34
 
35
+ const deployTabs = [
36
+ { id: "binary", label: "Linux binary" },
37
+ { id: "docker", label: "Docker run" },
38
+ { id: "compose", label: "Docker Compose" },
39
+ { id: "kubernetes", label: "Kubernetes manifest" },
40
+ { id: "systemd", label: "systemd unit" },
41
+ ];
42
+
35
43
  const disabledTabs = [
36
44
  { id: "active", label: "Active" },
37
45
  { id: "completed", label: "Completed" },
@@ -105,3 +113,12 @@
105
113
  {/snippet}
106
114
  </Tabs>
107
115
  </Story>
116
+
117
+ <!-- `scrollable` keeps every label on one line and scrolls the bar sideways. -->
118
+ <Story name="Scrollable" asChild>
119
+ <div class="flex max-w-xs flex-col gap-4">
120
+ <Tabs tabs={deployTabs} variant="enclosed" scrollable />
121
+ <Tabs tabs={deployTabs} variant="pills" scrollable />
122
+ <Tabs tabs={deployTabs} variant="underline" scrollable />
123
+ </div>
124
+ </Story>