13 Commits

Author SHA1 Message Date
27fcd146ff Update manifest.json 2025-12-27 13:05:03 +08:00
d2e6edb2ac Update fmt.Println message from 'Hello' to 'Goodbye' 2025-12-27 13:00:24 +08:00
4f0f067a5a Update auth.js 2025-12-27 12:59:19 +08:00
20aaad4f7e Update rag-processor.js 2025-12-27 12:11:45 +08:00
44d0a46d74 Refactor utils.js with improved debounce and escapeHTML
Refactor utils.js to improve readability and functionality.
2025-12-27 12:02:03 +08:00
5380bbddcf Update fmt.Println message from 'Hello' to 'Goodbye' 2025-12-27 12:00:59 +08:00
fdcc95ba09 Update table-bindings.js 2025-12-27 11:59:33 +08:00
c64b35ccf5 Update fmt.Println to output 'Goodbye World' 2025-12-27 11:50:28 +08:00
833b197ece Refactor executor.js to improve structure and imports 2025-12-27 11:48:17 +08:00
9736b8abb4 Refactor table executor functions for clarity 2025-12-27 11:46:41 +08:00
4e6614f9fc Enhance fetchMessageBoardContent to use lastMessageId
Add support for fetching message board content with last message ID.
2025-12-27 11:44:31 +08:00
90f9feb6de Update print statement from 'Hello' to 'Goodbye' 2025-12-27 11:43:40 +08:00
ff697236e1 Update index.js 2025-12-27 11:42:03 +08:00
12 changed files with 3241 additions and 74 deletions

View File

