Add files via upload
This commit is contained in:
@@ -437,6 +437,27 @@ body {
|
||||
from { opacity: 0; transform: translateX(-50%) translateY(10px); }
|
||||
to { opacity: 1; transform: translateX(-50%) translateY(0); }
|
||||
}
|
||||
.stat-warning {
|
||||
font-size: 0.625rem;
|
||||
color: #ff9800;
|
||||
margin-top: 4px;
|
||||
}
|
||||
#keep-visible-count {
|
||||
width: 32px;
|
||||
padding: 2px 4px;
|
||||
margin: 0 2px;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
font-size: inherit;
|
||||
font-weight: bold;
|
||||
color: var(--highlight);
|
||||
text-align: center;
|
||||
border-radius: 3px;
|
||||
}
|
||||
#keep-visible-count:focus {
|
||||
border-color: var(--accent);
|
||||
outline: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -458,14 +479,17 @@ body {
|
||||
<div class="stat-item">
|
||||
<div class="stat-value"><span class="highlight" id="stat-pending">0</span></div>
|
||||
<div class="stat-label">待总结</div>
|
||||
<div class="stat-warning hidden" id="pending-warning">再删1条将回滚</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="controls-bar">
|
||||
<label class="status-checkbox">
|
||||
<input type="checkbox" id="hide-summarized">
|
||||
<span>聊天时隐藏已总结 · <strong id="summarized-count">0</strong> 楼(保留3楼)</span>
|
||||
</label>
|
||||
<span>聊天时隐藏已总结 · <strong id="summarized-count">0</strong> 楼(保留
|
||||
<input type="number" id="keep-visible-count" min="0" max="50" value="3">
|
||||
楼)</span>
|
||||
</label>
|
||||
<span class="spacer"></span>
|
||||
<button class="btn btn-icon" id="btn-settings">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
@@ -681,7 +705,16 @@ function preserveAddedAt(newItem, oldItem) { if (oldItem?._addedAt != null) newI
|
||||
function loadConfig() {
|
||||
try {
|
||||
const saved = localStorage.getItem('summary_panel_config');
|
||||
if (saved) { const p = JSON.parse(saved); Object.assign(config.api, p.api || {}); Object.assign(config.gen, p.gen || {}); Object.assign(config.trigger, p.trigger || {}); }
|
||||
if (saved) {
|
||||
const p = JSON.parse(saved);
|
||||
Object.assign(config.api, p.api || {});
|
||||
Object.assign(config.gen, p.gen || {});
|
||||
Object.assign(config.trigger, p.trigger || {});
|
||||
if (config.trigger.timing === 'manual' && config.trigger.enabled) {
|
||||
config.trigger.enabled = false;
|
||||
saveConfig();
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
function saveConfig() { try { localStorage.setItem('summary_panel_config', JSON.stringify(config)); } catch {} }
|
||||
@@ -921,7 +954,15 @@ function renderArcs(arcs) {
|
||||
});
|
||||
});
|
||||
}
|
||||
function updateStats(s) { if (!s) return; document.getElementById('stat-summarized').textContent = s.summarizedUpTo ?? 0; document.getElementById('stat-events').textContent = s.eventsCount ?? 0; document.getElementById('stat-pending').textContent = s.pendingFloors ?? 0; }
|
||||
function updateStats(s) {
|
||||
if (!s) return;
|
||||
document.getElementById('stat-summarized').textContent = s.summarizedUpTo ?? 0;
|
||||
document.getElementById('stat-events').textContent = s.eventsCount ?? 0;
|
||||
|
||||
const pending = s.pendingFloors ?? 0;
|
||||
document.getElementById('stat-pending').textContent = pending;
|
||||
document.getElementById('pending-warning').classList.toggle('hidden', pending !== -1);
|
||||
}
|
||||
const editorModal = document.getElementById('editor-modal');
|
||||
const editorTextarea = document.getElementById('editor-textarea');
|
||||
const editorError = document.getElementById('editor-error');
|
||||
@@ -1141,6 +1182,17 @@ function openSettings() {
|
||||
document.getElementById('trigger-enabled').checked = config.trigger.enabled;
|
||||
document.getElementById('trigger-interval').value = config.trigger.interval;
|
||||
document.getElementById('trigger-timing').value = config.trigger.timing;
|
||||
|
||||
const enabledCheckbox = document.getElementById('trigger-enabled');
|
||||
if (config.trigger.timing === 'manual') {
|
||||
enabledCheckbox.checked = false;
|
||||
enabledCheckbox.disabled = true;
|
||||
enabledCheckbox.parentElement.style.opacity = '0.5';
|
||||
} else {
|
||||
enabledCheckbox.disabled = false;
|
||||
enabledCheckbox.parentElement.style.opacity = '1';
|
||||
}
|
||||
|
||||
if (config.api.modelCache.length > 0) {
|
||||
const sel = document.getElementById('api-model-select');
|
||||
sel.innerHTML = config.api.modelCache.map(m => `<option value="${m}" ${m === config.api.model ? 'selected' : ''}>${m}</option>`).join('');
|
||||
@@ -1169,9 +1221,12 @@ function closeSettings(save) {
|
||||
config.gen.top_k = pn('gen-top-k');
|
||||
config.gen.presence_penalty = pn('gen-presence');
|
||||
config.gen.frequency_penalty = pn('gen-frequency');
|
||||
config.trigger.enabled = document.getElementById('trigger-enabled').checked;
|
||||
|
||||
const timing = document.getElementById('trigger-timing').value;
|
||||
config.trigger.timing = timing;
|
||||
config.trigger.enabled = (timing === 'manual') ? false : document.getElementById('trigger-enabled').checked;
|
||||
config.trigger.interval = parseInt(document.getElementById('trigger-interval').value) || 20;
|
||||
config.trigger.timing = document.getElementById('trigger-timing').value;
|
||||
|
||||
saveConfig();
|
||||
}
|
||||
tempConfig = null;
|
||||
@@ -1254,7 +1309,12 @@ window.addEventListener('message', event => {
|
||||
updateStats(data.stats);
|
||||
document.getElementById('summarized-count').textContent = data.stats.hiddenCount ?? 0;
|
||||
}
|
||||
if (data.hideSummarized !== undefined) document.getElementById('hide-summarized').checked = data.hideSummarized;
|
||||
if (data.hideSummarized !== undefined) {
|
||||
document.getElementById('hide-summarized').checked = data.hideSummarized;
|
||||
}
|
||||
if (data.keepVisibleCount !== undefined) {
|
||||
document.getElementById('keep-visible-count').value = data.keepVisibleCount;
|
||||
}
|
||||
break;
|
||||
case 'SUMMARY_FULL_DATA':
|
||||
if (data.payload) {
|
||||
@@ -1294,17 +1354,43 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
renderKeywords([]);
|
||||
renderTimeline([]);
|
||||
renderArcs([]);
|
||||
|
||||
document.getElementById('hide-summarized').addEventListener('change', e => {
|
||||
window.parent.postMessage({ source: 'LittleWhiteBox-StoryFrame', type: 'TOGGLE_HIDE_SUMMARIZED', enabled: e.target.checked }, '*');
|
||||
});
|
||||
|
||||
document.getElementById('keep-visible-count').addEventListener('change', e => {
|
||||
const count = Math.max(0, Math.min(50, parseInt(e.target.value) || 3));
|
||||
e.target.value = count;
|
||||
window.parent.postMessage({
|
||||
source: 'LittleWhiteBox-StoryFrame',
|
||||
type: 'UPDATE_KEEP_VISIBLE',
|
||||
count: count
|
||||
}, '*');
|
||||
});
|
||||
|
||||
document.getElementById('btn-fullscreen-relations').addEventListener('click', openRelationsFullscreen);
|
||||
document.getElementById('relations-fullscreen-backdrop').addEventListener('click', closeRelationsFullscreen);
|
||||
document.getElementById('relations-fullscreen-close').addEventListener('click', closeRelationsFullscreen);
|
||||
|
||||
document.getElementById('trigger-timing').addEventListener('change', e => {
|
||||
const timing = e.target.value;
|
||||
const enabledCheckbox = document.getElementById('trigger-enabled');
|
||||
if (timing === 'manual') {
|
||||
enabledCheckbox.checked = false;
|
||||
enabledCheckbox.disabled = true;
|
||||
enabledCheckbox.parentElement.style.opacity = '0.5';
|
||||
} else {
|
||||
enabledCheckbox.disabled = false;
|
||||
enabledCheckbox.parentElement.style.opacity = '1';
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
relationChart?.resize();
|
||||
relationChartFullscreen?.resize();
|
||||
});
|
||||
|
||||
window.parent.postMessage({ source: 'LittleWhiteBox-StoryFrame', type: 'FRAME_READY' }, '*');
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -22,7 +22,6 @@ const events = createModuleEvents(MODULE_ID);
|
||||
const SUMMARY_SESSION_ID = 'xb9';
|
||||
const SUMMARY_PROMPT_KEY = 'LittleWhiteBox_StorySummary';
|
||||
const iframePath = `${extensionFolderPath}/modules/story-summary/story-summary.html`;
|
||||
const KEEP_VISIBLE_COUNT = 3;
|
||||
const VALID_SECTIONS = ['keywords', 'events', 'characters', 'arcs'];
|
||||
|
||||
const PROVIDER_MAP = {
|
||||
@@ -54,8 +53,14 @@ let eventsRegistered = false;
|
||||
|
||||
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
||||
|
||||
function getKeepVisibleCount() {
|
||||
const store = getSummaryStore();
|
||||
return store?.keepVisibleCount ?? 3;
|
||||
}
|
||||
|
||||
function calcHideRange(lastSummarized) {
|
||||
const hideEnd = lastSummarized - KEEP_VISIBLE_COUNT;
|
||||
const keepCount = getKeepVisibleCount();
|
||||
const hideEnd = lastSummarized - keepCount;
|
||||
if (hideEnd < 0) return null;
|
||||
return { start: 0, end: hideEnd };
|
||||
}
|
||||
@@ -217,25 +222,35 @@ function rollbackSummaryIfNeeded() {
|
||||
const currentLength = Array.isArray(chat) ? chat.length : 0;
|
||||
const store = getSummaryStore();
|
||||
|
||||
if (!store || store.lastSummarizedMesId == null || store.lastSummarizedMesId < 0) return false;
|
||||
if (!store || store.lastSummarizedMesId == null || store.lastSummarizedMesId < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (currentLength <= store.lastSummarizedMesId) {
|
||||
const deletedCount = store.lastSummarizedMesId + 1 - currentLength;
|
||||
if (deletedCount < 2) return false;
|
||||
const lastSummarized = store.lastSummarizedMesId;
|
||||
|
||||
xbLog.warn(MODULE_ID, `删除已总结楼层 ${deletedCount} 个,触发回滚`);
|
||||
if (currentLength <= lastSummarized) {
|
||||
const deletedCount = lastSummarized + 1 - currentLength;
|
||||
|
||||
if (deletedCount < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
xbLog.warn(MODULE_ID, `删除已总结楼层 ${deletedCount} 条,当前${currentLength},原总结到${lastSummarized + 1},触发回滚`);
|
||||
|
||||
const history = store.summaryHistory || [];
|
||||
let targetEndMesId = -1;
|
||||
|
||||
for (let i = history.length - 1; i >= 0; i--) {
|
||||
if (history[i].endMesId < currentLength) {
|
||||
targetEndMesId = history[i].endMesId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
executeFilterRollback(store, targetEndMesId, currentLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -251,6 +266,7 @@ function executeFilterRollback(store, targetEndMesId, currentLength) {
|
||||
store.hideSummarizedHistory = false;
|
||||
} else {
|
||||
const json = store.json || {};
|
||||
|
||||
json.events = (json.events || []).filter(e => (e._addedAt ?? 0) <= targetEndMesId);
|
||||
json.keywords = (json.keywords || []).filter(k => (k._addedAt ?? 0) <= targetEndMesId);
|
||||
json.arcs = (json.arcs || []).filter(a => (a._addedAt ?? 0) <= targetEndMesId);
|
||||
@@ -259,6 +275,7 @@ function executeFilterRollback(store, targetEndMesId, currentLength) {
|
||||
typeof m === 'string' || (m._addedAt ?? 0) <= targetEndMesId
|
||||
);
|
||||
});
|
||||
|
||||
if (json.characters) {
|
||||
json.characters.main = (json.characters.main || []).filter(m =>
|
||||
typeof m === 'string' || (m._addedAt ?? 0) <= targetEndMesId
|
||||
@@ -267,15 +284,20 @@ function executeFilterRollback(store, targetEndMesId, currentLength) {
|
||||
(r._addedAt ?? 0) <= targetEndMesId
|
||||
);
|
||||
}
|
||||
|
||||
store.json = json;
|
||||
store.lastSummarizedMesId = targetEndMesId;
|
||||
store.summaryHistory = (store.summaryHistory || []).filter(h => h.endMesId <= targetEndMesId);
|
||||
}
|
||||
|
||||
if (oldHideRange) {
|
||||
const newHideRange = targetEndMesId >= 0 ? calcHideRange(targetEndMesId) : null;
|
||||
const unhideStart = newHideRange ? newHideRange.end + 1 : 0;
|
||||
if (oldHideRange && oldHideRange.end >= 0) {
|
||||
const newHideRange = (targetEndMesId >= 0 && store.hideSummarizedHistory)
|
||||
? calcHideRange(targetEndMesId)
|
||||
: null;
|
||||
|
||||
const unhideStart = newHideRange ? Math.min(newHideRange.end + 1, currentLength) : 0;
|
||||
const unhideEnd = Math.min(oldHideRange.end, currentLength - 1);
|
||||
|
||||
if (unhideStart <= unhideEnd) {
|
||||
executeSlashCommand(`/unhide ${unhideStart}-${unhideEnd}`);
|
||||
}
|
||||
@@ -440,6 +462,39 @@ function handleFrameMessage(event) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "UPDATE_KEEP_VISIBLE": {
|
||||
const store = getSummaryStore();
|
||||
if (!store) break;
|
||||
|
||||
const oldCount = store.keepVisibleCount ?? 3;
|
||||
const newCount = Math.max(0, Math.min(50, parseInt(data.count) || 3));
|
||||
|
||||
if (newCount === oldCount) break;
|
||||
|
||||
store.keepVisibleCount = newCount;
|
||||
saveSummaryStore();
|
||||
|
||||
const lastSummarized = store.lastSummarizedMesId ?? -1;
|
||||
|
||||
if (store.hideSummarizedHistory && lastSummarized >= 0) {
|
||||
(async () => {
|
||||
await executeSlashCommand(`/unhide 0-${lastSummarized}`);
|
||||
const range = calcHideRange(lastSummarized);
|
||||
if (range) {
|
||||
await executeSlashCommand(`/hide ${range.start}-${range.end}`);
|
||||
}
|
||||
const { chat } = getContext();
|
||||
const totalFloors = Array.isArray(chat) ? chat.length : 0;
|
||||
sendFrameBaseData(store, totalFloors);
|
||||
})();
|
||||
} else {
|
||||
const { chat } = getContext();
|
||||
const totalFloors = Array.isArray(chat) ? chat.length : 0;
|
||||
sendFrameBaseData(store, totalFloors);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -558,6 +613,7 @@ function sendFrameBaseData(store, totalFloors) {
|
||||
hiddenCount,
|
||||
},
|
||||
hideSummarized: store?.hideSummarizedHistory || false,
|
||||
keepVisibleCount: store?.keepVisibleCount ?? 3,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -721,11 +777,18 @@ function getSummaryPanelConfig() {
|
||||
const raw = localStorage.getItem('summary_panel_config');
|
||||
if (!raw) return defaults;
|
||||
const parsed = JSON.parse(raw);
|
||||
return {
|
||||
|
||||
const result = {
|
||||
api: { ...defaults.api, ...(parsed.api || {}) },
|
||||
gen: { ...defaults.gen, ...(parsed.gen || {}) },
|
||||
trigger: { ...defaults.trigger, ...(parsed.trigger || {}) },
|
||||
};
|
||||
|
||||
if (result.trigger.timing === 'manual') {
|
||||
result.trigger.enabled = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch {
|
||||
return defaults;
|
||||
}
|
||||
@@ -876,10 +939,12 @@ async function maybeAutoRunSummary(reason) {
|
||||
|
||||
const cfgAll = getSummaryPanelConfig();
|
||||
const trig = cfgAll.trigger || {};
|
||||
|
||||
if (trig.timing === 'manual') return;
|
||||
if (!trig.enabled) return;
|
||||
if (trig.timing === 'after_ai' && reason !== 'after_ai') return;
|
||||
if (trig.timing === 'before_user' && reason !== 'before_user') return;
|
||||
if (trig.timing === 'manual') return;
|
||||
|
||||
if (isSummaryGenerating()) return;
|
||||
|
||||
const store = getSummaryStore();
|
||||
@@ -976,29 +1041,34 @@ function clearSummaryExtensionPrompt() {
|
||||
|
||||
function handleChatChanged() {
|
||||
const { chat } = getContext();
|
||||
lastKnownChatLength = Array.isArray(chat) ? chat.length : 0;
|
||||
const newLength = Array.isArray(chat) ? chat.length : 0;
|
||||
|
||||
rollbackSummaryIfNeeded();
|
||||
|
||||
lastKnownChatLength = newLength;
|
||||
initButtonsForAll();
|
||||
updateSummaryExtensionPrompt();
|
||||
|
||||
const store = getSummaryStore();
|
||||
const lastSummarized = store?.lastSummarizedMesId ?? -1;
|
||||
if (lastSummarized >= 0 && store?.hideSummarizedHistory) {
|
||||
|
||||
if (lastSummarized >= 0 && store?.hideSummarizedHistory === true) {
|
||||
const range = calcHideRange(lastSummarized);
|
||||
if (range) executeSlashCommand(`/hide ${range.start}-${range.end}`);
|
||||
}
|
||||
|
||||
if (frameReady) {
|
||||
sendFrameBaseData(store, lastKnownChatLength);
|
||||
sendFrameFullData(store, lastKnownChatLength);
|
||||
sendFrameBaseData(store, newLength);
|
||||
sendFrameFullData(store, newLength);
|
||||
}
|
||||
}
|
||||
|
||||
function handleMessageDeleted() {
|
||||
const { chat } = getContext();
|
||||
const currentLength = Array.isArray(chat) ? chat.length : 0;
|
||||
if (currentLength < lastKnownChatLength) {
|
||||
rollbackSummaryIfNeeded();
|
||||
}
|
||||
|
||||
rollbackSummaryIfNeeded();
|
||||
|
||||
lastKnownChatLength = currentLength;
|
||||
updateSummaryExtensionPrompt();
|
||||
}
|
||||
@@ -1021,7 +1091,11 @@ function handleMessageSent() {
|
||||
|
||||
function handleMessageUpdated() {
|
||||
const { chat } = getContext();
|
||||
lastKnownChatLength = Array.isArray(chat) ? chat.length : 0;
|
||||
const currentLength = Array.isArray(chat) ? chat.length : 0;
|
||||
|
||||
rollbackSummaryIfNeeded();
|
||||
|
||||
lastKnownChatLength = currentLength;
|
||||
updateSummaryExtensionPrompt();
|
||||
initButtonsForAll();
|
||||
}
|
||||
@@ -1050,7 +1124,7 @@ function registerEvents() {
|
||||
getSize: () => pendingFrameMessages.length,
|
||||
getBytes: () => {
|
||||
try {
|
||||
return JSON.stringify(pendingFrameMessages || []).length * 2; // UTF-16
|
||||
return JSON.stringify(pendingFrameMessages || []).length * 2;
|
||||
} catch {
|
||||
return 0;
|
||||
}
|
||||
@@ -1061,15 +1135,18 @@ function registerEvents() {
|
||||
},
|
||||
});
|
||||
|
||||
const { chat } = getContext();
|
||||
lastKnownChatLength = Array.isArray(chat) ? chat.length : 0;
|
||||
|
||||
initButtonsForAll();
|
||||
|
||||
events.on(event_types.CHAT_CHANGED, () => setTimeout(handleChatChanged, 80));
|
||||
events.on(event_types.MESSAGE_DELETED, () => setTimeout(handleMessageDeleted, 100));
|
||||
events.on(event_types.MESSAGE_DELETED, () => setTimeout(handleMessageDeleted, 50));
|
||||
events.on(event_types.MESSAGE_RECEIVED, () => setTimeout(handleMessageReceived, 150));
|
||||
events.on(event_types.MESSAGE_SENT, () => setTimeout(handleMessageSent, 150));
|
||||
events.on(event_types.MESSAGE_SWIPED, () => setTimeout(handleMessageUpdated, 150));
|
||||
events.on(event_types.MESSAGE_UPDATED, () => setTimeout(handleMessageUpdated, 150));
|
||||
events.on(event_types.MESSAGE_EDITED, () => setTimeout(handleMessageUpdated, 150));
|
||||
events.on(event_types.MESSAGE_SWIPED, () => setTimeout(handleMessageUpdated, 100));
|
||||
events.on(event_types.MESSAGE_UPDATED, () => setTimeout(handleMessageUpdated, 100));
|
||||
events.on(event_types.MESSAGE_EDITED, () => setTimeout(handleMessageUpdated, 100));
|
||||
events.on(event_types.USER_MESSAGE_RENDERED, data => setTimeout(() => handleMessageRendered(data), 50));
|
||||
events.on(event_types.CHARACTER_MESSAGE_RENDERED, data => setTimeout(() => handleMessageRendered(data), 50));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user