diff --git a/core/web_api.py b/core/web_api.py
index b4cb425..4b7456c 100644
--- a/core/web_api.py
+++ b/core/web_api.py
@@ -149,8 +149,8 @@ def admin_required(f):
@app.route('/')
@login_required
def index():
- """返回聊天页面(默认首页)"""
- return app.send_static_file('index.html')
+ """默认首页 - 星图页面"""
+ return app.send_static_file('graph.html')
@app.route('/graph.html')
@@ -159,13 +159,6 @@ def graph_html():
"""返回星图页面"""
return app.send_static_file('graph.html')
-
-@app.route('/chat')
-@login_required
-def chat():
- """返回聊天页面"""
- return app.send_static_file('index.html')
-
# 全局服务器和客户端实例
backend_server: BackendServer = None
backend_client: BackendClient = None
diff --git a/ui/static/graph.html b/ui/static/graph.html
index b7b9ae4..198a15f 100644
--- a/ui/static/graph.html
+++ b/ui/static/graph.html
@@ -331,6 +331,104 @@
.chat-input-area button:hover {
background: rgba(68, 136, 255, 0.5);
}
+.chat-input-area textarea {
+ flex: 1;
+ background: rgba(20, 20, 40, 0.9);
+ border: 1px solid rgba(100, 100, 255, 0.2);
+ color: #eee;
+ padding: 10px 14px;
+ border-radius: 6px;
+ font-family: 'Courier New', monospace;
+ font-size: 13px;
+ outline: none;
+ resize: none;
+ min-height: 40px;
+ max-height: 120px;
+}
+.chat-input-area textarea:focus {
+ border-color: rgba(68, 136, 255, 0.5);
+}
+.attach-btn {
+ background: rgba(68, 136, 255, 0.2);
+ border: 1px solid rgba(68, 136, 255, 0.3);
+ color: #aaf;
+ width: 40px;
+ height: 40px;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 18px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 0;
+ flex-shrink: 0;
+}
+.attach-btn:hover {
+ background: rgba(68, 136, 255, 0.4);
+}
+.chat-files-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+ padding: 8px 16px;
+ border-top: 1px solid rgba(100, 100, 255, 0.1);
+ background: rgba(10, 10, 25, 0.5);
+}
+.chat-file-chip {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ background: rgba(40, 40, 80, 0.8);
+ border: 1px solid rgba(100, 100, 255, 0.25);
+ border-radius: 4px;
+ padding: 4px 8px;
+ font-size: 12px;
+ color: #dde;
+ max-width: 200px;
+}
+.chat-file-chip .file-icon {
+ color: #7af;
+ flex-shrink: 0;
+}
+.chat-file-chip .file-name {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ flex: 1;
+}
+.chat-file-chip .file-size {
+ color: #889;
+ font-size: 10px;
+ flex-shrink: 0;
+}
+.chat-file-chip .file-remove {
+ color: #f66;
+ cursor: pointer;
+ font-size: 14px;
+ line-height: 1;
+ flex-shrink: 0;
+}
+.chat-file-chip .file-remove:hover {
+ color: #f33;
+}
+.chat-messages.drag-over {
+ background: rgba(68, 136, 255, 0.15);
+ border: 2px dashed rgba(68, 136, 255, 0.6);
+}
+.chat-messages.drag-over::after {
+ content: '📄 松开以上传文件';
+ position: absolute;
+ top: 50%; left: 50%;
+ transform: translate(-50%, -50%);
+ background: rgba(10, 10, 40, 0.9);
+ border: 2px dashed rgba(68, 136, 255, 0.5);
+ color: #4488ff;
+ padding: 20px 40px;
+ border-radius: 12px;
+ font-size: 18px;
+ z-index: 10;
+ pointer-events: none;
+}
.chat-status {
padding: 8px 16px;
border-top: 1px solid rgba(100, 100, 255, 0.1);
@@ -435,9 +533,12 @@
你好!我是 TrulyMEM,你的图记忆助手~
-
+
+
+
+
● 已连接
@@ -1775,6 +1876,73 @@ function smoothReposition() {
const chatCloseBtn = document.getElementById('chatCloseBtn');
const toggleChat = document.getElementById('toggle-chat');
const chatPanel = document.getElementById('chat-panel');
+ const attachBtn = document.getElementById('attachBtn');
+ const fileInput = document.getElementById('fileInput');
+ const chatFilesContainer = document.getElementById('chatFilesContainer');
+
+ let attachedFiles = [];
+
+ function formatFileSize(bytes) {
+ if (bytes < 1024) return bytes + 'B';
+ if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + 'KB';
+ return (bytes / (1024 * 1024)).toFixed(1) + 'MB';
+ }
+
+ function getFileKey(file) {
+ return file.name + '_' + file.size;
+ }
+
+ function addFileChip(file) {
+ const key = getFileKey(file);
+ if (attachedFiles.some(f => getFileKey(f) === key)) return;
+ attachedFiles.push(file);
+ renderFileChips();
+ }
+
+ function removeFileChip(file) {
+ const key = getFileKey(file);
+ attachedFiles = attachedFiles.filter(f => getFileKey(f) !== key);
+ renderFileChips();
+ }
+
+ function renderFileChips() {
+ chatFilesContainer.innerHTML = '';
+ attachedFiles.forEach(file => {
+ const chip = document.createElement('div');
+ chip.className = 'chat-file-chip';
+ chip.innerHTML = `📄${file.name}${formatFileSize(file.size)}✕`;
+ chip.querySelector('.file-remove').onclick = () => removeFileChip(file);
+ chatFilesContainer.appendChild(chip);
+ });
+ }
+
+ // 输入框自适应高度
+ chatInput.addEventListener('input', () => {
+ chatInput.style.height = 'auto';
+ chatInput.style.height = Math.min(chatInput.scrollHeight, 120) + 'px';
+ });
+
+ attachBtn.addEventListener('click', () => fileInput.click());
+
+ fileInput.addEventListener('change', () => {
+ Array.from(fileInput.files).forEach(addFileChip);
+ fileInput.value = '';
+ });
+
+ chatMessages.addEventListener('dragover', e => {
+ e.preventDefault();
+ chatMessages.classList.add('drag-over');
+ });
+
+ chatMessages.addEventListener('dragleave', () => {
+ chatMessages.classList.remove('drag-over');
+ });
+
+ chatMessages.addEventListener('drop', e => {
+ e.preventDefault();
+ chatMessages.classList.remove('drag-over');
+ Array.from(e.dataTransfer.files).forEach(addFileChip);
+ });
let chatCollapsed = false;
toggleChat.addEventListener('click', () => {
@@ -1836,11 +2004,44 @@ function smoothReposition() {
}
}
+ function buildFullMessage(text, files) {
+ if (!files || files.length === 0) return text;
+ let parts = [];
+ files.forEach(file => {
+ parts.push(`文件名:${file.name}`);
+ parts.push(`内容:`);
+ parts.push(file.content);
+ parts.push('---');
+ });
+ parts.push(text);
+ return parts.join('\n');
+ }
+
async function sendChatMessage() {
+ const files = attachedFiles.slice();
const msg = chatInput.value.trim();
- if (!msg) return;
+ if (!msg && files.length === 0) return;
chatInput.value = '';
- addChatMessage('user', msg);
+ // 读取文件内容
+ let fullMsg = msg;
+ if (files.length > 0) {
+ const fileReads = files.map(file => new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.onload = () => { file.content = reader.result; resolve(); };
+ reader.onerror = reject;
+ reader.readAsText(file);
+ }));
+ try {
+ await Promise.all(fileReads);
+ } catch(e) {
+ console.error('文件读取失败:', e);
+ }
+ fullMsg = buildFullMessage(msg, files);
+ // 清除文件芯片
+ attachedFiles = [];
+ renderFileChips();
+ }
+ addChatMessage('user', fullMsg);
chatInput.disabled = true;
chatSendBtn.disabled = true;
chatStatus.textContent = '⏳ 处理中...';
@@ -1850,7 +2051,7 @@ function smoothReposition() {
const res = await fetch('/api/message', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
- body: JSON.stringify({message: msg})
+ body: JSON.stringify({message: fullMsg})
});
const data = await res.json();
if (data.success) {
diff --git a/ui/static/index.html b/ui/static/index.html
deleted file mode 100644
index f7db682..0000000
--- a/ui/static/index.html
+++ /dev/null
@@ -1,1399 +0,0 @@
-
-
-
-
-
-TrulyMEM - TrueHumanMEM
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
API 配置
-
-
API Key
-
-
-
Base URL
-
-
-
Model
-
-
-
修改后自动保存
-
-
-
-
-
-
-
-
-
-
-
○
-
API: 未配置
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
TrulyMEM - TrueHumanMEM
-
一个基于 Web 的 AI 对话界面,支持工具调用和实时操作日志。
-
-
操作说明:
-
- - 📂 侧边栏:显示/隐藏右侧配置面板(状态栏按钮)
- - ✕ 收起:收起侧边栏(侧边栏顶部按钮)
- - 🔧 工具详情:展开/折叠所有工具调用详情
- - 🗑️ 清屏:清空聊天记录(AI 记忆保持不变)
- - ℹ️ 帮助:显示此帮助信息
-
-
-
API 地址: http://localhost:5555
-
-
-
-
-
-
-
-