@runwisp/ui 0.12.0 → 0.14.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 (60) hide show
  1. package/package.json +40 -45
  2. package/src/astro/CTABand.astro +19 -12
  3. package/src/astro/Hero.astro +3 -3
  4. package/src/astro/SiteFooter.astro +4 -4
  5. package/src/astro/SiteHeader.astro +4 -4
  6. package/src/lib/assets/runwisp-logo.svg +1 -8
  7. package/src/lib/components/AccordionItem.svelte +4 -10
  8. package/src/lib/components/Alert.svelte +11 -11
  9. package/src/lib/components/Avatar.svelte +1 -1
  10. package/src/lib/components/Badge.svelte +13 -13
  11. package/src/lib/components/Breadcrumb.svelte +3 -3
  12. package/src/lib/components/Button.svelte +2 -2
  13. package/src/lib/components/Card.svelte +4 -4
  14. package/src/lib/components/Checkbox.svelte +12 -10
  15. package/src/lib/components/CodeBlock.svelte +7 -13
  16. package/src/lib/components/CronInput.svelte +12 -11
  17. package/src/lib/components/DataGrid.svelte +14 -11
  18. package/src/lib/components/Drawer.svelte +9 -6
  19. package/src/lib/components/Dropdown.svelte +10 -14
  20. package/src/lib/components/DurationInput.svelte +10 -14
  21. package/src/lib/components/EmptyState.svelte +1 -1
  22. package/src/lib/components/ErrorState.svelte +4 -4
  23. package/src/lib/components/FeatureCard.svelte +4 -4
  24. package/src/lib/components/FormField.svelte +11 -6
  25. package/src/lib/components/Heading.svelte +4 -1
  26. package/src/lib/components/Input.svelte +10 -12
  27. package/src/lib/components/Kbd.svelte +1 -1
  28. package/src/lib/components/Link.svelte +2 -3
  29. package/src/lib/components/LinkButton.svelte +2 -2
  30. package/src/lib/components/LogConsole.svelte +22 -21
  31. package/src/lib/components/Logo.svelte +3 -1
  32. package/src/lib/components/Modal.svelte +9 -8
  33. package/src/lib/components/PageContainer.svelte +1 -1
  34. package/src/lib/components/PageHeader.svelte +1 -1
  35. package/src/lib/components/Pagination.svelte +4 -4
  36. package/src/lib/components/Popover.svelte +5 -7
  37. package/src/lib/components/Progress.svelte +6 -4
  38. package/src/lib/components/Prose.svelte +4 -4
  39. package/src/lib/components/Radio.svelte +11 -10
  40. package/src/lib/components/RadioGroup.svelte +3 -5
  41. package/src/lib/components/Select.svelte +21 -31
  42. package/src/lib/components/Separator.svelte +4 -1
  43. package/src/lib/components/Skeleton.svelte +6 -6
  44. package/src/lib/components/StatusIndicator.svelte +7 -15
  45. package/src/lib/components/Table.svelte +1 -1
  46. package/src/lib/components/TableCell.svelte +1 -1
  47. package/src/lib/components/TableRow.svelte +1 -5
  48. package/src/lib/components/Tabs.svelte +10 -11
  49. package/src/lib/components/TaskCard.svelte +7 -4
  50. package/src/lib/components/Textarea.svelte +10 -11
  51. package/src/lib/components/ThemeToggle.svelte +1 -1
  52. package/src/lib/components/ToastContainer.svelte +3 -3
  53. package/src/lib/components/Toggle.svelte +2 -3
  54. package/src/lib/components/Tooltip.svelte +8 -9
  55. package/src/lib/components/button-styles.ts +17 -18
  56. package/src/lib/components/dashboard/RunDetailPanel.svelte +61 -49
  57. package/src/lib/components/dashboard/RunFilterPopover.svelte +17 -11
  58. package/src/lib/components/dashboard/RunsList.svelte +26 -21
  59. package/src/lib/theme-tokens.css +179 -114
  60. package/src/lib/theme.css +49 -35
@@ -34,21 +34,21 @@
34
34
  };
35
35
 
