diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/chat-context-kind-registry.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/chat-context-kind-registry.tsx
new file mode 100644
index 00000000000..efcae7e3394
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/chat-context-kind-registry.tsx
@@ -0,0 +1,83 @@
+import type { ReactNode } from 'react'
+import {
+ Blimp,
+ Database,
+ Folder as FolderIcon,
+ Library,
+ 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 { ChatContextKind, ChatMessageContext } from '@/app/workspace/[workspaceId]/home/types'
+
+interface RenderIconArgs {
+ context: ChatMessageContext
+ className: string
+ workflowColor?: string | null
+}
+
+interface ChatContextKindConfig {
+ /** Human label for the kind (used in tooltips / accessible names). */
+ label: string
+ /** Renders the chip icon. Returns null when no icon should be shown for this kind. */
+ renderIcon: (args: RenderIconArgs) => ReactNode | null
+}
+
+function renderWorkflowSquare({ className, workflowColor }: RenderIconArgs): ReactNode | null {
+ if (!workflowColor) return null
+ return (
+
+ )
+}
+
+/**
+ * Single source of truth for the icon and label associated with each
+ * {@link ChatContextKind}. The `Record` typing forces a
+ * compile error whenever a new kind is added to the union without a
+ * corresponding entry here, preventing the chip from silently rendering
+ * without an icon.
+ */
+export const CHAT_CONTEXT_KIND_REGISTRY: Record = {
+ workflow: { label: 'Workflow', renderIcon: renderWorkflowSquare },
+ current_workflow: { label: 'Current workflow', renderIcon: renderWorkflowSquare },
+ workflow_block: { label: 'Block', renderIcon: renderWorkflowSquare },
+ blocks: { label: 'Blocks', renderIcon: () => null },
+ knowledge: {
+ label: 'Knowledge base',
+ renderIcon: ({ className }) => ,
+ },
+ table: {
+ label: 'Table',
+ renderIcon: ({ className }) => ,
+ },
+ file: {
+ label: 'File',
+ renderIcon: ({ context, className }) => {
+ const FileDocIcon = getDocumentIcon('', context.label)
+ return
+ },
+ },
+ folder: {
+ label: 'Folder',
+ renderIcon: ({ className }) => ,
+ },
+ past_chat: {
+ label: 'Past chat',
+ renderIcon: ({ className }) => ,
+ },
+ logs: {
+ label: 'Logs',
+ renderIcon: ({ className }) => ,
+ },
+ templates: { label: 'Templates', renderIcon: () => null },
+ docs: { label: 'Docs', renderIcon: () => null },
+ slash_command: { label: 'Command', renderIcon: () => null },
+}
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/context-mention-icon.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/context-mention-icon.tsx
index 1e7a2514620..3f739bb0e40 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/context-mention-icon.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/context-mention-icon.tsx
@@ -1,7 +1,4 @@
-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 { CHAT_CONTEXT_KIND_REGISTRY } from '@/app/workspace/[workspaceId]/home/components/chat-context-kind-registry'
import type { ChatMessageContext } from '@/app/workspace/[workspaceId]/home/types'
interface ContextMentionIconProps {
@@ -14,32 +11,11 @@ interface ContextMentionIconProps {
/** 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 ? (
-
- ) : null
- case 'knowledge':
- return
- case 'table':
- return
- case 'file': {
- const FileDocIcon = getDocumentIcon('', context.label)
- return
- }
- case 'folder':
- return
- case 'past_chat':
- return
- default:
- return null
- }
+ return (
+ CHAT_CONTEXT_KIND_REGISTRY[context.kind].renderIcon({
+ context,
+ className,
+ workflowColor,
+ }) ?? null
+ )
}
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts
index a689fb98801..59d31ed8615 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts
@@ -1,5 +1,8 @@
import { cn } from '@/lib/core/utils/cn'
-import type { MothershipResource } from '@/app/workspace/[workspaceId]/home/types'
+import type {
+ MothershipResource,
+ MothershipResourceType,
+} from '@/app/workspace/[workspaceId]/home/types'
import type { ChatContext } from '@/stores/panel'
export interface SpeechRecognitionEvent extends Event {
@@ -72,29 +75,26 @@ export function autoResizeTextarea(e: React.FormEvent, maxH
target.style.height = `${Math.min(target.scrollHeight, maxHeight)}px`
}
+/**
+ * Maps a {@link MothershipResource} (resource-picker domain) to a
+ * {@link ChatContext} (chat-input domain). Keyed by `MothershipResourceType`
+ * so adding a new resource type fails compilation here until a conversion is
+ * supplied — preventing silent drift between the two taxonomies.
+ */
+const RESOURCE_TO_CONTEXT: Record<
+ MothershipResourceType,
+ (resource: MothershipResource) => ChatContext
+> = {
+ workflow: (r) => ({ kind: 'workflow', workflowId: r.id, label: r.title }),
+ knowledgebase: (r) => ({ kind: 'knowledge', knowledgeId: r.id, label: r.title }),
+ table: (r) => ({ kind: 'table', tableId: r.id, label: r.title }),
+ file: (r) => ({ kind: 'file', fileId: r.id, label: r.title }),
+ folder: (r) => ({ kind: 'folder', folderId: r.id, label: r.title }),
+ task: (r) => ({ kind: 'past_chat', chatId: r.id, label: r.title }),
+ log: (r) => ({ kind: 'logs', executionId: r.id, label: r.title }),
+ generic: (r) => ({ kind: 'docs', label: r.title }),
+}
+
export function mapResourceToContext(resource: MothershipResource): ChatContext {
- switch (resource.type) {
- case 'workflow':
- return {
- kind: 'workflow',
- workflowId: resource.id,
- label: resource.title,
- }
- case 'knowledgebase':
- return {
- kind: 'knowledge',
- knowledgeId: resource.id,
- label: resource.title,
- }
- case 'table':
- return { kind: 'table', tableId: resource.id, label: resource.title }
- case 'file':
- 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 }
- }
+ return RESOURCE_TO_CONTEXT[resource.type](resource)
}