diff --git a/apps/codex-plus-launcher/Cargo.toml b/apps/codex-plus-launcher/Cargo.toml
index e973a9142..37a581faf 100644
--- a/apps/codex-plus-launcher/Cargo.toml
+++ b/apps/codex-plus-launcher/Cargo.toml
@@ -9,6 +9,11 @@ repository.workspace = true
name = "codex-plus-plus"
path = "src/main.rs"
+# Keep the pre-1.2.43 launcher name available for older manager builds and shortcuts.
+[[bin]]
+name = "codex-plus"
+path = "src/main.rs"
+
[dependencies]
anyhow.workspace = true
async-trait = "0.1"
diff --git a/apps/codex-plus-manager/src/App.tsx b/apps/codex-plus-manager/src/App.tsx
index b4bc12550..9ad3801d0 100644
--- a/apps/codex-plus-manager/src/App.tsx
+++ b/apps/codex-plus-manager/src/App.tsx
@@ -2564,8 +2564,8 @@ export function App() {
if (!current) return current;
if (isWindowsPlatform) {
const config = { ...current.config };
- delete config.colors;
delete config.palette;
+ config.colors = defaultDreamSkinColors();
return { ...current, config };
}
const defaults = defaultDreamSkinTheme();
@@ -3630,15 +3630,6 @@ function DreamSkinScreen({
const themeAppearance = theme.appearance === "light" || theme.appearance === "dark"
? theme.appearance
: "auto";
- const windowsAccent = typeof theme.palette?.accent === "string" ? theme.palette.accent : "";
- const updateWindowsAccent = (value: string) => {
- const palette = { ...(theme.palette ?? {}) };
- if (value.trim()) palette.accent = value;
- else delete palette.accent;
- const next: DreamSkinThemeConfig = { ...theme, palette };
- if (!Object.keys(palette).length) delete next.palette;
- updateTheme(next);
- };
const companion = theme.companion;
const companionDataUrl = typeof companion?.dataUrl === "string" ? companion.dataUrl : "";
const companionEnabled = Boolean(companionDataUrl) && companion?.enabled !== false;
@@ -3946,7 +3937,7 @@ function DreamSkinScreen({
{isWindowsPlatform
- ? t("Windows 使用亮暗模式、图片取色和可选强调色;完整色板仅在 macOS 生效。")
+ ? t("Windows 现在会读取主题中的完整 colors 色板;清除色板后将恢复从图片自适应配色。")
: t("macOS 会应用主题中的图片、文字和颜色配置。")}
@@ -4108,24 +4099,34 @@ function DreamSkinScreen({
))}
-
-
+
+ {dreamSkinColorFields().map(([key, label]) => (
+ updateThemeColor(key, value)}
+ />
+ ))}
+
+
-
+
- {t("亮暗模式直接控制 Codex 外观;强调色留空时自动从主题图片提取。")}
+ {t("Windows 现在会读取主题中的完整 colors 色板;清除色板后将恢复从图片自适应配色。")}
) : (
diff --git a/apps/codex-plus-manager/src/dream-skin.test.ts b/apps/codex-plus-manager/src/dream-skin.test.ts
index 32c119cf7..d10463927 100644
--- a/apps/codex-plus-manager/src/dream-skin.test.ts
+++ b/apps/codex-plus-manager/src/dream-skin.test.ts
@@ -51,6 +51,8 @@ describe("dream skin theme helpers", () => {
assert.match(assets, /__GLASS_VISION_CSS_JSON__/);
assert.match(renderer, /__CODEX_PLUS_EXTERNAL_DREAM_SKIN_RUNTIME__/);
assert.match(renderer, /__CODEX_PLUS_CLEAR_DREAM_SKIN__/);
+ assert.match(renderer, /data-codex-plus-dream-skin-main-compat/);
+ assert.match(renderer, /classList\.add\("main-surface"\)/);
});
it("preserves target-only theme fields without rewriting them", () => {
@@ -116,17 +118,20 @@ describe("dream skin theme helpers", () => {
assert.match(app, /Math\.max\(-160, Math\.min\(160, Number\(event\.currentTarget\.value\) \|\| 0\)\)/);
});
- it("keeps the Windows skin active when the sidebar is hidden", async () => {
+ it("keeps the modern Dream Skin runtime active when the sidebar is hidden", async () => {
const renderer = await readFile(
new URL("../../../assets/inject/upstream/dream-skin/windows/renderer-inject.js", import.meta.url),
"utf8",
);
- assert.match(renderer, /const shellMain = document\.querySelector\("main\.main-surface"\)/);
+ assert.match(renderer, /main\.main-surface/);
+ assert.match(renderer, /data-dream-skin/);
+ assert.match(renderer, /data-dream-shell/);
+ assert.match(renderer, /ensure\(\{ root: true/);
assert.doesNotMatch(renderer, /!shellMain\s*\|\|\s*!shellSidebar/);
});
- it("extends the Windows wallpaper treatment to right and bottom dock panels", async () => {
+ it("uses the stable selector contract for the full Codex shell", async () => {
const renderer = await readFile(
new URL("../../../assets/inject/upstream/dream-skin/windows/renderer-inject.js", import.meta.url),
"utf8",
@@ -136,15 +141,18 @@ describe("dream skin theme helpers", () => {
"utf8",
);
- assert.match(renderer, /\[data-app-shell-tabs="true"\]/);
- assert.match(renderer, /dream-aux-panel-layer/);
- assert.match(renderer, /dream-aux-panel-right/);
- assert.match(renderer, /dream-aux-panel-bottom/);
- assert.match(renderer, /clearAuxiliaryPanelClasses/);
- assert.match(css, /\.dream-aux-panel-layer/);
- assert.match(css, /\.dream-aux-panel-right/);
- assert.match(css, /\.dream-aux-panel-bottom/);
- assert.match(css, /\[data-codex-terminal="true"\]/);
+ assert.match(renderer, /codex-dream-skin-selectors\/1/);
+ assert.match(renderer, /app-shell-left-panel/);
+ assert.match(renderer, /home-route/);
+ assert.match(renderer, /adoptedStyleSheets/);
+ assert.match(renderer, /MutationObserver/);
+ assert.match(renderer, /data-codex-plus-dream-skin-main-compat/);
+ assert.match(renderer, /ensureShellMainCompatibility/);
+ assert.match(css, /html\[data-dream-skin="active"\]/);
+ assert.match(css, /main\.main-surface/);
+ assert.match(css, /_MainContentTopFade_/);
+ assert.match(css, /aside\.app-shell-left-panel/);
+ assert.match(css, /\.composer-surface-chrome/);
});
it("exposes companion image controls in the theme editor", async () => {
@@ -256,7 +264,7 @@ describe("dream skin theme helpers", () => {
const css = await readFile(new URL("./styles.css", import.meta.url), "utf8");
assert.match(app, /dream-skin-theme-library/);
- assert.match(app, /Windows 使用亮暗模式、图片取色和可选强调色/);
+ assert.match(app, /Windows 现在会读取主题中的完整 colors 色板/);
assert.match(css, /\.dream-skin-market-grid\s*\{[^}]*grid-template-columns:\s*repeat\(3,/s);
assert.match(css, /\.dream-skin-theme-list\s*\{[^}]*grid-template-columns:\s*repeat\(3,/s);
assert.match(css, /@media \(max-width:\s*760px\)[\s\S]*\.dream-skin-theme-list\s*\{[^}]*grid-template-columns:\s*1fr/s);
@@ -286,7 +294,7 @@ describe("dream skin theme helpers", () => {
assert.match(customizer, /t\("恢复 Codex 默认配色"\)/);
});
- it("exposes only effective Windows appearance and accent controls", async () => {
+ it("exposes Windows appearance and complete color controls", async () => {
const app = await readFile(new URL("./App.tsx", import.meta.url), "utf8");
assert.match(app, /dream-skin-windows-theme-controls/);
@@ -295,7 +303,7 @@ describe("dream skin theme helpers", () => {
assert.match(app, /暗色/);
assert.match(app, /跟随图片配色/);
assert.match(app, /isWindowsPlatform \? \([\s\S]*dream-skin-windows-theme-controls[\s\S]*dream-skin-colors/);
- assert.match(app, /if \(isWindowsPlatform\) \{[\s\S]*delete config\.colors;[\s\S]*delete config\.palette;/);
+ assert.doesNotMatch(app, /if \(isWindowsPlatform\) \{[\s\S]*delete config\.colors;/);
});
it("separates the remote marketplace from local theme editing", async () => {
diff --git a/apps/codex-plus-manager/src/dream-skin.ts b/apps/codex-plus-manager/src/dream-skin.ts
index 104d7f1c6..537650e48 100644
--- a/apps/codex-plus-manager/src/dream-skin.ts
+++ b/apps/codex-plus-manager/src/dream-skin.ts
@@ -183,6 +183,7 @@ export function defaultDreamSkinTheme(): DreamSkinThemeConfig {
projectLabel: "◉ 选择项目",
statusText: "DREAM SKIN ONLINE",
quote: "MAKE SOMETHING WONDERFUL",
+ colors: defaultDreamSkinColors(),
image: "dream-reference.jpg",
appearance: "auto",
art: {
diff --git a/apps/codex-plus-manager/src/i18n-en.ts b/apps/codex-plus-manager/src/i18n-en.ts
index 16ff65ad2..0a12cae3d 100644
--- a/apps/codex-plus-manager/src/i18n-en.ts
+++ b/apps/codex-plus-manager/src/i18n-en.ts
@@ -57,8 +57,6 @@ export const EN_PLAIN: Record = {
"亮色": "Light",
"暗色": "Dark",
"跟随图片配色": "Use image colors",
- "亮暗模式直接控制 Codex 外观;强调色留空时自动从主题图片提取。":
- "The appearance mode directly controls Codex. Leave the accent empty to derive it from the theme image.",
"主题市场": "Theme marketplace",
"主题视图": "Theme view",
"社区主题": "Community themes",
@@ -115,8 +113,8 @@ export const EN_PLAIN: Record = {
"主题操作": "Theme actions",
"重命名": "Rename",
"正在加载主题库…": "Loading theme library...",
- "Windows 使用亮暗模式、图片取色和可选强调色;完整色板仅在 macOS 生效。":
- "Windows uses appearance mode, image-derived colors, and an optional accent; the full palette applies only on macOS.",
+ "Windows 现在会读取主题中的完整 colors 色板;清除色板后将恢复从图片自适应配色。":
+ "Windows uses the theme image, appearance mode, and complete colors palette.",
"macOS 会应用主题中的图片、文字和颜色配置。":
"macOS applies the image, text, and color settings from the theme.",
"Dream Skin 图片预览": "Dream Skin image preview",
diff --git a/apps/codex-plus-manager/src/styles.css b/apps/codex-plus-manager/src/styles.css
index 97f6c6975..6d055dd11 100644
--- a/apps/codex-plus-manager/src/styles.css
+++ b/apps/codex-plus-manager/src/styles.css
@@ -5245,8 +5245,7 @@ select {
padding-top: 2px;
}
-.dream-skin-windows-theme-controls > .field,
-.dream-skin-windows-accent .field {
+.dream-skin-windows-theme-controls > .field {
min-width: 0;
margin: 0;
}
@@ -5255,17 +5254,6 @@ select {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
-.dream-skin-windows-accent {
- display: grid;
- grid-template-columns: minmax(0, 1fr) auto;
- gap: 10px;
- align-items: end;
-}
-
-.dream-skin-windows-accent > button {
- min-height: 36px;
-}
-
.dream-skin-windows-theme-note {
color: hsl(var(--muted-foreground));
font-size: 12px;
@@ -5456,7 +5444,6 @@ select {
.dream-skin-text-grid,
.dream-skin-colors,
- .dream-skin-windows-accent,
.dream-skin-companion-fields,
.dream-skin-diagnostics-grid,
.dream-skin-verification-meta {
diff --git a/assets/inject/renderer-inject.js b/assets/inject/renderer-inject.js
index fda3287e9..2cfd24400 100644
--- a/assets/inject/renderer-inject.js
+++ b/assets/inject/renderer-inject.js
@@ -1770,6 +1770,10 @@
document.querySelectorAll(".dream-home-shell").forEach((node) => node.classList.remove("dream-home-shell"));
document.querySelectorAll(".dream-skin-home").forEach((node) => node.classList.remove("dream-skin-home"));
document.querySelectorAll(".dream-skin-home-shell").forEach((node) => node.classList.remove("dream-skin-home-shell"));
+ document.querySelectorAll('main[data-codex-plus-dream-skin-main-compat="true"]').forEach((node) => {
+ node.classList.remove("main-surface");
+ node.removeAttribute("data-codex-plus-dream-skin-main-compat");
+ });
document.querySelectorAll("[class]").forEach((node) => {
for (const className of [...node.classList]) {
if (
@@ -1933,6 +1937,12 @@
clearDreamSkinPresentation();
return;
}
+ // Codex 26.727 moved the shell's stable class behind CSS Modules. Keep
+ // the DreamSkin selector contract working without changing Codex's DOM.
+ if (!shellMain.classList.contains("main-surface")) {
+ shellMain.classList.add("main-surface");
+ shellMain.setAttribute("data-codex-plus-dream-skin-main-compat", "true");
+ }
root.classList.add(descriptor.rootClass);
root.setAttribute("data-codex-plus-dream-skin", "true");
diff --git a/assets/inject/upstream/dream-skin/macos/dream-skin.css b/assets/inject/upstream/dream-skin/macos/dream-skin.css
index 07976b196..2656280ba 100644
--- a/assets/inject/upstream/dream-skin/macos/dream-skin.css
+++ b/assets/inject/upstream/dream-skin/macos/dream-skin.css
@@ -1,4 +1,5 @@
-:root.codex-dream-skin {
+/* Canonical cross-platform skin. Run tools/sync-runtime-assets.mjs after editing. */
+:root[data-dream-skin="active"] {
color-scheme: dark !important;
--ds-bg: #111318;
--ds-panel: #191c22;
@@ -41,6 +42,9 @@
rgb(var(--ds-bg-rgb) / .18) 32%,
rgb(var(--ds-bg-rgb) / .76) 68%,
rgb(var(--ds-bg-rgb) / 1) 100%);
+ --ds-task-full-veil: linear-gradient(
+ rgb(var(--ds-bg-rgb) / .10),
+ rgb(var(--ds-bg-rgb) / .10));
--ds-immersive-edge: rgb(var(--ds-bg-rgb) / .40);
--ds-immersive-mid: rgb(var(--ds-bg-rgb) / .26);
--ds-immersive-far: rgb(var(--ds-bg-rgb) / .16);
@@ -57,7 +61,7 @@
--ds-task-immersive-far: rgb(var(--ds-bg-rgb) / .60);
}
-html.codex-dream-skin[data-dream-shell="light"] {
+html[data-dream-skin="active"][data-dream-shell="light"] {
color-scheme: light !important;
--ds-bg: #f3f5f6;
--ds-panel: #fafbfb;
@@ -88,6 +92,9 @@ html.codex-dream-skin[data-dream-shell="light"] {
rgb(var(--ds-bg-rgb) / .22) 34%,
rgb(var(--ds-bg-rgb) / .78) 70%,
rgb(var(--ds-bg-rgb) / 1) 100%);
+ --ds-task-full-veil: linear-gradient(
+ rgb(var(--ds-panel-rgb) / .10),
+ rgb(var(--ds-panel-rgb) / .10));
--ds-immersive-edge: rgb(var(--ds-panel-rgb) / .44);
--ds-immersive-mid: rgb(var(--ds-panel-rgb) / .28);
--ds-immersive-far: rgb(var(--ds-panel-rgb) / .14);
@@ -102,12 +109,31 @@ html.codex-dream-skin[data-dream-shell="light"] {
--ds-task-immersive-far: rgb(var(--ds-panel-rgb) / .66);
}
-html.codex-dream-skin:is([data-dream-art-safe="left"], [data-dream-art-safe-area="left"]) {
+/* Native token surfaces (dropdown/popover) resolve from Codex's own
+ appearanceTheme, not the skin; when that disagrees with data-dream-shell the
+ popover keeps the opposite palette (#233). Remap the dropdown token family to
+ skin variables so those surfaces inherit the theme in both shells. */
+html[data-dream-skin="active"] {
+ --color-token-dropdown-background: var(--ds-panel);
+}
+
+html[data-dream-skin="active"] [class~="bg-token-dropdown-background"] {
+ color: var(--ds-text);
+ --color-token-foreground: var(--ds-text);
+ --color-token-text-secondary: var(--ds-muted);
+ --color-token-muted-foreground: var(--ds-muted);
+ --color-token-description-foreground: var(--ds-muted);
+ --color-token-list-hover-background: rgb(var(--ds-panel-2-rgb) / .9);
+ --color-token-border: var(--ds-line);
+ --color-token-border-default: var(--ds-line);
+}
+
+html[data-dream-skin="active"]:is([data-dream-art-safe="left"], [data-dream-art-safe-area="left"]) {
--ds-safe-side: left;
--ds-art-position: 100% var(--ds-focus-y);
}
-html.codex-dream-skin:is([data-dream-art-safe="right"], [data-dream-art-safe-area="right"]) {
+html[data-dream-skin="active"]:is([data-dream-art-safe="right"], [data-dream-art-safe-area="right"]) {
--ds-safe-side: right;
--ds-art-position: 0% var(--ds-focus-y);
--ds-hero-scrim: linear-gradient(270deg,
@@ -121,7 +147,7 @@ html.codex-dream-skin:is([data-dream-art-safe="right"], [data-dream-art-safe-are
rgb(var(--ds-bg-rgb) / .12) 100%);
}
-html.codex-dream-skin[data-dream-shell="light"]:is([data-dream-art-safe="right"], [data-dream-art-safe-area="right"]) {
+html[data-dream-skin="active"][data-dream-shell="light"]:is([data-dream-art-safe="right"], [data-dream-art-safe-area="right"]) {
--ds-hero-scrim: linear-gradient(270deg,
rgb(var(--ds-panel-rgb) / .96) 0%,
rgb(var(--ds-panel-rgb) / .82) 50%,
@@ -133,7 +159,7 @@ html.codex-dream-skin[data-dream-shell="light"]:is([data-dream-art-safe="right"]
rgb(var(--ds-bg-rgb) / .12) 100%);
}
-html.codex-dream-skin:is([data-dream-art-safe="center"], [data-dream-art-safe-area="center"]) {
+html[data-dream-skin="active"]:is([data-dream-art-safe="center"], [data-dream-art-safe-area="center"]) {
--ds-safe-side: center;
--ds-hero-scrim: linear-gradient(90deg,
transparent 0%,
@@ -147,7 +173,7 @@ html.codex-dream-skin:is([data-dream-art-safe="center"], [data-dream-art-safe-ar
rgb(var(--ds-bg-rgb) / .10) 100%);
}
-html.codex-dream-skin[data-dream-shell="light"]:is([data-dream-art-safe="center"], [data-dream-art-safe-area="center"]) {
+html[data-dream-skin="active"][data-dream-shell="light"]:is([data-dream-art-safe="center"], [data-dream-art-safe-area="center"]) {
--ds-hero-scrim: linear-gradient(90deg,
transparent 0%,
rgb(var(--ds-panel-rgb) / .72) 25%,
@@ -160,7 +186,7 @@ html.codex-dream-skin[data-dream-shell="light"]:is([data-dream-art-safe="center"
rgb(var(--ds-bg-rgb) / .10) 100%);
}
-html.codex-dream-skin:is([data-dream-art-safe="none"], [data-dream-art-safe-area="none"]) {
+html[data-dream-skin="active"]:is([data-dream-art-safe="none"], [data-dream-art-safe-area="none"]) {
--ds-safe-side: none;
--ds-hero-scrim: linear-gradient(180deg,
rgb(var(--ds-bg-rgb) / .34),
@@ -170,7 +196,7 @@ html.codex-dream-skin:is([data-dream-art-safe="none"], [data-dream-art-safe-area
rgb(var(--ds-bg-rgb) / .14));
}
-html.codex-dream-skin[data-dream-shell="light"]:is([data-dream-art-safe="none"], [data-dream-art-safe-area="none"]) {
+html[data-dream-skin="active"][data-dream-shell="light"]:is([data-dream-art-safe="none"], [data-dream-art-safe-area="none"]) {
--ds-hero-scrim: linear-gradient(180deg,
rgb(var(--ds-panel-rgb) / .46),
rgb(var(--ds-panel-rgb) / .28));
@@ -179,17 +205,17 @@ html.codex-dream-skin[data-dream-shell="light"]:is([data-dream-art-safe="none"],
rgb(var(--ds-bg-rgb) / .16));
}
-html.codex-dream-skin body {
+html[data-dream-skin="active"] body {
background: var(--ds-bg) !important;
color: var(--ds-text) !important;
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "PingFang SC", "Microsoft YaHei UI", "Segoe UI", system-ui, sans-serif !important;
}
-html.codex-dream-skin body::before {
+html[data-dream-skin="active"] body::before {
content: none;
}
-html.codex-dream-skin aside.app-shell-left-panel {
+html[data-dream-skin="active"] aside.app-shell-left-panel {
background:
linear-gradient(180deg,
rgb(var(--ds-panel-rgb) / .98),
@@ -202,41 +228,41 @@ html.codex-dream-skin aside.app-shell-left-panel {
backdrop-filter: none !important;
}
-html.codex-dream-skin aside.app-shell-left-panel nav { background: transparent !important; }
+html[data-dream-skin="active"] aside.app-shell-left-panel nav { background: transparent !important; }
-html.codex-dream-skin aside.app-shell-left-panel button,
-html.codex-dream-skin aside.app-shell-left-panel a {
+html[data-dream-skin="active"] aside.app-shell-left-panel button,
+html[data-dream-skin="active"] aside.app-shell-left-panel a {
color: var(--ds-text) !important;
transition: background-color .18s cubic-bezier(.22, 1, .36, 1),
border-color .18s cubic-bezier(.22, 1, .36, 1),
color .18s cubic-bezier(.22, 1, .36, 1) !important;
}
-html.codex-dream-skin aside.app-shell-left-panel [class*="text-token-foreground"] {
+html[data-dream-skin="active"] aside.app-shell-left-panel [class*="text-token-foreground"] {
color: var(--ds-text) !important;
}
-html.codex-dream-skin aside.app-shell-left-panel [class*="text-token-input-placeholder-foreground"] {
+html[data-dream-skin="active"] aside.app-shell-left-panel [class*="text-token-input-placeholder-foreground"] {
color: rgb(var(--ds-muted-rgb) / .92) !important;
}
-html.codex-dream-skin aside.app-shell-left-panel svg {
+html[data-dream-skin="active"] aside.app-shell-left-panel svg {
color: rgb(var(--ds-muted-rgb) / .96) !important;
}
-html.codex-dream-skin aside.app-shell-left-panel button:hover,
-html.codex-dream-skin aside.app-shell-left-panel a:hover {
+html[data-dream-skin="active"] aside.app-shell-left-panel button:hover,
+html[data-dream-skin="active"] aside.app-shell-left-panel a:hover {
background: rgb(var(--ds-accent-rgb) / .09) !important;
color: var(--ds-text) !important;
transform: none !important;
}
-html.codex-dream-skin aside.app-shell-left-panel button:hover svg,
-html.codex-dream-skin aside.app-shell-left-panel a:hover svg {
+html[data-dream-skin="active"] aside.app-shell-left-panel button:hover svg,
+html[data-dream-skin="active"] aside.app-shell-left-panel a:hover svg {
color: var(--ds-accent) !important;
}
-html.codex-dream-skin aside.app-shell-left-panel button[aria-label^="切换模式"] {
+html[data-dream-skin="active"] aside.app-shell-left-panel button[aria-label^="切换模式"] {
background: transparent !important;
border: 0 !important;
color: var(--ds-accent) !important;
@@ -246,24 +272,24 @@ html.codex-dream-skin aside.app-shell-left-panel button[aria-label^="切换模
text-shadow: none !important;
}
-html.codex-dream-skin aside.app-shell-left-panel button[aria-label^="切换模式"]::after {
+html[data-dream-skin="active"] aside.app-shell-left-panel button[aria-label^="切换模式"]::after {
content: " ·";
color: var(--ds-secondary);
}
-html.codex-dream-skin aside.app-shell-left-panel [class~="bg-token-list-hover-background"],
-html.codex-dream-skin aside.app-shell-left-panel [aria-current="page"] {
+html[data-dream-skin="active"] aside.app-shell-left-panel [class~="bg-token-list-hover-background"],
+html[data-dream-skin="active"] aside.app-shell-left-panel [aria-current="page"] {
background: rgb(var(--ds-accent-rgb) / .12) !important;
border: 1px solid rgb(var(--ds-accent-rgb) / .22) !important;
box-shadow: 0 4px 16px rgb(var(--ds-bg-rgb) / .12) !important;
color: var(--ds-text) !important;
}
-html.codex-dream-skin aside.app-shell-left-panel [aria-current="page"] svg {
+html[data-dream-skin="active"] aside.app-shell-left-panel [aria-current="page"] svg {
color: var(--ds-accent) !important;
}
-html.codex-dream-skin main.main-surface {
+html[data-dream-skin="active"] main.main-surface {
position: relative !important;
isolation: isolate;
overflow: hidden !important;
@@ -275,7 +301,7 @@ html.codex-dream-skin main.main-surface {
box-shadow: -8px 0 28px rgb(var(--ds-bg-rgb) / .18) !important;
}
-html.codex-dream-skin main.main-surface:not(.dream-skin-home-shell)::before {
+html[data-dream-skin="active"] main.main-surface:not(:has([role="main"]))::before {
content: "";
position: absolute;
inset: 0;
@@ -288,23 +314,36 @@ html.codex-dream-skin main.main-surface:not(.dream-skin-home-shell)::before {
opacity: .78;
}
-html.codex-dream-skin[data-dream-shell="light"] main.main-surface:not(.dream-skin-home-shell)::before {
+html[data-dream-skin="active"][data-dream-shell="light"] main.main-surface:not(:has([role="main"]))::before {
opacity: .72;
}
-html.codex-dream-skin:is([data-dream-art-wide="true"], [data-dream-art-aspect="wide"], [data-dream-art-aspect="ultrawide"])
- main.main-surface:not(.dream-skin-home-shell)::before {
+html[data-dream-skin="active"]:is([data-dream-art-wide="true"], [data-dream-art-aspect="wide"], [data-dream-art-aspect="ultrawide"])
+ main.main-surface:not(:has([role="main"]))::before {
background-position: center, center, var(--ds-art-position);
background-size: 100% 100%, 100% 100%, cover;
}
-html.codex-dream-skin:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])
- main.main-surface:not(.dream-skin-home-shell)::before {
+/* Studio's full mode keeps the task artwork at normal strength with only the
+ baseline readability veil. Ambient mode deliberately uses the heavier task
+ fade/shade below, while off removes the artwork entirely. */
+html[data-dream-skin="active"]:is([data-dream-task-mode="full"], [data-dream-art-task-mode="full"])
+ main.main-surface:not(:has([role="main"]))::before {
inset: 0;
height: auto;
+ background-image: var(--ds-task-full-veil), var(--dream-skin-art);
+ background-position: center, var(--ds-art-position);
+ background-size: 100% 100%, cover;
+ opacity: 1;
}
-html.codex-dream-skin:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]:has(main.main-surface:not(.dream-skin-home-shell)) body {
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])
+ main.main-surface:not(:has([role="main"]))::before {
+ inset: 0;
+ height: auto;
+}
+
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]:has(main.main-surface):not(:has(main.main-surface [role="main"])) body {
background-image: var(--dream-skin-art) !important;
background-position: var(--ds-art-position) !important;
background-size: cover !important;
@@ -312,7 +351,47 @@ html.codex-dream-skin:is([data-dream-task-mode="ambient"], [data-dream-art-task-
background-attachment: fixed !important;
}
-html.codex-dream-skin:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]:has(main.main-surface:not(.dream-skin-home-shell))
+html[data-dream-skin="active"]:is([data-dream-task-mode="full"], [data-dream-art-task-mode="full"])[data-dream-art-wide="true"]:has(main.main-surface):not(:has(main.main-surface [role="main"])) body {
+ background-image: var(--dream-skin-art) !important;
+ background-position: var(--ds-art-position) !important;
+ background-size: cover !important;
+ background-repeat: no-repeat !important;
+ background-attachment: fixed !important;
+}
+
+html[data-dream-skin="active"]:is([data-dream-task-mode="full"], [data-dream-art-task-mode="full"])[data-dream-art-wide="true"]:has(main.main-surface):not(:has(main.main-surface [role="main"]))
+ aside.app-shell-left-panel {
+ background: linear-gradient(90deg,
+ var(--ds-immersive-sidebar),
+ var(--ds-immersive-edge) 100%) !important;
+ border: 0 !important;
+ border-radius: 0 !important;
+ box-shadow: none !important;
+ backdrop-filter: none !important;
+}
+
+html[data-dream-skin="active"]:is([data-dream-task-mode="full"], [data-dream-art-task-mode="full"])[data-dream-art-wide="true"]:has(main.main-surface):not(:has(main.main-surface [role="main"]))
+ aside.app-shell-left-panel::after {
+ background: transparent !important;
+}
+
+html[data-dream-skin="active"]:is([data-dream-task-mode="full"], [data-dream-art-task-mode="full"])[data-dream-art-wide="true"]:has(main.main-surface):not(:has(main.main-surface [role="main"]))
+ main.main-surface:not(:has([role="main"])) {
+ background: linear-gradient(90deg,
+ var(--ds-immersive-edge),
+ var(--ds-immersive-mid) 64%,
+ var(--ds-immersive-far)) !important;
+ border: 0 !important;
+ border-radius: 0 !important;
+ box-shadow: none !important;
+}
+
+html[data-dream-skin="active"]:is([data-dream-task-mode="full"], [data-dream-art-task-mode="full"])[data-dream-art-wide="true"]
+ main.main-surface:not(:has([role="main"]))::before {
+ content: none;
+}
+
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]:has(main.main-surface):not(:has(main.main-surface [role="main"]))
aside.app-shell-left-panel {
background: linear-gradient(90deg,
var(--ds-task-immersive-sidebar),
@@ -325,13 +404,13 @@ html.codex-dream-skin:is([data-dream-task-mode="ambient"], [data-dream-art-task-
/* Native sidebar resize chrome inherits the sidebar background and extends
20px into the main surface. Keeping it clear preserves one continuous image. */
-html.codex-dream-skin:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]:has(main.main-surface:not(.dream-skin-home-shell))
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]:has(main.main-surface):not(:has(main.main-surface [role="main"]))
aside.app-shell-left-panel::after {
background: transparent !important;
}
-html.codex-dream-skin:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]:has(main.main-surface:not(.dream-skin-home-shell))
- main.main-surface:not(.dream-skin-home-shell) {
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]:has(main.main-surface):not(:has(main.main-surface [role="main"]))
+ main.main-surface:not(:has([role="main"])) {
background: linear-gradient(90deg,
var(--ds-task-immersive-edge),
var(--ds-task-immersive-mid) 64%,
@@ -341,12 +420,12 @@ html.codex-dream-skin:is([data-dream-task-mode="ambient"], [data-dream-art-task-
box-shadow: none !important;
}
-html.codex-dream-skin:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]
- main.main-surface:not(.dream-skin-home-shell)::before {
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]
+ main.main-surface:not(:has([role="main"]))::before {
content: none;
}
-html.codex-dream-skin:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]:has(main.main-surface:not(.dream-skin-home-shell))
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]:has(main.main-surface):not(:has(main.main-surface [role="main"]))
.composer-surface-chrome {
background: var(--ds-immersive-composer-solid) !important;
border: 0 !important;
@@ -357,7 +436,7 @@ html.codex-dream-skin:is([data-dream-task-mode="ambient"], [data-dream-art-task-
backdrop-filter: none !important;
}
-html.codex-dream-skin:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]:has(main.main-surface:not(.dream-skin-home-shell))
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]:has(main.main-surface):not(:has(main.main-surface [role="main"]))
main.main-surface > header.app-header-tint {
background: transparent !important;
border-bottom: 0 !important;
@@ -365,48 +444,48 @@ html.codex-dream-skin:is([data-dream-task-mode="ambient"], [data-dream-art-task-
backdrop-filter: none !important;
}
-html.codex-dream-skin:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]
- main.main-surface:not(.dream-skin-home-shell) [class*="_markdown"] {
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"], [data-dream-task-mode="full"], [data-dream-art-task-mode="full"])[data-dream-art-wide="true"]
+ main.main-surface:not(:has([role="main"])) [class*="_markdown"] {
color: var(--ds-text) !important;
text-shadow:
0 1px 2px rgb(var(--ds-bg-rgb) / .82),
0 0 10px rgb(var(--ds-bg-rgb) / .58);
}
-html.codex-dream-skin[data-dream-shell="light"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]
- main.main-surface:not(.dream-skin-home-shell) [class*="_markdown"] {
+html[data-dream-skin="active"][data-dream-shell="light"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"], [data-dream-task-mode="full"], [data-dream-art-task-mode="full"])[data-dream-art-wide="true"]
+ main.main-surface:not(:has([role="main"])) [class*="_markdown"] {
text-shadow:
0 1px 2px rgb(var(--ds-panel-rgb) / .92),
0 0 10px rgb(var(--ds-panel-rgb) / .72);
}
-html.codex-dream-skin:is([data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])
- main.main-surface:not(.dream-skin-home-shell)::before {
+html[data-dream-skin="active"]:is([data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])
+ main.main-surface:not(:has([role="main"]))::before {
inset: 0 0 auto;
height: clamp(280px, 46vh, 520px);
background-position: center top, center top, var(--ds-art-position);
background-size: 100% 100%, 100% 100%, cover;
}
-html.codex-dream-skin:is([data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"]):is([data-dream-art-wide="true"], [data-dream-art-aspect="wide"], [data-dream-art-aspect="ultrawide"])
- main.main-surface:not(.dream-skin-home-shell)::before {
+html[data-dream-skin="active"]:is([data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"]):is([data-dream-art-wide="true"], [data-dream-art-aspect="wide"], [data-dream-art-aspect="ultrawide"])
+ main.main-surface:not(:has([role="main"]))::before {
inset: 0;
height: auto;
background-position: center, center, var(--ds-art-position);
background-size: 100% 100%, 100% 100%, cover;
}
-html.codex-dream-skin:is([data-dream-task-mode="off"], [data-dream-art-task-mode="off"])
- main.main-surface:not(.dream-skin-home-shell)::before {
+html[data-dream-skin="active"]:is([data-dream-task-mode="off"], [data-dream-art-task-mode="off"])
+ main.main-surface:not(:has([role="main"]))::before {
content: none;
}
-html.codex-dream-skin main.main-surface:not(.dream-skin-home-shell) > :not(header.app-header-tint) {
+html[data-dream-skin="active"] main.main-surface:not(:has([role="main"])) > :not(header.app-header-tint) {
position: relative;
z-index: 1;
}
-html.codex-dream-skin main.main-surface:not(.dream-skin-home-shell) [role="main"] {
+html[data-dream-skin="active"] main.main-surface:not(:has([role="main"])) [role="main"] {
background: transparent !important;
color: var(--ds-text) !important;
text-shadow:
@@ -414,149 +493,191 @@ html.codex-dream-skin main.main-surface:not(.dream-skin-home-shell) [role="main"
0 0 10px rgb(var(--ds-bg-rgb) / .46);
}
-html.codex-dream-skin[data-dream-shell="light"] main.main-surface:not(.dream-skin-home-shell) [role="main"] {
+html[data-dream-skin="active"][data-dream-shell="light"] main.main-surface:not(:has([role="main"])) [role="main"] {
text-shadow: none;
}
-html.codex-dream-skin main.main-surface:not(.dream-skin-home-shell) article {
+/* Newer Codex builds paint the list/detail panes (pull requests, the chat
+ review/terminal/browser/files side panel) with token surfaces instead of
+ [role="main"]. The side panel can coexist with a visible home shell, so this
+ transparency is deliberately not gated on the home-route selector. The nested
+ surfaces all carry the same token, so they are cleared everywhere and the
+ translucent pane tint is painted once, on the bordered outer wrapper. */
+html[data-dream-skin="active"] main.main-surface
+ :is(div, section, aside)[class~="bg-token-main-surface-primary"] {
+ background: transparent !important;
+}
+
+html[data-dream-skin="active"] main.main-surface
+ div[class~="bg-token-main-surface-primary"][class~="border-l"] {
+ background: rgb(var(--ds-panel-rgb) / .62) !important;
+ backdrop-filter: blur(10px) saturate(106%) !important;
+}
+
+html[data-dream-skin="active"] main.main-surface:not(:has([role="main"])) article {
border: 1px solid rgb(var(--ds-muted-rgb) / .12);
background: rgb(var(--ds-panel-rgb) / .44);
box-shadow: 0 8px 24px rgb(var(--ds-bg-rgb) / .10);
backdrop-filter: blur(7px) saturate(105%);
}
-html.codex-dream-skin[data-dream-shell="light"] main.main-surface:not(.dream-skin-home-shell) article {
+html[data-dream-skin="active"][data-dream-shell="light"] main.main-surface:not(:has([role="main"])) article {
background: rgb(var(--ds-panel-rgb) / .72);
}
-html.codex-dream-skin main.main-surface > header.app-header-tint {
+html[data-dream-skin="active"][data-dream-shell="light"]
+ main.main-surface:has([role="main"])::after,
+html[data-dream-skin="active"][data-dream-shell="light"] [role="main"]:has([data-testid="home-icon"]) [data-feature="game-source"]::before {
+ text-shadow: none;
+}
+
+html[data-dream-skin="active"] main.main-surface > header.app-header-tint {
/* Preserve Codex's native fixed header geometry and side-panel controls. */
background: rgb(var(--ds-panel-rgb) / .90) !important;
border-bottom: 1px solid var(--ds-line) !important;
backdrop-filter: blur(14px) saturate(108%) !important;
}
-#codex-dream-skin-chrome {
- position: fixed;
- z-index: 31;
- overflow: hidden;
- pointer-events: none;
- border-radius: 16px 0 0 0;
-}
-
-.dream-skin-brand {
+/* Decorative chrome lives on the native shell; no injected positioning host is needed. */
+html[data-dream-skin="active"]
+ main.main-surface:not(:has([role="main"])) > header.app-header-tint::before {
+ content: var(--dream-skin-name, "Codex Dream Skin") " · "
+ var(--dream-skin-brand-subtitle, "CODEX DREAM SKIN");
position: absolute;
left: 22px;
top: 4px;
- height: 40px;
- display: none;
- align-items: center;
- gap: 10px;
- color: var(--ds-text);
-}
-
-#codex-dream-skin-chrome.dream-skin-home-shell .dream-skin-brand { display: none !important; }
-
-.dream-skin-portal-mark {
- width: 28px;
- height: 28px;
- display: grid;
- place-items: center;
- border: 1px solid rgb(var(--ds-accent-rgb) / .44);
- border-radius: 50%;
- color: var(--ds-accent);
- background: rgb(var(--ds-accent-rgb) / .08);
- font-size: 18px;
- box-shadow: 0 0 0 4px rgb(var(--ds-accent-rgb) / .05);
-}
-
-.dream-skin-brand b {
- display: block;
+ z-index: 2;
+ max-width: min(42%, 360px);
+ overflow: hidden;
color: var(--ds-accent);
- font-size: 13px;
- letter-spacing: 0;
-}
-
-.dream-skin-brand small {
- display: block;
- margin-top: 2px;
- color: var(--ds-muted);
- font-size: 9px;
- letter-spacing: 0;
- text-transform: uppercase;
+ font: 800 11px/1.3 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ letter-spacing: .03em;
+ text-overflow: ellipsis;
+ text-shadow: 0 1px 12px rgb(var(--ds-bg-rgb) / .32);
+ white-space: nowrap;
+ pointer-events: none;
}
-.dream-skin-brand small,
-.dream-skin-status { display: none !important; }
-
-.dream-skin-status {
+html[data-dream-skin="active"]
+ main.main-surface:not(:has([role="main"])) > header.app-header-tint::after {
+ content: var(--dream-skin-status, "DREAM SKIN ONLINE");
position: absolute;
right: 84px;
top: 13px;
- display: none;
- align-items: center;
- gap: 7px;
+ z-index: 2;
+ max-width: 28%;
+ overflow: hidden;
color: var(--ds-muted);
- font: 700 10px/1 ui-monospace, "SFMono-Regular", Consolas, monospace;
- letter-spacing: 0;
+ font: 700 9px/1.2 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ letter-spacing: .08em;
+ text-overflow: ellipsis;
+ text-shadow: 0 1px 10px rgb(var(--ds-bg-rgb) / .32);
+ white-space: nowrap;
+ pointer-events: none;
}
-.dream-skin-status i {
- width: 7px;
- height: 7px;
- border-radius: 50%;
- background: var(--ds-accent);
- box-shadow: 0 0 8px rgb(var(--ds-accent-rgb) / .42);
+html[data-dream-skin="active"]
+ main.main-surface:has([role="main"])::after {
+ content: var(--dream-skin-quote, "MAKE SOMETHING WONDERFUL");
+ position: absolute;
+ right: 28px;
+ bottom: 72px;
+ z-index: 2;
+ pointer-events: none;
+ color: rgb(var(--ds-accent-rgb) / .74);
+ font: italic 14px/1.2 "Segoe Print", "Comic Sans MS", cursive;
+ letter-spacing: 0;
+ text-shadow: 0 0 13px rgb(var(--ds-accent-rgb) / .30);
+ transform: rotate(-3deg);
}
-.dream-skin-quote,
-.dream-skin-orbit { display: none !important; }
-
-.dream-skin-particles {
- display: none;
+html[data-dream-skin="active"][data-dream-shell="light"]
+ main.main-surface:not(:has([role="main"])) > header.app-header-tint::before,
+html[data-dream-skin="active"][data-dream-shell="light"]
+ main.main-surface:not(:has([role="main"])) > header.app-header-tint::after {
+ text-shadow: none;
}
-.dream-skin-particles i {
- position: absolute;
- width: 3px;
- height: 3px;
- border-radius: 50%;
- background: var(--ds-accent);
- box-shadow: 0 0 8px 2px rgb(var(--ds-accent-rgb) / .24);
+/* Windows keeps this native bar outside main; macOS simply has no match. */
+html[data-dream-skin="active"][data-dream-shell="dark"]
+ [class~="group/application-menu-top-bar"] {
+ color: var(--ds-text) !important;
+ background: rgb(var(--ds-panel-rgb) / .90) !important;
+ border-bottom: 1px solid var(--ds-line) !important;
+ box-shadow: 0 1px 12px rgb(var(--ds-bg-rgb) / .72) !important;
+ backdrop-filter: blur(16px) saturate(90%) !important;
}
-.dream-skin-particles i:nth-child(1) { left: 6%; top: 13%; }
-.dream-skin-particles i:nth-child(2) { left: 29%; top: 7%; background: var(--ds-secondary); }
-.dream-skin-particles i:nth-child(3) { left: 51%; top: 18%; }
-.dream-skin-particles i:nth-child(4) { left: 75%; top: 9%; background: var(--ds-secondary); }
-.dream-skin-particles i:nth-child(5) { left: 91%; top: 28%; }
-.dream-skin-particles i:nth-child(6) { left: 64%; top: 63%; background: var(--ds-secondary); }
-.dream-skin-particles i:nth-child(7) { left: 22%; top: 72%; }
-.dream-skin-particles i:nth-child(8) { left: 86%; top: 78%; }
+html[data-dream-skin="active"][data-dream-shell="dark"]
+ [class~="group/application-menu-top-bar"] :is(button, svg) {
+ color: var(--ds-text) !important;
+ text-shadow: 0 1px 2px rgb(var(--ds-bg-rgb) / .82) !important;
+}
-html.codex-dream-skin [role="main"] {
+html[data-dream-skin="active"] main.main-surface [role="main"] {
background: transparent !important;
scrollbar-color: rgb(var(--ds-accent-rgb) / .38) transparent;
}
-html.codex-dream-skin .dream-skin-home {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) {
--thread-content-max-width: min(1180px, calc(100cqw - 44px)) !important;
overflow-x: hidden !important;
}
-.dream-skin-home > div:first-child {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child {
min-height: 100% !important;
padding-top: 15px !important;
}
-.dream-skin-home > div:first-child > div:first-child {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child {
flex: 0 0 440px !important;
min-height: 440px !important;
align-items: flex-start !important;
padding-bottom: 0 !important;
}
-.dream-skin-home > div:first-child > div:first-child > div:first-child {
+/* Codex 26.721+: home-route's first child now only wraps the (usually empty)
+ native .home-banners slot; the real content column moved out to become
+ this wrapper's sibling instead of its descendant. The two rules above
+ still assume the old nesting and force this now-mostly-empty wrapper to
+ 100% height / 440px, which pushes the real sibling content off-screen
+ (see #244). Override with higher specificity only when that new slot is
+ present, so pre-26.721 layouts are untouched. */
+html[data-dream-skin="active"]
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child:has(> .home-banners) {
+ min-height: 0 !important;
+ padding-top: 0 !important;
+}
+
+html[data-dream-skin="active"]
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child:has(> .home-banners) > .home-banners {
+ flex: 0 1 auto !important;
+ min-height: 0 !important;
+}
+
+html[data-dream-skin="active"]
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child:has(> .home-banners) > .home-banners > div:first-child {
+ height: auto !important;
+ min-height: 0 !important;
+ border: 0 !important;
+ border-radius: 0 !important;
+ background: transparent !important;
+ box-shadow: none !important;
+}
+
+html[data-dream-skin="active"]
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child:has(> .home-banners) > .home-banners > div:first-child::before {
+ content: none !important;
+}
+
+/* Codex 26.721+: this chain (3-6 first-child levels deep) targets the old
+ compact hero-art card that used to live inside div:first-child's second
+ level. That level is now the empty .home-banners slot (see #244), so on
+ the new DOM these rules currently match nothing and the compact hero card
+ is dormant rather than misrendered. Left as-is pending a real fixture of
+ the new nested shape; the critical content-invisible bug is the block
+ above this one. */
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child {
position: relative !important;
isolation: isolate;
width: calc(100% - 44px) !important;
@@ -576,7 +697,7 @@ html.codex-dream-skin .dream-skin-home {
box-shadow: 0 16px 38px rgb(var(--ds-bg-rgb) / .30) !important;
}
-.dream-skin-home > div:first-child > div:first-child > div:first-child::before {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child::before {
content: "";
position: absolute;
inset: 0 auto 0 0;
@@ -587,26 +708,26 @@ html.codex-dream-skin .dream-skin-home {
background: var(--ds-hero-scrim);
}
-html.codex-dream-skin:is([data-dream-art-safe="right"], [data-dream-art-safe-area="right"])
- .dream-skin-home > div:first-child > div:first-child > div:first-child::before {
+html[data-dream-skin="active"]:is([data-dream-art-safe="right"], [data-dream-art-safe-area="right"])
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child::before {
inset: 0 0 0 auto;
}
-html.codex-dream-skin:is([data-dream-art-safe="center"], [data-dream-art-safe-area="center"])
- .dream-skin-home > div:first-child > div:first-child > div:first-child::before {
+html[data-dream-skin="active"]:is([data-dream-art-safe="center"], [data-dream-art-safe-area="center"])
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child::before {
inset: 0 14%;
width: auto;
border-radius: 0;
}
-html.codex-dream-skin:is([data-dream-art-safe="none"], [data-dream-art-safe-area="none"])
- .dream-skin-home > div:first-child > div:first-child > div:first-child::before {
+html[data-dream-skin="active"]:is([data-dream-art-safe="none"], [data-dream-art-safe-area="none"])
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child::before {
inset: 0;
width: auto;
border-radius: 21px;
}
-.dream-skin-home > div:first-child > div:first-child > div:first-child > div:first-child {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child > div:first-child {
position: relative;
z-index: 1;
height: 100%;
@@ -615,38 +736,38 @@ html.codex-dream-skin:is([data-dream-art-safe="none"], [data-dream-art-safe-area
padding: 0 38px;
}
-html.codex-dream-skin:is([data-dream-art-safe="right"], [data-dream-art-safe-area="right"])
- .dream-skin-home > div:first-child > div:first-child > div:first-child > div:first-child {
+html[data-dream-skin="active"]:is([data-dream-art-safe="right"], [data-dream-art-safe-area="right"])
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child > div:first-child {
justify-content: flex-end !important;
}
-html.codex-dream-skin:is([data-dream-art-safe="center"], [data-dream-art-safe-area="center"])
- .dream-skin-home > div:first-child > div:first-child > div:first-child > div:first-child {
+html[data-dream-skin="active"]:is([data-dream-art-safe="center"], [data-dream-art-safe-area="center"])
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child > div:first-child {
justify-content: center !important;
}
-.dream-skin-home > div:first-child > div:first-child > div:first-child > div:first-child > div:first-child {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child > div:first-child > div:first-child {
width: min(46%, 520px) !important;
align-items: flex-start !important;
gap: 0 !important;
}
-html.codex-dream-skin:is([data-dream-art-safe="right"], [data-dream-art-safe-area="right"])
- .dream-skin-home > div:first-child > div:first-child > div:first-child > div:first-child > div:first-child {
+html[data-dream-skin="active"]:is([data-dream-art-safe="right"], [data-dream-art-safe-area="right"])
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child > div:first-child > div:first-child {
align-items: flex-end !important;
text-align: right !important;
}
-html.codex-dream-skin:is([data-dream-art-safe="center"], [data-dream-art-safe-area="center"])
- .dream-skin-home > div:first-child > div:first-child > div:first-child > div:first-child > div:first-child {
+html[data-dream-skin="active"]:is([data-dream-art-safe="center"], [data-dream-art-safe-area="center"])
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child > div:first-child > div:first-child {
width: min(58%, 640px) !important;
align-items: center !important;
text-align: center !important;
}
-.dream-skin-home [data-testid="home-icon"] { display: none !important; }
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) [data-testid="home-icon"] { display: none !important; }
-.dream-skin-home [data-feature="game-source"] {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) [data-feature="game-source"] {
display: block !important;
max-width: 100% !important;
color: var(--ds-text) !important;
@@ -659,11 +780,17 @@ html.codex-dream-skin:is([data-dream-art-safe="center"], [data-dream-art-safe-ar
pointer-events: auto !important;
}
-.dream-skin-home [data-feature="game-source"]::before {
- content: none;
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) [data-feature="game-source"]::before {
+ content: var(--dream-skin-name, "Codex Dream Skin");
+ display: block;
+ margin-bottom: 9px;
+ color: var(--ds-accent);
+ font: 800 12px/1.3 "Microsoft YaHei UI", sans-serif;
+ letter-spacing: .10em;
+ text-shadow: 0 0 14px rgb(var(--ds-accent-rgb) / .26);
}
-.dream-skin-home [data-feature="game-source"]::after {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) [data-feature="game-source"]::after {
content: var(--dream-skin-tagline, "把喜欢的画面变成可交互的 Codex 工作台。");
display: block;
margin-top: 13px;
@@ -674,7 +801,7 @@ html.codex-dream-skin:is([data-dream-art-safe="center"], [data-dream-art-safe-ar
letter-spacing: 0;
}
-.dream-skin-home [data-feature="game-source"] button {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) [data-feature="game-source"] button {
margin: 0 5px;
padding: 2px 9px 3px;
border: 1px solid rgb(var(--ds-accent-rgb) / .36);
@@ -685,7 +812,7 @@ html.codex-dream-skin:is([data-dream-art-safe="center"], [data-dream-art-safe-ar
text-underline-offset: 5px;
}
-.dream-skin-home [data-feature="game-source"] button::before {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) [data-feature="game-source"] button::before {
content: var(--dream-skin-project-prefix, "选择项目 · ");
font-size: .46em;
font-weight: 700;
@@ -693,16 +820,16 @@ html.codex-dream-skin:is([data-dream-art-safe="center"], [data-dream-art-safe-ar
opacity: .86;
}
-.dream-skin-home > div:first-child > div:first-child > div:first-child > div:nth-child(2) {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child > div:nth-child(2) {
left: 14px !important;
right: 14px !important;
top: 100% !important;
margin-top: 13px !important;
}
-.dream-skin-home .group\/home-suggestions { overflow: visible !important; }
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) .group\/home-suggestions { overflow: visible !important; }
-.dream-skin-home .group\/home-suggestions button {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) .group\/home-suggestions button {
position: relative !important;
min-height: 118px !important;
padding: 14px 13px 12px !important;
@@ -723,21 +850,24 @@ html.codex-dream-skin:is([data-dream-art-safe="center"], [data-dream-art-safe-ar
border-color .18s cubic-bezier(.22, 1, .36, 1) !important;
}
-.dream-skin-home .group\/home-suggestions button [class~="text-token-text-primary"] {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) .group\/home-suggestions button [class~="text-token-text-primary"] {
color: var(--ds-text) !important;
}
-.dream-skin-home .group\/home-suggestions button:hover {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) .group\/home-suggestions button:hover {
transform: translateY(-2px) !important;
border-color: rgb(var(--ds-accent-rgb) / .42) !important;
box-shadow: 0 12px 28px rgb(var(--ds-bg-rgb) / .24) !important;
}
-.dream-skin-home .group\/home-suggestions button > span:first-child > span:first-child {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) .group\/home-suggestions button > span:first-child > span:first-child {
width: 38px;
height: 38px;
- display: grid !important;
- place-items: center;
+ /* Native spans carry justify-start; grid + place-items cannot override
+ justify-content, so the glyph parks at the circle's left edge. (#176) */
+ display: flex !important;
+ align-items: center !important;
+ justify-content: center !important;
margin: 0 auto;
border: 1px solid rgb(var(--ds-accent-rgb) / .24);
border-radius: 50%;
@@ -746,16 +876,19 @@ html.codex-dream-skin:is([data-dream-art-safe="center"], [data-dream-art-safe-ar
box-shadow: 0 0 0 6px rgb(var(--ds-accent-rgb) / .05);
}
-.dream-skin-home .group\/home-suggestions button svg {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) .group\/home-suggestions button svg {
width: 21px !important;
height: 21px !important;
+ display: block !important;
+ margin: 0 !important;
+ flex: 0 0 auto !important;
color: var(--ds-accent) !important;
}
-.dream-skin-home .group\/home-suggestions button > span:first-child { justify-content: center !important; }
-.dream-skin-home .group\/home-suggestions button > span:last-child { align-items: center !important; text-align: center !important; }
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) .group\/home-suggestions button > span:first-child { justify-content: center !important; }
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) .group\/home-suggestions button > span:last-child { align-items: center !important; text-align: center !important; }
-html.codex-dream-skin .composer-surface-chrome {
+html[data-dream-skin="active"] .composer-surface-chrome {
overflow: visible !important;
border: 1px solid rgb(var(--ds-muted-rgb) / .18) !important;
border-radius: 22px !important;
@@ -764,11 +897,11 @@ html.codex-dream-skin .composer-surface-chrome {
backdrop-filter: blur(16px) saturate(108%) !important;
}
-html.codex-dream-skin .composer-surface-chrome::before { content: none !important; }
+html[data-dream-skin="active"] .composer-surface-chrome::before { content: none !important; }
/* A wide image can carry the home screen too. The native hero remains live,
but the artwork is painted once on the window instead of repeated in a card. */
-html.codex-dream-skin[data-dream-art-wide="true"]:has(main.main-surface.dream-skin-home-shell) body {
+html[data-dream-skin="active"][data-dream-art-wide="true"]:has(main.main-surface [role="main"]) body {
background-image: var(--dream-skin-art) !important;
background-position: var(--ds-art-position) !important;
background-size: cover !important;
@@ -776,7 +909,7 @@ html.codex-dream-skin[data-dream-art-wide="true"]:has(main.main-surface.dream-sk
background-attachment: fixed !important;
}
-html.codex-dream-skin[data-dream-art-wide="true"]:has(main.main-surface.dream-skin-home-shell)
+html[data-dream-skin="active"][data-dream-art-wide="true"]:has(main.main-surface [role="main"])
aside.app-shell-left-panel {
background: linear-gradient(90deg,
var(--ds-immersive-sidebar),
@@ -787,12 +920,12 @@ html.codex-dream-skin[data-dream-art-wide="true"]:has(main.main-surface.dream-sk
backdrop-filter: none !important;
}
-html.codex-dream-skin[data-dream-art-wide="true"]:has(main.main-surface.dream-skin-home-shell)
+html[data-dream-skin="active"][data-dream-art-wide="true"]:has(main.main-surface [role="main"])
aside.app-shell-left-panel::after {
background: transparent !important;
}
-html.codex-dream-skin[data-dream-art-wide="true"] main.main-surface.dream-skin-home-shell {
+html[data-dream-skin="active"][data-dream-art-wide="true"] main.main-surface:has([role="main"]) {
background: linear-gradient(90deg,
var(--ds-immersive-edge),
var(--ds-immersive-mid) 64%,
@@ -802,7 +935,7 @@ html.codex-dream-skin[data-dream-art-wide="true"] main.main-surface.dream-skin-h
box-shadow: none !important;
}
-html.codex-dream-skin[data-dream-art-wide="true"] main.main-surface.dream-skin-home-shell
+html[data-dream-skin="active"][data-dream-art-wide="true"] main.main-surface:has([role="main"])
> header.app-header-tint {
background: transparent !important;
border-bottom: 0 !important;
@@ -810,7 +943,7 @@ html.codex-dream-skin[data-dream-art-wide="true"] main.main-surface.dream-skin-h
backdrop-filter: none !important;
}
-html.codex-dream-skin[data-dream-art-wide="true"] main.main-surface
+html[data-dream-skin="active"][data-dream-art-wide="true"] main.main-surface
> header.app-header-tint {
color: var(--ds-text) !important;
text-shadow:
@@ -818,27 +951,27 @@ html.codex-dream-skin[data-dream-art-wide="true"] main.main-surface
0 0 8px rgb(var(--ds-bg-rgb) / .52) !important;
}
-html.codex-dream-skin[data-dream-art-wide="true"] main.main-surface
+html[data-dream-skin="active"][data-dream-art-wide="true"] main.main-surface
> header.app-header-tint svg {
color: rgb(var(--ds-muted-rgb) / .96) !important;
filter: drop-shadow(0 1px 2px rgb(var(--ds-bg-rgb) / .72));
}
-html.codex-dream-skin[data-dream-art-wide="true"] main.main-surface.dream-skin-home-shell
- .dream-skin-home > div:first-child > div:first-child > div:first-child {
+html[data-dream-skin="active"][data-dream-art-wide="true"] main.main-surface:has([role="main"])
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child {
border: 0 !important;
border-radius: 0 !important;
background: transparent !important;
box-shadow: none !important;
}
-html.codex-dream-skin[data-dream-art-wide="true"] main.main-surface.dream-skin-home-shell
- .dream-skin-home > div:first-child > div:first-child > div:first-child::before {
+html[data-dream-skin="active"][data-dream-art-wide="true"] main.main-surface:has([role="main"])
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child::before {
content: none !important;
}
-html.codex-dream-skin[data-dream-art-wide="true"] main.main-surface.dream-skin-home-shell
- .dream-skin-home .group\/home-suggestions button {
+html[data-dream-skin="active"][data-dream-art-wide="true"] main.main-surface:has([role="main"])
+ [role="main"]:has([data-testid="home-icon"]) .group\/home-suggestions button {
background: rgb(var(--ds-panel-rgb) / .56) !important;
box-shadow:
0 8px 22px rgb(var(--ds-bg-rgb) / .16),
@@ -846,7 +979,7 @@ html.codex-dream-skin[data-dream-art-wide="true"] main.main-surface.dream-skin-h
backdrop-filter: blur(8px) saturate(104%) !important;
}
-html.codex-dream-skin[data-dream-art-wide="true"] .composer-surface-chrome {
+html[data-dream-skin="active"][data-dream-art-wide="true"] .composer-surface-chrome {
background: var(--ds-immersive-composer-solid) !important;
border: 0 !important;
box-shadow:
@@ -856,7 +989,7 @@ html.codex-dream-skin[data-dream-art-wide="true"] .composer-surface-chrome {
backdrop-filter: none !important;
}
-html.codex-dream-skin[data-dream-shell="light"][data-dream-art-wide="true"]
+html[data-dream-skin="active"][data-dream-shell="light"][data-dream-art-wide="true"]
.composer-surface-chrome {
box-shadow:
0 10px 28px rgb(var(--ds-bg-rgb) / .14),
@@ -865,7 +998,7 @@ html.codex-dream-skin[data-dream-shell="light"][data-dream-art-wide="true"]
backdrop-filter: blur(8px) saturate(102%) !important;
}
-html.codex-dream-skin[data-dream-shell="light"]
+html[data-dream-skin="active"][data-dream-shell="light"]
.composer-surface-chrome p.placeholder::after {
color: rgb(var(--ds-muted-rgb) / .78) !important;
opacity: 1 !important;
@@ -874,7 +1007,7 @@ html.codex-dream-skin[data-dream-shell="light"]
/* Current Codex builds render the home project picker as a separate opaque
cap above the composer. Join both native controls into one continuous
surface while leaving task and utility-route inputs untouched. */
-html.codex-dream-skin .dream-skin-home-utility {
+html[data-dream-skin="active"] [class*="_homeUtilityBar_"] {
top: 0 !important;
width: 100% !important;
margin-inline: 0 !important;
@@ -888,7 +1021,7 @@ html.codex-dream-skin .dream-skin-home-utility {
backdrop-filter: blur(16px) saturate(108%) !important;
}
-html.codex-dream-skin .dream-skin-home:has(.dream-skin-home-utility)
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]):has([class*="_homeUtilityBar_"])
.composer-surface-chrome {
border: 0 !important;
border-radius: 0 0 22px 22px !important;
@@ -899,7 +1032,7 @@ html.codex-dream-skin .dream-skin-home:has(.dream-skin-home-utility)
inset 0 -1px rgb(var(--ds-muted-rgb) / .18) !important;
}
-html.codex-dream-skin[data-dream-art-wide="true"] .dream-skin-home-utility {
+html[data-dream-skin="active"][data-dream-art-wide="true"] [class*="_homeUtilityBar_"] {
background: var(--ds-immersive-composer-solid) !important;
box-shadow:
inset 1px 0 var(--ds-immersive-line),
@@ -908,8 +1041,8 @@ html.codex-dream-skin[data-dream-art-wide="true"] .dream-skin-home-utility {
backdrop-filter: none !important;
}
-html.codex-dream-skin[data-dream-art-wide="true"]
- .dream-skin-home:has(.dream-skin-home-utility) .composer-surface-chrome {
+html[data-dream-skin="active"][data-dream-art-wide="true"]
+ [role="main"]:has([data-testid="home-icon"]):has([class*="_homeUtilityBar_"]) .composer-surface-chrome {
box-shadow:
0 10px 30px rgb(var(--ds-bg-rgb) / .20),
inset 1px 0 var(--ds-immersive-line),
@@ -917,8 +1050,8 @@ html.codex-dream-skin[data-dream-art-wide="true"]
inset 0 -1px var(--ds-immersive-line) !important;
}
-html.codex-dream-skin[data-dream-shell="light"][data-dream-art-wide="true"]
- .dream-skin-home-utility {
+html[data-dream-skin="active"][data-dream-shell="light"][data-dream-art-wide="true"]
+ [class*="_homeUtilityBar_"] {
box-shadow:
inset 1px 0 var(--ds-immersive-line),
inset -1px 0 var(--ds-immersive-line),
@@ -926,46 +1059,46 @@ html.codex-dream-skin[data-dream-shell="light"][data-dream-art-wide="true"]
backdrop-filter: blur(8px) saturate(102%) !important;
}
-html.codex-dream-skin .dream-skin-home-utility button,
-html.codex-dream-skin .composer-surface-chrome button:not([class~="bg-token-foreground"]) {
+html[data-dream-skin="active"] [class*="_homeUtilityBar_"] button,
+html[data-dream-skin="active"] .composer-surface-chrome button:not([class~="bg-token-foreground"]) {
color: var(--ds-muted) !important;
}
-html.codex-dream-skin .dream-skin-home-utility button svg,
-html.codex-dream-skin .composer-surface-chrome button svg {
+html[data-dream-skin="active"] [class*="_homeUtilityBar_"] button svg,
+html[data-dream-skin="active"] .composer-surface-chrome button svg {
color: currentColor !important;
}
-html.codex-dream-skin .dream-skin-home-utility button *,
-html.codex-dream-skin .composer-surface-chrome button:not([class~="bg-token-foreground"]) * {
+html[data-dream-skin="active"] [class*="_homeUtilityBar_"] button *,
+html[data-dream-skin="active"] .composer-surface-chrome button:not([class~="bg-token-foreground"]) * {
color: currentColor !important;
}
-html.codex-dream-skin .dream-skin-home-utility button:hover,
-html.codex-dream-skin .composer-surface-chrome button:not([class~="bg-token-foreground"]):hover {
+html[data-dream-skin="active"] [class*="_homeUtilityBar_"] button:hover,
+html[data-dream-skin="active"] .composer-surface-chrome button:not([class~="bg-token-foreground"]):hover {
color: var(--ds-text) !important;
background: rgb(var(--ds-accent-rgb) / .10) !important;
}
-html.codex-dream-skin .composer-surface-chrome p.placeholder::after {
+html[data-dream-skin="active"] .composer-surface-chrome p.placeholder::after {
color: rgb(var(--ds-muted-rgb) / .82) !important;
opacity: 1 !important;
}
/* Search routes ship an opaque sticky band and input surface. Keep the
control legible while allowing the selected image to remain continuous. */
-html.codex-dream-skin:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"])[data-dream-art-wide="true"]
- main.main-surface:not(.dream-skin-home-shell) div.sticky:has(input[type="text"]) {
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"])[data-dream-art-wide="true"]
+ main.main-surface:not(:has([role="main"])) div.sticky:has(input[type="text"]) {
background: transparent !important;
}
-html.codex-dream-skin:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"])[data-dream-art-wide="true"]
- main.main-surface:not(.dream-skin-home-shell) div.sticky:has(input[type="text"])::after {
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"])[data-dream-art-wide="true"]
+ main.main-surface:not(:has([role="main"])) div.sticky:has(input[type="text"])::after {
background: transparent !important;
}
-html.codex-dream-skin:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"])[data-dream-art-wide="true"]
- main.main-surface:not(.dream-skin-home-shell) div.no-drag:has(> input[type="text"]) {
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"])[data-dream-art-wide="true"]
+ main.main-surface:not(:has([role="main"])) div.no-drag:has(> input[type="text"]) {
background: var(--ds-immersive-composer) !important;
border: 0 !important;
box-shadow:
@@ -976,18 +1109,18 @@ html.codex-dream-skin:is([data-dream-task-mode="ambient"], [data-dream-art-task-
/* Pull Requests and similar utility routes wrap their content in a native
full-size opaque surface. Only clear full-window wrappers, not cards. */
-html.codex-dream-skin:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"])[data-dream-art-wide="true"]
- main.main-surface:not(.dream-skin-home-shell)
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"])[data-dream-art-wide="true"]
+ main.main-surface:not(:has([role="main"]))
[class~="bg-token-main-surface-primary"][class~="h-full"][class~="w-full"] {
background: transparent !important;
}
-html.codex-dream-skin[data-dream-art-wide="true"] main.main-surface
+html[data-dream-skin="active"][data-dream-art-wide="true"] main.main-surface
.app-shell-main-content-frame {
border-top: 0 !important;
}
-html.codex-dream-skin[data-dream-art-wide="true"] main.main-surface
+html[data-dream-skin="active"][data-dream-art-wide="true"] main.main-surface
.app-shell-main-content-top-fade {
display: none !important;
background: transparent !important;
@@ -996,12 +1129,12 @@ html.codex-dream-skin[data-dream-art-wide="true"] main.main-surface
/* Codex paints a second opaque fade behind the sticky composer. The composer
already owns its readable surface, so retaining this layer creates a false
bottom panel and makes the control look duplicated. */
-html.codex-dream-skin[data-dream-art-wide="true"] main.main-surface
+html[data-dream-skin="active"][data-dream-art-wide="true"] main.main-surface
.thread-scroll-container .bg-gradient-to-t.from-token-main-surface-primary {
background: transparent !important;
}
-.dream-skin-home div:has(> .horizontal-scroll-fade-mask .group\/project-selector) {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) div:has(> .horizontal-scroll-fade-mask .group\/project-selector) {
position: relative;
padding-top: 28px !important;
border: 1px solid rgb(var(--ds-muted-rgb) / .16);
@@ -1009,7 +1142,7 @@ html.codex-dream-skin[data-dream-art-wide="true"] main.main-surface
background: rgb(var(--ds-panel-rgb) / .92) !important;
}
-.dream-skin-home div:has(> .horizontal-scroll-fade-mask .group\/project-selector)::before {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) div:has(> .horizontal-scroll-fade-mask .group\/project-selector)::before {
content: var(--dream-skin-project-label, "选择项目");
position: absolute;
left: 13px;
@@ -1022,58 +1155,57 @@ html.codex-dream-skin[data-dream-art-wide="true"] main.main-surface
white-space: nowrap;
}
-.dream-skin-home .group\/project-selector > button {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) .group\/project-selector > button {
border-color: rgb(var(--ds-accent-rgb) / .22) !important;
background: rgb(var(--ds-accent-rgb) / .08) !important;
color: var(--ds-text) !important;
box-shadow: 0 3px 12px rgb(var(--ds-bg-rgb) / .12) !important;
}
-html.codex-dream-skin .ProseMirror {
+html[data-dream-skin="active"] .ProseMirror {
color: var(--ds-text) !important;
caret-color: var(--ds-accent) !important;
}
-html.codex-dream-skin button[class~="bg-token-foreground"] {
+html[data-dream-skin="active"] button[class~="bg-token-foreground"] {
background: var(--ds-accent) !important;
color: var(--ds-on-accent) !important;
box-shadow: 0 5px 14px rgb(var(--ds-accent-rgb) / .20) !important;
}
-html.codex-dream-skin article,
-html.codex-dream-skin [data-message-author-role] { border-radius: 16px; }
+html[data-dream-skin="active"] article,
+html[data-dream-skin="active"] [data-message-author-role] { border-radius: 16px; }
@media (max-width: 1120px) {
- .dream-skin-status { display: none !important; }
- .dream-skin-home { --thread-content-max-width: min(940px, calc(100cqw - 30px)) !important; }
- .dream-skin-home > div:first-child > div:first-child > div:first-child { width: calc(100% - 28px) !important; }
+ html[data-dream-skin="active"]
+ main.main-surface:has([role="main"])::after { content: none; }
+ html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) { --thread-content-max-width: min(940px, calc(100cqw - 30px)) !important; }
+ html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child { width: calc(100% - 28px) !important; }
}
@media (max-width: 900px) {
- .dream-skin-brand { left: 15px; }
- .dream-skin-brand b { font-size: 12px; }
- .dream-skin-home > div:first-child > div:first-child {
+ html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child {
flex-basis: 408px !important;
min-height: 408px !important;
}
- .dream-skin-home > div:first-child > div:first-child > div:first-child {
+ html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child {
height: 232px !important;
min-height: 232px !important;
}
- .dream-skin-home > div:first-child > div:first-child > div:first-child > div:first-child { padding: 0 26px; }
- .dream-skin-home > div:first-child > div:first-child > div:first-child > div:first-child > div:first-child {
+ html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child > div:first-child { padding: 0 26px; }
+ html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child > div:first-child > div:first-child {
width: min(58%, 460px) !important;
}
- .dream-skin-home [data-feature="game-source"] { font-size: 18px !important; }
- .dream-skin-home [data-feature="game-source"]::before { margin-bottom: 7px; font-size: 10px; }
- .dream-skin-home [data-feature="game-source"]::after { margin-top: 9px; font-size: 11px; }
- .dream-skin-home .group\/home-suggestions button { min-height: 112px !important; font-size: 12px !important; }
+ html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) [data-feature="game-source"] { font-size: 18px !important; }
+ html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) [data-feature="game-source"]::before { margin-bottom: 7px; font-size: 10px; }
+ html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) [data-feature="game-source"]::after { margin-top: 9px; font-size: 11px; }
+ html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) .group\/home-suggestions button { min-height: 112px !important; font-size: 12px !important; }
}
@media (prefers-reduced-motion: reduce) {
- html.codex-dream-skin *,
- html.codex-dream-skin *::before,
- html.codex-dream-skin *::after {
+ html[data-dream-skin="active"] *,
+ html[data-dream-skin="active"] *::before,
+ html[data-dream-skin="active"] *::after {
scroll-behavior: auto !important;
transition-duration: .01ms !important;
animation-duration: .01ms !important;
diff --git a/assets/inject/upstream/dream-skin/macos/renderer-inject.js b/assets/inject/upstream/dream-skin/macos/renderer-inject.js
index 9988cf181..be7998889 100644
--- a/assets/inject/upstream/dream-skin/macos/renderer-inject.js
+++ b/assets/inject/upstream/dream-skin/macos/renderer-inject.js
@@ -1,10 +1,14 @@
+// Canonical cross-platform renderer. Run tools/sync-runtime-assets.mjs after editing.
((cssText, artDataUrl, themeConfig) => {
+ const SELECTOR_CONTRACT = {"schema":"codex-dream-skin-selectors/1","selectors":[{"key":"shell-main","selector":"main.main-surface","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.app-header-tint","tier":"L1","scope":"all","required":true},{"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":"[data-message-author-role]","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"]};
const STATE_KEY = "__CODEX_DREAM_SKIN_STATE__";
const DISABLED_KEY = "__CODEX_DREAM_SKIN_DISABLED__";
+ const STYLE_REGISTRY_KEY = "__CODEX_DREAM_SKIN_STYLE_SHEETS__";
const STYLE_ID = "codex-dream-skin-style";
- const CHROME_ID = "codex-dream-skin-chrome";
const SHELL_ATTR = "data-dream-shell";
- const ART_ATTRS = [
+ const PART_ATTR = "data-ds-part";
+ const ROOT_ATTRS = [
+ "data-dream-skin", SHELL_ATTR,
"data-dream-art-wide", "data-dream-art-safe", "data-dream-task-mode",
"data-dream-art-safe-area", "data-dream-art-task-mode", "data-dream-art-aspect",
"data-dream-art-ready",
@@ -26,8 +30,24 @@
"--dream-art-focus-x", "--dream-art-focus-y", "--dream-art-position",
"--dream-skin-focus-x", "--dream-skin-focus-y", "--dream-skin-art-position",
"--dream-skin-name", "--dream-skin-tagline", "--dream-skin-project-prefix",
- "--dream-skin-project-label",
+ "--dream-skin-project-label", "--dream-skin-brand-subtitle", "--dream-skin-status",
+ "--dream-skin-quote", "--dream-skin-art",
+ "--ds-theme-color-background", "--ds-theme-color-panel",
+ "--ds-theme-color-panel-alt", "--ds-theme-color-accent",
+ "--ds-theme-color-accent-alt", "--ds-theme-color-secondary",
+ "--ds-theme-color-highlight", "--ds-theme-color-text",
+ "--ds-theme-color-muted", "--ds-theme-color-line",
+ "--ds-theme-font-family", "--ds-theme-font-scale",
+ "--ds-theme-surface-radius", "--ds-theme-surface-opacity",
+ "--ds-theme-surface-blur", "--ds-theme-surface-border-alpha",
+ "--ds-theme-surface-shadow", "--ds-theme-image-focus-x",
+ "--ds-theme-image-focus-y", "--ds-theme-image-zoom",
+ "--ds-theme-image-dim", "--ds-theme-image-task-intensity",
+ "--ds-theme-density-scale", "--ds-theme-motion-level",
];
+ const selectorByKey = new Map(SELECTOR_CONTRACT.selectors.map((entry) => [entry.key, entry]));
+ const stableTestidSelector = (testid) => SELECTOR_CONTRACT.stableTestids?.includes(testid)
+ ? `[data-testid="${testid}"]` : null;
const installToken = {};
const existingAnalysisCache = window[ANALYSIS_CACHE_KEY];
const analysisCache = existingAnalysisCache && typeof existingAnalysisCache.get === "function" &&
@@ -35,8 +55,12 @@
window[ANALYSIS_CACHE_KEY] = analysisCache;
let artAnalysis = typeof THEME.artKey === "string" ? analysisCache.get(THEME.artKey) ?? null : null;
let analysisTimer = null;
- let samplingNativeShell = false;
let rootObserver = null;
+ let partObserver = null;
+ let bodyReadyHandler = null;
+ let styleMode = null;
+ let styleNode = null;
+ let styleSheet = null;
const now = () => typeof performance === "object" && typeof performance.now === "function"
? performance.now() : Date.now();
const metrics = {
@@ -46,15 +70,24 @@
layoutReads: 0,
attributeWrites: 0,
styleWrites: 0,
- textWrites: 0,
+ styleRepairs: 0,
+ partPasses: 0,
+ partWrites: 0,
+ navigationEvents: 0,
+ safetyPasses: 0,
analysisRuns: 0,
analysisCacheHits: artAnalysis ? 1 : 0,
firstEnsureMs: null,
analysisMs: null,
};
- window[DISABLED_KEY] = false;
const previous = window[STATE_KEY];
+ if (typeof previous?.cleanup === "function") previous.cleanup();
+ window[DISABLED_KEY] = false;
+
+ const existingStyleRegistry = window[STYLE_REGISTRY_KEY];
+ const styleRegistry = existingStyleRegistry instanceof Set ? existingStyleRegistry : new Set();
+ window[STYLE_REGISTRY_KEY] = styleRegistry;
const artUrl = (() => {
const comma = artDataUrl.indexOf(",");
const mime = /^data:([^;,]+)/.exec(artDataUrl)?.[1] || "image/png";
@@ -64,20 +97,6 @@
return URL.createObjectURL(new Blob([bytes], { type: mime }));
})();
- if (previous?.observer) previous.observer.disconnect();
- if (previous?.rootObserver) previous.rootObserver.disconnect();
- if (previous?.resizeObserver) previous.resizeObserver.disconnect();
- if (previous?.timer) clearInterval(previous.timer);
- if (previous?.scheduler?.timeout) clearTimeout(previous.scheduler.timeout);
- if (previous?.scheduler?.frame != null && typeof cancelAnimationFrame === "function") {
- cancelAnimationFrame(previous.scheduler.frame);
- }
- if (previous?.analysisTimer) clearTimeout(previous.analysisTimer);
- if (previous?.resizeHandler) window.removeEventListener("resize", previous.resizeHandler);
- if (previous?.mediaHandler && previous?.mediaQuery) {
- try { previous.mediaQuery.removeEventListener("change", previous.mediaHandler); } catch {}
- }
-
const cssString = (value) => JSON.stringify(String(value ?? ""));
const setStyleProperty = (root, name, value) => {
@@ -95,18 +114,14 @@
}
};
- const setTextContent = (node, value) => {
- if (node && node.textContent !== value) {
- node.textContent = value;
- metrics.textWrites += 1;
- }
- };
-
const parseRgb = (value) => {
if (!value || value === "transparent") return null;
- const hex = String(value).trim().match(/^#([0-9a-f]{6})$/i);
+ const hex = String(value).trim().match(/^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i);
if (hex) {
- const number = Number.parseInt(hex[1], 16);
+ const rgbHex = hex[1].length <= 4
+ ? hex[1].slice(0, 3).split("").map((digit) => `${digit}${digit}`).join("")
+ : hex[1].slice(0, 6);
+ const number = Number.parseInt(rgbHex, 16);
return { r: number >> 16, g: (number >> 8) & 255, b: number & 255 };
}
const m = String(value).match(/rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)/i);
@@ -118,7 +133,9 @@
const rgbString = (value) => {
const rgb = parseRgb(value);
- return rgb ? `${Math.round(rgb.r)} ${Math.round(rgb.g)} ${Math.round(rgb.b)}` : null;
+ return rgb ? [rgb.r, rgb.g, rgb.b]
+ .map((channel) => Math.round(clamp(channel, 0, 255)))
+ .join(" ") : null;
};
const rgbToHex = ({ r, g, b }) => `#${[r, g, b]
@@ -160,98 +177,11 @@
return { r: channel(1 / 3) * 255, g: channel(0) * 255, b: channel(-1 / 3) * 255 };
};
- const luminance = ({ r, g, b }) => {
- const lin = [r, g, b].map((c) => {
- const x = c / 255;
- return x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4;
- });
- return 0.2126 * lin[0] + 0.7152 * lin[1] + 0.0722 * lin[2];
- };
-
- /** Detect Codex app light/dark shell for CSS branching. */
- const detectShellMode = () => {
+ const detectShellAppearance = () => {
const root = document.documentElement;
- const body = document.body;
- const cls = `${root.className || ""} ${body?.className || ""}`.toLowerCase();
-
- if (/\b(dark|theme-dark|appearance-dark)\b/.test(cls)) return "dark";
- if (/\b(light|theme-light|appearance-light)\b/.test(cls)) return "light";
-
- const dataTheme = (
- root.getAttribute("data-theme") ||
- root.getAttribute("data-appearance") ||
- root.getAttribute("data-color-mode") ||
- body?.getAttribute("data-theme") ||
- body?.getAttribute("data-appearance") ||
- ""
- ).toLowerCase();
- if (dataTheme.includes("dark")) return "dark";
- if (dataTheme.includes("light")) return "light";
-
- // Radios in profile menu (if present in DOM)
- const checked = document.querySelector('input[name="appearance-theme"]:checked');
- if (checked) {
- const label = (checked.getAttribute("aria-label") || checked.value || "").toLowerCase();
- if (label.includes("暗") || label.includes("dark")) return "dark";
- if (label.includes("浅") || label.includes("light")) return "light";
- if (label.includes("系统") || label.includes("system")) {
- return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
- }
- }
-
- // The skin itself declares color-scheme on :root. Once installed,
- // reading getComputedStyle(root) directly would therefore keep `auto`
- // themes locked to the previous shell mode. Temporarily remove only our
- // own root class/attribute, sample the native computed scheme, then restore
- // synchronously. Mutation records created by this probe are drained below
- // so the root observer does not schedule a redundant ensure pass.
- try {
- const hadSkin = root.classList.contains("codex-dream-skin");
- const savedShell = root.getAttribute(SHELL_ATTR);
- samplingNativeShell = true;
- if (hadSkin) root.classList.remove("codex-dream-skin");
- if (savedShell !== null) root.removeAttribute(SHELL_ATTR);
- let colorScheme = "";
- try {
- colorScheme = getComputedStyle(root).colorScheme || "";
- } finally {
- if (hadSkin) root.classList.add("codex-dream-skin");
- if (savedShell !== null) root.setAttribute(SHELL_ATTR, savedShell);
- rootObserver?.takeRecords?.();
- samplingNativeShell = false;
- }
- if (colorScheme.includes("dark") && !colorScheme.includes("light")) return "dark";
- if (colorScheme.includes("light") && !colorScheme.includes("dark")) return "light";
- } catch {
- samplingNativeShell = false;
- }
-
- try {
- return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
- } catch {}
-
- // Only use surface luminance before the skin owns those surfaces. Sampling
- // our own translucent layers would create route-dependent light/dark flips.
- if (!root.classList.contains("codex-dream-skin")) {
- const samples = [
- body,
- document.querySelector("main.main-surface"),
- document.querySelector("aside.app-shell-left-panel"),
- ].filter(Boolean);
- let votesLight = 0;
- let votesDark = 0;
- for (const el of samples) {
- try {
- const rgb = parseRgb(getComputedStyle(el).backgroundColor);
- if (!rgb) continue;
- const L = luminance(rgb);
- if (L >= 0.55) votesLight += 1;
- else if (L <= 0.25) votesDark += 1;
- } catch {}
- }
- if (votesLight > votesDark) return "light";
- if (votesDark > votesLight) return "dark";
- }
+ if (root?.classList?.contains("electron-dark")) return "dark";
+ if (root?.classList?.contains("electron-light")) return "light";
+ try { return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; } catch {}
return "light";
};
@@ -295,14 +225,24 @@
// Image luminance may tune accents and scrims, but auto appearance follows
// Codex/ChatGPT (or the OS fallback) so a bright wallpaper cannot flip a
// native dark session back to a light shell after analysis.
- return detectShellMode();
+ return detectShellAppearance();
};
const applyTheme = (root, shell) => {
- const colors = THEME.colors || {};
- const explicit = new Set(Array.isArray(THEME.explicitColorKeys) ? THEME.explicitColorKeys : []);
+ const declaredColors = THEME.colors && typeof THEME.colors === "object" ? THEME.colors : {};
+ const legacyPalette = THEME.palette && typeof THEME.palette === "object" ? THEME.palette : {};
+ // macOS themes use the full `colors` contract; older Windows themes used
+ // `palette.accent`. Accept both while keeping one renderer source.
+ const colors = Object.keys(declaredColors).length ? declaredColors : legacyPalette;
+ const hasExplicitKeyList = Array.isArray(THEME.explicitColorKeys);
+ const explicit = new Set(hasExplicitKeyList ? THEME.explicitColorKeys : []);
+ if (!hasExplicitKeyList && (THEME.colorMode === "explicit" || !Object.hasOwn(THEME, "colorMode"))) {
+ for (const key of Object.keys(declaredColors)) explicit.add(key);
+ }
+ if (typeof legacyPalette.accent === "string") explicit.add("accent");
const adaptive = makeAdaptivePalette(artAnalysis?.accentRgb, shell);
- const legacyLight = !THEME.appearance && shell === "light";
+ const legacyLight = (THEME.appearance === undefined || THEME.appearance === "auto")
+ && THEME.colorMode !== "explicit" && shell === "light";
const structural = new Set(["background", "panel", "panelAlt", "text", "muted"]);
const pick = (name) => {
const allowExplicit = explicit.has(name) && !(legacyLight && structural.has(name));
@@ -326,6 +266,33 @@
for (const [name, value] of Object.entries(variables)) {
if (typeof value === "string" && value) setStyleProperty(root, name, value);
}
+ const publicColors = {
+ "--ds-theme-color-background": variables["--ds-bg"],
+ "--ds-theme-color-panel": variables["--ds-panel"],
+ "--ds-theme-color-panel-alt": variables["--ds-panel-2"],
+ "--ds-theme-color-accent": variables["--ds-green"],
+ "--ds-theme-color-accent-alt": variables["--ds-lime"],
+ "--ds-theme-color-secondary": variables["--ds-cyan"],
+ "--ds-theme-color-highlight": variables["--ds-purple"],
+ "--ds-theme-color-text": variables["--ds-text"],
+ "--ds-theme-color-muted": variables["--ds-muted"],
+ "--ds-theme-color-line": variables["--ds-line"],
+ };
+ for (const [name, value] of Object.entries(publicColors)) {
+ if (typeof value === "string" && value) setStyleProperty(root, name, value);
+ }
+ setStyleProperty(root, "--ds-theme-surface-radius", "12px");
+ setStyleProperty(root, "--ds-theme-surface-opacity", "1");
+ setStyleProperty(root, "--ds-theme-surface-blur", "0px");
+ setStyleProperty(root, "--ds-theme-font-family", "system");
+ setStyleProperty(root, "--ds-theme-font-scale", "1");
+ setStyleProperty(root, "--ds-theme-surface-border-alpha", "0.14");
+ setStyleProperty(root, "--ds-theme-surface-shadow", "soft");
+ setStyleProperty(root, "--ds-theme-image-zoom", "1");
+ setStyleProperty(root, "--ds-theme-image-dim", "0");
+ setStyleProperty(root, "--ds-theme-image-task-intensity", "0.35");
+ setStyleProperty(root, "--ds-theme-density-scale", "standard");
+ setStyleProperty(root, "--ds-theme-motion-level", "standard");
const rgbVariables = {
"--ds-bg-rgb": variables["--ds-bg"],
"--ds-panel-rgb": variables["--ds-panel"],
@@ -344,6 +311,11 @@
}
setStyleProperty(root, "--dream-skin-name", cssString(THEME.name || "Codex Dream Skin"));
setStyleProperty(root, "--dream-skin-tagline", cssString(THEME.tagline || "Make something wonderful."));
+ setStyleProperty(root, "--dream-skin-quote", cssString(THEME.quote || "MAKE SOMETHING WONDERFUL"));
+ setStyleProperty(root, "--dream-skin-brand-subtitle", cssString(
+ THEME.brandSubtitle || "CODEX DREAM SKIN",
+ ));
+ setStyleProperty(root, "--dream-skin-status", cssString(THEME.statusText || "DREAM SKIN ONLINE"));
setStyleProperty(root, "--dream-skin-project-prefix", cssString(THEME.projectPrefix || "选择项目 · "));
setStyleProperty(root, "--dream-skin-project-label", cssString(THEME.projectLabel || "◉ 选择项目"));
};
@@ -377,6 +349,8 @@
setStyleProperty(root, "--dream-skin-focus-x", focusXValue);
setStyleProperty(root, "--dream-skin-focus-y", focusYValue);
setStyleProperty(root, "--dream-skin-art-position", `${focusXValue} ${focusYValue}`);
+ setStyleProperty(root, "--ds-theme-image-focus-x", String(Number(focusX.toFixed(4))));
+ setStyleProperty(root, "--ds-theme-image-focus-y", String(Number(focusY.toFixed(4))));
};
const analyzeArt = () => new Promise((resolve) => {
@@ -524,247 +498,327 @@
image.src = artUrl;
});
- let chromeParts = null;
- let observedShellMain = null;
- let resizeObserver = null;
-
- const ensureStyle = (root) => {
- let style = document.getElementById(STYLE_ID);
- if (!style) {
- style = document.createElement("style");
- style.id = STYLE_ID;
- style.textContent = cssText;
- style.dataset.dreamSkinVersion = VERSION;
- (document.head || root).appendChild(style);
- } else if (style.dataset.dreamSkinStyleRevision !== STYLE_REVISION) {
- style.textContent = cssText;
+ const installStyle = () => {
+ try {
+ if (!("adoptedStyleSheets" in document) || typeof CSSStyleSheet !== "function") {
+ throw new Error("Constructable stylesheets are unavailable");
+ }
+ const sheet = new CSSStyleSheet();
+ if (typeof sheet.replaceSync !== "function") throw new Error("replaceSync is unavailable");
+ sheet.replaceSync(cssText);
+ const retained = [...document.adoptedStyleSheets]
+ .filter((candidate) => !styleRegistry.has(candidate));
+ document.adoptedStyleSheets = [...retained, sheet];
+ styleRegistry.clear();
+ styleRegistry.add(sheet);
+ document.getElementById(STYLE_ID)?.remove();
+ styleSheet = sheet;
+ styleMode = "adopted";
+ return;
+ } catch {
+ styleSheet = null;
+ }
+
+ styleNode = document.getElementById(STYLE_ID) || document.createElement("style");
+ styleNode.id = STYLE_ID;
+ styleNode.textContent = cssText;
+ if (!styleNode.parentElement) (document.head || document.documentElement).appendChild(styleNode);
+ styleMode = "style";
+ };
+
+ const ensureStyle = () => {
+ if (styleMode === "adopted" && styleSheet) {
+ const current = [...document.adoptedStyleSheets];
+ if (!current.includes(styleSheet)) {
+ document.adoptedStyleSheets = [...current, styleSheet];
+ metrics.styleRepairs += 1;
+ }
+ return;
+ }
+ if (styleNode && document.getElementById(STYLE_ID) !== styleNode) {
+ document.getElementById(STYLE_ID)?.remove();
+ (document.head || document.documentElement).appendChild(styleNode);
+ metrics.styleRepairs += 1;
}
- style.dataset.dreamSkinVersion = VERSION;
- style.dataset.dreamSkinStyleRevision = STYLE_REVISION;
- return style;
};
+ installStyle();
+
const applyRootState = (root) => {
metrics.rootPasses += 1;
- ensureStyle(root);
+ ensureStyle();
const shell = resolvedShell();
+ setAttribute(root, "data-dream-skin", "active");
setAttribute(root, SHELL_ATTR, shell);
setStyleProperty(root, "--dream-skin-art", `url("${artUrl}")`);
applyTheme(root, shell);
applyArtMetadata(root);
- root.classList.add("codex-dream-skin");
return shell;
};
- const syncRouteState = (shell, { layout = false } = {}) => {
- metrics.routePasses += 1;
- const root = document.documentElement;
- if (!root) return;
- shell ||= root.getAttribute(SHELL_ATTR) || resolvedShell();
- const shellMain = document.querySelector("main.main-surface") || document.querySelector("main");
- const homeIndicator = document.querySelector('[data-testid="home-icon"]');
- const home = homeIndicator?.closest('[role="main"]') ||
- [...document.querySelectorAll('[role="main"]')].find((candidate) =>
- candidate.querySelector('[data-feature="game-source"]') &&
- candidate.querySelector('.group\\\\/home-suggestions')) || null;
- for (const candidate of document.querySelectorAll('[role="main"].dream-skin-home')) {
- if (candidate !== home) candidate.classList.remove("dream-skin-home");
- }
- if (home) home.classList.add("dream-skin-home");
- const homeUtilityBars = new Set(home
- ? home.querySelectorAll('[class*="_homeUtilityBar_"]')
- : []);
- for (const candidate of document.querySelectorAll(".dream-skin-home-utility")) {
- if (!homeUtilityBars.has(candidate)) candidate.classList.remove("dream-skin-home-utility");
- }
- for (const candidate of homeUtilityBars) candidate.classList.add("dream-skin-home-utility");
-
- if (!shellMain || !document.body) return;
- if (observedShellMain !== shellMain) {
- resizeObserver?.disconnect();
- resizeObserver?.observe(shellMain);
- observedShellMain = shellMain;
- layout = true;
- }
- shellMain.classList.toggle("dream-skin-home-shell", Boolean(home));
- let chrome = document.getElementById(CHROME_ID);
- let created = false;
- if (!chrome || chrome.parentElement !== document.body) {
- chrome?.remove();
- chrome = document.createElement("div");
- chrome.id = CHROME_ID;
- chrome.setAttribute("aria-hidden", "true");
- chrome.innerHTML = `
-
- ◉
-
-
-
-
-
- `;
- document.body.appendChild(chrome);
- created = true;
- chromeParts = null;
- }
- if (!chromeParts || chromeParts.chrome !== chrome) {
- chromeParts = {
- chrome,
- name: chrome.querySelector(".dream-skin-brand b"),
- subtitle: chrome.querySelector(".dream-skin-brand small"),
- status: chrome.querySelector(".dream-skin-status span"),
- quote: chrome.querySelector(".dream-skin-quote"),
- };
+ const selectorHit = (key) => {
+ const selector = selectorByKey.get(key)?.selector;
+ if (!selector) return false;
+ try { return Boolean(document.querySelector(selector)); } catch { return false; }
+ };
+
+ const stableTestidHit = (testid) => {
+ const selector = stableTestidSelector(testid);
+ if (!selector) return false;
+ try { return Boolean(document.querySelector(selector)); } catch { return false; }
+ };
+
+ const partNodes = new Set();
+ const queryAll = (selector) => {
+ if (!selector) return [];
+ try { return [...document.querySelectorAll(selector)]; } catch { return []; }
+ };
+ const selectorNodes = (key) => queryAll(selectorByKey.get(key)?.selector);
+ const addPart = (desired, part, nodes) => {
+ for (const node of nodes) {
+ if (node && typeof node.setAttribute === "function" && !desired.has(node)) {
+ desired.set(node, part);
+ }
}
- setTextContent(chromeParts.name, THEME.name || "Codex Dream Skin");
- setTextContent(chromeParts.subtitle, THEME.brandSubtitle || "CODEX DREAM SKIN");
- setTextContent(chromeParts.status, THEME.statusText || "DREAM SKIN ONLINE");
- setTextContent(chromeParts.quote, THEME.quote || "MAKE SOMETHING WONDERFUL");
- if (layout || created) {
- metrics.layoutReads += 1;
- const shellBox = shellMain.getBoundingClientRect();
- setStyleProperty(chrome, "left", `${Math.round(shellBox.left)}px`);
- setStyleProperty(chrome, "top", `${Math.round(shellBox.top)}px`);
- setStyleProperty(chrome, "width", `${Math.round(shellBox.width)}px`);
- setStyleProperty(chrome, "height", `${Math.round(shellBox.height)}px`);
+ };
+ const refreshParts = () => {
+ metrics.partPasses += 1;
+ const desired = new Map();
+ addPart(desired, "root", [document.documentElement]);
+ addPart(desired, "sidebar", selectorNodes("left-panel"));
+ addPart(desired, "main", selectorNodes("shell-main"));
+ addPart(desired, "header", selectorNodes("header-tint"));
+ addPart(desired, "home", selectorNodes("home-route"));
+ addPart(desired, "project-list", selectorNodes("project-selector"));
+ addPart(desired, "thread", selectorNodes("thread-surface"));
+ addPart(desired, "message", selectorNodes("message"));
+ addPart(desired, "composer", selectorNodes("composer-chrome"));
+ addPart(desired, "composer-toolbar", selectorNodes("composer-toolbar"));
+ addPart(desired, "dialog", selectorNodes("overlay-dialog"));
+ const homeHero = selectorNodes("home-icon")[0]?.parentElement;
+ addPart(desired, "home-hero", homeHero ? [homeHero] : []);
+
+ for (const node of partNodes) {
+ if (!desired.has(node)) {
+ node.removeAttribute?.(PART_ATTR);
+ metrics.partWrites += 1;
+ }
}
- chrome.classList.toggle("dream-skin-home-shell", Boolean(home));
- if (chrome.dataset.dreamShell !== shell) {
- chrome.dataset.dreamShell = shell;
- metrics.attributeWrites += 1;
+ partNodes.clear();
+ for (const [node, part] of desired) {
+ if (node.getAttribute?.(PART_ATTR) !== part) {
+ node.setAttribute(PART_ATTR, part);
+ metrics.partWrites += 1;
+ }
+ partNodes.add(node);
}
};
- const ensure = ({ root: rootPass = true, route = true, layout = true } = {}) => {
+ const removeParts = () => {
+ for (const node of partNodes) node.removeAttribute?.(PART_ATTR);
+ partNodes.clear();
+ for (const node of queryAll(`[${PART_ATTR}]`)) node.removeAttribute?.(PART_ATTR);
+ };
+
+ const scopeMatches = (scope, baseState, overlay) => {
+ const active = new Set([baseState]);
+ if (baseState !== "settings") active.add("all");
+ if (overlay) active.add("overlay");
+ const tokens = String(scope || "all").toLowerCase().match(/[a-z]+/g) || ["all"];
+ return tokens.some((token) => token !== "config" && active.has(token));
+ };
+
+ const detectScope = () => {
+ const overlay = selectorHit("overlay-menu") || selectorHit("overlay-dialog") ||
+ selectorHit("overlay-popper");
+ let baseState = "thread";
+ if (selectorHit("appearance-radio") || stableTestidHit("theme-preview")) baseState = "settings";
+ else if (selectorHit("home-icon") || selectorHit("home-route")) baseState = "home";
+ else if (!selectorHit("shell-main")) baseState = "settings";
+ const missingL1 = SELECTOR_CONTRACT.selectors
+ .filter((entry) => entry.tier === "L1" && entry.required &&
+ scopeMatches(entry.scope, baseState, overlay) && !selectorHit(entry.key))
+ .map((entry) => entry.key);
+ return {
+ state: overlay ? "overlay" : baseState,
+ baseState,
+ overlay,
+ // Settings replaces (or partially replaces) the app shell on macOS and
+ // can retain a shell on Windows. It is therefore always an L0 scope;
+ // never treat the absence of the home/thread L1 anchors as a failure.
+ level: baseState === "settings" || missingL1.length ? "L0" : "L1",
+ missingL1,
+ };
+ };
+
+ const refreshScope = () => {
+ metrics.routePasses += 1;
+ const scope = detectScope();
+ const state = window[STATE_KEY];
+ if (state?.installToken === installToken) state.scope = scope;
+ return scope;
+ };
+
+ const ensure = ({ root: rootPass = true, scope: scopePass = false, parts: partPass = false } = {}) => {
if (window[DISABLED_KEY]) return;
const root = document.documentElement;
if (!root) return;
metrics.ensureCalls += 1;
- const shell = rootPass ? applyRootState(root) : null;
- if (route) syncRouteState(shell, { layout });
+ if (rootPass) applyRootState(root);
+ if (partPass) refreshParts();
+ if (scopePass) refreshScope();
};
const cleanup = () => {
const state = window[STATE_KEY];
if (state?.installToken !== installToken) return false;
window[DISABLED_KEY] = true;
- document.documentElement?.classList.remove("codex-dream-skin");
- document.documentElement?.removeAttribute(SHELL_ATTR);
- for (const name of ART_ATTRS) document.documentElement?.removeAttribute(name);
- document.documentElement?.style.removeProperty("--dream-skin-art");
- for (const name of THEME_VARIABLES) document.documentElement?.style.removeProperty(name);
- document.querySelectorAll(".dream-skin-home").forEach((node) => node.classList.remove("dream-skin-home"));
- document.querySelectorAll(".dream-skin-home-shell").forEach((node) => node.classList.remove("dream-skin-home-shell"));
- document.querySelectorAll(".dream-skin-home-utility").forEach((node) => node.classList.remove("dream-skin-home-utility"));
- document.getElementById(STYLE_ID)?.remove();
- document.getElementById(CHROME_ID)?.remove();
- state?.observer?.disconnect();
+ const root = document.documentElement;
+ for (const name of ROOT_ATTRS) root?.removeAttribute(name);
+ for (const attribute of [...(root?.attributes || [])]) {
+ if (attribute.name.startsWith("data-dream-")) root.removeAttribute(attribute.name);
+ }
+ for (const name of THEME_VARIABLES) root?.style.removeProperty(name);
+ for (const property of [...(root?.style || [])]) {
+ if (property.startsWith("--dream-") || property.startsWith("--ds-")) {
+ root.style.removeProperty(property);
+ }
+ }
+ removeParts();
state?.rootObserver?.disconnect();
- state?.resizeObserver?.disconnect();
+ state?.partObserver?.disconnect();
+ if (bodyReadyHandler && typeof document.removeEventListener === "function") {
+ document.removeEventListener("DOMContentLoaded", bodyReadyHandler);
+ }
if (state?.timer) clearInterval(state.timer);
if (state?.scheduler?.timeout) clearTimeout(state.scheduler.timeout);
- if (state?.scheduler?.frame != null && typeof cancelAnimationFrame === "function") {
- cancelAnimationFrame(state.scheduler.frame);
- }
if (analysisTimer) clearTimeout(analysisTimer);
- if (state?.resizeHandler) window.removeEventListener("resize", state.resizeHandler);
if (state?.mediaHandler && state?.mediaQuery) {
try { state.mediaQuery.removeEventListener("change", state.mediaHandler); } catch {}
}
+ if (state?.navigationHandler && state?.navigation) {
+ try { state.navigation.removeEventListener("navigate", state.navigationHandler); } catch {}
+ }
+ if (styleSheet) {
+ try {
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets]
+ .filter((candidate) => candidate !== styleSheet);
+ } catch {}
+ styleRegistry.delete(styleSheet);
+ }
+ styleNode?.remove();
+ if (document.getElementById(STYLE_ID) === styleNode) document.getElementById(STYLE_ID)?.remove();
+ if (styleRegistry.size === 0) delete window[STYLE_REGISTRY_KEY];
if (state?.artUrl) URL.revokeObjectURL(state.artUrl);
delete window[STATE_KEY];
return true;
};
- const scheduler = { timeout: null, frame: null, root: false, route: false, layout: false };
+ const scheduler = { timeout: null, root: false, scope: false, parts: false };
const flushScheduledEnsure = () => {
- if (scheduler.frame !== null && typeof cancelAnimationFrame === "function") {
- cancelAnimationFrame(scheduler.frame);
- }
if (scheduler.timeout) clearTimeout(scheduler.timeout);
- scheduler.frame = null;
scheduler.timeout = null;
- const pending = { root: scheduler.root, route: scheduler.route, layout: scheduler.layout };
+ const pending = { root: scheduler.root, scope: scheduler.scope, parts: scheduler.parts };
scheduler.root = false;
- scheduler.route = false;
- scheduler.layout = false;
+ scheduler.scope = false;
+ scheduler.parts = false;
ensure(pending);
};
- const scheduleEnsure = ({ root = false, route = true, layout = false } = {}) => {
+ const scheduleEnsure = ({ root = false, scope = false, parts = false } = {}, delay = 64) => {
scheduler.root ||= root;
- scheduler.route ||= route;
- scheduler.layout ||= layout;
- if (scheduler.timeout || scheduler.frame !== null) return;
- if (typeof requestAnimationFrame === "function") {
- scheduler.frame = requestAnimationFrame(flushScheduledEnsure);
- scheduler.timeout = setTimeout(flushScheduledEnsure, 96);
- } else {
- scheduler.timeout = setTimeout(flushScheduledEnsure, 64);
- }
+ scheduler.scope ||= scope;
+ scheduler.parts ||= parts;
+ if (scheduler.timeout) return;
+ scheduler.timeout = setTimeout(flushScheduledEnsure, delay);
};
- const observer = new MutationObserver(() => scheduleEnsure({ route: true }));
- rootObserver = new MutationObserver(() => {
- if (samplingNativeShell) return;
- scheduleEnsure({ root: true, route: true });
- });
- const resizeHandler = () => scheduleEnsure({ route: true, layout: true });
- if (typeof ResizeObserver === "function") {
- resizeObserver = new ResizeObserver(() => scheduleEnsure({ route: true, layout: true }));
+ if (typeof MutationObserver === "function") {
+ rootObserver = new MutationObserver(() => scheduleEnsure({ root: true }));
+ partObserver = new MutationObserver(() => scheduleEnsure({ parts: true }, 80));
}
let mediaQuery = null;
let mediaHandler = null;
try {
mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
- mediaHandler = () => scheduleEnsure({ root: true, route: true });
+ mediaHandler = () => scheduleEnsure({ root: true });
} catch {}
+ const navigationApi = window.navigation && typeof window.navigation.addEventListener === "function"
+ ? window.navigation : null;
+ const navigationHandler = navigationApi ? () => {
+ metrics.navigationEvents += 1;
+ scheduleEnsure({ scope: true, parts: true }, 180);
+ } : null;
+
window[STATE_KEY] = {
ensure,
cleanup,
- observer,
rootObserver,
- resizeObserver,
+ partObserver,
timer: null,
scheduler,
- resizeHandler,
mediaQuery,
mediaHandler,
+ navigation: navigationApi,
+ navigationHandler,
artUrl,
installToken,
+ styleMode,
+ styleNode,
+ styleSheet,
+ styleRevision: STYLE_REVISION,
analysis: artAnalysis,
artMetadata: ART_METADATA,
+ scope: null,
+ selectorsSchema: SELECTOR_CONTRACT.schema,
metrics,
version: VERSION,
themeId: THEME.id || "custom",
revision: PAYLOAD_REVISION,
- detectShellMode,
+ detectShellAppearance,
};
const firstEnsureStartedAt = now();
- ensure({ layout: !previous || !document.getElementById(CHROME_ID) });
+ ensure({ root: true, parts: true });
+ const initialScope = refreshScope();
metrics.firstEnsureMs = Number((now() - firstEnsureStartedAt).toFixed(3));
- if (previous?.artUrl && previous.artUrl !== artUrl) URL.revokeObjectURL(previous.artUrl);
- observer.observe(document.documentElement, {
- childList: true,
- subtree: true,
- });
- rootObserver.observe(document.documentElement, {
- attributes: true,
- attributeFilter: ["class", "data-theme", "data-appearance", "data-color-mode"],
- });
- if (document.body) {
- rootObserver.observe(document.body, {
+ const observeAttributes = (node) => {
+ if (!rootObserver || !node) return;
+ rootObserver.observe(node, {
attributes: true,
attributeFilter: ["class", "data-theme", "data-appearance", "data-color-mode"],
});
+ };
+ const observePartTree = (node) => {
+ if (!partObserver || !node) return;
+ partObserver.observe(node, { childList: true, subtree: true });
+ };
+ observeAttributes(document.documentElement);
+ const observeBody = () => {
+ observeAttributes(document.body);
+ observePartTree(document.body);
+ };
+ if (document.body) observeBody();
+ else if (typeof document.addEventListener === "function") {
+ bodyReadyHandler = () => {
+ if (!window[DISABLED_KEY]) {
+ observeBody();
+ scheduleEnsure({ parts: true }, 0);
+ }
+ };
+ document.addEventListener("DOMContentLoaded", bodyReadyHandler, { once: true });
}
- const timer = setInterval(() => ensure(), 4000);
+ const timer = setInterval(() => {
+ metrics.safetyPasses += 1;
+ ensure({ root: true });
+ }, 30000);
window[STATE_KEY].timer = timer;
- window.addEventListener("resize", resizeHandler, { passive: true });
- if (mediaHandler && mediaQuery) {
+ if (mediaHandler && mediaQuery && typeof mediaQuery.addEventListener === "function") {
mediaQuery.addEventListener("change", mediaHandler);
}
+ if (navigationHandler && navigationApi) {
+ navigationApi.addEventListener("navigate", navigationHandler);
+ }
const analysisPromise = artAnalysis ? Promise.resolve(null) : analyzeArt();
window[STATE_KEY].analysisTimer = analysisTimer;
analysisPromise.then((analysis) => {
@@ -776,7 +830,7 @@
analysisCache.set(THEME.artKey, analysis);
while (analysisCache.size > 8) analysisCache.delete(analysisCache.keys().next().value);
}
- ensure({ root: true, route: false, layout: false });
+ ensure({ root: true });
}).catch(() => {});
return {
installed: true,
@@ -784,6 +838,8 @@
themeId: THEME.id || "custom",
revision: PAYLOAD_REVISION,
shell: resolvedShell(),
+ scope: initialScope,
+ styleMode,
analysis: artAnalysis,
};
})(__DREAM_SKIN_CSS_JSON__, __DREAM_SKIN_ART_JSON__, __DREAM_SKIN_THEME_JSON__)
diff --git a/assets/inject/upstream/dream-skin/windows/dream-skin.css b/assets/inject/upstream/dream-skin/windows/dream-skin.css
index 7e8d061bb..d35fe2ac0 100644
--- a/assets/inject/upstream/dream-skin/windows/dream-skin.css
+++ b/assets/inject/upstream/dream-skin/windows/dream-skin.css
@@ -1,662 +1,1216 @@
-:root.codex-dream-skin {
- color-scheme: light;
- --dream-accent: oklch(0.66 0.15 18);
- --dream-accent-ink: oklch(0.98 0.006 20);
- --dream-art-position: 72% 45%;
- --dream-canvas: color-mix(in oklab, oklch(0.975 0.006 35) 96%, var(--dream-accent));
- --dream-surface: color-mix(in oklab, oklch(0.988 0.004 35) 97%, var(--dream-accent));
- --dream-surface-raised: color-mix(in oklab, oklch(0.995 0.003 35) 98%, var(--dream-accent));
- --dream-sidebar: color-mix(in oklab, oklch(0.965 0.006 35) 94%, var(--dream-accent));
- --dream-text: oklch(0.25 0.018 30);
- --dream-text-muted: oklch(0.48 0.018 30);
- --dream-line: color-mix(in oklab, oklch(0.75 0.012 30) 80%, var(--dream-accent));
- --dream-line-soft: color-mix(in oklab, transparent 76%, var(--dream-accent));
- --dream-accent-soft: color-mix(in oklab, var(--dream-accent) 14%, var(--dream-surface));
- --dream-accent-hover: color-mix(in oklab, var(--dream-accent) 20%, var(--dream-surface));
- --dream-hero-shade: color-mix(in oklab, var(--dream-surface) 93%, transparent);
- --dream-ambient-opacity: .18;
- --dream-shadow: 0 16px 42px color-mix(in oklab, var(--dream-accent) 9%, transparent);
- --dream-immersive-edge: color-mix(in oklab, var(--dream-surface) 46%, transparent);
- --dream-immersive-mid: color-mix(in oklab, var(--dream-surface) 28%, transparent);
- --dream-immersive-far: color-mix(in oklab, var(--dream-surface) 14%, transparent);
- --dream-immersive-sidebar: color-mix(in oklab, var(--dream-sidebar) 50%, transparent);
- --dream-task-immersive-sidebar: color-mix(in oklab, var(--dream-sidebar) 72%, transparent);
- --dream-immersive-composer: color-mix(in oklab, var(--dream-surface-raised) 76%, var(--dream-accent) 4%);
- --dream-immersive-line: color-mix(in oklab, var(--dream-line) 76%, transparent);
- --dream-task-immersive-edge: color-mix(in oklab, var(--dream-surface) 86%, transparent);
- --dream-task-immersive-mid: color-mix(in oklab, var(--dream-surface) 78%, transparent);
- --dream-task-immersive-far: color-mix(in oklab, var(--dream-surface) 66%, transparent);
-}
-
-:root.codex-dream-skin.dream-theme-dark {
- color-scheme: dark;
- --dream-canvas: color-mix(in oklab, oklch(0.16 0.012 245) 96%, var(--dream-accent));
- --dream-surface: color-mix(in oklab, oklch(0.19 0.012 245) 95%, var(--dream-accent));
- --dream-surface-raised: color-mix(in oklab, oklch(0.23 0.012 245) 95%, var(--dream-accent));
- --dream-sidebar: color-mix(in oklab, oklch(0.145 0.014 245) 94%, var(--dream-accent));
- --dream-text: oklch(0.93 0.008 245);
- --dream-text-muted: oklch(0.7 0.012 245);
- --dream-line: color-mix(in oklab, oklch(0.42 0.018 245) 76%, var(--dream-accent));
- --dream-line-soft: color-mix(in oklab, transparent 78%, var(--dream-accent));
- --dream-accent-soft: color-mix(in oklab, var(--dream-accent) 18%, var(--dream-surface));
- --dream-accent-hover: color-mix(in oklab, var(--dream-accent) 27%, var(--dream-surface));
- --dream-hero-shade: color-mix(in oklab, var(--dream-surface) 91%, transparent);
- --dream-ambient-opacity: .22;
- --dream-shadow: 0 18px 48px color-mix(in oklab, oklch(0.06 0.01 245) 72%, transparent);
- --dream-immersive-edge: color-mix(in oklab, var(--dream-surface) 43%, transparent);
- --dream-immersive-mid: color-mix(in oklab, var(--dream-surface) 27%, transparent);
- --dream-immersive-far: color-mix(in oklab, var(--dream-surface) 16%, transparent);
- --dream-immersive-sidebar: color-mix(in oklab, var(--dream-sidebar) 48%, transparent);
- --dream-task-immersive-sidebar: color-mix(in oklab, var(--dream-sidebar) 70%, transparent);
- --dream-immersive-composer: color-mix(in oklab, var(--dream-surface-raised) 88%, var(--dream-accent) 5%);
- --dream-immersive-line: color-mix(in oklab, var(--dream-line) 82%, transparent);
- --dream-task-immersive-edge: color-mix(in oklab, var(--dream-surface) 82%, transparent);
- --dream-task-immersive-mid: color-mix(in oklab, var(--dream-surface) 74%, transparent);
- --dream-task-immersive-far: color-mix(in oklab, var(--dream-surface) 60%, transparent);
-}
-
-html.codex-dream-skin body {
- background: var(--dream-canvas) !important;
- color: var(--dream-text) !important;
- font-family: "Segoe UI Variable Text", "Segoe UI", "Microsoft YaHei UI", system-ui, sans-serif !important;
-}
-
-html.codex-dream-skin aside.app-shell-left-panel {
- color: var(--dream-text) !important;
- background: var(--dream-sidebar) !important;
- border-color: var(--dream-line-soft) !important;
- box-shadow: inset -1px 0 var(--dream-line-soft) !important;
+/* Canonical cross-platform skin. Run tools/sync-runtime-assets.mjs after editing. */
+:root[data-dream-skin="active"] {
+ color-scheme: dark !important;
+ --ds-bg: #111318;
+ --ds-panel: #191c22;
+ --ds-panel-2: #20242b;
+ --ds-green: #8298a3;
+ --ds-lime: #a0adb3;
+ --ds-cyan: #8da397;
+ --ds-purple: #9d94a3;
+ --ds-text: #edf0f1;
+ --ds-muted: #a3aaae;
+ --ds-line: rgba(130, 152, 163, .24);
+ --ds-bg-rgb: 17 19 24;
+ --ds-panel-rgb: 25 28 34;
+ --ds-panel-2-rgb: 32 36 43;
+ --ds-text-rgb: 237 240 241;
+ --ds-muted-rgb: 163 170 174;
+ --ds-accent-rgb: 130 152 163;
+ --ds-secondary-rgb: 141 163 151;
+ --ds-highlight-rgb: var(--ds-secondary-rgb);
+ --ds-accent: var(--ds-green);
+ --ds-accent-soft: var(--ds-lime);
+ --ds-secondary: var(--ds-cyan);
+ --ds-highlight: var(--ds-purple);
+ --ds-on-accent: rgb(var(--ds-bg-rgb) / 1);
+ --ds-focus-x: var(--dream-skin-focus-x, var(--dream-art-focus-x, 50%));
+ --ds-focus-y: var(--dream-skin-focus-y, var(--dream-art-focus-y, 50%));
+ --ds-art-position: var(--dream-art-position, var(--dream-skin-art-position, var(--ds-focus-x) var(--ds-focus-y)));
+ --ds-safe-side: left;
+ --ds-hero-scrim: linear-gradient(90deg,
+ rgb(var(--ds-bg-rgb) / .90) 0%,
+ rgb(var(--ds-bg-rgb) / .76) 50%,
+ rgb(var(--ds-bg-rgb) / .18) 84%,
+ transparent 100%);
+ --ds-task-shade: linear-gradient(90deg,
+ rgb(var(--ds-bg-rgb) / .56) 0%,
+ rgb(var(--ds-bg-rgb) / .36) 48%,
+ rgb(var(--ds-bg-rgb) / .12) 100%);
+ --ds-task-fade: linear-gradient(180deg,
+ rgb(var(--ds-bg-rgb) / .10) 0%,
+ rgb(var(--ds-bg-rgb) / .18) 32%,
+ rgb(var(--ds-bg-rgb) / .76) 68%,
+ rgb(var(--ds-bg-rgb) / 1) 100%);
+ --ds-task-full-veil: linear-gradient(
+ rgb(var(--ds-bg-rgb) / .10),
+ rgb(var(--ds-bg-rgb) / .10));
+ --ds-immersive-edge: rgb(var(--ds-bg-rgb) / .40);
+ --ds-immersive-mid: rgb(var(--ds-bg-rgb) / .26);
+ --ds-immersive-far: rgb(var(--ds-bg-rgb) / .16);
+ --ds-immersive-sidebar: rgb(var(--ds-panel-rgb) / .46);
+ --ds-task-immersive-sidebar: rgb(var(--ds-panel-rgb) / .70);
+ --ds-immersive-chrome: rgb(var(--ds-panel-rgb) / .28);
+ --ds-immersive-composer: rgb(var(--ds-panel-rgb) / .44);
+ --ds-immersive-composer-solid: color-mix(in srgb,
+ rgb(var(--ds-panel-2-rgb)) 88%,
+ rgb(var(--ds-muted-rgb)) 12%);
+ --ds-immersive-line: rgb(var(--ds-muted-rgb) / .42);
+ --ds-task-immersive-edge: rgb(var(--ds-bg-rgb) / .82);
+ --ds-task-immersive-mid: rgb(var(--ds-bg-rgb) / .74);
+ --ds-task-immersive-far: rgb(var(--ds-bg-rgb) / .60);
+}
+
+html[data-dream-skin="active"][data-dream-shell="light"] {
+ color-scheme: light !important;
+ --ds-bg: #f3f5f6;
+ --ds-panel: #fafbfb;
+ --ds-panel-2: #e9edef;
+ --ds-text: #22272a;
+ --ds-muted: #687176;
+ --ds-line: rgba(91, 111, 121, .18);
+ --ds-bg-rgb: 243 245 246;
+ --ds-panel-rgb: 250 251 251;
+ --ds-panel-2-rgb: 233 237 239;
+ --ds-text-rgb: 34 39 42;
+ --ds-muted-rgb: 104 113 118;
+ --ds-accent-rgb: 84 112 126;
+ --ds-secondary-rgb: 102 128 116;
+ --ds-highlight-rgb: var(--ds-secondary-rgb);
+ --ds-on-accent: rgb(var(--ds-panel-rgb) / 1);
+ --ds-hero-scrim: linear-gradient(90deg,
+ rgb(var(--ds-panel-rgb) / .96) 0%,
+ rgb(var(--ds-panel-rgb) / .82) 50%,
+ rgb(var(--ds-panel-rgb) / .20) 84%,
+ transparent 100%);
+ --ds-task-shade: linear-gradient(90deg,
+ rgb(var(--ds-bg-rgb) / .68) 0%,
+ rgb(var(--ds-bg-rgb) / .40) 48%,
+ rgb(var(--ds-bg-rgb) / .12) 100%);
+ --ds-task-fade: linear-gradient(180deg,
+ rgb(var(--ds-bg-rgb) / .08) 0%,
+ rgb(var(--ds-bg-rgb) / .22) 34%,
+ rgb(var(--ds-bg-rgb) / .78) 70%,
+ rgb(var(--ds-bg-rgb) / 1) 100%);
+ --ds-task-full-veil: linear-gradient(
+ rgb(var(--ds-panel-rgb) / .10),
+ rgb(var(--ds-panel-rgb) / .10));
+ --ds-immersive-edge: rgb(var(--ds-panel-rgb) / .44);
+ --ds-immersive-mid: rgb(var(--ds-panel-rgb) / .28);
+ --ds-immersive-far: rgb(var(--ds-panel-rgb) / .14);
+ --ds-immersive-sidebar: rgb(var(--ds-panel-rgb) / .48);
+ --ds-task-immersive-sidebar: rgb(var(--ds-panel-rgb) / .72);
+ --ds-immersive-chrome: rgb(var(--ds-panel-rgb) / .34);
+ --ds-immersive-composer: rgb(var(--ds-panel-rgb) / .46);
+ --ds-immersive-composer-solid: rgb(var(--ds-panel-rgb) / .74);
+ --ds-immersive-line: rgb(var(--ds-accent-rgb) / .24);
+ --ds-task-immersive-edge: rgb(var(--ds-panel-rgb) / .86);
+ --ds-task-immersive-mid: rgb(var(--ds-panel-rgb) / .78);
+ --ds-task-immersive-far: rgb(var(--ds-panel-rgb) / .66);
+}
+
+/* Native token surfaces (dropdown/popover) resolve from Codex's own
+ appearanceTheme, not the skin; when that disagrees with data-dream-shell the
+ popover keeps the opposite palette (#233). Remap the dropdown token family to
+ skin variables so those surfaces inherit the theme in both shells. */
+html[data-dream-skin="active"] {
+ --color-token-dropdown-background: var(--ds-panel);
+}
+
+html[data-dream-skin="active"] [class~="bg-token-dropdown-background"] {
+ color: var(--ds-text);
+ --color-token-foreground: var(--ds-text);
+ --color-token-text-secondary: var(--ds-muted);
+ --color-token-muted-foreground: var(--ds-muted);
+ --color-token-description-foreground: var(--ds-muted);
+ --color-token-list-hover-background: rgb(var(--ds-panel-2-rgb) / .9);
+ --color-token-border: var(--ds-line);
+ --color-token-border-default: var(--ds-line);
+}
+
+html[data-dream-skin="active"]:is([data-dream-art-safe="left"], [data-dream-art-safe-area="left"]) {
+ --ds-safe-side: left;
+ --ds-art-position: 100% var(--ds-focus-y);
+}
+
+html[data-dream-skin="active"]:is([data-dream-art-safe="right"], [data-dream-art-safe-area="right"]) {
+ --ds-safe-side: right;
+ --ds-art-position: 0% var(--ds-focus-y);
+ --ds-hero-scrim: linear-gradient(270deg,
+ rgb(var(--ds-bg-rgb) / .90) 0%,
+ rgb(var(--ds-bg-rgb) / .76) 50%,
+ rgb(var(--ds-bg-rgb) / .18) 84%,
+ transparent 100%);
+ --ds-task-shade: linear-gradient(270deg,
+ rgb(var(--ds-bg-rgb) / .56) 0%,
+ rgb(var(--ds-bg-rgb) / .36) 48%,
+ rgb(var(--ds-bg-rgb) / .12) 100%);
+}
+
+html[data-dream-skin="active"][data-dream-shell="light"]:is([data-dream-art-safe="right"], [data-dream-art-safe-area="right"]) {
+ --ds-hero-scrim: linear-gradient(270deg,
+ rgb(var(--ds-panel-rgb) / .96) 0%,
+ rgb(var(--ds-panel-rgb) / .82) 50%,
+ rgb(var(--ds-panel-rgb) / .20) 84%,
+ transparent 100%);
+ --ds-task-shade: linear-gradient(270deg,
+ rgb(var(--ds-bg-rgb) / .68) 0%,
+ rgb(var(--ds-bg-rgb) / .40) 48%,
+ rgb(var(--ds-bg-rgb) / .12) 100%);
+}
+
+html[data-dream-skin="active"]:is([data-dream-art-safe="center"], [data-dream-art-safe-area="center"]) {
+ --ds-safe-side: center;
+ --ds-hero-scrim: linear-gradient(90deg,
+ transparent 0%,
+ rgb(var(--ds-bg-rgb) / .68) 25%,
+ rgb(var(--ds-bg-rgb) / .78) 50%,
+ rgb(var(--ds-bg-rgb) / .68) 75%,
+ transparent 100%);
+ --ds-task-shade: radial-gradient(ellipse at center,
+ rgb(var(--ds-bg-rgb) / .48) 0%,
+ rgb(var(--ds-bg-rgb) / .30) 52%,
+ rgb(var(--ds-bg-rgb) / .10) 100%);
+}
+
+html[data-dream-skin="active"][data-dream-shell="light"]:is([data-dream-art-safe="center"], [data-dream-art-safe-area="center"]) {
+ --ds-hero-scrim: linear-gradient(90deg,
+ transparent 0%,
+ rgb(var(--ds-panel-rgb) / .72) 25%,
+ rgb(var(--ds-panel-rgb) / .88) 50%,
+ rgb(var(--ds-panel-rgb) / .72) 75%,
+ transparent 100%);
+ --ds-task-shade: radial-gradient(ellipse at center,
+ rgb(var(--ds-bg-rgb) / .58) 0%,
+ rgb(var(--ds-bg-rgb) / .34) 52%,
+ rgb(var(--ds-bg-rgb) / .10) 100%);
+}
+
+html[data-dream-skin="active"]:is([data-dream-art-safe="none"], [data-dream-art-safe-area="none"]) {
+ --ds-safe-side: none;
+ --ds-hero-scrim: linear-gradient(180deg,
+ rgb(var(--ds-bg-rgb) / .34),
+ rgb(var(--ds-bg-rgb) / .20));
+ --ds-task-shade: linear-gradient(180deg,
+ rgb(var(--ds-bg-rgb) / .28),
+ rgb(var(--ds-bg-rgb) / .14));
+}
+
+html[data-dream-skin="active"][data-dream-shell="light"]:is([data-dream-art-safe="none"], [data-dream-art-safe-area="none"]) {
+ --ds-hero-scrim: linear-gradient(180deg,
+ rgb(var(--ds-panel-rgb) / .46),
+ rgb(var(--ds-panel-rgb) / .28));
+ --ds-task-shade: linear-gradient(180deg,
+ rgb(var(--ds-bg-rgb) / .34),
+ rgb(var(--ds-bg-rgb) / .16));
+}
+
+html[data-dream-skin="active"] body {
+ background: var(--ds-bg) !important;
+ color: var(--ds-text) !important;
+ font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "PingFang SC", "Microsoft YaHei UI", "Segoe UI", system-ui, sans-serif !important;
+}
+
+html[data-dream-skin="active"] body::before {
+ content: none;
+}
+
+html[data-dream-skin="active"] aside.app-shell-left-panel {
+ background:
+ linear-gradient(180deg,
+ rgb(var(--ds-panel-rgb) / .98),
+ rgb(var(--ds-bg-rgb) / .96)) !important;
+ border: 1px solid var(--ds-line) !important;
+ border-left: 0 !important;
+ border-radius: 0 16px 16px 0 !important;
+ box-shadow: 10px 0 30px rgb(var(--ds-bg-rgb) / .22) !important;
+ color: var(--ds-text) !important;
backdrop-filter: none !important;
}
-html.codex-dream-skin aside.app-shell-left-panel nav {
- background: transparent !important;
-}
+html[data-dream-skin="active"] aside.app-shell-left-panel nav { background: transparent !important; }
-html.codex-dream-skin aside.app-shell-left-panel button {
- color: var(--dream-text) !important;
- transition: background-color 180ms cubic-bezier(.22, 1, .36, 1), color 180ms cubic-bezier(.22, 1, .36, 1) !important;
+html[data-dream-skin="active"] aside.app-shell-left-panel button,
+html[data-dream-skin="active"] aside.app-shell-left-panel a {
+ color: var(--ds-text) !important;
+ transition: background-color .18s cubic-bezier(.22, 1, .36, 1),
+ border-color .18s cubic-bezier(.22, 1, .36, 1),
+ color .18s cubic-bezier(.22, 1, .36, 1) !important;
}
-html.codex-dream-skin aside.app-shell-left-panel button:hover {
- background: var(--dream-accent-soft) !important;
+html[data-dream-skin="active"] aside.app-shell-left-panel [class*="text-token-foreground"] {
+ color: var(--ds-text) !important;
}
-html.codex-dream-skin aside.app-shell-left-panel [class~="bg-token-list-hover-background"],
-html.codex-dream-skin aside.app-shell-left-panel [aria-current="page"] {
- color: var(--dream-text) !important;
- background: var(--dream-accent-hover) !important;
- box-shadow: inset 0 0 0 1px var(--dream-line-soft) !important;
+html[data-dream-skin="active"] aside.app-shell-left-panel [class*="text-token-input-placeholder-foreground"] {
+ color: rgb(var(--ds-muted-rgb) / .92) !important;
}
-html.codex-dream-skin aside.app-shell-left-panel svg,
-html.codex-dream-skin aside.app-shell-left-panel [class*="text-token"] {
- color: currentColor;
+html[data-dream-skin="active"] aside.app-shell-left-panel svg {
+ color: rgb(var(--ds-muted-rgb) / .96) !important;
}
-html.codex-dream-skin main.main-surface {
- position: relative;
- isolation: isolate;
- overflow: hidden !important;
- color: var(--dream-text) !important;
- background: var(--dream-surface) !important;
- border-color: var(--dream-line-soft) !important;
- box-shadow: inset 0 1px color-mix(in oklab, var(--dream-surface-raised) 72%, transparent) !important;
+html[data-dream-skin="active"] aside.app-shell-left-panel button:hover,
+html[data-dream-skin="active"] aside.app-shell-left-panel a:hover {
+ background: rgb(var(--ds-accent-rgb) / .09) !important;
+ color: var(--ds-text) !important;
+ transform: none !important;
}
-html.codex-dream-skin main.main-surface > header.app-header-tint {
- /* Preserve Codex's native fixed header geometry and side-panel controls. */
- color: var(--dream-text) !important;
- background: color-mix(in oklab, var(--dream-surface) 94%, transparent) !important;
- border-color: var(--dream-line-soft) !important;
- backdrop-filter: blur(12px) saturate(1.05) !important;
+html[data-dream-skin="active"] aside.app-shell-left-panel button:hover svg,
+html[data-dream-skin="active"] aside.app-shell-left-panel a:hover svg {
+ color: var(--ds-accent) !important;
}
-/* Wide art also reaches the native application menu, which sits outside the
- main surface and needs its own dark-theme legibility layer. */
-html.codex-dream-skin.dream-theme-dark [class~="group/application-menu-top-bar"] {
- color: var(--dream-text) !important;
- background: color-mix(in oklab, var(--dream-sidebar) 90%, transparent) !important;
- border-bottom: 1px solid var(--dream-line-soft) !important;
- box-shadow: 0 1px 12px color-mix(in oklab, var(--dream-canvas) 72%, transparent) !important;
- backdrop-filter: blur(16px) saturate(.9) !important;
+html[data-dream-skin="active"] aside.app-shell-left-panel button[aria-label^="切换模式"] {
+ background: transparent !important;
+ border: 0 !important;
+ color: var(--ds-accent) !important;
+ font-family: inherit !important;
+ font-size: 19px !important;
+ font-weight: 800 !important;
+ text-shadow: none !important;
}
-html.codex-dream-skin.dream-theme-dark [class~="group/application-menu-top-bar"] button,
-html.codex-dream-skin.dream-theme-dark [class~="group/application-menu-top-bar"] svg {
- color: var(--dream-text) !important;
- text-shadow: 0 1px 2px color-mix(in oklab, var(--dream-canvas) 82%, transparent) !important;
+html[data-dream-skin="active"] aside.app-shell-left-panel button[aria-label^="切换模式"]::after {
+ content: " ·";
+ color: var(--ds-secondary);
}
-#codex-dream-skin-chrome {
- display: none !important;
- pointer-events: none !important;
+html[data-dream-skin="active"] aside.app-shell-left-panel [class~="bg-token-list-hover-background"],
+html[data-dream-skin="active"] aside.app-shell-left-panel [aria-current="page"] {
+ background: rgb(var(--ds-accent-rgb) / .12) !important;
+ border: 1px solid rgb(var(--ds-accent-rgb) / .22) !important;
+ box-shadow: 0 4px 16px rgb(var(--ds-bg-rgb) / .12) !important;
+ color: var(--ds-text) !important;
}
-html.codex-dream-skin [role="main"] {
- color: var(--dream-text);
+html[data-dream-skin="active"] aside.app-shell-left-panel [aria-current="page"] svg {
+ color: var(--ds-accent) !important;
}
-/* Task routes use the art as a quiet atmospheric layer. Wide banners are shown
- at their natural width instead of being enlarged and center-cropped. */
-html.codex-dream-skin .dream-task {
- position: relative;
+html[data-dream-skin="active"] main.main-surface {
+ position: relative !important;
isolation: isolate;
- min-height: 100%;
- background: var(--dream-surface) !important;
+ overflow: hidden !important;
+ background: var(--ds-bg) !important;
+ border: 1px solid var(--ds-line) !important;
+ border-right: 0 !important;
+ border-bottom: 0 !important;
+ border-radius: 16px 0 0 0 !important;
+ box-shadow: -8px 0 28px rgb(var(--ds-bg-rgb) / .18) !important;
}
-html.codex-dream-skin .dream-task::before {
+html[data-dream-skin="active"] main.main-surface:not(:has([role="main"]))::before {
content: "";
position: absolute;
- z-index: 0;
inset: 0;
+ z-index: 0;
pointer-events: none;
- opacity: var(--dream-ambient-opacity);
- background-image: var(--dream-art);
+ background-image: var(--ds-task-fade), var(--ds-task-shade), var(--dream-skin-art);
background-repeat: no-repeat;
- background-position: var(--dream-art-position);
- background-size: cover;
- mask-image: linear-gradient(to bottom, oklch(0 0 0) 0, oklch(0 0 0 / .92) 38%, transparent 88%);
+ background-position: center, center, var(--ds-art-position);
+ background-size: 100% 100%, 100% 100%, cover;
+ opacity: .78;
}
-html.codex-dream-skin.dream-art-wide .dream-task::before {
- background-position: center top;
- background-size: 100% auto;
+html[data-dream-skin="active"][data-dream-shell="light"] main.main-surface:not(:has([role="main"]))::before {
+ opacity: .72;
}
-html.codex-dream-skin.dream-art-wide:not(.dream-task-banner) .dream-task::before {
- background-position: var(--dream-art-position);
- background-size: cover;
- mask-image: linear-gradient(to bottom, oklch(0 0 0) 0, oklch(0 0 0 / .92) 44%, oklch(0 0 0 / .72) 100%);
+html[data-dream-skin="active"]:is([data-dream-art-wide="true"], [data-dream-art-aspect="wide"], [data-dream-art-aspect="ultrawide"])
+ main.main-surface:not(:has([role="main"]))::before {
+ background-position: center, center, var(--ds-art-position);
+ background-size: 100% 100%, 100% 100%, cover;
}
-html.codex-dream-skin.dream-task-banner .dream-task::before {
- bottom: auto;
- height: min(46vh, 520px);
- opacity: calc(var(--dream-ambient-opacity) * 1.8);
- mask-image: linear-gradient(to bottom, oklch(0 0 0) 0, oklch(0 0 0 / .9) 52%, transparent 100%);
+/* Studio's full mode keeps the task artwork at normal strength with only the
+ baseline readability veil. Ambient mode deliberately uses the heavier task
+ fade/shade below, while off removes the artwork entirely. */
+html[data-dream-skin="active"]:is([data-dream-task-mode="full"], [data-dream-art-task-mode="full"])
+ main.main-surface:not(:has([role="main"]))::before {
+ inset: 0;
+ height: auto;
+ background-image: var(--ds-task-full-veil), var(--dream-skin-art);
+ background-position: center, var(--ds-art-position);
+ background-size: 100% 100%, cover;
+ opacity: 1;
}
-html.codex-dream-skin.dream-task-off .dream-task::before {
- display: none;
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])
+ main.main-surface:not(:has([role="main"]))::before {
+ inset: 0;
+ height: auto;
+}
+
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]:has(main.main-surface):not(:has(main.main-surface [role="main"])) body {
+ background-image: var(--dream-skin-art) !important;
+ background-position: var(--ds-art-position) !important;
+ background-size: cover !important;
+ background-repeat: no-repeat !important;
+ background-attachment: fixed !important;
}
-html.codex-dream-skin .dream-task > * {
+html[data-dream-skin="active"]:is([data-dream-task-mode="full"], [data-dream-art-task-mode="full"])[data-dream-art-wide="true"]:has(main.main-surface):not(:has(main.main-surface [role="main"])) body {
+ background-image: var(--dream-skin-art) !important;
+ background-position: var(--ds-art-position) !important;
+ background-size: cover !important;
+ background-repeat: no-repeat !important;
+ background-attachment: fixed !important;
+}
+
+html[data-dream-skin="active"]:is([data-dream-task-mode="full"], [data-dream-art-task-mode="full"])[data-dream-art-wide="true"]:has(main.main-surface):not(:has(main.main-surface [role="main"]))
+ aside.app-shell-left-panel {
+ background: linear-gradient(90deg,
+ var(--ds-immersive-sidebar),
+ var(--ds-immersive-edge) 100%) !important;
+ border: 0 !important;
+ border-radius: 0 !important;
+ box-shadow: none !important;
+ backdrop-filter: none !important;
+}
+
+html[data-dream-skin="active"]:is([data-dream-task-mode="full"], [data-dream-art-task-mode="full"])[data-dream-art-wide="true"]:has(main.main-surface):not(:has(main.main-surface [role="main"]))
+ aside.app-shell-left-panel::after {
+ background: transparent !important;
+}
+
+html[data-dream-skin="active"]:is([data-dream-task-mode="full"], [data-dream-art-task-mode="full"])[data-dream-art-wide="true"]:has(main.main-surface):not(:has(main.main-surface [role="main"]))
+ main.main-surface:not(:has([role="main"])) {
+ background: linear-gradient(90deg,
+ var(--ds-immersive-edge),
+ var(--ds-immersive-mid) 64%,
+ var(--ds-immersive-far)) !important;
+ border: 0 !important;
+ border-radius: 0 !important;
+ box-shadow: none !important;
+}
+
+html[data-dream-skin="active"]:is([data-dream-task-mode="full"], [data-dream-art-task-mode="full"])[data-dream-art-wide="true"]
+ main.main-surface:not(:has([role="main"]))::before {
+ content: none;
+}
+
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]:has(main.main-surface):not(:has(main.main-surface [role="main"]))
+ aside.app-shell-left-panel {
+ background: linear-gradient(90deg,
+ var(--ds-task-immersive-sidebar),
+ var(--ds-task-immersive-edge) 100%) !important;
+ border: 0 !important;
+ border-radius: 0 !important;
+ box-shadow: none !important;
+ backdrop-filter: none !important;
+}
+
+/* Native sidebar resize chrome inherits the sidebar background and extends
+ 20px into the main surface. Keeping it clear preserves one continuous image. */
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]:has(main.main-surface):not(:has(main.main-surface [role="main"]))
+ aside.app-shell-left-panel::after {
+ background: transparent !important;
+}
+
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]:has(main.main-surface):not(:has(main.main-surface [role="main"]))
+ main.main-surface:not(:has([role="main"])) {
+ background: linear-gradient(90deg,
+ var(--ds-task-immersive-edge),
+ var(--ds-task-immersive-mid) 64%,
+ var(--ds-task-immersive-far)) !important;
+ border: 0 !important;
+ border-radius: 0 !important;
+ box-shadow: none !important;
+}
+
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]
+ main.main-surface:not(:has([role="main"]))::before {
+ content: none;
+}
+
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]:has(main.main-surface):not(:has(main.main-surface [role="main"]))
+ .composer-surface-chrome {
+ background: var(--ds-immersive-composer-solid) !important;
+ border: 0 !important;
+ box-shadow:
+ 0 10px 30px rgb(var(--ds-bg-rgb) / .20),
+ inset 0 0 0 1px var(--ds-immersive-line),
+ inset 0 1px rgb(var(--ds-text-rgb) / .12) !important;
+ backdrop-filter: none !important;
+}
+
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"]:has(main.main-surface):not(:has(main.main-surface [role="main"]))
+ main.main-surface > header.app-header-tint {
+ background: transparent !important;
+ border-bottom: 0 !important;
+ box-shadow: none !important;
+ backdrop-filter: none !important;
+}
+
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"], [data-dream-task-mode="full"], [data-dream-art-task-mode="full"])[data-dream-art-wide="true"]
+ main.main-surface:not(:has([role="main"])) [class*="_markdown"] {
+ color: var(--ds-text) !important;
+ text-shadow:
+ 0 1px 2px rgb(var(--ds-bg-rgb) / .82),
+ 0 0 10px rgb(var(--ds-bg-rgb) / .58);
+}
+
+html[data-dream-skin="active"][data-dream-shell="light"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"], [data-dream-task-mode="full"], [data-dream-art-task-mode="full"])[data-dream-art-wide="true"]
+ main.main-surface:not(:has([role="main"])) [class*="_markdown"] {
+ text-shadow:
+ 0 1px 2px rgb(var(--ds-panel-rgb) / .92),
+ 0 0 10px rgb(var(--ds-panel-rgb) / .72);
+}
+
+html[data-dream-skin="active"]:is([data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])
+ main.main-surface:not(:has([role="main"]))::before {
+ inset: 0 0 auto;
+ height: clamp(280px, 46vh, 520px);
+ background-position: center top, center top, var(--ds-art-position);
+ background-size: 100% 100%, 100% 100%, cover;
+}
+
+html[data-dream-skin="active"]:is([data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"]):is([data-dream-art-wide="true"], [data-dream-art-aspect="wide"], [data-dream-art-aspect="ultrawide"])
+ main.main-surface:not(:has([role="main"]))::before {
+ inset: 0;
+ height: auto;
+ background-position: center, center, var(--ds-art-position);
+ background-size: 100% 100%, 100% 100%, cover;
+}
+
+html[data-dream-skin="active"]:is([data-dream-task-mode="off"], [data-dream-art-task-mode="off"])
+ main.main-surface:not(:has([role="main"]))::before {
+ content: none;
+}
+
+html[data-dream-skin="active"] main.main-surface:not(:has([role="main"])) > :not(header.app-header-tint) {
position: relative;
z-index: 1;
}
-html.codex-dream-skin .dream-task article,
-html.codex-dream-skin .dream-task [data-message-author-role] {
- color: var(--dream-text);
+html[data-dream-skin="active"] main.main-surface:not(:has([role="main"])) [role="main"] {
+ background: transparent !important;
+ color: var(--ds-text) !important;
+ text-shadow:
+ 0 1px 2px rgb(var(--ds-bg-rgb) / .72),
+ 0 0 10px rgb(var(--ds-bg-rgb) / .46);
+}
+
+html[data-dream-skin="active"][data-dream-shell="light"] main.main-surface:not(:has([role="main"])) [role="main"] {
+ text-shadow: none;
+}
+
+/* Newer Codex builds paint the list/detail panes (pull requests, the chat
+ review/terminal/browser/files side panel) with token surfaces instead of
+ [role="main"]. The side panel can coexist with a visible home shell, so this
+ transparency is deliberately not gated on the home-route selector. The nested
+ surfaces all carry the same token, so they are cleared everywhere and the
+ translucent pane tint is painted once, on the bordered outer wrapper. */
+html[data-dream-skin="active"] main.main-surface
+ :is(div, section, aside)[class~="bg-token-main-surface-primary"] {
+ background: transparent !important;
+}
+
+html[data-dream-skin="active"] main.main-surface
+ div[class~="bg-token-main-surface-primary"][class~="border-l"] {
+ background: rgb(var(--ds-panel-rgb) / .62) !important;
+ backdrop-filter: blur(10px) saturate(106%) !important;
+}
+
+html[data-dream-skin="active"] main.main-surface:not(:has([role="main"])) article {
+ border: 1px solid rgb(var(--ds-muted-rgb) / .12);
+ background: rgb(var(--ds-panel-rgb) / .44);
+ box-shadow: 0 8px 24px rgb(var(--ds-bg-rgb) / .10);
+ backdrop-filter: blur(7px) saturate(105%);
+}
+
+html[data-dream-skin="active"][data-dream-shell="light"] main.main-surface:not(:has([role="main"])) article {
+ background: rgb(var(--ds-panel-rgb) / .72);
+}
+
+html[data-dream-skin="active"][data-dream-shell="light"]
+ main.main-surface:has([role="main"])::after,
+html[data-dream-skin="active"][data-dream-shell="light"] [role="main"]:has([data-testid="home-icon"]) [data-feature="game-source"]::before {
+ text-shadow: none;
+}
+
+html[data-dream-skin="active"] main.main-surface > header.app-header-tint {
+ /* Preserve Codex's native fixed header geometry and side-panel controls. */
+ background: rgb(var(--ds-panel-rgb) / .90) !important;
+ border-bottom: 1px solid var(--ds-line) !important;
+ backdrop-filter: blur(14px) saturate(108%) !important;
+}
+
+/* Decorative chrome lives on the native shell; no injected positioning host is needed. */
+html[data-dream-skin="active"]
+ main.main-surface:not(:has([role="main"])) > header.app-header-tint::before {
+ content: var(--dream-skin-name, "Codex Dream Skin") " · "
+ var(--dream-skin-brand-subtitle, "CODEX DREAM SKIN");
+ position: absolute;
+ left: 22px;
+ top: 4px;
+ z-index: 2;
+ max-width: min(42%, 360px);
+ overflow: hidden;
+ color: var(--ds-accent);
+ font: 800 11px/1.3 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ letter-spacing: .03em;
+ text-overflow: ellipsis;
+ text-shadow: 0 1px 12px rgb(var(--ds-bg-rgb) / .32);
+ white-space: nowrap;
+ pointer-events: none;
+}
+
+html[data-dream-skin="active"]
+ main.main-surface:not(:has([role="main"])) > header.app-header-tint::after {
+ content: var(--dream-skin-status, "DREAM SKIN ONLINE");
+ position: absolute;
+ right: 84px;
+ top: 13px;
+ z-index: 2;
+ max-width: 28%;
+ overflow: hidden;
+ color: var(--ds-muted);
+ font: 700 9px/1.2 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ letter-spacing: .08em;
+ text-overflow: ellipsis;
+ text-shadow: 0 1px 10px rgb(var(--ds-bg-rgb) / .32);
+ white-space: nowrap;
+ pointer-events: none;
+}
+
+html[data-dream-skin="active"]
+ main.main-surface:has([role="main"])::after {
+ content: var(--dream-skin-quote, "MAKE SOMETHING WONDERFUL");
+ position: absolute;
+ right: 28px;
+ bottom: 72px;
+ z-index: 2;
+ pointer-events: none;
+ color: rgb(var(--ds-accent-rgb) / .74);
+ font: italic 14px/1.2 "Segoe Print", "Comic Sans MS", cursive;
+ letter-spacing: 0;
+ text-shadow: 0 0 13px rgb(var(--ds-accent-rgb) / .30);
+ transform: rotate(-3deg);
+}
+
+html[data-dream-skin="active"][data-dream-shell="light"]
+ main.main-surface:not(:has([role="main"])) > header.app-header-tint::before,
+html[data-dream-skin="active"][data-dream-shell="light"]
+ main.main-surface:not(:has([role="main"])) > header.app-header-tint::after {
+ text-shadow: none;
+}
+
+/* Windows keeps this native bar outside main; macOS simply has no match. */
+html[data-dream-skin="active"][data-dream-shell="dark"]
+ [class~="group/application-menu-top-bar"] {
+ color: var(--ds-text) !important;
+ background: rgb(var(--ds-panel-rgb) / .90) !important;
+ border-bottom: 1px solid var(--ds-line) !important;
+ box-shadow: 0 1px 12px rgb(var(--ds-bg-rgb) / .72) !important;
+ backdrop-filter: blur(16px) saturate(90%) !important;
}
-/* The home route keeps the source image expressive, with a semantic safe-area
- wash selected from image focus metadata. */
-html.codex-dream-skin .dream-home {
- --thread-content-max-width: min(1180px, calc(100cqw - 56px)) !important;
+html[data-dream-skin="active"][data-dream-shell="dark"]
+ [class~="group/application-menu-top-bar"] :is(button, svg) {
+ color: var(--ds-text) !important;
+ text-shadow: 0 1px 2px rgb(var(--ds-bg-rgb) / .82) !important;
+}
+
+html[data-dream-skin="active"] main.main-surface [role="main"] {
+ background: transparent !important;
+ scrollbar-color: rgb(var(--ds-accent-rgb) / .38) transparent;
+}
+
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) {
+ --thread-content-max-width: min(1180px, calc(100cqw - 44px)) !important;
overflow-x: hidden !important;
- background: var(--dream-surface) !important;
}
-.dream-home > div:first-child {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child {
min-height: 100% !important;
- padding-top: 24px !important;
+ padding-top: 15px !important;
}
-.dream-home > div:first-child > div:first-child {
- flex: 0 0 clamp(390px, 42cqw, 510px) !important;
- min-height: 390px !important;
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child {
+ flex: 0 0 440px !important;
+ min-height: 440px !important;
align-items: flex-start !important;
padding-bottom: 0 !important;
}
-.dream-home > div:first-child > div:first-child > div:first-child {
+/* Codex 26.721+: home-route's first child now only wraps the (usually empty)
+ native .home-banners slot; the real content column moved out to become
+ this wrapper's sibling instead of its descendant. The two rules above
+ still assume the old nesting and force this now-mostly-empty wrapper to
+ 100% height / 440px, which pushes the real sibling content off-screen
+ (see #244). Override with higher specificity only when that new slot is
+ present, so pre-26.721 layouts are untouched. */
+html[data-dream-skin="active"]
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child:has(> .home-banners) {
+ min-height: 0 !important;
+ padding-top: 0 !important;
+}
+
+html[data-dream-skin="active"]
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child:has(> .home-banners) > .home-banners {
+ flex: 0 1 auto !important;
+ min-height: 0 !important;
+}
+
+html[data-dream-skin="active"]
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child:has(> .home-banners) > .home-banners > div:first-child {
+ height: auto !important;
+ min-height: 0 !important;
+ border: 0 !important;
+ border-radius: 0 !important;
+ background: transparent !important;
+ box-shadow: none !important;
+}
+
+html[data-dream-skin="active"]
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child:has(> .home-banners) > .home-banners > div:first-child::before {
+ content: none !important;
+}
+
+/* Codex 26.721+: this chain (3-6 first-child levels deep) targets the old
+ compact hero-art card that used to live inside div:first-child's second
+ level. That level is now the empty .home-banners slot (see #244), so on
+ the new DOM these rules currently match nothing and the compact hero card
+ is dormant rather than misrendered. Left as-is pending a real fixture of
+ the new nested shape; the critical content-invisible bug is the block
+ above this one. */
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child {
position: relative !important;
isolation: isolate;
- width: calc(100% - 56px) !important;
- max-width: 1180px !important;
- height: clamp(230px, 27cqw, 326px) !important;
- min-height: 230px !important;
+ width: calc(100% - 44px) !important;
+ max-width: none !important;
+ height: clamp(248px, 31cqw, 376px) !important;
+ min-height: 248px !important;
flex: 0 1 auto !important;
+ overflow: visible !important;
padding: 0 !important;
- overflow: hidden !important;
- border: 1px solid var(--dream-line) !important;
- border-radius: 20px !important;
- background-image: var(--dream-art) !important;
+ border: 1px solid rgb(var(--ds-accent-rgb) / .30) !important;
+ border-radius: 22px !important;
+ background-color: var(--ds-panel) !important;
+ background-image: var(--dream-skin-art) !important;
background-repeat: no-repeat !important;
+ background-position: var(--ds-art-position) !important;
background-size: cover !important;
- background-position: var(--dream-art-position) !important;
- box-shadow: var(--dream-shadow) !important;
-}
-
-html.codex-dream-skin.dream-art-wide .dream-home > div:first-child > div:first-child > div:first-child {
- background-size: cover !important;
+ box-shadow: 0 16px 38px rgb(var(--ds-bg-rgb) / .30) !important;
}
-.dream-home > div:first-child > div:first-child > div:first-child::before {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child::before {
content: "";
position: absolute;
+ inset: 0 auto 0 0;
z-index: 0;
- inset: 0;
+ width: 62%;
+ border-radius: 21px 0 0 21px;
pointer-events: none;
- background: linear-gradient(90deg, var(--dream-hero-shade) 0 32%, transparent 70%);
+ background: var(--ds-hero-scrim);
}
-html.codex-dream-skin.dream-safe-right .dream-home > div:first-child > div:first-child > div:first-child::before {
- background: linear-gradient(270deg, var(--dream-hero-shade) 0 32%, transparent 70%);
+html[data-dream-skin="active"]:is([data-dream-art-safe="right"], [data-dream-art-safe-area="right"])
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child::before {
+ inset: 0 0 0 auto;
}
-html.codex-dream-skin.dream-safe-center .dream-home > div:first-child > div:first-child > div:first-child::before {
- background: radial-gradient(ellipse at center, var(--dream-hero-shade) 0 23%, transparent 72%);
+html[data-dream-skin="active"]:is([data-dream-art-safe="center"], [data-dream-art-safe-area="center"])
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child::before {
+ inset: 0 14%;
+ width: auto;
+ border-radius: 0;
}
-html.codex-dream-skin.dream-safe-none .dream-home > div:first-child > div:first-child > div:first-child::before {
- background: transparent;
+html[data-dream-skin="active"]:is([data-dream-art-safe="none"], [data-dream-art-safe-area="none"])
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child::before {
+ inset: 0;
+ width: auto;
+ border-radius: 21px;
}
-.dream-home > div:first-child > div:first-child > div:first-child > div:first-child {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child > div:first-child {
position: relative;
z-index: 1;
- box-sizing: border-box;
height: 100%;
align-items: center !important;
justify-content: flex-start !important;
- padding: 30px 40px;
+ padding: 0 38px;
}
-.dream-home > div:first-child > div:first-child > div:first-child > div:first-child > div:first-child {
- width: min(48%, 520px) !important;
- align-items: flex-start !important;
- gap: 0 !important;
+html[data-dream-skin="active"]:is([data-dream-art-safe="right"], [data-dream-art-safe-area="right"])
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child > div:first-child {
+ justify-content: flex-end !important;
}
-html.codex-dream-skin.dream-safe-right .dream-home > div:first-child > div:first-child > div:first-child > div:first-child {
- justify-content: flex-end !important;
+html[data-dream-skin="active"]:is([data-dream-art-safe="center"], [data-dream-art-safe-area="center"])
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child > div:first-child {
+ justify-content: center !important;
}
-html.codex-dream-skin.dream-safe-right .dream-home > div:first-child > div:first-child > div:first-child > div:first-child > div:first-child {
- align-items: flex-end !important;
- text-align: right !important;
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child > div:first-child > div:first-child {
+ width: min(46%, 520px) !important;
+ align-items: flex-start !important;
+ gap: 0 !important;
}
-html.codex-dream-skin.dream-safe-center .dream-home > div:first-child > div:first-child > div:first-child > div:first-child {
- justify-content: center !important;
+html[data-dream-skin="active"]:is([data-dream-art-safe="right"], [data-dream-art-safe-area="right"])
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child > div:first-child > div:first-child {
+ align-items: flex-end !important;
+ text-align: right !important;
}
-html.codex-dream-skin.dream-safe-center .dream-home > div:first-child > div:first-child > div:first-child > div:first-child > div:first-child {
+html[data-dream-skin="active"]:is([data-dream-art-safe="center"], [data-dream-art-safe-area="center"])
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child > div:first-child > div:first-child {
+ width: min(58%, 640px) !important;
align-items: center !important;
text-align: center !important;
}
-.dream-home [data-testid="home-icon"] {
- display: none !important;
-}
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) [data-testid="home-icon"] { display: none !important; }
-.dream-home [data-feature="game-source"] {
- display: flex !important;
- flex-direction: column !important;
- align-items: inherit !important;
- justify-content: flex-start !important;
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) [data-feature="game-source"] {
+ display: block !important;
max-width: 100% !important;
- color: var(--dream-text) !important;
- font-size: 28px !important;
- line-height: 1.25 !important;
- font-weight: 720 !important;
+ color: var(--ds-text) !important;
+ font-size: clamp(19px, 1.85vw, 27px) !important;
+ line-height: 1.28 !important;
+ font-weight: 760 !important;
text-align: inherit !important;
- text-shadow: 0 1px 16px var(--dream-surface);
+ text-shadow: none !important;
opacity: 1 !important;
pointer-events: auto !important;
}
-.dream-home [data-feature="game-source"] button {
- margin: 0 4px;
- padding: 2px 7px;
- border: 1px solid var(--dream-line);
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) [data-feature="game-source"]::before {
+ content: var(--dream-skin-name, "Codex Dream Skin");
+ display: block;
+ margin-bottom: 9px;
+ color: var(--ds-accent);
+ font: 800 12px/1.3 "Microsoft YaHei UI", sans-serif;
+ letter-spacing: .10em;
+ text-shadow: 0 0 14px rgb(var(--ds-accent-rgb) / .26);
+}
+
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) [data-feature="game-source"]::after {
+ content: var(--dream-skin-tagline, "把喜欢的画面变成可交互的 Codex 工作台。");
+ display: block;
+ margin-top: 13px;
+ color: rgb(var(--ds-text-rgb) / .76);
+ font-size: 13px;
+ line-height: 1.5;
+ font-weight: 500;
+ letter-spacing: 0;
+}
+
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) [data-feature="game-source"] button {
+ margin: 0 5px;
+ padding: 2px 9px 3px;
+ border: 1px solid rgb(var(--ds-accent-rgb) / .36);
border-radius: 999px;
- color: var(--dream-text) !important;
- background: var(--dream-accent-soft) !important;
- text-underline-offset: 4px;
+ background: rgb(var(--ds-accent-rgb) / .10);
+ color: var(--ds-accent) !important;
+ text-decoration-color: rgb(var(--ds-accent-rgb) / .56) !important;
+ text-underline-offset: 5px;
}
-.dream-home > div:first-child > div:first-child > div:first-child > div:nth-child(2) {
- left: 0 !important;
- right: 0 !important;
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) [data-feature="game-source"] button::before {
+ content: var(--dream-skin-project-prefix, "选择项目 · ");
+ font-size: .46em;
+ font-weight: 700;
+ vertical-align: middle;
+ opacity: .86;
+}
+
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child > div:nth-child(2) {
+ left: 14px !important;
+ right: 14px !important;
top: 100% !important;
- margin-top: 16px !important;
+ margin-top: 13px !important;
}
-.dream-home .group\/home-suggestions {
- overflow: visible !important;
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) .group\/home-suggestions { overflow: visible !important; }
+
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) .group\/home-suggestions button {
+ position: relative !important;
+ min-height: 118px !important;
+ padding: 14px 13px 12px !important;
+ align-items: stretch !important;
+ justify-content: flex-start !important;
+ text-align: center !important;
+ border: 1px solid rgb(var(--ds-muted-rgb) / .18) !important;
+ border-radius: 18px !important;
+ background: rgb(var(--ds-panel-rgb) / .90) !important;
+ color: var(--ds-text) !important;
+ font-weight: 600 !important;
+ line-height: 1.45 !important;
+ box-shadow: 0 8px 22px rgb(var(--ds-bg-rgb) / .18) !important;
+ backdrop-filter: blur(10px) saturate(105%) !important;
+ transform: translateY(0);
+ transition: transform .18s cubic-bezier(.22, 1, .36, 1),
+ box-shadow .18s cubic-bezier(.22, 1, .36, 1),
+ border-color .18s cubic-bezier(.22, 1, .36, 1) !important;
}
-.dream-home .group\/home-suggestions button {
- min-height: 112px !important;
- padding: 16px 14px !important;
- color: var(--dream-text) !important;
- border: 1px solid var(--dream-line-soft) !important;
- border-radius: 16px !important;
- background: var(--dream-surface-raised) !important;
- box-shadow: 0 8px 24px color-mix(in oklab, var(--dream-accent) 6%, transparent) !important;
- transition: transform 180ms cubic-bezier(.22, 1, .36, 1), border-color 180ms cubic-bezier(.22, 1, .36, 1), background-color 180ms cubic-bezier(.22, 1, .36, 1) !important;
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) .group\/home-suggestions button [class~="text-token-text-primary"] {
+ color: var(--ds-text) !important;
}
-.dream-home .group\/home-suggestions button:hover {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) .group\/home-suggestions button:hover {
transform: translateY(-2px) !important;
- border-color: var(--dream-line) !important;
- background: var(--dream-accent-soft) !important;
+ border-color: rgb(var(--ds-accent-rgb) / .42) !important;
+ box-shadow: 0 12px 28px rgb(var(--ds-bg-rgb) / .24) !important;
}
-.dream-home .group\/home-suggestions button > span:first-child > span:first-child {
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) .group\/home-suggestions button > span:first-child > span:first-child {
width: 38px;
height: 38px;
- display: grid !important;
- place-items: center;
- border-radius: 12px;
- color: var(--dream-accent-ink) !important;
- background: var(--dream-accent) !important;
- box-shadow: 0 0 0 6px var(--dream-accent-soft);
+ /* Native spans carry justify-start; grid + place-items cannot override
+ justify-content, so the glyph parks at the circle's left edge. (#176) */
+ display: flex !important;
+ align-items: center !important;
+ justify-content: center !important;
+ margin: 0 auto;
+ border: 1px solid rgb(var(--ds-accent-rgb) / .24);
+ border-radius: 50%;
+ color: var(--ds-accent) !important;
+ background: rgb(var(--ds-accent-rgb) / .12);
+ box-shadow: 0 0 0 6px rgb(var(--ds-accent-rgb) / .05);
}
-.dream-home .group\/home-suggestions button svg {
- width: 20px !important;
- height: 20px !important;
- color: currentColor !important;
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) .group\/home-suggestions button svg {
+ width: 21px !important;
+ height: 21px !important;
+ display: block !important;
+ margin: 0 !important;
+ flex: 0 0 auto !important;
+ color: var(--ds-accent) !important;
}
-html.codex-dream-skin .composer-surface-chrome {
- color: var(--dream-text) !important;
- border: 1px solid var(--dream-line) !important;
- border-radius: 18px !important;
- background: color-mix(in oklab, var(--dream-surface-raised) 95%, transparent) !important;
- box-shadow: 0 12px 34px color-mix(in oklab, var(--dream-accent) 8%, transparent) !important;
- backdrop-filter: blur(14px) saturate(1.06) !important;
-}
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) .group\/home-suggestions button > span:first-child { justify-content: center !important; }
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) .group\/home-suggestions button > span:last-child { align-items: center !important; text-align: center !important; }
-html.codex-dream-skin .ProseMirror {
- color: var(--dream-text) !important;
- caret-color: var(--dream-accent) !important;
-}
-
-html.codex-dream-skin button[class~="bg-token-foreground"] {
- color: var(--dream-accent-ink) !important;
- background: var(--dream-accent) !important;
- box-shadow: none !important;
+html[data-dream-skin="active"] .composer-surface-chrome {
+ overflow: visible !important;
+ border: 1px solid rgb(var(--ds-muted-rgb) / .18) !important;
+ border-radius: 22px !important;
+ background: rgb(var(--ds-panel-rgb) / .94) !important;
+ box-shadow: 0 10px 28px rgb(var(--ds-bg-rgb) / .24) !important;
+ backdrop-filter: blur(16px) saturate(108%) !important;
}
-html.codex-dream-skin [data-message-author-role],
-html.codex-dream-skin article {
- color: var(--dream-text);
-}
+html[data-dream-skin="active"] .composer-surface-chrome::before { content: none !important; }
-/* A 16:9 or wider image is painted once on the native window. The sidebar,
- title bar and route surface become coordinated readability layers instead
- of repeating or cropping the image inside individual panels. */
-html.codex-dream-skin.dream-art-wide:has(main.main-surface.dream-home-shell) body,
-html.codex-dream-skin.dream-art-wide:is(.dream-task-ambient, .dream-task-banner):has(main.main-surface:not(.dream-home-shell)) body {
- background-color: var(--dream-canvas) !important;
- background-image: var(--dream-art) !important;
- background-position: var(--dream-art-position) !important;
+/* A wide image can carry the home screen too. The native hero remains live,
+ but the artwork is painted once on the window instead of repeated in a card. */
+html[data-dream-skin="active"][data-dream-art-wide="true"]:has(main.main-surface [role="main"]) body {
+ background-image: var(--dream-skin-art) !important;
+ background-position: var(--ds-art-position) !important;
background-size: cover !important;
background-repeat: no-repeat !important;
background-attachment: fixed !important;
}
-html.codex-dream-skin.dream-art-wide:has(main.main-surface.dream-home-shell)
- aside.app-shell-left-panel,
-html.codex-dream-skin.dream-art-wide:is(.dream-task-ambient, .dream-task-banner):has(main.main-surface:not(.dream-home-shell))
+html[data-dream-skin="active"][data-dream-art-wide="true"]:has(main.main-surface [role="main"])
aside.app-shell-left-panel {
background: linear-gradient(90deg,
- var(--dream-immersive-sidebar),
- var(--dream-immersive-edge)) !important;
+ var(--ds-immersive-sidebar),
+ var(--ds-immersive-edge) 100%) !important;
border: 0 !important;
border-radius: 0 !important;
box-shadow: none !important;
backdrop-filter: none !important;
}
-html.codex-dream-skin.dream-art-wide:is(.dream-task-ambient, .dream-task-banner):has(main.main-surface:not(.dream-home-shell))
- aside.app-shell-left-panel {
- background: linear-gradient(90deg,
- var(--dream-task-immersive-sidebar),
- var(--dream-task-immersive-edge)) !important;
-}
-
-html.codex-dream-skin.dream-art-wide:has(main.main-surface.dream-home-shell)
- aside.app-shell-left-panel::after,
-html.codex-dream-skin.dream-art-wide:is(.dream-task-ambient, .dream-task-banner):has(main.main-surface:not(.dream-home-shell))
+html[data-dream-skin="active"][data-dream-art-wide="true"]:has(main.main-surface [role="main"])
aside.app-shell-left-panel::after {
background: transparent !important;
}
-html.codex-dream-skin.dream-art-wide main.main-surface.dream-home-shell,
-html.codex-dream-skin.dream-art-wide:is(.dream-task-ambient, .dream-task-banner) main.main-surface:not(.dream-home-shell) {
+html[data-dream-skin="active"][data-dream-art-wide="true"] main.main-surface:has([role="main"]) {
background: linear-gradient(90deg,
- var(--dream-immersive-edge),
- var(--dream-immersive-mid) 64%,
- var(--dream-immersive-far)) !important;
+ var(--ds-immersive-edge),
+ var(--ds-immersive-mid) 64%,
+ var(--ds-immersive-far)) !important;
border: 0 !important;
border-radius: 0 !important;
box-shadow: none !important;
}
-html.codex-dream-skin.dream-art-wide:is(.dream-task-ambient, .dream-task-banner) main.main-surface:not(.dream-home-shell) {
- background: linear-gradient(90deg,
- var(--dream-task-immersive-edge),
- var(--dream-task-immersive-mid) 64%,
- var(--dream-task-immersive-far)) !important;
-}
-
-html.codex-dream-skin.dream-art-wide:is(.dream-task-ambient, .dream-task-banner) .dream-task {
- background: transparent !important;
-}
-
-html.codex-dream-skin.dream-art-wide:is(.dream-task-ambient, .dream-task-banner) .dream-task::before {
- content: none !important;
-}
-
-html.codex-dream-skin.dream-art-wide main.main-surface > header.app-header-tint {
+html[data-dream-skin="active"][data-dream-art-wide="true"] main.main-surface:has([role="main"])
+ > header.app-header-tint {
background: transparent !important;
border-bottom: 0 !important;
box-shadow: none !important;
backdrop-filter: none !important;
- text-shadow: 0 1px 2px color-mix(in oklab, var(--dream-canvas) 86%, transparent),
- 0 0 8px color-mix(in oklab, var(--dream-canvas) 58%, transparent) !important;
}
-html.codex-dream-skin.dream-art-wide main.main-surface > header.app-header-tint svg {
- filter: drop-shadow(0 1px 2px color-mix(in oklab, var(--dream-canvas) 72%, transparent));
+html[data-dream-skin="active"][data-dream-art-wide="true"] main.main-surface
+ > header.app-header-tint {
+ color: var(--ds-text) !important;
+ text-shadow:
+ 0 1px 2px rgb(var(--ds-bg-rgb) / .86),
+ 0 0 8px rgb(var(--ds-bg-rgb) / .52) !important;
}
-html.codex-dream-skin.dream-art-wide main.main-surface [role="main"] {
- background: transparent !important;
+html[data-dream-skin="active"][data-dream-art-wide="true"] main.main-surface
+ > header.app-header-tint svg {
+ color: rgb(var(--ds-muted-rgb) / .96) !important;
+ filter: drop-shadow(0 1px 2px rgb(var(--ds-bg-rgb) / .72));
}
-html.codex-dream-skin.dream-art-wide main.main-surface.dream-home-shell
- .dream-home > div:first-child > div:first-child > div:first-child {
+html[data-dream-skin="active"][data-dream-art-wide="true"] main.main-surface:has([role="main"])
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child {
border: 0 !important;
border-radius: 0 !important;
background: transparent !important;
box-shadow: none !important;
}
-html.codex-dream-skin.dream-art-wide main.main-surface.dream-home-shell
- .dream-home > div:first-child > div:first-child > div:first-child::before {
+html[data-dream-skin="active"][data-dream-art-wide="true"] main.main-surface:has([role="main"])
+ [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child::before {
content: none !important;
}
-html.codex-dream-skin.dream-art-wide main.main-surface.dream-home-shell
- .dream-home .group\/home-suggestions button {
- background: color-mix(in oklab, var(--dream-surface-raised) 58%, transparent) !important;
- box-shadow: 0 8px 22px color-mix(in oklab, var(--dream-canvas) 18%, transparent),
- inset 0 0 0 1px var(--dream-immersive-line) !important;
- backdrop-filter: blur(8px) saturate(1.04) !important;
+html[data-dream-skin="active"][data-dream-art-wide="true"] main.main-surface:has([role="main"])
+ [role="main"]:has([data-testid="home-icon"]) .group\/home-suggestions button {
+ background: rgb(var(--ds-panel-rgb) / .56) !important;
+ box-shadow:
+ 0 8px 22px rgb(var(--ds-bg-rgb) / .16),
+ inset 0 0 0 1px rgb(var(--ds-muted-rgb) / .20) !important;
+ backdrop-filter: blur(8px) saturate(104%) !important;
}
-/* The composer owns exactly one solid readability surface. Native bottom
- gradients are cleared below so the control never appears double stacked. */
-html.codex-dream-skin.dream-art-wide .composer-surface-chrome {
- color: var(--dream-text) !important;
- background: var(--dream-immersive-composer) !important;
+html[data-dream-skin="active"][data-dream-art-wide="true"] .composer-surface-chrome {
+ background: var(--ds-immersive-composer-solid) !important;
border: 0 !important;
- box-shadow: 0 10px 30px color-mix(in oklab, var(--dream-canvas) 20%, transparent),
- inset 0 0 0 1px var(--dream-immersive-line) !important;
+ box-shadow:
+ 0 10px 30px rgb(var(--ds-bg-rgb) / .20),
+ inset 0 0 0 1px var(--ds-immersive-line),
+ inset 0 1px rgb(var(--ds-text-rgb) / .12) !important;
backdrop-filter: none !important;
}
-html.codex-dream-skin.dream-art-wide .composer-surface-chrome::before {
- content: none !important;
+html[data-dream-skin="active"][data-dream-shell="light"][data-dream-art-wide="true"]
+ .composer-surface-chrome {
+ box-shadow:
+ 0 10px 28px rgb(var(--ds-bg-rgb) / .14),
+ inset 0 0 0 1px var(--ds-immersive-line),
+ inset 0 1px rgb(var(--ds-panel-rgb) / .72) !important;
+ backdrop-filter: blur(8px) saturate(102%) !important;
}
-/* Current Codex builds render the home project picker in a hashed utility bar.
- Merge it with the composer so the bottom control reads as one surface. */
-html.codex-dream-skin .dream-home-utility {
- border: 1px solid var(--dream-line) !important;
- border-bottom: 0 !important;
- border-radius: 18px 18px 0 0 !important;
- background: var(--dream-immersive-composer) !important;
- box-shadow: none !important;
+html[data-dream-skin="active"][data-dream-shell="light"]
+ .composer-surface-chrome p.placeholder::after {
+ color: rgb(var(--ds-muted-rgb) / .78) !important;
+ opacity: 1 !important;
}
-html.codex-dream-skin .dream-home:has(.dream-home-utility) .composer-surface-chrome {
- border-radius: 0 0 18px 18px !important;
- box-shadow: 0 10px 30px color-mix(in oklab, var(--dream-canvas) 20%, transparent),
- inset 1px 0 var(--dream-immersive-line),
- inset -1px 0 var(--dream-immersive-line),
- inset 0 -1px var(--dream-immersive-line) !important;
+/* Current Codex builds render the home project picker as a separate opaque
+ cap above the composer. Join both native controls into one continuous
+ surface while leaving task and utility-route inputs untouched. */
+html[data-dream-skin="active"] [class*="_homeUtilityBar_"] {
+ top: 0 !important;
+ width: 100% !important;
+ margin-inline: 0 !important;
+ padding-inline: 18px !important;
+ border-radius: 22px 22px 0 0 !important;
+ background: rgb(var(--ds-panel-rgb) / .94) !important;
+ box-shadow:
+ inset 1px 0 rgb(var(--ds-muted-rgb) / .18),
+ inset -1px 0 rgb(var(--ds-muted-rgb) / .18),
+ inset 0 1px rgb(var(--ds-muted-rgb) / .18) !important;
+ backdrop-filter: blur(16px) saturate(108%) !important;
+}
+
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]):has([class*="_homeUtilityBar_"])
+ .composer-surface-chrome {
+ border: 0 !important;
+ border-radius: 0 0 22px 22px !important;
+ box-shadow:
+ 0 10px 28px rgb(var(--ds-bg-rgb) / .24),
+ inset 1px 0 rgb(var(--ds-muted-rgb) / .18),
+ inset -1px 0 rgb(var(--ds-muted-rgb) / .18),
+ inset 0 -1px rgb(var(--ds-muted-rgb) / .18) !important;
+}
+
+html[data-dream-skin="active"][data-dream-art-wide="true"] [class*="_homeUtilityBar_"] {
+ background: var(--ds-immersive-composer-solid) !important;
+ box-shadow:
+ inset 1px 0 var(--ds-immersive-line),
+ inset -1px 0 var(--ds-immersive-line),
+ inset 0 1px rgb(var(--ds-text-rgb) / .12) !important;
+ backdrop-filter: none !important;
}
-html.codex-dream-skin.dream-art-wide:is(.dream-task-ambient, .dream-task-banner)
- .dream-aux-panel-layer {
- background: transparent !important;
+html[data-dream-skin="active"][data-dream-art-wide="true"]
+ [role="main"]:has([data-testid="home-icon"]):has([class*="_homeUtilityBar_"]) .composer-surface-chrome {
+ box-shadow:
+ 0 10px 30px rgb(var(--ds-bg-rgb) / .20),
+ inset 1px 0 var(--ds-immersive-line),
+ inset -1px 0 var(--ds-immersive-line),
+ inset 0 -1px var(--ds-immersive-line) !important;
+}
+
+html[data-dream-skin="active"][data-dream-shell="light"][data-dream-art-wide="true"]
+ [class*="_homeUtilityBar_"] {
+ box-shadow:
+ inset 1px 0 var(--ds-immersive-line),
+ inset -1px 0 var(--ds-immersive-line),
+ inset 0 1px rgb(var(--ds-panel-rgb) / .72) !important;
+ backdrop-filter: blur(8px) saturate(102%) !important;
+}
+
+html[data-dream-skin="active"] [class*="_homeUtilityBar_"] button,
+html[data-dream-skin="active"] .composer-surface-chrome button:not([class~="bg-token-foreground"]) {
+ color: var(--ds-muted) !important;
+}
+
+html[data-dream-skin="active"] [class*="_homeUtilityBar_"] button svg,
+html[data-dream-skin="active"] .composer-surface-chrome button svg {
+ color: currentColor !important;
+}
+
+html[data-dream-skin="active"] [class*="_homeUtilityBar_"] button *,
+html[data-dream-skin="active"] .composer-surface-chrome button:not([class~="bg-token-foreground"]) * {
+ color: currentColor !important;
}
-html.codex-dream-skin.dream-art-wide:is(.dream-task-ambient, .dream-task-banner)
- .dream-aux-panel-right {
- border-left-color: var(--dream-immersive-line) !important;
+html[data-dream-skin="active"] [class*="_homeUtilityBar_"] button:hover,
+html[data-dream-skin="active"] .composer-surface-chrome button:not([class~="bg-token-foreground"]):hover {
+ color: var(--ds-text) !important;
+ background: rgb(var(--ds-accent-rgb) / .10) !important;
}
-html.codex-dream-skin.dream-art-wide:is(.dream-task-ambient, .dream-task-banner)
- .dream-aux-panel-bottom {
- border-top-color: var(--dream-immersive-line) !important;
+html[data-dream-skin="active"] .composer-surface-chrome p.placeholder::after {
+ color: rgb(var(--ds-muted-rgb) / .82) !important;
+ opacity: 1 !important;
}
-html.codex-dream-skin.dream-art-wide:is(.dream-task-ambient, .dream-task-banner)
- .dream-aux-panel-bottom [data-codex-terminal="true"] {
- background: color-mix(in oklab, var(--dream-surface-raised) 58%, transparent) !important;
+/* Search routes ship an opaque sticky band and input surface. Keep the
+ control legible while allowing the selected image to remain continuous. */
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"])[data-dream-art-wide="true"]
+ main.main-surface:not(:has([role="main"])) div.sticky:has(input[type="text"]) {
+ background: transparent !important;
}
-/* Plugins, scheduled tasks and search routes ship opaque sticky wrappers.
- Clear only full-window or sticky route chrome, leaving functional cards and
- their hover states intact. */
-html.codex-dream-skin.dream-art-wide:is(.dream-task-ambient, .dream-task-banner)
- main.main-surface:not(.dream-home-shell) div.sticky:has(input[type="text"]),
-html.codex-dream-skin.dream-art-wide:is(.dream-task-ambient, .dream-task-banner)
- main.main-surface:not(.dream-home-shell) div.sticky:has(input[type="text"])::after {
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"])[data-dream-art-wide="true"]
+ main.main-surface:not(:has([role="main"])) div.sticky:has(input[type="text"])::after {
background: transparent !important;
}
-html.codex-dream-skin.dream-art-wide:is(.dream-task-ambient, .dream-task-banner)
- main.main-surface:not(.dream-home-shell) div.no-drag:has(> input[type="text"]) {
- background: var(--dream-immersive-composer) !important;
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"])[data-dream-art-wide="true"]
+ main.main-surface:not(:has([role="main"])) div.no-drag:has(> input[type="text"]) {
+ background: var(--ds-immersive-composer) !important;
border: 0 !important;
- box-shadow: inset 0 0 0 1px var(--dream-immersive-line) !important;
+ box-shadow:
+ 0 8px 22px rgb(var(--ds-bg-rgb) / .16),
+ inset 0 0 0 1px var(--ds-immersive-line) !important;
backdrop-filter: none !important;
}
-html.codex-dream-skin.dream-art-wide:is(.dream-task-ambient, .dream-task-banner)
- main.main-surface:not(.dream-home-shell)
+/* Pull Requests and similar utility routes wrap their content in a native
+ full-size opaque surface. Only clear full-window wrappers, not cards. */
+html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"])[data-dream-art-wide="true"]
+ main.main-surface:not(:has([role="main"]))
[class~="bg-token-main-surface-primary"][class~="h-full"][class~="w-full"] {
background: transparent !important;
}
-html.codex-dream-skin.dream-art-wide main.main-surface .app-shell-main-content-frame {
+html[data-dream-skin="active"][data-dream-art-wide="true"] main.main-surface
+ .app-shell-main-content-frame {
border-top: 0 !important;
}
-html.codex-dream-skin.dream-art-wide main.main-surface .app-shell-main-content-top-fade {
+html[data-dream-skin="active"][data-dream-art-wide="true"] main.main-surface
+ .app-shell-main-content-top-fade,
+html[data-dream-skin="active"][data-dream-art-wide="true"] main.main-surface
+ [class*="_MainContentTopFade_"] {
display: none !important;
background: transparent !important;
}
-html.codex-dream-skin.dream-art-wide main.main-surface
+/* Codex paints a second opaque fade behind the sticky composer. The composer
+ already owns its readable surface, so retaining this layer creates a false
+ bottom panel and makes the control look duplicated. */
+html[data-dream-skin="active"][data-dream-art-wide="true"] main.main-surface
.thread-scroll-container .bg-gradient-to-t.from-token-main-surface-primary {
background: transparent !important;
}
-@media (max-width: 1120px) {
- html.codex-dream-skin .dream-home {
- --thread-content-max-width: min(940px, calc(100cqw - 36px)) !important;
- }
-
- .dream-home > div:first-child > div:first-child > div:first-child {
- width: calc(100% - 36px) !important;
- }
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) div:has(> .horizontal-scroll-fade-mask .group\/project-selector) {
+ position: relative;
+ padding-top: 28px !important;
+ border: 1px solid rgb(var(--ds-muted-rgb) / .16);
+ border-bottom: 0;
+ background: rgb(var(--ds-panel-rgb) / .92) !important;
}
-@media (max-width: 900px) {
- .dream-home > div:first-child {
- padding-top: 16px !important;
- }
-
- .dream-home > div:first-child > div:first-child {
- flex-basis: 370px !important;
- min-height: 370px !important;
- }
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) div:has(> .horizontal-scroll-fade-mask .group\/project-selector)::before {
+ content: var(--dream-skin-project-label, "选择项目");
+ position: absolute;
+ left: 13px;
+ top: 6px;
+ z-index: 2;
+ color: var(--ds-muted);
+ font-size: 12px;
+ font-weight: 720;
+ letter-spacing: 0;
+ white-space: nowrap;
+}
- .dream-home > div:first-child > div:first-child > div:first-child {
- height: 220px !important;
- min-height: 220px !important;
- border-radius: 16px !important;
- }
+html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) .group\/project-selector > button {
+ border-color: rgb(var(--ds-accent-rgb) / .22) !important;
+ background: rgb(var(--ds-accent-rgb) / .08) !important;
+ color: var(--ds-text) !important;
+ box-shadow: 0 3px 12px rgb(var(--ds-bg-rgb) / .12) !important;
+}
- .dream-home > div:first-child > div:first-child > div:first-child > div:first-child {
- padding: 24px;
- }
+html[data-dream-skin="active"] .ProseMirror {
+ color: var(--ds-text) !important;
+ caret-color: var(--ds-accent) !important;
+}
- .dream-home > div:first-child > div:first-child > div:first-child > div:first-child > div:first-child {
- width: 62% !important;
- }
+html[data-dream-skin="active"] button[class~="bg-token-foreground"] {
+ background: var(--ds-accent) !important;
+ color: var(--ds-on-accent) !important;
+ box-shadow: 0 5px 14px rgb(var(--ds-accent-rgb) / .20) !important;
+}
- .dream-home [data-feature="game-source"] {
- font-size: 22px !important;
- }
+html[data-dream-skin="active"] article,
+html[data-dream-skin="active"] [data-message-author-role] { border-radius: 16px; }
- .dream-home .group\/home-suggestions button {
- min-height: 104px !important;
- padding: 12px 10px !important;
- font-size: 12px !important;
- }
+@media (max-width: 1120px) {
+ html[data-dream-skin="active"]
+ main.main-surface:has([role="main"])::after { content: none; }
+ html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) { --thread-content-max-width: min(940px, calc(100cqw - 30px)) !important; }
+ html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child { width: calc(100% - 28px) !important; }
}
-@media (max-width: 680px) {
- .dream-home > div:first-child > div:first-child > div:first-child::before,
- html.codex-dream-skin.dream-safe-right .dream-home > div:first-child > div:first-child > div:first-child::before,
- html.codex-dream-skin.dream-safe-center .dream-home > div:first-child > div:first-child > div:first-child::before {
- background: color-mix(in oklab, var(--dream-surface) 78%, transparent);
+@media (max-width: 900px) {
+ html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child {
+ flex-basis: 408px !important;
+ min-height: 408px !important;
}
-
- .dream-home > div:first-child > div:first-child > div:first-child > div:first-child,
- html.codex-dream-skin.dream-safe-right .dream-home > div:first-child > div:first-child > div:first-child > div:first-child,
- html.codex-dream-skin.dream-safe-center .dream-home > div:first-child > div:first-child > div:first-child > div:first-child {
- justify-content: center !important;
+ html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child {
+ height: 232px !important;
+ min-height: 232px !important;
}
-
- .dream-home > div:first-child > div:first-child > div:first-child > div:first-child > div:first-child,
- html.codex-dream-skin.dream-safe-right .dream-home > div:first-child > div:first-child > div:first-child > div:first-child > div:first-child,
- html.codex-dream-skin.dream-safe-center .dream-home > div:first-child > div:first-child > div:first-child > div:first-child > div:first-child {
- width: min(88%, 440px) !important;
- align-items: center !important;
- text-align: center !important;
+ html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child > div:first-child { padding: 0 26px; }
+ html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) > div:first-child > div:first-child > div:first-child > div:first-child > div:first-child {
+ width: min(58%, 460px) !important;
}
+ html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) [data-feature="game-source"] { font-size: 18px !important; }
+ html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) [data-feature="game-source"]::before { margin-bottom: 7px; font-size: 10px; }
+ html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) [data-feature="game-source"]::after { margin-top: 9px; font-size: 11px; }
+ html[data-dream-skin="active"] [role="main"]:has([data-testid="home-icon"]) .group\/home-suggestions button { min-height: 112px !important; font-size: 12px !important; }
}
@media (prefers-reduced-motion: reduce) {
- html.codex-dream-skin *,
- html.codex-dream-skin *::before,
- html.codex-dream-skin *::after {
+ html[data-dream-skin="active"] *,
+ html[data-dream-skin="active"] *::before,
+ html[data-dream-skin="active"] *::after {
scroll-behavior: auto !important;
transition-duration: .01ms !important;
+ animation-duration: .01ms !important;
+ animation-iteration-count: 1 !important;
}
}
diff --git a/assets/inject/upstream/dream-skin/windows/renderer-inject.js b/assets/inject/upstream/dream-skin/windows/renderer-inject.js
index e4b6812c4..68e1e5c9b 100644
--- a/assets/inject/upstream/dream-skin/windows/renderer-inject.js
+++ b/assets/inject/upstream/dream-skin/windows/renderer-inject.js
@@ -1,459 +1,863 @@
-((cssText, artDataUrl, rawConfig) => {
+// Canonical cross-platform renderer. Run tools/sync-runtime-assets.mjs after editing.
+((cssText, artDataUrl, themeConfig) => {
+ const SELECTOR_CONTRACT = {"schema":"codex-dream-skin-selectors/1","selectors":[{"key":"shell-main","selector":"main.main-surface","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.app-header-tint","tier":"L1","scope":"all","required":true},{"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":"[data-message-author-role]","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"]};
const STATE_KEY = "__CODEX_DREAM_SKIN_STATE__";
+ const DISABLED_KEY = "__CODEX_DREAM_SKIN_DISABLED__";
+ const STYLE_REGISTRY_KEY = "__CODEX_DREAM_SKIN_STYLE_SHEETS__";
const STYLE_ID = "codex-dream-skin-style";
- const CHROME_ID = "codex-dream-skin-chrome";
- const ROOT_CLASSES = [
- "codex-dream-skin",
- "dream-theme-light",
- "dream-theme-dark",
- "dream-art-wide",
- "dream-art-standard",
- "dream-focus-left",
- "dream-focus-center",
- "dream-focus-right",
- "dream-safe-left",
- "dream-safe-center",
- "dream-safe-right",
- "dream-safe-none",
- "dream-task-ambient",
- "dream-task-banner",
- "dream-task-off",
+ const MAIN_COMPAT_ATTR = "data-codex-plus-dream-skin-main-compat";
+ const SHELL_ATTR = "data-dream-shell";
+ const PART_ATTR = "data-ds-part";
+ const ROOT_ATTRS = [
+ "data-dream-skin", SHELL_ATTR,
+ "data-dream-art-wide", "data-dream-art-safe", "data-dream-task-mode",
+ "data-dream-art-safe-area", "data-dream-art-task-mode", "data-dream-art-aspect",
+ "data-dream-art-ready",
];
- const ROOT_PROPERTIES = [
- "--dream-art",
- "--dream-art-position",
- "--dream-focus-x",
- "--dream-focus-y",
- "--dream-accent",
- "--dream-accent-ink",
- "--dream-image-luma",
+ const VERSION = __DREAM_SKIN_VERSION_JSON__;
+ const STYLE_REVISION = __DREAM_SKIN_STYLE_REVISION_JSON__;
+ const PAYLOAD_REVISION = __DREAM_SKIN_PAYLOAD_REVISION_JSON__;
+ const THEME = themeConfig && typeof themeConfig === "object" ? themeConfig : {};
+ const ART = THEME.art && typeof THEME.art === "object" ? THEME.art : {};
+ const ART_METADATA = THEME.artMetadata && typeof THEME.artMetadata === "object"
+ ? THEME.artMetadata : null;
+ const ANALYSIS_CACHE_KEY = "__CODEX_DREAM_SKIN_ANALYSIS_CACHE__";
+ const THEME_VARIABLES = [
+ "--ds-bg", "--ds-panel", "--ds-panel-2", "--ds-green", "--ds-lime",
+ "--ds-cyan", "--ds-purple", "--ds-text", "--ds-muted", "--ds-line",
+ "--ds-bg-rgb", "--ds-panel-rgb", "--ds-panel-2-rgb", "--ds-accent-rgb",
+ "--ds-accent-alt-rgb", "--ds-secondary-rgb", "--ds-highlight-rgb",
+ "--ds-text-rgb", "--ds-muted-rgb", "--ds-line-rgb",
+ "--dream-art-focus-x", "--dream-art-focus-y", "--dream-art-position",
+ "--dream-skin-focus-x", "--dream-skin-focus-y", "--dream-skin-art-position",
+ "--dream-skin-name", "--dream-skin-tagline", "--dream-skin-project-prefix",
+ "--dream-skin-project-label", "--dream-skin-brand-subtitle", "--dream-skin-status",
+ "--dream-skin-quote", "--dream-skin-art",
+ "--ds-theme-color-background", "--ds-theme-color-panel",
+ "--ds-theme-color-panel-alt", "--ds-theme-color-accent",
+ "--ds-theme-color-accent-alt", "--ds-theme-color-secondary",
+ "--ds-theme-color-highlight", "--ds-theme-color-text",
+ "--ds-theme-color-muted", "--ds-theme-color-line",
+ "--ds-theme-font-family", "--ds-theme-font-scale",
+ "--ds-theme-surface-radius", "--ds-theme-surface-opacity",
+ "--ds-theme-surface-blur", "--ds-theme-surface-border-alpha",
+ "--ds-theme-surface-shadow", "--ds-theme-image-focus-x",
+ "--ds-theme-image-focus-y", "--ds-theme-image-zoom",
+ "--ds-theme-image-dim", "--ds-theme-image-task-intensity",
+ "--ds-theme-density-scale", "--ds-theme-motion-level",
];
- const HOME_UTILITY_CLASS = "dream-home-utility";
- const AUX_PANEL_LAYER_CLASS = "dream-aux-panel-layer";
- const AUX_PANEL_RIGHT_CLASS = "dream-aux-panel-right";
- const AUX_PANEL_BOTTOM_CLASS = "dream-aux-panel-bottom";
- const AUX_PANEL_CLASSES = [AUX_PANEL_LAYER_CLASS, AUX_PANEL_RIGHT_CLASS, AUX_PANEL_BOTTOM_CLASS];
+ const selectorByKey = new Map(SELECTOR_CONTRACT.selectors.map((entry) => [entry.key, entry]));
+ const stableTestidSelector = (testid) => SELECTOR_CONTRACT.stableTestids?.includes(testid)
+ ? `[data-testid="${testid}"]` : null;
const installToken = {};
- let samplingNativeShell = false;
- let observer = null;
- window.__CODEX_DREAM_SKIN_DISABLED__ = false;
-
- const clamp = (value, min = 0, max = 1) => Math.min(max, Math.max(min, Number(value)));
- const luminance = (red, green, blue) => {
- const linear = [red, green, blue].map((value) => {
- const channel = value / 255;
- return channel <= .04045 ? channel / 12.92 : ((channel + .055) / 1.055) ** 2.4;
- });
- return .2126 * linear[0] + .7152 * linear[1] + .0722 * linear[2];
- };
- const defaultProfile = {
- appearance: "dark",
- accent: [108, 131, 142],
- focusX: .5,
- focusY: .5,
- aspect: 1.6,
- luma: .32,
- safeArea: "center",
- };
-
- const normalizeConfig = (value) => {
- const config = value && typeof value === "object" ? value : {};
- const art = config.art && typeof config.art === "object" ? config.art : {};
- const hasNumber = (candidate) =>
- (typeof candidate === "number" || (typeof candidate === "string" && candidate.trim() !== "")) &&
- Number.isFinite(Number(candidate));
- const requestedAccent = typeof config?.palette?.accent === "string"
- ? config.palette.accent.trim()
- : "";
- const safeAccent = /^(?:#[\da-f]{3,8}|(?:rgb|hsl|oklch|oklab)\([^;{}]{1,96}\))$/i.test(requestedAccent)
- ? requestedAccent
- : null;
- const appearance = ["auto", "light", "dark"].includes(config.appearance)
- ? config.appearance
- : "auto";
- const safeArea = ["auto", "left", "right", "center", "none"].includes(art.safeArea)
- ? art.safeArea
- : "auto";
- const taskMode = ["auto", "ambient", "banner", "off"].includes(art.taskMode)
- ? art.taskMode
- : "auto";
- const metadataRatio = Number(config?.artMetadata?.ratio);
- return {
- appearance,
- safeArea,
- taskMode,
- focusX: hasNumber(art.focusX) ? clamp(art.focusX) : null,
- focusY: hasNumber(art.focusY) ? clamp(art.focusY) : null,
- accent: safeAccent,
- initialAspect: Number.isFinite(metadataRatio) && metadataRatio > 0 ? metadataRatio : null,
- };
+ const existingAnalysisCache = window[ANALYSIS_CACHE_KEY];
+ const analysisCache = existingAnalysisCache && typeof existingAnalysisCache.get === "function" &&
+ typeof existingAnalysisCache.set === "function" ? existingAnalysisCache : new Map();
+ window[ANALYSIS_CACHE_KEY] = analysisCache;
+ let artAnalysis = typeof THEME.artKey === "string" ? analysisCache.get(THEME.artKey) ?? null : null;
+ let analysisTimer = null;
+ let rootObserver = null;
+ let partObserver = null;
+ let bodyReadyHandler = null;
+ let styleMode = null;
+ let styleNode = null;
+ let styleSheet = null;
+ const now = () => typeof performance === "object" && typeof performance.now === "function"
+ ? performance.now() : Date.now();
+ const metrics = {
+ ensureCalls: 0,
+ rootPasses: 0,
+ routePasses: 0,
+ layoutReads: 0,
+ attributeWrites: 0,
+ styleWrites: 0,
+ styleRepairs: 0,
+ partPasses: 0,
+ partWrites: 0,
+ navigationEvents: 0,
+ safetyPasses: 0,
+ analysisRuns: 0,
+ analysisCacheHits: artAnalysis ? 1 : 0,
+ firstEnsureMs: null,
+ analysisMs: null,
};
const previous = window[STATE_KEY];
- if (previous?.observer) previous.observer.disconnect();
- if (previous?.timer) clearInterval(previous.timer);
- if (previous?.scheduler?.timeout) clearTimeout(previous.scheduler.timeout);
- if (previous?.artUrl) URL.revokeObjectURL(previous.artUrl);
+ if (typeof previous?.cleanup === "function") previous.cleanup();
+ window[DISABLED_KEY] = false;
+
+ const existingStyleRegistry = window[STYLE_REGISTRY_KEY];
+ const styleRegistry = existingStyleRegistry instanceof Set ? existingStyleRegistry : new Set();
+ window[STYLE_REGISTRY_KEY] = styleRegistry;
const artUrl = (() => {
const comma = artDataUrl.indexOf(",");
+ const mime = /^data:([^;,]+)/.exec(artDataUrl)?.[1] || "image/png";
const binary = atob(artDataUrl.slice(comma + 1));
const bytes = new Uint8Array(binary.length);
for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);
- const mime = /^data:([^;,]+)/.exec(artDataUrl)?.[1] || "image/png";
return URL.createObjectURL(new Blob([bytes], { type: mime }));
})();
- const config = normalizeConfig(rawConfig);
- let profile = {
- ...defaultProfile,
- aspect: config.initialAspect ?? defaultProfile.aspect,
- };
- const existingStyle = document.getElementById(STYLE_ID);
- if (existingStyle) {
- existingStyle.textContent = cssText;
- existingStyle.dataset.dreamVersion = "3";
- }
+
+ const cssString = (value) => JSON.stringify(String(value ?? ""));
+
+ const setStyleProperty = (root, name, value) => {
+ if (root.style.getPropertyValue(name) !== value) {
+ root.style.setProperty(name, value);
+ metrics.styleWrites += 1;
+ }
+ };
+
+ const setAttribute = (root, name, value) => {
+ const normalized = String(value);
+ if (root.getAttribute(name) !== normalized) {
+ root.setAttribute(name, normalized);
+ metrics.attributeWrites += 1;
+ }
+ };
+
+ const parseRgb = (value) => {
+ if (!value || value === "transparent") return null;
+ const hex = String(value).trim().match(/^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i);
+ if (hex) {
+ const rgbHex = hex[1].length <= 4
+ ? hex[1].slice(0, 3).split("").map((digit) => `${digit}${digit}`).join("")
+ : hex[1].slice(0, 6);
+ const number = Number.parseInt(rgbHex, 16);
+ return { r: number >> 16, g: (number >> 8) & 255, b: number & 255 };
+ }
+ const m = String(value).match(/rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)/i);
+ if (!m) return null;
+ return { r: Number(m[1]), g: Number(m[2]), b: Number(m[3]) };
+ };
+
+ const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
+
+ const rgbString = (value) => {
+ const rgb = parseRgb(value);
+ return rgb ? [rgb.r, rgb.g, rgb.b]
+ .map((channel) => Math.round(clamp(channel, 0, 255)))
+ .join(" ") : null;
+ };
+
+ const rgbToHex = ({ r, g, b }) => `#${[r, g, b]
+ .map((value) => clamp(Math.round(value), 0, 255).toString(16).padStart(2, "0"))
+ .join("")}`;
+
+ const rgbToHsl = ({ r, g, b }) => {
+ const values = [r, g, b].map((value) => value / 255);
+ const max = Math.max(...values);
+ const min = Math.min(...values);
+ const lightness = (max + min) / 2;
+ if (max === min) return { h: 0, s: 0, l: lightness };
+ const delta = max - min;
+ const saturation = lightness > 0.5 ? delta / (2 - max - min) : delta / (max + min);
+ let hue;
+ if (max === values[0]) hue = (values[1] - values[2]) / delta + (values[1] < values[2] ? 6 : 0);
+ else if (max === values[1]) hue = (values[2] - values[0]) / delta + 2;
+ else hue = (values[0] - values[1]) / delta + 4;
+ return { h: hue * 60, s: saturation, l: lightness };
+ };
+
+ const hslToRgb = ({ h, s, l }) => {
+ const hue = ((h % 360) + 360) % 360 / 360;
+ if (s === 0) {
+ const neutral = Math.round(l * 255);
+ return { r: neutral, g: neutral, b: neutral };
+ }
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ const p = 2 * l - q;
+ const channel = (offset) => {
+ let t = hue + offset;
+ if (t < 0) t += 1;
+ if (t > 1) t -= 1;
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
+ if (t < 1 / 2) return q;
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
+ return p;
+ };
+ return { r: channel(1 / 3) * 255, g: channel(0) * 255, b: channel(-1 / 3) * 255 };
+ };
+
+ const detectShellAppearance = () => {
+ const root = document.documentElement;
+ if (root?.classList?.contains("electron-dark")) return "dark";
+ if (root?.classList?.contains("electron-light")) return "light";
+ try { return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; } catch {}
+ return "light";
+ };
+
+ const makeAdaptivePalette = (sample, shell) => {
+ const source = sample || { r: 108, g: 126, b: 136 };
+ const hsl = rgbToHsl(source);
+ const hue = hsl.s < 0.12 ? 214 : hsl.h;
+ const saturation = clamp(hsl.s, 0.38, 0.72);
+ const accent = hslToRgb({ h: hue, s: saturation, l: shell === "light" ? 0.42 : 0.66 });
+ const accentAlt = hslToRgb({ h: hue + 12, s: saturation * 0.82, l: shell === "light" ? 0.52 : 0.73 });
+ const secondary = hslToRgb({ h: hue - 24, s: saturation * 0.64, l: shell === "light" ? 0.56 : 0.62 });
+ const highlight = hslToRgb({ h: hue + 24, s: saturation * 0.76, l: shell === "light" ? 0.36 : 0.58 });
+ const neutral = (lightness, chroma = 0.08) => rgbToHex(hslToRgb({ h: hue, s: chroma, l: lightness }));
+ return shell === "light" ? {
+ background: neutral(0.965, 0.07),
+ panel: neutral(0.987, 0.035),
+ panelAlt: neutral(0.945, 0.09),
+ accent: rgbToHex(accent),
+ accentAlt: rgbToHex(accentAlt),
+ secondary: rgbToHex(secondary),
+ highlight: rgbToHex(highlight),
+ text: neutral(0.13, 0.10),
+ muted: neutral(0.42, 0.08),
+ line: `rgba(${Math.round(accent.r)}, ${Math.round(accent.g)}, ${Math.round(accent.b)}, .24)`,
+ } : {
+ background: neutral(0.055, 0.045),
+ panel: neutral(0.085, 0.04),
+ panelAlt: neutral(0.125, 0.05),
+ accent: rgbToHex(accent),
+ accentAlt: rgbToHex(accentAlt),
+ secondary: rgbToHex(secondary),
+ highlight: rgbToHex(highlight),
+ text: neutral(0.93, 0.025),
+ muted: neutral(0.69, 0.03),
+ line: `rgba(${Math.round(accent.r)}, ${Math.round(accent.g)}, ${Math.round(accent.b)}, .28)`,
+ };
+ };
+
+ const resolvedShell = () => {
+ if (THEME.appearance === "light" || THEME.appearance === "dark") return THEME.appearance;
+ // Image luminance may tune accents and scrims, but auto appearance follows
+ // Codex/ChatGPT (or the OS fallback) so a bright wallpaper cannot flip a
+ // native dark session back to a light shell after analysis.
+ return detectShellAppearance();
+ };
+
+ const applyTheme = (root, shell) => {
+ const declaredColors = THEME.colors && typeof THEME.colors === "object" ? THEME.colors : {};
+ const legacyPalette = THEME.palette && typeof THEME.palette === "object" ? THEME.palette : {};
+ // macOS themes use the full `colors` contract; older Windows themes used
+ // `palette.accent`. Accept both while keeping one renderer source.
+ const colors = Object.keys(declaredColors).length ? declaredColors : legacyPalette;
+ const hasExplicitKeyList = Array.isArray(THEME.explicitColorKeys);
+ const explicit = new Set(hasExplicitKeyList ? THEME.explicitColorKeys : []);
+ if (!hasExplicitKeyList && (THEME.colorMode === "explicit" || !Object.hasOwn(THEME, "colorMode"))) {
+ for (const key of Object.keys(declaredColors)) explicit.add(key);
+ }
+ if (typeof legacyPalette.accent === "string") explicit.add("accent");
+ const adaptive = makeAdaptivePalette(artAnalysis?.accentRgb, shell);
+ const legacyLight = (THEME.appearance === undefined || THEME.appearance === "auto")
+ && THEME.colorMode !== "explicit" && shell === "light";
+ const structural = new Set(["background", "panel", "panelAlt", "text", "muted"]);
+ const pick = (name) => {
+ const allowExplicit = explicit.has(name) && !(legacyLight && structural.has(name));
+ return allowExplicit && typeof colors[name] === "string" ? colors[name] : adaptive[name];
+ };
+ const accent = pick("accent");
+ const accentAlt = explicit.has("accentAlt") ? pick("accentAlt") : (explicit.has("accent") ? accent : adaptive.accentAlt);
+ const variables = {
+ "--ds-bg": pick("background"),
+ "--ds-panel": pick("panel"),
+ "--ds-panel-2": pick("panelAlt"),
+ "--ds-green": accent,
+ "--ds-lime": accentAlt,
+ "--ds-cyan": pick("secondary"),
+ "--ds-purple": pick("highlight"),
+ "--ds-text": pick("text"),
+ "--ds-muted": pick("muted"),
+ "--ds-line": explicit.has("line") && typeof colors.line === "string" ? colors.line : adaptive.line,
+ };
+
+ for (const [name, value] of Object.entries(variables)) {
+ if (typeof value === "string" && value) setStyleProperty(root, name, value);
+ }
+ const publicColors = {
+ "--ds-theme-color-background": variables["--ds-bg"],
+ "--ds-theme-color-panel": variables["--ds-panel"],
+ "--ds-theme-color-panel-alt": variables["--ds-panel-2"],
+ "--ds-theme-color-accent": variables["--ds-green"],
+ "--ds-theme-color-accent-alt": variables["--ds-lime"],
+ "--ds-theme-color-secondary": variables["--ds-cyan"],
+ "--ds-theme-color-highlight": variables["--ds-purple"],
+ "--ds-theme-color-text": variables["--ds-text"],
+ "--ds-theme-color-muted": variables["--ds-muted"],
+ "--ds-theme-color-line": variables["--ds-line"],
+ };
+ for (const [name, value] of Object.entries(publicColors)) {
+ if (typeof value === "string" && value) setStyleProperty(root, name, value);
+ }
+ setStyleProperty(root, "--ds-theme-surface-radius", "12px");
+ setStyleProperty(root, "--ds-theme-surface-opacity", "1");
+ setStyleProperty(root, "--ds-theme-surface-blur", "0px");
+ setStyleProperty(root, "--ds-theme-font-family", "system");
+ setStyleProperty(root, "--ds-theme-font-scale", "1");
+ setStyleProperty(root, "--ds-theme-surface-border-alpha", "0.14");
+ setStyleProperty(root, "--ds-theme-surface-shadow", "soft");
+ setStyleProperty(root, "--ds-theme-image-zoom", "1");
+ setStyleProperty(root, "--ds-theme-image-dim", "0");
+ setStyleProperty(root, "--ds-theme-image-task-intensity", "0.35");
+ setStyleProperty(root, "--ds-theme-density-scale", "standard");
+ setStyleProperty(root, "--ds-theme-motion-level", "standard");
+ const rgbVariables = {
+ "--ds-bg-rgb": variables["--ds-bg"],
+ "--ds-panel-rgb": variables["--ds-panel"],
+ "--ds-panel-2-rgb": variables["--ds-panel-2"],
+ "--ds-accent-rgb": variables["--ds-green"],
+ "--ds-accent-alt-rgb": variables["--ds-lime"],
+ "--ds-secondary-rgb": variables["--ds-cyan"],
+ "--ds-highlight-rgb": variables["--ds-purple"],
+ "--ds-text-rgb": variables["--ds-text"],
+ "--ds-muted-rgb": variables["--ds-muted"],
+ "--ds-line-rgb": variables["--ds-line"],
+ };
+ for (const [name, value] of Object.entries(rgbVariables)) {
+ const rgb = rgbString(value);
+ if (rgb) setStyleProperty(root, name, rgb);
+ }
+ setStyleProperty(root, "--dream-skin-name", cssString(THEME.name || "Codex Dream Skin"));
+ setStyleProperty(root, "--dream-skin-tagline", cssString(THEME.tagline || "Make something wonderful."));
+ setStyleProperty(root, "--dream-skin-quote", cssString(THEME.quote || "MAKE SOMETHING WONDERFUL"));
+ setStyleProperty(root, "--dream-skin-brand-subtitle", cssString(
+ THEME.brandSubtitle || "CODEX DREAM SKIN",
+ ));
+ setStyleProperty(root, "--dream-skin-status", cssString(THEME.statusText || "DREAM SKIN ONLINE"));
+ setStyleProperty(root, "--dream-skin-project-prefix", cssString(THEME.projectPrefix || "选择项目 · "));
+ setStyleProperty(root, "--dream-skin-project-label", cssString(THEME.projectLabel || "◉ 选择项目"));
+ };
+
+ const applyArtMetadata = (root) => {
+ const profile = artAnalysis || ART_METADATA;
+ const inferredSafe = profile?.safeArea || "center";
+ const safeArea = ART.safeArea && ART.safeArea !== "auto" ? ART.safeArea : inferredSafe;
+ const canonicalSafe = ["left", "right", "center", "none"].includes(safeArea)
+ ? safeArea : "center";
+ const focusX = typeof ART.focusX === "number" ? ART.focusX
+ : profile?.focusX ?? (safeArea === "left" ? 0.72 : safeArea === "right" ? 0.28 : 0.5);
+ const focusY = typeof ART.focusY === "number" ? ART.focusY : profile?.focusY ?? 0.5;
+ const taskMode = ART.taskMode && ART.taskMode !== "auto"
+ ? ART.taskMode : profile?.taskMode || "ambient";
+ const wide = profile?.wide || false;
+ const aspect = profile?.aspect || "unknown";
+ const focusXValue = `${(clamp(focusX, 0, 1) * 100).toFixed(2)}%`;
+ const focusYValue = `${(clamp(focusY, 0, 1) * 100).toFixed(2)}%`;
+
+ setAttribute(root, "data-dream-art-wide", wide ? "true" : "false");
+ setAttribute(root, "data-dream-art-safe", canonicalSafe);
+ setAttribute(root, "data-dream-task-mode", taskMode);
+ setAttribute(root, "data-dream-art-safe-area", safeArea);
+ setAttribute(root, "data-dream-art-task-mode", taskMode);
+ setAttribute(root, "data-dream-art-aspect", aspect);
+ setAttribute(root, "data-dream-art-ready", artAnalysis ? "true" : "false");
+ setStyleProperty(root, "--dream-art-focus-x", focusXValue);
+ setStyleProperty(root, "--dream-art-focus-y", focusYValue);
+ setStyleProperty(root, "--dream-art-position", `${focusXValue} ${focusYValue}`);
+ setStyleProperty(root, "--dream-skin-focus-x", focusXValue);
+ setStyleProperty(root, "--dream-skin-focus-y", focusYValue);
+ setStyleProperty(root, "--dream-skin-art-position", `${focusXValue} ${focusYValue}`);
+ setStyleProperty(root, "--ds-theme-image-focus-x", String(Number(focusX.toFixed(4))));
+ setStyleProperty(root, "--ds-theme-image-focus-y", String(Number(focusY.toFixed(4))));
+ };
const analyzeArt = () => new Promise((resolve) => {
- if (typeof Image !== "function") {
- resolve(defaultProfile);
+ const startedAt = now();
+ metrics.analysisRuns += 1;
+ if (typeof window.Image !== "function" || !document?.createElement) {
+ metrics.analysisMs = Number((now() - startedAt).toFixed(3));
+ resolve(null);
return;
}
- const image = new Image();
+ const image = new window.Image();
+ let settled = false;
+ const finish = (value) => {
+ if (settled) return;
+ settled = true;
+ if (analysisTimer) clearTimeout(analysisTimer);
+ analysisTimer = null;
+ metrics.analysisMs = Number((now() - startedAt).toFixed(3));
+ resolve(value);
+ };
+ analysisTimer = setTimeout(() => finish(null), 6000);
+ image.onerror = () => finish(null);
image.onload = () => {
try {
- const width = 48;
- const height = Math.max(12, Math.round(width * image.naturalHeight / image.naturalWidth));
+ const ratio = image.naturalWidth / image.naturalHeight;
+ if (!Number.isFinite(ratio) || ratio <= 0) throw new Error("Invalid image dimensions");
+ const maxDimension = 96;
+ const width = Math.max(16, Math.round(ratio >= 1 ? maxDimension : maxDimension * ratio));
+ const height = Math.max(16, Math.round(ratio >= 1 ? maxDimension / ratio : maxDimension));
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
const context = canvas.getContext?.("2d", { willReadFrequently: true });
if (!context) throw new Error("Canvas is unavailable");
context.drawImage(image, 0, 0, width, height);
- const pixels = context.getImageData(0, 0, width, height).data;
+ const data = context.getImageData(0, 0, width, height).data;
+ const samples = new Array(width * height);
+ const bins = Array.from({ length: 24 }, () => ({ weight: 0, r: 0, g: 0, b: 0 }));
+ let lightTotal = 0;
let count = 0;
- let totalRed = 0;
- let totalGreen = 0;
- let totalBlue = 0;
- let totalBrightness = 0;
- const samples = [];
- const sampleMap = new Array(width * height);
- for (let offset = 0; offset < pixels.length; offset += 4) {
- if (pixels[offset + 3] < 96) continue;
- const red = pixels[offset];
- const green = pixels[offset + 1];
- const blue = pixels[offset + 2];
- const light = (.2126 * red + .7152 * green + .0722 * blue) / 255;
- const sample = { red, green, blue, light, index: offset / 4 };
- samples.push(sample);
- sampleMap[sample.index] = sample;
- totalRed += red;
- totalGreen += green;
- totalBlue += blue;
- totalBrightness += light;
- count += 1;
+
+ for (let y = 0; y < height; y += 1) {
+ for (let x = 0; x < width; x += 1) {
+ const offset = (y * width + x) * 4;
+ if (data[offset + 3] < 32) continue;
+ const rgb = { r: data[offset], g: data[offset + 1], b: data[offset + 2] };
+ const light = (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b) / 255;
+ const hsl = rgbToHsl(rgb);
+ samples[y * width + x] = { light, saturation: hsl.s };
+ lightTotal += light;
+ count += 1;
+ if (hsl.s >= 0.16 && hsl.l >= 0.16 && hsl.l <= 0.86) {
+ const bin = bins[Math.min(23, Math.floor(hsl.h / 15))];
+ const weight = hsl.s * (1 - Math.abs(hsl.l - 0.52) * 0.85);
+ bin.weight += weight;
+ bin.r += rgb.r * weight;
+ bin.g += rgb.g * weight;
+ bin.b += rgb.b * weight;
+ }
+ }
}
- if (!count) throw new Error("Image contains no opaque pixels");
- const average = [totalRed / count, totalGreen / count, totalBlue / count];
- const averageBrightness = totalBrightness / count;
+ if (!count) throw new Error("Image has no visible pixels");
+ const brightness = lightTotal / count;
const information = (start, end) => {
let total = 0;
let totalSquared = 0;
let edges = 0;
let edgeCount = 0;
- let sampleCount = 0;
+ let pixels = 0;
for (let y = 0; y < height; y += 1) {
for (let x = start; x < end; x += 1) {
- const sample = sampleMap[y * width + x];
+ const sample = samples[y * width + x];
if (!sample) continue;
total += sample.light;
totalSquared += sample.light * sample.light;
- sampleCount += 1;
- const previousSample = x > start ? sampleMap[y * width + x - 1] : null;
- const above = y > 0 ? sampleMap[(y - 1) * width + x] : null;
- if (previousSample) { edges += Math.abs(sample.light - previousSample.light); edgeCount += 1; }
+ pixels += 1;
+ const previous = x > start ? samples[y * width + x - 1] : null;
+ const above = y > 0 ? samples[(y - 1) * width + x] : null;
+ if (previous) { edges += Math.abs(sample.light - previous.light); edgeCount += 1; }
if (above) { edges += Math.abs(sample.light - above.light); edgeCount += 1; }
}
}
- const mean = sampleCount ? total / sampleCount : 0;
- const variance = sampleCount ? Math.max(0, totalSquared / sampleCount - mean * mean) : 1;
- return Math.sqrt(variance) * .58 + (edgeCount ? edges / edgeCount : 1) * .42;
+ const mean = pixels ? total / pixels : 0;
+ const variance = pixels ? Math.max(0, totalSquared / pixels - mean * mean) : 1;
+ return Math.sqrt(variance) * 0.58 + (edgeCount ? edges / edgeCount : 1) * 0.42;
};
- const zoneWidth = Math.max(1, Math.floor(width * .38));
+ const zoneWidth = Math.max(1, Math.floor(width * 0.38));
const leftInformation = information(0, zoneWidth);
const rightInformation = information(width - zoneWidth, width);
let safeArea = "center";
- if (leftInformation < rightInformation * .86) safeArea = "left";
- else if (rightInformation < leftInformation * .86) safeArea = "right";
- let focusWeight = 0;
- let focusX = 0;
- let focusY = 0;
- let accentWeight = 0;
- let accent = [0, 0, 0];
- for (const sample of samples) {
- const x = sample.index % width;
- const y = Math.floor(sample.index / width);
- const difference = Math.sqrt(
- (sample.red - average[0]) ** 2 +
- (sample.green - average[1]) ** 2 +
- (sample.blue - average[2]) ** 2,
- ) / 441.7;
- const saliency = .03 + difference ** 1.35;
- focusX += (x / Math.max(1, width - 1)) * saliency;
- focusY += (y / Math.max(1, height - 1)) * saliency;
- focusWeight += saliency;
- const max = Math.max(sample.red, sample.green, sample.blue);
- const min = Math.min(sample.red, sample.green, sample.blue);
- const saturation = max ? (max - min) / max : 0;
- const usableLight = 1 - Math.min(1, Math.abs(sample.light - .46) / .54);
- const weight = saturation ** 2 * (.15 + usableLight);
- accent[0] += sample.red * weight;
- accent[1] += sample.green * weight;
- accent[2] += sample.blue * weight;
- accentWeight += weight;
+ if (leftInformation < rightInformation * 0.86) safeArea = "left";
+ else if (rightInformation < leftInformation * 0.86) safeArea = "right";
+
+ let saliencyTotal = 0;
+ let saliencyX = 0;
+ let saliencyY = 0;
+ for (let y = 0; y < height; y += 1) {
+ for (let x = 0; x < width; x += 1) {
+ const sample = samples[y * width + x];
+ if (!sample) continue;
+ const previous = x > 0 ? samples[y * width + x - 1] : null;
+ const above = y > 0 ? samples[(y - 1) * width + x] : null;
+ const edge = (previous ? Math.abs(sample.light - previous.light) : 0) +
+ (above ? Math.abs(sample.light - above.light) : 0);
+ const weight = 0.01 + Math.abs(sample.light - brightness) * 0.48 +
+ sample.saturation * 0.34 + edge * 0.28;
+ saliencyTotal += weight;
+ saliencyX += (x + 0.5) / width * weight;
+ saliencyY += (y + 0.5) / height * weight;
+ }
}
- const resolvedAccent = accentWeight > 1
- ? accent.map((channel) => Math.round(channel / accentWeight))
- : average.map((channel) => Math.round(channel));
- let resolvedFocusX = clamp(focusX / focusWeight);
- if (safeArea === "left") resolvedFocusX = Math.max(.64, resolvedFocusX);
- if (safeArea === "right") resolvedFocusX = Math.min(.36, resolvedFocusX);
- resolve({
- appearance: averageBrightness >= .58 ? "light" : "dark",
- accent: resolvedAccent,
- focusX: resolvedFocusX,
- focusY: clamp(focusY / focusWeight),
- aspect: image.naturalWidth / Math.max(1, image.naturalHeight),
- luma: clamp(averageBrightness),
+ let focusX = saliencyTotal ? saliencyX / saliencyTotal : 0.5;
+ let focusY = saliencyTotal ? saliencyY / saliencyTotal : 0.5;
+ if (safeArea === "left") focusX = Math.max(0.64, focusX);
+ if (safeArea === "right") focusX = Math.min(0.36, focusX);
+ focusX = clamp(focusX, 0.12, 0.88);
+ focusY = clamp(focusY, 0.18, 0.82);
+
+ const accentBin = bins.reduce((best, candidate) => candidate.weight > best.weight ? candidate : best, bins[0]);
+ const accentRgb = accentBin.weight > 0 ? {
+ r: accentBin.r / accentBin.weight,
+ g: accentBin.g / accentBin.weight,
+ b: accentBin.b / accentBin.weight,
+ } : null;
+ const aspect = ratio >= 2.25 ? "ultrawide" : ratio >= 1.45 ? "wide"
+ : ratio >= 1.08 ? "landscape" : ratio >= 0.9 ? "square" : "portrait";
+ finish({
+ width: image.naturalWidth,
+ height: image.naturalHeight,
+ ratio,
+ wide: ratio >= 1.75,
+ aspect,
+ brightness,
+ shell: brightness >= 0.58 ? "light" : "dark",
safeArea,
+ focusX,
+ focusY,
+ taskMode: ratio >= 2.25 ? "banner" : "ambient",
+ accentRgb,
});
} catch {
- resolve(defaultProfile);
+ finish(null);
}
};
- image.onerror = () => resolve(defaultProfile);
image.src = artUrl;
});
- const detectShellAppearance = () => {
- const root = document.documentElement;
- const body = document.body;
- const classes = `${root?.className || ""} ${body?.className || ""}`
- .toLowerCase()
- .replace(/\bdream-theme-(?:dark|light)\b/g, "");
- if (/\b(dark|electron-dark|theme-dark|appearance-dark)\b/.test(classes)) return "dark";
- if (/\b(light|electron-light|theme-light|appearance-light)\b/.test(classes)) return "light";
-
- const dataTheme = (
- root?.getAttribute?.("data-theme") ||
- root?.getAttribute?.("data-appearance") ||
- root?.getAttribute?.("data-color-mode") ||
- body?.getAttribute?.("data-theme") ||
- body?.getAttribute?.("data-appearance") ||
- ""
- ).toLowerCase();
- if (dataTheme.includes("dark")) return "dark";
- if (dataTheme.includes("light")) return "light";
-
+ const installStyle = () => {
try {
- const hadSkin = root?.classList?.contains?.("codex-dream-skin");
- const savedSkinClasses = hadSkin
- ? ROOT_CLASSES.filter((className) => root.classList.contains(className))
- : [];
- samplingNativeShell = true;
- if (hadSkin) root.classList.remove(...ROOT_CLASSES);
- try {
- const colorScheme = getComputedStyle(root).colorScheme || "";
- if (colorScheme.includes("dark") && !colorScheme.includes("light")) return "dark";
- if (colorScheme.includes("light") && !colorScheme.includes("dark")) return "light";
- } finally {
- if (hadSkin) root.classList.add(...savedSkinClasses);
- observer?.takeRecords?.();
- samplingNativeShell = false;
+ if (!("adoptedStyleSheets" in document) || typeof CSSStyleSheet !== "function") {
+ throw new Error("Constructable stylesheets are unavailable");
}
+ const sheet = new CSSStyleSheet();
+ if (typeof sheet.replaceSync !== "function") throw new Error("replaceSync is unavailable");
+ sheet.replaceSync(cssText);
+ const retained = [...document.adoptedStyleSheets]
+ .filter((candidate) => !styleRegistry.has(candidate));
+ document.adoptedStyleSheets = [...retained, sheet];
+ styleRegistry.clear();
+ styleRegistry.add(sheet);
+ document.getElementById(STYLE_ID)?.remove();
+ styleSheet = sheet;
+ styleMode = "adopted";
+ return;
} catch {
- samplingNativeShell = false;
+ styleSheet = null;
}
- try {
- return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
- } catch {}
- return "light";
- };
- const clearAuxiliaryPanelClasses = () => {
- for (const candidate of document.querySelectorAll(`.${AUX_PANEL_LAYER_CLASS}`)) {
- candidate.classList.remove(...AUX_PANEL_CLASSES);
- }
+ styleNode = document.getElementById(STYLE_ID) || document.createElement("style");
+ styleNode.id = STYLE_ID;
+ styleNode.textContent = cssText;
+ if (!styleNode.parentElement) (document.head || document.documentElement).appendChild(styleNode);
+ styleMode = "style";
};
- const clearSkinDom = () => {
- const root = document.documentElement;
- root?.classList.remove(...ROOT_CLASSES);
- for (const property of ROOT_PROPERTIES) root?.style.removeProperty(property);
- document.querySelectorAll(".dream-home").forEach((node) => node.classList.remove("dream-home"));
- document.querySelectorAll(".dream-task").forEach((node) => node.classList.remove("dream-task"));
- document.querySelectorAll(".dream-home-shell").forEach((node) => node.classList.remove("dream-home-shell"));
- document.querySelectorAll(`.${HOME_UTILITY_CLASS}`).forEach((node) => node.classList.remove(HOME_UTILITY_CLASS));
- clearAuxiliaryPanelClasses();
- document.getElementById(STYLE_ID)?.remove();
- document.getElementById(CHROME_ID)?.remove();
- };
-
- const applyProfile = (root) => {
- const focusX = config.focusX ?? profile.focusX;
- const focusY = config.focusY ?? profile.focusY;
- const appearance = config.appearance === "auto" ? detectShellAppearance() : config.appearance;
- const focus = focusX < .4 ? "left" : focusX > .6 ? "right" : "center";
- const safeArea = config.safeArea === "auto" ? (profile.safeArea ||
- (focus === "left" ? "right" : focus === "right" ? "left" : "center")) : config.safeArea;
- const taskMode = config.taskMode === "auto"
- ? profile.aspect >= 2.25 ? "banner" : "ambient"
- : config.taskMode;
- const accent = config.accent || `rgb(${profile.accent.join(" ")})`;
- const accentInk = luminance(...profile.accent) > .42 ? "rgb(26 24 28)" : "rgb(250 248 251)";
- root.classList.toggle("dream-theme-light", appearance === "light");
- root.classList.toggle("dream-theme-dark", appearance === "dark");
- root.classList.toggle("dream-art-wide", profile.aspect >= 1.75);
- root.classList.toggle("dream-art-standard", profile.aspect < 1.75);
- for (const value of ["left", "center", "right"]) {
- root.classList.toggle(`dream-focus-${value}`, focus === value);
- }
- for (const value of ["left", "center", "right", "none"]) {
- root.classList.toggle(`dream-safe-${value}`, safeArea === value);
- }
- for (const value of ["ambient", "banner", "off"]) {
- root.classList.toggle(`dream-task-${value}`, taskMode === value);
- }
- root.style.setProperty("--dream-art", `url("${artUrl}")`);
- root.style.setProperty("--dream-art-position", `${Math.round(focusX * 100)}% ${Math.round(focusY * 100)}%`);
- root.style.setProperty("--dream-focus-x", String(focusX));
- root.style.setProperty("--dream-focus-y", String(focusY));
- root.style.setProperty("--dream-accent", accent);
- root.style.setProperty("--dream-accent-ink", accentInk);
- root.style.setProperty("--dream-image-luma", profile.luma.toFixed(3));
- };
-
- const reconcileAuxiliaryPanels = (shellMain) => {
- const shellRect = shellMain.getBoundingClientRect();
- const activeLayers = new Set();
-
- for (const tabs of document.querySelectorAll('[data-app-shell-tabs="true"]')) {
- const rect = tabs.getBoundingClientRect();
- if (rect.width < 1 || rect.height < 1) continue;
-
- const roleClass = rect.top >= shellRect.top + shellRect.height * .5
- && rect.width >= shellRect.width * .65
- ? AUX_PANEL_BOTTOM_CLASS
- : rect.left >= shellRect.left + shellRect.width * .45
- && rect.height >= shellRect.height * .45
- ? AUX_PANEL_RIGHT_CLASS
- : null;
- if (!roleClass) continue;
-
- for (let layer = tabs, depth = 0; layer && depth < 3; layer = layer.parentElement, depth += 1) {
- const layerRect = layer.getBoundingClientRect();
- if (Math.abs(layerRect.x - rect.x) > 3 || Math.abs(layerRect.y - rect.y) > 3
- || Math.abs(layerRect.width - rect.width) > 3 || Math.abs(layerRect.height - rect.height) > 3) break;
- layer.classList.add(AUX_PANEL_LAYER_CLASS, roleClass);
- activeLayers.add(layer);
+ const ensureStyle = () => {
+ if (styleMode === "adopted" && styleSheet) {
+ const current = [...document.adoptedStyleSheets];
+ if (!current.includes(styleSheet)) {
+ document.adoptedStyleSheets = [...current, styleSheet];
+ metrics.styleRepairs += 1;
}
+ return;
}
-
- for (const candidate of document.querySelectorAll(`.${AUX_PANEL_LAYER_CLASS}`)) {
- if (!activeLayers.has(candidate)) candidate.classList.remove(...AUX_PANEL_CLASSES);
+ if (styleNode && document.getElementById(STYLE_ID) !== styleNode) {
+ document.getElementById(STYLE_ID)?.remove();
+ (document.head || document.documentElement).appendChild(styleNode);
+ metrics.styleRepairs += 1;
}
};
- const ensure = () => {
- if (window.__CODEX_DREAM_SKIN_DISABLED__) return;
- const root = document.documentElement;
- if (!root || !document.body) return;
+ installStyle();
- const shellMain = document.querySelector("main.main-surface");
- if (!shellMain) {
- clearSkinDom();
- return;
- }
+ const applyRootState = (root) => {
+ metrics.rootPasses += 1;
+ ensureStyle();
+ const shell = resolvedShell();
+ setAttribute(root, "data-dream-skin", "active");
+ setAttribute(root, SHELL_ATTR, shell);
+ setStyleProperty(root, "--dream-skin-art", `url("${artUrl}")`);
+ applyTheme(root, shell);
+ applyArtMetadata(root);
+ return shell;
+ };
- root.classList.add("codex-dream-skin");
- applyProfile(root);
+ const selectorHit = (key) => {
+ const selector = selectorByKey.get(key)?.selector;
+ if (!selector) return false;
+ try { return Boolean(document.querySelector(selector)); } catch { return false; }
+ };
- let style = document.getElementById(STYLE_ID);
- if (!style) {
- style = document.createElement("style");
- style.id = STYLE_ID;
- (document.head || root).appendChild(style);
- }
- if (style.dataset.dreamVersion !== "3") {
- style.textContent = cssText;
- style.dataset.dreamVersion = "3";
+ const ensureShellMainCompatibility = () => {
+ const shellMain = document.querySelector("main.main-surface") || document.querySelector("main");
+ if (!shellMain) return null;
+ // Codex 26.727 moved this stable class behind CSS Modules. Preserve the
+ // DreamSkin selector contract while leaving Codex's own class untouched.
+ if (!shellMain.classList.contains("main-surface")) {
+ shellMain.classList.add("main-surface");
+ shellMain.setAttribute(MAIN_COMPAT_ATTR, "true");
}
+ return shellMain;
+ };
- const home = document.querySelector('[role="main"]:has([data-testid="home-icon"])');
- for (const candidate of document.querySelectorAll('[role="main"]')) {
- candidate.classList.toggle("dream-home", candidate === home);
- candidate.classList.toggle("dream-task", candidate !== home);
+ const stableTestidHit = (testid) => {
+ const selector = stableTestidSelector(testid);
+ if (!selector) return false;
+ try { return Boolean(document.querySelector(selector)); } catch { return false; }
+ };
+
+ const partNodes = new Set();
+ const queryAll = (selector) => {
+ if (!selector) return [];
+ try { return [...document.querySelectorAll(selector)]; } catch { return []; }
+ };
+ const selectorNodes = (key) => queryAll(selectorByKey.get(key)?.selector);
+ const addPart = (desired, part, nodes) => {
+ for (const node of nodes) {
+ if (node && typeof node.setAttribute === "function" && !desired.has(node)) {
+ desired.set(node, part);
+ }
}
- const utilityBars = new Set(home ? home.querySelectorAll('[class*="_homeUtilityBar_"]') : []);
- for (const candidate of document.querySelectorAll(`.${HOME_UTILITY_CLASS}`)) {
- if (!utilityBars.has(candidate)) candidate.classList.remove(HOME_UTILITY_CLASS);
+ };
+ const refreshParts = () => {
+ metrics.partPasses += 1;
+ const desired = new Map();
+ addPart(desired, "root", [document.documentElement]);
+ addPart(desired, "sidebar", selectorNodes("left-panel"));
+ addPart(desired, "main", selectorNodes("shell-main"));
+ addPart(desired, "header", selectorNodes("header-tint"));
+ addPart(desired, "home", selectorNodes("home-route"));
+ addPart(desired, "project-list", selectorNodes("project-selector"));
+ addPart(desired, "thread", selectorNodes("thread-surface"));
+ addPart(desired, "message", selectorNodes("message"));
+ addPart(desired, "composer", selectorNodes("composer-chrome"));
+ addPart(desired, "composer-toolbar", selectorNodes("composer-toolbar"));
+ addPart(desired, "dialog", selectorNodes("overlay-dialog"));
+ const homeHero = selectorNodes("home-icon")[0]?.parentElement;
+ addPart(desired, "home-hero", homeHero ? [homeHero] : []);
+
+ for (const node of partNodes) {
+ if (!desired.has(node)) {
+ node.removeAttribute?.(PART_ATTR);
+ metrics.partWrites += 1;
+ }
}
- for (const candidate of utilityBars) candidate.classList.add(HOME_UTILITY_CLASS);
- shellMain.classList.toggle("dream-home-shell", Boolean(home));
- reconcileAuxiliaryPanels(shellMain);
-
- let chrome = document.getElementById(CHROME_ID);
- if (!chrome || chrome.parentElement !== document.body) {
- chrome?.remove();
- chrome = document.createElement("div");
- chrome.id = CHROME_ID;
- chrome.setAttribute("aria-hidden", "true");
- document.body.appendChild(chrome);
+ partNodes.clear();
+ for (const [node, part] of desired) {
+ if (node.getAttribute?.(PART_ATTR) !== part) {
+ node.setAttribute(PART_ATTR, part);
+ metrics.partWrites += 1;
+ }
+ partNodes.add(node);
}
- chrome.classList.toggle("dream-home-shell", Boolean(home));
+ };
+
+ const removeParts = () => {
+ for (const node of partNodes) node.removeAttribute?.(PART_ATTR);
+ partNodes.clear();
+ for (const node of queryAll(`[${PART_ATTR}]`)) node.removeAttribute?.(PART_ATTR);
+ };
+
+ const scopeMatches = (scope, baseState, overlay) => {
+ const active = new Set([baseState]);
+ if (baseState !== "settings") active.add("all");
+ if (overlay) active.add("overlay");
+ const tokens = String(scope || "all").toLowerCase().match(/[a-z]+/g) || ["all"];
+ return tokens.some((token) => token !== "config" && active.has(token));
+ };
+
+ const detectScope = () => {
+ const overlay = selectorHit("overlay-menu") || selectorHit("overlay-dialog") ||
+ selectorHit("overlay-popper");
+ let baseState = "thread";
+ if (selectorHit("appearance-radio") || stableTestidHit("theme-preview")) baseState = "settings";
+ else if (selectorHit("home-icon") || selectorHit("home-route")) baseState = "home";
+ else if (!selectorHit("shell-main")) baseState = "settings";
+ const missingL1 = SELECTOR_CONTRACT.selectors
+ .filter((entry) => entry.tier === "L1" && entry.required &&
+ scopeMatches(entry.scope, baseState, overlay) && !selectorHit(entry.key))
+ .map((entry) => entry.key);
+ return {
+ state: overlay ? "overlay" : baseState,
+ baseState,
+ overlay,
+ // Settings replaces (or partially replaces) the app shell on macOS and
+ // can retain a shell on Windows. It is therefore always an L0 scope;
+ // never treat the absence of the home/thread L1 anchors as a failure.
+ level: baseState === "settings" || missingL1.length ? "L0" : "L1",
+ missingL1,
+ };
+ };
+
+ const refreshScope = () => {
+ metrics.routePasses += 1;
+ const scope = detectScope();
+ const state = window[STATE_KEY];
+ if (state?.installToken === installToken) state.scope = scope;
+ return scope;
+ };
+
+ const ensure = ({ root: rootPass = true, scope: scopePass = false, parts: partPass = false } = {}) => {
+ if (window[DISABLED_KEY]) return;
+ const root = document.documentElement;
+ if (!root) return;
+ metrics.ensureCalls += 1;
+ ensureShellMainCompatibility();
+ if (rootPass) applyRootState(root);
+ if (partPass) refreshParts();
+ if (scopePass) refreshScope();
};
const cleanup = () => {
const state = window[STATE_KEY];
if (state?.installToken !== installToken) return false;
- window.__CODEX_DREAM_SKIN_DISABLED__ = true;
- clearSkinDom();
- state?.observer?.disconnect();
+ window[DISABLED_KEY] = true;
+ const root = document.documentElement;
+ for (const name of ROOT_ATTRS) root?.removeAttribute(name);
+ for (const attribute of [...(root?.attributes || [])]) {
+ if (attribute.name.startsWith("data-dream-")) root.removeAttribute(attribute.name);
+ }
+ for (const name of THEME_VARIABLES) root?.style.removeProperty(name);
+ for (const property of [...(root?.style || [])]) {
+ if (property.startsWith("--dream-") || property.startsWith("--ds-")) {
+ root.style.removeProperty(property);
+ }
+ }
+ removeParts();
+ for (const node of document.querySelectorAll(`main[${MAIN_COMPAT_ATTR}="true"]`)) {
+ node.classList.remove("main-surface");
+ node.removeAttribute(MAIN_COMPAT_ATTR);
+ }
+ state?.rootObserver?.disconnect();
+ state?.partObserver?.disconnect();
+ if (bodyReadyHandler && typeof document.removeEventListener === "function") {
+ document.removeEventListener("DOMContentLoaded", bodyReadyHandler);
+ }
if (state?.timer) clearInterval(state.timer);
if (state?.scheduler?.timeout) clearTimeout(state.scheduler.timeout);
+ if (analysisTimer) clearTimeout(analysisTimer);
+ if (state?.mediaHandler && state?.mediaQuery) {
+ try { state.mediaQuery.removeEventListener("change", state.mediaHandler); } catch {}
+ }
+ if (state?.navigationHandler && state?.navigation) {
+ try { state.navigation.removeEventListener("navigate", state.navigationHandler); } catch {}
+ }
+ if (styleSheet) {
+ try {
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets]
+ .filter((candidate) => candidate !== styleSheet);
+ } catch {}
+ styleRegistry.delete(styleSheet);
+ }
+ styleNode?.remove();
+ if (document.getElementById(STYLE_ID) === styleNode) document.getElementById(STYLE_ID)?.remove();
+ if (styleRegistry.size === 0) delete window[STYLE_REGISTRY_KEY];
if (state?.artUrl) URL.revokeObjectURL(state.artUrl);
delete window[STATE_KEY];
return true;
};
- const scheduler = { timeout: null };
- const scheduleEnsure = () => {
+ const scheduler = { timeout: null, root: false, scope: false, parts: false };
+ const flushScheduledEnsure = () => {
if (scheduler.timeout) clearTimeout(scheduler.timeout);
- scheduler.timeout = setTimeout(() => {
- scheduler.timeout = null;
- ensure();
- }, 180);
- };
- observer = new MutationObserver(() => {
- if (samplingNativeShell) return;
- scheduleEnsure();
- });
- observer.observe(document.documentElement, {
- childList: true,
- subtree: true,
- attributes: true,
- attributeFilter: ["class", "data-theme", "data-appearance", "data-color-mode"],
- });
- const timer = setInterval(ensure, 5000);
+ scheduler.timeout = null;
+ const pending = { root: scheduler.root, scope: scheduler.scope, parts: scheduler.parts };
+ scheduler.root = false;
+ scheduler.scope = false;
+ scheduler.parts = false;
+ ensure(pending);
+ };
+ const scheduleEnsure = ({ root = false, scope = false, parts = false } = {}, delay = 64) => {
+ scheduler.root ||= root;
+ scheduler.scope ||= scope;
+ scheduler.parts ||= parts;
+ if (scheduler.timeout) return;
+ scheduler.timeout = setTimeout(flushScheduledEnsure, delay);
+ };
+ if (typeof MutationObserver === "function") {
+ rootObserver = new MutationObserver(() => scheduleEnsure({ root: true }));
+ partObserver = new MutationObserver(() => scheduleEnsure({ parts: true }, 80));
+ }
+
+ let mediaQuery = null;
+ let mediaHandler = null;
+ try {
+ mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
+ mediaHandler = () => scheduleEnsure({ root: true });
+ } catch {}
+
+ const navigationApi = window.navigation && typeof window.navigation.addEventListener === "function"
+ ? window.navigation : null;
+ const navigationHandler = navigationApi ? () => {
+ metrics.navigationEvents += 1;
+ scheduleEnsure({ scope: true, parts: true }, 180);
+ } : null;
+
window[STATE_KEY] = {
- ensure, cleanup, observer, timer, scheduler, artUrl, profile, config, installToken, version: "1.2.0",
+ ensure,
+ cleanup,
+ rootObserver,
+ partObserver,
+ timer: null,
+ scheduler,
+ mediaQuery,
+ mediaHandler,
+ navigation: navigationApi,
+ navigationHandler,
+ artUrl,
+ installToken,
+ styleMode,
+ styleNode,
+ styleSheet,
+ styleRevision: STYLE_REVISION,
+ analysis: artAnalysis,
+ artMetadata: ART_METADATA,
+ scope: null,
+ selectorsSchema: SELECTOR_CONTRACT.schema,
+ metrics,
+ version: VERSION,
+ themeId: THEME.id || "custom",
+ revision: PAYLOAD_REVISION,
+ detectShellAppearance,
+ };
+ const firstEnsureStartedAt = now();
+ ensure({ root: true, parts: true });
+ const initialScope = refreshScope();
+ metrics.firstEnsureMs = Number((now() - firstEnsureStartedAt).toFixed(3));
+
+ const observeAttributes = (node) => {
+ if (!rootObserver || !node) return;
+ rootObserver.observe(node, {
+ attributes: true,
+ attributeFilter: ["class", "data-theme", "data-appearance", "data-color-mode"],
+ });
+ };
+ const observePartTree = (node) => {
+ if (!partObserver || !node) return;
+ partObserver.observe(node, { childList: true, subtree: true });
+ };
+ observeAttributes(document.documentElement);
+ const observeBody = () => {
+ observeAttributes(document.body);
+ observePartTree(document.body);
};
- ensure();
- analyzeArt().then((result) => {
+ if (document.body) observeBody();
+ else if (typeof document.addEventListener === "function") {
+ bodyReadyHandler = () => {
+ if (!window[DISABLED_KEY]) {
+ observeBody();
+ scheduleEnsure({ parts: true }, 0);
+ }
+ };
+ document.addEventListener("DOMContentLoaded", bodyReadyHandler, { once: true });
+ }
+ const timer = setInterval(() => {
+ metrics.safetyPasses += 1;
+ ensure({ root: true });
+ }, 30000);
+ window[STATE_KEY].timer = timer;
+ if (mediaHandler && mediaQuery && typeof mediaQuery.addEventListener === "function") {
+ mediaQuery.addEventListener("change", mediaHandler);
+ }
+ if (navigationHandler && navigationApi) {
+ navigationApi.addEventListener("navigate", navigationHandler);
+ }
+ const analysisPromise = artAnalysis ? Promise.resolve(null) : analyzeArt();
+ window[STATE_KEY].analysisTimer = analysisTimer;
+ analysisPromise.then((analysis) => {
const state = window[STATE_KEY];
- if (state?.installToken !== installToken || window.__CODEX_DREAM_SKIN_DISABLED__) return;
- profile = result;
- state.profile = result;
- ensure();
- });
- return { installed: true, version: "1.2.0", adaptive: true };
-})(__DREAM_CSS_JSON__, __DREAM_ART_JSON__, __DREAM_THEME_JSON__)
+ if (!analysis || state?.installToken !== installToken || window[DISABLED_KEY]) return;
+ artAnalysis = analysis;
+ state.analysis = analysis;
+ if (typeof THEME.artKey === "string") {
+ analysisCache.set(THEME.artKey, analysis);
+ while (analysisCache.size > 8) analysisCache.delete(analysisCache.keys().next().value);
+ }
+ ensure({ root: true });
+ }).catch(() => {});
+ return {
+ installed: true,
+ version: VERSION,
+ themeId: THEME.id || "custom",
+ revision: PAYLOAD_REVISION,
+ shell: resolvedShell(),
+ scope: initialScope,
+ styleMode,
+ analysis: artAnalysis,
+ };
+})(__DREAM_SKIN_CSS_JSON__, __DREAM_SKIN_ART_JSON__, __DREAM_SKIN_THEME_JSON__)
diff --git a/crates/codex-plus-core/src/assets.rs b/crates/codex-plus-core/src/assets.rs
index daa739724..4bc058b8a 100644
--- a/crates/codex-plus-core/src/assets.rs
+++ b/crates/codex-plus-core/src/assets.rs
@@ -49,7 +49,7 @@ const STEPWISE_SCRIPT: &str = include_str!("../../../assets/inject/stepwise-inje
const SPONSOR_ALIPAY: &[u8] = include_bytes!("../../../assets/images/sponsor-alipay.jpg");
const SPONSOR_WECHAT: &[u8] = include_bytes!("../../../assets/images/sponsor-wechat.jpg");
pub const DIAGNOSTIC_BUILD_ID: &str = "diag-20260518-1";
-const DREAM_SKIN_RENDERER_REVISION: &str = "17";
+const DREAM_SKIN_RENDERER_REVISION: &str = "18";
pub fn renderer_script() -> &'static str {
RENDERER_SCRIPT
diff --git a/crates/codex-plus-core/src/dream_skin_runtime.rs b/crates/codex-plus-core/src/dream_skin_runtime.rs
index ebada7887..715f4ed00 100644
--- a/crates/codex-plus-core/src/dream_skin_runtime.rs
+++ b/crates/codex-plus-core/src/dream_skin_runtime.rs
@@ -406,27 +406,46 @@ pub fn renderer_verification_script() -> &'static str {
visible: rect.width > 0 && rect.height > 0 && style.display !== "none" && style.visibility !== "hidden",
};
};
+ const root = document.documentElement;
const homeSignal = document.querySelector('[data-testid="home-icon"]') ||
document.querySelector('[data-feature="game-source"]') ||
document.querySelector('.group\\/home-suggestions');
const homeRoute = homeSignal?.closest('[role="main"]') || null;
- const home = document.querySelector('[role="main"].dream-home, [role="main"].dream-skin-home, [role="main"].glass-vision-home');
+ const home = document.querySelector('[role="main"]:has([data-testid="home-icon"])') ||
+ homeRoute ||
+ document.querySelector('[role="main"].dream-home, [role="main"].dream-skin-home, [role="main"].glass-vision-home');
const suggestions = home?.querySelector('.group\\/home-suggestions') || null;
- const cards = suggestions ? [...suggestions.querySelectorAll('button')].map(box) : [];
+ const cardButtons = suggestions ? [...suggestions.querySelectorAll('button')] : [];
+ const cards = cardButtons.map(box);
const chrome = document.getElementById('codex-dream-skin-chrome') ||
document.getElementById('codex-glass-vision-skin-chrome');
+ const runtime = window.__CODEX_DREAM_SKIN_STATE__ || window.__CODEX_GLASS_VISION_SKIN_STATE__;
+ const modernInstalled = root?.getAttribute('data-dream-skin') === 'active';
+ const adopted = runtime?.styleMode === 'adopted' && runtime.styleSheet &&
+ [...document.adoptedStyleSheets].includes(runtime.styleSheet);
+ const fallbackStyle = runtime?.styleMode === 'style' &&
+ document.getElementById('codex-dream-skin-style') === runtime.styleNode;
+ const homeChildren = home?.children ? [...home.children] : [];
+ const bannerHolder = homeChildren.find((node) => node.querySelector?.('.home-banners'));
+ const siblingCandidates = homeChildren.filter((node) => node !== bannerHolder).map(box);
+ const heroChain = [];
+ for (let node = home?.firstElementChild || null; node && heroChain.length < 3;
+ node = node.firstElementChild) heroChain.push(node);
+ const heroCandidates = heroChain.map(box);
+ const hero = siblingCandidates.find((item) => item?.visible && item.width >= 280 && item.height >= 120) ||
+ [...heroCandidates].reverse().find((item) => item?.visible) ||
+ siblingCandidates.find((item) => item?.visible) || null;
return JSON.stringify({
- installed: document.documentElement.classList.contains('codex-dream-skin') ||
- document.documentElement.classList.contains('codex-glass-vision-skin'),
- version: window.__CODEX_DREAM_SKIN_STATE__?.version ||
- window.__CODEX_GLASS_VISION_SKIN_STATE__?.version || null,
- stylePresent: Boolean(document.getElementById('codex-dream-skin-style') ||
+ installed: modernInstalled || root?.classList.contains('codex-dream-skin') ||
+ root?.classList.contains('codex-glass-vision-skin'),
+ version: runtime?.version || null,
+ stylePresent: Boolean(adopted || fallbackStyle || document.getElementById('codex-dream-skin-style') ||
document.getElementById('codex-glass-vision-skin-style')),
- chromePresent: Boolean(chrome),
- chromePointerEvents: getComputedStyle(chrome || document.body).pointerEvents,
+ chromePresent: Boolean(chrome || modernInstalled),
+ chromePointerEvents: chrome ? getComputedStyle(chrome).pointerEvents : (modernInstalled ? "none" : null),
homeRoute: Boolean(homeRoute),
homePresent: Boolean(home),
- hero: box(home?.firstElementChild?.firstElementChild?.firstElementChild),
+ hero,
visibleCardCount: cards.filter((item) => item?.visible).length,
projectButton: box(home?.querySelector('.group\\/project-selector > button')),
composer: box(document.querySelector('.composer-surface-chrome')),
diff --git a/crates/codex-plus-core/src/settings.rs b/crates/codex-plus-core/src/settings.rs
index 4a2d54bb9..7e4a07a39 100644
--- a/crates/codex-plus-core/src/settings.rs
+++ b/crates/codex-plus-core/src/settings.rs
@@ -316,11 +316,7 @@ impl Default for DreamSkinThemeConfig {
project_label: default_dream_skin_project_label(),
status_text: default_dream_skin_status_text(),
quote: default_dream_skin_quote(),
- colors: if cfg!(windows) {
- None
- } else {
- Some(DreamSkinColors::default())
- },
+ colors: Some(DreamSkinColors::default()),
extra_fields,
}
}
diff --git a/crates/codex-plus-core/src/watcher.rs b/crates/codex-plus-core/src/watcher.rs
index 9ac74aa94..b3dda1c62 100644
--- a/crates/codex-plus-core/src/watcher.rs
+++ b/crates/codex-plus-core/src/watcher.rs
@@ -132,7 +132,9 @@ pub fn filter_killable_launcher_processes<'a>(
processes
.into_iter()
.filter(|(process_id, _, exe_file)| {
- !protected.contains(process_id) && exe_file.eq_ignore_ascii_case("codex-plus-plus.exe")
+ !protected.contains(process_id)
+ && (exe_file.eq_ignore_ascii_case("codex-plus-plus.exe")
+ || exe_file.eq_ignore_ascii_case("codex-plus.exe"))
})
.map(|(process_id, _, _)| process_id)
.collect()
diff --git a/crates/codex-plus-core/tests/cdp_bridge.rs b/crates/codex-plus-core/tests/cdp_bridge.rs
index d988f23f5..2ac7ebddb 100644
--- a/crates/codex-plus-core/tests/cdp_bridge.rs
+++ b/crates/codex-plus-core/tests/cdp_bridge.rs
@@ -536,11 +536,13 @@ fn injection_script_installs_dream_skin_from_backend_settings() {
assert!(script.contains("state.observer?.disconnect?.()"));
assert!(script.contains("window.__CODEX_PLUS_DREAM_SKIN_PAYLOAD_SIGNATURE__"));
assert!(script.contains("window.__CODEX_PLUS_DREAM_SKIN_THEME__"));
+ assert!(script.contains("\"colors\""));
assert!(script.contains("data:image/webp;base64,UklGRg=="));
assert!(script.contains("codex-dream-skin-companion"));
assert!(script.contains("removeDreamSkinCompanion"));
if cfg!(windows) {
- assert!(script.contains(":root.codex-dream-skin"));
+ assert!(script.contains("data-dream-skin"));
+ assert!(script.contains("data-dream-shell"));
assert!(!script.contains("薛凯琪专属定制皮肤"));
}
assert!(script.contains(".group\\\\/home-suggestions"));
diff --git a/crates/codex-plus-core/tests/dream_skin.rs b/crates/codex-plus-core/tests/dream_skin.rs
index 0356d1c63..dbc708bc7 100644
--- a/crates/codex-plus-core/tests/dream_skin.rs
+++ b/crates/codex-plus-core/tests/dream_skin.rs
@@ -15,7 +15,7 @@ fn backend_settings_defaults_to_upstream_platform_theme_config() {
assert_eq!(theme.id, "preset-arina-hashimoto");
assert_eq!(theme.name, "桥本有菜");
assert_eq!(theme.tagline, "把柔光与玫瑰带进今天的工作台。");
- assert!(theme.colors.is_none());
+ assert_eq!(theme.colors.as_ref().unwrap().accent, "#E25563");
assert_eq!(theme.extra_fields["appearance"], "auto");
assert_eq!(theme.extra_fields["art"]["safeArea"], "left");
} else {
diff --git a/crates/codex-plus-core/tests/dream_skin_runtime.rs b/crates/codex-plus-core/tests/dream_skin_runtime.rs
index 3e871e187..68d02260f 100644
--- a/crates/codex-plus-core/tests/dream_skin_runtime.rs
+++ b/crates/codex-plus-core/tests/dream_skin_runtime.rs
@@ -118,6 +118,28 @@ fn verification_accepts_target_project_live_contract() {
assert!(result.pass);
}
+#[test]
+fn verification_accepts_modern_runtime_without_legacy_chrome() {
+ let result = parse_renderer_verification(serde_json::json!({
+ "installed": true,
+ "version": "codex-plus:windows:dream-skin:r18",
+ "stylePresent": true,
+ "chromePresent": true,
+ "chromePointerEvents": "none",
+ "homeRoute": false,
+ "homePresent": false,
+ "visibleCardCount": 0,
+ "projectButton": null,
+ "composer": { "visible": true },
+ "sidebar": { "visible": true },
+ "documentOverflow": { "x": false, "y": false }
+ }))
+ .unwrap();
+
+ assert_eq!(result.state, DreamSkinState::Pass);
+ assert!(result.pass);
+}
+
#[test]
fn windows_identity_requires_a_path_inside_the_registered_package_root() {
let root = std::path::Path::new(
diff --git a/crates/codex-plus-core/tests/upstream_theme_assets.rs b/crates/codex-plus-core/tests/upstream_theme_assets.rs
index 4ffa044e2..060f1690e 100644
--- a/crates/codex-plus-core/tests/upstream_theme_assets.rs
+++ b/crates/codex-plus-core/tests/upstream_theme_assets.rs
@@ -19,19 +19,27 @@ fn bundled_target_renderers_and_styles_remain_byte_exact() {
for (path, hash) in [
(
"assets/inject/upstream/dream-skin/windows/renderer-inject.js",
- "74D3BFB0F0F55C138EE3B0933F7B55BC11F71D09F1B07794BCA51B6598DE203D",
+ "DB48AE78497EB9C1EB800F32C110778490A3913EDD969429697DC3EBD292DE97",
),
(
"assets/inject/upstream/dream-skin/windows/dream-skin.css",
- "12848DA7DDAACF1B0F18CD419B2B27A6355DCBCE01AF650F1BCE14D99FEBD532",
+ "AFF9433B526D7149DA99B7C07367B75BCA558F9D8114243331FB9099B54E2916",
+ ),
+ (
+ "assets/inject/upstream/dream-skin/windows/theme.json",
+ "9068F781F190D213FE5BE180A12AB1ED534FAFDD0BFBA00276C951A178DFE72A",
),
(
"assets/inject/upstream/dream-skin/macos/renderer-inject.js",
- "2704C39506C66554C3529BF0D15B876B4AEC2DD9A36B1796AB43E19C33A046FC",
+ "DB48AE78497EB9C1EB800F32C110778490A3913EDD969429697DC3EBD292DE97",
),
(
"assets/inject/upstream/dream-skin/macos/dream-skin.css",
- "EC3C3BC5F6E10E20A3F2307796BD1E1350E80E5D23D37318EE5468833C95A6DF",
+ "AFF9433B526D7149DA99B7C07367B75BCA558F9D8114243331FB9099B54E2916",
+ ),
+ (
+ "assets/inject/upstream/dream-skin/macos/theme.json",
+ "FCCE3F314500BE1A58381FFE7F9A6B212912D54FF9A0E7D17B6664C1516F750E",
),
(
"assets/inject/upstream/cidala-tiger/windows/renderer-inject.js",
diff --git a/crates/codex-plus-core/tests/watcher.rs b/crates/codex-plus-core/tests/watcher.rs
index 03feecd24..6c547c4de 100644
--- a/crates/codex-plus-core/tests/watcher.rs
+++ b/crates/codex-plus-core/tests/watcher.rs
@@ -120,10 +120,11 @@ fn launcher_process_filter_protects_current_process_ancestry() {
(20, 10, "codex-plus-plus.exe"),
(30, 20, "codex-plus-plus.exe"),
(40, 10, "codex-plus-plus.exe"),
- (50, 10, "codex-plus-plus-manager.exe"),
+ (50, 10, "codex-plus.exe"),
+ (60, 10, "codex-plus-plus-manager.exe"),
];
- assert_eq!(filter_killable_launcher_processes(processes, 30), vec![40]);
+ assert_eq!(filter_killable_launcher_processes(processes, 30), vec![40, 50]);
}
#[test]