36
36
  const textareaClasses = `
37
- w-full px-3.5 py-2.5 rounded-lg border
37
+ w-full px-3.5 py-2.5 rounded-[3px] border font-mono
38
38
  text-sm bg-surface-raised text-on-surface placeholder:text-on-surface-faint
39
- focus:outline-none focus:ring-2 focus:ring-offset-0
39
+ focus:outline-none focus:ring-2 focus:ring-offset-2
40
40
  disabled:bg-surface-sunken disabled:text-on-surface-muted disabled:cursor-not-allowed
41
- transition-colors duration-fast
42
- `;
41
+ `;
43
42
 
44
43
  const normalBorder =
45
- "border-outline hover:border-outline-hover focus:border-ring focus:ring-ring/20";
46
- const errorBorder = "border-danger-400 focus:border-danger-500 focus:ring-danger-500/20";
44
+ "border-outline hover:border-outline-hover shadow-sm focus:border-ring focus:ring-ring";
45
+ const errorBorder =
46
+ "border-danger-surface focus:border-danger-surface focus:ring-danger-surface";
47
47
  </script>
48
48
 
49
49
  <div class="space-y-1.5 {className}">
50
50
  {#if label}
51
- <label class="block text-sm font-medium text-on-surface-muted" for={id}>
51
+ <label class="block font-mono text-xs font-medium text-on-surface-muted" for={id}>
52
52
  {label}
53
53
  </label>
54
54
  {/if}
@@ -63,8 +63,7 @@
63
63
  {disabled}
64
64
  {id}
65
65
  bind:value
66
- {...restProps}
67
- ></textarea>
66
+ {...restProps}></textarea>
68
67
 
69
68
  {#if error}
70
69
  <div class="pointer-events-none absolute top-3 right-3 text-danger-soft-text">
@@ -74,8 +73,8 @@
74
73
  </div>
75
74
 
76
75
  {#if error}
77
- <p class="text-sm text-danger-soft-text">{error}</p>
76
+ <p class="font-sans text-xs text-danger-soft-text">{error}</p>
78
77
  {:else if hint}
79
- <p class="text-sm text-on-surface-muted">{hint}</p>
78
+ <p class="font-sans text-xs text-on-surface-muted">{hint}</p>
80
79
  {/if}
81
80
  </div>
@@ -37,7 +37,7 @@
37
37
  aria-pressed={isDark}
38
38
  aria-label={isDark ? "Switch to light theme" : "Switch to dark theme"}
39
39
  title={isDark ? "Switch to light theme" : "Switch to dark theme"}
40
- class="inline-flex h-9 w-9 items-center justify-center rounded-lg border border-outline bg-surface-raised text-on-surface-muted transition-colors hover:border-outline-hover hover:text-on-surface {className}"
40
+ class="inline-flex h-9 w-9 items-center justify-center rounded-[3px] border border-outline bg-surface-raised text-on-surface-muted hover:border-outline-hover hover:text-on-surface {className}"
41
41
  >
42
42
  {#if mounted && isDark}
43
43
  <Sun size={16} />
@@ -62,7 +62,7 @@
62
62
  <div
63
63
  in:fly={{ y: 20, duration: 300 }}
64
64
  out:fade={{ duration: 200 }}
65
- class="pointer-events-auto isolate flex max-w-md min-w-[320px] items-start gap-3 rounded-xl border bg-surface-overlay/95 px-4 py-3 shadow-lg ring-1 ring-on-surface/5 backdrop-blur-md {getColorClasses(
65
+ class="pointer-events-auto isolate flex max-w-md min-w-[320px] items-start gap-3 rounded-[4px] border bg-surface-overlay px-4 py-3 shadow-lg {getColorClasses(
66
66
  toastItem.type,
67
67
  )}"
68
68
  role="alert"
@@ -79,14 +79,14 @@
79
79
  action.onClick();
80
80
  toast.remove(toastItem.id);
81
81
  }}
82
- class="flex-shrink-0 rounded-md px-2 py-1 text-xs font-semibold text-on-surface underline-offset-2 transition-colors hover:bg-surface-sunken hover:underline"
82
+ class="flex-shrink-0 rounded-[3px] px-2 py-1 font-mono text-xs font-semibold text-on-surface underline-offset-2 hover:bg-surface-sunken hover:underline"
83
83
  >
84
84
  {action.label}
85
85
  </button>
86
86
  {/if}
87
87
  <button
88
88
  onclick={() => toast.remove(toastItem.id)}
89
- class="flex-shrink-0 rounded-lg p-1 text-on-surface-muted transition-colors hover:bg-surface-sunken hover:text-on-surface"
89
+ class="flex-shrink-0 rounded-[3px] p-1 text-on-surface-muted hover:bg-surface-sunken hover:text-on-surface"
90
90
  aria-label="Close notification"
91
91
  >
92
92
  <X class="h-4 w-4" aria-hidden="true" />
@@ -55,7 +55,6 @@
55
55
  class="
56
56
  relative inline-flex shrink-0 items-center rounded-full
57
57
  {sizeConfig.track}
58
- transition-colors duration-200
59
58
  focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2
60
59
  {checked ? 'bg-primary' : 'bg-outline-hover'}
61
60
  {disabled ? 'cursor-not-allowed' : 'cursor-pointer'}
@@ -65,7 +64,7 @@
65
64
  class="
66
65
  inline-block rounded-full bg-surface-raised shadow-sm
67
66
  {sizeConfig.thumb}
68
- transform transition-transform duration-200
67
+ transform transition-transform duration-[120ms]
69
68
  {checked ? sizeConfig.translate : 'translate-x-0.5'}
70
69
  "
71
70
  ></span>
@@ -74,7 +73,7 @@
74
73
  {#if label || description}
75
74
  <div class="flex flex-col gap-0.5">
76
75
  {#if label}
77
- <span class="{sizeConfig.text} font-medium text-on-surface">{label}</span>
76
+ <span class="{sizeConfig.text} font-mono font-medium text-on-surface">{label}</span>
78
77
  {/if}
79
78
  {#if description}
80
79
  <span class="text-sm text-on-surface-muted">{description}</span>
@@ -31,10 +31,10 @@
31
31
  };
32
32
 
33
33
  const arrowClasses: Record<TooltipPosition, string> = {
34
- top: "top-full left-1/2 -translate-x-1/2 border-t-mist-800 border-x-transparent border-b-transparent",
35
- bottom: "bottom-full left-1/2 -translate-x-1/2 border-b-mist-800 border-x-transparent border-t-transparent",
36
- left: "left-full top-1/2 -translate-y-1/2 border-l-mist-800 border-y-transparent border-r-transparent",
37
- right: "right-full top-1/2 -translate-y-1/2 border-r-mist-800 border-y-transparent border-l-transparent",
34
+ top: "top-full left-1/2 -translate-x-1/2 border-t-outline border-x-transparent border-b-transparent",
35
+ bottom: "bottom-full left-1/2 -translate-x-1/2 border-b-outline border-x-transparent border-t-transparent",
36
+ left: "left-full top-1/2 -translate-y-1/2 border-l-outline border-y-transparent border-r-transparent",
37
+ right: "right-full top-1/2 -translate-y-1/2 border-r-outline border-y-transparent border-l-transparent",
38
38
  };
39
39
  </script>
40
40
 
@@ -44,12 +44,11 @@
44
44
  <div
45
45
  class="
46
46
  absolute z-50 {positionClasses[position]}
47
- pointer-events-none invisible rounded-lg
48
- bg-mist-800 px-2.5 py-1.5 text-xs
49
- font-medium
47
+ pointer-events-none invisible rounded-[3px]
48
+ border border-outline bg-surface-overlay px-2.5 py-1.5 font-sans
49
+ text-xs
50
50
  {wide ? 'w-max max-w-xs whitespace-normal' : 'whitespace-nowrap'}
51
- text-white opacity-0 transition-opacity
52
- duration-150 group-hover:visible
51
+ text-on-surface opacity-0 group-hover:visible
53
52
  group-hover:opacity-100
54
53
  "
55
54
  role="tooltip"
@@ -7,45 +7,44 @@ export type ButtonSize = "xs" | "sm" | "md" | "lg";
7
7
  export const BUTTON_BASE = `
