@wendongfly/myhi 1.3.113 → 1.3.115

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
@@ -313,7 +313,10 @@
313
313
  .mem-title { color: var(--blue); font-weight: 600; }
314
314
  .mem-desc { display: block; color: var(--muted); font-size: 0.68rem; font-weight: 400; margin-top: 0.12rem; line-height: 1.4; white-space: normal; }
315
315
  /* PC 上记忆面板加宽,别挤在窄底 sheet 里 */
316
- @media (min-width: 900px) { #memory-sheet .action-sheet-box { max-width: 820px; } }
316
+ /* 记忆面板改成近全屏大面板(居中而非贴底),不再挤在窄弹窗里 */
317
+ #memory-sheet.action-sheet.open { align-items: center; }
318
+ #memory-sheet .action-sheet-box { width: 94vw; max-width: 980px; border-radius: 14px 14px 14px 14px; padding-bottom: 0.75rem; }
319
+ #memory-content { flex: 1; min-height: 0; }
317
320
  .slash-inp { width: 100%; padding: 0.65rem 0.8rem; background: var(--bg); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: 0.9rem; outline: none; }
318
321
  .slash-inp:focus { border-color: var(--purple); }
319
322
 
@@ -446,6 +449,12 @@
446
449
  <button class="fp-btn" onclick="fpExitSync()">退出同步</button>
447
450
  </div>
448
451
  <div class="fp-path" id="fp-path"></div>
452
+ <div id="fp-tools" style="display:flex;gap:0.4rem;align-items:center;padding:0.3rem 0.75rem;flex-wrap:wrap">
453
+ <button class="fp-btn" onclick="fpOpenFolder()" title="在服务端机器打开系统文件夹(你就坐在跑 myhi 的机器时有效)">📂 打开文件夹</button>
454
+ <button class="fp-btn" onclick="fpOpenEditor()" title="用 VS Code 打开项目(本机、装了 code 命令时有效)">VS Code</button>
455
+ <span style="flex:1"></span>
456
+ <button class="fp-btn" id="fp-sort-btn" onclick="fpToggleSort()" title="切换排序">排序: 名称</button>
457
+ </div>
449
458
  <div class="fp-list" id="fp-list"></div>
450
459
  <div class="fp-diff-actions" id="fp-diff-actions" style="display:none">
451
460
  <button class="fp-btn fp-btn-primary" onclick="fpSyncToLocal()">下载到本地</button>
@@ -609,7 +618,7 @@
609
618
 
610
619
  <div id="memory-sheet" class="action-sheet">
611
620
  <div class="action-sheet-backdrop" onclick="closeMemorySheet()"></div>
612
- <div class="action-sheet-box" style="max-height:80vh;display:flex;flex-direction:column">
621
+ <div class="action-sheet-box" style="height:90vh;max-height:90vh;display:flex;flex-direction:column">
613
622
  <div class="action-sheet-title">Claude 记忆</div>
614
623
  <div id="memory-content" style="flex:1;overflow-y:auto;padding:0.3rem 0;font-size:0.78rem;color:var(--text2);scrollbar-width:thin">
615
624
  <div style="text-align:center;color:var(--muted);padding:2rem 0">加载中...</div>
@@ -1580,6 +1589,13 @@
1580
1589
  cancelBtn.classList.toggle('show', busy);
1581
1590
  sendBtn.style.display = busy ? 'none' : '';
1582
1591
  }
1592
+ _postStatus(state);
1593
+ }
1594
+ // 内嵌分屏(iframe)时把状态上报给外层 grid,更新窗格状态灯
1595
+ function _postStatus(state) {
1596
+ if (window.parent && window.parent !== window) {
1597
+ try { window.parent.postMessage({ type: 'myhi-status', sessionId: SESSION_ID, state }, '*'); } catch {}
1598
+ }
1583
1599
  }
1584
1600
 
