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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 39 additions & 2 deletions apps/codex-plus-manager/src/dream-skin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ describe("dream skin theme helpers", () => {
assert.match(renderer, /\.composer-surface-chrome/);
assert.match(renderer, /data:image\/(?:png|jpeg|webp|gif);base64/);
assert.match(renderer, /removeDreamSkinCompanion/);
assert.match(renderer, /ensureDreamSkinCompanion\(\s*window\.__CODEX_PLUS_DREAM_SKIN_THEME__/);
assert.match(renderer, /const theme = window\.__CODEX_PLUS_DREAM_SKIN_THEME__/);
assert.match(renderer, /ensureDreamSkinCompanion\(theme\)/);
});

it("aligns tall companion images by rendered height with a wider vertical offset range", async () => {
Expand Down Expand Up @@ -138,7 +139,31 @@ describe("dream skin theme helpers", () => {
assert.match(compatibility, /shellMain\.classList\.add\("main-surface"\)/);
assert.match(compatibility, /data-codex-plus-dream-skin-main-surface/);
assert.match(compatibility, /clearDreamSkinMainSurfaceCompatibility\(\)/);
assert.match(assets, /DREAM_SKIN_RENDERER_REVISION: &str = "20-modern-main-surface"/);
assert.match(assets, /DREAM_SKIN_RENDERER_REVISION: &str = "28-codex-plus-side-panel-layout"/);
});

it("bridges the modern Codex Skin API without forcing visual properties", async () => {
const assets = await readFile(
new URL("../../../crates/codex-plus-core/src/assets.rs", import.meta.url),
"utf8",
);

assert.match(assets, /--ds-theme-font-family/);
assert.match(assets, /--ds-theme-surface-radius/);
assert.match(assets, /--ds-theme-surface-opacity/);
assert.match(assets, /--ds-theme-surface-blur/);
assert.match(assets, /dream-skin\/themes/);
assert.match(assets, /join\("theme\.css"\)/);
assert.match(assets, /threadViewport/);
assert.match(assets, /const threadSurface = threadViewport \|\| threadScroll/);
assert.match(assets, /data-ds-thread-scroll/);
assert.match(assets, /data-app-shell-main-surface/);
assert.match(assets, /data-local-conversation-user-anchor/);
assert.match(assets, /_ComposerLayoutRoot_/);
assert.match(assets, /composer-toolbar-empty/);
assert.match(assets, /desiredParts = new Map/);
assert.match(assets, /desiredThreadSurfaces = new Set/);
assert.doesNotMatch(assets, /DREAM_MODERN_CODEX_COMPAT_CSS/);
});

it("extends the Windows wallpaper treatment to right and bottom dock panels", async () => {
Expand All @@ -162,6 +187,18 @@ describe("dream skin theme helpers", () => {
assert.match(css, /\[data-codex-terminal="true"\]/);
});

it("themes the modern Codex right side panel surface", async () => {
const css = await readFile(
new URL("../../../assets/inject/upstream/dream-skin/macos/dream-skin.css", import.meta.url),
"utf8",
);

assert.match(css, /\[data-app-shell-tabs="true"\]/);
assert.match(css, /\[data-browser-sidebar-webview-host-root\]/);
assert.match(css, /\[data-browser-sidebar-webview\]/);
assert.match(css, /--app-shell-panel-background/);
});

it("keeps transient new-chat drafts on native geometry", async () => {
const renderer = await readFile(
new URL("../../../assets/inject/upstream/dream-skin/windows/renderer-inject.js", import.meta.url),
Expand Down
161 changes: 161 additions & 0 deletions apps/codex-plus-manager/src/renderer-inject.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,132 @@ function installRendererStyle(renderer: string) {
return appended;
}

class DockStyle {
private readonly values = new Map<string, { value: string; priority: string }>();

setProperty(property: string, value: string, priority = "") {
this.values.set(property, { value, priority });
}

getPropertyValue(property: string) {
return this.values.get(property)?.value ?? "";
}

getPropertyPriority(property: string) {
return this.values.get(property)?.priority ?? "";
}

removeProperty(property: string) {
this.values.delete(property);
}
}

class DockElement {
readonly children: DockElement[] = [];
readonly style = new DockStyle();
parentElement: DockElement | null = null;
closestElement: DockElement | null = null;
querySelectorResult: DockElement | null = null;
private readonly attributes = new Map<string, string>();
readonly name: string;

constructor(name: string) {
this.name = name;
}

get nextSibling(): DockElement | null {
if (!this.parentElement) return null;
const index = this.parentElement.children.indexOf(this);
return index >= 0 ? this.parentElement.children[index + 1] ?? null : null;
}

appendChild(child: DockElement) {
if (child.parentElement) {
const index = child.parentElement.children.indexOf(child);
if (index >= 0) child.parentElement.children.splice(index, 1);
}
child.parentElement = this;
this.children.push(child);
return child;
}

querySelector() {
return this.querySelectorResult;
}

closest() {
return this.closestElement;
}

getAttribute(name: string) {
return this.attributes.get(name) ?? null;
}

setAttribute(name: string, value: string) {
this.attributes.set(name, value);
}

removeAttribute(name: string) {
this.attributes.delete(name);
}
}

function composerDockRuntime(renderer: string) {
const start = renderer.indexOf(" function visibleDreamSkinComposer()");
const end = renderer.indexOf("\n function ensureDreamSkinCompanion", start);
assert.ok(start >= 0 && end > start, "composer docking block not found in renderer-inject.js");
const source = renderer.slice(start, end);

const main = new DockElement("main");
const layout = new DockElement("thread-viewport");
const scroll = new DockElement("thread-scroll");
const footer = new DockElement("thread-footer");
main.appendChild(layout);
layout.appendChild(scroll);
scroll.appendChild(footer);
scroll.closestElement = layout;
main.querySelectorResult = scroll;
scroll.querySelectorResult = footer;

const document = {
querySelector(selector: string) {
return selector.startsWith("main.main-surface") ? main : null;
},
querySelectorAll() {
return [];
},
};
const factory = new Function(
"document",
"setCodexPlusInlineStyleIfChanged",
"setCodexPlusAttributeIfChanged",
`${source}\nreturn { syncDreamSkinComposerDocking };`,
) as (
documentValue: typeof document,
setStyle: (element: DockElement, property: string, value: string, priority?: string) => void,
setAttribute: (element: DockElement, name: string, value: string) => void,
) => {
syncDreamSkinComposerDocking: (enabled: boolean) => void;
};
return {
...factory(
document,
(element, property, value, priority = "") => {
if (element.style.getPropertyValue(property) !== value
|| element.style.getPropertyPriority(property) !== priority) {
element.style.setProperty(property, value, priority);
}
},
(element, name, value) => {
if (element.getAttribute(name) !== value) element.setAttribute(name, value);
},
),
footer,
main,
layout,
};
}

describe("renderer injection header compatibility", () => {
it("adds the session copy shortcut through the native fork action", async () => {
const renderer = await readFile(new URL("../../../assets/inject/renderer-inject.js", import.meta.url), "utf8");
Expand Down Expand Up @@ -255,6 +381,41 @@ describe("renderer injection header compatibility", () => {
assert.match(renderer, /ensureShellMain/);
}
});

it("keeps modern Dream Skin composers out of reverse thread scrolling", async () => {
const renderer = await readFile(new URL("../../../assets/inject/renderer-inject.js", import.meta.url), "utf8");

assert.match(renderer, /data-thread-scroll-footer/);
assert.match(renderer, /_ComposerLayoutRoot_/);
assert.match(renderer, /syncDreamSkinComposerDocking/);
assert.match(renderer, /data-codex-plus-dreamskin-composer-docked/);
assert.match(renderer, /setCodexPlusInlineStyleIfChanged\(footer, property, value, "important"\)/);
});

it("anchors a docked composer footer to the resizable modern thread viewport", async () => {
const renderer = await readFile(new URL("../../../assets/inject/renderer-inject.js", import.meta.url), "utf8");
const runtime = composerDockRuntime(renderer);

runtime.syncDreamSkinComposerDocking(true);

assert.equal(runtime.footer.parentElement, runtime.layout);
assert.notEqual(runtime.footer.parentElement, runtime.main);
assert.equal(runtime.footer.style.getPropertyValue("left"), "0");
assert.equal(runtime.footer.style.getPropertyValue("right"), "0");
});

it("coalesces renderer work and keeps layout writes idempotent", async () => {
const renderer = await readFile(new URL("../../../assets/inject/renderer-inject.js", import.meta.url), "utf8");

assert.match(renderer, /let scanDeferredFrame = 0/);
assert.match(renderer, /if \(!wasRunning \|\| previousContentEl !== conversationViewState\.contentEl/);
assert.match(renderer, /function setCodexPlusInlineStyleIfChanged\(/);
assert.match(renderer, /function setCodexPlusDatasetIfChanged\(/);
assert.match(renderer, /codexServiceTierBadgeRetryAt/);
assert.match(renderer, /scheduleThreadScrollSync\(\);/);
assert.match(renderer, /ownedOffsets: new WeakMap\(\)/);
assert.match(renderer, /Number\.parseFloat\(owned\.left\)/);
});
});

/** 从注入脚本里取出 `shouldScheduleScan`,配上可控的依赖来跑。 */
Expand Down
Loading