@cadraos/sdk 0.1.12 → 0.1.13

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.
@@ -3,6 +3,7 @@
3
3
  var react = require('react');
4
4
  var fetchEventSource = require('@microsoft/fetch-event-source');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
+ var reactDom = require('react-dom');
6
7
  var ReactMarkdown = require('react-markdown');
7
8
  var remarkGfm = require('remark-gfm');
8
9
 
@@ -4032,6 +4033,24 @@ function useFileAttachments(options) {
4032
4033
  console.log("[useFileAttachments] prepareForSubmit result:", aiUuids);
4033
4034
  return aiUuids;
4034
4035
  }, [onSubmitUpload, onError]);
4036
+ const addExternalFile = react.useCallback(
4037
+ (opts) => {
4038
+ const category = opts.category || (opts.mimeType.startsWith("image/") ? "image" : "document");
4039
+ const placeholderFile = new File([], opts.filename, { type: opts.mimeType });
4040
+ const newAttachment = {
4041
+ uuid: opts.uuid,
4042
+ file: placeholderFile,
4043
+ status: "ready",
4044
+ progress: 100,
4045
+ category,
4046
+ previewUrl: opts.previewUrl,
4047
+ hasThumbnail: category === "image",
4048
+ actualSize: opts.size
4049
+ };
4050
+ setFiles((prev) => [...prev, newAttachment]);
4051
+ },
4052
+ []
4053
+ );
4035
4054
  const clear = react.useCallback(() => {
4036
4055
  for (const file of filesRef.current) {
4037
4056
  if (file.previewUrl) {
@@ -4057,6 +4076,7 @@ function useFileAttachments(options) {
4057
4076
  files,
4058
4077
  dropZoneProps,
4059
4078
  removeFile,
4079
+ addExternalFile,
4060
4080
  getReadyUuids,
4061
4081
  getReadyUrls,
4062
4082
  prepareForSubmit,
@@ -5772,23 +5792,45 @@ function ImageLightbox({
5772
5792
  }) {
5773
5793
  const currentImage = images[currentIndex];
5774
5794
  const hasMultiple = images.length > 1;
5795
+ const [container, setContainer] = react.useState(null);
5796
+ react.useEffect(() => {
5797
+ const el = document.createElement("div");
5798
+ el.id = "cadra-lightbox-root";
5799
+ el.style.position = "fixed";
5800
+ el.style.inset = "0";
5801
+ el.style.zIndex = "999999";
5802
+ el.style.pointerEvents = "auto";
5803
+ document.body.appendChild(el);
5804
+ setContainer(el);
5805
+ return () => {
5806
+ document.body.removeChild(el);
5807
+ };
5808
+ }, []);
5809
+ const onCloseRef = react.useRef(onClose);
5810
+ const onPrevRef = react.useRef(onPrev);
5811
+ const onNextRef = react.useRef(onNext);
5812
+ onCloseRef.current = onClose;
5813
+ onPrevRef.current = onPrev;
5814
+ onNextRef.current = onNext;
5775
5815
  react.useEffect(() => {
5776
5816
  function handleKeyDown(e) {
5777
5817
  switch (e.key) {
5778
5818
  case "Escape":
5779
- onClose();
5819
+ e.preventDefault();
5820
+ e.stopPropagation();
5821
+ onCloseRef.current();
5780
5822
  break;
5781
5823
  case "ArrowLeft":
5782
- if (hasMultiple) onPrev();
5824
+ if (hasMultiple) onPrevRef.current();
5783
5825
  break;
5784
5826
  case "ArrowRight":
5785
- if (hasMultiple) onNext();
5827
+ if (hasMultiple) onNextRef.current();
5786
5828
  break;
5787
5829
  }
5788
5830
  }
5789
- document.addEventListener("keydown", handleKeyDown);
5790
- return () => document.removeEventListener("keydown", handleKeyDown);
5791
- }, [onClose, onPrev, onNext, hasMultiple]);
5831
+ document.addEventListener("keydown", handleKeyDown, true);
5832
+ return () => document.removeEventListener("keydown", handleKeyDown, true);
5833
+ }, [hasMultiple]);
5792
5834
  react.useEffect(() => {
5793
5835
  const originalOverflow = document.body.style.overflow;
5794
5836
  document.body.style.overflow = "hidden";
@@ -5796,97 +5838,143 @@ function ImageLightbox({
5796
5838
  document.body.style.overflow = originalOverflow;
5797
5839
  };
5798
5840
  }, []);
5799
- const handleDownload = react.useCallback(() => {
5841
+ react.useEffect(() => {
5842
+ if (!container) return;
5843
+ function handleClick(e) {
5844
+ const target = e.target;
5845
+ if (target.dataset.lightboxBackdrop === "true") {
5846
+ e.preventDefault();
5847
+ e.stopPropagation();
5848
+ onCloseRef.current();
5849
+ }
5850
+ }
5851
+ container.addEventListener("click", handleClick, true);
5852
+ return () => container.removeEventListener("click", handleClick, true);
5853
+ }, [container]);
5854
+ const handleDownload = react.useCallback(async () => {
5800
5855
  if (!currentImage) return;
5801
- const link = document.createElement("a");
5802
- link.href = currentImage.url;
5803
- link.download = currentImage.filename || `image-${currentIndex + 1}`;
5804
- link.target = "_blank";
5805
- link.rel = "noopener noreferrer";
5806
- document.body.appendChild(link);
5807
- link.click();
5808
- document.body.removeChild(link);
5809
- }, [currentImage, currentIndex]);
5810
- if (!currentImage) return null;
5811
- return /* @__PURE__ */ jsxRuntime.jsxs(
5812
- "div",
5813
- {
5814
- className: `fixed inset-0 z-50 flex items-center justify-center bg-black/80 ${className || ""}`,
5815
- onClick: onClose,
5816
- children: [
5817
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-0 left-0 right-0 flex items-center justify-between p-4 z-10", children: [
5818
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-white text-sm", children: hasMultiple && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
5819
- currentIndex + 1,
5820
- " / ",
5821
- images.length
5822
- ] }) }),
5823
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
5824
- /* @__PURE__ */ jsxRuntime.jsx(
5825
- "button",
5826
- {
5827
- type: "button",
5828
- onClick: (e) => {
5829
- e.stopPropagation();
5830
- handleDownload();
5831
- },
5832
- className: "p-2 rounded-full bg-black/50 hover:bg-black/70 text-white transition-colors",
5833
- "aria-label": "Download image",
5834
- children: /* @__PURE__ */ jsxRuntime.jsx(DownloadIcon, { className: "h-5 w-5" })
5835
- }
5836
- ),
5837
- /* @__PURE__ */ jsxRuntime.jsx(
5838
- "button",
5839
- {
5840
- type: "button",
5841
- onClick: (e) => {
5842
- e.stopPropagation();
5843
- onClose();
5844
- },
5845
- className: "p-2 rounded-full bg-black/50 hover:bg-black/70 text-white transition-colors",
5846
- "aria-label": "Close lightbox",
5847
- children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, { className: "h-5 w-5" })
5848
- }
5849
- )
5850
- ] })
5851
- ] }),
5852
- hasMultiple && /* @__PURE__ */ jsxRuntime.jsx(
5853
- "button",
5854
- {
5855
- type: "button",
5856
- onClick: (e) => {
5857
- e.stopPropagation();
5858
- onPrev();
5859
- },
5860
- className: "absolute left-4 top-1/2 -translate-y-1/2 p-2 rounded-full bg-black/50 hover:bg-black/70 text-white transition-colors z-10",
5861
- "aria-label": "Previous image",
5862
- children: /* @__PURE__ */ jsxRuntime.jsx(ChevronLeftIcon, { className: "h-6 w-6" })
5863
- }
5864
- ),
5865
- /* @__PURE__ */ jsxRuntime.jsx(
5866
- "img",
5867
- {
5868
- src: currentImage.url,
5869
- alt: currentImage.alt || `Image ${currentIndex + 1}`,
5870
- className: "max-w-[90vw] max-h-[85vh] object-contain",
5871
- onClick: (e) => e.stopPropagation()
5872
- }
5873
- ),
5874
- hasMultiple && /* @__PURE__ */ jsxRuntime.jsx(
5875
- "button",
5876
- {
5877
- type: "button",
5878
- onClick: (e) => {
5879
- e.stopPropagation();
5880
- onNext();
5881
- },
5882
- className: "absolute right-4 top-1/2 -translate-y-1/2 p-2 rounded-full bg-black/50 hover:bg-black/70 text-white transition-colors z-10",
5883
- "aria-label": "Next image",
5884
- children: /* @__PURE__ */ jsxRuntime.jsx(ChevronRightIcon, { className: "h-6 w-6" })
5885
- }
5886
- ),
5887
- currentImage.alt && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-0 left-0 right-0 p-4 text-center z-10", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-white text-sm bg-black/50 inline-block px-3 py-1 rounded-full", children: currentImage.alt }) })
5888
- ]
5856
+ const filename = currentImage.filename || `image-${currentIndex + 1}`;
5857
+ try {
5858
+ const response = await fetch(currentImage.url);
5859
+ const blob = await response.blob();
5860
+ const blobUrl = URL.createObjectURL(blob);
5861
+ const link = document.createElement("a");
5862
+ link.href = blobUrl;
5863
+ link.download = filename;
5864
+ document.body.appendChild(link);
5865
+ link.click();
5866
+ document.body.removeChild(link);
5867
+ URL.revokeObjectURL(blobUrl);
5868
+ } catch {
5869
+ window.open(currentImage.url, "_blank");
5889
5870
  }
