@flanksource/clicky-ui 0.3.46 → 0.3.47
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/NOTICE.md +11 -0
- package/dist/ai.cjs +13 -8
- package/dist/ai.cjs.map +1 -1
- package/dist/ai.js +7 -2
- package/dist/ai.js.map +1 -1
- package/dist/components/ErrorWrapper.cjs +2 -3
- package/dist/components/ErrorWrapper.cjs.map +1 -1
- package/dist/components/ErrorWrapper.d.ts.map +1 -1
- package/dist/components/ErrorWrapper.js +3 -4
- package/dist/components/ErrorWrapper.js.map +1 -1
- package/dist/data/Clicky.cjs +3 -0
- package/dist/data/Clicky.cjs.map +1 -1
- package/dist/data/Clicky.js +3 -0
- package/dist/data/Clicky.js.map +1 -1
- package/dist/data/FilterPill.cjs +15 -2
- package/dist/data/FilterPill.cjs.map +1 -1
- package/dist/data/FilterPill.d.ts +1 -1
- package/dist/data/FilterPill.d.ts.map +1 -1
- package/dist/data/FilterPill.js +15 -2
- package/dist/data/FilterPill.js.map +1 -1
- package/dist/data/LogViewer.cjs +27 -8
- package/dist/data/LogViewer.cjs.map +1 -1
- package/dist/data/LogViewer.d.ts +11 -3
- package/dist/data/LogViewer.d.ts.map +1 -1
- package/dist/data/LogViewer.js +27 -8
- package/dist/data/LogViewer.js.map +1 -1
- package/dist/data/Properties.cjs +2 -15
- package/dist/data/Properties.cjs.map +1 -1
- package/dist/data/Properties.d.ts +4 -0
- package/dist/data/Properties.d.ts.map +1 -1
- package/dist/data/Properties.js +2 -15
- package/dist/data/Properties.js.map +1 -1
- package/dist/data/PropertyValue.cjs +37 -0
- package/dist/data/PropertyValue.cjs.map +1 -0
- package/dist/data/PropertyValue.d.ts +6 -0
- package/dist/data/PropertyValue.d.ts.map +1 -0
- package/dist/data/PropertyValue.js +37 -0
- package/dist/data/PropertyValue.js.map +1 -0
- package/dist/data/Tree.cjs +4 -3
- package/dist/data/Tree.cjs.map +1 -1
- package/dist/data/Tree.d.ts +3 -1
- package/dist/data/Tree.d.ts.map +1 -1
- package/dist/data/Tree.js +4 -3
- package/dist/data/Tree.js.map +1 -1
- package/dist/data/ai/ChatFab.cjs +1 -1
- package/dist/data/ai/ChatFab.cjs.map +1 -1
- package/dist/data/ai/ChatFab.js +1 -1
- package/dist/data/ai/ChatFab.js.map +1 -1
- package/dist/data/ai/ChatWindow.cjs +106 -171
- package/dist/data/ai/ChatWindow.cjs.map +1 -1
- package/dist/data/ai/ChatWindow.d.ts +6 -2
- package/dist/data/ai/ChatWindow.d.ts.map +1 -1
- package/dist/data/ai/ChatWindow.header.cjs +104 -0
- package/dist/data/ai/ChatWindow.header.cjs.map +1 -0
- package/dist/data/ai/ChatWindow.header.d.ts +29 -0
- package/dist/data/ai/ChatWindow.header.d.ts.map +1 -0
- package/dist/data/ai/ChatWindow.header.js +104 -0
- package/dist/data/ai/ChatWindow.header.js.map +1 -0
- package/dist/data/ai/ChatWindow.js +107 -172
- package/dist/data/ai/ChatWindow.js.map +1 -1
- package/dist/data/ai/ChatWindow.placement.cjs +95 -0
- package/dist/data/ai/ChatWindow.placement.cjs.map +1 -0
- package/dist/data/ai/ChatWindow.placement.d.ts +32 -0
- package/dist/data/ai/ChatWindow.placement.d.ts.map +1 -0
- package/dist/data/ai/ChatWindow.placement.js +95 -0
- package/dist/data/ai/ChatWindow.placement.js.map +1 -0
- package/dist/data/ai/SessionChatComposer.cjs +3 -1
- package/dist/data/ai/SessionChatComposer.cjs.map +1 -1
- package/dist/data/ai/SessionChatComposer.d.ts +4 -1
- package/dist/data/ai/SessionChatComposer.d.ts.map +1 -1
- package/dist/data/ai/SessionChatComposer.js +3 -1
- package/dist/data/ai/SessionChatComposer.js.map +1 -1
- package/dist/data/ai/SessionInspector.cjs +68 -72
- package/dist/data/ai/SessionInspector.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.composer-slot.cjs +8 -0
- package/dist/data/ai/SessionInspector.composer-slot.cjs.map +1 -0
- package/dist/data/ai/SessionInspector.composer-slot.d.ts +9 -0
- package/dist/data/ai/SessionInspector.composer-slot.d.ts.map +1 -0
- package/dist/data/ai/SessionInspector.composer-slot.js +8 -0
- package/dist/data/ai/SessionInspector.composer-slot.js.map +1 -0
- package/dist/data/ai/SessionInspector.d.ts +6 -1
- package/dist/data/ai/SessionInspector.d.ts.map +1 -1
- package/dist/data/ai/SessionInspector.js +69 -73
- package/dist/data/ai/SessionInspector.js.map +1 -1
- package/dist/data/ai/SessionInspector.legacy.cjs +41 -42
- package/dist/data/ai/SessionInspector.legacy.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.legacy.d.ts.map +1 -1
- package/dist/data/ai/SessionInspector.legacy.js +41 -42
- package/dist/data/ai/SessionInspector.legacy.js.map +1 -1
- package/dist/data/ai/SessionInspector.remote-view.cjs +14 -9
- package/dist/data/ai/SessionInspector.remote-view.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.remote-view.d.ts +6 -2
- package/dist/data/ai/SessionInspector.remote-view.d.ts.map +1 -1
- package/dist/data/ai/SessionInspector.remote-view.js +14 -9
- package/dist/data/ai/SessionInspector.remote-view.js.map +1 -1
- package/dist/data/ai/SessionInspector.remote.cjs +84 -16
- package/dist/data/ai/SessionInspector.remote.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.remote.d.ts +42 -7
- package/dist/data/ai/SessionInspector.remote.d.ts.map +1 -1
- package/dist/data/ai/SessionInspector.remote.js +86 -18
- package/dist/data/ai/SessionInspector.remote.js.map +1 -1
- package/dist/data/ai/SessionInspector.transcript.cjs +5 -0
- package/dist/data/ai/SessionInspector.transcript.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.transcript.d.ts.map +1 -1
- package/dist/data/ai/SessionInspector.transcript.js +5 -0
- package/dist/data/ai/SessionInspector.transcript.js.map +1 -1
- package/dist/data/ai/SessionTail.cjs +63 -0
- package/dist/data/ai/SessionTail.cjs.map +1 -0
- package/dist/data/ai/SessionTail.d.ts +14 -0
- package/dist/data/ai/SessionTail.d.ts.map +1 -0
- package/dist/data/ai/SessionTail.js +63 -0
- package/dist/data/ai/SessionTail.js.map +1 -0
- package/dist/data/ai/SessionViewer.cjs +10 -3
- package/dist/data/ai/SessionViewer.cjs.map +1 -1
- package/dist/data/ai/SessionViewer.d.ts +7 -1
- package/dist/data/ai/SessionViewer.d.ts.map +1 -1
- package/dist/data/ai/SessionViewer.js +10 -3
- package/dist/data/ai/SessionViewer.js.map +1 -1
- package/dist/data/ai/SessionViewer.unified.d.ts +14 -0
- package/dist/data/ai/SessionViewer.unified.d.ts.map +1 -1
- package/dist/data/ai/index.d.ts +4 -3
- package/dist/data/ai/index.d.ts.map +1 -1
- package/dist/data/ai/use-session-scroll.cjs +30 -6
- package/dist/data/ai/use-session-scroll.cjs.map +1 -1
- package/dist/data/ai/use-session-scroll.d.ts +7 -0
- package/dist/data/ai/use-session-scroll.d.ts.map +1 -1
- package/dist/data/ai/use-session-scroll.js +30 -6
- package/dist/data/ai/use-session-scroll.js.map +1 -1
- package/dist/data/chat/Chat.cjs +1 -1
- package/dist/data/chat/Chat.cjs.map +1 -1
- package/dist/data/chat/Chat.js +1 -1
- package/dist/data/chat/Chat.js.map +1 -1
- package/dist/data/chat/ChatRuntimeToolbar.cjs +4 -4
- package/dist/data/chat/ChatRuntimeToolbar.cjs.map +1 -1
- package/dist/data/chat/ChatRuntimeToolbar.d.ts.map +1 -1
- package/dist/data/chat/ChatRuntimeToolbar.js +4 -4
- package/dist/data/chat/ChatRuntimeToolbar.js.map +1 -1
- package/dist/data/chat/PromptInput.cjs +4 -2
- package/dist/data/chat/PromptInput.cjs.map +1 -1
- package/dist/data/chat/PromptInput.d.ts +5 -1
- package/dist/data/chat/PromptInput.d.ts.map +1 -1
- package/dist/data/chat/PromptInput.js +4 -2
- package/dist/data/chat/PromptInput.js.map +1 -1
- package/dist/data/diagnostics/ErrorDetails.cjs +13 -2
- package/dist/data/diagnostics/ErrorDetails.cjs.map +1 -1
- package/dist/data/diagnostics/ErrorDetails.d.ts +3 -1
- package/dist/data/diagnostics/ErrorDetails.d.ts.map +1 -1
- package/dist/data/diagnostics/ErrorDetails.js +14 -3
- package/dist/data/diagnostics/ErrorDetails.js.map +1 -1
- package/dist/data/diagnostics/FrameSourceWindow.cjs +3 -2
- package/dist/data/diagnostics/FrameSourceWindow.cjs.map +1 -1
- package/dist/data/diagnostics/FrameSourceWindow.d.ts.map +1 -1
- package/dist/data/diagnostics/FrameSourceWindow.js +3 -2
- package/dist/data/diagnostics/FrameSourceWindow.js.map +1 -1
- package/dist/data/diagnostics/error-diagnostics.cjs +5 -0
- package/dist/data/diagnostics/error-diagnostics.cjs.map +1 -1
- package/dist/data/diagnostics/error-diagnostics.d.ts +1 -0
- package/dist/data/diagnostics/error-diagnostics.d.ts.map +1 -1
- package/dist/data/diagnostics/error-diagnostics.js +6 -1
- package/dist/data/diagnostics/error-diagnostics.js.map +1 -1
- package/dist/data/har/HarPanel.cjs +3 -1
- package/dist/data/har/HarPanel.cjs.map +1 -1
- package/dist/data/har/HarPanel.d.ts +4 -1
- package/dist/data/har/HarPanel.d.ts.map +1 -1
- package/dist/data/har/HarPanel.js +3 -1
- package/dist/data/har/HarPanel.js.map +1 -1
- package/dist/data/test-runner/Expectation.cjs +57 -0
- package/dist/data/test-runner/Expectation.cjs.map +1 -0
- package/dist/data/test-runner/Expectation.d.ts +19 -0
- package/dist/data/test-runner/Expectation.d.ts.map +1 -0
- package/dist/data/test-runner/Expectation.js +57 -0
- package/dist/data/test-runner/Expectation.js.map +1 -0
- package/dist/data/test-runner/MarkIcons.cjs +38 -0
- package/dist/data/test-runner/MarkIcons.cjs.map +1 -0
- package/dist/data/test-runner/MarkIcons.d.ts +15 -0
- package/dist/data/test-runner/MarkIcons.d.ts.map +1 -0
- package/dist/data/test-runner/MarkIcons.js +38 -0
- package/dist/data/test-runner/MarkIcons.js.map +1 -0
- package/dist/data/test-runner/TestDetailPanel.cjs +74 -42
- package/dist/data/test-runner/TestDetailPanel.cjs.map +1 -1
- package/dist/data/test-runner/TestDetailPanel.d.ts.map +1 -1
- package/dist/data/test-runner/TestDetailPanel.js +76 -44
- package/dist/data/test-runner/TestDetailPanel.js.map +1 -1
- package/dist/data/test-runner/TestFailureDetail.cjs +23 -27
- package/dist/data/test-runner/TestFailureDetail.cjs.map +1 -1
- package/dist/data/test-runner/TestFailureDetail.d.ts +2 -2
- package/dist/data/test-runner/TestFailureDetail.d.ts.map +1 -1
- package/dist/data/test-runner/TestFailureDetail.js +23 -27
- package/dist/data/test-runner/TestFailureDetail.js.map +1 -1
- package/dist/data/test-runner/TestFilterBar.cjs +87 -93
- package/dist/data/test-runner/TestFilterBar.cjs.map +1 -1
- package/dist/data/test-runner/TestFilterBar.d.ts +3 -2
- package/dist/data/test-runner/TestFilterBar.d.ts.map +1 -1
- package/dist/data/test-runner/TestFilterBar.js +89 -95
- package/dist/data/test-runner/TestFilterBar.js.map +1 -1
- package/dist/data/test-runner/TestRunSummary.cjs +35 -21
- package/dist/data/test-runner/TestRunSummary.cjs.map +1 -1
- package/dist/data/test-runner/TestRunSummary.d.ts.map +1 -1
- package/dist/data/test-runner/TestRunSummary.js +35 -21
- package/dist/data/test-runner/TestRunSummary.js.map +1 -1
- package/dist/data/test-runner/TestRunner.cjs +52 -30
- package/dist/data/test-runner/TestRunner.cjs.map +1 -1
- package/dist/data/test-runner/TestRunner.d.ts +3 -2
- package/dist/data/test-runner/TestRunner.d.ts.map +1 -1
- package/dist/data/test-runner/TestRunner.js +52 -30
- package/dist/data/test-runner/TestRunner.js.map +1 -1
- package/dist/data/test-runner/TestTree.cjs +5 -2
- package/dist/data/test-runner/TestTree.cjs.map +1 -1
- package/dist/data/test-runner/TestTree.d.ts +5 -2
- package/dist/data/test-runner/TestTree.d.ts.map +1 -1
- package/dist/data/test-runner/TestTree.js +5 -2
- package/dist/data/test-runner/TestTree.js.map +1 -1
- package/dist/data/test-runner/TestTreeNode.cjs +27 -19
- package/dist/data/test-runner/TestTreeNode.cjs.map +1 -1
- package/dist/data/test-runner/TestTreeNode.d.ts.map +1 -1
- package/dist/data/test-runner/TestTreeNode.js +28 -20
- package/dist/data/test-runner/TestTreeNode.js.map +1 -1
- package/dist/data/test-runner/adapter.cjs +11 -0
- package/dist/data/test-runner/adapter.cjs.map +1 -1
- package/dist/data/test-runner/adapter.d.ts +18 -7
- package/dist/data/test-runner/adapter.d.ts.map +1 -1
- package/dist/data/test-runner/adapter.js +11 -0
- package/dist/data/test-runner/adapter.js.map +1 -1
- package/dist/data/test-runner/copy.cjs +114 -0
- package/dist/data/test-runner/copy.cjs.map +1 -0
- package/dist/data/test-runner/copy.d.ts +47 -0
- package/dist/data/test-runner/copy.d.ts.map +1 -0
- package/dist/data/test-runner/copy.js +114 -0
- package/dist/data/test-runner/copy.js.map +1 -0
- package/dist/data/test-runner/index.d.ts +5 -2
- package/dist/data/test-runner/index.d.ts.map +1 -1
- package/dist/data/test-runner/marks.cjs +143 -0
- package/dist/data/test-runner/marks.cjs.map +1 -0
- package/dist/data/test-runner/marks.d.ts +30 -0
- package/dist/data/test-runner/marks.d.ts.map +1 -0
- package/dist/data/test-runner/marks.js +143 -0
- package/dist/data/test-runner/marks.js.map +1 -0
- package/dist/data/test-runner/status.cjs +27 -44
- package/dist/data/test-runner/status.cjs.map +1 -1
- package/dist/data/test-runner/status.d.ts +12 -3
- package/dist/data/test-runner/status.d.ts.map +1 -1
- package/dist/data/test-runner/status.js +27 -44
- package/dist/data/test-runner/status.js.map +1 -1
- package/dist/data/verification/adapterViews.cjs +16 -41
- package/dist/data/verification/adapterViews.cjs.map +1 -1
- package/dist/data/verification/adapterViews.d.ts.map +1 -1
- package/dist/data/verification/adapterViews.js +16 -41
- package/dist/data/verification/adapterViews.js.map +1 -1
- package/dist/data/verification/adapters.cjs +115 -3
- package/dist/data/verification/adapters.cjs.map +1 -1
- package/dist/data/verification/adapters.d.ts.map +1 -1
- package/dist/data/verification/adapters.js +115 -3
- package/dist/data/verification/adapters.js.map +1 -1
- package/dist/data/verification/verifyReportTests.cjs.map +1 -1
- package/dist/data/verification/verifyReportTests.js.map +1 -1
- package/dist/data/version-info.cjs +3 -3
- package/dist/data/version-info.js +3 -3
- package/dist/data.cjs +44 -3
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.ts +2 -0
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +45 -4
- package/dist/data.js.map +1 -1
- package/dist/icons/components/UiLogoEslint.cjs +10 -0
- package/dist/icons/components/UiLogoEslint.cjs.map +1 -0
- package/dist/icons/components/UiLogoEslint.d.ts +9 -0
- package/dist/icons/components/UiLogoEslint.d.ts.map +1 -0
- package/dist/icons/components/UiLogoEslint.js +10 -0
- package/dist/icons/components/UiLogoEslint.js.map +1 -0
- package/dist/icons/components/UiLogoGo.cjs +10 -0
- package/dist/icons/components/UiLogoGo.cjs.map +1 -0
- package/dist/icons/components/UiLogoGo.d.ts +9 -0
- package/dist/icons/components/UiLogoGo.d.ts.map +1 -0
- package/dist/icons/components/UiLogoGo.js +10 -0
- package/dist/icons/components/UiLogoGo.js.map +1 -0
- package/dist/icons/components/UiLogoJest.cjs +10 -0
- package/dist/icons/components/UiLogoJest.cjs.map +1 -0
- package/dist/icons/components/UiLogoJest.d.ts +9 -0
- package/dist/icons/components/UiLogoJest.d.ts.map +1 -0
- package/dist/icons/components/UiLogoJest.js +10 -0
- package/dist/icons/components/UiLogoJest.js.map +1 -0
- package/dist/icons/components/UiLogoMarkdown.cjs +10 -0
- package/dist/icons/components/UiLogoMarkdown.cjs.map +1 -0
- package/dist/icons/components/UiLogoMarkdown.d.ts +9 -0
- package/dist/icons/components/UiLogoMarkdown.d.ts.map +1 -0
- package/dist/icons/components/UiLogoMarkdown.js +10 -0
- package/dist/icons/components/UiLogoMarkdown.js.map +1 -0
- package/dist/icons/components/UiLogoOxc.cjs +10 -0
- package/dist/icons/components/UiLogoOxc.cjs.map +1 -0
- package/dist/icons/components/UiLogoOxc.d.ts +9 -0
- package/dist/icons/components/UiLogoOxc.d.ts.map +1 -0
- package/dist/icons/components/UiLogoOxc.js +10 -0
- package/dist/icons/components/UiLogoOxc.js.map +1 -0
- package/dist/icons/components/UiLogoPlaywright.cjs +10 -0
- package/dist/icons/components/UiLogoPlaywright.cjs.map +1 -0
- package/dist/icons/components/UiLogoPlaywright.d.ts +9 -0
- package/dist/icons/components/UiLogoPlaywright.d.ts.map +1 -0
- package/dist/icons/components/UiLogoPlaywright.js +10 -0
- package/dist/icons/components/UiLogoPlaywright.js.map +1 -0
- package/dist/icons/components/UiLogoPython.cjs +10 -0
- package/dist/icons/components/UiLogoPython.cjs.map +1 -0
- package/dist/icons/components/UiLogoPython.d.ts +9 -0
- package/dist/icons/components/UiLogoPython.d.ts.map +1 -0
- package/dist/icons/components/UiLogoPython.js +10 -0
- package/dist/icons/components/UiLogoPython.js.map +1 -0
- package/dist/icons/components/UiLogoReact.cjs +10 -0
- package/dist/icons/components/UiLogoReact.cjs.map +1 -0
- package/dist/icons/components/UiLogoReact.d.ts +9 -0
- package/dist/icons/components/UiLogoReact.d.ts.map +1 -0
- package/dist/icons/components/UiLogoReact.js +10 -0
- package/dist/icons/components/UiLogoReact.js.map +1 -0
- package/dist/icons/components/UiLogoRuff.cjs +10 -0
- package/dist/icons/components/UiLogoRuff.cjs.map +1 -0
- package/dist/icons/components/UiLogoRuff.d.ts +9 -0
- package/dist/icons/components/UiLogoRuff.d.ts.map +1 -0
- package/dist/icons/components/UiLogoRuff.js +10 -0
- package/dist/icons/components/UiLogoRuff.js.map +1 -0
- package/dist/icons/components/UiLogoTypescript.cjs +10 -0
- package/dist/icons/components/UiLogoTypescript.cjs.map +1 -0
- package/dist/icons/components/UiLogoTypescript.d.ts +9 -0
- package/dist/icons/components/UiLogoTypescript.d.ts.map +1 -0
- package/dist/icons/components/UiLogoTypescript.js +10 -0
- package/dist/icons/components/UiLogoTypescript.js.map +1 -0
- package/dist/icons/components/UiLogoVitest.cjs +10 -0
- package/dist/icons/components/UiLogoVitest.cjs.map +1 -0
- package/dist/icons/components/UiLogoVitest.d.ts +9 -0
- package/dist/icons/components/UiLogoVitest.d.ts.map +1 -0
- package/dist/icons/components/UiLogoVitest.js +10 -0
- package/dist/icons/components/UiLogoVitest.js.map +1 -0
- package/dist/icons/index.d.ts +11 -0
- package/dist/icons/index.d.ts.map +1 -1
- package/dist/icons/registry.cjs +22 -0
- package/dist/icons/registry.cjs.map +1 -1
- package/dist/icons/registry.d.ts.map +1 -1
- package/dist/icons/registry.js +22 -0
- package/dist/icons/registry.js.map +1 -1
- package/dist/icons.cjs +22 -0
- package/dist/icons.cjs.map +1 -1
- package/dist/icons.js +22 -0
- package/dist/icons.js.map +1 -1
- package/dist/index.cjs +44 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +45 -4
- package/dist/index.js.map +1 -1
- package/dist/overlay/DropdownMenuSubmenu.cjs +4 -4
- package/dist/overlay/DropdownMenuSubmenu.cjs.map +1 -1
- package/dist/overlay/DropdownMenuSubmenu.js +4 -4
- package/dist/overlay/DropdownMenuSubmenu.js.map +1 -1
- package/dist/rpc/InlineError.cjs +12 -43
- package/dist/rpc/InlineError.cjs.map +1 -1
- package/dist/rpc/InlineError.d.ts.map +1 -1
- package/dist/rpc/InlineError.js +13 -44
- package/dist/rpc/InlineError.js.map +1 -1
- package/dist/rpc/OperationScheduleDialog.cjs +63 -10
- package/dist/rpc/OperationScheduleDialog.cjs.map +1 -1
- package/dist/rpc/OperationScheduleDialog.d.ts +4 -0
- package/dist/rpc/OperationScheduleDialog.d.ts.map +1 -1
- package/dist/rpc/OperationScheduleDialog.js +63 -10
- package/dist/rpc/OperationScheduleDialog.js.map +1 -1
- package/dist/rpc/OperationSchedules.cjs +12 -2
- package/dist/rpc/OperationSchedules.cjs.map +1 -1
- package/dist/rpc/OperationSchedules.d.ts.map +1 -1
- package/dist/rpc/OperationSchedules.js +12 -2
- package/dist/rpc/OperationSchedules.js.map +1 -1
- package/dist/rpc/operation-schedule-model.cjs +2 -0
- package/dist/rpc/operation-schedule-model.cjs.map +1 -1
- package/dist/rpc/operation-schedule-model.d.ts.map +1 -1
- package/dist/rpc/operation-schedule-model.js +2 -0
- package/dist/rpc/operation-schedule-model.js.map +1 -1
- package/dist/rpc/operation-schedule-types.d.ts +7 -1
- package/dist/rpc/operation-schedule-types.d.ts.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/dist/data/test-runner/frameworkIcon.cjs +0 -27
- package/dist/data/test-runner/frameworkIcon.cjs.map +0 -1
- package/dist/data/test-runner/frameworkIcon.d.ts +0 -3
- package/dist/data/test-runner/frameworkIcon.d.ts.map +0 -1
- package/dist/data/test-runner/frameworkIcon.js +0 -27
- package/dist/data/test-runner/frameworkIcon.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HarPanel.js","sources":["../../../src/data/har/HarPanel.tsx"],"sourcesContent":["import { createContext, useContext, useEffect, useMemo, useState } from \"react\";\nimport { useResourceClock } from \"../../hooks/use-resource-clock\";\nimport { UiLoader } from \"../../icons\";\nimport { formatDuration } from \"../../lib/format\";\nimport { DataTable, type DataTableColumn, type DataTableFooterContext } from \"../DataTable\";\nimport { Icon } from \"../Icon\";\nimport { JsonView } from \"../JsonView\";\nimport type { HAREntry } from \"./types\";\n\nconst ERROR_PREVIEW_CHARS = 80;\n\n/** Wall-clock ms shared by every in-flight row, ticking only while one exists. */\nconst HarClockContext = createContext<number | undefined>(undefined);\n\nexport type HarPanelProps = {\n /** HAR entries to render. */\n entries: HAREntry[];\n /** Optional external search term. When omitted, the internal DataTable search is shown. */\n search?: string;\n /** Empty-state label for no entries or no matches. */\n emptyLabel?: string;\n /** Classes applied to the panel root. */\n className?: string;\n /** Optional private body store used only when an expanded row needs a preview. */\n bodySource?: HarPanelBodySource;\n};\n\nexport type HarPanelBodySource = {\n hasBody: (entry: HAREntry, part: \"request\" | \"response\") => boolean;\n preview: (requestId: string, part: \"request\" | \"response\") => Promise<string>;\n downloadHref: (entry: HAREntry, part: \"request\" | \"response\") => string;\n};\n\nexport type HarEntryDetailsProps = { entry: HAREntry; bodySource?: HarPanelBodySource | undefined };\n\nconst COLS: DataTableColumn<HAREntry>[] = [\n {\n key: \"request.method\",\n label: \"Method\",\n shrink: true,\n },\n {\n key: \"request.url\",\n label: \"URL\",\n grow: true,\n render: (value, entry) => (\n <span className=\"inline-flex min-w-0 items-baseline gap-density-2\">\n <span title={String(value ?? \"\")}>{String(value ?? \"\")}</span>\n {entry._error ? (\n <span className=\"text-destructive text-xs\" title={entry._error}>\n {previewError(entry._error)}\n </span>\n ) : null}\n </span>\n ),\n },\n {\n key: \"response.status\",\n label: \"Status\",\n shrink: true,\n render: (value, entry) => <HarStatus status={Number(value ?? 0)} entry={entry} />,\n sortValue: (value) => Number(value ?? 0),\n filterValue: (value, entry) => harStatusLabel(Number(value ?? 0), entry),\n },\n {\n key: \"time\",\n label: \"Time\",\n align: \"right\",\n shrink: true,\n render: (value, entry) =>\n entry._pending ? <PendingElapsed entry={entry} /> : `${Number(value ?? 0).toFixed(0)}ms`,\n sortValue: (value, entry) =>\n entry._pending ? pendingElapsedMs(entry, Date.now()) : Number(value ?? 0),\n },\n {\n key: \"response.bodySize\",\n label: \"Size\",\n align: \"right\",\n shrink: true,\n render: (value) => formatBytes(Number(value ?? 0)),\n sortValue: (value) => Number(value ?? 0),\n },\n {\n key: \"response.content.mimeType\",\n label: \"Type\",\n shrink: true,\n },\n];\n\nexport function HarPanel({\n entries,\n search,\n emptyLabel = \"No HTTP traffic captured\",\n className,\n bodySource,\n}: HarPanelProps) {\n const filteredEntries = useMemo(() => {\n if (search === undefined || search.trim() === \"\") return entries;\n return entries.filter((entry) => matchesSearch(search.trim().toLowerCase(), entry));\n }, [entries, search]);\n const runningCount = useMemo(() => entries.filter((entry) => entry._pending).length, [entries]);\n const now = useResourceClock(runningCount > 0);\n\n if (!entries || entries.length === 0) {\n return (\n <div className=\"p-density-6 text-center text-muted-foreground text-sm\">{emptyLabel}</div>\n );\n }\n\n return (\n // A flex column, not a plain block: DataTable sizes itself as `flex-1\n // min-h-0` so its own scroll region can be bounded, and in a block parent\n // that resolves to content height — the table grows past the panel and\n // nothing scrolls.\n <div className={`flex h-full min-h-0 flex-col ${className ?? \"\"}`}>\n <HarClockContext.Provider value={now}>\n <DataTable\n data={filteredEntries}\n columns={COLS}\n autoFilter\n showGlobalFilter={search === undefined}\n globalFilterPlaceholder=\"Filter URL, method, or body…\"\n defaultSort={{ key: \"time\", dir: \"asc\" }}\n emptyMessage={emptyLabel}\n {...(runningCount > 0\n ? {\n footer: ({ visibleRowCount, totalRowCount }: DataTableFooterContext) =>\n `${visibleRowCount} of ${totalRowCount} row${totalRowCount === 1 ? \"\" : \"s\"} · ${runningCount} running`,\n }\n : {})}\n getRowId={(entry, index) =>\n // `_id` is the same on a request's pending and completed snapshots,\n // so an expanded row survives the transition. Without it, leave out\n // an in-flight entry's `time`, which grows with every snapshot.\n entry._id ??\n `${entry.startedDateTime ?? index}-${entry.request.method}-${entry.request.url}${entry._pending ? \"-pending\" : `-${entry.time}`}`\n }\n renderExpandedRow={(entry) => <HarEntryDetails entry={entry} bodySource={bodySource} />}\n />\n </HarClockContext.Provider>\n </div>\n );\n}\n\nfunction HarStatus({ status, entry }: { status: number; entry: HAREntry }) {\n if (entry._pending) {\n return (\n <span className=\"inline-flex items-center gap-1 text-muted-foreground\">\n <Icon icon={UiLoader} className=\"animate-spin\" />\n running\n </span>\n );\n }\n if (entry._error) return <span className=\"font-semibold text-destructive\">ERR</span>;\n return <span className={statusColor(status)}>{String(status)}</span>;\n}\n\nfunction PendingElapsed({ entry }: { entry: HAREntry }) {\n const now = useContext(HarClockContext);\n if (now === undefined) throw new Error(\"PendingElapsed must render inside HarPanel's clock\");\n return <span className=\"tabular-nums\">{formatDuration(pendingElapsedMs(entry, now))}</span>;\n}\n\n/**\n * Elapsed time of an in-flight request, measured from `startedDateTime` so it\n * keeps ticking between snapshots. The snapshot's `time` is only used when the\n * start timestamp cannot be parsed.\n */\nfunction pendingElapsedMs(entry: HAREntry, now: number): number {\n const started = Date.parse(entry.startedDateTime ?? \"\");\n if (Number.isNaN(started)) return entry.time;\n // Server and browser clocks can disagree by a little; a negative elapsed is noise.\n return Math.max(0, now - started);\n}\n\nfunction harStatusLabel(status: number, entry: HAREntry): string {\n if (entry._pending) return \"running\";\n if (entry._error) return \"ERR\";\n return String(status);\n}\n\nfunction previewError(error: string): string {\n return error.length > ERROR_PREVIEW_CHARS ? `${error.slice(0, ERROR_PREVIEW_CHARS)}…` : error;\n}\n\nexport function HarEntryDetails({ entry, bodySource }: HarEntryDetailsProps) {\n const parentNow = useContext(HarClockContext);\n const ownNow = useResourceClock(Boolean(entry._pending) && parentNow === undefined);\n return <HarClockContext.Provider value={parentNow ?? ownNow}><HarRowDetails entry={entry} bodySource={bodySource} /></HarClockContext.Provider>;\n}\n\nfunction HarRowDetails({ entry, bodySource }: HarEntryDetailsProps) {\n return (\n <>\n {entry._error && (\n <div className=\"mb-density-3\">\n <div className=\"font-semibold text-foreground mb-1\">Transport Error</div>\n <pre className=\"whitespace-pre-wrap break-all text-destructive\">{entry._error}</pre>\n </div>\n )}\n {entry._pending && (\n <div className=\"mb-density-3 text-muted-foreground\">\n Waiting for response — in flight for <PendingElapsed entry={entry} />\n </div>\n )}\n <div className=\"grid grid-cols-2 gap-density-4\">\n <div>\n <HeaderList\n title=\"Request Headers\"\n {...(entry.request.headers ? { headers: entry.request.headers } : {})}\n />\n {(entry.request.postData?.text || bodySource?.hasBody(entry, \"request\")) && (\n <BodySection entry={entry} part=\"request\" text={entry.request.postData?.text}\n mimeType={entry.request.postData?.mimeType} bodySource={bodySource} />\n )}\n </div>\n <div>\n <HeaderList\n title=\"Response Headers\"\n {...(entry.response.headers ? { headers: entry.response.headers } : {})}\n />\n </div>\n </div>\n {(entry.response.content?.text || bodySource?.hasBody(entry, \"response\")) && (\n <BodySection entry={entry} part=\"response\" text={entry.response.content?.text}\n mimeType={entry.response.content?.mimeType} bodySource={bodySource} />\n )}\n </>\n );\n}\n\nfunction BodySection({ entry, part, text, mimeType, bodySource }: {\n entry: HAREntry;\n part: \"request\" | \"response\";\n text?: string | undefined;\n mimeType?: string | undefined;\n bodySource?: HarPanelBodySource | undefined;\n}) {\n const remote = !text && bodySource?.hasBody(entry, part);\n const requestId = entry._id;\n if (remote && !requestId) throw new Error(\"Private HAR body requires a request ID\");\n const [preview, setPreview] = useState<string | undefined>();\n const [error, setError] = useState<Error | undefined>();\n useEffect(() => {\n if (!remote || !bodySource || !requestId) return;\n let active = true;\n setPreview(undefined);\n setError(undefined);\n bodySource.preview(requestId, part).then(\n (value) => { if (active) setPreview(value); },\n (reason: unknown) => { if (active) setError(reason instanceof Error ? reason : new Error(String(reason))); },\n );\n return () => { active = false; };\n }, [bodySource, requestId, part, remote]);\n return (\n <div className=\"mt-density-3\">\n <div className=\"mb-1 flex items-center gap-2 font-semibold text-foreground\">\n {part === \"request\" ? \"Request Body\" : \"Response Body\"}\n {remote && bodySource && <a className=\"text-sm font-normal text-primary underline\" href={bodySource.downloadHref(entry, part)} download>Download full body</a>}\n </div>\n <div className=\"bg-background p-density-2 rounded border border-border overflow-auto max-h-64\">\n {error ? <span role=\"alert\" className=\"text-destructive\">{error.message}</span>\n : remote && preview === undefined ? <span className=\"text-muted-foreground\">Loading preview…</span>\n : <BodyView text={text || preview || \"\"} {...(mimeType ? { mimeType } : {})} />}\n </div>\n {remote && <p className=\"mt-1 text-xs text-muted-foreground\">Preview shows at most the first 64 KiB.</p>}\n </div>\n );\n}\n\nfunction HeaderList({\n title,\n headers,\n}: {\n title: string;\n headers?: { name: string; value: string }[];\n}) {\n return (\n <>\n <div className=\"font-semibold text-foreground mb-1\">{title}</div>\n <div className=\"space-y-0.5\">\n {headers?.map((h, i) => (\n <div key={i} className=\"whitespace-nowrap\">\n <span className=\"text-purple-600 dark:text-purple-400\">{h.name}:</span> {h.value}\n </div>\n ))}\n </div>\n </>\n );\n}\n\nfunction BodyView({ text, mimeType }: { text: string; mimeType?: string }) {\n if (isJsonType(mimeType)) {\n const parsed = tryParseJson(text);\n if (parsed !== null) return <JsonView data={parsed} />;\n }\n return <pre className=\"whitespace-pre-wrap break-all\">{text}</pre>;\n}\n\nfunction tryParseJson(text: string): unknown | null {\n try {\n return JSON.parse(text);\n } catch {\n return null;\n }\n}\n\nfunction isJsonType(mime?: string): boolean {\n return !!mime && (mime.includes(\"json\") || mime.includes(\"javascript\"));\n}\n\nfunction matchesSearch(needle: string, entry: HAREntry): boolean {\n return [\n entry.request.method,\n entry.request.url,\n entry.request.postData?.text,\n entry.response.content?.text,\n entry._error,\n ].some((value) => !!value && value.toLowerCase().includes(needle));\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 0) return \"\";\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / 1024 / 1024).toFixed(1)}MB`;\n}\n\nfunction statusColor(status: number): string {\n if (status >= 500) return \"text-red-600\";\n if (status >= 400) return \"text-amber-600\";\n if (status >= 300) return \"text-blue-600\";\n if (status >= 200) return \"text-green-600\";\n return \"text-muted-foreground\";\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"HarPanel.js","sources":["../../../src/data/har/HarPanel.tsx"],"sourcesContent":["import { createContext, useContext, useEffect, useMemo, useState } from \"react\";\nimport { useResourceClock } from \"../../hooks/use-resource-clock\";\nimport { UiLoader } from \"../../icons\";\nimport { formatDuration } from \"../../lib/format\";\nimport {\n DataTable,\n type DataTableColumn,\n type DataTableFooterContext,\n type DataTableMenuAction,\n} from \"../DataTable\";\nimport { Icon } from \"../Icon\";\nimport { JsonView } from \"../JsonView\";\nimport type { HAREntry } from \"./types\";\n\nconst ERROR_PREVIEW_CHARS = 80;\n\n/** Wall-clock ms shared by every in-flight row, ticking only while one exists. */\nconst HarClockContext = createContext<number | undefined>(undefined);\n\nexport type HarPanelProps = {\n /** HAR entries to render. */\n entries: HAREntry[];\n /** Optional external search term. When omitted, the internal DataTable search is shown. */\n search?: string;\n /** Empty-state label for no entries or no matches. */\n emptyLabel?: string;\n /** Classes applied to the panel root. */\n className?: string;\n /** Optional private body store used only when an expanded row needs a preview. */\n bodySource?: HarPanelBodySource;\n /** Entries for the table's overflow menu, e.g. a `useClickyExport` action. */\n menuActions?: DataTableMenuAction[];\n};\n\nexport type HarPanelBodySource = {\n hasBody: (entry: HAREntry, part: \"request\" | \"response\") => boolean;\n preview: (requestId: string, part: \"request\" | \"response\") => Promise<string>;\n downloadHref: (entry: HAREntry, part: \"request\" | \"response\") => string;\n};\n\nexport type HarEntryDetailsProps = { entry: HAREntry; bodySource?: HarPanelBodySource | undefined };\n\nconst COLS: DataTableColumn<HAREntry>[] = [\n {\n key: \"request.method\",\n label: \"Method\",\n shrink: true,\n },\n {\n key: \"request.url\",\n label: \"URL\",\n grow: true,\n render: (value, entry) => (\n <span className=\"inline-flex min-w-0 items-baseline gap-density-2\">\n <span title={String(value ?? \"\")}>{String(value ?? \"\")}</span>\n {entry._error ? (\n <span className=\"text-destructive text-xs\" title={entry._error}>\n {previewError(entry._error)}\n </span>\n ) : null}\n </span>\n ),\n },\n {\n key: \"response.status\",\n label: \"Status\",\n shrink: true,\n render: (value, entry) => <HarStatus status={Number(value ?? 0)} entry={entry} />,\n sortValue: (value) => Number(value ?? 0),\n filterValue: (value, entry) => harStatusLabel(Number(value ?? 0), entry),\n },\n {\n key: \"time\",\n label: \"Time\",\n align: \"right\",\n shrink: true,\n render: (value, entry) =>\n entry._pending ? <PendingElapsed entry={entry} /> : `${Number(value ?? 0).toFixed(0)}ms`,\n sortValue: (value, entry) =>\n entry._pending ? pendingElapsedMs(entry, Date.now()) : Number(value ?? 0),\n },\n {\n key: \"response.bodySize\",\n label: \"Size\",\n align: \"right\",\n shrink: true,\n render: (value) => formatBytes(Number(value ?? 0)),\n sortValue: (value) => Number(value ?? 0),\n },\n {\n key: \"response.content.mimeType\",\n label: \"Type\",\n shrink: true,\n },\n];\n\nexport function HarPanel({\n entries,\n search,\n emptyLabel = \"No HTTP traffic captured\",\n className,\n bodySource,\n menuActions,\n}: HarPanelProps) {\n const filteredEntries = useMemo(() => {\n if (search === undefined || search.trim() === \"\") return entries;\n return entries.filter((entry) => matchesSearch(search.trim().toLowerCase(), entry));\n }, [entries, search]);\n const runningCount = useMemo(() => entries.filter((entry) => entry._pending).length, [entries]);\n const now = useResourceClock(runningCount > 0);\n\n if (!entries || entries.length === 0) {\n return (\n <div className=\"p-density-6 text-center text-muted-foreground text-sm\">{emptyLabel}</div>\n );\n }\n\n return (\n // A flex column, not a plain block: DataTable sizes itself as `flex-1\n // min-h-0` so its own scroll region can be bounded, and in a block parent\n // that resolves to content height — the table grows past the panel and\n // nothing scrolls.\n <div className={`flex h-full min-h-0 flex-col ${className ?? \"\"}`}>\n <HarClockContext.Provider value={now}>\n <DataTable\n data={filteredEntries}\n columns={COLS}\n autoFilter\n showGlobalFilter={search === undefined}\n globalFilterPlaceholder=\"Filter URL, method, or body…\"\n defaultSort={{ key: \"time\", dir: \"asc\" }}\n emptyMessage={emptyLabel}\n {...(menuActions ? { menuActions } : {})}\n {...(runningCount > 0\n ? {\n footer: ({ visibleRowCount, totalRowCount }: DataTableFooterContext) =>\n `${visibleRowCount} of ${totalRowCount} row${totalRowCount === 1 ? \"\" : \"s\"} · ${runningCount} running`,\n }\n : {})}\n getRowId={(entry, index) =>\n // `_id` is the same on a request's pending and completed snapshots,\n // so an expanded row survives the transition. Without it, leave out\n // an in-flight entry's `time`, which grows with every snapshot.\n entry._id ??\n `${entry.startedDateTime ?? index}-${entry.request.method}-${entry.request.url}${entry._pending ? \"-pending\" : `-${entry.time}`}`\n }\n renderExpandedRow={(entry) => <HarEntryDetails entry={entry} bodySource={bodySource} />}\n />\n </HarClockContext.Provider>\n </div>\n );\n}\n\nfunction HarStatus({ status, entry }: { status: number; entry: HAREntry }) {\n if (entry._pending) {\n return (\n <span className=\"inline-flex items-center gap-1 text-muted-foreground\">\n <Icon icon={UiLoader} className=\"animate-spin\" />\n running\n </span>\n );\n }\n if (entry._error) return <span className=\"font-semibold text-destructive\">ERR</span>;\n return <span className={statusColor(status)}>{String(status)}</span>;\n}\n\nfunction PendingElapsed({ entry }: { entry: HAREntry }) {\n const now = useContext(HarClockContext);\n if (now === undefined) throw new Error(\"PendingElapsed must render inside HarPanel's clock\");\n return <span className=\"tabular-nums\">{formatDuration(pendingElapsedMs(entry, now))}</span>;\n}\n\n/**\n * Elapsed time of an in-flight request, measured from `startedDateTime` so it\n * keeps ticking between snapshots. The snapshot's `time` is only used when the\n * start timestamp cannot be parsed.\n */\nfunction pendingElapsedMs(entry: HAREntry, now: number): number {\n const started = Date.parse(entry.startedDateTime ?? \"\");\n if (Number.isNaN(started)) return entry.time;\n // Server and browser clocks can disagree by a little; a negative elapsed is noise.\n return Math.max(0, now - started);\n}\n\nfunction harStatusLabel(status: number, entry: HAREntry): string {\n if (entry._pending) return \"running\";\n if (entry._error) return \"ERR\";\n return String(status);\n}\n\nfunction previewError(error: string): string {\n return error.length > ERROR_PREVIEW_CHARS ? `${error.slice(0, ERROR_PREVIEW_CHARS)}…` : error;\n}\n\nexport function HarEntryDetails({ entry, bodySource }: HarEntryDetailsProps) {\n const parentNow = useContext(HarClockContext);\n const ownNow = useResourceClock(Boolean(entry._pending) && parentNow === undefined);\n return <HarClockContext.Provider value={parentNow ?? ownNow}><HarRowDetails entry={entry} bodySource={bodySource} /></HarClockContext.Provider>;\n}\n\nfunction HarRowDetails({ entry, bodySource }: HarEntryDetailsProps) {\n return (\n <>\n {entry._error && (\n <div className=\"mb-density-3\">\n <div className=\"font-semibold text-foreground mb-1\">Transport Error</div>\n <pre className=\"whitespace-pre-wrap break-all text-destructive\">{entry._error}</pre>\n </div>\n )}\n {entry._pending && (\n <div className=\"mb-density-3 text-muted-foreground\">\n Waiting for response — in flight for <PendingElapsed entry={entry} />\n </div>\n )}\n <div className=\"grid grid-cols-2 gap-density-4\">\n <div>\n <HeaderList\n title=\"Request Headers\"\n {...(entry.request.headers ? { headers: entry.request.headers } : {})}\n />\n {(entry.request.postData?.text || bodySource?.hasBody(entry, \"request\")) && (\n <BodySection entry={entry} part=\"request\" text={entry.request.postData?.text}\n mimeType={entry.request.postData?.mimeType} bodySource={bodySource} />\n )}\n </div>\n <div>\n <HeaderList\n title=\"Response Headers\"\n {...(entry.response.headers ? { headers: entry.response.headers } : {})}\n />\n </div>\n </div>\n {(entry.response.content?.text || bodySource?.hasBody(entry, \"response\")) && (\n <BodySection entry={entry} part=\"response\" text={entry.response.content?.text}\n mimeType={entry.response.content?.mimeType} bodySource={bodySource} />\n )}\n </>\n );\n}\n\nfunction BodySection({ entry, part, text, mimeType, bodySource }: {\n entry: HAREntry;\n part: \"request\" | \"response\";\n text?: string | undefined;\n mimeType?: string | undefined;\n bodySource?: HarPanelBodySource | undefined;\n}) {\n const remote = !text && bodySource?.hasBody(entry, part);\n const requestId = entry._id;\n if (remote && !requestId) throw new Error(\"Private HAR body requires a request ID\");\n const [preview, setPreview] = useState<string | undefined>();\n const [error, setError] = useState<Error | undefined>();\n useEffect(() => {\n if (!remote || !bodySource || !requestId) return;\n let active = true;\n setPreview(undefined);\n setError(undefined);\n bodySource.preview(requestId, part).then(\n (value) => { if (active) setPreview(value); },\n (reason: unknown) => { if (active) setError(reason instanceof Error ? reason : new Error(String(reason))); },\n );\n return () => { active = false; };\n }, [bodySource, requestId, part, remote]);\n return (\n <div className=\"mt-density-3\">\n <div className=\"mb-1 flex items-center gap-2 font-semibold text-foreground\">\n {part === \"request\" ? \"Request Body\" : \"Response Body\"}\n {remote && bodySource && <a className=\"text-sm font-normal text-primary underline\" href={bodySource.downloadHref(entry, part)} download>Download full body</a>}\n </div>\n <div className=\"bg-background p-density-2 rounded border border-border overflow-auto max-h-64\">\n {error ? <span role=\"alert\" className=\"text-destructive\">{error.message}</span>\n : remote && preview === undefined ? <span className=\"text-muted-foreground\">Loading preview…</span>\n : <BodyView text={text || preview || \"\"} {...(mimeType ? { mimeType } : {})} />}\n </div>\n {remote && <p className=\"mt-1 text-xs text-muted-foreground\">Preview shows at most the first 64 KiB.</p>}\n </div>\n );\n}\n\nfunction HeaderList({\n title,\n headers,\n}: {\n title: string;\n headers?: { name: string; value: string }[];\n}) {\n return (\n <>\n <div className=\"font-semibold text-foreground mb-1\">{title}</div>\n <div className=\"space-y-0.5\">\n {headers?.map((h, i) => (\n <div key={i} className=\"whitespace-nowrap\">\n <span className=\"text-purple-600 dark:text-purple-400\">{h.name}:</span> {h.value}\n </div>\n ))}\n </div>\n </>\n );\n}\n\nfunction BodyView({ text, mimeType }: { text: string; mimeType?: string }) {\n if (isJsonType(mimeType)) {\n const parsed = tryParseJson(text);\n if (parsed !== null) return <JsonView data={parsed} />;\n }\n return <pre className=\"whitespace-pre-wrap break-all\">{text}</pre>;\n}\n\nfunction tryParseJson(text: string): unknown | null {\n try {\n return JSON.parse(text);\n } catch {\n return null;\n }\n}\n\nfunction isJsonType(mime?: string): boolean {\n return !!mime && (mime.includes(\"json\") || mime.includes(\"javascript\"));\n}\n\nfunction matchesSearch(needle: string, entry: HAREntry): boolean {\n return [\n entry.request.method,\n entry.request.url,\n entry.request.postData?.text,\n entry.response.content?.text,\n entry._error,\n ].some((value) => !!value && value.toLowerCase().includes(needle));\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 0) return \"\";\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / 1024 / 1024).toFixed(1)}MB`;\n}\n\nfunction statusColor(status: number): string {\n if (status >= 500) return \"text-red-600\";\n if (status >= 400) return \"text-amber-600\";\n if (status >= 300) return \"text-blue-600\";\n if (status >= 200) return \"text-green-600\";\n return \"text-muted-foreground\";\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const JsonView = require("../JsonView.cjs");
|
|
5
|
+
const utils = require("../../lib/utils.cjs");
|
|
6
|
+
function formatValue(value) {
|
|
7
|
+
if (typeof value === "string") return value;
|
|
8
|
+
return JSON.stringify(value);
|
|
9
|
+
}
|
|
10
|
+
function isStructured(value) {
|
|
11
|
+
if (Array.isArray(value)) return value.some((item) => item !== null && typeof item === "object");
|
|
12
|
+
return value !== null && typeof value === "object";
|
|
13
|
+
}
|
|
14
|
+
function Value({
|
|
15
|
+
value,
|
|
16
|
+
tree = false,
|
|
17
|
+
className
|
|
18
|
+
}) {
|
|
19
|
+
if (tree && isStructured(value)) return /* @__PURE__ */ jsxRuntime.jsx(JsonView.JsonView, { data: value });
|
|
20
|
+
return /* @__PURE__ */ jsxRuntime.jsx("code", { className: utils.cn("whitespace-pre-wrap break-words font-mono", className), children: formatValue(value) });
|
|
21
|
+
}
|
|
22
|
+
function Row({ label, children }) {
|
|
23
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
24
|
+
/* @__PURE__ */ jsxRuntime.jsx("dt", { className: "text-muted-foreground", children: label }),
|
|
25
|
+
/* @__PURE__ */ jsxRuntime.jsx("dd", { className: "min-w-0", children })
|
|
26
|
+
] });
|
|
27
|
+
}
|
|
28
|
+
function Expectation({
|
|
29
|
+
check,
|
|
30
|
+
checkLabel = "matcher",
|
|
31
|
+
expected,
|
|
32
|
+
actual,
|
|
33
|
+
vars,
|
|
34
|
+
location,
|
|
35
|
+
className
|
|
36
|
+
}) {
|
|
37
|
+
const varEntries = Object.entries(vars ?? {});
|
|
38
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
39
|
+
"dl",
|
|
40
|
+
{
|
|
41
|
+
className: utils.cn(
|
|
42
|
+
"grid grid-cols-[auto_minmax(0,1fr)] items-baseline gap-x-3 gap-y-0.5 rounded border border-border bg-muted/30 px-2 py-1.5 text-[11px]",
|
|
43
|
+
className
|
|
44
|
+
),
|
|
45
|
+
"data-expectation": "",
|
|
46
|
+
children: [
|
|
47
|
+
check && /* @__PURE__ */ jsxRuntime.jsx(Row, { label: checkLabel, children: /* @__PURE__ */ jsxRuntime.jsx(Value, { value: check }) }),
|
|
48
|
+
varEntries.map(([name, value]) => /* @__PURE__ */ jsxRuntime.jsx(Row, { label: name, children: /* @__PURE__ */ jsxRuntime.jsx(Value, { value, tree: true }) }, name)),
|
|
49
|
+
expected !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Row, { label: "expected", children: /* @__PURE__ */ jsxRuntime.jsx(Value, { value: expected, className: "text-emerald-700 dark:text-emerald-400" }) }),
|
|
50
|
+
actual !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Row, { label: "actual", children: /* @__PURE__ */ jsxRuntime.jsx(Value, { value: actual, className: "text-red-700 dark:text-red-400" }) }),
|
|
51
|
+
location && /* @__PURE__ */ jsxRuntime.jsx(Row, { label: "at", children: /* @__PURE__ */ jsxRuntime.jsx(Value, { value: location, className: "text-muted-foreground" }) })
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
exports.Expectation = Expectation;
|
|
57
|
+
//# sourceMappingURL=Expectation.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Expectation.cjs","sources":["../../../src/data/test-runner/Expectation.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { JsonView } from \"../JsonView\";\nimport { cn } from \"../../lib/utils\";\n\nexport type ExpectationProps = {\n /** What decided the verdict: a matcher name (`Equal`) or a CEL expression. */\n check?: string | undefined;\n /** Label for `check`; defaults to \"matcher\". */\n checkLabel?: string | undefined;\n expected?: unknown;\n actual?: unknown;\n /** Variables the check saw (CEL `cel_vars`). */\n vars?: Record<string, unknown> | undefined;\n location?: string | undefined;\n className?: string | undefined;\n};\n\nfunction formatValue(value: unknown): string {\n if (typeof value === \"string\") return value;\n return JSON.stringify(value);\n}\n\n// A recorder summary or a list of rows nests too deep to read as one line.\nfunction isStructured(value: unknown): boolean {\n if (Array.isArray(value)) return value.some((item) => item !== null && typeof item === \"object\");\n return value !== null && typeof value === \"object\";\n}\n\nfunction Value({\n value,\n tree = false,\n className,\n}: {\n value: unknown;\n /** Expand a structured value as a tree instead of one JSON line. */\n tree?: boolean;\n className?: string;\n}) {\n if (tree && isStructured(value)) return <JsonView data={value} />;\n return (\n <code className={cn(\"whitespace-pre-wrap break-words font-mono\", className)}>\n {formatValue(value)}\n </code>\n );\n}\n\nfunction Row({ label, children }: { label: string; children: ReactNode }) {\n return (\n <>\n <dt className=\"text-muted-foreground\">{label}</dt>\n <dd className=\"min-w-0\">{children}</dd>\n </>\n );\n}\n\n/**\n * One compact label/value grid for what a check expected and what it got —\n * shared by test failures (matcher, expected, actual, location) and CEL\n * assertions (expression, variables, expected, actual).\n */\nexport function Expectation({\n check,\n checkLabel = \"matcher\",\n expected,\n actual,\n vars,\n location,\n className,\n}: ExpectationProps) {\n const varEntries = Object.entries(vars ?? {});\n return (\n <dl\n className={cn(\n \"grid grid-cols-[auto_minmax(0,1fr)] items-baseline gap-x-3 gap-y-0.5 rounded border border-border bg-muted/30 px-2 py-1.5 text-[11px]\",\n className,\n )}\n data-expectation=\"\"\n >\n {check && (\n <Row label={checkLabel}>\n <Value value={check} />\n </Row>\n )}\n {varEntries.map(([name, value]) => (\n <Row key={name} label={name}>\n <Value value={value} tree />\n </Row>\n ))}\n {expected !== undefined && (\n <Row label=\"expected\">\n <Value value={expected} className=\"text-emerald-700 dark:text-emerald-400\" />\n </Row>\n )}\n {actual !== undefined && (\n <Row label=\"actual\">\n <Value value={actual} className=\"text-red-700 dark:text-red-400\" />\n </Row>\n )}\n {location && (\n <Row label=\"at\">\n <Value value={location} className=\"text-muted-foreground\" />\n </Row>\n )}\n </dl>\n );\n}\n"],"names":["jsx","JsonView","cn","jsxs","Fragment"],"mappings":";;;;;AAiBA,SAAS,YAAY,OAAwB;AAC3C,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,SAAO,KAAK,UAAU,KAAK;AAC7B;AAGA,SAAS,aAAa,OAAyB;AAC7C,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,KAAK,CAAC,SAAS,SAAS,QAAQ,OAAO,SAAS,QAAQ;AAC/F,SAAO,UAAU,QAAQ,OAAO,UAAU;AAC5C;AAEA,SAAS,MAAM;AAAA,EACb;AAAA,EACA,OAAO;AAAA,EACP;AACF,GAKG;AACD,MAAI,QAAQ,aAAa,KAAK,UAAUA,2BAAAA,IAACC,mBAAA,EAAS,MAAM,OAAO;AAC/D,SACED,+BAAC,UAAK,WAAWE,MAAAA,GAAG,6CAA6C,SAAS,GACvE,UAAA,YAAY,KAAK,EAAA,CACpB;AAEJ;AAEA,SAAS,IAAI,EAAE,OAAO,YAAoD;AACxE,SACEC,2BAAAA,KAAAC,qBAAA,EACE,UAAA;AAAA,IAAAJ,2BAAAA,IAAC,MAAA,EAAG,WAAU,yBAAyB,UAAA,OAAM;AAAA,IAC7CA,2BAAAA,IAAC,MAAA,EAAG,WAAU,WAAW,SAAA,CAAS;AAAA,EAAA,GACpC;AAEJ;AAOO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAqB;AACnB,QAAM,aAAa,OAAO,QAAQ,QAAQ,CAAA,CAAE;AAC5C,SACEG,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAWD,MAAAA;AAAAA,QACT;AAAA,QACA;AAAA,MAAA;AAAA,MAEF,oBAAiB;AAAA,MAEhB,UAAA;AAAA,QAAA,SACCF,2BAAAA,IAAC,OAAI,OAAO,YACV,yCAAC,OAAA,EAAM,OAAO,OAAO,EAAA,CACvB;AAAA,QAED,WAAW,IAAI,CAAC,CAAC,MAAM,KAAK,MAC3BA,2BAAAA,IAAC,KAAA,EAAe,OAAO,MACrB,yCAAC,OAAA,EAAM,OAAc,MAAI,KAAA,CAAC,KADlB,IAEV,CACD;AAAA,QACA,aAAa,UACZA,2BAAAA,IAAC,KAAA,EAAI,OAAM,YACT,UAAAA,2BAAAA,IAAC,OAAA,EAAM,OAAO,UAAU,WAAU,yCAAA,CAAyC,GAC7E;AAAA,QAED,WAAW,UACVA,2BAAAA,IAAC,KAAA,EAAI,OAAM,UACT,UAAAA,2BAAAA,IAAC,OAAA,EAAM,OAAO,QAAQ,WAAU,iCAAA,CAAiC,GACnE;AAAA,QAED,YACCA,2BAAAA,IAAC,KAAA,EAAI,OAAM,MACT,UAAAA,2BAAAA,IAAC,OAAA,EAAM,OAAO,UAAU,WAAU,wBAAA,CAAwB,EAAA,CAC5D;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAIR;;"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type ExpectationProps = {
|
|
2
|
+
/** What decided the verdict: a matcher name (`Equal`) or a CEL expression. */
|
|
3
|
+
check?: string | undefined;
|
|
4
|
+
/** Label for `check`; defaults to "matcher". */
|
|
5
|
+
checkLabel?: string | undefined;
|
|
6
|
+
expected?: unknown;
|
|
7
|
+
actual?: unknown;
|
|
8
|
+
/** Variables the check saw (CEL `cel_vars`). */
|
|
9
|
+
vars?: Record<string, unknown> | undefined;
|
|
10
|
+
location?: string | undefined;
|
|
11
|
+
className?: string | undefined;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* One compact label/value grid for what a check expected and what it got —
|
|
15
|
+
* shared by test failures (matcher, expected, actual, location) and CEL
|
|
16
|
+
* assertions (expression, variables, expected, actual).
|
|
17
|
+
*/
|
|
18
|
+
export declare function Expectation({ check, checkLabel, expected, actual, vars, location, className, }: ExpectationProps): import("react").JSX.Element;
|
|
19
|
+
//# sourceMappingURL=Expectation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Expectation.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/Expectation.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,gDAAgD;IAChD,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAAC;AAwCF;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,UAAsB,EACtB,QAAQ,EACR,MAAM,EACN,IAAI,EACJ,QAAQ,EACR,SAAS,GACV,EAAE,gBAAgB,+BAqClB"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { JsonView } from "../JsonView.js";
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
function formatValue(value) {
|
|
5
|
+
if (typeof value === "string") return value;
|
|
6
|
+
return JSON.stringify(value);
|
|
7
|
+
}
|
|
8
|
+
function isStructured(value) {
|
|
9
|
+
if (Array.isArray(value)) return value.some((item) => item !== null && typeof item === "object");
|
|
10
|
+
return value !== null && typeof value === "object";
|
|
11
|
+
}
|
|
12
|
+
function Value({
|
|
13
|
+
value,
|
|
14
|
+
tree = false,
|
|
15
|
+
className
|
|
16
|
+
}) {
|
|
17
|
+
if (tree && isStructured(value)) return /* @__PURE__ */ jsx(JsonView, { data: value });
|
|
18
|
+
return /* @__PURE__ */ jsx("code", { className: cn("whitespace-pre-wrap break-words font-mono", className), children: formatValue(value) });
|
|
19
|
+
}
|
|
20
|
+
function Row({ label, children }) {
|
|
21
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
22
|
+
/* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: label }),
|
|
23
|
+
/* @__PURE__ */ jsx("dd", { className: "min-w-0", children })
|
|
24
|
+
] });
|
|
25
|
+
}
|
|
26
|
+
function Expectation({
|
|
27
|
+
check,
|
|
28
|
+
checkLabel = "matcher",
|
|
29
|
+
expected,
|
|
30
|
+
actual,
|
|
31
|
+
vars,
|
|
32
|
+
location,
|
|
33
|
+
className
|
|
34
|
+
}) {
|
|
35
|
+
const varEntries = Object.entries(vars ?? {});
|
|
36
|
+
return /* @__PURE__ */ jsxs(
|
|
37
|
+
"dl",
|
|
38
|
+
{
|
|
39
|
+
className: cn(
|
|
40
|
+
"grid grid-cols-[auto_minmax(0,1fr)] items-baseline gap-x-3 gap-y-0.5 rounded border border-border bg-muted/30 px-2 py-1.5 text-[11px]",
|
|
41
|
+
className
|
|
42
|
+
),
|
|
43
|
+
"data-expectation": "",
|
|
44
|
+
children: [
|
|
45
|
+
check && /* @__PURE__ */ jsx(Row, { label: checkLabel, children: /* @__PURE__ */ jsx(Value, { value: check }) }),
|
|
46
|
+
varEntries.map(([name, value]) => /* @__PURE__ */ jsx(Row, { label: name, children: /* @__PURE__ */ jsx(Value, { value, tree: true }) }, name)),
|
|
47
|
+
expected !== void 0 && /* @__PURE__ */ jsx(Row, { label: "expected", children: /* @__PURE__ */ jsx(Value, { value: expected, className: "text-emerald-700 dark:text-emerald-400" }) }),
|
|
48
|
+
actual !== void 0 && /* @__PURE__ */ jsx(Row, { label: "actual", children: /* @__PURE__ */ jsx(Value, { value: actual, className: "text-red-700 dark:text-red-400" }) }),
|
|
49
|
+
location && /* @__PURE__ */ jsx(Row, { label: "at", children: /* @__PURE__ */ jsx(Value, { value: location, className: "text-muted-foreground" }) })
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
export {
|
|
55
|
+
Expectation
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=Expectation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Expectation.js","sources":["../../../src/data/test-runner/Expectation.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { JsonView } from \"../JsonView\";\nimport { cn } from \"../../lib/utils\";\n\nexport type ExpectationProps = {\n /** What decided the verdict: a matcher name (`Equal`) or a CEL expression. */\n check?: string | undefined;\n /** Label for `check`; defaults to \"matcher\". */\n checkLabel?: string | undefined;\n expected?: unknown;\n actual?: unknown;\n /** Variables the check saw (CEL `cel_vars`). */\n vars?: Record<string, unknown> | undefined;\n location?: string | undefined;\n className?: string | undefined;\n};\n\nfunction formatValue(value: unknown): string {\n if (typeof value === \"string\") return value;\n return JSON.stringify(value);\n}\n\n// A recorder summary or a list of rows nests too deep to read as one line.\nfunction isStructured(value: unknown): boolean {\n if (Array.isArray(value)) return value.some((item) => item !== null && typeof item === \"object\");\n return value !== null && typeof value === \"object\";\n}\n\nfunction Value({\n value,\n tree = false,\n className,\n}: {\n value: unknown;\n /** Expand a structured value as a tree instead of one JSON line. */\n tree?: boolean;\n className?: string;\n}) {\n if (tree && isStructured(value)) return <JsonView data={value} />;\n return (\n <code className={cn(\"whitespace-pre-wrap break-words font-mono\", className)}>\n {formatValue(value)}\n </code>\n );\n}\n\nfunction Row({ label, children }: { label: string; children: ReactNode }) {\n return (\n <>\n <dt className=\"text-muted-foreground\">{label}</dt>\n <dd className=\"min-w-0\">{children}</dd>\n </>\n );\n}\n\n/**\n * One compact label/value grid for what a check expected and what it got —\n * shared by test failures (matcher, expected, actual, location) and CEL\n * assertions (expression, variables, expected, actual).\n */\nexport function Expectation({\n check,\n checkLabel = \"matcher\",\n expected,\n actual,\n vars,\n location,\n className,\n}: ExpectationProps) {\n const varEntries = Object.entries(vars ?? {});\n return (\n <dl\n className={cn(\n \"grid grid-cols-[auto_minmax(0,1fr)] items-baseline gap-x-3 gap-y-0.5 rounded border border-border bg-muted/30 px-2 py-1.5 text-[11px]\",\n className,\n )}\n data-expectation=\"\"\n >\n {check && (\n <Row label={checkLabel}>\n <Value value={check} />\n </Row>\n )}\n {varEntries.map(([name, value]) => (\n <Row key={name} label={name}>\n <Value value={value} tree />\n </Row>\n ))}\n {expected !== undefined && (\n <Row label=\"expected\">\n <Value value={expected} className=\"text-emerald-700 dark:text-emerald-400\" />\n </Row>\n )}\n {actual !== undefined && (\n <Row label=\"actual\">\n <Value value={actual} className=\"text-red-700 dark:text-red-400\" />\n </Row>\n )}\n {location && (\n <Row label=\"at\">\n <Value value={location} className=\"text-muted-foreground\" />\n </Row>\n )}\n </dl>\n );\n}\n"],"names":[],"mappings":";;;AAiBA,SAAS,YAAY,OAAwB;AAC3C,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,SAAO,KAAK,UAAU,KAAK;AAC7B;AAGA,SAAS,aAAa,OAAyB;AAC7C,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,KAAK,CAAC,SAAS,SAAS,QAAQ,OAAO,SAAS,QAAQ;AAC/F,SAAO,UAAU,QAAQ,OAAO,UAAU;AAC5C;AAEA,SAAS,MAAM;AAAA,EACb;AAAA,EACA,OAAO;AAAA,EACP;AACF,GAKG;AACD,MAAI,QAAQ,aAAa,KAAK,UAAU,oBAAC,UAAA,EAAS,MAAM,OAAO;AAC/D,SACE,oBAAC,UAAK,WAAW,GAAG,6CAA6C,SAAS,GACvE,UAAA,YAAY,KAAK,EAAA,CACpB;AAEJ;AAEA,SAAS,IAAI,EAAE,OAAO,YAAoD;AACxE,SACE,qBAAA,UAAA,EACE,UAAA;AAAA,IAAA,oBAAC,MAAA,EAAG,WAAU,yBAAyB,UAAA,OAAM;AAAA,IAC7C,oBAAC,MAAA,EAAG,WAAU,WAAW,SAAA,CAAS;AAAA,EAAA,GACpC;AAEJ;AAOO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAqB;AACnB,QAAM,aAAa,OAAO,QAAQ,QAAQ,CAAA,CAAE;AAC5C,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MAAA;AAAA,MAEF,oBAAiB;AAAA,MAEhB,UAAA;AAAA,QAAA,SACC,oBAAC,OAAI,OAAO,YACV,8BAAC,OAAA,EAAM,OAAO,OAAO,EAAA,CACvB;AAAA,QAED,WAAW,IAAI,CAAC,CAAC,MAAM,KAAK,MAC3B,oBAAC,KAAA,EAAe,OAAO,MACrB,8BAAC,OAAA,EAAM,OAAc,MAAI,KAAA,CAAC,KADlB,IAEV,CACD;AAAA,QACA,aAAa,UACZ,oBAAC,KAAA,EAAI,OAAM,YACT,UAAA,oBAAC,OAAA,EAAM,OAAO,UAAU,WAAU,yCAAA,CAAyC,GAC7E;AAAA,QAED,WAAW,UACV,oBAAC,KAAA,EAAI,OAAM,UACT,UAAA,oBAAC,OAAA,EAAM,OAAO,QAAQ,WAAU,iCAAA,CAAiC,GACnE;AAAA,QAED,YACC,oBAAC,KAAA,EAAI,OAAM,MACT,UAAA,oBAAC,OAAA,EAAM,OAAO,UAAU,WAAU,wBAAA,CAAwB,EAAA,CAC5D;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAIR;"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const Icon = require("../Icon.cjs");
|
|
5
|
+
const utils = require("../../lib/utils.cjs");
|
|
6
|
+
const marks = require("./marks.cjs");
|
|
7
|
+
function MarkIcon({ mark, className }) {
|
|
8
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9
|
+
Icon.Icon,
|
|
10
|
+
{
|
|
11
|
+
icon: mark.icon,
|
|
12
|
+
title: mark.label,
|
|
13
|
+
className: utils.cn(
|
|
14
|
+
"shrink-0",
|
|
15
|
+
mark.className,
|
|
16
|
+
"spin" in mark && mark.spin && "motion-safe:animate-spin",
|
|
17
|
+
className
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
function OutcomeIcon({ outcome, className }) {
|
|
23
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MarkIcon, { mark: marks.TEST_OUTCOMES[outcome], className });
|
|
24
|
+
}
|
|
25
|
+
function LintSeverityIcon({
|
|
26
|
+
severity,
|
|
27
|
+
className
|
|
28
|
+
}) {
|
|
29
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MarkIcon, { mark: marks.LINT_SEVERITIES[severity], className });
|
|
30
|
+
}
|
|
31
|
+
function ToolIcon({ tool, className }) {
|
|
32
|
+
const mark = marks.toolMark(tool);
|
|
33
|
+
return mark ? /* @__PURE__ */ jsxRuntime.jsx(MarkIcon, { mark, className }) : null;
|
|
34
|
+
}
|
|
35
|
+
exports.LintSeverityIcon = LintSeverityIcon;
|
|
36
|
+
exports.OutcomeIcon = OutcomeIcon;
|
|
37
|
+
exports.ToolIcon = ToolIcon;
|
|
38
|
+
//# sourceMappingURL=MarkIcons.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MarkIcons.cjs","sources":["../../../src/data/test-runner/MarkIcons.tsx"],"sourcesContent":["// Renders the marks.ts catalogs. Kept apart from them so this file exports\n// components only (react/only-export-components).\n\nimport { Icon } from \"../Icon\";\nimport { cn } from \"../../lib/utils\";\nimport {\n LINT_SEVERITIES,\n TEST_OUTCOMES,\n toolMark,\n type LintSeverity,\n type ResultMark,\n type TestOutcome,\n type ToolMark,\n} from \"./marks\";\n\ntype MarkIconProps = { className?: string | undefined };\n\nfunction MarkIcon({ mark, className }: MarkIconProps & { mark: ResultMark | ToolMark }) {\n return (\n <Icon\n icon={mark.icon}\n title={mark.label}\n className={cn(\n \"shrink-0\",\n mark.className,\n \"spin\" in mark && mark.spin && \"motion-safe:animate-spin\",\n className,\n )}\n />\n );\n}\n\nexport function OutcomeIcon({ outcome, className }: MarkIconProps & { outcome: TestOutcome }) {\n return <MarkIcon mark={TEST_OUTCOMES[outcome]} className={className} />;\n}\n\nexport function LintSeverityIcon({\n severity,\n className,\n}: MarkIconProps & { severity: LintSeverity }) {\n return <MarkIcon mark={LINT_SEVERITIES[severity]} className={className} />;\n}\n\nexport function ToolIcon({ tool, className }: MarkIconProps & { tool: string | undefined }) {\n const mark = toolMark(tool);\n return mark ? <MarkIcon mark={mark} className={className} /> : null;\n}\n"],"names":["jsx","Icon","cn","TEST_OUTCOMES","LINT_SEVERITIES","toolMark"],"mappings":";;;;;;AAiBA,SAAS,SAAS,EAAE,MAAM,aAA8D;AACtF,SACEA,2BAAAA;AAAAA,IAACC,KAAAA;AAAAA,IAAA;AAAA,MACC,MAAM,KAAK;AAAA,MACX,OAAO,KAAK;AAAA,MACZ,WAAWC,MAAAA;AAAAA,QACT;AAAA,QACA,KAAK;AAAA,QACL,UAAU,QAAQ,KAAK,QAAQ;AAAA,QAC/B;AAAA,MAAA;AAAA,IACF;AAAA,EAAA;AAGN;AAEO,SAAS,YAAY,EAAE,SAAS,aAAuD;AAC5F,wCAAQ,UAAA,EAAS,MAAMC,MAAAA,cAAc,OAAO,GAAG,WAAsB;AACvE;AAEO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AACF,GAA+C;AAC7C,wCAAQ,UAAA,EAAS,MAAMC,MAAAA,gBAAgB,QAAQ,GAAG,WAAsB;AAC1E;AAEO,SAAS,SAAS,EAAE,MAAM,aAA2D;AAC1F,QAAM,OAAOC,MAAAA,SAAS,IAAI;AAC1B,SAAO,OAAOL,2BAAAA,IAAC,UAAA,EAAS,MAAY,WAAsB,IAAK;AACjE;;;;"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { LintSeverity, TestOutcome } from './marks.js';
|
|
2
|
+
type MarkIconProps = {
|
|
3
|
+
className?: string | undefined;
|
|
4
|
+
};
|
|
5
|
+
export declare function OutcomeIcon({ outcome, className }: MarkIconProps & {
|
|
6
|
+
outcome: TestOutcome;
|
|
7
|
+
}): import("react").JSX.Element;
|
|
8
|
+
export declare function LintSeverityIcon({ severity, className, }: MarkIconProps & {
|
|
9
|
+
severity: LintSeverity;
|
|
10
|
+
}): import("react").JSX.Element;
|
|
11
|
+
export declare function ToolIcon({ tool, className }: MarkIconProps & {
|
|
12
|
+
tool: string | undefined;
|
|
13
|
+
}): import("react").JSX.Element | null;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=MarkIcons.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MarkIcons.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/MarkIcons.tsx"],"names":[],"mappings":"AAKA,OAAO,EAIL,KAAK,YAAY,EAEjB,KAAK,WAAW,EAEjB,MAAM,SAAS,CAAC;AAEjB,KAAK,aAAa,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAiBxD,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,aAAa,GAAG;IAAE,OAAO,EAAE,WAAW,CAAA;CAAE,+BAE3F;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,SAAS,GACV,EAAE,aAAa,GAAG;IAAE,QAAQ,EAAE,YAAY,CAAA;CAAE,+BAE5C;AAED,wBAAgB,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,aAAa,GAAG;IAAE,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,sCAGzF"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Icon } from "../Icon.js";
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
import { LINT_SEVERITIES, TEST_OUTCOMES, toolMark } from "./marks.js";
|
|
5
|
+
function MarkIcon({ mark, className }) {
|
|
6
|
+
return /* @__PURE__ */ jsx(
|
|
7
|
+
Icon,
|
|
8
|
+
{
|
|
9
|
+
icon: mark.icon,
|
|
10
|
+
title: mark.label,
|
|
11
|
+
className: cn(
|
|
12
|
+
"shrink-0",
|
|
13
|
+
mark.className,
|
|
14
|
+
"spin" in mark && mark.spin && "motion-safe:animate-spin",
|
|
15
|
+
className
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
function OutcomeIcon({ outcome, className }) {
|
|
21
|
+
return /* @__PURE__ */ jsx(MarkIcon, { mark: TEST_OUTCOMES[outcome], className });
|
|
22
|
+
}
|
|
23
|
+
function LintSeverityIcon({
|
|
24
|
+
severity,
|
|
25
|
+
className
|
|
26
|
+
}) {
|
|
27
|
+
return /* @__PURE__ */ jsx(MarkIcon, { mark: LINT_SEVERITIES[severity], className });
|
|
28
|
+
}
|
|
29
|
+
function ToolIcon({ tool, className }) {
|
|
30
|
+
const mark = toolMark(tool);
|
|
31
|
+
return mark ? /* @__PURE__ */ jsx(MarkIcon, { mark, className }) : null;
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
LintSeverityIcon,
|
|
35
|
+
OutcomeIcon,
|
|
36
|
+
ToolIcon
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=MarkIcons.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MarkIcons.js","sources":["../../../src/data/test-runner/MarkIcons.tsx"],"sourcesContent":["// Renders the marks.ts catalogs. Kept apart from them so this file exports\n// components only (react/only-export-components).\n\nimport { Icon } from \"../Icon\";\nimport { cn } from \"../../lib/utils\";\nimport {\n LINT_SEVERITIES,\n TEST_OUTCOMES,\n toolMark,\n type LintSeverity,\n type ResultMark,\n type TestOutcome,\n type ToolMark,\n} from \"./marks\";\n\ntype MarkIconProps = { className?: string | undefined };\n\nfunction MarkIcon({ mark, className }: MarkIconProps & { mark: ResultMark | ToolMark }) {\n return (\n <Icon\n icon={mark.icon}\n title={mark.label}\n className={cn(\n \"shrink-0\",\n mark.className,\n \"spin\" in mark && mark.spin && \"motion-safe:animate-spin\",\n className,\n )}\n />\n );\n}\n\nexport function OutcomeIcon({ outcome, className }: MarkIconProps & { outcome: TestOutcome }) {\n return <MarkIcon mark={TEST_OUTCOMES[outcome]} className={className} />;\n}\n\nexport function LintSeverityIcon({\n severity,\n className,\n}: MarkIconProps & { severity: LintSeverity }) {\n return <MarkIcon mark={LINT_SEVERITIES[severity]} className={className} />;\n}\n\nexport function ToolIcon({ tool, className }: MarkIconProps & { tool: string | undefined }) {\n const mark = toolMark(tool);\n return mark ? <MarkIcon mark={mark} className={className} /> : null;\n}\n"],"names":[],"mappings":";;;;AAiBA,SAAS,SAAS,EAAE,MAAM,aAA8D;AACtF,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAM,KAAK;AAAA,MACX,OAAO,KAAK;AAAA,MACZ,WAAW;AAAA,QACT;AAAA,QACA,KAAK;AAAA,QACL,UAAU,QAAQ,KAAK,QAAQ;AAAA,QAC/B;AAAA,MAAA;AAAA,IACF;AAAA,EAAA;AAGN;AAEO,SAAS,YAAY,EAAE,SAAS,aAAuD;AAC5F,6BAAQ,UAAA,EAAS,MAAM,cAAc,OAAO,GAAG,WAAsB;AACvE;AAEO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AACF,GAA+C;AAC7C,6BAAQ,UAAA,EAAS,MAAM,gBAAgB,QAAQ,GAAG,WAAsB;AAC1E;AAEO,SAAS,SAAS,EAAE,MAAM,aAA2D;AAC1F,QAAM,OAAO,SAAS,IAAI;AAC1B,SAAO,OAAO,oBAAC,UAAA,EAAS,MAAY,WAAsB,IAAK;AACjE;"}
|
|
@@ -4,24 +4,24 @@ const jsxRuntime = require("react/jsx-runtime");
|
|
|
4
4
|
const react = require("react");
|
|
5
5
|
const Icon = require("../Icon.cjs");
|
|
6
6
|
const button = require("../../components/button.cjs");
|
|
7
|
+
const SplitButton = require("../../components/SplitButton.cjs");
|
|
8
|
+
const clipboard = require("../../components/clipboard.cjs");
|
|
9
|
+
const utils = require("../../lib/utils.cjs");
|
|
7
10
|
const JsonView = require("../JsonView.cjs");
|
|
8
11
|
const TabButton = require("../TabButton.cjs");
|
|
9
|
-
const utils = require("../../lib/utils.cjs");
|
|
10
12
|
const context = require("./context.cjs");
|
|
13
|
+
const copy = require("./copy.cjs");
|
|
11
14
|
const status = require("./status.cjs");
|
|
12
|
-
const
|
|
15
|
+
const MarkIcons = require("./MarkIcons.cjs");
|
|
13
16
|
const TestFailureDetail = require("./TestFailureDetail.cjs");
|
|
14
17
|
const UiClass = require("../../icons/components/UiClass.cjs");
|
|
18
|
+
const UiClock = require("../../icons/components/UiClock.cjs");
|
|
15
19
|
const UiRefresh = require("../../icons/components/UiRefresh.cjs");
|
|
16
20
|
const UiStop = require("../../icons/components/UiStop.cjs");
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
success: "emerald",
|
|
22
|
-
info: "sky",
|
|
23
|
-
neutral: "neutral"
|
|
24
|
-
};
|
|
21
|
+
const UiJson = require("../../icons/components/UiJson.cjs");
|
|
22
|
+
const UiWarningCircle = require("../../icons/components/UiWarningCircle.cjs");
|
|
23
|
+
const UiCheck = require("../../icons/components/UiCheck.cjs");
|
|
24
|
+
const UiCopy = require("../../icons/components/UiCopy.cjs");
|
|
25
25
|
function DefaultDetailBody({ node }) {
|
|
26
26
|
var _a;
|
|
27
27
|
const hasChildren = (((_a = node.children) == null ? void 0 : _a.length) ?? 0) > 0;
|
|
@@ -55,25 +55,71 @@ function DefaultDetailBody({ node }) {
|
|
|
55
55
|
] })
|
|
56
56
|
] });
|
|
57
57
|
}
|
|
58
|
+
function copyMenuItem(item, copy2) {
|
|
59
|
+
const icon = item.icon ? { icon: item.icon } : {};
|
|
60
|
+
if ("reason" in item) {
|
|
61
|
+
return { label: item.label, ...icon, disabled: true, title: item.reason, onSelect: () => {
|
|
62
|
+
} };
|
|
63
|
+
}
|
|
64
|
+
return { label: item.label, ...icon, onSelect: () => copy2(item.value) };
|
|
65
|
+
}
|
|
66
|
+
const COPY_LABEL = { idle: "Copy details", copied: "Copied", error: "Copy failed" };
|
|
67
|
+
const COPY_ICON = { idle: UiCopy.UiCopy, copied: UiCheck.UiCheck, error: UiWarningCircle.UiWarningCircle };
|
|
68
|
+
function CopyDetailsButton({ node }) {
|
|
69
|
+
const runner = context.useTestRunner();
|
|
70
|
+
const { state, copy: copy$1 } = clipboard.useCopyFlash();
|
|
71
|
+
const sections = react.useMemo(() => runner.adapters.resolveCopy(node, runner), [node, runner]);
|
|
72
|
+
const items = [
|
|
73
|
+
{ label: "Copy as JSON", icon: UiJson.UiJson, onSelect: () => copy$1(copy.buildNodeJson(node)) },
|
|
74
|
+
...sections.items.map((item) => copyMenuItem(item, copy$1))
|
|
75
|
+
];
|
|
76
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
77
|
+
SplitButton.SplitButton,
|
|
78
|
+
{
|
|
79
|
+
label: COPY_LABEL[state],
|
|
80
|
+
icon: COPY_ICON[state],
|
|
81
|
+
variant: "outline",
|
|
82
|
+
size: "sm",
|
|
83
|
+
align: "right",
|
|
84
|
+
title: "More copy options",
|
|
85
|
+
onClick: () => copy$1(copy.buildNodeBrief(node, sections)),
|
|
86
|
+
items
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
}
|
|
58
90
|
function DetailHeader({ node, actions }) {
|
|
59
91
|
const runner = context.useTestRunner();
|
|
60
|
-
const
|
|
92
|
+
const outcome = status.outcomeFor(node);
|
|
61
93
|
const canRerun = !!runner.onRerun && node.framework !== "task";
|
|
62
94
|
const canStop = !!runner.onStop && !!node.can_stop && !!node.task_id;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
95
|
+
const location = node.file ? `${node.file}${node.line ? `:${node.line}` : ""}` : void 0;
|
|
96
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
97
|
+
"div",
|
|
98
|
+
{
|
|
99
|
+
className: "flex min-w-0 items-center gap-2 border-b border-border px-density-4 py-density-2",
|
|
100
|
+
"data-detail-header": "",
|
|
101
|
+
children: [
|
|
102
|
+
outcome ? /* @__PURE__ */ jsxRuntime.jsx(MarkIcons.OutcomeIcon, { outcome, className: "text-lg" }) : /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiClass.UiClass, className: "shrink-0 text-lg text-muted-foreground" }),
|
|
103
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "min-w-0 shrink truncate text-base font-semibold", title: node.name, children: node.name }),
|
|
104
|
+
node.framework && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex shrink-0 items-center gap-1 rounded bg-muted px-1.5 py-0.5 text-xs text-muted-foreground", children: [
|
|
105
|
+
/* @__PURE__ */ jsxRuntime.jsx(MarkIcons.ToolIcon, { tool: node.framework, className: "text-sm" }),
|
|
106
|
+
node.framework
|
|
107
|
+
] }),
|
|
108
|
+
node.duration ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex shrink-0 items-center gap-1 text-xs text-muted-foreground", children: [
|
|
109
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiClock.UiClock, className: "text-xs" }),
|
|
110
|
+
status.formatTestDuration(node.duration)
|
|
111
|
+
] }) : null,
|
|
112
|
+
location && /* @__PURE__ */ jsxRuntime.jsx(
|
|
113
|
+
"span",
|
|
114
|
+
{
|
|
115
|
+
className: "min-w-0 shrink-[100] truncate font-mono text-xs text-muted-foreground",
|
|
116
|
+
title: location,
|
|
117
|
+
children: location
|
|
118
|
+
}
|
|
119
|
+
),
|
|
120
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ml-auto flex shrink-0 items-center gap-1", children: [
|
|
76
121
|
actions,
|
|
122
|
+
/* @__PURE__ */ jsxRuntime.jsx(CopyDetailsButton, { node }),
|
|
77
123
|
canRerun && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
78
124
|
button.Button,
|
|
79
125
|
{
|
|
@@ -102,23 +148,9 @@ function DetailHeader({ node, actions }) {
|
|
|
102
148
|
}
|
|
103
149
|
)
|
|
104
150
|
] })
|
|
105
|
-
]
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: fwIcon, className: "text-sm" }),
|
|
109
|
-
node.framework
|
|
110
|
-
] }),
|
|
111
|
-
node.duration ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
112
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiClock.UiClock, className: "text-xs" }),
|
|
113
|
-
status.formatTestDuration(node.duration)
|
|
114
|
-
] }) : null,
|
|
115
|
-
node.file && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono", children: [
|
|
116
|
-
node.file,
|
|
117
|
-
node.line ? `:${node.line}` : ""
|
|
118
|
-
] })
|
|
119
|
-
] })
|
|
120
|
-
] })
|
|
121
|
-
] });
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
);
|
|
122
154
|
}
|
|
123
155
|
function TestDetailPanel() {
|
|
124
156
|
var _a;
|
|
@@ -144,7 +176,7 @@ function TestDetailPanel() {
|
|
|
144
176
|
const ownsScroll = runner.adapters.ownsScroll(node, runner);
|
|
145
177
|
const body = (detailAdapter == null ? void 0 : detailAdapter.renderDetail) ? detailAdapter.renderDetail({ node, runner }) : /* @__PURE__ */ jsxRuntime.jsx(DefaultDetailBody, { node });
|
|
146
178
|
const actions = (_a = actionAdapter == null ? void 0 : actionAdapter.nodeActions) == null ? void 0 : _a.call(actionAdapter, { node, runner });
|
|
147
|
-
const detailRegion =
|
|
179
|
+
const detailRegion = utils.cn("min-h-0 flex-1 [container-type:size]", !ownsScroll && "overflow-y-auto");
|
|
148
180
|
if (tabs.length === 0) {
|
|
149
181
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [
|
|
150
182
|
/* @__PURE__ */ jsxRuntime.jsx(DetailHeader, { node, actions }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TestDetailPanel.cjs","sources":["../../../src/data/test-runner/TestDetailPanel.tsx"],"sourcesContent":["import { useState, type ReactNode } from \"react\";\nimport { Icon } from \"../Icon\";\nimport { Button } from \"../../components/button\";\nimport { JsonView } from \"../JsonView\";\nimport { TabButton } from \"../TabButton\";\nimport { cn } from \"../../lib/utils\";\nimport { UiClass, UiClock, UiRefresh, UiStop } from \"../../icons\";\nimport type { Test } from \"./types\";\nimport { useTestRunner } from \"./context\";\nimport { formatTestDuration, statusIconFor, statusToneFor, sum } from \"./status\";\nimport { frameworkIcon } from \"./frameworkIcon\";\nimport { TestAttempts, TestFailureDetail, TestOutput } from \"./TestFailureDetail\";\n\nconst ICON_TONE = {\n danger: \"rose\",\n warning: \"amber\",\n success: \"emerald\",\n info: \"sky\",\n neutral: \"neutral\",\n} as const;\n\n/** Built-in detail body — used when no adapter contributes `renderDetail`. */\nfunction DefaultDetailBody({ node }: { node: Test }) {\n const hasChildren = (node.children?.length ?? 0) > 0;\n const counts = hasChildren ? sum(node) : null;\n return (\n <div className=\"space-y-4 p-density-4\">\n {node.message && !node.failure_detail && (\n <p className=\"whitespace-pre-wrap break-words text-sm\">{node.message}</p>\n )}\n {node.failure_detail && <TestFailureDetail failure={node.failure_detail} />}\n <TestOutput node={node} />\n {node.attempts && node.attempts.length > 0 && <TestAttempts attempts={node.attempts} />}\n {counts && counts.total > 0 && (\n <div className=\"flex flex-wrap gap-3 text-xs text-muted-foreground\">\n <span>{counts.total} total</span>\n {counts.passed > 0 && <span>{counts.passed} passed</span>}\n {counts.failed > 0 && <span>{counts.failed} failed</span>}\n {counts.skipped > 0 && <span>{counts.skipped} skipped</span>}\n </div>\n )}\n {node.detail != null && (\n <div className=\"space-y-1\">\n <div className=\"text-[10px] uppercase tracking-wide text-muted-foreground\">Detail</div>\n <JsonView data={node.detail} />\n </div>\n )}\n </div>\n );\n}\n\nfunction DetailHeader({ node, actions }: { node: Test; actions?: ReactNode }) {\n const runner = useTestRunner();\n const fwIcon = frameworkIcon(node.framework);\n const canRerun = !!runner.onRerun && node.framework !== \"task\";\n const canStop = !!runner.onStop && !!node.can_stop && !!node.task_id;\n return (\n <div className=\"flex items-start gap-2 border-b border-border p-density-4\">\n <Icon\n icon={statusIconFor(node)}\n className={cn(\"mt-0.5 shrink-0 text-2xl\", node.running && \"animate-spin\")}\n tone={ICON_TONE[statusToneFor(node)]}\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-start justify-between gap-2\">\n <h2 className=\"break-words text-base font-semibold\">{node.name}</h2>\n <div className=\"flex shrink-0 items-center gap-1\">\n {actions}\n {canRerun && (\n <Button\n size=\"sm\"\n disabled={runner.busy.rerun}\n onClick={() => runner.onRerun!(node)}\n title=\"Rerun this test or subtree\"\n >\n <Icon icon={UiRefresh} className=\"mr-1 text-sm\" />\n {runner.busy.rerun ? \"Running...\" : \"Rerun\"}\n </Button>\n )}\n {canStop && (\n <Button\n size=\"sm\"\n variant=\"destructive\"\n disabled={runner.busy.stop}\n onClick={() => runner.onStop!(node)}\n title=\"Stop this running task\"\n >\n <Icon icon={UiStop} className=\"mr-1 text-sm\" />\n {runner.busy.stop ? \"Stopping...\" : \"Stop\"}\n </Button>\n )}\n </div>\n </div>\n <div className=\"mt-1 flex flex-wrap items-center gap-2 text-xs text-muted-foreground\">\n {fwIcon && (\n <span className=\"inline-flex items-center gap-1 rounded bg-muted px-1.5 py-0.5\">\n <Icon icon={fwIcon} className=\"text-sm\" />\n {node.framework}\n </span>\n )}\n {node.duration ? (\n <span className=\"inline-flex items-center gap-1\">\n <Icon icon={UiClock} className=\"text-xs\" />\n {formatTestDuration(node.duration)}\n </span>\n ) : null}\n {node.file && (\n <span className=\"font-mono\">\n {node.file}\n {node.line ? `:${node.line}` : \"\"}\n </span>\n )}\n </div>\n </div>\n </div>\n );\n}\n\n/**\n * The right pane. Resolves the node's adapter for body / tabs / header actions,\n * falling back to the built-in default body. Shows an empty state when nothing\n * is selected.\n */\nexport function TestDetailPanel() {\n const runner = useTestRunner();\n const node = runner.selected;\n // Tab state is controlled when the host supplies activeTab (e.g. to mirror it\n // into the URL); otherwise the panel owns it. The render below tolerates an\n // unknown id by falling back to the built-in \"detail\" tab.\n const controlled = runner.activeTab !== undefined;\n const [internalTab, setInternalTab] = useState<string>(\"detail\");\n const activeTab = controlled ? runner.activeTab! : internalTab;\n const setActiveTab = (id: string) => {\n if (controlled) runner.onTabChange?.(id);\n else setInternalTab(id);\n };\n\n if (!node) {\n return (\n <div className=\"flex h-full items-center justify-center text-sm text-muted-foreground\">\n <div className=\"text-center\">\n <Icon icon={UiClass} className=\"mb-2 block text-4xl\" />\n Select a test to view its details.\n </div>\n </div>\n );\n }\n\n const detailAdapter = runner.adapters.resolveDetail(node, runner);\n const actionAdapter = runner.adapters.resolveActions(node, runner);\n const tabs = runner.adapters.resolveTabs(node, runner);\n const ownsScroll = runner.adapters.ownsScroll(node, runner);\n\n const body = detailAdapter?.renderDetail\n ? detailAdapter.renderDetail({ node, runner })\n : <DefaultDetailBody node={node} />;\n const actions = actionAdapter?.nodeActions?.({ node, runner });\n\n const detailRegion = ownsScroll ? \"min-h-0 flex-1\" : \"min-h-0 flex-1 overflow-y-auto\";\n\n // No extra tabs → just the body under the header.\n if (tabs.length === 0) {\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <DetailHeader node={node} actions={actions} />\n <div className={detailRegion}>{body}</div>\n </div>\n );\n }\n\n const detailTab = { id: \"detail\", label: \"Detail\", render: () => body };\n const allTabs = [detailTab, ...tabs];\n const active = allTabs.find((t) => t.id === activeTab) ?? detailTab;\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <DetailHeader node={node} actions={actions} />\n <div className=\"flex shrink-0 items-center gap-1 border-b border-border px-density-2 py-density-1\">\n {allTabs.map((t) => (\n <TabButton\n key={t.id}\n active={active.id === t.id}\n onClick={() => setActiveTab(t.id)}\n label={t.label}\n />\n ))}\n </div>\n <div className={detailRegion}>{active.render()}</div>\n </div>\n );\n}\n"],"names":["sum","jsxs","jsx","TestFailureDetail","TestOutput","TestAttempts","JsonView","useTestRunner","frameworkIcon","Icon","statusIconFor","cn","statusToneFor","Button","UiRefresh","UiStop","UiClock","formatTestDuration","useState","_a","UiClass","TabButton"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TestDetailPanel.cjs","sources":["../../../src/data/test-runner/TestDetailPanel.tsx"],"sourcesContent":["import { useMemo, useState, type ReactNode } from \"react\";\nimport { Icon } from \"../Icon\";\nimport { Button } from \"../../components/button\";\nimport { SplitButton } from \"../../components/SplitButton\";\nimport { useCopyFlash } from \"../../components/clipboard\";\nimport type { DropdownMenuItem } from \"../../overlay/DropdownMenu\";\nimport { cn } from \"../../lib/utils\";\nimport { JsonView } from \"../JsonView\";\nimport { TabButton } from \"../TabButton\";\nimport {\n UiCheck,\n UiClass,\n UiClock,\n UiCopy,\n UiJson,\n UiRefresh,\n UiStop,\n UiWarningCircle,\n} from \"../../icons\";\nimport type { Test } from \"./types\";\nimport { useTestRunner } from \"./context\";\nimport { buildNodeBrief, buildNodeJson, type CopyItem } from \"./copy\";\nimport { formatTestDuration, outcomeFor, sum } from \"./status\";\nimport { OutcomeIcon, ToolIcon } from \"./MarkIcons\";\nimport { TestAttempts, TestFailureDetail, TestOutput } from \"./TestFailureDetail\";\n\n/** Built-in detail body — used when no adapter contributes `renderDetail`. */\nfunction DefaultDetailBody({ node }: { node: Test }) {\n const hasChildren = (node.children?.length ?? 0) > 0;\n const counts = hasChildren ? sum(node) : null;\n return (\n <div className=\"space-y-4 p-density-4\">\n {node.message && !node.failure_detail && (\n <p className=\"whitespace-pre-wrap break-words text-sm\">{node.message}</p>\n )}\n {node.failure_detail && <TestFailureDetail failure={node.failure_detail} />}\n <TestOutput node={node} />\n {node.attempts && node.attempts.length > 0 && <TestAttempts attempts={node.attempts} />}\n {counts && counts.total > 0 && (\n <div className=\"flex flex-wrap gap-3 text-xs text-muted-foreground\">\n <span>{counts.total} total</span>\n {counts.passed > 0 && <span>{counts.passed} passed</span>}\n {counts.failed > 0 && <span>{counts.failed} failed</span>}\n {counts.skipped > 0 && <span>{counts.skipped} skipped</span>}\n </div>\n )}\n {node.detail != null && (\n <div className=\"space-y-1\">\n <div className=\"text-[10px] uppercase tracking-wide text-muted-foreground\">Detail</div>\n <JsonView data={node.detail} />\n </div>\n )}\n </div>\n );\n}\n\nfunction copyMenuItem(item: CopyItem, copy: (text: string) => void): DropdownMenuItem {\n const icon = item.icon ? { icon: item.icon } : {};\n if (\"reason\" in item) {\n return { label: item.label, ...icon, disabled: true, title: item.reason, onSelect: () => {} };\n }\n return { label: item.label, ...icon, onSelect: () => copy(item.value) };\n}\n\nconst COPY_LABEL = { idle: \"Copy details\", copied: \"Copied\", error: \"Copy failed\" } as const;\nconst COPY_ICON = { idle: UiCopy, copied: UiCheck, error: UiWarningCircle } as const;\n\n/**\n * \"Copy details\" split button: the primary action copies a Markdown brief of\n * the generic sections plus every matching adapter's `copy` sections; the menu\n * offers the node as JSON and the adapters' items, disabled with a reason when\n * they have nothing to copy. The label flips to \"Copied\" or \"Copy failed\".\n */\nfunction CopyDetailsButton({ node }: { node: Test }) {\n const runner = useTestRunner();\n const { state, copy } = useCopyFlash();\n const sections = useMemo(() => runner.adapters.resolveCopy(node, runner), [node, runner]);\n const items: DropdownMenuItem[] = [\n { label: \"Copy as JSON\", icon: UiJson, onSelect: () => copy(buildNodeJson(node)) },\n ...sections.items.map((item) => copyMenuItem(item, copy)),\n ];\n return (\n <SplitButton\n label={COPY_LABEL[state]}\n icon={COPY_ICON[state]}\n variant=\"outline\"\n size=\"sm\"\n align=\"right\"\n title=\"More copy options\"\n onClick={() => copy(buildNodeBrief(node, sections))}\n items={items}\n />\n );\n}\n\nfunction DetailHeader({ node, actions }: { node: Test; actions?: ReactNode }) {\n const runner = useTestRunner();\n const outcome = outcomeFor(node);\n const canRerun = !!runner.onRerun && node.framework !== \"task\";\n const canStop = !!runner.onStop && !!node.can_stop && !!node.task_id;\n const location = node.file ? `${node.file}${node.line ? `:${node.line}` : \"\"}` : undefined;\n return (\n <div\n className=\"flex min-w-0 items-center gap-2 border-b border-border px-density-4 py-density-2\"\n data-detail-header=\"\"\n >\n {outcome ? (\n <OutcomeIcon outcome={outcome} className=\"text-lg\" />\n ) : (\n <Icon icon={UiClass} className=\"shrink-0 text-lg text-muted-foreground\" />\n )}\n <h2 className=\"min-w-0 shrink truncate text-base font-semibold\" title={node.name}>\n {node.name}\n </h2>\n {node.framework && (\n <span className=\"inline-flex shrink-0 items-center gap-1 rounded bg-muted px-1.5 py-0.5 text-xs text-muted-foreground\">\n <ToolIcon tool={node.framework} className=\"text-sm\" />\n {node.framework}\n </span>\n )}\n {node.duration ? (\n <span className=\"inline-flex shrink-0 items-center gap-1 text-xs text-muted-foreground\">\n <Icon icon={UiClock} className=\"text-xs\" />\n {formatTestDuration(node.duration)}\n </span>\n ) : null}\n {location && (\n <span\n className=\"min-w-0 shrink-[100] truncate font-mono text-xs text-muted-foreground\"\n title={location}\n >\n {location}\n </span>\n )}\n <div className=\"ml-auto flex shrink-0 items-center gap-1\">\n {actions}\n <CopyDetailsButton node={node} />\n {canRerun && (\n <Button\n size=\"sm\"\n disabled={runner.busy.rerun}\n onClick={() => runner.onRerun!(node)}\n title=\"Rerun this test or subtree\"\n >\n <Icon icon={UiRefresh} className=\"mr-1 text-sm\" />\n {runner.busy.rerun ? \"Running...\" : \"Rerun\"}\n </Button>\n )}\n {canStop && (\n <Button\n size=\"sm\"\n variant=\"destructive\"\n disabled={runner.busy.stop}\n onClick={() => runner.onStop!(node)}\n title=\"Stop this running task\"\n >\n <Icon icon={UiStop} className=\"mr-1 text-sm\" />\n {runner.busy.stop ? \"Stopping...\" : \"Stop\"}\n </Button>\n )}\n </div>\n </div>\n );\n}\n\n/**\n * The right pane. Resolves the node's adapter for body / tabs / header actions,\n * falling back to the built-in default body. Shows an empty state when nothing\n * is selected.\n */\nexport function TestDetailPanel() {\n const runner = useTestRunner();\n const node = runner.selected;\n // Tab state is controlled when the host supplies activeTab (e.g. to mirror it\n // into the URL); otherwise the panel owns it. The render below tolerates an\n // unknown id by falling back to the built-in \"detail\" tab.\n const controlled = runner.activeTab !== undefined;\n const [internalTab, setInternalTab] = useState<string>(\"detail\");\n const activeTab = controlled ? runner.activeTab! : internalTab;\n const setActiveTab = (id: string) => {\n if (controlled) runner.onTabChange?.(id);\n else setInternalTab(id);\n };\n\n if (!node) {\n return (\n <div className=\"flex h-full items-center justify-center text-sm text-muted-foreground\">\n <div className=\"text-center\">\n <Icon icon={UiClass} className=\"mb-2 block text-4xl\" />\n Select a test to view its details.\n </div>\n </div>\n );\n }\n\n const detailAdapter = runner.adapters.resolveDetail(node, runner);\n const actionAdapter = runner.adapters.resolveActions(node, runner);\n const tabs = runner.adapters.resolveTabs(node, runner);\n const ownsScroll = runner.adapters.ownsScroll(node, runner);\n\n const body = detailAdapter?.renderDetail\n ? detailAdapter.renderDetail({ node, runner })\n : <DefaultDetailBody node={node} />;\n const actions = actionAdapter?.nodeActions?.({ node, runner });\n\n // A size container, so log blocks can cap themselves at a share of the pane (cqh).\n const detailRegion = cn(\"min-h-0 flex-1 [container-type:size]\", !ownsScroll && \"overflow-y-auto\");\n\n // No extra tabs → just the body under the header.\n if (tabs.length === 0) {\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <DetailHeader node={node} actions={actions} />\n <div className={detailRegion}>{body}</div>\n </div>\n );\n }\n\n const detailTab = { id: \"detail\", label: \"Detail\", render: () => body };\n const allTabs = [detailTab, ...tabs];\n const active = allTabs.find((t) => t.id === activeTab) ?? detailTab;\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <DetailHeader node={node} actions={actions} />\n <div className=\"flex shrink-0 items-center gap-1 border-b border-border px-density-2 py-density-1\">\n {allTabs.map((t) => (\n <TabButton\n key={t.id}\n active={active.id === t.id}\n onClick={() => setActiveTab(t.id)}\n label={t.label}\n />\n ))}\n </div>\n <div className={detailRegion}>{active.render()}</div>\n </div>\n );\n}\n"],"names":["sum","jsxs","jsx","TestFailureDetail","TestOutput","TestAttempts","JsonView","copy","UiCopy","UiCheck","UiWarningCircle","useTestRunner","useCopyFlash","useMemo","UiJson","buildNodeJson","SplitButton","buildNodeBrief","outcomeFor","OutcomeIcon","Icon","UiClass","ToolIcon","UiClock","formatTestDuration","Button","UiRefresh","UiStop","useState","_a","cn","TabButton"],"mappings":"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"}
|