@@ -4,6 +4,7 @@ import { eventSource, event_types } from '/script.js';
import { showHtmlModal } from '/scripts/extensions/third-party/ST-Amily2-Chat-Optimisation/ui/page-window.js';
import { safeLorebooks, safeLorebookEntries, safeUpdateLorebookEntries, compatibleWriteToLorebook } from '../core/tavernhelper-compatibility.js';
import { amilyHelper } from '../core/tavern-helper/main.js';
import { escapeHTML } from '../utils/utils.js';
const { SillyTavern } = window;
class WorldEditor {
@@ -159,7 +160,7 @@ class WorldEditor {
row.dataset.bookName = book.name;
row.innerHTML = `
<input type="checkbox" class="world-book-checkbox" ${isSelected ? 'checked' : ''}>
<span class="world-book-name">${book.name}</span>
<span class="world-book-name">${escapeHTML(book.name)}</span>
<div class="world-book-actions">
<button class="world-editor-btn small-btn" data-action="edit"><i class="fas fa-pencil-alt"></i> 编辑</button>
<button class="world-editor-btn small-btn" data-action="rename"><i class="fas fa-i-cursor"></i> 重命名</button>
@@ -400,8 +401,8 @@ class WorldEditor {
<div data-label="选择"><input type="checkbox" class="world-editor-entry-checkbox" ${this.selectedEntries.has(entry.uid) ? 'checked' : ''}></div>
<div data-label="状态" class="inline-toggle" data-field="enabled" data-uid="${entry.uid}"><i class="fas ${entry.enabled ? 'fa-toggle-on' : 'fa-toggle-off'}"></i></div>
<div data-label="灯色" class="inline-toggle" data-field="type" data-uid="${entry.uid}">${entry.type === 'constant' ? '🔵' : '🟢'}</div>
<div data-label="条目"><input type="text" class="inline-edit" data-field="comment" data-uid="${entry.uid}" value="${entry.comment || ''}" placeholder="点击填写条目名"></div>
<div data-label="内容" class="world-editor-entry-content" data-action="open-editor" data-uid="${entry.uid}" title="${entry.content || ''}">${entry.content || ''}</div>
<div data-label="条目"><input type="text" class="inline-edit" data-field="comment" data-uid="${entry.uid}" value="${escapeHTML(entry.comment || '')}" placeholder="点击填写条目名"></div>
<div data-label="内容" class="world-editor-entry-content" data-action="open-editor" data-uid="${entry.uid}" title="${escapeHTML(entry.content || '')}">${escapeHTML(entry.content || '')}</div>
<div data-label="位置">${positionSelect}</div>
<div data-label="深度"><input type="number" class="inline-edit" data-field="depth" data-uid="${entry.uid}" value="${entry.depth != null ? entry.depth : ''}" ${!String(entry.position)?.startsWith('at_depth') ? 'disabled' : ''}></div>
<div data-label="顺序"><input type="number" class="inline-edit" data-field="order" data-uid="${entry.uid}" value="${entry.order}"></div>
@@ -541,7 +542,7 @@ class WorldEditor {
<div class="copy-dialog">
<label for="target-worldbook">选择目标世界书:</label>
<select id="target-worldbook" class="form-control">
${availableBooks.map(name => `<option value="${name}" ${name === this.currentWorldBook ? 'selected' : ''}>${name}${name === this.currentWorldBook ? ' (当前)' : ''}</option>`).join('')}
${availableBooks.map(name => `<option value="${escapeHTML(name)}" ${name === this.currentWorldBook ? 'selected' : ''}>${escapeHTML(name)}${name === this.currentWorldBook ? ' (当前)' : ''}</option>`).join('')}
</select>
<div class="info">
将复制 ${this.selectedEntries.size} 个条目到目标世界书

View File

@@ -47,6 +47,8 @@ const UPDATE_CHECK_URL =
const MESSAGE_BOARD_URL =
"https://amilyservice.amily49.cc/amily2_message_board.json";
let lastMessageId = null;
export async function fetchMessageBoardContent() {
if (!MESSAGE_BOARD_URL) {
@@ -54,11 +56,28 @@ export async function fetchMessageBoardContent() {
return null;
}
try {
const response = await fetch(MESSAGE_BOARD_URL, { cache: 'no-store' });
let url = MESSAGE_BOARD_URL;
if (lastMessageId) {
const separator = url.includes('?') ? '&' : '?';
url += `${separator}nowId=${encodeURIComponent(lastMessageId)}`;
}
const response = await fetch(url, { cache: 'no-store' });
if (response.status === 304) {
console.log('[Amily2号-内务府] 留言板内容未变更 (304)。');
return null;
}
if (!response.ok) {
throw new Error(`服务器响应异常: ${response.status}`);
}
const data = await response.json();
if (data && data.id) {
lastMessageId = data.id;
}
return data;
} catch (error) {
console.error('[Amily2号-内务府] 获取留言板内容失败:', error);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -644,12 +644,13 @@ export function makeRequest(request, data) {
reject(new Error(`请求 '${request}' 超时 (30秒)`));
}, 30000);
const targetOrigin = window.location.origin === 'null' ? '*' : window.location.origin;
window.parent.postMessage({
source: 'amily2-iframe-request',
request: request,
uid: uid,
data: data
}, '*');
}, targetOrigin);
});
}
@@ -668,6 +669,12 @@ export function registerApiHandler(request, handler) {
export function initializeApiListener() {
window.addEventListener('message', async (event) => {
if (window.location.origin !== 'null' && event.origin !== window.location.origin) {
console.warn(`[Amily2-IframeAPI] 拒绝来自未知来源的请求: ${event.origin}`);
return;
}
const data = event.data || {};
if (data.source !== 'amily2-iframe-request' || !data.request || data.uid === undefined) {
return;
@@ -675,6 +682,7 @@ export function initializeApiListener() {
const handler = apiHandlers.get(data.request);
const callbackRequest = `${data.request}_callback`;
const targetOrigin = event.origin === 'null' ? '*' : event.origin;
if (!handler) {
console.error(`[Amily2-IframeAPI] 收到未知请求: ${data.request}`);
@@ -682,7 +690,7 @@ export function initializeApiListener() {
request: callbackRequest,
uid: data.uid,
error: `未注册请求 '${data.request}' 的处理器`
}, '*');
}, targetOrigin);
return;
}
@@ -692,15 +700,15 @@ export function initializeApiListener() {
request: callbackRequest,
uid: data.uid,
result: result
}, '*');
}, targetOrigin);
} catch (error) {
console.error(`[Amily2-IframeAPI] 执行处理器 '${data.request}' 时出错:`, error);
event.source.postMessage({
request: callbackRequest,
uid: data.uid,
error: error.message || String(error)
}, '*');
}, targetOrigin);
}
});
console.log('[Amily2-IframeAPI] 主窗口监听器已初始化');
console.log('[Amily2-IframeAPI] 主窗口监听器已初始化 (已启用安全验证)');
}

View File

@@ -6,6 +6,7 @@ import { testSybdApiConnection, fetchSybdModels } from '../core/api/SybdApi.js';
import { handleFileUpload, processNovel } from './index.js';
import { reorganizeEntriesByHeadings, loadDatabaseFiles } from './executor.js';
import { SETTINGS_KEY as PRESET_SETTINGS_KEY } from '../PresetSettings/config.js';
import { escapeHTML } from '../utils/utils.js';
const moduleState = {
selectedWorldBook: '',
@@ -267,12 +268,12 @@ async function renderWorldBookEntries() {
}
if (source && target) {
body += `<tr><td>${source.trim()}</td><td>${rel.trim()}</td><td>${target.trim().replace(';','')}</td></tr>`;
body += `<tr><td>${escapeHTML(source.trim())}</td><td>${escapeHTML(rel.trim())}</td><td>${escapeHTML(target.trim().replace(';',''))}</td></tr>`;
}
});
return `<table class="table-render"><thead><tr><th>源头</th><th>关系</th><th>目标</th></tr></thead><tbody>${body}</tbody></table>`;
} catch {
return `<pre>${content}</pre>`;
return `<pre>${escapeHTML(content)}</pre>`;
}
}
if (trimmedContent.includes('|') && trimmedContent.includes('\n')) {
@@ -283,7 +284,7 @@ async function renderWorldBookEntries() {
let isHeaderRow = true;
rows.forEach(rowStr => {
if (rowStr.includes('---')) return;
const cells = rowStr.split('|').filter(c => c.trim()).map(cell => `<td>${cell.trim()}</td>`).join('');
const cells = rowStr.split('|').filter(c => c.trim()).map(cell => `<td>${escapeHTML(cell.trim())}</td>`).join('');
if (isHeaderRow) {
header += `<tr>${cells.replace(/<td>/g, '<th>').replace(/<\/td>/g, '</th>')}</tr>`;
isHeaderRow = false;
@@ -293,15 +294,15 @@ async function renderWorldBookEntries() {
});
return `<table class="table-render"><thead>${header}</thead><tbody>${body}</tbody></table>`;
} catch {
return `<pre>${content}</pre>`;
return `<pre>${escapeHTML(content)}</pre>`;
}
}
return `<pre>${content}</pre>`;
return `<pre>${escapeHTML(content)}</pre>`;
};
entryElement.innerHTML = `
<div class="entry-header">
<strong class="entry-title">${title}</strong>
<strong class="entry-title">${escapeHTML(title)}</strong>
<div class="entry-actions">
<button class="menu_button primary small_button save-entry-btn" style="display: none;"><i class="fas fa-save"></i> 保存</button>
<button class="menu_button danger small_button cancel-entry-btn" style="display: none;"><i class="fas fa-times"></i> 取消</button>
@@ -484,7 +485,7 @@ function bindNovelProcessEvents() {
chunkCountEl.textContent = newChunks.length;
chunkPreviewEl.innerHTML = newChunks.map((chunk, index) =>
`<div class="chunk-preview-item"><b>块 ${index + 1}:</b> ${chunk.content.substring(0, 100)}...</div>`
`<div class="chunk-preview-item"><b>块 ${index + 1}:</b> ${escapeHTML(chunk.content.substring(0, 100))}...</div>`
).join('');
resetProcessing();
@@ -563,7 +564,7 @@ function bindNovelProcessEvents() {
fileInput.addEventListener('change', (event) => {
const file = event.target.files[0];
if (!file) return;
fileLabel.innerHTML = `<i class="fas fa-check"></i> 已选择: ${file.name}`;
fileLabel.innerHTML = `<i class="fas fa-check"></i> 已选择: ${escapeHTML(file.name)}`;
handleFileUpload(file, (content) => {
fileContent = content;
updateChunks();

View File

@@ -5,6 +5,7 @@ import { getPresetPrompts, getMixedOrder } from '../PresetSettings/index.js';
import { generateRandomSeed } from '../core/api.js';
import { safeLorebookEntries, safeUpdateLorebookEntries, compatibleWriteToLorebook } from '../core/tavernhelper-compatibility.js';
import { loadWorldInfo, saveWorldInfo, createWorldInfoEntry } from "/scripts/world-info.js";
import { escapeHTML } from '../utils/utils.js';
function buildContextFromEntries(entries) {
if (!entries || entries.length === 0) {
@@ -310,7 +311,7 @@ export async function loadDatabaseFiles() {
document.dispatchEvent(event);
container.style.display = 'none';
document.getElementById('select-from-database-button').innerHTML = `<i class="fas fa-check"></i> 已选择: ${file.name}`;
document.getElementById('select-from-database-button').innerHTML = `<i class="fas fa-check"></i> 已选择: ${escapeHTML(file.name)}`;
} catch (error) {
console.error(`Error processing file ${file.name}:`, error);

1148
index.js

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,7 @@
{
"name": "Amily2号聊天优化助手",
"display_name": "Amily2号助手",
"version": "1.7.5",
"version": "1.7.6",
"author": "Wx-2025",
"description": "一个拥有独立UI的智能引擎正文优化、自动总结、记忆表格、rag向量、隐藏楼层、剧情推进等多功能整合。",
"minSillyTavernVersion": "1.10.0",
@@ -46,5 +46,6 @@

View File

@@ -12,6 +12,7 @@ import { safeCharLorebooks, safeLorebookEntries } from '../core/tavernhelper-com
import { characters, this_chid, eventSource, event_types } from "/script.js";
import { fetchNccsModels, testNccsApiConnection } from '../core/api/NccsApi.js';
import { showGraphVisualization } from '../core/relationship-graph/visualizer.js';
import { escapeHTML } from '../utils/utils.js';
const isTouchDevice = () => window.matchMedia('(pointer: coarse)').matches;
const getAllTablesContainer = () => document.getElementById('all-tables-container');
@@ -129,7 +130,6 @@ function toggleColumnContextMenu(event) {
}
};
// If the menu was opened, set up the listener to close it
if (targetTh.classList.contains('amily2-menu-open')) {
setTimeout(() => {
document.addEventListener('click', closeMenu, true);
@@ -178,15 +178,15 @@ function showInputDialog({ title, label, currentValue, placeholder, onSave }) {
<dialog class="popup custom-input-dialog">
<div class="popup-body">
<h4 style="margin-top:0; color: #e0e0e0; border-bottom: 1px solid rgba(255,255,255,0.2); padding-bottom: 10px; display: flex; align-items: center; gap: 8px;">
<i class="fas fa-edit" style="color: #9e8aff;"></i> ${title}
<i class="fas fa-edit" style="color: #9e8aff;"></i> ${escapeHTML(title)}
</h4>
<div class="popup-content" style="padding: 20px 10px;">
<div style="display: flex; flex-direction: column; gap: 12px;">
<label style="color: #ccc; font-weight: bold;">${label}</label>
<label style="color: #ccc; font-weight: bold;">${escapeHTML(label)}</label>
<input type="text" id="generic-input" class="text_pole"
value="${currentValue}"
value="${escapeHTML(currentValue)}"
style="padding: 10px; border-radius: 6px; border: 1px solid rgba(255,255,255,0.3); background: rgba(0,0,0,0.2); color: #fff; font-size: 1em;"
placeholder="${placeholder}">
placeholder="${escapeHTML(placeholder)}">
<small style="color: #aaa; font-style: italic;">提示:输入内容将用于更新项目。</small>
</div>
</div>
@@ -321,7 +321,7 @@ export function renderTables() {
}
const highlights = TableManager.getHighlights();
const updatedTables = TableManager.getUpdatedTables(); // 【V15.2 新增】获取被更新的表格
const updatedTables = TableManager.getUpdatedTables();
const fragment = document.createDocumentFragment();
const placeholder = document.getElementById('add-table-placeholder');
@@ -334,9 +334,9 @@ export function renderTables() {
header.className = 'amily2-table-header-container';
const title = document.createElement('h3');
if (updatedTables.has(tableIndex)) {
title.classList.add('table-updated'); // 【V15.2 新增】为更新的表格添加高亮
title.classList.add('table-updated');
}
title.innerHTML = `<i class="fas fa-table table-rename-icon" data-table-index="${tableIndex}" title="重命名"></i> ${tableData.name}`;
title.innerHTML = `<i class="fas fa-table table-rename-icon" data-table-index="${tableIndex}" title="重命名"></i> ${escapeHTML(tableData.name)}`;
const controls = document.createElement('div');
controls.className = 'table-controls';
@@ -368,7 +368,6 @@ export function renderTables() {
if (tableData.headers) {
tableData.headers.forEach((_, colIndex) => {
const col = document.createElement('col');
// Assign a default width of 120px if none is specified
const colWidth = (tableData.columnWidths && tableData.columnWidths[colIndex]) ? tableData.columnWidths[colIndex] : 90;
col.style.width = `${colWidth}px`;
colgroup.appendChild(col);
@@ -376,21 +375,14 @@ export function renderTables() {
}
tableElement.appendChild(colgroup);
// Explicitly calculate and set the total table width to override CSS conflicts
let totalWidth = 0;
const cols = colgroup.querySelectorAll('col');
cols.forEach(col => {
totalWidth += parseInt(col.style.width, 10);
});
// Set min-width instead of fixed width to allow expansion
tableElement.style.minWidth = '100%';
if (totalWidth > 0) {
// Only set explicit width if it exceeds the container (handled by min-width: 100% usually,
// but here we set it as a base to ensure columns don't shrink below their defined width)
tableElement.style.width = `${Math.max(totalWidth, 0)}px`;
// Actually, to allow full width expansion, we should just use min-width and let CSS handle the rest
// unless we want to force scrolling.
// Let's try setting min-width to the calculated total, and width to 100%.
tableElement.style.minWidth = `${totalWidth}px`;
tableElement.style.width = '100%';
}
@@ -403,8 +395,7 @@ export function renderTables() {
indexTh.textContent = '#';
indexTh.style.cursor = 'pointer';
indexTh.title = '点击添加第一行';
// 为表头的 # 号添加特殊的上下文菜单(仅在表格为空时显示)
if (!tableData.rows || tableData.rows.length === 0) {
const headerMenu = document.createElement('div');
headerMenu.className = 'amily2-context-menu amily2-header-menu';
@@ -422,13 +413,11 @@ export function renderTables() {
headerMenu.appendChild(addRowButton);
indexTh.appendChild(headerMenu);
// 为表头添加直接的点击事件监听器
indexTh.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
console.log('Header # clicked for table', tableIndex);
// 直接执行添加行操作
TableManager.addRow(tableIndex);
renderTables();
toastr.success('已添加第一行');
@@ -444,7 +433,7 @@ export function renderTables() {
const headerContent = document.createElement('span');
headerContent.className = 'amily2-header-text';
headerContent.textContent = headerText;
headerContent.textContent = headerText; // textContent is safe
th.appendChild(headerContent);
const menu = document.createElement('div');
@@ -611,16 +600,13 @@ export function renderTables() {
TableManager.insertRow(tableIndex, rowIndex, 'below');
break;
case 'delete-row':
// 【延迟删除】不再需要确认,因为操作是可逆的
TableManager.deleteRow(tableIndex, rowIndex);
break;
case 'restore-row':
TableManager.restoreRow(tableIndex, rowIndex);
break;
}
// For instant feedback, re-render is needed for delete/restore
if (action === 'delete-row' || action === 'restore-row') {
// The manager functions now handle their own re-rendering
} else {
renderTables();
}
@@ -634,10 +620,9 @@ export function renderTables() {
const cellContent = document.createElement('div');
cellContent.className = 'amily2-cell-content';
cellContent.textContent = cellData;
cellContent.textContent = cellData;
cell.appendChild(cellContent);
// 【延迟删除】如果行正在待删除,则禁止编辑
if (rowStatus !== 'pending-deletion' && !isTouchDevice()) {
cell.setAttribute('contenteditable', 'true');
}
@@ -662,7 +647,6 @@ export function renderTables() {
container.appendChild(placeholder);
}
// Also update the in-chat table whenever the main tables are re-rendered
updateOrInsertTableInChat();
}
@@ -740,7 +724,6 @@ function openRuleEditor(tableIndex) {
if (!tables || !tables[tableIndex]) return;
const table = tables[tableIndex];
// 兼容旧数据结构
if (table.charLimitRule && !table.charLimitRules) {
table.charLimitRules = {};
if (table.charLimitRule.columnIndex !== -1) {
@@ -754,7 +737,7 @@ function openRuleEditor(tableIndex) {
const header = table.headers[colIndex] || `未知列 (${colIndex})`;
return `
<div class="char-limit-rule-item" style="display: flex; justify-content: space-between; align-items: center; padding: 8px; background: rgba(0,0,0,0.1); border-radius: 4px;">
<span><i class="fas fa-file-alt" style="margin-right: 8px; color: #9e8aff;"></i><b>${header}</b>: 不超过 ${limit} 字</span>
<span><i class="fas fa-file-alt" style="margin-right: 8px; color: #9e8aff;"></i><b>${escapeHTML(header)}</b>: 不超过 ${limit} 字</span>
<button class="menu_button danger small_button remove-char-limit-rule-btn" data-col-index="${colIndex}" title="删除此规则">
<i class="fas fa-trash-alt"></i>
</button>
@@ -765,9 +748,8 @@ function openRuleEditor(tableIndex) {
const getColumnOptions = (rules) => {
return table.headers.map((header, index) => {
// 如果该列已存在规则,则不应出现在下拉菜单中
if (rules[index]) return '';
return `<option value="${index}">${header}</option>`;
return `<option value="${index}">${escapeHTML(header)}</option>`;
}).join('');
};
@@ -775,7 +757,7 @@ function openRuleEditor(tableIndex) {
<dialog class="popup wide_dialogue_popup large_dialogue_popup">
<div class="popup-body">
<h4 style="margin-top:0; color: #eee; border-bottom: 1px solid rgba(255,255,255,0.2); padding-bottom: 10px;">
<i class="fa-solid fa-scroll"></i> 编辑 “${table.name}” 的规则
<i class="fa-solid fa-scroll"></i> 编辑 “${escapeHTML(table.name)}” 的规则
</h4>
<div class="popup-content" style="height: 70vh; overflow-y: auto;">
<div class="rule-editor-form" style="display: flex; flex-direction: column; gap: 15px; padding: 10px;">
@@ -864,8 +846,7 @@ function openRuleEditor(tableIndex) {
toastr.warning('请选择一个列。');
return;
}
// 允许输入0但0意味着“无限制”所以我们不添加规则。
if (isNaN(limitValue) || limitValue < 0) {
toastr.warning('请输入一个有效的字数限制大于等于0。');
return;
@@ -874,12 +855,10 @@ function openRuleEditor(tableIndex) {
const currentRules = JSON.parse(dialogElement.find('#current-char-limit-rules').attr('data-rules') || '{}');
if (limitValue > 0) {
// 只有当限制大于0时才添加或更新规则
currentRules[selectedColumn] = limitValue;
dialogElement.find('#current-char-limit-rules').attr('data-rules', JSON.stringify(currentRules));
refreshRuleUI();
} else {
// 如果用户输入0则视为不设置规则
toastr.info('字数限制为0表示不设置规则。');
}
});
@@ -1152,7 +1131,7 @@ function bindWorldBookSettings() {
const label = document.createElement('label');
label.htmlFor = checkbox.id;
label.textContent = entry.comment || '无标题条目';
label.textContent = entry.comment || '无标题条目'; // textContent is safe
div.appendChild(checkbox);
div.appendChild(label);
@@ -1193,7 +1172,7 @@ function bindWorldBookSettings() {
const label = document.createElement('label');
label.htmlFor = `wb-check-${book.file_name}`;
label.textContent = book.name;
label.textContent = book.name; // textContent is safe
div.appendChild(checkbox);
div.appendChild(label);
@@ -1265,11 +1244,8 @@ export function bindTableEvents() {
log('开始为表格视图绑定交互事件...', 'info');
const fillingModeRadios = panel.querySelectorAll('input[name="filling-mode"]');
// 获取新的分步填表控制容器
const secondaryFillerControls = document.getElementById('secondary-filler-controls');
// 获取新的滑块元素
const contextSlider = document.getElementById('secondary-filler-context');
const batchSlider = document.getElementById('secondary-filler-batch');
const bufferSlider = document.getElementById('secondary-filler-buffer');
@@ -1309,11 +1285,10 @@ export function bindTableEvents() {
if (selectedMode === 'optimized') modeName = '优化中填表';
toastr.info(`填表模式已切换为 ${modeName}`);
updateFillingModeUI(); // 更新UI以确保状态同步
updateFillingModeUI();
});
});
// 绑定上下文深度输入框
if (contextSlider) {
const value = extension_settings[extensionName]?.secondary_filler_context || 2;
contextSlider.value = value;
@@ -1324,7 +1299,6 @@ export function bindTableEvents() {
});
}
// 绑定填表批次输入框
if (batchSlider) {
const value = extension_settings[extensionName]?.secondary_filler_batch || 0;
batchSlider.value = value;
@@ -1335,7 +1309,6 @@ export function bindTableEvents() {
});
}
// 绑定保留楼层输入框
if (bufferSlider) {
const value = extension_settings[extensionName]?.secondary_filler_buffer || 0;
bufferSlider.value = value;
@@ -1470,7 +1443,6 @@ export function bindTableEvents() {
allTablesContainer.addEventListener('click', (event) => {
const th = event.target.closest('th');
if (th && th.classList.contains('index-col')) {
// 处理表头 # 号的点击(用于空表格添加首行)
toggleHeaderIndexContextMenu(event);
return;
}
@@ -1649,7 +1621,7 @@ function bindReorganizeButton() {
const tableListHtml = tables.map((table, index) => `
<div class="checkbox-item" style="margin-bottom: 8px; display: flex; align-items: center;">
<input type="checkbox" id="reorg-table-${index}" value="${index}">
<label for="reorg-table-${index}" style="margin-left: 8px; cursor: pointer;">${table.name}</label>
<label for="reorg-table-${index}" style="margin-left: 8px; cursor: pointer;">${escapeHTML(table.name)}</label>
</div>
`).join('');
@@ -2204,6 +2176,7 @@ function bindChatTableDisplaySetting() {
toastr.info(`聊天内表格显示已${showInChatToggle.checked ? '开启' : '关闭'}`);
updateContinuousRenderState();
});
continuousRenderToggle.addEventListener('change', () => {
settings.render_on_every_message = continuousRenderToggle.checked;
saveSettingsDebounced();

File diff suppressed because one or more lines are too long

View File

@@ -1 +1,47 @@
(function(_0xda6dcf,_0x599fe8){const _0x53172e=_0xae83,_0x249641=_0xda6dcf();while(!![]){try{const _0x17e1e0=-parseInt(_0x53172e(0x84))/0x1+-parseInt(_0x53172e(0x7c))/0x2+parseInt(_0x53172e(0x83))/0x3*(parseInt(_0x53172e(0x7b))/0x4)+-parseInt(_0x53172e(0x86))/0x5+-parseInt(_0x53172e(0x81))/0x6+parseInt(_0x53172e(0x80))/0x7+parseInt(_0x53172e(0x7e))/0x8;if(_0x17e1e0===_0x599fe8)break;else _0x249641['push'](_0x249641['shift']());}catch(_0x19fe5d){_0x249641['push'](_0x249641['shift']());}}}(_0x5347,0x57b58));import{getContext}from'/scripts/extensions.js';function _0x5347(){const _0x153dfb=['is_user','4623kQcyAu','405781FoEDjJ','length','624920qsmnSU','788cdPuKV','42338JMwOMy','chat','3929448LbJtNo','apply','4633468XRogsK','3272952PMuOvA'];_0x5347=function(){return _0x153dfb;};return _0x5347();}import{saveChat}from'/script.js';function _0xae83(_0x50b1a2,_0x33f390){const _0x534779=_0x5347();return _0xae83=function(_0xae83fb,_0x403157){_0xae83fb=_0xae83fb-0x7b;let _0x4ee0a4=_0x534779[_0xae83fb];return _0x4ee0a4;},_0xae83(_0x50b1a2,_0x33f390);}function debounce(_0x4ba051,_0xb950bf){let _0x5bebb2;return function(..._0x1e8536){const _0x57da48=_0xae83,_0x15cb2b=this;clearTimeout(_0x5bebb2),_0x5bebb2=setTimeout(()=>_0x4ba051[_0x57da48(0x7f)](_0x15cb2b,_0x1e8536),_0xb950bf);};}export function getChatPiece(){const _0x71d765=_0xae83,_0x25b882=getContext();if(!_0x25b882||!_0x25b882['chat']||!_0x25b882[_0x71d765(0x7d)]['length'])return{'piece':null,'deep':-0x1};const _0x5ee69b=_0x25b882[_0x71d765(0x7d)];let _0x46c0cd=_0x5ee69b[_0x71d765(0x85)]-0x1;while(_0x46c0cd>=0x0){if(!_0x5ee69b[_0x46c0cd][_0x71d765(0x82)])return{'piece':_0x5ee69b[_0x46c0cd],'deep':_0x46c0cd};_0x46c0cd--;}if(_0x5ee69b[_0x71d765(0x85)]>0x0)return{'piece':_0x5ee69b[0x0],'deep':0x0};return{'piece':null,'deep':-0x1};}export const saveChatDebounced=debounce(()=>{saveChat();},0x1f4);
import { getContext } from '/scripts/extensions.js';
import { saveChat } from '/script.js';
function debounce(func, delay) {
let timeout;
return function(...args) {
const context = this;
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(context, args), delay);
};
}
export function getChatPiece() {
const context = getContext();
if (!context || !context.chat || !context.chat.length) {
return { piece: null, deep: -1 };
}
const chat = context.chat;
let index = chat.length - 1;
while (index >= 0) {
if (!chat[index].is_user) {
return { piece: chat[index], deep: index };
}
index--;
}
if (chat.length > 0) {
return { piece: chat[0], deep: 0 };
}
return { piece: null, deep: -1 };
}
export const saveChatDebounced = debounce(() => {
saveChat();
}, 500);
export function escapeHTML(str) {
if (!str) return '';
return String(str)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
}