inspect-ai 0.3.58__py3-none-any.whl → 0.3.59__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 (127) hide show
  1. inspect_ai/_cli/common.py +3 -1
  2. inspect_ai/_cli/eval.py +15 -2
  3. inspect_ai/_display/core/active.py +4 -1
  4. inspect_ai/_display/core/config.py +3 -3
  5. inspect_ai/_display/core/panel.py +7 -3
  6. inspect_ai/_display/plain/__init__.py +0 -0
  7. inspect_ai/_display/plain/display.py +203 -0
  8. inspect_ai/_display/rich/display.py +0 -5
  9. inspect_ai/_display/textual/widgets/port_mappings.py +110 -0
  10. inspect_ai/_display/textual/widgets/samples.py +78 -11
  11. inspect_ai/_display/textual/widgets/sandbox.py +37 -0
  12. inspect_ai/_eval/score.py +1 -0
  13. inspect_ai/_eval/task/results.py +50 -22
  14. inspect_ai/_eval/task/run.py +41 -7
  15. inspect_ai/_eval/task/sandbox.py +10 -5
  16. inspect_ai/_util/constants.py +1 -0
  17. inspect_ai/_util/port_names.py +61 -0
  18. inspect_ai/_util/text.py +23 -0
  19. inspect_ai/_view/www/App.css +31 -1
  20. inspect_ai/_view/www/dist/assets/index.css +31 -1
  21. inspect_ai/_view/www/dist/assets/index.js +25344 -1849
  22. inspect_ai/_view/www/log-schema.json +32 -2
  23. inspect_ai/_view/www/package.json +2 -0
  24. inspect_ai/_view/www/src/App.mjs +8 -10
  25. inspect_ai/_view/www/src/Types.mjs +0 -1
  26. inspect_ai/_view/www/src/components/ChatView.mjs +133 -43
  27. inspect_ai/_view/www/src/components/ExpandablePanel.mjs +0 -4
  28. inspect_ai/_view/www/src/components/LargeModal.mjs +19 -20
  29. inspect_ai/_view/www/src/components/TabSet.mjs +3 -1
  30. inspect_ai/_view/www/src/components/VirtualList.mjs +266 -84
  31. inspect_ai/_view/www/src/index.js +75 -2
  32. inspect_ai/_view/www/src/navbar/Navbar.mjs +3 -0
  33. inspect_ai/_view/www/src/navbar/SecondaryBar.mjs +18 -9
  34. inspect_ai/_view/www/src/samples/SampleDialog.mjs +5 -1
  35. inspect_ai/_view/www/src/samples/SampleDisplay.mjs +23 -15
  36. inspect_ai/_view/www/src/samples/SampleList.mjs +18 -48
  37. inspect_ai/_view/www/src/samples/SampleTranscript.mjs +8 -3
  38. inspect_ai/_view/www/src/samples/SamplesDescriptor.mjs +24 -12
  39. inspect_ai/_view/www/src/samples/SamplesTab.mjs +4 -1
  40. inspect_ai/_view/www/src/samples/SamplesTools.mjs +8 -8
  41. inspect_ai/_view/www/src/samples/tools/SampleFilter.mjs +712 -89
  42. inspect_ai/_view/www/src/samples/tools/filters.mjs +260 -87
  43. inspect_ai/_view/www/src/samples/transcript/ErrorEventView.mjs +24 -2
  44. inspect_ai/_view/www/src/samples/transcript/EventPanel.mjs +29 -24
  45. inspect_ai/_view/www/src/samples/transcript/EventRow.mjs +1 -1
  46. inspect_ai/_view/www/src/samples/transcript/InfoEventView.mjs +24 -2
  47. inspect_ai/_view/www/src/samples/transcript/InputEventView.mjs +24 -2
  48. inspect_ai/_view/www/src/samples/transcript/ModelEventView.mjs +31 -10
  49. inspect_ai/_view/www/src/samples/transcript/SampleInitEventView.mjs +24 -2
  50. inspect_ai/_view/www/src/samples/transcript/SampleLimitEventView.mjs +23 -2
  51. inspect_ai/_view/www/src/samples/transcript/ScoreEventView.mjs +24 -2
  52. inspect_ai/_view/www/src/samples/transcript/StepEventView.mjs +33 -3
  53. inspect_ai/_view/www/src/samples/transcript/SubtaskEventView.mjs +25 -2
  54. inspect_ai/_view/www/src/samples/transcript/ToolEventView.mjs +25 -2
  55. inspect_ai/_view/www/src/samples/transcript/TranscriptView.mjs +193 -11
  56. inspect_ai/_view/www/src/samples/transcript/Types.mjs +10 -0
  57. inspect_ai/_view/www/src/samples/transcript/state/StateEventView.mjs +26 -2
  58. inspect_ai/_view/www/src/types/log.d.ts +13 -2
  59. inspect_ai/_view/www/src/utils/Format.mjs +10 -3
  60. inspect_ai/_view/www/src/utils/Json.mjs +12 -6
  61. inspect_ai/_view/www/src/workspace/WorkSpace.mjs +10 -4
  62. inspect_ai/_view/www/vite.config.js +7 -0
  63. inspect_ai/_view/www/yarn.lock +116 -0
  64. inspect_ai/approval/_human/__init__.py +0 -0
  65. inspect_ai/approval/_policy.py +12 -6
  66. inspect_ai/log/_log.py +1 -1
  67. inspect_ai/log/_samples.py +16 -0
  68. inspect_ai/log/_transcript.py +4 -1
  69. inspect_ai/model/_call_tools.py +4 -0
  70. inspect_ai/model/_conversation.py +20 -8
  71. inspect_ai/model/_generate_config.py +10 -4
  72. inspect_ai/model/_model.py +117 -18
  73. inspect_ai/model/_model_output.py +7 -2
  74. inspect_ai/model/_providers/anthropic.py +100 -44
  75. inspect_ai/model/_providers/azureai.py +20 -20
  76. inspect_ai/model/_providers/bedrock.py +37 -40
  77. inspect_ai/model/_providers/google.py +46 -54
  78. inspect_ai/model/_providers/mistral.py +11 -11
  79. inspect_ai/model/_providers/openai.py +15 -16
  80. inspect_ai/model/_providers/openai_o1.py +9 -8
  81. inspect_ai/model/_providers/providers.py +1 -1
  82. inspect_ai/model/_providers/together.py +8 -8
  83. inspect_ai/model/_providers/vertex.py +1 -4
  84. inspect_ai/scorer/_reducer/reducer.py +1 -1
  85. inspect_ai/scorer/_scorer.py +2 -2
  86. inspect_ai/solver/__init__.py +2 -5
  87. inspect_ai/solver/_prompt.py +35 -5
  88. inspect_ai/solver/_task_state.py +80 -38
  89. inspect_ai/tool/__init__.py +2 -0
  90. inspect_ai/tool/_tool.py +12 -1
  91. inspect_ai/tool/_tool_call.py +10 -0
  92. inspect_ai/tool/_tool_def.py +16 -5
  93. inspect_ai/tool/_tool_with.py +21 -4
  94. inspect_ai/tool/beta/__init__.py +5 -0
  95. inspect_ai/tool/beta/_computer/__init__.py +3 -0
  96. inspect_ai/tool/beta/_computer/_common.py +133 -0
  97. inspect_ai/tool/beta/_computer/_computer.py +155 -0
  98. inspect_ai/tool/beta/_computer/_computer_split.py +198 -0
  99. inspect_ai/tool/beta/_computer/_resources/Dockerfile +100 -0
  100. inspect_ai/tool/beta/_computer/_resources/README.md +30 -0
  101. inspect_ai/tool/beta/_computer/_resources/entrypoint/entrypoint.sh +18 -0
  102. inspect_ai/tool/beta/_computer/_resources/entrypoint/novnc_startup.sh +20 -0
  103. inspect_ai/tool/beta/_computer/_resources/entrypoint/x11vnc_startup.sh +48 -0
  104. inspect_ai/tool/beta/_computer/_resources/entrypoint/xfce_startup.sh +13 -0
  105. inspect_ai/tool/beta/_computer/_resources/entrypoint/xvfb_startup.sh +48 -0
  106. inspect_ai/tool/beta/_computer/_resources/image_home_dir/Desktop/Firefox Web Browser.desktop +10 -0
  107. inspect_ai/tool/beta/_computer/_resources/image_home_dir/Desktop/Visual Studio Code.desktop +10 -0
  108. inspect_ai/tool/beta/_computer/_resources/image_home_dir/Desktop/XPaint.desktop +10 -0
  109. inspect_ai/tool/beta/_computer/_resources/tool/__init__.py +0 -0
  110. inspect_ai/tool/beta/_computer/_resources/tool/_logger.py +22 -0
  111. inspect_ai/tool/beta/_computer/_resources/tool/_run.py +42 -0
  112. inspect_ai/tool/beta/_computer/_resources/tool/_tool_result.py +33 -0
  113. inspect_ai/tool/beta/_computer/_resources/tool/_x11_client.py +262 -0
  114. inspect_ai/tool/beta/_computer/_resources/tool/computer_tool.py +85 -0
  115. inspect_ai/tool/beta/_computer/_resources/tool/requirements.txt +0 -0
  116. inspect_ai/util/__init__.py +2 -0
  117. inspect_ai/util/_limit.py +26 -0
  118. inspect_ai/util/_sandbox/docker/docker.py +64 -1
  119. inspect_ai/util/_sandbox/docker/internal.py +3 -1
  120. inspect_ai/util/_sandbox/environment.py +14 -0
  121. {inspect_ai-0.3.58.dist-info → inspect_ai-0.3.59.dist-info}/METADATA +2 -2
  122. {inspect_ai-0.3.58.dist-info → inspect_ai-0.3.59.dist-info}/RECORD +126 -98
  123. inspect_ai/_view/www/src/samples/transcript/TranscriptState.mjs +0 -70
  124. {inspect_ai-0.3.58.dist-info → inspect_ai-0.3.59.dist-info}/LICENSE +0 -0
  125. {inspect_ai-0.3.58.dist-info → inspect_ai-0.3.59.dist-info}/WHEEL +0 -0
  126. {inspect_ai-0.3.58.dist-info → inspect_ai-0.3.59.dist-info}/entry_points.txt +0 -0
  127. {inspect_ai-0.3.58.dist-info → inspect_ai-0.3.59.dist-info}/top_level.txt +0 -0
