Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
2a8d23d
feat(chat): drag workflows and folders from sidebar into chat input
waleedlatif1 Apr 8, 2026
c0dd830
fix(chat): fix effectAllowed, stale atInsertPosRef, and drag-enter ov…
waleedlatif1 Apr 8, 2026
9cfd1ee
feat(chat): add task dragging and visible drag ghost for sidebar items
waleedlatif1 Apr 8, 2026
3bdedad
feat(sidebar): add drag ghost with icons and task icon to context chips
waleedlatif1 Apr 8, 2026
0ebee18
refactor(types): narrow ChatMessageContext.kind to ChatContextKind un…
waleedlatif1 Apr 8, 2026
86e7c9e
feat(user-input): support Tab to select resource in mention dropdown
waleedlatif1 Apr 8, 2026
4e1fa16
fix(user-input): narrow ChatContext discriminated union before access…
waleedlatif1 Apr 8, 2026
fccf98d
fix(colors): overload workflowBorderColor to accept string | undefined
waleedlatif1 Apr 8, 2026
4ff9b84
fix(colors): simplify workflowBorderColor to single string | undefine…
waleedlatif1 Apr 8, 2026
7b8fa00
fix(chat): remove resource panel tab when context mention is deleted …
waleedlatif1 Apr 8, 2026
6d31c36
fix(chat): use resource ID for context removal identity check
waleedlatif1 Apr 8, 2026
4d492a4
fix(chat): add folder/task cases to resource resolver, task key to ex…
waleedlatif1 Apr 8, 2026
abaac15
revert(chat): remove folder/task from resolveResourceFromContext — no…
waleedlatif1 Apr 8, 2026
669f2ac
fix(chat): add chatId to stored context types and workflow.color to d…
waleedlatif1 Apr 8, 2026
e471b2e
fix(chat): guard chatId before adding task key to existingResourceKeys
waleedlatif1 Apr 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
xAIIcon,
} from '@/components/icons'
import { cn } from '@/lib/core/utils/cn'
import { workflowBorderColor } from '@/lib/workspaces/colors'

