@guideai/react-native 0.0.0-stage → 0.1.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.mjs","sources":["../src/ui/markdown-parser.ts","../src/ui/Markdown.tsx","../src/ui/theme.ts","../src/ui/GuideAIChat.tsx","../src/ui/GuideAISupportChat.tsx","../src/ui/GuideAILauncher.tsx"],"sourcesContent":["// ============================================================\n// @guideai/react-native — markdown parser\n// ============================================================\n//\n// The assistant answers in markdown. Rendered as plain text it reads as\n// literal `**bold**` and `- ` noise, which is what a user sees today.\n//\n// This parses the subset the assistant actually emits — headings, bold,\n// italic, inline code, bullet and numbered lists, fenced code, block\n// quotes, horizontal rules and tables — into a flat block list that a\n// React Native view can render with <Text> and <View>.\n//\n// Deliberately no dependency: react-native-markdown-display and friends\n// pull in a full CommonMark stack for a chat bubble, and several are\n// unmaintained against RN 0.81 / React 19. This is a parser, not a\n// renderer, so it stays testable in plain Node with no device.\n// ============================================================\n\n/** One run of text with the marks that apply to it. */\nexport interface InlineSpan {\n text: string;\n bold?: boolean;\n italic?: boolean;\n code?: boolean;\n /** Present when this span is a link; `text` is the label. */\n href?: string;\n}\n\nexport type MarkdownBlock =\n | { type: 'heading'; level: 1 | 2 | 3; spans: InlineSpan[] }\n | { type: 'paragraph'; spans: InlineSpan[] }\n | { type: 'bullet'; spans: InlineSpan[]; depth: number }\n | { type: 'ordered'; spans: InlineSpan[]; index: number; depth: number }\n | { type: 'code'; text: string; language?: string }\n | { type: 'quote'; spans: InlineSpan[] }\n | { type: 'rule' }\n | { type: 'table'; header: InlineSpan[][]; rows: InlineSpan[][][] };\n\n/**\n * Parse inline marks within one line.\n *\n * Order matters: code spans are taken first and their contents left\n * untouched, so `**not bold**` inside backticks stays literal.\n */\nexport function parseInline(text: string): InlineSpan[] {\n const spans: InlineSpan[] = [];\n // Code, bold, italic, link — matched in one pass so nesting cannot\n // produce overlapping ranges.\n const pattern = /(`[^`]+`)|(\\*\\*[^*]+\\*\\*)|(__[^_]+__)|(\\*[^*\\n]+\\*)|(_[^_\\n]+_)|(\\[[^\\]]+\\]\\([^)]+\\))/g;\n\n let cursor = 0;\n let match: RegExpExecArray | null;\n\n while ((match = pattern.exec(text)) !== null) {\n if (match.index > cursor) {\n spans.push({ text: text.slice(cursor, match.index) });\n }\n const token = match[0];\n\n if (token.startsWith('`')) {\n spans.push({ text: token.slice(1, -1), code: true });\n } else if (token.startsWith('**') || token.startsWith('__')) {\n spans.push({ text: token.slice(2, -2), bold: true });\n } else if (token.startsWith('[')) {\n const split = token.indexOf('](');\n spans.push({\n text: token.slice(1, split),\n href: token.slice(split + 2, -1),\n });\n } else {\n spans.push({ text: token.slice(1, -1), italic: true });\n }\n cursor = match.index + token.length;\n }\n\n if (cursor < text.length) spans.push({ text: text.slice(cursor) });\n // A line with no marks still needs one span, or it renders as nothing.\n return spans.length ? spans : [{ text }];\n}\n\n/** Split a table row on unescaped pipes, dropping the outer ones. */\nfunction tableCells(line: string): InlineSpan[][] {\n return line\n .replace(/^\\s*\\|/, '')\n .replace(/\\|\\s*$/, '')\n .split('|')\n .map((cell) => parseInline(cell.trim()));\n}\n\nconst TABLE_DIVIDER = /^\\s*\\|?[\\s:-]*-[\\s|:-]*\\|?\\s*$/;\n\n/**\n * Parse a markdown document into blocks.\n *\n * Tolerant by design: this renders model output, which is not guaranteed\n * to be well-formed. Anything unrecognised falls through as a paragraph\n * rather than being dropped — losing text is worse than losing styling.\n */\nexport function parseMarkdown(source: string): MarkdownBlock[] {\n const blocks: MarkdownBlock[] = [];\n const lines = source.replace(/\\r\\n/g, '\\n').split('\\n');\n\n let index = 0;\n while (index < lines.length) {\n const line = lines[index];\n const trimmed = line.trim();\n\n if (!trimmed) { index += 1; continue; }\n\n // Fenced code. An unterminated fence still renders — the model may\n // be mid-stream, and swallowing the rest of the message would be worse.\n const fence = /^```(\\w+)?/.exec(trimmed);\n if (fence) {\n const language = fence[1];\n const body: string[] = [];\n index += 1;\n while (index < lines.length && !lines[index].trim().startsWith('```')) {\n body.push(lines[index]);\n index += 1;\n }\n index += 1;\n blocks.push({ type: 'code', text: body.join('\\n'), language });\n continue;\n }\n\n // Table: a header row followed by a |---|---| divider.\n if (trimmed.includes('|') && index + 1 < lines.length\n && TABLE_DIVIDER.test(lines[index + 1])) {\n const header = tableCells(trimmed);\n const rows: InlineSpan[][][] = [];\n index += 2;\n while (index < lines.length && lines[index].includes('|') && lines[index].trim()) {\n rows.push(tableCells(lines[index]));\n index += 1;\n }\n blocks.push({ type: 'table', header, rows });\n continue;\n }\n\n const heading = /^(#{1,6})\\s+(.*)$/.exec(trimmed);\n if (heading) {\n // Levels past 3 are visually indistinguishable in a chat bubble.\n const level = Math.min(heading[1].length, 3) as 1 | 2 | 3;\n blocks.push({ type: 'heading', level, spans: parseInline(heading[2]) });\n index += 1;\n continue;\n }\n\n if (/^(\\*\\s*){3,}$|^(-\\s*){3,}$|^(_\\s*){3,}$/.test(trimmed)) {\n blocks.push({ type: 'rule' });\n index += 1;\n continue;\n }\n\n const quote = /^>\\s?(.*)$/.exec(trimmed);\n if (quote) {\n blocks.push({ type: 'quote', spans: parseInline(quote[1]) });\n index += 1;\n continue;\n }\n\n const indent = /^(\\s*)/.exec(line)?.[1].length ?? 0;\n const depth = Math.floor(indent / 2);\n\n const bullet = /^[-*+]\\s+(.*)$/.exec(trimmed);\n if (bullet) {\n blocks.push({ type: 'bullet', spans: parseInline(bullet[1]), depth });\n index += 1;\n continue;\n }\n\n const ordered = /^(\\d+)[.)]\\s+(.*)$/.exec(trimmed);\n if (ordered) {\n blocks.push({\n type: 'ordered',\n spans: parseInline(ordered[2]),\n index: Number(ordered[1]),\n depth,\n });\n index += 1;\n continue;\n }\n\n // Consecutive plain lines join into one paragraph, so a hard-wrapped\n // sentence does not render as several stacked fragments.\n const paragraph: string[] = [trimmed];\n index += 1;\n while (index < lines.length) {\n const nextLine = lines[index].trim();\n if (!nextLine\n || /^(#{1,6})\\s/.test(nextLine)\n || /^[-*+]\\s/.test(nextLine)\n || /^\\d+[.)]\\s/.test(nextLine)\n || nextLine.startsWith('```')\n || nextLine.startsWith('>')\n || nextLine.includes('|')) break;\n paragraph.push(nextLine);\n index += 1;\n }\n blocks.push({ type: 'paragraph', spans: parseInline(paragraph.join(' ')) });\n }\n\n return blocks;\n}\n","// ============================================================\n// @guideai/react-native — markdown renderer\n// ============================================================\n//\n// Renders the blocks parsed by markdown.ts with React Native primitives.\n// The parser is separate and dependency-free, so the syntax handling is\n// unit-tested in Node while this file stays a thin mapping to views.\n// ============================================================\n\nimport React from 'react';\nimport { Linking, ScrollView, StyleSheet, Text, View, type TextStyle } from 'react-native';\nimport { parseMarkdown, type InlineSpan, type MarkdownBlock } from './markdown-parser';\n\nexport interface MarkdownProps {\n children: string;\n /** Base colour for body text; headings and code derive from the theme. */\n color?: string;\n /** Colour for links and list markers. */\n accentColor?: string;\n fontSize?: number;\n}\n\nfunction Spans({ spans, style, accentColor }: {\n spans: InlineSpan[]; style: TextStyle; accentColor: string;\n}) {\n return (\n <>\n {spans.map((span, index) => {\n if (span.href) {\n return (\n <Text\n key={index}\n style={[style, { color: accentColor, textDecorationLine: 'underline' }]}\n onPress={() => { void Linking.openURL(span.href!); }}\n accessibilityRole=\"link\"\n >\n {span.text}\n </Text>\n );\n }\n return (\n <Text\n key={index}\n style={[\n style,\n span.bold ? styles.bold : null,\n span.italic ? styles.italic : null,\n span.code ? styles.inlineCode : null,\n ]}\n >\n {span.text}\n </Text>\n );\n })}\n </>\n );\n}\n\nexport function Markdown({\n children, color = '#0f172a', accentColor = '#2563eb', fontSize = 15,\n}: MarkdownProps): React.ReactElement {\n const blocks = React.useMemo(() => parseMarkdown(children), [children]);\n const body: TextStyle = { color, fontSize, lineHeight: fontSize * 1.45 };\n\n return (\n <View style={styles.container}>\n {blocks.map((block, index) => (\n <Block\n key={index}\n block={block}\n body={body}\n color={color}\n accentColor={accentColor}\n fontSize={fontSize}\n />\n ))}\n </View>\n );\n}\n\nfunction Block({ block, body, color, accentColor, fontSize }: {\n block: MarkdownBlock; body: TextStyle; color: string; accentColor: string; fontSize: number;\n}) {\n switch (block.type) {\n case 'heading': {\n // Headings step down in size and keep weight, so a nested section\n // still reads as subordinate rather than as another title.\n const sizes = { 1: fontSize + 6, 2: fontSize + 3, 3: fontSize + 1 };\n return (\n <Text style={[styles.heading, { color, fontSize: sizes[block.level] }]}>\n <Spans spans={block.spans} accentColor={accentColor}\n style={{ color, fontSize: sizes[block.level], fontWeight: '700' }} />\n </Text>\n );\n }\n\n case 'paragraph':\n return (\n <Text style={styles.paragraph}>\n <Spans spans={block.spans} style={body} accentColor={accentColor} />\n </Text>\n );\n\n case 'bullet':\n return (\n <View style={[styles.listRow, { marginLeft: block.depth * 14 }]}>\n <Text style={[body, { color: accentColor }]}>{'•'}</Text>\n <Text style={styles.listText}>\n <Spans spans={block.spans} style={body} accentColor={accentColor} />\n </Text>\n </View>\n );\n\n case 'ordered':\n return (\n <View style={[styles.listRow, { marginLeft: block.depth * 14 }]}>\n <Text style={[body, { color: accentColor, fontVariant: ['tabular-nums'] }]}>\n {block.index}.\n </Text>\n <Text style={styles.listText}>\n <Spans spans={block.spans} style={body} accentColor={accentColor} />\n </Text>\n </View>\n );\n\n case 'code':\n return (\n <View style={styles.codeBlock}>\n <Text style={[styles.codeText, { fontSize: fontSize - 2 }]}>{block.text}</Text>\n </View>\n );\n\n case 'quote':\n return (\n <View style={[styles.quote, { borderLeftColor: accentColor }]}>\n <Text>\n <Spans spans={block.spans} accentColor={accentColor}\n style={{ ...body, color: '#475569', fontStyle: 'italic' }} />\n </Text>\n </View>\n );\n\n case 'rule':\n return <View style={styles.rule} />;\n\n case 'table': {\n // Cells are `flex: 1`, so four or more columns on a phone squeeze\n // every one of them to a couple of characters and wrap mid-word.\n // Past three, give the table its natural width and let it scroll\n // sideways instead — the row stays readable, which is the point of\n // a table. `alwaysBounceHorizontal={false}` so a table that does fit\n // does not rubber-band as if there were more to see.\n const columns = Math.max(block.header.length, ...block.rows.map((r) => r.length), 1);\n const scrolls = columns > 3;\n const table = (\n <View style={[styles.table, scrolls ? { minWidth: columns * 116 } : null]}>\n <View style={[styles.tableRow, styles.tableHeaderRow]}>\n {block.header.map((cell, index) => (\n <View key={index} style={styles.tableCell}>\n <Text>\n <Spans spans={cell} accentColor={accentColor}\n style={{ ...body, fontWeight: '700', fontSize: fontSize - 1 }} />\n </Text>\n </View>\n ))}\n </View>\n {block.rows.map((row, rowIndex) => (\n <View key={rowIndex} style={styles.tableRow}>\n {row.map((cell, cellIndex) => (\n <View key={cellIndex} style={styles.tableCell}>\n <Text>\n <Spans spans={cell} accentColor={accentColor}\n style={{ ...body, fontSize: fontSize - 1 }} />\n </Text>\n </View>\n ))}\n </View>\n ))}\n </View>\n );\n\n return scrolls\n ? (\n <ScrollView\n horizontal\n showsHorizontalScrollIndicator={false}\n alwaysBounceHorizontal={false}\n >\n {table}\n </ScrollView>\n )\n : table;\n }\n\n default:\n return null;\n }\n}\n\nconst mono = { fontFamily: 'Menlo' } as const;\n\nconst styles = StyleSheet.create({\n container: { gap: 6 },\n heading: { fontWeight: '700', marginTop: 4 },\n paragraph: {},\n listRow: { flexDirection: 'row', gap: 8, alignItems: 'flex-start' },\n listText: { flex: 1 },\n bold: { fontWeight: '700' },\n italic: { fontStyle: 'italic' },\n inlineCode: {\n ...mono,\n backgroundColor: 'rgba(100,116,139,0.14)',\n color: '#0f172a',\n },\n codeBlock: {\n backgroundColor: '#0f172a',\n borderRadius: 10,\n padding: 12,\n marginVertical: 2,\n },\n codeText: { ...mono, color: '#e2e8f0' },\n quote: { borderLeftWidth: 3, paddingLeft: 10, paddingVertical: 2 },\n rule: { height: StyleSheet.hairlineWidth, backgroundColor: '#cbd5e1', marginVertical: 6 },\n table: {\n borderWidth: StyleSheet.hairlineWidth,\n borderColor: '#cbd5e1',\n borderRadius: 8,\n overflow: 'hidden',\n },\n tableRow: {\n flexDirection: 'row',\n borderBottomWidth: StyleSheet.hairlineWidth,\n borderBottomColor: '#e2e8f0',\n },\n tableHeaderRow: { backgroundColor: 'rgba(100,116,139,0.10)' },\n tableCell: { flex: 1, padding: 8 },\n});\n","// ============================================================\n// @guideai/react-native — theme\n// ============================================================\n//\n// Every colour and radius the default UI paints, in one place, so a host\n// can restyle the assistant to match their app without forking the\n// component or touching any behaviour.\n//\n// Partial by design: a host overrides the two or three tokens they care\n// about and inherits the rest, so adding a token later cannot break an\n// existing app.\n// ============================================================\n\nexport interface GuideAITheme {\n /** Page background behind the conversation. */\n background: string;\n /** Secondary surfaces: the composer pill, assistant bubbles. */\n surface: string;\n /**\n * An OPAQUE surface, for anything floating over app content — the\n * nudge tooltip. `surface` is translucent by design, which would let\n * the page show through the text behind it.\n */\n surfaceRaised: string;\n /** Primary text. */\n text: string;\n /** Placeholder, status text, timestamps. */\n textMuted: string;\n /** Send button, links, list markers, the avatar glow. */\n accent: string;\n /** Text drawn on top of `accent`. */\n onAccent: string;\n /** The user's own message bubble. */\n userBubble: string;\n /** Text inside the user's bubble. */\n onUserBubble: string;\n /** Failed-message background. */\n error: string;\n /** Hairlines and dividers. */\n border: string;\n /** Corner radius for the composer and bubbles. */\n radius: number;\n /** Base body size; headings and code derive from it. */\n fontSize: number;\n}\n\n/**\n * A dark, focused surface where the conversation is the only lit thing on\n * screen. Was the default until the light ground below took over; still one\n * token away for a host whose app is dark.\n */\nexport const darkTheme: GuideAITheme = {\n background: '#0B0713',\n surface: 'rgba(255,255,255,0.06)',\n surfaceRaised: '#1C1626',\n text: '#F5F3FF',\n textMuted: '#8B85A0',\n accent: '#7C5CFF',\n onAccent: '#FFFFFF',\n userBubble: '#7C5CFF',\n onUserBubble: '#FFFFFF',\n error: 'rgba(248,113,113,0.16)',\n border: 'rgba(255,255,255,0.10)',\n radius: 22,\n fontSize: 15,\n};\n\n/**\n * The default look: a white ground with the product's purple as the one\n * accent, so the assistant reads as part of the host's app rather than a\n * dark panel dropped into it.\n */\nexport const lightTheme: GuideAITheme = {\n background: '#FFFFFF',\n surface: '#F1F5F9',\n surfaceRaised: '#FFFFFF',\n text: '#0F172A',\n textMuted: '#64748B',\n accent: '#7C5CFF',\n onAccent: '#FFFFFF',\n userBubble: '#7C5CFF',\n onUserBubble: '#FFFFFF',\n error: '#FEE2E2',\n border: '#E2E8F0',\n radius: 22,\n fontSize: 15,\n};\n\n/** A host's overrides, merged onto a base. */\nexport type GuideAIThemeOverrides = Partial<GuideAITheme>;\n\nexport function resolveTheme(\n overrides?: GuideAIThemeOverrides,\n base: GuideAITheme = lightTheme,\n): GuideAITheme {\n return overrides ? { ...base, ...overrides } : base;\n}\n","// ============================================================\n// @guideai/react-native — default chat UI\n// ============================================================\n//\n// Built on useGuideAI() and nothing else, which is the point: the hook\n// is the whole API, and this is one consumer of it. A host replacing\n// this UI loses no capability, because there is no privileged channel\n// between the brain and the built-in view.\n//\n// Every prop here is presentational. There is deliberately none for the\n// harness, transport, tools or chat mode — those are ours, so the agent\n// can be upgraded underneath a host app without it changing a line.\n// ============================================================\n\nimport React, { useEffect, useRef, useState } from 'react';\nimport {\n ActivityIndicator, Alert, Animated, Easing, FlatList, KeyboardAvoidingView,\n Platform, Pressable, StatusBar, StyleSheet, Text, TextInput, View,\n} from 'react-native';\nimport { EP } from '@guideai/core';\nimport { useGuideAI, type GuideAIMessage } from '../react/useGuideAI';\nimport { Markdown } from './Markdown';\nimport { requireConfigured } from '../setup';\nimport { useOnboarding } from '../react/useOnboarding';\nimport { resolveTheme, type GuideAITheme, type GuideAIThemeOverrides } from './theme';\n\nexport interface GuideAIChatProps {\n /** Colours and radii. Partial — unset tokens keep their defaults. */\n theme?: GuideAIThemeOverrides;\n placeholder?: string;\n /** Greeting shown before the first message. */\n greeting?: string;\n /** Replaces the whole pre-conversation state, greeting included. */\n emptyState?: React.ReactNode;\n /** Show the mic button. Inert until voice ships. */\n showMic?: boolean;\n /** Rendered at the left of the composer — an attach action, usually. */\n leadingAccessory?: React.ReactNode;\n /**\n * Shown in the header beside the close button. Omitted, the header\n * carries only the close control.\n */\n title?: string;\n /**\n * Called by the header's close button. Supplied automatically by\n * <GuideAILauncher>; pass it yourself when embedding the chat in your\n * own route.\n *\n * The header renders regardless — it carries the assistant's name and\n * the Talk to a person action — but without this there is no close\n * control, since an embedded chat that is a whole screen should be\n * dismissed by the host's own navigation.\n */\n onClose?: () => void;\n /**\n * The visitor asked for a person.\n *\n * Mirrors the web SDK, where `handleRequestHuman()` closes the AI chat\n * and dispatches `guideai:open-support` so the visitor ends up in one\n * conversation rather than two. React Native has no support panel to\n * hand off to, so the host supplies the destination — their own support\n * route, Intercom, a phone dialler.\n */\n onTalkToHuman?: () => void;\n /**\n * Show the Talk to a person button without asking the backend.\n *\n * Left unset, the button appears only when the live-support service says\n * a human is reachable — the same `support/public/status` check the web\n * SDK's `refreshHelpSupport()` makes, so switching Live Support on in the\n * dashboard takes effect without shipping a new build.\n */\n humanHandoverAvailable?: boolean;\n /**\n * Extra space above the navbar, for the status bar / notch.\n *\n * The sheet is presented `statusBarTranslucent` on Android, so its\n * content starts at y=0 — underneath the clock and the notification\n * icons. Defaulted rather than required because this package will not\n * take a peer dependency on react-native-safe-area-context for one\n * measurement; a host that already has it should pass the real inset:\n *\n * const { top } = useSafeAreaInsets();\n * <GuideAIChat topInset={top} />\n */\n topInset?: number;\n}\n\n/**\n * The headset the web SDK draws beside \"Human\", built from views.\n *\n * The web uses an inline SVG; this package deliberately has no SVG\n * dependency (a peer dep for one 14px glyph is a poor trade), so the same\n * shape — a band with two ear cups — is drawn with borders.\n */\nfunction PersonIcon({ color }: { color: string }) {\n return (\n <View style={styles.iconWrap}>\n <View style={[styles.iconBand, { borderColor: color }]} />\n <View style={[styles.iconCup, styles.iconCupLeft, { backgroundColor: color }]} />\n <View style={[styles.iconCup, styles.iconCupRight, { backgroundColor: color }]} />\n </View>\n );\n}\n\n/**\n * The assistant's presence: a soft orb that breathes while it thinks.\n *\n * Motion is the honest signal that work is happening — a spinner would\n * read as loading, which is not the same as reasoning.\n */\nfunction Orb({ theme, active }: { theme: GuideAITheme; active: boolean }) {\n const pulse = useRef(new Animated.Value(0)).current;\n\n React.useEffect(() => {\n if (!active) {\n pulse.stopAnimation();\n Animated.timing(pulse, { toValue: 0, duration: 240, useNativeDriver: true }).start();\n return;\n }\n const loop = Animated.loop(\n Animated.sequence([\n Animated.timing(pulse, {\n toValue: 1, duration: 900, easing: Easing.inOut(Easing.quad), useNativeDriver: true,\n }),\n Animated.timing(pulse, {\n toValue: 0, duration: 900, easing: Easing.inOut(Easing.quad), useNativeDriver: true,\n }),\n ]),\n );\n loop.start();\n return () => loop.stop();\n }, [active, pulse]);\n\n const scale = pulse.interpolate({ inputRange: [0, 1], outputRange: [1, 1.18] });\n const glow = pulse.interpolate({ inputRange: [0, 1], outputRange: [0.35, 0.7] });\n\n return (\n <View style={styles.orbWrap}>\n <Animated.View\n style={[\n styles.orbGlow,\n { backgroundColor: theme.accent, opacity: glow, transform: [{ scale }] },\n ]}\n />\n <View style={[styles.orbCore, { backgroundColor: theme.accent }]} />\n </View>\n );\n}\n\nfunction Bubble({ message, theme, onTalkToHuman }: {\n message: GuideAIMessage;\n theme: GuideAITheme;\n onTalkToHuman?: () => void;\n}) {\n if (message.role === 'user') {\n return (\n <View style={[styles.row, styles.rowUser]}>\n <View style={[\n styles.bubble,\n { backgroundColor: theme.userBubble, borderRadius: theme.radius },\n message.error ? { backgroundColor: theme.error } : null,\n ]}>\n {/* User text is literal: rendering it as markdown would mangle\n a message that merely happens to contain * or _. */}\n <Text style={{\n color: theme.onUserBubble,\n fontSize: theme.fontSize,\n lineHeight: theme.fontSize * 1.4,\n }}>\n {message.content}\n </Text>\n </View>\n </View>\n );\n }\n\n return (\n <View style={[styles.row, styles.rowAssistant]}>\n <View style={[\n styles.assistantBody,\n message.error\n ? { backgroundColor: theme.error, borderRadius: theme.radius, padding: 12 }\n : null,\n ]}>\n {message.content ? (\n <Markdown color={theme.text} accentColor={theme.accent} fontSize={theme.fontSize}>\n {message.content}\n </Markdown>\n ) : (\n // A streaming message with no text yet would be an empty box.\n <Text style={{ color: theme.textMuted, fontSize: theme.fontSize }}>\n {message.streaming ? '…' : ''}\n </Text>\n )}\n\n {/* The failure message offers a person, so make that an action\n rather than a sentence the visitor has to act on themselves.\n Only when the host gave us somewhere to send them. */}\n {message.error && onTalkToHuman ? (\n <Pressable\n onPress={onTalkToHuman}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Talk to a person\"\n style={[styles.errorAction, { borderColor: theme.accent }]}\n hitSlop={6}\n >\n <PersonIcon color={theme.accent} />\n <Text style={[styles.humanText, {\n color: theme.accent,\n fontSize: theme.fontSize - 1,\n }]}>\n Talk to a human\n </Text>\n </Pressable>\n ) : null}\n </View>\n </View>\n );\n}\n\n/**\n * How much room the status bar needs above the navbar.\n *\n * A host that has react-native-safe-area-context should pass the real\n * inset; these are the fallbacks for one that does not. Android reports\n * its status bar height directly. iOS does not expose it here, so the\n * value is the standard inset for a notched device — the sheet is\n * presented as a pageSheet, which already sits below the status bar, so\n * this only has to clear the sheet's own grabber area.\n */\nfunction resolveTopInset(explicit: number | undefined): number {\n if (explicit !== undefined) return explicit;\n if (Platform.OS === 'android') return StatusBar.currentHeight ?? 24;\n return 8;\n}\n\n/**\n * Whether to offer a person, and the same gate the web SDK applies.\n *\n * The web's `refreshHelpSupport()` asks the live-support service directly\n * rather than trusting the knowledge base, because that endpoint 404s for a\n * site that was never scanned and is CDN cached for minutes. Asking the\n * service means switching Live Support on in the dashboard takes effect on\n * the next open, with no new build and no knowledge base at all.\n *\n * An explicit `available` prop skips the probe: a host who already knows\n * should not pay for a request to be told.\n */\nfunction useHumanHandover(\n onTalkToHuman: (() => void) | undefined,\n available: boolean | undefined,\n): boolean {\n const [reachable, setReachable] = useState(false);\n\n useEffect(() => {\n // No destination, or the host already decided — nothing to ask.\n if (!onTalkToHuman || available !== undefined) return;\n\n let cancelled = false;\n void (async () => {\n try {\n const { api } = requireConfigured();\n const status = await api.get<{ enabled: boolean; agents_online: number }>(\n EP.supportStatus(),\n );\n if (!cancelled) setReachable(!!status?.enabled);\n } catch {\n // Unreachable, or a backend old enough to have no such endpoint.\n // Same as the web: stay quiet rather than offering a dead button.\n }\n })();\n return () => { cancelled = true; };\n }, [onTalkToHuman, available]);\n\n if (!onTalkToHuman) return false;\n return available ?? reachable;\n}\n\nexport function GuideAIChat({\n theme: overrides, placeholder = 'Ask me anything',\n greeting = 'Ask me anything', emptyState,\n showMic = false, leadingAccessory, title, onClose,\n onTalkToHuman, humanHandoverAvailable, topInset,\n}: GuideAIChatProps): React.ReactElement {\n // Resolved from GuideAI.configure(), not passed in: the host should\n // never hold a reference to the harness.\n const { harness, debug } = requireConfigured();\n // Input state lives in the hook, so this component demonstrates the\n // same API a host would use for their own UI — no privileged path.\n const {\n messages, input, setInput, send, busy, canSend, status,\n } = useGuideAI({ harness, trace: debug });\n const theme = resolveTheme(overrides);\n const listRef = useRef<FlatList<GuideAIMessage>>(null);\n const showHuman = useHumanHandover(onTalkToHuman, humanHandoverAvailable);\n const { checklist, dismiss } = useOnboarding();\n const nextSetupStep = checklist?.steps.find((step) => step.state === 'available');\n const headerTop = resolveTopInset(topInset);\n\n const submit = () => { void send(); };\n\n return (\n <KeyboardAvoidingView\n style={[styles.container, { backgroundColor: theme.background }]}\n behavior={Platform.OS === 'ios' ? 'padding' : undefined}\n >\n {/* Navbar. Rendered whenever it has something to carry — a title,\n a way out, or a person to hand off to — so the handoff is\n reachable at any point in the conversation rather than only from\n the welcome screen, which is how the web SDK places it too. */}\n {title || onClose || showHuman ? (\n <View style={[\n styles.header,\n { borderBottomColor: theme.border, paddingTop: headerTop + 14 },\n ]}>\n <View style={styles.headerSide}>\n {title ? (\n <Text\n numberOfLines={1}\n style={[styles.title, { color: theme.text, fontSize: theme.fontSize + 1 }]}\n >\n {title}\n </Text>\n ) : null}\n </View>\n\n <View style={styles.headerActions}>\n {showHuman ? (\n <Pressable\n onPress={onTalkToHuman}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Talk to a person\"\n style={[styles.human, {\n backgroundColor: theme.surface,\n borderColor: theme.border,\n }]}\n hitSlop={8}\n >\n <PersonIcon color={theme.accent} />\n <Text style={[styles.humanText, {\n color: theme.accent,\n fontSize: theme.fontSize - 2,\n }]}>\n Talk to human\n </Text>\n </Pressable>\n ) : null}\n\n {onClose ? (\n <Pressable\n onPress={onClose}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Close assistant\"\n // A filled 32px target, not a bare glyph: many Android phones\n // are gesture-only, and iOS has no back button at all, so this\n // is the sole way out of the sheet.\n style={[styles.close, { backgroundColor: theme.surface }]}\n hitSlop={10}\n >\n <Text style={[styles.closeIcon, { color: theme.text }]}>✕</Text>\n </Pressable>\n ) : null}\n </View>\n </View>\n ) : null}\n\n {checklist && !checklist.completed && !checklist.dismissed ? (\n <View style={{ margin: 14, padding: 14, borderRadius: 12, borderWidth: 1, borderColor: theme.border, backgroundColor: theme.surface }} accessibilityLabel=\"Setup checklist\">\n <Text style={{ color: theme.text, fontWeight: '700', fontSize: theme.fontSize + 2 }}>{checklist.title}</Text>\n <Text style={{ color: theme.textMuted, marginTop: 3 }}>{checklist.goal}</Text>\n <Text style={{ color: theme.textMuted, marginTop: 8 }}>{checklist.completed_required} of {checklist.total_required} required steps complete</Text>\n {checklist.steps.map((step) => (\n <Text key={step.id} style={{ color: step.state === 'locked' ? theme.textMuted : theme.text, marginTop: 5 }}>\n {step.state === 'complete' ? '✓' : step.state === 'locked' ? '–' : '○'} {step.title}\n </Text>\n ))}\n {nextSetupStep ? (\n <Pressable accessibilityRole=\"button\" accessibilityLabel={`Help with ${nextSetupStep.title}`} onPress={() => void send(`Help me with ${nextSetupStep.title}`)} style={{ alignSelf: 'flex-start', marginTop: 10, padding: 10, borderRadius: 8, backgroundColor: theme.accent }}>\n <Text style={{ color: theme.onAccent, fontWeight: '600' }}>Continue setup</Text>\n </Pressable>\n ) : null}\n {checklist.allow_dismissal ? <Pressable accessibilityRole=\"button\" accessibilityLabel=\"I don't need this setup\" onPress={() => Alert.alert('Stop showing setup?', 'This checklist will not appear again on future visits.', [\n { text: 'Keep setup', style: 'cancel' },\n { text: \"I don't need it\", onPress: () => { void dismiss().catch(() => Alert.alert('Could not dismiss setup', 'Please try again.')); } },\n ])} style={{ alignSelf: 'flex-start', marginTop: 10, padding: 10 }}>\n <Text style={{ color: theme.textMuted, textDecorationLine: 'underline' }}>I don't need this setup</Text>\n </Pressable> : null}\n </View>\n ) : null}\n\n {messages.length === 0 ? (\n <View style={styles.empty}>\n {emptyState ?? (\n <View style={styles.greetingRow}>\n <Orb theme={theme} active={busy} />\n <Text style={[\n styles.greeting,\n { color: theme.textMuted, fontSize: theme.fontSize + 3 },\n ]}>\n {greeting}\n </Text>\n </View>\n )}\n </View>\n ) : (\n <FlatList\n ref={listRef}\n data={messages}\n keyExtractor={(message) => message.id}\n renderItem={({ item }) => (\n <Bubble message={item} theme={theme} onTalkToHuman={onTalkToHuman} />\n )}\n contentContainerStyle={styles.list}\n onContentSizeChange={() => listRef.current?.scrollToEnd({ animated: true })}\n keyboardShouldPersistTaps=\"handled\"\n />\n )}\n\n {status ? (\n <View style={styles.status}>\n <ActivityIndicator size=\"small\" color={theme.accent} />\n <Text style={{ color: theme.textMuted, fontSize: theme.fontSize - 2 }}>{status}</Text>\n </View>\n ) : null}\n\n <View style={styles.composerRow}>\n <View style={[\n styles.composer,\n {\n backgroundColor: theme.surface,\n borderRadius: theme.radius + 6,\n borderColor: theme.border,\n },\n ]}>\n {leadingAccessory ? <View style={styles.leading}>{leadingAccessory}</View> : null}\n\n <TextInput\n style={[styles.input, { color: theme.text, fontSize: theme.fontSize }]}\n value={input}\n onChangeText={setInput}\n placeholder={checklist && !checklist.completed && checklist.chat_policy === 'setup_only' ? 'Ask about your setup' : placeholder}\n placeholderTextColor={theme.textMuted}\n editable={!busy}\n onSubmitEditing={submit}\n returnKeyType=\"send\"\n multiline\n accessibilityLabel={placeholder}\n />\n\n <Pressable\n onPress={submit}\n disabled={!canSend}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Send\"\n style={[styles.send, { backgroundColor: canSend ? theme.accent : theme.border }]}\n hitSlop={6}\n >\n <Text style={[\n styles.sendIcon,\n { color: canSend ? theme.onAccent : theme.textMuted },\n ]}>\n ↑\n </Text>\n </Pressable>\n </View>\n\n {showMic ? (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel=\"Voice input\"\n style={[styles.mic, { backgroundColor: theme.surface, borderColor: theme.border }]}\n >\n <Text style={{ color: theme.textMuted, fontSize: 18 }}>🎤</Text>\n </Pressable>\n ) : null}\n </View>\n </KeyboardAvoidingView>\n );\n}\n\nconst styles = StyleSheet.create({\n container: { flex: 1 },\n header: {\n flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between',\n // paddingTop is set inline from the status-bar inset; only the\n // bottom half of the navbar's breathing room lives here.\n paddingHorizontal: 16, paddingBottom: 14,\n borderBottomWidth: StyleSheet.hairlineWidth,\n },\n headerSide: { flex: 1, minWidth: 0 },\n headerActions: { flexDirection: 'row', alignItems: 'center', gap: 8 },\n title: { fontWeight: '600' },\n human: {\n flexDirection: 'row', alignItems: 'center', gap: 6,\n paddingHorizontal: 10, paddingVertical: 6,\n borderRadius: 999, borderWidth: StyleSheet.hairlineWidth,\n },\n humanText: { fontWeight: '600' },\n errorAction: {\n flexDirection: 'row', alignItems: 'center', gap: 6, alignSelf: 'flex-start',\n marginTop: 10, paddingHorizontal: 12, paddingVertical: 7,\n borderRadius: 999, borderWidth: StyleSheet.hairlineWidth,\n },\n\n iconWrap: { width: 14, height: 14, alignItems: 'center', justifyContent: 'center' },\n iconBand: {\n position: 'absolute', top: 1, width: 12, height: 9,\n borderWidth: 1.6, borderBottomWidth: 0,\n borderTopLeftRadius: 6, borderTopRightRadius: 6,\n },\n iconCup: { position: 'absolute', bottom: 1, width: 3.5, height: 6, borderRadius: 1.5 },\n iconCupLeft: { left: 0 },\n iconCupRight: { right: 0 },\n close: {\n width: 32, height: 32, borderRadius: 16,\n alignItems: 'center', justifyContent: 'center',\n },\n closeIcon: { fontSize: 15, fontWeight: '600', lineHeight: 18 },\n list: { padding: 16, paddingBottom: 8, gap: 14 },\n empty: {\n // Centred, not pinned near the top: before the first message the\n // greeting is the only thing on screen, and hanging it under the\n // navbar leaves the rest of the display empty.\n flex: 1, alignItems: 'center', justifyContent: 'center',\n paddingHorizontal: 24, paddingBottom: 24,\n },\n greetingRow: { flexDirection: 'row', alignItems: 'center', gap: 14 },\n greeting: { fontWeight: '400', textAlign: 'center', flexShrink: 1 },\n\n orbWrap: { width: 34, height: 34, alignItems: 'center', justifyContent: 'center' },\n orbGlow: { position: 'absolute', width: 34, height: 34, borderRadius: 17 },\n orbCore: { width: 20, height: 20, borderRadius: 10 },\n\n row: { flexDirection: 'row' },\n rowUser: { justifyContent: 'flex-end' },\n rowAssistant: { justifyContent: 'flex-start' },\n // The user's bubble hugs its text: a three-word question drawn as a\n // full-width block reads as a paragraph, not a chat message. It is\n // capped at 80% so a long one still leaves the reply side legible.\n bubble: { maxWidth: '80%', paddingHorizontal: 16, paddingVertical: 11 },\n\n // The assistant's body does NOT hug its content, and that is the whole\n // point of `width` here rather than `maxWidth` alone.\n //\n // A View with only maxWidth shrink-wraps — React Native's equivalent of\n // width: fit-content. For a paragraph that looks fine, because the text\n // fills the measure anyway. For anything that lays itself out against\n // its parent — a table whose cells are `flex: 1`, a fenced code block,\n // a numbered list — the parent has collapsed to nothing by the time the\n // child is measured, so the table renders as an empty sliver and every\n // reply ends up a different width.\n //\n // Fixing it here rather than on the table keeps every block type\n // consistent: one column, the same measure for prose, code and tables.\n assistantBody: { width: '80%' },\n\n status: {\n flexDirection: 'row', alignItems: 'center', gap: 8,\n paddingHorizontal: 22, paddingBottom: 8,\n },\n\n composerRow: {\n flexDirection: 'row', alignItems: 'flex-end', gap: 10,\n paddingHorizontal: 14, paddingBottom: Platform.OS === 'ios' ? 10 : 14, paddingTop: 4,\n },\n composer: {\n flex: 1, flexDirection: 'row', alignItems: 'center',\n paddingLeft: 8, paddingRight: 6, paddingVertical: 6,\n borderWidth: StyleSheet.hairlineWidth, gap: 6,\n },\n leading: { paddingLeft: 4 },\n input: {\n flex: 1, paddingHorizontal: 8,\n paddingVertical: Platform.OS === 'ios' ? 8 : 4, maxHeight: 120,\n },\n send: { width: 34, height: 34, borderRadius: 17, alignItems: 'center', justifyContent: 'center' },\n sendIcon: { fontSize: 17, fontWeight: '700', lineHeight: 20 },\n mic: {\n width: 46, height: 46, borderRadius: 23,\n alignItems: 'center', justifyContent: 'center',\n borderWidth: StyleSheet.hairlineWidth,\n },\n});\n","// ============================================================\n// @guideai/react-native — live support chat\n// ============================================================\n//\n// The other side of \"Talk to a human\": a thread with a real agent,\n// polled from the same endpoints the web SDK's SupportChat uses\n// (support/public/{status,conversations,conversations/:id/messages}).\n//\n// Deliberately a separate component from GuideAIChat rather than a mode\n// inside it. The two have nothing in common but a text input: this one\n// has no agent loop, no tools, no streaming, no markdown — it is a\n// message list and a poll. Folding it in would mean every AI-side change\n// risks breaking a support conversation, and vice versa.\n// ============================================================\n\nimport React, { useCallback, useEffect, useRef, useState } from 'react';\nimport {\n ActivityIndicator, FlatList, KeyboardAvoidingView, Platform, Pressable,\n ScrollView, StatusBar, StyleSheet, Text, TextInput, View,\n} from 'react-native';\nimport { EP } from '@guideai/core';\nimport { requireConfigured } from '../setup';\nimport { resolveTheme, type GuideAITheme, type GuideAIThemeOverrides } from './theme';\n\n/** How often to ask for new messages. Matches the web SDK. */\nconst POLL_INTERVAL_MS = 3_000;\n\n/** Where the open thread's id is kept, so closing the sheet does not lose it. */\nconst CONVERSATION_KEY = 'guideai_support_conversation';\n\ninterface SupportMessage {\n id: string;\n sender: 'visitor' | 'agent' | 'system';\n sender_name: string | null;\n body: string;\n created_at: string;\n}\n\ninterface ThreadResponse {\n conversation_id: string;\n status: string;\n agents_online: number;\n messages: SupportMessage[];\n}\n\nexport interface GuideAISupportChatProps {\n theme?: GuideAIThemeOverrides;\n /** Close the support thread — returns the sheet to the assistant. */\n onClose?: () => void;\n /** Go back to the AI assistant without closing the sheet. */\n onBackToAssistant?: () => void;\n /** Status-bar inset; see GuideAIChat's `topInset`. */\n topInset?: number;\n /**\n * Opening context for a new thread — the AI conversation that led here.\n *\n * The agent picks up a thread that already says what the visitor was\n * trying to do, rather than \"how can I help?\" to someone who has just\n * explained themselves at length to a bot.\n */\n openingContext?: string;\n}\n\n/**\n * The thread id, kept across sheet opens.\n *\n * A visitor who closes the sheet mid-conversation and reopens it must land\n * back in the same thread — the agent is still there, and starting a second\n * conversation would strand the first. Stored through the SDK's own KV\n * store, so it lives exactly as long as the host's store does.\n */\nfunction useStoredConversation(): [string | null, (id: string | null) => void] {\n const { runtime } = requireConfigured();\n const [id, setId] = useState<string | null>(() => {\n try {\n return runtime.storage.getItem(CONVERSATION_KEY);\n } catch {\n return null;\n }\n });\n\n const persist = useCallback((next: string | null) => {\n setId(next);\n try {\n if (next) runtime.storage.setItem(CONVERSATION_KEY, next);\n else runtime.storage.removeItem(CONVERSATION_KEY);\n } catch {\n // A store that refuses a write still leaves a working session —\n // only the resume-after-close behaviour is lost.\n }\n }, [runtime]);\n\n return [id, persist];\n}\n\nfunction Bubble({ message, theme }: { message: SupportMessage; theme: GuideAITheme }) {\n // A system note is the thread talking about itself — \"an agent joined\" —\n // so it is centred and quiet rather than attributed to either side.\n if (message.sender === 'system') {\n return (\n <Text style={[styles.systemNote, {\n color: theme.textMuted,\n fontSize: theme.fontSize - 2,\n }]}>\n {message.body}\n </Text>\n );\n }\n\n const fromVisitor = message.sender === 'visitor';\n\n return (\n <View style={[styles.row, fromVisitor ? styles.rowUser : styles.rowAgent]}>\n <View style={styles.column}>\n {!fromVisitor && message.sender_name ? (\n <Text style={[styles.senderName, {\n color: theme.textMuted,\n fontSize: theme.fontSize - 3,\n }]}>\n {message.sender_name}\n </Text>\n ) : null}\n <View style={[\n styles.bubble,\n {\n borderRadius: theme.radius,\n backgroundColor: fromVisitor ? theme.userBubble : theme.surface,\n },\n ]}>\n <Text style={{\n color: fromVisitor ? theme.onUserBubble : theme.text,\n fontSize: theme.fontSize,\n lineHeight: theme.fontSize * 1.4,\n }}>\n {message.body}\n </Text>\n </View>\n </View>\n </View>\n );\n}\n\nexport function GuideAISupportChat({\n theme: overrides, onClose, onBackToAssistant, topInset, openingContext,\n}: GuideAISupportChatProps): React.ReactElement {\n const { api } = requireConfigured();\n const theme = resolveTheme(overrides);\n const headerTop = topInset ?? (Platform.OS === 'android' ? StatusBar.currentHeight ?? 24 : 8);\n\n const [conversationId, setConversationId] = useStoredConversation();\n const [messages, setMessages] = useState<SupportMessage[]>([]);\n const [agentsOnline, setAgentsOnline] = useState<number | null>(null);\n const [input, setInput] = useState('');\n const [busy, setBusy] = useState(false);\n const [failed, setFailed] = useState(false);\n\n // Intake is skipped entirely for a thread that already exists.\n const [name, setName] = useState('');\n const [email, setEmail] = useState('');\n const [issue, setIssue] = useState('');\n\n const listRef = useRef<FlatList<SupportMessage>>(null);\n const cursor = useRef<string | null>(null);\n const seenIds = useRef<Set<string>>(new Set());\n const mounted = useRef(true);\n useEffect(() => () => { mounted.current = false; }, []);\n\n /**\n * Fetch anything newer than the cursor.\n *\n * The cursor is a `created_at` timestamp rather than an offset, so a poll\n * that overlaps a send cannot skip a message; `seenIds` then makes a\n * repeat harmless. Failures are swallowed — the next tick retries, and a\n * dropped poll on a flaky connection is not worth an error in the thread.\n */\n const poll = useCallback(async (id: string) => {\n try {\n const path = EP.supportMsgs(id);\n const result = await api.get<ThreadResponse>(\n cursor.current ? `${path}?since=${encodeURIComponent(cursor.current)}` : path,\n );\n if (!mounted.current) return;\n\n setAgentsOnline(result.agents_online);\n\n const fresh = (result.messages ?? []).filter((m) => !seenIds.current.has(m.id));\n if (fresh.length === 0) return;\n\n for (const message of fresh) {\n seenIds.current.add(message.id);\n if (!cursor.current || message.created_at > cursor.current) {\n cursor.current = message.created_at;\n }\n }\n setMessages((previous) => [...previous, ...fresh]);\n } catch {\n // Transient — the next tick tries again.\n }\n }, [api]);\n\n // Poll while a thread is open, and once immediately so reopening the\n // sheet shows the backlog rather than an empty thread for three seconds.\n useEffect(() => {\n if (!conversationId) return;\n void poll(conversationId);\n const timer = setInterval(() => { void poll(conversationId); }, POLL_INTERVAL_MS);\n return () => clearInterval(timer);\n }, [conversationId, poll]);\n\n const startConversation = useCallback(async () => {\n const text = issue.trim();\n if (!text || busy) return;\n\n setBusy(true);\n setFailed(false);\n try {\n const result = await api.post<{ conversation_id: string; agents_online: number }>(\n EP.supportConvs(),\n {\n message: openingContext ? `${text}\\n\\n---\\n${openingContext}` : text,\n visitor_name: name.trim() || undefined,\n visitor_email: email.trim() || undefined,\n },\n );\n if (!mounted.current) return;\n setAgentsOnline(result.agents_online);\n setConversationId(result.conversation_id);\n } catch {\n if (mounted.current) setFailed(true);\n } finally {\n if (mounted.current) setBusy(false);\n }\n }, [api, busy, email, issue, name, openingContext, setConversationId]);\n\n const send = useCallback(async () => {\n const text = input.trim();\n if (!text || !conversationId || busy) return;\n\n setInput('');\n setBusy(true);\n try {\n await api.post(EP.supportMsgs(conversationId), { body: text });\n // No local echo: the poll below renders the server's copy, which is\n // the one with a real id, so the message is never shown twice.\n await poll(conversationId);\n } catch {\n if (mounted.current) {\n setInput(text); // Give the text back rather than losing it.\n setFailed(true);\n }\n } finally {\n if (mounted.current) setBusy(false);\n }\n }, [api, busy, conversationId, input, poll]);\n\n const presence = agentsOnline === null\n ? 'Connecting…'\n : agentsOnline > 0\n ? `${agentsOnline} agent${agentsOnline === 1 ? '' : 's'} online`\n : 'No agents online — leave a message and we will reply by email';\n\n return (\n <KeyboardAvoidingView\n style={[styles.container, { backgroundColor: theme.background }]}\n behavior={Platform.OS === 'ios' ? 'padding' : 'height'}\n >\n <View style={[\n styles.header,\n { borderBottomColor: theme.border, paddingTop: headerTop + 14 },\n ]}>\n <View style={styles.headerSide}>\n <Text\n numberOfLines={1}\n style={[styles.title, { color: theme.text, fontSize: theme.fontSize + 1 }]}\n >\n Support\n </Text>\n <View style={styles.presenceRow}>\n <View style={[styles.presenceDot, {\n backgroundColor: agentsOnline ? '#22c55e' : theme.textMuted,\n }]} />\n <Text\n numberOfLines={1}\n style={{ color: theme.textMuted, fontSize: theme.fontSize - 3 }}\n >\n {presence}\n </Text>\n </View>\n </View>\n\n <View style={styles.headerActions}>\n {onBackToAssistant ? (\n <Pressable\n onPress={onBackToAssistant}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Back to the assistant\"\n style={[styles.backBtn, { borderColor: theme.border }]}\n hitSlop={8}\n >\n <Text style={{ color: theme.accent, fontSize: theme.fontSize - 2, fontWeight: '600' }}>\n Assistant\n </Text>\n </Pressable>\n ) : null}\n {onClose ? (\n <Pressable\n onPress={onClose}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Close support\"\n style={[styles.close, { backgroundColor: theme.surface }]}\n hitSlop={10}\n >\n <Text style={[styles.closeIcon, { color: theme.text }]}>✕</Text>\n </Pressable>\n ) : null}\n </View>\n </View>\n\n {!conversationId ? (\n // ── Intake ──\n // Name and email are optional; the issue is not. Asking for\n // everything before a visitor may speak is how support forms lose\n // people who just want to ask something.\n <ScrollView\n style={styles.intakeScroll}\n contentContainerStyle={styles.intake}\n keyboardShouldPersistTaps=\"handled\"\n >\n <Text style={[styles.intakeTitle, { color: theme.text, fontSize: theme.fontSize + 2 }]}>\n Talk to a person\n </Text>\n <Text style={{ color: theme.textMuted, fontSize: theme.fontSize - 1, lineHeight: 20 }}>\n Tell us what you need and we will connect you with someone.\n </Text>\n\n <TextInput\n style={[styles.field, {\n color: theme.text, borderColor: theme.border,\n backgroundColor: theme.surface, fontSize: theme.fontSize,\n }]}\n value={name}\n onChangeText={setName}\n placeholder=\"Your name (optional)\"\n placeholderTextColor={theme.textMuted}\n autoCapitalize=\"words\"\n />\n <TextInput\n style={[styles.field, {\n color: theme.text, borderColor: theme.border,\n backgroundColor: theme.surface, fontSize: theme.fontSize,\n }]}\n value={email}\n onChangeText={setEmail}\n placeholder=\"Email (optional)\"\n placeholderTextColor={theme.textMuted}\n autoCapitalize=\"none\"\n keyboardType=\"email-address\"\n />\n <TextInput\n style={[styles.field, styles.fieldMultiline, {\n color: theme.text, borderColor: theme.border,\n backgroundColor: theme.surface, fontSize: theme.fontSize,\n }]}\n value={issue}\n onChangeText={setIssue}\n placeholder=\"How can we help?\"\n placeholderTextColor={theme.textMuted}\n multiline\n />\n\n {failed ? (\n <Text style={{ color: theme.textMuted, fontSize: theme.fontSize - 2 }}>\n We could not start the conversation. Please try again.\n </Text>\n ) : null}\n\n <Pressable\n onPress={() => { void startConversation(); }}\n disabled={!issue.trim() || busy}\n accessibilityRole=\"button\"\n style={[styles.primaryBtn, {\n backgroundColor: issue.trim() && !busy ? theme.accent : theme.border,\n }]}\n >\n {busy ? (\n <ActivityIndicator size=\"small\" color={theme.onAccent} />\n ) : (\n <Text style={{\n color: issue.trim() ? theme.onAccent : theme.textMuted,\n fontSize: theme.fontSize,\n fontWeight: '700',\n }}>\n Start conversation\n </Text>\n )}\n </Pressable>\n </ScrollView>\n ) : (\n // ── Thread ──\n <>\n <FlatList\n ref={listRef}\n style={styles.list}\n data={messages}\n keyExtractor={(message) => message.id}\n renderItem={({ item }) => <Bubble message={item} theme={theme} />}\n contentContainerStyle={[\n styles.listContent,\n // Centre the waiting state instead of pinning it under the\n // header: an empty thread is most of the screen.\n messages.length === 0 ? styles.listContentEmpty : null,\n ]}\n onContentSizeChange={() => listRef.current?.scrollToEnd({ animated: true })}\n keyboardShouldPersistTaps=\"handled\"\n ListEmptyComponent={\n // This is most of the screen while a visitor waits, so it\n // says what is happening and what to expect rather than\n // leaving them looking at one grey line.\n <View style={styles.waiting}>\n <ActivityIndicator size=\"small\" color={theme.accent} />\n <Text style={[styles.waitingTitle, {\n color: theme.text,\n fontSize: theme.fontSize,\n }]}>\n {agentsOnline ? 'Connecting you to an agent…' : 'Message sent'}\n </Text>\n <Text style={[styles.waitingBody, {\n color: theme.textMuted,\n fontSize: theme.fontSize - 2,\n }]}>\n {agentsOnline\n ? 'Someone will be with you shortly. You can keep typing in the meantime.'\n : 'Nobody is online right now, but your message is saved and we will reply by email.'}\n </Text>\n </View>\n }\n />\n\n <View style={[styles.composerRow, { borderTopColor: theme.border }]}>\n <View style={[styles.composer, {\n backgroundColor: theme.surface,\n borderRadius: theme.radius + 6,\n borderColor: theme.border,\n }]}>\n <TextInput\n style={[styles.input, { color: theme.text, fontSize: theme.fontSize }]}\n value={input}\n onChangeText={setInput}\n placeholder=\"Write a message\"\n placeholderTextColor={theme.textMuted}\n onSubmitEditing={() => { void send(); }}\n returnKeyType=\"send\"\n multiline\n />\n <Pressable\n onPress={() => { void send(); }}\n disabled={!input.trim() || busy}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Send\"\n style={[styles.send, {\n backgroundColor: input.trim() && !busy ? theme.accent : theme.border,\n }]}\n hitSlop={6}\n >\n <Text style={[styles.sendIcon, {\n color: input.trim() && !busy ? theme.onAccent : theme.textMuted,\n }]}>\n ↑\n </Text>\n </Pressable>\n </View>\n </View>\n </>\n )}\n </KeyboardAvoidingView>\n );\n}\n\nconst styles = StyleSheet.create({\n container: { flex: 1 },\n header: {\n flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between',\n paddingHorizontal: 16, paddingBottom: 14,\n borderBottomWidth: StyleSheet.hairlineWidth,\n },\n headerSide: { flex: 1, minWidth: 0 },\n headerActions: { flexDirection: 'row', alignItems: 'center', gap: 8 },\n title: { fontWeight: '600' },\n presenceRow: { flexDirection: 'row', alignItems: 'center', gap: 6, marginTop: 3 },\n presenceDot: { width: 7, height: 7, borderRadius: 4 },\n backBtn: {\n paddingHorizontal: 12, paddingVertical: 7,\n borderRadius: 999, borderWidth: StyleSheet.hairlineWidth,\n },\n close: {\n width: 32, height: 32, borderRadius: 16,\n alignItems: 'center', justifyContent: 'center',\n },\n closeIcon: { fontSize: 15, fontWeight: '600', lineHeight: 18 },\n\n // The form has to own the space under the header — without flex it sits\n // jammed at the top with the rest of the sheet blank — and it has to\n // scroll, or the keyboard buries the button on a short phone.\n intakeScroll: { flex: 1 },\n intake: { padding: 20, gap: 14, paddingBottom: 32 },\n intakeTitle: { fontWeight: '700' },\n field: {\n borderWidth: StyleSheet.hairlineWidth, borderRadius: 12,\n paddingHorizontal: 14,\n // 48pt tall: a text field below the 44pt touch minimum is hard to hit\n // and reads as disabled.\n minHeight: 48,\n paddingVertical: Platform.OS === 'ios' ? 14 : 10,\n },\n fieldMultiline: { minHeight: 110, textAlignVertical: 'top', paddingTop: 14 },\n primaryBtn: {\n borderRadius: 999, minHeight: 50,\n alignItems: 'center', justifyContent: 'center', marginTop: 6,\n },\n\n // `style` fills the sheet; `contentContainerStyle` lays out the rows.\n // Putting padding on the container and nothing on the list is what left\n // the composer floating in the middle of the screen.\n list: { flex: 1 },\n listContent: { padding: 16, paddingBottom: 12, gap: 14 },\n listContentEmpty: { flexGrow: 1, justifyContent: 'center' },\n\n row: { flexDirection: 'row' },\n rowUser: { justifyContent: 'flex-end' },\n rowAgent: { justifyContent: 'flex-start' },\n // ONE width cap, on the column. It used to be here and on the bubble\n // too, and nested percentages multiply: an agent's reply was 80% of 80%\n // — 64% of the sheet — while the visitor's own messages ran to 80%.\n column: { maxWidth: '82%' },\n bubble: { paddingHorizontal: 15, paddingVertical: 11 },\n senderName: { marginBottom: 4, marginLeft: 8, fontWeight: '600' },\n systemNote: { textAlign: 'center', marginVertical: 6, paddingHorizontal: 24 },\n waiting: { alignItems: 'center', gap: 10, paddingHorizontal: 32 },\n waitingTitle: { fontWeight: '600', marginTop: 4 },\n waitingBody: { textAlign: 'center', lineHeight: 19 },\n\n composerRow: {\n flexDirection: 'row', alignItems: 'flex-end', gap: 10,\n paddingHorizontal: 14, paddingBottom: Platform.OS === 'ios' ? 12 : 14, paddingTop: 10,\n // A hairline above the composer: without it the input floats against\n // the last message with nothing marking where the thread ends.\n borderTopWidth: StyleSheet.hairlineWidth,\n },\n composer: {\n flex: 1, flexDirection: 'row', alignItems: 'flex-end',\n paddingLeft: 10, paddingRight: 6, paddingVertical: 5,\n borderWidth: StyleSheet.hairlineWidth, gap: 6,\n },\n input: {\n flex: 1, paddingHorizontal: 6, minHeight: 38, maxHeight: 120,\n paddingVertical: Platform.OS === 'ios' ? 9 : 6,\n },\n // 38pt, matching the input's resting height so the two line up, and\n // paired with hitSlop to clear the 44pt touch minimum.\n send: { width: 38, height: 38, borderRadius: 19, alignItems: 'center', justifyContent: 'center' },\n sendIcon: { fontSize: 18, fontWeight: '700', lineHeight: 21 },\n});\n","// ============================================================\n// @guideai/react-native — launcher\n// ============================================================\n//\n// The assistant has to be reachable from every screen, so the default is\n// a floating button that opens the chat in a modal over whatever the\n// user is looking at. Mounted once at the app root, it follows the user\n// rather than being re-added per screen.\n//\n// But a floating circle is not right for every app, so the same sheet is\n// available imperatively: `useGuideAISheet().open()` attaches it to any\n// control a host already has — a nav-bar icon, a menu row, a help link.\n// One implementation, two entry points, so a host is never forced to\n// rebuild the sheet to change how it is opened.\n// ============================================================\n\nimport React, {\n createContext, useCallback, useContext, useEffect, useMemo, useRef, useState,\n} from 'react';\nimport {\n Animated, Dimensions, Easing, Modal, PanResponder, Platform,\n Pressable, StyleSheet, Text, View,\n} from 'react-native';\nimport { GuideAIChat, type GuideAIChatProps } from './GuideAIChat';\nimport { GuideAISupportChat } from './GuideAISupportChat';\nimport { resolveTheme, type GuideAIThemeOverrides } from './theme';\nimport { useOnboarding } from '../react/useOnboarding';\n\ninterface SheetControls {\n open(): void;\n close(): void;\n toggle(): void;\n isOpen: boolean;\n}\n\nconst SheetContext = createContext<SheetControls | null>(null);\n\n/**\n * Open or close the assistant from anywhere beneath <GuideAILauncher>.\n *\n * This is what makes \"attach it to your own button\" a one-liner rather\n * than a reimplementation:\n *\n * const { open } = useGuideAISheet();\n * <Pressable onPress={open}><HelpIcon /></Pressable>\n */\nexport function useGuideAISheet(): SheetControls {\n const controls = useContext(SheetContext);\n if (!controls) {\n throw new Error(\n 'useGuideAISheet() must be used inside <GuideAILauncher>. Mount it once '\n + 'at your app root, above your navigator.',\n );\n }\n return controls;\n}\n\nexport interface GuideAILauncherProps extends Omit<GuideAIChatProps, 'onClose'> {\n /**\n * Hide the floating button and open only from your own controls via\n * useGuideAISheet(). The sheet still works exactly the same.\n */\n hideButton?: boolean;\n /** Which corner the floating button sits in. */\n position?: 'bottom-right' | 'bottom-left';\n /** Distance from the screen edges, to clear a tab bar. */\n offset?: { bottom?: number; horizontal?: number };\n\n /**\n * Let the user drag the button out of the way.\n *\n * It snaps to whichever side it is nearest on release, so it never\n * ends up marooned mid-screen, and stays within the safe margins. A\n * fixed button will always cover something on some screen; dragging is\n * the cheapest fix that does not require the host to think about it.\n */\n draggable?: boolean;\n /** Replaces the icon inside the floating button. */\n icon?: React.ReactNode;\n /** Accessible label for the floating button. */\n label?: string;\n\n /**\n * A one-off tooltip beside the button, to make a first-time visitor\n * notice the assistant is there.\n *\n * Shown once per app launch after `nudgeDelay`, then withdrawn after\n * `nudgeDuration`. Set to null to disable.\n */\n nudge?: string | null;\n /** How long to wait before showing it. Lets the screen settle first. */\n nudgeDelay?: number;\n /** How long it stays before withdrawing. */\n nudgeDuration?: number;\n\n children?: React.ReactNode;\n}\n\n\n/**\n * A tooltip that appears beside the button, then withdraws.\n *\n * The whole point is discoverability: a floating circle in a corner is\n * easy to never notice, and an assistant nobody opens may as well not\n * ship. It shows once per launch rather than on every screen — a hint\n * that keeps reappearing stops reading as help and starts reading as an\n * advert.\n */\nfunction Nudge({ text, side, bottom, horizontal, theme, onDismiss }: {\n text: string;\n side: 'left' | 'right';\n bottom: number;\n horizontal: number;\n theme: ReturnType<typeof resolveTheme>;\n onDismiss(): void;\n}) {\n const enter = useRef(new Animated.Value(0)).current;\n\n useEffect(() => {\n Animated.timing(enter, {\n toValue: 1, duration: 260, easing: Easing.out(Easing.back(1.4)), useNativeDriver: true,\n }).start();\n }, [enter]);\n\n const translate = enter.interpolate({\n inputRange: [0, 1],\n // Slides in from the button it belongs to, so the eye is led to it.\n outputRange: [side === 'right' ? 14 : -14, 0],\n });\n\n return (\n <Animated.View\n pointerEvents=\"box-none\"\n style={[\n styles.nudge,\n {\n bottom: bottom + 14,\n backgroundColor: theme.surfaceRaised,\n borderColor: theme.border,\n opacity: enter,\n transform: [{ translateX: translate }],\n },\n side === 'right'\n ? { right: horizontal + 64 }\n : { left: horizontal + 64 },\n ]}\n >\n <Pressable onPress={onDismiss} accessibilityRole=\"button\" accessibilityLabel={text}>\n <Text style={[styles.nudgeText, { color: theme.text, fontSize: theme.fontSize - 1 }]}>\n {text}\n </Text>\n </Pressable>\n </Animated.View>\n );\n}\n\nexport function GuideAILauncher({\n hideButton = false,\n position = 'bottom-right',\n offset,\n icon,\n label = 'Open assistant',\n draggable = true,\n nudge = 'Need a hand? Ask me anything',\n nudgeDelay = 1200,\n nudgeDuration = 5000,\n children,\n ...chatProps\n}: GuideAILauncherProps): React.ReactElement {\n const [isOpen, setIsOpen] = useState(false);\n const { checklist } = useOnboarding();\n const autoOpenedFor = useRef<string | null>(null);\n // Which side of the handoff the sheet is showing. Held here rather than\n // in GuideAIChat so the assistant conversation is not unmounted while\n // the visitor talks to a person — going back returns to it intact.\n const [view, setView] = useState<'assistant' | 'support'>('assistant');\n const theme = resolveTheme(chatProps.theme as GuideAIThemeOverrides | undefined);\n\n useEffect(() => {\n if (checklist && !checklist.completed && !checklist.dismissed && checklist.start_mode === 'automatic' && autoOpenedFor.current !== checklist.id) {\n autoOpenedFor.current = checklist.id;\n setIsOpen(true);\n }\n }, [checklist?.id, checklist?.completed, checklist?.dismissed, checklist?.start_mode]);\n\n const effectiveNudge = checklist && !checklist.completed && !checklist.dismissed\n ? `Setup: ${checklist.total_required - checklist.completed_required} required steps remaining`\n : nudge;\n\n const open = useCallback(() => setIsOpen(true), []);\n const close = useCallback(() => setIsOpen(false), []);\n const toggle = useCallback(() => setIsOpen((previous) => !previous), []);\n\n const controls = useMemo<SheetControls>(\n () => ({ open, close, toggle, isOpen }),\n [open, close, toggle, isOpen],\n );\n\n const bottom = offset?.bottom ?? 28;\n const horizontal = offset?.horizontal ?? 20;\n\n // ── Dragging ───────────────────────────────────────────\n //\n // Position is held as an animated offset from the resting corner, so\n // the non-draggable case renders identically with no extra state.\n const FAB_SIZE = 56;\n const screen = Dimensions.get('window');\n const pan = useRef(new Animated.ValueXY({ x: 0, y: 0 })).current;\n // Read synchronously by the responder; Animated values cannot be.\n const panValue = useRef({ x: 0, y: 0 });\n const [dragging, setDragging] = useState(false);\n\n useEffect(() => {\n const id = pan.addListener((value) => { panValue.current = value; });\n return () => pan.removeListener(id);\n }, [pan]);\n\n const responder = useMemo(() => PanResponder.create({\n // Claim the gesture only once it is clearly a drag, so a tap still\n // reaches the button's onPress.\n onMoveShouldSetPanResponder: (_event, gesture) =>\n draggable && (Math.abs(gesture.dx) > 4 || Math.abs(gesture.dy) > 4),\n\n onPanResponderGrant: () => {\n setDragging(true);\n pan.setOffset({ ...panValue.current });\n pan.setValue({ x: 0, y: 0 });\n },\n\n onPanResponderMove: Animated.event(\n [null, { dx: pan.x, dy: pan.y }],\n // PanResponder has no native-driver equivalent; the values feed a\n // transform, which stays smooth enough for one small view.\n { useNativeDriver: false },\n ),\n\n onPanResponderRelease: () => {\n pan.flattenOffset();\n setDragging(false);\n\n const restingX = position === 'bottom-right'\n ? screen.width - horizontal - FAB_SIZE\n : horizontal;\n const currentX = restingX + panValue.current.x;\n const currentY = screen.height - bottom - FAB_SIZE + panValue.current.y;\n\n // Snap to the nearer edge: a button left mid-screen covers more\n // than one parked against a side.\n const snapLeft = currentX + FAB_SIZE / 2 < screen.width / 2;\n const targetX = (snapLeft ? horizontal : screen.width - horizontal - FAB_SIZE) - restingX;\n\n // Keep it on screen vertically, clear of the status bar and the\n // bottom inset.\n const minY = -(screen.height - bottom - FAB_SIZE - 60);\n const maxY = bottom - 12;\n const targetY = Math.min(Math.max(panValue.current.y, minY), maxY);\n\n Animated.spring(pan, {\n toValue: { x: targetX, y: targetY },\n useNativeDriver: false,\n friction: 7,\n tension: 60,\n }).start();\n },\n }), [draggable, pan, position, screen.width, screen.height, horizontal, bottom]);\n\n\n // Shown once per launch, and never while the sheet is open or the\n // button is hidden — a tooltip pointing at a control that is not there\n // is worse than no tooltip.\n const [nudgeVisible, setNudgeVisible] = useState(false);\n const nudgeShown = useRef(false);\n\n useEffect(() => {\n if (!effectiveNudge || hideButton || nudgeShown.current) return;\n\n const show = setTimeout(() => {\n nudgeShown.current = true;\n setNudgeVisible(true);\n }, nudgeDelay);\n return () => clearTimeout(show);\n }, [effectiveNudge, hideButton, nudgeDelay]);\n\n useEffect(() => {\n if (!nudgeVisible) return;\n const hide = setTimeout(() => setNudgeVisible(false), nudgeDuration);\n return () => clearTimeout(hide);\n }, [nudgeVisible, nudgeDuration]);\n\n // Opening the assistant makes the hint redundant.\n useEffect(() => {\n if (isOpen) setNudgeVisible(false);\n }, [isOpen]);\n\n // So does moving the button: the tooltip is anchored to the resting\n // corner, so after a drag it would point at empty space. A user who\n // is dragging the button has plainly already noticed it.\n useEffect(() => {\n if (dragging) setNudgeVisible(false);\n }, [dragging]);\n\n return (\n <SheetContext.Provider value={controls}>\n {children}\n\n {nudgeVisible && effectiveNudge && !hideButton ? (\n <Nudge\n text={effectiveNudge}\n side={position === 'bottom-right' ? 'right' : 'left'}\n bottom={bottom}\n horizontal={horizontal}\n theme={theme}\n onDismiss={() => setNudgeVisible(false)}\n />\n ) : null}\n\n {!hideButton ? (\n <Animated.View\n {...(draggable ? responder.panHandlers : {})}\n style={[\n styles.fabWrap,\n { bottom },\n position === 'bottom-right' ? { right: horizontal } : { left: horizontal },\n { transform: pan.getTranslateTransform() },\n ]}\n >\n <Pressable\n onPress={open}\n accessibilityRole=\"button\"\n accessibilityLabel={checklist && !checklist.completed ? `Open setup checklist, ${checklist.total_required - checklist.completed_required} steps remaining` : label}\n style={[\n styles.fab,\n { backgroundColor: theme.accent },\n // Lifts under the finger, so a drag reads as picked up.\n dragging ? styles.fabDragging : null,\n ]}\n >\n {icon ?? <View style={[styles.fabOrb, { backgroundColor: theme.onAccent }]} />}\n </Pressable>\n </Animated.View>\n ) : null}\n\n <Modal\n visible={isOpen}\n animationType=\"slide\"\n presentationStyle=\"pageSheet\"\n // Android has no pageSheet; without this the sheet renders on a\n // transparent ground and the app shows through behind the text.\n statusBarTranslucent={Platform.OS === 'android'}\n onRequestClose={close}\n >\n <View style={{ flex: 1, backgroundColor: theme.background }}>\n {/* Both stay mounted: `display: none` rather than unmounting, so\n switching to support and back does not throw away the\n assistant's scroll position or its in-flight turn. */}\n <View style={view === 'assistant' ? styles.pane : styles.paneHidden}>\n <GuideAIChat\n {...chatProps}\n onClose={close}\n // A host handler wins — they may have their own desk to send\n // people to. Otherwise the built-in thread opens.\n onTalkToHuman={chatProps.onTalkToHuman ?? (() => setView('support'))}\n />\n </View>\n\n {view === 'support' ? (\n <View style={styles.pane}>\n <GuideAISupportChat\n theme={chatProps.theme}\n topInset={chatProps.topInset}\n onClose={close}\n onBackToAssistant={() => setView('assistant')}\n />\n </View>\n ) : null}\n </View>\n </Modal>\n </SheetContext.Provider>\n );\n}\n\n/**\n * The default floating button on its own, for a host that wants to place\n * it inside their own layout rather than have it float.\n */\nexport function GuideAIButton({\n icon, label = 'Open assistant', theme: overrides, size = 56,\n}: {\n icon?: React.ReactNode;\n label?: string;\n theme?: GuideAIThemeOverrides;\n size?: number;\n}): React.ReactElement {\n const { open } = useGuideAISheet();\n const theme = resolveTheme(overrides);\n\n return (\n <Pressable\n onPress={open}\n accessibilityRole=\"button\"\n accessibilityLabel={label}\n style={{\n width: size, height: size, borderRadius: size / 2,\n backgroundColor: theme.accent, alignItems: 'center', justifyContent: 'center',\n }}\n >\n {icon ?? <View style={[styles.fabOrb, { backgroundColor: theme.onAccent }]} />}\n </Pressable>\n );\n}\n\nconst styles = StyleSheet.create({\n pane: { ...StyleSheet.absoluteFillObject },\n paneHidden: { ...StyleSheet.absoluteFillObject, display: 'none' },\n fabWrap: { position: 'absolute' },\n fabDragging: { opacity: 0.9, transform: [{ scale: 1.08 }] },\n fab: {\n width: 56, height: 56, borderRadius: 28,\n alignItems: 'center', justifyContent: 'center',\n // Raised enough to read as floating above content, not part of it.\n shadowColor: '#000', shadowOpacity: 0.3,\n shadowRadius: 12, shadowOffset: { width: 0, height: 6 },\n elevation: 8,\n },\n fabOrb: { width: 18, height: 18, borderRadius: 9 },\n nudge: {\n position: 'absolute',\n maxWidth: 220,\n paddingHorizontal: 14, paddingVertical: 10,\n borderRadius: 16, borderWidth: StyleSheet.hairlineWidth,\n shadowColor: '#000', shadowOpacity: 0.25,\n shadowRadius: 10, shadowOffset: { width: 0, height: 4 },\n elevation: 6,\n },\n nudgeText: { lineHeight: 19 },\n});\n"],"names":["styles","Bubble","_a"],"mappings":"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"}