@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,17 +5,22 @@ import type { Event, Hook, Step, WorkflowRun } from '@workflow/world';
5
5
  import type { ModelMessage } from 'ai';
6
6
  import { Lock } from 'lucide-react';
7
7
  import type { KeyboardEvent, ReactNode } from 'react';
8
- import { useCallback, useMemo, useState } from 'react';
8
+ import { useCallback, useContext, useMemo, useState } from 'react';
9
9
  import { isEncryptedMarker, isExpiredMarker } from '../../lib/hydration';
10
10
  import { useToast } from '../../lib/toast';
11
11
  import { extractConversation, isDoStreamStep } from '../../lib/utils';
12
- import { StreamClickContext } from '../ui/data-inspector';
12
+ import {
13
+ DecryptClickContext,
14
+ RunClickContext,
15
+ StreamClickContext,
16
+ } from '../ui/data-inspector';
13
17
  import { ErrorCard } from '../ui/error-card';
14
18
  import {
15
19
  ErrorStackBlock,
16
20
  isStructuredErrorWithStack,
17
21
  } from '../ui/error-stack-block';
18
22
  import { Skeleton } from '../ui/skeleton';
23
+ import { Spinner } from '../ui/spinner';
19
24
  import { TimestampTooltip } from '../ui/timestamp-tooltip';
20
25
  import { ConversationView } from './conversation-view';
21
26
  import { CopyableDataBlock } from './copyable-data-block';
