diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/file-viewer.tsx b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/file-viewer.tsx index 45f4bc223a..fe8660aa46 100644 --- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/file-viewer.tsx +++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/file-viewer.tsx @@ -2,6 +2,7 @@ import { memo, useCallback, useEffect, useRef, useState } from 'react' import { createLogger } from '@sim/logger' +import { ZoomIn, ZoomOut } from 'lucide-react' import { Skeleton } from '@/components/emcn' import { cn } from '@/lib/core/utils/cn' import type { WorkspaceFileRecord } from '@/lib/uploads/contexts/workspace' @@ -432,17 +433,120 @@ const IframePreview = memo(function IframePreview({ file }: { file: WorkspaceFil ) }) +const ZOOM_MIN = 0.25 +const ZOOM_MAX = 4 +const ZOOM_WHEEL_SENSITIVITY = 0.005 +const ZOOM_BUTTON_FACTOR = 1.2 + +const clampZoom = (z: number) => Math.min(Math.max(z, ZOOM_MIN), ZOOM_MAX) + const ImagePreview = memo(function ImagePreview({ file }: { file: WorkspaceFileRecord }) { const serveUrl = `/api/files/serve/${encodeURIComponent(file.key)}?context=workspace` + const [zoom, setZoom] = useState(1) + const [offset, setOffset] = useState({ x: 0, y: 0 }) + const isDragging = useRef(false) + const dragStart = useRef({ x: 0, y: 0 }) + const offsetAtDragStart = useRef({ x: 0, y: 0 }) + const offsetRef = useRef(offset) + offsetRef.current = offset + + const containerRef = useRef(null) + + const zoomIn = useCallback(() => setZoom((z) => clampZoom(z * ZOOM_BUTTON_FACTOR)), []) + const zoomOut = useCallback(() => setZoom((z) => clampZoom(z / ZOOM_BUTTON_FACTOR)), []) + + useEffect(() => { + const el = containerRef.current + if (!el) return + const onWheel = (e: WheelEvent) => { + e.preventDefault() + if (e.ctrlKey || e.metaKey) { + setZoom((z) => clampZoom(z * Math.exp(-e.deltaY * ZOOM_WHEEL_SENSITIVITY))) + } else { + setOffset((o) => ({ x: o.x - e.deltaX, y: o.y - e.deltaY })) + } + } + el.addEventListener('wheel', onWheel, { passive: false }) + return () => el.removeEventListener('wheel', onWheel) + }, []) + + const handleMouseDown = useCallback((e: React.MouseEvent) => { + if (e.button !== 0) return + isDragging.current = true + dragStart.current = { x: e.clientX, y: e.clientY } + offsetAtDragStart.current = offsetRef.current + if (containerRef.current) containerRef.current.style.cursor = 'grabbing' + e.preventDefault() + }, []) + + const handleMouseMove = useCallback((e: React.MouseEvent) => { + if (!isDragging.current) return + setOffset({ + x: offsetAtDragStart.current.x + (e.clientX - dragStart.current.x), + y: offsetAtDragStart.current.y + (e.clientY - dragStart.current.y), + }) + }, []) + + const handleMouseUp = useCallback(() => { + isDragging.current = false + if (containerRef.current) containerRef.current.style.cursor = 'grab' + }, []) + + useEffect(() => { + setZoom(1) + setOffset({ x: 0, y: 0 }) + }, [file.key]) return ( -
- {file.name} +
+
+ {file.name} +
+
e.stopPropagation()} + > + + + {Math.round(zoom * 100)}% + + +
) }) diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/document.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/document.tsx index 687babb9f7..f43c84dbdd 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/document.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/document.tsx @@ -185,7 +185,7 @@ export function Document({ ? 'tokenCount' : activeSort?.column === 'status' ? 'enabled' - : activeSort + : activeSort?.column === 'index' ? 'chunkIndex' : undefined, activeSort?.direction diff --git a/apps/sim/app/workspace/[workspaceId]/logs/logs.tsx b/apps/sim/app/workspace/[workspaceId]/logs/logs.tsx index adfccbfdd1..85de3bebd0 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/logs.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/logs.tsx @@ -640,11 +640,12 @@ export default function Logs() { }, [initializeFromURL]) const loadMoreLogs = useCallback(() => { + if (activeSort) return const { isFetching, hasNextPage, fetchNextPage } = logsQueryRef.current if (!isFetching && hasNextPage) { fetchNextPage() } - }, []) + }, [activeSort]) useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { @@ -1144,7 +1145,7 @@ export default function Logs() { onRowContextMenu={handleLogContextMenu} isLoading={!logsQuery.data} onLoadMore={loadMoreLogs} - hasMore={logsQuery.hasNextPage ?? false} + hasMore={!activeSort && (logsQuery.hasNextPage ?? false)} isLoadingMore={logsQuery.isFetchingNextPage} emptyMessage='No logs found' overlay={sidebarOverlay} diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx index b21d2a90e8..1190f0a390 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx @@ -183,13 +183,13 @@ const SidebarTaskItem = memo(function SidebarTaskItem({
{task.name}
{task.id !== 'new' && (
- {isActive && !isCurrentRoute && ( + {isActive && !isCurrentRoute && !isMenuOpen && ( )} - {isActive && !isCurrentRoute && ( + {isActive && !isCurrentRoute && !isMenuOpen && ( )} - {!isActive && isUnread && !isCurrentRoute && ( + {!isActive && isUnread && !isCurrentRoute && !isMenuOpen && ( )}