fully functional
additional clean up k quick nit k push changes general organization k k v2 push nits k v3 nit
@@ -1,49 +1,42 @@
|
||||
# Onyx Chrome Extension
|
||||
|
||||
This is a minimal Chrome extension for Onyx (formerly Danswer) that provides a default home screen and basic interactions with the Onyx platform.
|
||||
A minimal Chrome extension for Onyx (formerly Danswer) providing:
|
||||
|
||||
## Features
|
||||
|
||||
- Embedded iframe to Onyx chat (default: http://localhost:3000/nrf)
|
||||
- Configurable domain for local or remote instances
|
||||
- Hotkey to open a right panel for quick access
|
||||
- Default home screen
|
||||
- Basic interactions with Onyx platform
|
||||
- Embedded iframe to Onyx chat
|
||||
- Configurable domain for local/remote instances
|
||||
- Hotkey for quick access panel
|
||||
|
||||
## Installation
|
||||
|
||||
1. Clone this repository or download the source code.
|
||||
2. Open Google Chrome and navigate to `chrome://extensions`.
|
||||
3. Enable "Developer mode" in the top right corner.
|
||||
4. Click "Load unpacked" and select the `onyx-extension` folder.
|
||||
- Clone repo or download source
|
||||
- Navigate to `chrome://extensions`
|
||||
- Enable "Developer mode"
|
||||
- Load unpacked extension
|
||||
|
||||
## Usage
|
||||
|
||||
- Click the extension icon in the Chrome toolbar to open the Onyx home page.
|
||||
- Use `Ctrl+Shift+P` (or `Cmd+Shift+P` on Mac) to open the Onyx panel.
|
||||
- Access the options page to configure the Onyx domain:
|
||||
1. Right-click the extension icon
|
||||
2. Select "Options"
|
||||
3. Enter your desired Onyx domain (e.g., http://localhost:3000/nrf)
|
||||
4. Click "Save"
|
||||
- Click extension icon for Onyx home page
|
||||
- Use hotkey for Onyx panel
|
||||
- Configure domain in options page
|
||||
|
||||
## Development
|
||||
|
||||
To make changes to the extension:
|
||||
|
||||
1. Modify the relevant files in the `src` directory.
|
||||
2. If you make changes, go to `chrome://extensions/` and click the refresh icon for the Onyx extension.
|
||||
- Modify files in `src` directory
|
||||
- Refresh extension in Chrome
|
||||
|
||||
## File Structure
|
||||
|
||||
- `manifest.json`: Extension configuration
|
||||
- `src/pages/onyx_home.html` & `onyx_home.js`: Main extension popup
|
||||
- `src/pages/options.html` & `options.js`: Options page for domain configuration
|
||||
- `src/panel/panel.html` & `panel.js`: Side panel UI
|
||||
- `src/service_worker.js`: Background script for handling commands
|
||||
- `manifest.json`: Configuration
|
||||
- `src/pages/`: Main popup and options
|
||||
- `src/panel/`: Side panel UI
|
||||
- `src/service_worker.js`: Background script
|
||||
|
||||
## Contributing
|
||||
|
||||
Feel free to submit issues or pull requests for any bugs or improvements.
|
||||
Submit issues or pull requests for improvements
|
||||
|
||||
## License
|
||||
|
||||
[Add your chosen license here]
|
||||
MIT
|
||||
|
||||
@@ -1,49 +1,47 @@
|
||||
{
|
||||
"name": "Onyx Extension",
|
||||
"version": "0.1",
|
||||
"manifest_version": 3,
|
||||
"description": "Minimal Onyx (Danswer) extension to embed or open the Onyx homepage and panel.",
|
||||
"permissions": ["storage", "sidePanel", "contextMenus", "activeTab", "tabs"],
|
||||
"host_permissions": ["http://localhost:3000/*"],
|
||||
"icons": {
|
||||
"16": "icons/icon16.png",
|
||||
"48": "icons/icon48.png",
|
||||
"128": "icons/icon128.png"
|
||||
},
|
||||
"options_ui": {
|
||||
"page": "src/pages/options.html",
|
||||
"open_in_tab": true
|
||||
"name": "Onyx Extension",
|
||||
"version": "1.0",
|
||||
"description": "Onyx integration for Chrome",
|
||||
"permissions": [
|
||||
"sidePanel",
|
||||
"storage",
|
||||
"activeTab",
|
||||
"scripting",
|
||||
"tabs",
|
||||
"cookies"
|
||||
],
|
||||
"host_permissions": ["<all_urls>"],
|
||||
"background": {
|
||||
"service_worker": "service_worker.js",
|
||||
"type": "module"
|
||||
},
|
||||
"action": {
|
||||
"default_title": "Onyx Home",
|
||||
"default_icon": {
|
||||
"16": "icons/icon16.png",
|
||||
"48": "icons/icon48.png",
|
||||
"128": "icons/icon128.png"
|
||||
"16": "public/icon16.png",
|
||||
"48": "public/icon48.png",
|
||||
"128": "public/icon128.png"
|
||||
}
|
||||
},
|
||||
"background": {
|
||||
"service_worker": "src/service_worker.js"
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"js": ["src/content/content.js"]
|
||||
}
|
||||
],
|
||||
"side_panel": {
|
||||
"default_path": "src/panel/panel.html"
|
||||
"icons": {
|
||||
"16": "public/icon16.png",
|
||||
"48": "public/icon48.png",
|
||||
"128": "public/icon128.png"
|
||||
},
|
||||
"options_page": "src/pages/options.html",
|
||||
"chrome_url_overrides": {
|
||||
"newtab": "src/pages/onyx_home.html"
|
||||
},
|
||||
"commands": {
|
||||
"send-to-onyx": {
|
||||
"toggle-new-tab-override": {
|
||||
"suggested_key": {
|
||||
"default": "Ctrl+Shift+P",
|
||||
"mac": "Command+Shift+P"
|
||||
"default": "Ctrl+Shift+O",
|
||||
"mac": "Command+Shift+O"
|
||||
},
|
||||
"description": "Send selected text to Onyx"
|
||||
"description": "Toggle Onyx New Tab Override"
|
||||
}
|
||||
},
|
||||
"side_panel": {
|
||||
"default_path": "src/pages/panel.html"
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 235 B After Width: | Height: | Size: 235 B |
|
Before Width: | Height: | Size: 551 B After Width: | Height: | Size: 551 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
@@ -0,0 +1,164 @@
|
||||
import {
|
||||
DEFAULT_ONYX_DOMAIN,
|
||||
CHROME_SPECIFIC_STORAGE_KEYS,
|
||||
ACTIONS,
|
||||
} from "./src/utils/constants.js";
|
||||
|
||||
async function setupSidePanel() {
|
||||
if (chrome.sidePanel) {
|
||||
try {
|
||||
await chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true });
|
||||
} catch (error) {
|
||||
console.error("Error setting up side panel:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function openSidePanel(tabId) {
|
||||
try {
|
||||
await chrome.sidePanel.open({ tabId });
|
||||
} catch (error) {
|
||||
console.error("Error opening side panel:", error);
|
||||
}
|
||||
}
|
||||
async function closeSidePanel() {
|
||||
try {
|
||||
await chrome.sidePanel.setOptions({ enabled: false });
|
||||
} catch (error) {
|
||||
console.error("Error closing side panel:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function sendToOnyx(info, tab) {
|
||||
const selectedText = encodeURIComponent(info.selectionText);
|
||||
const currentUrl = encodeURIComponent(tab.url);
|
||||
|
||||
try {
|
||||
const result = await chrome.storage.local.get({
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]: DEFAULT_ONYX_DOMAIN,
|
||||
});
|
||||
const url = `${
|
||||
result[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]
|
||||
}/chat?input=${selectedText}&url=${currentUrl}`;
|
||||
|
||||
await openSidePanel(tab.id);
|
||||
chrome.runtime.sendMessage({
|
||||
action: ACTIONS.OPEN_ONYX_WITH_INPUT,
|
||||
url: url,
|
||||
pageUrl: tab.url,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error sending to Onyx:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleNewTabOverride() {
|
||||
try {
|
||||
const result = await chrome.storage.local.get(
|
||||
CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB
|
||||
);
|
||||
const newValue =
|
||||
!result[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB];
|
||||
await chrome.storage.local.set({
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB]: newValue,
|
||||
});
|
||||
|
||||
chrome.notifications.create({
|
||||
type: "basic",
|
||||
iconUrl: "icon.png",
|
||||
title: "Onyx New Tab",
|
||||
message: `New Tab Override ${newValue ? "enabled" : "disabled"}`,
|
||||
});
|
||||
|
||||
// Send a message to inform all tabs about the change
|
||||
chrome.tabs.query({}, (tabs) => {
|
||||
tabs.forEach((tab) => {
|
||||
chrome.tabs.sendMessage(tab.id, {
|
||||
action: "newTabOverrideToggled",
|
||||
value: newValue,
|
||||
});
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error toggling new tab override:", error);
|
||||
}
|
||||
}
|
||||
|
||||
chrome.action.onClicked.addListener((tab) => {
|
||||
openSidePanel(tab.id);
|
||||
});
|
||||
|
||||
chrome.commands.onCommand.addListener(async (command) => {
|
||||
console.log("command", command);
|
||||
if (command === ACTIONS.SEND_TO_ONYX) {
|
||||
try {
|
||||
const [tab] = await chrome.tabs.query({
|
||||
active: true,
|
||||
lastFocusedWindow: true,
|
||||
});
|
||||
if (tab) {
|
||||
const response = await chrome.tabs.sendMessage(tab.id, {
|
||||
action: ACTIONS.GET_SELECTED_TEXT,
|
||||
});
|
||||
const selectedText = response?.selectedText || "";
|
||||
sendToOnyx({ selectionText: selectedText }, tab);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error sending to Onyx:", error);
|
||||
}
|
||||
} else if (command === ACTIONS.TOGGLE_NEW_TAB_OVERRIDE) {
|
||||
toggleNewTabOverride();
|
||||
} else if (command === ACTIONS.CLOSE_SIDE_PANEL) {
|
||||
try {
|
||||
await chrome.sidePanel.hide();
|
||||
console.log("Side panel closed via command");
|
||||
} catch (error) {
|
||||
console.error("Error closing side panel via command:", error);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
if (request.action === ACTIONS.GET_CURRENT_ONYX_DOMAIN) {
|
||||
chrome.storage.local.get(
|
||||
{ [CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]: DEFAULT_ONYX_DOMAIN },
|
||||
(result) => {
|
||||
sendResponse({
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]:
|
||||
result[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN],
|
||||
});
|
||||
}
|
||||
);
|
||||
return true;
|
||||
}
|
||||
if (request.action === ACTIONS.CLOSE_SIDE_PANEL) {
|
||||
closeSidePanel();
|
||||
chrome.storage.local.get(
|
||||
{ [CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]: DEFAULT_ONYX_DOMAIN },
|
||||
(result) => {
|
||||
chrome.tabs.create({
|
||||
url: `${result[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]}/auth/login`,
|
||||
active: true,
|
||||
});
|
||||
}
|
||||
);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
chrome.storage.onChanged.addListener((changes, namespace) => {
|
||||
if (
|
||||
namespace === "local" &&
|
||||
changes[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB]
|
||||
) {
|
||||
const newValue =
|
||||
changes[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB]
|
||||
.newValue;
|
||||
|
||||
if (newValue === false) {
|
||||
chrome.runtime.openOptionsPage();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setupSidePanel();
|
||||
@@ -0,0 +1,20 @@
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// Fetch user settings for the theme and background URLs
|
||||
chrome.storage.local.get(
|
||||
["onyxTheme", "darkBgUrl", "lightBgUrl"],
|
||||
(items) => {
|
||||
const { onyxTheme, darkBgUrl, lightBgUrl } = items;
|
||||
|
||||
// Decide which background to use based on the stored theme
|
||||
if (onyxTheme === "dark" && darkBgUrl) {
|
||||
document.getElementById(
|
||||
"background"
|
||||
).style.backgroundImage = `url("${darkBgUrl}")`;
|
||||
} else if (onyxTheme === "light" && lightBgUrl) {
|
||||
document.getElementById(
|
||||
"background"
|
||||
).style.backgroundImage = `url("${lightBgUrl}")`;
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -1,8 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Onyx Home</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Onyx New Tab</title>
|
||||
<link rel="stylesheet" href="../styles/shared.css">
|
||||
<style>
|
||||
body, html {
|
||||
margin: 0;
|
||||
@@ -11,19 +13,52 @@
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html, body {
|
||||
background-color: #000;
|
||||
}
|
||||
}
|
||||
@media (prefers-color-scheme: light) {
|
||||
html, body {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
}
|
||||
#background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
transition: opacity 0.5s ease-in-out;
|
||||
}
|
||||
#content {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s ease-in-out;
|
||||
}
|
||||
iframe {
|
||||
border: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<iframe
|
||||
id="onyx-iframe"
|
||||
allow="fullscreen"
|
||||
allowfullscreen>
|
||||
</iframe>
|
||||
<script src="onyx_home.js"></script>
|
||||
<div id="background"></div>
|
||||
<div id="content">
|
||||
<iframe id="onyx-iframe" allowfullscreen></iframe>
|
||||
</div>
|
||||
<script src="../utils/error-modal.js" type="module"></script>
|
||||
<script src="background_loader.js" type="module"></script>
|
||||
<script src="onyx_home.js" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -1,13 +1,248 @@
|
||||
(function() {
|
||||
const iframe = document.getElementById('onyx-iframe');
|
||||
|
||||
// For now, we're using a static URL. In the future, this could be made configurable.
|
||||
const defaultUrl = 'http://localhost:3000/nrf';
|
||||
|
||||
// Set the iframe src
|
||||
iframe.src = defaultUrl;
|
||||
|
||||
// In the future, you might want to add functionality here to dynamically update the iframe src
|
||||
// based on user settings stored in chrome.storage.sync
|
||||
})();
|
||||
|
||||
import {
|
||||
CHROME_MESSAGE,
|
||||
CHROME_SPECIFIC_STORAGE_KEYS,
|
||||
WEB_MESSAGE,
|
||||
} from "../utils/constants.js";
|
||||
import {
|
||||
showErrorModal,
|
||||
hideErrorModal,
|
||||
initErrorModal,
|
||||
} from "../utils/error-modal.js";
|
||||
import { getOnyxDomain } from "../utils/storage.js";
|
||||
|
||||
(function () {
|
||||
let mainIframe = document.getElementById("onyx-iframe");
|
||||
let preloadedIframe = null;
|
||||
const background = document.getElementById("background");
|
||||
const content = document.getElementById("content");
|
||||
const DEFAULT_LIGHT_BACKGROUND_IMAGE =
|
||||
"https://images.unsplash.com/photo-1692520883599-d543cfe6d43d?q=80&w=2666&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D";
|
||||
const DEFAULT_DARK_BACKGROUND_IMAGE =
|
||||
"https://images.unsplash.com/photo-1692520883599-d543cfe6d43d?q=80&w=2666&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D";
|
||||
|
||||
let iframeLoadTimeout;
|
||||
let iframeLoaded = false;
|
||||
|
||||
initErrorModal();
|
||||
|
||||
async function preloadChatInterface() {
|
||||
preloadedIframe = document.createElement("iframe");
|
||||
|
||||
const domain = await getOnyxDomain();
|
||||
preloadedIframe.src = domain + "/chat";
|
||||
preloadedIframe.style.opacity = "0";
|
||||
preloadedIframe.style.visibility = "hidden";
|
||||
preloadedIframe.style.transition = "opacity 0.3s ease-in";
|
||||
preloadedIframe.style.border = "none";
|
||||
preloadedIframe.style.width = "100%";
|
||||
preloadedIframe.style.height = "100%";
|
||||
preloadedIframe.style.position = "absolute";
|
||||
preloadedIframe.style.top = "0";
|
||||
preloadedIframe.style.left = "0";
|
||||
preloadedIframe.style.zIndex = "1";
|
||||
content.appendChild(preloadedIframe);
|
||||
}
|
||||
|
||||
function setIframeSrc(url) {
|
||||
mainIframe.src = url;
|
||||
startIframeLoadTimeout();
|
||||
iframeLoaded = false;
|
||||
}
|
||||
|
||||
function startIframeLoadTimeout() {
|
||||
clearTimeout(iframeLoadTimeout);
|
||||
iframeLoadTimeout = setTimeout(() => {
|
||||
if (!iframeLoaded) {
|
||||
try {
|
||||
if (
|
||||
mainIframe.contentWindow.location.pathname.includes("/auth/login")
|
||||
) {
|
||||
showLoginPage();
|
||||
} else {
|
||||
showErrorModal(mainIframe.src);
|
||||
}
|
||||
} catch (error) {
|
||||
showErrorModal(mainIframe.src);
|
||||
}
|
||||
}
|
||||
}, 2500);
|
||||
}
|
||||
|
||||
function showLoginPage() {
|
||||
background.style.opacity = "0";
|
||||
mainIframe.style.opacity = "1";
|
||||
mainIframe.style.visibility = "visible";
|
||||
content.style.opacity = "1";
|
||||
hideErrorModal();
|
||||
}
|
||||
|
||||
function setTheme(theme, customBackgroundImage) {
|
||||
const imageUrl =
|
||||
customBackgroundImage ||
|
||||
(theme === "dark"
|
||||
? DEFAULT_DARK_BACKGROUND_IMAGE
|
||||
: DEFAULT_LIGHT_BACKGROUND_IMAGE);
|
||||
background.style.backgroundImage = `url('${imageUrl}')`;
|
||||
}
|
||||
|
||||
function fadeInContent() {
|
||||
content.style.transition = "opacity 0.5s ease-in";
|
||||
mainIframe.style.transition = "opacity 0.5s ease-in";
|
||||
content.style.opacity = "0";
|
||||
mainIframe.style.opacity = "0";
|
||||
mainIframe.style.visibility = "visible";
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
content.style.opacity = "1";
|
||||
mainIframe.style.opacity = "1";
|
||||
|
||||
setTimeout(() => {
|
||||
background.style.transition = "opacity 0.3s ease-out";
|
||||
background.style.opacity = "0";
|
||||
}, 500);
|
||||
});
|
||||
}
|
||||
|
||||
function checkOnyxPreference() {
|
||||
chrome.storage.local.get(
|
||||
[
|
||||
CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB,
|
||||
CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN,
|
||||
],
|
||||
(items) => {
|
||||
let useOnyxAsDefaultNewTab =
|
||||
items[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB];
|
||||
|
||||
if (useOnyxAsDefaultNewTab === undefined) {
|
||||
useOnyxAsDefaultNewTab = !!(
|
||||
localStorage.getItem(
|
||||
CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB
|
||||
) === "1"
|
||||
);
|
||||
chrome.storage.local.set({
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB]:
|
||||
useOnyxAsDefaultNewTab,
|
||||
});
|
||||
}
|
||||
|
||||
if (!useOnyxAsDefaultNewTab) {
|
||||
chrome.tabs.update({
|
||||
url: "chrome://new-tab-page",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setIframeSrc(
|
||||
items[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN] + "/chat/nrf"
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function loadThemeAndBackground() {
|
||||
chrome.storage.local.get(
|
||||
[
|
||||
CHROME_SPECIFIC_STORAGE_KEYS.THEME,
|
||||
CHROME_SPECIFIC_STORAGE_KEYS.BACKGROUND_IMAGE,
|
||||
CHROME_SPECIFIC_STORAGE_KEYS.DARK_BG_URL,
|
||||
CHROME_SPECIFIC_STORAGE_KEYS.LIGHT_BG_URL,
|
||||
],
|
||||
function (result) {
|
||||
const theme = result[CHROME_SPECIFIC_STORAGE_KEYS.THEME] || "light";
|
||||
const customBackgroundImage =
|
||||
result[CHROME_SPECIFIC_STORAGE_KEYS.BACKGROUND_IMAGE];
|
||||
const darkBgUrl = result[CHROME_SPECIFIC_STORAGE_KEYS.DARK_BG_URL];
|
||||
const lightBgUrl = result[CHROME_SPECIFIC_STORAGE_KEYS.LIGHT_BG_URL];
|
||||
|
||||
let backgroundImage;
|
||||
if (customBackgroundImage) {
|
||||
backgroundImage = customBackgroundImage;
|
||||
} else if (theme === "dark" && darkBgUrl) {
|
||||
backgroundImage = darkBgUrl;
|
||||
} else if (theme === "light" && lightBgUrl) {
|
||||
backgroundImage = lightBgUrl;
|
||||
}
|
||||
|
||||
setTheme(theme, backgroundImage);
|
||||
checkOnyxPreference();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function loadNewPage(newSrc) {
|
||||
if (preloadedIframe && preloadedIframe.contentWindow) {
|
||||
preloadedIframe.contentWindow.postMessage(
|
||||
{ type: WEB_MESSAGE.PAGE_CHANGE, href: newSrc },
|
||||
"*"
|
||||
);
|
||||
} else {
|
||||
console.error("Preloaded iframe not available");
|
||||
}
|
||||
}
|
||||
|
||||
function completePendingPageLoad() {
|
||||
if (preloadedIframe) {
|
||||
preloadedIframe.style.visibility = "visible";
|
||||
preloadedIframe.style.opacity = "1";
|
||||
preloadedIframe.style.zIndex = "1";
|
||||
mainIframe.style.zIndex = "2";
|
||||
mainIframe.style.opacity = "0";
|
||||
|
||||
setTimeout(() => {
|
||||
if (content.contains(mainIframe)) {
|
||||
content.removeChild(mainIframe);
|
||||
}
|
||||
|
||||
mainIframe = preloadedIframe;
|
||||
mainIframe.id = "onyx-iframe";
|
||||
mainIframe.style.zIndex = "";
|
||||
iframeLoaded = true;
|
||||
clearTimeout(iframeLoadTimeout);
|
||||
}, 200);
|
||||
} else {
|
||||
console.warn("No preloaded iframe available");
|
||||
}
|
||||
}
|
||||
|
||||
chrome.storage.onChanged.addListener(function (changes, namespace) {
|
||||
if (namespace === "local" && changes.useOnyxAsDefaultNewTab) {
|
||||
checkOnyxPreference();
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("message", function (event) {
|
||||
if (event.data.type === CHROME_MESSAGE.SET_DEFAULT_NEW_TAB) {
|
||||
chrome.storage.local.set({ useOnyxAsDefaultNewTab: event.data.value });
|
||||
} else if (event.data.type === CHROME_MESSAGE.ONYX_APP_LOADED) {
|
||||
clearTimeout(iframeLoadTimeout);
|
||||
hideErrorModal();
|
||||
fadeInContent();
|
||||
iframeLoaded = true;
|
||||
} else if (event.data.type === CHROME_MESSAGE.PREFERENCES_UPDATED) {
|
||||
const { theme, backgroundUrl } = event.data.payload;
|
||||
chrome.storage.local.set(
|
||||
{
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.THEME]: theme,
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.BACKGROUND_IMAGE]: backgroundUrl,
|
||||
},
|
||||
() => {}
|
||||
);
|
||||
} else if (event.data.type === CHROME_MESSAGE.LOAD_NEW_PAGE) {
|
||||
loadNewPage(event.data.href);
|
||||
} else if (event.data.type === CHROME_MESSAGE.LOAD_NEW_CHAT_PAGE) {
|
||||
completePendingPageLoad();
|
||||
}
|
||||
});
|
||||
|
||||
mainIframe.onload = function () {
|
||||
clearTimeout(iframeLoadTimeout);
|
||||
startIframeLoadTimeout();
|
||||
};
|
||||
|
||||
mainIframe.onerror = function (error) {
|
||||
showErrorModal(mainIframe.src);
|
||||
};
|
||||
|
||||
loadThemeAndBackground();
|
||||
preloadChatInterface();
|
||||
})();
|
||||
|
||||
@@ -1,33 +1,45 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Onyx Extension Options</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Onyx Extension Settings</title>
|
||||
<link rel="stylesheet" href="../styles/shared.css">
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
padding: 20px;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
input[type="text"] {
|
||||
width: 300px;
|
||||
padding: 5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
button {
|
||||
padding: 5px 10px;
|
||||
background-color: var(--background-color);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Onyx Extension Settings</h1>
|
||||
<label for="onyxDomain">Onyx Domain:</label>
|
||||
<input type="text" id="onyxDomain" placeholder="http://localhost:3000/nrf" />
|
||||
<button id="save">Save</button>
|
||||
<p id="status"></p>
|
||||
<script src="options.js"></script>
|
||||
<div class="container">
|
||||
<div class="card">
|
||||
<h1>Onyx Extension Settings</h1>
|
||||
<div class="option-group">
|
||||
<label for="onyxDomain">Root Domain</label>
|
||||
<input type="text" id="onyxDomain" placeholder="https://cloud.onyx.app">
|
||||
</div>
|
||||
<div class="option-group">
|
||||
<label for="useOnyxAsDefault" class="toggle-label">
|
||||
Use Onyx as default new tab
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="useOnyxAsDefault">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</label>
|
||||
</div>
|
||||
<button id="save" class="button primary">Save</button>
|
||||
<div id="statusContainer" class="status-container" style="display: none;">
|
||||
<p id="status" class="status-message"></p>
|
||||
<button id="newTab" class="button secondary">Open New Tab to Test</button>
|
||||
</div>
|
||||
<p class="shortcut-info">Tip: Use <kbd><span id="shortcut-key"></span></kbd> to quickly toggle the New Tab Override.</p>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="options.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -1,20 +1,102 @@
|
||||
(function() {
|
||||
const domainInput = document.getElementById('onyxDomain');
|
||||
const saveButton = document.getElementById('save');
|
||||
const statusElement = document.getElementById('status');
|
||||
import {
|
||||
CHROME_SPECIFIC_STORAGE_KEYS,
|
||||
DEFAULT_ONYX_DOMAIN,
|
||||
} from "../utils/constants.js";
|
||||
|
||||
// Load existing value from storage
|
||||
chrome.storage.sync.get({ onyxDomain: 'http://localhost:3000/nrf' }, (result) => {
|
||||
domainInput.value = result.onyxDomain;
|
||||
});
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const domainInput = document.getElementById("onyxDomain");
|
||||
const useOnyxAsDefaultToggle = document.getElementById("useOnyxAsDefault");
|
||||
const saveButton = document.getElementById("save");
|
||||
const statusContainer = document.getElementById("statusContainer");
|
||||
const statusElement = document.getElementById("status");
|
||||
const shortcutKeySpan = document.getElementById("shortcut-key");
|
||||
const newTabButton = document.getElementById("newTab");
|
||||
|
||||
saveButton.addEventListener('click', () => {
|
||||
function setShortcutKey() {
|
||||
if (shortcutKeySpan) {
|
||||
shortcutKeySpan.textContent =
|
||||
navigator.platform.indexOf("Mac") === 0 ? "⌘+Shift+O" : "Ctrl+Shift+O";
|
||||
}
|
||||
}
|
||||
|
||||
function loadStoredValues() {
|
||||
chrome.storage.local.get(
|
||||
{
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]: DEFAULT_ONYX_DOMAIN,
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB]: false,
|
||||
},
|
||||
(result) => {
|
||||
if (domainInput)
|
||||
domainInput.value = result[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN];
|
||||
if (useOnyxAsDefaultToggle)
|
||||
useOnyxAsDefaultToggle.checked =
|
||||
result[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB];
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function saveSettings() {
|
||||
const domain = domainInput.value.trim();
|
||||
chrome.storage.sync.set({ onyxDomain: domain || 'http://localhost:3000/nrf' }, () => {
|
||||
statusElement.textContent = 'Domain saved!';
|
||||
setTimeout(() => {
|
||||
statusElement.textContent = '';
|
||||
}, 3000);
|
||||
});
|
||||
});
|
||||
})();
|
||||
const useOnyxAsDefault = useOnyxAsDefaultToggle
|
||||
? useOnyxAsDefaultToggle.checked
|
||||
: false;
|
||||
console.log(domain, useOnyxAsDefault);
|
||||
chrome.storage.local.set(
|
||||
{
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]: domain,
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB]:
|
||||
useOnyxAsDefault,
|
||||
},
|
||||
showStatusMessage
|
||||
);
|
||||
}
|
||||
|
||||
function showStatusMessage() {
|
||||
if (statusElement) {
|
||||
const useOnyxAsDefault = useOnyxAsDefaultToggle
|
||||
? useOnyxAsDefaultToggle.checked
|
||||
: false;
|
||||
if (useOnyxAsDefault) {
|
||||
statusElement.textContent =
|
||||
"Settings updated. Open a new tab to test it out. Click on the extension icon to bring up Onyx from any page.";
|
||||
if (newTabButton) newTabButton.style.display = "block";
|
||||
} else {
|
||||
statusElement.textContent = "Settings updated.";
|
||||
if (newTabButton) newTabButton.style.display = "none";
|
||||
}
|
||||
statusElement.style.color = "black";
|
||||
}
|
||||
if (statusContainer) {
|
||||
statusContainer.style.display = "block";
|
||||
statusContainer.style.opacity = "1";
|
||||
}
|
||||
if (statusElement) statusElement.style.opacity = "1";
|
||||
if (newTabButton) newTabButton.style.opacity = "1";
|
||||
|
||||
setTimeout(hideStatusMessage, 5000);
|
||||
}
|
||||
|
||||
function hideStatusMessage() {
|
||||
if (statusContainer) statusContainer.style.opacity = "0";
|
||||
if (statusElement) statusElement.style.opacity = "0";
|
||||
if (newTabButton) newTabButton.style.opacity = "0";
|
||||
setTimeout(() => {
|
||||
if (statusContainer) statusContainer.style.display = "none";
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function openNewTab() {
|
||||
chrome.tabs.create({});
|
||||
}
|
||||
|
||||
setShortcutKey();
|
||||
loadStoredValues();
|
||||
|
||||
if (saveButton) {
|
||||
saveButton.addEventListener("click", saveSettings);
|
||||
}
|
||||
|
||||
if (newTabButton) {
|
||||
newTabButton.addEventListener("click", openNewTab);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Onyx Side Panel</title>
|
||||
<link rel="stylesheet" href="../styles/shared.css">
|
||||
<style>
|
||||
body, html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
#loading-screen {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #f5f5f5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
transition: opacity 0.5s ease-in-out;
|
||||
}
|
||||
#logo {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-image: url('/public/logo.png');
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.1); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
#loading-text {
|
||||
color: #0a0a0a;
|
||||
margin-top: 20px;
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
iframe {
|
||||
border: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="loading-screen">
|
||||
<div id="logo"></div>
|
||||
<div id="loading-text">Loading Onyx...</div>
|
||||
</div>
|
||||
<iframe id="onyx-panel-iframe"></iframe>
|
||||
<script src="../utils/error-modal.js" type="module"></script>
|
||||
<script src="panel.js" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,106 @@
|
||||
import { showErrorModal, showAuthModal } from "../utils/error-modal.js";
|
||||
import {
|
||||
ACTIONS,
|
||||
CHROME_MESSAGE,
|
||||
WEB_MESSAGE,
|
||||
CHROME_SPECIFIC_STORAGE_KEYS,
|
||||
} from "../utils/constants.js";
|
||||
(function () {
|
||||
const iframe = document.getElementById("onyx-panel-iframe");
|
||||
const loadingScreen = document.getElementById("loading-screen");
|
||||
|
||||
let currentUrl = "";
|
||||
let iframeLoaded = false;
|
||||
let iframeLoadTimeout;
|
||||
let authRequired = false;
|
||||
|
||||
function initializePanel() {
|
||||
loadingScreen.style.display = "flex";
|
||||
loadingScreen.style.opacity = "1";
|
||||
iframe.style.opacity = "0";
|
||||
loadOnyxDomain();
|
||||
}
|
||||
|
||||
function setIframeSrc(url, pageUrl) {
|
||||
iframe.src = url;
|
||||
currentUrl = pageUrl;
|
||||
}
|
||||
|
||||
function sendWebsiteToIframe(pageUrl) {
|
||||
if (iframe.contentWindow && pageUrl !== currentUrl) {
|
||||
iframe.contentWindow.postMessage(
|
||||
{
|
||||
type: WEB_MESSAGE.PAGE_CHANGE,
|
||||
url: pageUrl,
|
||||
},
|
||||
"*"
|
||||
);
|
||||
currentUrl = pageUrl;
|
||||
}
|
||||
}
|
||||
|
||||
function startIframeLoadTimeout() {
|
||||
iframeLoadTimeout = setTimeout(() => {
|
||||
if (!iframeLoaded) {
|
||||
if (authRequired) {
|
||||
showAuthModal();
|
||||
} else {
|
||||
showErrorModal(iframe.src);
|
||||
}
|
||||
}
|
||||
}, 2500);
|
||||
}
|
||||
|
||||
function handleMessage(event) {
|
||||
if (event.data.type === CHROME_MESSAGE.ONYX_APP_LOADED) {
|
||||
clearTimeout(iframeLoadTimeout);
|
||||
iframeLoaded = true;
|
||||
showIframe();
|
||||
if (iframe.contentWindow) {
|
||||
iframe.contentWindow.postMessage({ type: "PANEL_READY" }, "*");
|
||||
}
|
||||
} else if (event.data.type === CHROME_MESSAGE.AUTH_REQUIRED) {
|
||||
authRequired = true;
|
||||
}
|
||||
}
|
||||
|
||||
function showIframe() {
|
||||
iframe.style.opacity = "1";
|
||||
loadingScreen.style.opacity = "0";
|
||||
setTimeout(() => {
|
||||
loadingScreen.style.display = "none";
|
||||
}, 500);
|
||||
}
|
||||
|
||||
async function loadOnyxDomain() {
|
||||
const response = await chrome.runtime.sendMessage({
|
||||
action: ACTIONS.GET_CURRENT_ONYX_DOMAIN,
|
||||
});
|
||||
console.log("CURRENT DOMAIn");
|
||||
console.log(response);
|
||||
if (response && response[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]) {
|
||||
setIframeSrc(
|
||||
response[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN] +
|
||||
"/chat?defaultSidebarOff=true",
|
||||
""
|
||||
);
|
||||
} else {
|
||||
console.warn("Onyx domain not found, using default");
|
||||
const domain = await getOnyxDomain();
|
||||
setIframeSrc(domain + "/chat?defaultSidebarOff=true", "");
|
||||
}
|
||||
}
|
||||
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
if (request.action === "openOnyxWithInput") {
|
||||
setIframeSrc(request.url, request.pageUrl);
|
||||
} else if (request.action === ACTIONS.UPDATE_PAGE_URL) {
|
||||
sendWebsiteToIframe(request.pageUrl);
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("message", handleMessage);
|
||||
|
||||
initializePanel();
|
||||
startIframeLoadTimeout();
|
||||
})();
|
||||
@@ -1,25 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Onyx Side Panel</title>
|
||||
<style>
|
||||
body, html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
iframe {
|
||||
border: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<iframe id="onyx-panel-iframe"></iframe>
|
||||
<script src="panel.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,49 +0,0 @@
|
||||
(function() {
|
||||
const iframe = document.getElementById('onyx-panel-iframe');
|
||||
|
||||
function setIframeSrc(url, pageUrl) {
|
||||
console.log('Setting iframe src to:', url);
|
||||
iframe.src = url;
|
||||
sendWebsiteToIframe(pageUrl);
|
||||
}
|
||||
|
||||
function sendWebsiteToIframe(pageUrl) {
|
||||
if (iframe.contentWindow) {
|
||||
iframe.contentWindow.postMessage(
|
||||
{
|
||||
type: "PAGE_URL",
|
||||
url: pageUrl,
|
||||
},
|
||||
"*"
|
||||
);
|
||||
console.log('Sent PAGE_URL message to iframe:', pageUrl);
|
||||
} else {
|
||||
console.error('iframe.contentWindow not available');
|
||||
}
|
||||
}
|
||||
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
console.log('Panel received message:', request);
|
||||
if (request.action === "openOnyxWithInput") {
|
||||
setIframeSrc(request.url, request.pageUrl);
|
||||
} else if (request.action === "updatePageUrl") {
|
||||
sendWebsiteToIframe(request.pageUrl);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Panel loaded');
|
||||
chrome.runtime.sendMessage({action: "getCurrentOnyxDomain"}, function(response) {
|
||||
if (response && response.onyxDomain) {
|
||||
console.log('Onyx domain:', response.onyxDomain);
|
||||
setIframeSrc(response.onyxDomain + '/chat', '');
|
||||
} else {
|
||||
console.log('No Onyx domain found');
|
||||
console.error('Failed to get Onyx domain');
|
||||
setIframeSrc('http://localhost:3000/chat', '');
|
||||
}
|
||||
});
|
||||
|
||||
iframe.onerror = function() {
|
||||
console.error('Failed to load iframe');
|
||||
};
|
||||
})();
|
||||
@@ -1,96 +0,0 @@
|
||||
chrome.sidePanel
|
||||
.setPanelBehavior({ openPanelOnActionClick: true })
|
||||
.catch((error) => console.error(error));
|
||||
|
||||
chrome.action.onClicked.addListener((tab) => {
|
||||
chrome.sidePanel.open({tabId: tab.id});
|
||||
});
|
||||
|
||||
chrome.runtime.onInstalled.addListener(() => {
|
||||
console.log('Onyx Extension installed');
|
||||
chrome.contextMenus.create({
|
||||
id: "sendToOnyx",
|
||||
title: "Send to Onyx",
|
||||
contexts: ["selection"]
|
||||
});
|
||||
});
|
||||
|
||||
function sendToOnyx(info, tab) {
|
||||
const selectedText = encodeURIComponent(info.selectionText);
|
||||
const currentUrl = encodeURIComponent(tab.url);
|
||||
chrome.storage.sync.get({ onyxDomain: 'http://localhost:3000' }, (result) => {
|
||||
const url = `${result.onyxDomain}/chat?input=${selectedText}&url=${currentUrl}`;
|
||||
chrome.sidePanel.open({tabId: tab.id}).then(() => {
|
||||
chrome.runtime.sendMessage({
|
||||
action: "openOnyxWithInput",
|
||||
url: url,
|
||||
pageUrl: tab.url
|
||||
});
|
||||
});
|
||||
console.log("sendToOnyx called with url:", url);
|
||||
});
|
||||
}
|
||||
|
||||
chrome.contextMenus.onClicked.addListener((info, tab) => {
|
||||
if (info.menuItemId === "sendToOnyx") {
|
||||
sendToOnyx(info, tab);
|
||||
}
|
||||
});
|
||||
|
||||
chrome.commands.onCommand.addListener((command) => {
|
||||
if (command === "send-to-onyx") {
|
||||
console.log("send-to-onyx command received");
|
||||
chrome.tabs.query({active: true, lastFocusedWindow: true}, ([tab]) => {
|
||||
if (tab) {
|
||||
chrome.tabs.sendMessage(tab.id, {action: "getSelectedText"}, (response) => {
|
||||
if (response && response.selectedText) {
|
||||
console.log("Selected text received");
|
||||
sendToOnyx({selectionText: response.selectedText}, tab);
|
||||
} else {
|
||||
console.log("No selected text found");
|
||||
sendToOnyx({selectionText: ""}, tab);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.error("No active tab found");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function sendPageUrlMessage() {
|
||||
chrome.tabs.query({active: true, lastFocusedWindow: true}, ([tab]) => {
|
||||
if (tab) {
|
||||
console.log("Sending updatePageUrl message with URL:", tab.url);
|
||||
chrome.runtime.sendMessage({
|
||||
action: "updatePageUrl",
|
||||
pageUrl: tab.url
|
||||
});
|
||||
} else {
|
||||
console.error("No active tab found");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
chrome.tabs.onActivated.addListener(() => {
|
||||
console.log("onActivated called");
|
||||
sendPageUrlMessage();
|
||||
});
|
||||
|
||||
chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
|
||||
if (changeInfo.status === 'complete') {
|
||||
console.log("onUpdated called");
|
||||
sendPageUrlMessage();
|
||||
}
|
||||
});
|
||||
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
console.log('Received message:', request);
|
||||
if (request.action === "getCurrentOnyxDomain") {
|
||||
chrome.storage.sync.get({ onyxDomain: 'http://localhost:3000' }, (result) => {
|
||||
console.log('Sending Onyx domain:', result.onyxDomain);
|
||||
sendResponse({ onyxDomain: result.onyxDomain });
|
||||
});
|
||||
return true; // Indicates that the response is asynchronous
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,173 @@
|
||||
:root {
|
||||
--primary-color: #4285f4;
|
||||
--primary-hover-color: #3367d6;
|
||||
--secondary-color: #f1f3f4;
|
||||
--secondary-hover-color: #e8eaed;
|
||||
--text-color: #333;
|
||||
--text-light-color: #666;
|
||||
--background-color: #f1f3f4;
|
||||
--card-background-color: #fff;
|
||||
--border-color: #ccc;
|
||||
--font-family: Arial, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-family);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 500px;
|
||||
width: 90%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: var(--card-background-color);
|
||||
padding: 25px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: var(--text-color);
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
margin-top: 0;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.option-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
color: var(--text-light-color);
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
background-color: var(--card-background-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.button {
|
||||
width: 100%;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.button.primary {
|
||||
background-color: var(--primary-color);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.button.primary:hover {
|
||||
background-color: var(--primary-hover-color);
|
||||
}
|
||||
|
||||
.button.secondary {
|
||||
background-color: var(--secondary-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.button.secondary:hover {
|
||||
background-color: var(--secondary-hover-color);
|
||||
}
|
||||
|
||||
.status-container {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.status-message {
|
||||
margin: 0 0 10px 0;
|
||||
color: var(--text-color);
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
transition: opacity 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.shortcut-info {
|
||||
margin-top: 15px;
|
||||
font-size: 14px;
|
||||
color: var(--text-light-color);
|
||||
text-align: center;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
kbd {
|
||||
background-color: var(--secondary-color);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 3px;
|
||||
padding: 2px 5px;
|
||||
font-family: monospace;
|
||||
font-weight: 500;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.toggle-label {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.toggle-switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 50px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.toggle-switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: var(--secondary-color);
|
||||
transition: .4s;
|
||||
border-radius: 24px;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
left: 2px;
|
||||
bottom: 2px;
|
||||
background-color: white;
|
||||
transition: .4s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
background-color: var(--primary-color);
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
transform: translateX(26px);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
export const THEMES = {
|
||||
LIGHT: "light",
|
||||
DARK: "dark",
|
||||
};
|
||||
|
||||
export const DEFAULT_ONYX_DOMAIN = "http://localhost:3000";
|
||||
|
||||
// Keyboard shortcuts
|
||||
export const KEYBOARD_SHORTCUTS = {
|
||||
TOGGLE_NEW_TAB_OVERRIDE: "Ctrl+Shift+O",
|
||||
OPEN_SIDE_PANEL: "Ctrl+Shift+P",
|
||||
};
|
||||
|
||||
// Actions to send to the service worker
|
||||
export const ACTIONS = {
|
||||
GET_SELECTED_TEXT: "getSelectedText",
|
||||
GET_CURRENT_ONYX_DOMAIN: "getCurrentOnyxDomain",
|
||||
UPDATE_PAGE_URL: "updatePageUrl",
|
||||
SEND_TO_ONYX: "sendToOnyx",
|
||||
CLOSE_SIDE_PANEL: "closeSidePanel",
|
||||
};
|
||||
|
||||
// Chrome-extension specific storage keys
|
||||
export const CHROME_SPECIFIC_STORAGE_KEYS = {
|
||||
ONYX_DOMAIN: "onyxExtensionDomain",
|
||||
USE_ONYX_AS_DEFAULT_NEW_TAB: "onyxExtensionDefaultNewTab",
|
||||
THEME: "onyxExtensionTheme",
|
||||
BACKGROUND_IMAGE: "onyxExtensionBackgroundImage",
|
||||
DARK_BG_URL: "onyxExtensionDarkBgUrl",
|
||||
LIGHT_BG_URL: "onyxExtensionLightBgUrl",
|
||||
};
|
||||
|
||||
// Messages sent from the iframe to the extension
|
||||
export const CHROME_MESSAGE = {
|
||||
PREFERENCES_UPDATED: "PREFERENCES_UPDATED",
|
||||
ONYX_APP_LOADED: "ONYX_APP_LOADED",
|
||||
SET_DEFAULT_NEW_TAB: "SET_DEFAULT_NEW_TAB",
|
||||
LOAD_NEW_CHAT_PAGE: "LOAD_NEW_CHAT_PAGE",
|
||||
LOAD_NEW_PAGE: "LOAD_NEW_PAGE",
|
||||
AUTH_REQUIRED: "AUTH_REQUIRED",
|
||||
};
|
||||
|
||||
// Messages to send to the iframe
|
||||
export const WEB_MESSAGE = {
|
||||
PAGE_CHANGE: "PAGE_CHANGE",
|
||||
};
|
||||
@@ -1,8 +1,8 @@
|
||||
let sidePanel = null;
|
||||
|
||||
function createSidePanel() {
|
||||
sidePanel = document.createElement('div');
|
||||
sidePanel.id = 'onyx-side-panel';
|
||||
sidePanel = document.createElement("div");
|
||||
sidePanel.id = "onyx-side-panel";
|
||||
sidePanel.style.cssText = `
|
||||
position: fixed;
|
||||
top: 0;
|
||||
@@ -15,40 +15,31 @@ function createSidePanel() {
|
||||
z-index: 9999;
|
||||
`;
|
||||
|
||||
const iframe = document.createElement('iframe');
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.style.cssText = `
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
`;
|
||||
|
||||
chrome.runtime.sendMessage({action: "getCurrentOnyxDomain"}, function(response) {
|
||||
iframe.src = response.onyxDomain;
|
||||
});
|
||||
chrome.runtime.sendMessage(
|
||||
{ action: ACTIONS.GET_CURRENT_ONYX_DOMAIN },
|
||||
function (response) {
|
||||
iframe.src = response[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN];
|
||||
}
|
||||
);
|
||||
|
||||
sidePanel.appendChild(iframe);
|
||||
document.body.appendChild(sidePanel);
|
||||
}
|
||||
|
||||
function toggleSidePanel() {
|
||||
if (!sidePanel) {
|
||||
createSidePanel();
|
||||
}
|
||||
|
||||
if (sidePanel.style.right === '0px') {
|
||||
sidePanel.style.right = '-400px';
|
||||
} else {
|
||||
sidePanel.style.right = '0px';
|
||||
}
|
||||
}
|
||||
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
console.log('Content script received message:', request);
|
||||
console.log("Content script received message:", request);
|
||||
if (request.action === "openOnyxWithInput") {
|
||||
chrome.runtime.sendMessage({action: "openSidePanel", url: request.url});
|
||||
chrome.runtime.sendMessage({ action: "openSidePanel", url: request.url });
|
||||
}
|
||||
if (request.action === "getSelectedText") {
|
||||
const selectedText = window.getSelection().toString();
|
||||
sendResponse({selectedText: selectedText});
|
||||
sendResponse({ selectedText: selectedText });
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,260 @@
|
||||
import {
|
||||
CHROME_SPECIFIC_STORAGE_KEYS,
|
||||
DEFAULT_ONYX_DOMAIN,
|
||||
ACTIONS,
|
||||
} from "./constants.js";
|
||||
|
||||
// Create and append error modal HTML
|
||||
const errorModalHTML = `
|
||||
<div id="error-modal">
|
||||
<div class="modal-content">
|
||||
<h2>Onyx Configuration Error</h2>
|
||||
<p>The Onyx configuration needs to be updated. Please check your settings or contact your Onyx administrator.</p>
|
||||
<p>Attempted to load: <span id="attempted-url"></span></p>
|
||||
<div class="button-container">
|
||||
<button id="open-options" class="button primary">Open Extension Options</button>
|
||||
<button id="disable-override" class="button secondary">Disable New Tab Override</button>
|
||||
</div>
|
||||
<p class="shortcut-info">Tip: Use <kbd><span id="shortcut-key"></span></kbd> to quickly toggle the New Tab Override.</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Add styles for the error modal
|
||||
const style = document.createElement("style");
|
||||
style.textContent = `
|
||||
#error-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 2000;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
#error-modal .modal-content {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
max-width: 95%;
|
||||
width: 500px;
|
||||
text-align: center;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
#error-modal h2 {
|
||||
margin-top: 0;
|
||||
color: #333;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
#error-modal p {
|
||||
color: #666;
|
||||
margin-bottom: 15px;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
}
|
||||
#error-modal #attempted-url {
|
||||
word-break: break-all;
|
||||
}
|
||||
#error-modal .button-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
#error-modal .button {
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
#error-modal .button.primary {
|
||||
background-color: #4285f4;
|
||||
color: #fff;
|
||||
}
|
||||
#error-modal .button.primary:hover {
|
||||
background-color: #3367d6;
|
||||
}
|
||||
#error-modal .button.secondary {
|
||||
background-color: #f1f3f4;
|
||||
color: #3c4043;
|
||||
}
|
||||
#error-modal .button.secondary:hover {
|
||||
background-color: #e8eaed;
|
||||
}
|
||||
#error-modal .shortcut-info {
|
||||
margin-top: 15px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
font-weight: 400;
|
||||
}
|
||||
#error-modal kbd {
|
||||
background-color: #f1f3f4;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
padding: 2px 5px;
|
||||
font-family: monospace;
|
||||
font-weight: 500;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
#error-modal .button-container {
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const authModalHTML = `
|
||||
<div id="error-modal">
|
||||
<div class="modal-content">
|
||||
<h2>Onyx Authentication Required</h2>
|
||||
<p>You need to log in to access Onyx. Click the button below to authenticate.</p>
|
||||
<div class="button-container">
|
||||
<button id="open-auth" class="button primary">Log In to Onyx</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
let errorModal,
|
||||
attemptedUrlSpan,
|
||||
openOptionsButton,
|
||||
disableOverrideButton,
|
||||
shortcutKeySpan;
|
||||
|
||||
let authModal, openAuthButton;
|
||||
|
||||
export function initErrorModal() {
|
||||
if (!document.getElementById("error-modal")) {
|
||||
document.body.insertAdjacentHTML("beforeend", errorModalHTML);
|
||||
document.head.appendChild(style);
|
||||
|
||||
errorModal = document.getElementById("error-modal");
|
||||
authModal = document.getElementById("error-modal");
|
||||
attemptedUrlSpan = document.getElementById("attempted-url");
|
||||
openOptionsButton = document.getElementById("open-options");
|
||||
disableOverrideButton = document.getElementById("disable-override");
|
||||
shortcutKeySpan = document.getElementById("shortcut-key");
|
||||
|
||||
openOptionsButton.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
chrome.runtime.openOptionsPage();
|
||||
});
|
||||
|
||||
disableOverrideButton.addEventListener("click", () => {
|
||||
chrome.storage.local.set({ useOnyxAsDefaultNewTab: false }, () => {
|
||||
chrome.tabs.update({ url: "chrome://new-tab-page" });
|
||||
});
|
||||
});
|
||||
|
||||
shortcutKeySpan.textContent =
|
||||
navigator.platform.indexOf("Mac") === 0 ? "⌘+Shift+O" : "Ctrl+Shift+O";
|
||||
}
|
||||
}
|
||||
|
||||
export function showErrorModal(url) {
|
||||
if (!errorModal) {
|
||||
initErrorModal();
|
||||
}
|
||||
if (errorModal) {
|
||||
errorModal.style.display = "flex";
|
||||
errorModal.style.zIndex = "9999";
|
||||
errorModal.style.opacity = "1";
|
||||
attemptedUrlSpan.textContent = url;
|
||||
}
|
||||
}
|
||||
|
||||
export function hideErrorModal() {
|
||||
if (errorModal) {
|
||||
errorModal.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
export function checkModalVisibility() {
|
||||
return errorModal
|
||||
? window.getComputedStyle(errorModal).display !== "none"
|
||||
: false;
|
||||
}
|
||||
|
||||
export function initAuthModal() {
|
||||
if (!document.getElementById("error-modal")) {
|
||||
document.body.insertAdjacentHTML("beforeend", authModalHTML);
|
||||
document.head.appendChild(style);
|
||||
|
||||
authModal = document.getElementById("error-modal");
|
||||
openAuthButton = document.getElementById("open-auth");
|
||||
|
||||
openAuthButton.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
chrome.storage.local.get(
|
||||
{ [CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]: DEFAULT_ONYX_DOMAIN },
|
||||
(result) => {
|
||||
const onyxDomain = result[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN];
|
||||
console.log("sendinga message", ACTIONS.CLOSE_SIDE_PANEL);
|
||||
chrome.runtime.sendMessage(
|
||||
{ action: ACTIONS.CLOSE_SIDE_PANEL },
|
||||
() => {
|
||||
if (chrome.runtime.lastError) {
|
||||
console.error(
|
||||
"Error closing side panel:",
|
||||
chrome.runtime.lastError
|
||||
);
|
||||
}
|
||||
// Open the auth window after attempting to close the side panel
|
||||
chrome.tabs.create(
|
||||
{
|
||||
url: `${onyxDomain}/auth/login`,
|
||||
active: true,
|
||||
},
|
||||
(tab) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
console.error(
|
||||
"Error opening auth tab:",
|
||||
chrome.runtime.lastError
|
||||
);
|
||||
} else {
|
||||
console.log("Auth tab opened successfully:", tab.id);
|
||||
// Optionally, you can focus the newly created tab
|
||||
chrome.tabs.update(tab.id, { active: true });
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function showAuthModal() {
|
||||
if (!authModal) {
|
||||
initAuthModal();
|
||||
}
|
||||
if (authModal) {
|
||||
authModal.style.display = "flex";
|
||||
authModal.style.zIndex = "9999";
|
||||
authModal.style.opacity = "1";
|
||||
|
||||
document.body.style.overflow = "hidden";
|
||||
authModal.style.position = "fixed";
|
||||
authModal.style.top = "0";
|
||||
authModal.style.left = "0";
|
||||
authModal.style.width = "100%";
|
||||
authModal.style.height = "100%";
|
||||
authModal.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
|
||||
}
|
||||
}
|
||||
|
||||
export function hideAuthModal() {
|
||||
if (authModal) {
|
||||
authModal.style.display = "none";
|
||||
document.body.style.overflow = "auto";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import {
|
||||
DEFAULT_ONYX_DOMAIN,
|
||||
CHROME_SPECIFIC_STORAGE_KEYS,
|
||||
} from "./constants.js";
|
||||
|
||||
export async function getOnyxDomain() {
|
||||
const result = await chrome.storage.local.get({
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]: DEFAULT_ONYX_DOMAIN,
|
||||
});
|
||||
return result[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN];
|
||||
}
|
||||
|
||||
export function setOnyxDomain(domain, callback) {
|
||||
chrome.storage.local.set(
|
||||
{ [CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]: domain },
|
||||
callback
|
||||
);
|
||||
}
|
||||
|
||||
export function getOnyxDomainSync() {
|
||||
return new Promise((resolve) => {
|
||||
getOnyxDomain(resolve);
|
||||
});
|
||||
}
|
||||