Skip to content

Feat/text selection code copy#358

Open
dishit-wednesday wants to merge 2 commits into
mainfrom
feat/text-selection-code-copy
Open

Feat/text selection code copy#358
dishit-wednesday wants to merge 2 commits into
mainfrom
feat/text-selection-code-copy

Conversation

@dishit-wednesday

@dishit-wednesday dishit-wednesday commented May 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Replaced TouchableOpacity wrapper on chat message bubbles with View — the TouchableOpacity was intercepting all touch gestures, so the native OS text selection UI never triggered. Long press would open the action sheet instead of showing selection handles
  • Swapped @ronradtke/react-native-markdown-display for react-native-enriched-markdown — the old lib rendered each paragraph as a View, which is a hard OS boundary for text selection. Cross-paragraph selection was impossible regardless of the wrapper fix. The new lib is a Fabric-native renderer (New Architecture) that parses markdown in C and renders as native text, enabling cross-paragraph selection out of the box
  • Code fences are extracted before passing to the renderer and rendered as a custom CodeBlock component with a language label and per-block Copy button — the new lib's native renderer cannot host custom views inside code blocks so we split rendering at the JS layer
  • Restored the ••• action sheet (Copy all / Regenerate / Edit / Generate Image) via the meta row button

Impact

  • Users can now long press and drag to select any portion of assistant message text, including across paragraph boundaries
  • Each code block has a dedicated Copy button with "Copied" feedback
  • All 5113 existing tests pass

- Replace TouchableOpacity bubble wrapper with View so native text
  selection gestures reach inner text nodes
- Swap @ronradtke/react-native-markdown-display for
  react-native-enriched-markdown (native Fabric renderer) to enable
  cross-paragraph text selection
- Split markdown rendering: code fences extracted and rendered as a
  custom CodeBlock component with language label and Copy button;
  prose segments rendered by EnrichedMarkdownText with selectable=true
- Remove Copy and Regenerate from long-press action sheet; restore
  full action sheet (Copy all, Regenerate, Edit, Generate Image) via
  the ••• meta row button
- Fix phi model detection regex to use word-boundary + digit pattern
  so dolphin models are no longer incorrectly blocked
- Add terminal icon in chat header to open existing DebugLogsScreen
  with touch and structure logs for diagnosing gesture issues

Co-authored-by: Dishit Karia <hanmadishit74@gmail.com>"

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@sonarqubecloud

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request replaces the markdown rendering library with react-native-enriched-markdown, adds a debug logs screen, and introduces logging for message content interactions. Feedback focuses on a regression where the action menu became inaccessible after replacing TouchableOpacity with a View. Additionally, the reviewer advised against manually splitting markdown segments with regex due to potential issues with text selection and streaming UX, suggested using a non-deprecated Clipboard API, and recommended avoiding .trim() on markdown content to preserve necessary whitespace.

Comment on lines +249 to 255
<View
testID={isUser ? 'user-message' : 'assistant-message'}
style={[
styles.container,
isUser ? styles.userContainer : styles.assistantContainer,
]}
activeOpacity={0.8}
onLongPress={handleLongPress}
delayLongPress={300}
>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The replacement of TouchableOpacity with a View and the removal of the handleLongPress logic (previously at line 233) makes the ActionMenuSheet inaccessible. Users can no longer open the menu to access essential actions like Copy, Edit, Retry, or Generate Image. While this change likely aims to enable native text selection, a replacement trigger for the action menu (such as a dedicated 'more' button or a different gesture) should be implemented to avoid this regression.

const [copied, setCopied] = useState(false);

const handleCopy = useCallback(() => {
Clipboard.setString(code);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The Clipboard API from the react-native core is deprecated. It is recommended to use the community-maintained package @react-native-clipboard/clipboard instead.

Comment on lines +80 to +101
function splitSegments(text: string): Segment[] {
const segments: Segment[] = [];
const fence = /^```([^\n]*)\n([\s\S]*?)^```/gm;
let lastIndex = 0;
let match: RegExpExecArray | null;

while ((match = fence.exec(text)) !== null) {
if (match.index > lastIndex) {
const md = text.slice(lastIndex, match.index).trim();
if (md) segments.push({ type: 'markdown', content: md });
}
segments.push({ type: 'code', language: match[1].trim(), code: match[2] });
lastIndex = match.index + match[0].length;
}

if (lastIndex < text.length) {
const md = text.slice(lastIndex).trim();
if (md) segments.push({ type: 'markdown', content: md });
}

return segments;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Manually splitting markdown with regex is fragile and introduces several issues:

  1. Selection Continuity: Because markdown segments and code blocks are rendered as separate top-level children in a View, users cannot perform a single continuous text selection that spans across a paragraph and a code block.
  2. Streaming UX: During streaming, the closing backticks are missing, so the regex won't match. The code block will be rendered by the markdown library until it's finished, at which point it will suddenly 'pop' into this custom CodeBlock component, causing a flickering effect and layout shift.
  3. Markdown Integrity: It can break structures like nested lists or blockquotes if a code block appears inside them.

Since react-native-enriched-markdown appears to support custom styles (see line 204), it is better to use the library's built-in customization hooks (e.g., renderRules for the fence or code_block types) to inject the Copy button directly into the markdown rendering pipeline.

Comment on lines +88 to +89
const md = text.slice(lastIndex, match.index).trim();
if (md) segments.push({ type: 'markdown', content: md });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using .trim() on markdown segments removes leading and trailing whitespace (including newlines) that may be critical for correct markdown parsing and visual spacing between paragraphs and code blocks.

Suggested change
const md = text.slice(lastIndex, match.index).trim();
if (md) segments.push({ type: 'markdown', content: md });
const md = text.slice(lastIndex, match.index);
if (md) segments.push({ type: 'markdown', content: md });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant