Files
cc-web/public/index.html

358 lines
22 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<meta name="application-name" content="CC-Web">
<meta name="theme-color" content="#020c16">
<title>CC-Web</title>
<link rel="icon" href="favicon.ico" sizes="any">
<link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png">
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png">
<link rel="manifest" href="site.webmanifest">
<script>
(function () {
var theme = localStorage.getItem('cc-web-theme') || 'washi';
if (theme === 'gilded') {
theme = 'washi';
localStorage.setItem('cc-web-theme', theme);
}
var dividerTime = localStorage.getItem('cc-web-show-divider-time') === '0' ? 'hide' : 'show';
document.documentElement.dataset.theme = theme;
document.documentElement.dataset.dividerTime = dividerTime;
})();
</script>
<link rel="stylesheet" href="style.css?v=20260803-usage-statistics">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
</head>
<body>
<!-- Login -->
<div id="login-overlay" class="login-overlay">
<div class="login-box">
<img class="login-logo" src="icon-192.png" alt="CC-Web">
<h2>CC-Web</h2>
<p>Claude / Codex Web Chat</p>
<form id="login-form">
<input type="password" id="login-password" placeholder="输入密码" autocomplete="current-password" autofocus>
<label class="remember-label">
<input type="checkbox" id="remember-pw"> 记住密码
</label>
<button type="submit">登录</button>
</form>
<div id="login-error" class="login-error" hidden>密码错误</div>
</div>
</div>
<!-- Main App -->
<div id="app" class="app" hidden>
<!-- Sidebar -->
<aside id="sidebar" class="sidebar">
<div class="sidebar-header">
<div class="new-chat-split">
<button id="new-chat-btn" class="new-chat-btn"><span class="new-chat-glyph" aria-hidden="true">+</span> 新会话</button>
<button id="new-chat-arrow" class="new-chat-arrow" title="更多"></button>
</div>
<div id="new-chat-dropdown" class="new-chat-dropdown" hidden>
<button id="import-session-btn">导入本地 CLI 会话</button>
</div>
<div class="session-search-row">
<div class="session-search">
<input id="session-search-input" class="session-search-input" type="search" placeholder="检索会话 / 项目" autocomplete="off" aria-label="检索会话或项目">
<button id="session-search-clear" class="session-search-clear" type="button" title="清空检索" aria-label="清空检索" hidden>×</button>
</div>
<button id="advanced-search-open" class="advanced-search-open" type="button" title="高级会话检索" aria-label="打开高级会话检索" aria-controls="advanced-search-panel" aria-expanded="false"></button>
</div>
</div>
<div id="session-list" class="session-list"></div>
<div class="sidebar-footer">
<div class="sidebar-footer-actions" role="group" aria-label="CC-Web 工具">
<button id="settings-btn" class="settings-btn" type="button" title="设置" aria-label="打开设置"></button>
<button id="usage-dashboard-open" class="usage-dashboard-open" type="button" title="使用统计" aria-label="打开使用统计" aria-controls="usage-dashboard-panel" aria-expanded="false" hidden>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M5 21v-6"></path><path d="M12 21V9"></path><path d="M19 21V3"></path>
</svg>
</button>
</div>
<span class="brand">CC-Web</span>
</div>
</aside>
<div id="sidebar-overlay" class="sidebar-overlay" hidden></div>
<!-- Chat -->
<main class="chat-main">
<header class="chat-header">
<button id="menu-btn" class="menu-btn" type="button" title="收起左侧菜单" aria-label="收起左侧菜单" aria-controls="sidebar" aria-expanded="true"></button>
<span id="chat-title" class="chat-title">新会话</span>
<button id="chat-session-id-btn" class="chat-session-id-btn" type="button" title="复制当前会话 ID" hidden>ID</button>
<button id="chat-cwd" class="chat-cwd" type="button" hidden></button>
</header>
<div class="messages-wrap">
<div id="messages" class="messages">
<div class="welcome-msg">
<section class="warframe-hero" aria-labelledby="warframe-welcome-title">
<div class="warframe-hero-art" aria-hidden="true">
<img class="warframe-hero-image" src="assets/themes/gilded-wasteland.png" alt="" draggable="false" decoding="async">
<span class="warframe-hero-orbit"></span>
<span class="warframe-hero-emblem"></span>
</div>
<div class="wasteland-welcome-art" aria-hidden="true">
<div class="wasteland-welcome-surface"></div>
<img class="wasteland-welcome-frame" src="assets/themes/wasteland/frames/welcome-card.png" alt="" draggable="false" decoding="async">
</div>
<div class="welcome-icon"></div>
<div class="welcome-copy">
<div class="welcome-kicker">OROKIN FIELD ARCHIVE · CC-WEB</div>
<h3 id="warframe-welcome-title" data-welcome-project-copy></h3>
<p>本次你要构建什么?</p>
</div>
</section>
</div>
</div>
<div class="custom-scrollbar" id="custom-scrollbar">
<div class="custom-scrollbar-thumb" id="custom-scrollbar-thumb"></div>
</div>
</div>
<!-- Slash command menu -->
<div id="cmd-menu" class="cmd-menu" hidden></div>
<section id="advanced-search-panel" class="advanced-search-panel" role="dialog" aria-labelledby="advanced-search-title" hidden>
<div class="advanced-search-shell">
<header class="advanced-search-header">
<button id="advanced-search-close" class="advanced-search-close" type="button" aria-label="关闭高级会话检索"></button>
<div class="advanced-search-field">
<label id="advanced-search-title" class="advanced-search-title" for="advanced-search-input">高级会话检索</label>
<div class="advanced-search-input-wrap">
<input id="advanced-search-input" class="advanced-search-input" type="search" placeholder="搜索会话标题、路径与消息内容" autocomplete="off" aria-label="高级会话检索关键词" aria-describedby="advanced-search-status">
<button id="advanced-search-clear" class="advanced-search-clear" type="button" aria-label="清空高级检索关键词" hidden>×</button>
</div>
</div>
<button id="advanced-search-submit" class="advanced-search-submit" type="button" aria-label="执行高级会话检索"></button>
</header>
<div class="advanced-search-toolbar" aria-label="高级检索选项">
<div class="advanced-search-toggle-group" role="group" aria-label="排序方式">
<button id="advanced-search-sort-relevance" class="advanced-search-pill" type="button" aria-pressed="false">最相关</button>
<button id="advanced-search-sort-newest" class="advanced-search-pill" type="button" aria-pressed="true">最新优先</button>
</div>
<div class="advanced-search-toggle-group" role="group" aria-label="匹配方式">
<button id="advanced-search-match-contains" class="advanced-search-pill" type="button" aria-pressed="false">包含</button>
<button id="advanced-search-match-word" class="advanced-search-pill" type="button" aria-pressed="true">整词</button>
</div>
</div>
<div id="advanced-search-status" class="advanced-search-status" role="status" aria-live="polite">输入至少 2 个字符开始检索</div>
<div id="advanced-search-results" class="advanced-search-results" role="list" aria-label="高级检索结果"></div>
<div id="advanced-search-empty" class="advanced-search-empty" role="status" hidden>没有匹配的会话</div>
</div>
</section>
<section id="usage-dashboard-panel" class="usage-dashboard" role="dialog" aria-labelledby="usage-dashboard-title" aria-describedby="usage-dashboard-status" data-state="idle" hidden>
<div class="usage-dashboard__shell">
<header class="usage-dashboard__header">
<button id="usage-dashboard-close" class="usage-dashboard__icon-button usage-dashboard__back" type="button" aria-label="返回聊天" title="返回聊天">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="m15 18-6-6 6-6"></path>
</svg>
</button>
<div class="usage-dashboard__heading">
<h2 id="usage-dashboard-title">使用统计</h2>
<p id="usage-dashboard-status" role="status" aria-live="polite">基于当前保留的会话数据</p>
</div>
<div class="usage-dashboard__header-actions">
<time id="usage-dashboard-generated-at" class="usage-dashboard__generated-at"></time>
<button id="usage-dashboard-refresh" class="usage-dashboard__icon-button" type="button" aria-label="刷新统计" title="刷新统计">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M21 12a9 9 0 0 0-15.12-6.6L3 8"></path><path d="M3 3v5h5"></path><path d="M3 12a9 9 0 0 0 15.12 6.6L21 16"></path><path d="M16 16h5v5"></path>
</svg>
</button>
</div>
</header>
<div class="usage-dashboard__toolbar" aria-label="统计范围">
<div class="usage-dashboard__periods" role="group" aria-label="快捷时间范围">
<button id="usage-dashboard-period-week" class="usage-dashboard__period" type="button" aria-pressed="true">本周</button>
<button id="usage-dashboard-period-month" class="usage-dashboard__period" type="button" aria-pressed="false">本月</button>
</div>
<div class="usage-dashboard__dates">
<label><input id="usage-dashboard-from" type="date" aria-label="统计开始日期"></label>
<span aria-hidden="true"></span>
<label><input id="usage-dashboard-to" type="date" aria-label="统计结束日期"></label>
<button id="usage-dashboard-apply" class="usage-dashboard__apply" type="button">应用</button>
</div>
</div>
<div class="usage-dashboard__body">
<div id="usage-dashboard-loading" class="usage-dashboard__loading" role="status" hidden>正在整理统计数据</div>
<div id="usage-dashboard-error" class="usage-dashboard__error" role="alert" hidden></div>
<section class="usage-dashboard__metric-grid" aria-label="统计概览">
<article class="usage-dashboard__metric">
<span>新建会话</span><strong data-usage-metric="newSessions">0</strong><small>所选时间内</small>
</article>
<article class="usage-dashboard__metric">
<span>发送消息</span><strong data-usage-metric="messages">0</strong><small data-usage-message-split>直接 0 · 跨会话 0</small>
</article>
<article class="usage-dashboard__metric">
<span>MCP 调用</span><strong data-usage-metric="mcpCalls">0</strong><small>按 assistant 消息时间</small>
</article>
<article class="usage-dashboard__metric usage-dashboard__metric--danger">
<span>MCP 失败</span><strong data-usage-metric="mcpFailures">0</strong><small data-usage-failure-rate>失败率 0%</small>
</article>
<article class="usage-dashboard__metric">
<span>Skill 显式使用</span><strong data-usage-metric="skillMentions">0</strong><small>仅统计 $skill mention</small>
</article>
</section>
<div class="usage-dashboard__content-grid">
<section class="usage-dashboard__panel usage-dashboard__panel--trend" aria-labelledby="usage-dashboard-trend-title">
<div class="usage-dashboard__panel-heading">
<div><h3 id="usage-dashboard-trend-title">每日趋势</h3><p>会话、消息、MCP 与 Skill</p></div>
<div id="usage-dashboard-trend-legend" class="usage-dashboard__legend" aria-label="趋势图例"></div>
</div>
<div class="usage-dashboard__chart-wrap">
<svg id="usage-dashboard-trend" class="usage-dashboard__chart" viewBox="0 0 720 280" role="img" aria-label="每日使用趋势"></svg>
</div>
</section>
<section class="usage-dashboard__panel usage-dashboard__panel--status" aria-labelledby="usage-dashboard-mcp-status-title">
<div class="usage-dashboard__panel-heading">
<div><h3 id="usage-dashboard-mcp-status-title">MCP 状态</h3><p>调用结果分布</p></div>
</div>
<div id="usage-dashboard-mcp-status" class="usage-dashboard__status-list"></div>
</section>
</div>
<section class="usage-dashboard__panel" aria-labelledby="usage-dashboard-mcp-title">
<div class="usage-dashboard__panel-heading">
<div><h3 id="usage-dashboard-mcp-title">MCP 工具</h3><p>按调用次数排序</p></div>
</div>
<div class="usage-dashboard__table-wrap">
<table class="usage-dashboard__table">
<thead><tr><th scope="col">工具</th><th scope="col">调用</th><th scope="col">成功</th><th scope="col">失败</th><th scope="col">成功率</th><th scope="col"><span class="sr-only">操作</span></th></tr></thead>
<tbody id="usage-dashboard-mcp-rows"></tbody>
</table>
</div>
<div id="usage-dashboard-mcp-empty" class="usage-dashboard__empty" hidden>所选时间内没有 MCP 调用</div>
</section>
<section id="usage-dashboard-detail" class="usage-dashboard__panel usage-dashboard__detail" aria-labelledby="usage-dashboard-detail-title" hidden>
<div class="usage-dashboard__panel-heading">
<div><h3 id="usage-dashboard-detail-title">调用明细</h3><p id="usage-dashboard-detail-meta"></p></div>
<button id="usage-dashboard-detail-close" class="usage-dashboard__icon-button" type="button" aria-label="关闭调用明细" title="关闭调用明细">×</button>
</div>
<div class="usage-dashboard__table-wrap">
<table class="usage-dashboard__table">
<thead><tr><th scope="col">时间</th><th scope="col">状态</th><th scope="col">会话</th><th scope="col">Agent</th></tr></thead>
<tbody id="usage-dashboard-detail-rows"></tbody>
</table>
</div>
</section>
<div class="usage-dashboard__content-grid usage-dashboard__content-grid--lower">
<section class="usage-dashboard__panel" aria-labelledby="usage-dashboard-skills-title">
<div class="usage-dashboard__panel-heading">
<div><h3 id="usage-dashboard-skills-title">Skill 显式使用</h3><p>按 mention 次数排序</p></div>
</div>
<ol id="usage-dashboard-skill-rows" class="usage-dashboard__rank-list"></ol>
<div id="usage-dashboard-skill-empty" class="usage-dashboard__empty" hidden>所选时间内没有显式 Skill 使用</div>
</section>
<section class="usage-dashboard__panel" aria-labelledby="usage-dashboard-sessions-title">
<div class="usage-dashboard__panel-heading">
<div><h3 id="usage-dashboard-sessions-title">最近会话</h3><p>点击可返回会话</p></div>
</div>
<div class="usage-dashboard__table-wrap">
<table class="usage-dashboard__table">
<thead><tr><th scope="col">会话</th><th scope="col">消息</th><th scope="col">MCP</th><th scope="col">Skill</th><th scope="col">最近时间</th></tr></thead>
<tbody id="usage-dashboard-session-rows"></tbody>
</table>
</div>
<div id="usage-dashboard-session-empty" class="usage-dashboard__empty" hidden>所选时间内没有会话记录</div>
</section>
</div>
</div>
</div>
</section>
<div class="input-area">
<div class="chat-controls" aria-label="会话控制">
<span id="chat-runtime-state" class="chat-runtime-state" hidden>运行中</span>
<select id="mode-select" class="mode-select" title="权限模式">
<option value="yolo">YOLO</option>
<option value="default">默认</option>
<option value="plan">Plan</option>
</select>
<div class="user-outline-anchor">
<button id="user-outline-btn" class="user-outline-btn" type="button" aria-expanded="false" aria-controls="user-outline-panel" title="定位用户消息">定位</button>
<div id="user-outline-panel" class="user-outline-panel" hidden></div>
</div>
<div class="ccweb-prompt-outline-anchor" hidden>
<button id="ccweb-prompt-outline-btn" class="user-outline-btn ccweb-prompt-outline-btn" type="button" aria-expanded="false" aria-controls="ccweb-prompt-outline-panel" title="定位待处理表单">表单</button>
<div id="ccweb-prompt-outline-panel" class="user-outline-panel ccweb-prompt-outline-panel" hidden></div>
</div>
<button id="reload-mcp-btn" class="reload-mcp-btn" type="button" title="重载 Codex App MCP 配置" hidden>重载 MCP</button>
<span id="cost-display" class="cost-display" hidden></span>
</div>
<div id="attachment-tray" class="attachment-tray" hidden></div>
<div id="pending-notes-tray" class="pending-notes-tray" hidden></div>
<div class="input-wrapper">
<input id="image-upload-input" type="file" accept="image/png,image/jpeg,image/webp,image/gif" multiple hidden>
<button id="attach-btn" class="attach-btn" title="上传图片" type="button">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21.44 11.05l-8.49 8.49a6 6 0 1 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"></path>
</svg>
</button>
<textarea id="msg-input" rows="1" placeholder="输入消息… 输入 / 查看指令" autocomplete="off"></textarea>
<button id="note-mode-btn" class="note-mode-btn" title="笔记模式" aria-label="笔记模式" aria-pressed="false" type="button">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 20h9"></path>
<path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z"></path>
<path d="m15 5 3 3"></path>
</svg>
</button>
<button id="queue-send-btn" class="queue-send-btn" title="排队发送" aria-label="排队发送" type="button" hidden>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 6h10"></path>
<path d="M4 12h8"></path>
<path d="M4 18h10"></path>
<path d="m16 10 4 4-4 4"></path>
</svg>
</button>
<button id="send-btn" class="send-btn" title="发送">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
</svg>
</button>
<button id="abort-btn" class="abort-btn" title="停止" hidden>
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
<rect x="6" y="6" width="12" height="12" rx="2"></rect>
</svg>
</button>
</div>
</div>
</main>
</div>
<div id="session-loading-overlay" class="session-loading-overlay" hidden aria-live="polite" aria-hidden="true">
<div class="session-loading-card" role="status" aria-busy="true">
<div class="session-loading-badge">Session</div>
<div class="session-loading-title">会话加载中</div>
<div id="session-loading-label" class="session-loading-label">正在整理消息与上下文…</div>
<div class="session-loading-bar">
<span class="session-loading-bar-fill"></span>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.1/marked.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.9.1/mermaid.min.js"></script>
<script src="app.js?v=20260803-usage-statistics"></script>
</body>
</html>