sync(ena-planner): align planner and outline with upstream
This commit is contained in:
@@ -618,6 +618,50 @@ textarea.input {
|
|||||||
font-size: .8125rem;
|
font-size: .8125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Message cards inside log */
|
||||||
|
.msg-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg-card {
|
||||||
|
border-radius: var(--radius);
|
||||||
|
border-left: 3px solid var(--bdr);
|
||||||
|
background: var(--code-bg);
|
||||||
|
padding: 8px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg-card.msg-system { border-left-color: #6b8afd; }
|
||||||
|
.msg-card.msg-user { border-left-color: #4ecdc4; }
|
||||||
|
.msg-card.msg-assistant { border-left-color: #f7a046; }
|
||||||
|
|
||||||
|
.msg-role {
|
||||||
|
font-size: .6875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: .04em;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
color: var(--txt3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg-system .msg-role { color: #6b8afd; }
|
||||||
|
.msg-user .msg-role { color: #4ecdc4; }
|
||||||
|
.msg-assistant .msg-role { color: #f7a046; }
|
||||||
|
|
||||||
|
.msg-content {
|
||||||
|
font-family: 'SF Mono', Monaco, Consolas, 'Liberation Mono', monospace;
|
||||||
|
font-size: .6875rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
color: var(--code-txt);
|
||||||
|
margin: 0;
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
details {
|
details {
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
@@ -841,4 +885,4 @@ details summary:hover {
|
|||||||
details summary {
|
details summary {
|
||||||
padding: 8px 0;
|
padding: 8px 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<header>
|
<header>
|
||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<h1>Ena<span>Planner</span></h1>
|
<h1>Ena<span>Planner</span></h1>
|
||||||
<div class="subtitle">Story Planning · LLM Integration</div>
|
<div class="subtitle">Story Planning · LLM Integration —— Created by Hao19911125</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats">
|
<div class="stats">
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
@@ -636,6 +636,23 @@
|
|||||||
const time = item.time ? new Date(item.time).toLocaleString() : '-';
|
const time = item.time ? new Date(item.time).toLocaleString() : '-';
|
||||||
const cls = item.ok ? 'success' : 'error';
|
const cls = item.ok ? 'success' : 'error';
|
||||||
const label = item.ok ? '成功' : '失败';
|
const label = item.ok ? '成功' : '失败';
|
||||||
|
|
||||||
|
// Format request messages: one card per message with role label
|
||||||
|
let msgHtml = '';
|
||||||
|
if (Array.isArray(item.requestMessages) && item.requestMessages.length) {
|
||||||
|
msgHtml = item.requestMessages.map((m, i) => {
|
||||||
|
const role = escapeHtml(m.role || 'unknown');
|
||||||
|
const roleClass = role === 'system' ? 'msg-system' : role === 'user' ? 'msg-user' : 'msg-assistant';
|
||||||
|
const content = escapeHtml(m.content || '');
|
||||||
|
return `<div class="msg-card ${roleClass}">
|
||||||
|
<div class="msg-role">[${i + 1}] ${role}</div>
|
||||||
|
<pre class="msg-content">${content}</pre>
|
||||||
|
</div>`;
|
||||||
|
}).join('');
|
||||||
|
} else {
|
||||||
|
msgHtml = '<div class="log-empty">无消息</div>';
|
||||||
|
}
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="log-item">
|
<div class="log-item">
|
||||||
<div class="log-meta">
|
<div class="log-meta">
|
||||||
@@ -643,8 +660,8 @@
|
|||||||
<span>${escapeHtml(item.model || '-')}</span>
|
<span>${escapeHtml(item.model || '-')}</span>
|
||||||
</div>
|
</div>
|
||||||
${item.error ? `<div class="log-error">${escapeHtml(item.error)}</div>` : ''}
|
${item.error ? `<div class="log-error">${escapeHtml(item.error)}</div>` : ''}
|
||||||
<details><summary>请求消息</summary>
|
<details><summary>请求消息 (${(item.requestMessages || []).length} 条)</summary>
|
||||||
<pre class="log-pre">${escapeHtml(JSON.stringify(item.requestMessages || [], null, 2))}</pre>
|
<div class="msg-list">${msgHtml}</div>
|
||||||
</details>
|
</details>
|
||||||
<details><summary>原始回复</summary>
|
<details><summary>原始回复</summary>
|
||||||
<pre class="log-pre">${escapeHtml(item.rawReply || '')}</pre>
|
<pre class="log-pre">${escapeHtml(item.rawReply || '')}</pre>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { extensionFolderPath } from '../../core/constants.js';
|
|||||||
import { EnaPlannerStorage } from '../../core/server-storage.js';
|
import { EnaPlannerStorage } from '../../core/server-storage.js';
|
||||||
import { postToIframe, isTrustedIframeEvent } from '../../core/iframe-messaging.js';
|
import { postToIframe, isTrustedIframeEvent } from '../../core/iframe-messaging.js';
|
||||||
import { DEFAULT_PROMPT_BLOCKS, BUILTIN_TEMPLATES } from './ena-planner-presets.js';
|
import { DEFAULT_PROMPT_BLOCKS, BUILTIN_TEMPLATES } from './ena-planner-presets.js';
|
||||||
|
import { formatOutlinePrompt } from '../story-outline/story-outline.js';
|
||||||
|
|
||||||
const EXT_NAME = 'ena-planner';
|
const EXT_NAME = 'ena-planner';
|
||||||
const OVERLAY_ID = 'xiaobaix-ena-planner-overlay';
|
const OVERLAY_ID = 'xiaobaix-ena-planner-overlay';
|
||||||
@@ -307,7 +308,8 @@ function formatCharCardBlock(charObj) {
|
|||||||
function cleanAiMessageText(text) {
|
function cleanAiMessageText(text) {
|
||||||
let out = String(text ?? '');
|
let out = String(text ?? '');
|
||||||
|
|
||||||
// 1) Strip properly wrapped <think>/<thinking> blocks only.
|
// 1) Strip everything before and including </think> (handles unclosed think blocks)
|
||||||
|
out = out.replace(/^[\s\S]*?<\/think>/i, '');
|
||||||
out = out.replace(/<think\b[^>]*>[\s\S]*?<\/think>/gi, '');
|
out = out.replace(/<think\b[^>]*>[\s\S]*?<\/think>/gi, '');
|
||||||
out = out.replace(/<thinking\b[^>]*>[\s\S]*?<\/thinking>/gi, '');
|
out = out.replace(/<thinking\b[^>]*>[\s\S]*?<\/thinking>/gi, '');
|
||||||
|
|
||||||
@@ -688,19 +690,51 @@ async function buildWorldbookBlock(scanText) {
|
|||||||
* --------------------------
|
* --------------------------
|
||||||
*/
|
*/
|
||||||
function getChatVariables() {
|
function getChatVariables() {
|
||||||
// Try multiple paths to get ST chat variables
|
let vars = {};
|
||||||
|
|
||||||
|
// 1) Chat-level variables
|
||||||
|
try {
|
||||||
|
const ctx = getContextSafe();
|
||||||
|
if (ctx?.chatMetadata?.variables) vars = { ...ctx.chatMetadata.variables };
|
||||||
|
} catch {}
|
||||||
|
if (!Object.keys(vars).length) {
|
||||||
try {
|
try {
|
||||||
const ctx = getContextSafe();
|
if (window.chat_metadata?.variables) vars = { ...window.chat_metadata.variables };
|
||||||
if (ctx?.chatMetadata?.variables) return ctx.chatMetadata.variables;
|
} catch {}
|
||||||
} catch { }
|
}
|
||||||
|
if (!Object.keys(vars).length) {
|
||||||
try {
|
try {
|
||||||
if (window.chat_metadata?.variables) return window.chat_metadata.variables;
|
const ctx = getContextSafe();
|
||||||
} catch { }
|
if (ctx?.chat_metadata?.variables) vars = { ...ctx.chat_metadata.variables };
|
||||||
try {
|
} catch {}
|
||||||
const ctx = getContextSafe();
|
}
|
||||||
if (ctx?.chat_metadata?.variables) return ctx.chat_metadata.variables;
|
|
||||||
} catch { }
|
// 2) Always merge message-level variables (some presets store vars here instead of chat-level)
|
||||||
return {};
|
try {
|
||||||
|
const msgVars = getLatestMessageVarTable();
|
||||||
|
if (msgVars && typeof msgVars === 'object') {
|
||||||
|
for (const key of Object.keys(msgVars)) {
|
||||||
|
// Skip MVU internal metadata keys
|
||||||
|
if (key === 'schema' || key === 'display_data' || key === 'delta_data') continue;
|
||||||
|
if (vars[key] === undefined) {
|
||||||
|
// Chat-level doesn't have this key at all — take from message-level
|
||||||
|
vars[key] = msgVars[key];
|
||||||
|
} else if (
|
||||||
|
vars[key] && typeof vars[key] === 'object' && !Array.isArray(vars[key]) &&
|
||||||
|
msgVars[key] && typeof msgVars[key] === 'object' && !Array.isArray(msgVars[key])
|
||||||
|
) {
|
||||||
|
// Both have this key as objects — shallow merge (message-level fills gaps)
|
||||||
|
for (const subKey of Object.keys(msgVars[key])) {
|
||||||
|
if (vars[key][subKey] === undefined) {
|
||||||
|
vars[key][subKey] = msgVars[key][subKey];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
return vars;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildEjsContext() {
|
function buildEjsContext() {
|
||||||
@@ -735,6 +769,7 @@ function buildEjsContext() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
getvar, setvar,
|
getvar, setvar,
|
||||||
|
vars,
|
||||||
Number, Math, JSON, String, Array, Object, parseInt, parseFloat,
|
Number, Math, JSON, String, Array, Object, parseInt, parseFloat,
|
||||||
console: { log: () => { }, warn: () => { }, error: () => { } },
|
console: { log: () => { }, warn: () => { }, error: () => { } },
|
||||||
};
|
};
|
||||||
@@ -1101,6 +1136,7 @@ async function buildPlannerMessages(rawUserInput) {
|
|||||||
const scanText = [charBlockRaw, cachedSummary, recentChatRaw, vectorRaw, plotsRaw, rawUserInput].join('\n\n');
|
const scanText = [charBlockRaw, cachedSummary, recentChatRaw, vectorRaw, plotsRaw, rawUserInput].join('\n\n');
|
||||||
|
|
||||||
const worldbookRaw = await buildWorldbookBlock(scanText);
|
const worldbookRaw = await buildWorldbookBlock(scanText);
|
||||||
|
const outlineRaw = typeof formatOutlinePrompt === 'function' ? (formatOutlinePrompt() || '') : '';
|
||||||
|
|
||||||
// Render templates/macros
|
// Render templates/macros
|
||||||
const charBlock = await renderTemplateAll(charBlockRaw, env, messageVars);
|
const charBlock = await renderTemplateAll(charBlockRaw, env, messageVars);
|
||||||
@@ -1110,6 +1146,7 @@ async function buildPlannerMessages(rawUserInput) {
|
|||||||
const storySummary = cachedSummary.trim().length > 30 ? await renderTemplateAll(cachedSummary, env, messageVars) : '';
|
const storySummary = cachedSummary.trim().length > 30 ? await renderTemplateAll(cachedSummary, env, messageVars) : '';
|
||||||
const worldbook = await renderTemplateAll(worldbookRaw, env, messageVars);
|
const worldbook = await renderTemplateAll(worldbookRaw, env, messageVars);
|
||||||
const userInput = await renderTemplateAll(rawUserInput, env, messageVars);
|
const userInput = await renderTemplateAll(rawUserInput, env, messageVars);
|
||||||
|
const storyOutline = outlineRaw.trim().length > 10 ? await renderTemplateAll(outlineRaw, env, messageVars) : '';
|
||||||
|
|
||||||
const messages = [];
|
const messages = [];
|
||||||
|
|
||||||
@@ -1125,6 +1162,11 @@ async function buildPlannerMessages(rawUserInput) {
|
|||||||
// 3) Worldbook
|
// 3) Worldbook
|
||||||
if (String(worldbook).trim()) messages.push({ role: 'system', content: worldbook });
|
if (String(worldbook).trim()) messages.push({ role: 'system', content: worldbook });
|
||||||
|
|
||||||
|
// 3.5) Story Outline / 剧情地图(小白板世界架构)
|
||||||
|
if (storyOutline.trim()) {
|
||||||
|
messages.push({ role: 'system', content: `<plot_map>\n${storyOutline}\n</plot_map>` });
|
||||||
|
}
|
||||||
|
|
||||||
// 4) Chat history (last 2 AI responses — floors N-1 & N-3)
|
// 4) Chat history (last 2 AI responses — floors N-1 & N-3)
|
||||||
if (String(recentChat).trim()) messages.push({ role: 'system', content: recentChat });
|
if (String(recentChat).trim()) messages.push({ role: 'system', content: recentChat });
|
||||||
|
|
||||||
|
|||||||
@@ -1395,4 +1395,4 @@ jQuery(() => {
|
|||||||
window.registerModuleCleanup?.('storyOutline', cleanup);
|
window.registerModuleCleanup?.('storyOutline', cleanup);
|
||||||
});
|
});
|
||||||
|
|
||||||
export { cleanup };
|
export { cleanup, formatOutlinePrompt };
|
||||||
|
|||||||
Reference in New Issue
Block a user