diff --git a/index.json b/index.json
index 75eefe8..fb1f3b4 100644
--- a/index.json
+++ b/index.json
@@ -1,6 +1,6 @@
{
"version": 1,
- "updated_at": "2026-06-08T06:45:59.000Z",
+ "updated_at": "2026-06-15T06:08:00.000Z",
"scripts": [
{
"id": "codex-context-ring-restore",
@@ -100,6 +100,23 @@
"homepage": "",
"script_url": "https://raw.githubusercontent.com/hL091015/CodexPlusPlusScriptMarket/main/scripts/zh_CN%E6%B1%89%E5%8C%96.user.js",
"sha256": "72214D31D425D1CE936B457AA43FCC40DF55F4DE3B9B140F9510C7F392CDC845"
+ },
+ {
+ "id": "bennett-ui-improvements",
+ "name": "Bennett UI Improvements",
+ "description": "迁移自 b-nnett Bennett UI 的 renderer-only UI 增强",
+ "version": "1.0.5-bigpizza.1",
+ "author": "bennett; JHees",
+ "tags": [
+ "codex",
+ "ui",
+ "sidebar",
+ "settings",
+ "polish"
+ ],
+ "homepage": "",
+ "script_url": "https://raw.githubusercontent.com/BigPizzaV3/CodexPlusPlusScriptMarket/main/scripts/bennett-ui-improvements.js",
+ "sha256": "4c13009815d74b9aa911e80660cf70596c87340d2ed2f194dbe37915f9cc969f"
}
]
}
diff --git a/scripts/bennett-ui-improvements.js b/scripts/bennett-ui-improvements.js
new file mode 100644
index 0000000..7c892b1
--- /dev/null
+++ b/scripts/bennett-ui-improvements.js
@@ -0,0 +1,7981 @@
+/*
+ * Bennett UI Improvements for BigPizzaV3 Codex++
+ *
+ * Source project: https://github.com/b-nnett/codex-plusplus-bennett-ui
+ * Original tweak id: co.bennett.ui-improvements
+ * Original author: bennett
+ * Original license: MIT License, Copyright (c) 2026 Bennett
+ *
+ * This file is a compatibility migration from the b-nnett Codex++ tweak
+ * runtime to the BigPizzaV3 Codex++ renderer-only user script runtime.
+ * The UI implementation below is not original work by the migrator; the
+ * wrapper only adapts storage/logging/renderer lifecycle assumptions.
+ *
+ * MIT permission notice from the source project applies: permission is
+ * granted to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies, provided the copyright notice and permission notice
+ * are included in all copies or substantial portions of the Software.
+ */
+
+(() => {
+ "use strict";
+
+ const INSTALL_KEY = "__bennettUiImprovementsBigPizza";
+ const VERSION = "1.0.5-bigpizza.1";
+ const previous = window[INSTALL_KEY];
+ if (previous && typeof previous.stop === "function") {
+ try {
+ previous.stop();
+ } catch (error) {
+ console.warn("[Bennett UI/BigPizza] previous stop failed", error);
+ }
+ }
+
+ const module = { exports: {} };
+ const exports = module.exports;
+/**
+ * Bennett's UI Improvements
+ *
+ * A bag of small, individually-toggleable UI tweaks for Codex. Settings
+ * live on a dedicated sidebar entry under the "Tweaks" group.
+ *
+ * Features
+ * --------
+ * • hide-upgrade-prompts Hides the sidebar "Upgrade" pill and the
+ * top-bar "Get Plus" button. Pure DOM filter,
+ * fully reversible.
+ * • show-usage-in-sidebar (experimental) Renders a single usage box where
+ * the upgrade pill was. Click toggles between
+ * 5h and Weekly; hover replaces content with
+ * "Resets: HH:MM" or "Resets: Wed, HH:MM".
+ * Red when <15% remaining.
+ * Sources data from Codex's authenticated
+ * /wham/usage app-server endpoint.
+ * • square-sidebar Flatten the rounded seam between sidebar and
+ * main content panel.
+ * • settings-search Adds a small search field to Codex Settings.
+ * • match-sidebar-width Force the settings page sidebar to match the
+ * main UI sidebar's width, eliminating the
+ * layout jump when opening/closing Settings.
+ * • sidebar-action-grid Render the four main sidebar actions as a 2x2
+ * grid of filled buttons.
+ * • sidebar-project-backgrounds Add subtle grouped backgrounds behind
+ * project rows in the main sidebar.
+ * • sidebar-chat-multi-select Cmd/Ctrl-click sidebar chats to select
+ * multiple rows and run batch actions.
+ * • show-pinned-chat-project-names Shows a small project name under
+ * pinned sidebar chats.
+ * • show-message-metrics-on-hover Shows Codex token metrics beside
+ * assistant messages on hover.
+ * • slash-menu-polish Tightens the composer slash menu with denser rows,
+ * clearer active state, and calmer section headers.
+ *
+ * Authoring notes
+ * ---------------
+ * • Renderer + main; main reads local Codex session JSONL for metrics.
+ * • Each feature returns a `dispose()` so toggling off is clean.
+ * • Match-by-text-content for resilience: Codex's main shell has no
+ * stable testids/aria-labels for these widgets.
+ */
+
+/** @type {import("@codex-plusplus/sdk").Tweak} */
+module.exports = {
+ start(api) {
+ if (api.process === "main") {
+ startMainMetricsProvider(api);
+ startMainUsageProvider(api);
+ startMainProjectLabelProvider(api);
+ startMainSidebarBatchMenuProvider(api);
+ startMainSlashMenuShortcutBridge(api);
+ return;
+ }
+
+ const state = {
+ api,
+ features: new Map(/* id -> { dispose } */),
+ defaults: {
+ "hide-upgrade-prompts": true,
+ "show-usage-in-sidebar": true,
+ "show-message-metrics-on-hover": false,
+ "square-sidebar": false,
+ "settings-search": true,
+ "match-sidebar-width": true,
+ "sidebar-action-grid": true,
+ "sidebar-project-backgrounds": true,
+ "sidebar-chat-multi-select": false,
+ "show-pinned-chat-project-names": false,
+ "slash-menu-polish": true,
+ },
+ };
+ this._state = state;
+
+ // ── settings page ──────────────────────────────────────────────────
+ // We require `registerPage`. The older `register()` API would render
+ // these toggles as a *nested section* inside Codex++'s built-in
+ // "Tweaks" page — that's misleading, since this tweak is supposed to
+ // own its own sidebar entry. If the runtime is too old we just log
+ // and skip the UI; the features themselves still activate below.
+ if (typeof api.settings?.registerPage !== "function") {
+ api.log.warn(
+ "registerPage unavailable — Codex++ runtime is too old. " +
+ "Restart Codex to pick up the latest preload. Settings UI not mounted.",
+ );
+ } else {
+ this._pageHandle = api.settings.registerPage({
+ id: "main",
+ title: "UI Improvements",
+ description: "Bennett's small quality-of-life tweaks.",
+ iconSvg:
+ '",
+ render: (root) => renderSettings(root, state),
+ });
+ }
+
+ // ── activate features per stored prefs ─────────────────────────────
+ for (const id of Object.keys(state.defaults)) {
+ const enabled = readFlag(api, id, state.defaults[id]);
+ if (enabled && FEATURES[id]) activateFeature(state, id);
+ }
+ },
+
+ stop() {
+ const s = this._state;
+ if (!s) return;
+ for (const [, f] of s.features) {
+ try {
+ f.dispose?.();
+ } catch (e) {
+ s.api.log.warn("dispose failed", e);
+ }
+ }
+ s.features.clear();
+ this._pageHandle?.unregister();
+ },
+};
+
+// ─────────────────────────────────────────────────────────── settings UI ──
+
+/**
+ * Render the dedicated page. Mirrors Codex's standard form: one
+ * `flex flex-col gap-2` section per group, rounded card with rows.
+ */
+function renderSettings(root, state) {
+ const features = [
+ {
+ id: "hide-upgrade-prompts",
+ title: "Hide upgrade prompts",
+ description:
+ 'Hide the "Upgrade" pill in the app sidebar and the "Get Plus" button in the top bar.',
+ },
+ {
+ id: "show-usage-in-sidebar",
+ title: "Show usage in sidebar (experimental)",
+ description:
+ "Render 5-hour and weekly rate limits where the upgrade button was. Open the rate-limits breakdown (account menu → Rate limits) at least once to seed the values.",
+ },
+ {
+ id: "show-message-metrics-on-hover",
+ title: "Show message metrics on hover",
+ description:
+ "Show per-turn token usage beside assistant messages.",
+ },
+ {
+ id: "square-sidebar",
+ title: "Square sidebar corners",
+ description:
+ "Remove the rounded inner corners on the main content panel so it sits flush against the sidebar.",
+ },
+ {
+ id: "settings-search",
+ title: "Settings search",
+ description:
+ "Add a search field above the Settings tabs so sections can be filtered quickly.",
+ },
+ {
+ id: "match-sidebar-width",
+ title: "Match settings sidebar width",
+ description:
+ "Stop the layout jump when opening Settings: the settings sidebar (fixed at 300px) is forced to match the main UI sidebar's current width.",
+ },
+ {
+ id: "sidebar-action-grid",
+ title: "Sidebar action grid",
+ description:
+ "Render New chat, Search, Plugins, and Automations as a compact 2x2 grid of filled buttons.",
+ },
+ {
+ id: "sidebar-project-backgrounds",
+ title: "Sidebar project backgrounds",
+ description:
+ "Add subtle grouped backgrounds behind project rows so adjacent projects are easier to scan.",
+ },
+ {
+ id: "sidebar-chat-multi-select",
+ title: "Multi-select sidebar chats",
+ description:
+ "Cmd/Ctrl-click sidebar chats to select multiple rows, then right-click for batch actions.",
+ },
+ {
+ id: "show-pinned-chat-project-names",
+ title: "Show project label for pinned chats",
+ description:
+ "Show a smaller, subdued project label under pinned chats, and under all chats in chronological list mode.",
+ },
+ {
+ id: "slash-menu-polish",
+ title: "Slash menu polish",
+ description:
+ "Tighten the composer slash menu with denser rows, clearer active state, and calmer section headers.",
+ },
+ ];
+
+ const section = el("section", "flex flex-col gap-2");
+ section.appendChild(sectionTitle("Features"));
+
+ const card = roundedCard();
+ for (const f of features) {
+ card.appendChild(featureRow(state, f));
+ }
+ section.appendChild(card);
+ root.appendChild(section);
+}
+
+/**
+ * Heuristic sidebar finder. Codex's left rail is typically a flex column
+ * pinned to x=0 with substantial height. We rank candidates by:
+ * • bounding-rect.left near 0
+ * • height > 60% of viewport
+ * • narrow-ish width (< 360px) for collapsed/expanded sidebars
+ * • presence of `nav` or aria-label="Primary"
+ * and pick the best. Returns the chosen element + a few selector hints.
+ *
+ * Currently unused — kept around for ad-hoc DOM debugging during tweak
+ * development. Wire it up to a temporary button if needed.
+ */
+// eslint-disable-next-line no-unused-vars
+async function dumpSidebar(api) {
+ const candidates = [];
+ const all = document.querySelectorAll(
+ 'aside, nav, [role="navigation"], [data-testid*="sidebar" i], div',
+ );
+ const vh = window.innerHeight;
+ for (const el of all) {
+ const r = el.getBoundingClientRect();
+ if (r.left > 8) continue;
+ if (r.height < vh * 0.6) continue;
+ if (r.width < 40 || r.width > 420) continue;
+ let score = 0;
+ if (el.tagName === "ASIDE" || el.tagName === "NAV") score += 5;
+ if (el.getAttribute("role") === "navigation") score += 3;
+ if (el.querySelector("nav")) score += 1;
+ if (/sidebar/i.test(el.getAttribute("data-testid") || "")) score += 4;
+ if (/rounded/.test(el.className || "")) score += 2;
+ score += Math.max(0, 200 - r.width) / 100; // prefer narrower
+ candidates.push({ el, score, rect: r });
+ }
+ candidates.sort((a, b) => b.score - a.score);
+ const top = candidates[0];
+ if (!top) return { ok: false, reason: "no candidate" };
+
+ const html = top.el.outerHTML;
+ const summary = candidates.slice(0, 5).map((c) => ({
+ tag: c.el.tagName.toLowerCase(),
+ classes: c.el.className,
+ rect: {
+ x: Math.round(c.rect.left),
+ y: Math.round(c.rect.top),
+ w: Math.round(c.rect.width),
+ h: Math.round(c.rect.height),
+ },
+ score: c.score,
+ }));
+
+ const payload =
+ `\n` +
+ summary.map((s) => "").join("\n") +
+ `\n\n\n` +
+ html;
+
+ let wrotePath = null;
+ try {
+ if (typeof api.fs?.write === "function") {
+ await api.fs.write("sidebar-dump.html", payload);
+ wrotePath = "sidebar-dump.html (in tweak data dir)";
+ }
+ } catch (e) {
+ api.log.warn("fs.write failed", e);
+ }
+
+ let copied = false;
+ try {
+ await navigator.clipboard.writeText(payload);
+ copied = true;
+ } catch (e) {
+ api.log.warn("clipboard write failed", e);
+ }
+
+ return { ok: true, copied, wrotePath, summary };
+}
+
+function featureRow(state, f) {
+ const row = el("div", "flex items-center justify-between gap-4 p-3");
+
+ const left = el("div", "flex min-w-0 flex-col gap-1");
+ const label = el("div", "min-w-0 text-sm text-token-text-primary");
+ label.textContent = f.title;
+ left.appendChild(label);
+ if (f.description) {
+ const desc = el("div", "text-token-text-secondary min-w-0 text-sm");
+ desc.textContent = f.description;
+ left.appendChild(desc);
+ }
+ row.appendChild(left);
+
+ const initial = readFlag(state.api, f.id, state.defaults[f.id]);
+ const sw = switchControl(initial, async (next) => {
+ writeFlag(state.api, f.id, next);
+ window.dispatchEvent(new CustomEvent("codexpp-ui-improvements-setting-changed", {
+ detail: { id: f.id, value: next },
+ }));
+ if (next) activateFeature(state, f.id);
+ else deactivateFeature(state, f.id);
+ });
+ row.appendChild(sw);
+ return row;
+}
+
+// ─────────────────────────────────────────────────────────── feature reg ──
+
+function activateFeature(state, id) {
+ if (state.features.has(id)) return;
+ const fn = FEATURES[id];
+ if (!fn) {
+ state.api.log.warn("unknown feature", id);
+ return;
+ }
+ try {
+ const dispose = fn(state.api);
+ state.features.set(id, { dispose });
+ state.api.log.info("activated", id);
+ } catch (e) {
+ state.api.log.error("activate failed", id, e);
+ }
+}
+
+function deactivateFeature(state, id) {
+ const f = state.features.get(id);
+ if (!f) return;
+ try {
+ f.dispose?.();
+ } finally {
+ state.features.delete(id);
+ state.api.log.info("deactivated", id);
+ }
+}
+
+// ─────────────────────────────────────────────────────────────── features ──
+
+const FEATURES = {
+ /**
+ * Hide the "Upgrade" / "Get Plus" buttons. We match by visible text
+ * across the document, skipping anything inside Codex's settings shell
+ * or our own injected panels. Hidden via inline `display:none` so we
+ * can restore it cleanly on dispose.
+ */
+ "hide-upgrade-prompts"(api) {
+ // Two matcher tiers:
+ // • EXACT: short pill labels we trust (case-insensitive, exact match).
+ // • CONTAINS: longer phrases that may appear with trailing icons/arrows
+ // or wrapped in extra spans. We substring-match (case-insensitive).
+ const EXACT = new Set([
+ "upgrade",
+ "get plus",
+ "get chatgpt plus",
+ "upgrade plan",
+ "upgrade your plan",
+ "upgrade to plus",
+ ]);
+ const CONTAINS = ["upgrade for higher limits"];
+ const hidden = new Set(/* HTMLElement */);
+
+ const isInsideOurShell = (el) => {
+ let n = el;
+ while (n) {
+ if (n instanceof HTMLElement && n.dataset?.codexpp) return true;
+ n = n.parentElement;
+ }
+ return false;
+ };
+
+ // Codex sometimes splits the label across icon + text spans, so we use
+ // textContent and collapse whitespace.
+ const normText = (el) =>
+ (el.textContent || "").replace(/\s+/g, " ").trim().toLowerCase();
+
+ const matches = (text) => {
+ if (!text) return false;
+ if (EXACT.has(text)) return true;
+ for (const c of CONTAINS) if (text.includes(c)) return true;
+ return false;
+ };
+
+ const scan = () => {
+ const candidates = document.querySelectorAll(
+ 'button, a, [role="button"], [role="menuitem"]',
+ );
+ for (const el of candidates) {
+ if (hidden.has(el)) continue;
+ if (isInsideOurShell(el)) continue;
+ const t = normText(el);
+ if (t.length === 0 || t.length > 80) continue;
+ if (!matches(t)) continue;
+ const host = el.closest('[class*="rounded"], [class*="badge"]') || el;
+ if (!(host instanceof HTMLElement)) continue;
+ host.dataset.codexppPrevDisplay = host.style.display || "";
+ host.style.display = "none";
+ hidden.add(host);
+ api.log.info("hid upgrade element", { text: t });
+ }
+ };
+
+ scan();
+ const obs = new MutationObserver(scan);
+ obs.observe(document.documentElement, { childList: true, subtree: true });
+
+ return () => {
+ obs.disconnect();
+ for (const el of hidden) {
+ if ("codexppPrevDisplay" in el.dataset) {
+ el.style.display = el.dataset.codexppPrevDisplay;
+ delete el.dataset.codexppPrevDisplay;
+ }
+ }
+ hidden.clear();
+ };
+ },
+
+ /**
+ * Surface 5h + Weekly rate limits in the sidebar slot where the "Upgrade"
+ * pill lives. Sources its data from Codex's authenticated app-server usage
+ * endpoint, with Codex's rendered rate-limit UI as a fallback.
+ *
+ * Strategy
+ * --------
+ * 1. Fetch `/wham/usage` through Codex's existing renderer fetch bridge.
+ * 2. Parse the expanded/compact rendered labels only when the bridge is
+ * unavailable or the request fails.
+ * 3. Persist the latest snapshot and refresh the mounted sidebar box in
+ * place. Re-mount only when Codex replaces the sidebar subtree.
+ */
+ "show-usage-in-sidebar"(api) {
+ /**
+ * Persisted snapshot:
+ * { fiveHour:{label,pct,resetAt} | null,
+ * weekly: {label,pct,resetAt} | null,
+ * at:number }
+ * `pct` is REMAINING (Codex displays remaining %, e.g. "100%").
+ * `resetAt` is whatever Codex shows verbatim (typically "HH:MM",
+ * or "Wed, HH:MM" for weekly API data).
+ */
+ let snapshot = null; // Do not render persisted quota data before this page fetches fresh usage.
+ let mounted = null; // HTMLElement currently rendered in the sidebar
+ let directUsageAvailable = false;
+ let directUsageInFlight = false;
+ let directUsageLastAttemptAt = 0;
+ let directUsageFailureLogged = false;
+ let directUsageSuccessLogged = false;
+ let usageBridgeReadyLogged = false;
+ let usageBridgeScriptInjected = false;
+ let bridgeRequestSeq = 0;
+ let lastMountedMode = null;
+ let accountMode = "unknown"; // "official" | "api" | "unknown"
+ let accountModeInFlight = false;
+ let accountModeLastCheckedAt = 0;
+ let accountModeLogged = false;
+
+ const log = (...a) => api.log.info("[usage]", ...a);
+ const ASIDE_SELECTOR = [
+ "aside.pointer-events-auto.relative.flex.overflow-hidden",
+ "aside.pointer-events-auto.relative.flex.overflow-visible",
+ "aside.pointer-events-auto.relative.flex",
+ ].join(", ");
+
+ // ── parsing ────────────────────────────────────────────────────────
+ const isVisibleElement = (node) => {
+ if (!(node instanceof HTMLElement) || !node.isConnected) return false;
+ if (node.closest("[hidden], [inert], [aria-hidden='true']")) return false;
+ const style = window.getComputedStyle(node);
+ if (
+ style.display === "none" ||
+ style.visibility === "hidden" ||
+ style.opacity === "0"
+ ) {
+ return false;
+ }
+ const rect = node.getBoundingClientRect();
+ return rect.width > 0 && rect.height > 0;
+ };
+
+ const applySnapshot = (partial, source) => {
+ if (!partial?.fiveHour && !partial?.weekly) return false;
+ if (accountMode === "api") return false;
+ const next = {
+ fiveHour: partial.fiveHour || snapshot?.fiveHour || null,
+ weekly: partial.weekly || snapshot?.weekly || null,
+ at: Date.now(),
+ };
+ const changed =
+ JSON.stringify(next.fiveHour) !== JSON.stringify(snapshot?.fiveHour) ||
+ JSON.stringify(next.weekly) !== JSON.stringify(snapshot?.weekly);
+ snapshot = next;
+ writeSnapshot(api, snapshot);
+ if (changed) {
+ log(`parsed snapshot from ${source}`, snapshot);
+ ensureMounted();
+ }
+ return changed;
+ };
+
+ const ensureUsageBridgeScript = () => {
+ if (usageBridgeScriptInjected) return;
+ usageBridgeScriptInjected = true;
+ window.addEventListener(
+ "codexpp-usage-bridge-ready",
+ (event) => {
+ if (usageBridgeReadyLogged) return;
+ usageBridgeReadyLogged = true;
+ api.log.info("[usage] bridge ready", event.detail);
+ },
+ { once: true },
+ );
+ const script = document.createElement("script");
+ script.dataset.codexppUsageBridge = "true";
+ script.textContent = `(() => {
+ if (window.__codexppUsageBridgeInstalled) return;
+ window.__codexppUsageBridgeInstalled = true;
+ const pending = new Set();
+ window.dispatchEvent(new CustomEvent("codexpp-usage-bridge-ready", {
+ detail: {
+ hasElectronBridge: typeof window.electronBridge?.sendMessageFromView === "function",
+ },
+ }));
+ window.addEventListener("codexpp-usage-request", (event) => {
+ const message = event.detail;
+ if (!message || typeof message !== "object" || !message.requestId) return;
+ pending.add(message.requestId);
+ let forwarded = false;
+ const bridge = window.electronBridge;
+ if (typeof bridge?.sendMessageFromView === "function") {
+ forwarded = true;
+ bridge.sendMessageFromView(message).catch(() => {});
+ }
+ const forwardedEvent = new CustomEvent("codex-message-from-view", {
+ detail: message,
+ });
+ if (forwarded) forwardedEvent.__codexForwardedViaBridge = true;
+ window.dispatchEvent(forwardedEvent);
+ });
+ window.addEventListener("message", (event) => {
+ const data = event.data;
+ if (
+ !data ||
+ typeof data !== "object" ||
+ data.type !== "fetch-response" ||
+ !pending.has(data.requestId)
+ ) {
+ return;
+ }
+ pending.delete(data.requestId);
+ window.dispatchEvent(new CustomEvent("codexpp-usage-response", {
+ detail: data,
+ }));
+ window.postMessage({
+ type: "codexpp-usage-response",
+ detail: data,
+ }, "*");
+ });
+ })();`;
+ (document.head || document.documentElement).appendChild(script);
+ script.remove();
+ };
+
+ const dispatchCodexViewMessage = (message) => {
+ ensureUsageBridgeScript();
+ window.dispatchEvent(
+ new CustomEvent("codexpp-usage-request", { detail: message }),
+ );
+
+ let forwarded = false;
+ const bridge = window.electronBridge;
+ if (typeof bridge?.sendMessageFromView === "function") {
+ forwarded = true;
+ bridge.sendMessageFromView(message).catch((e) => {
+ if (!directUsageFailureLogged) {
+ directUsageFailureLogged = true;
+ api.log.warn("[usage] bridge send failed", e);
+ }
+ });
+ }
+ const event = new CustomEvent("codex-message-from-view", {
+ detail: message,
+ });
+ if (forwarded) event.__codexForwardedViaBridge = true;
+ window.dispatchEvent(event);
+ };
+
+ const fetchCodexAppServerJson = async (url, timeoutMs = 10_000) => {
+ try {
+ return await api.ipc.invoke("usage-fetch", url);
+ } catch {
+ // Older runtimes or a failed main-webview probe fall through to the
+ // renderer bridge attempt below.
+ }
+
+ const hostId =
+ new URL(window.location.href).searchParams.get("hostId")?.trim() ||
+ "local";
+ const requestId = `codexpp-usage-${Date.now()}-${++bridgeRequestSeq}`;
+
+ return new Promise((resolve, reject) => {
+ let done = false;
+ const cleanup = () => {
+ done = true;
+ window.removeEventListener("message", onMessage);
+ window.removeEventListener("codexpp-usage-response", onBridgeResponse);
+ window.clearTimeout(timer);
+ };
+ const finish = (fn, value) => {
+ if (done) return;
+ cleanup();
+ fn(value);
+ };
+ const onMessage = (event) => {
+ const data =
+ event.data?.type === "codexpp-usage-response"
+ ? event.data.detail
+ : event.data;
+ handleResponse(data);
+ };
+ const onBridgeResponse = (event) => {
+ handleResponse(event.detail);
+ };
+ const handleResponse = (data) => {
+ if (
+ !data ||
+ typeof data !== "object" ||
+ data.type !== "fetch-response" ||
+ data.requestId !== requestId
+ ) {
+ return;
+ }
+ if (data.responseType === "success") {
+ try {
+ const body = JSON.parse(data.bodyJsonString);
+ if (data.status >= 200 && data.status < 300) {
+ finish(resolve, body);
+ } else {
+ finish(reject, new Error(`HTTP ${data.status}`));
+ }
+ } catch (e) {
+ finish(reject, e);
+ }
+ } else {
+ finish(reject, new Error(data.error || "fetch failed"));
+ }
+ };
+ const timer = window.setTimeout(() => {
+ dispatchCodexViewMessage({ type: "cancel-fetch", requestId });
+ finish(reject, new Error("usage request timed out"));
+ }, timeoutMs);
+ window.addEventListener("message", onMessage);
+ window.addEventListener("codexpp-usage-response", onBridgeResponse);
+ dispatchCodexViewMessage({
+ type: "fetch",
+ hostId,
+ requestId,
+ method: "GET",
+ url,
+ });
+ });
+ };
+
+ const bridgePostJson = async (path, payload = {}, timeoutMs = 2_500) => {
+ const bridge = window.__codexSessionDeleteBridge;
+ if (typeof bridge !== "function") return null;
+ return await Promise.race([
+ bridge(path, payload),
+ new Promise((resolve) => window.setTimeout(() => resolve(null), timeoutMs)),
+ ]);
+ };
+
+ const activeRelayProfile = (settings) => {
+ if (!settings || typeof settings !== "object") return null;
+ const profiles = Array.isArray(settings.relayProfiles) ? settings.relayProfiles : [];
+ const activeId =
+ typeof settings.activeRelayId === "string" && settings.activeRelayId.trim()
+ ? settings.activeRelayId
+ : "default";
+ return profiles.find((profile) => profile?.id === activeId) || profiles[0] || null;
+ };
+
+ const fieldValue = (object, ...keys) => {
+ if (!object || typeof object !== "object") return undefined;
+ for (const key of keys) {
+ if (Object.prototype.hasOwnProperty.call(object, key)) return object[key];
+ }
+ return undefined;
+ };
+
+ const catalogLooksLikeApiMode = (catalog) => {
+ if (!catalog || typeof catalog !== "object") return false;
+ const provider = String(catalog.model_provider || catalog.provider_name || "").toLowerCase();
+ if (!provider) return false;
+ return !["openai", "chatgpt"].includes(provider);
+ };
+
+ const refreshAccountMode = async (force = false) => {
+ if (accountModeInFlight) return accountMode;
+ const now = Date.now();
+ if (!force && accountModeLastCheckedAt && now - accountModeLastCheckedAt < 10_000) {
+ return accountMode;
+ }
+ accountModeLastCheckedAt = now;
+ accountModeInFlight = true;
+ try {
+ let nextMode = "unknown";
+ let settingsMode = "unknown";
+ const settings = await bridgePostJson("/settings/get", {});
+ const profile = activeRelayProfile(settings);
+ const relayMode = fieldValue(profile, "relayMode", "relay_mode");
+ const officialMixApiKey = !!fieldValue(profile, "officialMixApiKey", "official_mix_api_key");
+ const legacyApiConfigured = !!(
+ String(fieldValue(settings, "relayApiKey", "relay_api_key") || "").trim() ||
+ String(fieldValue(settings, "relayBaseUrl", "relay_base_url") || "").trim()
+ );
+ if (relayMode === "official" && !officialMixApiKey) {
+ nextMode = "official";
+ } else if (relayMode === "pureApi" || relayMode === "pure_api") {
+ nextMode = "api";
+ } else if (relayMode === "mixedApi" || relayMode === "mixed_api" || officialMixApiKey) {
+ nextMode = "api";
+ } else if (!relayMode && legacyApiConfigured) {
+ nextMode = "api";
+ }
+
+ if (nextMode === "unknown") {
+ const catalog = await bridgePostJson("/codex-model-catalog", {});
+ if (catalogLooksLikeApiMode(catalog)) nextMode = "api";
+ else if (catalog?.model_provider === "openai" || catalog?.provider_name === "openai") {
+ nextMode = "official";
+ }
+ }
+ if (nextMode === "unknown") nextMode = settingsMode;
+
+ if (nextMode !== "unknown" && nextMode !== accountMode) {
+ accountMode = nextMode;
+ if (accountMode === "api") {
+ snapshot = {
+ fiveHour: { label: "API", pct: null, resetAt: null, apiMode: true },
+ weekly: null,
+ at: Date.now(),
+ apiMode: true,
+ };
+ } else if (snapshot?.apiMode) {
+ snapshot = null;
+ }
+ ensureMounted(true);
+ }
+ if (!accountModeLogged && accountMode !== "unknown") {
+ accountModeLogged = true;
+ log("account mode", accountMode);
+ }
+ return accountMode;
+ } catch (e) {
+ return accountMode;
+ } finally {
+ accountModeInFlight = false;
+ }
+ };
+
+ const remainingPercent = (usedPercent) => {
+ const used = Number(usedPercent);
+ if (!Number.isFinite(used)) return null;
+ return Math.round(Math.min(Math.max(100 - used, 0), 100));
+ };
+
+ const formatResetAt = (epochSeconds, includeDay = false) => {
+ const seconds = Number(epochSeconds);
+ if (!Number.isFinite(seconds)) return null;
+ const date = new Date(seconds * 1000);
+ if (!Number.isFinite(date.getTime())) return null;
+ return date.toLocaleTimeString(undefined, {
+ ...(includeDay ? { weekday: "short" } : {}),
+ hour: "numeric",
+ minute: "2-digit",
+ });
+ };
+
+ const normalizeUsageWindow = (window, label) => {
+ if (!window || typeof window !== "object") return null;
+ const pct = remainingPercent(window.used_percent);
+ if (pct == null) return null;
+ const minutes = Number(window.limit_window_seconds) / 60;
+ const includeResetDay = Number.isFinite(minutes) && minutes >= 1440;
+ return {
+ label,
+ pct,
+ resetAt: formatResetAt(window.reset_at, includeResetDay),
+ };
+ };
+
+ const pickClosestWindow = (windows, targetMinutes, predicate) => {
+ let best = null;
+ let bestDistance = Infinity;
+ for (const window of windows) {
+ const minutes = Number(window?.limit_window_seconds) / 60;
+ if (!Number.isFinite(minutes) || !predicate(minutes)) continue;
+ const distance = Math.abs(minutes - targetMinutes);
+ if (
+ !best ||
+ distance < bestDistance ||
+ (distance === bestDistance &&
+ minutes > Number(best.limit_window_seconds) / 60)
+ ) {
+ best = window;
+ bestDistance = distance;
+ }
+ }
+ return best;
+ };
+
+ const snapshotFromUsageStatus = (status) => {
+ const limits = [];
+ const pushLimit = (rateLimit) => {
+ if (!rateLimit || typeof rateLimit !== "object") return;
+ if (rateLimit.primary_window) limits.push(rateLimit.primary_window);
+ if (rateLimit.secondary_window) limits.push(rateLimit.secondary_window);
+ };
+
+ pushLimit(status?.rate_limit);
+ if (Array.isArray(status?.additional_rate_limits)) {
+ for (const item of status.additional_rate_limits) {
+ pushLimit(item?.rate_limit);
+ }
+ }
+
+ const five = pickClosestWindow(
+ limits,
+ 300,
+ (minutes) => minutes > 0 && minutes < 1440,
+ );
+ const weekly = pickClosestWindow(
+ limits,
+ 7 * 24 * 60,
+ (minutes) => minutes >= 1440,
+ );
+
+ return {
+ fiveHour: normalizeUsageWindow(five, "5h"),
+ weekly: normalizeUsageWindow(weekly, "Weekly"),
+ };
+ };
+
+ const collectUsageWindows = (value, out = [], seen = new WeakSet()) => {
+ if (!value || typeof value !== "object") return out;
+ if (seen.has(value)) return out;
+ seen.add(value);
+ if (
+ "used_percent" in value &&
+ "limit_window_seconds" in value &&
+ "reset_at" in value
+ ) {
+ out.push(value);
+ }
+ if (Array.isArray(value)) {
+ for (const item of value) collectUsageWindows(item, out, seen);
+ } else {
+ for (const item of Object.values(value)) {
+ collectUsageWindows(item, out, seen);
+ }
+ }
+ return out;
+ };
+
+ const snapshotFromUsageWindows = (windows) => {
+ const five = pickClosestWindow(
+ windows,
+ 300,
+ (minutes) => minutes > 0 && minutes < 1440,
+ );
+ const weekly = pickClosestWindow(
+ windows,
+ 7 * 24 * 60,
+ (minutes) => minutes >= 1440,
+ );
+ return {
+ fiveHour: normalizeUsageWindow(five, "5h"),
+ weekly: normalizeUsageWindow(weekly, "Weekly"),
+ };
+ };
+
+ const applyUsageEvent = (message) => {
+ if (!message || typeof message !== "object") return false;
+ const windows = collectUsageWindows(message);
+ if (!windows.length) return false;
+ const partial = snapshotFromUsageWindows(windows);
+ if (!partial.fiveHour && !partial.weekly) return false;
+ directUsageAvailable = true;
+ applySnapshot(partial, "rate-limit-event");
+ return true;
+ };
+
+ const onUsageMessage = (event) => {
+ const data = event.data;
+ if (!data || typeof data !== "object") return;
+ applyUsageEvent(data);
+ };
+
+ const refreshUsageFromApi = async () => {
+ if ((await refreshAccountMode()) !== "official") return false;
+ if (directUsageInFlight) return false;
+ const now = Date.now();
+ if (directUsageLastAttemptAt && now - directUsageLastAttemptAt < 15_000) {
+ return false;
+ }
+ directUsageLastAttemptAt = now;
+ directUsageInFlight = true;
+ try {
+ const status = await fetchCodexAppServerJson("/wham/usage");
+ const partial = snapshotFromUsageStatus(status);
+ if (partial.fiveHour || partial.weekly) {
+ directUsageAvailable = true;
+ directUsageFailureLogged = false;
+ if (!directUsageSuccessLogged) {
+ directUsageSuccessLogged = true;
+ log("api active", partial);
+ }
+ applySnapshot(partial, "api");
+ return true;
+ }
+ return false;
+ } catch (e) {
+ if (!directUsageFailureLogged) {
+ directUsageFailureLogged = true;
+ api.log.warn("[usage] /wham/usage unavailable; falling back to DOM", e);
+ }
+ return false;
+ } finally {
+ directUsageInFlight = false;
+ }
+ };
+
+ /**
+ * Codex's expanded breakdown is a 2-column CSS grid: label in col-1,
+ * value in col-2. We locate the grid by its unique class signature,
+ * then walk children pairwise.
+ *
+ * Returns the breakdown grid element, or null.
+ */
+ const findBreakdownGrid = () => {
+ // The full class string is long and may shift; we anchor on the
+ // distinctive `grid-cols-[minmax(0,1fr)_auto]` token.
+ const grids = document.querySelectorAll(
+ 'div[class*="grid-cols-[minmax(0,1fr)_auto]"]',
+ );
+ for (const g of grids) {
+ if (!isVisibleElement(g)) continue;
+ const txt = (g.textContent || "").toLowerCase();
+ if (
+ (txt.includes("5h") || txt.includes("hourly")) &&
+ txt.includes("week")
+ )
+ return g;
+ }
+ return null;
+ };
+
+ /**
+ * Parse a value span (e.g. "100%·16:19") into `{ pct, resetAt }`.
+ * Falls back to `null` fields when a piece is missing.
+ */
+ const parseValueText = (txt, root) => {
+ const pctMatch = txt.match(/(\d{1,3})\s*%/);
+ const pct = pctMatch ? Math.max(0, Math.min(100, +pctMatch[1])) : null;
+ // Prefer the inner [title="HH:MM"] attribute, else regex the text.
+ const titled = root?.querySelector?.("[title]");
+ let resetAt = titled ? titled.getAttribute("title") : null;
+ if (!resetAt) {
+ const tMatch =
+ txt.match(/\b(\d{1,2}:\d{2})\b/) ||
+ txt.match(/\b(\d+\s*(?:m|h|d))\b/i);
+ resetAt = tMatch ? tMatch[1] : null;
+ }
+ return { pct, resetAt };
+ };
+
+ const parseValue = (span) => {
+ const txt = (span.textContent || "").replace(/\s+/g, " ").trim();
+ return parseValueText(txt, span);
+ };
+
+ const scanBreakdown = (grid) => {
+ const kids = Array.from(grid.children);
+ let five = null;
+ let week = null;
+ // Pair (label, value) — col-1 then col-2 in DOM order.
+ for (let i = 0; i + 1 < kids.length; i += 2) {
+ const labelTxt = (kids[i].textContent || "")
+ .replace(/\s+/g, " ")
+ .trim();
+ const value = parseValue(kids[i + 1]);
+ const lower = labelTxt.toLowerCase();
+ if (!five && (lower === "5h" || lower.startsWith("hourly"))) {
+ five = { label: labelTxt, ...value };
+ } else if (!week && lower.startsWith("week")) {
+ week = { label: labelTxt, ...value };
+ }
+ }
+ if (!five && !week) return false;
+ applySnapshot({ fiveHour: five, weekly: week }, "breakdown");
+ return true;
+ };
+
+ const parseCompactUsageNode = (node) => {
+ if (!(node instanceof HTMLElement)) return null;
+ if (node.closest('[data-codexpp="usage-box"]')) return null;
+ if (!isVisibleElement(node)) return null;
+ const text = (node.textContent || "").replace(/\s+/g, " ").trim();
+ if (!text || text.length > 160 || !/%/.test(text)) return null;
+ const lower = text.toLowerCase();
+ const hasFive = /\b(5h|5\s*hour|hourly)\b/.test(lower);
+ const hasWeek = /\b(weekly|week)\b/.test(lower);
+ if (!hasFive && !hasWeek) return null;
+
+ const value = parseValueText(text, node);
+ if (value.pct == null) return null;
+ const label = hasFive && !hasWeek ? "5h" : hasWeek && !hasFive ? "Weekly" : null;
+ if (!label) return null;
+ return label === "5h"
+ ? { fiveHour: { label, ...value } }
+ : { weekly: { label, ...value } };
+ };
+
+ const scanCompactUsage = () => {
+ const candidates = document.querySelectorAll(
+ 'button, [role="button"], [role="status"], [aria-label], [title], span',
+ );
+ for (const node of candidates) {
+ const partial = parseCompactUsageNode(node);
+ if (partial) applySnapshot(partial, "compact");
+ }
+ };
+
+ // ── sidebar mount ─────────────────────────────────────────────────
+ /**
+ * Find the sidebar slot for the upgrade pill. The pill itself is
+ * hidden by `hide-upgrade-prompts`, so we mount as a sibling that
+ * replaces its visual footprint. We anchor on the parent of any
+ * button/link with text "Upgrade" (case-insensitive), or fall back
+ * to the bottom of the sidebar group.
+ *
+ * Returns the parent element to mount into, or null if not found.
+ */
+ const compactSidebarText = (node) =>
+ (node?.textContent || "").replace(/\s+/g, " ").trim().toLowerCase();
+
+ const looksLikeSettingsSidebar = (sidebar) => {
+ if (!(sidebar instanceof HTMLElement)) return false;
+ if (
+ sidebar.matches(".window-fx-sidebar-surface.w-token-sidebar") ||
+ sidebar.closest(".window-fx-sidebar-surface.w-token-sidebar") ||
+ sidebar.querySelector("[data-codexpp-settings-search]")
+ ) {
+ return true;
+ }
+ const text = compactSidebarText(sidebar);
+ const englishSettings =
+ text.includes("general") &&
+ text.includes("appearance") &&
+ (text.includes("account") || text.includes("configuration"));
+ const chineseSettings =
+ text.includes("常规") &&
+ text.includes("外观") &&
+ (
+ text.includes("配置") ||
+ text.includes("个性化") ||
+ text.includes("键盘快捷键") ||
+ text.includes("mcp 服务器") ||
+ text.includes("钩子") ||
+ text.includes("连接") ||
+ text.includes("环境") ||
+ text.includes("工作树") ||
+ text.includes("已归档")
+ );
+ return englishSettings || chineseSettings;
+ };
+
+ const looksLikeMainAppSidebar = (sidebar) => {
+ const text = compactSidebarText(sidebar);
+ const hasNewChat = /\bnew chat\b|\bquick chat\b|新建|新对话/.test(text);
+ const hasSearch = /\bsearch\b|搜索/.test(text);
+ const hasProjectOrHistory =
+ /\bprojects?\b|\bhistory\b|\bchats?\b|项目|历史|会话/.test(text);
+ return (hasNewChat && hasSearch) || (hasSearch && hasProjectOrHistory);
+ };
+
+ const findUsageSidebar = () => {
+ const candidates = Array.from(document.querySelectorAll(ASIDE_SELECTOR))
+ .filter((node) => node instanceof HTMLElement && isVisibleElement(node))
+ .filter((sidebar) => {
+ const rect = sidebar.getBoundingClientRect();
+ return rect.width >= 180 && !looksLikeSettingsSidebar(sidebar);
+ });
+ return candidates.find(looksLikeMainAppSidebar) || null;
+ };
+
+ const controlText = (node) =>
+ [
+ node.getAttribute?.("aria-label"),
+ node.getAttribute?.("title"),
+ node.textContent,
+ ]
+ .filter(Boolean)
+ .join(" ")
+ .replace(/\s+/g, " ")
+ .trim()
+ .toLowerCase();
+
+ const isSettingsOrDeviceButton = (button) => {
+ const text = controlText(button);
+ return (
+ /\bsettings?\b|preferences?|设置|偏好/.test(text) ||
+ /\bmobile\b|\bphone\b|\bdevice\b|手机|移动|设备|连接/.test(text)
+ );
+ };
+
+ const nearestControlRow = (sidebar, button) => {
+ const sidebarRect = sidebar.getBoundingClientRect();
+ let row = button.parentElement;
+ while (row && row !== document.body && row !== sidebar.parentElement) {
+ if (!(row instanceof HTMLElement)) break;
+ const rect = row.getBoundingClientRect();
+ const style = window.getComputedStyle(row);
+ const buttonCount = row.querySelectorAll('button, a, [role="button"]').length;
+ const insideSidebar =
+ rect.left >= sidebarRect.left - 8 &&
+ rect.right <= sidebarRect.right + 8;
+ const looksLikeControlLayer =
+ insideSidebar &&
+ rect.height > 0 &&
+ rect.height <= 88 &&
+ (style.display === "flex" || style.display === "grid" || buttonCount >= 2);
+ if (looksLikeControlLayer) return row;
+ row = row.parentElement;
+ }
+ return button.parentElement instanceof HTMLElement ? button.parentElement : null;
+ };
+
+ const createInlineSlot = (row, anchor) => {
+ const existing = row.querySelector(':scope > [data-codexpp="usage-slot"]');
+ if (existing instanceof HTMLElement) return existing;
+ const slot = document.createElement("div");
+ slot.dataset.codexpp = "usage-slot";
+ slot.dataset.codexppUsageSlot = "controls-inline";
+ slot.className = "flex shrink-0 items-center";
+ if (anchor?.parentElement === row) row.insertBefore(slot, anchor);
+ else row.appendChild(slot);
+ return slot;
+ };
+
+ const findSidebarSlot = () => {
+ const sidebar = findUsageSidebar();
+ if (!sidebar) return null;
+ const existingSlot = sidebar.querySelector('[data-codexpp="usage-slot"]');
+ if (existingSlot instanceof HTMLElement) return existingSlot;
+
+ const controls = Array.from(sidebar.querySelectorAll('button, a, [role="button"]'))
+ .filter((button) => button instanceof HTMLElement && isVisibleElement(button));
+ const preferredControls = controls.filter(isSettingsOrDeviceButton);
+ const ordered = (preferredControls.length ? preferredControls : controls).sort((a, b) => {
+ const ar = a.getBoundingClientRect();
+ const br = b.getBoundingClientRect();
+ return br.bottom - ar.bottom;
+ });
+
+ for (const button of ordered) {
+ const row = nearestControlRow(sidebar, button);
+ if (row) return createInlineSlot(row, button);
+ }
+
+ return null;
+ };
+
+ const displaySnapshot = () =>
+ accountMode === "api"
+ ? {
+ fiveHour: { label: "API", pct: null, resetAt: null, apiMode: true },
+ weekly: null,
+ at: Date.now(),
+ apiMode: true,
+ }
+ :
+ snapshot && (snapshot.fiveHour || snapshot.weekly)
+ ? snapshot
+ : {
+ fiveHour: { label: "5h", pct: null, resetAt: null },
+ weekly: { label: "Weekly", pct: null, resetAt: null },
+ at: 0,
+ };
+
+ const ensureMounted = (forceRebuild = false) => {
+ const visibleSnapshot = displaySnapshot();
+ const slot = findSidebarSlot();
+ document.querySelectorAll('[data-codexpp="usage-floating-slot"]').forEach((node) => node.remove());
+ if (!slot) {
+ if (mounted) {
+ mounted.remove();
+ mounted = null;
+ }
+ for (const stale of document.querySelectorAll(
+ '[data-codexpp="usage-box"], [data-codexpp="usage-boxes"]',
+ )) {
+ stale.remove();
+ }
+ if (!ensureMounted._warned) {
+ log("ensureMounted: no sidebar slot found yet");
+ ensureMounted._warned = true;
+ }
+ return;
+ }
+
+ // Defensive: remove any stale boxes left by a previous mount cycle
+ // (hot-reload, stop() race, or an older shape of this tweak that
+ // used `data-codexpp="usage-boxes"`).
+ for (const stale of document.querySelectorAll(
+ '[data-codexpp="usage-box"], [data-codexpp="usage-boxes"]',
+ )) {
+ if (stale !== mounted) stale.remove();
+ }
+
+ if (mounted && slot.contains(mounted) && !forceRebuild) {
+ mounted._refresh?.(visibleSnapshot);
+ return;
+ }
+ if (mounted) mounted.remove();
+ mounted = renderUsageBox(api, visibleSnapshot);
+ mounted.dataset.codexpp = "usage-box";
+ slot.appendChild(mounted);
+ lastMountedMode = slot.dataset.codexppUsageSlot || "unknown";
+ mounted.style.flex = "0 1 auto";
+ mounted.style.width = "auto";
+ mounted.style.minWidth = "4.75rem";
+ mounted.style.maxWidth = "8.5rem";
+ if (slot.dataset.codexppUsageSlot === "settings-inline-windows" || slot.dataset.codexppUsageSlot === "controls-inline") {
+ mounted.style.width = "auto";
+ mounted.style.minWidth = "4.75rem";
+ }
+ log("mounted usage box", {
+ mode: lastMountedMode,
+ slotTag: slot.tagName,
+ slotClass: slot.className,
+ });
+ };
+
+ // Initial render from persisted snapshot (so first paint isn't empty
+ // even before the user opens the popover).
+ ensureMounted(true);
+
+ // ── observers ─────────────────────────────────────────────────────
+ // We throttle to one tick per animation frame so a flood of React
+ // re-renders can't tank the renderer (Codex mutates the DOM heavily
+ // while typing). Coalesces N onMutate() calls into one scan.
+ let scheduled = false;
+ const onMutate = () => {
+ if (scheduled) return;
+ scheduled = true;
+ requestAnimationFrame(() => {
+ scheduled = false;
+ refreshAccountMode().then((mode) => {
+ if (mode === "official") refreshUsageFromApi();
+ });
+ if (accountMode === "official" && !directUsageAvailable) {
+ const grid = findBreakdownGrid();
+ if (grid) scanBreakdown(grid);
+ scanCompactUsage();
+ }
+ ensureMounted();
+ });
+ };
+
+ onMutate();
+ const obs = new MutationObserver(onMutate);
+ obs.observe(document.documentElement, { childList: true, subtree: true });
+ const interval = window.setInterval(onMutate, 15_000);
+ window.addEventListener("focus", onMutate);
+ window.addEventListener("message", onUsageMessage);
+ document.addEventListener("visibilitychange", onMutate);
+
+ log("active", { snapshot });
+
+ return () => {
+ obs.disconnect();
+ window.clearInterval(interval);
+ window.removeEventListener("focus", onMutate);
+ window.removeEventListener("message", onUsageMessage);
+ document.removeEventListener("visibilitychange", onMutate);
+ if (mounted) {
+ mounted.remove();
+ mounted = null;
+ }
+ for (const slot of document.querySelectorAll('[data-codexpp="usage-slot"]')) {
+ if (slot instanceof HTMLElement && slot.children.length === 0) slot.remove();
+ }
+ for (const slot of document.querySelectorAll('[data-codexpp="usage-floating-slot"]')) {
+ slot.remove();
+ }
+ };
+ },
+
+ /**
+ * Square sidebar: the visual "rounded sidebar" is actually the main
+ * content panel — `` —
+ * which has `border-radius: 12.5px 0 0 12.5px` (TL+BL via Tailwind's
+ * logical `rounded-s-2xl`). Its rounded left edge curves into the
+ * sidebar, making the sidebar's TR+BR corners *appear* rounded.
+ * Flattening `.main-surface`'s left side squares the seam.
+ */
+ "square-sidebar"() {
+ const STYLE_ID = "codexpp-square-sidebar";
+ document.getElementById(STYLE_ID)?.remove();
+
+ const style = document.createElement("style");
+ style.id = STYLE_ID;
+ style.textContent = `
+ /* Flatten the main panel's left (logical-start) corners.
+ Codex applies these via Tailwind's rounded-s-2xl utility. */
+ .main-surface {
+ border-start-start-radius: 0 !important;
+ border-end-start-radius: 0 !important;
+ }
+ `;
+ document.head.appendChild(style);
+
+ return () => {
+ style.remove();
+ };
+ },
+
+ /**
+ * Refine the composer slash menu by lightly annotating the live DOM.
+ *
+ * Live DOM shape captured via Electron CDP:
+ * [data-composer-overlay-floating-ui] > slash panel
+ * slash panel [data-list-navigation-item="true"]
+ * slash panel .sticky.top-0 section headers
+ */
+ "slash-menu-polish"() {
+ const STYLE_ID = "codexpp-slash-menu-polish";
+ const MENU_ATTR = "data-codexpp-slash-menu";
+ const OVERLAY_ATTR = "data-codexpp-slash-overlay";
+ const TOPBAR_ATTR = "data-codexpp-slash-topbar";
+ const SECTION_ATTR = "data-codexpp-slash-section";
+ const SECTION_EMPTY_ATTR = "data-codexpp-slash-section-empty";
+ const SECTION_TITLE_ATTR = "data-codexpp-slash-section-title";
+ const SECTION_ICON_ATTR = "data-codexpp-slash-section-icon";
+ const INPUT_MODE_ATTR = "data-codexpp-slash-input-mode";
+ const PROGRAM_SCROLL_ATTR = "data-codexpp-slash-programmatic-scroll";
+ const HOVER_SUPPRESS_ATTR = "data-codexpp-slash-hover-suppressed";
+ const OVERLAY_NOISE_ATTR = "data-codexpp-slash-overlay-noise";
+ const FAVORITES_GROUP_ATTR = "data-codexpp-slash-favorites";
+ const FAVORITE_KEY_ATTR = "data-codexpp-slash-favorite-key";
+ const FAVORITE_CLONE_ATTR = "data-codexpp-slash-favorite-clone";
+ const FAVORITE_SOURCE_SECTION_ATTR = "data-codexpp-slash-favorite-source-section";
+ const FAVORITE_DUPLICATE_HIDDEN_ATTR =
+ "data-codexpp-slash-favorite-duplicate-hidden";
+ const FAVORITES_STORAGE_KEY = "codexpp.slashMenuFavorites.v1";
+ const FAVORITE_BUTTON_CLASS = "codexpp-slash-favorite-button";
+ const SKILL_COPY_CLASS = "codexpp-slash-skill-copy";
+ document.getElementById(STYLE_ID)?.remove();
+
+ const style = document.createElement("style");
+ style.id = STYLE_ID;
+ style.textContent = `
+ [data-composer-overlay-floating-ui="true"] {
+ isolation: isolate;
+ }
+
+ [data-composer-overlay-floating-ui="true"][${OVERLAY_ATTR}="true"]
+ > :not([${MENU_ATTR}="true"]) {
+ display: none !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ > [${OVERLAY_NOISE_ATTR}="true"] {
+ display: none !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [data-codexpp="nav-group"],
+ [data-composer-overlay-floating-ui="true"]
+ [data-codexpp="pages-group"],
+ [data-composer-overlay-floating-ui="true"]
+ [data-codexpp="nav-config"],
+ [data-composer-overlay-floating-ui="true"]
+ [data-codexpp="nav-tweaks"],
+ [data-composer-overlay-floating-ui="true"]
+ [data-codexpp^="nav-page-"] {
+ display: none !important;
+ height: 0 !important;
+ min-height: 0 !important;
+ margin: 0 !important;
+ padding: 0 !important;
+ border: 0 !important;
+ overflow: hidden !important;
+ pointer-events: none !important;
+ }
+
+ [class*="[container-name:home-main-content]"]
+ [data-codexpp="nav-group"],
+ [class*="[container-name:home-main-content]"]
+ [data-codexpp="pages-group"],
+ [class*="[container-name:home-main-content]"]
+ [data-codexpp="nav-config"],
+ [class*="[container-name:home-main-content]"]
+ [data-codexpp="nav-tweaks"],
+ [class*="[container-name:home-main-content]"]
+ [data-codexpp^="nav-tweak"],
+ [class*="[container-name:home-main-content]"]
+ [data-codexpp^="nav-page-"] {
+ display: none !important;
+ height: 0 !important;
+ min-height: 0 !important;
+ margin: 0 !important;
+ padding: 0 !important;
+ border: 0 !important;
+ overflow: hidden !important;
+ pointer-events: none !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ > [${MENU_ATTR}="true"] {
+ width: min(100%, calc(100vw - 1rem)) !important;
+ max-width: calc(100vw - 1rem) !important;
+ border-color: color-mix(in srgb, currentColor 13%, transparent) !important;
+ background-color: var(--color-token-dropdown-background) !important;
+ background-color: color-mix(
+ in srgb,
+ var(--color-token-dropdown-background) 94%,
+ var(--color-token-main-surface-primary) 6%
+ ) !important;
+ box-shadow:
+ 0 18px 48px rgb(0 0 0 / 0.28),
+ 0 1px 0 rgb(255 255 255 / 0.06) inset !important;
+ padding: 0.375rem !important;
+ backdrop-filter: blur(16px) saturate(130%) !important;
+ overflow-x: hidden !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ .vertical-scroll-fade-mask {
+ gap: 0.125rem !important;
+ overflow-x: hidden !important;
+ overscroll-behavior-x: none !important;
+ padding-top: 0.5rem !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ .vertical-scroll-fade-mask
+ > div:not([${TOPBAR_ATTR}]) {
+ display: flex !important;
+ flex: 0 0 auto !important;
+ flex-direction: column !important;
+ height: auto !important;
+ min-width: 0 !important;
+ max-width: 100% !important;
+ overflow-x: hidden !important;
+ overflow-y: visible !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ .vertical-scroll-fade-mask
+ > div[${SECTION_ATTR}]:not(:first-child) {
+ border-top: 1px solid color-mix(in srgb, currentColor 14%, transparent) !important;
+ margin-top: 0.25rem !important;
+ padding-top: 0.25rem !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ .vertical-scroll-fade-mask
+ > div[${SECTION_EMPTY_ATTR}="true"] {
+ display: none !important;
+ margin: 0 !important;
+ padding: 0 !important;
+ border: 0 !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [${TOPBAR_ATTR}="true"] {
+ display: flex !important;
+ flex: none !important;
+ min-width: 0 !important;
+ align-items: center !important;
+ justify-content: space-between !important;
+ gap: 0.75rem !important;
+ margin: -0.375rem -0.375rem 0 !important;
+ border-bottom: 1px solid color-mix(in srgb, currentColor 10%, transparent) !important;
+ background-color: var(--color-token-dropdown-background) !important;
+ background-image: linear-gradient(
+ to bottom,
+ color-mix(in srgb, var(--color-token-dropdown-background) 98%, transparent),
+ color-mix(in srgb, var(--color-token-dropdown-background) 90%, transparent)
+ ) !important;
+ padding: 0.375rem 0.5rem 0.375rem 0.625rem !important;
+ color: var(--color-token-text-primary) !important;
+ backdrop-filter: blur(16px) saturate(130%) !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [${SECTION_TITLE_ATTR}] {
+ min-width: 0 !important;
+ overflow: hidden !important;
+ text-overflow: ellipsis !important;
+ white-space: nowrap !important;
+ font-size: 0.75rem !important;
+ font-weight: 600 !important;
+ letter-spacing: 0 !important;
+ line-height: 1rem !important;
+ text-transform: none !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [${SECTION_TITLE_ATTR}][data-changing="true"] {
+ animation: codexpp-slash-title-change 180ms ease !important;
+ }
+
+ @keyframes codexpp-slash-title-change {
+ 0% {
+ opacity: 0;
+ transform: translateY(0.25rem);
+ }
+ 100% {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ .codexpp-slash-section-icons {
+ display: flex !important;
+ flex: none !important;
+ align-items: center !important;
+ gap: 0.125rem !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [${SECTION_ICON_ATTR}] {
+ display: inline-flex !important;
+ position: relative !important;
+ width: 1.5rem !important;
+ height: 1.5rem !important;
+ flex: none !important;
+ align-items: center !important;
+ justify-content: center !important;
+ border: 0 !important;
+ border-radius: 999px !important;
+ background: transparent !important;
+ color: var(--color-token-text-secondary) !important;
+ font-weight: 800 !important;
+ opacity: 0.78 !important;
+ overflow: hidden !important;
+ transition:
+ color 140ms ease,
+ opacity 140ms ease,
+ transform 140ms ease !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [${SECTION_ICON_ATTR}]::before {
+ content: "" !important;
+ position: absolute !important;
+ inset: 0 !important;
+ border-radius: inherit !important;
+ background: var(--codexpp-section-color, var(--color-token-text-primary)) !important;
+ box-shadow: 0 0 0 1px color-mix(in srgb, #fff 24%, transparent) inset !important;
+ opacity: 0 !important;
+ transform: scale(0.62) !important;
+ transition:
+ opacity 160ms ease,
+ transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1) !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [${SECTION_ICON_ATTR}][data-active="true"] {
+ color: #fff !important;
+ font-weight: 900 !important;
+ opacity: 1 !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [${SECTION_ICON_ATTR}][data-active="true"]::before {
+ opacity: 1 !important;
+ transform: scale(1) !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [${SECTION_ICON_ATTR}]:hover:not([data-active="true"]) {
+ background: color-mix(in srgb, currentColor 8%, transparent) !important;
+ opacity: 1 !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [${SECTION_ICON_ATTR}][data-active="true"]:hover {
+ color: #fff !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [${SECTION_ICON_ATTR}]
+ svg {
+ position: relative !important;
+ z-index: 1 !important;
+ width: 0.9375rem !important;
+ height: 0.9375rem !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [${SECTION_ICON_ATTR}][data-active="true"]
+ svg path {
+ stroke-width: 1.8 !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [data-list-navigation-item="true"][${FAVORITE_DUPLICATE_HIDDEN_ATTR}="true"] {
+ display: none !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [data-list-navigation-item="true"] {
+ box-sizing: border-box !important;
+ position: relative !important;
+ width: 100% !important;
+ min-height: 1.75rem !important;
+ height: 1.75rem !important;
+ padding: 0 0.625rem !important;
+ color: var(--color-token-text-primary) !important;
+ opacity: 0.9 !important;
+ max-width: 100% !important;
+ min-width: 0 !important;
+ overflow-x: hidden !important;
+ transition:
+ background-color 120ms ease,
+ color 120ms ease,
+ opacity 120ms ease !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [data-list-navigation-item="true"]
+ > div {
+ min-height: 1.25rem !important;
+ gap: 0.625rem !important;
+ min-width: 0 !important;
+ max-width: 100% !important;
+ overflow-x: hidden !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [data-list-navigation-item="true"]
+ svg {
+ width: 1rem !important;
+ height: 1rem !important;
+ color: var(--color-token-description-foreground) !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [data-list-navigation-item="true"]:hover,
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [data-list-navigation-item="true"]:focus-visible {
+ background-color: color-mix(in srgb, currentColor 7%, transparent) !important;
+ opacity: 1 !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [data-list-navigation-item="true"][aria-selected="true"] {
+ background-color: color-mix(
+ in srgb,
+ var(--color-token-text-primary, currentColor) 11%,
+ transparent
+ ) !important;
+ color: var(--color-token-text-primary) !important;
+ opacity: 1 !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [data-list-navigation-item="true"][${FAVORITE_CLONE_ATTR}="true"]:hover,
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [data-list-navigation-item="true"][${FAVORITE_CLONE_ATTR}="true"][aria-selected="true"] {
+ background-color: color-mix(
+ in srgb,
+ var(--color-token-text-primary, currentColor) 10%,
+ transparent
+ ) !important;
+ opacity: 1 !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [data-list-navigation-item="true"][aria-selected="true"]
+ svg {
+ color: var(--color-token-text-primary) !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"][${INPUT_MODE_ATTR}="keyboard"]
+ [data-list-navigation-item="true"]:hover:not([aria-selected="true"]) {
+ background-color: transparent !important;
+ opacity: 0.9 !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"][${PROGRAM_SCROLL_ATTR}="true"]
+ .vertical-scroll-fade-mask
+ [data-list-navigation-item="true"],
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"][${HOVER_SUPPRESS_ATTR}="true"]
+ .vertical-scroll-fade-mask
+ [data-list-navigation-item="true"] {
+ pointer-events: none !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"][${HOVER_SUPPRESS_ATTR}="true"]
+ [data-list-navigation-item="true"]:hover:not([aria-selected="true"]),
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"][${HOVER_SUPPRESS_ATTR}="true"]
+ [data-list-navigation-item="true"]:focus-visible:not([aria-selected="true"]) {
+ background-color: transparent !important;
+ opacity: 0.9 !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [data-list-navigation-item="true"]
+ div,
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [data-list-navigation-item="true"]
+ span {
+ min-width: 0 !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [data-list-navigation-item="true"]
+ .text-token-description-foreground,
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [data-list-navigation-item="true"]
+ span[class*="text-token-description-foreground"] {
+ color: var(--color-token-text-secondary) !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [data-list-navigation-item="true"]
+ span.ml-auto {
+ max-width: 40% !important;
+ overflow: hidden !important;
+ text-overflow: ellipsis !important;
+ white-space: nowrap !important;
+ border: 1px solid color-mix(in srgb, currentColor 12%, transparent) !important;
+ border-radius: 999px !important;
+ padding: 0 0.375rem !important;
+ font-size: 0.6875rem !important;
+ line-height: 1rem !important;
+ color: var(--color-token-text-secondary) !important;
+ background-color: color-mix(in srgb, currentColor 5%, transparent) !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ .${FAVORITE_BUTTON_CLASS} {
+ display: inline-flex !important;
+ width: 1.25rem !important;
+ height: 1.25rem !important;
+ flex: 0 0 1.25rem !important;
+ align-items: center !important;
+ justify-content: center !important;
+ border: 0 !important;
+ border-radius: 999px !important;
+ background: transparent !important;
+ color: var(--color-token-text-secondary) !important;
+ cursor: pointer !important;
+ opacity: 0 !important;
+ transform: scale(0.92) !important;
+ transition:
+ color 120ms ease,
+ opacity 120ms ease,
+ transform 120ms ease !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [data-list-navigation-item="true"]:hover
+ .${FAVORITE_BUTTON_CLASS},
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [data-list-navigation-item="true"][aria-selected="true"]
+ .${FAVORITE_BUTTON_CLASS},
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ .${FAVORITE_BUTTON_CLASS}[data-favorite="true"] {
+ opacity: 1 !important;
+ transform: scale(1) !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"][${HOVER_SUPPRESS_ATTR}="true"]
+ [data-list-navigation-item="true"]:hover
+ .${FAVORITE_BUTTON_CLASS}:not([data-favorite="true"]) {
+ opacity: 0 !important;
+ transform: scale(0.92) !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ .${FAVORITE_BUTTON_CLASS}[data-favorite="true"] {
+ color: #f4c95d !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ .${FAVORITE_BUTTON_CLASS}:hover {
+ color: #ffd76a !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ .${FAVORITE_BUTTON_CLASS}
+ svg {
+ width: 0.875rem !important;
+ height: 0.875rem !important;
+ color: currentColor !important;
+ stroke-width: 2 !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ .sticky.top-0 {
+ position: static !important;
+ height: 0 !important;
+ margin: 0 !important;
+ overflow: hidden !important;
+ padding: 0 !important;
+ border: 0 !important;
+ opacity: 0 !important;
+ pointer-events: none !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [data-list-navigation-item="true"][${SECTION_ATTR}="skills"],
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [data-list-navigation-item="true"][${FAVORITE_SOURCE_SECTION_ATTR}="skills"] {
+ height: auto !important;
+ min-height: 2.875rem !important;
+ padding-top: 0.3125rem !important;
+ padding-bottom: 0.3125rem !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [data-list-navigation-item="true"][${SECTION_ATTR}="skills"]
+ > div,
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ [data-list-navigation-item="true"][${FAVORITE_SOURCE_SECTION_ATTR}="skills"]
+ > div {
+ align-items: center !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ .${SKILL_COPY_CLASS} {
+ display: flex !important;
+ min-width: 0 !important;
+ flex: 1 1 auto !important;
+ flex-direction: column !important;
+ gap: 0.0625rem !important;
+ overflow: hidden !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ .${SKILL_COPY_CLASS}
+ > div {
+ max-width: 100% !important;
+ flex: none !important;
+ overflow: hidden !important;
+ text-overflow: ellipsis !important;
+ white-space: nowrap !important;
+ line-height: 1rem !important;
+ }
+
+ [data-composer-overlay-floating-ui="true"]
+ [${MENU_ATTR}="true"]
+ .${SKILL_COPY_CLASS}
+ > span {
+ max-width: 100% !important;
+ flex: none !important;
+ overflow: hidden !important;
+ text-overflow: ellipsis !important;
+ white-space: nowrap !important;
+ color: var(--color-token-text-secondary) !important;
+ font-size: 0.75rem !important;
+ line-height: 1rem !important;
+ }
+ `;
+ document.head.appendChild(style);
+
+ const scrollHandlers = new Map();
+ const pointerHandlers = new Map();
+ const hoverGuardHandlers = new Map();
+ const wheelHandlers = new Map();
+ const hoverScrollStates = new WeakMap();
+ const hoverSuppressStates = new WeakMap();
+ const scrollAnimations = new Map();
+ const titleTimers = new Map();
+ const HOVER_GUARD_EVENTS = [
+ "pointermove",
+ "pointerover",
+ "pointerenter",
+ "mousemove",
+ "mouseover",
+ "mouseenter",
+ ];
+ const NAV_NOISE_SELECTOR = [
+ '[data-codexpp="nav-group"]',
+ '[data-codexpp="pages-group"]',
+ '[data-codexpp="nav-config"]',
+ '[data-codexpp="nav-tweaks"]',
+ '[data-codexpp^="nav-tweak"]',
+ '[data-codexpp^="nav-page-"]',
+ ].join(", ");
+ const OBSERVER_OPTIONS = {
+ characterData: true,
+ childList: true,
+ subtree: true,
+ };
+ let scanFrame = 0;
+ let scanTimer = 0;
+ let homePruneFrame = 0;
+ let hardPruneTimer = 0;
+ let disposed = false;
+ let observer = null;
+ let documentHoverGuard = null;
+ let slashRowScrollAllowedUntil = 0;
+ const nativeScrollIntoView = Element.prototype.scrollIntoView;
+
+ const normText = (node) =>
+ String(node?.textContent || "").replace(/\s+/g, " ").trim();
+
+ const isOverlayNoise = (node) => {
+ if (node instanceof HTMLElement) {
+ const codexpp = node.getAttribute("data-codexpp") || "";
+ if (
+ codexpp === "nav-group" ||
+ codexpp === "pages-group" ||
+ codexpp.startsWith("nav-page-") ||
+ codexpp === "nav-config" ||
+ codexpp === "nav-tweaks"
+ ) {
+ return true;
+ }
+ }
+ const text = normText(node);
+ return (
+ /^Codex\+\+\b/.test(text) ||
+ /^Tweaks\b/.test(text) ||
+ /\bTweak Store\b/.test(text) ||
+ /Better TerminalKeyboard ShortcutsDatabase Explorer/.test(text)
+ );
+ };
+
+ const stopHoverSelectionEvent = (menu, event) => {
+ if (!(menu instanceof HTMLElement)) return;
+ trackPointerPosition(menu, event);
+ if (shouldBlockSuppressedHover(menu, event)) {
+ event.stopPropagation();
+ event.stopImmediatePropagation?.();
+ return;
+ }
+ freezeHoverScroll(menu);
+ event.stopPropagation();
+ event.stopImmediatePropagation?.();
+ };
+
+ const installDocumentHoverGuard = () => {
+ if (documentHoverGuard) return;
+ documentHoverGuard = (event) => {
+ const target = event.target;
+ if (!(target instanceof Element)) return;
+ stopHoverSelectionEvent(target.closest(`[${MENU_ATTR}="true"]`), event);
+ };
+ HOVER_GUARD_EVENTS.forEach((type) =>
+ window.addEventListener(type, documentHoverGuard, true),
+ );
+ HOVER_GUARD_EVENTS.forEach((type) =>
+ document.addEventListener(type, documentHoverGuard, true),
+ );
+ };
+
+ const allowSlashRowScrollIntoView = (duration = 220) => {
+ slashRowScrollAllowedUntil = Math.max(
+ slashRowScrollAllowedUntil,
+ performance.now() + duration,
+ );
+ };
+
+ const isSlashMenuRow = (node) =>
+ node instanceof HTMLElement &&
+ node.matches('[data-list-navigation-item="true"]') &&
+ !!node.closest(`[${MENU_ATTR}="true"]`);
+
+ const hoverSuppressStateFor = (menu) => {
+ let state = hoverSuppressStates.get(menu);
+ if (!state) {
+ state = {
+ active: false,
+ pointerX: null,
+ pointerY: null,
+ releaseAfter: 0,
+ };
+ hoverSuppressStates.set(menu, state);
+ }
+ return state;
+ };
+
+ const eventPointerPosition = (event) => {
+ if (!event || typeof event.clientX !== "number" || typeof event.clientY !== "number") {
+ return null;
+ }
+ return { x: event.clientX, y: event.clientY };
+ };
+
+ const trackPointerPosition = (menu, event) => {
+ const point = eventPointerPosition(event);
+ if (!point) return;
+ const state = hoverSuppressStateFor(menu);
+ if (!state.active) {
+ state.pointerX = point.x;
+ state.pointerY = point.y;
+ }
+ };
+
+ const clearHoverSelection = (menu) => {
+ const state = hoverSuppressStateFor(menu);
+ const pointerTarget =
+ typeof state.pointerX === "number" && typeof state.pointerY === "number"
+ ? document.elementFromPoint(state.pointerX, state.pointerY)
+ : null;
+ const rows = new Set(
+ Array.from(menu.querySelectorAll('[data-list-navigation-item="true"]:hover')),
+ );
+ const pointerRow = pointerTarget?.closest?.('[data-list-navigation-item="true"]');
+ if (pointerRow instanceof HTMLElement && menu.contains(pointerRow)) {
+ rows.add(pointerRow);
+ }
+ menu
+ .querySelectorAll('[data-list-navigation-item="true"][aria-selected="true"]')
+ .forEach((row) => {
+ if (!(row instanceof HTMLElement)) return;
+ rows.add(row);
+ const rect = row.getBoundingClientRect();
+ if (
+ typeof state.pointerX === "number" &&
+ typeof state.pointerY === "number" &&
+ state.pointerX >= rect.left &&
+ state.pointerX <= rect.right &&
+ state.pointerY >= rect.top &&
+ state.pointerY <= rect.bottom
+ ) {
+ rows.add(row);
+ }
+ });
+ rows.forEach((row) => {
+ if (!(row instanceof HTMLElement)) return;
+ row.setAttribute("aria-selected", "false");
+ row.blur();
+ });
+ };
+
+ const suppressHoverUntilPointerMoves = (menu, duration = 900) => {
+ if (!(menu instanceof HTMLElement)) return;
+ const state = hoverSuppressStateFor(menu);
+ state.active = true;
+ state.releaseAfter = performance.now() + duration;
+ menu.setAttribute(HOVER_SUPPRESS_ATTR, "true");
+ clearHoverSelection(menu);
+ [0, 80, 240].forEach((delay) => {
+ window.setTimeout(() => {
+ if (menu.hasAttribute(HOVER_SUPPRESS_ATTR)) clearHoverSelection(menu);
+ }, delay);
+ });
+ };
+
+ const clearHoverSuppression = (menu) => {
+ if (!(menu instanceof HTMLElement)) return;
+ const state = hoverSuppressStateFor(menu);
+ state.active = false;
+ state.releaseAfter = 0;
+ menu.removeAttribute(HOVER_SUPPRESS_ATTR);
+ if (!menu.hasAttribute(PROGRAM_SCROLL_ATTR)) {
+ menu.setAttribute(INPUT_MODE_ATTR, "pointer");
+ }
+ };
+
+ const shouldBlockSuppressedHover = (menu, event) => {
+ const state = hoverSuppressStateFor(menu);
+ if (!state.active) return false;
+ const point = eventPointerPosition(event);
+ const now = performance.now();
+ const scroller = menu.querySelector(".vertical-scroll-fade-mask");
+ const programmatic =
+ menu.hasAttribute(PROGRAM_SCROLL_ATTR) ||
+ (scroller instanceof HTMLElement &&
+ typeof hoverScrollStateFor(scroller).programmaticTarget === "number" &&
+ now < hoverScrollStateFor(scroller).programmaticUntil);
+
+ if (!point) return true;
+ if (state.pointerX === null || state.pointerY === null) {
+ if (!programmatic && now >= state.releaseAfter - 450) {
+ clearHoverSuppression(menu);
+ state.pointerX = point.x;
+ state.pointerY = point.y;
+ return false;
+ }
+ state.pointerX = point.x;
+ state.pointerY = point.y;
+ return true;
+ }
+ const moved = Math.hypot(point.x - state.pointerX, point.y - state.pointerY);
+ if (moved >= 5 && !programmatic && now >= state.releaseAfter - 450) {
+ clearHoverSuppression(menu);
+ state.pointerX = point.x;
+ state.pointerY = point.y;
+ return false;
+ }
+ return true;
+ };
+
+ const hoverScrollStateFor = (scroller) => {
+ let state = hoverScrollStates.get(scroller);
+ if (!state) {
+ state = {
+ freezeTop: scroller.scrollTop,
+ freezeUntil: 0,
+ lastTop: scroller.scrollTop,
+ programmaticTarget: null,
+ programmaticUntil: 0,
+ restoreFrame: 0,
+ };
+ hoverScrollStates.set(scroller, state);
+ }
+ return state;
+ };
+
+ const enforceHoverScrollFreeze = (scroller) => {
+ const state = hoverScrollStateFor(scroller);
+ const now = performance.now();
+ const currentTop = scroller.scrollTop;
+ const programmaticActive =
+ typeof state.programmaticTarget === "number" && now < state.programmaticUntil;
+ const programmaticDown = programmaticActive && state.programmaticTarget >= state.lastTop;
+
+ if (now <= state.freezeUntil && (!programmaticActive || programmaticDown)) {
+ if (currentTop < state.freezeTop - 1) {
+ scroller.scrollTop = state.freezeTop;
+ state.lastTop = state.freezeTop;
+ return true;
+ }
+ state.freezeTop = Math.max(state.freezeTop, currentTop);
+ }
+
+ state.lastTop = scroller.scrollTop;
+ return false;
+ };
+
+ const requestHoverScrollFreezeFrame = (scroller) => {
+ const state = hoverScrollStateFor(scroller);
+ if (state.restoreFrame) return;
+ const tick = () => {
+ state.restoreFrame = 0;
+ enforceHoverScrollFreeze(scroller);
+ if (performance.now() <= state.freezeUntil) {
+ state.restoreFrame = requestAnimationFrame(tick);
+ }
+ };
+ state.restoreFrame = requestAnimationFrame(tick);
+ };
+
+ const queueHoverScrollFreezeChecks = (scroller) => {
+ [0, 16, 80, 180, 360].forEach((delay) => {
+ window.setTimeout(() => enforceHoverScrollFreeze(scroller), delay);
+ });
+ };
+
+ const freezeHoverScroll = (menu) => {
+ const scroller = menu.querySelector(".vertical-scroll-fade-mask");
+ if (!(scroller instanceof HTMLElement)) return;
+ const state = hoverScrollStateFor(scroller);
+ const now = performance.now();
+ if (
+ typeof state.programmaticTarget === "number" &&
+ now < state.programmaticUntil &&
+ state.programmaticTarget < scroller.scrollTop - 1
+ ) {
+ state.freezeUntil = 0;
+ state.freezeTop = scroller.scrollTop;
+ state.lastTop = scroller.scrollTop;
+ return;
+ }
+ const stableTop = Math.max(state.lastTop, scroller.scrollTop);
+ state.freezeTop = Math.max(state.freezeTop, stableTop);
+ state.freezeUntil = Math.max(state.freezeUntil, now + 450);
+ requestHoverScrollFreezeFrame(scroller);
+ queueHoverScrollFreezeChecks(scroller);
+ };
+
+ const clearHoverScrollFreeze = (scroller) => {
+ const state = hoverScrollStateFor(scroller);
+ state.freezeUntil = 0;
+ state.freezeTop = scroller.scrollTop;
+ state.lastTop = scroller.scrollTop;
+ };
+
+ const allowProgrammaticScroll = (scroller, targetTop, duration = 900) => {
+ const state = hoverScrollStateFor(scroller);
+ if (targetTop < scroller.scrollTop - 1) {
+ state.freezeUntil = 0;
+ state.freezeTop = targetTop;
+ }
+ state.programmaticTarget = targetTop;
+ state.programmaticUntil = performance.now() + duration;
+ };
+
+ const patchedScrollIntoView = function (...args) {
+ if (isSlashMenuRow(this) && performance.now() > slashRowScrollAllowedUntil) {
+ return;
+ }
+ return nativeScrollIntoView.apply(this, args);
+ };
+
+ const looksLikeSlashPanel = (node) => {
+ if (!(node instanceof HTMLElement)) return false;
+ if (node.hasAttribute(MENU_ATTR)) return true;
+ if (/^No commands$/i.test(normText(node))) return true;
+ const scroller = node.querySelector(".vertical-scroll-fade-mask");
+ return (
+ scroller instanceof HTMLElement &&
+ node.querySelector('[data-list-navigation-item="true"]')
+ );
+ };
+
+ const isOverlaySlashActive = (overlay) =>
+ isSlashQueryActive() ||
+ Array.from(overlay.children).some((child) => looksLikeSlashPanel(child));
+
+ const markOverlayNoise = (overlay) => {
+ const active = isOverlaySlashActive(overlay);
+ Array.from(overlay.children).forEach((child) => {
+ if (!(child instanceof HTMLElement)) return;
+ if (active && !looksLikeSlashPanel(child) && isOverlayNoise(child)) {
+ child.setAttribute(OVERLAY_NOISE_ATTR, "true");
+ } else {
+ child.removeAttribute(OVERLAY_NOISE_ATTR);
+ }
+ });
+ };
+
+ const pruneOverlayNoise = (overlay) => {
+ if (!isOverlaySlashActive(overlay)) return;
+ Array.from(overlay.children).forEach((child) => {
+ if (!(child instanceof HTMLElement)) return;
+ if (looksLikeSlashPanel(child) || !isOverlayNoise(child)) return;
+ child.remove();
+ });
+ };
+
+ const pruneMenuNoise = (menu) => {
+ menu.querySelectorAll(NAV_NOISE_SELECTOR).forEach((node) => node.remove());
+ };
+
+ const pruneHomeContentNoise = () => {
+ document
+ .querySelectorAll(
+ [
+ '[class*="[container-name:home-main-content]"] [data-codexpp="nav-group"]',
+ '[class*="[container-name:home-main-content]"] [data-codexpp="pages-group"]',
+ '[class*="[container-name:home-main-content]"] [data-codexpp="nav-config"]',
+ '[class*="[container-name:home-main-content]"] [data-codexpp="nav-tweaks"]',
+ '[class*="[container-name:home-main-content]"] [data-codexpp^="nav-tweak"]',
+ '[class*="[container-name:home-main-content]"] [data-codexpp^="nav-page-"]',
+ ].join(", "),
+ )
+ .forEach((node) => node.remove());
+ };
+
+ const shouldPruneHomeContentNoise = () =>
+ isSlashQueryActive() ||
+ !!document.querySelector(
+ '[data-composer-overlay-floating-ui="true"], [data-codexpp-slash-menu="true"]',
+ );
+
+ const scheduleHomeContentPrune = () => {
+ if (homePruneFrame) return;
+ homePruneFrame = requestAnimationFrame(() => {
+ homePruneFrame = 0;
+ if (shouldPruneHomeContentNoise()) pruneHomeContentNoise();
+ });
+ };
+
+ const hardPruneNoise = () => {
+ try {
+ pruneHomeContentNoise();
+ document
+ .querySelectorAll(`[${MENU_ATTR}="true"]`)
+ .forEach((menu) => {
+ if (menu instanceof HTMLElement) pruneMenuNoise(menu);
+ });
+ } catch {
+ // Ignore transient DOM shapes while Codex is replacing the slash panel.
+ }
+ };
+
+ const scheduleHardPruneNoise = () => {
+ if (hardPruneTimer || disposed) return;
+ hardPruneTimer = window.setTimeout(() => {
+ hardPruneTimer = 0;
+ if (disposed || !shouldPruneHomeContentNoise()) return;
+ observer?.disconnect();
+ hardPruneNoise();
+ requestAnimationFrame(() => {
+ if (!disposed) {
+ observer?.observe(document.body, OBSERVER_OPTIONS);
+ scheduleScan();
+ }
+ });
+ }, 60);
+ };
+
+ const sectionKey = (title) =>
+ String(title || "General")
+ .replace(/\s+/g, "-")
+ .replace(/[^a-z0-9-]/gi, "")
+ .toLowerCase() || "general";
+
+ const sectionColor = (key, index) => {
+ const known = {
+ favorites: "#f4c95d",
+ general: "#8ab4ff",
+ skills: "#7dd3a8",
+ mcp: "#f0b86a",
+ tools: "#c4a7ff",
+ };
+ return known[key] || ["#8ab4ff", "#7dd3a8", "#f0b86a", "#c4a7ff"][index % 4];
+ };
+
+ const sectionIconSvg = (key) => {
+ if (key === "favorites") {
+ return (
+ '"
+ );
+ }
+ if (key === "skills") {
+ return (
+ '"
+ );
+ }
+ return (
+ '"
+ );
+ };
+
+ const starIconSvg = (filled) =>
+ filled
+ ? ''
+ : '';
+
+ const readFavorites = () => {
+ try {
+ const raw = window.localStorage?.getItem(FAVORITES_STORAGE_KEY);
+ const values = JSON.parse(raw || "[]");
+ return new Set(Array.isArray(values) ? values.filter(Boolean) : []);
+ } catch {
+ return new Set();
+ }
+ };
+
+ const writeFavorites = (favorites) => {
+ try {
+ window.localStorage?.setItem(
+ FAVORITES_STORAGE_KEY,
+ JSON.stringify(Array.from(favorites).sort()),
+ );
+ } catch {
+ // Ignore storage failures; the row controls still update for this render.
+ }
+ };
+
+ const rowFavoriteKey = (button, fallbackSectionKey) => {
+ const section = fallbackSectionKey || button.getAttribute(SECTION_ATTR) || "general";
+ const text = normText(button)
+ .replace(/\s+/g, " ")
+ .trim()
+ .toLowerCase();
+ return text ? `${section}:${text}` : "";
+ };
+
+ const isSlashSearchActive = () =>
+ Array.from(document.querySelectorAll('.ProseMirror[contenteditable="true"]')).some(
+ (editor) => {
+ if (!(editor instanceof HTMLElement)) return false;
+ const text = normText(editor);
+ return text.startsWith("/") && text.length > 1;
+ },
+ );
+
+ const refreshFavoriteViews = () => {
+ document
+ .querySelectorAll(`[${MENU_ATTR}="true"] .vertical-scroll-fade-mask`)
+ .forEach((scroller) => {
+ if (!(scroller instanceof HTMLElement)) return;
+ syncFavoriteControls(scroller);
+ syncFavoritesSection(scroller);
+ const sections = groupSections(scroller);
+ const topbar = scroller.previousElementSibling;
+ if (topbar instanceof HTMLElement) renderTopbarIcons(topbar, sections);
+ updateTopbar(scroller, sections);
+ });
+ };
+
+ const toggleFavorite = (key) => {
+ if (!key) return;
+ const favorites = readFavorites();
+ if (favorites.has(key)) favorites.delete(key);
+ else favorites.add(key);
+ writeFavorites(favorites);
+ refreshFavoriteViews();
+ scheduleScan();
+ };
+
+ const stripNativeCommandState = (row) => {
+ if (!(row instanceof HTMLElement)) return;
+ const stripNode = (node) => {
+ if (!(node instanceof HTMLElement)) return;
+ node.removeAttribute(FAVORITE_DUPLICATE_HIDDEN_ATTR);
+ for (const attr of Array.from(node.attributes)) {
+ if (
+ attr.name === "cmdk-item" ||
+ attr.name === "data-value" ||
+ (attr.name.startsWith("data-codexpp-") &&
+ !attr.name.startsWith("data-codexpp-slash-"))
+ ) {
+ node.removeAttribute(attr.name);
+ }
+ }
+ };
+ stripNode(row);
+ row.querySelectorAll("*").forEach(stripNode);
+ };
+
+ const ensureFavoriteControl = (button, key, favorites = readFavorites()) => {
+ if (!key) return;
+ button.setAttribute(FAVORITE_KEY_ATTR, key);
+ const inner = button.firstElementChild instanceof HTMLElement ? button.firstElementChild : button;
+ let control = button.querySelector(`:scope .${FAVORITE_BUTTON_CLASS}`);
+ if (!(control instanceof HTMLElement)) {
+ control = document.createElement("span");
+ control.setAttribute("role", "button");
+ control.setAttribute("tabindex", "-1");
+ control.className = FAVORITE_BUTTON_CLASS;
+ control.addEventListener("pointerdown", (event) => {
+ event.preventDefault();
+ event.stopPropagation();
+ });
+ control.addEventListener("mousedown", (event) => {
+ event.preventDefault();
+ event.stopPropagation();
+ });
+ control.addEventListener("pointerup", (event) => {
+ event.preventDefault();
+ event.stopPropagation();
+ toggleFavorite(button.getAttribute(FAVORITE_KEY_ATTR) || key);
+ });
+ control.addEventListener("click", (event) => {
+ event.preventDefault();
+ event.stopPropagation();
+ });
+ const shortcut = inner.querySelector("span.ml-auto");
+ if (shortcut instanceof HTMLElement) inner.insertBefore(control, shortcut);
+ else inner.appendChild(control);
+ }
+ const active = favorites.has(key);
+ control.dataset.favorite = active ? "true" : "false";
+ control.setAttribute("aria-label", active ? "Remove from favorites" : "Add to favorites");
+ control.innerHTML = starIconSvg(active);
+ };
+
+ const unwrapSkillRow = (button) => {
+ const copy = button.querySelector(`.${SKILL_COPY_CLASS}`);
+ if (!copy || !copy.parentElement) return;
+ while (copy.firstChild) copy.parentElement.insertBefore(copy.firstChild, copy);
+ copy.remove();
+ };
+
+ const wrapSkillRow = (button) => {
+ const inner = button.firstElementChild;
+ if (!(inner instanceof HTMLElement)) return;
+ if (inner.querySelector(`.${SKILL_COPY_CLASS}`)) return;
+ const children = Array.from(inner.children);
+ const title = children.find(
+ (node) =>
+ node instanceof HTMLElement &&
+ node.tagName === "DIV" &&
+ normText(node).length > 0,
+ );
+ const description = children.find(
+ (node) =>
+ node instanceof HTMLElement &&
+ node.tagName === "SPAN" &&
+ String(node.className).includes("text-token-description-foreground"),
+ );
+ if (!title || !description) return;
+ const copy = document.createElement("div");
+ copy.className = SKILL_COPY_CLASS;
+ inner.insertBefore(copy, title);
+ copy.appendChild(title);
+ copy.appendChild(description);
+ };
+
+ const sourceCommandRows = (scroller) =>
+ Array.from(scroller.querySelectorAll('[data-list-navigation-item="true"]')).filter(
+ (row) =>
+ row instanceof HTMLElement &&
+ !row.closest(`[${FAVORITES_GROUP_ATTR}="true"]`) &&
+ !row.hasAttribute(FAVORITE_CLONE_ATTR),
+ );
+
+ const syncSectionVisibility = (scroller) => {
+ Array.from(scroller.children).forEach((group) => {
+ if (!(group instanceof HTMLElement) || group.hasAttribute(TOPBAR_ATTR)) return;
+ const rows = Array.from(
+ group.querySelectorAll('[data-list-navigation-item="true"]'),
+ ).filter((row) => row instanceof HTMLElement);
+ const hasVisibleRows = rows.some(
+ (row) => !row.hasAttribute(FAVORITE_DUPLICATE_HIDDEN_ATTR),
+ );
+ group.setAttribute(SECTION_EMPTY_ATTR, hasVisibleRows ? "false" : "true");
+ });
+ };
+
+ const syncFavoriteSourceVisibility = (scroller, favoriteKeys = new Set()) => {
+ const hideDuplicates = isSlashSearchActive() && favoriteKeys.size > 0;
+ let hiddenSelectedKey = "";
+ sourceCommandRows(scroller).forEach((row) => {
+ const key = row.getAttribute(FAVORITE_KEY_ATTR) || rowFavoriteKey(row);
+ if (hideDuplicates && key && favoriteKeys.has(key)) {
+ if (row.getAttribute("aria-selected") === "true") hiddenSelectedKey = key;
+ row.setAttribute(FAVORITE_DUPLICATE_HIDDEN_ATTR, "true");
+ } else {
+ row.removeAttribute(FAVORITE_DUPLICATE_HIDDEN_ATTR);
+ }
+ });
+ syncSectionVisibility(scroller);
+ if (!hiddenSelectedKey) return;
+ const favorite = favoriteRows(scroller).find(
+ (row) => row.getAttribute(FAVORITE_KEY_ATTR) === hiddenSelectedKey,
+ );
+ if (favorite instanceof HTMLElement) selectNavigationRow(scroller, favorite);
+ };
+
+ const syncFavoriteControls = (scroller) => {
+ const favorites = readFavorites();
+ sourceCommandRows(scroller).forEach((row) => {
+ const key = row.getAttribute(FAVORITE_KEY_ATTR) || rowFavoriteKey(row);
+ ensureFavoriteControl(row, key, favorites);
+ });
+ scroller
+ .querySelectorAll(`[${FAVORITE_CLONE_ATTR}="true"]`)
+ .forEach((row) => {
+ if (!(row instanceof HTMLElement)) return;
+ stripNativeCommandState(row);
+ const key = row.getAttribute(FAVORITE_KEY_ATTR) || rowFavoriteKey(row, "favorites");
+ ensureFavoriteControl(row, key, favorites);
+ });
+ };
+
+ const removeFavoriteSection = (scroller) => {
+ scroller
+ .querySelectorAll(`:scope > [${FAVORITES_GROUP_ATTR}="true"]`)
+ .forEach((group) => group.remove());
+ syncFavoriteSourceVisibility(scroller);
+ syncSectionVisibility(scroller);
+ delete scroller.dataset.codexppSlashFavoriteSelectionReady;
+ delete scroller.dataset.codexppSlashFavoriteSelectionTouched;
+ };
+
+ const syncFavoritesSection = (scroller) => {
+ const favorites = readFavorites();
+ const rowsByKey = new Map();
+ sourceCommandRows(scroller).forEach((row) => {
+ const key = row.getAttribute(FAVORITE_KEY_ATTR) || rowFavoriteKey(row);
+ if (key && favorites.has(key) && !rowsByKey.has(key)) rowsByKey.set(key, row);
+ });
+ const entries = Array.from(rowsByKey.entries());
+ if (entries.length === 0) {
+ removeFavoriteSection(scroller);
+ return;
+ }
+ const entryKeys = new Set(entries.map(([key]) => key));
+ syncFavoriteSourceVisibility(scroller, entryKeys);
+
+ let group = scroller.querySelector(`:scope > [${FAVORITES_GROUP_ATTR}="true"]`);
+ if (!(group instanceof HTMLElement)) {
+ group = document.createElement("div");
+ group.setAttribute(FAVORITES_GROUP_ATTR, "true");
+ scroller.insertBefore(group, scroller.firstElementChild);
+ } else if (group !== scroller.firstElementChild) {
+ scroller.insertBefore(group, scroller.firstElementChild);
+ }
+
+ const signature = entries.map(([key]) => key).join("|");
+ if (group.dataset.signature === signature) return;
+ group.dataset.signature = signature;
+ delete scroller.dataset.codexppSlashFavoriteSelectionReady;
+ delete scroller.dataset.codexppSlashFavoriteSelectionTouched;
+ group.replaceChildren();
+
+ const header = document.createElement("div");
+ header.className = "sticky top-0";
+ header.textContent = "Favorites";
+ group.appendChild(header);
+
+ entries.forEach(([key, sourceRow]) => {
+ const clone = sourceRow.cloneNode(true);
+ if (!(clone instanceof HTMLElement)) return;
+ clone.setAttribute(FAVORITE_CLONE_ATTR, "true");
+ clone.setAttribute(FAVORITE_KEY_ATTR, key);
+ clone.setAttribute(
+ FAVORITE_SOURCE_SECTION_ATTR,
+ sourceRow.getAttribute(SECTION_ATTR) || "",
+ );
+ stripNativeCommandState(clone);
+ clone.removeAttribute("aria-selected");
+ clone.querySelectorAll(`.${FAVORITE_BUTTON_CLASS}`).forEach((node) => node.remove());
+ ["pointermove", "mousemove", "mouseover"].forEach((type) => {
+ clone.addEventListener(type, (event) => {
+ event.stopPropagation();
+ });
+ });
+ clone.addEventListener("click", (event) => {
+ if (event.target instanceof HTMLElement && event.target.closest(`.${FAVORITE_BUTTON_CLASS}`)) {
+ return;
+ }
+ event.preventDefault();
+ event.stopPropagation();
+ sourceRow.click();
+ });
+ clone.addEventListener("keydown", (event) => {
+ if (event.key !== "Enter" && event.key !== " ") return;
+ event.preventDefault();
+ event.stopPropagation();
+ sourceRow.click();
+ });
+ group.appendChild(clone);
+ });
+ };
+
+ const navigationRows = (scroller) =>
+ Array.from(scroller.querySelectorAll('[data-list-navigation-item="true"]')).filter(
+ (row) => row instanceof HTMLElement && row.offsetParent !== null,
+ );
+
+ const favoriteRows = (scroller) =>
+ Array.from(
+ scroller.querySelectorAll(
+ `[${FAVORITES_GROUP_ATTR}="true"] [data-list-navigation-item="true"]`,
+ ),
+ ).filter((row) => row instanceof HTMLElement && row.offsetParent !== null);
+
+ const selectedNavigationRow = (scroller) =>
+ navigationRows(scroller).find(
+ (row) => row.getAttribute("aria-selected") === "true",
+ );
+
+ const reconcileFavoriteSelection = (scroller) => {
+ const rows = navigationRows(scroller);
+ const selected = rows.filter((row) => row.getAttribute("aria-selected") === "true");
+ if (selected.length <= 1) return;
+ const favoriteSelected =
+ selected.find((row) => row.hasAttribute(FAVORITE_CLONE_ATTR)) || selected[0];
+ rows.forEach((row) =>
+ row.setAttribute("aria-selected", row === favoriteSelected ? "true" : "false"),
+ );
+ };
+
+ const selectNavigationRow = (scroller, row, options = {}) => {
+ if (!(row instanceof HTMLElement)) return;
+ const menu = scroller.closest(`[${MENU_ATTR}="true"]`);
+ if (options.inputMode !== false) {
+ menu?.setAttribute(INPUT_MODE_ATTR, options.inputMode || "keyboard");
+ }
+ navigationRows(scroller).forEach((item) =>
+ item.setAttribute("aria-selected", item === row ? "true" : "false"),
+ );
+ allowSlashRowScrollIntoView();
+ row.scrollIntoView({ block: "nearest" });
+ updateTopbar(scroller);
+ };
+
+ const ensureInitialFavoriteSelection = (scroller) => {
+ if (scroller.dataset.codexppSlashFavoriteSelectionReady === "true") return;
+ if (scroller.closest(`[${MENU_ATTR}="true"]`)?.hasAttribute(HOVER_SUPPRESS_ATTR)) return;
+ const firstFavorite = favoriteRows(scroller)[0];
+ if (!(firstFavorite instanceof HTMLElement)) return;
+ selectNavigationRow(scroller, firstFavorite, { inputMode: false });
+ scroller.dataset.codexppSlashFavoriteSelectionReady = "true";
+ const keepFavoriteSelected = () => {
+ if (!scroller.isConnected) return;
+ if (scroller.closest(`[${MENU_ATTR}="true"]`)?.hasAttribute(HOVER_SUPPRESS_ATTR)) return;
+ if (scroller.dataset.codexppSlashFavoriteSelectionTouched === "true") return;
+ const nextFirstFavorite = favoriteRows(scroller)[0];
+ if (!(nextFirstFavorite instanceof HTMLElement)) return;
+ if (selectedNavigationRow(scroller) !== nextFirstFavorite) {
+ selectNavigationRow(scroller, nextFirstFavorite);
+ }
+ };
+ requestAnimationFrame(keepFavoriteSelected);
+ window.setTimeout(keepFavoriteSelected, 80);
+ };
+
+ const handleFavoriteNavigationKey = (event, scroller) => {
+ const rows = navigationRows(scroller);
+ const favs = favoriteRows(scroller);
+ if (rows.length === 0 || favs.length === 0) return false;
+
+ if (event.key === "Enter") {
+ const selected = selectedNavigationRow(scroller);
+ if (!(selected instanceof HTMLElement)) return false;
+ scroller.dataset.codexppSlashFavoriteSelectionTouched = "true";
+ event.preventDefault();
+ event.stopPropagation();
+ event.stopImmediatePropagation?.();
+ selected.click();
+ return true;
+ }
+
+ if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return false;
+ scroller.dataset.codexppSlashFavoriteSelectionTouched = "true";
+ event.preventDefault();
+ event.stopPropagation();
+ event.stopImmediatePropagation?.();
+
+ const selected = selectedNavigationRow(scroller);
+ const currentIndex = selected instanceof HTMLElement ? rows.indexOf(selected) : -1;
+ const fallbackIndex = event.key === "ArrowDown" ? 0 : rows.length - 1;
+ const nextIndex =
+ currentIndex < 0
+ ? fallbackIndex
+ : event.key === "ArrowDown"
+ ? Math.min(rows.length - 1, currentIndex + 1)
+ : Math.max(0, currentIndex - 1);
+ selectNavigationRow(scroller, rows[nextIndex]);
+ return true;
+ };
+
+ const cleanupMenu = (menu) => {
+ const overlay = menu.closest('[data-composer-overlay-floating-ui="true"]');
+ const scroller = menu.querySelector(".vertical-scroll-fade-mask");
+ if (scroller instanceof HTMLElement) {
+ const scrollHandler = scrollHandlers.get(scroller);
+ if (scrollHandler) {
+ scroller.removeEventListener("scroll", scrollHandler);
+ scrollHandlers.delete(scroller);
+ }
+ const animation = scrollAnimations.get(scroller);
+ if (animation) {
+ cancelScrollAnimation(scroller);
+ }
+ const pointerHandler = pointerHandlers.get(scroller);
+ if (pointerHandler) {
+ scroller.removeEventListener("pointermove", pointerHandler);
+ scroller.removeEventListener("pointerdown", pointerHandler);
+ pointerHandlers.delete(scroller);
+ }
+ const wheelHandler = wheelHandlers.get(scroller);
+ if (wheelHandler) {
+ scroller.removeEventListener("wheel", wheelHandler);
+ wheelHandlers.delete(scroller);
+ }
+ const hoverGuardHandler = hoverGuardHandlers.get(scroller);
+ if (hoverGuardHandler) {
+ HOVER_GUARD_EVENTS.forEach((type) =>
+ scroller.removeEventListener(type, hoverGuardHandler, true),
+ );
+ hoverGuardHandlers.delete(scroller);
+ }
+ }
+ if (scroller instanceof HTMLElement) removeFavoriteSection(scroller);
+ menu.removeAttribute(MENU_ATTR);
+ menu.removeAttribute(INPUT_MODE_ATTR);
+ menu.removeAttribute(PROGRAM_SCROLL_ATTR);
+ menu.removeAttribute(HOVER_SUPPRESS_ATTR);
+ menu.querySelectorAll(`[${TOPBAR_ATTR}]`).forEach((node) => node.remove());
+ menu.querySelectorAll(`.${FAVORITE_BUTTON_CLASS}`).forEach((node) => node.remove());
+ menu.querySelectorAll(`.${SKILL_COPY_CLASS}`).forEach((copy) => {
+ if (!(copy instanceof HTMLElement) || !copy.parentElement) return;
+ while (copy.firstChild) copy.parentElement.insertBefore(copy.firstChild, copy);
+ copy.remove();
+ });
+ menu
+ .querySelectorAll(
+ `[${FAVORITE_KEY_ATTR}], [${FAVORITE_CLONE_ATTR}], [${FAVORITE_SOURCE_SECTION_ATTR}], [${FAVORITE_DUPLICATE_HIDDEN_ATTR}]`,
+ )
+ .forEach((node) => {
+ node.removeAttribute(FAVORITE_KEY_ATTR);
+ node.removeAttribute(FAVORITE_CLONE_ATTR);
+ node.removeAttribute(FAVORITE_SOURCE_SECTION_ATTR);
+ node.removeAttribute(FAVORITE_DUPLICATE_HIDDEN_ATTR);
+ });
+ menu
+ .querySelectorAll(`[${SECTION_ATTR}]`)
+ .forEach((node) => {
+ node.removeAttribute(SECTION_ATTR);
+ node.removeAttribute(SECTION_EMPTY_ATTR);
+ });
+ if (overlay && !overlay.querySelector(`[${MENU_ATTR}="true"]`)) {
+ overlay.removeAttribute(OVERLAY_ATTR);
+ markOverlayNoise(overlay);
+ }
+ };
+
+ const isSlashMenu = (menu) => {
+ if (!menu.closest('[data-composer-overlay-floating-ui="true"]')) return false;
+ if (isEmptySlashMenu(menu)) return true;
+ const scroller = menu.querySelector(".vertical-scroll-fade-mask");
+ if (!(scroller instanceof HTMLElement)) return false;
+ if (
+ isSlashQueryActive() &&
+ menu.querySelectorAll('[data-list-navigation-item="true"]').length > 0
+ ) {
+ return true;
+ }
+ return Array.from(scroller.children).some((group) => {
+ if (!(group instanceof HTMLElement)) return false;
+ const rows = group.querySelectorAll('[data-list-navigation-item="true"]');
+ if (rows.length < 2) return false;
+ const header = group.querySelector(":scope > .sticky.top-0");
+ const headerText = normText(header);
+ if (!/^Skills\b/i.test(headerText)) return false;
+ return Array.from(rows).some((row) =>
+ row.querySelector(
+ '.text-token-description-foreground, span[class*="text-token-description-foreground"]',
+ ),
+ );
+ });
+ };
+
+ const isSlashQueryActive = () =>
+ Array.from(document.querySelectorAll('.ProseMirror[contenteditable="true"]')).some(
+ (editor) => editor instanceof HTMLElement && normText(editor).startsWith("/"),
+ );
+
+ const isEmptySlashMenu = (menu) =>
+ isSlashQueryActive() &&
+ menu.querySelectorAll('[data-list-navigation-item="true"]').length === 0 &&
+ /^No commands$/i.test(normText(menu));
+
+ const buildTopbar = (menu, scroller) => {
+ let topbar = menu.querySelector(`:scope > [${TOPBAR_ATTR}="true"]`);
+ if (topbar instanceof HTMLElement) return topbar;
+ topbar = document.createElement("div");
+ topbar.setAttribute(TOPBAR_ATTR, "true");
+ topbar.innerHTML =
+ `General
` +
+ '';
+ menu.insertBefore(topbar, scroller);
+ return topbar;
+ };
+
+ const setTopbarTitle = (title, text) => {
+ if (!(title instanceof HTMLElement) || title.textContent === text) return;
+ title.textContent = text;
+ title.setAttribute("data-changing", "true");
+ const previousTimer = titleTimers.get(title);
+ if (previousTimer) window.clearTimeout(previousTimer);
+ const timer = window.setTimeout(() => {
+ title.removeAttribute("data-changing");
+ titleTimers.delete(title);
+ }, 190);
+ titleTimers.set(title, timer);
+ };
+
+ const groupSections = (scroller) =>
+ Array.from(scroller.children)
+ .filter(
+ (node) =>
+ node instanceof HTMLElement &&
+ !node.hasAttribute(TOPBAR_ATTR) &&
+ node.getAttribute(SECTION_EMPTY_ATTR) !== "true" &&
+ node.querySelector('[data-list-navigation-item="true"]'),
+ )
+ .map((group, index) => {
+ const header = group.querySelector(":scope > .sticky.top-0");
+ const isFavorites = group.hasAttribute(FAVORITES_GROUP_ATTR);
+ const title = isFavorites ? "Favorites" : normText(header) || "General";
+ const key = sectionKey(title);
+ const color = sectionColor(key, index);
+ const favorites = readFavorites();
+ group.setAttribute(SECTION_ATTR, key);
+ group.dataset.codexppSlashSectionTitle = title;
+ group.style.setProperty("--codexpp-section-color", color);
+ group.querySelectorAll('[data-list-navigation-item="true"]').forEach((button) => {
+ if (!(button instanceof HTMLElement)) return;
+ if (button.hasAttribute(FAVORITE_CLONE_ATTR)) stripNativeCommandState(button);
+ button.setAttribute(SECTION_ATTR, key);
+ button.style.setProperty("--codexpp-section-color", color);
+ const visualKey =
+ button.getAttribute(FAVORITE_SOURCE_SECTION_ATTR) ||
+ button.getAttribute(SECTION_ATTR) ||
+ key;
+ if (visualKey === "skills") wrapSkillRow(button);
+ else unwrapSkillRow(button);
+ const favoriteKey =
+ button.getAttribute(FAVORITE_KEY_ATTR) || rowFavoriteKey(button, key);
+ ensureFavoriteControl(button, favoriteKey, favorites);
+ });
+ return { group, title, key, color };
+ });
+
+ const renderTopbarIcons = (topbar, sections) => {
+ const icons = topbar.querySelector(".codexpp-slash-section-icons");
+ if (!(icons instanceof HTMLElement)) return;
+ const signature = sections.map((s) => `${s.key}:${s.title}`).join("|");
+ if (icons.dataset.signature === signature) return;
+ icons.dataset.signature = signature;
+ icons.replaceChildren();
+ for (const section of sections) {
+ const button = document.createElement("button");
+ button.type = "button";
+ button.setAttribute(SECTION_ICON_ATTR, section.key);
+ button.setAttribute("aria-label", section.title);
+ button.style.setProperty("--codexpp-section-color", section.color);
+ button.innerHTML = sectionIconSvg(section.key);
+ button.addEventListener("click", (event) => {
+ event.preventDefault();
+ event.stopPropagation();
+ const scroller = topbar.nextElementSibling;
+ if (!(scroller instanceof HTMLElement)) return;
+ scrollToSection(scroller, section, sections);
+ updateTopbar(scroller, sections);
+ });
+ icons.appendChild(button);
+ }
+ };
+
+ const scrollToSection = (scroller, section, sections = groupSections(scroller)) => {
+ const menu = scroller.closest(`[${MENU_ATTR}="true"]`);
+ menu?.setAttribute(INPUT_MODE_ATTR, "keyboard");
+ menu?.setAttribute(PROGRAM_SCROLL_ATTR, "true");
+ if (menu instanceof HTMLElement) suppressHoverUntilPointerMoves(menu);
+ scroller.dataset.codexppSlashFavoriteSelectionTouched = "true";
+ scroller.scrollLeft = 0;
+ const targetTop = sectionTop(scroller, section.group);
+ const adjustedTop =
+ sections.indexOf(section) > 0
+ ? Math.min(targetTop + 1, scroller.scrollHeight - scroller.clientHeight)
+ : targetTop;
+ allowProgrammaticScroll(scroller, adjustedTop);
+ const topbar = scroller.previousElementSibling;
+ if (topbar instanceof HTMLElement) {
+ topbar.dataset.forcedActiveSection = section.key;
+ }
+ updateTopbar(scroller, sections);
+ animateScrollTop(
+ scroller,
+ adjustedTop,
+ () => updateTopbar(scroller, sections),
+ () => {
+ menu?.removeAttribute(PROGRAM_SCROLL_ATTR);
+ if (topbar instanceof HTMLElement) delete topbar.dataset.forcedActiveSection;
+ updateTopbar(scroller, sections);
+ },
+ );
+ updateTopbar(scroller, sections);
+ };
+
+ const sectionTop = (scroller, group) => {
+ const target =
+ scroller.scrollTop +
+ group.getBoundingClientRect().top -
+ scroller.getBoundingClientRect().top;
+ return Math.max(0, Math.min(target, scroller.scrollHeight - scroller.clientHeight));
+ };
+
+ const cancelScrollAnimation = (scroller) => {
+ const animation = scrollAnimations.get(scroller);
+ if (!animation) return;
+ cancelAnimationFrame(animation.frame);
+ window.clearTimeout(animation.timer);
+ scrollAnimations.delete(scroller);
+ };
+
+ const animateScrollTop = (scroller, targetTop, onStep, onDone) => {
+ cancelScrollAnimation(scroller);
+ const startTop = scroller.scrollTop;
+ const delta = targetTop - startTop;
+ if (Math.abs(delta) < 1) {
+ scroller.scrollTop = targetTop;
+ onStep?.();
+ onDone?.();
+ return;
+ }
+ const start = performance.now();
+ const duration = 260;
+ const scheduleTick = () => {
+ const animation = { frame: 0, timer: 0 };
+ const run = (now = performance.now()) => {
+ if (scrollAnimations.get(scroller) !== animation) return;
+ cancelAnimationFrame(animation.frame);
+ window.clearTimeout(animation.timer);
+ tick(now);
+ };
+ animation.frame = requestAnimationFrame(run);
+ animation.timer = window.setTimeout(() => run(performance.now()), 16);
+ scrollAnimations.set(scroller, animation);
+ };
+ const tick = (now) => {
+ const progress = Math.min(1, (now - start) / duration);
+ const eased = 1 - Math.pow(1 - progress, 3);
+ scroller.scrollTop = startTop + delta * eased;
+ onStep?.();
+ if (progress < 1) {
+ scheduleTick();
+ } else {
+ scrollAnimations.delete(scroller);
+ onDone?.();
+ }
+ };
+ scheduleTick();
+ };
+
+ const updateTopbar = (scroller, sections = groupSections(scroller)) => {
+ const topbar = scroller.previousElementSibling;
+ if (!(topbar instanceof HTMLElement) || !topbar.hasAttribute(TOPBAR_ATTR)) return;
+ if (!(topbar instanceof HTMLElement) || sections.length === 0) return;
+ const threshold = scroller.scrollTop + 4;
+ let active = sections[0];
+ for (const section of sections) {
+ if (sectionTop(scroller, section.group) <= threshold) active = section;
+ }
+ if (topbar.dataset.forcedActiveSection) {
+ active =
+ sections.find((section) => section.key === topbar.dataset.forcedActiveSection) ||
+ active;
+ }
+ const title = topbar.querySelector(`[${SECTION_TITLE_ATTR}]`);
+ setTopbarTitle(title, active.title);
+ topbar.dataset.activeSection = active.key;
+ topbar.style.setProperty("--codexpp-section-color", active.color);
+ topbar
+ .querySelectorAll(`[${SECTION_ICON_ATTR}]`)
+ .forEach((button) =>
+ button.setAttribute(
+ "data-active",
+ button.getAttribute(SECTION_ICON_ATTR) === active.key ? "true" : "false",
+ ),
+ );
+ };
+
+ const enhanceMenu = (menu) => {
+ if (!isSlashMenu(menu)) {
+ cleanupMenu(menu);
+ return;
+ }
+ menu.setAttribute(MENU_ATTR, "true");
+ menu.closest('[data-composer-overlay-floating-ui="true"]')?.setAttribute(OVERLAY_ATTR, "true");
+ pruneMenuNoise(menu);
+ if (isEmptySlashMenu(menu)) {
+ menu.querySelectorAll(`[${TOPBAR_ATTR}]`).forEach((node) => node.remove());
+ return;
+ }
+ const scroller = menu.querySelector(".vertical-scroll-fade-mask");
+ if (!(scroller instanceof HTMLElement)) {
+ menu.querySelectorAll(`[${TOPBAR_ATTR}]`).forEach((node) => node.remove());
+ return;
+ }
+ scroller.scrollLeft = 0;
+ const topbar = buildTopbar(menu, scroller);
+ groupSections(scroller);
+ syncFavoritesSection(scroller);
+ const sections = groupSections(scroller);
+ renderTopbarIcons(topbar, sections);
+ updateTopbar(scroller, sections);
+ ensureInitialFavoriteSelection(scroller);
+ reconcileFavoriteSelection(scroller);
+ if (!scrollHandlers.has(scroller)) {
+ const handler = () => {
+ enforceHoverScrollFreeze(scroller);
+ updateTopbar(scroller);
+ };
+ scroller.addEventListener("scroll", handler, { passive: true });
+ scrollHandlers.set(scroller, handler);
+ }
+ hoverScrollStateFor(scroller).lastTop = scroller.scrollTop;
+ if (!pointerHandlers.has(scroller)) {
+ const handler = (event) => {
+ if (menu.hasAttribute(PROGRAM_SCROLL_ATTR)) return;
+ if (event.type === "pointermove") {
+ if (!menu.hasAttribute(HOVER_SUPPRESS_ATTR)) {
+ menu.setAttribute(INPUT_MODE_ATTR, "pointer");
+ }
+ return;
+ }
+ menu.setAttribute(INPUT_MODE_ATTR, "pointer");
+ };
+ scroller.addEventListener("pointermove", handler, { passive: true });
+ scroller.addEventListener("pointerdown", handler, { passive: true });
+ pointerHandlers.set(scroller, handler);
+ }
+ if (!wheelHandlers.has(scroller)) {
+ const handler = () => clearHoverScrollFreeze(scroller);
+ scroller.addEventListener("wheel", handler, { passive: true });
+ wheelHandlers.set(scroller, handler);
+ }
+ if (!hoverGuardHandlers.has(scroller)) {
+ const handler = (event) => {
+ stopHoverSelectionEvent(menu, event);
+ };
+ HOVER_GUARD_EVENTS.forEach((type) => scroller.addEventListener(type, handler, true));
+ hoverGuardHandlers.set(scroller, handler);
+ }
+ };
+
+ const activeSlashMenu = () =>
+ Array.from(document.querySelectorAll(`[${MENU_ATTR}="true"]`)).find(
+ (menu) =>
+ menu instanceof HTMLElement &&
+ menu.isConnected &&
+ menu.querySelector(".vertical-scroll-fade-mask"),
+ );
+
+ installDocumentHoverGuard();
+ Element.prototype.scrollIntoView = patchedScrollIntoView;
+
+ const keyDigit = (event) => {
+ const key = String(event.key || "");
+ if (/^[1-9]$/.test(key)) return Number(key);
+ const code = String(event.code || "");
+ const match = /^(?:Digit|Numpad)([1-9])$/.exec(code);
+ return match ? Number(match[1]) : 0;
+ };
+
+ const onSectionShortcut = (event) => {
+ const menu = activeSlashMenu();
+ if (!(menu instanceof HTMLElement)) return;
+ const scroller = menu.querySelector(".vertical-scroll-fade-mask");
+ if (!(scroller instanceof HTMLElement)) return;
+
+ if (handleFavoriteNavigationKey(event, scroller)) return;
+
+ if (
+ event.key === "ArrowDown" ||
+ event.key === "ArrowUp" ||
+ event.key === "Home" ||
+ event.key === "End" ||
+ event.key === "PageDown" ||
+ event.key === "PageUp"
+ ) {
+ allowSlashRowScrollIntoView();
+ menu.setAttribute(INPUT_MODE_ATTR, "keyboard");
+ return;
+ }
+
+ if (!(event.metaKey || event.ctrlKey) || event.altKey || event.shiftKey) return;
+ const digit = keyDigit(event);
+ activateSectionByDigit(scroller, digit, event);
+ };
+
+ const onSectionShortcutBridge = (event) => {
+ const menu = activeSlashMenu();
+ if (!(menu instanceof HTMLElement)) return;
+ const scroller = menu.querySelector(".vertical-scroll-fade-mask");
+ if (!(scroller instanceof HTMLElement)) return;
+ activateSectionByDigit(scroller, Number(event.detail?.digit) || 0, event);
+ };
+
+ const activateSectionByDigit = (scroller, digit, event) => {
+ if (!digit) return;
+ const sections = groupSections(scroller);
+ const section = sections[digit - 1];
+ if (!section) return;
+ event.preventDefault();
+ event.stopPropagation();
+ event.stopImmediatePropagation?.();
+ scrollToSection(scroller, section, sections);
+ };
+
+ const scan = () => {
+ scanFrame = 0;
+ try {
+ pruneHomeContentNoise();
+ } catch {
+ // Ignore transient DOM shapes while Codex is replacing the slash panel.
+ }
+ document
+ .querySelectorAll('[data-composer-overlay-floating-ui="true"]')
+ .forEach((overlay) => {
+ if (!(overlay instanceof HTMLElement)) return;
+ try {
+ pruneOverlayNoise(overlay);
+ markOverlayNoise(overlay);
+ } catch {
+ // Keep the observer alive if Codex swaps the overlay mid-scan.
+ }
+ });
+ document
+ .querySelectorAll('[data-composer-overlay-floating-ui="true"] > *')
+ .forEach((menu) => {
+ if (!(menu instanceof HTMLElement)) return;
+ try {
+ enhanceMenu(menu);
+ } catch {
+ // Keep scanning other candidates.
+ }
+ });
+ };
+
+ const scheduleScan = () => {
+ if (scanFrame || scanTimer) return;
+ const run = () => {
+ if (scanFrame) cancelAnimationFrame(scanFrame);
+ if (scanTimer) window.clearTimeout(scanTimer);
+ scanFrame = 0;
+ scanTimer = 0;
+ scan();
+ };
+ scanFrame = requestAnimationFrame(run);
+ scanTimer = window.setTimeout(run, 60);
+ };
+
+ const scheduleSlashWork = () => {
+ scheduleHomeContentPrune();
+ scheduleHardPruneNoise();
+ scheduleScan();
+ };
+
+ scan();
+ observer = new MutationObserver(scheduleSlashWork);
+ observer.observe(document.body, OBSERVER_OPTIONS);
+ document.addEventListener("input", scheduleSlashWork, true);
+ document.addEventListener("keyup", scheduleSlashWork, true);
+ window.addEventListener("codexpp-slash-section-shortcut", onSectionShortcutBridge);
+ window.addEventListener("keydown", onSectionShortcut, true);
+ document.addEventListener("keydown", onSectionShortcut, true);
+ const activeSlashInterval = window.setInterval(() => {
+ if (isSlashQueryActive()) scheduleSlashWork();
+ }, 250);
+
+ return () => {
+ disposed = true;
+ observer.disconnect();
+ window.clearInterval(activeSlashInterval);
+ if (scanFrame) cancelAnimationFrame(scanFrame);
+ if (scanTimer) window.clearTimeout(scanTimer);
+ if (homePruneFrame) cancelAnimationFrame(homePruneFrame);
+ if (hardPruneTimer) window.clearTimeout(hardPruneTimer);
+ document.removeEventListener("input", scheduleSlashWork, true);
+ document.removeEventListener("keyup", scheduleSlashWork, true);
+ window.removeEventListener("codexpp-slash-section-shortcut", onSectionShortcutBridge);
+ window.removeEventListener("keydown", onSectionShortcut, true);
+ document.removeEventListener("keydown", onSectionShortcut, true);
+ for (const [scroller, handler] of scrollHandlers) {
+ scroller.removeEventListener("scroll", handler);
+ }
+ scrollHandlers.clear();
+ for (const [scroller, handler] of pointerHandlers) {
+ scroller.removeEventListener("pointermove", handler);
+ scroller.removeEventListener("pointerdown", handler);
+ }
+ pointerHandlers.clear();
+ for (const [scroller, handler] of wheelHandlers) {
+ scroller.removeEventListener("wheel", handler);
+ }
+ wheelHandlers.clear();
+ for (const [scroller, handler] of hoverGuardHandlers) {
+ HOVER_GUARD_EVENTS.forEach((type) =>
+ scroller.removeEventListener(type, handler, true),
+ );
+ }
+ hoverGuardHandlers.clear();
+ if (documentHoverGuard) {
+ HOVER_GUARD_EVENTS.forEach((type) =>
+ window.removeEventListener(type, documentHoverGuard, true),
+ );
+ HOVER_GUARD_EVENTS.forEach((type) =>
+ document.removeEventListener(type, documentHoverGuard, true),
+ );
+ documentHoverGuard = null;
+ }
+ if (Element.prototype.scrollIntoView === patchedScrollIntoView) {
+ Element.prototype.scrollIntoView = nativeScrollIntoView;
+ }
+ for (const animation of scrollAnimations.values()) {
+ cancelAnimationFrame(animation.frame);
+ window.clearTimeout(animation.timer);
+ }
+ scrollAnimations.clear();
+ for (const timer of titleTimers.values()) window.clearTimeout(timer);
+ titleTimers.clear();
+ document
+ .querySelectorAll(`[${FAVORITES_GROUP_ATTR}]`)
+ .forEach((node) => node.remove());
+ document.querySelectorAll(`.${FAVORITE_BUTTON_CLASS}`).forEach((node) => node.remove());
+ document.querySelectorAll(`.${SKILL_COPY_CLASS}`).forEach((copy) => {
+ if (!(copy instanceof HTMLElement) || !copy.parentElement) return;
+ while (copy.firstChild) copy.parentElement.insertBefore(copy.firstChild, copy);
+ copy.remove();
+ });
+ document.querySelectorAll(`[${TOPBAR_ATTR}]`).forEach((node) => node.remove());
+ document
+ .querySelectorAll(`[${MENU_ATTR}]`)
+ .forEach((node) => node.removeAttribute(MENU_ATTR));
+ document
+ .querySelectorAll(`[${INPUT_MODE_ATTR}]`)
+ .forEach((node) => node.removeAttribute(INPUT_MODE_ATTR));
+ document
+ .querySelectorAll(`[${PROGRAM_SCROLL_ATTR}]`)
+ .forEach((node) => node.removeAttribute(PROGRAM_SCROLL_ATTR));
+ document
+ .querySelectorAll(`[${HOVER_SUPPRESS_ATTR}]`)
+ .forEach((node) => node.removeAttribute(HOVER_SUPPRESS_ATTR));
+ document
+ .querySelectorAll(`[${OVERLAY_ATTR}]`)
+ .forEach((node) => node.removeAttribute(OVERLAY_ATTR));
+ document
+ .querySelectorAll(`[${OVERLAY_NOISE_ATTR}]`)
+ .forEach((node) => node.removeAttribute(OVERLAY_NOISE_ATTR));
+ document
+ .querySelectorAll(`[${SECTION_ATTR}]`)
+ .forEach((node) => {
+ node.removeAttribute(SECTION_ATTR);
+ node.removeAttribute(SECTION_EMPTY_ATTR);
+ });
+ document
+ .querySelectorAll(
+ `[${FAVORITE_KEY_ATTR}], [${FAVORITE_CLONE_ATTR}], [${FAVORITE_SOURCE_SECTION_ATTR}], [${FAVORITE_DUPLICATE_HIDDEN_ATTR}]`,
+ )
+ .forEach((node) => {
+ node.removeAttribute(FAVORITE_KEY_ATTR);
+ node.removeAttribute(FAVORITE_CLONE_ATTR);
+ node.removeAttribute(FAVORITE_SOURCE_SECTION_ATTR);
+ node.removeAttribute(FAVORITE_DUPLICATE_HIDDEN_ATTR);
+ });
+ style.remove();
+ };
+ },
+
+ /**
+ * Add a compact search field to the Settings sidebar and filter the
+ * visible settings tabs in place. This is deliberately a tweak, not core
+ * Codex++, because it is a reversible UI convenience layer.
+ */
+ "settings-search"(api) {
+ const STYLE_ID = "codexpp-settings-search-style";
+ const ROOT_ATTR = "data-codexpp-settings-search";
+ const HIDDEN_ATTR = "data-codexpp-settings-search-hidden";
+ const PREV_DISPLAY_ATTR = "codexppSettingsSearchPrevDisplay";
+ const SIDEBAR_SELECTOR = ".window-fx-sidebar-surface.w-token-sidebar";
+
+ document.getElementById(STYLE_ID)?.remove();
+ const style = document.createElement("style");
+ style.id = STYLE_ID;
+ style.textContent = `
+ [${ROOT_ATTR}] {
+ padding: 0.75rem 0 0.5rem;
+ }
+
+ [${ROOT_ATTR}] .codexpp-settings-search-box {
+ position: relative;
+ display: flex;
+ align-items: center;
+ }
+
+ [${ROOT_ATTR}] svg {
+ position: absolute;
+ left: 0.625rem;
+ height: 1rem;
+ width: 1rem;
+ color: var(--color-token-text-secondary);
+ pointer-events: none;
+ }
+
+ [${ROOT_ATTR}] input {
+ width: 100%;
+ height: 2rem;
+ min-width: 0;
+ border-radius: var(--radius-md, 0.375rem);
+ border: 1px solid color-mix(in srgb, currentColor 13%, transparent);
+ background: color-mix(in srgb, currentColor 4%, transparent);
+ color: var(--color-token-text-primary);
+ font-size: 0.875rem;
+ line-height: 1.25rem;
+ padding: 0 0.625rem 0 2rem;
+ outline: none;
+ }
+
+ [${ROOT_ATTR}] input::placeholder {
+ color: var(--color-token-text-secondary);
+ }
+
+ [${ROOT_ATTR}] input:focus {
+ border-color: color-mix(in srgb, currentColor 18%, transparent);
+ box-shadow: none;
+ }
+
+ [${ROOT_ATTR}] .codexpp-settings-search-empty {
+ display: none;
+ padding-top: 1.25rem;
+ color: var(--color-token-text-secondary);
+ font-size: 0.75rem;
+ line-height: 1rem;
+ text-align: center;
+ }
+
+ [${ROOT_ATTR}][data-empty="true"] .codexpp-settings-search-empty {
+ display: block;
+ }
+
+ [${ROOT_ATTR}] .codexpp-settings-search-results {
+ display: none;
+ flex-direction: column;
+ gap: 0.125rem;
+ padding-top: 0.375rem;
+ }
+
+ [${ROOT_ATTR}][data-has-results="true"] .codexpp-settings-search-results {
+ display: flex;
+ }
+
+ [${ROOT_ATTR}] .codexpp-settings-search-result {
+ display: flex;
+ min-width: 0;
+ width: 100%;
+ align-items: center;
+ justify-content: space-between;
+ gap: 0.375rem;
+ border-radius: var(--radius-md, 0.375rem);
+ padding: 0.25rem 0.5rem;
+ color: var(--color-token-text-secondary);
+ font-size: 0.75rem;
+ line-height: 1rem;
+ text-align: left;
+ }
+
+ [${ROOT_ATTR}] .codexpp-settings-search-result:hover,
+ [${ROOT_ATTR}] .codexpp-settings-search-result:focus-visible {
+ background: color-mix(in srgb, currentColor 8%, transparent);
+ color: var(--color-token-text-primary);
+ outline: none;
+ }
+
+ [${ROOT_ATTR}] .codexpp-settings-search-result span {
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ [data-codexpp-settings-search-highlight="true"] {
+ outline: 2px solid var(--color-token-focus-border, var(--color-token-border));
+ outline-offset: 5px;
+ border-radius: var(--radius-md, 0.375rem);
+ transition:
+ outline-color 220ms ease,
+ outline-offset 220ms ease;
+ }
+
+ [data-codexpp-settings-search-highlight="fading"] {
+ outline: 2px solid transparent;
+ outline-offset: 9px;
+ border-radius: var(--radius-md, 0.375rem);
+ transition:
+ outline-color 420ms ease,
+ outline-offset 420ms ease;
+ }
+ `;
+ document.head.appendChild(style);
+
+ const root = document.createElement("div");
+ root.setAttribute(ROOT_ATTR, "true");
+
+ const box = document.createElement("div");
+ box.className = "codexpp-settings-search-box";
+ box.innerHTML =
+ '";
+
+ const input = document.createElement("input");
+ input.type = "search";
+ input.placeholder = "Search settings";
+ input.autocomplete = "off";
+ input.spellcheck = false;
+ input.setAttribute("aria-label", "Search settings");
+ box.appendChild(input);
+ root.appendChild(box);
+
+ const empty = document.createElement("div");
+ empty.className = "codexpp-settings-search-empty";
+ empty.textContent = "No matching settings";
+ root.appendChild(empty);
+
+ const results = document.createElement("div");
+ results.className = "codexpp-settings-search-results";
+ root.appendChild(results);
+
+ let scheduled = false;
+ let disposed = false;
+ let lastSidebar = null;
+ let highlightTimer = null;
+ const revealTimers = new Set();
+ const pageIndex = new Map();
+
+ const compact = (value) =>
+ String(value || "").replace(/\s+/g, " ").trim().toLowerCase();
+
+ const knownContent = [
+ {
+ page: "General",
+ title: "Work mode",
+ text: "work mode coding everyday technical detail",
+ },
+ {
+ page: "General",
+ title: "Permissions",
+ text: "permissions default permissions auto-review full access",
+ },
+ {
+ page: "General",
+ title: "General",
+ text: "general default open destination language show in menu bar prevent sleep follow-up behavior import other agent setup",
+ },
+ {
+ page: "General",
+ title: "Dictation",
+ text: "dictation hold-to-dictate hotkey toggle dictation hotkey dictation dictionary recent dictations",
+ },
+ {
+ page: "General",
+ title: "Dictation dictionary",
+ text: "dictation dictionary words phrases dictation should recognize",
+ },
+ {
+ page: "General",
+ title: "Notifications",
+ text: "notifications turn completion notifications permission notifications alerts",
+ },
+ ].map((item) => ({
+ ...item,
+ text: compact(`${item.title} ${item.text}`),
+ node: null,
+ }));
+
+ const labelFor = (node) =>
+ compact(
+ [
+ node.getAttribute?.("aria-label"),
+ node.getAttribute?.("title"),
+ node.textContent,
+ ]
+ .filter(Boolean)
+ .join(" "),
+ );
+
+ const visibleLabelFor = (node) => compact(node?.textContent || "");
+ const displayLabelFor = (node) =>
+ String(node?.textContent || "").replace(/\s+/g, " ").trim();
+
+ const findSettingsSidebar = () => {
+ const exact = document.querySelector(SIDEBAR_SELECTOR);
+ if (exact instanceof HTMLElement) return exact;
+ const candidates = Array.from(document.querySelectorAll("div")).filter(
+ (node) => {
+ if (!(node instanceof HTMLElement)) return false;
+ const rect = node.getBoundingClientRect();
+ if (rect.width < 180 || rect.width > 420 || rect.height < 240) return false;
+ const text = compact(node.textContent);
+ return (
+ text.includes("general") &&
+ text.includes("appearance") &&
+ (text.includes("configuration") || text.includes("account"))
+ );
+ },
+ );
+ return candidates[0] instanceof HTMLElement ? candidates[0] : null;
+ };
+
+ const findMount = (sidebar) => {
+ const groups = Array.from(sidebar.querySelectorAll("div")).filter(
+ (node) =>
+ node instanceof HTMLElement &&
+ node.classList.contains("flex") &&
+ node.classList.contains("flex-col") &&
+ node.classList.contains("gap-px") &&
+ Array.from(node.children).some(
+ (child) =>
+ child instanceof HTMLElement &&
+ child.matches("button, a") &&
+ visibleLabelFor(child) === "general",
+ ),
+ );
+ const itemsGroup = groups[0];
+ const outer = itemsGroup?.parentElement;
+ if (itemsGroup instanceof HTMLElement && outer instanceof HTMLElement) {
+ const header = Array.from(outer.children).find(
+ (child) =>
+ child instanceof HTMLElement &&
+ child !== root &&
+ !child.querySelector("button, a") &&
+ visibleLabelFor(child) === "general",
+ );
+ return {
+ parent: outer,
+ before: header instanceof HTMLElement ? header : itemsGroup,
+ };
+ }
+ const nav = sidebar.querySelector("nav");
+ return {
+ parent: nav instanceof HTMLElement ? nav : sidebar,
+ before: nav instanceof HTMLElement ? nav.firstElementChild : sidebar.firstElementChild,
+ };
+ };
+
+ const hide = (node, hidden) => {
+ if (!(node instanceof HTMLElement) || root.contains(node)) return;
+ if (hidden) {
+ if (node.getAttribute(HIDDEN_ATTR) === "true") return;
+ node.dataset[PREV_DISPLAY_ATTR] = node.style.display || "";
+ node.style.display = "none";
+ node.setAttribute(HIDDEN_ATTR, "true");
+ } else if (node.getAttribute(HIDDEN_ATTR) === "true") {
+ node.style.display = node.dataset[PREV_DISPLAY_ATTR] || "";
+ delete node.dataset[PREV_DISPLAY_ATTR];
+ node.removeAttribute(HIDDEN_ATTR);
+ }
+ };
+
+ const navigateToPage = (sidebar, page) => {
+ const nav = navForPage(sidebar, page);
+ if (!(nav instanceof HTMLElement)) return false;
+ hide(nav, false);
+ nav.click();
+ return true;
+ };
+
+ const restoreHidden = (scope = document) => {
+ scope.querySelectorAll(`[${HIDDEN_ATTR}="true"]`).forEach((node) => {
+ hide(node, false);
+ });
+ };
+
+ const visibleControlsIn = (node) =>
+ Array.from(node.querySelectorAll("button, a")).filter(
+ (control) =>
+ control instanceof HTMLElement &&
+ !root.contains(control) &&
+ control.getAttribute(HIDDEN_ATTR) !== "true",
+ );
+
+ const navControls = (sidebar) =>
+ Array.from(sidebar.querySelectorAll("button, a")).filter(
+ (node) => node instanceof HTMLElement && !root.contains(node),
+ );
+
+ const activePageLabel = (sidebar) => {
+ const active = navControls(sidebar).find((node) => {
+ const className = String(node.className || "");
+ return (
+ node.getAttribute("aria-current") === "page" ||
+ node.getAttribute("data-state") === "active" ||
+ className.includes("active") ||
+ className.includes("selection")
+ );
+ });
+ const activeLabel = displayLabelFor(active);
+ if (activeLabel) return titleCaseLabel(activeLabel);
+
+ const heading = document.querySelector(
+ ".main-surface .heading-base, .main-surface .electron\\:heading-lg, .main-surface [role='heading']",
+ );
+ const headingLabel = displayLabelFor(heading);
+ return headingLabel ? titleCaseLabel(headingLabel) : "Settings";
+ };
+
+ const titleCaseLabel = (value) => {
+ const raw = String(value || "").replace(/\s+/g, " ").trim();
+ return raw || "Settings";
+ };
+
+ const mainSurface = () => {
+ const surface = document.querySelector(".main-surface");
+ return surface instanceof HTMLElement ? surface : null;
+ };
+
+ const shortText = (node) =>
+ String(node?.textContent || "")
+ .replace(/\s+/g, " ")
+ .trim();
+
+ const sectionTitleFor = (node) => {
+ const candidates = [
+ ":scope > div:first-child .text-base",
+ ":scope > div:first-child [class*='heading']",
+ ":scope > div:first-child [role='heading']",
+ ".text-base.font-medium",
+ ".min-w-0.text-sm.text-token-text-primary",
+ ".text-sm.text-token-text-primary",
+ "button .text-sm",
+ "button span",
+ ];
+ for (const selector of candidates) {
+ const found = node.querySelector(selector);
+ const text = shortText(found);
+ if (text && text.length <= 80) return text;
+ }
+ const text = shortText(node);
+ return text.slice(0, 80);
+ };
+
+ const contentCandidates = () => {
+ const surface = mainSurface();
+ if (!surface) return [];
+ const nodes = Array.from(
+ surface.querySelectorAll(
+ "section, [class*='p-3'], button[class*='p-3'], button.flex.w-full",
+ ),
+ ).filter((node) => node instanceof HTMLElement);
+ return nodes.filter((node) => {
+ if (root.contains(node)) return false;
+ const rect = node.getBoundingClientRect();
+ if (rect.width < 120 || rect.height < 18) return false;
+ const text = shortText(node);
+ if (!text || text.length < 2) return false;
+ return !nodes.some(
+ (other) =>
+ other !== node &&
+ other instanceof HTMLElement &&
+ node.contains(other) &&
+ shortText(other) === text,
+ );
+ });
+ };
+
+ const updateCurrentPageIndex = (sidebar) => {
+ const page = activePageLabel(sidebar);
+ const items = [];
+ const seen = new Set();
+ for (const node of contentCandidates()) {
+ const title = sectionTitleFor(node);
+ const text = shortText(node);
+ const key = compact(title);
+ if (!title || seen.has(key)) continue;
+ seen.add(key);
+ items.push({ page, title, text: compact(`${title} ${text}`), node });
+ }
+ if (items.length > 0) pageIndex.set(page, items);
+ };
+
+ const contentMatches = (query) => {
+ if (!query) return [];
+ const matches = [];
+ const seen = new Set();
+ for (const item of knownContent) {
+ const key = `${item.page}:${item.title}`;
+ if (!item.text.includes(query) || seen.has(key)) continue;
+ seen.add(key);
+ matches.push(item);
+ }
+ for (const [page, items] of pageIndex.entries()) {
+ for (const item of items) {
+ const key = `${page}:${item.title}`;
+ if (!item.text.includes(query) || seen.has(key)) continue;
+ seen.add(key);
+ matches.push({ ...item, page });
+ if (matches.length >= 8) return matches;
+ }
+ }
+ return matches;
+ };
+
+ const navForPage = (sidebar, page) =>
+ navControls(sidebar).find((node) => visibleLabelFor(node) === compact(page));
+
+ const clearHighlight = () => {
+ document
+ .querySelectorAll("[data-codexpp-settings-search-highlight]")
+ .forEach((node) => node.removeAttribute("data-codexpp-settings-search-highlight"));
+ if (highlightTimer) {
+ window.clearTimeout(highlightTimer);
+ highlightTimer = null;
+ }
+ };
+
+ const fadeHighlight = (target) => {
+ if (target.getAttribute("data-codexpp-settings-search-highlight") !== "true") return;
+ target.setAttribute("data-codexpp-settings-search-highlight", "fading");
+ highlightTimer = window.setTimeout(clearHighlight, 450);
+ };
+
+ const findContentTarget = (match) => {
+ if (match.node instanceof HTMLElement && document.contains(match.node)) {
+ return match.node;
+ }
+ const title = compact(match.title);
+ const candidates = contentCandidates();
+ return (
+ candidates.find((node) => compact(sectionTitleFor(node)) === title) ||
+ candidates.find((node) => compact(shortText(node)).includes(title)) ||
+ null
+ );
+ };
+
+ const scrollToMatch = (match) => {
+ const target = findContentTarget(match);
+ if (!(target instanceof HTMLElement)) return false;
+ clearHighlight();
+ target.setAttribute("data-codexpp-settings-search-highlight", "true");
+ target.scrollIntoView({ block: "center", behavior: "smooth" });
+ highlightTimer = window.setTimeout(() => fadeHighlight(target), 3000);
+ return true;
+ };
+
+ const clearRevealTimers = () => {
+ for (const timer of revealTimers) window.clearTimeout(timer);
+ revealTimers.clear();
+ };
+
+ const revealMatch = (match, attempts = 12) => {
+ if (disposed) return;
+ if (lastSidebar) updateCurrentPageIndex(lastSidebar);
+ if (scrollToMatch(match)) return;
+ if (attempts <= 0) return;
+ const timer = window.setTimeout(() => {
+ revealTimers.delete(timer);
+ revealMatch(match, attempts - 1);
+ }, 125);
+ revealTimers.add(timer);
+ };
+
+ const renderResults = (sidebar, matches) => {
+ results.replaceChildren();
+ for (const match of matches.slice(0, 5)) {
+ const button = document.createElement("button");
+ button.type = "button";
+ button.className = "codexpp-settings-search-result cursor-interaction";
+ button.title = `Reveal ${match.page} > ${match.title}`;
+ const label = document.createElement("span");
+ label.textContent = `${match.page} > ${match.title}`;
+ button.appendChild(label);
+ const reveal = (event) => {
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ clearRevealTimers();
+ const currentSidebar = findSettingsSidebar() || sidebar;
+ navigateToPage(currentSidebar, match.page);
+ window.setTimeout(() => revealMatch(match), 0);
+ };
+ button.addEventListener("pointerdown", reveal);
+ button.addEventListener("click", reveal);
+ button.addEventListener("keydown", (event) => {
+ if (event.key !== "Enter" && event.key !== " ") return;
+ reveal(event);
+ });
+ results.appendChild(button);
+ }
+ root.dataset.hasResults = matches.length > 0 ? "true" : "false";
+ };
+
+ const syncGroupVisibility = (parent, query) => {
+ const children = Array.from(parent.children).filter(
+ (child) => child instanceof HTMLElement && child !== root,
+ );
+
+ for (const child of children) {
+ if (!(child instanceof HTMLElement)) continue;
+ if (child.querySelector("button, a")) {
+ const hasVisibleControl = visibleControlsIn(child).length > 0;
+ const groupLabelMatches = compact(child.textContent).includes(query);
+ hide(child, !hasVisibleControl && !groupLabelMatches);
+ }
+ }
+
+ for (let i = 0; i < children.length; i++) {
+ const child = children[i];
+ if (!(child instanceof HTMLElement) || child.querySelector("button, a")) continue;
+ const labelMatches = compact(child.textContent).includes(query);
+ const nextGroup = children
+ .slice(i + 1)
+ .find((candidate) => candidate instanceof HTMLElement && candidate.querySelector("button, a"));
+ const nextVisible =
+ nextGroup instanceof HTMLElement &&
+ nextGroup.getAttribute(HIDDEN_ATTR) !== "true" &&
+ visibleControlsIn(nextGroup).length > 0;
+ hide(child, !labelMatches && !nextVisible);
+ }
+ };
+
+ const applyFilter = () => {
+ scheduled = false;
+ if (disposed) return;
+
+ const sidebar = findSettingsSidebar();
+ if (!sidebar) {
+ root.remove();
+ restoreHidden(document);
+ return;
+ }
+ lastSidebar = sidebar;
+
+ const mount = findMount(sidebar);
+ if (!root.isConnected || root.parentElement !== mount.parent) {
+ mount.parent.insertBefore(root, mount.before);
+ } else if (root.nextElementSibling !== mount.before && mount.before !== root) {
+ mount.parent.insertBefore(root, mount.before);
+ }
+
+ updateCurrentPageIndex(sidebar);
+ restoreHidden(sidebar);
+ const query = compact(input.value);
+ root.dataset.empty = "false";
+ root.dataset.hasResults = "false";
+ results.replaceChildren();
+ if (!query) return;
+
+ const matches = contentMatches(query);
+ const matchingPages = new Set(matches.map((match) => compact(match.page)));
+
+ const controls = navControls(sidebar);
+ let visibleCount = 0;
+ for (const control of controls) {
+ const matchesNav =
+ labelFor(control).includes(query) || matchingPages.has(visibleLabelFor(control));
+ hide(control, !matchesNav);
+ if (matchesNav) visibleCount++;
+ }
+
+ if (root.parentElement instanceof HTMLElement) {
+ syncGroupVisibility(root.parentElement, query);
+ }
+ renderResults(sidebar, matches);
+ root.dataset.empty = visibleCount === 0 && matches.length === 0 ? "true" : "false";
+ };
+
+ const schedule = () => {
+ if (scheduled || disposed) return;
+ scheduled = true;
+ requestAnimationFrame(applyFilter);
+ };
+
+ input.addEventListener("input", schedule);
+ input.addEventListener("keydown", (event) => {
+ if (event.key !== "Escape") return;
+ if (input.value) {
+ input.value = "";
+ schedule();
+ } else {
+ input.blur();
+ }
+ event.stopPropagation();
+ });
+
+ const onDocumentKeydown = (event) => {
+ if (event.key.toLowerCase() !== "f" || (!event.metaKey && !event.ctrlKey)) return;
+ const sidebar = findSettingsSidebar();
+ if (!sidebar || !document.contains(sidebar)) return;
+ event.preventDefault();
+ event.stopPropagation();
+ if (document.activeElement === input) {
+ input.blur();
+ return;
+ }
+ schedule();
+ window.setTimeout(() => {
+ input.focus();
+ input.select();
+ }, 0);
+ };
+
+ const observer = new MutationObserver(schedule);
+ observer.observe(document.documentElement, { childList: true, subtree: true });
+ document.addEventListener("keydown", onDocumentKeydown, true);
+ window.addEventListener("codexpp:settings-surface", schedule);
+ schedule();
+
+ api.log.info("settings search active");
+
+ return () => {
+ disposed = true;
+ observer.disconnect();
+ document.removeEventListener("keydown", onDocumentKeydown, true);
+ window.removeEventListener("codexpp:settings-surface", schedule);
+ clearRevealTimers();
+ clearHighlight();
+ restoreHidden(document);
+ root.remove();
+ style.remove();
+ };
+ },
+
+ /**
+ * Match settings sidebar width to the main UI sidebar.
+ *
+ * Codex's main UI sidebar is `