diff --git a/package.json b/package.json
index eecc6251..e107c502 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,7 @@
"build:prod": "vite build --mode production",
"build": "npm run build:dev",
"build:core": "cd ./src/core && npm run build && cd ../.. && node ./script/copy-core.cjs",
+ "build:webui": "cd ./src/webui && vite build",
"watch": "npm run watch:dev",
"debug-win": "powershell dist/napcat.ps1",
"lint": "eslint --fix src/**/*.{js,ts}",
diff --git a/static/components/NapCat.ts b/src/webui/src/NapCat.ts
similarity index 97%
rename from static/components/NapCat.ts
rename to src/webui/src/NapCat.ts
index 417a52c6..9ffcaa3d 100644
--- a/static/components/NapCat.ts
+++ b/src/webui/src/NapCat.ts
@@ -1,9 +1,9 @@
-import { SettingList } from "./SettingList";
-import { SettingItem } from "./SettingItem";
-import { SettingButton } from "./SettingButton";
-import { SettingSwitch } from "./SettingSwitch";
-import { SettingSelect } from "./SettingSelect";
-import { WebUiApi } from "./WebApi"
+import { SettingList } from "./components/SettingList";
+import { SettingItem } from "./components/SettingItem";
+import { SettingButton } from "./components/SettingButton";
+import { SettingSwitch } from "./components/SettingSwitch";
+import { SettingSelect } from "./components/SettingSelect";
+import { WebUiApi } from "./components/WebApi"
async function onSettingWindowCreated(view: Element) {
const isEmpty = (value: any) => value === undefined || value === undefined || value === '';
let ob11Config = await WebUiApi.getOB11Config();
diff --git a/static/components/SettingButton.ts b/src/webui/src/components/SettingButton.ts
similarity index 100%
rename from static/components/SettingButton.ts
rename to src/webui/src/components/SettingButton.ts
diff --git a/static/components/SettingItem.ts b/src/webui/src/components/SettingItem.ts
similarity index 100%
rename from static/components/SettingItem.ts
rename to src/webui/src/components/SettingItem.ts
diff --git a/static/components/SettingList.ts b/src/webui/src/components/SettingList.ts
similarity index 100%
rename from static/components/SettingList.ts
rename to src/webui/src/components/SettingList.ts
diff --git a/static/components/SettingOption.ts b/src/webui/src/components/SettingOption.ts
similarity index 100%
rename from static/components/SettingOption.ts
rename to src/webui/src/components/SettingOption.ts
diff --git a/static/components/SettingSelect.ts b/src/webui/src/components/SettingSelect.ts
similarity index 100%
rename from static/components/SettingSelect.ts
rename to src/webui/src/components/SettingSelect.ts
diff --git a/static/components/SettingSwitch.ts b/src/webui/src/components/SettingSwitch.ts
similarity index 100%
rename from static/components/SettingSwitch.ts
rename to src/webui/src/components/SettingSwitch.ts
diff --git a/static/components/WebApi.ts b/src/webui/src/components/WebApi.ts
similarity index 100%
rename from static/components/WebApi.ts
rename to src/webui/src/components/WebApi.ts
diff --git a/src/webui/vite.config.ts b/src/webui/vite.config.ts
new file mode 100644
index 00000000..56a17370
--- /dev/null
+++ b/src/webui/vite.config.ts
@@ -0,0 +1,13 @@
+import { defineConfig } from 'vite'
+
+export default defineConfig({
+ build:{
+ target: 'esnext',
+ minify: false,
+ lib: {
+ entry: 'src/NapCat.ts',
+ formats: ['cjs'],
+ fileName: () => 'renderer.js',
+ }
+ }
+});
\ No newline at end of file
diff --git a/static/assets/renderer.js b/static/assets/renderer.js
new file mode 100644
index 00000000..b28e910a
--- /dev/null
+++ b/static/assets/renderer.js
@@ -0,0 +1,386 @@
+'use strict';
+
+Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
+
+const SettingList = (items, title, isCollapsible = false, direction = "column") => {
+ return `
+
+
+ ${items.join("")}
+
+
+ `;
+};
+
+const SettingItem = (title, subtitle, action, id, visible = true) => {
+ return `
+
+ ${title}
+ ${subtitle ? `${subtitle}` : ""}
+
+ ${action ? `${action}
` : ""}
+ `;
+};
+
+const SettingButton = (text, id, type = "secondary") => {
+ return `${text}`;
+};
+
+const SettingSwitch = (configKey, isActive = false, extraData) => {
+ return ` `data-${key}="${extraData[key]}"`) : ""}
+ >
+ `;
+};
+
+const SettingOption = (text, value, isSelected = false) => {
+ return `${text}`;
+};
+
+const SelectTemplate = document.createElement("template");
+SelectTemplate.innerHTML = `
+
",
+ `
+
+ `,
+ SettingList([
+ SettingItem(
+ '
正在检查 Napcat 更新',
+ void 0,
+ SettingButton("请稍候", "napcat-update-button", "secondary")
+ )
+ ]),
+ SettingList([
+ SettingItem(
+ "启用 HTTP 服务",
+ void 0,
+ SettingSwitch("ob11.enableHttp", ob11Config.enableHttp, { "control-display-id": "config-ob11-httpPort" })
+ ),
+ SettingItem(
+ "HTTP 服务监听端口",
+ void 0,
+ `
`,
+ "config-ob11-httpPort",
+ ob11Config.enableHttp
+ ),
+ SettingItem(
+ "启用 HTTP 心跳",
+ void 0,
+ SettingSwitch("ob11.enableHttpHeart", ob11Config.enableHttpHeart, {
+ "control-display-id": "config-ob11-enableHttpHeart"
+ })
+ ),
+ SettingItem(
+ "启用 HTTP 事件上报",
+ void 0,
+ SettingSwitch("ob11.enableHttpPost", ob11Config.enableHttpPost, {
+ "control-display-id": "config-ob11-httpHosts"
+ })
+ ),
+ `
+
+
+ HTTP 事件上报密钥
+
+
+
+
+
+
+
+ HTTP 事件上报地址
+
+ 添加
+
+
+
`,
+ SettingItem(
+ "启用正向 WebSocket 服务",
+ void 0,
+ SettingSwitch("ob11.enableWs", ob11Config.enableWs, { "control-display-id": "config-ob11-wsPort" })
+ ),
+ SettingItem(
+ "正向 WebSocket 服务监听端口",
+ void 0,
+ `
`,
+ "config-ob11-wsPort",
+ ob11Config.enableWs
+ ),
+ SettingItem(
+ "启用反向 WebSocket 服务",
+ void 0,
+ SettingSwitch("ob11.enableWsReverse", ob11Config.enableWsReverse, {
+ "control-display-id": "config-ob11-wsHosts"
+ })
+ ),
+ `
+
+
+ 反向 WebSocket 监听地址
+
+ 添加
+
+
+
`,
+ SettingItem(
+ " WebSocket 服务心跳间隔",
+ "控制每隔多久发送一个心跳包,单位为毫秒",
+ `
`
+ ),
+ SettingItem(
+ "Access token",
+ void 0,
+ `
`
+ ),
+ SettingItem(
+ "新消息上报格式",
+ `如客户端无特殊需求推荐保持默认设置,两者的详细差异可参考
OneBot v11 文档`,
+ SettingSelect(
+ [
+ { text: "消息段", value: "array" },
+ { text: "CQ码", value: "string" }
+ ],
+ "ob11.messagePostFormat",
+ ob11Config.messagePostFormat
+ )
+ ),
+ SettingItem(
+ "音乐卡片签名地址",
+ void 0,
+ `
`,
+ "config-musicSignUrl"
+ ),
+ SettingItem("", void 0, SettingButton("保存", "config-ob11-save", "primary"))
+ ]),
+ SettingList([
+ SettingItem(
+ "上报 Bot 自身发送的消息",
+ "上报 event 为 message_sent",
+ SettingSwitch("reportSelfMessage", ob11Config.reportSelfMessage)
+ )
+ ]),
+ SettingList([
+ SettingItem("GitHub 仓库", `https://github.com/`, SettingButton("点个星星", "open-github")),
+ SettingItem("NapCat 文档", `https://`, SettingButton("看看文档", "open-docs")),
+ SettingItem("Telegram 群", `https://t.me/+nLZEnpne-pQ1OWFl`, SettingButton("进去逛逛", "open-telegram")),
+ SettingItem("QQ 群", `545402644`, SettingButton("我要进去", "open-qq-group"))
+ ]),
+ "
"
+ ].join(""),
+ "text/html"
+ );
+ doc.querySelector("#open-github")?.addEventListener("click", () => {
+ window.open("https://github.com/", "_blank");
+ });
+ doc.querySelector("#open-telegram")?.addEventListener("click", () => {
+ window.open("https://t.me/+nLZEnpne-pQ1OWFl");
+ });
+ doc.querySelector("#open-qq-group")?.addEventListener("click", () => {
+ window.open("https://qm.qq.com/q/bDnHRG38aI");
+ });
+ doc.querySelector("#open-docs")?.addEventListener("click", () => {
+ window.open("https://github.io/");
+ });
+ const buildHostListItem = (type, host, index, inputAttrs = {}) => {
+ const dom = {
+ container: document.createElement("setting-item"),
+ input: document.createElement("input"),
+ inputContainer: document.createElement("div"),
+ deleteBtn: document.createElement("setting-button")
+ };
+ dom.container.classList.add("setting-host-list-item");
+ dom.container.dataset.direction = "row";
+ Object.assign(dom.input, inputAttrs);
+ dom.input.classList.add("q-input__inner");
+ dom.input.type = "url";
+ dom.input.value = host;
+ dom.input.addEventListener("input", () => {
+ ob11Config[type][index] = dom.input.value;
+ });
+ dom.inputContainer.classList.add("q-input");
+ dom.inputContainer.appendChild(dom.input);
+ dom.deleteBtn.innerHTML = "删除";
+ dom.deleteBtn.dataset.type = "secondary";
+ dom.deleteBtn.addEventListener("click", () => {
+ ob11Config[type].splice(index, 1);
+ initReverseHost(type);
+ });
+ dom.container.appendChild(dom.inputContainer);
+ dom.container.appendChild(dom.deleteBtn);
+ return dom.container;
+ };
+ const buildHostList = (hosts, type, inputAttr = {}) => {
+ const result = [];
+ hosts.forEach((host, index) => {
+ result.push(buildHostListItem(type, host, index, inputAttr));
+ });
+ return result;
+ };
+ const addReverseHost = (type, doc2 = document, inputAttr = {}) => {
+ const hostContainerDom = doc2.body.querySelector(`#config-ob11-${type}-list`);
+ hostContainerDom?.appendChild(buildHostListItem(type, "", ob11Config[type].length, inputAttr));
+ ob11Config[type].push("");
+ };
+ const initReverseHost = (type, doc2 = document) => {
+ const hostContainerDom = doc2.body?.querySelector(`#config-ob11-${type}-list`);
+ [...hostContainerDom.childNodes].forEach((dom) => dom.remove());
+ buildHostList(ob11Config[type], type).forEach((dom) => {
+ hostContainerDom?.appendChild(dom);
+ });
+ };
+ initReverseHost("httpHosts", doc);
+ initReverseHost("wsHosts", doc);
+ doc.querySelector("#config-ob11-httpHosts-add")?.addEventListener(
+ "click",
+ () => addReverseHost("httpHosts", document, { placeholder: "如:http://127.0.0.1:5140/onebot" })
+ );
+ doc.querySelector("#config-ob11-wsHosts-add")?.addEventListener(
+ "click",
+ () => addReverseHost("wsHosts", document, { placeholder: "如:ws://127.0.0.1:5140/onebot" })
+ );
+ doc.querySelector("#config-ffmpeg-select")?.addEventListener("click", () => {
+ });
+ doc.querySelector("#config-open-log-path")?.addEventListener("click", () => {
+ });
+ doc.querySelectorAll("setting-switch[data-config-key]").forEach((dom) => {
+ dom.addEventListener("click", () => {
+ const active = dom.getAttribute("is-active") === void 0;
+ setOB11Config(dom.dataset.configKey);
+ if (active)
+ dom.setAttribute("is-active", "");
+ else
+ dom.removeAttribute("is-active");
+ if (!isEmpty(dom.dataset.controlDisplayId)) {
+ const displayDom = document.querySelector(`#${dom.dataset.controlDisplayId}`);
+ if (active)
+ displayDom?.removeAttribute("is-hidden");
+ else
+ displayDom?.setAttribute("is-hidden", "");
+ }
+ });
+ });
+ doc.querySelectorAll("setting-item .q-input input.q-input__inner[data-config-key]").forEach((dom) => {
+ dom.addEventListener("input", () => {
+ const Type = dom.getAttribute("type");
+ dom.dataset.configKey;
+ Type === "number" ? parseInt(dom.value) >= 1 ? parseInt(dom.value) : 1 : dom.value;
+ });
+ });
+ doc.querySelectorAll("ob-setting-select[data-config-key]").forEach((dom) => {
+ dom?.addEventListener("selected", (e) => {
+ dom.dataset.configKey;
+ e.detail.value;
+ });
+ });
+ doc.querySelector("#config-ob11-save")?.addEventListener("click", () => {
+ WebUiApi.setOB11Config(ob11Config);
+ alert("保存成功");
+ });
+}
+
+exports.onSettingWindowCreated = onSettingWindowCreated;
diff --git a/static/index.html b/static/index.html
index 95f91129..7670f6fd 100644
--- a/static/index.html
+++ b/static/index.html
@@ -12,6 +12,12 @@