From c4b3d91d67a42ab701eae0f45d728929573e3178 Mon Sep 17 00:00:00 2001 From: waleed Date: Tue, 10 Mar 2026 20:42:09 -0700 Subject: [PATCH 1/3] optimized kb connector sync engine, rerenders in tables, files, editors, chat --- .../resource-header/resource-header.tsx | 6 +- .../resource-options-bar.tsx | 6 +- .../workspace/[workspaceId]/files/files.tsx | 168 ++++++---- .../home/components/user-input/user-input.tsx | 31 +- .../app/workspace/[workspaceId]/home/home.tsx | 16 +- .../home/hooks/use-animated-placeholder.ts | 8 +- .../components/chunk-editor/chunk-editor.tsx | 38 ++- .../knowledge/[id]/[documentId]/document.tsx | 293 +++++++++-------- .../[id]/components/action-bar/action-bar.tsx | 2 +- .../[tableId]/components/table/table.tsx | 247 +++++++++----- .../components/search-modal/search-modal.tsx | 2 +- .../workflow-list/workflow-list.tsx | 19 +- .../w/components/sidebar/sidebar.tsx | 273 ++++++++-------- .../emcn/components/checkbox/checkbox.tsx | 24 +- .../emcn/components/modal/modal.tsx | 2 +- .../emcn/components/s-modal/s-modal.tsx | 2 +- .../emcn/components/switch/switch.tsx | 34 +- .../lib/knowledge/connectors/sync-engine.ts | 301 +++++++++++------- 18 files changed, 867 insertions(+), 605 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-header/resource-header.tsx b/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-header/resource-header.tsx index d5bc84e4c5c..7c94ac098c1 100644 --- a/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-header/resource-header.tsx +++ b/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-header/resource-header.tsx @@ -1,4 +1,4 @@ -import { Fragment } from 'react' +import { Fragment, memo } from 'react' import { Button, ChevronDown, @@ -54,7 +54,7 @@ interface ResourceHeaderProps { actions?: HeaderAction[] } -export function ResourceHeader({ +export const ResourceHeader = memo(function ResourceHeader({ icon: Icon, title, breadcrumbs, @@ -134,7 +134,7 @@ export function ResourceHeader({ ) -} +}) function BreadcrumbSegment({ icon: Icon, diff --git a/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-options-bar/resource-options-bar.tsx b/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-options-bar/resource-options-bar.tsx index c7e7399ac27..c57ca87cadf 100644 --- a/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-options-bar/resource-options-bar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-options-bar/resource-options-bar.tsx @@ -1,4 +1,4 @@ -import type { ReactNode } from 'react' +import { memo, type ReactNode } from 'react' import * as PopoverPrimitive from '@radix-ui/react-popover' import { ArrowDown, @@ -65,7 +65,7 @@ interface ResourceOptionsBarProps { extras?: ReactNode } -export function ResourceOptionsBar({ +export const ResourceOptionsBar = memo(function ResourceOptionsBar({ search, sort, filter, @@ -172,7 +172,7 @@ export function ResourceOptionsBar({ ) -} +}) function SortDropdown({ config }: { config: SortConfig }) { const { options, active, onSort, onClear } = config diff --git a/apps/sim/app/workspace/[workspaceId]/files/files.tsx b/apps/sim/app/workspace/[workspaceId]/files/files.tsx index f1b6bbe98cd..748285db269 100644 --- a/apps/sim/app/workspace/[workspaceId]/files/files.tsx +++ b/apps/sim/app/workspace/[workspaceId]/files/files.tsx @@ -327,6 +327,73 @@ export function Files() { } }, [isDirty]) + const handleStartHeaderRename = useCallback(() => { + if (selectedFile) headerRename.startRename(selectedFile.id, selectedFile.name) + }, [selectedFile, headerRename.startRename]) + + const handleDownloadSelected = useCallback(() => { + if (selectedFile) handleDownload(selectedFile) + }, [selectedFile, handleDownload]) + + const handleDeleteSelected = useCallback(() => { + if (selectedFile) { + setDeleteTargetFile(selectedFile) + setShowDeleteConfirm(true) + } + }, [selectedFile]) + + const fileDetailBreadcrumbs = useMemo( + () => + selectedFile + ? [ + { label: 'Files', onClick: handleBackAttempt }, + { + label: selectedFile.name, + editing: headerRename.editingId + ? { + isEditing: true, + value: headerRename.editValue, + onChange: headerRename.setEditValue, + onSubmit: headerRename.submitRename, + onCancel: headerRename.cancelRename, + } + : undefined, + dropdownItems: [ + { + label: 'Rename', + icon: Pencil, + onClick: handleStartHeaderRename, + }, + { + label: 'Download', + icon: Download, + onClick: handleDownloadSelected, + }, + { + label: 'Delete', + icon: Trash, + onClick: handleDeleteSelected, + }, + ], + }, + ] + : [], + [ + selectedFile, + handleBackAttempt, + headerRename.editingId, + headerRename.editValue, + headerRename.setEditValue, + headerRename.submitRename, + headerRename.cancelRename, + handleStartHeaderRename, + handleDownloadSelected, + handleDeleteSelected, + ] + ) + + const handleTogglePreview = useCallback(() => setShowPreview((prev) => !prev), []) + const handleDiscardChanges = useCallback(() => { setShowUnsavedChangesAlert(false) setIsDirty(false) @@ -427,25 +494,11 @@ export function Files() { return () => window.removeEventListener('beforeunload', handler) }, [isDirty]) - if (selectedFileId && !selectedFile) { - return ( -
- setSelectedFileId(null) }, - { label: '...' }, - ]} - /> -
- -
-
- ) - } - - if (selectedFile) { + const fileActions = useMemo(() => { + if (!selectedFile) return [] const canEditText = isTextEditable(selectedFile) + const canPreview = isPreviewable(selectedFile) + const saveLabel = saveStatus === 'saving' ? 'Saving...' @@ -455,15 +508,13 @@ export function Files() { ? 'Save failed' : 'Save' - const canPreview = isPreviewable(selectedFile) - - const fileActions: HeaderAction[] = [ + return [ ...(canPreview ? [ { label: showPreview ? 'Hide Preview' : 'Preview', icon: Eye, - onClick: () => setShowPreview((prev) => !prev), + onClick: handleTogglePreview, }, ] : []), @@ -482,58 +533,51 @@ export function Files() { { label: 'Download', icon: Download, - onClick: () => handleDownload(selectedFile), + onClick: handleDownloadSelected, }, { label: 'Delete', icon: Trash, - onClick: () => { - setDeleteTargetFile(selectedFile) - setShowDeleteConfirm(true) - }, + onClick: handleDeleteSelected, }, ] + }, [ + selectedFile, + saveStatus, + showPreview, + handleTogglePreview, + handleSave, + isDirty, + handleDownloadSelected, + handleDeleteSelected, + ]) + + if (selectedFileId && !selectedFile) { + return ( +
+ setSelectedFileId(null) }, + { label: '...' }, + ]} + /> +
+ +
+
+ ) + } + + if (selectedFile) { + const canPreview = isPreviewable(selectedFile) return ( <>
headerRename.startRename(selectedFile.id, selectedFile.name), - }, - { - label: 'Download', - icon: Download, - onClick: () => handleDownload(selectedFile), - }, - { - label: 'Delete', - icon: Trash, - onClick: () => { - setDeleteTargetFile(selectedFile) - setShowDeleteConfirm(true) - }, - }, - ], - }, - ]} + breadcrumbs={fileDetailBreadcrumbs} actions={fileActions} /> void - onSubmit: (fileAttachments?: FileAttachmentForApi[]) => void + defaultValue?: string + onSubmit: (text: string, fileAttachments?: FileAttachmentForApi[]) => void isSending: boolean onStopGeneration: () => void isInitialView?: boolean @@ -52,15 +51,20 @@ interface UserInputProps { } export function UserInput({ - value, - onChange, + defaultValue = '', onSubmit, isSending, onStopGeneration, isInitialView = true, userId, }: UserInputProps) { - const animatedPlaceholder = useAnimatedPlaceholder() + const [value, setValue] = useState(defaultValue) + + useEffect(() => { + if (defaultValue) setValue(defaultValue) + }, [defaultValue]) + + const animatedPlaceholder = useAnimatedPlaceholder(isInitialView) const placeholder = isInitialView ? animatedPlaceholder : 'Send message to Sim' const files = useFileAttachments({ userId, disabled: false, isLoading: isSending }) @@ -95,13 +99,14 @@ export function UserInput({ size: f.size, })) - onSubmit(fileAttachmentsForApi.length > 0 ? fileAttachmentsForApi : undefined) + onSubmit(value, fileAttachmentsForApi.length > 0 ? fileAttachmentsForApi : undefined) + setValue('') files.clearAttachedFiles() if (textareaRef.current) { textareaRef.current.style.height = 'auto' } - }, [onSubmit, files]) + }, [onSubmit, files, value]) const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { @@ -149,7 +154,7 @@ export function UserInput({ transcript += event.results[i][0].transcript } const prefix = prefixRef.current - onChange(prefix ? `${prefix} ${transcript}` : transcript) + setValue(prefix ? `${prefix} ${transcript}` : transcript) } recognition.onend = () => { @@ -172,7 +177,7 @@ export function UserInput({ recognitionRef.current = recognition recognition.start() setIsListening(true) - }, [isListening, value, onChange]) + }, [isListening, value]) return (
files.handleFileClick(file)} > @@ -229,7 +234,7 @@ export function UserInput({ e.stopPropagation() files.removeFile(file.id) }} - className='absolute top-[2px] right-[2px] flex h-[16px] w-[16px] items-center justify-center rounded-full bg-black/60 opacity-0 transition-opacity group-hover:opacity-100' + className='absolute top-[2px] right-[2px] flex h-[16px] w-[16px] items-center justify-center rounded-full bg-black/60 opacity-0 group-hover:opacity-100' > @@ -243,7 +248,7 @@ export function UserInput({