@adminide-stack/yantra-mobile 12.0.64-alpha.1 → 12.0.64-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/contexts/ChatLiveStreamContext.js +5 -2
- package/lib/contexts/ChatLiveStreamContext.js.map +1 -1
- package/lib/features/canvas/surfaceIndex.js +4 -1
- package/lib/features/canvas/surfaceIndex.js.map +1 -1
- package/lib/features/chat/ChatTranscript.js +15 -2
- package/lib/features/chat/ChatTranscript.js.map +1 -1
- package/lib/features/invite/InviteDeepLink.js +202 -0
- package/lib/features/invite/InviteDeepLink.js.map +1 -0
- package/lib/features/invite/inviteLink.js +28 -0
- package/lib/features/invite/inviteLink.js.map +1 -0
- package/lib/features/invite/pendingInvite.js +42 -0
- package/lib/features/invite/pendingInvite.js.map +1 -0
- package/lib/features/memory/MemoryPressureResponder.js +21 -0
- package/lib/features/memory/MemoryPressureResponder.js.map +1 -0
- package/lib/features/memory/memoryPressure.js +24 -0
- package/lib/features/memory/memoryPressure.js.map +1 -0
- package/lib/features/memory/useMemoryPressureResponder.js +23 -0
- package/lib/features/memory/useMemoryPressureResponder.js.map +1 -0
- package/lib/hooks/mintSurfaceSystemToken.js +8 -0
- package/lib/hooks/mintSurfaceSystemToken.js.map +1 -1
- package/lib/hooks/useChatStream.js +96 -25
- package/lib/hooks/useChatStream.js.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/screens/Chat/index.js +73 -9
- package/lib/screens/Chat/index.js.map +1 -1
- package/lib/screens/Home/components/CanvasPrewarm.js +4 -2
- package/lib/screens/Home/components/CanvasPrewarm.js.map +1 -1
- package/lib/services/queuedGatewayMessages.js +145 -0
- package/lib/services/queuedGatewayMessages.js.map +1 -0
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../src/screens/Chat/index.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';\nimport { Keyboard, StyleSheet, TextInput, View, useWindowDimensions } from 'react-native';\nimport { Box, InputToolBar, Text, getDefaultLeftItems } from '@admin-layout/gluestack-ui-mobile';\nimport { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';\nimport { useNavigation, useRoute, CommonActions } from '@react-navigation/native';\nimport { MessageActionBar } from '../../features/chat/MessageActionBar';\nimport { ChatTranscript } from '../../features/chat/ChatTranscript';\nimport { AskUserCard } from '../../features/chat/AskUserCard';\nimport { pendingAskUser } from '../../features/chat/askUser';\nimport { useCdecliConnection } from '../../contexts/CdecliConnectionContext';\nimport { useChatLiveStream } from '../../contexts/ChatLiveStreamContext';\nimport { KeyboardAwareScreen, KeyboardComposerDock } from '../../components/KeyboardComposerDock';\nimport { YantraBrandLoader, YANTRA_LOADER_SIZE_COMPACT } from '../../components/YantraBrandLoader';\nimport ThinkingIndicator from '../../components/ThinkingIndicator';\nimport { mobileTokens } from '../../theme/mobileTokens';\nimport { useAppTheme } from '../../theme/AppThemeProvider';\nimport DeepSearchModal from '../Home/components/DeepSearchModal';\nimport { extractDeepSearchSources, normalizeSummaryText, type DeepSearchSourceItem } from '../Home/deepSearchUtils';\nimport type { ModeSubmission, RoutingMode } from '../Home/types';\nimport { AudioRecorderPanel } from '../../features/audio-input/AudioRecorderPanel';\nimport { NativeDictationBar } from '../../features/audio-input/NativeDictationBar';\nimport { isNativeDictationAvailable } from '../../features/audio-input/nativeDictation';\nimport { useImageAttachments } from '../../features/attachments/useImageAttachments';\nimport { ComposerAttachmentBar } from '../../features/attachments/ComposerAttachmentBar';\nimport { useComposerAttachMenu } from '../../features/attachments/ComposerAttachMenu';\nimport { hasComposerAttachments, takeComposerAttachments } from '../../features/attachments/composerAttachmentHandoff';\nimport { MicErrorBoundary } from '../../features/audio-input/MicErrorBoundary';\nimport { requestMicPermission } from '../../features/audio-input/useAudioPermission';\nimport { shouldRedirectToShop } from '../../features/shopping/chat/homeShoppingIntent';\nimport { pushShopNative, replaceWithShopNative, shopHandoffParams } from '../../features/shopping/chat/openShopNative';\n\n/** Route params passed by Home (or by deep-link) when entering the Chat surface. */\nexport interface ChatScreenRouteParams {\n /** Channel id is required — the screen is keyed on the conversation. */\n channelId: string;\n /** Org slug used by gateway/account context. */\n orgName?: string | null;\n /**\n * First user message to auto-fire when the screen mounts. Home generates\n * this when the user submits from the empty composer so the assistant\n * starts streaming on Chat without an extra round trip.\n */\n initialQuery?: string | null;\n /**\n * Seed for the composer's mode (`'chat'` | `'deep-search'` | `'build'`).\n *\n * Set by `HomeScreen` to carry the user's last selection across the\n * navigation hop (\"research from home → research-shaped composer on chat\").\n * `ChatHistoryScreen` does NOT forward this — revisits always land on the\n * default `'chat'` mode, matching Home's defaults, and the user is free to\n * switch from there. Build-mode send hands off to Web Builder.\n *\n * After mount, this param is purely a seed: the screen keeps its own\n * `activeMode` so the user can toggle freely while inside the thread.\n */\n initialMode?: RoutingMode;\n /** Optional attachments forwarded with the initial message. */\n initialAttachments?: ModeSubmission['attachments'];\n}\n\nexport default function ChatScreen() {\n const navigation = useNavigation<any>();\n const route = useRoute<any>();\n\n const params = (route?.params ?? {}) as Partial<ChatScreenRouteParams>;\n const channelId = useMemo(() => {\n const raw = typeof params.channelId === 'string' ? params.channelId.trim() : '';\n return raw.length > 0 ? raw : null;\n }, [params.channelId]);\n const initialQuery = useMemo(() => {\n const raw = typeof params.initialQuery === 'string' ? params.initialQuery.trim() : '';\n return raw.length > 0 ? raw : null;\n }, [params.initialQuery]);\n const initialAttachments = params.initialAttachments;\n\n const { isDark } = useAppTheme();\n const { width: screenWidth } = useWindowDimensions();\n const contentMaxWidth = Math.min(720, Math.max(360, screenWidth - 24));\n\n const surfaceColor = isDark ? '#0f172a' : mobileTokens.color.surface;\n const secondaryTextColor = isDark ? '#94a3b8' : mobileTokens.color.textMuted;\n\n const insets = useSafeAreaInsets();\n\n /**\n * Gateway + chat stream routing.\n *\n * The CDeCLI lifecycle is owned app-wide by `CdecliConnectionProvider`,\n * so we just register this screen's channelId and read the shared status.\n * This avoids racing `gatewayConnect` mutations with the header's\n * `GatewayConnector` (which previously ran its own `useCdecliAutoConnect`\n * and could clobber the chat session binding).\n */\n const cdecli = useCdecliConnection();\n\n useLayoutEffect(() => {\n if (!channelId) return;\n cdecli.setActiveChannelId(channelId);\n }, [channelId, cdecli.setActiveChannelId]);\n\n const chat = useChatLiveStream();\n\n /**\n * Gifted-shaped transcript rows, memoised on `messages` alone.\n *\n * This screen re-renders on every streamed token — `useChatStream` does\n * `setResponse((r) => r + text)` per delta. Building this array inline in\n * the JSX therefore handed `PlanModeView` a fresh `messages` identity on\n * every token, which invalidated its own `useMemo([messages, …])`, re-mapped\n * every row, and re-rendered every bubble. Assistant bubbles render through\n * `react-native-markdown-display`, so that meant re-parsing the markdown of\n * the whole thread once per token — O(messages) parses per delta, on the JS\n * thread, which is exactly when the UI has to stay responsive.\n *\n * `messages` is already stable upstream (`useChatApi` memoises it on the\n * Apollo result), so this only rebuilds when a message is actually added or\n * changed.\n */\n const transcriptRows = useMemo(\n () =>\n chat.messages.map((msg, index) => ({\n id: `msg-${index}-${msg.role}`,\n role: msg.role,\n content: msg.content,\n attachments: msg.attachments,\n metadata: (msg as { metadata?: unknown }).metadata,\n })),\n [chat.messages],\n );\n const {\n messages,\n response,\n quickReply,\n error: chatError,\n isLoading,\n isStreaming,\n messagesLoading,\n sendMessage,\n cancel,\n } = chat;\n\n /**\n * \"Try again\" on an assistant message: resend the nearest preceding user\n * prompt on the same channel. Row ids are `msg-<index>-<role>` (see\n * transcriptRows), so the transcript index comes straight out of the id.\n */\n const regenerateFrom = useCallback(\n (messageId: string) => {\n const match = /^msg-(\\d+)-assistant$/.exec(messageId);\n if (!match || !channelId) return;\n for (let i = Number(match[1]) - 1; i >= 0; i -= 1) {\n const prior = chat.messages[i];\n if (prior?.role === 'user' && (prior.content?.trim() || prior.attachments?.length)) {\n void chat.sendMessage(prior.content, prior.attachments, channelId);\n return;\n }\n }\n },\n [chat, channelId],\n );\n\n const chatBusy = isLoading || isStreaming;\n const renderMessageActions = useCallback(\n (message: { id: string; role: string; content: string }) =>\n message.role === 'assistant' ? (\n <MessageActionBar\n text={message.content}\n onRegenerate={chatBusy ? undefined : () => regenerateFrom(message.id)}\n />\n ) : null,\n [chatBusy, regenerateFrom],\n );\n\n const imageAttachments = useImageAttachments();\n const { rightItems, menu: attachMenu } = useComposerAttachMenu(imageAttachments, isDark);\n\n /*\n * Composer state — kept local so it doesn't leak across navigations.\n *\n * `valueRef` mirrors the controlled `value` so `handleSend` can read the\n * latest text without taking a dependency on it. Without this, `handleSend`\n * (and therefore `micSendButton.onSend`) would be recreated on every\n * keystroke, which is unnecessary churn for `InputToolBar`.\n *\n * `inputRef` is forwarded to InputToolBar's TextInput so we can call\n * `.clear()` imperatively after sending. On iOS, the native text buffer\n * can stay desynced from the controlled `value` for a frame when a state\n * update is followed by an async dispatch in the same gesture — the\n * \"send-and-clear\" race. The imperative clear closes that gap deterministically.\n */\n const [value, setValue] = useState('');\n const valueRef = useRef(value);\n valueRef.current = value;\n const inputRef = useRef<TextInput>(null);\n const trimmed = value.trim();\n const hasQuery = trimmed.length > 0;\n const hasAttachments = imageAttachments.pending.length > 0;\n const canSubmit = (hasQuery || hasAttachments) && Boolean(channelId);\n\n /**\n * Composer mode is FREELY user-controlled, mirroring `HomeScreen`.\n *\n * Seed:\n * • From Home (which forwards `initialMode` alongside `initialQuery`):\n * start in whatever mode the user picked there so the \"research from\n * home → research-shaped chat\" hand-off feels continuous.\n * • From ChatHistory (which intentionally does NOT pass `initialMode`):\n * default to `'chat'`, same as Home's empty composer.\n *\n * Post-mount this is a normal local state — the search/zap pills toggle\n * it on tap, and every place that previously branched on\n * `params.initialMode` now branches on `activeMode` instead.\n */\n const [activeMode, setActiveMode] = useState<RoutingMode>(() =>\n params.initialMode === 'deep-search' ? 'deep-search' : params.initialMode === 'build' ? 'build' : 'chat',\n );\n const handleModeSwitch = useCallback((mode: RoutingMode) => {\n setActiveMode(mode);\n }, []);\n const isDeepSearchMode = activeMode === 'deep-search';\n const isBuildMode = activeMode === 'build';\n\n /**\n * Inline voice recorder state — parity with HomeScreen and the web\n * `YantraSearchComposer.showAudioRecorder` flow. When active, the panel\n * fully replaces the InputToolBar so the toolbar's mic→send button\n * doesn't compete with the panel's own send/cancel affordances.\n */\n const [showAudioRecorder, setShowAudioRecorder] = useState(false);\n // Apple on-device dictation (the iOS-built-in mic) is preferred when the\n // binary has it; the Whisper recorder above is the fallback.\n const [showNativeDictation, setShowNativeDictation] = useState(false);\n const [voiceError, setVoiceError] = useState<string | null>(null);\n\n /**\n * Auto-fire the initial query once when the screen mounts on a fresh channel.\n * Home creates the channel optimistically and forwards the user prompt here so\n * the first assistant token arrives without an extra interaction.\n *\n * Wait for CDeCLI to finish connecting (or fail) before auto-firing — chat is\n * cdecli-only and `sendMessage` rejects sends while the gateway is disconnected.\n */\n const autoFiredRef = useRef(false);\n useEffect(() => {\n if (autoFiredRef.current) return;\n if (!channelId) return;\n if (cdecli.status !== 'connected' && cdecli.status !== 'error') return;\n const stashed = hasComposerAttachments(channelId) ? takeComposerAttachments(channelId) : undefined;\n const attachments = stashed?.length ? stashed : initialAttachments;\n if (!initialQuery && !attachments?.length) return;\n\n autoFiredRef.current = true;\n if (isDeepSearchMode) {\n setIsDeepSearchModalOpen(true);\n }\n void (async () => {\n const mode: RoutingMode = isDeepSearchMode ? 'deep-search' : isBuildMode ? 'build' : 'chat';\n let openShop = false;\n try {\n openShop = await shouldRedirectToShop(initialQuery ?? '', attachments?.length ?? 0, mode);\n } catch {\n openShop = false;\n }\n if (openShop) {\n replaceWithShopNative(\n navigation,\n shopHandoffParams({\n orgName: params.orgName,\n query: initialQuery ?? '',\n files: attachments,\n }),\n );\n return;\n }\n void sendMessage(initialQuery ?? '', attachments, channelId);\n })();\n try {\n navigation.setParams({ initialQuery: null, initialAttachments: null });\n } catch {\n /* setParams may be unavailable in tests; clearing is best-effort. */\n }\n }, [\n channelId,\n initialQuery,\n initialAttachments,\n sendMessage,\n navigation,\n cdecli.status,\n isDeepSearchMode,\n isBuildMode,\n params.orgName,\n ]);\n\n /**\n * Answer a parked `[ASK_USER]` question. The formatted `**Title:** answer`\n * block goes out as an ordinary user message — cdecli absorbs it into the\n * turn it parked, which is what releases the run. Deliberately NOT routed\n * through handleSend: no deep-search/build-mode rewriting, no attachments,\n * and it must send even while the turn is still streaming.\n */\n const handleAskUserAnswer = useCallback(\n (formatted: string) => {\n const text = formatted.trim();\n if (!text) return;\n void sendMessage(text, undefined, channelId);\n },\n [sendMessage, channelId],\n );\n\n const [resolvedAskIds, setResolvedAskIds] = useState<Record<string, boolean>>({});\n const pendingAsk = useMemo(\n () =>\n pendingAskUser([\n ...transcriptRows,\n ...(response?.trim() ? [{ role: 'assistant', content: response }] : []),\n ]),\n [transcriptRows, response],\n );\n const showAskCard = Boolean(pendingAsk && !resolvedAskIds[pendingAsk.requestId]);\n\n const handleSend = useCallback(\n (text?: string) => {\n const pending = imageAttachments.forSend();\n const t = (text ?? valueRef.current).trim();\n if ((!t && !pending?.length) || !channelId) return;\n // Clear both ways: the controlled state (so React's model stays\n // consistent on next render) AND the native buffer via the ref\n // (so the visible text disappears on the same frame the user\n // pressed send, even before React flushes).\n setValue('');\n inputRef.current?.clear();\n\n // Build mode: hand off to Web Builder on this channel (web `/c/:id/webbuilder`).\n if (isBuildMode) {\n navigation.dispatch(\n CommonActions.navigate({\n name: 'MainStack.WebBuilder',\n params: {\n ...(params.orgName ? { orgName: params.orgName } : {}),\n channelId,\n message: t,\n },\n }),\n );\n return;\n }\n\n // Deep-search mode: every new query pops the structured research\n // modal back open (parity with the previous Home flow, where every\n // submit re-opened the DeepSearchModal). Without this, a user who\n // closed the modal once would see plain assistant turns for every\n // subsequent send. Reads the LOCAL `isDeepSearchMode` so the user\n // can toggle modes mid-thread and have sends behave accordingly.\n if (isDeepSearchMode) {\n setIsDeepSearchModalOpen(true);\n }\n void (async () => {\n let openShop = false;\n try {\n openShop = await shouldRedirectToShop(t, pending.length, isDeepSearchMode ? 'deep-search' : 'chat');\n } catch {\n openShop = false;\n }\n if (openShop) {\n const shopParams = shopHandoffParams({\n orgName: params.orgName,\n query: t,\n files: pending,\n });\n if (messages.length === 0) {\n replaceWithShopNative(navigation, shopParams);\n } else {\n pushShopNative(navigation, shopParams);\n }\n return;\n }\n void sendMessage(t, pending, channelId);\n })();\n imageAttachments.clear();\n },\n [\n channelId,\n sendMessage,\n isDeepSearchMode,\n isBuildMode,\n navigation,\n params.orgName,\n imageAttachments,\n messages.length,\n ],\n );\n\n const handleValueChange = useCallback((e: any) => {\n setValue(e?.nativeEvent?.text ?? '');\n }, []);\n\n /**\n * Mic affordance — web parity (`handleMicClick` in `YantraSearchComposer.tsx`).\n * The shared `MicSendButton` in `InputToolBar.tsx` only routes to `onMic`\n * when the input has no content, so we only need to handle the empty-input\n * case here.\n *\n * Permission is requested HERE, before the panel mounts. The previous flow\n * flipped `showAudioRecorder=true` immediately, which mounted\n * `AudioRecorderPanel` and constructed the native `AVAudioRecorder` via\n * `useAudioRecorder()` during the panel's render phase — before iOS had\n * granted mic access. On SDK 53 + `newArchEnabled: true` + Hermes that\n * ordering hard-crashes TestFlight builds even though it works in Expo Go\n * (Expo Go pre-caches permission). Asking up front means iOS shows the\n * system alert first and the panel only mounts (and therefore the native\n * recorder is only allocated) once permission is granted.\n *\n * `expo-audio` is lazy-imported so a missing/broken native module surfaces\n * as a user-visible banner instead of a synchronous TurboModule crash.\n */\n /** Open the record -> Whisper recorder (the fallback mic lane). */\n const openWhisperRecorder = useCallback(() => {\n setVoiceError(null);\n void (async () => {\n const { granted, error } = await requestMicPermission();\n if (!granted) {\n setVoiceError(error || 'Microphone is not available.');\n return;\n }\n setShowAudioRecorder(true);\n })();\n }, []);\n\n const handleMicPress = useCallback(() => {\n if (hasQuery) return;\n setVoiceError(null);\n Keyboard.dismiss();\n // Prefer Apple's on-device dictation (the iOS-built-in mic): it streams\n // text live and needs no upload. The bar requests its own permission\n // and calls back to the Whisper recorder if the native path is\n // unusable. Only reachable on a build that linked the native module.\n if (isNativeDictationAvailable()) {\n setShowNativeDictation(true);\n return;\n }\n openWhisperRecorder();\n }, [hasQuery, openWhisperRecorder]);\n\n /**\n * On a successful Whisper round-trip, paste the transcript into the input\n * field, close the recorder, and let the user review/send manually. Matches\n * web's `handleTranscriptionComplete`: we deliberately do NOT auto-fire\n * `sendMessage` because the user often wants to edit the dictation first.\n */\n /** Append a finished transcript to the composer and re-focus it. Shared by\n * the Whisper recorder and native dictation. */\n const pasteTranscript = useCallback((text: string) => {\n const cleaned = (text ?? '').trim();\n if (!cleaned) return;\n setValue((prev) => {\n const next = prev.trim().length > 0 ? `${prev.trim()} ${cleaned}` : cleaned;\n /*\n * Keep `inputRef`'s native text in sync with our controlled value\n * for the same reason `handleSend` does (the iOS native buffer\n * can lag the React state across a single gesture). `setNativeProps`\n * is the safe way to do this without re-rendering.\n */\n requestAnimationFrame(() => {\n inputRef.current?.setNativeProps?.({ text: next });\n inputRef.current?.focus?.();\n });\n return next;\n });\n }, []);\n\n const handleTranscriptionComplete = useCallback(\n (text: string) => {\n setShowAudioRecorder(false);\n pasteTranscript(text);\n },\n [pasteTranscript],\n );\n\n const handleRecorderCancel = useCallback(() => {\n setShowAudioRecorder(false);\n }, []);\n\n const handleDictationComplete = useCallback(\n (text: string) => {\n setShowNativeDictation(false);\n pasteTranscript(text);\n },\n [pasteTranscript],\n );\n\n const handleDictationCancel = useCallback(() => setShowNativeDictation(false), []);\n\n /** Native dictation could not run - drop to the Whisper recorder, which\n * requests its own permission and surfaces its own errors. */\n const handleDictationFallback = useCallback(() => {\n setShowNativeDictation(false);\n openWhisperRecorder();\n }, [openWhisperRecorder]);\n\n const handleRecorderError = useCallback((error: string) => {\n setVoiceError(error);\n }, []);\n\n const waitingForAssistant = useMemo(() => {\n if ((!isLoading && !isStreaming) || messages.length === 0) return false;\n if ((response ?? '').trim()) return false;\n return messages[messages.length - 1]?.role === 'user';\n }, [isLoading, isStreaming, messages, response]);\n\n /*\n * NOTE: the previous read-only `isDeepSearchChannel` derivation has been\n * removed. Mode is now a freely-toggleable local state (`activeMode`)\n * driven by tapping the search/zap pills, mirroring HomeScreen. All\n * downstream branches use `isDeepSearchMode` declared with the rest of\n * the composer state above.\n */\n\n /**\n * Deep-search overlay state.\n *\n * Restored from the previous Home-screen flow (commit history): a deep-search\n * channel renders the structured `DeepSearchModal` (query / live summary /\n * sources / research process) on top of the regular Chat conversation. Closing\n * the modal reveals the same channel's transcript underneath so the\n * user can still see the raw turns.\n *\n * Trigger semantics — IMPORTANT:\n * • Opens ONLY when a request is actually fired in this session:\n * - Home → Chat auto-fire of `initialQuery`\n * - User sends a new message in a deep-search channel from the composer\n * - Explicit retry from inside the modal\n * • Does NOT open on plain entry from ChatHistoryScreen. That's a revisit\n * of an already-completed thread — no new API call, nothing live to\n * stream, so popping the modal would just be a confusing flash. The\n * user still sees the deep-search rendering in the chat conversation\n * underneath, and any new send re-opens the live view.\n *\n * Local UI state (accordions, open flag) lives here; data state (query /\n * summary / sources) is DERIVED from the chat stream, not stored separately,\n * so there's no drift between the modal and the underlying conversation.\n */\n const [isDeepSearchModalOpen, setIsDeepSearchModalOpen] = useState(false);\n const [researchProcessOpen, setResearchProcessOpen] = useState(true);\n const [sourcesAccordionOpen, setSourcesAccordionOpen] = useState(true);\n\n /**\n * Modal data is always scoped to the CURRENT run, not the whole channel.\n * In a multi-turn deep-search channel each send is its own \"run\": one user\n * query → one assistant summary. If we sourced from \"first user message\" /\n * \"any assistant message\", a follow-up send would keep showing the previous\n * run's query and stale sources during the transient window between the new\n * user message being appended and the first stream token arriving.\n *\n * Cursor: the LAST user message in the thread. The assistant content for\n * the current run is either:\n * • `response` while the stream is filling (token-by-token), OR\n * • the assistant message that comes AFTER the last user (run complete), OR\n * • empty — covers the brief \"user message appended, no response yet\"\n * window so the modal flips to \"Running...\" with no stale body.\n */\n const lastUserIndex = useMemo(() => {\n for (let i = messages.length - 1; i >= 0; i -= 1) {\n if (messages[i]?.role === 'user') return i;\n }\n return -1;\n }, [messages]);\n\n const deepSearchQuery = useMemo(() => {\n if (lastUserIndex >= 0) {\n const txt = messages[lastUserIndex]?.content?.trim();\n if (txt) return txt;\n }\n return initialQuery ?? null;\n }, [messages, lastUserIndex, initialQuery]);\n\n const latestAssistantContent = useMemo(() => {\n if (response && response.trim()) return response;\n if (lastUserIndex < 0) return '';\n // Look for an assistant turn strictly AFTER the last user message.\n // Anything before it belongs to a prior run and must not leak into\n // the modal for the current one.\n for (let i = lastUserIndex + 1; i < messages.length; i += 1) {\n if (messages[i]?.role === 'assistant') {\n return messages[i]?.content ?? '';\n }\n }\n return '';\n }, [response, messages, lastUserIndex]);\n\n const deepSearchSummary = useMemo(() => normalizeSummaryText(latestAssistantContent), [latestAssistantContent]);\n const deepSearchSources: DeepSearchSourceItem[] = useMemo(\n () => extractDeepSearchSources(latestAssistantContent),\n [latestAssistantContent],\n );\n\n const handleDeepSearchClose = useCallback(() => {\n if (isStreaming) cancel();\n setIsDeepSearchModalOpen(false);\n }, [isStreaming, cancel]);\n\n const handleDeepSearchRetry = useCallback(() => {\n if (!deepSearchQuery || isStreaming || !channelId) return;\n void sendMessage(deepSearchQuery, undefined, channelId);\n }, [deepSearchQuery, isStreaming, channelId, sendMessage]);\n const leftItems = useMemo(\n () =>\n getDefaultLeftItems({\n search: { active: activeMode === 'chat', onClick: () => handleModeSwitch('chat') },\n zap: { active: activeMode === 'deep-search', onClick: () => handleModeSwitch('deep-search') },\n lightbulb: {\n active: activeMode === 'build',\n onClick: () => handleModeSwitch('build'),\n },\n }),\n [activeMode, handleModeSwitch],\n );\n const inputPlaceholder = isDeepSearchMode\n ? 'Research anything...'\n : isBuildMode\n ? 'Describe an app to build...'\n : 'Ask anything...';\n\n const inputConfig = useMemo(\n () => ({\n value,\n onChange: handleValueChange,\n placeholder: inputPlaceholder,\n disabled: !channelId,\n inputRef,\n returnKeyType: 'send',\n blurOnSubmit: false,\n onSubmitEditing: () => handleSend(),\n }),\n [value, handleValueChange, inputPlaceholder, channelId, handleSend],\n );\n const micSendButton = useMemo(\n () => ({\n hasContent: hasQuery || hasAttachments,\n onSend: () => handleSend(),\n onMic: handleMicPress,\n disabled: !channelId,\n // Empty + streaming → Stop. Typed follow-up → Send (steer). Never lock the composer.\n isLoading: canSubmit ? false : isLoading,\n onStop: isStreaming && !canSubmit ? cancel : undefined,\n steerable: isLoading || isStreaming,\n }),\n [canSubmit, handleSend, handleMicPress, isLoading, channelId, isStreaming, cancel, hasQuery, hasAttachments],\n );\n\n /**\n * Loader visibility:\n * - `isHydrating`: Apollo channel history is being fetched and we have nothing to show yet.\n * - `isWaitingForGateway`: Home navigated us here with an `initialQuery`, but CDeCLI is still\n * establishing the workspace session, so the auto-fire is deferred. Show a \"Connecting…\"\n * placeholder instead of an empty thread so the user doesn't think their send was dropped.\n */\n const isHydrating = messagesLoading && messages.length === 0 && !response && !quickReply && !isStreaming;\n const isWaitingForGateway =\n !autoFiredRef.current &&\n !!channelId &&\n (!!initialQuery || hasComposerAttachments(channelId)) &&\n cdecli.status !== 'connected' &&\n cdecli.status !== 'error';\n const showSendingLoader = waitingForAssistant;\n const pinSendingLoaderNearComposer = messages.length > 0 || Boolean((response ?? '').trim());\n\n return (\n <SafeAreaView edges={['left', 'right']} style={{ flex: 1, backgroundColor: surfaceColor }}>\n <KeyboardAwareScreen>\n <View style={styles.chatColumn}>\n {(chatError || cdecli.error) && (\n <Box\n mb=\"$2\"\n mx=\"$4\"\n p=\"$3\"\n borderRadius=\"$md\"\n style={{\n backgroundColor: isDark ? '#2b1a1d' : '#fef2f2',\n borderWidth: 1,\n borderColor: isDark ? '#7f1d1d' : '#fecaca',\n }}\n >\n <Text fontSize=\"$sm\" style={{ color: isDark ? '#fecaca' : '#991b1b' }}>\n {chatError || cdecli.error}\n </Text>\n </Box>\n )}\n {!channelId ? (\n <Box flex={1} alignItems=\"center\" justifyContent=\"center\">\n <YantraBrandLoader size={YANTRA_LOADER_SIZE_COMPACT} />\n <Text style={[styles.statusText, { color: secondaryTextColor }]}>No channel selected</Text>\n </Box>\n ) : isHydrating ? (\n <Box flex={1} px=\"$4\" pt=\"$8\">\n <ThinkingIndicator\n variant=\"awaiting\"\n color={secondaryTextColor}\n isDark={isDark}\n label=\"Opening conversation…\"\n />\n </Box>\n ) : isWaitingForGateway ? (\n <Box flex={1} alignItems=\"center\" justifyContent=\"center\">\n <YantraBrandLoader size={YANTRA_LOADER_SIZE_COMPACT} />\n <Text style={[styles.statusText, { color: secondaryTextColor }]}>Connecting gateway…</Text>\n </Box>\n ) : (\n <View style={styles.transcriptPane}>\n <ChatTranscript\n messages={transcriptRows}\n streamingContent={response}\n listFooter={\n showSendingLoader && pinSendingLoaderNearComposer ? (\n <View style={styles.thinkingInThread}>\n <ThinkingIndicator\n variant=\"awaiting\"\n color={secondaryTextColor}\n isDark={isDark}\n />\n </View>\n ) : null\n }\n renderMessageActions={renderMessageActions}\n isDark={isDark}\n listContentStyle={{\n paddingTop: 12,\n paddingBottom: 8,\n margin: 0,\n width: contentMaxWidth,\n alignSelf: 'center',\n }}\n />\n </View>\n )}\n {showSendingLoader && !pinSendingLoaderNearComposer ? (\n <View pointerEvents=\"none\" style={styles.sendingLoaderEmptyState}>\n <ThinkingIndicator variant=\"awaiting\" color={secondaryTextColor} isDark={isDark} />\n </View>\n ) : null}\n <KeyboardComposerDock closedInset={insets.bottom} onSwipeUp={() => inputRef.current?.focus()}>\n {showAskCard && pendingAsk ? (\n <View style={{ width: contentMaxWidth, paddingHorizontal: 8 }}>\n <AskUserCard\n payload={pendingAsk}\n isDark={isDark}\n onAnswer={(formatted) => {\n setResolvedAskIds((prev) => ({ ...prev, [pendingAsk.requestId]: true }));\n handleAskUserAnswer(formatted);\n }}\n onDismiss={() =>\n setResolvedAskIds((prev) => ({ ...prev, [pendingAsk.requestId]: true }))\n }\n />\n </View>\n ) : null}\n <View style={[styles.bottomComposerInner, { width: contentMaxWidth }]}>\n {voiceError || imageAttachments.error ? (\n <View\n style={[\n styles.voiceErrorBanner,\n {\n backgroundColor: isDark ? '#2b1a1d' : '#fef2f2',\n borderColor: isDark ? '#7f1d1d' : '#fecaca',\n },\n ]}\n >\n <Text style={[styles.voiceErrorText, { color: isDark ? '#fecaca' : '#991b1b' }]}>\n {voiceError || imageAttachments.error}\n </Text>\n </View>\n ) : null}\n {showNativeDictation ? (\n // A throw inside the native recognizer degrades to\n // the Whisper recorder rather than killing the JS\n // thread (same guard rationale as the recorder).\n <MicErrorBoundary onCancel={handleDictationFallback} onError={handleDictationFallback}>\n <NativeDictationBar\n isDark={isDark}\n onInterim={() => undefined}\n onTranscriptionComplete={handleDictationComplete}\n onCancel={handleDictationCancel}\n onFallback={handleDictationFallback}\n />\n </MicErrorBoundary>\n ) : showAudioRecorder ? (\n /*\n * Render-time JS errors thrown by `useAudioRecorder`\n * (e.g. expo-audio native module missing, recorder\n * constructor throws in a release build) are caught\n * by the boundary and converted into a soft cancel\n * via the same handlers the panel itself uses on\n * runtime errors. Without this, a throw during the\n * panel's render phase kills the JS thread in\n * release / TestFlight.\n */\n <MicErrorBoundary onCancel={handleRecorderCancel} onError={handleRecorderError}>\n <AudioRecorderPanel\n isDark={isDark}\n onTranscriptionComplete={handleTranscriptionComplete}\n onCancel={handleRecorderCancel}\n onError={handleRecorderError}\n />\n </MicErrorBoundary>\n ) : (\n <>\n <ComposerAttachmentBar\n attachments={imageAttachments.pending}\n onRemove={imageAttachments.remove}\n isDark={isDark}\n />\n <InputToolBar\n inputConfig={inputConfig}\n leftItems={leftItems}\n rightItems={rightItems}\n templateButton={null}\n templateModalConfig={null}\n micSendButton={micSendButton}\n />\n </>\n )}\n </View>\n </KeyboardComposerDock>\n </View>\n </KeyboardAwareScreen>\n {isDeepSearchMode ? (\n <DeepSearchModal\n visible={isDeepSearchModalOpen}\n query={deepSearchQuery}\n summaryText={deepSearchSummary}\n sources={deepSearchSources}\n isStreaming={isStreaming}\n researchProcessOpen={researchProcessOpen}\n sourcesAccordionOpen={sourcesAccordionOpen}\n onToggleResearchProcess={() => setResearchProcessOpen((prev) => !prev)}\n onToggleSourcesAccordion={() => setSourcesAccordionOpen((prev) => !prev)}\n onRetry={handleDeepSearchRetry}\n onStop={cancel}\n onClose={handleDeepSearchClose}\n />\n ) : null}\n {attachMenu}\n </SafeAreaView>\n );\n}\n\nconst styles = StyleSheet.create({\n chatColumn: {\n flex: 1,\n width: '100%',\n },\n transcriptPane: {\n flex: 1,\n minHeight: 0,\n width: '100%',\n },\n statusText: {\n marginTop: 14,\n fontSize: 13,\n fontWeight: '500',\n },\n bottomComposerInner: {\n paddingHorizontal: 14,\n paddingTop: 10,\n paddingBottom: 0,\n },\n voiceErrorBanner: {\n marginBottom: 8,\n paddingHorizontal: 12,\n paddingVertical: 8,\n borderRadius: 10,\n borderWidth: 1,\n },\n voiceErrorText: {\n fontSize: 12,\n fontWeight: '500',\n },\n sendingLoaderEmptyState: {\n position: 'absolute',\n left: 0,\n right: 0,\n top: 24,\n paddingHorizontal: 20,\n zIndex: 40,\n },\n thinkingInThread: {\n paddingHorizontal: 12,\n paddingTop: 10,\n paddingBottom: 4,\n },\n});\n"],"names":["_a"],"mappings":";;;;;;;;;;;;;;;;;;;AA2DA,SAAwB,UAAa,GAAA;AA3DrC,EAAA,IAAA,EAAA;AA4DE,EAAA,MAAM,aAAa,aAAmB,EAAA;AACtC,EAAA,MAAM,QAAQ,QAAc,EAAA;AAC5B,EAAA,MAAM,MAAU,GAAA,CAAA,EAAA,GAAA,KAAA,IAAA,IAAA,GAAA,MAAA,GAAA,KAAA,CAAO,MAAP,KAAA,IAAA,GAAA,EAAA,GAAiB,EAAC;AAClC,EAAM,MAAA,SAAA,GAAY,QAAQ,MAAM;AAC9B,IAAM,MAAA,GAAA,GAAM,OAAO,MAAO,CAAA,SAAA,KAAc,WAAW,MAAO,CAAA,SAAA,CAAU,MAAS,GAAA,EAAA;AAC7E,IAAO,OAAA,GAAA,CAAI,MAAS,GAAA,CAAA,GAAI,GAAM,GAAA,IAAA;AAAA,GAC7B,EAAA,CAAC,MAAO,CAAA,SAAS,CAAC,CAAA;AACrB,EAAM,MAAA,YAAA,GAAe,QAAQ,MAAM;AACjC,IAAM,MAAA,GAAA,GAAM,OAAO,MAAO,CAAA,YAAA,KAAiB,WAAW,MAAO,CAAA,YAAA,CAAa,MAAS,GAAA,EAAA;AACnF,IAAO,OAAA,GAAA,CAAI,MAAS,GAAA,CAAA,GAAI,GAAM,GAAA,IAAA;AAAA,GAC7B,EAAA,CAAC,MAAO,CAAA,YAAY,CAAC,CAAA;AACxB,EAAA,MAAM,qBAAqB,MAAO,CAAA,kBAAA;AAClC,EAAM,MAAA;AAAA,IACJ;AAAA,MACE,WAAY,EAAA;AAChB,EAAM,MAAA;AAAA,IACJ,KAAO,EAAA;AAAA,MACL,mBAAoB,EAAA;AACxB,EAAM,MAAA,eAAA,GAAkB,KAAK,GAAI,CAAA,GAAA,EAAK,KAAK,GAAI,CAAA,GAAA,EAAK,WAAc,GAAA,EAAE,CAAC,CAAA;AACrE,EAAA,MAAM,YAAe,GAAA,MAAA,GAAS,SAAY,GAAA,YAAA,CAAa,KAAM,CAAA,OAAA;AAC7D,EAAA,MAAM,kBAAqB,GAAA,MAAA,GAAS,SAAY,GAAA,YAAA,CAAa,KAAM,CAAA,SAAA;AACnE,EAAA,MAAM,SAAS,iBAAkB,EAAA;AAWjC,EAAA,MAAM,SAAS,mBAAoB,EAAA;AACnC,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAA,IAAI,CAAC,SAAW,EAAA;AAChB,IAAA,MAAA,CAAO,mBAAmB,SAAS,CAAA;AAAA,GAClC,EAAA,CAAC,SAAW,EAAA,MAAA,CAAO,kBAAkB,CAAC,CAAA;AACzC,EAAA,MAAM,OAAO,iBAAkB,EAAA;AAkB/B,EAAM,MAAA,cAAA,GAAiB,QAAQ,MAAM,IAAA,CAAK,SAAS,GAAI,CAAA,CAAC,KAAK,KAAW,MAAA;AAAA,IACtE,EAAI,EAAA,CAAA,IAAA,EAAO,KAAK,CAAA,CAAA,EAAI,IAAI,IAAI,CAAA,CAAA;AAAA,IAC5B,MAAM,GAAI,CAAA,IAAA;AAAA,IACV,SAAS,GAAI,CAAA,OAAA;AAAA,IACb,aAAa,GAAI,CAAA,WAAA;AAAA,IACjB,UAAW,GAER,CAAA;AAAA,GACH,CAAA,CAAA,EAAG,CAAC,IAAA,CAAK,QAAQ,CAAC,CAAA;AACpB,EAAM,MAAA;AAAA,IACJ,QAAA;AAAA,IACA,QAAA;AAAA,IACA,UAAA;AAAA,IACA,KAAO,EAAA,SAAA;AAAA,IACP,SAAA;AAAA,IACA,WAAA;AAAA,IACA,eAAA;AAAA,IACA,WAAA;AAAA,IACA;AAAA,GACE,GAAA,IAAA;AAOJ,EAAM,MAAA,cAAA,GAAiB,WAAY,CAAA,CAAC,SAAsB,KAAA;AA7I5D,IAAA,IAAAA,GAAA,EAAA,EAAA;AA8II,IAAM,MAAA,KAAA,GAAQ,uBAAwB,CAAA,IAAA,CAAK,SAAS,CAAA;AACpD,IAAI,IAAA,CAAC,KAAS,IAAA,CAAC,SAAW,EAAA;AAC1B,IAAS,KAAA,IAAA,CAAA,GAAI,MAAO,CAAA,KAAA,CAAM,CAAC,CAAC,IAAI,CAAG,EAAA,CAAA,IAAK,CAAG,EAAA,CAAA,IAAK,CAAG,EAAA;AACjD,MAAM,MAAA,KAAA,GAAQ,IAAK,CAAA,QAAA,CAAS,CAAC,CAAA;AAC7B,MAAA,IAAA,CAAI,KAAO,IAAA,IAAA,GAAA,MAAA,GAAA,KAAA,CAAA,IAAA,MAAS,MAAWA,KAAAA,CAAAA,CAAAA,GAAAA,GAAA,KAAM,CAAA,OAAA,KAAN,IAAAA,GAAAA,MAAAA,GAAAA,GAAAA,CAAe,IAAU,EAAA,MAAA,CAAA,EAAA,GAAA,KAAA,CAAM,WAAN,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAmB,MAAS,CAAA,CAAA,EAAA;AAClF,QAAA,KAAK,KAAK,WAAY,CAAA,KAAA,CAAM,OAAS,EAAA,KAAA,CAAM,aAAa,SAAS,CAAA;AACjE,QAAA;AAAA;AACF;AACF,GACC,EAAA,CAAC,IAAM,EAAA,SAAS,CAAC,CAAA;AACpB,EAAA,MAAM,WAAW,SAAa,IAAA,WAAA;AAC9B,EAAM,MAAA,oBAAA,GAAuB,WAAY,CAAA,CAAC,OAIpC,KAAA,OAAA,CAAQ,SAAS,WAAc,mBAAA,GAAA,CAAC,gBAAiB,EAAA,EAAA,IAAA,EAAM,OAAQ,CAAA,OAAA,EAAS,cAAc,QAAW,GAAA,MAAA,GAAY,MAAM,cAAA,CAAe,OAAQ,CAAA,EAAE,CAAG,EAAA,CAAA,GAAK,IAAM,EAAA,CAAC,QAAU,EAAA,cAAc,CAAC,CAAA;AAC1L,EAAA,MAAM,mBAAmB,mBAAoB,EAAA;AAC7C,EAAM,MAAA;AAAA,IACJ,UAAA;AAAA,IACA,IAAM,EAAA;AAAA,GACR,GAAI,qBAAsB,CAAA,gBAAA,EAAkB,MAAM,CAAA;AAgBlD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAS,EAAE,CAAA;AACrC,EAAM,MAAA,QAAA,GAAW,OAAO,KAAK,CAAA;AAC7B,EAAA,QAAA,CAAS,OAAU,GAAA,KAAA;AACnB,EAAM,MAAA,QAAA,GAAW,OAAkB,IAAI,CAAA;AACvC,EAAM,MAAA,OAAA,GAAU,MAAM,IAAK,EAAA;AAC3B,EAAM,MAAA,QAAA,GAAW,QAAQ,MAAS,GAAA,CAAA;AAClC,EAAM,MAAA,cAAA,GAAiB,gBAAiB,CAAA,OAAA,CAAQ,MAAS,GAAA,CAAA;AACzD,EAAA,MAAM,SAAa,GAAA,CAAA,QAAA,IAAY,cAAmB,KAAA,OAAA,CAAQ,SAAS,CAAA;AAgBnE,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,SAAsB,MAAM,MAAA,CAAO,WAAgB,KAAA,aAAA,GAAgB,aAAgB,GAAA,MAAA,CAAO,WAAgB,KAAA,OAAA,GAAU,UAAU,MAAM,CAAA;AACxK,EAAM,MAAA,gBAAA,GAAmB,WAAY,CAAA,CAAC,IAAsB,KAAA;AAC1D,IAAA,aAAA,CAAc,IAAI,CAAA;AAAA,GACpB,EAAG,EAAE,CAAA;AACL,EAAA,MAAM,mBAAmB,UAAe,KAAA,aAAA;AACxC,EAAA,MAAM,cAAc,UAAe,KAAA,OAAA;AAQnC,EAAA,MAAM,CAAC,iBAAA,EAAmB,oBAAoB,CAAA,GAAI,SAAS,KAAK,CAAA;AAGhE,EAAA,MAAM,CAAC,mBAAA,EAAqB,sBAAsB,CAAA,GAAI,SAAS,KAAK,CAAA;AACpE,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,SAAwB,IAAI,CAAA;AAUhE,EAAM,MAAA,YAAA,GAAe,OAAO,KAAK,CAAA;AACjC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,aAAa,OAAS,EAAA;AAC1B,IAAA,IAAI,CAAC,SAAW,EAAA;AAChB,IAAA,IAAI,MAAO,CAAA,MAAA,KAAW,WAAe,IAAA,MAAA,CAAO,WAAW,OAAS,EAAA;AAChE,IAAA,MAAM,UAAU,sBAAuB,CAAA,SAAS,CAAI,GAAA,uBAAA,CAAwB,SAAS,CAAI,GAAA,MAAA;AACzF,IAAM,MAAA,WAAA,GAAA,CAAc,OAAS,IAAA,IAAA,GAAA,MAAA,GAAA,OAAA,CAAA,MAAA,IAAS,OAAU,GAAA,kBAAA;AAChD,IAAA,IAAI,CAAC,YAAA,IAAgB,EAAC,WAAA,IAAA,IAAA,GAAA,MAAA,GAAA,WAAA,CAAa,MAAQ,CAAA,EAAA;AAC3C,IAAA,YAAA,CAAa,OAAU,GAAA,IAAA;AACvB,IAAA,IAAI,gBAAkB,EAAA;AACpB,MAAA,wBAAA,CAAyB,IAAI,CAAA;AAAA;AAE/B,IAAA,KAAA,CAAM,YAAY;AAhPtB,MAAAA,IAAAA,GAAAA;AAiPM,MAAA,MAAM,IAAoB,GAAA,gBAAA,GAAmB,aAAgB,GAAA,WAAA,GAAc,OAAU,GAAA,MAAA;AACrF,MAAA,IAAI,QAAW,GAAA,KAAA;AACf,MAAI,IAAA;AACF,QAAW,QAAA,GAAA,MAAM,oBAAqB,CAAA,YAAA,IAAA,IAAA,GAAA,YAAA,GAAgB,EAAIA,EAAAA,CAAAA,GAAAA,GAAA,2CAAa,MAAb,KAAA,IAAA,GAAAA,GAAuB,GAAA,CAAA,EAAG,IAAI,CAAA;AAAA,OAClF,CAAA,OAAA,CAAA,EAAA;AACN,QAAW,QAAA,GAAA,KAAA;AAAA;AAEb,MAAA,IAAI,QAAU,EAAA;AACZ,QAAA,qBAAA,CAAsB,YAAY,iBAAkB,CAAA;AAAA,UAClD,SAAS,MAAO,CAAA,OAAA;AAAA,UAChB,OAAO,YAAgB,IAAA,IAAA,GAAA,YAAA,GAAA,EAAA;AAAA,UACvB,KAAO,EAAA;AAAA,SACR,CAAC,CAAA;AACF,QAAA;AAAA;AAEF,MAAA,KAAK,WAAY,CAAA,YAAA,IAAA,IAAA,GAAA,YAAA,GAAgB,EAAI,EAAA,WAAA,EAAa,SAAS,CAAA;AAAA,KAC1D,GAAA;AACH,IAAI,IAAA;AACF,MAAA,UAAA,CAAW,SAAU,CAAA;AAAA,QACnB,YAAc,EAAA,IAAA;AAAA,QACd,kBAAoB,EAAA;AAAA,OACrB,CAAA;AAAA,KACK,CAAA,OAAA,CAAA,EAAA;AAAA;AAER,GACC,EAAA,CAAC,SAAW,EAAA,YAAA,EAAc,kBAAoB,EAAA,WAAA,EAAa,UAAY,EAAA,MAAA,CAAO,MAAQ,EAAA,gBAAA,EAAkB,WAAa,EAAA,MAAA,CAAO,OAAO,CAAC,CAAA;AASvI,EAAM,MAAA,mBAAA,GAAsB,WAAY,CAAA,CAAC,SAAsB,KAAA;AAC7D,IAAM,MAAA,IAAA,GAAO,UAAU,IAAK,EAAA;AAC5B,IAAA,IAAI,CAAC,IAAM,EAAA;AACX,IAAK,KAAA,WAAA,CAAY,IAAM,EAAA,MAAA,EAAW,SAAS,CAAA;AAAA,GAC1C,EAAA,CAAC,WAAa,EAAA,SAAS,CAAC,CAAA;AAC3B,EAAA,MAAM,CAAC,cAAgB,EAAA,iBAAiB,CAAI,GAAA,QAAA,CAAkC,EAAE,CAAA;AAChF,EAAM,MAAA,UAAA,GAAa,OAAQ,CAAA,MAAM,cAAe,CAAA,CAAC,GAAG,cAAgB,EAAA,GAAA,CAAI,QAAU,IAAA,IAAA,GAAA,MAAA,GAAA,QAAA,CAAA,IAAA,EAAA,IAAS,CAAC;AAAA,IAC1F,IAAM,EAAA,WAAA;AAAA,IACN,OAAS,EAAA;AAAA,GACV,IAAI,EAAG,CAAC,CAAG,EAAA,CAAC,cAAgB,EAAA,QAAQ,CAAC,CAAA;AACtC,EAAA,MAAM,cAAc,OAAQ,CAAA,UAAA,IAAc,CAAC,cAAe,CAAA,UAAA,CAAW,SAAS,CAAC,CAAA;AAC/E,EAAM,MAAA,UAAA,GAAa,WAAY,CAAA,CAAC,IAAkB,KAAA;AA9RpD,IAAAA,IAAAA,GAAAA;AA+RI,IAAM,MAAA,OAAA,GAAU,iBAAiB,OAAQ,EAAA;AACzC,IAAA,MAAM,CAAK,GAAA,CAAA,IAAA,IAAA,IAAA,GAAA,IAAA,GAAQ,QAAS,CAAA,OAAA,EAAS,IAAK,EAAA;AAC1C,IAAA,IAAI,CAAC,CAAK,IAAA,EAAC,OAAS,IAAA,IAAA,GAAA,MAAA,GAAA,OAAA,CAAA,MAAA,CAAA,IAAU,CAAC,SAAW,EAAA;AAK1C,IAAA,QAAA,CAAS,EAAE,CAAA;AACX,IAAA,CAAAA,GAAA,GAAA,QAAA,CAAS,OAAT,KAAA,IAAA,GAAA,MAAA,GAAAA,GAAkB,CAAA,KAAA,EAAA;AAGlB,IAAA,IAAI,WAAa,EAAA;AACf,MAAW,UAAA,CAAA,QAAA,CAAS,cAAc,QAAS,CAAA;AAAA,QACzC,IAAM,EAAA,sBAAA;AAAA,QACN,MAAA,EAAQ,aACF,CAAA,cAAA,CAAA,EAAA,EAAA,MAAA,CAAO,OAAU,GAAA;AAAA,UACnB,SAAS,MAAO,CAAA;AAAA,SAClB,GAAI,EAHE,CAAA,EAAA;AAAA,UAIN,SAAA;AAAA,UACA,OAAS,EAAA;AAAA,SACX;AAAA,OACD,CAAC,CAAA;AACF,MAAA;AAAA;AASF,IAAA,IAAI,gBAAkB,EAAA;AACpB,MAAA,wBAAA,CAAyB,IAAI,CAAA;AAAA;AAE/B,IAAA,KAAA,CAAM,YAAY;AAChB,MAAA,IAAI,QAAW,GAAA,KAAA;AACf,MAAI,IAAA;AACF,QAAA,QAAA,GAAW,MAAM,oBAAqB,CAAA,CAAA,EAAG,QAAQ,MAAQ,EAAA,gBAAA,GAAmB,gBAAgB,MAAM,CAAA;AAAA,OAC5F,CAAA,OAAA,CAAA,EAAA;AACN,QAAW,QAAA,GAAA,KAAA;AAAA;AAEb,MAAA,IAAI,QAAU,EAAA;AACZ,QAAA,MAAM,aAAa,iBAAkB,CAAA;AAAA,UACnC,SAAS,MAAO,CAAA,OAAA;AAAA,UAChB,KAAO,EAAA,CAAA;AAAA,UACP,KAAO,EAAA;AAAA,SACR,CAAA;AACD,QAAI,IAAA,QAAA,CAAS,WAAW,CAAG,EAAA;AACzB,UAAA,qBAAA,CAAsB,YAAY,UAAU,CAAA;AAAA,SACvC,MAAA;AACL,UAAA,cAAA,CAAe,YAAY,UAAU,CAAA;AAAA;AAEvC,QAAA;AAAA;AAEF,MAAK,KAAA,WAAA,CAAY,CAAG,EAAA,OAAA,EAAS,SAAS,CAAA;AAAA,KACrC,GAAA;AACH,IAAA,gBAAA,CAAiB,KAAM,EAAA;AAAA,GACtB,EAAA,CAAC,SAAW,EAAA,WAAA,EAAa,gBAAkB,EAAA,WAAA,EAAa,UAAY,EAAA,MAAA,CAAO,OAAS,EAAA,gBAAA,EAAkB,QAAS,CAAA,MAAM,CAAC,CAAA;AACzH,EAAM,MAAA,iBAAA,GAAoB,WAAY,CAAA,CAAC,CAAW,KAAA;AAzVpD,IAAA,IAAAA,GAAA,EAAA,EAAA;AA0VI,IAAS,QAAA,CAAA,CAAA,EAAA,GAAA,CAAAA,MAAA,CAAG,IAAA,IAAA,GAAA,MAAA,GAAA,CAAA,CAAA,WAAA,KAAH,gBAAAA,GAAgB,CAAA,IAAA,KAAhB,YAAwB,EAAE,CAAA;AAAA,GACrC,EAAG,EAAE,CAAA;AAsBL,EAAM,MAAA,mBAAA,GAAsB,YAAY,MAAM;AAC5C,IAAA,aAAA,CAAc,IAAI,CAAA;AAClB,IAAA,KAAA,CAAM,YAAY;AAChB,MAAM,MAAA;AAAA,QACJ,OAAA;AAAA,QACA;AAAA,OACF,GAAI,MAAM,oBAAqB,EAAA;AAC/B,MAAA,IAAI,CAAC,OAAS,EAAA;AACZ,QAAA,aAAA,CAAc,SAAS,8BAA8B,CAAA;AACrD,QAAA;AAAA;AAEF,MAAA,oBAAA,CAAqB,IAAI,CAAA;AAAA,KACxB,GAAA;AAAA,GACL,EAAG,EAAE,CAAA;AACL,EAAM,MAAA,cAAA,GAAiB,YAAY,MAAM;AACvC,IAAA,IAAI,QAAU,EAAA;AACd,IAAA,aAAA,CAAc,IAAI,CAAA;AAClB,IAAA,QAAA,CAAS,OAAQ,EAAA;AAKjB,IAAA,IAAI,4BAA8B,EAAA;AAChC,MAAA,sBAAA,CAAuB,IAAI,CAAA;AAC3B,MAAA;AAAA;AAEF,IAAoB,mBAAA,EAAA;AAAA,GACnB,EAAA,CAAC,QAAU,EAAA,mBAAmB,CAAC,CAAA;AAUlC,EAAM,MAAA,eAAA,GAAkB,WAAY,CAAA,CAAC,IAAiB,KAAA;AACpD,IAAM,MAAA,OAAA,GAAA,CAAW,IAAQ,IAAA,IAAA,GAAA,IAAA,GAAA,EAAA,EAAI,IAAK,EAAA;AAClC,IAAA,IAAI,CAAC,OAAS,EAAA;AACd,IAAA,QAAA,CAAS,CAAQ,IAAA,KAAA;AACf,MAAA,MAAM,IAAO,GAAA,IAAA,CAAK,IAAK,EAAA,CAAE,MAAS,GAAA,CAAA,GAAI,CAAG,EAAA,IAAA,CAAK,IAAK,EAAC,CAAI,CAAA,EAAA,OAAO,CAAK,CAAA,GAAA,OAAA;AAOpE,MAAA,qBAAA,CAAsB,MAAM;AAjalC,QAAA,IAAAA,GAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AAkaQ,QAAA,CAAA,EAAA,GAAA,CAAAA,MAAA,QAAS,CAAA,OAAA,KAAT,gBAAAA,GAAkB,CAAA,cAAA,KAAlB,wBAAAA,GAAmC,EAAA;AAAA,UACjC,IAAM,EAAA;AAAA,SACR,CAAA;AACA,QAAS,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,QAAA,CAAA,OAAA,KAAT,mBAAkB,KAAlB,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,EAAA,CAAA;AAAA,OACD,CAAA;AACD,MAAO,OAAA,IAAA;AAAA,KACR,CAAA;AAAA,GACH,EAAG,EAAE,CAAA;AACL,EAAM,MAAA,2BAAA,GAA8B,WAAY,CAAA,CAAC,IAAiB,KAAA;AAChE,IAAA,oBAAA,CAAqB,KAAK,CAAA;AAC1B,IAAA,eAAA,CAAgB,IAAI,CAAA;AAAA,GACtB,EAAG,CAAC,eAAe,CAAC,CAAA;AACpB,EAAM,MAAA,oBAAA,GAAuB,YAAY,MAAM;AAC7C,IAAA,oBAAA,CAAqB,KAAK,CAAA;AAAA,GAC5B,EAAG,EAAE,CAAA;AACL,EAAM,MAAA,uBAAA,GAA0B,WAAY,CAAA,CAAC,IAAiB,KAAA;AAC5D,IAAA,sBAAA,CAAuB,KAAK,CAAA;AAC5B,IAAA,eAAA,CAAgB,IAAI,CAAA;AAAA,GACtB,EAAG,CAAC,eAAe,CAAC,CAAA;AACpB,EAAA,MAAM,wBAAwB,WAAY,CAAA,MAAM,uBAAuB,KAAK,CAAA,EAAG,EAAE,CAAA;AAIjF,EAAM,MAAA,uBAAA,GAA0B,YAAY,MAAM;AAChD,IAAA,sBAAA,CAAuB,KAAK,CAAA;AAC5B,IAAoB,mBAAA,EAAA;AAAA,GACtB,EAAG,CAAC,mBAAmB,CAAC,CAAA;AACxB,EAAM,MAAA,mBAAA,GAAsB,WAAY,CAAA,CAAC,KAAkB,KAAA;AACzD,IAAA,aAAA,CAAc,KAAK,CAAA;AAAA,GACrB,EAAG,EAAE,CAAA;AACL,EAAM,MAAA,mBAAA,GAAsB,QAAQ,MAAM;AAhc5C,IAAAA,IAAAA,GAAAA;AAicI,IAAA,IAAI,CAAC,SAAa,IAAA,CAAC,eAAe,QAAS,CAAA,MAAA,KAAW,GAAU,OAAA,KAAA;AAChE,IAAA,IAAA,CAAK,QAAY,IAAA,IAAA,GAAA,QAAA,GAAA,EAAA,EAAI,IAAK,EAAA,EAAU,OAAA,KAAA;AACpC,IAAOA,OAAAA,CAAAA,CAAAA,GAAAA,GAAA,SAAS,QAAS,CAAA,MAAA,GAAS,CAAC,CAA5B,KAAA,IAAA,GAAA,MAAA,GAAAA,IAA+B,IAAS,MAAA,MAAA;AAAA,KAC9C,CAAC,SAAA,EAAW,WAAa,EAAA,QAAA,EAAU,QAAQ,CAAC,CAAA;AAkC/C,EAAA,MAAM,CAAC,qBAAA,EAAuB,wBAAwB,CAAA,GAAI,SAAS,KAAK,CAAA;AACxE,EAAA,MAAM,CAAC,mBAAA,EAAqB,sBAAsB,CAAA,GAAI,SAAS,IAAI,CAAA;AACnE,EAAA,MAAM,CAAC,oBAAA,EAAsB,uBAAuB,CAAA,GAAI,SAAS,IAAI,CAAA;AAiBrE,EAAM,MAAA,aAAA,GAAgB,QAAQ,MAAM;AAzftC,IAAAA,IAAAA,GAAAA;AA0fI,IAAA,KAAA,IAAS,IAAI,QAAS,CAAA,MAAA,GAAS,GAAG,CAAK,IAAA,CAAA,EAAG,KAAK,CAAG,EAAA;AAChD,MAAIA,IAAAA,CAAAA,CAAAA,GAAAA,GAAA,SAAS,CAAC,CAAA,KAAV,gBAAAA,GAAa,CAAA,IAAA,MAAS,QAAe,OAAA,CAAA;AAAA;AAE3C,IAAO,OAAA,EAAA;AAAA,GACT,EAAG,CAAC,QAAQ,CAAC,CAAA;AACb,EAAM,MAAA,eAAA,GAAkB,QAAQ,MAAM;AA/fxC,IAAA,IAAAA,GAAA,EAAA,EAAA;AAggBI,IAAA,IAAI,iBAAiB,CAAG,EAAA;AACtB,MAAM,MAAA,GAAA,GAAA,CAAM,MAAAA,GAAA,GAAA,QAAA,CAAS,aAAa,CAAtB,KAAA,IAAA,GAAA,MAAA,GAAAA,GAAyB,CAAA,OAAA,KAAzB,IAAkC,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,EAAA;AAC9C,MAAA,IAAI,KAAY,OAAA,GAAA;AAAA;AAElB,IAAA,OAAO,YAAgB,IAAA,IAAA,GAAA,YAAA,GAAA,IAAA;AAAA,GACtB,EAAA,CAAC,QAAU,EAAA,aAAA,EAAe,YAAY,CAAC,CAAA;AAC1C,EAAM,MAAA,sBAAA,GAAyB,QAAQ,MAAM;AAtgB/C,IAAA,IAAAA,GAAA,EAAA,EAAA,EAAA,EAAA;AAugBI,IAAA,IAAI,QAAY,IAAA,QAAA,CAAS,IAAK,EAAA,EAAU,OAAA,QAAA;AACxC,IAAI,IAAA,aAAA,GAAgB,GAAU,OAAA,EAAA;AAI9B,IAAA,KAAA,IAAS,IAAI,aAAgB,GAAA,CAAA,EAAG,IAAI,QAAS,CAAA,MAAA,EAAQ,KAAK,CAAG,EAAA;AAC3D,MAAA,IAAA,CAAA,CAAIA,MAAA,QAAS,CAAA,CAAC,MAAV,IAAAA,GAAAA,MAAAA,GAAAA,GAAAA,CAAa,UAAS,WAAa,EAAA;AACrC,QAAA,OAAA,CAAO,EAAS,GAAA,CAAA,EAAA,GAAA,QAAA,CAAA,CAAC,CAAV,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAa,YAAb,IAAwB,GAAA,EAAA,GAAA,EAAA;AAAA;AACjC;AAEF,IAAO,OAAA,EAAA;AAAA,GACN,EAAA,CAAC,QAAU,EAAA,QAAA,EAAU,aAAa,CAAC,CAAA;AACtC,EAAM,MAAA,iBAAA,GAAoB,QAAQ,MAAM,oBAAA,CAAqB,sBAAsB,CAAG,EAAA,CAAC,sBAAsB,CAAC,CAAA;AAC9G,EAAM,MAAA,iBAAA,GAA4C,QAAQ,MAAM,wBAAA,CAAyB,sBAAsB,CAAG,EAAA,CAAC,sBAAsB,CAAC,CAAA;AAC1I,EAAM,MAAA,qBAAA,GAAwB,YAAY,MAAM;AAC9C,IAAA,IAAI,aAAoB,MAAA,EAAA;AACxB,IAAA,wBAAA,CAAyB,KAAK,CAAA;AAAA,GAC7B,EAAA,CAAC,WAAa,EAAA,MAAM,CAAC,CAAA;AACxB,EAAM,MAAA,qBAAA,GAAwB,YAAY,MAAM;AAC9C,IAAA,IAAI,CAAC,eAAA,IAAmB,WAAe,IAAA,CAAC,SAAW,EAAA;AACnD,IAAK,KAAA,WAAA,CAAY,eAAiB,EAAA,MAAA,EAAW,SAAS,CAAA;AAAA,KACrD,CAAC,eAAA,EAAiB,WAAa,EAAA,SAAA,EAAW,WAAW,CAAC,CAAA;AACzD,EAAM,MAAA,SAAA,GAAY,OAAQ,CAAA,MAAM,mBAAoB,CAAA;AAAA,IAClD,MAAQ,EAAA;AAAA,MACN,QAAQ,UAAe,KAAA,MAAA;AAAA,MACvB,OAAA,EAAS,MAAM,gBAAA,CAAiB,MAAM;AAAA,KACxC;AAAA,IACA,GAAK,EAAA;AAAA,MACH,QAAQ,UAAe,KAAA,aAAA;AAAA,MACvB,OAAA,EAAS,MAAM,gBAAA,CAAiB,aAAa;AAAA,KAC/C;AAAA,IACA,SAAW,EAAA;AAAA,MACT,QAAQ,UAAe,KAAA,OAAA;AAAA,MACvB,OAAA,EAAS,MAAM,gBAAA,CAAiB,OAAO;AAAA;AACzC,GACD,CAAA,EAAG,CAAC,UAAA,EAAY,gBAAgB,CAAC,CAAA;AAClC,EAAA,MAAM,gBAAmB,GAAA,gBAAA,GAAmB,sBAAyB,GAAA,WAAA,GAAc,6BAAgC,GAAA,iBAAA;AACnH,EAAM,MAAA,WAAA,GAAc,QAAQ,OAAO;AAAA,IACjC,KAAA;AAAA,IACA,QAAU,EAAA,iBAAA;AAAA,IACV,WAAa,EAAA,gBAAA;AAAA,IACb,UAAU,CAAC,SAAA;AAAA,IACX,QAAA;AAAA,IACA,aAAe,EAAA,MAAA;AAAA,IACf,YAAc,EAAA,KAAA;AAAA,IACd,eAAA,EAAiB,MAAM,UAAW;AAAA,MAChC,CAAC,KAAA,EAAO,mBAAmB,gBAAkB,EAAA,SAAA,EAAW,UAAU,CAAC,CAAA;AACvE,EAAM,MAAA,aAAA,GAAgB,QAAQ,OAAO;AAAA,IACnC,YAAY,QAAY,IAAA,cAAA;AAAA,IACxB,MAAA,EAAQ,MAAM,UAAW,EAAA;AAAA,IACzB,KAAO,EAAA,cAAA;AAAA,IACP,UAAU,CAAC,SAAA;AAAA;AAAA,IAEX,SAAA,EAAW,YAAY,KAAQ,GAAA,SAAA;AAAA,IAC/B,MAAQ,EAAA,WAAA,IAAe,CAAC,SAAA,GAAY,MAAS,GAAA,MAAA;AAAA,IAC7C,WAAW,SAAa,IAAA;AAAA,GAC1B,CAAA,EAAI,CAAC,SAAA,EAAW,UAAY,EAAA,cAAA,EAAgB,SAAW,EAAA,SAAA,EAAW,WAAa,EAAA,MAAA,EAAQ,QAAU,EAAA,cAAc,CAAC,CAAA;AAShH,EAAM,MAAA,WAAA,GAAc,mBAAmB,QAAS,CAAA,MAAA,KAAW,KAAK,CAAC,QAAA,IAAY,CAAC,UAAA,IAAc,CAAC,WAAA;AAC7F,EAAA,MAAM,sBAAsB,CAAC,YAAA,CAAa,OAAW,IAAA,CAAC,CAAC,SAAc,KAAA,CAAC,CAAC,YAAA,IAAgB,uBAAuB,SAAS,CAAA,CAAA,IAAM,OAAO,MAAW,KAAA,WAAA,IAAe,OAAO,MAAW,KAAA,OAAA;AAChL,EAAA,MAAM,iBAAoB,GAAA,mBAAA;AAC1B,EAAM,MAAA,4BAAA,GAA+B,SAAS,MAAS,GAAA,CAAA,IAAK,SAAS,QAAY,IAAA,IAAA,GAAA,QAAA,GAAA,EAAA,EAAI,MAAM,CAAA;AAC3F,EAAA,4BAAQ,YAAa,EAAA,EAAA,KAAA,EAAO,CAAC,MAAQ,EAAA,OAAO,GAAG,KAAO,EAAA;AAAA,IACpD,IAAM,EAAA,CAAA;AAAA,IACN,eAAiB,EAAA;AAAA,GAET,EAAA,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,mBACG,EAAA,EAAA,QAAA,kBAAA,IAAA,CAAC,IAAK,EAAA,EAAA,KAAA,EAAO,OAAO,UACd,EAAA,QAAA,EAAA;AAAA,MAAA,CAAA,SAAA,IAAa,MAAO,CAAA,KAAA,qBAAW,GAAA,CAAA,GAAA,EAAA,EAAI,EAAG,EAAA,IAAA,EAAK,EAAG,EAAA,IAAA,EAAK,CAAE,EAAA,IAAA,EAAK,YAAa,EAAA,KAAA,EAAM,KAAO,EAAA;AAAA,QAChG,eAAA,EAAiB,SAAS,SAAY,GAAA,SAAA;AAAA,QACtC,WAAa,EAAA,CAAA;AAAA,QACb,WAAA,EAAa,SAAS,SAAY,GAAA;AAAA,OAEhB,EAAA,QAAA,kBAAA,GAAA,CAAC,IAAK,EAAA,EAAA,QAAA,EAAS,OAAM,KAAO,EAAA;AAAA,QAC5C,KAAA,EAAO,SAAS,SAAY,GAAA;AAAA,OAEP,EAAA,QAAA,EAAA,SAAA,IAAa,MAAO,CAAA,KAAA,EACzB,CACJ,EAAA,CAAA;AAAA,MACH,CAAC,4BAAa,IAAA,CAAA,GAAA,EAAA,EAAI,MAAM,CAAG,EAAA,UAAA,EAAW,QAAS,EAAA,cAAA,EAAe,QACvD,EAAA,QAAA,EAAA;AAAA,wBAAC,GAAA,CAAA,iBAAA,EAAA,EAAkB,MAAM,0BAA4B,EAAA,CAAA;AAAA,wBACpD,GAAA,CAAA,IAAA,EAAA,EAAK,KAAO,EAAA,CAAC,OAAO,UAAY,EAAA;AAAA,UACjD,KAAO,EAAA;AAAA,SACR,GAAG,QAAmB,EAAA,qBAAA,EAAA;AAAA,OAAA,EACT,CAAS,GAAA,WAAA,mBAAe,GAAA,CAAA,GAAA,EAAA,EAAI,IAAM,EAAA,CAAA,EAAG,EAAG,EAAA,IAAA,EAAK,EAAG,EAAA,IAAA,EAC5C,QAAC,kBAAA,GAAA,CAAA,iBAAA,EAAA,EAAkB,OAAQ,EAAA,UAAA,EAAW,KAAO,EAAA,kBAAA,EAAoB,MAAgB,EAAA,KAAA,EAAM,4BAAwB,EAAA,CAAA,EACnH,CAAS,GAAA,mBAAA,mBAAuB,IAAA,CAAA,GAAA,EAAA,EAAI,IAAM,EAAA,CAAA,EAAG,UAAW,EAAA,QAAA,EAAS,gBAAe,QAC5E,EAAA,QAAA,EAAA;AAAA,wBAAC,GAAA,CAAA,iBAAA,EAAA,EAAkB,MAAM,0BAA4B,EAAA,CAAA;AAAA,wBACpD,GAAA,CAAA,IAAA,EAAA,EAAK,KAAO,EAAA,CAAC,OAAO,UAAY,EAAA;AAAA,UACjD,KAAO,EAAA;AAAA,SACR,GAAG,QAAmB,EAAA,0BAAA,EAAA;AAAA,OAAA,EACT,CAAS,mBAAA,GAAA,CAAC,IAAK,EAAA,EAAA,KAAA,EAAO,OAAO,cACzB,EAAA,QAAA,kBAAA,GAAA,CAAC,cAAe,EAAA,EAAA,QAAA,EAAU,cAAgB,EAAA,gBAAA,EAAkB,QAAU,EAAA,UAAA,EAAY,qBAAqB,4BAA+B,mBAAA,GAAA,CAAC,IAAK,EAAA,EAAA,KAAA,EAAO,MAAO,CAAA,gBAAA,EAC1I,QAAC,kBAAA,GAAA,CAAA,iBAAA,EAAA,EAAkB,SAAQ,UAAW,EAAA,KAAA,EAAO,kBAAoB,EAAA,MAAA,EAAgB,CACrF,EAAA,CAAA,GAAU,IAAM,EAAA,oBAAA,EAA4C,QAAgB,gBAAkB,EAAA;AAAA,QAC1H,UAAY,EAAA,EAAA;AAAA,QACZ,aAAe,EAAA,CAAA;AAAA,QACf,MAAQ,EAAA,CAAA;AAAA,QACR,KAAO,EAAA,eAAA;AAAA,QACP,SAAW,EAAA;AAAA,SACV,CACW,EAAA,CAAA;AAAA,MACH,qBAAqB,CAAC,4BAAA,uBAAgC,IAAK,EAAA,EAAA,aAAA,EAAc,QAAO,KAAO,EAAA,MAAA,CAAO,uBACvF,EAAA,QAAA,kBAAA,GAAA,CAAC,qBAAkB,OAAQ,EAAA,UAAA,EAAW,OAAO,kBAAoB,EAAA,MAAA,EAAgB,GACrF,CAAU,GAAA,IAAA;AAAA,2BACb,oBAAqB,EAAA,EAAA,WAAA,EAAa,MAAO,CAAA,MAAA,EAAQ,WAAW,MAAG;AAvnBpF,QAAAA,IAAAA,GAAAA;AAunBuF,QAAA,OAAA,CAAAA,GAAA,GAAA,QAAA,CAAS,OAAT,KAAA,IAAA,GAAA,MAAA,GAAAA,GAAkB,CAAA,KAAA,EAAA;AAAA,OAChF,EAAA,QAAA,EAAA;AAAA,QAAe,WAAA,IAAA,UAAA,mBAAc,GAAA,CAAA,IAAA,EAAA,EAAK,KAAO,EAAA;AAAA,UACtD,KAAO,EAAA,eAAA;AAAA,UACP,iBAAmB,EAAA;AAAA,WAEC,QAAC,kBAAA,GAAA,CAAA,WAAA,EAAA,EAAY,SAAS,UAAY,EAAA,MAAA,EAAgB,UAAU,CAAa,SAAA,KAAA;AAC3F,UAAkB,iBAAA,CAAA,CAAA,IAAA,KAAS,iCACtB,IADsB,CAAA,EAAA;AAAA,YAEzB,CAAC,UAAW,CAAA,SAAS,GAAG;AAAA,WACxB,CAAA,CAAA;AACF,UAAA,mBAAA,CAAoB,SAAS,CAAA;AAAA,WAC5B,SAAW,EAAA,MAAM,iBAAkB,CAAA,CAAA,IAAA,KAAS,iCAC1C,IAD0C,CAAA,EAAA;AAAA,UAE7C,CAAC,UAAW,CAAA,SAAS,GAAG;AAAA,SAC1B,CAAE,CAAG,EAAA,CAAA,EACW,CAAU,GAAA,IAAA;AAAA,wBACb,IAAA,CAAA,IAAA,EAAA,EAAK,KAAO,EAAA,CAAC,OAAO,mBAAqB,EAAA;AAAA,UACtD,KAAO,EAAA;AAAA,SACR,CACkB,EAAA,QAAA,EAAA;AAAA,UAAA,UAAA,IAAc,iBAAiB,KAAQ,mBAAA,GAAA,CAAC,QAAK,KAAO,EAAA,CAAC,OAAO,gBAAkB,EAAA;AAAA,YAC7F,eAAA,EAAiB,SAAS,SAAY,GAAA,SAAA;AAAA,YACtC,WAAA,EAAa,SAAS,SAAY,GAAA;AAAA,WACnC,CACuB,EAAA,QAAA,kBAAA,GAAA,CAAC,QAAK,KAAO,EAAA,CAAC,OAAO,cAAgB,EAAA;AAAA,YACzD,KAAA,EAAO,SAAS,SAAY,GAAA;AAAA,WAC7B,CAC0B,EAAA,QAAA,EAAA,UAAA,IAAc,gBAAiB,CAAA,KAAA,EACpC,GACJ,CAAU,GAAA,IAAA;AAAA,UACb,mBAAA;AAAA;AAAA;AAAA;AAAA,gCAIhB,gBAAiB,EAAA,EAAA,QAAA,EAAU,yBAAyB,OAAS,EAAA,uBAAA,EACtC,8BAAC,kBAAmB,EAAA,EAAA,MAAA,EAAgB,SAAW,EAAA,MAAM,QAAW,uBAAyB,EAAA,uBAAA,EAAyB,UAAU,qBAAuB,EAAA,UAAA,EAAY,yBAAyB,CAC5L,EAAA;AAAA,cAAsB,iBAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAWzC,GAAA,CAAA,gBAAA,EAAA,EAAiB,QAAU,EAAA,oBAAA,EAAsB,SAAS,mBACnC,EAAA,QAAA,kBAAA,GAAA,CAAC,kBAAmB,EAAA,EAAA,MAAA,EAAgB,yBAAyB,2BAA6B,EAAA,QAAA,EAAU,oBAAsB,EAAA,OAAA,EAAS,qBAAqB,CAC5J,EAAA;AAAA,8BACI,IAAA,CAAA,QAAA,EAAA,EAAA,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,yBAAsB,WAAa,EAAA,gBAAA,CAAiB,SAAS,QAAU,EAAA,gBAAA,CAAiB,QAAQ,MAAgB,EAAA,CAAA;AAAA,4BACjH,GAAA,CAAC,gBAAa,WAA0B,EAAA,SAAA,EAAsB,YAAwB,cAAgB,EAAA,IAAA,EAAM,mBAAqB,EAAA,IAAA,EAAM,aAA8B,EAAA;AAAA,WACzK,EAAA;AAAA,SACR,EAAA;AAAA,OACJ,EAAA;AAAA,KAAA,EACJ,CACJ,EAAA,CAAA;AAAA,IACC,gBAAmB,mBAAA,GAAA,CAAC,eAAgB,EAAA,EAAA,OAAA,EAAS,uBAAuB,KAAO,EAAA,eAAA,EAAiB,WAAa,EAAA,iBAAA,EAAmB,SAAS,iBAAmB,EAAA,WAAA,EAA0B,mBAA0C,EAAA,oBAAA,EAA4C,yBAAyB,MAAM,sBAAA,CAAuB,CAAQ,IAAA,KAAA,CAAC,IAAI,CAAA,EAAG,wBAA0B,EAAA,MAAM,wBAAwB,CAAQ,IAAA,KAAA,CAAC,IAAI,CAAA,EAAG,SAAS,qBAAuB,EAAA,MAAA,EAAQ,MAAQ,EAAA,OAAA,EAAS,uBAAuB,CAAK,GAAA,IAAA;AAAA,IAC3e;AAAA,GACL,EAAA,CAAA;AACR;AACA,MAAM,MAAA,GAAS,WAAW,MAAO,CAAA;AAAA,EAC/B,UAAY,EAAA;AAAA,IACV,IAAM,EAAA,CAAA;AAAA,IACN,KAAO,EAAA;AAAA,GACT;AAAA,EACA,cAAgB,EAAA;AAAA,IACd,IAAM,EAAA,CAAA;AAAA,IACN,SAAW,EAAA,CAAA;AAAA,IACX,KAAO,EAAA;AAAA,GACT;AAAA,EACA,UAAY,EAAA;AAAA,IACV,SAAW,EAAA,EAAA;AAAA,IACX,QAAU,EAAA,EAAA;AAAA,IACV,UAAY,EAAA;AAAA,GACd;AAAA,EACA,mBAAqB,EAAA;AAAA,IACnB,iBAAmB,EAAA,EAAA;AAAA,IACnB,UAAY,EAAA,EAAA;AAAA,IACZ,aAAe,EAAA;AAAA,GACjB;AAAA,EACA,gBAAkB,EAAA;AAAA,IAChB,YAAc,EAAA,CAAA;AAAA,IACd,iBAAmB,EAAA,EAAA;AAAA,IACnB,eAAiB,EAAA,CAAA;AAAA,IACjB,YAAc,EAAA,EAAA;AAAA,IACd,WAAa,EAAA;AAAA,GACf;AAAA,EACA,cAAgB,EAAA;AAAA,IACd,QAAU,EAAA,EAAA;AAAA,IACV,UAAY,EAAA;AAAA,GACd;AAAA,EACA,uBAAyB,EAAA;AAAA,IACvB,QAAU,EAAA,UAAA;AAAA,IACV,IAAM,EAAA,CAAA;AAAA,IACN,KAAO,EAAA,CAAA;AAAA,IACP,GAAK,EAAA,EAAA;AAAA,IACL,iBAAmB,EAAA,EAAA;AAAA,IACnB,MAAQ,EAAA;AAAA,GACV;AAAA,EACA,gBAAkB,EAAA;AAAA,IAChB,iBAAmB,EAAA,EAAA;AAAA,IACnB,UAAY,EAAA,EAAA;AAAA,IACZ,aAAe,EAAA;AAAA;AAEnB,CAAC,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../src/screens/Chat/index.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';\nimport { Keyboard, Pressable, StyleSheet, TextInput, View, useWindowDimensions } from 'react-native';\nimport { Box, InputToolBar, Text, getDefaultLeftItems } from '@admin-layout/gluestack-ui-mobile';\nimport { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';\nimport { useNavigation, useRoute, CommonActions } from '@react-navigation/native';\nimport { MessageActionBar } from '../../features/chat/MessageActionBar';\nimport { ChatTranscript } from '../../features/chat/ChatTranscript';\nimport { AskUserCard } from '../../features/chat/AskUserCard';\nimport { pendingAskUser } from '../../features/chat/askUser';\nimport { useCdecliConnection } from '../../contexts/CdecliConnectionContext';\nimport { useChatLiveStream } from '../../contexts/ChatLiveStreamContext';\nimport type { MessageAttachment } from '../../hooks/useChatStream';\nimport { KeyboardAwareScreen, KeyboardComposerDock } from '../../components/KeyboardComposerDock';\nimport { YantraBrandLoader, YANTRA_LOADER_SIZE_COMPACT } from '../../components/YantraBrandLoader';\nimport ThinkingIndicator from '../../components/ThinkingIndicator';\nimport { mobileTokens } from '../../theme/mobileTokens';\nimport { useAppTheme } from '../../theme/AppThemeProvider';\nimport DeepSearchModal from '../Home/components/DeepSearchModal';\nimport { extractDeepSearchSources, normalizeSummaryText, type DeepSearchSourceItem } from '../Home/deepSearchUtils';\nimport type { ModeSubmission, RoutingMode } from '../Home/types';\nimport { AudioRecorderPanel } from '../../features/audio-input/AudioRecorderPanel';\nimport { NativeDictationBar } from '../../features/audio-input/NativeDictationBar';\nimport { isNativeDictationAvailable } from '../../features/audio-input/nativeDictation';\nimport { useImageAttachments } from '../../features/attachments/useImageAttachments';\nimport { ComposerAttachmentBar } from '../../features/attachments/ComposerAttachmentBar';\nimport { useComposerAttachMenu } from '../../features/attachments/ComposerAttachMenu';\nimport { hasComposerAttachments, takeComposerAttachments } from '../../features/attachments/composerAttachmentHandoff';\nimport { MicErrorBoundary } from '../../features/audio-input/MicErrorBoundary';\nimport { requestMicPermission } from '../../features/audio-input/useAudioPermission';\nimport { shouldRedirectToShop } from '../../features/shopping/chat/homeShoppingIntent';\nimport { pushShopNative, replaceWithShopNative, shopHandoffParams } from '../../features/shopping/chat/openShopNative';\n\n/** Route params passed by Home (or by deep-link) when entering the Chat surface. */\nexport interface ChatScreenRouteParams {\n /** Channel id is required — the screen is keyed on the conversation. */\n channelId: string;\n /** Org slug used by gateway/account context. */\n orgName?: string | null;\n /**\n * First user message to auto-fire when the screen mounts. Home generates\n * this when the user submits from the empty composer so the assistant\n * starts streaming on Chat without an extra round trip.\n */\n initialQuery?: string | null;\n /**\n * Seed for the composer's mode (`'chat'` | `'deep-search'` | `'build'`).\n *\n * Set by `HomeScreen` to carry the user's last selection across the\n * navigation hop (\"research from home → research-shaped composer on chat\").\n * `ChatHistoryScreen` does NOT forward this — revisits always land on the\n * default `'chat'` mode, matching Home's defaults, and the user is free to\n * switch from there. Build-mode send hands off to Web Builder.\n *\n * After mount, this param is purely a seed: the screen keeps its own\n * `activeMode` so the user can toggle freely while inside the thread.\n */\n initialMode?: RoutingMode;\n /** Optional attachments forwarded with the initial message. */\n initialAttachments?: ModeSubmission['attachments'];\n}\n\nexport default function ChatScreen() {\n const navigation = useNavigation<any>();\n const route = useRoute<any>();\n\n const params = (route?.params ?? {}) as Partial<ChatScreenRouteParams>;\n const channelId = useMemo(() => {\n const raw = typeof params.channelId === 'string' ? params.channelId.trim() : '';\n return raw.length > 0 ? raw : null;\n }, [params.channelId]);\n const initialQuery = useMemo(() => {\n const raw = typeof params.initialQuery === 'string' ? params.initialQuery.trim() : '';\n return raw.length > 0 ? raw : null;\n }, [params.initialQuery]);\n const initialAttachments = params.initialAttachments;\n\n const { isDark } = useAppTheme();\n const { width: screenWidth } = useWindowDimensions();\n const contentMaxWidth = Math.min(720, Math.max(360, screenWidth - 24));\n\n const surfaceColor = isDark ? '#0f172a' : mobileTokens.color.surface;\n const secondaryTextColor = isDark ? '#94a3b8' : mobileTokens.color.textMuted;\n\n const insets = useSafeAreaInsets();\n\n /**\n * Gateway + chat stream routing.\n *\n * The CDeCLI lifecycle is owned app-wide by `CdecliConnectionProvider`,\n * so we just register this screen's channelId and read the shared status.\n * This avoids racing `gatewayConnect` mutations with the header's\n * `GatewayConnector` (which previously ran its own `useCdecliAutoConnect`\n * and could clobber the chat session binding).\n */\n const cdecli = useCdecliConnection();\n\n useLayoutEffect(() => {\n if (!channelId) return;\n cdecli.setActiveChannelId(channelId);\n }, [channelId, cdecli.setActiveChannelId]);\n\n const chat = useChatLiveStream();\n\n /**\n * Gifted-shaped transcript rows, memoised on `messages` alone.\n *\n * This screen re-renders on every streamed token — `useChatStream` does\n * `setResponse((r) => r + text)` per delta. Building this array inline in\n * the JSX therefore handed `PlanModeView` a fresh `messages` identity on\n * every token, which invalidated its own `useMemo([messages, …])`, re-mapped\n * every row, and re-rendered every bubble. Assistant bubbles render through\n * `react-native-markdown-display`, so that meant re-parsing the markdown of\n * the whole thread once per token — O(messages) parses per delta, on the JS\n * thread, which is exactly when the UI has to stay responsive.\n *\n * `messages` is already stable upstream (`useChatApi` memoises it on the\n * Apollo result), so this only rebuilds when a message is actually added or\n * changed.\n */\n const transcriptRows = useMemo(\n () => [\n ...chat.messages.map((msg, index) => ({\n id: `msg-${index}-${msg.role}`,\n role: msg.role,\n content: msg.content,\n attachments: msg.attachments,\n metadata: (msg as { metadata?: unknown }).metadata,\n })),\n // Turns typed while the gateway was down. They live in the outbox, not in\n // the thread, so they survive a backend hydration and an app restart - and\n // they are always the newest rows, so they belong at the end.\n ...chat.queuedMessages.map((queued) => ({\n id: `queued-${queued.id}`,\n role: 'user' as const,\n content: queued.text,\n attachments: queued.attachments,\n queued: true,\n })),\n ],\n [chat.messages, chat.queuedMessages],\n );\n const {\n messages,\n response,\n quickReply,\n error: chatError,\n isLoading,\n isStreaming,\n messagesLoading,\n queuedMessages,\n flushQueuedMessages,\n discardQueuedMessages,\n sendMessage,\n cancel,\n } = chat;\n\n /**\n * \"Try again\" on an assistant message: resend the nearest preceding user\n * prompt on the same channel. Row ids are `msg-<index>-<role>` (see\n * transcriptRows), so the transcript index comes straight out of the id.\n */\n const regenerateFrom = useCallback(\n (messageId: string) => {\n const match = /^msg-(\\d+)-assistant$/.exec(messageId);\n if (!match || !channelId) return;\n for (let i = Number(match[1]) - 1; i >= 0; i -= 1) {\n const prior = chat.messages[i];\n if (prior?.role === 'user' && (prior.content?.trim() || prior.attachments?.length)) {\n void chat.sendMessage(prior.content, prior.attachments, channelId);\n return;\n }\n }\n },\n [chat, channelId],\n );\n\n const chatBusy = isLoading || isStreaming;\n const renderMessageActions = useCallback(\n (message: { id: string; role: string; content: string }) =>\n message.role === 'assistant' ? (\n <MessageActionBar\n text={message.content}\n onRegenerate={chatBusy ? undefined : () => regenerateFrom(message.id)}\n />\n ) : null,\n [chatBusy, regenerateFrom],\n );\n\n const imageAttachments = useImageAttachments();\n const { rightItems, menu: attachMenu } = useComposerAttachMenu(imageAttachments, isDark);\n\n /*\n * Composer state — kept local so it doesn't leak across navigations.\n *\n * `valueRef` mirrors the controlled `value` so `handleSend` can read the\n * latest text without taking a dependency on it. Without this, `handleSend`\n * (and therefore `micSendButton.onSend`) would be recreated on every\n * keystroke, which is unnecessary churn for `InputToolBar`.\n *\n * `inputRef` is forwarded to InputToolBar's TextInput so we can call\n * `.clear()` imperatively after sending. On iOS, the native text buffer\n * can stay desynced from the controlled `value` for a frame when a state\n * update is followed by an async dispatch in the same gesture — the\n * \"send-and-clear\" race. The imperative clear closes that gap deterministically.\n */\n const [value, setValue] = useState('');\n const valueRef = useRef(value);\n valueRef.current = value;\n const inputRef = useRef<TextInput>(null);\n const trimmed = value.trim();\n const hasQuery = trimmed.length > 0;\n const hasAttachments = imageAttachments.pending.length > 0;\n const canSubmit = (hasQuery || hasAttachments) && Boolean(channelId);\n\n /**\n * Composer mode is FREELY user-controlled, mirroring `HomeScreen`.\n *\n * Seed:\n * • From Home (which forwards `initialMode` alongside `initialQuery`):\n * start in whatever mode the user picked there so the \"research from\n * home → research-shaped chat\" hand-off feels continuous.\n * • From ChatHistory (which intentionally does NOT pass `initialMode`):\n * default to `'chat'`, same as Home's empty composer.\n *\n * Post-mount this is a normal local state — the search/zap pills toggle\n * it on tap, and every place that previously branched on\n * `params.initialMode` now branches on `activeMode` instead.\n */\n const [activeMode, setActiveMode] = useState<RoutingMode>(() =>\n params.initialMode === 'deep-search' ? 'deep-search' : params.initialMode === 'build' ? 'build' : 'chat',\n );\n const handleModeSwitch = useCallback((mode: RoutingMode) => {\n setActiveMode(mode);\n }, []);\n const isDeepSearchMode = activeMode === 'deep-search';\n const isBuildMode = activeMode === 'build';\n\n /**\n * Inline voice recorder state — parity with HomeScreen and the web\n * `YantraSearchComposer.showAudioRecorder` flow. When active, the panel\n * fully replaces the InputToolBar so the toolbar's mic→send button\n * doesn't compete with the panel's own send/cancel affordances.\n */\n const [showAudioRecorder, setShowAudioRecorder] = useState(false);\n // Apple on-device dictation (the iOS-built-in mic) is preferred when the\n // binary has it; the Whisper recorder above is the fallback.\n const [showNativeDictation, setShowNativeDictation] = useState(false);\n const [voiceError, setVoiceError] = useState<string | null>(null);\n\n /**\n * Auto-fire the initial query once when the screen mounts on a fresh channel.\n * Home creates the channel optimistically and forwards the user prompt here so\n * the first assistant token arrives without an extra interaction.\n *\n * Wait for CDeCLI to finish connecting (or fail) before auto-firing — chat is\n * cdecli-only and `sendMessage` rejects sends while the gateway is disconnected.\n */\n const autoFiredRef = useRef(false);\n useEffect(() => {\n if (autoFiredRef.current) return;\n if (!channelId) return;\n if (cdecli.status !== 'connected' && cdecli.status !== 'error') return;\n const stashed = hasComposerAttachments(channelId) ? takeComposerAttachments(channelId) : undefined;\n // Route params carry the looser `ModeAttachment` (everything optional); the send\n // lane wants a `MessageAttachment`. Normalise once, here, rather than casting at\n // each use - `shopHandoffParams` takes the narrower shape just as happily.\n const attachments: MessageAttachment[] | undefined = stashed?.length\n ? stashed\n : initialAttachments?.map((a, index) => ({\n id: a.id ?? `att-${index}`,\n name: a.name ?? `attachment-${index + 1}`,\n type: a.type === 'screenshot' ? ('screenshot' as const) : ('file' as const),\n ...(a.mimeType ? { mimeType: a.mimeType } : {}),\n ...(a.size ? { size: a.size } : {}),\n ...((a.url ?? a.uri) ? { url: a.url ?? a.uri } : {}),\n ...(a.dataUrl ? { dataUrl: a.dataUrl } : {}),\n }));\n if (!initialQuery && !attachments?.length) return;\n\n autoFiredRef.current = true;\n if (isDeepSearchMode) {\n setIsDeepSearchModalOpen(true);\n }\n void (async () => {\n const mode: RoutingMode = isDeepSearchMode ? 'deep-search' : isBuildMode ? 'build' : 'chat';\n let openShop = false;\n try {\n openShop = await shouldRedirectToShop(initialQuery ?? '', attachments?.length ?? 0, mode);\n } catch {\n openShop = false;\n }\n if (openShop) {\n replaceWithShopNative(\n navigation,\n shopHandoffParams({\n orgName: params.orgName,\n query: initialQuery ?? '',\n files: attachments,\n }),\n );\n return;\n }\n void sendMessage(initialQuery ?? '', attachments, channelId);\n })();\n try {\n navigation.setParams({ initialQuery: null, initialAttachments: null });\n } catch {\n /* setParams may be unavailable in tests; clearing is best-effort. */\n }\n }, [\n channelId,\n initialQuery,\n initialAttachments,\n sendMessage,\n navigation,\n cdecli.status,\n isDeepSearchMode,\n isBuildMode,\n params.orgName,\n ]);\n\n /**\n * Answer a parked `[ASK_USER]` question. The formatted `**Title:** answer`\n * block goes out as an ordinary user message — cdecli absorbs it into the\n * turn it parked, which is what releases the run. Deliberately NOT routed\n * through handleSend: no deep-search/build-mode rewriting, no attachments,\n * and it must send even while the turn is still streaming.\n */\n const handleAskUserAnswer = useCallback(\n (formatted: string) => {\n const text = formatted.trim();\n if (!text) return;\n void sendMessage(text, undefined, channelId);\n },\n [sendMessage, channelId],\n );\n\n const [resolvedAskIds, setResolvedAskIds] = useState<Record<string, boolean>>({});\n const pendingAsk = useMemo(\n () =>\n pendingAskUser([\n ...transcriptRows,\n ...(response?.trim() ? [{ role: 'assistant', content: response }] : []),\n ]),\n [transcriptRows, response],\n );\n const showAskCard = Boolean(pendingAsk && !resolvedAskIds[pendingAsk.requestId]);\n\n const handleSend = useCallback(\n (text?: string) => {\n const pending = imageAttachments.forSend();\n const t = (text ?? valueRef.current).trim();\n if ((!t && !pending?.length) || !channelId) return;\n // Clear both ways: the controlled state (so React's model stays\n // consistent on next render) AND the native buffer via the ref\n // (so the visible text disappears on the same frame the user\n // pressed send, even before React flushes).\n setValue('');\n inputRef.current?.clear();\n\n // Build mode: hand off to Web Builder on this channel (web `/c/:id/webbuilder`).\n if (isBuildMode) {\n navigation.dispatch(\n CommonActions.navigate({\n name: 'MainStack.WebBuilder',\n params: {\n ...(params.orgName ? { orgName: params.orgName } : {}),\n channelId,\n message: t,\n },\n }),\n );\n return;\n }\n\n // Deep-search mode: every new query pops the structured research\n // modal back open (parity with the previous Home flow, where every\n // submit re-opened the DeepSearchModal). Without this, a user who\n // closed the modal once would see plain assistant turns for every\n // subsequent send. Reads the LOCAL `isDeepSearchMode` so the user\n // can toggle modes mid-thread and have sends behave accordingly.\n if (isDeepSearchMode) {\n setIsDeepSearchModalOpen(true);\n }\n void (async () => {\n let openShop = false;\n try {\n openShop = await shouldRedirectToShop(t, pending.length, isDeepSearchMode ? 'deep-search' : 'chat');\n } catch {\n openShop = false;\n }\n if (openShop) {\n const shopParams = shopHandoffParams({\n orgName: params.orgName,\n query: t,\n files: pending,\n });\n if (messages.length === 0) {\n replaceWithShopNative(navigation, shopParams);\n } else {\n pushShopNative(navigation, shopParams);\n }\n return;\n }\n void sendMessage(t, pending, channelId);\n })();\n imageAttachments.clear();\n },\n [\n channelId,\n sendMessage,\n isDeepSearchMode,\n isBuildMode,\n navigation,\n params.orgName,\n imageAttachments,\n messages.length,\n ],\n );\n\n const handleValueChange = useCallback((e: any) => {\n setValue(e?.nativeEvent?.text ?? '');\n }, []);\n\n /**\n * Mic affordance — web parity (`handleMicClick` in `YantraSearchComposer.tsx`).\n * The shared `MicSendButton` in `InputToolBar.tsx` only routes to `onMic`\n * when the input has no content, so we only need to handle the empty-input\n * case here.\n *\n * Permission is requested HERE, before the panel mounts. The previous flow\n * flipped `showAudioRecorder=true` immediately, which mounted\n * `AudioRecorderPanel` and constructed the native `AVAudioRecorder` via\n * `useAudioRecorder()` during the panel's render phase — before iOS had\n * granted mic access. On SDK 53 + `newArchEnabled: true` + Hermes that\n * ordering hard-crashes TestFlight builds even though it works in Expo Go\n * (Expo Go pre-caches permission). Asking up front means iOS shows the\n * system alert first and the panel only mounts (and therefore the native\n * recorder is only allocated) once permission is granted.\n *\n * `expo-audio` is lazy-imported so a missing/broken native module surfaces\n * as a user-visible banner instead of a synchronous TurboModule crash.\n */\n /** Open the record -> Whisper recorder (the fallback mic lane). */\n const openWhisperRecorder = useCallback(() => {\n setVoiceError(null);\n void (async () => {\n const { granted, error } = await requestMicPermission();\n if (!granted) {\n setVoiceError(error || 'Microphone is not available.');\n return;\n }\n setShowAudioRecorder(true);\n })();\n }, []);\n\n const handleMicPress = useCallback(() => {\n if (hasQuery) return;\n setVoiceError(null);\n Keyboard.dismiss();\n // Prefer Apple's on-device dictation (the iOS-built-in mic): it streams\n // text live and needs no upload. The bar requests its own permission\n // and calls back to the Whisper recorder if the native path is\n // unusable. Only reachable on a build that linked the native module.\n if (isNativeDictationAvailable()) {\n setShowNativeDictation(true);\n return;\n }\n openWhisperRecorder();\n }, [hasQuery, openWhisperRecorder]);\n\n /**\n * On a successful Whisper round-trip, paste the transcript into the input\n * field, close the recorder, and let the user review/send manually. Matches\n * web's `handleTranscriptionComplete`: we deliberately do NOT auto-fire\n * `sendMessage` because the user often wants to edit the dictation first.\n */\n /** Append a finished transcript to the composer and re-focus it. Shared by\n * the Whisper recorder and native dictation. */\n const pasteTranscript = useCallback((text: string) => {\n const cleaned = (text ?? '').trim();\n if (!cleaned) return;\n setValue((prev) => {\n const next = prev.trim().length > 0 ? `${prev.trim()} ${cleaned}` : cleaned;\n /*\n * Keep `inputRef`'s native text in sync with our controlled value\n * for the same reason `handleSend` does (the iOS native buffer\n * can lag the React state across a single gesture). `setNativeProps`\n * is the safe way to do this without re-rendering.\n */\n requestAnimationFrame(() => {\n inputRef.current?.setNativeProps?.({ text: next });\n inputRef.current?.focus?.();\n });\n return next;\n });\n }, []);\n\n const handleTranscriptionComplete = useCallback(\n (text: string) => {\n setShowAudioRecorder(false);\n pasteTranscript(text);\n },\n [pasteTranscript],\n );\n\n const handleRecorderCancel = useCallback(() => {\n setShowAudioRecorder(false);\n }, []);\n\n const handleDictationComplete = useCallback(\n (text: string) => {\n setShowNativeDictation(false);\n pasteTranscript(text);\n },\n [pasteTranscript],\n );\n\n const handleDictationCancel = useCallback(() => setShowNativeDictation(false), []);\n\n /** Native dictation could not run - drop to the Whisper recorder, which\n * requests its own permission and surfaces its own errors. */\n const handleDictationFallback = useCallback(() => {\n setShowNativeDictation(false);\n openWhisperRecorder();\n }, [openWhisperRecorder]);\n\n const handleRecorderError = useCallback((error: string) => {\n setVoiceError(error);\n }, []);\n\n const waitingForAssistant = useMemo(() => {\n if ((!isLoading && !isStreaming) || messages.length === 0) return false;\n if ((response ?? '').trim()) return false;\n return messages[messages.length - 1]?.role === 'user';\n }, [isLoading, isStreaming, messages, response]);\n\n /*\n * NOTE: the previous read-only `isDeepSearchChannel` derivation has been\n * removed. Mode is now a freely-toggleable local state (`activeMode`)\n * driven by tapping the search/zap pills, mirroring HomeScreen. All\n * downstream branches use `isDeepSearchMode` declared with the rest of\n * the composer state above.\n */\n\n /**\n * Deep-search overlay state.\n *\n * Restored from the previous Home-screen flow (commit history): a deep-search\n * channel renders the structured `DeepSearchModal` (query / live summary /\n * sources / research process) on top of the regular Chat conversation. Closing\n * the modal reveals the same channel's transcript underneath so the\n * user can still see the raw turns.\n *\n * Trigger semantics — IMPORTANT:\n * • Opens ONLY when a request is actually fired in this session:\n * - Home → Chat auto-fire of `initialQuery`\n * - User sends a new message in a deep-search channel from the composer\n * - Explicit retry from inside the modal\n * • Does NOT open on plain entry from ChatHistoryScreen. That's a revisit\n * of an already-completed thread — no new API call, nothing live to\n * stream, so popping the modal would just be a confusing flash. The\n * user still sees the deep-search rendering in the chat conversation\n * underneath, and any new send re-opens the live view.\n *\n * Local UI state (accordions, open flag) lives here; data state (query /\n * summary / sources) is DERIVED from the chat stream, not stored separately,\n * so there's no drift between the modal and the underlying conversation.\n */\n const [isDeepSearchModalOpen, setIsDeepSearchModalOpen] = useState(false);\n const [researchProcessOpen, setResearchProcessOpen] = useState(true);\n const [sourcesAccordionOpen, setSourcesAccordionOpen] = useState(true);\n\n /**\n * Modal data is always scoped to the CURRENT run, not the whole channel.\n * In a multi-turn deep-search channel each send is its own \"run\": one user\n * query → one assistant summary. If we sourced from \"first user message\" /\n * \"any assistant message\", a follow-up send would keep showing the previous\n * run's query and stale sources during the transient window between the new\n * user message being appended and the first stream token arriving.\n *\n * Cursor: the LAST user message in the thread. The assistant content for\n * the current run is either:\n * • `response` while the stream is filling (token-by-token), OR\n * • the assistant message that comes AFTER the last user (run complete), OR\n * • empty — covers the brief \"user message appended, no response yet\"\n * window so the modal flips to \"Running...\" with no stale body.\n */\n const lastUserIndex = useMemo(() => {\n for (let i = messages.length - 1; i >= 0; i -= 1) {\n if (messages[i]?.role === 'user') return i;\n }\n return -1;\n }, [messages]);\n\n const deepSearchQuery = useMemo(() => {\n if (lastUserIndex >= 0) {\n const txt = messages[lastUserIndex]?.content?.trim();\n if (txt) return txt;\n }\n return initialQuery ?? null;\n }, [messages, lastUserIndex, initialQuery]);\n\n const latestAssistantContent = useMemo(() => {\n if (response && response.trim()) return response;\n if (lastUserIndex < 0) return '';\n // Look for an assistant turn strictly AFTER the last user message.\n // Anything before it belongs to a prior run and must not leak into\n // the modal for the current one.\n for (let i = lastUserIndex + 1; i < messages.length; i += 1) {\n if (messages[i]?.role === 'assistant') {\n return messages[i]?.content ?? '';\n }\n }\n return '';\n }, [response, messages, lastUserIndex]);\n\n const deepSearchSummary = useMemo(() => normalizeSummaryText(latestAssistantContent), [latestAssistantContent]);\n const deepSearchSources: DeepSearchSourceItem[] = useMemo(\n () => extractDeepSearchSources(latestAssistantContent),\n [latestAssistantContent],\n );\n\n const handleDeepSearchClose = useCallback(() => {\n if (isStreaming) cancel();\n setIsDeepSearchModalOpen(false);\n }, [isStreaming, cancel]);\n\n const handleDeepSearchRetry = useCallback(() => {\n if (!deepSearchQuery || isStreaming || !channelId) return;\n void sendMessage(deepSearchQuery, undefined, channelId);\n }, [deepSearchQuery, isStreaming, channelId, sendMessage]);\n const leftItems = useMemo(\n () =>\n getDefaultLeftItems({\n search: { active: activeMode === 'chat', onClick: () => handleModeSwitch('chat') },\n zap: { active: activeMode === 'deep-search', onClick: () => handleModeSwitch('deep-search') },\n lightbulb: {\n active: activeMode === 'build',\n onClick: () => handleModeSwitch('build'),\n },\n }),\n [activeMode, handleModeSwitch],\n );\n const inputPlaceholder = isDeepSearchMode\n ? 'Research anything...'\n : isBuildMode\n ? 'Describe an app to build...'\n : 'Ask anything...';\n\n const inputConfig = useMemo(\n () => ({\n value,\n onChange: handleValueChange,\n placeholder: inputPlaceholder,\n disabled: !channelId,\n inputRef,\n returnKeyType: 'send',\n blurOnSubmit: false,\n onSubmitEditing: () => handleSend(),\n }),\n [value, handleValueChange, inputPlaceholder, channelId, handleSend],\n );\n const micSendButton = useMemo(\n () => ({\n hasContent: hasQuery || hasAttachments,\n onSend: () => handleSend(),\n onMic: handleMicPress,\n disabled: !channelId,\n // Empty + streaming → Stop. Typed follow-up → Send (steer). Never lock the composer.\n isLoading: canSubmit ? false : isLoading,\n onStop: isStreaming && !canSubmit ? cancel : undefined,\n steerable: isLoading || isStreaming,\n }),\n [canSubmit, handleSend, handleMicPress, isLoading, channelId, isStreaming, cancel, hasQuery, hasAttachments],\n );\n\n // Queue affordances: the gateway is live, so a queue that is still sitting there\n // failed its release and the user gets a manual retry beside the discard.\n const gatewayLive = cdecli.channelConnected;\n const queuedActionColor = isDark ? '#fbbf24' : '#b45309';\n const handleSendQueuedNow = useCallback(() => {\n void flushQueuedMessages();\n }, [flushQueuedMessages]);\n\n /**\n * Loader visibility:\n * - `isHydrating`: Apollo channel history is being fetched and we have nothing to show yet.\n * - `isWaitingForGateway`: Home navigated us here with an `initialQuery`, but CDeCLI is still\n * establishing the workspace session, so the auto-fire is deferred. Show a \"Connecting…\"\n * placeholder instead of an empty thread so the user doesn't think their send was dropped.\n */\n const isHydrating = messagesLoading && messages.length === 0 && !response && !quickReply && !isStreaming;\n const isWaitingForGateway =\n !autoFiredRef.current &&\n !!channelId &&\n (!!initialQuery || hasComposerAttachments(channelId)) &&\n cdecli.status !== 'connected' &&\n cdecli.status !== 'error';\n const showSendingLoader = waitingForAssistant;\n const pinSendingLoaderNearComposer = messages.length > 0 || Boolean((response ?? '').trim());\n\n return (\n <SafeAreaView edges={['left', 'right']} style={{ flex: 1, backgroundColor: surfaceColor }}>\n <KeyboardAwareScreen>\n <View style={styles.chatColumn}>\n {queuedMessages.length > 0 && (\n <Box\n mb=\"$2\"\n mx=\"$4\"\n p=\"$3\"\n borderRadius=\"$md\"\n style={{\n backgroundColor: isDark ? '#2a2113' : '#fffbeb',\n borderWidth: 1,\n borderColor: isDark ? '#78350f' : '#fde68a',\n flexDirection: 'row',\n alignItems: 'center',\n }}\n >\n <Text fontSize=\"$sm\" style={{ color: isDark ? '#fde68a' : '#92400e', flex: 1 }}>\n {queuedMessages.length === 1\n ? '1 message waiting for the gateway.'\n : `${queuedMessages.length} messages waiting for the gateway.`}{' '}\n {gatewayLive ? 'Sending…' : 'They send as soon as it connects.'}\n </Text>\n {gatewayLive ? (\n <Pressable onPress={handleSendQueuedNow} hitSlop={8} style={styles.queuedAction}>\n <Text\n fontSize=\"$xs\"\n style={[styles.queuedActionText, { color: queuedActionColor }]}\n >\n Send now\n </Text>\n </Pressable>\n ) : null}\n <Pressable onPress={discardQueuedMessages} hitSlop={8} style={styles.queuedAction}>\n <Text fontSize=\"$xs\" style={[styles.queuedActionText, { color: queuedActionColor }]}>\n Discard\n </Text>\n </Pressable>\n </Box>\n )}\n {(chatError || cdecli.error) && (\n <Box\n mb=\"$2\"\n mx=\"$4\"\n p=\"$3\"\n borderRadius=\"$md\"\n style={{\n backgroundColor: isDark ? '#2b1a1d' : '#fef2f2',\n borderWidth: 1,\n borderColor: isDark ? '#7f1d1d' : '#fecaca',\n }}\n >\n <Text fontSize=\"$sm\" style={{ color: isDark ? '#fecaca' : '#991b1b' }}>\n {chatError || cdecli.error}\n </Text>\n </Box>\n )}\n {!channelId ? (\n <Box flex={1} alignItems=\"center\" justifyContent=\"center\">\n <YantraBrandLoader size={YANTRA_LOADER_SIZE_COMPACT} />\n <Text style={[styles.statusText, { color: secondaryTextColor }]}>No channel selected</Text>\n </Box>\n ) : isHydrating ? (\n <Box flex={1} px=\"$4\" pt=\"$8\">\n <ThinkingIndicator\n variant=\"awaiting\"\n color={secondaryTextColor}\n isDark={isDark}\n label=\"Opening conversation…\"\n />\n </Box>\n ) : isWaitingForGateway ? (\n <Box flex={1} alignItems=\"center\" justifyContent=\"center\">\n <YantraBrandLoader size={YANTRA_LOADER_SIZE_COMPACT} />\n <Text style={[styles.statusText, { color: secondaryTextColor }]}>Connecting gateway…</Text>\n </Box>\n ) : (\n <View style={styles.transcriptPane}>\n <ChatTranscript\n messages={transcriptRows}\n streamingContent={response}\n listFooter={\n showSendingLoader && pinSendingLoaderNearComposer ? (\n <View style={styles.thinkingInThread}>\n <ThinkingIndicator\n variant=\"awaiting\"\n color={secondaryTextColor}\n isDark={isDark}\n />\n </View>\n ) : null\n }\n renderMessageActions={renderMessageActions}\n isDark={isDark}\n listContentStyle={{\n paddingTop: 12,\n paddingBottom: 8,\n margin: 0,\n width: contentMaxWidth,\n alignSelf: 'center',\n }}\n />\n </View>\n )}\n {showSendingLoader && !pinSendingLoaderNearComposer ? (\n <View pointerEvents=\"none\" style={styles.sendingLoaderEmptyState}>\n <ThinkingIndicator variant=\"awaiting\" color={secondaryTextColor} isDark={isDark} />\n </View>\n ) : null}\n <KeyboardComposerDock closedInset={insets.bottom} onSwipeUp={() => inputRef.current?.focus()}>\n {showAskCard && pendingAsk ? (\n <View style={{ width: contentMaxWidth, paddingHorizontal: 8 }}>\n <AskUserCard\n payload={pendingAsk}\n isDark={isDark}\n onAnswer={(formatted) => {\n setResolvedAskIds((prev) => ({ ...prev, [pendingAsk.requestId]: true }));\n handleAskUserAnswer(formatted);\n }}\n onDismiss={() =>\n setResolvedAskIds((prev) => ({ ...prev, [pendingAsk.requestId]: true }))\n }\n />\n </View>\n ) : null}\n <View style={[styles.bottomComposerInner, { width: contentMaxWidth }]}>\n {voiceError || imageAttachments.error ? (\n <View\n style={[\n styles.voiceErrorBanner,\n {\n backgroundColor: isDark ? '#2b1a1d' : '#fef2f2',\n borderColor: isDark ? '#7f1d1d' : '#fecaca',\n },\n ]}\n >\n <Text style={[styles.voiceErrorText, { color: isDark ? '#fecaca' : '#991b1b' }]}>\n {voiceError || imageAttachments.error}\n </Text>\n </View>\n ) : null}\n {showNativeDictation ? (\n // A throw inside the native recognizer degrades to\n // the Whisper recorder rather than killing the JS\n // thread (same guard rationale as the recorder).\n <MicErrorBoundary onCancel={handleDictationFallback} onError={handleDictationFallback}>\n <NativeDictationBar\n isDark={isDark}\n onInterim={() => undefined}\n onTranscriptionComplete={handleDictationComplete}\n onCancel={handleDictationCancel}\n onFallback={handleDictationFallback}\n />\n </MicErrorBoundary>\n ) : showAudioRecorder ? (\n /*\n * Render-time JS errors thrown by `useAudioRecorder`\n * (e.g. expo-audio native module missing, recorder\n * constructor throws in a release build) are caught\n * by the boundary and converted into a soft cancel\n * via the same handlers the panel itself uses on\n * runtime errors. Without this, a throw during the\n * panel's render phase kills the JS thread in\n * release / TestFlight.\n */\n <MicErrorBoundary onCancel={handleRecorderCancel} onError={handleRecorderError}>\n <AudioRecorderPanel\n isDark={isDark}\n onTranscriptionComplete={handleTranscriptionComplete}\n onCancel={handleRecorderCancel}\n onError={handleRecorderError}\n />\n </MicErrorBoundary>\n ) : (\n <>\n <ComposerAttachmentBar\n attachments={imageAttachments.pending}\n onRemove={imageAttachments.remove}\n isDark={isDark}\n />\n <InputToolBar\n inputConfig={inputConfig}\n leftItems={leftItems}\n rightItems={rightItems}\n templateButton={null}\n templateModalConfig={null}\n micSendButton={micSendButton}\n />\n </>\n )}\n </View>\n </KeyboardComposerDock>\n </View>\n </KeyboardAwareScreen>\n {isDeepSearchMode ? (\n <DeepSearchModal\n visible={isDeepSearchModalOpen}\n query={deepSearchQuery}\n summaryText={deepSearchSummary}\n sources={deepSearchSources}\n isStreaming={isStreaming}\n researchProcessOpen={researchProcessOpen}\n sourcesAccordionOpen={sourcesAccordionOpen}\n onToggleResearchProcess={() => setResearchProcessOpen((prev) => !prev)}\n onToggleSourcesAccordion={() => setSourcesAccordionOpen((prev) => !prev)}\n onRetry={handleDeepSearchRetry}\n onStop={cancel}\n onClose={handleDeepSearchClose}\n />\n ) : null}\n {attachMenu}\n </SafeAreaView>\n );\n}\n\nconst styles = StyleSheet.create({\n chatColumn: {\n flex: 1,\n width: '100%',\n },\n transcriptPane: {\n flex: 1,\n minHeight: 0,\n width: '100%',\n },\n statusText: {\n marginTop: 14,\n fontSize: 13,\n fontWeight: '500',\n },\n bottomComposerInner: {\n paddingHorizontal: 14,\n paddingTop: 10,\n paddingBottom: 0,\n },\n voiceErrorBanner: {\n marginBottom: 8,\n paddingHorizontal: 12,\n paddingVertical: 8,\n borderRadius: 10,\n borderWidth: 1,\n },\n voiceErrorText: {\n fontSize: 12,\n fontWeight: '500',\n },\n sendingLoaderEmptyState: {\n position: 'absolute',\n left: 0,\n right: 0,\n top: 24,\n paddingHorizontal: 20,\n zIndex: 40,\n },\n queuedAction: {\n paddingHorizontal: 8,\n paddingVertical: 2,\n },\n queuedActionText: {\n fontWeight: '600',\n },\n thinkingInThread: {\n paddingHorizontal: 12,\n paddingTop: 10,\n paddingBottom: 4,\n },\n});\n"],"names":["_a"],"mappings":"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"}
|
|
@@ -21,12 +21,14 @@ function CanvasPrewarm({
|
|
|
21
21
|
}, [canvasGraphqlUrl]);
|
|
22
22
|
useEffect(() => {
|
|
23
23
|
if (!indexUrl) return;
|
|
24
|
-
void hydrateCanvasBoardsSnapshot(indexUrl);
|
|
25
|
-
void preloadNativeCanvasExperience();
|
|
26
24
|
if (!crossBackendAuth && (!orgName || !projectId)) return;
|
|
27
25
|
let cancelled = false;
|
|
28
26
|
(async () => {
|
|
29
27
|
try {
|
|
28
|
+
const knownBoards = await hydrateCanvasBoardsSnapshot(indexUrl);
|
|
29
|
+
if (cancelled) return;
|
|
30
|
+
if (!(knownBoards == null ? void 0 : knownBoards.length)) return;
|
|
31
|
+
void preloadNativeCanvasExperience();
|
|
30
32
|
const session = await warmWebBuilderSession({
|
|
31
33
|
graphqlUrl: canvasGraphqlUrl,
|
|
32
34
|
orgName: crossBackendAuth ? void 0 : orgName,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CanvasPrewarm.js","sources":["../../../../src/screens/Home/components/CanvasPrewarm.tsx"],"sourcesContent":["/**\n * Mint the canvas surface JWT and prefetch boards while the user is on Home.\n * Opening Canvas Native then reads the in-memory board cache instead of\n * blocking on \"Loading your canvases…\".\n *\n * Receives org/project from Home (do NOT call usePrerequisiteIds here).\n */\nimport React, { useEffect, useMemo } from 'react';\nimport { config } from '../../../config/env-config';\nimport { useObtainSystemToken } from '../../../hooks/useObtainSystemToken';\nimport { resolveSurfaceAuthGraphqlUrl } from '../../../hooks/resolveSurfaceAuthGraphqlUrl';\nimport { warmWebBuilderSession } from '../../WebBuilder/webbuilderWarmCache';\nimport {\n fetchMyCanvasBoards,\n getCachedCanvasBoards,\n surfaceIndexUrl,\n surfaceIndexUrlFromTemplate,\n} from '../../../features/canvas/surfaceIndex';\nimport { hydrateCanvasBoardsSnapshot } from '../../../features/canvas/canvasBoardsSnapshot';\nimport { preloadNativeCanvasExperience } from '../../../features/canvas/nativeViewerRegistry';\n\ntype Props = {\n orgName?: string | null;\n projectId?: string | null;\n tagId?: string | null;\n};\n\nfunction backendsEqual(a: string, b: string): boolean {\n return a.trim().replace(/\\/$/, '') === b.trim().replace(/\\/$/, '');\n}\n\nexport function CanvasPrewarm({ orgName, projectId, tagId }: Props): null {\n const canvasGraphqlUrl = useMemo(\n () =>\n resolveSurfaceAuthGraphqlUrl(config.CANVAS_SURFACE_URL, config.GRAPHQL_URL, {\n CANVAS_SURFACE_URL: config.CANVAS_SURFACE_URL,\n CANVAS_BACKEND_GRAPHQL_URL: config.CANVAS_BACKEND_GRAPHQL_URL,\n }),\n [],\n );\n const crossBackendAuth = !backendsEqual(canvasGraphqlUrl, config.GRAPHQL_URL);\n const { obtainSystemToken } = useObtainSystemToken(!crossBackendAuth);\n const indexUrl = useMemo(() => {\n const derived = surfaceIndexUrl(canvasGraphqlUrl, config.SURFACE_INDEX_URL);\n if (derived) return derived;\n return surfaceIndexUrlFromTemplate(config.SURFACE_URL_TEMPLATE);\n }, [canvasGraphqlUrl]);\n\n useEffect(() => {\n if (!indexUrl) return;\n
|
|
1
|
+
{"version":3,"file":"CanvasPrewarm.js","sources":["../../../../src/screens/Home/components/CanvasPrewarm.tsx"],"sourcesContent":["/**\n * Mint the canvas surface JWT and prefetch boards while the user is on Home.\n * Opening Canvas Native then reads the in-memory board cache instead of\n * blocking on \"Loading your canvases…\".\n *\n * Receives org/project from Home (do NOT call usePrerequisiteIds here).\n */\nimport React, { useEffect, useMemo } from 'react';\nimport { config } from '../../../config/env-config';\nimport { useObtainSystemToken } from '../../../hooks/useObtainSystemToken';\nimport { resolveSurfaceAuthGraphqlUrl } from '../../../hooks/resolveSurfaceAuthGraphqlUrl';\nimport { warmWebBuilderSession } from '../../WebBuilder/webbuilderWarmCache';\nimport {\n fetchMyCanvasBoards,\n getCachedCanvasBoards,\n surfaceIndexUrl,\n surfaceIndexUrlFromTemplate,\n} from '../../../features/canvas/surfaceIndex';\nimport { hydrateCanvasBoardsSnapshot } from '../../../features/canvas/canvasBoardsSnapshot';\nimport { preloadNativeCanvasExperience } from '../../../features/canvas/nativeViewerRegistry';\n\ntype Props = {\n orgName?: string | null;\n projectId?: string | null;\n tagId?: string | null;\n};\n\nfunction backendsEqual(a: string, b: string): boolean {\n return a.trim().replace(/\\/$/, '') === b.trim().replace(/\\/$/, '');\n}\n\nexport function CanvasPrewarm({ orgName, projectId, tagId }: Props): null {\n const canvasGraphqlUrl = useMemo(\n () =>\n resolveSurfaceAuthGraphqlUrl(config.CANVAS_SURFACE_URL, config.GRAPHQL_URL, {\n CANVAS_SURFACE_URL: config.CANVAS_SURFACE_URL,\n CANVAS_BACKEND_GRAPHQL_URL: config.CANVAS_BACKEND_GRAPHQL_URL,\n }),\n [],\n );\n const crossBackendAuth = !backendsEqual(canvasGraphqlUrl, config.GRAPHQL_URL);\n const { obtainSystemToken } = useObtainSystemToken(!crossBackendAuth);\n const indexUrl = useMemo(() => {\n const derived = surfaceIndexUrl(canvasGraphqlUrl, config.SURFACE_INDEX_URL);\n if (derived) return derived;\n return surfaceIndexUrlFromTemplate(config.SURFACE_URL_TEMPLATE);\n }, [canvasGraphqlUrl]);\n\n useEffect(() => {\n if (!indexUrl) return;\n if (!crossBackendAuth && (!orgName || !projectId)) return;\n\n let cancelled = false;\n (async () => {\n try {\n /**\n * Prewarming used to be unconditional, so EVERY session - including\n * someone who only ever sends a chat message - downloaded and\n * evaluated the canvas remote and fetched all of their boards into\n * memory. On a 6GB iPhone that is part of why iOS evicts the whole\n * app seconds after backgrounding.\n *\n * A persisted boards snapshot is the evidence that this user\n * actually uses canvas; only then is prewarming worth the resident\n * memory. Canvas users keep the fast path, everyone else stops\n * paying for it. Cost for the rest is one AsyncStorage read.\n */\n const knownBoards = await hydrateCanvasBoardsSnapshot(indexUrl);\n if (cancelled) return;\n if (!knownBoards?.length) return;\n\n void preloadNativeCanvasExperience();\n\n const session = await warmWebBuilderSession({\n graphqlUrl: canvasGraphqlUrl,\n orgName: crossBackendAuth ? undefined : orgName,\n projectId: crossBackendAuth ? undefined : projectId,\n tagId: crossBackendAuth ? undefined : tagId,\n obtainSystemToken: crossBackendAuth ? undefined : obtainSystemToken,\n discoverScope: crossBackendAuth,\n });\n if (cancelled) return;\n if (getCachedCanvasBoards(indexUrl)) return;\n await fetchMyCanvasBoards({ url: indexUrl, token: session.token });\n if (cancelled) return;\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.log('[CanvasPrewarm] boards ready');\n }\n } catch (err) {\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.warn('[CanvasPrewarm] warm failed:', err);\n }\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [canvasGraphqlUrl, crossBackendAuth, indexUrl, obtainSystemToken, orgName, projectId, tagId]);\n\n return null;\n}\n"],"names":[],"mappings":"8oBAoBA,SAAS,aAAA,CAAc,GAAW,CAAoB,EAAA;AACpD,EAAA,OAAO,CAAE,CAAA,IAAA,EAAO,CAAA,OAAA,CAAQ,KAAO,EAAA,EAAE,CAAM,KAAA,CAAA,CAAE,IAAK,EAAA,CAAE,OAAQ,CAAA,KAAA,EAAO,EAAE,CAAA;AACnE;AACO,SAAS,aAAc,CAAA;AAAA,EAC5B,OAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAgB,EAAA;AACd,EAAA,MAAM,mBAAmB,OAAQ,CAAA,MAAM,6BAA6B,MAAO,CAAA,kBAAA,EAAoB,OAAO,WAAa,EAAA;AAAA,IACjH,oBAAoB,MAAO,CAAA,kBAAA;AAAA,IAC3B,4BAA4B,MAAO,CAAA;AAAA,GACpC,CAAG,EAAA,EAAE,CAAA;AACN,EAAA,MAAM,gBAAmB,GAAA,CAAC,aAAc,CAAA,gBAAA,EAAkB,OAAO,WAAW,CAAA;AAC5E,EAAM,MAAA;AAAA,IACJ;AAAA,GACF,GAAI,oBAAqB,CAAA,CAAC,gBAAgB,CAAA;AAC1C,EAAM,MAAA,QAAA,GAAW,QAAQ,MAAM;AAC7B,IAAA,MAAM,OAAU,GAAA,eAAA,CAAgB,gBAAkB,EAAA,MAAA,CAAO,iBAAiB,CAAA;AAC1E,IAAA,IAAI,SAAgB,OAAA,OAAA;AACpB,IAAO,OAAA,2BAAA,CAA4B,OAAO,oBAAoB,CAAA;AAAA,GAChE,EAAG,CAAC,gBAAgB,CAAC,CAAA;AACrB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,QAAU,EAAA;AACf,IAAA,IAAI,CAAC,gBAAA,KAAqB,CAAC,OAAA,IAAW,CAAC,SAAY,CAAA,EAAA;AACnD,IAAA,IAAI,SAAY,GAAA,KAAA;AAChB,IAAA,CAAC,YAAY;AACX,MAAI,IAAA;AAaF,QAAM,MAAA,WAAA,GAAc,MAAM,2BAAA,CAA4B,QAAQ,CAAA;AAC9D,QAAA,IAAI,SAAW,EAAA;AACf,QAAI,IAAA,EAAC,2CAAa,MAAQ,CAAA,EAAA;AAC1B,QAAA,KAAK,6BAA8B,EAAA;AACnC,QAAM,MAAA,OAAA,GAAU,MAAM,qBAAsB,CAAA;AAAA,UAC1C,UAAY,EAAA,gBAAA;AAAA,UACZ,OAAA,EAAS,mBAAmB,KAAY,CAAA,GAAA,OAAA;AAAA,UACxC,SAAA,EAAW,mBAAmB,KAAY,CAAA,GAAA,SAAA;AAAA,UAC1C,KAAA,EAAO,mBAAmB,KAAY,CAAA,GAAA,KAAA;AAAA,UACtC,iBAAA,EAAmB,mBAAmB,KAAY,CAAA,GAAA,iBAAA;AAAA,UAClD,aAAe,EAAA;AAAA,SAChB,CAAA;AACD,QAAA,IAAI,SAAW,EAAA;AACf,QAAI,IAAA,qBAAA,CAAsB,QAAQ,CAAG,EAAA;AACrC,QAAA,MAAM,mBAAoB,CAAA;AAAA,UACxB,GAAK,EAAA,QAAA;AAAA,UACL,OAAO,OAAQ,CAAA;AAAA,SAChB,CAAA;AACD,QAAA,IAAI,SAAW,EAAA;AACf,QAAA,IAAI,OAAS,EAAA;AAEX,UAAA,OAAA,CAAQ,IAAI,8BAA8B,CAAA;AAAA;AAC5C,eACO,GAAK,EAAA;AACZ,QAAA,IAAI,OAAS,EAAA;AAEX,UAAQ,OAAA,CAAA,IAAA,CAAK,gCAAgC,GAAG,CAAA;AAAA;AAClD;AACF,KACC,GAAA;AACH,IAAA,OAAO,MAAM;AACX,MAAY,SAAA,GAAA,IAAA;AAAA,KACd;AAAA,GACF,EAAG,CAAC,gBAAkB,EAAA,gBAAA,EAAkB,UAAU,iBAAmB,EAAA,OAAA,EAAS,SAAW,EAAA,KAAK,CAAC,CAAA;AAC/F,EAAO,OAAA,IAAA;AACT"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
3
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __spreadValues = (a, b) => {
|
|
9
|
+
for (var prop in b || (b = {}))
|
|
10
|
+
if (__hasOwnProp.call(b, prop))
|
|
11
|
+
__defNormalProp(a, prop, b[prop]);
|
|
12
|
+
if (__getOwnPropSymbols)
|
|
13
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
+
if (__propIsEnum.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
}
|
|
17
|
+
return a;
|
|
18
|
+
};
|
|
19
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
+
var _a;
|
|
21
|
+
const MAX_QUEUED_MESSAGES = 20;
|
|
22
|
+
const TTL_MS = 60 * 60 * 1e3;
|
|
23
|
+
const STORAGE_KEY = "yantra.queuedGatewayMessages";
|
|
24
|
+
let storage = null;
|
|
25
|
+
try {
|
|
26
|
+
storage = (_a = require("@react-native-async-storage/async-storage").default) != null ? _a : null;
|
|
27
|
+
} catch (e) {
|
|
28
|
+
storage = null;
|
|
29
|
+
}
|
|
30
|
+
let queue = [];
|
|
31
|
+
let seq = 0;
|
|
32
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
33
|
+
const EMPTY = Object.freeze([]);
|
|
34
|
+
let snapshots = /* @__PURE__ */ new Map();
|
|
35
|
+
function isFresh(entry) {
|
|
36
|
+
return Date.now() - entry.queuedAt < TTL_MS;
|
|
37
|
+
}
|
|
38
|
+
function persist() {
|
|
39
|
+
if (!storage) return;
|
|
40
|
+
const durable = queue.filter((entry) => {
|
|
41
|
+
var _a2;
|
|
42
|
+
return !((_a2 = entry.attachments) == null ? void 0 : _a2.length);
|
|
43
|
+
});
|
|
44
|
+
void storage.setItem(STORAGE_KEY, JSON.stringify(durable)).catch(() => {
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function changed() {
|
|
48
|
+
snapshots = /* @__PURE__ */ new Map();
|
|
49
|
+
listeners.forEach((fn) => fn());
|
|
50
|
+
}
|
|
51
|
+
function prune() {
|
|
52
|
+
const kept = queue.filter(isFresh);
|
|
53
|
+
if (kept.length !== queue.length) {
|
|
54
|
+
queue = kept;
|
|
55
|
+
persist();
|
|
56
|
+
changed();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
let restoring = null;
|
|
60
|
+
function restoreQueuedGatewayMessages() {
|
|
61
|
+
if (!storage) return Promise.resolve();
|
|
62
|
+
if (!restoring) {
|
|
63
|
+
restoring = storage.getItem(STORAGE_KEY).then((raw) => {
|
|
64
|
+
if (!raw) return;
|
|
65
|
+
const rows = JSON.parse(raw);
|
|
66
|
+
const restored = (Array.isArray(rows) ? rows : []).filter((row) => (row == null ? void 0 : row.id) && (row == null ? void 0 : row.channelId) && (row == null ? void 0 : row.text) && isFresh(row));
|
|
67
|
+
if (!restored.length) return;
|
|
68
|
+
queue = [...restored, ...queue].sort((a, b) => a.queuedAt - b.queuedAt);
|
|
69
|
+
changed();
|
|
70
|
+
}).catch(() => {
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return restoring;
|
|
74
|
+
}
|
|
75
|
+
function subscribeQueuedGatewayMessages(listener) {
|
|
76
|
+
listeners.add(listener);
|
|
77
|
+
return () => {
|
|
78
|
+
listeners.delete(listener);
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function queuedGatewayMessages(channelId, accountId) {
|
|
82
|
+
if (!channelId) return EMPTY;
|
|
83
|
+
const key = `${channelId}::${accountId != null ? accountId : ""}`;
|
|
84
|
+
const cached = snapshots.get(key);
|
|
85
|
+
if (cached) return cached;
|
|
86
|
+
const rows = queue.filter((entry) => entry.channelId === channelId && (!accountId || entry.accountId === accountId) && isFresh(entry));
|
|
87
|
+
const snapshot = rows.length ? rows : EMPTY;
|
|
88
|
+
snapshots.set(key, snapshot);
|
|
89
|
+
return snapshot;
|
|
90
|
+
}
|
|
91
|
+
function queuedGatewayMessageCount(channelId, accountId) {
|
|
92
|
+
return queuedGatewayMessages(channelId, accountId).length;
|
|
93
|
+
}
|
|
94
|
+
function queueGatewayMessage(entry) {
|
|
95
|
+
var _a2, _b;
|
|
96
|
+
prune();
|
|
97
|
+
const depth = queuedGatewayMessageCount(entry.channelId, entry.accountId);
|
|
98
|
+
if (!entry.channelId || !entry.text.trim() && !((_a2 = entry.attachments) == null ? void 0 : _a2.length)) {
|
|
99
|
+
return {
|
|
100
|
+
ok: false,
|
|
101
|
+
reason: "empty",
|
|
102
|
+
depth
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if (depth >= MAX_QUEUED_MESSAGES) {
|
|
106
|
+
return {
|
|
107
|
+
ok: false,
|
|
108
|
+
reason: "full",
|
|
109
|
+
depth
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
seq += 1;
|
|
113
|
+
const queued = __spreadProps(__spreadValues({
|
|
114
|
+
id: `q${Date.now().toString(36)}${seq.toString(36)}`,
|
|
115
|
+
channelId: entry.channelId,
|
|
116
|
+
accountId: entry.accountId,
|
|
117
|
+
text: entry.text
|
|
118
|
+
}, ((_b = entry.attachments) == null ? void 0 : _b.length) ? {
|
|
119
|
+
attachments: entry.attachments
|
|
120
|
+
} : {}), {
|
|
121
|
+
queuedAt: Date.now()
|
|
122
|
+
});
|
|
123
|
+
queue = [...queue, queued];
|
|
124
|
+
persist();
|
|
125
|
+
changed();
|
|
126
|
+
return {
|
|
127
|
+
ok: true,
|
|
128
|
+
entry: queued,
|
|
129
|
+
depth: depth + 1
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function dropQueuedGatewayMessage(id) {
|
|
133
|
+
const next = queue.filter((entry) => entry.id !== id);
|
|
134
|
+
if (next.length === queue.length) return;
|
|
135
|
+
queue = next;
|
|
136
|
+
persist();
|
|
137
|
+
changed();
|
|
138
|
+
}
|
|
139
|
+
function clearQueuedGatewayMessages(channelId) {
|
|
140
|
+
const next = channelId ? queue.filter((entry) => entry.channelId !== channelId) : [];
|
|
141
|
+
if (next.length === queue.length) return;
|
|
142
|
+
queue = next;
|
|
143
|
+
persist();
|
|
144
|
+
changed();
|
|
145
|
+
}export{MAX_QUEUED_MESSAGES,clearQueuedGatewayMessages,dropQueuedGatewayMessage,queueGatewayMessage,queuedGatewayMessageCount,queuedGatewayMessages,restoreQueuedGatewayMessages,subscribeQueuedGatewayMessages};//# sourceMappingURL=queuedGatewayMessages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queuedGatewayMessages.js","sources":["../../src/services/queuedGatewayMessages.ts"],"sourcesContent":["/**\n * queuedGatewayMessages - the outbox for turns typed while the CDeCLI gateway is down.\n *\n * Sending with the gateway disconnected used to erase the message: the optimistic\n * bubble was popped off the thread and all the user kept was a red banner\n * (\"CDeCLI is not connected yet\"). Everything typed during a reconnect - which on\n * mobile happens on every cold start, every return from background and every\n * network switch - was lost.\n *\n * So a refused send is remembered instead of dropped. The entry stays visible in\n * the thread as a queued bubble, and the chat stream releases the queue in FIFO\n * order the moment the channel reports connected.\n *\n * Kept in sync with `packages-modules/account/browser/src/services/queuedGatewayMessages.ts`;\n * the two differ only in their storage adapter (AsyncStorage here, sessionStorage\n * on web). This file is a dependency-free leaf so it unit-tests without dragging\n * in the Apollo / React Native import chain.\n */\n\n/** Per channel. Deep enough for a long reconnect, shallow enough that a release is still a conversation. */\nexport const MAX_QUEUED_MESSAGES = 20;\n\n/** Past this the turn is stale - the user has moved on and would not expect it to fire. */\nconst TTL_MS = 60 * 60 * 1000;\n\nexport interface QueuedMessageAttachment {\n id: string;\n name: string;\n type: 'file' | 'screenshot';\n dataUrl?: string;\n mimeType?: string;\n size?: number;\n url?: string;\n}\n\nexport interface QueuedGatewayMessage {\n id: string;\n channelId: string;\n /** Whose queue this is. A flush only releases entries for the signed-in account. */\n accountId: string;\n text: string;\n attachments?: QueuedMessageAttachment[];\n queuedAt: number;\n}\n\n// `entry?: undefined` / `reason?: undefined` on the opposite arms keep both fields\n// reachable on the union: with strictNullChecks off (repo-wide) TS does not narrow a\n// discriminated union after an `if (!result.ok)` guard.\nexport type QueueMessageResult =\n | { ok: true; entry: QueuedGatewayMessage; reason?: undefined; depth: number }\n | { ok: false; entry?: undefined; reason: 'empty' | 'full'; depth: number };\n\ntype StorageLike = {\n getItem(key: string): Promise<string | null>;\n setItem(key: string, value: string): Promise<void>;\n};\n\nconst STORAGE_KEY = 'yantra.queuedGatewayMessages';\n\nlet storage: StorageLike | null = null;\ntry {\n // eslint-disable-next-line @typescript-eslint/no-var-requires, global-require\n storage = require('@react-native-async-storage/async-storage').default ?? null;\n} catch {\n // Tests and any build without the native module keep an in-memory queue,\n // which is what the live-reconnect case needs anyway.\n storage = null;\n}\n\nlet queue: QueuedGatewayMessage[] = [];\nlet seq = 0;\n\nconst listeners = new Set<() => void>();\n/** `useSyncExternalStore` compares snapshots by identity - one frozen empty array, and per-channel arrays cached until the queue changes. */\nconst EMPTY: readonly QueuedGatewayMessage[] = Object.freeze([]);\nlet snapshots = new Map<string, readonly QueuedGatewayMessage[]>();\n\nfunction isFresh(entry: QueuedGatewayMessage): boolean {\n return Date.now() - entry.queuedAt < TTL_MS;\n}\n\nfunction persist(): void {\n if (!storage) return;\n // Attachments carry base64 data URLs (and short-lived upload URLs); they stay\n // in memory only. An entry that cannot be replayed faithfully is not written.\n const durable = queue.filter((entry) => !entry.attachments?.length);\n void storage.setItem(STORAGE_KEY, JSON.stringify(durable)).catch(() => {\n // Storage full or unavailable - the in-memory queue still covers the\n // reconnect this exists for.\n });\n}\n\nfunction changed(): void {\n snapshots = new Map();\n listeners.forEach((fn) => fn());\n}\n\nfunction prune(): void {\n const kept = queue.filter(isFresh);\n if (kept.length !== queue.length) {\n queue = kept;\n persist();\n changed();\n }\n}\n\nlet restoring: Promise<void> | null = null;\n\n/**\n * Rehydrate once per process. Mobile is killed aggressively in the background, so\n * a queue that only lived in memory would lose exactly the messages typed just\n * before the app went away.\n */\nexport function restoreQueuedGatewayMessages(): Promise<void> {\n if (!storage) return Promise.resolve();\n if (!restoring) {\n restoring = storage\n .getItem(STORAGE_KEY)\n .then((raw) => {\n if (!raw) return;\n const rows: QueuedGatewayMessage[] = JSON.parse(raw);\n const restored = (Array.isArray(rows) ? rows : []).filter(\n (row) => row?.id && row?.channelId && row?.text && isFresh(row),\n );\n if (!restored.length) return;\n // Anything queued in this process wins its slot; restored rows sort in by time.\n queue = [...restored, ...queue].sort((a, b) => a.queuedAt - b.queuedAt);\n changed();\n })\n .catch(() => {\n // Corrupt payload - start clean rather than throw on import.\n });\n }\n return restoring;\n}\n\nexport function subscribeQueuedGatewayMessages(listener: () => void): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\n/** Queued turns for one channel, oldest first. The array identity is stable until the queue changes. */\nexport function queuedGatewayMessages(\n channelId: string | null | undefined,\n accountId?: string | null,\n): readonly QueuedGatewayMessage[] {\n if (!channelId) return EMPTY;\n const key = `${channelId}::${accountId ?? ''}`;\n const cached = snapshots.get(key);\n if (cached) return cached;\n const rows = queue.filter(\n (entry) => entry.channelId === channelId && (!accountId || entry.accountId === accountId) && isFresh(entry),\n );\n const snapshot = rows.length ? rows : EMPTY;\n snapshots.set(key, snapshot);\n return snapshot;\n}\n\nexport function queuedGatewayMessageCount(channelId: string | null | undefined, accountId?: string | null): number {\n return queuedGatewayMessages(channelId, accountId).length;\n}\n\n/**\n * Remember a turn the gateway refused. Returns `full` rather than dropping the\n * oldest: silently eating the message the user just typed is the bug this file\n * exists to fix, and evicting the one they typed first is no better.\n */\nexport function queueGatewayMessage(entry: {\n channelId: string;\n accountId: string;\n text: string;\n attachments?: QueuedMessageAttachment[];\n}): QueueMessageResult {\n prune();\n const depth = queuedGatewayMessageCount(entry.channelId, entry.accountId);\n if (!entry.channelId || (!entry.text.trim() && !entry.attachments?.length)) {\n return { ok: false, reason: 'empty', depth };\n }\n if (depth >= MAX_QUEUED_MESSAGES) {\n return { ok: false, reason: 'full', depth };\n }\n seq += 1;\n const queued: QueuedGatewayMessage = {\n id: `q${Date.now().toString(36)}${seq.toString(36)}`,\n channelId: entry.channelId,\n accountId: entry.accountId,\n text: entry.text,\n ...(entry.attachments?.length ? { attachments: entry.attachments } : {}),\n queuedAt: Date.now(),\n };\n queue = [...queue, queued];\n persist();\n changed();\n return { ok: true, entry: queued, depth: depth + 1 };\n}\n\nexport function dropQueuedGatewayMessage(id: string): void {\n const next = queue.filter((entry) => entry.id !== id);\n if (next.length === queue.length) return;\n queue = next;\n persist();\n changed();\n}\n\n/** Drop a channel's queue (the user chose not to send), or everything on sign-out. */\nexport function clearQueuedGatewayMessages(channelId?: string | null): void {\n const next = channelId ? queue.filter((entry) => entry.channelId !== channelId) : [];\n if (next.length === queue.length) return;\n queue = next;\n persist();\n changed();\n}\n"],"names":["_a"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adminide-stack/yantra-mobile",
|
|
3
|
-
"version": "12.0.64-alpha.
|
|
3
|
+
"version": "12.0.64-alpha.3",
|
|
4
4
|
"description": "Sample core for higher packages to depend on",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"author": "CDMBase LLC",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"uuid": "^9.0.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"common": "12.0.64-alpha.
|
|
28
|
+
"common": "12.0.64-alpha.3"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"@admin-layout/gluestack-ui-mobile": "*",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@messenger-box/platform-mobile": "*",
|
|
42
42
|
"@react-native-async-storage/async-storage": "*",
|
|
43
43
|
"@react-navigation/native": "*",
|
|
44
|
-
"common": "12.0.64-alpha.
|
|
44
|
+
"common": "12.0.64-alpha.2",
|
|
45
45
|
"expo-constants": "*",
|
|
46
46
|
"expo-secure-store": "*",
|
|
47
47
|
"lodash": "*",
|
|
@@ -57,5 +57,5 @@
|
|
|
57
57
|
"typescript": {
|
|
58
58
|
"definition": "lib/index.d.ts"
|
|
59
59
|
},
|
|
60
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "2466ea826d831bdae73023560305d3a7c3ba8e7f"
|
|
61
61
|
}
|