@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.
@@ -1,6 +1,7 @@
1
1
  import { createContext, forwardRef, useState, useRef, useImperativeHandle, useCallback, useEffect, useMemo, useContext } from 'react';
2
2
  import { fetchEventSource } from '@microsoft/fetch-event-source';
3
3
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
+ import { createPortal } from 'react-dom';
4
5
  import ReactMarkdown from 'react-markdown';
5
6
  import remarkGfm from 'remark-gfm';
6
7
 
@@ -4025,6 +4026,24 @@ function useFileAttachments(options) {
4025
4026
  console.log("[useFileAttachments] prepareForSubmit result:", aiUuids);
4026
4027
  return aiUuids;
4027
4028
  }, [onSubmitUpload, onError]);
4029
+ const addExternalFile = useCallback(
4030
+ (opts) => {
4031
+ const category = opts.category || (opts.mimeType.startsWith("image/") ? "image" : "document");
4032
+ const placeholderFile = new File([], opts.filename, { type: opts.mimeType });
4033
+ const newAttachment = {
4034
+ uuid: opts.uuid,
4035
+ file: placeholderFile,
4036
+ status: "ready",
4037
+ progress: 100,
4038
+ category,
4039
+ previewUrl: opts.previewUrl,
4040
+ hasThumbnail: category === "image",
4041
+ actualSize: opts.size
4042
+ };
4043
+ setFiles((prev) => [...prev, newAttachment]);
4044
+ },
4045
+ []
4046
+ );
4028
4047
  const clear = useCallback(() => {
4029
4048
  for (const file of filesRef.current) {
4030
4049
  if (file.previewUrl) {
@@ -4050,6 +4069,7 @@ function useFileAttachments(options) {
4050
4069
  files,
4051
4070
  dropZoneProps,
4052
4071
  removeFile,
4072
+ addExternalFile,
4053
4073
  getReadyUuids,
4054
4074
  getReadyUrls,
4055
4075
  prepareForSubmit,
@@ -5765,23 +5785,45 @@ function ImageLightbox({
5765
5785
  }) {
5766
5786
  const currentImage = images[currentIndex];
5767
5787
  const hasMultiple = images.length > 1;
5788
+ const [container, setContainer] = useState(null);
5789
+ useEffect(() => {
5790
+ const el = document.createElement("div");
5791
+ el.id = "cadra-lightbox-root";
5792
+ el.style.position = "fixed";
5793
+ el.style.inset = "0";
5794
+ el.style.zIndex = "999999";
5795
+ el.style.pointerEvents = "auto";
5796
+ document.body.appendChild(el);
5797
+ setContainer(el);
5798
+ return () => {
5799
+ document.body.removeChild(el);
5800
+ };
5801
+ }, []);
5802
+ const onCloseRef = useRef(onClose);
5803
+ const onPrevRef = useRef(onPrev);
5804
+ const onNextRef = useRef(onNext);
5805
+ onCloseRef.current = onClose;
5806
+ onPrevRef.current = onPrev;
5807
+ onNextRef.current = onNext;
5768
5808
  useEffect(() => {
5769
5809
  function handleKeyDown(e) {
5770
5810
  switch (e.key) {
5771
5811
  case "Escape":
5772
- onClose();
5812
+ e.preventDefault();
5813
+ e.stopPropagation();
5814
+ onCloseRef.current();
5773
5815
  break;
5774
5816
  case "ArrowLeft":
5775
- if (hasMultiple) onPrev();
5817
+ if (hasMultiple) onPrevRef.current();
5776
5818
  break;
5777
5819
  case "ArrowRight":
5778
- if (hasMultiple) onNext();
5820
+ if (hasMultiple) onNextRef.current();
5779
5821
  break;
5780
5822
  }
5781
5823
  }
5782
- document.addEventListener("keydown", handleKeyDown);
5783
- return () => document.removeEventListener("keydown", handleKeyDown);
5784
- }, [onClose, onPrev, onNext, hasMultiple]);
5824
+ document.addEventListener("keydown", handleKeyDown, true);
5825
+ return () => document.removeEventListener("keydown", handleKeyDown, true);
5826
+ }, [hasMultiple]);
5785
5827
  useEffect(() => {
5786
5828
  const originalOverflow = document.body.style.overflow;
5787
5829
  document.body.style.overflow = "hidden";
@@ -5789,97 +5831,143 @@ function ImageLightbox({
5789
5831
  document.body.style.overflow = originalOverflow;
5790
5832
  };
5791
5833
  }, []);
5792
- const handleDownload = useCallback(() => {
5834
+ useEffect(() => {
5835
+ if (!container) return;
5836
+ function handleClick(e) {
5837
+ const target = e.target;
5838
+ if (target.dataset.lightboxBackdrop === "true") {
5839
+ e.preventDefault();
5840
+ e.stopPropagation();
5841
+ onCloseRef.current();
5842
+ }
5843
+ }
5844
+ container.addEventListener("click", handleClick, true);
5845
+ return () => container.removeEventListener("click", handleClick, true);
5846
+ }, [container]);
5847
+ const handleDownload = useCallback(async () => {
5793
5848
  if (!currentImage) return;
5794
- const link = document.createElement("a");
5795
- link.href = currentImage.url;
5796
- link.download = currentImage.filename || `image-${currentIndex + 1}`;
5797
- link.target = "_blank";
5798
- link.rel = "noopener noreferrer";
5799
- document.body.appendChild(link);
5800
- link.click();
5801
- document.body.removeChild(link);
5802
- }, [currentImage, currentIndex]);
5803
- if (!currentImage) return null;
5804
- return /* @__PURE__ */ jsxs(
5805
- "div",
5806
- {
5807
- className: `fixed inset-0 z-50 flex items-center justify-center bg-black/80 ${className || ""}`,
5808
- onClick: onClose,
5809
- children: [
5810
- /* @__PURE__ */ jsxs("div", { className: "absolute top-0 left-0 right-0 flex items-center justify-between p-4 z-10", children: [
5811
- /* @__PURE__ */ jsx("div", { className: "text-white text-sm", children: hasMultiple && /* @__PURE__ */ jsxs("span", { children: [
5812
- currentIndex + 1,
5813
- " / ",
5814
- images.length
5815
- ] }) }),
5816
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
5817
- /* @__PURE__ */ jsx(
5818
- "button",
5819
- {
5820
- type: "button",
5821
- onClick: (e) => {
5822
- e.stopPropagation();
5823
- handleDownload();
5824
- },
5825
- className: "p-2 rounded-full bg-black/50 hover:bg-black/70 text-white transition-colors",
5826
- "aria-label": "Download image",
5827
- children: /* @__PURE__ */ jsx(DownloadIcon, { className: "h-5 w-5" })
5828
- }
5829
- ),
5830
- /* @__PURE__ */ jsx(
5831
- "button",
5832
- {
5833
- type: "button",
5834
- onClick: (e) => {
5835
- e.stopPropagation();
5836
- onClose();
5837
- },
5838
- className: "p-2 rounded-full bg-black/50 hover:bg-black/70 text-white transition-colors",
5839
- "aria-label": "Close lightbox",
5840
- children: /* @__PURE__ */ jsx(XIcon, { className: "h-5 w-5" })
5841
- }
5842
- )
5843
- ] })
5844
- ] }),
5845
- hasMultiple && /* @__PURE__ */ jsx(
5846
- "button",
5847
- {
5848
- type: "button",
5849
- onClick: (e) => {
5850
- e.stopPropagation();
5851
- onPrev();
5852
- },
5853
- 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",
5854
- "aria-label": "Previous image",
5855
- children: /* @__PURE__ */ jsx(ChevronLeftIcon, { className: "h-6 w-6" })
5856
- }
5857
- ),
5858
- /* @__PURE__ */ jsx(
5859
- "img",
5860
- {
5861
- src: currentImage.url,
5862
- alt: currentImage.alt || `Image ${currentIndex + 1}`,
5863
- className: "max-w-[90vw] max-h-[85vh] object-contain",
5864
- onClick: (e) => e.stopPropagation()
5865
- }
5866
- ),
5867
- hasMultiple && /* @__PURE__ */ jsx(
5868
- "button",
5869
- {
5870
- type: "button",
5871
- onClick: (e) => {
5872
- e.stopPropagation();
5873
- onNext();
5874
- },
5875
- 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",
5876
- "aria-label": "Next image",
5877
- children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: "h-6 w-6" })
5878
- }
5879
- ),
5880
- currentImage.alt && /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 right-0 p-4 text-center z-10", children: /* @__PURE__ */ jsx("p", { className: "text-white text-sm bg-black/50 inline-block px-3 py-1 rounded-full", children: currentImage.alt }) })
5881
- ]
5849
+ const filename = currentImage.filename || `image-${currentIndex + 1}`;
5850
+ try {
5851
+ const response = await fetch(currentImage.url);
5852
+ const blob = await response.blob();
5853
+ const blobUrl = URL.createObjectURL(blob);
5854
+ const link = document.createElement("a");
5855
+ link.href = blobUrl;
5856
+ link.download = filename;
5857
+ document.body.appendChild(link);
5858
+ link.click();
5859
+ document.body.removeChild(link);
5860
+ URL.revokeObjectURL(blobUrl);
5861
+ } catch {
5862
+ window.open(currentImage.url, "_blank");
5882
5863
  }
5864
+ }, [currentImage, currentIndex]);
5865
+ const handleClose = useCallback(() => {
5866
+ onCloseRef.current();
5867
+ }, []);
5868
+ if (!currentImage || !container) return null;
5869
+ return createPortal(
5870
+ /* @__PURE__ */ jsxs(
5871
+ "div",
5872
+ {
5873
+ "data-lightbox-backdrop": "true",
5874
+ className: `absolute inset-0 flex items-center justify-center bg-black/90 ${className || ""}`,
5875
+ style: { cursor: "zoom-out" },
5876
+ children: [
5877
+ /* @__PURE__ */ jsxs(
5878
+ "div",
5879
+ {
5880
+ className: "absolute top-0 left-0 right-0 flex items-center justify-between p-4",
5881
+ style: { zIndex: 10 },
5882
+ children: [
5883
+ /* @__PURE__ */ jsx("div", { className: "text-white text-sm", children: hasMultiple && /* @__PURE__ */ jsxs("span", { children: [
5884
+ currentIndex + 1,
5885
+ " / ",
5886
+ images.length
5887
+ ] }) }),
5888
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
5889
+ /* @__PURE__ */ jsx(
5890
+ "button",
5891
+ {
5892
+ type: "button",
5893
+ onClick: (e) => {
5894
+ e.stopPropagation();
5895
+ handleDownload();
5896
+ },
5897
+ className: "p-2 rounded-full bg-white/10 hover:bg-white/20 text-white transition-colors",
5898
+ style: { cursor: "pointer", pointerEvents: "auto" },
5899
+ "aria-label": "Download image",
5900
+ children: /* @__PURE__ */ jsx(DownloadIcon, { className: "h-5 w-5" })
5901
+ }
5902
+ ),
5903
+ /* @__PURE__ */ jsx(
5904
+ "button",
5905
+ {
5906
+ type: "button",
5907
+ onClick: (e) => {
5908
+ e.stopPropagation();
5909
+ handleClose();
5910
+ },
5911
+ className: "p-2 rounded-full bg-white/10 hover:bg-white/20 text-white transition-colors",
5912
+ style: { cursor: "pointer", pointerEvents: "auto" },
5913
+ "aria-label": "Close lightbox",
5914
+ children: /* @__PURE__ */ jsx(XIcon, { className: "h-5 w-5" })
5915
+ }
5916
+ )
5917
+ ] })
5918
+ ]
5919
+ }
5920
+ ),
5921
+ hasMultiple && /* @__PURE__ */ jsx(
5922
+ "button",
5923
+ {
5924
+ type: "button",
5925
+ onClick: (e) => {
5926
+ e.stopPropagation();
5927
+ onPrev();
5928
+ },
5929
+ 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",
5930
+ style: { zIndex: 10, cursor: "pointer", pointerEvents: "auto" },
5931
+ "aria-label": "Previous image",
5932
+ children: /* @__PURE__ */ jsx(ChevronLeftIcon, { className: "h-6 w-6" })
5933
+ }
5934
+ ),
5935
+ /* @__PURE__ */ jsx(
5936
+ "img",
5937
+ {
5938
+ src: currentImage.url,
5939
+ alt: currentImage.alt || `Image ${currentIndex + 1}`,
5940
+ className: "max-w-[90vw] max-h-[85vh] object-contain select-none",
5941
+ style: { cursor: "default", pointerEvents: "auto" },
5942
+ draggable: false
5943
+ }
5944
+ ),
5945
+ hasMultiple && /* @__PURE__ */ jsx(
5946
+ "button",
5947
+ {
5948
+ type: "button",
5949
+ onClick: (e) => {
5950
+ e.stopPropagation();
5951
+ onNext();
5952
+ },
5953
+ 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",
5954
+ style: { zIndex: 10, cursor: "pointer", pointerEvents: "auto" },
5955
+ "aria-label": "Next image",
5956
+ children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: "h-6 w-6" })
5957
+ }
5958
+ ),
5959
+ currentImage.alt && /* @__PURE__ */ jsx(
5960
+ "div",
5961
+ {
5962
+ className: "absolute bottom-0 left-0 right-0 p-4 text-center",
5963
+ style: { zIndex: 10, pointerEvents: "none" },
5964
+ children: /* @__PURE__ */ jsx("p", { className: "text-white text-sm bg-black/50 inline-block px-3 py-1 rounded-full", children: currentImage.alt })
5965
+ }
5966
+ )
5967
+ ]
5968
+ }
5969
+ ),
5970
+ container
5883
5971
  );
