@tutti-os/agent-gui 0.0.37 → 0.0.39

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 (49) hide show
  1. package/dist/{AgentMessageMarkdown-Fv7I0HUN.d.ts → AgentMessageMarkdown-BVeh06AJ.d.ts} +5 -0
  2. package/dist/agent-conversation/index.d.ts +1 -1
  3. package/dist/agent-conversation/index.js +5 -5
  4. package/dist/agent-message-center/index.d.ts +1 -0
  5. package/dist/agent-message-center/index.js +231 -15
  6. package/dist/agent-message-center/index.js.map +1 -1
  7. package/dist/app/renderer/agentactivity.css +67 -0
  8. package/dist/{chunk-5YJPISWJ.js → chunk-3AXEEQMV.js} +24 -7
  9. package/dist/chunk-3AXEEQMV.js.map +1 -0
  10. package/dist/{chunk-BQNHPMQY.js → chunk-46IYJEZX.js} +98 -5
  11. package/dist/chunk-46IYJEZX.js.map +1 -0
  12. package/dist/{chunk-UHQDBXGH.js → chunk-5HQDW73P.js} +497 -233
  13. package/dist/chunk-5HQDW73P.js.map +1 -0
  14. package/dist/{chunk-2ADVHCST.js → chunk-B2JKDZSR.js} +1 -1
  15. package/dist/{chunk-2ADVHCST.js.map → chunk-B2JKDZSR.js.map} +1 -1
  16. package/dist/{chunk-MKSRP5XS.js → chunk-C7ZZZXKB.js} +5 -5
  17. package/dist/{chunk-XROBPALT.js → chunk-RE3AHBX6.js} +2 -2
  18. package/dist/{chunk-JL2WNRYC.js → chunk-WJPEKD3F.js} +17 -1
  19. package/dist/chunk-WJPEKD3F.js.map +1 -0
  20. package/dist/{chunk-NCS7X3XY.js → chunk-XL2EKJTQ.js} +15 -3
  21. package/dist/chunk-XL2EKJTQ.js.map +1 -0
  22. package/dist/{chunk-WHWZZYBV.js → chunk-Y2DQJPTL.js} +2 -2
  23. package/dist/{chunk-SXHVKUUI.js → chunk-Y5SE2XMU.js} +72 -15
  24. package/dist/chunk-Y5SE2XMU.js.map +1 -0
  25. package/dist/{chunk-MUZMBEFO.js → chunk-ZWJBG2JM.js} +21 -2
  26. package/dist/chunk-ZWJBG2JM.js.map +1 -0
  27. package/dist/context-mention-palette/index.js +6 -6
  28. package/dist/i18n/index.d.ts +16 -0
  29. package/dist/i18n/index.js +1 -1
  30. package/dist/index.d.ts +5 -3
  31. package/dist/index.js +2163 -998
  32. package/dist/index.js.map +1 -1
  33. package/dist/workbench/contribution.js +2 -2
  34. package/dist/workbench/index.d.ts +2 -1
  35. package/dist/workbench/index.js +2 -2
  36. package/dist/workbench/state.d.ts +6 -0
  37. package/dist/workbench/state.js +1 -1
  38. package/dist/workspace-agent-generated-files.js +2 -2
  39. package/package.json +13 -12
  40. package/dist/chunk-5YJPISWJ.js.map +0 -1
  41. package/dist/chunk-BQNHPMQY.js.map +0 -1
  42. package/dist/chunk-JL2WNRYC.js.map +0 -1
  43. package/dist/chunk-MUZMBEFO.js.map +0 -1
  44. package/dist/chunk-NCS7X3XY.js.map +0 -1
  45. package/dist/chunk-SXHVKUUI.js.map +0 -1
  46. package/dist/chunk-UHQDBXGH.js.map +0 -1
  47. /package/dist/{chunk-MKSRP5XS.js.map → chunk-C7ZZZXKB.js.map} +0 -0
  48. /package/dist/{chunk-XROBPALT.js.map → chunk-RE3AHBX6.js.map} +0 -0
  49. /package/dist/{chunk-WHWZZYBV.js.map → chunk-Y2DQJPTL.js.map} +0 -0
@@ -1,10 +1,11 @@
1
1
  import {
2
2
  getOptionalAgentHostApi,
3
3
  useOptionalAgentHostApi
4
- } from "./chunk-2ADVHCST.js";
4
+ } from "./chunk-B2JKDZSR.js";
5
5
  import {
6
+ translate,
6
7
  useTranslation
7
- } from "./chunk-JL2WNRYC.js";
8
+ } from "./chunk-WJPEKD3F.js";
8
9
  import {
9
10
  resolveAgentWorkspaceFileVisualKind
10
11
  } from "./chunk-PJP5BUU6.js";
