@agent-native/core 0.91.0 → 0.91.2

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 (219) hide show
  1. package/blueprints/action/crud.md +1 -1
  2. package/blueprints/channel/discord.md +1 -1
  3. package/blueprints/provider/stripe.md +1 -1
  4. package/blueprints/sandbox/docker.md +1 -1
  5. package/corpus/README.md +1 -1
  6. package/corpus/core/CHANGELOG.md +26 -0
  7. package/corpus/core/blueprints/action/crud.md +1 -1
  8. package/corpus/core/blueprints/channel/discord.md +1 -1
  9. package/corpus/core/blueprints/provider/stripe.md +1 -1
  10. package/corpus/core/blueprints/sandbox/docker.md +1 -1
  11. package/corpus/core/package.json +6 -5
  12. package/corpus/core/src/agent/run-manager.ts +23 -2
  13. package/corpus/core/src/agent/run-store.ts +2 -2
  14. package/corpus/core/src/cli/add.ts +1 -1
  15. package/corpus/core/src/cli/create.ts +8 -3
  16. package/corpus/core/src/cli/design-connect.ts +53 -9
  17. package/corpus/core/src/cli/index.ts +5 -4
  18. package/corpus/core/src/client/extensions/ExtensionSlot.tsx +7 -0
  19. package/corpus/core/src/client/session-replay.ts +97 -3
  20. package/corpus/core/src/jobs/scheduler.ts +30 -1
  21. package/corpus/core/src/localization/default-messages.ts +21 -0
  22. package/corpus/core/src/templates/default/app/i18n/ar-SA.ts +21 -0
  23. package/corpus/core/src/templates/default/app/i18n/de-DE.ts +21 -0
  24. package/corpus/core/src/templates/default/app/i18n/en-US.ts +21 -0
  25. package/corpus/core/src/templates/default/app/i18n/es-ES.ts +21 -0
  26. package/corpus/core/src/templates/default/app/i18n/fr-FR.ts +21 -0
  27. package/corpus/core/src/templates/default/app/i18n/hi-IN.ts +20 -0
  28. package/corpus/core/src/templates/default/app/i18n/ja-JP.ts +21 -0
  29. package/corpus/core/src/templates/default/app/i18n/ko-KR.ts +20 -0
  30. package/corpus/core/src/templates/default/app/i18n/pt-BR.ts +21 -0
  31. package/corpus/core/src/templates/default/app/i18n/zh-CN.ts +20 -0
  32. package/corpus/core/src/templates/default/app/i18n/zh-TW.ts +19 -0
  33. package/corpus/core/src/templates/default/package.json +2 -2
  34. package/corpus/core/src/templates/headless/package.json +3 -3
  35. package/corpus/core/src/templates/workspace-core/package.json +6 -6
  36. package/corpus/core/src/templates/workspace-root/package.json +2 -2
  37. package/corpus/core/src/triggers/dispatcher.ts +30 -2
  38. package/corpus/core/src/vite/client.ts +96 -1
  39. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +21 -2
  40. package/corpus/templates/analytics/app/global.css +26 -10
  41. package/corpus/templates/analytics/app/i18n/zh-TW.ts +2 -0
  42. package/corpus/templates/analytics/app/i18n-data.ts +25 -0
  43. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +548 -237
  44. package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +285 -294
  45. package/corpus/templates/analytics/changelog/2026-07-08-console-details-expand-inline-under-each-row-and-the-event-t.md +6 -0
  46. package/corpus/templates/analytics/changelog/2026-07-08-session-replay-clamps-ultra-wide-viewports-while-keeping-the.md +6 -0
  47. package/corpus/templates/analytics/changelog/2026-07-08-session-replay-keeps-a-usable-stage-when-dev-tools-is-open-a.md +6 -0
  48. package/corpus/templates/analytics/changelog/2026-07-08-session-replay-resumes-after-scrubbing-and-expands-console-r.md +6 -0
  49. package/corpus/templates/analytics/changelog/2026-07-08-session-replays-wait-for-full-load-keep-a-normal-viewport-an.md +6 -0
  50. package/corpus/templates/analytics/package.json +2 -2
  51. package/corpus/templates/assets/package.json +2 -2
  52. package/corpus/templates/brain/package.json +2 -2
  53. package/corpus/templates/calendar/AGENTS.md +10 -0
  54. package/corpus/templates/calendar/actions/create-event.ts +5 -1
  55. package/corpus/templates/calendar/actions/event-action-helpers.ts +41 -0
  56. package/corpus/templates/calendar/actions/get-attendee-timezones.ts +28 -0
  57. package/corpus/templates/calendar/actions/set-attendee-timezone.ts +68 -0
  58. package/corpus/templates/calendar/app/components/calendar/EventAttendeesSection.tsx +150 -11
  59. package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +36 -1
  60. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +32 -0
  61. package/corpus/templates/calendar/app/hooks/use-attendee-timezones.ts +24 -0
  62. package/corpus/templates/calendar/app/i18n/zh-TW.ts +2 -0
  63. package/corpus/templates/calendar/app/i18n-data.ts +20 -0
  64. package/corpus/templates/calendar/app/lib/attendee-local-time.ts +122 -0
  65. package/corpus/templates/calendar/changelog/2026-07-08-event-details-now-support-extension-widgets-and-you-can-see-.md +6 -0
  66. package/corpus/templates/calendar/changelog/2026-07-08-when-you-invite-guests-to-a-new-event-you-now-appear-in.md +6 -0
  67. package/corpus/templates/calendar/package.json +2 -2
  68. package/corpus/templates/calendar/server/handlers/events.ts +5 -1
  69. package/corpus/templates/calendar/shared/api.ts +6 -0
  70. package/corpus/templates/chat/package.json +2 -2
  71. package/corpus/templates/clips/.agents/skills/meetings/SKILL.md +6 -2
  72. package/corpus/templates/clips/actions/create-meeting.ts +8 -3
  73. package/corpus/templates/clips/actions/list-meetings.ts +31 -3
  74. package/corpus/templates/clips/changelog/2026-07-08-after-finishing-a-desktop-recording-reopening-clips-restor.md +6 -0
  75. package/corpus/templates/clips/changelog/2026-07-08-clips-now-pops-up-to-take-notes-when-you-join-an-adhoc-zoom-.md +6 -0
  76. package/corpus/templates/clips/changelog/2026-07-08-desktop-clip-recordings-are-louder-after-capture-mic-only.md +6 -0
  77. package/corpus/templates/clips/changelog/2026-07-08-meeting-reminders-now-appear-1-minute-before-start-stay-unti.md +6 -0
  78. package/corpus/templates/clips/changelog/2026-07-08-the-desktop-meetings-panel-has-room-for-its-drop-shadow-and-.md +6 -0
  79. package/corpus/templates/clips/chrome-extension/package.json +2 -2
  80. package/corpus/templates/clips/desktop/package.json +2 -2
  81. package/corpus/templates/clips/desktop/src/app.tsx +91 -34
  82. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +15 -5
  83. package/corpus/templates/clips/desktop/src/lib/meeting-notification-timing.ts +83 -0
  84. package/corpus/templates/clips/desktop/src/lib/native-recording-warm.ts +21 -0
  85. package/corpus/templates/clips/desktop/src/lib/recorder.ts +98 -20
  86. package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +174 -50
  87. package/corpus/templates/clips/desktop/src/styles.css +128 -53
  88. package/corpus/templates/clips/desktop/src-tauri/src/adhoc_meetings_watcher.rs +364 -0
  89. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +1 -1
  90. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +5 -0
  91. package/corpus/templates/clips/desktop/src-tauri/src/meetings_watcher.rs +91 -11
  92. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +69 -19
  93. package/corpus/templates/clips/desktop/src-tauri/src/native_speech.rs +36 -1
  94. package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +62 -10
  95. package/corpus/templates/clips/desktop/src-tauri/src/silence_detector.rs +1 -24
  96. package/corpus/templates/clips/desktop/src-tauri/src/util.rs +36 -1
  97. package/corpus/templates/clips/package.json +2 -2
  98. package/corpus/templates/content/actions/_content-database-personal-view.ts +80 -0
  99. package/corpus/templates/content/actions/get-content-database-personal-view.ts +27 -0
  100. package/corpus/templates/content/actions/update-content-database-personal-view.ts +31 -0
  101. package/corpus/templates/content/actions/update-content-database-view.ts +3 -0
  102. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +1 -1
  103. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +2584 -546
  104. package/corpus/templates/content/app/components/editor/database/filter-sort.ts +220 -18
  105. package/corpus/templates/content/app/hooks/use-content-database.ts +32 -0
  106. package/corpus/templates/content/app/i18n/zh-TW.ts +16 -0
  107. package/corpus/templates/content/app/i18n-data.ts +154 -0
  108. package/corpus/templates/content/changelog/2026-07-08-database-filters-now-support-notion-style-reset-save-for-eve.md +6 -0
  109. package/corpus/templates/content/changelog/2026-07-08-date-filters-can-now-target-a-between-range.md +6 -0
  110. package/corpus/templates/content/package.json +2 -2
  111. package/corpus/templates/content/parity/matrix.md +1 -1
  112. package/corpus/templates/content/parity/matrix.ts +2 -0
  113. package/corpus/templates/content/shared/api.ts +24 -0
  114. package/corpus/templates/design/AGENTS.md +9 -0
  115. package/corpus/templates/design/actions/request-localhost-write-consent.ts +141 -0
  116. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +13 -19
  117. package/corpus/templates/design/package.json +2 -2
  118. package/corpus/templates/design/server/lib/verify-write-grant.ts +4 -2
  119. package/corpus/templates/dispatch/app/i18n/zh-TW.ts +34 -1
  120. package/corpus/templates/dispatch/app/i18n-data.ts +387 -10
  121. package/corpus/templates/dispatch/app/routes/automations.tsx +1 -0
  122. package/corpus/templates/dispatch/changelog/2026-07-08-added-an-automations-page-to-review-pause-and-create.md +6 -0
  123. package/corpus/templates/dispatch/changelog/2026-07-08-destinations-now-shows-outbound-delivery-queue-health.md +6 -0
  124. package/corpus/templates/dispatch/package.json +2 -2
  125. package/corpus/templates/forms/package.json +2 -2
  126. package/corpus/templates/macros/package.json +2 -2
  127. package/corpus/templates/mail/app/hooks/use-emails.ts +56 -44
  128. package/corpus/templates/mail/app/lib/gmail-mutation-queue.ts +325 -0
  129. package/corpus/templates/mail/changelog/2026-07-08-rapid-archive-and-mark-read-now-batch-gmail-updates-so-the-i.md +6 -0
  130. package/corpus/templates/mail/package.json +2 -2
  131. package/corpus/templates/plan/package.json +2 -2
  132. package/corpus/templates/slides/app/components/editor/ImageDropPromptPopover.tsx +46 -29
  133. package/corpus/templates/slides/app/context/DeckContext.tsx +45 -18
  134. package/corpus/templates/slides/app/i18n/ar-SA.ts +4 -0
  135. package/corpus/templates/slides/app/i18n/de-DE.ts +4 -0
  136. package/corpus/templates/slides/app/i18n/en-US.ts +4 -0
  137. package/corpus/templates/slides/app/i18n/es-ES.ts +4 -0
  138. package/corpus/templates/slides/app/i18n/fr-FR.ts +4 -0
  139. package/corpus/templates/slides/app/i18n/hi-IN.ts +4 -0
  140. package/corpus/templates/slides/app/i18n/ja-JP.ts +4 -0
  141. package/corpus/templates/slides/app/i18n/ko-KR.ts +4 -0
  142. package/corpus/templates/slides/app/i18n/pt-BR.ts +4 -0
  143. package/corpus/templates/slides/app/i18n/zh-CN.ts +4 -0
  144. package/corpus/templates/slides/app/i18n/zh-TW.ts +4 -0
  145. package/corpus/templates/slides/app/lib/image-drop-to-agent.ts +115 -0
  146. package/corpus/templates/slides/app/pages/DeckEditor.tsx +22 -13
  147. package/corpus/templates/slides/changelog/2026-07-08-dropping-an-image-onto-a-slide-and-sending-it-to-the-agent-w.md +6 -0
  148. package/corpus/templates/slides/changelog/2026-07-08-undo-works-after-the-agent-edits-a-slide-so-you-can-reverse.md +6 -0
  149. package/corpus/templates/slides/package.json +2 -2
  150. package/dist/agent/run-manager.d.ts.map +1 -1
  151. package/dist/agent/run-manager.js +23 -2
  152. package/dist/agent/run-manager.js.map +1 -1
  153. package/dist/agent/run-store.js +2 -2
  154. package/dist/agent/run-store.js.map +1 -1
  155. package/dist/catalog.json +2 -2
  156. package/dist/cli/add.js +1 -1
  157. package/dist/cli/add.js.map +1 -1
  158. package/dist/cli/create.d.ts.map +1 -1
  159. package/dist/cli/create.js +8 -3
  160. package/dist/cli/create.js.map +1 -1
  161. package/dist/cli/design-connect.d.ts.map +1 -1
  162. package/dist/cli/design-connect.js +40 -10
  163. package/dist/cli/design-connect.js.map +1 -1
  164. package/dist/cli/index.js +5 -4
  165. package/dist/cli/index.js.map +1 -1
  166. package/dist/client/extensions/ExtensionSlot.js +6 -0
  167. package/dist/client/extensions/ExtensionSlot.js.map +1 -1
  168. package/dist/client/session-replay.d.ts +8 -0
  169. package/dist/client/session-replay.d.ts.map +1 -1
  170. package/dist/client/session-replay.js +80 -3
  171. package/dist/client/session-replay.js.map +1 -1
  172. package/dist/collab/routes.d.ts +1 -1
  173. package/dist/jobs/scheduler.js +26 -1
  174. package/dist/jobs/scheduler.js.map +1 -1
  175. package/dist/localization/default-messages.d.ts +19 -0
  176. package/dist/localization/default-messages.d.ts.map +1 -1
  177. package/dist/localization/default-messages.js +19 -0
  178. package/dist/localization/default-messages.js.map +1 -1
  179. package/dist/notifications/routes.d.ts +1 -1
  180. package/dist/observability/routes.d.ts +1 -1
  181. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  182. package/dist/resources/handlers.d.ts +1 -1
  183. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  184. package/dist/templates/default/app/i18n/ar-SA.ts +21 -0
  185. package/dist/templates/default/app/i18n/de-DE.ts +21 -0
  186. package/dist/templates/default/app/i18n/en-US.ts +21 -0
  187. package/dist/templates/default/app/i18n/es-ES.ts +21 -0
  188. package/dist/templates/default/app/i18n/fr-FR.ts +21 -0
  189. package/dist/templates/default/app/i18n/hi-IN.ts +20 -0
  190. package/dist/templates/default/app/i18n/ja-JP.ts +21 -0
  191. package/dist/templates/default/app/i18n/ko-KR.ts +20 -0
  192. package/dist/templates/default/app/i18n/pt-BR.ts +21 -0
  193. package/dist/templates/default/app/i18n/zh-CN.ts +20 -0
  194. package/dist/templates/default/app/i18n/zh-TW.ts +19 -0
  195. package/dist/templates/default/package.json +2 -2
  196. package/dist/templates/headless/package.json +3 -3
  197. package/dist/templates/workspace-core/package.json +6 -6
  198. package/dist/templates/workspace-root/package.json +2 -2
  199. package/dist/triggers/dispatcher.js +27 -2
  200. package/dist/triggers/dispatcher.js.map +1 -1
  201. package/dist/vite/client.d.ts.map +1 -1
  202. package/dist/vite/client.js +70 -1
  203. package/dist/vite/client.js.map +1 -1
  204. package/package.json +7 -6
  205. package/src/templates/default/app/i18n/ar-SA.ts +21 -0
  206. package/src/templates/default/app/i18n/de-DE.ts +21 -0
  207. package/src/templates/default/app/i18n/en-US.ts +21 -0
  208. package/src/templates/default/app/i18n/es-ES.ts +21 -0
  209. package/src/templates/default/app/i18n/fr-FR.ts +21 -0
  210. package/src/templates/default/app/i18n/hi-IN.ts +20 -0
  211. package/src/templates/default/app/i18n/ja-JP.ts +21 -0
  212. package/src/templates/default/app/i18n/ko-KR.ts +20 -0
  213. package/src/templates/default/app/i18n/pt-BR.ts +21 -0
  214. package/src/templates/default/app/i18n/zh-CN.ts +20 -0
  215. package/src/templates/default/app/i18n/zh-TW.ts +19 -0
  216. package/src/templates/default/package.json +2 -2
  217. package/src/templates/headless/package.json +3 -3
  218. package/src/templates/workspace-core/package.json +6 -6
  219. package/src/templates/workspace-root/package.json +2 -2
