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

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 (102) 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 +1 -1
  9. package/dist/components/index.d.ts.map +1 -1
  10. package/dist/components/index.js +1 -1
  11. package/dist/components/index.js.map +1 -1
  12. package/dist/components/run-trace-view.d.ts +2 -1
  13. package/dist/components/run-trace-view.d.ts.map +1 -1
  14. package/dist/components/run-trace-view.js +2 -2
  15. package/dist/components/run-trace-view.js.map +1 -1
  16. package/dist/components/sidebar/attribute-panel.d.ts +7 -1
  17. package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
  18. package/dist/components/sidebar/attribute-panel.js +36 -26
  19. package/dist/components/sidebar/attribute-panel.js.map +1 -1
  20. package/dist/components/sidebar/conversation-view.js.map +1 -1
  21. package/dist/components/sidebar/copyable-data-block.js.map +1 -1
  22. package/dist/components/sidebar/detail-card.js.map +1 -1
  23. package/dist/components/sidebar/entity-detail-panel.d.ts +5 -1
  24. package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
  25. package/dist/components/sidebar/entity-detail-panel.js +18 -16
  26. package/dist/components/sidebar/entity-detail-panel.js.map +1 -1
  27. package/dist/components/sidebar/events-list.js.map +1 -1
  28. package/dist/components/sidebar/resolve-hook-modal.js.map +1 -1
  29. package/dist/components/stream-viewer.js.map +1 -1
  30. package/dist/components/trace-viewer/components/map.js.map +1 -1
  31. package/dist/components/trace-viewer/components/markers.js.map +1 -1
  32. package/dist/components/trace-viewer/components/node.js.map +1 -1
  33. package/dist/components/trace-viewer/components/search-input.js.map +1 -1
  34. package/dist/components/trace-viewer/components/search.js.map +1 -1
  35. package/dist/components/trace-viewer/components/span-content.js.map +1 -1
  36. package/dist/components/trace-viewer/components/span-detail-panel.js.map +1 -1
  37. package/dist/components/trace-viewer/components/span-segments.js.map +1 -1
  38. package/dist/components/trace-viewer/components/span-strategies.js.map +1 -1
  39. package/dist/components/trace-viewer/components/ui.js.map +1 -1
  40. package/dist/components/trace-viewer/components/zoom-button.js.map +1 -1
  41. package/dist/components/trace-viewer/components/zoom-icons.js.map +1 -1
  42. package/dist/components/trace-viewer/context.js.map +1 -1
  43. package/dist/components/trace-viewer/index.js.map +1 -1
  44. package/dist/components/trace-viewer/trace-viewer.js.map +1 -1
  45. package/dist/components/trace-viewer/types.js.map +1 -1
  46. package/dist/components/trace-viewer/util/constants.js.map +1 -1
  47. package/dist/components/trace-viewer/util/scrollbar-width.js.map +1 -1
  48. package/dist/components/trace-viewer/util/timing.js.map +1 -1
  49. package/dist/components/trace-viewer/util/tree.js.map +1 -1
  50. package/dist/components/trace-viewer/util/use-immediate-style.js.map +1 -1
  51. package/dist/components/trace-viewer/util/use-streaming-spans.js.map +1 -1
  52. package/dist/components/trace-viewer/util/use-trackpad-zoom.js.map +1 -1
  53. package/dist/components/trace-viewer/worker.js.map +1 -1
  54. package/dist/components/ui/alert.js.map +1 -1
  55. package/dist/components/ui/card.js.map +1 -1
  56. package/dist/components/ui/data-inspector.d.ts +17 -1
  57. package/dist/components/ui/data-inspector.d.ts.map +1 -1
  58. package/dist/components/ui/data-inspector.js +116 -21
  59. package/dist/components/ui/data-inspector.js.map +1 -1
  60. package/dist/components/ui/decrypt-button.js.map +1 -1
  61. package/dist/components/ui/error-card.js.map +1 -1
  62. package/dist/components/ui/error-stack-block.js.map +1 -1
  63. package/dist/components/ui/inspector-theme.js.map +1 -1
  64. package/dist/components/ui/load-more-button.js.map +1 -1
  65. package/dist/components/ui/menu-dropdown.js.map +1 -1
  66. package/dist/components/ui/skeleton.js.map +1 -1
  67. package/dist/components/ui/spinner.js.map +1 -1
  68. package/dist/components/ui/timestamp-tooltip.js.map +1 -1
  69. package/dist/components/workflow-trace-view.d.ts +5 -1
  70. package/dist/components/workflow-trace-view.d.ts.map +1 -1
  71. package/dist/components/workflow-trace-view.js +7 -2
  72. package/dist/components/workflow-trace-view.js.map +1 -1
  73. package/dist/components/workflow-traces/event-colors.js.map +1 -1
  74. package/dist/components/workflow-traces/trace-colors.js.map +1 -1
  75. package/dist/components/workflow-traces/trace-span-construction.js +1 -1
  76. package/dist/components/workflow-traces/trace-span-construction.js.map +1 -1
  77. package/dist/components/workflow-traces/trace-time-utils.js.map +1 -1
  78. package/dist/hooks/use-dark-mode.js.map +1 -1
  79. package/dist/index.d.ts +1 -1
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +1 -1
  82. package/dist/index.js.map +1 -1
  83. package/dist/lib/event-analysis.js.map +1 -1
  84. package/dist/lib/event-materialization.js.map +1 -1
  85. package/dist/lib/hydration.d.ts +8 -1
  86. package/dist/lib/hydration.d.ts.map +1 -1
  87. package/dist/lib/hydration.js +32 -2
  88. package/dist/lib/hydration.js.map +1 -1
  89. package/dist/lib/toast.js.map +1 -1
  90. package/dist/lib/trace-builder.js.map +1 -1
  91. package/dist/lib/utils.js.map +1 -1
  92. package/package.json +3 -3
  93. package/src/components/event-list-view.tsx +181 -173
  94. package/src/components/index.ts +6 -1
  95. package/src/components/run-trace-view.tsx +3 -0
  96. package/src/components/sidebar/attribute-panel.tsx +160 -109
  97. package/src/components/sidebar/entity-detail-panel.tsx +111 -96
  98. package/src/components/ui/data-inspector.tsx +205 -31
  99. package/src/components/workflow-trace-view.tsx +14 -0
  100. package/src/components/workflow-traces/trace-span-construction.ts +1 -1
  101. package/src/index.ts +1 -0
  102. package/src/lib/hydration.ts +37 -0
