Merge branch 'Wx-2025:main' into main

This commit is contained in:
SilenceLurker
2025-12-06 19:10:49 +08:00
committed by GitHub
35 changed files with 7092 additions and 4241 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,242 +1,248 @@
import { getSlideToggleOptions } from '/script.js';
import { slideToggle } from '/lib.js';
import { extension_settings, renderExtensionTemplateAsync } from "/scripts/extensions.js";
import { extensionName, defaultSettings } from "../utils/settings.js";
import {
checkAuthorization,
displayExpiryInfo,
pluginAuthStatus,
} from "../utils/auth.js";
import {
updateUI,
setAvailableModels,
populateModelDropdown,
applyUpdateIndicator,
} from "./state.js";
import { bindModalEvents } from "./bindings.js";
import { fetchModels } from "../core/api.js";
import { bindHistoriographyEvents } from "./historiography-bindings.js";
import { bindHanlinyuanEvents } from "./hanlinyuan-bindings.js";
import { bindTableEvents } from './table-bindings.js';
import { showContentModal } from "./page-window.js";
import { initializeRendererBindings } from "../core/tavern-helper/renderer-bindings.js";
const extensionFolderPath = `scripts/extensions/third-party/${extensionName}`;
async function loadSettings() {
extension_settings[extensionName] = {
...defaultSettings,
...(extension_settings[extensionName] || {}),
};
checkAuthorization();
const autoLogin = localStorage.getItem("plugin_auto_login") === "true";
console.log(
`[Amily2-调试] 授权状态: ${pluginAuthStatus.authorized}, 自动登录标志: ${autoLogin}`,
);
if (autoLogin && pluginAuthStatus.authorized) {
console.log("[Amily2号] 检测到有效授权将执行自动UI更新。");
}
$("#expiry_info").html(displayExpiryInfo());
updateUI();
if (pluginAuthStatus.authorized && extension_settings[extensionName].apiUrl) {
const cachedModels = localStorage.getItem("cached_models_amily2");
if (cachedModels) {
const models = JSON.parse(cachedModels);
console.log(`[Amily2号] 从缓存加载模型列表 (${models.length}个)`);
setAvailableModels(models);
populateModelDropdown();
} else {
toastr.info("正在自动加载模型列表...", "Amily2号");
setTimeout(async () => {
const models = await fetchModels();
if (models.length > 0) {
setAvailableModels(models);
localStorage.setItem("cached_models_amily2", JSON.stringify(models));
populateModelDropdown();
}
}, 500);
}
}
}
async function initializePanel(contentPanel, errorContainer) {
if (contentPanel.data("initialized")) return;
try {
const modalContent = await $.get(`${extensionFolderPath}/assets/amily2-modal.html`);
contentPanel.html(modalContent);
const mainContainer = contentPanel.find('#amily2_chat_optimiser');
if (mainContainer.length) {
const additionalFeaturesContent = await $.get(`${extensionFolderPath}/assets/Amily2-AdditionalFeatures.html`);
const additionalPanelHtml = `<div id="amily2_additional_features_panel" style="display: none;">${additionalFeaturesContent}</div>`;
mainContainer.append(additionalPanelHtml);
const hanlinyuanContent = await $.get(`${extensionFolderPath}/assets/hanlinyuan.html`);
const hanlinyuanPanelHtml = `<div id="amily2_hanlinyuan_panel" style="display: none;">${hanlinyuanContent}</div>`;
mainContainer.append(hanlinyuanPanelHtml);
const memorisationFormsContent = await $.get(`${extensionFolderPath}/assets/Memorisation-forms.html`);
const memorisationFormsPanelHtml = `<div id="amily2_memorisation_forms_panel" style="display: none;">${memorisationFormsContent}</div>`;
mainContainer.append(memorisationFormsPanelHtml);
const plotOptimizationContent = await $.get(`${extensionFolderPath}/assets/Amily2-optimization.html`);
const plotOptimizationPanelHtml = `<div id="amily2_plot_optimization_panel" style="display: none;">${plotOptimizationContent}</div>`;
mainContainer.append(plotOptimizationPanelHtml);
const cwbContent = await $.get(`${extensionFolderPath}/CharacterWorldBook/cwb_settings.html`);
const cwbPanelHtml = `<div id="amily2_character_world_book_panel" style="display: none;">${cwbContent}</div>`;
mainContainer.append(cwbPanelHtml);
const worldEditorContent = await $.get(`${extensionFolderPath}/WorldEditor.html`);
const worldEditorPanelHtml = `<div id="amily2_world_editor_panel" style="display: none;">${worldEditorContent}</div>`;
mainContainer.append(worldEditorPanelHtml);
const glossaryContent = await $.get(`${extensionFolderPath}/assets/amily2-glossary.html`);
const glossaryPanelHtml = `<div id="amily2_glossary_panel" style="display: none;">${glossaryContent}</div>`;
mainContainer.append(glossaryPanelHtml);
const rendererContent = await $.get(`${extensionFolderPath}/core/tavern-helper/renderer.html`);
const rendererPanelHtml = `<div id="amily2_renderer_panel" style="display: none;">${rendererContent}</div>`;
mainContainer.append(rendererPanelHtml);
// 在面板创建后,加载世界书编辑器脚本
const worldEditorScriptId = 'world-editor-script';
if (!document.getElementById(worldEditorScriptId)) {
const worldEditorScript = document.createElement("script");
worldEditorScript.id = worldEditorScriptId;
worldEditorScript.type = "module"; // 必须作为模块加载
worldEditorScript.src = `${extensionFolderPath}/WorldEditor/WorldEditor.js?v=${Date.now()}`;
document.head.appendChild(worldEditorScript);
}
}
bindModalEvents();
bindHistoriographyEvents();
await loadSettings();
bindHanlinyuanEvents();
bindTableEvents();
initializeRendererBindings();
contentPanel.data("initialized", true);
console.log("[Amily-重构] 宫殿模块已按蓝图竣工。");
applyUpdateIndicator();
} catch (error) {
console.error("[Amily-建设部] 紧急报告:加载模块化蓝图时发生意外:", error);
const errorMessage = errorContainer
? '<p style="color:red; padding:10px; border:1px solid red; border-radius:5px;">紧急报告在扩展区域建造Amily2号府邸时发生意外。</p>'
: '<p style="color:red; padding: 20px;">紧急报告无法加载Amily2号府邸内饰。</p>';
if (errorContainer) {
errorContainer.append(errorMessage);
} else {
contentPanel.html(errorMessage);
}
}
}
function toggleDrawerFallback() {
const drawerIcon = $('#amily2_drawer_icon');
const contentPanel = $('#amily2_drawer_content');
if (drawerIcon.hasClass('openIcon') && !contentPanel.is(':visible')) {
drawerIcon.removeClass('openIcon').addClass('closedIcon');
}
if (drawerIcon.hasClass('closedIcon')) {
$('.openDrawer').not(contentPanel).not('.pinnedOpen').addClass('resizing').each((_, el) => {
slideToggle(el, {
...getSlideToggleOptions(),
onAnimationEnd: function (el) {
el.closest('.drawer-content').classList.remove('resizing');
},
});
});
$('.openIcon').not(drawerIcon).not('.drawerPinnedOpen').toggleClass('closedIcon openIcon');
$('.openDrawer').not(contentPanel).not('.pinnedOpen').toggleClass('closedDrawer openDrawer');
drawerIcon.toggleClass('closedIcon openIcon');
contentPanel.toggleClass('closedDrawer openDrawer');
contentPanel.addClass('resizing').each((_, el) => {
slideToggle(el, {
...getSlideToggleOptions(),
onAnimationEnd: function (el) {
el.closest('.drawer-content').classList.remove('resizing');
},
});
});
} else {
drawerIcon.toggleClass('openIcon closedIcon');
contentPanel.toggleClass('openDrawer closedDrawer');
contentPanel.addClass('resizing').each((_, el) => {
slideToggle(el, {
...getSlideToggleOptions(),
onAnimationEnd: function (el) {
el.closest('.drawer-content').classList.remove('resizing');
},
});
});
}
}
export async function createDrawer() {
const settings = extension_settings[extensionName];
const location = settings.iconLocation || 'topbar';
if (location === 'topbar') {
if ($("#amily2_main_drawer").length > 0) return;
const amily2DrawerHtml = `
<div id="amily2_main_drawer" class="drawer">
<div class="drawer-toggle" data-drawer="amily2_drawer_content">
<div id="amily2_drawer_icon" class="drawer-icon fa-solid fa-magic fa-fw closedIcon interactable" title="Amily2号优化助手" tabindex="0"></div>
</div>
<div id="amily2_drawer_content" class="drawer-content closedDrawer">
</div>
</div>
`;
$("#sys-settings-button").after(amily2DrawerHtml);
const contentPanel = $("#amily2_drawer_content");
await initializePanel(contentPanel);
try {
const { doNavbarIconClick } = await import('/script.js');
if (typeof doNavbarIconClick === 'function') {
$('#amily2_main_drawer .drawer-toggle').on('click', doNavbarIconClick);
console.log('[Amily2-兼容性] 检测到新版环境,已绑定官方点击事件。');
} else {
throw new Error('doNavbarIconClick is not a function');
}
} catch (error) {
$('#amily2_main_drawer .drawer-toggle').on('click', toggleDrawerFallback);
console.log('[Amily2-兼容性] 检测到版环境 (无法导入 doNavbarIconClick),已绑定后备点击事件。');
}
} else if (location === 'extensions') {
if ($("#extensions_settings2 #amily2_chat_optimiser").length > 0) return;
const amilyFrameHtml = `
<div id="amily2_extension_frame">
<div class="inline-drawer">
<div class="inline-drawer-toggle inline-drawer-header">
<b><i class="fas fa-crown" style="color: #ffc107;"></i> Amily2号 优化中枢</b>
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
</div>
<div class="inline-drawer-content" style="display: none;">
</div>
</div>
</div>
`;
const frame = $(amilyFrameHtml);
$('#extensions_settings2').append(frame);
const contentPanel = frame.find('.inline-drawer-content');
initializePanel(contentPanel, frame);
}
}
import { getSlideToggleOptions } from '/script.js';
import { slideToggle } from '/lib.js';
import { extension_settings, renderExtensionTemplateAsync } from "/scripts/extensions.js";
import { extensionName, defaultSettings } from "../utils/settings.js";
import {
checkAuthorization,
displayExpiryInfo,
pluginAuthStatus,
} from "../utils/auth.js";
import {
updateUI,
setAvailableModels,
populateModelDropdown,
applyUpdateIndicator,
} from "./state.js";
import { bindModalEvents } from "./bindings.js";
import { fetchModels } from "../core/api.js";
import { bindHistoriographyEvents } from "./historiography-bindings.js";
import { bindHanlinyuanEvents } from "./hanlinyuan-bindings.js";
import { bindTableEvents } from './table-bindings.js';
import { showContentModal } from "./page-window.js";
import { initializeRendererBindings } from "../core/tavern-helper/renderer-bindings.js";
import { bindSuperMemoryEvents } from "../core/super-memory/bindings.js";
const extensionFolderPath = `scripts/extensions/third-party/${extensionName}`;
async function loadSettings() {
extension_settings[extensionName] = {
...defaultSettings,
...(extension_settings[extensionName] || {}),
};
checkAuthorization();
const autoLogin = localStorage.getItem("plugin_auto_login") === "true";
console.log(
`[Amily2-调试] 授权状态: ${pluginAuthStatus.authorized}, 自动登录标志: ${autoLogin}`,
);
if (autoLogin && pluginAuthStatus.authorized) {
console.log("[Amily2号] 检测到有效授权将执行自动UI更新。");
}
$("#expiry_info").html(displayExpiryInfo());
updateUI();
if (pluginAuthStatus.authorized && extension_settings[extensionName].apiUrl) {
const cachedModels = localStorage.getItem("cached_models_amily2");
if (cachedModels) {
const models = JSON.parse(cachedModels);
console.log(`[Amily2号] 从缓存加载模型列表 (${models.length}个)`);
setAvailableModels(models);
populateModelDropdown();
} else {
toastr.info("正在自动加载模型列表...", "Amily2号");
setTimeout(async () => {
const models = await fetchModels();
if (models.length > 0) {
setAvailableModels(models);
localStorage.setItem("cached_models_amily2", JSON.stringify(models));
populateModelDropdown();
}
}, 500);
}
}
}
async function initializePanel(contentPanel, errorContainer) {
if (contentPanel.data("initialized")) return;
try {
const modalContent = await $.get(`${extensionFolderPath}/assets/amily2-modal.html`);
contentPanel.html(modalContent);
const mainContainer = contentPanel.find('#amily2_chat_optimiser');
if (mainContainer.length) {
const additionalFeaturesContent = await $.get(`${extensionFolderPath}/assets/Amily2-AdditionalFeatures.html`);
const additionalPanelHtml = `<div id="amily2_additional_features_panel" style="display: none;">${additionalFeaturesContent}</div>`;
mainContainer.append(additionalPanelHtml);
const hanlinyuanContent = await $.get(`${extensionFolderPath}/assets/hanlinyuan.html`);
const hanlinyuanPanelHtml = `<div id="amily2_hanlinyuan_panel" style="display: none;">${hanlinyuanContent}</div>`;
mainContainer.append(hanlinyuanPanelHtml);
const memorisationFormsContent = await $.get(`${extensionFolderPath}/assets/Memorisation-forms.html`);
const memorisationFormsPanelHtml = `<div id="amily2_memorisation_forms_panel" style="display: none;">${memorisationFormsContent}</div>`;
mainContainer.append(memorisationFormsPanelHtml);
const plotOptimizationContent = await $.get(`${extensionFolderPath}/assets/Amily2-optimization.html`);
const plotOptimizationPanelHtml = `<div id="amily2_plot_optimization_panel" style="display: none;">${plotOptimizationContent}</div>`;
mainContainer.append(plotOptimizationPanelHtml);
const cwbContent = await $.get(`${extensionFolderPath}/CharacterWorldBook/cwb_settings.html`);
const cwbPanelHtml = `<div id="amily2_character_world_book_panel" style="display: none;">${cwbContent}</div>`;
mainContainer.append(cwbPanelHtml);
const worldEditorContent = await $.get(`${extensionFolderPath}/WorldEditor.html`);
const worldEditorPanelHtml = `<div id="amily2_world_editor_panel" style="display: none;">${worldEditorContent}</div>`;
mainContainer.append(worldEditorPanelHtml);
const glossaryContent = await $.get(`${extensionFolderPath}/assets/amily2-glossary.html`);
const glossaryPanelHtml = `<div id="amily2_glossary_panel" style="display: none;">${glossaryContent}</div>`;
mainContainer.append(glossaryPanelHtml);
const rendererContent = await $.get(`${extensionFolderPath}/core/tavern-helper/renderer.html`);
const rendererPanelHtml = `<div id="amily2_renderer_panel" style="display: none;">${rendererContent}</div>`;
mainContainer.append(rendererPanelHtml);
const superMemoryContent = await $.get(`${extensionFolderPath}/core/super-memory/index.html`);
const superMemoryPanelHtml = `<div id="amily2_super_memory_panel" style="display: none;">${superMemoryContent}</div>`;
mainContainer.append(superMemoryPanelHtml);
// 在面板创建后,加载世界书编辑器脚本
const worldEditorScriptId = 'world-editor-script';
if (!document.getElementById(worldEditorScriptId)) {
const worldEditorScript = document.createElement("script");
worldEditorScript.id = worldEditorScriptId;
worldEditorScript.type = "module"; // 必须作为模块加载
worldEditorScript.src = `${extensionFolderPath}/WorldEditor/WorldEditor.js?v=${Date.now()}`;
document.head.appendChild(worldEditorScript);
}
}
bindModalEvents();
bindHistoriographyEvents();
await loadSettings();
bindHanlinyuanEvents();
bindTableEvents();
initializeRendererBindings();
bindSuperMemoryEvents();
contentPanel.data("initialized", true);
console.log("[Amily-重构] 宫殿模块已按蓝图竣工。");
applyUpdateIndicator();
} catch (error) {
console.error("[Amily-建设部] 紧急报告:加载模块化蓝图时发生意外:", error);
const errorMessage = errorContainer
? '<p style="color:red; padding:10px; border:1px solid red; border-radius:5px;">紧急报告在扩展区域建造Amily2号府邸时发生意外。</p>'
: '<p style="color:red; padding: 20px;">紧急报告无法加载Amily2号府邸内饰。</p>';
if (errorContainer) {
errorContainer.append(errorMessage);
} else {
contentPanel.html(errorMessage);
}
}
}
function toggleDrawerFallback() {
const drawerIcon = $('#amily2_drawer_icon');
const contentPanel = $('#amily2_drawer_content');
if (drawerIcon.hasClass('openIcon') && !contentPanel.is(':visible')) {
drawerIcon.removeClass('openIcon').addClass('closedIcon');
}
if (drawerIcon.hasClass('closedIcon')) {
$('.openDrawer').not(contentPanel).not('.pinnedOpen').addClass('resizing').each((_, el) => {
slideToggle(el, {
...getSlideToggleOptions(),
onAnimationEnd: function (el) {
el.closest('.drawer-content').classList.remove('resizing');
},
});
});
$('.openIcon').not(drawerIcon).not('.drawerPinnedOpen').toggleClass('closedIcon openIcon');
$('.openDrawer').not(contentPanel).not('.pinnedOpen').toggleClass('closedDrawer openDrawer');
drawerIcon.toggleClass('closedIcon openIcon');
contentPanel.toggleClass('closedDrawer openDrawer');
contentPanel.addClass('resizing').each((_, el) => {
slideToggle(el, {
...getSlideToggleOptions(),
onAnimationEnd: function (el) {
el.closest('.drawer-content').classList.remove('resizing');
},
});
});
} else {
drawerIcon.toggleClass('openIcon closedIcon');
contentPanel.toggleClass('openDrawer closedDrawer');
contentPanel.addClass('resizing').each((_, el) => {
slideToggle(el, {
...getSlideToggleOptions(),
onAnimationEnd: function (el) {
el.closest('.drawer-content').classList.remove('resizing');
},
});
});
}
}
export async function createDrawer() {
const settings = extension_settings[extensionName];
const location = settings.iconLocation || 'topbar';
if (location === 'topbar') {
if ($("#amily2_main_drawer").length > 0) return;
const amily2DrawerHtml = `
<div id="amily2_main_drawer" class="drawer">
<div class="drawer-toggle" data-drawer="amily2_drawer_content">
<div id="amily2_drawer_icon" class="drawer-icon fa-solid fa-magic fa-fw closedIcon interactable" title="Amily2号优化助手" tabindex="0"></div>
</div>
<div id="amily2_drawer_content" class="drawer-content closedDrawer">
</div>
</div>
`;
$("#sys-settings-button").after(amily2DrawerHtml);
const contentPanel = $("#amily2_drawer_content");
await initializePanel(contentPanel);
try {
const { doNavbarIconClick } = await import('/script.js');
if (typeof doNavbarIconClick === 'function') {
$('#amily2_main_drawer .drawer-toggle').on('click', doNavbarIconClick);
console.log('[Amily2-兼容性] 检测到版环境,已绑定官方点击事件。');
} else {
throw new Error('doNavbarIconClick is not a function');
}
} catch (error) {
$('#amily2_main_drawer .drawer-toggle').on('click', toggleDrawerFallback);
console.log('[Amily2-兼容性] 检测到旧版环境 (无法导入 doNavbarIconClick),已绑定后备点击事件。');
}
} else if (location === 'extensions') {
if ($("#extensions_settings2 #amily2_chat_optimiser").length > 0) return;
const amilyFrameHtml = `
<div id="amily2_extension_frame">
<div class="inline-drawer">
<div class="inline-drawer-toggle inline-drawer-header">
<b><i class="fas fa-crown" style="color: #ffc107;"></i> Amily2号 优化中枢</b>
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
</div>
<div class="inline-drawer-content" style="display: none;">
</div>
</div>
</div>
`;
const frame = $(amilyFrameHtml);
$('#extensions_settings2').append(frame);
const contentPanel = frame.find('.inline-drawer-content');
initializePanel(contentPanel, frame);
}
}

