Merge branch 'Wx-2025:main' into main

This commit is contained in:
SilenceLurker
2025-11-11 11:59:00 +08:00
committed by GitHub
15 changed files with 371 additions and 34 deletions

View File

@@ -324,6 +324,10 @@ async function proceedWithCardUpdate($panel, messagesToUse) {
async function triggerAutomaticUpdate($panel) { async function triggerAutomaticUpdate($panel) {
logDebug(`检查是否需要更新。总消息数: ${state.allChatMessages.length}, 自动更新启用: ${state.autoUpdateEnabled}`); logDebug(`检查是否需要更新。总消息数: ${state.allChatMessages.length}, 自动更新启用: ${state.autoUpdateEnabled}`);
if (!isCwbEnabled()) {
logDebug('更新检查已跳过 - CharacterWorldBook总开关已关闭。');
return;
}
if (!state.autoUpdateEnabled || isUpdatingCard || !state.customApiConfig.url || !state.customApiConfig.model || state.allChatMessages.length === 0) { if (!state.autoUpdateEnabled || isUpdatingCard || !state.customApiConfig.url || !state.customApiConfig.model || state.allChatMessages.length === 0) {
logDebug('更新检查已跳过(未启用、正在更新、未配置或无消息)。'); logDebug('更新检查已跳过(未启用、正在更新、未配置或无消息)。');
return; return;
@@ -544,6 +548,10 @@ async function processNextBatch($panel) {
} }
export async function startBatchUpdate($panel) { export async function startBatchUpdate($panel) {
if (!isCwbEnabled()) {
showToastr('warning', 'CharacterWorldBook总开关已关闭无法执行批量更新。');
return;
}
await loadAllChatMessages($panel); await loadAllChatMessages($panel);
if (!state.customApiConfig.url || !state.customApiConfig.model) { if (!state.customApiConfig.url || !state.customApiConfig.model) {
showToastr('warning', '请先配置API信息。'); showToastr('warning', '请先配置API信息。');
@@ -581,6 +589,10 @@ export async function startBatchUpdate($panel) {
} }
export async function handleFloorRangeUpdate($panel) { export async function handleFloorRangeUpdate($panel) {
if (!isCwbEnabled()) {
showToastr('warning', 'CharacterWorldBook总开关已关闭无法执行楼层范围更新。');
return;
}
await loadAllChatMessages($panel); await loadAllChatMessages($panel);
if (isUpdatingCard || isBatchUpdating) { if (isUpdatingCard || isBatchUpdating) {
showToastr('info', '已有更新任务在进行中。'); showToastr('info', '已有更新任务在进行中。');
@@ -639,6 +651,10 @@ export async function handleFloorRangeUpdate($panel) {
} }
export async function manualUpdateLogic($panel = null) { export async function manualUpdateLogic($panel = null) {
if (!isCwbEnabled()) {
logDebug('手动更新已跳过 - CharacterWorldBook总开关已关闭。');
return;
}
if (isUpdatingCard) { if (isUpdatingCard) {
showToastr('info', '已有更新任务在进行中。'); showToastr('info', '已有更新任务在进行中。');
return; return;

View File

@@ -78,6 +78,7 @@
<button class="world-editor-btn world-editor-btn-warning" id="world-editor-disable-selected-btn">批量禁用</button> <button class="world-editor-btn world-editor-btn-warning" id="world-editor-disable-selected-btn">批量禁用</button>
<button class="world-editor-btn world-editor-btn-primary" id="world-editor-set-blue-btn">批量蓝灯</button> <button class="world-editor-btn world-editor-btn-primary" id="world-editor-set-blue-btn">批量蓝灯</button>
<button class="world-editor-btn world-editor-btn-success" id="world-editor-set-green-btn">批量绿灯</button> <button class="world-editor-btn world-editor-btn-success" id="world-editor-set-green-btn">批量绿灯</button>
<button class="world-editor-btn world-editor-btn-info" id="world-editor-copy-entries-btn">复制条目</button>
<button class="world-editor-btn world-editor-btn-danger" id="world-editor-delete-selected-btn">批量删除</button> <button class="world-editor-btn world-editor-btn-danger" id="world-editor-delete-selected-btn">批量删除</button>
<button class="world-editor-btn world-editor-btn-primary" id="world-editor-set-disable-recursion-btn">不可递归</button> <button class="world-editor-btn world-editor-btn-primary" id="world-editor-set-disable-recursion-btn">不可递归</button>
<button class="world-editor-btn world-editor-btn-primary" id="world-editor-set-prevent-recursion-btn">防止递归</button> <button class="world-editor-btn world-editor-btn-primary" id="world-editor-set-prevent-recursion-btn">防止递归</button>

View File

@@ -37,6 +37,14 @@
cursor: pointer; cursor: pointer;
font-size: 12px; font-size: 12px;
transition: background-color 0.3s; transition: background-color 0.3s;
color: white;
font-weight: 500;
white-space: nowrap;
}
#world-editor-container .world-editor-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
} }
#world-editor-container .world-editor-btn-primary { #world-editor-container .world-editor-btn-primary {
@@ -75,6 +83,15 @@
background-color: #e68900; background-color: #e68900;
} }
#world-editor-container .world-editor-btn-info {
background-color: #17a2b8;
color: white;
}
#world-editor-container .world-editor-btn-info:hover {
background-color: #138496;
}
#world-editor-container .world-editor-content { #world-editor-container .world-editor-content {
flex: 1; flex: 1;
padding: 20px; padding: 20px;
@@ -400,6 +417,12 @@
.world-editor-btn.small-btn { .world-editor-btn.small-btn {
padding: 4px 8px; padding: 4px 8px;
font-size: 11px; font-size: 11px;
color: white;
background-color: #4a90e2;
}
.world-editor-btn.small-btn:hover {
background-color: #357abd;
} }
#world-editor-container .world-editor-selector h3 { #world-editor-container .world-editor-selector h3 {
@@ -414,6 +437,26 @@
align-items: center; align-items: center;
} }
#world-editor-container .world-editor-selector button {
color: white;
font-weight: 500;
background-color: #4a90e2;
}
#world-editor-container .world-editor-selector button:hover {
background-color: #357abd;
}
/* 确保返回列表按钮有颜色 */
#world-editor-back-to-list-btn {
color: white !important;
background-color: #4a90e2 !important;
}
#world-editor-back-to-list-btn:hover {
background-color: #357abd !important;
}
/* ====== 布局修正 v2针对 fieldset ====== */ /* ====== 布局修正 v2针对 fieldset ====== */
/* 1. 重置 fieldset 的默认样式,使其表现为标准的 flex 容器 */ /* 1. 重置 fieldset 的默认样式,使其表现为标准的 flex 容器 */

View File

@@ -51,7 +51,7 @@ class WorldEditor {
'world-editor-select-all', 'world-editor-selected-count', 'world-editor-batch-actions', 'world-editor-select-all', 'world-editor-selected-count', 'world-editor-batch-actions',
'world-editor-entries-container', 'world-editor-entries-container',
'world-editor-enable-selected-btn', 'world-editor-disable-selected-btn', 'world-editor-enable-selected-btn', 'world-editor-disable-selected-btn',
'world-editor-set-blue-btn', 'world-editor-set-green-btn', 'world-editor-delete-selected-btn', 'world-editor-set-blue-btn', 'world-editor-set-green-btn', 'world-editor-copy-entries-btn', 'world-editor-delete-selected-btn',
'world-editor-set-disable-recursion-btn', 'world-editor-set-prevent-recursion-btn' 'world-editor-set-disable-recursion-btn', 'world-editor-set-prevent-recursion-btn'
]; ];
this.elements = {}; this.elements = {};
@@ -95,6 +95,7 @@ class WorldEditor {
this.elements.worldEditorDisableSelectedBtn.addEventListener('click', () => this.batchUpdateEntries({ enabled: false })); this.elements.worldEditorDisableSelectedBtn.addEventListener('click', () => this.batchUpdateEntries({ enabled: false }));
this.elements.worldEditorSetBlueBtn.addEventListener('click', () => this.batchUpdateEntries({ type: 'constant' })); this.elements.worldEditorSetBlueBtn.addEventListener('click', () => this.batchUpdateEntries({ type: 'constant' }));
this.elements.worldEditorSetGreenBtn.addEventListener('click', () => this.batchUpdateEntries({ type: 'selective' })); this.elements.worldEditorSetGreenBtn.addEventListener('click', () => this.batchUpdateEntries({ type: 'selective' }));
this.elements.worldEditorCopyEntriesBtn.addEventListener('click', () => this.copySelectedEntries());
this.elements.worldEditorDeleteSelectedBtn.addEventListener('click', () => this.batchDeleteEntries()); this.elements.worldEditorDeleteSelectedBtn.addEventListener('click', () => this.batchDeleteEntries());
this.elements.worldEditorSetDisableRecursionBtn.addEventListener('click', () => this.toggleBatchRecursion('exclude_recursion', '不可递归')); this.elements.worldEditorSetDisableRecursionBtn.addEventListener('click', () => this.toggleBatchRecursion('exclude_recursion', '不可递归'));
this.elements.worldEditorSetPreventRecursionBtn.addEventListener('click', () => this.toggleBatchRecursion('prevent_recursion', '防止递归')); this.elements.worldEditorSetPreventRecursionBtn.addEventListener('click', () => this.toggleBatchRecursion('prevent_recursion', '防止递归'));
@@ -298,13 +299,37 @@ class WorldEditor {
this.setLoading(true); this.setLoading(true);
this.currentWorldBook = worldBookName; this.currentWorldBook = worldBookName;
try { try {
const rawEntries = await safeLorebookEntries(worldBookName); const bookData = await loadWorldInfo(worldBookName);
this.entries = (rawEntries || []).map(e => ({ if (!bookData || !bookData.entries) {
uid: e.uid, enabled: e.enabled, type: e.type || (e.constant ? 'constant' : 'selective'), this.entries = [];
keys: e.keys || [], content: e.content || '', position: e.position || 'before_character_definition', this.filteredEntries = [];
depth: (String(e.position)?.startsWith('at_depth')) ? e.depth : null, order: e.order || 100, comment: e.comment || '', this.renderEntries();
exclude_recursion: e.exclude_recursion, prevent_recursion: e.prevent_recursion this.updateEntryCount();
return;
}
const positionMap = {
0: 'before_character_definition',
1: 'after_character_definition',
2: 'before_author_note',
3: 'after_author_note',
4: 'at_depth'
};
this.entries = Object.entries(bookData.entries).map(([uid, e]) => ({
uid: parseInt(uid),
enabled: !e.disable,
type: e.constant ? 'constant' : 'selective',
keys: e.key || [],
content: e.content || '',
position: positionMap[e.position] || 'at_depth',
depth: e.depth != null ? e.depth : 4,
order: e.order != null ? e.order : 100,
comment: e.comment || '',
exclude_recursion: e.excludeRecursion || false,
prevent_recursion: e.preventRecursion || false
})); }));
this.filteredEntries = [...this.entries]; this.filteredEntries = [...this.entries];
this.renderEntries(); this.renderEntries();
this.updateEntryCount(); this.updateEntryCount();
@@ -488,6 +513,114 @@ class WorldEditor {
this.batchUpdateEntries({ [field]: shouldEnable }, confirmation); this.batchUpdateEntries({ [field]: shouldEnable }, confirmation);
} }
async copySelectedEntries() {
if (this.selectedEntries.size === 0) {
this.showError('请先选择要复制的条目');
return;
}
// 获取所有世界书列表(包括当前世界书,允许在同一世界书内复制)
const availableBooks = this.allWorldBooks.map(book => book.name);
if (availableBooks.length === 0) {
this.showError('没有可用的世界书');
return;
}
console.log('[世界书编辑器] 准备复制条目,已选择:', this.selectedEntries.size, '个条目');
console.log('[世界书编辑器] 选中的UID:', Array.from(this.selectedEntries));
// 创建选择对话框
const selectHtml = `
<style>
.copy-dialog { padding: 20px; }
.copy-dialog label { display: block; margin-bottom: 10px; color: #ccc; font-weight: bold; }
.copy-dialog select { width: 100%; padding: 10px; background-color: #404040; color: white; border: 1px solid #555; border-radius: 4px; font-size: 14px; }
.copy-dialog .info { margin-top: 15px; padding: 10px; background-color: #2a2a2a; border-left: 3px solid #4a9eff; color: #ccc; }
</style>
<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('')}
</select>
<div class="info">
将复制 ${this.selectedEntries.size} 个条目到目标世界书
</div>
</div>
`;
showHtmlModal('复制条目', selectHtml, {
onOk: async (dialog) => {
const targetBook = dialog.find('#target-worldbook').val();
if (!targetBook) {
this.showError('请选择目标世界书');
return false;
}
await this.performCopy(targetBook);
return true;
}
});
}
async performCopy(targetBookName) {
this.setLoading(true);
try {
// 获取要复制的条目
const entriesToCopy = this.entries.filter(e => this.selectedEntries.has(e.uid));
console.log('[世界书编辑器] 过滤后的条目数量:', entriesToCopy.length);
console.log('[世界书编辑器] 条目详情:', entriesToCopy);
if (entriesToCopy.length === 0) {
this.showError('没有选中的条目');
return;
}
// 加载目标世界书
const targetBookData = await loadWorldInfo(targetBookName);
if (!targetBookData) {
this.showError(`目标世界书 "${targetBookName}" 不存在`);
return;
}
// 准备要创建的条目数据
const newEntries = entriesToCopy.map(entry => ({
enabled: entry.enabled,
type: entry.type,
keys: Array.isArray(entry.keys) ? entry.keys : [],
content: entry.content || '',
position: entry.position,
depth: entry.depth != null ? entry.depth : 4,
order: entry.order != null ? entry.order : 100,
comment: entry.comment || '',
exclude_recursion: entry.exclude_recursion || false,
prevent_recursion: entry.prevent_recursion || false
}));
console.log('[世界书编辑器] 准备创建的条目:', newEntries);
// 在目标世界书中创建条目
await amilyHelper.createLorebookEntries(targetBookName, newEntries);
if (window.toastr) {
window.toastr.success(`成功复制 ${entriesToCopy.length} 个条目到 "${targetBookName}"`);
}
// 如果复制到当前世界书,刷新视图
if (targetBookName === this.currentWorldBook) {
await this.loadWorldBookEntries(this.currentWorldBook);
}
} catch (error) {
console.error('[世界书编辑器] 复制失败:', error);
this.showError(`复制失败: ${error.message}`);
} finally {
this.setLoading(false);
}
}
async batchDeleteEntries() { async batchDeleteEntries() {
if (this.selectedEntries.size === 0 || !confirm(`删除 ${this.selectedEntries.size} 个条目?`)) return; if (this.selectedEntries.size === 0 || !confirm(`删除 ${this.selectedEntries.size} 个条目?`)) return;
try { try {

View File

@@ -1,5 +1,5 @@
{ {
"message": "插件群1060183271更新了多个版本了现在是v1.5.7,术语表上线。个人原因,降低更新频率以及无暇看帖子,有问题最好加群。" "message": "插件群1060183271有问题最好加群。"
} }
@@ -43,5 +43,6 @@

File diff suppressed because one or more lines are too long

View File

@@ -15,12 +15,20 @@
<div class="control-pair-container"> <div class="control-pair-container">
<div class="amily2_settings_block"> <div class="amily2_settings_block">
<label for="amily2_auto_hide_enabled">启用自动隐藏</label> <label for="amily2_auto_hide_enabled">按阈值自动隐藏</label>
<label class="toggle-switch"> <label class="toggle-switch">
<input id="amily2_auto_hide_enabled" type="checkbox" /> <input id="amily2_auto_hide_enabled" type="checkbox" />
<span class="slider"></span> <span class="slider"></span>
</label> </label>
</div> </div>
<div class="amily2_settings_block">
<label for="amily2_auto_hide_summarized_enabled">隐藏已总结楼层</label>
<label class="toggle-switch">
<input id="amily2_auto_hide_summarized_enabled" type="checkbox" />
<span class="slider"></span>
</label>
</div>
<div class="amily2_settings_block"> <div class="amily2_settings_block">

View File

@@ -1 +1,129 @@
(function(_0x4ee988,_0x4037de){const _0x37bb46=_0x58cf,_0x4f5812=_0x4ee988();while(!![]){try{const _0x1c6d7c=-parseInt(_0x37bb46(0x1c9))/0x1*(parseInt(_0x37bb46(0x1df))/0x2)+parseInt(_0x37bb46(0x1da))/0x3*(-parseInt(_0x37bb46(0x1d0))/0x4)+-parseInt(_0x37bb46(0x1d2))/0x5+-parseInt(_0x37bb46(0x1ea))/0x6+parseInt(_0x37bb46(0x1dd))/0x7*(-parseInt(_0x37bb46(0x1d6))/0x8)+-parseInt(_0x37bb46(0x1d1))/0x9+parseInt(_0x37bb46(0x1d7))/0xa;if(_0x1c6d7c===_0x4037de)break;else _0x4f5812['push'](_0x4f5812['shift']());}catch(_0xa818b5){_0x4f5812['push'](_0x4f5812['shift']());}}}(_0x5992,0xc4f22));function _0x58cf(_0x18e475,_0x4450b0){const _0x599238=_0x5992();return _0x58cf=function(_0x58cf6f,_0x5e2dd5){_0x58cf6f=_0x58cf6f-0x1c0;let _0x33b306=_0x599238[_0x58cf6f];return _0x33b306;},_0x58cf(_0x18e475,_0x4450b0);}import{getContext,extension_settings}from'/scripts/extensions.js';import{SlashCommandParser}from'/scripts/slash-commands/SlashCommandParser.js';import{extensionName}from'../utils/settings.js';async function executeSlashCommand(_0x28ecae){const _0x4e2c65=_0x58cf;if(!_0x28ecae)return;try{console['log']('[Amily-敕令执行官]\x20准备执行圣谕:\x20'+_0x28ecae);const _0x54b194=new SlashCommandParser(),_0x2ebd11=_0x54b194[_0x4e2c65(0x1e8)](_0x28ecae,![]);if(_0x2ebd11&&typeof _0x2ebd11[_0x4e2c65(0x1c7)]===_0x4e2c65(0x1e4))await _0x2ebd11[_0x4e2c65(0x1c7)](),console[_0x4e2c65(0x1d3)]('[Amily-敕令执行官]\x20圣谕:\x20\x22'+_0x28ecae+_0x4e2c65(0x1cd)),toastr[_0x4e2c65(0x1e5)](_0x4e2c65(0x1c1)+_0x28ecae+'\x22\x20已成功颁布','敕令司回报');else{const _0x4902df=_0x4e2c65(0x1db)+_0x28ecae;console[_0x4e2c65(0x1e6)](_0x4e2c65(0x1d8)+_0x4902df),toastr[_0x4e2c65(0x1e6)](_0x4902df,_0x4e2c65(0x1e2));}}catch(_0x3fb9f0){console[_0x4e2c65(0x1e6)](_0x4e2c65(0x1cb)+_0x28ecae+_0x4e2c65(0x1c5),_0x3fb9f0),toastr[_0x4e2c65(0x1e6)](_0x4e2c65(0x1e0)+_0x3fb9f0[_0x4e2c65(0x1c6)],_0x4e2c65(0x1e2));}}export async function executeAutoHide(){const _0x5f18de=_0x58cf;try{const _0x5185f0=extension_settings[extensionName];if(!_0x5185f0[_0x5f18de(0x1c8)])return;const _0x1bd89b=_0x5185f0['autoHideThreshold']||0x1e,_0x4cdc8a=getContext(),_0x22e5ff=_0x4cdc8a['chat']['length'],_0x430d62=_0x22e5ff-_0x1bd89b-0x1;if(_0x430d62<0x0){;return;}const _0x4768bf=_0x5f18de(0x1c3)+_0x430d62;console[_0x5f18de(0x1d3)](_0x5f18de(0x1ca)+_0x4768bf);const _0x1b6e36=new SlashCommandParser(),_0x31a7eb=_0x1b6e36[_0x5f18de(0x1e8)](_0x4768bf,![]);_0x31a7eb&&typeof _0x31a7eb[_0x5f18de(0x1c7)]===_0x5f18de(0x1e4)?(await _0x31a7eb[_0x5f18de(0x1c7)](),console[_0x5f18de(0x1d3)]('[Amily-史册管理员]\x20圣谕颁布成功。')):console[_0x5f18de(0x1e6)](_0x5f18de(0x1e3));}catch(_0x170bc5){console[_0x5f18de(0x1e6)](_0x5f18de(0x1e7),_0x170bc5);}}export async function executeManualCommand(_0xaf2ae3,_0x15bb0a={}){const _0x567b75=_0x58cf,{from:_0x1666d9,to:_0x3462b1}=_0x15bb0a;let _0x5cc3b1='';switch(_0xaf2ae3){case _0x567b75(0x1de):{const _0x6bd434=getContext()[_0x567b75(0x1e1)][_0x567b75(0x1c4)];if(_0x6bd434>0x0){const _0x4dbd57=_0x6bd434-0x1;_0x5cc3b1=_0x567b75(0x1cf)+_0x4dbd57;}else{toastr[_0x567b75(0x1c0)](_0x567b75(0x1ce),'敕令司回报');return;}break;}case _0x567b75(0x1c2):case _0x567b75(0x1d5):{const _0x582643=_0xaf2ae3===_0x567b75(0x1c2)?'/hide':_0x567b75(0x1cc);if(_0x1666d9===''&&_0x3462b1!=='')_0x5cc3b1=_0x582643+'\x20'+_0x3462b1;else{if(_0x1666d9!==''&&_0x3462b1!==''){if(parseInt(_0x1666d9)>parseInt(_0x3462b1)){toastr[_0x567b75(0x1e9)](_0x567b75(0x1d9),_0x567b75(0x1dc));return;}_0x5cc3b1=_0x582643+'\x20'+_0x1666d9+'-'+_0x3462b1;}else{toastr[_0x567b75(0x1e9)]('请输入有效的楼层范围',_0x567b75(0x1dc));return;}}break;}default:console[_0x567b75(0x1e6)](_0x567b75(0x1d4)+_0xaf2ae3);return;}await executeSlashCommand(_0x5cc3b1);}function _0x5992(){const _0x133664=['success','error','[Amily-史册管理员]\x20执行自动隐藏律法时发生意外错误:','parse','warning','8803272zKOyks','info','圣谕\x20\x22','manual_hide','/hide\x200-','length','\x22\x20时发生意外:','message','execute','autoHideEnabled','17XaePWh','[Amily-史册管理员]\x20颁布圣谕:\x20','[Amily-敕令执行官]\x20执行圣谕\x20\x22','/unhide','\x22\x20已成功颁布。','史册为空,无需解除隐藏。','/unhide\x200-','3241588UWNBsl','2333502whztNt','6368380RiIATK','log','[Amily-手动敕令司]\x20未知的命令类型:\x20','manual_unhide','12590440mhbLFH','65756490EvHMoU','[Amily-敕令执行官]\x20','起始层不能大于结束层','3MrmhsI','铸造出的圣谕法印无法执行!指令:\x20','敕令司提示','7TQjRrw','unhide_all','45246UriBuS','执行圣谕时发生意外:\x20','chat','敕令司紧急报告','[Amily-史册管理员]\x20致命错误铸造出的圣谕法印无法执行','function'];_0x5992=function(){return _0x133664;};return _0x5992();} import { getContext, extension_settings } from "/scripts/extensions.js";
import { SlashCommandParser } from "/scripts/slash-commands/SlashCommandParser.js";
import { extensionName } from "../utils/settings.js";
import { readGoldenLedgerProgress } from "./historiographer.js";
import { characters } from "/script.js";
import { getChatIdentifier } from "./lore.js";
async function executeSlashCommand(commandString) {
if (!commandString) return;
try {
console.log(`[Amily-敕令执行官] 准备执行圣谕: ${commandString}`);
const parser = new SlashCommandParser();
const closure = parser.parse(commandString, false);
if (closure && typeof closure.execute === 'function') {
await closure.execute();
console.log(`[Amily-敕令执行官] 圣谕: "${commandString}" 已成功颁布。`);
toastr.success(`圣谕 "${commandString}" 已成功颁布`, "敕令司回报");
} else {
const errorMsg = `铸造出的圣谕法印无法执行!指令: ${commandString}`;
console.error(`[Amily-敕令执行官] ${errorMsg}`);
toastr.error(errorMsg, "敕令司紧急报告");
}
} catch (error) {
console.error(`[Amily-敕令执行官] 执行圣谕 "${commandString}" 时发生意外:`, error);
toastr.error(`执行圣谕时发生意外: ${error.message}`, "敕令司紧急报告");
}
}
export async function executeAutoHide() {
try {
const settings = extension_settings[extensionName];
const context = getContext();
const chatLength = context.chat.length;
let hideUntilIndex = -1;
if (settings.autoHideSummarizedEnabled) {
let targetLorebookName;
switch (settings.lorebookTarget) {
case "character_main":
targetLorebookName = characters[context.characterId]?.data?.extensions?.world;
break;
case "dedicated":
const chatIdentifier = await getChatIdentifier();
targetLorebookName = `Amily2-Lore-${chatIdentifier}`;
break;
}
if (targetLorebookName) {
const summarizedCount = await readGoldenLedgerProgress(targetLorebookName);
if (summarizedCount > 0) {
hideUntilIndex = summarizedCount - 1;
}
}
}
if (settings.autoHideEnabled) {
const threshold = settings.autoHideThreshold || 30;
const thresholdHideIndex = chatLength - threshold - 1;
if (thresholdHideIndex > hideUntilIndex) {
hideUntilIndex = thresholdHideIndex;
}
}
if (hideUntilIndex < 0) {
return;
}
const commandString = `/hide 0-${hideUntilIndex}`;
console.log(`[Amily-史册管理员] 颁布圣谕: ${commandString}`);
const parser = new SlashCommandParser();
const closure = parser.parse(commandString, false);
if (closure && typeof closure.execute === 'function') {
await closure.execute();
console.log(`[Amily-史册管理员] 圣谕颁布成功。`);
} else {
console.error('[Amily-史册管理员] 致命错误:铸造出的圣谕法印无法执行!');
}
} catch (error) {
console.error('[Amily-史册管理员] 执行自动隐藏律法时发生意外错误:', error);
}
}
export async function executeManualCommand(commandType, params = {}) {
const { from, to } = params;
let commandString = '';
switch (commandType) {
case 'unhide_all': {
const chatLength = getContext().chat.length;
if (chatLength > 0) {
const lastIndex = chatLength - 1;
commandString = `/unhide 0-${lastIndex}`;
} else {
toastr.info("史册为空,无需解除隐藏。", "敕令司回报");
return;
}
break;
}
case 'manual_hide':
case 'manual_unhide': {
const command = commandType === 'manual_hide' ? '/hide' : '/unhide';
if (from === '' && to !== '') {
commandString = `${command} ${to}`;
} else if (from !== '' && to !== '') {
if (parseInt(from) > parseInt(to)) {
toastr.warning("起始层不能大于结束层", "敕令司提示");
return;
}
commandString = `${command} ${from}-${to}`;
} else {
toastr.warning("请输入有效的楼层范围", "敕令司提示");
return;
}
break;
}
default:
console.error(`[Amily-手动敕令司] 未知的命令类型: ${commandType}`);
return;
}
await executeSlashCommand(commandString);
}

View File

@@ -16,6 +16,7 @@ import { showSummaryModal, showHtmlModal } from "../ui/page-window.js";
import { getPresetPrompts, getMixedOrder } from '../PresetSettings/index.js'; import { getPresetPrompts, getMixedOrder } from '../PresetSettings/index.js';
import { callAI, generateRandomSeed } from "./api.js"; import { callAI, generateRandomSeed } from "./api.js";
import { callNgmsAI } from "./api/Ngms_api.js"; import { callNgmsAI } from "./api/Ngms_api.js";
import { executeAutoHide } from "./autoHideManager.js";
let isExpeditionRunning = false; let isExpeditionRunning = false;
let manualStopRequested = false; let manualStopRequested = false;
@@ -24,7 +25,7 @@ const RUNNING_LOG_COMMENT = "【敕史局】对话流水总帐";
const PROGRESS_SEAL_REGEX = const PROGRESS_SEAL_REGEX =
/本条勿动【前(\d+)楼总结已完成】否则后续总结无法进行。$/; /本条勿动【前(\d+)楼总结已完成】否则后续总结无法进行。$/;
async function readGoldenLedgerProgress(targetLorebookName) { export async function readGoldenLedgerProgress(targetLorebookName) {
if (!targetLorebookName) return 0; if (!targetLorebookName) return 0;
try { try {
const bookData = await loadWorldInfo(targetLorebookName); const bookData = await loadWorldInfo(targetLorebookName);
@@ -127,10 +128,9 @@ export async function executeManualSummary(startFloor, endFloor, isAuto = false)
onRegenerate: async (summaryDialog) => { onRegenerate: async (summaryDialog) => {
summaryDialog.find('textarea').prop('disabled', true).val('正在重新生成,请稍候...'); summaryDialog.find('textarea').prop('disabled', true).val('正在重新生成,请稍候...');
const newSummary = await getSummary(textToSummarize, toastTitle); const newSummary = await getSummary(textToSummarize, toastTitle);
if (newSummary) { summaryDialog.find('textarea').prop('disabled', false).val(newSummary || summary);
summaryDialog.find('textarea').prop('disabled', false).val(newSummary); summaryDialog[0].showModal(); // 重新显示弹窗
} else { if (!newSummary) {
summaryDialog.find('textarea').prop('disabled', false).val(summary);
toastr.error("重新生成失败,已恢复原始内容。", "模型召唤失败"); toastr.error("重新生成失败,已恢复原始内容。", "模型召唤失败");
} }
}, },
@@ -242,10 +242,9 @@ export async function executeManualSummary(startFloor, endFloor, isAuto = false)
onRegenerate: async (summaryDialog) => { onRegenerate: async (summaryDialog) => {
summaryDialog.find('textarea').prop('disabled', true).val('正在重新生成,请稍候...'); summaryDialog.find('textarea').prop('disabled', true).val('正在重新生成,请稍候...');
const newSummary = await getSummary(textToSummarize, toastTitle); const newSummary = await getSummary(textToSummarize, toastTitle);
if (newSummary) { summaryDialog.find('textarea').prop('disabled', false).val(newSummary || summary);
summaryDialog.find('textarea').prop('disabled', false).val(newSummary); summaryDialog[0].showModal(); // 重新显示弹窗
} else { if (!newSummary) {
summaryDialog.find('textarea').prop('disabled', false).val(summary);
toastr.error("重新生成失败,已恢复原始内容。", "模型召唤失败"); toastr.error("重新生成失败,已恢复原始内容。", "模型召唤失败");
} }
}, },
@@ -454,6 +453,7 @@ async function writeSummary(summary, startFloor, endFloor, toastTitle) {
if (success) { if (success) {
toastr.success(`编年史已成功更新!`, `${toastTitle} - 国史馆`); toastr.success(`编年史已成功更新!`, `${toastTitle} - 国史馆`);
executeAutoHide(); // 总结成功后立即触发自动隐藏
return true; return true;
} else { } else {
// 错误已在 compatibleWriteToLorebook 内部处理和记录 // 错误已在 compatibleWriteToLorebook 内部处理和记录
@@ -618,10 +618,9 @@ export async function executeRefinement(worldbook, loreKey) {
onRegenerate: async (dialog) => { onRegenerate: async (dialog) => {
dialog.find('textarea').prop('disabled', true).val('正在重新生成,请稍候...'); dialog.find('textarea').prop('disabled', true).val('正在重新生成,请稍候...');
const newContent = await getRefinedContent(); const newContent = await getRefinedContent();
if (newContent) { dialog.find('textarea').prop('disabled', false).val(newContent || currentRefinedContent);
dialog.find('textarea').prop('disabled', false).val(newContent); dialog[0].showModal(); // 重新显示弹窗
} else { if (!newContent) {
dialog.find('textarea').prop('disabled', false).val(currentRefinedContent);
toastr.error("重新生成失败,已恢复原始内容。", "模型召唤失败"); toastr.error("重新生成失败,已恢复原始内容。", "模型召唤失败");
} }
}, },

File diff suppressed because one or more lines are too long

View File

@@ -33,8 +33,9 @@ const DEFAULT_AI_FLOW_TEMPLATE = `# dataTable 说明
【说明】: 【说明】:
· [表格用途和规则的说明] · [表格用途和规则的说明]
<[tableName]内容> <[tableName]内容>
rowIndex,[colIndex]:[colName],[colIndex]:[colName],... | rowIndex | [colIndex]:[colName] | [colIndex]:[colName] | ... |
[rowIndex],[单元格数据],[单元格数据],... |---|---|---|---|
| [rowIndex] | [单元格数据] | [单元格数据] | ... |
... ...
</[tableName]内容> </[tableName]内容>
【增加】: · [插入新行的触发条件] 【增加】: · [插入新行的触发条件]
@@ -46,9 +47,9 @@ rowIndex,[colIndex]:[colName],[colIndex]:[colName],...
### 格式解析: ### 格式解析:
- \`* [tableIndex]:[tableName]\`: 表格的标题行,包含表格的索引(\`tableIndex\`)和名称(\`tableName\`)。 - \`* [tableIndex]:[tableName]\`: 表格的标题行,包含表格的索引(\`tableIndex\`)和名称(\`tableName\`)。
- \`【说明】\`: 提供了表格的详细用途和填写规则。 - \`【说明】\`: 提供了表格的详细用途和填写规则。
- \`<[tableName]内容>\`: 包含了表格的实际数据。 - \`<[tableName]内容>\`: 包含了使用 Markdown 格式的实际数据表格
- 第一行是表头定义了每一列的索引 (\`colIndex\`) 和名称 (\`colName\`)。 - 表头定义了每一列的索引 (\`colIndex\`) 和名称 (\`colName\`)。第一列始终是 \`rowIndex\`
- 后续每一行都是一条数据记录,以行索引 (\`rowIndex\`) 开头,后面跟着对应列的单元格数据。 - 后续每一行都是一条数据记录,第一列是该行的索引 (\`rowIndex\`),后面跟着对应列的单元格数据。
- \`【增加】\`, \`【删除】\`, \`【修改】\`: 分别描述了你应该在何种剧情下对表格进行增、删、改操作。 - \`【增加】\`, \`【删除】\`, \`【修改】\`: 分别描述了你应该在何种剧情下对表格进行增、删、改操作。
### 示例: ### 示例:
@@ -57,8 +58,9 @@ rowIndex,[colIndex]:[colName],[colIndex]:[colName],...
【说明】: 【说明】:
(内容省略... (内容省略...
<时空栏内容> <时空栏内容>
rowIndex,0:日期,1:时段,2:时间,3:地点,4:此地角色 | rowIndex | 0:日期 | 1:时段 | 2:时间 | 3:地点 | 4:此地角色 |
0,2025-09-04,下午,18:40,办公室,艾克/克莱因 |---|---|---|---|---|---|
| 0 | 2025-09-04 | 下午 | 18:40 | 办公室 | 艾克/克莱因 |
</时空栏内容> </时空栏内容>
【增加】: · 此表不存在任何一行时 【增加】: · 此表不存在任何一行时
【删除】: · 此表大于一行时应删除多余行 【删除】: · 此表大于一行时应删除多余行

View File

@@ -416,6 +416,9 @@ class AmilyHelper {
existingEntry.position = positionMap[entryUpdate.position] ?? 4; existingEntry.position = positionMap[entryUpdate.position] ?? 4;
} }
if (entryUpdate.depth !== undefined) existingEntry.depth = entryUpdate.depth; if (entryUpdate.depth !== undefined) existingEntry.depth = entryUpdate.depth;
if (entryUpdate.order !== undefined) existingEntry.order = entryUpdate.order;
if (entryUpdate.exclude_recursion !== undefined) existingEntry.excludeRecursion = entryUpdate.exclude_recursion;
if (entryUpdate.prevent_recursion !== undefined) existingEntry.preventRecursion = entryUpdate.prevent_recursion;
} }
} }
await saveWorldInfo(bookName, bookData, true); await saveWorldInfo(bookName, bookData, true);

View File

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

View File

@@ -150,6 +150,7 @@ export function showSummaryModal(summaryText, callbacks) {
const regenerateButton = $('<button class="menu_button secondary interactable" style="margin-right: auto;">重新生成</button>'); const regenerateButton = $('<button class="menu_button secondary interactable" style="margin-right: auto;">重新生成</button>');
regenerateButton.on('click', () => { regenerateButton.on('click', () => {
if (onRegenerate) { if (onRegenerate) {
dialogElement[0].close();
onRegenerate(dialogElement); onRegenerate(dialogElement);
} }
}); });

View File

@@ -132,6 +132,7 @@ export function updateUI() {
$(`input[name="amily2_icon_location"][value="${settings.iconLocation}"]`).prop("checked", true); $(`input[name="amily2_icon_location"][value="${settings.iconLocation}"]`).prop("checked", true);
$("#amily2_auto_hide_enabled").prop("checked", settings.autoHideEnabled); $("#amily2_auto_hide_enabled").prop("checked", settings.autoHideEnabled);
$("#amily2_auto_hide_summarized_enabled").prop("checked", settings.autoHideSummarizedEnabled);
$("#amily2_auto_hide_threshold").val(settings.autoHideThreshold); $("#amily2_auto_hide_threshold").val(settings.autoHideThreshold);
$("#amily2_auto_hide_threshold_value").text(settings.autoHideThreshold); $("#amily2_auto_hide_threshold_value").text(settings.autoHideThreshold);
$('#amily2_lore_activation_mode').val(settings.loreActivationMode); $('#amily2_lore_activation_mode').val(settings.loreActivationMode);