@runwisp/ui 0.15.0 → 0.15.1

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 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.15.0",
4
+ "version": "0.15.1",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
@@ -49,8 +49,8 @@
49
49
  "devDependencies": {
50
50
  "@chromatic-com/storybook": "^5.2.1",
51
51
  "@eslint/js": "^10.0.1",
52
- "@runwisp/eslint-config": "0.15.0",
53
- "@runwisp/typescript-config": "0.15.0",
52
+ "@runwisp/eslint-config": "0.15.1",
53
+ "@runwisp/typescript-config": "0.15.1",
54
54
  "@storybook/addon-a11y": "^10.5.5",
55
55
  "@storybook/addon-docs": "^10.5.5",
56
56
  "@storybook/addon-svelte-csf": "^5.1.2",
@@ -93,7 +93,7 @@
93
93
  "@fontsource-variable/geist-mono": "^5.3.0",
94
94
  "@fontsource/tasa-orbiter": "^5.3.0",
95
95
  "@lucide/svelte": "^1.28.0",
96
- "@runwisp/common": "0.15.0",
96
+ "@runwisp/common": "0.15.1",
97
97
  "@tanstack/svelte-virtual": "^3.13.35",
98
98
  "ansi-to-html": "^0.7.2",
99
99
  "cron-parser": "^5.7.0",
@@ -32,7 +32,7 @@
32
32
  ...restProps
33
33
  }: Props = $props();
34
34
 
35
- const generatedId = `duration-input-${Math.random().toString(36).slice(2, 10)}`;
35
+ const generatedId = $props.id();
36
36
  const inputId = $derived(id ?? generatedId);
37
37
 
38
38
  let textValue = $state("");
@@ -30,7 +30,7 @@
30
30
  id,
31
31
  }: Props = $props();
32
32
 
33
- const generatedId = `field-${Math.random().toString(36).slice(2, 10)}`;
33
+ const generatedId = $props.id();
34
34
  const fieldId = $derived(id ?? generatedId);
35
35
  </script>
36
36
 
@@ -33,7 +33,7 @@
33
33
  ...restProps
34
34
  }: Props = $props();
35
35
 
36
- const generatedId = `input-${Math.random().toString(36).slice(2, 10)}`;
36
+ const generatedId = $props.id();
37
37
  const inputId = $derived(id ?? generatedId);
38
38
 
39
39
  const sizeClasses: Record<InputSize, string> = {
@@ -30,7 +30,7 @@
30
30
  import Tooltip from "../Tooltip.svelte";
31
31
  import { portal } from "../../actions/portal.js";
32
32
  import type { Run } from "./types.js";
33
- import type { LogEvent, LogSlice } from "../../log-console/types.js";
33
+ import { isLogEvent, type LogEvent, type LogSlice } from "../../log-console/types.js";
34
34
  import { formatClockTime, formatCalendarDate } from "../../utils/format.js";
35
35
  import { formatShortId } from "../../utils/id.js";
36
36
  import { TickingNow } from "../../utils/ticking-now.svelte.js";
@@ -146,22 +146,41 @@
146
146
  if (!stream) return;
147
147
 
148
148
  let cleanup: (() => void) | undefined;
149
+ let cancelled = false;
149
150
 
150
151
  untrack(() => {
151
- // Single SSE call seeds the viewport via backfill (negative
152
- // fromLine = tail-from-end) and continues with live events on
153
- // the same connection. No tail+stream handoff, no duplicate
154
- // replay, native Last-Event-ID resume on reconnect.
155
- cleanup = stream(
156
- id,
157
- (event: LogEvent) => {
158
- if (logConsole) logConsole.onStream(event);
159
- },
160
- { fromLine: -TAIL_LINES },
161
- );
152
+ void (async () => {
153
+ // Paint the tail in one batched page so the console opens
154
+ // already at the newest output, instead of replaying 1000
155
+ // backfill lines over SSE and visibly scrolling down to reach
156
+ // the end. The live stream then picks up after the seeded tail
157
+ // (its disk backfill from that anchor closes the fetch↔stream
158
+ // race, and the daemon dedupes anything already shown).
159
+ let fromLine = -TAIL_LINES;
160
+ try {
161
+ const seed = await fetchLogs(id, -TAIL_LINES, -1);
162
+ if (cancelled) return;
163
+ if (isLogEvent(seed)) {
164
+ if (logConsole) logConsole.onStream(seed);
165
+ if (seed.finished) return; // ended run: nothing live to follow
166
+ if (seed.sizeLines > 0) fromLine = seed.sizeLines;
167
+ }
168
+ } catch {
169
+ // Seed failed — fall back to the SSE tail backfill.
170
+ }
171
+ if (cancelled) return;
172
+ cleanup = stream(
173
+ id,
174
+ (event: LogEvent) => {
175
+ if (logConsole) logConsole.onStream(event);
176
+ },
177
+ { fromLine },
178
+ );
179
+ })();
162
180
  });
163
181
 
164
182
  return () => {
183
+ cancelled = true;
165
184
  if (cleanup) cleanup();
166
185
  };
167
186
  });