5871
+ }, [currentImage, currentIndex]);
5872
+ const handleClose = react.useCallback(() => {
5873
+ onCloseRef.current();
5874
+ }, []);
5875
+ if (!currentImage || !container) return null;
5876
+ return reactDom.createPortal(
5877
+ /* @__PURE__ */ jsxRuntime.jsxs(
5878
+ "div",
5879
+ {
5880
+ "data-lightbox-backdrop": "true",
5881
+ className: `absolute inset-0 flex items-center justify-center bg-black/90 ${className || ""}`,
5882
+ style: { cursor: "zoom-out" },
5883
+ children: [
5884
+ /* @__PURE__ */ jsxRuntime.jsxs(
5885
+ "div",
5886
+ {
5887
+ className: "absolute top-0 left-0 right-0 flex items-center justify-between p-4",
5888
+ style: { zIndex: 10 },
5889
+ children: [
5890
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-white text-sm", children: hasMultiple && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
5891
+ currentIndex + 1,
5892
+ " / ",
5893
+ images.length
5894
+ ] }) }),
5895
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
5896
+ /* @__PURE__ */ jsxRuntime.jsx(
5897
+ "button",
5898
+ {
5899
+ type: "button",
5900
+ onClick: (e) => {
5901
+ e.stopPropagation();
5902
+ handleDownload();
5903
+ },
5904
+ className: "p-2 rounded-full bg-white/10 hover:bg-white/20 text-white transition-colors",
5905
+ style: { cursor: "pointer", pointerEvents: "auto" },
5906
+ "aria-label": "Download image",
5907
+ children: /* @__PURE__ */ jsxRuntime.jsx(DownloadIcon, { className: "h-5 w-5" })
5908
+ }
5909
+ ),
5910
+ /* @__PURE__ */ jsxRuntime.jsx(
5911
+ "button",
5912
+ {
5913
+ type: "button",
5914
+ onClick: (e) => {
5915
+ e.stopPropagation();
5916
+ handleClose();
5917
+ },
5918
+ className: "p-2 rounded-full bg-white/10 hover:bg-white/20 text-white transition-colors",
5919
+ style: { cursor: "pointer", pointerEvents: "auto" },
5920
+ "aria-label": "Close lightbox",
5921
+ children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, { className: "h-5 w-5" })
5922
+ }
5923
+ )
5924
+ ] })
5925
+ ]
5926
+ }
5927
+ ),
5928
+ hasMultiple && /* @__PURE__ */ jsxRuntime.jsx(
5929
+ "button",
5930
+ {
5931
+ type: "button",
5932
+ onClick: (e) => {
5933
+ e.stopPropagation();
5934
+ onPrev();
5935
+ },
5936
+ className: "absolute left-4 top-1/2 -translate-y-1/2 p-2 rounded-full bg-white/10 hover:bg-white/20 text-white transition-colors",
5937
+ style: { zIndex: 10, cursor: "pointer", pointerEvents: "auto" },
5938
+ "aria-label": "Previous image",
5939
+ children: /* @__PURE__ */ jsxRuntime.jsx(ChevronLeftIcon, { className: "h-6 w-6" })
5940
+ }
5941
+ ),
5942
+ /* @__PURE__ */ jsxRuntime.jsx(
5943
+ "img",
5944
+ {
5945
+ src: currentImage.url,
5946
+ alt: currentImage.alt || `Image ${currentIndex + 1}`,
5947
+ className: "max-w-[90vw] max-h-[85vh] object-contain select-none",
5948
+ style: { cursor: "default", pointerEvents: "auto" },
5949
+ draggable: false
5950
+ }
5951
+ ),
5952
+ hasMultiple && /* @__PURE__ */ jsxRuntime.jsx(
5953
+ "button",
5954
+ {
5955
+ type: "button",
5956
+ onClick: (e) => {
5957
+ e.stopPropagation();
5958
+ onNext();
5959
+ },
5960
+ className: "absolute right-4 top-1/2 -translate-y-1/2 p-2 rounded-full bg-white/10 hover:bg-white/20 text-white transition-colors",
5961
+ style: { zIndex: 10, cursor: "pointer", pointerEvents: "auto" },
5962
+ "aria-label": "Next image",
5963
+ children: /* @__PURE__ */ jsxRuntime.jsx(ChevronRightIcon, { className: "h-6 w-6" })
5964
+ }
5965
+ ),
5966
+ currentImage.alt && /* @__PURE__ */ jsxRuntime.jsx(
5967
+ "div",
5968
+ {
5969
+ className: "absolute bottom-0 left-0 right-0 p-4 text-center",
5970
+ style: { zIndex: 10, pointerEvents: "none" },
5971
+ children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-white text-sm bg-black/50 inline-block px-3 py-1 rounded-full", children: currentImage.alt })
5972
+ }
5973
+ )
5974
+ ]
5975
+ }
5976
+ ),
5977
+ container
5890
5978
  );
