Skip to content

Commit 6c3caf6

Browse files
authored
feat(chat): drag workflows and folders from sidebar into chat input (#4028)
* feat(chat): drag workflows and folders from sidebar into chat input * fix(chat): fix effectAllowed, stale atInsertPosRef, and drag-enter overlay for resource drags * feat(chat): add task dragging and visible drag ghost for sidebar items * feat(sidebar): add drag ghost with icons and task icon to context chips * refactor(types): narrow ChatMessageContext.kind to ChatContextKind union and add workflowBorderColor utility * feat(user-input): support Tab to select resource in mention dropdown * fix(user-input): narrow ChatContext discriminated union before accessing workflowId * fix(colors): overload workflowBorderColor to accept string | undefined * fix(colors): simplify workflowBorderColor to single string | undefined signature * fix(chat): remove resource panel tab when context mention is deleted from input * fix(chat): use resource ID for context removal identity check * fix(chat): add folder/task cases to resource resolver, task key to existingResourceKeys, and use workflowBorderColor in drag ghost * revert(chat): remove folder/task from resolveResourceFromContext — no panel UI for these types * fix(chat): add chatId to stored context types and workflow.color to drag callback deps * fix(chat): guard chatId before adding task key to existingResourceKeys
1 parent 98be968 commit 6c3caf6

File tree

31 files changed

+435
-148
lines changed

31 files changed

+435
-148
lines changed

apps/sim/app/(landing)/components/features/components/features-preview.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
xAIIcon,
1919
} from '@/components/icons'
2020
import { cn } from '@/lib/core/utils/cn'
21+
import { workflowBorderColor } from '@/lib/workspaces/colors'
2122

