Skip to content

Commit ce09e02

Browse files
committed
fix(rich-editor): integration mentions are display-only; robust chip selection
- An integration mention's id is a block type (gmail_v2), not a routable resource — /integrations/[block] expects a slug and a type maps to zero-or-many credentials — so it no longer links to a 404. The chip only shows a pointer/navigates on kinds that resolve to a real page. - Scope the block-leaf selection ring off the mention chip robustly (covers a node-view wrapper via :has), so a selected chip shows a subtle fill, not the outline.
1 parent e717cf8 commit ce09e02

4 files changed

Lines changed: 22 additions & 18 deletions

File tree

apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/mention/mention-node.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,27 +131,27 @@ function MentionChipView({ node, editor }: ReactNodeViewProps) {
131131
const router = useRouter()
132132
const params = useParams()
133133
const { kind, id, label } = node.attrs as MentionAttrs
134-
const Icon = mentionIcon(kind, id) as StyleableIcon | undefined
135-
const iconStyle = Icon ? getBareIconStyle(Icon) : undefined
134+
const Icon = mentionIcon(kind, id) as StyleableIcon
135+
const iconStyle = getBareIconStyle(Icon)
136136
const navigable = editor.storage.mention?.navigable === true
137+
const workspaceId = typeof params.workspaceId === 'string' ? params.workspaceId : undefined
138+
// Only show the pointer / route on a kind that actually resolves to a page (e.g. not an integration).
139+
const path = navigable && workspaceId ? simLinkPath(workspaceId, kind, id) : null
137140

138141
const handleClick = (event: MouseEvent) => {
139-
if (!(event.metaKey || event.ctrlKey)) return
140-
const workspaceId = typeof params.workspaceId === 'string' ? params.workspaceId : undefined
141-
const path = workspaceId && simLinkPath(workspaceId, kind, id)
142-
if (!path) return
142+
if (!path || !(event.metaKey || event.ctrlKey)) return
143143
event.preventDefault()
144144
router.push(path)
145145
}
146146

147147
return (
148148
<NodeViewWrapper
149149
as='span'
150-
className={cn(CHIP_CLASS, navigable && 'cursor-pointer')}
151-
onClick={navigable ? handleClick : undefined}
150+
className={cn(CHIP_CLASS, path && 'cursor-pointer')}
151+
onClick={path ? handleClick : undefined}
152152
title={label}
153153
>
154-
{Icon && <Icon style={iconStyle} />}
154+
<Icon style={iconStyle} />
155155
<span>{label}</span>
156156
</NodeViewWrapper>
157157
)

apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/mention/sim-link.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@ describe('simLinkPath', () => {
2525
expect(simLinkPath(ws, 'knowledge', 'k1')).toBe('/workspace/ws1/knowledge/k1')
2626
expect(simLinkPath(ws, 'workflow', 'w1')).toBe('/workspace/ws1/w/w1')
2727
expect(simLinkPath(ws, 'skill', 's1')).toBe('/workspace/ws1/skills?skillId=s1')
28-
expect(simLinkPath(ws, 'integration', 'slack')).toBe('/workspace/ws1/integrations/slack')
2928
})
3029

31-
it('returns null for an unknown kind', () => {
30+
it('returns null for kinds with no navigable resource (integration) and unknown kinds', () => {
31+
// An integration mention's id is a block type, not a routable resource.
32+
expect(simLinkPath(ws, 'integration', 'slack')).toBeNull()
3233
expect(simLinkPath(ws, 'mystery', 'x')).toBeNull()
3334
})
3435
})

apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/mention/sim-link.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@ export function parseSimHref(href: string): { kind: string; id: string } | null
1919
}
2020

2121
/**
22-
* Resolves the in-app route for a clicked `sim:` mention link, or `null` for an unknown kind. Each
23-
* destination matches the entity's real route: files open the file detail view, folders/skills deep-link
24-
* the file browser / skills modal via their query params, the rest hit their `[id]` route.
22+
* Resolves the in-app route for a clicked `sim:` mention, or `null` when the kind has no navigable
23+
* destination. Each path matches the entity's real route: files open the file detail view,
24+
* folders/skills deep-link the file browser / skills modal via their query params, the rest hit their
25+
* `[id]` route. Integrations are intentionally non-navigable — a mention's id is a block *type*
26+
* (`gmail_v2`), which isn't a routable resource (no per-type page; it maps to zero-or-many
27+
* credentials), so the chip stays display-only.
2528
*/
2629
export function simLinkPath(workspaceId: string, kind: string, id: string): string | null {
2730
const base = `/workspace/${workspaceId}`
@@ -38,8 +41,6 @@ export function simLinkPath(workspaceId: string, kind: string, id: string): stri
3841
return `${base}/w/${id}`
3942
case 'skill':
4043
return `${base}/skills?skillId=${id}`
41-
case 'integration':
42-
return `${base}/integrations/${id}`
4344
default:
4445
return null
4546
}

apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/rich-markdown-editor.css

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@
3737
}
3838

3939
/* The inline mention chip is too small for the block-leaf ring above; a direct click selects it with a
40-
subtle fill instead, so it never wears the heavy divider/image outline. */
41-
.rich-markdown-prose .mention-chip.ProseMirror-selectednode {
40+
subtle fill instead, so it never wears the heavy divider/image outline. `:has` covers the case where
41+
ProseMirror puts `selectednode` on a wrapper around the chip span rather than the span itself. */
42+
.rich-markdown-prose .mention-chip.ProseMirror-selectednode,
43+
.rich-markdown-prose .ProseMirror-selectednode:has(.mention-chip) {
4244
outline: none;
4345
border-radius: 3px;
4446
background: var(--surface-active);

0 commit comments

Comments
 (0)