PluginProbe
ZIP AI – AI Website Builder & AI Agent (Beta) / 0.0.4
ZIP AI – AI Website Builder & AI Agent (Beta) v0.0.4
0.0.10 0.0.9 trunk 0.0.4 0.0.5 0.0.6 0.0.7 0.0.8
zip-ai / src / services / bridge.js

bridge.js in ZIP AI – AI Website Builder & AI Agent (Beta) 0.0.4, at src/services/bridge.js

234 lines 6.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Direct Bridge Service
3 *
4 * Replaces PostMessageBridge for same-window communication.
5 * The React app now runs directly in WordPress (no iframe),
6 * so cross-frame postMessage is replaced with direct function calls.
7 */
8
9 class DirectBridge {
10 constructor() {
11 this.messageHandlers = new Map();
12 window.zipAiMcpAppBridge = this;
13 }
14
15 /**
16 * Initialize — no-op (no iframe to listen to)
17 */
18 init() {}
19
20 /**
21 * Destroy — no-op
22 */
23 destroy() {}
24
25 /**
26 * Register a handler for incoming messages
27 */
28 on(type, handler) {
29 this.messageHandlers.set(type, handler);
30 }
31
32 /**
33 * Unregister a handler
34 */
35 off(type) {
36 this.messageHandlers.delete(type);
37 }
38
39 /**
40 * Emit an incoming message to the React app.
41 */
42 emit(type, data = {}) {
43 const handler = this.messageHandlers.get(type);
44 if (!handler) {
45 return undefined;
46 }
47
48 return handler(data);
49 }
50
51 /**
52 * Send a message — routes to the WordPress host bridge directly
53 */
54 send(type, data = {}) {
55 const wpBridge = window.zipAiMcpBridge;
56 if (!wpBridge) return;
57
58 switch (type) {
59 case 'close_iframe':
60 wpBridge.togglePanel();
61 break;
62 case 'toggle_fullscreen':
63 wpBridge.toggleFullscreen(data.fullscreen);
64 break;
65 case 'ai_tool_started':
66 wpBridge.applyCanvasLoader(data.tool_name, data.target);
67 break;
68 case 'ai_tool_finished':
69 wpBridge.removeCanvasLoader(data.tool_name, data.target, data.success);
70 break;
71 case 'execute_tools':
72 if (data.execution_results?.length > 0) {
73 wpBridge.executeTools(data.execution_results);
74 }
75 break;
76 case 'set_panel_layout':
77 wpBridge.setPanelLayout(data.layout);
78 break;
79 case 'preview_palette':
80 wpBridge.previewPalette(data.colors);
81 break;
82 case 'start_annotation_overlay':
83 if (wpBridge.startAnnotationOverlay) {
84 wpBridge.startAnnotationOverlay(data.dataUrl);
85 }
86 break;
87 case 'annotation_undo':
88 if (wpBridge.annotationUndo) {
89 wpBridge.annotationUndo();
90 }
91 break;
92 case 'annotation_clear':
93 if (wpBridge.annotationClear) {
94 wpBridge.annotationClear();
95 }
96 break;
97 case 'annotation_cancel':
98 if (wpBridge.annotationCancel) {
99 wpBridge.annotationCancel();
100 }
101 break;
102 case 'annotation_done':
103 if (wpBridge.annotationDone) {
104 wpBridge.annotationDone();
105 }
106 break;
107 case 'ready':
108 case 'auth_ready':
109 case 'auth_status_changed':
110 // No-op in same-window mode
111 break;
112 default:
113 // Forward unknown messages to host bridge if it has a handler
114 if (wpBridge.handleMessage) {
115 wpBridge.handleMessage(type, data);
116 }
117 break;
118 }
119 }
120
121 /**
122 * Send a request and wait for response — routes to WordPress host bridge
123 */
124 async request(type, data = {}, timeout = 30000) {
125 const wpBridge = window.zipAiMcpBridge;
126 if (!wpBridge) {
127 throw new Error('WordPress bridge not available');
128 }
129
130 switch (type) {
131 case 'get_context':
132 return wpBridge.getContext();
133 case 'render_block_preview':
134 return wpBridge.renderBlockPreview(data.blockHtml);
135 case 'insert_blocks':
136 return wpBridge.insertBlocks(data.blocks_html);
137 case 'replace_blocks':
138 return wpBridge.replaceBlocks(data.blocks_html);
139 case 'replace_block_by_id':
140 return wpBridge.replaceBlockById(data.client_id, data.blocks_html);
141 case 'capture_block_screenshot':
142 return wpBridge.captureBlockScreenshot(data.clientId, data.options);
143 case 'capture_screenshot':
144 return wpBridge.captureScreenshot();
145 case 'snapshot_blocks':
146 return wpBridge.snapshotBlocks();
147 case 'restore_snapshot':
148 return wpBridge.restoreSnapshot(data.snapshotHtml);
149 default:
150 throw new Error(`Unknown request type: ${type}`);
151 }
152 }
153
154 /**
155 * Request current WordPress context
156 */
157 async requestContext() {
158 try {
159 return await this.request('get_context', {}, 5000);
160 } catch {
161 return {};
162 }
163 }
164
165 /**
166 * Close/toggle the assistant panel
167 */
168 closeIframe() {
169 this.send('close_iframe');
170 }
171
172 /**
173 * Open authentication popup
174 */
175 async openAuthPopup(timeout = 300000) {
176 const wpBridge = window.zipAiMcpBridge;
177 if (!wpBridge) {
178 throw new Error('WordPress bridge not available');
179 }
180 return wpBridge.openAuthPopup();
181 }
182
183 /**
184 * Notify of auth status change — no-op in same-window mode
185 */
186 notifyAuthStatusChanged() {}
187
188 /**
189 * Send auth ready signal — no-op in same-window mode
190 */
191 sendAuthReady() {}
192
193 /**
194 * Request WordPress to render a block preview
195 */
196 async requestBlockPreview(blockHtml) {
197 return this.request('render_block_preview', { blockHtml }, 30000);
198 }
199
200 /**
201 * Preview a color palette
202 */
203 previewPalette(colors) {
204 this.send('preview_palette', { colors });
205 }
206
207 /**
208 * Toggle fullscreen mode
209 */
210 toggleFullscreen(isFullscreen) {
211 this.send('toggle_fullscreen', { fullscreen: isFullscreen });
212 }
213
214 /**
215 * Set panel layout — 'sidebar' (pushes content) or 'popover' (floating card)
216 */
217 setPanelLayout(layout) {
218 this.send('set_panel_layout', { layout });
219 }
220
221 /**
222 * Reset popover position and size to defaults
223 */
224 resetPopoverLayout() {
225 const wpBridge = window.zipAiMcpBridge;
226 if (wpBridge && wpBridge.resetPopoverLayout) {
227 wpBridge.resetPopoverLayout();
228 }
229 }
230 }
231
232 // Export singleton instance
233 export const bridge = new DirectBridge();
234