mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 22:30:38 +08:00
Improve injection preview readability
This commit is contained in:
259
panel.js
259
panel.js
@@ -1207,9 +1207,7 @@ async function _refreshInjectionPreview() {
|
||||
try {
|
||||
const { estimateTokens } = await import("./injector.js");
|
||||
const totalTokens = estimateTokens(injection);
|
||||
const preview = document.createElement("div");
|
||||
preview.className = "bme-injection-preview";
|
||||
preview.textContent = injection;
|
||||
const preview = _buildInjectionPreviewNode(injection);
|
||||
container.replaceChildren(preview);
|
||||
if (tokenEl) tokenEl.textContent = `≈ ${totalTokens} tokens`;
|
||||
} catch (error) {
|
||||
@@ -1222,6 +1220,261 @@ async function _refreshInjectionPreview() {
|
||||
}
|
||||
}
|
||||
|
||||
function _buildInjectionPreviewNode(injectionText = "") {
|
||||
const parsed = _parseInjectionPreview(String(injectionText || ""));
|
||||
if (!parsed.sections.length) {
|
||||
const preview = document.createElement("div");
|
||||
preview.className = "bme-injection-preview";
|
||||
preview.textContent = injectionText;
|
||||
return preview;
|
||||
}
|
||||
|
||||
const root = document.createElement("div");
|
||||
root.className = "bme-injection-rich";
|
||||
|
||||
const hint = document.createElement("div");
|
||||
hint.className = "bme-injection-rich__hint";
|
||||
hint.textContent = "这里是结构化预览,便于阅读;实际发给模型的仍是原始注入文本。";
|
||||
root.appendChild(hint);
|
||||
|
||||
for (const section of parsed.sections) {
|
||||
const card = document.createElement("section");
|
||||
card.className = `bme-injection-card ${_getInjectionSectionFlavor(section.title)}`;
|
||||
|
||||
const title = document.createElement("div");
|
||||
title.className = "bme-injection-card__title";
|
||||
title.textContent = section.title;
|
||||
card.appendChild(title);
|
||||
|
||||
if (section.note) {
|
||||
const note = document.createElement("div");
|
||||
note.className = "bme-injection-card__note";
|
||||
note.textContent = section.note;
|
||||
card.appendChild(note);
|
||||
}
|
||||
|
||||
for (const block of section.blocks) {
|
||||
if (block.type === "table") {
|
||||
card.appendChild(_buildInjectionTableNode(block));
|
||||
} else if (block.type === "text" && block.text) {
|
||||
const text = document.createElement("div");
|
||||
text.className = "bme-injection-card__text";
|
||||
text.textContent = block.text;
|
||||
card.appendChild(text);
|
||||
}
|
||||
}
|
||||
|
||||
root.appendChild(card);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
function _parseInjectionPreview(injectionText = "") {
|
||||
const lines = String(injectionText || "").replace(/\r/g, "").split("\n");
|
||||
const sections = [];
|
||||
let index = 0;
|
||||
let currentSection = null;
|
||||
|
||||
function ensureSection(title = "Memory") {
|
||||
if (!currentSection) {
|
||||
currentSection = {
|
||||
title,
|
||||
note: "",
|
||||
blocks: [],
|
||||
};
|
||||
sections.push(currentSection);
|
||||
}
|
||||
return currentSection;
|
||||
}
|
||||
|
||||
while (index < lines.length) {
|
||||
const rawLine = lines[index] ?? "";
|
||||
const line = rawLine.trim();
|
||||
|
||||
if (!line) {
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
const sectionMatch = line.match(/^\[(Memory\s*-\s*.+)]$/i);
|
||||
if (sectionMatch) {
|
||||
currentSection = {
|
||||
title: sectionMatch[1],
|
||||
note: "",
|
||||
blocks: [],
|
||||
};
|
||||
sections.push(currentSection);
|
||||
index += 1;
|
||||
|
||||
const noteCandidate = (lines[index] ?? "").trim();
|
||||
if (
|
||||
noteCandidate &&
|
||||
!noteCandidate.startsWith("[") &&
|
||||
!noteCandidate.endsWith(":") &&
|
||||
!noteCandidate.startsWith("|") &&
|
||||
!noteCandidate.startsWith("## ")
|
||||
) {
|
||||
currentSection.note = noteCandidate;
|
||||
index += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const section = ensureSection();
|
||||
|
||||
if (line.endsWith(":") && String(lines[index + 1] || "").trim().startsWith("|")) {
|
||||
const tableName = line.slice(0, -1).trim();
|
||||
const tableLines = [];
|
||||
index += 1;
|
||||
while (index < lines.length) {
|
||||
const tableLine = String(lines[index] || "");
|
||||
if (!tableLine.trim().startsWith("|")) {
|
||||
break;
|
||||
}
|
||||
tableLines.push(tableLine.trim());
|
||||
index += 1;
|
||||
}
|
||||
const parsedTable = _parseInjectionTable(tableName, tableLines);
|
||||
if (parsedTable) {
|
||||
section.blocks.push(parsedTable);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const textLines = [];
|
||||
while (index < lines.length) {
|
||||
const candidate = String(lines[index] || "").trim();
|
||||
if (!candidate) {
|
||||
index += 1;
|
||||
if (textLines.length > 0) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
/^\[(Memory\s*-\s*.+)]$/i.test(candidate) ||
|
||||
(candidate.endsWith(":") && String(lines[index + 1] || "").trim().startsWith("|"))
|
||||
) {
|
||||
break;
|
||||
}
|
||||
textLines.push(candidate);
|
||||
index += 1;
|
||||
}
|
||||
if (textLines.length > 0) {
|
||||
section.blocks.push({
|
||||
type: "text",
|
||||
text: textLines.join("\n"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { sections };
|
||||
}
|
||||
|
||||
function _parseInjectionTable(tableName, tableLines = []) {
|
||||
if (!Array.isArray(tableLines) || tableLines.length < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const headerCells = _splitInjectionTableRow(tableLines[0]);
|
||||
if (!headerCells.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const rows = tableLines
|
||||
.slice(2)
|
||||
.map((row) => _splitInjectionTableRow(row))
|
||||
.filter((cells) => cells.length > 0);
|
||||
|
||||
return {
|
||||
type: "table",
|
||||
name: tableName,
|
||||
headers: headerCells,
|
||||
rows,
|
||||
};
|
||||
}
|
||||
|
||||
function _splitInjectionTableRow(row = "") {
|
||||
const text = String(row || "").trim();
|
||||
if (!text.startsWith("|")) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const inner = text.replace(/^\|/, "").replace(/\|$/, "");
|
||||
const cells = [];
|
||||
let current = "";
|
||||
let escaped = false;
|
||||
|
||||
for (const ch of inner) {
|
||||
if (escaped) {
|
||||
current += ch;
|
||||
escaped = false;
|
||||
continue;
|
||||
}
|
||||
if (ch === "\\") {
|
||||
escaped = true;
|
||||
continue;
|
||||
}
|
||||
if (ch === "|") {
|
||||
cells.push(current.trim());
|
||||
current = "";
|
||||
continue;
|
||||
}
|
||||
current += ch;
|
||||
}
|
||||
|
||||
cells.push(current.trim());
|
||||
return cells.map((cell) => cell.replace(/\\\|/g, "|").trim());
|
||||
}
|
||||
|
||||
function _buildInjectionTableNode(table) {
|
||||
const wrap = document.createElement("div");
|
||||
wrap.className = "bme-injection-table-wrap";
|
||||
|
||||
const name = document.createElement("div");
|
||||
name.className = "bme-injection-table-name";
|
||||
name.textContent = table.name;
|
||||
wrap.appendChild(name);
|
||||
|
||||
const tableEl = document.createElement("table");
|
||||
tableEl.className = "bme-injection-table";
|
||||
|
||||
const thead = document.createElement("thead");
|
||||
const headRow = document.createElement("tr");
|
||||
for (const header of table.headers) {
|
||||
const th = document.createElement("th");
|
||||
th.textContent = header;
|
||||
headRow.appendChild(th);
|
||||
}
|
||||
thead.appendChild(headRow);
|
||||
tableEl.appendChild(thead);
|
||||
|
||||
const tbody = document.createElement("tbody");
|
||||
for (const row of table.rows) {
|
||||
const tr = document.createElement("tr");
|
||||
const normalizedCells = table.headers.map((_, idx) => row[idx] ?? "");
|
||||
for (const cell of normalizedCells) {
|
||||
const td = document.createElement("td");
|
||||
td.textContent = cell;
|
||||
tr.appendChild(td);
|
||||
}
|
||||
tbody.appendChild(tr);
|
||||
}
|
||||
tableEl.appendChild(tbody);
|
||||
wrap.appendChild(tableEl);
|
||||
return wrap;
|
||||
}
|
||||
|
||||
function _getInjectionSectionFlavor(title = "") {
|
||||
const normalized = String(title || "").toLowerCase();
|
||||
if (normalized.includes("character pov")) return "character-pov";
|
||||
if (normalized.includes("user pov")) return "user-pov";
|
||||
if (normalized.includes("current region")) return "objective-current";
|
||||
if (normalized.includes("global")) return "objective-global";
|
||||
return "generic";
|
||||
}
|
||||
|
||||
// ==================== 图谱 ====================
|
||||
|
||||
/** SillyTavern 用户显示名(name1),用于图谱分区:误标为角色的用户 POV 强制归用户区 */
|
||||
|
||||
Reference in New Issue
Block a user