@@ -1137,6 +1137,7 @@
1137
1137
  "logprobs": null,
1138
1138
  "top_logprobs": null,
1139
1139
  "parallel_tool_calls": null,
1140
+ "internal_tools": null,
1140
1141
  "max_tool_output": null,
1141
1142
  "cache_prompt": null,
1142
1143
  "reasoning_effort": null
@@ -1516,7 +1517,8 @@
1516
1517
  "time",
1517
1518
  "message",
1518
1519
  "token",
1519
- "operator"
1520
+ "operator",
1521
+ "custom"
1520
1522
  ],
1521
1523
  "title": "Type",
1522
1524
  "type": "string"
@@ -2190,6 +2192,18 @@
2190
2192
  "default": null,
2191
2193
  "title": "Parallel Tool Calls"
2192
2194
  },
2195
+ "internal_tools": {
2196
+ "anyOf": [
2197
+ {
2198
+ "type": "boolean"
2199
+ },
2200
+ {
2201
+ "type": "null"
2202
+ }
2203
+ ],
2204
+ "default": null,
2205
+ "title": "Internal Tools"
2206
+ },
2193
2207
  "max_tool_output": {
2194
2208
  "anyOf": [
2195
2209
  {
@@ -2258,6 +2272,7 @@
2258
2272
  "logprobs",
2259
2273
  "top_logprobs",
2260
2274
  "parallel_tool_calls",
2275
+ "internal_tools",
2261
2276
  "max_tool_output",
2262
2277
  "cache_prompt",
2263
2278
  "reasoning_effort"
@@ -2681,6 +2696,18 @@
2681
2696
  "output": {
2682
2697
  "$ref": "#/$defs/ModelOutput"
2683
2698
  },
2699
+ "error": {
2700
+ "anyOf": [
2701
+ {
2702
+ "type": "string"
2703
+ },
2704
+ {
2705
+ "type": "null"
2706
+ }
2707
+ ],
2708
+ "default": null,
2709
+ "title": "Error"
2710
+ },
2684
2711
  "cache": {
2685
2712
  "anyOf": [
2686
2713
  {
@@ -2719,6 +2746,7 @@
2719
2746
  "tool_choice",
2720
2747
  "config",
2721
2748
  "output",
2749
+ "error",
2722
2750
  "cache",
2723
2751
  "call"
2724
2752
  ],
@@ -3066,7 +3094,8 @@
3066
3094
  "message",
3067
3095
  "time",
3068
3096
  "token",
3069
- "operator"
3097
+ "operator",
3098
+ "custom"
3070
3099
  ],
3071
3100
  "title": "Type",
3072
3101
  "type": "string"
@@ -4207,6 +4236,7 @@
4207
4236
  "best_of": null,
4208
4237
  "cache_prompt": null,
4209
4238
  "frequency_penalty": null,
4239
+ "internal_tools": null,
4210
4240
  "logit_bias": null,
4211
4241
  "logprobs": null,
4212
4242
  "max_connections": null,
@@ -30,8 +30,10 @@
30
30
  "bootstrap": "^5.3.3",
31
31
  "bootstrap-icons": "^1.11.3",
32
32
  "clipboard": "^2.0.11",
33
+ "codemirror": "^6.0.1",
33
34
  "fast-json-patch": "^3.1.1",
34
35
  "fflate": "^0.8.2",
36
+ "filtrex": "^3.1.0",
35
37
  "htm": "^3.1.1",
36
38
  "json": "^11.0.0",
37
39
  "json5": "^2.2.3",
@@ -38,7 +38,7 @@ import {
38
38
  } from "./samples/SamplesDescriptor.mjs";
39
39
  import { byEpoch, bySample, sortSamples } from "./samples/tools/SortFilter.mjs";
40
40
  import { resolveAttachments } from "./utils/attachments.mjs";
41
- import { filterFnForType } from "./samples/tools/filters.mjs";
41
+ import { filterSamples } from "./samples/tools/filters.mjs";
42
42
 
43
43
  import {
44
44
  kEvalWorkspaceTabId,
@@ -308,21 +308,19 @@ export function App({
308
308
 
309
309
  useEffect(() => {
310
310
  const samples = selectedLog?.contents?.sampleSummaries || [];
311
- const filtered = samples.filter((sample) => {
311
+ const { result: prefiltered } = filterSamples(
312
+ evalDescriptor,
313
+ samples,
314
+ filter?.value,
315
+ );
316
+ const filtered = prefiltered.filter((sample) => {
312
317
  // Filter by epoch if specified
313
318
  if (epoch && epoch !== "all") {
314
319
  if (epoch !== sample.epoch + "") {
315
320
  return false;
316
321
  }
317
322
  }
318
-
319
- // Apply the filter
320
- const filterFn = filterFnForType(filter);
321
- if (filterFn && filter.value) {
322
- return filterFn(samplesDescriptor, sample, filter.value);
323
- } else {
324
- return true;
325
- }
323
+ return true;
326
324
  });
327
325
 
328
326
  // Sort the samples
@@ -25,7 +25,6 @@
25
25
  /**
26
26
  * @typedef {Object} ScoreFilter
27
27
  * @property {string} [value]
28
- * @property {string} [type]
29
28
  */
30
29
 
31
30
  /**
@@ -7,6 +7,54 @@ import { MessageContent } from "./MessageContent.mjs";
7
7
  import { ExpandablePanel } from "./ExpandablePanel.mjs";
8
8
  import { FontSize, TextStyle } from "../appearance/Fonts.mjs";
9
9
  import { resolveToolInput, ToolCallView } from "./Tools.mjs";
10
+ import { VirtualList } from "./VirtualList.mjs";
11
+
12
+ /**
13
+ * Renders the ChatViewVirtualList component.
14
+ *
15
+ * @param {Object} props - The properties passed to the component.
16
+ * @param {string} props.id - The ID for the chat view.
17
+ * @param {import("../types/log").Messages} props.messages - The array of chat messages.
18
+ * @param {"compact" | "complete"} [props.toolCallStyle] - Whether to show tool calls
19
+ * @param {Object} [props.style] - Inline styles for the chat view.
20
+ * @param {boolean} props.indented - Whether the chatview has indented messages
21
+ * @param {boolean} [props.numbered] - Whether the chatview is numbered
22
+ * @param {import("htm/preact").MutableRef<HTMLElement>} props.scrollRef - The scrollable parent element
23
+ * @returns {import("preact").JSX.Element} The component.
24
+ */
25
+ export const ChatViewVirtualList = ({
26
+ id,
27
+ messages,
28
+ toolCallStyle,
29
+ style,
30
+ indented,
31
+ numbered = true,
32
+ scrollRef,
33
+ }) => {
34
+ const collapsedMessages = resolveMessages(messages);
35
+
36
+ const renderRow = (item, index) => {
37
+ const number =
38
+ collapsedMessages.length > 1 && numbered ? index + 1 : undefined;
39
+ return html`<${ChatMessageRow}
40
+ id=${id}
41
+ number=${number}
42
+ resolvedMessage=${item}
43
+ indented=${indented}
44
+ toolCallStyle=${toolCallStyle}
45
+ />`;
46
+ };
47
+
48
+ const result = html`<${VirtualList}
49
+ data=${collapsedMessages}
50
+ tabIndex="0"
51
+ renderRow=${renderRow}
52
+ scrollRef=${scrollRef}
53
+ style=${{ width: "100%", marginTop: "1em", ...style }}
54
+ />`;
55
+
56
+ return result;
57
+ };
10
58
 
11
59
  /**
12
60
  * Renders the ChatView component.
@@ -28,6 +76,90 @@ export const ChatView = ({
28
76
  indented,
29
77
  numbered = true,
30
78
  }) => {
79
+ const collapsedMessages = resolveMessages(messages);
80
+ const result = html` <div style=${style}>
81
+ ${collapsedMessages.map((msg, index) => {
82
+ const number =
83
+ collapsedMessages.length > 1 && numbered ? index + 1 : undefined;
84
+ return html`<${ChatMessageRow}
85
+ id=${id}
86
+ number=${number}
87
+ resolvedMessage=${msg}
88
+ indented=${indented}
89
+ toolCallStyle=${toolCallStyle}
90
+ />`;
91
+ })}
92
+ </div>`;
93
+ return result;
94
+ };
95
+
96
+ /**
97
+ * Renders the ChatMessage component.
98
+ *
99
+ * @param {Object} props - The properties passed to the component.
100
+ * @param {string} props.id - The ID for the chat view.
101
+ * @param {number} [props.number] - The message number
102
+ * @param {ResolvedMessage} props.resolvedMessage - The array of chat messages.
103
+ * @param {"compact" | "complete"} [props.toolCallStyle] - Whether to show tool calls
104
+ * @param {boolean} props.indented - Whether the chatview has indented messages
105
+ * @returns {import("preact").JSX.Element} The component.
106
+ */
107
+ export const ChatMessageRow = ({
108
+ id,
109
+ number,
110
+ resolvedMessage,
111
+ toolCallStyle,
112
+ indented,
113
+ }) => {
114
+ if (number) {
115
+ return html` <div
116
+ style=${{
117
+ display: "grid",
118
+ gridTemplateColumns: "max-content auto",
119
+ columnGap: "0.4em",
120
+ }}
121
+ >
122
+ <div
123
+ style=${{
124
+ fontSize: FontSize.smaller,
125
+ ...TextStyle.secondary,
126
+ marginTop: "0.1em",
127
+ }}
128
+ >
129
+ ${number}
130
+ </div>
131
+ <${ChatMessage}
132
+ id=${`${id}-chat-messages`}
133
+ message=${resolvedMessage.message}
134
+ toolMessages=${resolvedMessage.toolMessages}
135
+ indented=${indented}
136
+ toolCallStyle=${toolCallStyle}
137
+ />
138
+ </div>`;
139
+ } else {
140
+ return html`<${ChatMessage}
141
+ id=${`${id}-chat-messages`}
142
+ message=${resolvedMessage.message}
143
+ toolMessages=${resolvedMessage.toolMessages}
144
+ indented=${indented}
145
+ toolCallStyle=${toolCallStyle}
146
+ />`;
147
+ }
148
+ };
149
+
150
+ /**
151
+ * @typedef {Object} ResolvedMessage
152
+ * @property {import("../types/log").ChatMessageAssistant | import("../types/log").ChatMessageSystem | import("../types/log").ChatMessageUser} message - The main chat message.
153
+ * @property {import("../types/log").ChatMessageTool[]} [toolMessages] - Optional array of tool-related messages.
154
+ */
155
+
156
+ /**
157
+ * Renders the ChatView component.
158
+ *
159
+ * @param {import("../types/log").Messages} messages - The array of chat messages.
160
+ * @returns {ResolvedMessage[]} The component.
161
+ */
162
+ export const resolveMessages = (messages) => {
31
163
  // Filter tool messages into a sidelist that the chat stream
32
164
  // can use to lookup the tool responses
33
165
 
@@ -88,49 +220,7 @@ export const ChatView = ({
88
220
  if (systemMessage && systemMessage.content.length > 0) {
89
221
  collapsedMessages.unshift({ message: systemMessage });
90
222
  }
91
-
92
- const result = html`
93
- <div style=${style}>
94
- ${collapsedMessages.map((msg, index) => {
95
- if (collapsedMessages.length > 1 && numbered) {
96
- return html` <div
97
- style=${{
98
- display: "grid",
99
- gridTemplateColumns: "max-content auto",
100
- columnGap: "0.4em",
101
- }}
102
- >
103
- <div
104
- style=${{
105
- fontSize: FontSize.smaller,
106
- ...TextStyle.secondary,
107
- marginTop: "0.1em",
108
- }}
109
- >
110
- ${index + 1}
111
- </div>
112
- <${ChatMessage}
113
- id=${`${id}-chat-messages`}
114
- message=${msg.message}
115
- toolMessages=${msg.toolMessages}
116
- indented=${indented}
117
- toolCallStyle=${toolCallStyle}
118
- />
119
- </div>`;
120
- } else {
121
- return html` <${ChatMessage}
122
- id=${`${id}-chat-messages`}
123
- message=${msg.message}
124
- toolMessages=${msg.toolMessages}
125
- indented=${indented}
126
- toolCallStyle=${toolCallStyle}
127
- />`;
128
- }
129
- })}
130
- </div>
131
- `;
132
-
133
- return result;
223
+ return collapsedMessages;
134
224
  };
135
225
 
136
226
  /**
@@ -70,10 +70,6 @@ export const ExpandablePanel = ({
70
70
  contentsStyle.border = "solid var(--bs-light-border-subtle) 1px";
71
71
  }
72
72
 
73
- if (!showToggle) {
74
- contentsStyle.marginBottom = "1em";
75
- }
76
-
77
73
  return html`<div
78
74
  class="expandable-panel"
79
75
  ref=${contentsRef}
@@ -5,25 +5,24 @@ import { FontSize } from "../appearance/Fonts.mjs";
5
5
  import { ProgressBar } from "./ProgressBar.mjs";
6
6
  import { MessageBand } from "./MessageBand.mjs";
7
7
 
8
- export const LargeModal = (props) => {
9
- const {
10
- id,
11
- title,
12
- detail,
13
- detailTools,
14
- footer,
15
- onkeyup,
16
- visible,
17
- onHide,
18
- showProgress,
19
- children,
20
- initialScrollPositionRef,
21
- setInitialScrollPosition,
22
- warning,
23
- warningHidden,
24
- setWarningHidden,
25
- } = props;
26
-
8
+ export const LargeModal = ({
9
+ id,
10
+ title,
11
+ detail,
12
+ detailTools,
13
+ footer,
14
+ onkeyup,
15
+ visible,
16
+ onHide,
17
+ showProgress,
18
+ children,
19
+ initialScrollPositionRef,
20
+ setInitialScrollPosition,
21
+ warning,
22
+ warningHidden,
23
+ setWarningHidden,
24
+ scrollRef,
25
+ }) => {
27
26
  // The footer
28
27
  const modalFooter = footer
29
28
  ? html`<div class="modal-footer">${footer}</div>`
@@ -31,7 +30,7 @@ export const LargeModal = (props) => {
31
30
 
32
31
  // Support restoring the scroll position
33
32
  // but only do this for the first time that the children are set
34
- const scrollRef = useRef(/** @type {HTMLElement|null} */ (null));
33
+ scrollRef = scrollRef || useRef(/** @type {HTMLElement|null} */ (null));
35
34
  useEffect(() => {
36
35
  if (scrollRef.current) {
37
36
  setTimeout(() => {
@@ -38,13 +38,15 @@ export const TabPanel = ({
38
38
  selected,
39
39
  style,
40
40
  scrollable,
41
+ scrollRef,
41
42
  classes,
42
43
  scrollPosition,
43
44
  setScrollPosition,
44
45
  children,
45
46
  }) => {
46
47
  const tabContentsId = computeTabContentsId(id, index);
47
- const tabContentsRef = useRef(/** @type {HTMLElement|null} */ (null));
48
+ const tabContentsRef =
49
+ scrollRef || useRef(/** @type {HTMLElement|null} */ (null));
48
50
  useEffect(() => {
49
51
  setTimeout(() => {
50
52
  if (