diff --git a/background.js b/background.js
index f3eef0d..1949c23 100644
--- a/background.js
+++ b/background.js
@@ -1,20 +1,85 @@
+// Define your token count at the top of the script
+let tokenCount = 0;
+
+// 4/25/23: Need to revisit this and dig deeper into seeing
+// if we are actually removing suggestion to be able to keep the conversation going.
+// at some point the conversation cannot continue if it exceeds 4097 tokens.
+// seems like I need to make an option to save the conversation and or something...
+// Define the token counting function
+function countTokens(text) {
+ let wordCount = text.split(' ').length;
+ let punctuationCount = (text.match(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g) || []).length;
+ return wordCount + punctuationCount;
+}
+
async function fetchApiKey() {
const { apiKey } = await chrome.storage.sync.get("apiKey");
return apiKey;
}
-// This is the proper URL to use.
-// https://api.openai.com/v1/engines/text-davinci-002/completions
+// Initialize the conversation in storage
+chrome.runtime.onInstalled.addListener(() => {
+ chrome.storage.sync.set({ conversation: [] });
+});
+
+// Only use this to test the notification notice.
+// The version should be one less that the version in the manifest file.
+/*chrome.storage.sync.set({ lastNotifiedVersion: '0.0.2', notificationClicked: 'no' }, () => {
+ console.log('Version set in storage and notification option stored');
+});*/
+/*chrome.storage.sync.get("lastNotifiedVersion", function(data) {
+ console.log('Last notified version:', data.lastNotifiedVersion);
+});
+chrome.storage.sync.get("notificationClicked", function(data) {
+ console.log('Was notification clicked:', data.notificationClicked);
+});*/
+
+// Update the last notified version in storage when the extension is updated
+chrome.runtime.onInstalled.addListener(details => {
+ if (details.reason === "update") {
+ const currentVersion = chrome.runtime.getManifest().version;
+ chrome.storage.sync.set({ lastNotifiedVersion: currentVersion });
+ }
+});
+
+// Update the conversation in the storage whenever it changes
+function updateConversation(convo) {
+ chrome.storage.sync.set({ conversation: convo });
+}
+
+let conversation = [];
+chrome.storage.sync.get("conversation", function(data) {
+ if (data.conversation !== undefined) {
+ conversation = data.conversation;
+ tokenCount = conversation.reduce((sum, message) => sum + message.tokens, 0);
+ }
+});
+
async function getSuggestionsFromApi(apiKey, prompt, maxTokens, n, stop, temperature, engine) {
+
if (!apiKey) {
- throw new Error("API key not provided. Please enter your API key in the settings.");
+ throw new Error("API key not provided. To get started please click on the Menu option in the top right corner, click on Settings and then enter your API key.");
}
+ // gpt-3.5-turbo
+ const model = engine === 'gpt3' ? 'gpt-3.5-turbo' : 'gpt-4';
+
+ const engineURL = 'https://api.openai.com/v1/chat/completions';
- // gpt3: curie
- // gpt4: text-davinci-002
- const engineURL = engine === 'gpt3' ? 'https://api.openai.com/v1/engines/curie/completions' : 'https://api.openai.com/v1/engines/text-davinci-002/completions';
+ let promptTokens = countTokens(prompt);
+ conversation.push({role: "user", content: prompt, tokens: promptTokens});
+ tokenCount += promptTokens;
- console.log( engineURL );
+ while (tokenCount > 4096 && conversation.length > 0) {
+ let removedMessage = conversation.shift();
+ tokenCount -= removedMessage.tokens; // Subtract the tokens of the removed message
+ }
+
+ if (promptTokens > 4096) {
+ throw new Error("Your message is too long! Please limit messages to 4096 tokens.");
+ }
+
+ // copy the conversation array and remove tokens property
+ const conversationCopy = conversation.map(({ role, content }) => ({ role, content }));
const response = await fetch(engineURL, {
method: "POST",
@@ -23,8 +88,9 @@ async function getSuggestionsFromApi(apiKey, prompt, maxTokens, n, stop, tempera
"Authorization": `Bearer ${apiKey}`
},
body: JSON.stringify({
- prompt: prompt,
- max_tokens: parseInt(maxTokens) || 250,
+ model: model,
+ messages: conversationCopy,
+ max_tokens: parseInt(maxTokens) || 2000,
n: parseInt(n) || 1,
stop: stop || null,
temperature: parseFloat(temperature) || 0.1
@@ -35,21 +101,37 @@ async function getSuggestionsFromApi(apiKey, prompt, maxTokens, n, stop, tempera
const errorData = await response.json();
if (errorData.error && errorData.error.code === "invalid_api_key") {
throw new Error("Invalid API key provided. Please check your API key.");
- } else {
+ }
+ else if (errorData.error && errorData.error.message === "The model: `gpt-4` does not exist") {
+ //console.log( errorData.error );
+ throw new Error("Please choose the GPT-3 Option from the Settings page to continue. The OpenAI API Key you entered does not have GPT-4 access yet.
You must have explicitly been granted a GPT-4 API Key from OpenAI for the GPT-4 option to work on our Settings page. You can apply for access here https://openai.com/waitlist/gpt-4-api");
+ }
+ else{
throw new Error("Unexpected response from OpenAI API: " + JSON.stringify(errorData));
}
}
const data = await response.json();
if (data.choices) {
- return data.choices.map((choice) => choice.text.trim());
+ let suggestionResponses = data.choices.map((choice) => {
+ let messageContent = choice.message.content.trim();
+ let tokens = countTokens(messageContent);
+ conversation.push({role: "assistant", content: messageContent, tokens: tokens});
+
+ // Notify loading-suggestions.js that a new suggestion has been added
+ chrome.runtime.sendMessage({ action: "newSuggestionAdded", tokens: tokens });
+
+ return messageContent;
+ });
+ return suggestionResponses;
} else {
console.error("Unexpected response from OpenAI API:", JSON.stringify(data, null, 2));
return [];
}
}
-chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
+
+chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === "getSuggestions") {
if (request.text) {
fetchApiKey().then(async function (apiKey) {
@@ -58,12 +140,13 @@ chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
const suggestions = await getSuggestionsFromApi(
apiKey,
request.text,
- settings.maxTokens || 50,
+ settings.maxTokens || 2000,
settings.n || 1,
settings.stop || null,
settings.temperature || 0.1,
- settings.engine || 'gpt4'
+ settings.engine || 'gpt3'
);
+ tokenCount += suggestions.reduce((sum, suggestion) => sum + countTokens(suggestion), 0);
sendResponse({ suggestions: suggestions });
} catch (error) {
console.error('Error:', error);
@@ -77,5 +160,53 @@ chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
console.error("No text provided.");
}
}
+
+ if (request.action === "clearConversationAndTokenCount") {
+ conversation = []; // Clear the conversation array
+ tokenCount = 0; // Clear the token count
+ updateConversation(conversation); // Save the cleared conversation
+ sendResponse({ message: "Conversation and token count cleared." });
+ return true;
+ }
+
+
+
+ if (request.action === "fetchChangelog") {
+ fetchChangelog()
+ .then(changelog => sendResponse({changelog: changelog}))
+ .catch(error => sendResponse({error: error.message}));
+ return true; // Indicate that we will send a response asynchronously
+ }
});
+function fetchChangelog(attempt = 1) {
+ const maxAttempts = 3;
+ const repo = 'https://raw.githubusercontent.com/spencerslickremix/Slick-Personal-Assistant/main/';
+
+ return new Promise((resolve, reject) => {
+ fetch(repo + 'changelog.txt')
+ .then(response => {
+ if (response.status === 404) {
+ throw new Error('The changelog could not be found. Please check the file path.');
+ } else if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+ return response.text();
+ })
+ .then(changelog => {
+ resolve(changelog);
+ })
+ .catch(error => {
+ if (error.message.includes('changelog could not be found')) {
+ // If the changelog was not found, reject the promise immediately
+ reject(error);
+ } else if (attempt < maxAttempts) {
+ // If the fetch failed for a different reason and we haven't reached the max number of attempts, try again
+ fetchChangelog(attempt + 1).then(resolve).catch(reject);
+ } else {
+ // If we've reached the max number of attempts, reject the promise
+ reject(error);
+ }
+ });
+ });
+}
diff --git a/changelog.txt b/changelog.txt
new file mode 100644
index 0000000..b68518a
--- /dev/null
+++ b/changelog.txt
@@ -0,0 +1,28 @@
+= Version 0.0.3 Monday, May 29th, 2023 =
+ * New: Settings: Custom font size option.
+ * New: Settings: Custom background and color options.
+ * New: Settings: Choose the width of the Popup.
+ * New: Suggestions: Added Token Count so users can see how many tokens are used per response. This text will also link you directly to the Settings page for quicker navigation.
+ * New: Header: The SlickRemix logo now links to the website, and the text "Your Personal Automated Assistant" links to the Suggestions tab for quicker navigation.
+ * New: Organized files in their respective folders.
+ * New: Namespaced and classify entire extension.
+ * Updated: Select a portion of text from a response to be copied automatically.
+ * Updated: Settings description for Max Tokens and Temperature to be more clear.
+ * Fixed: Added additional checks to Regex to parse URLs properly. We will probably end up adding a library in the future.
+ * Fixed: Added error messages to the suggestion list so users know when there is a problem.
+ * Fixed: Added GPT-4 specific error message to let users know if the API token they have entered will work with GPT-4 if they chose that model.
+ * Removed: content_scripts: matches": ["http://*/*", "https://*/*"] & js": ["contentScript.js"]. These options were not needed because I am calling chrome.scripting.executeScript from the popup.js file already, so the call was being duplicated.
+
+= Version 0.0.2 Thursday, May 18th, 2023 =
+ * New: Replaced model davinci-003 with gpt-3.5-turbo
+ * New: Updated completions to use /chat/completions
+ * New: Save user and response text to array to chrome.storage
+ * New: Complete UI change. Made Dark Mode true by default now.
+ * New: Regex to parse URLs properly.
+ * New: Settings: Display Twitter icon to quickly share responses.
+ * New: Custom Prompts tab. Includes the option to add up to 5 custom prompts or chose to display prompts saved from a GitHub accounts readme.md file.
+ * New: Showdown.js to convert code to HTML and Prism.js to beatify the code.
+ * Removed: permissions: tabs. Was not actually needed and was warned by Google it needed to be removed.
+
+= Version 0.0.1 Monday, May 2nd, 2023 =
+ * Initial Release
\ No newline at end of file
diff --git a/content.js b/content.js
deleted file mode 100644
index debd73a..0000000
--- a/content.js
+++ /dev/null
@@ -1,18 +0,0 @@
-chrome.runtime.onConnect.addListener((port) => {
- if (port.name === "replaceSelectedText") {
- port.onMessage.addListener((request) => {
- if (request.action === 'replaceSelectedText') {
- const selectedText = window.getSelection();
- const activeElement = document.activeElement;
-
- if (activeElement.tagName.toLowerCase() === 'textarea' && selectedText.toString().length > 0) {
- const newText = activeElement.value.substring(0, activeElement.selectionStart) + request.replacementText + activeElement.value.substring(activeElement.selectionEnd);
- activeElement.value = newText;
- port.postMessage({ success: true });
- } else {
- port.postMessage({ success: false, message: 'No text is selected in a textarea.' });
- }
- }
- });
- }
-});
diff --git a/layout/css/custom/background/dark.css b/layout/css/custom/background/dark.css
new file mode 100644
index 0000000..1b1f5c7
--- /dev/null
+++ b/layout/css/custom/background/dark.css
@@ -0,0 +1,93 @@
+body {
+ background: #202123;
+}
+.header {
+ color: #ececf1;
+}
+.tabcontent p {
+ color: #c6c6c6;
+}
+.container input {
+ color: #ececf1;
+}
+.toggle-label {
+ color: #b6b6b6;
+}
+label {
+ color: #939393 !important;
+}
+.suggestionsLink.slickremix-title {
+ color: #ffffff;
+}
+span.slickremix-logo:before {
+ color: #f0f0f0;
+}
+.switch-description {
+ color: #f0f0f0;
+}
+.tab {
+ background-color: #f2f2f2;
+ border-bottom: 1px solid #e0e0e0;
+}
+.settings-content {
+ background-color: #ececf1;
+}
+.suggestion {
+ background: #40414f;
+ color:#dbdbdb
+}
+.tabcontent h3 {
+ color: #f9f8f8;
+}
+.suggestion.user-input {
+ background: #202123;
+}
+#suggestions-tab .content {
+ background: #40414f;
+}
+textarea {
+ border: 1px solid #e0e0e0;
+ color:#d7d7d7
+}
+.copied-overlay {
+ color: #ececf1;
+}
+.dot {
+ background-color: #ececf1;
+}
+#githubUrlContainer .helper-text {
+ color: #ececf1
+}
+.switch-wrap {
+ background: #444654;
+}
+.prompt-wrap {
+ background: #444654;
+}
+.material-tooltip {
+ background: #2e313b;
+}
+.settings-menu li {
+ background: #202123;
+}
+#custom-context-menu {
+ background-color: #202123;
+}
+#changelog-container {
+ color: #fff;
+ border-top: 1px dashed #4f5052;
+}
+input:-webkit-autofill,
+textarea:-webkit-autofill,
+select:-webkit-autofill {
+ -webkit-box-shadow: 0 0 0 1000px #202123 inset !important;
+ /*use inset box-shadow to cover background-color*/
+ -webkit-text-fill-color: #ffffff !important;
+ /*use text fill color to cover font color*/
+}
+#custom-context-menu-settings-tab input:-webkit-autofill,
+#custom-context-menu-settings-tab textarea:-webkit-autofill,
+#custom-context-menu-settings-tab select:-webkit-autofill {
+ -webkit-box-shadow: 0 0 0 1000px #444654 inset !important;
+ /*use inset box-shadow to cover background-color*/
+}
\ No newline at end of file
diff --git a/layout/css/custom/background/darker.css b/layout/css/custom/background/darker.css
new file mode 100644
index 0000000..c879192
--- /dev/null
+++ b/layout/css/custom/background/darker.css
@@ -0,0 +1,93 @@
+body {
+ background: #000000;
+}
+.header {
+ color: #ffffff;
+}
+.tabcontent p {
+ color: #c6c6c6;
+}
+.container input {
+ color: #ececf1;
+}
+.toggle-label {
+ color: #b6b6b6;
+}
+label {
+ color: #939393 !important;
+}
+.suggestionsLink.slickremix-title {
+ color: #ffffff;
+}
+span.slickremix-logo:before {
+ color: #f0f0f0;
+}
+.switch-description {
+ color: #f0f0f0;
+}
+.tab {
+ background-color: #f2f2f2;
+ border-bottom: 1px solid #e0e0e0;
+}
+.settings-content {
+ background-color: #ececf1;
+}
+.suggestion {
+ background: #1a1a1a;
+ color:#dbdbdb
+}
+.tabcontent h3 {
+ color: #f9f8f8;
+}
+.suggestion.user-input {
+ background: #000000;
+}
+#suggestions-tab .content {
+ background: #1a1a1a;
+}
+textarea {
+ border: 1px solid #e0e0e0;
+ color:#d7d7d7
+}
+.copied-overlay {
+ color: #ececf1;
+}
+.dot {
+ background-color: #ececf1;
+}
+#githubUrlContainer .helper-text {
+ color: #ececf1
+}
+.switch-wrap {
+ background: #19191a;
+}
+.prompt-wrap {
+ background: #1a1a1a;
+}
+.material-tooltip {
+ background: #2c2c2c;
+}
+.settings-menu li {
+ background: #141414;
+}
+#custom-context-menu {
+ background-color: #000000;
+}
+#changelog-container {
+ color: #fff;
+ border-top: 1px dashed #4f5052;
+}
+input:-webkit-autofill,
+textarea:-webkit-autofill,
+select:-webkit-autofill {
+ -webkit-box-shadow: 0 0 0 1000px #000000 inset !important;
+ /*use inset box-shadow to cover background-color*/
+ -webkit-text-fill-color: #ececf1 !important;
+ /*use text fill color to cover font color*/
+}
+#custom-context-menu-settings-tab input:-webkit-autofill,
+#custom-context-menu-settings-tab textarea:-webkit-autofill,
+#custom-context-menu-settings-tab select:-webkit-autofill {
+ -webkit-box-shadow: 0 0 0 1000px #1a1a1a inset !important;
+ /*use inset box-shadow to cover background-color*/
+}
\ No newline at end of file
diff --git a/layout/css/custom/background/default.css b/layout/css/custom/background/default.css
new file mode 100644
index 0000000..a145619
--- /dev/null
+++ b/layout/css/custom/background/default.css
@@ -0,0 +1,69 @@
+.header {
+ color: #ffffff;
+}
+.switch-description {
+ color: #686767;
+}
+.tabcontent p {
+ color: #989696;
+}
+.suggestionsLink.slickremix-title {
+ color: #ffffff;
+}
+span.slickremix-logo:before {
+ color: #f0f0f0;
+}
+label {
+ color: #929292 !important;
+}
+.tab {
+ background-color: #f2f2f2;
+ border-bottom: 1px solid #e0e0e0;
+}
+.settings-content {
+ background-color: #fff;
+}
+.suggestion {
+ background: #f7f7f7;
+}
+#suggestions-tab .content {
+ background: #f7f7f7;
+}
+textarea {
+ border: 1px solid #e0e0e0;
+ color:#444444;
+}
+.copied-overlay {
+ color: #fff;
+}
+.dot {
+ background-color: #939393;
+}
+#githubUrlContainer .helper-text {
+ color: #989696
+}
+.switch-wrap {
+ background: #f7f7f7;
+}
+.prompt-wrap {
+ box-shadow: 1px 2px 20px 1px #e3dfdf85;
+ background: ffffff;
+}
+.settings-menu li {
+ background: #0e0e0e;
+}
+#custom-context-menu {
+ background-color: #222222;
+}
+#changelog-container {
+ color: #555555;
+ border-top: 1px dashed #4f5052;
+}
+input:-webkit-autofill,
+textarea:-webkit-autofill,
+select:-webkit-autofill {
+ -webkit-box-shadow: 0 0 0 1000px #ffffff inset !important;
+ /*use inset box-shadow to cover background-color*/
+ -webkit-text-fill-color: #000000 !important;
+ /*use text fill color to cover font color*/
+}
\ No newline at end of file
diff --git a/layout/css/custom/background/twitter.css b/layout/css/custom/background/twitter.css
new file mode 100644
index 0000000..adaee19
--- /dev/null
+++ b/layout/css/custom/background/twitter.css
@@ -0,0 +1,93 @@
+body {
+ background: #15202b;
+}
+.header {
+ color: #ffffff;
+}
+.tabcontent p {
+ color: #c6c6c6;
+}
+.container input {
+ color: #ffffff;
+}
+.toggle-label {
+ color: #b6b6b6;
+}
+label {
+ color: #c7c7c7 !important;
+}
+.suggestionsLink.slickremix-title {
+ color: #ffffff;
+}
+span.slickremix-logo:before {
+ color: #f0f0f0;
+}
+.switch-description {
+ color: #f0f0f0;
+}
+.tab {
+ background-color: #f2f2f2;
+ border-bottom: 1px solid #e0e0e0;
+}
+.settings-content {
+ background-color: #fff;
+}
+.suggestion {
+ background: #18293a;
+ color:#dbdbdb
+}
+.tabcontent h3 {
+ color: #f9f8f8;
+}
+.suggestion.user-input {
+ background: #18293a00;
+}
+#suggestions-tab .content {
+ background: #18293a;
+}
+textarea {
+ border: 1px solid #e0e0e0;
+ color:#d7d7d7
+}
+.copied-overlay {
+ color: #fff;
+}
+.dot {
+ background-color: #fff;
+}
+#githubUrlContainer .helper-text {
+ color: #fff
+}
+.switch-wrap {
+ background: #18293a;
+}
+.prompt-wrap {
+ background: #18293a;
+}
+.material-tooltip {
+ background: #152634;
+}
+.settings-menu li {
+ background: #15202b;
+}
+#custom-context-menu {
+ background-color: #18293a;
+}
+#changelog-container {
+ color: #fff;
+ border-top: 1px dashed #4f5052;
+}
+input:-webkit-autofill,
+textarea:-webkit-autofill,
+select:-webkit-autofill {
+ -webkit-box-shadow: 0 0 0 1000px #15202b inset !important;
+ /*use inset box-shadow to cover background-color*/
+ -webkit-text-fill-color: #ffffff !important;
+ /*use text fill color to cover font color*/
+}
+#custom-context-menu-settings-tab input:-webkit-autofill,
+#custom-context-menu-settings-tab textarea:-webkit-autofill,
+#custom-context-menu-settings-tab select:-webkit-autofill {
+ -webkit-box-shadow: 0 0 0 1000px #18293a inset !important;
+ /*use inset box-shadow to cover background-color*/
+}
\ No newline at end of file
diff --git a/layout/css/custom/color/black.css b/layout/css/custom/color/black.css
new file mode 100644
index 0000000..516acc4
--- /dev/null
+++ b/layout/css/custom/color/black.css
@@ -0,0 +1,109 @@
+.header {
+ background-color: #121212;
+}
+a, .default-mode.black-color a {
+ color: #020202;
+}
+.dark-mode a, .twitter-mode a,
+.darker-mode a, .twitter-mode a{
+ color:#fefefe
+}
+
+.tablinks:hover:not(.active) {
+ background-color: #000000;
+}
+
+input:not([type]):focus:not([readonly]), input[type=text]:not(.browser-default):focus:not([readonly]), input[type=password]:not(.browser-default):focus:not([readonly]), input[type=email]:not(.browser-default):focus:not([readonly]), input[type=url]:not(.browser-default):focus:not([readonly]), input[type=time]:not(.browser-default):focus:not([readonly]), input[type=date]:not(.browser-default):focus:not([readonly]), input[type=datetime]:not(.browser-default):focus:not([readonly]), input[type=datetime-local]:not(.browser-default):focus:not([readonly]), input[type=tel]:not(.browser-default):focus:not([readonly]), input[type=number]:not(.browser-default):focus:not([readonly]), input[type=search]:not(.browser-default):focus:not([readonly]), textarea.materialize-textarea:focus:not([readonly]) {
+ border-bottom: 1px solid #000000;
+ -webkit-box-shadow: 0 1px 0 0 #000000;
+ box-shadow: 0 1px 0 0 #000000;
+}
+
+.settings-form input:focus {
+ border-color: #000000;
+ box-shadow: 0 0 5px rgba(0, 92, 185, 0.25);
+}
+
+button {
+ background-color: #1a1a1a;
+}
+.darker-mode .content button.submit-button-suggestions {
+ background-color: #000000;
+}
+.darker-mode .content button {
+ background-color: #1a1a1a;
+}
+.saved-prompts-list {
+ background: #1a1a1a;
+}
+.default-mode .saved-prompts-list a {
+ color: #FFFFFF;
+}
+#font-size::-webkit-slider-runnable-track, #popup-size::-webkit-slider-runnable-track {
+ background: #000000;
+}
+
+.darker-mode .settings-menu li {
+ background: #1a1a1a;
+}
+
+.settings-menu li.active {
+ background: #000000;
+}
+
+.darker-mode .settings-menu li.active {
+ background: #141414;
+}
+
+input[type=range]+.thumb {
+ background-color: #000000;
+}
+
+#font-size::-webkit-slider-runnable-track, #popup-size::-webkit-slider-runnable-track {
+ background: #000000;
+}
+
+#customPromptsLink, .suggestionsLink {
+ color: #444444;
+}
+.twitter-mode #customPromptsLink,
+.twitter-mode .suggestionsLink,
+.black-mode #customPromptsLink,
+.black-mode .suggestionsLink,
+.dark-mode #customPromptsLink,
+.dark-mode .suggestionsLink,
+.darker-mode #customPromptsLink,
+.darker-mode .suggestionsLink{
+ color: #ffffff;
+ font-weight:bold;
+}
+.default-mode #customPromptsLink,
+.default-mode .suggestionsLink{
+ color: #000000;
+}
+.default-mode .header .suggestionsLink,
+.dark-mode .header .suggestionsLink,
+.darker-mode .header .suggestionsLink{
+ color: #e8e6e6;
+ font-weight:normal;
+}
+.switch label input[type=checkbox]:checked + .lever {
+ background-color: #000000 !important;
+}
+
+#custom-context-menu li:hover {
+ background-color: #1a1a1a;
+}
+
+#changelog-container {
+ border-top: 2px dashed #4f5052;
+}
+
+textarea:focus {
+ border-color: #777777;
+ box-shadow: 0 0 5px rgba(0, 92, 185, 0.25);
+}
+
+button:hover, button:focus {
+ background-color: #3b3b3b;
+}
\ No newline at end of file
diff --git a/layout/css/custom/color/blue.css b/layout/css/custom/color/blue.css
new file mode 100644
index 0000000..3d57f01
--- /dev/null
+++ b/layout/css/custom/color/blue.css
@@ -0,0 +1,66 @@
+.header {
+ background-color: #005cb9;
+}
+a {
+ color: #197de3;
+}
+.tablinks:hover:not(.active) {
+ background-color: #005cb9;
+}
+
+input:not([type]):focus:not([readonly]), input[type=text]:not(.browser-default):focus:not([readonly]), input[type=password]:not(.browser-default):focus:not([readonly]), input[type=email]:not(.browser-default):focus:not([readonly]), input[type=url]:not(.browser-default):focus:not([readonly]), input[type=time]:not(.browser-default):focus:not([readonly]), input[type=date]:not(.browser-default):focus:not([readonly]), input[type=datetime]:not(.browser-default):focus:not([readonly]), input[type=datetime-local]:not(.browser-default):focus:not([readonly]), input[type=tel]:not(.browser-default):focus:not([readonly]), input[type=number]:not(.browser-default):focus:not([readonly]), input[type=search]:not(.browser-default):focus:not([readonly]), textarea.materialize-textarea:focus:not([readonly]) {
+ border-bottom: 1px solid #005cb9;
+ -webkit-box-shadow: 0 1px 0 0 #005cb9;
+ box-shadow: 0 1px 0 0 #005cb9;
+}
+
+.settings-form input:focus {
+ border-color: #005cb9;
+ box-shadow: 0 0 5px rgba(0, 92, 185, 0.25);
+}
+
+
+button {
+ background-color: #005cb9;
+}
+
+.saved-prompts-list {
+ background: #005cb9;
+}
+
+#font-size::-webkit-slider-runnable-track, #popup-size::-webkit-slider-runnable-track {
+ background: #005cb9;
+}
+
+.settings-menu li.active {
+ background: #005cb9;
+}
+
+input[type=range]+.thumb {
+ background-color: #005cb9;
+}
+
+#customPromptsLink, .suggestionsLink {
+ color: #187de3;
+}
+
+.switch label input[type=checkbox]:checked + .lever {
+ background-color: #005cb9 !important;
+}
+
+#custom-context-menu li:hover {
+ background-color: #005cb9;
+}
+
+#changelog-container {
+ border-top: 2px dashed #005cb9;
+}
+
+textarea:focus {
+ border-color: #0071ed;
+ box-shadow: 0 0 5px rgba(0, 92, 185, 0.25);
+}
+
+button:hover, button:focus {
+ background-color: #0071ed;
+}
\ No newline at end of file
diff --git a/layout/css/custom/color/green.css b/layout/css/custom/color/green.css
new file mode 100644
index 0000000..8eb95f3
--- /dev/null
+++ b/layout/css/custom/color/green.css
@@ -0,0 +1,69 @@
+.header {
+ background-color: #00ba7c;
+}
+a {
+ color:#00ba7c;
+}
+.tablinks:hover:not(.active) {
+ background-color: #00ba7c;
+}
+
+input:not([type]):focus:not([readonly]), input[type=text]:not(.browser-default):focus:not([readonly]), input[type=password]:not(.browser-default):focus:not([readonly]), input[type=email]:not(.browser-default):focus:not([readonly]), input[type=url]:not(.browser-default):focus:not([readonly]), input[type=time]:not(.browser-default):focus:not([readonly]), input[type=date]:not(.browser-default):focus:not([readonly]), input[type=datetime]:not(.browser-default):focus:not([readonly]), input[type=datetime-local]:not(.browser-default):focus:not([readonly]), input[type=tel]:not(.browser-default):focus:not([readonly]), input[type=number]:not(.browser-default):focus:not([readonly]), input[type=search]:not(.browser-default):focus:not([readonly]), textarea.materialize-textarea:focus:not([readonly]) {
+ border-bottom: 1px solid #00ba7c;
+ -webkit-box-shadow: 0 1px 0 0 #00ba7c;
+ box-shadow: 0 1px 0 0 #00ba7c;
+}
+
+.settings-form input:focus {
+ border-color: #00ba7c;
+ box-shadow: 0 0 5px rgba(0, 92, 185, 0.25);
+}
+
+
+button {
+ background-color: #00ba7c;
+}
+
+.saved-prompts-list {
+ background: #00ba7c;
+}
+
+#font-size::-webkit-slider-runnable-track, #popup-size::-webkit-slider-runnable-track {
+ background: #00ba7c;
+}
+
+.settings-menu li.active {
+ background: #00ba7c;
+}
+
+input[type=range]+.thumb {
+ background-color: #00ba7c;
+}
+
+#font-size::-webkit-slider-runnable-track, #popup-size::-webkit-slider-runnable-track {
+ background: #00ba7c;
+}
+
+#customPromptsLink, .suggestionsLink {
+ color: #00ba7c;
+}
+
+.switch label input[type=checkbox]:checked + .lever {
+ background-color: #00ba7c !important;
+}
+
+#custom-context-menu li:hover {
+ background-color: #00ba7c;
+}
+
+#changelog-container {
+ border-top: 2px dashed #00ba7c;
+}
+
+textarea:focus {
+ border-color: #0dda94;
+}
+
+button:hover, button:focus {
+ background-color: #0dda94;
+}
diff --git a/layout/css/custom/color/orange.css b/layout/css/custom/color/orange.css
new file mode 100644
index 0000000..0669313
--- /dev/null
+++ b/layout/css/custom/color/orange.css
@@ -0,0 +1,69 @@
+.header {
+ background-color: #ff7900;
+}
+a {
+ color:#ff7900;
+}
+.tablinks:hover:not(.active) {
+ background-color: #ff7900;
+}
+
+input:not([type]):focus:not([readonly]), input[type=text]:not(.browser-default):focus:not([readonly]), input[type=password]:not(.browser-default):focus:not([readonly]), input[type=email]:not(.browser-default):focus:not([readonly]), input[type=url]:not(.browser-default):focus:not([readonly]), input[type=time]:not(.browser-default):focus:not([readonly]), input[type=date]:not(.browser-default):focus:not([readonly]), input[type=datetime]:not(.browser-default):focus:not([readonly]), input[type=datetime-local]:not(.browser-default):focus:not([readonly]), input[type=tel]:not(.browser-default):focus:not([readonly]), input[type=number]:not(.browser-default):focus:not([readonly]), input[type=search]:not(.browser-default):focus:not([readonly]), textarea.materialize-textarea:focus:not([readonly]) {
+ border-bottom: 1px solid #ff7900;
+ -webkit-box-shadow: 0 1px 0 0 #ff7900;
+ box-shadow: 0 1px 0 0 #ff7900;
+}
+
+.settings-form input:focus {
+ border-color: #ff7900;
+ box-shadow: 0 0 5px rgba(0, 92, 185, 0.25);
+}
+
+
+button {
+ background-color: #ff7900;
+}
+
+.saved-prompts-list {
+ background: #ff7900;
+}
+
+#font-size::-webkit-slider-runnable-track, #popup-size::-webkit-slider-runnable-track {
+ background: #ff7900;
+}
+
+.settings-menu li.active {
+ background: #ff7900;
+}
+
+input[type=range]+.thumb {
+ background-color: #ff7900;
+}
+
+#font-size::-webkit-slider-runnable-track, #popup-size::-webkit-slider-runnable-track {
+ background: #ff7900;
+}
+
+#customPromptsLink, .suggestionsLink {
+ color: #ff7900;
+}
+
+.switch label input[type=checkbox]:checked + .lever {
+ background-color: #ff7900 !important;
+}
+
+#custom-context-menu li:hover {
+ background-color: #ff7900;
+}
+
+#changelog-container {
+ border-top: 2px dashed #ff7900;
+}
+
+textarea:focus {
+ border-color: #ff8018;
+}
+
+button:hover, button:focus {
+ background-color: #ff9b0d;
+}
diff --git a/layout/css/custom/color/pink.css b/layout/css/custom/color/pink.css
new file mode 100644
index 0000000..b473ec3
--- /dev/null
+++ b/layout/css/custom/color/pink.css
@@ -0,0 +1,69 @@
+.header {
+ background-color: #f91980;
+}
+a {
+ color:#f91980;
+}
+.tablinks:hover:not(.active) {
+ background-color: #f91980;
+}
+
+input:not([type]):focus:not([readonly]), input[type=text]:not(.browser-default):focus:not([readonly]), input[type=password]:not(.browser-default):focus:not([readonly]), input[type=email]:not(.browser-default):focus:not([readonly]), input[type=url]:not(.browser-default):focus:not([readonly]), input[type=time]:not(.browser-default):focus:not([readonly]), input[type=date]:not(.browser-default):focus:not([readonly]), input[type=datetime]:not(.browser-default):focus:not([readonly]), input[type=datetime-local]:not(.browser-default):focus:not([readonly]), input[type=tel]:not(.browser-default):focus:not([readonly]), input[type=number]:not(.browser-default):focus:not([readonly]), input[type=search]:not(.browser-default):focus:not([readonly]), textarea.materialize-textarea:focus:not([readonly]) {
+ border-bottom: 1px solid #f91980;
+ -webkit-box-shadow: 0 1px 0 0 #f91980;
+ box-shadow: 0 1px 0 0 #f91980;
+}
+
+.settings-form input:focus {
+ border-color: #f91980;
+ box-shadow: 0 0 5px rgba(0, 92, 185, 0.25);
+}
+
+
+button {
+ background-color: #f91980;
+}
+
+.saved-prompts-list {
+ background: #f91980;
+}
+
+#font-size::-webkit-slider-runnable-track, #popup-size::-webkit-slider-runnable-track {
+ background: #f91980;
+}
+
+.settings-menu li.active {
+ background: #f91980;
+}
+
+input[type=range]+.thumb {
+ background-color: #f91980;
+}
+
+#font-size::-webkit-slider-runnable-track, #popup-size::-webkit-slider-runnable-track {
+ background: #f91980;
+}
+
+#customPromptsLink, .suggestionsLink {
+ color: #f91980;
+}
+
+.switch label input[type=checkbox]:checked + .lever {
+ background-color: #f91980 !important;
+}
+
+#custom-context-menu li:hover {
+ background-color: #f91980;
+}
+
+#changelog-container {
+ border-top: 2px dashed #f91980;
+}
+
+textarea:focus {
+ border-color: #e31172;
+}
+
+button:hover, button:focus {
+ background-color: #e31172;
+}
diff --git a/layout/css/custom/color/purple.css b/layout/css/custom/color/purple.css
new file mode 100644
index 0000000..87b4c91
--- /dev/null
+++ b/layout/css/custom/color/purple.css
@@ -0,0 +1,69 @@
+.header {
+ background-color: #7856ff;
+}
+a {
+ color:#7856ff;
+}
+.tablinks:hover:not(.active) {
+ background-color: #7856ff;
+}
+
+input:not([type]):focus:not([readonly]), input[type=text]:not(.browser-default):focus:not([readonly]), input[type=password]:not(.browser-default):focus:not([readonly]), input[type=email]:not(.browser-default):focus:not([readonly]), input[type=url]:not(.browser-default):focus:not([readonly]), input[type=time]:not(.browser-default):focus:not([readonly]), input[type=date]:not(.browser-default):focus:not([readonly]), input[type=datetime]:not(.browser-default):focus:not([readonly]), input[type=datetime-local]:not(.browser-default):focus:not([readonly]), input[type=tel]:not(.browser-default):focus:not([readonly]), input[type=number]:not(.browser-default):focus:not([readonly]), input[type=search]:not(.browser-default):focus:not([readonly]), textarea.materialize-textarea:focus:not([readonly]) {
+ border-bottom: 1px solid #7856ff;
+ -webkit-box-shadow: 0 1px 0 0 #7856ff;
+ box-shadow: 0 1px 0 0 #7856ff;
+}
+
+.settings-form input:focus {
+ border-color: #7856ff;
+ box-shadow: 0 0 5px rgba(0, 92, 185, 0.25);
+}
+
+
+button {
+ background-color: #7856ff;
+}
+
+.saved-prompts-list {
+ background: #7856ff;
+}
+
+#font-size::-webkit-slider-runnable-track, #popup-size::-webkit-slider-runnable-track {
+ background: #7856ff;
+}
+
+.settings-menu li.active {
+ background: #7856ff;
+}
+
+input[type=range]+.thumb {
+ background-color: #7856ff;
+}
+
+#font-size::-webkit-slider-runnable-track, #popup-size::-webkit-slider-runnable-track {
+ background: #7856ff;
+}
+
+#customPromptsLink, .suggestionsLink {
+ color: #7856ff;
+}
+
+.switch label input[type=checkbox]:checked + .lever {
+ background-color: #7856ff !important;
+}
+
+#changelog-container {
+ border-top: 2px dashed #7856ff;
+}
+
+#custom-context-menu li:hover {
+ background-color: #7856ff;
+}
+
+textarea:focus {
+ border-color: #886cff;
+}
+
+button:hover, button:focus {
+ background-color: #886cff;
+}
diff --git a/layout/css/custom/color/yellow.css b/layout/css/custom/color/yellow.css
new file mode 100644
index 0000000..4147dc3
--- /dev/null
+++ b/layout/css/custom/color/yellow.css
@@ -0,0 +1,70 @@
+.header {
+ background-color: #ffd400;
+}
+a {
+ color:#ffd400;
+}
+.tablinks:hover:not(.active) {
+ background-color: #ffd400;
+}
+
+input:not([type]):focus:not([readonly]), input[type=text]:not(.browser-default):focus:not([readonly]), input[type=password]:not(.browser-default):focus:not([readonly]), input[type=email]:not(.browser-default):focus:not([readonly]), input[type=url]:not(.browser-default):focus:not([readonly]), input[type=time]:not(.browser-default):focus:not([readonly]), input[type=date]:not(.browser-default):focus:not([readonly]), input[type=datetime]:not(.browser-default):focus:not([readonly]), input[type=datetime-local]:not(.browser-default):focus:not([readonly]), input[type=tel]:not(.browser-default):focus:not([readonly]), input[type=number]:not(.browser-default):focus:not([readonly]), input[type=search]:not(.browser-default):focus:not([readonly]), textarea.materialize-textarea:focus:not([readonly]) {
+ border-bottom: 1px solid #ffd400;
+ -webkit-box-shadow: 0 1px 0 0 #ffd400;
+ box-shadow: 0 1px 0 0 #ffd400;
+}
+
+.settings-form input:focus {
+ border-color: #ffd400;
+ box-shadow: 0 0 5px rgba(0, 92, 185, 0.25);
+}
+
+
+button {
+ background-color: #ffd400;
+}
+
+.saved-prompts-list {
+ background: #ffd400;
+}
+
+#font-size::-webkit-slider-runnable-track, #popup-size::-webkit-slider-runnable-track {
+ background: #ffd400;
+}
+
+.settings-menu li.active {
+ background: #ffd400;
+}
+
+input[type=range]+.thumb {
+ background-color: #ffd400;
+}
+
+#font-size::-webkit-slider-runnable-track, #popup-size::-webkit-slider-runnable-track {
+ background: #ffd400;
+}
+
+#customPromptsLink, .suggestionsLink {
+ color: #ffd400;
+}
+
+.switch label input[type=checkbox]:checked + .lever {
+ background-color: #ffd400 !important;
+}
+
+#custom-context-menu li:hover {
+ background-color: #ffd400;
+}
+
+#changelog-container {
+ border-top: 2px dashed #ffd400;
+}
+
+textarea:focus {
+ border-color: #ffe04a;
+ box-shadow: 0 0 5px rgba(0, 92, 185, 0.25);
+}
+
+button:hover, button:focus {
+ background-color: #ffe04a;
+}
diff --git a/layout/css/fonts/SlickRemix.eot b/layout/css/fonts/SlickRemix.eot
new file mode 100644
index 0000000..e69de29
diff --git a/layout/css/fonts/SlickRemix.svg b/layout/css/fonts/SlickRemix.svg
new file mode 100644
index 0000000..3179ad0
--- /dev/null
+++ b/layout/css/fonts/SlickRemix.svg
@@ -0,0 +1,15 @@
+
+
+
\ No newline at end of file
diff --git a/layout/css/fonts/SlickRemix.ttf b/layout/css/fonts/SlickRemix.ttf
new file mode 100644
index 0000000..cc2eb08
Binary files /dev/null and b/layout/css/fonts/SlickRemix.ttf differ
diff --git a/layout/css/fonts/SlickRemix.woff b/layout/css/fonts/SlickRemix.woff
new file mode 100644
index 0000000..e69de29
diff --git a/layout/css/popup.css b/layout/css/popup.css
index 9b0d02b..37d733b 100644
--- a/layout/css/popup.css
+++ b/layout/css/popup.css
@@ -1,35 +1,61 @@
+html {
+ font-size: var(--font-size, 100%);
+}
+
body {
font-family: Arial, sans-serif;
margin: 0;
- padding: 0;
- width: 400px;
+ padding: 70px 0 0;
+ width: 450px;
display: flex;
flex-direction: column;
- /* min-height: 100vh; */
}
+@font-face {
+ font-family: SlickRemix;
+ src: url(fonts/SlickRemix.eot?p9iqv);
+ src: url(fonts/SlickRemix.eot?#iefixp9iqv) format('embedded-opentype'), url(fonts/SlickRemix.ttf?p9iqv) format('truetype'), url(fonts/SlickRemix.svg?p9iqv#SlickRemix) format('svg');
+ font-weight: 400;
+ font-style: normal
+}
+
+span.slickremix-logo:before {
+ font-family: SlickRemix;
+ content: "\e600";
+ line-height: 1.2em;
+ font-size: 22px;
+ margin-right: 9px;
+ font-style: normal;
+}
+
+.slickremix-title {
+ position: relative;
+ top: -3px;
+ font-style: italic;
+ font-size: 16px;
+}
+
+
.header {
- background-color: #005cb9;
- color: white;
- text-align: center;
- padding: 16px;
- font-size: 17px;
- font-weight: 600;
+ text-align: left;
+ padding: 18px 18px 18px 20px;
+ font-weight: normal;
position: fixed;
top: 0;
left: 0;
width: 100%;
- z-index: 10;
+ z-index: 11;
+ box-shadow: 0px 0px 3px 0px #000000;
}
.tab {
margin-top: 60px;
display: flex;
justify-content: space-around;
- background-color: #f2f2f2;
padding: 10px 0;
- border-bottom: 1px solid #e0e0e0;
width: 100%;
+ z-index: 1;
+ position: relative;
}
.tablinks {
@@ -38,41 +64,45 @@ body {
outline: none;
cursor: pointer;
padding: 10px 16px;
- font-size: 14px;
- color: #666;
+ font-size:1.2em;
+ font-weight: normal;
+ color: #b0afaf;
}
.tablinks.active {
- background-color: #fffdfd;
- color: #333;
+ /* background-color: #fffdfd; */
+ /* color: #333; */
}
.suggestion {
- padding: 10px 15px;
- background:#f7f7f7;
- border:1px solid #dcd9d9;
- border-radius:3px;
- font-size:14px;
- margin:10px 0;
- overflow: hidden;
+ white-space: pre-wrap;
+ padding: 17px 25px;
+ border-radius: 3px;
+ font-size:1.2em;
+ margin: 10px 0;
+ word-break: break-word;
+ overflow-wrap: break-word;
+ position: relative;
+ color: #444444;
}
-button:focus {
+
+.suggestion button:focus {
outline: none;
- background: none;
+ background: none !important;
}
-.submit-button-suggestions:focus, .submit-button:focus {
- background:#015cb9;
+
+button:focus {
+ outline: none;
}
.tablinks:hover:not(.active) {
- background-color: #ffffff;
- color: #333;
+ color: #fff;
}
.tabcontent {
display: none;
flex-grow: 1;
- padding: 16px; /* Add padding to the content */
+ padding: 15px 10px; /* Add padding to the content */
}
.settings-form {
@@ -87,34 +117,46 @@ button:focus {
}
label {
- position:relative;
- padding-left: 23px;
- color: #929292;
+ font-size: .84rem;
+ position: relative;
+ padding-left: 5px;
}
-.switch label {
+
+.gpt-description {
+ padding: 0 0 20px 0;
+ font-size:1.1em;
+}
+
+.gpt-description strong {
+ font-weight: bold;
+}
+
+#custom-context-menu-settings-tab label {
padding-left: 0px;
}
+#custom-context-menu-settings-tab input {
+ margin-bottom: 10px;
+}
-.toggle-label {
- display:inline-block;
- min-width:35px;
+.switch label {
+ padding-left: 0px;
}
-input:not([type]):focus:not([readonly]), input[type=text]:not(.browser-default):focus:not([readonly]), input[type=password]:not(.browser-default):focus:not([readonly]), input[type=email]:not(.browser-default):focus:not([readonly]), input[type=url]:not(.browser-default):focus:not([readonly]), input[type=time]:not(.browser-default):focus:not([readonly]), input[type=date]:not(.browser-default):focus:not([readonly]), input[type=datetime]:not(.browser-default):focus:not([readonly]), input[type=datetime-local]:not(.browser-default):focus:not([readonly]), input[type=tel]:not(.browser-default):focus:not([readonly]), input[type=number]:not(.browser-default):focus:not([readonly]), input[type=search]:not(.browser-default):focus:not([readonly]), textarea.materialize-textarea:focus:not([readonly]) {
- border-bottom: 1px solid #1a73e8;
- -webkit-box-shadow: 0 1px 0 0 #1a73e8;
- box-shadow: 0 1px 0 0 #1a73e8;
+
+.toggle-label {
+ display: inline-block;
+ min-width: 35px;
}
-input:not([type]):focus:not([readonly])+label, input[type=text]:not(.browser-default):focus:not([readonly])+label, input[type=password]:not(.browser-default):focus:not([readonly])+label, input[type=email]:not(.browser-default):focus:not([readonly])+label, input[type=url]:not(.browser-default):focus:not([readonly])+label, input[type=time]:not(.browser-default):focus:not([readonly])+label, input[type=date]:not(.browser-default):focus:not([readonly])+label, input[type=datetime]:not(.browser-default):focus:not([readonly])+label, input[type=datetime-local]:not(.browser-default):focus:not([readonly])+label, input[type=tel]:not(.browser-default):focus:not([readonly])+label, input[type=number]:not(.browser-default):focus:not([readonly])+label, input[type=search]:not(.browser-default):focus:not([readonly])+label, textarea.materialize-textarea:focus:not([readonly])+label {
+input:not([type]):focus:not([readonly]) + label, input[type=text]:not(.browser-default):focus:not([readonly]) + label, input[type=password]:not(.browser-default):focus:not([readonly]) + label, input[type=email]:not(.browser-default):focus:not([readonly]) + label, input[type=url]:not(.browser-default):focus:not([readonly]) + label, input[type=time]:not(.browser-default):focus:not([readonly]) + label, input[type=date]:not(.browser-default):focus:not([readonly]) + label, input[type=datetime]:not(.browser-default):focus:not([readonly]) + label, input[type=datetime-local]:not(.browser-default):focus:not([readonly]) + label, input[type=tel]:not(.browser-default):focus:not([readonly]) + label, input[type=number]:not(.browser-default):focus:not([readonly]) + label, input[type=search]:not(.browser-default):focus:not([readonly]) + label, textarea.materialize-textarea:focus:not([readonly]) + label {
color: #a4a6a7;
}
.settings-form input {
width: 100%;
padding: 8px;
- font-size: 14px;
+ font-size:1.2em;
border: 1px solid #e0e0e0;
border-radius: 6px;
box-sizing: border-box;
@@ -122,35 +164,22 @@ input:not([type]):focus:not([readonly])+label, input[type=text]:not(.browser-def
.settings-form input:focus {
outline: none;
- border-color: #005cb9;
- box-shadow: 0 0 5px rgba(0, 92, 185, 0.25);
}
/* Google Forms-like style for textarea and buttons */
textarea,
button {
font-family: 'Roboto', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
- font-size: 14px;
+ font-size: 1.15em;
}
-textarea {
- padding: 8px;
- border: 1px solid #e0e0e0;
- border-radius: 4px;
- box-sizing: border-box;
- resize: vertical;
- width: 100%;
- min-height: 200px;
-}
textarea:focus {
outline: none;
- border-color: #005cb9;
- box-shadow: 0 0 5px rgba(0, 92, 185, 0.25);
+ box-shadow: 0 0 5px rgba(117, 117, 117, 0.25);
}
button {
- background-color: #005cb9;
color: white;
border: none;
border-radius: 50px;
@@ -158,20 +187,23 @@ button {
cursor: pointer;
font-weight: 500;
transition: background-color 0.3s;
-}
-
-button:hover {
- background-color: #0073e6;
+ line-height:1.1
}
.submit-button {
margin: 30px 0 15px;
}
+
+#settings-form .submit-button {
+ margin: 15px 0 15px;
+}
+
.submit-button-suggestions {
- margin: 10px 0 15px;
+ margin: 10px 0 8px;
}
+
#temperature {
- margin-bottom: 5px;
+ margin-bottom: 25px;
}
.copied-overlay {
@@ -179,10 +211,9 @@ button:hover {
top: 50%;
left: 50%;
background-color: rgba(0, 0, 0, 0.7);
- color: #fff;
padding: 8px 16px;
border-radius: 4px;
- font-size: 14px;
+ font-size: 1.15em;
font-weight: bold;
opacity: 0;
animation: fade-in-out 1.5s forwards;
@@ -205,19 +236,21 @@ button:hover {
#suggestions-container {
position: relative;
+ margin-bottom: 120px;
+ padding-right: 10px;
+ margin-right: -10px;
}
.loading-animation {
display: flex;
justify-content: center;
align-items: center;
- margin: 20px 0;
+ margin: 40px 0;
}
.dot {
width: 8px;
height: 8px;
- background-color: #939393;
border-radius: 50%;
margin: 0 3px;
animation: dotBounce 1.4s infinite;
@@ -241,81 +274,504 @@ button:hover {
}
-.material-icons {
- font-size: 19px;
- position: absolute;
- margin: 0;
- padding: 0;
- top: -3px;
- left: 0;
+.settings-wrap .material-icons {
+ font-size: 1.5em;
+ margin:0 !important;
+ padding:0 !important;
+ float:left;
}
.material-tooltip {
width: 340px !important;
text-align: left;
- padding:15px;
- z-index:1000;
+ padding: 15px;
+ z-index: 1000;
+ font-size: 1.2em;
}
.material-tooltip {
left: 30px !important;
- margin-top:-10px
+ margin-top: -10px
}
-.saving-overlay {
- font-size: 16px;
+.overlay {
+ font-size: 1.3em;
+ opacity: 0;
+ animation: fade-in-out 1.5s forwards;
}
-.gtp-model-type {
- margin: 20px 0 25px;
+
+.overlay.fade-out {
+ opacity: 0;
}
+.switch-wrap {
+ margin: 4px 0;
+ padding: 5px 15px 25px 25px;
+}
+.switch-description {
+ padding: 15px 0 0;
+ font-size: 1.08em;
+ margin: 0 0 15px;
+}
+.share-container {
+ display: flex;
+ justify-content: space-between;
+ margin-top: 5px;
+ position: absolute;
+ right: -4px;
+ top: -23px;
+}
+.share-button {
+ background: none;
+ border: none;
+ color: white;
+ cursor: pointer;
+ font-size: 14px;
+ padding: 4px 8px;
+ text-align: center;
+ text-decoration: none;
+ border-radius: 4px;
+}
-/* Dark mode styles */
-.dark-mode {
- background-color: #212121;
- color:#ffffff;
+.share-button svg {
+ fill: #1c9cf2;
+ max-width: 18px;
}
-.dark-mode .header {
- background-color: #424242;
+.share-button:hover {
+ background: none;
}
-.dark-mode textarea,
-.dark-mode input[type="text"] {
- color: #ffffff;
- border-color: #616161;
+.share-button svg:hover {
+ fill: #0f98cd;
}
-.dark-mode .suggestion {
- background-color: #424242;
- color: #ffffff;
- border-color: #616161;
+.settings-wrap {
+ margin-bottom: 20px
}
-.dark-mode .tab {
- background-color: #292929;
+#githubUrlContainer .helper-text {
+ font-size: 1.1em;
+ color: #989696
}
-.dark-mode .tab button {
- color: #ffffff;
+#githubUrlContainer .prompt-wrap {
+ padding-top: 5px
+}
+
+.switch label input[type=checkbox]:checked + .lever:after {
+ background-color: #d9d9d9;
+}
+
+.gtp-model-type label {
+ display: block;
+}
+
+#suggestions-tab .content {
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ padding: 10px 15px;
+ box-shadow: -2px 8px 16px 4px #aeacac00;
+ width: 100%;
+ z-index: 1;
+}
+
+#suggestions-tab .content textarea {
+ border: none;
+ overflow-y: auto;
+ resize: none;
+ max-height: 250px;
}
-.dark-mode .tab button:hover {
- background-color: #616161;
+textarea {
+ padding: 8px 8px 0 8px;
+ border-radius: 4px;
+ box-sizing: border-box;
+ width: 100%;
+ box-shadow: none !important;
+}
+
+#suggestions-tab {
+ min-height: 40px;
+}
+
+#suggestions-tab.tabcontent {
+ display: none;
+ flex-grow: 1;
+ padding: 0; /* Add padding to the content */
+}
+
+#submit-button {
+ margin-left: 5px;
+}
+
+#suggestions-container {
+ height: 403px;
+ overflow: auto;
+}
+
+/* Add styles for the custom context menu */
+#custom-context-menu {
+ display: none;
+ position: absolute;
+ border: 1px solid #4f4e4e;
+ border-radius: 5px;
+ box-shadow: 0px 5px 20px 0px rgb(0 0 0 / 30%);
+ z-index: 100;
color: #fff;
+ max-width: 350px;
+ min-width: 350px;
+ opacity: 0;
+ transition: opacity .5s; /* adjust duration as needed */
+ overflow: hidden;
}
-.dark-mode .tab button.active {
- background-color: #212121;
+#saved-custom-prompts-header {
+ cursor: pointer;
}
-.switch label input[type=checkbox]:checked+.lever {
- background-color: #6d6d6d !important;
+.saved-prompts-list {
+ padding: 10px 8px;
+ text-align: center;
+ font-size: 1.15em;
+ box-shadow: inset 0px -2px 0px #000;
}
-.switch label input[type=checkbox]:checked+.lever:after {
- background-color: #d9d9d9;
+
+.saved-prompts-list a {
+ color: #fff;
+}
+
+#custom-context-menu ul {
+ margin: 0;
+ padding: 5px;
+ list-style: none;
+ overflow: auto;
+ max-height: 298px;
+ transition: .9s;
+}
+
+#custom-context-menu li {
+ padding: 6px 12px;
+ cursor: pointer;
+ font-size: 1.1em;
+ transition: .2s;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.tabcontent h3 {
+ font-size: 1.7em;
+ margin: 0 0 10px 0;
+ color: #444;
+ font-weight: bold;
+}
+
+.tabcontent p {
+ font-size: 1.09em;
+ margin: 0 0 25px 0;
+}
+
+#save-custom-context-menu-settings {
+ margin: -10px 0 20px 0;
+}
+
+.prompt-wrap {
+ padding: 15px;
+ margin-bottom: 15px;
+}
+
+.hamburger-menu {
+ cursor: pointer;
+ position: absolute;
+ top: 17px;
+ right: 17px;
+ width: 28px;
+ height: 20px;
+ margin-top: 3px;
+}
+
+.hamburger-menu span {
+ position: absolute;
+ width: 100%;
+ height: 2px;
+ border-radius: 50px;
+ background-color: #fffefe;
+ transition: all 0.3s;
+}
+
+.hamburger-menu span:first-child {
+ top: 0;
+}
+
+.hamburger-menu span:nth-child(2) {
+ top: 50%;
+ transform: translateY(-50%);
+}
+
+.hamburger-menu span:last-child {
+ bottom: 0;
+}
+
+.settings-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgb(0 0 0 / 83%);
+ z-index: 11;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity 0.3s ease;
+}
+
+.settings-overlay.visible {
+ opacity: 1;
+ pointer-events: auto;
+}
+
+.settings-content {
+ padding: 20px;
+ border-radius: 4px;
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
+}
+
+.settings-menu {
+ position: absolute;
+ top: 63px;
+ list-style-type: none;
+ padding: 0;
+ margin: 4px 0 0;
+ text-align: center; /* Add this line to center the menu items horizontally */
+ width: 100%;
+}
+
+.settings-menu li {
+ padding: 15px 16px;
+ cursor: pointer;
+ transition: background-color 0.3s;
+ text-align: center;
+ margin-bottom: 2px;
+ color: #fff;
+}
+
+.settings-menu li.active svg, .settings-menu li:hover svg {
+ /* fill:#0b0b0b; */
+}
+
+.settings-menu svg {
+ max-width: 25px;
+ display: inline-block;
+ fill: #fff;
+ margin: 5px 0 2px 0;
+}
+
+.settings-menu div {
+ position: relative;
+ margin: 0px 0 5px;
+}
+
+.settings-menu button {
+ width: 200px;
+ text-align: left
+}
+
+.container input {
+ font-size: 1.2em !important;
+}
+
+.github-option {
+ margin-top: 30px;
+ margin-left: 1px
+}
+
+.github-option p {
+ padding-left: 8px;
+ margin-bottom: 15px;
+}
+
+.suggestion.user-input {
+ background: #f1f1f100;
+}
+
+.user-input .share-container {
+ display: none
+}
+
+script {
+ display: block
+}
+
+#customPromptsLink, .suggestionsLink {
+ cursor: pointer;
+}
+
+#token-count {
+ color: #c2bebe;
+ position:relative;
+ top:22px;
+ font-size:1.1em;
+ float:right;
+ cursor: pointer;
+}
+
+.theme-option {
+ width: 72px;
+ height: 72px;
+ border-radius: 50%;
+ margin-right: 11px;
+ cursor: pointer;
+ transition:.2s;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 10px;
+}
+
+.button-color-option {
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ margin-right: 5px;
+ cursor: pointer;
+ transition:.2s;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 10px;
+}
+@media(max-width: 400px){
+ .button-color-option {
+ width: 37px;
+ height: 37px;
+ margin-right: 2px;
+ }
+ .theme-option {
+ margin-right: 5px;
+ width:67px;
+ height:67px;
+ }
+}
+.theme-option{
+ border: 1px solid #ffffff3d;
+ outline: 1px solid #aeaeae8a;
+ outline-offset: -8px;
+}
+.button-color-option{
+ border: 1px solid #ffffff00;
+ outline: 1px solid #fffdfd36;
+ outline-offset: -3px;
+}
+
+
+.theme-option .material-icons{
+ opacity:0;
+ color: #cdcccc;
+}
+
+.button-color-option .material-icons {
+ opacity:0;
+ color: #ffffff;
+}
+
+.theme-option.active .material-icons,
+.button-color-option.active .material-icons {
+ opacity:1;
+}
+.button-color-option .material-icons {
+ font-size:20px;
+}
+
+.settings-size-slider {
+ position: relative;
+ height: 25px;
+ margin-right: 9px;
+}
+
+#font-size, #popup-size {
+ width: 100%;
+ height: 10px;
+ background: transparent;
+ -webkit-appearance: none;
+ margin: 5px 0;
+ border-radius: 10px !important;
+}
+
+#font-size::-webkit-slider-runnable-track, #popup-size::-webkit-slider-runnable-track {
+ width: 100%;
+ height: 10px;
+ cursor: pointer;
+ border-radius: 10px;
+}
+
+#font-size::-webkit-slider-thumb, #popup-size::-webkit-slider-thumb {
+ height: 20px;
+ width: 20px;
+ background: #f3f3f3;
+ box-shadow:0 0 4px #868484;
+ cursor: pointer;
+ -webkit-appearance: none;
+ border-radius: 50%;
+ transition: background .15s ease-in-out;
+}
+
+#font-size:active::-webkit-slider-thumb {
+ background: #ffffff;
+}
+
+input[type=range]+.thumb {
+ position: absolute;
+ top:-32px !important;
+ display: none;
+ opacity: 0;
+}
+#changelog-container {
+ white-space: pre-wrap;
+ font-family: arial, 'sans-serif';
+ font-size:1.1em;
+ padding:25px 15px;
+}
+#updateNotification {
+ color:#ffffff;
+ opacity: 0;
+ display:block;
+ position:absolute;
+ top:19px;
+ right:60px;
+ cursor:pointer;
+ transition: 1s;
+}
+.changelog-fade-in {
+ opacity: 0;
+ transition: opacity 0.5s;
+ opacity: 1;
+}
+.container {
+ width: 90% !important;
+}
+body.settings-options,
+body.settings-options .header,
+body.settings-options #font-size::-webkit-slider-runnable-track,
+body.settings-options #popup-size::-webkit-slider-runnable-track,
+body.settings-options .switch label input[type=checkbox]:checked + .lever,
+body.settings-options .material-icons{
+ transition:.4s;
+}
+
+body.settings-options .switch-wrap {
+ transition: background-color 0.4s linear;
+}
+
+body.settings-options .switch-wrap .switch-description,
+body.settings-options .switch-wrap label {
+ transition:.03s !important;
}
\ No newline at end of file
diff --git a/layout/css/prism.min.css b/layout/css/prism.min.css
new file mode 100644
index 0000000..a4f6722
--- /dev/null
+++ b/layout/css/prism.min.css
@@ -0,0 +1,122 @@
+/**
+ * prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML
+ * Based on https://github.com/chriskempson/tomorrow-theme
+ * @author Rose Pritchard
+ */
+
+code[class*="language-"],
+pre[class*="language-"] {
+ color: #ccc;
+ background: none;
+ font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
+ font-size: 1em;
+ text-align: left;
+ white-space: pre;
+ word-spacing: normal;
+ word-break: normal;
+ word-wrap: normal;
+ line-height: 1.5;
+
+ -moz-tab-size: 4;
+ -o-tab-size: 4;
+ tab-size: 4;
+
+ -webkit-hyphens: none;
+ -moz-hyphens: none;
+ -ms-hyphens: none;
+ hyphens: none;
+
+}
+
+/* Code blocks */
+pre[class*="language-"] {
+ padding: 1em;
+ margin: .5em 0;
+ overflow: auto;
+}
+
+:not(pre) > code[class*="language-"],
+pre[class*="language-"] {
+ background: #2d2d2d;
+}
+
+/* Inline code */
+:not(pre) > code[class*="language-"] {
+ padding: .1em;
+ border-radius: .3em;
+ white-space: normal;
+}
+
+.token.comment,
+.token.block-comment,
+.token.prolog,
+.token.doctype,
+.token.cdata {
+ color: #999;
+}
+
+.token.punctuation {
+ color: #ccc;
+}
+
+.token.tag,
+.token.attr-name,
+.token.namespace,
+.token.deleted {
+ color: #e2777a;
+}
+
+.token.function-name {
+ color: #6196cc;
+}
+
+.token.boolean,
+.token.number,
+.token.function {
+ color: #f08d49;
+}
+
+.token.property,
+.token.class-name,
+.token.constant,
+.token.symbol {
+ color: #f8c555;
+}
+
+.token.selector,
+.token.important,
+.token.atrule,
+.token.keyword,
+.token.builtin {
+ color: #cc99cd;
+}
+
+.token.string,
+.token.char,
+.token.attr-value,
+.token.regex,
+.token.variable {
+ color: #7ec699;
+}
+
+.token.operator,
+.token.entity,
+.token.url {
+ color: #67cdcc;
+}
+
+.token.important,
+.token.bold {
+ font-weight: bold;
+}
+.token.italic {
+ font-style: italic;
+}
+
+.token.entity {
+ cursor: help;
+}
+
+.token.inserted {
+ color: green;
+}
\ No newline at end of file
diff --git a/layout/js/materialize.min.js b/layout/js/code-view/materialize.min.js
similarity index 100%
rename from layout/js/materialize.min.js
rename to layout/js/code-view/materialize.min.js
diff --git a/layout/js/code-view/prism.min.js b/layout/js/code-view/prism.min.js
new file mode 100644
index 0000000..412adab
--- /dev/null
+++ b/layout/js/code-view/prism.min.js
@@ -0,0 +1 @@
+var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(o){var n=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,e={},j={manual:o.Prism&&o.Prism.manual,disableWorkerMessageHandler:o.Prism&&o.Prism.disableWorkerMessageHandler,util:{encode:function e(t){return t instanceof C?new C(t.type,e(t.content),t.alias):Array.isArray(t)?t.map(e):t.replace(/&/g,"&").replace(/=i.reach);y+=b.value.length,b=b.next){var v=b.value;if(n.length>t.length)return;if(!(v instanceof C)){var F,x=1;if(m){if(!(F=L(f,y,t,p))||F.index>=t.length)break;var k=F.index,w=F.index+F[0].length,A=y;for(A+=b.value.length;A<=k;)b=b.next,A+=b.value.length;if(A-=b.value.length,y=A,b.value instanceof C)continue;for(var P=b;P!==n.tail&&(A
(GFM Style)",type:"boolean"},requireSpaceBeforeHeadingText:{defaultValue:!1,describe:"Makes adding a space between `#` and the header text mandatory (GFM Style)",type:"boolean"},ghMentions:{defaultValue:!1,describe:"Enables github @mentions",type:"boolean"},ghMentionsLink:{defaultValue:"https://github.com/{u}",describe:"Changes the link generated by @mentions. Only applies if ghMentions option is enabled.",type:"string"},encodeEmails:{defaultValue:!0,describe:"Encode e-mail addresses through the use of Character Entities, transforming ASCII e-mail addresses into its equivalent decimal entities",type:"boolean"},openLinksInNewWindow:{defaultValue:!1,describe:"Open all links in new windows",type:"boolean"},backslashEscapesHTMLTags:{defaultValue:!1,describe:"Support for HTML Tag escaping. ex:
',showdown:"S"},x.Converter=function(e){"use strict";var r,t,n={},i=[],l=[],o={},a=h,s={parsed:{},raw:"",format:""};for(r in e=e||{},p)p.hasOwnProperty(r)&&(n[r]=p[r]);if("object"!=typeof e)throw Error("Converter expects the passed parameter to be an object, but "+typeof e+" was passed instead.");for(t in e)e.hasOwnProperty(t)&&(n[t]=e[t]);function c(e,r){if(r=r||null,x.helper.isString(e)){if(r=e=x.helper.stdExtName(e),x.extensions[e]){console.warn("DEPRECATION WARNING: "+e+" is an old extension that uses a deprecated loading method.Please inform the developer that the extension should be updated!");var t=x.extensions[e],a=e;if("function"==typeof t&&(t=t(new x.Converter)),x.helper.isArray(t)||(t=[t]),!(a=g(t,a)).valid)throw Error(a.error);for(var n=0;n[^\r]+?<\/pre>)/gm,function(e,r){return r.replace(/^ /gm,"¨0").replace(/¨0/g,"")}),x.subParser("hashBlock")("","gim"),e=s.converter._dispatch("hashPreCodeTags.after",e,n,s)}),x.subParser("headers",function(e,n,s){"use strict";e=s.converter._dispatch("headers.before",e,n,s);var o=isNaN(parseInt(n.headerLevelStart))?1:parseInt(n.headerLevelStart),r=n.smoothLivePreview?/^(.+)[ \t]*\n={2,}[ \t]*\n+/gm:/^(.+)[ \t]*\n=+[ \t]*\n+/gm,t=n.smoothLivePreview?/^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm:/^(.+)[ \t]*\n-+[ \t]*\n+/gm,r=(e=(e=e.replace(r,function(e,r){var t=x.subParser("spanGamut")(r,n,s),r=n.noHeaderId?"":' id="'+i(r)+'"',r="\n"+e+"\n
",r,t)}),e=t.converter._dispatch("blockQuotes.after",e,r,t)}),x.subParser("codeBlocks",function(e,n,s){"use strict";e=s.converter._dispatch("codeBlocks.before",e,n,s);return e=(e=(e+="¨0").replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=¨0))/g,function(e,r,t){var a="\n",r=x.subParser("outdent")(r,n,s);return r=x.subParser("encodeCode")(r,n,s),r="
",x.subParser("hashBlock")(r,n,s)+t})).replace(/¨0/,""),e=s.converter._dispatch("codeBlocks.after",e,n,s)}),x.subParser("codeSpans",function(e,n,s){"use strict";return e=(e=void 0===(e=s.converter._dispatch("codeSpans.before",e,n,s))?"":e).replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,function(e,r,t,a){return a=(a=a.replace(/^([ \t]*)/g,"")).replace(/[ \t]*$/g,""),a=r+""+(r=(r=(r=x.subParser("detab")(r,n,s)).replace(/^\n+/g,"")).replace(/\n+$/g,""))+(a=n.omitExtraWLInCodeBlocks?"":a)+""+(a=x.subParser("encodeCode")(a,n,s))+"",a=x.subParser("hashHTMLSpans")(a,n,s)}),e=s.converter._dispatch("codeSpans.after",e,n,s)}),x.subParser("completeHTMLDocument",function(e,r,t){"use strict";if(!r.completeHTMLDocument)return e;e=t.converter._dispatch("completeHTMLDocument.before",e,r,t);var a,n="html",s="\n",o="",i='\n',l="",c="";for(a in void 0!==t.metadata.parsed.doctype&&(s="\n","html"!==(n=t.metadata.parsed.doctype.toString().toLowerCase())&&"html5"!==n||(i='')),t.metadata.parsed)if(t.metadata.parsed.hasOwnProperty(a))switch(a.toLowerCase()){case"doctype":break;case"title":o="
",a=x.subParser("hashBlock")(a,s,o),"\n\n¨G"+(o.ghCodeBlocks.push({text:e,codeblock:a})-1)+"G\n\n"})).replace(/¨0/,""),o.converter._dispatch("githubCodeBlocks.after",e,s,o)):e}),x.subParser("hashBlock",function(e,r,t){"use strict";return e=(e=t.converter._dispatch("hashBlock.before",e,r,t)).replace(/(^\n+|\n+$)/g,""),e="\n\n¨K"+(t.gHtmlBlocks.push(e)-1)+"K\n\n",e=t.converter._dispatch("hashBlock.after",e,r,t)}),x.subParser("hashCodeTags",function(e,n,s){"use strict";e=s.converter._dispatch("hashCodeTags.before",e,n,s);return e=x.helper.replaceRecursiveRegExp(e,function(e,r,t,a){t=t+x.subParser("encodeCode")(r,n,s)+a;return"¨C"+(s.gHtmlSpans.push(t)-1)+"C"},""+(a=(a=(a=x.subParser("detab")(a,s,o)).replace(/^\n+/g,"")).replace(/\n+$/g,""))+n+"]*>","","gim"),e=s.converter._dispatch("hashCodeTags.after",e,n,s)}),x.subParser("hashElement",function(e,r,t){"use strict";return function(e,r){return r=(r=(r=r.replace(/\n\n/g,"\n")).replace(/^\n/,"")).replace(/\n+$/g,""),r="\n\n¨K"+(t.gHtmlBlocks.push(r)-1)+"K\n\n"}}),x.subParser("hashHTMLBlocks",function(e,r,n){"use strict";e=n.converter._dispatch("hashHTMLBlocks.before",e,r,n);function t(e,r,t,a){return-1!==t.search(/\bmarkdown\b/)&&(e=t+n.converter.makeHtml(r)+a),"\n\n¨K"+(n.gHtmlBlocks.push(e)-1)+"K\n\n"}var a=["pre","div","h1","h2","h3","h4","h5","h6","blockquote","table","dl","ol","ul","script","noscript","form","fieldset","iframe","math","style","section","header","footer","nav","article","aside","address","audio","canvas","figure","hgroup","output","video","p"];r.backslashEscapesHTMLTags&&(e=e.replace(/\\<(\/?[^>]+?)>/g,function(e,r){return"<"+r+">"}));for(var s=0;s]*>","^ {0,3}\\s*
",r,t);return e=(e=(e=e.replace(/^ {0,2}( ?-){3,}[ \t]*$/gm,a)).replace(/^ {0,2}( ?\*){3,}[ \t]*$/gm,a)).replace(/^ {0,2}( ?_){3,}[ \t]*$/gm,a),e=t.converter._dispatch("horizontalRule.after",e,r,t)}),x.subParser("images",function(e,r,d){"use strict";function l(e,r,t,a,n,s,o,i){var l=d.gUrls,c=d.gTitles,u=d.gDimensions;if(t=t.toLowerCase(),i=i||"",-1"}return e=(e=(e=(e=(e=(e=d.converter._dispatch("images.before",e,r,d)).replace(/!\[([^\]]*?)] ?(?:\n *)?\[([\s\S]*?)]()()()()()/g,l)).replace(/!\[([^\]]*?)][ \t]*()\([ \t]?(data:.+?\/.+?;base64,[A-Za-z0-9+/=\n]+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,function(e,r,t,a,n,s,o,i){return l(e,r,t,a=a.replace(/\s/g,""),n,s,0,i)})).replace(/!\[([^\]]*?)][ \t]*()\([ \t]?<([^>]*)>(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(?:(["'])([^"]*?)\6))?[ \t]?\)/g,l)).replace(/!\[([^\]]*?)][ \t]*()\([ \t]?([\S]+?(?:\([\S]*?\)[\S]*?)?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,l)).replace(/!\[([^\[\]]+)]()()()()()/g,l),e=d.converter._dispatch("images.after",e,r,d)}),x.subParser("italicsAndBold",function(e,r,t){"use strict";return e=t.converter._dispatch("italicsAndBold.before",e,r,t),e=r.literalMidWordUnderscores?(e=(e=e.replace(/\b___(\S[\s\S]*?)___\b/g,function(e,r){return""+r+""})).replace(/\b__(\S[\s\S]*?)__\b/g,function(e,r){return""+r+""})).replace(/\b_(\S[\s\S]*?)_\b/g,function(e,r){return""+r+""}):(e=(e=e.replace(/___(\S[\s\S]*?)___/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/__(\S[\s\S]*?)__/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/_([^\s_][\s\S]*?)_/g,function(e,r){return/\S$/.test(r)?""+r+"":e}),e=r.literalMidWordAsterisks?(e=(e=e.replace(/([^*]|^)\B\*\*\*(\S[\s\S]*?)\*\*\*\B(?!\*)/g,function(e,r,t){return r+""+t+""})).replace(/([^*]|^)\B\*\*(\S[\s\S]*?)\*\*\B(?!\*)/g,function(e,r,t){return r+""+t+""})).replace(/([^*]|^)\B\*(\S[\s\S]*?)\*\B(?!\*)/g,function(e,r,t){return r+""+t+""}):(e=(e=e.replace(/\*\*\*(\S[\s\S]*?)\*\*\*/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/\*\*(\S[\s\S]*?)\*\*/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/\*([^\s*][\s\S]*?)\*/g,function(e,r){return/\S$/.test(r)?""+r+"":e}),e=t.converter._dispatch("italicsAndBold.after",e,r,t)}),x.subParser("lists",function(e,d,c){"use strict";function p(e,r){c.gListLevel++,e=e.replace(/\n{2,}$/,"\n");var t=/(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0| {0,3}([*+-]|\d+[.])[ \t]+))/gm,l=/\n[ \t]*\n(?!¨0)/.test(e+="¨0");return d.disableForced4SpacesIndentedSublists&&(t=/(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0|\2([*+-]|\d+[.])[ \t]+))/gm),e=(e=e.replace(t,function(e,r,t,a,n,s,o){o=o&&""!==o.trim();var n=x.subParser("outdent")(n,d,c),i="";return s&&d.tasklists&&(i=' class="task-list-item" style="list-style-type: none;"',n=n.replace(/^[ \t]*\[(x|X| )?]/m,function(){var e='"})),n=n.replace(/^([-*+]|\d\.)[ \t]+[\S\n ]*/g,function(e){return"¨A"+e}),n="
]*>/.test(c)&&(u=!0)}n[o]=c}return e=(e=(e=n.join("\n")).replace(/^\n+/g,"")).replace(/\n+$/g,""),t.converter._dispatch("paragraphs.after",e,r,t)}),x.subParser("runExtension",function(e,r,t,a){"use strict";return e.filter?r=e.filter(r,a.converter,t):e.regex&&((a=e.regex)instanceof RegExp||(a=new RegExp(a,"g")),r=r.replace(a,e.replace)),r}),x.subParser("spanGamut",function(e,r,t){"use strict";return e=t.converter._dispatch("spanGamut.before",e,r,t),e=x.subParser("codeSpans")(e,r,t),e=x.subParser("escapeSpecialCharsWithinTagAttributes")(e,r,t),e=x.subParser("encodeBackslashEscapes")(e,r,t),e=x.subParser("images")(e,r,t),e=x.subParser("anchors")(e,r,t),e=x.subParser("autoLinks")(e,r,t),e=x.subParser("simplifiedAutoLinks")(e,r,t),e=x.subParser("emoji")(e,r,t),e=x.subParser("underline")(e,r,t),e=x.subParser("italicsAndBold")(e,r,t),e=x.subParser("strikethrough")(e,r,t),e=x.subParser("ellipsis")(e,r,t),e=x.subParser("hashHTMLSpans")(e,r,t),e=x.subParser("encodeAmpsAndAngles")(e,r,t),r.simpleLineBreaks?/\n\n¨K/.test(e)||(e=e.replace(/\n+/g,"
\n")):e=e.replace(/ +\n/g,"
\n"),e=t.converter._dispatch("spanGamut.after",e,r,t)}),x.subParser("strikethrough",function(e,t,a){"use strict";return t.strikethrough&&(e=(e=a.converter._dispatch("strikethrough.before",e,t,a)).replace(/(?:~){2}([\s\S]+?)(?:~){2}/g,function(e,r){return r=r,""+(r=t.simplifiedAutoLink?x.subParser("simplifiedAutoLinks")(r,t,a):r)+""}),e=a.converter._dispatch("strikethrough.after",e,t,a)),e}),x.subParser("stripLinkDefinitions",function(i,l,c){"use strict";function e(e,r,t,a,n,s,o){return r=r.toLowerCase(),i.toLowerCase().split(r).length-1<2?e:(t.match(/^data:.+?\/.+?;base64,/)?c.gUrls[r]=t.replace(/\s/g,""):c.gUrls[r]=x.subParser("encodeAmpsAndAngles")(t,l,c),s?s+o:(o&&(c.gTitles[r]=o.replace(/"|'/g,""")),l.parseImgDimensions&&a&&n&&(c.gDimensions[r]={width:a,height:n}),""))}return i=(i=(i=(i+="¨0").replace(/^ {0,3}\[([^\]]+)]:[ \t]*\n?[ \t]*(data:.+?\/.+?;base64,[A-Za-z0-9+/=\n]+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n\n|(?=¨0)|(?=\n\[))/gm,e)).replace(/^ {0,3}\[([^\]]+)]:[ \t]*\n?[ \t]*([^>\s]+)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=¨0))/gm,e)).replace(/¨0/,"")}),x.subParser("tables",function(e,y,P){"use strict";if(!y.tables)return e;function r(e){for(var r=e.split("\n"),t=0;t"+(n=x.subParser("spanGamut")(n,y,P))+"\n"));for(t=0;t"+x.subParser("spanGamut")(i,y,P)+"\n"));h.push(_)}for(var m=d,f=h,b="\n\n\n",w=m.length,k=0;k\n \n\n",k=0;k\n";for(var v=0;v\n"}return b+=" \n
\n"}return e=(e=(e=(e=P.converter._dispatch("tables.before",e,y,P)).replace(/\\(\|)/g,x.helper.escapeCharactersCallback)).replace(/^ {0,3}\|?.+\|.+\n {0,3}\|?[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:[-=]){2,}[\s\S]+?(?:\n\n|¨0)/gm,r)).replace(/^ {0,3}\|.+\|[ \t]*\n {0,3}\|[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*\n( {0,3}\|.+\|[ \t]*\n)*(?:\n|¨0)/gm,r),e=P.converter._dispatch("tables.after",e,y,P)}),x.subParser("underline",function(e,r,t){"use strict";return r.underline?(e=t.converter._dispatch("underline.before",e,r,t),e=(e=r.literalMidWordUnderscores?(e=e.replace(/\b___(\S[\s\S]*?)___\b/g,function(e,r){return""+r+""})).replace(/\b__(\S[\s\S]*?)__\b/g,function(e,r){return""+r+""}):(e=e.replace(/___(\S[\s\S]*?)___/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/__(\S[\s\S]*?)__/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/(_)/g,x.helper.escapeCharactersCallback),t.converter._dispatch("underline.after",e,r,t)):e}),x.subParser("unescapeSpecialChars",function(e,r,t){"use strict";return e=(e=t.converter._dispatch("unescapeSpecialChars.before",e,r,t)).replace(/¨E(\d+)E/g,function(e,r){r=parseInt(r);return String.fromCharCode(r)}),e=t.converter._dispatch("unescapeSpecialChars.after",e,r,t)}),x.subParser("makeMarkdown.blockquote",function(e,r){"use strict";var t="";if(e.hasChildNodes())for(var a=e.childNodes,n=a.length,s=0;s ")}),x.subParser("makeMarkdown.codeBlock",function(e,r){"use strict";var t=e.getAttribute("language"),e=e.getAttribute("precodenum");return"```"+t+"\n"+r.preList[e]+"\n```"}),x.subParser("makeMarkdown.codeSpan",function(e){"use strict";return"`"+e.innerHTML+"`"}),x.subParser("makeMarkdown.emphasis",function(e,r){"use strict";var t="";if(e.hasChildNodes()){t+="*";for(var a=e.childNodes,n=a.length,s=0;s",e.hasAttribute("width")&&e.hasAttribute("height")&&(r+=" ="+e.getAttribute("width")+"x"+e.getAttribute("height")),e.hasAttribute("title")&&(r+=' "'+e.getAttribute("title")+'"'),r+=")"),r}),x.subParser("makeMarkdown.links",function(e,r){"use strict";var t="";if(e.hasChildNodes()&&e.hasAttribute("href")){for(var a=e.childNodes,n=a.length,t="[",s=0;s"),e.hasAttribute("title")&&(t+=' "'+e.getAttribute("title")+'"'),t+=")"}return t}),x.subParser("makeMarkdown.list",function(e,r,t){"use strict";var a="";if(!e.hasChildNodes())return"";for(var n=e.childNodes,s=n.length,o=e.getAttribute("start")||1,i=0;i"+r.preList[e]+""}),x.subParser("makeMarkdown.strikethrough",function(e,r){"use strict";var t="";if(e.hasChildNodes()){t+="~~";for(var a=e.childNodes,n=a.length,s=0;str>th"),s=e.querySelectorAll("tbody>tr"),o=0;o/g,"\\$1>")).replace(/^#/gm,"\\#")).replace(/^(\s*)([-=]{3,})(\s*)$/,"$1\\$2$3")).replace(/^( {0,3}\d+)\./gm,"$1\\.")).replace(/^( {0,3})([+-])/gm,"$1\\$2")).replace(/]([\s]*)\(/g,"\\]$1\\(")).replace(/^ {0,3}\[([\S \t]*?)]:/gm,"\\[$1]:")});"function"==typeof define&&define.amd?define(function(){"use strict";return x}):"undefined"!=typeof module&&module.exports?module.exports=x:this.showdown=x}.call(this);
+//# sourceMappingURL=showdown.min.js.map
\ No newline at end of file
diff --git a/layout/js/custom-colors/custom-colors.js b/layout/js/custom-colors/custom-colors.js
new file mode 100644
index 0000000..9eb21e2
--- /dev/null
+++ b/layout/js/custom-colors/custom-colors.js
@@ -0,0 +1,214 @@
+class CustomColors {
+ constructor() {
+ // Initialize the darker(black) theme and button color
+ this.currentTheme = "darker";
+ this.currentButtonColor = "blue";
+ this.addBodyClass();
+ }
+ settingsCustomColors() {
+ var themeOptions = document.getElementsByClassName('theme-option');
+
+ for (var i = 0; i < themeOptions.length; i++) {
+ themeOptions[i].addEventListener('click', function() {
+ // Remove active class from all theme options
+ for (var j = 0; j < themeOptions.length; j++) {
+ themeOptions[j].classList.remove('active');
+ }
+
+ // Add active class to selected theme option
+ this.classList.add('active');
+
+ var theme = this.id;
+ chrome.storage.sync.set({theme: theme}, function() {
+ console.log('Theme is ' + theme);
+ window.MyAssistant.customColors.applyTheme(theme);
+ });
+ });
+ }
+
+ var self = this;
+ chrome.storage.sync.get('theme', function(data) {
+ var themeOption;
+ if (data.theme) {
+ self.applyTheme(data.theme);
+ themeOption = document.getElementById(data.theme);
+ } else {
+ self.applyTheme(self.currentTheme);
+ themeOption = document.getElementById(self.currentTheme);
+ }
+
+ // Add active class to the selected theme option
+ if (themeOption) {
+ themeOption.classList.add('active');
+ }
+ });
+ }
+
+ applyTheme(theme) {
+ var oldLink = document.getElementById('theme-style');
+ var link = document.createElement('link');
+ link.id = 'theme-style';
+ link.rel = 'stylesheet';
+ link.href = 'layout/css/custom/background/' + theme + '.css';
+ if (oldLink) {
+ document.head.removeChild(oldLink);
+ }
+ document.head.appendChild(link);
+
+ // Remove previous theme class if exists
+ let previousTheme = this.currentTheme;
+ if (previousTheme) {
+ document.body.classList.remove(previousTheme + "-mode");
+ }
+
+ // Apply new theme class
+ document.body.classList.add(theme + "-mode");
+ this.currentTheme = theme;
+ }
+
+
+ settingsButtonColors() {
+ var buttonColorOptions = document.getElementsByClassName('button-color-option');
+
+ for (var i = 0; i < buttonColorOptions.length; i++) {
+ buttonColorOptions[i].addEventListener('click', function() {
+ // Remove active class from all button color options
+ for (var j = 0; j < buttonColorOptions.length; j++) {
+ buttonColorOptions[j].classList.remove('active');
+ }
+
+ // Add active class to selected button color option
+ this.classList.add('active');
+
+ var buttonColor = this.id;
+ chrome.storage.sync.set({buttonColor: buttonColor}, function() {
+ console.log('Button color is ' + buttonColor);
+ window.MyAssistant.customColors.applyButtonTheme(buttonColor);
+ });
+ });
+ }
+
+ var self = this;
+ chrome.storage.sync.get('buttonColor', function(data) {
+ var buttonColorOption;
+ if (data.buttonColor) {
+ self.applyButtonTheme(data.buttonColor);
+ buttonColorOption = document.getElementById(data.buttonColor);
+ } else {
+ self.applyButtonTheme(self.currentButtonColor);
+ buttonColorOption = document.getElementById(self.currentButtonColor);
+ }
+
+ // Add active class to the selected button color option
+ if (buttonColorOption) {
+ buttonColorOption.classList.add('active');
+ }
+ });
+ }
+
+ applyButtonTheme(buttonColor) {
+ var oldLink = document.getElementById('button-color-style');
+ var link = document.createElement('link');
+ link.id = 'button-color-style';
+ link.rel = 'stylesheet';
+ link.href = 'layout/css/custom/color/' + buttonColor + '.css';
+ if (oldLink) {
+ document.head.removeChild(oldLink);
+ }
+ document.head.appendChild(link);
+
+ // Remove previous button color class if exists
+ let previousButtonColor = this.currentButtonColor;
+ if (previousButtonColor) {
+ document.body.classList.remove(previousButtonColor + "-color");
+ }
+
+ // Apply new button color class
+ document.body.classList.add(buttonColor + "-color");
+ this.currentButtonColor = buttonColor;
+ }
+
+
+ settingsFontSize() {
+ var fontSizeSlider = document.getElementById('font-size');
+ var fontSizeSpan = document.getElementById('font-size-span');
+
+ // Event listener for when the slider is being moved
+ fontSizeSlider.addEventListener('input', function() {
+ var fontSize = this.value;
+ fontSizeSpan.textContent = fontSize; // Update display as slider is moved
+ });
+
+ // Event listener for when the slider is released
+ fontSizeSlider.addEventListener('mouseup', function() {
+ var fontSize = this.value;
+ chrome.storage.sync.set({fontSize: fontSize}, function() {
+ console.log('Font size is ' + fontSize);
+ window.MyAssistant.customColors.applyFontSize(fontSize);
+ });
+ });
+
+ chrome.storage.sync.get('fontSize', function(data) {
+ if (data.fontSize) {
+ window.MyAssistant.customColors.applyFontSize(data.fontSize);
+ fontSizeSlider.value = data.fontSize;
+ fontSizeSpan.textContent = data.fontSize; // Add size to font-size-span div when loading stored value
+ }
+ });
+ }
+
+ applyFontSize(fontSize) {
+ document.documentElement.style.setProperty('--font-size', fontSize + "%");
+ }
+
+
+
+ settingsPopupSize() {
+ var popupSizeSlider = document.getElementById('popup-size');
+ var popupWidthSpan = document.getElementById('popup-width-span');
+
+ // Event listener for when the slider is being moved
+ popupSizeSlider.addEventListener('input', function() {
+ var popupSize = this.value;
+ popupWidthSpan.textContent = popupSize; // Update the display as the slider is being moved
+ });
+
+ // Event listener for when the slider is released
+ popupSizeSlider.addEventListener('mouseup', function() {
+ var popupSize = this.value;
+ chrome.storage.sync.set({popupSize: popupSize}, function() {
+ console.log('Popup size is ' + popupSize);
+ window.MyAssistant.customColors.applyPopupSize(popupSize);
+ });
+ });
+
+ chrome.storage.sync.get('popupSize', function(data) {
+ if (data.popupSize) {
+ window.MyAssistant.customColors.applyPopupSize(data.popupSize);
+ popupSizeSlider.value = data.popupSize;
+ popupWidthSpan.textContent = data.popupSize;
+ }
+ });
+ }
+
+ applyPopupSize(popupSize) {
+ document.body.style.setProperty('width', popupSize + "px");
+ }
+
+ addBodyClass(apiKeyException){
+ const activeTabLink = document.querySelector('.settings-menu-option.tablinks.active');
+
+ if (activeTabLink || apiKeyException === "active-missing-api-key-exception") {
+ let apiKeyExceptionCheck = apiKeyException === "active-missing-api-key-exception" ? 1000 : 0;
+ setTimeout(function() {
+ document.body.classList.add('settings-options');
+ }, apiKeyExceptionCheck); // Time is in milliseconds, so 5000 ms equals 5 seconds
+
+
+ } else {
+ document.body.classList.remove('settings-options');
+ }
+ }
+
+}
+window.MyAssistant.customColors = new CustomColors();
diff --git a/layout/js/popup.js b/layout/js/popup.js
deleted file mode 100644
index 78cdfa9..0000000
--- a/layout/js/popup.js
+++ /dev/null
@@ -1,350 +0,0 @@
-document.addEventListener("DOMContentLoaded", function () {
- // Initialize Materialize tooltips
- const tooltips = document.querySelectorAll(".tooltipped");
- M.Tooltip.init(tooltips);
-
- // Add the event listener for the dark mode toggle switch
- document.getElementById("darkModeToggle").addEventListener("change", function() {
- document.body.classList.toggle("dark-mode", this.checked);
- });
-
-
- const inputText = document.getElementById("input-text");
-
- inputText.addEventListener("input", function () {
- chrome.storage.local.set({ savedText: this.value }, function () {
- console.log("Text saved.");
- });
- });
-
- document.getElementById("engineToggle").addEventListener("change", function() {
- chrome.storage.sync.set({ engine: this.checked ? 'gpt3' : 'gpt4' }, function() {
- console.log("Engine setting saved.");
- });
- });
-
- chrome.storage.local.get("savedText", function (data) {
- inputText.value = data.savedText || "";
- });
-
- // Add event listeners for the tablinks
- document.querySelectorAll(".tablinks").forEach((tab) => {
- tab.addEventListener("click", function (event) {
- openTab(event.target.getAttribute("data-tab"));
- // Remove active class from other tablinks
- document.querySelectorAll(".tablinks").forEach(t => t.classList.remove("active"));
- event.target.classList.add("active");
- });
- });
-
- // Open the Suggestions tab by default
- openTab("suggestions-tab");
- document.querySelector('[data-tab="suggestions-tab"]').classList.add("active");
-
- var inputs = document.querySelectorAll('input');
-
- inputs.forEach(function(input) {
- input.addEventListener('focus', function() {
- var label = document.querySelector('label[for="' + input.id + '"]');
- if (label) {
- label.style.color = '#1a73e8'; // Change this to the desired color
- }
- });
-
- input.addEventListener('blur', function() {
- var label = document.querySelector('label[for="' + input.id + '"]');
- if (label) {
- label.style.color = ''; // Reset the color to the default
- }
- });
- });
-
- // Add a cancel flag
- let cancelRequest = false;
-
- const submitButton = document.getElementById("submit-button");
- if (submitButton) {
- submitButton.addEventListener("click", async () => {
- if (submitButton.innerText === "Get Suggestions") {
-
- const inputText = document.getElementById("input-text").value;
- if (inputText.trim() === "") {
- displaySuggestions(["Add some text above to get a response."]);
- return;
- }
-
- try {
- const inputText = document.getElementById("input-text").value;
- showLoadingAnimation();
- submitButton.innerText = "Cancel";
- cancelRequest = false;
- const engine = document.getElementById("engineToggle").checked ? 'gpt3' : 'gpt4';
- const response = await sendMessageToBackground(inputText, { action: "getSuggestions", engine });
- if (!cancelRequest) {
- const suggestions = response.suggestions || [];
- displaySuggestions(suggestions);
- }
- } catch (error) {
- console.error("Error:", error.message);
- displaySuggestions([`Error: ${error.message}`]);
- } finally {
- hideLoadingAnimation();
- submitButton.innerText = "Get Suggestions";
- }
- } else {
- cancelRequest = true;
- submitButton.innerText = "Cancel";
- }
- });
- } else {
- console.error('submit-button not found');
- }
-
-
-
- const clearButton = document.getElementById("clear-button");
- if (clearButton) {
- clearButton.addEventListener("click", () => {
- // Clear the suggestions
- displaySuggestions([]);
-
- // Clear the input field
- const inputText = document.getElementById("input-text");
- inputText.value = "";
-
- // Clear the savedText in chrome.storage.local
- chrome.storage.local.set({ savedText: "" }, function () {
- console.log("Text cleared.");
- });
- });
- } else {
- console.error('clear-button not found');
- }
-});
-
-(async () => {
- // Wrap the event listener assignment in a DOMContentLoaded event listener
- document.addEventListener("DOMContentLoaded", () => {
- const settingsForm = document.getElementById("settings-form");
- if (settingsForm) {
- settingsForm.addEventListener("submit", (e) => {
- e.preventDefault();
-
- // Show the "Saving" overlay
- const savingOverlay = showSavingOverlay();
-
- // Save the settings here, for example, using the chrome.storage API
- const apiKey = document.getElementById("api-key").value;
- const maxTokens = document.getElementById("max-tokens").value;
- const n = document.getElementById("n").value;
- const stop = document.getElementById("stop").value;
- const temperature = document.getElementById("temperature").value;
- const darkMode = document.getElementById("darkModeToggle").checked;
-
- chrome.storage.sync.set({
- apiKey: apiKey,
- maxTokens: maxTokens,
- n: n,
- stop: stop,
- temperature: temperature,
- darkMode: darkMode
- }, () => {
- console.log('Settings saved');
-
- // Remove the "Saving" overlay after a short delay
- setTimeout(() => {
- savingOverlay.remove();
- }, 1000);
- });
- });
- } else {
- console.error('settings-form not found');
- }
- });
-
-
- // Load the settings when the popup is opened, for example, using the chrome.storage API
- chrome.storage.sync.get(['apiKey', 'maxTokens', 'n', 'stop', 'temperature', 'darkMode', 'engine'], (result) => {
- document.getElementById("api-key").value = result.apiKey || '';
- document.getElementById("max-tokens").value = result.maxTokens || '';
- document.getElementById("n").value = result.n || '';
- document.getElementById("stop").value = result.stop || '';
- document.getElementById("temperature").value = result.temperature || '';
- document.getElementById("darkModeToggle").checked = result.darkMode || false;
- document.body.classList.toggle("dark-mode", result.darkMode);
- document.getElementById("engineToggle").checked = result.engine === 'gpt3';
- });
-})();
-
-chrome.storage.local.get("savedSuggestions", function (data) {
- const suggestions = data.savedSuggestions || [];
- displaySuggestions(suggestions);
-});
-
-
-// Add this function to copy text to the clipboard
-function copyToClipboard(text) {
- const el = document.createElement('textarea');
- el.value = text;
- el.setAttribute('readonly', '');
- el.style.position = 'absolute';
- el.style.left = '-9999px';
- document.body.appendChild(el);
- el.select();
- document.execCommand('copy');
- document.body.removeChild(el);
-}
-
-function showCopiedOverlay(target) {
- const overlay = document.createElement('div');
- overlay.classList.add('copied-overlay');
- overlay.textContent = 'Copied';
-
- // Set position and size of the overlay
- const suggestionRect = target.getBoundingClientRect();
-
- const containerRect = target.parentElement.getBoundingClientRect();
-
- overlay.style.position = 'absolute';
- overlay.style.top = (suggestionRect.top - containerRect.top) + 'px';
- overlay.style.left = (suggestionRect.left - containerRect.left) + 'px';
- overlay.style.width = suggestionRect.width + 'px';
- overlay.style.height = suggestionRect.height + 'px';
- overlay.style.display = 'flex';
- overlay.style.justifyContent = 'center';
- overlay.style.alignItems = 'center';
-
- document.getElementById("suggestions-container").appendChild(overlay);
- setTimeout(() => {
- document.getElementById("suggestions-container").removeChild(overlay);
- }, 1500);
-}
-
-function openTab(tabName) {
- var i, tabcontent, tablinks;
- tabcontent = document.getElementsByClassName("tabcontent");
- for (i = 0; i < tabcontent.length; i++) {
- tabcontent[i].style.display = "none";
- }
- tablinks = document.getElementsByClassName("tablinks");
- for (i = 0; i < tablinks.length; i++) {
- tablinks[i].className = tablinks[i].className.replace(" active", "");
- }
- document.getElementById(tabName).style.display = "block";
-}
-
-document.querySelectorAll(".tablinks").forEach((tab) => {
- tab.addEventListener("click", function (event) {
- openTab(event.target.getAttribute("data-tab"));
- });
-});
-
-function sendMessageToBackground(text, message, cancelSignal) {
- return new Promise((resolve, reject) => {
- const listener = function (response) {
- if (chrome.runtime.lastError) {
- reject(chrome.runtime.lastError);
- } else if (response.error) {
- reject(new Error(response.error));
- } else {
- resolve(response);
- }
- };
-
- chrome.runtime.sendMessage({ ...message, text }, listener);
-
- if (cancelSignal) {
- cancelSignal.addEventListener('cancel', () => {
- chrome.runtime.onMessage.removeListener(listener);
- reject(new Error('Request canceled'));
- });
- }
- });
-}
-
-
-function createSuggestionElement(text) {
- const suggestion = document.createElement("div");
- suggestion.classList.add("suggestion");
- suggestion.textContent = text;
-
- // Add an event listener to the suggestion element
- suggestion.addEventListener('click', (e) => {
- copyToClipboard(text);
- showCopiedOverlay(e.target);
- });
-
- return suggestion;
-}
-
-function displaySuggestions(suggestions) {
- const suggestionsContainer = document.getElementById("suggestions-container");
- suggestionsContainer.innerHTML = "";
-
- for (const suggestion of suggestions) {
- const suggestionElement = createSuggestionElement(suggestion);
- suggestionsContainer.appendChild(suggestionElement);
- }
-
- // Save the suggestions to local storage
- chrome.storage.local.set({ savedSuggestions: suggestions }, function () {
- console.log("Suggestions saved.");
- });
-
- // Show or hide the "Clear" button
- const clearButton = document.getElementById("clear-button");
- if (clearButton) {
- if (suggestions.length > 0) {
- clearButton.style.display = "inline-block";
- } else {
- clearButton.style.display = "none";
- }
- }
-}
-
-
-function showLoadingAnimation() {
- const suggestionsContainer = document.getElementById("suggestions-container");
- suggestionsContainer.innerHTML = ""; // Clear the suggestions container
-
- const loadingAnimation = document.createElement("div");
- loadingAnimation.classList.add("loading-animation");
-
- for (let i = 0; i < 3; i++) {
- const dot = document.createElement("div");
- dot.classList.add("dot");
- loadingAnimation.appendChild(dot);
- }
-
- suggestionsContainer.appendChild(loadingAnimation);
-}
-
-function hideLoadingAnimation() {
- const loadingAnimation = document.querySelector(".loading-animation");
- if (loadingAnimation) {
- document.getElementById("suggestions-container").removeChild(loadingAnimation);
- }
-}
-
-function showSavingOverlay() {
- const settingsContent = document.getElementById("settings-tab");
- const savingOverlay = document.createElement("div");
- savingOverlay.classList.add("saving-overlay");
- savingOverlay.textContent = "Saving...";
-
- savingOverlay.style.position = "absolute";
- savingOverlay.style.top = "0";
- savingOverlay.style.left = "0";
- savingOverlay.style.width = "100%";
- savingOverlay.style.height = "100%";
- savingOverlay.style.display = "flex";
- savingOverlay.style.justifyContent = "center";
- savingOverlay.style.alignItems = "center";
- savingOverlay.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
- savingOverlay.style.color = "white";
- savingOverlay.style.zIndex = "10";
-
- settingsContent.appendChild(savingOverlay);
-
- return savingOverlay;
-}
\ No newline at end of file
diff --git a/layout/js/popup/popup.js b/layout/js/popup/popup.js
new file mode 100644
index 0000000..daedd92
--- /dev/null
+++ b/layout/js/popup/popup.js
@@ -0,0 +1,549 @@
+class Popup {
+ constructor() {
+ this.handleDOMContentLoaded();
+ this.settings();
+ this.documentTabLinks();
+ this.registerDocumentEvents();
+ }
+
+ handleDOMContentLoaded() {
+
+ document.addEventListener("DOMContentLoaded", () => {
+
+ window.MyAssistant.updates.checkForUpdates();
+ window.MyAssistant.tokenCount.refreshTokenCount();
+ window.MyAssistant.promptsMenu.updateCustomContextMenuOptions();
+
+ document.getElementById('shareButtonsToggle').addEventListener('change', (event) => {
+ chrome.storage.sync.set({ showShareButtons: event.target.checked });
+ });
+
+ chrome.storage.sync.get('showShareButtons', (data) => {
+ document.getElementById('shareButtonsToggle').checked = data.showShareButtons;
+ });
+
+ document.getElementById("useGithubToggle").addEventListener("change", function () {
+ const useGithub = this.checked;
+ chrome.storage.sync.set({ useGithub }, function () {
+ console.log("Use GitHub setting saved.");
+ });
+
+ // Show or hide the GitHub URL input field based on the toggle state
+ // const githubUrlContainer = document.getElementById("githubUrlContainer");
+ const customPromptContainer = document.getElementById("customPromptContainer");
+ if (useGithub) {
+ // githubUrlContainer.style.display = "block";
+ customPromptContainer.style.display = "none";
+ } else {
+ // githubUrlContainer.style.display = "none";
+ customPromptContainer.style.display = "block";
+ }
+ });
+
+ document.getElementById("githubUrl").addEventListener("input", function () {
+ const githubUrl = this.value;
+ chrome.storage.sync.set({ githubUrl }, function () {
+ console.log("GitHub URL saved.");
+ });
+ });
+
+ // Load the saved settings when the popup is opened
+ chrome.storage.sync.get(["useGithub", "githubUrl"], function (result) {
+ const useGithubToggle = document.getElementById("useGithubToggle");
+ const githubUrl = document.getElementById("githubUrl");
+ const githubUrlContainer = document.getElementById("githubUrlContainer");
+ const customPromptContainer = document.getElementById("customPromptContainer");
+
+ useGithubToggle.checked = result.useGithub || false;
+ githubUrl.value = result.githubUrl || "";
+
+ if (useGithubToggle.checked) {
+ // githubUrlContainer.style.display = "block";
+ customPromptContainer.style.display = "none";
+ } else {
+ // githubUrlContainer.style.display = "none";
+ customPromptContainer.style.display = "block";
+ }
+ });
+
+ // Get the hamburger menu and settings overlay elements
+ const hamburgerMenu = document.querySelector(".hamburger-menu");
+ const settingsOverlay = document.querySelector(".settings-overlay");
+ const settingsMenuItems = document.querySelectorAll(".settings-menu li");
+
+ // Add event listener for the hamburger menu click
+ hamburgerMenu.addEventListener("click", () => {
+ if (settingsOverlay.classList.contains("visible")) {
+ settingsOverlay.classList.remove("visible");
+ } else {
+ settingsOverlay.classList.add("visible");
+ }
+ });
+
+ // Add event listener for the menu items click
+ settingsMenuItems.forEach((menuItem) => {
+ menuItem.addEventListener("click", () => {
+ settingsOverlay.classList.remove("visible");
+ });
+ });
+
+ document.body.addEventListener("contextmenu", function (event) {
+ if (event.target.closest('#suggestions-container') || event.target.closest('#custom-context-menu-directions')) {
+ event.preventDefault();
+
+ showCustomContextMenu();
+ }
+ });
+
+ // Hide the custom context menu when clicking outside of it
+ document.addEventListener('click', (e) => {
+ const customContextMenu = document.getElementById('custom-context-menu');
+ if (e.target.closest('#custom-context-menu') === null) {
+ customContextMenu.style.display = 'none';
+ }
+ });
+
+ // load the custom context menu options to the menu.
+ chrome.storage.sync.get("customContextMenuPrompts", (result) => {
+ const prompts = result.customContextMenuPrompts || [];
+ const customContextMenuList = document.querySelector("#custom-context-menu > ul");
+ customContextMenuList.innerHTML = "";
+
+ prompts.forEach((prompt, index) => {
+ const listItem = document.createElement("li");
+ listItem.id = `option-${index}`;
+ listItem.textContent = prompt.description;
+ listItem.setAttribute("value", prompt.value);
+ customContextMenuList.appendChild(listItem);
+ });
+ });
+
+
+ window.MyAssistant.promptsMenu.promptsSettingsForm();
+ window.MyAssistant.promptsMenu.promptsGetMenuPrompts();
+
+
+ // Define the "adjustTextarea" function to resize the textarea
+ const textarea = document.querySelector('#input-text');
+ const submitBtn = document.querySelector('#submit-button');
+
+ textarea.addEventListener('input', adjustTextarea);
+ submitBtn.addEventListener('click', resetTextareaHeight);
+ textarea.addEventListener('focus', adjustTextarea); // Add this line to resize the textarea on focus
+
+ function adjustTextarea() {
+ textarea.style.height = 'auto';
+ textarea.style.height = `${textarea.scrollHeight}px`;
+ }
+
+ document.addEventListener('click', function(event) {
+ if (event.target !== submitBtn && event.target !== textarea) {
+ resetTextareaHeight();
+ }
+ });
+
+ function resetTextareaHeight() {
+ textarea.style.height = 'auto';
+ }
+
+ // Initialize Materialize tooltips
+ const tooltips = document.querySelectorAll(".tooltipped");
+ M.Tooltip.init(tooltips);
+
+ // Function to show the custom context menu
+ function showCustomContextMenu() {
+ const customContextMenu = document.getElementById("custom-context-menu");
+ customContextMenu.style.display = "block";
+ customContextMenu.style.position = "fixed";
+ customContextMenu.style.opacity = '0'; // initially hide the menu
+
+ const bodyWidth = document.body.clientWidth;
+ const bodyHeight = document.body.clientHeight;
+ const menuWidth = customContextMenu.offsetWidth;
+ const menuHeight = customContextMenu.offsetHeight;
+
+ // Define the vertical position from the top
+ const verticalPosition = 100; // Change this value to adjust the position
+
+ customContextMenu.style.left = `${(bodyWidth - menuWidth) / 2}px`;
+ customContextMenu.style.top = `${verticalPosition}px`;
+
+ // delay and fade in
+ setTimeout(() => {
+ customContextMenu.style.opacity = '1';
+ }, 100); // delay of 100ms, adjust as needed
+ }
+
+ window.MyAssistant.customColors.settingsFontSize();
+ window.MyAssistant.customColors.settingsPopupSize();
+ window.MyAssistant.customColors.settingsCustomColors();
+ window.MyAssistant.customColors.settingsButtonColors();
+
+ const inputText = document.getElementById("input-text");
+
+ inputText.addEventListener("input", function () {
+ chrome.storage.local.set({ savedText: this.value }, function () {
+
+ console.log("Text saved.");
+ });
+ });
+
+ document.getElementById("engineToggle").addEventListener("change", function() {
+ chrome.storage.sync.set({ engine: this.checked ? 'gpt4' : 'gpt3' }, function() {
+ console.log("Engine setting saved.");
+ });
+ });
+
+ // This gets the saved text that might be in the input should the user close the popup and reopen.
+ chrome.storage.local.get("savedText", function (data) {
+ inputText.value = data.savedText || "";
+
+ // Check if there are any suggestions in the suggestions-container
+ const suggestionsContainer = document.getElementById("suggestions-container");
+ const hasSuggestions = suggestionsContainer.childElementCount > 0;
+
+ // Check if the input-text field is empty. This only works for text that was pasted into the textarea,
+ // and the popup is closed and reponed. It does not work for pre-selected text on the page that gets added to textarea,
+ // that is fixed in getSelectedTextFromActiveTab function.
+ const inputTextEmpty = !inputText.value || inputText.value.trim() === "";
+
+ if (!hasSuggestions && inputTextEmpty) {
+ showCustomContextMenu();
+ } else {
+ const customContextMenu = document.getElementById("custom-context-menu");
+ customContextMenu.style.display = "none";
+ }
+ });
+
+ // Add event listeners for the tablinks
+ document.querySelectorAll(".settings-menu .tablinks").forEach((tab) => {
+ tab.addEventListener("click", (event) => {
+ this.openTab(event.target.closest("li").getAttribute("data-tab"));
+ // Remove active class from other tablinks
+ document.querySelectorAll(".settings-menu .tablinks").forEach(t => t.classList.remove("active"));
+ event.target.closest("li").classList.add("active");
+ window.MyAssistant.customColors.addBodyClass();
+ });
+ });
+
+
+ var inputs = document.querySelectorAll('input');
+
+ inputs.forEach(function(input) {
+ input.addEventListener('focus', function() {
+ var label = document.querySelector('label[for="' + input.id + '"]');
+ if (label) {
+ label.style.color = '#1a73e8'; // Change this to the desired color
+ }
+ });
+
+ input.addEventListener('blur', function() {
+ var label = document.querySelector('label[for="' + input.id + '"]');
+ if (label) {
+ label.style.color = ''; // Reset the color to the default
+ }
+ });
+ });
+
+ // Add a cancel flag
+ let cancelRequest = false;
+
+ // Start Suggestions and adding them to storage using my sendMessageToBackground function.
+ const submitButton = document.getElementById("submit-button");
+ if (submitButton) {
+ submitButton.addEventListener("click", async () => {
+ if (submitButton.innerText === "Submit") {
+ const inputText = document.getElementById("input-text").value;
+ if (inputText.trim() === "") {
+ window.MyAssistant.loadingSuggestions.showOverlay("Type text below...");
+ return;
+ }
+ try {
+ const inputTextObj = { text: inputText, isUserInput: true };
+ window.MyAssistant.loadingSuggestions.displaySuggestions(inputTextObj, []);
+
+ const loadingAnimation = window.MyAssistant.loadingSuggestions.showLoadingAnimation();
+ window.MyAssistant.loadingSuggestions.scrollToLoadingAnimation(loadingAnimation);
+
+ submitButton.innerText = "Cancel";
+ cancelRequest = false;
+ const engine = document.getElementById("engineToggle").checked ? 'gpt4' : 'gpt3';
+ const response = await this.sendMessageToBackground(inputText, { action: "getSuggestions", engine });
+
+ if (!cancelRequest) {
+ // If there is no error message, display the suggestions
+ const suggestions = response.suggestions.map(suggestion => ({ text: suggestion, isUserInput: false }));
+ window.MyAssistant.loadingSuggestions.displaySuggestions(null, suggestions);
+ }
+ } catch (error) {
+ console.error("Error:", error.message);
+ const errorMessage = { text: `Error: ${error.message}`, isUserInput: false };
+ window.MyAssistant.loadingSuggestions.displaySuggestions(null, [errorMessage]);
+
+ } finally {
+ window.MyAssistant.loadingSuggestions.hideLoadingAnimation();
+ submitButton.innerText = "Submit";
+ document.getElementById("input-text").value = "";
+ }
+ } else {
+ cancelRequest = true;
+ submitButton.innerText = "Cancel";
+ }
+ });
+ } else {
+ console.error('submit-button not found');
+ }
+
+
+ // Clear Suggestions from container and storage
+ const clearButton = document.getElementById("clear-button");
+ if (clearButton) {
+ clearButton.addEventListener("click", () => {
+ // Clear the suggestions
+ const suggestionsContainer = document.getElementById("suggestions-container");
+ suggestionsContainer.innerHTML = ""; // Clear the suggestions container
+
+ // Also clear the saved suggestions in local storage
+ chrome.storage.local.remove('savedSuggestions', function() {
+ console.log('Saved suggestions cleared.');
+ });
+
+ // Also clear the total token count in local storage
+ chrome.storage.local.remove('totalTokenCount', function() {
+ console.log('Total token count cleared.');
+ });
+
+ // Clear conversation and reset token count
+ chrome.runtime.sendMessage({ action: 'clearConversationAndTokenCount' }, function(response) {
+ console.log(response.message);
+ window.MyAssistant.tokenCount.refreshTokenCount(); // refresh the token count after it's cleared
+ });
+
+ });
+ }
+
+
+
+ // Scroll to the bottom of the suggestionsContainer
+ setTimeout(() => {
+ const suggestionsContainer = document.getElementById("suggestions-container");
+ suggestionsContainer.scrollTop = suggestionsContainer.scrollHeight;
+ }, 0);
+
+
+
+ // Get the active tab
+ // Additional Script selected text in a tab and copy/paste it back.
+ chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
+ const activeTab = tabs[0];
+
+ // Execute the contentScript.js file in the active tab
+ chrome.scripting.executeScript(
+ {
+ target: { tabId: activeTab.id },
+ files: ["layout/js/selected-text/contentScript.js"]
+ },
+ function() {
+ window.MyAssistant.selectedText.getSelectedTextFromActiveTab();
+ }
+ );
+ });
+
+ // Add a message listener to handle the "textCopied" message from the content script
+ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
+ if (request.action === "textCopied") {
+ adjustTextarea(); // Adjust the textarea height when the "textCopied" message is received
+ }
+ });
+
+
+
+ }); // End addEventListener("DOMContentLoaded")
+
+ }
+
+
+ async settings() {
+ // Wrap the event listener assignment in a DOMContentLoaded event listener
+ document.addEventListener("DOMContentLoaded", () => {
+ const settingsForm = document.getElementById("settings-form");
+ if (settingsForm) {
+ settingsForm.addEventListener("submit", (e) => {
+ e.preventDefault();
+
+ // Save the settings here, for example, using the chrome.storage API
+ const apiKey = document.getElementById("api-key").value;
+ const maxTokens = document.getElementById("max-tokens").value;
+ const n = document.getElementById("n").value;
+ const stop = document.getElementById("stop").value;
+ const temperature = document.getElementById("temperature").value;
+
+ chrome.storage.sync.set({
+ apiKey: apiKey,
+ maxTokens: maxTokens,
+ n: n,
+ stop: stop,
+ temperature: temperature,
+ }, () => {
+ console.log('Settings saved');
+ window.MyAssistant.loadingSuggestions.showOverlay("Saving...");
+ });
+ });
+ } else {
+ console.error('settings-form not found');
+ }
+ });
+
+ // Load the settings when the popup is opened, for example, using the chrome.storage API
+ chrome.storage.sync.get(['apiKey', 'maxTokens', 'n', 'stop', 'temperature', 'engine'], (result) => {
+ document.getElementById("api-key").value = result.apiKey || '';
+ document.getElementById("max-tokens").value = result.maxTokens || '';
+ document.getElementById("n").value = result.n || '';
+ document.getElementById("stop").value = result.stop || '';
+ document.getElementById("temperature").value = result.temperature || '';
+ document.getElementById("engineToggle").checked = result.engine === 'gpt4';
+
+ // Add your tab selection logic here
+ // Remove active class from all tab links
+ const tablinks = document.querySelectorAll(".tablinks");
+ tablinks.forEach((tab) => {
+ tab.classList.remove("active");
+ });
+
+ if (!result.apiKey) {
+ // If api-key is blank, open settings-tab
+ this.openTab("settings-tab");
+ document.querySelector('[data-tab="settings-tab"]').classList.add("active");
+ window.MyAssistant.customColors.addBodyClass("active-missing-api-key-exception");
+ } else {
+ // If api-key has some value, open suggestions-tab
+ this.openTab("suggestions-tab");
+ document.querySelector('[data-tab="suggestions-tab"]').classList.add("active");
+ }
+ });
+ }
+
+
+ // Copy text to the clipboard
+ copyToClipboard(text) {
+ const el = document.createElement('textarea');
+ el.value = text;
+ el.setAttribute('readonly', '');
+ el.style.position = 'absolute';
+ el.style.left = '-9999px';
+ document.body.appendChild(el);
+ el.select();
+ document.execCommand('copy');
+ document.body.removeChild(el);
+ }
+
+
+ openTab(tabName) {
+ var i, tabcontent, tablinks;
+ tabcontent = document.getElementsByClassName("tabcontent");
+ for (i = 0; i < tabcontent.length; i++) {
+ tabcontent[i].style.display = "none";
+ }
+ tablinks = document.getElementsByClassName("tablinks");
+ for (i = 0; i < tablinks.length; i++) {
+ tablinks[i].className = tablinks[i].className.replace(" active", "");
+ }
+ if(tabName){
+ document.getElementById(tabName).style.display = "block";
+ }
+ }
+
+
+ documentTabLinks(){
+ document.querySelectorAll(".tablinks").forEach((tab) => {
+ tab.addEventListener("click", (event) => {
+ this.openTab(event.target.getAttribute("data-tab"));
+ });
+ });
+ }
+
+
+ registerDocumentEvents() {
+ window.addEventListener('DOMContentLoaded', (event) => {
+
+ const updateNotification = document.getElementsByClassName('clickUpdateNotification');
+ const customPromptsLink = document.getElementById('customPromptsLink');
+ const suggestionsLink = document.getElementsByClassName('suggestionsLink');
+ const settingsLink = document.getElementsByClassName('settingsLink');
+
+ if (updateNotification && updateNotification.length > 0) {
+ for (let i = 0; i < updateNotification.length; i++) {
+ updateNotification[i].addEventListener('click', () => {
+ this.openTab("changelog-tab");
+ document.querySelector('[data-tab="changelog-tab"]').classList.add("active");
+ window.MyAssistant.updates.setupNotificationClickListener();
+ });
+ }
+ }
+
+ if (customPromptsLink) {
+ customPromptsLink.addEventListener('click', () => {
+ this.openTab("custom-context-menu-settings-tab");
+ document.querySelector('[data-tab="custom-context-menu-settings-tab"]').classList.add("active");
+ window.MyAssistant.customColors.addBodyClass();
+ });
+ }
+
+ if (suggestionsLink && suggestionsLink.length > 0) {
+ for (let i = 0; i < suggestionsLink.length; i++) {
+ suggestionsLink[i].addEventListener('click', () => {
+ this.openTab("suggestions-tab");
+ document.querySelector('[data-tab="suggestions-tab"]').classList.add("active");
+ window.MyAssistant.customColors.addBodyClass();
+ });
+ }
+ }
+
+ if (settingsLink && settingsLink.length > 0) {
+ for (let i = 0; i < settingsLink.length; i++) {
+ settingsLink[i].addEventListener('click', () => {
+ this.openTab("settings-tab");
+ document.querySelector('[data-tab="settings-tab"]').classList.add("active");
+ window.MyAssistant.customColors.addBodyClass();
+ });
+ }
+ }
+
+ const savedPromptsHeader = document.getElementById('saved-custom-prompts-header');
+ const customContextMenuOption = document.querySelector('.custom-context-menu-option');
+
+ if (savedPromptsHeader && customContextMenuOption) {
+ savedPromptsHeader.addEventListener('click', () => {
+ customContextMenuOption.click();
+ });
+ }
+
+ });
+ }
+
+
+ sendMessageToBackground(text, message, cancelSignal) {
+ return new Promise((resolve, reject) => {
+ const listener = function (response) {
+ if (chrome.runtime.lastError) {
+ reject(chrome.runtime.lastError);
+ } else if (response.error) {
+ reject(new Error(response.error));
+ } else {
+ resolve(response);
+ }
+ };
+
+ chrome.runtime.sendMessage({ ...message, text }, listener);
+
+ if (cancelSignal) {
+ cancelSignal.addEventListener('cancel', () => {
+ chrome.runtime.onMessage.removeListener(listener);
+ reject(new Error('Request canceled'));
+ });
+ }
+ });
+ }
+
+}
+window.MyAssistant.popup = new Popup();
\ No newline at end of file
diff --git a/layout/js/prompts/prompts-menu.js b/layout/js/prompts/prompts-menu.js
new file mode 100644
index 0000000..0284aa9
--- /dev/null
+++ b/layout/js/prompts/prompts-menu.js
@@ -0,0 +1,218 @@
+class PromptsMenu {
+ constructor() {
+ this.customContextMenuInitialization();
+ this.updateCustomContextMenuOptions();
+ }
+
+ customContextMenuInitialization() {
+ // This is for the custom context menu items.
+ // Wrap the entire code block with an IIFE to avoid conflicts and isolate the scope
+ (function () {
+ // Declare the customContextMenu variable and assign the element reference
+ const customContextMenu = document.querySelector('#custom-context-menu');
+
+ const onClickHandler = (event) => {
+ const target = event.target;
+
+ if (target.tagName.toLowerCase() === 'li') {
+ event.stopPropagation(); // Prevent event bubbling
+
+ // Replace the "STOP:" text with the selected prompt
+ const selectedPrompt = target.getAttribute("value");
+ const textarea = document.getElementById('input-text');
+ const stopIndex = textarea.value.indexOf('STOP:');
+
+ if (stopIndex !== -1) {
+ // Replace "STOP:" with the selected prompt
+ textarea.setRangeText(selectedPrompt, stopIndex, stopIndex + 5, 'select');
+ } else {
+ // Insert the selected prompt after a line break
+ const insertionIndex = textarea.value.length;
+ textarea.setRangeText( selectedPrompt, insertionIndex, insertionIndex, 'select');
+ }
+ document.getElementById('submit-button').click();
+ }
+ };
+
+ if (customContextMenu) {
+ customContextMenu.removeEventListener('click', onClickHandler);
+ customContextMenu.addEventListener('click', onClickHandler);
+ }
+ })();
+ }
+
+ convertRepoUrlToApiUrl(repoUrl) {
+ // Remove trailing slash if present
+ if (repoUrl.endsWith('/')) {
+ repoUrl = repoUrl.slice(0, -1);
+ }
+
+ // Extract the username and repository name from the URL
+ const repoPath = new URL(repoUrl).pathname;
+ const [username, repoName] = repoPath.split('/').filter(Boolean);
+
+ // Construct and return the GitHub API URL for the README.md file
+ return `https://api.github.com/repos/${username}/${repoName}/contents/README.md`;
+ }
+
+ async updateCustomContextMenuOptions() {
+ chrome.storage.sync.get(["useGithub", "githubUrl"], async (result) => {
+ const useGithub = result.useGithub || false;
+ const githubUrl = result.githubUrl || "";
+ let prompts = [];
+
+ const customContextMenuList = document.querySelector("#custom-context-menu > ul");
+ const customContextMenu = document.querySelector("#custom-context-menu");
+ customContextMenu.style.opacity = '0'; // hide the list initially
+
+ if (useGithub && githubUrl) {
+ const apiUrl = this.convertRepoUrlToApiUrl(githubUrl);
+ try {
+ const readmeContent = await this.fetchReadmeFromRepo(apiUrl);
+ prompts = this.parseReadmeContent(readmeContent);
+ } catch (error) {
+ console.error("Error fetching prompts from the repo:", error.message);
+ prompts = await this.loadPromptsFromChromeStorage();
+ }
+ } else {
+ prompts = await this.loadPromptsFromChromeStorage();
+ }
+
+ // Filter out empty prompts
+ const promptsCheck = prompts.filter(prompt => prompt.description !== "" && prompt.value !== "");
+
+ // If there are no prompts, return from the function early
+ if ( promptsCheck.length === 0) {
+ customContextMenu.style.display = "none";
+ return;
+ }
+
+ const savedPromptsList = document.querySelector(".saved-prompts-list");
+
+ // Check if there's already an a element in savedPromptsList, if not create one.
+ let linkElement = savedPromptsList.querySelector("a");
+ if (!linkElement) {
+ linkElement = document.createElement("a");
+ savedPromptsList.appendChild(linkElement);
+ }
+
+ // Set properties of the link element
+ linkElement.textContent = "Saved Prompts";
+ linkElement.style.display = useGithub ? "block" : "none"; // display only when useGithub is true
+ if (useGithub) {
+ linkElement.href = githubUrl;
+
+ const savedCustomPromptsHeader = document.getElementById("saved-custom-prompts-header");
+ savedCustomPromptsHeader.style.display = "none";
+ }
+ else {
+ const savedCustomPromptsHeader = document.getElementById("saved-custom-prompts-header");
+ savedCustomPromptsHeader.style.display = "block";
+ }
+
+ linkElement.target = "_blank"; // to open in a new tab
+
+ customContextMenuList.innerHTML = "";
+
+ prompts.forEach((prompt, index) => {
+ const listItem = document.createElement("li");
+ listItem.id = `option-${index}`;
+ listItem.textContent = prompt.description;
+ listItem.setAttribute("value", prompt.value);
+ customContextMenuList.appendChild(listItem);
+ });
+
+ setTimeout(() => {
+ customContextMenu.style.opacity = '1'; // fade the menu in after a delay
+ }, 200); // adjust delay as needed
+ });
+ }
+
+ async fetchReadmeFromRepo(repoUrl) {
+ const response = await fetch(repoUrl);
+ const data = await response.json();
+ // Decode the base64 encoded content
+ return atob(data.content);
+ }
+
+ parseReadmeContent(content) {
+ const lines = content.split('\n');
+ const prompts = [];
+ let startParsing = false; // flag to indicate when to start parsing
+
+ lines.forEach(line => {
+ // check if the current line starts with a dash (-)
+ if (line.trim().startsWith('-')) {
+ startParsing = true; // start parsing from the current line
+ }
+
+ // if startParsing is true, parse the line
+ if (startParsing) {
+ const match = line.match(/^\-\s(.+):\s(.+)$/);
+ if (match) {
+ prompts.push({ description: match[1], value: match[2] });
+ }
+ }
+ });
+
+ return prompts;
+ }
+
+ loadPromptsFromChromeStorage() {
+ return new Promise((resolve) => {
+ chrome.storage.sync.get("customContextMenuPrompts", (result) => {
+ const prompts = result.customContextMenuPrompts || [];
+ resolve(prompts);
+ });
+ });
+ }
+
+
+
+ promptsSettingsForm(){
+ // Add this code in the DOMContentLoaded event listener in popup.js
+ const customContextMenuSettingsForm = document.getElementById("custom-context-menu-settings-tab");
+ if (customContextMenuSettingsForm) {
+ customContextMenuSettingsForm.addEventListener("submit", (e) => {
+ e.preventDefault();
+
+ const prompts = [];
+ for (let i = 1; i <= 5; i++) {
+ const promptDescriptionInput = document.getElementById(`prompt-${i}-description`);
+ const promptValueInput = document.getElementById(`prompt-${i}-value`);
+ if (promptDescriptionInput && promptValueInput) {
+ prompts.push({
+ description: promptDescriptionInput.value,
+ value: promptValueInput.value
+ });
+ }
+ }
+
+ chrome.storage.sync.set({ customContextMenuPrompts: prompts }, () => {
+ console.log("Custom context menu prompts saved.");
+ window.MyAssistant.loadingSuggestions.showOverlay("Saving...");
+ this.updateCustomContextMenuOptions(); // Call the function to update the custom context menu options
+ });
+ });
+ } else {
+ console.error("Custom context menu settings form not found");
+ }
+ }
+
+ promptsGetMenuPrompts(){
+ // Load the custom context menu settings when the popup is opened
+ chrome.storage.sync.get("customContextMenuPrompts", (result) => {
+ const prompts = result.customContextMenuPrompts || [];
+ for (let i = 1; i <= 5; i++) {
+ const promptDescriptionInput = document.getElementById(`prompt-${i}-description`);
+ const promptValueInput = document.getElementById(`prompt-${i}-value`);
+ if (promptDescriptionInput && promptValueInput) {
+ promptDescriptionInput.value = prompts[i - 1]?.description || "";
+ promptValueInput.value = prompts[i - 1]?.value || "";
+ }
+ }
+ });
+ }
+
+}
+window.MyAssistant.promptsMenu = new PromptsMenu;
\ No newline at end of file
diff --git a/layout/js/selected-text/contentScript.js b/layout/js/selected-text/contentScript.js
new file mode 100644
index 0000000..c1a43f9
--- /dev/null
+++ b/layout/js/selected-text/contentScript.js
@@ -0,0 +1,70 @@
+// This script is to allow us to get selected text on the webpage
+// and then copy it to our input field for a quicker user experience.
+if (!window.hasRunContentScript) {
+ window.hasRunContentScript = true;
+
+ console.log('contentScript.js is loading ok.');
+
+ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
+
+ if (request.action === "getSelectedText") {
+ const selectedText = window.getSelection().toString();
+
+ //console.log('Runtime.onMessage get selected text' );
+ //console.log( selectedText );
+
+ chrome.runtime.sendMessage({ action: "textCopied" });
+
+ sendResponse(selectedText);
+
+ } else if (request.action === 'replaceSelectedText') {
+ const replaced = replaceSelectedText(request.replacementText);
+ sendResponse({ success: true, replaced });
+ }
+ });
+
+ let isReplacing = false;
+
+ function replaceSelectedText(replacementText) {
+ if (isReplacing) {
+ return false; // Prevent multiple calls during the same event
+ }
+
+ isReplacing = true;
+
+ const activeElement = document.activeElement;
+ const isContentEditable = activeElement.getAttribute("contentEditable") === "true";
+ const selection = window.getSelection();
+ let replaced = false;
+
+ if (activeElement.tagName.toLowerCase() === 'textarea' || activeElement.tagName.toLowerCase() === 'input') {
+ const start = activeElement.selectionStart;
+ const end = activeElement.selectionEnd;
+ activeElement.setRangeText(replacementText, start, end, 'select');
+ activeElement.focus();
+ replaced = true;
+ } else if (isContentEditable) {
+ if (selection.rangeCount > 0) {
+ const range = selection.getRangeAt(0);
+ range.deleteContents();
+ const textNode = document.createTextNode(replacementText);
+ range.insertNode(textNode);
+
+ // Move the caret to the end of the inserted text
+ range.setStartAfter(textNode);
+ range.setEndAfter(textNode);
+ range.collapse(false);
+
+ selection.removeAllRanges();
+ selection.addRange(range);
+ activeElement.focus();
+ replaced = true;
+ }
+ } else {
+ console.log('Cannot replace selected text: active element is not an input, textarea, or contentEditable element');
+ }
+
+ isReplacing = false; // Reset the flag after the function has finished
+ return replaced;
+ }
+}
diff --git a/layout/js/selected-text/selected-text.js b/layout/js/selected-text/selected-text.js
new file mode 100644
index 0000000..7e5d368
--- /dev/null
+++ b/layout/js/selected-text/selected-text.js
@@ -0,0 +1,41 @@
+class SelectedText {
+
+ getSelectedTextFromActiveTab() {
+ chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
+ const activeTab = tabs[0];
+ chrome.tabs.sendMessage(activeTab.id, { action: "getSelectedText" }, (response) => {
+ // Check if response exists before trying to trim it
+ if (response && response.trim() !== "") {
+ const inputText = document.getElementById("input-text");
+ inputText.value = response + '\n\n' + 'STOP: ';
+
+ // Check if there are any suggestions in the suggestions-container
+ const suggestionsContainer = document.getElementById("suggestions-container");
+ const hasSuggestions = suggestionsContainer.childElementCount > 0;
+
+ // Check if the input-text field is empty
+ const inputTextEmpty = !inputText.value || inputText.value.trim() === "";
+
+ if (!hasSuggestions && inputTextEmpty) {
+ showCustomContextMenu();
+ } else {
+ const customContextMenu = document.getElementById("custom-context-menu");
+ customContextMenu.style.display = "none";
+ }
+ }
+ });
+ });
+ }
+
+ replaceSelectedTextOnActiveTab(replacementText) {
+ chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
+ const activeTab = tabs[0];
+ chrome.tabs.sendMessage(activeTab.id, { action: 'replaceSelectedText', replacementText }, (response) => {
+ if (!response.replaced) {
+ window.MyAssistant.popup.copyToClipboard(replacementText);
+ }
+ });
+ });
+ }
+}
+window.MyAssistant.selectedText = new SelectedText();
\ No newline at end of file
diff --git a/layout/js/share/share.js b/layout/js/share/share.js
new file mode 100644
index 0000000..62fce42
--- /dev/null
+++ b/layout/js/share/share.js
@@ -0,0 +1,25 @@
+class Share {
+ createShareButtons(responseTexts) {
+ const shareContainer = document.createElement("div");
+ shareContainer.classList.add("share-container");
+
+ responseTexts.forEach(responseText => {
+ const twitterShare = document.createElement("button");
+ twitterShare.classList.add("share-button", "twitter-share");
+ twitterShare.innerHTML = `
+
+ `;
+ twitterShare.addEventListener("click", () => {
+ const url = `https://twitter.com/intent/tweet?text=${encodeURIComponent(responseText)}`;
+ window.open(url, "_blank");
+ });
+
+ shareContainer.appendChild(twitterShare);
+ });
+
+ return shareContainer;
+ }
+}
+window.MyAssistant.share = new Share();
\ No newline at end of file
diff --git a/layout/js/suggestions/loading-suggestions.js b/layout/js/suggestions/loading-suggestions.js
new file mode 100644
index 0000000..1d69e30
--- /dev/null
+++ b/layout/js/suggestions/loading-suggestions.js
@@ -0,0 +1,289 @@
+class LoadingSuggestions {
+ constructor() {
+ this.getSavedSuggestions();
+ }
+ urlify(text) {
+
+ // Here are a few examples of the URLs that this regex would capture:
+ // https://www.example.com
+ // http://www.example.com
+ // https://www.example.com/path/to/resource
+ // https://www.example.com?query=string
+ // https://www.example.com#fragment
+ // https://sub-domain.example.com
+ //And here are a few examples of strings that this regex would not consider to be URLs:
+ // https://www.example.com.
+ // https://www.example.com!
+ // https://www.example.com,
+ // https://www.example.com...
+ // https://www..example.com
+ let urlRegex = /(https?:\/\/[^\s\/)]+(\/[^\s)]*)?)/g;
+
+ text = text.replace(urlRegex, function(url) {
+ return '' + url + '';
+ });
+
+ let twitterHandleRegex = /(@[a-zA-Z0-9_]{1,15})/g;
+ text = text.replace(twitterHandleRegex, function(handle) {
+ let handleWithoutAt = handle.substring(1);
+ return '' + handle + '';
+ });
+
+ let twitterHashtagRegex = /(#[a-zA-Z0-9_]+)/g;
+ text = text.replace(twitterHashtagRegex, function(hashtag) {
+ let hashtagWithoutHash = encodeURIComponent(hashtag.substring(1));
+ return '' + hashtag + '';
+ });
+
+ let codeBlockRegex = /(```[\s\S]*?```)/g;
+ text = text.replace(codeBlockRegex, function(code) {
+ // Convert markdown to HTML
+ let html = new showdown.Converter({backslashEscapesHTMLTags: true}).makeHtml(code);
+
+ return html;
+ });
+
+ return text;
+ }
+
+ createSuggestionElement(suggestionObj) {
+ const suggestion = document.createElement("div");
+ suggestion.classList.add("suggestion");
+
+ if (suggestionObj.isUserInput) {
+ suggestion.classList.add("user-input");
+ }
+
+ suggestion.innerHTML = this.urlify(suggestionObj.text);
+
+ // Add an event listener to the suggestion element
+ suggestion.addEventListener('click', (e) => {
+
+ let selectedText = window.getSelection().toString();
+
+ // If user selected some text, copy that
+ if (selectedText.length > 0) {
+ window.MyAssistant.selectedText.replaceSelectedTextOnActiveTab(selectedText);
+ }
+ // If no text is selected, copy all the text within the div
+ else {
+ window.MyAssistant.selectedText.replaceSelectedTextOnActiveTab(suggestionObj.text);
+ }
+ this.showCopiedOverlay(e.target);
+ });
+
+ const shareButtons = window.MyAssistant.share.createShareButtons([suggestionObj.text]);
+
+ chrome.storage.sync.get('showShareButtons', (data) => {
+ if (data.showShareButtons) {
+ const shareButtons = window.MyAssistant.share.createShareButtons([suggestionObj.text]);
+ suggestion.appendChild(shareButtons);
+ }
+ });
+
+ return suggestion;
+ }
+
+ // This needs to be out of the addEventListener("DOMContentLoaded") so the
+ // custom context menu will not load if there are suggestions in the local storage
+ // that need to be loaded.
+ // Get Saved Suggestions from local storage if there before anything else.
+ getSavedSuggestions() {
+
+ chrome.storage.local.get("savedSuggestions", (data) => {
+
+ console.log(data);
+ const suggestions = data.savedSuggestions || [];
+ let totalTokenCount = 0;
+ const suggestionsContainer = document.getElementById("suggestions-container");
+ suggestions.forEach(suggestionObj => {
+ const suggestionElement = this.createSuggestionElement(suggestionObj);
+ suggestionsContainer.appendChild(suggestionElement);
+ // Call Prism.highlightAll() after the content has been added to the DOM
+ Prism.highlightAll();
+
+ console.log("Current suggestion tokens: ", suggestionObj.tokens);
+
+ // Add the token count of the current suggestion to the total token count
+ totalTokenCount += suggestionObj.tokens;
+ });
+ // Now totalTokenCount contains the sum of tokens of all saved suggestions
+ console.log(totalTokenCount);
+ // Find the HTML element where the total token count will be displayed
+ const totalTokenCountElement = document.getElementById("token-count");
+
+ // Update its text content
+ totalTokenCountElement.textContent = `Total Tokens: ${totalTokenCount}`;
+ });
+ }
+
+ displaySuggestions(inputTextObj, suggestions) {
+ const suggestionsContainer = document.getElementById("suggestions-container");
+
+ let totalTokenCount = 0; // Initialize total token count
+
+ if (inputTextObj) {
+ const inputTextElement = this.createSuggestionElement(inputTextObj);
+ suggestionsContainer.appendChild(inputTextElement);
+ totalTokenCount += inputTextObj.tokens; // Add the token count of the inputTextObj to the total token count
+ }
+
+ for (const suggestionObj of suggestions) {
+ const suggestionElement = this.createSuggestionElement(suggestionObj);
+ suggestionsContainer.appendChild(suggestionElement);
+ totalTokenCount += suggestionObj.tokens; // Add the token count of the suggestionObj to the total token count
+ }
+
+ // Save total token count to local storage
+ chrome.storage.local.set({ totalTokenCount: totalTokenCount }, function() {
+
+ // Testing
+ // console.log("Total token count saved to local storage: " + totalTokenCount);
+
+ window.MyAssistant.tokenCount.refreshTokenCount(); // Move the call to refreshTokenCount here
+
+ if (chrome.runtime.lastError) {
+ console.log("Runtime error: ", chrome.runtime.lastError);
+ } else {
+ // Testing
+ // console.log("Total token count saved to local storage: " + totalTokenCount);
+ }
+ });
+
+ // Call Prism.highlightAll() after the content has been added to the DOM
+ Prism.highlightAll();
+
+ chrome.storage.local.get(['savedSuggestions'], function(result) {
+ let existingSuggestions = result.savedSuggestions;
+ if (existingSuggestions === undefined) {
+ existingSuggestions = [];
+ }
+
+ // Add input text to existingSuggestions
+ if (inputTextObj) {
+ inputTextObj.tokens = inputTextObj.text.split(" ").length;
+ existingSuggestions.push(inputTextObj);
+ }
+
+ // Add all new suggestions to existingSuggestions
+ suggestions.forEach(suggestionObj => {
+ suggestionObj.tokens = suggestionObj.text.split(" ").length;
+ existingSuggestions.push(suggestionObj);
+ });
+
+ // Save the updated list back to storage
+ chrome.storage.local.set({ savedSuggestions: existingSuggestions }, function () {
+ console.log("Suggestions saved.");
+ // Update total token count
+ let totalTokenCount = 0;
+ existingSuggestions.forEach(suggestionObj => {
+ totalTokenCount += suggestionObj.tokens;
+ });
+ // Save the total token count in the local storage
+ chrome.storage.local.set({ totalTokenCount: totalTokenCount }, function() {
+ // Testing
+ // console.log("Total token count saved to local storage: " + totalTokenCount);
+ window.MyAssistant.tokenCount.refreshTokenCount(); // Refresh the count after it's saved
+ });
+ });
+ });
+
+ // Show or hide the "Clear" button
+ const clearButton = document.getElementById("clear-button");
+ if (clearButton) {
+ if (suggestions.length > 0) {
+ clearButton.style.display = "inline-block";
+ } else {
+ clearButton.style.display = "none";
+ }
+ }
+ }
+
+ showLoadingAnimation() {
+ const suggestionsContainer = document.getElementById("suggestions-container");
+ // this will clear the suggestions if you click the Get Suggestions button.
+ // suggestionsContainer.innerHTML = ""; // Clear the suggestions container
+
+ const loadingAnimation = document.createElement("div");
+ loadingAnimation.classList.add("loading-animation");
+
+ for (let i = 0; i < 3; i++) {
+ const dot = document.createElement("div");
+ dot.classList.add("dot");
+ loadingAnimation.appendChild(dot);
+ }
+
+ suggestionsContainer.appendChild(loadingAnimation);
+ return loadingAnimation; // Add this line
+ }
+
+ scrollToLoadingAnimation(loadingAnimation) {
+ const container = document.getElementById("suggestions-container");
+ const loadingAnimationOffset = loadingAnimation.offsetTop - 140;
+ container.scrollTo({
+ top: loadingAnimationOffset,
+ behavior: 'smooth'
+ });
+ }
+
+
+ hideLoadingAnimation() {
+ const loadingAnimation = document.querySelector(".loading-animation");
+ if (loadingAnimation) {
+ document.getElementById("suggestions-container").removeChild(loadingAnimation);
+ }
+ }
+
+// For Get Suggestions specifically
+ showCopiedOverlay(target) {
+ const overlay = document.createElement('div');
+ overlay.classList.add('copied-overlay');
+ overlay.textContent = 'Copied';
+
+ // Set position and size of the overlay
+ const suggestionRect = target.getBoundingClientRect();
+
+ overlay.style.position = 'fixed';
+ overlay.style.top = suggestionRect.top + 'px';
+ overlay.style.left = suggestionRect.left + 'px';
+ overlay.style.width = suggestionRect.width + 'px';
+ overlay.style.height = suggestionRect.height + 'px';
+ overlay.style.display = 'flex';
+ overlay.style.justifyContent = 'center';
+ overlay.style.alignItems = 'center';
+
+ document.body.appendChild(overlay);
+ setTimeout(() => {
+ document.body.removeChild(overlay);
+ }, 1500);
+ }
+
+// For Saving
+ showOverlay(text) {
+ const targetElement = document.body;
+ const overlay = document.createElement("div");
+ overlay.classList.add("overlay");
+ overlay.textContent = text;
+
+ overlay.style.position = "fixed";
+ overlay.style.top = "0";
+ overlay.style.left = "0";
+ overlay.style.width = "100%";
+ overlay.style.height = "100%";
+ overlay.style.display = "flex";
+ overlay.style.justifyContent = "center";
+ overlay.style.alignItems = "center";
+ overlay.style.backgroundColor = "rgba(0, 0, 0, .8)";
+ overlay.style.color = "white";
+ overlay.style.zIndex = "10";
+
+ targetElement.appendChild(overlay);
+
+ setTimeout(() => {
+ document.body.removeChild(overlay);
+ }, 1500);
+
+ // return overlay;
+ }
+}
+window.MyAssistant.loadingSuggestions = new LoadingSuggestions();
\ No newline at end of file
diff --git a/layout/js/token-count/token-count.js b/layout/js/token-count/token-count.js
new file mode 100644
index 0000000..1cbe9c4
--- /dev/null
+++ b/layout/js/token-count/token-count.js
@@ -0,0 +1,19 @@
+class TokenCount {
+ refreshTokenCount() {
+ // Retrieve total token count from local storage
+ chrome.storage.local.get("totalTokenCount", function(data) {
+ let totalTokenCount = data.totalTokenCount || 0; // If there's no value in the local storage, default to 0
+
+ // Find the HTML element where the total token count will be displayed
+ const totalTokenCountElement = document.getElementById("token-count");
+
+ // Used for testing. This will return NAN if using the clear button, be best to set an option to
+ // not run it in the future.
+ // console.log("Total token count loaded from local storage: " + data.totalTokenCount);
+
+ // Update its text content
+ totalTokenCountElement.textContent = `Total Tokens: ${totalTokenCount}`;
+ });
+ }
+}
+window.MyAssistant.tokenCount = new TokenCount();
\ No newline at end of file
diff --git a/layout/js/updates/updates.js b/layout/js/updates/updates.js
new file mode 100644
index 0000000..75cf7b7
--- /dev/null
+++ b/layout/js/updates/updates.js
@@ -0,0 +1,67 @@
+class Updates {
+ checkForUpdates() {
+ // Fetch the last notified version and last clicked version from storage
+ chrome.storage.sync.get(['lastNotifiedVersion', 'lastClickedVersion'], ({ lastNotifiedVersion, lastClickedVersion }) => {
+ // Fetch the current version from the extension's manifest
+ const currentVersion = chrome.runtime.getManifest().version;
+
+ // If the current version doesn't match the last notified version or the last clicked version, show the update notification
+ if (!lastNotifiedVersion || currentVersion !== lastNotifiedVersion || currentVersion !== lastClickedVersion) {
+ document.getElementById('updateNotification').style.opacity = '1';
+ console.log( currentVersion );
+ console.log( lastClickedVersion );
+ } else {
+ // If the versions match, hide the update notification
+ document.getElementById('updateNotification').style.opacity = '0';
+ document.getElementById('updateNotification').style.pointerEvents = 'none';
+ }
+ });
+ }
+
+ setupNotificationClickListener() {
+ // When the notification is clicked, update the last clicked version to the current version
+ const currentVersion = chrome.runtime.getManifest().version;
+ chrome.storage.sync.set({ lastClickedVersion: currentVersion }, () => {
+ // Hide the notification
+ document.getElementById('updateNotification').style.opacity = '0';
+ document.getElementById('updateNotification').style.pointerEvents = 'none';
+
+ // Request the changelog from the background script
+ this.fetchChangeLog();
+ });
+ }
+
+
+
+ fetchChangeLog() {
+ const changelogContainer = document.getElementById("changelog-container");
+
+ // Create and display loading animation
+ const loadingAnimation = document.createElement("div");
+ loadingAnimation.classList.add("loading-animation");
+ for (let i = 0; i < 3; i++) {
+ const dot = document.createElement("div");
+ dot.classList.add("dot");
+ loadingAnimation.appendChild(dot);
+ }
+ changelogContainer.appendChild(loadingAnimation);
+
+ // Request the changelog from the background script
+ chrome.runtime.sendMessage({action: "fetchChangelog"}, response => {
+ // Remove loading animation
+ changelogContainer.removeChild(loadingAnimation);
+
+ // Handle fetch failure
+ if (response.error) {
+ changelogContainer.textContent = "Failed to load the changelog. Please try again.";
+ return;
+ }
+
+ // Display and fade in changelog text
+ changelogContainer.textContent = response.changelog;
+ changelogContainer.classList.add("changelog-fade-in");
+ });
+ }
+
+}
+window.MyAssistant.updates = new Updates();
\ No newline at end of file
diff --git a/manifest.json b/manifest.json
index 921b8b7..ea9a4a1 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,9 +1,9 @@
{
"manifest_version": 3,
"name": "SlickRemix: Your ChatGPT Web Assistant",
- "version": "0.0.1",
- "description": "Swiftly optimize text, code, and creativity on any website using GPT-3 or GPT-4.",
- "permissions": ["storage", "tabs", "activeTab"],
+ "version": "0.0.3",
+ "description": "Swiftly optimize text, code, and creativity on any website using GPT-3 Turbo or GPT-4.",
+ "permissions": ["storage","activeTab","scripting"],
"action": {
"default_popup": "popup.html",
"default_icon": {
@@ -15,12 +15,6 @@
"background": {
"service_worker": "background.js"
},
- "content_scripts": [
- {
- "matches": [""],
- "js": ["content.js"]
- }
- ],
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
diff --git a/namespace.js b/namespace.js
new file mode 100644
index 0000000..12ae35a
--- /dev/null
+++ b/namespace.js
@@ -0,0 +1,2 @@
+// My namespace.js
+window.MyAssistant = window.MyAssistant || {};
\ No newline at end of file
diff --git a/popup.html b/popup.html
index 4c25e44..2f9990b 100644
--- a/popup.html
+++ b/popup.html
@@ -1,92 +1,365 @@
-
+
+
-
-
-
+
+
-
- Your Personal Automated Assistant
-
-
-
+
+
+
+
+
+
+
-
-
+
+
-
+ Token count: 0
+ Settings Options
+ To get started, add an API Key below. If you don't have one, you can sign up for Free to gain your API Key from OpenAI. After
+ adding your API Key, click the menu in the top right corner and choose Custom Prompts;
+ otherwise, click Suggestions.
+