72 lines
4.1 KiB
Markdown
72 lines
4.1 KiB
Markdown
你的实现逻辑应为:扫描:Vue 前端获取插件列表,读取每个插件的 manifest.json。识别:寻找 settings_file 或类似的字段(或者约定插件根目录下的 settings.html 为面板入口)。渲染:点击插件设置时,由 Vue 动态创建一个 <iframe> 或受控的 DIV 来加载该 HTML。2. 前端实现:插件设置包装组件 (PluginSettingsWrapper.vue)由于 LittleWhiteBox 等插件是基于原生 HTML/JS 编写的,直接注入 Vue 可能会有 CSS 污染。推荐使用 Iframe 沙箱 模式。代码段<template>
|
||
<div class="plugin-settings-container">
|
||
<div class="flex justify-between items-center mb-4">
|
||
<h3 class="text-lg font-bold">{{ plugin.name }}</h3>
|
||
<el-switch v-model="isEnabled" @change="togglePlugin" />
|
||
</div>
|
||
|
||
<div v-if="hasSettingsUI" class="settings-iframe-wrapper">
|
||
<iframe
|
||
ref="settingsIframe"
|
||
:src="settingsUrl"
|
||
class="w-full h-[500px] border-none"
|
||
@load="injectBridge"
|
||
></iframe>
|
||
</div>
|
||
|
||
<div v-else class="text-gray-500">
|
||
该插件没有独立设置面板。
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, onMounted } from 'vue';
|
||
|
||
const props = defineProps(['plugin']); // 后端返回的插件对象
|
||
const settingsIframe = ref(null);
|
||
|
||
// 计算 HTML 入口地址:指向你的 Go 后端静态资源接口
|
||
const settingsUrl = computed(() => {
|
||
return `${import.meta.env.VITE_API_BASE}/plugins/files/${props.plugin.id}/settings.html`;
|
||
});
|
||
|
||
const hasSettingsUI = computed(() => props.plugin.has_settings);
|
||
|
||
// 核心:注入桥接脚本(Bridge)
|
||
const injectBridge = () => {
|
||
const frame = settingsIframe.value;
|
||
if (!frame) return;
|
||
|
||
// 向 Iframe 注入一个全局变量,让插件能调用你的系统功能
|
||
// 例如:LittleWhiteBox 里的 JS 可能会寻找 'SillyTavern' 对象
|
||
const bridgeScript = `
|
||
window.parent.SillyTavern = {
|
||
saveSettings: (data) => {
|
||
window.parent.postMessage({ type: 'SAVE_PLUGIN_SETTINGS', data: data }, '*');
|
||
},
|
||
getSettings: () => ${JSON.stringify(props.plugin.user_settings)}
|
||
};
|
||
`;
|
||
|
||
try {
|
||
const scriptTag = frame.contentDocument.createElement('script');
|
||
scriptTag.textContent = bridgeScript;
|
||
frame.contentDocument.head.appendChild(scriptTag);
|
||
} catch (e) {
|
||
console.error("跨域限制或插件加载失败", e);
|
||
}
|
||
};
|
||
</script>
|
||
3. 后端支持 (Go):静态资源与配置下发你的 Go 后端需要能够提供插件目录下的文件访问。Go// Go 伪代码:静态文件分发
|
||
r.GET("/plugins/files/:plugin_id/*filepath", func(c *gin.Context) {
|
||
pluginID := c.Param("plugin_id")
|
||
filePath := c.Param("filepath")
|
||
|
||
// 从数据库查询该插件的物理路径
|
||
basePath := db.GetPluginPath(pluginID)
|
||
|
||
// 返回对应的 HTML/JS/CSS
|
||
c.File(filepath.Join(basePath, filePath))
|
||
})
|
||
4. 针对 LittleWhiteBox 的适配建议通过分析 LittleWhiteBox 的代码,它包含了大量的模块(如 modules/tts, modules/novel-draw)。这些模块通常通过 index.js 加载。如果你要完美支持它,需要解决以下两个关键点:CSS 样式映射:LittleWhiteBox 的样式可能依赖原版酒馆的 CSS 类名(如 .menu_button, .panel)。解决方案:在你的 Vue 项目中,为插件 iframe 注入一个包含“酒馆兼容层样式”的 CSS 文件,模拟原版的 UI 环境。事件监听 (Event Bus):LittleWhiteBox 里的很多功能(如 modules/event-manager.js)是基于事件触发的。解决方案:在你的 PluginContext 中实现一个简单的事件中心,当你的 Vue 界面发生“消息发送”、“收到回复”等动作时,通过 postMessage 通知 Iframe 内的插件。5. 文档补全:插件设置渲染规范步骤开发者操作系统行为检测后端扫描 manifest.json发现 settings_file: "settings.html"。入口Vue 点击插件列表中的“设置”图标在右侧弹出 Drawer 或 Modal,挂载 PluginSettingsWrapper。握手Iframe 加载完成后执行注入将当前用户的 PgSQL 配置注入插件内存。持久化插件内点击保存插件调用 SillyTavern.saveSettings -> Vue 捕获消息 -> Go 写入 PgSQL。 |