@tutti-os/agent-gui 0.0.76 → 0.0.78

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.
@@ -260,17 +260,16 @@ import {
260
260
  useContext as useContext2,
261
261
  memo,
262
262
  useMemo,
263
- useRef as useRef3,
263
+ useRef as useRef4,
264
264
  useState as useState4
265
265
  } from "react";
266
266
  import { Check, Copy, FileText } from "lucide-react";
267
267
 
268
268
  // app/renderer/components/ZoomableImage.tsx
269
269
  import {
270
- cloneElement,
271
- isValidElement,
272
270
  useCallback as useCallback2,
273
271
  useEffect,
272
+ useRef as useRef2,
274
273
  useState as useState2
275
274
  } from "react";
276
275
  import { createPortal } from "react-dom";
@@ -285,7 +284,6 @@ import {
285
284
  RestoreIcon
286
285
  } from "@tutti-os/ui-system";
287
286
  import { RotateCcwIcon, ZoomInIcon, ZoomOutIcon } from "lucide-react";
288
- import Zoom from "react-medium-image-zoom";
289
287
 
290
288
  // shared/agentConversation/components/ConversationImageContextMenu.tsx
291
289
  import {
@@ -436,12 +434,60 @@ function ZoomableImage({
436
434
  const [imagePreviewZoom, setImagePreviewZoom] = useState2(1);
437
435
  const [isWheelZooming, setIsWheelZooming] = useState2(false);
438
436
  const [isImagePreviewOpen, setIsImagePreviewOpen] = useState2(false);
437
+ const [isImagePreviewClosing, setIsImagePreviewClosing] = useState2(false);
438
+ const closePreviewTimerRef = useRef2(null);
439
439
  const imagePreviewZoomPercent = Math.round(imagePreviewZoom * 100);
440
440
  const canZoomOut = imagePreviewZoom > IMAGE_PREVIEW_ZOOM_MIN;
441
441
  const canZoomIn = imagePreviewZoom < IMAGE_PREVIEW_ZOOM_MAX;
442
442
  const closeContextMenu = useCallback2(() => {
443
443
  setContextMenuPosition(null);
444
444
  }, []);
445
+ const finishClosePreviewImage = useCallback2(() => {
446
+ if (closePreviewTimerRef.current !== null) {
447
+ window.clearTimeout(closePreviewTimerRef.current);
448
+ closePreviewTimerRef.current = null;
449
+ }
450
+ setIsImagePreviewOpen(false);
451
+ setIsImagePreviewClosing(false);
452
+ setIsWheelZooming(false);
453
+ setImagePreviewZoom(1);
454
+ }, []);
455
+ const closePreviewImage = useCallback2(() => {
456
+ if (!isImagePreviewOpen) {
457
+ return;
458
+ }
459
+ if (closePreviewTimerRef.current !== null) {
460
+ window.clearTimeout(closePreviewTimerRef.current);
461
+ }
462
+ setIsImagePreviewClosing(true);
463
+ setIsWheelZooming(false);
464
+ setImagePreviewZoom(1);
465
+ closeContextMenu();
466
+ closePreviewTimerRef.current = window.setTimeout(
467
+ finishClosePreviewImage,
468
+ 180
469
+ );
470
+ }, [closeContextMenu, finishClosePreviewImage, isImagePreviewOpen]);
471
+ const openPreviewImage = useCallback2(() => {
472
+ if (!actionSource) {
473
+ return;
474
+ }
475
+ if (closePreviewTimerRef.current !== null) {
476
+ window.clearTimeout(closePreviewTimerRef.current);
477
+ closePreviewTimerRef.current = null;
478
+ }
479
+ closeContextMenu();
480
+ setIsImagePreviewClosing(false);
481
+ setIsImagePreviewOpen(true);
482
+ }, [actionSource, closeContextMenu]);
483
+ useEffect(
484
+ () => () => {
485
+ if (closePreviewTimerRef.current !== null) {
486
+ window.clearTimeout(closePreviewTimerRef.current);
487
+ }
488
+ },
489
+ []
490
+ );
445
491
  useEffect(() => {
446
492
  if (!contextMenuPosition) {
447
493
  return;
@@ -453,6 +499,23 @@ function ZoomableImage({
453
499
  document.removeEventListener("scroll", closeContextMenu, true);
454
500
  };
455
501
  }, [closeContextMenu, contextMenuPosition]);
502
+ useEffect(() => {
503
+ if (!isImagePreviewOpen) {
504
+ return;
505
+ }
506
+ const handleKeyDown = (event) => {
507
+ if (event.key !== "Escape") {
508
+ return;
509
+ }
510
+ event.preventDefault();
511
+ event.stopPropagation();
512
+ closePreviewImage();
513
+ };
514
+ document.addEventListener("keydown", handleKeyDown, true);
515
+ return () => {
516
+ document.removeEventListener("keydown", handleKeyDown, true);
517
+ };
518
+ }, [closePreviewImage, isImagePreviewOpen]);
456
519
  useEffect(() => {
457
520
  if (!copyStatus || copyStatus.busy) {
458
521
  return;
@@ -554,141 +617,80 @@ function ZoomableImage({
554
617
  onDownload: handleDownloadImage
555
618
  }
556
619
  ) : null;
557
- const renderZoomContent = ({
558
- buttonUnzoom,
559
- img,
560
- modalState
561
- }) => {
562
- const typedButtonUnzoom = buttonUnzoom;
563
- const buttonUnzoomProps = typedButtonUnzoom.props;
564
- const zoomSrc = isValidElement(img) && typeof img.props.src === "string" ? img.props.src : null;
565
- const isUnzooming = modalState === "UNLOADING";
566
- const effectiveImagePreviewZoom = isUnzooming ? 1 : imagePreviewZoom;
567
- const renderedImage = img ? cloneImageWithPreviewZoom(
568
- img,
569
- effectiveImagePreviewZoom,
570
- isUnzooming ? false : isWheelZooming,
571
- handlePreviewImageWheel
572
- ) : null;
573
- return /* @__PURE__ */ jsxs2(Fragment, { children: [
574
- actionButtons && renderedImage && zoomSrc ? cloneElement(
575
- renderedImage,
576
- {
577
- onContextMenu: handleContextMenu
578
- }
579
- ) : !actionButtons && renderedImage && zoomSrc ? /* @__PURE__ */ jsx2(
580
- ConversationImageContextMenu,
581
- {
582
- src: zoomSrc,
583
- asChild: true,
584
- contentStyle: { zIndex: "var(--z-dialog-popover)" },
585
- children: renderedImage
586
- }
587
- ) : renderedImage,
588
- /* @__PURE__ */ jsx2(
589
- ImagePreviewZoomControls,
590
- {
591
- canZoomIn,
592
- canZoomOut,
593
- percent: imagePreviewZoomPercent,
594
- percentLabel: t("common.imageZoomPercent", {
595
- percent: imagePreviewZoomPercent
596
- }),
597
- reportPercentStatus: !copyStatus,
598
- resetLabel: t("common.resetImageZoom"),
599
- zoomInLabel: t("common.zoomInImage"),
600
- zoomOutLabel: t("common.zoomOutImage"),
601
- onReset: resetPreviewImageZoom,
602
- onZoomIn: zoomInPreviewImage,
603
- onZoomOut: zoomOutPreviewImage,
604
- onWheel: handlePreviewImageWheel
605
- }
606
- ),
607
- actionButtons ? /* @__PURE__ */ jsx2("div", { className: "tsh-zoom-dialog__image-actions nodrag tsh-desktop-no-drag", children: actionButtons }) : null,
608
- contextMenuPosition?.inZoomDialog && actionButtons ? /* @__PURE__ */ jsx2(
609
- "div",
610
- {
611
- className: "tsh-image-context-menu nodrag tsh-desktop-no-drag",
612
- style: {
613
- left: contextMenuPosition.x,
614
- top: contextMenuPosition.y
615
- },
616
- role: "menu",
617
- onClick: (event) => event.stopPropagation(),
618
- children: /* @__PURE__ */ jsx2(
619
- ImageActionButtons,
620
+ const previewImage = (isImagePreviewOpen || isImagePreviewClosing) && actionSource ? /* @__PURE__ */ jsx2(
621
+ "img",
622
+ {
623
+ alt,
624
+ "data-rmiz-modal-img": "",
625
+ "data-tsh-image-zoom": formatImagePreviewZoom(imagePreviewZoom),
626
+ draggable: false,
627
+ src: actionSource,
628
+ title: typeof props.title === "string" ? props.title : void 0,
629
+ className: "tsh-zoom-dialog__image nodrag tsh-desktop-no-drag",
630
+ style: {
631
+ transform: resolveZoomedImageTransform(
632
+ void 0,
633
+ imagePreviewZoom,
634
+ null,
635
+ null
636
+ ),
637
+ transition: isWheelZooming ? "none" : mergeImagePreviewTransition(void 0)
638
+ },
639
+ onClick: (event) => {
640
+ event.stopPropagation();
641
+ closePreviewImage();
642
+ },
643
+ onContextMenu: hasImageActions ? handleContextMenu : onContextMenu,
644
+ onTransitionEnd: isImagePreviewClosing ? finishClosePreviewImage : void 0,
645
+ onWheel: handlePreviewImageWheel
646
+ }
647
+ ) : null;
648
+ const previewContent = previewImage && !actionButtons && actionSource ? /* @__PURE__ */ jsx2(
649
+ ConversationImageContextMenu,
650
+ {
651
+ src: actionSource,
652
+ asChild: true,
653
+ contentStyle: { zIndex: "var(--z-dialog-popover)" },
654
+ children: previewImage
655
+ }
656
+ ) : previewImage;
657
+ const Wrapper = wrapElement;
658
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
659
+ /* @__PURE__ */ jsxs2(
660
+ Wrapper,
661
+ {
662
+ className: cn(
663
+ "tsh-zoomable-image nodrag tsh-desktop-no-drag",
664
+ wrapElement === "div" && "tsh-zoomable-image--block"
665
+ ),
666
+ children: [
667
+ /* @__PURE__ */ jsx2(
668
+ "img",
620
669
  {
621
- copyLabel: t("common.copyImage"),
622
- downloadLabel: t("common.downloadImage"),
623
- itemRole: "menuitem",
624
- onCopy: handleCopyImageAction,
625
- onDownload: handleDownloadImage
626
- }
627
- )
628
- }
629
- ) : null,
630
- copyStatus ? /* @__PURE__ */ jsx2(
631
- ImageCopyStatusToast,
632
- {
633
- busy: copyStatus.busy,
634
- message: copyStatus.message,
635
- variant: copyStatus.variant,
636
- onOpenChange: (open) => {
637
- if (!open) {
638
- setCopyStatus(null);
670
+ ...props,
671
+ alt,
672
+ src,
673
+ onClick: (event) => {
674
+ props.onClick?.(event);
675
+ if (!event.defaultPrevented) {
676
+ openPreviewImage();
677
+ }
678
+ },
679
+ onContextMenu: hasImageActions ? handleContextMenu : onContextMenu,
680
+ className: cn("nodrag tsh-desktop-no-drag cursor-zoom-in", className)
639
681
  }
640
- }
641
- }
642
- ) : null,
643
- /* @__PURE__ */ jsx2(
644
- Button,
645
- {
646
- asChild: true,
647
- className: "tsh-zoom-dialog__icon-button nodrag tsh-desktop-no-drag",
648
- size: "icon",
649
- variant: "chrome",
650
- children: cloneElement(
651
- typedButtonUnzoom,
682
+ ),
683
+ /* @__PURE__ */ jsx2(
684
+ "button",
652
685
  {
653
- onClick: (event) => {
654
- setIsWheelZooming(false);
655
- setImagePreviewZoom(1);
656
- buttonUnzoomProps.onClick?.(event);
657
- }
658
- },
659
- /* @__PURE__ */ jsx2(RestoreIcon, { "aria-hidden": "true", className: "size-4" })
686
+ type: "button",
687
+ "aria-label": t("common.expandImage"),
688
+ className: "tsh-zoomable-image__trigger nodrag tsh-desktop-no-drag",
689
+ onClick: openPreviewImage,
690
+ onContextMenu: handleContextMenu
691
+ }
660
692
  )
661
- }
662
- )
663
- ] });
664
- };
665
- return /* @__PURE__ */ jsxs2(Fragment, { children: [
666
- /* @__PURE__ */ jsx2(
667
- Zoom,
668
- {
669
- a11yNameButtonZoom: t("common.expandImage"),
670
- a11yNameButtonUnzoom: t("common.minimizeImage"),
671
- classDialog: "tsh-zoom-dialog nodrag tsh-desktop-no-drag",
672
- wrapElement,
673
- zoomMargin: 24,
674
- ZoomContent: renderZoomContent,
675
- onZoomChange: (zoomed) => {
676
- setIsImagePreviewOpen(zoomed);
677
- if (!zoomed) {
678
- setIsWheelZooming(false);
679
- setImagePreviewZoom(1);
680
- }
681
- },
682
- children: /* @__PURE__ */ jsx2(
683
- "img",
684
- {
685
- ...props,
686
- alt,
687
- src,
688
- onContextMenu: hasImageActions ? handleContextMenu : onContextMenu,
689
- className: cn("nodrag tsh-desktop-no-drag cursor-zoom-in", className)
690
- }
691
- )
693
+ ]
692
694
  }
693
695
  ),
694
696
  contextMenuPosition && !contextMenuPosition.inZoomDialog && actionButtons ? createPortal(
@@ -716,6 +718,108 @@ function ZoomableImage({
716
718
  ),
717
719
  document.body
718
720
  ) : null,
721
+ (isImagePreviewOpen || isImagePreviewClosing) && actionSource ? createPortal(
722
+ /* @__PURE__ */ jsxs2(
723
+ "div",
724
+ {
725
+ "aria-modal": "true",
726
+ className: "tsh-zoom-dialog nodrag tsh-desktop-no-drag",
727
+ "data-rmiz-modal": "",
728
+ role: "dialog",
729
+ tabIndex: -1,
730
+ onKeyDown: (event) => {
731
+ if (event.key === "Escape") {
732
+ closePreviewImage();
733
+ }
734
+ },
735
+ children: [
736
+ /* @__PURE__ */ jsx2(
737
+ "div",
738
+ {
739
+ "data-rmiz-modal-overlay": "visible",
740
+ onClick: closePreviewImage
741
+ }
742
+ ),
743
+ /* @__PURE__ */ jsx2("div", { "data-rmiz-modal-content": "true", children: previewContent }),
744
+ /* @__PURE__ */ jsx2(
745
+ ImagePreviewZoomControls,
746
+ {
747
+ canZoomIn,
748
+ canZoomOut,
749
+ percent: imagePreviewZoomPercent,
750
+ percentLabel: t("common.imageZoomPercent", {
751
+ percent: imagePreviewZoomPercent
752
+ }),
753
+ reportPercentStatus: !copyStatus,
754
+ resetLabel: t("common.resetImageZoom"),
755
+ zoomInLabel: t("common.zoomInImage"),
756
+ zoomOutLabel: t("common.zoomOutImage"),
757
+ onReset: resetPreviewImageZoom,
758
+ onZoomIn: zoomInPreviewImage,
759
+ onZoomOut: zoomOutPreviewImage,
760
+ onWheel: handlePreviewImageWheel
761
+ }
762
+ ),
763
+ actionButtons ? /* @__PURE__ */ jsx2("div", { className: "tsh-zoom-dialog__image-actions nodrag tsh-desktop-no-drag", children: actionButtons }) : null,
764
+ contextMenuPosition?.inZoomDialog && actionButtons ? /* @__PURE__ */ jsx2(
765
+ "div",
766
+ {
767
+ className: "tsh-image-context-menu nodrag tsh-desktop-no-drag",
768
+ style: {
769
+ left: contextMenuPosition.x,
770
+ top: contextMenuPosition.y
771
+ },
772
+ role: "menu",
773
+ onClick: (event) => event.stopPropagation(),
774
+ children: /* @__PURE__ */ jsx2(
775
+ ImageActionButtons,
776
+ {
777
+ copyLabel: t("common.copyImage"),
778
+ downloadLabel: t("common.downloadImage"),
779
+ itemRole: "menuitem",
780
+ onCopy: handleCopyImageAction,
781
+ onDownload: handleDownloadImage
782
+ }
783
+ )
784
+ }
785
+ ) : null,
786
+ copyStatus ? /* @__PURE__ */ jsx2(
787
+ ImageCopyStatusToast,
788
+ {
789
+ busy: copyStatus.busy,
790
+ message: copyStatus.message,
791
+ variant: copyStatus.variant,
792
+ onOpenChange: (open) => {
793
+ if (!open) {
794
+ setCopyStatus(null);
795
+ }
796
+ }
797
+ }
798
+ ) : null,
799
+ /* @__PURE__ */ jsx2(
800
+ Button,
801
+ {
802
+ asChild: true,
803
+ className: "tsh-zoom-dialog__icon-button nodrag tsh-desktop-no-drag",
804
+ size: "icon",
805
+ variant: "chrome",
806
+ children: /* @__PURE__ */ jsx2(
807
+ "button",
808
+ {
809
+ type: "button",
810
+ "aria-label": t("common.minimizeImage"),
811
+ "data-rmiz-btn-unzoom": "",
812
+ onClick: closePreviewImage,
813
+ children: /* @__PURE__ */ jsx2(RestoreIcon, { "aria-hidden": "true", className: "size-4" })
814
+ }
815
+ )
816
+ }
817
+ )
818
+ ]
819
+ }
820
+ ),
821
+ document.body
822
+ ) : null,
719
823
  copyStatus && !isImagePreviewOpen ? createPortal(
720
824
  /* @__PURE__ */ jsx2(
721
825
  ImageCopyStatusToast,
@@ -917,31 +1021,6 @@ function ImageActionButtons({
917
1021
  )
918
1022
  ] });
919
1023
  }
920
- function cloneImageWithPreviewZoom(img, zoom, isWheelZooming, onWheel) {
921
- const props = img.props;
922
- const style = props.style;
923
- const mergedStyle = {
924
- ...style,
925
- transform: resolveZoomedImageTransform(
926
- style?.transform,
927
- zoom,
928
- resolveImagePreviewDimension(style?.width ?? props.width),
929
- resolveImagePreviewDimension(style?.height ?? props.height)
930
- ),
931
- transition: isWheelZooming ? "none" : mergeImagePreviewTransition(style?.transition)
932
- };
933
- if (style?.transformOrigin !== void 0) {
934
- mergedStyle.transformOrigin = style.transformOrigin;
935
- }
936
- return cloneElement(img, {
937
- "data-tsh-image-zoom": formatImagePreviewZoom(zoom),
938
- onWheel: (event) => {
939
- props.onWheel?.(event);
940
- onWheel(event);
941
- },
942
- style: mergedStyle
943
- });
944
- }
945
1024
  function resolveWheelZoomDelta(event) {
946
1025
  return -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 2e-3) * (event.ctrlKey ? 10 : 1);
947
1026
  }
@@ -968,16 +1047,6 @@ function resolveImagePreviewZoomTransform(zoom, width, height) {
968
1047
  const halfHeight = formatCssNumber(height / 2);
969
1048
  return `translate(${halfWidth}px,${halfHeight}px) ${scale} translate(-${halfWidth}px,-${halfHeight}px)`;
970
1049
  }
971
- function resolveImagePreviewDimension(value) {
972
- if (typeof value === "number") {
973
- return Number.isFinite(value) && value > 0 ? value : null;
974
- }
975
- if (typeof value !== "string") {
976
- return null;
977
- }
978
- const dimension = Number.parseFloat(value);
979
- return Number.isFinite(dimension) && dimension > 0 ? dimension : null;
980
- }
981
1050
  function formatCssNumber(value) {
982
1051
  return Number(value.toFixed(3)).toString();
983
1052
  }
@@ -1669,7 +1738,7 @@ function countChar(value, char) {
1669
1738
  import {
1670
1739
  startTransition,
1671
1740
  useEffect as useEffect2,
1672
- useRef as useRef2,
1741
+ useRef as useRef3,
1673
1742
  useState as useState3
1674
1743
  } from "react";
1675
1744
  var DEFAULT_FRAME_MS = 24;
@@ -1683,9 +1752,9 @@ function useStreamingVisibleText(sourceText, options) {
1683
1752
  trailingFlushChars = DEFAULT_TRAILING_FLUSH_CHARS
1684
1753
  } = options;
1685
1754
  const [visibleText, setVisibleText] = useState3(sourceText);
1686
- const sourceRef = useRef2(sourceText);
1687
- const visibleRef = useRef2(visibleText);
1688
- const timerRef = useRef2(null);
1755
+ const sourceRef = useRef3(sourceText);
1756
+ const visibleRef = useRef3(visibleText);
1757
+ const timerRef = useRef3(null);
1689
1758
  useEffect2(() => {
1690
1759
  visibleRef.current = visibleText;
1691
1760
  }, [visibleText]);
@@ -3273,9 +3342,9 @@ function MarkdownPre({
3273
3342
  ...props
3274
3343
  }) {
3275
3344
  "use memo";
3276
- const preRef = useRef3(null);
3345
+ const preRef = useRef4(null);
3277
3346
  const [copied, setCopied] = useState4(false);
3278
- const copyResetRef = useRef3(null);
3347
+ const copyResetRef = useRef4(null);
3279
3348
  const handleCopy = useCallback3(() => {
3280
3349
  const text = preRef.current?.textContent?.trim();
3281
3350
  if (!text) {
@@ -3346,7 +3415,7 @@ import {
3346
3415
  forwardRef,
3347
3416
  useCallback as useCallback4,
3348
3417
  useEffect as useEffect4,
3349
- useRef as useRef4,
3418
+ useRef as useRef5,
3350
3419
  useState as useState5
3351
3420
  } from "react";
3352
3421
  import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
@@ -3360,8 +3429,8 @@ function CustomScrollbar({
3360
3429
  syncKey
3361
3430
  }) {
3362
3431
  "use memo";
3363
- const trackRef = useRef4(null);
3364
- const dragStateRef = useRef4(null);
3432
+ const trackRef = useRef5(null);
3433
+ const dragStateRef = useRef5(null);
3365
3434
  const [scrollbarState, setScrollbarState] = useState5({
3366
3435
  scrollable: false,
3367
3436
  thumbHeight: 0,
@@ -3548,7 +3617,7 @@ var CustomScrollArea = forwardRef(function CustomScrollArea2({
3548
3617
  ...viewportProps
3549
3618
  }, forwardedRef) {
3550
3619
  "use memo";
3551
- const viewportRef = useRef4(null);
3620
+ const viewportRef = useRef5(null);
3552
3621
  const getViewport = useCallback4(() => viewportRef.current, []);
3553
3622
  return /* @__PURE__ */ jsxs4(
3554
3623
  "div",
@@ -3605,7 +3674,7 @@ import {
3605
3674
  useCallback as useCallback5,
3606
3675
  useEffect as useEffect5,
3607
3676
  useImperativeHandle,
3608
- useRef as useRef5
3677
+ useRef as useRef6
3609
3678
  } from "react";
3610
3679
  import {
3611
3680
  motion,
@@ -3656,7 +3725,7 @@ var MessageSquareMoreIcon = forwardRef2(
3656
3725
  }, ref) => {
3657
3726
  const controls = useAnimation();
3658
3727
  const reduceMotion = useReducedMotion();
3659
- const isControlledRef = useRef5(false);
3728
+ const isControlledRef = useRef6(false);
3660
3729
  const startAnimation = useCallback5(() => {
3661
3730
  if (reduceMotion) {
3662
3731
  return;
@@ -3776,4 +3845,4 @@ export {
3776
3845
  CustomScrollArea,
3777
3846
  MessageSquareMoreIcon
3778
3847
  };
3779
- //# sourceMappingURL=chunk-3FYA2K4P.js.map
3848
+ //# sourceMappingURL=chunk-5CLGEUWK.js.map