1551 lines
86 KiB
HTML
1551 lines
86 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||
<meta name="theme-color" content="#1a0f08">
|
||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||
<meta name="apple-mobile-web-app-title" content="爱马仕">
|
||
<link rel="manifest" href="./manifest.webmanifest">
|
||
<link rel="icon" type="image/svg+xml" href="./icon.svg">
|
||
<link rel="apple-touch-icon" href="./icon.svg">
|
||
<title>爱马仕 · AI</title>
|
||
<link rel="stylesheet" href="./styles.css?v=20260511-modules-ia-v36">
|
||
</head>
|
||
<body>
|
||
|
||
<!-- SVG 折射滤镜 -->
|
||
<svg style="position:absolute;width:0;height:0" aria-hidden="true">
|
||
<defs>
|
||
<filter id="glass-distortion" x="-20%" y="-20%" width="140%" height="140%">
|
||
<feTurbulence type="fractalNoise" baseFrequency="0.008 0.012" numOctaves="2" seed="17" result="turbulence"/>
|
||
<feGaussianBlur in="turbulence" stdDeviation="2" result="softMap"/>
|
||
<feSpecularLighting in="softMap" surfaceScale="5" specularConstant="1" specularExponent="100" lighting-color="white" result="specLight">
|
||
<fePointLight x="-200" y="-200" z="300"/>
|
||
</feSpecularLighting>
|
||
<feDisplacementMap in="SourceGraphic" in2="softMap" scale="80" xChannelSelector="R" yChannelSelector="G"/>
|
||
</filter>
|
||
</defs>
|
||
</svg>
|
||
|
||
<!-- 动态背景 -->
|
||
<div class="bg-aurora" aria-hidden="true">
|
||
<div class="blob blob-1"></div>
|
||
<div class="blob blob-2"></div>
|
||
<div class="blob blob-3"></div>
|
||
<div class="blob blob-4"></div>
|
||
</div>
|
||
|
||
<div class="app-shell">
|
||
|
||
<!-- 侧栏 -->
|
||
<aside class="sidebar">
|
||
<div class="side-brand">
|
||
<div class="hermes-tag" aria-label="爱马仕">
|
||
<span class="hermes-tag-top">HERMÈS</span>
|
||
<span class="hermes-tag-mid">PARIS</span>
|
||
</div>
|
||
<div class="side-brand-text">爱马仕 · AI</div>
|
||
</div>
|
||
|
||
<nav class="side-nav">
|
||
<button class="side-item active" data-tab="chat">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
|
||
<span>对话</span>
|
||
</button>
|
||
<button class="side-item" data-tab="sessions">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M3 12h18"/><path d="M3 18h18"/></svg>
|
||
<span>会话</span>
|
||
</button>
|
||
<button class="side-item" data-tab="agents">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a5 5 0 0 0-5 5v2H5a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-9a2 2 0 0 0-2-2h-2V7a5 5 0 0 0-5-5z"/><circle cx="9" cy="14" r="1"/><circle cx="15" cy="14" r="1"/></svg>
|
||
<span>档案</span>
|
||
</button>
|
||
<button class="side-item" data-tab="office">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18"/><path d="M9 21V9"/></svg>
|
||
<span>工作区</span>
|
||
</button>
|
||
<button class="side-item" data-tab="models">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2v20"/><path d="M2 12h20"/><path d="M4.93 4.93 19.07 19.07"/><path d="M19.07 4.93 4.93 19.07"/></svg>
|
||
<span>模型</span>
|
||
</button>
|
||
<button class="side-item" data-tab="providers">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 7h.01"/><path d="M7.5 7h3.75a3.75 3.75 0 0 1 0 7.5H9L6 18v-3.5H5A3.5 3.5 0 0 1 5 7h2.5z"/><path d="M15 14.5h1a3.5 3.5 0 0 0 0-7h-1"/></svg>
|
||
<span>提供商</span>
|
||
</button>
|
||
<button class="side-item" data-tab="skills">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2L2 7v10l10 5 10-5V7L12 2z"/><path d="M2 7l10 5 10-5"/><path d="M12 12v10"/></svg>
|
||
<span>技能</span>
|
||
</button>
|
||
<button class="side-item" data-tab="soul">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3l1.8 5.2L19 10l-5.2 1.8L12 17l-1.8-5.2L5 10l5.2-1.8L12 3z"/><path d="M19 15l.8 2.2L22 18l-2.2.8L19 21l-.8-2.2L16 18l2.2-.8L19 15z"/></svg>
|
||
<span>人格</span>
|
||
</button>
|
||
<button class="side-item" data-tab="memory">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3 3 3 0 0 0 3-3V5a3 3 0 0 0-3-3z"/><path d="M6 10a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3 3 3 0 0 0 3-3v-2a3 3 0 0 0-3-3z"/><path d="M18 10a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3 3 3 0 0 0 3-3v-2a3 3 0 0 0-3-3z"/><path d="M12 18a3 3 0 0 0-3 3"/><path d="M12 18a3 3 0 0 1 3 3"/></svg>
|
||
<span>记忆</span>
|
||
</button>
|
||
<button class="side-item" data-tab="tools">
|
||
<svg 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.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>
|
||
<span>工具</span>
|
||
</button>
|
||
<button class="side-item" data-tab="schedules">
|
||
<svg 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"/><polyline points="12 6 12 12 16 14"/></svg>
|
||
<span>计划任务</span>
|
||
</button>
|
||
<button class="side-item" data-tab="gateway">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><path d="M10 6.5h4"/><path d="M6.5 10v4"/><path d="M17.5 10v4"/><path d="M10 17.5h4"/></svg>
|
||
<span>网关</span>
|
||
</button>
|
||
<button class="side-item" data-tab="settings">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||
<span>设置</span>
|
||
</button>
|
||
</nav>
|
||
|
||
<div class="side-search">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
|
||
<input type="text" id="searchInput" placeholder="搜索对话 / 标签…">
|
||
<button class="search-clear" id="searchClear" onclick="clearSearch()" style="display:none">×</button>
|
||
</div>
|
||
|
||
<div class="side-history" id="sideHistory">
|
||
<div class="side-history-label">对话历史</div>
|
||
</div>
|
||
|
||
<!-- 重命名 / 标签 Modal -->
|
||
<div class="modal-mask" id="renameModal" onclick="if(event.target===this)closeRenameModal()">
|
||
<div class="modal">
|
||
<div class="modal-head">
|
||
<h3>编辑对话</h3>
|
||
<button class="modal-close" onclick="closeRenameModal()">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="settings-field">
|
||
<label for="renameTitle">标题</label>
|
||
<input type="text" id="renameTitle" placeholder="对话标题" maxlength="80">
|
||
</div>
|
||
<div class="settings-field">
|
||
<label for="renameTags">标签 (逗号分隔)</label>
|
||
<input type="text" id="renameTags" placeholder="例如: 工作, 研究, 灵感">
|
||
<div class="settings-help">用标签快速分类,在搜索框里输入标签名可筛选。常用: 工作 · 研究 · 灵感 · 代码 · 写作 · 米乐</div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<button class="glass-btn-sm" onclick="closeRenameModal()">取消</button>
|
||
<button class="glass-btn-sm primary" onclick="saveRename()">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Flow 编排管理 Modal -->
|
||
<div class="modal-mask" id="flowModal" onclick="if(event.target===this)closeFlowManager()">
|
||
<div class="modal modal-lg">
|
||
<div class="modal-head">
|
||
<h3>Skill 编排 · 技能组合预设</h3>
|
||
<button class="modal-close" onclick="closeFlowManager()">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="flow-intro">
|
||
把常用的 Skill 组合打包成"编排",一键套用到任意智能体。技能按编排里的顺序应用,前面的优先级更高。
|
||
</div>
|
||
<div class="flow-toolbar">
|
||
<button class="glass-btn-sm primary" onclick="openFlowEdit()">
|
||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12h14"/></svg>
|
||
新建编排
|
||
</button>
|
||
</div>
|
||
<div class="flow-list" id="flowList"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Flow 编辑 Modal -->
|
||
<div class="modal-mask" id="flowEditModal" onclick="if(event.target===this)closeFlowEdit()">
|
||
<div class="modal modal-md">
|
||
<div class="modal-head">
|
||
<h3 id="flowEditTitle">新建编排</h3>
|
||
<button class="modal-close" onclick="closeFlowEdit()">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="settings-field">
|
||
<label>图标</label>
|
||
<div class="emoji-input-wrap">
|
||
<div class="emoji-preview" id="flowEmojiPreview" onclick="openEmojiPicker('flow')">🧠</div>
|
||
<button type="button" class="glass-btn-sm" onclick="openEmojiPicker('flow')">选择图标</button>
|
||
<input type="hidden" id="flowEmoji" value="🧠">
|
||
</div>
|
||
</div>
|
||
<div class="settings-field">
|
||
<label>名称</label>
|
||
<input type="text" id="flowName" placeholder="例如: 研究型 / 开发型 / 小红书种草" maxlength="30">
|
||
</div>
|
||
<div class="settings-field">
|
||
<label>简介</label>
|
||
<input type="text" id="flowDesc" placeholder="这个编排适合什么场景" maxlength="60">
|
||
</div>
|
||
<div class="settings-field">
|
||
<label>技能组合 (上下拖动调整顺序,编号即优先级)</label>
|
||
<div class="flow-skill-picker" id="flowSkillPicker"></div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<button class="glass-btn-sm danger-btn" id="flowDeleteBtn" onclick="deleteFlow()" style="margin-right:auto;display:none">删除</button>
|
||
<button class="glass-btn-sm" onclick="closeFlowEdit()">取消</button>
|
||
<button class="glass-btn-sm primary" onclick="saveFlow()">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 快速应用编排 Modal -->
|
||
<div class="modal-mask" id="flowApplyModal" onclick="if(event.target===this)closeFlowApply()">
|
||
<div class="modal modal-md">
|
||
<div class="modal-head">
|
||
<h3>应用编排到当前智能体</h3>
|
||
<button class="modal-close" onclick="closeFlowApply()">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="flow-intro" style="margin-bottom:14px">选一个编排,它会替换当前智能体的 skill 选择并保持顺序。</div>
|
||
<div class="flow-apply-list" id="flowApplyList"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 自定义 Skill Modal -->
|
||
<div class="modal-mask" id="skillModal" onclick="if(event.target===this)closeSkillModal()">
|
||
<div class="modal">
|
||
<div class="modal-head">
|
||
<h3 id="skillModalTitle">新建技能</h3>
|
||
<button class="modal-close" onclick="closeSkillModal()">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="settings-field">
|
||
<label>图标</label>
|
||
<div class="emoji-input-wrap">
|
||
<div class="emoji-preview" id="skillEmojiPreview" onclick="openEmojiPicker('skill')">✨</div>
|
||
<button type="button" class="glass-btn-sm" onclick="openEmojiPicker('skill')">选择图标</button>
|
||
<input type="hidden" id="skillEmoji" value="✨">
|
||
</div>
|
||
</div>
|
||
<div class="settings-field">
|
||
<label>名称</label>
|
||
<input type="text" id="skillName" placeholder="例如: 电商文案腔调" maxlength="20">
|
||
</div>
|
||
<div class="settings-field">
|
||
<label>指令 Prompt</label>
|
||
<textarea id="skillPrompt" rows="5" placeholder="告诉智能体这个技能要做什么,例如: 回答时使用小红书风格,多用 emoji、短句、分行,结尾带 2-3 个话题标签。"></textarea>
|
||
<div class="settings-help">这段话会在发消息时,自动拼到智能体的 system prompt 后面</div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<button class="glass-btn-sm" id="skillDeleteBtn" onclick="deleteCurrentSkill()" style="margin-right:auto;display:none">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/></svg>
|
||
删除
|
||
</button>
|
||
<button class="glass-btn-sm" onclick="closeSkillModal()">取消</button>
|
||
<button class="glass-btn-sm primary" onclick="saveCustomSkill()">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 通用 Agent Picker (邀请 / 派分支) -->
|
||
<div class="modal-mask" id="agentPicker" onclick="if(event.target===this)closeAgentPicker()">
|
||
<div class="modal modal-md">
|
||
<div class="modal-head">
|
||
<h3 id="agentPickerTitle">选择智能体</h3>
|
||
<button class="modal-close" onclick="closeAgentPicker()">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="agent-picker-list" id="agentPickerList"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Emoji / 品牌图标 选择器 -->
|
||
<div class="modal-mask" id="emojiPicker" onclick="if(event.target===this)closeEmojiPicker()">
|
||
<div class="modal modal-md">
|
||
<div class="modal-head">
|
||
<h3>选择图标</h3>
|
||
<button class="modal-close" onclick="closeEmojiPicker()">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="emoji-section-label">🏢 品牌</div>
|
||
<div class="emoji-grid brand-grid" id="brandGrid"></div>
|
||
<div class="emoji-section-label">😀 表情</div>
|
||
<div class="emoji-search-wrap">
|
||
<input type="text" id="emojiSearch" placeholder="搜索表情 (输入中文/英文关键词)">
|
||
</div>
|
||
<div class="emoji-grid" id="emojiGrid"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="side-bottom">
|
||
<button class="side-new" onclick="newChat()">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12h14"/></svg>
|
||
<span>新对话</span>
|
||
</button>
|
||
<button class="theme-toggle" onclick="toggleTheme()" aria-label="切换明暗">
|
||
<svg class="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/></svg>
|
||
<svg class="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
|
||
<span class="theme-label"></span>
|
||
</button>
|
||
<div class="side-status" id="sideStatus">
|
||
<span class="dot"></span>
|
||
<span id="statusText">连接中…</span>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- 主内容 -->
|
||
<main class="main">
|
||
|
||
<!-- TAB: 对话 -->
|
||
<section class="tab-panel active" id="tab-chat">
|
||
<div class="chat-topbar">
|
||
<div class="chat-topbar-title" id="chatTitleText" onclick="renameCurrent()" title="点击编辑标题和标签">新对话</div>
|
||
<button class="chat-clear" onclick="renameCurrent()" title="编辑标题/标签">✎</button>
|
||
<button class="chat-agent-badge" id="chatAgentBtn" onclick="openAgentPickerForChat()" title="邀请智能体到当前对话">
|
||
<div class="ag-emoji">+</div>
|
||
<span>邀请智能体</span>
|
||
</button>
|
||
<div class="chat-model-pick">
|
||
<select id="modelPick">
|
||
<option value="google/gemini-3.1-pro-preview">Gemini 3.1 Pro · OpenRouter</option>
|
||
<option value="gemini-3-pro-preview">Gemini 3 Pro</option>
|
||
<option value="gemini-2.5-pro">Gemini 2.5 Pro</option>
|
||
<option value="gemini-2.5-flash">Gemini 2.5 Flash</option>
|
||
</select>
|
||
<datalist id="modelOptions">
|
||
<option value="google/gemini-3.1-pro-preview" label="Gemini 3.1 Pro · OpenRouter"></option>
|
||
<option value="gemini-3-pro-preview" label="Gemini 3 Pro"></option>
|
||
<option value="gemini-2.5-pro" label="Gemini 2.5 Pro"></option>
|
||
<option value="gemini-2.5-flash" label="Gemini 2.5 Flash"></option>
|
||
</datalist>
|
||
</div>
|
||
<button class="chat-clear" onclick="saveWeeklyReportFromChat()" title="保存最近一条回答为周报记录">存周报</button>
|
||
<button class="chat-clear" id="clearBtn" title="清空对话">清空</button>
|
||
</div>
|
||
|
||
<div class="gpt-messages" id="chatMessages"></div>
|
||
|
||
<form class="gpt-input-wrap" id="chatForm" autocomplete="off">
|
||
<div class="pending-agent-bar" id="pendingAgentBar" style="display:none"></div>
|
||
<div class="gpt-input-box">
|
||
<button type="button" class="input-at" onclick="useAgentOnce()" title="本次消息 @ 一个智能体回答">
|
||
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-4 8"/></svg>
|
||
</button>
|
||
<textarea id="chatInput" placeholder="给爱马仕发消息…" rows="1" autofocus></textarea>
|
||
<button type="submit" class="send-btn" aria-label="发送">
|
||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 5l7 7-7 7"/></svg>
|
||
</button>
|
||
</div>
|
||
<div class="gpt-footnote">⏎ 发送 · Shift+⏎ 换行 · @ 本次对话用指定智能体</div>
|
||
</form>
|
||
</section>
|
||
|
||
<!-- TAB: 档案 -->
|
||
<section class="tab-panel" id="tab-agents">
|
||
<div class="panel-head">
|
||
<div class="panel-head-row">
|
||
<div>
|
||
<h2>档案</h2>
|
||
<p>每个档案都是独立的 Hermes 工作区,可绑定模型 Profile、技能、编排阶段和提示词。</p>
|
||
</div>
|
||
<div class="panel-head-actions">
|
||
<button class="glass-btn-sm primary" onclick="openAgentModal()">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12h14"/></svg>
|
||
新建智能体
|
||
</button>
|
||
<button class="glass-btn-sm" onclick="openFlowManager()">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><circle cx="5" cy="6" r="2"/><circle cx="12" cy="6" r="2"/><circle cx="19" cy="6" r="2"/><path d="M7 6h3M14 6h3"/><path d="M5 8v10M12 8v10M19 8v10"/><path d="M5 18h14"/></svg>
|
||
Skill 编排
|
||
</button>
|
||
<button class="glass-btn-sm" onclick="openClusterMode()">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="6" r="2"/><circle cx="18" cy="6" r="2"/><circle cx="12" cy="18" r="2"/><path d="M7.4 7.4 11 16.6M16.6 7.4 13 16.6M8 6h8"/></svg>
|
||
集群对话
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="agent-grid" id="agentGrid"></div>
|
||
</section>
|
||
|
||
<!-- 新建/编辑智能体 Modal -->
|
||
<div class="modal-mask" id="agentModal" onclick="if(event.target===this)closeAgentModal()">
|
||
<div class="modal">
|
||
<div class="modal-head">
|
||
<h3 id="agentModalTitle">新建智能体</h3>
|
||
<button class="modal-close" onclick="closeAgentModal()">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="setting-row">
|
||
<label>头像</label>
|
||
<div class="emoji-input-wrap">
|
||
<div class="emoji-preview" id="agentEmojiPreview" onclick="openEmojiPicker('agent')">🤖</div>
|
||
<button type="button" class="glass-btn-sm" onclick="openEmojiPicker('agent')">选择图标</button>
|
||
<input type="hidden" id="agentEmoji" value="🤖">
|
||
</div>
|
||
</div>
|
||
<div class="setting-row">
|
||
<label>名称</label>
|
||
<input type="text" id="agentName" placeholder="例如: 代码专家" maxlength="40">
|
||
</div>
|
||
<div class="setting-row">
|
||
<label>简介</label>
|
||
<input type="text" id="agentDesc" placeholder="一句话说明这个智能体擅长什么" maxlength="80">
|
||
</div>
|
||
<div class="setting-row">
|
||
<label>模型 ID</label>
|
||
<input type="text" id="agentModel" list="modelOptions" placeholder="google/gemini-3.1-pro-preview" autocomplete="off">
|
||
</div>
|
||
<div class="setting-row setting-row-full">
|
||
<label>模型 Profile</label>
|
||
<select id="agentModelProfile" onchange="applyAgentModelProfileSelection()">
|
||
<option value="">跟随对话默认 / 仅使用模型 ID</option>
|
||
</select>
|
||
<div class="settings-help">Profile 记录 provider / base URL / API Key 引用;当前请求仍按模型 ID 发给 Hermes API。</div>
|
||
</div>
|
||
<div class="setting-row setting-row-full">
|
||
<label>角色设定 (System Prompt)</label>
|
||
<textarea id="agentPrompt" rows="5" placeholder="告诉爱马仕这个智能体的角色、语气、规则、能力边界..."></textarea>
|
||
</div>
|
||
<div class="setting-row setting-row-full">
|
||
<label>
|
||
技能 Skills
|
||
<span style="opacity:0.6;font-weight:500;font-size:11px">顺序决定执行优先级,可上下调整</span>
|
||
<button type="button" class="glass-btn-sm" style="margin-left:8px;padding:4px 10px;font-size:10px" onclick="openFlowApply()">应用编排 →</button>
|
||
</label>
|
||
<div class="skills-picker" id="skillsPicker"></div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<button class="glass-btn-sm" onclick="closeAgentModal()">取消</button>
|
||
<button class="glass-btn-sm primary" onclick="saveAgent()">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 集群对话 Modal -->
|
||
<div class="modal-mask" id="clusterModal" onclick="if(event.target===this)closeClusterMode()">
|
||
<div class="modal modal-lg">
|
||
<div class="modal-head">
|
||
<h3>集群对话 — 多智能体并行响应</h3>
|
||
<button class="modal-close" onclick="closeClusterMode()">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="cluster-pick">
|
||
<div class="cluster-label">选择要参与的智能体</div>
|
||
<div class="cluster-agent-list" id="clusterAgentList"></div>
|
||
</div>
|
||
<div class="cluster-input">
|
||
<textarea id="clusterPrompt" rows="3" placeholder="输入问题,所有选中的智能体将并行回答..."></textarea>
|
||
<button class="glass-btn-sm primary" onclick="runCluster()">并行执行</button>
|
||
</div>
|
||
<div class="cluster-results" id="clusterResults"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- TAB: 技能 -->
|
||
<section class="tab-panel" id="tab-skills">
|
||
<div class="panel-head">
|
||
<div class="panel-head-row">
|
||
<div>
|
||
<h2>技能</h2>
|
||
<p id="studioSubtitle">可复用技能库 + 编排画布 · 接入 Hermes 真实 skill(75 个,26 类) + 前端 prompt 技能</p>
|
||
</div>
|
||
<div class="panel-head-actions">
|
||
<button class="glass-btn-sm" onclick="studioNewFlow()">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12h14"/></svg>
|
||
新建编排
|
||
</button>
|
||
<button class="glass-btn-sm" onclick="studioLoadFlow()">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7h18M3 12h18M3 17h18"/></svg>
|
||
载入编排
|
||
</button>
|
||
<button class="glass-btn-sm primary" id="studioSaveBtn" onclick="studioSaveFlow()">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
||
保存编排
|
||
</button>
|
||
<button class="glass-btn-sm" onclick="studioApplyToAgent()">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a5 5 0 0 0-5 5v2H5a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-9a2 2 0 0 0-2-2h-2V7a5 5 0 0 0-5-5z"/><path d="M12 13v5M9 15l3-3 3 3"/></svg>
|
||
应用到智能体
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="studio-grid">
|
||
<!-- 左: 技能库 -->
|
||
<div class="studio-col studio-library">
|
||
<div class="studio-col-head">
|
||
<div class="studio-tabs">
|
||
<button class="studio-tab active" data-lib="hermes">Hermes 库</button>
|
||
<button class="studio-tab" data-lib="builtin">Prompt 内建</button>
|
||
<button class="studio-tab" data-lib="custom">自定义</button>
|
||
</div>
|
||
<input type="text" class="studio-search" id="studioSearch" placeholder="搜索…">
|
||
</div>
|
||
<div class="studio-list" id="studioLibrary">
|
||
<div class="history-empty">加载中…</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 中: 编排画布 -->
|
||
<div class="studio-col studio-canvas-col">
|
||
<div class="studio-col-head">
|
||
<input type="text" class="studio-flow-title" id="studioFlowName" placeholder="编排名称(例如: 产品上线评估流)">
|
||
<div class="studio-flow-meta">
|
||
<div class="emoji-preview studio-flow-emoji" id="studioFlowEmojiPreview" onclick="openEmojiPicker('studioFlow')">🎯</div>
|
||
<input type="text" id="studioFlowDesc" placeholder="一句话描述场景" class="studio-desc">
|
||
<input type="hidden" id="studioFlowEmoji" value="🎯">
|
||
</div>
|
||
</div>
|
||
<div class="studio-canvas" id="studioCanvas">
|
||
<div class="studio-stage" data-stage="pre">
|
||
<div class="studio-stage-head">
|
||
<span class="studio-stage-badge">1</span>
|
||
<div class="studio-stage-name">前置 · Prep</div>
|
||
<div class="studio-stage-desc">理解目标 / 拆解任务 / 澄清</div>
|
||
</div>
|
||
<div class="studio-stage-slots" data-slots="pre"></div>
|
||
</div>
|
||
<div class="studio-stage-arrow">↓</div>
|
||
<div class="studio-stage" data-stage="exec">
|
||
<div class="studio-stage-head">
|
||
<span class="studio-stage-badge">2</span>
|
||
<div class="studio-stage-name">执行 · Execute</div>
|
||
<div class="studio-stage-desc">主任务 / 工具调用 / 生成内容</div>
|
||
</div>
|
||
<div class="studio-stage-slots" data-slots="exec"></div>
|
||
</div>
|
||
<div class="studio-stage-arrow">↓</div>
|
||
<div class="studio-stage" data-stage="post">
|
||
<div class="studio-stage-head">
|
||
<span class="studio-stage-badge">3</span>
|
||
<div class="studio-stage-name">收尾 · Review</div>
|
||
<div class="studio-stage-desc">自查 / 格式化输出 / 风险提示</div>
|
||
</div>
|
||
<div class="studio-stage-slots" data-slots="post"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右: 详情预览 -->
|
||
<div class="studio-col studio-preview-col">
|
||
<div class="studio-col-head">
|
||
<div class="studio-tab active" style="pointer-events:none">详情预览</div>
|
||
</div>
|
||
<div class="studio-preview" id="studioPreview">
|
||
<div class="history-empty">点左侧技能查看详情 · 点"+ 加入编排"放入画布</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- TAB: 计划任务 (Cron,真实对接 Hermes /api/jobs) -->
|
||
<section class="tab-panel" id="tab-schedules">
|
||
<div class="panel-head">
|
||
<div class="panel-head-row">
|
||
<div>
|
||
<h2>计划任务</h2>
|
||
<p>通过定时运行 Agent 自动完成任务;直接对接 Hermes 后端 <code>/api/jobs</code></p>
|
||
</div>
|
||
<div class="panel-head-actions">
|
||
<button class="glass-btn-sm" onclick="refreshCron()">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 0 1 15.5-6.3L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-15.5 6.3L3 16"/><path d="M3 21v-5h5"/></svg>
|
||
刷新
|
||
</button>
|
||
<button class="glass-btn-sm primary" onclick="openCronModal()">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12h14"/></svg>
|
||
新建任务
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="cron-list" id="cronList">
|
||
<div class="history-empty">加载中…</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Cron 创建/编辑 Modal -->
|
||
<div class="modal-mask" id="cronModal" onclick="if(event.target===this)closeCronModal()">
|
||
<div class="modal">
|
||
<div class="modal-head">
|
||
<h3 id="cronModalTitle">新建定时任务</h3>
|
||
<button class="modal-close" onclick="closeCronModal()">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="settings-field">
|
||
<label>任务名称</label>
|
||
<input type="text" id="cronName" placeholder="例如: 每日晨报生成" maxlength="60">
|
||
</div>
|
||
<div class="settings-field">
|
||
<label>调度(支持 cron / 间隔 / 一次性 / 自然语言)</label>
|
||
<input type="text" id="cronSchedule" placeholder="0 9 * * * 或 every 2h 或 30m 或 2026-04-15T09:00" maxlength="80">
|
||
<div class="settings-help">
|
||
<b>Cron 5 段</b>: <code>0 9 * * *</code> 每天 9 点 · <code>*/15 * * * *</code> 每 15 分钟<br>
|
||
<b>间隔</b>: <code>every 30m</code> · <code>every 2h</code><br>
|
||
<b>一次性延迟</b>: <code>30m</code> · <code>2h</code><br>
|
||
<b>绝对时间</b>: <code>2026-04-15T14:00</code>
|
||
</div>
|
||
</div>
|
||
<div class="settings-field setting-row-full">
|
||
<label>Prompt(任务执行时发给 AI 的指令)</label>
|
||
<textarea id="cronPrompt" rows="4" placeholder="例如: 用 200 字概括今天科技圈最大的 3 件事"></textarea>
|
||
</div>
|
||
<div class="settings-field">
|
||
<label>投递目标</label>
|
||
<input type="text" id="cronDeliver" placeholder="local (默认) 或 telegram / discord / telegram:12345" maxlength="60">
|
||
<div class="settings-help"><code>local</code> 保存到 <code>~/.hermes/cron/output/</code> · <code>telegram</code> / <code>discord</code> 投到聊天频道</div>
|
||
</div>
|
||
<div class="settings-field toggle-field">
|
||
<div>
|
||
<label>启用</label>
|
||
<div class="settings-help">关闭后任务保留但不会触发</div>
|
||
</div>
|
||
<label class="switch"><input type="checkbox" id="cronEnabled" checked><span class="slider"></span></label>
|
||
</div>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<button class="glass-btn-sm danger-btn" id="cronDeleteBtn" onclick="deleteCronJob()" style="margin-right:auto;display:none">删除</button>
|
||
<button class="glass-btn-sm" onclick="closeCronModal()">取消</button>
|
||
<button class="glass-btn-sm primary" onclick="saveCronJob()">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- TAB: 人格 (真实 Hermes SOUL.md) -->
|
||
<section class="tab-panel" id="tab-soul">
|
||
<div class="panel-head">
|
||
<div class="panel-head-row">
|
||
<div>
|
||
<h2>人格</h2>
|
||
<p>通过 <code>SOUL.md</code> 定义 Agent 的人格、语气和长期指令。</p>
|
||
</div>
|
||
<div class="panel-head-actions">
|
||
<button class="glass-btn-sm" onclick="refreshMemory()">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 0 1 15.5-6.3L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-15.5 6.3L3 16"/><path d="M3 21v-5h5"/></svg>
|
||
刷新
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="memory-grid">
|
||
<div class="memory-pane">
|
||
<div class="memory-pane-head">SOUL.md · 人格文档</div>
|
||
<pre class="memory-body" id="memorySoul">加载中…</pre>
|
||
</div>
|
||
<div class="memory-pane">
|
||
<div class="memory-pane-head">说明</div>
|
||
<div class="memory-sessions">
|
||
<div class="settings-help">每次对话会加载这份长期指令。编辑能力暂未开放在前端,当前页面用于读取线上 Hermes 同步快照。</div>
|
||
<button class="glass-btn-sm" onclick="switchTab('chat');fillPrompt('请总结当前 SOUL.md 的人格设定和约束')">让 Agent 解读人格</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- TAB: 记忆 -->
|
||
<section class="tab-panel" id="tab-memory">
|
||
<div class="panel-head">
|
||
<div class="panel-head-row">
|
||
<div>
|
||
<h2>记忆</h2>
|
||
<p>Hermes 在不同会话之间沉淀的偏好、事实和可检索上下文。</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="memory-grid">
|
||
<div class="memory-pane">
|
||
<div class="memory-pane-head">代理记忆</div>
|
||
<div class="memory-sessions">
|
||
<div class="settings-help">当前个人版已同步 SOUL.md 和会话快照;更细的外部记忆 Provider 仍由 Hermes 后端配置。</div>
|
||
<button class="glass-btn-sm" onclick="switchTab('soul')">查看人格</button>
|
||
</div>
|
||
</div>
|
||
<div class="memory-pane">
|
||
<div class="memory-pane-head">相关入口</div>
|
||
<div class="memory-sessions">
|
||
<button class="glass-btn-sm" onclick="switchTab('sessions')">查看会话</button>
|
||
<button class="glass-btn-sm" onclick="switchTab('tools');fillPrompt('列出当前可用的记忆相关工具')">查看记忆工具</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- TAB: 提供商 -->
|
||
<section class="tab-panel" id="tab-providers">
|
||
<div class="panel-head">
|
||
<div class="panel-head-row">
|
||
<div>
|
||
<h2>提供商</h2>
|
||
<p>配置 LLM Provider、运行模型、Base URL 和服务器端 API Key 引用。</p>
|
||
</div>
|
||
<div class="panel-head-actions">
|
||
<button class="glass-btn-sm" onclick="refreshHermesConfig(true)">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 0 1 15.5-6.3L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-15.5 6.3L3 16"/><path d="M3 21v-5h5"/></svg>
|
||
读取配置
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="models-scroll settings-scroll">
|
||
<div class="settings-group wide">
|
||
<div class="settings-group-head">
|
||
<div class="settings-group-icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 7h.01"/><path d="M7.5 7h3.75a3.75 3.75 0 0 1 0 7.5H9L6 18v-3.5H5A3.5 3.5 0 0 1 5 7h2.5z"/><path d="M15 14.5h1a3.5 3.5 0 0 0 0-7h-1"/></svg>
|
||
</div>
|
||
<div>
|
||
<div class="settings-group-title">运行 Provider</div>
|
||
<div class="settings-group-desc">线上 Hermes agent 当前默认调用的模型和 Provider 配置</div>
|
||
</div>
|
||
</div>
|
||
<div class="settings-group-body">
|
||
<div class="settings-grid-3">
|
||
<div class="settings-field">
|
||
<label for="hermesModelDefault">默认模型 ID</label>
|
||
<input type="text" id="hermesModelDefault" placeholder="google/gemini-3.1-pro-preview" autocomplete="off">
|
||
</div>
|
||
<div class="settings-field">
|
||
<label for="hermesModelProvider">Provider</label>
|
||
<input type="text" id="hermesModelProvider" placeholder="openrouter" autocomplete="off">
|
||
</div>
|
||
<div class="settings-field">
|
||
<label for="hermesModelBaseUrl">Base URL</label>
|
||
<input type="text" id="hermesModelBaseUrl" placeholder="https://openrouter.ai/api/v1" autocomplete="off">
|
||
</div>
|
||
</div>
|
||
<div class="settings-grid-3">
|
||
<div class="settings-field">
|
||
<label for="hermesModelApiKeyRef">AI API Key</label>
|
||
<input type="password" id="hermesModelApiKeyRef" placeholder="服务器环境变量,不在浏览器显示" disabled>
|
||
<div class="settings-help">真实 Key 仍只放在服务器环境变量;这里不保存、不回显密钥。多 Agent 的专属 Key 引用在「模型」Profile 里维护。</div>
|
||
</div>
|
||
</div>
|
||
<div class="settings-actions">
|
||
<button class="glass-btn-sm" onclick="refreshHermesConfig(true)">读取 Provider 配置</button>
|
||
<button class="glass-btn-sm primary" id="hermesModelSaveBtn" onclick="saveModelConfig()">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><path d="M17 21v-8H7v8"/><path d="M7 3v5h8"/></svg>
|
||
保存 Provider 并重启
|
||
</button>
|
||
<div class="settings-help" id="hermesModelStatus">打开提供商页后自动读取。</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- TAB: 模型 -->
|
||
<section class="tab-panel" id="tab-models">
|
||
<div class="panel-head">
|
||
<div class="panel-head-row">
|
||
<div>
|
||
<h2>模型</h2>
|
||
<p>管理模型库和 Agent 可绑定的模型 Profiles;Provider 和运行模型放在「提供商」。</p>
|
||
</div>
|
||
<div class="panel-head-actions">
|
||
<button class="glass-btn-sm" onclick="clearModelProfileForm()">新增 Profile</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="models-scroll settings-scroll">
|
||
<div class="settings-group wide">
|
||
<div class="settings-group-head">
|
||
<div class="settings-group-icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2"/><path d="M9 9h6v6H9z"/><path d="M9 1v3"/><path d="M15 1v3"/><path d="M9 20v3"/><path d="M15 20v3"/><path d="M20 9h3"/><path d="M20 14h3"/><path d="M1 9h3"/><path d="M1 14h3"/></svg>
|
||
</div>
|
||
<div>
|
||
<div class="settings-group-title">模型 Profiles</div>
|
||
<div class="settings-group-desc">给不同 Agent 绑定不同模型、Provider、Base URL 和服务器端 Key 引用</div>
|
||
</div>
|
||
</div>
|
||
<div class="settings-group-body">
|
||
<div class="settings-subpanel">
|
||
<div class="settings-subpanel-head">
|
||
<div>
|
||
<div class="settings-subtitle">已配置 Profiles</div>
|
||
<div class="settings-help" id="modelProfilesCount">用于给不同智能体绑定不同模型接入信息。</div>
|
||
</div>
|
||
<button class="glass-btn-sm" type="button" onclick="clearModelProfileForm()">新增 Profile</button>
|
||
</div>
|
||
<div class="model-profile-list" id="modelProfilesList">
|
||
<div class="settings-help">正在读取服务器共享配置...</div>
|
||
</div>
|
||
<div class="model-profile-form">
|
||
<div class="settings-subtitle" id="modelProfileFormTitle">新增模型 Profile</div>
|
||
<div class="settings-grid-3">
|
||
<div class="settings-field">
|
||
<label for="modelProfileName">显示名</label>
|
||
<input type="text" id="modelProfileName" placeholder="例如 OpenRouter Gemini" autocomplete="off">
|
||
</div>
|
||
<div class="settings-field">
|
||
<label for="modelProfileProvider">Provider</label>
|
||
<input type="text" id="modelProfileProvider" placeholder="openrouter / openai / anthropic" autocomplete="off">
|
||
</div>
|
||
<div class="settings-field">
|
||
<label for="modelProfileModel">模型 ID</label>
|
||
<input type="text" id="modelProfileModel" placeholder="google/gemini-3.1-pro-preview" autocomplete="off">
|
||
</div>
|
||
<div class="settings-field">
|
||
<label for="modelProfileBaseUrl">Base URL</label>
|
||
<input type="text" id="modelProfileBaseUrl" placeholder="https://openrouter.ai/api/v1" autocomplete="off">
|
||
</div>
|
||
<div class="settings-field">
|
||
<label for="modelProfileApiKeyRef">API Key 引用</label>
|
||
<input type="text" id="modelProfileApiKeyRef" placeholder="OPENROUTER_API_KEY / 服务器环境变量" autocomplete="off">
|
||
<div class="settings-help">只填服务器环境变量名;前端不保存真实 Key。Agent 使用该 Profile 时会由桥接服务代理请求。</div>
|
||
</div>
|
||
<div class="settings-field model-profile-checks">
|
||
<label class="settings-checkline"><input type="checkbox" id="modelProfileEnabled" checked> 启用</label>
|
||
<label class="settings-checkline"><input type="checkbox" id="modelProfileDefault"> 作为默认</label>
|
||
</div>
|
||
</div>
|
||
<div class="settings-actions">
|
||
<button class="glass-btn-sm primary" type="button" onclick="saveModelProfile()">保存 Profile</button>
|
||
<button class="glass-btn-sm" type="button" onclick="clearModelProfileForm()">清空表单</button>
|
||
<div class="settings-help" id="sharedConfigStatus">共享配置会保存到服务器,供同事端同步。</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- TAB: 工具 (真实 Hermes tools list) -->
|
||
<section class="tab-panel" id="tab-tools">
|
||
<div class="panel-head">
|
||
<div class="panel-head-row">
|
||
<div>
|
||
<h2>工具</h2>
|
||
<p>Hermes 已注册工具包 · <code>hermes tools list</code> 输出 · 每分钟同步</p>
|
||
</div>
|
||
<div class="panel-head-actions">
|
||
<button class="glass-btn-sm" onclick="refreshTools()">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 0 1 15.5-6.3L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-15.5 6.3L3 16"/><path d="M3 21v-5h5"/></svg>
|
||
刷新
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="tools-scroll">
|
||
<div class="tools-grid" id="toolsGrid">加载中…</div>
|
||
|
||
<div class="settings-group wide tools-mcp-group">
|
||
<div class="settings-group-head">
|
||
<div class="settings-group-icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><path d="M3.3 7 12 12l8.7-5"/><path d="M12 22V12"/></svg>
|
||
</div>
|
||
<div>
|
||
<div class="settings-group-title">MCP 工具接入</div>
|
||
<div class="settings-group-desc">外部工具、知识库和服务调用配置</div>
|
||
</div>
|
||
</div>
|
||
<div class="settings-group-body">
|
||
<div class="settings-field">
|
||
<label for="mcpServersYaml">MCP Servers YAML</label>
|
||
<textarea id="mcpServersYaml" rows="8" spellcheck="false" placeholder='mcp_servers:
|
||
time:
|
||
command: uvx
|
||
args: ["mcp-server-time"]'></textarea>
|
||
<div class="settings-help">留空会移除 <code>mcp_servers</code>;保存只改工具接入,不要求同时填写模型配置。</div>
|
||
</div>
|
||
<div class="settings-actions">
|
||
<button class="glass-btn-sm" onclick="refreshHermesConfig(true)">读取 MCP 配置</button>
|
||
<button class="glass-btn-sm primary" id="hermesMcpSaveBtn" onclick="saveMcpConfig()">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><path d="M17 21v-8H7v8"/><path d="M7 3v5h8"/></svg>
|
||
保存 MCP 并重启
|
||
</button>
|
||
<div class="settings-help" id="hermesMcpStatus">打开工具页后自动读取。</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- TAB: 异步任务 (/v1/runs + SSE 事件流) -->
|
||
<section class="tab-panel" id="tab-runs">
|
||
<div class="panel-head">
|
||
<div class="panel-head-row">
|
||
<div>
|
||
<h2>异步任务</h2>
|
||
<p>POST <code>/v1/runs</code> 启动长任务,SSE 实时事件流展示 agent 调工具/思考/结果</p>
|
||
</div>
|
||
<div class="panel-head-actions">
|
||
<button class="glass-btn-sm" id="runCancelBtn" onclick="cancelRun()" style="display:none">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="5" width="14" height="14"/></svg>
|
||
断开流
|
||
</button>
|
||
<button class="glass-btn-sm primary" id="runStartBtn" onclick="startRun()">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"/></svg>
|
||
运行
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="runs-layout">
|
||
<div class="runs-left">
|
||
<div class="runs-input-head">任务指令</div>
|
||
<textarea id="runPrompt" placeholder="例如: 帮我研究一下特斯拉最新的 Robotaxi 进展,给出 5 条关键信息 + 来源"></textarea>
|
||
<div class="runs-hint">长任务 / 带工具调用 / 需要流式看中间步骤时用这个。短问题用对话 tab 即可。</div>
|
||
<div class="runs-active" id="runsActive"></div>
|
||
</div>
|
||
<div class="runs-right">
|
||
<div class="runs-events-head">
|
||
<span>事件流</span>
|
||
<span class="runs-meta" id="runsMeta">未启动</span>
|
||
</div>
|
||
<div class="runs-events" id="runsEvents">
|
||
<div class="history-empty">点「运行」启动一个任务,事件会实时显示在这里</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- TAB: 帮助 · 官方 Console (iframe) -->
|
||
<section class="tab-panel" id="tab-help">
|
||
<div class="panel-head">
|
||
<div class="panel-head-row">
|
||
<div>
|
||
<h2>官方 Console</h2>
|
||
<p>内嵌 Hermes 官方 Web UI(React + Vite 构建),7 个页面含完整架构文档 / 技能 / 记忆 / 工具 / 定时任务说明</p>
|
||
</div>
|
||
<div class="panel-head-actions">
|
||
<a href="/classic/" target="_blank" class="glass-btn-sm">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
|
||
新窗口打开
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<iframe src="/classic/" class="help-iframe" title="Hermes 官方 Console" id="helpIframe"></iframe>
|
||
</section>
|
||
|
||
<!-- TAB: 帮助(旧版本自写文档,保留结构防脚本错误,不显示) -->
|
||
<section class="tab-panel" id="tab-help-old" style="display:none !important">
|
||
<div class="help-scroll">
|
||
<div class="help-card">
|
||
<h3>🏗 总体架构</h3>
|
||
<p>爱马仕 UI 是 <b>纯前端 SPA</b>(无构建),通过 nginx 反代对接 Hermes Agent 后端。Hermes 后端部署在 Incus LXC 容器里,内嵌 Docker 容器跑 Python Gateway,暴露 OpenAI 兼容的 HTTP API。</p>
|
||
<pre class="md-pre"><code>浏览器
|
||
└─ https://hermes.kang-kang.com/
|
||
└─ 宿主 nginx (cookie 门禁 + Bearer 注入)
|
||
├─ / 静态 UI (index.html + app.js + styles.css)
|
||
├─ /_auth/verify htpasswd 登录(kang)
|
||
├─ /v1/* → Hermes API (LXC incusbr0:8642)
|
||
├─ /api/jobs* → 定时任务 CRUD
|
||
├─ /hermes-skills/ 真实 78 个 SKILL.md(docker cp 快照)
|
||
└─ /memory/ SOUL.md + sessions + tools.txt(systemd timer 每 1 分钟同步)</code></pre>
|
||
</div>
|
||
|
||
<div class="help-card">
|
||
<h3>🔌 各 tab 对应的后端调用</h3>
|
||
<table class="md-table">
|
||
<thead><tr><th>Tab</th><th>调用</th><th>数据源</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>对话</td><td><code>POST /v1/chat/completions</code> (SSE 流式)</td><td>实时 · 前端 localStorage 存会话</td></tr>
|
||
<tr><td>会话</td><td>本地对话列表 + <code>/memory/sessions.json</code> + <code>/memory/sessions/{id}.json</code></td><td>localStorage + systemd timer sync</td></tr>
|
||
<tr><td>档案</td><td>前端 CRUD + system prompt 组装</td><td>localStorage (hermes-ui-agents-v1)</td></tr>
|
||
<tr><td>工作区</td><td><code>GET /v1/models</code> + localStorage 聚合</td><td>实时 + 本地</td></tr>
|
||
<tr><td>模型</td><td><code>GET/POST /feishu/ui-config</code></td><td>服务器共享模型 Profiles</td></tr>
|
||
<tr><td>提供商</td><td><code>GET/PUT /feishu/hermes-config</code> 的 <code>model</code> 块</td><td>Hermes 运行模型配置</td></tr>
|
||
<tr><td>技能</td><td><code>GET /hermes-skills/</code> autoindex JSON</td><td>docker cp 快照 (78 个真实 SKILL.md)</td></tr>
|
||
<tr><td>人格</td><td><code>GET /memory/SOUL.md</code></td><td>systemd timer sync</td></tr>
|
||
<tr><td>记忆</td><td><code>GET /memory/sessions.json</code> 入口聚合</td><td>systemd timer sync</td></tr>
|
||
<tr><td>工具</td><td><code>GET /memory/tools.txt</code> + <code>GET/PUT /feishu/hermes-config</code> 的 <code>mcp_servers</code> 块</td><td><code>hermes tools list</code> + MCP 配置</td></tr>
|
||
<tr><td>计划任务</td><td><code>GET/POST/PATCH/DELETE /api/jobs</code></td><td>Hermes 后端实时</td></tr>
|
||
<tr><td>网关</td><td><code>GET/POST/DELETE /feishu/apps</code></td><td>外部消息入口</td></tr>
|
||
<tr><td>异步任务</td><td><code>POST /v1/runs</code> + EventSource <code>/v1/runs/{id}/events</code></td><td>实时 SSE</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<div class="help-card">
|
||
<h3>🔐 认证与安全</h3>
|
||
<ul>
|
||
<li><b>Cookie 门禁</b>: <code>hermes_auth=ok</code>,HttpOnly + Secure + 24h 有效</li>
|
||
<li><b>登录流程</b>: 前端 login.html → fetch <code>/_auth/verify</code> Basic Auth → nginx 校验 htpasswd → 返回 Set-Cookie</li>
|
||
<li><b>Bearer 注入</b>: nginx 在 proxy_pass 时自动加 <code>Authorization: Bearer ...</code>,浏览器永远看不到真 API key</li>
|
||
<li><b>用户</b>: <code>kang</code>(bcrypt 存在 <code>/etc/nginx/.htpasswd-hermes-kang</code>)</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="help-card">
|
||
<h3>🧠 技能编排原理</h3>
|
||
<p>不是真的在后端运行编排,而是前端<b>组装复合 system prompt</b>:</p>
|
||
<pre class="md-pre"><code>Agent.stages.pre → 阶段一 · 前置(理解目标)
|
||
Agent.stages.exec → 阶段二 · 执行(工具调用 / 生成)
|
||
Agent.stages.post → 阶段三 · 收尾(自查 / 格式化)
|
||
|
||
每个 stage 把选中的 skill 文本拼起来
|
||
→ 作为 messages[0] system 发给 /v1/chat/completions
|
||
→ Gemini 3 Pro 按阶段指令输出</code></pre>
|
||
<p>真实 Hermes skill(SKILL.md)会把前 1200 字节正文注入 prompt,内建 skill 是简短的 prompt 片段。</p>
|
||
</div>
|
||
|
||
<div class="help-card">
|
||
<h3>⚙ 部署 / 更新</h3>
|
||
<pre class="md-pre"><code># 本机改 src/ 后
|
||
cd ~/Projects/code/20260421-hermes-glass-ui-personal
|
||
|
||
# 同步个人 VPS
|
||
rsync -az src/ root@76.13.31.179:/var/www/hermes-kang/
|
||
|
||
# Git
|
||
git push # Gitea kangwan/hermes-glass-ui-personal
|
||
|
||
# 如果改了 JS/CSS 要让浏览器刷新,更新 index.html 里的资源 query 版本</code></pre>
|
||
</div>
|
||
|
||
<div class="help-card">
|
||
<h3>🛠 常见问题</h3>
|
||
<ul>
|
||
<li><b>新改的东西没显示</b> → 刷新页面;旧浏览器如果曾安装 Service Worker,当前 <code>sw.js</code> 会自动清理并注销</li>
|
||
<li><b>VPS 后端换模型</b> → 改容器内 <code>/opt/hermes-agent/config.yaml + .env</code>,<code>docker restart hermes-agent</code></li>
|
||
<li><b>记忆 tab 看不到最新会话</b> → systemd timer 1 分钟一次,急的话 <code>ssh root@76.13.31.179 /usr/local/bin/sync-hermes-memory.sh</code></li>
|
||
<li><b>Cron 任务不触发</b> → 看 Hermes 容器日志 <code>docker logs hermes-agent</code></li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="help-card">
|
||
<h3>📦 技术栈</h3>
|
||
<ul>
|
||
<li>前端:纯 HTML + CSS + JS,无构建,<b>无任何 npm 依赖</b>,约 120KB app.js + 75KB styles.css</li>
|
||
<li>风格:Apple Liquid Glass(iOS/macOS 26 风格)+ Hermès 橙 #FF6900</li>
|
||
<li>后端对接:OpenAI 兼容 HTTP + SSE,不走 WebSocket</li>
|
||
<li>持久化:前端 localStorage(6 个 key),后端 Hermes session DB</li>
|
||
<li>PWA:manifest + service worker,可装 Dock App(macOS 本地版)</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- TAB: 会话 -->
|
||
<section class="tab-panel" id="tab-sessions">
|
||
<div class="panel-head">
|
||
<div class="panel-head-row">
|
||
<div>
|
||
<h2>会话</h2>
|
||
<p>查看本地对话和 Hermes 后端同步的会话快照,可导入继续聊。</p>
|
||
</div>
|
||
<div class="panel-head-actions">
|
||
<button class="glass-btn-sm primary" onclick="newChat()">新建聊天</button>
|
||
<button class="glass-btn-sm" onclick="renderSessionsPanel();refreshMemory()">刷新</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="memory-grid">
|
||
<div class="memory-pane">
|
||
<div class="memory-pane-head">本地会话</div>
|
||
<div class="memory-sessions" id="localSessionsList">加载中…</div>
|
||
</div>
|
||
<div class="memory-pane">
|
||
<div class="memory-pane-head">云端会话 · <span id="memorySessCount">0</span></div>
|
||
<div class="memory-sessions" id="memorySessions">加载中…</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- TAB: 工作区 -->
|
||
<section class="tab-panel" id="tab-office">
|
||
<div class="panel-head">
|
||
<div class="panel-head-row">
|
||
<div>
|
||
<h2>工作区</h2>
|
||
<p>集中查看运行状态、快捷入口、活动热力图和工作节奏。</p>
|
||
</div>
|
||
<div class="panel-head-actions">
|
||
<button class="glass-btn-sm" onclick="refreshDashboard()">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 0 1 15.5-6.3L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-15.5 6.3L3 16"/><path d="M3 21v-5h5"/></svg>
|
||
刷新
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="dash-scroll">
|
||
|
||
<!-- 快捷入口 -->
|
||
<div class="deploy-links">
|
||
<a href="https://hermes.kang-kang.com/" target="_blank" rel="noopener" class="deploy-link">
|
||
<div class="deploy-link-icon">
|
||
<svg width="18" height="18" 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="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
|
||
</div>
|
||
<div class="deploy-link-body">
|
||
<div class="deploy-link-label">个人版 · 主站</div>
|
||
<div class="deploy-link-url">hermes.kang-kang.com</div>
|
||
</div>
|
||
<svg class="deploy-link-arrow" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
|
||
</a>
|
||
<a href="https://hermes.kang-kang.com/api/v1/models" target="_blank" rel="noopener" class="deploy-link">
|
||
<div class="deploy-link-icon">
|
||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
|
||
</div>
|
||
<div class="deploy-link-body">
|
||
<div class="deploy-link-label">后端 · OpenAI 兼容</div>
|
||
<div class="deploy-link-url">/api/v1/models</div>
|
||
</div>
|
||
<svg class="deploy-link-arrow" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
|
||
</a>
|
||
<a href="https://hermes.kang-kang.com/feishu/apps" target="_blank" rel="noopener" class="deploy-link">
|
||
<div class="deploy-link-icon">
|
||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a4 4 0 0 1-4 4H8l-5 3V7a4 4 0 0 1 4-4h10a4 4 0 0 1 4 4z"/><path d="M8 9h8"/><path d="M8 13h5"/></svg>
|
||
</div>
|
||
<div class="deploy-link-body">
|
||
<div class="deploy-link-label">飞书 · 机器人列表</div>
|
||
<div class="deploy-link-url">/feishu/apps</div>
|
||
</div>
|
||
<svg class="deploy-link-arrow" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
|
||
</a>
|
||
<a href="https://styles.kang-kang.com/" target="_blank" rel="noopener" class="deploy-link">
|
||
<div class="deploy-link-icon">
|
||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M4 4.5A2.5 2.5 0 0 1 6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5z"/></svg>
|
||
</div>
|
||
<div class="deploy-link-body">
|
||
<div class="deploy-link-label">文档 · 解析</div>
|
||
<div class="deploy-link-url">styles.kang-kang.com</div>
|
||
</div>
|
||
<svg class="deploy-link-arrow" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
|
||
</a>
|
||
</div>
|
||
|
||
<!-- Hero 数据区 -->
|
||
<div class="dash-hero">
|
||
<div class="dash-hero-main">
|
||
<div class="dash-hero-label">今日 Token 消耗</div>
|
||
<div class="dash-hero-num" id="stTodayTokens">0</div>
|
||
<div class="dash-hero-sub">
|
||
<span id="stTodaySub">0 条消息</span>
|
||
<span class="dash-hero-dot">·</span>
|
||
<span id="stTodayConvosText">0 个对话</span>
|
||
</div>
|
||
</div>
|
||
<div class="dash-hero-side">
|
||
<div class="hero-side-item">
|
||
<div class="hero-side-num" id="stWeek">0</div>
|
||
<div class="hero-side-lbl">本周消息</div>
|
||
<div class="hero-side-sub" id="stWeekSub">0 tokens</div>
|
||
</div>
|
||
<div class="hero-side-item">
|
||
<div class="hero-side-num" id="stMonth">0</div>
|
||
<div class="hero-side-lbl">本月消息</div>
|
||
<div class="hero-side-sub" id="stMonthSub">0 tokens</div>
|
||
</div>
|
||
<div class="hero-side-item">
|
||
<div class="hero-side-num" id="stAll">0</div>
|
||
<div class="hero-side-lbl">总对话</div>
|
||
<div class="hero-side-sub" id="stAllSub">0 条消息</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 运行状态 + 最常用智能体 -->
|
||
<div class="dash-grid-lg">
|
||
<div class="stat stat-lg">
|
||
<div class="stat-icon">
|
||
<svg width="22" height="22" 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"/><circle cx="12" cy="12" r="4"/></svg>
|
||
</div>
|
||
<div class="stat-body">
|
||
<div class="stat-label">API 状态</div>
|
||
<div class="stat-value" id="statApi">—</div>
|
||
<div class="stat-sub" id="statApiSub">127.0.0.1:8642</div>
|
||
</div>
|
||
</div>
|
||
<div class="stat stat-lg">
|
||
<div class="stat-icon">
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||
</div>
|
||
<div class="stat-body">
|
||
<div class="stat-label">当前模型</div>
|
||
<div class="stat-value" id="statModel">google/gemini-3.1-pro-preview</div>
|
||
<div class="stat-sub" id="statModelSub">Provider 以提供商页为准</div>
|
||
</div>
|
||
</div>
|
||
<div class="stat stat-lg">
|
||
<div class="stat-icon">
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M6 8h.01M10 8h.01"/></svg>
|
||
</div>
|
||
<div class="stat-body">
|
||
<div class="stat-label">本机地址</div>
|
||
<div class="stat-value" id="statIP">—</div>
|
||
<div class="stat-sub">Mac mini M4</div>
|
||
</div>
|
||
</div>
|
||
<div class="stat stat-lg">
|
||
<div class="stat-icon">
|
||
<svg width="22" height="22" 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="M2 12h20"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
|
||
</div>
|
||
<div class="stat-body">
|
||
<div class="stat-label">代理</div>
|
||
<div class="stat-value">Clash <span class="pill-ok">✓</span></div>
|
||
<div class="stat-sub">127.0.0.1:7897</div>
|
||
</div>
|
||
</div>
|
||
<div class="stat stat-lg">
|
||
<div class="stat-icon">
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a5 5 0 0 0-5 5v2H5a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-9a2 2 0 0 0-2-2h-2V7a5 5 0 0 0-5-5z"/></svg>
|
||
</div>
|
||
<div class="stat-body">
|
||
<div class="stat-label">智能体</div>
|
||
<div class="stat-value" id="statAgents">0</div>
|
||
<div class="stat-sub">已创建</div>
|
||
</div>
|
||
</div>
|
||
<div class="stat stat-lg">
|
||
<div class="stat-icon">
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5v14c0 1.7 4 3 9 3s9-1.3 9-3V5"/><path d="M3 12c0 1.7 4 3 9 3s9-1.3 9-3"/></svg>
|
||
</div>
|
||
<div class="stat-body">
|
||
<div class="stat-label">存储占用</div>
|
||
<div class="stat-value" id="statStorage">—</div>
|
||
<div class="stat-sub">localStorage</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 最常用智能体(大) -->
|
||
<div class="top-agent-card" id="topAgentCard">
|
||
<div class="top-agent-avatar" id="topAgentAvatar">—</div>
|
||
<div class="top-agent-info">
|
||
<div class="top-agent-label">最常使用的智能体</div>
|
||
<div class="top-agent-name" id="stTopAgent">还未使用智能体</div>
|
||
<div class="top-agent-sub" id="stTopAgentSub">创建一个智能体开始对话</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 热力图 -->
|
||
<div class="heatmap-wrap">
|
||
<div class="heatmap-head">
|
||
<div>
|
||
<div class="heatmap-kicker">活动热力图</div>
|
||
<h3>最近使用节奏</h3>
|
||
<p>按天聚合消息量,颜色越深代表当天对话越活跃。</p>
|
||
</div>
|
||
<div class="heatmap-metrics">
|
||
<span id="hmActiveDays">0 个活跃日</span>
|
||
<span id="hmPeakDay">峰值 0</span>
|
||
</div>
|
||
</div>
|
||
<div class="heatmap-board">
|
||
<div class="heatmap-months" id="heatmapMonths"></div>
|
||
<div class="heatmap" id="heatmap"></div>
|
||
</div>
|
||
<div class="heatmap-legend">
|
||
<span>少</span>
|
||
<div class="hm-cell lvl-0"></div>
|
||
<div class="hm-cell lvl-1"></div>
|
||
<div class="hm-cell lvl-2"></div>
|
||
<div class="hm-cell lvl-3"></div>
|
||
<div class="hm-cell lvl-4"></div>
|
||
<span>多</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 日详情 -->
|
||
<div class="dash-section-label" id="daydetailLabel">日详情</div>
|
||
<div class="day-detail" id="dayDetail">
|
||
<div class="history-empty">点上面热力图中的任意一天查看详情</div>
|
||
</div>
|
||
|
||
</div>
|
||
</section>
|
||
|
||
<!-- TAB: 网关 -->
|
||
<section class="tab-panel" id="tab-gateway">
|
||
<div class="panel-head">
|
||
<h2>网关</h2>
|
||
<p>统一管理飞书、微信、WhatsApp、Telegram、Discord 等外部消息入口。</p>
|
||
</div>
|
||
|
||
<div class="integrations-scroll">
|
||
<div class="integration-grid">
|
||
<div class="integration-channel-card active">
|
||
<div class="integration-channel-head">
|
||
<div class="integration-channel-icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a4 4 0 0 1-4 4H8l-5 3V7a4 4 0 0 1 4-4h10a4 4 0 0 1 4 4z"/><path d="M8 9h8"/><path d="M8 13h5"/></svg>
|
||
</div>
|
||
<span class="integration-status ready">已接入</span>
|
||
</div>
|
||
<div class="integration-channel-title">飞书机器人</div>
|
||
<div class="integration-channel-desc">接收私聊、群聊、进群和消息事件,支持多个飞书 App。</div>
|
||
</div>
|
||
|
||
<div class="integration-channel-card muted">
|
||
<div class="integration-channel-head">
|
||
<div class="integration-channel-icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M7 10a5 5 0 0 1 9.6-2"/><path d="M17 14a5 5 0 0 1-9.6 2"/><path d="M3 14a6 6 0 0 0 6 6h2"/><path d="M21 10a6 6 0 0 0-6-6h-2"/></svg>
|
||
</div>
|
||
<span class="integration-status pending">待接入</span>
|
||
</div>
|
||
<div class="integration-channel-title">微信</div>
|
||
<div class="integration-channel-desc">预留企业微信或个人微信网关入口。</div>
|
||
</div>
|
||
|
||
<div class="integration-channel-card muted">
|
||
<div class="integration-channel-head">
|
||
<div class="integration-channel-icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12a7 7 0 1 1 3 5.74L4 19l1.26-4A6.98 6.98 0 0 1 5 12z"/><path d="M9 10h.01"/><path d="M12 10h.01"/><path d="M15 10h.01"/></svg>
|
||
</div>
|
||
<span class="integration-status pending">待接入</span>
|
||
</div>
|
||
<div class="integration-channel-title">WhatsApp</div>
|
||
<div class="integration-channel-desc">预留 WhatsApp Business Webhook。</div>
|
||
</div>
|
||
|
||
<div class="integration-channel-card muted">
|
||
<div class="integration-channel-head">
|
||
<div class="integration-channel-icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.5 4.5 3 11.5l7 2 2 7 9.5-16z"/><path d="m10 13.5 5-5"/></svg>
|
||
</div>
|
||
<span class="integration-status pending">待接入</span>
|
||
</div>
|
||
<div class="integration-channel-title">Telegram</div>
|
||
<div class="integration-channel-desc">预留 Bot Token、Webhook 和群组授权。</div>
|
||
</div>
|
||
|
||
<div class="integration-channel-card muted">
|
||
<div class="integration-channel-head">
|
||
<div class="integration-channel-icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="5" width="16" height="13" rx="4"/><path d="M8 18 6 21"/><path d="m16 18 2 3"/><path d="M9 11h.01"/><path d="M15 11h.01"/><path d="M10 14h4"/></svg>
|
||
</div>
|
||
<span class="integration-status pending">待接入</span>
|
||
</div>
|
||
<div class="integration-channel-title">Discord</div>
|
||
<div class="integration-channel-desc">预留 Discord Bot、频道事件和 Slash Commands。</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="integration-panel" id="feishuSettingsGroup">
|
||
<div class="integration-panel-head">
|
||
<div>
|
||
<div class="integration-panel-title">飞书集成</div>
|
||
<div class="integration-panel-desc">当前已接入的飞书机器人和事件回调地址</div>
|
||
</div>
|
||
<button class="glass-btn-sm" onclick="refreshFeishuApps()">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 0 1 15.5-6.3L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-15.5 6.3L3 16"/><path d="M3 21v-5h5"/></svg>
|
||
刷新
|
||
</button>
|
||
</div>
|
||
<div class="integration-panel-body">
|
||
<div class="feishu-toolbar">
|
||
<div class="settings-help">只显示 App ID、回调地址和服务状态;Secret 与 Token 只保存在服务器环境文件。</div>
|
||
</div>
|
||
<div class="feishu-apps" id="feishuApps">
|
||
<div class="settings-help">打开网关页后自动读取飞书桥接服务。</div>
|
||
</div>
|
||
<form class="feishu-form" id="feishuAddForm" onsubmit="saveFeishuApp(event)">
|
||
<div class="settings-field">
|
||
<label for="feishuAppId">App ID</label>
|
||
<input type="text" id="feishuAppId" placeholder="cli_xxxxxxxxxxxxxxxx" autocomplete="off">
|
||
</div>
|
||
<div class="settings-field">
|
||
<label for="feishuAppSecret">App Secret</label>
|
||
<input type="password" id="feishuAppSecret" placeholder="只写入服务器,不保存在浏览器" autocomplete="off">
|
||
</div>
|
||
<div class="settings-field">
|
||
<label for="feishuVerifyToken">Verification Token</label>
|
||
<input type="password" id="feishuVerifyToken" placeholder="飞书加密策略里的 Verification Token" autocomplete="off">
|
||
</div>
|
||
<div class="feishu-form-actions">
|
||
<button class="glass-btn-sm primary" type="submit">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14"/><path d="M5 12h14"/></svg>
|
||
添加 / 更新机器人
|
||
</button>
|
||
<div class="settings-help">保存前会校验 App ID / Secret;成功后复制回调地址到飞书事件配置。</div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- TAB: 设置 -->
|
||
<section class="tab-panel" id="tab-settings">
|
||
<div class="panel-head">
|
||
<h2>设置</h2>
|
||
<p>调整连接、偏好、数据和外观。</p>
|
||
</div>
|
||
|
||
<div class="settings-scroll">
|
||
|
||
<!-- 连接 -->
|
||
<div class="settings-group wide settings-connection-group">
|
||
<div class="settings-group-head">
|
||
<div class="settings-group-icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
|
||
</div>
|
||
<div>
|
||
<div class="settings-group-title">连接</div>
|
||
<div class="settings-group-desc">爱马仕后端的 API 地址和密钥</div>
|
||
</div>
|
||
</div>
|
||
<div class="settings-group-body">
|
||
<div class="settings-field">
|
||
<label for="apiBase">API 地址</label>
|
||
<input type="text" id="apiBase" value="/api/v1">
|
||
<div class="settings-help">Hermes API Server 的 OpenAI 兼容端点。本地默认 <code>/api/v1</code>(nginx 反代到 127.0.0.1:8642)</div>
|
||
</div>
|
||
<div class="settings-field">
|
||
<label for="apiKey">API Key</label>
|
||
<input type="password" id="apiKey" value="hermes-mini-local-key-2026">
|
||
<div class="settings-help">任意字符串,只要和 Hermes <code>.env</code> 里的 <code>API_SERVER_KEY</code> 一致</div>
|
||
</div>
|
||
<div class="settings-actions">
|
||
<button class="glass-btn-sm" onclick="saveSettings()">保存 API</button>
|
||
<button class="glass-btn-sm" onclick="testApiConnection()">测试连接</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 对话偏好 -->
|
||
<div class="settings-group">
|
||
<div class="settings-group-head">
|
||
<div class="settings-group-icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
|
||
</div>
|
||
<div>
|
||
<div class="settings-group-title">对话偏好</div>
|
||
<div class="settings-group-desc">控制消息发送行为</div>
|
||
</div>
|
||
</div>
|
||
<div class="settings-group-body">
|
||
<div class="settings-field toggle-field">
|
||
<div>
|
||
<label>流式输出</label>
|
||
<div class="settings-help">打开后 AI 边生成边显示,更接近打字机效果</div>
|
||
</div>
|
||
<label class="switch"><input type="checkbox" id="streamMode" checked><span class="slider"></span></label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 外观 -->
|
||
<div class="settings-group">
|
||
<div class="settings-group-head">
|
||
<div class="settings-group-icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="13.5" cy="6.5" r=".5"/><circle cx="17.5" cy="10.5" r=".5"/><circle cx="8.5" cy="7.5" r=".5"/><circle cx="6.5" cy="12.5" r=".5"/><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 0 1 1.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2z"/></svg>
|
||
</div>
|
||
<div>
|
||
<div class="settings-group-title">外观</div>
|
||
<div class="settings-group-desc">主题、字体、动画</div>
|
||
</div>
|
||
</div>
|
||
<div class="settings-group-body">
|
||
<div class="settings-field toggle-field">
|
||
<div>
|
||
<label>主题</label>
|
||
<div class="settings-help">左下角"明亮/暗色"按钮切换</div>
|
||
</div>
|
||
<button class="theme-chip" onclick="toggleTheme()">切换主题</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 数据 -->
|
||
<div class="settings-group wide">
|
||
<div class="settings-group-head">
|
||
<div class="settings-group-icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5v14c0 1.7 4 3 9 3s9-1.3 9-3V5"/><path d="M3 12c0 1.7 4 3 9 3s9-1.3 9-3"/></svg>
|
||
</div>
|
||
<div>
|
||
<div class="settings-group-title">数据</div>
|
||
<div class="settings-group-desc">导出、导入、清空本地数据</div>
|
||
</div>
|
||
</div>
|
||
<div class="settings-group-body">
|
||
<div class="settings-actions">
|
||
<button class="glass-btn-sm" onclick="exportData()">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
|
||
导出 JSON
|
||
</button>
|
||
<button class="glass-btn-sm" onclick="document.getElementById('importFile').click()">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
|
||
导入 JSON
|
||
</button>
|
||
<input type="file" id="importFile" accept=".json" style="display:none" onchange="importData(event)">
|
||
<button class="glass-btn-sm danger-btn" onclick="wipeAll()">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
|
||
清空全部数据
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 周报记录 -->
|
||
<div class="settings-group wide" id="weeklyReportGroup">
|
||
<div class="settings-group-head">
|
||
<div class="settings-group-icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M4 4.5A2.5 2.5 0 0 1 6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5z"/><path d="M8 7h8"/><path d="M8 11h8"/></svg>
|
||
</div>
|
||
<div>
|
||
<div class="settings-group-title">周报记录</div>
|
||
<div class="settings-group-desc">保存任务描述、优化过程和最终周报</div>
|
||
</div>
|
||
</div>
|
||
<div class="settings-group-body">
|
||
<div class="weekly-toolbar">
|
||
<div class="settings-help">对话页点「存周报」会把最近一次任务描述和回答存到这里,之后可以打开、复制或删除。</div>
|
||
<button class="glass-btn-sm" onclick="renderWeeklyReports()">刷新</button>
|
||
</div>
|
||
<div class="weekly-report-list" id="weeklyReportsList">
|
||
<div class="settings-help">还没有保存过周报记录。</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 关于 -->
|
||
<div class="settings-group wide">
|
||
<div class="settings-group-head">
|
||
<div class="settings-group-icon">
|
||
<svg 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 16v-4M12 8h.01"/></svg>
|
||
</div>
|
||
<div>
|
||
<div class="settings-group-title">关于</div>
|
||
<div class="settings-group-desc">爱马仕 · AI</div>
|
||
</div>
|
||
</div>
|
||
<div class="settings-group-body">
|
||
<div class="about-grid">
|
||
<div class="about-item">
|
||
<div class="about-lbl">版本</div>
|
||
<div class="about-val">v0.2 · Liquid Glass</div>
|
||
</div>
|
||
<div class="about-item">
|
||
<div class="about-lbl">运行于</div>
|
||
<div class="about-val">Mac mini M4 · macOS 26.3</div>
|
||
</div>
|
||
<div class="about-item">
|
||
<div class="about-lbl">模型</div>
|
||
<div class="about-val" id="aboutModelValue">以「模型」页配置为准</div>
|
||
</div>
|
||
<div class="about-item">
|
||
<div class="about-lbl">代理</div>
|
||
<div class="about-val">Clash Verge · 127.0.0.1:7897</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-save-bar">
|
||
<button class="glass-btn-sm primary" onclick="saveSettings()">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
||
保存设置
|
||
</button>
|
||
</div>
|
||
|
||
</div>
|
||
</section>
|
||
|
||
</main>
|
||
</div>
|
||
|
||
<script src="./app.js?v=20260511-modules-ia-v36"></script>
|
||
</body>
|
||
</html>
|