|
2 | 2 | const STATE_KEY = "__CODEX_DREAM_SKIN_STATE__"; |
3 | 3 | const STYLE_ID = "codex-dream-skin-style"; |
4 | 4 | const CHROME_ID = "codex-dream-skin-chrome"; |
| 5 | + const PET_STYLE_ID = "codex-dream-skin-pet-style"; |
| 6 | + const PET_ROOT_CLASS = "codex-dream-skin-pet"; |
5 | 7 | const ROOT_CLASSES = [ |
6 | 8 | "codex-dream-skin", |
7 | 9 | "dream-theme-light", |
|
28 | 30 | "--dream-accent-ink", |
29 | 31 | "--dream-image-luma", |
30 | 32 | ]; |
| 33 | + const PET_PROPERTIES = ["--dream-pet-accent", "--dream-pet-glow-blur"]; |
31 | 34 | const HOME_UTILITY_CLASS = "dream-home-utility"; |
32 | 35 | const installToken = {}; |
33 | 36 | let samplingNativeShell = false; |
|
55 | 58 | const normalizeConfig = (value) => { |
56 | 59 | const config = value && typeof value === "object" ? value : {}; |
57 | 60 | const art = config.art && typeof config.art === "object" ? config.art : {}; |
| 61 | + const pet = config.pet && typeof config.pet === "object" ? config.pet : {}; |
58 | 62 | const hasNumber = (candidate) => |
59 | 63 | (typeof candidate === "number" || (typeof candidate === "string" && candidate.trim() !== "")) && |
60 | 64 | Number.isFinite(Number(candidate)); |
|
74 | 78 | ? art.taskMode |
75 | 79 | : "auto"; |
76 | 80 | const metadataRatio = Number(config?.artMetadata?.ratio); |
| 81 | + const petMode = ["accent", "off"].includes(pet.mode) ? pet.mode : "accent"; |
77 | 82 | return { |
78 | 83 | appearance, |
79 | 84 | safeArea, |
80 | 85 | taskMode, |
81 | 86 | focusX: hasNumber(art.focusX) ? clamp(art.focusX) : null, |
82 | 87 | focusY: hasNumber(art.focusY) ? clamp(art.focusY) : null, |
83 | 88 | accent: safeAccent, |
| 89 | + petMode, |
| 90 | + petGlow: hasNumber(pet.glow) ? clamp(pet.glow) : .45, |
84 | 91 | initialAspect: Number.isFinite(metadataRatio) && metadataRatio > 0 ? metadataRatio : null, |
85 | 92 | }; |
86 | 93 | }; |
|
275 | 282 | return "light"; |
276 | 283 | }; |
277 | 284 |
|
278 | | - const clearSkinDom = () => { |
| 285 | + const clearPetDom = () => { |
| 286 | + const root = document.documentElement; |
| 287 | + root?.classList.remove(PET_ROOT_CLASS); |
| 288 | + for (const property of PET_PROPERTIES) root?.style.removeProperty(property); |
| 289 | + document.getElementById(PET_STYLE_ID)?.remove(); |
| 290 | + }; |
| 291 | + |
| 292 | + const clearShellSkinDom = () => { |
279 | 293 | const root = document.documentElement; |
280 | 294 | root?.classList.remove(...ROOT_CLASSES); |
281 | 295 | for (const property of ROOT_PROPERTIES) root?.style.removeProperty(property); |
|
287 | 301 | document.getElementById(CHROME_ID)?.remove(); |
288 | 302 | }; |
289 | 303 |
|
| 304 | + const clearSkinDom = () => { |
| 305 | + clearShellSkinDom(); |
| 306 | + clearPetDom(); |
| 307 | + }; |
| 308 | + |
| 309 | + const applyPetProfile = (root) => { |
| 310 | + const accent = config.accent || `rgb(${profile.accent.join(" ")})`; |
| 311 | + const blur = 4 + config.petGlow * 12; |
| 312 | + root.classList.add(PET_ROOT_CLASS); |
| 313 | + root.style.setProperty("--dream-pet-accent", accent); |
| 314 | + root.style.setProperty("--dream-pet-glow-blur", `${blur.toFixed(1).replace(/\.0$/, "")}px`); |
| 315 | + |
| 316 | + let style = document.getElementById(PET_STYLE_ID); |
| 317 | + if (!style) { |
| 318 | + style = document.createElement("style"); |
| 319 | + style.id = PET_STYLE_ID; |
| 320 | + (document.head || root).appendChild(style); |
| 321 | + } |
| 322 | + if (style.dataset.dreamVersion !== "1") { |
| 323 | + style.textContent = ` |
| 324 | + html.${PET_ROOT_CLASS}, |
| 325 | + html.${PET_ROOT_CLASS} body { |
| 326 | + background: transparent !important; |
| 327 | + } |
| 328 | + html.${PET_ROOT_CLASS} body { |
| 329 | + filter: drop-shadow( |
| 330 | + 0 0 var(--dream-pet-glow-blur) |
| 331 | + color-mix(in srgb, var(--dream-pet-accent) 62%, transparent) |
| 332 | + ); |
| 333 | + } |
| 334 | + `; |
| 335 | + style.dataset.dreamVersion = "1"; |
| 336 | + } |
| 337 | + }; |
| 338 | + |
290 | 339 | const applyProfile = (root) => { |
291 | 340 | const focusX = config.focusX ?? profile.focusX; |
292 | 341 | const focusY = config.focusY ?? profile.focusY; |
|
329 | 378 | const shellMain = document.querySelector("main.main-surface"); |
330 | 379 | const shellSidebar = document.querySelector("aside.app-shell-left-panel"); |
331 | 380 | if (!shellMain || !shellSidebar) { |
332 | | - clearSkinDom(); |
| 381 | + clearShellSkinDom(); |
| 382 | + const petOverlay = location.protocol === "app:" && location.hostname === "codex" && |
| 383 | + location.pathname === "/avatar-overlay" && !location.search && !location.hash; |
| 384 | + if (petOverlay && config.petMode === "accent") applyPetProfile(root); |
| 385 | + else clearPetDom(); |
333 | 386 | return; |
334 | 387 | } |
335 | 388 |
|
| 389 | + clearPetDom(); |
336 | 390 | root.classList.add("codex-dream-skin"); |
337 | 391 | applyProfile(root); |
338 | 392 |
|
|
0 commit comments