@@ -52,14 +52,37 @@ function issueDetailPath(issueId: string): string {
52
52
  /** Pause row auto-follow for a while after the user scrolls the list. */
53
53
  const MANUAL_SCROLL_FOLLOW_PAUSE_MS = 4000;
54
54
  const DEVTOOLS_ROW_HEIGHT = 34;
55
+ const DEVTOOLS_EXPANDED_ESTIMATE = 220;
55
56
  const DEVTOOLS_OVERSCAN_ROWS = 10;
56
57
  const DEVTOOLS_MIN_HEIGHT = 180;
57
58
  const DEVTOOLS_MAX_HEIGHT = 620;
58
59
 
60
+ /**
61
+ * Layout offsets for the virtualized Dev Tools list. Expanded rows reserve
62
+ * extra height so details render inline under the selected line without
63
+ * disabling virtualization for the rest of the list.
64
+ */
65
+ export function buildDevToolsRowOffsets(
66
+ entryCount: number,
67
+ expandedIndex: number,
68
+ ): number[] {
69
+ const offsets = new Array<number>(entryCount + 1);
70
+ offsets[0] = 0;
71
+ for (let index = 0; index < entryCount; index += 1) {
72
+ const height =
73
+ index === expandedIndex
74
+ ? DEVTOOLS_EXPANDED_ESTIMATE
75
+ : DEVTOOLS_ROW_HEIGHT;
76
+ offsets[index + 1] = offsets[index] + height;
77
+ }
78
+ return offsets;
79
+ }
80
+
59
81
  export function SessionDevToolsPanel({
60
82
  diagnostics,
61
83
  currentTime,
62
84
  height,
85
+ maxHeight = DEVTOOLS_MAX_HEIGHT,
63
86
  onHeightChange,
64
87
  onSeek,
65
88
  issueMatches,
@@ -67,6 +90,7 @@ export function SessionDevToolsPanel({
67
90
  diagnostics: ReplayDevToolsDiagnostics;
68
91
  currentTime: number;
69
92
  height: number;
93
+ maxHeight?: number;
70
94
  onHeightChange: (height: number) => void;
71
95
  onSeek: (ms: number) => void;
72
96
  /** Resolved error issues by console entry id, for cross-linking to Errors. */
@@ -105,11 +129,6 @@ export function SessionDevToolsPanel({
105
129
  ?.id ?? null)
106
130
  : null;
107
131
 
108
- const selectedConsole =
109
- filteredConsole.find((entry) => entry.id === selectedConsoleId) ?? null;
110
- const selectedNetwork =
111
- filteredNetwork.find((entry) => entry.id === selectedNetworkId) ?? null;
112
-
113
132
  useEffect(() => {
114
133
  if (
115
134
  selectedConsoleId &&
@@ -148,9 +167,13 @@ export function SessionDevToolsPanel({
148
167
  return (
149
168
  <div
150
169
  className="analytics-session-devtools relative flex min-h-0 shrink-0 flex-col border-t bg-background"
151
- style={{ height }}
170
+ style={{ height: Math.min(height, maxHeight) }}
152
171
  >
153
- <DevToolsResizeHandle height={height} onHeightChange={onHeightChange} />
172
+ <DevToolsResizeHandle
173
+ height={Math.min(height, maxHeight)}
174
+ maxHeight={maxHeight}
175
+ onHeightChange={onHeightChange}
176
+ />
154
177
  <Tabs
155
178
  value={tab}
156
179
  onValueChange={(value) => setTab(value as "console" | "network")}
@@ -230,16 +253,17 @@ export function SessionDevToolsPanel({
230
253
  <VirtualizedDevToolsList
231
254
  entries={filteredConsole}
232
255
  activeEntryId={activeConsoleId}
256
+ expandedEntryId={selectedConsoleId}
233
257
  emptyMessage={
234
258
  diagnostics.console.length
235
259
  ? t("sessions.devtoolsNoConsoleMatches")
236
260
  : t("sessions.devtoolsNoConsole")
237
261
  }
238
- renderRow={(entry) => (
262
+ renderRow={(entry, expanded) => (
239
263
  <ConsoleRow
240
264
  entry={entry}
241
265
  active={entry.id === activeConsoleId}
242
- selected={entry.id === selectedConsoleId}
266
+ selected={expanded}
243
267
  issueMatch={issueMatches?.get(entry.id) ?? null}
244
268
  onSelect={() =>
245
269
  setSelectedConsoleId((current) =>
@@ -250,13 +274,6 @@ export function SessionDevToolsPanel({
250
274
  />
251
275
  )}
252
276
  />
253
- {selectedConsole ? (
254
- <ConsoleDetailPane
255
- entry={selectedConsole}
256
- issueMatch={issueMatches?.get(selectedConsole.id) ?? null}
257
- onSeek={onSeek}
258
- />
259
- ) : null}
260
277
  </TabsContent>
261
278
 
262
279
  <TabsContent
@@ -302,16 +319,17 @@ export function SessionDevToolsPanel({
302
319
  <VirtualizedDevToolsList
303
320
  entries={filteredNetwork}
304
321
  activeEntryId={activeNetworkId}
322
+ expandedEntryId={selectedNetworkId}
305
323
  emptyMessage={
306
324
  diagnostics.network.length
307
325
  ? t("sessions.devtoolsNoNetworkMatches")
308
326
  : t("sessions.devtoolsNoNetwork")
309
327
  }
310
- renderRow={(entry) => (
328
+ renderRow={(entry, expanded) => (
311
329
  <NetworkRow
312
330
  entry={entry}
313
331
  active={entry.id === activeNetworkId}
314
- selected={entry.id === selectedNetworkId}
332
+ selected={expanded}
315
333
  onSelect={() =>
316
334
  setSelectedNetworkId((current) =>
317
335
  current === entry.id ? null : entry.id,
@@ -321,9 +339,6 @@ export function SessionDevToolsPanel({
321
339
  />
322
340
  )}
323
341
  />
324
- {selectedNetwork ? (
325
- <NetworkDetailPane entry={selectedNetwork} onSeek={onSeek} />
326
- ) : null}
327
342
  </TabsContent>
328
343
  </Tabs>
329
344
  </div>
@@ -332,12 +347,15 @@ export function SessionDevToolsPanel({
332
347
 
333
348
  function DevToolsResizeHandle({
334
349
  height,
350
+ maxHeight,
335
351
  onHeightChange,
336
352
  }: {
337
353
  height: number;
354
+ maxHeight: number;
338
355
  onHeightChange: (height: number) => void;
339
356
  }) {
340
357
  const t = useT();
358
+ const cappedMax = Math.max(DEVTOOLS_MIN_HEIGHT, maxHeight);
341
359
 
342
360
  function handlePointerDown(event: ReactPointerEvent<HTMLDivElement>) {
343
361
  event.preventDefault();
@@ -348,8 +366,8 @@ function DevToolsResizeHandle({
348
366
  onHeightChange(
349
367
  clamp(
350
368
  startHeight - (moveEvent.clientY - startY),
351
- DEVTOOLS_MIN_HEIGHT,
352
- DEVTOOLS_MAX_HEIGHT,
369
+ Math.min(DEVTOOLS_MIN_HEIGHT, cappedMax),
370
+ cappedMax,
353
371
  ),
354
372
  );
355
373
  }
@@ -368,8 +386,8 @@ function DevToolsResizeHandle({
368
386
  role="separator"
369
387
  aria-label={t("sessions.devtoolsResize")}
370
388
  aria-orientation="horizontal"
371
- aria-valuemin={DEVTOOLS_MIN_HEIGHT}
372
- aria-valuemax={DEVTOOLS_MAX_HEIGHT}
389
+ aria-valuemin={Math.min(DEVTOOLS_MIN_HEIGHT, cappedMax)}
390
+ aria-valuemax={cappedMax}
373
391
  aria-valuenow={Math.round(height)}
374
392
  className="absolute inset-x-0 -top-1 z-10 flex h-2 cursor-row-resize items-center justify-center"
375
393
  onPointerDown={handlePointerDown}
@@ -382,13 +400,15 @@ function DevToolsResizeHandle({
382
400
  function VirtualizedDevToolsList<T extends { id: string }>({
383
401
  entries,
384
402
  activeEntryId,
403
+ expandedEntryId,
385
404
  emptyMessage,
386
405
  renderRow,
387
406
  }: {
388
407
  entries: T[];
389
408
  activeEntryId: string | null;
409
+ expandedEntryId: string | null;
390
410
  emptyMessage: string;
391
- renderRow: (entry: T) => ReactNode;
411
+ renderRow: (entry: T, expanded: boolean) => ReactNode;
392
412
  }) {
393
413
  const containerRef = useRef<HTMLDivElement>(null);
394
414
  const lastManualScrollAtRef = useRef(0);
@@ -398,6 +418,16 @@ function VirtualizedDevToolsList<T extends { id: string }>({
398
418
  const activeIndex = activeEntryId
399
419
  ? entries.findIndex((entry) => entry.id === activeEntryId)
400
420
  : -1;
421
+ const expandedIndex = expandedEntryId
422
+ ? entries.findIndex((entry) => entry.id === expandedEntryId)
423
+ : -1;
424
+
425
+ const rowOffsets = useMemo(
426
+ () => buildDevToolsRowOffsets(entries.length, expandedIndex),
427
+ [entries.length, expandedIndex],
428
+ );
429
+
430
+ const totalHeight = rowOffsets[entries.length] ?? 0;
401
431
 
402
432
  useEffect(() => {
403
433
  const el = containerRef.current;
@@ -419,15 +449,16 @@ function VirtualizedDevToolsList<T extends { id: string }>({
419
449
  }
420
450
  const el = containerRef.current;
421
451
  if (!el) return;
422
- const rowTop = activeIndex * DEVTOOLS_ROW_HEIGHT;
423
- const rowBottom = rowTop + DEVTOOLS_ROW_HEIGHT;
452
+ const rowTop = rowOffsets[activeIndex] ?? 0;
453
+ const rowBottom =
454
+ rowOffsets[activeIndex + 1] ?? rowTop + DEVTOOLS_ROW_HEIGHT;
424
455
  if (rowTop >= el.scrollTop && rowBottom <= el.scrollTop + el.clientHeight) {
425
456
  return;
426
457
  }
427
458
  el.scrollTo({
428
459
  top: Math.max(0, rowTop - el.clientHeight / 2 + DEVTOOLS_ROW_HEIGHT),
429
460
  });
430
- }, [activeIndex, entries.length]);
461
+ }, [activeIndex, entries.length, rowOffsets]);
431
462
 
432
463
  const markManualScroll = () => {
433
464
  lastManualScrollAtRef.current = Date.now();
@@ -442,14 +473,24 @@ function VirtualizedDevToolsList<T extends { id: string }>({
442
473
  }
443
474
 
444
475
  const measuredHeight = viewportHeight || 240;
445
- const startIndex = clamp(
446
- Math.floor(scrollTop / DEVTOOLS_ROW_HEIGHT) - DEVTOOLS_OVERSCAN_ROWS,
447
- 0,
448
- entries.length,
449
- );
450
- const endIndex = clamp(
451
- Math.ceil((scrollTop + measuredHeight) / DEVTOOLS_ROW_HEIGHT) +
452
- DEVTOOLS_OVERSCAN_ROWS,
476
+ let startIndex = 0;
477
+ while (
478
+ startIndex < entries.length &&
479
+ (rowOffsets[startIndex + 1] ?? 0) < scrollTop
480
+ ) {
481
+ startIndex += 1;
482
+ }
483
+ startIndex = clamp(startIndex - DEVTOOLS_OVERSCAN_ROWS, 0, entries.length);
484
+
485
+ let endIndex = startIndex;
486
+ while (
487
+ endIndex < entries.length &&
488
+ (rowOffsets[endIndex] ?? 0) < scrollTop + measuredHeight
489
+ ) {
490
+ endIndex += 1;
491
+ }
492
+ endIndex = clamp(
493
+ endIndex + DEVTOOLS_OVERSCAN_ROWS,
453
494
  startIndex,
454
495
  entries.length,
455
496
  );
@@ -464,22 +505,26 @@ function VirtualizedDevToolsList<T extends { id: string }>({
464
505
  onPointerDown={markManualScroll}
465
506
  onTouchMove={markManualScroll}
466
507
  >
467
- <div
468
- className="relative"
469
- style={{ height: entries.length * DEVTOOLS_ROW_HEIGHT }}
470
- >
471
- {visibleEntries.map((entry, offset) => (
472
- <div
473
- key={entry.id}
474
- className="absolute inset-x-0"
475
- style={{
476
- height: DEVTOOLS_ROW_HEIGHT,
477
- top: (startIndex + offset) * DEVTOOLS_ROW_HEIGHT,
478
- }}
479
- >
480
- {renderRow(entry)}
481
- </div>
482
- ))}
508
+ <div className="relative" style={{ height: totalHeight }}>
509
+ {visibleEntries.map((entry, offset) => {
510
+ const index = startIndex + offset;
511
+ const expanded = entry.id === expandedEntryId;
512
+ const top = rowOffsets[index] ?? 0;
513
+ const height =
514
+ (rowOffsets[index + 1] ?? top + DEVTOOLS_ROW_HEIGHT) - top;
515
+ return (
516
+ <div
517
+ key={entry.id}
518
+ className="absolute inset-x-0"
519
+ style={{
520
+ minHeight: height,
521
+ top,
522
+ }}
523
+ >
524
+ {renderRow(entry, expanded)}
525
+ </div>
526
+ );
527
+ })}
483
528
  </div>
484
529
  </div>
485
530
  );
@@ -617,66 +662,104 @@ function ConsoleRow({
617
662
  onSelect: () => void;
618
663
  onSeek: (ms: number) => void;
619
664
  }) {
665
+ const t = useT();
620
666
  const bucket = consoleLevelBucket(entry.level);
621
667
 
622
668
  return (
623
669
  <div
624
670
  data-entry-id={entry.id}
625
671
  className={cn(
626
- "group flex h-full items-center gap-2 border-b px-3 transition-colors hover:bg-muted/50",
627
- active && "bg-muted",
628
- selected && "bg-muted/80",
672
+ "group border-b transition-colors hover:bg-muted/50",
673
+ active && "bg-primary/[0.06] dark:bg-primary/[0.09]",
674
+ selected && "bg-muted/40",
629
675
  )}
630
676
  >
631
- <button
632
- type="button"
633
- className="flex min-w-0 flex-1 items-center gap-2 text-left"
634
- aria-expanded={selected}
635
- onClick={onSelect}
636
- >
637
- <span className="w-10 shrink-0 font-mono text-[11px] text-muted-foreground">
638
- {formatOffsetClock(entry.offsetMs)}
639
- </span>
640
- <span
641
- className={cn(
642
- "h-2 w-2 shrink-0 rounded-full",
643
- bucket === "error" && "bg-red-500",
644
- bucket === "warn" && "bg-amber-500",
645
- bucket === "info" && "bg-sky-500",
646
- bucket === "log" && "bg-muted-foreground/50",
647
- )}
648
- aria-hidden
649
- />
650
- <span
651
- className={cn(
652
- "min-w-0 flex-1 truncate font-mono text-xs",
653
- bucket === "error" && "text-red-600 dark:text-red-400",
654
- bucket === "warn" && "text-amber-600 dark:text-amber-400",
655
- bucket !== "error" && bucket !== "warn" && "text-foreground/80",
656
- )}
657
- title={entry.message}
677
+ <div className="flex h-[34px] items-center gap-2 px-3">
678
+ <button
679
+ type="button"
680
+ className="flex min-w-0 flex-1 items-center gap-2 text-left"
681
+ aria-expanded={selected}
682
+ onClick={onSelect}
658
683
  >
659
- {entry.message || entry.level}
660
- </span>
661
- {entry.repeat > 1 ? (
662
- <span className="inline-flex shrink-0 items-center rounded-full bg-muted px-1.5 font-mono text-[10px] text-muted-foreground">
663
- x{entry.repeat}
684
+ <span className="w-10 shrink-0 font-mono text-[11px] text-muted-foreground">
685
+ {formatOffsetClock(entry.offsetMs)}
664
686
  </span>
665
- ) : null}
666
- {entry.source !== "console" ? (
667
- <span className="hidden shrink-0 rounded border px-1 font-mono text-[10px] text-muted-foreground sm:inline-flex">
668
- {entry.source}
687
+ <span
688
+ className={cn(
689
+ "h-2 w-2 shrink-0 rounded-full",
690
+ bucket === "error" && "bg-red-500",
691
+ bucket === "warn" && "bg-amber-500",
692
+ bucket === "info" && "bg-sky-500",
693
+ bucket === "log" && "bg-muted-foreground/50",
694
+ )}
695
+ aria-hidden
696
+ />
697
+ <span
698
+ className={cn(
699
+ "min-w-0 flex-1 truncate font-mono text-xs",
700
+ bucket === "error" && "text-red-600 dark:text-red-400",
701
+ bucket === "warn" && "text-amber-600 dark:text-amber-400",
702
+ bucket !== "error" && bucket !== "warn" && "text-foreground/80",
703
+ )}
704
+ title={entry.message}
705
+ >
706
+ {entry.message || entry.level}
669
707
  </span>
670
- ) : null}
671
- <IconChevronRight
672
- className={cn(
673
- "h-3.5 w-3.5 shrink-0 text-muted-foreground transition-transform rtl:-scale-x-100",
674
- selected && "rotate-90 rtl:scale-x-100",
675
- )}
676
- />
677
- </button>
678
- {issueMatch ? <ViewIssueLink issueId={issueMatch.issueId} /> : null}
679
- <JumpToButton offsetMs={entry.offsetMs} onSeek={onSeek} />
708
+ {entry.repeat > 1 ? (
709
+ <span className="inline-flex shrink-0 items-center rounded-full bg-muted px-1.5 font-mono text-[10px] text-muted-foreground">
710
+ x{entry.repeat}
711
+ </span>
712
+ ) : null}
713
+ {entry.source !== "console" ? (
714
+ <span className="hidden shrink-0 rounded border px-1 font-mono text-[10px] text-muted-foreground sm:inline-flex">
715
+ {entry.source}
716
+ </span>
717
+ ) : null}
718
+ <IconChevronRight
719
+ className={cn(
720
+ "h-3.5 w-3.5 shrink-0 text-muted-foreground transition-transform rtl:-scale-x-100",
721
+ selected && "rotate-90 rtl:scale-x-100",
722
+ )}
723
+ />
724
+ </button>
725
+ {issueMatch ? <ViewIssueLink issueId={issueMatch.issueId} /> : null}
726
+ <JumpToButton offsetMs={entry.offsetMs} onSeek={onSeek} />
727
+ </div>
728
+ {selected ? (
729
+ <div className="space-y-2 border-t border-border/60 bg-muted/20 px-3 py-2 ps-[3.25rem]">
730
+ <DetailField label={t("sessions.devtoolsMessage")}>
731
+ <pre className="whitespace-pre-wrap break-all font-mono text-[11px] text-foreground/85">
732
+ {entry.message || entry.level}
733
+ </pre>
734
+ </DetailField>
735
+ {entry.url ? (
736
+ <DetailField label={t("sessions.url")}>
737
+ <p className="break-all font-mono text-[11px] text-muted-foreground">
738
+ {entry.url}
739
+ </p>
740
+ </DetailField>
741
+ ) : null}
742
+ {entry.args.length ? (
743
+ <DetailField label={t("sessions.devtoolsArgs")}>
744
+ <pre className="whitespace-pre-wrap break-all font-mono text-[11px] text-muted-foreground">
745
+ {entry.args.join("\n")}
746
+ </pre>
747
+ </DetailField>
748
+ ) : null}
749
+ {entry.stack ? (
750
+ <DetailField label={t("sessions.devtoolsStack")}>
751
+ <pre className="max-h-40 overflow-auto whitespace-pre-wrap break-all font-mono text-[11px] text-muted-foreground">
752
+ {entry.stack}
753
+ </pre>
754
+ </DetailField>
755
+ ) : null}
756
+ {issueMatch ? (
757
+ <div className="pt-1">
758
+ <ViewIssueLink issueId={issueMatch.issueId} />
759
+ </div>
760
+ ) : null}
761
+ </div>
762
+ ) : null}
680
763
  </div>
681
764
  );
682
765
  }
@@ -701,206 +784,114 @@ function NetworkRow({
701
784
  <div
702
785
  data-entry-id={entry.id}
703
786
  className={cn(
704
- "group flex h-full items-center gap-2 border-b px-3 transition-colors hover:bg-muted/50",
705
- active && "bg-muted",
706
- selected && "bg-muted/80",
787
+ "group border-b transition-colors hover:bg-muted/50",
788
+ active && "bg-primary/[0.06] dark:bg-primary/[0.09]",
789
+ selected && "bg-muted/40",
707
790
  )}
708
791
  >
709
- <button
710
- type="button"
711
- className="flex min-w-0 flex-1 items-center gap-2 text-left"
712
- aria-expanded={selected}
713
- onClick={onSelect}
714
- >
715
- <span className="w-10 shrink-0 font-mono text-[11px] text-muted-foreground">
716
- {formatOffsetClock(entry.offsetMs)}
717
- </span>
718
- <span
719
- className={cn(
720
- "w-12 shrink-0 font-mono text-[11px] font-semibold",
721
- entry.failed
722
- ? "text-red-600 dark:text-red-400"
723
- : "text-muted-foreground",
724
- )}
725
- >
726
- {entry.status > 0 ? entry.status : t("sessions.devtoolsFailedStatus")}
727
- </span>
728
- <span className="w-12 shrink-0 font-mono text-[11px] text-muted-foreground">
729
- {entry.method}
730
- </span>
731
- <span className="hidden w-10 shrink-0 font-mono text-[10px] uppercase text-muted-foreground/70 sm:inline">
732
- {entry.api === "xhr" ? "XHR" : "fetch"}
733
- </span>
734
- <span
735
- className="min-w-0 flex-1 truncate font-mono text-xs text-foreground/80"
736
- title={entry.url}
792
+ <div className="flex h-[34px] items-center gap-2 px-3">
793
+ <button
794
+ type="button"
795
+ className="flex min-w-0 flex-1 items-center gap-2 text-left"
796
+ aria-expanded={selected}
797
+ onClick={onSelect}
737
798
  >
738
- {displayUrl}
739
- </span>
740
- {entry.error ? (
799
+ <span className="w-10 shrink-0 font-mono text-[11px] text-muted-foreground">
800
+ {formatOffsetClock(entry.offsetMs)}
801
+ </span>
741
802
  <span
742
- className="hidden max-w-32 shrink-0 truncate font-mono text-[11px] text-red-600 dark:text-red-400 lg:inline"
743
- title={entry.error}
803
+ className={cn(
804
+ "w-12 shrink-0 font-mono text-[11px] font-semibold",
805
+ entry.failed
806
+ ? "text-red-600 dark:text-red-400"
807
+ : "text-muted-foreground",
808
+ )}
744
809
  >
745
- {entry.error}
810
+ {entry.status > 0
811
+ ? entry.status
812
+ : t("sessions.devtoolsFailedStatus")}
746
813
  </span>
747
- ) : null}
748
- <span className="w-14 shrink-0 text-end font-mono text-[11px] text-muted-foreground">
749
- {t("sessions.devtoolsDurationMs", {
750
- ms: String(entry.durationMs),
751
- })}
752
- </span>
753
- <IconChevronRight
754
- className={cn(
755
- "h-3.5 w-3.5 shrink-0 text-muted-foreground transition-transform rtl:-scale-x-100",
756
- selected && "rotate-90 rtl:scale-x-100",
757
- )}
758
- />
759
- </button>
760
- <JumpToButton offsetMs={entry.offsetMs} onSeek={onSeek} />
761
- </div>
762
- );
763
- }
764
-
765
- function ConsoleDetailPane({
766
- entry,
767
- issueMatch,
768
- onSeek,
769
- }: {
770
- entry: ReplayConsoleEntry;
771
- issueMatch: SessionIssueMatch | null;
772
- onSeek: (ms: number) => void;
773
- }) {
774
- const t = useT();
775
- return (
776
- <DevToolsDetailPane
777
- title={`${entry.level} at ${formatOffsetClock(entry.offsetMs)}`}
778
- action={
779
- issueMatch ? <ViewIssueLink issueId={issueMatch.issueId} /> : null
780
- }
781
- onSeek={() => onSeek(entry.offsetMs)}
782
- >
783
- <DetailField label={t("sessions.devtoolsMessage")}>
784
- <pre className="whitespace-pre-wrap break-all font-mono text-[11px] text-foreground/85">
785
- {entry.message || entry.level}
786
- </pre>
787
- </DetailField>
788
- {entry.url ? (
789
- <DetailField label={t("sessions.url")}>
790
- <p className="break-all font-mono text-[11px] text-muted-foreground">
791
- {entry.url}
792
- </p>
793
- </DetailField>
794
- ) : null}
795
- {entry.args.length ? (
796
- <DetailField label={t("sessions.devtoolsArgs")}>
797
- <pre className="whitespace-pre-wrap break-all font-mono text-[11px] text-muted-foreground">
798
- {entry.args.join("\n")}
799
- </pre>
800
- </DetailField>
801
- ) : null}
802
- {entry.stack ? (
803
- <DetailField label={t("sessions.devtoolsStack")}>
804
- <pre className="max-h-32 overflow-auto whitespace-pre-wrap break-all font-mono text-[11px] text-muted-foreground">
805
- {entry.stack}
806
- </pre>
807
- </DetailField>
808
- ) : null}
809
- </DevToolsDetailPane>
810
- );
811
- }
812
-
813
- function NetworkDetailPane({
814
- entry,
815
- onSeek,
816
- }: {
817
- entry: ReplayNetworkEntry;
818
- onSeek: (ms: number) => void;
819
- }) {
820
- const t = useT();
821
- return (
822
- <DevToolsDetailPane
823
- title={`${entry.method} ${entry.status > 0 ? entry.status : t("sessions.devtoolsFailedStatus")}`}
824
- onSeek={() => onSeek(entry.offsetMs)}
825
- >
826
- <div className="grid gap-2 sm:grid-cols-2">
827
- <DetailValue
828
- label={t("sessions.time")}
829
- value={formatOffsetClock(entry.offsetMs)}
830
- />
831
- <DetailValue
832
- label="API"
833
- value={entry.api === "xhr" ? "XHR" : "fetch"}
834
- />
835
- <DetailValue
836
- label="Status"
837
- value={
838
- entry.status > 0
839
- ? String(entry.status)
840
- : t("sessions.devtoolsFailedStatus")
841
- }
842
- />
843
- <DetailValue
844
- label="Duration"
845
- value={t("sessions.devtoolsDurationMs", {
846
- ms: String(entry.durationMs),
847
- })}
848
- />
849
- </div>
850
- <DetailField label={t("sessions.url")}>
851
- <p className="break-all font-mono text-[11px] text-muted-foreground">
852
- {entry.url}
853
- </p>
854
- </DetailField>
855
- {entry.error ? (
856
- <DetailField label="Error">
857
- <pre className="whitespace-pre-wrap break-all font-mono text-[11px] text-red-600 dark:text-red-400">
858
- {entry.error}
859
- </pre>
860
- </DetailField>
861
- ) : null}
862
- {entry.responseBody ? (
863
- <DetailField label={t("sessions.devtoolsResponseBody")}>
864
- <pre className="max-h-32 overflow-auto whitespace-pre-wrap break-all font-mono text-[11px] text-muted-foreground">
865
- {entry.responseBody}
866
- </pre>
867
- </DetailField>
868
- ) : null}
869
- </DevToolsDetailPane>
870
- );
871
- }
872
-
873
- function DevToolsDetailPane({
874
- title,
875
- onSeek,
876
- action,
877
- children,
878
- }: {
879
- title: string;
880
- onSeek: () => void;
881
- action?: ReactNode;
882
- children: ReactNode;
883
- }) {
884
- const t = useT();
885
- return (
886
- <div className="max-h-44 shrink-0 overflow-auto border-t bg-muted/25 px-3 py-2">
887
- <div className="mb-2 flex items-center justify-between gap-3">
888
- <p className="truncate text-xs font-semibold text-foreground">
889
- {title}
890
- </p>
891
- <div className="flex shrink-0 items-center gap-1.5">
892
- {action}
893
- <button
894
- type="button"
895
- className="inline-flex shrink-0 items-center gap-1 rounded border px-1.5 py-0.5 text-[11px] font-medium text-muted-foreground hover:bg-muted hover:text-foreground"
896
- onClick={onSeek}
814
+ <span className="w-12 shrink-0 font-mono text-[11px] text-muted-foreground">
815
+ {entry.method}
816
+ </span>
817
+ <span className="hidden w-10 shrink-0 font-mono text-[10px] uppercase text-muted-foreground/70 sm:inline">
818
+ {entry.api === "xhr" ? "XHR" : "fetch"}
819
+ </span>
820
+ <span
821
+ className="min-w-0 flex-1 truncate font-mono text-xs text-foreground/80"
822
+ title={entry.url}
897
823
  >
898
- <IconPlayerPlay className="h-3 w-3" />
899
- {t("sessions.devtoolsJumpTo")}
900
- </button>
901
- </div>
824
+ {displayUrl}
825
+ </span>
826
+ {entry.error ? (
827
+ <span
828
+ className="hidden max-w-32 shrink-0 truncate font-mono text-[11px] text-red-600 dark:text-red-400 lg:inline"
829
+ title={entry.error}
830
+ >
831
+ {entry.error}
832
+ </span>
833
+ ) : null}
834
+ <span className="w-14 shrink-0 text-end font-mono text-[11px] text-muted-foreground">
835
+ {t("sessions.devtoolsDurationMs", {
836
+ ms: String(entry.durationMs),
837
+ })}
838
+ </span>
839
+ <IconChevronRight
840
+ className={cn(
841
+ "h-3.5 w-3.5 shrink-0 text-muted-foreground transition-transform rtl:-scale-x-100",
842
+ selected && "rotate-90 rtl:scale-x-100",
843
+ )}
844
+ />
845
+ </button>
846
+ <JumpToButton offsetMs={entry.offsetMs} onSeek={onSeek} />
902
847
  </div>
903
- <div className="space-y-2">{children}</div>
848
+ {selected ? (
849
+ <div className="space-y-2 border-t border-border/60 bg-muted/20 px-3 py-2 ps-[3.25rem]">
850
+ <div className="grid gap-2 sm:grid-cols-2">
851
+ <DetailValue
852
+ label={t("sessions.time")}
853
+ value={formatOffsetClock(entry.offsetMs)}
854
+ />
855
+ <DetailValue
856
+ label="API"
857
+ value={entry.api === "xhr" ? "XHR" : "fetch"}
858
+ />
859
+ <DetailValue
860
+ label="Status"
861
+ value={
862
+ entry.status > 0
863
+ ? String(entry.status)
864
+ : t("sessions.devtoolsFailedStatus")
865
+ }
866
+ />
867
+ <DetailValue
868
+ label="Duration"
869
+ value={t("sessions.devtoolsDurationMs", {
870
+ ms: String(entry.durationMs),
871
+ })}
872
+ />
873
+ </div>
874
+ <DetailField label={t("sessions.url")}>
875
+ <p className="break-all font-mono text-[11px] text-muted-foreground">
876
+ {entry.url}
877
+ </p>
878
+ </DetailField>
879
+ {entry.error ? (
880
+ <DetailField label="Error">
881
+ <pre className="whitespace-pre-wrap break-all font-mono text-[11px] text-red-600 dark:text-red-400">
882
+ {entry.error}
883
+ </pre>
884
+ </DetailField>
885
+ ) : null}
886
+ {entry.responseBody ? (
887
+ <DetailField label={t("sessions.devtoolsResponseBody")}>
888
+ <pre className="max-h-40 overflow-auto whitespace-pre-wrap break-all font-mono text-[11px] text-muted-foreground">
889
+ {entry.responseBody}
890
+ </pre>
891
+ </DetailField>
892
+ ) : null}
893
+ </div>
894
+ ) : null}
904
895
  </div>
905
896
  );
906
897
  }