inspect-ai 0.3.81__py3-none-any.whl → 0.3.82__py3-none-any.whl
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.
- inspect_ai/_cli/eval.py +35 -2
- inspect_ai/_cli/util.py +44 -1
- inspect_ai/_display/core/config.py +1 -1
- inspect_ai/_display/core/display.py +13 -4
- inspect_ai/_display/core/results.py +1 -1
- inspect_ai/_display/textual/widgets/task_detail.py +5 -4
- inspect_ai/_eval/eval.py +38 -1
- inspect_ai/_eval/evalset.py +5 -0
- inspect_ai/_eval/run.py +5 -2
- inspect_ai/_eval/task/log.py +53 -6
- inspect_ai/_eval/task/run.py +51 -10
- inspect_ai/_util/constants.py +2 -0
- inspect_ai/_util/file.py +17 -1
- inspect_ai/_util/json.py +36 -1
- inspect_ai/_view/server.py +113 -1
- inspect_ai/_view/www/App.css +1 -1
- inspect_ai/_view/www/dist/assets/index.css +518 -296
- inspect_ai/_view/www/dist/assets/index.js +38803 -36307
- inspect_ai/_view/www/eslint.config.mjs +1 -1
- inspect_ai/_view/www/log-schema.json +13 -0
- inspect_ai/_view/www/node_modules/flatted/python/flatted.py +149 -0
- inspect_ai/_view/www/package.json +8 -2
- inspect_ai/_view/www/src/App.tsx +151 -855
- inspect_ai/_view/www/src/api/api-browser.ts +176 -5
- inspect_ai/_view/www/src/api/api-vscode.ts +75 -1
- inspect_ai/_view/www/src/api/client-api.ts +66 -10
- inspect_ai/_view/www/src/api/jsonrpc.ts +2 -0
- inspect_ai/_view/www/src/api/types.ts +107 -2
- inspect_ai/_view/www/src/appearance/icons.ts +1 -0
- inspect_ai/_view/www/src/components/AsciinemaPlayer.tsx +3 -3
- inspect_ai/_view/www/src/components/DownloadPanel.tsx +2 -2
- inspect_ai/_view/www/src/components/ExpandablePanel.tsx +56 -61
- inspect_ai/_view/www/src/components/FindBand.tsx +17 -9
- inspect_ai/_view/www/src/components/HumanBaselineView.tsx +1 -1
- inspect_ai/_view/www/src/components/JsonPanel.tsx +14 -24
- inspect_ai/_view/www/src/components/LargeModal.tsx +2 -35
- inspect_ai/_view/www/src/components/LightboxCarousel.tsx +27 -11
- inspect_ai/_view/www/src/components/LiveVirtualList.module.css +11 -0
- inspect_ai/_view/www/src/components/LiveVirtualList.tsx +177 -0
- inspect_ai/_view/www/src/components/MarkdownDiv.tsx +3 -3
- inspect_ai/_view/www/src/components/MessageBand.tsx +14 -9
- inspect_ai/_view/www/src/components/MorePopOver.tsx +3 -3
- inspect_ai/_view/www/src/components/NavPills.tsx +20 -8
- inspect_ai/_view/www/src/components/NoContentsPanel.module.css +12 -0
- inspect_ai/_view/www/src/components/NoContentsPanel.tsx +20 -0
- inspect_ai/_view/www/src/components/ProgressBar.module.css +5 -4
- inspect_ai/_view/www/src/components/ProgressBar.tsx +3 -2
- inspect_ai/_view/www/src/components/PulsingDots.module.css +81 -0
- inspect_ai/_view/www/src/components/PulsingDots.tsx +45 -0
- inspect_ai/_view/www/src/components/TabSet.tsx +4 -37
- inspect_ai/_view/www/src/components/ToolButton.tsx +3 -4
- inspect_ai/_view/www/src/index.tsx +26 -94
- inspect_ai/_view/www/src/logfile/remoteLogFile.ts +9 -1
- inspect_ai/_view/www/src/logfile/remoteZipFile.ts +30 -4
- inspect_ai/_view/www/src/metadata/RenderedContent.tsx +4 -6
- inspect_ai/_view/www/src/plan/ScorerDetailView.tsx +1 -1
- inspect_ai/_view/www/src/samples/InlineSampleDisplay.module.css +9 -1
- inspect_ai/_view/www/src/samples/InlineSampleDisplay.tsx +67 -28
- inspect_ai/_view/www/src/samples/SampleDialog.tsx +51 -22
- inspect_ai/_view/www/src/samples/SampleDisplay.module.css +4 -0
- inspect_ai/_view/www/src/samples/SampleDisplay.tsx +144 -90
- inspect_ai/_view/www/src/samples/SampleSummaryView.module.css +4 -0
- inspect_ai/_view/www/src/samples/SampleSummaryView.tsx +82 -35
- inspect_ai/_view/www/src/samples/SamplesTools.tsx +23 -30
- inspect_ai/_view/www/src/samples/chat/ChatMessage.tsx +2 -1
- inspect_ai/_view/www/src/samples/chat/ChatMessageRenderer.tsx +1 -1
- inspect_ai/_view/www/src/samples/chat/ChatViewVirtualList.tsx +45 -53
- inspect_ai/_view/www/src/samples/chat/MessageContent.tsx +4 -1
- inspect_ai/_view/www/src/samples/chat/MessageContents.tsx +3 -0
- inspect_ai/_view/www/src/samples/chat/messages.ts +34 -0
- inspect_ai/_view/www/src/samples/chat/tools/ToolCallView.module.css +3 -0
- inspect_ai/_view/www/src/samples/chat/tools/ToolCallView.tsx +10 -1
- inspect_ai/_view/www/src/samples/chat/tools/ToolInput.tsx +22 -46
- inspect_ai/_view/www/src/samples/descriptor/samplesDescriptor.tsx +25 -17
- inspect_ai/_view/www/src/samples/descriptor/score/ObjectScoreDescriptor.tsx +2 -1
- inspect_ai/_view/www/src/samples/descriptor/types.ts +6 -5
- inspect_ai/_view/www/src/samples/list/SampleFooter.module.css +21 -3
- inspect_ai/_view/www/src/samples/list/SampleFooter.tsx +20 -1
- inspect_ai/_view/www/src/samples/list/SampleList.tsx +105 -85
- inspect_ai/_view/www/src/samples/list/SampleRow.module.css +6 -0
- inspect_ai/_view/www/src/samples/list/SampleRow.tsx +27 -14
- inspect_ai/_view/www/src/samples/sample-tools/SelectScorer.tsx +29 -18
- inspect_ai/_view/www/src/samples/sample-tools/SortFilter.tsx +28 -28
- inspect_ai/_view/www/src/samples/sample-tools/sample-filter/SampleFilter.tsx +19 -9
- inspect_ai/_view/www/src/samples/sampleDataAdapter.ts +33 -0
- inspect_ai/_view/www/src/samples/sampleLimit.ts +2 -2
- inspect_ai/_view/www/src/samples/scores/SampleScoreView.tsx +7 -9
- inspect_ai/_view/www/src/samples/scores/SampleScores.tsx +7 -11
- inspect_ai/_view/www/src/samples/transcript/ErrorEventView.tsx +0 -13
- inspect_ai/_view/www/src/samples/transcript/InfoEventView.tsx +0 -13
- inspect_ai/_view/www/src/samples/transcript/InputEventView.tsx +0 -13
- inspect_ai/_view/www/src/samples/transcript/ModelEventView.module.css +4 -0
- inspect_ai/_view/www/src/samples/transcript/ModelEventView.tsx +10 -24
- inspect_ai/_view/www/src/samples/transcript/SampleInitEventView.tsx +0 -13
- inspect_ai/_view/www/src/samples/transcript/SampleLimitEventView.tsx +4 -22
- inspect_ai/_view/www/src/samples/transcript/SandboxEventView.tsx +15 -24
- inspect_ai/_view/www/src/samples/transcript/ScoreEventView.tsx +0 -13
- inspect_ai/_view/www/src/samples/transcript/StepEventView.tsx +6 -28
- inspect_ai/_view/www/src/samples/transcript/SubtaskEventView.tsx +24 -34
- inspect_ai/_view/www/src/samples/transcript/ToolEventView.module.css +4 -0
- inspect_ai/_view/www/src/samples/transcript/ToolEventView.tsx +8 -13
- inspect_ai/_view/www/src/samples/transcript/TranscriptView.tsx +197 -338
- inspect_ai/_view/www/src/samples/transcript/TranscriptVirtualListComponent.module.css +16 -0
- inspect_ai/_view/www/src/samples/transcript/TranscriptVirtualListComponent.tsx +44 -0
- inspect_ai/_view/www/src/samples/transcript/event/EventNav.tsx +7 -4
- inspect_ai/_view/www/src/samples/transcript/event/EventPanel.tsx +52 -58
- inspect_ai/_view/www/src/samples/transcript/event/EventProgressPanel.module.css +23 -0
- inspect_ai/_view/www/src/samples/transcript/event/EventProgressPanel.tsx +27 -0
- inspect_ai/_view/www/src/samples/transcript/state/StateEventRenderers.tsx +30 -1
- inspect_ai/_view/www/src/samples/transcript/state/StateEventView.tsx +102 -72
- inspect_ai/_view/www/src/scoring/utils.ts +87 -0
- inspect_ai/_view/www/src/state/appSlice.ts +244 -0
- inspect_ai/_view/www/src/state/hooks.ts +397 -0
- inspect_ai/_view/www/src/state/logPolling.ts +196 -0
- inspect_ai/_view/www/src/state/logSlice.ts +214 -0
- inspect_ai/_view/www/src/state/logsPolling.ts +118 -0
- inspect_ai/_view/www/src/state/logsSlice.ts +181 -0
- inspect_ai/_view/www/src/state/samplePolling.ts +311 -0
- inspect_ai/_view/www/src/state/sampleSlice.ts +127 -0
- inspect_ai/_view/www/src/state/sampleUtils.ts +21 -0
- inspect_ai/_view/www/src/state/scrolling.ts +206 -0
- inspect_ai/_view/www/src/state/store.ts +168 -0
- inspect_ai/_view/www/src/state/store_filter.ts +84 -0
- inspect_ai/_view/www/src/state/utils.ts +23 -0
- inspect_ai/_view/www/src/storage/index.ts +26 -0
- inspect_ai/_view/www/src/types/log.d.ts +2 -0
- inspect_ai/_view/www/src/types.ts +94 -32
- inspect_ai/_view/www/src/utils/attachments.ts +58 -23
- inspect_ai/_view/www/src/utils/logger.ts +52 -0
- inspect_ai/_view/www/src/utils/polling.ts +100 -0
- inspect_ai/_view/www/src/utils/react.ts +30 -0
- inspect_ai/_view/www/src/utils/vscode.ts +1 -1
- inspect_ai/_view/www/src/workspace/WorkSpace.tsx +181 -216
- inspect_ai/_view/www/src/workspace/WorkSpaceView.tsx +11 -53
- inspect_ai/_view/www/src/workspace/navbar/Navbar.tsx +8 -18
- inspect_ai/_view/www/src/workspace/navbar/PrimaryBar.module.css +1 -0
- inspect_ai/_view/www/src/workspace/navbar/PrimaryBar.tsx +40 -22
- inspect_ai/_view/www/src/workspace/navbar/ResultsPanel.module.css +0 -1
- inspect_ai/_view/www/src/workspace/navbar/ResultsPanel.tsx +98 -39
- inspect_ai/_view/www/src/workspace/navbar/RunningStatusPanel.module.css +32 -0
- inspect_ai/_view/www/src/workspace/navbar/RunningStatusPanel.tsx +32 -0
- inspect_ai/_view/www/src/workspace/navbar/SecondaryBar.tsx +11 -13
- inspect_ai/_view/www/src/workspace/navbar/StatusPanel.tsx +6 -2
- inspect_ai/_view/www/src/workspace/sidebar/LogDirectoryTitleView.tsx +4 -4
- inspect_ai/_view/www/src/workspace/sidebar/Sidebar.tsx +28 -13
- inspect_ai/_view/www/src/workspace/tabs/InfoTab.tsx +5 -10
- inspect_ai/_view/www/src/workspace/tabs/JsonTab.tsx +4 -4
- inspect_ai/_view/www/src/workspace/tabs/RunningNoSamples.module.css +22 -0
- inspect_ai/_view/www/src/workspace/tabs/RunningNoSamples.tsx +19 -0
- inspect_ai/_view/www/src/workspace/tabs/SamplesTab.tsx +110 -115
- inspect_ai/_view/www/src/workspace/tabs/grouping.ts +37 -5
- inspect_ai/_view/www/src/workspace/tabs/types.ts +4 -0
- inspect_ai/_view/www/src/workspace/types.ts +4 -3
- inspect_ai/_view/www/src/workspace/utils.ts +4 -4
- inspect_ai/_view/www/vite.config.js +6 -0
- inspect_ai/_view/www/yarn.lock +370 -354
- inspect_ai/log/_condense.py +26 -0
- inspect_ai/log/_log.py +6 -3
- inspect_ai/log/_recorders/buffer/__init__.py +14 -0
- inspect_ai/log/_recorders/buffer/buffer.py +30 -0
- inspect_ai/log/_recorders/buffer/database.py +685 -0
- inspect_ai/log/_recorders/buffer/filestore.py +259 -0
- inspect_ai/log/_recorders/buffer/types.py +84 -0
- inspect_ai/log/_recorders/eval.py +2 -11
- inspect_ai/log/_recorders/types.py +30 -0
- inspect_ai/log/_transcript.py +27 -1
- inspect_ai/model/_call_tools.py +1 -0
- inspect_ai/model/_generate_config.py +2 -2
- inspect_ai/model/_model.py +1 -0
- inspect_ai/tool/_tool_support_helpers.py +4 -4
- inspect_ai/tool/_tools/_web_browser/_web_browser.py +3 -1
- inspect_ai/util/_subtask.py +1 -0
- {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.82.dist-info}/METADATA +1 -1
- {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.82.dist-info}/RECORD +178 -138
- inspect_ai/_view/www/src/samples/transcript/SampleTranscript.tsx +0 -22
- {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.82.dist-info}/WHEEL +0 -0
- {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.82.dist-info}/entry_points.txt +0 -0
- {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.82.dist-info}/licenses/LICENSE +0 -0
- {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.82.dist-info}/top_level.txt +0 -0
@@ -1,24 +1,31 @@
|
|
1
1
|
import { clsx } from "clsx";
|
2
2
|
|
3
|
-
import { FC } from "react";
|
3
|
+
import { FC, useCallback } from "react";
|
4
4
|
import { ApplicationIcons } from "../appearance/icons";
|
5
|
+
import { useMessageVisibility } from "../state/hooks";
|
5
6
|
import "./MessageBand.css";
|
6
7
|
|
7
8
|
interface MessageBandProps {
|
9
|
+
id: string;
|
8
10
|
message: string;
|
9
|
-
|
10
|
-
setHidden: (hidden: boolean) => void;
|
11
|
+
scope?: "sample" | "eval";
|
11
12
|
type: "info" | "warning" | "error";
|
12
13
|
}
|
13
14
|
|
14
15
|
export const MessageBand: FC<MessageBandProps> = ({
|
16
|
+
id,
|
15
17
|
message,
|
16
|
-
hidden,
|
17
|
-
setHidden,
|
18
18
|
type,
|
19
|
+
scope = "eval",
|
19
20
|
}) => {
|
20
21
|
const className: string[] = [type];
|
21
|
-
|
22
|
+
|
23
|
+
const [visible, setVisible] = useMessageVisibility(id, scope);
|
24
|
+
const handleClick = useCallback(() => {
|
25
|
+
setVisible(false);
|
26
|
+
}, [setVisible]);
|
27
|
+
|
28
|
+
if (!visible) {
|
22
29
|
className.push("hidden");
|
23
30
|
}
|
24
31
|
|
@@ -29,9 +36,7 @@ export const MessageBand: FC<MessageBandProps> = ({
|
|
29
36
|
<button
|
30
37
|
className={clsx("btn", "message-band-btn", type)}
|
31
38
|
title="Close"
|
32
|
-
onClick={
|
33
|
-
setHidden(true);
|
34
|
-
}}
|
39
|
+
onClick={handleClick}
|
35
40
|
>
|
36
41
|
<i className={ApplicationIcons.close}></i>
|
37
42
|
</button>
|
@@ -1,14 +1,14 @@
|
|
1
1
|
import { Popover } from "bootstrap";
|
2
|
-
import
|
2
|
+
import { FC, ReactNode, useEffect, useRef } from "react";
|
3
3
|
import "./MorePopover.css";
|
4
4
|
|
5
5
|
interface MorePopoverProps {
|
6
6
|
title: string;
|
7
7
|
customClass?: string;
|
8
|
-
children:
|
8
|
+
children: ReactNode;
|
9
9
|
}
|
10
10
|
|
11
|
-
export const MorePopover:
|
11
|
+
export const MorePopover: FC<MorePopoverProps> = ({
|
12
12
|
title,
|
13
13
|
customClass,
|
14
14
|
children,
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import { clsx } from "clsx";
|
2
|
-
import { FC, ReactElement, ReactNode,
|
2
|
+
import { FC, MouseEvent, ReactElement, ReactNode, useCallback } from "react";
|
3
|
+
import { useProperty } from "../state/hooks";
|
3
4
|
import styles from "./NavPills.module.css";
|
4
5
|
|
5
6
|
interface NavPillChildProps {
|
@@ -8,13 +9,16 @@ interface NavPillChildProps {
|
|
8
9
|
}
|
9
10
|
|
10
11
|
interface NavPillsProps {
|
12
|
+
id: string;
|
11
13
|
children?: ReactElement<NavPillChildProps>[];
|
12
14
|
}
|
13
15
|
|
14
|
-
export const NavPills: FC<NavPillsProps> = ({ children }) => {
|
15
|
-
const [
|
16
|
-
|
17
|
-
|
16
|
+
export const NavPills: FC<NavPillsProps> = ({ id, children }) => {
|
17
|
+
const defaultNav = children ? children[0].props["title"] : "";
|
18
|
+
const [activeItem, setActiveItem] = useProperty(id, "active", {
|
19
|
+
defaultValue: defaultNav,
|
20
|
+
});
|
21
|
+
|
18
22
|
if (!activeItem || !children) {
|
19
23
|
return undefined;
|
20
24
|
}
|
@@ -77,6 +81,15 @@ const NavPill: FC<NavPillProps> = ({
|
|
77
81
|
children,
|
78
82
|
}) => {
|
79
83
|
const active = activeItem === title;
|
84
|
+
const handleClick = useCallback(
|
85
|
+
(e: MouseEvent<HTMLButtonElement>) => {
|
86
|
+
const target = (e.currentTarget as HTMLButtonElement).dataset.target;
|
87
|
+
if (target) {
|
88
|
+
setActiveItem(target);
|
89
|
+
}
|
90
|
+
},
|
91
|
+
[setActiveItem],
|
92
|
+
);
|
80
93
|
|
81
94
|
return (
|
82
95
|
<li className={"nav-item"}>
|
@@ -90,9 +103,8 @@ const NavPill: FC<NavPillProps> = ({
|
|
90
103
|
active ? "active " : "",
|
91
104
|
styles.pill,
|
92
105
|
)}
|
93
|
-
|
94
|
-
|
95
|
-
}}
|
106
|
+
data-target={title}
|
107
|
+
onClick={handleClick}
|
96
108
|
>
|
97
109
|
{title}
|
98
110
|
</button>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import clsx from "clsx";
|
2
|
+
import { FC } from "react";
|
3
|
+
|
4
|
+
import { ApplicationIcons } from "../appearance/icons";
|
5
|
+
import styles from "./NoContentsPanel.module.css";
|
6
|
+
|
7
|
+
interface NoContentsPanelProps {
|
8
|
+
text: string;
|
9
|
+
}
|
10
|
+
|
11
|
+
export const NoContentsPanel: FC<NoContentsPanelProps> = ({ text }) => {
|
12
|
+
return (
|
13
|
+
<div className={clsx(styles.panel)}>
|
14
|
+
<div className={clsx(styles.container, "text-size-smaller")}>
|
15
|
+
<i className={ApplicationIcons.noSamples} />
|
16
|
+
<div>{text}</div>
|
17
|
+
</div>
|
18
|
+
</div>
|
19
|
+
);
|
20
|
+
};
|
@@ -6,25 +6,26 @@
|
|
6
6
|
height: 0;
|
7
7
|
overflow-y: visible;
|
8
8
|
overflow-x: visible;
|
9
|
-
z-index: 1001;
|
10
9
|
text-align: center;
|
11
10
|
}
|
12
11
|
|
13
12
|
.container {
|
14
13
|
width: 100%;
|
15
|
-
height:
|
14
|
+
height: 1px;
|
16
15
|
background-color: transparent;
|
17
|
-
position:
|
16
|
+
position: sticky;
|
18
17
|
overflow: hidden;
|
18
|
+
z-index: 1200;
|
19
19
|
}
|
20
20
|
|
21
21
|
.animate {
|
22
22
|
width: 5%;
|
23
|
-
height:
|
23
|
+
height: 1px;
|
24
24
|
animation: leftToRight 2s linear infinite;
|
25
25
|
background-color: #3b82f6;
|
26
26
|
position: absolute;
|
27
27
|
left: 0;
|
28
|
+
top: 0;
|
28
29
|
}
|
29
30
|
|
30
31
|
@keyframes leftToRight {
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import clsx from "clsx";
|
1
2
|
import { FC } from "react";
|
2
3
|
import styles from "./ProgressBar.module.css";
|
3
4
|
|
@@ -7,9 +8,9 @@ interface ProgressBarProps {
|
|
7
8
|
|
8
9
|
export const ProgressBar: FC<ProgressBarProps> = ({ animating }) => {
|
9
10
|
return (
|
10
|
-
<div className={styles.wrapper}>
|
11
|
+
<div className={clsx(styles.wrapper)}>
|
11
12
|
<div
|
12
|
-
className={styles.container}
|
13
|
+
className={clsx(styles.container)}
|
13
14
|
role="progressbar"
|
14
15
|
aria-label="Basic example"
|
15
16
|
aria-valuenow={25}
|
@@ -0,0 +1,81 @@
|
|
1
|
+
/* PulsingDots.module.css */
|
2
|
+
.container {
|
3
|
+
display: inline-flex;
|
4
|
+
flex-direction: column;
|
5
|
+
align-items: center;
|
6
|
+
}
|
7
|
+
|
8
|
+
.dotsContainer {
|
9
|
+
padding-top: 8px;
|
10
|
+
display: flex;
|
11
|
+
align-items: center;
|
12
|
+
justify-content: center;
|
13
|
+
}
|
14
|
+
|
15
|
+
.small .dotsContainer {
|
16
|
+
column-gap: 3px;
|
17
|
+
}
|
18
|
+
|
19
|
+
.medium .dotsContainer {
|
20
|
+
column-gap: 5px;
|
21
|
+
padding-bottom: 2px;
|
22
|
+
}
|
23
|
+
|
24
|
+
.large .dotsContainer {
|
25
|
+
column-gap: 6px;
|
26
|
+
padding-bottom: 3px;
|
27
|
+
padding-left: 2px;
|
28
|
+
}
|
29
|
+
|
30
|
+
.dot {
|
31
|
+
border-radius: 50%;
|
32
|
+
display: inline-block;
|
33
|
+
animation: pulse 1.5s ease-in-out infinite;
|
34
|
+
}
|
35
|
+
|
36
|
+
.subtle {
|
37
|
+
background-color: var(--bs-primary-bg-subtle);
|
38
|
+
}
|
39
|
+
|
40
|
+
.primary {
|
41
|
+
background-color: var(--bs-primary);
|
42
|
+
}
|
43
|
+
|
44
|
+
.small .dot {
|
45
|
+
width: 4px;
|
46
|
+
height: 4px;
|
47
|
+
}
|
48
|
+
|
49
|
+
.medium .dot {
|
50
|
+
width: 8px;
|
51
|
+
height: 8px;
|
52
|
+
}
|
53
|
+
|
54
|
+
.large .dot {
|
55
|
+
width: 12px;
|
56
|
+
height: 12px;
|
57
|
+
}
|
58
|
+
|
59
|
+
.visuallyHidden {
|
60
|
+
position: absolute;
|
61
|
+
width: 1px;
|
62
|
+
height: 1px;
|
63
|
+
padding: 0;
|
64
|
+
margin: -1px;
|
65
|
+
overflow: hidden;
|
66
|
+
clip: rect(0, 0, 0, 0);
|
67
|
+
white-space: nowrap;
|
68
|
+
border-width: 0;
|
69
|
+
}
|
70
|
+
|
71
|
+
@keyframes pulse {
|
72
|
+
0%,
|
73
|
+
100% {
|
74
|
+
transform: scale(1);
|
75
|
+
opacity: 0.3;
|
76
|
+
}
|
77
|
+
50% {
|
78
|
+
transform: scale(1.2);
|
79
|
+
opacity: 1;
|
80
|
+
}
|
81
|
+
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import clsx from "clsx";
|
2
|
+
import { FC } from "react";
|
3
|
+
import styles from "./PulsingDots.module.css";
|
4
|
+
|
5
|
+
interface PulsingDotsProps {
|
6
|
+
text?: string;
|
7
|
+
dotsCount?: number;
|
8
|
+
subtle?: boolean;
|
9
|
+
size?: "small" | "medium" | "large";
|
10
|
+
}
|
11
|
+
|
12
|
+
export const PulsingDots: FC<PulsingDotsProps> = ({
|
13
|
+
text = "Loading...",
|
14
|
+
dotsCount = 3,
|
15
|
+
subtle = true,
|
16
|
+
size = "small",
|
17
|
+
}) => {
|
18
|
+
return (
|
19
|
+
<div
|
20
|
+
className={clsx(
|
21
|
+
styles.container,
|
22
|
+
size === "small"
|
23
|
+
? styles.small
|
24
|
+
: size === "medium"
|
25
|
+
? styles.medium
|
26
|
+
: styles.large,
|
27
|
+
)}
|
28
|
+
role="status"
|
29
|
+
>
|
30
|
+
<div className={styles.dotsContainer}>
|
31
|
+
{[...Array(dotsCount)].map((_, index) => (
|
32
|
+
<div
|
33
|
+
key={index}
|
34
|
+
className={clsx(
|
35
|
+
styles.dot,
|
36
|
+
subtle ? styles.subtle : styles.primary,
|
37
|
+
)}
|
38
|
+
style={{ animationDelay: `${index * 0.15}s` }}
|
39
|
+
/>
|
40
|
+
))}
|
41
|
+
</div>
|
42
|
+
<span className={styles.visuallyHidden}>{text}</span>
|
43
|
+
</div>
|
44
|
+
);
|
45
|
+
};
|
@@ -9,11 +9,9 @@ import {
|
|
9
9
|
ReactElement,
|
10
10
|
ReactNode,
|
11
11
|
RefObject,
|
12
|
-
UIEvent,
|
13
|
-
useCallback,
|
14
|
-
useEffect,
|
15
12
|
useRef,
|
16
13
|
} from "react";
|
14
|
+
import { useStatefulScrollPosition } from "../state/scrolling";
|
17
15
|
import moduleStyles from "./TabSet.module.css";
|
18
16
|
|
19
17
|
interface TabSetProps {
|
@@ -36,8 +34,6 @@ interface TabPanelProps {
|
|
36
34
|
scrollable?: boolean;
|
37
35
|
scrollRef?: RefObject<HTMLDivElement | null>;
|
38
36
|
className?: string | string[];
|
39
|
-
scrollPosition?: number;
|
40
|
-
setScrollPosition?: (position: number) => void;
|
41
37
|
children?: ReactNode;
|
42
38
|
title: string;
|
43
39
|
icon?: string;
|
@@ -107,7 +103,7 @@ const Tab: FC<{
|
|
107
103
|
role="tab"
|
108
104
|
aria-controls={tabContentsId}
|
109
105
|
aria-selected={isActive}
|
110
|
-
onClick={
|
106
|
+
onClick={tab.props.onSelected}
|
111
107
|
>
|
112
108
|
{tab.props.icon && (
|
113
109
|
<i className={clsx(tab.props.icon, moduleStyles.tabIcon)} />
|
@@ -139,42 +135,14 @@ export const TabPanel: FC<TabPanelProps> = ({
|
|
139
135
|
scrollable = true,
|
140
136
|
scrollRef,
|
141
137
|
className,
|
142
|
-
scrollPosition,
|
143
|
-
setScrollPosition,
|
144
138
|
children,
|
145
139
|
}) => {
|
146
140
|
const tabContentsId = computeTabContentsId(id);
|
147
141
|
const panelRef = useRef<HTMLDivElement>(null);
|
148
142
|
const tabContentsRef = scrollRef || panelRef;
|
149
143
|
|
150
|
-
|
151
|
-
|
152
|
-
return;
|
153
|
-
|
154
|
-
const observer = new MutationObserver(() => {
|
155
|
-
if (tabContentsRef.current) {
|
156
|
-
tabContentsRef.current.scrollTop = scrollPosition;
|
157
|
-
}
|
158
|
-
observer.disconnect(); // Stop observing after first content load
|
159
|
-
});
|
160
|
-
|
161
|
-
observer.observe(tabContentsRef.current, {
|
162
|
-
childList: true,
|
163
|
-
subtree: true,
|
164
|
-
});
|
165
|
-
|
166
|
-
return () => observer.disconnect();
|
167
|
-
}, []);
|
168
|
-
|
169
|
-
// Handle scrolling
|
170
|
-
const onScroll = useCallback(
|
171
|
-
(e: UIEvent<HTMLDivElement>) => {
|
172
|
-
if (setScrollPosition) {
|
173
|
-
setScrollPosition(e.currentTarget.scrollTop);
|
174
|
-
}
|
175
|
-
},
|
176
|
-
[setScrollPosition],
|
177
|
-
);
|
144
|
+
// Attach a scroll listener to this ref to track scrolling
|
145
|
+
useStatefulScrollPosition(tabContentsRef, tabContentsId, 1000, scrollable);
|
178
146
|
|
179
147
|
return (
|
180
148
|
<div
|
@@ -188,7 +156,6 @@ export const TabPanel: FC<TabPanelProps> = ({
|
|
188
156
|
scrollable && moduleStyles.scrollable,
|
189
157
|
)}
|
190
158
|
style={style}
|
191
|
-
onScroll={onScroll}
|
192
159
|
>
|
193
160
|
{children}
|
194
161
|
</div>
|
@@ -1,14 +1,13 @@
|
|
1
|
-
import
|
1
|
+
import { ButtonHTMLAttributes, forwardRef, ReactNode } from "react";
|
2
2
|
import "./ToolButton.css";
|
3
3
|
|
4
|
-
interface ToolButtonProps
|
5
|
-
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
4
|
+
interface ToolButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
6
5
|
label: string | ReactNode;
|
7
6
|
classes?: string;
|
8
7
|
icon?: string;
|
9
8
|
}
|
10
9
|
|
11
|
-
export const ToolButton =
|
10
|
+
export const ToolButton = forwardRef<HTMLButtonElement, ToolButtonProps>(
|
12
11
|
({ label, classes = "", icon, className, ...rest }, ref) => {
|
13
12
|
// Combine class names, ensuring default classes are applied first
|
14
13
|
const combinedClasses =
|
@@ -1,21 +1,29 @@
|
|
1
1
|
import { createRoot } from "react-dom/client";
|
2
|
-
import { App } from "./App";
|
3
2
|
import api from "./api/index";
|
4
|
-
import {
|
5
|
-
import {
|
3
|
+
import { Capabilities } from "./api/types";
|
4
|
+
import { App } from "./App";
|
5
|
+
import { AppErrorBoundary } from "./AppErrorBoundary";
|
6
|
+
import { initializeStore } from "./state/store";
|
7
|
+
import storage from "./storage";
|
6
8
|
import { getVscodeApi } from "./utils/vscode";
|
7
9
|
|
8
|
-
//
|
10
|
+
// Resolve the api
|
11
|
+
const applicationApi = api;
|
12
|
+
const applicationStorage = storage;
|
13
|
+
|
14
|
+
// Application capabilities
|
9
15
|
const vscode = getVscodeApi();
|
10
|
-
let initialState = undefined;
|
11
16
|
let capabilities: Capabilities = {
|
12
17
|
downloadFiles: true,
|
13
18
|
webWorkers: true,
|
19
|
+
streamSamples: !!applicationApi.get_log_pending_samples,
|
20
|
+
streamSampleData: !!applicationApi.get_log_sample_data,
|
21
|
+
nativeFind: !vscode,
|
14
22
|
};
|
15
|
-
if (vscode) {
|
16
|
-
initialState = filterState(vscode.getState() as ApplicationState);
|
17
23
|
|
18
|
-
|
24
|
+
// Initial state / storage
|
25
|
+
if (vscode) {
|
26
|
+
// Adjust capabilities
|
19
27
|
const extensionVersionEl = document.querySelector(
|
20
28
|
'meta[name="inspect-extension:version"]',
|
21
29
|
);
|
@@ -24,10 +32,15 @@ if (vscode) {
|
|
24
32
|
: undefined;
|
25
33
|
|
26
34
|
if (!extensionVersion) {
|
27
|
-
capabilities =
|
35
|
+
capabilities.downloadFiles = false;
|
36
|
+
capabilities.webWorkers = false;
|
28
37
|
}
|
29
38
|
}
|
30
39
|
|
40
|
+
// Inititialize the application store
|
41
|
+
initializeStore(applicationApi, capabilities, applicationStorage);
|
42
|
+
|
43
|
+
// Find the root element and render into it
|
31
44
|
const containerId = "app";
|
32
45
|
const container = document.getElementById(containerId);
|
33
46
|
if (!container) {
|
@@ -37,91 +50,10 @@ if (!container) {
|
|
37
50
|
);
|
38
51
|
}
|
39
52
|
|
53
|
+
// Render into the root
|
40
54
|
const root = createRoot(container as HTMLElement);
|
41
55
|
root.render(
|
42
|
-
<
|
43
|
-
api={
|
44
|
-
|
45
|
-
saveApplicationState={throttle((state) => {
|
46
|
-
const vscode = getVscodeApi();
|
47
|
-
if (vscode) {
|
48
|
-
vscode.setState(filterState(state));
|
49
|
-
}
|
50
|
-
}, 1000)}
|
51
|
-
capabilities={capabilities}
|
52
|
-
pollForLogs={false}
|
53
|
-
/>,
|
56
|
+
<AppErrorBoundary>
|
57
|
+
<App api={applicationApi} />
|
58
|
+
</AppErrorBoundary>,
|
54
59
|
);
|
55
|
-
|
56
|
-
function filterState(state: ApplicationState) {
|
57
|
-
if (!state) {
|
58
|
-
return state;
|
59
|
-
}
|
60
|
-
|
61
|
-
// When saving state, we can't store vast amounts of data (like a large sample)
|
62
|
-
const filters = [filterLargeSample, filterLargeSelectedLog];
|
63
|
-
return filters.reduce(
|
64
|
-
(filteredState, filter) => filter(filteredState),
|
65
|
-
state,
|
66
|
-
);
|
67
|
-
}
|
68
|
-
|
69
|
-
// Filters the selected Sample if it is large
|
70
|
-
function filterLargeSample(state: ApplicationState) {
|
71
|
-
if (!state || !state.selectedSample) {
|
72
|
-
return state;
|
73
|
-
}
|
74
|
-
|
75
|
-
const estimatedTotalSize = estimateSize(state.selectedSample.messages);
|
76
|
-
if (estimatedTotalSize > 400000) {
|
77
|
-
const { selectedSample, ...filteredState } = state;
|
78
|
-
return filteredState;
|
79
|
-
} else {
|
80
|
-
return state;
|
81
|
-
}
|
82
|
-
}
|
83
|
-
|
84
|
-
// Filters the selectedlog if it is too large
|
85
|
-
function filterLargeSelectedLog(state: ApplicationState) {
|
86
|
-
if (!state || !state.selectedLog?.contents) {
|
87
|
-
return state;
|
88
|
-
}
|
89
|
-
|
90
|
-
const estimatedSize = estimateSize(
|
91
|
-
state.selectedLog.contents.sampleSummaries,
|
92
|
-
);
|
93
|
-
if (estimatedSize > 400000) {
|
94
|
-
const { selectedLog, ...filteredState } = state;
|
95
|
-
return filteredState;
|
96
|
-
} else {
|
97
|
-
return state;
|
98
|
-
}
|
99
|
-
}
|
100
|
-
|
101
|
-
function estimateSize(list: unknown[], frequency = 0.2) {
|
102
|
-
if (!list || list.length === 0) {
|
103
|
-
return 0;
|
104
|
-
}
|
105
|
-
|
106
|
-
// Total number of samples
|
107
|
-
const sampleSize = Math.ceil(list.length * frequency);
|
108
|
-
|
109
|
-
// Get a proper random sample without duplicates
|
110
|
-
const messageIndices = new Set<number>();
|
111
|
-
while (
|
112
|
-
messageIndices.size < sampleSize &&
|
113
|
-
messageIndices.size < list.length
|
114
|
-
) {
|
115
|
-
const randomIndex = Math.floor(Math.random() * list.length);
|
116
|
-
messageIndices.add(randomIndex);
|
117
|
-
}
|
118
|
-
|
119
|
-
// Calculate size from sampled messages
|
120
|
-
const totalSize = Array.from(messageIndices).reduce((size, index) => {
|
121
|
-
return size + JSON.stringify(list[index]).length;
|
122
|
-
}, 0);
|
123
|
-
|
124
|
-
// Estimate total size based on sample
|
125
|
-
const estimatedTotalSize = (totalSize / sampleSize) * list.length;
|
126
|
-
return estimatedTotalSize;
|
127
|
-
}
|
@@ -17,6 +17,14 @@ interface SampleEntry {
|
|
17
17
|
epoch: number;
|
18
18
|
}
|
19
19
|
|
20
|
+
export class SampleNotFoundError extends Error {
|
21
|
+
constructor(message?: string) {
|
22
|
+
super(message || "Sample not found");
|
23
|
+
this.name = "SampleNotFoundError";
|
24
|
+
|
25
|
+
Object.setPrototypeOf(this, SampleNotFoundError.prototype);
|
26
|
+
}
|
27
|
+
}
|
20
28
|
export interface RemoteLogFile {
|
21
29
|
readHeader: () => Promise<EvalHeader>;
|
22
30
|
readLogSummary: () => Promise<EvalSummary>;
|
@@ -101,7 +109,7 @@ export const openRemoteLogFile = async (
|
|
101
109
|
if (remoteZipFile.centralDirectory.has(sampleFile)) {
|
102
110
|
return (await readJSONFile(sampleFile, MAX_BYTES)) as EvalSample;
|
103
111
|
} else {
|
104
|
-
throw new
|
112
|
+
throw new SampleNotFoundError(
|
105
113
|
`Unable to read sample file ${sampleFile} - it is not present in the manifest.`,
|
106
114
|
);
|
107
115
|
}
|
@@ -67,7 +67,15 @@ export const openRemoteZipFile = async (
|
|
67
67
|
|
68
68
|
// Check signature to make sure we found the EOCD record
|
69
69
|
if (eocdrView.getUint32(0, true) !== 0x06054b50) {
|
70
|
-
|
70
|
+
if (eocdrBuffer.length !== 22) {
|
71
|
+
// The range request seems like it was ignored because more bytes than
|
72
|
+
// were requested were returned.
|
73
|
+
throw new Error(
|
74
|
+
"Unexpected central directory size - does the HTTP server serving this file support HTTP range requests?",
|
75
|
+
);
|
76
|
+
} else {
|
77
|
+
throw new Error("End of central directory record not found");
|
78
|
+
}
|
71
79
|
}
|
72
80
|
|
73
81
|
let centralDirOffset = eocdrView.getUint32(16, true);
|
@@ -180,9 +188,27 @@ export const openRemoteZipFile = async (
|
|
180
188
|
};
|
181
189
|
|
182
190
|
export const fetchSize = async (url: string): Promise<number> => {
|
183
|
-
|
184
|
-
const
|
185
|
-
|
191
|
+
// First try HEAD request to get Content-Length
|
192
|
+
const headResponse = await fetch(`${url}`, { method: "HEAD" });
|
193
|
+
const contentLength = headResponse.headers.get("Content-Length");
|
194
|
+
|
195
|
+
if (contentLength !== null) {
|
196
|
+
return Number(contentLength);
|
197
|
+
}
|
198
|
+
|
199
|
+
// If Content-Length is not present, use a GET with an 1 byte range request:
|
200
|
+
const getResponse = await fetch(`${url}`, {
|
201
|
+
method: "GET",
|
202
|
+
headers: { Range: "bytes=0-0" },
|
203
|
+
});
|
204
|
+
const contentRange = getResponse.headers.get("Content-Range");
|
205
|
+
if (contentRange !== null) {
|
206
|
+
const rangeMatch = contentRange.match(/bytes (\d+)-(\d+)\/(\d+)/);
|
207
|
+
if (rangeMatch !== null) {
|
208
|
+
return Number(rangeMatch[3]);
|
209
|
+
}
|
210
|
+
}
|
211
|
+
throw new Error("Could not determine content length");
|
186
212
|
};
|
187
213
|
|
188
214
|
/**
|