"use client"; // Global conversation search. Live path calls the be-crm data door (crm.search → IIOS Meilisearch, // permission-scoped there); demo path filters a small in-memory set. Search is imperative (the query // changes on every keystroke), so it uses sdk.query directly rather than the cached useQuery hook. import { useCallback } from "react"; import { useAppShell } from "@abe-kap/appshell-sdk/react"; import { isShellConfigured } from "./appshell"; export interface SearchResult { interactionId: string; threadId: string; surface: "messenger" | "inbox"; title: string; /** Snippet with around the matched terms. */ snippet: string; at: number; } export type SearchFn = (query: string) => Promise; const MOCK: SearchResult[] = [ { interactionId: "s1", threadId: "th_mock_1", surface: "messenger", title: "Sofia Ramirez", snippet: "Can you confirm the Henderson scope?", at: Date.now() }, { interactionId: "s2", threadId: "th_mock_2", surface: "messenger", title: "Storm response — East side", snippet: "Crew is rolling out at 7", at: Date.now() }, { interactionId: "s3", threadId: "mt_invoice", surface: "inbox", title: "Invoice #1042 — Acme Roofing", snippet: "Attached is invoice #1042 for the East-side job", at: Date.now() }, ]; const SHELL = isShellConfigured(); function useLiveSearch(): SearchFn { const { sdk } = useAppShell(); return useCallback(async (query: string) => { const q = query.trim(); if (!q) return []; return sdk.query("crm.search", { query: q, limit: 20 }); }, [sdk]); } function useMockSearch(): SearchFn { return useCallback(async (query: string) => { const q = query.trim().toLowerCase(); if (!q) return []; return MOCK.filter((m) => (m.title + " " + m.snippet).toLowerCase().includes(q)); }, []); } export function useGlobalSearch(): SearchFn { return SHELL ? useLiveSearch() : useMockSearch(); }