@realnation/builder-shared-sdk 2.5.0 → 2.6.0

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.
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Where device snapshots come from.
3
+ *
4
+ * The session never touches `navigator` directly: it polls a source. The live
5
+ * source reads the Gamepad API on animation frames; the mock source replays a
6
+ * script. Tests and machines without a wheel drive the same session code the
7
+ * hardware does, which is the point (same principle as the runtime mock: it
8
+ * may be stricter than the real thing, never looser).
9
+ */
10
+ import type { GamepadLike } from './normalize.js';
11
+ export interface GamepadSource {
12
+ /** Starts delivering snapshots. Called once when the session connects. */
13
+ start(onFrame: (pads: readonly GamepadLike[]) => void): void;
14
+ /** Stops delivering. Must be safe to call when never started. */
15
+ stop(): void;
16
+ /** One snapshot on demand, for the connect-time device probe. */
17
+ read(): readonly GamepadLike[];
18
+ /** True when this environment can report gamepads at all. */
19
+ readonly supported: boolean;
20
+ }
21
+ /**
22
+ * Reads the browser's Gamepad API.
23
+ *
24
+ * Polling is the only option the API offers: there are no per-frame events,
25
+ * and Chrome hands out a fresh snapshot object on every call. `getGamepads()`
26
+ * returns a sparse array whose holes are disconnected slots, hence the filter.
27
+ */
28
+ export declare class LiveGamepadSource implements GamepadSource {
29
+ private readonly requestFrame;
30
+ private readonly cancelFrame;
31
+ private handle;
32
+ private onFrame;
33
+ readonly supported: boolean;
34
+ constructor(requestFrame?: (cb: () => void) => number, cancelFrame?: (handle: number) => void);
35
+ read(): readonly GamepadLike[];
36
+ start(onFrame: (pads: readonly GamepadLike[]) => void): void;
37
+ stop(): void;
38
+ }
39
+ export interface MockGamepadOptions {
40
+ /** The device to report. Absent means "nothing plugged in". */
41
+ pad?: GamepadLike | null;
42
+ /** Leaves frame delivery to `step()` instead of a timer. For tests. */
43
+ manual?: boolean;
44
+ /** Frame interval when not manual. */
45
+ intervalMs?: number;
46
+ setTimer?: (fn: () => void, ms: number) => unknown;
47
+ clearTimer?: (handle: unknown) => void;
48
+ }
49
+ /**
50
+ * A scripted device.
51
+ *
52
+ * Mutate `pad` between frames to drive a turn or a pedal. In manual mode the
53
+ * caller decides when a frame happens, so a test can assert on exactly the
54
+ * sequence it wrote rather than on whatever a timer produced.
55
+ */
56
+ export declare class MockGamepadSource implements GamepadSource {
57
+ readonly supported = true;
58
+ private onFrame;
59
+ private timer;
60
+ private readonly setTimer;
61
+ private readonly clearTimer;
62
+ private readonly intervalMs;
63
+ private readonly manual;
64
+ pad: GamepadLike | null;
65
+ constructor(options?: MockGamepadOptions);
66
+ read(): readonly GamepadLike[];
67
+ start(onFrame: (pads: readonly GamepadLike[]) => void): void;
68
+ stop(): void;
69
+ /** Delivers one frame. The only way frames happen in manual mode. */
70
+ step(): void;
71
+ }
72
+ /** Builds a mock device with every axis at rest and every button up. */
73
+ export declare function mockPad(id: string, axisCount: number, buttonCount: number, overrides?: {
74
+ axes?: number[];
75
+ buttons?: number[];
76
+ index?: number;
77
+ }): GamepadLike;
78
+ //# sourceMappingURL=source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../src/gamepad/source.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,KAAK,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,SAAS,WAAW,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IAC7D,iEAAiE;IACjE,IAAI,IAAI,IAAI,CAAC;IACb,iEAAiE;IACjE,IAAI,IAAI,SAAS,WAAW,EAAE,CAAC;IAC/B,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,qBAAa,iBAAkB,YAAW,aAAa;IAOnD,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAP9B,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,OAAO,CAAyD;IAExE,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;gBAGT,YAAY,GAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,MAA0C,EAC5E,WAAW,GAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAqC;IAKzF,IAAI,IAAI,SAAS,WAAW,EAAE;IAO9B,KAAK,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,SAAS,WAAW,EAAE,KAAK,IAAI,GAAG,IAAI;IAU5D,IAAI,IAAI,IAAI;CAKb;AAED,MAAM,WAAW,kBAAkB;IACjC,+DAA+D;IAC/D,GAAG,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACzB,uEAAuE;IACvE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,sCAAsC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IACnD,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CACxC;AAED;;;;;;GAMG;AACH,qBAAa,iBAAkB,YAAW,aAAa;IACrD,QAAQ,CAAC,SAAS,QAAQ;IAE1B,OAAO,CAAC,OAAO,CAAyD;IACxE,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA0C;IACnE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA4B;IACvD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAU;IAEjC,GAAG,EAAE,WAAW,GAAG,IAAI,CAAC;gBAEZ,OAAO,GAAE,kBAAuB;IAQ5C,IAAI,IAAI,SAAS,WAAW,EAAE;IAI9B,KAAK,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,SAAS,WAAW,EAAE,KAAK,IAAI,GAAG,IAAI;IAM5D,IAAI,IAAI,IAAI;IAMZ,qEAAqE;IACrE,IAAI,IAAI,IAAI;CAGb;AAED,wEAAwE;AACxE,wBAAgB,OAAO,CACrB,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,SAAS,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GACtE,WAAW,CAOb"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Reads the browser's Gamepad API.
3
+ *
4
+ * Polling is the only option the API offers: there are no per-frame events,
5
+ * and Chrome hands out a fresh snapshot object on every call. `getGamepads()`
6
+ * returns a sparse array whose holes are disconnected slots, hence the filter.
7
+ */
8
+ export class LiveGamepadSource {
9
+ requestFrame;
10
+ cancelFrame;
11
+ handle = null;
12
+ onFrame = null;
13
+ supported;
14
+ constructor(requestFrame = (cb) => requestAnimationFrame(cb), cancelFrame = (h) => cancelAnimationFrame(h)) {
15
+ this.requestFrame = requestFrame;
16
+ this.cancelFrame = cancelFrame;
17
+ this.supported = typeof navigator !== 'undefined' && typeof navigator.getGamepads === 'function';
18
+ }
19
+ read() {
20
+ if (!this.supported)
21
+ return [];
22
+ return Array.from(navigator.getGamepads()).filter((pad) => Boolean(pad?.connected));
23
+ }
24
+ start(onFrame) {
25
+ this.onFrame = onFrame;
26
+ if (!this.supported || this.handle !== null)
27
+ return;
28
+ const tick = () => {
29
+ this.handle = this.requestFrame(tick);
30
+ this.onFrame?.(this.read());
31
+ };
32
+ this.handle = this.requestFrame(tick);
33
+ }
34
+ stop() {
35
+ if (this.handle !== null)
36
+ this.cancelFrame(this.handle);
37
+ this.handle = null;
38
+ this.onFrame = null;
39
+ }
40
+ }
41
+ /**
42
+ * A scripted device.
43
+ *
44
+ * Mutate `pad` between frames to drive a turn or a pedal. In manual mode the
45
+ * caller decides when a frame happens, so a test can assert on exactly the
46
+ * sequence it wrote rather than on whatever a timer produced.
47
+ */
48
+ export class MockGamepadSource {
49
+ supported = true;
50
+ onFrame = null;
51
+ timer = null;
52
+ setTimer;
53
+ clearTimer;
54
+ intervalMs;
55
+ manual;
56
+ pad;
57
+ constructor(options = {}) {
58
+ this.pad = options.pad ?? null;
59
+ this.manual = options.manual ?? false;
60
+ this.intervalMs = options.intervalMs ?? 16;
61
+ this.setTimer = options.setTimer ?? ((fn, ms) => setInterval(fn, ms));
62
+ this.clearTimer = options.clearTimer ?? ((h) => clearInterval(h));
63
+ }
64
+ read() {
65
+ return this.pad?.connected ? [this.pad] : [];
66
+ }
67
+ start(onFrame) {
68
+ this.onFrame = onFrame;
69
+ if (this.manual || this.timer !== null)
70
+ return;
71
+ this.timer = this.setTimer(() => this.step(), this.intervalMs);
72
+ }
73
+ stop() {
74
+ if (this.timer !== null)
75
+ this.clearTimer(this.timer);
76
+ this.timer = null;
77
+ this.onFrame = null;
78
+ }
79
+ /** Delivers one frame. The only way frames happen in manual mode. */
80
+ step() {
81
+ this.onFrame?.(this.read());
82
+ }
83
+ }
84
+ /** Builds a mock device with every axis at rest and every button up. */
85
+ export function mockPad(id, axisCount, buttonCount, overrides = {}) {
86
+ const axes = Array.from({ length: axisCount }, (_, i) => overrides.axes?.[i] ?? 0);
87
+ const buttons = Array.from({ length: buttonCount }, (_, i) => {
88
+ const value = overrides.buttons?.[i] ?? 0;
89
+ return { value, pressed: value > 0.5 };
90
+ });
91
+ return { id, index: overrides.index ?? 0, connected: true, mapping: '', axes, buttons };
92
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/souvenir/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,EAKL,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,cAAc,EAEpB,MAAM,YAAY,CAAC;AA6CpB;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAI7E;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAO5F;AAyPD,gFAAgF;AAChF,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACrC,OAAO,CAAC,iBAAiB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAoBhE;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,cAAc,CAEpF;AAED,oDAAoD;AACpD,wBAAgB,UAAU,CAAC,MAAM,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAStH;AAED,8EAA8E;AAC9E,wBAAgB,SAAS,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM,EAAE,CAY/E;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,OAAO,CAc5F;AAED,+FAA+F;AAC/F,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAE,eAAoB,GAAG,MAAM,CAQnH;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,YAAY,EAClB,MAAM,GAAE,cAAmB,EAC3B,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,iBAAiB,CAAC,CA2E5B"}
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/souvenir/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,EAKL,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,cAAc,EAEpB,MAAM,YAAY,CAAC;AA6CpB;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAI7E;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAO5F;AAyPD,gFAAgF;AAChF,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACrC,OAAO,CAAC,iBAAiB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAoBhE;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,cAAc,CAEpF;AAED,oDAAoD;AACpD,wBAAgB,UAAU,CAAC,MAAM,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAStH;AAED,8EAA8E;AAC9E,wBAAgB,SAAS,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM,EAAE,CAa/E;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,OAAO,CAc5F;AAED,+FAA+F;AAC/F,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAE,eAAoB,GAAG,MAAM,CAQnH;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,YAAY,EAClB,MAAM,GAAE,cAAmB,EAC3B,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,iBAAiB,CAAC,CA2E5B"}
@@ -306,8 +306,9 @@ export function drawOrder(page, region) {
306
306
  .filter((eid) => !isHidden(page.properties?.[eid] ?? {}))
307
307
  // Controls stay on screen over the picture, so they are not in it
308
308
  .filter((eid) => !isSouvenirInteractive(page.elements[eid]?.type, page.properties?.[eid] ?? {}))
309
- // 框外的元件是页面的一部分,不进图
310
- .filter((eid) => !box || isInsideRegion(page.properties?.[eid] ?? {}, box))
309
+ // 框外的元件是页面的一部分,不进图。背景是整页的底,框在哪一块都画在它上面,
310
+ // 所以不按中心点判(背景的中心是画布中心,框摆到一边就会把底丢掉,出图透明一片)
311
+ .filter((eid) => !box || page.elements[eid]?.type === 'bg' || isInsideRegion(page.properties?.[eid] ?? {}, box))
311
312
  .map((eid, index) => ({ eid, index, z: num((page.properties?.[eid] ?? {})['zIndex']) }))
312
313
  .sort((a, b) => (a.z === b.z ? a.index - b.index : a.z - b.z))
313
314
  .map((entry) => entry.eid);
package/package.json CHANGED
@@ -1,64 +1,68 @@
1
- {
2
- "name": "@realnation/builder-shared-sdk",
3
- "version": "2.5.0",
4
- "type": "module",
5
- "private": false,
6
- "exports": {
7
- "./package.json": "./package.json",
8
- ".": {
9
- "types": "./dist/index.d.ts",
10
- "import": "./dist/index.js"
11
- },
12
- "./runtime": {
13
- "types": "./dist/runtime/index.d.ts",
14
- "import": "./dist/runtime/index.js"
15
- },
16
- "./runtime/vue": {
17
- "types": "./dist/runtime/vue/index.d.ts",
18
- "import": "./dist/runtime/vue/index.js"
19
- },
20
- "./interaction": {
21
- "types": "./dist/interaction/index.d.ts",
22
- "import": "./dist/interaction/index.js"
23
- },
24
- "./interaction/vue": {
25
- "types": "./dist/interaction/vue.d.ts",
26
- "import": "./dist/interaction/vue.js"
27
- },
28
- "./souvenir": {
29
- "types": "./dist/souvenir/index.d.ts",
30
- "import": "./dist/souvenir/index.js"
31
- }
32
- },
33
- "files": [
34
- "dist"
35
- ],
36
- "scripts": {
37
- "build": "tsc -p tsconfig.json",
38
- "clean": "rimraf dist",
39
- "test": "vitest run",
40
- "test:watch": "vitest",
41
- "prepublishOnly": "npm run clean && npm run test && npm run build",
42
- "prepare": "npm run clean && npm run build"
43
- },
44
- "dependencies": {
45
- "ali-oss": "^6.23.0",
46
- "axios": "^1.7.7"
47
- },
48
- "peerDependencies": {
49
- "vue": "^3.4.0"
50
- },
51
- "peerDependenciesMeta": {
52
- "vue": {
53
- "optional": true
54
- }
55
- },
56
- "devDependencies": {
57
- "@types/ali-oss": "^6.16.11",
58
- "@types/node": "^26.1.2",
59
- "rimraf": "^5.0.5",
60
- "typescript": "^5.4.5",
61
- "vitest": "^2.1.0",
62
- "vue": "^3.4.0"
63
- }
64
- }
1
+ {
2
+ "name": "@realnation/builder-shared-sdk",
3
+ "version": "2.6.0",
4
+ "type": "module",
5
+ "private": false,
6
+ "exports": {
7
+ "./package.json": "./package.json",
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ },
12
+ "./runtime": {
13
+ "types": "./dist/runtime/index.d.ts",
14
+ "import": "./dist/runtime/index.js"
15
+ },
16
+ "./runtime/vue": {
17
+ "types": "./dist/runtime/vue/index.d.ts",
18
+ "import": "./dist/runtime/vue/index.js"
19
+ },
20
+ "./interaction": {
21
+ "types": "./dist/interaction/index.d.ts",
22
+ "import": "./dist/interaction/index.js"
23
+ },
24
+ "./interaction/vue": {
25
+ "types": "./dist/interaction/vue.d.ts",
26
+ "import": "./dist/interaction/vue.js"
27
+ },
28
+ "./souvenir": {
29
+ "types": "./dist/souvenir/index.d.ts",
30
+ "import": "./dist/souvenir/index.js"
31
+ },
32
+ "./gamepad": {
33
+ "types": "./dist/gamepad/index.d.ts",
34
+ "import": "./dist/gamepad/index.js"
35
+ }
36
+ },
37
+ "files": [
38
+ "dist"
39
+ ],
40
+ "scripts": {
41
+ "build": "tsc -p tsconfig.json",
42
+ "clean": "rimraf dist",
43
+ "test": "vitest run",
44
+ "test:watch": "vitest",
45
+ "prepublishOnly": "npm run clean && npm run test && npm run build",
46
+ "prepare": "npm run clean && npm run build"
47
+ },
48
+ "dependencies": {
49
+ "ali-oss": "^6.23.0",
50
+ "axios": "^1.7.7"
51
+ },
52
+ "peerDependencies": {
53
+ "vue": "^3.4.0"
54
+ },
55
+ "peerDependenciesMeta": {
56
+ "vue": {
57
+ "optional": true
58
+ }
59
+ },
60
+ "devDependencies": {
61
+ "@types/ali-oss": "^6.16.11",
62
+ "@types/node": "^26.1.2",
63
+ "rimraf": "^5.0.5",
64
+ "typescript": "^5.4.5",
65
+ "vitest": "^2.1.0",
66
+ "vue": "^3.4.0"
67
+ }
68
+ }