mirror of
https://github.com/stablyai/orca.git
synced 2026-10-20 16:03:20 +00:00
- Linkify POSIX absolute paths in chat prose (leading-/ regex alternative; URL guard now keys off the char before the matched slash) - Parse agent-style path:line(:col) citations in prose, code spans, and the open flow; line/column ride into the mobile file preview route - Route non-web markdown hrefs (file: URIs, relative/absolute paths) to the file opener instead of silently dropping them; unknown schemes stay dead - Resolve chat paths against the worktree root, not the terminal's live cwd - Reuse the terminal tap-to-open flow for chat taps (haptic, preview route, tab activation with retries) via a shared identity-stable hook, and toast on misses instead of silent no-ops - Keep snake_case paths whole (intraword underscores are literal text), scan bold/italic/strike spans for paths, split trailing punctuation off autolinks, and let taps land while the composer keyboard is up
95 lines
3.5 KiB
TypeScript
95 lines
3.5 KiB
TypeScript
import { StyleSheet, View } from 'react-native'
|
|
import { MobileNativeChatView, type MobileNativeChatInputLockReason } from './MobileNativeChatView'
|
|
import type { MobileNativeChatImageAttachments } from './use-mobile-native-chat-image-attachments'
|
|
import type { MobileNativeChatController } from './use-mobile-native-chat-controller'
|
|
|
|
type Props = {
|
|
controller: MobileNativeChatController
|
|
/** Opens a tapped file reference (worktree-relative or absolute, optional
|
|
* :line(:col) suffix) through the shared tap-to-open flow. */
|
|
onOpenFile: (pathText: string) => void
|
|
/** Native-chat image attachments: picking adds a composer chip, and sending
|
|
* rides the pending images along with the message text (desktop parity). */
|
|
images: MobileNativeChatImageAttachments
|
|
onMicPress: () => void
|
|
micActive: boolean
|
|
dictationMode: 'toggle' | 'hold'
|
|
onMicPressIn: () => void
|
|
onMicPressOut: () => void
|
|
inputLockReason: MobileNativeChatInputLockReason | null
|
|
/** Latest send failure, rendered inline above the composer. */
|
|
sendErrorMessage: string | null
|
|
/** Drops that failure once a later send succeeds. */
|
|
onClearSendError: () => void
|
|
keyboardInset: number
|
|
}
|
|
|
|
/** Keeps the terminal mounted underneath chat so its PTY subscription survives
|
|
* view toggles while the native surface owns the visible composer. */
|
|
export function MobileNativeChatOverlay({
|
|
controller,
|
|
onOpenFile,
|
|
images,
|
|
onMicPress,
|
|
micActive,
|
|
dictationMode,
|
|
onMicPressIn,
|
|
onMicPressOut,
|
|
inputLockReason,
|
|
sendErrorMessage,
|
|
onClearSendError,
|
|
keyboardInset
|
|
}: Props): React.JSX.Element | null {
|
|
if (!controller.showNativeChat) {
|
|
return null
|
|
}
|
|
const session = controller.nativeChatSession
|
|
return (
|
|
<View style={styles.overlay}>
|
|
<MobileNativeChatView
|
|
messages={session.messages}
|
|
status={session.status}
|
|
error={session.error}
|
|
agent={controller.nativeChatAgent}
|
|
agentWorking={controller.nativeChatAgentWorking}
|
|
streamingText={controller.nativeChatStreamingText}
|
|
onStop={controller.handleNativeChatStop}
|
|
ask={controller.nativeChatAsk}
|
|
onAnswerAsk={controller.handleNativeChatAnswerAsk}
|
|
onCancelAsk={controller.handleNativeChatCancelAsk}
|
|
question={controller.nativeChatQuestion}
|
|
onAnswerQuestion={controller.handleNativeChatQuestionAnswer}
|
|
permission={controller.nativeChatPermission}
|
|
onRespondPermission={controller.handleNativeChatRespondPermission}
|
|
onOpenFile={onOpenFile}
|
|
hasMore={session.hasMore}
|
|
loadingEarlier={session.loadingEarlier}
|
|
onLoadEarlier={session.loadEarlier}
|
|
onSend={images.sendNativeChat}
|
|
pending={controller.chatPending}
|
|
composerText={controller.chatComposerText}
|
|
onComposerTextChange={controller.setChatComposerText}
|
|
onAttachImage={() => void images.attachImage('library')}
|
|
attachments={images.attachments}
|
|
onRemoveAttachment={images.removeAttachment}
|
|
isAttaching={images.isAttaching}
|
|
onMicPress={onMicPress}
|
|
micActive={micActive}
|
|
dictationMode={dictationMode}
|
|
onMicPressIn={onMicPressIn}
|
|
onMicPressOut={onMicPressOut}
|
|
inputLockReason={inputLockReason}
|
|
sendErrorMessage={sendErrorMessage}
|
|
onClearSendError={onClearSendError}
|
|
filePaths={controller.nativeChatFilePaths}
|
|
onNeedFiles={controller.loadNativeChatFiles}
|
|
keyboardInset={keyboardInset}
|
|
/>
|
|
</View>
|
|
)
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
overlay: StyleSheet.absoluteFillObject
|
|
})
|