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
11 changes: 11 additions & 0 deletions windows/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Windows Changelog

## Unreleased

### 改进

- 桌面宠物的精确 `/avatar-overlay` 辅助窗口现在可跟随活动主题显示柔和强调色光晕;默认从主题显式 `palette.accent` 或本地图像分析结果取色,主题热更新时同步刷新,并始终保持窗口透明与官方宠物交互不变。
- 主题可用 `pet.mode: accent | off` 控制联动,并用 `pet.glow`(`0..1`)调整光晕强度;缺省值分别为 `accent` 和 `0.45`。

### 测试

- 增加宠物目标 URL 白名单、主题联动、关闭模式、清理和非宠物辅助窗口隔离回归测试。

## 1.2.0 — 2026-07-17

### 新增
Expand Down
5 changes: 3 additions & 2 deletions windows/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Apply a reversible renderer skin through Chromium DevTools Protocol while launch
## Guardrails

- Preserve the official executable, package signature, user threads, pets, plugins, and authentication state.
- Pet theming is limited to a non-interactive accent glow on the exact `app://codex/avatar-overlay` renderer. Keep the auxiliary window transparent, do not replace official pet assets or animation, and use `pet.mode: off` to disable the effect.
- Theme images must be UI-free wallpapers. Never import a README screenshot, fake window, sidebar, card, composer, logo, or text baked into the bitmap.
- Paint one continuous 16:9 wallpaper across the full Codex window. Let the sidebar, main area, header, and composer act as coordinated readability layers; keep the home route expressive and task routes quieter.
- `appearance: auto` follows the native computed `color-scheme` first and the system appearance only as a fallback. Image brightness may tune color and composition, but must not flip the user's shell mode; explicit `light` and `dark` remain authoritative.
Expand All @@ -29,7 +30,7 @@ Apply a reversible renderer skin through Chromium DevTools Protocol while launch
- The watcher registers a generation-checked early payload for connected renderers so reload/navigation can paint the skin before the normal load-event fallback; unsupported CDP targets fall back safely.
- The active theme, saved themes, imported images, pause marker, and tray controls live under `%LOCALAPPDATA%\CodexDreamSkin`. Reject empty or over-16 MB images before copying or encoding them.
- Every managed-store write rejects junctions and other reparse points in every existing path component. Imports also use the bundled Node metadata parser before copying to reject dimensions above 16384px or 50MP.
- CDP targets must use a same-port loopback WebSocket, belong to the current Store package, retain the launch-time Browser ID, and expose expected Codex renderer markers.
- CDP targets must use a same-port loopback WebSocket, belong to the current Store package, retain the launch-time Browser ID, and expose either the expected Codex shell markers or the exact pet-overlay URL and structure.
- Loopback prevents LAN exposure, but Chromium CDP has no same-user authentication. Run only trusted local software while the skin is active, and use restore to close the debug session when it is no longer needed.
- Preserve `config.toml` as strict UTF-8. Never use encoding-dependent whole-file PowerShell reads/writes, silently transcode UTF-16, or overwrite a file that changed after it was read. Ambiguous TOML shapes must fail before writing rather than receive a best-effort rewrite.
- Keep install/start/restore/verify serialized with the per-user operation lock in `common-windows.ps1`.
Expand All @@ -51,7 +52,7 @@ node --check assets\renderer-inject.js
- `assets/dream-skin.css`: full visual layer.
- `assets/renderer-inject.js`: idempotent DOM integration and cleanup.
- `assets/dream-reference.jpg`: pure 2560 × 1440 Arina Hashimoto wallpaper seeded as the default and as a saved theme; it contains no Codex UI.
- `assets/theme.json`: shared adaptive theme contract for the seeded preset.
- `assets/theme.json`: shared adaptive theme contract for the seeded preset, including optional `pet.mode` and `pet.glow` controls.
- `scripts/theme-windows.ps1`: persistent active/saved theme store, safe image import, pause state, and preset seeding.
- `scripts/tray-dream-skin.ps1`: Windows Forms tray for apply, pause, import, save, switch, and complete restore.
- `references/qa-inventory.md`: required functional and visual signoff coverage.
Expand Down
58 changes: 56 additions & 2 deletions windows/assets/renderer-inject.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
const STATE_KEY = "__CODEX_DREAM_SKIN_STATE__";
const STYLE_ID = "codex-dream-skin-style";
const CHROME_ID = "codex-dream-skin-chrome";
const PET_STYLE_ID = "codex-dream-skin-pet-style";
const PET_ROOT_CLASS = "codex-dream-skin-pet";
const ROOT_CLASSES = [
"codex-dream-skin",
"dream-theme-light",
Expand All @@ -28,6 +30,7 @@
"--dream-accent-ink",
"--dream-image-luma",
];
const PET_PROPERTIES = ["--dream-pet-accent", "--dream-pet-glow-blur"];
const HOME_UTILITY_CLASS = "dream-home-utility";
const installToken = {};
let samplingNativeShell = false;
Expand Down Expand Up @@ -55,6 +58,7 @@
const normalizeConfig = (value) => {
const config = value && typeof value === "object" ? value : {};
const art = config.art && typeof config.art === "object" ? config.art : {};
const pet = config.pet && typeof config.pet === "object" ? config.pet : {};
const hasNumber = (candidate) =>
(typeof candidate === "number" || (typeof candidate === "string" && candidate.trim() !== "")) &&
Number.isFinite(Number(candidate));
Expand All @@ -74,13 +78,16 @@
? art.taskMode
: "auto";
const metadataRatio = Number(config?.artMetadata?.ratio);
const petMode = ["accent", "off"].includes(pet.mode) ? pet.mode : "accent";
return {
appearance,
safeArea,
taskMode,
focusX: hasNumber(art.focusX) ? clamp(art.focusX) : null,
focusY: hasNumber(art.focusY) ? clamp(art.focusY) : null,
accent: safeAccent,
petMode,
petGlow: hasNumber(pet.glow) ? clamp(pet.glow) : .45,
initialAspect: Number.isFinite(metadataRatio) && metadataRatio > 0 ? metadataRatio : null,
};
};
Expand Down Expand Up @@ -275,7 +282,14 @@
return "light";
};

