@workflow/web-shared 4.1.0-beta.64 → 4.1.0-beta.65

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 (45) hide show
  1. package/dist/components/event-list-view.d.ts +5 -1
  2. package/dist/components/event-list-view.d.ts.map +1 -1
  3. package/dist/components/event-list-view.js +40 -19
  4. package/dist/components/event-list-view.js.map +1 -1
  5. package/dist/components/index.d.ts +3 -0
  6. package/dist/components/index.d.ts.map +1 -1
  7. package/dist/components/index.js +3 -0
  8. package/dist/components/index.js.map +1 -1
  9. package/dist/components/sidebar/entity-detail-panel.d.ts +3 -1
  10. package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
  11. package/dist/components/sidebar/entity-detail-panel.js +4 -14
  12. package/dist/components/sidebar/entity-detail-panel.js.map +1 -1
  13. package/dist/components/stream-viewer.d.ts +3 -1
  14. package/dist/components/stream-viewer.d.ts.map +1 -1
  15. package/dist/components/stream-viewer.js +23 -28
  16. package/dist/components/stream-viewer.js.map +1 -1
  17. package/dist/components/ui/decrypt-button.d.ts +15 -0
  18. package/dist/components/ui/decrypt-button.d.ts.map +1 -0
  19. package/dist/components/ui/decrypt-button.js +12 -0
  20. package/dist/components/ui/decrypt-button.js.map +1 -0
  21. package/dist/components/ui/load-more-button.d.ts +13 -0
  22. package/dist/components/ui/load-more-button.d.ts.map +1 -0
  23. package/dist/components/ui/load-more-button.js +12 -0
  24. package/dist/components/ui/load-more-button.js.map +1 -0
  25. package/dist/components/ui/menu-dropdown.d.ts.map +1 -1
  26. package/dist/components/ui/menu-dropdown.js +2 -6
  27. package/dist/components/ui/menu-dropdown.js.map +1 -1
  28. package/dist/components/ui/spinner.d.ts +9 -0
  29. package/dist/components/ui/spinner.d.ts.map +1 -0
  30. package/dist/components/ui/spinner.js +57 -0
  31. package/dist/components/ui/spinner.js.map +1 -0
  32. package/dist/components/workflow-trace-view.d.ts +3 -1
  33. package/dist/components/workflow-trace-view.d.ts.map +1 -1
  34. package/dist/components/workflow-trace-view.js +4 -3
  35. package/dist/components/workflow-trace-view.js.map +1 -1
  36. package/package.json +2 -2
  37. package/src/components/event-list-view.tsx +153 -113
  38. package/src/components/index.ts +3 -0
  39. package/src/components/sidebar/entity-detail-panel.tsx +9 -25
  40. package/src/components/stream-viewer.tsx +52 -63
  41. package/src/components/ui/decrypt-button.tsx +69 -0
  42. package/src/components/ui/load-more-button.tsx +38 -0
  43. package/src/components/ui/menu-dropdown.tsx +3 -6
  44. package/src/components/ui/spinner.tsx +76 -0
  45. package/src/components/workflow-trace-view.tsx +6 -19
@@ -0,0 +1,38 @@
1
+ 'use client';
2
+
3
+ import { Spinner } from './spinner';
4
+
5
+ const STYLES = `.wf-load-more{appearance:none;-webkit-appearance:none;border:none;display:inline-flex;align-items:center;justify-content:center;height:32px;padding:0 12px;border-radius:6px;font-size:13px;font-weight:500;line-height:20px;color:var(--ds-gray-1000);background:var(--ds-background-100);box-shadow:0 0 0 1px var(--ds-gray-400);cursor:pointer;white-space:nowrap;gap:6px;transition:background 150ms}.wf-load-more:hover{background:var(--ds-gray-alpha-200)}.wf-load-more:disabled{opacity:.6;cursor:default}.wf-load-more:disabled:hover{background:var(--ds-background-100)}`;
6
+
7
+ interface LoadMoreButtonProps {
8
+ loading?: boolean;
9
+ onClick?: () => void;
10
+ label?: string;
11
+ loadingLabel?: string;
12
+ }
13
+
14
+ /**
15
+ * A "Load more" button matching Geist's Button type="secondary" size="small"
16
+ * with a spinner prefix when loading.
17
+ */
18
+ export function LoadMoreButton({
19
+ loading = false,
20
+ onClick,
21
+ label = 'Load more',
22
+ loadingLabel = 'Loading...',
23
+ }: LoadMoreButtonProps) {
24
+ return (
25
+ <>
26
+ <style dangerouslySetInnerHTML={{ __html: STYLES }} />
27
+ <button
28
+ type="button"
29
+ onClick={onClick}
30
+ disabled={loading}
31
+ className="wf-load-more"
32
+ >
33
+ {loading && <Spinner size={14} />}
34
+ {loading ? loadingLabel : label}
35
+ </button>
36
+ </>
37
+ );
38
+ }
@@ -2,6 +2,8 @@
2
2
 