8
8
  group
9
9
  inline-flex items-center justify-center gap-2
10
- font-medium cursor-pointer select-none
11
- border border-transparent
12
- focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring/10 focus-visible:border-ring
10
+ font-mono font-medium tracking-normal
11
+ cursor-pointer select-none border
12
+ focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2
13
13
  disabled:opacity-50 disabled:cursor-not-allowed
14
- transition-all duration-normal ease-out
15
- active:scale-[0.98]
14
+ active:translate-y-px
16
15
  `;
17
16
 
18
17
  export const BUTTON_VARIANTS: Record<ButtonVariant, string> = {
19
18
  primary: `
20
- bg-primary text-on-primary shadow-sm shadow-primary/20
21
- hover:bg-primary-hover hover:shadow-md hover:shadow-primary/30
19
+ bg-primary text-on-primary border-transparent
20
+ hover:bg-primary-hover
22
21
  active:bg-primary-active
23
22
  `,
24
23
  secondary: `
25
- bg-surface-raised text-on-surface-muted border-outline shadow-sm
26
- hover:bg-surface-sunken hover:text-on-surface hover:border-outline-hover
24
+ bg-surface-raised text-on-surface border-outline
25
+ hover:border-outline-hover hover:text-primary
27
26
  active:bg-surface-sunken
28
27
  `,
29
28
  ghost: `
30
- bg-transparent text-on-surface-muted
29
+ bg-transparent text-on-surface-muted border-transparent
31
30
  hover:bg-surface-sunken hover:text-on-surface
32
31
  active:bg-surface-sunken
33
32
  `,
34
33
  danger: `
35
- bg-danger-surface text-on-danger shadow-sm shadow-danger-surface/20
36
- hover:bg-danger-hover hover:shadow-md hover:shadow-danger-surface/30
34
+ bg-danger-surface text-on-danger border-transparent
35
+ hover:bg-danger-hover
37
36
  active:bg-danger-active
38
37
  `,
39
38
  success: `
40
- bg-success-surface text-on-success shadow-sm shadow-success-surface/20
41
- hover:bg-success-hover hover:shadow-md hover:shadow-success-surface/30
39
+ bg-success-surface text-on-success border-transparent
40
+ hover:bg-success-hover
42
41
  active:bg-success-surface
