@wendongfly/myhi 1.3.190 → 1.3.191
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/admin.html +114 -20
- package/dist/chat.html +76 -60
- package/dist/index.html +134 -10
- package/dist/index.js +4 -4
- package/dist/index.min.js +9 -8
- package/dist/lib/machine.js +5 -2
- package/dist/lib/prefs.js +51 -0
- package/package.json +1 -1
package/dist/admin.html
CHANGED
|
@@ -41,6 +41,20 @@
|
|
|
41
41
|
.inp-row { display: flex; gap: 0.5rem; align-items: center; margin-bottom: 0.5rem; }
|
|
42
42
|
.inp-row .inp { flex: 1; }
|
|
43
43
|
.btn-sm { padding: 0.5rem 1rem; background: #7c3aed; color: #fff; border: none; border-radius: 8px; cursor: pointer; font-size: 0.85rem; white-space: nowrap; }
|
|
44
|
+
.btn-sm.ghost { background: transparent; color: #b1bac4; border: 1px solid rgba(255,255,255,0.15); }
|
|
45
|
+
.mode-status { font-size: 0.85rem; line-height: 1.6; padding: 0.6rem 0.75rem; border-radius: 10px; background: rgba(124,58,237,0.1); border: 1px solid rgba(124,58,237,0.25); margin-bottom: 0.5rem; }
|
|
46
|
+
.mode-status b { color: #e6edf3; }
|
|
47
|
+
.mode-status small { display: block; color: #8b949e; font-size: 0.75rem; }
|
|
48
|
+
.sw { position: relative; width: 38px; height: 22px; flex-shrink: 0; }
|
|
49
|
+
.sw input { opacity: 0; width: 0; height: 0; }
|
|
50
|
+
.sw span { position: absolute; inset: 0; background: #30363d; border-radius: 11px; cursor: pointer; transition: 0.2s; }
|
|
51
|
+
.sw span::before { content: ''; position: absolute; width: 16px; height: 16px; left: 3px; top: 3px; background: #fff; border-radius: 50%; transition: 0.2s; }
|
|
52
|
+
.sw input:checked + span { background: #7c3aed; }
|
|
53
|
+
.sw input:checked + span::before { transform: translateX(16px); }
|
|
54
|
+
.dir-pick { border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; max-height: 220px; overflow-y: auto; margin-bottom: 0.5rem; font-size: 0.8rem; }
|
|
55
|
+
.dir-pick div { padding: 0.4rem 0.6rem; cursor: pointer; color: #b1bac4; }
|
|
56
|
+
.dir-pick div:hover { background: rgba(255,255,255,0.05); }
|
|
57
|
+
.dir-pick .cur { color: #8b949e; font-size: 0.72rem; cursor: default; border-bottom: 1px solid rgba(255,255,255,0.06); }
|
|
44
58
|
.msg { font-size: 0.8rem; margin-top: 0.3rem; min-height: 1.2em; }
|
|
45
59
|
.msg-ok { color: #3fb950; }
|
|
46
60
|
.msg-err { color: #f85149; }
|
|
@@ -101,21 +115,33 @@
|
|
|
101
115
|
</div>
|
|
102
116
|
|
|
103
117
|
<!-- 用户管理 -->
|
|
104
|
-
<div class="card">
|
|
105
|
-
<h3
|
|
118
|
+
<div class="card" id="mode">
|
|
119
|
+
<h3>使用模式</h3>
|
|
120
|
+
<div id="mode-status" class="mode-status"></div>
|
|
106
121
|
<div id="user-list"></div>
|
|
107
|
-
<div
|
|
108
|
-
<
|
|
122
|
+
<div id="excl-row" class="row" style="display:none">
|
|
123
|
+
<span>独占模式<br><small style="color:#6e7681">同一时间只允许一个人登录,别人登录会被挡住</small></span>
|
|
124
|
+
<label class="sw"><input type="checkbox" id="excl-toggle" onchange="setExclusive(this.checked)"><span></span></label>
|
|
125
|
+
</div>
|
|
126
|
+
<button class="btn-sm" id="add-open" onclick="openAddMember()" style="margin-top:0.6rem;width:100%"></button>
|
|
127
|
+
<div id="add-member" style="display:none;margin-top:0.7rem">
|
|
128
|
+
<div class="inp-row"><input class="inp" id="u-name" placeholder="成员名称(如:张三)"></div>
|
|
109
129
|
<div class="inp-row">
|
|
110
|
-
<input class="inp" id="u-pwd" placeholder="
|
|
111
|
-
<
|
|
130
|
+
<input class="inp" id="u-pwd" placeholder="登录密码(至少 4 位)" autocomplete="off">
|
|
131
|
+
<button class="btn-sm ghost" onclick="genPwd()">随机生成</button>
|
|
112
132
|
</div>
|
|
113
133
|
<div class="inp-row">
|
|
114
|
-
<input class="inp" id="u-dir" placeholder="
|
|
115
|
-
<button class="btn-sm" onclick="
|
|
134
|
+
<input class="inp" id="u-dir" placeholder="工作目录(留空 = 不限制)">
|
|
135
|
+
<button class="btn-sm ghost" onclick="pickDir()">浏览…</button>
|
|
136
|
+
</div>
|
|
137
|
+
<div id="dir-pick" style="display:none" class="dir-pick"></div>
|
|
138
|
+
<div class="inp-row" style="margin-top:0.2rem">
|
|
139
|
+
<button class="btn-sm ghost" onclick="closeAddMember()" style="flex:1">取消</button>
|
|
140
|
+
<button class="btn-sm" onclick="addUser()" style="flex:2">添加</button>
|
|
116
141
|
</div>
|
|
117
|
-
<div class="msg" id="u-msg"></div>
|
|
118
142
|
</div>
|
|
143
|
+
<div class="msg" id="u-msg"></div>
|
|
144
|
+
<div id="mode-foot" style="font-size:0.72rem;color:#6e7681;margin-top:0.5rem;line-height:1.5"></div>
|
|
119
145
|
</div>
|
|
120
146
|
|
|
121
147
|
<!-- 修改管理密码 -->
|
|
@@ -369,7 +395,7 @@ async function refresh() {
|
|
|
369
395
|
}
|
|
370
396
|
|
|
371
397
|
// 用户列表
|
|
372
|
-
renderUsers(d.users || []);
|
|
398
|
+
renderUsers(d.users || [], d.exclusive);
|
|
373
399
|
|
|
374
400
|
// 运行时长
|
|
375
401
|
if (d.uptime) {
|
|
@@ -379,20 +405,70 @@ async function refresh() {
|
|
|
379
405
|
} catch {}
|
|
380
406
|
}
|
|
381
407
|
|
|
382
|
-
|
|
408
|
+
// 使用模式:有成员 = 多用户,没有 = 单用户(默认)
|
|
409
|
+
let _users = [];
|
|
410
|
+
function renderUsers(users, exclusive) {
|
|
411
|
+
_users = users;
|
|
412
|
+
const multi = users.length > 0;
|
|
413
|
+
document.getElementById('mode-status').innerHTML = multi
|
|
414
|
+
? `<b>当前:多用户模式 · ${users.length} 个成员</b><small>成员用各自密码登录,可以升级 / 重启,不能改全局设置;你用管理密码或二维码登录,权限不变。</small>`
|
|
415
|
+
: `<b>当前:单用户模式</b><small>只有你在用,所有功能都能用。需要给别人用时再添加成员。</small>`;
|
|
416
|
+
document.getElementById('add-open').textContent = multi ? '+ 添加成员' : '+ 添加成员,开启多用户模式';
|
|
417
|
+
document.getElementById('excl-row').style.display = multi ? '' : 'none';
|
|
418
|
+
document.getElementById('excl-toggle').checked = !!exclusive;
|
|
419
|
+
document.getElementById('mode-foot').textContent = multi ? '删除全部成员即回到单用户模式。删除成员或重置密码后,他当前的登录会立即失效。' : '';
|
|
383
420
|
const el = document.getElementById('user-list');
|
|
384
|
-
if (!users.length) { el.innerHTML = '<div class="row" style="color:#6e7681">暂无用户</div>'; return; }
|
|
385
421
|
el.innerHTML = users.map(u => `
|
|
386
422
|
<div class="row">
|
|
387
|
-
<span><b style="color:#e6edf3">${esc(u.name)}</b> <span style="color:#6e7681;font-size:0.
|
|
423
|
+
<span><b style="color:#e6edf3">${esc(u.name)}</b> <span style="color:#6e7681;font-size:0.72rem">${u.dir ? '📁 ' + esc(u.dir) : '不限目录'}</span></span>
|
|
388
424
|
<div class="row-actions">
|
|
389
|
-
<button onclick="editUserPwd('${esc(u.name)}')"
|
|
425
|
+
<button onclick="editUserPwd('${esc(u.name)}')">重置密码</button>
|
|
390
426
|
<button class="del" onclick="delUser('${esc(u.name)}')">删除</button>
|
|
391
427
|
</div>
|
|
392
428
|
</div>
|
|
393
429
|
`).join('');
|
|
394
430
|
}
|
|
395
431
|
|
|
432
|
+
function openAddMember() {
|
|
433
|
+
document.getElementById('add-member').style.display = '';
|
|
434
|
+
document.getElementById('add-open').style.display = 'none';
|
|
435
|
+
document.getElementById('u-name').focus();
|
|
436
|
+
}
|
|
437
|
+
function closeAddMember() {
|
|
438
|
+
document.getElementById('add-member').style.display = 'none';
|
|
439
|
+
document.getElementById('add-open').style.display = '';
|
|
440
|
+
document.getElementById('dir-pick').style.display = 'none';
|
|
441
|
+
}
|
|
442
|
+
function genPwd() {
|
|
443
|
+
const cs = 'abcdefghjkmnpqrstuvwxyz23456789';
|
|
444
|
+
const a = crypto.getRandomValues(new Uint32Array(8));
|
|
445
|
+
document.getElementById('u-pwd').value = [...a].map(n => cs[n % cs.length]).join('');
|
|
446
|
+
}
|
|
447
|
+
// 目录点选:复用 /api/dirs(主站以管理员身份登录时可用;不行就手动输入)
|
|
448
|
+
async function pickDir(path) {
|
|
449
|
+
const box = document.getElementById('dir-pick');
|
|
450
|
+
box.style.display = '';
|
|
451
|
+
try {
|
|
452
|
+
const r = await fetch('/api/dirs' + (path ? '?path=' + encodeURIComponent(path) : ''));
|
|
453
|
+
const d = await r.json();
|
|
454
|
+
if (!r.ok) throw new Error(d.error || r.status);
|
|
455
|
+
document.getElementById('u-dir').value = d.current;
|
|
456
|
+
box.innerHTML = `<div class="cur">${esc(d.current)}</div>` +
|
|
457
|
+
(d.parent ? `<div data-p="${esc(d.parent)}">⬆ 上一级</div>` : '') +
|
|
458
|
+
(d.drives || []).map(x => `<div data-p="${esc(x)}">💽 ${esc(x)}</div>`).join('') +
|
|
459
|
+
d.dirs.map(x => `<div data-p="${esc(x.path)}">📁 ${esc(x.name)}</div>`).join('');
|
|
460
|
+
box.querySelectorAll('[data-p]').forEach(el => el.onclick = () => pickDir(el.dataset.p));
|
|
461
|
+
} catch (e) { box.innerHTML = `<div class="cur">无法浏览目录(${esc(e.message)}),请直接输入路径</div>`; }
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async function setExclusive(on) {
|
|
465
|
+
try {
|
|
466
|
+
const r = await fetch('/api/admin/exclusive', { method: 'POST', headers: H(), body: JSON.stringify({ on }) });
|
|
467
|
+
const d = await r.json();
|
|
468
|
+
if (!d.ok) { alert(d.error || '失败'); refresh(); }
|
|
469
|
+
} catch { alert('连接失败'); refresh(); }
|
|
470
|
+
}
|
|
471
|
+
|
|
396
472
|
function esc(s) { return String(s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,'''); }
|
|
397
473
|
|
|
398
474
|
async function addUser() {
|
|
@@ -400,17 +476,27 @@ async function addUser() {
|
|
|
400
476
|
const name = document.getElementById('u-name').value.trim();
|
|
401
477
|
const dir = document.getElementById('u-dir').value.trim();
|
|
402
478
|
const msg = document.getElementById('u-msg');
|
|
403
|
-
if (!
|
|
479
|
+
if (!name || !pwd) { msg.className = 'msg msg-err'; msg.textContent = '成员名称和登录密码必填'; return; }
|
|
480
|
+
// 第一个成员 = 从单用户切到多用户:先讲清楚会发生什么
|
|
481
|
+
if (!_users.length && !confirm(
|
|
482
|
+
'添加第一个成员后,myhi 进入「多用户模式」:\n\n' +
|
|
483
|
+
'· 成员用各自的密码登录' + (dir ? ',只能在绑定的目录里建任务' : '') + ';\n' +
|
|
484
|
+
'· 成员可以升级、重启,但不能改全局设置(本机显示名、语音、用户、网关等);\n' +
|
|
485
|
+
'· 你(管理员)照常用管理密码或二维码登录,权限不变。\n\n' +
|
|
486
|
+
'以后删除全部成员即回到单用户模式。确定添加?')) return;
|
|
404
487
|
try {
|
|
405
488
|
const r = await fetch('/api/admin/user', { method: 'POST', headers: H(), body: JSON.stringify({ password: pwd, name, dir }) });
|
|
406
489
|
const d = await r.json();
|
|
407
|
-
if (d.ok) {
|
|
408
|
-
|
|
490
|
+
if (d.ok) {
|
|
491
|
+
msg.className = 'msg msg-ok'; msg.textContent = `已添加「${name}」,登录密码:${pwd}(请告知对方)`;
|
|
492
|
+
['u-pwd', 'u-name', 'u-dir'].forEach(id => document.getElementById(id).value = '');
|
|
493
|
+
closeAddMember(); refresh();
|
|
494
|
+
} else { msg.className = 'msg msg-err'; msg.textContent = d.error || '失败'; }
|
|
409
495
|
} catch { msg.className = 'msg msg-err'; msg.textContent = '连接失败'; }
|
|
410
496
|
}
|
|
411
497
|
|
|
412
498
|
async function editUserPwd(name) {
|
|
413
|
-
const newPwd = prompt(
|
|
499
|
+
const newPwd = prompt(`为 ${name} 设置新的登录密码(至少 4 位,设置后他当前的登录会失效):`);
|
|
414
500
|
if (!newPwd) return;
|
|
415
501
|
try {
|
|
416
502
|
const r = await fetch('/api/admin/user/password', { method: 'POST', headers: H(), body: JSON.stringify({ name, password: newPwd }) });
|
|
@@ -421,7 +507,7 @@ async function editUserPwd(name) {
|
|
|
421
507
|
}
|
|
422
508
|
|
|
423
509
|
async function delUser(name) {
|
|
424
|
-
if (!confirm(
|
|
510
|
+
if (!confirm(`确定删除成员「${name}」?他当前的登录会立即失效。` + (_users.length === 1 ? '\n\n这是最后一个成员,删除后回到单用户模式。' : ''))) return;
|
|
425
511
|
try {
|
|
426
512
|
const r = await fetch('/api/admin/user', { method: 'DELETE', headers: H(), body: JSON.stringify({ name }) });
|
|
427
513
|
const d = await r.json();
|
|
@@ -515,7 +601,15 @@ async function waitReload() {
|
|
|
515
601
|
document.getElementById('op-msg').textContent = '重启超时,请手动刷新';
|
|
516
602
|
}
|
|
517
603
|
|
|
518
|
-
|
|
604
|
+
// 主站已是管理员 → 直接换后台令牌免登录;否则沿用本标签页已有令牌(重启后会失效)或显示登录框
|
|
605
|
+
(async () => {
|
|
606
|
+
try {
|
|
607
|
+
const r = await fetch('/api/admin/sso', { method: 'POST' });
|
|
608
|
+
const d = r.ok ? await r.json() : null;
|
|
609
|
+
if (d?.ok) { T = d.token; sessionStorage.setItem('myhi_admin', T); return showAdmin(); }
|
|
610
|
+
} catch {}
|
|
611
|
+
if (T) showAdmin();
|
|
612
|
+
})();
|
|
519
613
|
</script>
|
|
520
614
|
</body>
|
|
521
615
|
</html>
|
package/dist/chat.html
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
<link rel="apple-touch-icon" href="/icon.png">
|
|
11
11
|
<title>myhi 会话</title>
|
|
12
12
|
<script src="/lib/machine.js" data-session></script>
|
|
13
|
+
<script src="/lib/prefs.js"></script>
|
|
13
14
|
<script src="/sio-client/socket.io.min.js"></script>
|
|
14
15
|
<script>
|
|
15
16
|
(function(){try{
|
|
@@ -204,7 +205,6 @@
|
|
|
204
205
|
@media (hover: none) { .raw-copy { opacity: 0.85; } }
|
|
205
206
|
/* AI 工具的原始输出默认不显示(设置→聊天→显示 打开;终端会话的命令输出不受影响) */
|
|
206
207
|
body.hide-toolout .msg-raw.tool-out { display: none; }
|
|
207
|
-
.setting-toggle.off { opacity: 0.45; pointer-events: none; }
|
|
208
208
|
/* 消息里其他带框内容(代码块/表格/diff/工具详情…)同款复制按钮,见 attachCopyBtns */
|
|
209
209
|
.cpw, .cp-host { position: relative; }
|
|
210
210
|
.cp-btn { user-select: none; }
|
|
@@ -313,7 +313,7 @@
|
|
|
313
313
|
/* ── 设置面板 ───────────────────────────── */
|
|
314
314
|
#settings-backdrop { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 60; }
|
|
315
315
|
#settings-backdrop.open { display: block; }
|
|
316
|
-
#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; }
|
|
316
|
+
#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; box-sizing: border-box; }
|
|
317
317
|
#settings-panel.open { right: 0; }
|
|
318
318
|
.panel-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.6rem; font-size: 1rem; font-weight: 600; }
|
|
319
319
|
.panel-close { background: none; border: none; color: var(--muted); font-size: 1.2rem; cursor: pointer; }
|
|
@@ -330,6 +330,18 @@
|
|
|
330
330
|
.seg-row { display: flex; gap: 0.35rem; flex-wrap: wrap; }
|
|
331
331
|
.seg { flex: 1; min-width: 3.2rem; background: var(--surface2); color: var(--text2); border: 1px solid var(--border); border-radius: 8px; padding: 0.45rem 0.3rem; font-size: 0.8rem; cursor: pointer; white-space: nowrap; }
|
|
332
332
|
.seg.active { background: var(--purple); color: #fff; border-color: var(--purple); }
|
|
333
|
+
/* 设置里「名称 + 分段选择」一行 */
|
|
334
|
+
.set-line { display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; padding: 0.45rem 0; font-size: 0.85rem; }
|
|
335
|
+
.set-line > span { flex: 0 1 auto; min-width: 3.5rem; }
|
|
336
|
+
.set-line small { display: block; font-size: 0.68rem; color: var(--muted); }
|
|
337
|
+
.set-line .seg-row { flex: 1 1 auto; max-width: 13.5rem; flex-wrap: nowrap; }
|
|
338
|
+
.set-line .seg { min-width: 0; padding: 0.38rem 0.2rem; font-size: 0.76rem; }
|
|
339
|
+
.set-task-hint { font-size: 0.74rem; color: var(--muted); line-height: 1.5; margin-bottom: 0.6rem; }
|
|
340
|
+
.set-task-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.45rem; }
|
|
341
|
+
.set-task-grid button { background: var(--surface2); color: var(--text); border: 1px solid var(--border); border-radius: 8px; padding: 0.6rem 0.5rem; font-size: 0.82rem; cursor: pointer; text-align: left; }
|
|
342
|
+
.set-task-grid button:hover { border-color: var(--purple); }
|
|
343
|
+
.set-foot { font-size: 0.72rem; color: var(--muted); line-height: 1.55; margin-top: 1rem; padding-top: 0.7rem; border-top: 1px solid var(--border); }
|
|
344
|
+
.set-foot a { color: var(--blue); }
|
|
333
345
|
.setting-toggle input[type=checkbox] { width: 36px; height: 20px; appearance: none; -webkit-appearance: none; background: var(--border); border-radius: 10px; position: relative; cursor: pointer; transition: background 0.2s; }
|
|
334
346
|
.setting-toggle input[type=checkbox]:checked { background: var(--green2); }
|
|
335
347
|
.setting-toggle input[type=checkbox]::after { content: ''; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; background: #fff; border-radius: 50%; transition: left 0.2s; }
|
|
@@ -624,34 +636,16 @@
|
|
|
624
636
|
<div id="settings-panel">
|
|
625
637
|
<div class="panel-header"><span>设置</span><button class="panel-close" onclick="closeSettings()">✕</button></div>
|
|
626
638
|
<div id="settings-tabs">
|
|
627
|
-
<button class="stab active" data-tab="
|
|
628
|
-
<button class="stab" data-tab="chat">聊天</button>
|
|
639
|
+
<button class="stab active" data-tab="chat">聊天</button>
|
|
629
640
|
<button class="stab" data-tab="bar">快捷栏</button>
|
|
630
|
-
<button class="stab" data-tab="
|
|
641
|
+
<button class="stab" data-tab="task" id="stab-task">本任务</button>
|
|
631
642
|
</div>
|
|
632
|
-
<div class="setting-group" data-stab="
|
|
633
|
-
<div style="font-size:0.8rem;color:var(--text2);margin:0.1rem 0 0.35rem">主题</div>
|
|
634
|
-
<div id="seg-theme" class="seg-row">
|
|
635
|
-
<button class="seg" data-theme="system">跟随系统</button>
|
|
636
|
-
<button class="seg" data-theme="dark">深色</button>
|
|
637
|
-
<button class="seg" data-theme="light">浅色</button>
|
|
638
|
-
<button class="seg" data-theme="black">纯黑</button>
|
|
639
|
-
</div>
|
|
640
|
-
<div style="font-size:0.8rem;color:var(--text2);margin:0.6rem 0 0.35rem">字号</div>
|
|
641
|
-
<div id="seg-font" class="seg-row">
|
|
642
|
-
<button class="seg" data-font="small">小</button>
|
|
643
|
-
<button class="seg" data-font="medium">中</button>
|
|
644
|
-
<button class="seg" data-font="large">大</button>
|
|
645
|
-
</div>
|
|
646
|
-
</div>
|
|
647
|
-
<div class="setting-group" data-stab="chat" style="display:none">
|
|
643
|
+
<div class="setting-group" data-stab="chat">
|
|
648
644
|
<h4>显示</h4>
|
|
649
|
-
<
|
|
650
|
-
<
|
|
651
|
-
<
|
|
652
|
-
<label class="setting-toggle"><span
|
|
653
|
-
<label class="setting-toggle"><span>显示工具输出内容(AI 执行命令/读文件的原始结果)</span><input type="checkbox" id="set-toolout"></label>
|
|
654
|
-
<label class="setting-toggle" id="row-rawclip"><span> └ 工具输出只显示前 8 行(可展开)</span><input type="checkbox" id="set-rawclip" checked></label>
|
|
645
|
+
<div class="set-line"><span>思考过程</span><div id="seg-think" class="seg-row"><button class="seg" data-v="hide">隐藏</button><button class="seg" data-v="fold">折叠</button><button class="seg" data-v="open">展开</button></div></div>
|
|
646
|
+
<div class="set-line"><span>工具调用</span><div id="seg-tools" class="seg-row"><button class="seg" data-v="fold">折叠</button><button class="seg" data-v="open">展开</button></div></div>
|
|
647
|
+
<div class="set-line"><span>工具输出<small>AI 执行命令/读文件的原始结果</small></span><div id="seg-toolout" class="seg-row"><button class="seg" data-v="hide">隐藏</button><button class="seg" data-v="clip">前8行</button><button class="seg" data-v="all">全部</button></div></div>
|
|
648
|
+
<label class="setting-toggle" id="row-parse"><span>把终端里的 Claude 输出整理成聊天样式</span><input type="checkbox" id="set-parse" checked></label>
|
|
655
649
|
</div>
|
|
656
650
|
<div class="setting-group" data-stab="chat" style="display:none">
|
|
657
651
|
<h4>输入</h4>
|
|
@@ -675,15 +669,20 @@
|
|
|
675
669
|
</div>
|
|
676
670
|
<div id="sk-visible-list" style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:0.15rem 0.6rem;margin-top:0.5rem"></div>
|
|
677
671
|
</div>
|
|
678
|
-
<div class="setting-group" data-stab="
|
|
679
|
-
<
|
|
680
|
-
<div
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
672
|
+
<div class="setting-group" data-stab="task" style="display:none">
|
|
673
|
+
<div class="set-task-hint">这些设置只对当前任务(或它所在的项目)生效,快捷栏里隐藏了的也能从这里打开。</div>
|
|
674
|
+
<div class="set-task-grid">
|
|
675
|
+
<button onclick="closeSettings();openModelSheet()">🤖 模型</button>
|
|
676
|
+
<button onclick="closeSettings();openGuardSheet()">🛡 隐私护栏</button>
|
|
677
|
+
<button onclick="closeSettings();openSecretsSheet()">🔑 密钥</button>
|
|
678
|
+
<button onclick="closeSettings();openFollowUpSheet()">🔄 自动跟进</button>
|
|
679
|
+
<button onclick="closeSettings();showMemory()">🧠 记忆</button>
|
|
680
|
+
<button onclick="closeSettings();openSkillSheet()">🧩 技能</button>
|
|
681
|
+
<button onclick="closeSettings();openInboxSheet()">📥 草稿箱</button>
|
|
682
|
+
<button onclick="closeSettings();openGitSheet()">⬆ 提交代码</button>
|
|
683
|
+
</div>
|
|
686
684
|
</div>
|
|
685
|
+
<div class="set-foot">这里的设置会同步到你登录的其他设备。外观(主题 / 字号)、本机显示名、语音、版本升级与重启在 <a href="/?settings=1">首页 ⚙ 设置</a>。</div>
|
|
687
686
|
</div>
|
|
688
687
|
|
|
689
688
|
<!-- QR弹窗 -->
|
|
@@ -1217,7 +1216,7 @@
|
|
|
1217
1216
|
let settings = loadSettings();
|
|
1218
1217
|
|
|
1219
1218
|
function loadSettings() { try { return { ...DEFAULT_SETTINGS, ...JSON.parse(localStorage.getItem('myhi_chat_settings')) }; } catch { return { ...DEFAULT_SETTINGS }; } }
|
|
1220
|
-
function saveSettings() { localStorage.setItem('myhi_chat_settings', JSON.stringify(settings)); }
|
|
1219
|
+
function saveSettings() { localStorage.setItem('myhi_chat_settings', JSON.stringify(settings)); window.myhiPrefsPush?.(); } // 同步到其他设备
|
|
1221
1220
|
|
|
1222
1221
|
// ── 外观:主题 & 字号(跨页面共用 localStorage myhi_theme / myhi_font)──
|
|
1223
1222
|
const THEME_META = { dark: '#0d1117', light: '#ffffff', black: '#000000', system: '#0d1117' };
|
|
@@ -3585,7 +3584,8 @@
|
|
|
3585
3584
|
overlay.appendChild(wrap);
|
|
3586
3585
|
};
|
|
3587
3586
|
|
|
3588
|
-
window.openSettings = function() { document.getElementById('settings-backdrop').classList.add('open'); document.getElementById('settings-panel').classList.add('open'); syncSettingsUI();
|
|
3587
|
+
window.openSettings = function() { document.getElementById('settings-backdrop').classList.add('open'); document.getElementById('settings-panel').classList.add('open'); syncSettingsUI(); const t0 = localStorage.getItem('myhi_settings_tab');
|
|
3588
|
+
applySettingsTab(['bar', 'task'].includes(t0) && !(t0 === 'task' && currentSession?.mode !== 'agent') ? t0 : 'chat'); };
|
|
3589
3589
|
// 设置抽屉分类页签(1.3.174:一屏只看一类,不再长条堆叠)
|
|
3590
3590
|
function applySettingsTab(tab) {
|
|
3591
3591
|
document.querySelectorAll('#settings-tabs .stab').forEach(b => b.classList.toggle('active', b.dataset.tab === tab));
|
|
@@ -4122,35 +4122,51 @@
|
|
|
4122
4122
|
function syncSettingsUI() {
|
|
4123
4123
|
syncAppearanceUI();
|
|
4124
4124
|
document.getElementById('set-parse').checked = settings.parseOutput;
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
document.getElementById('
|
|
4128
|
-
document.getElementById('
|
|
4129
|
-
|
|
4125
|
+
// 「整理终端输出」只对终端模式的 Claude 会话有用;「本任务」入口只对 AI 会话有用
|
|
4126
|
+
const isAgent = currentSession?.mode === 'agent';
|
|
4127
|
+
document.getElementById('row-parse').style.display = isAgent ? 'none' : '';
|
|
4128
|
+
document.getElementById('stab-task').style.display = isAgent ? '' : 'none';
|
|
4129
|
+
syncDisplaySegs();
|
|
4130
4130
|
applyToolOut();
|
|
4131
4131
|
syncEnterModeUI();
|
|
4132
4132
|
syncSkModeUI();
|
|
4133
4133
|
}
|
|
4134
4134
|
document.getElementById('set-parse').addEventListener('change', (e) => { settings.parseOutput = e.target.checked; saveSettings(); });
|
|
4135
|
-
|
|
4136
|
-
document.
|
|
4137
|
-
document.getElementById('set-thinkcollapse').addEventListener('change', (e) => { settings.thinkCollapsed = e.target.checked; saveSettings(); });
|
|
4138
|
-
// 工具输出显隐:关着时「只显示前 8 行」没意义,置灰
|
|
4139
|
-
function applyToolOut() {
|
|
4140
|
-
document.body.classList.toggle('hide-toolout', !settings.showToolOut);
|
|
4141
|
-
document.getElementById('row-rawclip')?.classList.toggle('off', !settings.showToolOut);
|
|
4142
|
-
}
|
|
4135
|
+
// AI 工具输出显隐(body.hide-toolout 藏掉 .msg-raw.tool-out;终端会话的命令输出不受影响)
|
|
4136
|
+
function applyToolOut() { document.body.classList.toggle('hide-toolout', !settings.showToolOut); }
|
|
4143
4137
|
applyToolOut();
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
}
|
|
4138
|
+
// 显示设置:三组分段选择,各对应原来的一两个开关
|
|
4139
|
+
// 思考过程 隐藏/折叠/展开 = showThinking + thinkCollapsed;工具调用 折叠/展开 = collapseTools;
|
|
4140
|
+
// 工具输出 隐藏/前 8 行/全部 = showToolOut + rawClip
|
|
4141
|
+
function displayState() {
|
|
4142
|
+
return {
|
|
4143
|
+
think: !settings.showThinking ? 'hide' : (settings.thinkCollapsed !== false ? 'fold' : 'open'),
|
|
4144
|
+
tools: settings.collapseTools ? 'fold' : 'open',
|
|
4145
|
+
toolout: !settings.showToolOut ? 'hide' : (settings.rawClip !== false ? 'clip' : 'all'),
|
|
4146
|
+
};
|
|
4147
|
+
}
|
|
4148
|
+
function syncDisplaySegs() {
|
|
4149
|
+
const st = displayState();
|
|
4150
|
+
for (const [id, key] of [['seg-think', 'think'], ['seg-tools', 'tools'], ['seg-toolout', 'toolout']]) {
|
|
4151
|
+
document.querySelectorAll('#' + id + ' .seg').forEach(b => b.classList.toggle('active', b.dataset.v === st[key]));
|
|
4152
|
+
}
|
|
4153
|
+
}
|
|
4154
|
+
document.querySelectorAll('#seg-think .seg').forEach(b => b.addEventListener('click', () => {
|
|
4155
|
+
settings.showThinking = b.dataset.v !== 'hide';
|
|
4156
|
+
if (b.dataset.v !== 'hide') settings.thinkCollapsed = b.dataset.v === 'fold';
|
|
4157
|
+
saveSettings(); syncDisplaySegs();
|
|
4158
|
+
}));
|
|
4159
|
+
document.querySelectorAll('#seg-tools .seg').forEach(b => b.addEventListener('click', () => {
|
|
4160
|
+
settings.collapseTools = b.dataset.v === 'fold'; saveSettings(); syncDisplaySegs();
|
|
4161
|
+
}));
|
|
4162
|
+
document.querySelectorAll('#seg-toolout .seg').forEach(b => b.addEventListener('click', () => {
|
|
4163
|
+
const was = displayState().toolout;
|
|
4164
|
+
settings.showToolOut = b.dataset.v !== 'hide';
|
|
4165
|
+
if (b.dataset.v !== 'hide') settings.rawClip = b.dataset.v === 'clip';
|
|
4166
|
+
saveSettings(); syncDisplaySegs(); applyToolOut();
|
|
4167
|
+
// 显示方式变了:已渲染的输出块按新默认重新量(隐藏时量不出高度;用户手动点过的展开/收起也重置)
|
|
4168
|
+
if (settings.showToolOut && was !== b.dataset.v) chatArea.querySelectorAll('.msg-raw.tool-out').forEach(m => { delete m.dataset.open; rawClipSync(m); });
|
|
4169
|
+
}));
|
|
4154
4170
|
|
|
4155
4171
|
window.restartMyhi = async function() {
|
|
4156
4172
|
if (!confirm('确认重启 myhi 服务?\n重启期间连接会短暂中断,页面将自动重连。')) return;
|