From 14ae17d997a326bb52c17786a900b3469ef56ca9 Mon Sep 17 00:00:00 2001 From: Wx-2025 <351320169@qq.com> Date: Sun, 12 Oct 2025 16:07:27 +0800 Subject: [PATCH] Update WorldEditor.js --- WorldEditor/WorldEditor.js | 601 +++++++++++++++++++++++-------------- 1 file changed, 377 insertions(+), 224 deletions(-) diff --git a/WorldEditor/WorldEditor.js b/WorldEditor/WorldEditor.js index 659b8b1..bd1b878 100644 --- a/WorldEditor/WorldEditor.js +++ b/WorldEditor/WorldEditor.js @@ -1,20 +1,29 @@ -/** - * 世界书编辑器 - 最终稳定版 - */ -import { world_names, loadWorldInfo, saveWorldInfo } from "/scripts/world-info.js"; + +import { world_names, loadWorldInfo, saveWorldInfo, deleteWorldInfo, updateWorldInfoList } from "/scripts/world-info.js"; 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 } from '../core/tavernhelper-compatibility.js'; +import { writeToLorebookWithTavernHelper } from '../core/lore.js'; const { SillyTavern, TavernHelper } = window; class WorldEditor { constructor() { + // 通用状态 + this.isLoading = false; + + // 世界书视图状态 + this.allWorldBooks = []; + this.filteredWorldBooks = []; + this.selectedWorldBooks = new Set(); + + // 条目视图状态 this.currentWorldBook = null; this.entries = []; this.selectedEntries = new Set(); this.filteredEntries = []; - this.isLoading = false; this.currentEditingEntry = null; this.sortState = { key: 'order', asc: true }; + this.init(); } @@ -26,13 +35,22 @@ class WorldEditor { } this.bindEvents(); this.loadAvailableWorldBooks(); - this.bindExternalEvents(); // 绑定外部事件监听 + this.bindExternalEvents(); } initializeComponents() { const ids = [ - 'world-editor-world-select', 'world-editor-refresh-btn', 'world-editor-create-entry-btn', - 'world-editor-search-box', 'world-editor-search-btn', 'world-editor-entry-count', + // 主视图 + 'world-book-selection-view', 'world-editor-entry-view', + // 顶部按钮 + 'world-editor-refresh-btn', 'world-editor-create-book-btn', 'world-editor-create-entry-btn', + // 世界书视图 + 'world-book-search-box', 'world-book-search-btn', 'world-book-count', + 'world-book-batch-actions', 'world-book-selected-count', 'world-book-clone-btn', 'world-book-delete-btn', + 'world-book-list-container', + // 条目视图 + 'world-editor-current-book-title', 'world-editor-back-to-list-btn', + 'world-editor-search-type', 'world-editor-search-box', 'world-editor-search-btn', 'world-editor-entry-count', 'world-editor-select-all', 'world-editor-selected-count', 'world-editor-batch-actions', 'world-editor-entries-container', 'world-editor-enable-selected-btn', 'world-editor-disable-selected-btn', @@ -44,23 +62,35 @@ class WorldEditor { for (const id of ids) { const camelCaseId = id.replace(/-(\w)/g, (_, c) => c.toUpperCase()); this.elements[camelCaseId] = document.getElementById(id); - if (!this.elements[camelCaseId] && id.endsWith('container')) { // Only container is critical - console.error(`[世界书编辑器] 关键元素缺失: ${id}`); - missing = true; + if (!this.elements[camelCaseId]) { + console.warn(`[世界书编辑器] UI元素缺失: ${id}`); + if (id.endsWith('container') || id.endsWith('view')) { + missing = true; // 关键元素缺失 + } } } return !missing; } bindEvents() { - this.elements.worldEditorWorldSelect.addEventListener('change', (e) => this.loadWorldBookEntries(e.target.value)); + // 视图切换 + this.elements.worldEditorBackToListBtn.addEventListener('click', () => this.switchToBookListView()); + + // 顶部按钮 this.elements.worldEditorRefreshBtn.addEventListener('click', () => this.loadAvailableWorldBooks()); - document.querySelector('#world-editor-container .world-editor-entries-header').addEventListener('click', (e) => { - if (e.target.dataset.sort) { - this.sortEntries(e.target.dataset.sort); - } - }); + this.elements.worldEditorCreateBookBtn.addEventListener('click', () => this.createNewWorldBook()); this.elements.worldEditorCreateEntryBtn.addEventListener('click', () => this.openCreateModal()); + + // 世界书视图事件 + this.elements.worldBookSearchBox.addEventListener('input', () => this.filterWorldBooks()); + this.elements.worldBookSearchBtn.addEventListener('click', () => this.filterWorldBooks()); + this.elements.worldBookCloneBtn.addEventListener('click', () => this.cloneSelectedBooks()); + this.elements.worldBookDeleteBtn.addEventListener('click', () => this.deleteSelectedBooks()); + + // 条目视图事件 + document.querySelector('#world-editor-entry-view .world-editor-entries-header').addEventListener('click', (e) => { + if (e.target.dataset.sort) this.sortEntries(e.target.dataset.sort); + }); this.elements.worldEditorSearchBox.addEventListener('input', () => this.filterEntries()); this.elements.worldEditorSearchBtn.addEventListener('click', () => this.filterEntries()); this.elements.worldEditorSelectAll.addEventListener('change', (e) => this.toggleSelectAll(e.target.checked)); @@ -73,19 +103,29 @@ class WorldEditor { this.elements.worldEditorSetPreventRecursionBtn.addEventListener('click', () => this.toggleBatchRecursion('prevent_recursion', '防止递归')); } + // 视图管理 + switchToBookListView() { + this.elements.worldBookSelectionView.style.display = 'block'; + this.elements.worldEditorEntryView.style.display = 'none'; + this.elements.worldEditorCreateEntryBtn.disabled = true; + this.currentWorldBook = null; + } + + switchToEntryView(bookName) { + this.elements.worldBookSelectionView.style.display = 'none'; + this.elements.worldEditorEntryView.style.display = 'block'; + this.elements.worldEditorCreateEntryBtn.disabled = false; + this.elements.worldEditorCurrentBookTitle.textContent = `当前编辑:${bookName}`; + this.loadWorldBookEntries(bookName); + } + + // 世界书数据处理 async loadAvailableWorldBooks() { this.setLoading(true); try { const books = await this.getAllWorldBooks(); - const select = this.elements.worldEditorWorldSelect; - select.innerHTML = ''; - books.forEach(book => { - const option = document.createElement('option'); - option.value = book.name; - option.textContent = book.name; - select.appendChild(option); - }); - await this.selectCurrentCharacterWorldBook(); + this.allWorldBooks = books.sort((a, b) => a.name.localeCompare(b.name)); + this.filterWorldBooks(); // 这会渲染列表 } catch (error) { this.showError('加载世界书列表失败: ' + error.message); } finally { @@ -94,39 +134,174 @@ class WorldEditor { } async getAllWorldBooks() { - if (TavernHelper?.getLorebooks) { - const books = await TavernHelper.getLorebooks(); - if (Array.isArray(books) && books.length > 0) return books.map(name => ({ name })); - } - return (world_names || []).map(name => ({ name })); + const books = await safeLorebooks(); + return books.map(name => ({ name })); } - async selectCurrentCharacterWorldBook() { - if (TavernHelper?.getCurrentCharPrimaryLorebook) { - const primaryBook = await TavernHelper.getCurrentCharPrimaryLorebook(); - if (primaryBook) { - this.elements.worldEditorWorldSelect.value = primaryBook; - await this.loadWorldBookEntries(primaryBook); + filterWorldBooks() { + const term = this.elements.worldBookSearchBox.value.toLowerCase(); + this.filteredWorldBooks = this.allWorldBooks.filter(book => book.name.toLowerCase().includes(term)); + this.renderWorldBookList(); + this.updateWorldBookCount(); + } + + renderWorldBookList() { + const container = this.elements.worldBookListContainer; + container.innerHTML = ''; // 清空 + if (this.filteredWorldBooks.length === 0) { + container.innerHTML = '

没有找到世界书

'; + return; + } + + const fragment = document.createDocumentFragment(); + this.filteredWorldBooks.forEach(book => { + const isSelected = this.selectedWorldBooks.has(book.name); + const row = document.createElement('div'); + row.className = `world-book-row ${isSelected ? 'selected' : ''}`; + row.dataset.bookName = book.name; + row.innerHTML = ` + + ${book.name} +
+ + +
+ `; + fragment.appendChild(row); + }); + container.appendChild(fragment); + this.bindWorldBookListEvents(); + } + + bindWorldBookListEvents() { + this.elements.worldBookListContainer.querySelectorAll('.world-book-row').forEach(row => { + const bookName = row.dataset.bookName; + // 复选框事件 + row.querySelector('.world-book-checkbox').addEventListener('change', (e) => { + if (e.target.checked) { + this.selectedWorldBooks.add(bookName); + } else { + this.selectedWorldBooks.delete(bookName); + } + row.classList.toggle('selected', e.target.checked); + this.updateWorldBookSelectionUI(); + }); + + // 按钮事件 + row.querySelector('[data-action="edit"]').addEventListener('click', (e) => { + e.stopPropagation(); + this.switchToEntryView(bookName); + }); + row.querySelector('[data-action="rename"]').addEventListener('click', (e) => { + e.stopPropagation(); + this.renameWorldBook(bookName); + }); + }); + } + + async createNewWorldBook() { + const bookName = prompt("请输入新的世界书名称:"); + if (bookName && bookName.trim()) { + const trimmedBookName = bookName.trim(); + try { + await writeToLorebookWithTavernHelper(trimmedBookName, '新条目', () => '这是一个新条目', {}); + if (window.toastr) window.toastr.success(`世界书 "${trimmedBookName}" 创建成功!`); + this.loadAvailableWorldBooks(); + } catch (error) { + this.showError(`创建失败: ${error.message}`); } } } + async renameWorldBook(oldName) { + const newName = prompt(`重命名世界书 "${oldName}":`, oldName); + if (newName && newName.trim() && newName !== oldName) { + const trimmedNewName = newName.trim(); + try { + const bookData = await loadWorldInfo(oldName); + await saveWorldInfo(trimmedNewName, bookData); + await deleteWorldInfo(oldName); + if (window.toastr) window.toastr.success('重命名成功!'); + + await updateWorldInfoList(); + eventSource.emit(event_types.CHARACTER_PAGE_LOADED); + this.loadAvailableWorldBooks(); + } catch (error) { + this.showError(`重命名失败: ${error.message}`); + } + } + } + + async cloneSelectedBooks() { + if (this.selectedWorldBooks.size === 0) return; + if (!confirm(`确定要为 ${this.selectedWorldBooks.size} 个世界书创建备份吗?`)) return; + + this.setLoading(true); + try { + for (const bookName of this.selectedWorldBooks) { + const newName = `${bookName}_备份_${Date.now()}`; + const bookData = await loadWorldInfo(bookName); + await saveWorldInfo(newName, bookData); + } + if (window.toastr) window.toastr.success('备份创建成功!'); + + await updateWorldInfoList(); + eventSource.emit(event_types.CHARACTER_PAGE_LOADED); + this.loadAvailableWorldBooks(); + } catch (error) { + this.showError(`备份失败: ${error.message}`); + } finally { + this.setLoading(false); + } + } + + async deleteSelectedBooks() { + if (this.selectedWorldBooks.size === 0) return; + if (!confirm(`警告:这将永久删除 ${this.selectedWorldBooks.size} 个世界书及其所有内容!确定要继续吗?`)) return; + + this.setLoading(true); + try { + for (const bookName of this.selectedWorldBooks) { + await deleteWorldInfo(bookName); + } + if (window.toastr) window.toastr.success('批量删除成功!'); + + await updateWorldInfoList(); + eventSource.emit(event_types.CHARACTER_PAGE_LOADED); + this.loadAvailableWorldBooks(); + } catch (error) { + this.showError(`删除失败: ${error.message}`); + } finally { + this.setLoading(false); + } + } + + updateWorldBookCount() { + this.elements.worldBookCount.textContent = `世界书:${this.allWorldBooks.length}`; + } + + updateWorldBookSelectionUI() { + const count = this.selectedWorldBooks.size; + this.elements.worldBookSelectedCount.textContent = `已选择 ${count} 项`; + this.elements.worldBookBatchActions.classList.toggle('active', count > 0); + } + + + // 条目数据处理 (大部分逻辑与旧版相似) async loadWorldBookEntries(worldBookName) { - if (!worldBookName) { this.entries = []; this.renderEntries(); return; } + if (!worldBookName) { + this.entries = []; + this.filteredEntries = []; + this.selectedEntries.clear(); + this.renderEntries(); + this.updateEntryCount(); + this.updateSelectionUI(); + return; + } this.setLoading(true); this.currentWorldBook = worldBookName; try { - let rawEntries = await TavernHelper?.getLorebookEntries?.(worldBookName); - if (!rawEntries || rawEntries.length === 0) { - const bookData = await loadWorldInfo(worldBookName); - if (bookData?.entries) { - rawEntries = Object.entries(bookData.entries).map(([uid, entry]) => ({ - uid: parseInt(uid), enabled: !entry.disable, type: entry.constant ? 'constant' : 'selective', - keys: entry.key || [], content: entry.content || '', position: this.convertPositionFromNative(entry.position), - depth: entry.depth, order: entry.order, comment: entry.comment || '' - })); - } - } + const rawEntries = await safeLorebookEntries(worldBookName); this.entries = (rawEntries || []).map(e => ({ uid: e.uid, enabled: e.enabled, type: e.type || (e.constant ? 'constant' : 'selective'), keys: e.keys || [], content: e.content || '', position: e.position || 'before_character_definition', @@ -138,8 +313,11 @@ class WorldEditor { this.updateEntryCount(); } catch (error) { this.showError(`加载条目失败: ${error.message}`); - this.entries = []; this.renderEntries(); + this.entries = []; + this.filteredEntries = []; } finally { + this.selectedEntries.clear(); + this.updateSelectionUI(); this.setLoading(false); } } @@ -153,10 +331,6 @@ class WorldEditor { const container = this.elements.worldEditorEntriesContainer; const header = container.querySelector('.world-editor-entries-header'); - // Clear only the entry rows, not the header - while (container.firstChild && container.firstChild !== header) { - container.removeChild(container.firstChild); - } while (header && header.nextSibling) { container.removeChild(header.nextSibling); } @@ -178,7 +352,6 @@ class WorldEditor { tempDiv.innerHTML = rowHTML; const rowElement = tempDiv.firstChild; - // Safely set the content to prevent HTML rendering const contentCell = rowElement.querySelector('.world-editor-entry-content'); if (contentCell) { contentCell.textContent = e.content || ''; @@ -216,19 +389,13 @@ class WorldEditor { bindEntryEvents() { this.elements.worldEditorEntriesContainer.querySelectorAll('.world-editor-entry-row').forEach(row => { const uid = parseInt(row.dataset.uid); - - // Checkbox const checkbox = row.querySelector('.world-editor-entry-checkbox'); checkbox.addEventListener('change', (e) => { if (e.target.checked) this.selectedEntries.add(uid); else this.selectedEntries.delete(uid); row.classList.toggle('selected', e.target.checked); this.updateSelectionUI(); }); - - // Content click to open modal (for longer edits) row.querySelector('[data-action="open-editor"]').addEventListener('click', () => this.openEditModal(uid)); - - // Inline toggles (enabled, type) row.querySelectorAll('.inline-toggle').forEach(toggle => { toggle.addEventListener('click', (e) => { e.stopPropagation(); @@ -240,54 +407,106 @@ class WorldEditor { this.updateSingleEntry(uid, { [field]: newValue }); }); }); - - // Inline edits (inputs, selects) row.querySelectorAll('.inline-edit').forEach(input => { input.addEventListener('change', (e) => { e.stopPropagation(); const field = input.dataset.field; let value = input.value; if (input.type === 'number') value = parseInt(value, 10); - // The 'keys' field is no longer inline editable, so that specific logic can be removed. - const updates = { [field]: value }; - // If position changes, re-evaluate depth disable state if (field === 'position') { const depthInput = row.querySelector('[data-field="depth"]'); if (depthInput) depthInput.disabled = !value.startsWith('at_depth'); } - this.updateSingleEntry(uid, updates); }); - input.addEventListener('click', e => e.stopPropagation()); // Prevent row selection when clicking input + input.addEventListener('click', e => e.stopPropagation()); }); }); } + /** + * 使用原生 saveWorldInfo 更新条目,避免界面跳转 + * @param {Array} entriesToUpdate - 需要更新的条目对象数组 + */ + async updateEntriesWithNativeMethod(entriesToUpdate) { + try { + const bookData = await loadWorldInfo(this.currentWorldBook); + if (!bookData || !bookData.entries) { + throw new Error("无法加载世界书数据。"); + } + + const uidsToUpdate = new Set(entriesToUpdate.map(e => e.uid)); + const updatedUIDs = new Set(); + + // 更新 bookData.entries + for (const entry of entriesToUpdate) { + if (bookData.entries[entry.uid]) { + const nativeEntry = bookData.entries[entry.uid]; + nativeEntry.comment = entry.comment; + nativeEntry.content = entry.content; + nativeEntry.key = entry.keys; + nativeEntry.disable = !entry.enabled; + nativeEntry.constant = entry.type === 'constant'; + nativeEntry.position = this.convertPositionToNative(entry.position); + nativeEntry.depth = entry.depth; + nativeEntry.order = entry.order; + nativeEntry.exclude_recursion = entry.exclude_recursion; + nativeEntry.prevent_recursion = entry.prevent_recursion; + updatedUIDs.add(entry.uid); + } + } + + if (updatedUIDs.size !== uidsToUpdate.size) { + console.warn("[世界书编辑器] 部分条目更新失败,UID可能不存在。"); + } + + await saveWorldInfo(this.currentWorldBook, bookData, true); // true 表示静默保存 + + // Optimistic UI update in local state + for (const updatedEntry of entriesToUpdate) { + const localEntry = this.entries.find(e => e.uid === updatedEntry.uid); + if (localEntry) { + Object.assign(localEntry, updatedEntry); + } + } + this.renderEntries(); + + } catch (error) { + this.showError(`更新失败: ${error.message}`); + this.loadWorldBookEntries(this.currentWorldBook); // On error, re-sync with truth + } + } + + // Helper function to convert string position to native number format + convertPositionToNative(posStr) { + const map = { + 'before_character_definition': 0, + 'after_character_definition': 1, + 'before_author_note': 2, + 'after_author_note': 3, + 'at_depth': 4, + 'at_depth_as_system': 4 + }; + return map[posStr] !== undefined ? map[posStr] : 4; + } + async updateSingleEntry(uid, updates) { const entry = this.entries.find(e => e.uid === uid); if (!entry) return; - - // Optimistic UI update - Object.assign(entry, updates); - this.renderEntries(); // Re-render to reflect changes immediately - - try { - await TavernHelper.setLorebookEntries(this.currentWorldBook, [{ ...entry, ...updates }]); - } catch (error) { - this.showError(`更新失败: ${error.message}`); - // Revert on failure - this.loadWorldBookEntries(this.currentWorldBook); - } + const updatedEntry = { ...entry, ...updates }; + await this.updateEntriesWithNativeMethod([updatedEntry]); } async batchUpdateEntries(updates, confirmation = null) { if (this.selectedEntries.size === 0) return; if (confirmation && !confirm(confirmation)) return; - const entries = this.entries.filter(e => this.selectedEntries.has(e.uid)).map(e => ({ ...e, ...updates })); - await TavernHelper.setLorebookEntries(this.currentWorldBook, entries); - this.loadWorldBookEntries(this.currentWorldBook); // Refresh + const entriesToUpdate = this.entries + .filter(e => this.selectedEntries.has(e.uid)) + .map(e => ({ ...e, ...updates })); + + await this.updateEntriesWithNativeMethod(entriesToUpdate); if (window.toastr) window.toastr.success('批量更新成功!'); } @@ -303,9 +522,12 @@ class WorldEditor { async batchDeleteEntries() { if (this.selectedEntries.size === 0 || !confirm(`删除 ${this.selectedEntries.size} 个条目?`)) return; - await TavernHelper.deleteLorebookEntries(this.currentWorldBook, Array.from(this.selectedEntries)); - this.selectedEntries.clear(); - this.loadWorldBookEntries(this.currentWorldBook); // Refresh + try { + await TavernHelper.deleteLorebookEntries(this.currentWorldBook, Array.from(this.selectedEntries)); + this.loadWorldBookEntries(this.currentWorldBook); + } catch (error) { + this.showError(`删除失败: ${error.message}`); + } } toggleSelectAll(checked) { @@ -327,16 +549,8 @@ class WorldEditor { filterEntries() { const term = this.elements.worldEditorSearchBox.value.toLowerCase(); - const searchType = document.getElementById('world-editor-search-type').value; - - if (!term) { - this.filteredEntries = [...this.entries]; - } else { - this.filteredEntries = this.entries.filter(e => { - const targetField = e[searchType] || ''; - return targetField.toLowerCase().includes(term); - }); - } + const searchType = this.elements.worldEditorSearchType.value; + this.filteredEntries = !term ? [...this.entries] : this.entries.filter(e => (e[searchType] || '').toLowerCase().includes(term)); this.renderEntries(); } @@ -355,146 +569,84 @@ class WorldEditor { showEditModal(title, entry) { const formHtml = this.getEditFormHtml(entry); - showHtmlModal(title, formHtml, { - onOk: (dialog) => { - this.saveEntry(dialog); - return true; // Close the modal - } - }); + showHtmlModal(title, formHtml, { onOk: (d) => { this.saveEntry(d); return true; } }); } getEditFormHtml(entry) { return `
-
- - -
- - - - - - - - - - - - - - - - - - - - - - -
- - -
-
- - -
- +
+ + + + + + + +
+
`; } async saveEntry(dialog) { const formData = this.getFormDataFromModal(dialog); - if (this.currentEditingEntry) { - await TavernHelper.setLorebookEntries(this.currentWorldBook, [{ ...this.currentEditingEntry, ...formData }]); - } else { - await TavernHelper.createLorebookEntries(this.currentWorldBook, [formData]); + try { + if (this.currentEditingEntry) { + // 使用改造后的原生方法更新 + await this.updateEntriesWithNativeMethod([{ ...this.currentEditingEntry, ...formData }]); + } else { + // 创建条目仍然可以使用TavernHelper,因为它通常不会触发跳转 + await TavernHelper.createLorebookEntries(this.currentWorldBook, [formData]); + } + // 刷新当前视图 + this.loadWorldBookEntries(this.currentWorldBook); + } catch (error) { + this.showError(`保存失败: ${error.message}`); } - this.loadWorldBookEntries(this.currentWorldBook); } getFormDataFromModal(dialog) { - const data = {}; - data.enabled = dialog.find('#world-editor-entry-enabled').is(':checked'); - data.type = dialog.find('#world-editor-entry-type').val(); - data.keys = dialog.find('#world-editor-entry-keys').val().split('\n').map(k => k.trim()).filter(Boolean); - data.content = dialog.find('#world-editor-entry-content').val(); - data.position = dialog.find('#world-editor-entry-position').val(); - data.depth = parseInt(dialog.find('#world-editor-entry-depth').val()); - data.order = parseInt(dialog.find('#world-editor-entry-order').val()); - data.comment = dialog.find('#world-editor-entry-comment').val(); - data.exclude_recursion = dialog.find('#world-editor-entry-disable-recursion').is(':checked'); - data.prevent_recursion = dialog.find('#world-editor-entry-prevent-recursion').is(':checked'); - return data; + return { + enabled: dialog.find('#world-editor-entry-enabled').is(':checked'), + type: dialog.find('#world-editor-entry-type').val(), + keys: dialog.find('#world-editor-entry-keys').val().split('\n').map(k => k.trim()).filter(Boolean), + content: dialog.find('#world-editor-entry-content').val(), + position: dialog.find('#world-editor-entry-position').val(), + depth: parseInt(dialog.find('#world-editor-entry-depth').val()), + order: parseInt(dialog.find('#world-editor-entry-order').val()), + comment: dialog.find('#world-editor-entry-comment').val(), + exclude_recursion: dialog.find('#world-editor-entry-disable-recursion').is(':checked'), + prevent_recursion: dialog.find('#world-editor-entry-prevent-recursion').is(':checked') + }; } - setLoading(loading) { this.elements.worldEditorEntriesContainer.classList.toggle('loading', loading); } + setLoading(loading) { + this.isLoading = loading; + document.getElementById('world-editor-container').classList.toggle('loading', loading); + } showError(msg) { if (window.toastr) window.toastr.error(msg); console.error(msg); } sortEntries(key) { - if (this.sortState.key === key) { - this.sortState.asc = !this.sortState.asc; - } else { - this.sortState.key = key; - this.sortState.asc = true; - } + if (this.sortState.key === key) this.sortState.asc = !this.sortState.asc; + else { this.sortState.key = key; this.sortState.asc = true; } this.renderEntries(); } sortFilteredEntries() { const { key, asc } = this.sortState; this.filteredEntries.sort((a, b) => { - let valA = a[key]; - let valB = b[key]; - + let valA = a[key], valB = b[key]; if (typeof valA === 'string') valA = valA.toLowerCase(); if (typeof valB === 'string') valB = valB.toLowerCase(); - if (valA < valB) return asc ? -1 : 1; if (valA > valB) return asc ? 1 : -1; return 0; @@ -503,32 +655,33 @@ class WorldEditor { bindExternalEvents() { eventSource.on(event_types.CHAT_CHANGED, () => { - console.log('[世界书编辑器] 检测到聊天变更 (CHAT_CHANGED),将自动刷新。'); - this.loadAvailableWorldBooks(); + console.log('[世界书编辑器] 检测到聊天变更,将自动刷新。'); + if (this.currentWorldBook) { + this.loadWorldBookEntries(this.currentWorldBook); + } else { + this.loadAvailableWorldBooks(); + } }); - console.log('[世界书编辑器] 已成功绑定外部事件监听器。'); } } -function initializeWorldEditorWhenVisible() { - const panel = document.getElementById('amily2_world_editor_panel'); - if (!panel) { console.error('[WorldEditor] Panel not found!'); return; } - const observer = new MutationObserver(() => { - if (panel.style.display !== 'none' && !window.worldEditorInstance) { - window.worldEditorInstance = new WorldEditor(); - observer.disconnect(); - } - }); - observer.observe(panel, { attributes: true, attributeFilter: ['style'] }); - if (panel.style.display !== 'none') { // Check initial state - if (!window.worldEditorInstance) window.worldEditorInstance = new WorldEditor(); - observer.disconnect(); +function initializeWorldEditor() { + // 确保面板存在 + if (!document.getElementById('amily2_world_editor_panel')) { + console.error('[WorldEditor] Panel not found, initialization aborted.'); + return; + } + // 防止重复初始化 + if (!window.worldEditorInstance) { + console.log('[WorldEditor] Initializing WorldEditor instance.'); + window.worldEditorInstance = new WorldEditor(); } } +// 确保在DOM加载完毕后执行 if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initializeWorldEditorWhenVisible); + document.addEventListener('DOMContentLoaded', initializeWorldEditor); } else { - initializeWorldEditorWhenVisible(); + initializeWorldEditor(); }