@workflow/web-shared 4.1.0-beta.47 → 4.1.0-beta.49
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.
- package/README.md +26 -52
- package/dist/components/error-boundary.d.ts.map +1 -0
- package/dist/{error-boundary.js → components/error-boundary.js} +1 -1
- package/dist/components/error-boundary.js.map +1 -0
- package/dist/{event-list-view.d.ts → components/event-list-view.d.ts} +2 -3
- package/dist/components/event-list-view.d.ts.map +1 -0
- package/dist/{event-list-view.js → components/event-list-view.js} +9 -17
- package/dist/components/event-list-view.js.map +1 -0
- package/dist/{hook-actions.d.ts → components/hook-actions.d.ts} +2 -3
- package/dist/components/hook-actions.d.ts.map +1 -0
- package/dist/{hook-actions.js → components/hook-actions.js} +3 -4
- package/dist/components/hook-actions.js.map +1 -0
- package/dist/components/index.d.ts +10 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +8 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/run-trace-view.d.ts +22 -0
- package/dist/components/run-trace-view.d.ts.map +1 -0
- package/dist/components/run-trace-view.js +11 -0
- package/dist/components/run-trace-view.js.map +1 -0
- package/dist/components/sidebar/attribute-panel.d.ts.map +1 -0
- package/dist/{sidebar → components/sidebar}/attribute-panel.js +3 -3
- package/dist/components/sidebar/attribute-panel.js.map +1 -0
- package/dist/components/sidebar/conversation-view.d.ts.map +1 -0
- package/dist/components/sidebar/conversation-view.js.map +1 -0
- package/dist/components/sidebar/detail-card.d.ts.map +1 -0
- package/dist/components/sidebar/detail-card.js.map +1 -0
- package/dist/components/sidebar/entity-detail-panel.d.ts +32 -0
- package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -0
- package/dist/{sidebar → components/sidebar}/entity-detail-panel.js +61 -49
- package/dist/components/sidebar/entity-detail-panel.js.map +1 -0
- package/dist/components/sidebar/events-list.d.ts +8 -0
- package/dist/components/sidebar/events-list.d.ts.map +1 -0
- package/dist/components/sidebar/events-list.js +16 -0
- package/dist/components/sidebar/events-list.js.map +1 -0
- package/dist/components/sidebar/resolve-hook-modal.d.ts.map +1 -0
- package/dist/components/sidebar/resolve-hook-modal.js.map +1 -0
- package/dist/components/stream-viewer.d.ts +18 -0
- package/dist/components/stream-viewer.d.ts.map +1 -0
- package/dist/{stream-viewer.js → components/stream-viewer.js} +1 -59
- package/dist/components/stream-viewer.js.map +1 -0
- package/dist/components/trace-viewer/components/map.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/map.js.map +1 -0
- package/dist/components/trace-viewer/components/markers.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/markers.js.map +1 -0
- package/dist/components/trace-viewer/components/node.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/node.js.map +1 -0
- package/dist/components/trace-viewer/components/search-input.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/search-input.js.map +1 -0
- package/dist/components/trace-viewer/components/search.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/search.js.map +1 -0
- package/dist/components/trace-viewer/components/span-detail-panel.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/span-detail-panel.js.map +1 -0
- package/dist/components/trace-viewer/components/ui.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/ui.js.map +1 -0
- package/dist/components/trace-viewer/components/zoom-button.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/zoom-button.js.map +1 -0
- package/dist/components/trace-viewer/components/zoom-icons.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/zoom-icons.js.map +1 -0
- package/dist/components/trace-viewer/context.d.ts.map +1 -0
- package/dist/components/trace-viewer/context.js.map +1 -0
- package/dist/components/trace-viewer/index.d.ts.map +1 -0
- package/dist/components/trace-viewer/index.js.map +1 -0
- package/dist/components/trace-viewer/trace-viewer.d.ts.map +1 -0
- package/dist/components/trace-viewer/trace-viewer.js.map +1 -0
- package/dist/components/trace-viewer/types.d.ts.map +1 -0
- package/dist/components/trace-viewer/types.js.map +1 -0
- package/dist/components/trace-viewer/util/constants.d.ts.map +1 -0
- package/dist/components/trace-viewer/util/constants.js.map +1 -0
- package/dist/components/trace-viewer/util/scrollbar-width.d.ts.map +1 -0
- package/dist/components/trace-viewer/util/scrollbar-width.js.map +1 -0
- package/dist/{trace-viewer → components/trace-viewer}/util/timing.d.ts +1 -1
- package/dist/components/trace-viewer/util/timing.d.ts.map +1 -0
- package/dist/{trace-viewer → components/trace-viewer}/util/timing.js +1 -1
- package/dist/components/trace-viewer/util/timing.js.map +1 -0
- package/dist/components/trace-viewer/util/tree.d.ts.map +1 -0
- package/dist/components/trace-viewer/util/tree.js.map +1 -0
- package/dist/components/trace-viewer/util/use-immediate-style.d.ts.map +1 -0
- package/dist/components/trace-viewer/util/use-immediate-style.js.map +1 -0
- package/dist/components/trace-viewer/util/use-streaming-spans.d.ts.map +1 -0
- package/dist/components/trace-viewer/util/use-streaming-spans.js.map +1 -0
- package/dist/components/trace-viewer/util/use-trackpad-zoom.d.ts.map +1 -0
- package/dist/components/trace-viewer/util/use-trackpad-zoom.js.map +1 -0
- package/dist/components/trace-viewer/worker.d.ts.map +1 -0
- package/dist/components/trace-viewer/worker.js.map +1 -0
- package/dist/components/workflow-trace-view.d.ts +24 -0
- package/dist/components/workflow-trace-view.d.ts.map +1 -0
- package/dist/components/workflow-trace-view.js +152 -0
- package/dist/components/workflow-trace-view.js.map +1 -0
- package/dist/components/workflow-traces/event-colors.d.ts.map +1 -0
- package/dist/components/workflow-traces/event-colors.js.map +1 -0
- package/dist/components/workflow-traces/trace-colors.d.ts.map +1 -0
- package/dist/components/workflow-traces/trace-colors.js.map +1 -0
- package/dist/components/workflow-traces/trace-span-construction.d.ts.map +1 -0
- package/dist/components/workflow-traces/trace-span-construction.js.map +1 -0
- package/dist/components/workflow-traces/trace-time-utils.d.ts.map +1 -0
- package/dist/components/workflow-traces/trace-time-utils.js.map +1 -0
- package/dist/index.d.ts +3 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -9
- package/dist/index.js.map +1 -1
- package/package.json +15 -11
- package/src/components/error-boundary.tsx +79 -0
- package/src/components/event-list-view.tsx +429 -0
- package/src/components/hook-actions.tsx +167 -0
- package/src/components/index.d.ts +1 -0
- package/src/components/index.ts +23 -0
- package/src/components/run-trace-view.tsx +75 -0
- package/src/components/sidebar/attribute-panel.tsx +938 -0
- package/src/components/sidebar/conversation-view.tsx +235 -0
- package/src/components/sidebar/detail-card.tsx +43 -0
- package/src/components/sidebar/entity-detail-panel.tsx +338 -0
- package/src/components/sidebar/events-list.tsx +119 -0
- package/src/components/sidebar/resolve-hook-modal.tsx +219 -0
- package/src/components/stream-viewer.tsx +143 -0
- package/src/components/trace-viewer/components/map.tsx +226 -0
- package/src/components/trace-viewer/components/markers.tsx +564 -0
- package/src/components/trace-viewer/components/node.tsx +259 -0
- package/src/components/trace-viewer/components/search-input.tsx +52 -0
- package/src/components/trace-viewer/components/search.tsx +47 -0
- package/src/components/trace-viewer/components/span-detail-panel.tsx +650 -0
- package/src/components/trace-viewer/components/ui.tsx +156 -0
- package/src/components/trace-viewer/components/zoom-button.tsx +61 -0
- package/src/components/trace-viewer/components/zoom-icons.tsx +65 -0
- package/src/components/trace-viewer/context.tsx +633 -0
- package/src/components/trace-viewer/index.tsx +4 -0
- package/src/components/trace-viewer/modules.d.ts +16 -0
- package/src/components/trace-viewer/trace-viewer.module.css +1292 -0
- package/src/components/trace-viewer/trace-viewer.tsx +448 -0
- package/src/components/trace-viewer/types.ts +234 -0
- package/src/components/trace-viewer/util/constants.ts +8 -0
- package/src/components/trace-viewer/util/scrollbar-width.ts +13 -0
- package/src/components/trace-viewer/util/timing.ts +33 -0
- package/src/components/trace-viewer/util/tree.ts +277 -0
- package/src/components/trace-viewer/util/use-immediate-style.ts +38 -0
- package/src/components/trace-viewer/util/use-streaming-spans.ts +415 -0
- package/src/components/trace-viewer/util/use-trackpad-zoom.tsx +51 -0
- package/src/components/trace-viewer/worker.ts +128 -0
- package/src/components/ui/alert.tsx +59 -0
- package/src/components/ui/card.tsx +88 -0
- package/src/components/ui/error-card.tsx +65 -0
- package/src/components/ui/skeleton.tsx +15 -0
- package/src/components/workflow-trace-view.tsx +306 -0
- package/src/components/workflow-traces/event-colors.ts +94 -0
- package/src/components/workflow-traces/trace-colors.ts +112 -0
- package/src/components/workflow-traces/trace-span-construction.ts +299 -0
- package/src/components/workflow-traces/trace-time-utils.ts +50 -0
- package/src/hooks/use-dark-mode.ts +34 -0
- package/src/index.d.ts +1 -0
- package/src/index.ts +29 -0
- package/src/lib/event-analysis.ts +231 -0
- package/src/lib/utils.ts +166 -0
- package/dist/api/workflow-api-client.d.ts +0 -543
- package/dist/api/workflow-api-client.d.ts.map +0 -1
- package/dist/api/workflow-api-client.js +0 -953
- package/dist/api/workflow-api-client.js.map +0 -1
- package/dist/api/workflow-server-actions.d.ts +0 -230
- package/dist/api/workflow-server-actions.d.ts.map +0 -1
- package/dist/api/workflow-server-actions.js +0 -861
- package/dist/api/workflow-server-actions.js.map +0 -1
- package/dist/error-boundary.d.ts.map +0 -1
- package/dist/error-boundary.js.map +0 -1
- package/dist/event-list-view.d.ts.map +0 -1
- package/dist/event-list-view.js.map +0 -1
- package/dist/hook-actions.d.ts.map +0 -1
- package/dist/hook-actions.js.map +0 -1
- package/dist/run-trace-view.d.ts +0 -8
- package/dist/run-trace-view.d.ts.map +0 -1
- package/dist/run-trace-view.js +0 -15
- package/dist/run-trace-view.js.map +0 -1
- package/dist/sidebar/attribute-panel.d.ts.map +0 -1
- package/dist/sidebar/attribute-panel.js.map +0 -1
- package/dist/sidebar/conversation-view.d.ts.map +0 -1
- package/dist/sidebar/conversation-view.js.map +0 -1
- package/dist/sidebar/detail-card.d.ts.map +0 -1
- package/dist/sidebar/detail-card.js.map +0 -1
- package/dist/sidebar/entity-detail-panel.d.ts +0 -12
- package/dist/sidebar/entity-detail-panel.d.ts.map +0 -1
- package/dist/sidebar/entity-detail-panel.js.map +0 -1
- package/dist/sidebar/events-list.d.ts +0 -9
- package/dist/sidebar/events-list.d.ts.map +0 -1
- package/dist/sidebar/events-list.js +0 -36
- package/dist/sidebar/events-list.js.map +0 -1
- package/dist/sidebar/resolve-hook-modal.d.ts.map +0 -1
- package/dist/sidebar/resolve-hook-modal.js.map +0 -1
- package/dist/stream-viewer.d.ts +0 -13
- package/dist/stream-viewer.d.ts.map +0 -1
- package/dist/stream-viewer.js.map +0 -1
- package/dist/trace-viewer/components/map.d.ts.map +0 -1
- package/dist/trace-viewer/components/map.js.map +0 -1
- package/dist/trace-viewer/components/markers.d.ts.map +0 -1
- package/dist/trace-viewer/components/markers.js.map +0 -1
- package/dist/trace-viewer/components/node.d.ts.map +0 -1
- package/dist/trace-viewer/components/node.js.map +0 -1
- package/dist/trace-viewer/components/search-input.d.ts.map +0 -1
- package/dist/trace-viewer/components/search-input.js.map +0 -1
- package/dist/trace-viewer/components/search.d.ts.map +0 -1
- package/dist/trace-viewer/components/search.js.map +0 -1
- package/dist/trace-viewer/components/span-detail-panel.d.ts.map +0 -1
- package/dist/trace-viewer/components/span-detail-panel.js.map +0 -1
- package/dist/trace-viewer/components/ui.d.ts.map +0 -1
- package/dist/trace-viewer/components/ui.js.map +0 -1
- package/dist/trace-viewer/components/zoom-button.d.ts.map +0 -1
- package/dist/trace-viewer/components/zoom-button.js.map +0 -1
- package/dist/trace-viewer/components/zoom-icons.d.ts.map +0 -1
- package/dist/trace-viewer/components/zoom-icons.js.map +0 -1
- package/dist/trace-viewer/context.d.ts.map +0 -1
- package/dist/trace-viewer/context.js.map +0 -1
- package/dist/trace-viewer/index.d.ts.map +0 -1
- package/dist/trace-viewer/index.js.map +0 -1
- package/dist/trace-viewer/trace-viewer.d.ts.map +0 -1
- package/dist/trace-viewer/trace-viewer.js.map +0 -1
- package/dist/trace-viewer/types.d.ts.map +0 -1
- package/dist/trace-viewer/types.js.map +0 -1
- package/dist/trace-viewer/util/constants.d.ts.map +0 -1
- package/dist/trace-viewer/util/constants.js.map +0 -1
- package/dist/trace-viewer/util/scrollbar-width.d.ts.map +0 -1
- package/dist/trace-viewer/util/scrollbar-width.js.map +0 -1
- package/dist/trace-viewer/util/timing.d.ts.map +0 -1
- package/dist/trace-viewer/util/timing.js.map +0 -1
- package/dist/trace-viewer/util/tree.d.ts.map +0 -1
- package/dist/trace-viewer/util/tree.js.map +0 -1
- package/dist/trace-viewer/util/use-immediate-style.d.ts.map +0 -1
- package/dist/trace-viewer/util/use-immediate-style.js.map +0 -1
- package/dist/trace-viewer/util/use-streaming-spans.d.ts.map +0 -1
- package/dist/trace-viewer/util/use-streaming-spans.js.map +0 -1
- package/dist/trace-viewer/util/use-trackpad-zoom.d.ts.map +0 -1
- package/dist/trace-viewer/util/use-trackpad-zoom.js.map +0 -1
- package/dist/trace-viewer/worker.d.ts.map +0 -1
- package/dist/trace-viewer/worker.js.map +0 -1
- package/dist/workflow-trace-view.d.ts +0 -14
- package/dist/workflow-trace-view.d.ts.map +0 -1
- package/dist/workflow-trace-view.js +0 -135
- package/dist/workflow-trace-view.js.map +0 -1
- package/dist/workflow-traces/event-colors.d.ts.map +0 -1
- package/dist/workflow-traces/event-colors.js.map +0 -1
- package/dist/workflow-traces/trace-colors.d.ts.map +0 -1
- package/dist/workflow-traces/trace-colors.js.map +0 -1
- package/dist/workflow-traces/trace-span-construction.d.ts.map +0 -1
- package/dist/workflow-traces/trace-span-construction.js.map +0 -1
- package/dist/workflow-traces/trace-time-utils.d.ts.map +0 -1
- package/dist/workflow-traces/trace-time-utils.js.map +0 -1
- package/server/README.md +0 -1
- package/server/package.json +0 -4
- /package/dist/{error-boundary.d.ts → components/error-boundary.d.ts} +0 -0
- /package/dist/{sidebar → components/sidebar}/attribute-panel.d.ts +0 -0
- /package/dist/{sidebar → components/sidebar}/conversation-view.d.ts +0 -0
- /package/dist/{sidebar → components/sidebar}/conversation-view.js +0 -0
- /package/dist/{sidebar → components/sidebar}/detail-card.d.ts +0 -0
- /package/dist/{sidebar → components/sidebar}/detail-card.js +0 -0
- /package/dist/{sidebar → components/sidebar}/resolve-hook-modal.d.ts +0 -0
- /package/dist/{sidebar → components/sidebar}/resolve-hook-modal.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/map.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/map.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/markers.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/markers.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/node.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/node.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/search-input.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/search-input.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/search.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/search.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/span-detail-panel.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/span-detail-panel.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/ui.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/ui.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/zoom-button.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/zoom-button.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/zoom-icons.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/zoom-icons.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/context.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/context.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/index.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/index.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/trace-viewer.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/trace-viewer.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/trace-viewer.module.css +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/types.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/types.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/constants.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/constants.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/scrollbar-width.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/scrollbar-width.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/tree.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/tree.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/use-immediate-style.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/use-immediate-style.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/use-streaming-spans.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/use-streaming-spans.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/use-trackpad-zoom.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/use-trackpad-zoom.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/worker.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/worker.js +0 -0
- /package/dist/{workflow-traces → components/workflow-traces}/event-colors.d.ts +0 -0
- /package/dist/{workflow-traces → components/workflow-traces}/event-colors.js +0 -0
- /package/dist/{workflow-traces → components/workflow-traces}/trace-colors.d.ts +0 -0
- /package/dist/{workflow-traces → components/workflow-traces}/trace-colors.js +0 -0
- /package/dist/{workflow-traces → components/workflow-traces}/trace-span-construction.d.ts +0 -0
- /package/dist/{workflow-traces → components/workflow-traces}/trace-span-construction.js +0 -0
- /package/dist/{workflow-traces → components/workflow-traces}/trace-time-utils.d.ts +0 -0
- /package/dist/{workflow-traces → components/workflow-traces}/trace-time-utils.js +0 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import type { ModelMessage } from 'ai';
|
|
2
|
+
import { Streamdown } from 'streamdown';
|
|
3
|
+
|
|
4
|
+
interface ConversationViewProps {
|
|
5
|
+
messages: ModelMessage[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function ConversationView({ messages }: ConversationViewProps) {
|
|
9
|
+
if (messages.length === 0) {
|
|
10
|
+
return (
|
|
11
|
+
<div
|
|
12
|
+
className="text-center py-8 text-[11px]"
|
|
13
|
+
style={{ color: 'var(--ds-gray-600)' }}
|
|
14
|
+
>
|
|
15
|
+
No messages
|
|
16
|
+
</div>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className="flex flex-col gap-3 p-3">
|
|
22
|
+
{messages.map((message, index) => (
|
|
23
|
+
<MessageBubble key={index} message={message} />
|
|
24
|
+
))}
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function MessageBubble({ message }: { message: ModelMessage }) {
|
|
30
|
+
const role = message.role;
|
|
31
|
+
const parts = parseContent(message.content);
|
|
32
|
+
const style = getRoleStyle(role);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div
|
|
36
|
+
className="rounded-md border text-[11px]"
|
|
37
|
+
style={{
|
|
38
|
+
backgroundColor: style.bg,
|
|
39
|
+
borderColor: style.border,
|
|
40
|
+
}}
|
|
41
|
+
>
|
|
42
|
+
{/* Role header */}
|
|
43
|
+
<div
|
|
44
|
+
className="px-2.5 py-1 border-b text-[10px] font-medium uppercase tracking-wide"
|
|
45
|
+
style={{
|
|
46
|
+
borderColor: style.border,
|
|
47
|
+
color: style.label,
|
|
48
|
+
}}
|
|
49
|
+
>
|
|
50
|
+
{role}
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
{/* Content */}
|
|
54
|
+
<div className="px-2.5 py-2 space-y-2">
|
|
55
|
+
{parts.map((part, i) => (
|
|
56
|
+
<ContentPart key={i} part={part} role={role} />
|
|
57
|
+
))}
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface ParsedPart {
|
|
64
|
+
type: 'text' | 'tool-call' | 'tool-result';
|
|
65
|
+
text?: string;
|
|
66
|
+
toolName?: string;
|
|
67
|
+
input?: unknown;
|
|
68
|
+
output?: unknown;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function ContentPart({ part, role }: { part: ParsedPart; role: string }) {
|
|
72
|
+
if (part.type === 'text') {
|
|
73
|
+
if (!part.text) return null;
|
|
74
|
+
|
|
75
|
+
// Use Streamdown for assistant messages (they often contain markdown)
|
|
76
|
+
if (role === 'assistant') {
|
|
77
|
+
return (
|
|
78
|
+
<div
|
|
79
|
+
className="prose prose-sm max-w-none text-[11px] [&>*:first-child]:mt-0 [&>*:last-child]:mb-0"
|
|
80
|
+
style={{ color: 'var(--ds-gray-1000)' }}
|
|
81
|
+
>
|
|
82
|
+
<Streamdown>{part.text}</Streamdown>
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div
|
|
89
|
+
className="whitespace-pre-wrap break-words"
|
|
90
|
+
style={{ color: 'var(--ds-gray-1000)' }}
|
|
91
|
+
>
|
|
92
|
+
{part.text}
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (part.type === 'tool-call') {
|
|
98
|
+
return (
|
|
99
|
+
<div
|
|
100
|
+
className="rounded border px-2 py-1.5"
|
|
101
|
+
style={{
|
|
102
|
+
backgroundColor: 'var(--ds-purple-100)',
|
|
103
|
+
borderColor: 'var(--ds-purple-300)',
|
|
104
|
+
}}
|
|
105
|
+
>
|
|
106
|
+
<div className="flex items-center gap-1.5 text-[10px] font-medium">
|
|
107
|
+
<span>🔧</span>
|
|
108
|
+
<span style={{ color: 'var(--ds-purple-900)' }}>{part.toolName}</span>
|
|
109
|
+
</div>
|
|
110
|
+
{part.input != null && (
|
|
111
|
+
<pre
|
|
112
|
+
className="mt-1.5 text-[10px] overflow-x-auto p-1.5 rounded"
|
|
113
|
+
style={{
|
|
114
|
+
backgroundColor: 'var(--ds-gray-100)',
|
|
115
|
+
color: 'var(--ds-gray-800)',
|
|
116
|
+
}}
|
|
117
|
+
>
|
|
118
|
+
{typeof part.input === 'string'
|
|
119
|
+
? part.input
|
|
120
|
+
: JSON.stringify(part.input, null, 2)}
|
|
121
|
+
</pre>
|
|
122
|
+
)}
|
|
123
|
+
</div>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (part.type === 'tool-result') {
|
|
128
|
+
const outputText = formatOutput(part.output);
|
|
129
|
+
return (
|
|
130
|
+
<div
|
|
131
|
+
className="rounded border px-2 py-1.5"
|
|
132
|
+
style={{
|
|
133
|
+
backgroundColor: 'var(--ds-green-100)',
|
|
134
|
+
borderColor: 'var(--ds-green-300)',
|
|
135
|
+
}}
|
|
136
|
+
>
|
|
137
|
+
<div className="flex items-center gap-1.5 text-[10px] font-medium">
|
|
138
|
+
<span>✓</span>
|
|
139
|
+
<span style={{ color: 'var(--ds-green-900)' }}>
|
|
140
|
+
{part.toolName} result
|
|
141
|
+
</span>
|
|
142
|
+
</div>
|
|
143
|
+
{outputText && (
|
|
144
|
+
<pre
|
|
145
|
+
className="mt-1.5 text-[10px] overflow-x-auto max-h-[80px] p-1.5 rounded"
|
|
146
|
+
style={{
|
|
147
|
+
backgroundColor: 'var(--ds-gray-100)',
|
|
148
|
+
color: 'var(--ds-gray-800)',
|
|
149
|
+
}}
|
|
150
|
+
>
|
|
151
|
+
{outputText}
|
|
152
|
+
</pre>
|
|
153
|
+
)}
|
|
154
|
+
</div>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function getRoleStyle(role: string) {
|
|
162
|
+
switch (role) {
|
|
163
|
+
case 'user':
|
|
164
|
+
return {
|
|
165
|
+
bg: 'var(--ds-blue-100)',
|
|
166
|
+
border: 'var(--ds-blue-300)',
|
|
167
|
+
label: 'var(--ds-blue-700)',
|
|
168
|
+
};
|
|
169
|
+
case 'assistant':
|
|
170
|
+
return {
|
|
171
|
+
bg: 'var(--ds-gray-100)',
|
|
172
|
+
border: 'var(--ds-gray-300)',
|
|
173
|
+
label: 'var(--ds-gray-700)',
|
|
174
|
+
};
|
|
175
|
+
case 'system':
|
|
176
|
+
return {
|
|
177
|
+
bg: 'var(--ds-amber-100)',
|
|
178
|
+
border: 'var(--ds-amber-300)',
|
|
179
|
+
label: 'var(--ds-amber-700)',
|
|
180
|
+
};
|
|
181
|
+
case 'tool':
|
|
182
|
+
return {
|
|
183
|
+
bg: 'var(--ds-green-50)',
|
|
184
|
+
border: 'var(--ds-green-300)',
|
|
185
|
+
label: 'var(--ds-green-700)',
|
|
186
|
+
};
|
|
187
|
+
default:
|
|
188
|
+
return {
|
|
189
|
+
bg: 'var(--ds-gray-100)',
|
|
190
|
+
border: 'var(--ds-gray-300)',
|
|
191
|
+
label: 'var(--ds-gray-700)',
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function parseContent(content: unknown): ParsedPart[] {
|
|
197
|
+
if (typeof content === 'string') {
|
|
198
|
+
return [{ type: 'text', text: content }];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (Array.isArray(content)) {
|
|
202
|
+
return content.map((part): ParsedPart => {
|
|
203
|
+
if (typeof part === 'string') {
|
|
204
|
+
return { type: 'text', text: part };
|
|
205
|
+
}
|
|
206
|
+
if (part?.type === 'text') {
|
|
207
|
+
return { type: 'text', text: String(part.text ?? '') };
|
|
208
|
+
}
|
|
209
|
+
if (part?.type === 'tool-call') {
|
|
210
|
+
return {
|
|
211
|
+
type: 'tool-call',
|
|
212
|
+
toolName: part.toolName,
|
|
213
|
+
input: part.input,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
if (part?.type === 'tool-result') {
|
|
217
|
+
return {
|
|
218
|
+
type: 'tool-result',
|
|
219
|
+
toolName: part.toolName,
|
|
220
|
+
output: part.output,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
return { type: 'text', text: '' };
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function formatOutput(output: unknown): string | null {
|
|
231
|
+
if (output == null) return null;
|
|
232
|
+
const text =
|
|
233
|
+
typeof output === 'string' ? output : JSON.stringify(output, null, 2);
|
|
234
|
+
return text.length > 500 ? `${text.slice(0, 500)}...` : text;
|
|
235
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
export function DetailCard({
|
|
4
|
+
summary,
|
|
5
|
+
children,
|
|
6
|
+
}: {
|
|
7
|
+
summary: ReactNode;
|
|
8
|
+
children?: ReactNode;
|
|
9
|
+
}) {
|
|
10
|
+
return (
|
|
11
|
+
<details className="group">
|
|
12
|
+
<summary
|
|
13
|
+
className="cursor-pointer rounded-md border px-2.5 py-1.5 text-xs hover:brightness-95"
|
|
14
|
+
style={{
|
|
15
|
+
borderColor: 'var(--ds-gray-300)',
|
|
16
|
+
backgroundColor: 'var(--ds-gray-100)',
|
|
17
|
+
color: 'var(--ds-gray-900)',
|
|
18
|
+
}}
|
|
19
|
+
>
|
|
20
|
+
{summary}
|
|
21
|
+
</summary>
|
|
22
|
+
{/* Expanded content with connecting line */}
|
|
23
|
+
<div className="relative pl-6 mt-3">
|
|
24
|
+
{/* Curved connecting line - vertical part from summary */}
|
|
25
|
+
<div
|
|
26
|
+
className="absolute left-3 -top-3 w-px h-3"
|
|
27
|
+
style={{ backgroundColor: 'var(--ds-gray-400)' }}
|
|
28
|
+
/>
|
|
29
|
+
{/* Curved corner */}
|
|
30
|
+
<div
|
|
31
|
+
className="absolute left-3 top-0 w-3 h-3 border-l border-b rounded-bl-lg"
|
|
32
|
+
style={{ borderColor: 'var(--ds-gray-400)' }}
|
|
33
|
+
/>
|
|
34
|
+
{/* Horizontal part to content */}
|
|
35
|
+
<div
|
|
36
|
+
className="absolute left-6 top-3 w-0 h-px -translate-y-px"
|
|
37
|
+
style={{ backgroundColor: 'var(--ds-gray-400)' }}
|
|
38
|
+
/>
|
|
39
|
+
<div>{children}</div>
|
|
40
|
+
</div>
|
|
41
|
+
</details>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { Event, Hook, Step, WorkflowRun } from '@workflow/world';
|
|
4
|
+
import clsx from 'clsx';
|
|
5
|
+
import { Send, Zap } from 'lucide-react';
|
|
6
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
7
|
+
import { toast } from 'sonner';
|
|
8
|
+
import { useTraceViewer } from '../trace-viewer';
|
|
9
|
+
import { AttributePanel } from './attribute-panel';
|
|
10
|
+
import { EventsList } from './events-list';
|
|
11
|
+
import { ResolveHookModal } from './resolve-hook-modal';
|
|
12
|
+
|
|
13
|
+
// Type guards for runtime validation of span attribute data
|
|
14
|
+
function isStep(data: unknown): data is Step {
|
|
15
|
+
return data !== null && typeof data === 'object' && 'stepId' in data;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function isWorkflowRun(data: unknown): data is WorkflowRun {
|
|
19
|
+
return data !== null && typeof data === 'object' && 'runId' in data;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function isHook(data: unknown): data is Hook {
|
|
23
|
+
return data !== null && typeof data === 'object' && 'hookId' in data;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Info about the currently selected span
|
|
28
|
+
*/
|
|
29
|
+
export type SpanSelectionInfo = {
|
|
30
|
+
resource: 'run' | 'step' | 'hook' | 'sleep';
|
|
31
|
+
resourceId: string;
|
|
32
|
+
runId?: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Custom panel component for workflow traces that displays entity details
|
|
37
|
+
*/
|
|
38
|
+
export function EntityDetailPanel({
|
|
39
|
+
run,
|
|
40
|
+
onStreamClick,
|
|
41
|
+
spanDetailData,
|
|
42
|
+
spanDetailError,
|
|
43
|
+
spanDetailLoading,
|
|
44
|
+
onSpanSelect,
|
|
45
|
+
onWakeUpSleep,
|
|
46
|
+
onResolveHook,
|
|
47
|
+
}: {
|
|
48
|
+
run: WorkflowRun;
|
|
49
|
+
/** Callback when a stream reference is clicked */
|
|
50
|
+
onStreamClick?: (streamId: string) => void;
|
|
51
|
+
/** Pre-fetched span detail data for the selected span. */
|
|
52
|
+
spanDetailData: WorkflowRun | Step | Hook | Event | null;
|
|
53
|
+
/** Error from external span detail fetch. */
|
|
54
|
+
spanDetailError?: Error | null;
|
|
55
|
+
/** Loading state from external span detail fetch. */
|
|
56
|
+
spanDetailLoading?: boolean;
|
|
57
|
+
/** Callback when a span is selected. Use this to fetch data externally and pass via spanDetailData. */
|
|
58
|
+
onSpanSelect: (info: SpanSelectionInfo) => void;
|
|
59
|
+
/** Callback to wake up a pending sleep call. */
|
|
60
|
+
onWakeUpSleep?: (
|
|
61
|
+
runId: string,
|
|
62
|
+
correlationId: string
|
|
63
|
+
) => Promise<{ stoppedCount: number }>;
|
|
64
|
+
/** Callback to resolve a hook with a payload. */
|
|
65
|
+
onResolveHook?: (
|
|
66
|
+
hookToken: string,
|
|
67
|
+
payload: unknown,
|
|
68
|
+
hook?: Hook
|
|
69
|
+
) => Promise<void>;
|
|
70
|
+
}): React.JSX.Element | null {
|
|
71
|
+
const { state } = useTraceViewer();
|
|
72
|
+
const { selected } = state;
|
|
73
|
+
const [stoppingSleep, setStoppingSleep] = useState(false);
|
|
74
|
+
const [showResolveHookModal, setShowResolveHookModal] = useState(false);
|
|
75
|
+
const [resolvingHook, setResolvingHook] = useState(false);
|
|
76
|
+
|
|
77
|
+
const data = selected?.span.attributes?.data;
|
|
78
|
+
|
|
79
|
+
// Stable ref for onSpanSelect to avoid re-render loops when parent
|
|
80
|
+
// doesn't memoize the callback with useCallback.
|
|
81
|
+
const onSpanSelectRef = useRef(onSpanSelect);
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
onSpanSelectRef.current = onSpanSelect;
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Determine resource ID and runId (needed for steps)
|
|
87
|
+
// Uses type guards to validate the data shape matches the expected resource type
|
|
88
|
+
const { resource, resourceId, runId } = useMemo(() => {
|
|
89
|
+
const resource = selected?.span.attributes?.resource;
|
|
90
|
+
if (resource === 'step' && isStep(data)) {
|
|
91
|
+
return { resource: 'step', resourceId: data.stepId, runId: data.runId };
|
|
92
|
+
} else if (resource === 'run' && isWorkflowRun(data)) {
|
|
93
|
+
return { resource: 'run', resourceId: data.runId, runId: undefined };
|
|
94
|
+
} else if (resource === 'hook' && isHook(data)) {
|
|
95
|
+
return { resource: 'hook', resourceId: data.hookId, runId: undefined };
|
|
96
|
+
} else if (resource === 'sleep') {
|
|
97
|
+
return {
|
|
98
|
+
resource: 'sleep',
|
|
99
|
+
resourceId: selected?.span?.spanId,
|
|
100
|
+
runId: undefined,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return { resource: undefined, resourceId: undefined, runId: undefined };
|
|
104
|
+
}, [selected, data]);
|
|
105
|
+
|
|
106
|
+
// Notify parent when span selection changes
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (
|
|
109
|
+
resource &&
|
|
110
|
+
resourceId &&
|
|
111
|
+
['run', 'step', 'hook', 'sleep'].includes(resource)
|
|
112
|
+
) {
|
|
113
|
+
onSpanSelectRef.current({
|
|
114
|
+
resource: resource as 'run' | 'step' | 'hook' | 'sleep',
|
|
115
|
+
resourceId,
|
|
116
|
+
runId,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}, [resource, resourceId, runId]);
|
|
120
|
+
|
|
121
|
+
// Check if this sleep is still pending and can be woken up
|
|
122
|
+
// Requirements: no wait_completed event, resumeAt is in the future, run is not terminal
|
|
123
|
+
const spanEvents = selected?.span.events;
|
|
124
|
+
const spanEventsLength = spanEvents?.length ?? 0;
|
|
125
|
+
const canWakeUp = useMemo(() => {
|
|
126
|
+
void spanEventsLength; // Force dependency on length for reactivity
|
|
127
|
+
if (resource !== 'sleep' || !spanEvents) return false;
|
|
128
|
+
|
|
129
|
+
// Check run is not in a terminal state
|
|
130
|
+
const terminalStates = ['completed', 'failed', 'cancelled'];
|
|
131
|
+
if (terminalStates.includes(run.status)) return false;
|
|
132
|
+
|
|
133
|
+
// Check if wait has already completed
|
|
134
|
+
const hasWaitCompleted = spanEvents.some(
|
|
135
|
+
(e) => e.name === 'wait_completed'
|
|
136
|
+
);
|
|
137
|
+
if (hasWaitCompleted) return false;
|
|
138
|
+
|
|
139
|
+
// Check if resumeAt is in the future
|
|
140
|
+
const waitCreatedEvent = spanEvents.find((e) => e.name === 'wait_created');
|
|
141
|
+
const eventData = waitCreatedEvent?.attributes?.eventData as
|
|
142
|
+
| { resumeAt?: string | Date }
|
|
143
|
+
| undefined;
|
|
144
|
+
const resumeAt = eventData?.resumeAt;
|
|
145
|
+
if (!resumeAt) return false;
|
|
146
|
+
|
|
147
|
+
const resumeAtDate = new Date(resumeAt);
|
|
148
|
+
return resumeAtDate.getTime() > Date.now();
|
|
149
|
+
}, [resource, spanEvents, spanEventsLength, run.status]);
|
|
150
|
+
|
|
151
|
+
// Check if this hook can be resolved (not yet resolved, run is not terminal)
|
|
152
|
+
const canResolveHook = useMemo(() => {
|
|
153
|
+
void spanEventsLength; // Force dependency on length for reactivity
|
|
154
|
+
if (resource !== 'hook' || !spanEvents) return false;
|
|
155
|
+
|
|
156
|
+
// Check run is not in a terminal state
|
|
157
|
+
const terminalStates = ['completed', 'failed', 'cancelled'];
|
|
158
|
+
if (terminalStates.includes(run.status)) return false;
|
|
159
|
+
|
|
160
|
+
// Check if hook has already been disposed
|
|
161
|
+
const hasHookDisposed = spanEvents.some((e) => e.name === 'hook_disposed');
|
|
162
|
+
if (hasHookDisposed) return false;
|
|
163
|
+
|
|
164
|
+
// Hook can be resolved
|
|
165
|
+
return true;
|
|
166
|
+
}, [resource, spanEvents, spanEventsLength, run.status]);
|
|
167
|
+
|
|
168
|
+
const error = spanDetailError ?? undefined;
|
|
169
|
+
const loading = spanDetailLoading ?? false;
|
|
170
|
+
|
|
171
|
+
// Get the hook token for resolving (prefer fetched data when available)
|
|
172
|
+
const hookToken = useMemo(() => {
|
|
173
|
+
if (resource !== 'hook') return undefined;
|
|
174
|
+
const candidate = spanDetailData ?? data;
|
|
175
|
+
return isHook(candidate) ? candidate.token : undefined;
|
|
176
|
+
}, [resource, spanDetailData, data]);
|
|
177
|
+
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
if (error && selected && resource) {
|
|
180
|
+
toast.error(`Failed to load ${resource} details`, {
|
|
181
|
+
description: error.message,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}, [error, resource, selected]);
|
|
185
|
+
|
|
186
|
+
const handleWakeUp = async () => {
|
|
187
|
+
if (stoppingSleep || !resourceId) return;
|
|
188
|
+
if (!onWakeUpSleep) {
|
|
189
|
+
toast.error('Unable to wake up sleep', {
|
|
190
|
+
description: 'No wake-up handler provided.',
|
|
191
|
+
});
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
setStoppingSleep(true);
|
|
197
|
+
const result = await onWakeUpSleep(run.runId, resourceId);
|
|
198
|
+
if (result.stoppedCount > 0) {
|
|
199
|
+
toast.success('Run woken up', {
|
|
200
|
+
description:
|
|
201
|
+
'The sleep call has been interrupted and the run woken up.',
|
|
202
|
+
});
|
|
203
|
+
} else {
|
|
204
|
+
toast.info('Sleep already completed', {
|
|
205
|
+
description: 'This sleep call has already finished.',
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
} catch (err) {
|
|
209
|
+
console.error('Failed to wake up run:', err);
|
|
210
|
+
toast.error('Failed to wake up run', {
|
|
211
|
+
description:
|
|
212
|
+
err instanceof Error ? err.message : 'An unknown error occurred',
|
|
213
|
+
});
|
|
214
|
+
} finally {
|
|
215
|
+
setStoppingSleep(false);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
const handleResolveHook = useCallback(
|
|
220
|
+
async (payload: unknown) => {
|
|
221
|
+
if (resolvingHook) return;
|
|
222
|
+
if (!onResolveHook) {
|
|
223
|
+
toast.error('Unable to resolve hook', {
|
|
224
|
+
description: 'No resolve handler provided.',
|
|
225
|
+
});
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (!hookToken) {
|
|
229
|
+
toast.error('Unable to resolve hook', {
|
|
230
|
+
description:
|
|
231
|
+
'Missing hook token. Try refreshing the run data and retry.',
|
|
232
|
+
});
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
setResolvingHook(true);
|
|
238
|
+
const candidate = spanDetailData ?? data;
|
|
239
|
+
const hook = isHook(candidate) ? candidate : undefined;
|
|
240
|
+
await onResolveHook(hookToken, payload, hook);
|
|
241
|
+
toast.success('Hook resolved', {
|
|
242
|
+
description: 'The payload has been sent and the hook resolved.',
|
|
243
|
+
});
|
|
244
|
+
setShowResolveHookModal(false);
|
|
245
|
+
} catch (err) {
|
|
246
|
+
console.error('Failed to resolve hook:', err);
|
|
247
|
+
toast.error('Failed to resolve hook', {
|
|
248
|
+
description:
|
|
249
|
+
err instanceof Error ? err.message : 'An unknown error occurred',
|
|
250
|
+
});
|
|
251
|
+
} finally {
|
|
252
|
+
setResolvingHook(false);
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
[onResolveHook, hookToken, resolvingHook, spanDetailData, data]
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
if (!selected || !resource || !resourceId) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const displayData = (spanDetailData ?? data) as
|
|
263
|
+
| WorkflowRun
|
|
264
|
+
| Step
|
|
265
|
+
| Hook
|
|
266
|
+
| Event;
|
|
267
|
+
|
|
268
|
+
return (
|
|
269
|
+
<div className={clsx('flex flex-col px-2')}>
|
|
270
|
+
{/* Wake up button for pending sleep calls */}
|
|
271
|
+
{resource === 'sleep' && canWakeUp && (
|
|
272
|
+
<div className="mb-3 pb-3 border-b border-gray-200 dark:border-gray-700">
|
|
273
|
+
<button
|
|
274
|
+
type="button"
|
|
275
|
+
onClick={handleWakeUp}
|
|
276
|
+
disabled={stoppingSleep}
|
|
277
|
+
className={clsx(
|
|
278
|
+
'flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md w-full',
|
|
279
|
+
'bg-amber-100 dark:bg-amber-900/30 text-amber-800 dark:text-amber-200',
|
|
280
|
+
'hover:bg-amber-200 dark:hover:bg-amber-900/50',
|
|
281
|
+
'disabled:opacity-50 disabled:cursor-not-allowed',
|
|
282
|
+
'transition-colors',
|
|
283
|
+
stoppingSleep ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'
|
|
284
|
+
)}
|
|
285
|
+
>
|
|
286
|
+
<Zap className="h-4 w-4" />
|
|
287
|
+
{stoppingSleep ? 'Waking up...' : 'Wake up'}
|
|
288
|
+
</button>
|
|
289
|
+
<p className="mt-1.5 text-xs text-gray-500 dark:text-gray-400">
|
|
290
|
+
Interrupt this sleep call and wake up the run.
|
|
291
|
+
</p>
|
|
292
|
+
</div>
|
|
293
|
+
)}
|
|
294
|
+
|
|
295
|
+
{/* Resolve hook button for pending hooks */}
|
|
296
|
+
{resource === 'hook' && canResolveHook && (
|
|
297
|
+
<div className="mb-3 pb-3 border-b border-gray-200 dark:border-gray-700">
|
|
298
|
+
<button
|
|
299
|
+
type="button"
|
|
300
|
+
onClick={() => setShowResolveHookModal(true)}
|
|
301
|
+
disabled={resolvingHook}
|
|
302
|
+
className={clsx(
|
|
303
|
+
'flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md w-full',
|
|
304
|
+
'bg-primary text-primary-foreground hover:bg-primary/90',
|
|
305
|
+
'disabled:opacity-50 disabled:cursor-not-allowed',
|
|
306
|
+
'transition-colors',
|
|
307
|
+
resolvingHook ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'
|
|
308
|
+
)}
|
|
309
|
+
>
|
|
310
|
+
<Send className="h-4 w-4" />
|
|
311
|
+
Resolve Hook
|
|
312
|
+
</button>
|
|
313
|
+
<p className="mt-1.5 text-xs text-gray-500 dark:text-gray-400">
|
|
314
|
+
Send a JSON payload to resolve this hook.
|
|
315
|
+
</p>
|
|
316
|
+
</div>
|
|
317
|
+
)}
|
|
318
|
+
|
|
319
|
+
{/* Resolve Hook Modal */}
|
|
320
|
+
<ResolveHookModal
|
|
321
|
+
isOpen={showResolveHookModal}
|
|
322
|
+
onClose={() => setShowResolveHookModal(false)}
|
|
323
|
+
onSubmit={handleResolveHook}
|
|
324
|
+
isSubmitting={resolvingHook}
|
|
325
|
+
/>
|
|
326
|
+
|
|
327
|
+
{/* Content display */}
|
|
328
|
+
<AttributePanel
|
|
329
|
+
data={displayData}
|
|
330
|
+
expiredAt={run.expiredAt}
|
|
331
|
+
isLoading={loading}
|
|
332
|
+
error={error ?? undefined}
|
|
333
|
+
onStreamClick={onStreamClick}
|
|
334
|
+
/>
|
|
335
|
+
{resource !== 'run' && <EventsList events={selected.span.events} />}
|
|
336
|
+
</div>
|
|
337
|
+
);
|
|
338
|
+
}
|