mirror of
https://github.com/Wx-2025/ST-Amily2-Chat-Optimisation.git
synced 2026-06-06 16:15:50 +00:00
Compare commits
5 Commits
0c5ac2c70b
...
2.0.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58ff3c3faf | ||
|
|
c50e1a9425 | ||
|
|
2291a871eb | ||
|
|
bddda1802f | ||
|
|
1fdbe62142 |
@@ -1,6 +1,7 @@
|
|||||||
import { extension_settings } from '/scripts/extensions.js';
|
import { extension_settings } from '/scripts/extensions.js';
|
||||||
import { extensionName } from '../../utils/settings.js';
|
import { extensionName } from '../../utils/settings.js';
|
||||||
import { saveSettingsDebounced } from '/script.js';
|
import { saveSettingsDebounced } from '/script.js';
|
||||||
|
import { configManager } from '../../utils/config/ConfigManager.js';
|
||||||
import { world_names } from '/scripts/world-info.js';
|
import { world_names } from '/scripts/world-info.js';
|
||||||
import { state } from './cwb_state.js';
|
import { state } from './cwb_state.js';
|
||||||
import { cwbCompleteDefaultSettings } from './cwb_config.js';
|
import { cwbCompleteDefaultSettings } from './cwb_config.js';
|
||||||
@@ -38,7 +39,7 @@ function saveApiConfig() {
|
|||||||
const settings = getSettings();
|
const settings = getSettings();
|
||||||
settings.cwb_api_mode = $panel.find('#cwb-api-mode').val();
|
settings.cwb_api_mode = $panel.find('#cwb-api-mode').val();
|
||||||
settings.cwb_api_url = $panel.find('#cwb-api-url').val().trim();
|
settings.cwb_api_url = $panel.find('#cwb-api-url').val().trim();
|
||||||
settings.cwb_api_key = $panel.find('#cwb-api-key').val();
|
configManager.set('cwb_api_key', $panel.find('#cwb-api-key').val());
|
||||||
settings.cwb_api_model = $panel.find('#cwb-api-model').val();
|
settings.cwb_api_model = $panel.find('#cwb-api-model').val();
|
||||||
settings.cwb_tavern_profile = $panel.find('#cwb-tavern-profile').val();
|
settings.cwb_tavern_profile = $panel.find('#cwb-tavern-profile').val();
|
||||||
|
|
||||||
@@ -63,7 +64,7 @@ function saveApiConfig() {
|
|||||||
function clearApiConfig() {
|
function clearApiConfig() {
|
||||||
const settings = getSettings();
|
const settings = getSettings();
|
||||||
settings.cwb_api_url = '';
|
settings.cwb_api_url = '';
|
||||||
settings.cwb_api_key = '';
|
configManager.set('cwb_api_key', '');
|
||||||
settings.cwb_api_model = '';
|
settings.cwb_api_model = '';
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
state.customApiConfig.url = '';
|
state.customApiConfig.url = '';
|
||||||
@@ -86,6 +87,13 @@ function saveBreakArmorPrompt() {
|
|||||||
showToastr('success', '破甲预设已保存!');
|
showToastr('success', '破甲预设已保存!');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function autosaveBreakArmorPrompt() {
|
||||||
|
const newPrompt = $panel.find('#cwb-break-armor-prompt-textarea').val();
|
||||||
|
getSettings().cwb_break_armor_prompt = newPrompt;
|
||||||
|
state.currentBreakArmorPrompt = newPrompt;
|
||||||
|
saveSettingsDebounced();
|
||||||
|
}
|
||||||
|
|
||||||
function resetBreakArmorPrompt() {
|
function resetBreakArmorPrompt() {
|
||||||
getSettings().cwb_break_armor_prompt = cwbCompleteDefaultSettings.cwb_break_armor_prompt;
|
getSettings().cwb_break_armor_prompt = cwbCompleteDefaultSettings.cwb_break_armor_prompt;
|
||||||
state.currentBreakArmorPrompt = cwbCompleteDefaultSettings.cwb_break_armor_prompt;
|
state.currentBreakArmorPrompt = cwbCompleteDefaultSettings.cwb_break_armor_prompt;
|
||||||
@@ -106,6 +114,13 @@ function saveCharCardPrompt() {
|
|||||||
showToastr('success', '角色卡预设已保存!');
|
showToastr('success', '角色卡预设已保存!');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function autosaveCharCardPrompt() {
|
||||||
|
const newPrompt = $panel.find('#cwb-char-card-prompt-textarea').val();
|
||||||
|
getSettings().cwb_char_card_prompt = newPrompt;
|
||||||
|
state.currentCharCardPrompt = newPrompt;
|
||||||
|
saveSettingsDebounced();
|
||||||
|
}
|
||||||
|
|
||||||
function resetCharCardPrompt() {
|
function resetCharCardPrompt() {
|
||||||
getSettings().cwb_char_card_prompt = cwbCompleteDefaultSettings.cwb_char_card_prompt;
|
getSettings().cwb_char_card_prompt = cwbCompleteDefaultSettings.cwb_char_card_prompt;
|
||||||
state.currentCharCardPrompt = cwbCompleteDefaultSettings.cwb_char_card_prompt;
|
state.currentCharCardPrompt = cwbCompleteDefaultSettings.cwb_char_card_prompt;
|
||||||
@@ -128,6 +143,16 @@ function saveAutoUpdateThreshold() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function autosaveAutoUpdateThreshold() {
|
||||||
|
const valStr = $panel.find('#cwb-auto-update-threshold').val();
|
||||||
|
const newT = parseInt(valStr, 10);
|
||||||
|
if (!isNaN(newT) && newT >= 1) {
|
||||||
|
getSettings().cwb_auto_update_threshold = newT;
|
||||||
|
state.autoUpdateThreshold = newT;
|
||||||
|
saveSettingsDebounced();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function saveScanDepth() {
|
function saveScanDepth() {
|
||||||
const valStr = $panel.find('#cwb-scan-depth').val();
|
const valStr = $panel.find('#cwb-scan-depth').val();
|
||||||
const newT = parseInt(valStr, 10);
|
const newT = parseInt(valStr, 10);
|
||||||
@@ -142,6 +167,16 @@ function saveScanDepth() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function autosaveScanDepth() {
|
||||||
|
const valStr = $panel.find('#cwb-scan-depth').val();
|
||||||
|
const newT = parseInt(valStr, 10);
|
||||||
|
if (!isNaN(newT) && newT >= 1) {
|
||||||
|
getSettings().cwb_scan_depth = newT;
|
||||||
|
state.scanDepth = newT;
|
||||||
|
saveSettingsDebounced();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function bindWorldBookSettings() {
|
function bindWorldBookSettings() {
|
||||||
const MAX_RETRIES = 10;
|
const MAX_RETRIES = 10;
|
||||||
const RETRY_DELAY = 200;
|
const RETRY_DELAY = 200;
|
||||||
@@ -283,16 +318,15 @@ export function bindSettingsEvents($settingsPanel) {
|
|||||||
$panel.on('input', '#cwb-api-key', function() {
|
$panel.on('input', '#cwb-api-key', function() {
|
||||||
const apiKey = $(this).val();
|
const apiKey = $(this).val();
|
||||||
|
|
||||||
// 同时更新设置和状态
|
// 同时更新设置和状态(API Key 经 configManager 写入 localStorage)
|
||||||
getSettings().cwb_api_key = apiKey;
|
configManager.set('cwb_api_key', apiKey);
|
||||||
state.customApiConfig.apiKey = apiKey;
|
state.customApiConfig.apiKey = apiKey;
|
||||||
|
updateApiStatusDisplay($panel);
|
||||||
saveSettingsDebounced();
|
|
||||||
|
console.log('[CWB] API Key已更新 - 状态长度:', state.customApiConfig.apiKey?.length || 0);
|
||||||
console.log('[CWB] API Key已更新 - 设置长度:', getSettings().cwb_api_key?.length || 0, ', 状态长度:', state.customApiConfig.apiKey?.length || 0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$panel.on('change', '#cwb-api-model', function() {
|
$panel.on('input change', '#cwb-api-model', function(event) {
|
||||||
const model = $(this).val();
|
const model = $(this).val();
|
||||||
|
|
||||||
// 同时更新设置和状态
|
// 同时更新设置和状态
|
||||||
@@ -304,11 +338,16 @@ export function bindSettingsEvents($settingsPanel) {
|
|||||||
|
|
||||||
console.log('[CWB] 模型已更新 - 设置:', getSettings().cwb_api_model, ', 状态:', state.customApiConfig.model);
|
console.log('[CWB] 模型已更新 - 设置:', getSettings().cwb_api_model, ', 状态:', state.customApiConfig.model);
|
||||||
|
|
||||||
if (model) {
|
if (model && event.type === 'change') {
|
||||||
showToastr('success', `模型已选择: ${model}`);
|
showToastr('success', `模型已选择: ${model}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$panel.on('input change', '#cwb-break-armor-prompt-textarea', autosaveBreakArmorPrompt);
|
||||||
|
$panel.on('input change', '#cwb-char-card-prompt-textarea', autosaveCharCardPrompt);
|
||||||
|
$panel.on('input change', '#cwb-auto-update-threshold', autosaveAutoUpdateThreshold);
|
||||||
|
$panel.on('input change', '#cwb-scan-depth', autosaveScanDepth);
|
||||||
|
|
||||||
$panel.on('click', '#cwb-load-models', () => fetchModelsAndConnect($panel));
|
$panel.on('click', '#cwb-load-models', () => fetchModelsAndConnect($panel));
|
||||||
|
|
||||||
$panel.on('click', '#cwb-save-break-armor-prompt', saveBreakArmorPrompt);
|
$panel.on('click', '#cwb-save-break-armor-prompt', saveBreakArmorPrompt);
|
||||||
@@ -489,7 +528,7 @@ function updateUiWithSettings() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$panel.find('#cwb-api-url').val(settings.cwb_api_url);
|
$panel.find('#cwb-api-url').val(settings.cwb_api_url);
|
||||||
$panel.find('#cwb-api-key').val(settings.cwb_api_key);
|
$panel.find('#cwb-api-key').val(configManager.get('cwb_api_key') || '');
|
||||||
$panel.find('#cwb-tavern-profile').val(settings.cwb_tavern_profile);
|
$panel.find('#cwb-tavern-profile').val(settings.cwb_tavern_profile);
|
||||||
|
|
||||||
const $modelSelect = $panel.find('#cwb-api-model');
|
const $modelSelect = $panel.find('#cwb-api-model');
|
||||||
@@ -574,7 +613,7 @@ export function loadSettings() {
|
|||||||
state.isIncrementalUpdateEnabled = finalSettings.cwb_incremental_update_enabled;
|
state.isIncrementalUpdateEnabled = finalSettings.cwb_incremental_update_enabled;
|
||||||
|
|
||||||
state.customApiConfig.url = finalSettings.cwb_api_url || '';
|
state.customApiConfig.url = finalSettings.cwb_api_url || '';
|
||||||
state.customApiConfig.apiKey = finalSettings.cwb_api_key || '';
|
state.customApiConfig.apiKey = configManager.get('cwb_api_key') || '';
|
||||||
state.customApiConfig.model = finalSettings.cwb_api_model || '';
|
state.customApiConfig.model = finalSettings.cwb_api_model || '';
|
||||||
|
|
||||||
state.currentBreakArmorPrompt = finalSettings.cwb_break_armor_prompt;
|
state.currentBreakArmorPrompt = finalSettings.cwb_break_armor_prompt;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
import { extension_settings } from '/scripts/extensions.js';
|
import { extension_settings } from '/scripts/extensions.js';
|
||||||
import { saveSettingsDebounced } from '/script.js';
|
import { saveSettingsDebounced } from '/script.js';
|
||||||
import { amilyHelper } from '../../core/tavern-helper/main.js';
|
import { amilyHelper } from '../../core/tavern-helper/main.js';
|
||||||
|
import { configManager } from '../../utils/config/ConfigManager.js';
|
||||||
|
|
||||||
const { jQuery: $, SillyTavern } = window;
|
const { jQuery: $, SillyTavern } = window;
|
||||||
|
|
||||||
@@ -675,8 +676,7 @@
|
|||||||
|
|
||||||
$('#cwb-api-key').off('input').on('input', function() {
|
$('#cwb-api-key').off('input').on('input', function() {
|
||||||
const value = $(this).val();
|
const value = $(this).val();
|
||||||
extension_settings[extensionName].cwb_api_key = value;
|
configManager.set('cwb_api_key', value);
|
||||||
saveSettingsDebounced();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#cwb-model').off('input').on('input', function() {
|
$('#cwb-model').off('input').on('input', function() {
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ export default class TableModule extends Module {
|
|||||||
if (this.el) {
|
if (this.el) {
|
||||||
this.el.id = 'amily2_memorisation_forms_panel';
|
this.el.id = 'amily2_memorisation_forms_panel';
|
||||||
this.el.style.display = 'none';
|
this.el.style.display = 'none';
|
||||||
|
this.el.dataset.module = 'TableModule';
|
||||||
}
|
}
|
||||||
bindTableEvents();
|
bindTableEvents(this.el);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,15 @@
|
|||||||
<i class="fas fa-sync-alt"></i> 重新生成
|
<i class="fas fa-sync-alt"></i> 重新生成
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div style="display:flex; gap:6px; margin-top:6px; flex-wrap:wrap;">
|
||||||
|
<button id="amily2_export_key_bundle" class="menu_button interactable small_button" title="导出当前设备的私钥包,用于新设备恢复解密权限">
|
||||||
|
<i class="fas fa-download"></i> 导出私钥
|
||||||
|
</button>
|
||||||
|
<button id="amily2_import_key_bundle" class="menu_button interactable small_button" title="导入先前导出的私钥包,恢复云同步密钥的解密能力">
|
||||||
|
<i class="fas fa-upload"></i> 导入私钥
|
||||||
|
</button>
|
||||||
|
<input id="amily2_import_key_bundle_input" type="file" accept=".json,application/json" style="display:none;" />
|
||||||
|
</div>
|
||||||
<small class="notes" style="color: var(--warning-color);">
|
<small class="notes" style="color: var(--warning-color);">
|
||||||
⚠️ 重新生成密钥对后,所有已加密存储的 API Key 将失效,需重新输入。
|
⚠️ 重新生成密钥对后,所有已加密存储的 API Key 将失效,需重新输入。
|
||||||
</small>
|
</small>
|
||||||
@@ -159,6 +168,13 @@
|
|||||||
<label for="amily2_pf_temperature">温度(Temperature)</label>
|
<label for="amily2_pf_temperature">温度(Temperature)</label>
|
||||||
<input id="amily2_pf_temperature" type="number" class="text_pole" min="0" max="2" step="0.1" value="1.0" />
|
<input id="amily2_pf_temperature" type="number" class="text_pole" min="0" max="2" step="0.1" value="1.0" />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="amily2_settings_block" style="flex-direction:row; align-items:center; gap:8px;">
|
||||||
|
<input id="amily2_pf_fake_stream" type="checkbox" />
|
||||||
|
<label for="amily2_pf_fake_stream">
|
||||||
|
启用假流式(防 CF 超时)
|
||||||
|
<small class="notes" style="display:block; font-weight:normal;">以 stream:true 接收 SSE 后拼接,适用于经 CloudFlare 免费代理的接口</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
49
core/api.js
49
core/api.js
@@ -1,6 +1,7 @@
|
|||||||
import { extension_settings, getContext } from "/scripts/extensions.js";
|
import { extension_settings, getContext } from "/scripts/extensions.js";
|
||||||
import { characters } from "/script.js";
|
import { characters } from "/script.js";
|
||||||
import { getSlotProfile } from './api/api-resolver.js';
|
import { getSlotProfile, providerToApiMode } from './api/api-resolver.js';
|
||||||
|
import { configManager } from '../utils/config/ConfigManager.js';
|
||||||
import { world_names } from "/scripts/world-info.js";
|
import { world_names } from "/scripts/world-info.js";
|
||||||
import { extensionName } from "../utils/settings.js";
|
import { extensionName } from "../utils/settings.js";
|
||||||
import { extractContentByTag, replaceContentByTag, extractFullTagBlock } from '../utils/tagProcessor.js';
|
import { extractContentByTag, replaceContentByTag, extractFullTagBlock } from '../utils/tagProcessor.js';
|
||||||
@@ -441,20 +442,56 @@ export async function getApiSettings(slot = 'main') {
|
|||||||
// 优先读取槽位分配的 Profile(仅接管连接参数)
|
// 优先读取槽位分配的 Profile(仅接管连接参数)
|
||||||
const profile = await getSlotProfile(slot);
|
const profile = await getSlotProfile(slot);
|
||||||
if (profile) {
|
if (profile) {
|
||||||
|
const resolvedProvider = profile.provider === 'sillytavern_backend'
|
||||||
|
? 'sillytavern_backend'
|
||||||
|
: providerToApiMode(profile.provider);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
apiProvider: profile.provider,
|
apiProvider: resolvedProvider,
|
||||||
apiUrl: profile.apiUrl,
|
apiUrl: profile.apiUrl,
|
||||||
apiKey: profile.apiKey ?? '',
|
apiKey: profile.apiKey ?? '',
|
||||||
model: profile.model,
|
model: profile.model,
|
||||||
// 温度 / MaxTokens 读面板值(profile-sync 保留了这些输入框)
|
// 温度 / MaxTokens 读面板值(profile-sync 保留了这些输入框)
|
||||||
maxTokens: s.maxTokens ?? profile.maxTokens ?? 65500,
|
maxTokens: s.maxTokens ?? profile.maxTokens ?? 65500,
|
||||||
temperature: s.temperature ?? profile.temperature ?? 1.0,
|
temperature: s.temperature ?? profile.temperature ?? 1.0,
|
||||||
|
fakeStream: profile.fakeStream ?? false,
|
||||||
tavernProfile: '',
|
tavernProfile: '',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 降级:读旧 DOM 面板配置
|
// 降级:按槽位读取各自的独立配置
|
||||||
const settings = extension_settings[extensionName] || {};
|
const settings = extension_settings[extensionName] || {};
|
||||||
|
|
||||||
|
// plotOpt 槽有独立 API 面板(剧情优化),优先读其专属设置
|
||||||
|
if (slot === 'plotOpt') {
|
||||||
|
const apiMode = settings.plotOpt_apiMode || 'openai_test';
|
||||||
|
if (apiMode === 'sillytavern_preset') {
|
||||||
|
const context = getContext();
|
||||||
|
const profileId = settings.plotOpt_tavernProfile || '';
|
||||||
|
const stProfile = context.extensionSettings?.connectionManager?.profiles?.find(p => p.id === profileId);
|
||||||
|
return {
|
||||||
|
apiProvider: 'sillytavern_preset',
|
||||||
|
apiUrl: '',
|
||||||
|
apiKey: '',
|
||||||
|
model: stProfile?.openai_model || 'Preset Model',
|
||||||
|
maxTokens: settings.plotOpt_max_tokens ?? 65500,
|
||||||
|
temperature: settings.plotOpt_temperature ?? 1.0,
|
||||||
|
tavernProfile: profileId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
apiProvider: apiMode,
|
||||||
|
apiUrl: settings.plotOpt_apiUrl?.trim() || '',
|
||||||
|
apiKey: configManager.get('plotOpt_apiKey') || '',
|
||||||
|
model: document.getElementById('amily2_opt_model')?.value?.trim()
|
||||||
|
|| settings.plotOpt_model || '',
|
||||||
|
maxTokens: settings.plotOpt_max_tokens ?? 65500,
|
||||||
|
temperature: settings.plotOpt_temperature ?? 1.0,
|
||||||
|
tavernProfile: '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// main 槽(及其余未明确处理的槽):读主面板 DOM 配置
|
||||||
const apiProvider = document.getElementById('amily2_api_provider')?.value || 'openai';
|
const apiProvider = document.getElementById('amily2_api_provider')?.value || 'openai';
|
||||||
|
|
||||||
let model;
|
let model;
|
||||||
@@ -489,13 +526,15 @@ export async function testApiConnection() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const apiSettings = await getApiSettings();
|
const apiSettings = await getApiSettings();
|
||||||
|
const apiProvider = apiSettings.apiProvider || 'openai';
|
||||||
|
const requiresApiKey = !['sillytavern_backend', 'sillytavern_preset'].includes(apiProvider);
|
||||||
|
|
||||||
if (apiSettings.apiProvider === 'sillytavern_preset') {
|
if (apiProvider === 'sillytavern_preset') {
|
||||||
if (!apiSettings.tavernProfile) {
|
if (!apiSettings.tavernProfile) {
|
||||||
throw new Error("请先在下方选择一个SillyTavern预设");
|
throw new Error("请先在下方选择一个SillyTavern预设");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!apiSettings.apiUrl || !apiSettings.apiKey || !apiSettings.model) {
|
if (!apiSettings.apiUrl || !apiSettings.model) {
|
||||||
throw new Error("API配置不完整,请检查URL、Key和模型选择");
|
throw new Error("API配置不完整,请检查URL、Key和模型选择");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -329,7 +329,7 @@ export async function getPlotOptimizedWorldbookContent(context, apiSettings, isC
|
|||||||
selectedWorldbooks: apiSettings.plotOpt_selectedWorldbooks,
|
selectedWorldbooks: apiSettings.plotOpt_selectedWorldbooks,
|
||||||
autoSelectWorldbooks: apiSettings.plotOpt_autoSelectWorldbooks || [],
|
autoSelectWorldbooks: apiSettings.plotOpt_autoSelectWorldbooks || [],
|
||||||
worldbookCharLimit: apiSettings.plotOpt_worldbookCharLimit,
|
worldbookCharLimit: apiSettings.plotOpt_worldbookCharLimit,
|
||||||
contextLimit: apiSettings.plotOpt_contextLimit || 5,
|
contextLimit: apiSettings.plotOpt_contextLimit ?? apiSettings.plotOpt_contextTurnCount ?? 5,
|
||||||
enabledWorldbookEntries: apiSettings.plotOpt_enabledWorldbookEntries,
|
enabledWorldbookEntries: apiSettings.plotOpt_enabledWorldbookEntries,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -423,7 +423,7 @@ export async function processPlotOptimization(currentUserMessage, contextMessage
|
|||||||
}
|
}
|
||||||
|
|
||||||
let history = '';
|
let history = '';
|
||||||
const contextLimit = settings.plotOpt_contextLimit || 0;
|
const contextLimit = settings.plotOpt_contextLimit ?? settings.plotOpt_contextTurnCount ?? 0;
|
||||||
if (contextLimit > 0 && contextMessages.length > 0) {
|
if (contextLimit > 0 && contextMessages.length > 0) {
|
||||||
const historyMessages = contextMessages.slice(-contextLimit);
|
const historyMessages = contextMessages.slice(-contextLimit);
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ import { renderTables } from '../../ui/table-bindings.js';
|
|||||||
async function processMessageUpdate(messageId) {
|
async function processMessageUpdate(messageId) {
|
||||||
TableManager.clearHighlights();
|
TableManager.clearHighlights();
|
||||||
|
|
||||||
const settings = extension_settings[extensionName];
|
const settings = extension_settings[extensionName] || {};
|
||||||
const tableSystemEnabled = settings.table_system_enabled !== false;
|
const tableSystemEnabled = settings.table_system_enabled !== false;
|
||||||
if (!tableSystemEnabled) {
|
if (!tableSystemEnabled) {
|
||||||
log('【表格服务】表格系统总开关已关闭,跳过所有表格处理。', 'info');
|
log('【表格服务】表格系统总开关已关闭,跳过所有表格处理。', 'info');
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const MAX_RETRIES = 2;
|
|||||||
|
|
||||||
|
|
||||||
async function getWorldBookContext() {
|
async function getWorldBookContext() {
|
||||||
const settings = extension_settings[extensionName];
|
const settings = extension_settings[extensionName] || {};
|
||||||
if (!settings.table_worldbook_enabled) {
|
if (!settings.table_worldbook_enabled) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@@ -114,7 +114,7 @@ function updateButtonState(state, batchNum = 0, attemptNum = 0) {
|
|||||||
|
|
||||||
async function callTableModel(messages) {
|
async function callTableModel(messages) {
|
||||||
try {
|
try {
|
||||||
const settings = extension_settings[extensionName];
|
const settings = extension_settings[extensionName] || {};
|
||||||
|
|
||||||
if (settings.nccsEnabled) {
|
if (settings.nccsEnabled) {
|
||||||
log('使用 Nccs API 进行表格填充...', 'info');
|
log('使用 Nccs API 进行表格填充...', 'info');
|
||||||
@@ -141,7 +141,7 @@ async function callTableModel(messages) {
|
|||||||
function getRawMessagesForSummary(startFloor, endFloor) {
|
function getRawMessagesForSummary(startFloor, endFloor) {
|
||||||
const context = getContext();
|
const context = getContext();
|
||||||
const chat = context.chat;
|
const chat = context.chat;
|
||||||
const settings = extension_settings[extensionName];
|
const settings = extension_settings[extensionName] || {};
|
||||||
|
|
||||||
const historySlice = chat.slice(startFloor - 1, endFloor);
|
const historySlice = chat.slice(startFloor - 1, endFloor);
|
||||||
if (historySlice.length === 0) return null;
|
if (historySlice.length === 0) return null;
|
||||||
@@ -319,7 +319,7 @@ export function startBatchFilling() {
|
|||||||
const button = fillButton();
|
const button = fillButton();
|
||||||
if (!button) return;
|
if (!button) return;
|
||||||
|
|
||||||
const settings = extension_settings[extensionName];
|
const settings = extension_settings[extensionName] || {};
|
||||||
const tableSystemEnabled = settings.table_system_enabled !== false;
|
const tableSystemEnabled = settings.table_system_enabled !== false;
|
||||||
if (!tableSystemEnabled) {
|
if (!tableSystemEnabled) {
|
||||||
log('表格系统总开关已关闭,跳过批量填表。', 'info');
|
log('表格系统总开关已关闭,跳过批量填表。', 'info');
|
||||||
@@ -387,7 +387,7 @@ export function startBatchFilling() {
|
|||||||
|
|
||||||
|
|
||||||
export async function startFloorRangeFilling(startFloor, endFloor) {
|
export async function startFloorRangeFilling(startFloor, endFloor) {
|
||||||
const settings = extension_settings[extensionName];
|
const settings = extension_settings[extensionName] || {};
|
||||||
const tableSystemEnabled = settings.table_system_enabled !== false;
|
const tableSystemEnabled = settings.table_system_enabled !== false;
|
||||||
if (!tableSystemEnabled) {
|
if (!tableSystemEnabled) {
|
||||||
log('表格系统总开关已关闭,跳过楼层填表。', 'info');
|
log('表格系统总开关已关闭,跳过楼层填表。', 'info');
|
||||||
|
|||||||
@@ -1264,7 +1264,7 @@ export function getAiFlowTemplateForInjection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function updateTableFromText(textContent, options = {}) {
|
export async function updateTableFromText(textContent, options = {}) {
|
||||||
const settings = extension_settings[extensionName];
|
const settings = extension_settings[extensionName] || {};
|
||||||
if (settings.table_system_enabled === false) {
|
if (settings.table_system_enabled === false) {
|
||||||
log('表格系统总开关已关闭,跳过 <Amily2Edit> 标签处理。', 'info');
|
log('表格系统总开关已关闭,跳过 <Amily2Edit> 标签处理。', 'info');
|
||||||
return;
|
return;
|
||||||
@@ -1575,7 +1575,7 @@ export async function rollbackState() {
|
|||||||
|
|
||||||
export async function rollbackAndRefill() {
|
export async function rollbackAndRefill() {
|
||||||
// 检查表格系统总开关
|
// 检查表格系统总开关
|
||||||
const settings = extension_settings[extensionName];
|
const settings = extension_settings[extensionName] || {};
|
||||||
if (settings.table_system_enabled === false) {
|
if (settings.table_system_enabled === false) {
|
||||||
log('表格系统总开关已关闭,跳过回退填表。', 'info');
|
log('表格系统总开关已关闭,跳过回退填表。', 'info');
|
||||||
toastr.info('表格系统总开关已关闭,无法执行回退填表。');
|
toastr.info('表格系统总开关已关闭,无法执行回退填表。');
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import { renderTables } from '../../ui/table-bindings.js';
|
|||||||
import { extensionName } from "../../utils/settings.js";
|
import { extensionName } from "../../utils/settings.js";
|
||||||
import { convertTablesToCsvString, convertSelectedTablesToCsvString, saveStateToMessage, getMemoryState, updateTableFromText, getBatchFillerRuleTemplate, getBatchFillerFlowTemplate } from './manager.js';
|
import { convertTablesToCsvString, convertSelectedTablesToCsvString, saveStateToMessage, getMemoryState, updateTableFromText, getBatchFillerRuleTemplate, getBatchFillerFlowTemplate } from './manager.js';
|
||||||
import { getPresetPrompts, getMixedOrder } from '../../PresetSettings/index.js';
|
import { getPresetPrompts, getMixedOrder } from '../../PresetSettings/index.js';
|
||||||
import { callAI, generateRandomSeed, getApiSettings } from '../api.js';
|
import { callAI, generateRandomSeed } from '../api.js';
|
||||||
import { callNccsAI } from '../api/NccsApi.js';
|
import { callNccsAI } from '../api/NccsApi.js';
|
||||||
|
|
||||||
export async function reorganizeTableContent(selectedTableIndices) {
|
export async function reorganizeTableContent(selectedTableIndices) {
|
||||||
const settings = extension_settings[extensionName];
|
const settings = extension_settings[extensionName] || {};
|
||||||
|
|
||||||
if (settings.table_system_enabled === false) {
|
if (settings.table_system_enabled === false) {
|
||||||
toastr.warning('表格系统总开关已关闭。');
|
toastr.warning('表格系统总开关已关闭。');
|
||||||
@@ -20,13 +20,6 @@ export async function reorganizeTableContent(selectedTableIndices) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const resolvedApi = await getApiSettings('main');
|
|
||||||
const { apiUrl, apiKey, model, temperature, maxTokens, forceProxyForCustomApi } = resolvedApi ?? settings;
|
|
||||||
if (!apiUrl || !model) {
|
|
||||||
toastr.error("主API的URL或模型未配置,重新整理功能无法启动。", "Amily2-重新整理");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
toastr.info('正在重新整理表格内容...', 'Amily2-重新整理');
|
toastr.info('正在重新整理表格内容...', 'Amily2-重新整理');
|
||||||
|
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ import { updateOrInsertTableInChat } from '../../ui/message-table-renderer.js';
|
|||||||
import { extensionName } from "../../utils/settings.js";
|
import { extensionName } from "../../utils/settings.js";
|
||||||
import { updateTableFromText, getBatchFillerRuleTemplate, getBatchFillerFlowTemplate, convertTablesToCsvString, saveStateToMessage, getMemoryState, clearHighlights } from './manager.js';
|
import { updateTableFromText, getBatchFillerRuleTemplate, getBatchFillerFlowTemplate, convertTablesToCsvString, saveStateToMessage, getMemoryState, clearHighlights } from './manager.js';
|
||||||
import { getPresetPrompts, getMixedOrder } from '../../PresetSettings/index.js';
|
import { getPresetPrompts, getMixedOrder } from '../../PresetSettings/index.js';
|
||||||
import { callAI, generateRandomSeed, getApiSettings } from '../api.js';
|
import { callAI, generateRandomSeed } from '../api.js';
|
||||||
import { callNccsAI } from '../api/NccsApi.js';
|
import { callNccsAI } from '../api/NccsApi.js';
|
||||||
import { extractBlocksByTags, applyExclusionRules } from '../utils/rag-tag-extractor.js';
|
import { extractBlocksByTags, applyExclusionRules } from '../utils/rag-tag-extractor.js';
|
||||||
import { safeLorebookEntries } from '../tavernhelper-compatibility.js';
|
import { safeLorebookEntries } from '../tavernhelper-compatibility.js';
|
||||||
|
|
||||||
|
|
||||||
async function getWorldBookContext() {
|
async function getWorldBookContext() {
|
||||||
const settings = extension_settings[extensionName];
|
const settings = extension_settings[extensionName] || {};
|
||||||
|
|
||||||
if (!settings.table_worldbook_enabled) {
|
if (!settings.table_worldbook_enabled) {
|
||||||
return '';
|
return '';
|
||||||
@@ -67,7 +67,7 @@ async function getWorldBookContext() {
|
|||||||
export async function fillWithSecondaryApi(latestMessage, forceRun = false) {
|
export async function fillWithSecondaryApi(latestMessage, forceRun = false) {
|
||||||
clearHighlights();
|
clearHighlights();
|
||||||
|
|
||||||
const settings = extension_settings[extensionName];
|
const settings = extension_settings[extensionName] || {};
|
||||||
|
|
||||||
// 总开关关闭时,分步填表同样禁用
|
// 总开关关闭时,分步填表同样禁用
|
||||||
if (settings.table_system_enabled === false) {
|
if (settings.table_system_enabled === false) {
|
||||||
@@ -92,15 +92,6 @@ export async function fillWithSecondaryApi(latestMessage, forceRun = false) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const resolvedApi = await getApiSettings('main');
|
|
||||||
const { apiUrl, apiKey, model, temperature, maxTokens, forceProxyForCustomApi } = resolvedApi ?? settings;
|
|
||||||
if (!apiUrl || !model) {
|
|
||||||
if (!window.secondaryApiUrlWarned) {
|
|
||||||
toastr.error("主API的URL或模型未配置,分步填表功能无法启动。", "Amily2-分步填表");
|
|
||||||
window.secondaryApiUrlWarned = true;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const bufferSize = parseInt(settings.secondary_filler_buffer || 0, 10);
|
const bufferSize = parseInt(settings.secondary_filler_buffer || 0, 10);
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ export {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const tableSystemDefaultSettings = {
|
export const tableSystemDefaultSettings = {
|
||||||
|
table_system_enabled: true,
|
||||||
table_injection_enabled: false,
|
table_injection_enabled: false,
|
||||||
|
|
||||||
injection: {
|
injection: {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { extension_settings, getContext } from "/scripts/extensions.js";
|
import { extension_settings, getContext } from "/scripts/extensions.js";
|
||||||
import { saveSettingsDebounced, eventSource, event_types } from "/script.js";
|
import { saveSettingsDebounced, eventSource, event_types } from "/script.js";
|
||||||
import { extensionName } from "../utils/settings.js";
|
import { extensionName } from "../utils/settings.js";
|
||||||
|
import { configManager } from '../utils/config/ConfigManager.js';
|
||||||
|
import { SENSITIVE_KEYS } from '../utils/config/sensitive-keys.js';
|
||||||
import { safeLorebooks, safeLorebookEntries, safeUpdateLorebookEntries } from '../core/tavernhelper-compatibility.js';
|
import { safeLorebooks, safeLorebookEntries, safeUpdateLorebookEntries } from '../core/tavernhelper-compatibility.js';
|
||||||
import { testSybdApiConnection, fetchSybdModels } from '../core/api/SybdApi.js';
|
import { testSybdApiConnection, fetchSybdModels } from '../core/api/SybdApi.js';
|
||||||
import { handleFileUpload, processNovel } from './index.js';
|
import { handleFileUpload, processNovel } from './index.js';
|
||||||
@@ -29,18 +31,21 @@ function loadSettingsToUI() {
|
|||||||
const inputs = container.querySelectorAll('[data-setting-key]');
|
const inputs = container.querySelectorAll('[data-setting-key]');
|
||||||
inputs.forEach(target => {
|
inputs.forEach(target => {
|
||||||
const key = target.dataset.settingKey;
|
const key = target.dataset.settingKey;
|
||||||
const value = settings[key];
|
// 敏感字段从 configManager(localStorage)读取,其余从 extension_settings 读取
|
||||||
|
const value = SENSITIVE_KEYS.has(key) ? configManager.get(key) : settings[key];
|
||||||
|
|
||||||
if (value === undefined) {
|
if (value === undefined || value === null || value === '') {
|
||||||
let defaultValue;
|
if (!SENSITIVE_KEYS.has(key)) {
|
||||||
if (target.type === 'checkbox') {
|
let defaultValue;
|
||||||
defaultValue = target.checked;
|
if (target.type === 'checkbox') {
|
||||||
} else if (target.type === 'range') {
|
defaultValue = target.checked;
|
||||||
defaultValue = target.dataset.type === 'float' ? parseFloat(target.value) : parseInt(target.value, 10);
|
} else if (target.type === 'range') {
|
||||||
} else {
|
defaultValue = target.dataset.type === 'float' ? parseFloat(target.value) : parseInt(target.value, 10);
|
||||||
defaultValue = target.value;
|
} else {
|
||||||
|
defaultValue = target.value;
|
||||||
|
}
|
||||||
|
updateAndSaveSetting(key, defaultValue);
|
||||||
}
|
}
|
||||||
updateAndSaveSetting(key, defaultValue);
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -90,8 +95,13 @@ function bindAutoSaveEvents() {
|
|||||||
case 'float': value = parseFloat(value); break;
|
case 'float': value = parseFloat(value); break;
|
||||||
case 'boolean': value = (typeof value === 'boolean') ? value : (value === 'true'); break;
|
case 'boolean': value = (typeof value === 'boolean') ? value : (value === 'true'); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAndSaveSetting(key, value);
|
// 敏感字段(API Key)经 configManager 写入 localStorage
|
||||||
|
if (SENSITIVE_KEYS.has(key)) {
|
||||||
|
configManager.set(key, value);
|
||||||
|
} else {
|
||||||
|
updateAndSaveSetting(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
if (key === 'sybdApiMode') {
|
if (key === 'sybdApiMode') {
|
||||||
updateConfigVisibility(value);
|
updateConfigVisibility(value);
|
||||||
|
|||||||
12
index.js
12
index.js
@@ -568,11 +568,12 @@ async function onPlotGenerationAfterCommands(type, params, dryRun) {
|
|||||||
if (globalSettings?.plotOpt_enabled === false) return false;
|
if (globalSettings?.plotOpt_enabled === false) return false;
|
||||||
|
|
||||||
const isJqyhEnabled = globalSettings?.jqyhEnabled === true;
|
const isJqyhEnabled = globalSettings?.jqyhEnabled === true;
|
||||||
const hasMainProfile = !!apiProfileManager.getAssignment('main') || !!apiProfileManager.getAssignment('plotOpt');
|
const hasProfile = !!apiProfileManager.getAssignment('main') || !!apiProfileManager.getAssignment('plotOpt');
|
||||||
const isMainApiConfigured = hasMainProfile || !!globalSettings?.apiUrl || !!globalSettings?.tavernProfile;
|
const hasLegacyConfig = !!globalSettings?.apiUrl || !!globalSettings?.tavernProfile
|
||||||
|
|| !!globalSettings?.plotOpt_apiUrl || !!globalSettings?.plotOpt_tavernProfile;
|
||||||
|
|
||||||
if (!isJqyhEnabled && !isMainApiConfigured) {
|
if (!isJqyhEnabled && !hasProfile && !hasLegacyConfig) {
|
||||||
console.log("[Amily2-剧情优化] 优化已启用,但Jqyh API已禁用且主API未配置(无 Profile 分配亦无旧设置)。");
|
console.log("[Amily2-剧情优化] 优化已启用,但未配置任何可用的 API(无 Profile 分配亦无独立配置)。");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -609,7 +610,7 @@ async function onPlotGenerationAfterCommands(type, params, dryRun) {
|
|||||||
}, 100);
|
}, 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
const contextTurnCount = globalSettings.plotOpt_contextLimit || 10;
|
const contextTurnCount = globalSettings.plotOpt_contextLimit ?? globalSettings.plotOpt_contextTurnCount ?? 10;
|
||||||
const contextSource = isFromTextarea ? context.chat : context.chat.slice(0, -1);
|
const contextSource = isFromTextarea ? context.chat : context.chat.slice(0, -1);
|
||||||
const slicedContext = contextTurnCount > 0 ? contextSource.slice(-contextTurnCount) : contextSource;
|
const slicedContext = contextTurnCount > 0 ? contextSource.slice(-contextTurnCount) : contextSource;
|
||||||
|
|
||||||
@@ -879,6 +880,7 @@ jQuery(async () => {
|
|||||||
initializeAmilyHelper();
|
initializeAmilyHelper();
|
||||||
mergePluginSettings();
|
mergePluginSettings();
|
||||||
configManager.migrate(); // 将 extension_settings 中残留的敏感字段迁移到 localStorage
|
configManager.migrate(); // 将 extension_settings 中残留的敏感字段迁移到 localStorage
|
||||||
|
await configManager.init();
|
||||||
|
|
||||||
let attempts = 0;
|
let attempts = 0;
|
||||||
const maxAttempts = 100;
|
const maxAttempts = 100;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Amily2号聊天优化助手",
|
"name": "Amily2号聊天优化助手",
|
||||||
"display_name": "Amily2号助手",
|
"display_name": "Amily2号助手",
|
||||||
"version": "2.0.1",
|
"version": "2.0.3",
|
||||||
"author": "Wx-2025",
|
"author": "Wx-2025",
|
||||||
"description": "一个拥有独立UI的智能引擎,正文优化、自动总结、记忆表格、rag向量、隐藏楼层、剧情推进等多功能整合。",
|
"description": "一个拥有独立UI的智能引擎,正文优化、自动总结、记忆表格、rag向量、隐藏楼层、剧情推进等多功能整合。",
|
||||||
"minSillyTavernVersion": "1.10.0",
|
"minSillyTavernVersion": "1.10.0",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
import { apiProfileManager, PROFILE_TYPES, SLOTS } from '../utils/config/ApiProfileManager.js';
|
import { apiProfileManager, PROFILE_TYPES, SLOTS } from '../utils/config/ApiProfileManager.js';
|
||||||
import { apiKeyStore } from '../utils/config/api-key-store/ApiKeyStore.js';
|
import { apiKeyStore } from '../utils/config/api-key-store/ApiKeyStore.js';
|
||||||
|
import { configManager } from '../utils/config/ConfigManager.js';
|
||||||
import { getRequestHeaders, saveSettingsDebounced } from '/script.js';
|
import { getRequestHeaders, saveSettingsDebounced } from '/script.js';
|
||||||
import { extension_settings } from '/scripts/extensions.js';
|
import { extension_settings } from '/scripts/extensions.js';
|
||||||
import { extensionName } from '../utils/settings.js';
|
import { extensionName } from '../utils/settings.js';
|
||||||
@@ -97,6 +98,7 @@ function _bindStorageMode($c) {
|
|||||||
const $select = $c.find('#amily2_keystore_mode');
|
const $select = $c.find('#amily2_keystore_mode');
|
||||||
const $cloud = $c.find('#amily2_cloud_key_section');
|
const $cloud = $c.find('#amily2_cloud_key_section');
|
||||||
const $note = $c.find('#amily2_keystore_mode_note');
|
const $note = $c.find('#amily2_keystore_mode_note');
|
||||||
|
const $importInput = $c.find('#amily2_import_key_bundle_input');
|
||||||
|
|
||||||
const MODE_NOTES = {
|
const MODE_NOTES = {
|
||||||
local: '本地存储:API Key 仅存于本设备浏览器,绝不上传服务端。换设备需重新填写。',
|
local: '本地存储:API Key 仅存于本设备浏览器,绝不上传服务端。换设备需重新填写。',
|
||||||
@@ -124,6 +126,9 @@ function _bindStorageMode($c) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await apiKeyStore.setMode(newMode);
|
await apiKeyStore.setMode(newMode);
|
||||||
|
if (newMode === 'cloud') {
|
||||||
|
await configManager.syncSensitiveCache({ force: true });
|
||||||
|
}
|
||||||
$cloud.toggle(newMode === 'cloud');
|
$cloud.toggle(newMode === 'cloud');
|
||||||
$note.text(MODE_NOTES[newMode]);
|
$note.text(MODE_NOTES[newMode]);
|
||||||
if (newMode === 'cloud') _refreshFingerprint($c);
|
if (newMode === 'cloud') _refreshFingerprint($c);
|
||||||
@@ -142,6 +147,43 @@ function _bindStorageMode($c) {
|
|||||||
_refreshFingerprint($c);
|
_refreshFingerprint($c);
|
||||||
toastr.warning('新密钥对已生成,请重新输入各 Profile 的 API Key。');
|
toastr.warning('新密钥对已生成,请重新输入各 Profile 的 API Key。');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$c.find('#amily2_export_key_bundle').on('click', async () => {
|
||||||
|
try {
|
||||||
|
const bundle = await apiKeyStore.exportPrivateKeyBundle();
|
||||||
|
_downloadJson(
|
||||||
|
`amily2-keystore-${_timestampForFilename()}.json`,
|
||||||
|
bundle
|
||||||
|
);
|
||||||
|
toastr.success('私钥包已导出,请妥善保管。');
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[ApiConfig] 导出私钥包失败:', e);
|
||||||
|
toastr.error(e.message || '导出私钥包失败。');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$c.find('#amily2_import_key_bundle').on('click', () => {
|
||||||
|
$importInput.val('');
|
||||||
|
$importInput.trigger('click');
|
||||||
|
});
|
||||||
|
|
||||||
|
$importInput.on('change', async function () {
|
||||||
|
const file = this.files?.[0];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const text = await file.text();
|
||||||
|
await apiKeyStore.importPrivateKeyBundle(text);
|
||||||
|
await configManager.syncSensitiveCache({ force: true });
|
||||||
|
await _refreshFingerprint($c);
|
||||||
|
toastr.success('私钥包导入成功,已尝试恢复云同步的 API Key 缓存。');
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[ApiConfig] 导入私钥包失败:', e);
|
||||||
|
toastr.error(e.message || '导入私钥包失败。');
|
||||||
|
} finally {
|
||||||
|
$importInput.val('');
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function _refreshFingerprint($c) {
|
async function _refreshFingerprint($c) {
|
||||||
@@ -149,6 +191,24 @@ async function _refreshFingerprint($c) {
|
|||||||
$c.find('#amily2_keypair_fingerprint').text(fp);
|
$c.find('#amily2_keypair_fingerprint').text(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _downloadJson(filename, data) {
|
||||||
|
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = filename;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
a.remove();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _timestampForFilename() {
|
||||||
|
const now = new Date();
|
||||||
|
const pad = n => String(n).padStart(2, '0');
|
||||||
|
return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
|
||||||
|
}
|
||||||
|
|
||||||
// ── Profile 列表渲染 ──────────────────────────────────────────────────────────
|
// ── Profile 列表渲染 ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export function renderProfileList($c) {
|
export function renderProfileList($c) {
|
||||||
@@ -343,6 +403,7 @@ async function openModal($c, id) {
|
|||||||
if (p.type === 'chat') {
|
if (p.type === 'chat') {
|
||||||
$c.find('#amily2_pf_max_tokens').val(p.maxTokens);
|
$c.find('#amily2_pf_max_tokens').val(p.maxTokens);
|
||||||
$c.find('#amily2_pf_temperature').val(p.temperature);
|
$c.find('#amily2_pf_temperature').val(p.temperature);
|
||||||
|
$c.find('#amily2_pf_fake_stream').prop('checked', p.fakeStream ?? false);
|
||||||
} else if (p.type === 'embedding') {
|
} else if (p.type === 'embedding') {
|
||||||
$c.find('#amily2_pf_dimensions').val(p.dimensions ?? '');
|
$c.find('#amily2_pf_dimensions').val(p.dimensions ?? '');
|
||||||
$c.find('#amily2_pf_encoding_format').val(p.encodingFormat);
|
$c.find('#amily2_pf_encoding_format').val(p.encodingFormat);
|
||||||
@@ -362,6 +423,7 @@ async function openModal($c, id) {
|
|||||||
_handleProviderChange($c, 'openai');
|
_handleProviderChange($c, 'openai');
|
||||||
$c.find('#amily2_pf_max_tokens').val(65500);
|
$c.find('#amily2_pf_max_tokens').val(65500);
|
||||||
$c.find('#amily2_pf_temperature').val(1.0);
|
$c.find('#amily2_pf_temperature').val(1.0);
|
||||||
|
$c.find('#amily2_pf_fake_stream').prop('checked', false);
|
||||||
$c.find('#amily2_pf_dimensions').val('');
|
$c.find('#amily2_pf_dimensions').val('');
|
||||||
$c.find('#amily2_pf_encoding_format').val('float');
|
$c.find('#amily2_pf_encoding_format').val('float');
|
||||||
$c.find('#amily2_pf_top_n').val(5);
|
$c.find('#amily2_pf_top_n').val(5);
|
||||||
@@ -401,6 +463,7 @@ async function saveProfile($c) {
|
|||||||
if (type === 'chat') {
|
if (type === 'chat') {
|
||||||
data.maxTokens = parseInt($c.find('#amily2_pf_max_tokens').val(), 10) || 65500;
|
data.maxTokens = parseInt($c.find('#amily2_pf_max_tokens').val(), 10) || 65500;
|
||||||
data.temperature = parseFloat($c.find('#amily2_pf_temperature').val()) || 1.0;
|
data.temperature = parseFloat($c.find('#amily2_pf_temperature').val()) || 1.0;
|
||||||
|
data.fakeStream = $c.find('#amily2_pf_fake_stream').prop('checked');
|
||||||
} else if (type === 'embedding') {
|
} else if (type === 'embedding') {
|
||||||
const dim = $c.find('#amily2_pf_dimensions').val();
|
const dim = $c.find('#amily2_pf_dimensions').val();
|
||||||
data.dimensions = dim ? parseInt(dim, 10) : null;
|
data.dimensions = dim ? parseInt(dim, 10) : null;
|
||||||
@@ -582,8 +645,39 @@ async function _testConnection($c) {
|
|||||||
|
|
||||||
if (modelsResp.ok) {
|
if (modelsResp.ok) {
|
||||||
const rawData = await modelsResp.json();
|
const rawData = await modelsResp.json();
|
||||||
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 : [];
|
||||||
const count = list.length;
|
const count = list.length;
|
||||||
|
|
||||||
|
// chat 类型额外发一次假补全,验证 completion 端点也能正常鉴权
|
||||||
|
const type = $c.find('#amily2_pf_type').val();
|
||||||
|
const $sel = $c.find('#amily2_pf_model_select');
|
||||||
|
const model = ($sel.is(':visible') ? $sel.val() : $c.find('#amily2_pf_model').val()).trim();
|
||||||
|
|
||||||
|
if (type === 'chat' && model) {
|
||||||
|
$result.text('模型列表 ✓,正在验证补全端点…').css('color', 'var(--SmartThemeQuoteColor)');
|
||||||
|
const genResp = await fetch('/api/backends/chat-completions/generate', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { ...getRequestHeaders(), 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
reverse_proxy: apiUrl,
|
||||||
|
proxy_password: apiKey,
|
||||||
|
chat_completion_source: 'openai',
|
||||||
|
model,
|
||||||
|
messages: [{ role: 'user', content: 'Hi' }],
|
||||||
|
max_tokens: 1,
|
||||||
|
stream: false,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
if (!genResp.ok) {
|
||||||
|
const genErr = await genResp.json().catch(() => ({}));
|
||||||
|
const genMsg = genErr?.error?.message || `补全端点返回 HTTP ${genResp.status}`;
|
||||||
|
$result.text(`模型列表 ✓,补全失败:${genMsg}`).css('color', 'var(--warning-color)');
|
||||||
|
toastr.warning(`补全端点测试失败:${genMsg}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$result.text(`连接成功${count ? `,${count} 个可用模型` : ''}`).css('color', 'var(--green)');
|
$result.text(`连接成功${count ? `,${count} 个可用模型` : ''}`).css('color', 'var(--green)');
|
||||||
toastr.success('连接测试通过!');
|
toastr.success('连接测试通过!');
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { defaultSettings, extensionName, saveSettings, extensionBasePath } from
|
|||||||
import { pluginAuthStatus, activatePluginAuthorization, getPasswordForDate } from "../utils/auth.js";
|
import { pluginAuthStatus, activatePluginAuthorization, getPasswordForDate } from "../utils/auth.js";
|
||||||
import { fetchModels, testApiConnection } from "../core/api.js";
|
import { fetchModels, testApiConnection } from "../core/api.js";
|
||||||
import { safeLorebooks, safeCharLorebooks, safeLorebookEntries } from "../core/tavernhelper-compatibility.js";
|
import { safeLorebooks, safeCharLorebooks, safeLorebookEntries } from "../core/tavernhelper-compatibility.js";
|
||||||
|
import { configManager } from '../utils/config/ConfigManager.js';
|
||||||
|
|
||||||
import { setAvailableModels, populateModelDropdown, getLatestUpdateInfo } from "./state.js";
|
import { setAvailableModels, populateModelDropdown, getLatestUpdateInfo } from "./state.js";
|
||||||
import { fixCommand, testReplyChecker } from "../core/commands.js";
|
import { fixCommand, testReplyChecker } from "../core/commands.js";
|
||||||
@@ -1034,11 +1035,16 @@ export function bindModalEvents() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
container
|
container
|
||||||
.off("change.amily2.text")
|
.off("input.amily2.text change.amily2.text")
|
||||||
.on("change.amily2.text", "#amily2_api_url, #amily2_api_key, #amily2_optimization_target_tag", function () {
|
.on("input.amily2.text change.amily2.text", "#amily2_api_url, #amily2_api_key, #amily2_optimization_target_tag", function () {
|
||||||
if (!pluginAuthStatus.authorized) return;
|
if (!pluginAuthStatus.authorized) return;
|
||||||
const key = snakeToCamel(this.id.replace("amily2_", ""));
|
const key = snakeToCamel(this.id.replace("amily2_", ""));
|
||||||
updateAndSaveSetting(key, this.value);
|
// apiKey 是敏感字段,必须经 configManager 写入 localStorage
|
||||||
|
if (key === 'apiKey') {
|
||||||
|
configManager.set(key, this.value);
|
||||||
|
} else {
|
||||||
|
updateAndSaveSetting(key, this.value);
|
||||||
|
}
|
||||||
toastr.success(`配置 [${key}] 已自动保存!`, "Amily2号");
|
toastr.success(`配置 [${key}] 已自动保存!`, "Amily2号");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1076,6 +1082,25 @@ export function bindModalEvents() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
container
|
||||||
|
.off("input.amily2.number change.amily2.number")
|
||||||
|
.on(
|
||||||
|
"input.amily2.number change.amily2.number",
|
||||||
|
"#amily2_max_tokens, #amily2_temperature, #amily2_context_messages",
|
||||||
|
function () {
|
||||||
|
if (!pluginAuthStatus.authorized) return;
|
||||||
|
const key = snakeToCamel(this.id.replace("amily2_", ""));
|
||||||
|
const value = this.id.includes("temperature")
|
||||||
|
? parseFloat(this.value)
|
||||||
|
: parseInt(this.value, 10);
|
||||||
|
|
||||||
|
if (Number.isNaN(value)) return;
|
||||||
|
|
||||||
|
$(`#${this.id}_value`).text(value);
|
||||||
|
updateAndSaveSetting(key, value);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const promptMap = {
|
const promptMap = {
|
||||||
mainPrompt: "#amily2_main_prompt",
|
mainPrompt: "#amily2_main_prompt",
|
||||||
systemPrompt: "#amily2_system_prompt",
|
systemPrompt: "#amily2_system_prompt",
|
||||||
@@ -1097,6 +1122,14 @@ export function bindModalEvents() {
|
|||||||
.off("change.amily2.prompt_selector")
|
.off("change.amily2.prompt_selector")
|
||||||
.on("change.amily2.prompt_selector", selector, updateEditorView);
|
.on("change.amily2.prompt_selector", selector, updateEditorView);
|
||||||
|
|
||||||
|
container
|
||||||
|
.off("input.amily2.unified_editor change.amily2.unified_editor")
|
||||||
|
.on("input.amily2.unified_editor change.amily2.unified_editor", editor, function () {
|
||||||
|
const selectedKey = $(selector).val();
|
||||||
|
if (!selectedKey) return;
|
||||||
|
updateAndSaveSetting(selectedKey, $(this).val());
|
||||||
|
});
|
||||||
|
|
||||||
container
|
container
|
||||||
.off("click.amily2.unified_save")
|
.off("click.amily2.unified_save")
|
||||||
.on("click.amily2.unified_save", unifiedSaveButton, function () {
|
.on("click.amily2.unified_save", unifiedSaveButton, function () {
|
||||||
@@ -1119,8 +1152,8 @@ export function bindModalEvents() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
container
|
container
|
||||||
.off("change.amily2.lore_settings")
|
.off("input.amily2.lore_settings change.amily2.lore_settings")
|
||||||
.on("change.amily2.lore_settings",
|
.on("input.amily2.lore_settings change.amily2.lore_settings",
|
||||||
'select[id^="amily2_lore_"], input#amily2_lore_depth_input',
|
'select[id^="amily2_lore_"], input#amily2_lore_depth_input',
|
||||||
function () {
|
function () {
|
||||||
if (!pluginAuthStatus.authorized) return;
|
if (!pluginAuthStatus.authorized) return;
|
||||||
|
|||||||
@@ -16,6 +16,13 @@ import {
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
function escapeTextareaContent(text) {
|
||||||
|
return String(text ?? '')
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>');
|
||||||
|
}
|
||||||
|
|
||||||
function setupGlobalEventHandlers() {
|
function setupGlobalEventHandlers() {
|
||||||
|
|
||||||
window.saveHLYSettings = () => saveSettingsFromUI(false); // false表示非自动保存
|
window.saveHLYSettings = () => saveSettingsFromUI(false); // false表示非自动保存
|
||||||
@@ -1758,7 +1765,7 @@ function previewCondensation() {
|
|||||||
<textarea class="hly-preview-textarea"
|
<textarea class="hly-preview-textarea"
|
||||||
data-floor="${item.floor}"
|
data-floor="${item.floor}"
|
||||||
data-is-user="${item.is_user}"
|
data-is-user="${item.is_user}"
|
||||||
data-send-date="${item.send_date}">${item.content}</textarea>
|
data-send-date="${item.send_date}">${escapeTextareaContent(item.content)}</textarea>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
<button class="hly-preview-delete-btn-v2" data-target="${item.id}" title="删除此条">×</button>
|
<button class="hly-preview-delete-btn-v2" data-target="${item.id}" title="删除此条">×</button>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
} from "../utils/settings.js";
|
} from "../utils/settings.js";
|
||||||
import { showHtmlModal } from './page-window.js';
|
import { showHtmlModal } from './page-window.js';
|
||||||
import { applyExclusionRules, extractBlocksByTags } from '../core/utils/rag-tag-extractor.js';
|
import { applyExclusionRules, extractBlocksByTags } from '../core/utils/rag-tag-extractor.js';
|
||||||
|
import { configManager } from '../utils/config/ConfigManager.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getAvailableWorldbooks, getLoresForWorldbook,
|
getAvailableWorldbooks, getLoresForWorldbook,
|
||||||
@@ -459,16 +460,23 @@ function bindNgmsApiEvents() {
|
|||||||
// API配置字段绑定
|
// API配置字段绑定
|
||||||
const apiFields = [
|
const apiFields = [
|
||||||
{ id: 'amily2_ngms_api_url', key: 'ngmsApiUrl' },
|
{ id: 'amily2_ngms_api_url', key: 'ngmsApiUrl' },
|
||||||
{ id: 'amily2_ngms_api_key', key: 'ngmsApiKey' },
|
{ id: 'amily2_ngms_api_key', key: 'ngmsApiKey', sensitive: true },
|
||||||
{ id: 'amily2_ngms_model', key: 'ngmsModel' }
|
{ id: 'amily2_ngms_model', key: 'ngmsModel' }
|
||||||
];
|
];
|
||||||
|
|
||||||
apiFields.forEach(field => {
|
apiFields.forEach(field => {
|
||||||
const element = document.getElementById(field.id);
|
const element = document.getElementById(field.id);
|
||||||
if (element) {
|
if (element) {
|
||||||
element.value = extension_settings[extensionName][field.key] || '';
|
// 敏感字段(API Key)从 configManager(localStorage)读取
|
||||||
|
element.value = field.sensitive
|
||||||
|
? (configManager.get(field.key) || '')
|
||||||
|
: (extension_settings[extensionName][field.key] || '');
|
||||||
element.addEventListener('change', function() {
|
element.addEventListener('change', function() {
|
||||||
updateAndSaveSetting(field.key, this.value);
|
if (field.sensitive) {
|
||||||
|
configManager.set(field.key, this.value);
|
||||||
|
} else {
|
||||||
|
updateAndSaveSetting(field.key, this.value);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import { testConcurrentApiConnection, fetchConcurrentModels } from '../core/api/
|
|||||||
import { safeLorebooks, safeCharLorebooks, safeLorebookEntries } from "../core/tavernhelper-compatibility.js";
|
import { safeLorebooks, safeCharLorebooks, safeLorebookEntries } from "../core/tavernhelper-compatibility.js";
|
||||||
import { createDrawer } from '../ui/drawer.js';
|
import { createDrawer } from '../ui/drawer.js';
|
||||||
import { pluginAuthStatus } from "../utils/auth.js";
|
import { pluginAuthStatus } from "../utils/auth.js";
|
||||||
|
import { configManager } from '../utils/config/ConfigManager.js';
|
||||||
|
import { SENSITIVE_KEYS } from '../utils/config/sensitive-keys.js';
|
||||||
|
|
||||||
// ========== Prompt Cache (module-level state) ==========
|
// ========== Prompt Cache (module-level state) ==========
|
||||||
|
|
||||||
@@ -161,6 +163,9 @@ async function opt_saveSetting(key, value) {
|
|||||||
console.error(`[${extensionName}] 保存角色数据失败:`, error);
|
console.error(`[${extensionName}] 保存角色数据失败:`, error);
|
||||||
toastr.error('无法保存角色卡设置,请检查控制台。');
|
toastr.error('无法保存角色卡设置,请检查控制台。');
|
||||||
}
|
}
|
||||||
|
} else if (SENSITIVE_KEYS.has(key)) {
|
||||||
|
// 敏感字段(API Key)经 configManager 写入 localStorage
|
||||||
|
configManager.set(key, value);
|
||||||
} else {
|
} else {
|
||||||
if (!extension_settings[extensionName]) {
|
if (!extension_settings[extensionName]) {
|
||||||
extension_settings[extensionName] = {};
|
extension_settings[extensionName] = {};
|
||||||
@@ -179,6 +184,25 @@ function opt_getMergedSettings() {
|
|||||||
return { ...globalSettings, ...characterSettings };
|
return { ...globalSettings, ...characterSettings };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function bindInputLikeSave(element, handler) {
|
||||||
|
if (!element) return;
|
||||||
|
element.oninput = handler;
|
||||||
|
element.onchange = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncModelMirror(inputElement, selectElement) {
|
||||||
|
if (!inputElement || !selectElement) return;
|
||||||
|
const value = inputElement.value || '';
|
||||||
|
if (!value) return;
|
||||||
|
|
||||||
|
let option = Array.from(selectElement.options || []).find(item => item.value === value);
|
||||||
|
if (!option) {
|
||||||
|
option = new Option(value, value, true, true);
|
||||||
|
selectElement.add(option);
|
||||||
|
}
|
||||||
|
selectElement.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function opt_bindSlider(panel, sliderId, displayId) {
|
function opt_bindSlider(panel, sliderId, displayId) {
|
||||||
@@ -622,7 +646,8 @@ function opt_loadSettings(panel) {
|
|||||||
panel.find('#amily2_opt_worldbook_enabled').prop('checked', settings.plotOpt_worldbookEnabled);
|
panel.find('#amily2_opt_worldbook_enabled').prop('checked', settings.plotOpt_worldbookEnabled);
|
||||||
panel.find('#amily2_opt_new_memory_logic_enabled').prop('checked', settings.plotOpt_newMemoryLogicEnabled);
|
panel.find('#amily2_opt_new_memory_logic_enabled').prop('checked', settings.plotOpt_newMemoryLogicEnabled);
|
||||||
panel.find('#amily2_opt_api_url').val(settings.plotOpt_apiUrl);
|
panel.find('#amily2_opt_api_url').val(settings.plotOpt_apiUrl);
|
||||||
panel.find('#amily2_opt_api_key').val(settings.plotOpt_apiKey);
|
// plotOpt_apiKey 是敏感字段,从 configManager(localStorage)读取
|
||||||
|
panel.find('#amily2_opt_api_key').val(configManager.get('plotOpt_apiKey') || '');
|
||||||
|
|
||||||
const modelInput = panel.find('#amily2_opt_model');
|
const modelInput = panel.find('#amily2_opt_model');
|
||||||
const modelSelect = panel.find('#amily2_opt_model_select');
|
const modelSelect = panel.find('#amily2_opt_model_select');
|
||||||
@@ -635,14 +660,15 @@ function opt_loadSettings(panel) {
|
|||||||
modelSelect.append(new Option('<-请先获取模型', '', true, true));
|
modelSelect.append(new Option('<-请先获取模型', '', true, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
syncModelMirror(modelInput.get(0), modelSelect.get(0));
|
||||||
panel.find('#amily2_opt_max_tokens').val(settings.plotOpt_max_tokens);
|
panel.find('#amily2_opt_max_tokens').val(settings.plotOpt_max_tokens);
|
||||||
panel.find('#amily2_opt_temperature').val(settings.plotOpt_temperature);
|
panel.find('#amily2_opt_temperature').val(settings.plotOpt_temperature);
|
||||||
panel.find('#amily2_opt_top_p').val(settings.plotOpt_top_p);
|
panel.find('#amily2_opt_top_p').val(settings.plotOpt_top_p);
|
||||||
panel.find('#amily2_opt_presence_penalty').val(settings.plotOpt_presence_penalty);
|
panel.find('#amily2_opt_presence_penalty').val(settings.plotOpt_presence_penalty);
|
||||||
panel.find('#amily2_opt_frequency_penalty').val(settings.plotOpt_frequency_penalty);
|
panel.find('#amily2_opt_frequency_penalty').val(settings.plotOpt_frequency_penalty);
|
||||||
panel.find('#amily2_opt_context_turn_count').val(settings.plotOpt_contextTurnCount);
|
const contextLimit = settings.plotOpt_contextLimit ?? settings.plotOpt_contextTurnCount ?? defaultSettings.plotOpt_contextLimit;
|
||||||
panel.find('#amily2_opt_worldbook_char_limit').val(settings.plotOpt_worldbookCharLimit);
|
panel.find('#amily2_opt_worldbook_char_limit').val(settings.plotOpt_worldbookCharLimit);
|
||||||
panel.find('#amily2_opt_context_limit').val(settings.plotOpt_contextLimit);
|
panel.find('#amily2_opt_context_limit').val(contextLimit);
|
||||||
|
|
||||||
panel.find('#amily2_opt_rate_main').val(settings.plotOpt_rateMain);
|
panel.find('#amily2_opt_rate_main').val(settings.plotOpt_rateMain);
|
||||||
panel.find('#amily2_opt_rate_personal').val(settings.plotOpt_ratePersonal);
|
panel.find('#amily2_opt_rate_personal').val(settings.plotOpt_ratePersonal);
|
||||||
@@ -674,7 +700,6 @@ function opt_loadSettings(panel) {
|
|||||||
opt_bindSlider(panel, '#amily2_opt_top_p', '#amily2_opt_top_p_value');
|
opt_bindSlider(panel, '#amily2_opt_top_p', '#amily2_opt_top_p_value');
|
||||||
opt_bindSlider(panel, '#amily2_opt_presence_penalty', '#amily2_opt_presence_penalty_value');
|
opt_bindSlider(panel, '#amily2_opt_presence_penalty', '#amily2_opt_presence_penalty_value');
|
||||||
opt_bindSlider(panel, '#amily2_opt_frequency_penalty', '#amily2_opt_frequency_penalty_value');
|
opt_bindSlider(panel, '#amily2_opt_frequency_penalty', '#amily2_opt_frequency_penalty_value');
|
||||||
opt_bindSlider(panel, '#amily2_opt_context_turn_count', '#amily2_opt_context_turn_count_value');
|
|
||||||
opt_bindSlider(panel, '#amily2_opt_worldbook_char_limit', '#amily2_opt_worldbook_char_limit_value');
|
opt_bindSlider(panel, '#amily2_opt_worldbook_char_limit', '#amily2_opt_worldbook_char_limit_value');
|
||||||
opt_bindSlider(panel, '#amily2_opt_context_limit', '#amily2_opt_context_limit_value');
|
opt_bindSlider(panel, '#amily2_opt_context_limit', '#amily2_opt_context_limit_value');
|
||||||
|
|
||||||
@@ -701,14 +726,17 @@ function bindConcurrentApiEvents() {
|
|||||||
const fields = [
|
const fields = [
|
||||||
{ id: 'amily2_plotOpt_concurrentApiProvider', key: 'plotOpt_concurrentApiProvider' },
|
{ id: 'amily2_plotOpt_concurrentApiProvider', key: 'plotOpt_concurrentApiProvider' },
|
||||||
{ id: 'amily2_plotOpt_concurrentApiUrl', key: 'plotOpt_concurrentApiUrl' },
|
{ id: 'amily2_plotOpt_concurrentApiUrl', key: 'plotOpt_concurrentApiUrl' },
|
||||||
{ id: 'amily2_plotOpt_concurrentApiKey', key: 'plotOpt_concurrentApiKey' },
|
{ id: 'amily2_plotOpt_concurrentApiKey', key: 'plotOpt_concurrentApiKey', sensitive: true },
|
||||||
{ id: 'amily2_plotOpt_concurrentModel', key: 'plotOpt_concurrentModel' }
|
{ id: 'amily2_plotOpt_concurrentModel', key: 'plotOpt_concurrentModel' }
|
||||||
];
|
];
|
||||||
|
|
||||||
fields.forEach(field => {
|
fields.forEach(field => {
|
||||||
const element = document.getElementById(field.id);
|
const element = document.getElementById(field.id);
|
||||||
if (element) {
|
if (element) {
|
||||||
element.value = settings[field.key] || '';
|
// 敏感字段(API Key)从 configManager(localStorage)读取
|
||||||
|
element.value = field.sensitive
|
||||||
|
? (configManager.get(field.key) || '')
|
||||||
|
: (settings[field.key] || '');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -786,11 +814,22 @@ function bindConcurrentApiEvents() {
|
|||||||
fields.forEach(field => {
|
fields.forEach(field => {
|
||||||
const element = document.getElementById(field.id);
|
const element = document.getElementById(field.id);
|
||||||
if (element) {
|
if (element) {
|
||||||
element.addEventListener('change', function() {
|
const saveField = function() {
|
||||||
if (!extension_settings[extensionName]) extension_settings[extensionName] = {};
|
if (field.sensitive) {
|
||||||
extension_settings[extensionName][field.key] = this.value;
|
configManager.set(field.key, this.value);
|
||||||
saveSettingsDebounced();
|
} else {
|
||||||
});
|
if (!extension_settings[extensionName]) extension_settings[extensionName] = {};
|
||||||
|
extension_settings[extensionName][field.key] = this.value;
|
||||||
|
saveSettingsDebounced();
|
||||||
|
if (field.key === 'plotOpt_concurrentModel') {
|
||||||
|
syncModelMirror(
|
||||||
|
document.getElementById('amily2_plotOpt_concurrentModel'),
|
||||||
|
document.getElementById('amily2_plotOpt_concurrentModel_select')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
bindInputLikeSave(element, saveField);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1179,6 +1218,13 @@ export function initializePlotOptimizationBindings() {
|
|||||||
handleSettingChange(this);
|
handleSettingChange(this);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
panel.on('input.amily2_opt change.amily2_opt', '#amily2_opt_model', function() {
|
||||||
|
syncModelMirror(
|
||||||
|
panel.find('#amily2_opt_model').get(0),
|
||||||
|
panel.find('#amily2_opt_model_select').get(0)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
panel.on('change.amily2_opt', '#amily2_opt_model_select', function() {
|
panel.on('change.amily2_opt', '#amily2_opt_model_select', function() {
|
||||||
const selectedModel = $(this).val();
|
const selectedModel = $(this).val();
|
||||||
if (selectedModel) {
|
if (selectedModel) {
|
||||||
@@ -1384,17 +1430,31 @@ function bindJqyhApiEvents() {
|
|||||||
// API配置字段绑定
|
// API配置字段绑定
|
||||||
const apiFields = [
|
const apiFields = [
|
||||||
{ id: 'amily2_jqyh_api_url', key: 'jqyhApiUrl' },
|
{ id: 'amily2_jqyh_api_url', key: 'jqyhApiUrl' },
|
||||||
{ id: 'amily2_jqyh_api_key', key: 'jqyhApiKey' },
|
{ id: 'amily2_jqyh_api_key', key: 'jqyhApiKey', sensitive: true },
|
||||||
{ id: 'amily2_jqyh_model', key: 'jqyhModel' }
|
{ id: 'amily2_jqyh_model', key: 'jqyhModel' }
|
||||||
];
|
];
|
||||||
|
|
||||||
apiFields.forEach(field => {
|
apiFields.forEach(field => {
|
||||||
const element = document.getElementById(field.id);
|
const element = document.getElementById(field.id);
|
||||||
if (element) {
|
if (element) {
|
||||||
element.value = extension_settings[extensionName][field.key] || '';
|
// 敏感字段(API Key)从 configManager(localStorage)读取
|
||||||
element.addEventListener('change', function() {
|
element.value = field.sensitive
|
||||||
updateAndSaveSetting(field.key, this.value);
|
? (configManager.get(field.key) || '')
|
||||||
});
|
: (extension_settings[extensionName][field.key] || '');
|
||||||
|
const saveField = function() {
|
||||||
|
if (field.sensitive) {
|
||||||
|
configManager.set(field.key, this.value);
|
||||||
|
} else {
|
||||||
|
updateAndSaveSetting(field.key, this.value);
|
||||||
|
if (field.key === 'jqyhModel') {
|
||||||
|
syncModelMirror(
|
||||||
|
document.getElementById('amily2_jqyh_model'),
|
||||||
|
document.getElementById('amily2_jqyh_model_select')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
bindInputLikeSave(element, saveField);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ import { testNccsApiConnection } from '../core/api/NccsApi.js';
|
|||||||
// 用于通过子元素定位父 block 的选择器
|
// 用于通过子元素定位父 block 的选择器
|
||||||
const BLOCK_SEL = '.amily2_settings_block, .control-group, .amily2_opt_settings_block';
|
const BLOCK_SEL = '.amily2_settings_block, .control-group, .amily2_opt_settings_block';
|
||||||
|
|
||||||
|
// 每个槽位在回填 Profile 值前的 DOM 字段快照(用于取消分配时还原)
|
||||||
|
// 结构:{ [slot]: { [selector]: value } }
|
||||||
|
const _fieldSnapshots = {};
|
||||||
|
|
||||||
const CARD_CLASS = 'amily2_profile_status_card';
|
const CARD_CLASS = 'amily2_profile_status_card';
|
||||||
const CARD_SLOT_ATTR = 'data-card-slot';
|
const CARD_SLOT_ATTR = 'data-card-slot';
|
||||||
const HIDDEN_ATTR = 'data-profile-hidden';
|
const HIDDEN_ATTR = 'data-profile-hidden';
|
||||||
@@ -115,11 +119,37 @@ export async function syncSlot(slot) {
|
|||||||
_removeCard(slot);
|
_removeCard(slot);
|
||||||
_restoreHidden(slot);
|
_restoreHidden(slot);
|
||||||
|
|
||||||
if (!profile) return;
|
if (!profile) {
|
||||||
|
// 取消分配:将 DOM 字段值还原为分配 Profile 前的快照,
|
||||||
|
// 防止残留的 Profile 回填值(尤其是 '••••••••' 的 Key 占位符)
|
||||||
|
// 因 blur 事件被误存入 extension_settings / localStorage。
|
||||||
|
const snap = _fieldSnapshots[slot];
|
||||||
|
if (snap) {
|
||||||
|
for (const [sel, val] of Object.entries(snap)) {
|
||||||
|
const el = document.querySelector(sel);
|
||||||
|
if (el) el.value = val;
|
||||||
|
}
|
||||||
|
delete _fieldSnapshots[slot];
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const container = _resolveContainer(config.container);
|
const container = _resolveContainer(config.container);
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
||||||
|
// 回填前先快照各字段当前值(即 extension_settings / configManager 中的真实值),
|
||||||
|
// 以便取消分配时能还原,避免 Profile 值污染旧配置。
|
||||||
|
const snap = {};
|
||||||
|
for (const sel of Object.values(config.fields || {})) {
|
||||||
|
const el = document.querySelector(sel);
|
||||||
|
if (el) snap[sel] = el.value;
|
||||||
|
}
|
||||||
|
if (config.keyField) {
|
||||||
|
const keyEl = document.querySelector(config.keyField);
|
||||||
|
if (keyEl) snap[config.keyField] = keyEl.value;
|
||||||
|
}
|
||||||
|
_fieldSnapshots[slot] = snap;
|
||||||
|
|
||||||
// 回填值(向下兼容:部分代码仍从 DOM 读取 fallback)
|
// 回填值(向下兼容:部分代码仍从 DOM 读取 fallback)
|
||||||
for (const [key, sel] of Object.entries(config.fields || {})) {
|
for (const [key, sel] of Object.entries(config.fields || {})) {
|
||||||
const el = document.querySelector(sel);
|
const el = document.querySelector(sel);
|
||||||
|
|||||||
24
ui/state.js
24
ui/state.js
@@ -2,6 +2,7 @@ import { extension_settings } from "/scripts/extensions.js";
|
|||||||
import { characters, this_chid } from '/script.js';
|
import { characters, this_chid } from '/script.js';
|
||||||
import { extensionName, defaultSettings } from "../utils/settings.js";
|
import { extensionName, defaultSettings } from "../utils/settings.js";
|
||||||
import { pluginAuthStatus } from "../utils/auth.js";
|
import { pluginAuthStatus } from "../utils/auth.js";
|
||||||
|
import { configManager } from '../utils/config/ConfigManager.js';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -82,7 +83,7 @@ export function updateUI() {
|
|||||||
$("#amily2_api_provider").val(settings.apiProvider || 'openai');
|
$("#amily2_api_provider").val(settings.apiProvider || 'openai');
|
||||||
$("#amily2_api_url").val(settings.apiUrl);
|
$("#amily2_api_url").val(settings.apiUrl);
|
||||||
$("#amily2_api_url").attr('type', 'text');
|
$("#amily2_api_url").attr('type', 'text');
|
||||||
$("#amily2_api_key").val(settings.apiKey);
|
$("#amily2_api_key").val(configManager.get('apiKey') || '');
|
||||||
$("#amily2_model").val(settings.model);
|
$("#amily2_model").val(settings.model);
|
||||||
$("#amily2_preset_selector").val(settings.tavernProfile);
|
$("#amily2_preset_selector").val(settings.tavernProfile);
|
||||||
|
|
||||||
@@ -197,10 +198,20 @@ export function updatePlotOptimizationUI() {
|
|||||||
const settings = getMergedPlotOptSettings();
|
const settings = getMergedPlotOptSettings();
|
||||||
if (!settings) return;
|
if (!settings) return;
|
||||||
|
|
||||||
|
const contextLimit = settings.plotOpt_contextLimit ?? settings.plotOpt_contextTurnCount ?? defaultSettings.plotOpt_contextLimit;
|
||||||
|
const worldbookCharLimit = settings.plotOpt_worldbookCharLimit ?? defaultSettings.plotOpt_worldbookCharLimit;
|
||||||
|
const worldbookEnabled = settings.plotOpt_worldbookEnabled ?? settings.plotOpt_worldbook_enabled ?? defaultSettings.plotOpt_worldbookEnabled;
|
||||||
|
let tableEnabledValue = settings.plotOpt_tableEnabled;
|
||||||
|
if (tableEnabledValue === true) {
|
||||||
|
tableEnabledValue = 'main';
|
||||||
|
} else if (tableEnabledValue === false || tableEnabledValue === undefined) {
|
||||||
|
tableEnabledValue = 'disabled';
|
||||||
|
}
|
||||||
|
|
||||||
$('#amily2_opt_enabled').prop('checked', settings.plotOpt_enabled);
|
$('#amily2_opt_enabled').prop('checked', settings.plotOpt_enabled);
|
||||||
$('#amily2_opt_ejs_enabled').prop('checked', settings.plotOpt_ejsEnabled);
|
$('#amily2_opt_ejs_enabled').prop('checked', settings.plotOpt_ejsEnabled);
|
||||||
$('#amily2_opt_worldbook_enabled').prop('checked', settings.plotOpt_worldbook_enabled);
|
$('#amily2_opt_worldbook_enabled').prop('checked', worldbookEnabled);
|
||||||
$('#amily2_opt_table_enabled').prop('checked', settings.plotOpt_tableEnabled);
|
$('#amily2_opt_table_enabled').val(tableEnabledValue);
|
||||||
|
|
||||||
$('#amily2_opt_main_prompt').val(settings.plotOpt_mainPrompt);
|
$('#amily2_opt_main_prompt').val(settings.plotOpt_mainPrompt);
|
||||||
$('#amily2_opt_system_prompt').val(settings.plotOpt_systemPrompt);
|
$('#amily2_opt_system_prompt').val(settings.plotOpt_systemPrompt);
|
||||||
@@ -212,13 +223,12 @@ export function updatePlotOptimizationUI() {
|
|||||||
$('#amily2_opt_rate_cuckold').val(settings.plotOpt_rateCuckold);
|
$('#amily2_opt_rate_cuckold').val(settings.plotOpt_rateCuckold);
|
||||||
|
|
||||||
const sliders = {
|
const sliders = {
|
||||||
'#amily2_opt_context_limit': 'plotOpt_contextLimit',
|
'#amily2_opt_context_limit': contextLimit,
|
||||||
'#amily2_opt_worldbook_char_limit': 'plotOpt_worldbookCharLimit',
|
'#amily2_opt_worldbook_char_limit': worldbookCharLimit,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const sliderId in sliders) {
|
for (const sliderId in sliders) {
|
||||||
const key = sliders[sliderId];
|
const value = sliders[sliderId];
|
||||||
const value = settings[key];
|
|
||||||
const valueDisplayId = `${sliderId}_value`;
|
const valueDisplayId = `${sliderId}_value`;
|
||||||
|
|
||||||
if (value !== undefined) {
|
if (value !== undefined) {
|
||||||
|
|||||||
@@ -13,10 +13,26 @@ import { characters, this_chid, eventSource, event_types } from "/script.js";
|
|||||||
import { fetchNccsModels, testNccsApiConnection } from '../core/api/NccsApi.js';
|
import { fetchNccsModels, testNccsApiConnection } from '../core/api/NccsApi.js';
|
||||||
import { showGraphVisualization } from '../core/relationship-graph/visualizer.js';
|
import { showGraphVisualization } from '../core/relationship-graph/visualizer.js';
|
||||||
import { escapeHTML } from '../utils/utils.js';
|
import { escapeHTML } from '../utils/utils.js';
|
||||||
|
import { configManager } from '../utils/config/ConfigManager.js';
|
||||||
|
import { bindTableTemplateEditors } from './table/template-bindings.js';
|
||||||
|
import { bindNccsApiEvents as bindNccsApiSettingsEvents } from './table/nccs-bindings.js';
|
||||||
|
import { bindChatTableDisplaySetting as bindChatTableDisplaySettings } from './table/chat-display-bindings.js';
|
||||||
|
|
||||||
const isTouchDevice = () => window.matchMedia('(pointer: coarse)').matches;
|
const isTouchDevice = () => window.matchMedia('(pointer: coarse)').matches;
|
||||||
const getAllTablesContainer = () => document.getElementById('all-tables-container');
|
const getAllTablesContainer = () => document.getElementById('all-tables-container');
|
||||||
|
|
||||||
|
function getLiveExtensionSettings() {
|
||||||
|
if (!extension_settings[extensionName]) {
|
||||||
|
extension_settings[extensionName] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return extension_settings[extensionName];
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTableSystemEnabled() {
|
||||||
|
return getLiveExtensionSettings().table_system_enabled !== false;
|
||||||
|
}
|
||||||
|
|
||||||
let isResizing = false;
|
let isResizing = false;
|
||||||
let activeTableIndex = 0; // 【V155.0】当前激活的表格索引
|
let activeTableIndex = 0; // 【V155.0】当前激活的表格索引
|
||||||
|
|
||||||
@@ -767,7 +783,7 @@ export function renderTables() {
|
|||||||
|
|
||||||
|
|
||||||
function openTableRuleEditor() {
|
function openTableRuleEditor() {
|
||||||
const settings = extension_settings[extensionName];
|
const settings = getLiveExtensionSettings();
|
||||||
const tags = settings.table_tags_to_extract || '';
|
const tags = settings.table_tags_to_extract || '';
|
||||||
const exclusionRules = settings.table_exclusion_rules || [];
|
const exclusionRules = settings.table_exclusion_rules || [];
|
||||||
|
|
||||||
@@ -1010,8 +1026,6 @@ function openRuleEditor(tableIndex) {
|
|||||||
|
|
||||||
|
|
||||||
function bindInjectionSettings() {
|
function bindInjectionSettings() {
|
||||||
const settings = extension_settings[extensionName];
|
|
||||||
|
|
||||||
const masterSwitchCheckbox = document.getElementById('table-system-master-switch');
|
const masterSwitchCheckbox = document.getElementById('table-system-master-switch');
|
||||||
const enabledCheckbox = document.getElementById('table-injection-enabled');
|
const enabledCheckbox = document.getElementById('table-injection-enabled');
|
||||||
const optimizationCheckbox = document.getElementById('context-optimization-enabled'); // 【V144.0】
|
const optimizationCheckbox = document.getElementById('context-optimization-enabled'); // 【V144.0】
|
||||||
@@ -1023,6 +1037,15 @@ function bindInjectionSettings() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getLiveSettings = () => {
|
||||||
|
const liveSettings = getLiveExtensionSettings();
|
||||||
|
if (!liveSettings.injection) {
|
||||||
|
liveSettings.injection = { position: 1, depth: 0, role: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
return liveSettings;
|
||||||
|
};
|
||||||
|
|
||||||
const updateInjectionUI = () => {
|
const updateInjectionUI = () => {
|
||||||
const position = positionSelect.value;
|
const position = positionSelect.value;
|
||||||
const masterEnabled = masterSwitchCheckbox.checked;
|
const masterEnabled = masterSwitchCheckbox.checked;
|
||||||
@@ -1076,6 +1099,7 @@ function bindInjectionSettings() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const settings = getLiveSettings();
|
||||||
masterSwitchCheckbox.checked = settings.table_system_enabled !== false;
|
masterSwitchCheckbox.checked = settings.table_system_enabled !== false;
|
||||||
enabledCheckbox.checked = settings.table_injection_enabled;
|
enabledCheckbox.checked = settings.table_injection_enabled;
|
||||||
if (optimizationCheckbox) { // 【V144.0】
|
if (optimizationCheckbox) { // 【V144.0】
|
||||||
@@ -1094,7 +1118,8 @@ function bindInjectionSettings() {
|
|||||||
if (masterSwitchCheckbox.dataset.eventsBound) return;
|
if (masterSwitchCheckbox.dataset.eventsBound) return;
|
||||||
|
|
||||||
masterSwitchCheckbox.addEventListener('change', () => {
|
masterSwitchCheckbox.addEventListener('change', () => {
|
||||||
settings.table_system_enabled = masterSwitchCheckbox.checked;
|
const currentSettings = getLiveSettings();
|
||||||
|
currentSettings.table_system_enabled = masterSwitchCheckbox.checked;
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
updateInjectionUI();
|
updateInjectionUI();
|
||||||
|
|
||||||
@@ -1104,35 +1129,40 @@ function bindInjectionSettings() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
enabledCheckbox.addEventListener('change', () => {
|
enabledCheckbox.addEventListener('change', () => {
|
||||||
settings.table_injection_enabled = enabledCheckbox.checked;
|
const currentSettings = getLiveSettings();
|
||||||
|
currentSettings.table_injection_enabled = enabledCheckbox.checked;
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 【V144.0】
|
// 【V144.0】
|
||||||
if (optimizationCheckbox) {
|
if (optimizationCheckbox) {
|
||||||
optimizationCheckbox.addEventListener('change', () => {
|
optimizationCheckbox.addEventListener('change', () => {
|
||||||
settings.context_optimization_enabled = optimizationCheckbox.checked;
|
const currentSettings = getLiveSettings();
|
||||||
|
currentSettings.context_optimization_enabled = optimizationCheckbox.checked;
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
toastr.info(`上下文优化(世界书合并)已${optimizationCheckbox.checked ? '启用' : '禁用'}。`);
|
toastr.info(`上下文优化(世界书合并)已${optimizationCheckbox.checked ? '启用' : '禁用'}。`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
positionSelect.addEventListener('change', () => {
|
positionSelect.addEventListener('change', () => {
|
||||||
settings.injection.position = parseInt(positionSelect.value, 10);
|
const currentSettings = getLiveSettings();
|
||||||
|
currentSettings.injection.position = parseInt(positionSelect.value, 10);
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
|
|
||||||
updateInjectionUI();
|
updateInjectionUI();
|
||||||
});
|
});
|
||||||
|
|
||||||
depthInput.addEventListener('input', () => {
|
depthInput.addEventListener('input', () => {
|
||||||
settings.injection.depth = parseInt(depthInput.value, 10);
|
const currentSettings = getLiveSettings();
|
||||||
|
currentSettings.injection.depth = parseInt(depthInput.value, 10);
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
roleRadioGroup.forEach(radio => {
|
roleRadioGroup.forEach(radio => {
|
||||||
radio.addEventListener('change', () => {
|
radio.addEventListener('change', () => {
|
||||||
if (radio.checked) {
|
if (radio.checked) {
|
||||||
settings.injection.role = parseInt(radio.value, 10);
|
const currentSettings = getLiveSettings();
|
||||||
|
currentSettings.injection.role = parseInt(radio.value, 10);
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1144,15 +1174,12 @@ function bindInjectionSettings() {
|
|||||||
|
|
||||||
|
|
||||||
function updateAndSaveTableSetting(key, value) {
|
function updateAndSaveTableSetting(key, value) {
|
||||||
if (!extension_settings[extensionName]) {
|
getLiveExtensionSettings()[key] = value;
|
||||||
extension_settings[extensionName] = {};
|
|
||||||
}
|
|
||||||
extension_settings[extensionName][key] = value;
|
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
}
|
}
|
||||||
|
|
||||||
function bindWorldBookSettings() {
|
function bindWorldBookSettings() {
|
||||||
const settings = extension_settings[extensionName];
|
const settings = getLiveExtensionSettings();
|
||||||
|
|
||||||
if (settings.table_worldbook_enabled === undefined) settings.table_worldbook_enabled = false;
|
if (settings.table_worldbook_enabled === undefined) settings.table_worldbook_enabled = false;
|
||||||
if (settings.table_worldbook_char_limit === undefined) settings.table_worldbook_char_limit = 30000;
|
if (settings.table_worldbook_char_limit === undefined) settings.table_worldbook_char_limit = 30000;
|
||||||
@@ -1168,6 +1195,8 @@ function bindWorldBookSettings() {
|
|||||||
const refreshButton = document.getElementById('table_refresh_worldbooks');
|
const refreshButton = document.getElementById('table_refresh_worldbooks');
|
||||||
const bookListContainer = document.getElementById('table_worldbook_checkbox_list');
|
const bookListContainer = document.getElementById('table_worldbook_checkbox_list');
|
||||||
const entryListContainer = document.getElementById('table_worldbook_entry_list');
|
const entryListContainer = document.getElementById('table_worldbook_entry_list');
|
||||||
|
const bookSearchInput = document.getElementById('table_worldbook_search');
|
||||||
|
const entrySearchInput = document.getElementById('table_entry_search');
|
||||||
|
|
||||||
if (!enabledCheckbox || !limitSlider || !limitValueSpan || !sourceRadios.length || !manualSelectWrapper || !refreshButton || !bookListContainer || !entryListContainer) {
|
if (!enabledCheckbox || !limitSlider || !limitValueSpan || !sourceRadios.length || !manualSelectWrapper || !refreshButton || !bookListContainer || !entryListContainer) {
|
||||||
log('无法找到世界书设置的相关UI元素,绑定失败。', 'warn');
|
log('无法找到世界书设置的相关UI元素,绑定失败。', 'warn');
|
||||||
@@ -1175,6 +1204,7 @@ function bindWorldBookSettings() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const saveSelectedEntries = () => {
|
const saveSelectedEntries = () => {
|
||||||
|
const currentSettings = getLiveExtensionSettings();
|
||||||
const selected = {};
|
const selected = {};
|
||||||
entryListContainer.querySelectorAll('input[type="checkbox"]:checked').forEach(cb => {
|
entryListContainer.querySelectorAll('input[type="checkbox"]:checked').forEach(cb => {
|
||||||
const book = cb.dataset.book;
|
const book = cb.dataset.book;
|
||||||
@@ -1184,17 +1214,18 @@ function bindWorldBookSettings() {
|
|||||||
}
|
}
|
||||||
selected[book].push(uid);
|
selected[book].push(uid);
|
||||||
});
|
});
|
||||||
settings.table_selected_entries = selected;
|
currentSettings.table_selected_entries = selected;
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderWorldBookEntries = async () => {
|
const renderWorldBookEntries = async () => {
|
||||||
entryListContainer.innerHTML = '<p>加载条目中...</p>';
|
entryListContainer.innerHTML = '<p>加载条目中...</p>';
|
||||||
const source = settings.table_worldbook_source || 'character';
|
const currentSettings = getLiveExtensionSettings();
|
||||||
|
const source = currentSettings.table_worldbook_source || 'character';
|
||||||
let bookNames = [];
|
let bookNames = [];
|
||||||
|
|
||||||
if (source === 'manual') {
|
if (source === 'manual') {
|
||||||
bookNames = settings.table_selected_worldbooks || [];
|
bookNames = currentSettings.table_selected_worldbooks || [];
|
||||||
} else {
|
} else {
|
||||||
if (this_chid !== undefined && this_chid >= 0 && characters[this_chid]) {
|
if (this_chid !== undefined && this_chid >= 0 && characters[this_chid]) {
|
||||||
try {
|
try {
|
||||||
@@ -1241,7 +1272,7 @@ function bindWorldBookSettings() {
|
|||||||
checkbox.dataset.book = entry.bookName;
|
checkbox.dataset.book = entry.bookName;
|
||||||
checkbox.dataset.uid = entry.uid;
|
checkbox.dataset.uid = entry.uid;
|
||||||
|
|
||||||
const isChecked = settings.table_selected_entries[entry.bookName]?.includes(String(entry.uid));
|
const isChecked = currentSettings.table_selected_entries[entry.bookName]?.includes(String(entry.uid));
|
||||||
checkbox.checked = !!isChecked;
|
checkbox.checked = !!isChecked;
|
||||||
|
|
||||||
const label = document.createElement('label');
|
const label = document.createElement('label');
|
||||||
@@ -1271,15 +1302,16 @@ function bindWorldBookSettings() {
|
|||||||
checkbox.type = 'checkbox';
|
checkbox.type = 'checkbox';
|
||||||
checkbox.id = `wb-check-${book.file_name}`;
|
checkbox.id = `wb-check-${book.file_name}`;
|
||||||
checkbox.value = book.file_name;
|
checkbox.value = book.file_name;
|
||||||
checkbox.checked = settings.table_selected_worldbooks.includes(book.file_name);
|
checkbox.checked = getLiveExtensionSettings().table_selected_worldbooks.includes(book.file_name);
|
||||||
|
|
||||||
checkbox.addEventListener('change', () => {
|
checkbox.addEventListener('change', () => {
|
||||||
|
const currentSettings = getLiveExtensionSettings();
|
||||||
if (checkbox.checked) {
|
if (checkbox.checked) {
|
||||||
if (!settings.table_selected_worldbooks.includes(book.file_name)) {
|
if (!currentSettings.table_selected_worldbooks.includes(book.file_name)) {
|
||||||
settings.table_selected_worldbooks.push(book.file_name);
|
currentSettings.table_selected_worldbooks.push(book.file_name);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
settings.table_selected_worldbooks = settings.table_selected_worldbooks.filter(name => name !== book.file_name);
|
currentSettings.table_selected_worldbooks = currentSettings.table_selected_worldbooks.filter(name => name !== book.file_name);
|
||||||
}
|
}
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
renderWorldBookEntries();
|
renderWorldBookEntries();
|
||||||
@@ -1300,7 +1332,7 @@ function bindWorldBookSettings() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateManualSelectVisibility = () => {
|
const updateManualSelectVisibility = () => {
|
||||||
const isManual = settings.table_worldbook_source === 'manual';
|
const isManual = getLiveExtensionSettings().table_worldbook_source === 'manual';
|
||||||
manualSelectWrapper.style.display = isManual ? 'block' : 'none';
|
manualSelectWrapper.style.display = isManual ? 'block' : 'none';
|
||||||
renderWorldBookEntries();
|
renderWorldBookEntries();
|
||||||
if (isManual) {
|
if (isManual) {
|
||||||
@@ -1320,20 +1352,23 @@ function bindWorldBookSettings() {
|
|||||||
if (enabledCheckbox.dataset.eventsBound) return;
|
if (enabledCheckbox.dataset.eventsBound) return;
|
||||||
|
|
||||||
enabledCheckbox.addEventListener('change', () => {
|
enabledCheckbox.addEventListener('change', () => {
|
||||||
settings.table_worldbook_enabled = enabledCheckbox.checked;
|
const currentSettings = getLiveExtensionSettings();
|
||||||
|
currentSettings.table_worldbook_enabled = enabledCheckbox.checked;
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
limitSlider.addEventListener('input', () => { limitValueSpan.textContent = limitSlider.value; });
|
limitSlider.addEventListener('input', () => { limitValueSpan.textContent = limitSlider.value; });
|
||||||
limitSlider.addEventListener('change', () => {
|
limitSlider.addEventListener('change', () => {
|
||||||
settings.table_worldbook_char_limit = parseInt(limitSlider.value, 10);
|
const currentSettings = getLiveExtensionSettings();
|
||||||
|
currentSettings.table_worldbook_char_limit = parseInt(limitSlider.value, 10);
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
sourceRadios.forEach(radio => {
|
sourceRadios.forEach(radio => {
|
||||||
radio.addEventListener('change', () => {
|
radio.addEventListener('change', () => {
|
||||||
if (radio.checked) {
|
if (radio.checked) {
|
||||||
settings.table_worldbook_source = radio.value;
|
const currentSettings = getLiveExtensionSettings();
|
||||||
|
currentSettings.table_worldbook_source = radio.value;
|
||||||
updateManualSelectVisibility();
|
updateManualSelectVisibility();
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
}
|
}
|
||||||
@@ -1347,12 +1382,32 @@ function bindWorldBookSettings() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (bookSearchInput) {
|
||||||
|
bookSearchInput.addEventListener('input', () => {
|
||||||
|
const keyword = bookSearchInput.value.trim().toLowerCase();
|
||||||
|
bookListContainer.querySelectorAll('.checkbox-item').forEach(item => {
|
||||||
|
const text = item.textContent.toLowerCase();
|
||||||
|
item.style.display = text.includes(keyword) ? '' : 'none';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entrySearchInput) {
|
||||||
|
entrySearchInput.addEventListener('input', () => {
|
||||||
|
const keyword = entrySearchInput.value.trim().toLowerCase();
|
||||||
|
entryListContainer.querySelectorAll('.checkbox-item').forEach(item => {
|
||||||
|
const text = item.textContent.toLowerCase();
|
||||||
|
item.style.display = text.includes(keyword) ? '' : 'none';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
enabledCheckbox.dataset.eventsBound = 'true';
|
enabledCheckbox.dataset.eventsBound = 'true';
|
||||||
log('世界书设置已成功绑定。', 'success');
|
log('世界书设置已成功绑定。', 'success');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bindTableEvents() {
|
export function bindTableEvents(panelElement = null) {
|
||||||
const panel = document.getElementById('amily2_memorisation_forms_panel');
|
const panel = panelElement || document.getElementById('amily2_memorisation_forms_panel');
|
||||||
if (!panel || panel.dataset.eventsBound) {
|
if (!panel || panel.dataset.eventsBound) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1462,7 +1517,12 @@ export function bindTableEvents() {
|
|||||||
const renderAll = () => {
|
const renderAll = () => {
|
||||||
renderTables();
|
renderTables();
|
||||||
bindInjectionSettings();
|
bindInjectionSettings();
|
||||||
bindTemplateEditors();
|
bindTableTemplateEditors({
|
||||||
|
TableManager,
|
||||||
|
log,
|
||||||
|
defaultRuleTemplate: DEFAULT_AI_RULE_TEMPLATE,
|
||||||
|
defaultFlowTemplate: DEFAULT_AI_FLOW_TEMPLATE,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
renderAll();
|
renderAll();
|
||||||
@@ -1471,8 +1531,20 @@ export function bindTableEvents() {
|
|||||||
bindFloorFillButtons(); // 【新增】绑定楼层填表按钮
|
bindFloorFillButtons(); // 【新增】绑定楼层填表按钮
|
||||||
bindReorganizeButton(); // 【新增】绑定重新整理按钮
|
bindReorganizeButton(); // 【新增】绑定重新整理按钮
|
||||||
bindClearRecordsButton(); // 【新增】绑定清除记录按钮
|
bindClearRecordsButton(); // 【新增】绑定清除记录按钮
|
||||||
bindNccsApiEvents(); // 【新增】绑定Nccs API系统事件
|
bindNccsApiSettingsEvents({
|
||||||
bindChatTableDisplaySetting(); // 【新增】绑定聊天内表格显示开关
|
getLiveExtensionSettings,
|
||||||
|
saveSettingsDebounced,
|
||||||
|
getContext,
|
||||||
|
fetchNccsModels,
|
||||||
|
testNccsApiConnection,
|
||||||
|
configManager,
|
||||||
|
log,
|
||||||
|
}); // 【新增】绑定Nccs API系统事件
|
||||||
|
bindChatTableDisplaySettings({
|
||||||
|
getLiveExtensionSettings,
|
||||||
|
saveSettingsDebounced,
|
||||||
|
log,
|
||||||
|
}); // 【新增】绑定聊天内表格显示开关
|
||||||
|
|
||||||
const navDeck = document.querySelector('#amily2_memorisation_forms_panel .sinan-navigation-deck');
|
const navDeck = document.querySelector('#amily2_memorisation_forms_panel .sinan-navigation-deck');
|
||||||
if (navDeck) {
|
if (navDeck) {
|
||||||
@@ -1684,7 +1756,7 @@ export function bindTableEvents() {
|
|||||||
renderAll();
|
renderAll();
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const settings = extension_settings[extensionName];
|
const settings = getLiveExtensionSettings();
|
||||||
if (settings && settings.table_worldbook_enabled) {
|
if (settings && settings.table_worldbook_enabled) {
|
||||||
try {
|
try {
|
||||||
bindWorldBookSettings();
|
bindWorldBookSettings();
|
||||||
@@ -1703,8 +1775,7 @@ function bindBatchFillButton() {
|
|||||||
if (fillButton.dataset.batchEventBound) return;
|
if (fillButton.dataset.batchEventBound) return;
|
||||||
|
|
||||||
fillButton.addEventListener('click', (event) => {
|
fillButton.addEventListener('click', (event) => {
|
||||||
const settings = extension_settings[extensionName];
|
const tableSystemEnabled = isTableSystemEnabled();
|
||||||
const tableSystemEnabled = settings.table_system_enabled !== false;
|
|
||||||
|
|
||||||
if (!tableSystemEnabled) {
|
if (!tableSystemEnabled) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -1727,8 +1798,7 @@ function bindReorganizeButton() {
|
|||||||
if (reorganizeBtn.dataset.reorganizeEventBound) return;
|
if (reorganizeBtn.dataset.reorganizeEventBound) return;
|
||||||
|
|
||||||
reorganizeBtn.addEventListener('click', async (event) => {
|
reorganizeBtn.addEventListener('click', async (event) => {
|
||||||
const settings = extension_settings[extensionName];
|
const tableSystemEnabled = isTableSystemEnabled();
|
||||||
const tableSystemEnabled = settings.table_system_enabled !== false;
|
|
||||||
|
|
||||||
if (!tableSystemEnabled) {
|
if (!tableSystemEnabled) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -1842,8 +1912,7 @@ function bindFloorFillButtons() {
|
|||||||
if (selectedFloorsBtn.dataset.floorEventBound) return;
|
if (selectedFloorsBtn.dataset.floorEventBound) return;
|
||||||
|
|
||||||
selectedFloorsBtn.addEventListener('click', (event) => {
|
selectedFloorsBtn.addEventListener('click', (event) => {
|
||||||
const settings = extension_settings[extensionName];
|
const tableSystemEnabled = isTableSystemEnabled();
|
||||||
const tableSystemEnabled = settings.table_system_enabled !== false;
|
|
||||||
|
|
||||||
if (!tableSystemEnabled) {
|
if (!tableSystemEnabled) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -1885,8 +1954,7 @@ function bindFloorFillButtons() {
|
|||||||
if (currentFloorBtn.dataset.currentEventBound) return;
|
if (currentFloorBtn.dataset.currentEventBound) return;
|
||||||
|
|
||||||
currentFloorBtn.addEventListener('click', (event) => {
|
currentFloorBtn.addEventListener('click', (event) => {
|
||||||
const settings = extension_settings[extensionName];
|
const tableSystemEnabled = isTableSystemEnabled();
|
||||||
const tableSystemEnabled = settings.table_system_enabled !== false;
|
|
||||||
|
|
||||||
if (!tableSystemEnabled) {
|
if (!tableSystemEnabled) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -1907,8 +1975,7 @@ function bindFloorFillButtons() {
|
|||||||
if (rollbackBtn.dataset.rollbackEventBound) return;
|
if (rollbackBtn.dataset.rollbackEventBound) return;
|
||||||
|
|
||||||
rollbackBtn.addEventListener('click', async (event) => {
|
rollbackBtn.addEventListener('click', async (event) => {
|
||||||
const settings = extension_settings[extensionName];
|
const tableSystemEnabled = isTableSystemEnabled();
|
||||||
const tableSystemEnabled = settings.table_system_enabled !== false;
|
|
||||||
|
|
||||||
if (!tableSystemEnabled) {
|
if (!tableSystemEnabled) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -1931,355 +1998,3 @@ function bindFloorFillButtons() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bindTemplateEditors() {
|
|
||||||
const ruleEditor = document.getElementById('ai-rule-template-editor');
|
|
||||||
const ruleSaveBtn = document.getElementById('ai-rule-template-save-btn');
|
|
||||||
const ruleRestoreBtn = document.getElementById('ai-rule-template-restore-btn');
|
|
||||||
|
|
||||||
const flowEditor = document.getElementById('ai-flow-template-editor');
|
|
||||||
const flowSaveBtn = document.getElementById('ai-flow-template-save-btn');
|
|
||||||
const flowRestoreBtn = document.getElementById('ai-flow-template-restore-btn');
|
|
||||||
|
|
||||||
if (!ruleEditor || !flowEditor || !ruleSaveBtn || !flowSaveBtn) {
|
|
||||||
log('无法找到指令模板编辑器或其按钮,绑定失败。', 'warn');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ruleSaveBtn.dataset.templateEventsBound) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ruleEditor.value = TableManager.getBatchFillerRuleTemplate();
|
|
||||||
flowEditor.value = TableManager.getBatchFillerFlowTemplate();
|
|
||||||
|
|
||||||
ruleSaveBtn.addEventListener('click', () => {
|
|
||||||
TableManager.saveBatchFillerRuleTemplate(ruleEditor.value);
|
|
||||||
toastr.success('规则提示词已保存。');
|
|
||||||
log('批量填表-规则提示词已保存。', 'success');
|
|
||||||
});
|
|
||||||
|
|
||||||
flowSaveBtn.addEventListener('click', () => {
|
|
||||||
TableManager.saveBatchFillerFlowTemplate(flowEditor.value);
|
|
||||||
toastr.success('流程提示词已保存。');
|
|
||||||
log('批量填表-流程提示词已保存。', 'success');
|
|
||||||
});
|
|
||||||
|
|
||||||
ruleRestoreBtn.addEventListener('click', () => {
|
|
||||||
if (confirm('您确定要将规则提示词恢复为默认设置吗?')) {
|
|
||||||
ruleEditor.value = DEFAULT_AI_RULE_TEMPLATE;
|
|
||||||
TableManager.saveBatchFillerRuleTemplate(ruleEditor.value);
|
|
||||||
toastr.info('规则提示词已恢复为默认。');
|
|
||||||
log('批量填表-规则提示词已恢复默认。', 'info');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
flowRestoreBtn.addEventListener('click', () => {
|
|
||||||
if (confirm('您确定要将流程提示词恢复为默认设置吗?')) {
|
|
||||||
flowEditor.value = DEFAULT_AI_FLOW_TEMPLATE;
|
|
||||||
TableManager.saveBatchFillerFlowTemplate(flowEditor.value);
|
|
||||||
toastr.info('流程提示词已恢复为默认。');
|
|
||||||
log('批量填表-流程提示词已恢复默认。', 'info');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ruleSaveBtn.dataset.templateEventsBound = 'true';
|
|
||||||
flowSaveBtn.dataset.templateEventsBound = 'true';
|
|
||||||
log('指令模板编辑器已成功绑定。', 'success');
|
|
||||||
}
|
|
||||||
|
|
||||||
function bindNccsApiEvents() {
|
|
||||||
const settings = extension_settings[extensionName];
|
|
||||||
|
|
||||||
if (settings.nccsEnabled === undefined) settings.nccsEnabled = false;
|
|
||||||
if (settings.nccsFakeStreamEnabled === undefined) settings.nccsFakeStreamEnabled = false;
|
|
||||||
if (settings.nccsApiMode === undefined) settings.nccsApiMode = 'openai_test';
|
|
||||||
if (settings.nccsApiUrl === undefined) settings.nccsApiUrl = 'https://api.openai.com/v1';
|
|
||||||
if (settings.nccsApiKey === undefined) settings.nccsApiKey = '';
|
|
||||||
if (settings.nccsModel === undefined) settings.nccsModel = '';
|
|
||||||
if (settings.nccsTavernProfile === undefined) settings.nccsTavernProfile = '';
|
|
||||||
|
|
||||||
const enabledToggle = document.getElementById('nccs-api-enabled');
|
|
||||||
const enabledFakeStreamToggle = document.getElementById('nccs-api-fakestream-enabled');
|
|
||||||
const configDiv = document.getElementById('nccs-api-config');
|
|
||||||
const modeSelect = document.getElementById('nccs-api-mode');
|
|
||||||
const urlInput = document.getElementById('nccs-api-url');
|
|
||||||
const keyInput = document.getElementById('nccs-api-key');
|
|
||||||
const modelInput = document.getElementById('nccs-api-model');
|
|
||||||
const presetSelect = document.getElementById('nccs-sillytavern-preset');
|
|
||||||
const testButton = document.getElementById('nccs-test-connection');
|
|
||||||
const fetchModelsButton = document.getElementById('nccs-fetch-models');
|
|
||||||
|
|
||||||
if (!enabledToggle || !configDiv) return;
|
|
||||||
|
|
||||||
enabledToggle.checked = settings.nccsEnabled;
|
|
||||||
enabledFakeStreamToggle.checked = settings.nccsFakeStreamEnabled;
|
|
||||||
if (modeSelect) modeSelect.value = settings.nccsApiMode;
|
|
||||||
if (urlInput) urlInput.value = settings.nccsApiUrl;
|
|
||||||
if (keyInput) keyInput.value = settings.nccsApiKey;
|
|
||||||
if (modelInput) modelInput.value = settings.nccsModel;
|
|
||||||
if (presetSelect) presetSelect.value = settings.nccsTavernProfile || '';
|
|
||||||
|
|
||||||
const updateConfigVisibility = () => {
|
|
||||||
configDiv.style.display = enabledToggle.checked ? 'block' : 'none';
|
|
||||||
};
|
|
||||||
updateConfigVisibility();
|
|
||||||
|
|
||||||
const updateModeBasedVisibility = () => {
|
|
||||||
if (!modeSelect) return;
|
|
||||||
const isSillyTavernMode = modeSelect.value === 'sillytavern_preset';
|
|
||||||
const isOpenAIMode = modeSelect.value === 'openai_test';
|
|
||||||
|
|
||||||
const presetContainer = presetSelect?.closest('.amily2_opt_settings_block');
|
|
||||||
if (presetContainer) {
|
|
||||||
presetContainer.style.display = isSillyTavernMode ? 'block' : 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
const fieldsToHideInPresetMode = [
|
|
||||||
{ element: urlInput, containerId: null },
|
|
||||||
{ element: keyInput, containerId: null },
|
|
||||||
{ element: modelInput, containerId: null }
|
|
||||||
];
|
|
||||||
|
|
||||||
fieldsToHideInPresetMode.forEach(({ element }) => {
|
|
||||||
if (element) {
|
|
||||||
const container = element.closest('.amily2_opt_settings_block');
|
|
||||||
if (container) {
|
|
||||||
container.style.display = isSillyTavernMode ? 'none' : 'block';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const buttonsContainer = testButton?.closest('.nccs-button-row');
|
|
||||||
if (buttonsContainer) {
|
|
||||||
buttonsContainer.style.display = 'flex';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
updateModeBasedVisibility();
|
|
||||||
|
|
||||||
enabledToggle.addEventListener('change', () => {
|
|
||||||
settings.nccsEnabled = enabledToggle.checked;
|
|
||||||
saveSettingsDebounced();
|
|
||||||
updateConfigVisibility();
|
|
||||||
log(`Nccs API ${enabledToggle.checked ? '已启用' : '已禁用'}`, 'info');
|
|
||||||
});
|
|
||||||
|
|
||||||
enabledFakeStreamToggle.addEventListener('change', () => {
|
|
||||||
settings.nccsFakeStreamEnabled = enabledFakeStreamToggle.checked;
|
|
||||||
saveSettingsDebounced();
|
|
||||||
log(`Nccs API FakeStream ${enabledFakeStreamToggle.checked ? 'Enabled' : 'Disabled'}`, 'info');
|
|
||||||
});
|
|
||||||
|
|
||||||
if (modeSelect) {
|
|
||||||
modeSelect.addEventListener('change', () => {
|
|
||||||
settings.nccsApiMode = modeSelect.value;
|
|
||||||
saveSettingsDebounced();
|
|
||||||
updateModeBasedVisibility();
|
|
||||||
log(`Nccs API模式已切换为: ${modeSelect.value}`, 'info');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (urlInput) {
|
|
||||||
const saveUrl = () => {
|
|
||||||
settings.nccsApiUrl = urlInput.value;
|
|
||||||
saveSettingsDebounced();
|
|
||||||
};
|
|
||||||
|
|
||||||
urlInput.addEventListener('blur', saveUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyInput) {
|
|
||||||
const saveKey = () => {
|
|
||||||
settings.nccsApiKey = keyInput.value;
|
|
||||||
saveSettingsDebounced();
|
|
||||||
};
|
|
||||||
|
|
||||||
keyInput.addEventListener('blur', saveKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modelInput) {
|
|
||||||
const saveModel = () => {
|
|
||||||
settings.nccsModel = modelInput.value;
|
|
||||||
saveSettingsDebounced();
|
|
||||||
};
|
|
||||||
|
|
||||||
modelInput.addEventListener('blur', saveModel);
|
|
||||||
modelInput.addEventListener('input', saveModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (presetSelect) {
|
|
||||||
presetSelect.addEventListener('change', () => {
|
|
||||||
settings.nccsTavernProfile = presetSelect.value;
|
|
||||||
saveSettingsDebounced();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (testButton) {
|
|
||||||
testButton.addEventListener('click', async () => {
|
|
||||||
testButton.disabled = true;
|
|
||||||
testButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 测试中...';
|
|
||||||
|
|
||||||
try {
|
|
||||||
const success = await testNccsApiConnection();
|
|
||||||
if (success) {
|
|
||||||
toastr.success('Nccs API连接测试成功!');
|
|
||||||
log('Nccs API连接测试成功', 'success');
|
|
||||||
} else {
|
|
||||||
toastr.error('Nccs API连接测试失败,请检查配置');
|
|
||||||
log('Nccs API连接测试失败', 'error');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
toastr.error('Nccs API连接测试出错:' + error.message);
|
|
||||||
log('Nccs API连接测试出错:' + error.message, 'error');
|
|
||||||
} finally {
|
|
||||||
testButton.disabled = false;
|
|
||||||
testButton.innerHTML = '<i class="fas fa-plug"></i> 测试连接';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fetchModelsButton) {
|
|
||||||
fetchModelsButton.addEventListener('click', async () => {
|
|
||||||
fetchModelsButton.disabled = true;
|
|
||||||
fetchModelsButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 获取中...';
|
|
||||||
|
|
||||||
if (urlInput) {
|
|
||||||
settings.nccsApiUrl = urlInput.value;
|
|
||||||
}
|
|
||||||
if (keyInput) {
|
|
||||||
settings.nccsApiKey = keyInput.value;
|
|
||||||
}
|
|
||||||
saveSettingsDebounced();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const models = await fetchNccsModels();
|
|
||||||
if (models && models.length > 0) {
|
|
||||||
let modelSelect = document.getElementById('nccs-api-model-select');
|
|
||||||
if (!modelSelect) {
|
|
||||||
modelSelect = document.createElement('select');
|
|
||||||
modelSelect.id = 'nccs-api-model-select';
|
|
||||||
modelSelect.className = 'text_pole';
|
|
||||||
modelInput.parentNode.insertBefore(modelSelect, modelInput.nextSibling);
|
|
||||||
}
|
|
||||||
|
|
||||||
modelSelect.innerHTML = '<option value="">-- 请选择模型 --</option>';
|
|
||||||
models.forEach(model => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = model.id || model.name;
|
|
||||||
option.textContent = model.name || model.id;
|
|
||||||
if ((model.id || model.name) === settings.nccsModel) {
|
|
||||||
option.selected = true;
|
|
||||||
}
|
|
||||||
modelSelect.appendChild(option);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelInput.style.display = 'none';
|
|
||||||
modelSelect.style.display = 'block';
|
|
||||||
|
|
||||||
modelSelect.addEventListener('change', () => {
|
|
||||||
const selectedModel = modelSelect.value;
|
|
||||||
settings.nccsModel = selectedModel;
|
|
||||||
modelInput.value = selectedModel;
|
|
||||||
saveSettingsDebounced();
|
|
||||||
});
|
|
||||||
|
|
||||||
toastr.success(`成功获取 ${models.length} 个模型`);
|
|
||||||
log(`Nccs API获取到 ${models.length} 个模型`, 'success');
|
|
||||||
} else {
|
|
||||||
toastr.warning('未获取到可用模型');
|
|
||||||
log('Nccs API未获取到可用模型', 'warn');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
toastr.error('获取模型失败:' + error.message);
|
|
||||||
log('Nccs API获取模型失败:' + error.message, 'error');
|
|
||||||
} finally {
|
|
||||||
fetchModelsButton.disabled = false;
|
|
||||||
fetchModelsButton.innerHTML = '<i class="fas fa-download"></i> 获取模型';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadSillyTavernPresets = async () => {
|
|
||||||
if (!presetSelect) return;
|
|
||||||
try {
|
|
||||||
const context = getContext();
|
|
||||||
if (!context?.extensionSettings?.connectionManager?.profiles) {
|
|
||||||
throw new Error('无法获取SillyTavern配置文件列表');
|
|
||||||
}
|
|
||||||
|
|
||||||
const profiles = context.extensionSettings.connectionManager.profiles;
|
|
||||||
|
|
||||||
const currentProfileId = settings.nccsTavernProfile;
|
|
||||||
|
|
||||||
presetSelect.innerHTML = '';
|
|
||||||
presetSelect.appendChild(new Option('选择预设', '', false, false));
|
|
||||||
|
|
||||||
if (profiles && profiles.length > 0) {
|
|
||||||
profiles.forEach(profile => {
|
|
||||||
const isSelected = profile.id === currentProfileId;
|
|
||||||
const option = new Option(profile.name, profile.id, isSelected, isSelected);
|
|
||||||
presetSelect.appendChild(option);
|
|
||||||
});
|
|
||||||
log(`成功加载 ${profiles.length} 个SillyTavern配置文件`, 'success');
|
|
||||||
} else {
|
|
||||||
log('未找到可用的SillyTavern配置文件', 'warn');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
log('加载SillyTavern预设失败:' + error.message, 'error');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (modeSelect && presetSelect) {
|
|
||||||
modeSelect.addEventListener('change', () => {
|
|
||||||
if (modeSelect.value === 'sillytavern_preset') {
|
|
||||||
loadSillyTavernPresets();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (settings.nccsApiMode === 'sillytavern_preset') {
|
|
||||||
loadSillyTavernPresets();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log('Nccs API事件绑定完成', 'success');
|
|
||||||
}
|
|
||||||
|
|
||||||
function bindChatTableDisplaySetting() {
|
|
||||||
const settings = extension_settings[extensionName];
|
|
||||||
const showInChatToggle = document.getElementById('show-table-in-chat-toggle');
|
|
||||||
const continuousRenderToggle = document.getElementById('render-on-every-message-toggle');
|
|
||||||
|
|
||||||
if (!showInChatToggle || !continuousRenderToggle) {
|
|
||||||
log('找不到聊天内表格相关的开关,绑定失败。', 'warn');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
showInChatToggle.checked = settings.show_table_in_chat === true;
|
|
||||||
continuousRenderToggle.checked = settings.render_on_every_message === true;
|
|
||||||
|
|
||||||
const updateContinuousRenderState = () => {
|
|
||||||
if (showInChatToggle.checked) {
|
|
||||||
continuousRenderToggle.disabled = false;
|
|
||||||
continuousRenderToggle.closest('.control-block-with-switch').style.opacity = '1';
|
|
||||||
} else {
|
|
||||||
continuousRenderToggle.disabled = true;
|
|
||||||
continuousRenderToggle.closest('.control-block-with-switch').style.opacity = '0.5';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
updateContinuousRenderState();
|
|
||||||
|
|
||||||
showInChatToggle.addEventListener('change', () => {
|
|
||||||
settings.show_table_in_chat = showInChatToggle.checked;
|
|
||||||
saveSettingsDebounced();
|
|
||||||
toastr.info(`聊天内表格显示已${showInChatToggle.checked ? '开启' : '关闭'}。`);
|
|
||||||
updateContinuousRenderState();
|
|
||||||
});
|
|
||||||
|
|
||||||
continuousRenderToggle.addEventListener('change', () => {
|
|
||||||
settings.render_on_every_message = continuousRenderToggle.checked;
|
|
||||||
saveSettingsDebounced();
|
|
||||||
toastr.info(`持续渲染最新消息功能已${continuousRenderToggle.checked ? '开启' : '关闭'}。请切换聊天以应用更改。`);
|
|
||||||
});
|
|
||||||
|
|
||||||
log('聊天内表格显示设置及其依赖关系已成功绑定。', 'success');
|
|
||||||
}
|
|
||||||
|
|||||||
48
ui/table/chat-display-bindings.js
Normal file
48
ui/table/chat-display-bindings.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
export function bindChatTableDisplaySetting({
|
||||||
|
getLiveExtensionSettings,
|
||||||
|
saveSettingsDebounced,
|
||||||
|
log,
|
||||||
|
}) {
|
||||||
|
const settings = getLiveExtensionSettings();
|
||||||
|
const showInChatToggle = document.getElementById('show-table-in-chat-toggle');
|
||||||
|
const continuousRenderToggle = document.getElementById('render-on-every-message-toggle');
|
||||||
|
|
||||||
|
if (!showInChatToggle || !continuousRenderToggle) {
|
||||||
|
log('Chat table display toggles not found, skip binding.', 'warn');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showInChatToggle.checked = settings.show_table_in_chat === true;
|
||||||
|
continuousRenderToggle.checked = settings.render_on_every_message === true;
|
||||||
|
|
||||||
|
const updateContinuousRenderState = () => {
|
||||||
|
const controlBlock = continuousRenderToggle.closest('.control-block-with-switch');
|
||||||
|
if (showInChatToggle.checked) {
|
||||||
|
continuousRenderToggle.disabled = false;
|
||||||
|
if (controlBlock) controlBlock.style.opacity = '1';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
continuousRenderToggle.disabled = true;
|
||||||
|
if (controlBlock) controlBlock.style.opacity = '0.5';
|
||||||
|
};
|
||||||
|
|
||||||
|
updateContinuousRenderState();
|
||||||
|
|
||||||
|
showInChatToggle.addEventListener('change', () => {
|
||||||
|
const currentSettings = getLiveExtensionSettings();
|
||||||
|
currentSettings.show_table_in_chat = showInChatToggle.checked;
|
||||||
|
saveSettingsDebounced();
|
||||||
|
toastr.info(`Chat table display ${showInChatToggle.checked ? 'enabled' : 'disabled'}.`);
|
||||||
|
updateContinuousRenderState();
|
||||||
|
});
|
||||||
|
|
||||||
|
continuousRenderToggle.addEventListener('change', () => {
|
||||||
|
const currentSettings = getLiveExtensionSettings();
|
||||||
|
currentSettings.render_on_every_message = continuousRenderToggle.checked;
|
||||||
|
saveSettingsDebounced();
|
||||||
|
toastr.info(`Continuous chat render ${continuousRenderToggle.checked ? 'enabled' : 'disabled'}.`);
|
||||||
|
});
|
||||||
|
|
||||||
|
log('Chat table display settings bound.', 'success');
|
||||||
|
}
|
||||||
242
ui/table/nccs-bindings.js
Normal file
242
ui/table/nccs-bindings.js
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
export function bindNccsApiEvents({
|
||||||
|
getLiveExtensionSettings,
|
||||||
|
saveSettingsDebounced,
|
||||||
|
getContext,
|
||||||
|
fetchNccsModels,
|
||||||
|
testNccsApiConnection,
|
||||||
|
configManager,
|
||||||
|
log,
|
||||||
|
}) {
|
||||||
|
const settings = getLiveExtensionSettings();
|
||||||
|
|
||||||
|
if (settings.nccsEnabled === undefined) settings.nccsEnabled = false;
|
||||||
|
if (settings.nccsFakeStreamEnabled === undefined) settings.nccsFakeStreamEnabled = false;
|
||||||
|
if (settings.nccsApiMode === undefined) settings.nccsApiMode = 'openai_test';
|
||||||
|
if (settings.nccsApiUrl === undefined) settings.nccsApiUrl = 'https://api.openai.com/v1';
|
||||||
|
if (settings.nccsModel === undefined) settings.nccsModel = '';
|
||||||
|
if (settings.nccsTavernProfile === undefined) settings.nccsTavernProfile = '';
|
||||||
|
|
||||||
|
const enabledToggle = document.getElementById('nccs-api-enabled');
|
||||||
|
const enabledFakeStreamToggle = document.getElementById('nccs-api-fakestream-enabled');
|
||||||
|
const configDiv = document.getElementById('nccs-api-config');
|
||||||
|
const modeSelect = document.getElementById('nccs-api-mode');
|
||||||
|
const urlInput = document.getElementById('nccs-api-url');
|
||||||
|
const keyInput = document.getElementById('nccs-api-key');
|
||||||
|
const modelInput = document.getElementById('nccs-api-model');
|
||||||
|
const presetSelect = document.getElementById('nccs-sillytavern-preset');
|
||||||
|
const testButton = document.getElementById('nccs-test-connection');
|
||||||
|
const fetchModelsButton = document.getElementById('nccs-fetch-models');
|
||||||
|
|
||||||
|
if (!enabledToggle || !enabledFakeStreamToggle || !configDiv) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
enabledToggle.checked = settings.nccsEnabled;
|
||||||
|
enabledFakeStreamToggle.checked = settings.nccsFakeStreamEnabled;
|
||||||
|
if (modeSelect) modeSelect.value = settings.nccsApiMode;
|
||||||
|
if (urlInput) urlInput.value = settings.nccsApiUrl;
|
||||||
|
if (keyInput) keyInput.value = configManager.get('nccsApiKey') || '';
|
||||||
|
if (modelInput) modelInput.value = settings.nccsModel;
|
||||||
|
if (presetSelect) presetSelect.value = settings.nccsTavernProfile || '';
|
||||||
|
|
||||||
|
const updateConfigVisibility = () => {
|
||||||
|
configDiv.style.display = enabledToggle.checked ? 'block' : 'none';
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateModeBasedVisibility = () => {
|
||||||
|
if (!modeSelect) return;
|
||||||
|
|
||||||
|
const isPresetMode = modeSelect.value === 'sillytavern_preset';
|
||||||
|
const presetContainer = presetSelect?.closest('.amily2_opt_settings_block');
|
||||||
|
if (presetContainer) {
|
||||||
|
presetContainer.style.display = isPresetMode ? 'block' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
[urlInput, keyInput, modelInput].forEach((element) => {
|
||||||
|
const container = element?.closest('.amily2_opt_settings_block');
|
||||||
|
if (container) {
|
||||||
|
container.style.display = isPresetMode ? 'none' : 'block';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const buttonsContainer = testButton?.closest('.nccs-button-row');
|
||||||
|
if (buttonsContainer) {
|
||||||
|
buttonsContainer.style.display = 'flex';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveSetting = (key, value) => {
|
||||||
|
const currentSettings = getLiveExtensionSettings();
|
||||||
|
currentSettings[key] = value;
|
||||||
|
saveSettingsDebounced();
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadSillyTavernPresets = async () => {
|
||||||
|
if (!presetSelect) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const context = getContext();
|
||||||
|
const profiles = context?.extensionSettings?.connectionManager?.profiles;
|
||||||
|
if (!profiles) {
|
||||||
|
throw new Error('Unable to load SillyTavern presets.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentProfileId = getLiveExtensionSettings().nccsTavernProfile;
|
||||||
|
presetSelect.innerHTML = '';
|
||||||
|
presetSelect.appendChild(new Option('Select preset', '', false, false));
|
||||||
|
|
||||||
|
if (profiles.length === 0) {
|
||||||
|
log('No SillyTavern presets found.', 'warn');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
profiles.forEach((profile) => {
|
||||||
|
const isSelected = profile.id === currentProfileId;
|
||||||
|
presetSelect.appendChild(new Option(profile.name, profile.id, isSelected, isSelected));
|
||||||
|
});
|
||||||
|
|
||||||
|
log(`Loaded ${profiles.length} SillyTavern presets.`, 'success');
|
||||||
|
} catch (error) {
|
||||||
|
log(`Failed to load SillyTavern presets: ${error.message}`, 'error');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
updateConfigVisibility();
|
||||||
|
updateModeBasedVisibility();
|
||||||
|
|
||||||
|
enabledToggle.addEventListener('change', () => {
|
||||||
|
saveSetting('nccsEnabled', enabledToggle.checked);
|
||||||
|
updateConfigVisibility();
|
||||||
|
log(`NCCS API ${enabledToggle.checked ? 'enabled' : 'disabled'}.`, 'info');
|
||||||
|
});
|
||||||
|
|
||||||
|
enabledFakeStreamToggle.addEventListener('change', () => {
|
||||||
|
saveSetting('nccsFakeStreamEnabled', enabledFakeStreamToggle.checked);
|
||||||
|
log(`NCCS fake stream ${enabledFakeStreamToggle.checked ? 'enabled' : 'disabled'}.`, 'info');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (modeSelect) {
|
||||||
|
modeSelect.addEventListener('change', () => {
|
||||||
|
saveSetting('nccsApiMode', modeSelect.value);
|
||||||
|
updateModeBasedVisibility();
|
||||||
|
if (modeSelect.value === 'sillytavern_preset') {
|
||||||
|
loadSillyTavernPresets();
|
||||||
|
}
|
||||||
|
log(`NCCS API mode changed to ${modeSelect.value}.`, 'info');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (urlInput) {
|
||||||
|
urlInput.addEventListener('blur', () => {
|
||||||
|
saveSetting('nccsApiUrl', urlInput.value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyInput) {
|
||||||
|
keyInput.addEventListener('blur', () => {
|
||||||
|
configManager.set('nccsApiKey', keyInput.value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modelInput) {
|
||||||
|
const saveModel = () => saveSetting('nccsModel', modelInput.value);
|
||||||
|
modelInput.addEventListener('blur', saveModel);
|
||||||
|
modelInput.addEventListener('input', saveModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (presetSelect) {
|
||||||
|
presetSelect.addEventListener('change', () => {
|
||||||
|
saveSetting('nccsTavernProfile', presetSelect.value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testButton) {
|
||||||
|
testButton.addEventListener('click', async () => {
|
||||||
|
testButton.disabled = true;
|
||||||
|
testButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Testing...';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const success = await testNccsApiConnection();
|
||||||
|
if (success) {
|
||||||
|
toastr.success('NCCS API connection succeeded.');
|
||||||
|
log('NCCS API connection succeeded.', 'success');
|
||||||
|
} else {
|
||||||
|
toastr.error('NCCS API connection failed.');
|
||||||
|
log('NCCS API connection failed.', 'error');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
toastr.error(`NCCS API test failed: ${error.message}`);
|
||||||
|
log(`NCCS API test failed: ${error.message}`, 'error');
|
||||||
|
} finally {
|
||||||
|
testButton.disabled = false;
|
||||||
|
testButton.innerHTML = '<i class="fas fa-plug"></i> Test Connection';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fetchModelsButton && modelInput) {
|
||||||
|
fetchModelsButton.addEventListener('click', async () => {
|
||||||
|
fetchModelsButton.disabled = true;
|
||||||
|
fetchModelsButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Loading...';
|
||||||
|
|
||||||
|
if (urlInput) {
|
||||||
|
saveSetting('nccsApiUrl', urlInput.value);
|
||||||
|
}
|
||||||
|
if (keyInput) {
|
||||||
|
configManager.set('nccsApiKey', keyInput.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const models = await fetchNccsModels();
|
||||||
|
if (!models?.length) {
|
||||||
|
toastr.warning('No models returned.');
|
||||||
|
log('No NCCS models returned.', 'warn');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let modelSelect = document.getElementById('nccs-api-model-select');
|
||||||
|
if (!modelSelect) {
|
||||||
|
modelSelect = document.createElement('select');
|
||||||
|
modelSelect.id = 'nccs-api-model-select';
|
||||||
|
modelSelect.className = 'text_pole';
|
||||||
|
modelInput.parentNode.insertBefore(modelSelect, modelInput.nextSibling);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentModel = getLiveExtensionSettings().nccsModel;
|
||||||
|
modelSelect.innerHTML = '<option value="">-- Select model --</option>';
|
||||||
|
|
||||||
|
models.forEach((model) => {
|
||||||
|
const value = model.id || model.name;
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = value;
|
||||||
|
option.textContent = model.name || model.id;
|
||||||
|
option.selected = value === currentModel;
|
||||||
|
modelSelect.appendChild(option);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelInput.style.display = 'none';
|
||||||
|
modelSelect.style.display = 'block';
|
||||||
|
modelSelect.onchange = () => {
|
||||||
|
const selectedModel = modelSelect.value;
|
||||||
|
modelInput.value = selectedModel;
|
||||||
|
saveSetting('nccsModel', selectedModel);
|
||||||
|
};
|
||||||
|
|
||||||
|
toastr.success(`Loaded ${models.length} models.`);
|
||||||
|
log(`Loaded ${models.length} NCCS models.`, 'success');
|
||||||
|
} catch (error) {
|
||||||
|
toastr.error(`Failed to load models: ${error.message}`);
|
||||||
|
log(`Failed to load NCCS models: ${error.message}`, 'error');
|
||||||
|
} finally {
|
||||||
|
fetchModelsButton.disabled = false;
|
||||||
|
fetchModelsButton.innerHTML = '<i class="fas fa-download"></i> Fetch Models';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modeSelect?.value === 'sillytavern_preset' && presetSelect) {
|
||||||
|
loadSillyTavernPresets();
|
||||||
|
}
|
||||||
|
|
||||||
|
log('NCCS API settings bound.', 'success');
|
||||||
|
}
|
||||||
64
ui/table/template-bindings.js
Normal file
64
ui/table/template-bindings.js
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
export function bindTableTemplateEditors({
|
||||||
|
TableManager,
|
||||||
|
log,
|
||||||
|
defaultRuleTemplate,
|
||||||
|
defaultFlowTemplate,
|
||||||
|
}) {
|
||||||
|
const ruleEditor = document.getElementById('ai-rule-template-editor');
|
||||||
|
const ruleSaveBtn = document.getElementById('ai-rule-template-save-btn');
|
||||||
|
const ruleRestoreBtn = document.getElementById('ai-rule-template-restore-btn');
|
||||||
|
|
||||||
|
const flowEditor = document.getElementById('ai-flow-template-editor');
|
||||||
|
const flowSaveBtn = document.getElementById('ai-flow-template-save-btn');
|
||||||
|
const flowRestoreBtn = document.getElementById('ai-flow-template-restore-btn');
|
||||||
|
|
||||||
|
if (!ruleEditor || !flowEditor || !ruleSaveBtn || !flowSaveBtn) {
|
||||||
|
log('Template editors not found, skip binding.', 'warn');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ruleSaveBtn.dataset.templateEventsBound) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ruleEditor.value = TableManager.getBatchFillerRuleTemplate();
|
||||||
|
flowEditor.value = TableManager.getBatchFillerFlowTemplate();
|
||||||
|
|
||||||
|
ruleSaveBtn.addEventListener('click', () => {
|
||||||
|
TableManager.saveBatchFillerRuleTemplate(ruleEditor.value);
|
||||||
|
toastr.success('Rule template saved.');
|
||||||
|
log('Batch filler rule template saved.', 'success');
|
||||||
|
});
|
||||||
|
|
||||||
|
flowSaveBtn.addEventListener('click', () => {
|
||||||
|
TableManager.saveBatchFillerFlowTemplate(flowEditor.value);
|
||||||
|
toastr.success('Flow template saved.');
|
||||||
|
log('Batch filler flow template saved.', 'success');
|
||||||
|
});
|
||||||
|
|
||||||
|
ruleRestoreBtn.addEventListener('click', () => {
|
||||||
|
if (!confirm('Restore the default rule template?')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ruleEditor.value = defaultRuleTemplate;
|
||||||
|
TableManager.saveBatchFillerRuleTemplate(ruleEditor.value);
|
||||||
|
toastr.info('Rule template restored.');
|
||||||
|
log('Batch filler rule template restored.', 'info');
|
||||||
|
});
|
||||||
|
|
||||||
|
flowRestoreBtn.addEventListener('click', () => {
|
||||||
|
if (!confirm('Restore the default flow template?')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
flowEditor.value = defaultFlowTemplate;
|
||||||
|
TableManager.saveBatchFillerFlowTemplate(flowEditor.value);
|
||||||
|
toastr.info('Flow template restored.');
|
||||||
|
log('Batch filler flow template restored.', 'info');
|
||||||
|
});
|
||||||
|
|
||||||
|
ruleSaveBtn.dataset.templateEventsBound = 'true';
|
||||||
|
flowSaveBtn.dataset.templateEventsBound = 'true';
|
||||||
|
log('Template editors bound.', 'success');
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -23,6 +23,7 @@ import { extension_settings } from "/scripts/extensions.js";
|
|||||||
import { saveSettingsDebounced } from "/script.js";
|
import { saveSettingsDebounced } from "/script.js";
|
||||||
import { extensionName } from "../settings.js";
|
import { extensionName } from "../settings.js";
|
||||||
import { SENSITIVE_KEYS } from "./sensitive-keys.js";
|
import { SENSITIVE_KEYS } from "./sensitive-keys.js";
|
||||||
|
import { apiKeyStore } from "./api-key-store/ApiKeyStore.js";
|
||||||
|
|
||||||
// localStorage key 前缀,避免与其他插件冲突
|
// localStorage key 前缀,避免与其他插件冲突
|
||||||
const LS_PREFIX = 'amily2_secure_';
|
const LS_PREFIX = 'amily2_secure_';
|
||||||
@@ -30,6 +31,10 @@ const LS_PREFIX = 'amily2_secure_';
|
|||||||
// ── ConfigManager ────────────────────────────────────────────────────────────
|
// ── ConfigManager ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
class ConfigManager {
|
class ConfigManager {
|
||||||
|
async init() {
|
||||||
|
await apiKeyStore.init();
|
||||||
|
await this.syncSensitiveCache({ force: true });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 读取配置项。
|
* 读取配置项。
|
||||||
@@ -53,17 +58,18 @@ class ConfigManager {
|
|||||||
*/
|
*/
|
||||||
set(key, value) {
|
set(key, value) {
|
||||||
if (SENSITIVE_KEYS.has(key)) {
|
if (SENSITIVE_KEYS.has(key)) {
|
||||||
if (value !== null && value !== undefined && value !== '') {
|
this._setSensitiveCacheValue(key, value);
|
||||||
localStorage.setItem(LS_PREFIX + key, value);
|
|
||||||
} else {
|
|
||||||
localStorage.removeItem(LS_PREFIX + key);
|
|
||||||
}
|
|
||||||
// 确保 extension_settings 中不保留该敏感字段
|
// 确保 extension_settings 中不保留该敏感字段
|
||||||
const settings = extension_settings[extensionName];
|
const settings = extension_settings[extensionName];
|
||||||
if (settings && Object.prototype.hasOwnProperty.call(settings, key)) {
|
if (settings && Object.prototype.hasOwnProperty.call(settings, key)) {
|
||||||
delete settings[key];
|
delete settings[key];
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
}
|
}
|
||||||
|
if (apiKeyStore.getMode() === 'cloud') {
|
||||||
|
apiKeyStore.setKey(key, value).catch(e => {
|
||||||
|
console.error(`[ConfigManager] 云同步敏感字段 "${key}" 失败:`, e);
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!extension_settings[extensionName]) {
|
if (!extension_settings[extensionName]) {
|
||||||
extension_settings[extensionName] = {};
|
extension_settings[extensionName] = {};
|
||||||
@@ -128,6 +134,28 @@ class ConfigManager {
|
|||||||
console.info('[Amily2-Config] 敏感配置迁移完成,已从云同步配置中清除密钥。');
|
console.info('[Amily2-Config] 敏感配置迁移完成,已从云同步配置中清除密钥。');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async syncSensitiveCache({ force = false } = {}) {
|
||||||
|
if (apiKeyStore.getMode() !== 'cloud') return;
|
||||||
|
await apiKeyStore.init();
|
||||||
|
if (!apiKeyStore.isCloudReady()) return;
|
||||||
|
|
||||||
|
for (const key of SENSITIVE_KEYS) {
|
||||||
|
const cached = localStorage.getItem(LS_PREFIX + key);
|
||||||
|
if (!force && cached !== null && cached !== '') continue;
|
||||||
|
|
||||||
|
const value = await apiKeyStore.getKey(key);
|
||||||
|
this._setSensitiveCacheValue(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_setSensitiveCacheValue(key, value) {
|
||||||
|
if (value !== null && value !== undefined && value !== '') {
|
||||||
|
localStorage.setItem(LS_PREFIX + key, value);
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem(LS_PREFIX + key);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 单例导出 ─────────────────────────────────────────────────────────────────
|
// ── 单例导出 ─────────────────────────────────────────────────────────────────
|
||||||
@@ -147,6 +175,8 @@ setTimeout(() => {
|
|||||||
set: (key, value) => configManager.set(key, value),
|
set: (key, value) => configManager.set(key, value),
|
||||||
getSettings: () => configManager.getSettings(),
|
getSettings: () => configManager.getSettings(),
|
||||||
migrate: () => configManager.migrate(),
|
migrate: () => configManager.migrate(),
|
||||||
|
init: () => configManager.init(),
|
||||||
|
syncSensitiveCache: (options) => configManager.syncSensitiveCache(options),
|
||||||
});
|
});
|
||||||
_ctx.log('ConfigManager', 'info', 'Config 服务已注册到 Bus。');
|
_ctx.log('ConfigManager', 'info', 'Config 服务已注册到 Bus。');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
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();}
|
function a0_0x3df7(_0x2d6ff7,_0x66f04c){_0x2d6ff7=_0x2d6ff7-0x142;const _0x37dbf3=a0_0x37db();let _0x3df7d1=_0x37dbf3[_0x2d6ff7];if(a0_0x3df7['SRsBUW']===undefined){var _0x177cea=function(_0x263285){const _0x23b824='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x5dc9bb='',_0x4f00bc='';for(let _0x3a945f=0x0,_0x467661,_0x2c1afd,_0x361ba3=0x0;_0x2c1afd=_0x263285['charAt'](_0x361ba3++);~_0x2c1afd&&(_0x467661=_0x3a945f%0x4?_0x467661*0x40+_0x2c1afd:_0x2c1afd,_0x3a945f++%0x4)?_0x5dc9bb+=String['fromCharCode'](0xff&_0x467661>>(-0x2*_0x3a945f&0x6)):0x0){_0x2c1afd=_0x23b824['indexOf'](_0x2c1afd);}for(let _0x134167=0x0,_0x59c18c=_0x5dc9bb['length'];_0x134167<_0x59c18c;_0x134167++){_0x4f00bc+='%'+('00'+_0x5dc9bb['charCodeAt'](_0x134167)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x4f00bc);};const _0x2742fa=function(_0x225e04,_0x57e87c){let _0x196891=[],_0x5287e0=0x0,_0x4e6f16,_0x4e9137='';_0x225e04=_0x177cea(_0x225e04);let _0x3b2355;for(_0x3b2355=0x0;_0x3b2355<0x100;_0x3b2355++){_0x196891[_0x3b2355]=_0x3b2355;}for(_0x3b2355=0x0;_0x3b2355<0x100;_0x3b2355++){_0x5287e0=(_0x5287e0+_0x196891[_0x3b2355]+_0x57e87c['charCodeAt'](_0x3b2355%_0x57e87c['length']))%0x100,_0x4e6f16=_0x196891[_0x3b2355],_0x196891[_0x3b2355]=_0x196891[_0x5287e0],_0x196891[_0x5287e0]=_0x4e6f16;}_0x3b2355=0x0,_0x5287e0=0x0;for(let _0x14e882=0x0;_0x14e882<_0x225e04['length'];_0x14e882++){_0x3b2355=(_0x3b2355+0x1)%0x100,_0x5287e0=(_0x5287e0+_0x196891[_0x3b2355])%0x100,_0x4e6f16=_0x196891[_0x3b2355],_0x196891[_0x3b2355]=_0x196891[_0x5287e0],_0x196891[_0x5287e0]=_0x4e6f16,_0x4e9137+=String['fromCharCode'](_0x225e04['charCodeAt'](_0x14e882)^_0x196891[(_0x196891[_0x3b2355]+_0x196891[_0x5287e0])%0x100]);}return _0x4e9137;};a0_0x3df7['gGFzDR']=_0x2742fa,a0_0x3df7['xOxaTR']={},a0_0x3df7['SRsBUW']=!![];}const _0x2e2977=_0x37dbf3[0x0],_0x46cf12=_0x2d6ff7+_0x2e2977,_0x2e4631=a0_0x3df7['xOxaTR'][_0x46cf12];return!_0x2e4631?(a0_0x3df7['ObVLbg']===undefined&&(a0_0x3df7['ObVLbg']=!![]),_0x3df7d1=a0_0x3df7['gGFzDR'](_0x3df7d1,_0x66f04c),a0_0x3df7['xOxaTR'][_0x46cf12]=_0x3df7d1):_0x3df7d1=_0x2e4631,_0x3df7d1;}const a0_0x2af32d=a0_0x3df7;(function(_0xd2618c,_0x4148c8){const _0x3e9b8c=a0_0x3df7,_0x44ff83=_0xd2618c();while(!![]){try{const _0x3bd772=parseInt(_0x3e9b8c(0x149,'cURR'))/0x1+-parseInt(_0x3e9b8c(0x152,'Ipuz'))/0x2*(-parseInt(_0x3e9b8c(0x159,'r74E'))/0x3)+-parseInt(_0x3e9b8c(0x147,'DYEA'))/0x4*(-parseInt(_0x3e9b8c(0x14c,'r74E'))/0x5)+parseInt(_0x3e9b8c(0x142,'UpGh'))/0x6*(parseInt(_0x3e9b8c(0x14e,'8hxs'))/0x7)+parseInt(_0x3e9b8c(0x14a,'hAFE'))/0x8*(-parseInt(_0x3e9b8c(0x146,'OG5z'))/0x9)+parseInt(_0x3e9b8c(0x15d,'Z46V'))/0xa+-parseInt(_0x3e9b8c(0x157,'SE&p'))/0xb;if(_0x3bd772===_0x4148c8)break;else _0x44ff83['push'](_0x44ff83['shift']());}catch(_0x52e370){_0x44ff83['push'](_0x44ff83['shift']());}}}(a0_0x37db,0x6ab29));function a0_0x37db(){const _0x2331d0=['pSkZW5ZdVZC3xIi','z8o6W7ddLduovrhdGCoC','arfnxCoaBmoCD8kXymoU','tNDZBSoedXbjsxLmW4C','WQq3tSoIWRXfW508W7pcPCknW6xcJ28','lCo1f8ksWRFcJsClwmoTucTYka','W6DJDmovWRb8W4SB','WPmbW5/dGSoauXFdRcm/wa','WPWsWQjrW6xcGSocW6hdOCoRDa','o8olWRzxWPxdHMe','WQ4MWRpdHJPzWRryqrPveSkuW5S','ESoctmoDWQ/cHmkBuSkAWOBcR1pcQa','BSkgW7xcSSkXiW','er9nzSoGBmoCy8k/Fa','iWuRF8kjaWmAWOm0W4K','u1CynmkzkmkfrSkQvmo/W5Cv','WPSyW5TwoCkxuHvIpYVdNa','vmkXF8kKkGL6uCoOcWG','WP/dOSowW6GmWOLgW5JcQGNcV8kM','fSkEg1ddHCkgWRJcRa','t3HmWQhcRCk1hxRcUWOVaa','n8ocWQNdImoLAabgWPGhcSoL','WPzoWQGuASoiDW','e07dU8kcW7VdImodW69oWOC','WPKcW5VdGCoecapdJb45rSkh','WPqtWQjCWRNcPSo7W53dSCo1','nSocW5tdU8o5FwhdLSoiFG','m8oVW6eooSk7W5yy','jsBdK8oxWQ1yWQX8W5FcIq','yLX0o8o2ndqRWQqlW75WW4pdPSoBmCkJkXuvWPddTeWv'];a0_0x37db=function(){return _0x2331d0;};return a0_0x37db();}export const SENSITIVE_KEYS=new Set([a0_0x2af32d(0x15e,'cURR'),a0_0x2af32d(0x156,'(nn@'),a0_0x2af32d(0x151,'UpGh'),a0_0x2af32d(0x14b,'7yS&'),a0_0x2af32d(0x150,'v814'),a0_0x2af32d(0x153,'Ipuz'),a0_0x2af32d(0x154,'V0W['),a0_0x2af32d(0x15f,'V0W[')]);
|
||||||
@@ -997,65 +997,8 @@ export const defaultSettings = {
|
|||||||
|
|
||||||
|
|
||||||
export function validateSettings() {
|
export function validateSettings() {
|
||||||
const settings = extension_settings[extensionName] || {};
|
// 主 API 概念已移除,各功能模块通过 Profile 槽位或独立配置管理 API。
|
||||||
|
return null;
|
||||||
// 新版 Profile 系统管理 API 配置时,跳过旧版字段验证
|
|
||||||
const assignments = settings.amily2_profile_assignments || {};
|
|
||||||
if (assignments.main) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果启用了Ngms或Nccs,则跳过主API验证
|
|
||||||
if (settings.ngmsEnabled || settings.nccsEnabled) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const apiProvider = settings.apiProvider || 'openai';
|
|
||||||
const errors = [];
|
|
||||||
|
|
||||||
switch (apiProvider) {
|
|
||||||
case 'openai':
|
|
||||||
case 'openai_test':
|
|
||||||
if (!settings.apiUrl) {
|
|
||||||
errors.push("当前模式需要配置API URL");
|
|
||||||
} else if (!/^https?:\/\//.test(settings.apiUrl)) {
|
|
||||||
errors.push("API URL必须以http://或https://开头");
|
|
||||||
}
|
|
||||||
if (apiProvider === 'openai' && !settings.apiKey) {
|
|
||||||
errors.push("当前模式需要配置API Key");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'sillytavern_backend':
|
|
||||||
if (!settings.apiUrl) {
|
|
||||||
errors.push("SillyTavern后端模式需要配置API URL");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'google':
|
|
||||||
if (!settings.apiKey) {
|
|
||||||
errors.push("Google直连模式需要配置API Key");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'sillytavern_preset':
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (!settings.apiUrl) {
|
|
||||||
errors.push("API URL未配置");
|
|
||||||
}
|
|
||||||
if (!settings.apiKey) {
|
|
||||||
errors.push("API Key未配置");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!settings.model && apiProvider !== 'sillytavern_preset') {
|
|
||||||
errors.push("未选择模型");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settings.maxTokens < 100 || settings.maxTokens > 100000) {
|
|
||||||
errors.push(`Token数超限 (${settings.maxTokens}) - 必须在100-100000之间`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.length ? errors : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function saveSettings() {
|
export function saveSettings() {
|
||||||
|
|||||||
Reference in New Issue
Block a user