2223
interface FeaturesPreviewProps {
2324
activeTab: number
@@ -383,7 +384,7 @@ function MiniCardIcon({ variant, color }: { variant: CardVariant; color?: string
383384
className='h-[7px] w-[7px] flex-shrink-0 rounded-[1.5px] border'
384385
style={{
385386
backgroundColor: c,
386-
borderColor: `${c}60`,
387+
borderColor: workflowBorderColor(c),
387388
backgroundClip: 'padding-box',
388389
}}
389390
/>
@@ -470,7 +471,7 @@ function WorkflowCardBody({ color }: { color: string }) {
470471
className='absolute top-2.5 left-[40px] h-[14px] w-[14px] rounded-[3px] border-[2px]'
471472
style={{
472473
backgroundColor: color,
473-
borderColor: `${color}60`,
474+
borderColor: workflowBorderColor(color),
474475
backgroundClip: 'padding-box',
475476
}}
476477
/>
@@ -481,7 +482,7 @@ function WorkflowCardBody({ color }: { color: string }) {
481482
className='absolute top-[36px] left-[68px] h-[14px] w-[14px] rounded-[3px] border-[2px]'
482483
style={{
483484
backgroundColor: color,
484-
borderColor: `${color}60`,
485+
borderColor: workflowBorderColor(color),
485486
backgroundClip: 'padding-box',
486487
opacity: 0.5,
487488
}}
@@ -896,7 +897,7 @@ function MockLogDetailsSidebar({ selectedRow, onPrev, onNext }: MockLogDetailsSi
896897
className='h-[10px] w-[10px] shrink-0 rounded-[3px] border-[1.5px]'
897898
style={{
898899
backgroundColor: color,
899-
borderColor: `${color}60`,
900+
borderColor: workflowBorderColor(color),
900901
backgroundClip: 'padding-box',
901902
}}
902903
/>

apps/sim/app/(landing)/components/landing-preview/components/landing-preview-logs/landing-preview-logs.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Download } from 'lucide-react'
55
import { ArrowUpDown, Badge, Library, ListFilter, Search } from '@/components/emcn'
66
import type { BadgeProps } from '@/components/emcn/components/badge/badge'
77
import { cn } from '@/lib/core/utils/cn'
8+
import { workflowBorderColor } from '@/lib/workspaces/colors'
89

910
interface LogRow {
1011
id: string
@@ -283,7 +284,7 @@ export function LandingPreviewLogs() {
283284
className='h-[10px] w-[10px] flex-shrink-0 rounded-[3px] border-[1.5px]'
284285
style={{
285286
backgroundColor: log.workflowColor,
286-
borderColor: `${log.workflowColor}60`,
287+
borderColor: workflowBorderColor(log.workflowColor),
287288
backgroundClip: 'padding-box',
288289
}}
289290
/>

apps/sim/app/(landing)/components/landing-preview/components/landing-preview-sidebar/landing-preview-sidebar.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
Table,
1212
} from '@/components/emcn/icons'
1313
import { cn } from '@/lib/core/utils/cn'
14+
import { workflowBorderColor } from '@/lib/workspaces/colors'
1415
import type { PreviewWorkflow } from '@/app/(landing)/components/landing-preview/components/landing-preview-workflow/workflow-data'
1516

1617
export type SidebarView =
@@ -211,7 +212,7 @@ export function LandingPreviewSidebar({
211212
className='h-[14px] w-[14px] flex-shrink-0 rounded-[4px] border-[2.5px]'
212213
style={{
213214
backgroundColor: workflow.color,
214-
borderColor: `${workflow.color}60`,
215+
borderColor: workflowBorderColor(workflow.color),
215216
backgroundClip: 'padding-box',
216217
}}
217218
/>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { Blimp, Database, Folder as FolderIcon, Table as TableIcon } from '@/components/emcn/icons'
2+
import { getDocumentIcon } from '@/components/icons/document-icons'
3+
import { cn } from '@/lib/core/utils/cn'
4+
import { workflowBorderColor } from '@/lib/workspaces/colors'
5+
import type { ChatMessageContext } from '@/app/workspace/[workspaceId]/home/types'
6+
7+
interface ContextMentionIconProps {
8+
context: ChatMessageContext
9+
/** Only used when context.kind is 'workflow' or 'current_workflow'; ignored otherwise. */
10+
workflowColor?: string | null
11+
/** Applied to every icon element. Include sizing and positional classes (e.g. h-[12px] w-[12px]). */
12+
className: string
13+
}
14+
15+
/** Renders the icon for a context mention chip. Returns null when no icon applies. */
16+
export function ContextMentionIcon({ context, workflowColor, className }: ContextMentionIconProps) {
17+
switch (context.kind) {
18+
case 'workflow':
19+
case 'current_workflow':
20+
return workflowColor ? (
21+
<span
22+
className={cn('rounded-[3px] border-[2px]', className)}
23+
style={{
24+
backgroundColor: workflowColor,
25+
borderColor: workflowBorderColor(workflowColor),
26+
backgroundClip: 'padding-box',
27+
}}
28+
/>
29+
) : null
30+
case 'knowledge':
31+
return <Database className={className} />
32+
case 'table':
33+
return <TableIcon className={className} />
34+
case 'file': {
35+
const FileDocIcon = getDocumentIcon('', context.label)
36+
return <FileDocIcon className={className} />
37+
}
38+
case 'folder':
39+
return <FolderIcon className={className} />
40+
case 'past_chat':
41+
return <Blimp className={className} />
42+
default:
43+
return null
44+
}
45+
}

apps/sim/app/workspace/[workspaceId]/home/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export { ChatMessageAttachments } from './chat-message-attachments'
2+
export { ContextMentionIcon } from './context-mention-icon'
23
export {
34
assistantMessageHasRenderableContent,
45
MessageContent,

apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/mothership-chat.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ interface MothershipChatProps {
3737
userId?: string
3838
chatId?: string
3939
onContextAdd?: (context: ChatContext) => void
40+
onContextRemove?: (context: ChatContext) => void
4041
editValue?: string
4142
onEditValueConsumed?: () => void
4243
layout?: 'mothership-view' | 'copilot-view'
@@ -83,6 +84,7 @@ export function MothershipChat({
8384
userId,
8485
chatId,
8586
onContextAdd,
87+
onContextRemove,
8688
editValue,
8789
onEditValueConsumed,
8890
layout = 'mothership-view',
@@ -207,6 +209,7 @@ export function MothershipChat({
207209
isInitialView={false}
208210
userId={userId}
209211
onContextAdd={onContextAdd}
212+
onContextRemove={onContextRemove}
210213
editValue={editValue}
211214
onEditValueConsumed={onEditValueConsumed}
212215
onEnterWhileEmpty={handleEnterWhileEmpty}

apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/add-resource-dropdown.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import type {
2727
import { useFolders } from '@/hooks/queries/folders'
2828
import { useKnowledgeBasesQuery } from '@/hooks/queries/kb/knowledge'
2929
import { useTablesList } from '@/hooks/queries/tables'
30+
import { useTasks } from '@/hooks/queries/tasks'
3031
import { useWorkflows } from '@/hooks/queries/workflows'
3132
import { useWorkspaceFiles } from '@/hooks/queries/workspace-files'
3233

@@ -53,6 +54,7 @@ export function useAvailableResources(
5354
const { data: files = [] } = useWorkspaceFiles(workspaceId)
5455
const { data: knowledgeBases } = useKnowledgeBasesQuery(workspaceId)
5556
const { data: folders = [] } = useFolders(workspaceId)
57+
const { data: tasks = [] } = useTasks(workspaceId)
5658

5759
return useMemo(
5860
() => [
@@ -97,8 +99,16 @@ export function useAvailableResources(
9799
isOpen: existingKeys.has(`knowledgebase:${kb.id}`),
98100
})),
99101
},
102+
{
103+
type: 'task' as const,
104+
items: tasks.map((t) => ({
105+
id: t.id,
106+
name: t.name,
107+
isOpen: existingKeys.has(`task:${t.id}`),
108+
})),
109+
},
100110
],
101-
[workflows, folders, tables, files, knowledgeBases, existingKeys]
111+
[workflows, folders, tables, files, knowledgeBases, tasks, existingKeys]
102112
)
103113
}
104114

apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
getFileExtension,
2323
getMimeTypeFromExtension,
2424
} from '@/lib/uploads/utils/file-utils'
25+
import { workflowBorderColor } from '@/lib/workspaces/colors'
2526
import {
2627
FileViewer,
2728
type PreviewMode,
@@ -514,7 +515,7 @@ function EmbeddedFolder({ workspaceId, folderId }: EmbeddedFolderProps) {
514515
className='h-[12px] w-[12px] flex-shrink-0 rounded-[3px] border-[2px]'
515516
style={{
516517
backgroundColor: w.color,
517-
borderColor: `${w.color}60`,
518+
borderColor: workflowBorderColor(w.color),
518519
backgroundClip: 'padding-box',
519520
}}
520521
/>

apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-registry.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { type ElementType, type ReactNode, useMemo } from 'react'
44
import type { QueryClient } from '@tanstack/react-query'
55
import { useParams } from 'next/navigation'
66
import {
7+
Blimp,
78
Database,
89
File as FileIcon,
910
Folder as FolderIcon,
@@ -13,12 +14,14 @@ import {
1314
import { WorkflowIcon } from '@/components/icons'
1415
import { getDocumentIcon } from '@/components/icons/document-icons'
1516
import { cn } from '@/lib/core/utils/cn'
17+
import { workflowBorderColor } from '@/lib/workspaces/colors'
1618
import type {
1719
MothershipResource,
1820
MothershipResourceType,
1921
} from '@/app/workspace/[workspaceId]/home/types'
2022
import { knowledgeKeys } from '@/hooks/queries/kb/knowledge'
2123
import { tableKeys } from '@/hooks/queries/tables'
24+
import { taskKeys } from '@/hooks/queries/tasks'
2225
import { folderKeys } from '@/hooks/queries/utils/folder-keys'
2326
import { invalidateWorkflowLists } from '@/hooks/queries/utils/invalidate-workflow-lists'
2427
import { useWorkflows } from '@/hooks/queries/workflows'
@@ -48,7 +51,7 @@ function WorkflowTabSquare({ workflowId, className }: { workflowId: string; clas
4851
className={cn('flex-shrink-0 rounded-[3px] border-[2px]', className)}
4952
style={{
5053
backgroundColor: color,
51-
borderColor: `${color}60`,
54+
borderColor: workflowBorderColor(color),
5255
backgroundClip: 'padding-box',
5356
}}
5457
/>
@@ -63,7 +66,7 @@ function WorkflowDropdownItem({ item }: DropdownItemRenderProps) {
6366
className='h-[14px] w-[14px] flex-shrink-0 rounded-[3px] border-[2px]'
6467
style={{
6568
backgroundColor: color,
66-
borderColor: `${color}60`,
69+
borderColor: workflowBorderColor(color),
6770
backgroundClip: 'padding-box',
6871
}}
6972
/>
@@ -151,6 +154,15 @@ export const RESOURCE_REGISTRY: Record<MothershipResourceType, ResourceTypeConfi
151154
),
152155
renderDropdownItem: (props) => <IconDropdownItem {...props} icon={FolderIcon} />,
153156
},
157+
task: {
158+
type: 'task',
159+
label: 'Tasks',
160+
icon: Blimp,
161+
renderTabIcon: (_resource, className) => (
162+
<Blimp className={cn(className, 'text-[var(--text-icon)]')} />
163+
),
164+
renderDropdownItem: (props) => <IconDropdownItem {...props} icon={Blimp} />,
165+
},
154166
} as const
155167

156168
export const RESOURCE_TYPES = Object.values(RESOURCE_REGISTRY)
@@ -185,6 +197,9 @@ const RESOURCE_INVALIDATORS: Record<
185197
folder: (qc) => {
186198
qc.invalidateQueries({ queryKey: folderKeys.lists() })
187199
},
200+
task: (qc, wId) => {
201+
qc.invalidateQueries({ queryKey: taskKeys.list(wId) })
202+
},
188203
}
189204

190205
/**

apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-tabs/resource-tabs.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
import { Button, Tooltip } from '@/components/emcn'
1111
import { Columns3, Eye, PanelLeft, Pencil } from '@/components/emcn/icons'
1212
import { isEphemeralResource } from '@/lib/copilot/resource-extraction'
13+
import { SIM_RESOURCE_DRAG_TYPE } from '@/lib/copilot/resource-types'
1314
import { cn } from '@/lib/core/utils/cn'
1415
import type { PreviewMode } from '@/app/workspace/[workspaceId]/files/components/file-viewer'
1516
import { AddResourceDropdown } from '@/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown'
@@ -164,7 +165,7 @@ export function ResourceTabs({
164165
const resource = resources[idx]
165166
if (resource) {
166167
e.dataTransfer.setData(
167-
'application/x-sim-resource',
168+
SIM_RESOURCE_DRAG_TYPE,
168169
JSON.stringify({ type: resource.type, id: resource.id, title: resource.title })
169170
)
170171
}

0 commit comments

Comments
 (0)