@workflow/web-shared 5.0.0-beta.1 → 5.0.0-beta.3

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 (194) hide show
  1. package/README.md +1 -1
  2. package/dist/components/error-boundary.js.map +1 -1
  3. package/dist/components/event-list-view.d.ts +3 -1
  4. package/dist/components/event-list-view.d.ts.map +1 -1
  5. package/dist/components/event-list-view.js +53 -53
  6. package/dist/components/event-list-view.js.map +1 -1
  7. package/dist/components/hook-actions.js.map +1 -1
  8. package/dist/components/index.d.ts +3 -1
  9. package/dist/components/index.d.ts.map +1 -1
  10. package/dist/components/index.js +3 -1
  11. package/dist/components/index.js.map +1 -1
  12. package/dist/components/new-trace-viewer/components/copy-button.d.ts +9 -0
  13. package/dist/components/new-trace-viewer/components/copy-button.d.ts.map +1 -0
  14. package/dist/components/new-trace-viewer/components/copy-button.js +27 -0
  15. package/dist/components/new-trace-viewer/components/copy-button.js.map +1 -0
  16. package/dist/components/new-trace-viewer/components/event-list.d.ts +8 -0
  17. package/dist/components/new-trace-viewer/components/event-list.d.ts.map +1 -0
  18. package/dist/components/new-trace-viewer/components/event-list.js +35 -0
  19. package/dist/components/new-trace-viewer/components/event-list.js.map +1 -0
  20. package/dist/components/new-trace-viewer/components/split-pane.d.ts +14 -0
  21. package/dist/components/new-trace-viewer/components/split-pane.d.ts.map +1 -0
  22. package/dist/components/new-trace-viewer/components/split-pane.js +100 -0
  23. package/dist/components/new-trace-viewer/components/split-pane.js.map +1 -0
  24. package/dist/components/new-trace-viewer/components/timeline.d.ts +32 -0
  25. package/dist/components/new-trace-viewer/components/timeline.d.ts.map +1 -0
  26. package/dist/components/new-trace-viewer/components/timeline.js +134 -0
  27. package/dist/components/new-trace-viewer/components/timeline.js.map +1 -0
  28. package/dist/components/new-trace-viewer/context.d.ts +15 -0
  29. package/dist/components/new-trace-viewer/context.d.ts.map +1 -0
  30. package/dist/components/new-trace-viewer/context.js +49 -0
  31. package/dist/components/new-trace-viewer/context.js.map +1 -0
  32. package/dist/components/new-trace-viewer/detail-panel.d.ts +10 -0
  33. package/dist/components/new-trace-viewer/detail-panel.d.ts.map +1 -0
  34. package/dist/components/new-trace-viewer/detail-panel.js +11 -0
  35. package/dist/components/new-trace-viewer/detail-panel.js.map +1 -0
  36. package/dist/components/new-trace-viewer/icons.d.ts +6 -0
  37. package/dist/components/new-trace-viewer/icons.d.ts.map +1 -0
  38. package/dist/components/new-trace-viewer/icons.js +15 -0
  39. package/dist/components/new-trace-viewer/icons.js.map +1 -0
  40. package/dist/components/new-trace-viewer/trace-viewer.d.ts +8 -0
  41. package/dist/components/new-trace-viewer/trace-viewer.d.ts.map +1 -0
  42. package/dist/components/new-trace-viewer/trace-viewer.js +329 -0
  43. package/dist/components/new-trace-viewer/trace-viewer.js.map +1 -0
  44. package/dist/components/new-trace-viewer/types.d.ts +1 -0
  45. package/dist/components/new-trace-viewer/types.d.ts.map +1 -0
  46. package/dist/components/new-trace-viewer/types.js +2 -0
  47. package/dist/components/new-trace-viewer/types.js.map +1 -0
  48. package/dist/components/new-trace-viewer/util/constants.d.ts +1 -0
  49. package/dist/components/new-trace-viewer/util/constants.d.ts.map +1 -0
  50. package/dist/components/new-trace-viewer/util/constants.js +2 -0
  51. package/dist/components/new-trace-viewer/util/constants.js.map +1 -0
  52. package/dist/components/new-trace-viewer/utils.d.ts +47 -0
  53. package/dist/components/new-trace-viewer/utils.d.ts.map +1 -0
  54. package/dist/components/new-trace-viewer/utils.js +358 -0
  55. package/dist/components/new-trace-viewer/utils.js.map +1 -0
  56. package/dist/components/run-trace-view.d.ts +2 -1
  57. package/dist/components/run-trace-view.d.ts.map +1 -1
  58. package/dist/components/run-trace-view.js +2 -2
  59. package/dist/components/run-trace-view.js.map +1 -1
  60. package/dist/components/sidebar/attribute-panel.d.ts +7 -1
  61. package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
  62. package/dist/components/sidebar/attribute-panel.js +76 -47
  63. package/dist/components/sidebar/attribute-panel.js.map +1 -1
  64. package/dist/components/sidebar/conversation-view.js.map +1 -1
  65. package/dist/components/sidebar/copyable-data-block.js +1 -1
  66. package/dist/components/sidebar/copyable-data-block.js.map +1 -1
  67. package/dist/components/sidebar/detail-card.d.ts.map +1 -1
  68. package/dist/components/sidebar/detail-card.js +1 -1
  69. package/dist/components/sidebar/detail-card.js.map +1 -1
  70. package/dist/components/sidebar/entity-detail-panel.d.ts +3 -1
  71. package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
  72. package/dist/components/sidebar/entity-detail-panel.js +16 -36
  73. package/dist/components/sidebar/entity-detail-panel.js.map +1 -1
  74. package/dist/components/sidebar/events-list.d.ts.map +1 -1
  75. package/dist/components/sidebar/events-list.js +2 -2
  76. package/dist/components/sidebar/events-list.js.map +1 -1
  77. package/dist/components/sidebar/resolve-hook-modal.js.map +1 -1
  78. package/dist/components/sidebar/sidebar-data-context.d.ts +29 -0
  79. package/dist/components/sidebar/sidebar-data-context.d.ts.map +1 -0
  80. package/dist/components/sidebar/sidebar-data-context.js +19 -0
  81. package/dist/components/sidebar/sidebar-data-context.js.map +1 -0
  82. package/dist/components/stream-viewer.d.ts +1 -1
  83. package/dist/components/stream-viewer.d.ts.map +1 -1
  84. package/dist/components/stream-viewer.js +2 -26
  85. package/dist/components/stream-viewer.js.map +1 -1
  86. package/dist/components/trace-viewer/components/map.js.map +1 -1
  87. package/dist/components/trace-viewer/components/markers.js.map +1 -1
  88. package/dist/components/trace-viewer/components/node.js.map +1 -1
  89. package/dist/components/trace-viewer/components/search-input.js.map +1 -1
  90. package/dist/components/trace-viewer/components/search.js.map +1 -1
  91. package/dist/components/trace-viewer/components/span-content.js.map +1 -1
  92. package/dist/components/trace-viewer/components/span-detail-panel.js.map +1 -1
  93. package/dist/components/trace-viewer/components/span-segments.js.map +1 -1
  94. package/dist/components/trace-viewer/components/span-strategies.js.map +1 -1
  95. package/dist/components/trace-viewer/components/ui.js.map +1 -1
  96. package/dist/components/trace-viewer/components/zoom-button.js.map +1 -1
  97. package/dist/components/trace-viewer/components/zoom-icons.js.map +1 -1
  98. package/dist/components/trace-viewer/context.js.map +1 -1
  99. package/dist/components/trace-viewer/index.js.map +1 -1
  100. package/dist/components/trace-viewer/trace-viewer.js.map +1 -1
  101. package/dist/components/trace-viewer/types.js.map +1 -1
  102. package/dist/components/trace-viewer/util/constants.js.map +1 -1
  103. package/dist/components/trace-viewer/util/scrollbar-width.js.map +1 -1
  104. package/dist/components/trace-viewer/util/timing.js.map +1 -1
  105. package/dist/components/trace-viewer/util/tree.js.map +1 -1
  106. package/dist/components/trace-viewer/util/use-immediate-style.js.map +1 -1
  107. package/dist/components/trace-viewer/util/use-streaming-spans.js.map +1 -1
  108. package/dist/components/trace-viewer/util/use-trackpad-zoom.js.map +1 -1
  109. package/dist/components/trace-viewer/worker.js.map +1 -1
  110. package/dist/components/trace-viewer-new.d.ts +10 -0
  111. package/dist/components/trace-viewer-new.d.ts.map +1 -0
  112. package/dist/components/trace-viewer-new.js +23 -0
  113. package/dist/components/trace-viewer-new.js.map +1 -0
  114. package/dist/components/ui/alert.js.map +1 -1
  115. package/dist/components/ui/card.js.map +1 -1
  116. package/dist/components/ui/data-inspector.d.ts +38 -1
  117. package/dist/components/ui/data-inspector.d.ts.map +1 -1
  118. package/dist/components/ui/data-inspector.js +211 -21
  119. package/dist/components/ui/data-inspector.js.map +1 -1
  120. package/dist/components/ui/decrypt-button.js.map +1 -1
  121. package/dist/components/ui/error-card.js.map +1 -1
  122. package/dist/components/ui/error-stack-block.js.map +1 -1
  123. package/dist/components/ui/inspector-theme.d.ts +9 -10
  124. package/dist/components/ui/inspector-theme.d.ts.map +1 -1
  125. package/dist/components/ui/inspector-theme.js +38 -71
  126. package/dist/components/ui/inspector-theme.js.map +1 -1
  127. package/dist/components/ui/load-more-button.js.map +1 -1
  128. package/dist/components/ui/menu-dropdown.js.map +1 -1
  129. package/dist/components/ui/skeleton.js.map +1 -1
  130. package/dist/components/ui/spinner.js.map +1 -1
  131. package/dist/components/ui/timestamp-tooltip.js.map +1 -1
  132. package/dist/components/workflow-trace-view.d.ts +3 -1
  133. package/dist/components/workflow-trace-view.d.ts.map +1 -1
  134. package/dist/components/workflow-trace-view.js +9 -4
  135. package/dist/components/workflow-trace-view.js.map +1 -1
  136. package/dist/components/workflow-traces/event-colors.js.map +1 -1
  137. package/dist/components/workflow-traces/trace-colors.js.map +1 -1
  138. package/dist/components/workflow-traces/trace-span-construction.js +1 -1
  139. package/dist/components/workflow-traces/trace-span-construction.js.map +1 -1
  140. package/dist/components/workflow-traces/trace-time-utils.js.map +1 -1
  141. package/dist/hooks/use-dark-mode.js.map +1 -1
  142. package/dist/index.d.ts +2 -1
  143. package/dist/index.d.ts.map +1 -1
  144. package/dist/index.js +1 -1
  145. package/dist/index.js.map +1 -1
  146. package/dist/lib/event-analysis.js.map +1 -1
  147. package/dist/lib/event-materialization.js.map +1 -1
  148. package/dist/lib/hydration.d.ts +8 -1
  149. package/dist/lib/hydration.d.ts.map +1 -1
  150. package/dist/lib/hydration.js +32 -2
  151. package/dist/lib/hydration.js.map +1 -1
  152. package/dist/lib/stream-display.d.ts +18 -0
  153. package/dist/lib/stream-display.d.ts.map +1 -0
  154. package/dist/lib/stream-display.js +30 -0
  155. package/dist/lib/stream-display.js.map +1 -0
  156. package/dist/lib/toast.js.map +1 -1
  157. package/dist/lib/trace-builder.js.map +1 -1
  158. package/dist/lib/utils.d.ts +7 -9
  159. package/dist/lib/utils.d.ts.map +1 -1
  160. package/dist/lib/utils.js +17 -22
  161. package/dist/lib/utils.js.map +1 -1
  162. package/dist/styles.css +470 -1
  163. package/package.json +4 -3
  164. package/src/components/event-list-view.tsx +181 -173
  165. package/src/components/index.ts +11 -1
  166. package/src/components/new-trace-viewer/components/copy-button.tsx +68 -0
  167. package/src/components/new-trace-viewer/components/event-list.tsx +109 -0
  168. package/src/components/new-trace-viewer/components/split-pane.tsx +186 -0
  169. package/src/components/new-trace-viewer/components/timeline.tsx +378 -0
  170. package/src/components/new-trace-viewer/context.tsx +94 -0
  171. package/src/components/new-trace-viewer/detail-panel.tsx +57 -0
  172. package/src/components/new-trace-viewer/icons.tsx +79 -0
  173. package/src/components/new-trace-viewer/trace-viewer.tsx +585 -0
  174. package/src/components/new-trace-viewer/types.ts +0 -0
  175. package/src/components/new-trace-viewer/util/constants.ts +0 -0
  176. package/src/components/new-trace-viewer/utils.ts +524 -0
  177. package/src/components/run-trace-view.tsx +3 -0
  178. package/src/components/sidebar/attribute-panel.tsx +200 -138
  179. package/src/components/sidebar/copyable-data-block.tsx +1 -1
  180. package/src/components/sidebar/detail-card.tsx +1 -19
  181. package/src/components/sidebar/entity-detail-panel.tsx +91 -147
  182. package/src/components/sidebar/events-list.tsx +9 -6
  183. package/src/components/sidebar/sidebar-data-context.tsx +62 -0
  184. package/src/components/stream-viewer.tsx +18 -40
  185. package/src/components/trace-viewer-new.tsx +49 -0
  186. package/src/components/ui/data-inspector.tsx +436 -31
  187. package/src/components/ui/inspector-theme.ts +39 -81
  188. package/src/components/workflow-trace-view.tsx +12 -2
  189. package/src/components/workflow-traces/trace-span-construction.ts +1 -1
  190. package/src/index.ts +2 -0
  191. package/src/lib/hydration.ts +37 -0
  192. package/src/lib/stream-display.ts +48 -0
  193. package/src/lib/utils.ts +18 -23
  194. package/src/styles.css +470 -1
