Skip to content

Commit 4ae69d2

Browse files
committed
fix(rich-markdown-editor): fix mention chip losing ambient color (same class as #5573)
Auditing the whole "an element's own explicit color always wins over an inherited one" bug class (previously fixed for strong/em/code/del/s vs. links and h6 in #5573) turned up one more instance: the @-mention chip's label hardcoded text-[var(--text-primary)], which is redundant with the prose default anyway (matching the strong/em/code precedent) and silently overrides any ambient color a mention's container legitimately sets — a link's blue, or h6's dimmer --text-secondary — since a mention is inline content that can appear inside either (e.g. "###### see @some-file"). Removed the hardcoded color entirely so the label inherits correctly in every context, same fix as strong/em/code. The icon's own monochrome --text-icon fallback is untouched (icons intentionally don't follow ambient text color). New test renders MentionChipView directly and asserts the wrapper carries no explicit text-color utility class; verified it fails against the pre-fix className.
1 parent 2ba0b58 commit 4ae69d2

2 files changed

Lines changed: 76 additions & 2 deletions

File tree

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* @vitest-environment jsdom
3+
*
4+
* The chip label must never carry its own explicit text color — see the comment on `CHIP_CLASS` in
5+
* `mention-chip.tsx`. An element's own explicit `color` always wins over an inherited one regardless
6+
* of ancestor specificity, so hardcoding a color here would silently override any ambient color a
7+
* mention's container legitimately sets (a link's blue, an `h6` heading's dimmer `--text-secondary`) —
8+
* the same bug class already fixed for `strong`/`em`/`code` in `rich-markdown-editor.css`.
9+
*/
10+
import { act } from 'react'
11+
import type { Editor } from '@tiptap/react'
12+
import { createRoot, type Root } from 'react-dom/client'
13+
import { afterEach, describe, expect, it, vi } from 'vitest'
14+
15+
vi.mock('next/navigation', () => ({
16+
useRouter: () => ({ push: vi.fn() }),
17+
useParams: () => ({}),
18+
}))
19+
20+
// Override the global `getAllBlocks: () => ({})` stub — `getIconColorMap` iterates it as an array.
21+
vi.mock('@/blocks/registry', () => ({
22+
getAllBlocks: () => [],
23+
}))
24+
25+
const { MentionChipView } = await import('./mention-chip')
26+
27+
function fakeNode(attrs: Record<string, unknown>) {
28+
return { attrs } as unknown as Parameters<typeof MentionChipView>[0]['node']
29+
}
30+
31+
function fakeEditor(): Editor {
32+
return { storage: { mention: { navigable: false } } } as unknown as Editor
33+
}
34+
35+
let container: HTMLDivElement | null = null
36+
let root: Root | null = null
37+
38+
afterEach(() => {
39+
if (root) act(() => root?.unmount())
40+
container?.remove()
41+
container = null
42+
root = null
43+
})
44+
45+
describe('MentionChipView', () => {
46+
it('renders its wrapper with no explicit text-color utility class', async () => {
47+
;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
48+
container = document.createElement('div')
49+
document.body.appendChild(container)
50+
root = createRoot(container)
51+
52+
await act(async () => {
53+
root?.render(
54+
MentionChipView({
55+
node: fakeNode({ kind: 'file', id: 'f1', label: 'notes.md' }),
56+
editor: fakeEditor(),
57+
} as Parameters<typeof MentionChipView>[0])
58+
)
59+
})
60+
61+
const chip = container.querySelector('.mention-chip') as HTMLElement
62+
expect(chip).not.toBeNull()
63+
expect(chip.className).not.toMatch(/text-\[var\(--text-primary\)\]/)
64+
// The icon's own monochrome fallback is unrelated and must be untouched by this fix.
65+
expect(chip.className).toContain('[&>svg]:text-[var(--text-icon)]')
66+
})
67+
})

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,24 @@ import { simLinkPath } from './sim-link'
1515
* color, and a 12px icon. Integration icons keep their brand color via
1616
* {@link getBareIconStyle} (see {@link MentionChipView}); other kinds stay
1717
* monochrome through the `--text-icon` fallback below.
18+
*
19+
* No explicit label color — an element's own explicit `color` always wins over an inherited one
20+
* regardless of ancestor specificity, so hardcoding `--text-primary` here (redundant with the prose
21+
* default anyway) would silently override any ambient color a ancestor legitimately sets — a link's
22+
* blue, or `h6`'s dimmer `--text-secondary` — since a mention is inline content and can appear inside
23+
* either. Omitting it lets the label inherit correctly in both cases, same fix as `strong`/`em`/`code`
24+
* in rich-markdown-editor.css.
1825
*/
1926
const CHIP_CLASS =
20-
'mention-chip mx-px inline-flex items-center gap-1 align-middle text-[var(--text-primary)] leading-[1.5] [&>svg]:size-[12px] [&>svg]:shrink-0 [&>svg]:text-[var(--text-icon)]'
27+
'mention-chip mx-px inline-flex items-center gap-1 align-middle leading-[1.5] [&>svg]:size-[12px] [&>svg]:shrink-0 [&>svg]:text-[var(--text-icon)]'
2128

2229
/**
2330
* Live chip: an entity icon + label matching the chat input's mention rendering. Where the host opted
2431
* into navigation (the file viewer), Cmd/Ctrl-click routes to the resource; in a modal field it stays
2532
* inert so a click can't navigate away from an unsaved edit. This view pulls the block registry (for
2633
* integration brand icons), so it's kept out of the headless {@link MarkdownMention} module.
2734
*/
28-
function MentionChipView({ node, editor }: ReactNodeViewProps) {
35+
export function MentionChipView({ node, editor }: ReactNodeViewProps) {
2936
const router = useRouter()
3037
const params = useParams()
3138
const { kind, id, label } = node.attrs as MentionAttrs

0 commit comments

Comments
 (0)