@embedpdf/plugin-interaction-manager 1.4.1 → 2.0.0-next.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.
Files changed (40) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +456 -185
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/actions.d.ts +61 -36
  6. package/dist/lib/interaction-manager-plugin.d.ts +19 -14
  7. package/dist/lib/reducer.d.ts +2 -1
  8. package/dist/lib/types.d.ts +58 -50
  9. package/dist/preact/index.cjs +1 -1
  10. package/dist/preact/index.cjs.map +1 -1
  11. package/dist/preact/index.js +61 -40
  12. package/dist/preact/index.js.map +1 -1
  13. package/dist/react/index.cjs +1 -1
  14. package/dist/react/index.cjs.map +1 -1
  15. package/dist/react/index.js +61 -40
  16. package/dist/react/index.js.map +1 -1
  17. package/dist/shared/components/global-pointer-provider.d.ts +2 -1
  18. package/dist/shared/components/page-pointer-provider.d.ts +4 -5
  19. package/dist/shared/hooks/use-interaction-manager.d.ts +9 -7
  20. package/dist/shared-preact/components/global-pointer-provider.d.ts +2 -1
  21. package/dist/shared-preact/components/page-pointer-provider.d.ts +4 -5
  22. package/dist/shared-preact/hooks/use-interaction-manager.d.ts +9 -7
  23. package/dist/shared-react/components/global-pointer-provider.d.ts +2 -1
  24. package/dist/shared-react/components/page-pointer-provider.d.ts +4 -5
  25. package/dist/shared-react/hooks/use-interaction-manager.d.ts +9 -7
  26. package/dist/svelte/components/GlobalPointerProvider.svelte.d.ts +1 -0
  27. package/dist/svelte/components/PagePointerProvider.svelte.d.ts +4 -5
  28. package/dist/svelte/hooks/use-interaction-manager.svelte.d.ts +10 -8
  29. package/dist/svelte/index.cjs +1 -1
  30. package/dist/svelte/index.cjs.map +1 -1
  31. package/dist/svelte/index.js +128 -56
  32. package/dist/svelte/index.js.map +1 -1
  33. package/dist/vue/components/global-pointer-provider.vue.d.ts +6 -2
  34. package/dist/vue/components/page-pointer-provider.vue.d.ts +7 -7
  35. package/dist/vue/hooks/use-interaction-manager.d.ts +19 -26
  36. package/dist/vue/index.cjs +1 -1
  37. package/dist/vue/index.cjs.map +1 -1
  38. package/dist/vue/index.js +131 -63
  39. package/dist/vue/index.js.map +1 -1
  40. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -1,52 +1,53 @@
1
1
  import { BasePlugin, createEmitter, createBehaviorEmitter } from "@embedpdf/core";
2
+ const INIT_INTERACTION_STATE = "INTERACTION/INIT_STATE";
3
+ const CLEANUP_INTERACTION_STATE = "INTERACTION/CLEANUP_STATE";
2
4
  const ACTIVATE_MODE = "INTERACTION/ACTIVATE_MODE";
3
5
  const PAUSE_INTERACTION = "INTERACTION/PAUSE";
4
6
  const RESUME_INTERACTION = "INTERACTION/RESUME";
5
7
  const SET_CURSOR = "INTERACTION/SET_CURSOR";
8
+ const SET_ACTIVE_DOCUMENT = "INTERACTION/SET_ACTIVE_DOCUMENT";
6
9
  const SET_DEFAULT_MODE = "INTERACTION/SET_DEFAULT_MODE";
7
10
  const SET_EXCLUSION_RULES = "INTERACTION/SET_EXCLUSION_RULES";
8
11
  const ADD_EXCLUSION_CLASS = "INTERACTION/ADD_EXCLUSION_CLASS";
9
12
  const REMOVE_EXCLUSION_CLASS = "INTERACTION/REMOVE_EXCLUSION_CLASS";
10
13
  const ADD_EXCLUSION_ATTRIBUTE = "INTERACTION/ADD_EXCLUSION_ATTRIBUTE";
11
14
  const REMOVE_EXCLUSION_ATTRIBUTE = "INTERACTION/REMOVE_EXCLUSION_ATTRIBUTE";
