@skillbuilder/miniapp-simulator 0.2.7 → 0.2.9

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.
Files changed (46) hide show
  1. package/README.md +18 -1
  2. package/dist/sw/miniapp-simulator-service-worker.js +725 -375
  3. package/dist/sw/miniapp-simulator-service-worker.min.js +2 -1
  4. package/docs/assets/road-king-controls.png +0 -0
  5. package/docs/desktop-gesture-design.redoc.xml +96 -0
  6. package/lib/client-CX13jDtT.mjs +1371 -0
  7. package/lib/{index-CqClPZVv.mjs → index-DwepISqo.mjs} +1 -1
  8. package/lib/index.js +4228 -2791
  9. package/lib/remote-client.js +2 -2
  10. package/lib/types/core/simulator.d.ts +242 -2
  11. package/lib/types/gesture/host-bus.d.ts +27 -2
  12. package/lib/types/gesture/input-scheduler.d.ts +29 -0
  13. package/lib/types/gesture/keyboard-panel-view.d.ts +16 -0
  14. package/lib/types/gesture/keyboard-panel.d.ts +54 -0
  15. package/lib/types/gesture/plugins/index.d.ts +11 -14
  16. package/lib/types/gesture/plugins/keyboard-touch.d.ts +4 -0
  17. package/lib/types/gesture/plugins/multi-touch-hint.d.ts +1 -9
  18. package/lib/types/gesture/plugins/multi-touch-switch.d.ts +1 -1
  19. package/lib/types/gesture/plugins/touch-emitter.d.ts +1 -9
  20. package/lib/types/gesture/plugins/wheel-pinch.d.ts +3 -0
  21. package/lib/types/gesture/plugins/wheel-scroll.d.ts +1 -1
  22. package/lib/types/gesture/touch-cursor.d.ts +4 -0
  23. package/lib/types/gesture/touch-sequence.d.ts +12 -0
  24. package/lib/types/gesture/types.d.ts +33 -9
  25. package/lib/types/host/shadow-host.d.ts +19 -4
  26. package/lib/types/inspector/host-inspector.d.ts +1 -1
  27. package/lib/types/offload/manifest.d.ts +12 -0
  28. package/lib/types/offload/resource-session.d.ts +33 -0
  29. package/lib/types/remote/audit-controller.d.ts +2 -0
  30. package/lib/types/remote/audit-leave-monitor.d.ts +2 -0
  31. package/lib/types/remote/client.d.ts +4 -0
  32. package/lib/types/remote/keyboard-panel-host.d.ts +19 -0
  33. package/lib/types/remote/protocol.d.ts +30 -2
  34. package/lib/types/remote/runtime-entry.d.ts +2 -1
  35. package/lib/types/remote/runtime.d.ts +1 -1
  36. package/lib/types/sw/protocol.d.ts +2 -1
  37. package/lib/types/sw/service-worker-host.d.ts +1 -0
  38. package/lib/types/sw/worker/mount-store.d.ts +7 -4
  39. package/lib/types/sw/worker/offloaded.d.ts +3 -0
  40. package/lib/types/types/options.d.ts +4 -0
  41. package/lib/types/types/renderer.d.ts +8 -0
  42. package/lib/types/utils/screen-coordinates.d.ts +31 -0
  43. package/lib/types/utils/stage-fit.d.ts +7 -1
  44. package/lib/workbench.js +1 -1
  45. package/package.json +2 -1
  46. package/lib/client-BjYHK9LR.mjs +0 -1220
@@ -1,6 +1,6 @@
1
- import { R as r, a as O, c as e } from "./client-BjYHK9LR.mjs";
1
+ import { R as r, a as O, c as e } from "./client-CX13jDtT.mjs";
2
2
  import { B as s, M as _ } from "./options-B34ipLmF.mjs";
