Add generation lock and floating-first auto

This commit is contained in:
henrryyes
2026-01-18 18:23:54 +08:00
parent 4a59139c4d
commit 31d680c96c
2 changed files with 64 additions and 19 deletions

View File

@@ -10,6 +10,7 @@ import {
saveSettings, saveSettings,
findLastAIMessageId, findLastAIMessageId,
classifyError, classifyError,
isGenerating,
} from './novel-draw.js'; } from './novel-draw.js';
import { registerToToolbar, removeFromToolbar } from '../../widgets/message-toolbar.js'; import { registerToToolbar, removeFromToolbar } from '../../widgets/message-toolbar.js';
@@ -754,6 +755,11 @@ async function handleFloorDrawClick(messageId) {
const panelData = panelMap.get(messageId); const panelData = panelMap.get(messageId);
if (!panelData || panelData.state !== FloatState.IDLE) return; if (!panelData || panelData.state !== FloatState.IDLE) return;
if (isGenerating()) {
toastr?.info?.('已有任务进行中,请等待完成');
return;
}
try { try {
await generateAndInsertImages({ await generateAndInsertImages({
messageId, messageId,
@@ -777,8 +783,9 @@ async function handleFloorDrawClick(messageId) {
}); });
} catch (e) { } catch (e) {
console.error('[NovelDraw]', e); console.error('[NovelDraw]', e);
if (e.message === '已取消') { if (e.message === '已取消' || e.message?.includes('已有任务进行中')) {
setFloorState(messageId, FloatState.IDLE); setFloorState(messageId, FloatState.IDLE);
if (e.message?.includes('已有任务进行中')) toastr?.info?.(e.message);
} else { } else {
setFloorState(messageId, FloatState.ERROR, { error: classifyError(e) }); setFloorState(messageId, FloatState.ERROR, { error: classifyError(e) });
} }
@@ -1230,6 +1237,11 @@ async function handleFloatingDrawClick() {
return; return;
} }
if (isGenerating()) {
toastr?.info?.('已有任务进行中,请等待完成');
return;
}
try { try {
await generateAndInsertImages({ await generateAndInsertImages({
messageId, messageId,
@@ -1253,8 +1265,9 @@ async function handleFloatingDrawClick() {
}); });
} catch (e) { } catch (e) {
console.error('[NovelDraw]', e); console.error('[NovelDraw]', e);
if (e.message === '已取消') { if (e.message === '已取消' || e.message?.includes('已有任务进行中')) {
setFloatingState(FloatState.IDLE); setFloatingState(FloatState.IDLE);
if (e.message?.includes('已有任务进行中')) toastr?.info?.(e.message);
} else { } else {
setFloatingState(FloatState.ERROR, { error: classifyError(e) }); setFloatingState(FloatState.ERROR, { error: classifyError(e) });
} }
@@ -1547,4 +1560,5 @@ export {
SIZE_OPTIONS, SIZE_OPTIONS,
createFloatingButton, createFloatingButton,
destroyFloatingButton, destroyFloatingButton,
setFloatingState,
}; };

View File

@@ -274,7 +274,7 @@ function abortGeneration() {
} }
function isGenerating() { function isGenerating() {
return generationAbortController !== null; return autoBusy || generationAbortController !== null;
} }
// ═══════════════════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════════════════
@@ -1723,12 +1723,16 @@ async function handleMessageModified(data) {
// 多图生成 // 多图生成
// ═══════════════════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════════════════
async function generateAndInsertImages({ messageId, onStateChange }) { async function generateAndInsertImages({ messageId, onStateChange, skipLock = false }) {
await loadSettings(); await loadSettings();
const ctx = getContext(); const ctx = getContext();
const message = ctx.chat?.[messageId]; const message = ctx.chat?.[messageId];
if (!message) throw new NovelDrawError('消息不存在', ErrorType.PARSE); if (!message) throw new NovelDrawError('消息不存在', ErrorType.PARSE);
if (!skipLock && isGenerating()) {
throw new NovelDrawError('已有任务进行中', ErrorType.UNKNOWN);
}
generationAbortController = new AbortController(); generationAbortController = new AbortController();
const signal = generationAbortController.signal; const signal = generationAbortController.signal;
@@ -1936,7 +1940,12 @@ async function generateAndInsertImages({ messageId, onStateChange }) {
async function autoGenerateForLastAI() { async function autoGenerateForLastAI() {
const s = getSettings(); const s = getSettings();
if (!isModuleEnabled() || s.mode !== 'auto' || autoBusy) return; if (!isModuleEnabled() || s.mode !== 'auto') return;
if (isGenerating()) {
console.log('[NovelDraw] 自动模式:已有任务进行中,跳过');
return;
}
const ctx = getContext(); const ctx = getContext();
const chat = ctx.chat || []; const chat = ctx.chat || [];
@@ -1955,34 +1964,48 @@ async function autoGenerateForLastAI() {
autoBusy = true; autoBusy = true;
try { try {
const { setStateForMessage, FloatState, ensureNovelDrawPanel } = await import('./floating-panel.js'); const { setStateForMessage, setFloatingState, FloatState, ensureNovelDrawPanel } = await import('./floating-panel.js');
const floatingOn = s.showFloatingButton === true;
const floorOn = s.showFloorButton !== false;
const useFloatingOnly = floatingOn && floorOn;
const updateState = (state, data = {}) => {
if (useFloatingOnly || (floatingOn && !floorOn)) {
setFloatingState?.(state, data);
} else if (floorOn) {
setStateForMessage(lastIdx, state, data);
}
};
// 确保面板存在 if (floorOn && !useFloatingOnly) {
const messageEl = document.querySelector(`.mes[mesid="${lastIdx}"]`); const messageEl = document.querySelector(`.mes[mesid="${lastIdx}"]`);
if (messageEl) { if (messageEl) {
ensureNovelDrawPanel(messageEl, lastIdx, { force: true }); ensureNovelDrawPanel(messageEl, lastIdx, { force: true });
}
} }
await generateAndInsertImages({ await generateAndInsertImages({
messageId: lastIdx, messageId: lastIdx,
skipLock: true,
onStateChange: (state, data) => { onStateChange: (state, data) => {
switch (state) { switch (state) {
case 'llm': case 'llm':
setStateForMessage(lastIdx, FloatState.LLM); updateState(FloatState.LLM);
break; break;
case 'gen': case 'gen':
case 'progress': case 'progress':
setStateForMessage(lastIdx, FloatState.GEN, data); updateState(FloatState.GEN, data);
break; break;
case 'cooldown': case 'cooldown':
setStateForMessage(lastIdx, FloatState.COOLDOWN, data); updateState(FloatState.COOLDOWN, data);
break; break;
case 'success': case 'success':
setStateForMessage( updateState(
lastIdx, (data.aborted && data.success === 0) ? FloatState.IDLE
data.success === data.total ? FloatState.SUCCESS : FloatState.PARTIAL, : (data.success < data.total) ? FloatState.PARTIAL
: FloatState.SUCCESS,
data data
); );
break; break;
} }
} }
@@ -1993,8 +2016,16 @@ async function autoGenerateForLastAI() {
} catch (e) { } catch (e) {
console.error('[NovelDraw] 自动配图失败:', e); console.error('[NovelDraw] 自动配图失败:', e);
try { try {
const { setStateForMessage, FloatState } = await import('./floating-panel.js'); const { setStateForMessage, setFloatingState, FloatState } = await import('./floating-panel.js');
setStateForMessage(lastIdx, FloatState.ERROR, { error: classifyError(e) }); const floatingOn = s.showFloatingButton === true;
const floorOn = s.showFloorButton !== false;
const useFloatingOnly = floatingOn && floorOn;
if (useFloatingOnly || (floatingOn && !floorOn)) {
setFloatingState?.(FloatState.ERROR, { error: classifyError(e) });
} else if (floorOn) {
setStateForMessage(lastIdx, FloatState.ERROR, { error: classifyError(e) });
}
} catch {} } catch {}
} finally { } finally {
autoBusy = false; autoBusy = false;