diff --git a/addons-host.js b/addons-host.js
index c0fbc7bd..19d108d0 100644
--- a/addons-host.js
+++ b/addons-host.js
@@ -687,9 +687,15 @@ class AddonHost {
toolbarMenus: this.getToolbarMenus(),
};
}
+ // `plugin` marks surfaces of a first-class Silent Mode component (manifest
+ // category "plugin", e.g. Aegis): the chrome pins those to their own dock
+ // instead of the extensions row.
getSidebarPanels() {
const out = [];
- for (const active of this._active.values()) out.push(...active.sidebarPanels);
+ for (const active of this._active.values()) {
+ const plugin = active.manifest.category === "plugin";
+ for (const p of active.sidebarPanels) out.push({ ...p, plugin, addonName: active.manifest.name });
+ }
return out;
}
// Menu declarations from every active add-on that carries a toolbar-menu
@@ -704,6 +710,7 @@ class AddonHost {
addonId: active.manifest.id,
title: tm.title,
icon: tm.icon,
+ plugin: active.manifest.category === "plugin",
items: tm.items.map((it) => ({ id: it.id, label: it.label, icon: it.icon })),
});
}
diff --git a/chrome.html b/chrome.html
index c71be915..f13e18eb 100644
--- a/chrome.html
+++ b/chrome.html
@@ -345,6 +345,17 @@
uniform fixed width so the row reads as a neat grid regardless of the
underlying title length; the strip is 28px tall to fit the roomier chips. */
.bookmarks { display: flex; align-items: center; gap: 6px; padding: 4px 10px 6px; height: 28px; overflow: hidden; }
+ #bmlist { display: flex; align-items: center; gap: 6px; flex: 1 1 auto; min-width: 0; overflow: hidden; }
+ /* Plug-in dock: first-class Silent Mode components (Aegis) get a labelled
+ chip at the right end of the favorites row, directly under the Theseus
+ menu — always visible, never collapsed with the extensions. */
+ .plugdock { margin-left: auto; flex: none; display: flex; align-items: center; gap: 4px; padding-left: 8px; }
+ .plugbtn { display: inline-flex; align-items: center; gap: 6px; height: 22px; padding: 0 10px 0 8px; border-radius: 6px;
+ font: inherit; font-size: 12px; font-weight: 600; color: var(--mut); cursor: pointer; white-space: nowrap;
+ border: 1px solid var(--line); background: transparent; }
+ .plugbtn:hover { background: var(--line2); color: var(--ink); }
+ .plugbtn.active { background: rgb(from var(--acid) r g b / .12); color: var(--acid); border-color: color-mix(in srgb, var(--acid) 35%, transparent); }
+ .plugbtn img { display: block; }
/* Ctrl+F find-in-page bar. Sits below the address bar (above tabs body),
~32px tall so it's part of the chrome view height like .tordisc / .bcnrbar.
syncHeight in the tabs handler picks it up automatically. */
@@ -477,7 +488,7 @@
-
+
–
@@ -666,15 +677,21 @@
// an addon that declares a toolbar-menu both get one entry here. panels
// stay clickable-toggle; menus open a floating dropdown positioned under
// the icon and dispatch the picked item back to the add-on.
- let lastPanels = [], lastMenus = [], lastButtons = [];
+ let lastPanels = [], lastMenus = [], lastButtons = [], lastAllPanels = [], lastAllMenus = [];
let sidebarOpen = false, activePanelId = null;
let openMenuAddonId = null; // which addon's menu dropdown is currently open
function renderSidebarState(d) {
if (!d) return;
- lastPanels = Array.isArray(d.panels) ? d.panels : [];
- lastMenus = Array.isArray(d.toolbarMenus) ? d.toolbarMenus : [];
+ const allPanels = Array.isArray(d.panels) ? d.panels : [];
+ const allMenus = Array.isArray(d.toolbarMenus) ? d.toolbarMenus : [];
sidebarOpen = !!d.visible;
activePanelId = d.active || null;
+ // Plug-ins (Aegis) live in their own dock on the favorites row, under
+ // the Theseus menu; everything else is an extension in the toolbar dock.
+ lastAllPanels = allPanels; lastAllMenus = allMenus;
+ lastPanels = allPanels.filter((p) => !p.plugin);
+ lastMenus = allMenus.filter((m) => !m.plugin);
+ renderPlugDock(allPanels.filter((p) => p.plugin), allMenus.filter((m) => m.plugin));
// Build the flat button list, panels first (matches shipped order) then
// toolbar-menu buttons in registration order.
lastButtons = [];
@@ -724,6 +741,36 @@
$("extmore").classList.toggle("active", (sidebarOpen && !!activePanelId) || !!openMenuAddonId);
}
+ // Plug-in dock (favorites row, right end). A chip per plug-in surface:
+ // icon + name, tinted while its sidebar panel or menu is open. Same
+ // actions as the toolbar dock buttons, never collapsed.
+ function renderPlugDock(panels, menus) {
+ const dock = $("plugdock");
+ const chips = [];
+ // Chip label is the plug-in's product name (e.g. "Aegis Wallet"), the
+ // panel title goes in the tooltip.
+ for (const p of panels) chips.push({ kind: "panel", panelId: p.panelId, icon: p.icon, title: p.title || "Plug-in", name: p.addonName || p.title || "Plug-in", active: sidebarOpen && p.panelId === activePanelId });
+ for (const m of menus) chips.push({ kind: "menu", addonId: m.addonId, icon: m.icon, title: m.title || m.addonId, name: m.title || m.addonId, active: openMenuAddonId === m.addonId });
+ dock.hidden = chips.length === 0;
+ dock.innerHTML = chips.map((c) => {
+ const dataAttr = c.kind === "panel" ? `data-panel="${c.panelId.replace(/"/g,""")}"` : `data-menu="${c.addonId.replace(/"/g,""")}"`;
+ const label = String(c.name).split(/\s+[—–-]\s+/, 1)[0].replace(/${addonIconHtml(c.icon, 14)}${label}`;
+ }).join("");
+ dock.querySelectorAll(".plugbtn").forEach((btn) => {
+ btn.onclick = () => {
+ if (btn.dataset.panel) {
+ const pid = btn.dataset.panel;
+ closeToolbarMenu();
+ if (sidebarOpen && pid === activePanelId) { T.closeSidebar && T.closeSidebar(); }
+ else { T.openSidebar && T.openSidebar(pid); }
+ } else if (btn.dataset.menu) {
+ openToolbarMenu(btn.dataset.menu, btn);
+ }
+ };
+ });
+ }
+
// ---- toolbar-menu dropdown (per-addon) ----
// Positions under the clicked icon, styled after .extpop. Clicking an
// item dispatches to main via addonMenuSelect(addonId, itemId) and
@@ -734,7 +781,7 @@
if (openMenuAddonId) {
openMenuAddonId = null;
// Re-render dock so the "active" tint drops off the icon.
- renderSidebarState({ visible: sidebarOpen, active: activePanelId, panels: lastPanels, toolbarMenus: lastMenus });
+ renderSidebarState({ visible: sidebarOpen, active: activePanelId, panels: lastAllPanels, toolbarMenus: lastAllMenus });
}
}
function openToolbarMenu(addonId, anchorBtn) {
@@ -933,7 +980,7 @@
return bookmarkBrandCase(cut);
}
function renderBookmarks() {
- const box = $("bookmarks");
+ const box = $("bmlist");
box.innerHTML = bookmarks.length
? bookmarks.map((b, i) => {
const fav = b.favicon