43
42
  `,
44
43
  };
45
44
 
46
45
  export const BUTTON_SIZES: Record<ButtonSize, string> = {
47
- xs: "text-xs px-2 py-1 rounded-md",
48
- sm: "text-sm px-3 py-1.5 rounded-lg",
49
- md: "text-sm px-4 py-2 rounded-lg",
50
- lg: "text-base px-6 py-3 rounded-xl",
46
+ xs: "text-xs px-2.5 py-1 rounded-[3px]",
47
+ sm: "text-sm px-3 py-1.5 rounded-[3px]",
48
+ md: "text-sm px-4 py-2 rounded-[3px]",
49
+ lg: "text-base px-6 py-2.5 rounded-[3px]",
51
50
  };
@@ -15,12 +15,13 @@
15
15
  RotateCw,
16
16
  RefreshCcw,
17
17
  ChevronDown,
18
- History,
18
+ RotateCcwClock,
19
19
  PanelLeftClose,
20
20
  SlidersHorizontal,
21
21
  Maximize2,
22
22
  Minimize2,
23
23
  TextWrap,
24
+ SearchX,
24
25
  } from "@lucide/svelte";
25
26
  import Button from "../Button.svelte";
26
27
  import EmptyState from "../EmptyState.svelte";
@@ -67,6 +68,7 @@
67
68
  historyVisible = false,
68
69
  highlightLine = null,
69
70
  getInstanceCount = () => 1,
71
+ notFound = false,
70
72
  }: {
71
73
  run: Run | undefined;
72
74
  fetchLogs: (
@@ -112,6 +114,10 @@
112
114
  // Resolves a task's currently configured instance count so multi-instance
113
115
  // services render a 1-based #N suffix. Defaults to single-instance.
114
116
  getInstanceCount?: (taskName: string) => number;
117
+ // True when a deep-linked run id resolved to no run (deleted by retention,
118
+ // or never existed). The empty state then says so plainly instead of the
119
+ // generic "Select a run" — the caller must not silently substitute another.
120
+ notFound?: boolean;
115
121
  } = $props();
116
122
 
117
123
  let canDelete = $derived.by(() => {
@@ -356,7 +362,7 @@
356
362
  <div class="flex items-start gap-4">
357
363
  <!-- Verdict tile -->
358
364
  <div
359
- class="flex h-[38px] w-[38px] shrink-0 items-center justify-center rounded-[11px] {config.bg} {config.color} ring-1 ring-current/25 ring-inset"
365
+ class="flex h-[38px] w-[38px] shrink-0 items-center justify-center rounded-[4px] {config.bg} {config.color} ring-1 ring-current/25 ring-inset"
360
366
  >
361
367
  <DetailIcon size={20} class={isRunning ? "animate-spin" : ""} />
362
368
  </div>
@@ -382,7 +388,7 @@
382
388
  outcome, so stopped reads amber, not red. -->
383
389
  <Tooltip content={config.description} position="bottom" wide>
384
390
  <span
385
- class="rounded-full border px-2.5 py-0.5 text-2xs font-bold tracking-wider uppercase {config.color} {config.bg} {config.border}"
391
+ class="rounded-[3px] border px-2.5 py-0.5 font-mono text-2xs font-bold tracking-wider uppercase {config.color} {config.bg} {config.border}"
386
392
  >
387
393
  {status.toUpperCase()}
388
394
  </span>
@@ -396,7 +402,7 @@
396
402
  <!-- When the title carries the task name, the run's
397
403
  date/time lives here in the sub-line (otherwise it's
398
404
  already the title, so don't repeat it). -->
399
- <span title={formatFullDateTime(startedAt)}>
405
+ <span class="font-mono" title={formatFullDateTime(startedAt)}>
400
406
  Run · {formatCalendarDate(startedAt)}
401
407
  {formatTimeHM(startedAt)}
402
408
  </span>
@@ -406,7 +412,7 @@
406
412
  type="button"
407
413
  onclick={() => copyRunId(run.id)}
408
414
  title="Copy run ID"
409
- class="inline-flex items-center gap-1.5 rounded-md border border-outline-faint bg-surface-sunken px-1.5 py-0.5 font-mono text-2xs text-on-surface-faint transition-colors hover:border-outline hover:text-on-surface-muted"
415
+ class="inline-flex items-center gap-1.5 rounded-[3px] border border-outline-faint bg-surface-sunken px-1.5 py-0.5 font-mono text-2xs text-on-surface-faint hover:border-outline-hover hover:text-primary"
410
416
  >
411
417
  {#if copiedId}
412
418
  <Check size={11} class="text-success-surface" />Copied
@@ -417,7 +423,7 @@
417
423
  {#if retry}
418
424
  <span class="hidden h-1 w-1 rounded-full bg-outline-faint sm:block"
419
425
  ></span>
420
- <span class="inline-flex items-center gap-1.5">
426
+ <span class="inline-flex items-center gap-1.5 font-mono">
421
427
  <RotateCw size={14} />
422
428
  Retry #{run.retry_attempt}{#if run.retry_of_run_id}
423
429
  <span class="text-on-surface-faint"
@@ -431,7 +437,7 @@
431
437
  <Popover placement="bottom-start">
432
438
  {#snippet trigger()}
433
439
  <span
434
- class="inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-outline-faint bg-surface-sunken px-2.5 py-0.5 text-xs font-medium text-on-surface-muted transition-colors hover:border-outline hover:text-on-surface"
440
+ class="inline-flex cursor-pointer items-center gap-1.5 rounded-[3px] border border-outline-faint bg-surface-sunken px-2.5 py-0.5 font-mono text-xs font-medium text-on-surface-muted hover:border-outline-hover hover:text-primary"
435
441
  title="View run parameters"
436
442
  >
437
443
  <SlidersHorizontal size={12} />
@@ -441,7 +447,7 @@
441
447
  {/snippet}
442
448
  <div class="max-w-md min-w-48">
443
449
  <span
444
- class="text-xs font-semibold tracking-wide text-on-surface-faint uppercase"
450
+ class="font-mono text-xs font-semibold tracking-[0.14em] text-on-surface-faint uppercase"
445
451
  >Parameters</span
446
452
  >
447
453
  <dl
@@ -471,7 +477,7 @@
471
477
  <button
472
478
  type="button"
473
479
  onclick={() => onToggleHistory()}
474
- class="inline-flex items-center justify-center rounded-lg border border-outline-faint bg-surface-raised p-2 text-on-surface-muted transition-colors hover:bg-surface-sunken hover:text-on-surface"
480
+ class="inline-flex items-center justify-center rounded-[3px] border border-outline-faint bg-surface-raised p-2 text-on-surface-muted hover:border-outline-hover hover:bg-surface-sunken hover:text-primary"
475
481
  title={historyVisible ? "Hide run history" : "Show run history"}
476
482
  aria-label={historyVisible
477
483
  ? "Hide run history"
@@ -480,7 +486,7 @@
480
486
  {#if historyVisible}
481
487
  <PanelLeftClose size={15} />
482
488
  {:else}
483
- <History size={15} />
489
+ <RotateCcwClock size={15} />
484
490
  {/if}
485
491
  </button>
486
492
  {/if}
@@ -499,7 +505,7 @@
499
505
  title={serviceStopped
500
506
  ? "Starts the service"
501
507
  : "Cancels and respawns every instance"}
502
- class="inline-flex h-9 cursor-pointer items-center gap-1.5 rounded-lg border border-primary-soft bg-surface-raised px-3 text-sm font-medium text-primary transition-colors hover:bg-primary-soft disabled:cursor-not-allowed disabled:opacity-60"
508
+ class="inline-flex h-9 cursor-pointer items-center gap-1.5 rounded-[3px] border border-primary-soft-border bg-surface-raised px-3 font-mono text-sm font-medium text-primary hover:bg-primary-soft active:translate-y-px disabled:cursor-not-allowed disabled:opacity-60"
503
509
  >
504
510
  {#if serviceStopped}
505
511
  <Play size={15} fill="currentColor" stroke="none" />
@@ -517,7 +523,7 @@
517
523
  onclick={() => onStopService()}
518
524
  disabled={serviceBusy}
519
525
  title="Stops the service for the rest of the daemon's lifetime"
520
- class="inline-flex h-9 cursor-pointer items-center gap-1.5 rounded-lg border border-danger-soft bg-surface-raised px-3 text-sm font-medium text-danger-surface transition-colors hover:bg-danger-soft disabled:cursor-not-allowed disabled:opacity-60"
526
+ class="inline-flex h-9 cursor-pointer items-center gap-1.5 rounded-[3px] border border-danger-soft-border bg-surface-raised px-3 font-mono text-sm font-medium text-danger-surface hover:bg-danger-soft active:translate-y-px disabled:cursor-not-allowed disabled:opacity-60"
521
527
  >
522
528
  <Square size={15} fill="currentColor" stroke="none" />
523
529
  <span class="@max-md:hidden">Stop Service</span>
@@ -528,13 +534,13 @@
528
534
  <!-- Split button: Run (defaults) + an arrow for the
529
535
  reuse-parameters variant, shown only when there is one. -->
530
536
  <div
531
- class="inline-flex overflow-hidden rounded-lg border border-primary-soft"
537
+ class="inline-flex overflow-hidden rounded-[3px] border border-primary-soft-border"
532
538
  >
533
539
  <button
534
540
  type="button"
535
541
  onclick={() => onRun()}
536
542
  title="Run this task now with default options"
537
- class="inline-flex h-9 cursor-pointer items-center gap-1.5 bg-surface-raised px-3 text-sm font-medium text-primary transition-colors hover:bg-primary-soft"
543
+ class="inline-flex h-9 cursor-pointer items-center gap-1.5 bg-surface-raised px-3 font-mono text-sm font-medium text-primary hover:bg-primary-soft active:translate-y-px"
538
544
  >
539
545
  <Play size={15} fill="currentColor" stroke="none" />
540
546
  <span class="@max-md:hidden">Run</span>
@@ -543,7 +549,7 @@
543
549
  <Popover bind:open={runMenuOpen} placement="bottom-end">
544
550
  {#snippet trigger()}
545
551
  <span
546
- class="flex h-9 cursor-pointer items-center border-l border-primary-soft bg-surface-raised px-2 text-primary transition-colors hover:bg-primary-soft"
552
+ class="flex h-9 cursor-pointer items-center border-l border-primary-soft-border bg-surface-raised px-2 text-primary hover:bg-primary-soft"
547
553
  title="More run options"
548
554
  aria-label="More run options"
549
555
  >
@@ -564,7 +570,7 @@
564
570
  />
565
571
  <span class="flex flex-col">
566
572
  <span
567
- class="text-sm font-medium text-on-surface"
573
+ class="font-mono text-sm font-medium text-on-surface"
568
574
  >Run again</span
569
575
  >
570
576
  <span class="text-xs text-on-surface-muted"
@@ -580,7 +586,7 @@
580
586
  <button
581
587
  type="button"
582
588
  onclick={() => onStop(run.id)}
583
- class="inline-flex h-9 cursor-pointer items-center gap-1.5 rounded-lg border border-danger-soft bg-surface-raised px-3 text-sm font-medium text-danger-surface transition-colors hover:bg-danger-soft"
589
+ class="inline-flex h-9 cursor-pointer items-center gap-1.5 rounded-[3px] border border-danger-soft-border bg-surface-raised px-3 font-mono text-sm font-medium text-danger-surface hover:bg-danger-soft active:translate-y-px"
584
590
  >
585
591
  <Square size={15} fill="currentColor" stroke="none" />
586
592
  <span class="@max-md:hidden">Stop</span>
@@ -592,7 +598,7 @@
592
598
  run.task_name,
593
599
  )}/runs/{encodeURIComponent(run.id)}/log/raw"
594
600
  download="{run.task_name}-{run.id}.log"
595
- class="inline-flex items-center justify-center rounded-lg border border-outline-faint bg-surface-raised p-2 text-on-surface-muted transition-colors hover:bg-surface-sunken hover:text-on-surface"
601
+ class="inline-flex items-center justify-center rounded-[3px] border border-outline-faint bg-surface-raised p-2 text-on-surface-muted hover:border-outline-hover hover:bg-surface-sunken hover:text-primary"
596
602
  title="Download the full log (rotated and current parts as one file)"
597
603
  aria-label="Download log"
598
604
  >
@@ -602,7 +608,7 @@
602
608
  <Popover bind:open={confirmDeleteOpen} placement="bottom-end">
603
609
  {#snippet trigger()}
604
610
  <span
605
- class="flex cursor-pointer items-center justify-center rounded-lg border border-outline-faint bg-surface-raised p-2 text-on-surface-muted transition-colors hover:border-danger-soft hover:bg-danger-soft hover:text-danger-surface"
611
+ class="flex cursor-pointer items-center justify-center rounded-[3px] border border-outline-faint bg-surface-raised p-2 text-on-surface-muted hover:border-danger-soft-border hover:bg-danger-soft hover:text-danger-surface"
606
612
  title="Delete this run"
607
613
  aria-label="Delete run"
608
614
  >
@@ -610,7 +616,7 @@
610
616
  </span>
611
617
  {/snippet}
612
618
  <div class="w-60">
613
- <div class="text-sm font-semibold text-on-surface">
619
+ <div class="font-mono text-sm font-semibold text-on-surface">
614
620
  Delete this run?
615
621
  </div>
616
622
  <div class="mt-1 text-xs leading-relaxed text-on-surface-muted">
@@ -645,13 +651,13 @@
645
651
  <!-- Instrument cluster: the run's vital readout — value over a one-word
646
652
  context line, hairline-divided. -->
647
653
  <div
648
- class="mt-4 flex flex-wrap overflow-hidden rounded-xl border border-outline-faint bg-surface-raised"
654
+ class="mt-4 flex flex-wrap overflow-hidden rounded-[4px] border border-outline-faint bg-surface-raised"
649
655
  >
650
656
  <div
651
657
  class="flex min-w-30 flex-1 flex-col gap-0.5 border-l border-outline-faint px-4 py-2.5 first:border-l-0"
652
658
  >
653
659
  <span
654
- class="text-2xs font-bold tracking-wider text-on-surface-faint uppercase"
660
+ class="font-mono text-2xs font-bold tracking-[0.12em] text-on-surface-faint uppercase"
655
661
  >Queued</span
656
662
  >
657
663
  <span
@@ -660,7 +666,7 @@
660
666
  >
661
667
  {formatClockTime(run.created_at)}
662
668
  </span>
663
- <span class="text-2xs text-on-surface-muted"
669
+ <span class="font-mono text-2xs text-on-surface-muted"
664
670
  >+{startDelay ?? "0s"} queued</span
665
671
  >
666
672
  </div>
@@ -669,7 +675,7 @@
669
675
  class="flex min-w-30 flex-1 flex-col gap-0.5 border-l border-outline-faint px-4 py-2.5 first:border-l-0"
670
676
  >
671
677
  <span
672
- class="text-2xs font-bold tracking-wider text-on-surface-faint uppercase"
678
+ class="font-mono text-2xs font-bold tracking-[0.12em] text-on-surface-faint uppercase"
673
679
  >Started</span
674
680
  >
675
681
  <span
@@ -677,7 +683,7 @@
677
683
  >
678
684
  {run.start_at ? formatClockTime(run.start_at) : "—"}
679
685
  </span>
680
- <span class="text-2xs text-on-surface-muted">
686
+ <span class="font-mono text-2xs text-on-surface-muted">
681
687
  {run.start_at ? formatCalendarDate(run.start_at) : "not started"}
682
688
  </span>
683
689
  </div>
@@ -686,7 +692,7 @@
686
692
  class="flex min-w-30 flex-1 flex-col gap-0.5 border-l border-outline-faint px-4 py-2.5 first:border-l-0"
687
693
  >
688
694
  <span
689
- class="text-2xs font-bold tracking-wider text-on-surface-faint uppercase"
695
+ class="font-mono text-2xs font-bold tracking-[0.12em] text-on-surface-faint uppercase"
690
696
  >Ran for</span
691
697
  >
692
698
  <span
@@ -694,7 +700,7 @@
694
700
  >
695
701
  {duration ?? "—"}
696
702
  </span>
697
- <span class="text-2xs text-on-surface-muted">
703
+ <span class="font-mono text-2xs text-on-surface-muted">
698
704
  {isRunning ? "and counting" : run.end_at ? "wall clock" : "—"}
699
705
  </span>
700
706
  </div>
@@ -703,7 +709,7 @@
703
709
  class="flex min-w-30 flex-1 flex-col gap-0.5 border-l border-outline-faint px-4 py-2.5 first:border-l-0"
704
710
  >
705
711
  <span
706
- class="text-2xs font-bold tracking-wider text-on-surface-faint uppercase"
712
+ class="font-mono text-2xs font-bold tracking-[0.12em] text-on-surface-faint uppercase"
707
713
  >Exit</span
708
714
  >
709
715
  <span
@@ -716,26 +722,28 @@
716
722
  <span>{showCode ? run.exit_code : "—"}</span>
717
723
  {#if exitFail}
718
724
  <span
719
- class="rounded bg-danger-soft px-1 py-px text-[9px] font-bold tracking-wide text-danger-surface uppercase"
725
+ class="rounded-[3px] bg-danger-soft px-1 py-px font-mono text-[9px] font-bold tracking-wide text-danger-surface uppercase"
720
726
  >fail</span
721
727
  >
722
728
  {/if}
723
729
  </span>
724
- <span class="text-2xs text-on-surface-muted">{exitSubLabel(status)}</span>
730
+ <span class="font-mono text-2xs text-on-surface-muted"
731
+ >{exitSubLabel(status)}</span
732
+ >
725
733
  </div>
726
734
 
727
735
  <div
728
736
  class="flex min-w-30 flex-1 flex-col gap-0.5 border-l border-outline-faint px-4 py-2.5 first:border-l-0"
729
737
  >
730
738
  <span
731
- class="text-2xs font-bold tracking-wider text-on-surface-faint uppercase"
739
+ class="font-mono text-2xs font-bold tracking-[0.12em] text-on-surface-faint uppercase"
732
740
  >Trigger</span
733
741
  >
734
742
  <span
735
743
  class="font-mono text-base font-medium tracking-tight whitespace-nowrap text-on-surface tabular-nums"
736
744
  >{formatTriggeredByLabel(run.triggered_by)}</span
737
745
  >
738
- <span class="text-2xs text-on-surface-muted"
746
+ <span class="font-mono text-2xs text-on-surface-muted"
739
747
  >{triggerSub(run.triggered_by)}</span
740
748
  >
741
749
  </div>
@@ -755,25 +763,27 @@
755
763
  <div
756
764
  use:maximizePortal={consoleMaximized}
757
765
  class={consoleMaximized
758
- ? "console-zoom fixed inset-3 z-50 flex flex-col overflow-hidden rounded-xl bg-[var(--rw-con-bg)] shadow-2xl ring-1 ring-[rgb(255_255_255_/_0.08)] sm:inset-6"
759
- : "ml-1 flex min-h-[300px] flex-1 flex-col overflow-hidden border-t border-[rgb(255_255_255_/_0.06)] bg-[var(--rw-con-bg)]"}
766
+ ? "console-zoom fixed inset-3 z-50 flex flex-col overflow-hidden rounded-[4px] bg-[var(--rw-con-bg)] shadow-2xl ring-1 ring-[var(--rw-con-gutter)] sm:inset-6"
767
+ : "ml-1 flex min-h-[300px] flex-1 flex-col overflow-hidden border-t border-[var(--rw-con-gutter)] bg-[var(--rw-con-bg)]"}
760
768
  >
761
769
  <div
762
- class="flex shrink-0 items-center justify-between gap-3 border-b border-[rgb(255_255_255_/_0.06)] bg-[var(--rw-con-panel)] px-3.5 py-[9px] font-mono text-[11.5px] text-[var(--rw-con-dim)]"
770
+ class="flex shrink-0 items-center justify-between gap-3 border-b border-[var(--rw-con-gutter)] bg-[var(--rw-con-panel)] px-3.5 py-[9px] font-mono text-[11.5px] text-[var(--rw-con-dim)]"
763
771
  >
764
772
  <div class="flex min-w-0 items-center gap-3">
765
773
  {#if consoleMaximized}
766
774
  <!-- Focus-mode identity: which run is filling the screen -->
767
775
  <span class="flex min-w-0 items-center gap-2">
768
776
  <span class="h-2 w-2 shrink-0 rounded-full {spine}"></span>
769
- <span class="truncate font-medium text-[#b8c5d1]">
777
+ <span class="truncate font-medium text-[var(--rw-con-text)]">
770
778
  {run.task_name}{suffix}
771
779
  </span>
772
780
  <span class="text-[var(--rw-con-gutter)]">·</span>
773
781
  <span class="font-semibold capitalize {config.color}">{status}</span>
774
782
  </span>
775
783
  {:else}
776
- <span class="flex items-center gap-2 font-semibold text-[#98a7b5]">
784
+ <span
785
+ class="flex items-center gap-2 font-semibold text-[var(--rw-con-text)]"
786
+ >
777
787
  <TerminalIcon size={14} class="opacity-70" />
778
788
  Console output
779
789
  </span>
@@ -786,8 +796,8 @@
786
796
  <button
787
797
  type="button"
788
798
  onclick={() => (consoleWrap = !consoleWrap)}
789
- class="flex cursor-pointer items-center gap-1.5 rounded-md px-2 py-1 transition-colors hover:bg-[rgb(255_255_255_/_0.05)] hover:text-[#b8c5d1] {consoleWrap
790
- ? 'text-[#b8c5d1]'
799
+ class="flex cursor-pointer items-center gap-1.5 rounded-[3px] px-2 py-1 hover:bg-[var(--rw-con-gutter)]/30 hover:text-[var(--rw-con-text)] {consoleWrap
800
+ ? 'text-[var(--rw-con-text)]'
791
801
  : 'text-[var(--rw-con-dim)]'}"
792
802
  title={consoleWrap ? "Unwrap lines" : "Wrap long lines"}
793
803
  aria-pressed={consoleWrap}
@@ -799,14 +809,14 @@
799
809
  <button
800
810
  type="button"
801
811
  onclick={() => (consoleMaximized = !consoleMaximized)}
802
- class="flex cursor-pointer items-center gap-1.5 rounded-md px-2 py-1 text-[var(--rw-con-dim)] transition-colors hover:bg-[rgb(255_255_255_/_0.05)] hover:text-[#b8c5d1]"
812
+ class="flex cursor-pointer items-center gap-1.5 rounded-[3px] px-2 py-1 text-[var(--rw-con-dim)] hover:bg-[var(--rw-con-gutter)]/30 hover:text-[var(--rw-con-text)]"
803
813
  title={consoleMaximized ? "Restore console (Esc)" : "Maximize console · F"}
804
814
  aria-label={consoleMaximized ? "Restore console" : "Maximize console"}
805
815
  >
806
816
  {#if consoleMaximized}
807
817
  <Minimize2 size={13} />
808
818
  <span
809
- class="rounded border border-[var(--rw-con-gutter)] px-1.5 text-[10px] tracking-wide"
819
+ class="rounded-[3px] border border-[var(--rw-con-gutter)] px-1.5 text-[10px] tracking-wide"
810
820
  >Esc</span
811
821
  >
812
822
  {:else}
@@ -837,17 +847,19 @@
837
847
  class="flex min-w-0 flex-1 flex-col items-center justify-center gap-4 bg-surface-sunken/30"
838
848
  >
839
849
  <EmptyState
840
- title={onRunTask ? "No runs yet" : "Select a run"}
841
- description={onRunTask
842
- ? "This task hasn't run yet. Trigger it to see its output here."
843
- : "Pick a run from the list to view details and logs."}
844
- icon={MousePointerClick}
850
+ title={notFound ? "Run not found" : onRunTask ? "No runs yet" : "Select a run"}
851
+ description={notFound
852
+ ? "This run doesn't exist — it may have been deleted by retention, or the link is wrong. Pick a run from the list to continue."
853
+ : onRunTask
854
+ ? "This task hasn't run yet. Trigger it to see its output here."
855
+ : "Pick a run from the list to view details and logs."}
856
+ icon={notFound ? SearchX : MousePointerClick}
845
857
  />
846
- {#if onRunTask}
858
+ {#if onRunTask && !notFound}
847
859
  <button
848
860
  type="button"
849
861
  onclick={() => onRunTask()}
850
- class="inline-flex cursor-pointer items-center gap-1.5 rounded-lg border border-primary-soft bg-surface-raised px-3 py-2 text-sm font-medium text-primary transition-colors hover:bg-primary-soft"
862
+ class="inline-flex cursor-pointer items-center gap-1.5 rounded-[3px] border border-primary-soft-border bg-surface-raised px-3 py-2 font-mono text-sm font-medium text-primary hover:bg-primary-soft active:translate-y-px"
851
863
  >
852
864
  <Play size={15} fill="currentColor" stroke="none" />
853
865
  Run task
@@ -866,7 +878,7 @@
866
878
 
867
879
  /* Gentle fade for the maximize backdrop; disabled under reduced motion. */
868
880
  .console-backdrop {
869
- animation: console-backdrop-in 150ms ease-out;
881
+ animation: console-backdrop-in 150ms;
870
882
  }
871
883
  @keyframes console-backdrop-in {
872
884
  from {