@@ -180,9 +185,36 @@ function ConversationWithTabs({
180
185
  * with the lucide Lock icon matching the title bar Decrypt button.
181
186
  */
182
187
  function EncryptedFieldBlock() {
188
+ const ctx = useContext(DecryptClickContext);
189
+ if (ctx) {
190
+ return (
191
+ <button
192
+ type="button"
193
+ onClick={ctx.onDecrypt}
194
+ disabled={ctx.isDecrypting}
195
+ className="flex w-full items-center justify-center gap-1.5 rounded-md border px-3 py-2 text-xs cursor-pointer transition-colors"
196
+ style={{
197
+ borderColor: 'var(--ds-gray-400)',
198
+ backgroundColor: 'var(--ds-gray-100)',
199
+ color: 'var(--ds-gray-700)',
200
+ opacity: ctx.isDecrypting ? 0.6 : 1,
201
+ }}
202
+ title="Click to decrypt"
203
+ >
204
+ {ctx.isDecrypting ? (
205
+ <Spinner size={12} />
206
+ ) : (
207
+ <Lock className="h-3 w-3" />
208
+ )}
209
+ <span className="font-medium">
210
+ {ctx.isDecrypting ? 'Decrypting…' : 'Decrypt'}
211
+ </span>
212
+ </button>
213
+ );
214
+ }
183
215
  return (
184
216
  <div
185
- className="flex items-center gap-1.5 rounded-md border px-3 py-2 text-xs"
217
+ className="flex w-full items-center justify-center gap-1.5 rounded-md border px-3 py-2 text-xs"
186
218
  style={{
187
219
  borderColor: 'var(--ds-gray-300)',
188
220
  backgroundColor: 'var(--ds-gray-100)',
@@ -286,8 +318,29 @@ const sortByAttributeOrder = (a: string, b: string): number => {
286
318
  * Display names for attributes that should render differently from their key.
287
319
  */
288
320
  const attributeDisplayNames: Partial<Record<AttributeKey, string>> = {
321
+ moduleSpecifier: 'Module',
322
+ workflowName: 'Workflow Name',
323
+ stepName: 'Step Name',
324
+ stepId: 'Step ID',
325
+ hookId: 'Hook ID',
326
+ attempt: 'Attempts',
327
+ eventId: 'Event ID',
328
+ runId: 'Run ID',
329
+ eventType: 'Event Type',
330
+ correlationId: 'Correlation ID',
331
+ deploymentId: 'Deployment ID',
332
+ specVersion: 'Spec Version',
289
333
  workflowCoreVersion: '@workflow/core version',
290
- receivedCount: 'times resolved',
334
+ createdAt: 'Created',
335
+ startedAt: 'Started',
336
+ updatedAt: 'Updated',
337
+ completedAt: 'Completed',
338
+ expiredAt: 'Expired',
339
+ retryAfter: 'Retry After',
340
+ resumeAt: 'Resume',
341
+ lastReceivedAt: 'Last Received',
342
+ disposedAt: 'Disposed',
343
+ receivedCount: 'Times Resolved',
291
344
  };
292
345
 
293
346
  /**
@@ -374,17 +427,16 @@ const attributeToDisplayFn: Record<
374
427
  (value: unknown, context?: DisplayContext) => null | string | ReactNode
375
428
  > = {
376
429
  // Names that need pretty-printing
377
- workflowName: (value: unknown) =>
378
- parseWorkflowName(String(value))?.shortName ?? '?',
430
+ workflowName: (_value: unknown) => null,
379
431
  moduleSpecifier: (value: unknown) => getModuleSpecifierFromName(value),
380
- stepName: (value: unknown) => parseStepName(String(value))?.shortName ?? '?',
432
+ stepName: (_value: unknown) => null,
381
433
  // IDs
382
- runId: (value: unknown) => String(value),
383
- stepId: (value: unknown) => String(value),
434
+ runId: (_value: unknown) => null,
435
+ stepId: (_value: unknown) => null,
384
436
  hookId: (value: unknown) => String(value),
385
437
  eventId: (value: unknown) => String(value),
386
438
  // Run/step details
387
- status: (value: unknown) => String(value),
439
+ status: (_value: unknown) => null,
388
440
  attempt: (value: unknown) => String(value),
389
441
  // Hook details
390
442
  token: (value: unknown) => String(value),
@@ -407,9 +459,9 @@ const attributeToDisplayFn: Record<
407
459
  // Dates — wrapped with TimestampTooltip showing UTC/local + relative time
408
460
  createdAt: timestampWithTooltipOrNull,
409
461
  startedAt: timestampWithTooltipOrNull,
410
- updatedAt: timestampWithTooltipOrNull,
462
+ updatedAt: (_value: unknown) => null,
411
463
  completedAt: timestampWithTooltipOrNull,
412
- expiredAt: timestampWithTooltipOrNull,
464
+ expiredAt: (_value: unknown) => null,
413
465
  retryAfter: timestampWithTooltipOrNull,
414
466
  resumeAt: timestampWithTooltipOrNull,
415
467
  // Resolved attributes, won't actually use this function
@@ -463,7 +515,7 @@ const attributeToDisplayFn: Record<
463
515
  <DetailCard
464
516
  summary="Input (no data)"
465
517
  disabled
466
- summaryClassName="text-base py-2"
518
+ summaryClassName="text-label-14 font-medium py-2"
467
519
  />
468
520
  );
469
521
  }
@@ -472,7 +524,7 @@ const attributeToDisplayFn: Record<
472
524
  <>
473
525
  <DetailCard
474
526
  summary={`Input (${argCount} ${argLabel})`}
475
- summaryClassName="text-base py-2"
527
+ summaryClassName="text-label-14 font-medium py-2"
476
528
  contentClassName="mt-0"
477
529
  >
478
530
  {Array.isArray(args)
@@ -503,14 +555,14 @@ const attributeToDisplayFn: Record<
503
555
  <DetailCard
504
556
  summary="Input (no data)"
505
557
  disabled
506
- summaryClassName="text-base py-2"
558
+ summaryClassName="text-label-14 font-medium py-2"
507
559
  />
508
560
  );
509
561
  }
510
562
  return (
511
563
  <DetailCard
512
564
  summary={`Input (${argCount} ${argLabel})`}
513
- summaryClassName="text-base py-2"
565
+ summaryClassName="text-label-14 font-medium py-2"
514
566
  contentClassName="mt-0"
515
567
  >
516
568
  {Array.isArray(value)
@@ -530,7 +582,7 @@ const attributeToDisplayFn: Record<
530
582
  return (
531
583
  <DetailCard
532
584
  summary="Output"
533
- summaryClassName="text-base py-2"
585
+ summaryClassName="text-label-14 font-medium py-2"
534
586
  contentClassName="mt-0"
535
587
  >
536
588
  {JsonBlock(value)}
@@ -548,7 +600,7 @@ const attributeToDisplayFn: Record<
548
600
  return (
549
601
  <DetailCard
550
602
  summary="Error"
551
- summaryClassName="text-base py-2"
603
+ summaryClassName="text-label-14 font-medium py-2"
552
604
  contentClassName="mt-0"
553
605
  >
554
606
  <ErrorStackBlock value={value} />
@@ -559,7 +611,7 @@ const attributeToDisplayFn: Record<
559
611
  return (
560
612
  <DetailCard
561
613
  summary="Error"
562
- summaryClassName="text-base py-2"
614
+ summaryClassName="text-label-14 font-medium py-2"
563
615
  contentClassName="mt-0"
564
616
  >
565
617
  {JsonBlock(value)}
@@ -609,18 +661,13 @@ export const AttributeBlock = ({
609
661
  context?: DisplayContext;
610
662
  }) => {
611
663
  const isExpandableLoadingTarget =
612
- attribute === 'input' ||
613
- attribute === 'output' ||
614
- attribute === 'eventData';
664
+ attribute === 'input' || attribute === 'eventData';
615
665
  if (isLoading && isExpandableLoadingTarget && !hasDisplayContent(value)) {
616
666
  return (
617
667
  <div
618
- className={`my-2 flex flex-col ${attribute === 'input' || attribute === 'output' ? 'gap-2 my-3.5' : 'gap-0'}`}
668
+ className={`my-2 flex flex-col ${attribute === 'input' ? 'gap-2 my-3.5' : 'gap-0'}`}
619
669
  >
620
- <span
621
- className={`${attribute === 'input' || attribute === 'output' ? 'text-base' : 'text-xs'} font-medium first-letter:uppercase`}
622
- style={{ color: 'var(--ds-gray-700)' }}
623
- >
670
+ <span className="text-label-14 text-gray-1000 font-medium first-letter:uppercase">
624
671
  {attribute}
625
672
  </span>
626
673
  <Skeleton className="h-9 w-full rounded-md" />
@@ -668,10 +715,7 @@ export const AttributeBlock = ({
668
715
  key={attribute}
669
716
  className={`my-2 flex flex-col ${attribute === 'input' || attribute === 'output' || attribute === 'error' ? 'gap-2 my-3.5' : 'gap-0'}`}
670
717
  >
671
- <span
672
- className={`${attribute === 'input' || attribute === 'output' || attribute === 'error' ? 'text-base' : 'text-xs'} font-medium first-letter:uppercase`}
673
- style={{ color: 'var(--ds-gray-700)' }}
674
- >
718
+ <span className="text-label-14 text-gray-1000 font-medium first-letter:uppercase">
675
719
  {attribute}
676
720
  </span>
677
721
  <span className="text-xs" style={{ color: 'var(--ds-gray-1000)' }}>
@@ -689,6 +733,9 @@ export const AttributePanel = ({
689
733
  error,
690
734
  expiredAt,
691
735
  onStreamClick,
736
+ onRunClick,
737
+ onDecrypt,
738
+ isDecrypting = false,
692
739
  resource,
693
740
  }: {
694
741
  data: Record<string, unknown>;
@@ -698,6 +745,12 @@ export const AttributePanel = ({
698
745
  expiredAt?: string | Date;
699
746
  /** Callback when a stream reference is clicked */
700
747
  onStreamClick?: (streamId: string) => void;
748
+ /** Callback when a run reference is clicked */
749
+ onRunClick?: (runId: string) => void;
750
+ /** Callback when an encrypted marker is clicked (triggers decryption) */
751
+ onDecrypt?: () => void;
752
+ /** Whether decryption is currently in progress */
753
+ isDecrypting?: boolean;
701
754
  /** Resource type of the selected span — used to show targeted loading skeletons. */
702
755
  resource?: string;
703
756
  }) => {
@@ -731,9 +784,9 @@ export const AttributePanel = ({
731
784
 
732
785
  if (!isLoading) return present;
733
786
 
734
- // During loading, ensure input/output appear so their skeletons render
787
+ // During loading, ensure input appears so its skeleton renders
735
788
  // in the correct position (above the events section).
736
- const loadingDefaults = ['input', 'output'];
789
+ const loadingDefaults = ['input'];
737
790
  for (const key of loadingDefaults) {
738
791
  if (!present.includes(key)) {
739
792
  present.push(key);
@@ -797,117 +850,126 @@ export const AttributePanel = ({
797
850
  }, []);
798
851
 
799
852
  return (
800
- <StreamClickContext.Provider value={onStreamClick}>
801
- <div>
802
- {/* Basic attributes in a vertical layout with border */}
803
- {visibleBasicAttributes.length > 0 && (
804
- <div
805
- className="mb-3 flex flex-col overflow-hidden rounded-lg border"
806
- style={{
807
- borderColor: 'var(--ds-gray-300)',
808
- }}
809
- >
810
- {orderedBasicAttributes.map((attribute, index) => {
811
- const displayValue = attributeToDisplayFn[
812
- attribute as keyof typeof attributeToDisplayFn
813
- ]?.(displayData[attribute as keyof typeof displayData]);
814
- const isModuleSpecifier = attribute === 'moduleSpecifier';
815
- const moduleSpecifierValue =
816
- typeof displayValue === 'string'
817
- ? displayValue
818
- : String(displayValue ?? displayData.moduleSpecifier ?? '');
819
- const shouldCapitalizeLabel = attribute !== 'workflowCoreVersion';
820
- const showResumeAtSkeleton =
821
- isLoading && resource === 'sleep' && !displayData.resumeAt;
822
- const showDivider =
823
- index < orderedBasicAttributes.length - 1 ||
824
- showResumeAtSkeleton;
825
-
826
- return (
827
- <div key={attribute} className="py-1">
828
- <div className="flex min-h-[32px] items-center justify-between gap-4 rounded-sm px-2.5 py-1">
829
- <span
830
- className={
831
- shouldCapitalizeLabel
832
- ? 'text-[14px] first-letter:uppercase'
833
- : 'text-[14px]'
834
- }
835
- style={{ color: 'var(--ds-gray-700)' }}
836
- >
837
- {getAttributeDisplayName(attribute)}
838
- </span>
839
- {isModuleSpecifier ? (
840
- <button
841
- type="button"
842
- className="min-w-0 max-w-[70%] truncate text-right text-[13px] font-mono"
843
- style={{
844
- color: 'var(--ds-gray-1000)',
845
- background: 'transparent',
846
- border: 'none',
847
- padding: 0,
848
- }}
849
- title={moduleSpecifierValue}
850
- onClick={() =>
851
- handleCopyModuleSpecifier(moduleSpecifierValue)
852
- }
853
- >
854
- {moduleSpecifierValue}
855
- </button>
856
- ) : (
853
+ <RunClickContext.Provider value={onRunClick}>
854
+ <StreamClickContext.Provider value={onStreamClick}>
855
+ <DecryptClickContext.Provider
856
+ value={onDecrypt ? { onDecrypt, isDecrypting } : undefined}
857
+ >
858
+ <div>
859
+ {/* Basic attributes in a vertical layout with border */}
860
+ {visibleBasicAttributes.length > 0 && (
861
+ <div
862
+ className="mb-3 flex flex-col overflow-hidden rounded-md border"
863
+ style={{
864
+ borderColor: 'var(--ds-gray-300)',
865
+ }}
866
+ >
867
+ {orderedBasicAttributes.map((attribute, index) => {
868
+ const displayValue = attributeToDisplayFn[
869
+ attribute as keyof typeof attributeToDisplayFn
870
+ ]?.(displayData[attribute as keyof typeof displayData]);
871
+ const isModuleSpecifier = attribute === 'moduleSpecifier';
872
+ const moduleSpecifierValue =
873
+ typeof displayValue === 'string'
874
+ ? displayValue
875
+ : String(
876
+ displayValue ?? displayData.moduleSpecifier ?? ''
877
+ );
878
+ const shouldCapitalizeLabel =
879
+ attribute !== 'workflowCoreVersion';
880
+ const showResumeAtSkeleton =
881
+ isLoading && resource === 'sleep' && !displayData.resumeAt;
882
+ const showDivider =
883
+ index < orderedBasicAttributes.length - 1 ||
884
+ showResumeAtSkeleton;
885
+
886
+ return (
887
+ <div key={attribute} className="py-1">
888
+ <div className="flex min-h-[32px] items-center justify-between gap-4 rounded-sm px-2.5 py-1">
889
+ <span
890
+ className={
891
+ shouldCapitalizeLabel
892
+ ? 'text-[14px] first-letter:uppercase'
893
+ : 'text-[14px]'
894
+ }
895
+ style={{ color: 'var(--ds-gray-700)' }}
896
+ >
897
+ {getAttributeDisplayName(attribute)}
898
+ </span>
899
+ {isModuleSpecifier ? (
900
+ <button
901
+ type="button"
902
+ className="min-w-0 max-w-[70%] truncate text-right text-[13px] font-mono"
903
+ style={{
904
+ color: 'var(--ds-gray-1000)',
905
+ background: 'transparent',
906
+ border: 'none',
907
+ padding: 0,
908
+ }}
909
+ title={moduleSpecifierValue}
910
+ onClick={() =>
911
+ handleCopyModuleSpecifier(moduleSpecifierValue)
912
+ }
913
+ >
914
+ {moduleSpecifierValue}
915
+ </button>
916
+ ) : (
917
+ <span
918
+ className="min-w-0 max-w-[70%] truncate text-right text-[13px] font-mono"
919
+ style={{ color: 'var(--ds-gray-1000)' }}
920
+ >
921
+ {displayValue}
922
+ </span>
923
+ )}
924
+ </div>
925
+ {showDivider ? (
926
+ <div
927
+ className="mx-2.5 border-b"
928
+ style={{ borderColor: 'var(--ds-gray-300)' }}
929
+ />
930
+ ) : null}
931
+ </div>
932
+ );
933
+ })}
934
+ {isLoading && resource === 'sleep' && !displayData.resumeAt && (
935
+ <div className="py-1">
936
+ <div className="flex min-h-[32px] items-center justify-between gap-4 rounded-sm px-2.5 py-1">
857
937
  <span
858
- className="min-w-0 max-w-[70%] truncate text-right text-[13px] font-mono"
859
- style={{ color: 'var(--ds-gray-1000)' }}
938
+ className="text-[14px] first-letter:uppercase"
939
+ style={{ color: 'var(--ds-gray-700)' }}
860
940
  >
861
- {displayValue}
941
+ resumeAt
862
942
  </span>
863
- )}
943
+ <Skeleton className="h-4 w-[55%]" />
944
+ </div>
864
945
  </div>
865
- {showDivider ? (
866
- <div
867
- className="mx-2.5 border-b"
868
- style={{ borderColor: 'var(--ds-gray-300)' }}
869
- />
870
- ) : null}
871
- </div>
872
- );
873
- })}
874
- {isLoading && resource === 'sleep' && !displayData.resumeAt && (
875
- <div className="py-1">
876
- <div className="flex min-h-[32px] items-center justify-between gap-4 rounded-sm px-2.5 py-1">
877
- <span
878
- className="text-[14px] first-letter:uppercase"
879
- style={{ color: 'var(--ds-gray-700)' }}
880
- >
881
- resumeAt
882
- </span>
883
- <Skeleton className="h-4 w-[55%]" />
884
- </div>
946
+ )}
885
947
  </div>
886
948
  )}
887
- </div>
888
- )}
889
- {error ? (
890
- <ErrorCard
891
- title="Failed to load resource details"
892
- details={error.message}
893
- className="my-4"
894
- />
895
- ) : hasExpired ? (
896
- <ExpiredDataMessage />
897
- ) : (
898
- <>
899
- {resolvedAttributes.map((attribute) => (
900
- <AttributeBlock
901
- isLoading={isLoading}
902
- key={attribute}
903
- attribute={attribute}
904
- value={displayData[attribute as keyof typeof displayData]}
905
- context={displayContext}
949
+ {error ? (
950
+ <ErrorCard
951
+ title="Failed to load resource details"
952
+ details={error.message}
953
+ className="my-4"
906
954
  />
907
- ))}
908
- </>
909
- )}
910
- </div>
911
- </StreamClickContext.Provider>
955
+ ) : hasExpired ? (
956
+ <ExpiredDataMessage />
957
+ ) : (
958
+ <>
959
+ {resolvedAttributes.map((attribute) => (
960
+ <AttributeBlock
961
+ isLoading={isLoading}
962
+ key={attribute}
963
+ attribute={attribute}
964
+ value={displayData[attribute as keyof typeof displayData]}
965
+ context={displayContext}
966
+ />
967
+ ))}
968
+ </>
969
+ )}
970
+ </div>
971
+ </DecryptClickContext.Provider>
972
+ </StreamClickContext.Provider>
973
+ </RunClickContext.Provider>
912
974
  );
913
975
  };
@@ -24,7 +24,7 @@ export function CopyableDataBlock({ data }: { data: unknown }) {
24
24
  const toast = useToast();
25
25
  return (
26
26
  <div
27
- className="relative overflow-x-auto rounded-md border p-3 pt-9"
27
+ className="relative overflow-x-auto rounded-md border p-3"
28
28
  style={{ borderColor: 'var(--ds-gray-300)' }}
29
29
  >
30
30
  <button
@@ -59,25 +59,7 @@ export function DetailCard({
59
59
  {summary}
60
60
  </span>
61
61
  </summary>
62
- {/* Expanded content with connecting line */}
63
- <div className={`relative pl-6 mt-3 ${contentClassName ?? ''}`}>
64
- {/* Curved connecting line - vertical part from summary */}
65
- <div
66
- className="absolute left-3 -top-3 w-px h-3"
67
- style={{ backgroundColor: 'var(--ds-gray-400)' }}
68
- />
69
- {/* Curved corner */}
70
- <div
71
- className="absolute left-3 top-0 w-3 h-3 border-l border-b rounded-bl-lg"
72
- style={{ borderColor: 'var(--ds-gray-400)' }}
73
- />
74
- {/* Horizontal part to content */}
75
- <div
76
- className="absolute left-6 top-3 w-0 h-px -translate-y-px"
77
- style={{ backgroundColor: 'var(--ds-gray-400)' }}
78
- />
79
- <div>{children}</div>
80
- </div>
62
+ <div className={`mt-2 ${contentClassName ?? ''}`}>{children}</div>
81
63
  </details>
82
64
  );
83
65
  }