@cxyhhhhh/dsh-qqbot 0.5.0-dev.202610101615 → 0.5.0-dev.202610101706
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/dist/index.d.ts.map +1 -1
- package/dist/index.js +37 -20
- package/dist/index.js.map +1 -1
- package/dist/setup/index.d.ts +10 -3
- package/dist/setup/index.d.ts.map +1 -1
- package/dist/setup/index.js +10 -3
- package/dist/setup/index.js.map +1 -1
- package/lib/client.js +53 -35
- package/lib/client.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAQ/D,eAAO,MAAM,IAAI,aAAa,CAAC;AAC/B,eAAO,MAAM,MAAM,UAAa,CAAC;AACjC,eAAO,MAAM,MAAM,2DAAe,CAAC;AAEnC,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAQ/D,eAAO,MAAM,IAAI,aAAa,CAAC;AAC/B,eAAO,MAAM,MAAM,UAAa,CAAC;AACjC,eAAO,MAAM,MAAM,2DAAe,CAAC;AAEnC,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD9E"}
|
package/dist/index.js
CHANGED
|
@@ -14,26 +14,43 @@ export async function apply(ctx, config) {
|
|
|
14
14
|
let appSecret = resolveEnv(config.appSecret, 'QQBOT_SECRET');
|
|
15
15
|
// ── 凭据缺失时唤起扫码绑定 ──
|
|
16
16
|
if (!appId || !appSecret) {
|
|
17
|
-
logger.info('
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
17
|
+
logger.info('凭据未配置,启动扫码绑定...');
|
|
18
|
+
// 关键:不 await 扫码结果。boot 会等 Loader 树稳定(loader.await()),
|
|
19
|
+
// 无限期等待用户扫码会阻塞 Host ready——Desktop 界面无法进入、
|
|
20
|
+
// Web 的 URL 延迟输出。绑定编排(路由/呈现/生命周期)在
|
|
21
|
+
// ensureCredentials 的同步阶段即完成,凭据就绪后由异步链接管。
|
|
22
|
+
void ensureCredentials(ctx, { logger })
|
|
23
|
+
.then((credentials) => {
|
|
24
|
+
if (!credentials) {
|
|
25
|
+
logger.error('无法获取 QQ Bot 凭据,插件未启动');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
// 写入环境变量(供热更新后的下次 apply 或本次直接启动读取)
|
|
29
|
+
process.env.QQBOT_APPID = credentials.appId;
|
|
30
|
+
process.env.QQBOT_SECRET = credentials.appSecret;
|
|
31
|
+
// 持久化到 profile:成功则等待热更新重载,失败则用凭据直接启动
|
|
32
|
+
const persisted = persistCredentialsToProfile(credentials, getProfileDir() ?? undefined, logger);
|
|
33
|
+
if (persisted) {
|
|
34
|
+
// 写入 cordis.patch.yml 会触发 dsh 热更新,自动重新加载本插件。
|
|
35
|
+
logger.info('配置已保存,等待热更新重新加载...');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
logger.warn('凭据未能持久化,本次进程将使用环境变量凭据启动(重启后需重新绑定)');
|
|
39
|
+
// 本 fiber 已激活(ctx 有效),直接以凭据启动网关
|
|
40
|
+
void bootstrapGateway(ctx, agents, {
|
|
41
|
+
...config,
|
|
42
|
+
appId: credentials.appId,
|
|
43
|
+
appSecret: credentials.appSecret,
|
|
44
|
+
}, logger).catch((err) => {
|
|
45
|
+
logger.error(`网关启动失败: ${err instanceof Error ? err.message : String(err)}`);
|
|
46
|
+
});
|
|
47
|
+
})
|
|
48
|
+
.catch((err) => {
|
|
49
|
+
// 全兜:unhandled rejection 会被宿主 fail-loud 判致命
|
|
50
|
+
logger.error(`扫码绑定流程异常: ${err instanceof Error ? err.message : String(err)}`);
|
|
51
|
+
});
|
|
52
|
+
// apply 立即完成,插件 fiber 进入 ACTIVE,Host ready 不再被阻塞
|
|
53
|
+
return;
|
|
37
54
|
}
|
|
38
55
|
const resolvedConfig = { ...config, appId, appSecret };
|
|
39
56
|
await bootstrapGateway(ctx, agents, resolvedConfig, logger);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,YAAY,EAAsB,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,2BAA2B,EAAE,MAAM,kBAAkB,CAAC;AAGlF,qBAAqB;AACrB,MAAM,CAAC,MAAM,IAAI,GAAG,UAAU,CAAC;AAC/B,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,QAAQ,CAAC,CAAC;AACjC,MAAM,CAAC,MAAM,MAAM,GAAG,YAAY,CAAC;AAInC,aAAa;AACb,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,GAAY,EAAE,MAAqB;IAC7D,MAAM,MAAM,GAAI,GAA0C,CAAC,MAA0B,CAAC;IACtF,MAAM,MAAM,GAAa,GAA0C,CAAC,MAAiB,IAAI,OAAO,CAAC;IAEjG,IAAI,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;IACpD,IAAI,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IAE7D,oBAAoB;IACpB,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAE/B,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/setup/index.d.ts
CHANGED
|
@@ -22,10 +22,17 @@ export interface EnsureCredentialsOptions {
|
|
|
22
22
|
source?: string;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
25
|
+
* 执行扫码绑定编排,返回凭据就绪的 Promise;失败/取消 resolve null。
|
|
26
26
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
27
|
+
* 【同步契约】函数体必须保持无 await——绑定编排(服务接管、路由注册、
|
|
28
|
+
* 呈现策略、ctx 生命周期挂载)在调用后的同步阶段全部完成,随后返回
|
|
29
|
+
* `service.start()` 的挂起 Promise。调用方(插件 apply)因此可以不等待
|
|
30
|
+
* 扫码结果直接完成加载:boot 的 `loader.await()` 会等所有插件 fiber
|
|
31
|
+
* settle,若 apply 无限期等待用户扫码,Host ready 会被阻塞
|
|
32
|
+
* (Desktop 界面无法进入、Web URL 延迟输出)。
|
|
33
|
+
*
|
|
34
|
+
* 凭据持久化由调用方在 Promise 的 then 链中执行——成功写入
|
|
35
|
+
* cordis.patch.yml 会触发 HMR 热重载,新 fiber 以凭据启动网关。
|
|
29
36
|
*/
|
|
30
37
|
export declare function ensureCredentials(ctx: Context, options: EnsureCredentialsOptions): Promise<SetupCredentials | null>;
|
|
31
38
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/setup/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAI1C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAKhD,2BAA2B;AAC3B,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,0BAA0B;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/setup/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAI1C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAKhD,2BAA2B;AAC3B,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,0BAA0B;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,OAAO,EACZ,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA4ClC"}
|
package/dist/setup/index.js
CHANGED
|
@@ -6,10 +6,17 @@ export { isDesktopHost } from "./presenters.js";
|
|
|
6
6
|
/** 进程内活动绑定服务(HMR 接管用) */
|
|
7
7
|
let activeService = null;
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* 执行扫码绑定编排,返回凭据就绪的 Promise;失败/取消 resolve null。
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* 【同步契约】函数体必须保持无 await——绑定编排(服务接管、路由注册、
|
|
12
|
+
* 呈现策略、ctx 生命周期挂载)在调用后的同步阶段全部完成,随后返回
|
|
13
|
+
* `service.start()` 的挂起 Promise。调用方(插件 apply)因此可以不等待
|
|
14
|
+
* 扫码结果直接完成加载:boot 的 `loader.await()` 会等所有插件 fiber
|
|
15
|
+
* settle,若 apply 无限期等待用户扫码,Host ready 会被阻塞
|
|
16
|
+
* (Desktop 界面无法进入、Web URL 延迟输出)。
|
|
17
|
+
*
|
|
18
|
+
* 凭据持久化由调用方在 Promise 的 then 链中执行——成功写入
|
|
19
|
+
* cordis.patch.yml 会触发 HMR 热重载,新 fiber 以凭据启动网关。
|
|
13
20
|
*/
|
|
14
21
|
export async function ensureCredentials(ctx, options) {
|
|
15
22
|
const logger = options.logger;
|
package/dist/setup/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/setup/index.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAsB,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAIjG,OAAO,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,yBAAyB;AACzB,IAAI,aAAa,GAA0B,IAAI,CAAC;AAShD
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/setup/index.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAsB,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAIjG,OAAO,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,yBAAyB;AACzB,IAAI,aAAa,GAA0B,IAAI,CAAC;AAShD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,GAAY,EACZ,OAAiC;IAEjC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAC9B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,WAAW,CAAC;IAE7C,mCAAmC;IACnC,MAAM,OAAO,GAAG,IAAI,cAAc,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACvD,IAAI,aAAa,KAAK,IAAI,IAAI,aAAa,KAAK,OAAO,EAAE,CAAC;QACxD,aAAa,CAAC,OAAO,EAAE,CAAC;IAC1B,CAAC;IACD,aAAa,GAAG,OAAO,CAAC;IAExB,gCAAgC;IAC/B,GAAwE,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE;QAC1F,OAAO,CAAC,OAAO,EAAE,CAAC;QAClB,IAAI,aAAa,KAAK,OAAO;YAAE,aAAa,GAAG,IAAI,CAAC;IACtD,CAAC,EAAE,kBAAkB,CAAC,CAAC;IAEvB,QAAQ;IACR,6CAA6C;IAC7C,4CAA4C;IAC5C,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC;QACrB,OAAO,CAAC,MAAM,CAAC,uBAAuB,EAAE,CAAC,CAAC;IAC5C,CAAC;IACD,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,GAAG,EAAE,CAAC;QAChD,OAAO,CAAC,MAAM,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,0BAA0B;IAC1B,4CAA4C;IAC5C,uCAAuC;IACvC,2CAA2C;IAC1C,GAEC,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE;QAClC,MAAM,SAAS,GAAI,MAA6C,CAAC,SAAsC,CAAC;QACxG,IAAI,SAAS,KAAK,SAAS,IAAI,OAAO,SAAS,CAAC,QAAQ,KAAK,UAAU;YAAE,OAAO;QAChF,MAAM,aAAa,GAAG,kBAAkB,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QACrE,4BAA4B;QAC3B,MAA2E,CAAC,MAAM,CAAC,GAAG,EAAE;YACvF,OAAO,aAAa,CAAC;QACvB,CAAC,EAAE,sBAAsB,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC,KAAK,EAAE,CAAC;AACzB,CAAC"}
|
package/lib/client.js
CHANGED
|
@@ -7,12 +7,13 @@ window.__ModuleLoader__.load({
|
|
|
7
7
|
let react = require("react");
|
|
8
8
|
//#region src/client/bind-page.ts
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* 端内绑定面板 — 二维码展示与状态轮询
|
|
11
11
|
*
|
|
12
12
|
* 宿主插件在「待绑定」时注册 /qqbot/im-qqbot/bind 状态路由,本组件
|
|
13
13
|
* 轮询该路由渲染二维码与实时状态;扫码成功后宿主写入 profile 并经
|
|
14
14
|
* HMR 重载,路由随之消失(404),页面显示完成态。
|
|
15
15
|
*
|
|
16
|
+
* 同一组件复用于两种展示位(侧栏绑定页 / 设置页 QQ Bot 栏目)。
|
|
16
17
|
* 运行时仅依赖 react(dsh 客户端平台种子表提供);宿主通信走同源 fetch,
|
|
17
18
|
* Desktop 下由壳的 IPC 桥自动附加 Host 认证转发。
|
|
18
19
|
*/
|
|
@@ -23,6 +24,7 @@ window.__ModuleLoader__.load({
|
|
|
23
24
|
/** 轮询间隔(毫秒):活跃轮询 / 失败退避 */
|
|
24
25
|
const ACTIVE_POLL_MS = 2e3;
|
|
25
26
|
const RETRY_POLL_MS = 3e3;
|
|
27
|
+
/** 面板容器(设置页 / 侧栏通用) */
|
|
26
28
|
const CONTAINER = {
|
|
27
29
|
display: "flex",
|
|
28
30
|
flexDirection: "column",
|
|
@@ -30,7 +32,7 @@ window.__ModuleLoader__.load({
|
|
|
30
32
|
justifyContent: "center",
|
|
31
33
|
gap: "14px",
|
|
32
34
|
padding: "24px",
|
|
33
|
-
|
|
35
|
+
minHeight: "320px",
|
|
34
36
|
textAlign: "center",
|
|
35
37
|
fontSize: "14px"
|
|
36
38
|
};
|
|
@@ -68,15 +70,14 @@ window.__ModuleLoader__.load({
|
|
|
68
70
|
lineHeight: 1
|
|
69
71
|
};
|
|
70
72
|
/**
|
|
71
|
-
*
|
|
73
|
+
* 绑定状态轮询 hook。
|
|
72
74
|
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
+
* @returns 轮询快照:state 为宿主相位(null = 加载中),gone 表示
|
|
76
|
+
* 路由已消失(已绑定并重载,或插件未启用)。
|
|
75
77
|
*/
|
|
76
|
-
function
|
|
78
|
+
function useBindState() {
|
|
77
79
|
const [state, setState] = (0, react.useState)(null);
|
|
78
80
|
const [gone, setGone] = (0, react.useState)(false);
|
|
79
|
-
const [refreshing, setRefreshing] = (0, react.useState)(false);
|
|
80
81
|
(0, react.useEffect)(() => {
|
|
81
82
|
let stopped = false;
|
|
82
83
|
let timer;
|
|
@@ -111,26 +112,31 @@ window.__ModuleLoader__.load({
|
|
|
111
112
|
if (timer !== void 0) clearTimeout(timer);
|
|
112
113
|
};
|
|
113
114
|
}, []);
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
115
|
+
return {
|
|
116
|
+
state,
|
|
117
|
+
gone
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/** 请求宿主刷新二维码任务 */
|
|
121
|
+
async function refreshBindTask() {
|
|
122
|
+
try {
|
|
123
|
+
await fetch(BIND_REFRESH_URL, { method: "POST" });
|
|
124
|
+
} catch (err) {
|
|
125
|
+
console.error("[im-qqbot] 刷新二维码失败", err);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/** 依据宿主相位渲染面板主体(各展示位共用) */
|
|
129
|
+
function BindPanelBody() {
|
|
130
|
+
const { state, gone } = useBindState();
|
|
131
|
+
const refresh = (0, react.useCallback)(() => {
|
|
132
|
+
refreshBindTask();
|
|
124
133
|
}, []);
|
|
125
134
|
if (gone) return (0, react.createElement)("div", { style: CONTAINER }, (0, react.createElement)("div", { style: STATUS_ICON }, "✅"), (0, react.createElement)("p", { style: HINT }, "绑定已完成,或插件未启用。"));
|
|
126
135
|
if (state === null || state.phase === "starting") return (0, react.createElement)("div", { style: CONTAINER }, (0, react.createElement)("p", { style: HINT }, "正在生成二维码…"));
|
|
127
136
|
if (state.phase === "saving") return (0, react.createElement)("div", { style: CONTAINER }, (0, react.createElement)("div", { style: STATUS_ICON }, "🎉"), (0, react.createElement)("p", { style: TITLE }, "扫码成功"), (0, react.createElement)("p", { style: HINT }, "凭据已保存,插件正在重载,稍后即可开始对话。"));
|
|
128
137
|
if (state.phase === "failed") return (0, react.createElement)("div", { style: CONTAINER }, (0, react.createElement)("div", { style: STATUS_ICON }, "⚠️"), (0, react.createElement)("p", { style: TITLE }, "绑定失败"), (0, react.createElement)("p", { style: HINT }, state.reason), (0, react.createElement)("button", {
|
|
129
138
|
style: BUTTON,
|
|
130
|
-
|
|
131
|
-
onClick: () => {
|
|
132
|
-
refresh();
|
|
133
|
-
}
|
|
139
|
+
onClick: refresh
|
|
134
140
|
}, "重试"));
|
|
135
141
|
return (0, react.createElement)("div", { style: CONTAINER }, state.qrSvg === void 0 ? null : (0, react.createElement)("img", {
|
|
136
142
|
src: state.qrSvg,
|
|
@@ -145,11 +151,8 @@ window.__ModuleLoader__.load({
|
|
|
145
151
|
style: LINK
|
|
146
152
|
}, "在浏览器中打开扫码页"), (0, react.createElement)("button", {
|
|
147
153
|
style: BUTTON,
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
refresh();
|
|
151
|
-
}
|
|
152
|
-
}, refreshing ? "正在刷新…" : "刷新二维码"));
|
|
154
|
+
onClick: refresh
|
|
155
|
+
}, "刷新二维码"));
|
|
153
156
|
}
|
|
154
157
|
//#endregion
|
|
155
158
|
//#region src/client/index.ts
|
|
@@ -192,13 +195,28 @@ window.__ModuleLoader__.load({
|
|
|
192
195
|
strokeLinecap: "round"
|
|
193
196
|
}));
|
|
194
197
|
}
|
|
198
|
+
/** 设置页「QQ Bot」栏目:面板常驻展示绑定状态 */
|
|
199
|
+
function QQBotSettingsSection() {
|
|
200
|
+
return (0, react.createElement)("div", null, (0, react.createElement)("p", { style: {
|
|
201
|
+
margin: "0 0 8px",
|
|
202
|
+
opacity: .75,
|
|
203
|
+
lineHeight: 1.6,
|
|
204
|
+
fontSize: "13px"
|
|
205
|
+
} }, "扫码将 QQ 机器人绑定到当前配置;绑定完成后机器人接入私聊与群聊。"), (0, react.createElement)(BindPanelBody));
|
|
206
|
+
}
|
|
195
207
|
function apply(ctx) {
|
|
196
208
|
const tabs = ctx.sidebarRightTabs;
|
|
197
209
|
const right = ctx.sidebarRight;
|
|
198
210
|
const slots = ctx.slots;
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
211
|
+
const disposers = [];
|
|
212
|
+
if (slots !== void 0) disposers.push(slots.inject("settings.section", () => slots.register({
|
|
213
|
+
name: "settings.section",
|
|
214
|
+
id: "qqbot",
|
|
215
|
+
order: 7,
|
|
216
|
+
label: "QQ Bot"
|
|
217
|
+
}, QQBotSettingsSection)));
|
|
218
|
+
if (tabs !== void 0 && slots !== void 0) {
|
|
219
|
+
disposers.push(tabs.register({
|
|
202
220
|
id: TAB_ID,
|
|
203
221
|
kind: TAB_KIND,
|
|
204
222
|
title: () => "QQ Bot 绑定",
|
|
@@ -209,13 +227,13 @@ window.__ModuleLoader__.load({
|
|
|
209
227
|
description: () => "扫码将 QQ 机器人绑定到当前配置",
|
|
210
228
|
icon: BindGuideIcon
|
|
211
229
|
}]
|
|
212
|
-
})
|
|
213
|
-
slots.inject("sidebar.right.pane.tab", () => slots.register({
|
|
230
|
+
}));
|
|
231
|
+
disposers.push(slots.inject("sidebar.right.pane.tab", () => slots.register({
|
|
214
232
|
name: "sidebar.right.pane.tab",
|
|
215
233
|
key: TAB_ID
|
|
216
|
-
},
|
|
217
|
-
watchAndPrompt(right)
|
|
218
|
-
|
|
234
|
+
}, BindPanelBody)));
|
|
235
|
+
disposers.push(watchAndPrompt(right));
|
|
236
|
+
}
|
|
219
237
|
ctx.effect(() => () => {
|
|
220
238
|
for (const dispose of disposers.reverse()) dispose();
|
|
221
239
|
}, "im-qqbot-client.binding");
|
|
@@ -245,7 +263,7 @@ window.__ModuleLoader__.load({
|
|
|
245
263
|
return;
|
|
246
264
|
}
|
|
247
265
|
try {
|
|
248
|
-
const res = await fetch(
|
|
266
|
+
const res = await fetch("/qqbot/im-qqbot/bind", { cache: "no-store" });
|
|
249
267
|
if (res.ok) {
|
|
250
268
|
if ((await res.json()).phase === "waiting" && !opened && right !== void 0) try {
|
|
251
269
|
right.openTab(TAB_KIND);
|
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["useState","useCallback","createElement","createElement"],"sources":["../src/client/bind-page.ts","../src/client/index.ts"],"sourcesContent":["/**\n * 端内绑定页 — 二维码展示与状态轮询\n *\n * 宿主插件在「待绑定」时注册 /qqbot/im-qqbot/bind 状态路由,本组件\n * 轮询该路由渲染二维码与实时状态;扫码成功后宿主写入 profile 并经\n * HMR 重载,路由随之消失(404),页面显示完成态。\n *\n * 运行时仅依赖 react(dsh 客户端平台种子表提供);宿主通信走同源 fetch,\n * Desktop 下由壳的 IPC 桥自动附加 Host 认证转发。\n */\nimport { createElement, useCallback, useEffect, useState, type CSSProperties, type ReactNode } from 'react';\n\n/** 状态查询路由(宿主 bind-routes 注册) */\nexport const BIND_STATE_URL = '/qqbot/im-qqbot/bind';\n\n/** 刷新路由 */\nconst BIND_REFRESH_URL = '/qqbot/im-qqbot/bind/refresh';\n\n/** 轮询间隔(毫秒):活跃轮询 / 失败退避 */\nconst ACTIVE_POLL_MS = 2000;\nconst RETRY_POLL_MS = 3000;\n\n/** 宿主路由的序列化状态(与 src/setup/bind-routes.ts 的协议保持一致) */\ntype BindStateWire =\n | { phase: 'starting' }\n | { phase: 'waiting'; url: string; qrSvg?: string; issuedAt: number }\n | { phase: 'saving' }\n | { phase: 'failed'; reason: string };\n\nconst CONTAINER: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '14px',\n padding: '24px',\n height: '100%',\n textAlign: 'center',\n fontSize: '14px',\n};\n\nconst QR_IMAGE: CSSProperties = {\n borderRadius: '8px',\n background: '#fff',\n padding: '10px',\n maxWidth: '100%',\n};\n\nconst TITLE: CSSProperties = { margin: 0, fontSize: '16px', fontWeight: 600 };\nconst HINT: CSSProperties = { margin: 0, opacity: 0.75, lineHeight: 1.6 };\nconst LINK: CSSProperties = { fontSize: '13px', opacity: 0.85 };\nconst BUTTON: CSSProperties = {\n padding: '6px 16px',\n borderRadius: '6px',\n border: '1px solid currentColor',\n background: 'transparent',\n color: 'inherit',\n cursor: 'pointer',\n fontSize: '13px',\n};\n\nconst STATUS_ICON: CSSProperties = { fontSize: '32px', lineHeight: 1 };\n\n/**\n * 绑定页主体(注册在 sidebar.right.pane.tab 席位)。\n *\n * 组件只消费框架注入的渲染位置,不依赖业务注入面;\n * 状态来自宿主路由轮询,卸载(Tab 关闭/隐藏)时停止轮询。\n */\nexport function BindBody(): ReactNode {\n const [state, setState] = useState<BindStateWire | null>(null);\n const [gone, setGone] = useState(false);\n const [refreshing, setRefreshing] = useState(false);\n\n useEffect(() => {\n let stopped = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const schedule = (ms: number): void => {\n timer = setTimeout(() => { void tick(); }, ms);\n };\n\n const tick = async (): Promise<void> => {\n if (stopped) return;\n try {\n const res = await fetch(BIND_STATE_URL, { cache: 'no-store' });\n if (res.status === 404) {\n // 已绑定(插件重载后路由消失)或插件未启用\n if (!stopped) setGone(true);\n return;\n }\n if (!res.ok) {\n schedule(RETRY_POLL_MS);\n return;\n }\n const body = (await res.json()) as BindStateWire;\n if (stopped) return;\n setState(body);\n schedule(body.phase === 'saving' ? 500 : ACTIVE_POLL_MS);\n return;\n } catch {\n // 宿主启动窗口期不可达:退避重试\n }\n schedule(RETRY_POLL_MS);\n };\n\n void tick();\n return () => { stopped = true; if (timer !== undefined) clearTimeout(timer); };\n }, []);\n\n const refresh = useCallback(async (): Promise<void> => {\n setRefreshing(true);\n try {\n await fetch(BIND_REFRESH_URL, { method: 'POST' });\n setState({ phase: 'starting' });\n } catch (err) {\n console.error('[im-qqbot] 刷新二维码失败', err);\n } finally {\n setRefreshing(false);\n }\n }, []);\n\n if (gone) {\n return createElement('div', { style: CONTAINER },\n createElement('div', { style: STATUS_ICON }, '✅'),\n createElement('p', { style: HINT }, '绑定已完成,或插件未启用。'),\n );\n }\n\n if (state === null || state.phase === 'starting') {\n return createElement('div', { style: CONTAINER },\n createElement('p', { style: HINT }, '正在生成二维码…'),\n );\n }\n\n if (state.phase === 'saving') {\n return createElement('div', { style: CONTAINER },\n createElement('div', { style: STATUS_ICON }, '🎉'),\n createElement('p', { style: TITLE }, '扫码成功'),\n createElement('p', { style: HINT }, '凭据已保存,插件正在重载,稍后即可开始对话。'),\n );\n }\n\n if (state.phase === 'failed') {\n return createElement('div', { style: CONTAINER },\n createElement('div', { style: STATUS_ICON }, '⚠️'),\n createElement('p', { style: TITLE }, '绑定失败'),\n createElement('p', { style: HINT }, state.reason),\n createElement('button', { style: BUTTON, disabled: refreshing, onClick: () => { void refresh(); } }, '重试'),\n );\n }\n\n return createElement('div', { style: CONTAINER },\n state.qrSvg === undefined\n ? null\n : createElement('img', { src: state.qrSvg, alt: 'QQ 扫码二维码', width: 220, height: 220, style: QR_IMAGE }),\n createElement('p', { style: TITLE }, 'QQ Bot 绑定'),\n createElement('p', { style: HINT }, '请使用手机 QQ 扫描上方二维码,完成机器人绑定'),\n createElement('a', { href: state.url, target: '_blank', rel: 'noreferrer noopener', style: LINK }, '在浏览器中打开扫码页'),\n createElement('button', { style: BUTTON, disabled: refreshing, onClick: () => { void refresh(); } },\n refreshing ? '正在刷新…' : '刷新二维码'),\n );\n}\n","/**\n * im-qqbot 浏览器半边 —— QQ Bot 端内扫码绑定入口\n *\n * 宿主插件在凭据缺失时注册 /qqbot/im-qqbot/bind 状态路由;本插件在\n * Web / Desktop 的右侧栏注册「QQ Bot 绑定」页,并在检测到待绑定状态时\n * 自动打开(每次页面加载至多一次)。\n *\n * 运行时依赖仅 react(客户端平台种子表提供);宿主侧栏服务经 ctx 注入,\n * 以防御式探测访问——组合缺失(如未来精简构建)时静默退出,不阻塞浏览器启动。\n */\nimport type { Context } from '@deepseek-ai/cordis';\nimport { createElement } from 'react';\nimport { BindBody, BIND_STATE_URL } from './bind-page.ts';\n\nexport const name = 'im-qqbot-client';\nexport const inject = ['slots', 'sidebarRight', 'sidebarRightTabs'];\n\n/** 侧栏 Tab 类型(ui-sidebar-right 契约的局部投影) */\nconst TAB_KIND = 'qqbot-bind';\n/** 席位注册 key(Tab 类型 id,席位按其分发;只需全局唯一,与包名解耦) */\nconst TAB_ID = 'im-qqbot/bind';\n\n/** ui-sidebar-right 的 Tab 注册表服务(局部投影) */\ninterface TabRegistryLike {\n register(definition: {\n id: string;\n kind: string;\n title: (address: string) => string;\n guide?: ReadonlyArray<{\n id: string;\n order: number;\n title: () => string;\n description: () => string;\n icon: unknown;\n }>;\n }): () => void;\n}\n\n/** ui-sidebar-right 的导航控制器服务(局部投影) */\ninterface SidebarRightLike {\n openTab(kind: string, options?: Record<string, unknown>): unknown;\n}\n\n/** ui-slots 的席位系统服务(局部投影) */\ninterface SlotsLike {\n inject(slotName: string, register: () => (() => void)): () => void;\n register(spec: { name: string; key: string }, component: unknown): () => void;\n}\n\n/** 绑定入口图标(机器人轮廓,跟随主题 currentColor) */\nfunction BindGuideIcon(): unknown {\n return createElement('svg', { width: 24, height: 24, viewBox: '0 0 24 24', fill: 'none', 'aria-hidden': true },\n createElement('path', { d: 'M12 3a6 6 0 0 1 6 6v1.5a4.5 4.5 0 0 1 0 9H6a4.5 4.5 0 0 1 0-9V9a6 6 0 0 1 6-6Z', fill: 'currentColor', fillOpacity: 0.9 }),\n createElement('circle', { cx: 9.5, cy: 12, r: 1.4, fill: '#fff' }),\n createElement('circle', { cx: 14.5, cy: 12, r: 1.4, fill: '#fff' }),\n createElement('path', { d: 'M9.5 15.5a3 3 0 0 0 5 0', stroke: '#fff', strokeWidth: 1.2, strokeLinecap: 'round' }),\n );\n}\n\nexport function apply(ctx: Context): void {\n const tabs = (ctx as unknown as { sidebarRightTabs?: TabRegistryLike }).sidebarRightTabs;\n const right = (ctx as unknown as { sidebarRight?: SidebarRightLike }).sidebarRight;\n const slots = (ctx as unknown as { slots?: SlotsLike }).slots;\n // 侧栏组合缺失:静默退出(宿主绑定路由与终端呈现仍然可用)\n if (tabs === undefined || slots === undefined) return;\n\n const disposers: Array<() => void> = [\n // 1. Tab 类型注册(guide 入口 + 打开时的标题捕获)\n tabs.register({\n id: TAB_ID,\n kind: TAB_KIND,\n title: () => 'QQ Bot 绑定',\n guide: [{\n id: 'bind',\n order: 90,\n title: () => 'QQ Bot 绑定',\n description: () => '扫码将 QQ 机器人绑定到当前配置',\n icon: BindGuideIcon,\n }],\n }),\n // 2. Tab 主体(keyed 席位按 Tab 类型 id 分发)\n slots.inject('sidebar.right.pane.tab', () => slots.register(\n { name: 'sidebar.right.pane.tab', key: TAB_ID },\n BindBody,\n )),\n // 3. 待绑定检测与自动引导\n watchAndPrompt(right),\n ];\n\n (ctx as unknown as { effect(fn: () => () => void, name?: string): void }).effect(() => () => {\n for (const dispose of disposers.reverse()) dispose();\n }, 'im-qqbot-client.binding');\n}\n\n/**\n * 轮询绑定状态:待绑定时自动打开绑定页。\n *\n * - 页面隐藏时降频轮询(可见时立即恢复节拍)\n * - 404(已绑定/未安装)时退避到 30s,保持低成本常驻探测\n * - 每次页面加载至多打开一次(用户手动关闭后不打扰);\n * 打开失败(如启动早期 Session surface 尚未挂载)不消费机会,\n * 由下一轮轮询(3s)自动重试\n */\nfunction watchAndPrompt(right: SidebarRightLike | undefined): () => void {\n let stopped = false;\n let opened = right === undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const schedule = (ms: number): void => {\n timer = setTimeout(() => { void tick(); }, ms);\n };\n\n const tick = async (): Promise<void> => {\n if (stopped) return;\n if (typeof document !== 'undefined' && document.visibilityState === 'hidden') {\n schedule(5000);\n return;\n }\n try {\n const res = await fetch(BIND_STATE_URL, { cache: 'no-store' });\n if (res.ok) {\n const body = (await res.json()) as { phase?: string };\n if (body.phase === 'waiting' && !opened && right !== undefined) {\n try {\n right.openTab(TAB_KIND);\n opened = true;\n } catch (err) {\n // 启动早期 Session surface 可能尚未挂载:保留机会,3s 后随轮询重试\n console.error('[im-qqbot] 打开绑定页失败,稍后重试', err);\n }\n }\n schedule(3000);\n return;\n }\n // 404:已绑定或插件未启用\n schedule(30000);\n return;\n } catch {\n // 宿主暂不可达(启动窗口期):退避重试\n }\n schedule(10000);\n };\n\n void tick();\n return () => { stopped = true; if (timer !== undefined) clearTimeout(timer); };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["useState","useCallback","createElement","createElement"],"sources":["../src/client/bind-page.ts","../src/client/index.ts"],"sourcesContent":["/**\n * 端内绑定面板 — 二维码展示与状态轮询\n *\n * 宿主插件在「待绑定」时注册 /qqbot/im-qqbot/bind 状态路由,本组件\n * 轮询该路由渲染二维码与实时状态;扫码成功后宿主写入 profile 并经\n * HMR 重载,路由随之消失(404),页面显示完成态。\n *\n * 同一组件复用于两种展示位(侧栏绑定页 / 设置页 QQ Bot 栏目)。\n * 运行时仅依赖 react(dsh 客户端平台种子表提供);宿主通信走同源 fetch,\n * Desktop 下由壳的 IPC 桥自动附加 Host 认证转发。\n */\nimport { createElement, useCallback, useEffect, useState, type CSSProperties, type ReactNode } from 'react';\n\n/** 状态查询路由(宿主 bind-routes 注册) */\nexport const BIND_STATE_URL = '/qqbot/im-qqbot/bind';\n\n/** 刷新路由 */\nconst BIND_REFRESH_URL = '/qqbot/im-qqbot/bind/refresh';\n\n/** 轮询间隔(毫秒):活跃轮询 / 失败退避 */\nconst ACTIVE_POLL_MS = 2000;\nconst RETRY_POLL_MS = 3000;\n\n/** 宿主路由的序列化状态(与 src/setup/bind-routes.ts 的协议保持一致) */\nexport type BindStateWire =\n | { phase: 'starting' }\n | { phase: 'waiting'; url: string; qrSvg?: string; issuedAt: number }\n | { phase: 'saving' }\n | { phase: 'failed'; reason: string };\n\n/** 面板容器(设置页 / 侧栏通用) */\nconst CONTAINER: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '14px',\n padding: '24px',\n minHeight: '320px',\n textAlign: 'center',\n fontSize: '14px',\n};\n\nconst QR_IMAGE: CSSProperties = {\n borderRadius: '8px',\n background: '#fff',\n padding: '10px',\n maxWidth: '100%',\n};\n\nconst TITLE: CSSProperties = { margin: 0, fontSize: '16px', fontWeight: 600 };\nconst HINT: CSSProperties = { margin: 0, opacity: 0.75, lineHeight: 1.6 };\nconst LINK: CSSProperties = { fontSize: '13px', opacity: 0.85 };\nconst BUTTON: CSSProperties = {\n padding: '6px 16px',\n borderRadius: '6px',\n border: '1px solid currentColor',\n background: 'transparent',\n color: 'inherit',\n cursor: 'pointer',\n fontSize: '13px',\n};\n\nconst STATUS_ICON: CSSProperties = { fontSize: '32px', lineHeight: 1 };\n\n/**\n * 绑定状态轮询 hook。\n *\n * @returns 轮询快照:state 为宿主相位(null = 加载中),gone 表示\n * 路由已消失(已绑定并重载,或插件未启用)。\n */\nfunction useBindState(): { state: BindStateWire | null; gone: boolean } {\n const [state, setState] = useState<BindStateWire | null>(null);\n const [gone, setGone] = useState(false);\n\n useEffect(() => {\n let stopped = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const schedule = (ms: number): void => {\n timer = setTimeout(() => { void tick(); }, ms);\n };\n\n const tick = async (): Promise<void> => {\n if (stopped) return;\n try {\n const res = await fetch(BIND_STATE_URL, { cache: 'no-store' });\n if (res.status === 404) {\n // 已绑定(插件重载后路由消失)或插件未启用\n if (!stopped) setGone(true);\n return;\n }\n if (!res.ok) {\n schedule(RETRY_POLL_MS);\n return;\n }\n const body = (await res.json()) as BindStateWire;\n if (stopped) return;\n setState(body);\n schedule(body.phase === 'saving' ? 500 : ACTIVE_POLL_MS);\n return;\n } catch {\n // 宿主启动窗口期不可达:退避重试\n }\n schedule(RETRY_POLL_MS);\n };\n\n void tick();\n return () => { stopped = true; if (timer !== undefined) clearTimeout(timer); };\n }, []);\n\n return { state, gone };\n}\n\n/** 请求宿主刷新二维码任务 */\nasync function refreshBindTask(): Promise<void> {\n try {\n await fetch(BIND_REFRESH_URL, { method: 'POST' });\n } catch (err) {\n console.error('[im-qqbot] 刷新二维码失败', err);\n }\n}\n\n/** 依据宿主相位渲染面板主体(各展示位共用) */\nexport function BindPanelBody(): ReactNode {\n const { state, gone } = useBindState();\n const refresh = useCallback(() => { void refreshBindTask(); }, []);\n\n if (gone) {\n return createElement('div', { style: CONTAINER },\n createElement('div', { style: STATUS_ICON }, '✅'),\n createElement('p', { style: HINT }, '绑定已完成,或插件未启用。'),\n );\n }\n\n if (state === null || state.phase === 'starting') {\n return createElement('div', { style: CONTAINER },\n createElement('p', { style: HINT }, '正在生成二维码…'),\n );\n }\n\n if (state.phase === 'saving') {\n return createElement('div', { style: CONTAINER },\n createElement('div', { style: STATUS_ICON }, '🎉'),\n createElement('p', { style: TITLE }, '扫码成功'),\n createElement('p', { style: HINT }, '凭据已保存,插件正在重载,稍后即可开始对话。'),\n );\n }\n\n if (state.phase === 'failed') {\n return createElement('div', { style: CONTAINER },\n createElement('div', { style: STATUS_ICON }, '⚠️'),\n createElement('p', { style: TITLE }, '绑定失败'),\n createElement('p', { style: HINT }, state.reason),\n createElement('button', { style: BUTTON, onClick: refresh }, '重试'),\n );\n }\n\n return createElement('div', { style: CONTAINER },\n state.qrSvg === undefined\n ? null\n : createElement('img', { src: state.qrSvg, alt: 'QQ 扫码二维码', width: 220, height: 220, style: QR_IMAGE }),\n createElement('p', { style: TITLE }, 'QQ Bot 绑定'),\n createElement('p', { style: HINT }, '请使用手机 QQ 扫描上方二维码,完成机器人绑定'),\n createElement('a', { href: state.url, target: '_blank', rel: 'noreferrer noopener', style: LINK }, '在浏览器中打开扫码页'),\n createElement('button', { style: BUTTON, onClick: refresh }, '刷新二维码'),\n );\n}\n","/**\n * im-qqbot 浏览器半边 —— QQ Bot 端内扫码绑定入口\n *\n * 宿主插件在凭据缺失时注册 /qqbot/im-qqbot/bind 状态路由;本插件在\n * Web / Desktop 提供两个展示位(参照 dsh-whale-musume 的验证模式):\n *\n * 1. 设置页「QQ Bot」栏目(settings.section 席位)——注册即展示,\n * 无 Session surface 时序依赖,待绑定/已绑定状态常驻可见;\n * 2. 右侧栏「QQ Bot 绑定」页(sidebarRightTabs)——Start 页入口 +\n * 待绑定时自动打开(失败随轮询重试)。\n *\n * 运行时依赖仅 react(客户端平台种子表提供);宿主服务经 cordis inject\n * 声明访问(声明缺失时插件保持 inactive,不阻塞浏览器启动)。\n */\nimport type { Context } from '@deepseek-ai/cordis';\nimport { createElement, type ReactNode } from 'react';\nimport { BindPanelBody } from './bind-page.ts';\n\nexport const name = 'im-qqbot-client';\nexport const inject = ['slots', 'sidebarRight', 'sidebarRightTabs'];\n\n/** 侧栏 Tab 类型(ui-sidebar-right 契约的局部投影) */\nconst TAB_KIND = 'qqbot-bind';\n/** 席位注册 key(Tab 类型 id,席位按其分发;只需全局唯一,与包名解耦) */\nconst TAB_ID = 'im-qqbot/bind';\n\n/** ui-sidebar-right 的 Tab 注册表服务(局部投影) */\ninterface TabRegistryLike {\n register(definition: {\n id: string;\n kind: string;\n title: (address: string) => string;\n guide?: ReadonlyArray<{\n id: string;\n order: number;\n title: () => string;\n description: () => string;\n icon: unknown;\n }>;\n }): () => void;\n}\n\n/** ui-sidebar-right 的导航控制器服务(局部投影) */\ninterface SidebarRightLike {\n openTab(kind: string, options?: Record<string, unknown>): unknown;\n}\n\n/** ui-slots 的席位系统服务(局部投影) */\ninterface SlotsLike {\n inject(slotName: string, register: () => (() => void)): () => void;\n register(spec: Record<string, unknown>, component: unknown): () => void;\n}\n\n/** 绑定入口图标(机器人轮廓,跟随主题 currentColor) */\nfunction BindGuideIcon(): ReactNode {\n return createElement('svg', { width: 24, height: 24, viewBox: '0 0 24 24', fill: 'none', 'aria-hidden': true },\n createElement('path', { d: 'M12 3a6 6 0 0 1 6 6v1.5a4.5 4.5 0 0 1 0 9H6a4.5 4.5 0 0 1 0-9V9a6 6 0 0 1 6-6Z', fill: 'currentColor', fillOpacity: 0.9 }),\n createElement('circle', { cx: 9.5, cy: 12, r: 1.4, fill: '#fff' }),\n createElement('circle', { cx: 14.5, cy: 12, r: 1.4, fill: '#fff' }),\n createElement('path', { d: 'M9.5 15.5a3 3 0 0 0 5 0', stroke: '#fff', strokeWidth: 1.2, strokeLinecap: 'round' }),\n );\n}\n\n/** 设置页「QQ Bot」栏目:面板常驻展示绑定状态 */\nfunction QQBotSettingsSection(): ReactNode {\n return createElement('div', null,\n createElement('p', { style: { margin: '0 0 8px', opacity: 0.75, lineHeight: 1.6, fontSize: '13px' } },\n '扫码将 QQ 机器人绑定到当前配置;绑定完成后机器人接入私聊与群聊。'),\n createElement(BindPanelBody),\n );\n}\n\nexport function apply(ctx: Context): void {\n const tabs = (ctx as unknown as { sidebarRightTabs?: TabRegistryLike }).sidebarRightTabs;\n const right = (ctx as unknown as { sidebarRight?: SidebarRightLike }).sidebarRight;\n const slots = (ctx as unknown as { slots?: SlotsLike }).slots;\n\n const disposers: Array<() => void> = [];\n\n // 1. 设置页「QQ Bot」栏目(主展示位:注册即展示,无时序依赖)\n if (slots !== undefined) {\n disposers.push(slots.inject('settings.section', () => slots.register(\n { name: 'settings.section', id: 'qqbot', order: 7, label: 'QQ Bot' },\n QQBotSettingsSection,\n )));\n }\n\n // 2. 侧栏绑定页(快捷展示位:Start 页入口 + 待绑定自动打开)\n if (tabs !== undefined && slots !== undefined) {\n disposers.push(tabs.register({\n id: TAB_ID,\n kind: TAB_KIND,\n title: () => 'QQ Bot 绑定',\n guide: [{\n id: 'bind',\n order: 90,\n title: () => 'QQ Bot 绑定',\n description: () => '扫码将 QQ 机器人绑定到当前配置',\n icon: BindGuideIcon,\n }],\n }));\n disposers.push(slots.inject('sidebar.right.pane.tab', () => slots.register(\n { name: 'sidebar.right.pane.tab', key: TAB_ID },\n BindPanelBody,\n )));\n // 待绑定检测与自动引导\n disposers.push(watchAndPrompt(right));\n }\n\n (ctx as unknown as { effect(fn: () => () => void, name?: string): void }).effect(() => () => {\n for (const dispose of disposers.reverse()) dispose();\n }, 'im-qqbot-client.binding');\n}\n\n/**\n * 轮询绑定状态:待绑定时自动打开绑定页。\n *\n * - 页面隐藏时降频轮询(可见时立即恢复节拍)\n * - 404(已绑定/未安装)时退避到 30s,保持低成本常驻探测\n * - 每次页面加载至多打开一次(用户手动关闭后不打扰);\n * 打开失败(如启动早期 Session surface 尚未挂载)不消费机会,\n * 由下一轮轮询(3s)自动重试\n */\nfunction watchAndPrompt(right: SidebarRightLike | undefined): () => void {\n let stopped = false;\n let opened = right === undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const schedule = (ms: number): void => {\n timer = setTimeout(() => { void tick(); }, ms);\n };\n\n const tick = async (): Promise<void> => {\n if (stopped) return;\n if (typeof document !== 'undefined' && document.visibilityState === 'hidden') {\n schedule(5000);\n return;\n }\n try {\n const res = await fetch('/qqbot/im-qqbot/bind', { cache: 'no-store' });\n if (res.ok) {\n const body = (await res.json()) as { phase?: string };\n if (body.phase === 'waiting' && !opened && right !== undefined) {\n try {\n right.openTab(TAB_KIND);\n opened = true;\n } catch (err) {\n // 启动早期 Session surface 可能尚未挂载:保留机会,3s 后随轮询重试\n console.error('[im-qqbot] 打开绑定页失败,稍后重试', err);\n }\n }\n schedule(3000);\n return;\n }\n // 404:已绑定或插件未启用\n schedule(30000);\n return;\n } catch {\n // 宿主暂不可达(启动窗口期):退避重试\n }\n schedule(10000);\n };\n\n void tick();\n return () => { stopped = true; if (timer !== undefined) clearTimeout(timer); };\n}\n"],"mappings":"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"}
|