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.
Files changed (297) hide show
  1. inspect_ai/__init__.py +2 -1
  2. inspect_ai/_cli/eval.py +35 -2
  3. inspect_ai/_cli/util.py +44 -1
  4. inspect_ai/_display/core/config.py +1 -1
  5. inspect_ai/_display/core/display.py +13 -4
  6. inspect_ai/_display/core/results.py +1 -1
  7. inspect_ai/_display/textual/app.py +14 -3
  8. inspect_ai/_display/textual/display.py +4 -0
  9. inspect_ai/_display/textual/widgets/samples.py +9 -3
  10. inspect_ai/_display/textual/widgets/task_detail.py +8 -8
  11. inspect_ai/_display/textual/widgets/tasks.py +17 -1
  12. inspect_ai/_display/textual/widgets/vscode.py +44 -0
  13. inspect_ai/_eval/eval.py +74 -25
  14. inspect_ai/_eval/evalset.py +22 -18
  15. inspect_ai/_eval/loader.py +34 -11
  16. inspect_ai/_eval/run.py +13 -15
  17. inspect_ai/_eval/score.py +13 -3
  18. inspect_ai/_eval/task/generate.py +8 -9
  19. inspect_ai/_eval/task/log.py +55 -6
  20. inspect_ai/_eval/task/run.py +51 -10
  21. inspect_ai/_eval/task/task.py +23 -9
  22. inspect_ai/_util/constants.py +2 -0
  23. inspect_ai/_util/file.py +30 -1
  24. inspect_ai/_util/json.py +37 -1
  25. inspect_ai/_util/registry.py +1 -0
  26. inspect_ai/_util/vscode.py +37 -0
  27. inspect_ai/_view/server.py +113 -1
  28. inspect_ai/_view/www/App.css +7 -1
  29. inspect_ai/_view/www/dist/assets/index.css +813 -415
  30. inspect_ai/_view/www/dist/assets/index.js +54475 -32003
  31. inspect_ai/_view/www/eslint.config.mjs +1 -1
  32. inspect_ai/_view/www/log-schema.json +137 -31
  33. inspect_ai/_view/www/node_modules/flatted/python/flatted.py +149 -0
  34. inspect_ai/_view/www/package.json +11 -2
  35. inspect_ai/_view/www/src/App.tsx +161 -853
  36. inspect_ai/_view/www/src/api/api-browser.ts +176 -5
  37. inspect_ai/_view/www/src/api/api-vscode.ts +75 -1
  38. inspect_ai/_view/www/src/api/client-api.ts +66 -10
  39. inspect_ai/_view/www/src/api/jsonrpc.ts +2 -0
  40. inspect_ai/_view/www/src/api/types.ts +107 -2
  41. inspect_ai/_view/www/src/appearance/icons.ts +2 -0
  42. inspect_ai/_view/www/src/components/AsciinemaPlayer.tsx +3 -3
  43. inspect_ai/_view/www/src/components/Card.tsx +6 -4
  44. inspect_ai/_view/www/src/components/DownloadPanel.tsx +2 -2
  45. inspect_ai/_view/www/src/components/ExpandablePanel.tsx +56 -61
  46. inspect_ai/_view/www/src/components/FindBand.tsx +17 -9
  47. inspect_ai/_view/www/src/components/HumanBaselineView.tsx +1 -1
  48. inspect_ai/_view/www/src/components/JsonPanel.tsx +14 -24
  49. inspect_ai/_view/www/src/components/LargeModal.tsx +2 -35
  50. inspect_ai/_view/www/src/components/LightboxCarousel.tsx +27 -11
  51. inspect_ai/_view/www/src/components/LinkButton.module.css +16 -0
  52. inspect_ai/_view/www/src/components/LinkButton.tsx +33 -0
  53. inspect_ai/_view/www/src/components/LiveVirtualList.module.css +11 -0
  54. inspect_ai/_view/www/src/components/LiveVirtualList.tsx +177 -0
  55. inspect_ai/_view/www/src/components/MarkdownDiv.tsx +116 -26
  56. inspect_ai/_view/www/src/components/MessageBand.tsx +14 -9
  57. inspect_ai/_view/www/src/components/Modal.module.css +38 -0
  58. inspect_ai/_view/www/src/components/Modal.tsx +77 -0
  59. inspect_ai/_view/www/src/components/MorePopOver.tsx +3 -3
  60. inspect_ai/_view/www/src/components/NavPills.tsx +20 -8
  61. inspect_ai/_view/www/src/components/NoContentsPanel.module.css +12 -0
  62. inspect_ai/_view/www/src/components/NoContentsPanel.tsx +20 -0
  63. inspect_ai/_view/www/src/components/ProgressBar.module.css +5 -4
  64. inspect_ai/_view/www/src/components/ProgressBar.tsx +3 -2
  65. inspect_ai/_view/www/src/components/PulsingDots.module.css +81 -0
  66. inspect_ai/_view/www/src/components/PulsingDots.tsx +45 -0
  67. inspect_ai/_view/www/src/components/TabSet.tsx +4 -37
  68. inspect_ai/_view/www/src/components/ToolButton.tsx +3 -4
  69. inspect_ai/_view/www/src/index.tsx +26 -94
  70. inspect_ai/_view/www/src/logfile/remoteLogFile.ts +9 -1
  71. inspect_ai/_view/www/src/logfile/remoteZipFile.ts +30 -4
  72. inspect_ai/_view/www/src/metadata/RenderedContent.tsx +4 -6
  73. inspect_ai/_view/www/src/plan/DetailStep.module.css +4 -0
  74. inspect_ai/_view/www/src/plan/DetailStep.tsx +6 -3
  75. inspect_ai/_view/www/src/plan/ScorerDetailView.tsx +1 -1
  76. inspect_ai/_view/www/src/plan/SolverDetailView.module.css +2 -1
  77. inspect_ai/_view/www/src/samples/InlineSampleDisplay.module.css +9 -1
  78. inspect_ai/_view/www/src/samples/InlineSampleDisplay.tsx +74 -28
  79. inspect_ai/_view/www/src/samples/SampleDialog.tsx +58 -22
  80. inspect_ai/_view/www/src/samples/SampleDisplay.module.css +4 -0
  81. inspect_ai/_view/www/src/samples/SampleDisplay.tsx +135 -104
  82. inspect_ai/_view/www/src/samples/SampleSummaryView.module.css +10 -0
  83. inspect_ai/_view/www/src/samples/SampleSummaryView.tsx +83 -36
  84. inspect_ai/_view/www/src/samples/SamplesTools.tsx +35 -30
  85. inspect_ai/_view/www/src/samples/chat/ChatMessage.tsx +2 -1
  86. inspect_ai/_view/www/src/samples/chat/ChatMessageRenderer.tsx +1 -1
  87. inspect_ai/_view/www/src/samples/chat/ChatViewVirtualList.tsx +45 -53
  88. inspect_ai/_view/www/src/samples/chat/MessageContent.tsx +6 -1
  89. inspect_ai/_view/www/src/samples/chat/MessageContents.tsx +5 -0
  90. inspect_ai/_view/www/src/samples/chat/messages.ts +36 -0
  91. inspect_ai/_view/www/src/samples/chat/tools/ToolCallView.module.css +3 -0
  92. inspect_ai/_view/www/src/samples/chat/tools/ToolCallView.tsx +11 -1
  93. inspect_ai/_view/www/src/samples/chat/tools/ToolInput.tsx +22 -46
  94. inspect_ai/_view/www/src/samples/descriptor/samplesDescriptor.tsx +34 -20
  95. inspect_ai/_view/www/src/samples/descriptor/score/BooleanScoreDescriptor.module.css +3 -3
  96. inspect_ai/_view/www/src/samples/descriptor/score/BooleanScoreDescriptor.tsx +1 -1
  97. inspect_ai/_view/www/src/samples/descriptor/score/ObjectScoreDescriptor.module.css +4 -4
  98. inspect_ai/_view/www/src/samples/descriptor/score/ObjectScoreDescriptor.tsx +10 -10
  99. inspect_ai/_view/www/src/samples/descriptor/types.ts +6 -5
  100. inspect_ai/_view/www/src/samples/list/SampleFooter.module.css +22 -3
  101. inspect_ai/_view/www/src/samples/list/SampleFooter.tsx +27 -2
  102. inspect_ai/_view/www/src/samples/list/SampleList.tsx +122 -85
  103. inspect_ai/_view/www/src/samples/list/SampleRow.module.css +6 -0
  104. inspect_ai/_view/www/src/samples/list/SampleRow.tsx +28 -15
  105. inspect_ai/_view/www/src/samples/sample-tools/SelectScorer.tsx +29 -18
  106. inspect_ai/_view/www/src/samples/sample-tools/SortFilter.tsx +28 -28
  107. inspect_ai/_view/www/src/samples/sample-tools/sample-filter/SampleFilter.tsx +19 -9
  108. inspect_ai/_view/www/src/samples/sampleDataAdapter.ts +33 -0
  109. inspect_ai/_view/www/src/samples/sampleLimit.ts +2 -2
  110. inspect_ai/_view/www/src/samples/scores/SampleScores.tsx +12 -27
  111. inspect_ai/_view/www/src/samples/scores/SampleScoresGrid.module.css +38 -0
  112. inspect_ai/_view/www/src/samples/scores/SampleScoresGrid.tsx +118 -0
  113. inspect_ai/_view/www/src/samples/scores/{SampleScoreView.module.css → SampleScoresView.module.css} +10 -1
  114. inspect_ai/_view/www/src/samples/scores/SampleScoresView.tsx +78 -0
  115. inspect_ai/_view/www/src/samples/transcript/ErrorEventView.tsx +0 -13
  116. inspect_ai/_view/www/src/samples/transcript/InfoEventView.tsx +0 -13
  117. inspect_ai/_view/www/src/samples/transcript/InputEventView.tsx +0 -13
  118. inspect_ai/_view/www/src/samples/transcript/ModelEventView.module.css +4 -0
  119. inspect_ai/_view/www/src/samples/transcript/ModelEventView.tsx +10 -24
  120. inspect_ai/_view/www/src/samples/transcript/SampleInitEventView.tsx +0 -13
  121. inspect_ai/_view/www/src/samples/transcript/SampleLimitEventView.tsx +4 -22
  122. inspect_ai/_view/www/src/samples/transcript/SandboxEventView.tsx +15 -24
  123. inspect_ai/_view/www/src/samples/transcript/ScoreEventView.tsx +0 -13
  124. inspect_ai/_view/www/src/samples/transcript/StepEventView.tsx +6 -28
  125. inspect_ai/_view/www/src/samples/transcript/SubtaskEventView.tsx +24 -34
  126. inspect_ai/_view/www/src/samples/transcript/ToolEventView.module.css +4 -0
  127. inspect_ai/_view/www/src/samples/transcript/ToolEventView.tsx +33 -17
  128. inspect_ai/_view/www/src/samples/transcript/TranscriptView.tsx +197 -338
  129. inspect_ai/_view/www/src/samples/transcript/TranscriptVirtualListComponent.module.css +16 -0
  130. inspect_ai/_view/www/src/samples/transcript/TranscriptVirtualListComponent.tsx +44 -0
  131. inspect_ai/_view/www/src/samples/transcript/event/EventNav.tsx +7 -4
  132. inspect_ai/_view/www/src/samples/transcript/event/EventPanel.tsx +81 -60
  133. inspect_ai/_view/www/src/samples/transcript/event/EventProgressPanel.module.css +23 -0
  134. inspect_ai/_view/www/src/samples/transcript/event/EventProgressPanel.tsx +27 -0
  135. inspect_ai/_view/www/src/samples/transcript/state/StateEventRenderers.tsx +29 -1
  136. inspect_ai/_view/www/src/samples/transcript/state/StateEventView.tsx +102 -72
  137. inspect_ai/_view/www/src/scoring/utils.ts +87 -0
  138. inspect_ai/_view/www/src/state/appSlice.ts +244 -0
  139. inspect_ai/_view/www/src/state/hooks.ts +399 -0
  140. inspect_ai/_view/www/src/state/logPolling.ts +200 -0
  141. inspect_ai/_view/www/src/state/logSlice.ts +224 -0
  142. inspect_ai/_view/www/src/state/logsPolling.ts +118 -0
  143. inspect_ai/_view/www/src/state/logsSlice.ts +181 -0
  144. inspect_ai/_view/www/src/state/samplePolling.ts +314 -0
  145. inspect_ai/_view/www/src/state/sampleSlice.ts +140 -0
  146. inspect_ai/_view/www/src/state/sampleUtils.ts +21 -0
  147. inspect_ai/_view/www/src/state/scrolling.ts +206 -0
  148. inspect_ai/_view/www/src/state/store.ts +168 -0
  149. inspect_ai/_view/www/src/state/store_filter.ts +84 -0
  150. inspect_ai/_view/www/src/state/utils.ts +23 -0
  151. inspect_ai/_view/www/src/storage/index.ts +26 -0
  152. inspect_ai/_view/www/src/types/log.d.ts +36 -26
  153. inspect_ai/_view/www/src/types/markdown-it-katex.d.ts +21 -0
  154. inspect_ai/_view/www/src/types.ts +94 -32
  155. inspect_ai/_view/www/src/utils/attachments.ts +58 -23
  156. inspect_ai/_view/www/src/utils/json-worker.ts +79 -12
  157. inspect_ai/_view/www/src/utils/logger.ts +52 -0
  158. inspect_ai/_view/www/src/utils/polling.ts +100 -0
  159. inspect_ai/_view/www/src/utils/react.ts +30 -0
  160. inspect_ai/_view/www/src/utils/vscode.ts +1 -1
  161. inspect_ai/_view/www/src/workspace/WorkSpace.tsx +184 -217
  162. inspect_ai/_view/www/src/workspace/WorkSpaceView.tsx +11 -53
  163. inspect_ai/_view/www/src/workspace/navbar/Navbar.tsx +8 -18
  164. inspect_ai/_view/www/src/workspace/navbar/PrimaryBar.module.css +1 -0
  165. inspect_ai/_view/www/src/workspace/navbar/PrimaryBar.tsx +40 -22
  166. inspect_ai/_view/www/src/workspace/navbar/ResultsPanel.module.css +16 -1
  167. inspect_ai/_view/www/src/workspace/navbar/ResultsPanel.tsx +159 -103
  168. inspect_ai/_view/www/src/workspace/navbar/RunningStatusPanel.module.css +32 -0
  169. inspect_ai/_view/www/src/workspace/navbar/RunningStatusPanel.tsx +32 -0
  170. inspect_ai/_view/www/src/workspace/navbar/ScoreGrid.module.css +35 -0
  171. inspect_ai/_view/www/src/workspace/navbar/ScoreGrid.tsx +117 -0
  172. inspect_ai/_view/www/src/workspace/navbar/SecondaryBar.tsx +12 -14
  173. inspect_ai/_view/www/src/workspace/navbar/StatusPanel.tsx +6 -2
  174. inspect_ai/_view/www/src/workspace/sidebar/LogDirectoryTitleView.tsx +4 -4
  175. inspect_ai/_view/www/src/workspace/sidebar/Sidebar.module.css +3 -2
  176. inspect_ai/_view/www/src/workspace/sidebar/Sidebar.tsx +28 -13
  177. inspect_ai/_view/www/src/workspace/tabs/InfoTab.tsx +5 -10
  178. inspect_ai/_view/www/src/workspace/tabs/JsonTab.tsx +4 -4
  179. inspect_ai/_view/www/src/workspace/tabs/RunningNoSamples.module.css +22 -0
  180. inspect_ai/_view/www/src/workspace/tabs/RunningNoSamples.tsx +19 -0
  181. inspect_ai/_view/www/src/workspace/tabs/SamplesTab.tsx +128 -115
  182. inspect_ai/_view/www/src/workspace/tabs/grouping.ts +37 -5
  183. inspect_ai/_view/www/src/workspace/tabs/types.ts +4 -0
  184. inspect_ai/_view/www/src/workspace/types.ts +4 -3
  185. inspect_ai/_view/www/src/workspace/utils.ts +4 -4
  186. inspect_ai/_view/www/vite.config.js +6 -0
  187. inspect_ai/_view/www/yarn.lock +464 -355
  188. inspect_ai/agent/__init__.py +36 -0
  189. inspect_ai/agent/_agent.py +268 -0
  190. inspect_ai/agent/_as_solver.py +72 -0
  191. inspect_ai/agent/_as_tool.py +122 -0
  192. inspect_ai/{solver → agent}/_bridge/bridge.py +23 -37
  193. inspect_ai/{solver → agent}/_bridge/patch.py +9 -8
  194. inspect_ai/agent/_filter.py +46 -0
  195. inspect_ai/agent/_handoff.py +93 -0
  196. inspect_ai/{solver/_human_agent → agent/_human}/agent.py +11 -12
  197. inspect_ai/{solver/_human_agent → agent/_human}/commands/__init__.py +2 -3
  198. inspect_ai/{solver/_human_agent → agent/_human}/commands/clock.py +3 -1
  199. inspect_ai/{solver/_human_agent → agent/_human}/commands/score.py +5 -5
  200. inspect_ai/{solver/_human_agent → agent/_human}/install.py +6 -3
  201. inspect_ai/{solver/_human_agent → agent/_human}/service.py +7 -3
  202. inspect_ai/{solver/_human_agent → agent/_human}/state.py +5 -5
  203. inspect_ai/agent/_react.py +241 -0
  204. inspect_ai/agent/_run.py +36 -0
  205. inspect_ai/agent/_types.py +81 -0
  206. inspect_ai/log/_condense.py +26 -0
  207. inspect_ai/log/_log.py +17 -5
  208. inspect_ai/log/_recorders/buffer/__init__.py +14 -0
  209. inspect_ai/log/_recorders/buffer/buffer.py +30 -0
  210. inspect_ai/log/_recorders/buffer/database.py +685 -0
  211. inspect_ai/log/_recorders/buffer/filestore.py +259 -0
  212. inspect_ai/log/_recorders/buffer/types.py +84 -0
  213. inspect_ai/log/_recorders/eval.py +2 -11
  214. inspect_ai/log/_recorders/types.py +30 -0
  215. inspect_ai/log/_transcript.py +32 -2
  216. inspect_ai/model/__init__.py +7 -1
  217. inspect_ai/model/_call_tools.py +257 -52
  218. inspect_ai/model/_chat_message.py +7 -4
  219. inspect_ai/model/_conversation.py +13 -62
  220. inspect_ai/model/_display.py +85 -0
  221. inspect_ai/model/_generate_config.py +2 -2
  222. inspect_ai/model/_model.py +114 -14
  223. inspect_ai/model/_model_output.py +14 -9
  224. inspect_ai/model/_openai.py +16 -4
  225. inspect_ai/model/_openai_computer_use.py +162 -0
  226. inspect_ai/model/_openai_responses.py +319 -165
  227. inspect_ai/model/_providers/anthropic.py +20 -21
  228. inspect_ai/model/_providers/azureai.py +24 -13
  229. inspect_ai/model/_providers/bedrock.py +1 -7
  230. inspect_ai/model/_providers/cloudflare.py +3 -3
  231. inspect_ai/model/_providers/goodfire.py +2 -6
  232. inspect_ai/model/_providers/google.py +11 -10
  233. inspect_ai/model/_providers/groq.py +6 -3
  234. inspect_ai/model/_providers/hf.py +7 -3
  235. inspect_ai/model/_providers/mistral.py +7 -10
  236. inspect_ai/model/_providers/openai.py +47 -17
  237. inspect_ai/model/_providers/openai_o1.py +11 -4
  238. inspect_ai/model/_providers/openai_responses.py +12 -14
  239. inspect_ai/model/_providers/providers.py +2 -2
  240. inspect_ai/model/_providers/together.py +12 -2
  241. inspect_ai/model/_providers/util/chatapi.py +7 -2
  242. inspect_ai/model/_providers/util/hf_handler.py +4 -2
  243. inspect_ai/model/_providers/util/llama31.py +4 -2
  244. inspect_ai/model/_providers/vertex.py +11 -9
  245. inspect_ai/model/_providers/vllm.py +4 -4
  246. inspect_ai/scorer/__init__.py +2 -0
  247. inspect_ai/scorer/_metrics/__init__.py +2 -0
  248. inspect_ai/scorer/_metrics/grouped.py +84 -0
  249. inspect_ai/scorer/_score.py +26 -6
  250. inspect_ai/solver/__init__.py +2 -2
  251. inspect_ai/solver/_basic_agent.py +22 -9
  252. inspect_ai/solver/_bridge.py +31 -0
  253. inspect_ai/solver/_chain.py +20 -12
  254. inspect_ai/solver/_fork.py +5 -1
  255. inspect_ai/solver/_human_agent.py +52 -0
  256. inspect_ai/solver/_prompt.py +3 -1
  257. inspect_ai/solver/_run.py +59 -0
  258. inspect_ai/solver/_solver.py +14 -4
  259. inspect_ai/solver/_task_state.py +5 -3
  260. inspect_ai/tool/_tool_call.py +15 -8
  261. inspect_ai/tool/_tool_def.py +17 -12
  262. inspect_ai/tool/_tool_support_helpers.py +4 -4
  263. inspect_ai/tool/_tool_with.py +14 -11
  264. inspect_ai/tool/_tools/_bash_session.py +11 -2
  265. inspect_ai/tool/_tools/_computer/_common.py +18 -2
  266. inspect_ai/tool/_tools/_computer/_computer.py +18 -2
  267. inspect_ai/tool/_tools/_computer/_resources/tool/_constants.py +2 -0
  268. inspect_ai/tool/_tools/_computer/_resources/tool/_x11_client.py +17 -0
  269. inspect_ai/tool/_tools/_think.py +1 -1
  270. inspect_ai/tool/_tools/_web_browser/_web_browser.py +103 -62
  271. inspect_ai/util/__init__.py +2 -0
  272. inspect_ai/util/_anyio.py +27 -0
  273. inspect_ai/util/_sandbox/__init__.py +2 -1
  274. inspect_ai/util/_sandbox/context.py +32 -7
  275. inspect_ai/util/_sandbox/docker/cleanup.py +4 -0
  276. inspect_ai/util/_sandbox/docker/compose.py +2 -2
  277. inspect_ai/util/_sandbox/docker/docker.py +12 -1
  278. inspect_ai/util/_store_model.py +30 -7
  279. inspect_ai/util/_subprocess.py +13 -3
  280. inspect_ai/util/_subtask.py +1 -0
  281. {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.83.dist-info}/METADATA +1 -1
  282. {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.83.dist-info}/RECORD +295 -229
  283. inspect_ai/_view/www/src/samples/scores/SampleScoreView.tsx +0 -169
  284. inspect_ai/_view/www/src/samples/transcript/SampleTranscript.tsx +0 -22
  285. /inspect_ai/{solver → agent}/_bridge/__init__.py +0 -0
  286. /inspect_ai/{solver/_human_agent → agent/_human}/__init__.py +0 -0
  287. /inspect_ai/{solver/_human_agent → agent/_human}/commands/command.py +0 -0
  288. /inspect_ai/{solver/_human_agent → agent/_human}/commands/instructions.py +0 -0
  289. /inspect_ai/{solver/_human_agent → agent/_human}/commands/note.py +0 -0
  290. /inspect_ai/{solver/_human_agent → agent/_human}/commands/status.py +0 -0
  291. /inspect_ai/{solver/_human_agent → agent/_human}/commands/submit.py +0 -0
  292. /inspect_ai/{solver/_human_agent → agent/_human}/panel.py +0 -0
  293. /inspect_ai/{solver/_human_agent → agent/_human}/view.py +0 -0
  294. {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.83.dist-info}/WHEEL +0 -0
  295. {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.83.dist-info}/entry_points.txt +0 -0
  296. {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.83.dist-info}/licenses/LICENSE +0 -0
  297. {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.83.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,177 @@
1
+ import clsx from "clsx";
2
+ import {
3
+ ReactNode,
4
+ RefObject,
5
+ useCallback,
6
+ useEffect,
7
+ useRef,
8
+ useState,
9
+ } from "react";
10
+ import { Virtuoso, VirtuosoHandle } from "react-virtuoso";
11
+ import { usePrevious, useProperty } from "../state/hooks";
12
+ import { useRafThrottle, useVirtuosoState } from "../state/scrolling";
13
+ import { PulsingDots } from "./PulsingDots";
14
+
15
+ import styles from "./LiveVirtualList.module.css";
16
+
17
+ interface LiveVirtualListProps<T> {
18
+ id: string;
19
+ className?: string | string[];
20
+
21
+ // The scroll ref to use for the virtual list
22
+ scrollRef?: RefObject<HTMLDivElement | null>;
23
+
24
+ // The data and rendering function for the data
25
+ data: T[];
26
+ renderRow: (index: number, item: T) => ReactNode;
27
+
28
+ // Whether the virtual list is live (controls its follow
29
+ // behavior)
30
+ live?: boolean;
31
+
32
+ // The progress message to show (if any)
33
+ // no message show if progress isn't provided
34
+ showProgress?: boolean;
35
+ }
36
+
37
+ /**
38
+ * Renders the Transcript component.
39
+ */
40
+ export const LiveVirtualList = <T,>({
41
+ id,
42
+ className,
43
+ data,
44
+ renderRow,
45
+ scrollRef,
46
+ live,
47
+ showProgress,
48
+ }: LiveVirtualListProps<T>) => {
49
+ // The list handle and list state management
50
+ const listHandle = useRef<VirtuosoHandle>(null);
51
+ const { getRestoreState, isScrolling } = useVirtuosoState(
52
+ listHandle,
53
+ `live-virtual-list-${id}`,
54
+ );
55
+
56
+ // Track whether we're following output
57
+ const [followOutput, setFollowOutput] = useProperty<boolean | null>(
58
+ id,
59
+ "follow",
60
+ {
61
+ defaultValue: null,
62
+ },
63
+ );
64
+ const isAutoScrollingRef = useRef(false);
65
+
66
+ // Only we first load set the default value for following
67
+ // based upon whether or not the transcript is 'live'
68
+ useEffect(() => {
69
+ if (followOutput === null) {
70
+ setFollowOutput(!!live);
71
+ }
72
+ }, []);
73
+
74
+ // Track whether we were previously running so we can
75
+ // decide whether to pop up to the top
76
+ const prevLive = usePrevious(live);
77
+ useEffect(() => {
78
+ // When we finish running, if we are following output
79
+ // then scroll up to the top
80
+ if (!live && prevLive && followOutput && scrollRef?.current) {
81
+ setFollowOutput(false);
82
+ setTimeout(() => {
83
+ if (scrollRef.current) {
84
+ scrollRef.current.scrollTo({ top: 0, behavior: "instant" });
85
+ }
86
+ }, 100);
87
+ }
88
+ }, [live, followOutput]);
89
+
90
+ const handleScroll = useRafThrottle(() => {
91
+ // Skip processing if auto-scrolling is in progress
92
+ if (isAutoScrollingRef.current) return;
93
+
94
+ // If we're not running, don't mess with auto scrolling
95
+ if (!live) return;
96
+
97
+ // Make the bottom start following
98
+ if (scrollRef?.current && listHandle.current) {
99
+ const parent = scrollRef.current;
100
+ const isAtBottom =
101
+ parent.scrollHeight - parent.scrollTop <= parent.clientHeight + 30;
102
+
103
+ if (isAtBottom && !followOutput) {
104
+ setFollowOutput(true);
105
+ } else if (!isAtBottom && followOutput) {
106
+ setFollowOutput(false);
107
+ }
108
+ }
109
+ }, [setFollowOutput, followOutput, live]);
110
+
111
+ const heightChanged = useCallback(
112
+ (height: number) => {
113
+ requestAnimationFrame(() => {
114
+ if (followOutput && live && scrollRef?.current) {
115
+ isAutoScrollingRef.current = true;
116
+ listHandle.current?.scrollTo({ top: height });
117
+ requestAnimationFrame(() => {
118
+ isAutoScrollingRef.current = false;
119
+ });
120
+ }
121
+ });
122
+ },
123
+ [scrollRef, followOutput, live],
124
+ );
125
+
126
+ useEffect(() => {
127
+ // Force a re-render after initial mount
128
+ // This is here only because in VScode, for some reason,
129
+ // when this transcript is restored, the height isn't computed
130
+ // properly on the first render. This basically gives it a second
131
+ // change to compute the height and lay itself out.
132
+ const timer = setTimeout(() => {
133
+ forceUpdate();
134
+ }, 0);
135
+ return () => clearTimeout(timer);
136
+ }, []);
137
+
138
+ const [, forceRender] = useState({});
139
+ const forceUpdate = useCallback(() => forceRender({}), []);
140
+
141
+ const Footer = () => {
142
+ return showProgress ? (
143
+ <div className={clsx(styles.progressContainer)}>
144
+ <PulsingDots subtle={false} size="medium" />
145
+ </div>
146
+ ) : undefined;
147
+ };
148
+
149
+ useEffect(() => {
150
+ // Listen to scroll events
151
+ const parent = scrollRef?.current;
152
+ if (parent) {
153
+ parent.addEventListener("scroll", handleScroll);
154
+ return () => parent.removeEventListener("scroll", handleScroll);
155
+ }
156
+ }, [scrollRef, handleScroll]);
157
+
158
+ return (
159
+ <Virtuoso
160
+ ref={listHandle}
161
+ customScrollParent={scrollRef?.current ? scrollRef.current : undefined}
162
+ style={{ height: "100%", width: "100%" }}
163
+ data={data}
164
+ defaultItemHeight={250}
165
+ itemContent={renderRow}
166
+ increaseViewportBy={{ top: 1000, bottom: 1000 }}
167
+ overscan={{ main: 2, reverse: 2 }}
168
+ className={clsx("transcript", className)}
169
+ isScrolling={isScrolling}
170
+ restoreStateFrom={getRestoreState()}
171
+ totalListHeightChanged={heightChanged}
172
+ components={{
173
+ Footer,
174
+ }}
175
+ />
176
+ );
177
+ };
@@ -1,31 +1,46 @@
1
1
  import clsx from "clsx";
2
+ import "katex/dist/katex.min.css";
2
3
  import markdownit from "markdown-it";
3
- import React from "react";
4
+ import markdownitKatex from "markdown-it-katex";
5
+ import { CSSProperties, forwardRef } from "react";
4
6
  import "./MarkdownDiv.css";
5
7
 
6
8
  interface MarkdownDivProps {
7
9
  markdown: string;
8
- style?: React.CSSProperties;
10
+ style?: CSSProperties;
9
11
  className?: string | string[];
10
12
  }
11
13
 
12
- export const MarkdownDiv = React.forwardRef<HTMLDivElement, MarkdownDivProps>(
14
+ export const MarkdownDiv = forwardRef<HTMLDivElement, MarkdownDivProps>(
13
15
  ({ markdown, style, className }, ref) => {
16
+ // Protect backslashes in LaTeX expressions
17
+ const protectedContent = protectBackslashesInLatex(markdown);
18
+
14
19
  // Escape all tags
15
- const escaped = markdown ? escape(markdown) : "";
20
+ const escaped = escapeHtmlCharacters(protectedContent);
16
21
 
17
22
  // Pre-render any text that isn't handled by markdown
18
23
  const preRendered = preRenderText(escaped);
19
24
 
20
25
  const protectedText = protectMarkdown(preRendered);
21
26
 
22
- let renderedHtml = protectedText;
27
+ // Restore backslashes for LaTeX processing
28
+ const preparedForMarkdown = restoreBackslashesForLatex(protectedText);
29
+
30
+ let renderedHtml = preparedForMarkdown;
23
31
  try {
24
32
  const md = markdownit({
25
33
  breaks: true,
26
34
  html: true,
27
35
  });
28
- renderedHtml = md.render(protectedText);
36
+
37
+ // Add KaTeX support
38
+ md.use(markdownitKatex, {
39
+ throwOnError: false,
40
+ errorColor: "#cc0000",
41
+ });
42
+
43
+ renderedHtml = md.render(preparedForMarkdown);
29
44
  } catch (ex) {
30
45
  console.log("Unable to markdown render content");
31
46
  console.error(ex);
@@ -53,7 +68,95 @@ export const MarkdownDiv = React.forwardRef<HTMLDivElement, MarkdownDivProps>(
53
68
  const kLetterListPattern = /^([a-zA-Z][).]\s.*?)$/gm;
54
69
  const kCommonmarkReferenceLinkPattern = /\[([^\]]*)\]: (?!http)(.*)/g;
55
70
 
71
+ const protectBackslashesInLatex = (content: string): string => {
72
+ if (!content) return content;
73
+
74
+ try {
75
+ // Match inline math: $...$
76
+ const inlineRegex = /\$(.*?)\$/g;
77
+
78
+ // Match block math: $$...$$
79
+ const blockRegex = /\$\$([\s\S]*?)\$\$/g;
80
+
81
+ // Replace backslashes in LaTeX blocks with a placeholder
82
+ let result = content.replace(inlineRegex, (_match, latex) => {
83
+ const protectedTex = latex.replace(/\\/g, "___LATEX_BACKSLASH___");
84
+ return `$${protectedTex}$`;
85
+ });
86
+
87
+ result = result.replace(blockRegex, (_match, latex) => {
88
+ const protectedTex = latex.replace(/\\/g, "___LATEX_BACKSLASH___");
89
+ return `$$${protectedTex}$$`;
90
+ });
91
+
92
+ return result;
93
+ } catch (error) {
94
+ console.error("Error protecting LaTeX backslashes:", error);
95
+ return content;
96
+ }
97
+ };
98
+
99
+ const restoreBackslashesForLatex = (content: string): string => {
100
+ if (!content) return content;
101
+
102
+ try {
103
+ // First restore backslashes
104
+ let result = content.replace(/___LATEX_BACKSLASH___/g, "\\");
105
+
106
+ // Then fix dots notation for better KaTeX compatibility
107
+ // This replaces \dots with \ldots which has better support
108
+ result = fixDotsNotation(result);
109
+
110
+ return result;
111
+ } catch (error) {
112
+ console.error("Error restoring LaTeX backslashes:", error);
113
+ return content; // Return input content if something goes wrong
114
+ }
115
+ };
116
+
117
+ // Fixes dots notation in LaTeX by replacing \dots with \ldots for better compatibility
118
+ const fixDotsNotation = (content: string): string => {
119
+ if (!content) return content;
120
+
121
+ try {
122
+ // Handle both inline and block math
123
+ // First, fix inline math expressions ($...$)
124
+ let result = content.replace(/(\$[^$]*?)\\dots([^$]*?\$)/g, "$1\\ldots$2");
125
+
126
+ // Then, fix block math expressions ($...$)
127
+ result = result.replace(/(\$\$[^$]*?)\\dots([^$]*?\$\$)/g, "$1\\ldots$2");
128
+
129
+ return result;
130
+ } catch (error) {
131
+ console.error("Error fixing dots notation:", error);
132
+ return content;
133
+ }
134
+ };
135
+
136
+ const escapeHtmlCharacters = (content: string): string => {
137
+ if (!content) return content;
138
+
139
+ return content.replace(/[<>&'"]/g, (c: string): string => {
140
+ switch (c) {
141
+ case "<":
142
+ return "&lt;";
143
+ case ">":
144
+ return "&gt;";
145
+ case "&":
146
+ return "&amp;";
147
+ case "'":
148
+ return "&apos;";
149
+ case '"':
150
+ return "&quot;";
151
+ default:
152
+ throw new Error("Matched a value that isn't replaceable");
153
+ }
154
+ });
155
+ };
156
+
56
157
  const preRenderText = (txt: string): string => {
158
+ if (!txt) return txt;
159
+
57
160
  // eslint-disable-next-line no-misleading-character-class
58
161
  txt = txt.replace(/^[\u200B\u200C\u200D\u200E\u200F\uFEFF]/, "");
59
162
 
@@ -66,6 +169,8 @@ const preRenderText = (txt: string): string => {
66
169
  };
67
170
 
68
171
  const protectMarkdown = (txt: string): string => {
172
+ if (!txt) return txt;
173
+
69
174
  // Special handling for commonmark like reference links which might
70
175
  // look like:
71
176
  // [alias]: http://www.google.com
@@ -74,36 +179,21 @@ const protectMarkdown = (txt: string): string => {
74
179
  // Also fools this
75
180
  return txt.replaceAll(
76
181
  kCommonmarkReferenceLinkPattern,
77
- "(open:767A125E)$1(close:767A125E) $2 ",
182
+ "(open:767A125E)$1(close:767A125E) $2 ",
78
183
  );
79
184
  };
80
185
 
81
186
  const unprotectMarkdown = (txt: string): string => {
187
+ if (!txt) return txt;
188
+
82
189
  txt = txt.replaceAll("(open:767A125E)", "[");
83
190
  txt = txt.replaceAll("(close:767A125E)", "]");
84
191
  return txt;
85
192
  };
86
193
 
87
- const escape = (content: string): string => {
88
- return content.replace(/[<>&'"]/g, (c: string): string => {
89
- switch (c) {
90
- case "<":
91
- return "&lt;";
92
- case ">":
93
- return "&gt;";
94
- case "&":
95
- return "&amp;";
96
- case "'":
97
- return "&apos;";
98
- case '"':
99
- return "&quot;";
100
- default:
101
- throw new Error("Matched a value that isn't replaceable");
102
- }
103
- });
104
- };
105
-
106
194
  function unescapeCodeHtmlEntities(str: string): string {
195
+ if (!str) return str;
196
+
107
197
  const htmlEntities: Record<string, string> = {
108
198
  "&lt;": "<",
109
199
  "&gt;": ">",
@@ -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
- hidden: boolean;
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
- if (hidden) {
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>
@@ -0,0 +1,38 @@
1
+ .modal {
2
+ margin-left: auto;
3
+ margin-right: auto;
4
+ display: flex;
5
+ flex: 1 1 auto;
6
+ justify-content: center;
7
+ max-width: 90vw;
8
+ margin-top: 3rem;
9
+ border-radius: var(--bs-border-radius);
10
+ position: relative;
11
+ z-index: 1050;
12
+ }
13
+
14
+ .header {
15
+ height: 2em;
16
+ }
17
+
18
+ .modalTitle {
19
+ font-weight: 600;
20
+ }
21
+
22
+ .btnClose {
23
+ height: 0.8em;
24
+ width: 0.8em;
25
+ }
26
+
27
+ /* Backdrop styling for the glass effect */
28
+ .backdrop {
29
+ position: fixed;
30
+ top: 0;
31
+ left: 0;
32
+ width: 100%;
33
+ height: 100%;
34
+ background-color: var(--inspect-glass-color);
35
+ opacity: var(--inspect-glass-opacity);
36
+
37
+ z-index: 1040;
38
+ }
@@ -0,0 +1,77 @@
1
+ import clsx from "clsx";
2
+ import { FC, ReactNode } from "react";
3
+ import styles from "./Modal.module.css";
4
+
5
+ interface ModalProps {
6
+ id: string;
7
+ showing: boolean;
8
+ setShowing: (showing: boolean) => void;
9
+ title?: string;
10
+ children: ReactNode;
11
+ className?: string | string[];
12
+ }
13
+
14
+ export const Modal: FC<ModalProps> = ({
15
+ id,
16
+ title,
17
+ showing,
18
+ setShowing,
19
+ children,
20
+ className,
21
+ }) => {
22
+ return (
23
+ <>
24
+ {showing && (
25
+ <div className={styles.backdrop} onClick={() => setShowing(false)} />
26
+ )}
27
+ <div
28
+ id={id}
29
+ className={clsx("modal", "fade", showing ? "show" : "", className)}
30
+ tabIndex={-1}
31
+ style={{ display: showing ? "block" : "none" }}
32
+ >
33
+ <div className={clsx("modal-dialog", styles.modal)}>
34
+ <div className="modal-content">
35
+ <div className={clsx("modal-header", styles.header)}>
36
+ <div
37
+ className={clsx(
38
+ "modal-title",
39
+ "text-size-base",
40
+ styles.modalTitle,
41
+ )}
42
+ >
43
+ {title}
44
+ </div>
45
+ <button
46
+ type="button"
47
+ className={clsx(
48
+ "btn-close",
49
+ "text-size-smaller",
50
+ styles.btnClose,
51
+ )}
52
+ data-bs-dismiss="modal"
53
+ aria-label="Close"
54
+ onClick={() => {
55
+ setShowing(!showing);
56
+ }}
57
+ ></button>
58
+ </div>
59
+ <div className="modal-body">{children}</div>
60
+ <div className="modal-footer">
61
+ <button
62
+ type="button"
63
+ className="btn btn-secondary"
64
+ data-bs-dismiss="modal"
65
+ onClick={() => {
66
+ setShowing(!showing);
67
+ }}
68
+ >
69
+ Close
70
+ </button>
71
+ </div>
72
+ </div>
73
+ </div>
74
+ </div>
75
+ </>
76
+ );
77
+ };
@@ -1,14 +1,14 @@
1
1
  import { Popover } from "bootstrap";
2
- import React, { useEffect, useRef } from "react";
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: React.ReactNode;
8
+ children: ReactNode;
9
9
  }
10
10
 
11
- export const MorePopover: React.FC<MorePopoverProps> = ({
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, useState } from "react";
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 [activeItem, setActiveItem] = useState(
16
- children ? children[0].props["title"] : null,
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
- onClick={() => {
94
- setActiveItem(title);
95
- }}
106
+ data-target={title}
107
+ onClick={handleClick}
96
108
  >
97
109
  {title}
98
110
  </button>
@@ -0,0 +1,12 @@
1
+ .panel {
2
+ width: 100%;
3
+ display: flex;
4
+ justify-content: center;
5
+ }
6
+
7
+ .container {
8
+ margin-top: 3em;
9
+ display: grid;
10
+ grid-template-columns: max-content max-content;
11
+ column-gap: 0.3em;
12
+ }
@@ -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: 2px;
14
+ height: 1px;
16
15
  background-color: transparent;
17
- position: relative;
16
+ position: sticky;
18
17
  overflow: hidden;
18
+ z-index: 1200;
19
19
  }
20
20
 
21
21
  .animate {
22
22
  width: 5%;
23
- height: 2px;
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}