@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.
@@ -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,CAsC9E"}
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
- const credentials = await ensureCredentials(ctx, { logger });
19
- if (!credentials) {
20
- logger.error('无法获取 QQ Bot 凭据,插件未启动');
21
- return;
22
- }
23
- // 写入环境变量(供热更新后的下次 apply 或本次直接启动读取)
24
- process.env.QQBOT_APPID = credentials.appId;
25
- process.env.QQBOT_SECRET = credentials.appSecret;
26
- appId = credentials.appId;
27
- appSecret = credentials.appSecret;
28
- // 持久化到 profile:成功则等待热更新重载,失败则用 env 凭据直接启动
29
- const persisted = persistCredentialsToProfile(credentials, getProfileDir() ?? undefined, logger);
30
- if (persisted) {
31
- // 写入 cordis.patch.yml 会触发 dsh 热更新,自动重新加载本插件。
32
- // 直接返回,避免与热更新产生竞态。
33
- logger.info('配置已保存,等待热更新重新加载...');
34
- return;
35
- }
36
- logger.warn('凭据未能持久化,本次进程将使用环境变量凭据启动(重启后需重新绑定)');
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,MAAM,WAAW,GAAG,MAAM,iBAAiB,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QAE7D,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;YACrC,OAAO;QACT,CAAC;QAED,mCAAmC;QACnC,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG,WAAW,CAAC,KAAK,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,WAAW,CAAC,SAAS,CAAC;QACjD,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC;QAC1B,SAAS,GAAG,WAAW,CAAC,SAAS,CAAC;QAElC,0CAA0C;QAC1C,MAAM,SAAS,GAAG,2BAA2B,CAAC,WAAW,EAAE,aAAa,EAAE,IAAI,SAAS,EAAE,MAAM,CAAC,CAAC;QACjG,IAAI,SAAS,EAAE,CAAC;YACd,6CAA6C;YAC7C,mBAAmB;YACnB,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;YAClC,OAAO;QACT,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;IACnD,CAAC;IAED,MAAM,cAAc,GAAkB,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAEtE,MAAM,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC;AAC9D,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -22,10 +22,17 @@ export interface EnsureCredentialsOptions {
22
22
  source?: string;
23
23
  }
24
24
  /**
25
- * 执行扫码绑定编排,返回凭据;失败/取消返回 null。
25
+ * 执行扫码绑定编排,返回凭据就绪的 Promise;失败/取消 resolve null。
26
26
  *
27
- * 凭据持久化由调用方(插件入口)执行——成功写入 cordis.patch.yml 会
28
- * 触发 HMR 热重载,新 fiber 以凭据启动网关。
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;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,OAAO,EACZ,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA4ClC"}
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"}
@@ -6,10 +6,17 @@ export { isDesktopHost } from "./presenters.js";
6
6
  /** 进程内活动绑定服务(HMR 接管用) */
7
7
  let activeService = null;
8
8
  /**
9
- * 执行扫码绑定编排,返回凭据;失败/取消返回 null。
9
+ * 执行扫码绑定编排,返回凭据就绪的 Promise;失败/取消 resolve null。
10
10
  *
11
- * 凭据持久化由调用方(插件入口)执行——成功写入 cordis.patch.yml 会
12
- * 触发 HMR 热重载,新 fiber 以凭据启动网关。
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;
@@ -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;;;;;GAKG;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"}
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
- height: "100%",
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
- * 绑定页主体(注册在 sidebar.right.pane.tab 席位)。
73
+ * 绑定状态轮询 hook。
72
74
  *
73
- * 组件只消费框架注入的渲染位置,不依赖业务注入面;
74
- * 状态来自宿主路由轮询,卸载(Tab 关闭/隐藏)时停止轮询。
75
+ * @returns 轮询快照:state 为宿主相位(null = 加载中),gone 表示
76
+ * 路由已消失(已绑定并重载,或插件未启用)。
75
77
  */
76
- function BindBody() {
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
- const refresh = (0, react.useCallback)(async () => {
115
- setRefreshing(true);
116
- try {
117
- await fetch(BIND_REFRESH_URL, { method: "POST" });
118
- setState({ phase: "starting" });
119
- } catch (err) {
120
- console.error("[im-qqbot] 刷新二维码失败", err);
121
- } finally {
122
- setRefreshing(false);
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
- disabled: refreshing,
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
- disabled: refreshing,
149
- onClick: () => {
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
- if (tabs === void 0 || slots === void 0) return;
200
- const disposers = [
201
- tabs.register({
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
- }, BindBody)),
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(BIND_STATE_URL, { cache: "no-store" });
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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cxyhhhhh/dsh-qqbot",
3
- "version": "0.5.0-dev.202610101615",
3
+ "version": "0.5.0-dev.202610101706",
4
4
  "description": "QQ Bot IM channel plugin for deepseek-harness (dsh)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",