File diff suppressed because one or more lines are too long

View File

@@ -10,7 +10,8 @@ import { applyExclusionRules, extractBlocksByTags } from '../core/utils/rag-tag-
import {
getAvailableWorldbooks, getLoresForWorldbook,
executeManualSummary, executeRefinement,
executeExpedition, stopExpedition
executeExpedition, stopExpedition,
archiveCurrentLedger, getArchivedLedgers, restoreArchivedLedger
} from "../core/historiographer.js";
import { getNgmsApiSettings, testNgmsApiConnection, fetchNgmsModels } from "../core/api/Ngms_api.js";
@@ -265,6 +266,51 @@ export function bindHistoriographyEvents() {
updateExpeditionButtonUI('idle');
// ========== 📚 史册归档与回溯 绑定 ==========
const archiveCurrentBtn = document.getElementById("amily2_mhb_archive_current");
const archiveSelector = document.getElementById("amily2_mhb_archive_selector");
const refreshArchivesBtn = document.getElementById("amily2_mhb_refresh_archives");
const restoreArchiveBtn = document.getElementById("amily2_mhb_restore_archive");
const updateArchiveList = async () => {
archiveSelector.innerHTML = '<option value="">正在翻阅旧档...</option>';
const archives = await getArchivedLedgers();
archiveSelector.innerHTML = ""; // 清空
if (archives && archives.length > 0) {
archives.forEach((arch) => {
const option = document.createElement("option");
option.value = arch.key;
option.textContent = arch.comment;
archiveSelector.appendChild(option);
});
} else {
archiveSelector.innerHTML = '<option value="">未发现归档史册</option>';
}
};
archiveCurrentBtn.addEventListener("click", async () => {
if (confirm("确定要归档当前的【对话流水总帐】并停用它吗?\n这将允许您开始一段全新的历史记录。")) {
const success = await archiveCurrentLedger();
if (success) {
updateArchiveList(); // 归档成功后刷新列表
}
}
});
refreshArchivesBtn.addEventListener("click", updateArchiveList);
restoreArchiveBtn.addEventListener("click", async () => {
const selectedKey = archiveSelector.value;
if (!selectedKey) {
toastr.warning("请先选择一个要回溯的史册!", "圣谕不明");
return;
}
if (confirm("确定要回溯选中的史册吗?\n当前的活跃史册如果有将被自动归档。")) {
await restoreArchivedLedger(selectedKey);
updateArchiveList(); // 回溯后刷新列表
}
});
// ========== 💎 宏史卷 (史册精炼) 绑定 ==========
const largeWbSelector = document.getElementById(
"amily2_mhb_large_worldbook_selector",

View File

@@ -120,12 +120,22 @@ export function showHtmlModal(title, htmlContent, options = {}) {
}
function escapeHtml(text) {
if (!text) return '';
return text
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
export function showSummaryModal(summaryText, callbacks) {
const { onConfirm, onRegenerate, onCancel } = callbacks;
const modalHtml = `
<div class="historiographer-summary-modal">
<textarea class="text_pole" style="width: 100%; height: 50vh; resize: vertical;">${summaryText}</textarea>
<textarea class="text_pole" style="width: 100%; height: 50vh; resize: vertical;">${escapeHtml(summaryText)}</textarea>
</div>
`;

File diff suppressed because one or more lines are too long