feat(mobile): copy button on each code block in chat (#34039)

Co-authored-by: Radu Toader <radu.m.toader@gmail.com>
This commit is contained in:
Kelvin Amoaba
2026-10-26 20:00:10 +00:00
committed by GitHub
co-authored by Radu Toader
parent 2dd8207369
commit c4f8fe5f3c
8 changed files with 30 additions and 8 deletions
@@ -15,6 +15,7 @@ vi.mock('react-native', () => ({
View: 'View'
}))
vi.mock('./pr-sidebar/MermaidDiagram', () => ({ MermaidDiagram: 'MermaidDiagram' }))
vi.mock('./MobileSourceCopyButton', () => ({ MobileSourceCopyButton: 'MobileSourceCopyButton' }))
function flattenText(node: ReactTestInstance): string {
return node.children
@@ -16,6 +16,7 @@ vi.mock('react-native', () => ({
View: 'View'
}))
vi.mock('./pr-sidebar/MermaidDiagram', () => ({ MermaidDiagram: 'MermaidDiagram' }))
vi.mock('./MobileSourceCopyButton', () => ({ MobileSourceCopyButton: 'MobileSourceCopyButton' }))
let renderer: ReactTestRenderer | null = null
@@ -127,6 +128,12 @@ describe('MobileMarkdown rendering', () => {
expect(texts(render(content))).toEqual(expected)
})
it('gives each code block its own copy button holding only that code', () => {
const tree = render('Run this:\n\n```sh\npnpm test\n```\n\nthen:\n\n```\nexit\n```')
const buttons = tree.root.findAll((node) => String(node.type) === 'MobileSourceCopyButton')
expect(buttons.map((button) => button.props.text)).toEqual(['pnpm test', 'exit'])
})
it('C12: opens a link with brackets in it, and one with a title, at their own address', () => {
const tree = render(
'[wiki](https://en.wikipedia.org/wiki/Foo_(bar)) and [docs](https://x.com/a "The docs")'
@@ -15,6 +15,7 @@ vi.mock('react-native', () => ({
}))
vi.mock('./pr-sidebar/MermaidDiagram', () => ({ MermaidDiagram: 'MermaidDiagram' }))
vi.mock('./MobileSourceCopyButton', () => ({ MobileSourceCopyButton: 'MobileSourceCopyButton' }))
const CONTENT = [
'# Heading',
@@ -21,6 +21,7 @@ import { MARKDOWN_TONES, scaledMarkdownText, styles } from './mobile-markdown-st
import { MarkdownText } from './mobile-markdown-text'
import { codeFenceClosed, plainText } from './mobile-markdown-tree'
import { MermaidDiagram } from './pr-sidebar/MermaidDiagram'
import { MobileSourceCopyButton } from './MobileSourceCopyButton'
/** Prose base size — passed to MermaidDiagram fallback mono text. */
const MERMAID_BASE = 13
@@ -54,13 +55,18 @@ function renderCode(node: Code, key: number, ctx: MarkdownBlockContext): ReactNo
}
return (
<View key={key} style={styles.codeBlock}>
{node.lang ? <NativeText style={styles.codeLanguage}>{node.lang}</NativeText> : null}
<MarkdownText
selectable
style={[styles.codeText, scaledMarkdownText(styles.codeText, ctx.textScale)]}
>
{node.value}
</MarkdownText>
<View style={styles.codeBody}>
{node.lang ? <NativeText style={styles.codeLanguage}>{node.lang}</NativeText> : null}
<MarkdownText
selectable
style={[styles.codeText, scaledMarkdownText(styles.codeText, ctx.textScale)]}
>
{node.value}
</MarkdownText>
</View>
{node.value ? (
<MobileSourceCopyButton text={node.value} accessibilityLabel="Copy code" />
) : null}
</View>
)
}
@@ -14,6 +14,7 @@ vi.mock('react-native', () => ({
View: 'View'
}))
vi.mock('./pr-sidebar/MermaidDiagram', () => ({ MermaidDiagram: 'MermaidDiagram' }))
vi.mock('./MobileSourceCopyButton', () => ({ MobileSourceCopyButton: 'MobileSourceCopyButton' }))
let renderer: ReactTestRenderer | undefined
@@ -14,6 +14,7 @@ vi.mock('react-native', () => ({
}))
vi.mock('./pr-sidebar/MermaidDiagram', () => ({ MermaidDiagram: 'MermaidDiagram' }))
vi.mock('./MobileSourceCopyButton', () => ({ MobileSourceCopyButton: 'MobileSourceCopyButton' }))
type TestNode = {
type: string
@@ -62,8 +62,12 @@ export const styles = StyleSheet.create({
borderWidth: 1,
borderColor: colors.borderSubtle,
borderRadius: radii.input,
padding: spacing.sm
padding: spacing.sm,
flexDirection: 'row',
alignItems: 'flex-start',
gap: spacing.xs
},
codeBody: { flex: 1 },
codeLanguage: {
fontSize: 10,
color: colors.textMuted,
@@ -15,6 +15,7 @@ vi.mock('react-native', () => ({
}))
vi.mock('./pr-sidebar/MermaidDiagram', () => ({ MermaidDiagram: 'MermaidDiagram' }))
vi.mock('./MobileSourceCopyButton', () => ({ MobileSourceCopyButton: 'MobileSourceCopyButton' }))
/** Each rendered table row's cell styles, header row first. */
function renderTableCellStyles(content: string): unknown[][] {