imsg-mcp 1.3.0 → 1.3.2

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/dist/tui.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tui.js","sources":["../src/font-detect.ts","../src/url-schemes.ts","../src/tui/themes/ThemeContext.tsx","../src/tui/components/CommandPalette.tsx","../src/tui/components/ComposeRecipientModal.tsx","../src/tui/components/DatePicker.tsx","../src/tui/components/DateJumpModal.tsx","../src/tui/components/DevStats.tsx","../src/tui/components/ExportModal.tsx","../src/tui/components/HelpBar.tsx","../src/tui/themes/glyphs.ts","../src/tui/themes/color.ts","../src/tui/themes/palette.ts","../src/tui/theme.ts","../src/tui/components/MessageDrawer.tsx","../src/tui/components/SendViaModal.tsx","../src/tui/filter.ts","../src/tui/keymap.ts","../src/tui/modules/analytics/Pane.tsx","../src/tui/modules/analytics/SidebarItem.tsx","../src/tui/modules/analytics/module.tsx","../src/tui/modules/registry.ts","../src/visual-width.ts","../src/tui/components/ConversationItem.tsx","../src/tui/components/DefaultModuleSidebarItem.tsx","../src/tui/components/Sidebar.tsx","../src/tui/components/StatusBar.tsx","../src/tui/components/ComposeBar.tsx","../src/tui/components/MessageBubble.tsx","../src/tui/components/ThreadPane.tsx","../src/tui/hooks/useDevStats.ts","../src/tui/messageCache.ts","../src/tui/hooks/useImsg.ts","../src/tui/hooks/useMouse.ts","../src/tui/types.ts","../src/tui/App.tsx","../src/tui/index.tsx"],"sourcesContent":["/**\n * Best-effort Nerd-Font detection.\n *\n * Used by the TUI startup path to warn the user when they've selected the\n * `powerline` glyph preset but no Nerd Font is installed on the system —\n * which would otherwise render as silent blank/box-of-question-mark glyphs.\n *\n * Strategy: run `fc-list :family` (fontconfig). If it's not on PATH we\n * report `unavailable` rather than guessing — the warning is downgraded\n * to a soft hint in that case so we don't false-positive against the many\n * users who do have a Nerd Font but no `fc-list` (default macOS).\n *\n * Synchronous + short timeout so TUI startup isn't gated on a slow shell.\n */\n\nimport { spawnSync } from \"node:child_process\";\n\nexport type FontDetectResult =\n | { detected: true; source: \"fc-list\" }\n | { detected: false; source: \"fc-list\" }\n | { detected: null; source: \"unavailable\"; reason: string };\n\nconst FC_LIST_TIMEOUT_MS = 1000;\n\nlet cached: FontDetectResult | null = null;\n\n/**\n * Returns whether a Nerd Font is installed.\n *\n * `detected: null` means we couldn't tell (no `fc-list`, or it errored).\n * Cached per-process so repeated calls are free.\n */\nexport function detectNerdFont(): FontDetectResult {\n if (cached) return cached;\n\n const result = spawnSync(\"fc-list\", [\":family\"], {\n encoding: \"utf8\",\n timeout: FC_LIST_TIMEOUT_MS,\n // Suppress stderr noise like \"Fontconfig error: ...\" from cluttering\n // the user's terminal — we only care about stdout + exit status.\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n });\n\n let next: FontDetectResult;\n if (result.error || result.status !== 0) {\n next = {\n detected: null,\n source: \"unavailable\",\n reason: result.error?.message ?? `fc-list exited with status ${result.status}`,\n };\n } else {\n const stdout = result.stdout ?? \"\";\n // Match any family name containing \"Nerd\" (case-insensitive). All Nerd\n // Font patched families include the \"Nerd Font\" suffix.\n next = { detected: /Nerd/i.test(stdout), source: \"fc-list\" };\n }\n cached = next;\n return next;\n}\n\n/** Test-only: reset the per-process detection cache. */\nexport function _resetDetectNerdFontCache(): void {\n cached = null;\n}\n","/**\n * macOS URL-scheme dispatch helpers.\n *\n * Detects which chat/comms apps are installed in /Applications and builds\n * deep-link URIs that, when passed to `open <uri>`, launch the target app\n * focused on a specific contact. Designed for the TUI's `O` (open in\n * Messages) and `S` (send-via external app) keybinds.\n *\n * Body-carrying support varies by app:\n * - sms:, whatsapp:, tg:, mailto: → body/text param honored\n * - imessage:, sgnl:, facetime: → recipient only, body stripped\n */\n\nimport { existsSync } from \"node:fs\";\n\nexport interface ChatAppDef {\n /** Display name shown in TUI list. */\n name: string;\n /** /Applications/ bundle to probe. */\n appPath: string;\n /** Build the URL given a handle + optional body text. Returns null if the\n * handle isn't compatible (e.g. Signal can't take an email). */\n buildUri: (handle: string, body?: string) => string | null;\n /** True if `body` is honored by the URL scheme. */\n supportsBody: boolean;\n}\n\nconst APPS: ChatAppDef[] = [\n {\n name: \"Messages\",\n appPath: \"/System/Applications/Messages.app\",\n buildUri: (handle) => `imessage://${encodeURIComponent(handle)}`,\n supportsBody: false,\n },\n {\n name: \"FaceTime\",\n appPath: \"/System/Applications/FaceTime.app\",\n buildUri: (handle) => `facetime://${encodeURIComponent(handle)}`,\n supportsBody: false,\n },\n {\n name: \"FaceTime Audio\",\n appPath: \"/System/Applications/FaceTime.app\",\n buildUri: (handle) => `facetime-audio://${encodeURIComponent(handle)}`,\n supportsBody: false,\n },\n {\n name: \"Signal\",\n appPath: \"/Applications/Signal.app\",\n buildUri: (handle) => {\n // Signal only accepts phone numbers.\n const phone = handle.replace(/[^\\d+]/g, \"\");\n if (!phone || !phone.startsWith(\"+\")) return null;\n return `sgnl://send?phone=${encodeURIComponent(phone)}`;\n },\n supportsBody: false,\n },\n {\n name: \"WhatsApp\",\n appPath: \"/Applications/WhatsApp.app\",\n buildUri: (handle, body) => {\n const phone = handle.replace(/[^\\d+]/g, \"\").replace(/^\\+/, \"\");\n if (!phone) return null;\n const base = `whatsapp://send?phone=${encodeURIComponent(phone)}`;\n return body ? `${base}&text=${encodeURIComponent(body)}` : base;\n },\n supportsBody: true,\n },\n {\n name: \"Telegram\",\n appPath: \"/Applications/Telegram.app\",\n buildUri: (handle, body) => {\n const phone = handle.replace(/[^\\d+]/g, \"\");\n if (!phone) return null;\n const base = `tg://resolve?phone=${encodeURIComponent(phone)}`;\n return body ? `${base}&text=${encodeURIComponent(body)}` : base;\n },\n supportsBody: true,\n },\n {\n name: \"SMS\",\n appPath: \"/System/Applications/Messages.app\",\n buildUri: (handle, body) => {\n // sms: works for both numbers and email (Messages.app fallback).\n const base = `sms:${encodeURIComponent(handle)}`;\n return body ? `${base}&body=${encodeURIComponent(body)}` : base;\n },\n supportsBody: true,\n },\n];\n\n/**\n * Return the apps installed on this Mac, in the order they're declared.\n * Messages and FaceTime are typically always present; third-party apps\n * (Signal, WhatsApp, Telegram) require the app to be installed.\n */\nexport function getInstalledChatApps(): ChatAppDef[] {\n return APPS.filter((a) => existsSync(a.appPath));\n}\n\n/**\n * Build the right URI for a specific app by name (case-insensitive). Returns\n * null if the app is not installed OR the handle isn't compatible with the\n * app's URL scheme.\n */\nexport function buildChatAppUri(\n appName: string,\n handle: string,\n body?: string,\n): { uri: string; supportsBody: boolean } | null {\n const lower = appName.toLowerCase();\n const app = APPS.find((a) => a.name.toLowerCase() === lower);\n if (!app) return null;\n if (!existsSync(app.appPath)) return null;\n const uri = app.buildUri(handle, body);\n if (!uri) return null;\n return { uri, supportsBody: app.supportsBody };\n}\n","/**\n * React context that delivers a fully-resolved Theme to every TUI\n * component. Replaces the legacy pattern of `import { theme } from \"../theme\"`\n * — components now `const theme = useTheme()` so the same component tree\n * can render with different accents / glyph presets.\n *\n * Lookup outside a provider throws — TypeScript would let it slide\n * silently otherwise (default value), and a missing provider is a\n * developer error we want to surface during the first render.\n */\n\nimport { createContext, type ReactNode, useContext } from \"react\";\nimport type { Theme } from \"../theme.js\";\n\nconst ThemeCtx = createContext<Theme | null>(null);\n\nexport function ThemeProvider({ value, children }: { value: Theme; children: ReactNode }) {\n return <ThemeCtx.Provider value={value}>{children}</ThemeCtx.Provider>;\n}\n\nexport function useTheme(): Theme {\n const t = useContext(ThemeCtx);\n if (!t) {\n throw new Error(\"useTheme(): no <ThemeProvider> in the component tree above this hook\");\n }\n return t;\n}\n","/**\n * CommandPalette — fuzzy-searchable list of every action in the TUI.\n *\n * Empty query → grouped cheat sheet of all commands by category (acts as\n * the canonical keybinding reference). Typing → fuzzy rank via\n * `rankFuzzy` from `src/fuzzy.ts`.\n *\n * Input model: the palette owns its own `useInput` (gated on `mode === \"palette\"`\n * via App.tsx). Enter runs the highlighted command; arrows / Ctrl-n/Ctrl-p\n * move the cursor; Esc / Ctrl-P close.\n */\nimport { TextInput } from \"@inkjs/ui\";\nimport { Box, Text, useInput } from \"ink\";\nimport { useEffect, useMemo } from \"react\";\nimport { rankFuzzy } from \"../../fuzzy.js\";\nimport type { Command, CommandContext } from \"../keymap.js\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\n\ninterface Props {\n commands: Command[];\n query: string;\n cursor: number;\n width: number;\n height: number;\n ctx: CommandContext;\n onQueryChange: (q: string) => void;\n onCursorMove: (delta: number) => void;\n onSelectCursor: (idx: number) => void;\n onClose: () => void;\n}\n\ninterface RankedRow {\n command: Command;\n score: number;\n /** Only set in empty-query mode — the first row of a new category prints a header. */\n headerCategory?: string;\n}\n\n/** Build the rows to render: fuzzy-ranked when there's a query, grouped otherwise. */\nfunction buildRows(commands: Command[], query: string): RankedRow[] {\n const q = query.trim();\n if (q === \"\") {\n // Empty query — list every command grouped by category.\n const byCat = new Map<string, Command[]>();\n for (const c of commands) {\n if (!byCat.has(c.category)) byCat.set(c.category, []);\n byCat.get(c.category)!.push(c);\n }\n const rows: RankedRow[] = [];\n for (const [cat, cmds] of byCat) {\n let first = true;\n for (const cmd of cmds) {\n rows.push({ command: cmd, score: 1, headerCategory: first ? cat : undefined });\n first = false;\n }\n }\n return rows;\n }\n // Lower minScore (0.3) — palette queries are short and abbreviated.\n const ranked = rankFuzzy(\n q,\n commands,\n (c) => `${c.title} ${c.category} ${c.description ?? \"\"} ${c.keybinding ?? \"\"}`,\n 0.3,\n );\n return ranked.map((r) => ({ command: r.item, score: r.score }));\n}\n\nexport function CommandPalette({\n commands,\n query,\n cursor,\n width,\n height,\n ctx,\n onQueryChange,\n onCursorMove,\n onSelectCursor,\n onClose,\n}: Props) {\n const theme = useTheme();\n\n const rows = useMemo(() => buildRows(commands, query), [commands, query]);\n\n // Clamp the cursor if results shrank below it (e.g. user added a letter\n // that filtered out previously-visible entries).\n useEffect(() => {\n if (rows.length === 0) return;\n if (cursor > rows.length - 1) onSelectCursor(rows.length - 1);\n }, [rows.length, cursor, onSelectCursor]);\n\n // Visible window — keep the cursor on screen.\n const headerH = 3; // title + query + spacer\n const footerH = 1;\n const bodyH = Math.max(3, height - headerH - footerH - 2);\n const visibleStart = Math.max(0, Math.min(cursor - Math.floor(bodyH / 2), rows.length - bodyH));\n const visibleEnd = Math.min(rows.length, visibleStart + bodyH);\n const visibleRows = rows.slice(visibleStart, visibleEnd);\n\n useInput((input, key) => {\n if (key.escape || (key.ctrl && input === \"p\")) {\n onClose();\n return;\n }\n if (key.return) {\n const row = rows[cursor];\n if (row) {\n // Close BEFORE running so commands that themselves change mode (e.g.\n // ENTER_COMPOSE) end up in the right final state, rather than being\n // overridden by a trailing CLOSE_PALETTE.\n onClose();\n void Promise.resolve(row.command.run(ctx)).catch(() => {});\n }\n return;\n }\n if (key.downArrow || (key.ctrl && input === \"n\")) {\n onCursorMove(1);\n return;\n }\n if (key.upArrow || (key.ctrl && input === \"p\")) {\n onCursorMove(-1);\n return;\n }\n if (key.pageDown) {\n onCursorMove(bodyH);\n return;\n }\n if (key.pageUp) {\n onCursorMove(-bodyH);\n return;\n }\n });\n\n const modalWidth = Math.min(Math.max(60, Math.floor(width * 0.7)), 120);\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"double\"\n borderColor={theme.status.accent}\n backgroundColor={theme.header.dim.bg}\n paddingX={1}\n flexShrink={0}\n width={modalWidth}\n >\n <Box flexShrink={0}>\n <Text color={theme.status.accent} bold>\n Command Palette\n </Text>\n <Text color={theme.help.desc}>\n {\" \"}({rows.length} {rows.length === 1 ? \"command\" : \"commands\"})\n </Text>\n </Box>\n\n <Box flexShrink={0}>\n <Text color={theme.info.label}>{\"› \"}</Text>\n <TextInput defaultValue={query} onChange={onQueryChange} placeholder=\"Search commands…\" />\n </Box>\n\n <Box flexDirection=\"column\" flexShrink={0}>\n {rows.length === 0 ? (\n <Box>\n <Text color={theme.help.desc}>No commands match \"{query}\".</Text>\n </Box>\n ) : (\n visibleRows.map((row, i) => {\n const realIdx = visibleStart + i;\n const selected = realIdx === cursor;\n return (\n <PaletteRow\n key={`${row.command.id}-${realIdx}`}\n row={row}\n selected={selected}\n width={modalWidth - 4}\n />\n );\n })\n )}\n </Box>\n\n <Box flexShrink={0}>\n <Text color={theme.help.key}>↑↓</Text>\n <Text color={theme.help.desc}>:move </Text>\n <Text color={theme.help.key}>Enter</Text>\n <Text color={theme.help.desc}>:run </Text>\n <Text color={theme.help.key}>Esc</Text>\n <Text color={theme.help.desc}>:close</Text>\n </Box>\n </Box>\n );\n}\n\nfunction PaletteRow({\n row,\n selected,\n width,\n}: {\n row: RankedRow;\n selected: boolean;\n width: number;\n}) {\n const theme = useTheme();\n const keybindW = 14;\n const titleW = Math.max(20, width - keybindW - 2);\n const { command, headerCategory } = row;\n const title = command.title;\n const desc = command.description ?? \"\";\n\n return (\n <Box flexDirection=\"column\">\n {headerCategory && (\n <Box>\n <Text color={theme.help.desc} dimColor>\n ── {headerCategory} ──\n </Text>\n </Box>\n )}\n <Box backgroundColor={selected ? theme.sidebar.selected : undefined}>\n <Box width={titleW}>\n <Text color={selected ? theme.sidebar.selectedFg : undefined}>\n {selected ? \"▸ \" : \" \"}\n </Text>\n <Text\n color={selected ? theme.sidebar.selectedFg : theme.info.value}\n bold={selected}\n wrap=\"truncate\"\n >\n {title}\n </Text>\n {desc && (\n <Text color={theme.help.desc} wrap=\"truncate\">\n {\" \"}\n {desc}\n </Text>\n )}\n </Box>\n <Box width={keybindW} justifyContent=\"flex-end\">\n {command.keybinding ? (\n <Text color={theme.help.key}>{command.keybinding}</Text>\n ) : (\n <Text color={theme.help.desc} dimColor>\n —\n </Text>\n )}\n </Box>\n </Box>\n </Box>\n );\n}\n","/**\n * ComposeRecipientModal — two-stage compose for sending to a new thread.\n *\n * Triggered by `N` (or `c` from sidebar when no thread is selected).\n *\n * Stage 1: recipient entry\n * - Live-validated as the user types. Resolution surfaces:\n * - \"phone\" / \"email\" badge for direct handles\n * - \"contact\" badge for resolved contact-name lookups\n * - Candidate list when the typed string matches multiple contacts\n * - Error message when nothing matches\n * - Tab or Enter commits to stage 2 (only when a single resolution is\n * locked in).\n *\n * Stage 2: message body\n * - Standard text input. Enter sends, Esc cancels back to stage 1.\n *\n * The modal is opaque (backgroundColor) and uses flexShrink={0} on every\n * row — same defensive layout used by the SendViaModal fix from the live\n * audit pass.\n */\nimport { TextInput } from \"@inkjs/ui\";\nimport { Box, Text, useInput } from \"ink\";\nimport { useMemo, useState } from \"react\";\nimport type { RecipientResolution, ResolvedRecipient } from \"../../recipient.js\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\n\ntype Stage = \"recipient\" | \"body\";\n\ninterface Props {\n /** Live resolver — called on every keystroke against the trimmed input. */\n resolve: (input: string) => RecipientResolution;\n /** Final send. Returns {success, error?}. */\n onSend: (handle: string, text: string) => Promise<{ success: boolean; error?: string }>;\n /** Close without sending. */\n onCancel: () => void;\n}\n\nexport function ComposeRecipientModal({ resolve, onSend, onCancel }: Props) {\n const theme = useTheme();\n const [stage, setStage] = useState<Stage>(\"recipient\");\n const [recipientInput, setRecipientInput] = useState(\"\");\n const [lockedRecipient, setLockedRecipient] = useState<ResolvedRecipient | null>(null);\n const [body, setBody] = useState(\"\");\n const [status, setStatus] = useState<string>(\"\");\n\n const resolution = useMemo<RecipientResolution>(\n () => (recipientInput.trim() ? resolve(recipientInput) : { kind: \"error\", message: \"\" }),\n [recipientInput, resolve],\n );\n\n // Esc cancels at any stage. We listen at modal level so it fires\n // regardless of which inner input has focus.\n // Digit 1-9 picks an ambiguous-candidate when one is shown — same\n // pattern as the existing `contact:N` selector. Caps at the first 9\n // candidates (matches the visual cap below).\n useInput((input, key) => {\n if (key.escape) {\n if (stage === \"body\") {\n // Back out to recipient stage rather than full-cancel.\n setStage(\"recipient\");\n setStatus(\"\");\n return;\n }\n onCancel();\n return;\n }\n if (stage === \"recipient\" && resolution.kind === \"ambiguous\" && /^[1-9]$/.test(input)) {\n const idx = Number.parseInt(input, 10) - 1;\n const pick = resolution.candidates[idx];\n if (pick) {\n setLockedRecipient(pick);\n setStage(\"body\");\n setStatus(\"\");\n }\n }\n });\n\n const commitRecipient = () => {\n switch (resolution.kind) {\n case \"phone\":\n case \"email\":\n case \"contact\":\n setLockedRecipient(resolution);\n setStage(\"body\");\n setStatus(\"\");\n return;\n case \"ambiguous\":\n // Numbered picker shows in render; nudge user toward it.\n setStatus(`Ambiguous — ${resolution.candidates.length} matches. Press 1-9 to pick.`);\n return;\n case \"error\":\n setStatus(resolution.message);\n return;\n }\n };\n\n const handleSend = async () => {\n if (!lockedRecipient || !body.trim()) return;\n setStatus(\"Sending…\");\n const result = await onSend(lockedRecipient.handle, body.trim());\n if (result.success) {\n onCancel(); // close modal on success — caller handles UX (status toast)\n } else {\n setStatus(`Send failed: ${result.error ?? \"unknown error\"}`);\n }\n };\n\n // Render\n // Show the normalized handle in the badge when it differs from the raw\n // input — gives the user transparency about how their typing was parsed.\n // E.g. typing `0401990797` shows `[phone → +61401990797]`, typing\n // `+61401990797` directly shows just `[phone]` (no transformation).\n const badge = (() => {\n const trimmedInput = recipientInput.trim();\n const showResolved = (handle: string) =>\n handle.toLowerCase() === trimmedInput.toLowerCase() ? \"\" : ` → ${handle}`;\n if (resolution.kind === \"phone\") return `[phone${showResolved(resolution.handle)}]`;\n if (resolution.kind === \"email\") return `[email${showResolved(resolution.handle)}]`;\n if (resolution.kind === \"contact\") return `[contact → ${resolution.displayName}]`;\n if (resolution.kind === \"ambiguous\") return `[${resolution.candidates.length} matches]`;\n return \"\";\n })();\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"double\"\n borderColor={theme.status.accent}\n backgroundColor={theme.header.dim.bg}\n paddingX={1}\n flexShrink={0}\n >\n <Box flexShrink={0}>\n <Text color={theme.status.accent} bold>\n {stage === \"recipient\" ? \"New message — recipient\" : \"New message — body\"}\n </Text>\n </Box>\n\n {stage === \"recipient\" && (\n <>\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>To: </Text>\n <TextInput\n defaultValue=\"\"\n placeholder=\"phone, email, or contact name\"\n onChange={(v) => setRecipientInput(v)}\n onSubmit={commitRecipient}\n />\n {badge && <Text color={theme.info.label}>{` ${badge}`}</Text>}\n </Box>\n {resolution.kind === \"ambiguous\" && (\n <Box flexDirection=\"column\" flexShrink={0}>\n {resolution.candidates.slice(0, 9).map((c, i) => (\n <Box key={c.handle} flexShrink={0}>\n <Text color={theme.help.key} bold>{` ${i + 1}`}</Text>\n <Text color={theme.help.desc}>{`: ${c.displayName}`}</Text>\n </Box>\n ))}\n {resolution.candidates.length > 9 && (\n <Box flexShrink={0}>\n <Text\n color={theme.help.desc}\n >{` …and ${resolution.candidates.length - 9} more (refine search)`}</Text>\n </Box>\n )}\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>Press 1-9 to pick a match</Text>\n </Box>\n </Box>\n )}\n {resolution.kind === \"error\" && resolution.message && (\n <Box flexShrink={0}>\n <Text color={theme.edited}>{resolution.message}</Text>\n </Box>\n )}\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>Enter: continue · Esc: cancel</Text>\n </Box>\n </>\n )}\n\n {stage === \"body\" && lockedRecipient && (\n <>\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>{`To: ${lockedRecipient.displayName}`}</Text>\n </Box>\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>{\"> \"}</Text>\n <TextInput\n defaultValue=\"\"\n placeholder=\"Type your message…\"\n onChange={(v) => setBody(v)}\n onSubmit={handleSend}\n />\n </Box>\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>Enter: send · Esc: back to recipient</Text>\n </Box>\n </>\n )}\n\n {status && (\n <Box flexShrink={0}>\n <Text color={theme.edited}>{status}</Text>\n </Box>\n )}\n </Box>\n );\n}\n","/**\n * Three-field date picker (YYYY / MM / DD) with arrow-key navigation.\n *\n * ←/→ cycle active field\n * ↑/↓ increment/decrement value\n * 0–9 type digit (right-shifts onto the field; full field auto-advances)\n * Bksp delete last digit of active field\n * Enter submit\n *\n * Returns the picked date as an ISO string (\"YYYY-MM-DD\") to keep the modal's\n * existing parser path unchanged.\n */\nimport { Box, Text, useInput } from \"ink\";\nimport { useState } from \"react\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\n\ntype Field = \"year\" | \"month\" | \"day\";\nconst ORDER: Field[] = [\"year\", \"month\", \"day\"];\n\ninterface Props {\n initial?: Date;\n focused: boolean;\n onSubmit: (isoDate: string) => void;\n onCancel: () => void;\n}\n\nconst MIN_YEAR = 1900;\nconst MAX_YEAR = 2100;\n\nfunction daysInMonth(year: number, monthOneBased: number): number {\n return new Date(year, monthOneBased, 0).getDate();\n}\n\nfunction clamp(n: number, lo: number, hi: number): number {\n return Math.min(Math.max(n, lo), hi);\n}\n\nexport function DatePicker({ initial, focused, onSubmit, onCancel }: Props) {\n const theme = useTheme();\n const now = initial ?? new Date();\n const [year, setYear] = useState(now.getFullYear());\n const [month, setMonth] = useState(now.getMonth() + 1);\n const [day, setDay] = useState(now.getDate());\n const [active, setActive] = useState<Field>(\"year\");\n\n const fieldValue = (f: Field) => (f === \"year\" ? year : f === \"month\" ? month : day);\n\n const setField = (f: Field, v: number) => {\n if (f === \"year\") {\n const y = clamp(v, MIN_YEAR, MAX_YEAR);\n setYear(y);\n // Re-clamp day to month/year (Feb 29 → Feb 28 on non-leap)\n setDay((d) => clamp(d, 1, daysInMonth(y, month)));\n } else if (f === \"month\") {\n const m = clamp(v, 1, 12);\n setMonth(m);\n setDay((d) => clamp(d, 1, daysInMonth(year, m)));\n } else {\n setDay(clamp(v, 1, daysInMonth(year, month)));\n }\n };\n\n useInput(\n (input, key) => {\n if (!focused) return;\n if (key.escape) {\n onCancel();\n return;\n }\n if (key.return) {\n const iso = `${String(year).padStart(4, \"0\")}-${String(month).padStart(2, \"0\")}-${String(day).padStart(2, \"0\")}`;\n onSubmit(iso);\n return;\n }\n if (key.leftArrow) {\n const i = ORDER.indexOf(active);\n setActive(ORDER[(i + ORDER.length - 1) % ORDER.length]);\n return;\n }\n if (key.rightArrow) {\n const i = ORDER.indexOf(active);\n setActive(ORDER[(i + 1) % ORDER.length]);\n return;\n }\n if (key.upArrow) {\n setField(active, fieldValue(active) + 1);\n return;\n }\n if (key.downArrow) {\n setField(active, fieldValue(active) - 1);\n return;\n }\n if (input && /^[0-9]$/.test(input)) {\n const digit = Number.parseInt(input, 10);\n if (active === \"year\") {\n // Shift left and append. Year field is 4 digits.\n const next = (year % 1000) * 10 + digit;\n setField(\"year\", next);\n } else if (active === \"month\") {\n // 2-digit shift\n const next = (month % 10) * 10 + digit;\n setField(\"month\", next < 1 ? 1 : next);\n } else {\n const next = (day % 10) * 10 + digit;\n setField(\"day\", next < 1 ? 1 : next);\n }\n return;\n }\n if (key.backspace || key.delete) {\n if (active === \"year\") setField(\"year\", Math.floor(year / 10) || MIN_YEAR);\n else if (active === \"month\") setField(\"month\", Math.floor(month / 10) || 1);\n else setField(\"day\", Math.floor(day / 10) || 1);\n }\n },\n { isActive: focused },\n );\n\n const fieldStr = (f: Field, width: number) => String(fieldValue(f)).padStart(width, \"0\");\n\n const renderField = (f: Field, width: number) => {\n const isActive = active === f;\n return (\n <Text\n color={isActive ? theme.status.accent : theme.drawer.value}\n bold={isActive}\n inverse={isActive}\n >\n {fieldStr(f, width)}\n </Text>\n );\n };\n\n return (\n <Box flexDirection=\"column\">\n <Box>\n {renderField(\"year\", 4)}\n <Text color={theme.help.desc}>-</Text>\n {renderField(\"month\", 2)}\n <Text color={theme.help.desc}>-</Text>\n {renderField(\"day\", 2)}\n </Box>\n <Box>\n <Text color={theme.help.desc}>←/→ field · ↑/↓ adjust · digits to type · Enter to jump</Text>\n </Box>\n </Box>\n );\n}\n","/**\n * Date-jump modal — two input modes:\n * - picker (default): three-field YYYY-MM-DD picker, arrow-keys for navigation.\n * - text: free-form text input, parsed via `parseUserDate` (handles ISO,\n * keywords like \"yesterday\", relative phrases like \"2 weeks ago\",\n * compact \"5d/2w/3m/1y\", etc).\n *\n * `Tab` (when the inner field doesn't claim it) toggles between modes. The\n * existing caller path is preserved: `onSubmit` always receives a string,\n * either a free-form expression (text mode) or a normalized ISO date\n * (picker mode). Both flow through `parseUserDate` downstream.\n */\nimport { TextInput } from \"@inkjs/ui\";\nimport { Box, Text, useInput } from \"ink\";\nimport { useState } from \"react\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\nimport { DatePicker } from \"./DatePicker.js\";\n\ntype Mode = \"picker\" | \"text\";\n\ninterface Props {\n value: string;\n error: string;\n onChange: (v: string) => void;\n onSubmit: (v: string) => void;\n}\n\nexport function DateJumpModal({ value, error, onChange, onSubmit }: Props) {\n const theme = useTheme();\n const [mode, setMode] = useState<Mode>(\"picker\");\n\n // Tab swaps modes. Listen at the modal level so the swap fires regardless\n // of which inner widget has focus.\n useInput((_input, key) => {\n if (key.tab && !key.shift) {\n setMode((m) => (m === \"picker\" ? \"text\" : \"picker\"));\n }\n });\n\n // Opaque background + flexShrink={0} on every row matches the modal\n // discipline used by ComposeRecipientModal / SendViaModal — without\n // these, conversation-list slugs from the sidebar leak through the\n // modal's interior (visible bug in live tmux tests).\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"double\"\n borderColor={theme.status.accent}\n backgroundColor={theme.header.dim.bg}\n paddingX={1}\n flexShrink={0}\n >\n <Box flexShrink={0}>\n <Text color={theme.status.accent} bold>\n Jump to date\n </Text>\n <Text color={theme.help.desc}>\n {\" \"}[{mode}] · Tab to switch\n </Text>\n </Box>\n\n {mode === \"picker\" ? (\n <DatePicker\n focused\n onSubmit={onSubmit}\n onCancel={() => {\n // Esc handled by the App's date-jump exit branch — DatePicker\n // forwards it via onCancel but we leave actual exit to the parent.\n }}\n />\n ) : (\n <Box flexDirection=\"column\" flexShrink={0}>\n <Box flexShrink={0}>\n <Text color={theme.info.label}>Date: </Text>\n <TextInput\n defaultValue={value}\n onChange={onChange}\n onSubmit={onSubmit}\n placeholder=\"2024-03-15 | 3/15 | yesterday | 2 weeks ago | 1y\"\n />\n </Box>\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>\n Formats: YYYY-MM-DD · M/D · today · yesterday · N days/weeks/months/years ago · 5d /\n 2w / 3m / 1y\n </Text>\n </Box>\n </Box>\n )}\n\n {error && (\n <Box flexShrink={0}>\n <Text color={theme.edited}>{error}</Text>\n </Box>\n )}\n\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>Enter: jump · Esc: cancel · Tab: switch mode</Text>\n </Box>\n </Box>\n );\n}\n","import { Box, Text } from \"ink\";\nimport type { DevStatsData } from \"../hooks/useDevStats.js\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\n\ninterface Props {\n stats: DevStatsData;\n width: number;\n}\n\nexport function DevStats({ stats, width }: Props) {\n const theme = useTheme();\n const engineColor = stats.engine.startsWith(\"Rust\") ? theme.rustEngine : theme.status.accent;\n const cpuColor =\n stats.cpuPercent > 50 ? theme.cpuHigh : stats.cpuPercent > 20 ? theme.edited : theme.info.value;\n\n return (\n <Box\n flexDirection=\"column\"\n width={width}\n borderStyle=\"single\"\n borderColor={theme.border}\n overflow=\"hidden\"\n >\n <Box paddingX={1} backgroundColor={theme.header.dim.bg}>\n <Text color={theme.header.dim.fg} bold>\n Stats\n </Text>\n </Box>\n <Box flexDirection=\"column\" paddingX={1}>\n {/*\n * Engine row is the only stat whose value (\"Rust parser + TS DB\") is\n * wider than the narrow stats column. With `justifyContent=\"space-between\"`\n * Ink wraps the value mid-string and interleaves it with the label\n * (showing `EngiRust parser` / `e + TS DB`). Stack label-above-value\n * for this row so the value gets the full column width.\n */}\n <Text color={theme.info.label}>Engine</Text>\n <Text color={engineColor} bold>\n {stats.engine}\n </Text>\n <Box justifyContent=\"space-between\">\n <Text color={theme.info.label}>CPU</Text>\n <Text color={cpuColor}>{stats.cpuPercent}%</Text>\n </Box>\n <Box justifyContent=\"space-between\">\n <Text color={theme.info.label}>Mem</Text>\n <Text color={theme.info.value}>{stats.memMB}MB</Text>\n </Box>\n <Box justifyContent=\"space-between\">\n <Text color={theme.info.label}>PID</Text>\n <Text color={theme.info.value}>{stats.pid}</Text>\n </Box>\n <Box justifyContent=\"space-between\">\n <Text color={theme.info.label}>Up</Text>\n <Text color={theme.info.value}>{stats.uptime}</Text>\n </Box>\n {stats.lastQueryMs !== null && (\n <Box justifyContent=\"space-between\">\n <Text color={theme.info.label}>Query</Text>\n <Text color={stats.lastQueryMs > 500 ? theme.edited : theme.sms}>\n {stats.lastQueryMs}ms\n </Text>\n </Box>\n )}\n <Box justifyContent=\"space-between\">\n <Text color={theme.info.label}>Lag</Text>\n <Text\n color={\n stats.eventLoopP99Ms > 500\n ? theme.cpuHigh\n : stats.eventLoopP99Ms > 100\n ? theme.edited\n : theme.info.value\n }\n >\n {stats.eventLoopP99Ms}ms\n </Text>\n </Box>\n <Box justifyContent=\"space-between\">\n <Text color={theme.info.label}>Active</Text>\n <Text color={theme.info.value}>{stats.lastActivityAgo}</Text>\n </Box>\n </Box>\n </Box>\n );\n}\n\n/** Compact inline stats for the status bar (when full panel is hidden).\n *\n * Engine label is abbreviated (full \"Rust parser + TS DB\" → \"Rust+TS\")\n * so the bar fits on standard 80-col terminals without colliding with\n * the left-side service/handle segment. The full panel (toggled with\n * `d`) keeps the verbose label.\n *\n * flexShrink={0} keeps the gap={1} separators intact even when the\n * parent StatusBar's right-side Box is being squeezed by a long toast\n * — without it the row would render as \"TS0%0MB\" instead of \"TS 0% 0MB\".\n */\nexport function CompactStats({ stats }: { stats: DevStatsData }) {\n const theme = useTheme();\n const engineColor = stats.engine.startsWith(\"Rust\") ? theme.rustEngine : theme.status.accent;\n const compactEngine = stats.engine\n .replace(\"Rust parser + TS DB\", \"Rust+TS\")\n .replace(\"Rust parser + Rust DB\", \"Rust\")\n .replace(\"TS parser + TS DB\", \"TS\");\n return (\n <Box gap={1} flexShrink={0}>\n <Text color={engineColor}>{compactEngine}</Text>\n <Text color={theme.info.label}>{stats.cpuPercent}%</Text>\n <Text color={theme.info.label}>{stats.memMB}MB</Text>\n <Text color={theme.info.label}>PID:{stats.pid}</Text>\n </Box>\n );\n}\n","import { TextInput } from \"@inkjs/ui\";\nimport { Box, Text } from \"ink\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\n\ninterface Props {\n format: \"markdown\" | \"csv\" | \"json\";\n path: string;\n rangeSummary: string;\n onChangePath: (p: string) => void;\n onSubmit: () => void;\n}\n\n/**\n * Export modal — full-width banner at the top of the body area.\n * The format is cycled via Tab in App.tsx (which dispatches SET_EXPORT_FORMAT\n * before rendering this). The path is the only inline-editable field here.\n */\nexport function ExportModal({ format, path, rangeSummary, onChangePath, onSubmit }: Props) {\n const theme = useTheme();\n // Opaque background + flexShrink={0} on every row matches the modal\n // discipline used by ComposeRecipientModal / SendViaModal — prevents\n // the sidebar conversation list from leaking through the modal cells.\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"double\"\n borderColor={theme.status.accent}\n backgroundColor={theme.header.dim.bg}\n paddingX={1}\n flexShrink={0}\n >\n <Box flexShrink={0}>\n <Text color={theme.status.accent} bold>\n Export messages\n </Text>\n </Box>\n <Box flexShrink={0}>\n <Text color={theme.info.label}>Range: </Text>\n <Text color={theme.info.value}>{rangeSummary}</Text>\n </Box>\n <Box flexShrink={0}>\n <Text color={theme.info.label}>Format: </Text>\n <Text\n color={format === \"markdown\" ? theme.status.accent : theme.info.value}\n bold={format === \"markdown\"}\n >\n [Markdown]\n </Text>\n <Text> </Text>\n <Text\n color={format === \"csv\" ? theme.status.accent : theme.info.value}\n bold={format === \"csv\"}\n >\n [CSV]\n </Text>\n <Text> </Text>\n <Text\n color={format === \"json\" ? theme.status.accent : theme.info.value}\n bold={format === \"json\"}\n >\n [JSON]\n </Text>\n <Text color={theme.help.desc}> (Tab cycles)</Text>\n </Box>\n <Box flexShrink={0}>\n <Text color={theme.info.label}>Path: </Text>\n <TextInput\n defaultValue={path}\n onChange={onChangePath}\n onSubmit={onSubmit}\n placeholder=\"/absolute/path/to/file\"\n />\n </Box>\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>Enter: save Esc: cancel Tab: cycle format</Text>\n </Box>\n </Box>\n );\n}\n","import { Box, Text } from \"ink\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\nimport type { FocusPane, Mode } from \"../types.js\";\n\ninterface Props {\n mode: Mode;\n focus?: FocusPane;\n}\n\nconst PALETTE_HINT: [string, string] = [\"^P\", \"palette\"];\n\nconst SIDEBAR_KEYS = [\n [\"j/k\", \"move\"],\n [\"#j/k\", \"jump\"],\n [\"gg/G\", \"top/btm\"],\n [\"^d/u\", \"½page\"],\n [\"y\", \"copy slug\"],\n [\"N\", \"new msg\"],\n [\"Tab\", \"→msgs\"],\n [\"/\", \"filter\"],\n [\"d\", \"stats\"],\n [\"r\", \"refresh\"],\n PALETTE_HINT,\n [\"q\", \"quit\"],\n];\n\nconst THREAD_KEYS = [\n [\"j/k\", \"move\"],\n [\"#j/k\", \"jump\"],\n [\"{/}\", \"grp jump\"],\n [\"gg/G\", \"top/btm\"],\n [\"^d/u\", \"½page\"],\n [\"V\", \"select\"],\n [\":\", \"date jump\"],\n [\"Enter\", \"details\"],\n [\"o\", \"open att\"],\n [\"O\", \"open in Msgs\"],\n [\"S\", \"send-via\"],\n [\"c\", \"compose\"],\n PALETTE_HINT,\n [\"Tab\", \"→list\"],\n];\n\nconst PALETTE_KEYS = [\n [\"type\", \"filter\"],\n [\"↑↓\", \"move\"],\n [\"Enter\", \"run\"],\n [\"Esc\", \"close\"],\n];\n\nconst SEND_VIA_KEYS = [\n [\"1-9\", \"pick app\"],\n [\"Esc\", \"cancel\"],\n];\n\nconst SELECT_KEYS = [\n [\"j/k\", \"extend\"],\n [\"{/}\", \"grp\"],\n [\"gg/G\", \"top/btm\"],\n [\"^d/u\", \"½page\"],\n [\"e\", \"export\"],\n [\"y\", \"copy text\"],\n [\"Esc\", \"exit select\"],\n];\n\nconst EXPORT_KEYS = [\n [\"Tab\", \"fmt: md/csv/json\"],\n [\"Enter\", \"save\"],\n [\"Esc\", \"cancel\"],\n];\n\nconst DATE_JUMP_KEYS = [\n [\"Tab\", \"picker↔text\"],\n [\"←/→\", \"field\"],\n [\"↑/↓\", \"adjust\"],\n [\"Enter\", \"jump\"],\n [\"Esc\", \"cancel\"],\n];\n\nconst COMPOSE_KEYS = [\n [\"Enter\", \"send\"],\n [\"Esc\", \"cancel\"],\n];\n\nconst COMPOSE_NEW_KEYS = [\n [\"type\", \"phone/email/contact\"],\n [\"Enter\", \"next\"],\n [\"Esc\", \"cancel\"],\n];\n\nconst FILTER_KEYS = [[\"Enter/Esc\", \"exit filter\"]];\n\nconst DRAWER_KEYS = [\n [\"j/k\", \"scroll\"],\n [\"o\", \"open attachment\"],\n [\"Esc/q\", \"close\"],\n];\n\nexport function HelpBar({ mode, focus }: Props) {\n const theme = useTheme();\n let keys: string[][];\n if (mode === \"compose-new\") keys = COMPOSE_NEW_KEYS;\n else if (mode === \"compose\" || mode === \"confirm\") keys = COMPOSE_KEYS;\n else if (mode === \"filter\") keys = FILTER_KEYS;\n else if (mode === \"drawer\") keys = DRAWER_KEYS;\n else if (mode === \"select\") keys = SELECT_KEYS;\n else if (mode === \"export\") keys = EXPORT_KEYS;\n else if (mode === \"date-jump\") keys = DATE_JUMP_KEYS;\n else if (mode === \"send-via\") keys = SEND_VIA_KEYS;\n else if (mode === \"palette\") keys = PALETTE_KEYS;\n else keys = focus === \"thread\" ? THREAD_KEYS : SIDEBAR_KEYS;\n\n return (\n <Box paddingX={1} height={1} gap={1}>\n {keys.map(([key, desc]) => (\n // Render each hint as a SINGLE <Text> with nested colored spans.\n // Splitting `<Text>{key}</Text>` and `<Text>:desc</Text>` into separate\n // <Text> siblings inside a row Box makes Ink's flex-shrink consider\n // each individually — when the hints overflow the terminal width (which\n // happens in thread mode with 14 entries) flex-shrink trims ONE\n // character off each child's last <Text>, eating the final char of the\n // key label (e.g. `j/k` → `j/`, `Enter` → `Ente`, `V` → ``).\n // A single <Text> with nested color spans is treated as one atomic\n // string for shrink purposes — it either fits or wraps the whole\n // hint, never partial.\n // Also `flexShrink={0}` so overflow doesn't truncate at all; if the\n // row is too narrow the trailing hints drop off the right edge\n // instead of being silently clipped char-by-char.\n <Box key={key} flexShrink={0}>\n <Text>\n <Text color={theme.help.key}>{key}</Text>\n <Text color={theme.help.desc}>:{desc} </Text>\n </Text>\n </Box>\n ))}\n </Box>\n );\n}\n","/**\n * Glyph presets — the user-selectable \"theme\" axis.\n *\n * - \"safe\" (default): only universally-renderable glyphs. Geometric\n * Shapes (▶ ◀ ●), ASCII separators (─), emoji where every modern OS\n * font has them (✉ 💬 📱). No Powerline private-use codepoints, no\n * Nerd Font icons. Renders correctly in Apple Terminal, Warp, iTerm2,\n * VS Code's integrated terminal, Alacritty defaults, kitty defaults.\n *\n * - \"powerline\": Powerline arrows + Nerd Font icons. Looks better but\n * requires the user's terminal font to be a Nerd-Font-patched build\n * (FiraCode Nerd Font, JetBrainsMono Nerd Font, etc.). The README\n * warns about this. `imsg config show` flags it too.\n *\n * Categories — the consumer (theme.ts → makeTheme) just spreads one of\n * these into the produced Theme object.\n */\n\nexport interface GlyphSet {\n /** Powerline arrow separators. Only used when the user opts in. */\n arrowRight: string;\n arrowRightThin: string;\n arrowLeft: string;\n arrowLeftThin: string;\n\n /** Direction indicators on each message row. Always single-cell-wide. */\n sent: string;\n received: string;\n\n /** Status indicators */\n unreadDot: string;\n envelope: string;\n\n /** Service icons (iMessage / SMS) */\n iMessage: string;\n sms: string;\n\n /** Misc UI */\n paperclip: string;\n group: string;\n search: string;\n pencil: string;\n refresh: string;\n separator: string;\n}\n\nconst SAFE: GlyphSet = {\n // Powerline arrows aren't actually safe — for the safe preset we\n // substitute simple ASCII triangles. Components that draw arrow\n // separators between segments fall back to vertical bar `│`.\n arrowRight: \"│\",\n arrowRightThin: \"│\",\n arrowLeft: \"│\",\n arrowLeftThin: \"│\",\n // Geometric Shapes — fixed-width East-Asian range, every font has them.\n sent: \"▶\", // ▶\n received: \"◀\", // ◀\n unreadDot: \"●\",\n envelope: \"✉\",\n iMessage: \"💬\",\n sms: \"📱\",\n paperclip: \"📎\",\n group: \"☰\",\n search: \"⌕\",\n pencil: \"✎\",\n refresh: \"↻\",\n separator: \"─\",\n};\n\nconst POWERLINE: GlyphSet = {\n // Powerline private-use range (E0B0..E0B3).\n arrowRight: \"\",\n arrowRightThin: \"\",\n arrowLeft: \"\",\n arrowLeftThin: \"\",\n // Use the Powerline arrows as direction glyphs too — visually consistent.\n sent: \"\",\n received: \"\",\n unreadDot: \"●\",\n envelope: \"\", // Nerd Font envelope (FontAwesome)\n iMessage: \"\", // Nerd Font speech bubble\n sms: \"\", // Nerd Font phone\n paperclip: \"\", // Nerd Font paperclip\n group: \"\", // Nerd Font group\n search: \"\", // Nerd Font magnifier\n pencil: \"\", // Nerd Font pencil\n refresh: \"\", // Nerd Font refresh\n separator: \"─\",\n};\n\nexport const GLYPH_PRESETS = {\n safe: SAFE,\n powerline: POWERLINE,\n} as const;\n\nexport type GlyphPreset = keyof typeof GLYPH_PRESETS;\n\nexport const TAPBACK_EMOJI: Record<string, string> = {\n love: \"❤️\",\n like: \"👍\",\n dislike: \"👎\",\n laugh: \"😂\",\n emphasize: \"‼️\",\n question: \"❓\",\n};\n","/**\n * Pure color-space utilities. No dependencies, no side effects.\n *\n * The TUI palette derives its 30-odd hex colors from a single accent\n * hex; that derivation is HSL math (rotate hue, mix toward grey, push\n * lightness up or down). All of those operations live here so they can\n * be unit-tested without dragging in any rendering code.\n *\n * Hex format: always 7-char `#RRGGBB`. Output strings are lower-case.\n * HSL: h ∈ [0, 360), s ∈ [0, 1], l ∈ [0, 1].\n */\n\nexport interface Hsl {\n h: number;\n s: number;\n l: number;\n}\n\nconst HEX6_RE = /^#[0-9a-fA-F]{6}$/;\n\n/** Parse `#rrggbb` to {r,g,b} ∈ [0,255]. Throws on invalid input. */\nfunction hexToRgb(hex: string): { r: number; g: number; b: number } {\n if (!HEX6_RE.test(hex)) {\n throw new Error(`invalid hex color: ${hex} (expected #RRGGBB)`);\n }\n return {\n r: Number.parseInt(hex.slice(1, 3), 16),\n g: Number.parseInt(hex.slice(3, 5), 16),\n b: Number.parseInt(hex.slice(5, 7), 16),\n };\n}\n\nfunction rgbToHex(r: number, g: number, b: number): string {\n const c = (n: number) =>\n Math.max(0, Math.min(255, Math.round(n)))\n .toString(16)\n .padStart(2, \"0\");\n return `#${c(r)}${c(g)}${c(b)}`;\n}\n\nexport function hexToHsl(hex: string): Hsl {\n const { r, g, b } = hexToRgb(hex);\n const rn = r / 255;\n const gn = g / 255;\n const bn = b / 255;\n const max = Math.max(rn, gn, bn);\n const min = Math.min(rn, gn, bn);\n const l = (max + min) / 2;\n const d = max - min;\n let h = 0;\n let s = 0;\n if (d !== 0) {\n s = d / (1 - Math.abs(2 * l - 1));\n if (max === rn) h = 60 * (((gn - bn) / d) % 6);\n else if (max === gn) h = 60 * ((bn - rn) / d + 2);\n else h = 60 * ((rn - gn) / d + 4);\n if (h < 0) h += 360;\n }\n return { h, s, l };\n}\n\nexport function hslToHex({ h, s, l }: Hsl): string {\n const hh = ((h % 360) + 360) % 360;\n const ss = Math.max(0, Math.min(1, s));\n const ll = Math.max(0, Math.min(1, l));\n const c = (1 - Math.abs(2 * ll - 1)) * ss;\n const x = c * (1 - Math.abs(((hh / 60) % 2) - 1));\n const m = ll - c / 2;\n let r1 = 0;\n let g1 = 0;\n let b1 = 0;\n if (hh < 60) [r1, g1, b1] = [c, x, 0];\n else if (hh < 120) [r1, g1, b1] = [x, c, 0];\n else if (hh < 180) [r1, g1, b1] = [0, c, x];\n else if (hh < 240) [r1, g1, b1] = [0, x, c];\n else if (hh < 300) [r1, g1, b1] = [x, 0, c];\n else [r1, g1, b1] = [c, 0, x];\n return rgbToHex((r1 + m) * 255, (g1 + m) * 255, (b1 + m) * 255);\n}\n\n/** Return `hex` with lightness set to `l` ∈ [0,1]. */\nexport function withL(hex: string, l: number): string {\n const hsl = hexToHsl(hex);\n return hslToHex({ ...hsl, l });\n}\n\n/** Return `hex` with saturation set to `s` ∈ [0,1]. s=0 produces grey at the same lightness. */\nexport function withS(hex: string, s: number): string {\n const hsl = hexToHsl(hex);\n return hslToHex({ ...hsl, s });\n}\n\n/** Rotate hue by `degrees`. Negative values supported. */\nexport function rotateHue(hex: string, degrees: number): string {\n const hsl = hexToHsl(hex);\n return hslToHex({ ...hsl, h: hsl.h + degrees });\n}\n\n/**\n * Produce a desaturated, dark-or-light-tinted variant of `accent`. Used\n * by the palette to make borders / backgrounds / dim text feel like they\n * belong to the accent's \"color family\" without screaming.\n *\n * @param l Target lightness ∈ [0,1]\n * @param satMul Multiplier on the original saturation (1 = keep, 0 = grey)\n */\nexport function tint(accent: string, l: number, satMul: number): string {\n const hsl = hexToHsl(accent);\n return hslToHex({ h: hsl.h, s: hsl.s * satMul, l });\n}\n\n/**\n * WCAG-approximate relative luminance of a hex color.\n * Used by tests to assert palette-pair contrast ratios.\n */\nexport function relativeLuminance(hex: string): number {\n const { r, g, b } = hexToRgb(hex);\n const channel = (c: number) => {\n const cs = c / 255;\n return cs <= 0.03928 ? cs / 12.92 : ((cs + 0.055) / 1.055) ** 2.4;\n };\n return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);\n}\n\n/** Contrast ratio between two hex colors per WCAG 2.0. Range [1, 21]. */\nexport function contrastRatio(a: string, b: string): number {\n const la = relativeLuminance(a);\n const lb = relativeLuminance(b);\n const [hi, lo] = la > lb ? [la, lb] : [lb, la];\n return (hi + 0.05) / (lo + 0.05);\n}\n","/**\n * Single-accent palette derivation.\n *\n * The user picks one hex (default `#1982FC` = iMessage blue) and the\n * accent-driven parts of the palette come out of it. Approach:\n *\n * - **Accent-driven**: backgrounds, borders, headers, sidebar selection,\n * sent-bubble fill, and the unread dot all use the accent's hue at\n * varying lightness × saturation. Move the accent to orange and the\n * whole UI shifts orange.\n * - **Neutral with accent tint**: text colors and dim backgrounds keep\n * the accent's *hue* but at very low saturation (~5–10%). They feel\n * like part of the family without competing with the accent itself.\n * - **Semantic colors stay fixed**: error red, sms green, attachment\n * warm-yellow, edited gold. These need to be recognizable regardless\n * of accent (a magenta accent shouldn't make SMS markers magenta).\n * They're hand-tuned to coexist with any accent.\n *\n * The exported `Palette` shape mirrors what the legacy `theme` object\n * exported — the hand-tuned iMessage-inspired values are the target\n * for `accent = #1982FC`.\n */\n\nimport { contrastRatio, hexToHsl, hslToHex, withL, withS } from \"./color.js\";\n\nexport interface Palette {\n sent: { bg: string; fg: string; border: string };\n received: { bg: string; fg: string; border: string };\n pending: { bg: string; fg: string; border: string };\n\n sentText: string;\n receivedText: string;\n senderName: string;\n replyContext: string;\n attachment: string;\n lineNum: string;\n\n groupBg: { sent: string; received: string };\n selectionBg: string;\n\n sidebar: {\n selected: string;\n selectedFg: string;\n unread: string;\n read: string;\n snippet: string;\n slug: string;\n slugBg: string;\n separator: string;\n time: string;\n };\n\n border: string;\n dot: string;\n header: { focused: { bg: string; fg: string }; dim: { bg: string; fg: string } };\n info: { label: string; value: string };\n timestamp: string;\n status: { bg: string; fg: string; accent: string };\n help: { key: string; desc: string };\n sms: string;\n edited: string;\n compose: { bg: string; fg: string; placeholder: string };\n dateSep: string;\n drawer: { bg: string; border: string; label: string; value: string };\n\n /** Diagnostic — DevStats indicators that previously hard-coded #FF6B35 / #FF4444. */\n rustEngine: string;\n cpuHigh: string;\n}\n\nexport const DEFAULT_ACCENT = \"#1982FC\";\n\nexport function derivePalette(accent: string): Palette {\n const { h } = hexToHsl(accent);\n // Lightness clamp helpers — accept any input accent, never produce\n // text that's invisible on the dark TUI background.\n const acc = (l: number) => hslToHex({ h, s: 0.95, l });\n // Near-grey, slight accent tint — for borders, dim text, neutral bgs.\n const tint = (l: number, s = 0.06) => hslToHex({ h, s, l });\n // Choose whichever of {near-black, near-white} produces better\n // contrast against `bg`. Used by sent bubbles + sidebar selection\n // where the bg is accent-driven and may swing across the lightness\n // spectrum (magenta at L=0.55 has just-medium luminance and needs\n // dark fg; iMessage blue at L=0.55 needs light fg).\n const dark = tint(0.05, 0.1);\n const light = tint(0.97, 0.05);\n const fgFor = (bg: string) => (contrastRatio(dark, bg) > contrastRatio(light, bg) ? dark : light);\n\n const sentBg = acc(0.55); // accent at full saturation, mid-light → \"iMessage blue\"\n const selectedBg = acc(0.27);\n return {\n sent: {\n bg: sentBg,\n fg: fgFor(sentBg),\n border: acc(0.42),\n },\n received: {\n // Light grey bubble, very slight accent tint — preserves the iMessage\n // \"received\" look. Stays the same shade across accents (it's mostly grey).\n bg: tint(0.91, 0.05),\n fg: tint(0.12, 0.1),\n border: tint(0.75, 0.05),\n },\n pending: {\n bg: tint(0.27, 0.06),\n fg: tint(0.74, 0.06),\n border: tint(0.35, 0.08),\n },\n\n sentText: acc(0.78), // light accent — for sent text shown outside a bubble\n receivedText: tint(0.85, 0.06),\n // Sender names: a fixed teal that reads as \"another person\" in every\n // accent. Keeping accent-derived would produce magenta sender names on\n // a magenta accent, defeating the visual cue.\n senderName: SEMANTIC.senderName,\n replyContext: tint(0.62, 0.1),\n attachment: SEMANTIC.attachment, // fixed warm yellow\n lineNum: tint(0.45, 0.05),\n\n groupBg: {\n sent: tint(0.16, 0.4), // dark, clearly accent-tinted (sent rows)\n received: hslToHex({ h: (h + 20) % 360, s: 0.18, l: 0.14 }), // close-but-not-equal\n },\n selectionBg: SEMANTIC.selection, // muted warm yellow — universal \"selected\"\n\n sidebar: {\n selected: selectedBg,\n selectedFg: fgFor(selectedBg),\n unread: tint(0.97, 0.05),\n read: tint(0.74, 0.06),\n snippet: tint(0.51, 0.05),\n slug: hslToHex({ h, s: 0.28, l: 0.55 }),\n slugBg: tint(0.1, 0.15),\n separator: tint(0.2, 0.05),\n time: tint(0.51, 0.05),\n },\n\n border: tint(0.25, 0.05),\n dot: acc(0.55),\n header: {\n focused: { bg: tint(0.2, 0.06), fg: tint(0.97, 0.05) },\n dim: { bg: tint(0.13, 0.06), fg: tint(0.51, 0.05) },\n },\n info: { label: tint(0.62, 0.05), value: tint(0.85, 0.05) },\n timestamp: tint(0.62, 0.1),\n status: { bg: tint(0.13, 0.06), fg: tint(0.74, 0.06), accent: acc(0.55) },\n help: { key: tint(0.74, 0.06), desc: tint(0.4, 0.05) },\n sms: SEMANTIC.sms, // green — universal \"SMS not iMessage\" cue\n edited: SEMANTIC.edited, // gold — universal \"this was edited\" cue\n compose: {\n bg: tint(0.18, 0.06),\n fg: tint(0.97, 0.05),\n placeholder: tint(0.4, 0.05),\n },\n dateSep: tint(0.32, 0.05),\n drawer: {\n bg: tint(0.13, 0.06),\n border: tint(0.25, 0.05),\n label: tint(0.62, 0.05),\n value: tint(0.85, 0.05),\n },\n\n rustEngine: SEMANTIC.rustEngine, // warm orange — distinct from any accent\n cpuHigh: SEMANTIC.cpuHigh, // red — universal \"elevated\"\n };\n}\n\n/** Hand-tuned constants kept outside the accent-derivation loop. */\nconst SEMANTIC = {\n senderName: \"#5AC8C8\", // teal\n attachment: \"#FFB347\", // orange (paperclip)\n selection: \"#3C3814\", // dim olive (visual select bg)\n sms: \"#5AC85A\", // green (SMS marker)\n edited: \"#96821E\", // gold (edited indicator)\n rustEngine: \"#FF6B35\", // warm orange (DevStats engine label)\n cpuHigh: \"#FF4444\", // red (DevStats elevated CPU)\n} as const;\n\n// Surface tiny helpers for tests / setup probes.\nexport { withL, withS };\n","/**\n * TUI theme — assembled from a glyph preset + an accent-derived palette.\n *\n * Components consume the resolved theme via `useTheme()` from\n * `./themes/ThemeContext.js`. This file's `makeTheme()` is the single\n * place where palette + glyphs combine into the final shape.\n *\n * Backwards-compat: a few non-component places still want a constant\n * `glyphs` / `TAPBACK_EMOJI`. Those remain re-exported here. The legacy\n * `theme` constant export is gone — components were swept to `useTheme()`\n * in the same change as this refactor.\n */\n\nimport { GLYPH_PRESETS, type GlyphPreset, type GlyphSet } from \"./themes/glyphs.js\";\nimport { DEFAULT_ACCENT, derivePalette, type Palette } from \"./themes/palette.js\";\n\nexport interface Theme extends Palette {\n glyphs: GlyphSet;\n}\n\nexport interface ThemeOptions {\n preset?: GlyphPreset;\n accent?: string;\n}\n\nexport function makeTheme({ preset = \"safe\", accent = DEFAULT_ACCENT }: ThemeOptions = {}): Theme {\n return {\n ...derivePalette(accent),\n glyphs: GLYPH_PRESETS[preset],\n };\n}\n\nexport { GLYPH_PRESETS, type GlyphPreset, type GlyphSet, TAPBACK_EMOJI } from \"./themes/glyphs.js\";\nexport { DEFAULT_ACCENT } from \"./themes/palette.js\";\n","import { Box, Text } from \"ink\";\nimport type { Message } from \"../../types.js\";\nimport { TAPBACK_EMOJI } from \"../theme.js\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\n\ninterface Props {\n message: Message;\n width: number;\n height: number;\n}\n\nfunction formatFullDate(date: Date): string {\n return date.toLocaleString(\"en-US\", {\n weekday: \"short\",\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hour12: true,\n });\n}\n\n/**\n * Drawer-row label. Wrapped in a flexShrink={0} Box so a sibling\n * `<Text wrap=\"truncate\">` value (e.g. GUID) doesn't squeeze the label\n * to zero width — without this, \"GUID: 9C768341...\" rendered as\n * \"GUID9C768341...\" because the truncating value won the row's width\n * negotiation and the label's colon+space got eaten.\n */\nfunction Label({ children }: { children: string }) {\n const theme = useTheme();\n return (\n <Box flexShrink={0}>\n <Text color={theme.drawer.label}>{children}: </Text>\n </Box>\n );\n}\n\nexport function MessageDrawer({ message: m, width, height }: Props) {\n const theme = useTheme();\n const hasAttachments = m.attachments && m.attachments.length > 0;\n\n return (\n <Box\n flexDirection=\"column\"\n width={width}\n height={height}\n borderStyle=\"single\"\n borderColor={theme.drawer.border}\n overflow=\"hidden\"\n >\n {/* Header */}\n <Box paddingX={1} backgroundColor={theme.header.focused.bg}>\n <Text color={theme.header.focused.fg} bold>\n Message Details\n </Text>\n </Box>\n\n <Box flexDirection=\"column\" paddingX={1} paddingY={0} gap={0}>\n {/* Sender */}\n <Box>\n <Label>From</Label>\n <Text color={theme.drawer.value}>{m.isFromMe ? \"Me\" : (m.displayName ?? m.handle)}</Text>\n </Box>\n\n {/* Handle */}\n {!m.isFromMe && m.displayName && (\n <Box>\n <Label>Handle</Label>\n <Text color={theme.drawer.value}>{m.handle}</Text>\n </Box>\n )}\n\n {/* Date */}\n <Box>\n <Label>Sent</Label>\n <Text color={theme.drawer.value}>{formatFullDate(m.date)}</Text>\n </Box>\n\n {/* Read/Delivered */}\n {m.dateDelivered && (\n <Box>\n <Label>Delivered</Label>\n <Text color={theme.drawer.value}>{formatFullDate(m.dateDelivered)}</Text>\n </Box>\n )}\n {m.dateRead && (\n <Box>\n <Label>Read</Label>\n <Text color={theme.drawer.value}>{formatFullDate(m.dateRead)}</Text>\n </Box>\n )}\n\n {/* Service */}\n <Box>\n <Label>Service</Label>\n <Text color={m.service === \"SMS\" ? theme.sms : theme.info.label}>{m.service}</Text>\n </Box>\n\n {/* Chat ID */}\n <Box>\n <Label>Chat</Label>\n <Text color={theme.drawer.value}>{m.chatId}</Text>\n </Box>\n\n {/* GUID */}\n <Box>\n <Label>GUID</Label>\n <Text color={theme.drawer.value} wrap=\"truncate\">\n {m.guid}\n </Text>\n </Box>\n\n {/* Edited */}\n {m.isEdited && (\n <Box>\n <Label>Status</Label>\n <Text color={theme.edited}>Edited</Text>\n </Box>\n )}\n\n {/* Reactions */}\n {m.reactions && m.reactions.length > 0 && (\n <Box flexDirection=\"column\">\n <Text color={theme.drawer.label}>Reactions:</Text>\n {m.reactions\n .filter((r) => !r.isRemoval)\n .map((r) => (\n <Box\n key={`${r.fromHandle}-${r.type}-${r.emoji ?? \"\"}-${r.targetMessageGuid}-${r.targetMessagePart}`}\n paddingLeft={1}\n >\n <Text color={theme.drawer.value}>\n {r.emoji ?? TAPBACK_EMOJI[r.type] ?? r.type} {r.fromHandle ?? \"unknown\"}\n </Text>\n </Box>\n ))}\n </Box>\n )}\n\n {/* Attachments */}\n {hasAttachments && (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text color={theme.attachment} bold>\n Attachments ({m.attachments!.length}):\n </Text>\n {m.attachments!.map((att) => {\n return (\n <Box\n key={`${att.filename}-${att.transferName ?? \"\"}-${att.mimeType ?? \"\"}-${att.totalBytes}`}\n flexDirection=\"column\"\n paddingLeft={1}\n >\n <Text color={theme.drawer.value} wrap=\"truncate\">\n {att.transferName ?? att.filename}\n </Text>\n <Text color={theme.drawer.label}>\n {att.mimeType ?? \"unknown\"} ·{\" \"}\n {att.totalBytes > 0 ? formatBytes(att.totalBytes) : \"?\"}\n <Text color={theme.senderName}> (press o to preview)</Text>\n </Text>\n </Box>\n );\n })}\n <Box marginTop={1}>\n <Text color={theme.help.key}>o</Text>\n <Text color={theme.help.desc}>: open attachment</Text>\n </Box>\n </Box>\n )}\n\n {/* Full message text */}\n <Box flexDirection=\"column\" marginTop={1}>\n <Text color={theme.drawer.label}>Full text:</Text>\n <Box borderStyle=\"single\" borderColor={theme.drawer.border} paddingX={1}>\n <Text color={theme.drawer.value} wrap=\"wrap\">\n {m.text ?? \"(no text)\"}\n </Text>\n </Box>\n </Box>\n\n {/* Reply context */}\n {m.isReply && m.replyTo && (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text color={theme.drawer.label}>Reply to:</Text>\n <Text color={theme.replyContext} italic wrap=\"wrap\">\n {m.replyTo.replyToText ?? \"(unknown)\"}\n </Text>\n </Box>\n )}\n </Box>\n\n {/* Footer hint */}\n <Box flexGrow={1} />\n <Box paddingX={1}>\n <Text color={theme.help.desc}>Esc/q to close</Text>\n </Box>\n </Box>\n );\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n}\n","/**\n * Send-via modal — pick an installed external chat app and launch its URL\n * scheme deep-link for the current thread's handle. Triggered by `S` in\n * thread pane (TUI handler in App.tsx).\n *\n * Renders a numbered list of installed apps. Press `1`–`9` to launch,\n * Esc to cancel. The TUI's mode-level `useInput` handler in App.tsx\n * actually does the launching; this component is presentation-only.\n */\nimport { Box, Text } from \"ink\";\nimport type { ChatAppDef } from \"../../url-schemes.js\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\n\ninterface Props {\n handle: string;\n apps: ChatAppDef[];\n}\n\nexport function SendViaModal({ handle, apps }: Props) {\n const theme = useTheme();\n // The modal renders as a sibling of the main body Box. Because the body\n // takes `flexGrow={1}` of the parent column, the modal gets only the\n // leftover vertical space — which can be less than its natural content\n // height. Without explicit anti-shrink the modal collapses rows together\n // (ate the FaceTime row, ate the 'H' of \"Handle\", merged \"WhatsApp\" into\n // \"SMS\", etc).\n // Two-pronged defense:\n // 1. `flexShrink={0}` on the outer modal so the modal owns its full\n // natural height — it pushes the body content up instead of being\n // compressed itself.\n // 2. `flexShrink={0}` on every row Box so individual rows can't get\n // collapsed even if their parent is space-constrained.\n // Plus an opaque backgroundColor so any overlap with body content paints\n // over instead of leaking through transparent regions.\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"double\"\n borderColor={theme.status.accent}\n backgroundColor={theme.header.dim.bg}\n paddingX={1}\n flexShrink={0}\n >\n <Box flexShrink={0}>\n <Text color={theme.status.accent} bold>\n Send via external app\n </Text>\n </Box>\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>Handle: {handle}</Text>\n </Box>\n {apps.length === 0 ? (\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>No compatible apps installed.</Text>\n </Box>\n ) : (\n apps.map((a, i) => (\n <Box key={a.name} flexShrink={0}>\n <Text color={theme.help.key}>{i + 1}</Text>\n <Text color={theme.help.desc}>\n {\": \"}\n {a.name}\n {a.supportsBody ? \"\" : \" (no body support)\"}\n </Text>\n </Box>\n ))\n )}\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>1-9: launch · Esc: cancel</Text>\n </Box>\n </Box>\n );\n}\n","/**\n * Shared conversation-filter predicate. Used by Sidebar (rendering) and\n * App.tsx (Enter-to-jump-to-first-match handler).\n */\nimport type { Conversation } from \"../types.js\";\n\nexport function matchesConversationFilter(c: Conversation, queryLower: string): boolean {\n return (\n (c.displayName?.toLowerCase().includes(queryLower) ?? false) ||\n c.chatIdentifier.toLowerCase().includes(queryLower) ||\n c.threadSlug.toLowerCase().includes(queryLower)\n );\n}\n\n/**\n * Return the index of the first conversation matching `query` in the full\n * conversations array, or null if no match. Index is a position into the\n * ORIGINAL conversations array — not the filtered view — so it can be passed\n * to a SELECT action directly.\n */\nexport function firstFilterMatchIndex(conversations: Conversation[], query: string): number | null {\n const trimmed = query.trim();\n if (!trimmed) return null;\n const q = trimmed.toLowerCase();\n for (let i = 0; i < conversations.length; i++) {\n if (matchesConversationFilter(conversations[i]!, q)) return i;\n }\n return null;\n}\n","/**\n * Central keymap registry.\n *\n * Every action a user can take in the TUI is represented here as a `Command`,\n * even when it's also bound to a key directly inside `App.tsx`'s `useInput`.\n * The palette uses this list both for fuzzy search and (with an empty query)\n * as the complete keybinding cheat sheet.\n *\n * The reducer-level bindings in `App.tsx` still own actual key dispatch — this\n * file is the *description* of those bindings, plus a `run()` that can fire\n * them from the palette.\n */\nimport type { Dispatch } from \"react\";\nimport type { useImsg } from \"./hooks/useImsg.js\";\nimport type { Action, AppState } from \"./types.js\";\n\nexport interface CommandContext {\n state: AppState;\n dispatch: Dispatch<Action>;\n imsg: ReturnType<typeof useImsg>;\n}\n\nexport interface Command {\n /** Stable id (\"core.compose\", \"analytics.streaks\"). Used by tests + future keymap config. */\n id: string;\n /** Palette display title. */\n title: string;\n /** Optional dim description shown beside the title. */\n description?: string;\n /** Category for empty-query grouping (\"Navigation\", \"Compose\", \"Analytics\"…). */\n category: string;\n /** Display string for the keybinding column (\"c\", \"Ctrl-d\", \"gg\"). */\n keybinding?: string;\n /** Optional gate — when false the palette omits the entry. */\n when?: (state: AppState) => boolean;\n /** Invoked from the palette. */\n run: (ctx: CommandContext) => void | Promise<void>;\n}\n\nconst hasSelection = (s: AppState) =>\n s.selectedModuleIdx == null && s.conversations[s.selectedIdx] != null;\n\n/**\n * Core commands. The `run` bodies dispatch the same actions the inline\n * `useInput` handler in App.tsx dispatches. Many keys (movement, etc.) have no\n * sensible `run` from the palette — they're listed here purely so the palette\n * shows them as keybindings. Their `run` is a no-op.\n */\nexport const CORE_COMMANDS: Command[] = [\n // ── Navigation ─────────────────────────────────────────────────────────\n {\n id: \"core.move.down\",\n title: \"Move down\",\n category: \"Navigation\",\n keybinding: \"j / ↓\",\n run: () => {},\n },\n {\n id: \"core.move.up\",\n title: \"Move up\",\n category: \"Navigation\",\n keybinding: \"k / ↑\",\n run: () => {},\n },\n {\n id: \"core.move.count\",\n title: \"Jump N rows\",\n category: \"Navigation\",\n keybinding: \"#j / #k\",\n description: \"Vim-style count prefix\",\n run: () => {},\n },\n {\n id: \"core.move.top\",\n title: \"Go to top\",\n category: \"Navigation\",\n keybinding: \"gg\",\n run: () => {},\n },\n {\n id: \"core.move.bottom\",\n title: \"Go to bottom\",\n category: \"Navigation\",\n keybinding: \"G\",\n run: () => {},\n },\n {\n id: \"core.move.halfdown\",\n title: \"Half page down\",\n category: \"Navigation\",\n keybinding: \"Ctrl-d\",\n run: () => {},\n },\n {\n id: \"core.move.halfup\",\n title: \"Half page up\",\n category: \"Navigation\",\n keybinding: \"Ctrl-u\",\n run: () => {},\n },\n {\n id: \"core.move.pagedown\",\n title: \"Page down\",\n category: \"Navigation\",\n keybinding: \"Ctrl-f / PgDn\",\n run: () => {},\n },\n {\n id: \"core.move.pageup\",\n title: \"Page up\",\n category: \"Navigation\",\n keybinding: \"Ctrl-b / PgUp\",\n run: () => {},\n },\n {\n id: \"core.move.high\",\n title: \"First visible row\",\n category: \"Navigation\",\n keybinding: \"H\",\n run: () => {},\n },\n {\n id: \"core.move.middle\",\n title: \"Middle visible row\",\n category: \"Navigation\",\n keybinding: \"M\",\n run: () => {},\n },\n {\n id: \"core.move.low\",\n title: \"Last visible row\",\n category: \"Navigation\",\n keybinding: \"L\",\n run: () => {},\n },\n {\n id: \"core.move.groupnext\",\n title: \"Next sender group\",\n category: \"Navigation\",\n keybinding: \"} / ]\",\n description: \"Jump to next sender boundary\",\n run: () => {},\n },\n {\n id: \"core.move.groupprev\",\n title: \"Previous sender group\",\n category: \"Navigation\",\n keybinding: \"{ / [\",\n run: () => {},\n },\n {\n id: \"core.focus.toggle\",\n title: \"Switch pane (sidebar ↔ thread)\",\n category: \"Navigation\",\n keybinding: \"Tab\",\n run: ({ state, dispatch }) =>\n dispatch({ type: \"FOCUS\", pane: state.focus === \"sidebar\" ? \"thread\" : \"sidebar\" }),\n },\n\n // ── Conversations ──────────────────────────────────────────────────────\n {\n id: \"core.filter\",\n title: \"Filter conversations\",\n category: \"Conversations\",\n keybinding: \"/\",\n run: ({ dispatch }) => dispatch({ type: \"ENTER_FILTER\" }),\n },\n {\n id: \"core.copy.slug\",\n title: \"Copy thread slug\",\n category: \"Conversations\",\n keybinding: \"y\",\n description: \"Sidebar focus — copies ~slug to clipboard\",\n run: () => {},\n },\n {\n id: \"core.refresh\",\n title: \"Refresh conversations\",\n category: \"Conversations\",\n keybinding: \"r\",\n run: ({ dispatch }) => dispatch({ type: \"SET_STATUS\", status: \"Refreshing…\" }),\n },\n\n // ── Compose / send ─────────────────────────────────────────────────────\n {\n id: \"core.compose\",\n title: \"Compose in current thread\",\n category: \"Compose\",\n keybinding: \"c\",\n when: hasSelection,\n run: ({ dispatch }) => dispatch({ type: \"ENTER_COMPOSE\" }),\n },\n {\n id: \"core.compose.new\",\n title: \"New conversation\",\n category: \"Compose\",\n keybinding: \"N\",\n description: \"Compose to a new recipient\",\n run: ({ dispatch }) => dispatch({ type: \"ENTER_COMPOSE_NEW\" }),\n },\n {\n id: \"core.send.via\",\n title: \"Send via external app\",\n category: \"Compose\",\n keybinding: \"S\",\n description: \"Pick an installed chat app and deep-link to this thread\",\n when: hasSelection,\n run: ({ dispatch }) => dispatch({ type: \"ENTER_SEND_VIA\" }),\n },\n {\n id: \"core.open.messages\",\n title: \"Open in Messages.app\",\n category: \"Compose\",\n keybinding: \"O\",\n when: hasSelection,\n run: () => {},\n },\n\n // ── Thread actions ─────────────────────────────────────────────────────\n {\n id: \"core.details\",\n title: \"Open message details drawer\",\n category: \"Thread\",\n keybinding: \"Enter\",\n run: ({ dispatch }) => dispatch({ type: \"OPEN_DRAWER\" }),\n },\n {\n id: \"core.attachment.open\",\n title: \"Open attachment\",\n category: \"Thread\",\n keybinding: \"o\",\n description: \"Images → Quick Look, video → mpv\",\n run: () => {},\n },\n {\n id: \"core.select.start\",\n title: \"Visual select messages\",\n category: \"Thread\",\n keybinding: \"V\",\n run: ({ dispatch }) => dispatch({ type: \"ENTER_SELECT_MODE\" }),\n },\n {\n id: \"core.date.jump\",\n title: \"Jump to date\",\n category: \"Thread\",\n keybinding: \":\",\n run: ({ dispatch }) => dispatch({ type: \"ENTER_DATE_JUMP\" }),\n },\n {\n id: \"core.copy.text\",\n title: \"Copy selected messages\",\n category: \"Thread\",\n keybinding: \"y\",\n description: \"Visual select mode — copies text to clipboard\",\n run: () => {},\n },\n {\n id: \"core.export\",\n title: \"Export selection / thread\",\n category: \"Thread\",\n keybinding: \"e\",\n description: \"From visual select mode\",\n run: () => {},\n },\n\n // ── App ────────────────────────────────────────────────────────────────\n {\n id: \"core.devstats\",\n title: \"Toggle dev stats\",\n category: \"App\",\n keybinding: \"d\",\n description: \"Engine, perf, memory\",\n run: ({ dispatch }) => dispatch({ type: \"TOGGLE_DEV_STATS\" }),\n },\n {\n id: \"core.palette\",\n title: \"Command palette\",\n category: \"App\",\n keybinding: \"Ctrl-P / ?\",\n description: \"This window\",\n run: ({ dispatch }) => dispatch({ type: \"OPEN_PALETTE\" }),\n },\n { id: \"core.quit\", title: \"Quit\", category: \"App\", keybinding: \"q / Ctrl-C\", run: () => {} },\n];\n","/**\n * Analytics pane — renders one of six analytic types over a chosen time\n * window, computed against the unified `dispatchAnalytic` from\n * `src/analytics.ts`. Owns its own keyboard handlers via `useInput`.\n */\nimport { Box, Text, useInput } from \"ink\";\nimport { useEffect, useState } from \"react\";\nimport type { AnalyticType } from \"../../../analytics.js\";\nimport {\n type DoubleTextResult,\n dispatchAnalytic,\n type HeatmapResult,\n IMPLEMENTED_TYPES,\n type ResponseTimeStats,\n type StreakResult,\n type TapbackResult,\n type WrappedResult,\n} from \"../../../analytics.js\";\nimport type { Message } from \"../../../types.js\";\nimport { useTheme } from \"../../themes/ThemeContext.js\";\nimport type { ModulePaneProps } from \"../types.js\";\nimport type { AnalyticsRange, AnalyticsState } from \"./module.js\";\n\nexport const ANALYTIC_LABEL: Record<AnalyticType, string> = {\n messaging_streaks: \"Messaging Streaks\",\n double_texts: \"Double Texts\",\n response_time_stats: \"Response Times\",\n daily_heatmap: \"Daily Heatmap\",\n tapback_summary: \"Tapback Summary\",\n year_in_review_wrapped: \"Year in Review (Wrapped)\",\n};\n\nconst RANGE_LABEL: Record<AnalyticsRange, string> = {\n \"30d\": \"Last 30 days\",\n \"90d\": \"Last 90 days\",\n \"1y\": \"Last year\",\n all: \"All time\",\n};\n\nconst RANGE_ORDER: AnalyticsRange[] = [\"30d\", \"90d\", \"1y\", \"all\"];\n\n/** Convert a range key to a cutoff Date. `\"all\"` returns the Unix epoch. */\nfunction rangeCutoff(range: AnalyticsRange): Date {\n const now = Date.now();\n switch (range) {\n case \"30d\":\n return new Date(now - 30 * 86_400_000);\n case \"90d\":\n return new Date(now - 90 * 86_400_000);\n case \"1y\":\n return new Date(now - 365 * 86_400_000);\n case \"all\":\n return new Date(0);\n }\n}\n\nexport function AnalyticsPane({\n instance,\n imsg,\n width,\n height,\n focused,\n onUpdateState,\n onClose,\n setStatus,\n}: ModulePaneProps) {\n const theme = useTheme();\n const state = instance.state as AnalyticsState;\n const [messages, setMessages] = useState<Message[] | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [result, setResult] = useState<{ data: unknown } | null>(null);\n\n // Load messages whenever the range cutoff changes.\n useEffect(() => {\n let cancelled = false;\n setLoading(true);\n setError(null);\n setResult(null);\n const cutoff = rangeCutoff(state.range);\n imsg\n .loadMessagesInWindow(cutoff)\n .then((m) => {\n if (cancelled) return;\n setMessages(m);\n })\n .catch((e) => {\n if (cancelled) return;\n setError(e instanceof Error ? e.message : String(e));\n setLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [state.range, imsg]);\n\n // Compute the analytic OUT of the render path. `dispatchAnalytic` on\n // 30 days of messages takes 1-3 seconds — running it in a useMemo blocked\n // the render so \"Loading…\" never appeared and the pane sat empty during\n // the wait. Deferring via `setTimeout(0)` lets React paint loading state\n // before the heavy compute starts on the next macrotask.\n useEffect(() => {\n if (!messages) return;\n let cancelled = false;\n setLoading(true);\n const handle = setTimeout(() => {\n if (cancelled) return;\n try {\n const { data } = dispatchAnalytic(state.type, messages);\n setResult({ data });\n setError(null);\n } catch (e) {\n setError(e instanceof Error ? e.message : String(e));\n setResult(null);\n }\n setLoading(false);\n }, 0);\n return () => {\n cancelled = true;\n clearTimeout(handle);\n };\n }, [messages, state.type]);\n\n // Keyboard: Tab cycles analytic type, [/] cycles range, Esc closes.\n useInput(\n (input, key) => {\n if (key.escape) {\n onClose();\n return;\n }\n if (key.tab) {\n const cur = IMPLEMENTED_TYPES.indexOf(state.type);\n const next = IMPLEMENTED_TYPES[(cur + 1) % IMPLEMENTED_TYPES.length]!;\n onUpdateState({ ...state, type: next });\n setStatus(`Analytics: ${ANALYTIC_LABEL[next]}`);\n return;\n }\n if (input === \"]\" || input === \">\") {\n const cur = RANGE_ORDER.indexOf(state.range);\n const next = RANGE_ORDER[(cur + 1) % RANGE_ORDER.length]!;\n onUpdateState({ ...state, range: next });\n return;\n }\n if (input === \"[\" || input === \"<\") {\n const cur = RANGE_ORDER.indexOf(state.range);\n const next = RANGE_ORDER[(cur - 1 + RANGE_ORDER.length) % RANGE_ORDER.length]!;\n onUpdateState({ ...state, range: next });\n return;\n }\n },\n { isActive: focused },\n );\n\n return (\n <Box\n flexDirection=\"column\"\n width={width}\n height={height}\n borderStyle=\"single\"\n borderColor={focused ? theme.status.accent : theme.border}\n overflow=\"hidden\"\n >\n <Box\n paddingX={1}\n backgroundColor={focused ? theme.header.focused.bg : theme.header.dim.bg}\n flexShrink={0}\n >\n <Text color={focused ? theme.header.focused.fg : theme.header.dim.fg} bold={focused}>\n {ANALYTIC_LABEL[state.type]}\n </Text>\n <Text color={theme.help.desc}> · {RANGE_LABEL[state.range]}</Text>\n </Box>\n\n <Box flexDirection=\"column\" flexGrow={1} paddingX={1} overflow=\"hidden\">\n {loading && <Text color={theme.help.desc}>Loading…</Text>}\n {error && <Text color={theme.edited}>Error: {error}</Text>}\n {!loading && !error && result && (\n <AnalyticsResultView\n type={state.type}\n data={result.data}\n messages={messages ?? []}\n width={width - 2}\n height={height - 4}\n resolveNames={imsg.resolveNames}\n />\n )}\n </Box>\n\n <Box paddingX={1} height={1} flexShrink={0}>\n <Text color={theme.help.key}>Tab</Text>\n <Text color={theme.help.desc}>:type </Text>\n <Text color={theme.help.key}>[ ]</Text>\n <Text color={theme.help.desc}>:range </Text>\n <Text color={theme.help.key}>Esc</Text>\n <Text color={theme.help.desc}>:close</Text>\n </Box>\n </Box>\n );\n}\n\n// ── Result renderers ────────────────────────────────────────────────────\n\ninterface ResultViewProps {\n type: AnalyticType;\n data: unknown;\n messages: Message[];\n width: number;\n height: number;\n resolveNames: (handles: string[]) => string[];\n}\n\nfunction AnalyticsResultView({\n type,\n data,\n messages,\n width,\n height,\n resolveNames,\n}: ResultViewProps) {\n if (data == null) return <NoData />;\n switch (type) {\n case \"messaging_streaks\":\n return <StreaksView rows={data as StreakResult[]} height={height} />;\n case \"double_texts\":\n return <DoubleTextsView rows={data as DoubleTextResult[]} height={height} />;\n case \"response_time_stats\":\n return <ResponseTimesView rows={data as ResponseTimeStats[]} height={height} />;\n case \"daily_heatmap\":\n return <HeatmapView result={data as HeatmapResult} width={width} />;\n case \"tapback_summary\":\n return <TapbacksView rows={data as TapbackResult[]} height={height} />;\n case \"year_in_review_wrapped\":\n return <WrappedView result={data as WrappedResult} />;\n }\n void messages;\n void resolveNames;\n}\n\nfunction NoData() {\n const theme = useTheme();\n return <Text color={theme.help.desc}>No data in this window.</Text>;\n}\n\nfunction StreaksView({ rows, height }: { rows: StreakResult[]; height: number }) {\n const theme = useTheme();\n if (rows.length === 0) return <NoData />;\n const visible = rows.slice(0, Math.max(1, height - 2));\n return (\n <Box flexDirection=\"column\">\n <Box>\n <Text color={theme.info.label}>{\"Contact\".padEnd(28)}</Text>\n <Text color={theme.info.label}>{\"Longest\".padStart(10)}</Text>\n <Text color={theme.info.label}>{\"Current\".padStart(10)}</Text>\n <Text color={theme.info.label}>{\" Start..End\".padEnd(30)}</Text>\n </Box>\n {visible.map((r) => (\n <Box key={r.contact}>\n <Text wrap=\"truncate\">{trunc(r.contact, 27).padEnd(28)}</Text>\n <Text color={theme.status.accent}>{`${r.longestStreakDays}d`.padStart(10)}</Text>\n <Text>{`${r.currentStreakDays}d`.padStart(10)}</Text>\n <Text color={theme.help.desc}>\n {\" \"}\n {r.longestStreakStart ?? \"—\"}..{r.longestStreakEnd ?? \"—\"}\n </Text>\n </Box>\n ))}\n </Box>\n );\n}\n\nfunction DoubleTextsView({ rows, height }: { rows: DoubleTextResult[]; height: number }) {\n const theme = useTheme();\n if (rows.length === 0) return <NoData />;\n const visible = rows.slice(0, Math.max(1, height - 2));\n return (\n <Box flexDirection=\"column\">\n <Box>\n <Text color={theme.info.label}>{\"Contact\".padEnd(28)}</Text>\n <Text color={theme.info.label}>{\"From me\".padStart(10)}</Text>\n <Text color={theme.info.label}>{\"From them\".padStart(12)}</Text>\n </Box>\n {visible.map((r) => (\n <Box key={r.contact}>\n <Text wrap=\"truncate\">{trunc(r.contact, 27).padEnd(28)}</Text>\n <Text color={theme.status.accent}>{String(r.doubleTextsFromMe).padStart(10)}</Text>\n <Text>{String(r.doubleTextsFromThem).padStart(12)}</Text>\n </Box>\n ))}\n </Box>\n );\n}\n\nfunction ResponseTimesView({ rows, height }: { rows: ResponseTimeStats[]; height: number }) {\n const theme = useTheme();\n if (rows.length === 0) return <NoData />;\n const visible = rows.slice(0, Math.max(1, height - 2));\n return (\n <Box flexDirection=\"column\">\n <Box>\n <Text color={theme.info.label}>{\"Contact\".padEnd(28)}</Text>\n <Text color={theme.info.label}>{\"Count\".padStart(8)}</Text>\n <Text color={theme.info.label}>{\"Median\".padStart(12)}</Text>\n <Text color={theme.info.label}>{\"p95\".padStart(12)}</Text>\n <Text color={theme.info.label}>{\"Mean\".padStart(12)}</Text>\n </Box>\n {visible.map((r) => (\n <Box key={r.contact}>\n <Text wrap=\"truncate\">{trunc(r.contact, 27).padEnd(28)}</Text>\n <Text>{String(r.count).padStart(8)}</Text>\n <Text color={theme.status.accent}>{formatDuration(r.medianMs).padStart(12)}</Text>\n <Text>{formatDuration(r.p95Ms).padStart(12)}</Text>\n <Text>{formatDuration(r.meanMs).padStart(12)}</Text>\n </Box>\n ))}\n </Box>\n );\n}\n\nconst HEATMAP_SHADES = [\" \", \"·\", \"░\", \"▒\", \"▓\", \"█\"];\nconst DOW_LABELS = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\nconst HOURS_24 = [\n 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,\n] as const;\n\nfunction HeatmapView({ result, width }: { result: HeatmapResult; width: number }) {\n const theme = useTheme();\n const grid = result.grid;\n let peak = 0;\n for (const row of grid) for (const cell of row) if (cell > peak) peak = cell;\n\n // Each hour gets 2 cells of width (so the grid is readable on narrow panes).\n const cellW = width >= 24 * 2 + 6 ? 2 : 1;\n\n return (\n <Box flexDirection=\"column\">\n <Box>\n <Text color={theme.info.label}>{\" \"}</Text>\n {HOURS_24.map((h) => (\n <Text key={`h-${h}`} color={theme.help.desc}>\n {h % 6 === 0 ? `${h}`.padStart(cellW) : \"\".padStart(cellW, \" \")}\n </Text>\n ))}\n </Box>\n {DOW_LABELS.map((label, dow) => {\n const row = grid[dow] ?? [];\n return (\n <Box key={label}>\n <Text color={theme.info.label}>{label} </Text>\n {HOURS_24.map((h) => {\n const count = row[h] ?? 0;\n const ratio = peak > 0 ? count / peak : 0;\n const idx = Math.min(\n HEATMAP_SHADES.length - 1,\n Math.round(ratio * (HEATMAP_SHADES.length - 1)),\n );\n const shade = HEATMAP_SHADES[idx]!;\n return (\n <Text\n key={`c-${label}-${h}`}\n color={ratio > 0.66 ? theme.status.accent : undefined}\n >\n {shade.repeat(cellW)}\n </Text>\n );\n })}\n </Box>\n );\n })}\n <Box marginTop={1}>\n <Text color={theme.help.desc}>\n {result.total.toLocaleString()} messages · peak hour count: {peak}\n </Text>\n </Box>\n </Box>\n );\n}\n\nfunction TapbacksView({ rows, height }: { rows: TapbackResult[]; height: number }) {\n const theme = useTheme();\n if (rows.length === 0) return <NoData />;\n const visible = rows.slice(0, Math.max(1, height - 2));\n return (\n <Box flexDirection=\"column\">\n <Box>\n <Text color={theme.info.label}>{\"Contact\".padEnd(28)}</Text>\n <Text color={theme.info.label}>{\"❤\".padStart(4)}</Text>\n <Text color={theme.info.label}>{\"👍\".padStart(4)}</Text>\n <Text color={theme.info.label}>{\"👎\".padStart(4)}</Text>\n <Text color={theme.info.label}>{\"ha\".padStart(4)}</Text>\n <Text color={theme.info.label}>{\"‼\".padStart(4)}</Text>\n <Text color={theme.info.label}>{\"?\".padStart(4)}</Text>\n <Text color={theme.info.label}>{\"😀\".padStart(4)}</Text>\n <Text color={theme.info.label}>{\"Σ\".padStart(6)}</Text>\n </Box>\n {visible.map((r) => (\n <Box key={r.contact}>\n <Text wrap=\"truncate\">{trunc(r.contact, 27).padEnd(28)}</Text>\n <Text>{String(r.heart).padStart(4)}</Text>\n <Text>{String(r.thumbsUp).padStart(4)}</Text>\n <Text>{String(r.thumbsDown).padStart(4)}</Text>\n <Text>{String(r.haha).padStart(4)}</Text>\n <Text>{String(r.exclaim).padStart(4)}</Text>\n <Text>{String(r.question).padStart(4)}</Text>\n <Text>{String(r.emoji).padStart(4)}</Text>\n <Text color={theme.status.accent}>{String(r.total).padStart(6)}</Text>\n </Box>\n ))}\n </Box>\n );\n}\n\nfunction WrappedView({ result }: { result: WrappedResult }) {\n const theme = useTheme();\n return (\n <Box flexDirection=\"column\">\n <Box>\n <Text color={theme.help.desc}>\n {result.windowStart || \"—\"} → {result.windowEnd || \"—\"}\n </Text>\n </Box>\n <Box marginTop={1} gap={2}>\n <KV label=\"Sent\" value={result.totalSent.toLocaleString()} />\n <KV label=\"Received\" value={result.totalReceived.toLocaleString()} />\n <KV label=\"Reactions\" value={result.totalReactions.toLocaleString()} />\n </Box>\n <Box marginTop={1} flexDirection=\"column\">\n <Text color={theme.info.label}>Top contacts</Text>\n {result.topContacts.length === 0 ? (\n <Text color={theme.help.desc}>(none)</Text>\n ) : (\n result.topContacts.map((c) => (\n <Box key={c.contact}>\n <Text wrap=\"truncate\">{trunc(c.contact, 28).padEnd(30)}</Text>\n <Text color={theme.status.accent}>{String(c.total).padStart(6)}</Text>\n <Text color={theme.help.desc}>\n {\" \"}sent {c.sent} / received {c.received}\n </Text>\n </Box>\n ))\n )}\n </Box>\n <Box marginTop={1} flexDirection=\"column\">\n <KV\n label=\"Peak day\"\n value={result.peakDay ? `${result.peakDay.date} (${result.peakDay.count})` : \"—\"}\n />\n <KV\n label=\"Longest streak\"\n value={`${result.longestStreakDays}d${result.longestStreakContact ? ` with ${result.longestStreakContact}` : \"\"}`}\n />\n </Box>\n </Box>\n );\n}\n\nfunction KV({ label, value }: { label: string; value: string }) {\n const theme = useTheme();\n return (\n <Box>\n <Text color={theme.info.label}>{label}: </Text>\n <Text color={theme.info.value}>{value}</Text>\n </Box>\n );\n}\n\nfunction trunc(s: string, max: number): string {\n if (s.length <= max) return s;\n return `${s.slice(0, Math.max(0, max - 1))}…`;\n}\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`;\n if (ms < 3_600_000) return `${(ms / 60_000).toFixed(1)}m`;\n if (ms < 86_400_000) return `${(ms / 3_600_000).toFixed(1)}h`;\n return `${(ms / 86_400_000).toFixed(1)}d`;\n}\n","/**\n * Sidebar row for an analytics module instance. Matches the visual rhythm of\n * ConversationItem (3 content rows + separator) so cursor math in App.tsx\n * works without a per-module height table.\n */\nimport { Box, Text } from \"ink\";\nimport { useTheme } from \"../../themes/ThemeContext.js\";\nimport type { ModuleSidebarItemProps } from \"../types.js\";\nimport type { AnalyticsState } from \"./module.js\";\nimport { ANALYTIC_LABEL } from \"./Pane.js\";\n\nexport function AnalyticsSidebarItem({\n instance,\n selected,\n focused,\n width,\n lineNum,\n isLast,\n}: ModuleSidebarItemProps) {\n const theme = useTheme();\n const state = instance.state as AnalyticsState;\n const accent = instance.accentColor ?? theme.status.accent;\n\n return (\n <Box flexDirection=\"column\" width={width}>\n <Box\n flexDirection=\"column\"\n width={width}\n backgroundColor={selected ? theme.sidebar.selected : undefined}\n >\n {/* Title row */}\n <Box width={width}>\n {lineNum !== undefined && (\n <Text color={selected && focused ? theme.sent.bg : theme.lineNum}>\n {lineNum.padStart(3)}{\" \"}\n </Text>\n )}\n <Text color={selected && focused ? theme.sent.bg : accent}>\n {selected && focused ? \"▸\" : \"✦\"}\n </Text>\n <Text color={selected ? theme.sidebar.selectedFg : accent} bold>\n {\" \"}\n {ANALYTIC_LABEL[state.type]}\n </Text>\n </Box>\n\n {/* Subtitle row */}\n <Box width={width} paddingLeft={5}>\n <Text color={theme.sidebar.snippet} wrap=\"truncate\">\n range: {state.range}\n </Text>\n </Box>\n\n {/* Module-id row — matches slug position */}\n <Box\n width={width}\n justifyContent=\"flex-end\"\n paddingRight={1}\n backgroundColor={theme.sidebar.slugBg}\n >\n <Text color={theme.sidebar.slug} dimColor italic>\n ✦{instance.moduleId}\n </Text>\n </Box>\n </Box>\n\n {!isLast && (\n <Box paddingX={1}>\n <Text color={theme.sidebar.separator} dimColor>\n {theme.glyphs.separator.repeat(Math.max(width - 4, 1))}\n </Text>\n </Box>\n )}\n </Box>\n );\n}\n","/**\n * Analytics module — surfaces the six implemented analytics from\n * `src/analytics.ts` as palette commands. Each command opens a virtual\n * sidebar row whose pane lets the user flip between analytic types and\n * date ranges without leaving the row.\n */\nimport type { AnalyticType } from \"../../../analytics.js\";\nimport { IMPLEMENTED_TYPES } from \"../../../analytics.js\";\nimport type { FeatureModule, ModuleCommand, ModuleInstance } from \"../types.js\";\nimport { ANALYTIC_LABEL, AnalyticsPane } from \"./Pane.js\";\nimport { AnalyticsSidebarItem } from \"./SidebarItem.js\";\n\nexport type AnalyticsRange = \"30d\" | \"90d\" | \"1y\" | \"all\";\n\nexport interface AnalyticsState {\n type: AnalyticType;\n range: AnalyticsRange;\n /** When set, the pane scopes its dataset to that chat identifier. */\n chatIdentifier: string | null;\n}\n\nexport const ANALYTIC_DESCRIPTION: Record<AnalyticType, string> = {\n messaging_streaks: \"Longest and current daily-message streaks per contact\",\n double_texts: \"Consecutive messages without a reply\",\n response_time_stats: \"Reply latency (median, p95, mean)\",\n daily_heatmap: \"7×24 grid of activity by weekday × hour\",\n tapback_summary: \"Tapback reactions sent per contact\",\n year_in_review_wrapped: \"Wrapped summary: top contacts, peak day, totals\",\n};\n\nlet instanceCounter = 0;\n\nfunction makeInstance(type: AnalyticType): ModuleInstance {\n instanceCounter += 1;\n const state: AnalyticsState = { type, range: \"30d\", chatIdentifier: null };\n return {\n id: `analytics.${type}-${Date.now()}-${instanceCounter}`,\n moduleId: \"analytics\",\n title: `Analytics: ${ANALYTIC_LABEL[type]}`,\n subtitle: \"range: 30d\",\n state,\n };\n}\n\nconst commands: ModuleCommand[] = IMPLEMENTED_TYPES.map((type) => ({\n id: `analytics.${type}`,\n title: `Analytics: ${ANALYTIC_LABEL[type]}`,\n description: ANALYTIC_DESCRIPTION[type],\n open: () => makeInstance(type),\n}));\n\nexport const analyticsModule: FeatureModule = {\n id: \"analytics\",\n name: \"Analytics\",\n commands,\n SidebarItem: AnalyticsSidebarItem,\n Pane: AnalyticsPane,\n};\n","/**\n * Static registry of feature modules. To add a new module, drop its folder\n * under `src/tui/modules/<id>/`, export a `FeatureModule`, and append it\n * here. The palette + sidebar pick it up automatically.\n */\nimport type { Command } from \"../keymap.js\";\nimport { CORE_COMMANDS } from \"../keymap.js\";\nimport type { AppState } from \"../types.js\";\nimport { analyticsModule } from \"./analytics/module.js\";\nimport type { FeatureModule, ModuleCommand } from \"./types.js\";\n\nexport const MODULES: FeatureModule[] = [analyticsModule];\n\n/** Look up a module by id. Returns undefined for unknown ids. */\nexport function findModule(moduleId: string): FeatureModule | undefined {\n return MODULES.find((m) => m.id === moduleId);\n}\n\n/** Lift a `ModuleCommand` into the unified `Command` shape used by the palette. */\nfunction liftModuleCommand(mod: FeatureModule, cmd: ModuleCommand): Command {\n return {\n id: cmd.id,\n title: cmd.title,\n description: cmd.description,\n category: mod.name,\n keybinding: cmd.keybinding,\n run: (ctx) => {\n const inst = cmd.open(ctx);\n if (inst) ctx.dispatch({ type: \"OPEN_MODULE_INSTANCE\", instance: inst });\n },\n };\n}\n\n/** Concatenate core commands with every registered module's commands. */\nexport function allCommands(state: AppState): Command[] {\n const out: Command[] = [];\n for (const c of CORE_COMMANDS) {\n if (c.when && !c.when(state)) continue;\n out.push(c);\n }\n for (const mod of MODULES) {\n for (const cmd of mod.commands) {\n out.push(liftModuleCommand(mod, cmd));\n }\n }\n return out;\n}\n","/**\n * Grapheme-aware visual width and truncation helpers.\n *\n * The sidebar `ConversationItem` was truncating by `String#length`, which\n * counts UTF-16 code units. That splits surrogate pairs (one emoji is two\n * code units) and produces broken glyphs in the terminal. It also ignores\n * the fact that an emoji takes two terminal cells, not one.\n *\n * Approach:\n * - Walk the string with `Intl.Segmenter` (granularity: \"grapheme\") so we\n * never split inside a grapheme cluster (single emoji, family ZWJ, flag).\n * - Approximate each cluster's terminal cell width with a fast range\n * check — emoji, CJK, fullwidth and regional indicators are 2 cells;\n * everything else (incl. ASCII, Latin diacritics, combining marks) is 1.\n *\n * Width approximation is coarse but covers every real-world iMessage handle\n * we care about (emoji-in-display-name is the dominant failure mode).\n */\n\nconst SEG = new Intl.Segmenter(\"en\", { granularity: \"grapheme\" });\n\n/**\n * Returns the approximate width in monospaced terminal cells of a single\n * grapheme cluster. 2 for emoji / CJK / fullwidth, 1 otherwise.\n */\nexport function clusterWidth(cluster: string): number {\n // ASCII fast path — single-byte and 1-cell.\n if (cluster.length === 1) {\n const code = cluster.charCodeAt(0);\n if (code < 0x80) return 1;\n }\n for (const ch of cluster) {\n const cp = ch.codePointAt(0);\n if (cp === undefined) continue;\n // Note: the 0x2600..0x27BF \"misc symbols + dingbats\" range (▶ ◀ ● ✉\n // ✓ ✗ ★ etc) is *ambiguous-width* per UAX #11 but monospaced\n // terminals render it as single-cell — we leave it at 1 to match\n // what the TUI's `safe` theme actually paints.\n if (\n // CJK Unified Ideographs + Compatibility — wide.\n (cp >= 0x3000 && cp <= 0x9fff) ||\n // Hangul Syllables.\n (cp >= 0xac00 && cp <= 0xd7a3) ||\n // CJK Compatibility Ideographs.\n (cp >= 0xf900 && cp <= 0xfaff) ||\n // Fullwidth forms.\n (cp >= 0xff00 && cp <= 0xff60) ||\n // Halfwidth / fullwidth fence range (still 2-cell for fullwidth).\n (cp >= 0xffe0 && cp <= 0xffe6) ||\n // Misc symbols and pictographs — the true emoji plane.\n cp >= 0x1f300\n ) {\n return 2;\n }\n }\n return 1;\n}\n\n/** Total monospaced cell width of a string. */\nexport function visualWidth(str: string): number {\n let w = 0;\n for (const { segment } of SEG.segment(str)) {\n w += clusterWidth(segment);\n }\n return w;\n}\n\n/**\n * Truncates `str` so that the returned string (including any ellipsis if\n * applied) fits within `maxCols` terminal cells. Never splits inside a\n * grapheme cluster. If `maxCols <= 0`, returns \"\".\n */\nexport function truncateToWidth(str: string, maxCols: number, ellipsis = \"…\"): string {\n if (maxCols <= 0) return \"\";\n if (visualWidth(str) <= maxCols) return str;\n\n const ellipsisW = visualWidth(ellipsis);\n // If the ellipsis itself doesn't fit, take whatever clusters do and\n // skip the ellipsis — better than returning \"\" or a clipped ellipsis.\n if (ellipsisW >= maxCols) {\n let usedFallback = 0;\n let outFallback = \"\";\n for (const { segment } of SEG.segment(str)) {\n const w = clusterWidth(segment);\n if (usedFallback + w > maxCols) break;\n outFallback += segment;\n usedFallback += w;\n }\n return outFallback;\n }\n\n let used = 0;\n let out = \"\";\n for (const { segment } of SEG.segment(str)) {\n const w = clusterWidth(segment);\n if (used + w + ellipsisW > maxCols) break;\n out += segment;\n used += w;\n }\n return out + ellipsis;\n}\n","import { Box, Text } from \"ink\";\nimport type { Conversation } from \"../../types.js\";\nimport { truncateToWidth } from \"../../visual-width.js\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\n\nfunction relativeDate(date: Date | null): string {\n if (!date) return \"\";\n const now = new Date();\n const time = date.toLocaleTimeString(\"en-US\", {\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true,\n });\n if (date.toDateString() === now.toDateString()) return time;\n const y = new Date(now);\n y.setDate(now.getDate() - 1);\n if (date.toDateString() === y.toDateString()) return \"Yest\";\n return `${date.getMonth() + 1}/${date.getDate()}`;\n}\n\ninterface Props {\n conversation: Conversation;\n selected: boolean;\n width: number;\n lineNum?: string;\n focused?: boolean;\n isLast?: boolean;\n}\n\n/**\n * Width budget for the conversation row. Returning a precise budget for the\n * name text prevents the time/icons from wrapping onto the next row and\n * obscuring the snippet below — the original symptom of the layout bug.\n *\n * Layout (single row):\n * [lineNum 4][cursor 2][envelope 2?][group 2?][NAME ...truncated...] (count?) icon time\n * ^^^^^^^^^^^^^^^^^^^^\n * this is what we budget\n */\nfunction nameBudget(\n width: number,\n hasUnread: boolean,\n unreadCount: number,\n isGroup: boolean,\n): number {\n const lineNumW = 4; // \"123 \"\n const cursorW = 2; // \"▸ \" or \" \"\n const envelopeW = hasUnread ? 2 : 0;\n const groupW = isGroup ? 2 : 0;\n const countW = hasUnread ? ` (${unreadCount})`.length : 0;\n const iconW = 2; // \" 💬\"\n const timeW = 9; // max \" 12:34 PM\"\n const padding = 2; // safety margin\n return Math.max(\n width - lineNumW - cursorW - envelopeW - groupW - countW - iconW - timeW - padding,\n 8,\n );\n}\n\nexport function ConversationItem({\n conversation: c,\n selected,\n width,\n lineNum,\n focused,\n isLast,\n}: Props) {\n const theme = useTheme();\n const hasUnread = c.unreadCount > 0;\n const name = c.displayName ?? c.chatIdentifier;\n const time = relativeDate(c.lastMessageDate);\n const snippet = c.lastMessageSnippet ?? \"\";\n const serviceIcon = c.serviceType === \"SMS\" ? theme.glyphs.sms : theme.glyphs.iMessage;\n\n const nameW = nameBudget(width, hasUnread, c.unreadCount, c.isGroupChat);\n // Grapheme-aware truncation — never splits a surrogate pair (emoji)\n // and budgets each emoji as 2 terminal cells. See src/visual-width.ts.\n const truncatedName = truncateToWidth(name, nameW);\n const snippetW = Math.max(width - 6, 8); // -5 for left padding, -1 safety\n const truncatedSnippet = truncateToWidth(snippet, snippetW, \"\");\n\n return (\n <Box flexDirection=\"column\" width={width}>\n <Box\n flexDirection=\"column\"\n width={width}\n backgroundColor={selected ? theme.sidebar.selected : undefined}\n >\n {/* Name row — left+right split so time can't wrap */}\n <Box width={width} justifyContent=\"space-between\">\n {/* Left side */}\n <Box flexShrink={1}>\n {lineNum !== undefined && (\n <Text color={selected && focused ? theme.sent.bg : theme.lineNum}>\n {lineNum.padStart(3)}{\" \"}\n </Text>\n )}\n <Text color={selected && focused ? theme.sent.bg : undefined}>\n {selected && focused ? \"▸\" : \" \"}\n </Text>\n {hasUnread && <Text color={theme.dot}>{theme.glyphs.envelope} </Text>}\n {c.isGroupChat && <Text color={theme.info.label}>{theme.glyphs.group} </Text>}\n <Text\n color={\n selected\n ? theme.sidebar.selectedFg\n : hasUnread\n ? theme.sidebar.unread\n : theme.sidebar.read\n }\n bold={hasUnread}\n wrap=\"truncate\"\n >\n {truncatedName}\n </Text>\n </Box>\n\n {/* Right side: count + service icon + time, fixed-width */}\n <Box flexShrink={0}>\n {hasUnread && <Text color={theme.sidebar.unread}> ({c.unreadCount})</Text>}\n <Text color={c.serviceType === \"SMS\" ? theme.sms : theme.info.label}>\n {\" \"}\n {serviceIcon}\n </Text>\n <Text color={theme.sidebar.time}> {time}</Text>\n </Box>\n </Box>\n\n {/* Snippet row — explicit width so it can't bleed into adjacent rows */}\n <Box width={width} paddingLeft={5}>\n <Text color={theme.sidebar.snippet} wrap=\"truncate\">\n {truncatedSnippet}\n </Text>\n </Box>\n\n {/* Slug row — right-justified, italic, dim, subtle bg */}\n <Box\n width={width}\n justifyContent=\"flex-end\"\n paddingRight={1}\n backgroundColor={theme.sidebar.slugBg}\n >\n <Text color={theme.sidebar.slug} dimColor italic>\n ~{c.threadSlug}\n </Text>\n </Box>\n </Box>\n\n {/* Separator between items */}\n {!isLast && (\n <Box paddingX={1}>\n <Text color={theme.sidebar.separator} dimColor>\n {theme.glyphs.separator.repeat(Math.max(width - 4, 1))}\n </Text>\n </Box>\n )}\n </Box>\n );\n}\n\n// Exported for tests\nexport { nameBudget };\n","/**\n * Default sidebar row renderer used when a feature module doesn't supply its\n * own. Visually mirrors `ConversationItem` (3 rows + separator) so the\n * sidebar cursor math is unchanged.\n */\nimport { Box, Text } from \"ink\";\nimport type { ModuleSidebarItemProps } from \"../modules/types.js\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\n\nexport function DefaultModuleSidebarItem({\n instance,\n selected,\n focused,\n width,\n lineNum,\n isLast,\n}: ModuleSidebarItemProps) {\n const theme = useTheme();\n const accent = instance.accentColor ?? theme.status.accent;\n\n return (\n <Box flexDirection=\"column\" width={width}>\n <Box\n flexDirection=\"column\"\n width={width}\n backgroundColor={selected ? theme.sidebar.selected : undefined}\n >\n <Box width={width}>\n {lineNum !== undefined && (\n <Text color={selected && focused ? theme.sent.bg : theme.lineNum}>\n {lineNum.padStart(3)}{\" \"}\n </Text>\n )}\n <Text color={selected && focused ? theme.sent.bg : accent}>\n {selected && focused ? \"▸\" : \"✦\"}\n </Text>\n <Text color={selected ? theme.sidebar.selectedFg : accent} bold>\n {\" \"}\n {instance.title}\n </Text>\n </Box>\n\n <Box width={width} paddingLeft={5}>\n <Text color={theme.sidebar.snippet} wrap=\"truncate\">\n {instance.subtitle ?? \"\"}\n </Text>\n </Box>\n\n <Box\n width={width}\n justifyContent=\"flex-end\"\n paddingRight={1}\n backgroundColor={theme.sidebar.slugBg}\n >\n <Text color={theme.sidebar.slug} dimColor italic>\n ✦{instance.moduleId}\n </Text>\n </Box>\n </Box>\n\n {!isLast && (\n <Box paddingX={1}>\n <Text color={theme.sidebar.separator} dimColor>\n {theme.glyphs.separator.repeat(Math.max(width - 4, 1))}\n </Text>\n </Box>\n )}\n </Box>\n );\n}\n","import { Box, Text } from \"ink\";\nimport { useMemo } from \"react\";\nimport type { Conversation } from \"../../types.js\";\nimport { matchesConversationFilter } from \"../filter.js\";\nimport { findModule } from \"../modules/registry.js\";\nimport type { ModuleInstance } from \"../modules/types.js\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\nimport { ConversationItem } from \"./ConversationItem.js\";\nimport { DefaultModuleSidebarItem } from \"./DefaultModuleSidebarItem.js\";\n\ninterface Props {\n conversations: Conversation[];\n moduleInstances: ModuleInstance[];\n selectedIdx: number;\n /** Index into `moduleInstances` when a virtual row is selected; null otherwise. */\n selectedModuleIdx: number | null;\n scrollOffset: number;\n filterQuery: string;\n focused: boolean;\n width: number;\n height: number;\n}\n\nexport function Sidebar({\n conversations,\n moduleInstances,\n selectedIdx,\n selectedModuleIdx,\n scrollOffset,\n filterQuery,\n focused,\n width,\n height,\n}: Props) {\n const theme = useTheme();\n const filtered = useMemo(() => {\n if (!filterQuery) return conversations;\n const q = filterQuery.toLowerCase();\n return conversations.filter((c) => matchesConversationFilter(c, q));\n }, [conversations, filterQuery]);\n\n // Each row takes ~4 rows (3 content + 1 separator). Module rows match this\n // rhythm so the cursor math in App.tsx doesn't need a per-row height table.\n const itemHeight = 4;\n const headerH = 1 + (filterQuery ? 1 : 0);\n const borderH = 2;\n const visibleCount = Math.floor((height - headerH - borderH) / itemHeight);\n\n // Combined visible list: [...moduleInstances, ...conversations].\n // The scrollOffset is an index into the combined list.\n const combined: Array<\n { kind: \"module\"; instance: ModuleInstance } | { kind: \"conv\"; conv: Conversation }\n > = [\n ...moduleInstances.map((instance) => ({ kind: \"module\" as const, instance })),\n ...filtered.map((conv) => ({ kind: \"conv\" as const, conv })),\n ];\n const visible = combined.slice(scrollOffset, scrollOffset + visibleCount);\n const moduleCount = moduleInstances.length;\n\n // The displayed cursor index in the combined list.\n const combinedCursor = selectedModuleIdx != null ? selectedModuleIdx : moduleCount + selectedIdx;\n\n return (\n <Box\n flexDirection=\"column\"\n width={width}\n height={height}\n borderStyle=\"single\"\n borderColor={focused ? theme.header.focused.fg : theme.border}\n overflow=\"hidden\"\n >\n <Box paddingX={1} backgroundColor={focused ? theme.header.focused.bg : theme.header.dim.bg}>\n <Text color={focused ? theme.header.focused.fg : theme.header.dim.fg} bold={focused}>\n Conversations ({filtered.length}\n {moduleCount > 0 ? ` + ${moduleCount} tool${moduleCount === 1 ? \"\" : \"s\"}` : \"\"})\n </Text>\n </Box>\n\n {filterQuery && (\n <Box paddingX={1}>\n <Text color={theme.status.accent}>/ </Text>\n <Text color={theme.compose.fg}>{filterQuery}</Text>\n </Box>\n )}\n\n <Box flexDirection=\"column\" flexGrow={1} overflow=\"hidden\">\n {visible.length === 0 ? (\n <Box paddingX={1}>\n <Text color={theme.sidebar.snippet}>No conversations</Text>\n </Box>\n ) : (\n visible.map((row, i) => {\n const realIdx = scrollOffset + i;\n const selected = realIdx === combinedCursor;\n const relNum = selected ? `${realIdx}` : `${Math.abs(realIdx - combinedCursor)}`;\n const isLast = i === visible.length - 1;\n\n if (row.kind === \"module\") {\n const mod = findModule(row.instance.moduleId);\n const ItemComponent = mod?.SidebarItem ?? DefaultModuleSidebarItem;\n return (\n <ItemComponent\n key={row.instance.id}\n instance={row.instance}\n selected={selected}\n focused={!!focused}\n width={width - 2}\n lineNum={relNum}\n isLast={isLast}\n />\n );\n }\n\n return (\n <ConversationItem\n key={row.conv.threadSlug}\n conversation={row.conv}\n selected={selected}\n width={width - 2}\n lineNum={relNum}\n focused={focused}\n isLast={isLast}\n />\n );\n })\n )}\n </Box>\n </Box>\n );\n}\n","import { Box, Text } from \"ink\";\nimport type React from \"react\";\nimport type { Conversation } from \"../../types.js\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\n\ninterface Props {\n totalUnread: number;\n selected: Conversation | undefined;\n status: string;\n loading: boolean;\n children?: React.ReactNode;\n}\n\nexport function StatusBar({ totalUnread, selected, status, loading, children }: Props) {\n const theme = useTheme();\n return (\n <Box backgroundColor={theme.status.bg} paddingX={1} height={1} justifyContent=\"space-between\">\n {/* Each segment uses flexShrink={0} so the row's `gap={2}` separators\n * don't collapse when the status toast on the right side grows —\n * pre-fix you'd see \"Mum SMSRust parser + TS 0%0MB\" glued together.\n * The status text on the right truncates to a single line instead of\n * wrapping onto a second row. */}\n <Box gap={2} flexShrink={0}>\n {totalUnread > 0 && (\n <Text color={theme.status.accent} bold>\n ● {totalUnread} unread\n </Text>\n )}\n {selected && (\n <Text color={theme.status.fg}>{selected.displayName ?? selected.chatIdentifier}</Text>\n )}\n {selected && (\n <Text color={selected.serviceType === \"SMS\" ? theme.sms : theme.info.label}>\n {selected.serviceType}\n </Text>\n )}\n </Box>\n <Box gap={2} flexShrink={1} overflow=\"hidden\">\n {children}\n <Text color={theme.status.fg} wrap=\"truncate\">\n {loading ? \"loading...\" : status}\n </Text>\n </Box>\n </Box>\n );\n}\n","import { TextInput } from \"@inkjs/ui\";\nimport { Box, Text } from \"ink\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\nimport type { Mode } from \"../types.js\";\n\ninterface Props {\n mode: Mode;\n recipientName: string;\n onChangeText: (text: string) => void;\n onSubmit: (text: string) => void;\n}\n\nexport function ComposeBar({ mode, recipientName, onChangeText, onSubmit }: Props) {\n const theme = useTheme();\n if (mode === \"confirm\") {\n return (\n <Box backgroundColor={theme.compose.bg} paddingX={1} height={1}>\n <Text color={theme.status.accent} bold>\n Send to {recipientName}?{\" \"}\n </Text>\n <Text color={theme.compose.fg}>Enter: send Esc: cancel</Text>\n </Box>\n );\n }\n\n if (mode === \"compose\") {\n return (\n <Box backgroundColor={theme.compose.bg} paddingX={1} height={1}>\n <Text color={theme.compose.fg}>&gt; </Text>\n <TextInput onChange={onChangeText} onSubmit={onSubmit} placeholder=\"Type a message...\" />\n </Box>\n );\n }\n\n return null;\n}\n","import { Box, Text } from \"ink\";\nimport type { Message, Reaction } from \"../../types.js\";\nimport { TAPBACK_EMOJI } from \"../theme.js\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\n\nfunction relativeDate(date: Date): string {\n const now = new Date();\n const time = date.toLocaleTimeString(\"en-US\", {\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true,\n });\n if (date.toDateString() === now.toDateString()) return time;\n const y = new Date(now);\n y.setDate(now.getDate() - 1);\n if (date.toDateString() === y.toDateString()) return `Yest ${time}`;\n return `${date.getMonth() + 1}/${date.getDate()} ${time}`;\n}\n\nfunction formatReactions(reactions: Reaction[]): string {\n const counts = new Map<string, number>();\n for (const r of reactions) {\n if (r.isRemoval) continue;\n const emoji = r.emoji ?? TAPBACK_EMOJI[r.type] ?? r.type;\n counts.set(emoji, (counts.get(emoji) ?? 0) + 1);\n }\n if (counts.size === 0) return \"\";\n return [...counts.entries()].map(([e, c]) => (c > 1 ? `${e}${c}` : e)).join(\"\");\n}\n\n/** Check if two dates are on different calendar days */\nexport function isDifferentDay(a: Date, b: Date): boolean {\n return a.toDateString() !== b.toDateString();\n}\n\n/** Format a date separator label */\nexport function dateSeparator(date: Date): string {\n const now = new Date();\n if (date.toDateString() === now.toDateString()) return \"Today\";\n const y = new Date(now);\n y.setDate(now.getDate() - 1);\n if (date.toDateString() === y.toDateString()) return \"Yesterday\";\n return date.toLocaleDateString(\"en-US\", { weekday: \"short\", month: \"short\", day: \"numeric\" });\n}\n\n/** Determine if a message is the first in a group from the same sender */\nexport function isGroupStart(msg: Message, prev: Message | undefined): boolean {\n if (!prev) return true;\n return msg.isFromMe !== prev.isFromMe || msg.handle !== prev.handle;\n}\n\n/** Determine if a message is the last in a group from the same sender */\nexport function isGroupEnd(msg: Message, next: Message | undefined): boolean {\n if (!next) return true;\n return msg.isFromMe !== next.isFromMe || msg.handle !== next.handle;\n}\n\ninterface Props {\n message: Message;\n maxWidth: number;\n showSender?: boolean;\n senderName?: string;\n selected?: boolean;\n lineNum?: string;\n isFirstInGroup?: boolean; // first message from this sender in a consecutive run\n isLastInGroup?: boolean; // last message from this sender in a consecutive run\n bgTint?: string; // alternating background for sender groups\n /**\n * Optional resolver for reply target text by GUID. When `m.isReply` is\n * true but `m.replyTo.replyToText` is null/undefined (the iMessage DB\n * sometimes leaves it unpopulated even when the link metadata exists),\n * we fall back to looking up the target message in the currently loaded\n * messages array via this callback. If still not found, render a\n * placeholder so the reader at least sees the reply indicator.\n */\n lookupReplyText?: (guid: string) => string | null;\n /** True when this message is inside an active visual selection range. */\n inSelection?: boolean;\n}\n\n/**\n * Compact message row — no borders, minimal vertical space.\n * Sender groups get alternating background tints.\n * First message in group shows sender name; subsequent ones are indented.\n */\nexport function MessageBubble({\n message: m,\n maxWidth,\n showSender,\n senderName,\n selected,\n lineNum,\n isFirstInGroup,\n isLastInGroup,\n bgTint,\n lookupReplyText,\n inSelection,\n}: Props) {\n const theme = useTheme();\n const isSent = m.isFromMe;\n const text = m.text ?? \"(attachment)\";\n const timestamp = relativeDate(m.date);\n const reactions = m.reactions ? formatReactions(m.reactions) : \"\";\n const hasAttachments = m.hasAttachments;\n\n // Cursor indicator\n const cursor = selected ? \"▸\" : \" \";\n const cursorColor = selected ? theme.sent.bg : undefined;\n\n // Only show sender on first message in a group\n const sender = isFirstInGroup\n ? showSender && !isSent && senderName\n ? senderName\n : isSent\n ? \"Me\"\n : undefined\n : undefined;\n\n // Group separator: thin line between groups\n const groupSepChar = isFirstInGroup ? \"┌\" : isLastInGroup ? \"└\" : \"│\";\n const groupColor = isSent ? theme.sent.border : theme.received.border;\n\n return (\n <Box\n flexDirection=\"column\"\n backgroundColor={selected ? theme.sidebar.selected : inSelection ? theme.selectionBg : bgTint}\n >\n <Box>\n {/*\n * Prefix block — line number, cursor, group gutter, timestamp,\n * direction glyph, sender. Rendered as a SINGLE <Text> with nested\n * colored spans so Ink treats the whole prefix as one atomic\n * shrink-unit. Splitting it into separate <Text> siblings made\n * Ink's flex-shrink chop one char off each on overflow rows —\n * e.g. `9:49 PM ◀` rendered as `9:49 P◀` (M and trailing space\n * eaten), `Me: ` rendered as `Me:` (running into the message body).\n * Wrapped in a Box with flexShrink={0} so the prefix never loses\n * a char; the message <Text> below (with wrap=\"truncate\") is the\n * sole shrinkable element on the row.\n */}\n <Box flexShrink={0}>\n <Text>\n {lineNum !== undefined && (\n <Text color={selected ? theme.sent.bg : theme.lineNum}>{lineNum.padStart(3)} </Text>\n )}\n <Text color={cursorColor}>{cursor}</Text>\n <Text color={groupColor}>{groupSepChar} </Text>\n {isFirstInGroup ? (\n // pad to 14 (not 13) so even the widest natural timestamp\n // (`MM/DD HH:MM PM` = 13 chars, or `Yest 12:37 PM` = 13)\n // always has at least one trailing space before the glyph —\n // otherwise the row renders as `10:39 PM◀ Hey` with no gap.\n <Text color={theme.timestamp}>{timestamp.padEnd(14)}</Text>\n ) : (\n <Text color={theme.timestamp}>{\" \"}</Text>\n )}\n {isFirstInGroup ? (\n <>\n {isSent ? (\n <Text color={theme.sent.bg} bold>\n {theme.glyphs.sent}{\" \"}\n </Text>\n ) : (\n <Text color={theme.received.border} bold>\n {theme.glyphs.received}{\" \"}\n </Text>\n )}\n {sender && (\n <Text color={isSent ? theme.sent.bg : theme.senderName} bold>\n {sender.length > 12 ? `${sender.slice(0, 11)}…` : sender}\n {\": \"}\n </Text>\n )}\n </>\n ) : (\n // Continuation: indent to align with first message text\n <Text>\n {\" \"}\n {sender ? \" \" : \"\"}\n </Text>\n )}\n </Text>\n </Box>\n\n {/* Message text — the only shrinkable element on the row. */}\n <Text color={isSent ? theme.sentText : theme.receivedText} wrap=\"truncate\">\n {text}\n </Text>\n\n {/* Reactions */}\n {reactions && <Text> {reactions}</Text>}\n\n {/* Attachment indicator */}\n {hasAttachments && <Text color={theme.attachment}> 📎</Text>}\n\n {/* Edited indicator */}\n {m.isEdited && <Text color={theme.edited}> ✎</Text>}\n </Box>\n\n {/* Reply context — always render the indicator when isReply, even if\n replyToText is missing. iMessage sometimes leaves the text NULL\n even when the GUID link is present, so we try a runtime lookup\n and fall back to a placeholder so the user knows it IS a reply. */}\n {m.isReply &&\n (() => {\n let replyText: string | null = m.replyTo?.replyToText ?? null;\n if (!replyText && m.replyTo?.replyToGuid && lookupReplyText) {\n replyText = lookupReplyText(m.replyTo.replyToGuid);\n }\n const display = replyText\n ? replyText.slice(0, maxWidth - 12)\n : \"(replied to earlier message)\";\n return (\n <Box>\n {lineNum !== undefined && <Text>{\" \"}</Text>}\n <Text>{\" \"}</Text>\n <Text color={theme.replyContext} italic>\n {\" ↩ \"}\n {display}\n </Text>\n </Box>\n );\n })()}\n </Box>\n );\n}\n\ninterface PendingBubbleProps {\n text: string;\n status: \"sending\" | \"sent\" | \"failed\";\n maxWidth: number;\n}\n\nexport function PendingBubble({ text, status }: PendingBubbleProps) {\n const theme = useTheme();\n const indicator = status === \"sending\" ? \"⏳\" : status === \"failed\" ? \"⚠\" : \"✓\";\n const color = status === \"failed\" ? theme.edited : theme.pending.fg;\n\n return (\n <Box backgroundColor={theme.groupBg.sent}>\n <Text>{\" \"}</Text>\n <Text color={color}>{indicator} </Text>\n <Text color={theme.timestamp}>{\"now\".padEnd(14)}</Text>\n <Text color={theme.sent.bg} bold>\n {`${theme.glyphs.sent} Me: `}\n </Text>\n <Text color={theme.pending.fg} wrap=\"truncate\">\n {text}\n </Text>\n </Box>\n );\n}\n","import { Box, Text } from \"ink\";\nimport React, { useMemo } from \"react\";\nimport type { Conversation, Message } from \"../../types.js\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\nimport type { Mode, PendingMessage } from \"../types.js\";\nimport { ComposeBar } from \"./ComposeBar.js\";\nimport {\n dateSeparator,\n isDifferentDay,\n isGroupEnd,\n isGroupStart,\n MessageBubble,\n PendingBubble,\n} from \"./MessageBubble.js\";\n\ninterface Props {\n conversation: Conversation | undefined;\n messages: Message[];\n pending: PendingMessage[];\n resolvedNames: string[];\n scrollOffset: number;\n selectedMsgIdx: number;\n /** Anchor index for visual selection (set when V is pressed). null = no selection. */\n selectionAnchor: number | null;\n /** Eviction gap markers — placeholders showing \"N more messages — scroll to load\". */\n gapMarkers: Array<{ atIdx: number; oldestId: number; newestId: number; count: number }>;\n focused: boolean;\n width: number;\n height: number;\n mode: Mode;\n onChangeCompose: (text: string) => void;\n onSubmitCompose: (text: string) => void;\n}\n\nexport function ThreadPane({\n conversation,\n messages,\n pending,\n resolvedNames: _resolvedNames,\n scrollOffset: _scrollOffset,\n selectedMsgIdx,\n selectionAnchor,\n gapMarkers,\n focused,\n width,\n height,\n mode,\n onChangeCompose,\n onSubmitCompose,\n}: Props) {\n const theme = useTheme();\n const isGroup = conversation?.isGroupChat ?? false;\n const maxBubbleW = Math.max(width - 8, 20);\n const composing = mode === \"compose\" || mode === \"confirm\";\n\n // Available height for messages\n const headerH = 1;\n const composeH = composing ? 1 : 0;\n const borderH = 2;\n const msgAreaHeight = Math.max(height - headerH - composeH - borderH, 3);\n\n // Compute visible window anchored on selectedMsgIdx.\n // Account for multi-line items: replies take 2 lines, date separators take 1 extra line.\n const { visibleStart, visibleEnd } = useMemo(() => {\n const total = messages.length + pending.length;\n if (total === 0) return { visibleStart: 0, visibleEnd: 0 };\n\n let cursorIdx = selectedMsgIdx >= 0 ? selectedMsgIdx : total - 1;\n cursorIdx = Math.max(0, Math.min(cursorIdx, total - 1));\n\n // Bottom-anchor: when the cursor is at or near the last real message, walk\n // UP from the end accumulating real heights until we fill msgAreaHeight.\n // Fixes the clip where the last 1-3 messages run past Ink's overflow=\"hidden\"\n // because the downward fill loop never executes (end === total - 1 already).\n const NEAR_END = 2;\n if (cursorIdx >= messages.length - NEAR_END && pending.length === 0) {\n const end = messages.length - 1;\n let start = end;\n let totalLines = lineHeight(messages, end, maxBubbleW);\n while (\n start > 0 &&\n totalLines + lineHeight(messages, start - 1, maxBubbleW) <= msgAreaHeight\n ) {\n start--;\n totalLines += lineHeight(messages, start, maxBubbleW);\n }\n return { visibleStart: start, visibleEnd: end + 1 };\n }\n\n // Walk outward from cursor, counting actual lines consumed\n // Start by going upward from cursor\n let start = cursorIdx;\n let linesAbove = 0;\n const targetAbove = Math.floor(msgAreaHeight * 0.4);\n while (start > 0 && linesAbove < targetAbove) {\n start--;\n linesAbove += lineHeight(messages, start, maxBubbleW);\n }\n\n // Then go downward from cursor\n let end = cursorIdx;\n let totalLines = linesAbove + lineHeight(messages, cursorIdx, maxBubbleW);\n while (end < total - 1 && totalLines < msgAreaHeight) {\n end++;\n if (end < messages.length) {\n totalLines += lineHeight(messages, end, maxBubbleW);\n } else {\n totalLines += 1; // pending messages\n }\n }\n\n // If we have room, extend upward more\n while (start > 0 && totalLines < msgAreaHeight) {\n start--;\n totalLines += lineHeight(messages, start, maxBubbleW);\n }\n\n return { visibleStart: start, visibleEnd: end + 1 };\n // `messages` reference is preserved across non-messages reducer cases\n // (see types.ts reducer), so depending on length is sufficient for the\n // common fast path. Content-mutating actions (SET_MESSAGES /\n // PREPEND_MESSAGES) replace the whole array, which also flips length.\n }, [messages, pending.length, selectedMsgIdx, msgAreaHeight, maxBubbleW]);\n\n const visibleMessages = messages.slice(visibleStart, Math.min(visibleEnd, messages.length));\n\n // Build a GUID → text lookup so MessageBubble can resolve missing replyToText\n // from the loaded message set when iMessage didn't populate it. Memoized to\n // avoid rebuilding on every render.\n const messagesByGuid = useMemo(() => {\n const map = new Map<string, string>();\n for (const m of messages) {\n if (m.guid && m.text) map.set(m.guid, m.text);\n }\n return map;\n }, [messages]);\n\n const lookupReplyText = (guid: string): string | null => messagesByGuid.get(guid) ?? null;\n\n // Visual selection range — derived from anchor + cursor.\n const selRange: [number, number] | null =\n selectionAnchor != null && selectedMsgIdx >= 0\n ? [Math.min(selectionAnchor, selectedMsgIdx), Math.max(selectionAnchor, selectedMsgIdx)]\n : null;\n\n return (\n <Box\n flexDirection=\"column\"\n width={width}\n height={height}\n borderStyle=\"single\"\n borderColor={focused ? theme.header.focused.fg : theme.border}\n overflow=\"hidden\"\n >\n {/* Header.\n * flexShrink discipline: the LEFT side (name) shrinks first; \"(N\n * msgs)\" + the right column (identifier + service) keep their\n * width. Without these, narrowing the pane (e.g. opening dev\n * stats) wraps the header mid-character — \"iMessa\\nge\". */}\n <Box\n paddingX={1}\n backgroundColor={focused ? theme.header.focused.bg : theme.header.dim.bg}\n justifyContent=\"space-between\"\n flexShrink={0}\n >\n <Box flexShrink={1} overflow=\"hidden\">\n <Text\n color={focused ? theme.header.focused.fg : theme.header.dim.fg}\n bold={focused}\n wrap=\"truncate\"\n >\n {conversation?.displayName ?? conversation?.chatIdentifier ?? \"Thread\"}\n </Text>\n {conversation && (\n <Text color={theme.info.label} wrap=\"truncate\">{` (${messages.length} msgs)`}</Text>\n )}\n </Box>\n {conversation && (\n <Box gap={1} flexShrink={0}>\n {conversation.displayName && (\n <Text color={theme.info.label} wrap=\"truncate\">\n {conversation.rawIdentifier}\n </Text>\n )}\n <Text color={conversation.serviceType === \"SMS\" ? theme.sms : theme.info.label}>\n {conversation.serviceType}\n </Text>\n {conversation.isGroupChat && <Text color={theme.info.label}>Group</Text>}\n </Box>\n )}\n </Box>\n\n {/* Messages — compact rows with sender grouping */}\n <Box flexDirection=\"column\" flexGrow={1} overflow=\"hidden\">\n {messages.length === 0 && pending.length === 0 ? (\n <Box paddingX={1}>\n <Text color={theme.sidebar.snippet}>No messages</Text>\n </Box>\n ) : (\n <>\n {/* Scroll indicator top */}\n {visibleStart > 0 && (\n <Box justifyContent=\"center\">\n <Text color={theme.dateSep}>── ↑ {visibleStart} more ──</Text>\n </Box>\n )}\n\n {visibleMessages.map((msg, i) => {\n const realIdx = visibleStart + i;\n const prevMsg = realIdx > 0 ? messages[realIdx - 1] : undefined;\n const nextMsg = realIdx < messages.length - 1 ? messages[realIdx + 1] : undefined;\n const showDateSep = !prevMsg || isDifferentDay(prevMsg.date, msg.date);\n const firstInGroup = isGroupStart(msg, prevMsg);\n const lastInGroup = isGroupEnd(msg, nextMsg);\n\n // Alternating background tint based on sender\n const bgTint = msg.isFromMe ? theme.groupBg.sent : theme.groupBg.received;\n\n // Relative line number: distance from cursor\n const relNum =\n selectedMsgIdx >= 0\n ? realIdx === selectedMsgIdx\n ? `${realIdx}`\n : `${Math.abs(realIdx - selectedMsgIdx)}`\n : `${realIdx}`;\n\n return (\n <React.Fragment key={msg.id}>\n {(() => {\n // Gap marker — show \"N more messages\" before the first\n // message after an evicted region.\n const gap = gapMarkers.find((g) => g.atIdx === realIdx);\n if (!gap) return null;\n return (\n <Box justifyContent=\"center\" marginTop={1} marginBottom={1}>\n <Text color={theme.edited}>\n ─── {gap.count.toLocaleString()} older messages evicted (scroll back to\n reload) ───\n </Text>\n </Box>\n );\n })()}\n {showDateSep && (\n // Always 1 row of breathing room above date separators so the\n // visual rhythm is consistent — without this, separators that\n // appear after a same-sender continuation feel cramped while\n // ones after a different-sender row feel fine.\n <Box justifyContent=\"center\" marginTop={realIdx === 0 ? 0 : 1}>\n <Text color={theme.dateSep}>─── {dateSeparator(msg.date)} ───</Text>\n </Box>\n )}\n <MessageBubble\n message={msg}\n maxWidth={maxBubbleW}\n showSender={isGroup}\n senderName={msg.displayName ?? msg.handle}\n selected={realIdx === selectedMsgIdx && focused}\n lineNum={relNum}\n isFirstInGroup={firstInGroup || showDateSep}\n isLastInGroup={lastInGroup}\n bgTint={bgTint}\n lookupReplyText={lookupReplyText}\n inSelection={\n selRange != null && realIdx >= selRange[0] && realIdx <= selRange[1]\n }\n />\n {/* Group separator line between different senders */}\n {lastInGroup && nextMsg && !isDifferentDay(msg.date, nextMsg.date) && (\n <Box height={0} />\n )}\n </React.Fragment>\n );\n })}\n\n {/* Pending messages */}\n {pending.map((pm) => (\n <PendingBubble\n key={pm.text}\n text={pm.text}\n status={pm.status}\n maxWidth={maxBubbleW}\n />\n ))}\n\n {/* Scroll indicator bottom */}\n {visibleEnd < messages.length && (\n <Box justifyContent=\"center\">\n <Text color={theme.dateSep}>── ↓ {messages.length - visibleEnd} more ──</Text>\n </Box>\n )}\n </>\n )}\n </Box>\n\n {/* Compose bar */}\n {composing && (\n <ComposeBar\n mode={mode}\n recipientName={conversation?.displayName ?? conversation?.chatIdentifier ?? \"\"}\n onChangeText={onChangeCompose}\n onSubmit={onSubmitCompose}\n />\n )}\n </Box>\n );\n}\n\n/**\n * Estimate how many terminal lines a message at index `i` will consume.\n * Conservative upper bound: under-counts trigger bottom-of-thread clipping\n * (Ink's overflow=\"hidden\" silently drops rows past the box edge), so we err\n * toward over-counting wrap rows from text length.\n */\nfunction lineHeight(messages: Message[], i: number, bubbleWidth: number): number {\n if (i < 0 || i >= messages.length) return 1;\n const msg = messages[i];\n // Wrap rows for the message body. Bubble inner width is roughly bubbleWidth\n // minus padding/prefix (sender name, line number). Use max(20) so very narrow\n // terminals don't divide-by-near-zero.\n const innerW = Math.max(20, bubbleWidth - 4);\n const text = msg.text ?? \"\";\n let h = Math.max(1, Math.ceil(text.length / innerW));\n // Reply preview row(s)\n if (msg.isReply) {\n const replyLen = msg.replyTo?.replyToText?.length ?? 0;\n h += Math.max(1, Math.ceil(replyLen / Math.max(20, innerW - 4)));\n }\n // Date separator. Rendered with marginTop={1} when realIdx > 0\n // (ThreadPane.tsx ~line 216), so it actually occupies TWO terminal rows in\n // that case: 1 blank margin + 1 separator content. Under-counting this\n // pushes the last message past the box edge.\n if (i === 0) {\n h += 1;\n } else if (isDifferentDay(messages[i - 1].date, msg.date)) {\n h += 2;\n }\n // Attachment indicator row (paperclip + filename) when present\n if (msg.attachments && msg.attachments.length > 0) h += 1;\n return h;\n}\n\n/**\n * Find the index of the next sender-group boundary from the given position.\n * A group boundary is where the sender changes (isFromMe flips or handle changes).\n */\nexport function nextGroupBoundary(messages: Message[], fromIdx: number): number {\n if (fromIdx >= messages.length - 1) return messages.length - 1;\n const current = messages[fromIdx];\n // Skip to end of current group\n let i = fromIdx + 1;\n while (i < messages.length) {\n const m = messages[i];\n if (m.isFromMe !== current.isFromMe || m.handle !== current.handle) {\n return i;\n }\n i++;\n }\n return messages.length - 1;\n}\n\n/**\n * Find the index of the previous sender-group boundary from the given position.\n */\nexport function prevGroupBoundary(messages: Message[], fromIdx: number): number {\n if (fromIdx <= 0) return 0;\n const current = messages[fromIdx];\n // If we're at the start of a group, go to start of previous group\n const prev = messages[fromIdx - 1];\n if (prev.isFromMe !== current.isFromMe || prev.handle !== current.handle) {\n // We're at a boundary — find start of previous group\n let i = fromIdx - 1;\n while (i > 0) {\n const m = messages[i - 1];\n if (m.isFromMe !== prev.isFromMe || m.handle !== prev.handle) {\n return i;\n }\n i--;\n }\n return 0;\n }\n // We're in the middle of a group — go to start of current group\n let i = fromIdx - 1;\n while (i > 0) {\n const m = messages[i - 1];\n if (m.isFromMe !== current.isFromMe || m.handle !== current.handle) {\n return i;\n }\n i--;\n }\n return 0;\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { hasNativeModule } from \"../../native-bridge.js\";\nimport { readWatchdogState } from \"../../watchdog.js\";\n\nexport interface DevStatsData {\n engine: \"Rust parser + TS DB\" | \"TS\";\n cpuPercent: number;\n memMB: number;\n pid: number;\n uptime: string;\n lastQueryMs: number | null;\n eventLoopP99Ms: number;\n lastActivityAgo: string;\n}\n\nfunction formatUptime(seconds: number): string {\n if (seconds < 60) return `${Math.round(seconds)}s`;\n if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n return `${h}h${m}m`;\n}\n\nfunction formatAgo(ms: number): string {\n if (ms < 1_000) return \"now\";\n const sec = Math.floor(ms / 1_000);\n if (sec < 60) return `${sec}s`;\n if (sec < 3600) return `${Math.floor(sec / 60)}m`;\n return `${Math.floor(sec / 3600)}h`;\n}\n\nfunction engineLabel(): DevStatsData[\"engine\"] {\n return hasNativeModule() ? \"Rust parser + TS DB\" : \"TS\";\n}\n\nexport function useDevStats(visible: boolean): {\n stats: DevStatsData;\n recordQueryTime: (ms: number) => void;\n} {\n const [stats, setStats] = useState<DevStatsData>({\n engine: engineLabel(),\n cpuPercent: 0,\n memMB: 0,\n pid: process.pid,\n uptime: \"0s\",\n lastQueryMs: null,\n eventLoopP99Ms: 0,\n lastActivityAgo: \"now\",\n });\n\n const lastCpuRef = useRef(process.cpuUsage());\n const lastTimeRef = useRef(Date.now());\n const lastQueryMsRef = useRef<number | null>(null);\n\n useEffect(() => {\n if (!visible) return;\n const timer = setInterval(() => {\n const now = Date.now();\n const elapsed = now - lastTimeRef.current;\n if (elapsed === 0) return;\n\n const cpuNow = process.cpuUsage(lastCpuRef.current);\n // cpuUsage returns microseconds; convert to percentage of wall time\n const totalCpuUs = cpuNow.user + cpuNow.system;\n const cpuPercent = (totalCpuUs / 1000 / elapsed) * 100;\n\n lastCpuRef.current = process.cpuUsage();\n lastTimeRef.current = now;\n\n const { rss } = process.memoryUsage();\n const memMB = Math.round((rss / 1024 / 1024) * 10) / 10;\n\n const wd = readWatchdogState();\n setStats({\n engine: engineLabel(),\n cpuPercent: Math.round(cpuPercent * 10) / 10,\n memMB,\n pid: process.pid,\n uptime: formatUptime(process.uptime()),\n lastQueryMs: lastQueryMsRef.current,\n eventLoopP99Ms: Math.round(wd.eventLoopP99Ms * 10) / 10,\n lastActivityAgo: formatAgo(Date.now() - wd.lastActivityTs),\n });\n }, 2000);\n timer.unref();\n\n return () => clearInterval(timer);\n }, [visible]);\n\n const recordQueryTime = useCallback((ms: number) => {\n lastQueryMsRef.current = Math.round(ms * 10) / 10;\n }, []);\n\n return { stats, recordQueryTime };\n}\n","/**\n * In-memory cache of messages-per-chat for the TUI.\n *\n * Behavior:\n * - When the user re-enters a chat they've already viewed within\n * `STALE_MS`, return cached messages immediately (no DB round trip).\n * - Older entries get evicted on a TTL sweep every 60s.\n * - When heap pressure crosses `MEMORY_PRESSURE_MB` (sampled by the\n * watchdog), evict the LRU half of the cache until below threshold.\n *\n * Pure module — no React imports. Used by `useImsg.ts` and observable\n * via `cacheStats()` for the dev stats panel.\n */\n\nimport type { Message } from \"../types.js\";\nimport { onMemorySample } from \"../watchdog.js\";\n\ninterface CacheEntry {\n messages: Message[];\n oldestId: number; // for \"load older\" continuity\n loadedAt: number; // wall-clock ms\n lastAccess: number; // for LRU\n bytesEstimate: number;\n}\n\nfunction envNum(name: string, fallback: number): number {\n const raw = process.env[name];\n if (!raw) return fallback;\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;\n}\n\nconst TTL_MS = envNum(\"IMSG_TUI_CACHE_TTL_MS\", 600_000); // 10 min\nconst STALE_MS = envNum(\"IMSG_TUI_CACHE_STALE_MS\", 30_000); // 30s\nconst MEMORY_PRESSURE_MB = envNum(\"IMSG_TUI_CACHE_MEM_PRESSURE_MB\", 200); // heap MB\n\nconst cache = new Map<string, CacheEntry>();\nlet sweepTimer: ReturnType<typeof setInterval> | null = null;\nlet unsubMemSample: (() => void) | null = null;\n\nfunction estimateBytes(messages: Message[]): number {\n // Rough — only matters for relative sizing during eviction\n let bytes = 0;\n for (const m of messages) {\n bytes += (m.text?.length ?? 0) * 2; // UTF-16\n bytes += (m.handle?.length ?? 0) * 2;\n bytes += 80; // fixed overhead per message (date, ids, flags)\n }\n return bytes;\n}\n\n/** Get cached entry; returns undefined if missing. Touches lastAccess. */\nexport function getCached(chatIdentifier: string): CacheEntry | undefined {\n const entry = cache.get(chatIdentifier);\n if (entry) entry.lastAccess = Date.now();\n return entry;\n}\n\n/** Returns true if the entry is fresh enough to skip a DB round-trip. */\nexport function isFresh(entry: CacheEntry, now = Date.now()): boolean {\n return now - entry.loadedAt < STALE_MS;\n}\n\n/** Replace (or insert) the cache entry for a chat. */\nexport function setCached(chatIdentifier: string, messages: Message[], oldestId: number): void {\n const now = Date.now();\n cache.set(chatIdentifier, {\n messages,\n oldestId,\n loadedAt: now,\n lastAccess: now,\n bytesEstimate: estimateBytes(messages),\n });\n}\n\n/** Prepend older messages to an existing entry (dedup by id). */\nexport function prependCached(chatIdentifier: string, olderMessages: Message[]): void {\n const entry = cache.get(chatIdentifier);\n if (!entry) return;\n const existingIds = new Set(entry.messages.map((m) => m.id));\n const fresh = olderMessages.filter((m) => !existingIds.has(m.id));\n if (fresh.length === 0) return;\n const merged = [...fresh, ...entry.messages].sort((a, b) => a.date.getTime() - b.date.getTime());\n entry.messages = merged;\n // Reduce instead of `Math.min(entry.oldestId, ...fresh.map(...))`. The\n // spread form throws `RangeError: Maximum call stack size exceeded`\n // somewhere above ~125k arguments — reachable via batched older-load\n // on threads with huge history.\n let minFresh = fresh[0].id;\n for (let i = 1; i < fresh.length; i++) {\n if (fresh[i].id < minFresh) minFresh = fresh[i].id;\n }\n entry.oldestId = Math.min(entry.oldestId, minFresh);\n entry.lastAccess = Date.now();\n entry.bytesEstimate = estimateBytes(merged);\n}\n\n/** Clear all cached entries — used on shutdown / explicit refresh. */\nexport function clearCache(): void {\n cache.clear();\n}\n\n/** Number of cached chats and total estimated bytes. For dev stats display. */\nexport function cacheStats(): { entries: number; bytes: number } {\n let bytes = 0;\n for (const e of cache.values()) bytes += e.bytesEstimate;\n return { entries: cache.size, bytes };\n}\n\n/** TTL sweep: drop entries older than TTL_MS. Exported for tests. */\nexport function ttlSweep(now = Date.now()): number {\n let dropped = 0;\n for (const [k, v] of cache) {\n if (now - v.loadedAt > TTL_MS) {\n cache.delete(k);\n dropped++;\n }\n }\n return dropped;\n}\n\n/**\n * Memory-pressure eviction: when heap exceeds threshold, drop the LRU\n * half of the cache. Called from the watchdog memory sampler.\n */\nexport function evictUnderPressure(heapMb: number): number {\n if (heapMb < MEMORY_PRESSURE_MB) return 0;\n const sorted = [...cache.entries()].sort((a, b) => a[1].lastAccess - b[1].lastAccess);\n const half = Math.ceil(sorted.length / 2);\n for (let i = 0; i < half; i++) {\n cache.delete(sorted[i][0]);\n }\n return half;\n}\n\n/** Install TTL sweep + memory-pressure subscription. Idempotent. */\nexport function installCacheSweepers(): void {\n if (sweepTimer) return;\n\n sweepTimer = setInterval(() => {\n ttlSweep();\n }, 60_000);\n sweepTimer.unref();\n\n unsubMemSample = onMemorySample((_rss, heapMb) => {\n evictUnderPressure(heapMb);\n });\n}\n\n/** Stop sweepers. Used by tests + on shutdown. */\nexport function stopCacheSweepers(): void {\n if (sweepTimer) {\n clearInterval(sweepTimer);\n sweepTimer = null;\n }\n if (unsubMemSample) {\n unsubMemSample();\n unsubMemSample = null;\n }\n}\n","import { useCallback, useRef } from \"react\";\nimport { sendMessageAlt, sendToChat, sendToChatId } from \"../../applescript.js\";\nimport { getContactsDbPaths, getImsgDbPath, getSlugsDbPath } from \"../../config.js\";\nimport { IMessageDB } from \"../../imessage-db.js\";\nimport {\n defaultCountryFromEnv,\n type RecipientResolution,\n resolveRecipient,\n} from \"../../recipient.js\";\nimport { type Conversation, type Message, minMessageId } from \"../../types.js\";\nimport { getCached, isFresh, prependCached, setCached } from \"../messageCache.js\";\n\nexport function useImsg() {\n const dbRef = useRef<IMessageDB | null>(null);\n\n const getDb = useCallback(() => {\n if (!dbRef.current) {\n dbRef.current = new IMessageDB(getImsgDbPath(), getContactsDbPaths(), getSlugsDbPath());\n }\n return dbRef.current;\n }, []);\n\n const loadConversations = useCallback(\n async (limit = 200): Promise<Conversation[]> => {\n return getDb().listConversations(limit);\n },\n [getDb],\n );\n\n const loadMessages = useCallback(\n async (chatIdentifier: string): Promise<Message[]> => {\n // Read-through cache: return fresh entries directly without hitting the DB.\n const cached = getCached(chatIdentifier);\n if (cached && isFresh(cached)) return cached.messages;\n\n const messages = await getDb().getMessagesForChat(chatIdentifier, 200, {\n includeReactionDetails: true,\n });\n const oldestId = minMessageId(messages) ?? 0;\n setCached(chatIdentifier, messages, oldestId);\n return messages;\n },\n [getDb],\n );\n\n const loadOlderMessages = useCallback(\n async (chatIdentifier: string, beforeMessageId: number): Promise<Message[]> => {\n const older = await getDb().getMessagesForChat(chatIdentifier, 100, {\n includeReactionDetails: true,\n beforeMessageId,\n });\n // Merge into cache so subsequent re-entries see the full loaded history.\n if (older.length > 0) prependCached(chatIdentifier, older);\n return older;\n },\n [getDb],\n );\n\n /**\n * Load every message newer than the given Date — used by analytics panes\n * that compute over a time window rather than a single thread.\n */\n const loadMessagesInWindow = useCallback(\n async (since: Date): Promise<Message[]> => {\n return getDb().getMessagesInWindow(since.getTime());\n },\n [getDb],\n );\n\n const resolveNames = useCallback(\n (handles: string[]): string[] => {\n return getDb().resolveParticipantNames(handles);\n },\n [getDb],\n );\n\n const send = useCallback(\n async (threadSlug: string, text: string): Promise<{ success: boolean; error?: string }> => {\n const db = getDb();\n const slugRecord = db.getSlugRecord(threadSlug);\n if (!slugRecord) return { success: false, error: `Unknown slug: ${threadSlug}` };\n\n if (slugRecord.isGroup) {\n return slugRecord.displayName && !slugRecord.displayName.startsWith(\"chat\")\n ? sendToChat(slugRecord.displayName, text)\n : sendToChatId(slugRecord.chatGuid, text);\n }\n return sendMessageAlt(slugRecord.chatIdentifier, text);\n },\n [getDb],\n );\n\n /**\n * Resolve a free-form recipient (E.164 / local phone / iMessage email /\n * contact name) without sending — used by ComposeRecipientModal to validate\n * input as the user types and to surface ambiguous-contact candidates.\n */\n const resolveRecipientInput = useCallback(\n (input: string): RecipientResolution => {\n const db = getDb();\n return resolveRecipient(input, {\n contacts: db.contacts,\n defaultCountry: defaultCountryFromEnv(),\n });\n },\n [getDb],\n );\n\n /**\n * Send to an arbitrary recipient (not tied to an existing thread slug).\n * Runs the same `resolveRecipient` normalizer as the MCP path, so any of\n * the 4 input shapes work: E.164 phone, local phone, iMessage email,\n * unique contact name. Returns an error result for ambiguous matches —\n * caller should disambiguate before calling.\n */\n const sendToRecipient = useCallback(\n async (input: string, text: string): Promise<{ success: boolean; error?: string }> => {\n const resolution = resolveRecipientInput(input);\n if (resolution.kind === \"error\") return { success: false, error: resolution.message };\n if (resolution.kind === \"ambiguous\") {\n return {\n success: false,\n error: `Ambiguous: ${resolution.candidates.length} matches for \"${input}\". Pick a specific handle.`,\n };\n }\n return sendMessageAlt(resolution.handle, text);\n },\n [resolveRecipientInput],\n );\n\n const refresh = useCallback(() => {\n getDb().scheduleBackgroundRefresh();\n }, [getDb]);\n\n const close = useCallback(async () => {\n if (dbRef.current) {\n await dbRef.current.close();\n dbRef.current = null;\n }\n }, []);\n\n return {\n loadConversations,\n loadMessages,\n loadOlderMessages,\n loadMessagesInWindow,\n resolveNames,\n send,\n sendToRecipient,\n resolveRecipientInput,\n refresh,\n close,\n };\n}\n","import { useStdin } from \"ink\";\nimport { useEffect } from \"react\";\n\nexport interface MouseEvent {\n type: \"click\" | \"scroll-up\" | \"scroll-down\";\n x: number;\n y: number;\n button: number;\n}\n\n/**\n * Enable SGR mouse protocol and parse mouse events from stdin.\n * Calls `onEvent` for each click or scroll wheel event.\n */\nexport function useMouse(onEvent: (event: MouseEvent) => void) {\n const { stdin, setRawMode } = useStdin();\n\n useEffect(() => {\n // ?1000h = button-event tracking (press/release + scroll wheel only).\n // Do NOT use ?1003h (any-event tracking) — it fires for every pixel of\n // mouse motion, flooding stdin and pinning the event loop.\n // ?1006h = SGR extended coordinates so x/y aren't capped at 223.\n process.stdout.write(\"\\x1b[?1000h\\x1b[?1006h\");\n setRawMode(true);\n\n const handler = (data: Buffer) => {\n const str = data.toString();\n // SGR mouse format: \\x1b[<btn;x;y;M (press) or \\x1b[<btn;x;y;m (release)\n // biome-ignore lint/suspicious/noControlCharactersInRegex: ESC is the terminal mouse protocol prefix.\n const re = /\\x1b\\[<(\\d+);(\\d+);(\\d+)([Mm])/g;\n let match = re.exec(str);\n while (match !== null) {\n const btn = Number.parseInt(match[1], 10);\n const x = Number.parseInt(match[2], 10);\n const y = Number.parseInt(match[3], 10);\n const isPress = match[4] === \"M\";\n\n if (btn === 64 && isPress) {\n onEvent({ type: \"scroll-up\", x, y, button: btn });\n } else if (btn === 65 && isPress) {\n onEvent({ type: \"scroll-down\", x, y, button: btn });\n } else if (btn === 0 && isPress) {\n onEvent({ type: \"click\", x, y, button: btn });\n }\n match = re.exec(str);\n }\n };\n\n stdin.on(\"data\", handler);\n return () => {\n stdin.off(\"data\", handler);\n process.stdout.write(\"\\x1b[?1000l\\x1b[?1006l\");\n };\n }, [stdin, setRawMode, onEvent]);\n}\n","import { type Conversation, type Message, minMessageId } from \"../types.js\";\nimport type { ModuleInstance } from \"./modules/types.js\";\n\nexport type FocusPane = \"sidebar\" | \"thread\";\nexport type Mode =\n | \"browse\"\n | \"compose\"\n | \"compose-new\"\n | \"confirm\"\n | \"filter\"\n | \"drawer\"\n | \"select\"\n | \"export\"\n | \"date-jump\"\n | \"send-via\"\n | \"palette\";\n\nexport interface PendingMessage {\n text: string;\n sentAt: Date;\n status: \"sending\" | \"sent\" | \"failed\";\n}\n\nexport interface AppState {\n conversations: Conversation[];\n messages: Message[];\n selectedIdx: number;\n selectedMsgIdx: number; // cursor position in messages list\n focus: FocusPane;\n mode: Mode;\n sidebarScroll: number;\n threadScroll: number;\n composeText: string;\n filterQuery: string;\n pending: PendingMessage[];\n loading: boolean;\n status: string;\n numBuffer: string; // for vim-style number prefix (e.g. \"12j\")\n showDevStats: boolean;\n\n // Lazy-loading bookkeeping\n conversationLoadedCount: number; // how many we've requested from the DB so far\n conversationLoadingMore: boolean; // true while a \"load more\" fetch is in-flight\n messageOldestLoadedId: number | null; // oldest message ROWID currently in `messages` (for \"load older\")\n messageLoadingOlder: boolean; // true while a \"load older\" fetch is in-flight\n\n // Bounded message window — gaps appear when middle of history is evicted\n gapMarkers: Array<{ atIdx: number; oldestId: number; newestId: number; count: number }>;\n\n // Visual selection (vim V)\n selectionAnchor: number | null; // where the user pressed V — selection extends from anchor to selectedMsgIdx\n\n // Export modal state\n exportFormat: \"markdown\" | \"csv\" | \"json\";\n exportPath: string;\n exportStatus: string; // last export status message (e.g. \"Exported 247 msgs\")\n\n // Date-jump modal state\n dateJumpInput: string;\n dateJumpError: string;\n\n // Module instances rendered as virtual rows at the top of the sidebar.\n // The sidebar cursor traverses [...moduleInstances, ...conversations].\n moduleInstances: ModuleInstance[];\n /** Index into `moduleInstances` when a virtual row is selected; null when a real conversation is selected. */\n selectedModuleIdx: number | null;\n\n // Command palette state\n paletteQuery: string;\n paletteCursor: number;\n}\n\nexport type Action =\n | { type: \"SET_CONVERSATIONS\"; data: Conversation[] }\n | { type: \"SET_MESSAGES\"; data: Message[] }\n | { type: \"SELECT\"; index: number; visibleCount?: number }\n | { type: \"SELECT_MSG\"; index: number }\n | { type: \"MOVE_MSG\"; delta: number }\n | { type: \"FOCUS\"; pane: FocusPane }\n | { type: \"SCROLL_SIDEBAR\"; delta: number }\n | { type: \"SCROLL_THREAD\"; delta: number }\n | { type: \"SCROLL_THREAD_TO\"; position: number }\n | { type: \"ENTER_COMPOSE\" }\n | { type: \"UPDATE_COMPOSE\"; text: string }\n | { type: \"CONFIRM_SEND\" }\n | { type: \"CANCEL_COMPOSE\" }\n | { type: \"ADD_PENDING\"; msg: PendingMessage }\n | { type: \"RESOLVE_PENDING\"; text: string }\n | { type: \"FAIL_PENDING\"; text: string }\n | { type: \"SET_LOADING\"; loading: boolean; status?: string }\n | { type: \"SET_STATUS\"; status: string }\n | { type: \"ENTER_FILTER\" }\n | { type: \"UPDATE_FILTER\"; query: string }\n | { type: \"EXIT_FILTER\" }\n | { type: \"OPEN_DRAWER\" }\n | { type: \"CLOSE_DRAWER\" }\n | { type: \"SET_NUM_BUFFER\"; value: string }\n | { type: \"TOGGLE_DEV_STATS\" }\n | { type: \"APPEND_CONVERSATIONS\"; data: Conversation[]; loadedCount: number }\n | { type: \"PREPEND_MESSAGES\"; data: Message[]; oldestId: number }\n | { type: \"SET_LOADING_OLDER\"; loading: boolean }\n | { type: \"ENTER_SELECT_MODE\" }\n | { type: \"EXIT_SELECT_MODE\" }\n | { type: \"ENTER_EXPORT_MODE\"; defaultPath: string }\n | { type: \"EXIT_EXPORT_MODE\" }\n | { type: \"SET_EXPORT_FORMAT\"; format: \"markdown\" | \"csv\" | \"json\" }\n | { type: \"SET_EXPORT_PATH\"; path: string }\n | { type: \"SET_EXPORT_STATUS\"; status: string }\n | { type: \"ENTER_DATE_JUMP\" }\n | { type: \"EXIT_DATE_JUMP\" }\n | { type: \"ENTER_SEND_VIA\" }\n | { type: \"EXIT_SEND_VIA\" }\n | { type: \"ENTER_COMPOSE_NEW\" }\n | { type: \"EXIT_COMPOSE_NEW\" }\n | { type: \"SET_DATE_JUMP_INPUT\"; value: string }\n | { type: \"SET_DATE_JUMP_ERROR\"; error: string }\n | { type: \"OPEN_PALETTE\" }\n | { type: \"CLOSE_PALETTE\" }\n | { type: \"SET_PALETTE_QUERY\"; query: string }\n | { type: \"MOVE_PALETTE_CURSOR\"; delta: number }\n | { type: \"SET_PALETTE_CURSOR\"; index: number }\n | { type: \"OPEN_MODULE_INSTANCE\"; instance: ModuleInstance }\n | { type: \"CLOSE_MODULE_INSTANCE\"; instanceId: string }\n | { type: \"UPDATE_MODULE_INSTANCE_STATE\"; instanceId: string; state: unknown }\n | { type: \"SELECT_MODULE\"; index: number; visibleCount?: number };\n\nexport const initialState: AppState = {\n conversations: [],\n messages: [],\n selectedIdx: 0,\n selectedMsgIdx: -1,\n focus: \"sidebar\",\n mode: \"browse\",\n sidebarScroll: 0,\n threadScroll: 0,\n composeText: \"\",\n filterQuery: \"\",\n pending: [],\n loading: true,\n status: \"Loading...\",\n numBuffer: \"\",\n showDevStats: false,\n conversationLoadedCount: 0,\n conversationLoadingMore: false,\n messageOldestLoadedId: null,\n messageLoadingOlder: false,\n gapMarkers: [],\n selectionAnchor: null,\n exportFormat: \"markdown\",\n exportPath: \"\",\n exportStatus: \"\",\n dateJumpInput: \"\",\n dateJumpError: \"\",\n moduleInstances: [],\n selectedModuleIdx: null,\n paletteQuery: \"\",\n paletteCursor: 0,\n};\n\n/** Clamp message cursor and ensure it's visible by adjusting scroll */\nfunction clampMsg(state: AppState, idx: number): Partial<AppState> {\n const total = state.messages.length;\n if (total === 0) return { selectedMsgIdx: -1 };\n const clamped = Math.max(0, Math.min(idx, total - 1));\n return { selectedMsgIdx: clamped };\n}\n\n// ── Bounded message window ───────────────────────────────────────────────\n// When loaded messages exceed HARD_CAP, evict the middle of the array but\n// preserve two regions:\n// 1. ANCHOR_KEEP at the END (most recent messages — user can press G)\n// 2. WINDOW_BUFFER ± selectedMsgIdx (current viewing window)\n// The evicted block becomes a gap marker so the renderer can show \"N more\n// messages — scroll to load\" and the user can refill on demand.\n\nconst MESSAGES_HARD_CAP = Number.parseInt(process.env.IMSG_TUI_MSG_HARD_CAP ?? \"5000\", 10);\nconst ANCHOR_KEEP = 200;\nconst WINDOW_BUFFER = 300;\n\n/**\n * Apply the bounding policy to a fresh `messages` array. Returns the trimmed\n * array, an updated `selectedMsgIdx`, and any new gap markers. Pure function.\n */\nexport function boundMessagesIfNeeded(\n messages: Message[],\n selectedMsgIdx: number,\n existingGaps: AppState[\"gapMarkers\"],\n): {\n messages: Message[];\n selectedMsgIdx: number;\n gapMarkers: AppState[\"gapMarkers\"];\n} {\n if (messages.length <= MESSAGES_HARD_CAP) {\n return { messages, selectedMsgIdx, gapMarkers: existingGaps };\n }\n\n const total = messages.length;\n // Keep the LAST ANCHOR_KEEP messages (most recent — anchor for G)\n const anchorStart = Math.max(total - ANCHOR_KEEP, 0);\n // Keep WINDOW_BUFFER on each side of the cursor\n const cursorLo = Math.max(0, selectedMsgIdx - WINDOW_BUFFER);\n const cursorHi = Math.min(total - 1, selectedMsgIdx + WINDOW_BUFFER);\n\n // Build the kept-set as a sorted list of [start, end] ranges, merged\n type Range = [number, number];\n const ranges: Range[] = [\n [cursorLo, cursorHi],\n [anchorStart, total - 1],\n ];\n // Sort by start; merge overlapping/adjacent\n ranges.sort((a, b) => a[0] - b[0]);\n const merged: Range[] = [];\n for (const r of ranges) {\n if (merged.length === 0) {\n merged.push(r);\n continue;\n }\n const last = merged[merged.length - 1];\n if (r[0] <= last[1] + 1) {\n last[1] = Math.max(last[1], r[1]);\n } else {\n merged.push(r);\n }\n }\n\n // Slice + concat the kept ranges, recording gaps between them\n const kept: Message[] = [];\n const gapMarkers: AppState[\"gapMarkers\"] = [];\n for (let i = 0; i < merged.length; i++) {\n const [start, end] = merged[i];\n if (i > 0) {\n const prevEnd = merged[i - 1][1];\n const gapStart = prevEnd + 1;\n const gapEnd = start - 1;\n if (gapEnd >= gapStart) {\n gapMarkers.push({\n atIdx: kept.length,\n oldestId: messages[gapStart].id,\n newestId: messages[gapEnd].id,\n count: gapEnd - gapStart + 1,\n });\n }\n }\n for (let j = start; j <= end; j++) kept.push(messages[j]);\n }\n\n // Map old selectedMsgIdx to new index in `kept`\n let newCursor = -1;\n if (selectedMsgIdx >= 0) {\n let collapsedIdx = 0;\n for (const [start, end] of merged) {\n if (selectedMsgIdx >= start && selectedMsgIdx <= end) {\n newCursor = collapsedIdx + (selectedMsgIdx - start);\n break;\n }\n collapsedIdx += end - start + 1;\n }\n if (newCursor === -1) newCursor = 0; // cursor was in evicted region; clamp to start\n }\n\n // Note: existingGaps from before this trim aren't re-merged; we replace.\n // In practice gaps are always recomputed from the current array shape.\n return { messages: kept, selectedMsgIdx: newCursor, gapMarkers };\n}\n\n/**\n * Compute a `sidebarScroll` value that keeps `selectedIdx` inside the\n * visible window. Adds a 2-row buffer at each edge so the user sees what's\n * coming next when navigating with j/k. Pure function — used by reducer.\n *\n * @param selectedIdx the cursor's target index\n * @param currentScroll the current sidebar scroll offset\n * @param visibleCount how many items fit on-screen at once\n * @param totalCount total items available (for clamping)\n */\nexport function ensureVisibleScroll(\n selectedIdx: number,\n currentScroll: number,\n visibleCount: number,\n totalCount: number,\n): number {\n if (visibleCount <= 0 || totalCount <= 0) return 0;\n const buffer = Math.min(2, Math.floor(visibleCount / 4));\n // If cursor is above the visible window (with buffer), scroll up\n if (selectedIdx < currentScroll + buffer) {\n return Math.max(0, selectedIdx - buffer);\n }\n // If cursor is below the visible window (with buffer), scroll down\n const lastVisible = currentScroll + visibleCount - 1;\n if (selectedIdx > lastVisible - buffer) {\n return Math.min(\n Math.max(0, totalCount - visibleCount),\n selectedIdx - visibleCount + 1 + buffer,\n );\n }\n // Already visible — don't move\n return Math.max(0, Math.min(currentScroll, Math.max(0, totalCount - visibleCount)));\n}\n\n/**\n * Total number of selectable rows in the sidebar — module instances stacked\n * on top of conversations. Used by the unified cursor model.\n */\nexport function sidebarRowCount(state: AppState): number {\n return state.moduleInstances.length + state.conversations.length;\n}\n\n/**\n * Combined sidebar cursor index ([0..moduleInstances.length-1] = modules,\n * [moduleInstances.length..] = conversations). Used to drive global j/k\n * navigation in the sidebar.\n */\nexport function combinedSidebarIndex(state: AppState): number {\n if (state.selectedModuleIdx != null) return state.selectedModuleIdx;\n return state.moduleInstances.length + state.selectedIdx;\n}\n\nexport function reducer(state: AppState, action: Action): AppState {\n switch (action.type) {\n case \"SET_CONVERSATIONS\":\n return {\n ...state,\n conversations: action.data,\n conversationLoadedCount: action.data.length,\n conversationLoadingMore: false,\n };\n case \"APPEND_CONVERSATIONS\": {\n // Merge new entries by threadSlug (DB returns the same recent ones too)\n const existing = new Set(state.conversations.map((c) => c.threadSlug));\n const fresh = action.data.filter((c) => !existing.has(c.threadSlug));\n return {\n ...state,\n conversations: [...state.conversations, ...fresh],\n conversationLoadedCount: action.loadedCount,\n conversationLoadingMore: false,\n };\n }\n case \"SET_MESSAGES\": {\n // Scroll to bottom: set cursor to last message\n const msgs = action.data;\n const lastIdx = Math.max(0, msgs.length - 1);\n // Set threadScroll high so the view shows the bottom\n const scrollToEnd = Math.max(0, msgs.length);\n const oldestId = minMessageId(msgs);\n return {\n ...state,\n messages: msgs,\n pending: [],\n selectedMsgIdx: lastIdx,\n threadScroll: scrollToEnd,\n messageOldestLoadedId: oldestId,\n messageLoadingOlder: false,\n gapMarkers: [],\n };\n }\n case \"PREPEND_MESSAGES\": {\n // Merge older messages at the start; dedup by id; preserve cursor on the\n // same logical message by shifting selectedMsgIdx by the count added.\n const existingIds = new Set(state.messages.map((m) => m.id));\n const fresh = action.data.filter((m) => !existingIds.has(m.id));\n const merged = [...fresh, ...state.messages].sort(\n (a, b) => a.date.getTime() - b.date.getTime(),\n );\n const shift = fresh.length;\n const shiftedCursor =\n state.selectedMsgIdx >= 0 ? state.selectedMsgIdx + shift : state.selectedMsgIdx;\n\n // Apply bounded-window eviction if we've grown past the cap\n const bounded = boundMessagesIfNeeded(merged, shiftedCursor, state.gapMarkers);\n\n return {\n ...state,\n messages: bounded.messages,\n selectedMsgIdx: bounded.selectedMsgIdx,\n gapMarkers: bounded.gapMarkers,\n messageOldestLoadedId: action.oldestId,\n messageLoadingOlder: false,\n };\n }\n case \"SET_LOADING_OLDER\":\n return { ...state, messageLoadingOlder: action.loading };\n case \"ENTER_SELECT_MODE\":\n return { ...state, mode: \"select\", selectionAnchor: state.selectedMsgIdx };\n case \"EXIT_SELECT_MODE\":\n return { ...state, mode: \"browse\", selectionAnchor: null };\n case \"ENTER_EXPORT_MODE\":\n return { ...state, mode: \"export\", exportPath: action.defaultPath };\n case \"EXIT_EXPORT_MODE\":\n return { ...state, mode: state.selectionAnchor != null ? \"select\" : \"browse\" };\n case \"SET_EXPORT_FORMAT\":\n return { ...state, exportFormat: action.format };\n case \"SET_EXPORT_PATH\":\n return { ...state, exportPath: action.path };\n case \"SET_EXPORT_STATUS\":\n return { ...state, exportStatus: action.status };\n case \"ENTER_DATE_JUMP\":\n return { ...state, mode: \"date-jump\", dateJumpInput: \"\", dateJumpError: \"\" };\n case \"EXIT_DATE_JUMP\":\n return { ...state, mode: \"browse\", dateJumpInput: \"\", dateJumpError: \"\" };\n case \"ENTER_SEND_VIA\":\n return { ...state, mode: \"send-via\" };\n case \"EXIT_SEND_VIA\":\n return { ...state, mode: \"browse\" };\n case \"ENTER_COMPOSE_NEW\":\n return { ...state, mode: \"compose-new\", focus: \"sidebar\" };\n case \"EXIT_COMPOSE_NEW\":\n return { ...state, mode: \"browse\" };\n case \"SET_DATE_JUMP_INPUT\":\n return { ...state, dateJumpInput: action.value, dateJumpError: \"\" };\n case \"SET_DATE_JUMP_ERROR\":\n return { ...state, dateJumpError: action.error };\n case \"SELECT\": {\n const idx = Math.max(0, Math.min(action.index, Math.max(0, state.conversations.length - 1)));\n const sidebarScroll = action.visibleCount\n ? ensureVisibleScroll(\n // The visible row index is offset by the module rows above.\n idx + state.moduleInstances.length,\n state.sidebarScroll,\n action.visibleCount,\n sidebarRowCount(state),\n )\n : state.sidebarScroll;\n // Selecting a real conversation always clears module focus.\n return { ...state, selectedIdx: idx, sidebarScroll, selectedModuleIdx: null };\n }\n case \"SELECT_MSG\": {\n const c = clampMsg(state, action.index);\n return { ...state, ...c };\n }\n case \"MOVE_MSG\": {\n const c = clampMsg(state, state.selectedMsgIdx + action.delta);\n return { ...state, ...c };\n }\n case \"FOCUS\":\n return { ...state, focus: action.pane, numBuffer: \"\" };\n case \"SCROLL_SIDEBAR\":\n return { ...state, sidebarScroll: Math.max(0, state.sidebarScroll + action.delta) };\n case \"SCROLL_THREAD\":\n return { ...state, threadScroll: Math.max(0, state.threadScroll + action.delta) };\n case \"SCROLL_THREAD_TO\":\n return { ...state, threadScroll: Math.max(0, action.position) };\n case \"ENTER_COMPOSE\":\n return { ...state, mode: \"compose\", composeText: \"\", focus: \"thread\" };\n case \"UPDATE_COMPOSE\":\n return { ...state, composeText: action.text };\n case \"CONFIRM_SEND\":\n return { ...state, mode: \"confirm\" };\n case \"CANCEL_COMPOSE\":\n return { ...state, mode: \"browse\", composeText: \"\" };\n case \"ADD_PENDING\":\n return { ...state, mode: \"browse\", composeText: \"\", pending: [...state.pending, action.msg] };\n case \"RESOLVE_PENDING\":\n return { ...state, pending: state.pending.filter((p) => p.text !== action.text) };\n case \"FAIL_PENDING\":\n return {\n ...state,\n pending: state.pending.map((p) =>\n p.text === action.text ? { ...p, status: \"failed\" as const } : p,\n ),\n };\n case \"SET_LOADING\":\n return { ...state, loading: action.loading, status: action.status ?? state.status };\n case \"SET_STATUS\":\n return { ...state, status: action.status };\n case \"ENTER_FILTER\":\n // Reset cursor + scroll to the top: the filtered list shrinks, and a\n // stale scroll offset (e.g. after `G`) would slice past every match and\n // render \"No conversations\" despite the header showing a non-zero count.\n return {\n ...state,\n mode: \"filter\",\n filterQuery: \"\",\n focus: \"sidebar\",\n selectedIdx: 0,\n selectedModuleIdx: null,\n sidebarScroll: 0,\n };\n case \"UPDATE_FILTER\":\n // Each keystroke re-filters; keep the top matches in view.\n return {\n ...state,\n filterQuery: action.query,\n selectedIdx: 0,\n selectedModuleIdx: null,\n sidebarScroll: 0,\n };\n case \"EXIT_FILTER\":\n return { ...state, mode: \"browse\", filterQuery: \"\" };\n case \"OPEN_DRAWER\":\n return { ...state, mode: \"drawer\" };\n case \"CLOSE_DRAWER\":\n return { ...state, mode: \"browse\" };\n case \"SET_NUM_BUFFER\":\n return { ...state, numBuffer: action.value };\n case \"TOGGLE_DEV_STATS\":\n return { ...state, showDevStats: !state.showDevStats };\n\n // ── Command palette ────────────────────────────────────────────────\n case \"OPEN_PALETTE\":\n return { ...state, mode: \"palette\", paletteQuery: \"\", paletteCursor: 0 };\n case \"CLOSE_PALETTE\":\n return { ...state, mode: \"browse\" };\n case \"SET_PALETTE_QUERY\":\n // Reset cursor to the top so the highlighted row is always the best\n // match for the freshest query.\n return { ...state, paletteQuery: action.query, paletteCursor: 0 };\n case \"MOVE_PALETTE_CURSOR\":\n return { ...state, paletteCursor: Math.max(0, state.paletteCursor + action.delta) };\n case \"SET_PALETTE_CURSOR\":\n return { ...state, paletteCursor: Math.max(0, action.index) };\n\n // ── Module instances ───────────────────────────────────────────────\n case \"OPEN_MODULE_INSTANCE\": {\n // Prepend so newest is at the top of the sidebar, then focus it.\n const moduleInstances = [action.instance, ...state.moduleInstances];\n return {\n ...state,\n mode: \"browse\",\n moduleInstances,\n selectedModuleIdx: 0,\n focus: \"sidebar\",\n };\n }\n case \"CLOSE_MODULE_INSTANCE\": {\n const idx = state.moduleInstances.findIndex((i) => i.id === action.instanceId);\n if (idx === -1) return state;\n const moduleInstances = state.moduleInstances.filter((_, i) => i !== idx);\n // If the removed instance was selected, fall focus back to the first\n // real conversation (or the first remaining module, if any).\n let selectedModuleIdx: number | null = state.selectedModuleIdx;\n let selectedIdx = state.selectedIdx;\n if (state.selectedModuleIdx === idx) {\n if (moduleInstances.length > 0) {\n selectedModuleIdx = Math.min(idx, moduleInstances.length - 1);\n } else {\n selectedModuleIdx = null;\n selectedIdx = 0;\n }\n } else if (state.selectedModuleIdx != null && state.selectedModuleIdx > idx) {\n selectedModuleIdx = state.selectedModuleIdx - 1;\n }\n return { ...state, moduleInstances, selectedModuleIdx, selectedIdx };\n }\n case \"UPDATE_MODULE_INSTANCE_STATE\":\n return {\n ...state,\n moduleInstances: state.moduleInstances.map((i) =>\n i.id === action.instanceId ? { ...i, state: action.state } : i,\n ),\n };\n case \"SELECT_MODULE\": {\n const idx = Math.max(\n 0,\n Math.min(action.index, Math.max(0, state.moduleInstances.length - 1)),\n );\n const sidebarScroll = action.visibleCount\n ? ensureVisibleScroll(idx, state.sidebarScroll, action.visibleCount, sidebarRowCount(state))\n : state.sidebarScroll;\n return { ...state, selectedModuleIdx: idx, sidebarScroll };\n }\n default:\n return state;\n }\n}\n","import { execSync } from \"node:child_process\";\nimport { writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { useScreenSize } from \"fullscreen-ink\";\nimport { Box, useApp, useInput } from \"ink\";\nimport { useCallback, useEffect, useMemo, useReducer, useRef } from \"react\";\nimport { registerCleanup } from \"../shutdown.js\";\nimport { minMessageId } from \"../types.js\";\nimport { getInstalledChatApps } from \"../url-schemes.js\";\nimport { CommandPalette } from \"./components/CommandPalette.js\";\nimport { ComposeRecipientModal } from \"./components/ComposeRecipientModal.js\";\nimport { DateJumpModal } from \"./components/DateJumpModal.js\";\nimport { CompactStats, DevStats } from \"./components/DevStats.js\";\nimport { ExportModal } from \"./components/ExportModal.js\";\nimport { HelpBar } from \"./components/HelpBar.js\";\nimport { MessageDrawer } from \"./components/MessageDrawer.js\";\nimport { SendViaModal } from \"./components/SendViaModal.js\";\nimport { Sidebar } from \"./components/Sidebar.js\";\nimport { StatusBar } from \"./components/StatusBar.js\";\nimport { nextGroupBoundary, prevGroupBoundary, ThreadPane } from \"./components/ThreadPane.js\";\nimport { formatJumpTarget, parseUserDate } from \"./dateParse.js\";\nimport { extensionFor, toCSV, toJSON, toMarkdown } from \"./exportFormats.js\";\nimport { firstFilterMatchIndex } from \"./filter.js\";\nimport { useDevStats } from \"./hooks/useDevStats.js\";\nimport { useImsg } from \"./hooks/useImsg.js\";\nimport { useMouse } from \"./hooks/useMouse.js\";\nimport { allCommands, findModule } from \"./modules/registry.js\";\nimport { initialState, reducer, sidebarRowCount } from \"./types.js\";\n\nexport function App() {\n const [state, dispatch] = useReducer(reducer, initialState);\n const { exit } = useApp();\n const { width: columns, height: rows } = useScreenSize();\n const imsg = useImsg();\n const { stats: devStats, recordQueryTime } = useDevStats(state.showDevStats);\n const pollTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const moveDebounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const ggPendingRef = useRef(false); // tracks if 'g' was pressed, waiting for second 'g'\n const ggTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const drawerWidth = state.mode === \"drawer\" ? Math.min(Math.floor(columns * 0.35), 50) : 0;\n const devStatsWidth = state.showDevStats ? 20 : 0;\n const sidebarWidth = Math.max(Math.floor((columns - drawerWidth - devStatsWidth) * 0.32), 28);\n const threadWidth = Math.max(columns - sidebarWidth - drawerWidth - devStatsWidth, 20);\n const bodyHeight = rows - 2; // status + help\n\n // Sidebar item layout: each item = 4 rows (name + snippet + slug + separator)\n // Header row + filter row + borders subtract from available height.\n // Mirror the calculation in Sidebar.tsx so SELECT dispatches include the\n // right visible-count and the cursor stays on screen as the user navigates.\n const SIDEBAR_ITEM_HEIGHT = 4;\n const sidebarVisibleCount = Math.max(\n Math.floor((bodyHeight - 1 - (state.filterQuery ? 1 : 0) - 2) / SIDEBAR_ITEM_HEIGHT),\n 1,\n );\n\n const selected =\n state.selectedModuleIdx == null ? state.conversations[state.selectedIdx] : undefined;\n const selectedModule =\n state.selectedModuleIdx != null ? state.moduleInstances[state.selectedModuleIdx] : undefined;\n const totalUnread = state.conversations.reduce((s, c) => s + c.unreadCount, 0);\n const resolvedNames = selected ? imsg.resolveNames(selected.participants) : [];\n const selectedMsg = state.selectedMsgIdx >= 0 ? state.messages[state.selectedMsgIdx] : undefined;\n\n // Commands list for the palette — recomputed on state change so `when`\n // gates and module command lists stay in sync.\n const commands = useMemo(() => allCommands(state), [state]);\n const commandCtx = useMemo(() => ({ state, dispatch, imsg }), [state, imsg]);\n\n // ── Data loading ───────────────────────────────────────────────────\n\n const loadMessages = useCallback(\n async (idx: number) => {\n const conv = state.conversations[idx];\n if (!conv) return;\n dispatch({\n type: \"SET_LOADING\",\n loading: true,\n status: `Loading ${conv.displayName ?? conv.chatIdentifier}...`,\n });\n const t0 = performance.now();\n const msgs = await imsg.loadMessages(conv.chatIdentifier);\n recordQueryTime(performance.now() - t0);\n dispatch({ type: \"SET_MESSAGES\", data: msgs });\n dispatch({ type: \"SET_LOADING\", loading: false, status: \"\" });\n },\n [state.conversations, imsg, recordQueryTime],\n );\n\n const refreshAll = useCallback(async () => {\n dispatch({ type: \"SET_LOADING\", loading: true, status: \"Refreshing...\" });\n const convs = await imsg.loadConversations();\n dispatch({ type: \"SET_CONVERSATIONS\", data: convs });\n if (convs.length > 0) {\n const prevSlug = selected?.threadSlug;\n if (prevSlug) {\n const idx = convs.findIndex((c) => c.threadSlug === prevSlug);\n if (idx >= 0) dispatch({ type: \"SELECT\", index: idx, visibleCount: sidebarVisibleCount });\n }\n await loadMessages(state.selectedIdx);\n }\n imsg.refresh();\n dispatch({ type: \"SET_LOADING\", loading: false, status: \"\" });\n }, [imsg, loadMessages, selected?.threadSlug, state.selectedIdx, sidebarVisibleCount]);\n\n // ── Lazy-load more conversations when user nears the end ──────────────\n const NEAR_END_THRESHOLD = 20;\n const CONV_BATCH_SIZE = 100;\n\n const loadMoreConversations = useCallback(async () => {\n if (state.conversationLoadingMore) return;\n const targetCount = state.conversationLoadedCount + CONV_BATCH_SIZE;\n dispatch({ type: \"SET_STATUS\", status: `Loading more conversations (${targetCount})...` });\n const convs = await imsg.loadConversations(targetCount);\n dispatch({ type: \"APPEND_CONVERSATIONS\", data: convs, loadedCount: targetCount });\n dispatch({ type: \"SET_STATUS\", status: \"\" });\n }, [imsg, state.conversationLoadingMore, state.conversationLoadedCount]);\n\n // ── Lazy-load older messages when cursor nears the top ────────────────\n const NEAR_TOP_THRESHOLD = 10;\n const _MSG_BATCH_SIZE = 100;\n\n const loadOlderMessages = useCallback(async () => {\n if (!selected) return;\n if (state.messageLoadingOlder) return;\n if (state.messageOldestLoadedId == null) return;\n dispatch({ type: \"SET_LOADING_OLDER\", loading: true });\n const olderMsgs = await imsg.loadOlderMessages(\n selected.chatIdentifier,\n state.messageOldestLoadedId,\n );\n if (olderMsgs.length === 0) {\n // Chat history exhausted — keep flag clear, mark id at -1 sentinel so we\n // don't keep retrying on every cursor move at the top.\n dispatch({ type: \"PREPEND_MESSAGES\", data: [], oldestId: -1 });\n return;\n }\n const newOldestId = minMessageId(olderMsgs) ?? -1;\n dispatch({ type: \"PREPEND_MESSAGES\", data: olderMsgs, oldestId: newOldestId });\n }, [imsg, selected, state.messageLoadingOlder, state.messageOldestLoadedId]);\n\n // Trigger when cursor approaches the start of the message list\n useEffect(() => {\n if (state.loading || state.messageLoadingOlder) return;\n if (state.messages.length === 0) return;\n if (state.messageOldestLoadedId === -1) return; // exhausted\n if (state.selectedMsgIdx >= 0 && state.selectedMsgIdx < NEAR_TOP_THRESHOLD) {\n loadOlderMessages();\n }\n }, [\n state.selectedMsgIdx,\n state.messages.length,\n state.messageOldestLoadedId,\n state.messageLoadingOlder,\n state.loading,\n loadOlderMessages,\n ]);\n\n // Trigger lazy-load when cursor or scroll is near the end of loaded items.\n // Important: only fire when we've actually grown — DB returns N when N requested,\n // so once `conversations.length === conversationLoadedCount` and we asked for\n // 200, asking for 300 may yield no new entries (chat history exhausted).\n useEffect(() => {\n if (state.loading || state.conversationLoadingMore) return;\n if (state.conversations.length === 0) return;\n const cursorNearEnd = state.selectedIdx >= state.conversations.length - NEAR_END_THRESHOLD;\n const scrollNearEnd =\n state.sidebarScroll + sidebarVisibleCount >= state.conversations.length - NEAR_END_THRESHOLD;\n if (cursorNearEnd || scrollNearEnd) {\n // Only ask for more if we haven't seen this chat-count plateau yet\n if (state.conversationLoadedCount === state.conversations.length) {\n loadMoreConversations();\n }\n }\n }, [\n state.selectedIdx,\n state.sidebarScroll,\n state.conversations.length,\n state.conversationLoadedCount,\n state.conversationLoadingMore,\n state.loading,\n sidebarVisibleCount,\n loadMoreConversations,\n ]);\n\n // Initial load + register cleanup\n // biome-ignore lint/correctness/useExhaustiveDependencies: we intentionally run this only on mount to prevent a render-loop\n useEffect(() => {\n registerCleanup(() => imsg.close());\n refreshAll();\n }, [imsg.close]);\n\n // ── Send logic ─────────────────────────────────────────────────────\n\n const sendMessage = useCallback(async () => {\n if (!selected || !state.composeText.trim()) return;\n const text = state.composeText.trim();\n dispatch({ type: \"ADD_PENDING\", msg: { text, sentAt: new Date(), status: \"sending\" } });\n\n // Wrap the entire send + poll setup so any thrown error surfaces as a\n // FAIL_PENDING in the UI rather than escaping as an unhandled rejection.\n // Pre-fix, an osascript / DB exception here would log silently and the\n // pending bubble would just sit in \"sending\" forever, which read as\n // \"the tool exited\" when combined with a phantom watchdog kill.\n try {\n const result = await imsg.send(selected.threadSlug, text);\n if (!result.success) {\n dispatch({ type: \"FAIL_PENDING\", text });\n return;\n }\n\n let attempt = 0;\n const poll = async () => {\n // Check if we are still polling for this request (unmounted or cancelled)\n if (!pollTimerRef.current) return;\n attempt++;\n try {\n const msgs = await imsg.loadMessages(selected.chatIdentifier);\n const found = msgs.some((m) => m.isFromMe && m.text?.includes(text));\n if (found) {\n dispatch({ type: \"SET_MESSAGES\", data: msgs });\n dispatch({ type: \"RESOLVE_PENDING\", text });\n } else if (attempt < 7) {\n pollTimerRef.current = setTimeout(poll, 1500);\n }\n } catch {\n // Poll iteration failed (DB locked, transient I/O); drop the\n // pending state rather than spin forever or leak the rejection.\n dispatch({ type: \"FAIL_PENDING\", text });\n }\n };\n if (pollTimerRef.current) clearTimeout(pollTimerRef.current);\n pollTimerRef.current = setTimeout(poll, 1500);\n } catch {\n dispatch({ type: \"FAIL_PENDING\", text });\n }\n }, [selected, state.composeText, imsg]);\n\n /**\n * Move the sidebar cursor to a combined index that spans\n * [modules..., conversations...]. Dispatches to the right reducer action\n * based on which region the target lands in, and only fires `loadMessages`\n * when a real conversation is selected.\n */\n const selectSidebarCombined = useCallback(\n (combinedIdx: number) => {\n const moduleCount = state.moduleInstances.length;\n if (combinedIdx < moduleCount) {\n dispatch({\n type: \"SELECT_MODULE\",\n index: combinedIdx,\n visibleCount: sidebarVisibleCount,\n });\n return;\n }\n const convIdx = combinedIdx - moduleCount;\n dispatch({ type: \"SELECT\", index: convIdx, visibleCount: sidebarVisibleCount });\n // Debounce message loading (matches existing behaviour).\n if (moveDebounceRef.current) clearTimeout(moveDebounceRef.current);\n const target = convIdx;\n moveDebounceRef.current = setTimeout(() => {\n moveDebounceRef.current = null;\n loadMessages(target);\n }, 80);\n },\n [state.moduleInstances.length, sidebarVisibleCount, loadMessages],\n );\n\n // ── Vim number prefix helper ───────────────────────────────────────\n\n const getCount = useCallback((): number => {\n const n = state.numBuffer ? Number.parseInt(state.numBuffer, 10) : 1;\n dispatch({ type: \"SET_NUM_BUFFER\", value: \"\" });\n return Math.max(1, Math.min(n, 999));\n }, [state.numBuffer]);\n\n // ── Keyboard ───────────────────────────────────────────────────────\n\n useInput(async (input, key) => {\n // Ctrl-C always exits\n if (key.ctrl && input === \"c\") {\n await imsg.close();\n exit();\n return;\n }\n\n // Palette mode — owns its own useInput (inside CommandPalette).\n // Skip all App-level handling so navigation keys don't double-fire.\n if (state.mode === \"palette\") {\n return;\n }\n\n // Ctrl-P opens the palette from anywhere outside an active modal.\n if (key.ctrl && input === \"p\" && (state.mode === \"browse\" || state.mode === \"select\")) {\n dispatch({ type: \"OPEN_PALETTE\" });\n return;\n }\n\n // `?` from browse mode opens the palette as a keybinding cheat sheet.\n if (input === \"?\" && !key.ctrl && !key.meta && state.mode === \"browse\") {\n dispatch({ type: \"OPEN_PALETTE\" });\n return;\n }\n\n // Module pane mode — when a module instance is selected and thread pane is\n // focused, the pane's own useInput owns everything (Tab cycles type, [/]\n // cycles range, Esc closes). Tab MUST NOT be intercepted here: the pane\n // advertises \"Tab:type\" in its footer and the PR contract documents Tab as\n // the type-cycle key. Previously this branch dispatched FOCUS:sidebar on\n // Tab, which silently overrode the pane's handler.\n if (selectedModule && state.focus === \"thread\" && state.mode === \"browse\") {\n // Shift-Tab returns focus to the sidebar so the user can navigate to\n // another conversation without closing the module. Regular Tab and\n // every other key falls through to the pane's own useInput.\n if (key.shift && key.tab) {\n dispatch({ type: \"FOCUS\", pane: \"sidebar\" });\n return;\n }\n return;\n }\n\n // Filter mode\n if (state.mode === \"filter\") {\n if (key.return) {\n // Enter commits the filter: jump cursor to the first matching\n // conversation, then exit filter. Esc-only path below preserves the\n // pre-filter cursor (cancel semantics). Previously both keys exited\n // without navigating, which made the filter feel inert.\n const matchIdx = firstFilterMatchIndex(state.conversations, state.filterQuery);\n if (matchIdx !== null) {\n dispatch({ type: \"SELECT\", index: matchIdx });\n }\n dispatch({ type: \"EXIT_FILTER\" });\n } else if (key.escape) {\n dispatch({ type: \"EXIT_FILTER\" });\n } else if (key.backspace || key.delete) {\n dispatch({ type: \"UPDATE_FILTER\", query: state.filterQuery.slice(0, -1) });\n } else if (input && !key.ctrl && !key.meta) {\n dispatch({ type: \"UPDATE_FILTER\", query: state.filterQuery + input });\n }\n return;\n }\n\n // Compose mode\n if (state.mode === \"compose\") {\n if (key.escape) {\n dispatch({ type: \"CANCEL_COMPOSE\" });\n } else if (key.return && state.composeText.trim()) {\n dispatch({ type: \"CONFIRM_SEND\" });\n }\n return;\n }\n\n // Confirm mode\n if (state.mode === \"confirm\") {\n if (key.return) {\n await sendMessage();\n } else {\n dispatch({ type: \"CANCEL_COMPOSE\" });\n }\n return;\n }\n\n // Drawer mode\n if (state.mode === \"drawer\") {\n if (key.escape || input === \"q\") {\n dispatch({ type: \"CLOSE_DRAWER\" });\n } else if (input === \"o\" && selectedMsg) {\n // Open attachment in external viewer\n openAttachment(selectedMsg);\n }\n return;\n }\n\n // Date-jump modal mode — Esc cancels; Enter handled by TextInput.onSubmit\n if (state.mode === \"date-jump\") {\n if (key.escape) {\n dispatch({ type: \"EXIT_DATE_JUMP\" });\n }\n return;\n }\n\n // Send-via modal: digit picks an app + launches its URL-scheme deep link.\n if (state.mode === \"send-via\") {\n if (key.escape) {\n dispatch({ type: \"EXIT_SEND_VIA\" });\n return;\n }\n if (input && /^[1-9]$/.test(input) && selected) {\n const apps = getInstalledChatApps();\n const idx = Number.parseInt(input, 10) - 1;\n const app = apps[idx];\n if (app) {\n const lastMsgText = state.messages.length\n ? (state.messages[state.messages.length - 1]?.text ?? undefined)\n : undefined;\n const built = app.buildUri(selected.chatIdentifier, lastMsgText);\n if (built) {\n (await import(\"node:child_process\"))\n .spawn(\"open\", [built], { detached: true, stdio: \"ignore\" })\n .unref();\n dispatch({ type: \"SET_STATUS\", status: `Launched ${app.name}` });\n } else {\n dispatch({\n type: \"SET_STATUS\",\n status: `${app.name}: handle not compatible with this scheme`,\n });\n }\n }\n dispatch({ type: \"EXIT_SEND_VIA\" });\n setTimeout(() => dispatch({ type: \"SET_STATUS\", status: \"\" }), 2500);\n }\n return;\n }\n\n // Export-modal mode — Tab cycles format, Esc cancels.\n // Path text input + Enter handled by the inline TextInput inside the modal.\n if (state.mode === \"export\") {\n if (key.escape) {\n dispatch({ type: \"EXIT_EXPORT_MODE\" });\n } else if (key.tab) {\n const order: Array<\"markdown\" | \"csv\" | \"json\"> = [\"markdown\", \"csv\", \"json\"];\n const next = order[(order.indexOf(state.exportFormat) + 1) % order.length];\n dispatch({ type: \"SET_EXPORT_FORMAT\", format: next });\n // Update path extension to match new format\n const stripped = state.exportPath.replace(/\\.(md|csv|json)$/, \"\");\n dispatch({ type: \"SET_EXPORT_PATH\", path: `${stripped}.${extensionFor(next)}` });\n }\n return;\n }\n\n // Select mode — V'd. j/k extend, Esc exits, e opens export modal,\n // y copies selected text to clipboard.\n if (state.mode === \"select\") {\n if (key.escape) {\n dispatch({ type: \"EXIT_SELECT_MODE\" });\n return;\n }\n if (input === \"e\") {\n const slug = selected?.threadSlug ?? \"messages\";\n const stamp = new Date().toISOString().replace(/[:.]/g, \"-\").slice(0, 19);\n const defaultPath = join(homedir(), `imsg-export-${slug}-${stamp}.md`);\n dispatch({ type: \"ENTER_EXPORT_MODE\", defaultPath });\n return;\n }\n if (input === \"y\" && state.selectionAnchor != null) {\n const [lo, hi] = [\n Math.min(state.selectionAnchor, state.selectedMsgIdx),\n Math.max(state.selectionAnchor, state.selectedMsgIdx),\n ];\n const text = state.messages\n .slice(lo, hi + 1)\n .map(\n (m) =>\n `[${m.date.toISOString()}] ${m.isFromMe ? \"Me\" : (m.displayName ?? m.handle)}: ${m.text ?? \"(no text)\"}`,\n )\n .join(\"\\n\");\n try {\n execSync(\"pbcopy\", { input: text });\n dispatch({ type: \"SET_STATUS\", status: `Copied ${hi - lo + 1} msgs` });\n } catch {\n dispatch({ type: \"SET_STATUS\", status: \"Copy failed\" });\n }\n setTimeout(() => dispatch({ type: \"SET_STATUS\", status: \"\" }), 2000);\n return;\n }\n // j/k/G/gg/Ctrl-d/Ctrl-u — fall through to browse-mode movement (anchor stays)\n }\n\n // Browse mode\n if (input === \"d\" && !key.ctrl && !key.meta && state.mode === \"browse\") {\n dispatch({ type: \"TOGGLE_DEV_STATS\" });\n return;\n }\n if (input === \"V\" && state.focus === \"thread\" && state.selectedMsgIdx >= 0) {\n dispatch({ type: \"ENTER_SELECT_MODE\" });\n return;\n }\n if (input === \":\" && state.focus === \"thread\" && state.mode === \"browse\") {\n dispatch({ type: \"ENTER_DATE_JUMP\" });\n return;\n }\n // O — open the current thread in Messages.app via the imessage:// URL scheme.\n // For 1:1 chats this focuses or composes that thread. Groups have no URL\n // scheme — we fall back to AppleScript activate.\n if (\n input === \"O\" &&\n !key.ctrl &&\n !key.meta &&\n state.focus === \"thread\" &&\n state.mode === \"browse\" &&\n selected\n ) {\n const handle = selected.chatIdentifier;\n const uri = `imessage://${encodeURIComponent(handle)}`;\n (await import(\"node:child_process\"))\n .spawn(\"open\", [uri], { detached: true, stdio: \"ignore\" })\n .unref();\n dispatch({ type: \"SET_STATUS\", status: `Opened ${handle} in Messages.app` });\n setTimeout(() => dispatch({ type: \"SET_STATUS\", status: \"\" }), 2500);\n return;\n }\n // S — send-via picker: list installed external chat apps and let the user\n // launch one. Body is the most recent message text in the thread (best-\n // effort context); the URI carries it only on schemes that support body.\n if (\n input === \"S\" &&\n !key.ctrl &&\n !key.meta &&\n state.focus === \"thread\" &&\n state.mode === \"browse\" &&\n selected\n ) {\n dispatch({ type: \"ENTER_SEND_VIA\" });\n return;\n }\n if (input === \"q\") {\n await imsg.close();\n exit();\n return;\n }\n if (input === \"r\") {\n await refreshAll();\n return;\n }\n // N (uppercase) opens compose-to-new-thread from anywhere in browse mode.\n // Distinct from `c` which composes WITHIN the currently-selected thread.\n if (input === \"N\" && state.mode === \"browse\") {\n dispatch({ type: \"ENTER_COMPOSE_NEW\" });\n return;\n }\n if (input === \"c\" || (key.return && state.focus === \"thread\" && state.mode === \"browse\")) {\n if (state.focus === \"thread\" && key.return && state.selectedMsgIdx >= 0) {\n // Enter on a message opens drawer\n dispatch({ type: \"OPEN_DRAWER\" });\n return;\n }\n // `c` from sidebar with no selected thread → fall through to compose-new\n // so the user always gets a meaningful action.\n if (state.focus === \"sidebar\" && !selected) {\n dispatch({ type: \"ENTER_COMPOSE_NEW\" });\n return;\n }\n dispatch({ type: \"ENTER_COMPOSE\" });\n return;\n }\n if (input === \"/\" && state.mode === \"browse\") {\n dispatch({ type: \"ENTER_FILTER\" });\n return;\n }\n if (key.tab) {\n dispatch({ type: \"FOCUS\", pane: state.focus === \"sidebar\" ? \"thread\" : \"sidebar\" });\n return;\n }\n\n if (state.loading) return;\n\n // Number buffer for vim-style counts (e.g. \"12j\" to jump 12 lines)\n if (input && input >= \"0\" && input <= \"9\" && !key.ctrl && !key.meta) {\n // Don't buffer leading zeros unless building a number\n if (input === \"0\" && !state.numBuffer) {\n // '0' alone: go to first item (like vim)\n if (state.focus === \"sidebar\") {\n selectSidebarCombined(0);\n } else {\n dispatch({ type: \"SELECT_MSG\", index: 0 });\n }\n return;\n }\n dispatch({ type: \"SET_NUM_BUFFER\", value: state.numBuffer + input });\n return;\n }\n\n if (state.focus === \"sidebar\") {\n // Copy thread slug to clipboard (only meaningful for real conversations)\n if (input === \"y\" && selected) {\n try {\n execSync(\"pbcopy\", { input: `~${selected.threadSlug}` });\n dispatch({ type: \"SET_STATUS\", status: `Copied ~${selected.threadSlug}` });\n setTimeout(() => dispatch({ type: \"SET_STATUS\", status: \"\" }), 2000);\n } catch {\n dispatch({ type: \"SET_STATUS\", status: \"Failed to copy\" });\n }\n return;\n }\n\n const count = getCount();\n const totalRows = sidebarRowCount(state);\n const lastIdx = Math.max(0, totalRows - 1);\n const curIdx =\n state.selectedModuleIdx != null\n ? state.selectedModuleIdx\n : state.moduleInstances.length + state.selectedIdx;\n let next: number | null = null;\n\n if (input === \"j\" || key.downArrow) {\n next = Math.min(curIdx + count, lastIdx);\n } else if (input === \"k\" || key.upArrow) {\n next = Math.max(curIdx - count, 0);\n } else if (input === \"G\") {\n next = lastIdx;\n } else if (input === \"g\") {\n // Handle gg (go to top)\n if (ggPendingRef.current) {\n ggPendingRef.current = false;\n if (ggTimerRef.current) clearTimeout(ggTimerRef.current);\n next = 0;\n } else {\n ggPendingRef.current = true;\n // 350ms is the typical vim chord window — feels snappy without\n // false-positive-firing on a slow second keypress. Was 500ms.\n ggTimerRef.current = setTimeout(() => {\n ggPendingRef.current = false;\n }, 350);\n return;\n }\n } else if (key.ctrl && input === \"d\") {\n next = Math.min(curIdx + Math.floor(bodyHeight / 2), lastIdx);\n } else if (key.ctrl && input === \"u\") {\n next = Math.max(curIdx - Math.floor(bodyHeight / 2), 0);\n } else if ((key.ctrl && input === \"f\") || key.pageDown) {\n next = Math.min(curIdx + bodyHeight, lastIdx);\n } else if ((key.ctrl && input === \"b\") || key.pageUp) {\n next = Math.max(curIdx - bodyHeight, 0);\n } else if (input === \"H\") {\n next = state.sidebarScroll;\n } else if (input === \"M\") {\n next = Math.min(state.sidebarScroll + Math.floor(bodyHeight / 2), lastIdx);\n } else if (input === \"L\") {\n next = Math.min(state.sidebarScroll + bodyHeight - 1, lastIdx);\n }\n\n if (next !== null && next !== curIdx) {\n selectSidebarCombined(next);\n }\n } else {\n // Thread focus — message cursor movement\n const count = getCount();\n\n if (input === \"j\" || key.downArrow) {\n dispatch({ type: \"MOVE_MSG\", delta: count });\n } else if (input === \"k\" || key.upArrow) {\n dispatch({ type: \"MOVE_MSG\", delta: -count });\n } else if (input === \"G\") {\n dispatch({ type: \"SELECT_MSG\", index: state.messages.length - 1 });\n } else if (input === \"g\") {\n if (ggPendingRef.current) {\n ggPendingRef.current = false;\n if (ggTimerRef.current) clearTimeout(ggTimerRef.current);\n dispatch({ type: \"SELECT_MSG\", index: 0 });\n } else {\n ggPendingRef.current = true;\n // 350ms is the typical vim chord window — feels snappy without\n // false-positive-firing on a slow second keypress. Was 500ms.\n ggTimerRef.current = setTimeout(() => {\n ggPendingRef.current = false;\n }, 350);\n }\n } else if (key.ctrl && input === \"d\") {\n dispatch({ type: \"MOVE_MSG\", delta: Math.floor(bodyHeight / 2) });\n } else if (key.ctrl && input === \"u\") {\n dispatch({ type: \"MOVE_MSG\", delta: -Math.floor(bodyHeight / 2) });\n } else if ((key.ctrl && input === \"f\") || key.pageDown) {\n dispatch({ type: \"MOVE_MSG\", delta: bodyHeight });\n } else if ((key.ctrl && input === \"b\") || key.pageUp) {\n dispatch({ type: \"MOVE_MSG\", delta: -bodyHeight });\n } else if (input === \"H\") {\n // Jump to top of visible area (approximate)\n const visibleTop = Math.max(0, state.selectedMsgIdx - Math.floor(bodyHeight * 0.7));\n dispatch({ type: \"SELECT_MSG\", index: visibleTop });\n } else if (input === \"L\") {\n const visibleBottom = Math.min(\n state.messages.length - 1,\n state.selectedMsgIdx + Math.floor(bodyHeight * 0.3),\n );\n dispatch({ type: \"SELECT_MSG\", index: visibleBottom });\n } else if (input === \"M\") {\n // Stay at current (middle)\n } else if (input === \"}\" || input === \"]\") {\n // Jump to next sender group\n const next = nextGroupBoundary(state.messages, state.selectedMsgIdx);\n dispatch({ type: \"SELECT_MSG\", index: next });\n } else if (input === \"{\" || input === \"[\") {\n // Jump to previous sender group\n const prev = prevGroupBoundary(state.messages, state.selectedMsgIdx);\n dispatch({ type: \"SELECT_MSG\", index: prev });\n } else if (input === \"o\") {\n // Open attachment for selected message. Always call so the no-msg/\n // no-attachment toast fires when the user presses `o` without a\n // valid selection — silent no-op is confusing UX.\n openAttachment(\n state.selectedMsgIdx >= 0 ? state.messages[state.selectedMsgIdx] : undefined,\n );\n }\n }\n });\n\n // ── Date jump ──────────────────────────────────────────────────────\n\n const MAX_JUMP_BATCHES = 100; // bounded loop: 100 × 100 = 10,000 messages\n\n const doDateJump = useCallback(\n async (input: string) => {\n const target = parseUserDate(input);\n if (!target) {\n dispatch({\n type: \"SET_DATE_JUMP_ERROR\",\n error: `Could not parse \"${input}\". Try YYYY-MM-DD or \"1 week ago\".`,\n });\n return;\n }\n if (!selected) {\n dispatch({ type: \"EXIT_DATE_JUMP\" });\n return;\n }\n let batches = 0;\n // Loop: load older messages until oldest <= target, or exhausted\n while (batches < MAX_JUMP_BATCHES) {\n const oldest = state.messages.length > 0 ? state.messages[0].date : new Date();\n if (oldest <= target) break;\n if (state.messageOldestLoadedId == null || state.messageOldestLoadedId === -1) break;\n const older = await imsg.loadOlderMessages(\n selected.chatIdentifier,\n state.messageOldestLoadedId,\n );\n if (older.length === 0) break;\n const newOldestId = minMessageId(older) ?? -1;\n dispatch({ type: \"PREPEND_MESSAGES\", data: older, oldestId: newOldestId });\n batches++;\n // Yield to render between batches\n await new Promise((r) => setTimeout(r, 10));\n }\n // Find first message at or after target\n const idx = state.messages.findIndex((m) => m.date >= target);\n if (idx >= 0) {\n dispatch({ type: \"SELECT_MSG\", index: idx });\n }\n dispatch({ type: \"EXIT_DATE_JUMP\" });\n dispatch({\n type: \"SET_STATUS\",\n status:\n batches >= MAX_JUMP_BATCHES\n ? `Jumped (capped) — load more manually for older history`\n : `Jumped to ${formatJumpTarget(target)}`,\n });\n setTimeout(() => dispatch({ type: \"SET_STATUS\", status: \"\" }), 4000);\n },\n [imsg, selected, state.messages, state.messageOldestLoadedId],\n );\n\n // ── Export action ──────────────────────────────────────────────────\n\n const doExport = useCallback(() => {\n const messagesToExport =\n state.selectionAnchor != null\n ? state.messages.slice(\n Math.min(state.selectionAnchor, state.selectedMsgIdx),\n Math.max(state.selectionAnchor, state.selectedMsgIdx) + 1,\n )\n : state.messages;\n if (messagesToExport.length === 0) {\n dispatch({ type: \"SET_STATUS\", status: \"Nothing to export\" });\n return;\n }\n const expandedPath = state.exportPath.replace(/^~/, homedir());\n try {\n let content: string;\n const header = {\n thread: selected?.displayName ?? selected?.chatIdentifier ?? \"thread\",\n participants: selected?.participants ?? [],\n serviceType: selected?.serviceType,\n };\n switch (state.exportFormat) {\n case \"markdown\":\n content = toMarkdown(messagesToExport, header);\n break;\n case \"csv\":\n content = toCSV(messagesToExport);\n break;\n case \"json\":\n content = toJSON(messagesToExport, header);\n break;\n }\n writeFileSync(expandedPath, content, \"utf8\");\n dispatch({ type: \"EXIT_EXPORT_MODE\" });\n dispatch({ type: \"EXIT_SELECT_MODE\" });\n dispatch({\n type: \"SET_STATUS\",\n status: `Exported ${messagesToExport.length} msgs to ${expandedPath}`,\n });\n setTimeout(() => dispatch({ type: \"SET_STATUS\", status: \"\" }), 4000);\n } catch (err) {\n dispatch({\n type: \"SET_STATUS\",\n status: `Export failed: ${err instanceof Error ? err.message : String(err)}`,\n });\n }\n }, [\n state.exportFormat,\n state.exportPath,\n state.messages,\n state.selectedMsgIdx,\n state.selectionAnchor,\n selected,\n ]);\n\n // ── Open attachment ────────────────────────────────────────────────\n\n function openAttachment(msg: import(\"../types.js\").Message | undefined) {\n // Surface UX feedback when `o` can't do anything — previously this\n // silently no-op'd, leaving the user wondering if the key worked.\n if (!msg) {\n dispatch({ type: \"SET_STATUS\", status: \"No message selected.\" });\n return;\n }\n if (!msg.attachments?.length) {\n dispatch({ type: \"SET_STATUS\", status: \"No attachment on this message.\" });\n return;\n }\n const att = msg.attachments[0];\n if (!att.filename) {\n dispatch({ type: \"SET_STATUS\", status: \"Attachment has no file path.\" });\n return;\n }\n // Expand ~ to home directory\n const filepath = att.filename.replace(/^~/, process.env.HOME ?? \"~\");\n const mime = att.mimeType ?? \"\";\n\n // macOS Quick Look (qlmanage -p) handles images, PDFs, audio, docs, and\n // most archives natively — same UX as Finder spacebar preview. For video,\n // mpv is preferred when installed (better scrubbing); otherwise fall back\n // to Quick Look. All spawns are detached + unref'd so the TUI never blocks.\n import(\"node:child_process\").then(({ spawn }) => {\n const spawnQuickLook = () =>\n spawn(\"qlmanage\", [\"-p\", filepath], { detached: true, stdio: \"ignore\" }).unref();\n\n if (mime.startsWith(\"video/\")) {\n const child = spawn(\"mpv\", [filepath], { detached: true, stdio: \"ignore\" });\n child.on(\"error\", spawnQuickLook);\n child.unref();\n } else {\n spawnQuickLook();\n }\n });\n }\n\n // ── Mouse ──────────────────────────────────────────────────────────\n\n const handleMouse = useCallback(\n (event: { type: string; x: number; y: number }) => {\n if (event.type === \"click\") {\n if (event.x <= sidebarWidth) {\n dispatch({ type: \"FOCUS\", pane: \"sidebar\" });\n // Combined index covers [...moduleInstances, ...conversations].\n const combinedIdx = Math.floor((event.y - 2 + state.sidebarScroll * 3) / 3);\n const moduleCount = state.moduleInstances.length;\n if (combinedIdx >= 0 && combinedIdx < moduleCount + state.conversations.length) {\n selectSidebarCombined(combinedIdx);\n }\n } else {\n dispatch({ type: \"FOCUS\", pane: \"thread\" });\n }\n } else if (event.type === \"scroll-up\") {\n // Wheel delta: 1 line per event for fine-grained control. macOS / iTerm\n // typically batch 2-3 wheel events per touchpad swipe so this still feels\n // responsive in practice.\n if (event.x <= sidebarWidth) {\n dispatch({ type: \"SCROLL_SIDEBAR\", delta: -1 });\n } else {\n dispatch({ type: \"MOVE_MSG\", delta: -1 });\n }\n } else if (event.type === \"scroll-down\") {\n if (event.x <= sidebarWidth) {\n dispatch({ type: \"SCROLL_SIDEBAR\", delta: 1 });\n } else {\n dispatch({ type: \"MOVE_MSG\", delta: 1 });\n }\n }\n },\n [\n sidebarWidth,\n state.sidebarScroll,\n state.conversations.length,\n state.moduleInstances.length,\n selectSidebarCombined,\n ],\n );\n\n useMouse(handleMouse);\n\n // ── Cleanup ────────────────────────────────────────────────────────\n\n useEffect(() => {\n return () => {\n if (pollTimerRef.current) clearTimeout(pollTimerRef.current);\n if (moveDebounceRef.current) clearTimeout(moveDebounceRef.current);\n if (ggTimerRef.current) clearTimeout(ggTimerRef.current);\n };\n }, []);\n\n // ── Render ─────────────────────────────────────────────────────────\n\n // Resolve the module + its pane renderer when a virtual row is selected.\n const selectedModuleDef = selectedModule ? findModule(selectedModule.moduleId) : undefined;\n const ModulePane = selectedModuleDef?.Pane;\n\n return (\n <Box flexDirection=\"column\" height={rows} width={columns}>\n {/* Main layout */}\n <Box flexGrow={1} height={bodyHeight}>\n <Sidebar\n conversations={state.conversations}\n moduleInstances={state.moduleInstances}\n selectedIdx={state.selectedIdx}\n selectedModuleIdx={state.selectedModuleIdx}\n scrollOffset={state.sidebarScroll}\n filterQuery={state.filterQuery}\n focused={state.focus === \"sidebar\"}\n width={sidebarWidth}\n height={bodyHeight}\n />\n {selectedModule && ModulePane ? (\n <ModulePane\n instance={selectedModule}\n imsg={imsg}\n width={threadWidth}\n height={bodyHeight}\n focused={state.focus === \"thread\"}\n onUpdateState={(next) =>\n dispatch({\n type: \"UPDATE_MODULE_INSTANCE_STATE\",\n instanceId: selectedModule.id,\n state: next,\n })\n }\n onClose={() =>\n dispatch({ type: \"CLOSE_MODULE_INSTANCE\", instanceId: selectedModule.id })\n }\n setStatus={(s) => {\n dispatch({ type: \"SET_STATUS\", status: s });\n setTimeout(() => dispatch({ type: \"SET_STATUS\", status: \"\" }), 2500);\n }}\n />\n ) : (\n <ThreadPane\n conversation={selected}\n messages={state.messages}\n pending={state.pending}\n resolvedNames={resolvedNames}\n scrollOffset={state.threadScroll}\n selectedMsgIdx={state.selectedMsgIdx}\n selectionAnchor={state.selectionAnchor}\n gapMarkers={state.gapMarkers}\n focused={state.focus === \"thread\"}\n width={threadWidth}\n height={bodyHeight}\n mode={state.mode}\n onChangeCompose={(text) => dispatch({ type: \"UPDATE_COMPOSE\", text })}\n onSubmitCompose={(text) => text.trim() && dispatch({ type: \"CONFIRM_SEND\" })}\n />\n )}\n {state.mode === \"drawer\" && selectedMsg && (\n <MessageDrawer message={selectedMsg} width={drawerWidth} height={bodyHeight} />\n )}\n {state.showDevStats && <DevStats stats={devStats} width={devStatsWidth} />}\n </Box>\n\n {/* Date-jump modal */}\n {state.mode === \"date-jump\" && (\n <DateJumpModal\n value={state.dateJumpInput}\n error={state.dateJumpError}\n onChange={(v) => dispatch({ type: \"SET_DATE_JUMP_INPUT\", value: v })}\n onSubmit={(v) => doDateJump(v)}\n />\n )}\n\n {/* Send-via picker — launch external chat apps for the current thread */}\n {state.mode === \"send-via\" && selected && (\n <SendViaModal handle={selected.chatIdentifier} apps={getInstalledChatApps()} />\n )}\n\n {/* Compose-to-new-thread modal — `N` (or `c` from sidebar with no\n selected thread). Two-stage: recipient input → message body. */}\n {state.mode === \"compose-new\" && (\n <ComposeRecipientModal\n resolve={imsg.resolveRecipientInput}\n onSend={async (handle, text) => {\n const result = await imsg.sendToRecipient(handle, text);\n if (result.success) {\n dispatch({\n type: \"SET_STATUS\",\n status: `Sent to ${handle}`,\n });\n // Trigger a refresh so the new conversation shows up in the sidebar.\n await refreshAll();\n }\n return result;\n }}\n onCancel={() => dispatch({ type: \"EXIT_COMPOSE_NEW\" })}\n />\n )}\n\n {/* Export modal — overlays the bottom of the body when active */}\n {state.mode === \"export\" && (\n <ExportModal\n format={state.exportFormat}\n path={state.exportPath}\n rangeSummary={(() => {\n if (state.selectionAnchor != null) {\n const lo = Math.min(state.selectionAnchor, state.selectedMsgIdx);\n const hi = Math.max(state.selectionAnchor, state.selectedMsgIdx);\n const n = hi - lo + 1;\n return `${n} selected message${n === 1 ? \"\" : \"s\"}`;\n }\n const m = state.messages.length;\n return `entire loaded thread (${m} message${m === 1 ? \"\" : \"s\"})`;\n })()}\n onChangePath={(p) => dispatch({ type: \"SET_EXPORT_PATH\", path: p })}\n onSubmit={doExport}\n />\n )}\n\n {/* Command palette — overlay modal, Ctrl-P / ? from browse mode. */}\n {state.mode === \"palette\" && (\n <CommandPalette\n commands={commands}\n query={state.paletteQuery}\n cursor={state.paletteCursor}\n width={columns}\n height={rows}\n ctx={commandCtx}\n onQueryChange={(q) => dispatch({ type: \"SET_PALETTE_QUERY\", query: q })}\n onCursorMove={(d) => dispatch({ type: \"MOVE_PALETTE_CURSOR\", delta: d })}\n onSelectCursor={(i) => dispatch({ type: \"SET_PALETTE_CURSOR\", index: i })}\n onClose={() => dispatch({ type: \"CLOSE_PALETTE\" })}\n />\n )}\n\n {/* Status + Help */}\n <StatusBar\n totalUnread={totalUnread}\n selected={selected}\n status={state.status}\n loading={state.loading}\n >\n {!state.showDevStats && <CompactStats stats={devStats} />}\n </StatusBar>\n <HelpBar mode={state.mode} focus={state.focus} />\n </Box>\n );\n}\n","import { parseArgs } from \"node:util\";\nimport { withFullScreen } from \"fullscreen-ink\";\nimport { checkLocalAccess, formatAccessReport } from \"../access-check.js\";\nimport { detectNerdFont } from \"../font-detect.js\";\nimport {\n enableFileLogging,\n logShutdown,\n logStartup,\n startHeapMonitor,\n stopHeapMonitor,\n} from \"../logger.js\";\nimport { installShutdownHandlers, registerCleanup } from \"../shutdown.js\";\nimport { resolveTuiConfig } from \"../tui-config.js\";\nimport { installWatchdog } from \"../watchdog.js\";\nimport { App } from \"./App.js\";\nimport { clearCache, installCacheSweepers, stopCacheSweepers } from \"./messageCache.js\";\nimport { makeTheme } from \"./theme.js\";\nimport { ThemeProvider } from \"./themes/ThemeContext.js\";\n\n/**\n * Build the warning we print when the user picked `powerline` but no\n * Nerd Font is detected. Pure function so tests can lock the wording.\n */\nexport function buildPowerlineFontWarning(\n detection: ReturnType<typeof detectNerdFont>,\n): string | null {\n if (detection.source === \"fc-list\" && detection.detected === false) {\n return (\n \"warn: powerline theme selected but no Nerd Font was detected.\\n\" +\n \" Glyphs will render blank. Either install one (https://www.nerdfonts.com)\\n\" +\n \" or switch with: imsg --theme=safe (or `imsg config edit`)\"\n );\n }\n if (detection.source === \"unavailable\") {\n return (\n \"warn: powerline theme selected; could not auto-detect a Nerd Font (no fc-list).\\n\" +\n \" If glyphs render blank, install one (https://www.nerdfonts.com) or\\n\" +\n \" switch with: imsg --theme=safe\"\n );\n }\n return null;\n}\n\n/** Parse `--theme` and `--accent` from the TUI command line. Unknown\n * flags don't raise — the TUI accepts no other options today. */\nfunction parseTuiCliArgs(): { theme?: string; accent?: string } {\n try {\n const { values } = parseArgs({\n args: process.argv.slice(2),\n options: {\n theme: { type: \"string\" },\n accent: { type: \"string\" },\n },\n strict: false,\n allowPositionals: true,\n });\n return {\n theme: typeof values.theme === \"string\" ? values.theme : undefined,\n accent: typeof values.accent === \"string\" ? values.accent : undefined,\n };\n } catch {\n return {};\n }\n}\n\nexport async function runTui(): Promise<void> {\n if (!process.stdin.isTTY || !process.stdout.isTTY) {\n throw new Error(\"The TUI requires an interactive terminal (TTY).\");\n }\n\n const report = await checkLocalAccess();\n if (!report.ok) {\n console.log(formatAccessReport(report));\n process.exit(1);\n }\n\n // Resolve theme / accent: CLI > env > ~/.config/imsg-mcp/config.json > defaults.\n const cli = parseTuiCliArgs();\n const cfg = resolveTuiConfig({ cliTheme: cli.theme, cliAccent: cli.accent });\n for (const w of cfg.warnings) console.error(`warn: ${w}`);\n\n // Powerline theme needs a Nerd Font; warn the user once at startup if\n // we can detect (or strongly suspect) one is missing. The message lands\n // in stderr before alt-screen activates and surfaces in scrollback after\n // the user quits the TUI.\n if (cfg.theme === \"powerline\") {\n const warning = buildPowerlineFontWarning(detectNerdFont());\n if (warning) console.error(warning);\n }\n\n const theme = makeTheme({ preset: cfg.theme, accent: cfg.accentColor });\n\n // Install shutdown handlers + watchdog before starting TUI.\n //\n // Three TUI-specific lifecycle choices vs the MCP path:\n //\n // 1. `enableFileLogging()` — flip file logging on regardless of IMSG_DEV.\n // Without this the TUI is opaque on crash: \"tool exited on its own\"\n // leaves no postmortem trail in `$TMPDIR/imsg-mcp/`.\n // 2. `installShutdownHandlers({ exitOnUncaughtException: false })` — log\n // uncaught exceptions but DO NOT kill. The MCP-style hard-exit policy\n // would terminate the user's session over any stray React render or\n // background-task error.\n // 3. We deliberately skip `enableOrphanWatchdog()`. The orphan watchdog\n // kills on `ppid !== originalParentPid`, which is correct for an MCP\n // stdio child but is a phantom-exit landmine in a TUI: terminal\n // multiplexers (tmux), shell job-control, and even some IDEs reparent\n // the process during their lifetime — none of which mean the user\n // walked away. The terminal will SIGHUP us on a real disconnect.\n enableFileLogging();\n installShutdownHandlers({ exitOnUncaughtException: false });\n installWatchdog({ idleRestart: false });\n installCacheSweepers();\n startHeapMonitor();\n logStartup(\"tui\");\n\n registerCleanup(() => {\n stopCacheSweepers();\n clearCache();\n stopHeapMonitor();\n logShutdown(\"normal\");\n });\n\n const screen = withFullScreen(\n <ThemeProvider value={theme}>\n <App />\n </ThemeProvider>,\n );\n\n // Register screen cleanup so terminal is restored on any exit\n registerCleanup(() => {\n try {\n screen.instance.unmount();\n } catch {\n // Screen may already be unmounted\n }\n });\n\n await screen.start();\n await screen.waitUntilExit();\n}\n"],"names":["commands","relativeDate","end","start","totalLines","i","cached"],"mappings":"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"}
1
+ {"version":3,"file":"tui.js","sources":["../src/font-detect.ts","../src/url-schemes.ts","../src/tui/themes/ThemeContext.tsx","../src/tui/components/CommandPalette.tsx","../src/tui/components/ComposeRecipientModal.tsx","../src/tui/components/DatePicker.tsx","../src/tui/components/DateJumpModal.tsx","../src/tui/components/DevStats.tsx","../src/tui/components/ExportModal.tsx","../src/tui/components/HelpBar.tsx","../src/tui/themes/glyphs.ts","../src/tui/themes/color.ts","../src/tui/themes/palette.ts","../src/tui/theme.ts","../src/tui/components/MessageDrawer.tsx","../src/tui/components/SendViaModal.tsx","../src/tui/filter.ts","../src/tui/keymap.ts","../src/tui/modules/analytics/Pane.tsx","../src/tui/modules/analytics/SidebarItem.tsx","../src/tui/modules/analytics/module.tsx","../src/tui/modules/registry.ts","../src/visual-width.ts","../src/tui/components/ConversationItem.tsx","../src/tui/components/DefaultModuleSidebarItem.tsx","../src/tui/components/Sidebar.tsx","../src/tui/components/StatusBar.tsx","../src/tui/components/ComposeBar.tsx","../src/tui/components/MessageBubble.tsx","../src/tui/components/ThreadPane.tsx","../src/tui/hooks/useDevStats.ts","../src/tui/messageCache.ts","../src/tui/hooks/useImsg.ts","../src/tui/hooks/useMouse.ts","../src/tui/types.ts","../src/tui/App.tsx","../src/tui/index.tsx"],"sourcesContent":["/**\n * Best-effort Nerd-Font detection.\n *\n * Used by the TUI startup path to warn the user when they've selected the\n * `powerline` glyph preset but no Nerd Font is installed on the system —\n * which would otherwise render as silent blank/box-of-question-mark glyphs.\n *\n * Strategy: run `fc-list :family` (fontconfig). If it's not on PATH we\n * report `unavailable` rather than guessing — the warning is downgraded\n * to a soft hint in that case so we don't false-positive against the many\n * users who do have a Nerd Font but no `fc-list` (default macOS).\n *\n * Synchronous + short timeout so TUI startup isn't gated on a slow shell.\n */\n\nimport { spawnSync } from \"node:child_process\";\n\nexport type FontDetectResult =\n | { detected: true; source: \"fc-list\" }\n | { detected: false; source: \"fc-list\" }\n | { detected: null; source: \"unavailable\"; reason: string };\n\nconst FC_LIST_TIMEOUT_MS = 1000;\n\nlet cached: FontDetectResult | null = null;\n\n/**\n * Returns whether a Nerd Font is installed.\n *\n * `detected: null` means we couldn't tell (no `fc-list`, or it errored).\n * Cached per-process so repeated calls are free.\n */\nexport function detectNerdFont(): FontDetectResult {\n if (cached) return cached;\n\n const result = spawnSync(\"fc-list\", [\":family\"], {\n encoding: \"utf8\",\n timeout: FC_LIST_TIMEOUT_MS,\n // Suppress stderr noise like \"Fontconfig error: ...\" from cluttering\n // the user's terminal — we only care about stdout + exit status.\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n });\n\n let next: FontDetectResult;\n if (result.error || result.status !== 0) {\n next = {\n detected: null,\n source: \"unavailable\",\n reason: result.error?.message ?? `fc-list exited with status ${result.status}`,\n };\n } else {\n const stdout = result.stdout ?? \"\";\n // Match any family name containing \"Nerd\" (case-insensitive). All Nerd\n // Font patched families include the \"Nerd Font\" suffix.\n next = { detected: /Nerd/i.test(stdout), source: \"fc-list\" };\n }\n cached = next;\n return next;\n}\n\n/** Test-only: reset the per-process detection cache. */\nexport function _resetDetectNerdFontCache(): void {\n cached = null;\n}\n","/**\n * macOS URL-scheme dispatch helpers.\n *\n * Detects which chat/comms apps are installed in /Applications and builds\n * deep-link URIs that, when passed to `open <uri>`, launch the target app\n * focused on a specific contact. Designed for the TUI's `O` (open in\n * Messages) and `S` (send-via external app) keybinds.\n *\n * Body-carrying support varies by app:\n * - sms:, whatsapp:, tg:, mailto: → body/text param honored\n * - imessage:, sgnl:, facetime: → recipient only, body stripped\n */\n\nimport { existsSync } from \"node:fs\";\n\nexport interface ChatAppDef {\n /** Display name shown in TUI list. */\n name: string;\n /** /Applications/ bundle to probe. */\n appPath: string;\n /** Build the URL given a handle + optional body text. Returns null if the\n * handle isn't compatible (e.g. Signal can't take an email). */\n buildUri: (handle: string, body?: string) => string | null;\n /** True if `body` is honored by the URL scheme. */\n supportsBody: boolean;\n}\n\nconst APPS: ChatAppDef[] = [\n {\n name: \"Messages\",\n appPath: \"/System/Applications/Messages.app\",\n buildUri: (handle) => `imessage://${encodeURIComponent(handle)}`,\n supportsBody: false,\n },\n {\n name: \"FaceTime\",\n appPath: \"/System/Applications/FaceTime.app\",\n buildUri: (handle) => `facetime://${encodeURIComponent(handle)}`,\n supportsBody: false,\n },\n {\n name: \"FaceTime Audio\",\n appPath: \"/System/Applications/FaceTime.app\",\n buildUri: (handle) => `facetime-audio://${encodeURIComponent(handle)}`,\n supportsBody: false,\n },\n {\n name: \"Signal\",\n appPath: \"/Applications/Signal.app\",\n buildUri: (handle) => {\n // Signal only accepts phone numbers.\n const phone = handle.replace(/[^\\d+]/g, \"\");\n if (!phone || !phone.startsWith(\"+\")) return null;\n return `sgnl://send?phone=${encodeURIComponent(phone)}`;\n },\n supportsBody: false,\n },\n {\n name: \"WhatsApp\",\n appPath: \"/Applications/WhatsApp.app\",\n buildUri: (handle, body) => {\n const phone = handle.replace(/[^\\d+]/g, \"\").replace(/^\\+/, \"\");\n if (!phone) return null;\n const base = `whatsapp://send?phone=${encodeURIComponent(phone)}`;\n return body ? `${base}&text=${encodeURIComponent(body)}` : base;\n },\n supportsBody: true,\n },\n {\n name: \"Telegram\",\n appPath: \"/Applications/Telegram.app\",\n buildUri: (handle, body) => {\n const phone = handle.replace(/[^\\d+]/g, \"\");\n if (!phone) return null;\n const base = `tg://resolve?phone=${encodeURIComponent(phone)}`;\n return body ? `${base}&text=${encodeURIComponent(body)}` : base;\n },\n supportsBody: true,\n },\n {\n name: \"SMS\",\n appPath: \"/System/Applications/Messages.app\",\n buildUri: (handle, body) => {\n // sms: works for both numbers and email (Messages.app fallback).\n const base = `sms:${encodeURIComponent(handle)}`;\n return body ? `${base}&body=${encodeURIComponent(body)}` : base;\n },\n supportsBody: true,\n },\n];\n\n/**\n * Return the apps installed on this Mac, in the order they're declared.\n * Messages and FaceTime are typically always present; third-party apps\n * (Signal, WhatsApp, Telegram) require the app to be installed.\n */\nexport function getInstalledChatApps(): ChatAppDef[] {\n return APPS.filter((a) => existsSync(a.appPath));\n}\n\n/**\n * Build the right URI for a specific app by name (case-insensitive). Returns\n * null if the app is not installed OR the handle isn't compatible with the\n * app's URL scheme.\n */\nexport function buildChatAppUri(\n appName: string,\n handle: string,\n body?: string,\n): { uri: string; supportsBody: boolean } | null {\n const lower = appName.toLowerCase();\n const app = APPS.find((a) => a.name.toLowerCase() === lower);\n if (!app) return null;\n if (!existsSync(app.appPath)) return null;\n const uri = app.buildUri(handle, body);\n if (!uri) return null;\n return { uri, supportsBody: app.supportsBody };\n}\n","/**\n * React context that delivers a fully-resolved Theme to every TUI\n * component. Replaces the legacy pattern of `import { theme } from \"../theme\"`\n * — components now `const theme = useTheme()` so the same component tree\n * can render with different accents / glyph presets.\n *\n * Lookup outside a provider throws — TypeScript would let it slide\n * silently otherwise (default value), and a missing provider is a\n * developer error we want to surface during the first render.\n */\n\nimport { createContext, type ReactNode, useContext } from \"react\";\nimport type { Theme } from \"../theme.js\";\n\nconst ThemeCtx = createContext<Theme | null>(null);\n\nexport function ThemeProvider({ value, children }: { value: Theme; children: ReactNode }) {\n return <ThemeCtx.Provider value={value}>{children}</ThemeCtx.Provider>;\n}\n\nexport function useTheme(): Theme {\n const t = useContext(ThemeCtx);\n if (!t) {\n throw new Error(\"useTheme(): no <ThemeProvider> in the component tree above this hook\");\n }\n return t;\n}\n","/**\n * CommandPalette — fuzzy-searchable list of every action in the TUI.\n *\n * Empty query → grouped cheat sheet of all commands by category (acts as\n * the canonical keybinding reference). Typing → fuzzy rank via\n * `rankFuzzy` from `src/fuzzy.ts`.\n *\n * Input model: the palette owns its own `useInput` (gated on `mode === \"palette\"`\n * via App.tsx). Enter runs the highlighted command; arrows / Ctrl-n/Ctrl-p\n * move the cursor; Esc / Ctrl-P close.\n */\nimport { TextInput } from \"@inkjs/ui\";\nimport { Box, Text, useInput } from \"ink\";\nimport { useEffect, useMemo } from \"react\";\nimport { rankFuzzy } from \"../../fuzzy.js\";\nimport type { Command, CommandContext } from \"../keymap.js\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\n\ninterface Props {\n commands: Command[];\n query: string;\n cursor: number;\n width: number;\n height: number;\n ctx: CommandContext;\n onQueryChange: (q: string) => void;\n onCursorMove: (delta: number) => void;\n onSelectCursor: (idx: number) => void;\n onClose: () => void;\n}\n\ninterface RankedRow {\n command: Command;\n score: number;\n /** Only set in empty-query mode — the first row of a new category prints a header. */\n headerCategory?: string;\n}\n\n/** Build the rows to render: fuzzy-ranked when there's a query, grouped otherwise. */\nfunction buildRows(commands: Command[], query: string): RankedRow[] {\n const q = query.trim();\n if (q === \"\") {\n // Empty query — list every command grouped by category.\n const byCat = new Map<string, Command[]>();\n for (const c of commands) {\n if (!byCat.has(c.category)) byCat.set(c.category, []);\n byCat.get(c.category)!.push(c);\n }\n const rows: RankedRow[] = [];\n for (const [cat, cmds] of byCat) {\n let first = true;\n for (const cmd of cmds) {\n rows.push({ command: cmd, score: 1, headerCategory: first ? cat : undefined });\n first = false;\n }\n }\n return rows;\n }\n // Lower minScore (0.3) — palette queries are short and abbreviated.\n const ranked = rankFuzzy(\n q,\n commands,\n (c) => `${c.title} ${c.category} ${c.description ?? \"\"} ${c.keybinding ?? \"\"}`,\n 0.3,\n );\n return ranked.map((r) => ({ command: r.item, score: r.score }));\n}\n\nexport function CommandPalette({\n commands,\n query,\n cursor,\n width,\n height,\n ctx,\n onQueryChange,\n onCursorMove,\n onSelectCursor,\n onClose,\n}: Props) {\n const theme = useTheme();\n\n const rows = useMemo(() => buildRows(commands, query), [commands, query]);\n\n // Clamp the cursor if results shrank below it (e.g. user added a letter\n // that filtered out previously-visible entries).\n useEffect(() => {\n if (rows.length === 0) return;\n if (cursor > rows.length - 1) onSelectCursor(rows.length - 1);\n }, [rows.length, cursor, onSelectCursor]);\n\n // Visible window — keep the cursor on screen.\n const headerH = 3; // title + query + spacer\n const footerH = 1;\n const bodyH = Math.max(3, height - headerH - footerH - 2);\n const visibleStart = Math.max(0, Math.min(cursor - Math.floor(bodyH / 2), rows.length - bodyH));\n const visibleEnd = Math.min(rows.length, visibleStart + bodyH);\n const visibleRows = rows.slice(visibleStart, visibleEnd);\n\n useInput((input, key) => {\n if (key.escape || (key.ctrl && input === \"p\")) {\n onClose();\n return;\n }\n if (key.return) {\n const row = rows[cursor];\n if (row) {\n // Close BEFORE running so commands that themselves change mode (e.g.\n // ENTER_COMPOSE) end up in the right final state, rather than being\n // overridden by a trailing CLOSE_PALETTE.\n onClose();\n void Promise.resolve(row.command.run(ctx)).catch(() => {});\n }\n return;\n }\n if (key.downArrow || (key.ctrl && input === \"n\")) {\n onCursorMove(1);\n return;\n }\n if (key.upArrow || (key.ctrl && input === \"p\")) {\n onCursorMove(-1);\n return;\n }\n if (key.pageDown) {\n onCursorMove(bodyH);\n return;\n }\n if (key.pageUp) {\n onCursorMove(-bodyH);\n return;\n }\n });\n\n const modalWidth = Math.min(Math.max(60, Math.floor(width * 0.7)), 120);\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"double\"\n borderColor={theme.status.accent}\n backgroundColor={theme.header.dim.bg}\n paddingX={1}\n flexShrink={0}\n width={modalWidth}\n >\n <Box flexShrink={0}>\n <Text color={theme.status.accent} bold>\n Command Palette\n </Text>\n <Text color={theme.help.desc}>\n {\" \"}({rows.length} {rows.length === 1 ? \"command\" : \"commands\"})\n </Text>\n </Box>\n\n <Box flexShrink={0}>\n <Text color={theme.info.label}>{\"› \"}</Text>\n <TextInput defaultValue={query} onChange={onQueryChange} placeholder=\"Search commands…\" />\n </Box>\n\n <Box flexDirection=\"column\" flexShrink={0}>\n {rows.length === 0 ? (\n <Box>\n <Text color={theme.help.desc}>No commands match \"{query}\".</Text>\n </Box>\n ) : (\n visibleRows.map((row, i) => {\n const realIdx = visibleStart + i;\n const selected = realIdx === cursor;\n return (\n <PaletteRow\n key={`${row.command.id}-${realIdx}`}\n row={row}\n selected={selected}\n width={modalWidth - 4}\n />\n );\n })\n )}\n </Box>\n\n <Box flexShrink={0}>\n <Text color={theme.help.key}>↑↓</Text>\n <Text color={theme.help.desc}>:move </Text>\n <Text color={theme.help.key}>Enter</Text>\n <Text color={theme.help.desc}>:run </Text>\n <Text color={theme.help.key}>Esc</Text>\n <Text color={theme.help.desc}>:close</Text>\n </Box>\n </Box>\n );\n}\n\nfunction PaletteRow({\n row,\n selected,\n width,\n}: {\n row: RankedRow;\n selected: boolean;\n width: number;\n}) {\n const theme = useTheme();\n const keybindW = 14;\n const titleW = Math.max(20, width - keybindW - 2);\n const { command, headerCategory } = row;\n const title = command.title;\n const desc = command.description ?? \"\";\n\n return (\n <Box flexDirection=\"column\">\n {headerCategory && (\n <Box>\n <Text color={theme.help.desc} dimColor>\n ── {headerCategory} ──\n </Text>\n </Box>\n )}\n <Box backgroundColor={selected ? theme.sidebar.selected : undefined}>\n <Box width={titleW}>\n <Text color={selected ? theme.sidebar.selectedFg : undefined}>\n {selected ? \"▸ \" : \" \"}\n </Text>\n <Text\n color={selected ? theme.sidebar.selectedFg : theme.info.value}\n bold={selected}\n wrap=\"truncate\"\n >\n {title}\n </Text>\n {desc && (\n <Text color={theme.help.desc} wrap=\"truncate\">\n {\" \"}\n {desc}\n </Text>\n )}\n </Box>\n <Box width={keybindW} justifyContent=\"flex-end\">\n {command.keybinding ? (\n <Text color={theme.help.key}>{command.keybinding}</Text>\n ) : (\n <Text color={theme.help.desc} dimColor>\n —\n </Text>\n )}\n </Box>\n </Box>\n </Box>\n );\n}\n","/**\n * ComposeRecipientModal — two-stage compose for sending to a new thread.\n *\n * Triggered by `N` (or `c` from sidebar when no thread is selected).\n *\n * Stage 1: recipient entry\n * - Live-validated as the user types. Resolution surfaces:\n * - \"phone\" / \"email\" badge for direct handles\n * - \"contact\" badge for resolved contact-name lookups\n * - Candidate list when the typed string matches multiple contacts\n * - Error message when nothing matches\n * - Tab or Enter commits to stage 2 (only when a single resolution is\n * locked in).\n *\n * Stage 2: message body\n * - Standard text input. Enter sends, Esc cancels back to stage 1.\n *\n * The modal is opaque (backgroundColor) and uses flexShrink={0} on every\n * row — same defensive layout used by the SendViaModal fix from the live\n * audit pass.\n */\nimport { TextInput } from \"@inkjs/ui\";\nimport { Box, Text, useInput } from \"ink\";\nimport { useMemo, useState } from \"react\";\nimport type { RecipientResolution, ResolvedRecipient } from \"../../recipient.js\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\n\ntype Stage = \"recipient\" | \"body\";\n\ninterface Props {\n /** Live resolver — called on every keystroke against the trimmed input. */\n resolve: (input: string) => RecipientResolution;\n /** Final send. Returns {success, error?}. */\n onSend: (handle: string, text: string) => Promise<{ success: boolean; error?: string }>;\n /** Close without sending. */\n onCancel: () => void;\n}\n\nexport function ComposeRecipientModal({ resolve, onSend, onCancel }: Props) {\n const theme = useTheme();\n const [stage, setStage] = useState<Stage>(\"recipient\");\n const [recipientInput, setRecipientInput] = useState(\"\");\n const [lockedRecipient, setLockedRecipient] = useState<ResolvedRecipient | null>(null);\n const [body, setBody] = useState(\"\");\n const [status, setStatus] = useState<string>(\"\");\n\n const resolution = useMemo<RecipientResolution>(\n () => (recipientInput.trim() ? resolve(recipientInput) : { kind: \"error\", message: \"\" }),\n [recipientInput, resolve],\n );\n\n // Esc cancels at any stage. We listen at modal level so it fires\n // regardless of which inner input has focus.\n // Digit 1-9 picks an ambiguous-candidate when one is shown — same\n // pattern as the existing `contact:N` selector. Caps at the first 9\n // candidates (matches the visual cap below).\n useInput((input, key) => {\n if (key.escape) {\n if (stage === \"body\") {\n // Back out to recipient stage rather than full-cancel.\n setStage(\"recipient\");\n setStatus(\"\");\n return;\n }\n onCancel();\n return;\n }\n if (stage === \"recipient\" && resolution.kind === \"ambiguous\" && /^[1-9]$/.test(input)) {\n const idx = Number.parseInt(input, 10) - 1;\n const pick = resolution.candidates[idx];\n if (pick) {\n setLockedRecipient(pick);\n setStage(\"body\");\n setStatus(\"\");\n }\n }\n });\n\n const commitRecipient = () => {\n switch (resolution.kind) {\n case \"phone\":\n case \"email\":\n case \"contact\":\n setLockedRecipient(resolution);\n setStage(\"body\");\n setStatus(\"\");\n return;\n case \"ambiguous\":\n // Numbered picker shows in render; nudge user toward it.\n setStatus(`Ambiguous — ${resolution.candidates.length} matches. Press 1-9 to pick.`);\n return;\n case \"error\":\n setStatus(resolution.message);\n return;\n }\n };\n\n const handleSend = async () => {\n if (!lockedRecipient || !body.trim()) return;\n setStatus(\"Sending…\");\n const result = await onSend(lockedRecipient.handle, body.trim());\n if (result.success) {\n onCancel(); // close modal on success — caller handles UX (status toast)\n } else {\n setStatus(`Send failed: ${result.error ?? \"unknown error\"}`);\n }\n };\n\n // Render\n // Show the normalized handle in the badge when it differs from the raw\n // input — gives the user transparency about how their typing was parsed.\n // E.g. typing `0401990797` shows `[phone → +61401990797]`, typing\n // `+61401990797` directly shows just `[phone]` (no transformation).\n const badge = (() => {\n const trimmedInput = recipientInput.trim();\n const showResolved = (handle: string) =>\n handle.toLowerCase() === trimmedInput.toLowerCase() ? \"\" : ` → ${handle}`;\n if (resolution.kind === \"phone\") return `[phone${showResolved(resolution.handle)}]`;\n if (resolution.kind === \"email\") return `[email${showResolved(resolution.handle)}]`;\n if (resolution.kind === \"contact\") return `[contact → ${resolution.displayName}]`;\n if (resolution.kind === \"ambiguous\") return `[${resolution.candidates.length} matches]`;\n return \"\";\n })();\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"double\"\n borderColor={theme.status.accent}\n backgroundColor={theme.header.dim.bg}\n paddingX={1}\n flexShrink={0}\n >\n <Box flexShrink={0}>\n <Text color={theme.status.accent} bold>\n {stage === \"recipient\" ? \"New message — recipient\" : \"New message — body\"}\n </Text>\n </Box>\n\n {stage === \"recipient\" && (\n <>\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>To: </Text>\n <TextInput\n defaultValue=\"\"\n placeholder=\"phone, email, or contact name\"\n onChange={(v) => setRecipientInput(v)}\n onSubmit={commitRecipient}\n />\n {badge && <Text color={theme.info.label}>{` ${badge}`}</Text>}\n </Box>\n {resolution.kind === \"ambiguous\" && (\n <Box flexDirection=\"column\" flexShrink={0}>\n {resolution.candidates.slice(0, 9).map((c, i) => (\n <Box key={c.handle} flexShrink={0}>\n <Text color={theme.help.key} bold>{` ${i + 1}`}</Text>\n <Text color={theme.help.desc}>{`: ${c.displayName}`}</Text>\n </Box>\n ))}\n {resolution.candidates.length > 9 && (\n <Box flexShrink={0}>\n <Text\n color={theme.help.desc}\n >{` …and ${resolution.candidates.length - 9} more (refine search)`}</Text>\n </Box>\n )}\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>Press 1-9 to pick a match</Text>\n </Box>\n </Box>\n )}\n {resolution.kind === \"error\" && resolution.message && (\n <Box flexShrink={0}>\n <Text color={theme.edited}>{resolution.message}</Text>\n </Box>\n )}\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>Enter: continue · Esc: cancel</Text>\n </Box>\n </>\n )}\n\n {stage === \"body\" && lockedRecipient && (\n <>\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>{`To: ${lockedRecipient.displayName}`}</Text>\n </Box>\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>{\"> \"}</Text>\n <TextInput\n defaultValue=\"\"\n placeholder=\"Type your message…\"\n onChange={(v) => setBody(v)}\n onSubmit={handleSend}\n />\n </Box>\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>Enter: send · Esc: back to recipient</Text>\n </Box>\n </>\n )}\n\n {status && (\n <Box flexShrink={0}>\n <Text color={theme.edited}>{status}</Text>\n </Box>\n )}\n </Box>\n );\n}\n","/**\n * Three-field date picker (YYYY / MM / DD) with arrow-key navigation.\n *\n * ←/→ cycle active field\n * ↑/↓ increment/decrement value\n * 0–9 type digit (right-shifts onto the field; full field auto-advances)\n * Bksp delete last digit of active field\n * Enter submit\n *\n * Returns the picked date as an ISO string (\"YYYY-MM-DD\") to keep the modal's\n * existing parser path unchanged.\n */\nimport { Box, Text, useInput } from \"ink\";\nimport { useState } from \"react\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\n\ntype Field = \"year\" | \"month\" | \"day\";\nconst ORDER: Field[] = [\"year\", \"month\", \"day\"];\n\ninterface Props {\n initial?: Date;\n focused: boolean;\n onSubmit: (isoDate: string) => void;\n onCancel: () => void;\n}\n\nconst MIN_YEAR = 1900;\nconst MAX_YEAR = 2100;\n\nfunction daysInMonth(year: number, monthOneBased: number): number {\n return new Date(year, monthOneBased, 0).getDate();\n}\n\nfunction clamp(n: number, lo: number, hi: number): number {\n return Math.min(Math.max(n, lo), hi);\n}\n\nexport function DatePicker({ initial, focused, onSubmit, onCancel }: Props) {\n const theme = useTheme();\n const now = initial ?? new Date();\n const [year, setYear] = useState(now.getFullYear());\n const [month, setMonth] = useState(now.getMonth() + 1);\n const [day, setDay] = useState(now.getDate());\n const [active, setActive] = useState<Field>(\"year\");\n\n const fieldValue = (f: Field) => (f === \"year\" ? year : f === \"month\" ? month : day);\n\n const setField = (f: Field, v: number) => {\n if (f === \"year\") {\n const y = clamp(v, MIN_YEAR, MAX_YEAR);\n setYear(y);\n // Re-clamp day to month/year (Feb 29 → Feb 28 on non-leap)\n setDay((d) => clamp(d, 1, daysInMonth(y, month)));\n } else if (f === \"month\") {\n const m = clamp(v, 1, 12);\n setMonth(m);\n setDay((d) => clamp(d, 1, daysInMonth(year, m)));\n } else {\n setDay(clamp(v, 1, daysInMonth(year, month)));\n }\n };\n\n useInput(\n (input, key) => {\n if (!focused) return;\n if (key.escape) {\n onCancel();\n return;\n }\n if (key.return) {\n const iso = `${String(year).padStart(4, \"0\")}-${String(month).padStart(2, \"0\")}-${String(day).padStart(2, \"0\")}`;\n onSubmit(iso);\n return;\n }\n if (key.leftArrow) {\n const i = ORDER.indexOf(active);\n setActive(ORDER[(i + ORDER.length - 1) % ORDER.length]);\n return;\n }\n if (key.rightArrow) {\n const i = ORDER.indexOf(active);\n setActive(ORDER[(i + 1) % ORDER.length]);\n return;\n }\n if (key.upArrow) {\n setField(active, fieldValue(active) + 1);\n return;\n }\n if (key.downArrow) {\n setField(active, fieldValue(active) - 1);\n return;\n }\n if (input && /^[0-9]$/.test(input)) {\n const digit = Number.parseInt(input, 10);\n if (active === \"year\") {\n // Shift left and append. Year field is 4 digits.\n const next = (year % 1000) * 10 + digit;\n setField(\"year\", next);\n } else if (active === \"month\") {\n // 2-digit shift\n const next = (month % 10) * 10 + digit;\n setField(\"month\", next < 1 ? 1 : next);\n } else {\n const next = (day % 10) * 10 + digit;\n setField(\"day\", next < 1 ? 1 : next);\n }\n return;\n }\n if (key.backspace || key.delete) {\n if (active === \"year\") setField(\"year\", Math.floor(year / 10) || MIN_YEAR);\n else if (active === \"month\") setField(\"month\", Math.floor(month / 10) || 1);\n else setField(\"day\", Math.floor(day / 10) || 1);\n }\n },\n { isActive: focused },\n );\n\n const fieldStr = (f: Field, width: number) => String(fieldValue(f)).padStart(width, \"0\");\n\n const renderField = (f: Field, width: number) => {\n const isActive = active === f;\n return (\n <Text\n color={isActive ? theme.status.accent : theme.drawer.value}\n bold={isActive}\n inverse={isActive}\n >\n {fieldStr(f, width)}\n </Text>\n );\n };\n\n return (\n <Box flexDirection=\"column\">\n <Box>\n {renderField(\"year\", 4)}\n <Text color={theme.help.desc}>-</Text>\n {renderField(\"month\", 2)}\n <Text color={theme.help.desc}>-</Text>\n {renderField(\"day\", 2)}\n </Box>\n <Box>\n <Text color={theme.help.desc}>←/→ field · ↑/↓ adjust · digits to type · Enter to jump</Text>\n </Box>\n </Box>\n );\n}\n","/**\n * Date-jump modal — two input modes:\n * - picker (default): three-field YYYY-MM-DD picker, arrow-keys for navigation.\n * - text: free-form text input, parsed via `parseUserDate` (handles ISO,\n * keywords like \"yesterday\", relative phrases like \"2 weeks ago\",\n * compact \"5d/2w/3m/1y\", etc).\n *\n * `Tab` (when the inner field doesn't claim it) toggles between modes. The\n * existing caller path is preserved: `onSubmit` always receives a string,\n * either a free-form expression (text mode) or a normalized ISO date\n * (picker mode). Both flow through `parseUserDate` downstream.\n */\nimport { TextInput } from \"@inkjs/ui\";\nimport { Box, Text, useInput } from \"ink\";\nimport { useState } from \"react\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\nimport { DatePicker } from \"./DatePicker.js\";\n\ntype Mode = \"picker\" | \"text\";\n\ninterface Props {\n value: string;\n error: string;\n onChange: (v: string) => void;\n onSubmit: (v: string) => void;\n}\n\nexport function DateJumpModal({ value, error, onChange, onSubmit }: Props) {\n const theme = useTheme();\n const [mode, setMode] = useState<Mode>(\"picker\");\n\n // Tab swaps modes. Listen at the modal level so the swap fires regardless\n // of which inner widget has focus.\n useInput((_input, key) => {\n if (key.tab && !key.shift) {\n setMode((m) => (m === \"picker\" ? \"text\" : \"picker\"));\n }\n });\n\n // Opaque background + flexShrink={0} on every row matches the modal\n // discipline used by ComposeRecipientModal / SendViaModal — without\n // these, conversation-list slugs from the sidebar leak through the\n // modal's interior (visible bug in live tmux tests).\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"double\"\n borderColor={theme.status.accent}\n backgroundColor={theme.header.dim.bg}\n paddingX={1}\n flexShrink={0}\n >\n <Box flexShrink={0}>\n <Text color={theme.status.accent} bold>\n Jump to date\n </Text>\n <Text color={theme.help.desc}>\n {\" \"}[{mode}] · Tab to switch\n </Text>\n </Box>\n\n {mode === \"picker\" ? (\n <DatePicker\n focused\n onSubmit={onSubmit}\n onCancel={() => {\n // Esc handled by the App's date-jump exit branch — DatePicker\n // forwards it via onCancel but we leave actual exit to the parent.\n }}\n />\n ) : (\n <Box flexDirection=\"column\" flexShrink={0}>\n <Box flexShrink={0}>\n <Text color={theme.info.label}>Date: </Text>\n <TextInput\n defaultValue={value}\n onChange={onChange}\n onSubmit={onSubmit}\n placeholder=\"2024-03-15 | 3/15 | yesterday | 2 weeks ago | 1y\"\n />\n </Box>\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>\n Formats: YYYY-MM-DD · M/D · today · yesterday · N days/weeks/months/years ago · 5d /\n 2w / 3m / 1y\n </Text>\n </Box>\n </Box>\n )}\n\n {error && (\n <Box flexShrink={0}>\n <Text color={theme.edited}>{error}</Text>\n </Box>\n )}\n\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>Enter: jump · Esc: cancel · Tab: switch mode</Text>\n </Box>\n </Box>\n );\n}\n","import { Box, Text } from \"ink\";\nimport type { DevStatsData } from \"../hooks/useDevStats.js\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\n\ninterface Props {\n stats: DevStatsData;\n width: number;\n}\n\nexport function DevStats({ stats, width }: Props) {\n const theme = useTheme();\n const engineColor = stats.engine.startsWith(\"Rust\") ? theme.rustEngine : theme.status.accent;\n const cpuColor =\n stats.cpuPercent > 50 ? theme.cpuHigh : stats.cpuPercent > 20 ? theme.edited : theme.info.value;\n\n return (\n <Box\n flexDirection=\"column\"\n width={width}\n borderStyle=\"single\"\n borderColor={theme.border}\n overflow=\"hidden\"\n >\n <Box paddingX={1} backgroundColor={theme.header.dim.bg}>\n <Text color={theme.header.dim.fg} bold>\n Stats\n </Text>\n </Box>\n <Box flexDirection=\"column\" paddingX={1}>\n {/*\n * Engine row is the only stat whose value (\"Rust parser + TS DB\") is\n * wider than the narrow stats column. With `justifyContent=\"space-between\"`\n * Ink wraps the value mid-string and interleaves it with the label\n * (showing `EngiRust parser` / `e + TS DB`). Stack label-above-value\n * for this row so the value gets the full column width.\n */}\n <Text color={theme.info.label}>Engine</Text>\n <Text color={engineColor} bold>\n {stats.engine}\n </Text>\n <Box justifyContent=\"space-between\">\n <Text color={theme.info.label}>CPU</Text>\n <Text color={cpuColor}>{stats.cpuPercent}%</Text>\n </Box>\n <Box justifyContent=\"space-between\">\n <Text color={theme.info.label}>Mem</Text>\n <Text color={theme.info.value}>{stats.memMB}MB</Text>\n </Box>\n <Box justifyContent=\"space-between\">\n <Text color={theme.info.label}>PID</Text>\n <Text color={theme.info.value}>{stats.pid}</Text>\n </Box>\n <Box justifyContent=\"space-between\">\n <Text color={theme.info.label}>Up</Text>\n <Text color={theme.info.value}>{stats.uptime}</Text>\n </Box>\n {stats.lastQueryMs !== null && (\n <Box justifyContent=\"space-between\">\n <Text color={theme.info.label}>Query</Text>\n <Text color={stats.lastQueryMs > 500 ? theme.edited : theme.sms}>\n {stats.lastQueryMs}ms\n </Text>\n </Box>\n )}\n <Box justifyContent=\"space-between\">\n <Text color={theme.info.label}>Lag</Text>\n <Text\n color={\n stats.eventLoopP99Ms > 500\n ? theme.cpuHigh\n : stats.eventLoopP99Ms > 100\n ? theme.edited\n : theme.info.value\n }\n >\n {stats.eventLoopP99Ms}ms\n </Text>\n </Box>\n <Box justifyContent=\"space-between\">\n <Text color={theme.info.label}>Active</Text>\n <Text color={theme.info.value}>{stats.lastActivityAgo}</Text>\n </Box>\n </Box>\n </Box>\n );\n}\n\n/** Compact inline stats for the status bar (when full panel is hidden).\n *\n * Engine label is abbreviated (full \"Rust parser + TS DB\" → \"Rust+TS\")\n * so the bar fits on standard 80-col terminals without colliding with\n * the left-side service/handle segment. The full panel (toggled with\n * `d`) keeps the verbose label.\n *\n * flexShrink={0} keeps the gap={1} separators intact even when the\n * parent StatusBar's right-side Box is being squeezed by a long toast\n * — without it the row would render as \"TS0%0MB\" instead of \"TS 0% 0MB\".\n */\nexport function CompactStats({ stats }: { stats: DevStatsData }) {\n const theme = useTheme();\n const engineColor = stats.engine.startsWith(\"Rust\") ? theme.rustEngine : theme.status.accent;\n const compactEngine = stats.engine\n .replace(\"Rust parser + TS DB\", \"Rust+TS\")\n .replace(\"Rust parser + Rust DB\", \"Rust\")\n .replace(\"TS parser + TS DB\", \"TS\");\n return (\n <Box gap={1} flexShrink={0}>\n <Text color={engineColor}>{compactEngine}</Text>\n <Text color={theme.info.label}>{stats.cpuPercent}%</Text>\n <Text color={theme.info.label}>{stats.memMB}MB</Text>\n <Text color={theme.info.label}>PID:{stats.pid}</Text>\n </Box>\n );\n}\n","import { TextInput } from \"@inkjs/ui\";\nimport { Box, Text } from \"ink\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\n\ninterface Props {\n format: \"markdown\" | \"csv\" | \"json\";\n path: string;\n rangeSummary: string;\n onChangePath: (p: string) => void;\n onSubmit: () => void;\n}\n\n/**\n * Export modal — full-width banner at the top of the body area.\n * The format is cycled via Tab in App.tsx (which dispatches SET_EXPORT_FORMAT\n * before rendering this). The path is the only inline-editable field here.\n */\nexport function ExportModal({ format, path, rangeSummary, onChangePath, onSubmit }: Props) {\n const theme = useTheme();\n // Opaque background + flexShrink={0} on every row matches the modal\n // discipline used by ComposeRecipientModal / SendViaModal — prevents\n // the sidebar conversation list from leaking through the modal cells.\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"double\"\n borderColor={theme.status.accent}\n backgroundColor={theme.header.dim.bg}\n paddingX={1}\n flexShrink={0}\n >\n <Box flexShrink={0}>\n <Text color={theme.status.accent} bold>\n Export messages\n </Text>\n </Box>\n <Box flexShrink={0}>\n <Text color={theme.info.label}>Range: </Text>\n <Text color={theme.info.value}>{rangeSummary}</Text>\n </Box>\n <Box flexShrink={0}>\n <Text color={theme.info.label}>Format: </Text>\n <Text\n color={format === \"markdown\" ? theme.status.accent : theme.info.value}\n bold={format === \"markdown\"}\n >\n [Markdown]\n </Text>\n <Text> </Text>\n <Text\n color={format === \"csv\" ? theme.status.accent : theme.info.value}\n bold={format === \"csv\"}\n >\n [CSV]\n </Text>\n <Text> </Text>\n <Text\n color={format === \"json\" ? theme.status.accent : theme.info.value}\n bold={format === \"json\"}\n >\n [JSON]\n </Text>\n <Text color={theme.help.desc}> (Tab cycles)</Text>\n </Box>\n <Box flexShrink={0}>\n <Text color={theme.info.label}>Path: </Text>\n <TextInput\n defaultValue={path}\n onChange={onChangePath}\n onSubmit={onSubmit}\n placeholder=\"/absolute/path/to/file\"\n />\n </Box>\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>Enter: save Esc: cancel Tab: cycle format</Text>\n </Box>\n </Box>\n );\n}\n","import { Box, Text } from \"ink\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\nimport type { FocusPane, Mode } from \"../types.js\";\n\ninterface Props {\n mode: Mode;\n focus?: FocusPane;\n}\n\nconst PALETTE_HINT: [string, string] = [\"^P\", \"palette\"];\n\nconst SIDEBAR_KEYS = [\n [\"j/k\", \"move\"],\n [\"#j/k\", \"jump\"],\n [\"gg/G\", \"top/btm\"],\n [\"^d/u\", \"½page\"],\n [\"y\", \"copy slug\"],\n [\"N\", \"new msg\"],\n [\"Tab\", \"→msgs\"],\n [\"/\", \"filter\"],\n [\"d\", \"stats\"],\n [\"r\", \"refresh\"],\n PALETTE_HINT,\n [\"q\", \"quit\"],\n];\n\nconst THREAD_KEYS = [\n [\"j/k\", \"move\"],\n [\"#j/k\", \"jump\"],\n [\"{/}\", \"grp jump\"],\n [\"gg/G\", \"top/btm\"],\n [\"^d/u\", \"½page\"],\n [\"V\", \"select\"],\n [\":\", \"date jump\"],\n [\"Enter\", \"details\"],\n [\"o\", \"open att\"],\n [\"O\", \"open in Msgs\"],\n [\"S\", \"send-via\"],\n [\"c\", \"compose\"],\n PALETTE_HINT,\n [\"Tab\", \"→list\"],\n];\n\nconst PALETTE_KEYS = [\n [\"type\", \"filter\"],\n [\"↑↓\", \"move\"],\n [\"Enter\", \"run\"],\n [\"Esc\", \"close\"],\n];\n\nconst SEND_VIA_KEYS = [\n [\"1-9\", \"pick app\"],\n [\"Esc\", \"cancel\"],\n];\n\nconst SELECT_KEYS = [\n [\"j/k\", \"extend\"],\n [\"{/}\", \"grp\"],\n [\"gg/G\", \"top/btm\"],\n [\"^d/u\", \"½page\"],\n [\"e\", \"export\"],\n [\"y\", \"copy text\"],\n [\"Esc\", \"exit select\"],\n];\n\nconst EXPORT_KEYS = [\n [\"Tab\", \"fmt: md/csv/json\"],\n [\"Enter\", \"save\"],\n [\"Esc\", \"cancel\"],\n];\n\nconst DATE_JUMP_KEYS = [\n [\"Tab\", \"picker↔text\"],\n [\"←/→\", \"field\"],\n [\"↑/↓\", \"adjust\"],\n [\"Enter\", \"jump\"],\n [\"Esc\", \"cancel\"],\n];\n\nconst COMPOSE_KEYS = [\n [\"Enter\", \"send\"],\n [\"Esc\", \"cancel\"],\n];\n\nconst COMPOSE_NEW_KEYS = [\n [\"type\", \"phone/email/contact\"],\n [\"Enter\", \"next\"],\n [\"Esc\", \"cancel\"],\n];\n\nconst FILTER_KEYS = [[\"Enter/Esc\", \"exit filter\"]];\n\nconst DRAWER_KEYS = [\n [\"j/k\", \"scroll\"],\n [\"o\", \"open attachment\"],\n [\"Esc/q\", \"close\"],\n];\n\nexport function HelpBar({ mode, focus }: Props) {\n const theme = useTheme();\n let keys: string[][];\n if (mode === \"compose-new\") keys = COMPOSE_NEW_KEYS;\n else if (mode === \"compose\" || mode === \"confirm\") keys = COMPOSE_KEYS;\n else if (mode === \"filter\") keys = FILTER_KEYS;\n else if (mode === \"drawer\") keys = DRAWER_KEYS;\n else if (mode === \"select\") keys = SELECT_KEYS;\n else if (mode === \"export\") keys = EXPORT_KEYS;\n else if (mode === \"date-jump\") keys = DATE_JUMP_KEYS;\n else if (mode === \"send-via\") keys = SEND_VIA_KEYS;\n else if (mode === \"palette\") keys = PALETTE_KEYS;\n else keys = focus === \"thread\" ? THREAD_KEYS : SIDEBAR_KEYS;\n\n return (\n <Box paddingX={1} height={1} gap={1}>\n {keys.map(([key, desc]) => (\n // Render each hint as a SINGLE <Text> with nested colored spans.\n // Splitting `<Text>{key}</Text>` and `<Text>:desc</Text>` into separate\n // <Text> siblings inside a row Box makes Ink's flex-shrink consider\n // each individually — when the hints overflow the terminal width (which\n // happens in thread mode with 14 entries) flex-shrink trims ONE\n // character off each child's last <Text>, eating the final char of the\n // key label (e.g. `j/k` → `j/`, `Enter` → `Ente`, `V` → ``).\n // A single <Text> with nested color spans is treated as one atomic\n // string for shrink purposes — it either fits or wraps the whole\n // hint, never partial.\n // Also `flexShrink={0}` so overflow doesn't truncate at all; if the\n // row is too narrow the trailing hints drop off the right edge\n // instead of being silently clipped char-by-char.\n <Box key={key} flexShrink={0}>\n <Text>\n <Text color={theme.help.key}>{key}</Text>\n <Text color={theme.help.desc}>:{desc} </Text>\n </Text>\n </Box>\n ))}\n </Box>\n );\n}\n","/**\n * Glyph presets — the user-selectable \"theme\" axis.\n *\n * - \"safe\" (default): only universally-renderable glyphs. Geometric\n * Shapes (▶ ◀ ●), ASCII separators (─), emoji where every modern OS\n * font has them (✉ 💬 📱). No Powerline private-use codepoints, no\n * Nerd Font icons. Renders correctly in Apple Terminal, Warp, iTerm2,\n * VS Code's integrated terminal, Alacritty defaults, kitty defaults.\n *\n * - \"powerline\": Powerline arrows + Nerd Font icons. Looks better but\n * requires the user's terminal font to be a Nerd-Font-patched build\n * (FiraCode Nerd Font, JetBrainsMono Nerd Font, etc.). The README\n * warns about this. `imsg config show` flags it too.\n *\n * Categories — the consumer (theme.ts → makeTheme) just spreads one of\n * these into the produced Theme object.\n */\n\nexport interface GlyphSet {\n /** Powerline arrow separators. Only used when the user opts in. */\n arrowRight: string;\n arrowRightThin: string;\n arrowLeft: string;\n arrowLeftThin: string;\n\n /** Direction indicators on each message row. Always single-cell-wide. */\n sent: string;\n received: string;\n\n /** Status indicators */\n unreadDot: string;\n envelope: string;\n\n /** Service icons (iMessage / SMS) */\n iMessage: string;\n sms: string;\n\n /** Misc UI */\n paperclip: string;\n group: string;\n search: string;\n pencil: string;\n refresh: string;\n separator: string;\n}\n\nconst SAFE: GlyphSet = {\n // Powerline arrows aren't actually safe — for the safe preset we\n // substitute simple ASCII triangles. Components that draw arrow\n // separators between segments fall back to vertical bar `│`.\n arrowRight: \"│\",\n arrowRightThin: \"│\",\n arrowLeft: \"│\",\n arrowLeftThin: \"│\",\n // Geometric Shapes — fixed-width East-Asian range, every font has them.\n sent: \"▶\", // ▶\n received: \"◀\", // ◀\n unreadDot: \"●\",\n envelope: \"✉\",\n iMessage: \"💬\",\n sms: \"📱\",\n paperclip: \"📎\",\n group: \"☰\",\n search: \"⌕\",\n pencil: \"✎\",\n refresh: \"↻\",\n separator: \"─\",\n};\n\nconst POWERLINE: GlyphSet = {\n // Powerline private-use range (E0B0..E0B3).\n arrowRight: \"\",\n arrowRightThin: \"\",\n arrowLeft: \"\",\n arrowLeftThin: \"\",\n // Use the Powerline arrows as direction glyphs too — visually consistent.\n sent: \"\",\n received: \"\",\n unreadDot: \"●\",\n envelope: \"\", // Nerd Font envelope (FontAwesome)\n iMessage: \"\", // Nerd Font speech bubble\n sms: \"\", // Nerd Font phone\n paperclip: \"\", // Nerd Font paperclip\n group: \"\", // Nerd Font group\n search: \"\", // Nerd Font magnifier\n pencil: \"\", // Nerd Font pencil\n refresh: \"\", // Nerd Font refresh\n separator: \"─\",\n};\n\nexport const GLYPH_PRESETS = {\n safe: SAFE,\n powerline: POWERLINE,\n} as const;\n\nexport type GlyphPreset = keyof typeof GLYPH_PRESETS;\n\nexport const TAPBACK_EMOJI: Record<string, string> = {\n love: \"❤️\",\n like: \"👍\",\n dislike: \"👎\",\n laugh: \"😂\",\n emphasize: \"‼️\",\n question: \"❓\",\n};\n","/**\n * Pure color-space utilities. No dependencies, no side effects.\n *\n * The TUI palette derives its 30-odd hex colors from a single accent\n * hex; that derivation is HSL math (rotate hue, mix toward grey, push\n * lightness up or down). All of those operations live here so they can\n * be unit-tested without dragging in any rendering code.\n *\n * Hex format: always 7-char `#RRGGBB`. Output strings are lower-case.\n * HSL: h ∈ [0, 360), s ∈ [0, 1], l ∈ [0, 1].\n */\n\nexport interface Hsl {\n h: number;\n s: number;\n l: number;\n}\n\nconst HEX6_RE = /^#[0-9a-fA-F]{6}$/;\n\n/** Parse `#rrggbb` to {r,g,b} ∈ [0,255]. Throws on invalid input. */\nfunction hexToRgb(hex: string): { r: number; g: number; b: number } {\n if (!HEX6_RE.test(hex)) {\n throw new Error(`invalid hex color: ${hex} (expected #RRGGBB)`);\n }\n return {\n r: Number.parseInt(hex.slice(1, 3), 16),\n g: Number.parseInt(hex.slice(3, 5), 16),\n b: Number.parseInt(hex.slice(5, 7), 16),\n };\n}\n\nfunction rgbToHex(r: number, g: number, b: number): string {\n const c = (n: number) =>\n Math.max(0, Math.min(255, Math.round(n)))\n .toString(16)\n .padStart(2, \"0\");\n return `#${c(r)}${c(g)}${c(b)}`;\n}\n\nexport function hexToHsl(hex: string): Hsl {\n const { r, g, b } = hexToRgb(hex);\n const rn = r / 255;\n const gn = g / 255;\n const bn = b / 255;\n const max = Math.max(rn, gn, bn);\n const min = Math.min(rn, gn, bn);\n const l = (max + min) / 2;\n const d = max - min;\n let h = 0;\n let s = 0;\n if (d !== 0) {\n s = d / (1 - Math.abs(2 * l - 1));\n if (max === rn) h = 60 * (((gn - bn) / d) % 6);\n else if (max === gn) h = 60 * ((bn - rn) / d + 2);\n else h = 60 * ((rn - gn) / d + 4);\n if (h < 0) h += 360;\n }\n return { h, s, l };\n}\n\nexport function hslToHex({ h, s, l }: Hsl): string {\n const hh = ((h % 360) + 360) % 360;\n const ss = Math.max(0, Math.min(1, s));\n const ll = Math.max(0, Math.min(1, l));\n const c = (1 - Math.abs(2 * ll - 1)) * ss;\n const x = c * (1 - Math.abs(((hh / 60) % 2) - 1));\n const m = ll - c / 2;\n let r1 = 0;\n let g1 = 0;\n let b1 = 0;\n if (hh < 60) [r1, g1, b1] = [c, x, 0];\n else if (hh < 120) [r1, g1, b1] = [x, c, 0];\n else if (hh < 180) [r1, g1, b1] = [0, c, x];\n else if (hh < 240) [r1, g1, b1] = [0, x, c];\n else if (hh < 300) [r1, g1, b1] = [x, 0, c];\n else [r1, g1, b1] = [c, 0, x];\n return rgbToHex((r1 + m) * 255, (g1 + m) * 255, (b1 + m) * 255);\n}\n\n/** Return `hex` with lightness set to `l` ∈ [0,1]. */\nexport function withL(hex: string, l: number): string {\n const hsl = hexToHsl(hex);\n return hslToHex({ ...hsl, l });\n}\n\n/** Return `hex` with saturation set to `s` ∈ [0,1]. s=0 produces grey at the same lightness. */\nexport function withS(hex: string, s: number): string {\n const hsl = hexToHsl(hex);\n return hslToHex({ ...hsl, s });\n}\n\n/** Rotate hue by `degrees`. Negative values supported. */\nexport function rotateHue(hex: string, degrees: number): string {\n const hsl = hexToHsl(hex);\n return hslToHex({ ...hsl, h: hsl.h + degrees });\n}\n\n/**\n * Produce a desaturated, dark-or-light-tinted variant of `accent`. Used\n * by the palette to make borders / backgrounds / dim text feel like they\n * belong to the accent's \"color family\" without screaming.\n *\n * @param l Target lightness ∈ [0,1]\n * @param satMul Multiplier on the original saturation (1 = keep, 0 = grey)\n */\nexport function tint(accent: string, l: number, satMul: number): string {\n const hsl = hexToHsl(accent);\n return hslToHex({ h: hsl.h, s: hsl.s * satMul, l });\n}\n\n/**\n * WCAG-approximate relative luminance of a hex color.\n * Used by tests to assert palette-pair contrast ratios.\n */\nexport function relativeLuminance(hex: string): number {\n const { r, g, b } = hexToRgb(hex);\n const channel = (c: number) => {\n const cs = c / 255;\n return cs <= 0.03928 ? cs / 12.92 : ((cs + 0.055) / 1.055) ** 2.4;\n };\n return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);\n}\n\n/** Contrast ratio between two hex colors per WCAG 2.0. Range [1, 21]. */\nexport function contrastRatio(a: string, b: string): number {\n const la = relativeLuminance(a);\n const lb = relativeLuminance(b);\n const [hi, lo] = la > lb ? [la, lb] : [lb, la];\n return (hi + 0.05) / (lo + 0.05);\n}\n","/**\n * Single-accent palette derivation.\n *\n * The user picks one hex (default `#1982FC` = iMessage blue) and the\n * accent-driven parts of the palette come out of it. Approach:\n *\n * - **Accent-driven**: backgrounds, borders, headers, sidebar selection,\n * sent-bubble fill, and the unread dot all use the accent's hue at\n * varying lightness × saturation. Move the accent to orange and the\n * whole UI shifts orange.\n * - **Neutral with accent tint**: text colors and dim backgrounds keep\n * the accent's *hue* but at very low saturation (~5–10%). They feel\n * like part of the family without competing with the accent itself.\n * - **Semantic colors stay fixed**: error red, sms green, attachment\n * warm-yellow, edited gold. These need to be recognizable regardless\n * of accent (a magenta accent shouldn't make SMS markers magenta).\n * They're hand-tuned to coexist with any accent.\n *\n * The exported `Palette` shape mirrors what the legacy `theme` object\n * exported — the hand-tuned iMessage-inspired values are the target\n * for `accent = #1982FC`.\n */\n\nimport { contrastRatio, hexToHsl, hslToHex, withL, withS } from \"./color.js\";\n\nexport interface Palette {\n sent: { bg: string; fg: string; border: string };\n received: { bg: string; fg: string; border: string };\n pending: { bg: string; fg: string; border: string };\n\n sentText: string;\n receivedText: string;\n senderName: string;\n replyContext: string;\n attachment: string;\n lineNum: string;\n\n groupBg: { sent: string; received: string };\n selectionBg: string;\n\n sidebar: {\n selected: string;\n selectedFg: string;\n unread: string;\n read: string;\n snippet: string;\n slug: string;\n slugBg: string;\n separator: string;\n time: string;\n };\n\n border: string;\n dot: string;\n header: { focused: { bg: string; fg: string }; dim: { bg: string; fg: string } };\n info: { label: string; value: string };\n timestamp: string;\n status: { bg: string; fg: string; accent: string };\n help: { key: string; desc: string };\n sms: string;\n edited: string;\n compose: { bg: string; fg: string; placeholder: string };\n dateSep: string;\n drawer: { bg: string; border: string; label: string; value: string };\n\n /** Diagnostic — DevStats indicators that previously hard-coded #FF6B35 / #FF4444. */\n rustEngine: string;\n cpuHigh: string;\n}\n\nexport const DEFAULT_ACCENT = \"#1982FC\";\n\nexport function derivePalette(accent: string): Palette {\n const { h } = hexToHsl(accent);\n // Lightness clamp helpers — accept any input accent, never produce\n // text that's invisible on the dark TUI background.\n const acc = (l: number) => hslToHex({ h, s: 0.95, l });\n // Near-grey, slight accent tint — for borders, dim text, neutral bgs.\n const tint = (l: number, s = 0.06) => hslToHex({ h, s, l });\n // Choose whichever of {near-black, near-white} produces better\n // contrast against `bg`. Used by sent bubbles + sidebar selection\n // where the bg is accent-driven and may swing across the lightness\n // spectrum (magenta at L=0.55 has just-medium luminance and needs\n // dark fg; iMessage blue at L=0.55 needs light fg).\n const dark = tint(0.05, 0.1);\n const light = tint(0.97, 0.05);\n const fgFor = (bg: string) => (contrastRatio(dark, bg) > contrastRatio(light, bg) ? dark : light);\n\n const sentBg = acc(0.55); // accent at full saturation, mid-light → \"iMessage blue\"\n const selectedBg = acc(0.27);\n return {\n sent: {\n bg: sentBg,\n fg: fgFor(sentBg),\n border: acc(0.42),\n },\n received: {\n // Light grey bubble, very slight accent tint — preserves the iMessage\n // \"received\" look. Stays the same shade across accents (it's mostly grey).\n bg: tint(0.91, 0.05),\n fg: tint(0.12, 0.1),\n border: tint(0.75, 0.05),\n },\n pending: {\n bg: tint(0.27, 0.06),\n fg: tint(0.74, 0.06),\n border: tint(0.35, 0.08),\n },\n\n sentText: acc(0.78), // light accent — for sent text shown outside a bubble\n receivedText: tint(0.85, 0.06),\n // Sender names: a fixed teal that reads as \"another person\" in every\n // accent. Keeping accent-derived would produce magenta sender names on\n // a magenta accent, defeating the visual cue.\n senderName: SEMANTIC.senderName,\n replyContext: tint(0.62, 0.1),\n attachment: SEMANTIC.attachment, // fixed warm yellow\n lineNum: tint(0.45, 0.05),\n\n groupBg: {\n sent: tint(0.16, 0.4), // dark, clearly accent-tinted (sent rows)\n received: hslToHex({ h: (h + 20) % 360, s: 0.18, l: 0.14 }), // close-but-not-equal\n },\n selectionBg: SEMANTIC.selection, // muted warm yellow — universal \"selected\"\n\n sidebar: {\n selected: selectedBg,\n selectedFg: fgFor(selectedBg),\n unread: tint(0.97, 0.05),\n read: tint(0.74, 0.06),\n snippet: tint(0.51, 0.05),\n slug: hslToHex({ h, s: 0.28, l: 0.55 }),\n slugBg: tint(0.1, 0.15),\n separator: tint(0.2, 0.05),\n time: tint(0.51, 0.05),\n },\n\n border: tint(0.25, 0.05),\n dot: acc(0.55),\n header: {\n focused: { bg: tint(0.2, 0.06), fg: tint(0.97, 0.05) },\n dim: { bg: tint(0.13, 0.06), fg: tint(0.51, 0.05) },\n },\n info: { label: tint(0.62, 0.05), value: tint(0.85, 0.05) },\n timestamp: tint(0.62, 0.1),\n status: { bg: tint(0.13, 0.06), fg: tint(0.74, 0.06), accent: acc(0.55) },\n help: { key: tint(0.74, 0.06), desc: tint(0.4, 0.05) },\n sms: SEMANTIC.sms, // green — universal \"SMS not iMessage\" cue\n edited: SEMANTIC.edited, // gold — universal \"this was edited\" cue\n compose: {\n bg: tint(0.18, 0.06),\n fg: tint(0.97, 0.05),\n placeholder: tint(0.4, 0.05),\n },\n dateSep: tint(0.32, 0.05),\n drawer: {\n bg: tint(0.13, 0.06),\n border: tint(0.25, 0.05),\n label: tint(0.62, 0.05),\n value: tint(0.85, 0.05),\n },\n\n rustEngine: SEMANTIC.rustEngine, // warm orange — distinct from any accent\n cpuHigh: SEMANTIC.cpuHigh, // red — universal \"elevated\"\n };\n}\n\n/** Hand-tuned constants kept outside the accent-derivation loop. */\nconst SEMANTIC = {\n senderName: \"#5AC8C8\", // teal\n attachment: \"#FFB347\", // orange (paperclip)\n selection: \"#3C3814\", // dim olive (visual select bg)\n sms: \"#5AC85A\", // green (SMS marker)\n edited: \"#96821E\", // gold (edited indicator)\n rustEngine: \"#FF6B35\", // warm orange (DevStats engine label)\n cpuHigh: \"#FF4444\", // red (DevStats elevated CPU)\n} as const;\n\n// Surface tiny helpers for tests / setup probes.\nexport { withL, withS };\n","/**\n * TUI theme — assembled from a glyph preset + an accent-derived palette.\n *\n * Components consume the resolved theme via `useTheme()` from\n * `./themes/ThemeContext.js`. This file's `makeTheme()` is the single\n * place where palette + glyphs combine into the final shape.\n *\n * Backwards-compat: a few non-component places still want a constant\n * `glyphs` / `TAPBACK_EMOJI`. Those remain re-exported here. The legacy\n * `theme` constant export is gone — components were swept to `useTheme()`\n * in the same change as this refactor.\n */\n\nimport { GLYPH_PRESETS, type GlyphPreset, type GlyphSet } from \"./themes/glyphs.js\";\nimport { DEFAULT_ACCENT, derivePalette, type Palette } from \"./themes/palette.js\";\n\nexport interface Theme extends Palette {\n glyphs: GlyphSet;\n}\n\nexport interface ThemeOptions {\n preset?: GlyphPreset;\n accent?: string;\n}\n\nexport function makeTheme({ preset = \"safe\", accent = DEFAULT_ACCENT }: ThemeOptions = {}): Theme {\n return {\n ...derivePalette(accent),\n glyphs: GLYPH_PRESETS[preset],\n };\n}\n\nexport { GLYPH_PRESETS, type GlyphPreset, type GlyphSet, TAPBACK_EMOJI } from \"./themes/glyphs.js\";\nexport { DEFAULT_ACCENT } from \"./themes/palette.js\";\n","import { Box, Text } from \"ink\";\nimport type { Message } from \"../../types.js\";\nimport { TAPBACK_EMOJI } from \"../theme.js\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\n\ninterface Props {\n message: Message;\n width: number;\n height: number;\n}\n\nfunction formatFullDate(date: Date): string {\n return date.toLocaleString(\"en-US\", {\n weekday: \"short\",\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hour12: true,\n });\n}\n\n/**\n * Drawer-row label. Wrapped in a flexShrink={0} Box so a sibling\n * `<Text wrap=\"truncate\">` value (e.g. GUID) doesn't squeeze the label\n * to zero width — without this, \"GUID: 9C768341...\" rendered as\n * \"GUID9C768341...\" because the truncating value won the row's width\n * negotiation and the label's colon+space got eaten.\n */\nfunction Label({ children }: { children: string }) {\n const theme = useTheme();\n return (\n <Box flexShrink={0}>\n <Text color={theme.drawer.label}>{children}: </Text>\n </Box>\n );\n}\n\nexport function MessageDrawer({ message: m, width, height }: Props) {\n const theme = useTheme();\n const hasAttachments = m.attachments && m.attachments.length > 0;\n\n return (\n <Box\n flexDirection=\"column\"\n width={width}\n height={height}\n borderStyle=\"single\"\n borderColor={theme.drawer.border}\n overflow=\"hidden\"\n >\n {/* Header */}\n <Box paddingX={1} backgroundColor={theme.header.focused.bg}>\n <Text color={theme.header.focused.fg} bold>\n Message Details\n </Text>\n </Box>\n\n <Box flexDirection=\"column\" paddingX={1} paddingY={0} gap={0}>\n {/* Sender */}\n <Box>\n <Label>From</Label>\n <Text color={theme.drawer.value}>{m.isFromMe ? \"Me\" : (m.displayName ?? m.handle)}</Text>\n </Box>\n\n {/* Handle */}\n {!m.isFromMe && m.displayName && (\n <Box>\n <Label>Handle</Label>\n <Text color={theme.drawer.value}>{m.handle}</Text>\n </Box>\n )}\n\n {/* Date */}\n <Box>\n <Label>Sent</Label>\n <Text color={theme.drawer.value}>{formatFullDate(m.date)}</Text>\n </Box>\n\n {/* Read/Delivered */}\n {m.dateDelivered && (\n <Box>\n <Label>Delivered</Label>\n <Text color={theme.drawer.value}>{formatFullDate(m.dateDelivered)}</Text>\n </Box>\n )}\n {m.dateRead && (\n <Box>\n <Label>Read</Label>\n <Text color={theme.drawer.value}>{formatFullDate(m.dateRead)}</Text>\n </Box>\n )}\n\n {/* Service */}\n <Box>\n <Label>Service</Label>\n <Text color={m.service === \"SMS\" ? theme.sms : theme.info.label}>{m.service}</Text>\n </Box>\n\n {/* Chat ID */}\n <Box>\n <Label>Chat</Label>\n <Text color={theme.drawer.value}>{m.chatId}</Text>\n </Box>\n\n {/* GUID */}\n <Box>\n <Label>GUID</Label>\n <Text color={theme.drawer.value} wrap=\"truncate\">\n {m.guid}\n </Text>\n </Box>\n\n {/* Edited */}\n {m.isEdited && (\n <Box>\n <Label>Status</Label>\n <Text color={theme.edited}>Edited</Text>\n </Box>\n )}\n\n {/* Reactions */}\n {m.reactions && m.reactions.length > 0 && (\n <Box flexDirection=\"column\">\n <Text color={theme.drawer.label}>Reactions:</Text>\n {m.reactions\n .filter((r) => !r.isRemoval)\n .map((r) => (\n <Box\n key={`${r.fromHandle}-${r.type}-${r.emoji ?? \"\"}-${r.targetMessageGuid}-${r.targetMessagePart}`}\n paddingLeft={1}\n >\n <Text color={theme.drawer.value}>\n {r.emoji ?? TAPBACK_EMOJI[r.type] ?? r.type} {r.fromHandle ?? \"unknown\"}\n </Text>\n </Box>\n ))}\n </Box>\n )}\n\n {/* Attachments */}\n {hasAttachments && (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text color={theme.attachment} bold>\n Attachments ({m.attachments!.length}):\n </Text>\n {m.attachments!.map((att) => {\n return (\n <Box\n key={`${att.filename}-${att.transferName ?? \"\"}-${att.mimeType ?? \"\"}-${att.totalBytes}`}\n flexDirection=\"column\"\n paddingLeft={1}\n >\n <Text color={theme.drawer.value} wrap=\"truncate\">\n {att.transferName ?? att.filename}\n </Text>\n <Text color={theme.drawer.label}>\n {att.mimeType ?? \"unknown\"} ·{\" \"}\n {att.totalBytes > 0 ? formatBytes(att.totalBytes) : \"?\"}\n <Text color={theme.senderName}> (press o to preview)</Text>\n </Text>\n </Box>\n );\n })}\n <Box marginTop={1}>\n <Text color={theme.help.key}>o</Text>\n <Text color={theme.help.desc}>: open attachment</Text>\n </Box>\n </Box>\n )}\n\n {/* Full message text */}\n <Box flexDirection=\"column\" marginTop={1}>\n <Text color={theme.drawer.label}>Full text:</Text>\n <Box borderStyle=\"single\" borderColor={theme.drawer.border} paddingX={1}>\n <Text color={theme.drawer.value} wrap=\"wrap\">\n {m.text ?? \"(no text)\"}\n </Text>\n </Box>\n </Box>\n\n {/* Reply context */}\n {m.isReply && m.replyTo && (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text color={theme.drawer.label}>Reply to:</Text>\n <Text color={theme.replyContext} italic wrap=\"wrap\">\n {m.replyTo.replyToText ?? \"(unknown)\"}\n </Text>\n </Box>\n )}\n </Box>\n\n {/* Footer hint */}\n <Box flexGrow={1} />\n <Box paddingX={1}>\n <Text color={theme.help.desc}>Esc/q to close</Text>\n </Box>\n </Box>\n );\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n}\n","/**\n * Send-via modal — pick an installed external chat app and launch its URL\n * scheme deep-link for the current thread's handle. Triggered by `S` in\n * thread pane (TUI handler in App.tsx).\n *\n * Renders a numbered list of installed apps. Press `1`–`9` to launch,\n * Esc to cancel. The TUI's mode-level `useInput` handler in App.tsx\n * actually does the launching; this component is presentation-only.\n */\nimport { Box, Text } from \"ink\";\nimport type { ChatAppDef } from \"../../url-schemes.js\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\n\ninterface Props {\n handle: string;\n apps: ChatAppDef[];\n}\n\nexport function SendViaModal({ handle, apps }: Props) {\n const theme = useTheme();\n // The modal renders as a sibling of the main body Box. Because the body\n // takes `flexGrow={1}` of the parent column, the modal gets only the\n // leftover vertical space — which can be less than its natural content\n // height. Without explicit anti-shrink the modal collapses rows together\n // (ate the FaceTime row, ate the 'H' of \"Handle\", merged \"WhatsApp\" into\n // \"SMS\", etc).\n // Two-pronged defense:\n // 1. `flexShrink={0}` on the outer modal so the modal owns its full\n // natural height — it pushes the body content up instead of being\n // compressed itself.\n // 2. `flexShrink={0}` on every row Box so individual rows can't get\n // collapsed even if their parent is space-constrained.\n // Plus an opaque backgroundColor so any overlap with body content paints\n // over instead of leaking through transparent regions.\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"double\"\n borderColor={theme.status.accent}\n backgroundColor={theme.header.dim.bg}\n paddingX={1}\n flexShrink={0}\n >\n <Box flexShrink={0}>\n <Text color={theme.status.accent} bold>\n Send via external app\n </Text>\n </Box>\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>Handle: {handle}</Text>\n </Box>\n {apps.length === 0 ? (\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>No compatible apps installed.</Text>\n </Box>\n ) : (\n apps.map((a, i) => (\n <Box key={a.name} flexShrink={0}>\n <Text color={theme.help.key}>{i + 1}</Text>\n <Text color={theme.help.desc}>\n {\": \"}\n {a.name}\n {a.supportsBody ? \"\" : \" (no body support)\"}\n </Text>\n </Box>\n ))\n )}\n <Box flexShrink={0}>\n <Text color={theme.help.desc}>1-9: launch · Esc: cancel</Text>\n </Box>\n </Box>\n );\n}\n","/**\n * Shared conversation-filter predicate. Used by Sidebar (rendering) and\n * App.tsx (Enter-to-jump-to-first-match handler).\n */\nimport type { Conversation } from \"../types.js\";\n\nexport function matchesConversationFilter(c: Conversation, queryLower: string): boolean {\n return (\n (c.displayName?.toLowerCase().includes(queryLower) ?? false) ||\n c.chatIdentifier.toLowerCase().includes(queryLower) ||\n c.threadSlug.toLowerCase().includes(queryLower)\n );\n}\n\n/**\n * Return the index of the first conversation matching `query` in the full\n * conversations array, or null if no match. Index is a position into the\n * ORIGINAL conversations array — not the filtered view — so it can be passed\n * to a SELECT action directly.\n */\nexport function firstFilterMatchIndex(conversations: Conversation[], query: string): number | null {\n const trimmed = query.trim();\n if (!trimmed) return null;\n const q = trimmed.toLowerCase();\n for (let i = 0; i < conversations.length; i++) {\n if (matchesConversationFilter(conversations[i]!, q)) return i;\n }\n return null;\n}\n","/**\n * Central keymap registry.\n *\n * Every action a user can take in the TUI is represented here as a `Command`,\n * even when it's also bound to a key directly inside `App.tsx`'s `useInput`.\n * The palette uses this list both for fuzzy search and (with an empty query)\n * as the complete keybinding cheat sheet.\n *\n * The reducer-level bindings in `App.tsx` still own actual key dispatch — this\n * file is the *description* of those bindings, plus a `run()` that can fire\n * them from the palette.\n */\nimport type { Dispatch } from \"react\";\nimport type { useImsg } from \"./hooks/useImsg.js\";\nimport type { Action, AppState } from \"./types.js\";\n\nexport interface CommandContext {\n state: AppState;\n dispatch: Dispatch<Action>;\n imsg: ReturnType<typeof useImsg>;\n}\n\nexport interface Command {\n /** Stable id (\"core.compose\", \"analytics.streaks\"). Used by tests + future keymap config. */\n id: string;\n /** Palette display title. */\n title: string;\n /** Optional dim description shown beside the title. */\n description?: string;\n /** Category for empty-query grouping (\"Navigation\", \"Compose\", \"Analytics\"…). */\n category: string;\n /** Display string for the keybinding column (\"c\", \"Ctrl-d\", \"gg\"). */\n keybinding?: string;\n /** Optional gate — when false the palette omits the entry. */\n when?: (state: AppState) => boolean;\n /** Invoked from the palette. */\n run: (ctx: CommandContext) => void | Promise<void>;\n}\n\nconst hasSelection = (s: AppState) =>\n s.selectedModuleIdx == null && s.conversations[s.selectedIdx] != null;\n\n/**\n * Core commands. The `run` bodies dispatch the same actions the inline\n * `useInput` handler in App.tsx dispatches. Many keys (movement, etc.) have no\n * sensible `run` from the palette — they're listed here purely so the palette\n * shows them as keybindings. Their `run` is a no-op.\n */\nexport const CORE_COMMANDS: Command[] = [\n // ── Navigation ─────────────────────────────────────────────────────────\n {\n id: \"core.move.down\",\n title: \"Move down\",\n category: \"Navigation\",\n keybinding: \"j / ↓\",\n run: () => {},\n },\n {\n id: \"core.move.up\",\n title: \"Move up\",\n category: \"Navigation\",\n keybinding: \"k / ↑\",\n run: () => {},\n },\n {\n id: \"core.move.count\",\n title: \"Jump N rows\",\n category: \"Navigation\",\n keybinding: \"#j / #k\",\n description: \"Vim-style count prefix\",\n run: () => {},\n },\n {\n id: \"core.move.top\",\n title: \"Go to top\",\n category: \"Navigation\",\n keybinding: \"gg\",\n run: () => {},\n },\n {\n id: \"core.move.bottom\",\n title: \"Go to bottom\",\n category: \"Navigation\",\n keybinding: \"G\",\n run: () => {},\n },\n {\n id: \"core.move.halfdown\",\n title: \"Half page down\",\n category: \"Navigation\",\n keybinding: \"Ctrl-d\",\n run: () => {},\n },\n {\n id: \"core.move.halfup\",\n title: \"Half page up\",\n category: \"Navigation\",\n keybinding: \"Ctrl-u\",\n run: () => {},\n },\n {\n id: \"core.move.pagedown\",\n title: \"Page down\",\n category: \"Navigation\",\n keybinding: \"Ctrl-f / PgDn\",\n run: () => {},\n },\n {\n id: \"core.move.pageup\",\n title: \"Page up\",\n category: \"Navigation\",\n keybinding: \"Ctrl-b / PgUp\",\n run: () => {},\n },\n {\n id: \"core.move.high\",\n title: \"First visible row\",\n category: \"Navigation\",\n keybinding: \"H\",\n run: () => {},\n },\n {\n id: \"core.move.middle\",\n title: \"Middle visible row\",\n category: \"Navigation\",\n keybinding: \"M\",\n run: () => {},\n },\n {\n id: \"core.move.low\",\n title: \"Last visible row\",\n category: \"Navigation\",\n keybinding: \"L\",\n run: () => {},\n },\n {\n id: \"core.move.groupnext\",\n title: \"Next sender group\",\n category: \"Navigation\",\n keybinding: \"} / ]\",\n description: \"Jump to next sender boundary\",\n run: () => {},\n },\n {\n id: \"core.move.groupprev\",\n title: \"Previous sender group\",\n category: \"Navigation\",\n keybinding: \"{ / [\",\n run: () => {},\n },\n {\n id: \"core.focus.toggle\",\n title: \"Switch pane (sidebar ↔ thread)\",\n category: \"Navigation\",\n keybinding: \"Tab\",\n run: ({ state, dispatch }) =>\n dispatch({ type: \"FOCUS\", pane: state.focus === \"sidebar\" ? \"thread\" : \"sidebar\" }),\n },\n\n // ── Conversations ──────────────────────────────────────────────────────\n {\n id: \"core.filter\",\n title: \"Filter conversations\",\n category: \"Conversations\",\n keybinding: \"/\",\n run: ({ dispatch }) => dispatch({ type: \"ENTER_FILTER\" }),\n },\n {\n id: \"core.copy.slug\",\n title: \"Copy thread slug\",\n category: \"Conversations\",\n keybinding: \"y\",\n description: \"Sidebar focus — copies ~slug to clipboard\",\n run: () => {},\n },\n {\n id: \"core.refresh\",\n title: \"Refresh conversations\",\n category: \"Conversations\",\n keybinding: \"r\",\n run: ({ dispatch }) => dispatch({ type: \"SET_STATUS\", status: \"Refreshing…\" }),\n },\n\n // ── Compose / send ─────────────────────────────────────────────────────\n {\n id: \"core.compose\",\n title: \"Compose in current thread\",\n category: \"Compose\",\n keybinding: \"c\",\n when: hasSelection,\n run: ({ dispatch }) => dispatch({ type: \"ENTER_COMPOSE\" }),\n },\n {\n id: \"core.compose.new\",\n title: \"New conversation\",\n category: \"Compose\",\n keybinding: \"N\",\n description: \"Compose to a new recipient\",\n run: ({ dispatch }) => dispatch({ type: \"ENTER_COMPOSE_NEW\" }),\n },\n {\n id: \"core.send.via\",\n title: \"Send via external app\",\n category: \"Compose\",\n keybinding: \"S\",\n description: \"Pick an installed chat app and deep-link to this thread\",\n when: hasSelection,\n run: ({ dispatch }) => dispatch({ type: \"ENTER_SEND_VIA\" }),\n },\n {\n id: \"core.open.messages\",\n title: \"Open in Messages.app\",\n category: \"Compose\",\n keybinding: \"O\",\n when: hasSelection,\n run: () => {},\n },\n\n // ── Thread actions ─────────────────────────────────────────────────────\n {\n id: \"core.details\",\n title: \"Open message details drawer\",\n category: \"Thread\",\n keybinding: \"Enter\",\n run: ({ dispatch }) => dispatch({ type: \"OPEN_DRAWER\" }),\n },\n {\n id: \"core.attachment.open\",\n title: \"Open attachment\",\n category: \"Thread\",\n keybinding: \"o\",\n description: \"Images → Quick Look, video → mpv\",\n run: () => {},\n },\n {\n id: \"core.select.start\",\n title: \"Visual select messages\",\n category: \"Thread\",\n keybinding: \"V\",\n run: ({ dispatch }) => dispatch({ type: \"ENTER_SELECT_MODE\" }),\n },\n {\n id: \"core.date.jump\",\n title: \"Jump to date\",\n category: \"Thread\",\n keybinding: \":\",\n run: ({ dispatch }) => dispatch({ type: \"ENTER_DATE_JUMP\" }),\n },\n {\n id: \"core.copy.text\",\n title: \"Copy selected messages\",\n category: \"Thread\",\n keybinding: \"y\",\n description: \"Visual select mode — copies text to clipboard\",\n run: () => {},\n },\n {\n id: \"core.export\",\n title: \"Export selection / thread\",\n category: \"Thread\",\n keybinding: \"e\",\n description: \"From visual select mode\",\n run: () => {},\n },\n\n // ── App ────────────────────────────────────────────────────────────────\n {\n id: \"core.devstats\",\n title: \"Toggle dev stats\",\n category: \"App\",\n keybinding: \"d\",\n description: \"Engine, perf, memory\",\n run: ({ dispatch }) => dispatch({ type: \"TOGGLE_DEV_STATS\" }),\n },\n {\n id: \"core.palette\",\n title: \"Command palette\",\n category: \"App\",\n keybinding: \"Ctrl-P / ?\",\n description: \"This window\",\n run: ({ dispatch }) => dispatch({ type: \"OPEN_PALETTE\" }),\n },\n { id: \"core.quit\", title: \"Quit\", category: \"App\", keybinding: \"q / Ctrl-C\", run: () => {} },\n];\n","/**\n * Analytics pane — renders one of six analytic types over a chosen time\n * window, computed against the unified `dispatchAnalytic` from\n * `src/analytics.ts`. Owns its own keyboard handlers via `useInput`.\n */\nimport { Box, Text, useInput } from \"ink\";\nimport { useEffect, useState } from \"react\";\nimport type { AnalyticType } from \"../../../analytics.js\";\nimport {\n type DoubleTextResult,\n dispatchAnalytic,\n type HeatmapResult,\n IMPLEMENTED_TYPES,\n type ResponseTimeStats,\n type StreakResult,\n type TapbackResult,\n type WrappedResult,\n} from \"../../../analytics.js\";\nimport type { Message } from \"../../../types.js\";\nimport { useTheme } from \"../../themes/ThemeContext.js\";\nimport type { ModulePaneProps } from \"../types.js\";\nimport type { AnalyticsRange, AnalyticsState } from \"./module.js\";\n\nexport const ANALYTIC_LABEL: Record<AnalyticType, string> = {\n messaging_streaks: \"Messaging Streaks\",\n double_texts: \"Double Texts\",\n response_time_stats: \"Response Times\",\n daily_heatmap: \"Daily Heatmap\",\n tapback_summary: \"Tapback Summary\",\n year_in_review_wrapped: \"Year in Review (Wrapped)\",\n};\n\nconst RANGE_LABEL: Record<AnalyticsRange, string> = {\n \"30d\": \"Last 30 days\",\n \"90d\": \"Last 90 days\",\n \"1y\": \"Last year\",\n all: \"All time\",\n};\n\nconst RANGE_ORDER: AnalyticsRange[] = [\"30d\", \"90d\", \"1y\", \"all\"];\n\n/** Convert a range key to a cutoff Date. `\"all\"` returns the Unix epoch. */\nfunction rangeCutoff(range: AnalyticsRange): Date {\n const now = Date.now();\n switch (range) {\n case \"30d\":\n return new Date(now - 30 * 86_400_000);\n case \"90d\":\n return new Date(now - 90 * 86_400_000);\n case \"1y\":\n return new Date(now - 365 * 86_400_000);\n case \"all\":\n return new Date(0);\n }\n}\n\nexport function AnalyticsPane({\n instance,\n imsg,\n width,\n height,\n focused,\n onUpdateState,\n onClose,\n setStatus,\n}: ModulePaneProps) {\n const theme = useTheme();\n const state = instance.state as AnalyticsState;\n const [messages, setMessages] = useState<Message[] | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [result, setResult] = useState<{ data: unknown } | null>(null);\n\n // Load messages whenever the range cutoff changes.\n useEffect(() => {\n let cancelled = false;\n setLoading(true);\n setError(null);\n setResult(null);\n const cutoff = rangeCutoff(state.range);\n imsg\n .loadMessagesInWindow(cutoff)\n .then((m) => {\n if (cancelled) return;\n setMessages(m);\n })\n .catch((e) => {\n if (cancelled) return;\n setError(e instanceof Error ? e.message : String(e));\n setLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [state.range, imsg]);\n\n // Compute the analytic OUT of the render path. `dispatchAnalytic` on\n // 30 days of messages takes 1-3 seconds — running it in a useMemo blocked\n // the render so \"Loading…\" never appeared and the pane sat empty during\n // the wait. Deferring via `setTimeout(0)` lets React paint loading state\n // before the heavy compute starts on the next macrotask.\n useEffect(() => {\n if (!messages) return;\n let cancelled = false;\n setLoading(true);\n const handle = setTimeout(() => {\n if (cancelled) return;\n try {\n const { data } = dispatchAnalytic(state.type, messages);\n setResult({ data });\n setError(null);\n } catch (e) {\n setError(e instanceof Error ? e.message : String(e));\n setResult(null);\n }\n setLoading(false);\n }, 0);\n return () => {\n cancelled = true;\n clearTimeout(handle);\n };\n }, [messages, state.type]);\n\n // Keyboard: Tab cycles analytic type, [/] cycles range, Esc closes.\n useInput(\n (input, key) => {\n if (key.escape) {\n onClose();\n return;\n }\n if (key.tab) {\n const cur = IMPLEMENTED_TYPES.indexOf(state.type);\n const next = IMPLEMENTED_TYPES[(cur + 1) % IMPLEMENTED_TYPES.length]!;\n onUpdateState({ ...state, type: next });\n setStatus(`Analytics: ${ANALYTIC_LABEL[next]}`);\n return;\n }\n if (input === \"]\" || input === \">\") {\n const cur = RANGE_ORDER.indexOf(state.range);\n const next = RANGE_ORDER[(cur + 1) % RANGE_ORDER.length]!;\n onUpdateState({ ...state, range: next });\n return;\n }\n if (input === \"[\" || input === \"<\") {\n const cur = RANGE_ORDER.indexOf(state.range);\n const next = RANGE_ORDER[(cur - 1 + RANGE_ORDER.length) % RANGE_ORDER.length]!;\n onUpdateState({ ...state, range: next });\n return;\n }\n },\n { isActive: focused },\n );\n\n return (\n <Box\n flexDirection=\"column\"\n width={width}\n height={height}\n borderStyle=\"single\"\n borderColor={focused ? theme.status.accent : theme.border}\n overflow=\"hidden\"\n >\n <Box\n paddingX={1}\n backgroundColor={focused ? theme.header.focused.bg : theme.header.dim.bg}\n flexShrink={0}\n >\n <Text color={focused ? theme.header.focused.fg : theme.header.dim.fg} bold={focused}>\n {ANALYTIC_LABEL[state.type]}\n </Text>\n <Text color={theme.help.desc}> · {RANGE_LABEL[state.range]}</Text>\n </Box>\n\n <Box flexDirection=\"column\" flexGrow={1} paddingX={1} overflow=\"hidden\">\n {loading && <Text color={theme.help.desc}>Loading…</Text>}\n {error && <Text color={theme.edited}>Error: {error}</Text>}\n {!loading && !error && result && (\n <AnalyticsResultView\n type={state.type}\n data={result.data}\n messages={messages ?? []}\n width={width - 2}\n height={height - 4}\n resolveNames={imsg.resolveNames}\n />\n )}\n </Box>\n\n <Box paddingX={1} height={1} flexShrink={0}>\n <Text color={theme.help.key}>Tab</Text>\n <Text color={theme.help.desc}>:type </Text>\n <Text color={theme.help.key}>[ ]</Text>\n <Text color={theme.help.desc}>:range </Text>\n <Text color={theme.help.key}>Esc</Text>\n <Text color={theme.help.desc}>:close</Text>\n </Box>\n </Box>\n );\n}\n\n// ── Result renderers ────────────────────────────────────────────────────\n\ninterface ResultViewProps {\n type: AnalyticType;\n data: unknown;\n messages: Message[];\n width: number;\n height: number;\n resolveNames: (handles: string[]) => string[];\n}\n\nfunction AnalyticsResultView({\n type,\n data,\n messages,\n width,\n height,\n resolveNames,\n}: ResultViewProps) {\n if (data == null) return <NoData />;\n switch (type) {\n case \"messaging_streaks\":\n return <StreaksView rows={data as StreakResult[]} height={height} />;\n case \"double_texts\":\n return <DoubleTextsView rows={data as DoubleTextResult[]} height={height} />;\n case \"response_time_stats\":\n return <ResponseTimesView rows={data as ResponseTimeStats[]} height={height} />;\n case \"daily_heatmap\":\n return <HeatmapView result={data as HeatmapResult} width={width} />;\n case \"tapback_summary\":\n return <TapbacksView rows={data as TapbackResult[]} height={height} />;\n case \"year_in_review_wrapped\":\n return <WrappedView result={data as WrappedResult} />;\n }\n void messages;\n void resolveNames;\n}\n\nfunction NoData() {\n const theme = useTheme();\n return <Text color={theme.help.desc}>No data in this window.</Text>;\n}\n\nfunction StreaksView({ rows, height }: { rows: StreakResult[]; height: number }) {\n const theme = useTheme();\n if (rows.length === 0) return <NoData />;\n const visible = rows.slice(0, Math.max(1, height - 2));\n return (\n <Box flexDirection=\"column\">\n <Box>\n <Text color={theme.info.label}>{\"Contact\".padEnd(28)}</Text>\n <Text color={theme.info.label}>{\"Longest\".padStart(10)}</Text>\n <Text color={theme.info.label}>{\"Current\".padStart(10)}</Text>\n <Text color={theme.info.label}>{\" Start..End\".padEnd(30)}</Text>\n </Box>\n {visible.map((r) => (\n <Box key={r.contact}>\n <Text wrap=\"truncate\">{trunc(r.contact, 27).padEnd(28)}</Text>\n <Text color={theme.status.accent}>{`${r.longestStreakDays}d`.padStart(10)}</Text>\n <Text>{`${r.currentStreakDays}d`.padStart(10)}</Text>\n <Text color={theme.help.desc}>\n {\" \"}\n {r.longestStreakStart ?? \"—\"}..{r.longestStreakEnd ?? \"—\"}\n </Text>\n </Box>\n ))}\n </Box>\n );\n}\n\nfunction DoubleTextsView({ rows, height }: { rows: DoubleTextResult[]; height: number }) {\n const theme = useTheme();\n if (rows.length === 0) return <NoData />;\n const visible = rows.slice(0, Math.max(1, height - 2));\n return (\n <Box flexDirection=\"column\">\n <Box>\n <Text color={theme.info.label}>{\"Contact\".padEnd(28)}</Text>\n <Text color={theme.info.label}>{\"From me\".padStart(10)}</Text>\n <Text color={theme.info.label}>{\"From them\".padStart(12)}</Text>\n </Box>\n {visible.map((r) => (\n <Box key={r.contact}>\n <Text wrap=\"truncate\">{trunc(r.contact, 27).padEnd(28)}</Text>\n <Text color={theme.status.accent}>{String(r.doubleTextsFromMe).padStart(10)}</Text>\n <Text>{String(r.doubleTextsFromThem).padStart(12)}</Text>\n </Box>\n ))}\n </Box>\n );\n}\n\nfunction ResponseTimesView({ rows, height }: { rows: ResponseTimeStats[]; height: number }) {\n const theme = useTheme();\n if (rows.length === 0) return <NoData />;\n const visible = rows.slice(0, Math.max(1, height - 2));\n return (\n <Box flexDirection=\"column\">\n <Box>\n <Text color={theme.info.label}>{\"Contact\".padEnd(28)}</Text>\n <Text color={theme.info.label}>{\"Count\".padStart(8)}</Text>\n <Text color={theme.info.label}>{\"Median\".padStart(12)}</Text>\n <Text color={theme.info.label}>{\"p95\".padStart(12)}</Text>\n <Text color={theme.info.label}>{\"Mean\".padStart(12)}</Text>\n </Box>\n {visible.map((r) => (\n <Box key={r.contact}>\n <Text wrap=\"truncate\">{trunc(r.contact, 27).padEnd(28)}</Text>\n <Text>{String(r.count).padStart(8)}</Text>\n <Text color={theme.status.accent}>{formatDuration(r.medianMs).padStart(12)}</Text>\n <Text>{formatDuration(r.p95Ms).padStart(12)}</Text>\n <Text>{formatDuration(r.meanMs).padStart(12)}</Text>\n </Box>\n ))}\n </Box>\n );\n}\n\nconst HEATMAP_SHADES = [\" \", \"·\", \"░\", \"▒\", \"▓\", \"█\"];\nconst DOW_LABELS = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\nconst HOURS_24 = [\n 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,\n] as const;\n\nfunction HeatmapView({ result, width }: { result: HeatmapResult; width: number }) {\n const theme = useTheme();\n const grid = result.grid;\n let peak = 0;\n for (const row of grid) for (const cell of row) if (cell > peak) peak = cell;\n\n // Each hour gets 2 cells of width (so the grid is readable on narrow panes).\n const cellW = width >= 24 * 2 + 6 ? 2 : 1;\n\n return (\n <Box flexDirection=\"column\">\n <Box>\n <Text color={theme.info.label}>{\" \"}</Text>\n {HOURS_24.map((h) => (\n <Text key={`h-${h}`} color={theme.help.desc}>\n {h % 6 === 0 ? `${h}`.padStart(cellW) : \"\".padStart(cellW, \" \")}\n </Text>\n ))}\n </Box>\n {DOW_LABELS.map((label, dow) => {\n const row = grid[dow] ?? [];\n return (\n <Box key={label}>\n <Text color={theme.info.label}>{label} </Text>\n {HOURS_24.map((h) => {\n const count = row[h] ?? 0;\n const ratio = peak > 0 ? count / peak : 0;\n const idx = Math.min(\n HEATMAP_SHADES.length - 1,\n Math.round(ratio * (HEATMAP_SHADES.length - 1)),\n );\n const shade = HEATMAP_SHADES[idx]!;\n return (\n <Text\n key={`c-${label}-${h}`}\n color={ratio > 0.66 ? theme.status.accent : undefined}\n >\n {shade.repeat(cellW)}\n </Text>\n );\n })}\n </Box>\n );\n })}\n <Box marginTop={1}>\n <Text color={theme.help.desc}>\n {result.total.toLocaleString()} messages · peak hour count: {peak}\n </Text>\n </Box>\n </Box>\n );\n}\n\nfunction TapbacksView({ rows, height }: { rows: TapbackResult[]; height: number }) {\n const theme = useTheme();\n if (rows.length === 0) return <NoData />;\n const visible = rows.slice(0, Math.max(1, height - 2));\n return (\n <Box flexDirection=\"column\">\n <Box>\n <Text color={theme.info.label}>{\"Contact\".padEnd(28)}</Text>\n <Text color={theme.info.label}>{\"❤\".padStart(4)}</Text>\n <Text color={theme.info.label}>{\"👍\".padStart(4)}</Text>\n <Text color={theme.info.label}>{\"👎\".padStart(4)}</Text>\n <Text color={theme.info.label}>{\"ha\".padStart(4)}</Text>\n <Text color={theme.info.label}>{\"‼\".padStart(4)}</Text>\n <Text color={theme.info.label}>{\"?\".padStart(4)}</Text>\n <Text color={theme.info.label}>{\"😀\".padStart(4)}</Text>\n <Text color={theme.info.label}>{\"Σ\".padStart(6)}</Text>\n </Box>\n {visible.map((r) => (\n <Box key={r.contact}>\n <Text wrap=\"truncate\">{trunc(r.contact, 27).padEnd(28)}</Text>\n <Text>{String(r.heart).padStart(4)}</Text>\n <Text>{String(r.thumbsUp).padStart(4)}</Text>\n <Text>{String(r.thumbsDown).padStart(4)}</Text>\n <Text>{String(r.haha).padStart(4)}</Text>\n <Text>{String(r.exclaim).padStart(4)}</Text>\n <Text>{String(r.question).padStart(4)}</Text>\n <Text>{String(r.emoji).padStart(4)}</Text>\n <Text color={theme.status.accent}>{String(r.total).padStart(6)}</Text>\n </Box>\n ))}\n </Box>\n );\n}\n\nfunction WrappedView({ result }: { result: WrappedResult }) {\n const theme = useTheme();\n return (\n <Box flexDirection=\"column\">\n <Box>\n <Text color={theme.help.desc}>\n {result.windowStart || \"—\"} → {result.windowEnd || \"—\"}\n </Text>\n </Box>\n <Box marginTop={1} gap={2}>\n <KV label=\"Sent\" value={result.totalSent.toLocaleString()} />\n <KV label=\"Received\" value={result.totalReceived.toLocaleString()} />\n <KV label=\"Reactions\" value={result.totalReactions.toLocaleString()} />\n </Box>\n <Box marginTop={1} flexDirection=\"column\">\n <Text color={theme.info.label}>Top contacts</Text>\n {result.topContacts.length === 0 ? (\n <Text color={theme.help.desc}>(none)</Text>\n ) : (\n result.topContacts.map((c) => (\n <Box key={c.contact}>\n <Text wrap=\"truncate\">{trunc(c.contact, 28).padEnd(30)}</Text>\n <Text color={theme.status.accent}>{String(c.total).padStart(6)}</Text>\n <Text color={theme.help.desc}>\n {\" \"}sent {c.sent} / received {c.received}\n </Text>\n </Box>\n ))\n )}\n </Box>\n <Box marginTop={1} flexDirection=\"column\">\n <KV\n label=\"Peak day\"\n value={result.peakDay ? `${result.peakDay.date} (${result.peakDay.count})` : \"—\"}\n />\n <KV\n label=\"Longest streak\"\n value={`${result.longestStreakDays}d${result.longestStreakContact ? ` with ${result.longestStreakContact}` : \"\"}`}\n />\n </Box>\n </Box>\n );\n}\n\nfunction KV({ label, value }: { label: string; value: string }) {\n const theme = useTheme();\n return (\n <Box>\n <Text color={theme.info.label}>{label}: </Text>\n <Text color={theme.info.value}>{value}</Text>\n </Box>\n );\n}\n\nfunction trunc(s: string, max: number): string {\n if (s.length <= max) return s;\n return `${s.slice(0, Math.max(0, max - 1))}…`;\n}\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`;\n if (ms < 3_600_000) return `${(ms / 60_000).toFixed(1)}m`;\n if (ms < 86_400_000) return `${(ms / 3_600_000).toFixed(1)}h`;\n return `${(ms / 86_400_000).toFixed(1)}d`;\n}\n","/**\n * Sidebar row for an analytics module instance. Matches the visual rhythm of\n * ConversationItem (3 content rows + separator) so cursor math in App.tsx\n * works without a per-module height table.\n */\nimport { Box, Text } from \"ink\";\nimport { useTheme } from \"../../themes/ThemeContext.js\";\nimport type { ModuleSidebarItemProps } from \"../types.js\";\nimport type { AnalyticsState } from \"./module.js\";\nimport { ANALYTIC_LABEL } from \"./Pane.js\";\n\nexport function AnalyticsSidebarItem({\n instance,\n selected,\n focused,\n width,\n lineNum,\n isLast,\n}: ModuleSidebarItemProps) {\n const theme = useTheme();\n const state = instance.state as AnalyticsState;\n const accent = instance.accentColor ?? theme.status.accent;\n\n return (\n <Box flexDirection=\"column\" width={width}>\n <Box\n flexDirection=\"column\"\n width={width}\n backgroundColor={selected ? theme.sidebar.selected : undefined}\n >\n {/* Title row */}\n <Box width={width}>\n {lineNum !== undefined && (\n <Text color={selected && focused ? theme.sent.bg : theme.lineNum}>\n {lineNum.padStart(3)}{\" \"}\n </Text>\n )}\n <Text color={selected && focused ? theme.sent.bg : accent}>\n {selected && focused ? \"▸\" : \"✦\"}\n </Text>\n <Text color={selected ? theme.sidebar.selectedFg : accent} bold>\n {\" \"}\n {ANALYTIC_LABEL[state.type]}\n </Text>\n </Box>\n\n {/* Subtitle row */}\n <Box width={width} paddingLeft={5}>\n <Text color={theme.sidebar.snippet} wrap=\"truncate\">\n range: {state.range}\n </Text>\n </Box>\n\n {/* Module-id row — matches slug position */}\n <Box\n width={width}\n justifyContent=\"flex-end\"\n paddingRight={1}\n backgroundColor={theme.sidebar.slugBg}\n >\n <Text color={theme.sidebar.slug} dimColor italic>\n ✦{instance.moduleId}\n </Text>\n </Box>\n </Box>\n\n {!isLast && (\n <Box paddingX={1}>\n <Text color={theme.sidebar.separator} dimColor>\n {theme.glyphs.separator.repeat(Math.max(width - 4, 1))}\n </Text>\n </Box>\n )}\n </Box>\n );\n}\n","/**\n * Analytics module — surfaces the six implemented analytics from\n * `src/analytics.ts` as palette commands. Each command opens a virtual\n * sidebar row whose pane lets the user flip between analytic types and\n * date ranges without leaving the row.\n */\nimport type { AnalyticType } from \"../../../analytics.js\";\nimport { IMPLEMENTED_TYPES } from \"../../../analytics.js\";\nimport type { FeatureModule, ModuleCommand, ModuleInstance } from \"../types.js\";\nimport { ANALYTIC_LABEL, AnalyticsPane } from \"./Pane.js\";\nimport { AnalyticsSidebarItem } from \"./SidebarItem.js\";\n\nexport type AnalyticsRange = \"30d\" | \"90d\" | \"1y\" | \"all\";\n\nexport interface AnalyticsState {\n type: AnalyticType;\n range: AnalyticsRange;\n /** When set, the pane scopes its dataset to that chat identifier. */\n chatIdentifier: string | null;\n}\n\nexport const ANALYTIC_DESCRIPTION: Record<AnalyticType, string> = {\n messaging_streaks: \"Longest and current daily-message streaks per contact\",\n double_texts: \"Consecutive messages without a reply\",\n response_time_stats: \"Reply latency (median, p95, mean)\",\n daily_heatmap: \"7×24 grid of activity by weekday × hour\",\n tapback_summary: \"Tapback reactions sent per contact\",\n year_in_review_wrapped: \"Wrapped summary: top contacts, peak day, totals\",\n};\n\nlet instanceCounter = 0;\n\nfunction makeInstance(type: AnalyticType): ModuleInstance {\n instanceCounter += 1;\n const state: AnalyticsState = { type, range: \"30d\", chatIdentifier: null };\n return {\n id: `analytics.${type}-${Date.now()}-${instanceCounter}`,\n moduleId: \"analytics\",\n title: `Analytics: ${ANALYTIC_LABEL[type]}`,\n subtitle: \"range: 30d\",\n state,\n };\n}\n\nconst commands: ModuleCommand[] = IMPLEMENTED_TYPES.map((type) => ({\n id: `analytics.${type}`,\n title: `Analytics: ${ANALYTIC_LABEL[type]}`,\n description: ANALYTIC_DESCRIPTION[type],\n open: () => makeInstance(type),\n}));\n\nexport const analyticsModule: FeatureModule = {\n id: \"analytics\",\n name: \"Analytics\",\n commands,\n SidebarItem: AnalyticsSidebarItem,\n Pane: AnalyticsPane,\n};\n","/**\n * Static registry of feature modules. To add a new module, drop its folder\n * under `src/tui/modules/<id>/`, export a `FeatureModule`, and append it\n * here. The palette + sidebar pick it up automatically.\n */\nimport type { Command } from \"../keymap.js\";\nimport { CORE_COMMANDS } from \"../keymap.js\";\nimport type { AppState } from \"../types.js\";\nimport { analyticsModule } from \"./analytics/module.js\";\nimport type { FeatureModule, ModuleCommand } from \"./types.js\";\n\nexport const MODULES: FeatureModule[] = [analyticsModule];\n\n/** Look up a module by id. Returns undefined for unknown ids. */\nexport function findModule(moduleId: string): FeatureModule | undefined {\n return MODULES.find((m) => m.id === moduleId);\n}\n\n/** Lift a `ModuleCommand` into the unified `Command` shape used by the palette. */\nfunction liftModuleCommand(mod: FeatureModule, cmd: ModuleCommand): Command {\n return {\n id: cmd.id,\n title: cmd.title,\n description: cmd.description,\n category: mod.name,\n keybinding: cmd.keybinding,\n run: (ctx) => {\n const inst = cmd.open(ctx);\n if (inst) ctx.dispatch({ type: \"OPEN_MODULE_INSTANCE\", instance: inst });\n },\n };\n}\n\n/** Concatenate core commands with every registered module's commands. */\nexport function allCommands(state: AppState): Command[] {\n const out: Command[] = [];\n for (const c of CORE_COMMANDS) {\n if (c.when && !c.when(state)) continue;\n out.push(c);\n }\n for (const mod of MODULES) {\n for (const cmd of mod.commands) {\n out.push(liftModuleCommand(mod, cmd));\n }\n }\n return out;\n}\n","/**\n * Grapheme-aware visual width and truncation helpers.\n *\n * The sidebar `ConversationItem` was truncating by `String#length`, which\n * counts UTF-16 code units. That splits surrogate pairs (one emoji is two\n * code units) and produces broken glyphs in the terminal. It also ignores\n * the fact that an emoji takes two terminal cells, not one.\n *\n * Approach:\n * - Walk the string with `Intl.Segmenter` (granularity: \"grapheme\") so we\n * never split inside a grapheme cluster (single emoji, family ZWJ, flag).\n * - Approximate each cluster's terminal cell width with a fast range\n * check — emoji, CJK, fullwidth and regional indicators are 2 cells;\n * everything else (incl. ASCII, Latin diacritics, combining marks) is 1.\n *\n * Width approximation is coarse but covers every real-world iMessage handle\n * we care about (emoji-in-display-name is the dominant failure mode).\n */\n\nconst SEG = new Intl.Segmenter(\"en\", { granularity: \"grapheme\" });\n\n/**\n * Returns the approximate width in monospaced terminal cells of a single\n * grapheme cluster. 2 for emoji / CJK / fullwidth, 1 otherwise.\n */\nexport function clusterWidth(cluster: string): number {\n // ASCII fast path — single-byte and 1-cell.\n if (cluster.length === 1) {\n const code = cluster.charCodeAt(0);\n if (code < 0x80) return 1;\n }\n for (const ch of cluster) {\n const cp = ch.codePointAt(0);\n if (cp === undefined) continue;\n // Note: the 0x2600..0x27BF \"misc symbols + dingbats\" range (▶ ◀ ● ✉\n // ✓ ✗ ★ etc) is *ambiguous-width* per UAX #11 but monospaced\n // terminals render it as single-cell — we leave it at 1 to match\n // what the TUI's `safe` theme actually paints.\n if (\n // CJK Unified Ideographs + Compatibility — wide.\n (cp >= 0x3000 && cp <= 0x9fff) ||\n // Hangul Syllables.\n (cp >= 0xac00 && cp <= 0xd7a3) ||\n // CJK Compatibility Ideographs.\n (cp >= 0xf900 && cp <= 0xfaff) ||\n // Fullwidth forms.\n (cp >= 0xff00 && cp <= 0xff60) ||\n // Halfwidth / fullwidth fence range (still 2-cell for fullwidth).\n (cp >= 0xffe0 && cp <= 0xffe6) ||\n // Misc symbols and pictographs — the true emoji plane.\n cp >= 0x1f300\n ) {\n return 2;\n }\n }\n return 1;\n}\n\n/** Total monospaced cell width of a string. */\nexport function visualWidth(str: string): number {\n let w = 0;\n for (const { segment } of SEG.segment(str)) {\n w += clusterWidth(segment);\n }\n return w;\n}\n\n/**\n * Truncates `str` so that the returned string (including any ellipsis if\n * applied) fits within `maxCols` terminal cells. Never splits inside a\n * grapheme cluster. If `maxCols <= 0`, returns \"\".\n */\nexport function truncateToWidth(str: string, maxCols: number, ellipsis = \"…\"): string {\n if (maxCols <= 0) return \"\";\n if (visualWidth(str) <= maxCols) return str;\n\n const ellipsisW = visualWidth(ellipsis);\n // If the ellipsis itself doesn't fit, take whatever clusters do and\n // skip the ellipsis — better than returning \"\" or a clipped ellipsis.\n if (ellipsisW >= maxCols) {\n let usedFallback = 0;\n let outFallback = \"\";\n for (const { segment } of SEG.segment(str)) {\n const w = clusterWidth(segment);\n if (usedFallback + w > maxCols) break;\n outFallback += segment;\n usedFallback += w;\n }\n return outFallback;\n }\n\n let used = 0;\n let out = \"\";\n for (const { segment } of SEG.segment(str)) {\n const w = clusterWidth(segment);\n if (used + w + ellipsisW > maxCols) break;\n out += segment;\n used += w;\n }\n return out + ellipsis;\n}\n","import { Box, Text } from \"ink\";\nimport type { Conversation } from \"../../types.js\";\nimport { truncateToWidth } from \"../../visual-width.js\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\n\nfunction relativeDate(date: Date | null): string {\n if (!date) return \"\";\n const now = new Date();\n const time = date.toLocaleTimeString(\"en-US\", {\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true,\n });\n if (date.toDateString() === now.toDateString()) return time;\n const y = new Date(now);\n y.setDate(now.getDate() - 1);\n if (date.toDateString() === y.toDateString()) return \"Yest\";\n return `${date.getMonth() + 1}/${date.getDate()}`;\n}\n\ninterface Props {\n conversation: Conversation;\n selected: boolean;\n width: number;\n lineNum?: string;\n focused?: boolean;\n isLast?: boolean;\n}\n\n/**\n * Width budget for the conversation row. Returning a precise budget for the\n * name text prevents the time/icons from wrapping onto the next row and\n * obscuring the snippet below — the original symptom of the layout bug.\n *\n * Layout (single row):\n * [lineNum 4][cursor 2][envelope 2?][group 2?][NAME ...truncated...] (count?) icon time\n * ^^^^^^^^^^^^^^^^^^^^\n * this is what we budget\n */\nfunction nameBudget(\n width: number,\n hasUnread: boolean,\n unreadCount: number,\n isGroup: boolean,\n): number {\n const lineNumW = 4; // \"123 \"\n const cursorW = 2; // \"▸ \" or \" \"\n const envelopeW = hasUnread ? 2 : 0;\n const groupW = isGroup ? 2 : 0;\n const countW = hasUnread ? ` (${unreadCount})`.length : 0;\n const iconW = 2; // \" 💬\"\n const timeW = 9; // max \" 12:34 PM\"\n const padding = 2; // safety margin\n return Math.max(\n width - lineNumW - cursorW - envelopeW - groupW - countW - iconW - timeW - padding,\n 8,\n );\n}\n\nexport function ConversationItem({\n conversation: c,\n selected,\n width,\n lineNum,\n focused,\n isLast,\n}: Props) {\n const theme = useTheme();\n const hasUnread = c.unreadCount > 0;\n const name = c.displayName ?? c.chatIdentifier;\n const time = relativeDate(c.lastMessageDate);\n const snippet = c.lastMessageSnippet ?? \"\";\n const serviceIcon = c.serviceType === \"SMS\" ? theme.glyphs.sms : theme.glyphs.iMessage;\n\n const nameW = nameBudget(width, hasUnread, c.unreadCount, c.isGroupChat);\n // Grapheme-aware truncation — never splits a surrogate pair (emoji)\n // and budgets each emoji as 2 terminal cells. See src/visual-width.ts.\n const truncatedName = truncateToWidth(name, nameW);\n const snippetW = Math.max(width - 6, 8); // -5 for left padding, -1 safety\n const truncatedSnippet = truncateToWidth(snippet, snippetW, \"\");\n\n return (\n <Box flexDirection=\"column\" width={width}>\n <Box\n flexDirection=\"column\"\n width={width}\n backgroundColor={selected ? theme.sidebar.selected : undefined}\n >\n {/* Name row — left+right split so time can't wrap */}\n <Box width={width} justifyContent=\"space-between\">\n {/* Left side */}\n <Box flexShrink={1}>\n {lineNum !== undefined && (\n <Text color={selected && focused ? theme.sent.bg : theme.lineNum}>\n {lineNum.padStart(3)}{\" \"}\n </Text>\n )}\n <Text color={selected && focused ? theme.sent.bg : undefined}>\n {selected && focused ? \"▸\" : \" \"}\n </Text>\n {hasUnread && <Text color={theme.dot}>{theme.glyphs.envelope} </Text>}\n {c.isGroupChat && <Text color={theme.info.label}>{theme.glyphs.group} </Text>}\n <Text\n color={\n selected\n ? theme.sidebar.selectedFg\n : hasUnread\n ? theme.sidebar.unread\n : theme.sidebar.read\n }\n bold={hasUnread}\n wrap=\"truncate\"\n >\n {truncatedName}\n </Text>\n </Box>\n\n {/* Right side: count + service icon + time, fixed-width */}\n <Box flexShrink={0}>\n {hasUnread && <Text color={theme.sidebar.unread}> ({c.unreadCount})</Text>}\n <Text color={c.serviceType === \"SMS\" ? theme.sms : theme.info.label}>\n {\" \"}\n {serviceIcon}\n </Text>\n <Text color={theme.sidebar.time}> {time}</Text>\n </Box>\n </Box>\n\n {/* Snippet row — explicit width so it can't bleed into adjacent rows */}\n <Box width={width} paddingLeft={5}>\n <Text color={theme.sidebar.snippet} wrap=\"truncate\">\n {truncatedSnippet}\n </Text>\n </Box>\n\n {/* Slug row — right-justified, italic, dim, subtle bg */}\n <Box\n width={width}\n justifyContent=\"flex-end\"\n paddingRight={1}\n backgroundColor={theme.sidebar.slugBg}\n >\n <Text color={theme.sidebar.slug} dimColor italic>\n ~{c.threadSlug}\n </Text>\n </Box>\n </Box>\n\n {/* Separator between items */}\n {!isLast && (\n <Box paddingX={1}>\n <Text color={theme.sidebar.separator} dimColor>\n {theme.glyphs.separator.repeat(Math.max(width - 4, 1))}\n </Text>\n </Box>\n )}\n </Box>\n );\n}\n\n// Exported for tests\nexport { nameBudget };\n","/**\n * Default sidebar row renderer used when a feature module doesn't supply its\n * own. Visually mirrors `ConversationItem` (3 rows + separator) so the\n * sidebar cursor math is unchanged.\n */\nimport { Box, Text } from \"ink\";\nimport type { ModuleSidebarItemProps } from \"../modules/types.js\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\n\nexport function DefaultModuleSidebarItem({\n instance,\n selected,\n focused,\n width,\n lineNum,\n isLast,\n}: ModuleSidebarItemProps) {\n const theme = useTheme();\n const accent = instance.accentColor ?? theme.status.accent;\n\n return (\n <Box flexDirection=\"column\" width={width}>\n <Box\n flexDirection=\"column\"\n width={width}\n backgroundColor={selected ? theme.sidebar.selected : undefined}\n >\n <Box width={width}>\n {lineNum !== undefined && (\n <Text color={selected && focused ? theme.sent.bg : theme.lineNum}>\n {lineNum.padStart(3)}{\" \"}\n </Text>\n )}\n <Text color={selected && focused ? theme.sent.bg : accent}>\n {selected && focused ? \"▸\" : \"✦\"}\n </Text>\n <Text color={selected ? theme.sidebar.selectedFg : accent} bold>\n {\" \"}\n {instance.title}\n </Text>\n </Box>\n\n <Box width={width} paddingLeft={5}>\n <Text color={theme.sidebar.snippet} wrap=\"truncate\">\n {instance.subtitle ?? \"\"}\n </Text>\n </Box>\n\n <Box\n width={width}\n justifyContent=\"flex-end\"\n paddingRight={1}\n backgroundColor={theme.sidebar.slugBg}\n >\n <Text color={theme.sidebar.slug} dimColor italic>\n ✦{instance.moduleId}\n </Text>\n </Box>\n </Box>\n\n {!isLast && (\n <Box paddingX={1}>\n <Text color={theme.sidebar.separator} dimColor>\n {theme.glyphs.separator.repeat(Math.max(width - 4, 1))}\n </Text>\n </Box>\n )}\n </Box>\n );\n}\n","import { Box, Text } from \"ink\";\nimport { useMemo } from \"react\";\nimport type { Conversation } from \"../../types.js\";\nimport { matchesConversationFilter } from \"../filter.js\";\nimport { findModule } from \"../modules/registry.js\";\nimport type { ModuleInstance } from \"../modules/types.js\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\nimport { ConversationItem } from \"./ConversationItem.js\";\nimport { DefaultModuleSidebarItem } from \"./DefaultModuleSidebarItem.js\";\n\ninterface Props {\n conversations: Conversation[];\n moduleInstances: ModuleInstance[];\n selectedIdx: number;\n /** Index into `moduleInstances` when a virtual row is selected; null otherwise. */\n selectedModuleIdx: number | null;\n scrollOffset: number;\n filterQuery: string;\n focused: boolean;\n width: number;\n height: number;\n}\n\nexport function Sidebar({\n conversations,\n moduleInstances,\n selectedIdx,\n selectedModuleIdx,\n scrollOffset,\n filterQuery,\n focused,\n width,\n height,\n}: Props) {\n const theme = useTheme();\n const filtered = useMemo(() => {\n if (!filterQuery) return conversations;\n const q = filterQuery.toLowerCase();\n return conversations.filter((c) => matchesConversationFilter(c, q));\n }, [conversations, filterQuery]);\n\n // Each row takes ~4 rows (3 content + 1 separator). Module rows match this\n // rhythm so the cursor math in App.tsx doesn't need a per-row height table.\n const itemHeight = 4;\n const headerH = 1 + (filterQuery ? 1 : 0);\n const borderH = 2;\n const visibleCount = Math.floor((height - headerH - borderH) / itemHeight);\n\n // Combined visible list: [...moduleInstances, ...conversations].\n // The scrollOffset is an index into the combined list.\n const combined: Array<\n { kind: \"module\"; instance: ModuleInstance } | { kind: \"conv\"; conv: Conversation }\n > = [\n ...moduleInstances.map((instance) => ({ kind: \"module\" as const, instance })),\n ...filtered.map((conv) => ({ kind: \"conv\" as const, conv })),\n ];\n const visible = combined.slice(scrollOffset, scrollOffset + visibleCount);\n const moduleCount = moduleInstances.length;\n\n // The displayed cursor index in the combined list.\n const combinedCursor = selectedModuleIdx != null ? selectedModuleIdx : moduleCount + selectedIdx;\n\n return (\n <Box\n flexDirection=\"column\"\n width={width}\n height={height}\n borderStyle=\"single\"\n borderColor={focused ? theme.header.focused.fg : theme.border}\n overflow=\"hidden\"\n >\n <Box paddingX={1} backgroundColor={focused ? theme.header.focused.bg : theme.header.dim.bg}>\n <Text color={focused ? theme.header.focused.fg : theme.header.dim.fg} bold={focused}>\n Conversations ({filtered.length}\n {moduleCount > 0 ? ` + ${moduleCount} tool${moduleCount === 1 ? \"\" : \"s\"}` : \"\"})\n </Text>\n </Box>\n\n {filterQuery && (\n <Box paddingX={1}>\n <Text color={theme.status.accent}>/ </Text>\n <Text color={theme.compose.fg}>{filterQuery}</Text>\n </Box>\n )}\n\n <Box flexDirection=\"column\" flexGrow={1} overflow=\"hidden\">\n {visible.length === 0 ? (\n <Box paddingX={1}>\n <Text color={theme.sidebar.snippet}>No conversations</Text>\n </Box>\n ) : (\n visible.map((row, i) => {\n const realIdx = scrollOffset + i;\n const selected = realIdx === combinedCursor;\n const relNum = selected ? `${realIdx}` : `${Math.abs(realIdx - combinedCursor)}`;\n const isLast = i === visible.length - 1;\n\n if (row.kind === \"module\") {\n const mod = findModule(row.instance.moduleId);\n const ItemComponent = mod?.SidebarItem ?? DefaultModuleSidebarItem;\n return (\n <ItemComponent\n key={row.instance.id}\n instance={row.instance}\n selected={selected}\n focused={!!focused}\n width={width - 2}\n lineNum={relNum}\n isLast={isLast}\n />\n );\n }\n\n return (\n <ConversationItem\n key={row.conv.threadSlug}\n conversation={row.conv}\n selected={selected}\n width={width - 2}\n lineNum={relNum}\n focused={focused}\n isLast={isLast}\n />\n );\n })\n )}\n </Box>\n </Box>\n );\n}\n","import { Box, Text } from \"ink\";\nimport type React from \"react\";\nimport type { Conversation } from \"../../types.js\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\n\ninterface Props {\n totalUnread: number;\n selected: Conversation | undefined;\n status: string;\n loading: boolean;\n children?: React.ReactNode;\n}\n\nexport function StatusBar({ totalUnread, selected, status, loading, children }: Props) {\n const theme = useTheme();\n return (\n <Box backgroundColor={theme.status.bg} paddingX={1} height={1} justifyContent=\"space-between\">\n {/* Each segment uses flexShrink={0} so the row's `gap={2}` separators\n * don't collapse when the status toast on the right side grows —\n * pre-fix you'd see \"Mum SMSRust parser + TS 0%0MB\" glued together.\n * The status text on the right truncates to a single line instead of\n * wrapping onto a second row. */}\n <Box gap={2} flexShrink={0}>\n {totalUnread > 0 && (\n <Text color={theme.status.accent} bold>\n ● {totalUnread} unread\n </Text>\n )}\n {selected && (\n <Text color={theme.status.fg}>{selected.displayName ?? selected.chatIdentifier}</Text>\n )}\n {selected && (\n <Text color={selected.serviceType === \"SMS\" ? theme.sms : theme.info.label}>\n {selected.serviceType}\n </Text>\n )}\n </Box>\n <Box gap={2} flexShrink={1} overflow=\"hidden\">\n {children}\n <Text color={theme.status.fg} wrap=\"truncate\">\n {loading ? \"loading...\" : status}\n </Text>\n </Box>\n </Box>\n );\n}\n","import { TextInput } from \"@inkjs/ui\";\nimport { Box, Text } from \"ink\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\nimport type { Mode } from \"../types.js\";\n\ninterface Props {\n mode: Mode;\n recipientName: string;\n onChangeText: (text: string) => void;\n onSubmit: (text: string) => void;\n}\n\nexport function ComposeBar({ mode, recipientName, onChangeText, onSubmit }: Props) {\n const theme = useTheme();\n if (mode === \"confirm\") {\n return (\n <Box backgroundColor={theme.compose.bg} paddingX={1} height={1}>\n <Text color={theme.status.accent} bold>\n Send to {recipientName}?{\" \"}\n </Text>\n <Text color={theme.compose.fg}>Enter: send Esc: cancel</Text>\n </Box>\n );\n }\n\n if (mode === \"compose\") {\n return (\n <Box backgroundColor={theme.compose.bg} paddingX={1} height={1}>\n <Text color={theme.compose.fg}>&gt; </Text>\n <TextInput onChange={onChangeText} onSubmit={onSubmit} placeholder=\"Type a message...\" />\n </Box>\n );\n }\n\n return null;\n}\n","import { Box, Text } from \"ink\";\nimport type { Message, Reaction } from \"../../types.js\";\nimport { TAPBACK_EMOJI } from \"../theme.js\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\n\nfunction relativeDate(date: Date): string {\n const now = new Date();\n const time = date.toLocaleTimeString(\"en-US\", {\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true,\n });\n if (date.toDateString() === now.toDateString()) return time;\n const y = new Date(now);\n y.setDate(now.getDate() - 1);\n if (date.toDateString() === y.toDateString()) return `Yest ${time}`;\n return `${date.getMonth() + 1}/${date.getDate()} ${time}`;\n}\n\nfunction formatReactions(reactions: Reaction[]): string {\n const counts = new Map<string, number>();\n for (const r of reactions) {\n if (r.isRemoval) continue;\n const emoji = r.emoji ?? TAPBACK_EMOJI[r.type] ?? r.type;\n counts.set(emoji, (counts.get(emoji) ?? 0) + 1);\n }\n if (counts.size === 0) return \"\";\n return [...counts.entries()].map(([e, c]) => (c > 1 ? `${e}${c}` : e)).join(\"\");\n}\n\n/** Check if two dates are on different calendar days */\nexport function isDifferentDay(a: Date, b: Date): boolean {\n return a.toDateString() !== b.toDateString();\n}\n\n/** Format a date separator label */\nexport function dateSeparator(date: Date): string {\n const now = new Date();\n if (date.toDateString() === now.toDateString()) return \"Today\";\n const y = new Date(now);\n y.setDate(now.getDate() - 1);\n if (date.toDateString() === y.toDateString()) return \"Yesterday\";\n return date.toLocaleDateString(\"en-US\", { weekday: \"short\", month: \"short\", day: \"numeric\" });\n}\n\n/** Determine if a message is the first in a group from the same sender */\nexport function isGroupStart(msg: Message, prev: Message | undefined): boolean {\n if (!prev) return true;\n return msg.isFromMe !== prev.isFromMe || msg.handle !== prev.handle;\n}\n\n/** Determine if a message is the last in a group from the same sender */\nexport function isGroupEnd(msg: Message, next: Message | undefined): boolean {\n if (!next) return true;\n return msg.isFromMe !== next.isFromMe || msg.handle !== next.handle;\n}\n\ninterface Props {\n message: Message;\n maxWidth: number;\n showSender?: boolean;\n senderName?: string;\n selected?: boolean;\n lineNum?: string;\n isFirstInGroup?: boolean; // first message from this sender in a consecutive run\n isLastInGroup?: boolean; // last message from this sender in a consecutive run\n bgTint?: string; // alternating background for sender groups\n /**\n * Optional resolver for reply target text by GUID. When `m.isReply` is\n * true but `m.replyTo.replyToText` is null/undefined (the iMessage DB\n * sometimes leaves it unpopulated even when the link metadata exists),\n * we fall back to looking up the target message in the currently loaded\n * messages array via this callback. If still not found, render a\n * placeholder so the reader at least sees the reply indicator.\n */\n lookupReplyText?: (guid: string) => string | null;\n /** True when this message is inside an active visual selection range. */\n inSelection?: boolean;\n}\n\n/**\n * Compact message row — no borders, minimal vertical space.\n * Sender groups get alternating background tints.\n * First message in group shows sender name; subsequent ones are indented.\n */\nexport function MessageBubble({\n message: m,\n maxWidth,\n showSender,\n senderName,\n selected,\n lineNum,\n isFirstInGroup,\n isLastInGroup,\n bgTint,\n lookupReplyText,\n inSelection,\n}: Props) {\n const theme = useTheme();\n const isSent = m.isFromMe;\n const text = m.text ?? \"(attachment)\";\n const timestamp = relativeDate(m.date);\n const reactions = m.reactions ? formatReactions(m.reactions) : \"\";\n const hasAttachments = m.hasAttachments;\n\n // Cursor indicator\n const cursor = selected ? \"▸\" : \" \";\n const cursorColor = selected ? theme.sent.bg : undefined;\n\n // Only show sender on first message in a group\n const sender = isFirstInGroup\n ? showSender && !isSent && senderName\n ? senderName\n : isSent\n ? \"Me\"\n : undefined\n : undefined;\n\n // Group separator: thin line between groups\n const groupSepChar = isFirstInGroup ? \"┌\" : isLastInGroup ? \"└\" : \"│\";\n const groupColor = isSent ? theme.sent.border : theme.received.border;\n\n return (\n <Box\n flexDirection=\"column\"\n backgroundColor={selected ? theme.sidebar.selected : inSelection ? theme.selectionBg : bgTint}\n >\n <Box>\n {/*\n * Prefix block — line number, cursor, group gutter, timestamp,\n * direction glyph, sender. Rendered as a SINGLE <Text> with nested\n * colored spans so Ink treats the whole prefix as one atomic\n * shrink-unit. Splitting it into separate <Text> siblings made\n * Ink's flex-shrink chop one char off each on overflow rows —\n * e.g. `9:49 PM ◀` rendered as `9:49 P◀` (M and trailing space\n * eaten), `Me: ` rendered as `Me:` (running into the message body).\n * Wrapped in a Box with flexShrink={0} so the prefix never loses\n * a char; the message <Text> below (with wrap=\"truncate\") is the\n * sole shrinkable element on the row.\n */}\n <Box flexShrink={0}>\n <Text>\n {lineNum !== undefined && (\n <Text color={selected ? theme.sent.bg : theme.lineNum}>{lineNum.padStart(3)} </Text>\n )}\n <Text color={cursorColor}>{cursor}</Text>\n <Text color={groupColor}>{groupSepChar} </Text>\n {isFirstInGroup ? (\n // pad to 14 (not 13) so even the widest natural timestamp\n // (`MM/DD HH:MM PM` = 13 chars, or `Yest 12:37 PM` = 13)\n // always has at least one trailing space before the glyph —\n // otherwise the row renders as `10:39 PM◀ Hey` with no gap.\n <Text color={theme.timestamp}>{timestamp.padEnd(14)}</Text>\n ) : (\n <Text color={theme.timestamp}>{\" \"}</Text>\n )}\n {isFirstInGroup ? (\n <>\n {isSent ? (\n <Text color={theme.sent.bg} bold>\n {theme.glyphs.sent}{\" \"}\n </Text>\n ) : (\n <Text color={theme.received.border} bold>\n {theme.glyphs.received}{\" \"}\n </Text>\n )}\n {sender && (\n <Text color={isSent ? theme.sent.bg : theme.senderName} bold>\n {sender.length > 12 ? `${sender.slice(0, 11)}…` : sender}\n {\": \"}\n </Text>\n )}\n </>\n ) : (\n // Continuation: indent to align with first message text\n <Text>\n {\" \"}\n {sender ? \" \" : \"\"}\n </Text>\n )}\n </Text>\n </Box>\n\n {/* Message text — the only shrinkable element on the row. */}\n <Text color={isSent ? theme.sentText : theme.receivedText} wrap=\"truncate\">\n {text}\n </Text>\n\n {/* Reactions */}\n {reactions && <Text> {reactions}</Text>}\n\n {/* Attachment indicator */}\n {hasAttachments && <Text color={theme.attachment}> 📎</Text>}\n\n {/* Edited indicator */}\n {m.isEdited && <Text color={theme.edited}> ✎</Text>}\n </Box>\n\n {/* Reply context — always render the indicator when isReply, even if\n replyToText is missing. iMessage sometimes leaves the text NULL\n even when the GUID link is present, so we try a runtime lookup\n and fall back to a placeholder so the user knows it IS a reply. */}\n {m.isReply &&\n (() => {\n let replyText: string | null = m.replyTo?.replyToText ?? null;\n if (!replyText && m.replyTo?.replyToGuid && lookupReplyText) {\n replyText = lookupReplyText(m.replyTo.replyToGuid);\n }\n const display = replyText\n ? replyText.slice(0, maxWidth - 12)\n : \"(replied to earlier message)\";\n return (\n <Box>\n {lineNum !== undefined && <Text>{\" \"}</Text>}\n <Text>{\" \"}</Text>\n <Text color={theme.replyContext} italic>\n {\" ↩ \"}\n {display}\n </Text>\n </Box>\n );\n })()}\n </Box>\n );\n}\n\ninterface PendingBubbleProps {\n text: string;\n status: \"sending\" | \"sent\" | \"failed\";\n maxWidth: number;\n}\n\nexport function PendingBubble({ text, status }: PendingBubbleProps) {\n const theme = useTheme();\n const indicator = status === \"sending\" ? \"⏳\" : status === \"failed\" ? \"⚠\" : \"✓\";\n const color = status === \"failed\" ? theme.edited : theme.pending.fg;\n\n return (\n <Box backgroundColor={theme.groupBg.sent}>\n <Text>{\" \"}</Text>\n <Text color={color}>{indicator} </Text>\n <Text color={theme.timestamp}>{\"now\".padEnd(14)}</Text>\n <Text color={theme.sent.bg} bold>\n {`${theme.glyphs.sent} Me: `}\n </Text>\n <Text color={theme.pending.fg} wrap=\"truncate\">\n {text}\n </Text>\n </Box>\n );\n}\n","import { Box, Text } from \"ink\";\nimport React, { useMemo } from \"react\";\nimport type { Conversation, Message } from \"../../types.js\";\nimport { useTheme } from \"../themes/ThemeContext.js\";\nimport type { Mode, PendingMessage } from \"../types.js\";\nimport { ComposeBar } from \"./ComposeBar.js\";\nimport {\n dateSeparator,\n isDifferentDay,\n isGroupEnd,\n isGroupStart,\n MessageBubble,\n PendingBubble,\n} from \"./MessageBubble.js\";\n\ninterface Props {\n conversation: Conversation | undefined;\n messages: Message[];\n pending: PendingMessage[];\n resolvedNames: string[];\n scrollOffset: number;\n selectedMsgIdx: number;\n /** Anchor index for visual selection (set when V is pressed). null = no selection. */\n selectionAnchor: number | null;\n /** Eviction gap markers — placeholders showing \"N more messages — scroll to load\". */\n gapMarkers: Array<{ atIdx: number; oldestId: number; newestId: number; count: number }>;\n focused: boolean;\n width: number;\n height: number;\n mode: Mode;\n onChangeCompose: (text: string) => void;\n onSubmitCompose: (text: string) => void;\n}\n\nexport function ThreadPane({\n conversation,\n messages,\n pending,\n resolvedNames: _resolvedNames,\n scrollOffset: _scrollOffset,\n selectedMsgIdx,\n selectionAnchor,\n gapMarkers,\n focused,\n width,\n height,\n mode,\n onChangeCompose,\n onSubmitCompose,\n}: Props) {\n const theme = useTheme();\n const isGroup = conversation?.isGroupChat ?? false;\n const maxBubbleW = Math.max(width - 8, 20);\n const composing = mode === \"compose\" || mode === \"confirm\";\n\n // Available height for messages\n const headerH = 1;\n const composeH = composing ? 1 : 0;\n const borderH = 2;\n const msgAreaHeight = Math.max(height - headerH - composeH - borderH, 3);\n\n // Compute visible window anchored on selectedMsgIdx.\n // Account for multi-line items: replies take 2 lines, date separators take 1 extra line.\n const { visibleStart, visibleEnd } = useMemo(() => {\n const total = messages.length + pending.length;\n if (total === 0) return { visibleStart: 0, visibleEnd: 0 };\n\n let cursorIdx = selectedMsgIdx >= 0 ? selectedMsgIdx : total - 1;\n cursorIdx = Math.max(0, Math.min(cursorIdx, total - 1));\n\n // Bottom-anchor: when the cursor is at or near the last real message, walk\n // UP from the end accumulating real heights until we fill msgAreaHeight.\n // Fixes the clip where the last 1-3 messages run past Ink's overflow=\"hidden\"\n // because the downward fill loop never executes (end === total - 1 already).\n const NEAR_END = 2;\n if (cursorIdx >= messages.length - NEAR_END && pending.length === 0) {\n const end = messages.length - 1;\n let start = end;\n let totalLines = lineHeight(messages, end, maxBubbleW);\n while (\n start > 0 &&\n totalLines + lineHeight(messages, start - 1, maxBubbleW) <= msgAreaHeight\n ) {\n start--;\n totalLines += lineHeight(messages, start, maxBubbleW);\n }\n return { visibleStart: start, visibleEnd: end + 1 };\n }\n\n // Walk outward from cursor, counting actual lines consumed\n // Start by going upward from cursor\n let start = cursorIdx;\n let linesAbove = 0;\n const targetAbove = Math.floor(msgAreaHeight * 0.4);\n while (start > 0 && linesAbove < targetAbove) {\n start--;\n linesAbove += lineHeight(messages, start, maxBubbleW);\n }\n\n // Then go downward from cursor\n let end = cursorIdx;\n let totalLines = linesAbove + lineHeight(messages, cursorIdx, maxBubbleW);\n while (end < total - 1 && totalLines < msgAreaHeight) {\n end++;\n if (end < messages.length) {\n totalLines += lineHeight(messages, end, maxBubbleW);\n } else {\n totalLines += 1; // pending messages\n }\n }\n\n // If we have room, extend upward more\n while (start > 0 && totalLines < msgAreaHeight) {\n start--;\n totalLines += lineHeight(messages, start, maxBubbleW);\n }\n\n return { visibleStart: start, visibleEnd: end + 1 };\n // `messages` reference is preserved across non-messages reducer cases\n // (see types.ts reducer), so depending on length is sufficient for the\n // common fast path. Content-mutating actions (SET_MESSAGES /\n // PREPEND_MESSAGES) replace the whole array, which also flips length.\n }, [messages, pending.length, selectedMsgIdx, msgAreaHeight, maxBubbleW]);\n\n const visibleMessages = messages.slice(visibleStart, Math.min(visibleEnd, messages.length));\n\n // Build a GUID → text lookup so MessageBubble can resolve missing replyToText\n // from the loaded message set when iMessage didn't populate it. Memoized to\n // avoid rebuilding on every render.\n const messagesByGuid = useMemo(() => {\n const map = new Map<string, string>();\n for (const m of messages) {\n if (m.guid && m.text) map.set(m.guid, m.text);\n }\n return map;\n }, [messages]);\n\n const lookupReplyText = (guid: string): string | null => messagesByGuid.get(guid) ?? null;\n\n // Visual selection range — derived from anchor + cursor.\n const selRange: [number, number] | null =\n selectionAnchor != null && selectedMsgIdx >= 0\n ? [Math.min(selectionAnchor, selectedMsgIdx), Math.max(selectionAnchor, selectedMsgIdx)]\n : null;\n\n return (\n <Box\n flexDirection=\"column\"\n width={width}\n height={height}\n borderStyle=\"single\"\n borderColor={focused ? theme.header.focused.fg : theme.border}\n overflow=\"hidden\"\n >\n {/* Header.\n * flexShrink discipline: the LEFT side (name) shrinks first; \"(N\n * msgs)\" + the right column (identifier + service) keep their\n * width. Without these, narrowing the pane (e.g. opening dev\n * stats) wraps the header mid-character — \"iMessa\\nge\". */}\n <Box\n paddingX={1}\n backgroundColor={focused ? theme.header.focused.bg : theme.header.dim.bg}\n justifyContent=\"space-between\"\n flexShrink={0}\n >\n <Box flexShrink={1} overflow=\"hidden\">\n <Text\n color={focused ? theme.header.focused.fg : theme.header.dim.fg}\n bold={focused}\n wrap=\"truncate\"\n >\n {conversation?.displayName ?? conversation?.chatIdentifier ?? \"Thread\"}\n </Text>\n {conversation && (\n <Text color={theme.info.label} wrap=\"truncate\">{` (${messages.length} msgs)`}</Text>\n )}\n </Box>\n {conversation && (\n <Box gap={1} flexShrink={0}>\n {conversation.displayName && (\n <Text color={theme.info.label} wrap=\"truncate\">\n {conversation.rawIdentifier}\n </Text>\n )}\n <Text color={conversation.serviceType === \"SMS\" ? theme.sms : theme.info.label}>\n {conversation.serviceType}\n </Text>\n {conversation.isGroupChat && <Text color={theme.info.label}>Group</Text>}\n </Box>\n )}\n </Box>\n\n {/* Messages — compact rows with sender grouping */}\n <Box flexDirection=\"column\" flexGrow={1} overflow=\"hidden\">\n {messages.length === 0 && pending.length === 0 ? (\n <Box paddingX={1}>\n <Text color={theme.sidebar.snippet}>No messages</Text>\n </Box>\n ) : (\n <>\n {/* Scroll indicator top */}\n {visibleStart > 0 && (\n <Box justifyContent=\"center\">\n <Text color={theme.dateSep}>── ↑ {visibleStart} more ──</Text>\n </Box>\n )}\n\n {visibleMessages.map((msg, i) => {\n const realIdx = visibleStart + i;\n const prevMsg = realIdx > 0 ? messages[realIdx - 1] : undefined;\n const nextMsg = realIdx < messages.length - 1 ? messages[realIdx + 1] : undefined;\n const showDateSep = !prevMsg || isDifferentDay(prevMsg.date, msg.date);\n const firstInGroup = isGroupStart(msg, prevMsg);\n const lastInGroup = isGroupEnd(msg, nextMsg);\n\n // Alternating background tint based on sender\n const bgTint = msg.isFromMe ? theme.groupBg.sent : theme.groupBg.received;\n\n // Relative line number: distance from cursor\n const relNum =\n selectedMsgIdx >= 0\n ? realIdx === selectedMsgIdx\n ? `${realIdx}`\n : `${Math.abs(realIdx - selectedMsgIdx)}`\n : `${realIdx}`;\n\n return (\n <React.Fragment key={msg.id}>\n {(() => {\n // Gap marker — show \"N more messages\" before the first\n // message after an evicted region.\n const gap = gapMarkers.find((g) => g.atIdx === realIdx);\n if (!gap) return null;\n return (\n <Box justifyContent=\"center\" marginTop={1} marginBottom={1}>\n <Text color={theme.edited}>\n ─── {gap.count.toLocaleString()} older messages evicted (scroll back to\n reload) ───\n </Text>\n </Box>\n );\n })()}\n {showDateSep && (\n // Always 1 row of breathing room above date separators so the\n // visual rhythm is consistent — without this, separators that\n // appear after a same-sender continuation feel cramped while\n // ones after a different-sender row feel fine.\n <Box justifyContent=\"center\" marginTop={realIdx === 0 ? 0 : 1}>\n <Text color={theme.dateSep}>─── {dateSeparator(msg.date)} ───</Text>\n </Box>\n )}\n <MessageBubble\n message={msg}\n maxWidth={maxBubbleW}\n showSender={isGroup}\n senderName={msg.displayName ?? msg.handle}\n selected={realIdx === selectedMsgIdx && focused}\n lineNum={relNum}\n isFirstInGroup={firstInGroup || showDateSep}\n isLastInGroup={lastInGroup}\n bgTint={bgTint}\n lookupReplyText={lookupReplyText}\n inSelection={\n selRange != null && realIdx >= selRange[0] && realIdx <= selRange[1]\n }\n />\n {/* Group separator line between different senders */}\n {lastInGroup && nextMsg && !isDifferentDay(msg.date, nextMsg.date) && (\n <Box height={0} />\n )}\n </React.Fragment>\n );\n })}\n\n {/* Pending messages */}\n {pending.map((pm) => (\n <PendingBubble\n key={pm.text}\n text={pm.text}\n status={pm.status}\n maxWidth={maxBubbleW}\n />\n ))}\n\n {/* Scroll indicator bottom */}\n {visibleEnd < messages.length && (\n <Box justifyContent=\"center\">\n <Text color={theme.dateSep}>── ↓ {messages.length - visibleEnd} more ──</Text>\n </Box>\n )}\n </>\n )}\n </Box>\n\n {/* Compose bar */}\n {composing && (\n <ComposeBar\n mode={mode}\n recipientName={conversation?.displayName ?? conversation?.chatIdentifier ?? \"\"}\n onChangeText={onChangeCompose}\n onSubmit={onSubmitCompose}\n />\n )}\n </Box>\n );\n}\n\n/**\n * Estimate how many terminal lines a message at index `i` will consume.\n * Conservative upper bound: under-counts trigger bottom-of-thread clipping\n * (Ink's overflow=\"hidden\" silently drops rows past the box edge), so we err\n * toward over-counting wrap rows from text length.\n */\nfunction lineHeight(messages: Message[], i: number, bubbleWidth: number): number {\n if (i < 0 || i >= messages.length) return 1;\n const msg = messages[i];\n // Wrap rows for the message body. Bubble inner width is roughly bubbleWidth\n // minus padding/prefix (sender name, line number). Use max(20) so very narrow\n // terminals don't divide-by-near-zero.\n const innerW = Math.max(20, bubbleWidth - 4);\n const text = msg.text ?? \"\";\n let h = Math.max(1, Math.ceil(text.length / innerW));\n // Reply preview row(s)\n if (msg.isReply) {\n const replyLen = msg.replyTo?.replyToText?.length ?? 0;\n h += Math.max(1, Math.ceil(replyLen / Math.max(20, innerW - 4)));\n }\n // Date separator. Rendered with marginTop={1} when realIdx > 0\n // (ThreadPane.tsx ~line 216), so it actually occupies TWO terminal rows in\n // that case: 1 blank margin + 1 separator content. Under-counting this\n // pushes the last message past the box edge.\n if (i === 0) {\n h += 1;\n } else if (isDifferentDay(messages[i - 1].date, msg.date)) {\n h += 2;\n }\n // Attachment indicator row (paperclip + filename) when present\n if (msg.attachments && msg.attachments.length > 0) h += 1;\n return h;\n}\n\n/**\n * Find the index of the next sender-group boundary from the given position.\n * A group boundary is where the sender changes (isFromMe flips or handle changes).\n */\nexport function nextGroupBoundary(messages: Message[], fromIdx: number): number {\n if (fromIdx >= messages.length - 1) return messages.length - 1;\n const current = messages[fromIdx];\n // Skip to end of current group\n let i = fromIdx + 1;\n while (i < messages.length) {\n const m = messages[i];\n if (m.isFromMe !== current.isFromMe || m.handle !== current.handle) {\n return i;\n }\n i++;\n }\n return messages.length - 1;\n}\n\n/**\n * Find the index of the previous sender-group boundary from the given position.\n */\nexport function prevGroupBoundary(messages: Message[], fromIdx: number): number {\n if (fromIdx <= 0) return 0;\n const current = messages[fromIdx];\n // If we're at the start of a group, go to start of previous group\n const prev = messages[fromIdx - 1];\n if (prev.isFromMe !== current.isFromMe || prev.handle !== current.handle) {\n // We're at a boundary — find start of previous group\n let i = fromIdx - 1;\n while (i > 0) {\n const m = messages[i - 1];\n if (m.isFromMe !== prev.isFromMe || m.handle !== prev.handle) {\n return i;\n }\n i--;\n }\n return 0;\n }\n // We're in the middle of a group — go to start of current group\n let i = fromIdx - 1;\n while (i > 0) {\n const m = messages[i - 1];\n if (m.isFromMe !== current.isFromMe || m.handle !== current.handle) {\n return i;\n }\n i--;\n }\n return 0;\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { hasNativeModule } from \"../../native-bridge.js\";\nimport { readWatchdogState } from \"../../watchdog.js\";\n\nexport interface DevStatsData {\n engine: \"Rust parser + TS DB\" | \"TS\";\n cpuPercent: number;\n memMB: number;\n pid: number;\n uptime: string;\n lastQueryMs: number | null;\n eventLoopP99Ms: number;\n lastActivityAgo: string;\n}\n\nfunction formatUptime(seconds: number): string {\n if (seconds < 60) return `${Math.round(seconds)}s`;\n if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n return `${h}h${m}m`;\n}\n\nfunction formatAgo(ms: number): string {\n if (ms < 1_000) return \"now\";\n const sec = Math.floor(ms / 1_000);\n if (sec < 60) return `${sec}s`;\n if (sec < 3600) return `${Math.floor(sec / 60)}m`;\n return `${Math.floor(sec / 3600)}h`;\n}\n\nfunction engineLabel(): DevStatsData[\"engine\"] {\n return hasNativeModule() ? \"Rust parser + TS DB\" : \"TS\";\n}\n\nexport function useDevStats(visible: boolean): {\n stats: DevStatsData;\n recordQueryTime: (ms: number) => void;\n} {\n const [stats, setStats] = useState<DevStatsData>({\n engine: engineLabel(),\n cpuPercent: 0,\n memMB: 0,\n pid: process.pid,\n uptime: \"0s\",\n lastQueryMs: null,\n eventLoopP99Ms: 0,\n lastActivityAgo: \"now\",\n });\n\n const lastCpuRef = useRef(process.cpuUsage());\n const lastTimeRef = useRef(Date.now());\n const lastQueryMsRef = useRef<number | null>(null);\n\n useEffect(() => {\n if (!visible) return;\n const sample = () => {\n const now = Date.now();\n const elapsed = now - lastTimeRef.current;\n\n // CPU% needs a time delta; on the immediate first sample keep 0.\n let cpuPercent = 0;\n if (elapsed > 0) {\n const cpuNow = process.cpuUsage(lastCpuRef.current);\n // cpuUsage returns microseconds; convert to percentage of wall time\n const totalCpuUs = cpuNow.user + cpuNow.system;\n cpuPercent = (totalCpuUs / 1000 / elapsed) * 100;\n lastCpuRef.current = process.cpuUsage();\n lastTimeRef.current = now;\n }\n\n const { rss } = process.memoryUsage();\n const memMB = Math.round((rss / 1024 / 1024) * 10) / 10;\n\n const wd = readWatchdogState();\n setStats({\n engine: engineLabel(),\n cpuPercent: Math.round(cpuPercent * 10) / 10,\n memMB,\n pid: process.pid,\n uptime: formatUptime(process.uptime()),\n lastQueryMs: lastQueryMsRef.current,\n eventLoopP99Ms: Math.round(wd.eventLoopP99Ms * 10) / 10,\n lastActivityAgo: formatAgo(Date.now() - wd.lastActivityTs),\n });\n };\n\n // Paint real numbers as soon as the panel opens — without this the panel\n // shows 0MB / 0s until the first interval tick lands.\n sample();\n const timer = setInterval(sample, 2000);\n timer.unref();\n\n return () => clearInterval(timer);\n }, [visible]);\n\n const recordQueryTime = useCallback((ms: number) => {\n lastQueryMsRef.current = Math.round(ms * 10) / 10;\n }, []);\n\n return { stats, recordQueryTime };\n}\n","/**\n * In-memory cache of messages-per-chat for the TUI.\n *\n * Behavior:\n * - When the user re-enters a chat they've already viewed within\n * `STALE_MS`, return cached messages immediately (no DB round trip).\n * - Older entries get evicted on a TTL sweep every 60s.\n * - When heap pressure crosses `MEMORY_PRESSURE_MB` (sampled by the\n * watchdog), evict the LRU half of the cache until below threshold.\n *\n * Pure module — no React imports. Used by `useImsg.ts` and observable\n * via `cacheStats()` for the dev stats panel.\n */\n\nimport type { Message } from \"../types.js\";\nimport { onMemorySample } from \"../watchdog.js\";\n\ninterface CacheEntry {\n messages: Message[];\n oldestId: number; // for \"load older\" continuity\n loadedAt: number; // wall-clock ms\n lastAccess: number; // for LRU\n bytesEstimate: number;\n}\n\nfunction envNum(name: string, fallback: number): number {\n const raw = process.env[name];\n if (!raw) return fallback;\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;\n}\n\nconst TTL_MS = envNum(\"IMSG_TUI_CACHE_TTL_MS\", 600_000); // 10 min\nconst STALE_MS = envNum(\"IMSG_TUI_CACHE_STALE_MS\", 30_000); // 30s\nconst MEMORY_PRESSURE_MB = envNum(\"IMSG_TUI_CACHE_MEM_PRESSURE_MB\", 200); // heap MB\n\nconst cache = new Map<string, CacheEntry>();\nlet sweepTimer: ReturnType<typeof setInterval> | null = null;\nlet unsubMemSample: (() => void) | null = null;\n\nfunction estimateBytes(messages: Message[]): number {\n // Rough — only matters for relative sizing during eviction\n let bytes = 0;\n for (const m of messages) {\n bytes += (m.text?.length ?? 0) * 2; // UTF-16\n bytes += (m.handle?.length ?? 0) * 2;\n bytes += 80; // fixed overhead per message (date, ids, flags)\n }\n return bytes;\n}\n\n/** Get cached entry; returns undefined if missing. Touches lastAccess. */\nexport function getCached(chatIdentifier: string): CacheEntry | undefined {\n const entry = cache.get(chatIdentifier);\n if (entry) entry.lastAccess = Date.now();\n return entry;\n}\n\n/** Returns true if the entry is fresh enough to skip a DB round-trip. */\nexport function isFresh(entry: CacheEntry, now = Date.now()): boolean {\n return now - entry.loadedAt < STALE_MS;\n}\n\n/** Replace (or insert) the cache entry for a chat. */\nexport function setCached(chatIdentifier: string, messages: Message[], oldestId: number): void {\n const now = Date.now();\n cache.set(chatIdentifier, {\n messages,\n oldestId,\n loadedAt: now,\n lastAccess: now,\n bytesEstimate: estimateBytes(messages),\n });\n}\n\n/** Prepend older messages to an existing entry (dedup by id). */\nexport function prependCached(chatIdentifier: string, olderMessages: Message[]): void {\n const entry = cache.get(chatIdentifier);\n if (!entry) return;\n const existingIds = new Set(entry.messages.map((m) => m.id));\n const fresh = olderMessages.filter((m) => !existingIds.has(m.id));\n if (fresh.length === 0) return;\n const merged = [...fresh, ...entry.messages].sort((a, b) => a.date.getTime() - b.date.getTime());\n entry.messages = merged;\n // Reduce instead of `Math.min(entry.oldestId, ...fresh.map(...))`. The\n // spread form throws `RangeError: Maximum call stack size exceeded`\n // somewhere above ~125k arguments — reachable via batched older-load\n // on threads with huge history.\n let minFresh = fresh[0].id;\n for (let i = 1; i < fresh.length; i++) {\n if (fresh[i].id < minFresh) minFresh = fresh[i].id;\n }\n entry.oldestId = Math.min(entry.oldestId, minFresh);\n entry.lastAccess = Date.now();\n entry.bytesEstimate = estimateBytes(merged);\n}\n\n/** Clear all cached entries — used on shutdown / explicit refresh. */\nexport function clearCache(): void {\n cache.clear();\n}\n\n/** Number of cached chats and total estimated bytes. For dev stats display. */\nexport function cacheStats(): { entries: number; bytes: number } {\n let bytes = 0;\n for (const e of cache.values()) bytes += e.bytesEstimate;\n return { entries: cache.size, bytes };\n}\n\n/** TTL sweep: drop entries older than TTL_MS. Exported for tests. */\nexport function ttlSweep(now = Date.now()): number {\n let dropped = 0;\n for (const [k, v] of cache) {\n if (now - v.loadedAt > TTL_MS) {\n cache.delete(k);\n dropped++;\n }\n }\n return dropped;\n}\n\n/**\n * Memory-pressure eviction: when heap exceeds threshold, drop the LRU\n * half of the cache. Called from the watchdog memory sampler.\n */\nexport function evictUnderPressure(heapMb: number): number {\n if (heapMb < MEMORY_PRESSURE_MB) return 0;\n const sorted = [...cache.entries()].sort((a, b) => a[1].lastAccess - b[1].lastAccess);\n const half = Math.ceil(sorted.length / 2);\n for (let i = 0; i < half; i++) {\n cache.delete(sorted[i][0]);\n }\n return half;\n}\n\n/** Install TTL sweep + memory-pressure subscription. Idempotent. */\nexport function installCacheSweepers(): void {\n if (sweepTimer) return;\n\n sweepTimer = setInterval(() => {\n ttlSweep();\n }, 60_000);\n sweepTimer.unref();\n\n unsubMemSample = onMemorySample((_rss, heapMb) => {\n evictUnderPressure(heapMb);\n });\n}\n\n/** Stop sweepers. Used by tests + on shutdown. */\nexport function stopCacheSweepers(): void {\n if (sweepTimer) {\n clearInterval(sweepTimer);\n sweepTimer = null;\n }\n if (unsubMemSample) {\n unsubMemSample();\n unsubMemSample = null;\n }\n}\n","import { useCallback, useRef } from \"react\";\nimport { sendMessageAlt, sendToChat, sendToChatId } from \"../../applescript.js\";\nimport { getContactsDbPaths, getImsgDbPath, getSlugsDbPath } from \"../../config.js\";\nimport { IMessageDB } from \"../../imessage-db.js\";\nimport {\n defaultCountryFromEnv,\n type RecipientResolution,\n resolveRecipient,\n} from \"../../recipient.js\";\nimport { type Conversation, type Message, minMessageId } from \"../../types.js\";\nimport { getCached, isFresh, prependCached, setCached } from \"../messageCache.js\";\n\nexport function useImsg() {\n const dbRef = useRef<IMessageDB | null>(null);\n\n const getDb = useCallback(() => {\n if (!dbRef.current) {\n dbRef.current = new IMessageDB(getImsgDbPath(), getContactsDbPaths(), getSlugsDbPath());\n }\n return dbRef.current;\n }, []);\n\n const loadConversations = useCallback(\n async (limit = 200): Promise<Conversation[]> => {\n return getDb().listConversations(limit);\n },\n [getDb],\n );\n\n const loadMessages = useCallback(\n async (chatIdentifier: string): Promise<Message[]> => {\n // Read-through cache: return fresh entries directly without hitting the DB.\n const cached = getCached(chatIdentifier);\n if (cached && isFresh(cached)) return cached.messages;\n\n const messages = await getDb().getMessagesForChat(chatIdentifier, 200, {\n includeReactionDetails: true,\n });\n const oldestId = minMessageId(messages) ?? 0;\n setCached(chatIdentifier, messages, oldestId);\n return messages;\n },\n [getDb],\n );\n\n const loadOlderMessages = useCallback(\n async (chatIdentifier: string, beforeMessageId: number): Promise<Message[]> => {\n const older = await getDb().getMessagesForChat(chatIdentifier, 100, {\n includeReactionDetails: true,\n beforeMessageId,\n });\n // Merge into cache so subsequent re-entries see the full loaded history.\n if (older.length > 0) prependCached(chatIdentifier, older);\n return older;\n },\n [getDb],\n );\n\n /**\n * Load every message newer than the given Date — used by analytics panes\n * that compute over a time window rather than a single thread.\n */\n const loadMessagesInWindow = useCallback(\n async (since: Date): Promise<Message[]> => {\n return getDb().getMessagesInWindow(since.getTime());\n },\n [getDb],\n );\n\n const resolveNames = useCallback(\n (handles: string[]): string[] => {\n return getDb().resolveParticipantNames(handles);\n },\n [getDb],\n );\n\n const send = useCallback(\n async (threadSlug: string, text: string): Promise<{ success: boolean; error?: string }> => {\n const db = getDb();\n const slugRecord = db.getSlugRecord(threadSlug);\n if (!slugRecord) return { success: false, error: `Unknown slug: ${threadSlug}` };\n\n if (slugRecord.isGroup) {\n return slugRecord.displayName && !slugRecord.displayName.startsWith(\"chat\")\n ? sendToChat(slugRecord.displayName, text)\n : sendToChatId(slugRecord.chatGuid, text);\n }\n return sendMessageAlt(slugRecord.chatIdentifier, text);\n },\n [getDb],\n );\n\n /**\n * Resolve a free-form recipient (E.164 / local phone / iMessage email /\n * contact name) without sending — used by ComposeRecipientModal to validate\n * input as the user types and to surface ambiguous-contact candidates.\n */\n const resolveRecipientInput = useCallback(\n (input: string): RecipientResolution => {\n const db = getDb();\n return resolveRecipient(input, {\n contacts: db.contacts,\n defaultCountry: defaultCountryFromEnv(),\n });\n },\n [getDb],\n );\n\n /**\n * Send to an arbitrary recipient (not tied to an existing thread slug).\n * Runs the same `resolveRecipient` normalizer as the MCP path, so any of\n * the 4 input shapes work: E.164 phone, local phone, iMessage email,\n * unique contact name. Returns an error result for ambiguous matches —\n * caller should disambiguate before calling.\n */\n const sendToRecipient = useCallback(\n async (input: string, text: string): Promise<{ success: boolean; error?: string }> => {\n const resolution = resolveRecipientInput(input);\n if (resolution.kind === \"error\") return { success: false, error: resolution.message };\n if (resolution.kind === \"ambiguous\") {\n return {\n success: false,\n error: `Ambiguous: ${resolution.candidates.length} matches for \"${input}\". Pick a specific handle.`,\n };\n }\n return sendMessageAlt(resolution.handle, text);\n },\n [resolveRecipientInput],\n );\n\n const refresh = useCallback(() => {\n getDb().scheduleBackgroundRefresh();\n }, [getDb]);\n\n const close = useCallback(async () => {\n if (dbRef.current) {\n await dbRef.current.close();\n dbRef.current = null;\n }\n }, []);\n\n return {\n loadConversations,\n loadMessages,\n loadOlderMessages,\n loadMessagesInWindow,\n resolveNames,\n send,\n sendToRecipient,\n resolveRecipientInput,\n refresh,\n close,\n };\n}\n","import { useStdin } from \"ink\";\nimport { useEffect } from \"react\";\n\nexport interface MouseEvent {\n type: \"click\" | \"scroll-up\" | \"scroll-down\";\n x: number;\n y: number;\n button: number;\n}\n\n/**\n * Enable SGR mouse protocol and parse mouse events from stdin.\n * Calls `onEvent` for each click or scroll wheel event.\n */\nexport function useMouse(onEvent: (event: MouseEvent) => void) {\n const { stdin, setRawMode } = useStdin();\n\n useEffect(() => {\n // ?1000h = button-event tracking (press/release + scroll wheel only).\n // Do NOT use ?1003h (any-event tracking) — it fires for every pixel of\n // mouse motion, flooding stdin and pinning the event loop.\n // ?1006h = SGR extended coordinates so x/y aren't capped at 223.\n process.stdout.write(\"\\x1b[?1000h\\x1b[?1006h\");\n setRawMode(true);\n\n const handler = (data: Buffer) => {\n const str = data.toString();\n // SGR mouse format: \\x1b[<btn;x;y;M (press) or \\x1b[<btn;x;y;m (release)\n // biome-ignore lint/suspicious/noControlCharactersInRegex: ESC is the terminal mouse protocol prefix.\n const re = /\\x1b\\[<(\\d+);(\\d+);(\\d+)([Mm])/g;\n let match = re.exec(str);\n while (match !== null) {\n const btn = Number.parseInt(match[1], 10);\n const x = Number.parseInt(match[2], 10);\n const y = Number.parseInt(match[3], 10);\n const isPress = match[4] === \"M\";\n\n if (btn === 64 && isPress) {\n onEvent({ type: \"scroll-up\", x, y, button: btn });\n } else if (btn === 65 && isPress) {\n onEvent({ type: \"scroll-down\", x, y, button: btn });\n } else if (btn === 0 && isPress) {\n onEvent({ type: \"click\", x, y, button: btn });\n }\n match = re.exec(str);\n }\n };\n\n stdin.on(\"data\", handler);\n return () => {\n stdin.off(\"data\", handler);\n process.stdout.write(\"\\x1b[?1000l\\x1b[?1006l\");\n };\n }, [stdin, setRawMode, onEvent]);\n}\n","import { type Conversation, type Message, minMessageId } from \"../types.js\";\nimport type { ModuleInstance } from \"./modules/types.js\";\n\nexport type FocusPane = \"sidebar\" | \"thread\";\nexport type Mode =\n | \"browse\"\n | \"compose\"\n | \"compose-new\"\n | \"confirm\"\n | \"filter\"\n | \"drawer\"\n | \"select\"\n | \"export\"\n | \"date-jump\"\n | \"send-via\"\n | \"palette\";\n\nexport interface PendingMessage {\n text: string;\n sentAt: Date;\n status: \"sending\" | \"sent\" | \"failed\";\n}\n\nexport interface AppState {\n conversations: Conversation[];\n messages: Message[];\n selectedIdx: number;\n selectedMsgIdx: number; // cursor position in messages list\n focus: FocusPane;\n mode: Mode;\n sidebarScroll: number;\n threadScroll: number;\n composeText: string;\n filterQuery: string;\n pending: PendingMessage[];\n loading: boolean;\n status: string;\n numBuffer: string; // for vim-style number prefix (e.g. \"12j\")\n showDevStats: boolean;\n\n // Lazy-loading bookkeeping\n conversationLoadedCount: number; // how many we've requested from the DB so far\n conversationLoadingMore: boolean; // true while a \"load more\" fetch is in-flight\n messageOldestLoadedId: number | null; // oldest message ROWID currently in `messages` (for \"load older\")\n messageLoadingOlder: boolean; // true while a \"load older\" fetch is in-flight\n\n // Bounded message window — gaps appear when middle of history is evicted\n gapMarkers: Array<{ atIdx: number; oldestId: number; newestId: number; count: number }>;\n\n // Visual selection (vim V)\n selectionAnchor: number | null; // where the user pressed V — selection extends from anchor to selectedMsgIdx\n\n // Export modal state\n exportFormat: \"markdown\" | \"csv\" | \"json\";\n exportPath: string;\n exportStatus: string; // last export status message (e.g. \"Exported 247 msgs\")\n\n // Date-jump modal state\n dateJumpInput: string;\n dateJumpError: string;\n\n // Module instances rendered as virtual rows at the top of the sidebar.\n // The sidebar cursor traverses [...moduleInstances, ...conversations].\n moduleInstances: ModuleInstance[];\n /** Index into `moduleInstances` when a virtual row is selected; null when a real conversation is selected. */\n selectedModuleIdx: number | null;\n\n // Command palette state\n paletteQuery: string;\n paletteCursor: number;\n}\n\nexport type Action =\n | { type: \"SET_CONVERSATIONS\"; data: Conversation[] }\n | { type: \"SET_MESSAGES\"; data: Message[] }\n | { type: \"SELECT\"; index: number; visibleCount?: number }\n | { type: \"SELECT_MSG\"; index: number }\n | { type: \"MOVE_MSG\"; delta: number }\n | { type: \"FOCUS\"; pane: FocusPane }\n | { type: \"SCROLL_SIDEBAR\"; delta: number }\n | { type: \"SCROLL_THREAD\"; delta: number }\n | { type: \"SCROLL_THREAD_TO\"; position: number }\n | { type: \"ENTER_COMPOSE\" }\n | { type: \"UPDATE_COMPOSE\"; text: string }\n | { type: \"CONFIRM_SEND\" }\n | { type: \"CANCEL_COMPOSE\" }\n | { type: \"ADD_PENDING\"; msg: PendingMessage }\n | { type: \"RESOLVE_PENDING\"; text: string }\n | { type: \"FAIL_PENDING\"; text: string }\n | { type: \"SET_LOADING\"; loading: boolean; status?: string }\n | { type: \"SET_STATUS\"; status: string }\n | { type: \"ENTER_FILTER\" }\n | { type: \"UPDATE_FILTER\"; query: string }\n | { type: \"EXIT_FILTER\" }\n | { type: \"OPEN_DRAWER\" }\n | { type: \"CLOSE_DRAWER\" }\n | { type: \"SET_NUM_BUFFER\"; value: string }\n | { type: \"TOGGLE_DEV_STATS\" }\n | { type: \"APPEND_CONVERSATIONS\"; data: Conversation[]; loadedCount: number }\n | { type: \"PREPEND_MESSAGES\"; data: Message[]; oldestId: number }\n | { type: \"SET_LOADING_OLDER\"; loading: boolean }\n | { type: \"ENTER_SELECT_MODE\" }\n | { type: \"EXIT_SELECT_MODE\" }\n | { type: \"ENTER_EXPORT_MODE\"; defaultPath: string }\n | { type: \"EXIT_EXPORT_MODE\" }\n | { type: \"SET_EXPORT_FORMAT\"; format: \"markdown\" | \"csv\" | \"json\" }\n | { type: \"SET_EXPORT_PATH\"; path: string }\n | { type: \"SET_EXPORT_STATUS\"; status: string }\n | { type: \"ENTER_DATE_JUMP\" }\n | { type: \"EXIT_DATE_JUMP\" }\n | { type: \"ENTER_SEND_VIA\" }\n | { type: \"EXIT_SEND_VIA\" }\n | { type: \"ENTER_COMPOSE_NEW\" }\n | { type: \"EXIT_COMPOSE_NEW\" }\n | { type: \"SET_DATE_JUMP_INPUT\"; value: string }\n | { type: \"SET_DATE_JUMP_ERROR\"; error: string }\n | { type: \"OPEN_PALETTE\" }\n | { type: \"CLOSE_PALETTE\" }\n | { type: \"SET_PALETTE_QUERY\"; query: string }\n | { type: \"MOVE_PALETTE_CURSOR\"; delta: number }\n | { type: \"SET_PALETTE_CURSOR\"; index: number }\n | { type: \"OPEN_MODULE_INSTANCE\"; instance: ModuleInstance }\n | { type: \"CLOSE_MODULE_INSTANCE\"; instanceId: string }\n | { type: \"UPDATE_MODULE_INSTANCE_STATE\"; instanceId: string; state: unknown }\n | { type: \"SELECT_MODULE\"; index: number; visibleCount?: number };\n\nexport const initialState: AppState = {\n conversations: [],\n messages: [],\n selectedIdx: 0,\n selectedMsgIdx: -1,\n focus: \"sidebar\",\n mode: \"browse\",\n sidebarScroll: 0,\n threadScroll: 0,\n composeText: \"\",\n filterQuery: \"\",\n pending: [],\n loading: true,\n status: \"Loading...\",\n numBuffer: \"\",\n showDevStats: false,\n conversationLoadedCount: 0,\n conversationLoadingMore: false,\n messageOldestLoadedId: null,\n messageLoadingOlder: false,\n gapMarkers: [],\n selectionAnchor: null,\n exportFormat: \"markdown\",\n exportPath: \"\",\n exportStatus: \"\",\n dateJumpInput: \"\",\n dateJumpError: \"\",\n moduleInstances: [],\n selectedModuleIdx: null,\n paletteQuery: \"\",\n paletteCursor: 0,\n};\n\n/** Clamp message cursor and ensure it's visible by adjusting scroll */\nfunction clampMsg(state: AppState, idx: number): Partial<AppState> {\n const total = state.messages.length;\n if (total === 0) return { selectedMsgIdx: -1 };\n const clamped = Math.max(0, Math.min(idx, total - 1));\n return { selectedMsgIdx: clamped };\n}\n\n// ── Bounded message window ───────────────────────────────────────────────\n// When loaded messages exceed HARD_CAP, evict the middle of the array but\n// preserve two regions:\n// 1. ANCHOR_KEEP at the END (most recent messages — user can press G)\n// 2. WINDOW_BUFFER ± selectedMsgIdx (current viewing window)\n// The evicted block becomes a gap marker so the renderer can show \"N more\n// messages — scroll to load\" and the user can refill on demand.\n\nconst MESSAGES_HARD_CAP = Number.parseInt(process.env.IMSG_TUI_MSG_HARD_CAP ?? \"5000\", 10);\nconst ANCHOR_KEEP = 200;\nconst WINDOW_BUFFER = 300;\n\n/**\n * Apply the bounding policy to a fresh `messages` array. Returns the trimmed\n * array, an updated `selectedMsgIdx`, and any new gap markers. Pure function.\n */\nexport function boundMessagesIfNeeded(\n messages: Message[],\n selectedMsgIdx: number,\n existingGaps: AppState[\"gapMarkers\"],\n): {\n messages: Message[];\n selectedMsgIdx: number;\n gapMarkers: AppState[\"gapMarkers\"];\n} {\n if (messages.length <= MESSAGES_HARD_CAP) {\n return { messages, selectedMsgIdx, gapMarkers: existingGaps };\n }\n\n const total = messages.length;\n // Keep the LAST ANCHOR_KEEP messages (most recent — anchor for G)\n const anchorStart = Math.max(total - ANCHOR_KEEP, 0);\n // Keep WINDOW_BUFFER on each side of the cursor\n const cursorLo = Math.max(0, selectedMsgIdx - WINDOW_BUFFER);\n const cursorHi = Math.min(total - 1, selectedMsgIdx + WINDOW_BUFFER);\n\n // Build the kept-set as a sorted list of [start, end] ranges, merged\n type Range = [number, number];\n const ranges: Range[] = [\n [cursorLo, cursorHi],\n [anchorStart, total - 1],\n ];\n // Sort by start; merge overlapping/adjacent\n ranges.sort((a, b) => a[0] - b[0]);\n const merged: Range[] = [];\n for (const r of ranges) {\n if (merged.length === 0) {\n merged.push(r);\n continue;\n }\n const last = merged[merged.length - 1];\n if (r[0] <= last[1] + 1) {\n last[1] = Math.max(last[1], r[1]);\n } else {\n merged.push(r);\n }\n }\n\n // Slice + concat the kept ranges, recording gaps between them\n const kept: Message[] = [];\n const gapMarkers: AppState[\"gapMarkers\"] = [];\n for (let i = 0; i < merged.length; i++) {\n const [start, end] = merged[i];\n if (i > 0) {\n const prevEnd = merged[i - 1][1];\n const gapStart = prevEnd + 1;\n const gapEnd = start - 1;\n if (gapEnd >= gapStart) {\n gapMarkers.push({\n atIdx: kept.length,\n oldestId: messages[gapStart].id,\n newestId: messages[gapEnd].id,\n count: gapEnd - gapStart + 1,\n });\n }\n }\n for (let j = start; j <= end; j++) kept.push(messages[j]);\n }\n\n // Map old selectedMsgIdx to new index in `kept`\n let newCursor = -1;\n if (selectedMsgIdx >= 0) {\n let collapsedIdx = 0;\n for (const [start, end] of merged) {\n if (selectedMsgIdx >= start && selectedMsgIdx <= end) {\n newCursor = collapsedIdx + (selectedMsgIdx - start);\n break;\n }\n collapsedIdx += end - start + 1;\n }\n if (newCursor === -1) newCursor = 0; // cursor was in evicted region; clamp to start\n }\n\n // Note: existingGaps from before this trim aren't re-merged; we replace.\n // In practice gaps are always recomputed from the current array shape.\n return { messages: kept, selectedMsgIdx: newCursor, gapMarkers };\n}\n\n/**\n * Compute a `sidebarScroll` value that keeps `selectedIdx` inside the\n * visible window. Adds a 2-row buffer at each edge so the user sees what's\n * coming next when navigating with j/k. Pure function — used by reducer.\n *\n * @param selectedIdx the cursor's target index\n * @param currentScroll the current sidebar scroll offset\n * @param visibleCount how many items fit on-screen at once\n * @param totalCount total items available (for clamping)\n */\nexport function ensureVisibleScroll(\n selectedIdx: number,\n currentScroll: number,\n visibleCount: number,\n totalCount: number,\n): number {\n if (visibleCount <= 0 || totalCount <= 0) return 0;\n const buffer = Math.min(2, Math.floor(visibleCount / 4));\n // If cursor is above the visible window (with buffer), scroll up\n if (selectedIdx < currentScroll + buffer) {\n return Math.max(0, selectedIdx - buffer);\n }\n // If cursor is below the visible window (with buffer), scroll down\n const lastVisible = currentScroll + visibleCount - 1;\n if (selectedIdx > lastVisible - buffer) {\n return Math.min(\n Math.max(0, totalCount - visibleCount),\n selectedIdx - visibleCount + 1 + buffer,\n );\n }\n // Already visible — don't move\n return Math.max(0, Math.min(currentScroll, Math.max(0, totalCount - visibleCount)));\n}\n\n/**\n * Total number of selectable rows in the sidebar — module instances stacked\n * on top of conversations. Used by the unified cursor model.\n */\nexport function sidebarRowCount(state: AppState): number {\n return state.moduleInstances.length + state.conversations.length;\n}\n\n/**\n * Combined sidebar cursor index ([0..moduleInstances.length-1] = modules,\n * [moduleInstances.length..] = conversations). Used to drive global j/k\n * navigation in the sidebar.\n */\nexport function combinedSidebarIndex(state: AppState): number {\n if (state.selectedModuleIdx != null) return state.selectedModuleIdx;\n return state.moduleInstances.length + state.selectedIdx;\n}\n\nexport function reducer(state: AppState, action: Action): AppState {\n switch (action.type) {\n case \"SET_CONVERSATIONS\":\n return {\n ...state,\n conversations: action.data,\n conversationLoadedCount: action.data.length,\n conversationLoadingMore: false,\n };\n case \"APPEND_CONVERSATIONS\": {\n // Merge new entries by threadSlug (DB returns the same recent ones too)\n const existing = new Set(state.conversations.map((c) => c.threadSlug));\n const fresh = action.data.filter((c) => !existing.has(c.threadSlug));\n return {\n ...state,\n conversations: [...state.conversations, ...fresh],\n conversationLoadedCount: action.loadedCount,\n conversationLoadingMore: false,\n };\n }\n case \"SET_MESSAGES\": {\n // Scroll to bottom: set cursor to last message\n const msgs = action.data;\n const lastIdx = Math.max(0, msgs.length - 1);\n // Set threadScroll high so the view shows the bottom\n const scrollToEnd = Math.max(0, msgs.length);\n const oldestId = minMessageId(msgs);\n return {\n ...state,\n messages: msgs,\n pending: [],\n selectedMsgIdx: lastIdx,\n threadScroll: scrollToEnd,\n messageOldestLoadedId: oldestId,\n messageLoadingOlder: false,\n gapMarkers: [],\n };\n }\n case \"PREPEND_MESSAGES\": {\n // Merge older messages at the start; dedup by id; preserve cursor on the\n // same logical message by shifting selectedMsgIdx by the count added.\n const existingIds = new Set(state.messages.map((m) => m.id));\n const fresh = action.data.filter((m) => !existingIds.has(m.id));\n const merged = [...fresh, ...state.messages].sort(\n (a, b) => a.date.getTime() - b.date.getTime(),\n );\n const shift = fresh.length;\n const shiftedCursor =\n state.selectedMsgIdx >= 0 ? state.selectedMsgIdx + shift : state.selectedMsgIdx;\n\n // Apply bounded-window eviction if we've grown past the cap\n const bounded = boundMessagesIfNeeded(merged, shiftedCursor, state.gapMarkers);\n\n return {\n ...state,\n messages: bounded.messages,\n selectedMsgIdx: bounded.selectedMsgIdx,\n gapMarkers: bounded.gapMarkers,\n messageOldestLoadedId: action.oldestId,\n messageLoadingOlder: false,\n };\n }\n case \"SET_LOADING_OLDER\":\n return { ...state, messageLoadingOlder: action.loading };\n case \"ENTER_SELECT_MODE\":\n return { ...state, mode: \"select\", selectionAnchor: state.selectedMsgIdx };\n case \"EXIT_SELECT_MODE\":\n return { ...state, mode: \"browse\", selectionAnchor: null };\n case \"ENTER_EXPORT_MODE\":\n return { ...state, mode: \"export\", exportPath: action.defaultPath };\n case \"EXIT_EXPORT_MODE\":\n return { ...state, mode: state.selectionAnchor != null ? \"select\" : \"browse\" };\n case \"SET_EXPORT_FORMAT\":\n return { ...state, exportFormat: action.format };\n case \"SET_EXPORT_PATH\":\n return { ...state, exportPath: action.path };\n case \"SET_EXPORT_STATUS\":\n return { ...state, exportStatus: action.status };\n case \"ENTER_DATE_JUMP\":\n return { ...state, mode: \"date-jump\", dateJumpInput: \"\", dateJumpError: \"\" };\n case \"EXIT_DATE_JUMP\":\n return { ...state, mode: \"browse\", dateJumpInput: \"\", dateJumpError: \"\" };\n case \"ENTER_SEND_VIA\":\n return { ...state, mode: \"send-via\" };\n case \"EXIT_SEND_VIA\":\n return { ...state, mode: \"browse\" };\n case \"ENTER_COMPOSE_NEW\":\n return { ...state, mode: \"compose-new\", focus: \"sidebar\" };\n case \"EXIT_COMPOSE_NEW\":\n return { ...state, mode: \"browse\" };\n case \"SET_DATE_JUMP_INPUT\":\n return { ...state, dateJumpInput: action.value, dateJumpError: \"\" };\n case \"SET_DATE_JUMP_ERROR\":\n return { ...state, dateJumpError: action.error };\n case \"SELECT\": {\n const idx = Math.max(0, Math.min(action.index, Math.max(0, state.conversations.length - 1)));\n const sidebarScroll = action.visibleCount\n ? ensureVisibleScroll(\n // The visible row index is offset by the module rows above.\n idx + state.moduleInstances.length,\n state.sidebarScroll,\n action.visibleCount,\n sidebarRowCount(state),\n )\n : state.sidebarScroll;\n // Selecting a real conversation always clears module focus.\n return { ...state, selectedIdx: idx, sidebarScroll, selectedModuleIdx: null };\n }\n case \"SELECT_MSG\": {\n const c = clampMsg(state, action.index);\n return { ...state, ...c };\n }\n case \"MOVE_MSG\": {\n const c = clampMsg(state, state.selectedMsgIdx + action.delta);\n return { ...state, ...c };\n }\n case \"FOCUS\":\n return { ...state, focus: action.pane, numBuffer: \"\" };\n case \"SCROLL_SIDEBAR\":\n return { ...state, sidebarScroll: Math.max(0, state.sidebarScroll + action.delta) };\n case \"SCROLL_THREAD\":\n return { ...state, threadScroll: Math.max(0, state.threadScroll + action.delta) };\n case \"SCROLL_THREAD_TO\":\n return { ...state, threadScroll: Math.max(0, action.position) };\n case \"ENTER_COMPOSE\":\n return { ...state, mode: \"compose\", composeText: \"\", focus: \"thread\" };\n case \"UPDATE_COMPOSE\":\n return { ...state, composeText: action.text };\n case \"CONFIRM_SEND\":\n return { ...state, mode: \"confirm\" };\n case \"CANCEL_COMPOSE\":\n return { ...state, mode: \"browse\", composeText: \"\" };\n case \"ADD_PENDING\":\n return { ...state, mode: \"browse\", composeText: \"\", pending: [...state.pending, action.msg] };\n case \"RESOLVE_PENDING\":\n return { ...state, pending: state.pending.filter((p) => p.text !== action.text) };\n case \"FAIL_PENDING\":\n return {\n ...state,\n pending: state.pending.map((p) =>\n p.text === action.text ? { ...p, status: \"failed\" as const } : p,\n ),\n };\n case \"SET_LOADING\":\n return { ...state, loading: action.loading, status: action.status ?? state.status };\n case \"SET_STATUS\":\n return { ...state, status: action.status };\n case \"ENTER_FILTER\":\n // Reset cursor + scroll to the top: the filtered list shrinks, and a\n // stale scroll offset (e.g. after `G`) would slice past every match and\n // render \"No conversations\" despite the header showing a non-zero count.\n return {\n ...state,\n mode: \"filter\",\n filterQuery: \"\",\n focus: \"sidebar\",\n selectedIdx: 0,\n selectedModuleIdx: null,\n sidebarScroll: 0,\n };\n case \"UPDATE_FILTER\":\n // Each keystroke re-filters; keep the top matches in view.\n return {\n ...state,\n filterQuery: action.query,\n selectedIdx: 0,\n selectedModuleIdx: null,\n sidebarScroll: 0,\n };\n case \"EXIT_FILTER\":\n return { ...state, mode: \"browse\", filterQuery: \"\" };\n case \"OPEN_DRAWER\":\n return { ...state, mode: \"drawer\" };\n case \"CLOSE_DRAWER\":\n return { ...state, mode: \"browse\" };\n case \"SET_NUM_BUFFER\":\n return { ...state, numBuffer: action.value };\n case \"TOGGLE_DEV_STATS\":\n return { ...state, showDevStats: !state.showDevStats };\n\n // ── Command palette ────────────────────────────────────────────────\n case \"OPEN_PALETTE\":\n return { ...state, mode: \"palette\", paletteQuery: \"\", paletteCursor: 0 };\n case \"CLOSE_PALETTE\":\n return { ...state, mode: \"browse\" };\n case \"SET_PALETTE_QUERY\":\n // Reset cursor to the top so the highlighted row is always the best\n // match for the freshest query.\n return { ...state, paletteQuery: action.query, paletteCursor: 0 };\n case \"MOVE_PALETTE_CURSOR\":\n return { ...state, paletteCursor: Math.max(0, state.paletteCursor + action.delta) };\n case \"SET_PALETTE_CURSOR\":\n return { ...state, paletteCursor: Math.max(0, action.index) };\n\n // ── Module instances ───────────────────────────────────────────────\n case \"OPEN_MODULE_INSTANCE\": {\n // Prepend so newest is at the top of the sidebar, then focus it.\n const moduleInstances = [action.instance, ...state.moduleInstances];\n return {\n ...state,\n mode: \"browse\",\n moduleInstances,\n selectedModuleIdx: 0,\n focus: \"sidebar\",\n };\n }\n case \"CLOSE_MODULE_INSTANCE\": {\n const idx = state.moduleInstances.findIndex((i) => i.id === action.instanceId);\n if (idx === -1) return state;\n const moduleInstances = state.moduleInstances.filter((_, i) => i !== idx);\n // If the removed instance was selected, fall focus back to the first\n // real conversation (or the first remaining module, if any).\n let selectedModuleIdx: number | null = state.selectedModuleIdx;\n let selectedIdx = state.selectedIdx;\n if (state.selectedModuleIdx === idx) {\n if (moduleInstances.length > 0) {\n selectedModuleIdx = Math.min(idx, moduleInstances.length - 1);\n } else {\n selectedModuleIdx = null;\n selectedIdx = 0;\n }\n } else if (state.selectedModuleIdx != null && state.selectedModuleIdx > idx) {\n selectedModuleIdx = state.selectedModuleIdx - 1;\n }\n return { ...state, moduleInstances, selectedModuleIdx, selectedIdx };\n }\n case \"UPDATE_MODULE_INSTANCE_STATE\":\n return {\n ...state,\n moduleInstances: state.moduleInstances.map((i) =>\n i.id === action.instanceId ? { ...i, state: action.state } : i,\n ),\n };\n case \"SELECT_MODULE\": {\n const idx = Math.max(\n 0,\n Math.min(action.index, Math.max(0, state.moduleInstances.length - 1)),\n );\n const sidebarScroll = action.visibleCount\n ? ensureVisibleScroll(idx, state.sidebarScroll, action.visibleCount, sidebarRowCount(state))\n : state.sidebarScroll;\n return { ...state, selectedModuleIdx: idx, sidebarScroll };\n }\n default:\n return state;\n }\n}\n","import { execSync } from \"node:child_process\";\nimport { writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { useScreenSize } from \"fullscreen-ink\";\nimport { Box, useApp, useInput } from \"ink\";\nimport { useCallback, useEffect, useMemo, useReducer, useRef } from \"react\";\nimport { registerCleanup } from \"../shutdown.js\";\nimport { minMessageId } from \"../types.js\";\nimport { getInstalledChatApps } from \"../url-schemes.js\";\nimport { CommandPalette } from \"./components/CommandPalette.js\";\nimport { ComposeRecipientModal } from \"./components/ComposeRecipientModal.js\";\nimport { DateJumpModal } from \"./components/DateJumpModal.js\";\nimport { CompactStats, DevStats } from \"./components/DevStats.js\";\nimport { ExportModal } from \"./components/ExportModal.js\";\nimport { HelpBar } from \"./components/HelpBar.js\";\nimport { MessageDrawer } from \"./components/MessageDrawer.js\";\nimport { SendViaModal } from \"./components/SendViaModal.js\";\nimport { Sidebar } from \"./components/Sidebar.js\";\nimport { StatusBar } from \"./components/StatusBar.js\";\nimport { nextGroupBoundary, prevGroupBoundary, ThreadPane } from \"./components/ThreadPane.js\";\nimport { formatJumpTarget, parseUserDate } from \"./dateParse.js\";\nimport { extensionFor, toCSV, toJSON, toMarkdown } from \"./exportFormats.js\";\nimport { firstFilterMatchIndex } from \"./filter.js\";\nimport { useDevStats } from \"./hooks/useDevStats.js\";\nimport { useImsg } from \"./hooks/useImsg.js\";\nimport { useMouse } from \"./hooks/useMouse.js\";\nimport { allCommands, findModule } from \"./modules/registry.js\";\nimport { initialState, reducer, sidebarRowCount } from \"./types.js\";\n\nexport function App() {\n const [state, dispatch] = useReducer(reducer, initialState);\n const { exit } = useApp();\n const { width: columns, height: rows } = useScreenSize();\n const imsg = useImsg();\n const { stats: devStats, recordQueryTime } = useDevStats(state.showDevStats);\n const pollTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const moveDebounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const ggPendingRef = useRef(false); // tracks if 'g' was pressed, waiting for second 'g'\n const ggTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const drawerWidth = state.mode === \"drawer\" ? Math.min(Math.floor(columns * 0.35), 50) : 0;\n const devStatsWidth = state.showDevStats ? 20 : 0;\n const sidebarWidth = Math.max(Math.floor((columns - drawerWidth - devStatsWidth) * 0.32), 28);\n const threadWidth = Math.max(columns - sidebarWidth - drawerWidth - devStatsWidth, 20);\n const bodyHeight = rows - 2; // status + help\n\n // Sidebar item layout: each item = 4 rows (name + snippet + slug + separator)\n // Header row + filter row + borders subtract from available height.\n // Mirror the calculation in Sidebar.tsx so SELECT dispatches include the\n // right visible-count and the cursor stays on screen as the user navigates.\n const SIDEBAR_ITEM_HEIGHT = 4;\n const sidebarVisibleCount = Math.max(\n Math.floor((bodyHeight - 1 - (state.filterQuery ? 1 : 0) - 2) / SIDEBAR_ITEM_HEIGHT),\n 1,\n );\n\n const selected =\n state.selectedModuleIdx == null ? state.conversations[state.selectedIdx] : undefined;\n const selectedModule =\n state.selectedModuleIdx != null ? state.moduleInstances[state.selectedModuleIdx] : undefined;\n const totalUnread = state.conversations.reduce((s, c) => s + c.unreadCount, 0);\n const resolvedNames = selected ? imsg.resolveNames(selected.participants) : [];\n const selectedMsg = state.selectedMsgIdx >= 0 ? state.messages[state.selectedMsgIdx] : undefined;\n\n // Commands list for the palette — recomputed on state change so `when`\n // gates and module command lists stay in sync.\n const commands = useMemo(() => allCommands(state), [state]);\n const commandCtx = useMemo(() => ({ state, dispatch, imsg }), [state, imsg]);\n\n // ── Data loading ───────────────────────────────────────────────────\n\n const loadMessages = useCallback(\n async (idx: number) => {\n const conv = state.conversations[idx];\n if (!conv) return;\n dispatch({\n type: \"SET_LOADING\",\n loading: true,\n status: `Loading ${conv.displayName ?? conv.chatIdentifier}...`,\n });\n const t0 = performance.now();\n const msgs = await imsg.loadMessages(conv.chatIdentifier);\n recordQueryTime(performance.now() - t0);\n dispatch({ type: \"SET_MESSAGES\", data: msgs });\n dispatch({ type: \"SET_LOADING\", loading: false, status: \"\" });\n },\n [state.conversations, imsg, recordQueryTime],\n );\n\n const refreshAll = useCallback(async () => {\n dispatch({ type: \"SET_LOADING\", loading: true, status: \"Refreshing...\" });\n const convs = await imsg.loadConversations();\n dispatch({ type: \"SET_CONVERSATIONS\", data: convs });\n if (convs.length > 0) {\n const prevSlug = selected?.threadSlug;\n if (prevSlug) {\n const idx = convs.findIndex((c) => c.threadSlug === prevSlug);\n if (idx >= 0) dispatch({ type: \"SELECT\", index: idx, visibleCount: sidebarVisibleCount });\n }\n await loadMessages(state.selectedIdx);\n }\n imsg.refresh();\n dispatch({ type: \"SET_LOADING\", loading: false, status: \"\" });\n }, [imsg, loadMessages, selected?.threadSlug, state.selectedIdx, sidebarVisibleCount]);\n\n // ── Lazy-load more conversations when user nears the end ──────────────\n const NEAR_END_THRESHOLD = 20;\n const CONV_BATCH_SIZE = 100;\n\n const loadMoreConversations = useCallback(async () => {\n if (state.conversationLoadingMore) return;\n const targetCount = state.conversationLoadedCount + CONV_BATCH_SIZE;\n dispatch({ type: \"SET_STATUS\", status: `Loading more conversations (${targetCount})...` });\n const convs = await imsg.loadConversations(targetCount);\n dispatch({ type: \"APPEND_CONVERSATIONS\", data: convs, loadedCount: targetCount });\n dispatch({ type: \"SET_STATUS\", status: \"\" });\n }, [imsg, state.conversationLoadingMore, state.conversationLoadedCount]);\n\n // ── Lazy-load older messages when cursor nears the top ────────────────\n const NEAR_TOP_THRESHOLD = 10;\n const _MSG_BATCH_SIZE = 100;\n\n const loadOlderMessages = useCallback(async () => {\n if (!selected) return;\n if (state.messageLoadingOlder) return;\n if (state.messageOldestLoadedId == null) return;\n dispatch({ type: \"SET_LOADING_OLDER\", loading: true });\n const olderMsgs = await imsg.loadOlderMessages(\n selected.chatIdentifier,\n state.messageOldestLoadedId,\n );\n if (olderMsgs.length === 0) {\n // Chat history exhausted — keep flag clear, mark id at -1 sentinel so we\n // don't keep retrying on every cursor move at the top.\n dispatch({ type: \"PREPEND_MESSAGES\", data: [], oldestId: -1 });\n return;\n }\n const newOldestId = minMessageId(olderMsgs) ?? -1;\n dispatch({ type: \"PREPEND_MESSAGES\", data: olderMsgs, oldestId: newOldestId });\n }, [imsg, selected, state.messageLoadingOlder, state.messageOldestLoadedId]);\n\n // Trigger when cursor approaches the start of the message list\n useEffect(() => {\n if (state.loading || state.messageLoadingOlder) return;\n if (state.messages.length === 0) return;\n if (state.messageOldestLoadedId === -1) return; // exhausted\n if (state.selectedMsgIdx >= 0 && state.selectedMsgIdx < NEAR_TOP_THRESHOLD) {\n loadOlderMessages();\n }\n }, [\n state.selectedMsgIdx,\n state.messages.length,\n state.messageOldestLoadedId,\n state.messageLoadingOlder,\n state.loading,\n loadOlderMessages,\n ]);\n\n // Trigger lazy-load when cursor or scroll is near the end of loaded items.\n // Important: only fire when we've actually grown — DB returns N when N requested,\n // so once `conversations.length === conversationLoadedCount` and we asked for\n // 200, asking for 300 may yield no new entries (chat history exhausted).\n useEffect(() => {\n if (state.loading || state.conversationLoadingMore) return;\n if (state.conversations.length === 0) return;\n const cursorNearEnd = state.selectedIdx >= state.conversations.length - NEAR_END_THRESHOLD;\n const scrollNearEnd =\n state.sidebarScroll + sidebarVisibleCount >= state.conversations.length - NEAR_END_THRESHOLD;\n if (cursorNearEnd || scrollNearEnd) {\n // Only ask for more if we haven't seen this chat-count plateau yet\n if (state.conversationLoadedCount === state.conversations.length) {\n loadMoreConversations();\n }\n }\n }, [\n state.selectedIdx,\n state.sidebarScroll,\n state.conversations.length,\n state.conversationLoadedCount,\n state.conversationLoadingMore,\n state.loading,\n sidebarVisibleCount,\n loadMoreConversations,\n ]);\n\n // Initial load + register cleanup\n // biome-ignore lint/correctness/useExhaustiveDependencies: we intentionally run this only on mount to prevent a render-loop\n useEffect(() => {\n registerCleanup(() => imsg.close());\n refreshAll();\n }, [imsg.close]);\n\n // ── Send logic ─────────────────────────────────────────────────────\n\n const sendMessage = useCallback(async () => {\n if (!selected || !state.composeText.trim()) return;\n const text = state.composeText.trim();\n dispatch({ type: \"ADD_PENDING\", msg: { text, sentAt: new Date(), status: \"sending\" } });\n\n // Wrap the entire send + poll setup so any thrown error surfaces as a\n // FAIL_PENDING in the UI rather than escaping as an unhandled rejection.\n // Pre-fix, an osascript / DB exception here would log silently and the\n // pending bubble would just sit in \"sending\" forever, which read as\n // \"the tool exited\" when combined with a phantom watchdog kill.\n try {\n const result = await imsg.send(selected.threadSlug, text);\n if (!result.success) {\n dispatch({ type: \"FAIL_PENDING\", text });\n return;\n }\n\n let attempt = 0;\n const poll = async () => {\n // Check if we are still polling for this request (unmounted or cancelled)\n if (!pollTimerRef.current) return;\n attempt++;\n try {\n const msgs = await imsg.loadMessages(selected.chatIdentifier);\n const found = msgs.some((m) => m.isFromMe && m.text?.includes(text));\n if (found) {\n dispatch({ type: \"SET_MESSAGES\", data: msgs });\n dispatch({ type: \"RESOLVE_PENDING\", text });\n } else if (attempt < 7) {\n pollTimerRef.current = setTimeout(poll, 1500);\n }\n } catch {\n // Poll iteration failed (DB locked, transient I/O); drop the\n // pending state rather than spin forever or leak the rejection.\n dispatch({ type: \"FAIL_PENDING\", text });\n }\n };\n if (pollTimerRef.current) clearTimeout(pollTimerRef.current);\n pollTimerRef.current = setTimeout(poll, 1500);\n } catch {\n dispatch({ type: \"FAIL_PENDING\", text });\n }\n }, [selected, state.composeText, imsg]);\n\n /**\n * Move the sidebar cursor to a combined index that spans\n * [modules..., conversations...]. Dispatches to the right reducer action\n * based on which region the target lands in, and only fires `loadMessages`\n * when a real conversation is selected.\n */\n const selectSidebarCombined = useCallback(\n (combinedIdx: number) => {\n const moduleCount = state.moduleInstances.length;\n if (combinedIdx < moduleCount) {\n dispatch({\n type: \"SELECT_MODULE\",\n index: combinedIdx,\n visibleCount: sidebarVisibleCount,\n });\n return;\n }\n const convIdx = combinedIdx - moduleCount;\n dispatch({ type: \"SELECT\", index: convIdx, visibleCount: sidebarVisibleCount });\n // Debounce message loading (matches existing behaviour).\n if (moveDebounceRef.current) clearTimeout(moveDebounceRef.current);\n const target = convIdx;\n moveDebounceRef.current = setTimeout(() => {\n moveDebounceRef.current = null;\n loadMessages(target);\n }, 80);\n },\n [state.moduleInstances.length, sidebarVisibleCount, loadMessages],\n );\n\n // ── Vim number prefix helper ───────────────────────────────────────\n\n const getCount = useCallback((): number => {\n const n = state.numBuffer ? Number.parseInt(state.numBuffer, 10) : 1;\n dispatch({ type: \"SET_NUM_BUFFER\", value: \"\" });\n return Math.max(1, Math.min(n, 999));\n }, [state.numBuffer]);\n\n // ── Keyboard ───────────────────────────────────────────────────────\n\n useInput(async (input, key) => {\n // Ctrl-C always exits\n if (key.ctrl && input === \"c\") {\n await imsg.close();\n exit();\n return;\n }\n\n // Palette mode — owns its own useInput (inside CommandPalette).\n // Skip all App-level handling so navigation keys don't double-fire.\n if (state.mode === \"palette\") {\n return;\n }\n\n // Ctrl-P opens the palette from anywhere outside an active modal.\n if (key.ctrl && input === \"p\" && (state.mode === \"browse\" || state.mode === \"select\")) {\n dispatch({ type: \"OPEN_PALETTE\" });\n return;\n }\n\n // `?` from browse mode opens the palette as a keybinding cheat sheet.\n if (input === \"?\" && !key.ctrl && !key.meta && state.mode === \"browse\") {\n dispatch({ type: \"OPEN_PALETTE\" });\n return;\n }\n\n // Module pane mode — when a module instance is selected and thread pane is\n // focused, the pane's own useInput owns everything (Tab cycles type, [/]\n // cycles range, Esc closes). Tab MUST NOT be intercepted here: the pane\n // advertises \"Tab:type\" in its footer and the PR contract documents Tab as\n // the type-cycle key. Previously this branch dispatched FOCUS:sidebar on\n // Tab, which silently overrode the pane's handler.\n if (selectedModule && state.focus === \"thread\" && state.mode === \"browse\") {\n // Shift-Tab returns focus to the sidebar so the user can navigate to\n // another conversation without closing the module. Regular Tab and\n // every other key falls through to the pane's own useInput.\n if (key.shift && key.tab) {\n dispatch({ type: \"FOCUS\", pane: \"sidebar\" });\n return;\n }\n return;\n }\n\n // Filter mode\n if (state.mode === \"filter\") {\n if (key.return) {\n // Enter commits the filter: jump cursor to the first matching\n // conversation, then exit filter. Esc-only path below preserves the\n // pre-filter cursor (cancel semantics). Previously both keys exited\n // without navigating, which made the filter feel inert.\n const matchIdx = firstFilterMatchIndex(state.conversations, state.filterQuery);\n if (matchIdx !== null) {\n dispatch({ type: \"SELECT\", index: matchIdx });\n }\n dispatch({ type: \"EXIT_FILTER\" });\n } else if (key.escape) {\n dispatch({ type: \"EXIT_FILTER\" });\n } else if (key.backspace || key.delete) {\n dispatch({ type: \"UPDATE_FILTER\", query: state.filterQuery.slice(0, -1) });\n } else if (input && !key.ctrl && !key.meta) {\n dispatch({ type: \"UPDATE_FILTER\", query: state.filterQuery + input });\n }\n return;\n }\n\n // Compose mode\n if (state.mode === \"compose\") {\n if (key.escape) {\n dispatch({ type: \"CANCEL_COMPOSE\" });\n } else if (key.return && state.composeText.trim()) {\n dispatch({ type: \"CONFIRM_SEND\" });\n }\n return;\n }\n\n // Confirm mode\n if (state.mode === \"confirm\") {\n if (key.return) {\n await sendMessage();\n } else {\n dispatch({ type: \"CANCEL_COMPOSE\" });\n }\n return;\n }\n\n // Drawer mode\n if (state.mode === \"drawer\") {\n if (key.escape || input === \"q\") {\n dispatch({ type: \"CLOSE_DRAWER\" });\n } else if (input === \"o\" && selectedMsg) {\n // Open attachment in external viewer\n openAttachment(selectedMsg);\n }\n return;\n }\n\n // Date-jump modal mode — Esc cancels; Enter handled by TextInput.onSubmit\n if (state.mode === \"date-jump\") {\n if (key.escape) {\n dispatch({ type: \"EXIT_DATE_JUMP\" });\n }\n return;\n }\n\n // Send-via modal: digit picks an app + launches its URL-scheme deep link.\n if (state.mode === \"send-via\") {\n if (key.escape) {\n dispatch({ type: \"EXIT_SEND_VIA\" });\n return;\n }\n if (input && /^[1-9]$/.test(input) && selected) {\n const apps = getInstalledChatApps();\n const idx = Number.parseInt(input, 10) - 1;\n const app = apps[idx];\n if (app) {\n const lastMsgText = state.messages.length\n ? (state.messages[state.messages.length - 1]?.text ?? undefined)\n : undefined;\n const built = app.buildUri(selected.chatIdentifier, lastMsgText);\n if (built) {\n (await import(\"node:child_process\"))\n .spawn(\"open\", [built], { detached: true, stdio: \"ignore\" })\n .unref();\n dispatch({ type: \"SET_STATUS\", status: `Launched ${app.name}` });\n } else {\n dispatch({\n type: \"SET_STATUS\",\n status: `${app.name}: handle not compatible with this scheme`,\n });\n }\n }\n dispatch({ type: \"EXIT_SEND_VIA\" });\n setTimeout(() => dispatch({ type: \"SET_STATUS\", status: \"\" }), 2500);\n }\n return;\n }\n\n // Export-modal mode — Tab cycles format, Esc cancels.\n // Path text input + Enter handled by the inline TextInput inside the modal.\n if (state.mode === \"export\") {\n if (key.escape) {\n dispatch({ type: \"EXIT_EXPORT_MODE\" });\n } else if (key.tab) {\n const order: Array<\"markdown\" | \"csv\" | \"json\"> = [\"markdown\", \"csv\", \"json\"];\n const next = order[(order.indexOf(state.exportFormat) + 1) % order.length];\n dispatch({ type: \"SET_EXPORT_FORMAT\", format: next });\n // Update path extension to match new format\n const stripped = state.exportPath.replace(/\\.(md|csv|json)$/, \"\");\n dispatch({ type: \"SET_EXPORT_PATH\", path: `${stripped}.${extensionFor(next)}` });\n }\n return;\n }\n\n // Select mode — V'd. j/k extend, Esc exits, e opens export modal,\n // y copies selected text to clipboard.\n if (state.mode === \"select\") {\n if (key.escape) {\n dispatch({ type: \"EXIT_SELECT_MODE\" });\n return;\n }\n if (input === \"e\") {\n const slug = selected?.threadSlug ?? \"messages\";\n const stamp = new Date().toISOString().replace(/[:.]/g, \"-\").slice(0, 19);\n const defaultPath = join(homedir(), `imsg-export-${slug}-${stamp}.md`);\n dispatch({ type: \"ENTER_EXPORT_MODE\", defaultPath });\n return;\n }\n if (input === \"y\" && state.selectionAnchor != null) {\n const [lo, hi] = [\n Math.min(state.selectionAnchor, state.selectedMsgIdx),\n Math.max(state.selectionAnchor, state.selectedMsgIdx),\n ];\n const text = state.messages\n .slice(lo, hi + 1)\n .map(\n (m) =>\n `[${m.date.toISOString()}] ${m.isFromMe ? \"Me\" : (m.displayName ?? m.handle)}: ${m.text ?? \"(no text)\"}`,\n )\n .join(\"\\n\");\n try {\n execSync(\"pbcopy\", { input: text });\n dispatch({ type: \"SET_STATUS\", status: `Copied ${hi - lo + 1} msgs` });\n } catch {\n dispatch({ type: \"SET_STATUS\", status: \"Copy failed\" });\n }\n setTimeout(() => dispatch({ type: \"SET_STATUS\", status: \"\" }), 2000);\n return;\n }\n // j/k/G/gg/Ctrl-d/Ctrl-u — fall through to browse-mode movement (anchor stays)\n }\n\n // Browse mode\n if (input === \"d\" && !key.ctrl && !key.meta && state.mode === \"browse\") {\n dispatch({ type: \"TOGGLE_DEV_STATS\" });\n return;\n }\n if (input === \"V\" && state.focus === \"thread\" && state.selectedMsgIdx >= 0) {\n dispatch({ type: \"ENTER_SELECT_MODE\" });\n return;\n }\n if (input === \":\" && state.focus === \"thread\" && state.mode === \"browse\") {\n dispatch({ type: \"ENTER_DATE_JUMP\" });\n return;\n }\n // O — open the current thread in Messages.app via the imessage:// URL scheme.\n // For 1:1 chats this focuses or composes that thread. Groups have no URL\n // scheme — we fall back to AppleScript activate.\n if (\n input === \"O\" &&\n !key.ctrl &&\n !key.meta &&\n state.focus === \"thread\" &&\n state.mode === \"browse\" &&\n selected\n ) {\n const handle = selected.chatIdentifier;\n const uri = `imessage://${encodeURIComponent(handle)}`;\n (await import(\"node:child_process\"))\n .spawn(\"open\", [uri], { detached: true, stdio: \"ignore\" })\n .unref();\n dispatch({ type: \"SET_STATUS\", status: `Opened ${handle} in Messages.app` });\n setTimeout(() => dispatch({ type: \"SET_STATUS\", status: \"\" }), 2500);\n return;\n }\n // S — send-via picker: list installed external chat apps and let the user\n // launch one. Body is the most recent message text in the thread (best-\n // effort context); the URI carries it only on schemes that support body.\n if (\n input === \"S\" &&\n !key.ctrl &&\n !key.meta &&\n state.focus === \"thread\" &&\n state.mode === \"browse\" &&\n selected\n ) {\n dispatch({ type: \"ENTER_SEND_VIA\" });\n return;\n }\n if (input === \"q\") {\n await imsg.close();\n exit();\n return;\n }\n if (input === \"r\") {\n await refreshAll();\n return;\n }\n // N (uppercase) opens compose-to-new-thread from anywhere in browse mode.\n // Distinct from `c` which composes WITHIN the currently-selected thread.\n if (input === \"N\" && state.mode === \"browse\") {\n dispatch({ type: \"ENTER_COMPOSE_NEW\" });\n return;\n }\n if (input === \"c\" || (key.return && state.focus === \"thread\" && state.mode === \"browse\")) {\n if (state.focus === \"thread\" && key.return && state.selectedMsgIdx >= 0) {\n // Enter on a message opens drawer\n dispatch({ type: \"OPEN_DRAWER\" });\n return;\n }\n // `c` from sidebar with no selected thread → fall through to compose-new\n // so the user always gets a meaningful action.\n if (state.focus === \"sidebar\" && !selected) {\n dispatch({ type: \"ENTER_COMPOSE_NEW\" });\n return;\n }\n dispatch({ type: \"ENTER_COMPOSE\" });\n return;\n }\n if (input === \"/\" && state.mode === \"browse\") {\n dispatch({ type: \"ENTER_FILTER\" });\n return;\n }\n if (key.tab) {\n dispatch({ type: \"FOCUS\", pane: state.focus === \"sidebar\" ? \"thread\" : \"sidebar\" });\n return;\n }\n\n if (state.loading) return;\n\n // Number buffer for vim-style counts (e.g. \"12j\" to jump 12 lines)\n if (input && input >= \"0\" && input <= \"9\" && !key.ctrl && !key.meta) {\n // Don't buffer leading zeros unless building a number\n if (input === \"0\" && !state.numBuffer) {\n // '0' alone: go to first item (like vim)\n if (state.focus === \"sidebar\") {\n selectSidebarCombined(0);\n } else {\n dispatch({ type: \"SELECT_MSG\", index: 0 });\n }\n return;\n }\n dispatch({ type: \"SET_NUM_BUFFER\", value: state.numBuffer + input });\n return;\n }\n\n if (state.focus === \"sidebar\") {\n // Copy thread slug to clipboard (only meaningful for real conversations)\n if (input === \"y\" && selected) {\n try {\n execSync(\"pbcopy\", { input: `~${selected.threadSlug}` });\n dispatch({ type: \"SET_STATUS\", status: `Copied ~${selected.threadSlug}` });\n setTimeout(() => dispatch({ type: \"SET_STATUS\", status: \"\" }), 2000);\n } catch {\n dispatch({ type: \"SET_STATUS\", status: \"Failed to copy\" });\n }\n return;\n }\n\n const count = getCount();\n const totalRows = sidebarRowCount(state);\n const lastIdx = Math.max(0, totalRows - 1);\n const curIdx =\n state.selectedModuleIdx != null\n ? state.selectedModuleIdx\n : state.moduleInstances.length + state.selectedIdx;\n let next: number | null = null;\n\n if (input === \"j\" || key.downArrow) {\n next = Math.min(curIdx + count, lastIdx);\n } else if (input === \"k\" || key.upArrow) {\n next = Math.max(curIdx - count, 0);\n } else if (input === \"G\") {\n next = lastIdx;\n } else if (input === \"g\") {\n // Handle gg (go to top)\n if (ggPendingRef.current) {\n ggPendingRef.current = false;\n if (ggTimerRef.current) clearTimeout(ggTimerRef.current);\n next = 0;\n } else {\n ggPendingRef.current = true;\n // 350ms is the typical vim chord window — feels snappy without\n // false-positive-firing on a slow second keypress. Was 500ms.\n ggTimerRef.current = setTimeout(() => {\n ggPendingRef.current = false;\n }, 350);\n return;\n }\n } else if (key.ctrl && input === \"d\") {\n next = Math.min(curIdx + Math.floor(bodyHeight / 2), lastIdx);\n } else if (key.ctrl && input === \"u\") {\n next = Math.max(curIdx - Math.floor(bodyHeight / 2), 0);\n } else if ((key.ctrl && input === \"f\") || key.pageDown) {\n next = Math.min(curIdx + bodyHeight, lastIdx);\n } else if ((key.ctrl && input === \"b\") || key.pageUp) {\n next = Math.max(curIdx - bodyHeight, 0);\n } else if (input === \"H\") {\n next = state.sidebarScroll;\n } else if (input === \"M\") {\n next = Math.min(state.sidebarScroll + Math.floor(bodyHeight / 2), lastIdx);\n } else if (input === \"L\") {\n next = Math.min(state.sidebarScroll + bodyHeight - 1, lastIdx);\n }\n\n if (next !== null && next !== curIdx) {\n selectSidebarCombined(next);\n }\n } else {\n // Thread focus — message cursor movement\n const count = getCount();\n\n if (input === \"j\" || key.downArrow) {\n dispatch({ type: \"MOVE_MSG\", delta: count });\n } else if (input === \"k\" || key.upArrow) {\n dispatch({ type: \"MOVE_MSG\", delta: -count });\n } else if (input === \"G\") {\n dispatch({ type: \"SELECT_MSG\", index: state.messages.length - 1 });\n } else if (input === \"g\") {\n if (ggPendingRef.current) {\n ggPendingRef.current = false;\n if (ggTimerRef.current) clearTimeout(ggTimerRef.current);\n dispatch({ type: \"SELECT_MSG\", index: 0 });\n } else {\n ggPendingRef.current = true;\n // 350ms is the typical vim chord window — feels snappy without\n // false-positive-firing on a slow second keypress. Was 500ms.\n ggTimerRef.current = setTimeout(() => {\n ggPendingRef.current = false;\n }, 350);\n }\n } else if (key.ctrl && input === \"d\") {\n dispatch({ type: \"MOVE_MSG\", delta: Math.floor(bodyHeight / 2) });\n } else if (key.ctrl && input === \"u\") {\n dispatch({ type: \"MOVE_MSG\", delta: -Math.floor(bodyHeight / 2) });\n } else if ((key.ctrl && input === \"f\") || key.pageDown) {\n dispatch({ type: \"MOVE_MSG\", delta: bodyHeight });\n } else if ((key.ctrl && input === \"b\") || key.pageUp) {\n dispatch({ type: \"MOVE_MSG\", delta: -bodyHeight });\n } else if (input === \"H\") {\n // Jump to top of visible area (approximate)\n const visibleTop = Math.max(0, state.selectedMsgIdx - Math.floor(bodyHeight * 0.7));\n dispatch({ type: \"SELECT_MSG\", index: visibleTop });\n } else if (input === \"L\") {\n const visibleBottom = Math.min(\n state.messages.length - 1,\n state.selectedMsgIdx + Math.floor(bodyHeight * 0.3),\n );\n dispatch({ type: \"SELECT_MSG\", index: visibleBottom });\n } else if (input === \"M\") {\n // Stay at current (middle)\n } else if (input === \"}\" || input === \"]\") {\n // Jump to next sender group\n const next = nextGroupBoundary(state.messages, state.selectedMsgIdx);\n dispatch({ type: \"SELECT_MSG\", index: next });\n } else if (input === \"{\" || input === \"[\") {\n // Jump to previous sender group\n const prev = prevGroupBoundary(state.messages, state.selectedMsgIdx);\n dispatch({ type: \"SELECT_MSG\", index: prev });\n } else if (input === \"o\") {\n // Open attachment for selected message. Always call so the no-msg/\n // no-attachment toast fires when the user presses `o` without a\n // valid selection — silent no-op is confusing UX.\n openAttachment(\n state.selectedMsgIdx >= 0 ? state.messages[state.selectedMsgIdx] : undefined,\n );\n }\n }\n });\n\n // ── Date jump ──────────────────────────────────────────────────────\n\n const MAX_JUMP_BATCHES = 100; // bounded loop: 100 × 100 = 10,000 messages\n\n const doDateJump = useCallback(\n async (input: string) => {\n const target = parseUserDate(input);\n if (!target) {\n dispatch({\n type: \"SET_DATE_JUMP_ERROR\",\n error: `Could not parse \"${input}\". Try YYYY-MM-DD or \"1 week ago\".`,\n });\n return;\n }\n if (!selected) {\n dispatch({ type: \"EXIT_DATE_JUMP\" });\n return;\n }\n let batches = 0;\n // Loop: load older messages until oldest <= target, or exhausted\n while (batches < MAX_JUMP_BATCHES) {\n const oldest = state.messages.length > 0 ? state.messages[0].date : new Date();\n if (oldest <= target) break;\n if (state.messageOldestLoadedId == null || state.messageOldestLoadedId === -1) break;\n const older = await imsg.loadOlderMessages(\n selected.chatIdentifier,\n state.messageOldestLoadedId,\n );\n if (older.length === 0) break;\n const newOldestId = minMessageId(older) ?? -1;\n dispatch({ type: \"PREPEND_MESSAGES\", data: older, oldestId: newOldestId });\n batches++;\n // Yield to render between batches\n await new Promise((r) => setTimeout(r, 10));\n }\n // Find first message at or after target\n const idx = state.messages.findIndex((m) => m.date >= target);\n if (idx >= 0) {\n dispatch({ type: \"SELECT_MSG\", index: idx });\n }\n dispatch({ type: \"EXIT_DATE_JUMP\" });\n dispatch({\n type: \"SET_STATUS\",\n status:\n batches >= MAX_JUMP_BATCHES\n ? `Jumped (capped) — load more manually for older history`\n : `Jumped to ${formatJumpTarget(target)}`,\n });\n setTimeout(() => dispatch({ type: \"SET_STATUS\", status: \"\" }), 4000);\n },\n [imsg, selected, state.messages, state.messageOldestLoadedId],\n );\n\n // ── Export action ──────────────────────────────────────────────────\n\n const doExport = useCallback(() => {\n const messagesToExport =\n state.selectionAnchor != null\n ? state.messages.slice(\n Math.min(state.selectionAnchor, state.selectedMsgIdx),\n Math.max(state.selectionAnchor, state.selectedMsgIdx) + 1,\n )\n : state.messages;\n if (messagesToExport.length === 0) {\n dispatch({ type: \"SET_STATUS\", status: \"Nothing to export\" });\n return;\n }\n const expandedPath = state.exportPath.replace(/^~/, homedir());\n try {\n let content: string;\n const header = {\n thread: selected?.displayName ?? selected?.chatIdentifier ?? \"thread\",\n participants: selected?.participants ?? [],\n serviceType: selected?.serviceType,\n };\n switch (state.exportFormat) {\n case \"markdown\":\n content = toMarkdown(messagesToExport, header);\n break;\n case \"csv\":\n content = toCSV(messagesToExport);\n break;\n case \"json\":\n content = toJSON(messagesToExport, header);\n break;\n }\n writeFileSync(expandedPath, content, \"utf8\");\n dispatch({ type: \"EXIT_EXPORT_MODE\" });\n dispatch({ type: \"EXIT_SELECT_MODE\" });\n dispatch({\n type: \"SET_STATUS\",\n status: `Exported ${messagesToExport.length} msgs to ${expandedPath}`,\n });\n setTimeout(() => dispatch({ type: \"SET_STATUS\", status: \"\" }), 4000);\n } catch (err) {\n dispatch({\n type: \"SET_STATUS\",\n status: `Export failed: ${err instanceof Error ? err.message : String(err)}`,\n });\n }\n }, [\n state.exportFormat,\n state.exportPath,\n state.messages,\n state.selectedMsgIdx,\n state.selectionAnchor,\n selected,\n ]);\n\n // ── Open attachment ────────────────────────────────────────────────\n\n function openAttachment(msg: import(\"../types.js\").Message | undefined) {\n // Surface UX feedback when `o` can't do anything — previously this\n // silently no-op'd, leaving the user wondering if the key worked.\n if (!msg) {\n dispatch({ type: \"SET_STATUS\", status: \"No message selected.\" });\n return;\n }\n if (!msg.attachments?.length) {\n dispatch({ type: \"SET_STATUS\", status: \"No attachment on this message.\" });\n return;\n }\n const att = msg.attachments[0];\n if (!att.filename) {\n dispatch({ type: \"SET_STATUS\", status: \"Attachment has no file path.\" });\n return;\n }\n // Expand ~ to home directory\n const filepath = att.filename.replace(/^~/, process.env.HOME ?? \"~\");\n const mime = att.mimeType ?? \"\";\n\n // macOS Quick Look (qlmanage -p) handles images, PDFs, audio, docs, and\n // most archives natively — same UX as Finder spacebar preview. For video,\n // mpv is preferred when installed (better scrubbing); otherwise fall back\n // to Quick Look. All spawns are detached + unref'd so the TUI never blocks.\n import(\"node:child_process\").then(({ spawn }) => {\n const spawnQuickLook = () =>\n spawn(\"qlmanage\", [\"-p\", filepath], { detached: true, stdio: \"ignore\" }).unref();\n\n if (mime.startsWith(\"video/\")) {\n const child = spawn(\"mpv\", [filepath], { detached: true, stdio: \"ignore\" });\n child.on(\"error\", spawnQuickLook);\n child.unref();\n } else {\n spawnQuickLook();\n }\n });\n }\n\n // ── Mouse ──────────────────────────────────────────────────────────\n\n const handleMouse = useCallback(\n (event: { type: string; x: number; y: number }) => {\n if (event.type === \"click\") {\n if (event.x <= sidebarWidth) {\n dispatch({ type: \"FOCUS\", pane: \"sidebar\" });\n // Combined index covers [...moduleInstances, ...conversations].\n const combinedIdx = Math.floor((event.y - 2 + state.sidebarScroll * 3) / 3);\n const moduleCount = state.moduleInstances.length;\n if (combinedIdx >= 0 && combinedIdx < moduleCount + state.conversations.length) {\n selectSidebarCombined(combinedIdx);\n }\n } else {\n dispatch({ type: \"FOCUS\", pane: \"thread\" });\n }\n } else if (event.type === \"scroll-up\") {\n // Wheel delta: 1 line per event for fine-grained control. macOS / iTerm\n // typically batch 2-3 wheel events per touchpad swipe so this still feels\n // responsive in practice.\n if (event.x <= sidebarWidth) {\n dispatch({ type: \"SCROLL_SIDEBAR\", delta: -1 });\n } else {\n dispatch({ type: \"MOVE_MSG\", delta: -1 });\n }\n } else if (event.type === \"scroll-down\") {\n if (event.x <= sidebarWidth) {\n dispatch({ type: \"SCROLL_SIDEBAR\", delta: 1 });\n } else {\n dispatch({ type: \"MOVE_MSG\", delta: 1 });\n }\n }\n },\n [\n sidebarWidth,\n state.sidebarScroll,\n state.conversations.length,\n state.moduleInstances.length,\n selectSidebarCombined,\n ],\n );\n\n useMouse(handleMouse);\n\n // ── Cleanup ────────────────────────────────────────────────────────\n\n useEffect(() => {\n return () => {\n if (pollTimerRef.current) clearTimeout(pollTimerRef.current);\n if (moveDebounceRef.current) clearTimeout(moveDebounceRef.current);\n if (ggTimerRef.current) clearTimeout(ggTimerRef.current);\n };\n }, []);\n\n // ── Render ─────────────────────────────────────────────────────────\n\n // Resolve the module + its pane renderer when a virtual row is selected.\n const selectedModuleDef = selectedModule ? findModule(selectedModule.moduleId) : undefined;\n const ModulePane = selectedModuleDef?.Pane;\n\n return (\n <Box flexDirection=\"column\" height={rows} width={columns}>\n {/* Main layout */}\n <Box flexGrow={1} height={bodyHeight}>\n <Sidebar\n conversations={state.conversations}\n moduleInstances={state.moduleInstances}\n selectedIdx={state.selectedIdx}\n selectedModuleIdx={state.selectedModuleIdx}\n scrollOffset={state.sidebarScroll}\n filterQuery={state.filterQuery}\n focused={state.focus === \"sidebar\"}\n width={sidebarWidth}\n height={bodyHeight}\n />\n {selectedModule && ModulePane ? (\n <ModulePane\n instance={selectedModule}\n imsg={imsg}\n width={threadWidth}\n height={bodyHeight}\n focused={state.focus === \"thread\"}\n onUpdateState={(next) =>\n dispatch({\n type: \"UPDATE_MODULE_INSTANCE_STATE\",\n instanceId: selectedModule.id,\n state: next,\n })\n }\n onClose={() =>\n dispatch({ type: \"CLOSE_MODULE_INSTANCE\", instanceId: selectedModule.id })\n }\n setStatus={(s) => {\n dispatch({ type: \"SET_STATUS\", status: s });\n setTimeout(() => dispatch({ type: \"SET_STATUS\", status: \"\" }), 2500);\n }}\n />\n ) : (\n <ThreadPane\n conversation={selected}\n messages={state.messages}\n pending={state.pending}\n resolvedNames={resolvedNames}\n scrollOffset={state.threadScroll}\n selectedMsgIdx={state.selectedMsgIdx}\n selectionAnchor={state.selectionAnchor}\n gapMarkers={state.gapMarkers}\n focused={state.focus === \"thread\"}\n width={threadWidth}\n height={bodyHeight}\n mode={state.mode}\n onChangeCompose={(text) => dispatch({ type: \"UPDATE_COMPOSE\", text })}\n onSubmitCompose={(text) => text.trim() && dispatch({ type: \"CONFIRM_SEND\" })}\n />\n )}\n {state.mode === \"drawer\" && selectedMsg && (\n <MessageDrawer message={selectedMsg} width={drawerWidth} height={bodyHeight} />\n )}\n {state.showDevStats && <DevStats stats={devStats} width={devStatsWidth} />}\n </Box>\n\n {/* Date-jump modal */}\n {state.mode === \"date-jump\" && (\n <DateJumpModal\n value={state.dateJumpInput}\n error={state.dateJumpError}\n onChange={(v) => dispatch({ type: \"SET_DATE_JUMP_INPUT\", value: v })}\n onSubmit={(v) => doDateJump(v)}\n />\n )}\n\n {/* Send-via picker — launch external chat apps for the current thread */}\n {state.mode === \"send-via\" && selected && (\n <SendViaModal handle={selected.chatIdentifier} apps={getInstalledChatApps()} />\n )}\n\n {/* Compose-to-new-thread modal — `N` (or `c` from sidebar with no\n selected thread). Two-stage: recipient input → message body. */}\n {state.mode === \"compose-new\" && (\n <ComposeRecipientModal\n resolve={imsg.resolveRecipientInput}\n onSend={async (handle, text) => {\n const result = await imsg.sendToRecipient(handle, text);\n if (result.success) {\n dispatch({\n type: \"SET_STATUS\",\n status: `Sent to ${handle}`,\n });\n // Trigger a refresh so the new conversation shows up in the sidebar.\n await refreshAll();\n }\n return result;\n }}\n onCancel={() => dispatch({ type: \"EXIT_COMPOSE_NEW\" })}\n />\n )}\n\n {/* Export modal — overlays the bottom of the body when active */}\n {state.mode === \"export\" && (\n <ExportModal\n format={state.exportFormat}\n path={state.exportPath}\n rangeSummary={(() => {\n if (state.selectionAnchor != null) {\n const lo = Math.min(state.selectionAnchor, state.selectedMsgIdx);\n const hi = Math.max(state.selectionAnchor, state.selectedMsgIdx);\n const n = hi - lo + 1;\n return `${n} selected message${n === 1 ? \"\" : \"s\"}`;\n }\n const m = state.messages.length;\n return `entire loaded thread (${m} message${m === 1 ? \"\" : \"s\"})`;\n })()}\n onChangePath={(p) => dispatch({ type: \"SET_EXPORT_PATH\", path: p })}\n onSubmit={doExport}\n />\n )}\n\n {/* Command palette — overlay modal, Ctrl-P / ? from browse mode. */}\n {state.mode === \"palette\" && (\n <CommandPalette\n commands={commands}\n query={state.paletteQuery}\n cursor={state.paletteCursor}\n width={columns}\n height={rows}\n ctx={commandCtx}\n onQueryChange={(q) => dispatch({ type: \"SET_PALETTE_QUERY\", query: q })}\n onCursorMove={(d) => dispatch({ type: \"MOVE_PALETTE_CURSOR\", delta: d })}\n onSelectCursor={(i) => dispatch({ type: \"SET_PALETTE_CURSOR\", index: i })}\n onClose={() => dispatch({ type: \"CLOSE_PALETTE\" })}\n />\n )}\n\n {/* Status + Help */}\n <StatusBar\n totalUnread={totalUnread}\n selected={selected}\n status={state.status}\n loading={state.loading}\n >\n {!state.showDevStats && <CompactStats stats={devStats} />}\n </StatusBar>\n <HelpBar mode={state.mode} focus={state.focus} />\n </Box>\n );\n}\n","import { parseArgs } from \"node:util\";\nimport { withFullScreen } from \"fullscreen-ink\";\nimport { checkLocalAccess, formatAccessReport } from \"../access-check.js\";\nimport { detectNerdFont } from \"../font-detect.js\";\nimport {\n enableFileLogging,\n logShutdown,\n logStartup,\n startHeapMonitor,\n stopHeapMonitor,\n} from \"../logger.js\";\nimport { installShutdownHandlers, registerCleanup } from \"../shutdown.js\";\nimport { resolveTuiConfig } from \"../tui-config.js\";\nimport { installWatchdog } from \"../watchdog.js\";\nimport { App } from \"./App.js\";\nimport { clearCache, installCacheSweepers, stopCacheSweepers } from \"./messageCache.js\";\nimport { makeTheme } from \"./theme.js\";\nimport { ThemeProvider } from \"./themes/ThemeContext.js\";\n\n/**\n * Build the warning we print when the user picked `powerline` but no\n * Nerd Font is detected. Pure function so tests can lock the wording.\n */\nexport function buildPowerlineFontWarning(\n detection: ReturnType<typeof detectNerdFont>,\n): string | null {\n if (detection.source === \"fc-list\" && detection.detected === false) {\n return (\n \"warn: powerline theme selected but no Nerd Font was detected.\\n\" +\n \" Glyphs will render blank. Either install one (https://www.nerdfonts.com)\\n\" +\n \" or switch with: imsg --theme=safe (or `imsg config edit`)\"\n );\n }\n if (detection.source === \"unavailable\") {\n return (\n \"warn: powerline theme selected; could not auto-detect a Nerd Font (no fc-list).\\n\" +\n \" If glyphs render blank, install one (https://www.nerdfonts.com) or\\n\" +\n \" switch with: imsg --theme=safe\"\n );\n }\n return null;\n}\n\n/** Parse `--theme` and `--accent` from the TUI command line. Unknown\n * flags don't raise — the TUI accepts no other options today. */\nfunction parseTuiCliArgs(): { theme?: string; accent?: string } {\n try {\n const { values } = parseArgs({\n args: process.argv.slice(2),\n options: {\n theme: { type: \"string\" },\n accent: { type: \"string\" },\n },\n strict: false,\n allowPositionals: true,\n });\n return {\n theme: typeof values.theme === \"string\" ? values.theme : undefined,\n accent: typeof values.accent === \"string\" ? values.accent : undefined,\n };\n } catch {\n return {};\n }\n}\n\nexport async function runTui(): Promise<void> {\n if (!process.stdin.isTTY || !process.stdout.isTTY) {\n throw new Error(\"The TUI requires an interactive terminal (TTY).\");\n }\n\n const report = await checkLocalAccess();\n if (!report.ok) {\n console.log(formatAccessReport(report));\n process.exit(1);\n }\n\n // Resolve theme / accent: CLI > env > ~/.config/imsg-mcp/config.json > defaults.\n const cli = parseTuiCliArgs();\n const cfg = resolveTuiConfig({ cliTheme: cli.theme, cliAccent: cli.accent });\n for (const w of cfg.warnings) console.error(`warn: ${w}`);\n\n // Powerline theme needs a Nerd Font; warn the user once at startup if\n // we can detect (or strongly suspect) one is missing. The message lands\n // in stderr before alt-screen activates and surfaces in scrollback after\n // the user quits the TUI.\n if (cfg.theme === \"powerline\") {\n const warning = buildPowerlineFontWarning(detectNerdFont());\n if (warning) console.error(warning);\n }\n\n const theme = makeTheme({ preset: cfg.theme, accent: cfg.accentColor });\n\n // Install shutdown handlers + watchdog before starting TUI.\n //\n // Three TUI-specific lifecycle choices vs the MCP path:\n //\n // 1. `enableFileLogging()` — flip file logging on regardless of IMSG_DEV.\n // Without this the TUI is opaque on crash: \"tool exited on its own\"\n // leaves no postmortem trail in `$TMPDIR/imsg-mcp/`.\n // 2. `installShutdownHandlers({ exitOnUncaughtException: false })` — log\n // uncaught exceptions but DO NOT kill. The MCP-style hard-exit policy\n // would terminate the user's session over any stray React render or\n // background-task error.\n // 3. We deliberately skip `enableOrphanWatchdog()`. The orphan watchdog\n // kills on `ppid !== originalParentPid`, which is correct for an MCP\n // stdio child but is a phantom-exit landmine in a TUI: terminal\n // multiplexers (tmux), shell job-control, and even some IDEs reparent\n // the process during their lifetime — none of which mean the user\n // walked away. The terminal will SIGHUP us on a real disconnect.\n enableFileLogging();\n installShutdownHandlers({ exitOnUncaughtException: false });\n installWatchdog({ idleRestart: false });\n installCacheSweepers();\n startHeapMonitor();\n logStartup(\"tui\");\n\n registerCleanup(() => {\n stopCacheSweepers();\n clearCache();\n stopHeapMonitor();\n logShutdown(\"normal\");\n });\n\n const screen = withFullScreen(\n <ThemeProvider value={theme}>\n <App />\n </ThemeProvider>,\n );\n\n // Register screen cleanup so terminal is restored on any exit\n registerCleanup(() => {\n try {\n screen.instance.unmount();\n } catch {\n // Screen may already be unmounted\n }\n });\n\n await screen.start();\n await screen.waitUntilExit();\n}\n"],"names":["commands","relativeDate","end","start","totalLines","i","cached"],"mappings":"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"}