1585
1601
  function updateShortcutBar() {
@@ -2193,6 +2209,27 @@
2193
2209
 
2194
2210
  // ── 文件管理 ──────────────────────────────────
2195
2211
  let fpCurrentPath = '';
2212
+ let fpSort = 'name'; // name | recent
2213
+ window.fpToggleSort = function() {
2214
+ fpSort = fpSort === 'name' ? 'recent' : 'name';
2215
+ const b = document.getElementById('fp-sort-btn');
2216
+ if (b) b.textContent = '排序: ' + (fpSort === 'recent' ? '最近修改' : '名称');
2217
+ fpLoadFiles();
2218
+ };
2219
+ window.fpOpenFolder = async function() {
2220
+ try {
2221
+ const r = await fetch(`/api/session/${SESSION_ID}/open-folder`, { method: 'POST' });
2222
+ const d = await r.json();
2223
+ addStatusMessage(d.ok ? '已在服务端机器打开文件夹(远程访问则看不到,用下方列表浏览/下载)' : ('打开失败: ' + (d.error || '')));
2224
+ } catch (e) { addStatusMessage('打开出错: ' + e.message); }
2225
+ };
2226
+ window.fpOpenEditor = async function() {
2227
+ try {
2228
+ const r = await fetch(`/api/session/${SESSION_ID}/open-editor`, { method: 'POST' });
2229
+ const d = await r.json();
2230
+ addStatusMessage(d.ok ? '已尝试用 VS Code 打开(需服务端装了 code 命令且你在本机)' : ('打开失败: ' + (d.error || '')));
2231
+ } catch (e) { addStatusMessage('打开出错: ' + e.message); }
2232
+ };
2196
2233
  window.openFilePanel = function() {
2197
2234
  fpCurrentPath = '';
2198
2235
  fpLoadFiles();
@@ -2220,7 +2257,10 @@
2220
2257
  if (data.parent != null) {
2221
2258
  html += `<div class="fp-item" onclick="fpNav('${escHtml(data.parent)}')"><span class="fp-icon">📁</span><span class="fp-name">..</span></div>`;
2222
2259
  }
2223
- for (const e of data.entries) {
2260
+ const entries = (data.entries || []).slice();
2261
+ if (fpSort === 'recent') entries.sort((a, b) => (b.mtime || 0) - (a.mtime || 0)); // 最近修改在前,看 AI 刚生成啥
2262
+ else entries.sort((a, b) => (Number(b.isDir) - Number(a.isDir)) || a.name.localeCompare(b.name));
2263
+ for (const e of entries) {
2224
2264
  const rel = fpCurrentPath ? fpCurrentPath + '/' + e.name : e.name;
2225
2265
  if (e.isDir) {
2226
2266
  html += `<div class="fp-item" onclick="fpNav('${escHtml(rel)}')"><span class="fp-icon">📁</span><span class="fp-name">${escHtml(e.name)}</span></div>`;
@@ -2737,6 +2777,7 @@
2737
2777
  }
2738
2778
  if (!_askQuestions.length) return;
2739
2779
  _renderAskSheet();
2780
+ _postStatus('waiting'); // 通知外层 grid:本格在等你回答
2740
2781
  };
2741
2782
 
2742
2783
  function _renderAskSheet() {
package/dist/grid.html CHANGED
@@ -47,6 +47,12 @@
47
47
  .pane-head select { flex:1; min-width:0; background:var(--bg); color:var(--text); border:1px solid var(--border); border-radius:6px; padding:0.28rem 0.4rem; font-size:0.78rem; }
48
48
  .pane-head .pane-picker { flex:1; min-width:0; display:flex; align-items:center; justify-content:space-between; gap:0.3rem; text-align:left; }
49
49
  #tp-list [data-id]:hover { background:var(--surface2) !important; }
50
+ /* 窗格状态灯 */
51
+ .pane-dot { width:9px; height:9px; border-radius:50%; background:var(--dim); flex-shrink:0; }
52
+ .pane-dot.pulse { animation:panepulse 1.1s ease-in-out infinite; }
53
+ @keyframes panepulse { 0%,100%{opacity:1} 50%{opacity:0.3} }
54
+ .pane-head.flash { animation:paneflash 1s ease-in-out 3; }
55
+ @keyframes paneflash { 0%,100%{background:var(--surface2)} 50%{background:var(--blue2)} }
50
56
  .pane-head button { background:var(--bg); border:1px solid var(--border); color:var(--muted); border-radius:6px; padding:0.2rem 0.45rem; font-size:0.8rem; cursor:pointer; line-height:1; }
51
57
  .pane-head button:hover { color:var(--text); border-color:var(--blue2); }
52
58
  .pane-body { flex:1; min-height:0; position:relative; }
@@ -67,6 +73,7 @@
67
73
  <button data-lo="3">3列</button>
68
74
  </div>
69
75
  <span class="spacer"></span>
76
+ <button class="bar-btn" id="notify-btn" onclick="toggleNotify()" title="任务完成/待回答时桌面通知+提示音(默认关)">🔔 关</button>
70
77
  <button class="bar-btn" onclick="refreshSessions(true)" title="刷新任务列表">↻</button>
71
78
  <button class="bar-btn" onclick="addPane('')" title="加一个空窗格,从下拉选已有任务">+ 窗格</button>
72
79
  <button class="bar-btn" style="border-color:var(--green);color:var(--green)" onclick="openNewTask()" title="新建任务(和首页一致:预设/名称/命令/目录)">🆕 新建任务</button>
@@ -174,10 +181,12 @@
174
181
  function addPane(sid, skipSave) {
175
182
  const pane = document.createElement('div'); pane.className = 'pane'; pane._sid = sid || '';
176
183
  const head = document.createElement('div'); head.className = 'pane-head';
184
+ const dot = document.createElement('span'); dot.className = 'pane-dot'; dot.title = '任务状态';
177
185
  const picker = document.createElement('button'); picker.className = 'pane-picker';
178
186
  const full = document.createElement('button'); full.textContent='↗'; full.title='在新标签页打开';
179
187
  const close = document.createElement('button'); close.textContent='✕'; close.title='关闭此格'; close.style.color='var(--red)';
180
- head.appendChild(picker); head.appendChild(full); head.appendChild(close);
188
+ head.appendChild(dot); head.appendChild(picker); head.appendChild(full); head.appendChild(close);
189
+ pane._dot = dot; pane._head = head;
181
190
  const body = document.createElement('div'); body.className = 'pane-body';
182
191
  const empty = document.createElement('div'); empty.className='pane-empty'; empty.textContent='点上方「选择任务」挑一个(可搜索/分组/按标签)'; body.appendChild(empty);
183
192
  pane.appendChild(head); pane.appendChild(body);
@@ -350,6 +359,44 @@
350
359
  document.getElementById('nt-cwd').value = b.dataset.dir;
351
360
  });
352
361
 
362
+ // ── 窗格状态灯 + 完成/待回答高亮 + 通知(内嵌会话页 postMessage 上报)──
363
+ let _notify = localStorage.getItem('myhi_grid_notify') === '1';
364
+ function syncNotifyBtn(){ const b=document.getElementById('notify-btn'); if(b) b.textContent='🔔 '+(_notify?'开':'关'); }
365
+ window.toggleNotify = function(){
366
+ _notify = !_notify;
367
+ if (_notify && 'Notification' in window && Notification.permission==='default') Notification.requestPermission();
368
+ localStorage.setItem('myhi_grid_notify', _notify?'1':'0');
369
+ syncNotifyBtn();
370
+ };
371
+ syncNotifyBtn();
372
+ function paneTitle(pane){ const s = pane._sid ? sessById(pane._sid) : null; return s ? sName(s) : '任务'; }
373
+ 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 {} }
374
+ function fireNotify(pane,label){
375
+ if(!_notify) return;
376
+ try { if('Notification' in window && Notification.permission==='granted') new Notification('myhi · '+label,{body:paneTitle(pane)}); } catch {}
377
+ beep();
378
+ }
379
+ function setPaneStatus(pane,state){
380
+ const prev=pane._state; pane._state=state;
381
+ if(pane._dot){
382
+ const c={idle:'var(--green)',working:'var(--blue)',thinking:'var(--blue)',waiting:'#d29922'}[state]||'var(--dim)';
383
+ pane._dot.style.background=c;
384
+ pane._dot.classList.toggle('pulse', state==='working'||state==='thinking');
385
+ pane._dot.title={idle:'空闲/已完成',working:'执行中',thinking:'执行中',waiting:'等你回答'}[state]||'状态';
386
+ }
387
+ const justDone=(prev==='working'||prev==='thinking')&&state==='idle';
388
+ if(justDone||state==='waiting'){
389
+ if(pane._head){ pane._head.classList.remove('flash'); void pane._head.offsetWidth; pane._head.classList.add('flash'); setTimeout(()=>pane._head&&pane._head.classList.remove('flash'),3200); }
390
+ fireNotify(pane, state==='waiting'?'待回答':'已完成');
391
+ }
392
+ }
393
+ window.addEventListener('message',(e)=>{
394
+ const m=e.data;
395
+ if(!m||m.type!=='myhi-status') return;
396
+ const pane=paneEls.find(p=>p._sid&&p._sid===m.sessionId);
397
+ if(pane) setPaneStatus(pane,m.state);
398
+ });
399
+
353
400
  // ── 初始化 ──
354
401
  (async function init() {
355
402
  await refreshSessions(false);