@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 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
- // 多题模式状态:{ qList, answers: [{ question, label } | null] }
2436
- let askMultiState = null;
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 questions = input?.questions || input?.question;
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
- // 标题:多题用首题 header 或通用文案,单题保持原行为
2456
- title.textContent = (qList[0] && qList[0].header) || input?.header || (isMulti ? `提问(${qList.length} 题)` : '提问');
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 || q.text || '';
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 = `Q${qIdx + 1}`;
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
- desc.className = 'ask-q-text';
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, optIdx) => {
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 = `<div><div>${escHtml(label)}</div>${opt.description ? `<div class="desc">${escHtml(opt.description)}</div>` : ''}</div><span class="check" style="display:none">✓</span>`;
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
- closeAskSheet();
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 remaining = askMultiState.answers.filter(a => !a).length;
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 ? `还剩 ${remaining} 题未答` : '提交回答';
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 { answers } = askMultiState;
2536
- if (answers.some(a => !a)) return;
2537
- const reply = answers.map((a, i) => `Q${i + 1}: ${a.question}\nA: ${a.label}`).join('\n\n');
2538
- closeAskSheet();
2539
- askMultiState = null;
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
- closeAskSheet();
2581
+ _clearAsk();
2552
2582
  addInputMessage(text);
2553
2583
  socket.emit('agent:query', { prompt: text });
2554
2584
  showThinking();