Skip to content

Commit 636f05a

Browse files
committed
fix(chat): preserve literal tags when copying
1 parent d409b0d commit 636f05a

4 files changed

Lines changed: 33 additions & 11 deletions

File tree

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

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,27 +24,21 @@ import { useSubmitCopilotFeedback } from '@/hooks/queries/copilot-feedback'
2424
import { useForkMothershipChat } from '@/hooks/queries/mothership-chats'
2525
import { useFolderStore } from '@/stores/folders/store'
2626

27-
const SPECIAL_TAGS = 'thinking|options|usage_upgrade|credential|mothership-error|file|question'
28-
29-
export function toCopyableMarkdown(raw: string): string {
30-
return raw
31-
.replace(new RegExp(`<\\/?(${SPECIAL_TAGS})(?:>[\\s\\S]*?<\\/(${SPECIAL_TAGS})>|>)`, 'g'), '')
32-
.trim()
33-
}
34-
3527
const ICON_CLASS = 'size-[14px]'
3628
const BUTTON_CLASS =
3729
'flex size-[26px] items-center justify-center rounded-[6px] text-[var(--text-icon)] transition-colors hover-hover:bg-[var(--surface-hover)] focus-visible:outline-none'
3830

3931
interface MessageActionsProps {
4032
content: string
33+
prepareContentForCopy?: (content: string) => string
4134
userQuery?: string
4235
requestId?: string
4336
messageId?: string
4437
}
4538

4639
export const MessageActions = memo(function MessageActions({
4740
content,
41+
prepareContentForCopy,
4842
userQuery,
4943
requestId,
5044
messageId,
@@ -70,7 +64,7 @@ export const MessageActions = memo(function MessageActions({
7064

7165
const copyToClipboard = () => {
7266
if (!content) return
73-
const markdown = toCopyableMarkdown(content)
67+
const markdown = prepareContentForCopy?.(content) ?? content
7468
if (!markdown) return
7569
void copyMessage(markdown)
7670
}

apps/sim/app/workspace/[workspaceId]/components/message-actions/message-actions.test.ts renamed to apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/copyable-markdown.test.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, it } from 'vitest'
2-
import { toCopyableMarkdown } from '@/app/workspace/[workspaceId]/components/message-actions/message-actions'
2+
import { toCopyableMarkdown } from '@/app/workspace/[workspaceId]/home/components/mothership-chat/copyable-markdown'
33

44
describe('toCopyableMarkdown', () => {
55
it('preserves message Markdown, including fenced code and its language', () => {
@@ -23,12 +23,25 @@ describe('toCopyableMarkdown', () => {
2323
it('removes internal structured tags without flattening surrounding Markdown', () => {
2424
const message = [
2525
'Before **formatted text**.',
26-
'<credential>remove this UI payload</credential>',
26+
'<credential>{"type":"service_account","provider":"gmail"}</credential>',
2727
'After [a link](https://example.com).',
2828
].join('\n')
2929

3030
expect(toCopyableMarkdown(message)).toBe(
3131
['Before **formatted text**.', '', 'After [a link](https://example.com).'].join('\n')
3232
)
3333
})
34+
35+
it('preserves tag-shaped text that the chat renders literally', () => {
36+
const message = [
37+
'Document `<credential>example</credential>`.',
38+
'',
39+
'```html',
40+
'<file>example</file>',
41+
'<question>example</question>',
42+
'```',
43+
].join('\n')
44+
45+
expect(toCopyableMarkdown(message)).toBe(message)
46+
})
3447
})
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { sanitizeChatDisplayContent } from '@/app/workspace/[workspaceId]/home/components/message-content/components/chat-content/chat-sanitize'
2+
import { parseSpecialTags } from '@/app/workspace/[workspaceId]/home/components/message-content/components/special-tags'
3+
4+
export function toCopyableMarkdown(raw: string): string {
5+
const displayContent = sanitizeChatDisplayContent(raw)
6+
const { segments } = parseSpecialTags(displayContent, false)
7+
8+
return segments
9+
.reduce((markdown, segment) => {
10+
return segment.type === 'text' ? markdown + segment.content : markdown
11+
}, '')
12+
.trim()
13+
}

apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/mothership-chat.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
parseLastCredentialTag,
3030
parseLastQuestionTag,
3131
} from '@/app/workspace/[workspaceId]/home/components/message-content/components/special-tags'
32+
import { toCopyableMarkdown } from '@/app/workspace/[workspaceId]/home/components/mothership-chat/copyable-markdown'
3233
import { nextSizerFloor } from '@/app/workspace/[workspaceId]/home/components/mothership-chat/sizer-floor'
3334
import { QueuedMessages } from '@/app/workspace/[workspaceId]/home/components/queued-messages'
3435
import {
@@ -281,6 +282,7 @@ const AssistantMessageRow = memo(function AssistantMessageRow({
281282
actionsEligible ? (
282283
<MessageActions
283284
content={message.content}
285+
prepareContentForCopy={toCopyableMarkdown}
284286
userQuery={precedingUserContent}
285287
requestId={message.requestId}
286288
messageId={message.id}

0 commit comments

Comments
 (0)