mirror of
https://github.com/SilenceLurker/ST-Amily2-Chat-Optimisation.git
synced 2026-06-06 12:25:51 +00:00
Add files via upload
This commit is contained in:
196
ui/bindings.js
Normal file
196
ui/bindings.js
Normal file
@@ -0,0 +1,196 @@
|
||||
import { extension_settings } from "/scripts/extensions.js";
|
||||
import { saveSettingsDebounced } from "/script.js";
|
||||
import { defaultSettings, extensionName } from "../utils/settings.js";
|
||||
import { pluginAuthStatus, activatePluginAuthorization } from "../utils/auth.js";
|
||||
import { fetchSupportedModels } from "../core/api.js";
|
||||
import { setAvailableModels, populateModelDropdown } from "./state.js";
|
||||
import { fixCommand, testReplyChecker } from "../core/commands.js";
|
||||
|
||||
|
||||
export function bindModalEvents() {
|
||||
const container = $("#amily2-drawer-content");
|
||||
|
||||
if (container.data("events-bound")) return;
|
||||
|
||||
|
||||
const snakeToCamel = (s) => s.replace(/_([a-z])/g, (g) => g[1].toUpperCase());
|
||||
const updateAndSaveSetting = (key, value) => {
|
||||
|
||||
console.log(`[Amily-谕令确认] 收到指令: 将 [${key}] 设置为 ->`, value);
|
||||
|
||||
if (!extension_settings[extensionName]) {
|
||||
extension_settings[extensionName] = {};
|
||||
}
|
||||
extension_settings[extensionName] = {
|
||||
...extension_settings[extensionName],
|
||||
[key]: value,
|
||||
};
|
||||
saveSettingsDebounced();
|
||||
|
||||
console.log(`[Amily-谕令镌刻] [${key}] 的新状态已保存。`);
|
||||
};
|
||||
|
||||
|
||||
container
|
||||
.off("click.amily2.auth")
|
||||
.on("click.amily2.auth", "#auth_submit", async function () {
|
||||
const authCode = $("#amily2_auth_code").val().trim();
|
||||
if (authCode) {
|
||||
await activatePluginAuthorization(authCode);
|
||||
} else {
|
||||
toastr.warning("请输入授权码", "Amily2号");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
container
|
||||
.off("click.amily2.actions")
|
||||
.on(
|
||||
"click.amily2.actions",
|
||||
"#amily2_refresh_models, #amily2_test, #amily2_fix_now",
|
||||
async function () {
|
||||
if (!pluginAuthStatus.authorized) return;
|
||||
const button = $(this);
|
||||
const originalHtml = button.html();
|
||||
button
|
||||
.prop("disabled", true)
|
||||
.html('<i class="fas fa-spinner fa-spin"></i> 处理中');
|
||||
try {
|
||||
switch (this.id) {
|
||||
case "amily2_refresh_models":
|
||||
const models = await fetchSupportedModels();
|
||||
if (models.length > 0) {
|
||||
setAvailableModels(models);
|
||||
localStorage.setItem(
|
||||
"cached_models_amily2",
|
||||
JSON.stringify(models),
|
||||
);
|
||||
populateModelDropdown();
|
||||
}
|
||||
break;
|
||||
case "amily2_test":
|
||||
await testReplyChecker();
|
||||
break;
|
||||
case "amily2_fix_now":
|
||||
await fixCommand();
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[Amily2-工部] 操作按钮 ${this.id} 执行失败:`, error);
|
||||
toastr.error(`操作失败: ${error.message}`, "Amily2号");
|
||||
} finally {
|
||||
button.prop("disabled", false).html(originalHtml);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
container
|
||||
.off("change.amily2.checkbox")
|
||||
.on(
|
||||
"change.amily2.checkbox",
|
||||
'input[type="checkbox"][id^="amily2_"]',
|
||||
function () {
|
||||
if (!pluginAuthStatus.authorized) return;
|
||||
const key = snakeToCamel(this.id.replace("amily2_", ""));
|
||||
updateAndSaveSetting(key, this.checked);
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
container
|
||||
.off("change.amily2.radio")
|
||||
.on(
|
||||
"change.amily2.radio",
|
||||
'input[type="radio"][name^="amily2_"]',
|
||||
function () {
|
||||
if (!pluginAuthStatus.authorized) return;
|
||||
const key = snakeToCamel(this.name.replace("amily2_", ""));
|
||||
const value = $(`input[name="${this.name}"]:checked`).val();
|
||||
updateAndSaveSetting(key, value);
|
||||
},
|
||||
);
|
||||
|
||||
container
|
||||
.off("change.amily2.text")
|
||||
.on("change.amily2.text", "#amily2_api_url, #amily2_api_key", function () {
|
||||
if (!pluginAuthStatus.authorized) return;
|
||||
const key = snakeToCamel(this.id.replace("amily2_", ""));
|
||||
updateAndSaveSetting(key, this.value);
|
||||
toastr.success(`配置 [${key}] 已自动保存!`, "Amily2号");
|
||||
});
|
||||
|
||||
|
||||
container
|
||||
.off("change.amily2.select")
|
||||
.on("change.amily2.select", "select#amily2_model", function () {
|
||||
if (!pluginAuthStatus.authorized) return;
|
||||
const key = snakeToCamel(this.id.replace("amily2_", ""));
|
||||
updateAndSaveSetting(key, this.value);
|
||||
populateModelDropdown();
|
||||
});
|
||||
|
||||
|
||||
container
|
||||
.off("input.amily2.range")
|
||||
.on(
|
||||
"input.amily2.range",
|
||||
'input[type="range"][id^="amily2_"]',
|
||||
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);
|
||||
$(`#${this.id}_value`).text(value);
|
||||
updateAndSaveSetting(key, value);
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
const promptMap = {
|
||||
mainPrompt: "#amily2_main_prompt",
|
||||
systemPrompt: "#amily2_system_prompt",
|
||||
summarizationPrompt: "#amily2_summarization_prompt",
|
||||
outputFormatPrompt: "#amily2_output_format_prompt",
|
||||
};
|
||||
const selector = "#amily2_prompt_selector";
|
||||
const editor = "#amily2_unified_editor";
|
||||
const unifiedSaveButton = "#amily2_unified_save_button";
|
||||
|
||||
function updateEditorView() {
|
||||
const selectedKey = $(selector).val();
|
||||
if (!selectedKey) return;
|
||||
const content = extension_settings[extensionName][selectedKey] || "";
|
||||
$(editor).val(content);
|
||||
}
|
||||
|
||||
|
||||
container
|
||||
.off("change.amily2.prompt_selector")
|
||||
.on("change.amily2.prompt_selector", selector, updateEditorView);
|
||||
|
||||
container
|
||||
.off("click.amily2.unified_save")
|
||||
.on("click.amily2.unified_save", unifiedSaveButton, function () {
|
||||
const selectedKey = $(selector).val();
|
||||
if (!selectedKey) return;
|
||||
const newContent = $(editor).val();
|
||||
updateAndSaveSetting(selectedKey, newContent);
|
||||
toastr.success(`谕令 [${selectedKey}] 已镌刻!`, "Amily2号");
|
||||
});
|
||||
|
||||
container
|
||||
.off("click.amily2.unified_restore")
|
||||
.on("click.amily2.unified_restore", "#amily2_unified_restore_button", function () {
|
||||
const selectedKey = $(selector).val();
|
||||
if (!selectedKey) return;
|
||||
const defaultValue = defaultSettings[selectedKey];
|
||||
$(editor).val(defaultValue);
|
||||
updateAndSaveSetting(selectedKey, defaultValue);
|
||||
toastr.success(`谕令 [${selectedKey}] 已成功恢复为帝国初始蓝图。`, "Amily2号");
|
||||
});
|
||||
|
||||
setTimeout(updateEditorView, 100);
|
||||
|
||||
container.data("events-bound", true);
|
||||
}
|
||||
131
ui/drawer.js
Normal file
131
ui/drawer.js
Normal file
@@ -0,0 +1,131 @@
|
||||
import { extension_settings } from "/scripts/extensions.js";
|
||||
import { extensionName, defaultSettings } from "../utils/settings.js";
|
||||
import {
|
||||
checkAuthorization,
|
||||
displayExpiryInfo,
|
||||
pluginAuthStatus,
|
||||
} from "../utils/auth.js";
|
||||
import {
|
||||
updateUI,
|
||||
setAvailableModels,
|
||||
populateModelDropdown,
|
||||
} from "./state.js";
|
||||
import { bindModalEvents } from "./bindings.js";
|
||||
import { fetchSupportedModels } from "../core/api.js";
|
||||
|
||||
const extensionFolderPath = `scripts/extensions/third-party/${extensionName}`;
|
||||
|
||||
async function loadSettings() {
|
||||
if (!extension_settings[extensionName]) {
|
||||
extension_settings[extensionName] = {};
|
||||
}
|
||||
Object.assign(extension_settings[extensionName], {
|
||||
...defaultSettings,
|
||||
...extension_settings[extensionName],
|
||||
});
|
||||
|
||||
checkAuthorization();
|
||||
|
||||
|
||||
const autoLogin = localStorage.getItem("plugin_auto_login") === "true";
|
||||
console.log(
|
||||
`[Amily2-调试] 授权状态: ${pluginAuthStatus.authorized}, 自动登录标志: ${autoLogin}`,
|
||||
);
|
||||
if (autoLogin && pluginAuthStatus.authorized) {
|
||||
console.log("[Amily2号] 检测到有效授权,将执行自动UI更新。");
|
||||
}
|
||||
|
||||
$("#expiry_info").html(displayExpiryInfo());
|
||||
updateUI();
|
||||
|
||||
if (pluginAuthStatus.authorized && extension_settings[extensionName].apiUrl) {
|
||||
const cachedModels = localStorage.getItem("cached_models_amily2");
|
||||
if (cachedModels) {
|
||||
const models = JSON.parse(cachedModels);
|
||||
console.log(`[Amily2号] 从缓存加载模型列表 (${models.length}个)`);
|
||||
setAvailableModels(models);
|
||||
populateModelDropdown();
|
||||
} else {
|
||||
toastr.info("正在自动加载模型列表...", "Amily2号");
|
||||
setTimeout(async () => {
|
||||
const models = await fetchSupportedModels();
|
||||
if (models.length > 0) {
|
||||
setAvailableModels(models);
|
||||
localStorage.setItem("cached_models_amily2", JSON.stringify(models));
|
||||
populateModelDropdown();
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createDrawer() {
|
||||
if ($("#amily2-main-drawer").length > 0) return;
|
||||
|
||||
const amily2DrawerHtml = `
|
||||
<div id="amily2-main-drawer" class="drawer">
|
||||
<div class="drawer-toggle drawer-header" title="Amily2号优化助手">
|
||||
<div id="amily2-drawer-icon" class="drawer-icon fa-solid fa-magic fa-fw closedIcon interactable" tabindex="0"></div>
|
||||
</div>
|
||||
<div id="amily2-drawer-content" class="drawer-content" style="display: none;">
|
||||
<!-- 王座将在此处动态加载 -->
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
$("#sys-settings-button").after(amily2DrawerHtml);
|
||||
|
||||
|
||||
$(document).on(
|
||||
"mousedown",
|
||||
"#amily2-main-drawer .drawer-toggle",
|
||||
async function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const drawerIcon = $("#amily2-drawer-icon");
|
||||
const contentPanel = $("#amily2-drawer-content");
|
||||
const isOpening = drawerIcon.hasClass("closedIcon");
|
||||
|
||||
|
||||
$(".openIcon")
|
||||
.not(drawerIcon)
|
||||
.removeClass("openIcon")
|
||||
.addClass("closedIcon");
|
||||
$(".openDrawer")
|
||||
.not(contentPanel)
|
||||
.removeClass("openDrawer")
|
||||
.slideUp({ duration: 200, easing: "swing" });
|
||||
|
||||
|
||||
drawerIcon.toggleClass("closedIcon openIcon");
|
||||
contentPanel.toggleClass("openDrawer");
|
||||
contentPanel.slideToggle({
|
||||
duration: 200,
|
||||
easing: "swing",
|
||||
});
|
||||
|
||||
|
||||
const isInitialized = contentPanel.data("initialized");
|
||||
if (isOpening && !isInitialized) {
|
||||
try {
|
||||
const modalContent = await $.get(
|
||||
`${extensionFolderPath}/assets/amily2-modal.html`,
|
||||
);
|
||||
contentPanel.html(modalContent);
|
||||
await loadSettings();
|
||||
bindModalEvents();
|
||||
contentPanel.data("initialized", true);
|
||||
|
||||
console.log("[Amily2号-建设部] 宫殿内室已根据最高指令激活。");
|
||||
} catch (error) {
|
||||
|
||||
console.error("[Amily2号-建设部] 加载宫殿内部HTML失败:", error);
|
||||
contentPanel.html(
|
||||
'<p style="color:red; padding: 20px;">紧急报告:无法加载Amily2号府邸内饰。</p>',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
);
|
||||
}
|
||||
104
ui/state.js
Normal file
104
ui/state.js
Normal file
@@ -0,0 +1,104 @@
|
||||
import { extension_settings } from "/scripts/extensions.js";
|
||||
import { extensionName } from "../utils/settings.js";
|
||||
import { pluginAuthStatus } from "../utils/auth.js";
|
||||
|
||||
let availableModels = [];
|
||||
|
||||
|
||||
export function setAvailableModels(models) {
|
||||
availableModels = models;
|
||||
}
|
||||
|
||||
|
||||
export function populateModelDropdown() {
|
||||
const modelSelect = $("#amily2_model");
|
||||
const modelNotes = $("#amily2_model_notes");
|
||||
|
||||
modelSelect.empty();
|
||||
const currentModel = extension_settings[extensionName]?.model || "";
|
||||
|
||||
if (availableModels.length === 0) {
|
||||
modelSelect.append('<option value="">无可用模型,请刷新</option>');
|
||||
modelNotes.html(
|
||||
'<span style="color: #ff9800;">请检查API配置后点击"刷新模型"</span>',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const defaultOption = $("<option></option>").val("").text("-- 选择模型 --");
|
||||
modelSelect.append(defaultOption);
|
||||
|
||||
availableModels.forEach((model) => {
|
||||
const option = $("<option></option>").val(model).text(model);
|
||||
if (model === currentModel) {
|
||||
option.attr("selected", "selected");
|
||||
}
|
||||
modelSelect.append(option);
|
||||
});
|
||||
|
||||
if (currentModel && modelSelect.val() === currentModel) {
|
||||
modelNotes.html(`已选择: <strong>${currentModel}</strong>`);
|
||||
} else {
|
||||
modelNotes.html(`已加载 ${availableModels.length} 个可用模型`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function updateUI() {
|
||||
if (!pluginAuthStatus.authorized) {
|
||||
$("#auth_panel").show();
|
||||
$(".plugin-features").hide();
|
||||
} else {
|
||||
$("#auth_panel").hide();
|
||||
$(".plugin-features").show();
|
||||
|
||||
const settings = extension_settings[extensionName];
|
||||
if (!settings) return; // 安全检查
|
||||
|
||||
// --- 通用设置 ---
|
||||
$("#amily2_enabled").prop("checked", settings.enabled);
|
||||
$("#amily2_api_url").val(settings.apiUrl);
|
||||
$("#amily2_api_key").val(settings.apiKey);
|
||||
$("#amily2_model").val(settings.model);
|
||||
|
||||
|
||||
$("#amily2_max_tokens").val(settings.maxTokens);
|
||||
$("#amily2_max_tokens_value").text(settings.maxTokens);
|
||||
$("#amily2_temperature").val(settings.temperature);
|
||||
$("#amily2_temperature_value").text(settings.temperature);
|
||||
$("#amily2_context_messages").val(settings.contextMessages);
|
||||
$("#amily2_context_messages_value").text(settings.contextMessages);
|
||||
|
||||
|
||||
$(
|
||||
`input[name="amily2_optimization_mode"][value="${settings.optimizationMode}"]`,
|
||||
).prop("checked", true);
|
||||
$("#amily2_optimization_enabled").prop(
|
||||
"checked",
|
||||
settings.optimizationEnabled,
|
||||
);
|
||||
$("#amily2_show_optimization_toast").prop(
|
||||
"checked",
|
||||
settings.showOptimizationToast,
|
||||
);
|
||||
$("#amily2_suppress_toast").prop("checked", settings.suppressToast);
|
||||
|
||||
|
||||
$("#amily2_system_prompt").val(settings.systemPrompt);
|
||||
$("#amily2_main_prompt").val(settings.mainPrompt);
|
||||
$("#amily2_output_format_prompt").val(settings.outputFormatPrompt);
|
||||
$("#amily2_summarization_prompt").val(settings.summarizationPrompt);
|
||||
|
||||
|
||||
$("#amily2_worldbook_enabled").prop("checked", settings.worldbookEnabled);
|
||||
$("#amily2_summarization_enabled").prop(
|
||||
"checked",
|
||||
settings.summarizationEnabled,
|
||||
);
|
||||
$(
|
||||
`input[name="amily2_lorebook_target"][value="${settings.lorebookTarget}"]`,
|
||||
).prop("checked", true);
|
||||
|
||||
populateModelDropdown();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user