omnibox.ts RawHistoryFolder
1/**
2 * Omnibox Module Initialization
3 *
4 * Initializes the Svelte 5 omnibox component system
5 */
6
7import { mount } from "svelte";
8import { Omnibox } from "../../components/omnibox";
9import { ensureShadowMount } from "./shadowHost";
10import type { OmniboxConfig } from "../../components/omnibox/types";
11import { omniboxBridge } from "../../components/omnibox/messaging";
12
13let omniboxComponent: ReturnType<typeof mount> | null = null;
14let isInitialized = false;
15
16export interface OmniboxInitConfig extends OmniboxConfig {
17 // Additional initialization options if needed
18 containerId?: string;
19}
20
21/**
22 * Initialize the omnibox component system
23 */
24export async function initializeOmnibox(
25 _config: OmniboxInitConfig = {},
26): Promise<void> {
27 if (isInitialized) {
28 console.warn("Omnibox already initialized");
29 return;
30 }
31
32 try {
33 // Ensure we're in the main frame (not an iframe)
34 if (window.self !== window.top) {
35 console.log("🚫 Omnibox skipped in iframe");
36 return;
37 }
38
39 // Check if DOM is ready
40 if (!document.body) {
41 console.warn("DOM body not ready for omnibox initialization");
42 return;
43 }
44
45 // Mount omnibox into the shared global Shadow DOM host
46 const mountEl = ensureShadowMount("dumber-omnibox");
47 omniboxComponent = mount(Omnibox, {
48 target: mountEl as unknown as Element,
49 });
50
51 // Wait for the component to mount and set up the global API
52 await new Promise<void>((resolve) => {
53 const checkAPI = () => {
54 if (
55 window.__dumber_omnibox &&
56 typeof window.__dumber_omnibox === "object"
57 ) {
58 console.log("✅ Omnibox component system initialized");
59 console.log(
60 "🔧 Global API is available:",
61 Object.keys(window.__dumber_omnibox),
62 );
63 isInitialized = true;
64
65 // Fetch search shortcuts from backend
66 omniboxBridge.fetchSearchShortcuts().catch((err) => {
67 console.warn("Failed to fetch search shortcuts:", err);
68 });
69
70 // Notify isolated bundle listeners that omnibox is ready
71 try {
72 document.dispatchEvent(new CustomEvent("dumber:omnibox-ready"));
73 } catch (e) {
74 console.warn("Failed to dispatch omnibox-ready event", e);
75 }
76 resolve();
77 } else {
78 setTimeout(checkAPI, 10); // Check again in 10ms
79 }
80 };
81 checkAPI();
82 });
83
84 // Bridge: listen for page-world events carrying suggestions and forward to API
85 const handleSuggestionsEvent = (e: Event) => {
86 try {
87 const detail = (e as CustomEvent).detail;
88 const suggestions = detail?.suggestions ?? detail;
89 if (
90 Array.isArray(suggestions) &&
91 window.__dumber_omnibox?.setSuggestions
92 ) {
93 console.log(
94 "🔗 [Bridge] Received page-world suggestions event:",
95 suggestions.length,
96 );
97 window.__dumber_omnibox.setSuggestions(suggestions);
98 } else {
99 console.warn(
100 "🔗 [Bridge] Suggestions event received but API not ready or invalid payload",
101 );
102 }
103 } catch (err) {
104 console.warn("🔗 [Bridge] Failed handling suggestions event:", err);
105 }
106 };
107
108 document.addEventListener(
109 "dumber:omnibox-suggestions",
110 handleSuggestionsEvent,
111 false,
112 );
113 // Unified event name
114 document.addEventListener(
115 "dumber:omnibox:suggestions",
116 handleSuggestionsEvent,
117 false,
118 );
119 } catch (error) {
120 console.error("❌ Failed to initialize omnibox component:", error);
121 throw error;
122 }
123}
124
125/**
126 * Get the current initialization status
127 */
128export function isOmniboxInitialized(): boolean {
129 return isInitialized;
130}
131
132/**
133 * Cleanup omnibox component (useful for hot reload during development)
134 */
135export function cleanupOmnibox(): void {
136 if (omniboxComponent) {
137 try {
138 omniboxComponent.$destroy?.();
139 } catch (error) {
140 console.warn("Error destroying omnibox component:", error);
141 }
142 omniboxComponent = null;
143 }
144
145 // The shared shadow host remains persistent; just log cleanup
146
147 isInitialized = false;
148 console.log("🧹 Omnibox component cleaned up");
149}