@rezti/dsh-rez-suite 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -4,82 +4,10 @@ window.__ModuleLoader__.load({
4
4
  var module = { exports: {} };
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ (function(){var id="dsh-rez-suite-css";if(typeof document==="undefined"||document.getElementById(id))return;var s=document.createElement("style");s.id=id;s.textContent=".WRGRRW_shell {\n flex-direction: column;\n gap: 12px;\n min-width: 0;\n display: flex;\n}\n\n.WRGRRW_card {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-3);\n border-radius: 12px;\n list-style: none;\n transition: border-color .16s, background .16s;\n}\n\n.WRGRRW_card:hover {\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n.WRGRRW_cardOpen {\n background: var(--dsw-alias-bg-layer-2);\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n.WRGRRW_header {\n appearance: none;\n width: 100%;\n font: inherit;\n color: inherit;\n text-align: left;\n cursor: pointer;\n background: none;\n border: 0;\n border-radius: 12px;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n display: flex;\n}\n\n.WRGRRW_header:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n.WRGRRW_headText {\n flex-direction: column;\n flex: 1;\n gap: 4px;\n min-width: 0;\n display: flex;\n}\n\n.WRGRRW_name {\n color: var(--dsw-alias-label-primary);\n font-size: 15px;\n font-weight: 600;\n line-height: 1.4;\n}\n\n.WRGRRW_description {\n color: var(--dsw-alias-label-tertiary);\n font-size: 13px;\n line-height: 1.5;\n}\n\n.WRGRRW_chevron {\n color: var(--dsw-alias-label-tertiary);\n flex: none;\n transition: transform .16s;\n}\n\n.WRGRRW_chevronOpen {\n transform: rotate(180deg);\n}\n\n.WRGRRW_body {\n border-top: 1px solid var(--dsw-alias-border-l2);\n margin: 0 16px 12px;\n padding-top: 8px;\n}\n\n.WRGRRW_tabBar {\n border-bottom: 1px solid var(--dsw-alias-border-l1);\n flex: none;\n gap: 2px;\n display: flex;\n}\n\n.WRGRRW_tab {\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n background: none;\n border: none;\n border-bottom: 2px solid #0000;\n border-radius: 6px 6px 0 0;\n padding: 7px 14px;\n font-size: 13px;\n}\n\n.WRGRRW_tab:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.WRGRRW_tab[data-active] {\n color: var(--dsw-alias-label-primary);\n border-bottom-color: var(--dsw-alias-state-business-primary);\n font-weight: 600;\n}\n\n.WRGRRW_tabBody {\n flex-direction: column;\n gap: 12px;\n min-width: 0;\n padding: 12px 0 4px;\n display: flex;\n}\n\n.WRGRRW_section {\n border: 1px solid var(--dsw-alias-border-l1);\n background: var(--dsw-alias-bg-layer-1, var(--dsw-alias-bg-base));\n border-radius: 10px;\n flex-direction: column;\n gap: 8px;\n padding: 12px;\n display: flex;\n}\n\n.WRGRRW_sectionTitle {\n color: var(--dsw-alias-label-primary);\n margin: 0;\n font-size: 13px;\n font-weight: 600;\n}\n\n.WRGRRW_field {\n flex-direction: column;\n align-items: stretch;\n gap: 4px;\n display: flex;\n}\n\n.WRGRRW_label {\n color: var(--dsw-alias-label-secondary);\n font-size: 12.5px;\n}\n\n.WRGRRW_input, .WRGRRW_select {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-specific-input-major, var(--dsw-alias-bg-layer-3));\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n outline: none;\n padding: 6px 8px;\n font-size: 13px;\n}\n\n.WRGRRW_checkbox {\n justify-self: start;\n width: 15px;\n height: 15px;\n}\n\n.WRGRRW_toolbar {\n flex-wrap: wrap;\n flex: none;\n align-items: center;\n gap: 8px;\n display: flex;\n}\n\n.WRGRRW_button {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-specific-input-major, var(--dsw-alias-bg-layer-3));\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n border-radius: 8px;\n padding: 7px 12px;\n font-size: 13px;\n}\n\n.WRGRRW_button:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.WRGRRW_button:disabled {\n opacity: .4;\n cursor: default;\n}\n\n.WRGRRW_primaryButton {\n background: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-bg-layer-3);\n}\n\n.WRGRRW_dangerButton {\n background: var(--dsw-alias-state-danger, #c62828);\n border-color: var(--dsw-alias-state-danger, #c62828);\n color: #fff;\n}\n\n.WRGRRW_qr {\n background: #fff;\n border-radius: 8px;\n width: 200px;\n height: 200px;\n margin: 8px 0;\n display: block;\n}\n\n.WRGRRW_message {\n color: var(--dsw-alias-label-secondary);\n margin: 0;\n font-size: 12.5px;\n line-height: 1.5;\n}\n\n.WRGRRW_error {\n color: var(--dsw-alias-state-danger, #c62828);\n}\n\n.WRGRRW_tableWrap {\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n overflow: auto;\n}\n\n.WRGRRW_table {\n border-collapse: collapse;\n width: 100%;\n font-size: 12.5px;\n}\n\n.WRGRRW_table th {\n z-index: 1;\n text-align: left;\n background: var(--dsw-alias-bg-layer-2, var(--dsw-alias-bg-layer-1));\n padding: 8px 10px;\n position: sticky;\n top: 0;\n}\n\n.WRGRRW_table td {\n border-top: 1px solid var(--dsw-alias-border-l1);\n vertical-align: top;\n padding: 8px 10px;\n}\n\n.WRGRRW_badge {\n border-radius: 999px;\n padding: 2px 8px;\n font-size: 11px;\n display: inline-block;\n}\n\n.WRGRRW_badgeOk {\n color: #229a55;\n background: #229a5524;\n}\n\n.WRGRRW_badgeFail {\n color: #c62828;\n background: #c6282824;\n}\n\n.WRGRRW_cards {\n grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));\n gap: 8px;\n display: grid;\n}\n\n.WRGRRW_stat {\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n padding: 10px;\n}\n\n.WRGRRW_cardLabel {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.WRGRRW_cardValue {\n color: var(--dsw-alias-label-primary);\n font-size: 15px;\n font-weight: 600;\n}\n";document.head.appendChild(s);})();
7
8
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
8
- let react_dom_client = require("react-dom/client");
9
9
  let react = require("react");
10
10
  let react_jsx_runtime = require("react/jsx-runtime");
11
- //#region src/protocol.ts
12
- const REZ_API = {
13
- config: "/api/dsh-rez-suite/config",
14
- test: "/api/dsh-rez-suite/test",
15
- status: "/api/dsh-rez-suite/status",
16
- audit: "/api/dsh-rez-suite/audit",
17
- auditReset: "/api/dsh-rez-suite/audit/reset",
18
- weixin: "/api/dsh-rez-suite/weixin",
19
- weixinVerify: "/api/dsh-rez-suite/weixin/verify"
20
- };
21
- //#endregion
22
- //#region src/client/api.ts
23
- /**
24
- * Browser-side API client for /api/dsh-rez-suite. Plain same-origin fetch.
25
- */
26
- var RezApiError = class extends Error {
27
- constructor(message) {
28
- super(message);
29
- this.name = "RezApiError";
30
- }
31
- };
32
- async function readJson(response) {
33
- let body;
34
- try {
35
- body = await response.json();
36
- } catch {
37
- throw new RezApiError("HTTP " + response.status + ": invalid JSON response");
38
- }
39
- if (!response.ok) throw new RezApiError(typeof body === "object" && body !== null && typeof body.error === "string" ? body.error : "HTTP " + response.status);
40
- return body;
41
- }
42
- var RezApi = class {
43
- async getConfig() {
44
- return (await readJson(await fetch(REZ_API.config))).config;
45
- }
46
- async saveConfig(patch) {
47
- return (await readJson(await fetch(REZ_API.config, {
48
- method: "PUT",
49
- headers: { "content-type": "application/json" },
50
- body: JSON.stringify(patch)
51
- }))).config;
52
- }
53
- async test() {
54
- return (await readJson(await fetch(REZ_API.test, { method: "POST" }))).results;
55
- }
56
- async status() {
57
- return readJson(await fetch(REZ_API.status));
58
- }
59
- async audit() {
60
- return (await readJson(await fetch(REZ_API.audit))).audit;
61
- }
62
- async resetAudit() {
63
- await readJson(await fetch(REZ_API.auditReset, { method: "POST" }));
64
- }
65
- async weixinStatus() {
66
- return readJson(await fetch(REZ_API.weixin));
67
- }
68
- async weixinLogin() {
69
- return readJson(await fetch(REZ_API.weixin, { method: "POST" }));
70
- }
71
- async weixinVerify(code) {
72
- return readJson(await fetch(REZ_API.weixinVerify, {
73
- method: "POST",
74
- headers: { "content-type": "application/json" },
75
- body: JSON.stringify({ code })
76
- }));
77
- }
78
- async weixinLogout() {
79
- return readJson(await fetch(REZ_API.weixin, { method: "DELETE" }));
80
- }
81
- };
82
- //#endregion
83
11
  //#region src/client/locales.ts
84
12
  /**
85
13
  * dsh-rez-suite surface copy: zh is the key source, en mirrors every key.
@@ -87,6 +15,8 @@ var RezApi = class {
87
15
  const zh = {
88
16
  "entry.label": "Rez",
89
17
  "entry.tooltip": "Rez Suite 配置与审计面板",
18
+ "settings.title": "ReZ-TI",
19
+ "settings.description": "公司 MCP(Odoo / Nextcloud / 企微 / HA)与个人微信扫码通道。",
90
20
  "panel.title": "Rez Suite",
91
21
  "tab.config": "配置",
92
22
  "tab.weixin": "微信",
@@ -174,6 +104,8 @@ const zh = {
174
104
  const en = {
175
105
  "entry.label": "Rez",
176
106
  "entry.tooltip": "Rez Suite configuration and audit panel",
107
+ "settings.title": "ReZ-TI",
108
+ "settings.description": "Company MCP (Odoo / Nextcloud / WeCom / HA) and personal WeChat scan-and-chat.",
177
109
  "panel.title": "Rez Suite",
178
110
  "tab.config": "Config",
179
111
  "tab.weixin": "WeChat",
@@ -265,6 +197,78 @@ function t(dict, key, values) {
265
197
  return text;
266
198
  }
267
199
  //#endregion
200
+ //#region src/protocol.ts
201
+ const REZ_API = {
202
+ config: "/api/dsh-rez-suite/config",
203
+ test: "/api/dsh-rez-suite/test",
204
+ status: "/api/dsh-rez-suite/status",
205
+ audit: "/api/dsh-rez-suite/audit",
206
+ auditReset: "/api/dsh-rez-suite/audit/reset",
207
+ weixin: "/api/dsh-rez-suite/weixin",
208
+ weixinVerify: "/api/dsh-rez-suite/weixin/verify"
209
+ };
210
+ //#endregion
211
+ //#region src/client/api.ts
212
+ /**
213
+ * Browser-side API client for /api/dsh-rez-suite. Plain same-origin fetch.
214
+ */
215
+ var RezApiError = class extends Error {
216
+ constructor(message) {
217
+ super(message);
218
+ this.name = "RezApiError";
219
+ }
220
+ };
221
+ async function readJson(response) {
222
+ let body;
223
+ try {
224
+ body = await response.json();
225
+ } catch {
226
+ throw new RezApiError("HTTP " + response.status + ": invalid JSON response");
227
+ }
228
+ if (!response.ok) throw new RezApiError(typeof body === "object" && body !== null && typeof body.error === "string" ? body.error : "HTTP " + response.status);
229
+ return body;
230
+ }
231
+ var RezApi = class {
232
+ async getConfig() {
233
+ return (await readJson(await fetch(REZ_API.config))).config;
234
+ }
235
+ async saveConfig(patch) {
236
+ return (await readJson(await fetch(REZ_API.config, {
237
+ method: "PUT",
238
+ headers: { "content-type": "application/json" },
239
+ body: JSON.stringify(patch)
240
+ }))).config;
241
+ }
242
+ async test() {
243
+ return (await readJson(await fetch(REZ_API.test, { method: "POST" }))).results;
244
+ }
245
+ async status() {
246
+ return readJson(await fetch(REZ_API.status));
247
+ }
248
+ async audit() {
249
+ return (await readJson(await fetch(REZ_API.audit))).audit;
250
+ }
251
+ async resetAudit() {
252
+ await readJson(await fetch(REZ_API.auditReset, { method: "POST" }));
253
+ }
254
+ async weixinStatus() {
255
+ return readJson(await fetch(REZ_API.weixin));
256
+ }
257
+ async weixinLogin() {
258
+ return readJson(await fetch(REZ_API.weixin, { method: "POST" }));
259
+ }
260
+ async weixinVerify(code) {
261
+ return readJson(await fetch(REZ_API.weixinVerify, {
262
+ method: "POST",
263
+ headers: { "content-type": "application/json" },
264
+ body: JSON.stringify({ code })
265
+ }));
266
+ }
267
+ async weixinLogout() {
268
+ return readJson(await fetch(REZ_API.weixin, { method: "DELETE" }));
269
+ }
270
+ };
271
+ //#endregion
268
272
  //#region src/client/panel/helpers.ts
269
273
  /**
270
274
  * Shared panel helpers: active-language dictionary pick and error extraction.
@@ -285,38 +289,39 @@ var panel_module_default = {
285
289
  "badge": "WRGRRW_badge",
286
290
  "badgeFail": "WRGRRW_badgeFail",
287
291
  "badgeOk": "WRGRRW_badgeOk",
292
+ "body": "WRGRRW_body",
288
293
  "button": "WRGRRW_button",
289
294
  "card": "WRGRRW_card",
290
295
  "cardLabel": "WRGRRW_cardLabel",
296
+ "cardOpen": "WRGRRW_cardOpen",
291
297
  "cards": "WRGRRW_cards",
292
298
  "cardValue": "WRGRRW_cardValue",
293
299
  "checkbox": "WRGRRW_checkbox",
300
+ "chevron": "WRGRRW_chevron",
301
+ "chevronOpen": "WRGRRW_chevronOpen",
294
302
  "dangerButton": "WRGRRW_dangerButton",
295
- "entry": "WRGRRW_entry",
296
- "entryIcon": "WRGRRW_entryIcon",
297
- "entryLabel": "WRGRRW_entryLabel",
303
+ "description": "WRGRRW_description",
298
304
  "error": "WRGRRW_error",
299
305
  "field": "WRGRRW_field",
300
- "iconButton": "WRGRRW_iconButton",
306
+ "header": "WRGRRW_header",
307
+ "headText": "WRGRRW_headText",
301
308
  "input": "WRGRRW_input",
302
309
  "label": "WRGRRW_label",
303
310
  "message": "WRGRRW_message",
304
- "panel": "WRGRRW_panel",
305
- "panelContent": "WRGRRW_panelContent",
306
- "panelHeader": "WRGRRW_panelHeader",
307
- "panelTitle": "WRGRRW_panelTitle",
311
+ "name": "WRGRRW_name",
308
312
  "primaryButton": "WRGRRW_primaryButton",
309
313
  "qr": "WRGRRW_qr",
310
314
  "section": "WRGRRW_section",
311
315
  "sectionTitle": "WRGRRW_sectionTitle",
312
316
  "select": "WRGRRW_select",
317
+ "shell": "WRGRRW_shell",
318
+ "stat": "WRGRRW_stat",
313
319
  "tab": "WRGRRW_tab",
314
320
  "tabBar": "WRGRRW_tabBar",
315
321
  "tabBody": "WRGRRW_tabBody",
316
322
  "table": "WRGRRW_table",
317
323
  "tableWrap": "WRGRRW_tableWrap",
318
- "toolbar": "WRGRRW_toolbar",
319
- "view": "WRGRRW_view"
324
+ "toolbar": "WRGRRW_toolbar"
320
325
  };
321
326
  //#endregion
322
327
  //#region src/client/panel/AuditTab.tsx
@@ -387,7 +392,7 @@ function AuditTab({ api }) {
387
392
  className: panel_module_default.cards,
388
393
  children: [
389
394
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
390
- className: panel_module_default.card,
395
+ className: panel_module_default.stat,
391
396
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
392
397
  className: panel_module_default.cardLabel,
393
398
  children: tt("audit.totalCalls")
@@ -397,7 +402,7 @@ function AuditTab({ api }) {
397
402
  })]
398
403
  }),
399
404
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
400
- className: panel_module_default.card,
405
+ className: panel_module_default.stat,
401
406
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
402
407
  className: panel_module_default.cardLabel,
403
408
  children: tt("audit.inputTokens")
@@ -407,7 +412,7 @@ function AuditTab({ api }) {
407
412
  })]
408
413
  }),
409
414
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
410
- className: panel_module_default.card,
415
+ className: panel_module_default.stat,
411
416
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
412
417
  className: panel_module_default.cardLabel,
413
418
  children: tt("audit.outputTokens")
@@ -417,7 +422,7 @@ function AuditTab({ api }) {
417
422
  })]
418
423
  }),
419
424
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
420
- className: panel_module_default.card,
425
+ className: panel_module_default.stat,
421
426
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
422
427
  className: panel_module_default.cardLabel,
423
428
  children: tt("audit.totalCost")
@@ -427,7 +432,7 @@ function AuditTab({ api }) {
427
432
  })]
428
433
  }),
429
434
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
430
- className: panel_module_default.card,
435
+ className: panel_module_default.stat,
431
436
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
432
437
  className: panel_module_default.cardLabel,
433
438
  children: tt("audit.monthCost")
@@ -437,7 +442,7 @@ function AuditTab({ api }) {
437
442
  })]
438
443
  }),
439
444
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
440
- className: panel_module_default.card,
445
+ className: panel_module_default.stat,
441
446
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
442
447
  className: panel_module_default.cardLabel,
443
448
  children: tt("audit.budget")
@@ -1090,9 +1095,10 @@ function WeixinTab({ api }) {
1090
1095
  });
1091
1096
  }
1092
1097
  //#endregion
1093
- //#region src/client/panel/RezPanel.tsx
1098
+ //#region src/client/settings-card.tsx
1094
1099
  /**
1095
- * Rez Suite panel shell: header + three tabs (config/status/audit).
1100
+ * ReZ settings pages: one accordion card in 设置 → 插件 → 插件配置,
1101
+ * plus a dedicated Plugins tab for the same content.
1096
1102
  */
1097
1103
  const TABS = [
1098
1104
  {
@@ -1112,27 +1118,12 @@ const TABS = [
1112
1118
  label: () => tt("tab.audit")
1113
1119
  }
1114
1120
  ];
1115
- function RezPanel({ controller, api }) {
1121
+ function RezSettingsPage() {
1122
+ const api = (0, react.useMemo)(() => new RezApi(), []);
1116
1123
  const [activeTab, setActiveTab] = (0, react.useState)("config");
1117
1124
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1118
- className: panel_module_default.panel,
1125
+ className: panel_module_default.shell,
1119
1126
  children: [
1120
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1121
- className: panel_module_default.panelHeader,
1122
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
1123
- className: panel_module_default.panelTitle,
1124
- children: tt("panel.title")
1125
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1126
- type: "button",
1127
- className: panel_module_default.iconButton,
1128
- title: tt("common.close"),
1129
- "aria-label": tt("common.close"),
1130
- onClick: () => {
1131
- controller.close();
1132
- },
1133
- children: "x"
1134
- })]
1135
- }),
1136
1127
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1137
1128
  className: panel_module_default.tabBar,
1138
1129
  role: "tablist",
@@ -1148,218 +1139,76 @@ function RezPanel({ controller, api }) {
1148
1139
  children: tab.label()
1149
1140
  }, tab.id))
