feat(desktop): per-page top bar with caret toggles + drop annotation scaffold

1. Removed the two floating arrow buttons (.sb-toggle / .rp-toggle) that

   used to sit at the side seams between sidebar/main/rightpanel.

2. Each page section now owns its own .page-top bar with:

   - left: .pt-sb-toggle (caretLeft, borderless icon button) — collapses

     left sidebar; svg rotates 180° when sidebar is collapsed.

   - right: .pt-rp-toggle (caretRight) — same logic for right panel.

   - chat/collab keep run-state centered between the two icons.

   - services/token use a .pt-spacer to fill the middle gap.

3. .pt-icon style is borderless / transparent / opacity-0.7 (no boxy

   bordered button look); page-top padding tightened to 4px so the

   carets hug the screen edges. Bump styles.css?v=95→97.

4. Wiring: app.js queries .pt-sb-toggle/.pt-rp-toggle by class (4 of

   each, one per page) and binds them to the existing

   .body.classList.toggle('sb-collapsed') / .toggle('rp-collapsed')

   handlers. Behavior identical to the old single buttons.

5. Dropped the DEV annotation scaffold: removed annot.css link in head,

   annot.js script at end of body, and deleted both files.
This commit is contained in:
dd3xp committed 2026-06-02 07:52:36 +08:00
1 parent 2d003d7210
commit 2e64539ec5
5 files changed
+51 -156

No files matched your search

