mirror of
https://github.com/onyx-dot-app/onyx.git
synced 2026-10-19 01:50:51 -05:00
feat(chrome-ext): managed policy for Onyx URL and new-tab toggle; strip trailing slashes (#15027)
This commit is contained in:
1 parent
436209ee5d
commit
b2b7172634
13 files changed
+485
-229
No files matched your search
@@ -19,12 +19,64 @@ Onyx connects with dozens of popular workplace apps like Google Drive, Jira, Con
|
||||
|
||||
For Onyx Cloud Users, please visit the Chrome Plugin Store (pending approval still)
|
||||
|
||||
## Enterprise configuration (managed policy)
|
||||
|
||||
Admins can pre-set and lock settings with Chrome's extension policy
|
||||
(`ExtensionSettings` / "Managed storage" in Google Admin, or the
|
||||
`3rdparty.extensions.<id>` policy on Windows/macOS/Linux). Settings set by
|
||||
policy are read-only in the extension UI. Supported keys (see
|
||||
`managed_schema.json`):
|
||||
|
||||
| Key | Type | Description |
|
||||
| ---------------------------- | ------- | ----------------------------------------------- |
|
||||
| `onyxExtensionDomain` | string | Root URL of your Onyx instance |
|
||||
| `onyxExtensionDefaultNewTab` | boolean | Force the "Use Onyx as new tab page" toggle |
|
||||
|
||||
Example (Linux, `/etc/opt/chrome/policies/managed/onyx.json`; the Chrome Web
|
||||
Store extension ID is `dacfbnglakogghooelgjflkhjcdbemia`):
|
||||
|
||||
```json
|
||||
{
|
||||
"3rdparty": {
|
||||
"extensions": {
|
||||
"dacfbnglakogghooelgjflkhjcdbemia": {
|
||||
"onyxExtensionDomain": "https://onyx.example.com",
|
||||
"onyxExtensionDefaultNewTab": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Trailing slashes on `onyxExtensionDomain` are stripped automatically.
|
||||
|
||||
## Development
|
||||
|
||||
- Load unpacked extension in your browser
|
||||
- Modify files in `src` directory
|
||||
- Refresh extension in Chrome
|
||||
|
||||
### Testing managed policy locally
|
||||
|
||||
Use the ID shown on the unpacked extension's card in `chrome://extensions`
|
||||
(it differs from the Web Store ID).
|
||||
|
||||
Linux: write the JSON above to `/etc/opt/chrome/policies/managed/onyx.json`.
|
||||
|
||||
macOS: Chrome only reads extension policy from managed preferences, so
|
||||
`defaults write` is not enough. Write the plist with `plutil`:
|
||||
|
||||
```sh
|
||||
P="/Library/Managed Preferences/$USER/com.google.Chrome.extensions.<id>.plist"
|
||||
sudo plutil -create xml1 "$P"
|
||||
sudo plutil -insert onyxExtensionDomain -string "https://onyx.example.com" "$P"
|
||||
sudo plutil -insert onyxExtensionDefaultNewTab -bool false "$P"
|
||||
sudo killall cfprefsd
|
||||
```
|
||||
|
||||
Then quit and relaunch Chrome, click "Reload policies" on `chrome://policy`,
|
||||
and reload the extension. Remove the file and restart Chrome to undo.
|
||||
|
||||
## Contributing
|
||||
|
||||
Submit issues or pull requests for improvements
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-03/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"onyxExtensionDomain": {
|
||||
"title": "Onyx root domain",
|
||||
"description": "Root URL of the Onyx instance (for example https://cloud.onyx.app). When set by policy, users cannot change it in the extension.",
|
||||
"type": "string"
|
||||
},
|
||||
"onyxExtensionDefaultNewTab": {
|
||||
"title": "Use Onyx as new tab page",
|
||||
"description": "Force the new tab override on (true) or off (false). When set by policy, users cannot change the toggle in the extension.",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,9 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Onyx",
|
||||
"version": "1.1",
|
||||
"version": "1.2",
|
||||
"description": "Onyx lets you research, create, and automate with LLMs powered by your team's unique knowledge",
|
||||
"permissions": [
|
||||
"sidePanel",
|
||||
"storage",
|
||||
"activeTab",
|
||||
"tabs"
|
||||
],
|
||||
"permissions": ["sidePanel", "storage", "activeTab", "tabs"],
|
||||
"host_permissions": ["<all_urls>"],
|
||||
"background": {
|
||||
"service_worker": "service_worker.js",
|
||||
@@ -28,6 +23,9 @@
|
||||
"128": "public/icon128.png"
|
||||
},
|
||||
"options_page": "src/pages/options.html",
|
||||
"storage": {
|
||||
"managed_schema": "managed_schema.json"
|
||||
},
|
||||
"chrome_url_overrides": {
|
||||
"newtab": "src/pages/onyx_home.html"
|
||||
},
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import {
|
||||
DEFAULT_ONYX_DOMAIN,
|
||||
CHROME_SPECIFIC_STORAGE_KEYS,
|
||||
ACTIONS,
|
||||
SIDE_PANEL_PATH,
|
||||
} from "./src/utils/constants.js";
|
||||
import {
|
||||
getOnyxDomain,
|
||||
getUseOnyxAsDefaultNewTab,
|
||||
setUseOnyxAsDefaultNewTab,
|
||||
isNewTabOverrideChange,
|
||||
} from "./src/utils/storage.js";
|
||||
|
||||
// Track side panel state per window
|
||||
const sidePanelOpenState = new Map();
|
||||
@@ -52,12 +57,8 @@ async function sendToOnyx(info, tab) {
|
||||
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]
|
||||
}${SIDE_PANEL_PATH}?user-prompt=${selectedText}`;
|
||||
const domain = await getOnyxDomain();
|
||||
const url = `${domain}${SIDE_PANEL_PATH}?user-prompt=${selectedText}`;
|
||||
|
||||
await openSidePanel(tab.id);
|
||||
chrome.runtime.sendMessage({
|
||||
@@ -72,14 +73,12 @@ async function sendToOnyx(info, tab) {
|
||||
|
||||
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,
|
||||
});
|
||||
const newValue = !(await getUseOnyxAsDefaultNewTab());
|
||||
const updated = await setUseOnyxAsDefaultNewTab(newValue);
|
||||
if (!updated) {
|
||||
console.warn("New tab override is managed by policy; ignoring toggle");
|
||||
return;
|
||||
}
|
||||
|
||||
chrome.notifications.create({
|
||||
type: "basic",
|
||||
@@ -176,28 +175,16 @@ async function sendActiveTabUrlToPanel() {
|
||||
|
||||
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],
|
||||
});
|
||||
}
|
||||
);
|
||||
getOnyxDomain().then((domain) => {
|
||||
sendResponse({ [CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]: 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,
|
||||
});
|
||||
}
|
||||
);
|
||||
getOnyxDomain().then((domain) => {
|
||||
chrome.tabs.create({ url: `${domain}/auth/login`, active: true });
|
||||
});
|
||||
return true;
|
||||
}
|
||||
if (request.action === ACTIONS.OPEN_SIDE_PANEL_WITH_INPUT) {
|
||||
@@ -206,38 +193,34 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
const windowId = sender.tab?.windowId;
|
||||
|
||||
if (tabId && windowId) {
|
||||
chrome.storage.local.get(
|
||||
{ [CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]: DEFAULT_ONYX_DOMAIN },
|
||||
(result) => {
|
||||
const encodedText = encodeUserPrompt(selectedText);
|
||||
const onyxDomain = result[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN];
|
||||
const url = `${onyxDomain}${SIDE_PANEL_PATH}?user-prompt=${encodedText}`;
|
||||
getOnyxDomain().then((onyxDomain) => {
|
||||
const encodedText = encodeUserPrompt(selectedText);
|
||||
const url = `${onyxDomain}${SIDE_PANEL_PATH}?user-prompt=${encodedText}`;
|
||||
|
||||
chrome.storage.session.set({
|
||||
pendingInput: {
|
||||
chrome.storage.session.set({
|
||||
pendingInput: {
|
||||
url: url,
|
||||
pageUrl: pageUrl,
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
});
|
||||
|
||||
chrome.sidePanel
|
||||
.open({ windowId })
|
||||
.then(() => {
|
||||
chrome.runtime.sendMessage({
|
||||
action: ACTIONS.OPEN_ONYX_WITH_INPUT,
|
||||
url: url,
|
||||
pageUrl: pageUrl,
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
});
|
||||
|
||||
chrome.sidePanel
|
||||
.open({ windowId })
|
||||
.then(() => {
|
||||
chrome.runtime.sendMessage({
|
||||
action: ACTIONS.OPEN_ONYX_WITH_INPUT,
|
||||
url: url,
|
||||
pageUrl: pageUrl,
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(
|
||||
"[Onyx SW] Error opening side panel with text:",
|
||||
error
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(
|
||||
"[Onyx SW] Error opening side panel with text:",
|
||||
error
|
||||
);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
console.error("[Onyx SW] Missing tabId or windowId");
|
||||
}
|
||||
@@ -255,15 +238,12 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
});
|
||||
|
||||
chrome.storage.onChanged.addListener((changes, namespace) => {
|
||||
if (
|
||||
namespace === "local" &&
|
||||
changes[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB]
|
||||
) {
|
||||
if (isNewTabOverrideChange(changes, namespace)) {
|
||||
const newValue =
|
||||
changes[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB]
|
||||
.newValue;
|
||||
|
||||
if (newValue === false) {
|
||||
if (namespace === "local" && newValue === false) {
|
||||
chrome.runtime.openOptionsPage();
|
||||
}
|
||||
}
|
||||
@@ -279,11 +259,7 @@ chrome.omnibox.setDefaultSuggestion({
|
||||
|
||||
chrome.omnibox.onInputEntered.addListener(async (text) => {
|
||||
try {
|
||||
const result = await chrome.storage.local.get({
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]: DEFAULT_ONYX_DOMAIN,
|
||||
});
|
||||
|
||||
const domain = result[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN];
|
||||
const domain = await getOnyxDomain();
|
||||
const searchUrl = `${domain}/chat?user-prompt=${encodeURIComponent(text)}`;
|
||||
|
||||
chrome.tabs.update({ url: searchUrl });
|
||||
|
||||
@@ -8,7 +8,12 @@ import {
|
||||
hideErrorModal,
|
||||
initErrorModal,
|
||||
} from "../utils/error-modal.js";
|
||||
import { getOnyxDomain } from "../utils/storage.js";
|
||||
import {
|
||||
getOnyxDomain,
|
||||
getUseOnyxAsDefaultNewTab,
|
||||
setUseOnyxAsDefaultNewTab,
|
||||
isNewTabOverrideChange,
|
||||
} from "../utils/storage.js";
|
||||
|
||||
(function () {
|
||||
let mainIframe = document.getElementById("onyx-iframe");
|
||||
@@ -109,38 +114,26 @@ import { getOnyxDomain } from "../utils/storage.js";
|
||||
});
|
||||
}
|
||||
|
||||
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];
|
||||
async function checkOnyxPreference() {
|
||||
let useOnyxAsDefaultNewTab = await getUseOnyxAsDefaultNewTab();
|
||||
|
||||
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 === undefined) {
|
||||
useOnyxAsDefaultNewTab =
|
||||
localStorage.getItem(
|
||||
CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB
|
||||
) === "1";
|
||||
await setUseOnyxAsDefaultNewTab(useOnyxAsDefaultNewTab);
|
||||
}
|
||||
|
||||
if (!useOnyxAsDefaultNewTab) {
|
||||
chrome.tabs.update({
|
||||
url: "chrome://new-tab-page",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!useOnyxAsDefaultNewTab) {
|
||||
chrome.tabs.update({
|
||||
url: "chrome://new-tab-page",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setIframeSrc(items[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN] + "/nrf");
|
||||
}
|
||||
);
|
||||
const domain = await getOnyxDomain();
|
||||
setIframeSrc(domain + "/nrf");
|
||||
}
|
||||
|
||||
function loadThemeAndBackground() {
|
||||
@@ -209,19 +202,29 @@ import { getOnyxDomain } from "../utils/storage.js";
|
||||
}
|
||||
|
||||
chrome.storage.onChanged.addListener(function (changes, namespace) {
|
||||
if (namespace === "local" && changes.useOnyxAsDefaultNewTab) {
|
||||
if (isNewTabOverrideChange(changes, namespace)) {
|
||||
checkOnyxPreference();
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("message", function (event) {
|
||||
// Only accept messages from one of our own Onyx iframes, and only when
|
||||
// the sender origin matches that iframe's src (fails closed).
|
||||
function isTrustedIframeMessage(event) {
|
||||
const frame = [mainIframe, preloadedIframe].find(
|
||||
(f) => f && f.contentWindow === event.source
|
||||
);
|
||||
if (!frame) return false;
|
||||
try {
|
||||
if (event.origin !== getOnyxOrigin(mainIframe)) return;
|
||||
return getOnyxOrigin(frame) === event.origin;
|
||||
} catch {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("message", function (event) {
|
||||
if (!isTrustedIframeMessage(event)) return;
|
||||
if (event.data.type === CHROME_MESSAGE.SET_DEFAULT_NEW_TAB) {
|
||||
chrome.storage.local.set({ useOnyxAsDefaultNewTab: event.data.value });
|
||||
setUseOnyxAsDefaultNewTab(event.data.value);
|
||||
} else if (event.data.type === CHROME_MESSAGE.ONYX_APP_LOADED) {
|
||||
clearTimeout(iframeLoadTimeout);
|
||||
hideErrorModal();
|
||||
|
||||
@@ -259,6 +259,19 @@
|
||||
color: var(--text-light-03);
|
||||
}
|
||||
|
||||
.managed-notice {
|
||||
font-size: 12px;
|
||||
color: var(--text-light-03);
|
||||
padding: 0 12px 12px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.input-field:disabled,
|
||||
.toggle-switch input:disabled + .toggle-slider {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.setting-divider {
|
||||
height: 1px;
|
||||
background: var(--white-10);
|
||||
@@ -460,6 +473,9 @@
|
||||
placeholder="https://cloud.onyx.app"
|
||||
/>
|
||||
</div>
|
||||
<div id="domainManagedNotice" class="managed-notice">
|
||||
This setting is managed by your organization.
|
||||
</div>
|
||||
<div class="setting-divider"></div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-row-content">
|
||||
@@ -472,6 +488,9 @@
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div id="newTabManagedNotice" class="managed-notice">
|
||||
This setting is managed by your organization.
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import { CHROME_SPECIFIC_STORAGE_KEYS } from "../utils/constants.js";
|
||||
import {
|
||||
CHROME_SPECIFIC_STORAGE_KEYS,
|
||||
DEFAULT_ONYX_DOMAIN,
|
||||
} from "../utils/constants.js";
|
||||
getOnyxDomain,
|
||||
getUseOnyxAsDefaultNewTab,
|
||||
getManagedSettings,
|
||||
normalizeOnyxDomain,
|
||||
} from "../utils/storage.js";
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const domainInput = document.getElementById("onyxDomain");
|
||||
const useOnyxAsDefaultToggle = document.getElementById("useOnyxAsDefault");
|
||||
const domainManagedNotice = document.getElementById("domainManagedNotice");
|
||||
const newTabManagedNotice = document.getElementById("newTabManagedNotice");
|
||||
const statusContainer = document.getElementById("statusContainer");
|
||||
const statusElement = document.getElementById("status");
|
||||
const newTabButton = document.getElementById("newTab");
|
||||
@@ -29,49 +34,70 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
let domainManaged = false;
|
||||
let newTabManaged = false;
|
||||
|
||||
async function loadStoredValues() {
|
||||
const managed = await getManagedSettings();
|
||||
domainManaged =
|
||||
typeof managed[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN] === "string";
|
||||
newTabManaged =
|
||||
typeof managed[
|
||||
CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB
|
||||
] === "boolean";
|
||||
|
||||
const [domain, useOnyxAsDefault, themeResult] = await Promise.all([
|
||||
getOnyxDomain(),
|
||||
getUseOnyxAsDefaultNewTab(),
|
||||
chrome.storage.local.get({
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.THEME]: "dark",
|
||||
},
|
||||
(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];
|
||||
}),
|
||||
]);
|
||||
|
||||
currentTheme = result[CHROME_SPECIFIC_STORAGE_KEYS.THEME] || "dark";
|
||||
updateThemeIcon(currentTheme);
|
||||
if (domainInput) {
|
||||
domainInput.value = domain;
|
||||
domainInput.disabled = domainManaged;
|
||||
}
|
||||
if (domainManagedNotice) {
|
||||
domainManagedNotice.style.display = domainManaged ? "block" : "none";
|
||||
}
|
||||
if (useOnyxAsDefaultToggle) {
|
||||
useOnyxAsDefaultToggle.checked = !!useOnyxAsDefault;
|
||||
useOnyxAsDefaultToggle.disabled = newTabManaged;
|
||||
}
|
||||
if (newTabManagedNotice) {
|
||||
newTabManagedNotice.style.display = newTabManaged ? "block" : "none";
|
||||
}
|
||||
|
||||
document.body.className = currentTheme === "light" ? "light-theme" : "";
|
||||
}
|
||||
);
|
||||
currentTheme = themeResult[CHROME_SPECIFIC_STORAGE_KEYS.THEME] || "dark";
|
||||
updateThemeIcon(currentTheme);
|
||||
|
||||
document.body.className = currentTheme === "light" ? "light-theme" : "";
|
||||
}
|
||||
|
||||
function saveSettings() {
|
||||
const domain = domainInput.value.trim();
|
||||
const useOnyxAsDefault = useOnyxAsDefaultToggle
|
||||
? useOnyxAsDefaultToggle.checked
|
||||
: false;
|
||||
|
||||
chrome.storage.local.set(
|
||||
{
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]: domain,
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB]:
|
||||
useOnyxAsDefault,
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.THEME]: currentTheme,
|
||||
},
|
||||
() => {
|
||||
showStatusMessage(
|
||||
useOnyxAsDefault
|
||||
? "Settings updated. Open a new tab to test it out. Click on the extension icon to bring up Onyx from any page."
|
||||
: "Settings updated."
|
||||
);
|
||||
}
|
||||
);
|
||||
const values = { [CHROME_SPECIFIC_STORAGE_KEYS.THEME]: currentTheme };
|
||||
if (!domainManaged) {
|
||||
values[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN] = normalizeOnyxDomain(
|
||||
domainInput.value
|
||||
);
|
||||
}
|
||||
if (!newTabManaged) {
|
||||
values[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB] =
|
||||
useOnyxAsDefault;
|
||||
}
|
||||
|
||||
chrome.storage.local.set(values, () => {
|
||||
showStatusMessage(
|
||||
useOnyxAsDefault
|
||||
? "Settings updated. Open a new tab to test it out. Click on the extension icon to bring up Onyx from any page."
|
||||
: "Settings updated."
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function showStatusMessage(message) {
|
||||
@@ -124,8 +150,17 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
clearTimeout(domainInput.saveTimeout);
|
||||
domainInput.saveTimeout = setTimeout(saveSettings, 1000);
|
||||
});
|
||||
domainInput.addEventListener("blur", () => {
|
||||
domainInput.value = normalizeOnyxDomain(domainInput.value);
|
||||
});
|
||||
}
|
||||
|
||||
chrome.storage.onChanged.addListener((changes, namespace) => {
|
||||
if (namespace === "managed") {
|
||||
loadStoredValues();
|
||||
}
|
||||
});
|
||||
|
||||
if (useOnyxAsDefaultToggle) {
|
||||
useOnyxAsDefaultToggle.addEventListener("change", saveSettings);
|
||||
}
|
||||
|
||||
@@ -197,6 +197,18 @@
|
||||
input:checked + .toggle-slider:before {
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
.toggle-switch input:disabled + .toggle-slider {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.managed-notice {
|
||||
font-size: 12px;
|
||||
color: var(--text-light-03);
|
||||
padding: 0 12px 12px;
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -218,6 +230,9 @@
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div id="newTabManagedNotice" class="managed-notice">
|
||||
Managed by your organization.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="button-group">
|
||||
|
||||
@@ -1,25 +1,34 @@
|
||||
import { CHROME_SPECIFIC_STORAGE_KEYS } from "../utils/constants.js";
|
||||
import {
|
||||
getUseOnyxAsDefaultNewTab,
|
||||
setUseOnyxAsDefaultNewTab,
|
||||
isNewTabOverrideManaged,
|
||||
} from "../utils/storage.js";
|
||||
|
||||
document.addEventListener("DOMContentLoaded", async function () {
|
||||
const defaultNewTabToggle = document.getElementById("defaultNewTabToggle");
|
||||
const openSidePanelButton = document.getElementById("openSidePanel");
|
||||
const openOptionsButton = document.getElementById("openOptions");
|
||||
const newTabManagedNotice = document.getElementById("newTabManagedNotice");
|
||||
|
||||
async function loadSetting() {
|
||||
const result = await chrome.storage.local.get({
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB]: false,
|
||||
});
|
||||
const [value, managed] = await Promise.all([
|
||||
getUseOnyxAsDefaultNewTab(),
|
||||
isNewTabOverrideManaged(),
|
||||
]);
|
||||
if (defaultNewTabToggle) {
|
||||
defaultNewTabToggle.checked =
|
||||
result[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB];
|
||||
defaultNewTabToggle.checked = !!value;
|
||||
defaultNewTabToggle.disabled = managed;
|
||||
}
|
||||
if (newTabManagedNotice) {
|
||||
newTabManagedNotice.style.display = managed ? "block" : "none";
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleSetting() {
|
||||
const currentValue = defaultNewTabToggle.checked;
|
||||
await chrome.storage.local.set({
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB]: currentValue,
|
||||
});
|
||||
const updated = await setUseOnyxAsDefaultNewTab(
|
||||
defaultNewTabToggle.checked
|
||||
);
|
||||
if (!updated) await loadSetting();
|
||||
}
|
||||
|
||||
async function openSidePanel() {
|
||||
|
||||
@@ -376,6 +376,19 @@
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.managed-notice {
|
||||
font-size: 13px;
|
||||
color: var(--text-light-03);
|
||||
margin-top: 8px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.input-field:disabled,
|
||||
.toggle-switch input:disabled + .toggle-slider {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.setting-divider {
|
||||
height: 1px;
|
||||
background: var(--white-10);
|
||||
@@ -573,6 +586,9 @@
|
||||
class="input-field"
|
||||
placeholder="https://cloud.onyx.app"
|
||||
/>
|
||||
<div id="domainManagedNotice" class="managed-notice">
|
||||
This setting is managed by your organization.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="button-group">
|
||||
@@ -602,6 +618,9 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="newTabManagedNotice" class="managed-notice">
|
||||
This setting is managed by your organization.
|
||||
</div>
|
||||
|
||||
<div class="button-group">
|
||||
<button class="btn btn-secondary" id="backBtn">Back</button>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {
|
||||
CHROME_SPECIFIC_STORAGE_KEYS,
|
||||
DEFAULT_ONYX_DOMAIN,
|
||||
} from "../utils/constants.js";
|
||||
import { CHROME_SPECIFIC_STORAGE_KEYS } from "../utils/constants.js";
|
||||
import { getManagedSettings, normalizeOnyxDomain } from "../utils/storage.js";
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const domainInput = document.getElementById("onyxDomain");
|
||||
const useOnyxAsDefaultToggle = document.getElementById("useOnyxAsDefault");
|
||||
const domainManagedNotice = document.getElementById("domainManagedNotice");
|
||||
const newTabManagedNotice = document.getElementById("newTabManagedNotice");
|
||||
const continueBtn = document.getElementById("continueBtn");
|
||||
const backBtn = document.getElementById("backBtn");
|
||||
const finishBtn = document.getElementById("finishBtn");
|
||||
@@ -18,6 +18,8 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
|
||||
let currentStep = 1;
|
||||
let currentTheme = "dark";
|
||||
let domainManaged = false;
|
||||
let newTabManaged = false;
|
||||
|
||||
// Initialize theme based on system preference or stored value
|
||||
function initTheme() {
|
||||
@@ -121,47 +123,79 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
}
|
||||
|
||||
function handleFinish() {
|
||||
const domain = domainInput.value.trim() || DEFAULT_ONYX_DOMAIN;
|
||||
const useOnyxAsDefault = useOnyxAsDefaultToggle.checked;
|
||||
|
||||
chrome.storage.local.set(
|
||||
{
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]: domain,
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB]:
|
||||
useOnyxAsDefault,
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.THEME]: currentTheme,
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.ONBOARDING_COMPLETE]: true,
|
||||
},
|
||||
() => {
|
||||
// Open a new tab if they enabled the new tab feature, otherwise just close
|
||||
if (useOnyxAsDefault) {
|
||||
chrome.tabs.create({}, () => {
|
||||
window.close();
|
||||
});
|
||||
} else {
|
||||
const values = {
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.THEME]: currentTheme,
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.ONBOARDING_COMPLETE]: true,
|
||||
};
|
||||
if (!domainManaged) {
|
||||
values[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN] = normalizeOnyxDomain(
|
||||
domainInput.value
|
||||
);
|
||||
}
|
||||
if (!newTabManaged) {
|
||||
values[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB] =
|
||||
useOnyxAsDefault;
|
||||
}
|
||||
|
||||
chrome.storage.local.set(values, () => {
|
||||
// Open a new tab if they enabled the new tab feature, otherwise just close
|
||||
if (useOnyxAsDefault) {
|
||||
chrome.tabs.create({}, () => {
|
||||
window.close();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
window.close();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Lock controls that are set by policy. Unmanaged controls keep whatever
|
||||
// the user has entered so far.
|
||||
async function applyManagedSettings() {
|
||||
const managed = await getManagedSettings();
|
||||
const managedDomain = managed[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN];
|
||||
const managedNewTab =
|
||||
managed[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB];
|
||||
domainManaged = typeof managedDomain === "string";
|
||||
newTabManaged = typeof managedNewTab === "boolean";
|
||||
|
||||
domainInput.disabled = domainManaged;
|
||||
domainManagedNotice.style.display = domainManaged ? "block" : "none";
|
||||
if (domainManaged) {
|
||||
domainInput.value = normalizeOnyxDomain(managedDomain);
|
||||
}
|
||||
|
||||
useOnyxAsDefaultToggle.disabled = newTabManaged;
|
||||
newTabManagedNotice.style.display = newTabManaged ? "block" : "none";
|
||||
if (newTabManaged) {
|
||||
useOnyxAsDefaultToggle.checked = managedNewTab;
|
||||
}
|
||||
}
|
||||
|
||||
// Load any existing values (in case user returns to this page)
|
||||
function loadStoredValues() {
|
||||
chrome.storage.local.get(
|
||||
{
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]: "",
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB]: true,
|
||||
},
|
||||
(result) => {
|
||||
if (result[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]) {
|
||||
domainInput.value = result[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN];
|
||||
}
|
||||
useOnyxAsDefaultToggle.checked =
|
||||
result[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB];
|
||||
}
|
||||
);
|
||||
async function loadStoredValues() {
|
||||
const result = await chrome.storage.local.get({
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]: "",
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB]: true,
|
||||
});
|
||||
|
||||
if (result[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]) {
|
||||
domainInput.value = result[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN];
|
||||
}
|
||||
useOnyxAsDefaultToggle.checked =
|
||||
result[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB];
|
||||
|
||||
await applyManagedSettings();
|
||||
}
|
||||
|
||||
chrome.storage.onChanged.addListener((changes, namespace) => {
|
||||
if (namespace === "managed") {
|
||||
applyManagedSettings();
|
||||
}
|
||||
});
|
||||
|
||||
if (themeToggle) {
|
||||
themeToggle.addEventListener("click", toggleTheme);
|
||||
}
|
||||
@@ -185,6 +219,11 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
handleContinue();
|
||||
}
|
||||
});
|
||||
domainInput.addEventListener("blur", () => {
|
||||
if (domainInput.value.trim()) {
|
||||
domainInput.value = normalizeOnyxDomain(domainInput.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initTheme();
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { ACTIONS } from "./constants.js";
|
||||
import {
|
||||
CHROME_SPECIFIC_STORAGE_KEYS,
|
||||
DEFAULT_ONYX_DOMAIN,
|
||||
ACTIONS,
|
||||
} from "./constants.js";
|
||||
getOnyxDomain,
|
||||
isNewTabOverrideManaged,
|
||||
setUseOnyxAsDefaultNewTab,
|
||||
} from "./storage.js";
|
||||
|
||||
const errorModalHTML = `
|
||||
<div id="error-modal">
|
||||
@@ -277,8 +278,16 @@ export function initErrorModal() {
|
||||
chrome.runtime.openOptionsPage();
|
||||
});
|
||||
|
||||
isNewTabOverrideManaged().then((managed) => {
|
||||
if (managed) disableOverrideButton.style.display = "none";
|
||||
});
|
||||
|
||||
disableOverrideButton.addEventListener("click", () => {
|
||||
chrome.storage.local.set({ useOnyxAsDefaultNewTab: false }, () => {
|
||||
setUseOnyxAsDefaultNewTab(false).then((updated) => {
|
||||
if (!updated) {
|
||||
console.warn("New tab override is managed by policy; not disabled.");
|
||||
return;
|
||||
}
|
||||
chrome.tabs.update({ url: "chrome://new-tab-page" });
|
||||
});
|
||||
});
|
||||
@@ -325,37 +334,30 @@ export function initAuthModal() {
|
||||
|
||||
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];
|
||||
chrome.runtime.sendMessage(
|
||||
{ action: ACTIONS.CLOSE_SIDE_PANEL },
|
||||
() => {
|
||||
getOnyxDomain().then((onyxDomain) => {
|
||||
chrome.runtime.sendMessage({ action: ACTIONS.CLOSE_SIDE_PANEL }, () => {
|
||||
if (chrome.runtime.lastError) {
|
||||
console.error(
|
||||
"Error closing side panel:",
|
||||
chrome.runtime.lastError
|
||||
);
|
||||
}
|
||||
chrome.tabs.create(
|
||||
{
|
||||
url: `${onyxDomain}/auth/login`,
|
||||
active: true,
|
||||
},
|
||||
(_) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
console.error(
|
||||
"Error closing side panel:",
|
||||
"Error opening auth tab:",
|
||||
chrome.runtime.lastError
|
||||
);
|
||||
}
|
||||
chrome.tabs.create(
|
||||
{
|
||||
url: `${onyxDomain}/auth/login`,
|
||||
active: true,
|
||||
},
|
||||
(_) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
console.error(
|
||||
"Error opening auth tab:",
|
||||
chrome.runtime.lastError
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,22 +3,95 @@ import {
|
||||
CHROME_SPECIFIC_STORAGE_KEYS,
|
||||
} from "./constants.js";
|
||||
|
||||
export function normalizeOnyxDomain(domain) {
|
||||
if (typeof domain !== "string") return DEFAULT_ONYX_DOMAIN;
|
||||
const trimmed = domain.trim().replace(/\/+$/, "");
|
||||
return trimmed || DEFAULT_ONYX_DOMAIN;
|
||||
}
|
||||
|
||||
// Managed storage is populated by enterprise policy (see managed_schema.json).
|
||||
// It is read-only and takes precedence over anything the user sets locally.
|
||||
async function getManaged(keys) {
|
||||
if (!chrome.storage.managed) return {};
|
||||
try {
|
||||
return await chrome.storage.managed.get(keys);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Failed to read managed storage; using local settings",
|
||||
error
|
||||
);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
export async function getManagedSettings() {
|
||||
return getManaged([
|
||||
CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN,
|
||||
CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB,
|
||||
]);
|
||||
}
|
||||
|
||||
export async function isDomainManaged() {
|
||||
const managed = await getManaged(CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN);
|
||||
return typeof managed[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN] === "string";
|
||||
}
|
||||
|
||||
export async function isNewTabOverrideManaged() {
|
||||
const managed = await getManaged(
|
||||
CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB
|
||||
);
|
||||
return (
|
||||
typeof managed[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB] ===
|
||||
"boolean"
|
||||
);
|
||||
}
|
||||
|
||||
export async function getOnyxDomain() {
|
||||
const managed = await getManaged(CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN);
|
||||
if (typeof managed[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN] === "string") {
|
||||
return normalizeOnyxDomain(
|
||||
managed[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]
|
||||
);
|
||||
}
|
||||
const result = await chrome.storage.local.get({
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]: DEFAULT_ONYX_DOMAIN,
|
||||
});
|
||||
return result[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN];
|
||||
return normalizeOnyxDomain(result[CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]);
|
||||
}
|
||||
|
||||
export function setOnyxDomain(domain, callback) {
|
||||
chrome.storage.local.set(
|
||||
{ [CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]: domain },
|
||||
{ [CHROME_SPECIFIC_STORAGE_KEYS.ONYX_DOMAIN]: normalizeOnyxDomain(domain) },
|
||||
callback
|
||||
);
|
||||
}
|
||||
|
||||
export function getOnyxDomainSync() {
|
||||
return new Promise((resolve) => {
|
||||
getOnyxDomain(resolve);
|
||||
// Returns undefined when the user has never chosen, so callers can fall back
|
||||
// to their own default.
|
||||
export async function getUseOnyxAsDefaultNewTab() {
|
||||
const managed = await getManaged(
|
||||
CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB
|
||||
);
|
||||
const managedValue =
|
||||
managed[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB];
|
||||
if (typeof managedValue === "boolean") return managedValue;
|
||||
const result = await chrome.storage.local.get(
|
||||
CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB
|
||||
);
|
||||
return result[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB];
|
||||
}
|
||||
|
||||
export async function setUseOnyxAsDefaultNewTab(value) {
|
||||
if (await isNewTabOverrideManaged()) return false;
|
||||
await chrome.storage.local.set({
|
||||
[CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB]: !!value,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
export function isNewTabOverrideChange(changes, namespace) {
|
||||
return (
|
||||
(namespace === "local" || namespace === "managed") &&
|
||||
CHROME_SPECIFIC_STORAGE_KEYS.USE_ONYX_AS_DEFAULT_NEW_TAB in changes
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user