1150
1141
  }),
1151
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1152
- className: panel_module_default.panelContent,
1153
- children: [
1154
- activeTab === "config" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfigTab, { api }),
1155
- activeTab === "weixin" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WeixinTab, { api }),
1156
- activeTab === "status" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatusTab, { api }),
1157
- activeTab === "audit" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AuditTab, { api })
1158
- ]
1159
- })
1142
+ activeTab === "config" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfigTab, { api }),
1143
+ activeTab === "weixin" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WeixinTab, { api }),
1144
+ activeTab === "status" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatusTab, { api }),
1145
+ activeTab === "audit" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AuditTab, { api })
1160
1146
  ]
1161
1147
  });
1162
1148
  }
1163
- //#endregion
1164
- //#region src/client/mount.tsx
1165
- /**
1166
- * Rez panel view mounting. Single-occupant center-column takeover, same
1167
- * mechanism as the task board and ssh panels. The conversation subtree stays
1168
- * mounted under the panel.
1169
- */
1170
- const CONVERSATION_COLUMN_SELECTOR = "[data-pane=\"conversation\"]";
1171
- const ACTIVE_ATTR = "data-dsh-rez-active";
1172
- const OTHER_ACTIVE_ATTRS = ["data-dsh-taskboard-active", "data-dsh-ssh-active"];
1173
- const ACTIVATE_EVENT = "dsh-panel-activate";
1174
- const PANEL_NAME = "rez";
1175
- function conversationColumn() {
1176
- return document.querySelector(CONVERSATION_COLUMN_SELECTOR) ?? void 0;
1177
- }
1178
- function mountPanel(controller, api) {
1179
- let root;
1180
- let container;
1181
- const ensure = () => {
1182
- if (container !== void 0) {
1183
- if (container.isConnected) return;
1184
- root?.unmount();
1185
- root = void 0;
1186
- container.remove();
1187
- container = void 0;
1188
- }
1189
- const column = conversationColumn();
1190
- if (column === void 0) return;
1191
- container = document.createElement("div");
1192
- container.dataset.dshRezView = "";
1193
- container.className = panel_module_default.view;
1194
- column.appendChild(container);
1195
- root = (0, react_dom_client.createRoot)(container);
1196
- root.render(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(RezPanel, {
1197
- controller,
1198
- api
1199
- }));
1200
- };
1201
- const waitObserver = new MutationObserver(() => {
1202
- ensure();
1203
- });
1204
- waitObserver.observe(document.body, {
1205
- childList: true,
1206
- subtree: true
1207
- });
1208
- const retryTimer = window.setInterval(() => {
1209
- ensure();
1210
- if (container?.isConnected === true) window.clearInterval(retryTimer);
1211
- }, 250);
1212
- const applyActive = () => {
1213
- if (controller.getSnapshot().panelOpen) {
1214
- for (const attr of OTHER_ACTIVE_ATTRS) document.documentElement.removeAttribute(attr);
1215
- document.documentElement.setAttribute(ACTIVE_ATTR, "");
1216
- document.dispatchEvent(new CustomEvent(ACTIVATE_EVENT, { detail: PANEL_NAME }));
1217
- } else document.documentElement.removeAttribute(ACTIVE_ATTR);
1218
- };
1219
- const onOtherActivate = (event) => {
1220
- const detail = event.detail;
1221
- if ((detail === "taskboard" || detail === "ssh") && controller.getSnapshot().panelOpen) controller.close();
1222
- };
1223
- const SIDEBAR_ROW_SELECTOR = "[class*=\"sessionRow\"], [class*=\"projectRow\"], [class*=\"searchResultRow\"], [class*=\"searchResultWorkspace\"], [class*=\"newSession\"]";
1224
- const onClickSidebarRow = (event) => {
1225
- if (!controller.getSnapshot().panelOpen) return;
1226
- const target = event.target;
1227
- if (target === null) return;
1228
- if (target.closest(SIDEBAR_ROW_SELECTOR) !== null) controller.close();
1229
- };
1230
- document.addEventListener("click", onClickSidebarRow, true);
1231
- document.addEventListener(ACTIVATE_EVENT, onOtherActivate);
1232
- const unsubscribe = controller.subscribe(applyActive);
1233
- applyActive();
1234
- ensure();
1235
- return () => {
1236
- window.clearInterval(retryTimer);
1237
- document.removeEventListener("click", onClickSidebarRow, true);
1238
- document.removeEventListener(ACTIVATE_EVENT, onOtherActivate);
1239
- waitObserver.disconnect();
1240
- unsubscribe();
1241
- document.documentElement.removeAttribute(ACTIVE_ATTR);
1242
- root?.unmount();
1243
- root = void 0;
1244
- container?.remove();
1245
- container = void 0;
1246
- };
1247
- }
1248
- //#endregion
1249
- //#region src/client/panel/controller.ts
1250
- var PanelController = class {
1251
- panelOpen = false;
1252
- listeners = /* @__PURE__ */ new Set();
1253
- getSnapshot() {
1254
- return { panelOpen: this.panelOpen };
1255
- }
1256
- subscribe(fn) {
1257
- this.listeners.add(fn);
1258
- return () => {
1259
- this.listeners.delete(fn);
1260
- };
1261
- }
1262
- open() {
1263
- if (this.panelOpen) return;
1264
- this.panelOpen = true;
1265
- this.notify();
1266
- }
1267
- close() {
1268
- if (!this.panelOpen) return;
1269
- this.panelOpen = false;
1270
- this.notify();
1271
- }
1272
- toggle() {
1273
- if (this.panelOpen) this.close();
1274
- else this.open();
1275
- }
1276
- notify() {
1277
- for (const fn of [...this.listeners]) fn();
1278
- }
1279
- };
1280
- //#endregion
1281
- //#region src/client/sidebar-entry.ts
1282
- function sidebarRoot() {
1283
- const column = document.querySelector("[data-pane=\"sidebar\"], [class*=\"sidebarCol\"]");
1284
- if (column === null) return void 0;
1285
- return column.querySelector("[class*=\"logoRow\"]")?.parentElement ?? column.firstElementChild;
1286
- }
1287
- function newSessionButton(root) {
1288
- const nested = root.querySelector("button[class*=\"newSession\"]");
1289
- if (nested !== null) return nested;
1290
- for (const child of root.children) if (child.tagName === "BUTTON") return child;
1291
- }
1292
- function createEntry(controller) {
1293
- const entry = document.createElement("button");
1294
- entry.type = "button";
1295
- entry.dataset.dshRezEntry = "";
1296
- entry.className = panel_module_default.entry;
1297
- entry.setAttribute("aria-label", tt("entry.label"));
1298
- entry.setAttribute("title", tt("entry.tooltip"));
1299
- entry.innerHTML = "<span class=\"" + panel_module_default.entryIcon + "\"><svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"2.5\" y=\"2.5\" width=\"11\" height=\"11\" rx=\"2\"/><path d=\"M5 8l2 2 4-4\"/></svg></span><span class=\"" + panel_module_default.entryLabel + "\">" + tt("entry.label") + "</span>";
1300
- entry.addEventListener("click", () => {
1301
- controller.toggle();
1302
- });
1303
- return entry;
1304
- }
1305
- function placeEntry(root, entry) {
1306
- const button = newSessionButton(root);
1307
- if (button === void 0) return false;
1308
- if (entry.parentElement !== root) {
1309
- const row = button.closest("[class*=\"logoRow\"]");
1310
- const base = row !== null && row.parentElement === root ? row : button;
1311
- const family = Array.from(root.children).filter((el) => el instanceof HTMLElement && el.matches("[data-dsh-taskboard-entry], [data-dsh-ssh-entry], [data-dsh-rez-entry]"));
1312
- const anchor = family.length > 0 ? family[family.length - 1].nextElementSibling : base.nextElementSibling;
1313
- root.insertBefore(entry, anchor);
1314
- }
1315
- return true;
1316
- }
1317
- function mountSidebarEntry(controller) {
1318
- const entry = createEntry(controller);
1319
- let root;
1320
- let placed = false;
1321
- const place = () => {
1322
- const next = sidebarRoot();
1323
- if (next === void 0) return;
1324
- if (root !== void 0 && root !== next) {
1325
- if (entry.parentElement === root) entry.remove();
1326
- placed = false;
1327
- }
1328
- root = next;
1329
- if (!placed) placed = placeEntry(root, entry);
1330
- };
1331
- const observer = new MutationObserver(place);
1332
- observer.observe(document.body, {
1333
- childList: true,
1334
- subtree: true
1149
+ function RezPluginCard() {
1150
+ const [open, setOpen] = (0, react.useState)(false);
1151
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
1152
+ className: panel_module_default.card + (open ? " " + panel_module_default.cardOpen : ""),
1153
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1154
+ type: "button",
1155
+ className: panel_module_default.header,
1156
+ onClick: () => {
1157
+ setOpen(!open);
1158
+ },
1159
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1160
+ className: panel_module_default.headText,
1161
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1162
+ className: panel_module_default.name,
1163
+ children: tt("settings.title")
1164
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1165
+ className: panel_module_default.description,
1166
+ children: tt("settings.description")
1167
+ })]
1168
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1169
+ className: panel_module_default.chevron + (open ? " " + panel_module_default.chevronOpen : ""),
1170
+ viewBox: "0 0 14 14",
1171
+ width: "14",
1172
+ height: "14",
1173
+ "aria-hidden": "true",
1174
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1175
+ d: "M3 5l4 4 4-4",
1176
+ fill: "none",
1177
+ stroke: "currentColor",
1178
+ strokeWidth: "1.4",
1179
+ strokeLinecap: "round",
1180
+ strokeLinejoin: "round"
1181
+ })
1182
+ })]
1183
+ }), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1184
+ className: panel_module_default.body,
1185
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RezSettingsPage, {})
1186
+ }) : null]
1335
1187
  });
