ci: auto build & obfuscate [2026-04-06 19:02:37] (Jenkins #10)

This commit is contained in:
Jenkins CI
2026-04-06 19:02:37 +08:00
parent 0421e44e0f
commit 0c5ac2c70b
13 changed files with 694 additions and 82 deletions

542
SL/module/SfiGenModule.js Normal file
View File

@@ -0,0 +1,542 @@
import { Module, ModuleBuilder } from './Module.js';
import { extension_settings, getContext } from '../../../../../extensions.js';
import { saveSettingsDebounced, saveChat, reloadCurrentChat, eventSource, event_types } from '../../../../../../script.js';
import { registerSlashCommand } from '../../../../../slash-commands.js';
const extensionName = 'ST-Amily2-Chat-Optimisation-Dev'; // Use main extension name for settings
const sfigenSettingsKey = 'sfigen_settings';
const defaultSettings = {
api_key: '',
model: 'Qwen/Qwen-Image',
negative_prompt: '模糊, 低分辨率, 水印, 文字',
image_size: '1664x928',
steps: 50,
cfg: 4.0,
regex_tag: 'sfigen',
prefix_prompt: ''
};
const builder = new ModuleBuilder()
.name('SfiGen')
.view('assets/siliconflow-image-gen.html')
.strict(true)
.required(['mount']);
export default class SfiGenModule extends Module {
constructor() {
super(builder);
this.settings = {};
}
async init(ctx = {}) {
await super.init(ctx);
this._loadSettings();
return this;
}
_loadSettings() {
if (!extension_settings[extensionName]) {
extension_settings[extensionName] = {};
}
if (!extension_settings[extensionName][sfigenSettingsKey]) {
extension_settings[extensionName][sfigenSettingsKey] = { ...defaultSettings };
}
this.settings = extension_settings[extensionName][sfigenSettingsKey];
// Ensure all default keys exist
for (const key in defaultSettings) {
if (!(key in this.settings)) {
this.settings[key] = defaultSettings[key];
}
}
}
_saveSettings() {
extension_settings[extensionName][sfigenSettingsKey] = this.settings;
saveSettingsDebounced();
}
async mount() {
if (this.el) {
this.el.id = 'amily2_sfigen_panel';
this.el.style.display = 'none';
}
this._bindUI();
this._registerSlashCommand();
this._bindEvents();
this._bindButtonsGlobal();
}
_bindUI() {
const $el = $(this.el);
// Bind inputs
$el.find('#sfigen_api_key').val(this.settings.api_key).on('input', (e) => {
this.settings.api_key = $(e.target).val();
this._saveSettings();
});
$el.find('#sfigen_model').val(this.settings.model).on('input', (e) => {
this.settings.model = $(e.target).val();
this._saveSettings();
});
$el.find('#sfigen_negative_prompt').val(this.settings.negative_prompt).on('input', (e) => {
this.settings.negative_prompt = $(e.target).val();
this._saveSettings();
});
$el.find('#sfigen_image_size').val(this.settings.image_size).on('change', (e) => {
this.settings.image_size = $(e.target).val();
this._saveSettings();
});
$el.find('#sfigen_steps').val(this.settings.steps).on('input', (e) => {
this.settings.steps = $(e.target).val();
this._saveSettings();
});
$el.find('#sfigen_cfg').val(this.settings.cfg).on('input', (e) => {
this.settings.cfg = $(e.target).val();
this._saveSettings();
});
$el.find('#sfigen_regex_tag').val(this.settings.regex_tag).on('input', (e) => {
this.settings.regex_tag = $(e.target).val();
this._saveSettings();
});
$el.find('#sfigen_prefix_prompt').val(this.settings.prefix_prompt).on('input', (e) => {
this.settings.prefix_prompt = $(e.target).val();
this._saveSettings();
});
// Bind style tags
$el.find('.sfigen-style-tag').on('click', (e) => {
const promptToAdd = $(e.target).data('prompt');
const textarea = $el.find('#sfigen_prefix_prompt');
let currentVal = textarea.val().trim();
if (currentVal) {
if (!currentVal.endsWith(',')) {
currentVal += ', ';
} else {
currentVal += ' ';
}
textarea.val(currentVal + promptToAdd);
} else {
textarea.val(promptToAdd);
}
textarea.trigger('input');
$(e.target).css('opacity', '0.5');
setTimeout(() => $(e.target).css('opacity', '1'), 200);
});
// Bind back button
$el.find('#amily2_sfigen_back_to_main').on('click', () => {
$el.hide();
$('#amily2_chat_optimiser > .plugin-features').show();
});
}
async _generateImage(prompt) {
let finalPrompt = prompt;
if (this.settings.prefix_prompt && this.settings.prefix_prompt.trim() !== '') {
finalPrompt = `${this.settings.prefix_prompt.trim()}, ${prompt}`;
}
console.log(`[SfiGen] 开始生成图片,最终提示词:`, finalPrompt);
if (!this.settings.api_key) {
console.warn(`[SfiGen] 未配置 API Key`);
toastr.error('请先在扩展设置中配置 SiliconFlow API Key');
return null;
}
const url = 'https://api.siliconflow.cn/v1/images/generations';
const headers = {
'Authorization': `Bearer ${this.settings.api_key}`,
'Content-Type': 'application/json'
};
const body = {
model: this.settings.model,
prompt: finalPrompt,
negative_prompt: this.settings.negative_prompt,
image_size: this.settings.image_size,
seed: Math.floor(Math.random() * 1000000000),
num_inference_steps: parseInt(this.settings.steps),
cfg: parseFloat(this.settings.cfg)
};
try {
toastr.info('正在生成图片,请稍候...');
const response = await fetch(url, {
method: 'POST',
headers: headers,
body: JSON.stringify(body)
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (data.images && data.images.length > 0) {
toastr.success('图片生成成功!');
return data.images[0].url;
} else {
throw new Error('API 返回数据中没有图片 URL');
}
} catch (error) {
console.error(`[SfiGen] 生成图片失败:`, error);
toastr.error(`生成图片失败: ${error.message}`);
return null;
}
}
_escapeHtml(unsafe) {
return (unsafe || '').replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
_processMessageDOM(messageId) {
const messageElement = $(`.mes[mesid="${messageId}"] .mes_text`);
if (!messageElement.length) return;
// 检查是否已经处理过,如果已经有容器,说明已经处理过了,直接返回
if (messageElement.find('.sfigen-image-container').length > 0) {
return;
}
let html = messageElement.html();
const tag = this.settings.regex_tag || 'sfigen';
let newHtml = html;
let hasMatch = false;
// 1. 匹配 [tag: prompt]
const regexPrompt = new RegExp(`\\[${tag}:\\s*([^\\]]+)\\]`, 'gi');
newHtml = newHtml.replace(regexPrompt, (match, prompt) => {
hasMatch = true;
const buttonId = `sfigen-btn-${messageId}-${Math.random().toString(36).substr(2, 9)}`;
const safePrompt = this._escapeHtml(prompt);
const safeMatch = this._escapeHtml(match);
return `<div class="sfigen-image-container" data-message-id="${messageId}" data-prompt="${safePrompt}" data-original-tag="${safeMatch}" style="width: 96%; max-width: 600px; background: var(--SmartThemeBlurTintColor); border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); overflow: hidden; margin: 20px auto; padding: 15px; text-align: center; position: relative; z-index: 10;"><button id="${buttonId}" class="sfigen-generate-btn" style="background-color: var(--SmartThemeQuoteColor); color: var(--SmartThemeBodyColor); border: 1px solid var(--SmartThemeBorderColor); padding: 8px 20px; border-radius: 8px; cursor: pointer; pointer-events: auto; display: inline-block; font-weight: bold; transition: all 0.2s;"><i class="fa-solid fa-image"></i> 生成图片</button></div>`;
});
// 2. 匹配 [tag_img: prompt | url1,url2]
const regexImg = new RegExp(`\\[${tag}_img:\\s*([^\\]]+)\\]`, 'gi');
newHtml = newHtml.replace(regexImg, (match, content) => {
hasMatch = true;
let prompt = "未知提示词";
let imageList = [];
if (content.includes('|')) {
const parts = content.split('|');
prompt = parts[0].trim();
imageList = parts[1].split(',').map(u => u.trim());
} else {
imageList = content.split(',').map(u => u.trim());
}
const displayUrl = imageList[imageList.length - 1];
const buttonId = `sfigen-btn-${messageId}-${Math.random().toString(36).substr(2, 9)}`;
const safePrompt = this._escapeHtml(prompt);
const safeMatch = this._escapeHtml(match);
let navHtml = '';
if (imageList.length > 1) {
navHtml = `<div style="display: flex; justify-content: center; gap: 10px; margin-top: 10px;">`;
imageList.forEach((url, index) => {
const isActive = index === imageList.length - 1;
navHtml += `<button class="sfigen-nav-btn" data-url="${this._escapeHtml(url)}" style="width: 12px; height: 12px; border-radius: 50%; border: none; background-color: ${isActive ? 'var(--SmartThemeQuoteColor)' : 'var(--SmartThemeBorderColor)'}; cursor: pointer; padding: 0;"></button>`;
});
navHtml += `</div>`;
}
return `<div class="sfigen-image-container" data-message-id="${messageId}" data-prompt="${safePrompt}" data-original-tag="${safeMatch}" data-urls="${this._escapeHtml(imageList.join(','))}" style="width: 96%; max-width: 600px; background: var(--SmartThemeBlurTintColor); border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); overflow: hidden; margin: 20px auto; padding: 15px; text-align: center; position: relative; z-index: 10;">
<div style="width: calc(100% - 4px); margin: 2px auto 15px auto; border: 2px solid rgba(0,0,0,0.15); border-radius: 8px; overflow: hidden; position: relative; cursor: pointer;" class="sfigen-img-wrapper">
<img src="${this._escapeHtml(displayUrl)}" class="sfigen-display-img" style="width: 100%; display: block; transition: transform 0.3s;" alt="CG" title="点击放大">
<div class="sfigen-img-overlay" style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.3); display: flex; justify-content: center; align-items: center; opacity: 0; transition: opacity 0.2s;">
<i class="fa-solid fa-magnifying-glass-plus" style="color: white; font-size: 2em;"></i>
</div>
</div>
${navHtml}
<div style="display: flex; justify-content: center; gap: 10px; margin-top: 15px;">
<button id="${buttonId}" class="sfigen-generate-btn" style="background-color: var(--SmartThemeQuoteColor); color: var(--SmartThemeBodyColor); border: 1px solid var(--SmartThemeBorderColor); padding: 8px 20px; border-radius: 8px; cursor: pointer; pointer-events: auto; display: inline-block; font-weight: bold; transition: all 0.2s;"><i class="fa-solid fa-rotate-right"></i> 再次生成</button>
<button class="sfigen-save-btn" data-url="${this._escapeHtml(displayUrl)}" style="background-color: var(--SmartThemeQuoteColor); color: var(--SmartThemeBodyColor); border: 1px solid var(--SmartThemeBorderColor); padding: 8px 20px; border-radius: 8px; cursor: pointer; pointer-events: auto; display: inline-block; font-weight: bold; transition: all 0.2s;"><i class="fa-solid fa-download"></i> 保存图片</button>
</div>
</div>`;
});
if (hasMatch) {
messageElement.html(newHtml);
}
}
_bindEvents() {
const handleMessageRender = (messageId) => {
setTimeout(() => this._processMessageDOM(messageId), 50);
};
eventSource.on(event_types.USER_MESSAGE_RENDERED, handleMessageRender);
eventSource.on(event_types.CHARACTER_MESSAGE_RENDERED, handleMessageRender);
eventSource.on(event_types.MESSAGE_UPDATED, handleMessageRender);
eventSource.on(event_types.MESSAGE_EDITED, handleMessageRender);
eventSource.on(event_types.MESSAGE_SWIPED, handleMessageRender);
eventSource.on(event_types.CHAT_CHANGED, () => {
setTimeout(() => {
$('.mes').each((_, el) => {
const messageId = $(el).attr('mesid');
if (messageId) {
this._processMessageDOM(messageId);
}
});
}, 500);
});
// Initial processing
setTimeout(() => {
$('.mes').each((_, el) => {
const messageId = $(el).attr('mesid');
if (messageId) {
this._processMessageDOM(messageId);
}
});
}, 1000);
}
_bindButtonsGlobal() {
$(document).off('click', '.sfigen-generate-btn');
$(document).on('click', '.sfigen-generate-btn', async (e) => {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
const btn = $(e.currentTarget);
const container = btn.closest('.sfigen-image-container');
const prompt = container.data('prompt');
const messageId = container.data('message-id');
const originalTag = container.data('original-tag');
btn.prop('disabled', true);
btn.html('<i class="fa-solid fa-spinner fa-spin"></i> 生成中...');
const imageUrl = await this._generateImage(prompt);
if (imageUrl) {
const tag = this.settings.regex_tag || 'sfigen';
let existingUrls = container.data('urls') ? String(container.data('urls')).split(',') : [];
existingUrls.push(imageUrl);
const urlsString = existingUrls.join(',');
const newTag = `[${tag}_img: ${prompt} | ${urlsString}]`;
const context = getContext();
const chat = context.chat;
if (chat && chat[messageId]) {
const message = chat[messageId];
// Fix: Use a more robust replacement strategy
// Sometimes originalTag might have been modified by markdown parser
// So we replace the whole tag block in the original message
const regexPrompt = new RegExp(`\\[${tag}:\\s*([^\\]]+)\\]`, 'gi');
const regexImg = new RegExp(`\\[${tag}_img:\\s*([^\\]]+)\\]`, 'gi');
let replaced = false;
// Try exact match first
if (message.mes.includes(originalTag)) {
message.mes = message.mes.replace(originalTag, newTag);
replaced = true;
}
// If not found, try regex replacement
else {
message.mes = message.mes.replace(regexImg, (match, content) => {
if (content.includes(prompt)) {
replaced = true;
return newTag;
}
return match;
});
if (!replaced) {
message.mes = message.mes.replace(regexPrompt, (match, p) => {
if (p.trim() === prompt.trim()) {
replaced = true;
return newTag;
}
return match;
});
}
}
if (replaced) {
await saveChat();
// 立即在前端替换 DOM显示生成的图片
let navHtml = '';
if (existingUrls.length > 1) {
navHtml = `<div style="display: flex; justify-content: center; gap: 10px; margin-top: 10px;">`;
existingUrls.forEach((url, index) => {
const isActive = index === existingUrls.length - 1;
navHtml += `<button class="sfigen-nav-btn" data-url="${this._escapeHtml(url)}" style="width: 12px; height: 12px; border-radius: 50%; border: none; background-color: ${isActive ? 'var(--SmartThemeQuoteColor)' : 'var(--SmartThemeBorderColor)'}; cursor: pointer; padding: 0;"></button>`;
});
navHtml += `</div>`;
}
const newButtonId = `sfigen-btn-${messageId}-${Math.random().toString(36).substr(2, 9)}`;
const safePrompt = this._escapeHtml(prompt);
const safeNewTag = this._escapeHtml(newTag);
const safeUrlsString = this._escapeHtml(urlsString);
const safeImageUrl = this._escapeHtml(imageUrl);
const finalHtml = `<div class="sfigen-image-container" data-message-id="${messageId}" data-prompt="${safePrompt}" data-original-tag="${safeNewTag}" data-urls="${safeUrlsString}" style="width: 96%; max-width: 600px; background: var(--SmartThemeBlurTintColor); border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); overflow: hidden; margin: 20px auto; padding: 15px; text-align: center; position: relative; z-index: 10;">
<div style="width: calc(100% - 4px); margin: 2px auto 15px auto; border: 2px solid rgba(0,0,0,0.15); border-radius: 8px; overflow: hidden; position: relative; cursor: pointer;" class="sfigen-img-wrapper">
<img src="${safeImageUrl}" class="sfigen-display-img" style="width: 100%; display: block; transition: transform 0.3s;" alt="CG" title="点击放大">
<div class="sfigen-img-overlay" style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.3); display: flex; justify-content: center; align-items: center; opacity: 0; transition: opacity 0.2s;">
<i class="fa-solid fa-magnifying-glass-plus" style="color: white; font-size: 2em;"></i>
</div>
</div>
${navHtml}
<div style="display: flex; justify-content: center; gap: 10px; margin-top: 15px;">
<button id="${newButtonId}" class="sfigen-generate-btn" style="background-color: var(--SmartThemeQuoteColor); color: var(--SmartThemeBodyColor); border: 1px solid var(--SmartThemeBorderColor); padding: 8px 20px; border-radius: 8px; cursor: pointer; pointer-events: auto; display: inline-block; font-weight: bold; transition: all 0.2s;"><i class="fa-solid fa-rotate-right"></i> 再次生成</button>
<button class="sfigen-save-btn" data-url="${safeImageUrl}" style="background-color: var(--SmartThemeQuoteColor); color: var(--SmartThemeBodyColor); border: 1px solid var(--SmartThemeBorderColor); padding: 8px 20px; border-radius: 8px; cursor: pointer; pointer-events: auto; display: inline-block; font-weight: bold; transition: all 0.2s;"><i class="fa-solid fa-download"></i> 保存图片</button>
</div>
</div>`;
container.replaceWith(finalHtml);
} else {
console.warn(`[SfiGen] Could not find tag to replace in message ${messageId}`);
toastr.warning('图片已生成,但无法保存到聊天记录中。');
}
}
} else {
btn.prop('disabled', false);
btn.html('<i class="fa-solid fa-image"></i> 重新生成');
}
});
// Image hover and zoom
$(document).on('mouseenter', '.sfigen-img-wrapper', function() {
$(this).find('.sfigen-img-overlay').css('opacity', '1');
$(this).find('.sfigen-display-img').css('transform', 'scale(1.02)');
}).on('mouseleave', '.sfigen-img-wrapper', function() {
$(this).find('.sfigen-img-overlay').css('opacity', '0');
$(this).find('.sfigen-display-img').css('transform', 'scale(1)');
});
$(document).on('click', '.sfigen-img-wrapper', function(e) {
e.stopPropagation();
const imgUrl = $(this).find('img').attr('src');
const overlay = $(`
<div id="sfigen-zoom-overlay" style="position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0,0,0,0.9); z-index: 9999; display: flex; justify-content: center; align-items: center; cursor: zoom-out; opacity: 0; transition: opacity 0.3s;">
<img src="${imgUrl}" style="max-width: 95%; max-height: 95%; object-fit: contain; border-radius: 8px; box-shadow: 0 0 20px rgba(0,0,0,0.5); transform: scale(0.9); transition: transform 0.3s;">
<div style="position: absolute; top: 20px; right: 20px; color: white; font-size: 24px; cursor: pointer;"><i class="fa-solid fa-xmark"></i></div>
</div>
`);
$('body').append(overlay);
setTimeout(() => {
overlay.css('opacity', '1');
overlay.find('img').css('transform', 'scale(1)');
}, 10);
overlay.on('click', function() {
overlay.css('opacity', '0');
overlay.find('img').css('transform', 'scale(0.9)');
setTimeout(() => overlay.remove(), 300);
});
});
// Save image
$(document).on('click', '.sfigen-save-btn', async function(e) {
e.preventDefault();
e.stopPropagation();
const url = $(this).data('url');
try {
const response = await fetch(url);
const blob = await response.blob();
const downloadUrl = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = downloadUrl;
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
a.download = `sfigen_${timestamp}.png`;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(downloadUrl);
document.body.removeChild(a);
toastr.success('图片已保存到默认下载目录');
} catch (error) {
console.error(`[SfiGen] 保存图片失败:`, error);
toastr.error('保存图片失败');
}
});
// Nav buttons
$(document).on('click', '.sfigen-nav-btn', function(e) {
e.preventDefault();
e.stopPropagation();
const btn = $(this);
const container = btn.closest('.sfigen-image-container');
const targetUrl = btn.data('url');
container.find('.sfigen-display-img').attr('src', targetUrl);
container.find('.sfigen-save-btn').data('url', targetUrl);
container.find('.sfigen-nav-btn').css('background-color', 'var(--SmartThemeBorderColor)');
btn.css('background-color', 'var(--SmartThemeQuoteColor)');
});
}
_registerSlashCommand() {
registerSlashCommand('sfigen', async (args, value) => {
if (!value) {
toastr.warning('请提供提示词。例如: /sfigen 一个可爱的猫咪');
return;
}
const imageUrl = await this._generateImage(value);
if (imageUrl) {
const context = getContext();
const message = `<img src="${imageUrl}" alt="Generated Image" style="max-width: 100%; border-radius: 8px;" />`;
context.chat.push({
name: 'System',
is_user: false,
is_system: true,
mes: message,
send_date: Date.now(),
});
await saveChat();
if (typeof window.updateChat === 'function') {
window.updateChat();
} else if (typeof window.updateMessageBlock === 'function') {
window.updateMessageBlock(context.chat.length - 1, context.chat[context.chat.length - 1]);
} else {
await reloadCurrentChat();
}
}
}, [], '使用 SiliconFlow 生成图片', true, true);
}
}

View File

@@ -20,6 +20,7 @@ import GlossaryModule from './GlossaryModule.js';
import RendererModule from './RendererModule.js';
import SuperMemoryModule from './SuperMemoryModule.js';
import ApiConfigModule from './ApiConfigModule.js';
import SfiGenModule from './SfiGenModule.js';
export function registerAllModules() {
registry.register('AdditionalFeatures', () => new AdditionalFeaturesModule());
@@ -33,4 +34,5 @@ export function registerAllModules() {
registry.register('Renderer', () => new RendererModule());
registry.register('SuperMemory', () => new SuperMemoryModule());
registry.register('ApiConfig', () => new ApiConfigModule());
registry.register('SfiGen', () => new SfiGenModule());
}

View File

@@ -64,6 +64,11 @@
- **分步填表上下文丢失修复**:修复了 `core/table-system/secondary-filler.js``getHistoryContext` 函数的切片索引错误Off-by-one error确保紧挨着目标楼层的那条关键历史消息能够被正确提取并发送给 AI提供完整的上下文因果关系。
以下为更新内容:
- **硅基生图模块集成**
- 在“附加功能”面板中新增“硅基生图”入口,与“前端渲染”按钮平行排列。
- 支持在聊天消息中通过 `[sfigen: 提示词]` 标签一键生成图片,并支持多张图片切换、放大预览和保存到本地。
- 修复了编辑消息后生图 UI 重复渲染或消失的问题,确保 DOM 更新的稳定性。
- 修复了图片 URL 无法正确保存到聊天记录的问题。
- **自动构建器优化**
- **多会话管理**:支持创建、切换和删除多个独立的构建会话,方便用户同时进行多个角色的构建任务。
- **状态持久化**:动态规则、聊天记录和任务状态现在会保存在本地存储中,刷新页面或关闭窗口后不会丢失。

View File

@@ -223,7 +223,10 @@
<button id="amily2_open_text_optimization" class="menu_button wide_button"><i class="fas fa-cogs"></i> 正文优化</button>
<button id="amily2_open_world_editor" class="menu_button wide_button"><i class="fas fa-globe"></i> 世界编辑</button>
<button id="amily2_open_glossary" class="menu_button wide_button"><i class="fas fa-book"></i> 术语表单</button>
</div>
<div class="button-group" style="display: flex; justify-content: space-between; gap: 8px; margin-top: 8px;">
<button id="amily2_open_renderer" class="menu_button wide_button"><i class="fas fa-paint-brush"></i> 前端渲染</button>
<button id="amily2_open_sfigen" class="menu_button wide_button"><i class="fas fa-image"></i> 硅基生图</button>
</div>
</fieldset>

View File

@@ -74,15 +74,14 @@
</div>
</fieldset>
<!-- 新建/编辑 Profile 弹窗 -->
<div id="amily2_profile_modal" class="amily2-modal-overlay" style="display:none;">
<div class="amily2-modal-content">
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:14px;">
<strong id="amily2_profile_modal_title"><i class="fas fa-key"></i> 新建连接配置</strong>
<button id="amily2_profile_modal_close" class="menu_button small_button secondary interactable"></button>
</div>
<!-- 新建/编辑 Profile 表单details 折叠) -->
<details id="amily2_profile_form_details" class="settings-group amily2-profile-form">
<summary>
<i id="amily2_profile_form_icon" class="fas fa-plus"></i>
<span id="amily2_profile_modal_title">新建连接配置</span>
</summary>
<div style="padding-top:10px;">
<!-- 类型选择 -->
<div class="amily2_settings_block">
<label for="amily2_pf_type">配置类型</label>
@@ -111,7 +110,7 @@
<label for="amily2_pf_url">API 地址</label>
<input id="amily2_pf_url" type="text" class="text_pole" placeholder="https://api.example.com/v1" />
</div>
<!-- Google 专属提示(选 Google 时显示) -->
<!-- Google 专属提示 -->
<div id="amily2_pf_google_note" style="display:none; margin-bottom:8px;">
<small class="notes" style="display:block; padding:6px 10px; background:var(--black10a); border-radius:4px; border-left:3px solid #4285f4;">
<i class="fas fa-info-circle" style="color:#4285f4;"></i>
@@ -125,13 +124,11 @@
<small class="notes">留空则不修改现有 Key。</small>
</div>
<!-- 模型选择(带获取按钮) -->
<!-- 模型选择 -->
<div class="amily2_settings_block">
<label for="amily2_pf_model">模型</label>
<div style="display:flex; gap:6px; align-items:stretch;">
<input id="amily2_pf_model" type="text" class="text_pole"
placeholder="手动填写或点击「获取」"
style="flex:1;" />
<input id="amily2_pf_model" type="text" class="text_pole" placeholder="手动填写或点击「获取」" style="flex:1;" />
<select id="amily2_pf_model_select" class="text_pole" style="flex:1; display:none;"></select>
<button id="amily2_pf_fetch_models" class="menu_button small_button interactable" type="button" title="从 API 获取可用模型列表(需先填写地址和 Key">
<i class="fas fa-list"></i> 获取
@@ -147,7 +144,7 @@
<span id="amily2_pf_test_result" style="font-size:0.85em;"></span>
</div>
<!-- Chat 高级参数(折叠) -->
<!-- Chat 高级参数 -->
<div id="amily2_pf_chat_params">
<details class="amily2_advanced_section" style="margin-top:4px;">
<summary style="cursor:pointer; font-size:0.88em; color:var(--SmartThemeQuoteColor); user-select:none; padding:4px 0;">
@@ -166,7 +163,7 @@
</details>
</div>
<!-- Embedding 高级参数(折叠) -->
<!-- Embedding 高级参数 -->
<div id="amily2_pf_embedding_params" style="display:none;">
<details class="amily2_advanced_section" style="margin-top:4px;">
<summary style="cursor:pointer; font-size:0.88em; color:var(--SmartThemeQuoteColor); user-select:none; padding:4px 0;">
@@ -208,11 +205,13 @@
</div>
<!-- 操作按钮 -->
<div style="display:flex; gap:8px; margin-top:16px; justify-content:flex-end;">
<button id="amily2_profile_modal_cancel" class="menu_button secondary interactable">取消</button>
<div style="display:flex; gap:8px; margin-top:16px;">
<button id="amily2_profile_modal_cancel" class="menu_button secondary interactable">
<i class="fas fa-times"></i> 取消
</button>
<button id="amily2_profile_modal_save" class="menu_button interactable">
<i class="fas fa-save"></i> 保存
</button>
</div>
</div>
</div>
</details>

View File

@@ -0,0 +1,77 @@
<div class="amily2-header">
<button id="amily2_sfigen_back_to_main" class="menu_button secondary small_button interactable">
<i class="fas fa-arrow-left"></i> 返回主殿
</button>
<div class="additional-features-title interactable" title="SiliconFlow Image Gen">
<i class="fas fa-image"></i> 硅基流动生图
</div>
</div>
<hr class="header-divider">
<fieldset class="settings-group">
<legend><i class="fas fa-cog"></i> 基础配置</legend>
<div class="flex-container">
<label for="sfigen_api_key">API Key (Bearer Token):</label>
<input id="sfigen_api_key" class="text_pole" type="password" placeholder="sk-..." />
</div>
<div class="flex-container">
<label for="sfigen_model">Model (模型):</label>
<input id="sfigen_model" class="text_pole" type="text" value="Qwen/Qwen-Image" />
</div>
<div class="flex-container">
<label for="sfigen_negative_prompt">Negative Prompt (反向提示词):</label>
<input id="sfigen_negative_prompt" class="text_pole" type="text" value="模糊, 低分辨率, 水印, 文字" />
</div>
<div class="flex-container">
<label for="sfigen_image_size">Image Size (分辨率):</label>
<select id="sfigen_image_size" class="text_pole">
<option value="1024x1024">1024x1024</option>
<option value="512x1024">512x1024</option>
<option value="768x512">768x512</option>
<option value="768x1024">768x1024</option>
<option value="1024x576">1024x576</option>
<option value="576x1024">576x1024</option>
<option value="1664x928" selected>1664x928</option>
</select>
</div>
<div class="flex-container">
<label for="sfigen_steps">Steps (步数):</label>
<input id="sfigen_steps" class="text_pole" type="number" value="50" min="1" max="100" />
</div>
<div class="flex-container">
<label for="sfigen_cfg">CFG Scale:</label>
<input id="sfigen_cfg" class="text_pole" type="number" value="4.0" step="0.1" min="1.0" max="20.0" />
</div>
<div class="flex-container">
<label for="sfigen_regex_tag">触发标签 (Tag):</label>
<input id="sfigen_regex_tag" class="text_pole" type="text" value="sfigen" title="例如填入 sfigen则会抓取 [sfigen: 提示词] 标签" />
</div>
</fieldset>
<fieldset class="settings-group">
<legend><i class="fas fa-paint-brush"></i> 风格预设</legend>
<div class="flex-container" style="flex-direction: column; align-items: flex-start;">
<label for="sfigen_prefix_prompt">固定前缀提示词 (Prefix Prompt):</label>
<div id="sfigen_style_tags" style="display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0;">
<span class="sfigen-style-tag" data-prompt="masterpiece, best quality, high detail anime art, sharp line art, 8K, ultra HD" style="background: var(--SmartThemeQuoteColor); color: var(--SmartThemeBodyColor); padding: 4px 12px; border-radius: 16px; cursor: pointer; font-size: 0.85em; border: 1px solid var(--SmartThemeBorderColor); user-select: none; transition: opacity 0.2s;">日系高清二次元</span>
<span class="sfigen-style-tag" data-prompt="doujinshi style, illustration, vibrant colors, detailed background, pixiv" style="background: var(--SmartThemeQuoteColor); color: var(--SmartThemeBodyColor); padding: 4px 12px; border-radius: 16px; cursor: pointer; font-size: 0.85em; border: 1px solid var(--SmartThemeBorderColor); user-select: none; transition: opacity 0.2s;">同人插画风</span>
<span class="sfigen-style-tag" data-prompt="ancient chinese style, hanfu, traditional clothes, ink painting style, wuxia" style="background: var(--SmartThemeQuoteColor); color: var(--SmartThemeBodyColor); padding: 4px 12px; border-radius: 16px; cursor: pointer; font-size: 0.85em; border: 1px solid var(--SmartThemeBorderColor); user-select: none; transition: opacity 0.2s;">古风</span>
<span class="sfigen-style-tag" data-prompt="photorealistic, realistic, RAW photo, 8k uhd, dslr, soft lighting, high quality" style="background: var(--SmartThemeQuoteColor); color: var(--SmartThemeBodyColor); padding: 4px 12px; border-radius: 16px; cursor: pointer; font-size: 0.85em; border: 1px solid var(--SmartThemeBorderColor); user-select: none; transition: opacity 0.2s;">写实摄影</span>
<span class="sfigen-style-tag" data-prompt="cyberpunk style, neon lights, futuristic, sci-fi, dark city" style="background: var(--SmartThemeQuoteColor); color: var(--SmartThemeBodyColor); padding: 4px 12px; border-radius: 16px; cursor: pointer; font-size: 0.85em; border: 1px solid var(--SmartThemeBorderColor); user-select: none; transition: opacity 0.2s;">赛博朋克</span>
<span class="sfigen-style-tag" data-prompt="watercolor painting, soft edges, artistic, brush strokes" style="background: var(--SmartThemeQuoteColor); color: var(--SmartThemeBodyColor); padding: 4px 12px; border-radius: 16px; cursor: pointer; font-size: 0.85em; border: 1px solid var(--SmartThemeBorderColor); user-select: none; transition: opacity 0.2s;">水彩画</span>
<span class="sfigen-style-tag" data-prompt="clear skin texture, obvious body contour, soft warm dim lamp shadow" style="background: var(--SmartThemeQuoteColor); color: var(--SmartThemeBodyColor); padding: 4px 12px; border-radius: 16px; cursor: pointer; font-size: 0.85em; border: 1px solid var(--SmartThemeBorderColor); user-select: none; transition: opacity 0.2s;">质感光影</span>
<span class="sfigen-style-tag" data-prompt="1girl, solo, beautiful face, detailed eyes" style="background: var(--SmartThemeQuoteColor); color: var(--SmartThemeBodyColor); padding: 4px 12px; border-radius: 16px; cursor: pointer; font-size: 0.85em; border: 1px solid var(--SmartThemeBorderColor); user-select: none; transition: opacity 0.2s;">单人特写</span>
</div>
<textarea id="sfigen_prefix_prompt" class="text_pole" rows="3" placeholder="点击上方标签快速插入,或在此手动输入..." style="width: 100%; box-sizing: border-box;"></textarea>
</div>
</fieldset>
<fieldset class="settings-group">
<legend><i class="fas fa-info-circle"></i> 使用说明</legend>
<small>
<b>仅需填入硅基流动密钥0.3元(赠金亦可,模型默认)一张图。</b><br><br>
<b>使用方法 1</b> 在聊天框输入 <code>/sfigen 你的提示词</code><br>
<b>使用方法 2</b> 让 AI 在回复中输出 <code>[sfigen: 生图提示词]</code>,插件会自动将其替换为生图按钮。<br>
<b>固定前缀:</b> 每次生成时,会自动将“固定前缀提示词”加在您的提示词前面,以保证画风统一。
</small>
</fieldset>

View File

@@ -752,56 +752,23 @@ hr.header-divider {
}
}
/* === Profile 弹窗 === */
.amily2-modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.6);
z-index: 9999;
/* === Profile 表单details 折叠) === */
.amily2-profile-form > summary {
cursor: pointer;
user-select: none;
list-style: none;
display: flex;
align-items: center;
justify-content: center;
padding: 12px;
box-sizing: border-box;
gap: 6px;
font-weight: bold;
}
.amily2-modal-content {
background: var(--SmartThemeBlurTintColor);
border: 1px solid var(--SmartThemeBorderColor);
border-radius: 8px;
padding: 20px;
width: min(500px, 100%);
max-height: 90dvh;
overflow-y: auto;
box-sizing: border-box;
-webkit-overflow-scrolling: touch;
.amily2-profile-form > summary::-webkit-details-marker {
display: none;
}
@media (max-width: 600px) {
.amily2-modal-overlay {
align-items: flex-end;
padding: 0;
}
.amily2-modal-content {
width: 100%;
max-height: 92dvh;
border-radius: 12px 12px 0 0;
padding: 16px 14px;
}
/* 按钮行在窄屏下撑满宽度 */
.amily2-modal-content > div:last-child {
flex-wrap: wrap;
}
.amily2-modal-content > div:last-child .menu_button {
flex: 1;
min-width: 80px;
}
/* 模型获取按钮行 */
.amily2-modal-content #amily2_pf_fetch_models {
white-space: nowrap;
}
.amily2-profile-form[open] > summary {
margin-bottom: 4px;
padding-bottom: 8px;
border-bottom: 1px solid var(--SmartThemeBorderColor);
}

View File

@@ -80,11 +80,8 @@ export function bindApiConfigPanel(container) {
// 弹窗:测试连接
$c.find('#amily2_pf_test_conn').on('click', () => _testConnection($c));
// 弹窗:关闭
$c.find('#amily2_profile_modal_close, #amily2_profile_modal_cancel').on('click', () => closeModal($c));
$c.find('#amily2_profile_modal').on('click', function (e) {
if (e.target === this) closeModal($c);
});
// 表单:取消
$c.find('#amily2_profile_modal_cancel').on('click', () => closeModal($c));
// 弹窗:保存
$c.find('#amily2_profile_modal_save').on('click', () => saveProfile($c));
@@ -329,13 +326,13 @@ export function renderSlotAssignments($c) {
async function openModal($c, id) {
_editingId = id;
const $modal = $c.find('#amily2_profile_modal');
if (id) {
// 编辑模式
const p = apiProfileManager.getProfile(id);
if (!p) return;
$c.find('#amily2_profile_modal_title').html('<i class="fas fa-edit"></i> 编辑连接配置');
$c.find('#amily2_profile_modal_title').text('编辑连接配置');
$c.find('#amily2_profile_form_icon').attr('class', 'fas fa-edit');
$c.find('#amily2_pf_type').val(p.type).prop('disabled', true); // 不允许修改类型
$c.find('#amily2_pf_name').val(p.name);
$c.find('#amily2_pf_provider').val(p.provider);
@@ -357,7 +354,8 @@ async function openModal($c, id) {
_handleProviderChange($c, p.provider);
} else {
// 新建模式
$c.find('#amily2_profile_modal_title').html('<i class="fas fa-plus"></i> 新建连接配置');
$c.find('#amily2_profile_modal_title').text('新建连接配置');
$c.find('#amily2_profile_form_icon').attr('class', 'fas fa-plus');
$c.find('#amily2_pf_type').val('chat').prop('disabled', false);
$c.find('#amily2_pf_name, #amily2_pf_url, #amily2_pf_key, #amily2_pf_model').val('');
$c.find('#amily2_pf_provider').val('openai');
@@ -376,11 +374,13 @@ async function openModal($c, id) {
$c.find('#amily2_pf_model_select').hide().empty();
$c.find('#amily2_pf_model').show();
$modal.css('display', 'flex');
const $details = $c.find('#amily2_profile_form_details');
$details.prop('open', true);
$details[0].scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
function closeModal($c) {
$c.find('#amily2_profile_modal').hide();
$c.find('#amily2_profile_form_details').prop('open', false);
$c.find('#amily2_pf_type').prop('disabled', false);
_editingId = null;
}
@@ -442,9 +442,14 @@ async function saveProfile($c) {
async function _fetchModels($c) {
const apiUrl = $c.find('#amily2_pf_url').val().trim();
const apiKey = $c.find('#amily2_pf_key').val().trim();
const provider = $c.find('#amily2_pf_provider').val();
// 编辑模式下 Key 不回显,字段为空时从 ApiKeyStore 读取已存储的 Key
let apiKey = $c.find('#amily2_pf_key').val().trim();
if (!apiKey && _editingId) {
apiKey = await apiProfileManager.getKey(_editingId) ?? '';
}
if (!apiUrl) { toastr.warning('请先填写 API 地址。'); return; }
const $btn = $c.find('#amily2_pf_fetch_models').prop('disabled', true);
@@ -497,7 +502,8 @@ async function _fetchModels($c) {
}
const rawData = await resp.json();
// ST 返回原始数组或包含 data/models 字段的对象
const list = Array.isArray(rawData) ? rawData : (rawData.data ?? rawData.models ?? []);
const rawList = Array.isArray(rawData) ? rawData : (rawData.data ?? rawData.models ?? []);
const list = Array.isArray(rawList) ? rawList : [];
models = list.map(m => m.id ?? m.name ?? m).filter(m => typeof m === 'string' && m);
}
@@ -523,9 +529,14 @@ async function _fetchModels($c) {
async function _testConnection($c) {
const apiUrl = $c.find('#amily2_pf_url').val().trim();
const apiKey = $c.find('#amily2_pf_key').val().trim();
const provider = $c.find('#amily2_pf_provider').val();
// 编辑模式下 Key 不回显,字段为空时从 ApiKeyStore 读取已存储的 Key
let apiKey = $c.find('#amily2_pf_key').val().trim();
if (!apiKey && _editingId) {
apiKey = await apiProfileManager.getKey(_editingId) ?? '';
}
if (!apiUrl) { toastr.warning('请先填写 API 地址。'); return; }
const $btn = $c.find('#amily2_pf_test_conn').prop('disabled', true);

View File

@@ -801,7 +801,7 @@ export function bindModalEvents() {
container
.off("click.amily2.chamber_nav")
.on("click.amily2.chamber_nav",
"#amily2_open_text_optimization, #amily2_open_plot_optimization, #amily2_open_additional_features, #amily2_open_rag_palace, #amily2_open_memorisation_forms, #amily2_open_character_world_book, #amily2_open_world_editor, #amily2_open_glossary, #amily2_open_renderer, #amily2_open_super_memory, #amily2_open_auto_char_card, #amily2_open_api_config, #amily2_back_to_main_settings, #amily2_back_to_main_from_hanlinyuan, #amily2_back_to_main_from_forms, #amily2_back_to_main_from_optimization, #amily2_back_to_main_from_text_optimization, #amily2_back_to_main_from_cwb, #amily2_back_to_main_from_world_editor, #amily2_back_to_main_from_glossary, #amily2_renderer_back_button, #amily2_back_to_main_from_super_memory, #amily2_back_to_main_from_api_config", function () {
"#amily2_open_text_optimization, #amily2_open_plot_optimization, #amily2_open_additional_features, #amily2_open_rag_palace, #amily2_open_memorisation_forms, #amily2_open_character_world_book, #amily2_open_world_editor, #amily2_open_glossary, #amily2_open_renderer, #amily2_open_super_memory, #amily2_open_auto_char_card, #amily2_open_api_config, #amily2_open_sfigen, #amily2_back_to_main_settings, #amily2_back_to_main_from_hanlinyuan, #amily2_back_to_main_from_forms, #amily2_back_to_main_from_optimization, #amily2_back_to_main_from_text_optimization, #amily2_back_to_main_from_cwb, #amily2_back_to_main_from_world_editor, #amily2_back_to_main_from_glossary, #amily2_renderer_back_button, #amily2_back_to_main_from_super_memory, #amily2_back_to_main_from_api_config, #amily2_sfigen_back_to_main", function () {
if (!pluginAuthStatus.authorized) return;
const mainPanel = container.find('.plugin-features');
@@ -816,6 +816,7 @@ export function bindModalEvents() {
const rendererPanel = container.find('#amily2_renderer_panel');
const superMemoryPanel = container.find('#amily2_super_memory_panel');
const apiConfigPanel = container.find('#amily2_api_config_panel');
const sfigenPanel = container.find('#amily2_sfigen_panel');
mainPanel.hide();
additionalPanel.hide();
@@ -829,6 +830,7 @@ export function bindModalEvents() {
rendererPanel.hide();
superMemoryPanel.hide();
apiConfigPanel.hide();
sfigenPanel.hide();
switch (this.id) {
case 'amily2_open_text_optimization':
@@ -876,6 +878,9 @@ export function bindModalEvents() {
case 'amily2_open_api_config':
apiConfigPanel.show();
break;
case 'amily2_open_sfigen':
sfigenPanel.show();
break;
case 'amily2_back_to_main_settings':
case 'amily2_back_to_main_from_hanlinyuan':
case 'amily2_back_to_main_from_forms':
@@ -887,6 +892,7 @@ export function bindModalEvents() {
case 'amily2_renderer_back_button':
case 'amily2_back_to_main_from_super_memory':
case 'amily2_back_to_main_from_api_config':
case 'amily2_sfigen_back_to_main':
mainPanel.show();
break;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
const a0_0x464d5d=a0_0x575b;function a0_0x575b(_0x145d02,_0x900b5a){_0x145d02=_0x145d02-0x113;const _0x3236cf=a0_0x3236();let _0x575b3a=_0x3236cf[_0x145d02];if(a0_0x575b['jUrepA']===undefined){var _0x4e696d=function(_0x1fd176){const _0x1d0631='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0xf58097='',_0x23a574='';for(let _0x4fea97=0x0,_0x2d057d,_0x44da1a,_0x163df7=0x0;_0x44da1a=_0x1fd176['charAt'](_0x163df7++);~_0x44da1a&&(_0x2d057d=_0x4fea97%0x4?_0x2d057d*0x40+_0x44da1a:_0x44da1a,_0x4fea97++%0x4)?_0xf58097+=String['fromCharCode'](0xff&_0x2d057d>>(-0x2*_0x4fea97&0x6)):0x0){_0x44da1a=_0x1d0631['indexOf'](_0x44da1a);}for(let _0x3fe03b=0x0,_0x2a166b=_0xf58097['length'];_0x3fe03b<_0x2a166b;_0x3fe03b++){_0x23a574+='%'+('00'+_0xf58097['charCodeAt'](_0x3fe03b)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x23a574);};const _0x3aec01=function(_0x576625,_0x383ac5){let _0x2f6ea6=[],_0x396013=0x0,_0x206c5f,_0x1b66c6='';_0x576625=_0x4e696d(_0x576625);let _0x5007b3;for(_0x5007b3=0x0;_0x5007b3<0x100;_0x5007b3++){_0x2f6ea6[_0x5007b3]=_0x5007b3;}for(_0x5007b3=0x0;_0x5007b3<0x100;_0x5007b3++){_0x396013=(_0x396013+_0x2f6ea6[_0x5007b3]+_0x383ac5['charCodeAt'](_0x5007b3%_0x383ac5['length']))%0x100,_0x206c5f=_0x2f6ea6[_0x5007b3],_0x2f6ea6[_0x5007b3]=_0x2f6ea6[_0x396013],_0x2f6ea6[_0x396013]=_0x206c5f;}_0x5007b3=0x0,_0x396013=0x0;for(let _0x1548ef=0x0;_0x1548ef<_0x576625['length'];_0x1548ef++){_0x5007b3=(_0x5007b3+0x1)%0x100,_0x396013=(_0x396013+_0x2f6ea6[_0x5007b3])%0x100,_0x206c5f=_0x2f6ea6[_0x5007b3],_0x2f6ea6[_0x5007b3]=_0x2f6ea6[_0x396013],_0x2f6ea6[_0x396013]=_0x206c5f,_0x1b66c6+=String['fromCharCode'](_0x576625['charCodeAt'](_0x1548ef)^_0x2f6ea6[(_0x2f6ea6[_0x5007b3]+_0x2f6ea6[_0x396013])%0x100]);}return _0x1b66c6;};a0_0x575b['rfsTIp']=_0x3aec01,a0_0x575b['LSIvJu']={},a0_0x575b['jUrepA']=!![];}const _0x3ce10d=_0x3236cf[0x0],_0xc32439=_0x145d02+_0x3ce10d,_0x272228=a0_0x575b['LSIvJu'][_0xc32439];return!_0x272228?(a0_0x575b['CNmtar']===undefined&&(a0_0x575b['CNmtar']=!![]),_0x575b3a=a0_0x575b['rfsTIp'](_0x575b3a,_0x900b5a),a0_0x575b['LSIvJu'][_0xc32439]=_0x575b3a):_0x575b3a=_0x272228,_0x575b3a;}(function(_0x88d337,_0x39c146){const _0x32eabe=a0_0x575b,_0x102283=_0x88d337();while(!![]){try{const _0x180c20=-parseInt(_0x32eabe(0x11d,'qtUX'))/0x1*(parseInt(_0x32eabe(0x11c,'uh6d'))/0x2)+parseInt(_0x32eabe(0x11e,'CUEQ'))/0x3*(-parseInt(_0x32eabe(0x115,'xYG4'))/0x4)+-parseInt(_0x32eabe(0x12e,'xYG4'))/0x5+-parseInt(_0x32eabe(0x12c,'YCrk'))/0x6*(parseInt(_0x32eabe(0x126,'WQkW'))/0x7)+parseInt(_0x32eabe(0x123,'g$19'))/0x8+-parseInt(_0x32eabe(0x124,'0)Sx'))/0x9*(parseInt(_0x32eabe(0x128,'2Fg&'))/0xa)+parseInt(_0x32eabe(0x118,'2Fg&'))/0xb;if(_0x180c20===_0x39c146)break;else _0x102283['push'](_0x102283['shift']());}catch(_0x1fe01f){_0x102283['push'](_0x102283['shift']());}}}(a0_0x3236,0x1c926));export const SENSITIVE_KEYS=new Set([a0_0x464d5d(0x12d,'DLFS'),a0_0x464d5d(0x120,'CUEQ'),a0_0x464d5d(0x119,'F4K0'),a0_0x464d5d(0x114,'EnbL'),a0_0x464d5d(0x121,'cJ6F'),a0_0x464d5d(0x129,'Ek2%'),a0_0x464d5d(0x12a,'qtUX')]);function a0_0x3236(){const _0x10edce=['WPyFkSk4d2m','WPlcRxBcV8kapmk0WRtcLrpdIIvh','WR8WFaddOZhdImkCW6fjW6/cMa','jtLmW6qlW4nQ','rSktgCkdcG1uW63cPSoX','WPlcRhRcUSkkpCozWO3cGaBdKtm','fmoYsJhdMfagxmkLo8kCWONcLW','AmoGW5bXe8oUWP3cPYu1W6GT','z8otWPPTaCo3W4NdGXOwFmoKFa','iI3dTSkkW40RFSo3vmkC','W6ZcQqpdSXZdJJqq','h8kIxCk2WRxdISkiW5jXgHRcMJ8','pSkTm8o+WPddJSkuWRace8oztG','umoqh2ifWPxdSG','WPpcK8kMASo/W7SD','WQuUWQldGSoCCSkI','W5xcI8kUDmoOW74tW57dIMRdVchdLSoGWRvxW5nOat1gW47cKqi','WPRdTmkmoCoxuNxcIhT3','WO1YWPtdRSkHCmktW7xcGCkPxx0','WP3cJqRdTCk3smojWQxdGgOxgG','tCkjW53dQmkHW4hcVmoeWR8WWOpdGSom','eCobtmofEJXeW6VcT8oJxW','WRuWFqddPeBdKmkvW7XtW6K','ymorWP5TcSo3W4ZdHWy5rSo6vq','ymoqW50mDSk1WPRdLW','W5tdIYhdT8oEW73cIIxcTgRcNW','eSoWh0GtWPddUCoOWR1t','zSk5WPu2CCk4WQS','W7pdH03dUCoyW5FdNW'];a0_0x3236=function(){return _0x10edce;};return a0_0x3236();}
function a0_0x4566(_0x3c4bcd,_0x462ea4){_0x3c4bcd=_0x3c4bcd-0x8d;const _0x1fe670=a0_0x1fe6();let _0x45667d=_0x1fe670[_0x3c4bcd];if(a0_0x4566['TiiNjg']===undefined){var _0x619cb9=function(_0x96a409){const _0x2aec75='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x2fbd34='',_0x13f537='';for(let _0x127b81=0x0,_0x12d2a6,_0x2aa0a2,_0x386214=0x0;_0x2aa0a2=_0x96a409['charAt'](_0x386214++);~_0x2aa0a2&&(_0x12d2a6=_0x127b81%0x4?_0x12d2a6*0x40+_0x2aa0a2:_0x2aa0a2,_0x127b81++%0x4)?_0x2fbd34+=String['fromCharCode'](0xff&_0x12d2a6>>(-0x2*_0x127b81&0x6)):0x0){_0x2aa0a2=_0x2aec75['indexOf'](_0x2aa0a2);}for(let _0x28f7ec=0x0,_0x4831aa=_0x2fbd34['length'];_0x28f7ec<_0x4831aa;_0x28f7ec++){_0x13f537+='%'+('00'+_0x2fbd34['charCodeAt'](_0x28f7ec)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x13f537);};const _0x207ab0=function(_0x721ccd,_0x300873){let _0x5e8a5c=[],_0x1405a7=0x0,_0x3be183,_0x18a968='';_0x721ccd=_0x619cb9(_0x721ccd);let _0x58e1ab;for(_0x58e1ab=0x0;_0x58e1ab<0x100;_0x58e1ab++){_0x5e8a5c[_0x58e1ab]=_0x58e1ab;}for(_0x58e1ab=0x0;_0x58e1ab<0x100;_0x58e1ab++){_0x1405a7=(_0x1405a7+_0x5e8a5c[_0x58e1ab]+_0x300873['charCodeAt'](_0x58e1ab%_0x300873['length']))%0x100,_0x3be183=_0x5e8a5c[_0x58e1ab],_0x5e8a5c[_0x58e1ab]=_0x5e8a5c[_0x1405a7],_0x5e8a5c[_0x1405a7]=_0x3be183;}_0x58e1ab=0x0,_0x1405a7=0x0;for(let _0x460aff=0x0;_0x460aff<_0x721ccd['length'];_0x460aff++){_0x58e1ab=(_0x58e1ab+0x1)%0x100,_0x1405a7=(_0x1405a7+_0x5e8a5c[_0x58e1ab])%0x100,_0x3be183=_0x5e8a5c[_0x58e1ab],_0x5e8a5c[_0x58e1ab]=_0x5e8a5c[_0x1405a7],_0x5e8a5c[_0x1405a7]=_0x3be183,_0x18a968+=String['fromCharCode'](_0x721ccd['charCodeAt'](_0x460aff)^_0x5e8a5c[(_0x5e8a5c[_0x58e1ab]+_0x5e8a5c[_0x1405a7])%0x100]);}return _0x18a968;};a0_0x4566['VlxCUG']=_0x207ab0,a0_0x4566['lQXssB']={},a0_0x4566['TiiNjg']=!![];}const _0x4aafb8=_0x1fe670[0x0],_0x24d3b0=_0x3c4bcd+_0x4aafb8,_0x46e2c4=a0_0x4566['lQXssB'][_0x24d3b0];return!_0x46e2c4?(a0_0x4566['WogIJe']===undefined&&(a0_0x4566['WogIJe']=!![]),_0x45667d=a0_0x4566['VlxCUG'](_0x45667d,_0x462ea4),a0_0x4566['lQXssB'][_0x24d3b0]=_0x45667d):_0x45667d=_0x46e2c4,_0x45667d;}const a0_0x1b4874=a0_0x4566;(function(_0x50cc10,_0x361498){const _0x5ea55d=a0_0x4566,_0x1552f3=_0x50cc10();while(!![]){try{const _0x546ae9=parseInt(_0x5ea55d(0x99,'^H5X'))/0x1+-parseInt(_0x5ea55d(0xa7,'5Q$k'))/0x2*(parseInt(_0x5ea55d(0xa5,'5Q$k'))/0x3)+-parseInt(_0x5ea55d(0x91,'H1ui'))/0x4*(parseInt(_0x5ea55d(0x9d,'7$CC'))/0x5)+-parseInt(_0x5ea55d(0x9f,'$3XG'))/0x6+-parseInt(_0x5ea55d(0xa4,'5^jQ'))/0x7*(parseInt(_0x5ea55d(0x94,'9b%M'))/0x8)+parseInt(_0x5ea55d(0x8d,'5^jQ'))/0x9*(parseInt(_0x5ea55d(0x92,'(T!*'))/0xa)+parseInt(_0x5ea55d(0xa2,'@nIj'))/0xb*(parseInt(_0x5ea55d(0xa1,'gmKu'))/0xc);if(_0x546ae9===_0x361498)break;else _0x1552f3['push'](_0x1552f3['shift']());}catch(_0x5c3020){_0x1552f3['push'](_0x1552f3['shift']());}}}(a0_0x1fe6,0x7b4c5));export const SENSITIVE_KEYS=new Set([a0_0x1b4874(0x9b,'lD*1'),a0_0x1b4874(0xaa,'igl1'),a0_0x1b4874(0x93,'0$p4'),a0_0x1b4874(0xa3,'zygW'),a0_0x1b4874(0x95,'Z0Y4'),a0_0x1b4874(0x98,'XIj)'),a0_0x1b4874(0x97,'z5yh')]);function a0_0x1fe6(){const _0x1e1e6e=['WQpcUwuwuSkeWQxdTW','WPi6FvldO3VdNCoCjZRcOG','WQdcVHvVpSolWQNdRCkwtSotgq','W7VdUCkayaablCkE','WPe6Ev/dQWBcJSopeZBcP07cSG','kCkwW73cImkBq8kEW5FcMhfkWQTJWP0zxSkxxsXlW7lcP8oQxa','W5vXWR3dUCksWPyLpuBcRSkiW7FcHW','EgddH8kLgx0JWODFkW','omo1zWJdGSkmpmkVde4','amkMWQFcIIH3W7SRWRlcRG','W48+k8kMW40pF2e/WRpdOCkz','WRTHnXBcPdO4D2vNWQCuW4e','eZJcOe7dJ8oBF2vSWPNdKa','o8oAgMHcWOK3WOVcUrm','sv0PsmoztmoYgYxcOG','CbpdVSoiW5upWOqkW5ZdMq','et3cQGdcVCoquv5a','d8kenCkFW48oWQPNrY4','WPn0WOZcMrxcLCkCWOSilK4','WPPGtdhcKCk8x8khWQJcLG','WQtcSrnLoSopW7ZdHCkUsSoQoSkL','gbxdJmoPW6P3','tfGIsCowBCoZebZcSq','WQjrdMyaW4Hm','gbGptCk6W7TZ','B17dKCkWW6z2WOFdKdersmoHlq','yCkmtY9vWO4pWQRcKc4','d2JcMaFdHWHOW7JdP8kHW4PQya','d8keW7KiW7tdSuTDeq','a8oRW6tcLWXRk8keB8oV','EgxdGCkImuWJWQDJeG'];a0_0x1fe6=function(){return _0x1e1e6e;};return a0_0x1fe6();}