diff --git a/src/gui/src/UI/UIItem.js b/src/gui/src/UI/UIItem.js index f502aafea6..16f9cfa46d 100644 --- a/src/gui/src/UI/UIItem.js +++ b/src/gui/src/UI/UIItem.js @@ -32,6 +32,81 @@ import launch_app from "../helpers/launch_app.js" import open_item from "../helpers/open_item.js" import mime from "../lib/mime.js"; +const AI_APP_NAME = 'ai'; + +const parseItemMetadataForAI = (metadata) => { + if (!metadata) { + return undefined; + } + try { + return JSON.parse(metadata); + } catch (error) { + console.warn('Failed to parse item metadata for AI payload.', error); + return undefined; + } +}; + +const buildAIPayloadFromItems = ($elements) => { + return $elements.get().map((element) => { + const $element = $(element); + return { + uid: $element.attr('data-uid'), + path: $element.attr('data-path'), + name: $element.attr('data-name'), + is_dir: $element.attr('data-is_dir') === '1', + is_shortcut: $element.attr('data-is_shortcut') === '1', + shortcut_to: $element.attr('data-shortcut_to') || undefined, + shortcut_to_path: $element.attr('data-shortcut_to_path') || undefined, + size: $element.attr('data-size') || undefined, + type: $element.attr('data-type') || undefined, + modified: $element.attr('data-modified') || undefined, + metadata: parseItemMetadataForAI($element.attr('data-metadata')), + }; + }); +}; + +const ensureAIAppIframe = async () => { + let $aiWindow = $(`.window[data-app="${AI_APP_NAME}"]`); + if ($aiWindow.length === 0) { + try { + await launch_app({ name: AI_APP_NAME }); + } catch (error) { + console.error('Failed to launch AI app.', error); + return null; + } + $aiWindow = $(`.window[data-app="${AI_APP_NAME}"]`); + } + + if ($aiWindow.length === 0) { + return null; + } + + $aiWindow.makeWindowVisible(); + const iframe = $aiWindow.find('.window-app-iframe').get(0); + return iframe ?? null; +}; + +const sendSelectionToAIApp = async ($elements) => { + const items = buildAIPayloadFromItems($elements); + if (items.length === 0) { + return; + } + + const aiIframe = await ensureAIAppIframe(); + if (!aiIframe || !aiIframe.contentWindow) { + await UIAlert({ + message: i18n('ai_app_unavailable'), + }); + return; + } + + aiIframe.contentWindow.postMessage({ + msg: 'ai:openFsEntries', + items, + source: 'desktop-context-menu', + }, '*'); +}; + function UIItem(options){ const matching_appendto_count = $(options.appendTo).length; if(matching_appendto_count > 1){ @@ -850,6 +925,15 @@ function UIItem(options){ } }) // ------------------------------------------- + // Open in AI + // ------------------------------------------- + menu_items.push({ + html: i18n('open_in_ai'), + onClick: async function(){ + await sendSelectionToAIApp($selected_items); + } + }); + // ------------------------------------------- // - // ------------------------------------------- menu_items.push({ is_divider: true }); @@ -1178,6 +1262,15 @@ function UIItem(options){ UIWindowShare([{uid: $(el_item).attr('data-uid'), path: $(el_item).attr('data-path'), name: $(el_item).attr('data-name'), icon: $(el_item_icon).find('img').attr('src')}]); } }); + // ------------------------------------------- + // Open in AI + // ------------------------------------------- + menu_items.push({ + html: i18n('open_in_ai'), + onClick: async function(){ + await sendSelectionToAIApp($(el_item)); + } + }); } // ------------------------------------------- @@ -1753,4 +1846,4 @@ window.activate_item_name_editor= function(el_item){ $(el_item_name_editor).select(); } -export default UIItem; \ No newline at end of file +export default UIItem; diff --git a/src/gui/src/i18n/translations/en.js b/src/gui/src/i18n/translations/en.js index 2b1ac4073f..10c6409c6c 100644 --- a/src/gui/src/i18n/translations/en.js +++ b/src/gui/src/i18n/translations/en.js @@ -137,6 +137,7 @@ const en = { enter_password_to_confirm_delete_user: "Enter your password to confirm account deletion", error_message_is_missing: "Error message is missing.", error_unknown_cause: "An unknown error occurred.", + ai_app_unavailable: "The AI app isn't available right now. Please try again in a moment.", error_uploading_files: "Failed to upload files", favorites: "Favorites", feedback: "Feedback", @@ -197,6 +198,7 @@ const en = { new_window: "New Window", open_in_new_tab: "Open in New Tab", open_in_new_window: "Open in New Window", + open_in_ai: "Open in AI", open_trash: "Open Trash", open_with: "Open With", original_name: 'Original Name', @@ -520,4 +522,4 @@ const en = { } }; -export default en; \ No newline at end of file +export default en;