1336
- place();
1337
- return () => {
1338
- observer.disconnect();
1339
- entry.remove();
1340
- };
1341
1188
  }
1342
1189
  //#endregion
1343
1190
  //#region src/client/index.ts
1344
1191
  const NS = "dsh-rez-suite";
1345
1192
  const inject = ["slots", "locale"];
1346
1193
  function apply(ctx) {
1194
+ const t = ctx.locale.bind(NS);
1347
1195
  ctx.effect(() => ctx.locale.register(NS, {
1348
1196
  zh,
1349
1197
  en
1350
1198
  }), "dsh-rez-suite: dictionaries");
1351
- const controller = new PanelController();
1352
- const api = new RezApi();
1353
- const disposers = [];
1354
- try {
1355
- disposers.push(mountSidebarEntry(controller));
1356
- disposers.push(mountPanel(controller, api));
1357
- } catch (error) {
1358
- console.warn("[dsh-rez-suite] mount failed:", error);
1359
- }
1360
- ctx.effect(() => () => {
1361
- for (const dispose of disposers.splice(0)) dispose();
1362
- }, "dsh-rez-suite: ui mounts");
1199
+ ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
1200
+ name: "settings.plugin.item",
1201
+ id: "rez-suite",
1202
+ order: 40,
1203
+ locale: NS
1204
+ }, RezPluginCard));
1205
+ ctx.slots.inject("settings.plugins.tab", () => ctx.slots.register({
1206
+ name: "settings.plugins.tab",
1207
+ id: "rez",
1208
+ order: 20,
1209
+ label: () => t("settings.title"),
1210
+ locale: NS
1211
+ }, RezSettingsPage));
1363
1212
  }
