feat(search): global conversation search in the topbar with deep-link nav

Topbar search box → debounced crm.search → dropdown of hits (chat/mail, highlighted
snippet). Clicking a hit switches to the right tab and focuses the exact thread:
mail → Inbox, chat → Messenger (via the SDK's new focusThreadId, 0.1.6). Snippet HTML
is escaped except the <em> highlight. Demo mode searches an in-memory set.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 14:07:26 +05:30
parent c874a726ef
commit 904d003d32
13 changed files with 1242 additions and 105 deletions
+6 -6
View File
@@ -15,7 +15,7 @@ import type { DataDoor } from "@/lib/crm-messaging-adapter";
const SHELL = isShellConfigured();
export function InboxSdk() {
export function InboxSdk({ focusThreadId }: { focusThreadId?: string | null } = {}) {
return (
<div className="view">
{!SHELL && (
@@ -23,26 +23,26 @@ export function InboxSdk() {
Demo mode running on the SDK&apos;s mock inbox adapter.
</div>
)}
<div className="miu-host miu-host-inbox">{SHELL ? <LiveInbox /> : <DemoInbox />}</div>
<div className="miu-host miu-host-inbox">{SHELL ? <LiveInbox focusThreadId={focusThreadId} /> : <DemoInbox focusThreadId={focusThreadId} />}</div>
</div>
);
}
function DemoInbox() {
function DemoInbox({ focusThreadId }: { focusThreadId?: string | null }) {
const adapter = useMemo<InboxAdapter>(() => new MockInboxAdapter(), []);
return (
<InboxProvider adapter={adapter}>
<SdkInbox />
<SdkInbox focusThreadId={focusThreadId} />
</InboxProvider>
);
}
function LiveInbox() {
function LiveInbox({ focusThreadId }: { focusThreadId?: string | null }) {
const { sdk } = useAppShell();
const adapter = useMemo<InboxAdapter>(() => new CrmInboxAdapter(sdk as unknown as DataDoor), [sdk]);
return (
<InboxProvider adapter={adapter}>
<SdkInbox />
<SdkInbox focusThreadId={focusThreadId} />
</InboxProvider>
);
}