@@ -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)',
@@ -377,7 +409,8 @@ const attributeToDisplayFn: Record<
377
409
  workflowName: (value: unknown) =>
378
410
  parseWorkflowName(String(value))?.shortName ?? '?',
379
411
  moduleSpecifier: (value: unknown) => getModuleSpecifierFromName(value),
380
- stepName: (value: unknown) => parseStepName(String(value))?.shortName ?? '?',
412
+ stepName: (value: unknown) =>
413
+ parseStepName(String(value))?.shortName ?? String(value),
381
414
  // IDs
382
415
  runId: (value: unknown) => String(value),
383
416
  stepId: (value: unknown) => String(value),
@@ -689,6 +722,9 @@ export const AttributePanel = ({
689
722
  error,
690
723
  expiredAt,
691
724
  onStreamClick,
725
+ onRunClick,
726
+ onDecrypt,
727
+ isDecrypting = false,
692
728
  resource,
693
729
  }: {
694
730
  data: Record<string, unknown>;
@@ -698,6 +734,12 @@ export const AttributePanel = ({
698
734
  expiredAt?: string | Date;
699
735
  /** Callback when a stream reference is clicked */
700
736
  onStreamClick?: (streamId: string) => void;
737
+ /** Callback when a run reference is clicked */
738
+ onRunClick?: (runId: string) => void;
739
+ /** Callback when an encrypted marker is clicked (triggers decryption) */
740
+ onDecrypt?: () => void;
741
+ /** Whether decryption is currently in progress */
742
+ isDecrypting?: boolean;
701
743
  /** Resource type of the selected span — used to show targeted loading skeletons. */
702
744
  resource?: string;
703
745
  }) => {
@@ -797,117 +839,126 @@ export const AttributePanel = ({
797
839
  }, []);
798
840
 
799
841
  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
- ) : (
842
+ <RunClickContext.Provider value={onRunClick}>
843
+ <StreamClickContext.Provider value={onStreamClick}>
844
+ <DecryptClickContext.Provider
845
+ value={onDecrypt ? { onDecrypt, isDecrypting } : undefined}
846
+ >
847
+ <div>
848
+ {/* Basic attributes in a vertical layout with border */}
849
+ {visibleBasicAttributes.length > 0 && (
850
+ <div
851
+ className="mb-3 flex flex-col overflow-hidden rounded-lg border"
852
+ style={{
853
+ borderColor: 'var(--ds-gray-300)',
854
+ }}
855
+ >
856
+ {orderedBasicAttributes.map((attribute, index) => {
857
+ const displayValue = attributeToDisplayFn[
858
+ attribute as keyof typeof attributeToDisplayFn
859
+ ]?.(displayData[attribute as keyof typeof displayData]);
860
+ const isModuleSpecifier = attribute === 'moduleSpecifier';
861
+ const moduleSpecifierValue =
862
+ typeof displayValue === 'string'
863
+ ? displayValue
864
+ : String(
865
+ displayValue ?? displayData.moduleSpecifier ?? ''
866
+ );
867
+ const shouldCapitalizeLabel =
868
+ attribute !== 'workflowCoreVersion';
869
+ const showResumeAtSkeleton =
870
+ isLoading && resource === 'sleep' && !displayData.resumeAt;
871
+ const showDivider =
872
+ index < orderedBasicAttributes.length - 1 ||
873
+ showResumeAtSkeleton;
874
+
875
+ return (
876
+ <div key={attribute} className="py-1">
877
+ <div className="flex min-h-[32px] items-center justify-between gap-4 rounded-sm px-2.5 py-1">
878
+ <span
879
+ className={
880
+ shouldCapitalizeLabel
881
+ ? 'text-[14px] first-letter:uppercase'
882
+ : 'text-[14px]'
883
+ }
884
+ style={{ color: 'var(--ds-gray-700)' }}
885
+ >
886
+ {getAttributeDisplayName(attribute)}
887
+ </span>
888
+ {isModuleSpecifier ? (
889
+ <button
890
+ type="button"
891
+ className="min-w-0 max-w-[70%] truncate text-right text-[13px] font-mono"
892
+ style={{
893
+ color: 'var(--ds-gray-1000)',
894
+ background: 'transparent',
895
+ border: 'none',
896
+ padding: 0,
897
+ }}
898
+ title={moduleSpecifierValue}
899
+ onClick={() =>
900
+ handleCopyModuleSpecifier(moduleSpecifierValue)
901
+ }
902
+ >
903
+ {moduleSpecifierValue}
904
+ </button>
905
+ ) : (
906
+ <span
907
+ className="min-w-0 max-w-[70%] truncate text-right text-[13px] font-mono"
908
+ style={{ color: 'var(--ds-gray-1000)' }}
909
+ >
910
+ {displayValue}
911
+ </span>
912
+ )}
913
+ </div>
914
+ {showDivider ? (
915
+ <div
916
+ className="mx-2.5 border-b"
917
+ style={{ borderColor: 'var(--ds-gray-300)' }}
918
+ />
919
+ ) : null}
920
+ </div>
921
+ );
922
+ })}
923
+ {isLoading && resource === 'sleep' && !displayData.resumeAt && (
924
+ <div className="py-1">
925
+ <div className="flex min-h-[32px] items-center justify-between gap-4 rounded-sm px-2.5 py-1">
857
926
  <span
858
- className="min-w-0 max-w-[70%] truncate text-right text-[13px] font-mono"
859
- style={{ color: 'var(--ds-gray-1000)' }}
927
+ className="text-[14px] first-letter:uppercase"
928
+ style={{ color: 'var(--ds-gray-700)' }}
860
929
  >
861
- {displayValue}
930
+ resumeAt
862
931
  </span>
863
- )}
932
+ <Skeleton className="h-4 w-[55%]" />
933
+ </div>
864
934
  </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>
935
+ )}
885
936
  </div>
886
937
  )}
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}
938
+ {error ? (
939
+ <ErrorCard
940
+ title="Failed to load resource details"
941
+ details={error.message}
942
+ className="my-4"
906
943
  />
907
- ))}
908
- </>
909
- )}
910
- </div>
911
- </StreamClickContext.Provider>
944
+ ) : hasExpired ? (
945
+ <ExpiredDataMessage />
946
+ ) : (
947
+ <>
948
+ {resolvedAttributes.map((attribute) => (
949
+ <AttributeBlock
950
+ isLoading={isLoading}
951
+ key={attribute}
952
+ attribute={attribute}
953
+ value={displayData[attribute as keyof typeof displayData]}
954
+ context={displayContext}
955
+ />
956
+ ))}
957
+ </>
958
+ )}
959
+ </div>
960
+ </DecryptClickContext.Provider>
961
+ </StreamClickContext.Provider>
962
+ </RunClickContext.Provider>
912
963
  );