3
- import { M } from "./index-CqClPZVv.mjs";
3
+ import { M } from "./index-DwepISqo.mjs";
4
4
  export {
5
5
  s as BusinessLine,
6
6
  _ as MINIAPP_SCREENSHOT_ERROR_CODE,
@@ -1,9 +1,249 @@
1
- import type { MiniAppSimulator as MiniAppSimulatorInterface, MiniAppSimulatorOptions } from "../types";
1
+ import type { KeyboardPanelHost } from "../gesture/keyboard-panel";
2
+ import type { OffloadedResourceSession } from "../offload/resource-session";
3
+ import type { InjectionModule } from "../injection";
4
+ import type { MiniAppContentMode, MiniAppEventHandler, MiniAppEventName, MiniAppFileMap, MiniAppGamePlaytestController, MiniAppGestureController, MiniAppHostApiController, MiniAppInspectorController, MiniAppLoadFilesOptions, MiniAppLoadFilesResult, MiniAppLoadZipOptions, MiniAppLoadZipResult, MiniAppLoadZipSource, MiniAppNotePreviewController, MiniAppPermissionSheetController, MiniAppPhoneFrame, MiniAppRedirectOptions, MiniAppScreenShotResult, MiniAppSimulator as MiniAppSimulatorInterface, MiniAppSimulatorOptions, MiniAppSimulatorRoute, MiniAppSimulatorStatus, MiniAppViewport, MiniAppWidgetCoverController } from "../types";
5
+ declare class MiniAppSimulatorImpl implements MiniAppSimulatorInterface {
6
+ private readonly runtimeOptions;
7
+ private readonly lifecycle;
8
+ private readonly events;
9
+ /** SDK 在消费方 root 内创建的 shadow 宿主(含 stage / bounds) */
10
+ private readonly shadowHost;
11
+ private readonly phoneVibration;
12
+ /**
13
+ * 注入清单:security / jsapi / render-done 三个内置模块在 constructor 注册,
14
+ * 跨 reload 持久存在;后续可由 plugin 追加自定义注入。
15
+ */
16
+ private readonly injection;
17
+ private options;
18
+ private renderer;
19
+ /** 只允许最新一次加载继续提交状态;快速切版本时旧解析/挂载会被静默回收。 */
20
+ private loadGeneration;
21
+ private loadedUrl;
22
+ /** 当前 load 的触发来源,供 beforeLoad 事件标记 */
23
+ private loadSource;
24
+ /** SW 渲染模式:当前生效的 SW 宿主与已挂载 id(下次加载 / 销毁时 UNMOUNT 回收) */
25
+ private swHost;
26
+ private swMountedId;
27
+ /**
28
+ * 重算 head 注入所需的入参快照。SW 直供下 head 是 mount 时算好交给 worker 的,
29
+ * reload 只是重导航虚拟 URL,不会重新走注入装配;运行期新增注入模块(评测探针)
30
+ * 必须靠这份快照重算并 remount 才能生效。
31
+ */
32
+ private swInjectionContext;
33
+ /**
34
+ * setHeapLimitOverride 生效前的原值,供恢复用。undefined 表示当前没有覆盖。
35
+ * 只记一次:评测期间反复改值也只能回到最初的线上限值。
36
+ */
37
+ private baseHeapLimit;
38
+ /** 同步注入与 getLaunchOptions 共用的当前 usr 根路径。 */
39
+ private userDataPath;
40
+ /** SW 注册失败后置位:远程 appUrl 后续以真实 URL 直连(跳过 SW 代理) */
41
+ private serviceWorkerUnavailable;
42
+ private detachStageFit;
43
+ /** request / xhsRequest / abortRequest 的独立执行层。 */
44
+ private readonly requestRuntime;
45
+ private readonly storageScope;
46
+ /**
47
+ * 宿主能力 map:构造时来自 `options.hostApi`,运行时通过 `simulator.hostApi.set` 增删。
48
+ * **不会**自动注册到 bridge.handlers;只供内置 bridge handler 按需调用,
49
+ * 即"bridge 实现自己决定是调宿主能力还是 native-ui"。
50
+ */
51
+ private readonly hostApiMap;
52
+ private readonly showJsApiAlert;
53
+ /** 持久化的用户自定义 gesture plugin:跨 reload / renderer 重建保留 */
54
+ private readonly gesturePlugins;
55
+ /** 持久化的选取模式开启状态:跨 reload / renderer 重建后自动重应用 */
56
+ private inspectorEnabled;
57
+ /** 可视化选取的单选 / 多选模式:跨 renderer 重建保留 */
58
+ private inspectorSelectionMode;
59
+ private readonly tracker;
60
+ /** Debug 调试面板(仅 options.debug 时实例化);跨 reload 持久存在 */
61
+ private devtoolsPanel;
62
+ /** Debug 面板唤起快捷键卸载函数 */
63
+ private detachDevtoolsShortcut;
64
+ /** 当前页面路由(note / minitool / profile) */
65
+ private currentRoute;
66
+ /** 小工具收起后回落的活动页(note 笔记页 / profile 个人页) */
67
+ private activePage;
68
+ /** JSAPI 模块共用的只读运行态视图;字段访问始终读取最新模拟器状态。 */
69
+ private get jsapiContext();
70
+ readonly hostApi: MiniAppHostApiController;
71
+ readonly gesture: MiniAppGestureController;
72
+ readonly gamePlaytest: MiniAppGamePlaytestController;
73
+ readonly inspector: MiniAppInspectorController;
74
+ readonly notePreview: MiniAppNotePreviewController;
75
+ readonly widgetCover: MiniAppWidgetCoverController;
76
+ readonly permissionSheet: MiniAppPermissionSheetController;
77
+ constructor(options: MiniAppSimulatorOptions, runtimeOptions?: {
78
+ keyboardPanelHost?: KeyboardPanelHost;
79
+ requestManagerFrameUrl?: string;
80
+ gateways?: Record<string, string>;
81
+ businessLine?: string;
82
+ trackerContext?: () => Record<string, string | number | undefined>;
83
+ });
84
+ /**
85
+ * 初始化 Debug 调试面板:以 fixed 停靠窗口右侧(挂 document.body,不遮挡模拟器舞台),
86
+ * 注册 Shift+Cmd/Ctrl+U 唤起快捷键。能力开关仅供内部调试并触发整页 reload。
87
+ */
88
+ private initDevtools;
89
+ /** 从当前归一化选项构造调试面板快照 */
90
+ private buildDevtoolsSnapshot;
91
+ /** device 作为渲染容器,bounds 作为 stageFit 默认边界 */
92
+ private normalizeContext;
93
+ /** 去掉仅在创建期使用、normalize 不需要的 host / 回调字段 */
94
+ private stripHostOptions;
95
+ /** 实例创建时把 option 级回调绑定到对应事件 */
96
+ private bindLifecycleCallbacks;
97
+ /**
98
+ * 合并补丁并重新归一化(深合并 notePreview / security / review,与 updateConfig 一致)。
99
+ * 入参允许是已归一化形态(来自 this.options)+ 局部覆盖。
100
+ */
101
+ private mergeOptions;
102
+ get status(): MiniAppSimulatorStatus;
103
+ /**
104
+ * 尝试调用宿主注册的 getUserInfo handler 获取用户信息,
105
+ * 用于更新笔记页导航栏与个人页(资料区 / 卡片作者行)的头像和昵称。
106
+ * 未注册或调用失败时静默保留默认值。
107
+ */
108
+ private tryResolveHostUserInfo;
109
+ private syncRendererRednoteContext;
110
+ private syncRendererProfileContext;
111
+ /** 小工具封面:同步到 renderer 壳层(笔记尾卡 + 个人页卡片两处生效) */
112
+ private syncRendererWidgetCover;
113
+ /** 更新当前路由;变化时统一发出 routerChange 事件(宿主调用与内部跳转共用) */
114
+ private setRoute;
115
+ /** 壳层路由变化:note/profile(头像 / 返回 / 宿主跳转)、minitool(sheet 唤起)、back(sheet 收起回落活动页) */
116
+ private handleShellRouteChange;
117
+ getCurrentRoute(): MiniAppSimulatorRoute;
118
+ redirectTo(route: MiniAppSimulatorRoute, options?: MiniAppRedirectOptions): Promise<void>;
119
+ load(): Promise<void>;
120
+ private isCurrentLoad;
121
+ /** 终止当前 guest,并回收其 renderer 与 SW 挂载。 */
122
+ private interruptCurrentGuest;
123
+ private loadCurrent;
124
+ unload(): void;
125
+ /**
126
+ * 执行宿主定制项:函数则直接调用;`{ toast }` 则弹该文案并回成功。
127
+ */
128
+ private runHostEntry;
129
+ private runUnsupportedJsApi;
130
+ /** 注册模拟器 JSAPI 领域模块;未声明事件仍由 fallback 分发。 */
131
+ private registerBuiltinBridgeHandlers;
132
+ reload(): Promise<void>;
133
+ resize(viewport: MiniAppViewport): void;
134
+ /** @deprecated 创建实例时传入完整配置;配置变化时销毁并重新创建实例。 */
135
+ updateConfig(options: Partial<Omit<MiniAppSimulatorOptions, "mount" | "shadow">>): void;
136
+ setViewport(viewport: MiniAppViewport): void;
137
+ setZoomFactor(factor: number): void;
138
+ switchMode(mode: MiniAppContentMode): Promise<void>;
139
+ switchContentMode(mode: MiniAppContentMode): Promise<void>;
140
+ switchPhoneFrame(frame: string | MiniAppPhoneFrame | false): Promise<void>;
141
+ switchDevice(device: string): void;
142
+ loadApp(appUrl: string, options?: {
143
+ query?: MiniAppSimulatorOptions["query"];
144
+ }): Promise<void>;
145
+ loadZip(source: MiniAppLoadZipSource, options?: MiniAppLoadZipOptions): Promise<MiniAppLoadZipResult>;
146
+ loadFiles(files: MiniAppFileMap, options?: MiniAppLoadFilesOptions, offloaded?: OffloadedResourceSession): Promise<MiniAppLoadFilesResult>;
147
+ /**
148
+ * loadZip / loadFiles 走 `iframe-service-worker`(Web 运行时):把包文件挂载给
149
+ * Service Worker 当同源虚拟服务器直供。使用前请把 renderer 设为 `iframe-service-worker`。
150
+ */
151
+ private assertServiceWorkerLoader;
152
+ /** SW 模式前置校验:环境/配置不满足直接抛(宿主据此提示加载失败)。 */
153
+ private assertServiceWorkerUsable;
154
+ private toLoadMeta;
155
+ /** 注册 + 激活 SW(打点注册结果 / 耗时);注册失败置 serviceWorkerUnavailable,后续跳过 SW。 */
156
+ private acquireServiceWorkerHost;
157
+ /** SW 路径:算注入载荷 → 挂载原始文件到 SW → 以同源虚拟 URL 直连(SW 改写 HTML + 下发 CSP)。 */
158
+ private loadViaServiceWorker;
159
+ /** 是否对当前远程 appUrl 启用 SW 代理:SW 渲染器 + 配了 scriptUrl + 环境支持 + 未注册失败 + appUrl 为跨域 http(s)。 */
160
+ private shouldProxyRemoteViaServiceWorker;
161
+ /** load() 内调用:远程 appUrl 满足条件时挂进 SW 走同源代理;失败经 error 事件暴露并抛出。 */
162
+ private maybeMountRemoteViaServiceWorker;
163
+ /** 半屏/全屏壳层收起后再次唤起时,先恢复 SW 注册与内存挂载表,再加载虚拟 URL。 */
164
+ private restoreServiceWorkerMount;
165
+ /** 远程 SW 代理:fetch 远程入口 HTML(需开 CORS)→ 注入 → 仅挂入口+remoteBase → appUrl 改写为同源虚拟 URL,子资源由 SW 回源。 */
166
+ private mountRemoteViaServiceWorker;
167
+ setTouchEmulation(enabled: boolean): Promise<void>;
168
+ /** 回收 SW 模式的挂载。 */
169
+ private unmountServiceWorkerContent;
170
+ private resolveLoadUrl;
171
+ private isCurrentServiceWorkerMountUrl;
172
+ /**
173
+ * 开关小应用 DevTools。仅 webview 渲染器实现(独立 webContents);
174
+ * iframe 渲染器无该能力时为 no-op(DevTools 由宿主整窗负责)。
175
+ */
176
+ setDevtools(open: boolean): void;
177
+ /**
178
+ * 开关手势 overlay 的 pointer-events(touchEmulation 时生效)。
179
+ * 宿主嵌入 DevTools 做 Elements 审查时应设为 false,让点击穿透到 guest。
180
+ */
181
+ setGestureOverlayInteractionEnabled(enabled: boolean): void;
182
+ /**
183
+ * 返回小应用渲染容器的 webContents id(仅 webview 渲染器,dom-ready 后可用,否则 null)。
184
+ * 不做 lifecycle 断言:宿主可能在 load 完成后立即轮询。
185
+ */
186
+ getWebContentsId(): number | null;
187
+ /**
188
+ * 注册宿主侧扩展注入模块(评测探针等)。下次 load / reload 生效。
189
+ *
190
+ * 注册后会把 security 重新移回末位:seal 锁定内置原型,探针必须在它之前安装,
191
+ * 否则 hook 目标属性已不可配置,采集静默归零。
192
+ */
193
+ registerInjection(module: InjectionModule): void;
194
+ /** 注销扩展注入模块;下次 load / reload 生效。返回是否命中。 */
195
+ unregisterInjection(id: string): boolean;
196
+ /**
197
+ * 打开评测面板等内容的独立容器:竖直摆放时在设备右侧,旋转时在设备下方。
198
+ *
199
+ * 右侧面板在 width 与 maxWidth 之间适度扩展,底部面板占满挂载区宽度。
200
+ * 设备由 stageFit 在剩余区域内等比适配,桌面操作模式下靠上摆放。
201
+ */
202
+ openSidePanel(width: number, maxWidth?: number): HTMLElement;
203
+ /** 收起侧栏并清空其内容;设备恢复占满整个挂载区。 */
204
+ closeSidePanel(): void;
205
+ /** 整块模拟器容器,包含设备、侧栏及容器内留白。 */
206
+ getInteractionRegions(): readonly HTMLElement[];
207
+ /**
208
+ * 临时调整 guest 堆熔断线,供体验评测使用;传 null 恢复建实例时的值。
209
+ *
210
+ * 熔断会把整个 iframe 换成 about:blank,注入的探针随 Realm 一起消失,本次采集
211
+ * 只能作废——而"内存占用大"正是评测最该测出来的事。改值后须调 refreshInjection
212
+ * 并 reload 才会重新下发 resource-guard 的新阈值。
213
+ */
214
+ setHeapLimitOverride(maxHeapUsedBytes: number | false | null): void;
215
+ /**
216
+ * 重算 head 注入并就地替换 SW 已挂载实例的注入载荷。
217
+ *
218
+ * SW 直供路径下 head 是 mount 时一次性算好交给 worker 的,reload 只重导航虚拟 URL,
219
+ * 拿到的仍是旧 HTML。运行期通过 registerInjection 新增的模块必须调一次本方法,
220
+ * 之后的 reload 才会带上新注入。
221
+ *
222
+ * 返回 false 表示当前不是 SW 直供状态(无快照),调用方应改走完整 load。
223
+ */
224
+ refreshInjection(): Promise<boolean>;
225
+ capture(): Promise<Blob>;
226
+ /**
227
+ * 主动截取小工具运行画面(完整画面):成功返回 base64 dataURL,失败返回 code + reason(不抛异常)。
228
+ * 结果同时以 `screenShot` 事件(source: "manual")对外发出。
229
+ */
230
+ screenShot(): Promise<MiniAppScreenShotResult>;
231
+ destroy(reason?: string): void;
232
+ on<E extends MiniAppEventName>(event: E, handler: MiniAppEventHandler<E>): () => void;
233
+ once<E extends MiniAppEventName>(event: E, handler: MiniAppEventHandler<E>): () => void;
234
+ off<E extends MiniAppEventName>(event: E, handler: MiniAppEventHandler<E>): void;
235
+ /** 根据渲染器类型创建对应的 Transport;webview 手势 IPC 与 jsapi bridge 共用通道 */
236
+ private createTransport;
237
+ private createRenderer;
238
+ private bindRendererEvents;
239
+ }
2
240
  export declare function createMiniAppSimulator(options: MiniAppSimulatorOptions): MiniAppSimulatorInterface;
3
241
  /** @internal 远程 Runtime 注入服务端托管资源;不属于接入方配置。 */
4
242
  export declare function createRuntimeMiniAppSimulator(options: MiniAppSimulatorOptions, runtimeOptions: {
243
+ keyboardPanelHost?: KeyboardPanelHost;
5
244
  requestManagerFrameUrl: string;
6
245
  gateways?: Record<string, string>;
7
246
  businessLine?: string;
8
247
  trackerContext?: () => Record<string, string | number | undefined>;
9
- }): MiniAppSimulatorInterface;
248
+ }): MiniAppSimulatorImpl;
249
+ export {};
@@ -9,8 +9,11 @@
9
9
  *
