feat(mobile): open the session screen's three external URLs through the platform seam (OTA phase C, C7.2)

The landed external-link census, run over the session route's closure, reports
three modules reaching react-native's `Linking`: a terminal link tap whose open
mode is the phone's browser, and the two WebView-backed readers, each of which
sends a tapped link to the system browser rather than navigating the artifact
away.

Inside the shell `Linking.openURL` calls `window.open`, which both shells refuse
and which resolves either way, so all three reported success into a tap that did
nothing. The seam also stops swallowing the failure: each site caught and
discarded, and `openExternalLink` names it.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
This commit is contained in:
Jinwoo-H
2026-09-20 06:03:54 -04:00
parent dbca8bfe07
commit 20a0ead745
3 changed files with 8 additions and 6 deletions
+3 -2
View File
@@ -1,7 +1,8 @@
import { useState } from 'react'
import { Linking, Pressable, StyleSheet, Text, View } from 'react-native'
import { Pressable, StyleSheet, Text, View } from 'react-native'
import { WebView } from 'react-native-webview'
import { Code, Eye } from 'lucide-react-native'
import { openExternalLink } from '../platform/external-link'
import { colors, spacing, typography } from '../theme/mobile-theme'
type Props = {
@@ -50,7 +51,7 @@ export function MobileHtmlPreview({ html, renderSource }: Props) {
if (request.url === 'about:blank' || request.url.startsWith('data:')) {
return true
}
void Linking.openURL(request.url).catch(() => {})
openExternalLink(request.url)
return false
}}
/>
@@ -8,7 +8,8 @@ import {
type ComponentType,
type ForwardedRef
} from 'react'
import { Keyboard, Linking, Pressable, ScrollView, StyleSheet, View } from 'react-native'
import { Keyboard, Pressable, ScrollView, StyleSheet, View } from 'react-native'
import { openExternalLink } from '../platform/external-link'
import {
Bold,
Code2,
@@ -109,7 +110,7 @@ function MobileRichMarkdownEditorInner(
onOpenLink(url)
return
}
void Linking.openURL(url).catch(() => {})
openExternalLink(url)
},
[onOpenLink]
)
@@ -1,5 +1,5 @@
import { useRef, useCallback } from 'react'
import { Linking } from 'react-native'
import { openExternalLink } from '../platform/external-link'
import { useMobileFileTapHandlers } from './use-mobile-file-tap-handlers'
import { resolveMobileNativeChatFileSessionId } from './mobile-native-chat-eligibility'
import { activateOpenedSourceControlDiffTab } from './opened-mobile-session-tab'
@@ -92,7 +92,7 @@ export function useMobileSessionFileActions(scope: MobileSessionTerminalSendActi
// Why: browser.tabCreate resolves a real worktree, which the floating
// sentinel doesn't have — open taps in the phone browser instead.
if (terminalLinkOpenMode === 'phone-browser' || isFloatingWorkspaceRoute) {
void Linking.openURL(url).catch(() => {})
openExternalLink(url)
return
}
void handleCreateBrowserRef.current?.(url)