mirror of
https://github.com/Wx-2025/ST-Amily2-Chat-Optimisation.git
synced 2026-06-06 03:25:51 +00:00
Initial commit with CC BY-NC-ND 4.0 license
This commit is contained in:
320
MiZheSi/index.js
Normal file
320
MiZheSi/index.js
Normal file
@@ -0,0 +1,320 @@
|
||||
import { eventSource, event_types, main_api, stopGeneration } from '/script.js';
|
||||
import { renderExtensionTemplateAsync } from '/scripts/extensions.js';
|
||||
import { POPUP_RESULT, POPUP_TYPE, Popup } from '/scripts/popup.js';
|
||||
import { t } from '/scripts/i18n.js';
|
||||
import { extensionName } from '../utils/settings.js';
|
||||
import { getTokenCountAsync } from '/scripts/tokenizers.js';
|
||||
|
||||
window.MiZheSi_Global = {
|
||||
isEnabled: () => inspectEnabled,
|
||||
};
|
||||
|
||||
const miZheSiPath = `third-party/${extensionName}/MiZheSi`;
|
||||
const STORAGE_KEY = 'amily2_miZheSiEnabled';
|
||||
|
||||
if (!('GENERATE_AFTER_COMBINE_PROMPTS' in event_types) || !('CHAT_COMPLETION_PROMPT_READY' in event_types)) {
|
||||
toastr.error('【密折司】错误:您的SillyTavern版本过旧,缺少必要的事件支持。请更新至最新版本。');
|
||||
throw new Error('【密折司】缺少必要的事件支持。');
|
||||
}
|
||||
|
||||
let inspectEnabled = false;
|
||||
|
||||
function addLaunchButton() {
|
||||
const enabledText = '关闭Amliy2号密折司';
|
||||
const disabledText = '开启Amliy2号密折司';
|
||||
const iconClass = 'fa-solid fa-scroll';
|
||||
|
||||
const getText = () => inspectEnabled ? enabledText : disabledText;
|
||||
|
||||
const launchButton = document.createElement('div');
|
||||
launchButton.id = 'miZheSiLaunchButton';
|
||||
launchButton.classList.add('list-group-item', 'flex-container', 'flexGap5', 'interactable');
|
||||
launchButton.tabIndex = 0;
|
||||
launchButton.title = '切换【密折司】状态';
|
||||
|
||||
const icon = document.createElement('i');
|
||||
icon.className = iconClass;
|
||||
launchButton.appendChild(icon);
|
||||
|
||||
const textSpan = document.createElement('span');
|
||||
textSpan.textContent = getText();
|
||||
launchButton.appendChild(textSpan);
|
||||
|
||||
const extensionsMenu = document.getElementById('extensionsMenu');
|
||||
if (!extensionsMenu) {
|
||||
console.error('【密折司】无法找到左下角扩展菜单 (extensionsMenu)。');
|
||||
return;
|
||||
}
|
||||
|
||||
if (document.getElementById(launchButton.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
extensionsMenu.appendChild(launchButton);
|
||||
launchButton.addEventListener('click', () => {
|
||||
toggleInspectNext();
|
||||
textSpan.textContent = getText();
|
||||
launchButton.classList.toggle('active', inspectEnabled);
|
||||
});
|
||||
|
||||
launchButton.classList.toggle('active', inspectEnabled);
|
||||
}
|
||||
|
||||
function toggleInspectNext() {
|
||||
inspectEnabled = !inspectEnabled;
|
||||
toastr.info(`【密折司】已${inspectEnabled ? '开启' : '关闭'}`);
|
||||
localStorage.setItem(STORAGE_KEY, String(inspectEnabled));
|
||||
}
|
||||
|
||||
async function showPromptInspector(input) {
|
||||
const template = $(await renderExtensionTemplateAsync(miZheSiPath, 'template'));
|
||||
const container = template.find('#mizhesi-editor-container');
|
||||
let isJsonMode = false;
|
||||
|
||||
const titleHeader = template.find('.mizhesi-header h3');
|
||||
const charCountDisplay = $('<span id="mizhesi-char-count" style="font-size: 14px; color: #FFD700; margin-left: 15px; font-weight: normal;"></span>');
|
||||
titleHeader.append(charCountDisplay);
|
||||
|
||||
const updateTotalCharCount = async () => {
|
||||
let totalTokens = 0;
|
||||
let totalChars = 0;
|
||||
if (isJsonMode) {
|
||||
const textareas = template.find('.mizhesi-message-textarea');
|
||||
for (const textarea of textareas) {
|
||||
const text = $(textarea).val();
|
||||
totalTokens += await getTokenCountAsync(text);
|
||||
totalChars += text.length;
|
||||
}
|
||||
} else {
|
||||
const text = template.find('#mizhesi-plain-text-editor').val();
|
||||
totalTokens = await getTokenCountAsync(text);
|
||||
totalChars = text.length;
|
||||
}
|
||||
charCountDisplay.text(`(总 ${totalTokens} Tokens / ${totalChars} 字)`);
|
||||
};
|
||||
|
||||
try {
|
||||
const chat = JSON.parse(input);
|
||||
if (Array.isArray(chat)) {
|
||||
isJsonMode = true;
|
||||
container.empty(); // 清空容器
|
||||
for (const message of chat) {
|
||||
const block = $(`
|
||||
<div class="mizhesi-message-block" data-role="${message.role}">
|
||||
<div class="mizhesi-message-header">
|
||||
<span class="mizhesi-injection-icons" style="display: inline-flex; gap: 5px; margin-right: 10px; align-items: center;"></span>
|
||||
<span class="mizhesi-line-char-count" style="font-weight: normal; color: #FFD700; margin-right: 10px;"></span>
|
||||
<span class="mizhesi-role">${message.role}</span>
|
||||
</div>
|
||||
<div class="mizhesi-message-content">
|
||||
<textarea class="mizhesi-message-textarea"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
let content = message.content;
|
||||
const iconsContainer = block.find('.mizhesi-injection-icons');
|
||||
|
||||
// 【V11.0 升级】支持多种注入来源标记
|
||||
const injectionMarkers = {
|
||||
'%%HANLINYUAN_RAG_NOVEL%%': {
|
||||
icon: 'fa-book-open',
|
||||
title: '翰林院注入 (小说)',
|
||||
color: '#66ccff'
|
||||
},
|
||||
'%%HANLINYUAN_RAG_CHAT%%': {
|
||||
icon: 'fa-comments',
|
||||
title: '翰林院注入 (聊天记录)',
|
||||
color: '#66ccff'
|
||||
},
|
||||
'%%HANLINYUAN_RAG_LOREBOOK%%': {
|
||||
icon: 'fa-atlas',
|
||||
title: '翰林院注入 (世界书)',
|
||||
color: '#66ccff'
|
||||
},
|
||||
'%%HANLINYUAN_RAG_MANUAL%%': {
|
||||
icon: 'fa-pencil-alt',
|
||||
title: '翰林院注入 (手动)',
|
||||
color: '#66ccff'
|
||||
},
|
||||
'%%AMILY2_TABLE_INJECTION%%': {
|
||||
icon: 'fa-table-cells',
|
||||
title: '表格系统注入',
|
||||
color: '#99cc33'
|
||||
}
|
||||
};
|
||||
|
||||
for (const marker in injectionMarkers) {
|
||||
if (content.includes(marker)) {
|
||||
content = content.replace(marker, '');
|
||||
const details = injectionMarkers[marker];
|
||||
iconsContainer.append(`<i class="fa-solid ${details.icon}" title="${details.title}" style="color: ${details.color};"></i>`);
|
||||
}
|
||||
}
|
||||
|
||||
const textarea = block.find('textarea');
|
||||
textarea.val(content);
|
||||
container.append(block);
|
||||
|
||||
const lineCharCountDisplay = block.find('.mizhesi-line-char-count');
|
||||
const updateLineCharCount = async () => {
|
||||
const text = textarea.val();
|
||||
const lineTokens = await getTokenCountAsync(text);
|
||||
const lineChars = text.length;
|
||||
lineCharCountDisplay.text(`(${lineTokens} Tokens / ${lineChars} 字)`);
|
||||
};
|
||||
|
||||
await updateLineCharCount(); // 初始化行字数
|
||||
textarea.on('input', async () => {
|
||||
await updateLineCharCount();
|
||||
await updateTotalCharCount();
|
||||
});
|
||||
|
||||
block.find('.mizhesi-message-header').on('click', function(e) {
|
||||
if ($(e.target).is('.mizhesi-line-char-count, .mizhesi-injection-icons, .mizhesi-injection-icons *')) {
|
||||
e.stopPropagation(); // 防止点击字数或图标时折叠
|
||||
return;
|
||||
}
|
||||
const content = $(this).siblings('.mizhesi-message-content');
|
||||
const parentBlock = $(this).closest('.mizhesi-message-block');
|
||||
parentBlock.toggleClass('expanded');
|
||||
content.slideToggle('fast');
|
||||
});
|
||||
}
|
||||
} else {
|
||||
throw new Error("Input is not a chat array.");
|
||||
}
|
||||
} catch (e) {
|
||||
isJsonMode = false;
|
||||
const textArea = $('<textarea id="mizhesi-plain-text-editor" style="width: 100%; height: 100%; box-sizing: border-box;"></textarea>');
|
||||
textArea.val(input);
|
||||
container.empty().append(textArea);
|
||||
textArea.on('input', async () => await updateTotalCharCount());
|
||||
}
|
||||
|
||||
await updateTotalCharCount(); // 初始化总字数
|
||||
|
||||
const searchInput = template.find('#mizhesi-search-input');
|
||||
const searchButton = template.find('#mizhesi-search-button');
|
||||
const clearButton = template.find('#mizhesi-clear-button');
|
||||
|
||||
const performSearch = () => {
|
||||
const searchTerm = searchInput.val().trim();
|
||||
if (!searchTerm) return;
|
||||
|
||||
clearHighlights();
|
||||
|
||||
let firstMatch = null;
|
||||
const textareas = template.find('.mizhesi-message-textarea, #mizhesi-plain-text-editor');
|
||||
|
||||
textareas.each(function() {
|
||||
const textarea = $(this);
|
||||
const content = textarea.val();
|
||||
const regex = new RegExp(searchTerm.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'), 'gi');
|
||||
|
||||
if (regex.test(content)) {
|
||||
textarea.addClass('mizhesi-highlight-border');
|
||||
if (!firstMatch) {
|
||||
firstMatch = textarea;
|
||||
}
|
||||
|
||||
const block = textarea.closest('.mizhesi-message-block');
|
||||
if (block.length && !block.hasClass('expanded')) {
|
||||
block.addClass('expanded');
|
||||
block.find('.mizhesi-message-content').slideDown('fast');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (firstMatch) {
|
||||
firstMatch[0].scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
} else {
|
||||
toastr.info('【密折司】未找到匹配项。');
|
||||
}
|
||||
};
|
||||
|
||||
const clearHighlights = () => {
|
||||
template.find('.mizhesi-highlight-border').removeClass('mizhesi-highlight-border');
|
||||
};
|
||||
|
||||
searchButton.on('click', performSearch);
|
||||
searchInput.on('keypress', (e) => {
|
||||
if (e.which === 13) { // Enter key
|
||||
performSearch();
|
||||
}
|
||||
});
|
||||
clearButton.on('click', clearHighlights);
|
||||
|
||||
|
||||
const customButton = {
|
||||
text: '取消生成',
|
||||
result: POPUP_RESULT.CANCELLED,
|
||||
appendAtEnd: true,
|
||||
action: async () => {
|
||||
await stopGeneration();
|
||||
await popup.complete(POPUP_RESULT.CANCELLED);
|
||||
},
|
||||
};
|
||||
|
||||
const popup = new Popup(template, POPUP_TYPE.CONFIRM, '', {
|
||||
wide: true,
|
||||
large: true,
|
||||
okButton: '确认修改',
|
||||
cancelButton: '放弃修改',
|
||||
customButtons: [customButton]
|
||||
});
|
||||
|
||||
const result = await popup.show();
|
||||
|
||||
if (!result) {
|
||||
return input; // 用户取消,返回原始输入
|
||||
}
|
||||
|
||||
if (isJsonMode) {
|
||||
const newChat = [];
|
||||
template.find('.mizhesi-message-block').each(function() {
|
||||
const role = $(this).data('role');
|
||||
const content = $(this).find('textarea').val();
|
||||
newChat.push({ role, content });
|
||||
});
|
||||
return JSON.stringify(newChat, null, 4);
|
||||
} else {
|
||||
return template.find('#mizhesi-plain-text-editor').val();
|
||||
}
|
||||
}
|
||||
|
||||
function isChatCompletion() {
|
||||
return main_api === 'openai';
|
||||
}
|
||||
|
||||
eventSource.on(event_types.GENERATE_AFTER_COMBINE_PROMPTS, async (data) => {
|
||||
if (!inspectEnabled || data.dryRun || isChatCompletion()) return;
|
||||
if (typeof data.prompt !== 'string') return;
|
||||
|
||||
const result = await showPromptInspector(data.prompt);
|
||||
if (result !== data.prompt) {
|
||||
data.prompt = result;
|
||||
console.log('【密折司】奏章已按御笔修改 (Text Gen)。');
|
||||
}
|
||||
});
|
||||
|
||||
eventSource.on(event_types.CHAT_COMPLETION_PROMPT_READY, async (data) => {
|
||||
if (!inspectEnabled || data.dryRun || !isChatCompletion()) return;
|
||||
if (!Array.isArray(data.chat)) return;
|
||||
|
||||
const originalJson = JSON.stringify(data.chat, null, 4);
|
||||
const resultJson = await showPromptInspector(originalJson);
|
||||
|
||||
if (resultJson === originalJson) return;
|
||||
|
||||
try {
|
||||
const modifiedChat = JSON.parse(resultJson);
|
||||
data.chat.splice(0, data.chat.length, ...modifiedChat);
|
||||
console.log('【密折司】奏章已按御笔修改 (Chat Completion)。');
|
||||
} catch (e) {
|
||||
console.error('【密折司】解析修改后的JSON奏章失败:', e);
|
||||
toastr.error('【密折司】解析JSON失败,本次修改未生效。');
|
||||
}
|
||||
});
|
||||
|
||||
addLaunchButton();
|
||||
123
MiZheSi/template.html
Normal file
123
MiZheSi/template.html
Normal file
@@ -0,0 +1,123 @@
|
||||
<div class="mizhesi-container">
|
||||
<div class="mizhesi-header" style="justify-content: center;">
|
||||
<i class="fas fa-scroll"></i>
|
||||
<h3>密折司奏报</h3>
|
||||
</div>
|
||||
<div class="mizhesi-search-container">
|
||||
<input type="text" id="mizhesi-search-input" placeholder="搜索内容...">
|
||||
<button id="mizhesi-search-button" class="menu_button" title="搜索"><i class="fa-solid fa-magnifying-glass"></i></button>
|
||||
<button id="mizhesi-clear-button" class="menu_button" title="清除高亮"><i class="fa-solid fa-xmark"></i></button>
|
||||
</div>
|
||||
<div id="mizhesi-editor-container" class="mizhesi-editor-container">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
.mizhesi-container {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
color: #ddd;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
.mizhesi-header {
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #555;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.mizhesi-header i {
|
||||
font-size: 1.5em;
|
||||
color: #d4af37;
|
||||
}
|
||||
.mizhesi-header h3 {
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
.mizhesi-editor-container {
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.mizhesi-message-block {
|
||||
border: 1px solid #444;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 15px;
|
||||
background-color: #2b2d31;
|
||||
}
|
||||
.mizhesi-message-header {
|
||||
padding: 8px 12px;
|
||||
background-color: #3a3d42;
|
||||
border-top-left-radius: 7px;
|
||||
border-top-right-radius: 7px;
|
||||
font-weight: bold;
|
||||
font-size: 0.9em;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
.mizhesi-message-header::after {
|
||||
content: '▶';
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
transition: transform 0.2s ease-in-out;
|
||||
}
|
||||
.mizhesi-message-block.expanded .mizhesi-message-header::after {
|
||||
transform: translateY(-50%) rotate(90deg);
|
||||
}
|
||||
.mizhesi-message-content {
|
||||
padding: 12px;
|
||||
display: none;
|
||||
}
|
||||
.mizhesi-message-content textarea {
|
||||
width: 100%;
|
||||
min-height: 80px;
|
||||
height: auto;
|
||||
background-color: #202225;
|
||||
color: #ccc;
|
||||
border: 1px solid #555;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
font-size: 0.95em;
|
||||
line-height: 1.5;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.mizhesi-message-block[data-role="system"] .mizhesi-message-header {
|
||||
background-color: #5865f2;
|
||||
color: white;
|
||||
}
|
||||
.mizhesi-message-block[data-role="user"] .mizhesi-message-header {
|
||||
background-color: #57f287;
|
||||
color: #060607;
|
||||
}
|
||||
.mizhesi-message-block[data-role="assistant"] .mizhesi-message-header {
|
||||
background-color: #f25757;
|
||||
color: white;
|
||||
}
|
||||
.mizhesi-highlight {
|
||||
background-color: rgb(235, 7, 185);
|
||||
color: black;
|
||||
}
|
||||
.mizhesi-search-container {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.mizhesi-search-container input {
|
||||
flex-grow: 1;
|
||||
background-color: #202225;
|
||||
border: 1px solid #555;
|
||||
color: #ccc;
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.mizhesi-highlight-border {
|
||||
border: 2px solid #ff00dd !important;
|
||||
box-shadow: 0 0 10px #ff00c8;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user