feat(chrome-ext): managed policy for Onyx URL and new-tab toggle; strip trailing slashes (#15027)

This commit is contained in:
roshan authored and GitHub committed 2026-09-23 19:34:16 +00:00
1 parent 436209ee5d
commit b2b7172634
13 files changed
+485 -229

No files matched your search

+52
View File
@@ -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
+16
View File
@@ -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"
}
}
}
+5 -7
View File
@@ -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"
},
+47 -71
View File
@@ -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 });
+38 -35
View File
@@ -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();
+19
View File
@@ -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>
+71 -36
View File
@@ -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);
}
+15
View File
@@ -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">
+19 -10
View File
@@ -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() {
+19
View File
@@ -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>
+75 -36
View File
@@ -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();
+31 -29
View File
@@ -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
);
}
}
);
}
);
}
);
});
});
});
}
}
+78 -5
View File
@@ -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
);
}