mingdao-harness 0.6.20 → 0.6.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/web/app.js +89 -38
- package/src/web/index.html +158 -59
package/package.json
CHANGED
package/src/web/app.js
CHANGED
|
@@ -761,7 +761,7 @@ function renderWorkStatus(){
|
|
|
761
761
|
const running=bgTasks.filter((/** @type {any} */ t)=>t.status==='running');
|
|
762
762
|
const latest=running.length?running[running.length-1]:null;
|
|
763
763
|
bg.style.display=''; bg.textContent='🛠 '+bgRunning+' 个后台任务运行中'+(latest?' · '+String(latest.message||'').slice(0,22):'')+' — 点击查看详情';
|
|
764
|
-
bg.onclick=()=>{
|
|
764
|
+
bg.onclick=()=>{ activateRailTab('tasks'); }; // §3.82:后台任务 chip 打开右栏「任务」标签
|
|
765
765
|
} else { bg.style.display='none'; bg.onclick=null; }
|
|
766
766
|
}
|
|
767
767
|
// P1-4:右栏「本轮运行」与状态条**同源**——renderRunPanel() 内部只走 runPanelView()→runStripView(),
|
|
@@ -1166,6 +1166,44 @@ let homeDir = '';
|
|
|
1166
1166
|
let workingDirCache = '';
|
|
1167
1167
|
let compactAvailable = false; // Web 端当前没有 /api/compact;为 true 时才显示压缩入口
|
|
1168
1168
|
let runPanelOpen = false;
|
|
1169
|
+
// —— 右栏四标签(§3.82 第二步):轨迹 / 子Agent / 任务 / 仪表盘 ——
|
|
1170
|
+
// 标签切换**只控制 display**(railTabDisplay 纯函数),四个面板节点常驻 DOM、id 不变;
|
|
1171
|
+
// 不重写 updateTasksPanel / renderDashboard / openTraj / renderSubPanel 的数据逻辑。
|
|
1172
|
+
const RAIL_TABS = ['trace', 'sub', 'tasks', 'dash'];
|
|
1173
|
+
const RAIL_TAB_PANELS = { trace: '#tabTrace', sub: '#tabSub', tasks: '#tabTasks', dash: '#tabDash' };
|
|
1174
|
+
const RAIL_TAB_BUTTONS = { trace: '#rtTrace', sub: '#rtSub', tasks: '#rtTasks', dash: '#rtDash' };
|
|
1175
|
+
let activeRailTab = 'trace';
|
|
1176
|
+
function railTabDisplay(tab) {
|
|
1177
|
+
const active = RAIL_TABS.includes(tab) ? tab : 'trace';
|
|
1178
|
+
/** @type {Record<string, 'flex'|'none'>} */
|
|
1179
|
+
const out = {};
|
|
1180
|
+
for (const key of RAIL_TABS) out[key] = key === active ? 'flex' : 'none';
|
|
1181
|
+
return out;
|
|
1182
|
+
}
|
|
1183
|
+
function setRailTab(tab) {
|
|
1184
|
+
activeRailTab = RAIL_TABS.includes(tab) ? tab : 'trace';
|
|
1185
|
+
const disp = railTabDisplay(activeRailTab);
|
|
1186
|
+
for (const key of RAIL_TABS) {
|
|
1187
|
+
const panel = $(RAIL_TAB_PANELS[key]);
|
|
1188
|
+
if (panel) panel.style.display = disp[key];
|
|
1189
|
+
const btn = $(RAIL_TAB_BUTTONS[key]);
|
|
1190
|
+
if (btn) {
|
|
1191
|
+
btn.classList.toggle('on', key === activeRailTab);
|
|
1192
|
+
btn.setAttribute('aria-selected', key === activeRailTab ? 'true' : 'false');
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
// 搬进标签页的两个旧面板:display 继续跟着标签走(updateTasksPanel / refreshCostBadge 靠它判可见)。
|
|
1196
|
+
const tp = $('#tasksPanel'); if (tp) tp.style.display = disp.tasks;
|
|
1197
|
+
const dp = $('#dashPanel'); if (dp) dp.style.display = disp.dash;
|
|
1198
|
+
if (activeRailTab === 'tasks' && typeof updateTasksPanel === 'function') updateTasksPanel();
|
|
1199
|
+
if (activeRailTab === 'dash' && typeof refreshCostBadge === 'function') refreshCostBadge();
|
|
1200
|
+
return activeRailTab;
|
|
1201
|
+
}
|
|
1202
|
+
/** 点标签/既有入口的统一出口:先开右栏,再切标签。 */
|
|
1203
|
+
function activateRailTab(tab) {
|
|
1204
|
+
setRunPanel(true);
|
|
1205
|
+
return setRailTab(tab);
|
|
1206
|
+
}
|
|
1169
1207
|
let responsiveInitialized = false;
|
|
1170
1208
|
// —— 轨迹收纳状态(§3.73):两个折叠区 + 展开过的轮次/工具/子代理(重渲染后保持展开) ——
|
|
1171
1209
|
const traceFoldState = { trace: true, subs: true };
|
|
@@ -1214,8 +1252,7 @@ initTheme();
|
|
|
1214
1252
|
// —— 右栏「本轮运行」:<1180 收起为抽屉;宽屏默认展开;不做任何新轮询 ——
|
|
1215
1253
|
function syncRunPanel() {
|
|
1216
1254
|
const el = $('#runPanel'); if (!el) return;
|
|
1217
|
-
//
|
|
1218
|
-
// (syncPanelLayout 统一算,避免"两个面板各写一份互斥逻辑")。
|
|
1255
|
+
// §3.82:四个面板都在右栏标签页内,右边缘只剩右栏一个抽屉(显隐由 syncPanelLayout 统一算)。
|
|
1219
1256
|
syncPanelLayout();
|
|
1220
1257
|
const chip = $('#runChip'); if (chip) chip.setAttribute('aria-expanded', runPanelOpen ? 'true' : 'false');
|
|
1221
1258
|
const col = $('#rpCollapse'); if (col) col.textContent = '收起';
|
|
@@ -1242,6 +1279,12 @@ function initResponsive() {
|
|
|
1242
1279
|
initResponsive();
|
|
1243
1280
|
// §3.73:绑定两个折叠区 + 末尾区块总开关(渲染入口在下面的轨迹收纳块)。
|
|
1244
1281
|
initTraceUI();
|
|
1282
|
+
// §3.82:四个标签按钮(只切 display + 开右栏;默认轨迹)。
|
|
1283
|
+
for (const key of RAIL_TABS) {
|
|
1284
|
+
const btn = $(RAIL_TAB_BUTTONS[key]);
|
|
1285
|
+
if (btn) btn.onclick = () => activateRailTab(key);
|
|
1286
|
+
}
|
|
1287
|
+
setRailTab('trace');
|
|
1245
1288
|
|
|
1246
1289
|
// —— 工具时间线 DOM:一张卡 + 可复用行(开始/完成不换节点) ——
|
|
1247
1290
|
function toolStepNode(step) {
|
|
@@ -1678,6 +1721,7 @@ function focusTraceRound(round) {
|
|
|
1678
1721
|
const body = $('#tdBody'); if (body) body.hidden = false;
|
|
1679
1722
|
const toggle = $('#tdToggle'); if (toggle) toggle.setAttribute('aria-expanded', 'true');
|
|
1680
1723
|
} else {
|
|
1724
|
+
setRailTab('trace');
|
|
1681
1725
|
setRunPanel(true);
|
|
1682
1726
|
}
|
|
1683
1727
|
traceFoldState.trace = true;
|
|
@@ -1743,6 +1787,7 @@ function renderSessionSettings() {
|
|
|
1743
1787
|
? ((presetData.find((p) => p.name === psel.value) || {}).label || psel.value)
|
|
1744
1788
|
: '无预设';
|
|
1745
1789
|
runState.presetName = presetLabel;
|
|
1790
|
+
set('#ssPresetSrc', '当前:' + presetLabel + ' · 来源:你的选择 · 仅下一轮(工具白名单 / 参数)');
|
|
1746
1791
|
set('#ssEffective', sessionEffectiveLine({ model: md.name, modelLabel: md.label, permission: mode, sourceLabel: permissionSourceLabel(source), preset: presetLabel }));
|
|
1747
1792
|
set('#chipModel', md.label || md.name || '模型');
|
|
1748
1793
|
set('#chipPerm', permissionModeLabel(mode));
|
|
@@ -2070,38 +2115,29 @@ function openTraj(msg){
|
|
|
2070
2115
|
// 点击标题定位到聊天中的对应工具卡
|
|
2071
2116
|
list.appendChild(div);
|
|
2072
2117
|
}
|
|
2073
|
-
|
|
2074
|
-
$('#tasksPanel').style.display='none'; // 仅任务面板(右侧)与轨迹互斥;子代理面板可与轨迹同显
|
|
2118
|
+
// §3.82:轨迹入口不再单独开旧浮层——切到右栏「轨迹」标签(旧面板与其 #tjList 仍在 DOM 里)。
|
|
2075
2119
|
$('#trajRailBtn').classList.add('on');
|
|
2076
|
-
|
|
2120
|
+
activateRailTab('trace');
|
|
2077
2121
|
}
|
|
2078
|
-
//
|
|
2079
|
-
//
|
|
2122
|
+
// 面板布局同步:§3.82 起轨迹/子代理/任务/仪表盘都在右栏标签页内,
|
|
2123
|
+
// 右栏是否显示只由 runPanelOpen 决定;旧的四条 body 推开 class 不再使用(CSS 规则保留不动)。
|
|
2080
2124
|
function syncPanelLayout(){
|
|
2081
|
-
|
|
2082
|
-
document.body.classList.toggle('traj-open', open('trajPanel'));
|
|
2083
|
-
document.body.classList.toggle('sub-open', open('subPanel'));
|
|
2084
|
-
document.body.classList.toggle('tasks-open', open('tasksPanel'));
|
|
2085
|
-
document.body.classList.toggle('dash-open', open('dashPanel'));
|
|
2086
|
-
// P1-4:右边缘只留一个抽屉——既有右侧面板(子代理/任务/省钱仪表盘)打开时运行面板让位;
|
|
2087
|
-
// 它们关闭后 syncPanelLayout 再被调用,运行面板按 runPanelOpen 恢复。
|
|
2088
|
-
const otherRight=open('subPanel')||open('tasksPanel')||open('dashPanel');
|
|
2125
|
+
document.body.classList.remove('traj-open','sub-open','tasks-open','dash-open');
|
|
2089
2126
|
const narrow=layoutClassesFor(window.innerWidth||0).narrowRun;
|
|
2090
|
-
const showRun=Boolean(runPanelOpen)
|
|
2127
|
+
const showRun=Boolean(runPanelOpen);
|
|
2091
2128
|
const rp=$('#runPanel'); if(rp) rp.hidden=!showRun;
|
|
2092
2129
|
document.body.classList.toggle('run-open', showRun&&!narrow);
|
|
2093
2130
|
document.body.classList.toggle('run-drawer', showRun&&narrow);
|
|
2094
2131
|
// §3.73:窄屏右栏收起时,轨迹/子Agent 在对话流末尾显示;右栏(抽屉)一开就让位。
|
|
2095
2132
|
syncTraceDock();
|
|
2096
2133
|
}
|
|
2097
|
-
$('#tjClose').onclick=()=>{
|
|
2134
|
+
$('#tjClose').onclick=()=>{ setRunPanel(false); $('#trajRailBtn').classList.remove('on'); };
|
|
2098
2135
|
$('#trajRailBtn').onclick=()=>{
|
|
2099
|
-
|
|
2100
|
-
const
|
|
2101
|
-
if(
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
syncPanelLayout();
|
|
2136
|
+
// §3.82:轨迹入口 = 打开右栏并切到「轨迹」标签(没有轨迹时也切过去,显示空态)。
|
|
2137
|
+
const m=lastMsgWithTraj();
|
|
2138
|
+
if(m && m._traj && m._traj.length){ openTraj(m); return; }
|
|
2139
|
+
$('#trajRailBtn').classList.add('on');
|
|
2140
|
+
activateRailTab('trace');
|
|
2105
2141
|
};
|
|
2106
2142
|
function lastMsgWithTraj(){
|
|
2107
2143
|
const msgs=[...document.querySelectorAll('#chat .msg-ai')].reverse();
|
|
@@ -2128,15 +2164,13 @@ function renderSubPanel(){
|
|
|
2128
2164
|
if(!count) list.innerHTML='<div class="empty" style="color:var(--faint);font-size:12px;padding:10px">本会话还没有子 Agent(task 工具会派生子 Agent)</div>';
|
|
2129
2165
|
}
|
|
2130
2166
|
const toggleSubPanel=()=>{
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
$('#subRailBtn').classList.
|
|
2135
|
-
if(willShow){ $('#tasksPanel').style.display='none'; renderSubPanel(); } // 轨迹(左侧)可与子代理(右侧)同显
|
|
2136
|
-
syncPanelLayout();
|
|
2167
|
+
// §3.82:子Agent 入口 = 打开右栏并切到「子Agent」标签(不再单独开旧浮层)。
|
|
2168
|
+
renderSubPanel();
|
|
2169
|
+
activateRailTab('sub');
|
|
2170
|
+
$('#subRailBtn').classList.add('on');
|
|
2137
2171
|
};
|
|
2138
2172
|
$('#subRailBtn').onclick=toggleSubPanel;
|
|
2139
|
-
$('#sbClose').onclick=()=>{
|
|
2173
|
+
$('#sbClose').onclick=()=>{ setRunPanel(false); $('#subRailBtn').classList.remove('on'); };
|
|
2140
2174
|
// —— 任务面板(多会话并行) ——
|
|
2141
2175
|
function setBtn(){
|
|
2142
2176
|
// §138:停止点击后立刻有可见反馈(「停止中…」+ 禁用防连点),回合真正收尾后由 finally 复位。
|
|
@@ -2203,8 +2237,12 @@ async function refreshCostBadge(){
|
|
|
2203
2237
|
if($('#dashPanel').style.display==='flex') renderDashboard(j);
|
|
2204
2238
|
}
|
|
2205
2239
|
refreshCostBadge(); setInterval(refreshCostBadge, 15000);
|
|
2206
|
-
|
|
2207
|
-
$('#
|
|
2240
|
+
// §3.82:顶栏费用徽标 = 打开右栏「仪表盘」标签(原有 📊 内容仍在 #dashPanel 里;关闭用 ✕/收起)。
|
|
2241
|
+
$('#costBadge').onclick=()=>{
|
|
2242
|
+
$('#trajRailBtn').classList.remove('on'); $('#subRailBtn').classList.remove('on');
|
|
2243
|
+
activateRailTab('dash');
|
|
2244
|
+
};
|
|
2245
|
+
$('#dashClose').onclick=()=>{ setRunPanel(false); };
|
|
2208
2246
|
// 底部状态栏:轮次/步数/LLM 与工具时长/首 token 平均/吞吐/缓存命中/输入输出 tokens
|
|
2209
2247
|
async function refreshStatusBar(){
|
|
2210
2248
|
const r=await fetch('/api/cache-stats',{cache:'no-store'}).catch(()=>null); if(!r) return;
|
|
@@ -2216,9 +2254,20 @@ async function refreshStatusBar(){
|
|
|
2216
2254
|
$('#statusBar').textContent = s.turns+' 轮 · '+s.steps+' 步 | LLM '+fmtDur(s.llmMs)+' · 工具调用 '+fmtDur(s.toolMs)+' | 首 token 平均 '+ft+' · '+tps+' | 缓存命中 '+rate+' | 输入 '+fmtTok(s.prompt)+' tok · 输出 '+fmtTok(s.completion)+' tok';
|
|
2217
2255
|
}
|
|
2218
2256
|
refreshStatusBar(); setInterval(refreshStatusBar, 15000);
|
|
2219
|
-
|
|
2220
|
-
$('#
|
|
2257
|
+
// §3.82:顶栏「任务」= 打开右栏「任务」标签(右栏收起时也会展开;关闭用 ✕/收起)。
|
|
2258
|
+
$('#tasksBtn').onclick=()=>{
|
|
2259
|
+
$('#trajRailBtn').classList.remove('on'); $('#subRailBtn').classList.remove('on');
|
|
2260
|
+
activateRailTab('tasks');
|
|
2261
|
+
};
|
|
2262
|
+
$('#tpClose').onclick=()=>{ setRunPanel(false); };
|
|
2221
2263
|
|
|
2264
|
+
// v0.6.21(§3.81):顶栏「我在哪」——工作空间 › 当前会话。数据只取既有 currentWorkspaceName /
|
|
2265
|
+
// sessionsCache / currentSession,不新增后端字段。会话列表或工作空间刷新后同步更新。
|
|
2266
|
+
function renderCrumb(){
|
|
2267
|
+
const w=$('#crumbWs'); if(w) w.textContent=currentWorkspaceName||'工作空间';
|
|
2268
|
+
const cur=sessionsCache.find((s)=>s&&s.file===currentSession);
|
|
2269
|
+
const c=$('#crumbSession'); if(c) c.textContent=cur?(cur.label||cur.file):(currentSession?String(currentSession).replace(/\.jsonl$/,''):'新对话');
|
|
2270
|
+
}
|
|
2222
2271
|
async function refreshSessions(q){
|
|
2223
2272
|
const u=q?'/api/sessions?q='+encodeURIComponent(q):'/api/sessions';
|
|
2224
2273
|
const r=await fetch(u).catch(()=>null); if(!r)return;
|
|
@@ -2227,6 +2276,7 @@ async function refreshSessions(q){
|
|
|
2227
2276
|
const sel=$('#sessions'); sel.innerHTML='<option value="">历史会话</option>';
|
|
2228
2277
|
for(const s of sessionsCache){ const o=document.createElement('option'); o.value=s.file; o.textContent=s.label; sel.appendChild(o); }
|
|
2229
2278
|
if(currentSession&&!q) sel.value=currentSession;
|
|
2279
|
+
renderCrumb();
|
|
2230
2280
|
}
|
|
2231
2281
|
let searchTimer=null;
|
|
2232
2282
|
$('#sessionSearch').addEventListener('input',e=>{ clearTimeout(searchTimer); searchTimer=setTimeout(()=>refreshSessions(e.target.value.trim()),300); });
|
|
@@ -2381,6 +2431,7 @@ async function refreshWsSel(){
|
|
|
2381
2431
|
sel.appendChild(add);
|
|
2382
2432
|
const mgr=document.createElement('option'); mgr.value='__manage__'; mgr.textContent='管理(重命名/删除/改目录)…';
|
|
2383
2433
|
sel.appendChild(mgr);
|
|
2434
|
+
renderCrumb();
|
|
2384
2435
|
}
|
|
2385
2436
|
/** 命令面板与顶栏下拉共用的工作空间切换(携带当前会话,语义与旧路径一致)。 */
|
|
2386
2437
|
async function applyWorkspaceSwitch(name){
|
|
@@ -2799,13 +2850,13 @@ function finalizeCurrentSession(){
|
|
|
2799
2850
|
if(!currentSession) return;
|
|
2800
2851
|
fetch('/api/session-finalize',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({file:currentSession})}).catch(()=>{});
|
|
2801
2852
|
}
|
|
2802
|
-
async function loadSession(file){ if(currentSession && currentSession!==file) finalizeCurrentSession(); currentSession=file; chatEl.innerHTML=''; resetTraceState(); const r=await fetch('/api/session?file='+encodeURIComponent(file)); const j=await r.json(); for(const m of j.messages||[]){ if(typeof m.content==='string'&&m.content.startsWith('(系统提示)')) continue; if(m.role==='user') addUser(m.content); else { const el=newAiMsg(); aiContent(el).innerHTML=renderMarkdown(m.content); } } if(j.workspace){ const sel=$('#wsSel'); const has=[...sel.options].some(o=>o.value===j.workspace); if(has){ sel.value=j.workspace; } else { refreshWsSel(); } renderBanner({text:'↩ 已回到该会话的工作空间:'+j.workspace}); } if(j.taskState&&(j.taskState.status==='cap'||j.taskState.status==='interrupted')){ renderBanner({text:'⚠ 该会话有未完成任务(步数上限中断)——直接发送消息即可从断点续跑,已完成文件不会重复做。', warn:true}); } scrollBottom(); renderEmptyState(); }
|
|
2853
|
+
async function loadSession(file){ if(currentSession && currentSession!==file) finalizeCurrentSession(); currentSession=file; chatEl.innerHTML=''; resetTraceState(); const r=await fetch('/api/session?file='+encodeURIComponent(file)); const j=await r.json(); for(const m of j.messages||[]){ if(typeof m.content==='string'&&m.content.startsWith('(系统提示)')) continue; if(m.role==='user') addUser(m.content); else { const el=newAiMsg(); aiContent(el).innerHTML=renderMarkdown(m.content); } } if(j.workspace){ const sel=$('#wsSel'); const has=[...sel.options].some(o=>o.value===j.workspace); if(has){ sel.value=j.workspace; } else { refreshWsSel(); } renderBanner({text:'↩ 已回到该会话的工作空间:'+j.workspace}); } if(j.taskState&&(j.taskState.status==='cap'||j.taskState.status==='interrupted')){ renderBanner({text:'⚠ 该会话有未完成任务(步数上限中断)——直接发送消息即可从断点续跑,已完成文件不会重复做。', warn:true}); } scrollBottom(); renderEmptyState(); renderCrumb(); }
|
|
2803
2854
|
|
|
2804
2855
|
// §138:主按钮在生成态=停止,必须走**同一个**停止出口(stopTurnFromButton → stopTurn → POST /api/abort)
|
|
2805
2856
|
sendBtn.onclick=()=>{ if(generating){ stopTurnFromButton(); } else { send(); } };
|
|
2806
2857
|
input.addEventListener('keydown',e=>{ if(e.key==='Enter'&&!e.shiftKey){ e.preventDefault(); send(); } });
|
|
2807
2858
|
input.addEventListener('input',()=>{ input.style.height='44px'; input.style.height=Math.min(input.scrollHeight,200)+'px'; });
|
|
2808
|
-
$('#newChat').onclick=()=>{ finalizeCurrentSession(); currentSession=null; chatEl.innerHTML=''; resetTraceState(); renderEmptyState(); refreshSessions(); };
|
|
2859
|
+
$('#newChat').onclick=()=>{ finalizeCurrentSession(); currentSession=null; chatEl.innerHTML=''; resetTraceState(); renderEmptyState(); renderCrumb(); refreshSessions(); };
|
|
2809
2860
|
$('#sessMgrBtn').onclick=openSessModal;
|
|
2810
2861
|
$('#sessClose').onclick=()=>{ $('#sessModal').style.display='none'; };
|
|
2811
2862
|
async function openSessModal(){
|
|
@@ -2821,6 +2872,6 @@ async function openSessModal(){
|
|
|
2821
2872
|
div.appendChild(rn); div.appendChild(dl); list.appendChild(div);
|
|
2822
2873
|
}
|
|
2823
2874
|
}
|
|
2824
|
-
$('#sessions').onchange=e=>{ if(e.target.value){ loadSession(e.target.value); } else { finalizeCurrentSession(); currentSession=null; chatEl.innerHTML=''; resetTraceState(); renderEmptyState(); } };
|
|
2875
|
+
$('#sessions').onchange=e=>{ if(e.target.value){ loadSession(e.target.value); } else { finalizeCurrentSession(); currentSession=null; chatEl.innerHTML=''; resetTraceState(); renderEmptyState(); renderCrumb(); } };
|
|
2825
2876
|
if('serviceWorker' in navigator){ navigator.serviceWorker.register('/sw.js').catch(()=>{}); }
|
|
2826
2877
|
init();
|
package/src/web/index.html
CHANGED
|
@@ -65,14 +65,35 @@ header{min-height:52px;display:flex;align-items:center;gap:10px;padding:8px 12px
|
|
|
65
65
|
header .logo{font-weight:700;font-size:16px;color:var(--accent);letter-spacing:.5px;flex:none}
|
|
66
66
|
header .logo span{color:var(--text)}
|
|
67
67
|
header .badge{font-size:12px;color:var(--dim);background:var(--bg3);border:1px solid var(--border);padding:2px 10px;border-radius:20px;flex:none;overflow:hidden;text-overflow:ellipsis;max-width:180px}
|
|
68
|
+
/* §3.83:crumb 是左半区唯一会随会话名变长的项;限到 120px,避免会话名一长就把右半区的
|
|
69
|
+
「历史会话」再次挤出 1000px 视口(完整标题仍在 title 里,内容不丢)。 */
|
|
70
|
+
#crumb{max-width:120px}
|
|
68
71
|
header button{white-space:nowrap;flex:none}
|
|
69
72
|
header select{flex:none}
|
|
73
|
+
/* §3.83 顶栏 1000px 不裁切:右半区控件(费用 / 搜索会话 / 历史会话 / 任务 / 会话管理 / 新对话)
|
|
74
|
+
收进一个可横向滚动的容器(min-width:0 才允许收缩);宽屏由容器内的 .spacer 把控件推到最右,
|
|
75
|
+
窄屏从「费用 + 搜索会话 + 历史会话」开始露出,其余控件在同一容器里横向滚动触达——
|
|
76
|
+
不再整条顶栏溢出到屏幕外。 */
|
|
77
|
+
header .hdr-right{display:flex;align-items:center;gap:8px;flex:1 1 auto;min-width:0;overflow-x:auto;overflow-y:hidden;scrollbar-width:thin;scrollbar-color:var(--border) transparent}
|
|
78
|
+
header .hdr-right::-webkit-scrollbar{height:4px}
|
|
79
|
+
header .hdr-right::-webkit-scrollbar-thumb{background:var(--border);border:0;border-radius:2px}
|
|
80
|
+
header .hdr-right::-webkit-scrollbar-track{background:transparent}
|
|
70
81
|
#sessionSearch{flex:1 1 auto;min-width:70px;max-width:220px;background:var(--bg3);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:6px 12px;font-size:13px}
|
|
71
82
|
#modelSel{max-width:170px;min-width:0}
|
|
72
83
|
#presetSel{max-width:140px;min-width:0}
|
|
73
84
|
#permSel{max-width:150px;min-width:0}
|
|
74
85
|
#sessions{max-width:150px;min-width:0}
|
|
75
|
-
#wsSel{max-width:
|
|
86
|
+
#wsSel{max-width:110px;min-width:64px}
|
|
87
|
+
/* §3.83 1000px:诊断徽标(≤900 本就隐藏)让位给「费用 + 搜索 + 历史会话」;顶栏 gap 10→6,
|
|
88
|
+
右半区容器因此拿到约 322px(费用 122 + 搜索 70 + 历史会话 98),三者完整可见。 */
|
|
89
|
+
@media (max-width:1100px){
|
|
90
|
+
header{gap:6px;padding:8px}
|
|
91
|
+
header .hdr-right{gap:8px}
|
|
92
|
+
#envBadge{display:none!important}
|
|
93
|
+
#wsSel{max-width:120px}
|
|
94
|
+
#sessionSearch{min-width:70px;max-width:120px}
|
|
95
|
+
#sessions{max-width:110px}
|
|
96
|
+
}
|
|
76
97
|
@media (max-width:860px){main{padding:14px 0 8px}#chat{padding:0 10px}#envBadge{display:none!important}#sessionSearch{max-width:110px}#modelSel{max-width:110px}#permSel{max-width:120px}#sessions{max-width:110px}#wsSel{max-width:96px}header{gap:6px;padding:8px 8px}header .logo span{display:none}}
|
|
77
98
|
#sessionSearch:focus{outline:none;border-color:var(--accent2)}
|
|
78
99
|
#tasksPanel{position:fixed;right:0;top:52px;bottom:0;width:280px;background:var(--bg2);border-left:1px solid var(--border);z-index:40;display:flex;flex-direction:column}
|
|
@@ -450,17 +471,19 @@ body.run-open #scrollBottomBtn{right:326px}
|
|
|
450
471
|
body.run-drawer #runPanel{display:flex;top:52px;width:300px;max-width:92vw;z-index:60;box-shadow:-12px 0 32px rgba(0,0,0,.4)}
|
|
451
472
|
}
|
|
452
473
|
|
|
453
|
-
/* —— 会话设置弹层(收编模型 / 预设 / 权限三件套)——
|
|
454
|
-
|
|
474
|
+
/* —— 会话设置弹层(收编模型 / 预设 / 权限三件套)——
|
|
475
|
+
§3.83:间距按设计稿 .popover 对齐(head/body 11px 13px、field label 5px、
|
|
476
|
+
effective 13px/11px + 8px 10px、foot 9px 13px);圆角保持设计稿 14px(sheet)/8px(effective)。 */
|
|
477
|
+
#sessSheet{position:fixed;right:18px;bottom:110px;width:min(400px,calc(100vw - 24px));max-height:min(72vh,560px);background:var(--panel);border:1px solid var(--border);border-radius:14px;box-shadow:0 16px 48px rgba(0,0,0,.5);z-index:70;display:flex;flex-direction:column;overflow:hidden}
|
|
455
478
|
body.run-open #sessSheet{right:318px}
|
|
456
479
|
@media (max-width:1180px){body.run-open #sessSheet{right:18px}}
|
|
457
|
-
.ss-head{display:flex;align-items:center;gap:8px;padding:
|
|
480
|
+
.ss-head{display:flex;align-items:center;gap:8px;padding:11px 13px;border-bottom:1px solid var(--border);font-size:13px;font-weight:600;color:var(--text);flex:none}
|
|
458
481
|
.ss-head .ss-sub{font-size:11px;color:var(--faint);font-weight:400}
|
|
459
482
|
.ss-head button{margin-left:auto;padding:1px 8px;font-size:11px}
|
|
460
|
-
.ss-body{padding:
|
|
483
|
+
.ss-body{padding:11px 13px;overflow-y:auto;min-height:0}
|
|
461
484
|
.ss-field{margin-bottom:12px}
|
|
462
485
|
.ss-field:last-child{margin-bottom:0}
|
|
463
|
-
.ss-field>label{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--dim);margin-bottom:
|
|
486
|
+
.ss-field>label{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--dim);margin-bottom:5px}
|
|
464
487
|
.ss-field .ss-scope{margin-left:auto;font-size:10px;color:var(--faint);border:1px solid var(--border);border-radius:999px;padding:0 6px;line-height:16px}
|
|
465
488
|
.ss-field select{width:100%;background:var(--bg3);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:6px 8px;font-size:13px}
|
|
466
489
|
.ss-src{font-size:11px;color:var(--faint);margin-top:4px;line-height:1.55;word-break:break-word}
|
|
@@ -469,9 +492,9 @@ body.run-open #sessSheet{right:318px}
|
|
|
469
492
|
.ss-seg{display:flex;gap:4px;background:var(--bg3);border:1px solid var(--border);border-radius:9px;padding:3px}
|
|
470
493
|
.ss-seg button{flex:1;height:26px;border:0;border-radius:6px;background:transparent;color:var(--dim);font-size:12px}
|
|
471
494
|
.ss-seg button.on{background:var(--panel);color:var(--text);font-weight:600;box-shadow:0 1px 3px rgba(0,0,0,.18)}
|
|
472
|
-
.ss-effective{margin:0
|
|
495
|
+
.ss-effective{margin:0 13px 11px;padding:8px 10px;border:1px dashed var(--border);border-radius:8px;font-size:11.5px;color:var(--dim);line-height:1.65}
|
|
473
496
|
.ss-effective b{color:var(--text)}
|
|
474
|
-
.ss-foot{display:flex;align-items:center;gap:8px;padding:
|
|
497
|
+
.ss-foot{display:flex;align-items:center;gap:8px;padding:9px 13px;border-top:1px solid var(--border)}
|
|
475
498
|
.ss-foot .grow{flex:1;font-size:11px;color:var(--faint)}
|
|
476
499
|
#sessSettingsBtn{max-width:230px;display:inline-flex;align-items:center;gap:6px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
477
500
|
#sessSettingsBtn b{overflow:hidden;text-overflow:ellipsis}
|
|
@@ -562,63 +585,82 @@ body.run-open #sessSheet{right:318px}
|
|
|
562
585
|
.dr .p{color:var(--accent2);font-variant-numeric:tabular-nums}
|
|
563
586
|
.dr .c{color:var(--faint);font-variant-numeric:tabular-nums}
|
|
564
587
|
.badge-btn:hover{border-color:var(--accent2);color:var(--accent2)}
|
|
588
|
+
/* —— §3.82 右栏四标签(轨迹 / 子Agent / 任务 / 仪表盘)——
|
|
589
|
+
只新增标签栏/标签页容器样式;#runPanel 宽度、body.run-open 推开量、顶栏与网格均不动。
|
|
590
|
+
四个面板本体搬进标签页后不再 fixed(覆盖既有 #tasksPanel/#dashPanel 的定位,宽度跟随右栏)。 */
|
|
591
|
+
.rp-top{flex:none;padding:10px 12px 0}
|
|
592
|
+
.rp-top .rp-sec{margin-bottom:8px}
|
|
593
|
+
/* §3.83:四个标签统一成设计稿 `.seg` 分段控件观感(外框 bg3 + padding 3 / 圆角 9,
|
|
594
|
+
内按钮 26px 高 / 圆角 6;选中态 panel 底 + text 字 + 600 字重)。只改样式,切换逻辑不动。 */
|
|
595
|
+
.rp-tabs{display:flex;gap:4px;padding:3px;margin:0 12px 8px;flex:none;background:var(--bg3);border:1px solid var(--border);border-radius:9px}
|
|
596
|
+
.rp-tabs .seg{flex:1;min-width:0;height:26px;padding:0 4px;border:0;border-radius:6px;background:transparent;color:var(--dim);font-size:12px;white-space:nowrap;cursor:pointer}
|
|
597
|
+
.rp-tabs .seg:hover{color:var(--text)}
|
|
598
|
+
.rp-tabs .seg.on{background:var(--panel);color:var(--text);font-weight:600;box-shadow:0 1px 3px rgba(0,0,0,.18)}
|
|
599
|
+
.rp-tabbody{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}
|
|
600
|
+
.rp-tab{flex:1;min-height:0;display:flex;flex-direction:column;overflow-y:auto;padding:10px 12px}
|
|
601
|
+
.rp-tab.rp-tab-flush{padding:0;overflow:hidden}
|
|
602
|
+
#runPanel #tasksPanel,#runPanel #dashPanel{position:static;right:auto;top:auto;bottom:auto;width:auto;max-width:none;height:auto;border-left:0;z-index:auto;box-shadow:none;background:transparent;flex:1;min-height:0;overflow:hidden}
|
|
603
|
+
|
|
604
|
+
/* —— §3.83 对比度:承载信息的次要文本从 --faint 提到 --dim ——
|
|
605
|
+
只改下列选择器的 color,**不动** :root 里的 --faint/--dim 取值(避免全局漂移);
|
|
606
|
+
WCAG 对比度(暗色 #81858C→#ADB2B8):bg 4.92→8.54、bg2 4.64→8.06、
|
|
607
|
+
panel 3.76→6.53、bg3 3.26→5.67。覆盖:消息元信息/用量、提示条、任务与子Agent状态、
|
|
608
|
+
轨迹轮次与计数、工具耗时与输出计数、来源/作用域胶囊、rp-foot 说明、空态说明、底部累计状态。 */
|
|
609
|
+
.msg-meta,
|
|
610
|
+
.usage,
|
|
611
|
+
.banner:not(.banner-warn),
|
|
612
|
+
#hint,
|
|
613
|
+
#statusBar,
|
|
614
|
+
.sb-item .sb-meta,
|
|
615
|
+
.tp-item .tp-meta,
|
|
616
|
+
.tl-row .tl-dur,
|
|
617
|
+
.tl-out-meta,
|
|
618
|
+
.tl-more,
|
|
619
|
+
.tr-round-no,
|
|
620
|
+
.tr-sub-meta,
|
|
621
|
+
.tr-sub-stats,
|
|
622
|
+
.tr-sub-wait,
|
|
623
|
+
.tr-sub-state,
|
|
624
|
+
.rp-fold-meta,
|
|
625
|
+
.td-fold-meta,
|
|
626
|
+
.td-meta,
|
|
627
|
+
.rp-kv .rp-src,
|
|
628
|
+
.rp-foot .grow,
|
|
629
|
+
.ss-src,
|
|
630
|
+
.ss-field .ss-scope,
|
|
631
|
+
.dash-head .dash-sub,
|
|
632
|
+
.dr .c,
|
|
633
|
+
.tr-empty,
|
|
634
|
+
#emptyState .es-lead,
|
|
635
|
+
.empty-step .es-main span,
|
|
636
|
+
.empty-step .es-state{color:var(--dim)}
|
|
565
637
|
</style>
|
|
566
638
|
</head>
|
|
567
639
|
<body>
|
|
568
640
|
<header>
|
|
569
641
|
<div class="logo">MingDao <span>Harness</span></div>
|
|
642
|
+
<!-- v0.6.21 小步重排(§3.81):顶栏只保留「我在哪」的当前位置;模型/预设/权限只在 #sessSheet 里改。 -->
|
|
643
|
+
<span id="crumb" class="badge" title="当前工作空间 › 当前会话"><span id="crumbWs">工作空间</span><span aria-hidden="true"> › </span><b id="crumbSession">新对话</b></span>
|
|
570
644
|
<span id="cfgMsg" class="badge" style="display:none"></span>
|
|
571
645
|
<button id="paletteBtn" title="搜索或运行命令(⌘K / Ctrl+K)" aria-haspopup="dialog">⌘K <span class="palette-label">搜索或运行</span></button>
|
|
572
646
|
<button id="cfgBtn" title="设置">⚙</button>
|
|
573
647
|
<button id="runChip" title="本轮运行面板(窄屏以抽屉展开)" aria-expanded="false">运行</button>
|
|
574
648
|
<button id="themeBtn" title="切换明暗主题(跟随系统时由系统偏好决定)" aria-label="切换明暗主题">🌙</button>
|
|
575
649
|
<select id="wsSel" title="工作空间(切换 / 新建,目录自动创建)"></select>
|
|
576
|
-
<span class="badge" id="envBadge"></span
|
|
577
|
-
<div class="
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
650
|
+
<span class="badge" id="envBadge"></span>
|
|
651
|
+
<div class="hdr-right">
|
|
652
|
+
<div class="spacer"></div>
|
|
653
|
+
<span class="badge badge-btn" id="costBadge" title="省钱仪表盘(点击展开)" style="cursor:pointer">📊 —</span>
|
|
654
|
+
<input id="sessionSearch" placeholder="搜索会话…" title="关键词检索历史会话">
|
|
655
|
+
<select id="sessions" title="历史会话"><option value="">历史会话</option></select>
|
|
656
|
+
<button id="tasksBtn" title="任务面板">任务</button><button id="sessMgrBtn" title="管理会话(重命名/删除)">✎</button>
|
|
657
|
+
<button id="newChat">新对话</button>
|
|
658
|
+
</div>
|
|
582
659
|
</header>
|
|
583
660
|
<div id="trajRail">
|
|
584
661
|
<button class="rail-btn" id="trajRailBtn" title="轨迹(点击查看详情)">🧭</button>
|
|
585
662
|
<button class="rail-btn" id="subRailBtn" title="子 Agent">🤖<span class="rail-badge" id="subBadge" style="display:none"></span></button>
|
|
586
663
|
</div>
|
|
587
|
-
<aside id="subPanel" style="display:none">
|
|
588
|
-
<div class="sb-head">🤖 子 Agent<span id="sbCount" style="font-size:11.5px;color:var(--faint);font-weight:400"></span><button id="sbClose">关闭</button></div>
|
|
589
|
-
<div id="sbList"></div>
|
|
590
|
-
</aside>
|
|
591
|
-
<aside id="trajPanel" style="display:none">
|
|
592
|
-
<div class="tj-head">🧭 任务轨迹<span id="tjSummary" style="font-size:11.5px;color:var(--faint);font-weight:400"></span><button id="tjClose">关闭</button></div>
|
|
593
|
-
<div id="tjList"></div>
|
|
594
|
-
</aside>
|
|
595
|
-
<aside id="tasksPanel" style="display:none">
|
|
596
|
-
<div class="tp-head">任务面板 <span id="tpCount"></span><button id="tpClose" style="float:right">✕</button></div>
|
|
597
|
-
<div id="tpList"></div>
|
|
598
|
-
</aside>
|
|
599
|
-
<aside id="dashPanel" style="display:none">
|
|
600
|
-
<div class="dash-head">📊 省钱仪表盘 <span class="dash-sub">费用 · 命中 · 节省</span><button id="dashClose">✕</button></div>
|
|
601
|
-
<div class="dash-body">
|
|
602
|
-
<div class="dash-kpi">
|
|
603
|
-
<div class="kpi"><span class="kpi-l">今日费用</span><span class="kpi-v" id="kpiToday">—</span></div>
|
|
604
|
-
<div class="kpi"><span class="kpi-l">缓存命中</span><span class="kpi-v" id="kpiRate">—</span></div>
|
|
605
|
-
<div class="kpi"><span class="kpi-l">已节省</span><span class="kpi-v" style="color:var(--accent)" id="kpiSaved">—</span></div>
|
|
606
|
-
<div class="kpi"><span class="kpi-l">轮次/步数</span><span class="kpi-v" id="kpiTurns">—</span></div>
|
|
607
|
-
</div>
|
|
608
|
-
<div class="dash-gauge-wrap">
|
|
609
|
-
<svg id="rateGauge" viewBox="0 0 120 120" width="120" height="120"></svg>
|
|
610
|
-
<div class="gauge-label"><b id="rateGaugeNum">—</b><span>命中率</span></div>
|
|
611
|
-
</div>
|
|
612
|
-
<div class="dash-sec">近 14 天费用趋势(¥)</div>
|
|
613
|
-
<div id="trendChart" class="dash-chart"></div>
|
|
614
|
-
<div class="dash-sec">模型费用 Top</div>
|
|
615
|
-
<div id="dashModels" class="dash-bars"></div>
|
|
616
|
-
<div class="dash-sec">工具耗时 Top</div>
|
|
617
|
-
<div id="dashTools" class="dash-bars"></div>
|
|
618
|
-
<div class="dash-sec">最近缓存命中</div>
|
|
619
|
-
<div id="dashRecent" class="dash-recent"></div>
|
|
620
|
-
</div>
|
|
621
|
-
</aside>
|
|
622
664
|
<main>
|
|
623
665
|
<!-- P1-6 空态三步引导(v0.6.19 §3.70):无会话 / 无消息时显示;示例任务只填入输入框,不自动发送。 -->
|
|
624
666
|
<div id="emptyState" hidden>
|
|
@@ -690,7 +732,8 @@ body.run-open #sessSheet{right:318px}
|
|
|
690
732
|
≥1180 默认展开并推开正文;<1180 收起为顶栏「运行」chip,点开是覆盖式抽屉。无新增轮询。 -->
|
|
691
733
|
<aside id="runPanel" aria-label="本轮运行" hidden>
|
|
692
734
|
<div class="rp-head"><span class="rp-live" aria-hidden="true"></span><span>本轮运行</span><span class="rp-sub">指标口径:本轮(非累计)</span><button id="rpCollapse" type="button" title="收起右栏">收起</button></div>
|
|
693
|
-
|
|
735
|
+
<!-- §3.82:状态条(阶段/首帧/吞吐/工具数/上下文/费用)留在顶部,不随标签切换。 -->
|
|
736
|
+
<div class="rp-top">
|
|
694
737
|
<section class="rp-sec">
|
|
695
738
|
<div class="rp-sec-title">会话配置<span class="line"></span></div>
|
|
696
739
|
<dl class="rp-kv">
|
|
@@ -712,15 +755,71 @@ body.run-open #sessSheet{right:318px}
|
|
|
712
755
|
<div class="rp-metric wide"><span class="l">本轮费用(估算)</span><span class="v" data-rk="cost" id="rpCost">—</span></div>
|
|
713
756
|
</div>
|
|
714
757
|
</section>
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
758
|
+
</div>
|
|
759
|
+
<!-- §3.82 第二步:四个标签。只切 display(railTabDisplay),节点不销毁、id 不变。 -->
|
|
760
|
+
<div class="rp-tabs" role="tablist" aria-label="右栏标签">
|
|
761
|
+
<button id="rtTrace" type="button" role="tab" data-tab="trace" aria-selected="true" aria-controls="tabTrace" class="seg on">轨迹</button>
|
|
762
|
+
<button id="rtSub" type="button" role="tab" data-tab="sub" aria-selected="false" aria-controls="tabSub" class="seg">子Agent</button>
|
|
763
|
+
<button id="rtTasks" type="button" role="tab" data-tab="tasks" aria-selected="false" aria-controls="tabTasks" class="seg">任务</button>
|
|
764
|
+
<button id="rtDash" type="button" role="tab" data-tab="dash" aria-selected="false" aria-controls="tabDash" class="seg">仪表盘</button>
|
|
765
|
+
</div>
|
|
766
|
+
<div class="rp-tabbody">
|
|
767
|
+
<section id="tabTrace" class="rp-tab" role="tabpanel" aria-labelledby="rtTrace" data-tab="trace">
|
|
768
|
+
<!-- §3.73:轨迹折叠区(renderTracePanels 写入 #rpTraceList,不在这里重写)。 -->
|
|
769
|
+
<section class="rp-sec" id="rpTraceSec">
|
|
770
|
+
<button class="rp-sec-title rp-fold-head" type="button" data-fold="trace" aria-controls="rpTraceFoldBody" aria-expanded="true">轨迹<span class="rp-fold-meta" id="rpTraceMeta">—</span><span class="line"></span><span class="rp-fold-tri" aria-hidden="true"></span></button>
|
|
771
|
+
<div id="rpTraceFoldBody"><div id="rpTraceList" class="tr-list"></div></div>
|
|
772
|
+
</section>
|
|
773
|
+
<!-- 旧轨迹面板随标签搬入(id/子节点原样保留,openTraj 仍写 #tjList);默认收起,避免与上面的轮次列表重复。 -->
|
|
774
|
+
<aside id="trajPanel" style="display:none">
|
|
775
|
+
<div class="tj-head">🧭 任务轨迹<span id="tjSummary" style="font-size:11.5px;color:var(--faint);font-weight:400"></span><button id="tjClose">关闭</button></div>
|
|
776
|
+
<div id="tjList"></div>
|
|
777
|
+
</aside>
|
|
778
|
+
</section>
|
|
779
|
+
<section id="tabSub" class="rp-tab" role="tabpanel" aria-labelledby="rtSub" data-tab="sub" style="display:none">
|
|
780
|
+
<!-- §3.73:子Agent 折叠区(renderTracePanels 写入 #rpSubList)。 -->
|
|
781
|
+
<section class="rp-sec" id="rpSubSec">
|
|
782
|
+
<button class="rp-sec-title rp-fold-head" type="button" data-fold="subs" aria-controls="rpSubFoldBody" aria-expanded="true">子Agent<span class="rp-fold-meta" id="rpSubMeta">—</span><span class="line"></span><span class="rp-fold-tri" aria-hidden="true"></span></button>
|
|
783
|
+
<div id="rpSubFoldBody"><div id="rpSubList" class="tr-list"></div></div>
|
|
784
|
+
</section>
|
|
785
|
+
<!-- 旧子Agent 面板随标签搬入(id/子节点原样保留);默认收起。 -->
|
|
786
|
+
<aside id="subPanel" style="display:none">
|
|
787
|
+
<div class="sb-head">🤖 子 Agent<span id="sbCount" style="font-size:11.5px;color:var(--faint);font-weight:400"></span><button id="sbClose">关闭</button></div>
|
|
788
|
+
<div id="sbList"></div>
|
|
789
|
+
</aside>
|
|
790
|
+
</section>
|
|
791
|
+
<section id="tabTasks" class="rp-tab rp-tab-flush" role="tabpanel" aria-labelledby="rtTasks" data-tab="tasks" style="display:none">
|
|
792
|
+
<!-- 任务面板:原 <aside id="tasksPanel"> 整体搬入(#tpList/#tpCount 不动),宽度改为跟随右栏。 -->
|
|
793
|
+
<aside id="tasksPanel" style="display:none">
|
|
794
|
+
<div class="tp-head">任务面板 <span id="tpCount"></span><button id="tpClose" style="float:right">✕</button></div>
|
|
795
|
+
<div id="tpList"></div>
|
|
796
|
+
</aside>
|
|
720
797
|
</section>
|
|
721
|
-
<section class="rp-
|
|
722
|
-
|
|
723
|
-
<
|
|
798
|
+
<section id="tabDash" class="rp-tab rp-tab-flush" role="tabpanel" aria-labelledby="rtDash" data-tab="dash" style="display:none">
|
|
799
|
+
<!-- 省钱仪表盘:原 <aside id="dashPanel"> 整体搬入(KPI/趋势/Top 全部保留),宽度改为跟随右栏。 -->
|
|
800
|
+
<aside id="dashPanel" style="display:none">
|
|
801
|
+
<div class="dash-head">📊 省钱仪表盘 <span class="dash-sub">费用 · 命中 · 节省</span><button id="dashClose">✕</button></div>
|
|
802
|
+
<div class="dash-body">
|
|
803
|
+
<div class="dash-kpi">
|
|
804
|
+
<div class="kpi"><span class="kpi-l">今日费用</span><span class="kpi-v" id="kpiToday">—</span></div>
|
|
805
|
+
<div class="kpi"><span class="kpi-l">缓存命中</span><span class="kpi-v" id="kpiRate">—</span></div>
|
|
806
|
+
<div class="kpi"><span class="kpi-l">已节省</span><span class="kpi-v" style="color:var(--accent)" id="kpiSaved">—</span></div>
|
|
807
|
+
<div class="kpi"><span class="kpi-l">轮次/步数</span><span class="kpi-v" id="kpiTurns">—</span></div>
|
|
808
|
+
</div>
|
|
809
|
+
<div class="dash-gauge-wrap">
|
|
810
|
+
<svg id="rateGauge" viewBox="0 0 120 120" width="120" height="120"></svg>
|
|
811
|
+
<div class="gauge-label"><b id="rateGaugeNum">—</b><span>命中率</span></div>
|
|
812
|
+
</div>
|
|
813
|
+
<div class="dash-sec">近 14 天费用趋势(¥)</div>
|
|
814
|
+
<div id="trendChart" class="dash-chart"></div>
|
|
815
|
+
<div class="dash-sec">模型费用 Top</div>
|
|
816
|
+
<div id="dashModels" class="dash-bars"></div>
|
|
817
|
+
<div class="dash-sec">工具耗时 Top</div>
|
|
818
|
+
<div id="dashTools" class="dash-bars"></div>
|
|
819
|
+
<div class="dash-sec">最近缓存命中</div>
|
|
820
|
+
<div id="dashRecent" class="dash-recent"></div>
|
|
821
|
+
</div>
|
|
822
|
+
</aside>
|
|
724
823
|
</section>
|
|
725
824
|
</div>
|
|
726
825
|
<div class="rp-foot"><span class="grow" id="rpFootNote">停止与压缩:与状态条同一条停止出口</span><button id="rpCompact" type="button" hidden title="后端有 /compact 时才显示">压缩上下文</button><button id="rpStop" type="button" disabled>■ 停止</button></div>
|
|
@@ -739,7 +838,7 @@ body.run-open #sessSheet{right:318px}
|
|
|
739
838
|
<div class="ss-field">
|
|
740
839
|
<label for="presetSel">预设<span class="ss-scope">仅下一轮</span></label>
|
|
741
840
|
<select id="presetSel" title="智能体预设:一键切换工具白名单/权限/参数组合(选「无预设」恢复默认)"><option value="">无预设</option></select>
|
|
742
|
-
<div class="ss-src"
|
|
841
|
+
<div class="ss-src" id="ssPresetSrc">当前:无预设 · 来源:你的选择 · 仅下一轮(工具白名单 / 参数);不改变权限来源。</div>
|
|
743
842
|
</div>
|
|
744
843
|
<div class="ss-field">
|
|
745
844
|
<label>权限<span class="ss-scope">全局 + 本轮</span></label>
|