@@ -178,8 +178,130 @@ else if (data.type === 'event' && data.method) {
178178 };
179179 }
180180
181+ function sendNativeUI(action, data) {
182+ if (window.webkit?.messageHandlers?.craft) {
183+ window.webkit.messageHandlers.craft.postMessage({ t: 'nativeUI', a: action, d: data || {} });
184+ return true;
185+ }
186+
187+ if (window.CraftBridge?.postMessage) {
188+ window.CraftBridge.postMessage(JSON.stringify({ t: 'nativeUI', a: action, d: data || {} }));
189+ return true;
190+ }
191+
192+ return send({
193+ id: generateId(),
194+ type: 'nativeUI',
195+ action,
196+ data: data || {}
197+ });
198+ }
199+
200+ function createNativeUIFacade() {
201+ class Sidebar {
202+ constructor(id) {
203+ this.id = id;
204+ this._selectCallbacks = [];
205+ }
206+ addSection(section) {
207+ sendNativeUI('addSidebarSection', { sidebarId: this.id, section });
208+ return this;
209+ }
210+ setSelectedItem(itemId) {
211+ sendNativeUI('setSelectedItem', { sidebarId: this.id, itemId });
212+ return this;
213+ }
214+ onSelect(callback) {
215+ this._selectCallbacks.push(callback);
216+ return this;
217+ }
218+ destroy() {
219+ sendNativeUI('destroyComponent', { id: this.id, type: 'sidebar' });
220+ }
221+ }
222+
223+ class FileBrowser {
224+ constructor(id) {
225+ this.id = id;
226+ }
227+ addFile(file) {
228+ sendNativeUI('addFile', { browserId: this.id, file });
229+ return this;
230+ }
231+ addFiles(files) {
232+ sendNativeUI('addFiles', { browserId: this.id, files });
233+ return this;
234+ }
235+ clearFiles() {
236+ sendNativeUI('clearFiles', { browserId: this.id });
237+ return this;
238+ }
239+ destroy() {
240+ sendNativeUI('destroyComponent', { id: this.id, type: 'fileBrowser' });
241+ }
242+ }
243+
244+ class SplitView {
245+ constructor(id, sidebar, browser) {
246+ this.id = id;
247+ this.sidebar = sidebar;
248+ this.browser = browser;
249+ }
250+ setDividerPosition(position) {
251+ sendNativeUI('setDividerPosition', { splitViewId: this.id, position });
252+ return this;
253+ }
254+ destroy() {
255+ sendNativeUI('destroyComponent', { id: this.id, type: 'splitView' });
256+ }
257+ }
258+
259+ return {
260+ createSidebar(options) {
261+ const opts = options || {};
262+ const id = opts.id || 'sidebar-' + Date.now() + '-' + Math.random().toString(36).slice(2);
263+ sendNativeUI('createSidebar', Object.assign({}, opts, { id }));
264+ return new Sidebar(id);
265+ },
266+ createFileBrowser(options) {
267+ const opts = options || {};
268+ const id = opts.id || 'browser-' + Date.now() + '-' + Math.random().toString(36).slice(2);
269+ sendNativeUI('createFileBrowser', Object.assign({}, opts, { id }));
270+ return new FileBrowser(id);
271+ },
272+ createSplitView(options) {
273+ if (!options?.sidebar || !options?.browser) {
274+ throw new Error('createSplitView requires sidebar and browser options');
275+ }
276+ const id = options.id || 'splitview-' + Date.now() + '-' + Math.random().toString(36).slice(2);
277+ sendNativeUI('createSplitView', {
278+ id,
279+ sidebarId: options.sidebar.id,
280+ browserId: options.browser.id
281+ });
282+ return new SplitView(id, options.sidebar, options.browser);
283+ },
284+ showQuickLook(options) {
285+ sendNativeUI('showQuickLook', options);
286+ },
287+ closeQuickLook() {
288+ sendNativeUI('closeQuickLook', {});
289+ },
290+ toggleQuickLook(options) {
291+ sendNativeUI('toggleQuickLook', options);
292+ }
293+ };
294+ }
295+
296+ const existingCraft = window.craft || {};
297+ const nativeUI = existingCraft.nativeUI || createNativeUIFacade();
298+ const componentAPI = existingCraft.components || Object.assign({}, nativeUI, {
299+ updateComponent: (id, props) => request('component.update', { componentId: id, props }),
300+ destroyComponent: (id) => request('component.destroy', { componentId: id })
301+ });
302+
181303 // Build the craft API object
182- window.craft = {
304+ window.craft = Object.assign({}, existingCraft, {
183305 // Meta
184306 isCraft,
185307 on,
@@ -289,14 +411,9 @@ else if (data.type === 'event' && data.method) {
289411 },
290412
291413 // Native component helpers
292- components: {
293- createSidebar: (config) => request('component.createSidebar', config),
294- createFileBrowser: (config) => request('component.createFileBrowser', config),
295- createSplitView: (config) => request('component.createSplitView', config),
296- updateComponent: (id, props) => request('component.update', { componentId: id, props }),
297- destroyComponent: (id) => request('component.destroy', { componentId: id }),
298- }
299- };
414+ nativeUI,
415+ components: componentAPI
416+ });
300417
301418 // Dispatch ready event
302419 window.dispatchEvent(new CustomEvent('craft:ready', { detail: { isCraft: isCraft() } }));
0 commit comments