@wendongfly/myhi 1.3.171 → 1.3.173
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/dist/chat.html +172 -35
- package/dist/grid.html +34 -3
- package/dist/index.js +1 -1
- package/dist/index.min.js +1 -1
- package/package.json +1 -1
package/dist/chat.html
CHANGED
|
@@ -213,6 +213,28 @@
|
|
|
213
213
|
#input-box.focused { border-color: var(--blue); }
|
|
214
214
|
#input-box.disabled { opacity: 0.5; pointer-events: none; }
|
|
215
215
|
|
|
216
|
+
/* 用户消息快速导航(借鉴 deepseek 的对话引导):右缘悬浮把手 + 消息清单 */
|
|
217
|
+
#msg-nav-btn {
|
|
218
|
+
position: fixed; right: 0.4rem; bottom: 9.5rem; z-index: 60;
|
|
219
|
+
width: 2rem; height: 2rem; border-radius: 50%;
|
|
220
|
+
background: var(--surface); border: 1px solid var(--border); color: var(--muted);
|
|
221
|
+
font-size: 0.95rem; cursor: pointer; display: none; align-items: center; justify-content: center;
|
|
222
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.35);
|
|
223
|
+
}
|
|
224
|
+
#msg-nav-btn:hover { color: var(--text); border-color: var(--blue); }
|
|
225
|
+
#msg-nav-panel {
|
|
226
|
+
position: fixed; right: 0.5rem; bottom: 11.8rem; z-index: 60; display: none;
|
|
227
|
+
width: min(320px, 78vw); max-height: 55vh; overflow-y: auto;
|
|
228
|
+
background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
|
|
229
|
+
box-shadow: 0 8px 30px rgba(0,0,0,0.45); padding: 0.3rem;
|
|
230
|
+
}
|
|
231
|
+
#msg-nav-panel .mn-item {
|
|
232
|
+
padding: 0.42rem 0.55rem; border-radius: 7px; cursor: pointer;
|
|
233
|
+
font-size: 0.78rem; color: var(--text2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
234
|
+
}
|
|
235
|
+
#msg-nav-panel .mn-item:hover { background: var(--surface3); color: var(--text); }
|
|
236
|
+
.msg-flash { outline: 2px solid transparent; border-radius: 10px; animation: msgflash 1.8s; }
|
|
237
|
+
@keyframes msgflash { 0%, 55% { outline-color: var(--blue); } 100% { outline-color: transparent; } }
|
|
216
238
|
/* @ 文件引用补全浮层(挂在 body 上,fixed 定位在输入框上方) */
|
|
217
239
|
#mention-box {
|
|
218
240
|
position: fixed; z-index: 95; display: none;
|
|
@@ -497,34 +519,38 @@
|
|
|
497
519
|
<button class="sk" data-send="esc">Esc</button>
|
|
498
520
|
</div>
|
|
499
521
|
<div id="sk-claude-pty" style="display:none">
|
|
500
|
-
<button class="sk sk-claude" onclick="openModelSheet()">模型</button>
|
|
501
|
-
<button class="sk sk-claude" data-cmd="/cost">费用</button>
|
|
502
|
-
<button class="sk sk-claude" data-cmd="/memory">记忆</button>
|
|
503
|
-
<button class="sk sk-claude" onclick="openResumeSheet()">恢复</button>
|
|
504
|
-
<button class="sk sk-claude" onclick="doCompact()">压缩</button>
|
|
505
|
-
<button class="sk sk-claude" data-cmd="/clear">清除</button>
|
|
506
|
-
<button class="sk sk-claude" onclick="openGitSheet()" style="color:var(--green)">提交</button>
|
|
522
|
+
<button class="sk sk-claude" data-sk="model" onclick="openModelSheet()">模型</button>
|
|
523
|
+
<button class="sk sk-claude" data-sk="cost" data-cmd="/cost">费用</button>
|
|
524
|
+
<button class="sk sk-claude" data-sk="memory" data-cmd="/memory">记忆</button>
|
|
525
|
+
<button class="sk sk-claude" data-sk="resume" onclick="openResumeSheet()">恢复</button>
|
|
526
|
+
<button class="sk sk-claude" data-sk="compact" onclick="doCompact()">压缩</button>
|
|
527
|
+
<button class="sk sk-claude" data-sk="clear" data-cmd="/clear">清除</button>
|
|
528
|
+
<button class="sk sk-claude" data-sk="git" onclick="openGitSheet()" style="color:var(--green)">提交</button>
|
|
507
529
|
<button class="sk" data-send="ctrl-c">Ctrl+C</button>
|
|
508
530
|
<button class="sk" data-send="esc">Esc</button>
|
|
509
531
|
</div>
|
|
510
532
|
<div id="sk-claude-agent" style="display:none">
|
|
511
|
-
<button class="sk sk-claude" id="sk-inbox" onclick="openInboxSheet()" style="color:var(--yellow)">📥 草稿<span id="inbox-badge" style="display:none;margin-left:0.2rem;background:var(--yellow);color:#000;border-radius:8px;padding:0 0.32rem;font-size:0.66rem;font-weight:700"></span></button>
|
|
512
|
-
<button class="sk sk-claude" onclick="openModelSheet()">模型</button>
|
|
513
|
-
<button class="sk sk-claude" id="btn-plan" onclick="togglePlanMode()">计划</button>
|
|
514
|
-
<button class="sk sk-claude" onclick="doSlashCmd('/compact')">压缩</button>
|
|
515
|
-
<button class="sk sk-claude" onclick="openResumeSheet()">恢复</button>
|
|
516
|
-
<button class="sk sk-claude" onclick="doClear()">清除</button>
|
|
517
|
-
<button class="sk sk-claude" onclick="openSecretsSheet()" style="color:var(--blue2)">🔑 密钥</button>
|
|
518
|
-
<button class="sk sk-claude" onclick="openGuardSheet()" style="color:var(--blue2)">🛡 护栏</button>
|
|
519
|
-
<button class="sk sk-claude" onclick="openFollowUpSheet()" style="color:var(--blue2)">🔄 跟进</button>
|
|
520
|
-
<button class="sk sk-claude" onclick="openGitSheet()" style="color:var(--green)">提交</button>
|
|
521
|
-
<button class="sk sk-claude" onclick="showMemory()">记忆</button>
|
|
522
|
-
<button class="sk sk-claude" onclick="openSkillSheet()" style="color:var(--purple2)">技能</button>
|
|
523
|
-
<span id="sk-builtin-skills"></span>
|
|
524
|
-
<span id="sk-custom-skills"></span>
|
|
533
|
+
<button class="sk sk-claude" id="sk-inbox" data-sk="draft" onclick="openInboxSheet()" style="color:var(--yellow)">📥 草稿<span id="inbox-badge" style="display:none;margin-left:0.2rem;background:var(--yellow);color:#000;border-radius:8px;padding:0 0.32rem;font-size:0.66rem;font-weight:700"></span></button>
|
|
534
|
+
<button class="sk sk-claude" data-sk="model" onclick="openModelSheet()">模型</button>
|
|
535
|
+
<button class="sk sk-claude" id="btn-plan" data-sk="plan" onclick="togglePlanMode()">计划</button>
|
|
536
|
+
<button class="sk sk-claude" data-sk="compact" onclick="doSlashCmd('/compact')">压缩</button>
|
|
537
|
+
<button class="sk sk-claude" data-sk="resume" onclick="openResumeSheet()">恢复</button>
|
|
538
|
+
<button class="sk sk-claude" data-sk="clear" onclick="doClear()">清除</button>
|
|
539
|
+
<button class="sk sk-claude" data-sk="secrets" onclick="openSecretsSheet()" style="color:var(--blue2)">🔑 密钥</button>
|
|
540
|
+
<button class="sk sk-claude" data-sk="guard" onclick="openGuardSheet()" style="color:var(--blue2)">🛡 护栏</button>
|
|
541
|
+
<button class="sk sk-claude" data-sk="followup" onclick="openFollowUpSheet()" style="color:var(--blue2)">🔄 跟进</button>
|
|
542
|
+
<button class="sk sk-claude" data-sk="git" onclick="openGitSheet()" style="color:var(--green)">提交</button>
|
|
543
|
+
<button class="sk sk-claude" data-sk="memory" onclick="showMemory()">记忆</button>
|
|
544
|
+
<button class="sk sk-claude" data-sk="skills" onclick="openSkillSheet()" style="color:var(--purple2)">技能</button>
|
|
545
|
+
<span id="sk-builtin-skills" data-sk="skills"></span>
|
|
546
|
+
<span id="sk-custom-skills" data-sk="skills"></span>
|
|
525
547
|
</div>
|
|
526
548
|
</div>
|
|
527
549
|
|
|
550
|
+
<!-- 用户消息快速导航:右缘把手 + 消息清单(点一条跳到那条) -->
|
|
551
|
+
<button id="msg-nav-btn" onclick="toggleMsgNav()" title="定位我发过的消息">☰</button>
|
|
552
|
+
<div id="msg-nav-panel"></div>
|
|
553
|
+
|
|
528
554
|
<!-- 文件管理面板 -->
|
|
529
555
|
<div id="file-panel-backdrop" onclick="closeFilePanel()"></div>
|
|
530
556
|
<div id="file-panel">
|
|
@@ -541,8 +567,7 @@
|
|
|
541
567
|
</div>
|
|
542
568
|
<div class="fp-path" id="fp-path"></div>
|
|
543
569
|
<div id="fp-tools" style="display:flex;gap:0.4rem;align-items:center;padding:0.3rem 0.75rem;flex-wrap:wrap">
|
|
544
|
-
<button class="fp-btn" onclick="fpOpenFolder()" title="
|
|
545
|
-
<button class="fp-btn" onclick="fpOpenEditor()" title="在服务端本机用 VS Code 打开(需装 code 命令)">VS Code(服务端)</button>
|
|
570
|
+
<button class="fp-btn" onclick="fpOpenFolder()" title="在本机桌面打开该任务的文件夹——仅当浏览器与 myhi 服务在同一台机器时有效;远程访问不会有效果,请用下方列表浏览/下载">📂 打开当前文件夹(不支持远程)</button>
|
|
546
571
|
<span style="flex:1"></span>
|
|
547
572
|
<button class="fp-btn" id="fp-sort-btn" onclick="fpToggleSort()" title="切换排序">排序: 名称</button>
|
|
548
573
|
</div>
|
|
@@ -581,6 +606,7 @@
|
|
|
581
606
|
<label class="setting-toggle"><span>解析终端输出</span><input type="checkbox" id="set-parse" checked></label>
|
|
582
607
|
<label class="setting-toggle"><span>显示思考过程</span><input type="checkbox" id="set-thinking" checked></label>
|
|
583
608
|
<label class="setting-toggle"><span>工具调用默认折叠</span><input type="checkbox" id="set-collapse" checked></label>
|
|
609
|
+
<label class="setting-toggle"><span>思考过程默认折叠</span><input type="checkbox" id="set-thinkcollapse" checked></label>
|
|
584
610
|
</div>
|
|
585
611
|
<div class="setting-group">
|
|
586
612
|
<h4>输入</h4>
|
|
@@ -594,6 +620,17 @@
|
|
|
594
620
|
自动:电脑回车发送 / Shift+回车换行;手机回车换行、用按钮发送。<br>发送:回车总是发送(换行用 Shift+回车)。换行:回车总是换行(发送用 Ctrl/⌘+回车 或按钮)。
|
|
595
621
|
</div>
|
|
596
622
|
</div>
|
|
623
|
+
<div class="setting-group">
|
|
624
|
+
<h4>快捷栏</h4>
|
|
625
|
+
<div id="seg-skmode" class="seg-row">
|
|
626
|
+
<button class="seg" data-skmode="normal">普通</button>
|
|
627
|
+
<button class="seg" data-skmode="expert">专家</button>
|
|
628
|
+
</div>
|
|
629
|
+
<div style="font-size:0.72rem;color:var(--muted);margin-top:0.35rem;line-height:1.5">
|
|
630
|
+
专家 = 显示全部快捷按钮(默认)。普通 = 只显示下面勾选的,给简单用户减负。
|
|
631
|
+
</div>
|
|
632
|
+
<div id="sk-visible-list" style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:0.15rem 0.6rem;margin-top:0.5rem"></div>
|
|
633
|
+
</div>
|
|
597
634
|
<div class="setting-group">
|
|
598
635
|
<h4>语音识别 (ASR)</h4>
|
|
599
636
|
<div style="font-size:0.72rem;color:var(--muted);margin-bottom:0.35rem">SenseVoice 或兼容服务的地址,留空表示禁用语音</div>
|
|
@@ -1039,7 +1076,13 @@
|
|
|
1039
1076
|
const ansi = new AnsiUp();
|
|
1040
1077
|
ansi.use_classes = false;
|
|
1041
1078
|
|
|
1042
|
-
fetch('/api/me').then(r => r.json()).
|
|
1079
|
+
fetch('/api/me').then(r => r.json()).then(data => {
|
|
1080
|
+
// 打包默认快捷栏预设:用户没自定义过勾选(skVisible 为 null)时采用服务端 config.json 的 skMode
|
|
1081
|
+
if (data?.skMode && settings.skVisible == null) {
|
|
1082
|
+
settings.skVisible = data.skMode === 'normal' ? skPresetNormal() : skPresetAll();
|
|
1083
|
+
settings.skMode = data.skMode; saveSettings(); syncSkModeUI(); updateShortcutBar();
|
|
1084
|
+
}
|
|
1085
|
+
}).catch(() => {});
|
|
1043
1086
|
window.showUsage = async () => {
|
|
1044
1087
|
try {
|
|
1045
1088
|
const res = await fetch('/api/usage');
|
|
@@ -1125,7 +1168,10 @@
|
|
|
1125
1168
|
let workState = 'idle';
|
|
1126
1169
|
|
|
1127
1170
|
// 设置
|
|
1128
|
-
|
|
1171
|
+
// 快捷栏:skVisible 逐按钮勾选(唯一显隐依据,null=默认全显);skMode 仅记录最接近的预设(高亮用)
|
|
1172
|
+
const SK_LABELS = { draft:'📥 草稿', model:'模型', plan:'计划', compact:'压缩', resume:'恢复', clear:'清除', secrets:'🔑 密钥', guard:'🛡 护栏', followup:'🔄 跟进', git:'提交', memory:'记忆', skills:'技能', cost:'费用' };
|
|
1173
|
+
const SK_NORMAL_PRESET = ['draft', 'plan', 'clear', 'guard', 'memory', 'skills'];
|
|
1174
|
+
const DEFAULT_SETTINGS = { parseOutput: true, showThinking: true, collapseTools: true, thinkCollapsed: true, enterMode: 'auto', skMode: null, skVisible: null };
|
|
1129
1175
|
let settings = loadSettings();
|
|
1130
1176
|
|
|
1131
1177
|
function loadSettings() { try { return { ...DEFAULT_SETTINGS, ...JSON.parse(localStorage.getItem('myhi_chat_settings')) }; } catch { return { ...DEFAULT_SETTINGS }; } }
|
|
@@ -1173,6 +1219,53 @@
|
|
|
1173
1219
|
}));
|
|
1174
1220
|
updateEnterHint();
|
|
1175
1221
|
|
|
1222
|
+
// ── 快捷栏模式(普通/专家)──────────────────────────────────
|
|
1223
|
+
// 两者只是勾选清单的快速预设:专家=全选、普通=六件套;预设之后每个按钮
|
|
1224
|
+
// 仍可单独勾选,显隐始终以清单为准(专家模式下也能单独隐藏某个按钮)。
|
|
1225
|
+
// skMode 仅记录当前勾选组合最接近哪个预设(高亮用);null=没选过 → 用服务端
|
|
1226
|
+
// 打包默认(/api/me 的 skMode,config.json skMode 字段 / env MYHI_SK_MODE)
|
|
1227
|
+
function skVisibleMap() {
|
|
1228
|
+
if (!settings.skVisible) {
|
|
1229
|
+
// 未自定义过:默认专家(全显)
|
|
1230
|
+
const all = {};
|
|
1231
|
+
Object.keys(SK_LABELS).forEach(k => { all[k] = true; });
|
|
1232
|
+
return all;
|
|
1233
|
+
}
|
|
1234
|
+
return { ...Object.keys(SK_LABELS).reduce((a, k) => (a[k] = true, a), {}), ...settings.skVisible };
|
|
1235
|
+
}
|
|
1236
|
+
function skPresetAll() {
|
|
1237
|
+
const v = {}; Object.keys(SK_LABELS).forEach(k => { v[k] = true; }); return v;
|
|
1238
|
+
}
|
|
1239
|
+
function skPresetNormal() {
|
|
1240
|
+
const v = {}; Object.keys(SK_LABELS).forEach(k => { v[k] = SK_NORMAL_PRESET.includes(k); }); return v;
|
|
1241
|
+
}
|
|
1242
|
+
function syncSkModeUI() {
|
|
1243
|
+
const vis = skVisibleMap();
|
|
1244
|
+
// 高亮:勾选组合恰好等于某预设才亮,自定义组合两个都不亮
|
|
1245
|
+
const keys = Object.keys(SK_LABELS);
|
|
1246
|
+
const isAll = keys.every(k => vis[k]);
|
|
1247
|
+
const isNormal = keys.every(k => vis[k] === SK_NORMAL_PRESET.includes(k));
|
|
1248
|
+
document.querySelectorAll('#seg-skmode .seg').forEach(b => b.classList.toggle('active',
|
|
1249
|
+
b.dataset.skmode === 'expert' ? isAll : (b.dataset.skmode === 'normal' ? isNormal : false)));
|
|
1250
|
+
const list = document.getElementById('sk-visible-list');
|
|
1251
|
+
if (!list) return;
|
|
1252
|
+
list.innerHTML = keys.map(k =>
|
|
1253
|
+
`<label style="display:flex;align-items:center;gap:0.25rem;font-size:0.75rem;color:var(--text2);cursor:pointer">
|
|
1254
|
+
<input type="checkbox" data-skkey="${k}" ${vis[k] ? 'checked' : ''}> ${SK_LABELS[k]}
|
|
1255
|
+
</label>`).join('');
|
|
1256
|
+
list.querySelectorAll('input[data-skkey]').forEach(cb => cb.addEventListener('change', () => {
|
|
1257
|
+
const v = skVisibleMap(); v[cb.dataset.skkey] = cb.checked;
|
|
1258
|
+
settings.skVisible = v; saveSettings(); syncSkModeUI(); updateShortcutBar();
|
|
1259
|
+
}));
|
|
1260
|
+
}
|
|
1261
|
+
document.querySelectorAll('#seg-skmode .seg').forEach(b => b.addEventListener('click', () => {
|
|
1262
|
+
settings.skVisible = b.dataset.skmode === 'expert' ? skPresetAll() : skPresetNormal();
|
|
1263
|
+
settings.skMode = b.dataset.skmode;
|
|
1264
|
+
saveSettings(); syncSkModeUI(); updateShortcutBar();
|
|
1265
|
+
}));
|
|
1266
|
+
updateEnterHint();
|
|
1267
|
+
syncSkModeUI();
|
|
1268
|
+
|
|
1176
1269
|
// 输出累积
|
|
1177
1270
|
let outputBuffer = '';
|
|
1178
1271
|
let outputTimer = null;
|
|
@@ -2008,10 +2101,10 @@
|
|
|
2008
2101
|
el.className = 'msg msg-thinking';
|
|
2009
2102
|
const header = document.createElement('div');
|
|
2010
2103
|
header.className = 'thinking-header';
|
|
2011
|
-
header.innerHTML = '<span class="arrow">▶</span> 思考过程';
|
|
2012
2104
|
const body = document.createElement('div');
|
|
2013
|
-
body.className = 'thinking-body';
|
|
2105
|
+
body.className = 'thinking-body' + (settings.thinkCollapsed !== false ? '' : ' open');
|
|
2014
2106
|
body.textContent = thinkings.join('\n\n');
|
|
2107
|
+
header.innerHTML = '<span class="arrow' + (settings.thinkCollapsed !== false ? '' : ' open') + '">▶</span> 思考过程';
|
|
2015
2108
|
header.onclick = () => { body.classList.toggle('open'); header.querySelector('.arrow').classList.toggle('open'); };
|
|
2016
2109
|
el.appendChild(header);
|
|
2017
2110
|
el.appendChild(body);
|
|
@@ -2100,6 +2193,7 @@
|
|
|
2100
2193
|
const body = document.createElement('div');
|
|
2101
2194
|
body.className = 'thinking-body';
|
|
2102
2195
|
if (_thinkingText) body.textContent = _thinkingText;
|
|
2196
|
+
if (!settings.thinkCollapsed) { body.classList.add('open'); header.querySelector('.arrow').classList.add('open'); }
|
|
2103
2197
|
|
|
2104
2198
|
header.onclick = () => {
|
|
2105
2199
|
body.classList.toggle('open');
|
|
@@ -2256,6 +2350,48 @@
|
|
|
2256
2350
|
document.getElementById('sk-shell').style.display = isPlainShell ? 'flex' : 'none';
|
|
2257
2351
|
document.getElementById('sk-claude-pty').style.display = isClaudePty ? 'flex' : 'none';
|
|
2258
2352
|
document.getElementById('sk-claude-agent').style.display = isAgent ? 'flex' : 'none';
|
|
2353
|
+
// 快捷栏显隐:始终以勾选清单为准(skVisibleMap);普通/专家只是快速预设
|
|
2354
|
+
const vis = skVisibleMap();
|
|
2355
|
+
document.querySelectorAll('#sk-claude-pty [data-sk], #sk-claude-agent [data-sk]').forEach(el => {
|
|
2356
|
+
el.style.display = vis[el.dataset.sk] ? '' : 'none';
|
|
2357
|
+
});
|
|
2358
|
+
// 输入框提示跟模式走:agent 会话提示 @ 引用(普通用户不知道这个用法)
|
|
2359
|
+
cmdInput.placeholder = isAgent ? '输入消息…(打 @ 可引用任务里的文件)' : '输入命令...';
|
|
2360
|
+
// 用户消息导航把手:仅 agent 会话显示(终端会话没有聊天流)
|
|
2361
|
+
document.getElementById('msg-nav-btn').style.display = isAgent ? 'flex' : 'none';
|
|
2362
|
+
if (!isAgent) document.getElementById('msg-nav-panel').style.display = 'none';
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2365
|
+
// ── 用户消息快速导航(借鉴 deepseek):点清单项跳到那条消息并高亮 ──
|
|
2366
|
+
window.toggleMsgNav = function() {
|
|
2367
|
+
const p = document.getElementById('msg-nav-panel');
|
|
2368
|
+
if (p.style.display === 'block') { p.style.display = 'none'; return; }
|
|
2369
|
+
renderMsgNav();
|
|
2370
|
+
p.style.display = 'block';
|
|
2371
|
+
}
|
|
2372
|
+
function renderMsgNav() {
|
|
2373
|
+
const list = document.getElementById('msg-nav-panel');
|
|
2374
|
+
const items = [...chatArea.querySelectorAll('.msg-input .bubble')]
|
|
2375
|
+
.map(b => ({ el: b.closest('.msg-input'), text: (b.textContent || '').trim() }))
|
|
2376
|
+
.filter(x => x.text);
|
|
2377
|
+
list.innerHTML = '';
|
|
2378
|
+
if (!items.length) {
|
|
2379
|
+
list.innerHTML = '<div style="padding:0.6rem;color:var(--muted);font-size:0.75rem">本页还没有我发的消息(更早的往上翻加载)</div>';
|
|
2380
|
+
return;
|
|
2381
|
+
}
|
|
2382
|
+
items.forEach((it) => {
|
|
2383
|
+
const row = document.createElement('div');
|
|
2384
|
+
row.className = 'mn-item';
|
|
2385
|
+
row.textContent = it.text.length > 46 ? it.text.slice(0, 46) + '…' : it.text;
|
|
2386
|
+
row.title = it.text;
|
|
2387
|
+
row.onclick = () => {
|
|
2388
|
+
it.el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
2389
|
+
it.el.classList.add('msg-flash');
|
|
2390
|
+
setTimeout(() => it.el.classList.remove('msg-flash'), 1800);
|
|
2391
|
+
document.getElementById('msg-nav-panel').style.display = 'none';
|
|
2392
|
+
};
|
|
2393
|
+
list.appendChild(row);
|
|
2394
|
+
});
|
|
2259
2395
|
}
|
|
2260
2396
|
|
|
2261
2397
|
// ── Socket.IO ──────────────────────────────────
|
|
@@ -3130,14 +3266,12 @@
|
|
|
3130
3266
|
try {
|
|
3131
3267
|
const r = await fetch(`/api/session/${SESSION_ID}/open-folder`, { method: 'POST' });
|
|
3132
3268
|
const d = await r.json();
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
const d = await r.json();
|
|
3140
|
-
addStatusMessage(d.ok ? '已尝试用 VS Code 打开(需服务端装了 code 命令且你在本机)' : ('打开失败: ' + (d.error || '')));
|
|
3269
|
+
if (d.ok) {
|
|
3270
|
+
closeFilePanel();
|
|
3271
|
+
addStatusMessage('📂 已在本机打开文件夹 ' + (d.cwd || ''));
|
|
3272
|
+
} else {
|
|
3273
|
+
addStatusMessage('打开失败: ' + (d.error || '') + '(若你是远程访问本面板,此按钮本就不生效)');
|
|
3274
|
+
}
|
|
3141
3275
|
} catch (e) { addStatusMessage('打开出错: ' + e.message); }
|
|
3142
3276
|
};
|
|
3143
3277
|
window.openFilePanel = function() {
|
|
@@ -3645,7 +3779,9 @@
|
|
|
3645
3779
|
document.getElementById('set-parse').checked = settings.parseOutput;
|
|
3646
3780
|
document.getElementById('set-thinking').checked = settings.showThinking;
|
|
3647
3781
|
document.getElementById('set-collapse').checked = settings.collapseTools;
|
|
3782
|
+
document.getElementById('set-thinkcollapse').checked = settings.thinkCollapsed !== false;
|
|
3648
3783
|
syncEnterModeUI();
|
|
3784
|
+
syncSkModeUI();
|
|
3649
3785
|
// 从服务端加载 ASR URL
|
|
3650
3786
|
fetch('/api/server-config').then(r => r.json()).then(d => {
|
|
3651
3787
|
document.getElementById('set-asr-url').value = d.asrUrl || '';
|
|
@@ -3654,6 +3790,7 @@
|
|
|
3654
3790
|
document.getElementById('set-parse').addEventListener('change', (e) => { settings.parseOutput = e.target.checked; saveSettings(); });
|
|
3655
3791
|
document.getElementById('set-thinking').addEventListener('change', (e) => { settings.showThinking = e.target.checked; saveSettings(); });
|
|
3656
3792
|
document.getElementById('set-collapse').addEventListener('change', (e) => { settings.collapseTools = e.target.checked; saveSettings(); });
|
|
3793
|
+
document.getElementById('set-thinkcollapse').addEventListener('change', (e) => { settings.thinkCollapsed = e.target.checked; saveSettings(); });
|
|
3657
3794
|
|
|
3658
3795
|
window.saveAsrUrl = async function() {
|
|
3659
3796
|
const url = document.getElementById('set-asr-url').value.trim();
|
package/dist/grid.html
CHANGED
|
@@ -244,10 +244,28 @@
|
|
|
244
244
|
let _tpGroup = localStorage.getItem('myhi_grid_group') || 'time';
|
|
245
245
|
let _tpTag = null;
|
|
246
246
|
function sessionTime(s){ return s.lastActivityAt || s.createdAt || 0; }
|
|
247
|
+
// 与首页卡片同款相对时间(1.3.173:任务选择条目信息量对齐首页)
|
|
248
|
+
function fmtRelTime(ms) {
|
|
249
|
+
if (!ms) return '';
|
|
250
|
+
const diff = Date.now() - ms;
|
|
251
|
+
if (diff < 60000) return '刚刚';
|
|
252
|
+
if (diff < 3600000) return Math.floor(diff / 60000) + '分钟前';
|
|
253
|
+
if (diff < 86400000) return Math.floor(diff / 3600000) + '小时前';
|
|
254
|
+
const d = new Date(ms);
|
|
255
|
+
const hm = d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
256
|
+
if (diff < 172800000) return '昨天 ' + hm;
|
|
257
|
+
return (d.getMonth() + 1) + '月' + d.getDate() + '日';
|
|
258
|
+
}
|
|
259
|
+
// 待发草稿数(角标),打开弹窗时拉一次
|
|
260
|
+
let _draftCounts = {};
|
|
261
|
+
function refreshDraftCounts() {
|
|
262
|
+
return fetch('/api/inbox').then(r => r.json()).then(d => { _draftCounts = d.counts || {}; }).catch(() => {});
|
|
263
|
+
}
|
|
247
264
|
window.openTaskPicker = function(pane) {
|
|
248
265
|
_pickTarget = pane;
|
|
249
266
|
document.getElementById('task-picker').style.display = '';
|
|
250
267
|
document.getElementById('tp-search').value = '';
|
|
268
|
+
refreshDraftCounts();
|
|
251
269
|
refreshSessions(false).then(renderPicker);
|
|
252
270
|
renderPicker();
|
|
253
271
|
setTimeout(() => document.getElementById('tp-search').focus(), 30);
|
|
@@ -270,9 +288,22 @@
|
|
|
270
288
|
const itemHtml = (s) => {
|
|
271
289
|
const cur = _pickTarget && _pickTarget._sid === sId(s);
|
|
272
290
|
const tags = (s.tags||[]).map(t=>`<span style="background:var(--surface2);color:var(--muted);border-radius:4px;font-size:0.62rem;padding:0.02rem 0.3rem">${esc(t)}</span>`).join(' ');
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
291
|
+
// 与首页卡片同款信息:时间/在线/控制中 + 首条消息 + 待发草稿 + 存活状态点
|
|
292
|
+
const meta = [fmtRelTime(sessionTime(s)), s.viewers > 0 ? `${s.viewers} 在线` : '', s.controlHolderName ? `${s.controlHolderName} 控制中` : ''].filter(Boolean).join(' · ');
|
|
293
|
+
const gist = s.firstUserText ? `<div style="font-size:0.66rem;color:var(--muted);margin-top:0.12rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">💬 ${esc(s.firstUserText)}</div>` : '';
|
|
294
|
+
const nDraft = _draftCounts[sId(s)] || 0;
|
|
295
|
+
const draft = nDraft ? `<div style="font-size:0.64rem;color:#d29922;margin-top:0.1rem">📥 ${nDraft} 条待发草稿</div>` : '';
|
|
296
|
+
return `<div data-id="${esc(sId(s))}" style="display:flex;align-items:center;gap:0.5rem;padding:0.45rem 0.5rem;border-radius:7px;cursor:pointer;border:1px solid ${cur?'var(--blue2)':'transparent'};background:${cur?'var(--surface2)':'transparent'}">
|
|
297
|
+
<div style="flex:1;min-width:0">
|
|
298
|
+
<div style="font-size:0.82rem;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(sName(s))} ${tags}</div>
|
|
299
|
+
<div style="font-size:0.66rem;color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(s.cwd||'(无目录)')}${meta ? ' · ' + esc(meta) : ''}</div>
|
|
300
|
+
${gist}
|
|
301
|
+
${draft}
|
|
302
|
+
</div>
|
|
303
|
+
<div style="flex-shrink:0;display:flex;flex-direction:column;align-items:center;gap:0.2rem">
|
|
304
|
+
<span title="${s.alive ? '存活' : '已结束'}" style="width:8px;height:8px;border-radius:50%;background:${s.alive?'var(--green,#2ea043)':'var(--red,#f85149)'};box-shadow:${s.alive?'0 0 5px var(--green,#2ea043)':'none'}"></span>
|
|
305
|
+
${s.viewers > 1 ? `<span style="font-size:0.6rem;color:var(--dim)">👁 ${s.viewers}</span>` : ''}
|
|
306
|
+
</div>
|
|
276
307
|
</div>`;
|
|
277
308
|
};
|
|
278
309
|
const header = (t) => `<div style="color:var(--muted);font-size:0.7rem;font-weight:600;margin:0.6rem 0.2rem 0.25rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(t)}</div>`;
|