@wendongfly/myhi 1.3.179 → 1.3.181

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 CHANGED
@@ -410,6 +410,8 @@
410
410
  .dr-text { color: var(--text); font-size: 0.82rem; line-height: 1.5; word-break: break-word; white-space: pre-wrap; }
411
411
  .dr-meta { display: flex; align-items: center; flex-wrap: wrap; gap: 0.4rem 0.6rem; margin-top: 0.35rem; font-size: 0.66rem; color: var(--muted); }
412
412
  .dr-acts { display: flex; gap: 0.4rem; margin-left: auto; flex-shrink: 0; }
413
+ .atpick-row { padding: 0.42rem 0.7rem; cursor: pointer; color: var(--text2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
414
+ .atpick-row:hover { background: var(--surface2); color: var(--text); }
413
415
  .dr-btn { flex-shrink: 0; white-space: nowrap; padding: 0.28rem 0.7rem; font-size: 0.72rem; border: none; border-radius: 6px; background: var(--surface2); color: var(--text2); cursor: pointer; }
414
416
  .dr-btn.take { background: var(--green); color: #fff; font-weight: 600; }
415
417
  .dr-btn:active { opacity: 0.75; }
@@ -464,11 +466,11 @@
464
466
  <div id="session-title">加载中...</div>
465
467
  <div id="session-cwd"></div>
466
468
  </div>
469
+ <button class="top-btn" onclick="openChatHelp()" title="使用说明" style="font-size:0.7rem;color:var(--muted)">?</button>
467
470
  <button class="top-btn" onclick="openSettings()" title="设置">
468
471
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 01-2.83 2.83l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg>
469
472
  </button>
470
473
  <button class="top-btn" id="ctx-chip" onclick="openCtxSheet()" title="会话数据:上下文水位 / 成本 / token 明细" style="display:none;width:auto;padding:0 0.5rem;white-space:nowrap;font-size:0.7rem;color:var(--muted)">◈ --</button>
471
- <button class="top-btn" onclick="openChatHelp()" title="使用说明" style="font-size:0.7rem;color:var(--muted)">?</button>
472
474
  <button class="top-btn" onclick="openFilePanel()" title="文件" style="font-size:0.7rem;color:var(--muted)">文件</button>
473
475
  <button class="top-btn" onclick="openSwitchSheet()" title="切换会话" style="font-size:0.7rem;color:var(--muted)">切换</button>
474
476
  <button class="top-btn" onclick="goBack()" title="返回">
@@ -673,12 +675,28 @@
673
675
  </div>
674
676
  <div class="setting-group" data-stab="more" style="display:none">
675
677
  <h4>@ 引用范围(项目外)</h4>
676
- <div style="font-size:0.72rem;color:var(--muted);margin-bottom:0.35rem;line-height:1.5">允许 @ 补全引用任务目录之外的目录(如公共资料库),命中以绝对路径插入、AI 直接按绝对路径读取。</div>
678
+ <div style="font-size:0.72rem;color:var(--muted);margin-bottom:0.35rem;line-height:1.5">除了任务目录,再把常用的资料文件夹(如公共资料库)加进来,打 @ 时也能搜到里面的文件。</div>
677
679
  <div id="atroots-list"></div>
678
- <div style="display:flex;gap:0.4rem;margin-top:0.4rem">
679
- <input id="atroot-new" class="slash-inp" placeholder="例如 D:\资料库" autocomplete="off" style="flex:1;font-size:0.82rem">
680
- <button onclick="addAtRoot()" style="background:var(--blue2);color:#fff;border:none;border-radius:8px;padding:0.4rem 0.8rem;font-size:0.78rem;cursor:pointer;white-space:nowrap">添加</button>
680
+ <button id="atpick-open" onclick="openAtPicker()" style="width:100%;margin-top:0.4rem;background:var(--blue2);color:#fff;border:none;border-radius:8px;padding:0.5rem;font-size:0.8rem;cursor:pointer">📁 选择文件夹添加…</button>
681
+ <div id="atpick" style="display:none;margin-top:0.4rem;border:1px solid var(--border);border-radius:10px;overflow:hidden">
682
+ <div id="atpick-drives" style="display:none;gap:0.3rem;padding:0.4rem 0.5rem 0;flex-wrap:wrap"></div>
683
+ <div style="display:flex;align-items:center;gap:0.35rem;padding:0.4rem 0.5rem;border-bottom:1px solid var(--border)">
684
+ <button id="atpick-up" class="dr-btn" onclick="atPickUp()" title="上一级">⬆ 上一级</button>
685
+ <span id="atpick-cur" style="flex:1;min-width:0;font-size:0.72rem;color:var(--text2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;direction:rtl;text-align:left"></span>
686
+ </div>
687
+ <div id="atpick-list" style="max-height:200px;overflow-y:auto;scrollbar-width:thin;font-size:0.8rem"></div>
688
+ <div style="display:flex;gap:0.4rem;padding:0.45rem 0.5rem;border-top:1px solid var(--border)">
689
+ <button class="dr-btn" onclick="closeAtPicker()" style="flex:1;padding:0.45rem">取消</button>
690
+ <button onclick="addAtPicked()" style="flex:2;background:var(--blue2);color:#fff;border:none;border-radius:6px;padding:0.45rem;font-size:0.78rem;cursor:pointer">✓ 添加当前文件夹</button>
691
+ </div>
681
692
  </div>
693
+ <details style="margin-top:0.35rem;font-size:0.72rem;color:var(--muted)">
694
+ <summary style="cursor:pointer">手动输入路径</summary>
695
+ <div style="display:flex;gap:0.4rem;margin-top:0.35rem">
696
+ <input id="atroot-new" class="slash-inp" placeholder="例如 D:\资料库" autocomplete="off" style="flex:1;font-size:0.82rem">
697
+ <button onclick="addAtRoot()" style="background:var(--blue2);color:#fff;border:none;border-radius:8px;padding:0.4rem 0.8rem;font-size:0.78rem;cursor:pointer;white-space:nowrap">添加</button>
698
+ </div>
699
+ </details>
682
700
  <div id="atroot-status" style="font-size:0.72rem;min-height:1.2em;margin-top:0.25rem;color:var(--muted)"></div>
683
701
  </div>
684
702
  <div class="setting-group" data-stab="more" style="display:none">
@@ -1346,6 +1364,63 @@
1346
1364
  };
1347
1365
  window.delAtRoot = function(i) { saveAtRoots((_atRoots || []).filter((_, j) => j !== i)); };
1348
1366
 
1367
+ // 文件夹点选(复用 /api/dirs 目录浏览):普通用户不会敲绝对路径,点进去选中即可
1368
+ let _atPick = { current: '', parent: null };
1369
+ window.openAtPicker = function() {
1370
+ document.getElementById('atpick').style.display = '';
1371
+ document.getElementById('atpick-open').style.display = 'none';
1372
+ atPickGo('');
1373
+ };
1374
+ window.closeAtPicker = function() {
1375
+ document.getElementById('atpick').style.display = 'none';
1376
+ document.getElementById('atpick-open').style.display = '';
1377
+ };
1378
+ window.atPickGo = async function(path) {
1379
+ const list = document.getElementById('atpick-list');
1380
+ list.innerHTML = '<div style="padding:0.6rem;color:var(--muted);font-size:0.75rem">读取中…</div>';
1381
+ try {
1382
+ const r = await fetch('/api/dirs' + (path ? '?path=' + encodeURIComponent(path) : ''));
1383
+ const d = await readApiJson(r);
1384
+ if (!r.ok) throw new Error(d.error || r.status);
1385
+ _atPick = d;
1386
+ const cur = document.getElementById('atpick-cur');
1387
+ cur.textContent = '\u200e' + d.current; // rtl 截断时保留路径尾部;LRM 防标点跑位
1388
+ cur.title = d.current;
1389
+ document.getElementById('atpick-up').disabled = !d.parent;
1390
+ document.getElementById('atpick-up').style.opacity = d.parent ? '' : '0.4';
1391
+ const dv = document.getElementById('atpick-drives');
1392
+ dv.style.display = (d.drives || []).length ? 'flex' : 'none';
1393
+ dv.innerHTML = '';
1394
+ (d.drives || []).forEach(dr => {
1395
+ const b = document.createElement('button');
1396
+ b.className = 'dr-btn';
1397
+ b.textContent = '💽 ' + dr.replace(/\\$/, '');
1398
+ if (d.current.toUpperCase().startsWith(dr.toUpperCase())) b.style.cssText = 'background:var(--blue2);color:#fff';
1399
+ b.onclick = () => atPickGo(dr);
1400
+ dv.appendChild(b);
1401
+ });
1402
+ list.innerHTML = '';
1403
+ if (!d.dirs.length) list.innerHTML = '<div style="padding:0.6rem;color:var(--muted);font-size:0.75rem">(没有子文件夹)</div>';
1404
+ d.dirs.forEach(x => {
1405
+ const row = document.createElement('div');
1406
+ row.className = 'atpick-row';
1407
+ row.textContent = '📁 ' + x.name;
1408
+ row.onclick = () => atPickGo(x.path);
1409
+ list.appendChild(row);
1410
+ });
1411
+ } catch (e) {
1412
+ list.innerHTML = '<div style="padding:0.6rem;color:var(--red);font-size:0.75rem">读取失败:' + escHtml(String(e.message || e)) +
1413
+ (String(e.message).includes('权限') ? '(仅管理员可浏览目录,可展开下方「手动输入路径」)' : '') + '</div>';
1414
+ }
1415
+ };
1416
+ window.atPickUp = function() { if (_atPick.parent) atPickGo(_atPick.parent); };
1417
+ window.addAtPicked = async function() {
1418
+ const p = _atPick.current;
1419
+ if (!p) return;
1420
+ if ((_atRoots || []).includes(p)) { document.getElementById('atroot-status').textContent = '这个文件夹已经在列表里了'; return; }
1421
+ if (await saveAtRoots([...(_atRoots || []), p])) closeAtPicker();
1422
+ };
1423
+
1349
1424
  // 输出累积
1350
1425
  let outputBuffer = '';
1351
1426
  let outputTimer = null;
@@ -2340,6 +2415,15 @@
2340
2415
  scrollToBottom();
2341
2416
  }
2342
2417
 
2418
+ // 解析接口 JSON。升级后没重启 server 时,新页面调新接口会拿到 Express 的 404 HTML,
2419
+ // 直接 .json() 只报「Unexpected token '<'」,用户看不懂 → 换成明确的处理建议
2420
+ async function readApiJson(resp) {
2421
+ const text = await resp.text();
2422
+ try { return JSON.parse(text); } catch {}
2423
+ if (resp.status === 404 || /^\s*</.test(text)) throw new Error('服务端还是旧版本——升级后需要重启 myhi(设置→更多→重启 myhi)');
2424
+ throw new Error(`服务端返回异常(HTTP ${resp.status})`);
2425
+ }
2426
+
2343
2427
  // 复制到剪贴板。HTTP 访问(非安全上下文)没有 navigator.clipboard,退回 textarea + execCommand
2344
2428
  async function copyText(text) {
2345
2429
  try {
@@ -4023,7 +4107,7 @@
4023
4107
  headers: { 'Content-Type': 'application/json' },
4024
4108
  body: JSON.stringify({ name: document.getElementById('set-machine-name').value.trim() }),
4025
4109
  });
4026
- const data = await resp.json();
4110
+ const data = await readApiJson(resp);
4027
4111
  if (!data.ok) throw new Error(data.error || '未知错误');
4028
4112
  window.myhiSetMachineName?.(data.name);
4029
4113
  statusEl.style.color = 'var(--green)';
package/dist/grid.html CHANGED
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
6
6
  <meta name="theme-color" content="#0d1117">
7
- <title>myhi 分屏</title>
7
+ <title>myhi</title>
8
8
  <script src="/lib/machine.js"></script>
9
9
  <script src="/sio-client/socket.io.min.js"></script>
10
10
  <script>
@@ -411,7 +411,7 @@
411
411
  function beep(){ try { const c=new (window.AudioContext||window.webkitAudioContext)(); const o=c.createOscillator(), g=c.createGain(); o.connect(g); g.connect(c.destination); o.frequency.value=660; g.gain.value=0.06; o.start(); setTimeout(()=>{o.stop();c.close();},180);} catch {} }
412
412
  function fireNotify(pane,label){
413
413
  if(!_notify) return;
414
- try { if('Notification' in window && Notification.permission==='granted') new Notification('myhi · '+label,{body:paneTitle(pane)}); } catch {}
414
+ try { if('Notification' in window && Notification.permission==='granted') new Notification((localStorage.getItem('myhi_machine_name')||'myhi')+' · '+label,{body:paneTitle(pane)}); } catch {}
415
415
  beep();
416
416
  }
417
417
  function setPaneStatus(pane,state){
package/dist/index.html CHANGED
@@ -471,6 +471,47 @@
471
471
  #update-btn { background: var(--yellow); color: #000; border: none; border-radius: 6px; padding: 0.35rem 0.75rem; font-size: 0.75rem; font-weight: 600; cursor: pointer; white-space: nowrap; }
472
472
  #update-btn:active { opacity: 0.7; }
473
473
  #update-btn:disabled { opacity: 0.5; cursor: not-allowed; }
474
+ /* ── 快速上手引导 ── */
475
+ #help-backdrop { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.55); backdrop-filter: blur(2px); z-index: 30; }
476
+ #help-sheet { display: none; position: fixed; z-index: 31; background: var(--surface); border: 1px solid var(--border2);
477
+ left: 50%; top: 50%; transform: translate(-50%, -50%); width: min(560px, calc(100vw - 2rem)); max-height: 88vh; overflow-y: auto;
478
+ border-radius: 16px; padding: 1.4rem 1.5rem 0; box-shadow: 0 20px 60px rgba(0,0,0,0.5); animation: hsIn 0.22s ease-out; }
479
+ @keyframes hsIn { from { opacity: 0; transform: translate(-50%, -48%); } to { opacity: 1; transform: translate(-50%, -50%); } }
480
+ #help-sheet .hs-close { position: absolute; top: 0.8rem; right: 0.8rem; width: 2rem; height: 2rem; border-radius: 50%; border: none; background: transparent; color: var(--muted); font-size: 0.95rem; cursor: pointer; }
481
+ #help-sheet .hs-close:hover { background: var(--border); color: var(--text); }
482
+ .hs-hero { text-align: center; margin-bottom: 1rem; }
483
+ .hs-logo { font-size: 1.7rem; font-weight: 800; letter-spacing: -0.02em; color: var(--text); }
484
+ .hs-logo span { color: var(--accent); }
485
+ .hs-sub { margin-top: 0.3rem; font-size: 0.86rem; color: var(--text2); }
486
+ .hs-steps { list-style: none; display: flex; flex-direction: column; gap: 0.55rem; margin: 0 0 1rem; padding: 0; }
487
+ .hs-steps li { display: flex; gap: 0.8rem; align-items: flex-start; padding: 0.6rem 0.85rem; background: var(--bg); border: 1px solid var(--border); border-radius: 12px; }
488
+ .hs-num { flex-shrink: 0; width: 1.6rem; height: 1.6rem; border-radius: 50%; background: var(--accent); color: #fff; font-size: 0.8rem; font-weight: 700; display: flex; align-items: center; justify-content: center; margin-top: 0.05rem; }
489
+ .hs-steps b, .hs-tile b { font-size: 0.88rem; color: var(--text); font-weight: 600; }
490
+ .hs-steps p { margin: 0.15rem 0 0; font-size: 0.8rem; color: var(--text2); line-height: 1.55; }
491
+ #help-sheet kbd { display: inline-block; min-width: 1.3em; padding: 0 0.3em; border: 1px solid var(--border2); border-bottom-width: 2px; border-radius: 4px; background: var(--surface); font: inherit; font-size: 0.85em; text-align: center; color: var(--text); }
492
+ .hs-label { font-size: 0.72rem; color: var(--muted); letter-spacing: 0.06em; margin-bottom: 0.5rem; }
493
+ .hs-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.45rem; margin-bottom: 1rem; }
494
+ .hs-tile { display: flex; gap: 0.6rem; align-items: center; padding: 0.5rem 0.7rem; border: 1px solid var(--border); border-radius: 10px; }
495
+ .hs-tile > span { font-size: 1.15rem; width: 1.6rem; text-align: center; flex-shrink: 0; }
496
+ .hs-tile b { font-size: 0.8rem; }
497
+ .hs-tile p { margin: 0.1rem 0 0; font-size: 0.72rem; color: var(--muted); line-height: 1.4; }
498
+ .hs-tip { font-size: 0.76rem; color: var(--text2); background: rgba(124,58,237,0.08); border: 1px solid rgba(124,58,237,0.25); border-radius: 10px; padding: 0.6rem 0.75rem; line-height: 1.55; margin-bottom: 0.2rem; }
499
+ .hs-actions { display: flex; gap: 0.6rem; position: sticky; bottom: 0; background: var(--surface); padding: 0.9rem 0 1.2rem; } /* 内容超高滚动时按钮钉在底部 */
500
+ .hs-actions button { flex: 1; border-radius: 10px; padding: 0.72rem; font-size: 0.88rem; cursor: pointer; }
501
+ .hs-primary { flex: 1.6 !important; background: var(--accent); color: #fff; border: none; font-weight: 700; }
502
+ .hs-primary:hover { background: var(--accent2); }
503
+ .hs-ghost { background: transparent; color: var(--text2); border: 1px solid var(--border2); }
504
+ .hs-ghost:hover { color: var(--text); border-color: var(--muted); }
505
+ /* 手机:底部抽屉 + 功能格单列更易读 */
506
+ @media (max-width: 540px) {
507
+ #help-sheet { left: 0; right: 0; top: auto; bottom: 0; transform: none; width: auto; border-radius: 16px 16px 0 0; border-bottom: none;
508
+ padding: 1.3rem 1rem 0; animation: hsUp 0.25s ease-out; }
509
+ @keyframes hsUp { from { transform: translateY(30%); opacity: 0; } to { transform: none; opacity: 1; } }
510
+ .hs-grid { grid-template-columns: 1fr 1fr; }
511
+ .hs-tile { padding: 0.5rem 0.55rem; }
512
+ .hs-tile p { display: none; }
513
+ .hs-actions { padding-bottom: calc(1rem + env(safe-area-inset-bottom)); }
514
+ }
474
515
  /* 分屏是 PC 大屏功能,手机端(触屏)隐藏入口 */
475
516
  @media (pointer: coarse) { #grid-btn { display: none !important; } }
476
517
  </style>
@@ -525,32 +566,33 @@
525
566
  <button id="fab" onclick="openSheet()">+</button>
526
567
  <button id="fab-note" onclick="openNoteSheet()" title="随手记:把想法先记进某个任务的草稿箱">📥</button>
527
568
 
528
- <!-- 使用说明 / 快速上手(首次访问自动弹出,之后从顶栏 ❓ 打开) -->
529
- <div id="help-backdrop" onclick="closeHelpSheet()" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.55);z-index:30"></div>
530
- <div id="help-sheet" style="display:none;position:fixed;left:0;right:0;bottom:0;z-index:31;background:var(--surface);border-radius:16px 16px 0 0;padding:1rem 1rem calc(1rem + env(safe-area-inset-bottom));max-height:88vh;overflow-y:auto">
531
- <div class="sheet-handle"></div>
532
- <div class="sheet-title">🚀 快速上手</div>
533
- <div style="font-size:0.82rem;color:var(--text);line-height:1.7">
534
- <b>① 建一个任务</b>:点右下角 <b>+</b>,选「默认」预设、起个任务名(目录会自动拼好),点创建。<br>
535
- <b>② 进去说话</b>:任务卡片点进去就是对话界面,像发微信一样把需求发给 AI,它会自己读文件、改代码、跑命令。<br>
536
- <b>③ 让它读文件</b>:消息里打 <b>@</b> 会弹出文件搜索(也搜你在设置里配的「@ 引用范围」),选中即插入路径。<br>
537
- <b>④ AI 忙时</b>:输入框会变成「存草稿」,想法先存着,干完自动提醒取回;或点 📥 草稿抽屉查看。<br>
538
- <b>⑤ 文件</b>:会话里点 📂 看任务文件——引用 / 编辑 / 下载 / 在本机打开文件夹。
569
+ <!-- 使用说明 / 快速上手(首次访问自动弹出,之后从顶栏 ❓ 打开)。桌面居中卡片,手机底部抽屉 -->
570
+ <div id="help-backdrop" onclick="closeHelpSheet()"></div>
571
+ <div id="help-sheet" role="dialog" aria-label="快速上手">
572
+ <button class="hs-close" onclick="closeHelpSheet()" title="关闭">✕</button>
573
+ <div class="hs-hero">
574
+ <div class="hs-logo">my<span>hi</span></div>
575
+ <div class="hs-sub">用手机或电脑,把活儿交给 AI。三步就能开始:</div>
539
576
  </div>
540
- <div class="sheet-title" style="margin-top:1rem;font-size:0.9rem">常用功能</div>
541
- <div style="font-size:0.78rem;color:var(--text2);line-height:1.8">
542
- • <b>📥 随手记</b>(首页右下角):不在任务里也能记一条想法,指定归属任务,干完再发;勾「自动接力」可自动发出。<br>
543
- • <b>▦ 分屏</b>:电脑上多任务并排(/grid)。<br>
544
- • <b>🩺 体检</b>:查看各任务会话健康度。<br>
545
- • <b>⚙️ 会话内设置</b>:快捷栏普通/专家模式、消息默认折叠、主题字号、@ 引用范围。<br>
546
- • <b>🔄 恢复</b>(会话内):翻历史对话分支继续聊;<b>🛡 护栏 / 🔑 密钥</b>:敏感信息保护(详见各自抽屉里的说明)。
577
+ <ol class="hs-steps">
578
+ <li><span class="hs-num">1</span><div><b>建一个任务</b><p>点右下角 <kbd>+</kbd>,选「默认」预设、起个名字,点创建。</p></div></li>
579
+ <li><span class="hs-num">2</span><div><b>像发微信一样说需求</b><p>进入任务就是对话界面,AI 会自己读文件、改代码、跑命令。</p></div></li>
580
+ <li><span class="hs-num">3</span><div><b>用 <kbd>@</kbd> 引用文件</b><p>消息里打 @ 弹出文件搜索,选中即插入路径,不用手打文件名。</p></div></li>
581
+ </ol>
582
+ <div class="hs-label">常用功能</div>
583
+ <div class="hs-grid">
584
+ <div class="hs-tile"><span>📥</span><div><b>草稿 / 随手记</b><p>AI 忙时先存想法,干完再发</p></div></div>
585
+ <div class="hs-tile"><span>📂</span><div><b>文件</b><p>查看、编辑、下载任务文件</p></div></div>
586
+ <div class="hs-tile"><span>▦</span><div><b>分屏</b><p>电脑上多个任务并排</p></div></div>
587
+ <div class="hs-tile"><span>🔄</span><div><b>恢复</b><p>翻历史对话继续聊</p></div></div>
588
+ <div class="hs-tile"><span>🛡</span><div><b>护栏 / 密钥</b><p>敏感信息不外泄</p></div></div>
589
+ <div class="hs-tile"><span>⚙️</span><div><b>会话设置</b><p>快捷栏、折叠、主题字号</p></div></div>
547
590
  </div>
548
- <div class="sheet-title" style="margin-top:1rem;font-size:0.9rem">手机 / 远程访问</div>
549
- <div style="font-size:0.78rem;color:var(--text2);line-height:1.8">
550
- 同一地址在手机浏览器打开即可(局域网或 Tailscale 内网)。建议「添加到主屏幕」当 App 用。触屏下回车是换行、用发送键发送。
591
+ <div class="hs-tip">📱 手机浏览器打开同一地址即可(局域网 / Tailscale),建议「添加到主屏幕」当 App 用。</div>
592
+ <div class="hs-actions">
593
+ <button class="hs-ghost" onclick="closeHelpSheet()">我知道了</button>
594
+ <button class="hs-primary" onclick="closeHelpSheet(); openSheet();">+ 新建第一个任务</button>
551
595
  </div>
552
- <button onclick="closeHelpSheet(); openSheet();" style="width:100%;background:var(--accent,#2f81f7);color:#fff;border:none;border-radius:10px;padding:0.75rem;font-size:0.9rem;font-weight:700;cursor:pointer;margin-top:1rem">+ 新建我的第一个任务</button>
553
- <button onclick="closeHelpSheet()" style="width:100%;background:var(--bg);color:var(--muted);border:1px solid var(--border);border-radius:10px;padding:0.65rem;font-size:0.85rem;cursor:pointer;margin-top:0.5rem">我知道了</button>
554
596
  </div>
555
597
 
556
598
  <!-- 随手记:不进会话也能记一条草稿,但必须指定归属任务 -->