-45
View File
@@ -1,45 +0,0 @@
/* GenericAgent 桌面版 —— 开发脚手架(标注模式)样式。纯 dev 工具,非产品本体。
删掉 index.html 里 annot.css + annot.js 两行 include 即可彻底移除。自包含,不依赖 styles.css。 */
:root{
--annot-fg:#9a6b00; --annot-bg:#fff7e6; --annot-line:#e6c869;
--annot-tip-fg:#444444; --annot-tip-bg:#fffbe9;
--region-line:#b9c2d6; --region-tag:#5a76d8;
--fab-fg:#e8ecf6; --fab-bg:#1c202edb; --fab-line:#ffffff24;
--annot-shadow-fab:0 6px 22px #14192847;
--annot-shadow-fab-h:0 10px 28px #14192857;
--annot-shadow-pop:0 8px 24px #14192829;
}
.annot-fab{
position:fixed; right:18px; bottom:18px; z-index:999;
font-size:12px; font-weight:600; letter-spacing:.3px;
padding:9px 16px; border-radius:22px; cursor:pointer;
color:var(--fab-fg); background:var(--fab-bg); border:1px solid var(--fab-line);
box-shadow:var(--annot-shadow-fab); backdrop-filter:blur(4px);
transition:transform .12s, box-shadow .12s;
}
.annot-fab:hover{ transform:translateY(-1px); box-shadow:var(--annot-shadow-fab-h); }
.annot-inline{
display:none; font-size:11px; color:var(--annot-fg); background:var(--annot-bg);
border:1px dashed var(--annot-line); border-radius:8px; padding:8px 10px;
line-height:1.55; max-width:280px; margin:8px 0;
}
#app.annot-on .annot-inline{ display:block; }
#app.annot-on [data-region]{ position:relative; outline:1.5px dashed var(--region-line); outline-offset:-2px; }
#app.annot-on [data-region]::before{
content:attr(data-region); position:absolute; top:0; left:0; z-index:9;
font-size:10px; font-weight:700; color:#ffffff; background:var(--region-tag);
padding:2px 8px; border-bottom-right-radius:8px; max-width:96%;
white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
#app.annot-on [data-note]{ position:relative; }
#app.annot-on [data-note]:hover::after{
content:attr(data-note); position:absolute; left:0; top:100%; z-index:50;
margin-top:4px; width:220px; max-width:240px; font-size:11px; font-weight:400; color:var(--annot-tip-fg);
background:var(--annot-tip-bg); border:1px solid var(--annot-line); border-radius:8px;
padding:8px 10px; line-height:1.5; box-shadow:var(--annot-shadow-pop);
white-space:normal;
}
#app.annot-on .rightpanel [data-note]:hover::after,
#app.annot-on .tb-right [data-note]:hover::after{ left:auto; right:0; }
#app.annot-on .sidebar-foot [data-note]:hover::after,
#app.annot-on .composer [data-note]:hover::after{ top:auto; bottom:100%; margin:0 0 4px; }
-95
View File
@@ -1,95 +0,0 @@
// GenericAgent 桌面版 —— 开发脚手架(标注模式)。纯 dev 工具,不属于产品本体。
// 注释/区域说明/悬浮按钮全部由本文件运行时注入,产品 HTML 里没有任何标注痕迹。
// 删掉 index.html 里 annot.css + annot.js 两行 include 即可彻底移除。
(function () {
'use strict';
// 区域块标签:选择器 -> 说明
const REGIONS = [
['.sidebar', '① 侧边栏(美术重点 · 老师要求#1)'],
['.topbar', '顶栏 · 仅运行状态'],
['.page[data-page="chat"]', '聊天页 · 消息流 GET /session/{sid}/messages,WS 通知新内容'],
['.composer', '② 输入区 · 发送=POST /session/{sid}/prompt'],
['.page[data-page="services"]', '③ 后台服务(消息通道 + 状态面板,融 hub.pyw)'],
['.page[data-page="collab"]', '④ subagent / Hive 多体执行监控'],
['.page[data-page="token"]', '④ token 记录与成本'],
['.rightpanel', '④ 会话管理 · GET /sessions'],
];
// 悬停说明:选择器 -> 说明(同选择器多个元素都加)
const NOTES = [
['#settings-btn', '点击弹出『配置』面板(主题色 / 语言切换)'],
['#run-toggle', '运行/停止:运行中点击=停止当前会话(POST /session/{sid}/cancel)'],
['.tb-right .ic-btn', '刷新状态/会话'],
['#preset-btn', '② 预设功能:对话开始后随时弹出预设卡片'],
['#model-chip', '模型档位下拉(GET /model-profiles)'],
['.new-conv', '新建会话 POST /session/new'],
['#add-model-btn', '点击弹出『添加模型』面板(模板)'],
['.send', '发送'],
['.fcard[data-preset="goal"]', '② 注入预设prompt:读 L3 goal SOP 自主达成目标'],
['.fcard[data-preset="explore"]', '② 注入预设prompt:自动浏览并周期汇总'],
['.fcard[data-preset="hive"]', '② 注入预设prompt:拉起多 worker 协同(Hive)'],
['.fcard[data-preset="review"]', '② 注入预设prompt:监察者模式严格验收'],
['.fcard[data-preset="mine"]', '② 用户自存的预设示例:自定义预设统一用星标图标'],
['.fcard[data-preset="add"]', '② 体现『一句话即一个功能』:用户自存任意预设 prompt 为新卡片'],
];
// 内联「该放什么」卡片:宿主选择器 -> [位置, HTML]
const INLINES = [
['.page[data-page="chat"] .msg-area', 'prepend', '该放:用户/助手消息气泡、turn 折叠、工具调用详情、流式增量;<b>消息内容须支持 Markdown + LaTeX + 代码高亮</b>。空会话展示下方预设功能'],
['.page[data-page="services"] .svc-panel[data-svc-panel="channels"]', 'append', '每行该放:渠道名、连接状态、启停开关、配置入口;对应 hub.pyw 的 imbot 进程。真实版用各渠道品牌 logo'],
['.page[data-page="services"] .svc-panel[data-svc-panel="status"]', 'append', '每行该放:进程名、PID、状态、CPU/内存、启停/重启、日志;至少含 bridge、各 imbot、定时调度'],
['.page[data-page="collab"] .page-pad', 'append', '该放:worker 卡片 + BBS 帖子流 + 选中 worker 的 output 流。数据源 temp/hive_*/、output.txt、agent_bbs;参考 conductor.py'],
['.page[data-page="collab"] .page-pad', 'append', '下方:BBS 帖子流 + 选中项 output.txt 实时滚动'],
['.page[data-page="token"] .page-pad', 'append', '该放:汇总卡 + 按会话明细表 + 趋势图。数据来自 bridge 的用量记录(_record_usage)'],
];
const appEl = document.getElementById('app');
if (!appEl) return;
const fab = document.createElement('button');
fab.id = 'toggle-annot';
fab.className = 'annot-fab';
document.body.appendChild(fab);
let on = false;
const injected = [];
function annotate() {
REGIONS.forEach(([sel, txt]) =>
document.querySelectorAll(sel).forEach(el => el.setAttribute('data-region', txt)));
NOTES.forEach(([sel, txt]) =>
document.querySelectorAll(sel).forEach(el => el.setAttribute('data-note', txt)));
// 上传按钮(composer-top 里非 #preset-btn 的 ic-btn) 与 Plan/Auto 特殊定位
document.querySelectorAll('.composer-top .ic-btn').forEach(b => {
if (b.id !== 'preset-btn') b.setAttribute('data-note', '附件/图片上传(多模态 [image:path])');
});
const sm = document.querySelectorAll('.cb-left .chip.sm');
if (sm[0]) sm[0].setAttribute('data-note', '② Plan:预设进入计划模式');
if (sm[1]) sm[1].setAttribute('data-note', '② Auto:预设进入自主/goal 模式');
INLINES.forEach(([sel, pos, html]) => {
const host = document.querySelector(sel);
if (!host) return;
const d = document.createElement('div');
d.className = 'annot-inline';
d.innerHTML = html;
if (pos === 'prepend') host.insertBefore(d, host.firstChild);
else host.appendChild(d);
injected.push(d);
});
}
function clearAll() {
document.querySelectorAll('[data-region]').forEach(el => el.removeAttribute('data-region'));
document.querySelectorAll('[data-note]').forEach(el => el.removeAttribute('data-note'));
injected.splice(0).forEach(d => d.remove());
}
function setLabel() { fab.textContent = '标注模式:' + (on ? '开' : '关'); }
fab.addEventListener('click', () => {
on = !on;
if (on) { annotate(); appEl.classList.add('annot-on'); }
else { appEl.classList.remove('annot-on'); clearAll(); }
setLabel();
});
setLabel(); // 初始:关
})();
+4 -4
View File
@@ -1291,16 +1291,16 @@ const runLabel = runToggle?.querySelector('.rs-label');
const convListEl = document.querySelector('.conv-list');
const newConvBtn = document.querySelector('.new-conv');
const searchInput = document.querySelector('.search input');
const rpToggle = document.getElementById('rp-toggle');
const rpResize = document.getElementById('rp-resize');
const rpPanel = document.getElementById('rightpanel');
const bodyEl = document.querySelector('.body');
if (rpToggle) rpToggle.addEventListener('click', () => bodyEl.classList.toggle('rp-collapsed'));
/* 每个页面的 page-top 各自挂一对 hamburger / 会话 按钮(.pt-sb-toggle / .pt-rp-toggle),
全部绑同一个 toggle,效果跟以前的单一 sb-toggle/rp-toggle 一样,只是入口变成顶栏。 */
document.querySelectorAll('.pt-sb-toggle').forEach(b => b.addEventListener('click', () => bodyEl.classList.toggle('sb-collapsed')));
document.querySelectorAll('.pt-rp-toggle').forEach(b => b.addEventListener('click', () => bodyEl.classList.toggle('rp-collapsed')));
const sbToggle = document.getElementById('sb-toggle');
const sbResize = document.getElementById('sb-resize');
const sbPanel = document.querySelector('.sidebar');
if (sbToggle) sbToggle.addEventListener('click', () => bodyEl.classList.toggle('sb-collapsed'));
// 通用拖拽:dir=+1 拖动 →clientX 增大就增宽(左侧栏);dir=-1 反之(右侧)
function bindResize(handle, panel, dir, min, max) {
+32 -11
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<title>GenericAgent</title>
<link rel="stylesheet" href="assets/fonts/fonts.css">
<link rel="stylesheet" href="styles.css?v=94">
<link rel="stylesheet" href="styles.css?v=97">
<!-- KaTeX: LaTeX 公式渲染 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" crossorigin="anonymous">
<!-- highlight.js: 代码高亮 -->
@@ -13,8 +13,6 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.css" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/l10n/zh.js" crossorigin="anonymous"></script>
<!-- DEV 脚手架(标注模式):删本行 + 下方 annot.js 一行即可彻底移除 -->
<link rel="stylesheet" href="annot.css">
<script>
(function(){
var t=localStorage.getItem('ga_theme'); if(t) document.documentElement.dataset.theme=t;
@@ -93,9 +91,6 @@
</div>
</aside>
<div class="sb-resize" id="sb-resize"></div>
<button class="sb-toggle" id="sb-toggle" aria-label="toggle sidebar">
<span data-ga-icon="caretLeft"></span>
</button>
<!-- 中间主区 -->
<main class="main">
@@ -106,9 +101,15 @@
<section class="page active" data-page="chat"
>
<div class="page-top">
<button type="button" class="pt-icon pt-sb-toggle" aria-label="toggle sidebar">
<span data-ga-icon="caretLeft"></span>
</button>
<button class="run-state" id="run-toggle">
<i class="dot"></i> <span class="rs-label"></span>
</button>
<button type="button" class="pt-icon pt-rp-toggle" aria-label="toggle conversations">
<span data-ga-icon="caretRight"></span>
</button>
</div>
<div class="msg-area">
<div class="chat-start">
@@ -147,6 +148,15 @@
<!-- 后台服务:消息通道 + 状态面板 -->
<section class="page" data-page="services">
<div class="page-top">
<button type="button" class="pt-icon pt-sb-toggle" aria-label="toggle sidebar">
<span data-ga-icon="caretLeft"></span>
</button>
<span class="pt-spacer"></span>
<button type="button" class="pt-icon pt-rp-toggle" aria-label="toggle conversations">
<span data-ga-icon="caretRight"></span>
</button>
</div>
<div class="page-pad">
<div class="page-title" data-i18n="page.services.title"></div>
<div class="page-sub" data-i18n="page.services.sub"></div>
@@ -172,9 +182,15 @@
</div>
<div class="collab-reconnect" id="collab-reconnect" hidden data-i18n="collab.reconnect"></div>
<div class="page-top">
<button type="button" class="pt-icon pt-sb-toggle" aria-label="toggle sidebar">
<span data-ga-icon="caretLeft"></span>
</button>
<button class="run-state" id="collab-run-toggle" type="button">
<i class="dot"></i> <span class="rs-label"></span>
</button>
<button type="button" class="pt-icon pt-rp-toggle" aria-label="toggle conversations">
<span data-ga-icon="caretRight"></span>
</button>
</div>
<div class="msg-area collab-msgs" id="collab-msgs">
<div class="collab-scroll">
@@ -270,6 +286,15 @@
<!-- Token 统计 -->
<section class="page" data-page="token">
<div class="page-top">
<button type="button" class="pt-icon pt-sb-toggle" aria-label="toggle sidebar">
<span data-ga-icon="caretLeft"></span>
</button>
<span class="pt-spacer"></span>
<button type="button" class="pt-icon pt-rp-toggle" aria-label="toggle conversations">
<span data-ga-icon="caretRight"></span>
</button>
</div>
<div class="page-pad">
<div class="page-title" data-i18n="page.token.title"></div>
<div class="tok-tabs" id="tok-tabs">
@@ -312,9 +337,6 @@
</main>
<!-- ④ 右侧会话面板 -->
<button class="rp-toggle" id="rp-toggle" aria-label="toggle panel">
<span data-ga-icon="caretRight"></span>
</button>
<div class="rp-resize" id="rp-resize"></div>
<aside class="rightpanel" id="rightpanel">
<div class="search">
@@ -547,7 +569,6 @@
<!-- highlight.js -->
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/highlight.min.js" crossorigin="anonymous"></script>
<script src="phosphor-icons.js"></script>
<script src="app.js?v=82"></script>
<script src="annot.js"></script><!-- DEV 脚手架,可删 -->
<script src="app.js?v=83"></script>
</body>
</html>
+15 -1
View File
@@ -528,9 +528,23 @@ html[data-appearance="dark"] .swatch[data-theme="8"].sel::after{
/* 各页面自带的顶栏(融入页面、随页面切换),沿用旧 .topbar 视觉,但归属各 page */
.page > .page-top{
height:46px; flex:0 0 46px; display:flex; align-items:center;
justify-content:space-between; padding:0 16px;
justify-content:space-between; padding:0 4px; gap:10px;
background:var(--panel);
}
/* 顶栏左右两侧的纯图标按钮(无边框、无背景):
左 .pt-sb-toggle = 开关左栏,右 .pt-rp-toggle = 开关右栏。
收起时 svg 旋转 180°,跟之前那两个箭头 toggle 的视觉行为一致。 */
.pt-icon{
width:28px; height:28px; display:inline-flex; align-items:center; justify-content:center;
background:transparent; border:none; padding:0; flex:0 0 auto;
color:var(--txt-2); opacity:.7; cursor:pointer;
transition:opacity .15s, color .15s;
}
.pt-icon:hover{ opacity:1; color:var(--txt); }
.pt-icon svg{ width:16px; height:16px; transition:transform .2s; }
.body.sb-collapsed .pt-sb-toggle svg,
.body.rp-collapsed .pt-rp-toggle svg{ transform:rotate(180deg); }
.pt-spacer{ flex:1 1 auto; }
.tb-left{ display:flex; align-items:center; gap:14px; font-size:12px; }
.pill{
border:1px solid var(--line); background:var(--card); border-radius:20px;