10
10
  * Plugin 通过 `install(ctx)` 订阅事件并往 iframe 发协议消息。
11
11
  */
12
+ import type { ScreenRotationDirection } from "../utils/screen-coordinates";
12
13
  import type { GestureDispatchInput, GestureDispatchResult, GestureGuestChannel, GestureHostPlugin, GestureSharedState } from "./types";
13
14
  export interface GestureHostBusOptions {
15
+ /** 外置横竖屏控件,由已启用桌面控件的宿主提供。 */
16
+ rotateScreen?: (direction: ScreenRotationDirection) => void;
14
17
  /** 内容载体元素(iframe 或 webview);仅用于坐标换算与聚焦 */
15
18
  element: HTMLElement;
16
19
  /** host↔guest 介质通道(发送/回包/聚焦/cursor),由各 ContentHost 注入 */
@@ -39,12 +42,25 @@ export declare class GestureHostBus {
39
42
  /** light DOM 移入边界(shadow host),补 Shadow 穿透场景 */
40
43
  private readonly hoverEnterTarget;
41
44
  private readonly debug;
45
+ private readonly rotateScreen?;
42
46
  private readonly rawHandlers;
43
47
  private readonly logicHandlers;
44
48
  private readonly plugins;
49
+ private readonly pluginSubscriptions;
45
50
  /** 跨 plugin 共享状态 */
46
51
  readonly state: GestureSharedState;
52
+ private readonly inputScheduler;
53
+ private keyboardCaptureEnabled;
54
+ private keyboardCodes;
55
+ private guestInputPossible;
56
+ private hostFocusIntentAt;
57
+ /** 原生捕获与已提交手势分离,等待 guest 水位时也能接收 move/up。 */
58
+ private physicalPressed;
59
+ private readonly nativePrevention;
47
60
  private destroyed;
61
+ private resettingGuest;
62
+ /** 鼠标、滚轮和键盘各自维护序列,这里聚合 guest 可见的所有触点。 */
63
+ private readonly activeTouches;
48
64
  /** 当前 mouse 是否正在捕获 mousemove(mousedown 后绑定,mouseup 后解绑) */
49
65
  private capturingWindowMove;
50
66
  /** 业务 plugin 可临时覆盖 cursor,nul 时回到默认 */
@@ -72,13 +88,15 @@ export declare class GestureHostBus {
72
88
  * 或坐标缩放细节。
73
89
  */
74
90
  dispatch(input: GestureDispatchInput): Promise<GestureDispatchResult>;
91
+ /** 结束鼠标、键盘等输入源的活动触点,供切页和旋转前调用。 */
92
+ cancelActiveGestures(): void;
75
93
  /**
76
94
  * 由宿主在 iframe.onload + receiver runtime 注入完成后调用。
77
- * 触发 "iframe:ready" 逻辑事件,需要 receiver 就绪后才能 send 的 plugin
78
- * (如 `selection-disabler`)可通过 `ctx.hook("iframe:ready", ...)` 监听。
95
+ * 需要 receiver 就绪后发送配置的插件订阅 iframe:ready。
79
96
  */
80
97
  notifyIframeReady(): void;
81
98
  destroy(): void;
99
+ private uninstallPlugin;
82
100
  private makeContext;
83
101
  private subscribeRaw;
84
102
  private subscribeLogic;
@@ -101,11 +119,18 @@ export declare class GestureHostBus {
101
119
  * 回退 hoverTrackTarget。contentRoot 在 stageFit 缩放 + flex 布局下 rect 可能偏移/塌缩。
102
120
  */
103
121
  private get hoverRegion();
122
+ private guestHasFocus;
123
+ private focusGuest;
124
+ private discardPendingInputs;
125
+ private preventNative;
104
126
  private onMouseDown;
105
127
  private onMouseMoveDragging;
106
128
  private onMouseUp;
107
129
  private onWheel;
130
+ private onVisibilityChange;
108
131
  private onWindowBlur;
132
+ private onHostFocusIntent;
133
+ private applyGuestMessage;
109
134
  /** guest 内表单控件 focus 后,把宿主焦点切到载体,键盘输入才能进小应用 */
110
135
  private onGuestInbound;
111
136
  private bindCapturingWindowMove;
@@ -0,0 +1,29 @@
1
+ export declare class GestureInputScheduler {
2
+ private readonly options;
3
+ private tasks;
4
+ private nextOrdinal;
5
+ private nextRequest;
6
+ private request;
7
+ private watermark;
8
+ private running;
9
+ private applyingAt;
10
+ private wake;
11
+ private readonly waiters;
12
+ generation: number;
13
+ constructor(options: {
14
+ needsBarrier: () => boolean;
15
+ sendBarrier: (requestId: number, generation: number) => void;
16
+ onError: (error: unknown) => void;
17
+ });
18
+ time(): number;
19
+ enqueue(run: () => void, issuedAt?: number, fromGuest?: boolean, cleanupOnly?: boolean): () => void;
20
+ acknowledge(requestId: unknown, generation: unknown, issuedAt: unknown): void;
21
+ /** 关闭键盘输入时撤销旧 guest 的确认,保留鼠标释放和滚轮截止点。 */
22
+ endGuestStream(): void;
23
+ cancel(): void;
24
+ /** 等待当前时刻已经生成的输入;未来的长按/滚轮截止点不阻塞调用。 */
25
+ settled(): Promise<void>;
26
+ private hasDueTasks;
27
+ private settle;
28
+ private pump;
29
+ }
@@ -0,0 +1,16 @@
1
+ export { keyboardPanelCommandNeedsGameFocus } from "./keyboard-panel";
2
+ import type { KeyboardPanelCommand, KeyboardPanelConnection } from "./keyboard-panel";
3
+ export declare const KEYBOARD_UI_STYLES = "\n [data-mini-app-keyboard-ui],[data-mini-app-keyboard-ui] * {-webkit-user-select:none;user-select:none}\n [data-mini-app-keyboard-ui] button,[data-mini-app-keyboard-ui] select {font:inherit;cursor:pointer;color:inherit;background:#273244;border:1px solid #526078;border-radius:6px;padding:6px 9px}\n [data-mini-app-keyboard-ui] button:focus-visible,[data-mini-app-keyboard-ui] select:focus-visible {outline:2px solid #60a5fa;outline-offset:2px}\n [data-mini-app-keyboard-ui] button:disabled {opacity:.45;cursor:default}\n [data-mini-app-keyboard-ui] [data-mini-app-keyboard-panel] {width:296px;max-width:calc(100vw - 24px);box-sizing:border-box;position:fixed;z-index:2147483000;font:12px/1.5 system-ui,sans-serif;color:#e5e7eb;cursor:default;background:#172033;padding:14px;border:1px solid #526078;border-radius:10px;box-shadow:0 6px 24px #0005;max-height:calc(100vh - 24px);overflow:auto}\n [data-mini-app-keyboard-ui] [data-mini-app-keyboard-panel-handle] {display:flex;align-items:center;justify-content:space-between;gap:12px;cursor:grab;touch-action:none;user-select:none;position:sticky;top:-14px;background:#172033;padding:8px 0;z-index:1}\n [data-mini-app-keyboard-ui] [data-mini-app-keyboard-panel-handle] span {font-size:11px;color:#a6b4c9}\n [data-mini-app-keyboard-ui] [data-key-code] {display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin:10px 0;padding:8px 0;border-bottom:1px solid #334155}\n [data-mini-app-keyboard-ui] [data-key-label] {min-width:34px;font-weight:600}\n [data-mini-app-keyboard-ui] [data-mini-app-keyboard-panel] small {flex:1;color:#a6b4c9;font-size:11px}\n [data-mini-app-keyboard-ui] [data-mini-app-keyboard-panel] p {margin:8px 0;color:#c2cddd;font-size:12px}\n [data-mini-app-keyboard-ui] [data-mapping-notice] {padding:8px;background:#253650;border-radius:6px}\n";
4
+ export interface KeyboardPanelViewOptions {
5
+ root: HTMLElement;
6
+ onCommand: (command: KeyboardPanelCommand) => void;
7
+ /** 当前视图 window 的 client 坐标,跨 iframe 投影由调用者完成。 */
8
+ getAnchor: () => {
9
+ left: number;
10
+ top: number;
11
+ right: number;
12
+ bottom: number;
13
+ };
14
+ }
15
+ /** 面板渲染状态并转发自身焦点内的按键;游戏选区和触点由 gesture plugin 管理。 */
16
+ export declare function createKeyboardPanelView(options: KeyboardPanelViewOptions): KeyboardPanelConnection;
@@ -0,0 +1,54 @@
1
+ /** 可序列化的键位面板视图;游戏触点和选区状态由 gesture plugin 持有。 */
2
+ export interface KeyboardPanelState {
3
+ open: boolean;
4
+ enabled: boolean;
5
+ available: boolean;
6
+ busy: boolean;
7
+ notice: string;
8
+ bindings: Array<{
9
+ code: string;
10
+ mode: "hold" | "drag";
11
+ isRegion: boolean;
12
+ }>;
13
+ anchor: {
14
+ left: number;
15
+ top: number;
16
+ right: number;
17
+ bottom: number;
18
+ };
19
+ viewport: {
20
+ width: number;
21
+ height: number;
22
+ };
23
+ }
24
+ export type KeyboardPanelCommand = {
25
+ type: "add" | "cancel" | "close" | "outside";
26
+ } | {
27
+ type: "rebind" | "pick" | "remove" | "destination";
28
+ code: string;
29
+ } | {
30
+ type: "mode";
31
+ code: string;
32
+ mode: "hold" | "drag";
33
+ } | {
34
+ type: "enable";
35
+ enabled: boolean;
36
+ issuedAt: number;
37
+ sequence: number;
38
+ } | {
39
+ type: "key";
40
+ eventName: "keydown" | "keyup" | "cancel" | "focusout";
41
+ code: string;
42
+ repeat: boolean;
43
+ issuedAt: number;
44
+ sequence: number;
45
+ };
46
+ export interface KeyboardPanelConnection {
47
+ update(state: KeyboardPanelState): void;
48
+ dispose(): void;
49
+ }
50
+ /** Runtime 的宿主提供序列化通道;本地 SDK 使用同页面板视图。 */
51
+ export interface KeyboardPanelHost {
52
+ mount(onCommand: (command: KeyboardPanelCommand) => void): KeyboardPanelConnection;
53
+ }
54
+ export declare function keyboardPanelCommandNeedsGameFocus(command: KeyboardPanelCommand): boolean;
@@ -1,18 +1,10 @@
1
1
  /**
2
- * 内置的 host-side gesture plugin 工厂集合。
2
+ * 内置 host 插件按输入互斥与状态依赖排序:
3
+ * wheel-pinch → multi-touch-switch / pinch / rotate → touch-emitter → tap / long-press /
4
+ * double-tap → wheel-scroll → selection-disabler → multi-touch-hint。
3
5
  *
4
- * GestureHostBus 默认会安装所有这些 plugin(按以下顺序):
5
- * 1. multi-touch-switch / pinch / rotate ── 控制 state.multiTouchMode(必须在 touch-emitter 前)
6
- * 2. touch-emitter ── 派发 touch 事件(读 state 决定单/双指几何)
7
- * 3. tap ── 短按 → mini-app:tap(receiver 合成 click)
8
- * 4. long-press ── 长按 → mini-app:longpress(contextmenu)
9
- * 5. double-tap ── hook tap → mini-app:dblclick
10
- * 6. wheel-scroll ── 滚轮 → mini-app:wheel
11
- * 7. selection-disabler ── iframe body user-select: none
12
- * 8. multi-touch-hint ── 状态条 + 副触点圆点
13
- *
14
- * 注:手指圆点(hover/press 指针)已从 plugin 上移为 gesture 核心内置 `TouchCursor`
15
- * (见 `../touch-cursor.ts`),由 GestureHostBus 直接持有,圆点显示不再走事件管线。
6
+ * wheel-pinch 在鼠标按下前结束滚轮序列;模式插件在 touch-emitter 之前确定双指基点。
7
+ * TouchCursor 负责单指与悬停,多指提示读取 touch:points 绘制实际触点。
16
8
  */
17
9
  export { createTouchEmitterPlugin } from "./touch-emitter";
18
10
  export { createMultiTouchSwitchPlugin, createPinchPlugin, createRotatePlugin, } from "./multi-touch-switch";
@@ -20,8 +12,13 @@ export { createTapPlugin } from "./tap";
20
12
  export { createLongPressPlugin } from "./long-press";
21
13
  export { createDoubleTapPlugin } from "./double-tap";
22
14
  export { createWheelScrollPlugin } from "./wheel-scroll";
15
+ export { createWheelPinchPlugin } from "./wheel-pinch";
23
16
  export { createSelectionDisablerPlugin } from "./selection-disabler";
24
17
  export { createMultiTouchHintPlugin } from "./multi-touch-hint";
18
+ import type { KeyboardPanelHost } from "../keyboard-panel";
25
19
  import type { GestureHostPlugin } from "../types";
26
20
  /** 一次性生成全套内置 plugin(mode-switch 必须在 touch-emitter 之前注册) */
27
- export declare function createDefaultHostPlugins(): GestureHostPlugin[];
21
+ export declare function createDefaultHostPlugins(options?: {
22
+ keyboardMapping?: boolean;
23
+ keyboardPanelHost?: KeyboardPanelHost;
24
+ }): GestureHostPlugin[];
@@ -0,0 +1,4 @@
1
+ import type { KeyboardPanelHost } from "../keyboard-panel";
2
+ import type { GestureHostPlugin } from "../types";
3
+ /** 通用区域/拖动映射;每个按键和鼠标分别维护触点生命周期。 */
4
+ export declare function createKeyboardTouchPlugin(panelHost?: KeyboardPanelHost): GestureHostPlugin;
@@ -1,11 +1,3 @@
1
- /**
2
- * MultiTouchHintPlugin — 双指模式可视化提示。
3
- *
4
- * 当任意 MultiTouchSwitch/Pinch/Rotate emit "multitouch:enter" 时:
5
- * - 顶部加状态条,提示当前模式(mirror / pinch / rotate)
6
- * - 在副触点位置画一个圆点,随鼠标移动
7
- *
8
- * leave 时清掉两者。
9
- */
10
1
  import type { GestureHostPlugin } from "../types";
2
+ /** 圆点直接读取派发坐标,支持鼠标和触控板。 */
11
3
  export declare function createMultiTouchHintPlugin(): GestureHostPlugin;
@@ -12,7 +12,7 @@
12
12
  import type { GestureHostPlugin } from "../types";
13
13
  /** Shift+拖动 = 镜像双指(经典 hammerjs 风格) */
14
14
  export declare function createMultiTouchSwitchPlugin(): GestureHostPlugin;
15
- /** Alt+拖动 = 捏合双指(真实 pinch,副触点直接对称) */
15
+ /** Alt / Option+拖动 = 双指围绕固定中心捏合。 */
16
16
  export declare function createPinchPlugin(): GestureHostPlugin;
17
17
  /** Ctrl+拖动 = 旋转双指(副触点关于 base 旋转 180°) */
18
18
  export declare function createRotatePlugin(): GestureHostPlugin;
@@ -1,11 +1,3 @@
1
- /**
2
- * TouchEmitterPlugin — 核心 touch 派发器。
3
- *
4
- * 监听 PointerSnapshot 的 down/move/up/leave/blur,转换为 `mini-app:touch` 消息发到 iframe。
5
- * 根据 state.isMultiTouch + state.multiTouchMode 决定派 1 / 2 个触点及其几何算法:
6
- * - "mirror":副触点 = 主触点关于 base 对称 + MULTI_TOUCH_OFFSET 外推(默认双指)
7
- * - "pinch":副触点 = 主触点关于 base 真实对称(无偏移,模拟捏合)
8
- * - "rotate":副触点 = 主触点关于 base 旋转 180°
9
- */
10
1
  import type { GestureHostPlugin } from "../types";
2
+ /** 鼠标拖拽的单 / 双指触摸;基点由多指模式插件确定。 */
11
3
  export declare function createTouchEmitterPlugin(): GestureHostPlugin;
@@ -0,0 +1,3 @@
1
+ import type { GestureHostPlugin } from "../types";
2
+ /** 触控板 ctrl+wheel、鼠标 Alt+wheel、WebKit gesture 统一生成双指序列。 */
3
+ export declare function createWheelPinchPlugin(): GestureHostPlugin;
@@ -5,7 +5,7 @@
5
5
  * 本 plugin 监听 overlay 的 wheel 事件,转发到 iframe 内(receiver 端 WheelDispatchPlugin
6
6
  * 派发原生 WheelEvent + scrollBy fallback)。
7
7
  *
8
- * 注意:Alt + wheel 不在本 plugin 处理(由 PinchPlugin 拦截作为缩放)。
8
+ * Ctrl / Alt + wheel 由 wheel-pinch 处理为双指缩放。
9
9
  */
10
10
  import type { GestureHostPlugin } from "../types";
11
11
  export declare function createWheelScrollPlugin(): GestureHostPlugin;
@@ -39,6 +39,8 @@ export declare class TouchCursor {
39
39
  private destroyed;
40
40
  /** DevTools 审查等场景暂时禁止显示/跟随,避免挡住 Elements 选点 */
41
41
  private suppressed;
42
+ private multiTouch;
43
+ private inside;
42
44
  private visible;
43
45
  private pressed;
44
46
  private hot;
@@ -69,6 +71,8 @@ export declare class TouchCursor {
69
71
  private tickHot;
70
72
  /** DevTools 审查 / 权限弹窗 / 元素选取等场景:隐藏圆点并暂停跟随,避免干扰上层交互 */
71
73
  setSuppressed(suppressed: boolean): void;
74
+ /** 多指圆点由手势插件按实际触点坐标绘制。 */
75
+ setMultiTouch(active: boolean): void;
72
76
  private onMove;
73
77
  private onLeave;
74
78
  private onDown;
@@ -0,0 +1,12 @@
1
+ import type { GestureHostContext, GesturePoint, ModifierKeys, PointerSnapshot } from "./types";
2
+ /** 一段触摸序列;两个输入设备共用 touches / changedTouches 的生命周期。 */
3
+ export declare class TouchSequence {
4
+ private readonly ctx;
5
+ private points;
6
+ private modifiers;
7
+ constructor(ctx: GestureHostContext);
8
+ update(next: GesturePoint[], modifiers: ModifierKeys): void;
9
+ finish(cancel?: boolean): void;
10
+ private send;
11
+ }
12
+ export declare function gesturePoint(element: HTMLElement, snap: PointerSnapshot, identifier: number, x?: number, y?: number): GesturePoint;
@@ -6,6 +6,7 @@
6
6
  * - host 端 plugin 监听原始事件并 send 给 iframe,receiver 端 plugin 派发对应 DOM 事件
7
7
  * - 跨 plugin 通过共享状态 + 逻辑事件 emit/on 协作
8
8
  */
9
+ import type { ScreenRotationDirection } from "../utils/screen-coordinates";
9
10
  export interface GesturePoint {
10
11
  identifier: number;
11
12
  clientX: number;
@@ -27,7 +28,7 @@ export interface ModifierKeys {
27
28
  */
28
29
  export interface PointerSnapshot {
29
30
  kind: "down" | "move" | "up" | "leave" | "wheel" | "blur";
30
- /** iframe CSS 坐标空间(已做 transform scale 修正) */
31
+ /** iframe CSS 坐标空间(已修正设备缩放与屏幕旋转) */
31
32
  iframeX: number;
32
33
  iframeY: number;
33
34
  /** 宿主视口坐标(用于 overlay 上画 UI) */
@@ -72,7 +73,7 @@ export interface GestureDispatchResult {
72
73
  segments?: number;
73
74
  durationMs?: number;
74
75
  }
75
- export type GestureBusMessage = TouchMessage | WheelMessage | TapMessage | LongPressMessage | DoubleTapMessage | CursorQueryMessage | SelectionMessage;
76
+ export type GestureBusMessage = TouchMessage | WheelMessage | TapMessage | LongPressMessage | DoubleTapMessage | CursorQueryMessage | SelectionMessage | KeyboardCaptureMessage | InputBarrierMessage;
76
77
  export interface TouchMessage {
77
78
  type: "mini-app:touch";
78
79
  eventName: "touchstart" | "touchmove" | "touchend" | "touchcancel";
@@ -117,6 +118,17 @@ export interface CursorReplyMessage {
117
118
  queryId: number;
118
119
  cursor: string;
119
120
  }
121
+ export interface KeyboardCaptureMessage {
122
+ type: "mini-app:keyboard-capture";
123
+ codes: string[];
124
+ /** 由 host bus 填充;取消/重载后拒绝旧输入。 */
125
+ generation?: number;
126
+ }
127
+ export interface InputBarrierMessage {
128
+ type: "mini-app:input-barrier";
129
+ requestId: number;
130
+ generation: number;
131
+ }
120
132
  export interface SelectionMessage {
121
133
  type: "mini-app:selection";
122
134
  enabled: boolean;
@@ -145,12 +157,7 @@ export interface GestureGuestChannel {
145
157
  /** 读 host 视口坐标下 guest 元素的 computed cursor(hot 判定);不支持返回 undefined */
146
158
  readCursorAt(hostX: number, hostY: number): string | undefined;
147
159
  }
148
- /**
149
- * 双指模式下的副触点几何算法。
150
- * - "mirror":副触点为主触点关于 base 的对称点(再外推固定偏移)—— 经典 shift 双指
151
- * - "pinch":副触点为主触点关于 base 的真实对称点(无偏移)—— 模拟捏合
152
- * - "rotate":副触点为主触点关于 base 旋转 180° 后的点 —— 模拟双指旋转
153
- */
160
+ /** 双指模式:镜像 / 捏合 / 旋转,触点围绕固定基点对称且初始间距非零。 */
154
161
  export type MultiTouchMode = "mirror" | "pinch" | "rotate";
155
162
  export interface GestureSharedState {
156
163
  /** 鼠标主按键是否处于按下态 */
@@ -179,8 +186,25 @@ export type RawEventName = PointerSnapshot["kind"];
179
186
  export type RawEventHandler = (ev: PointerSnapshot) => void;
180
187
  export type LogicEventHandler = (...args: unknown[]) => void;
181
188
  export interface GestureHostContext {
189
+ /** 取消活动手势后旋转整台设备,保持逻辑视口尺寸;仅启用桌面控件时提供。 */
190
+ rotateScreen?: (direction: ScreenRotationDirection) => void;
191
+ /** 同步恢复 guest 原生键盘焦点,保留当前活动触点。 */
192
+ focusGuest(): void;
182
193
  /** 订阅原始指针事件 */
183
- on(event: RawEventName, handler: RawEventHandler): () => void;
194
+ on(event: RawEventName, handler: RawEventHandler, options?: {
195
+ /** 在原生事件返回前同步拦截默认行为;语义处理可等待 guest 水位。 */
196
+ preventDefault?: (snapshot: PointerSnapshot) => boolean;
197
+ }): () => void;
198
+ /** 在跨窗口输入水位内执行状态变更。 */
199
+ orderInput(run: () => void, issuedAt?: number, options?: {
200
+ cleanupOnly?: boolean;
201
+ }): void;
202
+ /** 按源输入时间调度可取消的截止点(例如长按)。 */
203
+ scheduleInput(run: () => void, dueAt: number, options?: {
204
+ cleanupOnly?: boolean;
205
+ }): () => void;
206
+ /** 当前提交输入的 epoch 时间;原生回调中返回当前时间。 */
207
+ inputTime(): number;
184
208
  /** 跨 plugin 协作:触发逻辑事件 */
185
209
  emit(event: string, ...args: unknown[]): void;
186
210
  /** 订阅逻辑事件(例如 'tap' / 'longpress' / 'pinch' / 'rotate') */