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'
|
'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) {
|
function injectScriptSequentially(urls, onSuccess, onFailure) {
|
||||||
let index = 0;
|
let index = 0;
|
||||||
const tryLoad = () => {
|
const tryLoad = () => {
|
||||||
@ -220,20 +300,8 @@ async function bootstrapApp() {
|
|||||||
|
|
||||||
// TODO 列表
|
// TODO 列表
|
||||||
todoList: null,
|
todoList: null,
|
||||||
todoEmoji: '🗒️',
|
icons: ICONS,
|
||||||
fileEmoji: '📁',
|
toolCategoryIcons: TOOL_CATEGORY_ICON_MAP,
|
||||||
todoDoneEmoji: '☑️',
|
|
||||||
todoPendingEmoji: '⬜️',
|
|
||||||
toolCategoryEmojis: {
|
|
||||||
network: '🌐',
|
|
||||||
file_edit: '📝',
|
|
||||||
read_focus: '🔍',
|
|
||||||
terminal_realtime: '🖥️',
|
|
||||||
terminal_command: '⌨️',
|
|
||||||
memory: '🧠',
|
|
||||||
todo: '🗒️',
|
|
||||||
sub_agent: '🤖'
|
|
||||||
},
|
|
||||||
|
|
||||||
// 右键菜单相关
|
// 右键菜单相关
|
||||||
contextMenu: {
|
contextMenu: {
|
||||||
@ -330,6 +398,22 @@ async function bootstrapApp() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
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() {
|
openGuiFileManager() {
|
||||||
window.open('/file-manager', '_blank');
|
window.open('/file-manager', '_blank');
|
||||||
},
|
},
|
||||||
@ -1303,7 +1387,7 @@ async function bootstrapApp() {
|
|||||||
this.activeTools.clear();
|
this.activeTools.clear();
|
||||||
this.toolActionIndex.clear();
|
this.toolActionIndex.clear();
|
||||||
|
|
||||||
// ✨ 新增:将所有未完成的工具标记为已完成
|
// 新增:将所有未完成的工具标记为已完成
|
||||||
this.messages.forEach(msg => {
|
this.messages.forEach(msg => {
|
||||||
if (msg.role === 'assistant' && msg.actions) {
|
if (msg.role === 'assistant' && msg.actions) {
|
||||||
msg.actions.forEach(action => {
|
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() {
|
async fetchSubAgents() {
|
||||||
try {
|
try {
|
||||||
const resp = await fetch('/api/sub_agents');
|
const resp = await fetch('/api/sub_agents');
|
||||||
@ -2666,38 +2737,8 @@ async function bootstrapApp() {
|
|||||||
|
|
||||||
// 修复:工具相关方法 - 接收tool对象而不是name
|
// 修复:工具相关方法 - 接收tool对象而不是name
|
||||||
getToolIcon(tool) {
|
getToolIcon(tool) {
|
||||||
const toolName = typeof tool === 'string' ? tool : tool.name;
|
const toolName = typeof tool === 'string' ? tool : (tool && tool.name);
|
||||||
const icons = {
|
return TOOL_ICON_MAP[toolName] || 'settings';
|
||||||
'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] || '⚙️';
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getToolAnimationClass(tool) {
|
getToolAnimationClass(tool) {
|
||||||
@ -3066,7 +3107,7 @@ async function bootstrapApp() {
|
|||||||
<div class="code-block-wrapper">
|
<div class="code-block-wrapper">
|
||||||
<div class="code-block-header">
|
<div class="code-block-header">
|
||||||
<span class="code-language">${language}</span>
|
<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>
|
</div>
|
||||||
<pre><code${attributes} data-code-id="${blockId}" data-original-code="${escapedContent}">${content}</code></pre>
|
<pre><code${attributes} data-code-id="${blockId}" data-original-code="${escapedContent}">${content}</code></pre>
|
||||||
</div>`;
|
</div>`;
|
||||||
@ -3243,6 +3284,10 @@ async function bootstrapApp() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
iconStyle(iconKey) {
|
||||||
|
const iconPath = ICONS[iconKey];
|
||||||
|
return iconPath ? { '--icon-src': `url(${iconPath})` } : {};
|
||||||
|
},
|
||||||
toggle() {
|
toggle() {
|
||||||
if (this.node.type === 'folder') {
|
if (this.node.type === 'folder') {
|
||||||
this.$emit('toggle-folder', this.node.path);
|
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">
|
<div v-if="node.type === 'folder'" class="file-node folder-node">
|
||||||
<button class="folder-header" type="button" :style="folderPadding" @click="toggle">
|
<button class="folder-header" type="button" :style="folderPadding" @click="toggle">
|
||||||
<span class="folder-arrow">{{ isExpanded ? '▾' : '▸' }}</span>
|
<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>
|
<span class="folder-name">{{ node.name }}</span>
|
||||||
</button>
|
</button>
|
||||||
<div v-show="isExpanded" class="folder-children">
|
<div v-show="isExpanded" class="folder-children">
|
||||||
@ -3270,7 +3317,9 @@ async function bootstrapApp() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="file-node file-leaf" :style="filePadding">
|
<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 class="file-name">{{ node.name }}</span>
|
||||||
<span v-if="node.annotation" class="annotation">{{ node.annotation }}</span>
|
<span v-if="node.annotation" class="annotation">{{ node.annotation }}</span>
|
||||||
</div>
|
</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>
|
<span class="btn-text">新建对话</span>
|
||||||
</button>
|
</button>
|
||||||
<button @click="toggleSidebar" class="toggle-sidebar-btn">
|
<button @click="toggleSidebar" class="toggle-sidebar-btn">
|
||||||
<span v-if="sidebarCollapsed">☰</span>
|
<span v-if="sidebarCollapsed"
|
||||||
<span v-else>←</span>
|
class="icon icon-md"
|
||||||
|
:style="iconStyle('menu')"
|
||||||
|
aria-hidden="true"></span>
|
||||||
|
<span v-else class="toggle-arrow" aria-hidden="true">←</span>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@ -166,7 +169,12 @@
|
|||||||
<div class="sidebar-status">
|
<div class="sidebar-status">
|
||||||
<div class="compact-status-card">
|
<div class="compact-status-card">
|
||||||
<div class="status-top">
|
<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>
|
<span class="agent-version" v-if="agentVersion">{{ agentVersion }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="status-bottom">
|
<div class="status-bottom">
|
||||||
@ -185,22 +193,36 @@
|
|||||||
<button class="sidebar-view-toggle"
|
<button class="sidebar-view-toggle"
|
||||||
@click.stop="togglePanelMenu"
|
@click.stop="togglePanelMenu"
|
||||||
title="切换侧边栏">
|
title="切换侧边栏">
|
||||||
☰
|
<span class="icon icon-md"
|
||||||
|
:style="iconStyle('menu')"
|
||||||
|
aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div class="panel-menu" v-if="panelMenuOpen">
|
<div class="panel-menu" v-if="panelMenuOpen">
|
||||||
<button type="button"
|
<button type="button"
|
||||||
:class="{ active: panelMode === 'files' }"
|
:class="{ active: panelMode === 'files' }"
|
||||||
@click.stop="selectPanelMode('files')"
|
@click.stop="selectPanelMode('files')"
|
||||||
title="项目文件">📁</button>
|
title="项目文件">
|
||||||
|
<span class="icon icon-md"
|
||||||
|
:style="iconStyle('folder')"
|
||||||
|
aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
:class="{ active: panelMode === 'todo' }"
|
:class="{ active: panelMode === 'todo' }"
|
||||||
@click.stop="selectPanelMode('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"
|
<button type="button"
|
||||||
:class="{ active: panelMode === 'subAgents' }"
|
:class="{ active: panelMode === 'subAgents' }"
|
||||||
@click.stop="selectPanelMode('subAgents')"
|
@click.stop="selectPanelMode('subAgents')"
|
||||||
title="子智能体">🤖</button>
|
title="子智能体">
|
||||||
|
<span class="icon icon-md"
|
||||||
|
:style="iconStyle('bot')"
|
||||||
|
aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@ -210,9 +232,24 @@
|
|||||||
管理
|
管理
|
||||||
</button>
|
</button>
|
||||||
<h3>
|
<h3>
|
||||||
<span v-if="panelMode === 'files'">{{ fileEmoji }} 项目文件</span>
|
<span v-if="panelMode === 'files'" class="icon-label">
|
||||||
<span v-else-if="panelMode === 'todo'">{{ todoEmoji }} 待办列表</span>
|
<span class="icon icon-sm"
|
||||||
<span v-else>🤖 子智能体</span>
|
: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>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar-panel-content">
|
<div class="sidebar-panel-content">
|
||||||
@ -226,7 +263,12 @@
|
|||||||
:key="task.index"
|
:key="task.index"
|
||||||
:class="{ done: task.status === 'done' }">
|
:class="{ done: task.status === 'done' }">
|
||||||
<span class="todo-task-title">task{{ task.index }}:{{ task.title }}</span>
|
<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>
|
||||||
<div class="todo-instruction">{{ todoList.instruction }}</div>
|
<div class="todo-instruction">{{ todoList.instruction }}</div>
|
||||||
</div>
|
</div>
|
||||||
@ -298,13 +340,23 @@
|
|||||||
|
|
||||||
<!-- 用户消息 -->
|
<!-- 用户消息 -->
|
||||||
<div v-if="msg.role === 'user'" class="user-message">
|
<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 class="message-text">{{ msg.content }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- AI消息 -->
|
<!-- AI消息 -->
|
||||||
<div v-else-if="msg.role === 'assistant'" class="assistant-message">
|
<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 -->
|
<!-- 按顺序显示所有actions -->
|
||||||
<div v-for="(action, actionIndex) in msg.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="collapsible-header" @click="toggleBlock(action.blockId || `${index}-thinking-${actionIndex}`)">
|
||||||
<div class="arrow"></div>
|
<div class="arrow"></div>
|
||||||
<div class="status-icon">
|
<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>
|
</div>
|
||||||
<span class="status-text">{{ action.streaming ? '正在思考...' : '思考过程' }}</span>
|
<span class="status-text">{{ action.streaming ? '正在思考...' : '思考过程' }}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -362,10 +418,20 @@
|
|||||||
:class="{ 'append-error': action.append?.success === false }">
|
:class="{ 'append-error': action.append?.success === false }">
|
||||||
<div class="append-placeholder-content">
|
<div class="append-placeholder-content">
|
||||||
<template v-if="action.append?.success !== false">
|
<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>
|
||||||
<template v-else>
|
<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>
|
</template>
|
||||||
<div class="append-meta" v-if="action.append">
|
<div class="append-meta" v-if="action.append">
|
||||||
<span v-if="action.append.lines !== null && action.append.lines !== undefined">
|
<span v-if="action.append.lines !== null && action.append.lines !== undefined">
|
||||||
@ -375,8 +441,11 @@
|
|||||||
· 字节 {{ action.append.bytes }}
|
· 字节 {{ action.append.bytes }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -386,16 +455,22 @@
|
|||||||
class="append-placeholder"
|
class="append-placeholder"
|
||||||
:class="{ 'append-error': action.append?.success === false }">
|
:class="{ 'append-error': action.append?.success === false }">
|
||||||
<div class="append-placeholder-content">
|
<div class="append-placeholder-content">
|
||||||
<div>
|
<div class="icon-label append-status">
|
||||||
✏️ {{ action.append?.summary || '文件追加完成' }}
|
<span class="icon icon-sm"
|
||||||
|
:style="iconStyle('pencil')"
|
||||||
|
aria-hidden="true"></span>
|
||||||
|
<span>{{ action.append?.summary || '文件追加完成' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="append-meta" v-if="action.append">
|
<div class="append-meta" v-if="action.append">
|
||||||
<span>{{ action.append.path || '目标文件' }}</span>
|
<span>{{ action.append.path || '目标文件' }}</span>
|
||||||
<span v-if="action.append.lines">· 行数 {{ action.append.lines }}</span>
|
<span v-if="action.append.lines">· 行数 {{ action.append.lines }}</span>
|
||||||
<span v-if="action.append.bytes">· 字节 {{ action.append.bytes }}</span>
|
<span v-if="action.append.bytes">· 字节 {{ action.append.bytes }}</span>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -403,7 +478,12 @@
|
|||||||
<!-- 修改内容占位 -->
|
<!-- 修改内容占位 -->
|
||||||
<div v-else-if="action.type === 'modify_payload'" class="modify-placeholder">
|
<div v-else-if="action.type === 'modify_payload'" class="modify-placeholder">
|
||||||
<div class="modify-placeholder-content">
|
<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">
|
<div class="modify-meta" v-if="action.modify">
|
||||||
<span v-if="action.modify.total !== null && action.modify.total !== undefined">
|
<span v-if="action.modify.total !== null && action.modify.total !== undefined">
|
||||||
· 共 {{ action.modify.total }} 处
|
· 共 {{ action.modify.total }} 处
|
||||||
@ -415,11 +495,17 @@
|
|||||||
· 未完成 {{ action.modify.failed.length }} 处
|
· 未完成 {{ action.modify.failed.length }} 处
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</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 class="modify-warning" v-if="action.modify?.failed && action.modify.failed.length">
|
<div class="modify-warning icon-label" v-if="action.modify?.failed && action.modify.failed.length">
|
||||||
⚠️ 未完成的序号:{{ action.modify.failed.map(f => f.index || f).join('、') || action.modify.failed.join('、') }},请根据提示重新输出。
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -427,14 +513,22 @@
|
|||||||
<!-- 修改结果摘要 -->
|
<!-- 修改结果摘要 -->
|
||||||
<div v-else-if="action.type === 'modify'" class="modify-placeholder">
|
<div v-else-if="action.type === 'modify'" class="modify-placeholder">
|
||||||
<div class="modify-placeholder-content">
|
<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">
|
<div class="modify-meta" v-if="action.modify">
|
||||||
<span v-if="action.modify.total">· 共 {{ action.modify.total }} 处</span>
|
<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.completed">· 完成 {{ action.modify.completed.length || action.modify.completed }} 处</span>
|
||||||
<span v-if="action.modify.failed">· 未完成 {{ action.modify.failed.length || action.modify.failed }} 处</span>
|
<span v-if="action.modify.failed">· 未完成 {{ action.modify.failed.length || action.modify.failed }} 处</span>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -451,10 +545,10 @@
|
|||||||
<div class="arrow"></div>
|
<div class="arrow"></div>
|
||||||
<div class="status-icon">
|
<div class="status-icon">
|
||||||
<!-- 修复:传递完整的tool对象 -->
|
<!-- 修复:传递完整的tool对象 -->
|
||||||
<span class="tool-icon"
|
<span class="tool-icon icon icon-md"
|
||||||
:class="getToolAnimationClass(action.tool)">
|
:class="getToolAnimationClass(action.tool)"
|
||||||
{{ getToolIcon(action.tool) }}
|
:style="iconStyle(getToolIcon(action.tool))"
|
||||||
</span>
|
aria-hidden="true"></span>
|
||||||
</div>
|
</div>
|
||||||
<span class="status-text">
|
<span class="status-text">
|
||||||
{{ getToolStatusText(action.tool) }}
|
{{ getToolStatusText(action.tool) }}
|
||||||
@ -509,7 +603,9 @@
|
|||||||
<div class="collapsible-header" @click="toggleBlock(`system-${index}`)">
|
<div class="collapsible-header" @click="toggleBlock(`system-${index}`)">
|
||||||
<div class="arrow"></div>
|
<div class="arrow"></div>
|
||||||
<div class="status-icon">
|
<div class="status-icon">
|
||||||
<span class="tool-icon">ℹ️</span>
|
<span class="tool-icon icon icon-md"
|
||||||
|
:style="iconStyle('info')"
|
||||||
|
aria-hidden="true"></span>
|
||||||
</div>
|
</div>
|
||||||
<span class="status-text">系统消息</span>
|
<span class="status-text">系统消息</span>
|
||||||
</div>
|
</div>
|
||||||
@ -622,9 +718,11 @@
|
|||||||
:class="{ disabled: !category.enabled }"
|
:class="{ disabled: !category.enabled }"
|
||||||
@click.stop="updateToolCategory(category.id, !category.enabled)"
|
@click.stop="updateToolCategory(category.id, !category.enabled)"
|
||||||
:disabled="streamingMessage || !isConnected || toolSettingsLoading">
|
:disabled="streamingMessage || !isConnected || toolSettingsLoading">
|
||||||
<span class="submenu-label">
|
<span class="submenu-label icon-label">
|
||||||
<span class="submenu-icon">{{ toolCategoryEmoji(category.id) }}</span>
|
<span class="icon icon-sm"
|
||||||
{{ category.label }}
|
:style="iconStyle(toolCategoryIcon(category.id))"
|
||||||
|
aria-hidden="true"></span>
|
||||||
|
<span>{{ category.label }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="entry-arrow">{{ category.enabled ? '禁用' : '启用' }}</span>
|
<span class="entry-arrow">{{ category.enabled ? '禁用' : '启用' }}</span>
|
||||||
</button>
|
</button>
|
||||||
@ -675,7 +773,12 @@
|
|||||||
:class="{ collapsed: rightCollapsed }"
|
:class="{ collapsed: rightCollapsed }"
|
||||||
:style="{ width: rightCollapsed ? '0px' : rightWidth + 'px' }">
|
:style="{ width: rightCollapsed ? '0px' : rightWidth + 'px' }">
|
||||||
<div class="sidebar-header">
|
<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>
|
||||||
<div class="focused-files" v-if="!rightCollapsed">
|
<div class="focused-files" v-if="!rightCollapsed">
|
||||||
<div v-if="Object.keys(focusedFiles).length === 0" class="no-files">
|
<div v-if="Object.keys(focusedFiles).length === 0" class="no-files">
|
||||||
@ -740,25 +843,24 @@
|
|||||||
// 使用保存的原始代码内容
|
// 使用保存的原始代码内容
|
||||||
const codeContent = codeElement.dataset.originalCode || codeElement.textContent;
|
const codeContent = codeElement.dataset.originalCode || codeElement.textContent;
|
||||||
|
|
||||||
// 首次点击时保存原始图标
|
// 保存原始提示文本
|
||||||
if (!button.dataset.originalIcon) {
|
if (!button.dataset.originalLabel) {
|
||||||
button.dataset.originalIcon = button.textContent;
|
button.dataset.originalLabel = button.getAttribute('aria-label') || '复制代码';
|
||||||
}
|
}
|
||||||
|
|
||||||
navigator.clipboard.writeText(codeContent).then(() => {
|
navigator.clipboard.writeText(codeContent).then(() => {
|
||||||
button.textContent = '✓';
|
|
||||||
button.classList.add('copied');
|
button.classList.add('copied');
|
||||||
|
button.setAttribute('aria-label', '已复制');
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// 使用保存的原始图标恢复
|
|
||||||
button.textContent = button.dataset.originalIcon || '📋';
|
|
||||||
button.classList.remove('copied');
|
button.classList.remove('copied');
|
||||||
|
button.setAttribute('aria-label', button.dataset.originalLabel);
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.error('复制失败:', err);
|
console.error('复制失败:', err);
|
||||||
// 即使失败也要恢复状态
|
// 即使失败也要恢复状态
|
||||||
button.textContent = button.dataset.originalIcon || '📋';
|
|
||||||
button.classList.remove('copied');
|
button.classList.remove('copied');
|
||||||
|
button.setAttribute('aria-label', button.dataset.originalLabel || '复制代码');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 使用事件委托处理复制按钮点击
|
// 使用事件委托处理复制按钮点击
|
||||||
|
|||||||
@ -27,6 +27,46 @@
|
|||||||
--claude-warning: #d99845;
|
--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 {
|
html, body {
|
||||||
height: var(--app-viewport, 100vh);
|
height: var(--app-viewport, 100vh);
|
||||||
}
|
}
|
||||||
@ -1298,6 +1338,15 @@ o-conversations {
|
|||||||
gap: 4px;
|
gap: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.append-status {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--claude-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.append-error-text {
|
||||||
|
color: #b0432a;
|
||||||
|
}
|
||||||
|
|
||||||
.append-warning {
|
.append-warning {
|
||||||
color: var(--claude-warning);
|
color: var(--claude-warning);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@ -1416,12 +1465,30 @@ o-conversations {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--claude-text-secondary);
|
color: var(--claude-text-secondary);
|
||||||
border: 1px solid rgba(121, 109, 94, 0.35);
|
border: 1px solid rgba(121, 109, 94, 0.35);
|
||||||
padding: 6px 10px;
|
padding: 0;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
font-size: 16px;
|
width: 36px;
|
||||||
|
height: 32px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s ease;
|
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 {
|
.copy-code-btn:hover {
|
||||||
@ -1435,6 +1502,11 @@ o-conversations {
|
|||||||
color: #f6fff8;
|
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 {
|
.code-block-wrapper pre {
|
||||||
background: #ffffff !important;
|
background: #ffffff !important;
|
||||||
@ -1529,6 +1601,7 @@ o-conversations {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
|
color: var(--claude-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 内容区域 */
|
/* 内容区域 */
|
||||||
@ -1950,10 +2023,6 @@ o-conversations {
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.submenu-icon {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quick-menu-enter-active,
|
.quick-menu-enter-active,
|
||||||
.quick-menu-leave-active {
|
.quick-menu-leave-active {
|
||||||
transition: opacity 0.2s ease, transform 0.2s ease;
|
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||||
|
|||||||