Files
ST-Bionic-Memory-Ecology/hide-engine.js
2026-04-02 16:05:29 +08:00

304 lines
8.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ST-BME: 隐藏旧楼层引擎
// 通过临时把旧楼层标记为 is_system=true让宿主主回复与 ST-BME 自己的聊天读取一起跳过这些楼层。
const hideState = {
managedChatRef: null,
hiddenIndices: new Set(),
lastProcessedLength: 0,
scheduledTimer: null,
};
function getTimerApi(runtime = {}) {
return {
setTimeout:
typeof runtime.setTimeout === "function"
? runtime.setTimeout.bind(runtime)
: globalThis.setTimeout.bind(globalThis),
clearTimeout:
typeof runtime.clearTimeout === "function"
? runtime.clearTimeout.bind(runtime)
: globalThis.clearTimeout.bind(globalThis),
};
}
function getJquery(runtime = {}) {
if (typeof runtime.$ === "function") return runtime.$;
if (typeof globalThis.$ === "function") return globalThis.$;
return null;
}
function getCurrentChat(runtime = {}) {
try {
const context =
typeof runtime.getContext === "function" ? runtime.getContext() : null;
return Array.isArray(context?.chat) ? context.chat : null;
} catch {
return null;
}
}
function normalizeHideSettings(settings = {}) {
return {
enabled: Boolean(settings.enabled),
hideLastN: Math.max(
0,
Math.trunc(
Number(
settings.hideLastN ??
settings.hide_last_n ??
settings.keepLastN ??
settings.keep_last_n ??
0,
) || 0,
),
),
};
}
function syncSystemAttribute(chat, indices = [], value = "true", runtime = {}) {
if (!Array.isArray(indices) || indices.length === 0) return;
if (getCurrentChat(runtime) !== chat) return;
const jq = getJquery(runtime);
if (!jq) return;
const selector = indices.map((index) => `.mes[mesid="${index}"]`).join(",");
if (!selector) return;
jq(selector).attr("is_system", value);
}
function unhideTrackedChat(chat, runtime = {}) {
if (!Array.isArray(chat) || hideState.hiddenIndices.size === 0) {
return { shownCount: 0 };
}
const toShow = [];
for (const index of hideState.hiddenIndices) {
const message = chat[index];
if (!message || message.is_system !== true) continue;
message.is_system = false;
toShow.push(index);
}
syncSystemAttribute(chat, toShow, "false", runtime);
return { shownCount: toShow.length };
}
function swapManagedChat(nextChat, runtime = {}) {
const previousChat = hideState.managedChatRef;
if (previousChat && previousChat !== nextChat) {
unhideTrackedChat(previousChat, runtime);
hideState.hiddenIndices.clear();
hideState.lastProcessedLength = 0;
}
hideState.managedChatRef = nextChat;
}
export function runFullHideCheck(settings = {}, runtime = {}) {
const normalized = normalizeHideSettings(settings);
const chat = getCurrentChat(runtime);
if (!chat || chat.length === 0) {
resetHideState(runtime);
return {
active: false,
hiddenCount: 0,
shownCount: 0,
managedCount: 0,
chatLength: 0,
};
}
swapManagedChat(chat, runtime);
if (!normalized.enabled || normalized.hideLastN <= 0) {
const { shownCount } = unhideTrackedChat(chat, runtime);
hideState.hiddenIndices.clear();
hideState.lastProcessedLength = chat.length;
return {
active: false,
hiddenCount: 0,
shownCount,
managedCount: 0,
chatLength: chat.length,
};
}
const visibleStart =
normalized.hideLastN >= chat.length
? 0
: Math.max(0, chat.length - normalized.hideLastN);
const desiredHiddenIndices = new Set();
const managedHiddenIndices = new Set();
const toHide = [];
const toShow = [];
for (let index = 0; index < chat.length; index++) {
const message = chat[index];
if (!message) continue;
const shouldHide = index < visibleStart;
const isHidden = message.is_system === true;
const wasHiddenByBme = hideState.hiddenIndices.has(index);
if (shouldHide) {
desiredHiddenIndices.add(index);
if (wasHiddenByBme || !isHidden) {
managedHiddenIndices.add(index);
}
if (!isHidden) {
message.is_system = true;
toHide.push(index);
}
continue;
}
if (isHidden && wasHiddenByBme) {
message.is_system = false;
toShow.push(index);
}
}
syncSystemAttribute(chat, [...desiredHiddenIndices], "true", runtime);
syncSystemAttribute(chat, toShow, "false", runtime);
hideState.hiddenIndices = managedHiddenIndices;
hideState.lastProcessedLength = chat.length;
return {
active: true,
hiddenCount: toHide.length,
shownCount: toShow.length,
managedCount: managedHiddenIndices.size,
chatLength: chat.length,
};
}
export function runIncrementalHideCheck(settings = {}, runtime = {}) {
const normalized = normalizeHideSettings(settings);
const chat = getCurrentChat(runtime);
if (!chat || chat.length === 0) {
resetHideState(runtime);
return {
active: false,
hiddenCount: 0,
shownCount: 0,
managedCount: 0,
chatLength: 0,
incremental: false,
};
}
if (
hideState.managedChatRef !== chat ||
!normalized.enabled ||
normalized.hideLastN <= 0
) {
return {
...runFullHideCheck(normalized, runtime),
incremental: false,
};
}
const chatLength = chat.length;
const previousLength = hideState.lastProcessedLength;
if (chatLength <= previousLength) {
return {
...runFullHideCheck(normalized, runtime),
incremental: false,
};
}
const previousVisibleStart =
previousLength > 0 ? Math.max(0, previousLength - normalized.hideLastN) : 0;
const nextVisibleStart = Math.max(0, chatLength - normalized.hideLastN);
const toHide = [];
if (nextVisibleStart > previousVisibleStart) {
for (let index = previousVisibleStart; index < nextVisibleStart; index++) {
const message = chat[index];
if (!message || message.is_system === true) continue;
message.is_system = true;
hideState.hiddenIndices.add(index);
toHide.push(index);
}
}
syncSystemAttribute(chat, toHide, "true", runtime);
hideState.lastProcessedLength = chatLength;
return {
active: true,
hiddenCount: toHide.length,
shownCount: 0,
managedCount: hideState.hiddenIndices.size,
chatLength,
incremental: true,
};
}
export function applyHideSettings(settings = {}, runtime = {}) {
return runFullHideCheck(settings, runtime);
}
export function scheduleHideSettingsApply(
settings = {},
runtime = {},
delayMs = 120,
) {
const timers = getTimerApi(runtime);
if (hideState.scheduledTimer) {
timers.clearTimeout(hideState.scheduledTimer);
hideState.scheduledTimer = null;
}
const snapshot = normalizeHideSettings(settings);
hideState.scheduledTimer = timers.setTimeout(() => {
hideState.scheduledTimer = null;
applyHideSettings(snapshot, runtime);
}, Math.max(0, Math.trunc(Number(delayMs) || 0)));
}
export function unhideAll(runtime = {}) {
const timers = getTimerApi(runtime);
if (hideState.scheduledTimer) {
timers.clearTimeout(hideState.scheduledTimer);
hideState.scheduledTimer = null;
}
const managedChat = hideState.managedChatRef;
const { shownCount } = unhideTrackedChat(managedChat, runtime);
hideState.hiddenIndices.clear();
hideState.lastProcessedLength = Array.isArray(managedChat)
? managedChat.length
: 0;
return {
active: false,
shownCount,
managedCount: 0,
};
}
export function resetHideState(runtime = {}) {
const timers = getTimerApi(runtime);
if (hideState.scheduledTimer) {
timers.clearTimeout(hideState.scheduledTimer);
hideState.scheduledTimer = null;
}
const managedChat = hideState.managedChatRef;
unhideTrackedChat(managedChat, runtime);
hideState.managedChatRef = null;
hideState.hiddenIndices.clear();
hideState.lastProcessedLength = 0;
}
export function getHideStateSnapshot() {
return {
hasManagedChat: Boolean(hideState.managedChatRef),
managedHiddenCount: hideState.hiddenIndices.size,
lastProcessedLength: hideState.lastProcessedLength,
scheduled: Boolean(hideState.scheduledTimer),
};
}