595 lines
34 KiB
HTML
595 lines
34 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" />
|
||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||
<link
|
||
href="https://fonts.googleapis.com/css2?family=ZCOOL+KuaiLe&family=Ma+Shan+Zheng&family=Long+Cang&family=ZCOOL+QingKe+HuangYou&display=swap"
|
||
rel="stylesheet"
|
||
/>
|
||
<link rel="icon" href="/image/favicon.svg" sizes="any" />
|
||
<link rel="stylesheet" href="/app.css?v=6" />
|
||
</head>
|
||
<body>
|
||
<div class="bg-scene" id="bgScene" aria-hidden="true">
|
||
<div class="bg-image" id="bgImageLayer"></div>
|
||
<div class="bg-vignette" aria-hidden="true"></div>
|
||
</div>
|
||
<div class="app-root" id="app">
|
||
<!-- 自定义标题栏(仅 Electron 无边框模式) -->
|
||
<header class="title-bar" id="titleBar">
|
||
<span class="title-bar-name">minor Agent</span>
|
||
<div class="title-bar-actions">
|
||
<button class="title-bar-btn title-bar-btn--settings" id="tbSettings" title="系统配置" aria-label="打开系统配置">⚙</button>
|
||
<button class="title-bar-btn" id="tbMin" title="最小化" aria-label="最小化"></button>
|
||
<button class="title-bar-btn" id="tbMax" title="最大化" aria-label="最大化"></button>
|
||
<button class="title-bar-btn title-bar-btn--close" id="tbClose" title="关闭" aria-label="关闭"></button>
|
||
</div>
|
||
</header>
|
||
<div class="app-body">
|
||
<div class="drawer-backdrop" id="drawerBackdrop" aria-hidden="true"></div>
|
||
<aside class="expand-rail" id="expandRail" aria-hidden="true">
|
||
<button type="button" class="rail-btn" id="expandBtn" title="展开侧栏">⟩</button>
|
||
</aside>
|
||
<aside class="sidebar" id="sidebar">
|
||
<div class="sidebar-resize-handle" id="sidebarResizeHandle" title="拖动调整侧栏宽度"></div>
|
||
<div class="sidebar-head">
|
||
<div class="mode-switch" id="modeSwitch">
|
||
<div class="mode-switch-inner">
|
||
<span class="mode-switch-item" data-mode="edit" id="modeEditBtn">Edit</span>
|
||
<span class="mode-switch-sep">|</span>
|
||
<span class="mode-switch-item" data-mode="cron" id="modeCronBtn">Cron</span>
|
||
<span class="mode-switch-sep">|</span>
|
||
<span class="mode-switch-item mode-switch-item--chat active" data-mode="chat" id="modeChatBtn">
|
||
Chat
|
||
<span class="mode-switch-expand" id="modeChatExpand">
|
||
<img src="/image/新增会话.svg" alt="新增会话" width="20" height="20" class="new-chat-icon" id="newSessionBtn" title="新建会话" />
|
||
</span>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sidebar-list-wrap" id="sessionListWrap">
|
||
<ul class="session-list" id="sessionList" role="listbox" aria-label="会话列表"></ul>
|
||
</div>
|
||
<!-- 文档树区域(Edit 模式) -->
|
||
<div class="doc-tree-wrap mode-hidden" id="docTreeWrap">
|
||
<div class="doc-tree" id="docTree"></div>
|
||
</div>
|
||
<!-- 定时任务列表(Cron 模式) -->
|
||
<div class="cron-list-wrap mode-hidden" id="cronListWrap">
|
||
<div class="cron-list-head">
|
||
<span class="cron-list-title">定时任务</span>
|
||
<button type="button" class="cron-new-btn" id="cronNewBtn" title="新建定时任务">+</button>
|
||
</div>
|
||
<ul class="cron-list" id="cronList" role="listbox" aria-label="定时任务列表"></ul>
|
||
</div>
|
||
<p class="sidebar-hint">鼠标悬停可重命名或删除会话。</p>
|
||
<div class="sidebar-collapse">
|
||
<button type="button" class="collapse-fab" id="collapseBtn" title="收起侧栏">⟨</button>
|
||
</div>
|
||
</aside>
|
||
<main class="main" id="mainPanel">
|
||
<header class="main-header">
|
||
<button
|
||
type="button"
|
||
class="menu-btn"
|
||
id="menuBtn"
|
||
aria-expanded="false"
|
||
aria-controls="sidebar"
|
||
title="会话与设置"
|
||
>
|
||
☰
|
||
</button>
|
||
<div class="main-header-text" id="mainHeaderText">
|
||
<h1 class="title">多模态Agent</h1>
|
||
</div>
|
||
<button
|
||
type="button"
|
||
class="settings-btn"
|
||
id="settingsBtn"
|
||
title="系统配置"
|
||
aria-label="打开系统配置"
|
||
>
|
||
⚙
|
||
</button>
|
||
</header>
|
||
<div class="main-content-wrap" id="mainContentWrap">
|
||
<!-- Chat 模式 -->
|
||
<section class="chat-shell" id="chatPanel">
|
||
<div class="chat-placeholder" id="chatPlaceholder">
|
||
<h3></h3>
|
||
<p></p>
|
||
<p class="muted"></p>
|
||
</div>
|
||
<div class="chat-messages" id="chatMessages" hidden></div>
|
||
</section>
|
||
<!-- 文件预览浮窗 -->
|
||
<div class="file-preview-overlay" id="filePreviewOverlay" hidden>
|
||
<div class="file-preview-dialog" id="filePreviewDialog">
|
||
<div class="file-preview-header">
|
||
<span class="file-preview-name" id="filePreviewName"></span>
|
||
<span class="file-preview-status" id="filePreviewStatus"></span>
|
||
<div class="file-preview-actions">
|
||
<button type="button" class="file-preview-btn" id="filePreviewDownload" title="下载">⬇</button>
|
||
<button type="button" class="file-preview-btn file-preview-close" id="filePreviewClose" title="关闭">✕</button>
|
||
</div>
|
||
</div>
|
||
<div class="file-preview-body" id="filePreviewBody"></div>
|
||
</div>
|
||
</div>
|
||
<!-- 文档修改面板 -->
|
||
<div class="doc-mod-panel" id="docModPanel" hidden>
|
||
<div class="doc-mod-header">
|
||
<span>文档变更</span>
|
||
<button type="button" class="doc-mod-close" id="docModClose">✕</button>
|
||
</div>
|
||
<div class="doc-mod-list" id="docModList"></div>
|
||
<div class="doc-mod-actions" id="docModActions" hidden>
|
||
<button type="button" class="btn btn-primary" id="docModAcceptAll">全部保留</button>
|
||
<button type="button" class="btn btn-danger" id="docModRejectAll">全部撤销</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 欢迎页(空会话居中时显示,在 main-content-wrap 外防止被折叠) -->
|
||
<div class="welcome-view" id="welcomeView" hidden>
|
||
<div class="welcome-content">
|
||
<div class="welcome-left">
|
||
<img src="/image/icon.svg" alt="" class="welcome-icon" />
|
||
</div>
|
||
<div class="welcome-right">
|
||
<h2 class="welcome-greeting" id="welcomeGreeting">欢迎使用 minor Agent</h2>
|
||
<div class="recent-folders-wrap" id="recentFoldersWrap" hidden>
|
||
<div class="recent-folders-label">最近打开</div>
|
||
<ul class="recent-folders-list" id="recentFoldersList"></ul>
|
||
</div>
|
||
</div>
|
||
<div class="welcome-clock-wrap">
|
||
<span class="welcome-clock" id="welcomeClock"></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<footer class="composer">
|
||
<div class="composer-refs" id="composerRefs" hidden></div>
|
||
<div class="composer-row">
|
||
<div class="mm-input" role="group" aria-label="多模态输入">
|
||
<div class="composer-input-shell">
|
||
<div class="input-inline-area" id="inputInlineArea">
|
||
<div class="input-inline-images" id="inputInlineImages"></div>
|
||
<div class="input-inline-chips" id="inputInlineChips"></div>
|
||
</div>
|
||
<textarea
|
||
id="textInput"
|
||
rows="2"
|
||
placeholder="输入消息或添加附件,Enter 发送(Shift+Enter 换行)"
|
||
></textarea>
|
||
<div class="composer-input-bottom-row">
|
||
<div class="composer-input-bottom-left">
|
||
<button type="button" class="voice-toggle-btn" id="voiceToggleBtn" title="文字/语音回复开关" aria-label="语音回复开关" aria-pressed="false">
|
||
<img src="/image/文本语音 .svg" alt="语音" aria-hidden="true" />
|
||
</button>
|
||
<div class="inline-drop-wrap" id="accessModeWrap">
|
||
<button type="button" class="inline-drop-trigger inline-drop-trigger--icon" id="accessModeTrigger" aria-label="工作空间访问模式" title="工作空间访问模式:限制访问 / 权限审查 / 完全访问">
|
||
<img src="/image/限制访问.svg" alt="访问模式" id="accessModeIcon" />
|
||
</button>
|
||
<div class="inline-drop-panel" id="accessModePanel" hidden>
|
||
<div class="inline-drop-item is-selected access-mode-item--restricted" data-value="restricted">限制访问</div>
|
||
<div class="inline-drop-item access-mode-item--approval" data-value="approval">权限审查</div>
|
||
<div class="inline-drop-item access-mode-item--full" data-value="full">完全访问</div>
|
||
</div>
|
||
</div>
|
||
<span class="input-actions-sep"></span>
|
||
<button type="button" class="action-icon-btn" id="actionTodoBtn" title="Todo List">
|
||
<img src="/image/todolist.svg" alt="Todo" />
|
||
</button>
|
||
<button type="button" class="action-icon-btn" id="actionPendingBtn" title="人机交互">
|
||
<img src="/image/人机交互.svg" alt="交互" />
|
||
</button>
|
||
<button type="button" class="action-icon-btn" id="actionDocBtn" title="文档变更">
|
||
<img src="/image/文档按钮.svg" alt="文档" />
|
||
</button>
|
||
<button type="button" class="action-icon-btn" id="actionCanvasBtn" title="画板">
|
||
<img src="/image/画板.svg" alt="画板" />
|
||
</button>
|
||
</div>
|
||
<div class="composer-input-bottom-right">
|
||
<div class="token-ring-wrap" id="tokenRingWrap">
|
||
<button type="button" class="token-ring-btn" id="tokenRingBtn" title="上下文 Token 占用" aria-label="上下文 Token 占用">
|
||
<svg class="token-ring-svg" id="tokenRingSvg" viewBox="0 0 30 30" width="13" height="13">
|
||
<circle class="token-ring-bg" cx="15" cy="15" r="11"></circle>
|
||
<circle class="token-ring-fill" id="tokenRingFill" cx="15" cy="15" r="11"></circle>
|
||
</svg>
|
||
</button>
|
||
<div class="token-ring-panel" id="tokenRingPanel" hidden>
|
||
<div class="token-ring-capacity" id="tokenRingCapacity">上下文容量 0k / 0k (0%)</div>
|
||
<div class="token-ring-rate-track" id="tokenRingRateTrack">
|
||
<div class="token-ring-rate-fill" id="tokenRingRateFill"></div>
|
||
<div class="token-ring-threshold" id="tokenRingThreshold"></div>
|
||
</div>
|
||
<div class="token-ring-rate-label" id="tokenRingRateLabel">压缩阈值 60%(在环境变量中修改)</div>
|
||
<div class="token-ring-breakdown" id="tokenRingBreakdown"></div>
|
||
</div>
|
||
</div>
|
||
<div class="inline-drop-wrap" id="modelSelectWrap">
|
||
<button type="button" class="inline-drop-trigger inline-drop-trigger--model" id="modelTrigger" aria-label="模型选择">模型</button>
|
||
<div class="inline-drop-panel" id="modelPanel" hidden></div>
|
||
</div>
|
||
<div class="inline-drop-wrap" id="thinkModeWrap">
|
||
<button type="button" class="inline-drop-trigger inline-drop-trigger--icon" id="thinkModeTrigger" aria-label="思考模式" title="思考模式:low / high / xhigh / max / ultra">
|
||
<img src="/image/思考模式.svg" alt="思考" id="thinkModeIcon" />
|
||
</button>
|
||
<div class="inline-drop-panel think-mode-panel" id="thinkModePanel" hidden>
|
||
<div class="think-mode-labels" id="thinkModeLabels"></div>
|
||
<div class="think-mode-track" id="thinkModeTrack">
|
||
<div class="think-mode-fill" id="thinkModeFill"></div>
|
||
<div class="think-mode-handle" id="thinkModeHandle"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="ws-btn-wrap" id="wsBtnWrap">
|
||
<button type="button" class="ws-btn" id="wsBtn" title="选择工作空间" aria-label="选择工作空间">
|
||
<img src="/image/文件夹.svg" alt="" aria-hidden="true" />
|
||
</button>
|
||
<div class="ws-dropdown" id="wsDropdown" hidden>
|
||
<div class="ws-dropdown-header">工作空间</div>
|
||
<div class="ws-dropdown-list" id="wsDropdownList"></div>
|
||
<div class="ws-dropdown-add">
|
||
<button type="button" class="ws-add-btn" id="wsAddBtn" title="添加工作空间">+ 添加工作空间</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<button type="button" class="mic-btn" id="micBtn" title="使用麦克风录音" aria-pressed="false" aria-label="使用麦克风录音">
|
||
<img src="/image/microphone.svg" alt="" aria-hidden="true" />
|
||
</button>
|
||
<label class="attachment-btn" title="上传附件" aria-label="上传附件">
|
||
<img src="/image/附件.svg" alt="" aria-hidden="true" class="attachment-btn-icon" />
|
||
<input type="file" id="fileInput" multiple hidden />
|
||
</label>
|
||
<button type="button" class="send-btn" id="sendBtn" title="发送" aria-label="发送">
|
||
<img src="/image/发送.svg" alt="" aria-hidden="true" class="send-btn-icon" />
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
</main>
|
||
</div>
|
||
|
||
<!-- 新建会话确认弹窗 -->
|
||
<div class="confirm-overlay" id="newSessionConfirm" hidden>
|
||
<div class="confirm-dialog">
|
||
<div class="confirm-title">新建会话</div>
|
||
<div class="confirm-body">
|
||
当前工作区存在 <strong id="confirmPendingCount">0</strong> 个未处理的文件变更。
|
||
请先接受或撤销这些变更后再新建会话。
|
||
</div>
|
||
<div class="confirm-actions">
|
||
<button type="button" class="confirm-btn confirm-btn--secondary" id="confirmCancel">取消</button>
|
||
<button type="button" class="confirm-btn confirm-btn--danger" id="confirmRejectAll">全部撤销并新建</button>
|
||
<button type="button" class="confirm-btn confirm-btn--primary" id="confirmAcceptAll">全部接受并新建</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 定时任务编辑/新建弹窗 -->
|
||
<div class="cron-edit-overlay" id="cronEditOverlay" hidden>
|
||
<div class="cron-edit-dialog" role="dialog" aria-modal="true" aria-label="定时任务编辑">
|
||
<div class="cron-edit-header">
|
||
<h3 id="cronEditTitle">新建定时任务</h3>
|
||
<button type="button" class="cron-edit-close" id="cronEditClose" aria-label="关闭">✕</button>
|
||
</div>
|
||
<div class="cron-edit-body">
|
||
<div class="cron-field">
|
||
<label class="cron-field-label" for="cronEditName">任务名称</label>
|
||
<input type="text" id="cronEditName" placeholder="如:每日晨报" autocomplete="off" />
|
||
</div>
|
||
<div class="cron-field">
|
||
<label class="cron-field-label" for="cronEditPrompt">任务提示词</label>
|
||
<textarea id="cronEditPrompt" placeholder="触发时投递给 Agent 的指令,如:查询今日天气并生成晨报"></textarea>
|
||
<span class="cron-field-hint">该提示词将在每次触发时投递给 Agent 自主执行。</span>
|
||
<span class="cron-field-hint cron-field-hint--warn" id="cronAccessModeHint" hidden>⚠️ 定时任务无人值守执行:当前访问模式为「限制访问/权限审查」时,超出工作空间的操作会被拦截或退化拦截,建议先切换为「完全访问」。</span>
|
||
</div>
|
||
<div class="cron-field">
|
||
<span class="cron-field-label">触发方式</span>
|
||
<div class="cron-trigger-types">
|
||
<button type="button" class="cron-trigger-type-btn" data-trigger-type="cron">定时闹钟</button>
|
||
<button type="button" class="cron-trigger-type-btn" data-trigger-type="interval">固定间隔</button>
|
||
<button type="button" class="cron-trigger-type-btn" data-trigger-type="once">一次性延时</button>
|
||
</div>
|
||
<!-- 闹钟式:周几方块 + 时分滚轮选择器 -->
|
||
<div data-trigger-field="cron" hidden>
|
||
<div class="cron-alarm-picker" id="cronAlarmPicker" data-mode="alarm">
|
||
<div class="cron-weekday-row" id="cronWeekdayRow" role="group" aria-label="选择星期"></div>
|
||
<div class="cron-time-row">
|
||
<span class="cron-time-label">触发时间</span>
|
||
<div class="cron-time-picker" id="cronTimePicker" role="group" aria-label="选择时间"></div>
|
||
</div>
|
||
<div class="cron-advanced-toggle">
|
||
<button type="button" id="cronAdvancedToggle">高级:手动输入 cron 表达式</button>
|
||
</div>
|
||
<div class="cron-advanced-raw" id="cronAdvancedRaw" hidden>
|
||
<input type="text" id="cronEditCron" placeholder="0 9 * * *(分 时 日 月 周)" autocomplete="off" />
|
||
<span class="cron-field-hint">标准 5 段 cron,如 <code>0 9 * * *</code> 每天9点、<code>*/30 * * * *</code> 每30分钟。</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div data-trigger-field="interval" hidden>
|
||
<input type="number" id="cronEditInterval" placeholder="3600(秒)" min="1" />
|
||
<span class="cron-field-hint">固定间隔秒数,如 3600 = 每小时。</span>
|
||
</div>
|
||
<!-- 一次性延时:主题化日期 + 时分选择器(替代原生 datetime-local) -->
|
||
<div data-trigger-field="once" hidden>
|
||
<div class="cron-once-picker" id="cronOncePicker">
|
||
<div class="cron-once-date-row">
|
||
<span class="cron-time-label">日期</span>
|
||
<div class="cron-date-picker" id="cronDatePicker"></div>
|
||
</div>
|
||
<div class="cron-time-row">
|
||
<span class="cron-time-label">时间</span>
|
||
<div class="cron-time-picker" id="cronOnceTimePicker" role="group" aria-label="选择时间"></div>
|
||
</div>
|
||
<input type="hidden" id="cronEditRunAt" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="cron-field">
|
||
<label class="cron-field-label" for="cronEditTimeout">单次执行超时(秒)</label>
|
||
<input type="number" id="cronEditTimeout" placeholder="300" min="10" value="300" />
|
||
</div>
|
||
<div class="cron-toggle-row">
|
||
<span class="cron-field-label">启用任务</span>
|
||
<button type="button" class="cron-switch" id="cronEnabledSwitch" data-on="true" aria-label="启用任务"></button>
|
||
</div>
|
||
</div>
|
||
<div class="cron-edit-footer">
|
||
<button type="button" class="cron-edit-btn cron-edit-btn--cancel" id="cronEditCancel">取消</button>
|
||
<button type="button" class="cron-edit-btn cron-edit-btn--save" id="cronEditSave">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 系统配置模态框 -->
|
||
<div class="settings-overlay" id="settingsOverlay" hidden>
|
||
<div class="settings-dialog" id="settingsDialog" role="dialog" aria-modal="true" aria-label="系统配置">
|
||
<div class="settings-header">
|
||
<h2>系统配置</h2>
|
||
<button type="button" class="settings-close-btn" id="settingsCloseBtn" aria-label="关闭">✕</button>
|
||
</div>
|
||
<div class="settings-tabs">
|
||
<button type="button" class="settings-tab active" data-tab="agent">Agent</button>
|
||
<button type="button" class="settings-tab" data-tab="tool">工具</button>
|
||
<button type="button" class="settings-tab" data-tab="env">环境变量</button>
|
||
<button type="button" class="settings-tab" data-tab="skills">Skills</button>
|
||
<button type="button" class="settings-tab" data-tab="other">GUI设置</button>
|
||
<button type="button" class="settings-tab" data-tab="theme">主题/语言</button>
|
||
</div>
|
||
<div class="settings-body">
|
||
<!-- Agent 配置面板 -->
|
||
<div class="settings-panel active" data-panel="agent" id="agentPanel">
|
||
<div class="settings-toolbar">
|
||
<button type="button" class="btn btn-primary" id="addAgentBtn">+ 添加 Agent</button>
|
||
</div>
|
||
<div class="settings-agents-list" id="agentsList"></div>
|
||
<button type="button" class="btn btn-primary settings-save-btn" data-save="agents">保存 Agent 配置</button>
|
||
</div>
|
||
<!-- 工具配置面板 -->
|
||
<div class="settings-panel" data-panel="tool" id="toolPanel" hidden>
|
||
<div class="settings-tools-list" id="toolsList"></div>
|
||
<button type="button" class="btn btn-primary settings-save-btn" data-save="tools">保存工具配置</button>
|
||
</div>
|
||
<!-- 环境变量配置面板 -->
|
||
<div class="settings-panel" data-panel="env" id="envPanel" hidden>
|
||
<div class="settings-env-list" id="envList"></div>
|
||
<button type="button" class="btn btn-primary settings-save-btn" data-save="env">保存环境变量配置</button>
|
||
</div>
|
||
<!-- Skills 管理面板 -->
|
||
<div class="settings-panel" data-panel="skills" id="skillsPanel" hidden>
|
||
<div class="settings-toolbar">
|
||
<div class="settings-toolbar-buttons">
|
||
<button type="button" class="btn btn-primary" id="addSkillBtn">+ 创建 Skill</button>
|
||
<span class="skill-import-wrap">
|
||
<button type="button" class="btn btn-primary import-skill-btn" id="importSkillBtn" title="导入 zip 或文件夹(自动解析 skill.md)">导入 Skill</button>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<input type="file" id="importSkillZipInput" accept=".zip" hidden />
|
||
<input type="file" id="importSkillFolderInput" webkitdirectory multiple hidden />
|
||
<div class="skills-list" id="skillsList"></div>
|
||
<!-- Skill 编辑/创建弹窗 -->
|
||
<div class="skill-editor-overlay" id="skillEditorOverlay" hidden>
|
||
<div class="skill-editor-dialog" role="dialog" aria-modal="true" aria-label="编辑 Skill">
|
||
<div class="skill-editor-header">
|
||
<h3 id="skillEditorTitle">创建 Skill</h3>
|
||
<button type="button" class="skill-editor-close" id="skillEditorClose">✕</button>
|
||
</div>
|
||
<div class="skill-editor-body">
|
||
<div class="setting-field">
|
||
<label>名称 (name)</label>
|
||
<input type="text" id="skillNameInput" placeholder="skill 名称(即文件夹名,英文+下划线)" />
|
||
</div>
|
||
<div class="setting-field">
|
||
<label>描述 (description)</label>
|
||
<input type="text" id="skillDescInput" placeholder="简要描述此 skill 的用途" />
|
||
</div>
|
||
<div class="setting-field">
|
||
<label>标签 (tags,逗号分隔,可选)</label>
|
||
<input type="text" id="skillTagsInput" placeholder="e.g. python, 自动化, GUI" />
|
||
</div>
|
||
<div class="setting-field">
|
||
<div class="toggle-row">
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="skillEnableCheck" checked />
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
<span class="toggle-label" id="skillEnableLabel">启用此 Skill(关闭后 Agent 将无法看到此 Skill)</span>
|
||
</div>
|
||
</div>
|
||
<div class="setting-field">
|
||
<label>指南内容 (skill.md)</label>
|
||
<textarea id="skillContentInput" rows="14" placeholder="编写此 skill 的操作指南(Markdown 格式)..."></textarea>
|
||
</div>
|
||
<div class="setting-field">
|
||
<label>附件文件</label>
|
||
<div class="skill-attachments" id="skillAttachmentsList"></div>
|
||
<label class="btn btn-ghost" style="margin-top:0.4rem;display:inline-flex;align-items:center;gap:0.3rem;">
|
||
+ 上传附件
|
||
<input type="file" id="skillFileInput" multiple hidden />
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div class="skill-editor-footer">
|
||
<span class="skill-editor-status" id="skillEditorStatus"></span>
|
||
<button type="button" class="btn btn-secondary" id="skillEditorCancel">取消</button>
|
||
<button type="button" class="btn btn-primary" id="skillEditorSave">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 其他设置面板 -->
|
||
<div class="settings-panel" data-panel="other" id="otherPanel" hidden>
|
||
<div class="settings-section">
|
||
<h4 class="settings-section-title">GUI 操作区域</h4>
|
||
<p class="settings-hint">选择 Agent 进行 GUI 自动化操作(点击、截图、拖拽等)的目标显示器。</p>
|
||
<div class="gui-monitors-grid" id="guiMonitorsGrid"></div>
|
||
</div>
|
||
<button type="button" class="btn btn-primary settings-save-btn" data-save="gui">保存 GUI 设置</button>
|
||
</div>
|
||
<!-- 主题/语言面板 -->
|
||
<div class="settings-panel" data-panel="theme" id="themePanel" hidden>
|
||
<div class="settings-section">
|
||
<h4 class="settings-section-title">主题预设</h4>
|
||
<div class="theme-grid" id="themeGrid"></div>
|
||
</div>
|
||
<div class="settings-section">
|
||
<h4 class="settings-section-title" style="margin-bottom:0.3rem;">自定义背景</h4>
|
||
<p class="settings-hint">输入本地图片路径或 URL(支持 jpg/png/webp),留空恢复默认。</p>
|
||
<div class="setting-field" style="margin-bottom:0.4rem;">
|
||
<div class="bg-input-row">
|
||
<input type="text" id="bgImageInput" placeholder="例如:C:\wallpaper.jpg 或 https://..." />
|
||
<button type="button" class="bg-picker-btn" id="bgImagePickerBtn" title="选择图片">⋮</button>
|
||
<input type="file" id="bgImageFileInput" accept="image/*" hidden />
|
||
</div>
|
||
</div>
|
||
<div style="display:flex;gap:0.5rem;">
|
||
<button type="button" class="btn btn-primary" id="bgImageApplyBtn">应用</button>
|
||
<button type="button" class="btn btn-secondary" id="bgImageResetBtn">恢复默认</button>
|
||
</div>
|
||
<span class="bg-preview-status" id="bgPreviewStatus" style="font-size:11px;color:#6a6a6a;display:block;margin-top:0.4rem;"></span>
|
||
</div>
|
||
<div class="settings-section">
|
||
<h4 class="settings-section-title" style="margin-bottom:0.3rem;">背景效果</h4>
|
||
<div style="display:flex;gap:1rem;">
|
||
<div class="setting-field" style="flex:1;">
|
||
<label style="font-size:0.7rem;display:block;margin-bottom:0.15rem;">模糊度 <span id="bgBlurVal" style="color:var(--accent);">0px</span></label>
|
||
<input type="range" id="bgBlurSlider" min="0" max="10" step="1" value="0" class="compact-slider">
|
||
</div>
|
||
<div class="setting-field" style="flex:1;">
|
||
<label style="font-size:0.7rem;display:block;margin-bottom:0.15rem;">暗角 <span id="bgVignetteVal" style="color:var(--accent);">0%</span></label>
|
||
<input type="range" id="bgVignetteSlider" min="0" max="1" step="0.05" value="0" class="compact-slider">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="settings-section">
|
||
<h4 class="settings-section-title" style="margin-bottom:0.3rem;">字体与字号</h4>
|
||
<div style="display:flex;gap:0.5rem;align-items:flex-end;">
|
||
<div style="flex:1;">
|
||
<label style="font-size:0.7rem;display:block;margin-bottom:0.2rem;">字体</label>
|
||
<select id="fontFamilySelect" class="lang-select" style="max-width:none;">
|
||
<option value="system">系统默认</option>
|
||
<option value="sans">无衬线</option>
|
||
<option value="serif">衬线</option>
|
||
<option value="mono">等宽</option>
|
||
<option value="zcool">站酷快乐体</option>
|
||
<option value="mashan">马山正楷</option>
|
||
<option value="longcang">龙藏手写</option>
|
||
<option value="zcool-qy">站酷庆科黄油体</option>
|
||
</select>
|
||
</div>
|
||
<div style="flex:1;">
|
||
<label style="font-size:0.7rem;display:block;margin-bottom:0.2rem;">字号</label>
|
||
<select id="fontSizeSelect" class="lang-select" style="max-width:none;">
|
||
<option value="12px">12px</option>
|
||
<option value="13px">小 (13px)</option>
|
||
<option value="14px" selected>中 (14px)</option>
|
||
<option value="15px">大 (15px)</option>
|
||
<option value="16px">16px</option>
|
||
<option value="18px">特大 (18px)</option>
|
||
<option value="20px">20px</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="settings-section" style="margin-top:1.2rem;">
|
||
<h4 class="settings-section-title">界面语言</h4>
|
||
<div class="lang-select-wrap">
|
||
<select id="langSelect" class="lang-select"></select>
|
||
</div>
|
||
<p class="theme-note">主题即时生效,语言需刷新页面后生效。</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="settings-footer">
|
||
<span class="settings-status" id="settingsStatus"></span>
|
||
<button type="button" class="btn btn-secondary" id="settingsCancelBtn">取消</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="toast-container" id="toastContainer" aria-live="polite"></div>
|
||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.1.6/dist/purify.min.js"></script>
|
||
<!-- Monaco Editor: CDN 优先(Web端),本地 npm 包兜底(Electron 离线) -->
|
||
<script>
|
||
(function() {
|
||
var base = 'https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/min/vs';
|
||
var s = document.createElement('script');
|
||
s.src = base + '/loader.js';
|
||
s.onload = function() {
|
||
require.config({ paths: { vs: base } });
|
||
window._monacoReady = new Promise(function(r) { require(['vs/editor/editor.main'], function() { r(monaco); }); });
|
||
};
|
||
s.onerror = function() {
|
||
var localBase = '/packages/node_modules/monaco-editor/min/vs';
|
||
var ls = document.createElement('script');
|
||
ls.src = localBase + '/loader.js';
|
||
ls.onload = function() {
|
||
require.config({ paths: { vs: localBase } });
|
||
window._monacoReady = new Promise(function(r) { require(['vs/editor/editor.main'], function() { r(monaco); }); });
|
||
};
|
||
document.head.appendChild(ls);
|
||
};
|
||
document.head.appendChild(s);
|
||
})();
|
||
</script>
|
||
<script>
|
||
// 诊断:验证基本 JS 可执行
|
||
window.__jsDiag = { ok: true, ts: Date.now() };
|
||
// 全局错误捕获(模块加载失败也会出现在这里)
|
||
function _showJsError(msg) {
|
||
var ph = document.getElementById('chatPlaceholder');
|
||
if (ph) {
|
||
ph.hidden = false;
|
||
ph.style.display = '';
|
||
ph.innerHTML = '<h3>\u26A0 JS \u52A0\u8F7D\u5931\u8D25</h3><p style="color:#ef4444">' +
|
||
(msg || 'unknown') +
|
||
'</p><p class="muted">\u8BF7\u6253\u5F00\u63A7\u5236\u53F0 (Ctrl+Shift+I) \u67E5\u770B\u8BE6\u60C5</p>';
|
||
}
|
||
}
|
||
window.addEventListener('error', function(e) {
|
||
if (e.target && e.target !== window && e.target.tagName === 'SCRIPT') {
|
||
_showJsError('Module load: ' + (e.target.src || e.target.dataset?.src || 'app.js'));
|
||
} else if (e.message) {
|
||
_showJsError(e.message);
|
||
}
|
||
});
|
||
window.addEventListener('unhandledrejection', function(e) {
|
||
_showJsError('Promise: ' + (e.reason?.message || String(e.reason)));
|
||
});
|
||
</script>
|
||
<script type="module" src="/app.js?v=6"></script>
|
||
</body>
|
||
</html>
|