diff --git a/frontends/desktop/static/app.js b/frontends/desktop/static/app.js index e2d7da01..fcdae4f6 100644 --- a/frontends/desktop/static/app.js +++ b/frontends/desktop/static/app.js @@ -304,7 +304,7 @@ const I18N = { 'st.starting': '启动中…', 'st.stopping': '停止中…', 'st.online': '在线', 'st.offline': '离线', 'st.error': '错误', 'st.running': '运行', 'st.abnormal': '异常', 'act.configure': '配置', 'act.logs': '日志', 'act.restart': '重启', 'act.stop': '停止', 'act.start': '启动', - 'act.copy': '复制', 'act.copied': '已复制', 'act.copyTex': 'TeX', + 'act.copy': '复制', 'act.copied': '已复制', 'act.copyTex': 'TeX', 'act.send': '发送', 'proc.imbotWechat': 'imbot · 微信', 'proc.imbotDing': 'imbot · 钉钉', 'proc.scheduler': '定时任务调度', 'cm.scheduling': '调度中', 'cm.running': '执行中', 'cm.idleSt': '空闲', 'cm.master': '已派 3 子任务', 'cm.w1': '子任务:抓取数据', 'cm.w2': '子任务:复核结果', 'cm.sub': '等待派单', @@ -409,7 +409,7 @@ const I18N = { 'st.starting': 'Starting…', 'st.stopping': 'Stopping…', 'st.online': 'Online', 'st.offline': 'Offline', 'st.error': 'Error', 'st.running': 'Running', 'st.abnormal': 'Error', 'act.configure': 'Configure', 'act.logs': 'Logs', 'act.restart': 'Restart', 'act.stop': 'Stop', 'act.start': 'Start', - 'act.copy': 'Copy', 'act.copied': 'Copied', 'act.copyTex': 'TeX', + 'act.copy': 'Copy', 'act.copied': 'Copied', 'act.copyTex': 'TeX', 'act.send': 'Send', 'proc.imbotWechat': 'imbot · WeChat', 'proc.imbotDing': 'imbot · DingTalk', 'proc.scheduler': 'Scheduler', 'cm.scheduling': 'Scheduling', 'cm.running': 'Running', 'cm.idleSt': 'Idle', 'cm.master': 'Dispatched 3 subtasks', 'cm.w1': 'Subtask: fetch data', 'cm.w2': 'Subtask: review results', 'cm.sub': 'Waiting for tasks', @@ -1041,22 +1041,19 @@ function statusLabel() { if (s && rt(s).busy) return t('status.running'); return state.bridgeReady ? t('status.ready') : t('status.disconnected'); } -function refreshStatusLabel() { if (!runToggle.classList.contains('stopped')) runLabel.textContent = statusLabel(); } +function refreshStatusLabel() { runLabel.textContent = statusLabel(); } function setBusy(sess, busy) { const r = rt(sess); r.busy = busy; if (!isActive(sess)) return; - runToggle.classList.remove('stopped'); runToggle.classList.toggle('busy', busy); runLabel.textContent = busy ? t('status.running') : (state.bridgeReady ? t('status.ready') : t('status.disconnected')); -} -runToggle.addEventListener('click', async () => { - const sess = activeSess(); - if (sess && rt(sess).busy) { - await cancelPrompt(); - runLabel.textContent = t('status.stopped'); - runToggle.classList.add('stopped'); + if (sendBtn) { + sendBtn.classList.toggle('is-stop', busy); + sendBtn.setAttribute('aria-label', busy ? t('act.stop') : t('act.send')); + sendBtn.title = busy ? t('act.stop') : ''; } -}); +} +// run-toggle 现为纯状态展示组件:运行中转红,不再响应点击(停止改由发送键的录制键承担) /* ═══════════════ 会话 ═══════════════ */ function isUntitled(x) { return !x || /^(new chat|新对话|新会话)$/i.test(String(x).trim()); } @@ -1406,7 +1403,12 @@ async function submitInput() { setComposerLocked(false); } } -sendBtn.addEventListener('click', (e) => { e.preventDefault(); submitInput(); }); +sendBtn.addEventListener('click', (e) => { + e.preventDefault(); + const sess = activeSess(); + if (sess && rt(sess).busy) { cancelPrompt(); return; } // 运行中:发送键是录制键 → 纯停止 + submitInput(); +}); inputEl.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); submitInput(); } }); inputEl.addEventListener('input', () => { inputEl.style.height = 'auto'; diff --git a/frontends/desktop/static/index.html b/frontends/desktop/static/index.html index 0420d330..44b7a12b 100644 --- a/frontends/desktop/static/index.html +++ b/frontends/desktop/static/index.html @@ -3,7 +3,7 @@ GenericAgent - + @@ -451,7 +451,7 @@ - + diff --git a/frontends/desktop/static/styles.css b/frontends/desktop/static/styles.css index b6378804..0d1e02d4 100644 --- a/frontends/desktop/static/styles.css +++ b/frontends/desktop/static/styles.css @@ -497,19 +497,17 @@ html[data-appearance="dark"] .swatch[data-theme="8"].sel::after{ padding:5px 12px; font-size:12px; color:var(--txt-2); cursor:pointer; } .pill:hover{ border-color:var(--line-strong); } +/* 纯状态展示组件(不可点):就绪=绿,运行中=红 */ .run-state{ display:flex; align-items:center; gap:9px; height:30px; color:var(--ok); font-weight:600; font-size:12px; border:none; background:transparent; - padding:0; font-family:inherit; cursor:pointer; line-height:1; + padding:0; font-family:inherit; cursor:default; line-height:1; } .run-state .dot{ - width:9px; height:9px; border-radius:50%; background:var(--ok); - flex:0 0 auto; transition:box-shadow .15s; + width:9px; height:9px; border-radius:50%; background:var(--ok); flex:0 0 auto; } -.run-state:hover .dot{ box-shadow:0 0 0 7px var(--ok-ring); } -.run-state.stopped{ color:var(--danger); } -.run-state.stopped .dot{ background:var(--danger-dot); border-radius:2px; } -.run-state.stopped:hover .dot{ box-shadow:0 0 0 7px var(--danger-ring); } +.run-state.busy{ color:var(--danger); } +.run-state.busy .dot{ background:var(--danger-dot); } .link{ color:var(--blue); cursor:pointer; } .muted{ color:var(--muted); } .ic-btn{ @@ -808,6 +806,14 @@ html[data-boot-has-sessions] .chat-start{ display:none !important; } animation:ga-spin .65s linear infinite; } .send svg{ width:15px; height:15px; } +/* 运行中:发送键变红色停止键(点击=暂停当前回合)。回车仍走发送(打断重发)。 */ +.send.is-stop{ background:var(--danger); cursor:pointer; } +.send.is-stop:hover{ background:var(--danger-dot); } +.send.is-stop svg{ display:none; } +.send.is-stop::after{ + content:''; width:12px; height:12px; border-radius:3px; + background:var(--on-accent); +} /* ============ ④ 右侧会话面板 ============ */ .rp-toggle, .sb-toggle{