3
3
  import { useEffect, useRef, useState } from 'react';
4
4
 
5
+ const STYLES = `.wf-menu-btn{appearance:none;-webkit-appearance:none;border:none;display:inline-flex;align-items:center;justify-content:center;height:40px;padding:0 12px;border-radius:6px;font-size:14px;font-weight:500;line-height:20px;color:var(--ds-gray-1000);background:var(--ds-background-100);box-shadow:0 0 0 1px var(--ds-gray-400);cursor:pointer;white-space:nowrap;transition:background 150ms}.wf-menu-btn:hover{background:var(--ds-gray-alpha-200)}.wf-menu-item{appearance:none;-webkit-appearance:none;border:none;display:flex;align-items:center;width:100%;height:40px;padding:0 8px;border-radius:6px;font-size:14px;color:var(--ds-gray-1000);background:transparent;cursor:pointer;transition:background 150ms}.wf-menu-item:hover{background:var(--ds-gray-alpha-100)}`;
6
+
5
7
  export interface MenuDropdownOption<T extends string = string> {
6
8
  value: T;
7
9
  label: string;
@@ -40,12 +42,7 @@ export function MenuDropdown<T extends string = string>({
40
42
 
41
43
  return (
42
44
  <div ref={ref} style={{ position: 'relative', flexShrink: 0 }}>
43
- <style>{`
44
- .wf-menu-btn{appearance:none;-webkit-appearance:none;border:none;display:inline-flex;align-items:center;justify-content:center;height:40px;padding:0 12px;border-radius:6px;font-size:14px;font-weight:500;line-height:20px;color:var(--ds-gray-1000);background:var(--ds-background-100);box-shadow:0 0 0 1px var(--ds-gray-400);cursor:pointer;white-space:nowrap;transition:background 150ms}
45
- .wf-menu-btn:hover{background:var(--ds-gray-alpha-200)}
46
- .wf-menu-item{appearance:none;-webkit-appearance:none;border:none;display:flex;align-items:center;width:100%;height:40px;padding:0 8px;border-radius:6px;font-size:14px;color:var(--ds-gray-1000);background:transparent;cursor:pointer;transition:background 150ms}
47
- .wf-menu-item:hover{background:var(--ds-gray-alpha-100)}
48
- `}</style>
45
+ <style dangerouslySetInnerHTML={{ __html: STYLES }} />
49
46
 
50
47
  <button
51
48
  type="button"
@@ -0,0 +1,76 @@
1
+ const KEYFRAMES = `@keyframes wf-spinner-fade{0%{opacity:1}100%{opacity:.15}}`;
2
+
3
+ /**
4
+ * Spinner matching Geist's multi-line fade spinner.
5
+ * At size ≤12: 8 lines, ≤16: 10 lines, else: 12 lines.
6
+ */
7
+ export function Spinner({
8
+ size = 14,
9
+ color,
10
+ }: {
11
+ size?: number;
12
+ color?: string;
13
+ }) {
14
+ const config =
15
+ size <= 12
16
+ ? {
17
+ count: 8,
18
+ angle: 45,
19
+ delays: [-875, -750, -625, -500, -375, -250, -125, 0],
20
+ duration: 1000,
21
+ lineW: 3,
22
+ lineH: 1.5,
23
+ }
24
+ : size <= 16
25
+ ? {
26
+ count: 10,
27
+ angle: 36,
28
+ delays: [-900, -800, -700, -600, -500, -400, -300, -200, -100, 0],
29
+ duration: 1000,
30
+ lineW: 4,
31
+ lineH: 1.5,
32
+ }
33
+ : {
34
+ count: 12,
35
+ angle: 30,
36
+ delays: [
37
+ -1100, -1000, -900, -800, -700, -600, -500, -400, -300, -200,
38
+ -100, 0,
39
+ ],
40
+ duration: 1200,
41
+ lineW: size * 0.24,
42
+ lineH: size * 0.08,
43
+ };
44
+
45
+ return (
46
+ <span
47
+ style={{
48
+ display: 'inline-flex',
49
+ position: 'relative',
50
+ width: size,
51
+ height: size,
52
+ }}
53
+ >
54
+ <style dangerouslySetInnerHTML={{ __html: KEYFRAMES }} />
55
+ {config.delays.map((delay, i) => (
56
+ <span
57
+ key={delay}
58
+ style={{
59
+ position: 'absolute',
60
+ left: '50%',
61
+ top: '50%',
62
+ width: config.lineW,
63
+ height: config.lineH,
64
+ marginLeft: -config.lineW / 2,
65
+ marginTop: -config.lineH / 2,
66
+ borderRadius: 1,
67
+ backgroundColor: color ?? 'var(--ds-gray-700)',
68
+ transform: `rotate(${i * config.angle}deg) translate(${size * 0.36}px)`,
69
+ animation: `wf-spinner-fade ${config.duration}ms linear infinite`,
70
+ animationDelay: `${delay}ms`,
71
+ }}
72
+ />
73
+ ))}
74
+ </span>
75
+ );
76
+ }
@@ -31,6 +31,7 @@ import {
31
31
  } from './trace-viewer';
32
32
  import type { Span } from './trace-viewer/types';
33
33
  import { Skeleton } from './ui/skeleton';
34
+ import { Spinner } from './ui/spinner';
34
35
  import {
35
36
  getCustomSpanClassName,
36
37
  getCustomSpanEventClassName,
@@ -719,25 +720,7 @@ function TraceViewerFooter({
719
720
  className="flex items-center justify-center gap-2 py-3 text-xs"
720
721
  style={style}
721
722
  >
722
- <svg
723
- className="h-3.5 w-3.5 animate-spin"
724
- viewBox="0 0 24 24"
725
- fill="none"
726
- >
727
- <circle
728
- className="opacity-25"
729
- cx="12"
730
- cy="12"
731
- r="10"
732
- stroke="currentColor"
733
- strokeWidth="4"
734
- />
735
- <path
736
- className="opacity-75"
737
- fill="currentColor"
738
- d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
739
- />
740
- </svg>
723
+ <Spinner size={14} />
741
724
  Loading more events…
742
725
  </div>
743
726
  );
@@ -785,6 +768,7 @@ export const WorkflowTraceViewer = ({
785
768
  isLoadingMoreSpans = false,
786
769
  encryptionKey,
787
770
  onDecrypt,
771
+ isDecrypting = false,
788
772
  }: {
789
773
  run: WorkflowRun;
790
774
  events: Event[];
@@ -823,6 +807,8 @@ export const WorkflowTraceViewer = ({
823
807
  encryptionKey?: Uint8Array;
824
808
  /** Callback to initiate decryption of encrypted run data */
825
809
  onDecrypt?: () => void;
810
+ /** Whether the encryption key is currently being fetched */
811
+ isDecrypting?: boolean;
826
812
  }) => {
827
813
  const [selectedSpan, setSelectedSpan] = useState<SelectedSpanInfo | null>(
828
814
  null
@@ -1156,6 +1142,7 @@ export const WorkflowTraceViewer = ({
1156
1142
  onResolveHook={onResolveHook}
1157
1143
  encryptionKey={encryptionKey}
1158
1144
  onDecrypt={onDecrypt}
1145
+ isDecrypting={isDecrypting}
1159
1146
  selectedSpan={selectedSpan}
1160
1147
  />
1161
1148
  </ErrorBoundary>