@@ -5,8 +5,7 @@ import clsx from 'clsx';
5
5
  import { Send, Zap } from 'lucide-react';
6
6
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
7
7
  import { useToast } from '../../lib/toast';
8
- import { isEncryptedMarker } from '../../lib/hydration';
9
- import { DecryptButton } from '../ui/decrypt-button';
8
+ import { DecryptClickContext } from '../ui/data-inspector';
10
9
  import { AttributePanel } from './attribute-panel';
11
10
  import { EventsList } from './events-list';
12
11
  import { ResolveHookModal } from './resolve-hook-modal';
@@ -56,6 +55,7 @@ export interface SelectedSpanInfo {
56
55
  export function EntityDetailPanel({
57
56
  run,
58
57
  onStreamClick,
58
+ onRunClick,
59
59
  spanDetailData,
60
60
  spanDetailError,
61
61
  spanDetailLoading,
@@ -71,6 +71,8 @@ export function EntityDetailPanel({
71
71
  run: WorkflowRun;
72
72
  /** Callback when a stream reference is clicked */
73
73
  onStreamClick?: (streamId: string) => void;
74
+ /** Callback when a run reference is clicked */
75
+ onRunClick?: (runId: string) => void;
74
76
  /** Pre-fetched span detail data for the selected span. */
75
77
  spanDetailData: WorkflowRun | Step | Hook | Event | null;
76
78
  /** Error from external span detail fetch. */
@@ -209,17 +211,6 @@ export function EntityDetailPanel({
209
211
  const error = spanDetailError ?? undefined;
210
212
  const loading = spanDetailLoading ?? false;
211
213
 
212
- const hasEncryptedFields = useMemo(() => {
213
- if (!spanDetailData) return false;
214
- const d = spanDetailData as Record<string, unknown>;
215
- return (
216
- isEncryptedMarker(d.input) ||
217
- isEncryptedMarker(d.output) ||
218
- isEncryptedMarker(d.error) ||
219
- isEncryptedMarker(d.metadata)
220
- );
221
- }, [spanDetailData]);
222
-
223
214
  // Get the hook token for resolving (prefer fetched data, then hooks array fallback)
224
215
  const hookToken = useMemo(() => {
225
216
  if (resource !== 'hook' || !resourceId) return undefined;
@@ -347,155 +338,108 @@ export function EntityDetailPanel({
347
338
  return null;
348
339
  }
349
340
 
350
- const resourceLabel = resource.charAt(0).toUpperCase() + resource.slice(1);
351
341
  const hasPendingActions =
352
342
  (resource === 'sleep' && canWakeUp) ||
353
343
  (resource === 'hook' && canResolveHook);
354
- const runStateLabel = run.completedAt ? 'Completed' : 'Live';
355
344
 
356
345
  return (
357
346
  <div className="flex h-full flex-col">
358
- <div
359
- className="border-b px-3 py-3"
360
- style={{ borderColor: 'var(--ds-gray-200)' }}
347
+ <DecryptClickContext.Provider
348
+ value={onDecrypt ? { onDecrypt, isDecrypting } : undefined}
361
349
  >
362
- <div className="flex items-start justify-between gap-2">
363
- <div className="min-w-0">
364
- <div className="flex items-center gap-2">
365
- <span
366
- className="inline-flex items-center rounded-full border px-2 py-0.5 text-[13px] font-medium"
367
- style={{
368
- borderColor: 'var(--ds-gray-300)',
369
- color: 'var(--ds-gray-900)',
370
- backgroundColor: 'var(--ds-background-100)',
371
- }}
372
- >
373
- {resourceLabel}
374
- </span>
375
- <span
376
- className="text-[13px]"
377
- style={{
378
- color: run.completedAt
379
- ? 'var(--ds-gray-700)'
380
- : 'var(--ds-green-800)',
381
- }}
350
+ <div className="flex-1 overflow-y-auto px-3 pt-3 pb-8">
351
+ {hasPendingActions && (
352
+ <div
353
+ className="mb-4 rounded-lg border p-2"
354
+ style={{
355
+ borderColor: 'var(--ds-gray-300)',
356
+ backgroundColor: 'var(--ds-gray-100)',
357
+ }}
358
+ >
359
+ <p
360
+ className="mb-2 px-1 text-[13px] font-medium uppercase tracking-wide"
361
+ style={{ color: 'var(--ds-gray-700)' }}
382
362
  >
383
- {runStateLabel}
384
- </span>
363
+ Actions
364
+ </p>
365
+ <div className="flex flex-col gap-2">
366
+ {/* Wake up button for pending sleep calls */}
367
+ {resource === 'sleep' && canWakeUp && (
368
+ <button
369
+ type="button"
370
+ onClick={handleWakeUp}
371
+ disabled={stoppingSleep}
372
+ className={clsx(
373
+ 'flex items-center justify-center gap-2 rounded-md px-3 py-2 text-sm font-medium',
374
+ 'disabled:opacity-50 disabled:cursor-not-allowed transition-colors',
375
+ stoppingSleep
376
+ ? 'opacity-50 cursor-not-allowed'
377
+ : 'cursor-pointer'
378
+ )}
379
+ style={{
380
+ background: 'var(--ds-amber-200)',
381
+ color: 'var(--ds-amber-900)',
382
+ }}
383
+ >
384
+ <Zap className="h-4 w-4" />
385
+ {stoppingSleep ? 'Waking up...' : 'Wake Up Sleep'}
386
+ </button>
387
+ )}
388
+
389
+ {/* Resolve hook button for pending hooks */}
390
+ {resource === 'hook' && canResolveHook && (
391
+ <button
392
+ type="button"
393
+ onClick={() => setShowResolveHookModal(true)}
394
+ disabled={resolvingHook}
395
+ className={clsx(
396
+ 'flex items-center justify-center gap-2 rounded-md px-3 py-2 text-sm font-medium',
397
+ 'disabled:opacity-50 disabled:cursor-not-allowed transition-colors',
398
+ resolvingHook
399
+ ? 'opacity-50 cursor-not-allowed'
400
+ : 'cursor-pointer'
401
+ )}
402
+ style={{
403
+ background: 'var(--ds-gray-1000)',
404
+ color: 'var(--ds-background-100)',
405
+ }}
406
+ >
407
+ <Send className="h-4 w-4" />
408
+ Resolve Hook
409
+ </button>
410
+ )}
411
+ </div>
385
412
  </div>
386
- <p
387
- className="mt-1 truncate font-mono text-[13px]"
388
- style={{ color: 'var(--ds-gray-700)' }}
389
- title={resourceId}
390
- >
391
- {resourceId}
392
- </p>
393
- </div>
394
- {(hasEncryptedFields || encryptionKey) && onDecrypt && (
395
- <DecryptButton
396
- decrypted={!!encryptionKey}
397
- loading={isDecrypting}
398
- onClick={onDecrypt}
399
- />
400
413
  )}
401
- </div>
402
- </div>
403
-
404
- <div className="flex-1 overflow-y-auto px-3 pt-3 pb-8">
405
- {hasPendingActions && (
406
- <div
407
- className="mb-4 rounded-lg border p-2"
408
- style={{
409
- borderColor: 'var(--ds-gray-300)',
410
- backgroundColor: 'var(--ds-gray-100)',
411
- }}
412
- >
413
- <p
414
- className="mb-2 px-1 text-[13px] font-medium uppercase tracking-wide"
415
- style={{ color: 'var(--ds-gray-700)' }}
416
- >
417
- Actions
418
- </p>
419
- <div className="flex flex-col gap-2">
420
- {/* Wake up button for pending sleep calls */}
421
- {resource === 'sleep' && canWakeUp && (
422
- <button
423
- type="button"
424
- onClick={handleWakeUp}
425
- disabled={stoppingSleep}
426
- className={clsx(
427
- 'flex items-center justify-center gap-2 rounded-md px-3 py-2 text-sm font-medium',
428
- 'disabled:opacity-50 disabled:cursor-not-allowed transition-colors',
429
- stoppingSleep
430
- ? 'opacity-50 cursor-not-allowed'
431
- : 'cursor-pointer'
432
- )}
433
- style={{
434
- background: 'var(--ds-amber-200)',
435
- color: 'var(--ds-amber-900)',
436
- }}
437
- >
438
- <Zap className="h-4 w-4" />
439
- {stoppingSleep ? 'Waking up...' : 'Wake Up Sleep'}
440
- </button>
441
- )}
442
-
443
- {/* Resolve hook button for pending hooks */}
444
- {resource === 'hook' && canResolveHook && (
445
- <button
446
- type="button"
447
- onClick={() => setShowResolveHookModal(true)}
448
- disabled={resolvingHook}
449
- className={clsx(
450
- 'flex items-center justify-center gap-2 rounded-md px-3 py-2 text-sm font-medium',
451
- 'disabled:opacity-50 disabled:cursor-not-allowed transition-colors',
452
- resolvingHook
453
- ? 'opacity-50 cursor-not-allowed'
454
- : 'cursor-pointer'
455
- )}
456
- style={{
457
- background: 'var(--ds-gray-1000)',
458
- color: 'var(--ds-background-100)',
459
- }}
460
- >
461
- <Send className="h-4 w-4" />
462
- Resolve Hook
463
- </button>
464
- )}
465
- </div>
466
- </div>
467
- )}
468
414
 
469
- <div className="space-y-4">
470
- <section>
471
- <h3
472
- className="mb-2 text-[13px] font-medium uppercase tracking-wide"
473
- style={{ color: 'var(--ds-gray-700)' }}
474
- >
475
- Details
476
- </h3>
477
- <AttributePanel
478
- data={displayData}
479
- moduleSpecifier={moduleSpecifier}
480
- expiredAt={run.expiredAt}
481
- isLoading={loading}
482
- error={error ?? undefined}
483
- onStreamClick={onStreamClick}
484
- resource={resource}
485
- />
486
- </section>
487
-
488
- {resource !== 'run' && rawEvents && (
415
+ <div className="space-y-4">
489
416
  <section>
490
- <EventsList
491
- events={rawEvents}
492
- onLoadEventData={onLoadEventData}
493
- encryptionKey={encryptionKey}
417
+ <AttributePanel
418
+ data={displayData}
419
+ moduleSpecifier={moduleSpecifier}
420
+ expiredAt={run.expiredAt}
421
+ isLoading={loading}
422
+ error={error ?? undefined}
423
+ onStreamClick={onStreamClick}
424
+ onRunClick={onRunClick}
425
+ onDecrypt={onDecrypt}
426
+ isDecrypting={isDecrypting}
427
+ resource={resource}
494
428
  />
495
429
  </section>
496
- )}
430
+
431
+ {resource !== 'run' && rawEvents && (
432
+ <section>
433
+ <EventsList
434
+ events={rawEvents}
435
+ onLoadEventData={onLoadEventData}
436
+ encryptionKey={encryptionKey}
437
+ />
438
+ </section>
439
+ )}
440
+ </div>
497
441
  </div>
498
- </div>
442
+ </DecryptClickContext.Provider>
499
443
 
500
444
  {/* Resolve Hook Modal */}
501
445
  <ResolveHookModal
@@ -142,8 +142,11 @@ function EventItem({
142
142
  className="flex min-h-[32px] items-center justify-between gap-4 px-2.5 py-1.5"
143
143
  style={{ borderColor: 'var(--ds-gray-300)' }}
144
144
  >
145
- <span className="text-[14px]" style={{ color: 'var(--ds-gray-700)' }}>
146
- eventId
145
+ <span
146
+ className="text-[13px] font-medium"
147
+ style={{ color: 'var(--ds-gray-700)' }}
148
+ >
149
+ Event ID
147
150
  </span>
148
151
  <span
149
152
  className="max-w-[70%] truncate text-right text-[13px] font-mono"
@@ -159,10 +162,10 @@ function EventItem({
159
162
  style={{ borderColor: 'var(--ds-gray-300)' }}
160
163
  >
161
164
  <span
162
- className="text-[14px]"
165
+ className="text-[13px] font-medium"
163
166
  style={{ color: 'var(--ds-gray-700)' }}
164
167
  >
165
- correlationId
168
+ Correlation ID
166
169
  </span>
167
170
  <span
168
171
  className="max-w-[70%] truncate text-right text-[13px] font-mono"
@@ -311,10 +314,10 @@ export function EventsList({
311
314
  return (
312
315
  <div className="mt-2" style={{ color: 'var(--ds-gray-1000)' }}>
313
316
  <h3
314
- className="text-heading-16 font-medium mt-4 mb-2"
317
+ className="text-label-14 font-medium mt-4 mb-2"
315
318
  style={{ color: 'var(--ds-gray-1000)' }}
316
319
  >
317
- Events {!isLoading && `(${sortedEvents.length})`}
320
+ Events
318
321
  </h3>
319
322
  {isLoading ? (
320
323
  <div className="flex flex-col gap-4">
@@ -0,0 +1,62 @@
1
+ 'use client';
2
+
3
+ import { createContext, useContext, type ReactNode } from 'react';
4
+ import type { Event, Hook, Step, WorkflowRun } from '@workflow/world';
5
+ import type { SpanSelectionInfo } from './entity-detail-panel';
6
+
7
+ export interface SidebarDataContextValue {
8
+ run: WorkflowRun;
9
+ events: Event[];
10
+ spanDetailData: WorkflowRun | Step | Hook | Event | null;
11
+ spanDetailError?: Error | null;
12
+ spanDetailLoading?: boolean;
13
+ onSpanSelect: (info: SpanSelectionInfo) => void;
14
+ onStreamClick?: (streamId: string) => void;
15
+ onRunClick?: (runId: string) => void;
16
+ onWakeUpSleep?: (
17
+ runId: string,
18
+ correlationId: string
19
+ ) => Promise<{ stoppedCount: number }>;
20
+ onLoadEventData?: (
21
+ correlationId: string,
22
+ eventId: string
23
+ ) => Promise<unknown | null>;
24
+ onResolveHook?: (
25
+ hookToken: string,
26
+ payload: unknown,
27
+ hook?: Hook
28
+ ) => Promise<void>;
29
+ encryptionKey?: Uint8Array;
30
+ onDecrypt?: () => void;
31
+ isDecrypting?: boolean;
32
+ hasEncryptedData?: boolean;
33
+ }
34
+
35
+ const SidebarDataContext = createContext<SidebarDataContextValue | null>(null);
36
+ SidebarDataContext.displayName = 'SidebarDataContext';
37
+
38
+ export function SidebarDataProvider({
39
+ value,
40
+ children,
41
+ }: {
42
+ value: SidebarDataContextValue;
43
+ children: ReactNode;
44
+ }) {
45
+ return (
46
+ <SidebarDataContext.Provider value={value}>
47
+ {children}
48
+ </SidebarDataContext.Provider>
49
+ );
50
+ }
51
+
52
+ export function useSidebarData(): SidebarDataContextValue {
53
+ const ctx = useContext(SidebarDataContext);
54
+ if (!ctx) {
55
+ throw new Error('useSidebarData must be used within a SidebarDataProvider');
56
+ }
57
+ return ctx;
58
+ }
59
+
60
+ export function useSidebarDataOptional(): SidebarDataContextValue | null {
61
+ return useContext(SidebarDataContext);
62
+ }
@@ -5,37 +5,13 @@ import { Virtuoso, type VirtuosoHandle } from 'react-virtuoso';
5
5
  import { DataInspector } from './ui/data-inspector';
6
6
  import { Skeleton } from './ui/skeleton';
7
7
 
8
- // ──────────────────────────────────────────────────────────────────────────
9
- // Helpers
10
- // ──────────────────────────────────────────────────────────────────────────
11
-
12
- function deserializeChunkText(text: string): string {
13
- try {
14
- const parsed = JSON.parse(text);
15
- if (typeof parsed === 'string') {
16
- return parsed;
17
- }
18
- return JSON.stringify(parsed, null, 2);
19
- } catch {
20
- return text;
21
- }
22
- }
23
-
24
- function parseChunkData(text: string): unknown {
25
- try {
26
- return JSON.parse(text);
27
- } catch {
28
- return text;
29
- }
30
- }
31
-
32
8
  // ──────────────────────────────────────────────────────────────────────────
33
9
  // Types
34
10
  // ──────────────────────────────────────────────────────────────────────────
35
11
 
36
12
  export interface StreamChunk {
37
13
  id: number;
38
- text: string;
14
+ value: unknown;
39
15
  }
40
16
 
41
17
  type Chunk = StreamChunk;
@@ -62,8 +38,6 @@ const ChunkRow = React.memo(function ChunkRow({
62
38
  chunk: Chunk;
63
39
  index: number;
64
40
  }) {
65
- const parsed = parseChunkData(chunk.text);
66
-
67
41
  return (
68
42
  <div
69
43
  className="text-[11px] rounded-md border p-3"
@@ -72,22 +46,26 @@ const ChunkRow = React.memo(function ChunkRow({
72
46
  backgroundColor: 'var(--ds-gray-100)',
73
47
  }}
74
48
  >
75
- <span
76
- className="select-none mr-2"
77
- style={{ color: 'var(--ds-gray-500)' }}
78
- >
79
- [{index}]
80
- </span>
81
- {typeof parsed === 'string' ? (
49
+ <div className="flex items-start gap-2">
82
50
  <span
83
- className="whitespace-pre-wrap break-words"
84
- style={{ color: 'var(--ds-gray-1000)' }}
51
+ className="select-none pt-px"
52
+ style={{ color: 'var(--ds-gray-500)' }}
85
53
  >
86
- {deserializeChunkText(parsed)}
54
+ [{index}]
87
55
  </span>
88
- ) : (
89
- <DataInspector data={parsed} expandLevel={1} />
90
- )}
56
+ <div className="min-w-0 flex-1">
57
+ {typeof chunk.value === 'string' ? (
58
+ <span
59
+ className="whitespace-pre-wrap break-words"
60
+ style={{ color: 'var(--ds-gray-1000)' }}
61
+ >
62
+ {chunk.value}
63
+ </span>
64
+ ) : (
65
+ <DataInspector data={chunk.value} expandLevel={1} />
66
+ )}
67
+ </div>
68
+ </div>
91
69
  </div>
92
70
  );
93
71
  });
@@ -0,0 +1,49 @@
1
+ import { useMemo } from 'react';
2
+ import type { WorkflowRun } from '@workflow/core/runtime';
3
+ import type { Event } from '@workflow/world';
4
+ import { buildTrace, type TraceWithMeta } from '../lib/trace-builder';
5
+ import { NewTraceViewer as NewTraceViewerComponent } from './new-trace-viewer/trace-viewer';
6
+ import {
7
+ SidebarDataProvider,
8
+ type SidebarDataContextValue,
9
+ } from './sidebar/sidebar-data-context';
10
+ import type { Trace } from './trace-viewer/types';
11
+
12
+ const NewTraceViewer = ({
13
+ run,
14
+ events,
15
+ sidebarData,
16
+ }: {
17
+ run: WorkflowRun;
18
+ events: Event[];
19
+ sidebarData: SidebarDataContextValue;
20
+ }) => {
21
+ // Build trace only when actual data changes — no timer-driven rebuilds.
22
+ // Active span widths are animated imperatively by useLiveTick at 60fps.
23
+ const traceWithMeta: TraceWithMeta | undefined = useMemo(() => {
24
+ if (!run?.runId) {
25
+ return undefined;
26
+ }
27
+ return buildTrace(run, events, new Date());
28
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- `new Date()` is intentionally not a dep; useLiveTick handles live growth
29
+ }, [run, events]);
30
+ const trace = traceWithMeta;
31
+
32
+ if (!trace) {
33
+ return (
34
+ <div className="relative w-full h-full flex items-center justify-center">
35
+ <div className="text-gray-500 text-sm">Loading trace…</div>
36
+ </div>
37
+ );
38
+ }
39
+
40
+ return (
41
+ <SidebarDataProvider value={sidebarData}>
42
+ <div className="relative w-full h-full flex">
43
+ <NewTraceViewerComponent trace={trace as Trace} />
44
+ </div>
45
+ </SidebarDataProvider>
46
+ );
47
+ };
48
+
49
+ export { NewTraceViewer };