feat(desktop): run-state becomes display-only; send key turns into a stop button while running
The top-left run-state is now a pure status indicator (green=ready, red=running) and no longer handles clicks. Stopping the current turn moves to the send key: while the agent is running the send button turns into a red stop square — clicking it cancels the current turn (ChatGPT/Claude style). The Enter key still submits, so "type while running + Enter" keeps lyx's interrupt-and-resend behavior (#5) intact. - setBusy toggles .is-stop on the send button + aria-label (stop/send) - send click: cancelPrompt() when busy, submitInput() otherwise - remove run-toggle click handler + .stopped state; .run-state.busy goes red - .send.is-stop: red bg + white rounded square (hide arrow); i18n act.send - cache bust styles v19->20, app v25->26
This commit is contained in:
1 parent
8783914077
commit
57cf4d6632
3 files changed
+30
-22
No files matched your search
@@ -304,7 +304,7 @@ const I18N = {
|
|||||||
'st.starting': '启动中…', 'st.stopping': '停止中…',
|
'st.starting': '启动中…', 'st.stopping': '停止中…',
|
||||||
'st.online': '在线', 'st.offline': '离线', 'st.error': '错误', 'st.running': '运行', 'st.abnormal': '异常',
|
'st.online': '在线', 'st.offline': '离线', 'st.error': '错误', 'st.running': '运行', 'st.abnormal': '异常',
|
||||||
'act.configure': '配置', 'act.logs': '日志', 'act.restart': '重启', 'act.stop': '停止', 'act.start': '启动',
|
'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': '定时任务调度',
|
'proc.imbotWechat': 'imbot · 微信', 'proc.imbotDing': 'imbot · 钉钉', 'proc.scheduler': '定时任务调度',
|
||||||
'cm.scheduling': '调度中', 'cm.running': '执行中', 'cm.idleSt': '空闲',
|
'cm.scheduling': '调度中', 'cm.running': '执行中', 'cm.idleSt': '空闲',
|
||||||
'cm.master': '已派 3 子任务', 'cm.w1': '子任务:抓取数据', 'cm.w2': '子任务:复核结果', 'cm.sub': '等待派单',
|
'cm.master': '已派 3 子任务', 'cm.w1': '子任务:抓取数据', 'cm.w2': '子任务:复核结果', 'cm.sub': '等待派单',
|
||||||
@@ -409,7 +409,7 @@ const I18N = {
|
|||||||
'st.starting': 'Starting…', 'st.stopping': 'Stopping…',
|
'st.starting': 'Starting…', 'st.stopping': 'Stopping…',
|
||||||
'st.online': 'Online', 'st.offline': 'Offline', 'st.error': 'Error', 'st.running': 'Running', 'st.abnormal': 'Error',
|
'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.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',
|
'proc.imbotWechat': 'imbot · WeChat', 'proc.imbotDing': 'imbot · DingTalk', 'proc.scheduler': 'Scheduler',
|
||||||
'cm.scheduling': 'Scheduling', 'cm.running': 'Running', 'cm.idleSt': 'Idle',
|
'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',
|
'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');
|
if (s && rt(s).busy) return t('status.running');
|
||||||
return state.bridgeReady ? t('status.ready') : t('status.disconnected');
|
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) {
|
function setBusy(sess, busy) {
|
||||||
const r = rt(sess); r.busy = busy;
|
const r = rt(sess); r.busy = busy;
|
||||||
if (!isActive(sess)) return;
|
if (!isActive(sess)) return;
|
||||||
runToggle.classList.remove('stopped');
|
|
||||||
runToggle.classList.toggle('busy', busy);
|
runToggle.classList.toggle('busy', busy);
|
||||||
runLabel.textContent = busy ? t('status.running') : (state.bridgeReady ? t('status.ready') : t('status.disconnected'));
|
runLabel.textContent = busy ? t('status.running') : (state.bridgeReady ? t('status.ready') : t('status.disconnected'));
|
||||||
}
|
if (sendBtn) {
|
||||||
runToggle.addEventListener('click', async () => {
|
sendBtn.classList.toggle('is-stop', busy);
|
||||||
const sess = activeSess();
|
sendBtn.setAttribute('aria-label', busy ? t('act.stop') : t('act.send'));
|
||||||
if (sess && rt(sess).busy) {
|
sendBtn.title = busy ? t('act.stop') : '';
|
||||||
await cancelPrompt();
|
|
||||||
runLabel.textContent = t('status.stopped');
|
|
||||||
runToggle.classList.add('stopped');
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
// run-toggle 现为纯状态展示组件:运行中转红,不再响应点击(停止改由发送键的录制键承担)
|
||||||
|
|
||||||
/* ═══════════════ 会话 ═══════════════ */
|
/* ═══════════════ 会话 ═══════════════ */
|
||||||
function isUntitled(x) { return !x || /^(new chat|新对话|新会话)$/i.test(String(x).trim()); }
|
function isUntitled(x) { return !x || /^(new chat|新对话|新会话)$/i.test(String(x).trim()); }
|
||||||
@@ -1406,7 +1403,12 @@ async function submitInput() {
|
|||||||
setComposerLocked(false);
|
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('keydown', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); submitInput(); } });
|
||||||
inputEl.addEventListener('input', () => {
|
inputEl.addEventListener('input', () => {
|
||||||
inputEl.style.height = 'auto';
|
inputEl.style.height = 'auto';
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>GenericAgent</title>
|
<title>GenericAgent</title>
|
||||||
<link rel="stylesheet" href="styles.css?v=19">
|
<link rel="stylesheet" href="styles.css?v=20">
|
||||||
<!-- KaTeX: LaTeX 公式渲染 -->
|
<!-- KaTeX: LaTeX 公式渲染 -->
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" crossorigin="anonymous">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" crossorigin="anonymous">
|
||||||
<!-- highlight.js: 代码高亮 -->
|
<!-- highlight.js: 代码高亮 -->
|
||||||
@@ -451,7 +451,7 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" crossorigin="anonymous"></script>
|
||||||
<!-- highlight.js -->
|
<!-- highlight.js -->
|
||||||
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/highlight.min.js" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/highlight.min.js" crossorigin="anonymous"></script>
|
||||||
<script src="app.js?v=25"></script>
|
<script src="app.js?v=26"></script>
|
||||||
<script src="annot.js"></script><!-- DEV 脚手架,可删 -->
|
<script src="annot.js"></script><!-- DEV 脚手架,可删 -->
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -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;
|
padding:5px 12px; font-size:12px; color:var(--txt-2); cursor:pointer;
|
||||||
}
|
}
|
||||||
.pill:hover{ border-color:var(--line-strong); }
|
.pill:hover{ border-color:var(--line-strong); }
|
||||||
|
/* 纯状态展示组件(不可点):就绪=绿,运行中=红 */
|
||||||
.run-state{
|
.run-state{
|
||||||
display:flex; align-items:center; gap:9px; height:30px; color:var(--ok);
|
display:flex; align-items:center; gap:9px; height:30px; color:var(--ok);
|
||||||
font-weight:600; font-size:12px; border:none; background:transparent;
|
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{
|
.run-state .dot{
|
||||||
width:9px; height:9px; border-radius:50%; background:var(--ok);
|
width:9px; height:9px; border-radius:50%; background:var(--ok); flex:0 0 auto;
|
||||||
flex:0 0 auto; transition:box-shadow .15s;
|
|
||||||
}
|
}
|
||||||
.run-state:hover .dot{ box-shadow:0 0 0 7px var(--ok-ring); }
|
.run-state.busy{ color:var(--danger); }
|
||||||
.run-state.stopped{ color:var(--danger); }
|
.run-state.busy .dot{ background:var(--danger-dot); }
|
||||||
.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); }
|
|
||||||
.link{ color:var(--blue); cursor:pointer; }
|
.link{ color:var(--blue); cursor:pointer; }
|
||||||
.muted{ color:var(--muted); }
|
.muted{ color:var(--muted); }
|
||||||
.ic-btn{
|
.ic-btn{
|
||||||
@@ -808,6 +806,14 @@ html[data-boot-has-sessions] .chat-start{ display:none !important; }
|
|||||||
animation:ga-spin .65s linear infinite;
|
animation:ga-spin .65s linear infinite;
|
||||||
}
|
}
|
||||||
.send svg{ width:15px; height:15px; }
|
.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{
|
.rp-toggle, .sb-toggle{
|
||||||
|
|||||||
Reference in new issue
Block a user