@nextclaw/ui 0.15.2 → 0.15.3

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 (109) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/assets/{api-B7Qigvtm.js → api-iNBpppzj.js} +2 -2
  3. package/dist/assets/{appearance-settings-page-CuE0Iiqj.js → appearance-settings-page-DMffQFR3.js} +1 -1
  4. package/dist/assets/{book-open-Ba2f7YiG.js → book-open-C3mJGwiy.js} +1 -1
  5. package/dist/assets/{channels-list-page-CTsM6TcP.js → channels-list-page-CCtp0BRS.js} +1 -1
  6. package/dist/assets/chat-page-H5a0dWvO.js +1 -0
  7. package/dist/assets/{config-split-page-Fbb4PtTf.js → config-split-page-CrOJgB-Y.js} +1 -1
  8. package/dist/assets/{confirm-dialog-D2EG3nqE.js → confirm-dialog-Chv50USy.js} +2 -2
  9. package/dist/assets/{desktop-update-config-BGR3HUi1.js → desktop-update-config-KzTuXRf_.js} +1 -1
  10. package/dist/assets/{dist-7JTjycxE.js → dist-BunzmC4B.js} +1 -1
  11. package/dist/assets/{dist-wXLhk4hl.js → dist-ChmGwvtK.js} +1 -1
  12. package/dist/assets/doc-browser-DNRL4Zvd.js +1 -0
  13. package/dist/assets/doc-browser-Dg0pmkOg.js +1 -0
  14. package/dist/assets/{doc-browser-context-CYAGm5d5.js → doc-browser-context-BHY-0FSD.js} +1 -1
  15. package/dist/assets/{es2015-j1SRCIX5.js → es2015-DQZ5JWwK.js} +1 -1
  16. package/dist/assets/{external-link-DxuPaDoK.js → external-link-CU0kzzIP.js} +1 -1
  17. package/dist/assets/{form-actions-BztL7P_T.js → form-actions-BvCp_0lS.js} +1 -1
  18. package/dist/assets/{i18n-provider-B4oMxOFx.js → i18n-provider-Cu8XyZb_.js} +1 -1
  19. package/dist/assets/index-CzfBdvK1.css +1 -0
  20. package/dist/assets/index-DGqHne1Q.js +104 -0
  21. package/dist/assets/loader-circle-CDSFuzmk.js +1 -0
  22. package/dist/assets/mcp-marketplace-page-BeEQK0DL.js +9 -0
  23. package/dist/assets/mcp-marketplace-page-D6idOTLp.js +1 -0
  24. package/dist/assets/{model-config-page-DeKk9gvE.js → model-config-page-bNNo-XyG.js} +1 -1
  25. package/dist/assets/plus-D4DsdLN-.js +1 -0
  26. package/dist/assets/{provider-scoped-model-input-DsH2LF1q.js → provider-scoped-model-input-IlgQCsoT.js} +1 -1
  27. package/dist/assets/{providers-config-page-BFnGp68F.js → providers-config-page-wLo6CAJw.js} +1 -1
  28. package/dist/assets/{react-UirzUuST.js → react-BPIsQIlO.js} +1 -1
  29. package/dist/assets/{refresh-cw-eT8BF-8B.js → refresh-cw-CILd1vAP.js} +1 -1
  30. package/dist/assets/remote-uKEYp7AR.js +1 -0
  31. package/dist/assets/rotate-cw-wPFPg1kH.js +1 -0
  32. package/dist/assets/{runtime-config-page-BelAqiNG.js → runtime-config-page-DDn1sP4G.js} +1 -1
  33. package/dist/assets/{save-Cb2g0BOi.js → save-ByJqcPTP.js} +1 -1
  34. package/dist/assets/{search-BcHjwdsb.js → search-DVIV6pfD.js} +1 -1
  35. package/dist/assets/{search-config-page-zPm6--Ig.js → search-config-page-B-Cf4iiB.js} +1 -1
  36. package/dist/assets/{secrets-config-page-Cxyp-KuQ.js → secrets-config-page-DPKsY8Iw.js} +1 -1
  37. package/dist/assets/security-config-e9SSBlKr.js +1 -0
  38. package/dist/assets/{select-CdSEOwVM.js → select-BdgczSNw.js} +1 -1
  39. package/dist/assets/{setting-row-CTAtGexr.js → setting-row-BkTVUTKx.js} +1 -1
  40. package/dist/assets/{settings-2-2DWauOjY.js → settings-2-CH5shGg_.js} +1 -1
  41. package/dist/assets/{side-dock-DPvakJ0a.js → side-dock-BsBvcJ9i.js} +2 -2
  42. package/dist/assets/{skeleton-P5oM14E1.js → skeleton-DeX1FKw7.js} +1 -1
  43. package/dist/assets/switch-Cv8Ekb3x.js +1 -0
  44. package/dist/assets/{tag-chip-D-WsKoT1.js → tag-chip-RB9uKcoC.js} +1 -1
  45. package/dist/assets/{tooltip-CjkOaHW7.js → tooltip-oQlEN-l6.js} +1 -1
  46. package/dist/assets/{use-config--hx5oxQn.js → use-config-l3W7y8Qz.js} +1 -1
  47. package/dist/assets/{use-confirm-dialog-CD9a_SK1.js → use-confirm-dialog-BAxaVOJI.js} +1 -1
  48. package/dist/assets/x-BBMffEN0.js +1 -0
  49. package/dist/index.html +26 -26
  50. package/package.json +9 -9
  51. package/src/app/components/layout/sidebar-items.tsx +3 -3
  52. package/src/app/components/layout/sidebar-rail.styles.ts +2 -2
  53. package/src/app/components/layout/sidebar.tsx +1 -1
  54. package/src/app/styles/design-system.css +17 -4
  55. package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +1 -1
  56. package/src/features/chat/features/conversation/components/session-queued-input-rows.tsx +1 -1
  57. package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +62 -1
  58. package/src/features/chat/features/message/components/chat-message-list.container.tsx +47 -1
  59. package/src/features/chat/features/message/utils/__tests__/chat-message-process-summary.utils.test.ts +11 -1
  60. package/src/features/chat/features/message/utils/__tests__/chat-message-texts.utils.test.ts +23 -0
  61. package/src/features/chat/features/message/utils/chat-message-process-summary.utils.ts +5 -0
  62. package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +138 -0
  63. package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +1 -1
  64. package/src/features/chat/features/session/components/session-header/chat-session-title-switcher.tsx +2 -2
  65. package/src/features/chat/features/welcome/components/chat-welcome-project-picker.tsx +1 -1
  66. package/src/features/chat/features/welcome/components/chat-welcome-session-type-picker.tsx +1 -1
  67. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.test.tsx +67 -0
  68. package/src/features/chat/features/workspace/components/chat-session-workspace-directory-browser.tsx +1 -1
  69. package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumb-browser.tsx +2 -2
  70. package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx +1 -1
  71. package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +174 -22
  72. package/src/features/chat/features/workspace/utils/chat-workspace-file-tab-persistence.utils.ts +4 -0
  73. package/src/features/chat/managers/chat-thread.manager.ts +2 -0
  74. package/src/features/chat/stores/chat-thread.store.ts +3 -0
  75. package/src/features/cron/components/cron-config.tsx +63 -80
  76. package/src/features/marketplace/components/detail-doc/__tests__/marketplace-detail-doc.test.tsx +44 -6
  77. package/src/features/marketplace/components/marketplace-detail-doc.tsx +8 -4
  78. package/src/features/marketplace/components/marketplace-page-parts.tsx +1 -1
  79. package/src/features/marketplace/hooks/use-marketplace-detail-doc-entry.ts +84 -0
  80. package/src/features/panel-apps/components/panel-apps-list.tsx +2 -2
  81. package/src/features/right-panel-resources/components/right-panel-resource-home-page.tsx +1 -1
  82. package/src/features/side-dock/components/side-dock.tsx +3 -3
  83. package/src/index.css +239 -13
  84. package/src/shared/components/actions/action-link.tsx +1 -1
  85. package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +1 -1
  86. package/src/shared/components/ui/actions/icon-action-button.tsx +1 -1
  87. package/src/shared/components/ui/button.tsx +1 -1
  88. package/src/shared/components/ui/dialog.tsx +1 -1
  89. package/src/shared/components/ui/input.tsx +1 -1
  90. package/src/shared/components/ui/select.tsx +1 -1
  91. package/src/shared/components/ui/switch.tsx +1 -1
  92. package/src/shared/components/ui/tabs.tsx +1 -1
  93. package/src/shared/components/ui/textarea.tsx +1 -1
  94. package/src/shared/lib/i18n/locales/en-US/chat.json +83 -0
  95. package/src/shared/lib/i18n/locales/zh-CN/chat.json +83 -0
  96. package/dist/assets/chat-page-C_5dcuE3.js +0 -1
  97. package/dist/assets/doc-browser-bHyXnxlj.js +0 -1
  98. package/dist/assets/doc-browser-gTrQ3jCF.js +0 -1
  99. package/dist/assets/index-CvrvblgX.css +0 -1
  100. package/dist/assets/index-Obd4JYU2.js +0 -104
  101. package/dist/assets/loader-circle-CdzgK4eH.js +0 -1
  102. package/dist/assets/mcp-marketplace-page-Bt7CuOoq.js +0 -9
  103. package/dist/assets/mcp-marketplace-page-Ck3t78_n.js +0 -1
  104. package/dist/assets/plus-Bx7wQbhG.js +0 -1
  105. package/dist/assets/remote-CDkHZtA8.js +0 -1
  106. package/dist/assets/rotate-cw-1JqxHTuw.js +0 -1
  107. package/dist/assets/security-config-B_gCq9fn.js +0 -1
  108. package/dist/assets/switch-BU_UEULb.js +0 -1
  109. package/dist/assets/x-DT5x45JU.js +0 -1
