@wendongfly/myhi 1.3.95 → 1.3.97
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 +157 -127
- package/dist/index.js +1 -1
- package/dist/index.min.js +1 -1
- package/package.json +1 -1
package/dist/chat.html
CHANGED
|
@@ -1581,7 +1581,6 @@
|
|
|
1581
1581
|
break;
|
|
1582
1582
|
case 'result':
|
|
1583
1583
|
removeThinking();
|
|
1584
|
-
setWorkState('idle');
|
|
1585
1584
|
// msg.result 是整轮的最终答复文本,纯文字回答时与本轮已渲染的文本重复;
|
|
1586
1585
|
// 本轮已经通过流式或 assistant 事件渲染过就跳过,避免生成第二个独立气泡
|
|
1587
1586
|
if (msg.result && !_textRenderedThisTurn) {
|
|
@@ -1591,6 +1590,9 @@
|
|
|
1591
1590
|
endStream();
|
|
1592
1591
|
const cost = msg.total_cost_usd ? ` ($${msg.total_cost_usd.toFixed(4)})` : '';
|
|
1593
1592
|
addStatusMessage(`完成${cost}`);
|
|
1593
|
+
// 关键:idle 放最后——渲染最终答复的 addAssistantMessage 会把状态置回 'working',
|
|
1594
|
+
// 若在其之前置 idle 会被顶回「执行中」+ 取消按钮不复位。渲染全部完成后再回 idle。
|
|
1595
|
+
setWorkState('idle');
|
|
1594
1596
|
break;
|
|
1595
1597
|
case 'content_block_start':
|
|
1596
1598
|
// 流式内容块开始:thinking 时创建占位,由 thinking_delta 增量填充;
|
|
@@ -1716,6 +1718,11 @@
|
|
|
1716
1718
|
_textRenderedThisTurn = true;
|
|
1717
1719
|
}
|
|
1718
1720
|
|
|
1721
|
+
// 回放是历史快照:结尾按会话真实忙闲复位状态。否则回放里最后一个 addToolMessage
|
|
1722
|
+
// 把状态留在 'working',会造成「已完成的会话重进/刷新后仍显示执行中、发送键停在红色取消方框」。
|
|
1723
|
+
// 中途 socket 重连(有 inflight 流式)或会话确实在忙 → working;否则 idle。
|
|
1724
|
+
setWorkState((inflightText || currentSession?.busy) ? 'working' : 'idle');
|
|
1725
|
+
|
|
1719
1726
|
scrollToBottom();
|
|
1720
1727
|
});
|
|
1721
1728
|
|
|
@@ -2320,89 +2327,89 @@
|
|
|
2320
2327
|
if (diff < 172800000) return '昨天';
|
|
2321
2328
|
return (d.getMonth() + 1) + '月' + d.getDate() + '日';
|
|
2322
2329
|
}
|
|
2323
|
-
function switchSessTime(s) {
|
|
2324
|
-
return (s.lastActivityAt || (s.createdAt ? new Date(s.createdAt).getTime() : 0)) || 0;
|
|
2325
|
-
}
|
|
2326
|
-
window.openSwitchSheet = async function() {
|
|
2327
|
-
let sessions = [];
|
|
2328
|
-
try {
|
|
2329
|
-
const res = await fetch('/api/sessions', { headers: { 'Cache-Control': 'no-cache' } });
|
|
2330
|
-
sessions = await res.json();
|
|
2331
|
-
} catch { addStatusMessage && addStatusMessage('会话列表加载失败'); return; }
|
|
2332
|
-
sessions.sort((a, b) => switchSessTime(b) - switchSessTime(a)); // 最新在前
|
|
2333
|
-
const esc2 = (s) => { const d = document.createElement('div'); d.textContent = s == null ? '' : s; return d.innerHTML; };
|
|
2334
|
-
let mode = localStorage.getItem('myhi_group_mode') || 'time'; // 与首页共用记忆
|
|
2335
|
-
|
|
2336
|
-
const rowHTML = (s) => {
|
|
2337
|
-
const cur = s.id === SESSION_ID;
|
|
2338
|
-
const proj = (s.cwd || '').replace(/\\/g, '/').split('/').filter(Boolean).slice(-1)[0] || '';
|
|
2339
|
-
const tags = (s.tags || []).map(x => '<span style="font-size:0.62rem;color:#a9b6c2;background:rgba(124,58,237,0.14);border:1px solid rgba(124,58,237,0.35);border-radius:999px;padding:0 0.4rem">' + esc2(x) + '</span>').join(' ');
|
|
2340
|
-
const sub = [proj, switchRelTime(switchSessTime(s)), s.busy ? '运行中' : '', s.controlHolderName ? (s.controlHolderName + ' 控制中') : ''].filter(Boolean).join(' · ');
|
|
2341
|
-
return '<div data-sid="' + esc2(s.id) + '" style="display:flex;align-items:center;gap:0.6rem;padding:0.7rem 1.1rem;border-top:1px solid #21262d;cursor:pointer;' + (cur ? 'background:rgba(124,58,237,0.12)' : '') + '">' +
|
|
2342
|
-
'<span style="width:8px;height:8px;border-radius:50%;flex-shrink:0;background:' + (s.alive ? '#3fb950' : '#6e7681') + '"></span>' +
|
|
2343
|
-
'<div style="flex:1;min-width:0">' +
|
|
2344
|
-
'<div style="font-size:0.9rem;color:#e6edf3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + esc2(s.title || '(无标题)') + ' ' + (cur ? '<span style="font-size:0.68rem;color:#7c3aed">· 当前</span>' : '') + '</div>' +
|
|
2345
|
-
'<div style="font-size:0.72rem;color:#8b949e;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + esc2(sub) + '</div>' +
|
|
2346
|
-
(tags ? '<div style="display:flex;flex-wrap:wrap;gap:0.25rem;margin-top:0.25rem">' + tags + '</div>' : '') +
|
|
2347
|
-
'</div></div>';
|
|
2348
|
-
};
|
|
2349
|
-
const groupHdr = (label, n) => '<div style="padding:0.5rem 1.1rem 0.3rem;color:#8b949e;font-size:0.72rem;font-weight:600;background:#0d1117">' + esc2(label) + ' · <span style="font-weight:400">' + n + '</span></div>';
|
|
2350
|
-
const pushInto = (map, key, s) => { if (!map.has(key)) map.set(key, []); map.get(key).push(s); };
|
|
2351
|
-
|
|
2352
|
-
const buildBody = () => {
|
|
2353
|
-
if (!sessions.length) return '<div style="padding:1rem;color:#8b949e;text-align:center">暂无其他会话</div>';
|
|
2354
|
-
if (mode === 'project') {
|
|
2355
|
-
const g = new Map();
|
|
2356
|
-
for (const s of sessions) pushInto(g, (s.cwd || '').replace(/\\/g, '/').split('/').filter(Boolean).slice(-1)[0] || '(默认目录)', s);
|
|
2357
|
-
return [...g.entries()].sort((a, b) => switchSessTime(b[1][0]) - switchSessTime(a[1][0]))
|
|
2358
|
-
.map(([name, arr]) => groupHdr('📁 ' + name, arr.length) + arr.map(rowHTML).join('')).join('');
|
|
2359
|
-
}
|
|
2360
|
-
if (mode === 'tag') {
|
|
2361
|
-
const g = new Map(); const untag = [];
|
|
2362
|
-
for (const s of sessions) { const ts = s.tags || []; if (!ts.length) { untag.push(s); continue; } for (const t of ts) pushInto(g, t, s); }
|
|
2363
|
-
let h = [...g.entries()].sort((a, b) => switchSessTime(b[1][0]) - switchSessTime(a[1][0]))
|
|
2364
|
-
.map(([name, arr]) => groupHdr('🏷 ' + name, arr.length) + arr.map(rowHTML).join('')).join('');
|
|
2365
|
-
if (untag.length) h += groupHdr('未分类', untag.length) + untag.map(rowHTML).join('');
|
|
2366
|
-
return h;
|
|
2367
|
-
}
|
|
2368
|
-
return sessions.map(rowHTML).join('');
|
|
2369
|
-
};
|
|
2370
|
-
|
|
2371
|
-
const overlay = document.createElement('div');
|
|
2372
|
-
overlay.style.cssText = 'position:fixed;inset:0;z-index:120;background:rgba(0,0,0,0.6);display:flex;align-items:flex-end;justify-content:center';
|
|
2373
|
-
const box = document.createElement('div');
|
|
2374
|
-
box.style.cssText = 'background:#161b22;border-radius:18px 18px 0 0;width:100%;max-width:640px;max-height:78vh;display:flex;flex-direction:column;padding-bottom:max(0.8rem,env(safe-area-inset-bottom))';
|
|
2375
|
-
const segBtn = (m, label) => '<button class="sw-seg" data-m="' + m + '" style="background:' + (mode === m ? '#7c3aed' : 'none') + ';color:' + (mode === m ? '#fff' : '#8b949e') + ';border:none;font-size:0.74rem;padding:0.3rem 0.7rem;cursor:pointer;' + (m !== 'time' ? 'border-left:1px solid #30363d' : '') + '">' + label + '</button>';
|
|
2376
|
-
box.innerHTML = '<div style="padding:0.75rem 1.1rem 0.5rem;display:flex;justify-content:space-between;align-items:center;gap:0.5rem">' +
|
|
2377
|
-
'<span style="font-size:0.85rem;font-weight:600;color:#e6edf3">切换会话(' + sessions.length + ')</span>' +
|
|
2378
|
-
'<div style="display:flex;align-items:center;gap:0.5rem">' +
|
|
2379
|
-
'<div style="display:inline-flex;border:1px solid #30363d;border-radius:7px;overflow:hidden">' + segBtn('time', '最近') + segBtn('project', '项目') + segBtn('tag', '标签') + '</div>' +
|
|
2380
|
-
'<button id="sw-home" style="background:none;border:1px solid #30363d;color:#8b949e;font-size:0.72rem;padding:0.25rem 0.6rem;border-radius:6px;cursor:pointer">+ 新建</button>' +
|
|
2381
|
-
'</div>' +
|
|
2382
|
-
'</div>' +
|
|
2383
|
-
'<div id="sw-body" style="overflow:auto">' + buildBody() + '</div>' +
|
|
2384
|
-
'<button id="sw-cancel" style="margin:0.5rem 1.1rem 0;background:none;border:1px solid #30363d;color:#8b949e;font-size:0.85rem;padding:0.7rem;border-radius:10px;cursor:pointer">取消</button>';
|
|
2385
|
-
overlay.appendChild(box);
|
|
2386
|
-
const close = () => overlay.remove();
|
|
2387
|
-
overlay.addEventListener('click', (e) => { if (e.target === overlay) close(); });
|
|
2388
|
-
box.querySelector('#sw-cancel').onclick = close;
|
|
2389
|
-
box.querySelector('#sw-home').onclick = () => { window.location.href = '/'; };
|
|
2390
|
-
const bindRows = () => box.querySelectorAll('[data-sid]').forEach(row => row.onclick = () => {
|
|
2391
|
-
const sid = row.dataset.sid;
|
|
2392
|
-
if (sid && sid !== SESSION_ID) window.location.href = '/terminal/' + sid;
|
|
2393
|
-
else close();
|
|
2394
|
-
});
|
|
2395
|
-
bindRows();
|
|
2396
|
-
box.querySelectorAll('.sw-seg').forEach(b => b.onclick = () => {
|
|
2397
|
-
mode = b.dataset.m;
|
|
2398
|
-
localStorage.setItem('myhi_group_mode', mode);
|
|
2399
|
-
box.querySelectorAll('.sw-seg').forEach(x => { const on = x.dataset.m === mode; x.style.background = on ? '#7c3aed' : 'none'; x.style.color = on ? '#fff' : '#8b949e'; });
|
|
2400
|
-
box.querySelector('#sw-body').innerHTML = buildBody();
|
|
2401
|
-
bindRows();
|
|
2402
|
-
});
|
|
2403
|
-
document.body.appendChild(overlay);
|
|
2404
|
-
};
|
|
2405
|
-
|
|
2330
|
+
function switchSessTime(s) {
|
|
2331
|
+
return (s.lastActivityAt || (s.createdAt ? new Date(s.createdAt).getTime() : 0)) || 0;
|
|
2332
|
+
}
|
|
2333
|
+
window.openSwitchSheet = async function() {
|
|
2334
|
+
let sessions = [];
|
|
2335
|
+
try {
|
|
2336
|
+
const res = await fetch('/api/sessions', { headers: { 'Cache-Control': 'no-cache' } });
|
|
2337
|
+
sessions = await res.json();
|
|
2338
|
+
} catch { addStatusMessage && addStatusMessage('会话列表加载失败'); return; }
|
|
2339
|
+
sessions.sort((a, b) => switchSessTime(b) - switchSessTime(a)); // 最新在前
|
|
2340
|
+
const esc2 = (s) => { const d = document.createElement('div'); d.textContent = s == null ? '' : s; return d.innerHTML; };
|
|
2341
|
+
let mode = localStorage.getItem('myhi_group_mode') || 'time'; // 与首页共用记忆
|
|
2342
|
+
|
|
2343
|
+
const rowHTML = (s) => {
|
|
2344
|
+
const cur = s.id === SESSION_ID;
|
|
2345
|
+
const proj = (s.cwd || '').replace(/\\/g, '/').split('/').filter(Boolean).slice(-1)[0] || '';
|
|
2346
|
+
const tags = (s.tags || []).map(x => '<span style="font-size:0.62rem;color:#a9b6c2;background:rgba(124,58,237,0.14);border:1px solid rgba(124,58,237,0.35);border-radius:999px;padding:0 0.4rem">' + esc2(x) + '</span>').join(' ');
|
|
2347
|
+
const sub = [proj, switchRelTime(switchSessTime(s)), s.busy ? '运行中' : '', s.controlHolderName ? (s.controlHolderName + ' 控制中') : ''].filter(Boolean).join(' · ');
|
|
2348
|
+
return '<div data-sid="' + esc2(s.id) + '" style="display:flex;align-items:center;gap:0.6rem;padding:0.7rem 1.1rem;border-top:1px solid #21262d;cursor:pointer;' + (cur ? 'background:rgba(124,58,237,0.12)' : '') + '">' +
|
|
2349
|
+
'<span style="width:8px;height:8px;border-radius:50%;flex-shrink:0;background:' + (s.alive ? '#3fb950' : '#6e7681') + '"></span>' +
|
|
2350
|
+
'<div style="flex:1;min-width:0">' +
|
|
2351
|
+
'<div style="font-size:0.9rem;color:#e6edf3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + esc2(s.title || '(无标题)') + ' ' + (cur ? '<span style="font-size:0.68rem;color:#7c3aed">· 当前</span>' : '') + '</div>' +
|
|
2352
|
+
'<div style="font-size:0.72rem;color:#8b949e;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + esc2(sub) + '</div>' +
|
|
2353
|
+
(tags ? '<div style="display:flex;flex-wrap:wrap;gap:0.25rem;margin-top:0.25rem">' + tags + '</div>' : '') +
|
|
2354
|
+
'</div></div>';
|
|
2355
|
+
};
|
|
2356
|
+
const groupHdr = (label, n) => '<div style="padding:0.5rem 1.1rem 0.3rem;color:#8b949e;font-size:0.72rem;font-weight:600;background:#0d1117">' + esc2(label) + ' · <span style="font-weight:400">' + n + '</span></div>';
|
|
2357
|
+
const pushInto = (map, key, s) => { if (!map.has(key)) map.set(key, []); map.get(key).push(s); };
|
|
2358
|
+
|
|
2359
|
+
const buildBody = () => {
|
|
2360
|
+
if (!sessions.length) return '<div style="padding:1rem;color:#8b949e;text-align:center">暂无其他会话</div>';
|
|
2361
|
+
if (mode === 'project') {
|
|
2362
|
+
const g = new Map();
|
|
2363
|
+
for (const s of sessions) pushInto(g, (s.cwd || '').replace(/\\/g, '/').split('/').filter(Boolean).slice(-1)[0] || '(默认目录)', s);
|
|
2364
|
+
return [...g.entries()].sort((a, b) => switchSessTime(b[1][0]) - switchSessTime(a[1][0]))
|
|
2365
|
+
.map(([name, arr]) => groupHdr('📁 ' + name, arr.length) + arr.map(rowHTML).join('')).join('');
|
|
2366
|
+
}
|
|
2367
|
+
if (mode === 'tag') {
|
|
2368
|
+
const g = new Map(); const untag = [];
|
|
2369
|
+
for (const s of sessions) { const ts = s.tags || []; if (!ts.length) { untag.push(s); continue; } for (const t of ts) pushInto(g, t, s); }
|
|
2370
|
+
let h = [...g.entries()].sort((a, b) => switchSessTime(b[1][0]) - switchSessTime(a[1][0]))
|
|
2371
|
+
.map(([name, arr]) => groupHdr('🏷 ' + name, arr.length) + arr.map(rowHTML).join('')).join('');
|
|
2372
|
+
if (untag.length) h += groupHdr('未分类', untag.length) + untag.map(rowHTML).join('');
|
|
2373
|
+
return h;
|
|
2374
|
+
}
|
|
2375
|
+
return sessions.map(rowHTML).join('');
|
|
2376
|
+
};
|
|
2377
|
+
|
|
2378
|
+
const overlay = document.createElement('div');
|
|
2379
|
+
overlay.style.cssText = 'position:fixed;inset:0;z-index:120;background:rgba(0,0,0,0.6);display:flex;align-items:flex-end;justify-content:center';
|
|
2380
|
+
const box = document.createElement('div');
|
|
2381
|
+
box.style.cssText = 'background:#161b22;border-radius:18px 18px 0 0;width:100%;max-width:640px;max-height:78vh;display:flex;flex-direction:column;padding-bottom:max(0.8rem,env(safe-area-inset-bottom))';
|
|
2382
|
+
const segBtn = (m, label) => '<button class="sw-seg" data-m="' + m + '" style="background:' + (mode === m ? '#7c3aed' : 'none') + ';color:' + (mode === m ? '#fff' : '#8b949e') + ';border:none;font-size:0.74rem;padding:0.3rem 0.7rem;cursor:pointer;' + (m !== 'time' ? 'border-left:1px solid #30363d' : '') + '">' + label + '</button>';
|
|
2383
|
+
box.innerHTML = '<div style="padding:0.75rem 1.1rem 0.5rem;display:flex;justify-content:space-between;align-items:center;gap:0.5rem">' +
|
|
2384
|
+
'<span style="font-size:0.85rem;font-weight:600;color:#e6edf3">切换会话(' + sessions.length + ')</span>' +
|
|
2385
|
+
'<div style="display:flex;align-items:center;gap:0.5rem">' +
|
|
2386
|
+
'<div style="display:inline-flex;border:1px solid #30363d;border-radius:7px;overflow:hidden">' + segBtn('time', '最近') + segBtn('project', '项目') + segBtn('tag', '标签') + '</div>' +
|
|
2387
|
+
'<button id="sw-home" style="background:none;border:1px solid #30363d;color:#8b949e;font-size:0.72rem;padding:0.25rem 0.6rem;border-radius:6px;cursor:pointer">+ 新建</button>' +
|
|
2388
|
+
'</div>' +
|
|
2389
|
+
'</div>' +
|
|
2390
|
+
'<div id="sw-body" style="overflow:auto">' + buildBody() + '</div>' +
|
|
2391
|
+
'<button id="sw-cancel" style="margin:0.5rem 1.1rem 0;background:none;border:1px solid #30363d;color:#8b949e;font-size:0.85rem;padding:0.7rem;border-radius:10px;cursor:pointer">取消</button>';
|
|
2392
|
+
overlay.appendChild(box);
|
|
2393
|
+
const close = () => overlay.remove();
|
|
2394
|
+
overlay.addEventListener('click', (e) => { if (e.target === overlay) close(); });
|
|
2395
|
+
box.querySelector('#sw-cancel').onclick = close;
|
|
2396
|
+
box.querySelector('#sw-home').onclick = () => { window.location.href = '/'; };
|
|
2397
|
+
const bindRows = () => box.querySelectorAll('[data-sid]').forEach(row => row.onclick = () => {
|
|
2398
|
+
const sid = row.dataset.sid;
|
|
2399
|
+
if (sid && sid !== SESSION_ID) window.location.href = '/terminal/' + sid;
|
|
2400
|
+
else close();
|
|
2401
|
+
});
|
|
2402
|
+
bindRows();
|
|
2403
|
+
box.querySelectorAll('.sw-seg').forEach(b => b.onclick = () => {
|
|
2404
|
+
mode = b.dataset.m;
|
|
2405
|
+
localStorage.setItem('myhi_group_mode', mode);
|
|
2406
|
+
box.querySelectorAll('.sw-seg').forEach(x => { const on = x.dataset.m === mode; x.style.background = on ? '#7c3aed' : 'none'; x.style.color = on ? '#fff' : '#8b949e'; });
|
|
2407
|
+
box.querySelector('#sw-body').innerHTML = buildBody();
|
|
2408
|
+
bindRows();
|
|
2409
|
+
});
|
|
2410
|
+
document.body.appendChild(overlay);
|
|
2411
|
+
};
|
|
2412
|
+
|
|
2406
2413
|
function scrollToBottom() { requestAnimationFrame(() => { chatArea.scrollTop = chatArea.scrollHeight; }); }
|
|
2407
2414
|
function trimMessages() {
|
|
2408
2415
|
if (_userScrolledUp) return; // 用户在往上翻历史,禁用自动删
|
|
@@ -2431,11 +2438,34 @@
|
|
|
2431
2438
|
];
|
|
2432
2439
|
let currentModelId = '';
|
|
2433
2440
|
|
|
2434
|
-
// ── AskUserQuestion
|
|
2435
|
-
//
|
|
2436
|
-
|
|
2441
|
+
// ── AskUserQuestion 弹窗(累加多题)──
|
|
2442
|
+
// AI 一轮里可能连发多个 AskUserQuestion 调用;旧逻辑每次 openAskSheet 都清空覆盖,
|
|
2443
|
+
// 导致只看到最后一题、其余被顶掉无法回答。改为把本轮所有提问累加进同一弹窗,
|
|
2444
|
+
// 多题一次答完;跨重渲染保留已选答案;去重防同一题被 assistant 事件+独立 tool_use 事件各触发一次。
|
|
2445
|
+
let _askQuestions = [];
|
|
2446
|
+
let askMultiState = null; // { answers: [{question,label}|null] },下标与 _askQuestions 对齐
|
|
2447
|
+
|
|
2448
|
+
function _normalizeAsk(input) {
|
|
2449
|
+
const q = input?.questions || input?.question;
|
|
2450
|
+
const list = Array.isArray(q)
|
|
2451
|
+
? q
|
|
2452
|
+
: [{ question: (typeof q === 'string' ? q : '') || input?.question || '请选择', options: input?.options, header: input?.header }];
|
|
2453
|
+
return list.map((x) => ({
|
|
2454
|
+
question: x.question || x.text || '',
|
|
2455
|
+
options: x.options || x.choices || [],
|
|
2456
|
+
header: x.header,
|
|
2457
|
+
}));
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2437
2460
|
window.openAskSheet = function(input) {
|
|
2438
|
-
const
|
|
2461
|
+
for (const q of _normalizeAsk(input)) {
|
|
2462
|
+
if (!q.question || !_askQuestions.some((e) => e.question === q.question)) _askQuestions.push(q);
|
|
2463
|
+
}
|
|
2464
|
+
if (!_askQuestions.length) return;
|
|
2465
|
+
_renderAskSheet();
|
|
2466
|
+
};
|
|
2467
|
+
|
|
2468
|
+
function _renderAskSheet() {
|
|
2439
2469
|
const sheet = document.getElementById('ask-sheet');
|
|
2440
2470
|
const title = document.getElementById('ask-sheet-title');
|
|
2441
2471
|
const body = document.getElementById('ask-sheet-body');
|
|
@@ -2443,62 +2473,53 @@
|
|
|
2443
2473
|
const inputWrap = document.getElementById('ask-sheet-input-wrap');
|
|
2444
2474
|
const btnMulti = document.getElementById('ask-sheet-submit-multi');
|
|
2445
2475
|
const btnSingle = document.getElementById('ask-sheet-submit-single');
|
|
2446
|
-
body.innerHTML = '';
|
|
2447
|
-
inp.value = '';
|
|
2448
|
-
|
|
2449
|
-
// 区分单题/多题。questions 是非空数组且 length>1 才进入批量模式
|
|
2450
|
-
const isMulti = Array.isArray(questions) && questions.length > 1;
|
|
2451
|
-
const qList = Array.isArray(questions)
|
|
2452
|
-
? questions
|
|
2453
|
-
: [{ question: questions || input?.question || '请选择', options: input?.options, header: input?.header }];
|
|
2454
2476
|
|
|
2455
|
-
|
|
2456
|
-
|
|
2477
|
+
const qList = _askQuestions;
|
|
2478
|
+
const isMulti = qList.length > 1;
|
|
2479
|
+
if (!askMultiState) askMultiState = { answers: [] };
|
|
2480
|
+
while (askMultiState.answers.length < qList.length) askMultiState.answers.push(null);
|
|
2457
2481
|
|
|
2482
|
+
title.textContent = (qList[0] && qList[0].header) || (isMulti ? '提问(' + qList.length + ' 题)' : '提问');
|
|
2458
2483
|
if (isMulti) {
|
|
2459
|
-
askMultiState = { qList, answers: new Array(qList.length).fill(null) };
|
|
2460
2484
|
inputWrap.style.display = 'none';
|
|
2461
2485
|
btnSingle.style.display = 'none';
|
|
2462
2486
|
btnMulti.style.display = '';
|
|
2463
2487
|
} else {
|
|
2464
|
-
askMultiState = null;
|
|
2465
2488
|
inputWrap.style.display = '';
|
|
2466
2489
|
btnSingle.style.display = '';
|
|
2467
2490
|
btnMulti.style.display = 'none';
|
|
2468
2491
|
}
|
|
2469
2492
|
|
|
2493
|
+
body.innerHTML = '';
|
|
2470
2494
|
qList.forEach((q, qIdx) => {
|
|
2471
|
-
const qText = q.question ||
|
|
2495
|
+
const qText = q.question || '';
|
|
2472
2496
|
const block = document.createElement('div');
|
|
2473
2497
|
block.className = isMulti ? 'ask-q-block' : '';
|
|
2474
|
-
|
|
2475
2498
|
if (isMulti) {
|
|
2476
2499
|
const label = document.createElement('div');
|
|
2477
2500
|
label.className = 'ask-q-label';
|
|
2478
|
-
label.textContent =
|
|
2501
|
+
label.textContent = 'Q' + (qIdx + 1);
|
|
2479
2502
|
block.appendChild(label);
|
|
2480
2503
|
}
|
|
2481
2504
|
if (qText) {
|
|
2482
2505
|
const desc = document.createElement('div');
|
|
2483
|
-
if (isMulti) {
|
|
2484
|
-
|
|
2485
|
-
desc.textContent = qText;
|
|
2486
|
-
} else {
|
|
2487
|
-
desc.style.cssText = 'padding:0.5rem 0.3rem;font-size:0.82rem;color:#c9d1d9;line-height:1.5;white-space:pre-wrap';
|
|
2488
|
-
desc.textContent = qText;
|
|
2489
|
-
}
|
|
2506
|
+
if (isMulti) { desc.className = 'ask-q-text'; desc.textContent = qText; }
|
|
2507
|
+
else { desc.style.cssText = 'padding:0.5rem 0.3rem;font-size:0.82rem;color:#c9d1d9;line-height:1.5;white-space:pre-wrap'; desc.textContent = qText; }
|
|
2490
2508
|
block.appendChild(desc);
|
|
2491
2509
|
}
|
|
2492
2510
|
if (q.options?.length) {
|
|
2493
|
-
q.options.forEach((opt
|
|
2511
|
+
q.options.forEach((opt) => {
|
|
2494
2512
|
const item = document.createElement('div');
|
|
2495
2513
|
item.className = 'action-sheet-item';
|
|
2496
2514
|
const label = opt.label || opt.value || String(opt);
|
|
2497
|
-
item.innerHTML =
|
|
2515
|
+
item.innerHTML = '<div><div>' + escHtml(label) + '</div>' + (opt.description ? '<div class="desc">' + escHtml(opt.description) + '</div>' : '') + '</div><span class="check" style="display:none">✓</span>';
|
|
2516
|
+
if (isMulti && askMultiState.answers[qIdx]?.label === label) {
|
|
2517
|
+
item.classList.add('ask-opt-selected');
|
|
2518
|
+
const c = item.querySelector('.check'); if (c) c.style.display = '';
|
|
2519
|
+
}
|
|
2498
2520
|
item.onclick = () => {
|
|
2499
2521
|
if (isMulti) {
|
|
2500
|
-
|
|
2501
|
-
Array.from(block.querySelectorAll('.action-sheet-item')).forEach(el => {
|
|
2522
|
+
Array.from(block.querySelectorAll('.action-sheet-item')).forEach((el) => {
|
|
2502
2523
|
el.classList.remove('ask-opt-selected');
|
|
2503
2524
|
const c = el.querySelector('.check'); if (c) c.style.display = 'none';
|
|
2504
2525
|
});
|
|
@@ -2507,7 +2528,7 @@
|
|
|
2507
2528
|
askMultiState.answers[qIdx] = { question: qText, label };
|
|
2508
2529
|
refreshAskMultiSubmit();
|
|
2509
2530
|
} else {
|
|
2510
|
-
|
|
2531
|
+
_clearAsk();
|
|
2511
2532
|
addInputMessage(label);
|
|
2512
2533
|
socket.emit('agent:query', { prompt: label });
|
|
2513
2534
|
showThinking();
|
|
@@ -2522,33 +2543,42 @@
|
|
|
2522
2543
|
if (isMulti) refreshAskMultiSubmit();
|
|
2523
2544
|
sheet.classList.add('open');
|
|
2524
2545
|
if (!isMulti) inp.focus();
|
|
2525
|
-
}
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2526
2548
|
function refreshAskMultiSubmit() {
|
|
2527
2549
|
const btn = document.getElementById('ask-sheet-submit-multi');
|
|
2528
2550
|
if (!askMultiState) return;
|
|
2529
|
-
const
|
|
2551
|
+
const n = _askQuestions.length;
|
|
2552
|
+
const remaining = n - askMultiState.answers.slice(0, n).filter(Boolean).length;
|
|
2530
2553
|
btn.disabled = remaining > 0;
|
|
2531
|
-
btn.textContent = remaining > 0 ?
|
|
2554
|
+
btn.textContent = remaining > 0 ? '还剩 ' + remaining + ' 题未答' : '提交回答';
|
|
2532
2555
|
}
|
|
2556
|
+
|
|
2557
|
+
function _clearAsk() {
|
|
2558
|
+
_askQuestions = [];
|
|
2559
|
+
askMultiState = null;
|
|
2560
|
+
const inp = document.getElementById('ask-sheet-input');
|
|
2561
|
+
if (inp) inp.value = '';
|
|
2562
|
+
document.getElementById('ask-sheet').classList.remove('open');
|
|
2563
|
+
}
|
|
2564
|
+
|
|
2533
2565
|
window.submitAskMulti = function() {
|
|
2534
2566
|
if (!askMultiState) return;
|
|
2535
|
-
const
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2567
|
+
const n = _askQuestions.length;
|
|
2568
|
+
const answers = askMultiState.answers.slice(0, n);
|
|
2569
|
+
if (answers.some((a) => !a)) return;
|
|
2570
|
+
const reply = answers.map((a, i) => 'Q' + (i + 1) + ': ' + a.question + '\nA: ' + a.label).join('\n\n');
|
|
2571
|
+
_clearAsk();
|
|
2540
2572
|
addInputMessage(reply);
|
|
2541
2573
|
socket.emit('agent:query', { prompt: reply });
|
|
2542
2574
|
showThinking();
|
|
2543
2575
|
};
|
|
2544
|
-
window.closeAskSheet = function() {
|
|
2545
|
-
document.getElementById('ask-sheet').classList.remove('open');
|
|
2546
|
-
};
|
|
2576
|
+
window.closeAskSheet = function() { _clearAsk(); };
|
|
2547
2577
|
window.submitAskReply = function() {
|
|
2548
2578
|
const inp = document.getElementById('ask-sheet-input');
|
|
2549
2579
|
const text = inp.value.trim();
|
|
2550
2580
|
if (!text) return;
|
|
2551
|
-
|
|
2581
|
+
_clearAsk();
|
|
2552
2582
|
addInputMessage(text);
|
|
2553
2583
|
socket.emit('agent:query', { prompt: text });
|
|
2554
2584
|
showThinking();
|