913
964
  };
@@ -4,8 +4,9 @@ import type { Event, Hook, Step, WorkflowRun } from '@workflow/world';
4
4
  import clsx from 'clsx';
5
5
  import { Send, Zap } from 'lucide-react';
6
6
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
7
- import { useToast } from '../../lib/toast';
8
7
  import { isEncryptedMarker } from '../../lib/hydration';
8
+ import { useToast } from '../../lib/toast';
9
+ import { DecryptClickContext } from '../ui/data-inspector';
9
10
  import { DecryptButton } from '../ui/decrypt-button';
10
11
  import { AttributePanel } from './attribute-panel';
11
12
  import { EventsList } from './events-list';
@@ -56,6 +57,7 @@ export interface SelectedSpanInfo {
56
57
  export function EntityDetailPanel({
57
58
  run,
58
59
  onStreamClick,
60
+ onRunClick,
59
61
  spanDetailData,
60
62
  spanDetailError,
61
63
  spanDetailLoading,
@@ -67,10 +69,13 @@ export function EntityDetailPanel({
67
69
  onDecrypt,
68
70
  isDecrypting = false,
69
71
  selectedSpan,
72
+ hasEncryptedData = false,
70
73
  }: {
71
74
  run: WorkflowRun;
72
75
  /** Callback when a stream reference is clicked */
73
76
  onStreamClick?: (streamId: string) => void;
77
+ /** Callback when a run reference is clicked */
78
+ onRunClick?: (runId: string) => void;
74
79
  /** Pre-fetched span detail data for the selected span. */
75
80
  spanDetailData: WorkflowRun | Step | Hook | Event | null;
76
81
  /** Error from external span detail fetch. */
@@ -103,6 +108,8 @@ export function EntityDetailPanel({
103
108
  isDecrypting?: boolean;
104
109
  /** Info about the currently selected span from the trace viewer */
105
110
  selectedSpan: SelectedSpanInfo | null;
111
+ /** Run-level hint: the run contains encrypted data (from probe). */
112
+ hasEncryptedData?: boolean;
106
113
  }): React.JSX.Element | null {
107
114
  const toast = useToast();
108
115
  const [stoppingSleep, setStoppingSleep] = useState(false);
@@ -391,111 +398,119 @@ export function EntityDetailPanel({
391
398
  {resourceId}
392
399
  </p>
393
400
  </div>
394
- {(hasEncryptedFields || encryptionKey) && onDecrypt && (
395
- <DecryptButton
396
- decrypted={!!encryptionKey}
397
- loading={isDecrypting}
398
- onClick={onDecrypt}
399
- />
400
- )}
401
+ {(hasEncryptedFields || hasEncryptedData || encryptionKey) &&
402
+ onDecrypt && (
403
+ <DecryptButton
404
+ decrypted={!!encryptionKey}
405
+ loading={isDecrypting}
406
+ onClick={onDecrypt}
407
+ />
408
+ )}
401
409
  </div>
402
410
  </div>
403
411
 
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)' }}
412
+ <DecryptClickContext.Provider
413
+ value={onDecrypt ? { onDecrypt, isDecrypting } : undefined}
414
+ >
415
+ <div className="flex-1 overflow-y-auto px-3 pt-3 pb-8">
416
+ {hasPendingActions && (
417
+ <div
418
+ className="mb-4 rounded-lg border p-2"
419
+ style={{
420
+ borderColor: 'var(--ds-gray-300)',
421
+ backgroundColor: 'var(--ds-gray-100)',
422
+ }}
416
423
  >
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
- )}
424
+ <p
425
+ className="mb-2 px-1 text-[13px] font-medium uppercase tracking-wide"
426
+ style={{ color: 'var(--ds-gray-700)' }}
427
+ >
428
+ Actions
429
+ </p>
430
+ <div className="flex flex-col gap-2">
431
+ {/* Wake up button for pending sleep calls */}
432
+ {resource === 'sleep' && canWakeUp && (
433
+ <button
434
+ type="button"
435
+ onClick={handleWakeUp}
436
+ disabled={stoppingSleep}
437
+ className={clsx(
438
+ 'flex items-center justify-center gap-2 rounded-md px-3 py-2 text-sm font-medium',
439
+ 'disabled:opacity-50 disabled:cursor-not-allowed transition-colors',
440
+ stoppingSleep
441
+ ? 'opacity-50 cursor-not-allowed'
442
+ : 'cursor-pointer'
443
+ )}
444
+ style={{
445
+ background: 'var(--ds-amber-200)',
446
+ color: 'var(--ds-amber-900)',
447
+ }}
448
+ >
449
+ <Zap className="h-4 w-4" />
450
+ {stoppingSleep ? 'Waking up...' : 'Wake Up Sleep'}
451
+ </button>
452
+ )}
453
+
454
+ {/* Resolve hook button for pending hooks */}
455
+ {resource === 'hook' && canResolveHook && (
456
+ <button
457
+ type="button"
458
+ onClick={() => setShowResolveHookModal(true)}
459
+ disabled={resolvingHook}
460
+ className={clsx(
461
+ 'flex items-center justify-center gap-2 rounded-md px-3 py-2 text-sm font-medium',
462
+ 'disabled:opacity-50 disabled:cursor-not-allowed transition-colors',
463
+ resolvingHook
464
+ ? 'opacity-50 cursor-not-allowed'
465
+ : 'cursor-pointer'
466
+ )}
467
+ style={{
468
+ background: 'var(--ds-gray-1000)',
469
+ color: 'var(--ds-background-100)',
470
+ }}
471
+ >
472
+ <Send className="h-4 w-4" />
473
+ Resolve Hook
474
+ </button>
475
+ )}
476
+ </div>
465
477
  </div>
466
- </div>
467
- )}
478
+ )}
468
479
 
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 && (
480
+ <div className="space-y-4">
489
481
  <section>
490
- <EventsList
491
- events={rawEvents}
492
- onLoadEventData={onLoadEventData}
493
- encryptionKey={encryptionKey}
482
+ <h3
483
+ className="mb-2 text-[13px] font-medium uppercase tracking-wide"
484
+ style={{ color: 'var(--ds-gray-700)' }}
485
+ >
486
+ Details
487
+ </h3>
488
+ <AttributePanel
489
+ data={displayData}
490
+ moduleSpecifier={moduleSpecifier}
491
+ expiredAt={run.expiredAt}
492
+ isLoading={loading}
493
+ error={error ?? undefined}
494
+ onStreamClick={onStreamClick}
495
+ onRunClick={onRunClick}
496
+ onDecrypt={onDecrypt}
497
+ isDecrypting={isDecrypting}
498
+ resource={resource}
494
499
  />
495
500
  </section>
496
- )}
501
+
502
+ {resource !== 'run' && rawEvents && (
503
+ <section>
504
+ <EventsList
505
+ events={rawEvents}
506
+ onLoadEventData={onLoadEventData}
507
+ encryptionKey={encryptionKey}
508
+ />
509
+ </section>
510
+ )}
511
+ </div>
497
512
  </div>
498
- </div>
513
+ </DecryptClickContext.Provider>
499
514
 
500
515
  {/* Resolve Hook Modal */}
501
516
  <ResolveHookModal