@wendongfly/myhi 1.3.52 → 1.3.54
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 +108 -1
- package/dist/index.js +1 -1
- package/dist/index.min.js +101 -90
- package/package.json +1 -1
package/dist/chat.html
CHANGED
|
@@ -664,7 +664,12 @@
|
|
|
664
664
|
let outputBuffer = '';
|
|
665
665
|
let outputTimer = null;
|
|
666
666
|
const OUTPUT_DELAY = 600; // 输出合并延迟(ms),越大越少DOM操作
|
|
667
|
-
const MAX_MESSAGES =
|
|
667
|
+
const MAX_MESSAGES = 500; // 聊天区最大消息数;用户主动向上翻历史时临时禁用 trim
|
|
668
|
+
// 历史分页加载(jsonl 是真相源;500 内存上限和 150 DOM 上限都不够长会话回顾)
|
|
669
|
+
let _historyOffset = 0; // 已加载的最老消息距最新的偏移(含初次 agent:history 回放的)
|
|
670
|
+
let _historyHasMore = true; // 后端还有更老消息
|
|
671
|
+
let _historyLoading = false; // 防抖锁
|
|
672
|
+
let _userScrolledUp = false; // 用户已主动往上翻 → 关掉 trim 防被自动删
|
|
668
673
|
|
|
669
674
|
// 命令历史
|
|
670
675
|
const cmdHistory = [];
|
|
@@ -1442,6 +1447,11 @@
|
|
|
1442
1447
|
chatArea.innerHTML = '';
|
|
1443
1448
|
endStream(); endToolGroup(); removeThinking();
|
|
1444
1449
|
|
|
1450
|
+
// 重置历史分页状态:从最新一批开始算偏移
|
|
1451
|
+
_historyOffset = history.length;
|
|
1452
|
+
_historyHasMore = true;
|
|
1453
|
+
_userScrolledUp = false;
|
|
1454
|
+
|
|
1445
1455
|
for (const msg of history) {
|
|
1446
1456
|
endStream(); // 每条历史消息都是独立的,不要流式合并
|
|
1447
1457
|
if (msg.type === 'user' && msg.content) {
|
|
@@ -1489,6 +1499,96 @@
|
|
|
1489
1499
|
scrollToBottom();
|
|
1490
1500
|
});
|
|
1491
1501
|
|
|
1502
|
+
// ── 历史分页:向上滚动到顶部触发加载更老批次 ──
|
|
1503
|
+
async function loadOlderHistory() {
|
|
1504
|
+
if (_historyLoading || !_historyHasMore) return;
|
|
1505
|
+
_historyLoading = true;
|
|
1506
|
+
// 顶部插入"加载中"提示
|
|
1507
|
+
const indicator = document.createElement('div');
|
|
1508
|
+
indicator.className = 'msg msg-status';
|
|
1509
|
+
indicator.id = '_hist_loading_indicator';
|
|
1510
|
+
indicator.textContent = '— 加载更早的历史… —';
|
|
1511
|
+
chatArea.insertBefore(indicator, chatArea.firstChild);
|
|
1512
|
+
// 记录滚动锚点(保持视觉位置)
|
|
1513
|
+
const prevScrollHeight = chatArea.scrollHeight;
|
|
1514
|
+
const prevScrollTop = chatArea.scrollTop;
|
|
1515
|
+
try {
|
|
1516
|
+
const r = await fetch(`/api/agent/history/${SESSION_ID}?offset=${_historyOffset}&limit=50`);
|
|
1517
|
+
const d = await r.json();
|
|
1518
|
+
indicator.remove();
|
|
1519
|
+
if (!d.ok || !d.messages?.length) {
|
|
1520
|
+
_historyHasMore = false;
|
|
1521
|
+
const end = document.createElement('div');
|
|
1522
|
+
end.className = 'msg msg-status';
|
|
1523
|
+
end.textContent = '— 已到最早 —';
|
|
1524
|
+
chatArea.insertBefore(end, chatArea.firstChild);
|
|
1525
|
+
return;
|
|
1526
|
+
}
|
|
1527
|
+
// 准备 fragment,按 history 渲染逻辑创建 DOM,然后整体 prepend
|
|
1528
|
+
const frag = document.createDocumentFragment();
|
|
1529
|
+
for (const msg of d.messages) {
|
|
1530
|
+
if (msg.type === 'user' && msg.content) {
|
|
1531
|
+
const el = document.createElement('div');
|
|
1532
|
+
el.className = 'msg msg-input';
|
|
1533
|
+
el.innerHTML = `<div><div class="bubble">${escHtml(msg.content)}</div><div class="meta">${msg.timestamp ? new Date(msg.timestamp).toLocaleString('zh-CN') : ''}</div></div>`;
|
|
1534
|
+
frag.appendChild(el);
|
|
1535
|
+
} else if (msg.type === 'assistant' && msg.message?.content) {
|
|
1536
|
+
const texts = [], tools = [];
|
|
1537
|
+
for (const block of msg.message.content) {
|
|
1538
|
+
if (block.type === 'text' && block.text) texts.push(block.text);
|
|
1539
|
+
else if (block.type === 'tool_use') tools.push({ name: block.name || '工具', input: block.input || {} });
|
|
1540
|
+
}
|
|
1541
|
+
if (texts.length) {
|
|
1542
|
+
const el = document.createElement('div');
|
|
1543
|
+
el.className = 'msg msg-assistant';
|
|
1544
|
+
const content = document.createElement('div');
|
|
1545
|
+
content.className = 'content';
|
|
1546
|
+
content.innerHTML = renderMarkdown(texts.join('\n\n'));
|
|
1547
|
+
el.appendChild(content);
|
|
1548
|
+
frag.appendChild(el);
|
|
1549
|
+
}
|
|
1550
|
+
// 历史里工具卡简化为状态行:用户翻历史主要看对话文本,不需要交互
|
|
1551
|
+
for (const t of tools) {
|
|
1552
|
+
const el = document.createElement('div');
|
|
1553
|
+
el.className = 'msg msg-status';
|
|
1554
|
+
el.textContent = '— 🔧 ' + t.name + ' —';
|
|
1555
|
+
frag.appendChild(el);
|
|
1556
|
+
}
|
|
1557
|
+
} else if (msg.type === 'result') {
|
|
1558
|
+
const el = document.createElement('div');
|
|
1559
|
+
el.className = 'msg msg-status';
|
|
1560
|
+
const cost = msg.total_cost_usd ? ` ($${msg.total_cost_usd.toFixed(4)})` : '';
|
|
1561
|
+
el.textContent = '— 完成' + cost + ' —';
|
|
1562
|
+
frag.appendChild(el);
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
chatArea.insertBefore(frag, chatArea.firstChild);
|
|
1566
|
+
_historyOffset += d.messages.length;
|
|
1567
|
+
_historyHasMore = d.hasMore;
|
|
1568
|
+
// 保持滚动位置:新内容在顶部插入后,scrollTop 加上新增高度
|
|
1569
|
+
chatArea.scrollTop = prevScrollTop + (chatArea.scrollHeight - prevScrollHeight);
|
|
1570
|
+
} catch (e) {
|
|
1571
|
+
indicator.textContent = '— 加载失败: ' + e.message + ' —';
|
|
1572
|
+
setTimeout(() => indicator.remove(), 3000);
|
|
1573
|
+
} finally {
|
|
1574
|
+
_historyLoading = false;
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
chatArea.addEventListener('scroll', () => {
|
|
1579
|
+
// 用户主动往上翻 → 禁用自动 trim,避免被删
|
|
1580
|
+
if (chatArea.scrollTop < chatArea.scrollHeight - chatArea.clientHeight - 100) {
|
|
1581
|
+
_userScrolledUp = true;
|
|
1582
|
+
} else if (chatArea.scrollTop > chatArea.scrollHeight - chatArea.clientHeight - 20) {
|
|
1583
|
+
// 回到接近底部 → 恢复 trim
|
|
1584
|
+
_userScrolledUp = false;
|
|
1585
|
+
}
|
|
1586
|
+
// 接近顶部 100px → 拉更老历史
|
|
1587
|
+
if (chatArea.scrollTop < 100 && _historyHasMore && !_historyLoading) {
|
|
1588
|
+
loadOlderHistory();
|
|
1589
|
+
}
|
|
1590
|
+
});
|
|
1591
|
+
|
|
1492
1592
|
socket.on('agent:busy', (busy) => {
|
|
1493
1593
|
if (busy) { showThinking(); setWorkState('thinking'); }
|
|
1494
1594
|
else { removeThinking(); setWorkState('idle'); }
|
|
@@ -1952,6 +2052,7 @@
|
|
|
1952
2052
|
window.goBack = function() { window.location.href = '/'; };
|
|
1953
2053
|
function scrollToBottom() { requestAnimationFrame(() => { chatArea.scrollTop = chatArea.scrollHeight; }); }
|
|
1954
2054
|
function trimMessages() {
|
|
2055
|
+
if (_userScrolledUp) return; // 用户在往上翻历史,禁用自动删
|
|
1955
2056
|
const msgs = chatArea.querySelectorAll('.msg');
|
|
1956
2057
|
if (msgs.length > MAX_MESSAGES) {
|
|
1957
2058
|
const remove = msgs.length - MAX_MESSAGES;
|
|
@@ -3007,6 +3108,12 @@
|
|
|
3007
3108
|
try {
|
|
3008
3109
|
const resp = await fetch('/api/update', { method: 'POST' });
|
|
3009
3110
|
const data = await resp.json();
|
|
3111
|
+
if (!data.ok) {
|
|
3112
|
+
addStatusMessage('更新失败:' + (data.error || '未知错误'));
|
|
3113
|
+
badge.textContent = '⬆ 更新失败';
|
|
3114
|
+
badge.style.color = '#f85149';
|
|
3115
|
+
return;
|
|
3116
|
+
}
|
|
3010
3117
|
addStatusMessage(data.message || '升级中...');
|
|
3011
3118
|
badge.textContent = '⬆ 重启中...';
|
|
3012
3119
|
// 等待服务重启后自动刷新
|