feat: replace ui emoji with svg icons
175
static/app.js
@ -36,6 +36,86 @@ const SOCKET_IO_CDN_SOURCES = [
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.5/socket.io.min.js'
|
||||
];
|
||||
|
||||
const ICONS = Object.freeze({
|
||||
bot: '/static/icons/bot.svg',
|
||||
book: '/static/icons/book.svg',
|
||||
brain: '/static/icons/brain.svg',
|
||||
camera: '/static/icons/camera.svg',
|
||||
check: '/static/icons/check.svg',
|
||||
checkbox: '/static/icons/checkbox.svg',
|
||||
circleAlert: '/static/icons/circle-alert.svg',
|
||||
clipboard: '/static/icons/clipboard.svg',
|
||||
clock: '/static/icons/clock.svg',
|
||||
eye: '/static/icons/eye.svg',
|
||||
file: '/static/icons/file.svg',
|
||||
flag: '/static/icons/flag.svg',
|
||||
folder: '/static/icons/folder.svg',
|
||||
folderOpen: '/static/icons/folder-open.svg',
|
||||
globe: '/static/icons/globe.svg',
|
||||
hammer: '/static/icons/hammer.svg',
|
||||
info: '/static/icons/info.svg',
|
||||
laptop: '/static/icons/laptop.svg',
|
||||
menu: '/static/icons/menu.svg',
|
||||
monitor: '/static/icons/monitor.svg',
|
||||
octagon: '/static/icons/octagon.svg',
|
||||
pencil: '/static/icons/pencil.svg',
|
||||
python: '/static/icons/python.svg',
|
||||
recycle: '/static/icons/recycle.svg',
|
||||
save: '/static/icons/save.svg',
|
||||
search: '/static/icons/search.svg',
|
||||
settings: '/static/icons/settings.svg',
|
||||
sparkles: '/static/icons/sparkles.svg',
|
||||
stickyNote: '/static/icons/sticky-note.svg',
|
||||
terminal: '/static/icons/terminal.svg',
|
||||
trash: '/static/icons/trash.svg',
|
||||
triangleAlert: '/static/icons/triangle-alert.svg',
|
||||
user: '/static/icons/user.svg',
|
||||
wrench: '/static/icons/wrench.svg',
|
||||
x: '/static/icons/x.svg'
|
||||
});
|
||||
|
||||
const TOOL_ICON_MAP = Object.freeze({
|
||||
append_to_file: 'pencil',
|
||||
close_sub_agent: 'octagon',
|
||||
create_file: 'file',
|
||||
create_folder: 'folder',
|
||||
create_sub_agent: 'bot',
|
||||
delete_file: 'trash',
|
||||
extract_webpage: 'globe',
|
||||
focus_file: 'eye',
|
||||
modify_file: 'pencil',
|
||||
ocr_image: 'camera',
|
||||
read_file: 'book',
|
||||
rename_file: 'pencil',
|
||||
run_command: 'terminal',
|
||||
run_python: 'python',
|
||||
save_webpage: 'save',
|
||||
sleep: 'clock',
|
||||
todo_create: 'stickyNote',
|
||||
todo_finish: 'flag',
|
||||
todo_finish_confirm: 'circleAlert',
|
||||
todo_update_task: 'check',
|
||||
terminal_input: 'terminal',
|
||||
terminal_reset: 'recycle',
|
||||
terminal_session: 'monitor',
|
||||
terminal_snapshot: 'clipboard',
|
||||
unfocus_file: 'eye',
|
||||
update_memory: 'brain',
|
||||
wait_sub_agent: 'clock',
|
||||
web_search: 'search'
|
||||
});
|
||||
|
||||
const TOOL_CATEGORY_ICON_MAP = Object.freeze({
|
||||
network: 'globe',
|
||||
file_edit: 'pencil',
|
||||
read_focus: 'eye',
|
||||
terminal_realtime: 'monitor',
|
||||
terminal_command: 'terminal',
|
||||
memory: 'brain',
|
||||
todo: 'stickyNote',
|
||||
sub_agent: 'bot'
|
||||
});
|
||||
|
||||
function injectScriptSequentially(urls, onSuccess, onFailure) {
|
||||
let index = 0;
|
||||
const tryLoad = () => {
|
||||
@ -220,20 +300,8 @@ async function bootstrapApp() {
|
||||
|
||||
// TODO 列表
|
||||
todoList: null,
|
||||
todoEmoji: '🗒️',
|
||||
fileEmoji: '📁',
|
||||
todoDoneEmoji: '☑️',
|
||||
todoPendingEmoji: '⬜️',
|
||||
toolCategoryEmojis: {
|
||||
network: '🌐',
|
||||
file_edit: '📝',
|
||||
read_focus: '🔍',
|
||||
terminal_realtime: '🖥️',
|
||||
terminal_command: '⌨️',
|
||||
memory: '🧠',
|
||||
todo: '🗒️',
|
||||
sub_agent: '🤖'
|
||||
},
|
||||
icons: ICONS,
|
||||
toolCategoryIcons: TOOL_CATEGORY_ICON_MAP,
|
||||
|
||||
// 右键菜单相关
|
||||
contextMenu: {
|
||||
@ -330,6 +398,22 @@ async function bootstrapApp() {
|
||||
},
|
||||
|
||||
methods: {
|
||||
iconStyle(iconKey, size) {
|
||||
const iconPath = this.icons ? this.icons[iconKey] : null;
|
||||
if (!iconPath) {
|
||||
return {};
|
||||
}
|
||||
const style = { '--icon-src': `url(${iconPath})` };
|
||||
if (size) {
|
||||
style['--icon-size'] = size;
|
||||
}
|
||||
return style;
|
||||
},
|
||||
|
||||
toolCategoryIcon(categoryId) {
|
||||
return this.toolCategoryIcons[categoryId] || 'settings';
|
||||
},
|
||||
|
||||
openGuiFileManager() {
|
||||
window.open('/file-manager', '_blank');
|
||||
},
|
||||
@ -1303,7 +1387,7 @@ async function bootstrapApp() {
|
||||
this.activeTools.clear();
|
||||
this.toolActionIndex.clear();
|
||||
|
||||
// ✨ 新增:将所有未完成的工具标记为已完成
|
||||
// 新增:将所有未完成的工具标记为已完成
|
||||
this.messages.forEach(msg => {
|
||||
if (msg.role === 'assistant' && msg.actions) {
|
||||
msg.actions.forEach(action => {
|
||||
@ -2195,19 +2279,6 @@ async function bootstrapApp() {
|
||||
}
|
||||
},
|
||||
|
||||
formatTaskStatus(task) {
|
||||
if (!task) {
|
||||
return '';
|
||||
}
|
||||
return task.status === 'done'
|
||||
? `${this.todoDoneEmoji} 完成`
|
||||
: `${this.todoPendingEmoji} 未完成`;
|
||||
},
|
||||
|
||||
toolCategoryEmoji(categoryId) {
|
||||
return this.toolCategoryEmojis[categoryId] || '⚙️';
|
||||
},
|
||||
|
||||
async fetchSubAgents() {
|
||||
try {
|
||||
const resp = await fetch('/api/sub_agents');
|
||||
@ -2666,38 +2737,8 @@ async function bootstrapApp() {
|
||||
|
||||
// 修复:工具相关方法 - 接收tool对象而不是name
|
||||
getToolIcon(tool) {
|
||||
const toolName = typeof tool === 'string' ? tool : tool.name;
|
||||
const icons = {
|
||||
'create_file': '📄',
|
||||
'sleep': '⏱️',
|
||||
'read_file': '📖',
|
||||
'ocr_image': '📸',
|
||||
'delete_file': '🗑️',
|
||||
'rename_file': '✏️',
|
||||
'modify_file': '✏️',
|
||||
'append_to_file': '✏️',
|
||||
'create_folder': '📁',
|
||||
'focus_file': '👁️',
|
||||
'unfocus_file': '👁️',
|
||||
'web_search': '🔍',
|
||||
'extract_webpage': '🌐',
|
||||
'save_webpage': '💾',
|
||||
'run_python': '🐍',
|
||||
'run_command': '$',
|
||||
'update_memory': '🧠',
|
||||
'terminal_session': '💻',
|
||||
'terminal_input': '⌨️',
|
||||
'terminal_snapshot': '📋',
|
||||
'terminal_reset': '♻️',
|
||||
'todo_create': '🗒️',
|
||||
'todo_update_task': '☑️',
|
||||
'todo_finish': '🏁',
|
||||
'todo_finish_confirm': '❗',
|
||||
'create_sub_agent': '🤖',
|
||||
'wait_sub_agent': '⏳',
|
||||
'close_sub_agent': '🛑'
|
||||
};
|
||||
return icons[toolName] || '⚙️';
|
||||
const toolName = typeof tool === 'string' ? tool : (tool && tool.name);
|
||||
return TOOL_ICON_MAP[toolName] || 'settings';
|
||||
},
|
||||
|
||||
getToolAnimationClass(tool) {
|
||||
@ -3066,7 +3107,7 @@ async function bootstrapApp() {
|
||||
<div class="code-block-wrapper">
|
||||
<div class="code-block-header">
|
||||
<span class="code-language">${language}</span>
|
||||
<button class="copy-code-btn" data-code="${blockId}" title="复制代码">📋</button>
|
||||
<button class="copy-code-btn" data-code="${blockId}" title="复制代码" aria-label="复制代码"></button>
|
||||
</div>
|
||||
<pre><code${attributes} data-code-id="${blockId}" data-original-code="${escapedContent}">${content}</code></pre>
|
||||
</div>`;
|
||||
@ -3243,6 +3284,10 @@ async function bootstrapApp() {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
iconStyle(iconKey) {
|
||||
const iconPath = ICONS[iconKey];
|
||||
return iconPath ? { '--icon-src': `url(${iconPath})` } : {};
|
||||
},
|
||||
toggle() {
|
||||
if (this.node.type === 'folder') {
|
||||
this.$emit('toggle-folder', this.node.path);
|
||||
@ -3254,7 +3299,9 @@ async function bootstrapApp() {
|
||||
<div v-if="node.type === 'folder'" class="file-node folder-node">
|
||||
<button class="folder-header" type="button" :style="folderPadding" @click="toggle">
|
||||
<span class="folder-arrow">{{ isExpanded ? '▾' : '▸' }}</span>
|
||||
<span class="folder-icon">{{ isExpanded ? '📂' : '📁' }}</span>
|
||||
<span class="icon icon-sm folder-icon"
|
||||
:style="iconStyle(isExpanded ? 'folderOpen' : 'folder')"
|
||||
aria-hidden="true"></span>
|
||||
<span class="folder-name">{{ node.name }}</span>
|
||||
</button>
|
||||
<div v-show="isExpanded" class="folder-children">
|
||||
@ -3270,7 +3317,9 @@ async function bootstrapApp() {
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="file-node file-leaf" :style="filePadding">
|
||||
<span class="file-icon">📄</span>
|
||||
<span class="icon icon-sm file-icon"
|
||||
:style="iconStyle('file')"
|
||||
aria-hidden="true"></span>
|
||||
<span class="file-name">{{ node.name }}</span>
|
||||
<span v-if="node.annotation" class="annotation">{{ node.annotation }}</span>
|
||||
</div>
|
||||
|
||||
13
static/icons/book.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 310 B |
18
static/icons/bot.svg
Normal file
@ -0,0 +1,18 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M12 8V4H8" />
|
||||
<rect width="16" height="12" x="4" y="8" rx="2" />
|
||||
<path d="M2 14h2" />
|
||||
<path d="M20 14h2" />
|
||||
<path d="M15 13v2" />
|
||||
<path d="M9 13v2" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 380 B |
20
static/icons/brain.svg
Normal file
@ -0,0 +1,20 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M12 18V5" />
|
||||
<path d="M15 13a4.17 4.17 0 0 1-3-4 4.17 4.17 0 0 1-3 4" />
|
||||
<path d="M17.598 6.5A3 3 0 1 0 12 5a3 3 0 1 0-5.598 1.5" />
|
||||
<path d="M17.997 5.125a4 4 0 0 1 2.526 5.77" />
|
||||
<path d="M18 18a4 4 0 0 0 2-7.464" />
|
||||
<path d="M19.967 17.483A4 4 0 1 1 12 18a4 4 0 1 1-7.967-.517" />
|
||||
<path d="M6 18a4 4 0 0 1-2-7.464" />
|
||||
<path d="M6.003 5.125a4 4 0 0 0-2.526 5.77" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 601 B |
14
static/icons/camera.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M13.997 4a2 2 0 0 1 1.76 1.05l.486.9A2 2 0 0 0 18.003 7H20a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h1.997a2 2 0 0 0 1.759-1.048l.489-.904A2 2 0 0 1 10.004 4z" />
|
||||
<circle cx="12" cy="13" r="3" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 437 B |
13
static/icons/check.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M20 6 9 17l-5-5" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 239 B |
13
static/icons/checkbox.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<rect x="5" y="5" width="14" height="14" rx="2" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 261 B |
15
static/icons/circle-alert.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<line x1="12" x2="12" y1="8" y2="12" />
|
||||
<line x1="12" x2="12.01" y1="16" y2="16" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 332 B |
14
static/icons/clipboard.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<rect width="8" height="4" x="8" y="2" rx="1" ry="1" />
|
||||
<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 354 B |
14
static/icons/clock.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
<path d="M12 7v5l3 3" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 270 B |
14
static/icons/eye.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0" />
|
||||
<circle cx="12" cy="12" r="3" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 360 B |
14
static/icons/file.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" />
|
||||
<path d="M14 2v5a1 1 0 0 0 1 1h5" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 373 B |
13
static/icons/flag.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M4 22V4a1 1 0 0 1 .4-.8A6 6 0 0 1 8 2c3 0 5 2 7.333 2q2 0 3.067-.8A1 1 0 0 1 20 4v10a1 1 0 0 1-.4.8A6 6 0 0 1 16 16c-3 0-5-2-8-2a6 6 0 0 0-4 1.528" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 370 B |
1
static/icons/folder-open.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="48" height="48" fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="opacity:1;"><path d="m6 14l1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2"/></svg>
|
||||
|
After Width: | Height: | Size: 395 B |
13
static/icons/folder.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 342 B |
15
static/icons/globe.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" />
|
||||
<path d="M2 12h20" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 331 B |
15
static/icons/hammer.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="m15 12-9.373 9.373a1 1 0 0 1-3.001-3L12 9" />
|
||||
<path d="m18 15 4-4" />
|
||||
<path d="m21.5 11.5-1.914-1.914A2 2 0 0 1 19 8.172v-.344a2 2 0 0 0-.586-1.414l-1.657-1.657A6 6 0 0 0 12.516 3H9l1.243 1.243A6 6 0 0 1 12 8.485V10l2 2h1.172a2 2 0 0 1 1.414.586L18.5 14.5" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 483 B |
15
static/icons/info.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
<path d="M12 16v-4" />
|
||||
<path d="M12 8h.01" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 293 B |
14
static/icons/laptop.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M18 5a2 2 0 0 1 2 2v8.526a2 2 0 0 0 .212.897l1.068 2.127a1 1 0 0 1-.9 1.45H3.62a1 1 0 0 1-.9-1.45l1.068-2.127A2 2 0 0 0 4 15.526V7a2 2 0 0 1 2-2z" />
|
||||
<path d="M20.054 15.987H3.946" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 405 B |
15
static/icons/menu.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M4 5h16" />
|
||||
<path d="M4 12h16" />
|
||||
<path d="M4 19h16" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 279 B |
15
static/icons/monitor.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<rect width="20" height="14" x="2" y="3" rx="2" />
|
||||
<line x1="8" x2="16" y1="21" y2="21" />
|
||||
<line x1="12" x2="12" y1="17" y2="21" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 346 B |
13
static/icons/octagon.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M2.586 16.726A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2h6.624a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 458 B |
14
static/icons/pencil.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" />
|
||||
<path d="m15 5 4 4" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 377 B |
1
static/icons/python.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Python</title><path d="M14.25.18l.9.2.73.26.59.3.45.32.34.34.25.34.16.33.1.3.04.26.02.2-.01.13V8.5l-.05.63-.13.55-.21.46-.26.38-.3.31-.33.25-.35.19-.35.14-.33.1-.3.07-.26.04-.21.02H8.77l-.69.05-.59.14-.5.22-.41.27-.33.32-.27.35-.2.36-.15.37-.1.35-.07.32-.04.27-.02.21v3.06H3.17l-.21-.03-.28-.07-.32-.12-.35-.18-.36-.26-.36-.36-.35-.46-.32-.59-.28-.73-.21-.88-.14-1.05-.05-1.23.06-1.22.16-1.04.24-.87.32-.71.36-.57.4-.44.42-.33.42-.24.4-.16.36-.1.32-.05.24-.01h.16l.06.01h8.16v-.83H6.18l-.01-2.75-.02-.37.05-.34.11-.31.17-.28.25-.26.31-.23.38-.2.44-.18.51-.15.58-.12.64-.1.71-.06.77-.04.84-.02 1.27.05zm-6.3 1.98l-.23.33-.08.41.08.41.23.34.33.22.41.09.41-.09.33-.22.23-.34.08-.41-.08-.41-.23-.33-.33-.22-.41-.09-.41.09zm13.09 3.95l.28.06.32.12.35.18.36.27.36.35.35.47.32.59.28.73.21.88.14 1.04.05 1.23-.06 1.23-.16 1.04-.24.86-.32.71-.36.57-.4.45-.42.33-.42.24-.4.16-.36.09-.32.05-.24.02-.16-.01h-8.22v.82h5.84l.01 2.76.02.36-.05.34-.11.31-.17.29-.25.25-.31.24-.38.2-.44.17-.51.15-.58.13-.64.09-.71.07-.77.04-.84.01-1.27-.04-1.07-.14-.9-.2-.73-.25-.59-.3-.45-.33-.34-.34-.25-.34-.16-.33-.1-.3-.04-.25-.02-.2.01-.13v-5.34l.05-.64.13-.54.21-.46.26-.38.3-.32.33-.24.35-.2.35-.14.33-.1.3-.06.26-.04.21-.02.13-.01h5.84l.69-.05.59-.14.5-.21.41-.28.33-.32.27-.35.2-.36.15-.36.1-.35.07-.32.04-.28.02-.21V6.07h2.09l.14.01zm-6.47 14.25l-.23.33-.08.41.08.41.23.33.33.23.41.08.41-.08.33-.23.23-.33.08-.41-.08-.41-.23-.33-.33-.23-.41-.08-.41.08z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
18
static/icons/recycle.svg
Normal file
@ -0,0 +1,18 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M7 19H4.815a1.83 1.83 0 0 1-1.57-.881 1.785 1.785 0 0 1-.004-1.784L7.196 9.5" />
|
||||
<path d="M11 19h8.203a1.83 1.83 0 0 0 1.556-.89 1.784 1.784 0 0 0 0-1.775l-1.226-2.12" />
|
||||
<path d="m14 16-3 3 3 3" />
|
||||
<path d="M8.293 13.596 7.196 9.5 3.1 10.598" />
|
||||
<path d="m9.344 5.811 1.093-1.892A1.83 1.83 0 0 1 11.985 3a1.784 1.784 0 0 1 1.546.888l3.943 6.843" />
|
||||
<path d="m13.378 9.633 4.096 1.098 1.097-4.096" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 630 B |
15
static/icons/save.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z" />
|
||||
<path d="M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7" />
|
||||
<path d="M7 3v4a1 1 0 0 0 1 1h7" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 417 B |
14
static/icons/search.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="m21 21-4.34-4.34" />
|
||||
<circle cx="11" cy="11" r="8" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 275 B |
14
static/icons/settings.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" />
|
||||
<circle cx="12" cy="12" r="3" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 586 B |
16
static/icons/sparkles.svg
Normal file
@ -0,0 +1,16 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z" />
|
||||
<path d="M20 2v4" />
|
||||
<path d="M22 4h-4" />
|
||||
<circle cx="4" cy="20" r="2" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 567 B |
14
static/icons/sticky-note.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M21 9a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 15 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2z" />
|
||||
<path d="M15 3v5a1 1 0 0 0 1 1h5" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 376 B |
14
static/icons/terminal.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M12 19h8" />
|
||||
<path d="m4 17 6-6-6-6" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 261 B |
15
static/icons/trash.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" />
|
||||
<path d="M3 6h18" />
|
||||
<path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 341 B |
15
static/icons/triangle-alert.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" />
|
||||
<path d="M12 9v4" />
|
||||
<path d="M12 17h.01" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 345 B |
14
static/icons/user.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" />
|
||||
<circle cx="12" cy="7" r="4" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 299 B |
13
static/icons/wrench.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 417 B |
14
static/icons/x.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M18 6 6 18" />
|
||||
<path d="m6 6 12 12" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 260 B |
@ -79,8 +79,11 @@
|
||||
<span class="btn-text">新建对话</span>
|
||||
</button>
|
||||
<button @click="toggleSidebar" class="toggle-sidebar-btn">
|
||||
<span v-if="sidebarCollapsed">☰</span>
|
||||
<span v-else>←</span>
|
||||
<span v-if="sidebarCollapsed"
|
||||
class="icon icon-md"
|
||||
:style="iconStyle('menu')"
|
||||
aria-hidden="true"></span>
|
||||
<span v-else class="toggle-arrow" aria-hidden="true">←</span>
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
@ -166,7 +169,12 @@
|
||||
<div class="sidebar-status">
|
||||
<div class="compact-status-card">
|
||||
<div class="status-top">
|
||||
<span class="logo">🤖 AI Agent</span>
|
||||
<span class="logo icon-label">
|
||||
<span class="icon icon-md"
|
||||
:style="iconStyle('bot')"
|
||||
aria-hidden="true"></span>
|
||||
<span>AI Agent</span>
|
||||
</span>
|
||||
<span class="agent-version" v-if="agentVersion">{{ agentVersion }}</span>
|
||||
</div>
|
||||
<div class="status-bottom">
|
||||
@ -185,22 +193,36 @@
|
||||
<button class="sidebar-view-toggle"
|
||||
@click.stop="togglePanelMenu"
|
||||
title="切换侧边栏">
|
||||
☰
|
||||
<span class="icon icon-md"
|
||||
:style="iconStyle('menu')"
|
||||
aria-hidden="true"></span>
|
||||
</button>
|
||||
<transition name="fade">
|
||||
<div class="panel-menu" v-if="panelMenuOpen">
|
||||
<button type="button"
|
||||
:class="{ active: panelMode === 'files' }"
|
||||
@click.stop="selectPanelMode('files')"
|
||||
title="项目文件">📁</button>
|
||||
title="项目文件">
|
||||
<span class="icon icon-md"
|
||||
:style="iconStyle('folder')"
|
||||
aria-hidden="true"></span>
|
||||
</button>
|
||||
<button type="button"
|
||||
:class="{ active: panelMode === 'todo' }"
|
||||
@click.stop="selectPanelMode('todo')"
|
||||
title="待办列表">{{ todoEmoji }}</button>
|
||||
title="待办列表">
|
||||
<span class="icon icon-md"
|
||||
:style="iconStyle('stickyNote')"
|
||||
aria-hidden="true"></span>
|
||||
</button>
|
||||
<button type="button"
|
||||
:class="{ active: panelMode === 'subAgents' }"
|
||||
@click.stop="selectPanelMode('subAgents')"
|
||||
title="子智能体">🤖</button>
|
||||
title="子智能体">
|
||||
<span class="icon icon-md"
|
||||
:style="iconStyle('bot')"
|
||||
aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
@ -210,9 +232,24 @@
|
||||
管理
|
||||
</button>
|
||||
<h3>
|
||||
<span v-if="panelMode === 'files'">{{ fileEmoji }} 项目文件</span>
|
||||
<span v-else-if="panelMode === 'todo'">{{ todoEmoji }} 待办列表</span>
|
||||
<span v-else>🤖 子智能体</span>
|
||||
<span v-if="panelMode === 'files'" class="icon-label">
|
||||
<span class="icon icon-sm"
|
||||
:style="iconStyle('folder')"
|
||||
aria-hidden="true"></span>
|
||||
<span>项目文件</span>
|
||||
</span>
|
||||
<span v-else-if="panelMode === 'todo'" class="icon-label">
|
||||
<span class="icon icon-sm"
|
||||
:style="iconStyle('stickyNote')"
|
||||
aria-hidden="true"></span>
|
||||
<span>待办列表</span>
|
||||
</span>
|
||||
<span v-else class="icon-label">
|
||||
<span class="icon icon-sm"
|
||||
:style="iconStyle('bot')"
|
||||
aria-hidden="true"></span>
|
||||
<span>子智能体</span>
|
||||
</span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="sidebar-panel-content">
|
||||
@ -226,7 +263,12 @@
|
||||
:key="task.index"
|
||||
:class="{ done: task.status === 'done' }">
|
||||
<span class="todo-task-title">task{{ task.index }}:{{ task.title }}</span>
|
||||
<span class="todo-task-status">{{ formatTaskStatus(task) }}</span>
|
||||
<span class="todo-task-status icon-label">
|
||||
<span class="icon icon-sm"
|
||||
:style="iconStyle(task.status === 'done' ? 'check' : 'checkbox')"
|
||||
aria-hidden="true"></span>
|
||||
<span>{{ task.status === 'done' ? '完成' : '未完成' }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="todo-instruction">{{ todoList.instruction }}</div>
|
||||
</div>
|
||||
@ -298,13 +340,23 @@
|
||||
|
||||
<!-- 用户消息 -->
|
||||
<div v-if="msg.role === 'user'" class="user-message">
|
||||
<div class="message-header">👤 用户</div>
|
||||
<div class="message-header icon-label">
|
||||
<span class="icon icon-sm"
|
||||
:style="iconStyle('user')"
|
||||
aria-hidden="true"></span>
|
||||
<span>用户</span>
|
||||
</div>
|
||||
<div class="message-text">{{ msg.content }}</div>
|
||||
</div>
|
||||
|
||||
<!-- AI消息 -->
|
||||
<div v-else-if="msg.role === 'assistant'" class="assistant-message">
|
||||
<div class="message-header">🤖 AI Assistant</div>
|
||||
<div class="message-header icon-label">
|
||||
<span class="icon icon-sm"
|
||||
:style="iconStyle('bot')"
|
||||
aria-hidden="true"></span>
|
||||
<span>AI Assistant</span>
|
||||
</div>
|
||||
|
||||
<!-- 按顺序显示所有actions -->
|
||||
<div v-for="(action, actionIndex) in msg.actions"
|
||||
@ -323,7 +375,11 @@
|
||||
<div class="collapsible-header" @click="toggleBlock(action.blockId || `${index}-thinking-${actionIndex}`)">
|
||||
<div class="arrow"></div>
|
||||
<div class="status-icon">
|
||||
<span class="thinking-icon" :class="{ 'thinking-animation': action.streaming }">🧠</span>
|
||||
<span class="thinking-icon" :class="{ 'thinking-animation': action.streaming }">
|
||||
<span class="icon icon-sm"
|
||||
:style="iconStyle('brain')"
|
||||
aria-hidden="true"></span>
|
||||
</span>
|
||||
</div>
|
||||
<span class="status-text">{{ action.streaming ? '正在思考...' : '思考过程' }}</span>
|
||||
</div>
|
||||
@ -362,10 +418,20 @@
|
||||
:class="{ 'append-error': action.append?.success === false }">
|
||||
<div class="append-placeholder-content">
|
||||
<template v-if="action.append?.success !== false">
|
||||
✏️ 已写入 {{ action.append?.path || '目标文件' }} 的追加内容(内容已保存至文件)
|
||||
<div class="icon-label append-status">
|
||||
<span class="icon icon-sm"
|
||||
:style="iconStyle('pencil')"
|
||||
aria-hidden="true"></span>
|
||||
<span>已写入 {{ action.append?.path || '目标文件' }} 的追加内容(内容已保存至文件)</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
❌ 向 {{ action.append?.path || '目标文件' }} 写入失败,内容已截获供后续修复。
|
||||
<div class="icon-label append-status append-error-text">
|
||||
<span class="icon icon-sm"
|
||||
:style="iconStyle('x')"
|
||||
aria-hidden="true"></span>
|
||||
<span>向 {{ action.append?.path || '目标文件' }} 写入失败,内容已截获供后续修复。</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="append-meta" v-if="action.append">
|
||||
<span v-if="action.append.lines !== null && action.append.lines !== undefined">
|
||||
@ -375,8 +441,11 @@
|
||||
· 字节 {{ action.append.bytes }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="append-warning" v-if="action.append?.forced">
|
||||
⚠️ 未检测到结束标记,请根据提示继续补充。
|
||||
<div class="append-warning icon-label" v-if="action.append?.forced">
|
||||
<span class="icon icon-sm"
|
||||
:style="iconStyle('triangleAlert')"
|
||||
aria-hidden="true"></span>
|
||||
<span>未检测到结束标记,请根据提示继续补充。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -386,16 +455,22 @@
|
||||
class="append-placeholder"
|
||||
:class="{ 'append-error': action.append?.success === false }">
|
||||
<div class="append-placeholder-content">
|
||||
<div>
|
||||
✏️ {{ action.append?.summary || '文件追加完成' }}
|
||||
<div class="icon-label append-status">
|
||||
<span class="icon icon-sm"
|
||||
:style="iconStyle('pencil')"
|
||||
aria-hidden="true"></span>
|
||||
<span>{{ action.append?.summary || '文件追加完成' }}</span>
|
||||
</div>
|
||||
<div class="append-meta" v-if="action.append">
|
||||
<span>{{ action.append.path || '目标文件' }}</span>
|
||||
<span v-if="action.append.lines">· 行数 {{ action.append.lines }}</span>
|
||||
<span v-if="action.append.bytes">· 字节 {{ action.append.bytes }}</span>
|
||||
</div>
|
||||
<div class="append-warning" v-if="action.append?.forced">
|
||||
⚠️ 未检测到结束标记,请按提示继续补充。
|
||||
<div class="append-warning icon-label" v-if="action.append?.forced">
|
||||
<span class="icon icon-sm"
|
||||
:style="iconStyle('triangleAlert')"
|
||||
aria-hidden="true"></span>
|
||||
<span>未检测到结束标记,请按提示继续补充。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -403,7 +478,12 @@
|
||||
<!-- 修改内容占位 -->
|
||||
<div v-else-if="action.type === 'modify_payload'" class="modify-placeholder">
|
||||
<div class="modify-placeholder-content">
|
||||
🛠️ 已对 {{ action.modify?.path || '目标文件' }} 执行补丁
|
||||
<span class="icon-label">
|
||||
<span class="icon icon-sm"
|
||||
:style="iconStyle('hammer')"
|
||||
aria-hidden="true"></span>
|
||||
<span>已对 {{ action.modify?.path || '目标文件' }} 执行补丁</span>
|
||||
</span>
|
||||
<div class="modify-meta" v-if="action.modify">
|
||||
<span v-if="action.modify.total !== null && action.modify.total !== undefined">
|
||||
· 共 {{ action.modify.total }} 处
|
||||
@ -415,11 +495,17 @@
|
||||
· 未完成 {{ action.modify.failed.length }} 处
|
||||
</span>
|
||||
</div>
|
||||
<div class="modify-warning" v-if="action.modify?.forced">
|
||||
⚠️ 未检测到结束标记,系统已在流结束时执行补丁。
|
||||
<div class="modify-warning icon-label" v-if="action.modify?.forced">
|
||||
<span class="icon icon-sm"
|
||||
:style="iconStyle('triangleAlert')"
|
||||
aria-hidden="true"></span>
|
||||
<span>未检测到结束标记,系统已在流结束时执行补丁。</span>
|
||||
</div>
|
||||
<div class="modify-warning" v-if="action.modify?.failed && action.modify.failed.length">
|
||||
⚠️ 未完成的序号:{{ action.modify.failed.map(f => f.index || f).join('、') || action.modify.failed.join('、') }},请根据提示重新输出。
|
||||
<div class="modify-warning icon-label" v-if="action.modify?.failed && action.modify.failed.length">
|
||||
<span class="icon icon-sm"
|
||||
:style="iconStyle('triangleAlert')"
|
||||
aria-hidden="true"></span>
|
||||
<span>未完成的序号:{{ action.modify.failed.map(f => f.index || f).join('、') || action.modify.failed.join('、') }},请根据提示重新输出。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -427,14 +513,22 @@
|
||||
<!-- 修改结果摘要 -->
|
||||
<div v-else-if="action.type === 'modify'" class="modify-placeholder">
|
||||
<div class="modify-placeholder-content">
|
||||
🛠️ {{ action.modify?.summary || `已处理 ${action.modify?.path || '目标文件'}` }}
|
||||
<div class="icon-label">
|
||||
<span class="icon icon-sm"
|
||||
:style="iconStyle('hammer')"
|
||||
aria-hidden="true"></span>
|
||||
<span>{{ action.modify?.summary || `已处理 ${action.modify?.path || '目标文件'}` }}</span>
|
||||
</div>
|
||||
<div class="modify-meta" v-if="action.modify">
|
||||
<span v-if="action.modify.total">· 共 {{ action.modify.total }} 处</span>
|
||||
<span v-if="action.modify.completed">· 完成 {{ action.modify.completed.length || action.modify.completed }} 处</span>
|
||||
<span v-if="action.modify.failed">· 未完成 {{ action.modify.failed.length || action.modify.failed }} 处</span>
|
||||
</div>
|
||||
<div class="modify-warning" v-if="action.modify?.forced">
|
||||
⚠️ 未检测到结束标记,系统已自动处理。
|
||||
<div class="modify-warning icon-label" v-if="action.modify?.forced">
|
||||
<span class="icon icon-sm"
|
||||
:style="iconStyle('triangleAlert')"
|
||||
aria-hidden="true"></span>
|
||||
<span>未检测到结束标记,系统已自动处理。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -451,10 +545,10 @@
|
||||
<div class="arrow"></div>
|
||||
<div class="status-icon">
|
||||
<!-- 修复:传递完整的tool对象 -->
|
||||
<span class="tool-icon"
|
||||
:class="getToolAnimationClass(action.tool)">
|
||||
{{ getToolIcon(action.tool) }}
|
||||
</span>
|
||||
<span class="tool-icon icon icon-md"
|
||||
:class="getToolAnimationClass(action.tool)"
|
||||
:style="iconStyle(getToolIcon(action.tool))"
|
||||
aria-hidden="true"></span>
|
||||
</div>
|
||||
<span class="status-text">
|
||||
{{ getToolStatusText(action.tool) }}
|
||||
@ -509,7 +603,9 @@
|
||||
<div class="collapsible-header" @click="toggleBlock(`system-${index}`)">
|
||||
<div class="arrow"></div>
|
||||
<div class="status-icon">
|
||||
<span class="tool-icon">ℹ️</span>
|
||||
<span class="tool-icon icon icon-md"
|
||||
:style="iconStyle('info')"
|
||||
aria-hidden="true"></span>
|
||||
</div>
|
||||
<span class="status-text">系统消息</span>
|
||||
</div>
|
||||
@ -622,9 +718,11 @@
|
||||
:class="{ disabled: !category.enabled }"
|
||||
@click.stop="updateToolCategory(category.id, !category.enabled)"
|
||||
:disabled="streamingMessage || !isConnected || toolSettingsLoading">
|
||||
<span class="submenu-label">
|
||||
<span class="submenu-icon">{{ toolCategoryEmoji(category.id) }}</span>
|
||||
{{ category.label }}
|
||||
<span class="submenu-label icon-label">
|
||||
<span class="icon icon-sm"
|
||||
:style="iconStyle(toolCategoryIcon(category.id))"
|
||||
aria-hidden="true"></span>
|
||||
<span>{{ category.label }}</span>
|
||||
</span>
|
||||
<span class="entry-arrow">{{ category.enabled ? '禁用' : '启用' }}</span>
|
||||
</button>
|
||||
@ -675,7 +773,12 @@
|
||||
:class="{ collapsed: rightCollapsed }"
|
||||
:style="{ width: rightCollapsed ? '0px' : rightWidth + 'px' }">
|
||||
<div class="sidebar-header">
|
||||
<h3>👁️ 聚焦文件 ({{ Object.keys(focusedFiles).length }}/3)</h3>
|
||||
<h3 class="icon-label">
|
||||
<span class="icon icon-sm"
|
||||
:style="iconStyle('eye')"
|
||||
aria-hidden="true"></span>
|
||||
<span>聚焦文件 ({{ Object.keys(focusedFiles).length }}/3)</span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="focused-files" v-if="!rightCollapsed">
|
||||
<div v-if="Object.keys(focusedFiles).length === 0" class="no-files">
|
||||
@ -740,25 +843,24 @@
|
||||
// 使用保存的原始代码内容
|
||||
const codeContent = codeElement.dataset.originalCode || codeElement.textContent;
|
||||
|
||||
// 首次点击时保存原始图标
|
||||
if (!button.dataset.originalIcon) {
|
||||
button.dataset.originalIcon = button.textContent;
|
||||
// 保存原始提示文本
|
||||
if (!button.dataset.originalLabel) {
|
||||
button.dataset.originalLabel = button.getAttribute('aria-label') || '复制代码';
|
||||
}
|
||||
|
||||
navigator.clipboard.writeText(codeContent).then(() => {
|
||||
button.textContent = '✓';
|
||||
button.classList.add('copied');
|
||||
button.setAttribute('aria-label', '已复制');
|
||||
|
||||
setTimeout(() => {
|
||||
// 使用保存的原始图标恢复
|
||||
button.textContent = button.dataset.originalIcon || '📋';
|
||||
button.classList.remove('copied');
|
||||
button.setAttribute('aria-label', button.dataset.originalLabel);
|
||||
}, 2000);
|
||||
}).catch(err => {
|
||||
console.error('复制失败:', err);
|
||||
// 即使失败也要恢复状态
|
||||
button.textContent = button.dataset.originalIcon || '📋';
|
||||
button.classList.remove('copied');
|
||||
button.setAttribute('aria-label', button.dataset.originalLabel || '复制代码');
|
||||
});
|
||||
}
|
||||
// 使用事件委托处理复制按钮点击
|
||||
|
||||
@ -27,6 +27,46 @@
|
||||
--claude-warning: #d99845;
|
||||
}
|
||||
|
||||
/* 全局图标工具类 */
|
||||
.icon {
|
||||
--icon-size: 1em;
|
||||
width: var(--icon-size);
|
||||
height: var(--icon-size);
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
background-color: currentColor;
|
||||
mask-image: var(--icon-src);
|
||||
mask-repeat: no-repeat;
|
||||
mask-position: center;
|
||||
mask-size: contain;
|
||||
-webkit-mask-image: var(--icon-src);
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
-webkit-mask-position: center;
|
||||
-webkit-mask-size: contain;
|
||||
}
|
||||
|
||||
.icon-sm {
|
||||
--icon-size: 16px;
|
||||
}
|
||||
|
||||
.icon-md {
|
||||
--icon-size: 20px;
|
||||
}
|
||||
|
||||
.icon-lg {
|
||||
--icon-size: 24px;
|
||||
}
|
||||
|
||||
.icon-xl {
|
||||
--icon-size: 32px;
|
||||
}
|
||||
|
||||
.icon-label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: var(--app-viewport, 100vh);
|
||||
}
|
||||
@ -1298,6 +1338,15 @@ o-conversations {
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.append-status {
|
||||
font-weight: 500;
|
||||
color: var(--claude-text);
|
||||
}
|
||||
|
||||
.append-error-text {
|
||||
color: #b0432a;
|
||||
}
|
||||
|
||||
.append-warning {
|
||||
color: var(--claude-warning);
|
||||
font-weight: 500;
|
||||
@ -1416,12 +1465,30 @@ o-conversations {
|
||||
background: transparent;
|
||||
color: var(--claude-text-secondary);
|
||||
border: 1px solid rgba(121, 109, 94, 0.35);
|
||||
padding: 6px 10px;
|
||||
padding: 0;
|
||||
border-radius: 6px;
|
||||
font-size: 16px;
|
||||
width: 36px;
|
||||
height: 32px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
line-height: 1;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.copy-code-btn::before {
|
||||
content: '';
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-color: currentColor;
|
||||
mask-image: url('/static/icons/clipboard.svg');
|
||||
mask-repeat: no-repeat;
|
||||
mask-position: center;
|
||||
mask-size: contain;
|
||||
-webkit-mask-image: url('/static/icons/clipboard.svg');
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
-webkit-mask-position: center;
|
||||
-webkit-mask-size: contain;
|
||||
}
|
||||
|
||||
.copy-code-btn:hover {
|
||||
@ -1435,6 +1502,11 @@ o-conversations {
|
||||
color: #f6fff8;
|
||||
}
|
||||
|
||||
.copy-code-btn.copied::before {
|
||||
mask-image: url('/static/icons/check.svg');
|
||||
-webkit-mask-image: url('/static/icons/check.svg');
|
||||
}
|
||||
|
||||
/* 代码块内容区 */
|
||||
.code-block-wrapper pre {
|
||||
background: #ffffff !important;
|
||||
@ -1529,6 +1601,7 @@ o-conversations {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
color: var(--claude-text);
|
||||
}
|
||||
|
||||
/* 内容区域 */
|
||||
@ -1950,10 +2023,6 @@ o-conversations {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.submenu-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.quick-menu-enter-active,
|
||||
.quick-menu-leave-active {
|
||||
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||
|
||||