@@ -187,18 +188,24 @@ function arrayValue(value) {
187
188
  import {
188
189
  createContext,
189
190
  startTransition as startTransition2,
190
- useCallback,
191
- useEffect as useEffect2,
191
+ useCallback as useCallback3,
192
+ useEffect as useEffect3,
192
193
  useContext,
193
194
  memo,
194
- useMemo,
195
- useState as useState2
195
+ useMemo as useMemo2,
196
+ useState as useState4
196
197
  } from "react";
197
198
 
198
199
  // app/renderer/components/ZoomableImage.tsx
199
200
  import {
200
- cloneElement
201
+ cloneElement,
202
+ isValidElement,
203
+ useCallback as useCallback2,
204
+ useEffect,
205
+ useMemo,
206
+ useState as useState2
201
207
  } from "react";
208
+ import { CopyIcon, DownloadIcon } from "lucide-react";
202
209
  import Zoom from "react-medium-image-zoom";
203
210
 
204
211
  // app/renderer/lib/utils.ts
@@ -208,44 +215,315 @@ function cn(...inputs) {
208
215
  return twMerge(clsx(inputs));
209
216
  }
210
217
 
218
+ // shared/agentConversation/components/ConversationImageContextMenu.tsx
219
+ import {
220
+ useCallback,
221
+ useRef,
222
+ useState
223
+ } from "react";
224
+
225
+ // app/renderer/components/ui/context-menu.tsx
226
+ import {
227
+ ContextMenu,
228
+ ContextMenuContent,
229
+ ContextMenuItem,
230
+ ContextMenuTrigger
231
+ } from "@tutti-os/ui-system";
232
+
233
+ // shared/agentConversation/lib/copyImageToClipboard.ts
234
+ async function imageSrcToPngBlob(src) {
235
+ const response = await fetch(src);
236
+ const blob = await response.blob();
237
+ if (blob.type === "image/png") {
238
+ return blob;
239
+ }
240
+ const bitmap = await createImageBitmap(blob);
241
+ const canvas = document.createElement("canvas");
242
+ canvas.width = bitmap.width;
243
+ canvas.height = bitmap.height;
244
+ const ctx = canvas.getContext("2d");
245
+ if (!ctx) {
246
+ return null;
247
+ }
248
+ ctx.drawImage(bitmap, 0, 0);
249
+ return await new Promise(
250
+ (resolve) => canvas.toBlob((result) => resolve(result), "image/png")
251
+ );
252
+ }
253
+ async function blobToBase64(blob) {
254
+ return new Promise((resolve, reject) => {
255
+ const reader = new FileReader();
256
+ reader.onerror = () => reject(reader.error ?? new Error("read failed"));
257
+ reader.onload = () => {
258
+ const result = typeof reader.result === "string" ? reader.result : "";
259
+ resolve(result.includes(",") ? result.split(",").pop() ?? "" : result);
260
+ };
261
+ reader.readAsDataURL(blob);
262
+ });
263
+ }
264
+ async function copyImageWithWebClipboard(blob) {
265
+ if (typeof navigator === "undefined" || typeof navigator.clipboard?.write !== "function" || typeof ClipboardItem === "undefined") {
266
+ return false;
267
+ }
268
+ try {
269
+ await navigator.clipboard.write([new ClipboardItem({ "image/png": blob })]);
270
+ return true;
271
+ } catch {
272
+ return false;
273
+ }
274
+ }
275
+ async function copyImageToClipboard(src, hostClipboard) {
276
+ try {
277
+ const blob = await imageSrcToPngBlob(src);
278
+ if (!blob) {
279
+ return false;
280
+ }
281
+ if (hostClipboard?.writeImage) {
282
+ try {
283
+ await hostClipboard.writeImage({
284
+ data: await blobToBase64(blob),
285
+ mimeType: "image/png"
286
+ });
287
+ return true;
288
+ } catch {
289
+ }
290
+ }
291
+ return copyImageWithWebClipboard(blob);
292
+ } catch {
293
+ return false;
294
+ }
295
+ }
296
+
297
+ // shared/agentConversation/components/ConversationImageContextMenu.tsx
298
+ import { jsx, jsxs } from "react/jsx-runtime";
299
+ function ConversationImageContextMenu({
300
+ src,
301
+ children,
302
+ asChild = false,
303
+ contentStyle
304
+ }) {
305
+ const agentHostApi = useOptionalAgentHostApi();
306
+ const copyStartedRef = useRef(false);
307
+ const [menuResetKey, setMenuResetKey] = useState(0);
308
+ const copyAndClose = useCallback(() => {
309
+ if (copyStartedRef.current) {
310
+ return;
311
+ }
312
+ copyStartedRef.current = true;
313
+ setMenuResetKey((key) => key + 1);
314
+ void copyImageToClipboard(src, agentHostApi?.clipboard).finally(() => {
315
+ copyStartedRef.current = false;
316
+ });
317
+ }, [agentHostApi?.clipboard, src]);
318
+ return /* @__PURE__ */ jsxs(ContextMenu, { children: [
319
+ /* @__PURE__ */ jsx(ContextMenuTrigger, { asChild, children }),
320
+ /* @__PURE__ */ jsx(ContextMenuContent, { style: contentStyle, children: /* @__PURE__ */ jsx(
321
+ ContextMenuItem,
322
+ {
323
+ onClick: copyAndClose,
324
+ onPointerDown: (event) => {
325
+ if (event.button !== 0) {
326
+ return;
327
+ }
328
+ event.preventDefault();
329
+ copyAndClose();
330
+ },
331
+ onSelect: copyAndClose,
332
+ children: translate("agentHost.agentGui.copyImage")
333
+ }
334
+ ) })
335
+ ] }, menuResetKey);
336
+ }
337
+
211
338
  // app/renderer/components/ZoomableImage.tsx
212
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
339
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
213
340
  function ZoomableImage({
214
341
  className,
342
+ downloadName,
343
+ onContextMenu,
344
+ src,
215
345
  wrapElement = "div",
216
346
  ...props
217
347
  }) {
218
348
  const { t } = useTranslation();
349
+ const actionSource = typeof src === "string" && src.trim() ? src.trim() : null;
350
+ const hasImageActions = Boolean(actionSource && downloadName !== void 0);
351
+ const resolvedDownloadName = useMemo(
352
+ () => resolveImageDownloadName(downloadName, actionSource),
353
+ [actionSource, downloadName]
354
+ );
355
+ const [contextMenuPosition, setContextMenuPosition] = useState2(null);
356
+ const closeContextMenu = useCallback2(() => {
357
+ setContextMenuPosition(null);
358
+ }, []);
359
+ useEffect(() => {
360
+ if (!contextMenuPosition) {
361
+ return;
362
+ }
363
+ document.addEventListener("click", closeContextMenu);
364
+ document.addEventListener("scroll", closeContextMenu, true);
365
+ return () => {
366
+ document.removeEventListener("click", closeContextMenu);
367
+ document.removeEventListener("scroll", closeContextMenu, true);
368
+ };
369
+ }, [closeContextMenu, contextMenuPosition]);
370
+ const handleContextMenu = useCallback2(
371
+ (event) => {
372
+ onContextMenu?.(event);
373
+ if (event.defaultPrevented || !actionSource || !hasImageActions) {
374
+ return;
375
+ }
376
+ event.preventDefault();
377
+ setContextMenuPosition({ x: event.clientX, y: event.clientY });
378
+ },
379
+ [actionSource, hasImageActions, onContextMenu]
380
+ );
381
+ const handleCopyImage = useCallback2(async () => {
382
+ if (!actionSource) {
383
+ return;
384
+ }
385
+ closeContextMenu();
386
+ await copyImageToClipboard(actionSource);
387
+ }, [actionSource, closeContextMenu]);
388
+ const handleCopyImageAction = useCallback2(() => {
389
+ void handleCopyImage().catch(() => void 0);
390
+ }, [handleCopyImage]);
391
+ const handleDownloadImage = useCallback2(() => {
392
+ if (!actionSource) {
393
+ return;
394
+ }
395
+ closeContextMenu();
396
+ downloadImage(actionSource, resolvedDownloadName);
397
+ }, [actionSource, closeContextMenu, resolvedDownloadName]);
398
+ const actionButtons = hasImageActions ? /* @__PURE__ */ jsx2(
399
+ ImageActionButtons,
400
+ {
401
+ copyLabel: t("common.copyImage"),
402
+ downloadLabel: t("common.downloadImage"),
403
+ onCopy: handleCopyImageAction,
404
+ onDownload: handleDownloadImage
405
+ }
406
+ ) : null;
219
407
  const renderZoomContent = ({
220
408
  buttonUnzoom,
221
409
  img
222
- }) => /* @__PURE__ */ jsxs(Fragment, { children: [
223
- img,
224
- cloneElement(buttonUnzoom, {
225
- className: cn(
226
- buttonUnzoom.props.className,
227
- "nodrag tsh-desktop-no-drag"
228
- )
229
- })
230
- ] });
231
- return /* @__PURE__ */ jsx(
232
- Zoom,
233
- {
234
- a11yNameButtonZoom: t("common.expandImage"),
235
- a11yNameButtonUnzoom: t("common.minimizeImage"),
236
- classDialog: "tsh-zoom-dialog nodrag tsh-desktop-no-drag",
237
- wrapElement,
238
- zoomMargin: 24,
239
- ZoomContent: renderZoomContent,
240
- children: /* @__PURE__ */ jsx(
241
- "img",
410
+ }) => {
411
+ const zoomSrc = isValidElement(img) && typeof img.props.src === "string" ? img.props.src : null;
412
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
413
+ !actionButtons && img && zoomSrc ? /* @__PURE__ */ jsx2(
414
+ ConversationImageContextMenu,
242
415
  {
243
- ...props,
244
- className: cn("nodrag tsh-desktop-no-drag cursor-zoom-in", className)
416
+ src: zoomSrc,
417
+ asChild: true,
418
+ contentStyle: { zIndex: "var(--z-dialog-popover)" },
419
+ children: img
245
420
  }
246
- )
247
- }
248
- );
421
+ ) : img,
422
+ actionButtons ? /* @__PURE__ */ jsx2("div", { className: "tsh-zoom-dialog__image-actions nodrag tsh-desktop-no-drag", children: actionButtons }) : null,
423
+ cloneElement(buttonUnzoom, {
424
+ className: cn(
425
+ buttonUnzoom.props.className,
426
+ "nodrag tsh-desktop-no-drag"
427
+ )
428
+ })
429
+ ] });
430
+ };
431
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
432
+ /* @__PURE__ */ jsx2(
433
+ Zoom,
434
+ {
435
+ a11yNameButtonZoom: t("common.expandImage"),
436
+ a11yNameButtonUnzoom: t("common.minimizeImage"),
437
+ classDialog: "tsh-zoom-dialog nodrag tsh-desktop-no-drag",
438
+ wrapElement,
439
+ zoomMargin: 24,
440
+ ZoomContent: renderZoomContent,
441
+ children: /* @__PURE__ */ jsx2(
442
+ "img",
443
+ {
444
+ ...props,
445
+ src,
446
+ onContextMenu: hasImageActions ? handleContextMenu : onContextMenu,
447
+ className: cn("nodrag tsh-desktop-no-drag cursor-zoom-in", className)
448
+ }
449
+ )
450
+ }
451
+ ),
452
+ contextMenuPosition && actionButtons ? /* @__PURE__ */ jsx2(
453
+ "div",
454
+ {
455
+ className: "tsh-image-context-menu nodrag tsh-desktop-no-drag",
456
+ style: {
457
+ left: contextMenuPosition.x,
458
+ top: contextMenuPosition.y
459
+ },
460
+ role: "menu",
461
+ onClick: (event) => event.stopPropagation(),
462
+ children: /* @__PURE__ */ jsx2(
463
+ ImageActionButtons,
464
+ {
465
+ copyLabel: t("common.copyImage"),
466
+ downloadLabel: t("common.downloadImage"),
467
+ itemRole: "menuitem",
468
+ onCopy: handleCopyImageAction,
469
+ onDownload: handleDownloadImage
470
+ }
471
+ )
472
+ }
473
+ ) : null
474
+ ] });
475
+ }
476
+ function ImageActionButtons({
477
+ copyLabel,
478
+ downloadLabel,
479
+ itemRole,
480
+ onCopy,
481
+ onDownload
482
+ }) {
483
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
484
+ /* @__PURE__ */ jsxs2("button", { type: "button", role: itemRole, title: copyLabel, onClick: onCopy, children: [
485
+ /* @__PURE__ */ jsx2(CopyIcon, { "aria-hidden": "true", className: "size-4" }),
486
+ /* @__PURE__ */ jsx2("span", { children: copyLabel })
487
+ ] }),
488
+ /* @__PURE__ */ jsxs2(
489
+ "button",
490
+ {
491
+ type: "button",
492
+ role: itemRole,
493
+ title: downloadLabel,
494
+ onClick: onDownload,
495
+ children: [
496
+ /* @__PURE__ */ jsx2(DownloadIcon, { "aria-hidden": "true", className: "size-4" }),
497
+ /* @__PURE__ */ jsx2("span", { children: downloadLabel })
498
+ ]
499
+ }
500
+ )
501
+ ] });
502
+ }
503
+ function downloadImage(src, name) {
504
+ const link = document.createElement("a");
505
+ link.href = src;
506
+ link.download = name;
507
+ link.rel = "noopener";
508
+ document.body.append(link);
509
+ link.click();
510
+ link.remove();
511
+ }
512
+ function resolveImageDownloadName(name, src) {
513
+ const trimmedName = name?.trim();
514
+ if (trimmedName) {
515
+ return trimmedName;
516
+ }
517
+ const srcName = src ? decodeURIComponentSafe(src.split(/[?#]/, 1)[0] ?? "") : "";
518
+ const lastSegment = srcName.split(/[\\/]/).pop()?.trim();
519
+ return lastSegment || "image.png";
520
+ }
521
+ function decodeURIComponentSafe(value) {
522
+ try {
523
+ return decodeURIComponent(value);
524
+ } catch {
525
+ return value;
526
+ }
249
527
  }
250
528
 
251
529
  // shared/AgentMessageMarkdown.tsx
@@ -828,9 +1106,9 @@ function countChar(value, char) {
828
1106
  // shared/useStreamingVisibleText.ts
829
1107
  import {
830
1108
  startTransition,
831
- useEffect,
832
- useRef,
833
- useState
1109
+ useEffect as useEffect2,
1110
+ useRef as useRef2,
1111
+ useState as useState3
834
1112
  } from "react";
835
1113
  var DEFAULT_FRAME_MS = 24;
836
1114
  var DEFAULT_MAX_CHARS_PER_SECOND = 6e3;
@@ -842,20 +1120,20 @@ function useStreamingVisibleText(sourceText, options) {
842
1120
  maxCharsPerSecond = DEFAULT_MAX_CHARS_PER_SECOND,
843
1121
  trailingFlushChars = DEFAULT_TRAILING_FLUSH_CHARS
844
1122
  } = options;
845
- const [visibleText, setVisibleText] = useState(sourceText);
846
- const sourceRef = useRef(sourceText);
847
- const visibleRef = useRef(visibleText);
848
- const timerRef = useRef(null);
849
- useEffect(() => {
1123
+ const [visibleText, setVisibleText] = useState3(sourceText);
1124
+ const sourceRef = useRef2(sourceText);
1125
+ const visibleRef = useRef2(visibleText);
1126
+ const timerRef = useRef2(null);
1127
+ useEffect2(() => {
850
1128
  visibleRef.current = visibleText;
851
1129
  }, [visibleText]);
852
- useEffect(
1130
+ useEffect2(
853
1131
  () => () => {
854
1132
  clearStreamingVisibleTextTimer(timerRef);
855
1133
  },
856
1134
  []
857
1135
  );
858
- useEffect(() => {
1136
+ useEffect2(() => {
859
1137
  sourceRef.current = sourceText;
860
1138
  if (!enabled) {
861
1139
  clearStreamingVisibleTextTimer(timerRef);
@@ -940,7 +1218,7 @@ function clearStreamingVisibleTextTimer(timerRef) {
940
1218
  }
941
1219
 
942
1220
  // shared/AgentMessageMarkdown.tsx
943
- import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1221
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
944
1222
  var COLLAPSED_LINE_LIMIT = 8;
945
1223
  var APPROX_CHARS_PER_LINE = 34;
946
1224
  var DEFERRED_LONG_MARKDOWN_CHAR_THRESHOLD = 4096;
@@ -955,11 +1233,26 @@ var PLAIN_SESSION_MENTION_AGENT_LABELS = [
955
1233
  "Nexight",
956
1234
  "Codex"
957
1235
  ];
1236
+ var STANDARD_MARKDOWN_LINK_PROTOCOLS = [
1237
+ "http",
1238
+ "https",
1239
+ "irc",
1240
+ "ircs",
1241
+ "mailto",
1242
+ "tel",
1243
+ "xmpp"
1244
+ ];
1245
+ var WINDOWS_DRIVE_HREF_PROTOCOLS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
958
1246
  var MARKDOWN_SANITIZE_SCHEMA = {
959
1247
  ...defaultSchema,
960
1248
  protocols: {
961
1249
  ...defaultSchema.protocols,
962
- href: [...defaultSchema.protocols?.href ?? [], "mention"]
1250
+ href: [
1251
+ ...defaultSchema.protocols?.href ?? [],
1252
+ "mention",
1253
+ ...STANDARD_MARKDOWN_LINK_PROTOCOLS,
1254
+ ...WINDOWS_DRIVE_HREF_PROTOCOLS
1255
+ ]
963
1256
  }
964
1257
  };
965
1258
  var EMPTY_WORKSPACE_APP_ICONS = [];
@@ -988,7 +1281,7 @@ function AgentMessageMarkdown({
988
1281
  maxCharsPerSecond: STREAMING_MARKDOWN_MAX_CHARS_PER_SECOND,
989
1282
  trailingFlushChars: STREAMING_MARKDOWN_TAIL_FLUSH_CHARS
990
1283
  });
991
- const stabilizedContent = useMemo(
1284
+ const stabilizedContent = useMemo2(
992
1285
  () => stabilizeStreamingMarkdownTail(visibleContent, {
993
1286
  streaming
994
1287
  }).content,
@@ -997,16 +1290,16 @@ function AgentMessageMarkdown({
997
1290
  const workspaceRoot = workspaceLinkContext?.workspaceRoot ?? null;
998
1291
  const basePath = workspaceLinkContext?.basePath ?? null;
999
1292
  const workspaceLinkSource = workspaceLinkContext?.source ?? null;
1000
- const [isExpanded, setIsExpanded] = useState2(false);
1293
+ const [isExpanded, setIsExpanded] = useState4(false);
1001
1294
  const resolvedExpandLabel = expandLabel ?? t("agentHost.workspaceAgentMessageExpand");
1002
1295
  const shouldCollapse = collapsible && isLikelyLongerThanLineLimit(stabilizedContent);
1003
1296
  const isCollapsed = shouldCollapse && !isExpanded;
1004
1297
  const ContainerTag = inline ? "span" : "div";
1005
- const contentSignature = useMemo(
1298
+ const contentSignature = useMemo2(
1006
1299
  () => hashMarkdownProfilerContent(stabilizedContent),
1007
1300
  [stabilizedContent]
1008
1301
  );
1009
- const normalizedContent = useMemo(
1302
+ const normalizedContent = useMemo2(
1010
1303
  () => linkBareLocalAbsolutePaths(
1011
1304
  normalizeMentionMarkdownLinks(
1012
1305
  normalizePlainIssueMentionTitle ? normalizePlainIssueMentionTitleContent(
@@ -1022,15 +1315,11 @@ function AgentMessageMarkdown({
1022
1315
  contentSignature,
1023
1316
  shouldDeferMarkdownRender
1024
1317
  );
1025
- const handleLinkClick = useCallback(
1318
+ const handleLinkClick = useCallback3(
1026
1319
  (href) => {
1027
1320
  if (workspaceLinkSource && onLinkAction && workspaceRoot) {
1028
- const resolvedHref = resolveWorkspaceFileHrefFromMessage(href, stabilizedContent, {
1029
- workspaceRoot,
1030
- basePath
1031
- }) ?? href;
1032
1321
  const action = resolveWorkspaceLinkAction({
1033
- href: resolvedHref,
1322
+ href,
1034
1323
  workspaceRoot,
1035
1324
  basePath,
1036
1325
  source: workspaceLinkSource
@@ -1042,16 +1331,9 @@ function AgentMessageMarkdown({
1042
1331
  }
1043
1332
  onLinkClick?.(href);
1044
1333
  },
1045
- [
1046
- basePath,
1047
- onLinkAction,
1048
- onLinkClick,
1049
- stabilizedContent,
1050
- workspaceLinkSource,
1051
- workspaceRoot
1052
- ]
1334
+ [basePath, onLinkAction, onLinkClick, workspaceLinkSource, workspaceRoot]
1053
1335
  );
1054
- const handleAnchorClickCapture = useCallback(
1336
+ const handleAnchorClickCapture = useCallback3(
1055
1337
  (event) => {
1056
1338
  const href = resolveMarkdownAnchorHref(event.target);
1057
1339
  if (!href) {
@@ -1063,9 +1345,9 @@ function AgentMessageMarkdown({
1063
1345
  },
1064
1346
  [handleLinkClick]
1065
1347
  );
1066
- const markdownComponents = useMemo(
1348
+ const markdownComponents = useMemo2(
1067
1349
  () => ({
1068
- a: (props) => /* @__PURE__ */ jsx2(
1350
+ a: (props) => /* @__PURE__ */ jsx3(
1069
1351
  MarkdownLink,
1070
1352
  {
1071
1353
  ...props,
@@ -1074,39 +1356,22 @@ function AgentMessageMarkdown({
1074
1356
  previewMode
1075
1357
  }
1076
1358
  ),
1077
- code: (props) => /* @__PURE__ */ jsx2(
1078
- MarkdownCode,
1079
- {
1080
- ...props,
1081
- onLinkClick: handleLinkClick,
1082
- workspaceFileLinksEnabled: Boolean(
1083
- workspaceRoot && workspaceLinkSource
1084
- )
1085
- }
1086
- ),
1087
- img: (props) => /* @__PURE__ */ jsx2(MarkdownMedia, { ...props, enableZoom: enableImageZoom }),
1088
- p: (props) => /* @__PURE__ */ jsx2(MarkdownParagraph, { ...props, inline }),
1359
+ code: (props) => /* @__PURE__ */ jsx3(MarkdownCode, { ...props, onLinkClick: handleLinkClick }),
1360
+ img: (props) => /* @__PURE__ */ jsx3(MarkdownMedia, { ...props, enableZoom: enableImageZoom }),
1361
+ p: (props) => /* @__PURE__ */ jsx3(MarkdownParagraph, { ...props, inline }),
1089
1362
  ul: MarkdownUnorderedList,
1090
1363
  ol: MarkdownOrderedList,
1091
1364
  li: MarkdownListItem
1092
1365
  }),
1093
- [
1094
- enableImageZoom,
1095
- handleLinkClick,
1096
- inline,
1097
- previewMode,
1098
- workspaceAppIcons,
1099
- workspaceLinkSource,
1100
- workspaceRoot
1101
- ]
1366
+ [enableImageZoom, handleLinkClick, inline, previewMode, workspaceAppIcons]
1102
1367
  );
1103
- return /* @__PURE__ */ jsxs2(
1368
+ return /* @__PURE__ */ jsxs3(
1104
1369
  ContainerTag,
1105
1370
  {
1106
1371
  className: "flex w-full min-w-0 flex-col items-start gap-1",
1107
1372
  "data-workspace-agent-markdown-shell": "true",
1108
1373
  children: [
1109
- /* @__PURE__ */ jsx2(
1374
+ /* @__PURE__ */ jsx3(
1110
1375
  ContainerTag,
1111
1376
  {
1112
1377
  className: cn(
@@ -1137,13 +1402,13 @@ function AgentMessageMarkdown({
1137
1402
  "data-agent-mention-only": isMentionOnly ? "true" : void 0,
1138
1403
  "data-collapsed": isCollapsed ? "true" : "false",
1139
1404
  onClickCapture: handleAnchorClickCapture,
1140
- children: markdownRenderReady ? streaming ? /* @__PURE__ */ jsx2(
1405
+ children: markdownRenderReady ? streaming ? /* @__PURE__ */ jsx3(
1141
1406
  StreamingMarkdownBlocks,
1142
1407
  {
1143
1408
  content: normalizedContent,
1144
1409
  components: markdownComponents
1145
1410
  }
1146
- ) : /* @__PURE__ */ jsx2(
1411
+ ) : /* @__PURE__ */ jsx3(
1147
1412
  ReactMarkdown,
1148
1413
  {
1149
1414
  remarkPlugins: [remarkGfm],
@@ -1152,7 +1417,7 @@ function AgentMessageMarkdown({
1152
1417
  components: markdownComponents,
1153
1418
  children: normalizedContent
1154
1419
  }
1155
- ) : /* @__PURE__ */ jsx2(
1420
+ ) : /* @__PURE__ */ jsx3(
1156
1421
  "div",
1157
1422
  {
1158
1423
  className: "whitespace-pre-wrap [overflow-wrap:anywhere]",
@@ -1162,7 +1427,7 @@ function AgentMessageMarkdown({
1162
1427
  )
1163
1428
  }
1164
1429
  ),
1165
- shouldCollapse && !isExpanded ? /* @__PURE__ */ jsx2(
1430
+ shouldCollapse && !isExpanded ? /* @__PURE__ */ jsx3(
1166
1431
  "button",
1167
1432
  {
1168
1433
  type: "button",
@@ -1179,11 +1444,11 @@ function StreamingMarkdownBlocks({
1179
1444
  content,
1180
1445
  components
1181
1446
  }) {
1182
- const blocks = useMemo(
1447
+ const blocks = useMemo2(
1183
1448
  () => splitStreamingMarkdownBlocks(content),
1184
1449
  [content]
1185
1450
  );
1186
- return /* @__PURE__ */ jsx2(Fragment2, { children: blocks.map((block, index) => /* @__PURE__ */ jsx2(
1451
+ return /* @__PURE__ */ jsx3(Fragment2, { children: blocks.map((block, index) => /* @__PURE__ */ jsx3(
1187
1452
  MemoizedMarkdownBlock,
1188
1453
  {
1189
1454
  content: block.content,
@@ -1196,7 +1461,7 @@ var MemoizedMarkdownBlock = memo(function MemoizedMarkdownBlock2({
1196
1461
  content,
1197
1462
  components
1198
1463
  }) {
1199
- return /* @__PURE__ */ jsx2(
1464
+ return /* @__PURE__ */ jsx3(
1200
1465
  ReactMarkdown,
1201
1466
  {
1202
1467
  remarkPlugins: [remarkGfm],
@@ -1300,11 +1565,11 @@ function activateMarkdownLinkFromPointer(event, href, onLinkClick) {
1300
1565
  activateMarkdownLink(event, href, onLinkClick);
1301
1566
  }
1302
1567
  function useDeferredMarkdownRenderReady(contentSignature, shouldDefer) {
1303
- const [readySignature, setReadySignature] = useState2(
1568
+ const [readySignature, setReadySignature] = useState4(
1304
1569
  shouldDefer ? null : contentSignature
1305
1570
  );
1306
1571
  const renderReady = !shouldDefer || readySignature === contentSignature;
1307
- useEffect2(() => {
1572
+ useEffect3(() => {
1308
1573
  if (!shouldDefer) {
1309
1574
  setReadySignature(contentSignature);
1310
1575
  return;
@@ -1380,7 +1645,7 @@ function MarkdownLink({
1380
1645
  t("agentHost.agentGui.workspaceAppFactoryMentionFallback")
1381
1646
  ) : null;
1382
1647
  if (mention) {
1383
- return /* @__PURE__ */ jsx2(
1648
+ return /* @__PURE__ */ jsx3(
1384
1649
  MentionLink,
1385
1650
  {
1386
1651
  ...props,
@@ -1396,7 +1661,7 @@ function MarkdownLink({
1396
1661
  textFromReactNode(props.children)
1397
1662
  ) : null;
1398
1663
  if (fileMention) {
1399
- return /* @__PURE__ */ jsx2(
1664
+ return /* @__PURE__ */ jsx3(
1400
1665
  WorkspaceFileMentionLink,
1401
1666
  {
1402
1667
  ...props,
@@ -1406,7 +1671,10 @@ function MarkdownLink({
1406
1671
  }
1407
1672
  );
1408
1673
  }
1409
- return /* @__PURE__ */ jsx2(MarkdownLinkContext.Provider, { value: true, children: /* @__PURE__ */ jsx2(
1674
+ if (!isClickableMarkdownHref(targetHref)) {
1675
+ return /* @__PURE__ */ jsx3(MarkdownLinkContext.Provider, { value: true, children: /* @__PURE__ */ jsx3("span", { className: props.className, title: props.title, children: props.children }) });
1676
+ }
1677
+ return /* @__PURE__ */ jsx3(MarkdownLinkContext.Provider, { value: true, children: /* @__PURE__ */ jsx3(
1410
1678
  "a",
1411
1679
  {
1412
1680
  ...props,
@@ -1490,7 +1758,7 @@ function WorkspaceFileMentionLink({
1490
1758
  ...props
1491
1759
  }) {
1492
1760
  "use memo";
1493
- return /* @__PURE__ */ jsxs2(
1761
+ return /* @__PURE__ */ jsxs3(
1494
1762
  "a",
1495
1763
  {
1496
1764
  ...props,
@@ -1517,8 +1785,8 @@ function WorkspaceFileMentionLink({
1517
1785
  activateMarkdownLinkFromKey(event, mention.href, onLinkClick);
1518
1786
  },
1519
1787
  children: [
1520
- /* @__PURE__ */ jsx2("span", { className: "tsh-agent-object-token__icon", "aria-hidden": "true" }),
1521
- /* @__PURE__ */ jsx2("span", { className: "tsh-agent-object-token__main", children: mention.label })
1788
+ /* @__PURE__ */ jsx3("span", { className: "tsh-agent-object-token__icon", "aria-hidden": "true" }),
1789
+ /* @__PURE__ */ jsx3("span", { className: "tsh-agent-object-token__main", children: mention.label })
1522
1790
  ]
1523
1791
  }
1524
1792
  );
@@ -1534,7 +1802,7 @@ function MentionLink({
1534
1802
  "use memo";
1535
1803
  const tooltipText = mention.kind === "session" ? `${mention.participant}${mention.summary ? ` ${mention.summary}` : ""}`.trim() : mention.label;
1536
1804
  const { ref: mainRef, overflowing } = useTextOverflow(tooltipText);
1537
- const link = /* @__PURE__ */ jsxs2(
1805
+ const link = /* @__PURE__ */ jsxs3(
1538
1806
  "a",
1539
1807
  {
1540
1808
  ...props,
@@ -1560,14 +1828,14 @@ function MentionLink({
1560
1828
  activateMarkdownLinkFromKey(event, href, onLinkClick);
1561
1829
  },
1562
1830
  children: [
1563
- mention.kind === "workspace-app" || mention.kind === "workspace-reference" ? /* @__PURE__ */ jsx2(
1831
+ mention.kind === "workspace-app" || mention.kind === "workspace-reference" ? /* @__PURE__ */ jsx3(
1564
1832
  "span",
1565
1833
  {
1566
1834
  className: "grid h-4 w-4 shrink-0 place-items-center overflow-hidden rounded-[4px] bg-block",
1567
1835
  "aria-hidden": "true",
1568
1836
  "data-agent-mention-app-icon": "true",
1569
1837
  "data-workspace-app-icon": "true",
1570
- children: mention.iconUrl ? /* @__PURE__ */ jsx2(
1838
+ children: mention.iconUrl ? /* @__PURE__ */ jsx3(
1571
1839
  "img",
1572
1840
  {
1573
1841
  src: mention.iconUrl,
@@ -1577,31 +1845,31 @@ function MentionLink({
1577
1845
  loading: "lazy",
1578
1846
  draggable: false
1579
1847
  }
1580
- ) : /* @__PURE__ */ jsx2("span", { className: "tsh-agent-object-token__kind-icon h-4 w-4" })
1848
+ ) : /* @__PURE__ */ jsx3("span", { className: "tsh-agent-object-token__kind-icon h-4 w-4" })
1581
1849
  }
1582
- ) : /* @__PURE__ */ jsx2("span", { className: "tsh-agent-object-token__kind", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(
1850
+ ) : /* @__PURE__ */ jsx3("span", { className: "tsh-agent-object-token__kind", "aria-hidden": "true", children: /* @__PURE__ */ jsx3(
1583
1851
  "span",
1584
1852
  {
1585
1853
  className: "tsh-agent-object-token__kind-icon",
1586
1854
  "aria-hidden": "true"
1587
1855
  }
1588
1856
  ) }),
1589
- mention.kind === "session" ? /* @__PURE__ */ jsxs2("span", { className: "tsh-agent-object-token__main", ref: mainRef, children: [
1590
- /* @__PURE__ */ jsx2("span", { className: "tsh-agent-object-token__participant", children: mention.participant }),
1591
- mention.summary ? /* @__PURE__ */ jsxs2("span", { className: "tsh-agent-object-token__summary", children: [
1857
+ mention.kind === "session" ? /* @__PURE__ */ jsxs3("span", { className: "tsh-agent-object-token__main", ref: mainRef, children: [
1858
+ /* @__PURE__ */ jsx3("span", { className: "tsh-agent-object-token__participant", children: mention.participant }),
1859
+ mention.summary ? /* @__PURE__ */ jsxs3("span", { className: "tsh-agent-object-token__summary", children: [
1592
1860
  " ",
1593
1861
  mention.summary
1594
1862
  ] }) : null
1595
- ] }) : /* @__PURE__ */ jsx2("span", { className: "tsh-agent-object-token__main", ref: mainRef, children: mention.label })
1863
+ ] }) : /* @__PURE__ */ jsx3("span", { className: "tsh-agent-object-token__main", ref: mainRef, children: mention.label })
1596
1864
  ]
1597
1865
  }
1598
1866
  );
1599
1867
  if (previewMode) {
1600
1868
  return link;
1601
1869
  }
1602
- return /* @__PURE__ */ jsx2(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs2(Tooltip, { children: [
1603
- /* @__PURE__ */ jsx2(TooltipTrigger, { asChild: true, children: link }),
1604
- overflowing ? /* @__PURE__ */ jsx2(TooltipContent, { className: "max-w-[min(420px,calc(100vw-32px))] whitespace-normal text-left [overflow-wrap:anywhere]", children: tooltipText }) : null
1870
+ return /* @__PURE__ */ jsx3(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs3(Tooltip, { children: [
1871
+ /* @__PURE__ */ jsx3(TooltipTrigger, { asChild: true, children: link }),
1872
+ overflowing ? /* @__PURE__ */ jsx3(TooltipContent, { className: "max-w-[min(420px,calc(100vw-32px))] whitespace-normal text-left [overflow-wrap:anywhere]", children: tooltipText }) : null
1605
1873
  ] }) });
1606
1874
  }
1607
1875
  function MarkdownCode({
@@ -1609,17 +1877,16 @@ function MarkdownCode({
1609
1877
  children,
1610
1878
  className,
1611
1879
  onLinkClick,
1612
- workspaceFileLinksEnabled = false,
1613
1880
  ...props
1614
1881
  }) {
1615
1882
  "use memo";
1616
1883
  const isInsideLink = useContext(MarkdownLinkContext);
1617
1884
  const text = textFromReactNode(children).trim();
1618
- const isLinkablePath = !isInsideLink && onLinkClick && !className && (isLocalAbsolutePath(text) || isHttpUrl(text) || workspaceFileLinksEnabled && isLikelyWorkspaceRelativeFilePath(text));
1885
+ const isLinkablePath = !isInsideLink && onLinkClick && !className && (isExplicitWorkspaceFilePath(text) || isHttpUrl(text));
1619
1886
  if (isLinkablePath) {
1620
- return /* @__PURE__ */ jsx2(PathLink, { href: text, onLinkClick, children });
1887
+ return /* @__PURE__ */ jsx3(PathLink, { href: text, onLinkClick, children });
1621
1888
  }
1622
- return /* @__PURE__ */ jsx2("code", { ...props, className, children });
1889
+ return /* @__PURE__ */ jsx3("code", { ...props, className, children });
1623
1890
  }
1624
1891
  var cachedMarkdownMedia = /* @__PURE__ */ new Map();
1625
1892
  var CACHED_MARKDOWN_MEDIA_REVOKE_DELAY_MS = 250;
@@ -1642,10 +1909,10 @@ function MarkdownMedia({
1642
1909
  const shouldEnableZoom = enableZoom && !isInsideLink;
1643
1910
  const fallbackMediaKind = typeof src === "string" ? resolveMarkdownMediaKind(src) : null;
1644
1911
  const resolvedSrc = typeof src === "string" ? resolveRenderableMarkdownMediaSrc(src) : src;
1645
- const [state, setState] = useState2(
1912
+ const [state, setState] = useState4(
1646
1913
  () => canReadWorkspaceImage && workspacePath ? peekCachedMarkdownMediaState(workspacePath) ?? { status: "loading" } : null
1647
1914
  );
1648
- useEffect2(() => {
1915
+ useEffect3(() => {
1649
1916
  if (!workspacePath || !readWorkspaceImage) {
1650
1917
  setState(null);
1651
1918
  return;
@@ -1712,9 +1979,9 @@ function MarkdownMedia({
1712
1979
  if (!workspacePath || !readWorkspaceImage) {
1713
1980
  if (fallbackMediaKind === "video") {
1714
1981
  if (!canRenderMarkdownVideoFallback(src)) {
1715
- return /* @__PURE__ */ jsx2(UnsupportedMarkdownMediaPreview, {});
1982
+ return /* @__PURE__ */ jsx3(UnsupportedMarkdownMediaPreview, {});
1716
1983
  }
1717
- return /* @__PURE__ */ jsx2(
1984
+ return /* @__PURE__ */ jsx3(
1718
1985
  "video",
1719
1986
  {
1720
1987
  src: resolvedSrc,
@@ -1731,7 +1998,7 @@ function MarkdownMedia({
1731
1998
  );
1732
1999
  }
1733
2000
  if (!shouldEnableZoom) {
1734
- return /* @__PURE__ */ jsx2(
2001
+ return /* @__PURE__ */ jsx3(
1735
2002
  "img",
1736
2003
  {
1737
2004
  ...props,
@@ -1742,13 +2009,14 @@ function MarkdownMedia({
1742
2009
  }
1743
2010
  );
1744
2011
  }
1745
- return /* @__PURE__ */ jsx2(
2012
+ return /* @__PURE__ */ jsx3(
1746
2013
  ZoomableImage,
1747
2014
  {
1748
2015
  ...props,
1749
2016
  src: resolvedSrc,
1750
2017
  alt,
1751
2018
  title,
2019
+ downloadName: resolveMarkdownImageDownloadName(src, alt),
1752
2020
  className,
1753
2021
  wrapElement: "span"
1754
2022
  }
@@ -1756,7 +2024,7 @@ function MarkdownMedia({
1756
2024
  }
1757
2025
  if (state?.status === "ready") {
1758
2026
  if (state.kind === "video") {
1759
- return /* @__PURE__ */ jsx2(
2027
+ return /* @__PURE__ */ jsx3(
1760
2028
  "video",
1761
2029
  {
1762
2030
  src: state.src,
@@ -1773,7 +2041,7 @@ function MarkdownMedia({
1773
2041
  );
1774
2042
  }
1775
2043
  if (!shouldEnableZoom) {
1776
- return /* @__PURE__ */ jsx2(
2044
+ return /* @__PURE__ */ jsx3(
1777
2045
  "img",
1778
2046
  {
1779
2047
  ...props,
@@ -1787,13 +2055,14 @@ function MarkdownMedia({
1787
2055
  }
1788
2056
  );
1789
2057
  }
1790
- return /* @__PURE__ */ jsx2(
2058
+ return /* @__PURE__ */ jsx3(
1791
2059
  ZoomableImage,
1792
2060
  {
1793
2061
  ...props,
1794
2062
  src: state.src,
1795
2063
  alt,
1796
2064
  title,
2065
+ downloadName: resolveMarkdownImageDownloadName(workspacePath, alt),
1797
2066
  className: cn(
1798
2067
  "mt-2 block max-h-[360px] max-w-full rounded-[8px] bg-[var(--transparency-block)] object-contain",
1799
2068
  className
@@ -1802,13 +2071,22 @@ function MarkdownMedia({
1802
2071
  }
1803
2072
  );
1804
2073
  }
1805
- return /* @__PURE__ */ jsx2("span", { className: "mt-2 flex min-h-[160px] w-full items-center justify-center rounded-[8px] bg-[var(--transparency-block)] px-5 py-5 text-center text-[13px] leading-5 text-[var(--text-tertiary)]", children: state?.status === "error" ? state.reason === "unsupported" ? t("agentHost.workspaceFileManager.previewUnsupported") : t("agentHost.workspaceFileManager.previewReadFailed", {
2074
+ return /* @__PURE__ */ jsx3("span", { className: "mt-2 flex min-h-[160px] w-full items-center justify-center rounded-[8px] bg-[var(--transparency-block)] px-5 py-5 text-center text-[13px] leading-5 text-[var(--text-tertiary)]", children: state?.status === "error" ? state.reason === "unsupported" ? t("agentHost.workspaceFileManager.previewUnsupported") : t("agentHost.workspaceFileManager.previewReadFailed", {
1806
2075
  message: state.detail ?? ""
1807
2076
  }) : t("agentHost.workspaceFileManager.previewLoading") });
1808
2077
  }
1809
2078
  function UnsupportedMarkdownMediaPreview() {
1810
2079
  const { t } = useTranslation();
1811
- return /* @__PURE__ */ jsx2("span", { className: "mt-2 flex min-h-[160px] w-full items-center justify-center rounded-[8px] bg-[var(--transparency-block)] px-5 py-5 text-center text-[13px] leading-5 text-[var(--text-tertiary)]", children: t("agentHost.workspaceFileManager.previewUnsupported") });
2080
+ return /* @__PURE__ */ jsx3("span", { className: "mt-2 flex min-h-[160px] w-full items-center justify-center rounded-[8px] bg-[var(--transparency-block)] px-5 py-5 text-center text-[13px] leading-5 text-[var(--text-tertiary)]", children: t("agentHost.workspaceFileManager.previewUnsupported") });
2081
+ }
2082
+ function resolveMarkdownImageDownloadName(src, alt) {
2083
+ if (typeof src === "string") {
2084
+ const pathName = basenameWorkspacePath(src.trim());
2085
+ if (pathName) {
2086
+ return pathName;
2087
+ }
2088
+ }
2089
+ return typeof alt === "string" ? alt.trim() || void 0 : void 0;
1812
2090
  }
1813
2091
  var MARKDOWN_ORDERED_LIST_STYLE = {
1814
2092
  listStylePosition: "outside",
@@ -1830,7 +2108,7 @@ function MarkdownUnorderedList({
1830
2108
  ...props
1831
2109
  }) {
1832
2110
  "use memo";
1833
- return /* @__PURE__ */ jsx2(
2111
+ return /* @__PURE__ */ jsx3(
1834
2112
  "ul",
1835
2113
  {
1836
2114
  ...props,
@@ -1848,7 +2126,7 @@ function MarkdownOrderedList({
1848
2126
  ...props
1849
2127
  }) {
1850
2128
  "use memo";
1851
- return /* @__PURE__ */ jsx2(
2129
+ return /* @__PURE__ */ jsx3(
1852
2130
  "ol",
1853
2131
  {
1854
2132
  ...props,
@@ -1866,7 +2144,7 @@ function MarkdownListItem({
1866
2144
  ...props
1867
2145
  }) {
1868
2146
  "use memo";
1869
- return /* @__PURE__ */ jsx2("li", { ...props, style: { ...MARKDOWN_LIST_ITEM_STYLE, ...style } });
2147
+ return /* @__PURE__ */ jsx3("li", { ...props, style: { ...MARKDOWN_LIST_ITEM_STYLE, ...style } });
1870
2148
  }
1871
2149
  function MarkdownParagraph({
1872
2150
  node: _node,
@@ -1875,14 +2153,54 @@ function MarkdownParagraph({
1875
2153
  }) {
1876
2154
  "use memo";
1877
2155
  if (inline) {
1878
- return /* @__PURE__ */ jsx2("span", { ...props });
2156
+ return /* @__PURE__ */ jsx3("span", { ...props });
1879
2157
  }
1880
- return /* @__PURE__ */ jsx2("p", { ...props });
2158
+ return /* @__PURE__ */ jsx3("p", { ...props });
1881
2159
  }
1882
2160
  function isLocalAbsolutePath(path) {
1883
2161
  const candidate = path.trim();
1884
2162
  return candidate.length > 1 && candidate.startsWith("/") && !candidate.startsWith("//") && !candidate.includes("://") && !/\s/.test(candidate);
1885
2163
  }
2164
+ function isHomeRelativePath(path) {
2165
+ const candidate = path.trim();
2166
+ return candidate.length > 0 && !/\s/.test(candidate) && (candidate === "~" || candidate.startsWith("~/") || candidate.startsWith("~\\"));
2167
+ }
2168
+ function isWindowsAbsolutePath2(path) {
2169
+ const candidate = path.trim();
2170
+ return /^[A-Za-z]:[\\/]/.test(candidate) && !/\s/.test(candidate);
2171
+ }
2172
+ function isExplicitWorkspaceFilePath(path) {
2173
+ const candidate = path.trim();
2174
+ if (!candidate || candidate.includes("://")) {
2175
+ return false;
2176
+ }
2177
+ return isLocalAbsolutePath(candidate) || isHomeRelativePath(candidate) || isWindowsAbsolutePath2(candidate);
2178
+ }
2179
+ function isClickableMarkdownHref(href) {
2180
+ const target = href.trim();
2181
+ return Boolean(
2182
+ target && (isStandardMarkdownLinkHref(target) || isRichTextMentionHref(target) || isExplicitWorkspaceFilePath(target))
2183
+ );
2184
+ }
2185
+ function isStandardMarkdownLinkHref(href) {
2186
+ const target = href.trim();
2187
+ if (!target || isExplicitWorkspaceFilePath(target)) {
2188
+ return false;
2189
+ }
2190
+ if (target.startsWith("#")) {
2191
+ return target.length > 1;
2192
+ }
2193
+ let url;
2194
+ try {
2195
+ url = new URL(target);
2196
+ } catch {
2197
+ return false;
2198
+ }
2199
+ const protocol = url.protocol.replace(/:$/, "").toLowerCase();
2200
+ return STANDARD_MARKDOWN_LINK_PROTOCOLS.includes(
2201
+ protocol
2202
+ );
2203
+ }
1886
2204
  function resolveRenderableMarkdownMediaSrc(src) {
1887
2205
  const trimmed = src.trim();
1888
2206
  if (!trimmed) {
@@ -1983,62 +2301,6 @@ function isHttpUrl(value) {
1983
2301
  return false;
1984
2302
  }
1985
2303
  }
1986
- function resolveWorkspaceFileHrefFromMessage(rawPath, messageContent, context) {
1987
- const path = trimTrailingPathPunctuation(rawPath.trim());
1988
- if (!path || isHttpUrl(path) || isLocalAbsolutePath(path) || !isLikelyWorkspaceRelativeFilePath(path)) {
1989
- return null;
1990
- }
1991
- const directoryHrefs = extractWorkspaceDirectoryLinkHrefs(messageContent);
1992
- const candidates = [];
1993
- if (path.includes("/")) {
1994
- candidates.push(path);
1995
- } else {
1996
- for (const directoryHref of directoryHrefs) {
1997
- candidates.push(`${directoryHref}/${path}`);
1998
- }
1999
- candidates.push(path);
2000
- }
2001
- for (const candidate of candidates) {
2002
- if (resolveWorkspaceFilePathCandidate({
2003
- path: candidate,
2004
- workspaceRoot: context.workspaceRoot,
2005
- basePath: context.basePath
2006
- })) {
2007
- return candidate;
2008
- }
2009
- }
2010
- return null;
2011
- }
2012
- function extractWorkspaceDirectoryLinkHrefs(content) {
2013
- const directories = [];
2014
- let index = 0;
2015
- while (index < content.length) {
2016
- const linkEnd = markdownLinkEndIndex(content, index);
2017
- if (linkEnd <= index) {
2018
- index += 1;
2019
- continue;
2020
- }
2021
- const slice = content.slice(index, linkEnd);
2022
- const match = /^\[([^\]]*)\]\(([^)]+)\)$/.exec(slice);
2023
- if (match) {
2024
- const href = match[2]?.trim() ?? "";
2025
- if (href && !isRichTextMentionHref(href) && !href.includes("://")) {
2026
- const normalizedHref = href.replace(/\/+$/g, "");
2027
- if (href.endsWith("/") || !normalizedHref.includes(".") && !normalizedHref.includes(" ")) {
2028
- directories.push(normalizedHref);
2029
- }
2030
- }
2031
- }
2032
- index = linkEnd;
2033
- }
2034
- return [...new Set(directories)];
2035
- }
2036
- function isLikelyWorkspaceRelativeFilePath(path) {
2037
- if (!path || /\s/.test(path) || path.includes("://")) {
2038
- return false;
2039
- }
2040
- return path.includes("/") || /\.[A-Za-z0-9][A-Za-z0-9._-]{0,15}$/.test(path);
2041
- }
2042
2304
  function linkBareLocalAbsolutePaths(content) {
2043
2305
  let out = "";
2044
2306
  for (let index = 0; index < content.length; ) {
@@ -2116,7 +2378,8 @@ function normalizePlainSessionMentionTitle(content) {
2116
2378
  return content;
2117
2379
  }
2118
2380
  function markdownUrlTransform(value) {
2119
- return isRichTextMentionHref(value) ? value : defaultUrlTransform(value);
2381
+ const target = value.trim();
2382
+ return isRichTextMentionHref(target) || isExplicitWorkspaceFilePath(target) || isStandardMarkdownLinkHref(target) ? target : defaultUrlTransform(value);
2120
2383
  }
2121
2384
  function parseMentionLink(href, rawLabel, workspaceAppIcons = [], appFactoryFallbackLabel = "Create app") {
2122
2385
  const mention = parseRichTextMentionHref2(href, rawLabel);
@@ -2271,7 +2534,7 @@ function PathLink({
2271
2534
  onLinkClick
2272
2535
  }) {
2273
2536
  "use memo";
2274
- return /* @__PURE__ */ jsx2(
2537
+ return /* @__PURE__ */ jsx3(
2275
2538
  "a",
2276
2539
  {
2277
2540
  className: "cursor-pointer",
@@ -2335,17 +2598,17 @@ var AgentGUIConversation_styles_default = styles;
2335
2598
  // app/renderer/components/icons/MessageSquareMoreIcon.tsx
2336
2599
  import {
2337
2600
  forwardRef,
2338
- useCallback as useCallback2,
2339
- useEffect as useEffect3,
2601
+ useCallback as useCallback4,
2602
+ useEffect as useEffect4,
2340
2603
  useImperativeHandle,
2341
- useRef as useRef2
2604
+ useRef as useRef3
2342
2605
  } from "react";
2343
2606
  import {
2344
2607
  motion,
2345
2608
  useAnimation,
2346
2609
  useReducedMotion
2347
2610
  } from "framer-motion";
2348
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
2611
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
2349
2612
  var DOT_TRANSITION = {
2350
2613
  times: [0, 0.1, 0.1, 0.2, 0.5, 0.6, 0.6, 0.7],
2351
2614
  duration: 1.5
@@ -2389,14 +2652,14 @@ var MessageSquareMoreIcon = forwardRef(
2389
2652
  }, ref) => {
2390
2653
  const controls = useAnimation();
2391
2654
  const reduceMotion = useReducedMotion();
2392
- const isControlledRef = useRef2(false);
2393
- const startAnimation = useCallback2(() => {
2655
+ const isControlledRef = useRef3(false);
2656
+ const startAnimation = useCallback4(() => {
2394
2657
  if (reduceMotion) {
2395
2658
  return;
2396
2659
  }
2397
2660
  void controls.start(active ? "active" : "animate");
2398
2661
  }, [active, controls, reduceMotion]);
2399
- const stopAnimation = useCallback2(() => {
2662
+ const stopAnimation = useCallback4(() => {
2400
2663
  void controls.start("normal");
2401
2664
  }, [controls]);
2402
2665
  useImperativeHandle(ref, () => {
@@ -2406,14 +2669,14 @@ var MessageSquareMoreIcon = forwardRef(
2406
2669
  stopAnimation
2407
2670
  };
2408
2671
  });
2409
- useEffect3(() => {
2672
+ useEffect4(() => {
2410
2673
  if (active) {
2411
2674
  startAnimation();
2412
2675
  return;
2413
2676
  }
2414
2677
  stopAnimation();
2415
2678
  }, [active, startAnimation, stopAnimation]);
2416
- const handleMouseEnter = useCallback2(
2679
+ const handleMouseEnter = useCallback4(
2417
2680
  (event) => {
2418
2681
  if (isControlledRef.current) {
2419
2682
  onMouseEnter?.(event);
@@ -2423,7 +2686,7 @@ var MessageSquareMoreIcon = forwardRef(
2423
2686
  },
2424
2687
  [onMouseEnter, startAnimation]
2425
2688
  );
2426
- const handleMouseLeave = useCallback2(
2689
+ const handleMouseLeave = useCallback4(
2427
2690
  (event) => {
2428
2691
  if (isControlledRef.current) {
2429
2692
  onMouseLeave?.(event);
@@ -2433,14 +2696,14 @@ var MessageSquareMoreIcon = forwardRef(
2433
2696
  },
2434
2697
  [onMouseLeave, stopAnimation]
2435
2698
  );
2436
- return /* @__PURE__ */ jsx3(
2699
+ return /* @__PURE__ */ jsx4(
2437
2700
  "div",
2438
2701
  {
2439
2702
  className: cn("inline-flex items-center justify-center", className),
2440
2703
  onMouseEnter: handleMouseEnter,
2441
2704
  onMouseLeave: handleMouseLeave,
2442
2705
  ...props,
2443
- children: /* @__PURE__ */ jsxs3(
2706
+ children: /* @__PURE__ */ jsxs4(
2444
2707
  "svg",
2445
2708
  {
2446
2709
  fill: "none",
@@ -2453,8 +2716,8 @@ var MessageSquareMoreIcon = forwardRef(
2453
2716
  width: size,
2454
2717
  xmlns: "http://www.w3.org/2000/svg",
2455
2718
  children: [
2456
- /* @__PURE__ */ jsx3("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }),
2457
- /* @__PURE__ */ jsx3(
2719
+ /* @__PURE__ */ jsx4("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }),
2720
+ /* @__PURE__ */ jsx4(
2458
2721
  motion.path,
2459
2722
  {
2460
2723
  animate: controls,
@@ -2463,7 +2726,7 @@ var MessageSquareMoreIcon = forwardRef(
2463
2726
  variants: DOT_VARIANTS
2464
2727
  }
2465
2728
  ),
2466
- /* @__PURE__ */ jsx3(
2729
+ /* @__PURE__ */ jsx4(
2467
2730
  motion.path,
2468
2731
  {
2469
2732
  animate: controls,
@@ -2472,7 +2735,7 @@ var MessageSquareMoreIcon = forwardRef(
2472
2735
  variants: DOT_VARIANTS
2473
2736
  }
2474
2737
  ),
2475
- /* @__PURE__ */ jsx3(
2738
+ /* @__PURE__ */ jsx4(
2476
2739
  motion.path,
2477
2740
  {
2478
2741
  animate: controls,
@@ -2493,12 +2756,12 @@ MessageSquareMoreIcon.displayName = "MessageSquareMoreIcon";
2493
2756
  // app/renderer/components/ui/custom-scroll-area.tsx
2494
2757
  import {
2495
2758
  forwardRef as forwardRef2,
2496
- useCallback as useCallback3,
2497
- useEffect as useEffect4,
2498
- useRef as useRef3,
2499
- useState as useState3
2759
+ useCallback as useCallback5,
2760
+ useEffect as useEffect5,
2761
+ useRef as useRef4,
2762
+ useState as useState5
2500
2763
  } from "react";
2501
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
2764
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
2502
2765
  var MIN_THUMB_HEIGHT = 24;
2503
2766
  function CustomScrollbar({
2504
2767
  getViewport,
@@ -2509,15 +2772,15 @@ function CustomScrollbar({
2509
2772
  syncKey
2510
2773
  }) {
2511
2774
  "use memo";
2512
- const trackRef = useRef3(null);
2513
- const dragStateRef = useRef3(null);
2514
- const [scrollbarState, setScrollbarState] = useState3({
2775
+ const trackRef = useRef4(null);
2776
+ const dragStateRef = useRef4(null);
2777
+ const [scrollbarState, setScrollbarState] = useState5({
2515
2778
  scrollable: false,
2516
2779
  thumbHeight: 0,
2517
2780
  thumbTop: 0
2518
2781
  });
2519
- const [dragging, setDragging] = useState3(false);
2520
- const syncScrollbarState = useCallback3(() => {
2782
+ const [dragging, setDragging] = useState5(false);
2783
+ const syncScrollbarState = useCallback5(() => {
2521
2784
  const viewport = getViewport();
2522
2785
  if (!viewport) {
2523
2786
  setScrollbarState({ scrollable: false, thumbHeight: 0, thumbTop: 0 });
@@ -2540,7 +2803,7 @@ function CustomScrollbar({
2540
2803
  (previous) => previous.scrollable && previous.thumbHeight === thumbHeight && previous.thumbTop === thumbTop ? previous : { scrollable: true, thumbHeight, thumbTop }
2541
2804
  );
2542
2805
  }, [getViewport]);
2543
- const scrollViewportToThumbTop = useCallback3(
2806
+ const scrollViewportToThumbTop = useCallback5(
2544
2807
  (thumbTop) => {
2545
2808
  const viewport = getViewport();
2546
2809
  const track = trackRef.current;
@@ -2563,7 +2826,7 @@ function CustomScrollbar({
2563
2826
  },
2564
2827
  [getViewport, scrollbarState.thumbHeight, syncScrollbarState]
2565
2828
  );
2566
- const handleTrackMouseDown = useCallback3(
2829
+ const handleTrackMouseDown = useCallback5(
2567
2830
  (event) => {
2568
2831
  if (event.button !== 0 || !scrollbarState.scrollable) {
2569
2832
  return;
@@ -2585,7 +2848,7 @@ function CustomScrollbar({
2585
2848
  scrollbarState.thumbHeight
2586
2849
  ]
2587
2850
  );
2588
- const handleThumbMouseDown = useCallback3(
2851
+ const handleThumbMouseDown = useCallback5(
2589
2852
  (event) => {
2590
2853
  if (event.button !== 0 || !scrollbarState.scrollable) {
2591
2854
  return;
@@ -2618,7 +2881,7 @@ function CustomScrollbar({
2618
2881
  },
2619
2882
  [getViewport, scrollbarState.scrollable, scrollbarState.thumbHeight]
2620
2883
  );
2621
- useEffect4(() => {
2884
+ useEffect5(() => {
2622
2885
  if (!dragging) {
2623
2886
  return;
2624
2887
  }
@@ -2643,7 +2906,7 @@ function CustomScrollbar({
2643
2906
  window.removeEventListener("mouseup", handleMouseUp);
2644
2907
  };
2645
2908
  }, [dragging, getViewport, syncScrollbarState]);
2646
- useEffect4(() => {
2909
+ useEffect5(() => {
2647
2910
  const viewport = getViewport();
2648
2911
  if (!viewport) {
2649
2912
  setScrollbarState({ scrollable: false, thumbHeight: 0, thumbTop: 0 });
@@ -2660,7 +2923,7 @@ function CustomScrollbar({
2660
2923
  resizeObserver?.disconnect();
2661
2924
  };
2662
2925
  }, [getViewport, syncKey, syncScrollbarState]);
2663
- return /* @__PURE__ */ jsx4(
2926
+ return /* @__PURE__ */ jsx5(
2664
2927
  "div",
2665
2928
  {
2666
2929
  ref: trackRef,
@@ -2670,7 +2933,7 @@ function CustomScrollbar({
2670
2933
  "data-testid": testId,
2671
2934
  "aria-hidden": "true",
2672
2935
  onMouseDown: handleTrackMouseDown,
2673
- children: /* @__PURE__ */ jsx4(
2936
+ children: /* @__PURE__ */ jsx5(
2674
2937
  "div",
2675
2938
  {
2676
2939
  className: cn("tsh-custom-scrollbar__thumb", thumbClassName),
@@ -2697,9 +2960,9 @@ var CustomScrollArea = forwardRef2(function CustomScrollArea2({
2697
2960
  ...viewportProps
2698
2961
  }, forwardedRef) {
2699
2962
  "use memo";
2700
- const viewportRef = useRef3(null);
2701
- const getViewport = useCallback3(() => viewportRef.current, []);
2702
- return /* @__PURE__ */ jsxs4(
2963
+ const viewportRef = useRef4(null);
2964
+ const getViewport = useCallback5(() => viewportRef.current, []);
2965
+ return /* @__PURE__ */ jsxs5(
2703
2966
  "div",
2704
2967
  {
2705
2968
  className: cn(
@@ -2707,7 +2970,7 @@ var CustomScrollArea = forwardRef2(function CustomScrollArea2({
2707
2970
  className
2708
2971
  ),
2709
2972
  children: [
2710
- /* @__PURE__ */ jsx4(
2973
+ /* @__PURE__ */ jsx5(
2711
2974
  "div",
2712
2975
  {
2713
2976
  ref: setRefs(viewportRef, forwardedRef),
@@ -2719,7 +2982,7 @@ var CustomScrollArea = forwardRef2(function CustomScrollArea2({
2719
2982
  children
2720
2983
  }
2721
2984
  ),
2722
- /* @__PURE__ */ jsx4(
2985
+ /* @__PURE__ */ jsx5(
2723
2986
  CustomScrollbar,
2724
2987
  {
2725
2988
  getViewport,
@@ -2755,10 +3018,11 @@ export {
2755
3018
  resolveWorkspaceFilePathCandidate,
2756
3019
  resolveWorkspaceFileLinkAction,
2757
3020
  resolveWorkspaceLinkAction,
3021
+ ConversationImageContextMenu,
2758
3022
  ZoomableImage,
2759
3023
  AgentMessageMarkdown,
2760
3024
  AgentGUIConversation_styles_default,
2761
3025
  CustomScrollArea,
2762
3026
  MessageSquareMoreIcon
2763
3027
  };
2764
- //# sourceMappingURL=chunk-UHQDBXGH.js.map
3028
+ //# sourceMappingURL=chunk-5HQDW73P.js.map