Skip to content

Commit

Permalink
Chat mode
Browse files Browse the repository at this point in the history
  • Loading branch information
Ali Raheem committed May 19, 2023
1 parent 07ffdab commit 4ece286
Show file tree
Hide file tree
Showing 12 changed files with 295 additions and 93 deletions.
Binary file modified aify.xpi
Binary file not shown.
7 changes: 2 additions & 5 deletions plugin/html/API.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const fetchModels = async (apiKey) => {
return await response.json();
};

export const fetchResponse = async (apiKey, model, original, prompt, maxTokens) => {
export const fetchResponse = async (apiKey, model, messages, maxTokens) => {
const response = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
Expand All @@ -23,10 +23,7 @@ export const fetchResponse = async (apiKey, model, original, prompt, maxTokens)
},
body: JSON.stringify({
model,
messages: [
{ role: "system", content: prompt },
{ role: "user", content: original }
],
messages: messages,
...(maxTokens > 0 ? { 'max_tokens': parseInt(maxTokens) } : {})
}),
});
Expand Down
1 change: 1 addition & 0 deletions plugin/html/actions.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<body>
<div id="actions-container"></div>
<hr>
<a href="/html/chat.html" class="flat" id="chat-link">Chat</a><br/><br/>
<a href="/html/settings.html" class="flat" id="settings-link">Settings </a>
<script type="module" src="actions.js"></script>
</body>
Expand Down
95 changes: 45 additions & 50 deletions plugin/html/actions.js
Original file line number Diff line number Diff line change
@@ -1,50 +1,45 @@
import { promptVersion } from './globals.js';

const addAction = (name, prompt, actionsContainer) => {
const actionDiv = document.createElement("div");
const nameInput = document.createElement("p");
nameInput.classList.add("flat");
nameInput.innerText = name;
nameInput.classList.add("action-name");

const getHighlightedText = () => {
return browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => {
return browser.tabs.sendMessage(tabs[0].id, { command: "getSelectedText" });
});
};

nameInput.onclick = () => {
getHighlightedText().then((highlightedText) => {
if (highlightedText) {
rewrite(highlightedText, prompt, name);
}
});
};

actionDiv.appendChild(nameInput);
actionsContainer.appendChild(actionDiv);
};

const rewrite = async (original, action, draftTitle) => {
await browser.storage.local.set({ original, action, draftTitle });
browser.windows.create({ url: "/html/draft.html", type: "popup", width: 512, height: 600 });
};

document.addEventListener("DOMContentLoaded", () => {
const actionsContainer = document.getElementById("actions-container");
browser.storage.local.get(["actions", "promptUpdated"], (data) => {
const { actions, promptUpdated = 0 } = data;

if (promptVersion > promptUpdated) {
const warningIcon = document.createElement('img');
warningIcon.src = "/images/warning.png";
warningIcon.classList.add('small-icon');
const settingsLink = document.getElementById('settings-link');
settingsLink.appendChild(warningIcon);
};

actions.forEach((action) => {
addAction(action.name, action.prompt, actionsContainer);
});
});
});
import { promptVersion } from './globals.js';

const addAction = (name, prompt, actionsContainer) => {
const actionDiv = document.createElement("div");
const nameInput = document.createElement("p");
nameInput.classList.add("flat");
nameInput.innerText = name;
nameInput.classList.add("action-name");

const getHighlightedText = () => {
return browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => {
return browser.tabs.sendMessage(tabs[0].id, { command: "getSelectedText" });
});
};

nameInput.onclick = () => {
getHighlightedText().then((highlightedText) => {
if (highlightedText) {
const messages = [{role: "system", content: prompt},
{role: "user", content: highlightedText}];
browser.storage.local.set({ messages, draftTitle: name }).then( () => {
browser.windows.create({ url: "/html/draft.html", type: "popup",
width: 600, height: 512 });
});
}
});
};
actionDiv.appendChild(nameInput);
actionsContainer.appendChild(actionDiv);
};

