Skip to content

Commit 89dafb3

Browse files
authored
fix(random): optimized kb connector sync engine, rerenders in tables, files, editors, chat (#3513)
* optimized kb connector sync engine, rerenders in tables, files, editors, chat * refactor(sidebar): rename onTaskClick to onMultiSelectClick for clarity Made-with: Cursor * ack comments, add docsFailed
1 parent 87e2910 commit 89dafb3

File tree

25 files changed

+13928
-606
lines changed

25 files changed

+13928
-606
lines changed

apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-header/resource-header.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Fragment } from 'react'
1+
import { Fragment, memo } from 'react'
22
import {
33
Button,
44
ChevronDown,
@@ -54,7 +54,7 @@ interface ResourceHeaderProps {
5454
actions?: HeaderAction[]
5555
}
5656

57-
export function ResourceHeader({
57+
export const ResourceHeader = memo(function ResourceHeader({
5858
icon: Icon,
5959
title,
6060
breadcrumbs,
@@ -134,7 +134,7 @@ export function ResourceHeader({
134134
</div>
135135
</div>
136136
)
137-
}
137+
})
138138

139139
function BreadcrumbSegment({
140140
icon: Icon,

apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-options-bar/resource-options-bar.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { ReactNode } from 'react'
1+
import { memo, type ReactNode } from 'react'
22
import * as PopoverPrimitive from '@radix-ui/react-popover'
33
import {
44
ArrowDown,
@@ -65,7 +65,7 @@ interface ResourceOptionsBarProps {
6565
extras?: ReactNode
6666
}
6767

68-
export function ResourceOptionsBar({
68+
export const ResourceOptionsBar = memo(function ResourceOptionsBar({
6969
search,
7070
sort,
7171
filter,
@@ -172,7 +172,7 @@ export function ResourceOptionsBar({
172172
</div>
173173
</div>
174174
)
175-
}
175+
})
176176

177177
function SortDropdown({ config }: { config: SortConfig }) {
178178
const { options, active, onSort, onClear } = config

apps/sim/app/workspace/[workspaceId]/files/files.tsx

Lines changed: 106 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,73 @@ export function Files() {
327327
}
328328
}, [isDirty])
329329

330+
const handleStartHeaderRename = useCallback(() => {
331+
if (selectedFile) headerRename.startRename(selectedFile.id, selectedFile.name)
332+
}, [selectedFile, headerRename.startRename])
333+
334+
const handleDownloadSelected = useCallback(() => {
335+
if (selectedFile) handleDownload(selectedFile)
336+
}, [selectedFile, handleDownload])
337+
338+
const handleDeleteSelected = useCallback(() => {
339+
if (selectedFile) {
340+
setDeleteTargetFile(selectedFile)
341+
setShowDeleteConfirm(true)
342+
}
343+
}, [selectedFile])
344+
345+
const fileDetailBreadcrumbs = useMemo(
346+
() =>
347+
selectedFile
348+
? [
349+
{ label: 'Files', onClick: handleBackAttempt },
350+
{
351+
label: selectedFile.name,
352+
editing: headerRename.editingId
353+
? {
354+
isEditing: true,
355+
value: headerRename.editValue,
356+
onChange: headerRename.setEditValue,
357+
onSubmit: headerRename.submitRename,
358+
onCancel: headerRename.cancelRename,
359+
}
360+
: undefined,
361+
dropdownItems: [
362+
{
363+
label: 'Rename',
364+
icon: Pencil,
365+
onClick: handleStartHeaderRename,
366+
},
367+
{
368+
label: 'Download',
369+
icon: Download,
370+
onClick: handleDownloadSelected,
371+
},
372+
{
373+
label: 'Delete',
374+
icon: Trash,
375+
onClick: handleDeleteSelected,
376+
},
377+
],
378+
},
379+
]
380+
: [],
381+
[
382+
selectedFile,
383+
handleBackAttempt,
384+
headerRename.editingId,
385+
headerRename.editValue,
386+
headerRename.setEditValue,
387+
headerRename.submitRename,
388+
headerRename.cancelRename,
389+
handleStartHeaderRename,
390+
handleDownloadSelected,
391+
handleDeleteSelected,
392+
]
393+
)
394+
395+
const handleTogglePreview = useCallback(() => setShowPreview((prev) => !prev), [])
396+
330397
const handleDiscardChanges = useCallback(() => {
331398
setShowUnsavedChangesAlert(false)
332399
setIsDirty(false)
@@ -427,25 +494,11 @@ export function Files() {
427494
return () => window.removeEventListener('beforeunload', handler)
428495
}, [isDirty])
429496

430-
if (selectedFileId && !selectedFile) {
431-
return (
432-
<div className='flex h-full flex-1 flex-col overflow-hidden bg-[var(--bg)]'>
433-
<ResourceHeader
434-
icon={FilesIcon}
435-
breadcrumbs={[
436-
{ label: 'Files', onClick: () => setSelectedFileId(null) },
437-
{ label: '...' },
438-
]}
439-
/>
440-
<div className='flex flex-1 items-center justify-center'>
441-
<Skeleton className='h-[16px] w-[200px]' />
442-
</div>
443-
</div>
444-
)
445-
}
446-
447-
if (selectedFile) {
497+
const fileActions = useMemo<HeaderAction[]>(() => {
498+
if (!selectedFile) return []
448499
const canEditText = isTextEditable(selectedFile)
500+
const canPreview = isPreviewable(selectedFile)
501+
449502
const saveLabel =
450503
saveStatus === 'saving'
451504
? 'Saving...'
@@ -455,15 +508,13 @@ export function Files() {
455508
? 'Save failed'
456509
: 'Save'
457510

458-
const canPreview = isPreviewable(selectedFile)
459-
460-
const fileActions: HeaderAction[] = [
511+
return [
461512
...(canPreview
462513
? [
463514
{
464515
label: showPreview ? 'Hide Preview' : 'Preview',
465516
icon: Eye,
466-
onClick: () => setShowPreview((prev) => !prev),
517+
onClick: handleTogglePreview,
467518
},
468519
]
469520
: []),
@@ -482,58 +533,51 @@ export function Files() {
482533
{
483534
label: 'Download',
484535
icon: Download,
485-
onClick: () => handleDownload(selectedFile),
536+
onClick: handleDownloadSelected,
486537
},
487538
{
488539
label: 'Delete',
489540
icon: Trash,
490-
onClick: () => {
491-
setDeleteTargetFile(selectedFile)
492-
setShowDeleteConfirm(true)
493-
},
541+
onClick: handleDeleteSelected,
494542
},
495543
]
544+
}, [
545+
selectedFile,
546+
saveStatus,
547+
showPreview,
548+
handleTogglePreview,
549+
handleSave,
550+
isDirty,
551+
handleDownloadSelected,
552+
handleDeleteSelected,
553+
])
554+
555+
if (selectedFileId && !selectedFile) {
556+
return (
557+
<div className='flex h-full flex-1 flex-col overflow-hidden bg-[var(--bg)]'>
558+
<ResourceHeader
559+
icon={FilesIcon}
560+
breadcrumbs={[
561+
{ label: 'Files', onClick: () => setSelectedFileId(null) },
562+
{ label: '...' },
563+
]}
564+
/>
565+
<div className='flex flex-1 items-center justify-center'>
566+
<Skeleton className='h-[16px] w-[200px]' />
567+
</div>
568+
</div>
569+
)
570+
}
571+
572+
if (selectedFile) {
573+
const canPreview = isPreviewable(selectedFile)
496574

497575
return (
498576
<>
499577
<div className='flex h-full flex-1 flex-col overflow-hidden bg-[var(--bg)]'>
500578
<ResourceHeader
501579
icon={FilesIcon}
502-
breadcrumbs={[
503-
{ label: 'Files', onClick: handleBackAttempt },
504-
{
505-
label: selectedFile.name,
506-
editing: headerRename.editingId
507-
? {
508-
isEditing: true,
509-
value: headerRename.editValue,
510-
onChange: headerRename.setEditValue,
511-
onSubmit: headerRename.submitRename,
512-
onCancel: headerRename.cancelRename,
513-
}
514-
: undefined,
515-
dropdownItems: [
516-
{
517-
label: 'Rename',
518-
icon: Pencil,
519-
onClick: () => headerRename.startRename(selectedFile.id, selectedFile.name),
520-
},
521-
{
522-
label: 'Download',
523-
icon: Download,
524-
onClick: () => handleDownload(selectedFile),
525-
},
526-
{
527-
label: 'Delete',
528-
icon: Trash,
529-
onClick: () => {
530-
setDeleteTargetFile(selectedFile)
531-
setShowDeleteConfirm(true)
532-
},
533-
},
534-
],
535-
},
536-
]}
580+
breadcrumbs={fileDetailBreadcrumbs}
537581
actions={fileActions}
538582
/>
539583
<FileViewer

apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,25 +42,29 @@ const ACCEPTED_FILE_TYPES =
4242
'image/*,.pdf,.txt,.csv,.md,.html,.json,.xml,text/plain,text/csv,text/markdown,text/html,application/json,application/xml,application/pdf'
4343

4444
interface UserInputProps {
45-
value: string
46-
onChange: (value: string) => void
47-
onSubmit: (fileAttachments?: FileAttachmentForApi[]) => void
45+
defaultValue?: string
46+
onSubmit: (text: string, fileAttachments?: FileAttachmentForApi[]) => void
4847
isSending: boolean
4948
onStopGeneration: () => void
5049
isInitialView?: boolean
5150
userId?: string
5251
}
5352

5453
export function UserInput({
55-
value,
56-
onChange,
54+
defaultValue = '',
5755
onSubmit,
5856
isSending,
5957
onStopGeneration,
6058
isInitialView = true,
6159
userId,
6260
}: UserInputProps) {
63-
const animatedPlaceholder = useAnimatedPlaceholder()
61+
const [value, setValue] = useState(defaultValue)
62+
63+
useEffect(() => {
64+
if (defaultValue) setValue(defaultValue)
65+
}, [defaultValue])
66+
67+
const animatedPlaceholder = useAnimatedPlaceholder(isInitialView)
6468
const placeholder = isInitialView ? animatedPlaceholder : 'Send message to Sim'
6569

6670
const files = useFileAttachments({ userId, disabled: false, isLoading: isSending })
@@ -95,13 +99,14 @@ export function UserInput({
9599
size: f.size,
96100
}))
97101

98-
onSubmit(fileAttachmentsForApi.length > 0 ? fileAttachmentsForApi : undefined)
102+
onSubmit(value, fileAttachmentsForApi.length > 0 ? fileAttachmentsForApi : undefined)
103+
setValue('')
99104
files.clearAttachedFiles()
100105

101106
if (textareaRef.current) {
102107
textareaRef.current.style.height = 'auto'
103108
}
104-
}, [onSubmit, files])
109+
}, [onSubmit, files, value])
105110

106111
const handleKeyDown = useCallback(
107112
(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
@@ -149,7 +154,7 @@ export function UserInput({
149154
transcript += event.results[i][0].transcript
150155
}
151156
const prefix = prefixRef.current
152-
onChange(prefix ? `${prefix} ${transcript}` : transcript)
157+
setValue(prefix ? `${prefix} ${transcript}` : transcript)
153158
}
154159

155160
recognition.onend = () => {
@@ -172,7 +177,7 @@ export function UserInput({
172177
recognitionRef.current = recognition
173178
recognition.start()
174179
setIsListening(true)
175-
}, [isListening, value, onChange])
180+
}, [isListening, value])
176181

177182
return (
178183
<div
@@ -195,7 +200,7 @@ export function UserInput({
195200
return (
196201
<div
197202
key={file.id}
198-
className='group relative h-[56px] w-[56px] flex-shrink-0 cursor-pointer overflow-hidden rounded-[8px] border border-[var(--border-1)] bg-[var(--surface-5)] transition-all hover:bg-[var(--surface-4)]'
203+
className='group relative h-[56px] w-[56px] flex-shrink-0 cursor-pointer overflow-hidden rounded-[8px] border border-[var(--border-1)] bg-[var(--surface-5)] hover:bg-[var(--surface-4)]'
199204
title={`${file.name} (${files.formatFileSize(file.size)})`}
200205
onClick={() => files.handleFileClick(file)}
201206
>
@@ -229,7 +234,7 @@ export function UserInput({
229234
e.stopPropagation()
230235
files.removeFile(file.id)
231236
}}
232-
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'
237+
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'
233238
>
234239
<X className='h-[10px] w-[10px] text-white' />
235240
</button>
@@ -243,7 +248,7 @@ export function UserInput({
243248
<textarea
244249
ref={textareaRef}
245250
value={value}
246-
onChange={(e) => onChange(e.target.value)}
251+
onChange={(e) => setValue(e.target.value)}
247252
onKeyDown={handleKeyDown}
248253
onInput={handleInput}
249254
placeholder={files.isDragging ? 'Drop files here...' : placeholder}

0 commit comments

Comments
 (0)