From f0a5f9317f32b74681479799976e019df854e5a9 Mon Sep 17 00:00:00 2001 From: achiyae <10044875+achiyae@users.noreply.github.com> Date: Fri, 4 Aug 2023 17:57:38 +0300 Subject: [PATCH] phase 1 --- popup/popup.html | 6 +- popup/popup.js | 138 +++++++++++++++++++++----------------- scripts/service-worker.js | 65 +++++++++++++++--- 3 files changed, 134 insertions(+), 75 deletions(-) diff --git a/popup/popup.html b/popup/popup.html index 5e66b10..d74435e 100644 --- a/popup/popup.html +++ b/popup/popup.html @@ -26,21 +26,21 @@
Text Improvement
Text Completion
Ask
diff --git a/popup/popup.js b/popup/popup.js index 68a1296..ef60457 100644 --- a/popup/popup.js +++ b/popup/popup.js @@ -1,105 +1,119 @@ -const apiKeyRegex = /sk-[a-zA-Z0-9]{48}/; - -const settings = [ - { key: "textCompletion", name: "text-completion" }, - { key: "textImprovement", name: "text-improvement" }, - { key: "textAsk", name: "text-ask" }, -]; +const apiKeyRegex = /sk-[a-zA-Z0-9]{48}/ function addMessage(message) { - $("#message-box").append(`
${message}
`); + $('#message-box').append(`
${message}
`) } function addErrorMessage(message) { - $("#message-box").append(`
${message}
`); + $('#message-box').append(`
${message}
`) } function clearMessages() { - $("#message-box").empty(); + $('#message-box').empty() +} + + +/** + * Set a setting in storage {@link https://developer.chrome.com/docs/extensions/reference/storage/#type-StorageArea:~:text=to%20the%20callback.-,set,-void} + * @param key + * @param value + * @param callback + */ +async function setSetting(key, value, callback = null) { + let obj = {} + obj[key] = value + chrome.storage.local.set(obj, callback).catch(error => { + console.log(`Failed to set ${key} setting. Error: ${error}`) + }) +} + +/** + * Get a setting from storage + * @param {string | string[] | object} [keys=null] - The keys to get (see {@link https://developer.chrome.com/docs/extensions/reference/storage/#usage}) + * @param {function} [callback=null] - Callback function + */ +async function getSetting(keys = null, callback = null) { + return chrome.storage.local.get(keys, callback) } async function refreshStorage() { - chrome.storage.local.get("openAIAPIKey").then(({ openAIAPIKey }) => { - $("#api-token-form .api-token-status").text(chrome.runtime.lastError || !openAIAPIKey ? "not set" : "set"); - }); + getSetting('openAIAPIKey').then(({ openAIAPIKey }) => { + $('#api-token-form .api-token-status').text(chrome.runtime.lastError || !openAIAPIKey ? 'not set' : 'set') + }) - chrome.storage.local.get(settings.map(({ key }) => key)).then((storage) => { - settings.forEach(({ key, name }) => { - $(`#settings-form input[name='${name}']:checkbox`).prop("checked", storage[key]); - }); - }); + getSetting(['Improve', 'Complete', 'Ask']).then(settings => + settings.forEach(({ key, shortcut, status }) => { + if (status === 'error') { + addErrorMessage(`${shortcut} could not be bound for the ${key} command. You can set it manually at chrome://extensions/shortcuts.`) + } + $(`#settings-form input[name='text-${key}']:checkbox`).prop('checked', status === 'checked') + })) } async function handleAPITokenSet(event) { - event.preventDefault(); - event.stopPropagation(); + event.preventDefault() + event.stopPropagation() - clearMessages(); + clearMessages() - const input = $("#api-token-form").find("input[name='api-token']"); - const openAIAPIKey = input.val(); - input.val(""); + const input = $('#api-token-form').find('input[name=\'api-token\']') + const openAIAPIKey = input.val() + input.val('') if (!openAIAPIKey || !apiKeyRegex.test(openAIAPIKey)) { - addErrorMessage("Invalid API Token."); - return; + addErrorMessage('Invalid API Token.') + return } try { - await chrome.storage.local.set({ openAIAPIKey }); + await chrome.storage.local.set({ openAIAPIKey }) } catch (error) { - console.log(error); - addErrorMessage("Failed to set API Token."); - return; + console.log(error) + addErrorMessage('Failed to set API Token.') + return } - await refreshStorage(); + await refreshStorage() } async function handleAPITokenClear(event) { - event.preventDefault(); - event.stopPropagation(); + event.preventDefault() + event.stopPropagation() - clearMessages(); + clearMessages() try { - await chrome.storage.local.remove("openAIAPIKey"); + await chrome.storage.local.remove('openAIAPIKey') } catch (error) { - console.log(error); - addErrorMessage("Failed to remove API Token."); - return; + console.log(error) + addErrorMessage('Failed to remove API Token.') + return } - await refreshStorage(); + await refreshStorage() } function makeHandleSettingChange(key) { return async (event) => { - event.preventDefault(); - event.stopPropagation(); + event.preventDefault() + event.stopPropagation() + clearMessages() - clearMessages(); - - const value = event.target.checked; - - try { - await chrome.storage.local.set({ [key]: value }); - } catch (error) { - console.log(error); - addErrorMessage(`Failed to set ${key} setting.`); - return; - } - - await refreshStorage(); - }; + const value = event.target.checked + getSetting(key).then(setting => { + setting.status = value ? 'checked' : 'unchecked' + setSetting(setting.key, setting) + }) + // await refreshStorage() + } } $(document).ready(async function () { - $("#api-token-form .submit").on("click", handleAPITokenSet); - $("#api-token-form .clear").on("click", handleAPITokenClear); + $('#api-token-form .submit').on('click', handleAPITokenSet) + $('#api-token-form .clear').on('click', handleAPITokenClear) - settings.forEach(({ key, name }) => - $(`#settings-form input[name='${name}']:checkbox`).on("change", makeHandleSettingChange(key)) - ); - await refreshStorage(); -}); + getSetting(['Improve', 'Complete', 'Ask']).forEach(key => { + $(`#settings-form input[name='text-${name}']:checkbox`).on('change', makeHandleSettingChange(key)) + }) + return refreshStorage() +}) diff --git a/scripts/service-worker.js b/scripts/service-worker.js index 201462b..4343890 100644 --- a/scripts/service-worker.js +++ b/scripts/service-worker.js @@ -1,3 +1,9 @@ +const settings = [ + { key: 'Complete', shortcut: 'Alt+C', status: 'enabled', type: 'Command' }, + { key: 'Improve', shortcut: 'Alt+I', status: 'enabled', type: 'Command' }, + { key: 'Ask', shortcut: 'Alt+A', status: 'enabled', type: 'Command' } +] + async function sendMessage(message) { const [tab] = await chrome.tabs.query({ active: true, lastFocusedWindow: true }) if (tab == null || tab.url?.startsWith('chrome://')) return undefined @@ -14,19 +20,58 @@ function addListener(commandName) { }) } +chrome.runtime.onInstalled.addListener((reason) => { + if (reason.reason === chrome.runtime.OnInstalledReason.INSTALL) { + checkCommandShortcuts() + } +}) + +// Only use this function during the initial install phase. After +// installation the user may have intentionally unassigned commands. +// Example for install commands: [{"description":"","name":"_execute_action","shortcut":""},{"description":"Use the selected text to ask GPT. It adds to the beginning of the selected text: 'In Latex, '","name":"Ask","shortcut":""},{"description":"Complete selected text","name":"Complete","shortcut":""},{"description":"Improve selected text","name":"Improve","shortcut":""}] +async function checkCommandShortcuts() { + chrome.commands.getAll((commands) => { + for (let { name, shortcut } of commands) { + let command = + settings.filter(({ type, key }) => 'Command' === type && name === key) + if (command.length > 0) { + command = command[0] + if (shortcut === '') { + command.status = 'error' + } + setSetting(command.key, command) + } + } + }) +} + +/** + * Set a setting in storage {@link https://developer.chrome.com/docs/extensions/reference/storage/#type-StorageArea:~:text=to%20the%20callback.-,set,-void} + * @param key + * @param value + * @param callback + */ +async function setSetting(key, value, callback = null) { + let obj = {} + obj[key] = value + chrome.storage.local.set(obj, callback).catch(error => { + console.log(`Failed to set ${key} setting. Error: ${error}`) + }) +} + +/** + * Get a setting from storage + * @param {string | string[] | object} [keys=null] - The keys to get (see {@link https://developer.chrome.com/docs/extensions/reference/storage/#usage}) + * @param {function} [callback=null] - Callback function + */ +async function getSetting(keys = null, callback = null) { + return chrome.storage.local.get(keys, callback) +} + async function setup() { addListener('Improve') addListener('Complete') addListener('Ask') - /*const [tab] = await chrome.tabs.query({ active: true, lastFocusedWindow: true }) - if (tab?.url?.startsWith('chrome://')) return undefined - console.log('tab'+tab) - chrome.scripting.executeScript({ - target: { tabId: tab.id }, - files: ['scripts/content.js'] - }).then(() => { - - })*/ } -setup() +setup() \ No newline at end of file