1364
1213
  //#endregion
1365
1214
  exports.apply = apply;
package/lib/index.js CHANGED
@@ -7558,7 +7558,7 @@ const REZ_GUIDANCE = [
7558
7558
  "本机已安装 ReZ-TI 套件(一次安装):工作身份由 dsh-rez-sso 提供,MCP 由官方 dsh-mcp-client 接入 Odoo / Nextcloud / 企业微信 / Home Assistant。",
7559
7559
  "工具名 mcp__odoo__*、mcp__nextcloud__*、mcp__wechat__*、mcp__homeassistant__*。",
7560
7560
  "密钥:REZ_ODOO_TOKEN、REZ_NEXTCLOUD_PASSWORD、REZ_WECHAT_WEBHOOK、REZ_HA_TOKEN。",
7561
- "个人微信(QClaw/ClawBot):打开 Rez 面板「微信」页扫码,然后直接在微信里说话,不必再跑命令、也不要调用微信 MCP 工具。",
7561
+ "个人微信(QClaw/ClawBot):打开 设置 插件 → ReZ-TI → 微信 扫码,然后直接在微信里说话,不必再跑命令、也不要调用微信 MCP 工具。",
7562
7562
  "意图用 rez_set_intent(erp/files/chat/home/auto)。审计用 rez_audit。",
7563
7563
  "涉及资金、对外发送、设备控制、生产写入时先征得用户批准。"
7564
7564
  ].join("");