document.addEventListener("DOMContentLoaded", () => {
const actionsContainer = document.getElementById("actions-container");
browser.storage.local.get(["actions", "promptUpdated"], (data) => {
const { actions, promptUpdated = 0 } = data;
if (promptVersion > promptUpdated) {
const warningIcon = document.createElement('img');
warningIcon.src = "/images/warning.png";
warningIcon.classList.add('small-icon');
const settingsLink = document.getElementById('settings-link');
settingsLink.appendChild(warningIcon);
};
actions.forEach((action) => {addAction(action.name, action.prompt, actionsContainer)});
});
});
50 changes: 50 additions & 0 deletions plugin/html/chat.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
}

.chat-container {
height: 100vh;
display: flex;
flex-direction: column;
}

.messages {
flex-grow: 1;
overflow-y: auto;
padding: 10px;
border-bottom: 1px solid #ccc;
}

.chat-input-area {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
height: 15%;
}

#message-input {
flex-grow: 1;
margin-right: 10px;
height: 80%;
}

.system-message {
background-color:hotpink;
}

.user-message {
background-color: blanchedalmond;
}

.assistant-message {
background-color: cadetblue;
}

.message {
padding: 10px;
margin: 5px;
border-radius: 4px;
}
21 changes: 21 additions & 0 deletions plugin/html/chat.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<title>Aify Chat</title>
<link rel="stylesheet" type="text/css" media="screen" href="chat.css">
<link rel="stylesheet" type="text/css" media="screen" href="styles.css">
</head>
<body>
<div class="chat-container">
<div id="messages" class="messages">
<!-- Chat messages will be added here dynamically -->
</div>
<div class="chat-input-area">
<textarea id="message-input" placeholder="Type your message here..."></textarea>
<button id="send-button" class="button good">Send</button>
<!-- <button id="clear-button" class="button bad">Clear</button> -->
</div>
</div>
<script type="module" src="chat.js"></script>
</body>
</html>
73 changes: 73 additions & 0 deletions plugin/html/chat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { fetchResponse } from './API.js';
import { chatPrompt } from './globals.js';

document.addEventListener('DOMContentLoaded', async function () {
var messageInput = document.getElementById('message-input');
var sendButton = document.getElementById('send-button');
// const clearButton = document.getElementById('clear-button');
var messagesContainer = document.getElementById('messages');
var loadingIcon = document.createElement('img');

loadingIcon.src = '/images/loading.png';
loadingIcon.classList.add('rotate');

var storage = await browser.storage.local.get(["messages"]);
var messages = storage.messages || [{role: "system", content: chatPrompt}];

messages.forEach(function(message) {
displayMessage(message.role, message.content);
});

//clearButton.addEventListener('click', async () => {messages = []; await browser.storage.local.set({messages}); window.close()})
sendButton.addEventListener('click', async function () {
var messageText = messageInput.value.trim();

if (messageText) {
messages.push({role: 'user', content: messageText});
displayMessage('user', messageText);

sendButton.disabled = true;
messagesContainer.appendChild(loadingIcon);

try {
const { apiKey, model, maxTokens } = await browser.storage.local.get(
["apiKey","model", "maxTokens"]);
const response = await fetchResponse(apiKey, model, messages, maxTokens);
// messagesContainer.innerText = response;
// Remove loading icon and enable button when API call finishes
loadingIcon.parentElement.removeChild(loadingIcon);
sendButton.disabled = false;

if (response) {
// Display the response from the model
displayMessage('assistant', response);
messages.push({role: 'assistant', content: response});

// Save messages to local storage
browser.storage.local.set({messages});
}
} catch(error) {
console.error(error);
messagesContainer.textContent = "Error: Unable to retrieve data";
}

messageInput.value = '';
}
});

function displayMessage(role, text) {
var messageDiv = document.createElement('div');
messageDiv.classList.add(role + '-message');
messageDiv.classList.add('message');
messageDiv.innerText = text;
messagesContainer.appendChild(messageDiv);

// Scroll to bottom
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}
});

