Skip to content

Commit 2587406

Browse files
committed
chat metadata
1 parent 18dacc5 commit 2587406

File tree

10 files changed

+119
-50
lines changed

10 files changed

+119
-50
lines changed

apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/tool-calls/tool-calls.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { cn } from '@/lib/core/utils/cn'
2-
import type { MothershipToolName, ToolCallStatus, ToolPhase } from '../../../../types'
3-
import { TOOL_UI_METADATA } from '../../../../types'
2+
import type { MothershipToolName, SubagentName, ToolCallStatus, ToolPhase } from '../../../../types'
3+
import { SUBAGENT_LABELS, TOOL_UI_METADATA } from '../../../../types'
44
import { getToolIcon } from '../../utils'
55

66
const STATUS_STYLES: Record<ToolCallStatus, string> = {
@@ -24,14 +24,18 @@ interface ToolCallProps {
2424
displayTitle?: string
2525
status: ToolCallStatus
2626
phaseLabel?: string
27+
calledBy?: string
2728
}
2829

29-
export function ToolCall({ toolName, displayTitle, status, phaseLabel }: ToolCallProps) {
30+
export function ToolCall({ toolName, displayTitle, status, phaseLabel, calledBy }: ToolCallProps) {
3031
const metadata = TOOL_UI_METADATA[toolName as MothershipToolName]
3132
const resolvedTitle = displayTitle || metadata?.title || toolName
3233
const resolvedPhase = phaseLabel || metadata?.phaseLabel
3334
const resolvedPhaseType = metadata?.phase
3435
const Icon = getToolIcon(toolName)
36+
const callerLabel = calledBy
37+
? (SUBAGENT_LABELS[calledBy as SubagentName] ?? calledBy)
38+
: 'Mothership'
3539

3640
return (
3741
<div className='flex items-center gap-[6px]'>
@@ -48,6 +52,7 @@ export function ToolCall({ toolName, displayTitle, status, phaseLabel }: ToolCal
4852
{resolvedPhase}
4953
</span>
5054
)}
55+
<span className='text-[11px] text-[var(--text-quaternary)]'>via {callerLabel}</span>
5156
</div>
5257
)
5358
}

apps/sim/app/workspace/[workspaceId]/home/components/message-content/message-content.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ interface ToolCallSegment {
1616
displayTitle?: string
1717
status: ToolCallStatus
1818
phaseLabel?: string
19+
calledBy?: string
1920
}
2021

2122
interface SubagentSegment {
@@ -92,6 +93,7 @@ function parseBlocks(blocks: ContentBlock[], isStreaming: boolean): MessageSegme
9293
displayTitle: block.toolCall.displayTitle || formatToolName(block.toolCall.name),
9394
status: block.toolCall.status,
9495
phaseLabel: block.toolCall.phaseLabel,
96+
calledBy: block.toolCall.calledBy,
9597
})
9698
}
9799
break
@@ -147,6 +149,7 @@ export function MessageContent({
147149
displayTitle={segment.displayTitle}
148150
status={segment.status}
149151
phaseLabel={segment.phaseLabel}
152+
calledBy={segment.calledBy}
150153
/>
151154
)
152155
case 'subagent':

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ export function UserInput({
9797

9898
onSubmit(fileAttachmentsForApi.length > 0 ? fileAttachmentsForApi : undefined)
9999
files.clearAttachedFiles()
100+
101+
if (textareaRef.current) {
102+
textareaRef.current.style.height = 'auto'
103+
}
100104
}, [onSubmit, files])
101105

102106
const handleKeyDown = useCallback(

apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,15 +200,38 @@ export function useChat(workspaceId: string, initialChatId?: string): UseChatRet
200200
if (restored.length > 0) {
201201
setResources(restored)
202202
setActiveResourceId(restored[restored.length - 1].id)
203+
204+
for (const resource of restored) {
205+
if (resource.type !== 'workflow') continue
206+
const registry = useWorkflowRegistry.getState()
207+
if (!registry.workflows[resource.id]) {
208+
useWorkflowRegistry.setState((state) => ({
209+
workflows: {
210+
...state.workflows,
211+
[resource.id]: {
212+
id: resource.id,
213+
name: resource.title,
214+
lastModified: new Date(),
215+
createdAt: new Date(),
216+
color: '#7F2FFF',
217+
workspaceId,
218+
folderId: null,
219+
sortOrder: 0,
220+
},
221+
},
222+
}))
223+
}
224+
}
203225
}
204-
}, [chatHistory])
226+
}, [chatHistory, workspaceId])
205227