const clearSkinDom = () => {
const clearPetDom = () => {
const root = document.documentElement;
root?.classList.remove(PET_ROOT_CLASS);
for (const property of PET_PROPERTIES) root?.style.removeProperty(property);
document.getElementById(PET_STYLE_ID)?.remove();
};

const clearShellSkinDom = () => {
const root = document.documentElement;
root?.classList.remove(...ROOT_CLASSES);
for (const property of ROOT_PROPERTIES) root?.style.removeProperty(property);
Expand All @@ -287,6 +301,41 @@
document.getElementById(CHROME_ID)?.remove();
};

const clearSkinDom = () => {
clearShellSkinDom();
clearPetDom();
};

const applyPetProfile = (root) => {
const accent = config.accent || `rgb(${profile.accent.join(" ")})`;
const blur = 4 + config.petGlow * 12;
root.classList.add(PET_ROOT_CLASS);
root.style.setProperty("--dream-pet-accent", accent);
root.style.setProperty("--dream-pet-glow-blur", `${blur.toFixed(1).replace(/\.0$/, "")}px`);

let style = document.getElementById(PET_STYLE_ID);
if (!style) {
style = document.createElement("style");
style.id = PET_STYLE_ID;
(document.head || root).appendChild(style);
}
if (style.dataset.dreamVersion !== "1") {
style.textContent = `
html.${PET_ROOT_CLASS},
html.${PET_ROOT_CLASS} body {
background: transparent !important;
}
html.${PET_ROOT_CLASS} body {
filter: drop-shadow(
0 0 var(--dream-pet-glow-blur)
color-mix(in srgb, var(--dream-pet-accent) 62%, transparent)
);
}
`;
style.dataset.dreamVersion = "1";
}
};

const applyProfile = (root) => {
const focusX = config.focusX ?? profile.focusX;
const focusY = config.focusY ?? profile.focusY;
Expand Down Expand Up @@ -329,10 +378,15 @@
const shellMain = document.querySelector("main.main-surface");
const shellSidebar = document.querySelector("aside.app-shell-left-panel");
if (!shellMain || !shellSidebar) {
clearSkinDom();
clearShellSkinDom();
const petOverlay = location.protocol === "app:" && location.hostname === "codex" &&
location.pathname === "/avatar-overlay" && !location.search && !location.hash;
if (petOverlay && config.petMode === "accent") applyPetProfile(root);
else clearPetDom();
return;
}

clearPetDom();
root.classList.add("codex-dream-skin");
applyProfile(root);

Expand Down
4 changes: 4 additions & 0 deletions windows/assets/theme.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,9 @@
"focusY": 0.45,
"safeArea": "left",
"taskMode": "ambient"
},
"pet": {
"mode": "accent",
"glow": 0.45
}
}
2 changes: 1 addition & 1 deletion windows/references/qa-inventory.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
- Task side panel: open and close the native thread panel twice, resize the window, and repeat; the toggle must remain visible and clickable.
- Composer: type text, verify caret/readability, then clear it without sending.
- Reload: use CDP `Page.reload`, wait, and confirm the injection marker returns.
- Pet overlay: open a desktop pet and confirm its auxiliary window stays transparent with no skin background or decoration layer behind it.
- Pet overlay: open a desktop pet and confirm its exact `/avatar-overlay` window stays transparent, retains native animation and interaction, and receives only the active theme's accent glow. Change themes and confirm the glow updates; pause or restore and confirm it is removed. Set `pet.mode` to `off` and confirm no pet style remains.
- Restore/reapply cycle: remove live skin, verify marker absent, apply again, verify marker present.
- Update resilience: resolve the current `OpenAI.Codex` Appx location dynamically for launch. A versioned path saved for cleanup must be revalidated against the registered package full/family identity before any process is stopped.
- Restart consent: an existing normal Codex window is never force-closed without explicit CLI authorization or shortcut confirmation.
Expand Down
Loading
Loading