@@ -35,9 +35,7 @@
35
35
  --rw-font-sans: "TASA Orbiter", system-ui, -apple-system, sans-serif;
36
36
  --rw-font-mono:
37
37
  "Geist Mono Variable", "Geist Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
38
- }
39
38
 
40
- :root {
41
39
  /* Primary — Wisp Teal (brand = #0E7C86, the artifact's deep teal). Deeper
42
40
  * and less cyan than Aurora (running/info) so the two stay distinct. */
43
41
  --rw-color-wisp-50: oklch(0.97 0.02 205);
@@ -1,35 +0,0 @@
1
- <!-- SPDX-FileCopyrightText: PoppyCake, s.r.o. -->
2
- <!-- SPDX-License-Identifier: Apache-2.0 -->
3
-
4
- <script lang="ts">
5
- import type { Component, Snippet } from "svelte";
6
-
7
- interface Props {
8
- title: string;
9
- icon?: Component;
10
- children?: Snippet;
11
- class?: string;
12
- }
13
-
14
- let { title, icon: Icon, children, class: className = "" }: Props = $props();
15
- </script>
16
-
17
- <article
18
- class="overflow-hidden rounded-[4px] border border-outline bg-surface-raised p-6 hover:border-outline-hover {className}"
19
- >
20
- <div class="flex flex-col gap-3">
21
- {#if Icon}
22
- <span
23
- class="inline-flex h-10 w-10 items-center justify-center rounded-[3px] border border-primary-soft-border bg-primary-soft text-primary-soft-text"
24
- >
25
- <Icon size={20} />
26
- </span>
27
- {/if}
28
- <h3 class="font-mono text-base text-primary">{title}</h3>
29
- {#if children}
30
- <div class="font-sans text-sm leading-relaxed text-on-surface-muted">
31
- {@render children()}
32
- </div>
33
- {/if}
34
- </div>
35
- </article>
@@ -1,37 +0,0 @@
1
- <!-- SPDX-FileCopyrightText: PoppyCake, s.r.o. -->
2
- <!-- SPDX-License-Identifier: Apache-2.0 -->
3
-
4
- <script lang="ts">
5
- import type { Snippet } from "svelte";
6
-
7
- interface Props {
8
- columns?: 1 | 2 | 3 | 4;
9
- gap?: "sm" | "md" | "lg";
10
- children?: Snippet;
11
- class?: string;
12
- }
13
-
14
- let { columns = 3, gap = "md", children, class: className = "" }: Props = $props();
15
-
16
- const colClasses: Record<1 | 2 | 3 | 4, string> = {
17
- 1: "grid-cols-1",
18
- 2: "grid-cols-1 sm:grid-cols-2",
19
- 3: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
20
- 4: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-4",
21
- };
22
-
23
- const gapClasses: Record<"sm" | "md" | "lg", string> = {
24
- sm: "gap-3",
25
- md: "gap-4",
26
- lg: "gap-6",
27
- };
28
-
29
- const colClass = $derived(colClasses[columns]);
30
- const gapClass = $derived(gapClasses[gap]);
31
- </script>
32
-
33
- <div class="grid {colClass} {gapClass} {className}">
34
- {#if children}
35
- {@render children()}
36
- {/if}
37
- </div>