Skip to content

Commit 8366a84

Browse files
authored
improvement(home): consolidate chat context kind icon registry (#4397)
* improvement(home): consolidate chat context kind icon registry * fix(home): fallback to docs context for unknown resource types * revert(home): drop unneeded mapResourceToContext fallback
1 parent dc195d3 commit 8366a84

3 files changed

Lines changed: 116 additions & 57 deletions

File tree

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import type { ReactNode } from 'react'
2+
import {
3+
Blimp,
4+
Database,
5+
Folder as FolderIcon,
6+
Library,
7+
Table as TableIcon,
8+
} from '@/components/emcn/icons'
9+
import { getDocumentIcon } from '@/components/icons/document-icons'
10+
import { cn } from '@/lib/core/utils/cn'
11+
import { workflowBorderColor } from '@/lib/workspaces/colors'
12+
import type { ChatContextKind, ChatMessageContext } from '@/app/workspace/[workspaceId]/home/types'
13+
14+
interface RenderIconArgs {
15+
context: ChatMessageContext
16+
className: string
17+
workflowColor?: string | null
18+
}
19+
20+
interface ChatContextKindConfig {
21+
/** Human label for the kind (used in tooltips / accessible names). */
22+
label: string
23+
/** Renders the chip icon. Returns null when no icon should be shown for this kind. */
24+
renderIcon: (args: RenderIconArgs) => ReactNode | null
25+
}
26+
27+
function renderWorkflowSquare({ className, workflowColor }: RenderIconArgs): ReactNode | null {
28+
if (!workflowColor) return null
29+
return (
30+
<span
31+
className={cn('rounded-[3px] border-[2px]', className)}
32+
style={{
33+
backgroundColor: workflowColor,
34+
borderColor: workflowBorderColor(workflowColor),
35+
backgroundClip: 'padding-box',
36+
}}
37+
/>
38+
)
39+
}
40+
41+
/**
42+
* Single source of truth for the icon and label associated with each
43+
* {@link ChatContextKind}. The `Record<ChatContextKind, …>` typing forces a
44+
* compile error whenever a new kind is added to the union without a
45+
* corresponding entry here, preventing the chip from silently rendering
46+
* without an icon.
47+
*/
48+
export const CHAT_CONTEXT_KIND_REGISTRY: Record<ChatContextKind, ChatContextKindConfig> = {
49+
workflow: { label: 'Workflow', renderIcon: renderWorkflowSquare },
50+
current_workflow: { label: 'Current workflow', renderIcon: renderWorkflowSquare },
51+
workflow_block: { label: 'Block', renderIcon: renderWorkflowSquare },
52+
blocks: { label: 'Blocks', renderIcon: () => null },
53+
knowledge: {
54+
label: 'Knowledge base',
55+
renderIcon: ({ className }) => <Database className={className} />,
56+
},
57+
table: {
58+
label: 'Table',
59+
renderIcon: ({ className }) => <TableIcon className={className} />,
60+
},
61+
file: {
62+
label: 'File',
63+
renderIcon: ({ context, className }) => {
64+
const FileDocIcon = getDocumentIcon('', context.label)
65+
return <FileDocIcon className={className} />
66+
},
67+
},
68+
folder: {
69+
label: 'Folder',
70+
renderIcon: ({ className }) => <FolderIcon className={className} />,
71+
},
72+
past_chat: {
73+
label: 'Past chat',
74+
renderIcon: ({ className }) => <Blimp className={className} />,
75+
},
76+
logs: {
77+
label: 'Logs',
78+
renderIcon: ({ className }) => <Library className={className} />,
79+
},
80+
templates: { label: 'Templates', renderIcon: () => null },
81+
docs: { label: 'Docs', renderIcon: () => null },
82+
slash_command: { label: 'Command', renderIcon: () => null },
83+
}
Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
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'
1+
import { CHAT_CONTEXT_KIND_REGISTRY } from '@/app/workspace/[workspaceId]/home/components/chat-context-kind-registry'
52
import type { ChatMessageContext } from '@/app/workspace/[workspaceId]/home/types'
63

74
interface ContextMentionIconProps {
@@ -14,32 +11,11 @@ interface ContextMentionIconProps {
1411

1512
/** Renders the icon for a context mention chip. Returns null when no icon applies. */
1613
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-
}
14+
return (
15+
CHAT_CONTEXT_KIND_REGISTRY[context.kind].renderIcon({
16+
context,
17+
className,
18+
workflowColor,
19+
}) ?? null
20+
)
4521
}

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

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { cn } from '@/lib/core/utils/cn'
2-
import type { MothershipResource } from '@/app/workspace/[workspaceId]/home/types'
2+
import type {
3+
MothershipResource,
4+
MothershipResourceType,
5+
} from '@/app/workspace/[workspaceId]/home/types'
36
import type { ChatContext } from '@/stores/panel'
47

58
export interface SpeechRecognitionEvent extends Event {
@@ -72,29 +75,26 @@ export function autoResizeTextarea(e: React.FormEvent<HTMLTextAreaElement>, maxH
7275
target.style.height = `${Math.min(target.scrollHeight, maxHeight)}px`
7376
}
7477

78+
/**
79+
* Maps a {@link MothershipResource} (resource-picker domain) to a
80+
* {@link ChatContext} (chat-input domain). Keyed by `MothershipResourceType`
81+
* so adding a new resource type fails compilation here until a conversion is
82+
* supplied — preventing silent drift between the two taxonomies.
83+
*/
84+
const RESOURCE_TO_CONTEXT: Record<
85+
MothershipResourceType,
86+
(resource: MothershipResource) => ChatContext
87+
> = {
88+
workflow: (r) => ({ kind: 'workflow', workflowId: r.id, label: r.title }),
89+
knowledgebase: (r) => ({ kind: 'knowledge', knowledgeId: r.id, label: r.title }),
90+
table: (r) => ({ kind: 'table', tableId: r.id, label: r.title }),
91+
file: (r) => ({ kind: 'file', fileId: r.id, label: r.title }),
92+
folder: (r) => ({ kind: 'folder', folderId: r.id, label: r.title }),
93+
task: (r) => ({ kind: 'past_chat', chatId: r.id, label: r.title }),
94+
log: (r) => ({ kind: 'logs', executionId: r.id, label: r.title }),
95+
generic: (r) => ({ kind: 'docs', label: r.title }),
96+
}
97+
7598
export function mapResourceToContext(resource: MothershipResource): ChatContext {
76-
switch (resource.type) {
77-
case 'workflow':
78-
return {
79-
kind: 'workflow',
80-
workflowId: resource.id,
81-
label: resource.title,
82-
}
83-
case 'knowledgebase':
84-
return {
85-
kind: 'knowledge',
86-
knowledgeId: resource.id,
87-
label: resource.title,
88-
}
89-
case 'table':
90-
return { kind: 'table', tableId: resource.id, label: resource.title }
91-
case 'file':
92-
return { kind: 'file', fileId: resource.id, label: resource.title }
93-
case 'folder':
94-
return { kind: 'folder', folderId: resource.id, label: resource.title }
95-
case 'task':
96-
return { kind: 'past_chat', chatId: resource.id, label: resource.title }
97-
default:
98-
return { kind: 'docs', label: resource.title }
99-
}
99+
return RESOURCE_TO_CONTEXT[resource.type](resource)
100100
}

0 commit comments

Comments
 (0)