5884
5972
  }
5885
5973
  function cn7(...classes) {
@@ -5892,12 +5980,12 @@ function ImageWithLoading({ src, alt, className, onClick }) {
5892
5980
  "div",
5893
5981
  {
5894
5982
  className: cn7(
5895
- "relative overflow-hidden bg-gray-100 dark:bg-gray-800",
5983
+ "relative overflow-hidden rounded-lg",
5896
5984
  className
5897
5985
  ),
5898
5986
  children: [
5899
- !isLoaded && !hasError && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 animate-pulse bg-gray-200 dark:bg-gray-700" }),
5900
- hasError && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center text-gray-400 dark:text-gray-500 text-sm", children: "Failed to load" }),
5987
+ !isLoaded && !hasError && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 animate-pulse bg-gray-200 dark:bg-gray-700 rounded-lg" }),
5988
+ hasError && /* @__PURE__ */ 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" }),
5901
5989
  /* @__PURE__ */ jsx(
5902
5990
  "img",
5903
5991
  {
@@ -5905,7 +5993,7 @@ function ImageWithLoading({ src, alt, className, onClick }) {
5905
5993
  alt,
5906
5994
  loading: "lazy",
5907
5995
  className: cn7(
5908
- "w-full h-auto max-h-[300px] object-contain transition-opacity duration-200",
5996
+ "w-full h-auto max-h-[280px] object-cover rounded-lg shadow-sm transition-opacity duration-200",
5909
5997
  isLoaded ? "opacity-100" : "opacity-0"
5910
5998
  ),
5911
5999
  onLoad: () => setIsLoaded(true),
@@ -5962,7 +6050,7 @@ function ImageGallery({
5962
6050
  {
5963
6051
  type: "button",
5964
6052
  onClick: () => handleImageClick(image, index),
5965
- 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",
6053
+ className: "block rounded-lg overflow-hidden transition-transform hover:scale-[1.02] text-left",
5966
6054
  children: /* @__PURE__ */ jsx(
5967
6055
  ImageWithLoading,
5968
6056
  {
@@ -6173,7 +6261,8 @@ var INTERNAL_TOOL_NAMES = /* @__PURE__ */ new Set([
6173
6261
  "check_task_status",
6174
6262
  "assign_task",
6175
6263
  "update_plan",
6176
- "check_delegations"
6264
+ "check_delegations",
6265
+ "activate_skill"
6177
6266
  ]);
6178
6267
  function isInternalTool(toolName) {
6179
6268
  if (!toolName) return false;
@@ -6192,6 +6281,15 @@ function cn10(...classes) {
6192
6281
  }
6193
6282
  function formatToolName(toolName) {
6194
6283
  if (!toolName) return toolName;
6284
+ if (toolName.includes(".")) {
6285
+ const segments = toolName.split(".");
6286
+ const uniqueSegments = segments.filter(
6287
+ (seg, idx) => idx === 0 || seg.toLowerCase().trim() !== segments[idx - 1]?.toLowerCase().trim()
6288
+ );
6289
+ const bestSegment = uniqueSegments[uniqueSegments.length - 1]?.trim() || toolName;
6290
+ if (bestSegment.includes(" ")) return bestSegment;
6291
+ return formatToolName(bestSegment);
6292
+ }
6195
6293
  const prefixesToRemove = [
6196
6294
  "skills_api_",
6197
6295
  "tools_api_",
@@ -6296,6 +6394,7 @@ function ToolCallEntry({
6296
6394
  compact = false
6297
6395
  }) {
6298
6396
  const [isExpanded, setIsExpanded] = useState(false);
6397
+ const [lightboxIndex, setLightboxIndex] = useState(null);
6299
6398
  const formattedName = useMemo(
6300
6399
  () => formatToolName(toolCall.name),
6301
6400
  [toolCall.name]
@@ -6304,6 +6403,14 @@ function ToolCallEntry({
6304
6403
  () => toolCall.status === "completed" && toolCall.result ? extractImagesFromResult2(toolCall.result) : [],
6305
6404
  [toolCall.status, toolCall.result]
6306
6405
  );
6406
+ const lightboxImages = useMemo(
6407
+ () => resultImages.map((img) => ({
6408
+ url: img.url,
6409
+ alt: img.alt || img.prompt,
6410
+ filename: img.alt || img.prompt
6411
+ })),
6412
+ [resultImages]
6413
+ );
6307
6414
  const hasImages = resultImages.length > 0;
6308
6415
  const statusIcon = useMemo(() => {
6309
6416
  switch (toolCall.status) {
@@ -6373,47 +6480,56 @@ function ToolCallEntry({
6373
6480
  ]
6374
6481
  }
6375
6482
  ),
6376
- hasImages && /* @__PURE__ */ jsx("div", { className: "px-3 pb-2", children: /* @__PURE__ */ jsx("div", { className: cn10(
6377
- "grid gap-2",
6378
- resultImages.length === 1 ? "grid-cols-1" : "grid-cols-2"
6379
- ), children: resultImages.map((img, idx) => {
6380
- const altText = img.alt || img.prompt || `Generated image ${idx + 1}`;
6381
- const imageElement = /* @__PURE__ */ jsxs(Fragment, { children: [
6382
- /* @__PURE__ */ jsx(
6383
- "img",
6384
- {
6385
- src: img.thumbnailUrl || img.url,
6386
- alt: altText,
6387
- className: "w-full h-auto max-h-[300px] object-contain bg-gray-50 dark:bg-gray-900",
6388
- loading: "lazy"
6389
- }
6390
- ),
6391
- img.prompt && /* @__PURE__ */ 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 })
6392
- ] });
6393
- if (onImageClick) {
6483
+ hasImages && /* @__PURE__ */ jsxs("div", { className: "px-3 pb-3", children: [
6484
+ /* @__PURE__ */ jsx("div", { className: cn10(
6485
+ "grid gap-3",
6486
+ resultImages.length === 1 ? "grid-cols-1 max-w-sm" : resultImages.length === 2 ? "grid-cols-2" : "grid-cols-2 sm:grid-cols-3"
6487
+ ), children: resultImages.map((img, idx) => {
6488
+ const altText = img.alt || img.prompt || `Generated image ${idx + 1}`;
6489
+ const imageElement = /* @__PURE__ */ jsxs("div", { className: "group relative", children: [
6490
+ /* @__PURE__ */ jsx(
6491
+ "img",
6492
+ {
6493
+ src: img.thumbnailUrl || img.url,
6494
+ alt: altText,
6495
+ className: "w-full h-auto max-h-[280px] object-cover rounded-lg shadow-sm",
6496
+ loading: "lazy"
6497
+ }
6498
+ ),
6499
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 rounded-lg bg-black/0 group-hover:bg-black/10 dark:group-hover:bg-white/5 transition-colors" })
6500
+ ] });
6394
6501
  return /* @__PURE__ */ jsx(
6395
6502
  "button",
6396
6503
  {
6397
6504
  type: "button",
6398
- onClick: () => onImageClick({ url: img.url, alt: altText, filename: altText }),
6399
- 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",
6505
+ onClick: () => {
6506
+ if (onImageClick) {
6507
+ onImageClick({ url: img.url, alt: altText, filename: altText });
6508
+ } else {
6509
+ setLightboxIndex(idx);
6510
+ }
6511
+ },
6512
+ className: "block rounded-lg overflow-hidden cursor-pointer text-left transition-transform hover:scale-[1.02]",
6400
6513
  children: imageElement
6401
6514
  },
6402
6515
  idx
6403
6516
  );
6404
- }
6405
- return /* @__PURE__ */ jsx(
6406
- "a",
6517
+ }) }),
6518
+ lightboxIndex !== null && !onImageClick && /* @__PURE__ */ jsx(
6519
+ ImageLightbox,
6407
6520
  {
6408
- href: img.url,
6409
- target: "_blank",
6410
- rel: "noopener noreferrer",
6411
- 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",
6412
- children: imageElement
6413
- },
6414
- idx
6415
- );
6416
- }) }) }),
6521
+ images: lightboxImages,
6522
+ currentIndex: lightboxIndex,
6523
+ onClose: () => setLightboxIndex(null),
6524
+ onPrev: () => setLightboxIndex(
6525
+ (prev) => prev !== null ? (prev - 1 + lightboxImages.length) % lightboxImages.length : null
6526
+ ),
6527
+ onNext: () => setLightboxIndex(
6528
+ (prev) => prev !== null ? (prev + 1) % lightboxImages.length : null
6529
+ )
6530
+ }
6531
+ )
6532
+ ] }),
6417
6533
  isExpanded && /* @__PURE__ */ jsxs("div", { className: "border-t border-gray-200 dark:border-gray-700 px-3 py-2 space-y-2", children: [
6418
6534
  toolCall.args && Object.keys(toolCall.args).length > 0 && /* @__PURE__ */ jsxs("div", { children: [
6419
6535
  /* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-gray-500 dark:text-gray-400 mb-1", children: "Input" }),
@@ -6435,11 +6551,168 @@ function ToolCallEntry({
6435
6551
  function cn11(...classes) {
6436
6552
  return classes.filter(Boolean).join(" ");
6437
6553
  }
6554
+ function normalizeToolNameForGrouping(name) {
6555
+ const lastSegment = name.includes(".") ? name.split(".").pop() : name;
6556
+ return lastSegment.toLowerCase().replace(/[^a-z0-9]/g, "");
6557
+ }
6558
+ function groupToolCalls(toolCalls) {
6559
+ const byNormalizedName = /* @__PURE__ */ new Map();
6560
+ const nameOrder = [];
6561
+ for (const tc of toolCalls) {
6562
+ const key = normalizeToolNameForGrouping(tc.name);
6563
+ if (!byNormalizedName.has(key)) {
6564
+ byNormalizedName.set(key, []);
6565
+ nameOrder.push(key);
6566
+ }
6567
+ byNormalizedName.get(key).push(tc);
6568
+ }
6569
+ const groups = [];
6570
+ for (const key of nameOrder) {
6571
+ const calls = byNormalizedName.get(key);
6572
+ if (calls.length === 1) {
6573
+ groups.push({ type: "single", toolCall: calls[0] });
6574
+ } else {
6575
+ groups.push({ type: "group", name: calls[0].name, toolCalls: calls });
6576
+ }
6577
+ }
6578
+ return groups;
6579
+ }
6580
+ function ToolCallList({ toolCalls, toolCallVerbosity, onImageClick, renderToolCall }) {
6581
+ const visibleToolCalls = useMemo(() => {
6582
+ if (toolCallVerbosity !== "compact") return toolCalls;
6583
+ return toolCalls.filter((tc) => !isInternalTool(tc.name));
6584
+ }, [toolCalls, toolCallVerbosity]);
6585
+ const grouped = useMemo(() => groupToolCalls(visibleToolCalls), [visibleToolCalls]);
6586
+ return /* @__PURE__ */ jsx("div", { className: "space-y-2", children: grouped.map((group) => {
6587
+ if (group.type === "single") {
6588
+ const toolCall = group.toolCall;
6589
+ const defaultRender = () => /* @__PURE__ */ jsx(
6590
+ ToolCallEntry,
6591
+ {
6592
+ toolCall,
6593
+ onImageClick
6594
+ },
6595
+ toolCall.id
6596
+ );
6597
+ if (renderToolCall) {
6598
+ return /* @__PURE__ */ jsx("div", { children: renderToolCall(toolCall, defaultRender) }, toolCall.id);
6599
+ }
6600
+ return /* @__PURE__ */ jsx("div", { children: defaultRender() }, toolCall.id);
6601
+ }
6602
+ const completed = group.toolCalls.filter((tc) => tc.status === "completed" || tc.status === "failed");
6603
+ const running = group.toolCalls.filter((tc) => tc.status !== "completed" && tc.status !== "failed");
6604
+ const allImages = completed.flatMap(
6605
+ (tc) => tc.result ? extractImagesFromResult2(tc.result) : []
6606
+ );
6607
+ const hasImages = allImages.length > 0;
6608
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
6609
+ running.length > 0 && running[0] && /* @__PURE__ */ jsx(
6610
+ ToolCallEntry,
6611
+ {
6612
+ toolCall: {
6613
+ ...running[0],
6614
+ name: running.length > 1 ? `${running[0].name} (${running.length})` : running[0].name
6615
+ },
6616
+ onImageClick
6617
+ },
6618
+ running[0].id
6619
+ ),
6620
+ completed.length > 0 && hasImages && /* @__PURE__ */ jsx(
6621
+ GroupedImageGallery,
6622
+ {
6623
+ images: allImages,
6624
+ completedCount: completed.length,
6625
+ onImageClick
6626
+ }
6627
+ ),
6628
+ completed.length > 0 && !hasImages && completed.map((tc) => {
6629
+ const defaultRender = () => /* @__PURE__ */ jsx(ToolCallEntry, { toolCall: tc, onImageClick }, tc.id);
6630
+ if (renderToolCall) {
6631
+ return /* @__PURE__ */ jsx("div", { children: renderToolCall(tc, defaultRender) }, tc.id);
6632
+ }
6633
+ return /* @__PURE__ */ jsx("div", { children: defaultRender() }, tc.id);
6634
+ })
6635
+ ] }, `group-${group.name}-${group.toolCalls[0]?.id}`);
6636
+ }) });
6637
+ }
6638
+ function GroupedImageGallery({
6639
+ images,
6640
+ completedCount,
6641
+ onImageClick
6642
+ }) {
6643
+ const [lightboxIndex, setLightboxIndex] = useState(null);
6644
+ const lightboxImages = useMemo(
6645
+ () => images.map((img) => ({
6646
+ url: img.url,
6647
+ alt: img.alt || img.prompt,
6648
+ filename: img.alt || img.prompt
6649
+ })),
6650
+ [images]
6651
+ );
6652
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
6653
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400 px-1", children: [
6654
+ /* @__PURE__ */ jsx("span", { className: "h-4 w-4 flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ 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__ */ jsx("polyline", { points: "20 6 9 17 4 12" }) }) }),
6655
+ /* @__PURE__ */ jsxs("span", { children: [
6656
+ completedCount,
6657
+ " image",
6658
+ completedCount !== 1 ? "s" : "",
6659
+ " generated"
6660
+ ] })
6661
+ ] }),
6662
+ /* @__PURE__ */ jsx("div", { className: cn11(
6663
+ "grid gap-3",
6664
+ images.length === 1 ? "grid-cols-1 max-w-sm" : images.length === 2 ? "grid-cols-2" : "grid-cols-2 sm:grid-cols-3"
6665
+ ), children: images.map((img, idx) => {
6666
+ const altText = img.alt || img.prompt || `Generated image ${idx + 1}`;
6667
+ return /* @__PURE__ */ jsx(
6668
+ "button",
6669
+ {
6670
+ type: "button",
6671
+ onClick: () => {
6672
+ if (onImageClick) {
6673
+ onImageClick({ url: img.url, alt: altText, filename: altText });
6674
+ } else {
6675
+ setLightboxIndex(idx);
6676
+ }
6677
+ },
6678
+ className: "block rounded-lg overflow-hidden cursor-pointer text-left transition-transform hover:scale-[1.02] group",
6679
+ children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
6680
+ /* @__PURE__ */ jsx(
6681
+ "img",
6682
+ {
6683
+ src: img.thumbnailUrl || img.url,
6684
+ alt: altText,
6685
+ className: "w-full h-auto max-h-[280px] object-cover rounded-lg shadow-sm",
6686
+ loading: "lazy"
6687
+ }
6688
+ ),
6689
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 rounded-lg bg-black/0 group-hover:bg-black/10 dark:group-hover:bg-white/5 transition-colors" })
6690
+ ] })
6691
+ },
6692
+ `${img.url}-${idx}`
6693
+ );
6694
+ }) }),
6695
+ lightboxIndex !== null && !onImageClick && /* @__PURE__ */ jsx(
6696
+ ImageLightbox,
6697
+ {
6698
+ images: lightboxImages,
6699
+ currentIndex: lightboxIndex,
6700
+ onClose: () => setLightboxIndex(null),
6701
+ onPrev: () => setLightboxIndex(
6702
+ (prev) => prev !== null ? (prev - 1 + lightboxImages.length) % lightboxImages.length : null
6703
+ ),
6704
+ onNext: () => setLightboxIndex(
6705
+ (prev) => prev !== null ? (prev + 1) % lightboxImages.length : null
6706
+ )
6707
+ }
6708
+ )
6709
+ ] });
6710
+ }
6438
6711
  function ChatMessage({
6439
6712
  message,
6440
6713
  enableMarkdown = true,
6441
6714
  showToolCalls = true,
6442
- toolCallVerbosity: _toolCallVerbosity,
6715
+ toolCallVerbosity = "compact",
6443
6716
  className,
6444
6717
  markdownComponents,
6445
6718
  markdownUrlTransform,
@@ -6513,21 +6786,16 @@ function ChatMessage({
6513
6786
  ),
6514
6787
  showThinking && message.thinkingBlocks && message.thinkingBlocks.length > 0 && /* @__PURE__ */ jsx(ThinkingBlockList, { blocks: message.thinkingBlocks }),
6515
6788
  showDelegation && message.delegationEvents && message.delegationEvents.length > 0 && /* @__PURE__ */ jsx(DelegationProgressList, { events: message.delegationEvents }),
6516
- showToolCalls && message.toolCalls && message.toolCalls.length > 0 && /* @__PURE__ */ jsx("div", { className: "space-y-2", children: message.toolCalls.map((toolCall) => {
6517
- const defaultRender = () => /* @__PURE__ */ jsx(
6518
- ToolCallEntry,
6519
- {
6520
- toolCall,
6521
- onImageClick
6522
- },
6523
- toolCall.id
6524
- );
6525
- if (renderToolCall) {
6526
- return /* @__PURE__ */ jsx("div", { children: renderToolCall(toolCall, defaultRender) }, toolCall.id);
6789
+ showToolCalls && toolCallVerbosity !== "hidden" && message.toolCalls && message.toolCalls.length > 0 && /* @__PURE__ */ jsx(
6790
+ ToolCallList,
6791
+ {
6792
+ toolCalls: message.toolCalls,
6793
+ toolCallVerbosity,
6794
+ onImageClick,
6795
+ renderToolCall
6527
6796
  }
6528
- return defaultRender();
6529
- }) }),
6530
- showImages && message.images && message.images.length > 0 && /* @__PURE__ */ jsx(
6797
+ ),
6798
+ showImages && !showToolCalls && message.images && message.images.length > 0 && /* @__PURE__ */ jsx(
6531
6799
  ImageGallery,
6532
6800
  {
6533
6801
  images: message.images,
@@ -6739,7 +7007,8 @@ function AgentChat({
6739
7007
  showImages = true,
6740
7008
  pollingInterval,
6741
7009
  pollingEnabled,
6742
- onDelegation
7010
+ onDelegation,
7011
+ onAttachmentsReady
6743
7012
  }) {
6744
7013
  const {
6745
7014
  messages,
@@ -6786,6 +7055,11 @@ function AgentChat({
6786
7055
  console.error("[AgentChat] Attachment error:", msg);
6787
7056
  }
6788
7057
  });
7058
+ useEffect(() => {
7059
+ if (enableAttachments && onAttachmentsReady) {
7060
+ onAttachmentsReady({ addExternalFile: attachments.addExternalFile });
7061
+ }
7062
+ }, [enableAttachments, onAttachmentsReady, attachments.addExternalFile]);
6789
7063
  const handleSend = useCallback(
6790
7064
  async (content) => {
6791
7065
  if (enableAttachments && attachments.hasReadyFiles) {