diff --git a/CHANGELOG.md b/CHANGELOG.md index 42f7d78..5f31b25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 0.1.2 — 2026-08-17 + +- Added Codex 26.810 compatibility for its renamed Home fade, project utility, + and task composer footer surfaces. +- Matched Home and task brightness by removing the task-wide dimming layer and + keeping both composers on one 54% / 18px glass surface. +- Removed the hidden 153px sticky footer compositing layer that could render as + a large black rectangle above the task composer. +- Strengthened live verification for effective surface opacity, nested native + blur and shadow layers, Home toolbar styling, and zero-sized footer gradients. + ## 0.1.1 — 2026-08-16 - Restored themed composer glass on Codex 26.803 by clearing its new opaque diff --git a/VERSION b/VERSION index 17e51c3..d917d3e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.1 +0.1.2 diff --git a/engine/assets/ai-themestore.css b/engine/assets/ai-themestore.css index 0658c73..dcb757d 100644 --- a/engine/assets/ai-themestore.css +++ b/engine/assets/ai-themestore.css @@ -20,6 +20,8 @@ --ds-card-surface-mix: 50%; --ds-workspace-surface-mix: 54%; --ds-composer-surface-mix: 54%; + --ds-task-surface-mix: 0%; + --ds-task-composer-surface-mix: 54%; --ds-control-surface-blur: 18px; --ds-sidebar-surface-mix: 50%; --ds-sidebar-surface-blur: 0px; @@ -457,11 +459,20 @@ html.ai-themestore .ai-themestore-composer-surface { overflow: visible !important; } +/* Keep task and Home composers on the same single 54% glass layer. */ +html.ai-themestore[data-themestore-shell="dark"] + main.main-surface:not(.ai-themestore-home-shell) + .ai-themestore-composer-surface { + background: color-mix(in srgb, var(--ds-panel) var(--ds-task-composer-surface-mix), transparent) !important; +} + /* Codex 26.803 can paint an opaque layout body inside the semantic composer surface. Keep the native controls live while letting the themed glass show. */ html.ai-themestore .ai-themestore-composer-surface > [data-composer-layout] { background-color: transparent !important; background-image: none !important; + box-shadow: none !important; + backdrop-filter: none !important; } html.ai-themestore main.main-surface:not(.ai-themestore-home-shell) @@ -592,7 +603,8 @@ html.ai-themestore[data-themestore-mark-style="none"] .ai-themestore-composer-su display: none; } -.ai-themestore-home div:has(> .horizontal-scroll-fade-mask .group\/project-selector) { +.ai-themestore-home div:has(> .horizontal-scroll-fade-mask .group\/project-selector), +.ai-themestore-home .ai-themestore-project-utility { position: relative; border: 1px solid color-mix(in srgb, var(--ds-cyan) 18%, transparent); border-bottom: 0; @@ -603,7 +615,8 @@ html.ai-themestore[data-themestore-mark-style="none"] .ai-themestore-composer-su backdrop-filter: blur(var(--ds-control-surface-blur)) saturate(112%) !important; } -.ai-themestore-home div:has(> .horizontal-scroll-fade-mask .group\/project-selector)::before { +.ai-themestore-home div:has(> .horizontal-scroll-fade-mask .group\/project-selector)::before, +.ai-themestore-home .ai-themestore-project-utility::before { content: none !important; } @@ -623,7 +636,8 @@ html.ai-themestore[data-themestore-mark-style="none"] .ai-themestore-composer-su user-select: none; } -.ai-themestore-home div:has(> .horizontal-scroll-fade-mask .group\/project-selector)::after { +.ai-themestore-home div:has(> .horizontal-scroll-fade-mask .group\/project-selector)::after, +.ai-themestore-home .ai-themestore-project-utility::after { content: ""; position: absolute; left: 0; @@ -819,7 +833,7 @@ html.ai-themestore[data-themestore-background="full"] main.main-surface { } html.ai-themestore[data-themestore-background="full"] main.main-surface:not(.ai-themestore-home-shell) { - background: color-mix(in srgb, var(--ds-panel) var(--ds-sidebar-surface-mix), transparent) !important; + background: color-mix(in srgb, var(--ds-panel) var(--ds-task-surface-mix), transparent) !important; backdrop-filter: blur(var(--ds-sidebar-surface-blur)) saturate(112%) !important; } @@ -860,6 +874,19 @@ html.ai-themestore[data-themestore-background="full"] html.ai-themestore[data-themestore-background="full"] main.main-surface:not(.ai-themestore-home-shell) [class~="bg-gradient-to-t"][class~="from-token-main-surface-primary"] { + display: none !important; + background-image: none !important; + background-clip: border-box !important; + clip-path: none !important; +} + +/* Codex 26.810 renamed the task composer footer gradient tokens. Even after + clearing its image, the 153px sticky backdrop layer can composite as an + opaque black rectangle, so remove the layer itself. */ +html.ai-themestore[data-themestore-background="full"] + main.main-surface:not(.ai-themestore-home-shell) + [class~="bg-gradient-to-t"][class~="from-surface"][class~="via-surface"] { + display: none !important; background-image: none !important; background-clip: border-box !important; clip-path: none !important; @@ -893,6 +920,27 @@ html.ai-themestore[data-themestore-background="full"][data-themestore-sidebar-fr backdrop-filter: none !important; } +/* Keep the artwork continuous across the native app header boundary and match + the transparent task main surface. */ +html.ai-themestore[data-themestore-background="full"] main.main-surface.ai-themestore-home-shell, +html.ai-themestore[data-themestore-shell="light"][data-themestore-background="full"] main.main-surface.ai-themestore-home-shell { + background-color: transparent !important; + background-image: none !important; + backdrop-filter: none !important; +} + +/* Codex 26.810 adds a native 24px top fade at the content-frame boundary. + It is useful on opaque pages but cuts a dark horizontal band through Home's + continuous wallpaper, so disable only the renderer-marked Home instance. */ +html.ai-themestore[data-themestore-background="full"] + main.main-surface.ai-themestore-home-shell + .ai-themestore-home-top-fade { + background-color: transparent !important; + background-image: none !important; + box-shadow: none !important; + backdrop-filter: none !important; +} + html.ai-themestore[data-themestore-background="full"] main.main-surface > header.app-header-tint { background: color-mix(in srgb, var(--ds-panel) 68%, transparent) !important; backdrop-filter: blur(calc(var(--ds-blur) + 2px)) saturate(112%) !important; diff --git a/engine/assets/renderer-inject.js b/engine/assets/renderer-inject.js index f2d2cc9..bb46983 100644 --- a/engine/assets/renderer-inject.js +++ b/engine/assets/renderer-inject.js @@ -476,9 +476,20 @@ controls?.classList.add("ai-themestore-home-controls"); } } + const homeTopFadeClass = "ai-themestore-home-top-fade"; + for (const candidate of document.querySelectorAll(`.${homeTopFadeClass}`)) { + candidate.classList.remove(homeTopFadeClass); + } + if (home && shellMain) { + shellMain.querySelector('[class*="_MainContentTopFade_"]')?.classList.add(homeTopFadeClass); + } for (const hint of document.querySelectorAll(".ai-themestore-project-hint")) { if (!home?.contains(hint)) hint.remove(); } + const projectUtilityClass = "ai-themestore-project-utility"; + for (const candidate of document.querySelectorAll(`.${projectUtilityClass}`)) { + candidate.classList.remove(projectUtilityClass); + } if (home) { for (const scrollArea of home.querySelectorAll("[data-composer-utility-bar-scroll-area]")) { const projectSelector = scrollArea.querySelector(".group\\/project-selector") ?? @@ -486,6 +497,7 @@ if (!projectSelector) continue; const utilityBar = scrollArea.parentElement; if (!utilityBar) continue; + utilityBar.classList.add(projectUtilityClass); let hint = utilityBar.querySelector(":scope > .ai-themestore-project-hint"); if (!hint) { hint = document.createElement("span"); @@ -576,6 +588,7 @@ for (const name of THEME_VARIABLES) document.documentElement?.style.removeProperty(name); document.querySelectorAll(".ai-themestore-home").forEach((node) => node.classList.remove("ai-themestore-home")); document.querySelectorAll(".ai-themestore-home-shell").forEach((node) => node.classList.remove("ai-themestore-home-shell")); + document.querySelectorAll(".ai-themestore-home-top-fade").forEach((node) => node.classList.remove("ai-themestore-home-top-fade")); document.querySelectorAll(".ai-themestore-chat-pane").forEach((node) => node.classList.remove("ai-themestore-chat-pane")); document.querySelectorAll(".ai-themestore-tool-pane").forEach((node) => node.classList.remove("ai-themestore-tool-pane")); document.querySelectorAll(`.${COMPOSER_SURFACE_CLASS}`).forEach((node) => { @@ -604,6 +617,9 @@ ".ai-themestore-composer-signature, .ai-themestore-composer-quote, " + ".ai-themestore-composer-orbit, .ai-themestore-project-hint", ).forEach((node) => node.remove()); + document.querySelectorAll(".ai-themestore-project-utility").forEach((node) => { + node.classList.remove("ai-themestore-project-utility"); + }); document.getElementById(STYLE_ID)?.remove(); document.getElementById(CHROME_ID)?.remove(); const motionVideo = document.getElementById(VIDEO_ID); diff --git a/engine/scripts/codex-shell-probe.mjs b/engine/scripts/codex-shell-probe.mjs index 6a9b8c4..63130f0 100644 --- a/engine/scripts/codex-shell-probe.mjs +++ b/engine/scripts/codex-shell-probe.mjs @@ -38,6 +38,31 @@ export function resolveShellMainDocument(documentRef) { export const SHELL_MAIN_RESOLVER_EXPRESSION = `(${resolveShellMainDocument.toString()})(document)`; +export function shellMainCoveragePass({ + visible, + homeRoute, + backgroundAlpha, + backgroundImage, +}) { + if (!visible || backgroundAlpha == null) return false; + return backgroundAlpha <= 0.035 && + (backgroundImage === "none" || backgroundImage === null); +} + +export const SHELL_MAIN_COVERAGE_EXPRESSION = + `(${shellMainCoveragePass.toString()})`; + +export function compositedSurfaceAlpha(backdropAlpha, foregroundAlpha) { + if (!Number.isFinite(backdropAlpha) || !Number.isFinite(foregroundAlpha)) return null; + if (backdropAlpha < 0 || backdropAlpha > 1 || foregroundAlpha < 0 || foregroundAlpha > 1) { + return null; + } + return backdropAlpha + foregroundAlpha * (1 - backdropAlpha); +} + +export const COMPOSITED_SURFACE_ALPHA_EXPRESSION = + `(${compositedSurfaceAlpha.toString()})`; + export function resolveToolPaneDocument(documentRef) { return documentRef.querySelector( 'aside[data-app-shell-focus-area="right-panel"]', @@ -65,12 +90,41 @@ export const COMPOSER_SURFACE_RESOLVER_EXPRESSION = export function composerNativeLayerCoveragePass(layers) { return layers.every((layer) => layer.backgroundAlpha <= 0.035 && - (layer.backgroundImage === "none" || layer.backgroundImage === null)); + (layer.backgroundImage === "none" || layer.backgroundImage === null) && + (layer.backdropFilter === "none" || layer.backdropFilter === null) && + (layer.boxShadow === "none" || layer.boxShadow === null)); } export const COMPOSER_NATIVE_LAYER_COVERAGE_EXPRESSION = `(${composerNativeLayerCoveragePass.toString()})`; +export function homeTopFadeCoveragePass({ homeRoute, present, backgroundImage, backdropFilter, boxShadow }) { + if (!homeRoute || !present) return true; + return (backgroundImage === "none" || backgroundImage === null) && + (backdropFilter === "none" || backdropFilter === null) && + (boxShadow === "none" || boxShadow === null); +} + +export const HOME_TOP_FADE_COVERAGE_EXPRESSION = + `(${homeTopFadeCoveragePass.toString()})`; + +export function workspaceSurfaceCoveragePass({ + homeRoute, + present, + backgroundAlpha, + backdropFilter, +}) { + if (!homeRoute) return true; + return Boolean( + present && backgroundAlpha != null && + backgroundAlpha >= 0.505 && backgroundAlpha <= 0.575 && + backdropFilter?.includes("blur(18px)"), + ); +} + +export const WORKSPACE_SURFACE_COVERAGE_EXPRESSION = + `(${workspaceSurfaceCoveragePass.toString()})`; + export function resolveChatPaneDocument(documentRef) { const marked = documentRef.querySelector("aside.ai-themestore-chat-pane"); if (marked) return marked; diff --git a/engine/scripts/injector.mjs b/engine/scripts/injector.mjs index 29e5d37..a185a48 100644 --- a/engine/scripts/injector.mjs +++ b/engine/scripts/injector.mjs @@ -5,11 +5,15 @@ import { CHAT_PANE_DIVIDER_PARITY_EXPRESSION, CHAT_PANE_RESOLVER_EXPRESSION, CODEX_SHELL_PROBE_EXPRESSION, + COMPOSITED_SURFACE_ALPHA_EXPRESSION, COMPOSER_NATIVE_LAYER_COVERAGE_EXPRESSION, COMPOSER_SURFACE_RESOLVER_EXPRESSION, + HOME_TOP_FADE_COVERAGE_EXPRESSION, OPTIONAL_SIDEBAR_VISIBILITY_EXPRESSION, + SHELL_MAIN_COVERAGE_EXPRESSION, SHELL_MAIN_RESOLVER_EXPRESSION, TOOL_PANE_RESOLVER_EXPRESSION, + WORKSPACE_SURFACE_COVERAGE_EXPRESSION, resolveComposerSurfaceDocument, } from "./codex-shell-probe.mjs"; @@ -542,6 +546,7 @@ async function verifySession(session, expected = null) { : projectSelector?.querySelector(':scope > button') ?? projectSelector); const projectScrollArea = projectSelector?.closest('[data-composer-utility-bar-scroll-area]') ?? null; const projectUtilityBar = projectScrollArea?.parentElement ?? null; + const projectUtilityStyle = projectUtilityBar ? getComputedStyle(projectUtilityBar) : null; const projectHintNode = projectUtilityBar?.querySelector(':scope > .ai-themestore-project-hint') ?? null; const projectRightControl = projectScrollArea?.nextElementSibling ?? null; const composerNode = ${COMPOSER_SURFACE_RESOLVER_EXPRESSION}; @@ -565,6 +570,8 @@ async function verifySession(session, expected = null) { backgroundColor: childStyle.backgroundColor, backgroundAlpha: backgroundAlpha(childStyle.backgroundColor), backgroundImage: childStyle.backgroundImage, + backdropFilter: childStyle.backdropFilter, + boxShadow: childStyle.boxShadow, }; }) : []; @@ -591,6 +598,17 @@ async function verifySession(session, expected = null) { semantic: shellMainNode?.hasAttribute('data-app-shell-main-surface') ?? false, backgroundColor: shellMainStyle?.backgroundColor ?? null, backgroundAlpha: shellMainStyle ? backgroundAlpha(shellMainStyle.backgroundColor) : null, + backgroundImage: shellMainStyle?.backgroundImage ?? null, + backdropFilter: shellMainStyle?.backdropFilter ?? null, + pass: false, + }; + const homeTopFadeNode = document.querySelector('.ai-themestore-home-top-fade'); + const homeTopFadeStyle = homeTopFadeNode ? getComputedStyle(homeTopFadeNode) : null; + const homeTopFadeCoverage = { + present: Boolean(homeTopFadeNode), + backgroundImage: homeTopFadeStyle?.backgroundImage ?? null, + backdropFilter: homeTopFadeStyle?.backdropFilter ?? null, + boxShadow: homeTopFadeStyle?.boxShadow ?? null, pass: false, }; const chrome = document.getElementById('ai-themestore-chrome'); @@ -612,18 +630,28 @@ async function verifySession(session, expected = null) { )] : []; const taskGradientSurfaces = [...document.querySelectorAll( - '[class~="bg-gradient-to-t"][class~="from-token-main-surface-primary"]', + '[class~="bg-gradient-to-t"][class~="from-token-main-surface-primary"], ' + + '[class~="bg-gradient-to-t"][class~="from-surface"][class~="via-surface"]', )].filter((node) => !chatPaneNode?.contains(node)); const nativeSurfacesClear = chatPaneNativeSurfaces.every((node) => { const style = getComputedStyle(node); return style.backgroundColor === 'rgba(0, 0, 0, 0)' && style.backgroundImage === 'none'; }); - const gradientSurfacesClear = chatPaneGradientSurfaces.every( - (node) => getComputedStyle(node).backgroundImage === 'none', - ); - const taskGradientsClear = taskGradientSurfaces.every( - (node) => getComputedStyle(node).backgroundImage === 'none', - ); + const gradientSurfaceMetric = (node) => { + const style = getComputedStyle(node); + const rect = node.getBoundingClientRect(); + return { + display: style.display, + backgroundImage: style.backgroundImage, + width: rect.width, + height: rect.height, + removed: style.display === 'none' && rect.width === 0 && rect.height === 0, + }; + }; + const chatPaneGradientMetrics = chatPaneGradientSurfaces.map(gradientSurfaceMetric); + const taskGradientMetrics = taskGradientSurfaces.map(gradientSurfaceMetric); + const gradientSurfacesClear = chatPaneGradientMetrics.every((metric) => metric.removed); + const taskGradientsClear = taskGradientMetrics.every((metric) => metric.removed); const chatPaneNativeDivider = chatPaneNode?.querySelector( '[class~="bg-token-main-surface-primary"][class~="border-l"][class~="border-token-border-default"]', ) ?? null; @@ -660,6 +688,7 @@ async function verifySession(session, expected = null) { nativeSurfaceCount: chatPaneNativeSurfaces.length, nativeSurfacesClear, gradientSurfaceCount: chatPaneGradientSurfaces.length, + gradientSurfaces: chatPaneGradientMetrics, gradientSurfacesClear, dividerParity, artworkSpansChatPane, @@ -672,6 +701,7 @@ async function verifySession(session, expected = null) { ); const taskSurfaceCoverage = { gradientSurfaceCount: taskGradientSurfaces.length, + gradientSurfaces: taskGradientMetrics, gradientsClear: taskGradientsClear, pass: taskGradientsClear, }; @@ -727,6 +757,8 @@ async function verifySession(session, expected = null) { cardSurfaceMix: rootStyle.getPropertyValue('--ds-card-surface-mix').trim(), workspaceSurfaceMix: rootStyle.getPropertyValue('--ds-workspace-surface-mix').trim(), composerSurfaceMix: rootStyle.getPropertyValue('--ds-composer-surface-mix').trim(), + taskSurfaceMix: rootStyle.getPropertyValue('--ds-task-surface-mix').trim(), + taskComposerSurfaceMix: rootStyle.getPropertyValue('--ds-task-composer-surface-mix').trim(), controlSurfaceBlur: rootStyle.getPropertyValue('--ds-control-surface-blur').trim(), sidebarSurfaceMix: rootStyle.getPropertyValue('--ds-sidebar-surface-mix').trim(), sidebarSurfaceBlur: rootStyle.getPropertyValue('--ds-sidebar-surface-blur').trim(), @@ -743,6 +775,8 @@ async function verifySession(session, expected = null) { visualBaseline.cardSurfaceMix === '50%' && visualBaseline.workspaceSurfaceMix === '54%' && visualBaseline.composerSurfaceMix === '54%' && + visualBaseline.taskSurfaceMix === '0%' && + visualBaseline.taskComposerSurfaceMix === '54%' && visualBaseline.controlSurfaceBlur === '18px' && visualBaseline.sidebarSurfaceMix === '50%' && visualBaseline.sidebarSurfaceBlur === '0px' && @@ -771,6 +805,13 @@ async function verifySession(session, expected = null) { projectButton, projectUtility: { bar: box(projectUtilityBar), + surface: { + present: Boolean(projectUtilityBar), + backgroundColor: projectUtilityStyle?.backgroundColor ?? null, + backgroundAlpha: projectUtilityStyle ? backgroundAlpha(projectUtilityStyle.backgroundColor) : null, + backdropFilter: projectUtilityStyle?.backdropFilter ?? null, + pass: false, + }, scrollArea: box(projectScrollArea), hint: { ...box(projectHintNode), @@ -785,6 +826,7 @@ async function verifySession(session, expected = null) { composer, composerSurface, shellMainCoverage, + homeTopFadeCoverage, chatPaneCoverage, taskSurfaceCoverage, toolPaneCoverage, @@ -818,20 +860,34 @@ async function verifySession(session, expected = null) { }, }; const shell = document.documentElement.getAttribute('data-themestore-shell') || 'unknown'; - result.shellMainCoverage.pass = Boolean( - result.shellMainCoverage.visible && - result.shellMainCoverage.backgroundAlpha != null && - result.shellMainCoverage.backgroundAlpha >= 0.28 && - result.shellMainCoverage.backgroundAlpha <= 0.55 - ); + result.shellMainCoverage.pass = ${SHELL_MAIN_COVERAGE_EXPRESSION}({ + ...result.shellMainCoverage, + homeRoute: result.homeRoute, + }); + result.homeTopFadeCoverage.pass = ${HOME_TOP_FADE_COVERAGE_EXPRESSION}({ + ...result.homeTopFadeCoverage, + homeRoute: result.homeRoute, + }); + const composerBackdropAlpha = result.homeRoute ? 0 : result.shellMainCoverage.backgroundAlpha; const expectedComposerAlpha = shell === 'light' ? 0.94 : 0.54; - const composerSurfacePass = (metric) => Boolean( - metric.backgroundAlpha != null && - Math.abs(metric.backgroundAlpha - expectedComposerAlpha) <= 0.035 && - metric.backdropFilter?.includes('blur(18px)') && - metric.borderRadius === '21px' && - metric.nativeLayersClear - ); + const composerSurfacePass = (metric, backdropAlpha = composerBackdropAlpha) => { + metric.effectiveBackgroundAlpha = ${COMPOSITED_SURFACE_ALPHA_EXPRESSION}( + backdropAlpha, + metric.backgroundAlpha, + ); + const effectiveAlphaPass = shell === 'light' || Boolean( + metric.effectiveBackgroundAlpha != null && + Math.abs(metric.effectiveBackgroundAlpha - 0.54) <= 0.035 + ); + return Boolean( + metric.backgroundAlpha != null && + Math.abs(metric.backgroundAlpha - expectedComposerAlpha) <= 0.035 && + effectiveAlphaPass && + metric.backdropFilter?.includes('blur(18px)') && + metric.borderRadius === '21px' && + metric.nativeLayersClear + ); + }; result.composerSurface.pass = composerSurfacePass(result.composerSurface); result.chatPaneCoverage.composerSurface.pass = !result.chatPaneCoverage.expected || composerSurfacePass(result.chatPaneCoverage.composerSurface); @@ -924,6 +980,10 @@ async function verifySession(session, expected = null) { orbit.ariaHidden && orbit.pointerEvents === 'none' && orbitBoundsPass ); const projectUtility = result.projectUtility; + projectUtility.surface.pass = ${WORKSPACE_SURFACE_COVERAGE_EXPRESSION}({ + ...projectUtility.surface, + homeRoute: result.homeRoute, + }); const hintShouldBeVisible = innerWidth > 1120; const hintSingleRowPass = !projectUtility.hint.visible || Boolean( projectUtility.bar?.visible && result.projectButton?.visible && @@ -944,7 +1004,7 @@ async function verifySession(session, expected = null) { projectUtility.bar?.visible && projectUtility.scrollArea?.visible && result.projectButton?.visible && projectUtility.hint.ariaHidden && projectUtility.hint.pointerEvents === 'none' && projectUtility.hint.role === null && projectUtility.hint.immediatelyBeforeScrollArea && - Boolean(projectUtility.hint.text) && + Boolean(projectUtility.hint.text) && projectUtility.surface.pass && (hintShouldBeVisible ? projectUtility.hint.visible : !projectUtility.hint.visible) && hintSingleRowPass && hintBeforeProjectPass && rightControlSafePass ) : !projectUtility.hint.visible @@ -966,6 +1026,7 @@ async function verifySession(session, expected = null) { result.signature.pass && result.composerDecorations.pass && result.composerSurface.pass && result.shellMainCoverage.pass && + result.homeTopFadeCoverage.pass && result.chatPaneCoverage.pass && result.taskSurfaceCoverage.pass && result.toolPaneCoverage.pass && result.readability.pass && diff --git a/tests/codex-shell-probe.test.mjs b/tests/codex-shell-probe.test.mjs index 15370cb..cc2dbcc 100644 --- a/tests/codex-shell-probe.test.mjs +++ b/tests/codex-shell-probe.test.mjs @@ -1,15 +1,28 @@ import assert from "node:assert/strict"; import { chatPaneDividerParityPass, + compositedSurfaceAlpha, composerNativeLayerCoveragePass, + homeTopFadeCoveragePass, optionalSidebarVisibilityPass, probeCodexShellDocument, resolveChatPaneDocument, resolveComposerSurfaceDocument, resolveShellMainDocument, resolveToolPaneDocument, + shellMainCoveragePass, + workspaceSurfaceCoveragePass, } from "../engine/scripts/codex-shell-probe.mjs"; +assert.equal(compositedSurfaceAlpha(0, 0.54), 0.54, + "Home composer effective alpha must equal its only glass layer"); +assert.ok(Math.abs(compositedSurfaceAlpha(0.3, 0.34) - 0.538) < 1e-9, + "surface alpha composition must remain mathematically explicit"); +assert.ok(Math.abs(compositedSurfaceAlpha(0.5, 0.54) - 0.77) < 1e-9, + "the former stacked task surfaces must expose their 77% effective darkness"); +assert.equal(compositedSurfaceAlpha(-0.1, 0.5), null, + "invalid surface alpha must be rejected"); + const canonicalLocation = Object.freeze({ protocol: "app:", host: "-", @@ -127,6 +140,37 @@ assert.equal(resolveShellMainDocument({ querySelectorAll: () => [invisibleOverlayMain, visibleFallbackMain], }), visibleFallbackMain, "an invisible overlay main must not become the themed shell"); +assert.equal(shellMainCoveragePass({ + visible: true, + homeRoute: true, + backgroundAlpha: 0, + backgroundImage: "none", +}), true, "Home must keep the wallpaper continuous through a transparent main surface"); +assert.equal(shellMainCoveragePass({ + visible: true, + homeRoute: true, + backgroundAlpha: 0.3, + backgroundImage: "none", +}), false, "Home must reject the dark main-surface band below the native header"); +assert.equal(shellMainCoveragePass({ + visible: true, + homeRoute: true, + backgroundAlpha: 0, + backgroundImage: "linear-gradient(rgb(0, 0, 0), rgb(0, 0, 0))", +}), false, "Home must reject a native main-surface background image"); +assert.equal(shellMainCoveragePass({ + visible: true, + homeRoute: false, + backgroundAlpha: 0, + backgroundImage: "none", +}), true, "task routes must expose the same un-dimmed artwork as Home"); +assert.equal(shellMainCoveragePass({ + visible: true, + homeRoute: false, + backgroundAlpha: 0.3, + backgroundImage: "none", +}), false, "task routes must reject a full-workspace dimming layer"); + const rightToolPane = {}; assert.equal(resolveToolPaneDocument({ querySelector(selector) { @@ -212,15 +256,70 @@ assert.equal(composerNativeLayerCoveragePass([]), true, assert.equal(composerNativeLayerCoveragePass([{ backgroundAlpha: 0, backgroundImage: "none", + backdropFilter: "none", + boxShadow: "none", }]), true, "a transparent Codex semantic composer body must preserve theme glass"); assert.equal(composerNativeLayerCoveragePass([{ backgroundAlpha: 0.864706, backgroundImage: "none", + backdropFilter: "none", + boxShadow: "none", }]), false, "an opaque Codex semantic composer body must fail verification"); assert.equal(composerNativeLayerCoveragePass([{ backgroundAlpha: 0, backgroundImage: "linear-gradient(rgb(0, 0, 0), rgb(0, 0, 0))", + backdropFilter: "none", + boxShadow: "none", }]), false, "a native composer background image must fail verification"); +assert.equal(composerNativeLayerCoveragePass([{ + backgroundAlpha: 0, + backgroundImage: "none", + backdropFilter: "blur(16px)", + boxShadow: "none", +}]), false, "a second native composer blur layer must fail verification"); +assert.equal(composerNativeLayerCoveragePass([{ + backgroundAlpha: 0, + backgroundImage: "none", + backdropFilter: "none", + boxShadow: "rgb(24, 24, 24) 0px 0px 0px 0.5px", +}]), false, "a native dark composer shadow must fail verification"); + +assert.equal(homeTopFadeCoveragePass({ + homeRoute: true, + present: true, + backgroundImage: "none", + backdropFilter: "none", + boxShadow: "none", +}), true, "Home must clear the native content-frame top fade"); +assert.equal(homeTopFadeCoveragePass({ + homeRoute: true, + present: true, + backgroundImage: "linear-gradient(rgb(24, 24, 24), transparent)", + backdropFilter: "none", + boxShadow: "none", +}), false, "Home must reject the native dark horizontal top fade"); +assert.equal(homeTopFadeCoveragePass({ + homeRoute: false, + present: true, + backgroundImage: "linear-gradient(rgb(24, 24, 24), transparent)", +}), true, "task routes may retain their native top fade"); + +assert.equal(workspaceSurfaceCoveragePass({ + homeRoute: true, + present: true, + backgroundAlpha: 0.54, + backdropFilter: "blur(18px) saturate(1.12)", +}), true, "Home workspace controls must retain the locked 54% / 18px surface"); +assert.equal(workspaceSurfaceCoveragePass({ + homeRoute: true, + present: true, + backgroundAlpha: 0, + backdropFilter: "none", +}), false, "Home must reject a washed-out transparent workspace toolbar"); +assert.equal(workspaceSurfaceCoveragePass({ + homeRoute: false, + present: false, +}), true, "task routes do not require the Home workspace toolbar"); assert.equal(chatPaneDividerParityPass({ chatPanePresent: false }), true, "absent Ask pane must not require divider verification"); diff --git a/tests/run-tests.sh b/tests/run-tests.sh index 6acc9eb..0947085 100755 --- a/tests/run-tests.sh +++ b/tests/run-tests.sh @@ -24,6 +24,41 @@ done printf 'Live verification must reject opaque native layers inside the composer.\n' >&2 exit 1 } +/usr/bin/grep -F -q '[class~="from-surface"][class~="via-surface"]' \ + "$ROOT/engine/assets/ai-themestore.css" || { + printf 'Task coverage must remove the Codex 26.810 composer footer gradient.\n' >&2 + exit 1 +} +/usr/bin/grep -F -q 'gradientSurfaces: taskGradientMetrics' \ + "$ROOT/engine/scripts/injector.mjs" || { + printf 'Live verification must inspect native sticky gradient geometry.\n' >&2 + exit 1 +} +/usr/bin/grep -F -q 'ai-themestore-home-top-fade' \ + "$ROOT/engine/assets/renderer-inject.js" || { + printf 'Renderer must identify the Codex 26.810 Home top fade.\n' >&2 + exit 1 +} +/usr/bin/grep -F -q 'result.homeTopFadeCoverage.pass' \ + "$ROOT/engine/scripts/injector.mjs" || { + printf 'Live verification must reject the native dark Home top fade.\n' >&2 + exit 1 +} +/usr/bin/grep -F -q 'ai-themestore-project-utility' \ + "$ROOT/engine/assets/renderer-inject.js" || { + printf 'Renderer must mark the modern project utility surface.\n' >&2 + exit 1 +} +/usr/bin/grep -F -q 'projectUtility.surface.pass' \ + "$ROOT/engine/scripts/injector.mjs" || { + printf 'Live verification must enforce project utility surface parity.\n' >&2 + exit 1 +} +/usr/bin/grep -F -q 'SHELL_MAIN_COVERAGE_EXPRESSION' \ + "$ROOT/engine/scripts/injector.mjs" || { + printf 'Live verification must enforce transparent Home and task main surfaces.\n' >&2 + exit 1 +} /usr/bin/grep -F -q 'result.hero.y - result.homeBox.y <= 2' \ "$ROOT/engine/scripts/injector.mjs" || { printf 'Live verification must reject the modern Home top spacer regression.\n' >&2