5891
5979
  }
5892
5980
  function cn7(...classes) {
@@ -5899,12 +5987,12 @@ function ImageWithLoading({ src, alt, className, onClick }) {
5899
5987
  "div",
5900
5988
  {
5901
5989
  className: cn7(
5902
- "relative overflow-hidden bg-gray-100 dark:bg-gray-800",
5990
+ "relative overflow-hidden rounded-lg",
5903
5991
  className
5904
5992
  ),
5905
5993
  children: [
5906
- !isLoaded && !hasError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 animate-pulse bg-gray-200 dark:bg-gray-700" }),
5907
- hasError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center text-gray-400 dark:text-gray-500 text-sm", children: "Failed to load" }),
5994
+ !isLoaded && !hasError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 animate-pulse bg-gray-200 dark:bg-gray-700 rounded-lg" }),
5995
+ hasError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center text-gray-400 dark:text-gray-500 text-sm rounded-lg bg-gray-100 dark:bg-gray-800", children: "Failed to load" }),
5908
5996
  /* @__PURE__ */ jsxRuntime.jsx(
5909
5997
  "img",
5910
5998
  {
@@ -5912,7 +6000,7 @@ function ImageWithLoading({ src, alt, className, onClick }) {
5912
6000
  alt,
5913
6001
  loading: "lazy",
5914
6002
  className: cn7(
5915
- "w-full h-auto max-h-[300px] object-contain transition-opacity duration-200",
6003
+ "w-full h-auto max-h-[280px] object-cover rounded-lg shadow-sm transition-opacity duration-200",
5916
6004
  isLoaded ? "opacity-100" : "opacity-0"
5917
6005
  ),
5918
6006
  onLoad: () => setIsLoaded(true),
@@ -5969,7 +6057,7 @@ function ImageGallery({
5969
6057
  {
5970
6058
  type: "button",
5971
6059
  onClick: () => handleImageClick(image, index),
5972
- className: "block rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700 hover:border-blue-400 dark:hover:border-blue-600 transition-colors text-left",
6060
+ className: "block rounded-lg overflow-hidden transition-transform hover:scale-[1.02] text-left",
5973
6061
  children: /* @__PURE__ */ jsxRuntime.jsx(
5974
6062
  ImageWithLoading,
5975
6063
  {
@@ -6180,7 +6268,8 @@ var INTERNAL_TOOL_NAMES = /* @__PURE__ */ new Set([
6180
6268
  "check_task_status",
6181
6269
  "assign_task",
6182
6270
  "update_plan",
6183
- "check_delegations"
6271
+ "check_delegations",
6272
+ "activate_skill"
6184
6273
  ]);
6185
6274
  function isInternalTool(toolName) {
6186
6275
  if (!toolName) return false;
@@ -6199,6 +6288,15 @@ function cn10(...classes) {
6199
6288
  }
6200
6289
  function formatToolName(toolName) {
6201
6290
  if (!toolName) return toolName;
6291
+ if (toolName.includes(".")) {
6292
+ const segments = toolName.split(".");
6293
+ const uniqueSegments = segments.filter(
6294
+ (seg, idx) => idx === 0 || seg.toLowerCase().trim() !== segments[idx - 1]?.toLowerCase().trim()
6295
+ );
6296
+ const bestSegment = uniqueSegments[uniqueSegments.length - 1]?.trim() || toolName;
6297
+ if (bestSegment.includes(" ")) return bestSegment;
6298
+ return formatToolName(bestSegment);
6299
+ }
6202
6300
  const prefixesToRemove = [
6203
6301
  "skills_api_",
6204
6302
  "tools_api_",
@@ -6303,6 +6401,7 @@ function ToolCallEntry({
6303
6401
  compact = false
6304
6402
  }) {
6305
6403
  const [isExpanded, setIsExpanded] = react.useState(false);
6404
+ const [lightboxIndex, setLightboxIndex] = react.useState(null);
6306
6405
  const formattedName = react.useMemo(
6307
6406
  () => formatToolName(toolCall.name),
6308
6407
  [toolCall.name]
@@ -6311,6 +6410,14 @@ function ToolCallEntry({
6311
6410
  () => toolCall.status === "completed" && toolCall.result ? extractImagesFromResult2(toolCall.result) : [],
6312
6411
  [toolCall.status, toolCall.result]
6313
6412
  );
6413
+ const lightboxImages = react.useMemo(
6414
+ () => resultImages.map((img) => ({
6415
+ url: img.url,
6416
+ alt: img.alt || img.prompt,
6417
+ filename: img.alt || img.prompt
6418
+ })),
6419
+ [resultImages]
6420
+ );
6314
6421
  const hasImages = resultImages.length > 0;
6315
6422
  const statusIcon = react.useMemo(() => {
6316
6423
  switch (toolCall.status) {
@@ -6380,47 +6487,56 @@ function ToolCallEntry({
6380
6487
  ]
6381
6488
  }
6382
6489
  ),
6383
- hasImages && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 pb-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn10(
6384
- "grid gap-2",
6385
- resultImages.length === 1 ? "grid-cols-1" : "grid-cols-2"
6386
- ), children: resultImages.map((img, idx) => {
6387
- const altText = img.alt || img.prompt || `Generated image ${idx + 1}`;
6388
- const imageElement = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6389
- /* @__PURE__ */ jsxRuntime.jsx(
6390
- "img",
6391
- {
6392
- src: img.thumbnailUrl || img.url,
6393
- alt: altText,
6394
- className: "w-full h-auto max-h-[300px] object-contain bg-gray-50 dark:bg-gray-900",
6395
- loading: "lazy"
6396
- }
6397
- ),
6398
- img.prompt && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 py-1 text-xs text-gray-500 dark:text-gray-400 truncate bg-gray-50 dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700", children: img.prompt })
6399
- ] });
6400
- if (onImageClick) {
6490
+ hasImages && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-3 pb-3", children: [
6491
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn10(
6492
+ "grid gap-3",
6493
+ resultImages.length === 1 ? "grid-cols-1 max-w-sm" : resultImages.length === 2 ? "grid-cols-2" : "grid-cols-2 sm:grid-cols-3"
6494
+ ), children: resultImages.map((img, idx) => {
6495
+ const altText = img.alt || img.prompt || `Generated image ${idx + 1}`;
6496
+ const imageElement = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "group relative", children: [
6497
+ /* @__PURE__ */ jsxRuntime.jsx(
6498
+ "img",
6499
+ {
6500
+ src: img.thumbnailUrl || img.url,
6501
+ alt: altText,
6502
+ className: "w-full h-auto max-h-[280px] object-cover rounded-lg shadow-sm",
6503
+ loading: "lazy"
6504
+ }
6505
+ ),
6506
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 rounded-lg bg-black/0 group-hover:bg-black/10 dark:group-hover:bg-white/5 transition-colors" })
6507
+ ] });
6401
6508
  return /* @__PURE__ */ jsxRuntime.jsx(
6402
6509
  "button",
6403
6510
  {
6404
6511
  type: "button",
6405
- onClick: () => onImageClick({ url: img.url, alt: altText, filename: altText }),
6406
- className: "block rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700 hover:border-blue-400 dark:hover:border-blue-600 transition-colors cursor-pointer text-left",
6512
+ onClick: () => {
6513
+ if (onImageClick) {
6514
+ onImageClick({ url: img.url, alt: altText, filename: altText });
6515
+ } else {
6516
+ setLightboxIndex(idx);
6517
+ }
6518
+ },
6519
+ className: "block rounded-lg overflow-hidden cursor-pointer text-left transition-transform hover:scale-[1.02]",
6407
6520
  children: imageElement
6408
6521
  },
6409
6522
  idx
6410
6523
  );
6411
- }
6412
- return /* @__PURE__ */ jsxRuntime.jsx(
6413
- "a",
6524
+ }) }),
6525
+ lightboxIndex !== null && !onImageClick && /* @__PURE__ */ jsxRuntime.jsx(
6526
+ ImageLightbox,
6414
6527
  {
6415
- href: img.url,
6416
- target: "_blank",
6417
- rel: "noopener noreferrer",
6418
- className: "block rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700 hover:border-blue-400 dark:hover:border-blue-600 transition-colors",
6419
- children: imageElement
6420
- },
6421
- idx
6422
- );
6423
- }) }) }),
6528
+ images: lightboxImages,
6529
+ currentIndex: lightboxIndex,
6530
+ onClose: () => setLightboxIndex(null),
6531
+ onPrev: () => setLightboxIndex(
6532
+ (prev) => prev !== null ? (prev - 1 + lightboxImages.length) % lightboxImages.length : null
6533
+ ),
6534
+ onNext: () => setLightboxIndex(
6535
+ (prev) => prev !== null ? (prev + 1) % lightboxImages.length : null
6536
+ )
6537
+ }
6538
+ )
6539
+ ] }),
6424
6540
  isExpanded && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-gray-200 dark:border-gray-700 px-3 py-2 space-y-2", children: [
6425
6541
  toolCall.args && Object.keys(toolCall.args).length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
6426
6542
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-medium text-gray-500 dark:text-gray-400 mb-1", children: "Input" }),
@@ -6442,11 +6558,168 @@ function ToolCallEntry({
6442
6558
  function cn11(...classes) {
6443
6559
  return classes.filter(Boolean).join(" ");
6444
6560
  }
6561
+ function normalizeToolNameForGrouping(name) {
6562
+ const lastSegment = name.includes(".") ? name.split(".").pop() : name;
6563
+ return lastSegment.toLowerCase().replace(/[^a-z0-9]/g, "");
6564
+ }
6565
+ function groupToolCalls(toolCalls) {
6566
+ const byNormalizedName = /* @__PURE__ */ new Map();
6567
+ const nameOrder = [];
6568
+ for (const tc of toolCalls) {
6569
+ const key = normalizeToolNameForGrouping(tc.name);
6570
+ if (!byNormalizedName.has(key)) {
6571
+ byNormalizedName.set(key, []);
6572
+ nameOrder.push(key);
6573
+ }
6574
+ byNormalizedName.get(key).push(tc);
6575
+ }
6576
+ const groups = [];
6577
+ for (const key of nameOrder) {
6578
+ const calls = byNormalizedName.get(key);
6579
+ if (calls.length === 1) {
6580
+ groups.push({ type: "single", toolCall: calls[0] });
6581
+ } else {
6582
+ groups.push({ type: "group", name: calls[0].name, toolCalls: calls });
6583
+ }
6584
+ }
6585
+ return groups;
6586
+ }
6587
+ function ToolCallList({ toolCalls, toolCallVerbosity, onImageClick, renderToolCall }) {
6588
+ const visibleToolCalls = react.useMemo(() => {
6589
+ if (toolCallVerbosity !== "compact") return toolCalls;
6590
+ return toolCalls.filter((tc) => !isInternalTool(tc.name));
6591
+ }, [toolCalls, toolCallVerbosity]);
6592
+ const grouped = react.useMemo(() => groupToolCalls(visibleToolCalls), [visibleToolCalls]);
6593
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: grouped.map((group) => {
6594
+ if (group.type === "single") {
6595
+ const toolCall = group.toolCall;
6596
+ const defaultRender = () => /* @__PURE__ */ jsxRuntime.jsx(
6597
+ ToolCallEntry,
6598
+ {
6599
+ toolCall,
6600
+ onImageClick
6601
+ },
6602
+ toolCall.id
6603
+ );
6604
+ if (renderToolCall) {
6605
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderToolCall(toolCall, defaultRender) }, toolCall.id);
6606
+ }
6607
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children: defaultRender() }, toolCall.id);
6608
+ }
6609
+ const completed = group.toolCalls.filter((tc) => tc.status === "completed" || tc.status === "failed");
6610
+ const running = group.toolCalls.filter((tc) => tc.status !== "completed" && tc.status !== "failed");
6611
+ const allImages = completed.flatMap(
6612
+ (tc) => tc.result ? extractImagesFromResult2(tc.result) : []
6613
+ );
6614
+ const hasImages = allImages.length > 0;
6615
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
6616
+ running.length > 0 && running[0] && /* @__PURE__ */ jsxRuntime.jsx(
6617
+ ToolCallEntry,
6618
+ {
6619
+ toolCall: {
6620
+ ...running[0],
6621
+ name: running.length > 1 ? `${running[0].name} (${running.length})` : running[0].name
6622
+ },
6623
+ onImageClick
6624
+ },
6625
+ running[0].id
6626
+ ),
6627
+ completed.length > 0 && hasImages && /* @__PURE__ */ jsxRuntime.jsx(
6628
+ GroupedImageGallery,
6629
+ {
6630
+ images: allImages,
6631
+ completedCount: completed.length,
6632
+ onImageClick
6633
+ }
6634
+ ),
6635
+ completed.length > 0 && !hasImages && completed.map((tc) => {
6636
+ const defaultRender = () => /* @__PURE__ */ jsxRuntime.jsx(ToolCallEntry, { toolCall: tc, onImageClick }, tc.id);
6637
+ if (renderToolCall) {
6638
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderToolCall(tc, defaultRender) }, tc.id);
6639
+ }
6640
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children: defaultRender() }, tc.id);
6641
+ })
6642
+ ] }, `group-${group.name}-${group.toolCalls[0]?.id}`);
6643
+ }) });
6644
+ }
6645
+ function GroupedImageGallery({
6646
+ images,
6647
+ completedCount,
6648
+ onImageClick
6649
+ }) {
6650
+ const [lightboxIndex, setLightboxIndex] = react.useState(null);
6651
+ const lightboxImages = react.useMemo(
6652
+ () => images.map((img) => ({
6653
+ url: img.url,
6654
+ alt: img.alt || img.prompt,
6655
+ filename: img.alt || img.prompt
6656
+ })),
6657
+ [images]
6658
+ );
6659
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
6660
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400 px-1", children: [
6661
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-4 w-4 flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "h-4 w-4 text-green-500", children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "20 6 9 17 4 12" }) }) }),
6662
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
6663
+ completedCount,
6664
+ " image",
6665
+ completedCount !== 1 ? "s" : "",
6666
+ " generated"
6667
+ ] })
6668
+ ] }),
6669
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn11(
6670
+ "grid gap-3",
6671
+ images.length === 1 ? "grid-cols-1 max-w-sm" : images.length === 2 ? "grid-cols-2" : "grid-cols-2 sm:grid-cols-3"
6672
+ ), children: images.map((img, idx) => {
6673
+ const altText = img.alt || img.prompt || `Generated image ${idx + 1}`;
6674
+ return /* @__PURE__ */ jsxRuntime.jsx(
6675
+ "button",
6676
+ {
6677
+ type: "button",
6678
+ onClick: () => {
6679
+ if (onImageClick) {
6680
+ onImageClick({ url: img.url, alt: altText, filename: altText });
6681
+ } else {
6682
+ setLightboxIndex(idx);
6683
+ }
6684
+ },
6685
+ className: "block rounded-lg overflow-hidden cursor-pointer text-left transition-transform hover:scale-[1.02] group",
6686
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
6687
+ /* @__PURE__ */ jsxRuntime.jsx(
6688
+ "img",
6689
+ {
6690
+ src: img.thumbnailUrl || img.url,
6691
+ alt: altText,
6692
+ className: "w-full h-auto max-h-[280px] object-cover rounded-lg shadow-sm",
6693
+ loading: "lazy"
6694
+ }
6695
+ ),
6696
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 rounded-lg bg-black/0 group-hover:bg-black/10 dark:group-hover:bg-white/5 transition-colors" })
6697
+ ] })
6698
+ },
6699
+ `${img.url}-${idx}`
6700
+ );
6701
+ }) }),
6702
+ lightboxIndex !== null && !onImageClick && /* @__PURE__ */ jsxRuntime.jsx(
6703
+ ImageLightbox,
6704
+ {
6705
+ images: lightboxImages,
6706
+ currentIndex: lightboxIndex,
6707
+ onClose: () => setLightboxIndex(null),
6708
+ onPrev: () => setLightboxIndex(
6709
+ (prev) => prev !== null ? (prev - 1 + lightboxImages.length) % lightboxImages.length : null
6710
+ ),
6711
+ onNext: () => setLightboxIndex(
6712
+ (prev) => prev !== null ? (prev + 1) % lightboxImages.length : null
6713
+ )
6714
+ }
6715
+ )
6716
+ ] });
6717
+ }
6445
6718
  function ChatMessage({
6446
6719
  message,
6447
6720
  enableMarkdown = true,
6448
6721
  showToolCalls = true,
6449
- toolCallVerbosity: _toolCallVerbosity,
6722
+ toolCallVerbosity = "compact",
6450
6723
  className,
6451
6724
  markdownComponents,
6452
6725
  markdownUrlTransform,
@@ -6520,21 +6793,16 @@ function ChatMessage({
6520
6793
  ),
6521
6794
  showThinking && message.thinkingBlocks && message.thinkingBlocks.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(ThinkingBlockList, { blocks: message.thinkingBlocks }),
6522
6795
  showDelegation && message.delegationEvents && message.delegationEvents.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(DelegationProgressList, { events: message.delegationEvents }),
6523
- showToolCalls && message.toolCalls && message.toolCalls.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: message.toolCalls.map((toolCall) => {
6524
- const defaultRender = () => /* @__PURE__ */ jsxRuntime.jsx(
6525
- ToolCallEntry,
6526
- {
6527
- toolCall,
6528
- onImageClick
6529
- },
6530
- toolCall.id
6531
- );
6532
- if (renderToolCall) {
6533
- return /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderToolCall(toolCall, defaultRender) }, toolCall.id);
6796
+ showToolCalls && toolCallVerbosity !== "hidden" && message.toolCalls && message.toolCalls.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
6797
+ ToolCallList,
6798
+ {
6799
+ toolCalls: message.toolCalls,
6800
+ toolCallVerbosity,
6801
+ onImageClick,
6802
+ renderToolCall
6534
6803
  }
6535
- return defaultRender();
6536
- }) }),
6537
- showImages && message.images && message.images.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
6804
+ ),
6805
+ showImages && !showToolCalls && message.images && message.images.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
6538
6806
  ImageGallery,
6539
6807
  {
6540
6808
  images: message.images,
@@ -6746,7 +7014,8 @@ function AgentChat({
6746
7014
  showImages = true,
6747
7015
  pollingInterval,
6748
7016
  pollingEnabled,
6749
- onDelegation
7017
+ onDelegation,
7018
+ onAttachmentsReady
6750
7019
  }) {
6751
7020
  const {
6752
7021
  messages,
@@ -6793,6 +7062,11 @@ function AgentChat({
6793
7062
  console.error("[AgentChat] Attachment error:", msg);
6794
7063
  }
6795
7064
  });
7065
+ react.useEffect(() => {
7066
+ if (enableAttachments && onAttachmentsReady) {
7067
+ onAttachmentsReady({ addExternalFile: attachments.addExternalFile });
7068
+ }
7069
+ }, [enableAttachments, onAttachmentsReady, attachments.addExternalFile]);
6796
7070
  const handleSend = react.useCallback(
6797
7071
  async (content) => {
6798
7072
  if (enableAttachments && attachments.hasReadyFiles) {