chore: rebuild release package

This commit is contained in:
shiyue
2026-07-18 10:14:38 +08:00
parent fa15b54469
commit a76de06c47
112 changed files with 8858 additions and 399 deletions

View File

@@ -161,6 +161,19 @@
desc: '炭黑底配低饱和玫瑰色,暗色里保留一点温度。',
swatches: ['#151112', '#2a2022', '#e68193', '#67c587'],
},
{
value: 'gilded',
label: 'Warframe Wasteland',
desc: '暖象牙与焦土铜铺开 Orokin 荒野档案,冷青能量只作细节点缀。',
swatches: ['#fbf3e5', '#efe4d2', '#7a3f20', '#147276'],
hidden: true,
},
{
value: 'wasteland',
label: '暗金荒野',
desc: '黑铁面板悬浮于荒野骑士主视觉之上,以旧金、余烬与状态绿标记工作层级。',
swatches: ['#050707', '#171511', '#c49a5a', '#6f783f'],
},
];
// --- State ---
@@ -310,9 +323,39 @@
window.addEventListener('resize', setVH);
window.addEventListener('orientationchange', () => setTimeout(setVH, 100));
function buildWelcomeMarkup(agent) {
const label = AGENT_LABELS[agent] || AGENT_LABELS.claude;
return `<div class="welcome-msg"><div class="welcome-icon">✿</div><h3>欢迎使用 CC-Web</h3><p>开始与 ${label} 对话</p></div>`;
function getWelcomeProjectName(cwd = currentCwd) {
return getPathLeaf(cwd) || '当前项目';
}
function getWelcomeCopy(cwd = currentCwd) {
return `你正在操作 ${getWelcomeProjectName(cwd)}`;
}
function syncWelcomeCopy(cwd = currentCwd) {
const copy = messagesDiv.querySelector('[data-welcome-project-copy]');
if (copy) copy.textContent = getWelcomeCopy(cwd);
}
function buildWelcomeMarkup(cwd = currentCwd) {
return `<div class="welcome-msg">
<section class="warframe-hero" aria-labelledby="warframe-welcome-title">
<div class="warframe-hero-art" aria-hidden="true">
<img class="warframe-hero-image" src="assets/themes/gilded-wasteland.png" alt="" draggable="false" decoding="async">
<span class="warframe-hero-orbit"></span>
<span class="warframe-hero-emblem"></span>
</div>
<div class="wasteland-welcome-art" aria-hidden="true">
<div class="wasteland-welcome-surface"></div>
<img class="wasteland-welcome-frame" src="assets/themes/wasteland/frames/welcome-card.png" alt="" draggable="false" decoding="async">
</div>
<div class="welcome-icon">✿</div>
<div class="welcome-copy">
<div class="welcome-kicker">OROKIN FIELD ARCHIVE · CC-WEB</div>
<h3 id="warframe-welcome-title" data-welcome-project-copy>${escapeHtml(getWelcomeCopy(cwd))}</h3>
<p>本次你要构建什么?</p>
</div>
</section>
</div>`;
}
function normalizeAgent(agent) {
@@ -526,6 +569,8 @@
const text = document.createElement('div');
text.className = 'note-text';
text.textContent = message.text;
const attachmentSummary = renderAttachmentPreviews(message.attachments);
if (attachmentSummary) text.insertAdjacentHTML('beforeend', attachmentSummary);
const actions = document.createElement('div');
actions.className = 'note-actions';
@@ -738,16 +783,21 @@
return true;
}
function getQueuedMessageValidationError(content) {
function getQueuedMessageValidationError(content, attachments = []) {
const text = String(content || '').trim();
const attachmentList = Array.isArray(attachments) ? attachments : [];
if (!supportsQueuedSend()) return '排队发送仅支持 Codex App。';
if (!String(content || '').trim()) return '排队内容不能为空。';
if (String(content || '').trim().startsWith('/')) return '排队发送暂不支持 slash 指令。';
if (!text && attachmentList.length === 0) return '排队内容不能为空。';
if (text.startsWith('/')) return '排队发送暂不支持 slash 指令。';
return '';
}
function addQueuedMessage(text, options = {}) {
const content = String(text || '').trim();
const validationError = getQueuedMessageValidationError(content);
const attachments = Array.isArray(options.attachments)
? options.attachments.map((attachment) => ({ ...attachment }))
: [];
const validationError = getQueuedMessageValidationError(content, attachments);
if (validationError) {
appendError(validationError);
return false;
@@ -755,6 +805,7 @@
const message = {
id: `queued-${Date.now().toString(36)}-${++queuedMessageSeq}`,
text: content,
attachments,
createdAt: Date.now(),
};
getCurrentQueue(true).push(message);
@@ -765,15 +816,14 @@
function queueMessageFromInput() {
const text = msgInput.value.trim();
if (!text || isBlockingSessionLoad()) return;
if ((!text && pendingAttachments.length === 0) || isBlockingSessionLoad()) return;
hideCmdMenu();
hideOptionPicker();
if (pendingAttachments.length > 0) {
appendError('排队发送暂不支持图片附件,请先移除图片。');
return;
}
if (addQueuedMessage(text)) {
const attachments = pendingAttachments.map((attachment) => ({ ...attachment }));
if (addQueuedMessage(text, { attachments })) {
msgInput.value = '';
pendingAttachments = [];
renderPendingAttachments();
autoResize();
}
}
@@ -782,7 +832,7 @@
const found = findPendingNote(noteId);
if (!found) return;
const text = String(found.note.text || '').trim();
const validationError = getQueuedMessageValidationError(text);
const validationError = getQueuedMessageValidationError(text, []);
if (validationError) {
appendError(validationError);
return;
@@ -809,7 +859,10 @@
}
function removeQueuedMessage(queueId) {
if (!dropQueuedMessage(queueId)) return;
const message = dropQueuedMessage(queueId);
if (!message) return;
(Array.isArray(message.attachments) ? message.attachments : [])
.forEach((attachment) => deleteUploadedAttachment(attachment?.id));
renderPendingNotes({ scroll: false });
}
@@ -917,7 +970,7 @@
const save = () => {
const next = editor.value.trim();
const validationError = getQueuedMessageValidationError(next);
const validationError = getQueuedMessageValidationError(next, found.message.attachments);
if (validationError) {
appendError(validationError);
editor.focus();
@@ -980,15 +1033,18 @@
renderPendingNotes({ scroll: false });
const text = String(message?.text || '').trim();
if (!text) {
const attachments = Array.isArray(message?.attachments)
? message.attachments.map((attachment) => ({ ...attachment }))
: [];
if (!text && attachments.length === 0) {
scheduleQueuedMessageDrain();
return;
}
submitUserMessage(text);
submitUserMessage(text, attachments);
}
function normalizeTheme(theme) {
return THEME_OPTIONS.some((item) => item.value === theme) ? theme : 'washi';
return THEME_OPTIONS.some((item) => item.value === theme && !item.hidden) ? theme : 'washi';
}
function getThemeOption(theme) {
@@ -1034,7 +1090,7 @@
return `
${showSectionTitle ? '<div class="settings-section-title">界面主题</div>' : ''}
<div class="theme-grid">
${THEME_OPTIONS.map((theme) => `
${THEME_OPTIONS.filter((theme) => !theme.hidden).map((theme) => `
<button class="theme-card${theme.value === currentTheme ? ' active' : ''}" type="button" data-theme-value="${theme.value}">
<div class="theme-card-preview">
${theme.swatches.map((color) => `<span class="theme-card-swatch" style="background:${color}"></span>`).join('')}
@@ -4099,6 +4155,7 @@
}
chatCwd.disabled = !currentCwd;
chatCwd.hidden = !currentCwd;
syncWelcomeCopy(currentCwd);
}
function currentSessionWaitState() {
@@ -4212,7 +4269,7 @@
updateSessionIdBadge();
updateCwdBadge();
updateReloadMcpButtonUI();
messagesDiv.innerHTML = buildWelcomeMarkup(currentAgent);
messagesDiv.innerHTML = buildWelcomeMarkup(currentCwd);
setStatsDisplay(null);
renderPendingAttachments();
renderPendingNotes({ scroll: false });
@@ -6541,6 +6598,48 @@
return title.length > 36 ? `${title.slice(0, 36)}` : title;
}
const COLLAB_AGENT_AUTO_TITLE_TOKEN_LABELS = {
plan: '计划',
review: '审查',
reviewer: '审查',
backend: '后端',
frontend: '前端',
state: '状态',
implement: '实现',
implementation: '实现',
check: '检查',
test: '测试',
research: '调研',
design: '设计',
release: '发布',
runtime: '运行时',
audit: '审计',
fix: '修复',
document: '文档',
docs: '文档',
quality: '质量',
trellis: 'Trellis',
};
function joinCollabAgentAutoTitleParts(parts) {
return parts.reduce((title, part) => {
if (!title) return part;
const shouldJoinDirectly = /[\u4e00-\u9fff]$/.test(title) && /^[\u4e00-\u9fff]/.test(part);
return shouldJoinDirectly ? `${title}${part}` : `${title} ${part}`;
}, '');
}
function readableCollabAgentAutoTitle(value) {
const text = cleanCollabAgentText(value);
if (!/^[a-z0-9]+(?:[_-][a-z0-9]+)+$/.test(text)) return text;
const parts = text.split(/[_-]+/).map((token) => {
const mapped = COLLAB_AGENT_AUTO_TITLE_TOKEN_LABELS[token];
if (mapped) return mapped;
return token ? `${token.charAt(0).toUpperCase()}${token.slice(1)}` : '';
}).filter(Boolean);
return joinCollabAgentAutoTitleParts(parts);
}
function collabAgentTaskDescription(state = {}, fallbackPrompt = '') {
return cleanCollabAgentText(
state.taskDescription
@@ -6556,6 +6655,7 @@
function pickCollabAgentTitle(state, id, index) {
const taskDescription = cleanCollabAgentText(state?.taskDescription || state?.prompt || '');
const canReadAutomaticTitle = state?.hasReadableSourceTitle !== false;
const titleCandidates = [
state.label,
state.title,
@@ -6564,7 +6664,9 @@
];
for (const value of titleCandidates) {
const candidate = cleanCollabAgentText(value);
if (candidate && !isGenericCollabAgentLabel(candidate, id)) return candidate;
if (candidate && !isGenericCollabAgentLabel(candidate, id)) {
return canReadAutomaticTitle ? readableCollabAgentAutoTitle(candidate) : candidate;
}
}
const promptTitle = collabAgentTitleFromPrompt(taskDescription);
if (promptTitle) return promptTitle;
@@ -6589,6 +6691,7 @@
const incomingHasReadableSourceTitle = incomingState.hasReadableSourceTitle == null
? hasReadableCollabAgentTitle(incomingState, id)
: incomingState.hasReadableSourceTitle === true;
const hasReadableSourceTitle = incomingHasReadableSourceTitle || previousState.hasReadableSourceTitle === true;
if (incomingHasReadableSourceTitle) {
nextState.label = pickCollabAgentTitle(incomingState, id, index);
} else {
@@ -6599,6 +6702,7 @@
}
return {
...nextState,
hasReadableSourceTitle,
label: pickCollabAgentTitle(nextState, id, index),
taskDescription,
};
@@ -6727,7 +6831,10 @@
return Object.entries(states).map(([id, value], index) => {
const state = value && typeof value === 'object' ? value : { status: value };
const taskDescription = collabAgentTaskDescription(state, data?.prompt || '');
const label = pickCollabAgentTitle({ ...state, taskDescription }, id, index);
const hasReadableSourceTitle = state.hasReadableSourceTitle == null
? hasReadableCollabAgentTitle(state, id)
: state.hasReadableSourceTitle === true;
const label = pickCollabAgentTitle({ ...state, taskDescription, hasReadableSourceTitle }, id, index);
const role = cleanCollabAgentText(state.role || state.agent || state.agentType || '');
let status = cleanCollabAgentText(state.status || state.state || 'pending') || 'pending';
if (state.closedAt && collabStateTone(status) !== 'closed') status = 'closed';
@@ -6742,7 +6849,7 @@
status,
detail,
taskDescription,
hasReadableSourceTitle: hasReadableCollabAgentTitle(state, id),
hasReadableSourceTitle,
};
});
}
@@ -7190,11 +7297,14 @@
stateEntries.forEach((entry, index) => {
const tone = collabStateTone(entry.status);
const displayTitle = pickCollabAgentTitle(entry, entry.id, index);
const descriptionText = summarizePrompt(entry.taskDescription);
const fallbackDescription = `子代理任务:${displayTitle}`;
const descriptionText = summarizePrompt(entry.taskDescription) || fallbackDescription;
const descriptionTitle = cleanCollabAgentText(entry.taskDescription) || fallbackDescription;
const item = document.createElement('div');
item.className = 'collab-agent-item';
item.title = [
displayTitle,
descriptionTitle,
entry.role ? `角色: ${entry.role}` : '',
entry.detail ? `结果: ${entry.detail}` : '',
entry.id ? `ID: ${entry.id}` : '',
@@ -7218,7 +7328,7 @@
const label = document.createElement('div');
label.className = 'collab-agent-item-label';
label.textContent = pickCollabAgentTitle(entry, entry.id, index);
label.textContent = displayTitle;
row.appendChild(label);
const chip = document.createElement('span');
@@ -7246,13 +7356,11 @@
}
item.appendChild(row);
if (descriptionText) {
const description = document.createElement('div');
description.className = 'collab-agent-item-description';
description.textContent = descriptionText;
description.title = entry.taskDescription;
item.appendChild(description);
}
const description = document.createElement('div');
description.className = 'collab-agent-item-description';
description.textContent = descriptionText;
description.title = descriptionTitle;
item.appendChild(description);
if (entry.id || entry.role) {
const footer = document.createElement('div');
@@ -7392,7 +7500,7 @@
messagesDiv.innerHTML = '';
clearUserMessageIndex();
if (messages.length === 0) {
messagesDiv.innerHTML = buildWelcomeMarkup(currentAgent);
messagesDiv.innerHTML = buildWelcomeMarkup(currentCwd);
updateUserOutlinePanel();
renderPendingNotes({ scroll: false });
scrollToBottom();
@@ -8142,6 +8250,7 @@
if (!isDragging) scrollbarEl.classList.remove('scrolling');
}, 1200);
}, { passive: true });
new ResizeObserver(updateScrollbar).observe(messagesDiv);
// Drag logic
@@ -8809,16 +8918,13 @@
hideOptionPicker();
if (runtimeInsert) {
if (pendingAttachments.length > 0) {
appendError('Codex App 运行中插入暂不支持图片附件,请先移除图片。');
return;
}
if (isKnownSlashCommandText(text)) {
appendError('Codex App 运行中暂不支持 slash 指令插入。');
return;
}
const attachments = pendingAttachments.map((attachment) => ({ ...attachment }));
const messageId = createLocalId('user');
const element = createMsgElement('user', text, [], { messageId, codexAppSteerStatus: 'pending' });
const element = createMsgElement('user', text, attachments, { messageId, codexAppSteerStatus: 'pending' });
const streamEl = document.getElementById('streaming-msg');
const shouldFollow = isNearBottom();
if (streamEl && streamEl.parentNode === messagesDiv) {
@@ -8838,8 +8944,10 @@
} else {
updateScrollbar();
}
send({ type: 'message', text, sessionId: currentSessionId, mode: currentMode, agent: currentAgent, clientMessageId: messageId });
send({ type: 'message', text, attachments, sessionId: currentSessionId, mode: currentMode, agent: currentAgent, clientMessageId: messageId });
msgInput.value = '';
pendingAttachments = [];
renderPendingAttachments();
autoResize();
return;
}