window.addEventListener('unload', async function (event) {
await browser.storage.local.set({ messages: "[]", draftTitle: "" });
});

1 change: 1 addition & 0 deletions plugin/html/draft.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

</main>
<div class="btns">
<button id="chat-button" class="button neutral">Convert to Chat</button><br/>
<button id="regenerate" value="Regenerate" class="button bad">Regenerate</button><br/>
</div>
</body>
Expand Down
76 changes: 39 additions & 37 deletions plugin/html/draft.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,39 @@
import { fetchModels, fetchResponse } from './API.js';

async function regenerate(draftContainer, original, action, draftTitle) {
const loadingIcon = document.createElement("img");
loadingIcon.src = "/images/loading.png";
loadingIcon.classList.add("rotate");

draftContainer.textContent = "";
draftContainer.appendChild(loadingIcon);

try {
const draft = await callBackendAPI(original, action);
draftContainer.innerText = draft;
document.title = draftTitle;
} catch (error) {
console.error(error);
draftContainer.textContent = "Error: Unable to retrieve data";
}
}

async function callBackendAPI(original, action) {
const { model, apiKey, maxTokens } = await browser.storage.local.get(["model", "apiKey", "maxTokens"]);
const response = await fetchResponse(apiKey, model, original, action, maxTokens);
return response;
}

document.addEventListener("DOMContentLoaded", async () => {
const regenButton = document.getElementById('regenerate');
const draftContainer = document.getElementById("draft-container");

const { original, action, draftTitle } = await browser.storage.local.get(["original", "action", "draftTitle"]);

await browser.storage.local.set({ original: "", action: "", draftTitle: "" });

regenButton.addEventListener("click", () => regenerate(draftContainer, original, action, draftTitle));
regenerate(draftContainer, original, action, draftTitle);
});
import { fetchResponse } from './API.js';

async function generate(draftContainer, messages, draftTitle) {
const loadingIcon = document.createElement("img");
loadingIcon.src = "/images/loading.png";
loadingIcon.classList.add("rotate");

draftContainer.textContent = "";
draftContainer.appendChild(loadingIcon);

try {
const { apiKey, model, maxTokens } = await browser.storage.local.get(
["apiKey","model", "maxTokens"]);
const response = await fetchResponse(apiKey, model, messages, maxTokens);
draftContainer.innerText = response;
messages.push({role: "assistant", content: response});
document.title = draftTitle;
await browser.storage.local.set({messages});
} catch (error) {
console.error(error);
draftContainer.textContent = "Error: Unable to retrieve data";
}
}

document.addEventListener("DOMContentLoaded", async () => {
const regenButton = document.getElementById('regenerate');
const chatButton = document.getElementById('chat-button');
const draftContainer = document.getElementById("draft-container");
const { messages, draftTitle } = await browser.storage.local.get(["messages", "draftTitle"]);
regenButton.addEventListener("click", () => generate(draftContainer, messages, draftTitle));
chatButton.addEventListener("click", async () => {
await browser.storage.local.set({messages});
await browser.windows.create({ url: "/html/chat.html", type: "popup",
width: 600, height: 600 });
window.close();
})
generate(draftContainer, messages, draftTitle);
await browser.storage.local.set({ messages: "[]", draftTitle: "" });
});
1 change: 1 addition & 0 deletions plugin/html/globals.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export const defaultActions = [
{ name: "Translate this", prompt: "Translate the following email in English." },
{ name: "Prompt provided", prompt: "You are a helpful chatbot that will do their best to complete the following tasks with a single response." },
];
export const chatPrompt = "You are a helpful chatbot, answer any questions the user has";
export const defaultModel = "gpt-3.5-turbo";
Loading

0 comments on commit 4ece286

Please sign in to comment.