interface FeaturesPreviewProps {
activeTab: number
Expand Down Expand Up @@ -383,7 +384,7 @@ function MiniCardIcon({ variant, color }: { variant: CardVariant; color?: string
className='h-[7px] w-[7px] flex-shrink-0 rounded-[1.5px] border'
style={{
backgroundColor: c,
borderColor: `${c}60`,
borderColor: workflowBorderColor(c),
backgroundClip: 'padding-box',
}}
/>
Expand Down Expand Up @@ -470,7 +471,7 @@ function WorkflowCardBody({ color }: { color: string }) {
className='absolute top-2.5 left-[40px] h-[14px] w-[14px] rounded-[3px] border-[2px]'
style={{
backgroundColor: color,
borderColor: `${color}60`,
borderColor: workflowBorderColor(color),
backgroundClip: 'padding-box',
}}
/>
Expand All @@ -481,7 +482,7 @@ function WorkflowCardBody({ color }: { color: string }) {
className='absolute top-[36px] left-[68px] h-[14px] w-[14px] rounded-[3px] border-[2px]'
style={{
backgroundColor: color,
borderColor: `${color}60`,
borderColor: workflowBorderColor(color),
backgroundClip: 'padding-box',
opacity: 0.5,
}}
Expand Down Expand Up @@ -896,7 +897,7 @@ function MockLogDetailsSidebar({ selectedRow, onPrev, onNext }: MockLogDetailsSi
className='h-[10px] w-[10px] shrink-0 rounded-[3px] border-[1.5px]'
style={{
backgroundColor: color,
borderColor: `${color}60`,
borderColor: workflowBorderColor(color),
backgroundClip: 'padding-box',
}}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Download } from 'lucide-react'
import { ArrowUpDown, Badge, Library, ListFilter, Search } from '@/components/emcn'
import type { BadgeProps } from '@/components/emcn/components/badge/badge'
import { cn } from '@/lib/core/utils/cn'
import { workflowBorderColor } from '@/lib/workspaces/colors'

interface LogRow {
id: string
Expand Down Expand Up @@ -283,7 +284,7 @@ export function LandingPreviewLogs() {
className='h-[10px] w-[10px] flex-shrink-0 rounded-[3px] border-[1.5px]'
style={{
backgroundColor: log.workflowColor,
borderColor: `${log.workflowColor}60`,
borderColor: workflowBorderColor(log.workflowColor),
backgroundClip: 'padding-box',
}}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
Table,
} from '@/components/emcn/icons'
import { cn } from '@/lib/core/utils/cn'
import { workflowBorderColor } from '@/lib/workspaces/colors'
import type { PreviewWorkflow } from '@/app/(landing)/components/landing-preview/components/landing-preview-workflow/workflow-data'

export type SidebarView =
Expand Down Expand Up @@ -211,7 +212,7 @@ export function LandingPreviewSidebar({
className='h-[14px] w-[14px] flex-shrink-0 rounded-[4px] border-[2.5px]'
style={{
backgroundColor: workflow.color,
borderColor: `${workflow.color}60`,
borderColor: workflowBorderColor(workflow.color),
backgroundClip: 'padding-box',
}}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Blimp, Database, Folder as FolderIcon, Table as TableIcon } from '@/components/emcn/icons'
import { getDocumentIcon } from '@/components/icons/document-icons'
import { cn } from '@/lib/core/utils/cn'
import { workflowBorderColor } from '@/lib/workspaces/colors'
import type { ChatMessageContext } from '@/app/workspace/[workspaceId]/home/types'

interface ContextMentionIconProps {
context: ChatMessageContext
/** Only used when context.kind is 'workflow' or 'current_workflow'; ignored otherwise. */
workflowColor?: string | null
/** Applied to every icon element. Include sizing and positional classes (e.g. h-[12px] w-[12px]). */
className: string
}

/** Renders the icon for a context mention chip. Returns null when no icon applies. */
export function ContextMentionIcon({ context, workflowColor, className }: ContextMentionIconProps) {
switch (context.kind) {
case 'workflow':
case 'current_workflow':
return workflowColor ? (
<span
className={cn('rounded-[3px] border-[2px]', className)}
style={{
backgroundColor: workflowColor,
borderColor: workflowBorderColor(workflowColor),
backgroundClip: 'padding-box',
}}
/>
) : null
case 'knowledge':
return <Database className={className} />
case 'table':
return <TableIcon className={className} />
case 'file': {
const FileDocIcon = getDocumentIcon('', context.label)
return <FileDocIcon className={className} />
}
case 'folder':
return <FolderIcon className={className} />
case 'past_chat':
return <Blimp className={className} />
default:
return null
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { ChatMessageAttachments } from './chat-message-attachments'
export { ContextMentionIcon } from './context-mention-icon'
export {
assistantMessageHasRenderableContent,
MessageContent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ interface MothershipChatProps {
userId?: string
chatId?: string
onContextAdd?: (context: ChatContext) => void
onContextRemove?: (context: ChatContext) => void
editValue?: string
onEditValueConsumed?: () => void
layout?: 'mothership-view' | 'copilot-view'
Expand Down Expand Up @@ -83,6 +84,7 @@ export function MothershipChat({
userId,
chatId,
onContextAdd,
onContextRemove,
editValue,
onEditValueConsumed,
layout = 'mothership-view',
Expand Down Expand Up @@ -207,6 +209,7 @@ export function MothershipChat({
isInitialView={false}
userId={userId}
onContextAdd={onContextAdd}
onContextRemove={onContextRemove}
editValue={editValue}
onEditValueConsumed={onEditValueConsumed}
onEnterWhileEmpty={handleEnterWhileEmpty}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import type {
import { useFolders } from '@/hooks/queries/folders'
import { useKnowledgeBasesQuery } from '@/hooks/queries/kb/knowledge'
import { useTablesList } from '@/hooks/queries/tables'
import { useTasks } from '@/hooks/queries/tasks'
import { useWorkflows } from '@/hooks/queries/workflows'
import { useWorkspaceFiles } from '@/hooks/queries/workspace-files'

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

return useMemo(
() => [
Expand Down Expand Up @@ -97,8 +99,16 @@ export function useAvailableResources(
isOpen: existingKeys.has(`knowledgebase:${kb.id}`),
})),
},
{
type: 'task' as const,
items: tasks.map((t) => ({
id: t.id,
name: t.name,
isOpen: existingKeys.has(`task:${t.id}`),
})),
},
],
[workflows, folders, tables, files, knowledgeBases, existingKeys]
[workflows, folders, tables, files, knowledgeBases, tasks, existingKeys]
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
getFileExtension,
getMimeTypeFromExtension,
} from '@/lib/uploads/utils/file-utils'
import { workflowBorderColor } from '@/lib/workspaces/colors'
import {
FileViewer,
type PreviewMode,
Expand Down Expand Up @@ -514,7 +515,7 @@ function EmbeddedFolder({ workspaceId, folderId }: EmbeddedFolderProps) {
className='h-[12px] w-[12px] flex-shrink-0 rounded-[3px] border-[2px]'
style={{
backgroundColor: w.color,
borderColor: `${w.color}60`,
borderColor: workflowBorderColor(w.color),
backgroundClip: 'padding-box',
}}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { type ElementType, type ReactNode, useMemo } from 'react'
import type { QueryClient } from '@tanstack/react-query'
import { useParams } from 'next/navigation'
import {
Blimp,
Database,
File as FileIcon,
Folder as FolderIcon,
Expand All @@ -13,12 +14,14 @@ import {
import { WorkflowIcon } from '@/components/icons'
import { getDocumentIcon } from '@/components/icons/document-icons'
import { cn } from '@/lib/core/utils/cn'
import { workflowBorderColor } from '@/lib/workspaces/colors'
import type {
MothershipResource,
MothershipResourceType,
} from '@/app/workspace/[workspaceId]/home/types'
import { knowledgeKeys } from '@/hooks/queries/kb/knowledge'
import { tableKeys } from '@/hooks/queries/tables'
import { taskKeys } from '@/hooks/queries/tasks'
import { folderKeys } from '@/hooks/queries/utils/folder-keys'
import { invalidateWorkflowLists } from '@/hooks/queries/utils/invalidate-workflow-lists'
import { useWorkflows } from '@/hooks/queries/workflows'
Expand Down Expand Up @@ -48,7 +51,7 @@ function WorkflowTabSquare({ workflowId, className }: { workflowId: string; clas
className={cn('flex-shrink-0 rounded-[3px] border-[2px]', className)}
style={{
backgroundColor: color,
borderColor: `${color}60`,
borderColor: workflowBorderColor(color),
backgroundClip: 'padding-box',
}}
/>
Expand All @@ -63,7 +66,7 @@ function WorkflowDropdownItem({ item }: DropdownItemRenderProps) {
className='h-[14px] w-[14px] flex-shrink-0 rounded-[3px] border-[2px]'
style={{
backgroundColor: color,
borderColor: `${color}60`,
borderColor: workflowBorderColor(color),
backgroundClip: 'padding-box',
}}
/>
Expand Down Expand Up @@ -151,6 +154,15 @@ export const RESOURCE_REGISTRY: Record<MothershipResourceType, ResourceTypeConfi
),
renderDropdownItem: (props) => <IconDropdownItem {...props} icon={FolderIcon} />,
},
task: {
type: 'task',
label: 'Tasks',
icon: Blimp,
renderTabIcon: (_resource, className) => (
<Blimp className={cn(className, 'text-[var(--text-icon)]')} />
),
renderDropdownItem: (props) => <IconDropdownItem {...props} icon={Blimp} />,
},
} as const

export const RESOURCE_TYPES = Object.values(RESOURCE_REGISTRY)
Expand Down Expand Up @@ -185,6 +197,9 @@ const RESOURCE_INVALIDATORS: Record<
folder: (qc) => {
qc.invalidateQueries({ queryKey: folderKeys.lists() })
},
task: (qc, wId) => {
qc.invalidateQueries({ queryKey: taskKeys.list(wId) })
},
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import { Button, Tooltip } from '@/components/emcn'
import { Columns3, Eye, PanelLeft, Pencil } from '@/components/emcn/icons'
import { isEphemeralResource } from '@/lib/copilot/resource-extraction'
import { SIM_RESOURCE_DRAG_TYPE } from '@/lib/copilot/resource-types'
import { cn } from '@/lib/core/utils/cn'
import type { PreviewMode } from '@/app/workspace/[workspaceId]/files/components/file-viewer'
import { AddResourceDropdown } from '@/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown'
Expand Down Expand Up @@ -164,7 +165,7 @@ export function ResourceTabs({
const resource = resources[idx]
if (resource) {
e.dataTransfer.setData(
'application/x-sim-resource',
SIM_RESOURCE_DRAG_TYPE,
JSON.stringify({ type: resource.type, id: resource.id, title: resource.title })
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ export function mapResourceToContext(resource: MothershipResource): ChatContext
return { kind: 'file', fileId: resource.id, label: resource.title }
case 'folder':
return { kind: 'folder', folderId: resource.id, label: resource.title }
case 'task':
return { kind: 'past_chat', chatId: resource.id, label: resource.title }
default:
return { kind: 'docs', label: resource.title }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export const PlusMenuDropdown = React.memo(
e.preventDefault()
const firstItem = contentRef.current?.querySelector<HTMLElement>('[role="menuitem"]')
firstItem?.focus()
} else if (e.key === 'Enter') {
} else if (e.key === 'Enter' || e.key === 'Tab') {
e.preventDefault()
const first = filteredItemsRef.current?.[0]
if (first) handleSelect({ type: first.type, id: first.item.id, title: first.item.name })
Expand All @@ -99,6 +99,12 @@ export const PlusMenuDropdown = React.memo(
e.preventDefault()
searchRef.current?.focus()
}
} else if (e.key === 'Tab') {
const focused = document.activeElement as HTMLElement | null
if (focused?.getAttribute('role') === 'menuitem') {
e.preventDefault()
focused.click()
}
}
}, [])

Expand Down
Loading
Loading