206228
const processSSEStream = useCallback(
207229
async (reader: ReadableStreamDefaultReader<Uint8Array>, assistantId: string) => {
208230
const decoder = new TextDecoder()
209231
let buffer = ''
210232
const blocks: ContentBlock[] = []
211233
const toolMap = new Map<string, number>()
234+
let activeSubagent: string | undefined
212235
let lastTableId: string | null = null
213236
let lastWorkflowId: string | null = null
214237
let runningText = ''
@@ -304,6 +327,7 @@ export function useChat(workspaceId: string, initialChatId?: string): UseChatRet
304327
}
305328
}
306329

330+
if (name.endsWith('_respond')) break
307331
const ui = parsed.ui || data?.ui
308332
if (ui?.hidden) break
309333
const displayTitle = ui?.title || ui?.phaseLabel
@@ -312,7 +336,14 @@ export function useChat(workspaceId: string, initialChatId?: string): UseChatRet
312336
toolMap.set(id, blocks.length)
313337
blocks.push({
314338
type: 'tool_call',
315-
toolCall: { id, name, status: 'executing', displayTitle, phaseLabel },
339+
toolCall: {
340+
id,
341+
name,
342+
status: 'executing',
343+
displayTitle,
344+
phaseLabel,
345+
calledBy: activeSubagent,
346+
},
316347
})
317348
} else {
318349
const idx = toolMap.get(id)!
@@ -424,12 +455,14 @@ export function useChat(workspaceId: string, initialChatId?: string): UseChatRet
424455
case 'subagent_start': {
425456
const name = parsed.subagent || getPayloadData(parsed)?.agent
426457
if (name) {
458+
activeSubagent = name
427459
blocks.push({ type: 'subagent', content: name })
428460
flush()
429461
}
430462
break
431463
}
432464
case 'subagent_end': {
465+
activeSubagent = undefined
433466
flush()
434467
break
435468
}

apps/sim/app/workspace/[workspaceId]/home/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export type MothershipToolName =
6464
| 'research'
6565
| 'plan'
6666
| 'debug'
67+
| 'edit'
6768

6869
/**
6970
* Subagent identifiers dispatched via `subagent_start` SSE events.
@@ -102,6 +103,7 @@ export interface ToolCallInfo {
102103
status: ToolCallStatus
103104
displayTitle?: string
104105
phaseLabel?: string
106+
calledBy?: string
105107
result?: { success: boolean; output?: unknown; error?: string }
106108
}
107109

@@ -180,6 +182,7 @@ export const TOOL_UI_METADATA: Partial<Record<MothershipToolName, ToolUIMetadata
180182
research: { title: 'Researching', phaseLabel: 'Research', phase: 'subagent' },
181183
plan: { title: 'Planning', phaseLabel: 'Plan', phase: 'subagent' },
182184
debug: { title: 'Debugging', phaseLabel: 'Debug', phase: 'subagent' },
185+
edit: { title: 'Editing workflow', phaseLabel: 'Edit', phase: 'subagent' },
183186
}
184187

185188
export interface SSEPayloadUI {

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ export interface WorkflowBlockProps {
1212
isPreview?: boolean
1313
/** Whether this block is selected in preview mode */
1414
isPreviewSelected?: boolean
15+
/** Whether this block is rendered inside an embedded (read-only) workflow view */
16+
isEmbedded?: boolean
1517
subBlockValues?: Record<string, any>
1618
blockState?: any
1719
}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1248,7 +1248,7 @@ export const WorkflowBlock = memo(function WorkflowBlock({
12481248
</div>
12491249
)}
12501250

1251-
{!data.isPreview && (
1251+
{!data.isPreview && !data.isEmbedded && (
12521252
<ActionBar blockId={id} blockType={type} disabled={!userPermissions.canEdit} />
12531253
)}
12541254

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-block-visual.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export function useBlockVisual({
3737
isSelected = false,
3838
}: UseBlockVisualProps) {
3939
const isPreview = data.isPreview ?? false
40+
const isEmbedded = data.isEmbedded ?? false
4041
const isPreviewSelected = data.isPreviewSelected ?? false
4142

4243
const currentWorkflow = useCurrentWorkflow()
@@ -56,24 +57,24 @@ export function useBlockVisual({
5657
const activeTabIsEditor = usePanelStore(
5758
useCallback(
5859
(state) => {
59-
if (isPreview || !isThisBlockInEditor) return false
60+
if (isPreview || isEmbedded || !isThisBlockInEditor) return false
6061
return state.activeTab === 'editor'
6162
},
62-
[isPreview, isThisBlockInEditor]
63+
[isPreview, isEmbedded, isThisBlockInEditor]
6364
)
6465
)
65-
const isEditorOpen = !isPreview && isThisBlockInEditor && activeTabIsEditor
66+
const isEditorOpen = !isPreview && !isEmbedded && isThisBlockInEditor && activeTabIsEditor
6667

6768
const lastRunPath = useLastRunPath()
6869
const runPathStatus = isPreview ? undefined : lastRunPath.get(blockId)
6970

7071
const setCurrentBlockId = usePanelEditorStore((state) => state.setCurrentBlockId)
7172

7273
const handleClick = useCallback(() => {
73-
if (!isPreview) {
74+
if (!isPreview && !isEmbedded) {
7475
setCurrentBlockId(blockId)
7576
}
76-
}, [blockId, setCurrentBlockId, isPreview])
77+
}, [blockId, setCurrentBlockId, isPreview, isEmbedded])
7778

7879
const { hasRing, ringClassName: ringStyles } = useMemo(
7980
() =>
@@ -85,7 +86,7 @@ export function useBlockVisual({
8586
diffStatus: isPreview ? undefined : diffStatus,
8687
runPathStatus,
8788
isPreviewSelection: isPreview && isPreviewSelected,
88-
isSelected: isPreview ? false : isSelected,
89+
isSelected: isPreview || isEmbedded ? false : isSelected,
8990
}),
9091
[
9192
isExecuting,
@@ -95,6 +96,7 @@ export function useBlockVisual({
9596
diffStatus,
9697
runPathStatus,
9798
isPreview,
99+
isEmbedded,
98100
isPreviewSelected,
99101
isSelected,
100102
]

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ const WorkflowContent = React.memo(
243243
const [isErrorConnectionDrag, setIsErrorConnectionDrag] = useState(false)
244244
const selectedIdsRef = useRef<string[] | null>(null)
245245
const canvasMode = useCanvasModeStore((state) => state.mode)
246-
const isHandMode = embedded ? false : canvasMode === 'hand'
246+
const isHandMode = embedded ? true : canvasMode === 'hand'
247247
const { handleCanvasMouseDown, selectionProps } = useShiftSelectionLock({ isHandMode })
248248
const [oauthModal, setOauthModal] = useState<{
249249
provider: OAuthProvider
@@ -2299,6 +2299,8 @@ const WorkflowContent = React.memo(
22992299

23002300
/** Handles navigation validation and redirects for invalid workflow IDs. */
23012301
useEffect(() => {
2302+
if (embedded) return
2303+
23022304
// Wait for metadata to finish loading before making navigation decisions
23032305
if (hydration.phase === 'metadata-loading' || hydration.phase === 'idle') {
23042306
return
@@ -2341,6 +2343,7 @@ const WorkflowContent = React.memo(
23412343
router.replace(`/workspace/${workflowData.workspaceId}/w/${workflowIdParam}`)
23422344
}
23432345
}, [
2346+
embedded,
23442347
workflowIdParam,
23452348
currentWorkflowExists,
23462349
workflowCount,
@@ -2480,6 +2483,7 @@ const WorkflowContent = React.memo(
24802483
name: block.name,
24812484
isActive,
24822485
isPending,
2486+
...(embedded && { isEmbedded: true }),
24832487
},
24842488
// Include dynamic dimensions for container resizing calculations (must match rendered size)
24852489
// Both note and workflow blocks calculate dimensions deterministically via useBlockDimensions
@@ -3862,14 +3866,14 @@ const WorkflowContent = React.memo(
38623866
onSelectionContextMenu={handleSelectionContextMenu}
38633867
onPointerMove={handleCanvasPointerMove}
38643868
onPointerLeave={handleCanvasPointerLeave}
3865-
elementsSelectable={true}
3866-
selectionOnDrag={selectionProps.selectionOnDrag}
3869+
elementsSelectable={!embedded}
3870+
selectionOnDrag={embedded ? false : selectionProps.selectionOnDrag}
38673871
selectionMode={SelectionMode.Partial}
3868-
panOnDrag={selectionProps.panOnDrag}
3869-
selectionKeyCode={selectionProps.selectionKeyCode}
3870-
multiSelectionKeyCode={['Meta', 'Control', 'Shift']}
3871-
nodesConnectable={effectivePermissions.canEdit}
3872-
nodesDraggable={effectivePermissions.canEdit}
3872+
panOnDrag={embedded ? true : selectionProps.panOnDrag}
3873+
selectionKeyCode={embedded ? null : selectionProps.selectionKeyCode}
3874+
multiSelectionKeyCode={embedded ? null : ['Meta', 'Control', 'Shift']}
3875+
nodesConnectable={!embedded && effectivePermissions.canEdit}
3876+
nodesDraggable={!embedded && effectivePermissions.canEdit}
38733877
draggable={false}
38743878
noWheelClassName='allow-scroll'
38753879
edgesFocusable={true}

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx

Lines changed: 43 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ export const Sidebar = memo(function Sidebar() {
381381
[fetchedTasks, workspaceId]
382382
)
383383

384+
const [visibleTaskCount, setVisibleTaskCount] = useState(5)
384385
const [renamingTaskId, setRenamingTaskId] = useState<string | null>(null)
385386
const [renameValue, setRenameValue] = useState('')
386387
const renameInputRef = useRef<HTMLInputElement>(null)
@@ -803,43 +804,55 @@ export const Sidebar = memo(function Sidebar() {
803804
{tasksLoading ? (
804805
<SidebarItemSkeleton />
805806
) : (
806-
tasks.map((task) => {
807-
const active = task.id !== 'new' && pathname === task.href
808-
const isRenaming = renamingTaskId === task.id
807+
<>
808+
{tasks.slice(0, visibleTaskCount).map((task) => {
809+
const active = task.id !== 'new' && pathname === task.href
810+
const isRenaming = renamingTaskId === task.id
811+
812+
if (isRenaming) {
813+
return (
814+
<div
815+
key={task.id}
816+
className='mx-[2px] flex h-[30px] items-center gap-[8px] rounded-[8px] bg-[var(--surface-active)] px-[8px] text-[14px]'
817+
>
818+
<Blimp className='h-[16px] w-[16px] flex-shrink-0 text-[var(--text-icon)]' />
819+
<input
820+
ref={renameInputRef}
821+
value={renameValue}
822+
onChange={(e) => setRenameValue(e.target.value)}
823+
onKeyDown={handleRenameKeyDown}
824+
onBlur={handleSaveTaskRename}
825+
className='min-w-0 flex-1 border-none bg-transparent font-base text-[14px] text-[var(--text-body)] outline-none'
826+
/>
827+
</div>
828+
)
829+
}
809830

810-
if (isRenaming) {
811831
return (
812-
<div
832+
<Link
813833
key={task.id}
814-
className='mx-[2px] flex h-[30px] items-center gap-[8px] rounded-[8px] bg-[var(--surface-active)] px-[8px] text-[14px]'
834+
href={task.href}
835+
className={`mx-[2px] flex h-[30px] items-center gap-[8px] rounded-[8px] px-[8px] text-[14px] hover:bg-[var(--surface-active)] ${active ? 'bg-[var(--surface-active)]' : ''}`}
836+
onContextMenu={(e) => handleTaskContextMenu(e, task.href, task.id)}
815837
>
816838
<Blimp className='h-[16px] w-[16px] flex-shrink-0 text-[var(--text-icon)]' />
817-
<input
818-
ref={renameInputRef}
819-
value={renameValue}
820-
onChange={(e) => setRenameValue(e.target.value)}
821-
onKeyDown={handleRenameKeyDown}
822-
onBlur={handleSaveTaskRename}
823-
className='min-w-0 flex-1 border-none bg-transparent font-base text-[14px] text-[var(--text-body)] outline-none'
824-
/>
825-
</div>
839+
<div className='min-w-0 truncate font-[var(--sidebar-font-weight)] text-[var(--text-body)]'>
840+
{task.name}
841+
</div>
842+
</Link>
826843
)
827-
}
828-
829-
return (
830-
<Link
831-
key={task.id}
832-
href={task.href}
833-
className={`mx-[2px] flex h-[30px] items-center gap-[8px] rounded-[8px] px-[8px] text-[14px] hover:bg-[var(--surface-active)] ${active ? 'bg-[var(--surface-active)]' : ''}`}
834-
onContextMenu={(e) => handleTaskContextMenu(e, task.href, task.id)}
844+
})}
845+
{tasks.length > visibleTaskCount && (
846+
<button
847+
type='button'
848+
onClick={() => setVisibleTaskCount((prev) => prev + 5)}
849+
className='mx-[2px] flex h-[30px] items-center gap-[8px] rounded-[8px] px-[8px] text-[14px] text-[var(--text-icon)] hover:bg-[var(--surface-active)]'
835850
>
836-
<Blimp className='h-[16px] w-[16px] flex-shrink-0 text-[var(--text-icon)]' />
837-
<div className='min-w-0 truncate font-[var(--sidebar-font-weight)] text-[var(--text-body)]'>
838-
{task.name}
839-
</div>
840-
</Link>
841-
)
842-
})
851+
<MoreHorizontal className='h-[16px] w-[16px] flex-shrink-0' />
852+
<span className='font-[var(--sidebar-font-weight)]'>See more</span>
853+
</button>
854+
)}
855+
</>
843856
)}
844857
</div>
845858
</div>

0 commit comments

Comments
 (0)