fix(ena-planner): restore planning lock UX and robust send interception flow
This commit is contained in:
@@ -139,10 +139,6 @@ async function saveConfigNow() {
|
||||
}
|
||||
}
|
||||
|
||||
function toastInfo(msg) {
|
||||
if (window.toastr?.info) return window.toastr.info(msg);
|
||||
console.log('[EnaPlanner]', msg);
|
||||
}
|
||||
function toastErr(msg) {
|
||||
if (window.toastr?.error) return window.toastr.error(msg);
|
||||
console.error('[EnaPlanner]', msg);
|
||||
@@ -183,6 +179,49 @@ function setSendUIBusy(busy) {
|
||||
if (textarea) textarea.disabled = !!busy;
|
||||
}
|
||||
|
||||
function ensurePlanningStatusEl() {
|
||||
const ta = getSendTextarea();
|
||||
if (!ta) return null;
|
||||
let el = document.getElementById('xb-ena-planning-status');
|
||||
if (el) return el;
|
||||
|
||||
el = document.createElement('div');
|
||||
el.id = 'xb-ena-planning-status';
|
||||
el.style.cssText = [
|
||||
'margin-top:6px',
|
||||
'font-size:12px',
|
||||
'line-height:1.4',
|
||||
'color:var(--SmartThemeBodyColor,#c9d1d9)',
|
||||
'opacity:.82',
|
||||
'display:none',
|
||||
].join(';');
|
||||
ta.insertAdjacentElement('afterend', el);
|
||||
return el;
|
||||
}
|
||||
|
||||
function setPlanningStatus(text, type = 'info') {
|
||||
const el = ensurePlanningStatusEl();
|
||||
if (!el) return;
|
||||
el.textContent = text || '';
|
||||
el.style.display = text ? 'block' : 'none';
|
||||
if (!text) return;
|
||||
if (type === 'error') {
|
||||
el.style.color = '#f87171';
|
||||
} else if (type === 'success') {
|
||||
el.style.color = '#3ecf8e';
|
||||
} else {
|
||||
el.style.color = 'var(--SmartThemeBodyColor,#c9d1d9)';
|
||||
}
|
||||
}
|
||||
|
||||
function clearPlanningStatus(delay = 0) {
|
||||
if (delay > 0) {
|
||||
setTimeout(() => setPlanningStatus(''), delay);
|
||||
return;
|
||||
}
|
||||
setPlanningStatus('');
|
||||
}
|
||||
|
||||
function safeStringify(val) {
|
||||
if (val == null) return '';
|
||||
if (typeof val === 'string') return val;
|
||||
@@ -1255,7 +1294,10 @@ async function runPlanningOnce(rawUserInput, silent = false) {
|
||||
const { messages } = await buildPlannerMessages(rawUserInput);
|
||||
log.requestMessages = messages;
|
||||
|
||||
const rawReply = await callPlanner(messages);
|
||||
const rawReply = await Promise.race([
|
||||
callPlanner(messages),
|
||||
new Promise((_, reject) => setTimeout(() => reject(new Error('规划超时,请重试')), 120000)),
|
||||
]);
|
||||
log.rawReply = rawReply;
|
||||
|
||||
const filtered = filterPlannerForInput(rawReply);
|
||||
@@ -1292,29 +1334,34 @@ function shouldInterceptNow() {
|
||||
return true;
|
||||
}
|
||||
|
||||
async function doInterceptAndPlanThenSend() {
|
||||
async function doInterceptAndPlanThenSend(rawSnapshot = '') {
|
||||
const ta = getSendTextarea();
|
||||
const btn = getSendButton();
|
||||
if (!ta || !btn) return;
|
||||
|
||||
const raw = String(ta.value ?? '').trim();
|
||||
const raw = String(rawSnapshot || ta.value || '').trim();
|
||||
if (!raw) return;
|
||||
|
||||
state.isPlanning = true;
|
||||
setSendUIBusy(true);
|
||||
setPlanningStatus('Planning...');
|
||||
|
||||
try {
|
||||
toastInfo('Ena Planner:正在规划…');
|
||||
const { filtered } = await runPlanningOnce(raw, false);
|
||||
const merged = `${raw}\n\n${filtered}`.trim();
|
||||
ta.value = merged;
|
||||
state.lastInjectedText = merged;
|
||||
setPlanningStatus('Planning done', 'success');
|
||||
|
||||
state.bypassNextSend = true;
|
||||
btn.click();
|
||||
} catch (err) {
|
||||
setPlanningStatus(String(err?.message || 'Planning failed'), 'error');
|
||||
throw err;
|
||||
} finally {
|
||||
state.isPlanning = false;
|
||||
setSendUIBusy(false);
|
||||
clearPlanningStatus(2000);
|
||||
setTimeout(() => { state.bypassNextSend = false; }, 800);
|
||||
}
|
||||
}
|
||||
@@ -1325,6 +1372,9 @@ function installSendInterceptors() {
|
||||
const btn = getSendButton();
|
||||
if (!btn) return;
|
||||
if (e.target !== btn && !btn.contains(e.target)) return;
|
||||
const ta = getSendTextarea();
|
||||
const raw = String(ta?.value ?? '').trim();
|
||||
if (!raw) return;
|
||||
if (state.isPlanning) {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
@@ -1333,7 +1383,7 @@ function installSendInterceptors() {
|
||||
if (!shouldInterceptNow()) return;
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
doInterceptAndPlanThenSend().catch(err => toastErr(String(err?.message ?? err)));
|
||||
doInterceptAndPlanThenSend(raw).catch(err => toastErr(String(err?.message ?? err)));
|
||||
};
|
||||
sendClickHandler = (e) => {
|
||||
const btn = getSendButton();
|
||||
@@ -1345,18 +1395,23 @@ function installSendInterceptors() {
|
||||
return;
|
||||
}
|
||||
if (!shouldInterceptNow()) return;
|
||||
const ta = getSendTextarea();
|
||||
const raw = String(ta?.value ?? '').trim();
|
||||
if (!raw) return;
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
doInterceptAndPlanThenSend().catch(err => toastErr(String(err?.message ?? err)));
|
||||
doInterceptAndPlanThenSend(raw).catch(err => toastErr(String(err?.message ?? err)));
|
||||
};
|
||||
sendKeydownHandler = (e) => {
|
||||
const ta = getSendTextarea();
|
||||
if (!ta || e.target !== ta) return;
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
if (!shouldInterceptNow()) return;
|
||||
const raw = String(ta.value ?? '').trim();
|
||||
if (!raw) return;
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
doInterceptAndPlanThenSend().catch(err => toastErr(String(err?.message ?? err)));
|
||||
doInterceptAndPlanThenSend(raw).catch(err => toastErr(String(err?.message ?? err)));
|
||||
}
|
||||
};
|
||||
sendKeyupHandler = (e) => {
|
||||
|
||||
Reference in New Issue
Block a user