@cxyhhhhh/dsh-qqbot 0.5.0-dev.202610101632 → 0.5.0-dev.202610101921

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.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 * 同一组件复用于两种展示位(侧栏绑定页 / 设置页 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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"}
1
+ {"version":3,"file":"client.js","names":["STYLE_ID","useState","useCallback","createElement","createElement","useState","useCallback","createElement"],"sources":["../src/shared/bind-protocol.ts","../src/client/bind-page.ts","../src/client/config-panel.ts","../src/client/index.ts"],"sourcesContent":["/**\n * 端内绑定协议 —— 宿主路由与客户端面板的共享契约\n *\n * 类型与路径常量的唯一事实源:宿主(setup/bind-routes.ts)与浏览器\n * (client/bind-page.ts)各自 import,杜绝两端协议漂移。\n * 本模块必须保持纯类型 + 纯常量(零 node 依赖、零副作用):\n * 客户端 tsdown 构建会将其内联进 bundle。\n */\n\n/** 状态查询(绑定页轮询) */\nexport const BIND_STATE_PATH = '/qqbot/im-qqbot/bind';\n\n/** 刷新二维码(绑定页按钮) */\nexport const BIND_REFRESH_PATH = '/qqbot/im-qqbot/bind/refresh';\n\n/** 手动填写/编辑凭据(绑定页表单) */\nexport const BIND_MANUAL_PATH = '/qqbot/im-qqbot/bind/manual';\n\n/** 解绑:移除已保存凭据,回到绑定流程 */\nexport const BIND_UNBIND_PATH = '/qqbot/im-qqbot/bind/unbind';\n\n/** 配置读写(设置面板表单) */\nexport const BIND_CONFIG_PATH = '/qqbot/im-qqbot/config';\n\n/** 线上协议(HTTP 路由序列化形态) */\nexport type BindStateWire =\n | { phase: 'starting' }\n | { phase: 'waiting'; url: string; qrSvg?: string; issuedAt: number }\n | { phase: 'saving' }\n | { phase: 'failed'; reason: string }\n | { phase: 'bound'; appId: string };\n\n/** manual / unbind 操作的统一响应 */\nexport interface BindActionResponse {\n ok: boolean;\n message?: string;\n error?: string;\n}\n","/**\n * 端内绑定面板 — 扫码 / 手动填写 / 已绑定编辑\n *\n * 宿主插件注册 /qqbot/im-qqbot/bind 路由(待绑定与已绑定 fiber 均常驻):\n * - 未绑定:waiting(二维码)/ starting / saving / failed 相位 + manual 提交\n * - 已绑定:bound 相位(回显脱敏 appId,secret 不回传)+ manual 更换 + 解绑\n * 扫码成功或手动保存均写入 profile patch 并经 HMR 重载生效。\n *\n * 样式为注入式 CSS 类(imqb-* 命名空间,随面板挂载注入一次):\n * hover/过渡/focus 样式无法用 inline style 表达,统一走类;\n * 颜色尽量取 DSW 主题变量(--dsw-alias-*),缺省值兜底。\n *\n * 同一组件复用于两种展示位(侧栏绑定页 / 设置页 QQ Bot 栏目)。\n * 运行时仅依赖 react(dsh 客户端平台种子表提供);宿主通信走同源 fetch,\n * Desktop 下由壳的 IPC 桥自动附加 Host 认证转发。\n */\nimport { createElement, useCallback, useEffect, useState, type FormEvent, type ReactNode } from 'react';\nimport {\n BIND_MANUAL_PATH,\n BIND_REFRESH_PATH,\n BIND_STATE_PATH,\n BIND_UNBIND_PATH,\n type BindStateWire,\n} from '../shared/bind-protocol.ts';\n\n/** 轮询间隔(毫秒):活跃轮询 / 失败退避 */\nconst ACTIVE_POLL_MS = 2000;\nconst RETRY_POLL_MS = 3000;\n\n/** 样式注入标记(幂等) */\nconst STYLE_ID = 'im-qqbot-bind-panel-styles';\n\n/**\n * 注入面板样式(一次)。交互态(hover/active/focus)无法用 inline style\n * 表达,统一收敛到类;DSW 主题变量缺失时用中性值兜底。\n */\nfunction usePanelStyles(): void {\n useEffect(() => {\n if (typeof document === 'undefined') return;\n if (document.getElementById(STYLE_ID) !== null) return;\n const style = document.createElement('style');\n style.id = STYLE_ID;\n style.textContent = `\n.imqb-panel {\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n gap: 14px; padding: 24px; min-height: 320px; text-align: center; font-size: 14px;\n}\n.imqb-title { margin: 0; font-size: 16px; font-weight: 600; }\n.imqb-hint { margin: 0; opacity: 0.75; line-height: 1.6; }\n.imqb-mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px; }\n.imqb-link { font-size: 13px; opacity: 0.85; color: var(--dsw-alias-brand, #4d6bfe); text-decoration: none; }\n.imqb-link:hover { text-decoration: underline; }\n.imqb-icon { font-size: 32px; line-height: 1; }\n.imqb-qr { border-radius: 10px; background: #fff; padding: 10px; max-width: 100%; }\n.imqb-row { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }\n.imqb-btn, .imqb-pill {\n font: inherit; cursor: pointer; color: inherit;\n transition: background-color .15s ease, border-color .15s ease, color .15s ease, opacity .15s ease;\n}\n.imqb-btn:disabled, .imqb-pill:disabled { opacity: 0.5; cursor: default; }\n.imqb-btn {\n padding: 7px 18px; border-radius: 8px; font-size: 13px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n background: transparent;\n}\n.imqb-btn:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.12)); }\n.imqb-btn--primary {\n border-color: transparent;\n background: var(--dsw-alias-brand, #4d6bfe); color: #fff; font-weight: 500;\n}\n.imqb-btn--primary:hover:not(:disabled) { background: var(--dsw-alias-brand-strong, #3b5cf0); }\n.imqb-btn--danger { color: var(--dsw-alias-danger, #f26d6d); border-color: var(--dsw-alias-danger, rgba(242, 109, 109, 0.5)); }\n.imqb-btn--danger:hover:not(:disabled) { background: var(--dsw-alias-danger, rgba(242, 109, 109, 0.12)); }\n.imqb-ghost {\n border: none; background: transparent; opacity: 0.65; padding: 4px 8px;\n font: inherit; font-size: 13px; cursor: pointer; color: inherit;\n}\n.imqb-ghost:hover { opacity: 1; text-decoration: underline; }\n.imqb-pill {\n padding: 5px 16px; border-radius: 999px; font-size: 13px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4)); background: transparent;\n}\n.imqb-pill:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.12)); }\n.imqb-btn:focus-visible, .imqb-pill:focus-visible, .imqb-input:focus-visible, .imqb-ghost:focus-visible {\n outline: 2px solid var(--dsw-alias-brand, #4d6bfe); outline-offset: 1px;\n}\n.imqb-form { display: flex; flex-direction: column; gap: 12px; width: min(320px, 100%); box-sizing: border-box; }\n.imqb-field { display: flex; flex-direction: column; gap: 5px; text-align: left; font-size: 13px; }\n.imqb-input {\n padding: 8px 10px; border-radius: 8px; box-sizing: border-box; width: 100%;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n background: var(--dsw-alias-bg-input, rgba(128, 128, 128, 0.08));\n color: inherit; font: inherit; font-size: 13px; outline: none;\n}\n.imqb-input:focus { border-color: var(--dsw-alias-brand, #4d6bfe); }\n.imqb-error { margin: 0; font-size: 12px; color: var(--dsw-alias-danger, #f26d6d); }\n/* 紧凑状态视图(已绑定 / 过渡态):设置行风格,无居中大卡留白 */\n.imqb-status { display: flex; flex-direction: column; text-align: left; font-size: 14px; }\n.imqb-status-row { display: flex; align-items: center; gap: 12px; padding: 12px 0; }\n.imqb-status-icon { font-size: 24px; line-height: 1; flex: none; }\n.imqb-qqicon {\n flex: none; display: block; border-radius: 8px;\n background: #fff; /* 深色企鹅 logo 需浅底,暗色主题下保持可见 */\n}\n.imqb-status-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }\n.imqb-status-title { margin: 0; font-size: 14px; font-weight: 600; line-height: 1.5; }\n.imqb-status-sub { margin: 0; font-size: 12px; line-height: 1.5; color: var(--dsw-alias-label-tertiary, inherit); opacity: .85; }\n.imqb-status-actions { display: flex; align-items: center; gap: 8px; padding: 4px 0 12px; }\n `;\n document.head.appendChild(style);\n return () => { style.remove(); };\n }, []);\n}\n\n/**\n * 绑定状态轮询 hook。\n *\n * 404 语义分两种:从未拿到过路由 = 插件未启用(终态);\n * 曾经拿到过 = HMR 重载窗口期(manual/unbind 保存后路由短暂消失),\n * 低频等待恢复而不是判定终态——否则保存成功提示会被\n * 「插件未启用」覆盖且无法自愈。\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 sawRoute = 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_PATH, { cache: 'no-store' });\n if (res.status === 404) {\n if (!stopped && !sawRoute) setGone(true);\n // sawRoute:HMR 窗口期,保留当前视图低频等待路由恢复\n if (!stopped && sawRoute) schedule(10000);\n return;\n }\n if (!res.ok) {\n schedule(RETRY_POLL_MS);\n return;\n }\n sawRoute = true;\n const body = (await res.json()) as BindStateWire;\n if (stopped) return;\n setState(body);\n // bound 为稳定态:大幅降频;saving 即将切换;其余活跃轮询\n schedule(body.phase === 'saving' ? 500 : body.phase === 'bound' ? 30000 : 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\ninterface ManualResult { ok: boolean; message?: string; error?: string }\n\n/** 手动提交凭据(填写 / 更换共用) */\nasync function submitManualCredentials(appId: string, appSecret: string): Promise<ManualResult> {\n try {\n const res = await fetch(BIND_MANUAL_PATH, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ appId, appSecret }),\n });\n const body = (await res.json().catch(() => ({}))) as ManualResult;\n if (res.ok && body.ok) return { ok: true, message: body.message };\n return { ok: false, error: body.error ?? `保存失败(HTTP ${res.status})` };\n } catch (err) {\n return { ok: false, error: err instanceof Error ? err.message : String(err) };\n }\n}\n\n/**\n * 手动凭据表单(填写 / 更换共用)。\n *\n * appSecret 永不回显(占位符提示),提交成功后展示保存结果;\n * 宿主写入 profile patch 并经 HMR 重载,状态轮询随后收敛为 bound。\n */\nfunction ManualForm({ initialAppId }: { initialAppId?: string }): ReactNode {\n const [appId, setAppId] = useState(initialAppId ?? '');\n const [appSecret, setAppSecret] = useState('');\n const [busy, setBusy] = useState(false);\n const [result, setResult] = useState<ManualResult | null>(null);\n\n const submit = useCallback((event: FormEvent): void => {\n event.preventDefault();\n if (busy) return;\n const trimmedId = appId.trim();\n const trimmedSecret = appSecret.trim();\n if (trimmedId.length === 0 || trimmedSecret.length === 0) {\n setResult({ ok: false, error: 'appId 与 appSecret 均为必填' });\n return;\n }\n setBusy(true);\n void submitManualCredentials(trimmedId, trimmedSecret).then((outcome) => {\n setResult(outcome);\n setBusy(false);\n });\n }, [appId, appSecret, busy]);\n\n if (result !== null && result.ok) {\n return createElement('div', { className: 'imqb-status' },\n createElement('div', { className: 'imqb-status-row' },\n createElement('div', { className: 'imqb-status-icon' }, '✅'),\n createElement('div', { className: 'imqb-status-meta' },\n createElement('p', { className: 'imqb-status-title' }, result.message ?? '凭据已保存'),\n createElement('p', { className: 'imqb-status-sub' }, '插件将自动重载;稍后即可开始对话'),\n ),\n ),\n );\n }\n\n return createElement('form', { className: 'imqb-form', onSubmit: submit },\n createElement('label', { className: 'imqb-field' },\n 'AppID',\n createElement('input', {\n className: 'imqb-input', value: appId, placeholder: 'QQ 机器人 AppID',\n onChange: (event) => { setAppId(event.currentTarget.value); },\n autoComplete: 'off', spellCheck: false,\n }),\n ),\n createElement('label', { className: 'imqb-field' },\n 'AppSecret',\n createElement('input', {\n className: 'imqb-input', type: 'password', value: appSecret,\n placeholder: initialAppId === undefined ? 'QQ 机器人 AppSecret' : '输入新的 AppSecret(不回显)',\n onChange: (event) => { setAppSecret(event.currentTarget.value); },\n autoComplete: 'new-password',\n }),\n ),\n result !== null && !result.ok\n ? createElement('p', { className: 'imqb-error' }, result.error)\n : null,\n createElement('button', { className: 'imqb-btn imqb-btn--primary', type: 'submit', disabled: busy },\n busy ? '正在保存…' : '保存'),\n );\n}\n\n/** QQ 企鹅 logo(256×256 PNG data URI;深色图案配白底圆角,明暗主题下均可见) */\nconst QQ_BOT_ICON_PNG =\n 'data:image/png;base64,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';\n\n/** QQ 机器人图标(logo 图片,白底圆角容器内呈现) */\nfunction QqBotIcon({ size = 28 }: { size?: number }): ReactNode {\n return createElement('img', {\n src: QQ_BOT_ICON_PNG, alt: 'QQ', width: size, height: size, className: 'imqb-qqicon',\n });\n}\n\n/** 已绑定视图:回显 appId + 更换凭据 / 解绑入口 */\nfunction BoundView({ appId }: { appId: string }): ReactNode {\n const [editing, setEditing] = useState(false);\n const [unbindResult, setUnbindResult] = useState<{ ok: boolean; message: string } | null>(null);\n\n const unbind = useCallback((): void => {\n if (!window.confirm('确定解绑吗?\\n解绑后机器人立即下线,插件将重新进入扫码绑定流程。')) return;\n void fetch(BIND_UNBIND_PATH, { method: 'POST' })\n .then(async (res) => {\n const body = (await res.json().catch(() => ({}))) as { ok?: boolean; message?: string; error?: string };\n if (res.ok && body.ok === true) {\n setUnbindResult({ ok: true, message: body.message ?? '已解绑,插件将重载并重新进入绑定流程' });\n return;\n }\n setUnbindResult({ ok: false, message: body.error ?? `解绑失败(HTTP ${res.status})` });\n })\n .catch((err: unknown) => {\n setUnbindResult({ ok: false, message: err instanceof Error ? err.message : String(err) });\n });\n }, []);\n\n if (unbindResult !== null) {\n return createElement('div', { className: 'imqb-status' },\n createElement('div', { className: 'imqb-status-row' },\n createElement('div', { className: 'imqb-status-icon' }, unbindResult.ok ? '👋' : '⚠️'),\n createElement('div', { className: 'imqb-status-meta' },\n createElement('p', { className: 'imqb-status-title' }, unbindResult.message),\n ),\n ),\n );\n }\n\n if (editing) {\n return createElement('div', { className: 'imqb-status' },\n createElement('div', { className: 'imqb-status-row' },\n createElement('div', { className: 'imqb-status-meta' },\n createElement('p', { className: 'imqb-status-title' }, '更换凭据'),\n ),\n createElement('button', { className: 'imqb-ghost', onClick: () => { setEditing(false); } }, '返回'),\n ),\n createElement(ManualForm, { initialAppId: appId }),\n );\n }\n return createElement('div', { className: 'imqb-status' },\n createElement('div', { className: 'imqb-status-row' },\n createElement(QqBotIcon, { size: 28 }),\n createElement('div', { className: 'imqb-status-meta' },\n createElement('p', { className: 'imqb-status-title' }, '已绑定'),\n createElement('p', { className: 'imqb-status-sub imqb-mono' }, `AppID ${appId} · 私聊与群聊已接入`),\n ),\n ),\n createElement('div', { className: 'imqb-status-actions' },\n createElement('button', { className: 'imqb-btn imqb-btn--primary', onClick: () => { setEditing(true); } }, '更换凭据'),\n createElement('button', { className: 'imqb-btn imqb-btn--danger', onClick: unbind }, '解绑'),\n ),\n );\n}\n\n/** 未绑定视图:扫码 / 手动填写切换 */\nfunction UnboundView({ state }: { state: Extract<BindStateWire, { phase: 'waiting' }> }): ReactNode {\n const [mode, setMode] = useState<'qr' | 'manual'>('qr');\n const refresh = useCallback(() => { void fetch(BIND_REFRESH_PATH, { method: 'POST' }).catch(() => {}); }, []);\n\n if (mode === 'manual') {\n return createElement('div', { className: 'imqb-panel' },\n createElement('p', { className: 'imqb-title' }, '手动填写凭据'),\n createElement(ManualForm, {}),\n createElement('button', { className: 'imqb-ghost', onClick: () => { setMode('qr'); } }, '改用扫码绑定'),\n );\n }\n\n return createElement('div', { className: 'imqb-panel' },\n state.qrSvg === undefined\n ? null\n : createElement('img', { src: state.qrSvg, alt: 'QQ 扫码二维码', width: 220, height: 220, className: 'imqb-qr' }),\n createElement('p', { className: 'imqb-title' }, '扫码绑定 QQ 机器人'),\n createElement('p', { className: 'imqb-hint' }, '使用手机 QQ 扫描二维码完成绑定'),\n createElement('a', { href: state.url, target: '_blank', rel: 'noreferrer noopener', className: 'imqb-link' }, '二维码显示异常?在浏览器中打开'),\n createElement('div', { className: 'imqb-row' },\n createElement('button', { className: 'imqb-pill', onClick: refresh }, '刷新二维码'),\n createElement('button', { className: 'imqb-pill', onClick: () => { setMode('manual'); } }, '手动填写'),\n ),\n );\n}\n\n/** 依据宿主相位渲染面板主体(各展示位共用) */\nexport function BindPanelBody(): ReactNode {\n usePanelStyles();\n const { state, gone } = useBindState();\n\n if (gone) {\n return createElement('div', { className: 'imqb-panel' },\n createElement('div', { className: 'imqb-icon' }, '❔'),\n createElement('p', { className: 'imqb-hint' }, '插件未启用,或当前版本不支持端内配置'),\n );\n }\n\n if (state === null || state.phase === 'starting') {\n return createElement('div', { className: 'imqb-panel' },\n createElement('p', { className: 'imqb-hint' }, '正在获取绑定状态…'),\n );\n }\n\n if (state.phase === 'saving') {\n return createElement('div', { className: 'imqb-status' },\n createElement('div', { className: 'imqb-status-row' },\n createElement('div', { className: 'imqb-status-icon' }, '🎉'),\n createElement('div', { className: 'imqb-status-meta' },\n createElement('p', { className: 'imqb-status-title' }, '扫码成功'),\n createElement('p', { className: 'imqb-status-sub' }, '凭据已保存,插件正在重载,稍后即可开始对话'),\n ),\n ),\n );\n }\n\n if (state.phase === 'failed') {\n return createElement('div', { className: 'imqb-panel' },\n createElement('div', { className: 'imqb-icon' }, '⚠️'),\n createElement('p', { className: 'imqb-title' }, '绑定失败'),\n createElement('p', { className: 'imqb-hint' }, state.reason),\n createElement('div', { className: 'imqb-row' },\n createElement('button', { className: 'imqb-btn imqb-btn--primary', onClick: () => { window.location.reload(); } }, '重试'),\n ),\n );\n }\n\n if (state.phase === 'bound') {\n return createElement(BoundView, { appId: state.appId });\n }\n\n return createElement(UnboundView, { state });\n}\n","/**\n * QQ 机器人配置面板 — 必要配置的 UI 编辑\n *\n * UI 复刻 DSW 通用设置(settings-form 原语)的视觉风格:\n * 左对齐设置列、0.5px 行分隔、label 13px/500、控件右置;\n * Switch 为 36×20 aria-checked 结构(thumb translateX 动画);\n * 三值选择用分段控件(滑动指示器按段数/索引的 CSS 变量定位);\n * 保存按钮为 DSW 反色主按钮(label-primary 底 + bg-layer-3 字)。\n *\n * 数据流:GET /qqbot/im-qqbot/config 回显当前生效配置(appSecret\n * 已由宿主剔除)→ 表单编辑 → dirty diff → POST 仅提交变更键 →\n * 宿主白名单 + Schema 校验 → 最小写入 profile patch → HMR 重载生效。\n *\n * 字段策略:只开放用户必要的配置;cwd 只读展示;超时与性能调优\n * 类字段走 profile patch / 环境变量。\n */\nimport { createElement, useCallback, useEffect, useState, type CSSProperties, type ChangeEvent, type FormEvent, type ReactNode } from 'react';\nimport { BIND_CONFIG_PATH } from '../shared/bind-protocol.ts';\n\n/** 访问控制配置(与 src/config.ts 的 AccessControlConfig 对应的局部投影) */\ninterface AccessControlWire {\n c2cMode: 'open' | 'allowlist' | 'disabled';\n c2cAllow: string[];\n groupMode: 'open' | 'allowlist' | 'disabled';\n groupAllow: string[];\n}\n\n/** GET 回显的生效配置(appSecret 已剔除) */\ntype ConfigWire = {\n requireMention?: boolean;\n streaming?: boolean;\n showToolResults?: boolean;\n debug?: boolean;\n provider?: string;\n model?: string;\n preset?: string;\n groupPrompt?: string;\n directPrompt?: string;\n access?: AccessControlWire;\n cwd?: string;\n};\n\n/** 样式注入标记(幂等) */\nconst STYLE_ID = 'im-qqbot-config-panel-styles';\n\n/** DSW 通用设置风格(settings-form / Switch / SegmentedControl 原语复刻) */\nfunction useConfigPanelStyles(): void {\n useEffect(() => {\n if (typeof document === 'undefined') return;\n if (document.getElementById(STYLE_ID) !== null) return;\n const style = document.createElement('style');\n style.id = STYLE_ID;\n style.textContent = `\n.imqb2-form { display: flex; flex-direction: column; width: 100%; }\n.imqb2-group-title {\n padding: 14px 0 2px; font-size: 12px; font-weight: 600; line-height: 18px;\n color: var(--dsw-alias-label-tertiary); text-transform: uppercase; letter-spacing: .04em;\n}\n.imqb2-field { display: flex; flex-direction: column; gap: 6px; padding: 12px 0; }\n.imqb2-field + .imqb2-field { border-top: 0.5px solid var(--dsw-alias-border-l2); }\n.imqb2-head { display: flex; align-items: center; gap: 8px; min-height: 28px; }\n.imqb2-label {\n flex: 1; min-width: 0; font-size: 13px; font-weight: 500; line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n.imqb2-help { margin: 0; font-size: 12px; line-height: 1.6; color: var(--dsw-alias-label-secondary); }\n.imqb2-hint { margin: 0; font-size: 12px; line-height: 1.5; color: var(--dsw-alias-label-tertiary); }\n/* Switch(aria-checked 驱动,视觉态与无障碍态同源) */\n.imqb2-switch {\n box-sizing: border-box; position: relative; flex: 0 0 auto;\n width: 36px; height: 20px; padding: 2px; border: 0; border-radius: 999px;\n background: var(--dsw-alias-border-l3); cursor: pointer; font: inherit;\n}\n.imqb2-switch[aria-checked='true'] { background: var(--dsw-alias-brand-primary); }\n.imqb2-switch:disabled { cursor: default; opacity: 0.5; }\n.imqb2-switch:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));\n outline-offset: 2px;\n}\n.imqb2-switch-thumb {\n display: block; width: 16px; height: 16px; border-radius: 50%;\n background: var(--dsw-alias-switch-thumb, #ffffff);\n transition: transform 120ms ease;\n}\n.imqb2-switch[aria-checked='true'] .imqb2-switch-thumb {\n background: var(--dsw-alias-label-primary-foreground, #ffffff);\n transform: translateX(16px);\n}\n/* 输入(官方 fields.input 复刻) */\n.imqb2-input {\n box-sizing: border-box; width: 100%; height: 34px; padding: 0 12px;\n border: 0.5px solid var(--dsw-alias-border-l4); border-radius: var(--dsw-radius-md, 8px);\n background: var(--dsw-alias-bg-layer-3); font: inherit; font-size: 13px; line-height: 1.5;\n color: var(--dsw-alias-label-primary); outline: none;\n}\n.imqb2-input:focus { outline: none; border-color: var(--dsw-alias-state-business-primary); }\n.imqb2-input:disabled { color: var(--dsw-alias-label-tertiary); cursor: default; }\n.imqb2-textarea { height: auto; min-height: 68px; padding: 8px 12px; resize: vertical; line-height: 1.5; }\n/* 分段控件(官方 SegmentedControl 复刻:指示器按段数/索引定位) */\n.imqb2-seg {\n position: relative; display: inline-grid; grid-auto-flow: column; grid-auto-columns: 1fr;\n gap: 2px; padding: 4px; border-radius: var(--dsw-radius-md, 8px);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n.imqb2-seg-indicator {\n position: absolute; top: 4px; left: 4px;\n width: calc((100% - 8px - 2px * (var(--imqb2-seg-count) - 1)) / var(--imqb2-seg-count));\n height: calc(100% - 8px);\n border: 0; border-radius: var(--dsw-radius-sm, 6px);\n background: var(--dsw-alias-bg-layer-1);\n box-shadow: var(--dsw-elevation-soft, 0 1px 3px rgba(0,0,0,.08));\n transform: translateX(calc(var(--imqb2-seg-index) * (100% + 2px)));\n transition: transform 160ms ease;\n pointer-events: none;\n}\n.imqb2-seg-tab {\n box-sizing: border-box; position: relative; z-index: 1; height: 28px; padding: 0 16px;\n border: 0; border-radius: var(--dsw-radius-sm, 6px); background: transparent;\n color: var(--dsw-alias-label-secondary); font: inherit; font-size: 13px; line-height: 20px;\n font-weight: 500; white-space: nowrap; cursor: pointer; transition: color 120ms ease;\n}\n.imqb2-seg-tab:hover, .imqb2-seg-tab[aria-selected='true'] { color: var(--dsw-alias-label-primary); }\n.imqb2-seg-tab:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));\n outline-offset: -2px;\n}\n/* 底栏(官方 SettingsForm.footer 复刻) */\n.imqb2-footer { display: flex; align-items: center; gap: 8px; padding-top: 16px; }\n.imqb2-note { flex: 1; min-width: 0; margin: 0; font-size: 12px; line-height: 1.5; color: var(--dsw-alias-label-tertiary); }\n.imqb2-note--error { color: var(--dsw-alias-state-error-primary, var(--dsw-alias-label-error)); }\n.imqb2-note--ok { color: var(--dsw-alias-label-secondary); }\n.imqb2-save {\n appearance: none; flex: none; border: 1px solid transparent;\n border-radius: var(--dsw-radius-md, 8px); padding: 5px 14px;\n font: inherit; font-size: 13px; line-height: 1.5; cursor: pointer;\n background: var(--dsw-alias-label-primary); color: var(--dsw-alias-bg-layer-3);\n}\n.imqb2-save:disabled { opacity: 0.4; cursor: default; }\n.imqb2-save:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));\n outline-offset: 1px;\n}\n/* cwd 只读行 */\n.imqb2-readonly { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; color: var(--dsw-alias-label-secondary); word-break: break-all; margin: 0; }\n `;\n document.head.appendChild(style);\n return () => { style.remove(); };\n }, []);\n}\n\n/** 开关(官方 Switch 原语结构:aria-checked 驱动视觉态) */\nfunction Switch({ checked, label, onChange }: {\n checked: boolean; label: string; onChange: (next: boolean) => void;\n}): ReactNode {\n return createElement('button', {\n type: 'button', role: 'switch', 'aria-checked': checked, 'aria-label': label,\n className: 'imqb2-switch',\n onClick: () => { onChange(!checked); },\n }, createElement('span', { className: 'imqb2-switch-thumb' }));\n}\n\n/** 访问模式分段选择(官方 SegmentedControl 结构:CSS 变量定位滑动指示器) */\nfunction Segmented({ value, options, onChange, label }: {\n value: AccessMode; options: ReadonlyArray<{ value: AccessMode; label: string }>;\n onChange: (next: AccessMode) => void; label: string;\n}): ReactNode {\n const index = Math.max(0, options.findIndex((o) => o.value === value));\n const trackStyle = {\n '--imqb2-seg-count': options.length,\n '--imqb2-seg-index': index,\n } as CSSProperties;\n return createElement('div', { className: 'imqb2-seg', role: 'tablist', 'aria-label': label, style: trackStyle },\n createElement('span', { className: 'imqb2-seg-indicator' }),\n options.map((o) => createElement('button', {\n key: o.value, type: 'button', role: 'tab', 'aria-selected': o.value === value,\n className: 'imqb2-seg-tab', onClick: () => { onChange(o.value); },\n }, o.label)),\n );\n}\n\n/** 开关行(label 左 + Switch 右 + 可选 help) */\nfunction SwitchRow({ label, help, checked, onChange }: {\n label: string; help?: string; checked: boolean; onChange: (next: boolean) => void;\n}): ReactNode {\n return createElement('div', { className: 'imqb2-field' },\n createElement('div', { className: 'imqb2-head' },\n createElement('span', { className: 'imqb2-label' }, label),\n createElement(Switch, { checked, label, onChange }),\n ),\n help === undefined ? null : createElement('p', { className: 'imqb2-help' }, help),\n );\n}\n\n/** 文本行(label 上 + 输入下,与官方 fields 布局一致) */\nfunction FieldRow({ label, help, value, placeholder, onChange }: {\n label: string; help?: string; value: string; placeholder?: string; onChange: (next: string) => void;\n}): ReactNode {\n return createElement('div', { className: 'imqb2-field' },\n createElement('span', { className: 'imqb2-label' }, label),\n createElement('input', {\n className: 'imqb2-input', value, placeholder, spellCheck: false,\n onChange: (event: ChangeEvent<HTMLInputElement>) => { onChange(event.currentTarget.value); },\n }),\n help === undefined ? null : createElement('p', { className: 'imqb2-hint' }, help),\n );\n}\n\n/** 多行文本行 */\nfunction AreaRow({ label, help, value, rows, onChange }: {\n label: string; help?: string; value: string; rows?: number; onChange: (next: string) => void;\n}): ReactNode {\n return createElement('div', { className: 'imqb2-field' },\n createElement('span', { className: 'imqb2-label' }, label),\n createElement('textarea', {\n className: 'imqb2-input imqb2-textarea', value, rows: rows ?? 3,\n onChange: (event: ChangeEvent<HTMLTextAreaElement>) => { onChange(event.currentTarget.value); },\n }),\n help === undefined ? null : createElement('p', { className: 'imqb2-hint' }, help),\n );\n}\n\n/** 访问模式分段选择行 */\nfunction SegmentedRow({ label, help, value, options, onChange }: {\n label: string; help?: string; value: AccessMode;\n options: ReadonlyArray<{ value: AccessMode; label: string }>; onChange: (next: AccessMode) => void;\n}): ReactNode {\n return createElement('div', { className: 'imqb2-field' },\n createElement('div', { className: 'imqb2-head' },\n createElement('span', { className: 'imqb2-label' }, label),\n createElement(Segmented, { value, options, onChange, label }),\n ),\n help === undefined ? null : createElement('p', { className: 'imqb2-hint' }, help),\n );\n}\n\ntype AccessMode = AccessControlWire['c2cMode'];\n\nconst MODE_OPTIONS: ReadonlyArray<{ value: AccessMode; label: string }> = [\n { value: 'open', label: '开放' },\n { value: 'allowlist', label: '白名单' },\n { value: 'disabled', label: '停用' },\n];\n\n/** 深比较(表单值与初始回显值的 dirty 判断) */\nfunction shallowEqual(a: unknown, b: unknown): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n}\n\n/**\n * 配置面板主体。\n *\n * 保存仅提交变更键(服务端按白名单合并),保存成功后提示\n * HMR 重载语义(进行中的机器人会话将重置)。\n */\nexport function ConfigPanel(): ReactNode {\n useConfigPanelStyles();\n const [initial, setInitial] = useState<ConfigWire | null>(null);\n const [form, setForm] = useState<ConfigWire | null>(null);\n const [busy, setBusy] = useState(false);\n const [result, setResult] = useState<{ ok: boolean; message: string } | null>(null);\n const [loadError, setLoadError] = useState<string | null>(null);\n\n useEffect(() => {\n let stopped = false;\n void fetch(BIND_CONFIG_PATH, { cache: 'no-store' })\n .then(async (res) => {\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const body = (await res.json()) as { config: ConfigWire };\n if (stopped) return;\n setInitial(body.config);\n setForm(body.config);\n })\n .catch((err: unknown) => {\n if (!stopped) setLoadError(err instanceof Error ? err.message : String(err));\n });\n return () => { stopped = true; };\n }, []);\n\n const patch = useCallback(<K extends keyof ConfigWire>(key: K, value: ConfigWire[K]): void => {\n setForm((current) => current === null ? current : { ...current, [key]: value });\n setResult(null);\n }, []);\n\n const submit = useCallback((event: FormEvent): void => {\n event.preventDefault();\n if (busy || initial === null || form === null) return;\n const changed: Record<string, unknown> = {};\n for (const key of Object.keys(form) as Array<keyof ConfigWire>) {\n if (!shallowEqual(form[key], initial[key])) {\n changed[key] = form[key];\n }\n }\n if (Object.keys(changed).length === 0) return;\n\n setBusy(true);\n void fetch(BIND_CONFIG_PATH, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(changed),\n })\n .then(async (res) => {\n const body = (await res.json().catch(() => ({}))) as { ok?: boolean; message?: string; error?: string };\n if (res.ok && body.ok === true) {\n setResult({ ok: true, message: body.message ?? '配置已保存,插件将自动重载' });\n setInitial(form);\n return;\n }\n setResult({ ok: false, message: body.error ?? `保存失败(HTTP ${res.status})` });\n })\n .catch((err: unknown) => {\n setResult({ ok: false, message: err instanceof Error ? err.message : String(err) });\n })\n .finally(() => { setBusy(false); });\n }, [busy, form, initial]);\n\n if (loadError !== null) {\n return createElement('p', { className: 'imqb2-hint', style: { padding: '12px 0' } },\n `配置加载失败:${loadError}`);\n }\n if (initial === null || form === null) {\n return createElement('p', { className: 'imqb2-hint', style: { padding: '12px 0' } }, '正在加载配置…');\n }\n\n const dirty = (Object.keys(form) as Array<keyof ConfigWire>).some((key) => !shallowEqual(form[key], initial[key]));\n const access: AccessControlWire = form.access ?? { c2cMode: 'open', c2cAllow: [], groupMode: 'open', groupAllow: [] };\n\n return createElement('form', { className: 'imqb2-form', onSubmit: submit },\n createElement('div', { className: 'imqb2-group-title' }, '访问控制'),\n createElement(SegmentedRow, {\n label: '私聊', value: access.c2cMode, options: MODE_OPTIONS,\n onChange: (v) => { patch('access', { ...access, c2cMode: v }); },\n }),\n access.c2cMode === 'allowlist'\n ? createElement(AreaRow, {\n label: '私聊白名单', help: '每行一个 openid', rows: 3,\n value: access.c2cAllow.join('\\n'),\n onChange: (v) => { patch('access', { ...access, c2cAllow: v.split('\\n').map((s) => s.trim()).filter(Boolean) }); },\n })\n : null,\n createElement(SegmentedRow, {\n label: '群聊', value: access.groupMode, options: MODE_OPTIONS,\n onChange: (v) => { patch('access', { ...access, groupMode: v }); },\n }),\n access.groupMode === 'allowlist'\n ? createElement(AreaRow, {\n label: '群聊白名单', help: '每行一个群 openid', rows: 3,\n value: access.groupAllow.join('\\n'),\n onChange: (v) => { patch('access', { ...access, groupAllow: v.split('\\n').map((s) => s.trim()).filter(Boolean) }); },\n })\n : null,\n\n createElement('div', { className: 'imqb2-group-title' }, '基本'),\n createElement(SwitchRow, {\n label: '群聊需要 @机器人', checked: form.requireMention ?? true,\n help: '开启后群聊中只有 @机器人 的消息会触发回复',\n onChange: (v) => { patch('requireMention', v); },\n }),\n createElement(SwitchRow, {\n label: '流式输出', checked: form.streaming ?? true,\n help: '回复内容分段实时推送到聊天',\n onChange: (v) => { patch('streaming', v); },\n }),\n createElement(SwitchRow, {\n label: '显示工具调用结果', checked: form.showToolResults ?? false,\n help: '在聊天中展示 agent 工具调用的结构化结果',\n onChange: (v) => { patch('showToolResults', v); },\n }),\n\n createElement('div', { className: 'imqb2-group-title' }, '模型'),\n createElement(FieldRow, {\n label: 'Provider', value: form.provider ?? '', placeholder: '留空继承宿主默认',\n onChange: (v) => { patch('provider', v); },\n }),\n createElement(FieldRow, {\n label: 'Model', value: form.model ?? '', placeholder: '留空继承宿主默认',\n onChange: (v) => { patch('model', v); },\n }),\n createElement(FieldRow, {\n label: 'Preset', value: form.preset ?? '', placeholder: 'Agent preset id',\n onChange: (v) => { patch('preset', v); },\n }),\n\n createElement('div', { className: 'imqb2-group-title' }, '补充指令'),\n createElement(AreaRow, {\n label: '群聊额外 system prompt', value: form.groupPrompt ?? '',\n onChange: (v) => { patch('groupPrompt', v); },\n }),\n createElement(AreaRow, {\n label: '私聊额外 system prompt', value: form.directPrompt ?? '',\n onChange: (v) => { patch('directPrompt', v); },\n }),\n\n form.cwd !== undefined\n ? createElement('div', { className: 'imqb2-field' },\n createElement('div', { className: 'imqb2-head' },\n createElement('span', { className: 'imqb2-label' }, 'Agent 工作目录'),\n createElement('span', { className: 'imqb2-hint' }, '只读'),\n ),\n createElement('p', { className: 'imqb2-readonly' }, form.cwd),\n )\n : null,\n\n createElement('div', { className: 'imqb2-footer' },\n result !== null\n ? createElement('p', { className: `imqb2-note${result.ok ? ' imqb2-note--ok' : ' imqb2-note--error'}` },\n (result.ok ? '' : '⚠️ ') + result.message)\n : dirty\n ? createElement('p', { className: 'imqb2-note' }, '保存后插件将重载,进行中的机器人会话将重置')\n : createElement('p', { className: 'imqb2-note' }),\n createElement('button', { className: 'imqb2-save', type: 'submit', disabled: busy || !dirty },\n busy ? '正在保存…' : '保存'),\n ),\n );\n}\n","/**\n * im-qqbot 浏览器半边 —— QQ Bot 端内扫码绑定入口\n *\n * 宿主插件在凭据缺失时注册 /qqbot/im-qqbot/bind 状态路由;本插件在\n * Web / Desktop 提供两个展示位:\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 { BIND_STATE_PATH } from '../shared/bind-protocol.ts';\nimport { BindPanelBody } from './bind-page.ts';\nimport { ConfigPanel } from './config-panel.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/** 绑定入口图标(QQ 机器人,跟随主题图标色变量) */\nfunction BindGuideIcon(): ReactNode {\n return createElement('svg', {\n width: 24, height: 24, viewBox: '0 0 24 24', fill: 'none', 'aria-hidden': true,\n },\n createElement('path', {\n d: 'M9.50039 11.1504C9.96983 11.1504 10.3504 11.5309 10.3504 12.0004V14.0004C10.3504 14.4698 9.96983 14.8504 9.50039 14.8504C9.03095 14.8504 8.65039 14.4698 8.65039 14.0004V12.0004C8.65039 11.5309 9.03095 11.1504 9.50039 11.1504Z',\n fill: 'var(------icon_primary, #1A1A1A)',\n }),\n createElement('path', {\n d: 'M14.5004 11.1504C14.9698 11.1504 15.3504 11.5309 15.3504 12.0004V14.0004C15.3504 14.4698 14.9698 14.8504 14.5004 14.8504C14.0309 14.8504 13.6504 14.4698 13.6504 14.0004V12.0004C13.6504 11.5309 14.0309 11.1504 14.5004 11.1504Z',\n fill: 'var(------icon_primary, #1A1A1A)',\n }),\n createElement('path', {\n fillRule: 'evenodd',\n clipRule: 'evenodd',\n d: 'M12.8504 5.3109C13.5299 4.99138 14.0004 4.30063 14.0004 3.5C14.0004 2.39543 13.1049 1.5 12.0004 1.5C10.8958 1.5 10.0004 2.39543 10.0004 3.5C10.0004 4.30065 10.4708 4.99141 11.1504 5.31092V6.18851C6.38487 6.61795 2.65039 10.6231 2.65039 15.5004V17.0004C2.65039 19.679 4.82181 21.8504 7.50039 21.8504H16.5004C19.179 21.8504 21.3504 19.679 21.3504 17.0004V15.5004C21.3504 10.6231 17.6159 6.61795 12.8504 6.18851V5.3109ZM12.0004 7.85039C7.77541 7.85039 4.35039 11.2754 4.35039 15.5004V17.0004C4.35039 18.7401 5.76069 20.1504 7.50039 20.1504H16.5004C18.2401 20.1504 19.6504 18.7401 19.6504 17.0004V15.5004C19.6504 11.2754 16.2254 7.85039 12.0004 7.85039Z',\n fill: 'var(------icon_primary, #1A1A1A)',\n }),\n );\n}\n\n/** 设置页「QQ Bot」栏目:面板常驻展示绑定状态 */\nfunction QQBotSettingsSection(): ReactNode {\n return createElement('div', null,\n createElement(BindPanelBody),\n createElement(ConfigPanel),\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 机器人' },\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 机器人绑定',\n guide: [{\n id: 'bind',\n order: 90,\n title: () => 'QQ 机器人绑定',\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(BIND_STATE_PATH, { 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.202610101632",
3
+ "version": "0.5.0-dev.202610101921",
4
4
  "description": "QQ Bot IM channel plugin for deepseek-harness (dsh)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -50,6 +50,13 @@
50
50
  },
51
51
  "client": {
52
52
  "platform": "web"
53
+ },
54
+ "compatibility": {
55
+ "dshReleases": {
56
+ "0.1.0-rc.8": "compatible",
57
+ "0.2.0-rc.2": "compatible"
58
+ },
59
+ "note": "0.1.0-rc.8 为 CLI(dsh web)已验证:扫码/手动/解绑全流程、终端二维码与端内绑定页;0.2.0-rc.2 为 Desktop 内嵌运行时已验证:端内绑定页(settings.section 与侧栏)、HMR 重载收敛。客户端依赖 settings.section / sidebarRightTabs / sidebar.right.pane.tab 席位契约与 dsh.client 模块系统,宿主大版本升级后请重新验收席位行为。"
53
60
  }
54
61
  },
55
62
  "dependencies": {
@@ -15,5 +15,5 @@
15
15
  "noUnusedParameters": true,
16
16
  "forceConsistentCasingInFileNames": true
17
17
  },
18
- "include": ["src/client/**/*.ts"]
18
+ "include": ["src/client/**/*.ts", "src/shared/bind-protocol.ts"]
19
19
  }