chore: rebuild release package
188
public/app.js
@@ -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;
|
||||
}
|
||||
|
||||
BIN
public/assets/themes/gilded-wasteland.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
public/assets/themes/warframe-ember-squad.jpg
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
public/assets/themes/warframe-orokin-figure.jpg
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
public/assets/themes/warframe-sacrifice.jpg
Normal file
|
After Width: | Height: | Size: 143 KiB |
BIN
public/assets/themes/wasteland/background.webp
Normal file
|
After Width: | Height: | Size: 134 KiB |
BIN
public/assets/themes/wasteland/frames/active-session.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
public/assets/themes/wasteland/frames/composer-action.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
public/assets/themes/wasteland/frames/composer-ornament.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
public/assets/themes/wasteland/frames/composer-shell.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
public/assets/themes/wasteland/frames/composer.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
151
public/assets/themes/wasteland/frames/manifest.json
Normal file
@@ -0,0 +1,151 @@
|
||||
[
|
||||
{
|
||||
"name": "new-chat",
|
||||
"source_box": [
|
||||
15,
|
||||
9,
|
||||
304,
|
||||
58
|
||||
],
|
||||
"size": [
|
||||
289,
|
||||
49
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "search",
|
||||
"source_box": [
|
||||
15,
|
||||
67,
|
||||
304,
|
||||
103
|
||||
],
|
||||
"size": [
|
||||
289,
|
||||
36
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "project",
|
||||
"source_box": [
|
||||
15,
|
||||
117,
|
||||
304,
|
||||
164
|
||||
],
|
||||
"size": [
|
||||
289,
|
||||
47
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "active-session",
|
||||
"source_box": [
|
||||
15,
|
||||
163,
|
||||
304,
|
||||
209
|
||||
],
|
||||
"size": [
|
||||
289,
|
||||
46
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "composer",
|
||||
"source_box": [
|
||||
557,
|
||||
830,
|
||||
1364,
|
||||
931
|
||||
],
|
||||
"size": [
|
||||
807,
|
||||
101
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "composer-action",
|
||||
"source_box": [
|
||||
1211,
|
||||
845,
|
||||
1257,
|
||||
891
|
||||
],
|
||||
"size": [
|
||||
46,
|
||||
46
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tool-call",
|
||||
"source_box": [
|
||||
421,
|
||||
603,
|
||||
1381,
|
||||
664
|
||||
],
|
||||
"size": [
|
||||
960,
|
||||
61
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "composer-shell",
|
||||
"derived_from": "composer.png",
|
||||
"source_box": [
|
||||
0,
|
||||
0,
|
||||
807,
|
||||
74
|
||||
],
|
||||
"size": [
|
||||
807,
|
||||
74
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "composer-ornament",
|
||||
"derived_from": "composer.png",
|
||||
"source_box": [
|
||||
130,
|
||||
50,
|
||||
547,
|
||||
51
|
||||
],
|
||||
"size": [
|
||||
547,
|
||||
51
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "welcome-card",
|
||||
"source": "2b88b81d-c5a8-4d92-a518-afeea691eade.webp",
|
||||
"source_box": [
|
||||
70,
|
||||
0,
|
||||
1165,
|
||||
875
|
||||
],
|
||||
"size": [
|
||||
1095,
|
||||
875
|
||||
],
|
||||
"output": "frames/welcome-card.png"
|
||||
},
|
||||
{
|
||||
"name": "welcome-card-surface",
|
||||
"source": "2b88b81d-c5a8-4d92-a518-afeea691eade.webp",
|
||||
"source_box": [
|
||||
350,
|
||||
250,
|
||||
950,
|
||||
600
|
||||
],
|
||||
"size": [
|
||||
600,
|
||||
350
|
||||
],
|
||||
"output": "textures/welcome-card-surface.webp"
|
||||
}
|
||||
]
|
||||
BIN
public/assets/themes/wasteland/frames/new-chat.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
public/assets/themes/wasteland/frames/project.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
public/assets/themes/wasteland/frames/search.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
public/assets/themes/wasteland/frames/tool-call.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
public/assets/themes/wasteland/frames/welcome-card.png
Normal file
|
After Width: | Height: | Size: 222 KiB |
BIN
public/assets/themes/wasteland/icons/ai.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
public/assets/themes/wasteland/icons/attachment.png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
public/assets/themes/wasteland/icons/chat.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
public/assets/themes/wasteland/icons/check.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
public/assets/themes/wasteland/icons/close.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
public/assets/themes/wasteland/icons/copy.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
public/assets/themes/wasteland/icons/dropdown.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
121
public/assets/themes/wasteland/icons/manifest.json
Normal file
@@ -0,0 +1,121 @@
|
||||
{
|
||||
"new-chat": {
|
||||
"source": "43bd9ed6-2ba6-4c9d-bd9d-25b068cb4711.webp",
|
||||
"card": "R1C1",
|
||||
"variant": "32px",
|
||||
"bbox": [204, 85, 69, 65],
|
||||
"output": "icons/new-chat.png"
|
||||
},
|
||||
"search": {
|
||||
"source": "43bd9ed6-2ba6-4c9d-bd9d-25b068cb4711.webp",
|
||||
"card": "R1C3",
|
||||
"variant": "32px",
|
||||
"bbox": [746, 86, 62, 62],
|
||||
"output": "icons/search.png"
|
||||
},
|
||||
"dropdown": {
|
||||
"source": "43bd9ed6-2ba6-4c9d-bd9d-25b068cb4711.webp",
|
||||
"card": "R1C2",
|
||||
"variant": "32px",
|
||||
"bbox": [480, 98, 59, 45],
|
||||
"output": "icons/dropdown.png"
|
||||
},
|
||||
"theme": {
|
||||
"source": "43bd9ed6-2ba6-4c9d-bd9d-25b068cb4711.webp",
|
||||
"card": "R2C1",
|
||||
"variant": "32px",
|
||||
"bbox": [204, 236, 70, 65],
|
||||
"output": "icons/theme.png"
|
||||
},
|
||||
"ai": {
|
||||
"source": "43bd9ed6-2ba6-4c9d-bd9d-25b068cb4711.webp",
|
||||
"card": "R2C2",
|
||||
"variant": "32px",
|
||||
"bbox": [476, 236, 65, 65],
|
||||
"output": "icons/ai.png"
|
||||
},
|
||||
"status": {
|
||||
"source": "43bd9ed6-2ba6-4c9d-bd9d-25b068cb4711.webp",
|
||||
"card": "R2C3",
|
||||
"variant": "32px",
|
||||
"bbox": [749, 242, 53, 53],
|
||||
"output": "icons/status.png"
|
||||
},
|
||||
"attachment": {
|
||||
"source": "43bd9ed6-2ba6-4c9d-bd9d-25b068cb4711.webp",
|
||||
"card": "R2C5",
|
||||
"variant": "32px",
|
||||
"bbox": [1277, 236, 49, 60],
|
||||
"output": "icons/attachment.png"
|
||||
},
|
||||
"send": {
|
||||
"source": "43bd9ed6-2ba6-4c9d-bd9d-25b068cb4711.webp",
|
||||
"card": "R2C6",
|
||||
"variant": "32px",
|
||||
"bbox": [1533, 237, 63, 57],
|
||||
"output": "icons/send.png"
|
||||
},
|
||||
"stop": {
|
||||
"source": "43bd9ed6-2ba6-4c9d-bd9d-25b068cb4711.webp",
|
||||
"card": "R3C1",
|
||||
"variant": "32px",
|
||||
"bbox": [211, 392, 56, 56],
|
||||
"output": "icons/stop.png"
|
||||
},
|
||||
"settings": {
|
||||
"source": "43bd9ed6-2ba6-4c9d-bd9d-25b068cb4711.webp",
|
||||
"card": "R3C2",
|
||||
"variant": "32px",
|
||||
"bbox": [476, 389, 65, 62],
|
||||
"output": "icons/settings.png"
|
||||
},
|
||||
"user": {
|
||||
"source": "43bd9ed6-2ba6-4c9d-bd9d-25b068cb4711.webp",
|
||||
"card": "R3C3",
|
||||
"variant": "32px",
|
||||
"bbox": [743, 389, 65, 62],
|
||||
"output": "icons/user.png"
|
||||
},
|
||||
"terminal": {
|
||||
"source": "43bd9ed6-2ba6-4c9d-bd9d-25b068cb4711.webp",
|
||||
"card": "R3C4",
|
||||
"variant": "32px",
|
||||
"bbox": [1009, 393, 59, 55],
|
||||
"output": "icons/terminal.png"
|
||||
},
|
||||
"copy": {
|
||||
"source": "43bd9ed6-2ba6-4c9d-bd9d-25b068cb4711.webp",
|
||||
"card": "R3C5",
|
||||
"variant": "32px",
|
||||
"bbox": [1278, 392, 48, 56],
|
||||
"output": "icons/copy.png"
|
||||
},
|
||||
"refresh": {
|
||||
"source": "43bd9ed6-2ba6-4c9d-bd9d-25b068cb4711.webp",
|
||||
"card": "R4C1",
|
||||
"variant": "32px",
|
||||
"bbox": [211, 541, 57, 57],
|
||||
"output": "icons/refresh.png"
|
||||
},
|
||||
"chat": {
|
||||
"source": "43bd9ed6-2ba6-4c9d-bd9d-25b068cb4711.webp",
|
||||
"card": "R4C3",
|
||||
"variant": "32px",
|
||||
"bbox": [745, 541, 62, 55],
|
||||
"output": "icons/chat.png"
|
||||
},
|
||||
"close": {
|
||||
"source": "43bd9ed6-2ba6-4c9d-bd9d-25b068cb4711.webp",
|
||||
"card": "R5C3",
|
||||
"variant": "32px",
|
||||
"bbox": [749, 690, 53, 53],
|
||||
"output": "icons/close.png"
|
||||
},
|
||||
"check": {
|
||||
"source": "43bd9ed6-2ba6-4c9d-bd9d-25b068cb4711.webp",
|
||||
"card": "R5C4",
|
||||
"variant": "32px",
|
||||
"bbox": [1010, 693, 56, 47],
|
||||
"output": "icons/check.png"
|
||||
}
|
||||
}
|
||||
BIN
public/assets/themes/wasteland/icons/new-chat.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
public/assets/themes/wasteland/icons/refresh.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
public/assets/themes/wasteland/icons/search.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
public/assets/themes/wasteland/icons/send.png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
public/assets/themes/wasteland/icons/settings.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
public/assets/themes/wasteland/icons/status.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
public/assets/themes/wasteland/icons/stop.png
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
BIN
public/assets/themes/wasteland/icons/terminal.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
public/assets/themes/wasteland/icons/theme.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
public/assets/themes/wasteland/icons/ui/ai.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
public/assets/themes/wasteland/icons/ui/attachment.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
public/assets/themes/wasteland/icons/ui/dropdown.png
Normal file
|
After Width: | Height: | Size: 336 B |
262
public/assets/themes/wasteland/icons/ui/manifest.json
Normal file
@@ -0,0 +1,262 @@
|
||||
[
|
||||
{
|
||||
"name": "search",
|
||||
"variant": "16",
|
||||
"slot_box": [
|
||||
596,
|
||||
80,
|
||||
60,
|
||||
75
|
||||
],
|
||||
"detected_box": [
|
||||
607,
|
||||
102,
|
||||
34,
|
||||
35
|
||||
],
|
||||
"size": [
|
||||
20,
|
||||
20
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dropdown",
|
||||
"variant": "16",
|
||||
"slot_box": [
|
||||
329,
|
||||
80,
|
||||
60,
|
||||
75
|
||||
],
|
||||
"detected_box": [
|
||||
343,
|
||||
109,
|
||||
30,
|
||||
24
|
||||
],
|
||||
"size": [
|
||||
14,
|
||||
10
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "theme",
|
||||
"variant": "24",
|
||||
"slot_box": [
|
||||
126,
|
||||
231,
|
||||
74,
|
||||
75
|
||||
],
|
||||
"detected_box": [
|
||||
136,
|
||||
245,
|
||||
45,
|
||||
51
|
||||
],
|
||||
"size": [
|
||||
26,
|
||||
26
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ai",
|
||||
"variant": "32",
|
||||
"slot_box": [
|
||||
461,
|
||||
231,
|
||||
89,
|
||||
75
|
||||
],
|
||||
"detected_box": [
|
||||
477,
|
||||
236,
|
||||
63,
|
||||
65
|
||||
],
|
||||
"size": [
|
||||
36,
|
||||
36
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"variant": "16",
|
||||
"slot_box": [
|
||||
596,
|
||||
231,
|
||||
60,
|
||||
75
|
||||
],
|
||||
"detected_box": [
|
||||
608,
|
||||
255,
|
||||
32,
|
||||
33
|
||||
],
|
||||
"size": [
|
||||
16,
|
||||
16
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "attachment",
|
||||
"variant": "24",
|
||||
"slot_box": [
|
||||
1187,
|
||||
231,
|
||||
74,
|
||||
75
|
||||
],
|
||||
"detected_box": [
|
||||
1202,
|
||||
245,
|
||||
40,
|
||||
47
|
||||
],
|
||||
"size": [
|
||||
26,
|
||||
28
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "send",
|
||||
"variant": "32",
|
||||
"slot_box": [
|
||||
1523,
|
||||
231,
|
||||
86,
|
||||
75
|
||||
],
|
||||
"detected_box": [
|
||||
1534,
|
||||
238,
|
||||
61,
|
||||
56
|
||||
],
|
||||
"size": [
|
||||
30,
|
||||
30
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "queue",
|
||||
"variant": "32",
|
||||
"slot_box": [
|
||||
992,
|
||||
231,
|
||||
88,
|
||||
75
|
||||
],
|
||||
"detected_box": [
|
||||
1012,
|
||||
241,
|
||||
52,
|
||||
53
|
||||
],
|
||||
"size": [
|
||||
30,
|
||||
30
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stop",
|
||||
"variant": "24",
|
||||
"slot_box": [
|
||||
126,
|
||||
384,
|
||||
74,
|
||||
75
|
||||
],
|
||||
"detected_box": [
|
||||
138,
|
||||
401,
|
||||
41,
|
||||
41
|
||||
],
|
||||
"size": [
|
||||
26,
|
||||
26
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "settings",
|
||||
"variant": "16",
|
||||
"slot_box": [
|
||||
329,
|
||||
384,
|
||||
60,
|
||||
75
|
||||
],
|
||||
"detected_box": [
|
||||
338,
|
||||
401,
|
||||
41,
|
||||
41
|
||||
],
|
||||
"size": [
|
||||
20,
|
||||
20
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "user",
|
||||
"variant": "32",
|
||||
"slot_box": [
|
||||
728,
|
||||
384,
|
||||
89,
|
||||
75
|
||||
],
|
||||
"detected_box": [
|
||||
744,
|
||||
388,
|
||||
63,
|
||||
63
|
||||
],
|
||||
"size": [
|
||||
36,
|
||||
36
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "terminal",
|
||||
"variant": "16",
|
||||
"slot_box": [
|
||||
860,
|
||||
384,
|
||||
60,
|
||||
75
|
||||
],
|
||||
"detected_box": [
|
||||
869,
|
||||
407,
|
||||
35,
|
||||
32
|
||||
],
|
||||
"size": [
|
||||
20,
|
||||
18
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "note",
|
||||
"variant": "32",
|
||||
"slot_box": [
|
||||
728,
|
||||
536,
|
||||
89,
|
||||
75
|
||||
],
|
||||
"detected_box": [
|
||||
745,
|
||||
541,
|
||||
61,
|
||||
70
|
||||
],
|
||||
"size": [
|
||||
36,
|
||||
36
|
||||
]
|
||||
}
|
||||
]
|
||||
BIN
public/assets/themes/wasteland/icons/ui/note.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
public/assets/themes/wasteland/icons/ui/queue.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
public/assets/themes/wasteland/icons/ui/search.png
Normal file
|
After Width: | Height: | Size: 834 B |
BIN
public/assets/themes/wasteland/icons/ui/send.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
public/assets/themes/wasteland/icons/ui/settings.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
public/assets/themes/wasteland/icons/ui/status.png
Normal file
|
After Width: | Height: | Size: 723 B |
BIN
public/assets/themes/wasteland/icons/ui/stop.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
public/assets/themes/wasteland/icons/ui/terminal.png
Normal file
|
After Width: | Height: | Size: 930 B |
BIN
public/assets/themes/wasteland/icons/ui/theme.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
public/assets/themes/wasteland/icons/ui/user.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
public/assets/themes/wasteland/icons/user.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 32 KiB |
@@ -15,12 +15,16 @@
|
||||
<script>
|
||||
(function () {
|
||||
var theme = localStorage.getItem('cc-web-theme') || 'washi';
|
||||
if (theme === 'gilded') {
|
||||
theme = 'washi';
|
||||
localStorage.setItem('cc-web-theme', theme);
|
||||
}
|
||||
var dividerTime = localStorage.getItem('cc-web-show-divider-time') === '0' ? 'hide' : 'show';
|
||||
document.documentElement.dataset.theme = theme;
|
||||
document.documentElement.dataset.dividerTime = dividerTime;
|
||||
})();
|
||||
</script>
|
||||
<link rel="stylesheet" href="style.css?v=20260629-ccweb-prompt-dark-theme">
|
||||
<link rel="stylesheet" href="style.css?v=20260717-wasteland-hifi-23">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
|
||||
</head>
|
||||
<body>
|
||||
@@ -47,7 +51,7 @@
|
||||
<aside id="sidebar" class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<div class="new-chat-split">
|
||||
<button id="new-chat-btn" class="new-chat-btn">+ 新会话</button>
|
||||
<button id="new-chat-btn" class="new-chat-btn"><span class="new-chat-glyph" aria-hidden="true">+</span> 新会话</button>
|
||||
<button id="new-chat-arrow" class="new-chat-arrow" title="更多">▾</button>
|
||||
</div>
|
||||
<div id="new-chat-dropdown" class="new-chat-dropdown" hidden>
|
||||
@@ -79,9 +83,23 @@
|
||||
<div class="messages-wrap">
|
||||
<div id="messages" class="messages">
|
||||
<div class="welcome-msg">
|
||||
<div class="welcome-icon">✿</div>
|
||||
<h3>欢迎使用 CC-Web</h3>
|
||||
<p>开始与 Claude 对话</p>
|
||||
<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></h3>
|
||||
<p>本次你要构建什么?</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<div class="custom-scrollbar" id="custom-scrollbar">
|
||||
@@ -165,6 +183,6 @@
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.1/marked.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.9.1/mermaid.min.js"></script>
|
||||
<script src="app.js?v=20260705-codexapp-ui-agent"></script>
|
||||
<script src="app.js?v=20260718-theme-bundle-4"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||