mirror of
https://github.com/stablyai/orca.git
synced 2026-11-02 18:17:36 +00:00
* fix(mobile): wall hosts that cannot serve the page instead of opening native screens * refactor(mobile): drop the native-route state and the shell fallback prop; unknown routes say unavailable * test(mobile): read the nullable page route refusal in its policy test * refactor(mobile): every build is the OTA shell; drop the native/OTA build switch The owner retired the native-screen fallback with no emergency native build, so EXPO_PUBLIC_MOBILE_SHELL, mobileShellBuildKind, shellSwitchDecision, the Metro cache-key suffix and both release workflows' shell input go. Every host route mounts the shell; a route naming no screen the shell could open gets the existing refusal screen instead of a native screen. Claude-Session: https://claude.ai/code/session_6cea92f6 * refactor(mobile): delete native screens the OTA shell no longer reaches With every build the OTA shell, these native siblings run nowhere: the native-executed closure (index.js plus every non-web route) does not reach them, and the page resolves their .web siblings. Each .web sibling takes the plain name, its web-overrides entry goes, and the native-only rich Markdown editor and Mermaid WebView generators and their outputs go with them. Kept: native siblings the node test suite still renders shared screens through (terminal WebView, browser frame path, clipboard, dictation, media picker, live input, snapshot budget, input styles). Claude-Session: https://claude.ai/code/session_6cea92f6 * test(mobile): drop the phone-preview comparison the deleted native sibling held Claude-Session: https://claude.ai/code/session_6cea92f6 * refactor(mobile): drop the dead web route, flag-era comments and the one-value shell kind The host route files no longer switch, so their comments say what they do now: a route naming no screen shows the unavailable screen. app/h/[hostId]/web.tsx duplicated index.tsx and nothing linked to it, so it and its stack entry go (the bundle sweep is re-measured: 77 scripts at 15 routes). Diagnostics print the bridge protocol on its own instead of a shell kind that has one value. Claude-Session: https://claude.ai/code/session_6cea92f6 * refactor(mobile): one host for the rich editor, plain Text, and drop two unused native deps MobileSelectableText was react-native's Text, so callers use Text. The rich Markdown editor's host-facing seams (messages, URL prompt, keyboard inset) are required now that the page is its only host; the WebView defaults go. Comments that described deleted native components are rewritten, and expo-image-manipulator and react-native-uitextview leave package.json and the lockfile. Claude-Session: https://claude.ai/code/session_6cea92f6
128 lines
3.9 KiB
TypeScript
128 lines
3.9 KiB
TypeScript
import { useContext } from 'react'
|
|
import { Image, Pressable, Text } from 'react-native'
|
|
import { INLINE_TEXT_SELECTION } from '../components/inline-text-selection'
|
|
import { MobileMarkdown } from '../components/MobileMarkdown'
|
|
import { isImageRefBlock, isTextBlock } from '../../../src/shared/native-chat-types'
|
|
import type {
|
|
NativeChatBlock,
|
|
NativeChatImageRefBlock
|
|
} from '../../../src/shared/native-chat-types'
|
|
import {
|
|
AGENT_SESSION_HOST_STATUS_COPY,
|
|
isAgentSessionHostStatusPresentation
|
|
} from '../../../src/shared/agent-session-host-status-rows'
|
|
import { withoutPendingNativeChatVisualDirectiveTail } from '../../../src/shared/native-chat-visual-directive'
|
|
import { MobileNativeChatRowOverlaysContext } from './mobile-native-chat-row-overlays'
|
|
import { isRenderableImageUri } from './mobile-native-chat-image-preview'
|
|
import {
|
|
CHAT_PROSE_SCALE,
|
|
scaledType,
|
|
styles,
|
|
TEXT_SIZE
|
|
} from './mobile-native-chat-message-styles'
|
|
import type { MobileNativeChatVisualRender } from './mobile-native-chat-visual-context'
|
|
|
|
export function Prose({
|
|
block,
|
|
text,
|
|
invert,
|
|
fontScale,
|
|
onOpenFile,
|
|
onLongPress,
|
|
renderVisual,
|
|
growing = false,
|
|
revealing = false
|
|
}: {
|
|
block: NativeChatBlock
|
|
text: string
|
|
invert?: boolean
|
|
fontScale: number
|
|
/** Assistant prose of a structured chat only. */
|
|
renderVisual?: MobileNativeChatVisualRender
|
|
/** The reply may still be growing: a directive still being typed at its tail is held back. */
|
|
growing?: boolean
|
|
revealing?: boolean
|
|
onOpenFile?: (relativePath: string) => void
|
|
/** Android only: routes a long press on a link span to the row's actions sheet. */
|
|
onLongPress?: () => void
|
|
}): React.JSX.Element | null {
|
|
if (isTextBlock(block)) {
|
|
// A host line standing in for a message too large to send, on the person's own row.
|
|
if (isAgentSessionHostStatusPresentation(block.presentation)) {
|
|
return (
|
|
<Text
|
|
selectable={INLINE_TEXT_SELECTION}
|
|
style={[styles.hostNotice, scaledType(TEXT_SIZE, fontScale)]}
|
|
>
|
|
{AGENT_SESSION_HOST_STATUS_COPY[block.presentation]}
|
|
</Text>
|
|
)
|
|
}
|
|
if (invert) {
|
|
return (
|
|
<MobileMarkdown
|
|
content={text}
|
|
tone="onBright"
|
|
rangeSelectable
|
|
textScale={CHAT_PROSE_SCALE * fontScale}
|
|
onOpenFile={onOpenFile}
|
|
onLongPress={onLongPress}
|
|
/>
|
|
)
|
|
}
|
|
return (
|
|
<MobileMarkdown
|
|
content={
|
|
renderVisual && (growing || revealing)
|
|
? withoutPendingNativeChatVisualDirectiveTail(text)
|
|
: text
|
|
}
|
|
growing={revealing}
|
|
renderVisual={renderVisual}
|
|
rangeSelectable
|
|
textScale={CHAT_PROSE_SCALE * fontScale}
|
|
onOpenFile={onOpenFile}
|
|
onLongPress={onLongPress}
|
|
/>
|
|
)
|
|
}
|
|
if (isImageRefBlock(block)) {
|
|
return <ImageAttachment block={block} fontScale={fontScale} onLongPress={onLongPress} />
|
|
}
|
|
return null
|
|
}
|
|
|
|
/** A local preview (composer echo) or real URL draws as a thumbnail that opens full size; a bare
|
|
* host path (not loadable on the device) falls back to a text placeholder. */
|
|
function ImageAttachment({
|
|
block,
|
|
fontScale,
|
|
onLongPress
|
|
}: {
|
|
block: NativeChatImageRefBlock
|
|
fontScale: number
|
|
onLongPress?: () => void
|
|
}): React.JSX.Element {
|
|
const { openImage } = useContext(MobileNativeChatRowOverlaysContext)
|
|
const uri = block.url ?? block.path
|
|
const label = block.alt ?? 'Attached image'
|
|
if (!isRenderableImageUri(uri)) {
|
|
return (
|
|
<Text style={[styles.imageRef, { fontSize: TEXT_SIZE * fontScale }]}>
|
|
🖼 {block.alt ?? block.path ?? block.url ?? 'image'}
|
|
</Text>
|
|
)
|
|
}
|
|
return (
|
|
<Pressable
|
|
onPress={() => openImage({ uri, label })}
|
|
onLongPress={onLongPress}
|
|
accessibilityRole="imagebutton"
|
|
accessibilityLabel={label}
|
|
accessibilityHint="Opens the image full size"
|
|
>
|
|
<Image source={{ uri }} style={styles.imageThumb} resizeMode="contain" />
|
|
</Pressable>
|
|
)
|
|
}
|