Add 'Open in AI' option to item context menus (#1974)
Docker Image CI / build-and-push-image (push) Has been cancelled
Maintain Release Merge PR / update-release-pr (push) Has been cancelled
release-please / release-please (push) Has been cancelled
test / test-backend (24.x) (push) Has been cancelled
test / API tests (node env, api-test) (24.x) (push) Has been cancelled
test / puterjs (node env, vitest) (24.x) (push) Has been cancelled

* Add 'Open in AI' option to item context menus

* Add new AI-related English translations
This commit is contained in:
Nariman Jelveh
2026-01-07 22:49:31 -08:00
committed by GitHub
parent dade7a6cba
commit a0c2ac4d07
2 changed files with 98 additions and 4 deletions
+95 -2
View File
@@ -32,7 +32,82 @@ import launch_app from '../helpers/launch_app.js';
import open_item from '../helpers/open_item.js';
import mime from '../lib/mime.js';
function UIItem (options) {
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 ) {
$(options.appendTo).each(function () {
@@ -909,6 +984,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 });
@@ -1243,6 +1327,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));
}
});
}
// -------------------------------------------
@@ -1837,4 +1930,4 @@ window.activate_item_name_editor = function (el_item) {
}
};
export default UIItem;
export default UIItem;
+3 -2
View File
@@ -27,6 +27,7 @@ const en = {
account_password: 'Verify Account Password',
access_granted_to: 'Access Granted To',
add_existing_account: 'Add Existing Account',
ai_app_unavailable: 'AI app is not available. Please try again later.',
all_fields_required: 'All fields are required.',
allow: 'Allow',
apply: 'Apply',
@@ -194,6 +195,7 @@ const en = {
ok: 'OK',
open: 'Open',
new_window: 'New Window',
open_in_ai: 'Open in AI',
open_in_new_tab: 'Open in New Tab',
open_in_new_window: 'Open in New Window',
open_trash: 'Open Trash',
@@ -426,7 +428,6 @@ const en = {
'billing.currently_on_free_plan': 'You are currently on the free plan.',
'billing.download_receipt': 'Download Receipt',
'billing.subscription_check_error': 'A problem occurred while checking your subscription status.',
'billing.payment_method_updated': 'Payment method updated!',
'billing.email_confirmation_needed': 'Your email has not been confirmed. We\'ll send you a code to confirm it now.',
'billing.sub_cancelled_but_valid_until': 'You have cancelled your subscription and it will automatically switch to the free tier at the end of the billing period. You will not be charged again unless you re-subscribe.',
'billing.current_plan_until_end_of_period': 'Your current plan until the end of this billing period.',
@@ -537,4 +538,4 @@ const en = {
},
};
export default en;
export default en;