Skip to content

Commit 159c650

Browse files
committed
fix Codex 26.727 settings renderer detection
1 parent 4b4ebfa commit 159c650

13 files changed

Lines changed: 160 additions & 19 deletions

macos/assets/renderer-inject.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Canonical cross-platform renderer. Run tools/sync-runtime-assets.mjs after editing.
22
((cssText, artDataUrl, themeConfig) => {
3-
const SELECTOR_CONTRACT = {"schema":"codex-dream-skin-selectors/1","selectors":[{"key":"shell-main","selector":"main:is(.main-surface, [data-app-shell-main-surface], [class*=\"_MainContentSurface_\"])","tier":"L1","scope":"all","required":true},{"key":"left-panel","selector":"aside.app-shell-left-panel","tier":"L1","scope":"all","required":true},{"key":"header-tint","selector":"header:is(.app-header-tint, [data-app-shell-header-edge-scroll], [class*=\"_Header_\"])","tier":"L1","scope":"all","required":true},{"key":"main-content-top-fade","selector":":is(.app-shell-main-content-top-fade, [data-app-shell-main-content-top-fade], [class*=\"_MainContentTopFade_\"])","tier":"L2","scope":"all","required":false},{"key":"home-icon","selector":"[data-testid=\"home-icon\"]","tier":"L1","scope":"home","required":true},{"key":"home-route","selector":"[role=\"main\"]:has([data-testid=\"home-icon\"])","tier":"L1","scope":"home","required":true},{"key":"home-route-css","selector":"[role=\"main\"]","tier":"L1","scope":"home","required":true},{"key":"home-banners","selector":".home-banners","tier":"L2","scope":"home","required":false},{"key":"composer-chrome","selector":".composer-surface-chrome","tier":"L2","scope":"home+thread","required":false},{"key":"composer-toolbar","selector":".composer-surface-chrome [class*=\"_footer_\"]","tier":"L2","scope":"home+thread","required":false},{"key":"home-utility","selector":"[class*=\"_homeUtilityBar_\"]","tier":"L2","scope":"home","required":false},{"key":"game-source","selector":"[data-feature=\"game-source\"]","tier":"L2","scope":"home","required":false},{"key":"home-suggestions","selector":".group\\/home-suggestions","tier":"L2","scope":"home","required":false},{"key":"project-selector","selector":".group\\/project-selector","tier":"L2","scope":"home config","required":false},{"key":"markdown","selector":"[class*=\"_markdown\"]","tier":"L2","scope":"thread","required":false},{"key":"thread-surface","selector":".thread-scroll-container","tier":"L2","scope":"thread","required":false},{"key":"message","selector":":is([data-message-author-role], [data-local-conversation-user-anchor], [data-local-conversation-final-assistant])","tier":"L2","scope":"thread","required":false},{"key":"appearance-radio","selector":"input[name=\"appearance-theme\"]","tier":"L2","scope":"settings","required":false},{"key":"overlay-menu","selector":"[role=\"menu\"]","tier":"L2","scope":"overlay","required":false},{"key":"overlay-dialog","selector":"[role=\"dialog\"]","tier":"L2","scope":"overlay","required":false},{"key":"overlay-popper","selector":"[data-radix-popper-content-wrapper]","tier":"L2","scope":"overlay","required":false}],"stableTestids":["app-shell-header-context-menu-surface","home-icon","theme-preview"]};
3+
const SELECTOR_CONTRACT = {"schema":"codex-dream-skin-selectors/1","selectors":[{"key":"shell-main","selector":"main:is(.main-surface, [data-app-shell-main-surface], [class*=\"_MainContentSurface_\"])","tier":"L1","scope":"all","required":true},{"key":"left-panel","selector":"aside.app-shell-left-panel","tier":"L1","scope":"all","required":true},{"key":"header-tint","selector":"header:is(.app-header-tint, [data-app-shell-header-edge-scroll], [class*=\"_Header_\"])","tier":"L1","scope":"all","required":true},{"key":"main-content-top-fade","selector":":is(.app-shell-main-content-top-fade, [data-app-shell-main-content-top-fade], [class*=\"_MainContentTopFade_\"])","tier":"L2","scope":"all","required":false},{"key":"home-icon","selector":"[data-testid=\"home-icon\"]","tier":"L1","scope":"home","required":true},{"key":"home-route","selector":"[role=\"main\"]:has([data-testid=\"home-icon\"])","tier":"L1","scope":"home","required":true},{"key":"home-route-css","selector":"[role=\"main\"]","tier":"L1","scope":"home","required":true},{"key":"home-banners","selector":".home-banners","tier":"L2","scope":"home","required":false},{"key":"composer-chrome","selector":".composer-surface-chrome","tier":"L2","scope":"home+thread","required":false},{"key":"composer-toolbar","selector":".composer-surface-chrome [class*=\"_footer_\"]","tier":"L2","scope":"home+thread","required":false},{"key":"home-utility","selector":"[class*=\"_homeUtilityBar_\"]","tier":"L2","scope":"home","required":false},{"key":"game-source","selector":"[data-feature=\"game-source\"]","tier":"L2","scope":"home","required":false},{"key":"home-suggestions","selector":".group\\/home-suggestions","tier":"L2","scope":"home","required":false},{"key":"project-selector","selector":".group\\/project-selector","tier":"L2","scope":"home config","required":false},{"key":"markdown","selector":"[class*=\"_markdown\"]","tier":"L2","scope":"thread","required":false},{"key":"thread-surface","selector":".thread-scroll-container","tier":"L2","scope":"thread","required":false},{"key":"message","selector":":is([data-message-author-role], [data-local-conversation-user-anchor], [data-local-conversation-final-assistant])","tier":"L2","scope":"thread","required":false},{"key":"settings-panel","selector":"[data-settings-panel-slug=\"general-settings\"]","tier":"L2","scope":"settings","required":false},{"key":"appearance-radio","selector":"input[name=\"appearance-theme\"]","tier":"L2","scope":"settings","required":false},{"key":"overlay-menu","selector":"[role=\"menu\"]","tier":"L2","scope":"overlay","required":false},{"key":"overlay-dialog","selector":"[role=\"dialog\"]","tier":"L2","scope":"overlay","required":false},{"key":"overlay-popper","selector":"[data-radix-popper-content-wrapper]","tier":"L2","scope":"overlay","required":false}],"stableTestids":["app-shell-header-context-menu-surface","home-icon","theme-preview"]};
44
const STATE_KEY = "__CODEX_DREAM_SKIN_STATE__";
55
const DISABLED_KEY = "__CODEX_DREAM_SKIN_DISABLED__";
66
const STYLE_REGISTRY_KEY = "__CODEX_DREAM_SKIN_STYLE_SHEETS__";
@@ -678,7 +678,8 @@
678678
const overlay = selectorHit("overlay-menu") || selectorHit("overlay-dialog") ||
679679
selectorHit("overlay-popper");
680680
let baseState = "thread";
681-
if (selectorHit("appearance-radio") || stableTestidHit("theme-preview")) baseState = "settings";
681+
if (selectorHit("settings-panel") || selectorHit("appearance-radio") ||
682+
stableTestidHit("theme-preview")) baseState = "settings";
682683
else if (selectorHit("home-icon") || selectorHit("home-route")) baseState = "home";
683684
else if (!selectorHit("shell-main") && !document.querySelector('main, [role="main"]')) baseState = "settings";
684685
const missingL1 = SELECTOR_CONTRACT.selectors

macos/assets/selectors.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@
4444
{ "key": "markdown", "selector": "[class*=\"_markdown\"]", "tier": "L2", "scope": "thread", "required": false, "notes": "会话内容块,单页可达 100+ 个,样式规则注意性能" },
4545
{ "key": "thread-surface", "selector": ".thread-scroll-container", "tier": "L2", "scope": "thread", "required": false, "notes": "会话滚动表面;双端活动会话快照均存在" },
4646
{ "key": "message", "selector": ":is([data-message-author-role], [data-local-conversation-user-anchor], [data-local-conversation-final-assistant])", "tier": "L2", "scope": "thread", "required": false, "notes": "用户与助手消息的稳定语义边界;兼容旧版 role 属性和 Codex 26.727.4816 的用户/助手语义属性,运行时对子节点变更做合并刷新" },
47-
{ "key": "appearance-radio", "selector": "input[name=\"appearance-theme\"]", "tier": "L2", "scope": "settings", "required": false, "notes": "外观三选一;theme-preview testid 同屏出现" },
47+
{ "key": "settings-panel", "selector": "[data-settings-panel-slug=\"general-settings\"]", "tier": "L2", "scope": "settings", "required": false, "notes": "Codex 26.727 设置页稳定导航锚点;设置页可先落在 General,不再保证 appearance-theme 或 theme-preview 存在" },
48+
{ "key": "appearance-radio", "selector": "input[name=\"appearance-theme\"]", "tier": "L2", "scope": "settings", "required": false, "notes": "旧版外观三选一;新设置页可能仅有 settings-panel" },
4849
{ "key": "overlay-menu", "selector": "[role=\"menu\"]", "tier": "L2", "scope": "overlay", "required": false },
4950
{ "key": "overlay-dialog", "selector": "[role=\"dialog\"]", "tier": "L2", "scope": "overlay", "required": false },
5051
{ "key": "overlay-popper", "selector": "[data-radix-popper-content-wrapper]", "tier": "L2", "scope": "overlay", "required": false }

macos/scripts/injector.mjs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,8 @@ async function probeSession(session) {
522522
main: Boolean(document.querySelector(${selectorLiteral("home-route")})),
523523
generic: genericCodexSurface(),
524524
};
525-
const settings = Boolean(document.querySelector(${selectorLiteral("appearance-radio")})) ||
525+
const settings = Boolean(document.querySelector(${selectorLiteral("settings-panel")})) ||
526+
Boolean(document.querySelector(${selectorLiteral("appearance-radio")})) ||
526527
Boolean(document.querySelector(${stableTestidLiteral("theme-preview")}));
527528
return {
528529
markers,
@@ -1154,6 +1155,7 @@ async function verifySession(session, expectedThemeId = null, expectedRevision =
11541155
const genericMain = box(document.querySelector('[data-ds-part="main"], [data-ds-part="home"]'));
11551156
const genericInput = box(document.querySelector('[data-ds-part="composer"]'));
11561157
const settingsBoxes = [
1158+
box(document.querySelector(${selectorLiteral("settings-panel")})),
11571159
box(document.querySelector(${selectorLiteral("appearance-radio")})),
11581160
box(document.querySelector(${stableTestidLiteral("theme-preview")})),
11591161
];
@@ -1406,7 +1408,8 @@ export function earlyPayloadFor(payload, revision) {
14061408
const shell = document.querySelector(${selectorLiteral("shell-main")});
14071409
const sidebar = document.querySelector(${selectorLiteral("left-panel")});
14081410
const main = document.querySelector(${selectorLiteral("home-route")});
1409-
const settings = document.querySelector(${selectorLiteral("appearance-radio")}) ||
1411+
const settings = document.querySelector(${selectorLiteral("settings-panel")}) ||
1412+
document.querySelector(${selectorLiteral("appearance-radio")}) ||
14101413
document.querySelector(${stableTestidLiteral("theme-preview")});
14111414
const genericMain = document.querySelector('main, [role="main"]');
14121415
const genericInput = document.querySelector('textarea, [contenteditable="true"], [role="textbox"]');

macos/tests/injector-bootstrap.test.mjs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ function createFixture() {
2020
shell: false,
2121
sidebar: false,
2222
main: false,
23+
settingsPanel: false,
2324
settings: false,
2425
genericInput: false,
2526
branding: false,
@@ -36,6 +37,9 @@ function createFixture() {
3637
if (selector === "aside.app-shell-left-panel") return markers.sidebar ? {} : null;
3738
if (selector === "[role=\"main\"]") return markers.main ? {} : null;
3839
if (selector === "main, [role=\"main\"]") return markers.main ? {} : null;
40+
if (selector === '[data-settings-panel-slug="general-settings"]') {
41+
return markers.settingsPanel ? {} : null;
42+
}
3943
if (selector.includes("textarea") || selector.includes("contenteditable") || selector.includes("textbox")) {
4044
return markers.genericInput ? {} : null;
4145
}
@@ -96,6 +100,16 @@ generic.tick();
96100
assert.deepEqual(generic.context.window.installs, ["generic"],
97101
"A verified app:// Codex surface with generic main/input anchors must accept newer renderer shells.");
98102

103+
const settingsPanel = createFixture();
104+
vm.runInNewContext(
105+
earlyPayloadFor('window.installs.push("settings-panel")', "settings-panel"),
106+
settingsPanel.context,
107+
);
108+
settingsPanel.markers.settingsPanel = true;
109+
settingsPanel.tick();
110+
assert.deepEqual(settingsPanel.context.window.installs, ["settings-panel"],
111+
"Codex 26.727 Settings must accept its stable general-settings panel without legacy appearance controls.");
112+
99113
const generations = createFixture();
100114
generations.makeNotReady();
101115
generations.markers.shell = true;
@@ -123,6 +137,46 @@ assert.match(earlySource, /setInterval\(install, 250\)/);
123137
const identityProbeStart = source.indexOf("async function probeSession");
124138
const identityProbeSource = source.slice(identityProbeStart, identityProbeStart + 1800);
125139
assert.ok(identityProbeStart >= 0, "The live target probe must remain covered by the identity test.");
140+
const probePrefix = "return session.evaluate(`";
141+
const probePayloadStart = source.indexOf(probePrefix, identityProbeStart) + probePrefix.length;
142+
const probePayloadEnd = source.indexOf("`);", probePayloadStart);
143+
assert.ok(probePayloadStart >= probePrefix.length && probePayloadEnd > probePayloadStart,
144+
"The live identity expression must remain extractable for behavioral testing.");
145+
const probeTemplate = source.slice(probePayloadStart, probePayloadEnd);
146+
assert.doesNotMatch(probeTemplate, /`/, "The live identity expression must not contain nested template literals.");
147+
const liveProbePayload = vm.runInNewContext(`\`${probeTemplate}\``, {
148+
selectorLiteral: (key) => JSON.stringify(`[selector-${key}]`),
149+
stableTestidLiteral: (key) => JSON.stringify(`[data-testid="${key}"]`),
150+
});
151+
const runLiveProbe = ({
152+
protocol = "app:", settingsPanel: hasSettingsPanel = false,
153+
genericMain = false, genericInput = false, branding = false,
154+
} = {}) => vm.runInNewContext(liveProbePayload, {
155+
location: { protocol },
156+
document: {
157+
querySelector(selector) {
158+
if (selector === "[selector-settings-panel]") return hasSettingsPanel ? {} : null;
159+
if (selector === 'main, [role="main"]') return genericMain ? {} : null;
160+
if (selector === 'textarea, [contenteditable="true"], [role="textbox"]') {
161+
return genericInput ? {} : null;
162+
}
163+
if (selector === '[data-testid="app-shell-header-context-menu-surface"]') {
164+
return branding ? {} : null;
165+
}
166+
return null;
167+
},
168+
},
169+
});
170+
assert.equal(runLiveProbe({ settingsPanel: true }).codex, true,
171+
"The live probe must accept the Codex 26.727 general Settings panel on app://.");
172+
assert.equal(runLiveProbe({ protocol: "https:", settingsPanel: true }).codex, false,
173+
"The Settings marker must never identify a non-app target.");
174+
assert.equal(runLiveProbe({ genericMain: true, genericInput: true }).codex, false,
175+
"The live probe must reject an unbranded generic app target.");
176+
assert.equal(runLiveProbe({ genericMain: true, genericInput: true, branding: true }).codex, true,
177+
"The live probe may accept generic anchors only with the stable Codex branding marker.");
178+
assert.match(identityProbeSource, /selectorLiteral\("settings-panel"\)/,
179+
"The live probe must retain the current Settings structural marker.");
126180
assert.match(identityProbeSource, /return Boolean\(main && input && branded\)/,
127181
"The live target probe must require branding together with both generic anchors.");
128182
assert.match(identityProbeSource, /app-shell-header-context-menu-surface/,

runtime/renderer-inject.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -678,7 +678,8 @@
678678
const overlay = selectorHit("overlay-menu") || selectorHit("overlay-dialog") ||
679679
selectorHit("overlay-popper");
680680
let baseState = "thread";
681-
if (selectorHit("appearance-radio") || stableTestidHit("theme-preview")) baseState = "settings";
681+
if (selectorHit("settings-panel") || selectorHit("appearance-radio") ||
682+
stableTestidHit("theme-preview")) baseState = "settings";
682683
else if (selectorHit("home-icon") || selectorHit("home-route")) baseState = "home";
683684
else if (!selectorHit("shell-main") && !document.querySelector('main, [role="main"]')) baseState = "settings";
684685
const missingL1 = SELECTOR_CONTRACT.selectors

tools/doctor-selectors.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ function pageDoctor(selectors, stableTestids = []) {
9595
const overlay = count("overlay-menu") > 0 || count("overlay-dialog") > 0 ||
9696
count("overlay-popper") > 0;
9797
let baseState = "thread";
98-
if (count("appearance-radio") > 0 || stableTestidCount("theme-preview") > 0) baseState = "settings";
98+
if (count("settings-panel") > 0 || count("appearance-radio") > 0 ||
99+
stableTestidCount("theme-preview") > 0) baseState = "settings";
99100
else if (count("home-icon") > 0 || count("home-route") > 0) baseState = "home";
100101
else if (count("shell-main") === 0) baseState = "settings";
101102

tools/doctor-selectors.test.mjs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ assert.equal(
2727
':is([data-message-author-role], [data-local-conversation-user-anchor], [data-local-conversation-final-assistant])',
2828
"The message contract must bridge both legacy and Codex 26.727 role boundaries.",
2929
);
30+
assert.equal(
31+
selectorFor("settings-panel"),
32+
'[data-settings-panel-slug="general-settings"]',
33+
"The Settings contract must use the stable Codex 26.727 general-settings panel marker.",
34+
);
3035
const resultFor = (baseState, hits, overlay = false) => gradeDoctorResult(contract, {
3136
baseState,
3237
overlay,
@@ -46,10 +51,12 @@ const brokenHome = resultFor("home", ["shell-main", "left-panel", "header-tint",
4651
assert.equal(brokenHome.pass, false);
4752
assert.equal(brokenHome.exitCode, 1);
4853

49-
const settings = resultFor("settings", ["appearance-radio"]);
54+
const settings = resultFor("settings", ["settings-panel"]);
5055
assert.equal(settings.pass, true);
5156
assert.equal(settings.tiers.L1.length, 0, "Settings must not inherit home/all L1 requirements");
52-
assert.deepEqual(settings.tiers.L2.map(({ key }) => key), ["appearance-radio"]);
57+
assert.deepEqual(settings.tiers.L2.map(({ key }) => key), ["settings-panel", "appearance-radio"]);
58+
assert.equal(settings.tiers.L2.find(({ key }) => key === "settings-panel").status, "ok");
59+
assert.equal(settings.tiers.L2.find(({ key }) => key === "appearance-radio").status, "miss");
5360

5461
assert.equal(selectorMatchesScope("home+thread", { baseState: "thread", overlay: false }), true);
5562
assert.equal(selectorMatchesScope("home config", { baseState: "home", overlay: false }), true);

0 commit comments

Comments
 (0)