inspect-ai 0.3.81__py3-none-any.whl → 0.3.83__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/__init__.py +2 -1
- 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/app.py +14 -3
- inspect_ai/_display/textual/display.py +4 -0
- inspect_ai/_display/textual/widgets/samples.py +9 -3
- inspect_ai/_display/textual/widgets/task_detail.py +8 -8
- inspect_ai/_display/textual/widgets/tasks.py +17 -1
- inspect_ai/_display/textual/widgets/vscode.py +44 -0
- inspect_ai/_eval/eval.py +74 -25
- inspect_ai/_eval/evalset.py +22 -18
- inspect_ai/_eval/loader.py +34 -11
- inspect_ai/_eval/run.py +13 -15
- inspect_ai/_eval/score.py +13 -3
- inspect_ai/_eval/task/generate.py +8 -9
- inspect_ai/_eval/task/log.py +55 -6
- inspect_ai/_eval/task/run.py +51 -10
- inspect_ai/_eval/task/task.py +23 -9
- inspect_ai/_util/constants.py +2 -0
- inspect_ai/_util/file.py +30 -1
- inspect_ai/_util/json.py +37 -1
- inspect_ai/_util/registry.py +1 -0
- inspect_ai/_util/vscode.py +37 -0
- inspect_ai/_view/server.py +113 -1
- inspect_ai/_view/www/App.css +7 -1
- inspect_ai/_view/www/dist/assets/index.css +813 -415
- inspect_ai/_view/www/dist/assets/index.js +54475 -32003
- inspect_ai/_view/www/eslint.config.mjs +1 -1
- inspect_ai/_view/www/log-schema.json +137 -31
- inspect_ai/_view/www/node_modules/flatted/python/flatted.py +149 -0
- inspect_ai/_view/www/package.json +11 -2
- inspect_ai/_view/www/src/App.tsx +161 -853
- 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 +2 -0
- inspect_ai/_view/www/src/components/AsciinemaPlayer.tsx +3 -3
- inspect_ai/_view/www/src/components/Card.tsx +6 -4
- 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/LinkButton.module.css +16 -0
- inspect_ai/_view/www/src/components/LinkButton.tsx +33 -0
- 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 +116 -26
- inspect_ai/_view/www/src/components/MessageBand.tsx +14 -9
- inspect_ai/_view/www/src/components/Modal.module.css +38 -0
- inspect_ai/_view/www/src/components/Modal.tsx +77 -0
- 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/DetailStep.module.css +4 -0
- inspect_ai/_view/www/src/plan/DetailStep.tsx +6 -3
- inspect_ai/_view/www/src/plan/ScorerDetailView.tsx +1 -1
- inspect_ai/_view/www/src/plan/SolverDetailView.module.css +2 -1
- inspect_ai/_view/www/src/samples/InlineSampleDisplay.module.css +9 -1
- inspect_ai/_view/www/src/samples/InlineSampleDisplay.tsx +74 -28
- inspect_ai/_view/www/src/samples/SampleDialog.tsx +58 -22
- inspect_ai/_view/www/src/samples/SampleDisplay.module.css +4 -0
- inspect_ai/_view/www/src/samples/SampleDisplay.tsx +135 -104
- inspect_ai/_view/www/src/samples/SampleSummaryView.module.css +10 -0
- inspect_ai/_view/www/src/samples/SampleSummaryView.tsx +83 -36
- inspect_ai/_view/www/src/samples/SamplesTools.tsx +35 -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 +6 -1
- inspect_ai/_view/www/src/samples/chat/MessageContents.tsx +5 -0
- inspect_ai/_view/www/src/samples/chat/messages.ts +36 -0
- inspect_ai/_view/www/src/samples/chat/tools/ToolCallView.module.css +3 -0
- inspect_ai/_view/www/src/samples/chat/tools/ToolCallView.tsx +11 -1
- inspect_ai/_view/www/src/samples/chat/tools/ToolInput.tsx +22 -46
- inspect_ai/_view/www/src/samples/descriptor/samplesDescriptor.tsx +34 -20
- inspect_ai/_view/www/src/samples/descriptor/score/BooleanScoreDescriptor.module.css +3 -3
- inspect_ai/_view/www/src/samples/descriptor/score/BooleanScoreDescriptor.tsx +1 -1
- inspect_ai/_view/www/src/samples/descriptor/score/ObjectScoreDescriptor.module.css +4 -4
- inspect_ai/_view/www/src/samples/descriptor/score/ObjectScoreDescriptor.tsx +10 -10
- inspect_ai/_view/www/src/samples/descriptor/types.ts +6 -5
- inspect_ai/_view/www/src/samples/list/SampleFooter.module.css +22 -3
- inspect_ai/_view/www/src/samples/list/SampleFooter.tsx +27 -2
- inspect_ai/_view/www/src/samples/list/SampleList.tsx +122 -85
- inspect_ai/_view/www/src/samples/list/SampleRow.module.css +6 -0
- inspect_ai/_view/www/src/samples/list/SampleRow.tsx +28 -15
- 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/SampleScores.tsx +12 -27
- inspect_ai/_view/www/src/samples/scores/SampleScoresGrid.module.css +38 -0
- inspect_ai/_view/www/src/samples/scores/SampleScoresGrid.tsx +118 -0
- inspect_ai/_view/www/src/samples/scores/{SampleScoreView.module.css → SampleScoresView.module.css} +10 -1
- inspect_ai/_view/www/src/samples/scores/SampleScoresView.tsx +78 -0
- 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 +33 -17
- 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 +81 -60
- 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 +29 -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 +399 -0
- inspect_ai/_view/www/src/state/logPolling.ts +200 -0
- inspect_ai/_view/www/src/state/logSlice.ts +224 -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 +314 -0
- inspect_ai/_view/www/src/state/sampleSlice.ts +140 -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 +36 -26
- inspect_ai/_view/www/src/types/markdown-it-katex.d.ts +21 -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/json-worker.ts +79 -12
- 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 +184 -217
- 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 +16 -1
- inspect_ai/_view/www/src/workspace/navbar/ResultsPanel.tsx +159 -103
- 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/ScoreGrid.module.css +35 -0
- inspect_ai/_view/www/src/workspace/navbar/ScoreGrid.tsx +117 -0
- inspect_ai/_view/www/src/workspace/navbar/SecondaryBar.tsx +12 -14
- 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.module.css +3 -2
- 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 +128 -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 +464 -355
- inspect_ai/agent/__init__.py +36 -0
- inspect_ai/agent/_agent.py +268 -0
- inspect_ai/agent/_as_solver.py +72 -0
- inspect_ai/agent/_as_tool.py +122 -0
- inspect_ai/{solver → agent}/_bridge/bridge.py +23 -37
- inspect_ai/{solver → agent}/_bridge/patch.py +9 -8
- inspect_ai/agent/_filter.py +46 -0
- inspect_ai/agent/_handoff.py +93 -0
- inspect_ai/{solver/_human_agent → agent/_human}/agent.py +11 -12
- inspect_ai/{solver/_human_agent → agent/_human}/commands/__init__.py +2 -3
- inspect_ai/{solver/_human_agent → agent/_human}/commands/clock.py +3 -1
- inspect_ai/{solver/_human_agent → agent/_human}/commands/score.py +5 -5
- inspect_ai/{solver/_human_agent → agent/_human}/install.py +6 -3
- inspect_ai/{solver/_human_agent → agent/_human}/service.py +7 -3
- inspect_ai/{solver/_human_agent → agent/_human}/state.py +5 -5
- inspect_ai/agent/_react.py +241 -0
- inspect_ai/agent/_run.py +36 -0
- inspect_ai/agent/_types.py +81 -0
- inspect_ai/log/_condense.py +26 -0
- inspect_ai/log/_log.py +17 -5
- 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 +32 -2
- inspect_ai/model/__init__.py +7 -1
- inspect_ai/model/_call_tools.py +257 -52
- inspect_ai/model/_chat_message.py +7 -4
- inspect_ai/model/_conversation.py +13 -62
- inspect_ai/model/_display.py +85 -0
- inspect_ai/model/_generate_config.py +2 -2
- inspect_ai/model/_model.py +114 -14
- inspect_ai/model/_model_output.py +14 -9
- inspect_ai/model/_openai.py +16 -4
- inspect_ai/model/_openai_computer_use.py +162 -0
- inspect_ai/model/_openai_responses.py +319 -165
- inspect_ai/model/_providers/anthropic.py +20 -21
- inspect_ai/model/_providers/azureai.py +24 -13
- inspect_ai/model/_providers/bedrock.py +1 -7
- inspect_ai/model/_providers/cloudflare.py +3 -3
- inspect_ai/model/_providers/goodfire.py +2 -6
- inspect_ai/model/_providers/google.py +11 -10
- inspect_ai/model/_providers/groq.py +6 -3
- inspect_ai/model/_providers/hf.py +7 -3
- inspect_ai/model/_providers/mistral.py +7 -10
- inspect_ai/model/_providers/openai.py +47 -17
- inspect_ai/model/_providers/openai_o1.py +11 -4
- inspect_ai/model/_providers/openai_responses.py +12 -14
- inspect_ai/model/_providers/providers.py +2 -2
- inspect_ai/model/_providers/together.py +12 -2
- inspect_ai/model/_providers/util/chatapi.py +7 -2
- inspect_ai/model/_providers/util/hf_handler.py +4 -2
- inspect_ai/model/_providers/util/llama31.py +4 -2
- inspect_ai/model/_providers/vertex.py +11 -9
- inspect_ai/model/_providers/vllm.py +4 -4
- inspect_ai/scorer/__init__.py +2 -0
- inspect_ai/scorer/_metrics/__init__.py +2 -0
- inspect_ai/scorer/_metrics/grouped.py +84 -0
- inspect_ai/scorer/_score.py +26 -6
- inspect_ai/solver/__init__.py +2 -2
- inspect_ai/solver/_basic_agent.py +22 -9
- inspect_ai/solver/_bridge.py +31 -0
- inspect_ai/solver/_chain.py +20 -12
- inspect_ai/solver/_fork.py +5 -1
- inspect_ai/solver/_human_agent.py +52 -0
- inspect_ai/solver/_prompt.py +3 -1
- inspect_ai/solver/_run.py +59 -0
- inspect_ai/solver/_solver.py +14 -4
- inspect_ai/solver/_task_state.py +5 -3
- inspect_ai/tool/_tool_call.py +15 -8
- inspect_ai/tool/_tool_def.py +17 -12
- inspect_ai/tool/_tool_support_helpers.py +4 -4
- inspect_ai/tool/_tool_with.py +14 -11
- inspect_ai/tool/_tools/_bash_session.py +11 -2
- inspect_ai/tool/_tools/_computer/_common.py +18 -2
- inspect_ai/tool/_tools/_computer/_computer.py +18 -2
- inspect_ai/tool/_tools/_computer/_resources/tool/_constants.py +2 -0
- inspect_ai/tool/_tools/_computer/_resources/tool/_x11_client.py +17 -0
- inspect_ai/tool/_tools/_think.py +1 -1
- inspect_ai/tool/_tools/_web_browser/_web_browser.py +103 -62
- inspect_ai/util/__init__.py +2 -0
- inspect_ai/util/_anyio.py +27 -0
- inspect_ai/util/_sandbox/__init__.py +2 -1
- inspect_ai/util/_sandbox/context.py +32 -7
- inspect_ai/util/_sandbox/docker/cleanup.py +4 -0
- inspect_ai/util/_sandbox/docker/compose.py +2 -2
- inspect_ai/util/_sandbox/docker/docker.py +12 -1
- inspect_ai/util/_store_model.py +30 -7
- inspect_ai/util/_subprocess.py +13 -3
- inspect_ai/util/_subtask.py +1 -0
- {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.83.dist-info}/METADATA +1 -1
- {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.83.dist-info}/RECORD +295 -229
- inspect_ai/_view/www/src/samples/scores/SampleScoreView.tsx +0 -169
- inspect_ai/_view/www/src/samples/transcript/SampleTranscript.tsx +0 -22
- /inspect_ai/{solver → agent}/_bridge/__init__.py +0 -0
- /inspect_ai/{solver/_human_agent → agent/_human}/__init__.py +0 -0
- /inspect_ai/{solver/_human_agent → agent/_human}/commands/command.py +0 -0
- /inspect_ai/{solver/_human_agent → agent/_human}/commands/instructions.py +0 -0
- /inspect_ai/{solver/_human_agent → agent/_human}/commands/note.py +0 -0
- /inspect_ai/{solver/_human_agent → agent/_human}/commands/status.py +0 -0
- /inspect_ai/{solver/_human_agent → agent/_human}/commands/submit.py +0 -0
- /inspect_ai/{solver/_human_agent → agent/_human}/panel.py +0 -0
- /inspect_ai/{solver/_human_agent → agent/_human}/view.py +0 -0
- {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.83.dist-info}/WHEEL +0 -0
- {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.83.dist-info}/entry_points.txt +0 -0
- {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.83.dist-info}/licenses/LICENSE +0 -0
- {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.83.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,206 @@
|
|
1
|
+
import { RefObject, useCallback, useEffect, useRef } from "react";
|
2
|
+
import { StateCallback, StateSnapshot, VirtuosoHandle } from "react-virtuoso";
|
3
|
+
import { createLogger } from "../utils/logger";
|
4
|
+
import { debounce } from "../utils/sync";
|
5
|
+
import { useStore } from "./store";
|
6
|
+
|
7
|
+
const log = createLogger("scrolling");
|
8
|
+
|
9
|
+
export function useStatefulScrollPosition<
|
10
|
+
T extends HTMLElement = HTMLDivElement,
|
11
|
+
>(
|
12
|
+
elementRef: RefObject<T | null>,
|
13
|
+
elementKey: string,
|
14
|
+
delay = 500,
|
15
|
+
scrollable = true,
|
16
|
+
) {
|
17
|
+
const getScrollPosition = useStore(
|
18
|
+
(state) => state.appActions.getScrollPosition,
|
19
|
+
);
|
20
|
+
const setScrollPosition = useStore(
|
21
|
+
(state) => state.appActions.setScrollPosition,
|
22
|
+
);
|
23
|
+
|
24
|
+
// Create debounced scroll handler
|
25
|
+
const handleScroll = useCallback(
|
26
|
+
debounce((e: Event) => {
|
27
|
+
const target = e.target as HTMLElement;
|
28
|
+
const position = target.scrollTop;
|
29
|
+
log.debug(`Storing scroll position`, elementKey, position);
|
30
|
+
setScrollPosition(elementKey, position);
|
31
|
+
}, delay),
|
32
|
+
[elementKey, setScrollPosition, delay],
|
33
|
+
);
|
34
|
+
|
35
|
+
// Function to manually restore scroll position
|
36
|
+
const restoreScrollPosition = useCallback(() => {
|
37
|
+
const element = elementRef.current;
|
38
|
+
const savedPosition = getScrollPosition(elementKey);
|
39
|
+
|
40
|
+
if (element && savedPosition !== undefined) {
|
41
|
+
requestAnimationFrame(() => {
|
42
|
+
element.scrollTop = savedPosition;
|
43
|
+
|
44
|
+
requestAnimationFrame(() => {
|
45
|
+
if (element.scrollTop !== savedPosition) {
|
46
|
+
element.scrollTop = savedPosition;
|
47
|
+
}
|
48
|
+
});
|
49
|
+
});
|
50
|
+
}
|
51
|
+
}, [elementKey, getScrollPosition, elementRef]);
|
52
|
+
|
53
|
+
// Set up scroll listener and restore position on mount
|
54
|
+
useEffect(() => {
|
55
|
+
const element = elementRef.current;
|
56
|
+
if (!element || !scrollable) {
|
57
|
+
return;
|
58
|
+
}
|
59
|
+
log.debug(`Restore Scroll Hook`, elementKey);
|
60
|
+
|
61
|
+
// Restore scroll position on mount
|
62
|
+
const savedPosition = getScrollPosition(elementKey);
|
63
|
+
if (savedPosition !== undefined) {
|
64
|
+
log.debug(`Restoring scroll position`, savedPosition);
|
65
|
+
// Ensure the element has fully rendered
|
66
|
+
requestAnimationFrame(() => {
|
67
|
+
if (element.scrollTop !== savedPosition) {
|
68
|
+
element.scrollTop = savedPosition;
|
69
|
+
}
|
70
|
+
});
|
71
|
+
}
|
72
|
+
|
73
|
+
// Set up scroll listener
|
74
|
+
if (element.addEventListener) {
|
75
|
+
element.addEventListener("scroll", handleScroll);
|
76
|
+
} else {
|
77
|
+
log.warn("Element has no way to add event listener", element);
|
78
|
+
}
|
79
|
+
|
80
|
+
// Clean up
|
81
|
+
return () => {
|
82
|
+
if (element.removeEventListener) {
|
83
|
+
element.removeEventListener("scroll", handleScroll);
|
84
|
+
} else {
|
85
|
+
log.warn("Element has no way to remove event listener", element);
|
86
|
+
}
|
87
|
+
};
|
88
|
+
}, [elementKey, elementRef, handleScroll]);
|
89
|
+
|
90
|
+
return { restoreScrollPosition };
|
91
|
+
}
|
92
|
+
|
93
|
+
// Define a type for the debounced function that includes the cancel method
|
94
|
+
type DebouncedFunction<T extends (...args: any[]) => any> = T & {
|
95
|
+
cancel: () => void;
|
96
|
+
flush: () => void;
|
97
|
+
};
|
98
|
+
|
99
|
+
export const useVirtuosoState = (
|
100
|
+
virtuosoRef: RefObject<VirtuosoHandle | null>,
|
101
|
+
elementKey: string,
|
102
|
+
delay = 1000,
|
103
|
+
) => {
|
104
|
+
// Use useCallback to stabilize the selectors
|
105
|
+
const restoreState = useStore(
|
106
|
+
useCallback((state) => state.app.listPositions[elementKey], [elementKey]),
|
107
|
+
);
|
108
|
+
|
109
|
+
const setListPosition = useStore(
|
110
|
+
useCallback((state) => state.appActions.setListPosition, []),
|
111
|
+
);
|
112
|
+
|
113
|
+
const clearListPosition = useStore(
|
114
|
+
useCallback((state) => state.appActions.clearListPosition, []),
|
115
|
+
);
|
116
|
+
|
117
|
+
// Properly type the debounced function ref
|
118
|
+
const debouncedFnRef = useRef<DebouncedFunction<
|
119
|
+
(isScrolling: boolean) => void
|
120
|
+
> | null>(null);
|
121
|
+
|
122
|
+
// Create the state change handler
|
123
|
+
const handleStateChange: StateCallback = useCallback(
|
124
|
+
(state: StateSnapshot) => {
|
125
|
+
log.debug(`Storing list state: [${elementKey}]`, state);
|
126
|
+
setListPosition(elementKey, state);
|
127
|
+
},
|
128
|
+
[elementKey, setListPosition],
|
129
|
+
);
|
130
|
+
|
131
|
+
// Setup the debounced function once
|
132
|
+
useEffect(() => {
|
133
|
+
debouncedFnRef.current = debounce((isScrolling: boolean) => {
|
134
|
+
log.debug("List scroll", isScrolling);
|
135
|
+
const element = virtuosoRef.current;
|
136
|
+
if (!element) {
|
137
|
+
return;
|
138
|
+
}
|
139
|
+
element.getState(handleStateChange);
|
140
|
+
}, delay) as DebouncedFunction<(isScrolling: boolean) => void>;
|
141
|
+
|
142
|
+
return () => {
|
143
|
+
// Clear the stored position when component unmounts
|
144
|
+
clearListPosition(elementKey);
|
145
|
+
};
|
146
|
+
}, [delay, elementKey, handleStateChange, clearListPosition, virtuosoRef]);
|
147
|
+
|
148
|
+
// Return a stable function reference that uses the ref internally
|
149
|
+
const isScrolling = useCallback((scrolling: boolean) => {
|
150
|
+
if (!scrolling) {
|
151
|
+
return;
|
152
|
+
}
|
153
|
+
|
154
|
+
if (debouncedFnRef.current) {
|
155
|
+
debouncedFnRef.current(scrolling);
|
156
|
+
}
|
157
|
+
}, []);
|
158
|
+
|
159
|
+
// Use a state to prevent re-rendering just because the restore
|
160
|
+
// state changes
|
161
|
+
const stateRef = useRef(restoreState);
|
162
|
+
useEffect(() => {
|
163
|
+
stateRef.current = restoreState;
|
164
|
+
}, [restoreState]);
|
165
|
+
|
166
|
+
const getRestoreState = useCallback(() => stateRef.current, []);
|
167
|
+
|
168
|
+
return { getRestoreState, isScrolling };
|
169
|
+
};
|
170
|
+
|
171
|
+
export function useRafThrottle<T extends (...args: any[]) => any>(
|
172
|
+
callback: T,
|
173
|
+
dependencies: any[] = [],
|
174
|
+
): (...args: Parameters<T>) => void {
|
175
|
+
const rafRef = useRef<number | null>(null);
|
176
|
+
const callbackRef = useRef<T>(callback);
|
177
|
+
|
178
|
+
// Update the callback ref when the callback changes
|
179
|
+
useEffect(() => {
|
180
|
+
callbackRef.current = callback;
|
181
|
+
}, [callback, ...dependencies]);
|
182
|
+
|
183
|
+
const throttledCallback = useCallback((...args: Parameters<T>) => {
|
184
|
+
// Skip if we already have a frame queued
|
185
|
+
if (rafRef.current) {
|
186
|
+
return;
|
187
|
+
}
|
188
|
+
|
189
|
+
rafRef.current = requestAnimationFrame(() => {
|
190
|
+
callbackRef.current(...args);
|
191
|
+
rafRef.current = null;
|
192
|
+
});
|
193
|
+
}, []);
|
194
|
+
|
195
|
+
// Clean up any pending animation frame on unmount
|
196
|
+
useEffect(() => {
|
197
|
+
return () => {
|
198
|
+
if (rafRef.current) {
|
199
|
+
cancelAnimationFrame(rafRef.current);
|
200
|
+
rafRef.current = null;
|
201
|
+
}
|
202
|
+
};
|
203
|
+
}, []);
|
204
|
+
|
205
|
+
return throttledCallback;
|
206
|
+
}
|
@@ -0,0 +1,168 @@
|
|
1
|
+
import { create, StoreApi, UseBoundStore } from "zustand";
|
2
|
+
import { devtools, persist } from "zustand/middleware";
|
3
|
+
import { immer } from "zustand/middleware/immer";
|
4
|
+
import { Capabilities, ClientAPI, ClientStorage } from "../api/types";
|
5
|
+
import { createLogger } from "../utils/logger";
|
6
|
+
import { AppSlice, createAppSlice, initializeAppSlice } from "./appSlice";
|
7
|
+
import { createLogSlice, initalializeLogSlice, LogSlice } from "./logSlice";
|
8
|
+
import { createLogsSlice, initializeLogsSlice, LogsSlice } from "./logsSlice";
|
9
|
+
import {
|
10
|
+
createSampleSlice,
|
11
|
+
initializeSampleSlice,
|
12
|
+
SampleSlice,
|
13
|
+
} from "./sampleSlice";
|
14
|
+
import { filterState } from "./store_filter";
|
15
|
+
|
16
|
+
const log = createLogger("store");
|
17
|
+
|
18
|
+
export interface StoreState extends AppSlice, LogsSlice, LogSlice, SampleSlice {
|
19
|
+
// The shared api
|
20
|
+
api?: ClientAPI | null;
|
21
|
+
|
22
|
+
// Global actions
|
23
|
+
initialize: (api: ClientAPI, capabilities: Capabilities) => void;
|
24
|
+
cleanup: () => void;
|
25
|
+
}
|
26
|
+
|
27
|
+
// The data that will actually be persisted
|
28
|
+
export type PersistedState = {
|
29
|
+
app: AppSlice["app"];
|
30
|
+
log: LogSlice["log"];
|
31
|
+
logs: LogsSlice["logs"];
|
32
|
+
sample: SampleSlice["sample"];
|
33
|
+
};
|
34
|
+
|
35
|
+
// The store implementation (this will be set when the store is initialized)
|
36
|
+
let storeImplementation: UseBoundStore<StoreApi<StoreState>> | null = null;
|
37
|
+
|
38
|
+
// Create a proxy store that forwards calls to the real store once initialized
|
39
|
+
export const useStore = ((selector?: any) => {
|
40
|
+
if (!storeImplementation) {
|
41
|
+
throw new Error(
|
42
|
+
"Store accessed before initialization. Call initializeStore first.",
|
43
|
+
);
|
44
|
+
}
|
45
|
+
return selector ? storeImplementation(selector) : storeImplementation();
|
46
|
+
}) as UseBoundStore<StoreApi<StoreState>>;
|
47
|
+
|
48
|
+
// Initialize the store
|
49
|
+
export const initializeStore = (
|
50
|
+
api: ClientAPI,
|
51
|
+
capabilities: Capabilities,
|
52
|
+
storage?: ClientStorage,
|
53
|
+
) => {
|
54
|
+
// Create the storage implementation
|
55
|
+
const storageImplementation = {
|
56
|
+
getItem: <T>(name: string): T | null => {
|
57
|
+
return storage ? (storage.getItem(name) as T) : null;
|
58
|
+
},
|
59
|
+
setItem: <T>(name: string, value: T): void => {
|
60
|
+
if (storage) {
|
61
|
+
storage.setItem(name, value);
|
62
|
+
}
|
63
|
+
},
|
64
|
+
removeItem: (name: string): void => {
|
65
|
+
if (storage) {
|
66
|
+
storage.removeItem(name);
|
67
|
+
}
|
68
|
+
},
|
69
|
+
};
|
70
|
+
|
71
|
+
// Create the actual store
|
72
|
+
const store = create<StoreState>()(
|
73
|
+
devtools(
|
74
|
+
persist(
|
75
|
+
immer((set, get, store) => {
|
76
|
+
const [appSlice, appCleanup] = createAppSlice(
|
77
|
+
set as (fn: (state: StoreState) => void) => void,
|
78
|
+
get,
|
79
|
+
store,
|
80
|
+
);
|
81
|
+
const [logsSlice, logsCleanup] = createLogsSlice(
|
82
|
+
set as (fn: (state: StoreState) => void) => void,
|
83
|
+
get,
|
84
|
+
store,
|
85
|
+
);
|
86
|
+
const [logSlice, logCleanup] = createLogSlice(
|
87
|
+
set as (fn: (state: StoreState) => void) => void,
|
88
|
+
get,
|
89
|
+
store,
|
90
|
+
);
|
91
|
+
const [sampleSlice, sampleCleanup] = createSampleSlice(
|
92
|
+
set as (fn: (state: StoreState) => void) => void,
|
93
|
+
get,
|
94
|
+
store,
|
95
|
+
);
|
96
|
+
|
97
|
+
return {
|
98
|
+
// Shared state
|
99
|
+
api: null,
|
100
|
+
|
101
|
+
// Initialize
|
102
|
+
initialize: (api, capabilities) => {
|
103
|
+
set((state) => {
|
104
|
+
state.api = api;
|
105
|
+
});
|
106
|
+
|
107
|
+
// Initialize application slices
|
108
|
+
initializeAppSlice(
|
109
|
+
set as (fn: (state: StoreState) => void) => void,
|
110
|
+
capabilities,
|
111
|
+
);
|
112
|
+
initializeLogsSlice(
|
113
|
+
set as (fn: (state: StoreState) => void) => void,
|
114
|
+
);
|
115
|
+
initalializeLogSlice(
|
116
|
+
set as (fn: (state: StoreState) => void) => void,
|
117
|
+
);
|
118
|
+
initializeSampleSlice(
|
119
|
+
set as (fn: (state: StoreState) => void) => void,
|
120
|
+
);
|
121
|
+
},
|
122
|
+
|
123
|
+
// Create the slices and merge them in
|
124
|
+
...appSlice,
|
125
|
+
...logsSlice,
|
126
|
+
...logSlice,
|
127
|
+
...sampleSlice,
|
128
|
+
|
129
|
+
cleanup: () => {
|
130
|
+
appCleanup();
|
131
|
+
logsCleanup();
|
132
|
+
logCleanup();
|
133
|
+
sampleCleanup();
|
134
|
+
},
|
135
|
+
};
|
136
|
+
}),
|
137
|
+
{
|
138
|
+
name: "app-storage",
|
139
|
+
storage: storageImplementation,
|
140
|
+
partialize: (state) => {
|
141
|
+
const persisted: PersistedState = filterState({
|
142
|
+
app: state.app,
|
143
|
+
log: state.log,
|
144
|
+
logs: state.logs,
|
145
|
+
sample: state.sample,
|
146
|
+
});
|
147
|
+
return persisted as unknown as StoreState;
|
148
|
+
},
|
149
|
+
version: 1,
|
150
|
+
onRehydrateStorage: (state: StoreState) => {
|
151
|
+
return (hydrationState, error) => {
|
152
|
+
log.debug("REHYDRATING STATE");
|
153
|
+
if (error) {
|
154
|
+
log.debug("ERROR", { error });
|
155
|
+
} else {
|
156
|
+
log.debug("STATE", { state, hydrationState });
|
157
|
+
}
|
158
|
+
};
|
159
|
+
},
|
160
|
+
},
|
161
|
+
),
|
162
|
+
),
|
163
|
+
);
|
164
|
+
|
165
|
+
// Set the implementation and initialize it
|
166
|
+
storeImplementation = store as UseBoundStore<StoreApi<StoreState>>;
|
167
|
+
store.getState().initialize(api, capabilities);
|
168
|
+
};
|
@@ -0,0 +1,84 @@
|
|
1
|
+
import { PersistedState } from "./store";
|
2
|
+
|
3
|
+
export function filterState(state: PersistedState) {
|
4
|
+
if (!state) {
|
5
|
+
return state;
|
6
|
+
}
|
7
|
+
|
8
|
+
// When saving state, we can't store vast amounts of data (like a large sample)
|
9
|
+
const filters = [filterLargeSample, filterLargeLogSummary];
|
10
|
+
return filters.reduce(
|
11
|
+
(filteredState, filter) => filter(filteredState),
|
12
|
+
state,
|
13
|
+
);
|
14
|
+
}
|
15
|
+
|
16
|
+
// Filters the selected Sample if it is large
|
17
|
+
function filterLargeSample(state: PersistedState): PersistedState {
|
18
|
+
if (!state || !state.sample || !state.sample.selectedSample) {
|
19
|
+
return state;
|
20
|
+
}
|
21
|
+
|
22
|
+
const estimatedTotalSize = estimateSize(state.sample.selectedSample.messages);
|
23
|
+
if (estimatedTotalSize > 250000) {
|
24
|
+
return {
|
25
|
+
...state,
|
26
|
+
sample: {
|
27
|
+
...state.sample,
|
28
|
+
selectedSample: undefined,
|
29
|
+
},
|
30
|
+
};
|
31
|
+
} else {
|
32
|
+
return state;
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
// Filters the selectedlog if it is too large
|
37
|
+
function filterLargeLogSummary(state: PersistedState): PersistedState {
|
38
|
+
if (!state || !state.log || !state.log.selectedLogSummary) {
|
39
|
+
return state;
|
40
|
+
}
|
41
|
+
|
42
|
+
const estimatedSize = estimateSize(
|
43
|
+
state.log.selectedLogSummary.sampleSummaries,
|
44
|
+
);
|
45
|
+
if (estimatedSize > 250000) {
|
46
|
+
return {
|
47
|
+
...state,
|
48
|
+
log: {
|
49
|
+
...state.log,
|
50
|
+
selectedLogSummary: undefined,
|
51
|
+
},
|
52
|
+
};
|
53
|
+
} else {
|
54
|
+
return state;
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
function estimateSize(list: unknown[], frequency = 0.2) {
|
59
|
+
if (!list || list.length === 0) {
|
60
|
+
return 0;
|
61
|
+
}
|
62
|
+
|
63
|
+
// Total number of samples
|
64
|
+
const sampleSize = Math.ceil(list.length * frequency);
|
65
|
+
|
66
|
+
// Get a proper random sample without duplicates
|
67
|
+
const messageIndices = new Set<number>();
|
68
|
+
while (
|
69
|
+
messageIndices.size < sampleSize &&
|
70
|
+
messageIndices.size < list.length
|
71
|
+
) {
|
72
|
+
const randomIndex = Math.floor(Math.random() * list.length);
|
73
|
+
messageIndices.add(randomIndex);
|
74
|
+
}
|
75
|
+
|
76
|
+
// Calculate size from sampled messages
|
77
|
+
const totalSize = Array.from(messageIndices).reduce((size, index) => {
|
78
|
+
return size + JSON.stringify(list[index]).length;
|
79
|
+
}, 0);
|
80
|
+
|
81
|
+
// Estimate total size based on sample
|
82
|
+
const estimatedTotalSize = (totalSize / sampleSize) * list.length;
|
83
|
+
return estimatedTotalSize;
|
84
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
import { SampleSummary } from "../api/types";
|
2
|
+
|
3
|
+
// Function to merge log samples with pending samples
|
4
|
+
export const mergeSampleSummaries = (
|
5
|
+
logSamples: SampleSummary[],
|
6
|
+
pendingSamples: SampleSummary[],
|
7
|
+
) => {
|
8
|
+
// Create a map of existing sample IDs to avoid duplicates
|
9
|
+
const existingSampleIds = new Set(
|
10
|
+
logSamples.map((sample) => `${sample.id}-${sample.epoch}`),
|
11
|
+
);
|
12
|
+
|
13
|
+
// Filter out any pending samples that already exist in the log
|
14
|
+
const uniquePendingSamples = pendingSamples
|
15
|
+
.filter((sample) => !existingSampleIds.has(`${sample.id}-${sample.epoch}`))
|
16
|
+
.map((sample) => {
|
17
|
+
// Always mark pending items as incomplete to be sure we trigger polling
|
18
|
+
return { ...sample, completed: false };
|
19
|
+
});
|
20
|
+
|
21
|
+
// Combine and return all samples
|
22
|
+
return [...logSamples, ...uniquePendingSamples];
|
23
|
+
};
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import { ClientStorage } from "../api/types";
|
2
|
+
import { PersistedState } from "../state/store";
|
3
|
+
import { getVscodeApi } from "../utils/vscode";
|
4
|
+
|
5
|
+
const resolveStorage = (): ClientStorage | undefined => {
|
6
|
+
const vscodeApi = getVscodeApi();
|
7
|
+
if (vscodeApi) {
|
8
|
+
return {
|
9
|
+
getItem: (_name: string) => {
|
10
|
+
const state = vscodeApi.getState() as PersistedState;
|
11
|
+
return state;
|
12
|
+
},
|
13
|
+
setItem: (_name: string, value: unknown) => {
|
14
|
+
// TODO: This is pretty gnarly type hijinks
|
15
|
+
const valObj = value as { state: PersistedState; version: number };
|
16
|
+
vscodeApi.setState(valObj);
|
17
|
+
},
|
18
|
+
removeItem: (_name: string) => {
|
19
|
+
vscodeApi.setState(null);
|
20
|
+
},
|
21
|
+
};
|
22
|
+
}
|
23
|
+
return undefined;
|
24
|
+
};
|
25
|
+
|
26
|
+
export default resolveStorage();
|