@@ -643,8 +660,8 @@
EnaPlanner
${escapeHtml(item.model || '-')}
${item.error ? `
${escapeHtml(item.error)}
` : ''}
-
请求消息
- ${escapeHtml(JSON.stringify(item.requestMessages || [], null, 2))}
+ 请求消息 (${(item.requestMessages || []).length} 条)
+ ${msgHtml}
原始回复
${escapeHtml(item.rawReply || '')}
From 24ca51051b0677063c2a177886ed041a75314022 Mon Sep 17 00:00:00 2001
From: Hao19911125 <99091644+Hao19911125@users.noreply.github.com>
Date: Sun, 1 Mar 2026 23:07:36 +0800
Subject: [PATCH 04/22] =?UTF-8?q?=E5=B0=8F=E7=99=BD=E6=9D=BF=E5=86=85?=
=?UTF-8?q?=E5=AE=B9=E6=9B=9D=E9=9C=B2=E7=BB=99ena-planner?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
modules/story-outline/story-outline.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/story-outline/story-outline.js b/modules/story-outline/story-outline.js
index aa01a92..3a0a08c 100644
--- a/modules/story-outline/story-outline.js
+++ b/modules/story-outline/story-outline.js
@@ -1395,4 +1395,4 @@ jQuery(() => {
window.registerModuleCleanup?.('storyOutline', cleanup);
});
-export { cleanup };
+export { cleanup, formatOutlinePrompt };
From f5eacbaebe00beea4291235d26afc1aa10de747d Mon Sep 17 00:00:00 2001
From: Hao19911125 <99091644+Hao19911125@users.noreply.github.com>
Date: Sun, 1 Mar 2026 23:10:44 +0800
Subject: [PATCH 05/22] =?UTF-8?q?=E5=B0=8F=E7=99=BD=E6=9D=BF=E5=86=85?=
=?UTF-8?q?=E5=AE=B9=E6=9B=9D=E9=9C=B2=E7=BB=99ena-planner?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
modules/ena-planner/ena-planner.js | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/modules/ena-planner/ena-planner.js b/modules/ena-planner/ena-planner.js
index fcc6bd9..db12f4f 100644
--- a/modules/ena-planner/ena-planner.js
+++ b/modules/ena-planner/ena-planner.js
@@ -7,6 +7,7 @@ import { extensionFolderPath } from '../../core/constants.js';
import { EnaPlannerStorage } from '../../core/server-storage.js';
import { postToIframe, isTrustedIframeEvent } from '../../core/iframe-messaging.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 OVERLAY_ID = 'xiaobaix-ena-planner-overlay';
@@ -1135,6 +1136,7 @@ async function buildPlannerMessages(rawUserInput) {
const scanText = [charBlockRaw, cachedSummary, recentChatRaw, vectorRaw, plotsRaw, rawUserInput].join('\n\n');
const worldbookRaw = await buildWorldbookBlock(scanText);
+ const outlineRaw = typeof formatOutlinePrompt === 'function' ? (formatOutlinePrompt() || '') : '';
// Render templates/macros
const charBlock = await renderTemplateAll(charBlockRaw, env, messageVars);
@@ -1144,6 +1146,7 @@ async function buildPlannerMessages(rawUserInput) {
const storySummary = cachedSummary.trim().length > 30 ? await renderTemplateAll(cachedSummary, env, messageVars) : '';
const worldbook = await renderTemplateAll(worldbookRaw, env, messageVars);
const userInput = await renderTemplateAll(rawUserInput, env, messageVars);
+ const storyOutline = outlineRaw.trim().length > 10 ? await renderTemplateAll(outlineRaw, env, messageVars) : '';
const messages = [];
@@ -1159,6 +1162,11 @@ async function buildPlannerMessages(rawUserInput) {
// 3) Worldbook
if (String(worldbook).trim()) messages.push({ role: 'system', content: worldbook });
+ // 3.5) Story Outline / 剧情地图(小白板世界架构)
+ if (storyOutline.trim()) {
+ messages.push({ role: 'system', content: `\n${storyOutline}\n` });
+ }
+
// 4) Chat history (last 2 AI responses — floors N-1 & N-3)
if (String(recentChat).trim()) messages.push({ role: 'system', content: recentChat });
From ce32861ca75c29c82126af0bc8837eb281019bc3 Mon Sep 17 00:00:00 2001
From: Hao19911125 <99091644+Hao19911125@users.noreply.github.com>
Date: Wed, 4 Mar 2026 13:57:53 +0800
Subject: [PATCH 06/22] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E4=B8=96=E7=95=8C?=
=?UTF-8?q?=E4=B9=A6=E5=AE=8F=E8=AF=BB=E5=8F=96=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
modules/ena-planner/ena-planner.js | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/modules/ena-planner/ena-planner.js b/modules/ena-planner/ena-planner.js
index db12f4f..57ec8db 100644
--- a/modules/ena-planner/ena-planner.js
+++ b/modules/ena-planner/ena-planner.js
@@ -8,6 +8,7 @@ import { EnaPlannerStorage } from '../../core/server-storage.js';
import { postToIframe, isTrustedIframeEvent } from '../../core/iframe-messaging.js';
import { DEFAULT_PROMPT_BLOCKS, BUILTIN_TEMPLATES } from './ena-planner-presets.js';
import { formatOutlinePrompt } from '../story-outline/story-outline.js';
+import jsyaml from '../../libs/js-yaml.mjs';
const EXT_NAME = 'ena-planner';
const OVERLAY_ID = 'xiaobaix-ena-planner-overlay';
@@ -551,6 +552,7 @@ function matchSelective(entry, scanText) {
const keys2 = Array.isArray(entry?.keysecondary) ? entry.keysecondary.filter(Boolean) : [];
const total = keys.length;
+ if (total === 0) return false;
const hit = keys.reduce((acc, kw) => acc + (keywordPresent(scanText, kw) ? 1 : 0), 0);
let ok = false;
@@ -838,6 +840,17 @@ function resolveGetMessageVariableMacros(text, messageVars) {
});
}
+function resolveFormatMessageVariableMacros(text, messageVars) {
+ return text.replace(/{{\s*format_message_variable::([^}]+)\s*}}/g, (_, rawPath) => {
+ const path = String(rawPath || '').trim();
+ if (!path) return '';
+ const val = deepGet(messageVars, path);
+ if (val == null) return '';
+ if (typeof val === 'string') return val;
+ try { return jsyaml.dump(val, { lineWidth: -1, noRefs: true }); } catch { return safeStringify(val); }
+ });
+}
+
function getLatestMessageVarTable() {
try {
if (window.Mvu?.getMvuData) {
@@ -858,6 +871,7 @@ async function renderTemplateAll(text, env, messageVars) {
out = await evalEjsIfPossible(out, env);
out = substituteMacrosViaST(out);
out = resolveGetMessageVariableMacros(out, messageVars);
+ out = resolveFormatMessageVariableMacros(out, messageVars);
return out;
}
From fe9736a1b425d09c9dfe665d1432882defcdcf35 Mon Sep 17 00:00:00 2001
From: Hao19911125 <99091644+Hao19911125@users.noreply.github.com>
Date: Mon, 9 Mar 2026 16:51:11 +0800
Subject: [PATCH 07/22] =?UTF-8?q?=E4=BF=AE=E6=AD=A3summary=E8=A7=A6?=
=?UTF-8?q?=E5=8F=91=E7=BB=BF=E7=81=AF=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
modules/ena-planner/ena-planner.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/ena-planner/ena-planner.js b/modules/ena-planner/ena-planner.js
index 57ec8db..c4db7cc 100644
--- a/modules/ena-planner/ena-planner.js
+++ b/modules/ena-planner/ena-planner.js
@@ -1147,7 +1147,7 @@ async function buildPlannerMessages(rawUserInput) {
const vectorRaw = '';
// Build scanText for worldbook keyword activation
- const scanText = [charBlockRaw, cachedSummary, recentChatRaw, vectorRaw, plotsRaw, rawUserInput].join('\n\n');
+ const scanText = [charBlockRaw, recentChatRaw, vectorRaw, plotsRaw, rawUserInput].join('\n\n');
const worldbookRaw = await buildWorldbookBlock(scanText);
const outlineRaw = typeof formatOutlinePrompt === 'function' ? (formatOutlinePrompt() || '') : '';
From d7f6d1f22bef2b6704d5f5a457d36922241dde23 Mon Sep 17 00:00:00 2001
From: Hao19911125 <99091644+Hao19911125@users.noreply.github.com>
Date: Mon, 9 Mar 2026 21:43:13 +0800
Subject: [PATCH 08/22] =?UTF-8?q?=E5=90=91=E9=87=8F=E5=AD=98=E5=82=A8?=
=?UTF-8?q?=E5=88=B0ST=E7=AB=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../story-summary/vector/storage/vector-io.js | 327 ++++++++++++++++++
1 file changed, 327 insertions(+)
diff --git a/modules/story-summary/vector/storage/vector-io.js b/modules/story-summary/vector/storage/vector-io.js
index 99899a7..f293dfd 100644
--- a/modules/story-summary/vector/storage/vector-io.js
+++ b/modules/story-summary/vector/storage/vector-io.js
@@ -5,6 +5,7 @@
import { zipSync, unzipSync, strToU8, strFromU8 } from '../../../../libs/fflate.mjs';
import { getContext } from '../../../../../../../extensions.js';
+import { getRequestHeaders } from '../../../../../../../../script.js';
import { xbLog } from '../../../../core/debug-core.js';
import {
getMeta,
@@ -72,6 +73,30 @@ function downloadBlob(blob, filename) {
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
+// 二进制 Uint8Array → base64(分块处理,避免 btoa 栈溢出)
+function uint8ToBase64(uint8) {
+ const CHUNK = 0x8000;
+ let result = '';
+ for (let i = 0; i < uint8.length; i += CHUNK) {
+ result += String.fromCharCode.apply(null, uint8.subarray(i, i + CHUNK));
+ }
+ return btoa(result);
+}
+
+// base64 → Uint8Array
+function base64ToUint8(base64) {
+ const binary = atob(base64);
+ const bytes = new Uint8Array(binary.length);
+ for (let i = 0; i < binary.length; i++) {
+ bytes[i] = binary.charCodeAt(i);
+ }
+ return bytes;
+}
+
+// 服务器备份文件名
+function getBackupFilename(chatId) {
+ return `LWB_VectorBackup_${chatId}.zip`;
+}
// ═══════════════════════════════════════════════════════════════════════════
// 导出
@@ -383,3 +408,305 @@ export async function importVectors(file, onProgress) {
fingerprintMismatch,
};
}
+// ═══════════════════════════════════════════════════════════════════════════
+// 备份到服务器
+// ═══════════════════════════════════════════════════════════════════════════
+
+export async function backupToServer(onProgress) {
+ const { chatId } = getContext();
+ if (!chatId) {
+ throw new Error('未打开聊天');
+ }
+
+ onProgress?.('读取数据...');
+
+ const meta = await getMeta(chatId);
+ const chunks = await getAllChunks(chatId);
+ const chunkVectors = await getAllChunkVectors(chatId);
+ const eventVectors = await getAllEventVectors(chatId);
+ const stateAtoms = getStateAtoms();
+ const stateVectors = await getAllStateVectors(chatId);
+
+ if (chunkVectors.length === 0 && eventVectors.length === 0 && stateVectors.length === 0) {
+ throw new Error('没有可备份的向量数据');
+ }
+
+ const dims = chunkVectors[0]?.vector?.length
+ || eventVectors[0]?.vector?.length
+ || stateVectors[0]?.vector?.length
+ || 0;
+ if (dims === 0) {
+ throw new Error('无法确定向量维度');
+ }
+
+ onProgress?.('构建索引...');
+
+ const sortedChunks = [...chunks].sort((a, b) => a.chunkId.localeCompare(b.chunkId));
+ const chunkVectorMap = new Map(chunkVectors.map(cv => [cv.chunkId, cv.vector]));
+
+ const chunksJsonl = sortedChunks.map(c => JSON.stringify({
+ chunkId: c.chunkId,
+ floor: c.floor,
+ chunkIdx: c.chunkIdx,
+ speaker: c.speaker,
+ isUser: c.isUser,
+ text: c.text,
+ textHash: c.textHash,
+ })).join('\n');
+
+ const chunkVectorsOrdered = sortedChunks.map(c => chunkVectorMap.get(c.chunkId) || new Array(dims).fill(0));
+
+ onProgress?.('压缩向量...');
+
+ const sortedEventVectors = [...eventVectors].sort((a, b) => a.eventId.localeCompare(b.eventId));
+ const eventsJsonl = sortedEventVectors.map(ev => JSON.stringify({
+ eventId: ev.eventId,
+ })).join('\n');
+ const eventVectorsOrdered = sortedEventVectors.map(ev => ev.vector);
+
+ const sortedStateVectors = [...stateVectors].sort((a, b) => String(a.atomId).localeCompare(String(b.atomId)));
+ const stateVectorsOrdered = sortedStateVectors.map(v => v.vector);
+ const rDims = sortedStateVectors.find(v => v.rVector?.length)?.rVector?.length || dims;
+ const stateRVectorsOrdered = sortedStateVectors.map(v =>
+ v.rVector?.length ? v.rVector : new Array(rDims).fill(0)
+ );
+ const stateVectorsJsonl = sortedStateVectors.map(v => JSON.stringify({
+ atomId: v.atomId,
+ floor: v.floor,
+ hasRVector: !!(v.rVector?.length),
+ rDims: v.rVector?.length || 0,
+ })).join('\n');
+
+ const manifest = {
+ version: EXPORT_VERSION,
+ exportedAt: Date.now(),
+ chatId,
+ fingerprint: meta.fingerprint || '',
+ dims,
+ chunkCount: sortedChunks.length,
+ chunkVectorCount: chunkVectors.length,
+ eventCount: sortedEventVectors.length,
+ stateAtomCount: stateAtoms.length,
+ stateVectorCount: stateVectors.length,
+ stateRVectorCount: sortedStateVectors.filter(v => v.rVector?.length).length,
+ rDims,
+ lastChunkFloor: meta.lastChunkFloor ?? -1,
+ };
+
+ onProgress?.('打包文件...');
+
+ const zipData = zipSync({
+ 'manifest.json': strToU8(JSON.stringify(manifest, null, 2)),
+ 'chunks.jsonl': strToU8(chunksJsonl),
+ 'chunk_vectors.bin': float32ToBytes(chunkVectorsOrdered, dims),
+ 'events.jsonl': strToU8(eventsJsonl),
+ 'event_vectors.bin': float32ToBytes(eventVectorsOrdered, dims),
+ 'state_atoms.json': strToU8(JSON.stringify(stateAtoms)),
+ 'state_vectors.jsonl': strToU8(stateVectorsJsonl),
+ 'state_vectors.bin': stateVectorsOrdered.length
+ ? float32ToBytes(stateVectorsOrdered, dims)
+ : new Uint8Array(0),
+ 'state_r_vectors.bin': stateRVectorsOrdered.length
+ ? float32ToBytes(stateRVectorsOrdered, rDims)
+ : new Uint8Array(0),
+ }, { level: 1 });
+
+ onProgress?.('上传到服务器...');
+
+ const base64 = uint8ToBase64(zipData);
+ const filename = getBackupFilename(chatId);
+
+ const res = await fetch('/api/files/upload', {
+ method: 'POST',
+ headers: getRequestHeaders(),
+ body: JSON.stringify({ name: filename, data: base64 }),
+ });
+ if (!res.ok) {
+ throw new Error(`服务器返回 ${res.status}`);
+ }
+
+ const sizeMB = (zipData.byteLength / 1024 / 1024).toFixed(2);
+ xbLog.info(MODULE_ID, `备份完成: ${filename} (${sizeMB}MB)`);
+
+ return {
+ filename,
+ size: zipData.byteLength,
+ chunkCount: sortedChunks.length,
+ eventCount: sortedEventVectors.length,
+ };
+}
+
+// ═══════════════════════════════════════════════════════════════════════════
+// 从服务器恢复
+// ═══════════════════════════════════════════════════════════════════════════
+
+export async function restoreFromServer(onProgress) {
+ const { chatId } = getContext();
+ if (!chatId) {
+ throw new Error('未打开聊天');
+ }
+
+ onProgress?.('从服务器下载...');
+
+ const filename = getBackupFilename(chatId);
+ const res = await fetch(`/user/files/${filename}`, {
+ headers: getRequestHeaders(),
+ cache: 'no-cache',
+ });
+
+ if (!res.ok) {
+ if (res.status === 404) {
+ throw new Error('服务器上没有找到此聊天的备份');
+ }
+ throw new Error(`服务器返回 ${res.status}`);
+ }
+
+ const text = await res.text();
+ if (!text) {
+ throw new Error('服务器上没有找到此聊天的备份');
+ }
+
+ onProgress?.('解压文件...');
+
+ const zipData = base64ToUint8(text);
+
+ let unzipped;
+ try {
+ unzipped = unzipSync(zipData);
+ } catch (e) {
+ throw new Error('备份文件格式错误,无法解压');
+ }
+
+ if (!unzipped['manifest.json']) {
+ throw new Error('缺少 manifest.json');
+ }
+
+ const manifest = JSON.parse(strFromU8(unzipped['manifest.json']));
+
+ if (![1, 2].includes(manifest.version)) {
+ throw new Error(`不支持的版本: ${manifest.version}`);
+ }
+
+ onProgress?.('校验数据...');
+
+ const vectorCfg = getVectorConfig();
+ const currentFingerprint = vectorCfg ? getEngineFingerprint(vectorCfg) : '';
+ const fingerprintMismatch = manifest.fingerprint && currentFingerprint && manifest.fingerprint !== currentFingerprint;
+ const chatIdMismatch = manifest.chatId !== chatId;
+
+ const warnings = [];
+ if (fingerprintMismatch) {
+ warnings.push(`向量引擎不匹配(文件: ${manifest.fingerprint}, 当前: ${currentFingerprint}),导入后需重新生成`);
+ }
+ if (chatIdMismatch) {
+ warnings.push(`聊天ID不匹配(文件: ${manifest.chatId}, 当前: ${chatId})`);
+ }
+
+ onProgress?.('解析数据...');
+
+ const chunksJsonl = unzipped['chunks.jsonl'] ? strFromU8(unzipped['chunks.jsonl']) : '';
+ const chunkMetas = chunksJsonl.split('\n').filter(Boolean).map(line => JSON.parse(line));
+
+ const chunkVectorsBytes = unzipped['chunk_vectors.bin'];
+ const chunkVectors = chunkVectorsBytes ? bytesToFloat32(chunkVectorsBytes, manifest.dims) : [];
+
+ const eventsJsonl = unzipped['events.jsonl'] ? strFromU8(unzipped['events.jsonl']) : '';
+ const eventMetas = eventsJsonl.split('\n').filter(Boolean).map(line => JSON.parse(line));
+
+ const eventVectorsBytes = unzipped['event_vectors.bin'];
+ const eventVectors = eventVectorsBytes ? bytesToFloat32(eventVectorsBytes, manifest.dims) : [];
+
+ const stateAtoms = unzipped['state_atoms.json']
+ ? JSON.parse(strFromU8(unzipped['state_atoms.json']))
+ : [];
+
+ const stateVectorsJsonl = unzipped['state_vectors.jsonl'] ? strFromU8(unzipped['state_vectors.jsonl']) : '';
+ const stateVectorMetas = stateVectorsJsonl.split('\n').filter(Boolean).map(line => JSON.parse(line));
+
+ const stateVectorsBytes = unzipped['state_vectors.bin'];
+ const stateVectors = (stateVectorsBytes && stateVectorMetas.length)
+ ? bytesToFloat32(stateVectorsBytes, manifest.dims)
+ : [];
+ const stateRVectorsBytes = unzipped['state_r_vectors.bin'];
+ const stateRVectors = (stateRVectorsBytes && stateVectorMetas.length)
+ ? bytesToFloat32(stateRVectorsBytes, manifest.rDims || manifest.dims)
+ : [];
+ const hasRVectorMeta = stateVectorMetas.some(m => typeof m.hasRVector === 'boolean');
+
+ if (chunkMetas.length !== chunkVectors.length) {
+ throw new Error(`chunk 数量不匹配: 元数据 ${chunkMetas.length}, 向量 ${chunkVectors.length}`);
+ }
+ if (eventMetas.length !== eventVectors.length) {
+ throw new Error(`event 数量不匹配: 元数据 ${eventMetas.length}, 向量 ${eventVectors.length}`);
+ }
+ if (stateVectorMetas.length !== stateVectors.length) {
+ throw new Error(`state 向量数量不匹配: 元数据 ${stateVectorMetas.length}, 向量 ${stateVectors.length}`);
+ }
+ if (stateRVectors.length > 0 && stateVectorMetas.length !== stateRVectors.length) {
+ throw new Error(`state r-vector count mismatch: meta=${stateVectorMetas.length}, vectors=${stateRVectors.length}`);
+ }
+
+ onProgress?.('清空旧数据...');
+
+ await clearAllChunks(chatId);
+ await clearEventVectors(chatId);
+ await clearStateVectors(chatId);
+ clearStateAtoms();
+
+ onProgress?.('写入数据...');
+
+ if (chunkMetas.length > 0) {
+ const chunksToSave = chunkMetas.map(meta => ({
+ chunkId: meta.chunkId,
+ floor: meta.floor,
+ chunkIdx: meta.chunkIdx,
+ speaker: meta.speaker,
+ isUser: meta.isUser,
+ text: meta.text,
+ textHash: meta.textHash,
+ }));
+ await saveChunks(chatId, chunksToSave);
+
+ const chunkVectorItems = chunkMetas.map((meta, idx) => ({
+ chunkId: meta.chunkId,
+ vector: chunkVectors[idx],
+ }));
+ await saveChunkVectors(chatId, chunkVectorItems, manifest.fingerprint);
+ }
+
+ if (eventMetas.length > 0) {
+ const eventVectorItems = eventMetas.map((meta, idx) => ({
+ eventId: meta.eventId,
+ vector: eventVectors[idx],
+ }));
+ await saveEventVectors(chatId, eventVectorItems, manifest.fingerprint);
+ }
+
+ if (stateAtoms.length > 0) {
+ saveStateAtoms(stateAtoms);
+ }
+
+ if (stateVectorMetas.length > 0) {
+ const stateVectorItems = stateVectorMetas.map((meta, idx) => ({
+ atomId: meta.atomId,
+ floor: meta.floor,
+ vector: stateVectors[idx],
+ rVector: (stateRVectors[idx] && (!hasRVectorMeta || meta.hasRVector)) ? stateRVectors[idx] : null,
+ }));
+ await saveStateVectors(chatId, stateVectorItems, manifest.fingerprint);
+ }
+
+ await updateMeta(chatId, {
+ fingerprint: manifest.fingerprint,
+ lastChunkFloor: manifest.lastChunkFloor,
+ });
+
+ xbLog.info(MODULE_ID, `从服务器恢复完成: ${chunkMetas.length} chunks, ${eventMetas.length} events, ${stateAtoms.length} state atoms`);
+
+ return {
+ chunkCount: chunkMetas.length,
+ eventCount: eventMetas.length,
+ warnings,
+ fingerprintMismatch,
+ };
+}
From ea940be35d0e76124957a0b6d8651679f708e903 Mon Sep 17 00:00:00 2001
From: Hao19911125 <99091644+Hao19911125@users.noreply.github.com>
Date: Mon, 9 Mar 2026 21:45:46 +0800
Subject: [PATCH 09/22] =?UTF-8?q?=E5=90=91=E9=87=8F=E5=AD=98=E5=82=A8?=
=?UTF-8?q?=E5=88=B0ST=E7=AB=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
modules/story-summary/story-summary.html | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/modules/story-summary/story-summary.html b/modules/story-summary/story-summary.html
index bef07e8..bff0d77 100644
--- a/modules/story-summary/story-summary.html
+++ b/modules/story-summary/story-summary.html
@@ -561,6 +561,13 @@ 编辑
style="flex:1">导入向量数据