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) {
|
function toastErr(msg) {
|
||||||
if (window.toastr?.error) return window.toastr.error(msg);
|
if (window.toastr?.error) return window.toastr.error(msg);
|
||||||
console.error('[EnaPlanner]', msg);
|
console.error('[EnaPlanner]', msg);
|
||||||
@@ -183,6 +179,49 @@ function setSendUIBusy(busy) {
|
|||||||
if (textarea) textarea.disabled = !!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) {
|
function safeStringify(val) {
|
||||||
if (val == null) return '';
|
if (val == null) return '';
|
||||||
if (typeof val === 'string') return val;
|
if (typeof val === 'string') return val;
|
||||||
@@ -1255,7 +1294,10 @@ async function runPlanningOnce(rawUserInput, silent = false) {
|
|||||||
const { messages } = await buildPlannerMessages(rawUserInput);
|
const { messages } = await buildPlannerMessages(rawUserInput);
|
||||||
log.requestMessages = messages;
|
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;
|
log.rawReply = rawReply;
|
||||||
|
|
||||||
const filtered = filterPlannerForInput(rawReply);
|
const filtered = filterPlannerForInput(rawReply);
|
||||||
@@ -1292,29 +1334,34 @@ function shouldInterceptNow() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function doInterceptAndPlanThenSend() {
|
async function doInterceptAndPlanThenSend(rawSnapshot = '') {
|
||||||
const ta = getSendTextarea();
|
const ta = getSendTextarea();
|
||||||
const btn = getSendButton();
|
const btn = getSendButton();
|
||||||
if (!ta || !btn) return;
|
if (!ta || !btn) return;
|
||||||
|
|
||||||
const raw = String(ta.value ?? '').trim();
|
const raw = String(rawSnapshot || ta.value || '').trim();
|
||||||
if (!raw) return;
|
if (!raw) return;
|
||||||
|
|
||||||
state.isPlanning = true;
|
state.isPlanning = true;
|
||||||
setSendUIBusy(true);
|
setSendUIBusy(true);
|
||||||
|
setPlanningStatus('Planning...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
toastInfo('Ena Planner:正在规划…');
|
|
||||||
const { filtered } = await runPlanningOnce(raw, false);
|
const { filtered } = await runPlanningOnce(raw, false);
|
||||||
const merged = `${raw}\n\n${filtered}`.trim();
|
const merged = `${raw}\n\n${filtered}`.trim();
|
||||||
ta.value = merged;
|
ta.value = merged;
|
||||||
state.lastInjectedText = merged;
|
state.lastInjectedText = merged;
|
||||||
|
setPlanningStatus('Planning done', 'success');
|
||||||
|
|
||||||
state.bypassNextSend = true;
|
state.bypassNextSend = true;
|
||||||
btn.click();
|
btn.click();
|
||||||
|
} catch (err) {
|
||||||
|
setPlanningStatus(String(err?.message || 'Planning failed'), 'error');
|
||||||
|
throw err;
|
||||||
} finally {
|
} finally {
|
||||||
state.isPlanning = false;
|
state.isPlanning = false;
|
||||||
setSendUIBusy(false);
|
setSendUIBusy(false);
|
||||||
|
clearPlanningStatus(2000);
|
||||||
setTimeout(() => { state.bypassNextSend = false; }, 800);
|
setTimeout(() => { state.bypassNextSend = false; }, 800);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1325,6 +1372,9 @@ function installSendInterceptors() {
|
|||||||
const btn = getSendButton();
|
const btn = getSendButton();
|
||||||
if (!btn) return;
|
if (!btn) return;
|
||||||
if (e.target !== btn && !btn.contains(e.target)) 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) {
|
if (state.isPlanning) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopImmediatePropagation();
|
e.stopImmediatePropagation();
|
||||||
@@ -1333,7 +1383,7 @@ function installSendInterceptors() {
|
|||||||
if (!shouldInterceptNow()) return;
|
if (!shouldInterceptNow()) return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopImmediatePropagation();
|
e.stopImmediatePropagation();
|
||||||
doInterceptAndPlanThenSend().catch(err => toastErr(String(err?.message ?? err)));
|
doInterceptAndPlanThenSend(raw).catch(err => toastErr(String(err?.message ?? err)));
|
||||||
};
|
};
|
||||||
sendClickHandler = (e) => {
|
sendClickHandler = (e) => {
|
||||||
const btn = getSendButton();
|
const btn = getSendButton();
|
||||||
@@ -1345,18 +1395,23 @@ function installSendInterceptors() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!shouldInterceptNow()) return;
|
if (!shouldInterceptNow()) return;
|
||||||
|
const ta = getSendTextarea();
|
||||||
|
const raw = String(ta?.value ?? '').trim();
|
||||||
|
if (!raw) return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopImmediatePropagation();
|
e.stopImmediatePropagation();
|
||||||
doInterceptAndPlanThenSend().catch(err => toastErr(String(err?.message ?? err)));
|
doInterceptAndPlanThenSend(raw).catch(err => toastErr(String(err?.message ?? err)));
|
||||||
};
|
};
|
||||||
sendKeydownHandler = (e) => {
|
sendKeydownHandler = (e) => {
|
||||||
const ta = getSendTextarea();
|
const ta = getSendTextarea();
|
||||||
if (!ta || e.target !== ta) return;
|
if (!ta || e.target !== ta) return;
|
||||||
if (e.key === 'Enter' && !e.shiftKey) {
|
if (e.key === 'Enter' && !e.shiftKey) {
|
||||||
if (!shouldInterceptNow()) return;
|
if (!shouldInterceptNow()) return;
|
||||||
|
const raw = String(ta.value ?? '').trim();
|
||||||
|
if (!raw) return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopImmediatePropagation();
|
e.stopImmediatePropagation();
|
||||||
doInterceptAndPlanThenSend().catch(err => toastErr(String(err?.message ?? err)));
|
doInterceptAndPlanThenSend(raw).catch(err => toastErr(String(err?.message ?? err)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
sendKeyupHandler = (e) => {
|
sendKeyupHandler = (e) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user