Add files via upload
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable no-new-func */
|
||||||
// Story Outline 提示词模板配置
|
// Story Outline 提示词模板配置
|
||||||
// 统一 UAUA (User-Assistant-User-Assistant) 结构
|
// 统一 UAUA (User-Assistant-User-Assistant) 结构
|
||||||
|
|
||||||
@@ -198,6 +199,13 @@ const DEFAULT_JSON_TEMPLATES = {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}`,
|
||||||
|
worldNewsRefresh: `{
|
||||||
|
"world": {
|
||||||
|
"news": [
|
||||||
|
{ "title": "新闻标题", "time": "时间(可选)", "content": "新闻内容" }
|
||||||
|
]
|
||||||
|
}
|
||||||
}`,
|
}`,
|
||||||
localMapGen: `{
|
localMapGen: `{
|
||||||
"review": {
|
"review": {
|
||||||
@@ -260,7 +268,7 @@ const DEFAULT_PROMPTS = {
|
|||||||
stranger: {
|
stranger: {
|
||||||
u1: v => `你是TRPG数据整理助手。从剧情文本中提取{{user}}遇到的陌生人/NPC,整理为JSON数组。`,
|
u1: v => `你是TRPG数据整理助手。从剧情文本中提取{{user}}遇到的陌生人/NPC,整理为JSON数组。`,
|
||||||
a1: () => `明白。请提供【世界观】和【剧情经历】,我将提取角色并以JSON数组输出。`,
|
a1: () => `明白。请提供【世界观】和【剧情经历】,我将提取角色并以JSON数组输出。`,
|
||||||
u2: v => `### 上下文\n\n**1. 世界观:**\n${worldInfo}\n\n**2. {{user}}经历:**\n${history(v.historyCount)}${v.storyOutline ? `\n\n**剧情大纲:**\n${wrap('story_outline', v.storyOutline)}` : ''}${nameList(v.existingContacts, v.existingStrangers)}\n\n### 输出要求\n\n1. 返回一个合法 JSON 数组,使用标准 JSON 语法(键名和字符串都用半角双引号 ")\n2. 只提取有具体称呼的角色\n3. 每个角色只需 name / location / info 三个字段\n4. 文本内容中如需使用引号,请使用单引号或中文引号「」或"",不要使用半角双引号 "\n5. 无新角色返回 []\n\n\n模板:${JSON_TEMPLATES.npc}`,
|
u2: v => `### 上下文\n\n**1. 世界观:**\n${worldInfo}\n\n**2. {{user}}经历:**\n${history(v.historyCount)}${v.storyOutline ? `\n\n**剧情大纲:**\n${wrap('story_outline', v.storyOutline)}` : ''}${nameList(v.existingContacts, v.existingStrangers)}\n\n### 输出要求\n\n1. 返回一个合法 JSON 数组,使用标准 JSON 语法(键名和字符串都用半角双引号 ")\n2. 只提取有具体称呼的角色\n3. 每个角色只需 name / location / info 三个字段\n4. 文本内容中如需使用引号,请使用单引号或中文引号「」或"",不要使用半角双引号 "\n5. 无新角色返回 []\n\n\n模板:${JSON_TEMPLATES.stranger}`,
|
||||||
a2: () => `了解,开始生成JSON:`
|
a2: () => `了解,开始生成JSON:`
|
||||||
},
|
},
|
||||||
worldGenStep1: {
|
worldGenStep1: {
|
||||||
@@ -372,6 +380,12 @@ const DEFAULT_PROMPTS = {
|
|||||||
u2: v => `【世界观设定】:\n${worldInfo}\n\n【{{user}}历史】:\n${history(v.historyCount)}\n\n【当前世界状态JSON】(可能包含 meta/world/maps 等字段):\n${v.currentWorldData || '{}'}\n\n【JSON模板(辅助模式)】:\n${JSON_TEMPLATES.worldSimAssist}`,
|
u2: v => `【世界观设定】:\n${worldInfo}\n\n【{{user}}历史】:\n${history(v.historyCount)}\n\n【当前世界状态JSON】(可能包含 meta/world/maps 等字段):\n${v.currentWorldData || '{}'}\n\n【JSON模板(辅助模式)】:\n${JSON_TEMPLATES.worldSimAssist}`,
|
||||||
a2: () => `开始按 worldSimAssist 模板输出JSON:`
|
a2: () => `开始按 worldSimAssist 模板输出JSON:`
|
||||||
},
|
},
|
||||||
|
worldNewsRefresh: {
|
||||||
|
u1: v => `你是世界新闻编辑。基于世界观设定与{{user}}近期经历,为世界生成「最新资讯」。\n\n要求:\n1) 只输出 world.news(不要输出 maps/meta/其他字段)。\n2) news 至少 ${randomRange(2, 4)} 条;语气轻松、中性,夹带少量日常生活细节;可以包含与主剧情相关的跟进报道。\n3) 只输出符合模板的 JSON,禁止解释文字。\n\n- 使用标准 JSON 语法:所有键名与字符串都使用半角双引号\n- 文本内容如需使用引号,请使用单引号或中文引号「」/“”,不要使用半角双引号`,
|
||||||
|
a1: () => `明白。我将只更新 world.news,不改动世界其它字段。请提供当前世界数据。`,
|
||||||
|
u2: v => `【世界观设定】:\n${worldInfo}\n\n【{{user}}历史】:\n${history(v.historyCount)}\n\n【当前世界状态JSON】(可能包含 meta/world/maps 等字段):\n${v.currentWorldData || '{}'}\n\n【JSON模板】:\n${JSON_TEMPLATES.worldNewsRefresh}`,
|
||||||
|
a2: () => `OK, worldNewsRefresh JSON generate start:`
|
||||||
|
},
|
||||||
localMapGen: {
|
localMapGen: {
|
||||||
u1: v => `你是TRPG局部场景生成器。你的任务是根据聊天历史,推断{{user}}当前或将要前往的位置(视经历的最后一条消息而定),并为该位置生成详细的局部地图/室内场景。
|
u1: v => `你是TRPG局部场景生成器。你的任务是根据聊天历史,推断{{user}}当前或将要前往的位置(视经历的最后一条消息而定),并为该位置生成详细的局部地图/室内场景。
|
||||||
|
|
||||||
@@ -588,6 +602,7 @@ export const buildExtractStrangersMessages = v => build('stranger', v);
|
|||||||
export const buildWorldGenStep1Messages = v => build('worldGenStep1', v);
|
export const buildWorldGenStep1Messages = v => build('worldGenStep1', v);
|
||||||
export const buildWorldGenStep2Messages = v => build('worldGenStep2', v);
|
export const buildWorldGenStep2Messages = v => build('worldGenStep2', v);
|
||||||
export const buildWorldSimMessages = v => build(v?.mode === 'assist' ? 'worldSimAssist' : 'worldSim', v);
|
export const buildWorldSimMessages = v => build(v?.mode === 'assist' ? 'worldSimAssist' : 'worldSim', v);
|
||||||
|
export const buildWorldNewsRefreshMessages = v => build('worldNewsRefresh', v);
|
||||||
export const buildSceneSwitchMessages = v => build('sceneSwitch', v);
|
export const buildSceneSwitchMessages = v => build('sceneSwitch', v);
|
||||||
export const buildLocalMapGenMessages = v => build('localMapGen', v);
|
export const buildLocalMapGenMessages = v => build('localMapGen', v);
|
||||||
export const buildLocalMapRefreshMessages = v => build('localMapRefresh', v);
|
export const buildLocalMapRefreshMessages = v => build('localMapRefresh', v);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable no-restricted-syntax */
|
||||||
/**
|
/**
|
||||||
* ============================================================================
|
* ============================================================================
|
||||||
* Story Outline 模块 - 小白板
|
* Story Outline 模块 - 小白板
|
||||||
@@ -20,7 +21,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// ==================== 1. 导入与常量 ====================
|
// ==================== 1. 导入与常量 ====================
|
||||||
import { extension_settings, saveMetadataDebounced } from "../../../../../extensions.js";
|
import { extension_settings, saveMetadataDebounced, writeExtensionField } from "../../../../../extensions.js";
|
||||||
import { chat_metadata, name1, processCommands, eventSource, event_types as st_event_types } from "../../../../../../script.js";
|
import { chat_metadata, name1, processCommands, eventSource, event_types as st_event_types } from "../../../../../../script.js";
|
||||||
import { loadWorldInfo, saveWorldInfo, world_names, world_info } from "../../../../../world-info.js";
|
import { loadWorldInfo, saveWorldInfo, world_names, world_info } from "../../../../../world-info.js";
|
||||||
import { getContext } from "../../../../../st-context.js";
|
import { getContext } from "../../../../../st-context.js";
|
||||||
@@ -32,7 +33,7 @@ import { promptManager } from "../../../../../openai.js";
|
|||||||
import {
|
import {
|
||||||
buildSmsMessages, buildSummaryMessages, buildSmsHistoryContent, buildExistingSummaryContent,
|
buildSmsMessages, buildSummaryMessages, buildSmsHistoryContent, buildExistingSummaryContent,
|
||||||
buildNpcGenerationMessages, formatNpcToWorldbookContent, buildExtractStrangersMessages,
|
buildNpcGenerationMessages, formatNpcToWorldbookContent, buildExtractStrangersMessages,
|
||||||
buildWorldGenStep1Messages, buildWorldGenStep2Messages, buildWorldSimMessages, buildSceneSwitchMessages,
|
buildWorldGenStep1Messages, buildWorldGenStep2Messages, buildWorldSimMessages, buildWorldNewsRefreshMessages, buildSceneSwitchMessages,
|
||||||
buildInviteMessages, buildLocalMapGenMessages, buildLocalMapRefreshMessages, buildLocalSceneGenMessages,
|
buildInviteMessages, buildLocalMapGenMessages, buildLocalMapRefreshMessages, buildLocalSceneGenMessages,
|
||||||
buildOverlayHtml, MOBILE_LAYOUT_STYLE, DESKTOP_LAYOUT_STYLE, getPromptConfigPayload, setPromptConfig
|
buildOverlayHtml, MOBILE_LAYOUT_STYLE, DESKTOP_LAYOUT_STYLE, getPromptConfigPayload, setPromptConfig
|
||||||
} from "./story-outline-prompt.js";
|
} from "./story-outline-prompt.js";
|
||||||
@@ -47,6 +48,86 @@ const DEBUG_KEY = 'LittleWhiteBox_StoryOutline_Debug';
|
|||||||
|
|
||||||
let overlayCreated = false, frameReady = false, pendingMsgs = [], presetCleanup = null, step1Cache = null;
|
let overlayCreated = false, frameReady = false, pendingMsgs = [], presetCleanup = null, step1Cache = null;
|
||||||
|
|
||||||
|
// ==================== PromptConfig (global + character card) ====================
|
||||||
|
// Global: server storage key `promptConfig` (old behavior)
|
||||||
|
// Character: character-card extension field (see scheduled-tasks implementation)
|
||||||
|
|
||||||
|
const PROMPTS_MODULE_NAME = 'xiaobaix-story-outline-prompts';
|
||||||
|
|
||||||
|
let promptConfigGlobal = { jsonTemplates: {}, promptSources: {} };
|
||||||
|
let promptConfigCharacter = { jsonTemplates: {}, promptSources: {} };
|
||||||
|
let promptConfigCharacterId = null;
|
||||||
|
|
||||||
|
function normalizePromptConfig(cfg) {
|
||||||
|
const src = (cfg && typeof cfg === 'object') ? cfg : {};
|
||||||
|
const out = {
|
||||||
|
jsonTemplates: { ...(src.jsonTemplates || {}) },
|
||||||
|
promptSources: {},
|
||||||
|
};
|
||||||
|
const ps = src.promptSources || src.prompts || {};
|
||||||
|
Object.entries(ps).forEach(([k, v]) => {
|
||||||
|
if (!v || typeof v !== 'object' || Array.isArray(v)) return;
|
||||||
|
out.promptSources[k] = { ...v };
|
||||||
|
});
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergePromptConfig(globalCfg, charCfg) {
|
||||||
|
const g = normalizePromptConfig(globalCfg);
|
||||||
|
const c = normalizePromptConfig(charCfg);
|
||||||
|
|
||||||
|
const mergedPromptSources = { ...(g.promptSources || {}) };
|
||||||
|
Object.entries(c.promptSources || {}).forEach(([key, parts]) => {
|
||||||
|
const base = mergedPromptSources[key];
|
||||||
|
mergedPromptSources[key] = (base && typeof base === 'object' && !Array.isArray(base))
|
||||||
|
? { ...base, ...(parts || {}) }
|
||||||
|
: { ...(parts || {}) };
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
jsonTemplates: { ...(g.jsonTemplates || {}), ...(c.jsonTemplates || {}) },
|
||||||
|
promptSources: mergedPromptSources,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCharacterPromptConfig() {
|
||||||
|
const ctx = getContext?.();
|
||||||
|
const charId = ctx?.characterId ?? null;
|
||||||
|
const char = (charId != null) ? ctx?.characters?.[charId] : null;
|
||||||
|
const cfg = char?.data?.extensions?.[PROMPTS_MODULE_NAME]?.promptConfig || null;
|
||||||
|
return normalizePromptConfig(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveCharacterPromptConfig(cfg) {
|
||||||
|
const ctx = getContext?.();
|
||||||
|
const charId = ctx?.characterId ?? null;
|
||||||
|
if (charId == null) return;
|
||||||
|
|
||||||
|
const payload = { promptConfig: normalizePromptConfig(cfg) };
|
||||||
|
await writeExtensionField(Number(charId), PROMPTS_MODULE_NAME, payload);
|
||||||
|
|
||||||
|
// Keep in-memory character extension in sync (same pattern as scheduled-tasks).
|
||||||
|
try {
|
||||||
|
const char = ctx?.characters?.[charId];
|
||||||
|
if (char) {
|
||||||
|
if (!char.data) char.data = {};
|
||||||
|
if (!char.data.extensions) char.data.extensions = {};
|
||||||
|
char.data.extensions[PROMPTS_MODULE_NAME] = payload;
|
||||||
|
}
|
||||||
|
} catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPromptConfigPayloadWithStores() {
|
||||||
|
const base = getPromptConfigPayload?.() || {};
|
||||||
|
return {
|
||||||
|
...base,
|
||||||
|
stores: {
|
||||||
|
global: normalizePromptConfig(promptConfigGlobal),
|
||||||
|
character: normalizePromptConfig(promptConfigCharacter),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== 2. 通用工具 ====================
|
// ==================== 2. 通用工具 ====================
|
||||||
|
|
||||||
/** 移动端检测 */
|
/** 移动端检测 */
|
||||||
@@ -610,17 +691,40 @@ function postFrame(payload) {
|
|||||||
|
|
||||||
const flushPending = () => { if (!frameReady) return; const f = document.getElementById("xiaobaix-story-outline-iframe"); pendingMsgs.forEach(p => { if (f) postToIframe(f, p, "LittleWhiteBox"); }); pendingMsgs = []; };
|
const flushPending = () => { if (!frameReady) return; const f = document.getElementById("xiaobaix-story-outline-iframe"); pendingMsgs.forEach(p => { if (f) postToIframe(f, p, "LittleWhiteBox"); }); pendingMsgs = []; };
|
||||||
|
|
||||||
|
async function syncPromptConfigForCurrentCharacter() {
|
||||||
|
const ctx = getContext?.();
|
||||||
|
const charId = ctx?.characterId ?? null;
|
||||||
|
|
||||||
|
if (promptConfigCharacterId !== charId) {
|
||||||
|
promptConfigCharacterId = charId;
|
||||||
|
promptConfigCharacter = getCharacterPromptConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const cfg = await StoryOutlineStorage?.get?.('promptConfig', null);
|
||||||
|
promptConfigGlobal = normalizePromptConfig(cfg);
|
||||||
|
} catch {
|
||||||
|
promptConfigGlobal = { jsonTemplates: {}, promptSources: {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
const merged = mergePromptConfig(promptConfigGlobal, promptConfigCharacter);
|
||||||
|
setPromptConfig?.(merged, false);
|
||||||
|
postFrame({ type: "PROMPT_CONFIG_UPDATED", promptConfig: getPromptConfigPayloadWithStores() });
|
||||||
|
}
|
||||||
|
|
||||||
/** 发送设置到iframe */
|
/** 发送设置到iframe */
|
||||||
function sendSettings() {
|
function sendSettings() {
|
||||||
const store = getOutlineStore(), { name: charName, desc: charDesc } = getCharInfo();
|
const store = getOutlineStore(), { name: charName, desc: charDesc } = getCharInfo();
|
||||||
|
syncPromptConfigForCurrentCharacter().catch(() => { });
|
||||||
postFrame({
|
postFrame({
|
||||||
type: "LOAD_SETTINGS", globalSettings: getGlobalSettings(), commSettings: getCommSettings(),
|
type: "LOAD_SETTINGS", globalSettings: getGlobalSettings(), commSettings: getCommSettings(),
|
||||||
stage: store?.stage ?? 0, deviationScore: store?.deviationScore ?? 0,
|
stage: store?.stage ?? 0, deviationScore: store?.deviationScore ?? 0,
|
||||||
simulationTarget: store?.simulationTarget ?? 5, playerLocation: store?.playerLocation ?? '家',
|
simulationTarget: store?.simulationTarget ?? 5, playerLocation: store?.playerLocation ?? '家',
|
||||||
dataChecked: store?.dataChecked || {}, outlineData: store?.outlineData || {}, promptConfig: getPromptConfigPayload?.(),
|
dataChecked: store?.dataChecked || {}, outlineData: store?.outlineData || {}, promptConfig: getPromptConfigPayloadWithStores(),
|
||||||
characterCardName: charName, characterCardDescription: charDesc,
|
characterCardName: charName, characterCardDescription: charDesc,
|
||||||
characterContactSmsHistory: getCharSmsHistory()
|
characterContactSmsHistory: getCharSmsHistory()
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadAndSend = () => { const s = getOutlineStore(); if (s?.mapData) postFrame({ type: "LOAD_MAP_DATA", mapData: s.mapData }); sendSettings(); };
|
const loadAndSend = () => { const s = getOutlineStore(); if (s?.mapData) postFrame({ type: "LOAD_MAP_DATA", mapData: s.mapData }); sendSettings(); };
|
||||||
@@ -710,6 +814,7 @@ const V = {
|
|||||||
wg1: d => !!d && typeof d === 'object', // 只要是对象就行,后续会 normalize
|
wg1: d => !!d && typeof d === 'object', // 只要是对象就行,后续会 normalize
|
||||||
wg2: d => !!((d?.world && (d?.maps || d?.world?.maps)?.outdoor) || (d?.outdoor && d?.inside)),
|
wg2: d => !!((d?.world && (d?.maps || d?.world?.maps)?.outdoor) || (d?.outdoor && d?.inside)),
|
||||||
wga: d => !!((d?.world && d?.maps?.outdoor) || d?.outdoor), ws: d => !!d, w: o => !!o && typeof o === 'object',
|
wga: d => !!((d?.world && d?.maps?.outdoor) || d?.outdoor), ws: d => !!d, w: o => !!o && typeof o === 'object',
|
||||||
|
wn: d => Array.isArray(d?.world?.news),
|
||||||
lm: o => !!o?.inside?.name && !!o?.inside?.description
|
lm: o => !!o?.inside?.name && !!o?.inside?.description
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1093,6 +1198,34 @@ async function handleSimWorld({ requestId, currentData, isAuto }) {
|
|||||||
} catch (e) { replyErr('SIMULATE_WORLD_RESULT', requestId, `推演失败: ${e.message}`); }
|
} catch (e) { replyErr('SIMULATE_WORLD_RESULT', requestId, `推演失败: ${e.message}`); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleRefreshWorldNews({ requestId }) {
|
||||||
|
try {
|
||||||
|
const store = getOutlineStore();
|
||||||
|
const od = store?.outlineData;
|
||||||
|
if (!od) return replyErr('REFRESH_WORLD_NEWS_RESULT', requestId, '未找到世界数据,请先生成世界');
|
||||||
|
|
||||||
|
// Store may persist maps either under `maps` or as `outdoor/indoor` (iframe SAVE_ALL_DATA format).
|
||||||
|
const maps = od?.maps || { outdoor: od?.outdoor || null, indoor: od?.indoor || null };
|
||||||
|
const snapshot = {
|
||||||
|
meta: od?.meta || {},
|
||||||
|
world: od?.world || {},
|
||||||
|
maps,
|
||||||
|
...(od?.timeline ? { timeline: od.timeline } : {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const msgs = buildWorldNewsRefreshMessages(getCommonPromptVars({
|
||||||
|
currentWorldData: JSON.stringify(snapshot, null, 2),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const data = await callLLMJson({ messages: msgs, validate: V.wn });
|
||||||
|
if (!Array.isArray(data?.world?.news)) return replyErr('REFRESH_WORLD_NEWS_RESULT', requestId, '世界新闻刷新失败:无法解析 JSON 数据');
|
||||||
|
|
||||||
|
reply('REFRESH_WORLD_NEWS_RESULT', requestId, { success: true, news: data.world.news });
|
||||||
|
} catch (e) {
|
||||||
|
replyErr('REFRESH_WORLD_NEWS_RESULT', requestId, `世界新闻刷新失败: ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleSaveSettings(d) {
|
function handleSaveSettings(d) {
|
||||||
if (d.globalSettings) saveGlobalSettings(d.globalSettings);
|
if (d.globalSettings) saveGlobalSettings(d.globalSettings);
|
||||||
if (d.commSettings) saveCommSettings(d.commSettings);
|
if (d.commSettings) saveCommSettings(d.commSettings);
|
||||||
@@ -1114,39 +1247,56 @@ function handleSaveSettings(d) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleSavePrompts(d) {
|
async function handleSavePrompts(d) {
|
||||||
// Back-compat: full payload (old iframe)
|
const scope = d?.scope === 'character' ? 'character' : 'global';
|
||||||
|
|
||||||
|
// Back-compat: full payload (old iframe) -> treat as global save (old server storage behavior).
|
||||||
if (d?.promptConfig) {
|
if (d?.promptConfig) {
|
||||||
const payload = setPromptConfig?.(d.promptConfig, false) || d.promptConfig;
|
promptConfigGlobal = normalizePromptConfig(d.promptConfig);
|
||||||
try { await StoryOutlineStorage?.set?.('promptConfig', payload); } catch { }
|
try { await StoryOutlineStorage?.set?.('promptConfig', promptConfigGlobal); } catch { }
|
||||||
postFrame({ type: "PROMPT_CONFIG_UPDATED", promptConfig: getPromptConfigPayload?.() });
|
|
||||||
|
// Re-read current character config (if any) and apply merged.
|
||||||
|
promptConfigCharacter = getCharacterPromptConfig();
|
||||||
|
const merged = mergePromptConfig(promptConfigGlobal, promptConfigCharacter);
|
||||||
|
setPromptConfig?.(merged, false);
|
||||||
|
postFrame({ type: "PROMPT_CONFIG_UPDATED", promptConfig: getPromptConfigPayloadWithStores() });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// New: incremental update by key
|
|
||||||
const key = d?.key;
|
const key = d?.key;
|
||||||
if (!key) return;
|
if (!key) return;
|
||||||
|
|
||||||
let current = null;
|
// Always merge against the latest global config from server storage.
|
||||||
try { current = await StoryOutlineStorage?.get?.('promptConfig', null); } catch { }
|
try { promptConfigGlobal = normalizePromptConfig(await StoryOutlineStorage?.get?.('promptConfig', null)); } catch { }
|
||||||
const next = (current && typeof current === 'object') ? {
|
// Always merge against the current character-card config.
|
||||||
jsonTemplates: { ...(current.jsonTemplates || {}) },
|
promptConfigCharacterId = getContext?.()?.characterId ?? null;
|
||||||
promptSources: { ...(current.promptSources || {}) },
|
promptConfigCharacter = getCharacterPromptConfig();
|
||||||
} : { jsonTemplates: {}, promptSources: {} };
|
|
||||||
|
|
||||||
|
const applyDelta = (cfg) => {
|
||||||
|
const next = normalizePromptConfig(cfg);
|
||||||
if (d?.reset) {
|
if (d?.reset) {
|
||||||
delete next.promptSources[key];
|
delete next.promptSources[key];
|
||||||
delete next.jsonTemplates[key];
|
delete next.jsonTemplates[key];
|
||||||
} else {
|
return next;
|
||||||
|
}
|
||||||
if (d?.prompt && typeof d.prompt === 'object') next.promptSources[key] = d.prompt;
|
if (d?.prompt && typeof d.prompt === 'object') next.promptSources[key] = d.prompt;
|
||||||
if ('jsonTemplate' in (d || {})) {
|
if ('jsonTemplate' in (d || {})) {
|
||||||
if (d.jsonTemplate == null) delete next.jsonTemplates[key];
|
if (d.jsonTemplate == null) delete next.jsonTemplates[key];
|
||||||
else next.jsonTemplates[key] = String(d.jsonTemplate ?? '');
|
else next.jsonTemplates[key] = String(d.jsonTemplate ?? '');
|
||||||
}
|
}
|
||||||
|
return next;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (scope === 'character') {
|
||||||
|
promptConfigCharacter = applyDelta(promptConfigCharacter);
|
||||||
|
await saveCharacterPromptConfig(promptConfigCharacter);
|
||||||
|
} else {
|
||||||
|
promptConfigGlobal = applyDelta(promptConfigGlobal);
|
||||||
|
try { await StoryOutlineStorage?.set?.('promptConfig', promptConfigGlobal); } catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload = setPromptConfig?.(next, false) || next;
|
const merged = mergePromptConfig(promptConfigGlobal, promptConfigCharacter);
|
||||||
try { await StoryOutlineStorage?.set?.('promptConfig', payload); } catch { }
|
setPromptConfig?.(merged, false);
|
||||||
postFrame({ type: "PROMPT_CONFIG_UPDATED", promptConfig: getPromptConfigPayload?.() });
|
postFrame({ type: "PROMPT_CONFIG_UPDATED", promptConfig: getPromptConfigPayloadWithStores() });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSaveContacts(d) {
|
function handleSaveContacts(d) {
|
||||||
@@ -1207,6 +1357,7 @@ const handlers = {
|
|||||||
GENERATE_WORLD: handleGenWorld,
|
GENERATE_WORLD: handleGenWorld,
|
||||||
RETRY_WORLD_GEN_STEP2: handleRetryStep2,
|
RETRY_WORLD_GEN_STEP2: handleRetryStep2,
|
||||||
SIMULATE_WORLD: handleSimWorld,
|
SIMULATE_WORLD: handleSimWorld,
|
||||||
|
REFRESH_WORLD_NEWS: handleRefreshWorldNews,
|
||||||
GENERATE_LOCAL_MAP: handleGenLocalMap,
|
GENERATE_LOCAL_MAP: handleGenLocalMap,
|
||||||
REFRESH_LOCAL_MAP: handleRefreshLocalMap,
|
REFRESH_LOCAL_MAP: handleRefreshLocalMap,
|
||||||
GENERATE_LOCAL_SCENE: handleGenLocalScene
|
GENERATE_LOCAL_SCENE: handleGenLocalScene
|
||||||
@@ -1369,10 +1520,7 @@ document.addEventListener('xiaobaixEnabledChanged', e => {
|
|||||||
|
|
||||||
async function initPromptConfigFromServer() {
|
async function initPromptConfigFromServer() {
|
||||||
try {
|
try {
|
||||||
const cfg = await StoryOutlineStorage?.get?.('promptConfig', null);
|
await syncPromptConfigForCurrentCharacter();
|
||||||
if (!cfg) return;
|
|
||||||
setPromptConfig?.(cfg, false);
|
|
||||||
postFrame({ type: "PROMPT_CONFIG_UPDATED", promptConfig: getPromptConfigPayload?.() });
|
|
||||||
} catch { }
|
} catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user