@wendongfly/myhi 1.3.111 → 1.3.113
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 +85 -2
- package/dist/grid.html +73 -12
- package/dist/index.js +1 -1
- package/dist/index.min.js +121 -121
- package/package.json +1 -1
package/dist/chat.html
CHANGED
|
@@ -3612,8 +3612,14 @@
|
|
|
3612
3612
|
`background:${_memScope===val?'var(--purple)':'var(--bg)'};color:${_memScope===val?'#fff':'var(--muted)'}">${label}</button>`;
|
|
3613
3613
|
const makeBar = () => {
|
|
3614
3614
|
const bar = document.createElement('div');
|
|
3615
|
-
bar.style.cssText = 'display:flex;gap:0.4rem;margin-bottom:0.55rem';
|
|
3616
|
-
|
|
3615
|
+
bar.style.cssText = 'display:flex;flex-direction:column;gap:0.4rem;margin-bottom:0.55rem';
|
|
3616
|
+
const row1 = document.createElement('div');
|
|
3617
|
+
row1.style.cssText = 'display:flex;gap:0.4rem';
|
|
3618
|
+
row1.innerHTML = segBtn('project', '本项目') + segBtn('all', '全部(含全局/其它项目)');
|
|
3619
|
+
const row2 = document.createElement('div');
|
|
3620
|
+
row2.style.cssText = 'display:flex;gap:0.4rem';
|
|
3621
|
+
row2.innerHTML = '<button onclick="analyzeMemory()" style="flex:1;padding:0.42rem;border-radius:7px;cursor:pointer;font-size:0.76rem;border:1px solid var(--blue2);background:var(--blue2);color:#fff;font-weight:600">🤖 让 AI 过一遍(找过时/重复/废弃)</button>';
|
|
3622
|
+
bar.appendChild(row1); bar.appendChild(row2);
|
|
3617
3623
|
return bar;
|
|
3618
3624
|
};
|
|
3619
3625
|
|
|
@@ -3659,16 +3665,32 @@
|
|
|
3659
3665
|
if (kw && !(f.name.toLowerCase().includes(kw) || (f.content || '').toLowerCase().includes(kw))) continue;
|
|
3660
3666
|
shown++;
|
|
3661
3667
|
const { desc, body: md } = parseFM(f.content || '');
|
|
3668
|
+
const deletable = f.name.startsWith('memory/') || f.name.startsWith('~/.claude/memory/');
|
|
3669
|
+
const editable = deletable || f.name.endsWith('CLAUDE.md') || f.name.endsWith('settings.json');
|
|
3662
3670
|
const card = document.createElement('div');
|
|
3663
3671
|
card.className = 'mem-file';
|
|
3664
3672
|
const header = document.createElement('div');
|
|
3665
3673
|
header.className = 'mem-file-header';
|
|
3666
3674
|
header.innerHTML = `<span class="arrow">▶</span><span style="flex:1;min-width:0"><span class="mem-title">${escHtml(cleanName(f.name))}</span>${desc ? '<span class="mem-desc">' + escHtml(desc) + '</span>' : ''}</span>`;
|
|
3675
|
+
const acts = document.createElement('span');
|
|
3676
|
+
acts.style.cssText = 'display:flex;gap:0.35rem;flex-shrink:0;align-items:center';
|
|
3677
|
+
if (editable) {
|
|
3678
|
+
const eb = document.createElement('span'); eb.textContent = '✏️'; eb.title = '编辑'; eb.style.cssText = 'cursor:pointer;font-size:0.82rem';
|
|
3679
|
+
eb.onclick = (ev) => { ev.stopPropagation(); startEditMemory(card, f); };
|
|
3680
|
+
acts.appendChild(eb);
|
|
3681
|
+
}
|
|
3682
|
+
if (deletable) {
|
|
3683
|
+
const db = document.createElement('span'); db.textContent = '🗑'; db.title = '删除'; db.style.cssText = 'cursor:pointer;font-size:0.82rem';
|
|
3684
|
+
db.onclick = (ev) => { ev.stopPropagation(); delMemory(f.name); };
|
|
3685
|
+
acts.appendChild(db);
|
|
3686
|
+
}
|
|
3687
|
+
header.appendChild(acts);
|
|
3667
3688
|
const body = document.createElement('div');
|
|
3668
3689
|
body.className = 'mem-file-body md';
|
|
3669
3690
|
body.innerHTML = renderMarkdown(md.trim());
|
|
3670
3691
|
header.onclick = () => { body.classList.toggle('open'); header.querySelector('.arrow').classList.toggle('open'); };
|
|
3671
3692
|
card.appendChild(header); card.appendChild(body);
|
|
3693
|
+
card._file = f; card._body = body;
|
|
3672
3694
|
listWrap.appendChild(card);
|
|
3673
3695
|
}
|
|
3674
3696
|
if (!shown) listWrap.innerHTML = '<div style="color:var(--muted);text-align:center;padding:1.2rem 0;font-size:0.8rem">没有匹配的记忆</div>';
|
|
@@ -3689,6 +3711,67 @@
|
|
|
3689
3711
|
document.getElementById('memory-sheet').classList.remove('open');
|
|
3690
3712
|
};
|
|
3691
3713
|
|
|
3714
|
+
// 让 AI 按规则过一遍记忆(只分析给建议,不改文件)
|
|
3715
|
+
window.analyzeMemory = function() {
|
|
3716
|
+
const scopeTxt = _memScope === 'all' ? '所有项目 + 全局' : '本项目';
|
|
3717
|
+
const prompt = [
|
|
3718
|
+
`请帮我过一遍并分析【${scopeTxt}】的记忆文件(在 ~/.claude/ 下:本项目记忆在对应当前工作目录 cwd 编码后的 projects/<编码>/memory/ 里,`,
|
|
3719
|
+
`全局在 ~/.claude/memory/;先 ls 找到目录再逐个读,可用子代理并行读)。`,
|
|
3720
|
+
`【只分析、给建议,先别改动任何文件】——我会在「记忆」面板里自己确认后维护。判断规则:`,
|
|
3721
|
+
`1) 过时:记忆里的文件/路径/版本/参数/节点状态与现状不符(不确定就标"需核实");`,
|
|
3722
|
+
`2) 重复/重叠:多条讲同一件事、可合并;`,
|
|
3723
|
+
`3) 已废弃/已完成:project_* 里已结束或标注"已弃/完成"的,建议降级为 reference 或删除;`,
|
|
3724
|
+
`4) 索引膨胀:MEMORY.md 有无可精简的行。`,
|
|
3725
|
+
`输出一个表格:文件名 | 类别(过时/重复/废弃/保留) | 原因 | 建议(删除/合并到X/更新/保留);最后一句总结:建议删 N 条、合并 M 组、更新 K 条。`,
|
|
3726
|
+
].join('');
|
|
3727
|
+
closeMemorySheet();
|
|
3728
|
+
if (currentSession?.mode === 'agent') {
|
|
3729
|
+
if (!isController && canTakeControl()) socket.emit('take-control', { sessionId: SESSION_ID });
|
|
3730
|
+
addInputMessage('🤖 让 AI 过一遍记忆(找过时/重复/废弃)');
|
|
3731
|
+
socket.emit('agent:query', { prompt });
|
|
3732
|
+
showThinking();
|
|
3733
|
+
} else {
|
|
3734
|
+
addStatusMessage('该功能仅在 agent(Claude) 会话可用');
|
|
3735
|
+
}
|
|
3736
|
+
};
|
|
3737
|
+
|
|
3738
|
+
// 编辑记忆文件:把该卡片正文换成 textarea(原始 markdown)+ 保存/取消
|
|
3739
|
+
window.startEditMemory = function(card, f) {
|
|
3740
|
+
const wrap = document.createElement('div');
|
|
3741
|
+
wrap.className = 'mem-file-body open';
|
|
3742
|
+
const ta = document.createElement('textarea');
|
|
3743
|
+
ta.value = f.content || '';
|
|
3744
|
+
ta.spellcheck = false;
|
|
3745
|
+
ta.style.cssText = 'width:100%;box-sizing:border-box;min-height:12rem;font-family:\'SF Mono\',Consolas,monospace;font-size:0.75rem;line-height:1.5;background:var(--surface2);color:var(--text);border:1px solid var(--border);border-radius:6px;padding:0.5rem;resize:vertical';
|
|
3746
|
+
const btns = document.createElement('div');
|
|
3747
|
+
btns.style.cssText = 'display:flex;gap:0.5rem;margin-top:0.4rem';
|
|
3748
|
+
const save = document.createElement('button'); save.textContent = '保存';
|
|
3749
|
+
save.style.cssText = 'flex:2;padding:0.45rem;border:none;border-radius:7px;background:var(--blue2);color:#fff;cursor:pointer;font-weight:600';
|
|
3750
|
+
const cancel = document.createElement('button'); cancel.textContent = '取消';
|
|
3751
|
+
cancel.style.cssText = 'flex:1;padding:0.45rem;border:1px solid var(--border);border-radius:7px;background:var(--bg);color:var(--muted);cursor:pointer';
|
|
3752
|
+
save.onclick = async () => {
|
|
3753
|
+
try {
|
|
3754
|
+
const r = await fetch('/api/memory/save', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ sessionId: SESSION_ID, name: f.name, content: ta.value }) });
|
|
3755
|
+
const d = await r.json();
|
|
3756
|
+
if (d.ok) { addStatusMessage('记忆已保存: ' + f.name); showMemory(); } else { alert('保存失败: ' + (d.error||'')); }
|
|
3757
|
+
} catch (e) { alert('保存出错: ' + e.message); }
|
|
3758
|
+
};
|
|
3759
|
+
cancel.onclick = () => showMemory();
|
|
3760
|
+
btns.appendChild(save); btns.appendChild(cancel);
|
|
3761
|
+
wrap.appendChild(ta); wrap.appendChild(btns);
|
|
3762
|
+
card._body.replaceWith(wrap); card._body = wrap;
|
|
3763
|
+
ta.focus();
|
|
3764
|
+
};
|
|
3765
|
+
|
|
3766
|
+
window.delMemory = async function(name) {
|
|
3767
|
+
if (!confirm('删除记忆文件「' + name.replace(/^.*\//,'') + '」?不可撤销。')) return;
|
|
3768
|
+
try {
|
|
3769
|
+
const r = await fetch('/api/memory/delete', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ sessionId: SESSION_ID, name }) });
|
|
3770
|
+
const d = await r.json();
|
|
3771
|
+
if (d.ok) { addStatusMessage('已删除记忆: ' + name); showMemory(); } else { alert('删除失败: ' + (d.error||'')); }
|
|
3772
|
+
} catch (e) { alert('删除出错: ' + e.message); }
|
|
3773
|
+
};
|
|
3774
|
+
|
|
3692
3775
|
// ── Agent 模式快捷功能 ──────────────────────────
|
|
3693
3776
|
let _totalAgentCost = 0;
|
|
3694
3777
|
// 追踪 result 消息中的费用
|
package/dist/grid.html
CHANGED
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
<span class="spacer"></span>
|
|
70
70
|
<button class="bar-btn" onclick="refreshSessions(true)" title="刷新任务列表">↻</button>
|
|
71
71
|
<button class="bar-btn" onclick="addPane('')" title="加一个空窗格,从下拉选已有任务">+ 窗格</button>
|
|
72
|
-
<button class="bar-btn" style="border-color:var(--green);color:var(--green)" onclick="
|
|
72
|
+
<button class="bar-btn" style="border-color:var(--green);color:var(--green)" onclick="openNewTask()" title="新建任务(和首页一致:预设/名称/命令/目录)">🆕 新建任务</button>
|
|
73
73
|
</div>
|
|
74
74
|
<div id="grid"></div>
|
|
75
75
|
<div id="empty-hint" style="display:none">点「+ 窗格」或「🆕 新建任务」把多个任务并排看。<br>拉宽窗口自动多几格,或用上方按钮锁定布局。</div>
|
|
@@ -92,6 +92,26 @@
|
|
|
92
92
|
</div>
|
|
93
93
|
</div>
|
|
94
94
|
|
|
95
|
+
<!-- 新建任务面板(和首页「新建终端会话」一致)-->
|
|
96
|
+
<div id="new-task" style="display:none;position:fixed;inset:0;z-index:100">
|
|
97
|
+
<div style="position:absolute;inset:0;background:rgba(0,0,0,0.5)" onclick="closeNewTask()"></div>
|
|
98
|
+
<div style="position:relative;max-width:460px;width:92%;margin:8vh auto 0;background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:1rem;box-shadow:0 16px 48px rgba(0,0,0,0.45)">
|
|
99
|
+
<div style="font-weight:600;margin-bottom:0.7rem">新建任务</div>
|
|
100
|
+
<div id="nt-presets" style="display:flex;gap:0.4rem;margin-bottom:0.7rem;flex-wrap:wrap"></div>
|
|
101
|
+
<label style="font-size:0.72rem;color:var(--muted)">名称 <span style="color:var(--red)">必填</span></label>
|
|
102
|
+
<input id="nt-title" placeholder="请输入任务名称" autocomplete="off" style="width:100%;box-sizing:border-box;margin:0.2rem 0 0.6rem;background:var(--bg);color:var(--text);border:1px solid var(--border);border-radius:7px;padding:0.45rem 0.6rem;font-size:0.85rem">
|
|
103
|
+
<label style="font-size:0.72rem;color:var(--muted)">启动命令(可选,终端类型用)</label>
|
|
104
|
+
<input id="nt-cmd" placeholder="留空仅打开 shell" autocomplete="off" style="width:100%;box-sizing:border-box;margin:0.2rem 0 0.6rem;background:var(--bg);color:var(--text);border:1px solid var(--border);border-radius:7px;padding:0.45rem 0.6rem;font-size:0.82rem;font-family:monospace">
|
|
105
|
+
<label style="font-size:0.72rem;color:var(--muted)">工作目录(可选)</label>
|
|
106
|
+
<input id="nt-cwd" placeholder="留空使用默认目录" autocomplete="off" style="width:100%;box-sizing:border-box;margin:0.2rem 0 0.4rem;background:var(--bg);color:var(--text);border:1px solid var(--border);border-radius:7px;padding:0.45rem 0.6rem;font-size:0.82rem">
|
|
107
|
+
<div id="nt-recent" style="display:flex;flex-wrap:wrap;gap:0.3rem;margin-bottom:0.7rem"></div>
|
|
108
|
+
<div style="display:flex;gap:0.5rem">
|
|
109
|
+
<button onclick="closeNewTask()" style="flex:1;background:var(--bg);border:1px solid var(--border);color:var(--muted);border-radius:8px;padding:0.5rem;cursor:pointer">取消</button>
|
|
110
|
+
<button onclick="submitNewTask()" style="flex:2;background:var(--green);border:none;color:#fff;border-radius:8px;padding:0.5rem;cursor:pointer;font-weight:600">创建</button>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
95
115
|
<script>
|
|
96
116
|
const LS_KEY = 'myhi_grid_v1';
|
|
97
117
|
const gridEl = document.getElementById('grid');
|
|
@@ -275,19 +295,60 @@
|
|
|
275
295
|
}));
|
|
276
296
|
document.getElementById('tp-search').addEventListener('input', renderPicker);
|
|
277
297
|
|
|
278
|
-
// ──
|
|
298
|
+
// ── 新建任务(和首页「新建终端会话」一致:预设 + 名称 + 命令 + 目录 + 最近目录)──
|
|
279
299
|
const socket = io();
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
300
|
+
const NT_PRESETS = [
|
|
301
|
+
{ icon:'🤖', label:'Claude', title:'claude', type:'agent', cmd:'' },
|
|
302
|
+
{ icon:'✨', label:'Gemini', title:'gemini', type:'pty', cmd:'gemini' },
|
|
303
|
+
{ icon:'🐚', label:'Shell', title:'shell', type:'pty', cmd:'' },
|
|
304
|
+
];
|
|
305
|
+
let _ntPreset = 0;
|
|
306
|
+
const RECENT_DIRS_KEY = 'myhi_recent_dirs'; // 和首页共用
|
|
307
|
+
function ntRecentDirs(){ try { return JSON.parse(localStorage.getItem(RECENT_DIRS_KEY)) || {}; } catch { return {}; } }
|
|
308
|
+
function ntRecordDir(dir){ if(!dir) return; const d=ntRecentDirs(); d[dir]=(d[dir]||0)+1; const s=Object.entries(d).sort((a,b)=>b[1]-a[1]).slice(0,5); localStorage.setItem(RECENT_DIRS_KEY, JSON.stringify(Object.fromEntries(s))); }
|
|
309
|
+
function ntRenderPresets(){
|
|
310
|
+
document.getElementById('nt-presets').innerHTML = NT_PRESETS.map((p,i)=>`<button data-i="${i}" style="flex:1;min-width:5rem;display:flex;align-items:center;justify-content:center;gap:0.3rem;border:1px solid ${i===_ntPreset?'var(--blue2)':'var(--border)'};background:${i===_ntPreset?'var(--blue2)':'var(--bg)'};color:${i===_ntPreset?'#fff':'var(--text2)'};border-radius:8px;padding:0.4rem;font-size:0.8rem;cursor:pointer">${p.icon} ${esc(p.label)}</button>`).join('');
|
|
311
|
+
}
|
|
312
|
+
function ntRenderRecent(){
|
|
313
|
+
const dirs = Object.entries(ntRecentDirs()).sort((a,b)=>b[1]-a[1]);
|
|
314
|
+
document.getElementById('nt-recent').innerHTML = dirs.map(([p])=>`<button data-dir="${esc(p)}" style="background:var(--surface2);border:1px solid var(--border);color:var(--muted);border-radius:6px;font-size:0.68rem;padding:0.12rem 0.4rem;cursor:pointer;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(p)}</button>`).join('');
|
|
315
|
+
}
|
|
316
|
+
window.openNewTask = function(){
|
|
317
|
+
_ntPreset = 0;
|
|
318
|
+
document.getElementById('new-task').style.display = '';
|
|
319
|
+
document.getElementById('nt-title').value = '';
|
|
320
|
+
document.getElementById('nt-cmd').value = '';
|
|
321
|
+
document.getElementById('nt-cwd').value = '';
|
|
322
|
+
ntRenderPresets(); ntRenderRecent();
|
|
323
|
+
setTimeout(()=>document.getElementById('nt-title').focus(), 30);
|
|
324
|
+
};
|
|
325
|
+
window.closeNewTask = function(){ document.getElementById('new-task').style.display='none'; };
|
|
326
|
+
window.submitNewTask = function(){
|
|
327
|
+
const title = document.getElementById('nt-title').value.trim();
|
|
328
|
+
if (!title) { document.getElementById('nt-title').focus(); return; }
|
|
329
|
+
const cmd = document.getElementById('nt-cmd').value.trim() || undefined;
|
|
330
|
+
const cwd = document.getElementById('nt-cwd').value.trim() || undefined;
|
|
331
|
+
if (cwd) ntRecordDir(cwd);
|
|
332
|
+
const preset = NT_PRESETS[_ntPreset];
|
|
333
|
+
const done = (res) => {
|
|
334
|
+
if (res && res.ok && res.session) { closeNewTask(); refreshSessions(true).then(()=>addPane(res.session.id)); }
|
|
335
|
+
else alert('创建失败:' + ((res && res.error) || '未知错误'));
|
|
336
|
+
};
|
|
337
|
+
if (preset.type === 'agent') socket.emit('create-agent', { title, cwd }, done);
|
|
338
|
+
else socket.emit('create', { title, initCmd: cmd, cwd }, done);
|
|
290
339
|
};
|
|
340
|
+
document.getElementById('nt-presets').addEventListener('click', (e) => {
|
|
341
|
+
const b = e.target.closest('[data-i]'); if (!b) return;
|
|
342
|
+
_ntPreset = +b.dataset.i; const p = NT_PRESETS[_ntPreset];
|
|
343
|
+
const t = document.getElementById('nt-title');
|
|
344
|
+
if (!t.value.trim() || NT_PRESETS.some(x => x.title === t.value.trim())) t.value = p.title;
|
|
345
|
+
document.getElementById('nt-cmd').value = p.cmd || '';
|
|
346
|
+
ntRenderPresets();
|
|
347
|
+
});
|
|
348
|
+
document.getElementById('nt-recent').addEventListener('click', (e) => {
|
|
349
|
+
const b = e.target.closest('[data-dir]'); if (!b) return;
|
|
350
|
+
document.getElementById('nt-cwd').value = b.dataset.dir;
|
|
351
|
+
});
|
|
291
352
|
|
|
292
353
|
// ── 初始化 ──
|
|
293
354
|
(async function init() {
|