@runwisp/ui 1.5.1 → 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.
- package/package.json +5 -7
- package/src/lib/actions/fold-to-fit.test.ts +36 -0
- package/src/lib/actions/fold-to-fit.ts +89 -0
- package/src/lib/actions/modal-dialog.test.ts +47 -0
- package/src/lib/actions/modal-dialog.ts +48 -0
- package/src/lib/components/AccordionItem.svelte +16 -5
- package/src/lib/components/Badge.svelte +3 -24
- package/src/lib/components/Button.svelte +2 -9
- package/src/lib/components/CodeBlock.svelte +7 -6
- package/src/lib/components/Drawer.svelte +22 -30
- package/src/lib/components/DurationInput.svelte +6 -25
- package/src/lib/components/FilterBar.svelte +2 -6
- package/src/lib/components/Link.svelte +9 -4
- package/src/lib/components/LogConsole.svelte +95 -418
- package/src/lib/components/Modal.svelte +7 -30
- package/src/lib/components/Spinner.svelte +10 -5
- package/src/lib/components/StatusIndicator.svelte +37 -104
- package/src/lib/components/Tabs.svelte +15 -1
- package/src/lib/components/dashboard/RunActions.svelte +156 -0
- package/src/lib/components/dashboard/RunConsoleFrame.svelte +319 -0
- package/src/lib/components/dashboard/RunDetailPanel.svelte +130 -717
- package/src/lib/components/dashboard/RunFacts.svelte +119 -0
- package/src/lib/components/dashboard/RunFilterChips.svelte +52 -0
- package/src/lib/components/dashboard/RunFilterPopover.svelte +4 -5
- package/src/lib/components/dashboard/RunHeaderLine.svelte +295 -0
- package/src/lib/components/dashboard/RunListSkeleton.svelte +43 -0
- package/src/lib/components/dashboard/RunRow.svelte +202 -0
- package/src/lib/components/dashboard/RunsList.svelte +265 -543
- package/src/lib/components/dashboard/bulk-selection.svelte.test.ts +42 -0
- package/src/lib/components/dashboard/bulk-selection.svelte.ts +50 -0
- package/src/lib/components/dashboard/cron-format.test.ts +9 -0
- package/src/lib/components/dashboard/cron-format.ts +5 -1
- package/src/lib/components/dashboard/run-filters.test.ts +60 -0
- package/src/lib/components/dashboard/run-filters.ts +78 -6
- package/src/lib/components/dashboard/run-helpers.test.ts +122 -12
- package/src/lib/components/dashboard/run-helpers.ts +122 -6
- package/src/lib/components/dashboard/status-config.ts +18 -0
- package/src/lib/components/dashboard/types.ts +6 -0
- package/src/lib/index.ts +13 -2
- package/src/lib/log-console/AnsiLine.svelte +12 -0
- package/src/lib/log-console/FrameHistoryBlock.svelte +57 -0
- package/src/lib/log-console/LogConsoleFooter.svelte +88 -0
- package/src/lib/log-console/frame-history.svelte.test.ts +47 -0
- package/src/lib/log-console/frame-history.svelte.ts +71 -0
- package/src/lib/log-console/wrap-layout.svelte.test.ts +92 -0
- package/src/lib/log-console/wrap-layout.svelte.ts +188 -0
- package/src/lib/utils/format.test.ts +122 -0
- package/src/lib/utils/format.ts +97 -4
- package/src/lib/utils/typing-target.ts +10 -0
- package/src/stories/Accordion.stories.svelte +60 -1
- package/src/stories/CodeBlock.stories.svelte +13 -0
- package/src/stories/Link.stories.svelte +25 -1
- package/src/stories/Tabs.stories.svelte +17 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
<!-- SPDX-FileCopyrightText: PoppyCake, s.r.o. -->
|
|
2
|
+
<!-- SPDX-License-Identifier: Apache-2.0 -->
|
|
3
|
+
|
|
4
|
+
<script lang="ts">
|
|
5
|
+
import { displayStatus, type Run } from "@runwisp/common";
|
|
6
|
+
import { RUN_STATUS_CONFIG } from "./status-config.js";
|
|
7
|
+
import {
|
|
8
|
+
formatTriggeredByLabel,
|
|
9
|
+
runRetryLabel,
|
|
10
|
+
runRowReadout,
|
|
11
|
+
runUsageAmounts,
|
|
12
|
+
runUsageLabel,
|
|
13
|
+
usageLevel,
|
|
14
|
+
highlightParts,
|
|
15
|
+
type RunUsageAmounts,
|
|
16
|
+
type RunUsageScale,
|
|
17
|
+
} from "./run-helpers.js";
|
|
18
|
+
import type { RunOutputMatch } from "./types.js";
|
|
19
|
+
import {
|
|
20
|
+
formatDateTime,
|
|
21
|
+
formatFullDateTime,
|
|
22
|
+
formatTimeHM,
|
|
23
|
+
formatDayMonth,
|
|
24
|
+
formatBytes,
|
|
25
|
+
formatDuration,
|
|
26
|
+
} from "../../utils/format.js";
|
|
27
|
+
|
|
28
|
+
let {
|
|
29
|
+
run,
|
|
30
|
+
active,
|
|
31
|
+
suffix,
|
|
32
|
+
showTaskName,
|
|
33
|
+
match,
|
|
34
|
+
outputQuery,
|
|
35
|
+
bulkActions,
|
|
36
|
+
selectionActive,
|
|
37
|
+
usage,
|
|
38
|
+
onselect,
|
|
39
|
+
}: {
|
|
40
|
+
run: Run;
|
|
41
|
+
active: boolean;
|
|
42
|
+
/** Instance suffix of a multi-instance service ("" otherwise). */
|
|
43
|
+
suffix: string;
|
|
44
|
+
showTaskName: boolean;
|
|
45
|
+
/** First output line that matched the active search, if any. */
|
|
46
|
+
match: RunOutputMatch | undefined;
|
|
47
|
+
outputQuery: string;
|
|
48
|
+
bulkActions: boolean;
|
|
49
|
+
selectionActive: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* The task's usage scale when the rail shows CPU/RAM bars, null while
|
|
52
|
+
* no run has usage yet (rows keep the bars' room), undefined for none.
|
|
53
|
+
*/
|
|
54
|
+
usage: RunUsageScale | null | undefined;
|
|
55
|
+
onselect: (runId: string) => void;
|
|
56
|
+
} = $props();
|
|
57
|
+
|
|
58
|
+
const dstatus = $derived(displayStatus(run.status, run.endReason));
|
|
59
|
+
const config = $derived(RUN_STATUS_CONFIG[dstatus]);
|
|
60
|
+
const running = $derived(run.status === "running");
|
|
61
|
+
const startedAt = $derived(run.startedAt ?? run.createdAt);
|
|
62
|
+
const retry = $derived(runRetryLabel(run));
|
|
63
|
+
const amounts = $derived(usage ? runUsageAmounts(run) : undefined);
|
|
64
|
+
const tight = $derived(usage === undefined ? "" : "@max-[19rem]:hidden");
|
|
65
|
+
const LEVEL_COLOR = ["bg-success-surface", "bg-warning-surface", "bg-danger-surface"];
|
|
66
|
+
// With bulk actions on, the status dot fades out on row hover, or whenever a
|
|
67
|
+
// selection exists, so the row checkbox can take its place over it.
|
|
68
|
+
const dotFade = $derived(
|
|
69
|
+
bulkActions ? (selectionActive ? "opacity-0" : "group-hover/row:opacity-0") : "",
|
|
70
|
+
);
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<button
|
|
74
|
+
class="btn-scale group @container relative w-full rounded-[3px] border text-left select-none {showTaskName
|
|
75
|
+
? 'p-3'
|
|
76
|
+
: 'px-3 py-[11px]'} {active
|
|
77
|
+
? 'border-outline bg-surface-raised shadow-sm'
|
|
78
|
+
: 'border-transparent hover:border-outline-hover hover:bg-surface-sunken'}"
|
|
79
|
+
onclick={() => onselect(run.id)}
|
|
80
|
+
>
|
|
81
|
+
{#if showTaskName}
|
|
82
|
+
<!-- Cross-task /runs variant: the same readout language as the task
|
|
83
|
+
rail, status dot, status-colored outcome, mono right readout,
|
|
84
|
+
with the task name carried as the primary. -->
|
|
85
|
+
<div class="flex items-center gap-2.5">
|
|
86
|
+
{@render dot()}
|
|
87
|
+
<span class="flex min-w-0 flex-1 flex-col gap-0.5">
|
|
88
|
+
<span class="flex items-center gap-1.5">
|
|
89
|
+
<span class="truncate font-mono text-[13px] font-semibold text-on-surface">
|
|
90
|
+
{run.taskName}{#if suffix}<span class="text-on-surface-muted">{suffix}</span
|
|
91
|
+
>{/if}
|
|
92
|
+
</span>
|
|
93
|
+
<span class="shrink-0 text-on-surface-faint">·</span>
|
|
94
|
+
<span
|
|
95
|
+
class="shrink-0 font-mono text-[12px] font-semibold capitalize {config.color}"
|
|
96
|
+
>{dstatus}</span
|
|
97
|
+
>
|
|
98
|
+
</span>
|
|
99
|
+
<span
|
|
100
|
+
class="flex min-w-0 items-center gap-1.5 font-mono text-2xs text-on-surface-faint"
|
|
101
|
+
title={formatFullDateTime(startedAt)}
|
|
102
|
+
>
|
|
103
|
+
<span class="truncate">{formatDateTime(startedAt)}</span>
|
|
104
|
+
<span class="shrink-0">· {formatTriggeredByLabel(run.triggeredBy)}</span>
|
|
105
|
+
{#if retry}
|
|
106
|
+
<span class="shrink-0 rounded bg-surface-sunken px-1 font-mono"
|
|
107
|
+
>{retry}</span
|
|
108
|
+
>
|
|
109
|
+
{/if}
|
|
110
|
+
</span>
|
|
111
|
+
</span>
|
|
112
|
+
<span
|
|
113
|
+
class="shrink-0 self-start pt-0.5 font-mono text-[11.5px] text-on-surface-faint tabular-nums"
|
|
114
|
+
title={retry ?? undefined}
|
|
115
|
+
>
|
|
116
|
+
{runRowReadout(run, dstatus)}
|
|
117
|
+
</span>
|
|
118
|
+
</div>
|
|
119
|
+
{:else}
|
|
120
|
+
<!-- Task-rail variant (artifact ".run"): a single dense line,
|
|
121
|
+
time · date · outcome, with a mono exit/duration readout.
|
|
122
|
+
leading-tight matches the artifact's ~1.2 line-height so the
|
|
123
|
+
(descender-less) text optically centers instead of riding high
|
|
124
|
+
inside Tailwind's default 1.5 line box. -->
|
|
125
|
+
<div class="flex items-center gap-[11px] leading-tight">
|
|
126
|
+
{@render dot()}
|
|
127
|
+
<span class="flex min-w-0 flex-1 items-center gap-1.5 truncate">
|
|
128
|
+
<span
|
|
129
|
+
class="font-mono text-[12.5px] font-semibold tracking-tight text-on-surface tabular-nums"
|
|
130
|
+
title={formatFullDateTime(startedAt)}
|
|
131
|
+
>
|
|
132
|
+
{formatTimeHM(startedAt)} · {formatDayMonth(startedAt)}
|
|
133
|
+
</span>
|
|
134
|
+
<!-- With usage bars a tight rail drops the status word; the dot
|
|
135
|
+
keeps its colour. -->
|
|
136
|
+
<span class="text-on-surface-faint {tight}">·</span>
|
|
137
|
+
<span
|
|
138
|
+
class="font-mono text-[12.5px] font-semibold capitalize {config.color} {tight}"
|
|
139
|
+
>{dstatus}</span
|
|
140
|
+
>
|
|
141
|
+
{#if suffix}
|
|
142
|
+
<span class="font-mono text-2xs text-on-surface-faint">{suffix}</span>
|
|
143
|
+
{/if}
|
|
144
|
+
</span>
|
|
145
|
+
{#if usage && amounts}
|
|
146
|
+
<span
|
|
147
|
+
class="flex w-6 shrink-0 cursor-help flex-col gap-0.5"
|
|
148
|
+
title="{runUsageLabel(run)}. Usual for this task: CPU {formatDuration(
|
|
149
|
+
usage.usual.cpu,
|
|
150
|
+
)} · RAM {formatBytes(usage.usual.ram)}"
|
|
151
|
+
data-testid="run-usage-bars"
|
|
152
|
+
>
|
|
153
|
+
{@render bar(amounts.cpu, usage.max.cpu, usage.usual.cpu, "cpu")}
|
|
154
|
+
{@render bar(amounts.ram, usage.max.ram, usage.usual.ram, "ram")}
|
|
155
|
+
</span>
|
|
156
|
+
{:else if usage !== undefined}
|
|
157
|
+
<span class="w-6 shrink-0" aria-hidden="true"></span>
|
|
158
|
+
{/if}
|
|
159
|
+
<span
|
|
160
|
+
class="shrink-0 font-mono text-[11.5px] text-on-surface-faint tabular-nums"
|
|
161
|
+
title={retry ?? undefined}
|
|
162
|
+
>
|
|
163
|
+
{runRowReadout(run, dstatus)}
|
|
164
|
+
</span>
|
|
165
|
+
</div>
|
|
166
|
+
{#if match}
|
|
167
|
+
{@const hl = highlightParts(match.text, outputQuery)}
|
|
168
|
+
<div
|
|
169
|
+
class="mt-1.5 truncate rounded-[3px] border border-outline-faint bg-surface-sunken px-2 py-1 font-mono text-2xs text-on-surface-muted"
|
|
170
|
+
>
|
|
171
|
+
{hl.before}<mark class="rounded-[3px] bg-primary-soft px-0.5 text-primary-soft-text"
|
|
172
|
+
>{hl.match}</mark
|
|
173
|
+
>{hl.after}
|
|
174
|
+
</div>
|
|
175
|
+
{/if}
|
|
176
|
+
{/if}
|
|
177
|
+
|
|
178
|
+
<div
|
|
179
|
+
class="absolute inset-y-2 left-[-6px] w-[3px] rounded-[3px] {config.solidDot} {active
|
|
180
|
+
? 'opacity-100'
|
|
181
|
+
: 'opacity-0'}"
|
|
182
|
+
aria-hidden="true"
|
|
183
|
+
></div>
|
|
184
|
+
</button>
|
|
185
|
+
|
|
186
|
+
{#snippet bar(value: number, max: number, usual: number, kind: keyof RunUsageAmounts)}
|
|
187
|
+
<span class="block h-[3px] overflow-hidden rounded-[1px] bg-outline-faint">
|
|
188
|
+
<span
|
|
189
|
+
class="block h-full {LEVEL_COLOR[usageLevel(value, usual, kind)]}"
|
|
190
|
+
style="width: {String(max > 0 ? Math.max(4, Math.round((100 * value) / max)) : 4)}%"
|
|
191
|
+
></span>
|
|
192
|
+
</span>
|
|
193
|
+
{/snippet}
|
|
194
|
+
|
|
195
|
+
{#snippet dot()}
|
|
196
|
+
<span
|
|
197
|
+
class="{config.color} size-[9px] shrink-0 rounded-full bg-current ring-[3px] ring-current/20 {running
|
|
198
|
+
? 'animate-pulse'
|
|
199
|
+
: ''} {dotFade}"
|
|
200
|
+
aria-hidden="true"
|
|
201
|
+
></span>
|
|
202
|
+
{/snippet}
|