diff --git a/_locales/en/messages.json b/_locales/en/messages.json index b62f87c5..87095fa4 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1011,6 +1011,34 @@ "message": "Extract all relevant details required to generate a calendar event from the following text. The extracted information should include:\n- Event Title\n- Start Date and Time (including timezone, if specified)\n- End Date and Time (including timezone, if specified)\n- Full day (if mentioned)\n- Attendees\nEnsure the data is formatted clearly and consistently so that it can be directly used for creating a calendar event.\nIf there are relative time references, consider that the date and time of the email are \"{%mail_datetime%}\". Calculate the start date and time based on this reference. If the calculated start date and time are earlier than \"{%current_datetime%}\", recalculate the start date and time using \"{%current_datetime%}\" as the base.\nIf the duration is not specified, set it to one hour.\nThese are the attendees: {%author%}, {%recipients%}, {%cc_list%}. If present, exclude my address: {%account_email_address%}.\nIf you're not able to get one or more of the required information, please respond with an empty string.\nGenerate a response in JSON format only. Do not include any additional text or explanations; provide only the JSON. Here is the format to be used:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"Calendar event summary here\",\n\"forceAllDay\": false,\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nHere's the text:\"{%mail_text_body_or_selected%}\"", "description": "" }, + "prompt_get_calendar_event_from_clipboard": { + "message": "Add calendar event from clipboard", + "description": "" + }, + "clipboard_read_error": { + "message": "Could not read clipboard. Please check permissions.", + "description": "" + }, + "clipboard_empty_error": { + "message": "Clipboard is empty. Please copy some text first.", + "description": "" + }, + "clipboard_permission_denied": { + "message": "Clipboard permission was denied. Please enable the feature again in settings to grant permission.", + "description": "" + }, + "clipboard_permission_error": { + "message": "Error requesting clipboard permission. Please try again.", + "description": "" + }, + "prefs_OptionText_get_calendar_event_from_clipboard": { + "message": "Get calendar event from clipboard", + "description": "" + }, + "prefs_OptionText_get_calendar_event_from_clipboard_Info": { + "message": "Show an additional menu item to create calendar events from clipboard text content.", + "description": "" + }, "Summarize_prompt_prefs_title": { "message": "Add Summarization Options", "description": "" diff --git a/js/mzta-menus.js b/js/mzta-menus.js index b164bae1..a2d50436 100644 --- a/js/mzta-menus.js +++ b/js/mzta-menus.js @@ -110,6 +110,24 @@ export class mzta_Menus { taWorkingStatus.startWorking(); const tabs = await browser.tabs.query({ active: true, currentWindow: true }); const msg_text = await getMailBody(tabs, placeholdersUtils.hasPlaceholder(curr_prompt.text,'mail_typed_text')); + + if (curr_prompt.id === 'prompt_get_calendar_event_from_clipboard') { + try { + const clipboardText = await navigator.clipboard.readText(); + if (!clipboardText) { + browser.tabs.sendMessage(tabs[0].id, { command: "sendAlert", curr_tab_type: tabs[0].type, message: browser.i18n.getMessage('clipboard_empty_error') }); + taWorkingStatus.stopWorking(); + return {ok:'0'}; + } + msg_text.selection = clipboardText; + } catch (e) { + console.error("Clipboard read error:", e); + browser.tabs.sendMessage(tabs[0].id, { command: "sendAlert", curr_tab_type: tabs[0].type, message: browser.i18n.getMessage('clipboard_read_error') }); + taWorkingStatus.stopWorking(); + return {ok:'0'}; + } + } + // console.log(">>>>>>>>>>>>> msg_text: " + JSON.stringify(msg_text)); //check if a selection is needed if(String(curr_prompt.need_selected) == "1" && (msg_text.selection==='')){ @@ -256,7 +274,8 @@ export class mzta_Menus { return {ok:'1'}; break; // Add tags to the email - END } - case 'prompt_get_calendar_event': { // Get a calendar event info + case 'prompt_get_calendar_event': // Get a calendar event info + case 'prompt_get_calendar_event_from_clipboard': { // here from clipboard let calendar_event_data = ''; let prefs_at = await browser.storage.sync.get({ connection_type: prefs_default.connection_type, diff --git a/js/mzta-prompts.js b/js/mzta-prompts.js index a3fac86e..4a73f20a 100644 --- a/js/mzta-prompts.js +++ b/js/mzta-prompts.js @@ -294,6 +294,21 @@ const specialPrompts = [ is_default: "1", is_special: "1", }, + { + id: 'prompt_get_calendar_event_from_clipboard', + name: "__MSG_prompt_get_calendar_event_from_clipboard__", + text: "prompt_get_calendar_event_full_text", + type: "1", + action: "0", + need_selected: "0", + need_signature: "0", + need_custom_text: "0", + define_response_lang: "0", + use_diff_viewer: "0", + api_type: '', + is_default: "1", + is_special: "1", + }, { id: 'prompt_get_task', name: "__MSG_prompt_get_task__", diff --git a/js/mzta-utils.js b/js/mzta-utils.js index 90feb52b..a8aabdb2 100644 --- a/js/mzta-utils.js +++ b/js/mzta-utils.js @@ -577,6 +577,7 @@ export function getActiveSpecialPromptsIDs(args = {}) { addtags = false, addtags_api = false, get_calendar_event = false, + get_calendar_event_from_clipboard = false, get_task = false, is_chatgpt_web = false } = args; @@ -596,6 +597,9 @@ export function getActiveSpecialPromptsIDs(args = {}) { if (get_calendar_event) { output.push('prompt_get_calendar_event'); } + if (get_calendar_event_from_clipboard) { + output.push('prompt_get_calendar_event_from_clipboard'); + } if (get_task) { output.push('prompt_get_task'); } diff --git a/manifest.json b/manifest.json index bb91cd1f..743688ab 100644 --- a/manifest.json +++ b/manifest.json @@ -34,6 +34,7 @@ "messagesTagsList" ], "optional_permissions": [ + "clipboardRead", "messagesTags", "messagesUpdate", "messagesMove", diff --git a/mzta-background.js b/mzta-background.js index cbdfbc7c..5fee2dd6 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -99,6 +99,7 @@ let special_prompts_ids = getActiveSpecialPromptsIDs({ addtags: prefs_init.add_tags, addtags_api: hasSpecificIntegration(prefs_init.add_tags_use_specific_integration, prefs_init.add_tags_connection_type), get_calendar_event: doGetSparkFeature(prefs_init.get_calendar_event), + get_calendar_event_from_clipboard: doGetSparkFeature(prefs_init.get_calendar_event_from_clipboard), get_task: doGetSparkFeature(prefs_init.get_task), is_chatgpt_web: (prefs_init.connection_type === "chatgpt_web") }); @@ -183,13 +184,15 @@ function preparePopupMenu(tab) { } async function _reload_menus() { - let prefs_reload = await browser.storage.sync.get({add_tags: prefs_default.add_tags, get_calendar_event: prefs_default.get_calendar_event, get_task: prefs_default.get_task, connection_type: prefs_default.connection_type}); + let prefs_reload = await browser.storage.sync.get({add_tags: prefs_default.add_tags, get_calendar_event: prefs_default.get_calendar_event, get_calendar_event_from_clipboard: prefs_default.get_calendar_event_from_clipboard, get_task: prefs_default.get_task, connection_type: prefs_default.connection_type}); let getCalendarEvent = doGetSparkFeature(prefs_reload.get_calendar_event); + let getCalendarEventFromClipboard = doGetSparkFeature(prefs_reload.get_calendar_event_from_clipboard); let getTask = doGetSparkFeature(prefs_reload.get_task); const special_prompts_ids = getActiveSpecialPromptsIDs({ addtags: prefs_reload.add_tags, addtags_api: hasSpecificIntegration(prefs_init.add_tags_use_specific_integration, prefs_init.add_tags_connection_type), get_calendar_event: getCalendarEvent, + get_calendar_event_from_clipboard: getCalendarEventFromClipboard, get_task: getTask, is_chatgpt_web: (prefs_reload.connection_type === "chatgpt_web") }); @@ -803,6 +806,7 @@ async function reload_pref_init(){ do_debug: prefs_default.do_debug, add_tags: prefs_default.add_tags, get_calendar_event: prefs_default.get_calendar_event, + get_calendar_event_from_clipboard: prefs_default.get_calendar_event_from_clipboard, get_task: prefs_default.get_task, connection_type: prefs_default.connection_type, add_tags_auto: prefs_default.add_tags_auto, @@ -828,11 +832,13 @@ function setupStorageChangeListener() { if (changes.add_tags) { const newTags = changes.add_tags.newValue; let getCalendarEvent = doGetSparkFeature(prefs_init.get_calendar_event); + let getCalendarEventFromClipboard = doGetSparkFeature(prefs_init.get_calendar_event_from_clipboard); let getTask = doGetSparkFeature(prefs_init.get_task); const special_prompts_ids = getActiveSpecialPromptsIDs({ addtags: newTags, addtags_api: hasSpecificIntegration(prefs_init.add_tags_use_specific_integration, prefs_init.add_tags_connection_type), get_calendar_event: getCalendarEvent, + get_calendar_event_from_clipboard: getCalendarEventFromClipboard, get_task: getTask, is_chatgpt_web: (prefs_init.connection_type === "chatgpt_web") }); @@ -843,11 +849,30 @@ function setupStorageChangeListener() { if (changes.get_calendar_event) { const newCalendarEvent = changes.get_calendar_event.newValue; let getCalendarEvent = doGetSparkFeature(newCalendarEvent); + let getCalendarEventFromClipboard = doGetSparkFeature(prefs_init.get_calendar_event_from_clipboard); let getTask = doGetSparkFeature(prefs_init.get_task); const special_prompts_ids = getActiveSpecialPromptsIDs({ addtags: prefs_init.add_tags, addtags_api: hasSpecificIntegration(prefs_init.add_tags_use_specific_integration, prefs_init.add_tags_connection_type), get_calendar_event: getCalendarEvent, + get_calendar_event_from_clipboard: getCalendarEventFromClipboard, + get_task: getTask, + is_chatgpt_web: (prefs_init.connection_type === "chatgpt_web") + }); + menus.reload(special_prompts_ids); + } + + // Process 'get_calendar_event_from_clipboard' changes + if (changes.get_calendar_event_from_clipboard) { + const newCalendarEventFromClipboard = changes.get_calendar_event_from_clipboard.newValue; + let getCalendarEvent = doGetSparkFeature(prefs_init.get_calendar_event); + let getCalendarEventFromClipboard = doGetSparkFeature(newCalendarEventFromClipboard); + let getTask = doGetSparkFeature(prefs_init.get_task); + const special_prompts_ids = getActiveSpecialPromptsIDs({ + addtags: prefs_init.add_tags, + addtags_api: hasSpecificIntegration(prefs_init.add_tags_use_specific_integration, prefs_init.add_tags_connection_type), + get_calendar_event: getCalendarEvent, + get_calendar_event_from_clipboard: getCalendarEventFromClipboard, get_task: getTask, is_chatgpt_web: (prefs_init.connection_type === "chatgpt_web") }); @@ -858,11 +883,13 @@ function setupStorageChangeListener() { if (changes.get_task) { const newTask = changes.get_task.newValue; let getCalendarEvent = doGetSparkFeature(prefs_init.get_calendar_event); + let getCalendarEventFromClipboard = doGetSparkFeature(prefs_init.get_calendar_event_from_clipboard); let getTask = doGetSparkFeature(newTask); const special_prompts_ids = getActiveSpecialPromptsIDs({ addtags: prefs_init.add_tags, addtags_api: hasSpecificIntegration(prefs_init.add_tags_use_specific_integration, prefs_init.add_tags_connection_type), get_calendar_event: getCalendarEvent, + get_calendar_event_from_clipboard: getCalendarEventFromClipboard, get_task: getTask, is_chatgpt_web: (prefs_init.connection_type === "chatgpt_web") }); @@ -873,11 +900,13 @@ function setupStorageChangeListener() { if (changes.connection_type) { const newConnectionType = changes.connection_type.newValue; let getCalendarEvent = doGetSparkFeature(prefs_init.get_calendar_event); + let getCalendarEventFromClipboard = doGetSparkFeature(prefs_init.get_calendar_event_from_clipboard); let getTask = doGetSparkFeature(prefs_init.get_task); const special_prompts_ids = getActiveSpecialPromptsIDs({ addtags: prefs_init.add_tags, addtags_api: hasSpecificIntegration(prefs_init.add_tags_use_specific_integration, prefs_init.add_tags_connection_type), get_calendar_event: getCalendarEvent, + get_calendar_event_from_clipboard: getCalendarEventFromClipboard, get_task: getTask, is_chatgpt_web: (newConnectionType === "chatgpt_web") }); diff --git a/options/mzta-options-default.js b/options/mzta-options-default.js index 64b5d839..408ad38d 100644 --- a/options/mzta-options-default.js +++ b/options/mzta-options-default.js @@ -125,6 +125,7 @@ export const prefs_default = { add_tags_auto_uselist_list: '', add_tags_enabled_accounts: [], get_calendar_event: true, + get_calendar_event_from_clipboard: false, // Enable calendar event from clipboard menu item get_task: true, calendar_enforce_timezone: false, calendar_timezone: '', diff --git a/pages/get-calendar-event/mzta-get-calendar-event.html b/pages/get-calendar-event/mzta-get-calendar-event.html index 095f26aa..53366e35 100644 --- a/pages/get-calendar-event/mzta-get-calendar-event.html +++ b/pages/get-calendar-event/mzta-get-calendar-event.html @@ -36,6 +36,16 @@ + + __MSG_prefs_OptionText_get_calendar_event_from_clipboard__ + + + + + __MSG_prefs_OptionText_calendar_enforce_timezone__ diff --git a/pages/get-calendar-event/mzta-get-calendar-event.js b/pages/get-calendar-event/mzta-get-calendar-event.js index 584fb244..c2bb8889 100644 --- a/pages/get-calendar-event/mzta-get-calendar-event.js +++ b/pages/get-calendar-event/mzta-get-calendar-event.js @@ -79,6 +79,7 @@ document.addEventListener('DOMContentLoaded', async () => { let get_calendar_event_reset_btn = document.getElementById('btn_reset_prompt'); let get_calendar_event_use_specific_integration = document.getElementById('get_calendar_event_use_specific_integration'); let get_calendar_event_no_selection = document.getElementById('calendar_no_selection'); + let get_calendar_event_from_clipboard = document.getElementById('get_calendar_event_from_clipboard'); get_calendar_event_textarea.addEventListener('input', (event) => { get_calendar_event_reset_btn.disabled = (event.target.value === browser.i18n.getMessage('prompt_get_calendar_event_full_text')); @@ -102,6 +103,35 @@ document.addEventListener('DOMContentLoaded', async () => { browser.runtime.sendMessage({command: "reload_menus"}); }); + get_calendar_event_from_clipboard.addEventListener('change', async (event) => { + if (event.target.checked) { + // Request clipboardRead permission when enabling the feature + try { + const granted = await browser.permissions.request({ + permissions: ["clipboardRead"] + }); + + if (granted) { + // Permission granted, enable the feature + await browser.storage.sync.set({ get_calendar_event_from_clipboard: true }); + browser.runtime.sendMessage({command: "reload_menus"}); + } else { + // Permission denied, uncheck the checkbox + event.target.checked = false; + alert(browser.i18n.getMessage("clipboard_permission_denied")); + } + } catch (err) { + taLog.error("Error requesting clipboard permission:", err); + event.target.checked = false; + alert(browser.i18n.getMessage("clipboard_permission_error")); + } + } else { + // Disabling the feature + await browser.storage.sync.set({ get_calendar_event_from_clipboard: false }); + browser.runtime.sendMessage({command: "reload_menus"}); + } + }); + get_calendar_event_reset_btn.addEventListener('click', () => { get_calendar_event_textarea.value = browser.i18n.getMessage('prompt_get_calendar_event_full_text'); get_calendar_event_reset_btn.disabled = true; diff --git a/popup/mzta-popup.js b/popup/mzta-popup.js index d5d69664..c50bc028 100644 --- a/popup/mzta-popup.js +++ b/popup/mzta-popup.js @@ -30,6 +30,7 @@ let add_tags = false; let add_tags_use_specific_integration = false; let add_tags_connection_type = ''; let get_calendar_event = false; +let get_calendar_event_from_clipboard = false; let get_task = false; let _ok_sparks = false; let tabType; @@ -43,6 +44,7 @@ document.addEventListener('DOMContentLoaded', async () => { add_tags_use_specific_integration: prefs_default.add_tags_use_specific_integration, add_tags_connection_type: prefs_default.add_tags_connection_type, get_calendar_event: prefs_default.get_calendar_event, + get_calendar_event_from_clipboard: prefs_default.get_calendar_event_from_clipboard, get_task: prefs_default.get_task, connection_type: prefs_default.connection_type }); @@ -66,6 +68,7 @@ document.addEventListener('DOMContentLoaded', async () => { add_tags_use_specific_integration = prefs.add_tags_use_specific_integration; add_tags_connection_type = prefs.add_tags_connection_type; get_calendar_event = prefs.get_calendar_event; + get_calendar_event_from_clipboard = prefs.get_calendar_event_from_clipboard; get_task = prefs.get_task; _ok_sparks = await checkSparksPresence() == 1; // console.log(">>>>>>>>>>>>>>>>> add_tags: " + add_tags); @@ -164,6 +167,7 @@ async function searchPrompt(allPrompts, tabId, tabType){ let do_add_tags = checkDoAddTags(); let do_get_calendar_event = checkDoCalendarEvent(); + let do_get_calendar_event_from_clipboard = checkDoCalendarEventFromClipboard(); let do_get_task = checkDoTask(); // console.log(">>>>>>>>>>> do_add_tags: " + do_add_tags); @@ -171,7 +175,7 @@ async function searchPrompt(allPrompts, tabId, tabType){ // console.log(">>>>>>>>>>> do_get_task: " + do_get_task); // console.log(">>>>>>>>>>> filteredData: " + JSON.stringify(filteredData)); - num_special_menu_items = (do_add_tags ? 1 : 0) + (do_get_calendar_event ? 1 : 0) + (do_get_task ? 1 : 0); + num_special_menu_items = (do_add_tags ? 1 : 0) + (do_get_calendar_event ? 1 : 0) + (do_get_calendar_event_from_clipboard ? 1 : 0) + (do_get_task ? 1 : 0); // console.log(">>>>>>>>>>>> num_special_menu_items: " + num_special_menu_items); if(num_special_menu_items > 0){ max_num_el -= num_special_menu_items; @@ -192,10 +196,18 @@ async function searchPrompt(allPrompts, tabId, tabType){ filteredData[gce_curr_pos].numberPrepended = 'true'; filteredData[gce_curr_pos].label = gce_curr_pos + '. ' + filteredData[gce_curr_pos].label; } + } + if(do_get_calendar_event_from_clipboard){ + filteredData = ensurePromptGetCalendarEventFromClipboardFirst(filteredData, do_add_tags, do_get_calendar_event); + let gcefc_curr_pos = (do_add_tags ? 1 : 0) + (do_get_calendar_event ? 1 : 0); + if (!filteredData[gcefc_curr_pos].numberPrepended) { + filteredData[gcefc_curr_pos].numberPrepended = 'true'; + filteredData[gcefc_curr_pos].label = gcefc_curr_pos + '. ' + filteredData[gcefc_curr_pos].label; + } } if(do_get_task){ - filteredData = ensurePromptGetTaskFirst(filteredData, do_add_tags, do_get_calendar_event); - let gtask_curr_pos = (do_add_tags ? 1 : 0) + (do_get_calendar_event ? 1 : 0); + filteredData = ensurePromptGetTaskFirst(filteredData, do_add_tags, do_get_calendar_event, do_get_calendar_event_from_clipboard); + let gtask_curr_pos = (do_add_tags ? 1 : 0) + (do_get_calendar_event ? 1 : 0) + (do_get_calendar_event_from_clipboard ? 1 : 0); if (!filteredData[gtask_curr_pos].numberPrepended) { filteredData[gtask_curr_pos].numberPrepended = 'true'; filteredData[gtask_curr_pos].label = gtask_curr_pos + '. ' + filteredData[gtask_curr_pos].label; @@ -222,7 +234,7 @@ async function searchPrompt(allPrompts, tabId, tabType){ itemDiv.classList.add('mzta_autocomplete-item'); itemDiv.textContent = item.label; itemDiv.setAttribute('data-id', item.id); - if((item.id === 'prompt_add_tags')||(item.id === 'prompt_get_calendar_event')||(item.id === 'prompt_get_task')){ + if((item.id === 'prompt_add_tags')||(item.id === 'prompt_get_calendar_event')||(item.id === 'prompt_get_calendar_event_from_clipboard')||(item.id === 'prompt_get_task')){ itemDiv.className += ' special_prompt'; } @@ -388,6 +400,10 @@ function checkDoCalendarEvent(){ return get_calendar_event && (connection_type !== "chatgpt_web" && tabType !== 'messageCompose') && _ok_sparks; } +function checkDoCalendarEventFromClipboard(){ + return get_calendar_event_from_clipboard && (connection_type !== "chatgpt_web" && tabType !== 'messageCompose') && _ok_sparks; +} + function checkDoTask(){ return get_task && (connection_type !== "chatgpt_web" && tabType !== 'messageCompose') && _ok_sparks; } @@ -424,18 +440,36 @@ function ensurePromptGetCalendarEventFirst(arr, do_add_tags) { return arr; } -function ensurePromptGetTaskFirst(arr, do_add_tags, do_get_calendar_event) { +function ensurePromptGetCalendarEventFromClipboardFirst(arr, do_add_tags, do_get_calendar_event) { + // Find the index of the object with id "prompt_get_calendar_event_from_clipboard" + const index = arr.findIndex(item => item.id === "prompt_get_calendar_event_from_clipboard"); + + const targetPosition = (do_add_tags ? 1 : 0) + (do_get_calendar_event ? 1 : 0); + + // If found and needs repositioning + if (index !== -1 && index !== targetPosition) { + // Remove it from its current position + const [promptAddTags] = arr.splice(index, 1); + + // Add it to the specified position + arr.splice(targetPosition, 0, promptAddTags); + } + + return arr; +} + +function ensurePromptGetTaskFirst(arr, do_add_tags, do_get_calendar_event, do_get_calendar_event_from_clipboard) { // Find the index of the object with id "prompt_get_task" const index = arr.findIndex(item => item.id === "prompt_get_task"); + // Determine the target position to insert "prompt_get_task" after calendar + const targetPosition = (do_add_tags ? 1 : 0) + (do_get_calendar_event ? 1 : 0) + (do_get_calendar_event_from_clipboard ? 1 : 0); + // If found and needs repositioning - if (index !== -1 && ((do_get_calendar_event && do_add_tags) ? index !== 2 : (do_get_calendar_event ? index !== 1 : index !== 0))) { + if (index !== -1 && index !== targetPosition) { // Remove it from its current position const [promptGetTask] = arr.splice(index, 1); - // Determine the target position to insert "prompt_get_task" after calendar - const targetPosition = do_add_tags && do_get_calendar_event ? 2 : (do_get_calendar_event ? 1 : 0); - // Add it to the specified position arr.splice(targetPosition, 0, promptGetTask); }