Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/codex-plus-manager/src/renderer-inject.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4549,7 +4549,7 @@ describe("功能页与原生弹出层的层级", () => {
it("页面打开时连同弹出层的直接父容器一起提升,原生 tooltip 不被页面盖住", async () => {
const style = await readFile(new URL("../../../assets/inject/renderer-inject/10-style.js", import.meta.url), "utf8");
// 新版 Codex 把 tooltip 挂在 z-60 的固定视口容器里,只提升子节点逃不出该堆叠上下文(实测复现过)。
assert.match(style, /html\[data-codex-plus-page-open\] div:has\(> \[data-radix-popper-content-wrapper\], > \[role="tooltip"\], > \[role="menu"\]\):not\(\.\$\{codexPlusPageClass\} \*\),/);
assert.match(style, /html\[data-codex-plus-page-open\] div:has\(> \[data-radix-popper-content-wrapper\], > \[role="tooltip"\], > \[role="menu"\]\):not\(\.\$\{codexPlusPageClass\} \*\) \{/);
const page = Number(/z-index: (\d+);\s*display: block;\s*background: var\(--codex-plus-bg-primary/.exec(style)?.[1]);
const lifted = Number(/\.app-header-tint \{\s*z-index: (\d+) !important;/.exec(style)?.[1]);
assert.ok(page > 0 && lifted > page, "提升后的弹出层必须高于功能页");
Expand Down
96 changes: 96 additions & 0 deletions apps/codex-plus-manager/src/theme-shell-performance.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import { it } from "node:test";

const asset = (name: string) => readFile(new URL(`../../../assets/inject/${name}`, import.meta.url), "utf8");

for (const platform of ["windows"]) {
it(`${platform}: removes stylesheet :has() dependencies from base and community themes`, async () => {
const renderer = await asset(`upstream/dream-skin/${platform}/renderer-inject.js`);
const css = [
await asset(`upstream/dream-skin/${platform}/dream-skin.css`),
await asset("dream-skin-native-shell-compat.css"),
await asset("dream-skin-community-compat.css"),
].join("\n");
const start = renderer.indexOf(" const CSS_PREDICATES =");
const end = renderer.indexOf(" const ART =", start);
assert.ok(start >= 0 && end > start);
const compile = new Function("cssText", `${renderer.slice(start, end)}\nreturn cssText;`);
const compiled = compile(css);
// 不仅检查新规则:防止添加样式后遗漏预计算表,重新引入常驻依赖。
assert.doesNotMatch(compiled.replace(/\/\*[\s\S]*?\*\//g, ""), /:has\(/);
});
}

it("keeps the popup parent rule installed only while the manager page is open", async () => {
const styleSource = await asset("renderer-inject/10-style.js");
const pageSource = await asset("renderer-inject/40-backend-settings.js");
const extract = (source: string, name: string) => {
const match = source.match(new RegExp(`^ function ${name}\\([^]*?^ \\}`, "m"));
assert.ok(match, name);
return match[0];
};
let open = false;
let writes = 0;
type StyleNode = { id: string; textContent: string; setAttribute(): void; remove(): void };
const nodes = new Map<string, StyleNode>();
const attributes = new Map<string, string>();
const document = {
getElementById: (id: string) => nodes.get(id),
querySelector: (selector: string) => selector === ".codex-plus-page-overlay" && open ? {} : null,
createElement: (): StyleNode => ({
id: "", textContent: "", setAttribute() {},
remove() { nodes.delete(this.id); },
}),
documentElement: {
appendChild(node: StyleNode) { nodes.set(node.id, node); writes++; },
setAttribute: (name: string, value: string) => attributes.set(name, value),
removeAttribute: (name: string) => attributes.delete(name),
style: { setProperty() {}, removeProperty() {} },
},
};
const sync = new Function("document", `
const styleId = "codex-delete-style";
const codexPlusIsWindowsPlatform = true;
const codexPlusPageClass = "codex-plus-page-overlay";
const codexPlusRailSelector = "nav[data-app-navigation-rail]";
const codexPlusRailDestinationSelector = "[data-sidebar-destination]";
${extract(styleSource, "syncCodexPlusPagePopupStyle")}
${extract(pageSource, "syncCodexPlusRailNativeSelection")}
return syncCodexPlusRailNativeSelection;
`)(document);
sync();
assert.equal(nodes.size, 0);
open = true;
sync();
sync();
assert.equal(writes, 1, "repeated scans must not recreate the stylesheet");
assert.equal(attributes.has("data-codex-plus-page-open"), true);
assert.match(nodes.get("codex-delete-style-page-popups")!.textContent, /div:has\(/);
open = false;
sync();
assert.equal(nodes.size, 0, "closing removes the CSS rule, not just its root condition");
assert.equal(attributes.has("data-codex-plus-page-open"), false);
open = true;
sync();
assert.equal(nodes.size, 1, "reopening restores popup stacking");
});

it("keeps the original popup stylesheet behavior on non-Windows platforms", async () => {
const source = await asset("renderer-inject/10-style.js");
const install = source.match(/^ function installStyle\([^]*?^ \}/m)![0];
const names = [...new Set(["styleId", "codexDeleteStyleVersion",
...Array.from(install.matchAll(/\$\{([A-Za-z_$][A-Za-z0-9_$]*)/g), m => m[1])])];
for (const windows of [false, true]) {
let text = "";
const document = {
getElementById: () => null,
createElement: () => ({id: "", dataset: {}, textContent: ""}),
documentElement: {appendChild: (style: {textContent: string}) => {text = style.textContent;}},
};
new Function("document", ...names, `${install}\ninstallStyle();`)(document,
...names.map(name => name === "codexPlusIsWindowsPlatform" ? windows : name === "selectors" ? {} : name));
assert.equal(text.includes("div:has("), !windows,
"only Windows should stop installing the persistent parent rule");
}
});
38 changes: 30 additions & 8 deletions assets/inject/renderer-inject.js
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@
const styleId = "codex-delete-style";
// 改 10-style.js 里的任何 CSS 都要把它 +1:installStyle 靠这个版本号判断
// 页面里已有的 <style> 是否过期,不升的话新样式在旧标签存在时会被直接跳过。
const codexDeleteStyleVersion = "37";
const codexDeleteStyleVersion = "38";
const codexPlusMenuId = "codex-plus-menu";
const codexPlusMenuFloatingClass = "codex-plus-menu-floating";
const codexPlusSidebarNavId = "codex-plus-sidebar-nav";
Expand Down Expand Up @@ -1187,12 +1187,11 @@
letter-spacing: -0.15px;
white-space: nowrap;
}
/*
* 提升定位包装层,避免只改 tooltip/menu 子节点仍被父级堆叠上下文困住。
* 新版 Codex 还把弹出层挂在一个 z-60 的固定视口容器里(zoom-adjusted-viewport),
* 只提升里面的节点逃不出这个堆叠上下文,所以直接父容器也一起提升。
*/
html[data-codex-plus-page-open] div:has(> [data-radix-popper-content-wrapper], > [role="tooltip"], > [role="menu"]):not(.${codexPlusPageClass} *),
${codexPlusIsWindowsPlatform ? "" : `
html[data-codex-plus-page-open] div:has(> [data-radix-popper-content-wrapper], > [role="tooltip"], > [role="menu"]):not(.${codexPlusPageClass} *) {
z-index: 2147483645 !important;
}
`}
html[data-codex-plus-page-open] [data-radix-popper-content-wrapper]:not(.${codexPlusPageClass} *),
html[data-codex-plus-page-open] [role="tooltip"]:not(.${codexPlusPageClass} *),
html[data-codex-plus-page-open] [role="menu"]:not(.${codexPlusPageClass} *),
Expand Down Expand Up @@ -2509,6 +2508,27 @@
document.documentElement.appendChild(style);
}

function syncCodexPlusPagePopupStyle(open) {
const id = `${styleId}-page-popups`;
const existing = document.getElementById(id);
if (!codexPlusIsWindowsPlatform || !open) {
existing?.remove();
return;
}
if (existing) return;
// Windows 仅在管理页打开时提升原生弹层的直接定位容器。即使根条件不命中,
// 常驻的 :has() 仍会增加聊天 DOM 变化时的样式失效开销。
const style = document.createElement("style");
style.id = id;
style.setAttribute("data-codex-plus-ext", "page-popup-style");
style.textContent = `
html[data-codex-plus-page-open] div:has(> [data-radix-popper-content-wrapper], > [role="tooltip"], > [role="menu"]):not(.${codexPlusPageClass} *) {
z-index: 2147483645 !important;
}
`;
document.documentElement.appendChild(style);
}

function defaultCodexPlusSettings() {
// capacityAutoRetry* 必须写成字面量:多个测试(agent-pet、
// backend-settings-sync…)把 defaultCodexPlusSettings 切片到不带 prelude 的沙箱里执行,
Expand Down Expand Up @@ -6640,7 +6660,9 @@
function syncCodexPlusRailNativeSelection() {
const root = document.documentElement;
if (!root) return;
if (!document.querySelector(`.${codexPlusPageClass}`)) {
const open = Boolean(document.querySelector(`.${codexPlusPageClass}`));
syncCodexPlusPagePopupStyle(open);
if (!open) {
root.removeAttribute("data-codex-plus-page-open");
root.style.removeProperty("--codex-plus-rail-dim");
return;
Expand Down
2 changes: 1 addition & 1 deletion assets/inject/renderer-inject/00-prelude.js
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@
const styleId = "codex-delete-style";
// 改 10-style.js 里的任何 CSS 都要把它 +1:installStyle 靠这个版本号判断
// 页面里已有的 <style> 是否过期,不升的话新样式在旧标签存在时会被直接跳过。
const codexDeleteStyleVersion = "37";
const codexDeleteStyleVersion = "38";
const codexPlusMenuId = "codex-plus-menu";
const codexPlusMenuFloatingClass = "codex-plus-menu-floating";
const codexPlusSidebarNavId = "codex-plus-sidebar-nav";
Expand Down
32 changes: 26 additions & 6 deletions assets/inject/renderer-inject/10-style.js
Original file line number Diff line number Diff line change
Expand Up @@ -514,12 +514,11 @@
letter-spacing: -0.15px;
white-space: nowrap;
}
/*
* 提升定位包装层,避免只改 tooltip/menu 子节点仍被父级堆叠上下文困住。
* 新版 Codex 还把弹出层挂在一个 z-60 的固定视口容器里(zoom-adjusted-viewport),
* 只提升里面的节点逃不出这个堆叠上下文,所以直接父容器也一起提升。
*/
html[data-codex-plus-page-open] div:has(> [data-radix-popper-content-wrapper], > [role="tooltip"], > [role="menu"]):not(.${codexPlusPageClass} *),
${codexPlusIsWindowsPlatform ? "" : `
html[data-codex-plus-page-open] div:has(> [data-radix-popper-content-wrapper], > [role="tooltip"], > [role="menu"]):not(.${codexPlusPageClass} *) {
z-index: 2147483645 !important;
}
`}
html[data-codex-plus-page-open] [data-radix-popper-content-wrapper]:not(.${codexPlusPageClass} *),
html[data-codex-plus-page-open] [role="tooltip"]:not(.${codexPlusPageClass} *),
html[data-codex-plus-page-open] [role="menu"]:not(.${codexPlusPageClass} *),
Expand Down Expand Up @@ -1836,6 +1835,27 @@
document.documentElement.appendChild(style);
}

function syncCodexPlusPagePopupStyle(open) {
const id = `${styleId}-page-popups`;
const existing = document.getElementById(id);
if (!codexPlusIsWindowsPlatform || !open) {
existing?.remove();
return;
}
if (existing) return;
// Windows 仅在管理页打开时提升原生弹层的直接定位容器。即使根条件不命中,
// 常驻的 :has() 仍会增加聊天 DOM 变化时的样式失效开销。
const style = document.createElement("style");
style.id = id;
style.setAttribute("data-codex-plus-ext", "page-popup-style");
style.textContent = `
html[data-codex-plus-page-open] div:has(> [data-radix-popper-content-wrapper], > [role="tooltip"], > [role="menu"]):not(.${codexPlusPageClass} *) {
z-index: 2147483645 !important;
}
`;
document.documentElement.appendChild(style);
}

function defaultCodexPlusSettings() {
// capacityAutoRetry* 必须写成字面量:多个测试(agent-pet、
// backend-settings-sync…)把 defaultCodexPlusSettings 切片到不带 prelude 的沙箱里执行,
Expand Down
4 changes: 3 additions & 1 deletion assets/inject/renderer-inject/40-backend-settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -1062,7 +1062,9 @@
function syncCodexPlusRailNativeSelection() {
const root = document.documentElement;
if (!root) return;
if (!document.querySelector(`.${codexPlusPageClass}`)) {
const open = Boolean(document.querySelector(`.${codexPlusPageClass}`));
syncCodexPlusPagePopupStyle(open);
if (!open) {
root.removeAttribute("data-codex-plus-page-open");
root.style.removeProperty("--codex-plus-rail-dim");
return;
Expand Down
4 changes: 2 additions & 2 deletions assets/inject/renderer-inject/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
{
"name": "10-style.js",
"description": "样式注入、默认设置、后端设置映射、梦皮主题变量与持久化",
"lines": 2816
"lines": 2836
},
{
"name": "20-menu.js",
Expand Down Expand Up @@ -53,7 +53,7 @@
{
"name": "40-backend-settings.js",
"description": "后端设置读写、心跳同步、按清单生成的设置页、用户脚本与市场状态、拓展 UI",
"lines": 2415
"lines": 2417
},
{
"name": "41-extension-detail.js",
Expand Down
Loading
Loading