@geometra/renderer-canvas 1.53.0 → 1.55.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.
- package/dist/__tests__/gestures.test.d.ts +2 -0
- package/dist/__tests__/gestures.test.d.ts.map +1 -0
- package/dist/__tests__/gestures.test.js +134 -0
- package/dist/__tests__/gestures.test.js.map +1 -0
- package/dist/gestures.d.ts +63 -0
- package/dist/gestures.d.ts.map +1 -0
- package/dist/gestures.js +88 -0
- package/dist/gestures.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gestures.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/gestures.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { attachGestureRecognizers } from '../gestures.js';
|
|
3
|
+
class FakeEventTarget {
|
|
4
|
+
listeners = new Map();
|
|
5
|
+
addEventListener(type, listener) {
|
|
6
|
+
if (!this.listeners.has(type))
|
|
7
|
+
this.listeners.set(type, new Set());
|
|
8
|
+
this.listeners.get(type).add(listener);
|
|
9
|
+
}
|
|
10
|
+
removeEventListener(type, listener) {
|
|
11
|
+
this.listeners.get(type)?.delete(listener);
|
|
12
|
+
}
|
|
13
|
+
dispatch(type, event) {
|
|
14
|
+
const list = this.listeners.get(type);
|
|
15
|
+
if (!list)
|
|
16
|
+
return;
|
|
17
|
+
for (const listener of list)
|
|
18
|
+
listener(event);
|
|
19
|
+
}
|
|
20
|
+
listenerCount(type) {
|
|
21
|
+
return this.listeners.get(type)?.size ?? 0;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function makeRecorder() {
|
|
25
|
+
const events = [];
|
|
26
|
+
return {
|
|
27
|
+
events,
|
|
28
|
+
pointerDown: sample => events.push({ type: 'down', sample }),
|
|
29
|
+
pointerMove: sample => events.push({ type: 'move', sample }),
|
|
30
|
+
pointerUp: sample => events.push({ type: 'up', sample }),
|
|
31
|
+
pointerCancel: pointerId => events.push({ type: 'cancel', pointerId }),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function makeCanvas(target, rect = { left: 10, top: 20 }) {
|
|
35
|
+
return {
|
|
36
|
+
addEventListener: target.addEventListener.bind(target),
|
|
37
|
+
removeEventListener: target.removeEventListener.bind(target),
|
|
38
|
+
getBoundingClientRect: () => rect,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
describe('attachGestureRecognizers', () => {
|
|
42
|
+
it('converts pointerdown clientX/Y to canvas-space samples and fans out to all recognizers', () => {
|
|
43
|
+
const canvasTarget = new FakeEventTarget();
|
|
44
|
+
const docTarget = new FakeEventTarget();
|
|
45
|
+
const canvas = makeCanvas(canvasTarget, { left: 10, top: 20 });
|
|
46
|
+
const a = makeRecorder();
|
|
47
|
+
const b = makeRecorder();
|
|
48
|
+
const cleanup = attachGestureRecognizers(canvas, [a, b], {
|
|
49
|
+
documentTarget: docTarget,
|
|
50
|
+
now: () => 42,
|
|
51
|
+
});
|
|
52
|
+
canvasTarget.dispatch('pointerdown', { pointerId: 1, clientX: 30, clientY: 50 });
|
|
53
|
+
expect(a.events).toEqual([{ type: 'down', sample: { id: 1, x: 20, y: 30, timestampMs: 42 } }]);
|
|
54
|
+
expect(b.events).toEqual([{ type: 'down', sample: { id: 1, x: 20, y: 30, timestampMs: 42 } }]);
|
|
55
|
+
cleanup();
|
|
56
|
+
});
|
|
57
|
+
it('routes pointermove/up/cancel through documentTarget when trackOutsideCanvas is true (default)', () => {
|
|
58
|
+
const canvasTarget = new FakeEventTarget();
|
|
59
|
+
const docTarget = new FakeEventTarget();
|
|
60
|
+
const canvas = makeCanvas(canvasTarget);
|
|
61
|
+
const r = makeRecorder();
|
|
62
|
+
const cleanup = attachGestureRecognizers(canvas, [r], {
|
|
63
|
+
documentTarget: docTarget,
|
|
64
|
+
now: () => 0,
|
|
65
|
+
});
|
|
66
|
+
canvasTarget.dispatch('pointerdown', { pointerId: 7, clientX: 10, clientY: 20 });
|
|
67
|
+
docTarget.dispatch('pointermove', { pointerId: 7, clientX: 60, clientY: 80 });
|
|
68
|
+
docTarget.dispatch('pointerup', { pointerId: 7, clientX: 60, clientY: 80 });
|
|
69
|
+
expect(r.events.map(e => e.type)).toEqual(['down', 'move', 'up']);
|
|
70
|
+
// Moves for unknown pointer ids are filtered out.
|
|
71
|
+
docTarget.dispatch('pointermove', { pointerId: 99, clientX: 0, clientY: 0 });
|
|
72
|
+
expect(r.events.map(e => e.type)).toEqual(['down', 'move', 'up']);
|
|
73
|
+
cleanup();
|
|
74
|
+
expect(canvasTarget.listenerCount('pointerdown')).toBe(0);
|
|
75
|
+
expect(docTarget.listenerCount('pointermove')).toBe(0);
|
|
76
|
+
expect(docTarget.listenerCount('pointerup')).toBe(0);
|
|
77
|
+
expect(docTarget.listenerCount('pointercancel')).toBe(0);
|
|
78
|
+
});
|
|
79
|
+
it('clamps all listeners to the canvas when trackOutsideCanvas is false', () => {
|
|
80
|
+
const canvasTarget = new FakeEventTarget();
|
|
81
|
+
const docTarget = new FakeEventTarget();
|
|
82
|
+
const canvas = makeCanvas(canvasTarget);
|
|
83
|
+
const r = makeRecorder();
|
|
84
|
+
const cleanup = attachGestureRecognizers(canvas, [r], {
|
|
85
|
+
trackOutsideCanvas: false,
|
|
86
|
+
documentTarget: docTarget,
|
|
87
|
+
now: () => 0,
|
|
88
|
+
});
|
|
89
|
+
canvasTarget.dispatch('pointerdown', { pointerId: 1, clientX: 10, clientY: 20 });
|
|
90
|
+
docTarget.dispatch('pointermove', { pointerId: 1, clientX: 60, clientY: 80 });
|
|
91
|
+
// Document move ignored because we only listen on canvas.
|
|
92
|
+
expect(r.events.map(e => e.type)).toEqual(['down']);
|
|
93
|
+
canvasTarget.dispatch('pointermove', { pointerId: 1, clientX: 60, clientY: 80 });
|
|
94
|
+
expect(r.events.map(e => e.type)).toEqual(['down', 'move']);
|
|
95
|
+
cleanup();
|
|
96
|
+
});
|
|
97
|
+
it('pointercancel forwards the pointer id and drops the active pointer', () => {
|
|
98
|
+
const canvasTarget = new FakeEventTarget();
|
|
99
|
+
const docTarget = new FakeEventTarget();
|
|
100
|
+
const canvas = makeCanvas(canvasTarget);
|
|
101
|
+
const r = makeRecorder();
|
|
102
|
+
const cleanup = attachGestureRecognizers(canvas, [r], {
|
|
103
|
+
documentTarget: docTarget,
|
|
104
|
+
now: () => 0,
|
|
105
|
+
});
|
|
106
|
+
canvasTarget.dispatch('pointerdown', { pointerId: 3, clientX: 10, clientY: 20 });
|
|
107
|
+
docTarget.dispatch('pointercancel', { pointerId: 3 });
|
|
108
|
+
expect(r.events[r.events.length - 1]).toEqual({ type: 'cancel', pointerId: 3 });
|
|
109
|
+
// After cancel, further moves for this id are ignored.
|
|
110
|
+
docTarget.dispatch('pointermove', { pointerId: 3, clientX: 100, clientY: 100 });
|
|
111
|
+
expect(r.events.map(e => e.type)).toEqual(['down', 'cancel']);
|
|
112
|
+
cleanup();
|
|
113
|
+
});
|
|
114
|
+
it('uses the options.now timestamp source for every sample', () => {
|
|
115
|
+
const canvasTarget = new FakeEventTarget();
|
|
116
|
+
const docTarget = new FakeEventTarget();
|
|
117
|
+
const canvas = makeCanvas(canvasTarget);
|
|
118
|
+
const r = makeRecorder();
|
|
119
|
+
let t = 0;
|
|
120
|
+
const clock = () => {
|
|
121
|
+
t += 10;
|
|
122
|
+
return t;
|
|
123
|
+
};
|
|
124
|
+
const cleanup = attachGestureRecognizers(canvas, [r], {
|
|
125
|
+
documentTarget: docTarget,
|
|
126
|
+
now: clock,
|
|
127
|
+
});
|
|
128
|
+
canvasTarget.dispatch('pointerdown', { pointerId: 1, clientX: 10, clientY: 20 });
|
|
129
|
+
docTarget.dispatch('pointermove', { pointerId: 1, clientX: 12, clientY: 22 });
|
|
130
|
+
expect(r.events.map(e => e.sample?.timestampMs)).toEqual([10, 20]);
|
|
131
|
+
cleanup();
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
//# sourceMappingURL=gestures.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gestures.test.js","sourceRoot":"","sources":["../../src/__tests__/gestures.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { PointerSample } from '@geometra/core';
|
|
2
|
+
/**
|
|
3
|
+
* Host-agnostic surface that every gesture recognizer in `@geometra/core`
|
|
4
|
+
* implements. We redeclare it here rather than importing each recognizer type
|
|
5
|
+
* so callers can pass any array of compatible recognizers (including user-built
|
|
6
|
+
* ones) without having to list every union member.
|
|
7
|
+
*/
|
|
8
|
+
export interface CanvasGestureRecognizerLike {
|
|
9
|
+
pointerDown(sample: PointerSample): void;
|
|
10
|
+
pointerMove(sample: PointerSample): void;
|
|
11
|
+
pointerUp(sample: PointerSample): void;
|
|
12
|
+
pointerCancel(pointerId: number): void;
|
|
13
|
+
}
|
|
14
|
+
export interface AttachGestureRecognizersOptions {
|
|
15
|
+
/**
|
|
16
|
+
* When true (default), `pointermove` / `pointerup` / `pointercancel` are
|
|
17
|
+
* attached to `document` so drags continue if the pointer leaves the canvas.
|
|
18
|
+
* Set false to clamp all events to the canvas bounds (useful in test
|
|
19
|
+
* environments without `document`).
|
|
20
|
+
*/
|
|
21
|
+
trackOutsideCanvas?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* High-resolution timestamp source. Defaults to `performance.now()` — or a
|
|
24
|
+
* frozen `0` when `performance` is unavailable (SSR/Node test envs).
|
|
25
|
+
*/
|
|
26
|
+
now?: () => number;
|
|
27
|
+
/**
|
|
28
|
+
* Optional override for the document-like target used for outside-canvas
|
|
29
|
+
* tracking. Primarily for tests that can't rely on a global `document`.
|
|
30
|
+
*/
|
|
31
|
+
documentTarget?: {
|
|
32
|
+
addEventListener: Document['addEventListener'];
|
|
33
|
+
removeEventListener: Document['removeEventListener'];
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Convert browser `PointerEvent`s into {@link PointerSample}s and fan them out
|
|
38
|
+
* to one or more `@geometra/core` gesture recognizers (pan / swipe / pinch or
|
|
39
|
+
* user-built state machines). Returns a cleanup function.
|
|
40
|
+
*
|
|
41
|
+
* Typical integration:
|
|
42
|
+
*
|
|
43
|
+
* ```ts
|
|
44
|
+
* import { createPanRecognizer } from '@geometra/core'
|
|
45
|
+
* import { attachGestureRecognizers } from '@geometra/renderer-canvas'
|
|
46
|
+
*
|
|
47
|
+
* const pan = createPanRecognizer({ onMove: e => setOffset(e.deltaX, e.deltaY) })
|
|
48
|
+
* const stop = attachGestureRecognizers(canvas, [pan])
|
|
49
|
+
* // ...later: stop()
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* `pointerdown` is always attached to the canvas. `pointermove` /
|
|
53
|
+
* `pointerup` / `pointercancel` are attached to `document` by default so drags
|
|
54
|
+
* continue after the pointer leaves the canvas — the sample coordinates stay
|
|
55
|
+
* relative to the canvas because we subtract `getBoundingClientRect()` on every
|
|
56
|
+
* event. Set `trackOutsideCanvas: false` to clamp to the canvas.
|
|
57
|
+
*
|
|
58
|
+
* We track which pointer IDs have been seen via `pointerdown` on this canvas so
|
|
59
|
+
* stray moves/ups from unrelated elements can't accidentally drive recognizer
|
|
60
|
+
* state.
|
|
61
|
+
*/
|
|
62
|
+
export declare function attachGestureRecognizers(canvas: HTMLCanvasElement, recognizers: ReadonlyArray<CanvasGestureRecognizerLike>, options?: AttachGestureRecognizersOptions): () => void;
|
|
63
|
+
//# sourceMappingURL=gestures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gestures.d.ts","sourceRoot":"","sources":["../src/gestures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD;;;;;GAKG;AACH,MAAM,WAAW,2BAA2B;IAC1C,WAAW,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAAA;IACxC,WAAW,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAAA;IACxC,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAAA;IACtC,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACvC;AAED,MAAM,WAAW,+BAA+B;IAC9C;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB;;;OAGG;IACH,cAAc,CAAC,EAAE;QACf,gBAAgB,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAAA;QAC9C,mBAAmB,EAAE,QAAQ,CAAC,qBAAqB,CAAC,CAAA;KACrD,CAAA;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,iBAAiB,EACzB,WAAW,EAAE,aAAa,CAAC,2BAA2B,CAAC,EACvD,OAAO,GAAE,+BAAoC,GAC5C,MAAM,IAAI,CA2DZ"}
|
package/dist/gestures.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert browser `PointerEvent`s into {@link PointerSample}s and fan them out
|
|
3
|
+
* to one or more `@geometra/core` gesture recognizers (pan / swipe / pinch or
|
|
4
|
+
* user-built state machines). Returns a cleanup function.
|
|
5
|
+
*
|
|
6
|
+
* Typical integration:
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { createPanRecognizer } from '@geometra/core'
|
|
10
|
+
* import { attachGestureRecognizers } from '@geometra/renderer-canvas'
|
|
11
|
+
*
|
|
12
|
+
* const pan = createPanRecognizer({ onMove: e => setOffset(e.deltaX, e.deltaY) })
|
|
13
|
+
* const stop = attachGestureRecognizers(canvas, [pan])
|
|
14
|
+
* // ...later: stop()
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* `pointerdown` is always attached to the canvas. `pointermove` /
|
|
18
|
+
* `pointerup` / `pointercancel` are attached to `document` by default so drags
|
|
19
|
+
* continue after the pointer leaves the canvas — the sample coordinates stay
|
|
20
|
+
* relative to the canvas because we subtract `getBoundingClientRect()` on every
|
|
21
|
+
* event. Set `trackOutsideCanvas: false` to clamp to the canvas.
|
|
22
|
+
*
|
|
23
|
+
* We track which pointer IDs have been seen via `pointerdown` on this canvas so
|
|
24
|
+
* stray moves/ups from unrelated elements can't accidentally drive recognizer
|
|
25
|
+
* state.
|
|
26
|
+
*/
|
|
27
|
+
export function attachGestureRecognizers(canvas, recognizers, options = {}) {
|
|
28
|
+
const trackOutsideCanvas = options.trackOutsideCanvas !== false;
|
|
29
|
+
const now = options.now ?? fallbackNow();
|
|
30
|
+
const activePointers = new Set();
|
|
31
|
+
function toSample(e) {
|
|
32
|
+
const rect = canvas.getBoundingClientRect();
|
|
33
|
+
return {
|
|
34
|
+
id: e.pointerId,
|
|
35
|
+
x: e.clientX - rect.left,
|
|
36
|
+
y: e.clientY - rect.top,
|
|
37
|
+
timestampMs: now(),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function onPointerDown(e) {
|
|
41
|
+
activePointers.add(e.pointerId);
|
|
42
|
+
const sample = toSample(e);
|
|
43
|
+
for (const r of recognizers)
|
|
44
|
+
r.pointerDown(sample);
|
|
45
|
+
}
|
|
46
|
+
function onPointerMove(e) {
|
|
47
|
+
if (!activePointers.has(e.pointerId))
|
|
48
|
+
return;
|
|
49
|
+
const sample = toSample(e);
|
|
50
|
+
for (const r of recognizers)
|
|
51
|
+
r.pointerMove(sample);
|
|
52
|
+
}
|
|
53
|
+
function onPointerUp(e) {
|
|
54
|
+
if (!activePointers.has(e.pointerId))
|
|
55
|
+
return;
|
|
56
|
+
activePointers.delete(e.pointerId);
|
|
57
|
+
const sample = toSample(e);
|
|
58
|
+
for (const r of recognizers)
|
|
59
|
+
r.pointerUp(sample);
|
|
60
|
+
}
|
|
61
|
+
function onPointerCancel(e) {
|
|
62
|
+
if (!activePointers.has(e.pointerId))
|
|
63
|
+
return;
|
|
64
|
+
activePointers.delete(e.pointerId);
|
|
65
|
+
for (const r of recognizers)
|
|
66
|
+
r.pointerCancel(e.pointerId);
|
|
67
|
+
}
|
|
68
|
+
canvas.addEventListener('pointerdown', onPointerDown);
|
|
69
|
+
const moveTarget = trackOutsideCanvas
|
|
70
|
+
? (options.documentTarget ?? (typeof document !== 'undefined' ? document : canvas))
|
|
71
|
+
: canvas;
|
|
72
|
+
moveTarget.addEventListener('pointermove', onPointerMove);
|
|
73
|
+
moveTarget.addEventListener('pointerup', onPointerUp);
|
|
74
|
+
moveTarget.addEventListener('pointercancel', onPointerCancel);
|
|
75
|
+
return () => {
|
|
76
|
+
canvas.removeEventListener('pointerdown', onPointerDown);
|
|
77
|
+
moveTarget.removeEventListener('pointermove', onPointerMove);
|
|
78
|
+
moveTarget.removeEventListener('pointerup', onPointerUp);
|
|
79
|
+
moveTarget.removeEventListener('pointercancel', onPointerCancel);
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function fallbackNow() {
|
|
83
|
+
if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
|
|
84
|
+
return () => performance.now();
|
|
85
|
+
}
|
|
86
|
+
return () => 0;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=gestures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gestures.js","sourceRoot":"","sources":["../src/gestures.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,4 +2,6 @@ export { createBrowserCanvasClient } from './browser-client.js';
|
|
|
2
2
|
export type { BrowserCanvasClientHandle, BrowserCanvasClientOptions } from './browser-client.js';
|
|
3
3
|
export { CanvasRenderer, enableSelection, enableFind, enableAccessibilityMirror, enableInputForwarding } from './renderer.js';
|
|
4
4
|
export type { CanvasRendererOptions, AccessibilityMirrorOptions, CanvasInputForwardingOptions } from './renderer.js';
|
|
5
|
+
export { attachGestureRecognizers } from './gestures.js';
|
|
6
|
+
export type { AttachGestureRecognizersOptions, CanvasGestureRecognizerLike } from './gestures.js';
|
|
5
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAA;AAC/D,YAAY,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAA;AAChG,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,UAAU,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAC7H,YAAY,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,4BAA4B,EAAE,MAAM,eAAe,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAA;AAC/D,YAAY,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAA;AAChG,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,UAAU,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAC7H,YAAY,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,4BAA4B,EAAE,MAAM,eAAe,CAAA;AACpH,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAA;AACxD,YAAY,EAAE,+BAA+B,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { createBrowserCanvasClient } from './browser-client.js';
|
|
2
2
|
export { CanvasRenderer, enableSelection, enableFind, enableAccessibilityMirror, enableInputForwarding } from './renderer.js';
|
|
3
|
+
export { attachGestureRecognizers } from './gestures.js';
|
|
3
4
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAA;AAE/D,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,UAAU,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAA;AAE/D,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,UAAU,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAE7H,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geometra/renderer-canvas",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.55.0",
|
|
4
4
|
"description": "Canvas2D renderer for Geometra — the geometry protocol for UI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -35,6 +35,6 @@
|
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@geometra/client": "^1.6.0",
|
|
38
|
-
"@geometra/core": "^1.
|
|
38
|
+
"@geometra/core": "^1.55.0"
|
|
39
39
|
}
|
|
40
40
|
}
|