@cxyhhhhh/dsh-qqbot 0.5.0-dev.202610101556 → 0.5.0-dev.202610101632
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 +61 -43
- package/lib/client.js.map +1 -1
- package/package.json +1 -1
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");
|
|
@@ -225,11 +243,13 @@ window.__ModuleLoader__.load({
|
|
|
225
243
|
*
|
|
226
244
|
* - 页面隐藏时降频轮询(可见时立即恢复节拍)
|
|
227
245
|
* - 404(已绑定/未安装)时退避到 30s,保持低成本常驻探测
|
|
228
|
-
* -
|
|
246
|
+
* - 每次页面加载至多打开一次(用户手动关闭后不打扰);
|
|
247
|
+
* 打开失败(如启动早期 Session surface 尚未挂载)不消费机会,
|
|
248
|
+
* 由下一轮轮询(3s)自动重试
|
|
229
249
|
*/
|
|
230
250
|
function watchAndPrompt(right) {
|
|
231
251
|
let stopped = false;
|
|
232
|
-
let opened =
|
|
252
|
+
let opened = right === void 0;
|
|
233
253
|
let timer;
|
|
234
254
|
const schedule = (ms) => {
|
|
235
255
|
timer = setTimeout(() => {
|
|
@@ -243,15 +263,13 @@ window.__ModuleLoader__.load({
|
|
|
243
263
|
return;
|
|
244
264
|
}
|
|
245
265
|
try {
|
|
246
|
-
const res = await fetch(
|
|
266
|
+
const res = await fetch("/qqbot/im-qqbot/bind", { cache: "no-store" });
|
|
247
267
|
if (res.ok) {
|
|
248
|
-
if ((await res.json()).phase === "waiting" && !opened) {
|
|
268
|
+
if ((await res.json()).phase === "waiting" && !opened && right !== void 0) try {
|
|
269
|
+
right.openTab(TAB_KIND);
|
|
249
270
|
opened = true;
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
} catch (err) {
|
|
253
|
-
console.error("[im-qqbot] 打开绑定页失败", err);
|
|
254
|
-
}
|
|
271
|
+
} catch (err) {
|
|
272
|
+
console.error("[im-qqbot] 打开绑定页失败,稍后重试", err);
|
|
255
273
|
}
|
|
256
274
|
schedule(3e3);
|
|
257
275
|
return;
|
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 */\nfunction watchAndPrompt(right: SidebarRightLike | undefined): () => void {\n let stopped = false;\n let opened = 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 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) {\n opened = true;\n try {\n right?.openTab(TAB_KIND);\n } catch (err) {\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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"}
|