优化目录候选文件名显示并重新打包
This commit is contained in:
@@ -671,7 +671,7 @@ function assertFrontendSidebarCollapseContract() {
|
||||
'Rich themes should provide isolated rail treatments on top of the shared semantic fallback'
|
||||
);
|
||||
assert(
|
||||
indexSource.includes('style.css?v=20260818-goal-mode-label')
|
||||
indexSource.includes('style.css?v=20260911-file-suggestion')
|
||||
&& indexSource.includes('app.js?v=__CC_WEB_FRONTEND_ASSET_VERSION__'),
|
||||
'Sidebar interaction assets should share the reviewed cache-busting version'
|
||||
);
|
||||
@@ -900,6 +900,42 @@ function assertFrontendComposerMcpContract() {
|
||||
assert(menuStart >= 0 && menuEnd > menuStart, 'Frontend should define showCmdMenu before requestComposerSuggestions');
|
||||
const menuBlock = source.slice(menuStart, menuEnd);
|
||||
assert(/item\.kind\s*===\s*'mcp'/.test(menuBlock) && menuBlock.includes("'MCP'"), 'Composer menu should render MCP item labels');
|
||||
assert(
|
||||
source.includes('function getComposerFileSuggestionDisplay(item)')
|
||||
&& menuBlock.includes('getComposerFileSuggestionDisplay(item)')
|
||||
&& menuBlock.includes("' file-suggestion'")
|
||||
&& menuBlock.includes('escapeHtmlAttr(fileDisplay.fullPath)'),
|
||||
'Composer file suggestions should show the basename first while retaining the full path as a tooltip'
|
||||
);
|
||||
const fileDisplaySource = extractFunctionSource(source, 'getComposerFileSuggestionDisplay');
|
||||
const fileDisplayApi = new Function(`${fileDisplaySource}; return getComposerFileSuggestionDisplay;`)();
|
||||
const nestedFileDisplay = fileDisplayApi({
|
||||
kind: 'file',
|
||||
itemType: 'file',
|
||||
name: '01_设计书说明文档/wayfinder/README.md',
|
||||
label: '@01_设计书说明文档/wayfinder/README.md',
|
||||
insertion: '@01_设计书说明文档/wayfinder/README.md',
|
||||
});
|
||||
assert(nestedFileDisplay.primaryText === 'README.md', 'Nested file suggestions should keep the final filename as the primary text');
|
||||
assert(nestedFileDisplay.secondaryText === '@01_设计书说明文档/wayfinder/', 'Nested file suggestions should show the parent path as secondary text');
|
||||
assert(nestedFileDisplay.fullPath === '@01_设计书说明文档/wayfinder/README.md', 'Nested file suggestions should retain the complete insertion path');
|
||||
const nestedDirectoryDisplay = fileDisplayApi({
|
||||
kind: 'file',
|
||||
itemType: 'directory',
|
||||
name: '01_设计书说明文档/wayfinder/hooks',
|
||||
label: '@01_设计书说明文档/wayfinder/hooks/',
|
||||
insertion: '@01_设计书说明文档/wayfinder/hooks/',
|
||||
});
|
||||
assert(nestedDirectoryDisplay.primaryText === 'hooks/', 'Nested directory suggestions should keep the final directory name and slash visible');
|
||||
assert(nestedDirectoryDisplay.secondaryText === '@01_设计书说明文档/wayfinder/', 'Nested directory suggestions should show the parent path as secondary text');
|
||||
const styleSource = fs.readFileSync(PUBLIC_STYLE_PATH, 'utf8');
|
||||
assert(
|
||||
/\.cmd-menu\s*\{[\s\S]*?width:\s*min\(560px,\s*calc\(100vw\s*-\s*32px\)\)/.test(styleSource)
|
||||
&& styleSource.includes('.cmd-item.file-suggestion .cmd-item-cmd')
|
||||
&& styleSource.includes('.cmd-item.file-suggestion .cmd-item-desc')
|
||||
&& /\.cmd-menu\s*\{[^}]*left:\s*10px;[^}]*right:\s*10px;[^}]*width:\s*auto;/.test(styleSource),
|
||||
'Composer menu should reserve enough width and style file path hierarchy'
|
||||
);
|
||||
const selectStart = source.indexOf('function selectComposerItemByIndex(index)');
|
||||
const selectEnd = source.indexOf('\n function selectCmdMenuItem()', selectStart);
|
||||
assert(selectStart >= 0 && selectEnd > selectStart, 'Frontend should define selectComposerItemByIndex before selectCmdMenuItem');
|
||||
@@ -1154,7 +1190,7 @@ function assertPlanListProgressContract() {
|
||||
assert(extractorSource.includes('references/source-assets/wasteland-icon-sheet.webp'), 'Plan progress extractor should read the archived source sheet');
|
||||
assert(!extractorSource.includes('sessions/_attachments'), 'Plan progress extractor should not depend on temporary session attachments');
|
||||
|
||||
assert(indexSource.includes('style.css?v=20260818-goal-mode-label'), 'Plan progress CSS should use the current cache-busted URL');
|
||||
assert(indexSource.includes('style.css?v=20260911-file-suggestion'), 'Plan progress CSS should use the current cache-busted URL');
|
||||
assert(indexSource.includes('app.js?v=__CC_WEB_FRONTEND_ASSET_VERSION__'), 'Plan progress frontend logic should use the dynamic cache-busted URL');
|
||||
}
|
||||
|
||||
@@ -1270,7 +1306,7 @@ 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=20260818-goal-mode-label'), 'Theme bundle stylesheet should use the current cache-busted asset URL');
|
||||
assert(indexSource.includes('style.css?v=20260911-file-suggestion'), 'Theme bundle stylesheet should use the current cache-busted asset URL');
|
||||
assert(indexSource.includes('app.js?v=__CC_WEB_FRONTEND_ASSET_VERSION__'), 'Theme bundle app script should use the dynamic cache-busted asset URL');
|
||||
}
|
||||
|
||||
@@ -1524,7 +1560,7 @@ function assertFrontendWastelandThemeContract() {
|
||||
assert(contrast('#c9bda6', backgroundColor) >= 4.5, `Wasteland muted text should reach AA contrast on ${backgroundColor}`);
|
||||
});
|
||||
|
||||
assert(indexSource.includes('style.css?v=20260818-goal-mode-label'), 'Wasteland stylesheet should share the cache-busted theme bundle URL');
|
||||
assert(indexSource.includes('style.css?v=20260911-file-suggestion'), 'Wasteland stylesheet should share the cache-busted theme bundle URL');
|
||||
assert(indexSource.includes('app.js?v=__CC_WEB_FRONTEND_ASSET_VERSION__'), 'Wasteland registration should share the dynamic cache-busted theme bundle URL');
|
||||
}
|
||||
|
||||
@@ -2554,7 +2590,7 @@ function assertGoalModeTitleContract() {
|
||||
assert(styleSource.includes('.msg.user.goal-message .msg-bubble'), 'Goal label styling should remain scoped to user Goal bubbles');
|
||||
assert(styleSource.includes('.goal-message-label'), 'Goal mode label should have a dedicated style');
|
||||
assert(styleSource.includes("html[data-theme='wasteland'] .msg.user.goal-message .goal-message-label"), 'Wasteland should provide a dedicated Goal label treatment');
|
||||
assert(indexSource.includes('style.css?v=20260818-goal-mode-label'), 'Goal label CSS should use the current cache-busted URL');
|
||||
assert(indexSource.includes('style.css?v=20260911-file-suggestion'), 'Goal label CSS should use the current cache-busted URL');
|
||||
assert(serverSource.includes('GOAL_DERIVED_TITLE_MAX_CHARS = 60'), 'Goal-derived titles should keep the 60-character limit');
|
||||
assert(serverSource.includes('function isDefaultConversationTitle(session)'), 'Goal title updates should use a default-title guard');
|
||||
assert(serverSource.includes('function applyDerivedGoalConversationTitle(session, objective)'), 'Goal title updates should stay in the /goal-specific server path');
|
||||
@@ -5568,7 +5604,7 @@ function assertAdvancedSessionSearchContract() {
|
||||
'Existing sidebar search clear control contract should remain unchanged');
|
||||
assert(indexSource.includes('id="advanced-search-panel"') && indexSource.includes('id="advanced-search-results"'),
|
||||
'Advanced search workspace should expose stable panel and result hooks');
|
||||
assert(indexSource.includes('style.css?v=20260818-goal-mode-label')
|
||||
assert(indexSource.includes('style.css?v=20260911-file-suggestion')
|
||||
&& indexSource.includes('app.js?v=__CC_WEB_FRONTEND_ASSET_VERSION__'),
|
||||
'Advanced search CSS and frontend script should share the reviewed cache-bust');
|
||||
|
||||
@@ -5685,7 +5721,7 @@ function assertUsageStatisticsContract() {
|
||||
'Usage dashboard should be a chat-main-local workspace');
|
||||
assert(indexSource.includes('class="usage-dashboard-open"') && !indexSource.includes('class="settings-btn usage-dashboard-open"'),
|
||||
'Usage entry must use its own class so theme-specific settings pseudo-elements cannot leak');
|
||||
assert(indexSource.includes('style.css?v=20260818-goal-mode-label')
|
||||
assert(indexSource.includes('style.css?v=20260911-file-suggestion')
|
||||
&& indexSource.includes('vendor/echarts.min.js?v=5.6.0')
|
||||
&& indexSource.includes('app.js?v=__CC_WEB_FRONTEND_ASSET_VERSION__'),
|
||||
'Usage dashboard CSS, local ECharts runtime, and frontend script should share the current asset contract');
|
||||
@@ -6433,6 +6469,11 @@ async function main() {
|
||||
console.log('Composer slash routing regression checks passed.');
|
||||
return;
|
||||
}
|
||||
if (regressionTarget === 'composer-file-display') {
|
||||
assertFrontendComposerMcpContract();
|
||||
console.log('Composer file display regression checks passed.');
|
||||
return;
|
||||
}
|
||||
if (regressionTarget === 'codexapp-unrouted-routing') {
|
||||
assertCodexAppUnroutedNotificationRoutingContract();
|
||||
console.log('Codex App unrouted routing regression checks passed.');
|
||||
@@ -6847,6 +6888,12 @@ async function main() {
|
||||
'command = "definitely-missing-mcp-command"',
|
||||
].join('\n'));
|
||||
fs.writeFileSync(path.join(codexInitCwd, 'context.txt'), 'Composer file context body.');
|
||||
const nestedComposerRoot = path.join(codexInitCwd, '01_设计书说明文档', 'wayfinder');
|
||||
const nestedComposerHooks = path.join(nestedComposerRoot, 'hooks', 'deep');
|
||||
mkdirp(nestedComposerHooks);
|
||||
fs.writeFileSync(path.join(nestedComposerRoot, 'README.md'), 'Nested composer file context body.');
|
||||
fs.writeFileSync(path.join(nestedComposerRoot, 'hooks', 'index.md'), 'Nested composer directory file.');
|
||||
fs.writeFileSync(path.join(nestedComposerHooks, 'notes.txt'), 'Three-level composer file context body.');
|
||||
ws.send(JSON.stringify({ type: 'new_session', agent: 'codex', cwd: codexInitCwd, mode: 'plan' }));
|
||||
const codexSession = await nextMessage(messages, ws, (msg) => msg.type === 'session_info' && msg.agent === 'codex' && msg.cwd === codexInitCwd);
|
||||
assert(codexSession.mode === 'plan', 'Codex new_session should follow requested mode');
|
||||
@@ -6964,6 +7011,35 @@ async function main() {
|
||||
assert(fileComposer.items.some((item) => item.kind === 'file' && item.name === 'context.txt'), 'Composer file suggestions should include cwd file');
|
||||
assert(!fileComposer.items.some((item) => item.kind === 'mcp'), 'Composer file suggestions should not include MCP tools');
|
||||
|
||||
ws.send(JSON.stringify({
|
||||
type: 'composer_suggestions',
|
||||
requestId: 'reg-file-nested',
|
||||
trigger: '@',
|
||||
query: '01_设计书说明文档/wayfinder/',
|
||||
sessionId: codexSession.sessionId,
|
||||
agent: 'codex',
|
||||
}));
|
||||
const nestedFileComposer = await nextMessage(messages, ws, (msg) => msg.type === 'composer_suggestions' && msg.requestId === 'reg-file-nested');
|
||||
const nestedFileSuggestion = nestedFileComposer.items.find((item) => item.kind === 'file' && item.name === '01_设计书说明文档/wayfinder/README.md');
|
||||
const nestedDirectorySuggestion = nestedFileComposer.items.find((item) => item.kind === 'file' && item.itemType === 'directory' && item.name === '01_设计书说明文档/wayfinder/hooks');
|
||||
assert(nestedFileSuggestion, 'Composer file suggestions should include a two-level nested file');
|
||||
assert(nestedDirectorySuggestion, 'Composer file suggestions should include a two-level nested directory');
|
||||
assert(nestedFileSuggestion.label === '@01_设计书说明文档/wayfinder/README.md' && nestedFileSuggestion.insertion === nestedFileSuggestion.label, 'Nested file suggestions should retain the complete insertion path');
|
||||
assert(nestedDirectorySuggestion.label === '@01_设计书说明文档/wayfinder/hooks/' && nestedDirectorySuggestion.insertion === nestedDirectorySuggestion.label && nestedDirectorySuggestion.appendSpace === false, 'Nested directory suggestions should retain the trailing slash and continue completion');
|
||||
|
||||
ws.send(JSON.stringify({
|
||||
type: 'composer_suggestions',
|
||||
requestId: 'reg-file-deep',
|
||||
trigger: '@',
|
||||
query: '01_设计书说明文档/wayfinder/hooks/deep/',
|
||||
sessionId: codexSession.sessionId,
|
||||
agent: 'codex',
|
||||
}));
|
||||
const deepFileComposer = await nextMessage(messages, ws, (msg) => msg.type === 'composer_suggestions' && msg.requestId === 'reg-file-deep');
|
||||
const deepFileSuggestion = deepFileComposer.items.find((item) => item.kind === 'file' && item.name === '01_设计书说明文档/wayfinder/hooks/deep/notes.txt');
|
||||
assert(deepFileSuggestion, 'Composer file suggestions should include a three-level nested file');
|
||||
assert(deepFileSuggestion.label === '@01_设计书说明文档/wayfinder/hooks/deep/notes.txt' && deepFileSuggestion.insertion === deepFileSuggestion.label, 'Three-level file suggestions should retain the complete insertion path');
|
||||
|
||||
ws.send(JSON.stringify({
|
||||
type: 'message',
|
||||
text: '@shipit @quick-note @context.txt $regression-skill $project-skill run composer regression',
|
||||
|
||||
Reference in New Issue
Block a user