diff --git a/modules/story-summary/data/store.js b/modules/story-summary/data/store.js
index 366ea97..0f96255 100644
--- a/modules/story-summary/data/store.js
+++ b/modules/story-summary/data/store.js
@@ -76,6 +76,27 @@ export function isRelationFact(f) {
return /^对.+的/.test(f.p);
}
+// ═══════════════════════════════════════════════════════════════════════════
+// 从 facts 提取关系(供关系图 UI 使用)
+// ═══════════════════════════════════════════════════════════════════════════
+
+export function extractRelationshipsFromFacts(facts) {
+ return (facts || [])
+ .filter(f => !f.retracted && isRelationFact(f))
+ .map(f => {
+ const match = f.p.match(/^对(.+)的/);
+ const to = match ? match[1] : '';
+ if (!to) return null;
+ return {
+ from: f.s,
+ to,
+ label: f.o,
+ trend: f.trend || '陌生',
+ };
+ })
+ .filter(Boolean);
+}
+
/**
* 生成 fact 的唯一键(s + p)
*/
diff --git a/modules/story-summary/story-summary-ui.js b/modules/story-summary/story-summary-ui.js
index c4f25b1..14c898e 100644
--- a/modules/story-summary/story-summary-ui.js
+++ b/modules/story-summary/story-summary-ui.js
@@ -1862,52 +1862,39 @@ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "
function renderFacts(facts) {
- summaryData.facts = facts || [];
+ summaryData.facts = facts || [];
- const container = $('facts-list');
- if (!container) return;
+ const container = $('facts-list');
+ if (!container) return;
- const activeFacts = (facts || []).filter(f => !f.retracted);
+ const isRelation = f => /^对.+的/.test(f.p);
+ const stateFacts = (facts || []).filter(f => !f.retracted && !isRelation(f));
- if (!activeFacts.length) {
- setHtml(container, '
暂无事实记录
');
- return;
- }
+ if (!stateFacts.length) {
+ setHtml(container, '暂无状态记录
');
+ return;
+ }
- const relations = activeFacts.filter(f => /^对.+的/.test(f.p));
- const states = activeFacts.filter(f => !/^对.+的/.test(f.p));
+ const grouped = new Map();
+ for (const f of stateFacts) {
+ if (!grouped.has(f.s)) grouped.set(f.s, []);
+ grouped.get(f.s).push(f);
+ }
- let html = '';
-
- if (states.length) {
- html += `
-
状态/属性
- ${states.map(f => `
+ let html = '';
+ for (const [subject, items] of grouped) {
+ html += `
+
${h(subject)}
+ ${items.map(f => `
- ${h(f.s)}
${h(f.p)}
${h(f.o)}
#${(f.since || 0) + 1}
`).join('')}
`;
- }
+ }
- if (relations.length) {
- html += `
-
人物关系
- ${relations.map(f => `
-
- ${h(f.s)}
- ${h(f.p)}
- ${h(f.o)}
- ${f.trend ? `${h(f.trend)}` : ''}
- #${(f.since || 0) + 1}
-
- `).join('')}
-
`;
+ setHtml(container, html);
}
-
- setHtml(container, html);
-}
})();
diff --git a/modules/story-summary/story-summary.css b/modules/story-summary/story-summary.css
index cb70f02..6fe71c0 100644
--- a/modules/story-summary/story-summary.css
+++ b/modules/story-summary/story-summary.css
@@ -21,7 +21,7 @@
}
.fact-group {
- margin-bottom: 16px;
+ margin-bottom: 12px;
}
.fact-group:last-child {
@@ -29,65 +29,43 @@
}
.fact-group-title {
- font-size: 0.6875rem;
+ font-size: 0.75rem;
font-weight: 600;
- text-transform: uppercase;
- letter-spacing: 0.08em;
- color: var(--txt3);
- margin-bottom: 8px;
- padding-bottom: 6px;
- border-bottom: 1px solid var(--bdr2);
+ color: var(--hl);
+ margin-bottom: 6px;
+ padding-bottom: 4px;
+ border-bottom: 1px dashed var(--bdr2);
}
.fact-item {
display: flex;
align-items: center;
- gap: 6px;
- padding: 8px 10px;
- margin-bottom: 6px;
+ gap: 8px;
+ padding: 6px 10px;
+ margin-bottom: 4px;
background: var(--bg3);
border: 1px solid var(--bdr2);
- border-radius: 6px;
+ border-radius: 4px;
font-size: 0.8125rem;
- flex-wrap: wrap;
-}
-
-.fact-item:hover {
- border-color: var(--bdr);
- background: var(--bg2);
-}
-
-.fact-subject {
- font-weight: 600;
- color: var(--txt);
}
.fact-predicate {
- color: var(--txt3);
- font-size: 0.75rem;
+ color: var(--txt2);
+ min-width: 60px;
}
-.fact-predicate::before {
- content: '→';
- margin-right: 4px;
+.fact-predicate::after {
+ content: ':';
}
.fact-object {
- color: var(--hl);
- font-weight: 500;
-}
-
-.fact-trend {
- font-size: 0.6875rem;
- padding: 2px 8px;
- border-radius: 10px;
- white-space: nowrap;
+ color: var(--txt);
+ flex: 1;
}
.fact-since {
font-size: 0.625rem;
color: var(--txt3);
- margin-left: auto;
}
@media (max-width: 768px) {
diff --git a/modules/story-summary/story-summary.html b/modules/story-summary/story-summary.html
index 405e206..e0f7e2b 100644
--- a/modules/story-summary/story-summary.html
+++ b/modules/story-summary/story-summary.html
@@ -83,7 +83,7 @@
diff --git a/modules/story-summary/story-summary.js b/modules/story-summary/story-summary.js
index 09120e2..cfb95a5 100644
--- a/modules/story-summary/story-summary.js
+++ b/modules/story-summary/story-summary.js
@@ -30,6 +30,7 @@ import {
calcHideRange,
rollbackSummaryIfNeeded,
clearSummaryData,
+ extractRelationshipsFromFacts,
} from "./data/store.js";
// prompt text builder
@@ -848,14 +849,19 @@ async function sendFrameBaseData(store, totalFloors) {
function sendFrameFullData(store, totalFloors) {
const lastSummarized = store?.lastSummarizedMesId ?? -1;
if (store?.json) {
+ const facts = store.json.facts || [];
+ const relationships = extractRelationshipsFromFacts(facts);
postToFrame({
type: "SUMMARY_FULL_DATA",
payload: {
keywords: store.json.keywords || [],
events: store.json.events || [],
- characters: store.json.characters || { main: [], relationships: [] },
+ characters: {
+ main: store.json.characters?.main || [],
+ relationships,
+ },
arcs: store.json.arcs || [],
- world: store.json.world || [],
+ facts,
lastSummarizedMesId: lastSummarized,
},
});