chore: rebuild release package
This commit is contained in:
@@ -524,6 +524,69 @@ function createFakeCodexConfig(homeDir, { model = 'gpt-5.5', reasoningEffort = '
|
||||
].join('\n'));
|
||||
}
|
||||
|
||||
function assertFrontendSidebarCollapseContract() {
|
||||
const source = fs.readFileSync(PUBLIC_APP_PATH, 'utf8');
|
||||
const indexSource = fs.readFileSync(PUBLIC_INDEX_PATH, 'utf8');
|
||||
const styleSource = fs.readFileSync(PUBLIC_STYLE_PATH, 'utf8');
|
||||
const buttonMarkup = indexSource.match(/<button id="menu-btn"[^>]*>/)?.[0] || '';
|
||||
|
||||
assert(buttonMarkup, 'Chat header should retain the sidebar toggle next to the title');
|
||||
['type="button"', 'aria-controls="sidebar"', 'aria-expanded="true"', 'aria-label="收起左侧菜单"'].forEach((attribute) => {
|
||||
assert(buttonMarkup.includes(attribute), `Sidebar toggle should expose ${attribute}`);
|
||||
});
|
||||
assert(source.includes("const SIDEBAR_COLLAPSE_STORAGE_KEY = 'cc-web-sidebar-collapsed'"), 'Desktop sidebar preference should use a stable storage key');
|
||||
assert(source.includes("const SIDEBAR_DRAWER_MEDIA_QUERY = '(max-width: 768px)'"), 'Sidebar drawer mode should match the mobile CSS breakpoint');
|
||||
assert(source.includes("const SIDEBAR_DESKTOP_COLLAPSE_MEDIA_QUERY = '(width > 768px) and (hover: hover) and (pointer: fine)'"), 'Desktop collapse mode should match the hover rail CSS capabilities');
|
||||
assert(source.includes("app.classList.toggle('sidebar-collapsed', nextCollapsed)"), 'Desktop sidebar should keep its fixed state on the app root');
|
||||
assert(source.includes('persistSidebarCollapsedPreference(nextCollapsed)'), 'Desktop sidebar toggle should persist the user preference');
|
||||
assert(
|
||||
source.includes("menuBtn.setAttribute('aria-expanded', String(expanded))")
|
||||
&& source.includes("menuBtn.setAttribute('aria-label', label)"),
|
||||
'Sidebar toggle should synchronize its accessible state and label'
|
||||
);
|
||||
assert(
|
||||
/menuBtn\.addEventListener\('click',[\s\S]*?if \(isSidebarDrawerMode\(\)\)[\s\S]*?openSidebar\(\)[\s\S]*?if \(isSidebarDesktopCollapseMode\(\)\)[\s\S]*?setSidebarCollapsed\(/.test(source),
|
||||
'Sidebar toggle should preserve the mobile drawer path before using desktop fixed collapse'
|
||||
);
|
||||
assert(
|
||||
source.includes('[sidebarDrawerMedia, sidebarDesktopCollapseMedia].forEach((media) =>')
|
||||
&& source.includes("media.addEventListener('change', handleSidebarInputModeChange)"),
|
||||
'Sidebar should reset transient state when either drawer or desktop collapse mode changes'
|
||||
);
|
||||
|
||||
assert(styleSource.includes('--sidebar-rail-width: 14px'), 'Collapsed desktop sidebar should retain a narrow hover rail');
|
||||
assert(
|
||||
styleSource.includes('@media (width > 768px) and (hover: hover) and (pointer: fine)'),
|
||||
'Desktop hover behavior should stay isolated from touch and narrow-screen drawers'
|
||||
);
|
||||
assert(
|
||||
styleSource.includes('margin-right: calc(var(--sidebar-rail-width) - var(--sidebar-width))')
|
||||
&& styleSource.includes('transform: translateX(calc(var(--sidebar-rail-width) - var(--sidebar-width)))'),
|
||||
'Collapsed sidebar should only consume the rail width without resizing theme-specific sidebars'
|
||||
);
|
||||
assert(
|
||||
styleSource.includes('.app.sidebar-collapsed .sidebar:is(:hover, :focus-within)')
|
||||
&& styleSource.includes('transform: translateX(0)'),
|
||||
'Collapsed sidebar should temporarily expand for pointer hover and keyboard focus'
|
||||
);
|
||||
assert(styleSource.includes('.menu-btn:focus-visible'), 'Sidebar toggle should retain a visible keyboard focus treatment');
|
||||
assert(
|
||||
source.includes('if (isSidebarDesktopCollapseMode())')
|
||||
&& source.includes("const expanded = isDrawer\n ? sidebar.classList.contains('open')\n : (isDesktopCollapsible ? !app.classList.contains('sidebar-collapsed') : true)"),
|
||||
'Unsupported wide-screen pointer modes should stay visually and accessibly expanded'
|
||||
);
|
||||
assert(
|
||||
styleSource.includes("html[data-theme='gilded'] .app.sidebar-collapsed .sidebar::after")
|
||||
&& styleSource.includes("html[data-theme='wasteland'] .app.sidebar-collapsed .sidebar::after"),
|
||||
'Rich themes should provide isolated rail treatments on top of the shared semantic fallback'
|
||||
);
|
||||
assert(
|
||||
indexSource.includes('style.css?v=20260718-wasteland-bg-right-4')
|
||||
&& indexSource.includes('app.js?v=20260718-wasteland-bg-right-4'),
|
||||
'Sidebar interaction assets should share the reviewed cache-busting version'
|
||||
);
|
||||
}
|
||||
|
||||
function assertFrontendGenerationControlsContract() {
|
||||
const source = fs.readFileSync(PUBLIC_APP_PATH, 'utf8');
|
||||
const controlsStart = source.indexOf('function updateGenerationControls()');
|
||||
@@ -811,8 +874,8 @@ function assertFrontendGildedThemeContract() {
|
||||
assert(contrast('#655446', '#fff7ea') >= 4.5, 'Gilded muted text should remain readable on ivory panels');
|
||||
assert(contrast('#fff7ea', '#7a3f20') >= 7, 'Gilded primary action text should reach AAA contrast on copper');
|
||||
assert(themeStyle.includes('@media (prefers-reduced-motion: reduce)'), 'Gilded theme motion should respect reduced-motion preferences');
|
||||
assert(indexSource.includes('style.css?v=20260717-wasteland-hifi-23'), 'Theme bundle stylesheet should use the current cache-busted asset URL');
|
||||
assert(indexSource.includes('app.js?v=20260718-theme-bundle-4'), 'Theme bundle app script should use the current cache-busted asset URL');
|
||||
assert(indexSource.includes('style.css?v=20260718-wasteland-bg-right-4'), 'Theme bundle stylesheet should use the current cache-busted asset URL');
|
||||
assert(indexSource.includes('app.js?v=20260718-wasteland-bg-right-4'), 'Theme bundle app script should use the current cache-busted asset URL');
|
||||
}
|
||||
|
||||
function assertFrontendWastelandThemeContract() {
|
||||
@@ -826,6 +889,8 @@ function assertFrontendWastelandThemeContract() {
|
||||
: '';
|
||||
const themeStyleStart = styleSource.indexOf('/* === 暗金荒野');
|
||||
const themeStyle = themeStyleStart >= 0 ? styleSource.slice(themeStyleStart) : '';
|
||||
const highFidelityStyleStart = styleSource.indexOf('/* === 暗金荒野高保真复刻');
|
||||
const highFidelityStyle = highFidelityStyleStart >= 0 ? styleSource.slice(highFidelityStyleStart) : '';
|
||||
const themeOption = source.match(/\{\s*value: 'wasteland',[\s\S]*?\n\s*\},/);
|
||||
|
||||
assert(themeOption, 'Frontend should register the wasteland theme option');
|
||||
@@ -853,6 +918,7 @@ function assertFrontendWastelandThemeContract() {
|
||||
'--wasteland-cut: polygon(',
|
||||
].forEach((token) => assert(themeVars.includes(token), `Wasteland theme is missing semantic token: ${token}`));
|
||||
assert(themeStyleStart >= 0, 'Stylesheet should include a dedicated wasteland component layer');
|
||||
assert(highFidelityStyleStart >= 0, 'Stylesheet should retain the final high-fidelity wasteland override layer');
|
||||
|
||||
const bodyRule = themeStyle.match(/html\[data-theme='wasteland'\] body\s*\{([^}]*)\}/);
|
||||
assert(bodyRule && bodyRule[1].includes("url('assets/themes/wasteland/background.webp')"), 'Wasteland body should render the reviewed local knight background');
|
||||
@@ -1037,7 +1103,14 @@ function assertFrontendWastelandThemeContract() {
|
||||
});
|
||||
|
||||
assert(!/url\(\s*['"]?https?:\/\//i.test(`${themeVars}\n${themeStyle}\n${JSON.stringify(manifest)}`), 'Wasteland variables, components and asset manifest should not load remote URLs');
|
||||
assert(/@media \(max-width:\s*720px\)[\s\S]*?html\[data-theme='wasteland'\] body\s*\{[\s\S]*?background-position:\s*center,\s*center,\s*70% 50%;/.test(themeStyle), 'Wasteland 720px layout should reposition and darken the local background');
|
||||
assert(
|
||||
/@media \(min-width:\s*721px\) and \(max-width:\s*1100px\)[\s\S]*?html\[data-theme='wasteland'\] body\s*\{[^}]*background-position:\s*center,\s*center,\s*75% 50%;/.test(highFidelityStyle),
|
||||
'Wasteland 721-1100px layout should reveal the right side of the local background'
|
||||
);
|
||||
assert(
|
||||
/@media \(max-width:\s*720px\)[\s\S]*?html\[data-theme='wasteland'\] body\s*\{[^}]*background-position:\s*center,\s*center,\s*75% 50%;/.test(highFidelityStyle),
|
||||
'Wasteland 720px layout should keep the local background right-aligned'
|
||||
);
|
||||
assert(/@media \(max-width:\s*560px\)[\s\S]*?html\[data-theme='wasteland'\] \.input-wrapper\s*\{[\s\S]*?min-width:\s*0;[\s\S]*?min-height:\s*48px;/.test(themeStyle), 'Wasteland 560px layout should keep the composer within the viewport');
|
||||
assert(/@media \(prefers-reduced-motion:\s*reduce\)[\s\S]*?html\[data-theme='wasteland'\] \*[\s\S]*?animation:\s*none !important;[\s\S]*?transition:\s*none !important;/.test(themeStyle), 'Wasteland motion should respect reduced-motion preferences');
|
||||
|
||||
@@ -1055,8 +1128,8 @@ function assertFrontendWastelandThemeContract() {
|
||||
assert(contrast('#c9bda6', backgroundColor) >= 4.5, `Wasteland muted text should reach AA contrast on ${backgroundColor}`);
|
||||
});
|
||||
|
||||
assert(indexSource.includes('style.css?v=20260717-wasteland-hifi-23'), 'Wasteland stylesheet should share the cache-busted theme bundle URL');
|
||||
assert(indexSource.includes('app.js?v=20260718-theme-bundle-4'), 'Wasteland registration should share the cache-busted theme bundle URL');
|
||||
assert(indexSource.includes('style.css?v=20260718-wasteland-bg-right-4'), 'Wasteland stylesheet should share the cache-busted theme bundle URL');
|
||||
assert(indexSource.includes('app.js?v=20260718-wasteland-bg-right-4'), 'Wasteland registration should share the cache-busted theme bundle URL');
|
||||
}
|
||||
|
||||
function assertFrontendCcwebPromptContract() {
|
||||
@@ -2730,6 +2803,11 @@ async function main() {
|
||||
console.log('Wasteland theme regression checks passed.');
|
||||
return;
|
||||
}
|
||||
if (regressionTarget === 'sidebar-collapse') {
|
||||
assertFrontendSidebarCollapseContract();
|
||||
console.log('Sidebar collapse regression checks passed.');
|
||||
return;
|
||||
}
|
||||
if (regressionTarget === 'runtime-image-send') {
|
||||
await runRuntimeImageSendTarget();
|
||||
console.log('Runtime image send regression checks passed.');
|
||||
@@ -2748,6 +2826,7 @@ async function main() {
|
||||
assertUnlimitedImageAttachmentsContract();
|
||||
assertFrontendGildedThemeContract();
|
||||
assertFrontendWastelandThemeContract();
|
||||
assertFrontendSidebarCollapseContract();
|
||||
assertFrontendGenerationControlsContract();
|
||||
assertFrontendComposerMcpContract();
|
||||
assertComposerSlashRoutingContract();
|
||||
|
||||
Reference in New Issue
Block a user