mirror of
https://github.com/SilenceLurker/ST-Amily2-Chat-Optimisation.git
synced 2026-06-06 08:55:50 +00:00
Merge branch 'main' of github.com:SilenceLurker/ST-Amily2-Chat-Optimisation
This commit is contained in:
@@ -3,9 +3,23 @@ import {
|
||||
loadWorldInfo,
|
||||
saveWorldInfo,
|
||||
createNewWorldInfo,
|
||||
createWorldInfoEntry,
|
||||
reloadEditor
|
||||
createWorldInfoEntry
|
||||
} from "/scripts/world-info.js";
|
||||
|
||||
let reloadEditor = () => {
|
||||
console.warn("[Amily助手] reloadEditor 函数不可用,可能是旧版本。已使用空函数代替。");
|
||||
};
|
||||
(async () => {
|
||||
try {
|
||||
const { reloadEditor: importedReloadEditor } = await import("/scripts/world-info.js");
|
||||
if (importedReloadEditor) {
|
||||
reloadEditor = importedReloadEditor;
|
||||
console.log("[Amily助手] 已成功动态导入 reloadEditor。");
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("[Amily助手] 动态导入 reloadEditor 失败,将使用空函数。错误信息:", error.message);
|
||||
}
|
||||
})();
|
||||
import {
|
||||
characters,
|
||||
eventSource,
|
||||
|
||||
@@ -6,21 +6,21 @@ import { saveSettingsDebounced } from "/script.js";
|
||||
let isRendererInitialized = false;
|
||||
|
||||
export function initializeRendererBindings() {
|
||||
const container = $("#amily2_drawer_content").length
|
||||
? $("#amily2_drawer_content")
|
||||
const container = $("#amily2_drawer_content").length
|
||||
? $("#amily2_drawer_content")
|
||||
: $("#amily2_chat_optimiser");
|
||||
|
||||
if (!container.length) {
|
||||
console.warn("[Amily2-Renderer] Could not find the settings container.");
|
||||
return;
|
||||
}
|
||||
container.on('change', '#render-enable-toggle-amily', function () {
|
||||
container.on('change', '#amily-render-enable-toggle', function () {
|
||||
const isChecked = this.checked;
|
||||
|
||||
if (!extension_settings[extensionName]) {
|
||||
extension_settings[extensionName] = {};
|
||||
}
|
||||
extension_settings[extensionName].render_enabled = isChecked;
|
||||
extension_settings[extensionName].amily_render_enabled = isChecked;
|
||||
saveSettingsDebounced();
|
||||
|
||||
if (isChecked && !isRendererInitialized) {
|
||||
@@ -36,7 +36,7 @@ export function initializeRendererBindings() {
|
||||
}
|
||||
});
|
||||
|
||||
container.on('change', '#render-depth', function() {
|
||||
container.on('change', '#render-depth', function () {
|
||||
const depth = parseInt(this.value, 10);
|
||||
if (!extension_settings[extensionName]) {
|
||||
extension_settings[extensionName] = {};
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="extension-content-item">
|
||||
<div class="name">启用前端渲染</div>
|
||||
<div class="description">在聊天消息中渲染HTML内容。</div>
|
||||
<input id="render-enable-toggle-amily" type="checkbox" class="slider">
|
||||
<input id="amily-render-enable-toggle" type="checkbox" class="slider">
|
||||
</div>
|
||||
<div class="extension-content-item">
|
||||
<div class="name">渲染深度</div>
|
||||
|
||||
@@ -16,6 +16,71 @@ const hashToBlobUrl = new Map();
|
||||
const blobLRU = [];
|
||||
const BLOB_CACHE_LIMIT = 32;
|
||||
|
||||
const viewport_adjust_script = `
|
||||
<script>
|
||||
window.addEventListener("message", function (event) {
|
||||
if (event.data && event.data.request === "updateViewportHeight") {
|
||||
const newHeight = event.data.newHeight;
|
||||
document.documentElement.style.setProperty("--viewport-height", newHeight + "px");
|
||||
}
|
||||
});
|
||||
</script>
|
||||
`;
|
||||
|
||||
function processAllVhUnits(htmlContent) {
|
||||
const viewportHeight = window.innerHeight;
|
||||
|
||||
let processedContent = htmlContent.replace(
|
||||
/((?:document\.body\.style\.minHeight|\.style\.minHeight|setProperty\s*\(\s*['"]min-height['"])\s*[=,]\s*['"`])([^'"`]*?)(['"`])/g,
|
||||
(match, prefix, value, suffix) => {
|
||||
if (value.includes('vh')) {
|
||||
const convertedValue = value.replace(/(\d+(?:\.\d+)?)vh/g, (num) => {
|
||||
const numValue = parseFloat(num);
|
||||
if (numValue === 100) {
|
||||
return `var(--viewport-height, ${viewportHeight}px)`;
|
||||
} else {
|
||||
return `calc(var(--viewport-height, ${viewportHeight}px) * ${numValue / 100})`;
|
||||
}
|
||||
});
|
||||
return prefix + convertedValue + suffix;
|
||||
}
|
||||
return match;
|
||||
},
|
||||
);
|
||||
|
||||
processedContent = processedContent.replace(/min-height:\s*([^;]*vh[^;]*);/g, expression => {
|
||||
const processedExpression = expression.replace(/(\d+(?:\.\d+)?)vh/g, num => {
|
||||
const numValue = parseFloat(num);
|
||||
if (numValue === 100) {
|
||||
return `var(--viewport-height, ${viewportHeight}px)`;
|
||||
} else {
|
||||
return `calc(var(--viewport-height, ${viewportHeight}px) * ${numValue / 100})`;
|
||||
}
|
||||
});
|
||||
return `${processedExpression};`;
|
||||
});
|
||||
|
||||
processedContent = processedContent.replace(
|
||||
/style\s*=\s*["']([^"']*min-height:\s*[^"']*vh[^"']*?)["']/gi,
|
||||
(match, styleContent) => {
|
||||
const processedStyleContent = styleContent.replace(/min-height:\s*([^;]*vh[^;]*)/g, (expression) => {
|
||||
const processedExpression = expression.replace(/(\d+(?:\.\d+)?)vh/g, num => {
|
||||
const numValue = parseFloat(num);
|
||||
if (numValue === 100) {
|
||||
return `var(--viewport-height, ${viewportHeight}px)`;
|
||||
} else {
|
||||
return `calc(var(--viewport-height, ${viewportHeight}px) * ${numValue / 100})`;
|
||||
}
|
||||
});
|
||||
return processedExpression;
|
||||
});
|
||||
return match.replace(styleContent, processedStyleContent);
|
||||
},
|
||||
);
|
||||
|
||||
return processedContent;
|
||||
}
|
||||
|
||||
function generateUniqueId() {
|
||||
return `amily2-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
||||
}
|
||||
@@ -144,11 +209,13 @@ function iframeClientScript() {
|
||||
})();`;
|
||||
}
|
||||
|
||||
function buildWrappedHtml(html) {
|
||||
function buildWrappedHtml(html, needsVh) {
|
||||
const origin = (typeof location !== 'undefined' && location.origin) ? location.origin : '';
|
||||
const baseTag = settings && settings.useBlob ? `<base href="${origin}/">` : "";
|
||||
const headHints = buildResourceHints(html);
|
||||
const vhFix = `<style>html,body{height:auto!important;min-height:0!important;max-height:none!important}.profile-container,[style*="100vh"]{height:auto!important;min-height:600px!important}[style*="height:100%"]{height:auto!important;min-height:100%!important}</style>`;
|
||||
const vhStyle = needsVh ? `<style>:root{--viewport-height:${window.innerHeight}px;}</style>` : '';
|
||||
const vhScript = needsVh ? viewport_adjust_script : '';
|
||||
|
||||
const apiScript = `
|
||||
<script>
|
||||
@@ -367,7 +434,9 @@ ${baseTag}
|
||||
<script>${iframeClientScript()}</script>
|
||||
${headHints}
|
||||
${vhFix}
|
||||
${vhStyle}
|
||||
${apiScript}
|
||||
${vhScript}
|
||||
`;
|
||||
|
||||
const isFullHtml = /<html/i.test(html) && /<\/html>/i.test(html);
|
||||
@@ -477,6 +546,17 @@ function releaseIframeBlob(iframe) {
|
||||
|
||||
function renderHtmlInIframe(htmlContent, container, preElement) {
|
||||
try {
|
||||
let processedHtml = htmlContent;
|
||||
let needsVh = false;
|
||||
|
||||
const hasMinVh = /min-height:\s*[^;]*vh/.test(htmlContent);
|
||||
const hasJsVhUsage = /\d+vh/.test(htmlContent);
|
||||
|
||||
if (hasMinVh || hasJsVhUsage) {
|
||||
processedHtml = processAllVhUnits(htmlContent);
|
||||
needsVh = true;
|
||||
}
|
||||
|
||||
const originalHash = djb2(htmlContent);
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.id = generateUniqueId();
|
||||
@@ -490,6 +570,11 @@ function renderHtmlInIframe(htmlContent, container, preElement) {
|
||||
} else {
|
||||
iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms allow-modals allow-popups');
|
||||
}
|
||||
|
||||
if (needsVh) {
|
||||
iframe.dataset.needsVh = 'true';
|
||||
}
|
||||
|
||||
const wrapper = getOrCreateWrapper(preElement);
|
||||
wrapper.querySelectorAll('.amily2-iframe').forEach(old => {
|
||||
try { old.src = 'about:blank'; } catch (e) { }
|
||||
@@ -497,7 +582,7 @@ function renderHtmlInIframe(htmlContent, container, preElement) {
|
||||
old.remove();
|
||||
});
|
||||
const codeHash = djb2(htmlContent);
|
||||
const full = buildWrappedHtml(htmlContent);
|
||||
const full = buildWrappedHtml(processedHtml, needsVh);
|
||||
if (settings.useBlob) {
|
||||
setIframeBlobHTML(iframe, full, codeHash);
|
||||
} else {
|
||||
@@ -517,7 +602,7 @@ function renderHtmlInIframe(htmlContent, container, preElement) {
|
||||
}
|
||||
|
||||
function processCodeBlocks(messageElement) {
|
||||
if (extension_settings[extensionName].render_enabled === false) return;
|
||||
if (extension_settings[extensionName].amily_render_enabled === false) return;
|
||||
try {
|
||||
const codeBlocks = messageElement.querySelectorAll('pre > code');
|
||||
codeBlocks.forEach(codeBlock => {
|
||||
@@ -576,6 +661,19 @@ export function initializeRenderer() {
|
||||
});
|
||||
|
||||
window.addEventListener('message', handleIframeMessage);
|
||||
|
||||
window.addEventListener('resize', function () {
|
||||
const viewportHeight = window.innerHeight;
|
||||
const iframes = document.querySelectorAll('iframe.amily2-iframe');
|
||||
iframes.forEach(iframe => {
|
||||
if (iframe.dataset.needsVh === 'true') {
|
||||
iframe.contentWindow?.postMessage({
|
||||
request: 'updateViewportHeight',
|
||||
newHeight: viewportHeight
|
||||
}, '*');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
console.log('[Amily2-Renderer] 渲染器已初始化,监听事件: MESSAGE_RECEIVED, MESSAGE_UPDATED, MESSAGE_SWIPED, MESSAGE_EDITED, USER_MESSAGE_RENDERED, CHARACTER_MESSAGE_RENDERED, IMPERSONATE_READY');
|
||||
}
|
||||
|
||||
@@ -4,12 +4,25 @@ import {
|
||||
loadWorldInfo,
|
||||
createNewWorldInfo,
|
||||
createWorldInfoEntry,
|
||||
saveWorldInfo,
|
||||
reloadEditor
|
||||
saveWorldInfo
|
||||
} from "/scripts/world-info.js";
|
||||
|
||||
let reloadEditor = () => {
|
||||
console.warn("[Amily助手 - 兼容性] reloadEditor 函数不可用,可能是旧版本。已使用空函数代替。");
|
||||
};
|
||||
(async () => {
|
||||
try {
|
||||
const { reloadEditor: importedReloadEditor } = await import("/scripts/world-info.js");
|
||||
if (importedReloadEditor) {
|
||||
reloadEditor = importedReloadEditor;
|
||||
console.log("[Amily助手 - 兼容性] 已成功动态导入 reloadEditor。");
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("[Amily助手 - 兼容性] 动态导入 reloadEditor 失败,将使用空函数。错误信息:", error.message);
|
||||
}
|
||||
})();
|
||||
import { refreshWorldbookListOnly } from './lore.js';
|
||||
|
||||
// 检查我们自己的 amilyHelper 是否存在
|
||||
export function isTavernHelperAvailable() {
|
||||
return typeof amilyHelper !== 'undefined' && amilyHelper !== null;
|
||||
}
|
||||
@@ -37,7 +50,6 @@ export async function safeUpdateLorebookEntries(bookName, entries) {
|
||||
export async function compatibleWriteToLorebook(targetLorebookName, entryComment, contentUpdateCallback, options = {}) {
|
||||
console.log('[兼容写入模块] 接收到的写入选项:', options);
|
||||
|
||||
// 优先使用 AmilyHelper
|
||||
if (isTavernHelperAvailable()) {
|
||||
try {
|
||||
console.log('[兼容写入模块] 检测到 AmilyHelper,优先使用新逻辑...');
|
||||
@@ -65,9 +77,8 @@ export async function compatibleWriteToLorebook(targetLorebookName, entryComment
|
||||
}
|
||||
console.log(`[Amily助手] 成功将条目 "${entryComment}" 写入《${targetLorebookName}》。`);
|
||||
|
||||
// 派发被证明有效的自定义刷新事件
|
||||
document.dispatchEvent(new CustomEvent('amily-lorebook-created', { detail: { bookName: targetLorebookName } }));
|
||||
refreshWorldbookListOnly(); // 刷新UI
|
||||
refreshWorldbookListOnly();
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(`[Amily助手] 写入失败,将尝试回退到传统逻辑。错误:`, error);
|
||||
@@ -75,7 +86,6 @@ export async function compatibleWriteToLorebook(targetLorebookName, entryComment
|
||||
}
|
||||
}
|
||||
|
||||
// AmilyHelper 不可用或失败时的后备传统逻辑
|
||||
try {
|
||||
console.log('[兼容写入模块] AmilyHelper 不可用或失败,使用传统逻辑...');
|
||||
let bookData = await loadWorldInfo(targetLorebookName);
|
||||
@@ -116,10 +126,7 @@ export async function compatibleWriteToLorebook(targetLorebookName, entryComment
|
||||
await saveWorldInfo(targetLorebookName, bookData, true);
|
||||
console.log(`[传统逻辑] 成功将条目 "${entryComment}" 写入《${targetLorebookName}》。`);
|
||||
|
||||
// 刷新编辑器(如果正在查看)
|
||||
reloadEditor(targetLorebookName);
|
||||
|
||||
// 派发被证明有效的自定义刷新事件
|
||||
document.dispatchEvent(new CustomEvent('amily-lorebook-created', { detail: { bookName: targetLorebookName } }));
|
||||
return true;
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user