@@ -39,6 +39,32 @@ function resolveFilePreviewViewer(params: {
39
39
  return params.viewer === "rendered" && /\.html?$/i.test(params.path) ? "rendered" : "source";
40
40
  }
41
41
 
42
+ type ContentUrlPreviewKind = "image" | "audio" | "video" | "pdf" | "html" | "other";
43
+
44
+ function resolveContentUrlPreviewKind(params: {
45
+ path: string;
46
+ mimeType?: string | null;
47
+ }): ContentUrlPreviewKind {
48
+ const mime = params.mimeType?.trim().toLowerCase() ?? "";
49
+ const path = params.path.trim().toLowerCase();
50
+ if (mime.startsWith("image/") || /\.(avif|bmp|gif|heic|heif|ico|jpe?g|png|svg|tiff?|webp)$/i.test(path)) {
51
+ return "image";
52
+ }
53
+ if (mime.startsWith("audio/") || /\.(aac|flac|m4a|mp3|ogg|opus|wav|weba)$/i.test(path)) {
54
+ return "audio";
55
+ }
56
+ if (mime.startsWith("video/") || /\.(avi|m4v|mkv|mov|mp4|webm|wmv)$/i.test(path)) {
57
+ return "video";
58
+ }
59
+ if (mime.includes("pdf") || path.endsWith(".pdf")) {
60
+ return "pdf";
61
+ }
62
+ if (mime.includes("html") || /\.html?$/i.test(path)) {
63
+ return "html";
64
+ }
65
+ return "other";
66
+ }
67
+
42
68
  function appendPreviewRefreshVersion(url: string, refreshVersion: number): string {
43
69
  if (refreshVersion <= 0) {
44
70
  return url;
@@ -175,7 +201,108 @@ function WorkspaceHtmlRenderedPreview({
175
201
  );
176
202
  }
177
203
 
204
+ function WorkspaceContentUrlPreview({
205
+ contentUrl,
206
+ kind,
207
+ label,
208
+ }: {
209
+ contentUrl: string;
210
+ kind: ContentUrlPreviewKind;
211
+ label: string;
212
+ }) {
213
+ if (kind === "image") {
214
+ return (
215
+ <div className="flex h-full items-center justify-center overflow-auto custom-scrollbar bg-white p-4">
216
+ <img
217
+ src={contentUrl}
218
+ alt={label}
219
+ className="max-h-full max-w-full object-contain"
220
+ data-testid="workspace-content-image"
221
+ />
222
+ </div>
223
+ );
224
+ }
225
+ if (kind === "audio") {
226
+ return (
227
+ <div className="flex h-full items-center justify-center bg-white px-6">
228
+ <audio
229
+ controls
230
+ preload="metadata"
231
+ aria-label={label}
232
+ className="w-full max-w-xl"
233
+ data-testid="workspace-content-audio"
234
+ src={contentUrl}
235
+ />
236
+ </div>
237
+ );
238
+ }
239
+ if (kind === "video") {
240
+ return (
241
+ <div className="flex h-full items-center justify-center bg-black">
242
+ <video
243
+ controls
244
+ playsInline
245
+ preload="metadata"
246
+ aria-label={label}
247
+ className="max-h-full max-w-full"
248
+ data-testid="workspace-content-video"
249
+ src={contentUrl}
250
+ />
251
+ </div>
252
+ );
253
+ }
254
+ if (kind === "pdf" || kind === "html") {
255
+ return (
256
+ <iframe
257
+ allowFullScreen
258
+ className="h-full w-full border-0 bg-white"
259
+ data-testid={kind === "pdf" ? "workspace-content-pdf" : "workspace-html-preview"}
260
+ src={contentUrl}
261
+ title={label}
262
+ />
263
+ );
264
+ }
265
+ return (
266
+ <div
267
+ className="flex h-full flex-col items-center justify-center gap-4 px-6 text-center"
268
+ data-testid="workspace-content-unsupported"
269
+ >
270
+ <div className="max-w-sm space-y-1.5">
271
+ <p className="truncate text-sm font-medium text-foreground" title={label}>
272
+ {label}
273
+ </p>
274
+ <p className="text-sm text-muted-foreground">
275
+ {t("chatWorkspacePreviewUnsupported")}
276
+ </p>
277
+ <p className="text-xs leading-5 text-muted-foreground/80">
278
+ {t("chatWorkspacePreviewUnsupportedHint")}
279
+ </p>
280
+ </div>
281
+ <div className="flex flex-wrap items-center justify-center gap-2">
282
+ <a
283
+ href={contentUrl}
284
+ download={label}
285
+ className="inline-flex h-8 items-center rounded-lg border border-border bg-card px-3 text-xs font-medium text-foreground transition-colors hover:bg-muted"
286
+ >
287
+ {t("chatWorkspacePreviewDownload")}
288
+ </a>
289
+ <a
290
+ href={contentUrl}
291
+ target="_blank"
292
+ rel="noreferrer"
293
+ className="inline-flex h-8 items-center rounded-lg bg-primary px-3 text-xs font-medium text-primary-foreground transition-colors hover:bg-primary/90"
294
+ >
295
+ {t("chatWorkspacePreviewOpenExternally")}
296
+ </a>
297
+ </div>
298
+ </div>
299
+ );
300
+ }
301
+
178
302
  function WorkspacePreviewBody({
303
+ contentUrl,
304
+ contentUrlKind,
305
+ contentLabel,
179
306
  directoryQuery,
180
307
  onFileOpen,
181
308
  previewBlock,
@@ -185,16 +312,29 @@ function WorkspacePreviewBody({
185
312
  previewUrl,
186
313
  previewViewer,
187
314
  }: {
188
- directoryQuery: ReturnType<typeof useServerPathBrowse>;
315
+ contentUrl: string | null;
316
+ contentUrlKind: ContentUrlPreviewKind | null;
317
+ contentLabel: string;
318
+ directoryQuery: ReturnType<typeof useServerPathBrowse> | null | undefined;
189
319
  onFileOpen: (action: ChatFileOpenActionViewModel) => void;
190
320
  previewBlock: ChatFileOperationBlockViewModel | null;
191
321
  previewKind: "text" | "markdown" | "binary";
192
- previewQuery: ReturnType<typeof useServerPathRead>;
322
+ previewQuery: ReturnType<typeof useServerPathRead> | null | undefined;
193
323
  previewText: string | null;
194
324
  previewUrl: string | null;
195
325
  previewViewer: "source" | "rendered";
196
326
  }) {
197
- if (directoryQuery.data) {
327
+ if (contentUrl && contentUrlKind) {
328
+ return (
329
+ <WorkspaceContentUrlPreview
330
+ contentUrl={contentUrl}
331
+ kind={contentUrlKind}
332
+ label={contentLabel}
333
+ />
334
+ );
335
+ }
336
+
337
+ if (directoryQuery?.data) {
198
338
  return (
199
339
  <ChatSessionWorkspaceDirectoryBrowser
200
340
  browseQuery={directoryQuery}
@@ -204,25 +344,25 @@ function WorkspacePreviewBody({
204
344
  );
205
345
  }
206
346
 
207
- if (directoryQuery.isLoading && previewQuery.error && !previewBlock) {
347
+ if (directoryQuery?.isLoading && previewQuery?.error && !previewBlock) {
208
348
  return (
209
349
  <WorkspaceFilePreviewStatus text={t("chatWorkspaceLoadingDirectory")} />
210
350
  );
211
351
  }
212
352
 
213
- if ((directoryQuery.isLoading || previewQuery.isLoading) && !previewBlock) {
353
+ if ((directoryQuery?.isLoading || previewQuery?.isLoading) && !previewBlock) {
214
354
  return (
215
355
  <WorkspaceFilePreviewStatus text={t("chatWorkspaceLoadingPreview")} />
216
356
  );
217
357
  }
218
358
 
219
- if (previewQuery.data?.kind === "binary") {
359
+ if (previewQuery?.data?.kind === "binary") {
220
360
  return (
221
361
  <WorkspaceFilePreviewStatus text={t("chatWorkspacePreviewUnsupported")} />
222
362
  );
223
363
  }
224
364
 
225
- if (previewQuery.error && !previewBlock) {
365
+ if (previewQuery?.error && !previewBlock) {
226
366
  return (
227
367
  <WorkspaceFilePreviewStatus
228
368
  tone="error"
@@ -278,62 +418,71 @@ export function ChatSessionWorkspaceFilePreview({
278
418
  onFileOpen,
279
419
  }: ChatSessionWorkspaceFilePreviewProps) {
280
420
  const isPreviewMode = file.viewMode === "preview";
421
+ const contentUrl = file.contentUrl?.trim() || null;
422
+ const usesServerPath = isPreviewMode && !contentUrl;
281
423
  const previewQuery = useServerPathRead({
282
424
  path: file.path,
283
425
  basePath: sessionWorkingDir,
284
- enabled: isPreviewMode,
426
+ enabled: usesServerPath,
285
427
  });
286
428
  const directoryQuery = useServerPathBrowse({
287
429
  path: file.path,
288
430
  basePath: sessionWorkingDir,
289
431
  includeFiles: true,
290
- enabled: isPreviewMode,
432
+ enabled: usesServerPath,
291
433
  });
292
434
  const diffBlock = useMemo(
293
435
  () => (file.viewMode === "diff" ? buildDiffBlock(file) : null),
294
436
  [file],
295
437
  );
296
438
  const previewText =
297
- isPreviewMode ? previewQuery.data?.text ?? file.rawText ?? null : null;
439
+ isPreviewMode ? previewQuery?.data?.text ?? file.rawText ?? null : null;
298
440
  const previewKind = inferPreviewKind({
299
- path: previewQuery.data?.resolvedPath ?? file.path,
300
- serverKind: previewQuery.data?.kind,
441
+ path: previewQuery?.data?.resolvedPath ?? file.path,
442
+ serverKind: previewQuery?.data?.kind,
301
443
  });
444
+ const contentUrlKind = contentUrl
445
+ ? resolveContentUrlPreviewKind({
446
+ path: file.path,
447
+ mimeType: file.mimeType,
448
+ })
449
+ : null;
302
450
  const resolvedPath =
303
- directoryQuery.data?.currentPath ??
304
- previewQuery.data?.resolvedPath ??
451
+ directoryQuery?.data?.currentPath ??
452
+ previewQuery?.data?.resolvedPath ??
305
453
  file.path;
306
- const previewPathKind = directoryQuery.data ? "directory" : "file";
454
+ const previewPathKind = directoryQuery?.data ? "directory" : "file";
307
455
  const previewViewer = resolveFilePreviewViewer({
308
456
  path: resolvedPath,
309
457
  viewer: file.previewViewer,
310
458
  });
311
459
  const previewUrl =
312
- previewViewer === "rendered" && previewQuery.data?.resolvedPath
460
+ previewViewer === "rendered" && previewQuery?.data?.resolvedPath
313
461
  ? appendPreviewRefreshVersion(
314
462
  buildServerPathContentUrl(previewQuery.data.resolvedPath),
315
463
  refreshVersion,
316
464
  )
317
465
  : null;
318
466
  const previewBlock = useMemo(() => {
319
- if (!isPreviewMode || !previewText) {
467
+ if (!isPreviewMode || !previewText || contentUrl) {
320
468
  return null;
321
469
  }
322
470
  return buildPreviewBlock({
323
- path: previewQuery.data?.resolvedPath ?? file.path,
471
+ path: previewQuery?.data?.resolvedPath ?? file.path,
324
472
  text: previewText,
325
- languageHint: previewQuery.data?.languageHint ?? null,
473
+ languageHint: previewQuery?.data?.languageHint ?? null,
326
474
  line: file.line,
327
475
  });
328
476
  }, [
477
+ contentUrl,
329
478
  file.line,
330
479
  file.path,
331
480
  isPreviewMode,
332
- previewQuery.data?.languageHint,
333
- previewQuery.data?.resolvedPath,
481
+ previewQuery?.data?.languageHint,
482
+ previewQuery?.data?.resolvedPath,
334
483
  previewText,
335
484
  ]);
336
- const isTruncated = Boolean(previewQuery.data?.truncated);
485
+ const isTruncated = Boolean(previewQuery?.data?.truncated);
337
486
  const breadcrumbBasePath = sessionProjectRoot ?? sessionWorkingDir;
338
487
  const breadcrumb = useMemo(
339
488
  () =>
@@ -367,6 +516,9 @@ export function ChatSessionWorkspaceFilePreview({
367
516
  <WorkspaceDiffBody diffBlock={diffBlock} />
368
517
  ) : (
369
518
  <WorkspacePreviewBody
519
+ contentUrl={contentUrl}
520
+ contentUrlKind={contentUrlKind}
521
+ contentLabel={file.label?.trim() || resolvedPath}
370
522
  directoryQuery={directoryQuery}
371
523
  onFileOpen={onFileOpen}
372
524
  previewBlock={previewBlock}
@@ -53,6 +53,8 @@ export function normalizePersistedWorkspaceFileTab(
53
53
  line: normalizeOptionalNumber(value.line),
54
54
  column: normalizeOptionalNumber(value.column),
55
55
  rawText: normalizeOptionalText(value.rawText),
56
+ contentUrl: normalizeOptionalString(value.contentUrl),
57
+ mimeType: normalizeOptionalString(value.mimeType),
56
58
  beforeText: normalizeOptionalText(value.beforeText),
57
59
  afterText: normalizeOptionalText(value.afterText),
58
60
  patchText: normalizeOptionalText(value.patchText),
@@ -74,6 +76,8 @@ export function toPersistedWorkspaceFileTab(
74
76
  line: tab.line,
75
77
  column: tab.column,
76
78
  rawText: tab.rawText,
79
+ contentUrl: tab.contentUrl,
80
+ mimeType: tab.mimeType,
77
81
  beforeText: tab.beforeText,
78
82
  afterText: tab.afterText,
79
83
  patchText: tab.patchText,
@@ -128,6 +128,8 @@ export class ChatThreadManager {
128
128
  line: action.line ?? null,
129
129
  column: action.column ?? null,
130
130
  rawText: action.rawText ?? null,
131
+ contentUrl: action.contentUrl?.trim() || null,
132
+ mimeType: action.mimeType?.trim() || null,
131
133
  beforeText: action.beforeText ?? null,
132
134
  afterText: action.afterText ?? null,
133
135
  patchText: action.patchText ?? null,
@@ -33,6 +33,9 @@ export type ChatWorkspaceFileTab = {
33
33
  line?: number | null;
34
34
  column?: number | null;
35
35
  rawText?: string | null;
36
+ /** Attachment/binary content URL for workspace-native media preview. */
37
+ contentUrl?: string | null;
38
+ mimeType?: string | null;
36
39
  beforeText?: string | null;
37
40
  afterText?: string | null;
38
41
  patchText?: string | null;