@wendongfly/myhi 1.3.172 → 1.3.174
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 +220 -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,31 @@
|
|
|
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
|
+
opacity: 0; pointer-events: none; transition: opacity 0.2s; /* 桌面端:鼠标移进聊天区才现身 */
|
|
224
|
+
}
|
|
225
|
+
#msg-nav-btn.hovered { opacity: 1; pointer-events: auto; }
|
|
226
|
+
@media (pointer: coarse) { #msg-nav-btn { opacity: 1; pointer-events: auto; } } /* 触屏没有 hover,常显 */
|
|
227
|
+
#msg-nav-btn:hover { color: var(--text); border-color: var(--blue); }
|
|
228
|
+
#msg-nav-panel {
|
|
229
|
+
position: fixed; right: 0.5rem; bottom: 11.8rem; z-index: 60; display: none;
|
|
230
|
+
width: min(320px, 78vw); max-height: 55vh; overflow-y: auto;
|
|
231
|
+
background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
|
|
232
|
+
box-shadow: 0 8px 30px rgba(0,0,0,0.45); padding: 0.3rem;
|
|
233
|
+
}
|
|
234
|
+
#msg-nav-panel .mn-item {
|
|
235
|
+
padding: 0.42rem 0.55rem; border-radius: 7px; cursor: pointer;
|
|
236
|
+
font-size: 0.78rem; color: var(--text2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
237
|
+
}
|
|
238
|
+
#msg-nav-panel .mn-item:hover { background: var(--surface3); color: var(--text); }
|
|
239
|
+
.msg-flash { outline: 2px solid transparent; border-radius: 10px; animation: msgflash 1.8s; }
|
|
240
|
+
@keyframes msgflash { 0%, 55% { outline-color: var(--blue); } 100% { outline-color: transparent; } }
|
|
216
241
|
/* @ 文件引用补全浮层(挂在 body 上,fixed 定位在输入框上方) */
|
|
217
242
|
#mention-box {
|
|
218
243
|
position: fixed; z-index: 95; display: none;
|
|
@@ -262,11 +287,18 @@
|
|
|
262
287
|
/* ── 设置面板 ───────────────────────────── */
|
|
263
288
|
#settings-backdrop { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 60; }
|
|
264
289
|
#settings-backdrop.open { display: block; }
|
|
265
|
-
#settings-panel { position: fixed; top: 0; right: -
|
|
290
|
+
#settings-panel { position: fixed; top: 0; right: -360px; width: 340px; max-width: 92vw; height: 100%; background: var(--surface); border-left: 1px solid var(--border); z-index: 61; transition: right 0.25s ease; overflow-y: auto; padding: 1rem; }
|
|
266
291
|
#settings-panel.open { right: 0; }
|
|
267
|
-
.panel-header { display: flex; justify-content: space-between; align-items: center; margin-bottom:
|
|
292
|
+
.panel-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.6rem; font-size: 1rem; font-weight: 600; }
|
|
268
293
|
.panel-close { background: none; border: none; color: var(--muted); font-size: 1.2rem; cursor: pointer; }
|
|
269
|
-
|
|
294
|
+
/* 设置抽屉分类页签:一屏只看一类,不再长条堆叠(1.3.174 用户反馈太拥挤) */
|
|
295
|
+
#settings-tabs { display: flex; gap: 0.25rem; margin-bottom: 0.9rem; border-bottom: 1px solid var(--border); padding-bottom: 0.4rem; }
|
|
296
|
+
#settings-tabs .stab {
|
|
297
|
+
flex: 1; background: none; border: none; color: var(--muted);
|
|
298
|
+
font-size: 0.8rem; padding: 0.35rem 0.2rem; border-radius: 7px; cursor: pointer; white-space: nowrap;
|
|
299
|
+
}
|
|
300
|
+
#settings-tabs .stab.active { background: var(--surface3); color: var(--text); font-weight: 600; }
|
|
301
|
+
.setting-group { margin-bottom: 1rem; }
|
|
270
302
|
.setting-group h4 { font-size: 0.75rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 0.5rem; }
|
|
271
303
|
.setting-toggle { display: flex; justify-content: space-between; align-items: center; padding: 0.5rem 0; font-size: 0.85rem; cursor: pointer; }
|
|
272
304
|
.seg-row { display: flex; gap: 0.35rem; flex-wrap: wrap; }
|
|
@@ -497,34 +529,39 @@
|
|
|
497
529
|
<button class="sk" data-send="esc">Esc</button>
|
|
498
530
|
</div>
|
|
499
531
|
<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>
|
|
532
|
+
<button class="sk sk-claude" data-sk="model" onclick="openModelSheet()">模型</button>
|
|
533
|
+
<button class="sk sk-claude" data-sk="cost" data-cmd="/cost">费用</button>
|
|
534
|
+
<button class="sk sk-claude" data-sk="memory" data-cmd="/memory">记忆</button>
|
|
535
|
+
<button class="sk sk-claude" data-sk="resume" onclick="openResumeSheet()">恢复</button>
|
|
536
|
+
<button class="sk sk-claude" data-sk="compact" onclick="doCompact()">压缩</button>
|
|
537
|
+
<button class="sk sk-claude" data-sk="clear" data-cmd="/clear">清除</button>
|
|
538
|
+
<button class="sk sk-claude" data-sk="git" onclick="openGitSheet()" style="color:var(--green)">提交</button>
|
|
507
539
|
<button class="sk" data-send="ctrl-c">Ctrl+C</button>
|
|
508
540
|
<button class="sk" data-send="esc">Esc</button>
|
|
509
541
|
</div>
|
|
510
542
|
<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="
|
|
513
|
-
<button class="sk sk-claude"
|
|
514
|
-
<button class="sk sk-claude" onclick="
|
|
515
|
-
<button class="sk sk-claude" onclick="
|
|
516
|
-
<button class="sk sk-claude" onclick="
|
|
517
|
-
<button class="sk sk-claude"
|
|
518
|
-
<button class="sk sk-claude" onclick="
|
|
519
|
-
<button class="sk sk-claude" onclick="
|
|
520
|
-
<button class="sk sk-claude" onclick="
|
|
521
|
-
<button class="sk sk-claude" onclick="
|
|
522
|
-
<button class="sk sk-claude"
|
|
523
|
-
<
|
|
524
|
-
<span id="sk-
|
|
543
|
+
<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>
|
|
544
|
+
<button class="sk sk-claude" data-sk="folder" onclick="fpOpenFolder()" style="color:var(--blue2)">📂 文件夹</button>
|
|
545
|
+
<button class="sk sk-claude" data-sk="model" onclick="openModelSheet()">模型</button>
|
|
546
|
+
<button class="sk sk-claude" id="btn-plan" data-sk="plan" onclick="togglePlanMode()">计划</button>
|
|
547
|
+
<button class="sk sk-claude" data-sk="compact" onclick="doSlashCmd('/compact')">压缩</button>
|
|
548
|
+
<button class="sk sk-claude" data-sk="resume" onclick="openResumeSheet()">恢复</button>
|
|
549
|
+
<button class="sk sk-claude" data-sk="clear" onclick="doClear()">清除</button>
|
|
550
|
+
<button class="sk sk-claude" data-sk="secrets" onclick="openSecretsSheet()" style="color:var(--blue2)">🔑 密钥</button>
|
|
551
|
+
<button class="sk sk-claude" data-sk="guard" onclick="openGuardSheet()" style="color:var(--blue2)">🛡 护栏</button>
|
|
552
|
+
<button class="sk sk-claude" data-sk="followup" onclick="openFollowUpSheet()" style="color:var(--blue2)">🔄 跟进</button>
|
|
553
|
+
<button class="sk sk-claude" data-sk="git" onclick="openGitSheet()" style="color:var(--green)">提交</button>
|
|
554
|
+
<button class="sk sk-claude" data-sk="memory" onclick="showMemory()">记忆</button>
|
|
555
|
+
<button class="sk sk-claude" data-sk="skills" onclick="openSkillSheet()" style="color:var(--purple2)">技能</button>
|
|
556
|
+
<span id="sk-builtin-skills" data-sk="skills"></span>
|
|
557
|
+
<span id="sk-custom-skills" data-sk="skills"></span>
|
|
525
558
|
</div>
|
|
526
559
|
</div>
|
|
527
560
|
|
|
561
|
+
<!-- 用户消息快速导航:右缘把手 + 消息清单(点一条跳到那条) -->
|
|
562
|
+
<button id="msg-nav-btn" onclick="toggleMsgNav()" title="定位我发过的消息">☰</button>
|
|
563
|
+
<div id="msg-nav-panel"></div>
|
|
564
|
+
|
|
528
565
|
<!-- 文件管理面板 -->
|
|
529
566
|
<div id="file-panel-backdrop" onclick="closeFilePanel()"></div>
|
|
530
567
|
<div id="file-panel">
|
|
@@ -559,8 +596,13 @@
|
|
|
559
596
|
<div id="settings-backdrop" onclick="closeSettings()"></div>
|
|
560
597
|
<div id="settings-panel">
|
|
561
598
|
<div class="panel-header"><span>设置</span><button class="panel-close" onclick="closeSettings()">✕</button></div>
|
|
562
|
-
<div
|
|
563
|
-
<
|
|
599
|
+
<div id="settings-tabs">
|
|
600
|
+
<button class="stab active" data-tab="look">外观</button>
|
|
601
|
+
<button class="stab" data-tab="chat">聊天</button>
|
|
602
|
+
<button class="stab" data-tab="bar">快捷栏</button>
|
|
603
|
+
<button class="stab" data-tab="more">更多</button>
|
|
604
|
+
</div>
|
|
605
|
+
<div class="setting-group" data-stab="look">
|
|
564
606
|
<div style="font-size:0.8rem;color:var(--text2);margin:0.1rem 0 0.35rem">主题</div>
|
|
565
607
|
<div id="seg-theme" class="seg-row">
|
|
566
608
|
<button class="seg" data-theme="system">跟随系统</button>
|
|
@@ -575,13 +617,14 @@
|
|
|
575
617
|
<button class="seg" data-font="large">大</button>
|
|
576
618
|
</div>
|
|
577
619
|
</div>
|
|
578
|
-
<div class="setting-group">
|
|
620
|
+
<div class="setting-group" data-stab="chat" style="display:none">
|
|
579
621
|
<h4>显示</h4>
|
|
580
622
|
<label class="setting-toggle"><span>解析终端输出</span><input type="checkbox" id="set-parse" checked></label>
|
|
581
623
|
<label class="setting-toggle"><span>显示思考过程</span><input type="checkbox" id="set-thinking" checked></label>
|
|
582
624
|
<label class="setting-toggle"><span>工具调用默认折叠</span><input type="checkbox" id="set-collapse" checked></label>
|
|
625
|
+
<label class="setting-toggle"><span>思考过程默认折叠</span><input type="checkbox" id="set-thinkcollapse" checked></label>
|
|
583
626
|
</div>
|
|
584
|
-
<div class="setting-group">
|
|
627
|
+
<div class="setting-group" data-stab="chat" style="display:none">
|
|
585
628
|
<h4>输入</h4>
|
|
586
629
|
<div style="font-size:0.8rem;color:var(--text2);margin:0.1rem 0 0.35rem">回车键</div>
|
|
587
630
|
<div id="seg-enter" class="seg-row">
|
|
@@ -593,7 +636,17 @@
|
|
|
593
636
|
自动:电脑回车发送 / Shift+回车换行;手机回车换行、用按钮发送。<br>发送:回车总是发送(换行用 Shift+回车)。换行:回车总是换行(发送用 Ctrl/⌘+回车 或按钮)。
|
|
594
637
|
</div>
|
|
595
638
|
</div>
|
|
596
|
-
<div class="setting-group">
|
|
639
|
+
<div class="setting-group" data-stab="bar" style="display:none">
|
|
640
|
+
<div id="seg-skmode" class="seg-row">
|
|
641
|
+
<button class="seg" data-skmode="normal">普通模式</button>
|
|
642
|
+
<button class="seg" data-skmode="expert">专家模式</button>
|
|
643
|
+
</div>
|
|
644
|
+
<div style="font-size:0.72rem;color:var(--muted);margin-top:0.35rem;line-height:1.5">
|
|
645
|
+
专家 = 显示全部快捷按钮。普通 = 只显示下面勾选的,给简单用户减负。两种模式下都可以再单独勾选。
|
|
646
|
+
</div>
|
|
647
|
+
<div id="sk-visible-list" style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:0.15rem 0.6rem;margin-top:0.5rem"></div>
|
|
648
|
+
</div>
|
|
649
|
+
<div class="setting-group" data-stab="more" style="display:none">
|
|
597
650
|
<h4>语音识别 (ASR)</h4>
|
|
598
651
|
<div style="font-size:0.72rem;color:var(--muted);margin-bottom:0.35rem">SenseVoice 或兼容服务的地址,留空表示禁用语音</div>
|
|
599
652
|
<div style="display:flex;gap:0.4rem">
|
|
@@ -602,7 +655,7 @@
|
|
|
602
655
|
</div>
|
|
603
656
|
<div id="set-asr-status" style="font-size:0.72rem;min-height:1.2em;margin-top:0.25rem;color:var(--muted)"></div>
|
|
604
657
|
</div>
|
|
605
|
-
<div class="setting-group">
|
|
658
|
+
<div class="setting-group" data-stab="more" style="display:none">
|
|
606
659
|
<h4>服务管理</h4>
|
|
607
660
|
<button onclick="restartMyhi()" style="width:100%;padding:0.6rem;background:var(--red2);color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:0.85rem;font-weight:600;margin-top:0.1rem">重启 myhi</button>
|
|
608
661
|
</div>
|
|
@@ -1038,7 +1091,13 @@
|
|
|
1038
1091
|
const ansi = new AnsiUp();
|
|
1039
1092
|
ansi.use_classes = false;
|
|
1040
1093
|
|
|
1041
|
-
fetch('/api/me').then(r => r.json()).
|
|
1094
|
+
fetch('/api/me').then(r => r.json()).then(data => {
|
|
1095
|
+
// 打包默认快捷栏预设:用户没自定义过勾选(skVisible 为 null)时采用服务端 config.json 的 skMode
|
|
1096
|
+
if (data?.skMode && settings.skVisible == null) {
|
|
1097
|
+
settings.skVisible = data.skMode === 'normal' ? skPresetNormal() : skPresetAll();
|
|
1098
|
+
settings.skMode = data.skMode; saveSettings(); syncSkModeUI(); updateShortcutBar();
|
|
1099
|
+
}
|
|
1100
|
+
}).catch(() => {});
|
|
1042
1101
|
window.showUsage = async () => {
|
|
1043
1102
|
try {
|
|
1044
1103
|
const res = await fetch('/api/usage');
|
|
@@ -1124,7 +1183,10 @@
|
|
|
1124
1183
|
let workState = 'idle';
|
|
1125
1184
|
|
|
1126
1185
|
// 设置
|
|
1127
|
-
|
|
1186
|
+
// 快捷栏:skVisible 逐按钮勾选(唯一显隐依据,null=默认全显);skMode 仅记录最接近的预设(高亮用)
|
|
1187
|
+
const SK_LABELS = { draft:'📥 草稿', folder:'📂 文件夹', model:'模型', plan:'计划', compact:'压缩', resume:'恢复', clear:'清除', secrets:'🔑 密钥', guard:'🛡 护栏', followup:'🔄 跟进', git:'提交', memory:'记忆', skills:'技能', cost:'费用' };
|
|
1188
|
+
const SK_NORMAL_PRESET = ['draft', 'folder', 'plan', 'clear', 'guard', 'memory', 'skills'];
|
|
1189
|
+
const DEFAULT_SETTINGS = { parseOutput: true, showThinking: true, collapseTools: true, thinkCollapsed: true, enterMode: 'auto', skMode: null, skVisible: null };
|
|
1128
1190
|
let settings = loadSettings();
|
|
1129
1191
|
|
|
1130
1192
|
function loadSettings() { try { return { ...DEFAULT_SETTINGS, ...JSON.parse(localStorage.getItem('myhi_chat_settings')) }; } catch { return { ...DEFAULT_SETTINGS }; } }
|
|
@@ -1172,6 +1234,53 @@
|
|
|
1172
1234
|
}));
|
|
1173
1235
|
updateEnterHint();
|
|
1174
1236
|
|
|
1237
|
+
// ── 快捷栏模式(普通/专家)──────────────────────────────────
|
|
1238
|
+
// 两者只是勾选清单的快速预设:专家=全选、普通=六件套;预设之后每个按钮
|
|
1239
|
+
// 仍可单独勾选,显隐始终以清单为准(专家模式下也能单独隐藏某个按钮)。
|
|
1240
|
+
// skMode 仅记录当前勾选组合最接近哪个预设(高亮用);null=没选过 → 用服务端
|
|
1241
|
+
// 打包默认(/api/me 的 skMode,config.json skMode 字段 / env MYHI_SK_MODE)
|
|
1242
|
+
function skVisibleMap() {
|
|
1243
|
+
if (!settings.skVisible) {
|
|
1244
|
+
// 未自定义过:默认专家(全显)
|
|
1245
|
+
const all = {};
|
|
1246
|
+
Object.keys(SK_LABELS).forEach(k => { all[k] = true; });
|
|
1247
|
+
return all;
|
|
1248
|
+
}
|
|
1249
|
+
return { ...Object.keys(SK_LABELS).reduce((a, k) => (a[k] = true, a), {}), ...settings.skVisible };
|
|
1250
|
+
}
|
|
1251
|
+
function skPresetAll() {
|
|
1252
|
+
const v = {}; Object.keys(SK_LABELS).forEach(k => { v[k] = true; }); return v;
|
|
1253
|
+
}
|
|
1254
|
+
function skPresetNormal() {
|
|
1255
|
+
const v = {}; Object.keys(SK_LABELS).forEach(k => { v[k] = SK_NORMAL_PRESET.includes(k); }); return v;
|
|
1256
|
+
}
|
|
1257
|
+
function syncSkModeUI() {
|
|
1258
|
+
const vis = skVisibleMap();
|
|
1259
|
+
// 高亮:勾选组合恰好等于某预设才亮,自定义组合两个都不亮
|
|
1260
|
+
const keys = Object.keys(SK_LABELS);
|
|
1261
|
+
const isAll = keys.every(k => vis[k]);
|
|
1262
|
+
const isNormal = keys.every(k => vis[k] === SK_NORMAL_PRESET.includes(k));
|
|
1263
|
+
document.querySelectorAll('#seg-skmode .seg').forEach(b => b.classList.toggle('active',
|
|
1264
|
+
b.dataset.skmode === 'expert' ? isAll : (b.dataset.skmode === 'normal' ? isNormal : false)));
|
|
1265
|
+
const list = document.getElementById('sk-visible-list');
|
|
1266
|
+
if (!list) return;
|
|
1267
|
+
list.innerHTML = keys.map(k =>
|
|
1268
|
+
`<label style="display:flex;align-items:center;gap:0.25rem;font-size:0.75rem;color:var(--text2);cursor:pointer">
|
|
1269
|
+
<input type="checkbox" data-skkey="${k}" ${vis[k] ? 'checked' : ''}> ${SK_LABELS[k]}
|
|
1270
|
+
</label>`).join('');
|
|
1271
|
+
list.querySelectorAll('input[data-skkey]').forEach(cb => cb.addEventListener('change', () => {
|
|
1272
|
+
const v = skVisibleMap(); v[cb.dataset.skkey] = cb.checked;
|
|
1273
|
+
settings.skVisible = v; saveSettings(); syncSkModeUI(); updateShortcutBar();
|
|
1274
|
+
}));
|
|
1275
|
+
}
|
|
1276
|
+
document.querySelectorAll('#seg-skmode .seg').forEach(b => b.addEventListener('click', () => {
|
|
1277
|
+
settings.skVisible = b.dataset.skmode === 'expert' ? skPresetAll() : skPresetNormal();
|
|
1278
|
+
settings.skMode = b.dataset.skmode;
|
|
1279
|
+
saveSettings(); syncSkModeUI(); updateShortcutBar();
|
|
1280
|
+
}));
|
|
1281
|
+
updateEnterHint();
|
|
1282
|
+
syncSkModeUI();
|
|
1283
|
+
|
|
1175
1284
|
// 输出累积
|
|
1176
1285
|
let outputBuffer = '';
|
|
1177
1286
|
let outputTimer = null;
|
|
@@ -2007,10 +2116,10 @@
|
|
|
2007
2116
|
el.className = 'msg msg-thinking';
|
|
2008
2117
|
const header = document.createElement('div');
|
|
2009
2118
|
header.className = 'thinking-header';
|
|
2010
|
-
header.innerHTML = '<span class="arrow">▶</span> 思考过程';
|
|
2011
2119
|
const body = document.createElement('div');
|
|
2012
|
-
body.className = 'thinking-body';
|
|
2120
|
+
body.className = 'thinking-body' + (settings.thinkCollapsed !== false ? '' : ' open');
|
|
2013
2121
|
body.textContent = thinkings.join('\n\n');
|
|
2122
|
+
header.innerHTML = '<span class="arrow' + (settings.thinkCollapsed !== false ? '' : ' open') + '">▶</span> 思考过程';
|
|
2014
2123
|
header.onclick = () => { body.classList.toggle('open'); header.querySelector('.arrow').classList.toggle('open'); };
|
|
2015
2124
|
el.appendChild(header);
|
|
2016
2125
|
el.appendChild(body);
|
|
@@ -2099,6 +2208,7 @@
|
|
|
2099
2208
|
const body = document.createElement('div');
|
|
2100
2209
|
body.className = 'thinking-body';
|
|
2101
2210
|
if (_thinkingText) body.textContent = _thinkingText;
|
|
2211
|
+
if (!settings.thinkCollapsed) { body.classList.add('open'); header.querySelector('.arrow').classList.add('open'); }
|
|
2102
2212
|
|
|
2103
2213
|
header.onclick = () => {
|
|
2104
2214
|
body.classList.toggle('open');
|
|
@@ -2255,6 +2365,69 @@
|
|
|
2255
2365
|
document.getElementById('sk-shell').style.display = isPlainShell ? 'flex' : 'none';
|
|
2256
2366
|
document.getElementById('sk-claude-pty').style.display = isClaudePty ? 'flex' : 'none';
|
|
2257
2367
|
document.getElementById('sk-claude-agent').style.display = isAgent ? 'flex' : 'none';
|
|
2368
|
+
// 快捷栏显隐:始终以勾选清单为准(skVisibleMap);普通/专家只是快速预设
|
|
2369
|
+
const vis = skVisibleMap();
|
|
2370
|
+
document.querySelectorAll('#sk-claude-pty [data-sk], #sk-claude-agent [data-sk]').forEach(el => {
|
|
2371
|
+
el.style.display = vis[el.dataset.sk] ? '' : 'none';
|
|
2372
|
+
});
|
|
2373
|
+
// 输入框提示跟模式走:agent 会话提示 @ 引用(普通用户不知道这个用法)
|
|
2374
|
+
cmdInput.placeholder = isAgent ? '输入消息…(打 @ 可引用任务里的文件)' : '输入命令...';
|
|
2375
|
+
// 用户消息导航把手:仅 agent 会话显示(终端会话没有聊天流)
|
|
2376
|
+
document.getElementById('msg-nav-btn').style.display = isAgent ? 'flex' : 'none';
|
|
2377
|
+
if (!isAgent) document.getElementById('msg-nav-panel').style.display = 'none';
|
|
2378
|
+
}
|
|
2379
|
+
|
|
2380
|
+
// ── 用户消息快速导航(借鉴 deepseek):点清单项跳到那条消息并高亮 ──
|
|
2381
|
+
window.toggleMsgNav = function() {
|
|
2382
|
+
const p = document.getElementById('msg-nav-panel');
|
|
2383
|
+
if (p.style.display === 'block') { p.style.display = 'none'; return; }
|
|
2384
|
+
renderMsgNav();
|
|
2385
|
+
p.style.display = 'block';
|
|
2386
|
+
}
|
|
2387
|
+
// 桌面端:鼠标移进聊天区/把手/清单 → 显示把手;离开 → 延迟 350ms 收起
|
|
2388
|
+
// (清单开着时不收把手,方便看着清单滚动定位)。触屏设备无常显(见 CSS)。
|
|
2389
|
+
let _navHideTimer = null;
|
|
2390
|
+
function navShow() { clearTimeout(_navHideTimer); document.getElementById('msg-nav-btn').classList.add('hovered'); }
|
|
2391
|
+
function navHideLater() {
|
|
2392
|
+
clearTimeout(_navHideTimer);
|
|
2393
|
+
_navHideTimer = setTimeout(() => {
|
|
2394
|
+
if (document.getElementById('msg-nav-panel').style.display === 'block') return;
|
|
2395
|
+
document.getElementById('msg-nav-btn').classList.remove('hovered');
|
|
2396
|
+
}, 350);
|
|
2397
|
+
}
|
|
2398
|
+
['mouseenter'].forEach(ev => {
|
|
2399
|
+
chatArea.addEventListener(ev, navShow);
|
|
2400
|
+
document.getElementById('msg-nav-btn').addEventListener(ev, navShow);
|
|
2401
|
+
document.getElementById('msg-nav-panel').addEventListener(ev, navShow);
|
|
2402
|
+
});
|
|
2403
|
+
['mouseleave'].forEach(ev => {
|
|
2404
|
+
chatArea.addEventListener(ev, navHideLater);
|
|
2405
|
+
document.getElementById('msg-nav-btn').addEventListener(ev, navHideLater);
|
|
2406
|
+
document.getElementById('msg-nav-panel').addEventListener(ev, navHideLater);
|
|
2407
|
+
});
|
|
2408
|
+
function renderMsgNav() {
|
|
2409
|
+
const list = document.getElementById('msg-nav-panel');
|
|
2410
|
+
const items = [...chatArea.querySelectorAll('.msg-input .bubble')]
|
|
2411
|
+
.map(b => ({ el: b.closest('.msg-input'), text: (b.textContent || '').trim() }))
|
|
2412
|
+
.filter(x => x.text);
|
|
2413
|
+
list.innerHTML = '';
|
|
2414
|
+
if (!items.length) {
|
|
2415
|
+
list.innerHTML = '<div style="padding:0.6rem;color:var(--muted);font-size:0.75rem">本页还没有我发的消息(更早的往上翻加载)</div>';
|
|
2416
|
+
return;
|
|
2417
|
+
}
|
|
2418
|
+
items.forEach((it) => {
|
|
2419
|
+
const row = document.createElement('div');
|
|
2420
|
+
row.className = 'mn-item';
|
|
2421
|
+
row.textContent = it.text.length > 46 ? it.text.slice(0, 46) + '…' : it.text;
|
|
2422
|
+
row.title = it.text;
|
|
2423
|
+
row.onclick = () => {
|
|
2424
|
+
it.el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
2425
|
+
it.el.classList.add('msg-flash');
|
|
2426
|
+
setTimeout(() => it.el.classList.remove('msg-flash'), 1800);
|
|
2427
|
+
document.getElementById('msg-nav-panel').style.display = 'none';
|
|
2428
|
+
};
|
|
2429
|
+
list.appendChild(row);
|
|
2430
|
+
});
|
|
2258
2431
|
}
|
|
2259
2432
|
|
|
2260
2433
|
// ── Socket.IO ──────────────────────────────────
|
|
@@ -3113,7 +3286,16 @@
|
|
|
3113
3286
|
}
|
|
3114
3287
|
|
|
3115
3288
|
// ── 设置面板 ──────────────────────────────────
|
|
3116
|
-
window.openSettings = function() { document.getElementById('settings-backdrop').classList.add('open'); document.getElementById('settings-panel').classList.add('open'); syncSettingsUI(); };
|
|
3289
|
+
window.openSettings = function() { document.getElementById('settings-backdrop').classList.add('open'); document.getElementById('settings-panel').classList.add('open'); syncSettingsUI(); applySettingsTab(localStorage.getItem('myhi_settings_tab') || 'look'); };
|
|
3290
|
+
// 设置抽屉分类页签(1.3.174:一屏只看一类,不再长条堆叠)
|
|
3291
|
+
function applySettingsTab(tab) {
|
|
3292
|
+
document.querySelectorAll('#settings-tabs .stab').forEach(b => b.classList.toggle('active', b.dataset.tab === tab));
|
|
3293
|
+
document.querySelectorAll('#settings-panel [data-stab]').forEach(g => { g.style.display = g.dataset.stab === tab ? '' : 'none'; });
|
|
3294
|
+
}
|
|
3295
|
+
document.querySelectorAll('#settings-tabs .stab').forEach(b => b.addEventListener('click', () => {
|
|
3296
|
+
localStorage.setItem('myhi_settings_tab', b.dataset.tab);
|
|
3297
|
+
applySettingsTab(b.dataset.tab);
|
|
3298
|
+
}));
|
|
3117
3299
|
window.closeSettings = function() { document.getElementById('settings-backdrop').classList.remove('open'); document.getElementById('settings-panel').classList.remove('open'); };
|
|
3118
3300
|
|
|
3119
3301
|
// ── 文件管理 ──────────────────────────────────
|
|
@@ -3642,7 +3824,9 @@
|
|
|
3642
3824
|
document.getElementById('set-parse').checked = settings.parseOutput;
|
|
3643
3825
|
document.getElementById('set-thinking').checked = settings.showThinking;
|
|
3644
3826
|
document.getElementById('set-collapse').checked = settings.collapseTools;
|
|
3827
|
+
document.getElementById('set-thinkcollapse').checked = settings.thinkCollapsed !== false;
|
|
3645
3828
|
syncEnterModeUI();
|
|
3829
|
+
syncSkModeUI();
|
|
3646
3830
|
// 从服务端加载 ASR URL
|
|
3647
3831
|
fetch('/api/server-config').then(r => r.json()).then(d => {
|
|
3648
3832
|
document.getElementById('set-asr-url').value = d.asrUrl || '';
|
|
@@ -3651,6 +3835,7 @@
|
|
|
3651
3835
|
document.getElementById('set-parse').addEventListener('change', (e) => { settings.parseOutput = e.target.checked; saveSettings(); });
|
|
3652
3836
|
document.getElementById('set-thinking').addEventListener('change', (e) => { settings.showThinking = e.target.checked; saveSettings(); });
|
|
3653
3837
|
document.getElementById('set-collapse').addEventListener('change', (e) => { settings.collapseTools = e.target.checked; saveSettings(); });
|
|
3838
|
+
document.getElementById('set-thinkcollapse').addEventListener('change', (e) => { settings.thinkCollapsed = e.target.checked; saveSettings(); });
|
|
3654
3839
|
|
|
3655
3840
|
window.saveAsrUrl = async function() {
|
|
3656
3841
|
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>`;
|