12
- const setExclusionRules = (rules) => ({
13
- type: SET_EXCLUSION_RULES,
14
- payload: { rules }
15
- });
16
- const addExclusionClass = (className) => ({
17
- type: ADD_EXCLUSION_CLASS,
18
- payload: { className }
19
- });
20
- const removeExclusionClass = (className) => ({
21
- type: REMOVE_EXCLUSION_CLASS,
22
- payload: { className }
23
- });
24
- const addExclusionAttribute = (attribute) => ({
25
- type: ADD_EXCLUSION_ATTRIBUTE,
26
- payload: { attribute }
27
- });
28
- const removeExclusionAttribute = (attribute) => ({
29
- type: REMOVE_EXCLUSION_ATTRIBUTE,
30
- payload: { attribute }
31
- });
32
- const activateMode = (mode) => ({
33
- type: ACTIVATE_MODE,
34
- payload: { mode }
35
- });
36
- const setCursor = (cursor) => ({
37
- type: SET_CURSOR,
38
- payload: { cursor }
39
- });
40
- const setDefaultMode = (mode) => ({
41
- type: SET_DEFAULT_MODE,
42
- payload: { mode }
43
- });
44
- const pauseInteraction = () => ({
45
- type: PAUSE_INTERACTION
46
- });
47
- const resumeInteraction = () => ({
48
- type: RESUME_INTERACTION
49
- });
15
+ function initInteractionState(documentId, state) {
16
+ return { type: INIT_INTERACTION_STATE, payload: { documentId, state } };
17
+ }
18
+ function cleanupInteractionState(documentId) {
19
+ return { type: CLEANUP_INTERACTION_STATE, payload: documentId };
20
+ }
21
+ function activateMode(documentId, mode) {
22
+ return { type: ACTIVATE_MODE, payload: { documentId, mode } };
23
+ }
24
+ function pauseInteraction(documentId) {
25
+ return { type: PAUSE_INTERACTION, payload: documentId };
26
+ }
27
+ function resumeInteraction(documentId) {
28
+ return { type: RESUME_INTERACTION, payload: documentId };
29
+ }
30
+ function setCursor(documentId, cursor) {
31
+ return { type: SET_CURSOR, payload: { documentId, cursor } };
32
+ }
33
+ function setDefaultMode(mode) {
34
+ return { type: SET_DEFAULT_MODE, payload: { mode } };
35
+ }
36
+ function setExclusionRules(rules) {
37
+ return { type: SET_EXCLUSION_RULES, payload: { rules } };
38
+ }
39
+ function addExclusionClass(className) {
40
+ return { type: ADD_EXCLUSION_CLASS, payload: { className } };
41
+ }
42
+ function removeExclusionClass(className) {
43
+ return { type: REMOVE_EXCLUSION_CLASS, payload: { className } };
44
+ }
45
+ function addExclusionAttribute(attribute) {
46
+ return { type: ADD_EXCLUSION_ATTRIBUTE, payload: { attribute } };
47
+ }
48
+ function removeExclusionAttribute(attribute) {
49
+ return { type: REMOVE_EXCLUSION_ATTRIBUTE, payload: { attribute } };
50
+ }
50
51
  function mergeHandlers(list) {
51
52
  const keys = [
52
53
  "onPointerDown",
@@ -73,55 +74,92 @@ function mergeHandlers(list) {
73
74
  }
74
75
  return out;
75
76
  }
76
- const INITIAL_MODE = "pointerMode";
77
+ const INITIAL_MODE$1 = "pointerMode";
77
78
  const _InteractionManagerPlugin = class _InteractionManagerPlugin extends BasePlugin {
78
79
  constructor(id, registry, config) {
79
80
  super(id, registry);
80
81
  this.modes = /* @__PURE__ */ new Map();
81
82
  this.cursorClaims = /* @__PURE__ */ new Map();
82
83
  this.buckets = /* @__PURE__ */ new Map();
83
- this.alwaysGlobal = /* @__PURE__ */ new Set();
84
+ this.alwaysGlobal = /* @__PURE__ */ new Map();
84
85
  this.alwaysPage = /* @__PURE__ */ new Map();
85
86
  this.onModeChange$ = createEmitter();
86
87
  this.onHandlerChange$ = createEmitter();
87
88
  this.onCursorChange$ = createEmitter();
88
89
  this.onStateChange$ = createBehaviorEmitter();
89
90
  this.registerMode({
90
- id: INITIAL_MODE,
91
+ id: INITIAL_MODE$1,
91
92
  scope: "page",
92
93
  exclusive: false,
93
94
  cursor: "auto"
94
95
  });
95
- this.setDefaultMode(INITIAL_MODE);
96
- this.activate(INITIAL_MODE);
96
+ this.dispatch(setDefaultMode(INITIAL_MODE$1));
97
97
  if (config.exclusionRules) {
98
98
  this.dispatch(setExclusionRules(config.exclusionRules));
99
99
  }
100
100
  }
101
- async initialize(_) {
101
+ // ─────────────────────────────────────────────────────────
102
+ // Document Lifecycle Hooks (from BasePlugin)
103
+ // ─────────────────────────────────────────────────────────
104
+ onDocumentLoadingStarted(documentId) {
105
+ const docState = {
106
+ activeMode: this.state.defaultMode,
107
+ cursor: "auto",
108
+ paused: false
109
+ };
110
+ this.dispatch(initInteractionState(documentId, docState));
111
+ this.cursorClaims.set(documentId, /* @__PURE__ */ new Map());
112
+ this.buckets.set(documentId, /* @__PURE__ */ new Map());
113
+ this.alwaysGlobal.set(documentId, /* @__PURE__ */ new Set());
114
+ this.alwaysPage.set(documentId, /* @__PURE__ */ new Map());
115
+ const docBuckets = this.buckets.get(documentId);
116
+ for (const modeId of this.modes.keys()) {
117
+ docBuckets.set(modeId, { global: /* @__PURE__ */ new Set(), page: /* @__PURE__ */ new Map() });
118
+ }
119
+ this.logger.debug(
120
+ "InteractionManagerPlugin",
121
+ "DocumentOpened",
122
+ `Initialized interaction state for document: ${documentId}`
123
+ );
124
+ }
125
+ onDocumentClosed(documentId) {
126
+ this.cursorClaims.delete(documentId);
127
+ this.buckets.delete(documentId);
128
+ this.alwaysGlobal.delete(documentId);
129
+ this.alwaysPage.delete(documentId);
130
+ this.dispatch(cleanupInteractionState(documentId));
131
+ this.logger.debug(
132
+ "InteractionManagerPlugin",
133
+ "DocumentClosed",
134
+ `Cleaned up interaction state for document: ${documentId}`
135
+ );
102
136
  }
137
+ // ─────────────────────────────────────────────────────────
138
+ // Capability
139
+ // ─────────────────────────────────────────────────────────
103
140
  buildCapability() {
104
141
  return {
105
- activate: (modeId) => this.activate(modeId),
106
- onModeChange: this.onModeChange$.on,
107
- onCursorChange: this.onCursorChange$.on,
108
- onHandlerChange: this.onHandlerChange$.on,
109
- onStateChange: this.onStateChange$.on,
110
- getActiveMode: () => this.state.activeMode,
142
+ // Active document operations
143
+ getActiveMode: () => this.getActiveMode(),
111
144
  getActiveInteractionMode: () => this.getActiveInteractionMode(),
112
- activateDefaultMode: () => this.activate(this.state.defaultMode),
113
- registerMode: (mode) => this.registerMode(mode),
114
- registerHandlers: (options) => this.registerHandlers(options),
115
- registerAlways: (options) => this.registerAlways(options),
116
- setCursor: (token, cursor, priority = 0) => this.setCursor(token, cursor, priority),
145
+ activate: (modeId) => this.activate(modeId),
146
+ activateDefaultMode: () => this.activateDefaultMode(),
147
+ setCursor: (token, cursor, priority) => this.setCursor(token, cursor, priority),
148
+ getCurrentCursor: () => this.getCurrentCursor(),
117
149
  removeCursor: (token) => this.removeCursor(token),
118
- getCurrentCursor: () => this.state.cursor,
119
150
  getHandlersForScope: (scope) => this.getHandlersForScope(scope),
120
151
  activeModeIsExclusive: () => this.activeModeIsExclusive(),
121
- pause: () => this.dispatch(pauseInteraction()),
122
- resume: () => this.dispatch(resumeInteraction()),
152
+ pause: () => this.pause(),
153
+ resume: () => this.resume(),
123
154
  // Treat a destroyed registry as "paused" so late DOM events are ignored during teardown.
124
- isPaused: () => this.registry.isDestroyed() || this.state.paused,
155
+ isPaused: () => this.registry.isDestroyed() || this.isPaused(),
156
+ getState: () => this.getDocumentStateOrThrow(),
157
+ // Document-scoped operations
158
+ forDocument: (documentId) => this.createInteractionScope(documentId),
159
+ // Global management
160
+ registerMode: (mode) => this.registerMode(mode),
161
+ registerHandlers: (options) => this.registerHandlers(options),
162
+ registerAlways: (options) => this.registerAlways(options),
125
163
  setDefaultMode: (id) => this.setDefaultMode(id),
126
164
  getDefaultMode: () => this.state.defaultMode,
127
165
  getExclusionRules: () => this.state.exclusionRules,
@@ -129,21 +167,83 @@ const _InteractionManagerPlugin = class _InteractionManagerPlugin extends BasePl
129
167
  addExclusionClass: (className) => this.dispatch(addExclusionClass(className)),
130
168
  removeExclusionClass: (className) => this.dispatch(removeExclusionClass(className)),
131
169
  addExclusionAttribute: (attribute) => this.dispatch(addExclusionAttribute(attribute)),
132
- removeExclusionAttribute: (attribute) => this.dispatch(removeExclusionAttribute(attribute))
170
+ removeExclusionAttribute: (attribute) => this.dispatch(removeExclusionAttribute(attribute)),
171
+ // Events
172
+ onModeChange: this.onModeChange$.on,
173
+ onCursorChange: this.onCursorChange$.on,
174
+ onHandlerChange: this.onHandlerChange$.on,
175
+ onStateChange: this.onStateChange$.on
176
+ };
177
+ }
178
+ // ─────────────────────────────────────────────────────────
179
+ // Document Scoping
180
+ // ─────────────────────────────────────────────────────────
181
+ createInteractionScope(documentId) {
182
+ return {
183
+ getActiveMode: () => this.getActiveMode(documentId),
184
+ getActiveInteractionMode: () => this.getActiveInteractionMode(documentId),
185
+ activate: (modeId) => this.activate(modeId, documentId),
186
+ activateDefaultMode: () => this.activateDefaultMode(documentId),
187
+ setCursor: (token, cursor, priority) => this.setCursor(token, cursor, priority, documentId),
188
+ getCurrentCursor: () => this.getCurrentCursor(documentId),
189
+ removeCursor: (token) => this.removeCursor(token, documentId),
190
+ getHandlersForScope: (scope) => this.getHandlersForScope(scope),
191
+ activeModeIsExclusive: () => this.activeModeIsExclusive(documentId),
192
+ pause: () => this.pause(documentId),
193
+ resume: () => this.resume(documentId),
194
+ isPaused: () => this.isPaused(documentId),
195
+ getState: () => this.getDocumentStateOrThrow(documentId),
196
+ onModeChange: (listener) => this.onModeChange$.on((event) => {
197
+ if (event.documentId === documentId) listener(event.activeMode);
198
+ }),
199
+ onCursorChange: (listener) => this.onCursorChange$.on((event) => {
200
+ if (event.documentId === documentId) listener(event.cursor);
201
+ }),
202
+ onStateChange: (listener) => this.onStateChange$.on((event) => {
203
+ if (event.documentId === documentId) listener(event.state);
204
+ })
133
205
  };
134
206
  }
135
- activate(mode) {
136
- if (!this.modes.has(mode)) {
137
- throw new Error(`[interaction] unknown mode '${mode}'`);
207
+ // ─────────────────────────────────────────────────────────
208
+ // State Helpers
209
+ // ─────────────────────────────────────────────────────────
210
+ getDocumentState(documentId) {
211
+ const id = documentId ?? this.getActiveDocumentId();
212
+ return this.state.documents[id] ?? null;
213
+ }
214
+ getDocumentStateOrThrow(documentId) {
215
+ const state = this.getDocumentState(documentId);
216
+ if (!state) {
217
+ throw new Error(`Interaction state not found for document: ${documentId ?? "active"}`);
138
218
  }
139
- if (mode === this.state.activeMode) return;
140
- const previousMode = this.state.activeMode;
141
- this.cursorClaims.clear();
142
- this.notifyHandlersInactive(previousMode);
143
- this.dispatch(activateMode(mode));
144
- this.emitCursor();
145
- this.notifyHandlersActive(mode);
146
- this.onModeChange$.emit({ ...this.state, activeMode: mode });
219
+ return state;
220
+ }
221
+ // ─────────────────────────────────────────────────────────
222
+ // Core Operations
223
+ // ─────────────────────────────────────────────────────────
224
+ activate(modeId, documentId) {
225
+ var _a;
226
+ const id = documentId ?? this.getActiveDocumentId();
227
+ const docState = this.getDocumentStateOrThrow(id);
228
+ if (!this.modes.has(modeId)) {
229
+ throw new Error(`[interaction] unknown mode '${modeId}'`);
230
+ }
231
+ if (modeId === docState.activeMode) return;
232
+ const previousMode = docState.activeMode;
233
+ (_a = this.cursorClaims.get(id)) == null ? void 0 : _a.clear();
234
+ this.notifyHandlersInactive(id, previousMode);
235
+ this.dispatch(activateMode(id, modeId));
236
+ this.emitCursor(id);
237
+ this.notifyHandlersActive(id, modeId);
238
+ this.onModeChange$.emit({
239
+ documentId: id,
240
+ activeMode: modeId,
241
+ previousMode
242
+ });
243
+ }
244
+ activateDefaultMode(documentId) {
245
+ const id = documentId ?? this.getActiveDocumentId();
246
+ this.activate(this.state.defaultMode, id);
147
247
  }
148
248
  setDefaultMode(modeId) {
149
249
  if (!this.modes.has(modeId)) {
@@ -151,78 +251,58 @@ const _InteractionManagerPlugin = class _InteractionManagerPlugin extends BasePl
151
251
  }
152
252
  this.dispatch(setDefaultMode(modeId));
153
253
  }
154
- notifyHandlersActive(modeId) {
155
- this.alwaysGlobal.forEach((handler) => {
156
- var _a;
157
- (_a = handler.onHandlerActiveStart) == null ? void 0 : _a.call(handler, modeId);
158
- });
159
- this.alwaysPage.forEach((handlerSet) => {
160
- handlerSet.forEach((handler) => {
161
- var _a;
162
- (_a = handler.onHandlerActiveStart) == null ? void 0 : _a.call(handler, modeId);
163
- });
164
- });
165
- const mode = this.modes.get(modeId);
166
- if (!mode) return;
167
- const bucket = this.buckets.get(modeId);
168
- if (!bucket) return;
169
- if (mode.scope === "global") {
170
- bucket.global.forEach((handler) => {
171
- var _a;
172
- (_a = handler.onHandlerActiveStart) == null ? void 0 : _a.call(handler, modeId);
173
- });
174
- }
175
- if (mode.scope === "page") {
176
- bucket.page.forEach((handlerSet, pageIndex) => {
177
- handlerSet.forEach((handler) => {
178
- var _a;
179
- (_a = handler.onHandlerActiveStart) == null ? void 0 : _a.call(handler, modeId);
180
- });
181
- });
182
- }
254
+ getActiveMode(documentId) {
255
+ return this.getDocumentStateOrThrow(documentId).activeMode;
183
256
  }
184
- notifyHandlersInactive(modeId) {
185
- this.alwaysGlobal.forEach((handler) => {
186
- var _a;
187
- (_a = handler.onHandlerActiveEnd) == null ? void 0 : _a.call(handler, modeId);
188
- });
189
- this.alwaysPage.forEach((handlerSet) => {
190
- handlerSet.forEach((handler) => {
191
- var _a;
192
- (_a = handler.onHandlerActiveEnd) == null ? void 0 : _a.call(handler, modeId);
193
- });
194
- });
195
- const mode = this.modes.get(modeId);
196
- if (!mode) return;
197
- const bucket = this.buckets.get(modeId);
198
- if (!bucket) return;
199
- if (mode.scope === "global") {
200
- bucket.global.forEach((handler) => {
201
- var _a;
202
- (_a = handler.onHandlerActiveEnd) == null ? void 0 : _a.call(handler, modeId);
203
- });
204
- }
205
- if (mode.scope === "page") {
206
- bucket.page.forEach((handlerSet, pageIndex) => {
207
- handlerSet.forEach((handler) => {
208
- var _a;
209
- (_a = handler.onHandlerActiveEnd) == null ? void 0 : _a.call(handler, modeId);
210
- });
211
- });
212
- }
257
+ getActiveInteractionMode(documentId) {
258
+ const docState = this.getDocumentState(documentId);
259
+ if (!docState) return null;
260
+ return this.modes.get(docState.activeMode) ?? null;
261
+ }
262
+ activeModeIsExclusive(documentId) {
263
+ const mode = this.getActiveInteractionMode(documentId);
264
+ return !!(mode == null ? void 0 : mode.exclusive);
265
+ }
266
+ pause(documentId) {
267
+ const id = documentId ?? this.getActiveDocumentId();
268
+ this.dispatch(pauseInteraction(id));
269
+ }
270
+ resume(documentId) {
271
+ const id = documentId ?? this.getActiveDocumentId();
272
+ this.dispatch(resumeInteraction(id));
213
273
  }
274
+ isPaused(documentId) {
275
+ return this.getDocumentStateOrThrow(documentId).paused;
276
+ }
277
+ // ─────────────────────────────────────────────────────────
278
+ // Mode Management
279
+ // ─────────────────────────────────────────────────────────
214
280
  registerMode(mode) {
215
281
  this.modes.set(mode.id, mode);
216
- if (!this.buckets.has(mode.id)) {
217
- this.buckets.set(mode.id, { global: /* @__PURE__ */ new Set(), page: /* @__PURE__ */ new Map() });
282
+ for (const documentId of this.buckets.keys()) {
283
+ const docBuckets = this.buckets.get(documentId);
284
+ if (!docBuckets.has(mode.id)) {
285
+ docBuckets.set(mode.id, { global: /* @__PURE__ */ new Set(), page: /* @__PURE__ */ new Map() });
286
+ }
218
287
  }
219
288
  }
220
- /** ---------- pointer-handler handling ------------ */
221
- registerHandlers({ modeId, handlers, pageIndex }) {
289
+ // ─────────────────────────────────────────────────────────
290
+ // Handler Management
291
+ // ─────────────────────────────────────────────────────────
292
+ registerHandlers({
293
+ documentId,
294
+ modeId,
295
+ handlers,
296
+ pageIndex
297
+ }) {
222
298
  const modeIds = Array.isArray(modeId) ? modeId : [modeId];
223
299
  const cleanupFunctions = [];
300
+ const docBuckets = this.buckets.get(documentId);
301
+ if (!docBuckets) {
302
+ throw new Error(`No buckets found for document: ${documentId}`);
303
+ }
224
304
  for (const id of modeIds) {
225
- const bucket = this.buckets.get(id);
305
+ const bucket = docBuckets.get(id);
226
306
  if (!bucket) throw new Error(`unknown mode '${id}'`);
227
307
  if (pageIndex == null) {
228
308
  bucket.global.add(handlers);
@@ -253,72 +333,182 @@ const _InteractionManagerPlugin = class _InteractionManagerPlugin extends BasePl
253
333
  }
254
334
  registerAlways({ scope, handlers }) {
255
335
  if (scope.type === "global") {
256
- this.alwaysGlobal.add(handlers);
336
+ const set2 = this.alwaysGlobal.get(scope.documentId) ?? /* @__PURE__ */ new Set();
337
+ set2.add(handlers);
338
+ this.alwaysGlobal.set(scope.documentId, set2);
257
339
  this.onHandlerChange$.emit({ ...this.state });
258
- return () => this.alwaysGlobal.delete(handlers);
340
+ return () => {
341
+ set2.delete(handlers);
342
+ this.onHandlerChange$.emit({ ...this.state });
343
+ };
259
344
  }
260
- const set = this.alwaysPage.get(scope.pageIndex) ?? /* @__PURE__ */ new Set();
345
+ const docPageMap = this.alwaysPage.get(scope.documentId) ?? /* @__PURE__ */ new Map();
346
+ const set = docPageMap.get(scope.pageIndex) ?? /* @__PURE__ */ new Set();
261
347
  set.add(handlers);
262
- this.alwaysPage.set(scope.pageIndex, set);
348
+ docPageMap.set(scope.pageIndex, set);
349
+ this.alwaysPage.set(scope.documentId, docPageMap);
263
350
  this.onHandlerChange$.emit({ ...this.state });
264
351
  return () => {
265
352
  set.delete(handlers);
266
353
  this.onHandlerChange$.emit({ ...this.state });
267
354
  };
268
355
  }
269
- /** Returns the *merged* handler set that should be active for the given
270
- * provider (`global` wrapper or a single page wrapper).
271
- * – `alwaysGlobal` / `alwaysPage` are **always** active.
272
- * – Handlers that belong to the current mode are added on top **iff**
273
- * the mode’s own scope matches the provider’s scope. */
274
356
  getHandlersForScope(scope) {
275
- if (!this.state) return null;
276
- const mode = this.modes.get(this.state.activeMode);
357
+ var _a;
358
+ const docState = this.getDocumentState(scope.documentId);
359
+ if (!docState) return null;
360
+ const mode = this.modes.get(docState.activeMode);
277
361
  if (!mode) return null;
278
- const bucket = this.buckets.get(mode.id);
362
+ const docBuckets = this.buckets.get(scope.documentId);
363
+ if (!docBuckets) return null;
364
+ const bucket = docBuckets.get(mode.id);
279
365
  if (!bucket) return null;
280
366
  const mergeSets = (a, b) => a.size || b.size ? mergeHandlers([...a, ...b]) : null;
281
367
  if (scope.type === "global") {
368
+ const alwaysSet = this.alwaysGlobal.get(scope.documentId) ?? /* @__PURE__ */ new Set();
282
369
  const modeSpecific = mode.scope === "global" ? bucket.global : /* @__PURE__ */ new Set();
283
- return mergeSets(this.alwaysGlobal, modeSpecific);
370
+ return mergeSets(alwaysSet, modeSpecific);
284
371
  }
285
- const alwaysPageSet = this.alwaysPage.get(scope.pageIndex) ?? /* @__PURE__ */ new Set();
372
+ const alwaysPageSet = ((_a = this.alwaysPage.get(scope.documentId)) == null ? void 0 : _a.get(scope.pageIndex)) ?? /* @__PURE__ */ new Set();
286
373
  const modePageSet = mode.scope === "page" ? bucket.page.get(scope.pageIndex) ?? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set();
287
374
  return mergeSets(alwaysPageSet, modePageSet);
288
375
  }
289
- /** ---------- cursor handling --------------------- */
290
- setCursor(token, cursor, priority = 0) {
291
- this.cursorClaims.set(token, { cursor, priority });
292
- this.emitCursor();
376
+ // ─────────────────────────────────────────────────────────
377
+ // Cursor Management
378
+ // ─────────────────────────────────────────────────────────
379
+ setCursor(token, cursor, priority = 0, documentId) {
380
+ const id = documentId ?? this.getActiveDocumentId();
381
+ const claims = this.cursorClaims.get(id);
382
+ if (!claims) return;
383
+ claims.set(token, { cursor, priority });
384
+ this.emitCursor(id);
293
385
  }
294
- removeCursor(token) {
295
- this.cursorClaims.delete(token);
296
- this.emitCursor();
386
+ removeCursor(token, documentId) {
387
+ const id = documentId ?? this.getActiveDocumentId();
388
+ const claims = this.cursorClaims.get(id);
389
+ if (!claims) return;
390
+ claims.delete(token);
391
+ this.emitCursor(id);
297
392
  }
298
- emitCursor() {
393
+ getCurrentCursor(documentId) {
394
+ return this.getDocumentStateOrThrow(documentId).cursor;
395
+ }
396
+ emitCursor(documentId) {
299
397
  var _a;
300
- const top = [...this.cursorClaims.values()].sort((a, b) => b.priority - a.priority)[0] ?? {
301
- cursor: ((_a = this.modes.get(this.state.activeMode)) == null ? void 0 : _a.cursor) ?? "auto"
398
+ const claims = this.cursorClaims.get(documentId);
399
+ if (!claims) return;
400
+ const docState = this.getDocumentState(documentId);
401
+ if (!docState) return;
402
+ const top = [...claims.values()].sort((a, b) => b.priority - a.priority)[0] ?? {
403
+ cursor: ((_a = this.modes.get(docState.activeMode)) == null ? void 0 : _a.cursor) ?? "auto"
302
404
  };
303
- if (top.cursor !== this.state.cursor) {
304
- this.dispatch(setCursor(top.cursor));
305
- this.onCursorChange$.emit(top.cursor);
405
+ if (top.cursor !== docState.cursor) {
406
+ this.dispatch(setCursor(documentId, top.cursor));
407
+ this.onCursorChange$.emit({
408
+ documentId,
409
+ cursor: top.cursor
410
+ });
306
411
  }
307
412
  }
308
- onStoreUpdated(_, newState) {
309
- this.onStateChange$.emit(newState);
413
+ // ─────────────────────────────────────────────────────────
414
+ // Handler Lifecycle Notifications
415
+ // ─────────────────────────────────────────────────────────
416
+ notifyHandlersActive(documentId, modeId) {
417
+ var _a, _b;
418
+ (_a = this.alwaysGlobal.get(documentId)) == null ? void 0 : _a.forEach((handler) => {
419
+ var _a2;
420
+ (_a2 = handler.onHandlerActiveStart) == null ? void 0 : _a2.call(handler, modeId);
421
+ });
422
+ (_b = this.alwaysPage.get(documentId)) == null ? void 0 : _b.forEach((handlerSet) => {
423
+ handlerSet.forEach((handler) => {
424
+ var _a2;
425
+ (_a2 = handler.onHandlerActiveStart) == null ? void 0 : _a2.call(handler, modeId);
426
+ });
427
+ });
428
+ const mode = this.modes.get(modeId);
429
+ if (!mode) return;
430
+ const docBuckets = this.buckets.get(documentId);
431
+ if (!docBuckets) return;
432
+ const bucket = docBuckets.get(modeId);
433
+ if (!bucket) return;
434
+ if (mode.scope === "global") {
435
+ bucket.global.forEach((handler) => {
436
+ var _a2;
437
+ (_a2 = handler.onHandlerActiveStart) == null ? void 0 : _a2.call(handler, modeId);
438
+ });
439
+ }
440
+ if (mode.scope === "page") {
441
+ bucket.page.forEach((handlerSet) => {
442
+ handlerSet.forEach((handler) => {
443
+ var _a2;
444
+ (_a2 = handler.onHandlerActiveStart) == null ? void 0 : _a2.call(handler, modeId);
445
+ });
446
+ });
447
+ }
310
448
  }
311
- activeModeIsExclusive() {
312
- const mode = this.modes.get(this.state.activeMode);
313
- return !!(mode == null ? void 0 : mode.exclusive);
449
+ notifyHandlersInactive(documentId, modeId) {
450
+ var _a, _b;
451
+ (_a = this.alwaysGlobal.get(documentId)) == null ? void 0 : _a.forEach((handler) => {
452
+ var _a2;
453
+ (_a2 = handler.onHandlerActiveEnd) == null ? void 0 : _a2.call(handler, modeId);
454
+ });
455
+ (_b = this.alwaysPage.get(documentId)) == null ? void 0 : _b.forEach((handlerSet) => {
456
+ handlerSet.forEach((handler) => {
457
+ var _a2;
458
+ (_a2 = handler.onHandlerActiveEnd) == null ? void 0 : _a2.call(handler, modeId);
459
+ });
460
+ });
461
+ const mode = this.modes.get(modeId);
462
+ if (!mode) return;
463
+ const docBuckets = this.buckets.get(documentId);
464
+ if (!docBuckets) return;
465
+ const bucket = docBuckets.get(modeId);
466
+ if (!bucket) return;
467
+ if (mode.scope === "global") {
468
+ bucket.global.forEach((handler) => {
469
+ var _a2;
470
+ (_a2 = handler.onHandlerActiveEnd) == null ? void 0 : _a2.call(handler, modeId);
471
+ });
472
+ }
473
+ if (mode.scope === "page") {
474
+ bucket.page.forEach((handlerSet) => {
475
+ handlerSet.forEach((handler) => {
476
+ var _a2;
477
+ (_a2 = handler.onHandlerActiveEnd) == null ? void 0 : _a2.call(handler, modeId);
478
+ });
479
+ });
480
+ }
481
+ }
482
+ // ─────────────────────────────────────────────────────────
483
+ // Store Update Handlers
484
+ // ─────────────────────────────────────────────────────────
485
+ onStoreUpdated(prevState, newState) {
486
+ for (const documentId in newState.documents) {
487
+ const prevDoc = prevState.documents[documentId];
488
+ const newDoc = newState.documents[documentId];
489
+ if (prevDoc !== newDoc) {
490
+ this.onStateChange$.emit({
491
+ documentId,
492
+ state: newDoc
493
+ });
494
+ }
495
+ }
314
496
  }
315
- getActiveInteractionMode() {
316
- return this.modes.get(this.state.activeMode) ?? null;
497
+ // ─────────────────────────────────────────────────────────
498
+ // Lifecycle
499
+ // ─────────────────────────────────────────────────────────
500
+ async initialize(_) {
501
+ this.logger.info(
502
+ "InteractionManagerPlugin",
503
+ "Initialize",
504
+ "Interaction Manager Plugin initialized"
505
+ );
317
506
  }
318
- // keep emitter clean
319
507
  async destroy() {
320
508
  this.onModeChange$.clear();
321
509
  this.onCursorChange$.clear();
510
+ this.onHandlerChange$.clear();
511
+ this.onStateChange$.clear();
322
512
  await super.destroy();
323
513
  }
324
514
  };
@@ -340,38 +530,119 @@ const manifest = {
340
530
  }
341
531
  }
342
532
  };
343
- const initialState = {
344
- activeMode: "pointerMode",
345
- defaultMode: "pointerMode",
533
+ const INITIAL_MODE = "pointerMode";
534
+ const initialDocumentState = {
535
+ activeMode: INITIAL_MODE,
346
536
  cursor: "auto",
347
- paused: false,
537
+ paused: false
538
+ };
539
+ const initialState = {
540
+ defaultMode: INITIAL_MODE,
348
541
  exclusionRules: {
349
542
  classes: [],
350
- dataAttributes: []
351
- }
543
+ dataAttributes: ["data-no-interaction"]
544
+ },
545
+ documents: {},
546
+ activeDocumentId: null
352
547
  };
353
- const reducer = (state, action) => {
548
+ const reducer = (state = initialState, action) => {
354
549
  switch (action.type) {
355
- case ACTIVATE_MODE:
550
+ // ─────────────────────────────────────────────────────────
551
+ // Document Lifecycle
552
+ // ─────────────────────────────────────────────────────────
553
+ case INIT_INTERACTION_STATE: {
554
+ const { documentId, state: docState } = action.payload;
555
+ return {
556
+ ...state,
557
+ documents: {
558
+ ...state.documents,
559
+ [documentId]: docState
560
+ },
561
+ // Set as active if no active document
562
+ activeDocumentId: state.activeDocumentId ?? documentId
563
+ };
564
+ }
565
+ case CLEANUP_INTERACTION_STATE: {
566
+ const documentId = action.payload;
567
+ const { [documentId]: removed, ...remainingDocs } = state.documents;
568
+ return {
569
+ ...state,
570
+ documents: remainingDocs,
571
+ activeDocumentId: state.activeDocumentId === documentId ? null : state.activeDocumentId
572
+ };
573
+ }
574
+ case SET_ACTIVE_DOCUMENT: {
356
575
  return {
357
576
  ...state,
358
- activeMode: action.payload.mode
577
+ activeDocumentId: action.payload
359
578
  };
360
- case SET_CURSOR:
579
+ }
580
+ // ─────────────────────────────────────────────────────────
581
+ // Per-Document Actions
582
+ // ─────────────────────────────────────────────────────────
583
+ case ACTIVATE_MODE: {
584
+ const { documentId, mode } = action.payload;
585
+ const docState = state.documents[documentId];
586
+ if (!docState) return state;
361
587
  return {
362
588
  ...state,
363
- cursor: action.payload.cursor
589
+ documents: {
590
+ ...state.documents,
591
+ [documentId]: {
592
+ ...docState,
593
+ activeMode: mode
594
+ }
595
+ }
364
596
  };
365
- case PAUSE_INTERACTION:
597
+ }
598
+ case SET_CURSOR: {
599
+ const { documentId, cursor } = action.payload;
600
+ const docState = state.documents[documentId];
601
+ if (!docState) return state;
366
602
  return {
367
603
  ...state,
368
- paused: true
604
+ documents: {
605
+ ...state.documents,
606
+ [documentId]: {
607
+ ...docState,
608
+ cursor
609
+ }
610
+ }
369
611
  };
370
- case RESUME_INTERACTION:
612
+ }
613
+ case PAUSE_INTERACTION: {
614
+ const documentId = action.payload;
615
+ const docState = state.documents[documentId];
616
+ if (!docState) return state;
371
617
  return {
372
618
  ...state,
373
- paused: false
619
+ documents: {
620
+ ...state.documents,
621
+ [documentId]: {
622
+ ...docState,
623
+ paused: true
624
+ }
625
+ }
374
626
  };
627
+ }
628
+ case RESUME_INTERACTION: {
629
+ const documentId = action.payload;
630
+ const docState = state.documents[documentId];
631
+ if (!docState) return state;
632
+ return {
633
+ ...state,
634
+ documents: {
635
+ ...state.documents,
636
+ [documentId]: {
637
+ ...docState,
638
+ paused: false
639
+ }
640
+ }
641
+ };
642
+ }
643
+ // ─────────────────────────────────────────────────────────
644
+ // Global Actions
645
+ // ─────────────────────────────────────────────────────────
375
646
  case SET_DEFAULT_MODE:
376
647
  return {
377
648
  ...state,
@@ -390,7 +661,6 @@ const reducer = (state, action) => {
390
661
  classes: [...state.exclusionRules.classes || [], action.payload.className].filter(
391
662
  (v, i, a) => a.indexOf(v) === i
392
663
  )
393
- // Remove duplicates
394
664
  }
395
665
  };
396
666
  case REMOVE_EXCLUSION_CLASS:
@@ -438,6 +708,7 @@ export {
438
708
  INTERACTION_MANAGER_PLUGIN_ID,
439
709
  InteractionManagerPlugin,
440
710
  InteractionManagerPluginPackage,
711
+ initialDocumentState,
441
712
  initialState,
442
713
  manifest,
443
714
  reducer