@embedpdf/core 1.5.0 → 2.0.0-next.1

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 (55) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +680 -113
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/base/base-plugin.d.ts +52 -0
  6. package/dist/lib/index.d.ts +1 -0
  7. package/dist/lib/registry/plugin-registry.d.ts +6 -2
  8. package/dist/lib/store/actions.d.ts +122 -31
  9. package/dist/lib/store/initial-state.d.ts +22 -6
  10. package/dist/lib/store/reducer-helpers.d.ts +11 -0
  11. package/dist/lib/store/selectors.d.ts +21 -3
  12. package/dist/lib/types/plugin.d.ts +2 -2
  13. package/dist/lib/utils/event-control.d.ts +37 -2
  14. package/dist/lib/utils/eventing.d.ts +1 -1
  15. package/dist/lib/utils/scoped-eventing.d.ts +71 -0
  16. package/dist/preact/index.cjs +1 -1
  17. package/dist/preact/index.cjs.map +1 -1
  18. package/dist/preact/index.js +53 -18
  19. package/dist/preact/index.js.map +1 -1
  20. package/dist/react/index.cjs +1 -1
  21. package/dist/react/index.cjs.map +1 -1
  22. package/dist/react/index.js +53 -18
  23. package/dist/react/index.js.map +1 -1
  24. package/dist/shared/context.d.ts +6 -1
  25. package/dist/shared/hooks/index.d.ts +1 -0
  26. package/dist/shared/hooks/use-core-state.d.ts +4 -2
  27. package/dist/shared/hooks/use-document-state.d.ts +8 -0
  28. package/dist/shared-preact/context.d.ts +6 -1
  29. package/dist/shared-preact/hooks/index.d.ts +1 -0
  30. package/dist/shared-preact/hooks/use-core-state.d.ts +4 -2
  31. package/dist/shared-preact/hooks/use-document-state.d.ts +8 -0
  32. package/dist/shared-react/context.d.ts +6 -1
  33. package/dist/shared-react/hooks/index.d.ts +1 -0
  34. package/dist/shared-react/hooks/use-core-state.d.ts +4 -2
  35. package/dist/shared-react/hooks/use-document-state.d.ts +8 -0
  36. package/dist/svelte/hooks/index.d.ts +1 -0
  37. package/dist/svelte/hooks/use-core-state.svelte.d.ts +5 -4
  38. package/dist/svelte/hooks/use-document-state.svelte.d.ts +9 -0
  39. package/dist/svelte/hooks/use-registry.svelte.d.ts +6 -1
  40. package/dist/svelte/index.cjs +1 -1
  41. package/dist/svelte/index.cjs.map +1 -1
  42. package/dist/svelte/index.js +55 -17
  43. package/dist/svelte/index.js.map +1 -1
  44. package/dist/vue/components/auto-mount.vue.d.ts +3 -2
  45. package/dist/vue/components/embed-pdf.vue.d.ts +13 -2
  46. package/dist/vue/components/nested-wrapper.vue.d.ts +3 -2
  47. package/dist/vue/composables/index.d.ts +1 -0
  48. package/dist/vue/composables/use-core-state.d.ts +8 -1
  49. package/dist/vue/composables/use-document-state.d.ts +8 -0
  50. package/dist/vue/context.d.ts +6 -1
  51. package/dist/vue/index.cjs +1 -1
  52. package/dist/vue/index.cjs.map +1 -1
  53. package/dist/vue/index.js +88 -27
  54. package/dist/vue/index.js.map +1 -1
  55. package/package.json +3 -3
@@ -2,6 +2,7 @@ import { IPlugin } from '../types/plugin';
2
2
  import { PluginRegistry } from '../registry/plugin-registry';
3
3
  import { Action, CoreAction, CoreState, PluginStore, Store, StoreState } from '../store';
4
4
  import { Logger, PdfEngine } from '@embedpdf/models';
5
+ import { DocumentState } from '../store/initial-state';
5
6
  export interface StateChangeHandler<TState> {
6
7
  (state: TState): void;
7
8
  }
@@ -17,6 +18,11 @@ export declare abstract class BasePlugin<TConfig = unknown, TCapability = unknow
17
18
  private debouncedTimeouts;
18
19
  private unsubscribeFromState;
19
20
  private unsubscribeFromCoreStore;
21
+ private unsubscribeFromStartLoadingDocument;
22
+ private unsubscribeFromSetDocumentLoaded;
23
+ private unsubscribeFromCloseDocument;
24
+ private unsubscribeFromSetScale;
25
+ private unsubscribeFromSetRotation;
20
26
  private _capability?;
21
27
  private readyPromise;
22
28
  private readyResolve;
@@ -96,6 +102,31 @@ export declare abstract class BasePlugin<TConfig = unknown, TCapability = unknow
96
102
  * @param newState New state
97
103
  */
98
104
  protected onCoreStoreUpdated(oldState: StoreState<CoreState>, newState: StoreState<CoreState>): void;
105
+ /**
106
+ * Called when a document is opened
107
+ * Override to initialize per-document state
108
+ * @param documentId The ID of the document that was opened
109
+ */
110
+ protected onDocumentLoadingStarted(documentId: string): void;
111
+ /**
112
+ * Called when a document is loaded
113
+ * @param documentId The ID of the document that is loaded
114
+ */
115
+ protected onDocumentLoaded(documentId: string): void;
116
+ /**
117
+ * Called when a document is closed
118
+ * Override to cleanup per-document state
119
+ * @param documentId The ID of the document that was closed
120
+ */
121
+ protected onDocumentClosed(documentId: string): void;
122
+ /**
123
+ * Called when the active document changes
124
+ * @param previousId The ID of the previous active document
125
+ * @param currentId The ID of the new active document
126
+ */
127
+ protected onActiveDocumentChanged(previousId: string | null, currentId: string | null): void;
128
+ protected onScaleChanged(documentId: string, scale: number): void;
129
+ protected onRotationChanged(documentId: string, rotation: number): void;
99
130
  /**
100
131
  * Cleanup method to be called when plugin is being destroyed
101
132
  */
@@ -112,4 +143,25 @@ export declare abstract class BasePlugin<TConfig = unknown, TCapability = unknow
112
143
  * Reset the ready state (useful for plugins that need to reinitialize)
113
144
  */
114
145
  protected resetReady(): void;
146
+ /**
147
+ * Get the active document ID
148
+ * @throws Error if no active document exists
149
+ */
150
+ protected getActiveDocumentId(): string;
151
+ /**
152
+ * Get the active document ID or null if none exists
153
+ */
154
+ protected getActiveDocumentIdOrNull(): string | null;
155
+ /**
156
+ * Get core document state for a specific document
157
+ * @param documentId Document ID (optional, defaults to active document)
158
+ * @returns Document state or null if not found
159
+ */
160
+ protected getCoreDocument(documentId?: string): DocumentState | null;
161
+ /**
162
+ * Get core document state for a specific document
163
+ * @param documentId Document ID (optional, defaults to active document)
164
+ * @throws Error if document not found
165
+ */
166
+ protected getCoreDocumentOrThrow(documentId?: string): DocumentState;
115
167
  }
@@ -10,6 +10,7 @@ export * from './store/initial-state';
10
10
  export * from './store/selectors';
11
11
  export * from './utils/event-control';
12
12
  export * from './utils/eventing';
13
+ export * from './utils/scoped-eventing';
13
14
  export * from './utils/math';
14
15
  export * from './utils/typed-object';
15
16
  export * from './plugin/builder';
@@ -51,9 +51,13 @@ export declare class PluginRegistry {
51
51
  */
52
52
  initialize(): Promise<void>;
53
53
  /**
54
- * Initialize a single plugin with all necessary checks
54
+ * Phase 2: Create instance and register capabilities
55
55
  */
56
- private initializePlugin;
56
+ private instantiatePlugin;
57
+ /**
58
+ * Phase 3: Run the initialize method
59
+ */
60
+ private runPluginInitialization;
57
61
  getPluginConfig<TConfig>(pluginId: string): TConfig;
58
62
  private validateConfig;
59
63
  updatePluginConfig<TConfig>(pluginId: string, config: Partial<TConfig>): Promise<void>;
@@ -1,51 +1,142 @@
1
- import { PdfDocumentObject, PdfPageObject, Rotation } from '@embedpdf/models';
2
- export declare const LOAD_DOCUMENT = "LOAD_DOCUMENT";
1
+ import { PdfDocumentObject, PdfErrorCode, PdfPageObject, Rotation } from '@embedpdf/models';
2
+ export declare const START_LOADING_DOCUMENT = "START_LOADING_DOCUMENT";
3
+ export declare const UPDATE_DOCUMENT_LOADING_PROGRESS = "UPDATE_DOCUMENT_LOADING_PROGRESS";
4
+ export declare const SET_DOCUMENT_LOADED = "SET_DOCUMENT_LOADED";
5
+ export declare const SET_DOCUMENT_ERROR = "SET_DOCUMENT_ERROR";
6
+ export declare const RETRY_LOADING_DOCUMENT = "RETRY_LOADING_DOCUMENT";
7
+ export declare const CLOSE_DOCUMENT = "CLOSE_DOCUMENT";
8
+ export declare const SET_ACTIVE_DOCUMENT = "SET_ACTIVE_DOCUMENT";
9
+ export declare const REORDER_DOCUMENTS = "REORDER_DOCUMENTS";
10
+ export declare const MOVE_DOCUMENT = "MOVE_DOCUMENT";
3
11
  export declare const REFRESH_DOCUMENT = "REFRESH_DOCUMENT";
4
12
  export declare const REFRESH_PAGES = "REFRESH_PAGES";
5
- export declare const SET_DOCUMENT = "SET_DOCUMENT";
6
- export declare const SET_DOCUMENT_ERROR = "SET_DOCUMENT_ERROR";
13
+ export declare const SET_PAGES = "SET_PAGES";
7
14
  export declare const SET_SCALE = "SET_SCALE";
8
15
  export declare const SET_ROTATION = "SET_ROTATION";
9
- export declare const SET_PAGES = "SET_PAGES";
10
- export declare const CORE_ACTION_TYPES: readonly ["LOAD_DOCUMENT", "REFRESH_DOCUMENT", "SET_DOCUMENT", "SET_DOCUMENT_ERROR", "SET_SCALE", "SET_ROTATION", "SET_PAGES"];
11
- export interface LoadDocumentAction {
12
- type: typeof LOAD_DOCUMENT;
16
+ export declare const SET_DEFAULT_SCALE = "SET_DEFAULT_SCALE";
17
+ export declare const SET_DEFAULT_ROTATION = "SET_DEFAULT_ROTATION";
18
+ export declare const CORE_ACTION_TYPES: readonly ["START_LOADING_DOCUMENT", "UPDATE_DOCUMENT_LOADING_PROGRESS", "SET_DOCUMENT_LOADED", "CLOSE_DOCUMENT", "SET_ACTIVE_DOCUMENT", "SET_DOCUMENT_ERROR", "RETRY_LOADING_DOCUMENT", "REFRESH_DOCUMENT", "REFRESH_PAGES", "SET_PAGES", "SET_SCALE", "SET_ROTATION", "SET_DEFAULT_SCALE", "SET_DEFAULT_ROTATION", "REORDER_DOCUMENTS", "MOVE_DOCUMENT"];
19
+ export interface StartLoadingDocumentAction {
20
+ type: typeof START_LOADING_DOCUMENT;
21
+ payload: {
22
+ documentId: string;
23
+ name?: string;
24
+ scale?: number;
25
+ rotation?: Rotation;
26
+ passwordProvided?: boolean;
27
+ autoActivate?: boolean;
28
+ };
29
+ }
30
+ export interface UpdateDocumentLoadingProgressAction {
31
+ type: typeof UPDATE_DOCUMENT_LOADING_PROGRESS;
32
+ payload: {
33
+ documentId: string;
34
+ progress: number;
35
+ };
36
+ }
37
+ export interface SetDocumentLoadedAction {
38
+ type: typeof SET_DOCUMENT_LOADED;
39
+ payload: {
40
+ documentId: string;
41
+ document: PdfDocumentObject;
42
+ };
43
+ }
44
+ export interface SetDocumentErrorAction {
45
+ type: typeof SET_DOCUMENT_ERROR;
46
+ payload: {
47
+ documentId: string;
48
+ error: string;
49
+ errorCode?: PdfErrorCode;
50
+ errorDetails?: any;
51
+ };
52
+ }
53
+ export interface RetryLoadingDocumentAction {
54
+ type: typeof RETRY_LOADING_DOCUMENT;
55
+ payload: {
56
+ documentId: string;
57
+ passwordProvided?: boolean;
58
+ };
59
+ }
60
+ export interface CloseDocumentAction {
61
+ type: typeof CLOSE_DOCUMENT;
62
+ payload: {
63
+ documentId: string;
64
+ nextActiveDocumentId?: string | null;
65
+ };
66
+ }
67
+ export interface SetActiveDocumentAction {
68
+ type: typeof SET_ACTIVE_DOCUMENT;
69
+ payload: string | null;
70
+ }
71
+ export interface ReorderDocumentsAction {
72
+ type: typeof REORDER_DOCUMENTS;
73
+ payload: string[];
74
+ }
75
+ export interface MoveDocumentAction {
76
+ type: typeof MOVE_DOCUMENT;
77
+ payload: {
78
+ documentId: string;
79
+ toIndex: number;
80
+ };
13
81
  }
14
82
  export interface RefreshDocumentAction {
15
83
  type: typeof REFRESH_DOCUMENT;
16
- payload: PdfDocumentObject;
84
+ payload: {
85
+ documentId: string;
86
+ document: PdfDocumentObject;
87
+ };
17
88
  }
18
89
  export interface RefreshPagesAction {
19
90
  type: typeof REFRESH_PAGES;
20
- payload: number[];
21
- }
22
- export interface SetDocumentAction {
23
- type: typeof SET_DOCUMENT;
24
- payload: PdfDocumentObject;
91
+ payload: {
92
+ documentId: string;
93
+ pageIndexes: number[];
94
+ };
25
95
  }
26
- export interface SetDocumentErrorAction {
27
- type: typeof SET_DOCUMENT_ERROR;
28
- payload: string;
96
+ export interface SetPagesAction {
97
+ type: typeof SET_PAGES;
98
+ payload: {
99
+ documentId: string;
100
+ pages: PdfPageObject[][];
101
+ };
29
102
  }
30
103
  export interface SetScaleAction {
31
104
  type: typeof SET_SCALE;
32
- payload: number;
105
+ payload: {
106
+ documentId?: string;
107
+ scale: number;
108
+ };
33
109
  }
34
110
  export interface SetRotationAction {
35
111
  type: typeof SET_ROTATION;
36
- payload: Rotation;
112
+ payload: {
113
+ documentId?: string;
114
+ rotation: Rotation;
115
+ };
37
116
  }
38
- export interface SetPagesAction {
39
- type: typeof SET_PAGES;
40
- payload: PdfPageObject[][];
117
+ export interface SetDefaultScaleAction {
118
+ type: typeof SET_DEFAULT_SCALE;
119
+ payload: number;
120
+ }
121
+ export interface SetDefaultRotationAction {
122
+ type: typeof SET_DEFAULT_ROTATION;
123
+ payload: Rotation;
41
124
  }
42
- export type DocumentAction = LoadDocumentAction | RefreshDocumentAction | RefreshPagesAction | SetDocumentAction | SetDocumentErrorAction | SetScaleAction | SetRotationAction | SetPagesAction;
125
+ export type DocumentAction = StartLoadingDocumentAction | UpdateDocumentLoadingProgressAction | SetDocumentLoadedAction | SetDocumentErrorAction | RetryLoadingDocumentAction | CloseDocumentAction | SetActiveDocumentAction | RefreshDocumentAction | RefreshPagesAction | SetPagesAction | SetScaleAction | SetRotationAction | SetDefaultScaleAction | SetDefaultRotationAction | ReorderDocumentsAction | MoveDocumentAction;
43
126
  export type CoreAction = DocumentAction;
44
- export declare const loadDocument: () => CoreAction;
45
- export declare const refreshDocument: (document: PdfDocumentObject) => CoreAction;
46
- export declare const refreshPages: (pages: number[]) => CoreAction;
47
- export declare const setDocument: (document: PdfDocumentObject) => CoreAction;
48
- export declare const setDocumentError: (error: string) => CoreAction;
49
- export declare const setScale: (scale: number) => CoreAction;
50
- export declare const setRotation: (rotation: Rotation) => CoreAction;
51
- export declare const setPages: (pages: PdfPageObject[][]) => CoreAction;
127
+ export declare const startLoadingDocument: (documentId: string, name?: string, scale?: number, rotation?: Rotation, passwordProvided?: boolean, autoActivate?: boolean) => CoreAction;
128
+ export declare const updateDocumentLoadingProgress: (documentId: string, progress: number) => CoreAction;
129
+ export declare const setDocumentLoaded: (documentId: string, document: PdfDocumentObject) => CoreAction;
130
+ export declare const setDocumentError: (documentId: string, error: string, errorCode?: PdfErrorCode, errorDetails?: any) => CoreAction;
131
+ export declare const retryLoadingDocument: (documentId: string, passwordProvided?: boolean) => CoreAction;
132
+ export declare const closeDocument: (documentId: string, nextActiveDocumentId?: string | null) => CoreAction;
133
+ export declare const setActiveDocument: (documentId: string | null) => CoreAction;
134
+ export declare const refreshDocument: (documentId: string, document: PdfDocumentObject) => CoreAction;
135
+ export declare const refreshPages: (documentId: string, pageIndexes: number[]) => CoreAction;
136
+ export declare const setPages: (documentId: string, pages: PdfPageObject[][]) => CoreAction;
137
+ export declare const setScale: (scale: number, documentId?: string) => CoreAction;
138
+ export declare const setRotation: (rotation: Rotation, documentId?: string) => CoreAction;
139
+ export declare const setDefaultScale: (scale: number) => CoreAction;
140
+ export declare const setDefaultRotation: (rotation: Rotation) => CoreAction;
141
+ export declare const reorderDocuments: (order: string[]) => CoreAction;
142
+ export declare const moveDocument: (documentId: string, toIndex: number) => CoreAction;
@@ -1,11 +1,27 @@
1
- import { PdfDocumentObject, PdfPageObject, Rotation } from '@embedpdf/models';
1
+ import { PdfDocumentObject, PdfErrorCode, Rotation } from '@embedpdf/models';
2
2
  import { PluginRegistryConfig } from '../types/plugin';
3
- export interface CoreState {
3
+ export type DocumentStatus = 'loading' | 'loaded' | 'error';
4
+ export interface DocumentState {
5
+ id: string;
6
+ name?: string;
7
+ status: DocumentStatus;
8
+ loadingProgress?: number;
9
+ error: string | null;
10
+ errorCode?: PdfErrorCode;
11
+ errorDetails?: any;
12
+ passwordProvided?: boolean;
13
+ document: PdfDocumentObject | null;
4
14
  scale: number;
5
15
  rotation: Rotation;
6
- document: PdfDocumentObject | null;
7
- pages: PdfPageObject[][];
8
- loading: boolean;
9
- error: string | null;
16
+ pageRefreshVersions: Record<number, number>;
17
+ loadStartedAt: number;
18
+ loadedAt?: number;
19
+ }
20
+ export interface CoreState {
21
+ documents: Record<string, DocumentState>;
22
+ documentOrder: string[];
23
+ activeDocumentId: string | null;
24
+ defaultScale: number;
25
+ defaultRotation: Rotation;
10
26
  }
11
27
  export declare const initialCoreState: (config?: PluginRegistryConfig) => CoreState;
@@ -0,0 +1,11 @@
1
+ import { CoreState } from './initial-state';
2
+ /**
3
+ * Calculate the next active document when closing a document.
4
+ * Returns the document to activate, or null if no documents remain.
5
+ */
6
+ export declare function calculateNextActiveDocument(state: CoreState, closingDocumentId: string, explicitNext?: string | null): string | null;
7
+ /**
8
+ * Reorder a document within the documentOrder array.
9
+ * Returns the new order, or null if the operation is invalid.
10
+ */
11
+ export declare function moveDocumentInOrder(currentOrder: string[], documentId: string, toIndex: number): string[] | null;
@@ -1,3 +1,21 @@
1
- import { CoreState } from './initial-state';
2
- import { PdfPageObjectWithRotatedSize } from '@embedpdf/models';
3
- export declare const getPagesWithRotatedSize: (state: CoreState) => PdfPageObjectWithRotatedSize[][];
1
+ import { CoreState, DocumentState } from './initial-state';
2
+ /**
3
+ * Get the active document state
4
+ */
5
+ export declare const getActiveDocumentState: (state: CoreState) => DocumentState | null;
6
+ /**
7
+ * Get document state by ID
8
+ */
9
+ export declare const getDocumentState: (state: CoreState, documentId: string) => DocumentState | null;
10
+ /**
11
+ * Get all document IDs
12
+ */
13
+ export declare const getDocumentIds: (state: CoreState) => string[];
14
+ /**
15
+ * Check if a document is loaded
16
+ */
17
+ export declare const isDocumentLoaded: (state: CoreState, documentId: string) => boolean;
18
+ /**
19
+ * Get the number of open documents
20
+ */
21
+ export declare const getDocumentCount: (state: CoreState) => number;
@@ -14,8 +14,8 @@ export interface BasePluginConfig {
14
14
  enabled?: boolean;
15
15
  }
16
16
  export interface PluginRegistryConfig {
17
- rotation?: Rotation;
18
- scale?: number;
17
+ defaultRotation?: Rotation;
18
+ defaultScale?: number;
19
19
  logger?: Logger;
20
20
  }
21
21
  export interface PluginManifest<TConfig = unknown> {
@@ -9,15 +9,50 @@ export interface ThrottleOptions extends BaseEventControlOptions {
9
9
  mode: 'throttle';
10
10
  throttleMode?: 'leading-trailing' | 'trailing';
11
11
  }
12
- export type EventControlOptions = DebounceOptions | ThrottleOptions;
12
+ export interface KeyedDebounceOptions<T> extends BaseEventControlOptions {
13
+ mode: 'debounce';
14
+ keyExtractor: (data: T) => string | number;
15
+ }
16
+ export interface KeyedThrottleOptions<T> extends BaseEventControlOptions {
17
+ mode: 'throttle';
18
+ throttleMode?: 'leading-trailing' | 'trailing';
19
+ keyExtractor: (data: T) => string | number;
20
+ }
21
+ export type EventControlOptions<T = any> = DebounceOptions | ThrottleOptions | KeyedDebounceOptions<T> | KeyedThrottleOptions<T>;
13
22
  export declare class EventControl<T> {
14
23
  private handler;
15
24
  private options;
16
25
  private timeoutId?;
17
26
  private lastRun;
18
- constructor(handler: EventHandler<T>, options: EventControlOptions);
27
+ constructor(handler: EventHandler<T>, options: DebounceOptions | ThrottleOptions);
19
28
  handle: (data: T) => void;
20
29
  private debounce;
21
30
  private throttle;
22
31
  destroy(): void;
23
32
  }
33
+ /**
34
+ * Event control with independent debouncing/throttling per key.
35
+ * Useful when events carry a discriminator (like documentId) and
36
+ * you want to debounce/throttle each key's events independently.
37
+ *
38
+ * @example
39
+ * // Debounce viewport resize events independently per document
40
+ * const control = new KeyedEventControl(
41
+ * (event) => recalcZoom(event.documentId),
42
+ * { mode: 'debounce', wait: 150, keyExtractor: (e) => e.documentId }
43
+ * );
44
+ * control.handle(event); // Each documentId gets its own 150ms debounce
45
+ */
46
+ export declare class KeyedEventControl<T> {
47
+ private handler;
48
+ private options;
49
+ private controls;
50
+ private readonly baseOptions;
51
+ constructor(handler: EventHandler<T>, options: KeyedDebounceOptions<T> | KeyedThrottleOptions<T>);
52
+ handle: (data: T) => void;
53
+ destroy(): void;
54
+ }
55
+ /**
56
+ * Type guard to check if options are keyed
57
+ */
58
+ export declare function isKeyedOptions<T>(options: EventControlOptions<T>): options is KeyedDebounceOptions<T> | KeyedThrottleOptions<T>;
@@ -1,7 +1,7 @@
1
1
  import { EventControlOptions } from './event-control';
2
2
  export type Listener<T = any> = (value: T) => void;
3
3
  export type Unsubscribe = () => void;
4
- export type EventListener<T> = ((listener: Listener<T>) => Unsubscribe) | ((listener: Listener<T>, options?: EventControlOptions) => Unsubscribe);
4
+ export type EventListener<T> = ((listener: Listener<T>) => Unsubscribe) | ((listener: Listener<T>, options?: EventControlOptions<T>) => Unsubscribe);
5
5
  export type EventHook<T = any> = EventListener<T>;
6
6
  export interface Emitter<T = any> {
7
7
  emit(value?: T): void;
@@ -0,0 +1,71 @@
1
+ import { EventHook } from './eventing';
2
+ /**
3
+ * A scoped behavior emitter that maintains separate cached values
4
+ * and listener sets per scope key.
5
+ *
6
+ * @typeParam TData - The scoped data type (without key context)
7
+ * @typeParam TGlobalEvent - The global event type (includes key context)
8
+ * @typeParam TKey - The key type (string, number, or both)
9
+ */
10
+ export interface ScopedEmitter<TData = any, TGlobalEvent = {
11
+ key: string;
12
+ data: TData;
13
+ }, TKey extends string | number = string | number> {
14
+ /**
15
+ * Emit an event for a specific scope key.
16
+ */
17
+ emit(key: TKey, data: TData): void;
18
+ /**
19
+ * Get a scoped event hook that only receives events for this key.
20
+ */
21
+ forScope(key: TKey): EventHook<TData>;
22
+ /**
23
+ * Global event hook that receives events from all scopes.
24
+ */
25
+ readonly onGlobal: EventHook<TGlobalEvent>;
26
+ /**
27
+ * Clear all scopes' caches and listeners
28
+ */
29
+ clear(): void;
30
+ /**
31
+ * Clear a specific scope's cache and listeners
32
+ */
33
+ clearScope(key: TKey): void;
34
+ /**
35
+ * Get the current cached value for a specific scope
36
+ */
37
+ getValue(key: TKey): TData | undefined;
38
+ /**
39
+ * Get all active scope keys
40
+ */
41
+ getScopes(): TKey[];
42
+ }
43
+ /**
44
+ * Creates a scoped emitter with optional caching behavior.
45
+ *
46
+ * @param toGlobalEvent - Transform function to convert (key, data) into a global event
47
+ * @param options - Configuration options
48
+ * @param options.cache - Whether to cache values per scope (default: true)
49
+ * @param options.equality - Equality function for cached values (default: arePropsEqual)
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * // With caching (stateful) - default behavior
54
+ * const window$ = createScopedEmitter<WindowState, WindowChangeEvent, string>(
55
+ * (documentId, window) => ({ documentId, window })
56
+ * );
57
+ *
58
+ * // Without caching (transient events)
59
+ * const refreshPages$ = createScopedEmitter<number[], RefreshPagesEvent, string>(
60
+ * (documentId, pages) => ({ documentId, pages }),
61
+ * { cache: false }
62
+ * );
63
+ * ```
64
+ */
65
+ export declare function createScopedEmitter<TData = any, TGlobalEvent = {
66
+ key: string;
67
+ data: TData;
68
+ }, TKey extends string | number = string | number>(toGlobalEvent: (key: TKey, data: TData) => TGlobalEvent, options?: {
69
+ cache?: boolean;
70
+ equality?: (a: TData, b: TData) => boolean;
71
+ }): ScopedEmitter<TData, TGlobalEvent, TKey>;
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("preact"),t=require("preact/hooks"),r=require("preact/jsx-runtime"),i=require("@embedpdf/core"),n=e.createContext({registry:null,isInitializing:!0,pluginsReady:!1});function s({plugins:n,children:s}){const{utilities:o,wrappers:u}=t.useMemo((()=>{const e=[],t=[];for(const r of n){const n=r.package;if(i.hasAutoMountElements(n)){const r=n.autoMountElements()||[];for(const i of r)"utility"===i.type?e.push(i.component):"wrapper"===i.type&&t.push(i.component)}}return{utilities:e,wrappers:t}}),[n]),l=u.reduce(((e,t)=>r.jsx(t,{children:e})),s);return r.jsxs(e.Fragment,{children:[l,o.map(((e,t)=>r.jsx(e,{},`utility-${t}`)))]})}function o(){const e=t.useContext(n);if(void 0===e)throw new Error("useCapability must be used within a PDFContext.Provider");const{registry:r,isInitializing:i}=e;if(i)return e;if(null===r)throw new Error("PDF registry failed to initialize properly");return e}function u(e){const{registry:t}=o();if(null===t)return{plugin:null,isLoading:!0,ready:new Promise((()=>{}))};const r=t.getPlugin(e);if(!r)throw new Error(`Plugin ${e} not found`);return{plugin:r,isLoading:!1,ready:r.ready()}}exports.EmbedPDF=function({engine:e,logger:o,onInitialized:u,plugins:l,children:a,autoMountDomElements:c=!0}){const[g,p]=t.useState(null),[d,f]=t.useState(!0),[y,h]=t.useState(!1),x=t.useRef(u);t.useEffect((()=>{x.current=u}),[u]),t.useEffect((()=>{const t=new i.PluginRegistry(e,{logger:o});t.registerPluginBatch(l);return(async()=>{var e;await t.initialize(),t.isDestroyed()||(await(null==(e=x.current)?void 0:e.call(x,t)),t.isDestroyed()||(t.pluginsReady().then((()=>{t.isDestroyed()||h(!0)})),p(t),f(!1)))})().catch(console.error),()=>{t.destroy(),p(null),f(!0),h(!1)}}),[e,l]);const w="function"==typeof a?a({registry:g,isInitializing:d,pluginsReady:y}):a;return r.jsx(n.Provider,{value:{registry:g,isInitializing:d,pluginsReady:y},children:y&&c?r.jsx(s,{plugins:l,children:w}):w})},exports.PDFContext=n,exports.useCapability=function(e){const{plugin:t,isLoading:r,ready:i}=u(e);if(!t)return{provides:null,isLoading:r,ready:i};if(!t.provides)throw new Error(`Plugin ${e} does not provide a capability`);return{provides:t.provides(),isLoading:r,ready:i}},exports.useCoreState=function(){const{registry:e}=o(),[r,n]=t.useState(null);return t.useEffect((()=>{if(!e)return;const t=e.getStore();n(t.getState().core);const r=t.subscribe(((e,r,s)=>{t.isCoreAction(e)&&!i.arePropsEqual(r.core,s.core)&&n(r.core)}));return()=>r()}),[e]),r},exports.usePlugin=u,exports.useRegistry=o,exports.useStoreState=function(){const{registry:e}=o(),[r,i]=t.useState(null);return t.useEffect((()=>{if(!e)return;i(e.getStore().getState());const t=e.getStore().subscribe(((e,t)=>{i(t)}));return()=>t()}),[e]),r};
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("preact"),t=require("preact/hooks"),n=require("preact/jsx-runtime"),r=require("@embedpdf/core"),i=e.createContext({registry:null,coreState:null,isInitializing:!0,pluginsReady:!1,activeDocumentId:null,activeDocument:null,documents:{},documentStates:[]});function o({plugins:i,children:o}){const{utilities:u,wrappers:s}=t.useMemo(()=>{const e=[],t=[];for(const n of i){const i=n.package;if(r.hasAutoMountElements(i)){const n=i.autoMountElements()||[];for(const r of n)"utility"===r.type?e.push(r.component):"wrapper"===r.type&&t.push(r.component)}}return{utilities:e,wrappers:t}},[i]),l=s.reduce((e,t)=>n.jsx(t,{children:e}),o);return n.jsxs(e.Fragment,{children:[l,u.map((e,t)=>n.jsx(e,{},`utility-${t}`))]})}function u(){const e=t.useContext(i);if(void 0===e)throw new Error("useCapability must be used within a PDFContext.Provider");const{registry:n,isInitializing:r}=e;if(r)return e;if(null===n)throw new Error("PDF registry failed to initialize properly");return e}function s(e){const{registry:t}=u();if(null===t)return{plugin:null,isLoading:!0,ready:new Promise(()=>{})};const n=t.getPlugin(e);if(!n)throw new Error(`Plugin ${e} not found`);return{plugin:n,isLoading:!1,ready:n.ready()}}function l(){const{coreState:e}=t.useContext(i);return e}exports.EmbedPDF=function({engine:e,logger:u,onInitialized:s,plugins:l,children:c,autoMountDomElements:a=!0}){const[d,p]=t.useState(null),[g,f]=t.useState(null),[y,m]=t.useState(!0),[S,v]=t.useState(!1),x=t.useRef(s);t.useEffect(()=>{x.current=s},[s]),t.useEffect(()=>{const t=new r.PluginRegistry(e,{logger:u});t.registerPluginBatch(l);let n;return(async()=>{var e;if(await t.initialize(),t.isDestroyed())return;const n=t.getStore();f(n.getState().core);const r=n.subscribe((e,t,r)=>{n.isCoreAction(e)&&t.core!==r.core&&f(t.core)});if(await(null==(e=x.current)?void 0:e.call(x,t)),!t.isDestroyed())return t.pluginsReady().then(()=>{t.isDestroyed()||v(!0)}),p(t),m(!1),r;r()})().then(e=>{n=e}).catch(console.error),()=>{null==n||n(),t.destroy(),p(null),f(null),m(!0),v(!1)}},[e,l]);const h=t.useMemo(()=>{const e=(null==g?void 0:g.activeDocumentId)??null,t=(null==g?void 0:g.documents)??{},n=(null==g?void 0:g.documentOrder)??[],r=e&&t[e]?t[e]:null,i=n.map(e=>t[e]).filter(e=>null!=e);return{registry:d,coreState:g,isInitializing:y,pluginsReady:S,activeDocumentId:e,activeDocument:r,documents:t,documentStates:i}},[d,g,y,S]),w="function"==typeof c?c(h):c;return n.jsx(i.Provider,{value:h,children:S&&a?n.jsx(o,{plugins:l,children:w}):w})},exports.PDFContext=i,exports.useCapability=function(e){const{plugin:t,isLoading:n,ready:r}=s(e);if(!t)return{provides:null,isLoading:n,ready:r};if(!t.provides)throw new Error(`Plugin ${e} does not provide a capability`);return{provides:t.provides(),isLoading:n,ready:r}},exports.useCoreState=l,exports.useDocumentState=function(e){const n=l();return t.useMemo(()=>n&&e?n.documents[e]??null:null,[n,e])},exports.usePlugin=s,exports.useRegistry=u,exports.useStoreState=function(){const{registry:e}=u(),[n,r]=t.useState(null);return t.useEffect(()=>{if(!e)return;r(e.getStore().getState());const t=e.getStore().subscribe((e,t)=>{r(t)});return()=>t()},[e]),n};
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/shared/context.ts","../../src/shared/components/auto-mount.tsx","../../src/shared/hooks/use-registry.ts","../../src/shared/hooks/use-plugin.ts","../../src/shared/components/embed-pdf.tsx","../../src/shared/hooks/use-capability.ts","../../src/shared/hooks/use-core-state.ts","../../src/shared/hooks/use-store-state.ts"],"sourcesContent":["import { createContext } from '@framework';\nimport type { PluginRegistry } from '@embedpdf/core';\n\nexport interface PDFContextState {\n registry: PluginRegistry | null;\n isInitializing: boolean;\n pluginsReady: boolean;\n}\n\nexport const PDFContext = createContext<PDFContextState>({\n registry: null,\n isInitializing: true,\n pluginsReady: false,\n});\n","import { Fragment, useMemo, ComponentType, ReactNode } from '@framework';\nimport { hasAutoMountElements } from '@embedpdf/core';\nimport type { PluginBatchRegistration, IPlugin } from '@embedpdf/core';\n\ninterface AutoMountProps {\n plugins: PluginBatchRegistration<IPlugin<any>, any>[];\n children: ReactNode;\n}\n\nexport function AutoMount({ plugins, children }: AutoMountProps) {\n const { utilities, wrappers } = useMemo(() => {\n // React-specific types for internal use\n const utilities: ComponentType[] = [];\n const wrappers: ComponentType<{ children: ReactNode }>[] = [];\n\n for (const reg of plugins) {\n const pkg = reg.package;\n if (hasAutoMountElements(pkg)) {\n const elements = pkg.autoMountElements() || [];\n\n for (const element of elements) {\n if (element.type === 'utility') {\n utilities.push(element.component);\n } else if (element.type === 'wrapper') {\n // In React context, we know wrappers need children\n wrappers.push(element.component);\n }\n }\n }\n }\n return { utilities, wrappers };\n }, [plugins]);\n\n // React-specific wrapping logic\n const wrappedContent = wrappers.reduce(\n (content, Wrapper) => <Wrapper>{content}</Wrapper>,\n children,\n );\n\n return (\n <Fragment>\n {wrappedContent}\n {utilities.map((Utility, i) => (\n <Utility key={`utility-${i}`} />\n ))}\n </Fragment>\n );\n}\n","import { useContext } from '@framework';\nimport { PDFContext, PDFContextState } from '../context';\n\n/**\n * Hook to access the PDF registry.\n * @returns The PDF registry or null during initialization\n */\nexport function useRegistry(): PDFContextState {\n const contextValue = useContext(PDFContext);\n\n // Error if used outside of context\n if (contextValue === undefined) {\n throw new Error('useCapability must be used within a PDFContext.Provider');\n }\n\n const { registry, isInitializing } = contextValue;\n\n // During initialization, return null instead of throwing an error\n if (isInitializing) {\n return contextValue;\n }\n\n // At this point, initialization is complete but registry is still null, which is unexpected\n if (registry === null) {\n throw new Error('PDF registry failed to initialize properly');\n }\n\n return contextValue;\n}\n","import type { BasePlugin } from '@embedpdf/core';\nimport { useRegistry } from './use-registry';\n\ntype PluginState<T extends BasePlugin> = {\n plugin: T | null;\n isLoading: boolean;\n ready: Promise<void>;\n};\n\n/**\n * Hook to access a plugin.\n * @param pluginId The ID of the plugin to access\n * @returns The plugin or null during initialization\n * @example\n * // Get zoom plugin\n * const zoom = usePlugin<ZoomPlugin>(ZoomPlugin.id);\n */\nexport function usePlugin<T extends BasePlugin>(pluginId: T['id']): PluginState<T> {\n const { registry } = useRegistry();\n\n if (registry === null) {\n return {\n plugin: null,\n isLoading: true,\n ready: new Promise(() => {}),\n };\n }\n\n const plugin = registry.getPlugin<T>(pluginId);\n\n if (!plugin) {\n throw new Error(`Plugin ${pluginId} not found`);\n }\n\n return {\n plugin,\n isLoading: false,\n ready: plugin.ready(),\n };\n}\n","import { useState, useEffect, useRef, ReactNode } from '@framework';\nimport { Logger, PdfEngine } from '@embedpdf/models';\nimport { PluginRegistry } from '@embedpdf/core';\nimport type { PluginBatchRegistrations } from '@embedpdf/core';\n\nimport { PDFContext, PDFContextState } from '../context';\nimport { AutoMount } from './auto-mount';\n\nexport type { PluginBatchRegistrations };\n\ninterface EmbedPDFProps {\n /**\n * The PDF engine to use for the PDF viewer.\n */\n engine: PdfEngine;\n /**\n * The logger to use for the PDF viewer.\n */\n logger?: Logger;\n /**\n * The callback to call when the PDF viewer is initialized.\n */\n onInitialized?: (registry: PluginRegistry) => Promise<void>;\n /**\n * The plugins to use for the PDF viewer.\n */\n plugins: PluginBatchRegistrations;\n /**\n * The children to render for the PDF viewer.\n */\n children: ReactNode | ((state: PDFContextState) => ReactNode);\n /**\n * Whether to auto-mount specific non-visual DOM elements from plugins.\n * @default true\n */\n autoMountDomElements?: boolean;\n}\n\nexport function EmbedPDF({\n engine,\n logger,\n onInitialized,\n plugins,\n children,\n autoMountDomElements = true,\n}: EmbedPDFProps) {\n const [registry, setRegistry] = useState<PluginRegistry | null>(null);\n const [isInitializing, setIsInitializing] = useState<boolean>(true);\n const [pluginsReady, setPluginsReady] = useState<boolean>(false);\n const initRef = useRef<EmbedPDFProps['onInitialized']>(onInitialized);\n\n useEffect(() => {\n initRef.current = onInitialized; // update without triggering re-runs\n }, [onInitialized]);\n\n useEffect(() => {\n const pdfViewer = new PluginRegistry(engine, { logger });\n pdfViewer.registerPluginBatch(plugins);\n\n const initialize = async () => {\n await pdfViewer.initialize();\n // if the registry is destroyed, don't do anything\n if (pdfViewer.isDestroyed()) {\n return;\n }\n\n /* always call the *latest* callback */\n await initRef.current?.(pdfViewer);\n\n // if the registry is destroyed, don't do anything\n if (pdfViewer.isDestroyed()) {\n return;\n }\n\n pdfViewer.pluginsReady().then(() => {\n if (!pdfViewer.isDestroyed()) {\n setPluginsReady(true);\n }\n });\n\n // Provide the registry to children via context\n setRegistry(pdfViewer);\n setIsInitializing(false);\n };\n\n initialize().catch(console.error);\n\n return () => {\n pdfViewer.destroy();\n setRegistry(null);\n setIsInitializing(true);\n setPluginsReady(false);\n };\n }, [engine, plugins]);\n\n const content =\n typeof children === 'function'\n ? children({ registry, isInitializing, pluginsReady })\n : children;\n\n return (\n <PDFContext.Provider value={{ registry, isInitializing, pluginsReady }}>\n {pluginsReady && autoMountDomElements ? (\n <AutoMount plugins={plugins}>{content}</AutoMount>\n ) : (\n content\n )}\n </PDFContext.Provider>\n );\n}\n","import type { BasePlugin } from '@embedpdf/core';\nimport { usePlugin } from './use-plugin';\n\ntype CapabilityState<T extends BasePlugin> = {\n provides: ReturnType<NonNullable<T['provides']>> | null;\n isLoading: boolean;\n ready: Promise<void>;\n};\n\n/**\n * Hook to access a plugin's capability.\n * @param pluginId The ID of the plugin to access\n * @returns The capability provided by the plugin or null during initialization\n * @example\n * // Get zoom capability\n * const zoom = useCapability<ZoomPlugin>(ZoomPlugin.id);\n */\nexport function useCapability<T extends BasePlugin>(pluginId: T['id']): CapabilityState<T> {\n const { plugin, isLoading, ready } = usePlugin<T>(pluginId);\n\n if (!plugin) {\n return {\n provides: null,\n isLoading,\n ready,\n };\n }\n\n if (!plugin.provides) {\n throw new Error(`Plugin ${pluginId} does not provide a capability`);\n }\n\n return {\n provides: plugin.provides() as ReturnType<NonNullable<T['provides']>>,\n isLoading,\n ready,\n };\n}\n","import { useState, useEffect } from '@framework';\nimport { CoreState, arePropsEqual } from '@embedpdf/core';\nimport { useRegistry } from './use-registry';\n\n/**\n * Hook that provides access to the current core state\n * and re-renders the component only when the core state changes\n */\nexport function useCoreState(): CoreState | null {\n const { registry } = useRegistry();\n const [coreState, setCoreState] = useState<CoreState | null>(null);\n\n useEffect(() => {\n if (!registry) return;\n\n const store = registry.getStore();\n\n // Get initial core state\n setCoreState(store.getState().core);\n\n // Create a single subscription that handles all core actions\n const unsubscribe = store.subscribe((action, newState, oldState) => {\n // Only update if it's a core action and the core state changed\n if (store.isCoreAction(action) && !arePropsEqual(newState.core, oldState.core)) {\n setCoreState(newState.core);\n }\n });\n\n return () => unsubscribe();\n }, [registry]);\n\n return coreState;\n}\n","import { useState, useEffect } from '@framework';\nimport { CoreState, StoreState } from '@embedpdf/core';\nimport { useRegistry } from './use-registry';\n\n/**\n * Hook that provides access to the current global store state\n * and re-renders the component when the state changes\n */\nexport function useStoreState<T = CoreState>(): StoreState<T> | null {\n const { registry } = useRegistry();\n const [state, setState] = useState<StoreState<T> | null>(null);\n\n useEffect(() => {\n if (!registry) return;\n\n // Get initial state\n setState(registry.getStore().getState() as StoreState<T>);\n\n // Subscribe to store changes\n const unsubscribe = registry.getStore().subscribe((_action, newState) => {\n setState(newState as StoreState<T>);\n });\n\n return () => unsubscribe();\n }, [registry]);\n\n return state;\n}\n"],"names":["PDFContext","createContext","registry","isInitializing","pluginsReady","AutoMount","plugins","children","utilities","wrappers","useMemo","reg","pkg","package","hasAutoMountElements","elements","autoMountElements","element","type","utilities2","push","component","wrappers2","wrappedContent","reduce","content","Wrapper","jsx","Fragment","map","Utility","i","useRegistry","contextValue","useContext","Error","usePlugin","pluginId","plugin","isLoading","ready","Promise","getPlugin","engine","logger","onInitialized","autoMountDomElements","setRegistry","useState","setIsInitializing","setPluginsReady","initRef","useRef","useEffect","current","pdfViewer","PluginRegistry","registerPluginBatch","async","initialize","isDestroyed","_a","call","then","catch","console","error","destroy","Provider","value","provides","coreState","setCoreState","store","getStore","getState","core","unsubscribe","subscribe","action","newState","oldState","isCoreAction","arePropsEqual","state","setState","_action"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../src/shared/context.ts","../../src/shared/components/auto-mount.tsx","../../src/shared/hooks/use-registry.ts","../../src/shared/hooks/use-plugin.ts","../../src/shared/hooks/use-core-state.ts","../../src/shared/components/embed-pdf.tsx","../../src/shared/hooks/use-capability.ts","../../src/shared/hooks/use-document-state.ts","../../src/shared/hooks/use-store-state.ts"],"sourcesContent":["import { createContext } from '@framework';\nimport type { CoreState, DocumentState, PluginRegistry } from '@embedpdf/core';\n\nexport interface PDFContextState {\n registry: PluginRegistry | null;\n coreState: CoreState | null;\n isInitializing: boolean;\n pluginsReady: boolean;\n\n // Convenience accessors (always safe to use)\n activeDocumentId: string | null;\n activeDocument: DocumentState | null;\n documents: Record<string, DocumentState>;\n documentStates: DocumentState[];\n}\n\nexport const PDFContext = createContext<PDFContextState>({\n registry: null,\n coreState: null,\n isInitializing: true,\n pluginsReady: false,\n activeDocumentId: null,\n activeDocument: null,\n documents: {},\n documentStates: [],\n});\n","import { Fragment, useMemo, ComponentType, ReactNode } from '@framework';\nimport { hasAutoMountElements } from '@embedpdf/core';\nimport type { PluginBatchRegistration, IPlugin } from '@embedpdf/core';\n\ninterface AutoMountProps {\n plugins: PluginBatchRegistration<IPlugin<any>, any>[];\n children: ReactNode;\n}\n\nexport function AutoMount({ plugins, children }: AutoMountProps) {\n const { utilities, wrappers } = useMemo(() => {\n // React-specific types for internal use\n const utilities: ComponentType[] = [];\n const wrappers: ComponentType<{ children: ReactNode }>[] = [];\n\n for (const reg of plugins) {\n const pkg = reg.package;\n if (hasAutoMountElements(pkg)) {\n const elements = pkg.autoMountElements() || [];\n\n for (const element of elements) {\n if (element.type === 'utility') {\n utilities.push(element.component);\n } else if (element.type === 'wrapper') {\n // In React context, we know wrappers need children\n wrappers.push(element.component);\n }\n }\n }\n }\n return { utilities, wrappers };\n }, [plugins]);\n\n // React-specific wrapping logic\n const wrappedContent = wrappers.reduce(\n (content, Wrapper) => <Wrapper>{content}</Wrapper>,\n children,\n );\n\n return (\n <Fragment>\n {wrappedContent}\n {utilities.map((Utility, i) => (\n <Utility key={`utility-${i}`} />\n ))}\n </Fragment>\n );\n}\n","import { useContext } from '@framework';\nimport { PDFContext, PDFContextState } from '../context';\n\n/**\n * Hook to access the PDF registry.\n * @returns The PDF registry or null during initialization\n */\nexport function useRegistry(): PDFContextState {\n const contextValue = useContext(PDFContext);\n\n // Error if used outside of context\n if (contextValue === undefined) {\n throw new Error('useCapability must be used within a PDFContext.Provider');\n }\n\n const { registry, isInitializing } = contextValue;\n\n // During initialization, return null instead of throwing an error\n if (isInitializing) {\n return contextValue;\n }\n\n // At this point, initialization is complete but registry is still null, which is unexpected\n if (registry === null) {\n throw new Error('PDF registry failed to initialize properly');\n }\n\n return contextValue;\n}\n","import type { BasePlugin } from '@embedpdf/core';\nimport { useRegistry } from './use-registry';\n\ntype PluginState<T extends BasePlugin> = {\n plugin: T | null;\n isLoading: boolean;\n ready: Promise<void>;\n};\n\n/**\n * Hook to access a plugin.\n * @param pluginId The ID of the plugin to access\n * @returns The plugin or null during initialization\n * @example\n * // Get zoom plugin\n * const zoom = usePlugin<ZoomPlugin>(ZoomPlugin.id);\n */\nexport function usePlugin<T extends BasePlugin>(pluginId: T['id']): PluginState<T> {\n const { registry } = useRegistry();\n\n if (registry === null) {\n return {\n plugin: null,\n isLoading: true,\n ready: new Promise(() => {}),\n };\n }\n\n const plugin = registry.getPlugin<T>(pluginId);\n\n if (!plugin) {\n throw new Error(`Plugin ${pluginId} not found`);\n }\n\n return {\n plugin,\n isLoading: false,\n ready: plugin.ready(),\n };\n}\n","import { useContext } from '@framework';\nimport { CoreState } from '@embedpdf/core';\nimport { PDFContext } from '../context';\n\n/**\n * Hook that provides access to the current core state.\n *\n * Note: This reads from the context which is already subscribed to core state changes\n * in the EmbedPDF component, so there's no additional subscription overhead.\n */\nexport function useCoreState(): CoreState | null {\n const { coreState } = useContext(PDFContext);\n return coreState;\n}\n","import { useState, useEffect, useRef, useMemo, ReactNode } from '@framework';\nimport { Logger, PdfEngine } from '@embedpdf/models';\nimport { PluginRegistry, CoreState, DocumentState } from '@embedpdf/core';\nimport type { PluginBatchRegistrations } from '@embedpdf/core';\n\nimport { PDFContext, PDFContextState } from '../context';\nimport { AutoMount } from './auto-mount';\n\nexport type { PluginBatchRegistrations };\n\ninterface EmbedPDFProps {\n /**\n * The PDF engine to use for the PDF viewer.\n */\n engine: PdfEngine;\n /**\n * The logger to use for the PDF viewer.\n */\n logger?: Logger;\n /**\n * The callback to call when the PDF viewer is initialized.\n */\n onInitialized?: (registry: PluginRegistry) => Promise<void>;\n /**\n * The plugins to use for the PDF viewer.\n */\n plugins: PluginBatchRegistrations;\n /**\n * The children to render for the PDF viewer.\n */\n children: ReactNode | ((state: PDFContextState) => ReactNode);\n /**\n * Whether to auto-mount specific non-visual DOM elements from plugins.\n * @default true\n */\n autoMountDomElements?: boolean;\n}\n\nexport function EmbedPDF({\n engine,\n logger,\n onInitialized,\n plugins,\n children,\n autoMountDomElements = true,\n}: EmbedPDFProps) {\n const [registry, setRegistry] = useState<PluginRegistry | null>(null);\n const [coreState, setCoreState] = useState<CoreState | null>(null);\n const [isInitializing, setIsInitializing] = useState<boolean>(true);\n const [pluginsReady, setPluginsReady] = useState<boolean>(false);\n const initRef = useRef<EmbedPDFProps['onInitialized']>(onInitialized);\n\n useEffect(() => {\n initRef.current = onInitialized;\n }, [onInitialized]);\n\n useEffect(() => {\n const pdfViewer = new PluginRegistry(engine, { logger });\n pdfViewer.registerPluginBatch(plugins);\n\n const initialize = async () => {\n await pdfViewer.initialize();\n\n if (pdfViewer.isDestroyed()) {\n return;\n }\n\n const store = pdfViewer.getStore();\n setCoreState(store.getState().core);\n\n const unsubscribe = store.subscribe((action, newState, oldState) => {\n // Only update if it's a core action and the core state changed\n if (store.isCoreAction(action) && newState.core !== oldState.core) {\n setCoreState(newState.core);\n }\n });\n\n /* always call the *latest* callback */\n await initRef.current?.(pdfViewer);\n\n if (pdfViewer.isDestroyed()) {\n unsubscribe();\n return;\n }\n\n pdfViewer.pluginsReady().then(() => {\n if (!pdfViewer.isDestroyed()) {\n setPluginsReady(true);\n }\n });\n\n // Provide the registry to children via context\n setRegistry(pdfViewer);\n setIsInitializing(false);\n\n // Return cleanup function\n return unsubscribe;\n };\n\n let cleanup: (() => void) | undefined;\n initialize()\n .then((unsub) => {\n cleanup = unsub;\n })\n .catch(console.error);\n\n return () => {\n cleanup?.();\n pdfViewer.destroy();\n setRegistry(null);\n setCoreState(null);\n setIsInitializing(true);\n setPluginsReady(false);\n };\n }, [engine, plugins]);\n\n // Compute convenience accessors\n const contextValue: PDFContextState = useMemo(() => {\n const activeDocumentId = coreState?.activeDocumentId ?? null;\n const documents = coreState?.documents ?? {};\n const documentOrder = coreState?.documentOrder ?? [];\n\n // Compute active document\n const activeDocument =\n activeDocumentId && documents[activeDocumentId] ? documents[activeDocumentId] : null;\n\n // Compute open documents in order\n const documentStates = documentOrder\n .map((docId) => documents[docId])\n .filter((doc): doc is DocumentState => doc !== null && doc !== undefined);\n\n return {\n registry,\n coreState,\n isInitializing,\n pluginsReady,\n // Convenience accessors (always safe to use)\n activeDocumentId,\n activeDocument,\n documents,\n documentStates,\n };\n }, [registry, coreState, isInitializing, pluginsReady]);\n\n const content = typeof children === 'function' ? children(contextValue) : children;\n\n return (\n <PDFContext.Provider value={contextValue}>\n {pluginsReady && autoMountDomElements ? (\n <AutoMount plugins={plugins}>{content}</AutoMount>\n ) : (\n content\n )}\n </PDFContext.Provider>\n );\n}\n","import type { BasePlugin } from '@embedpdf/core';\nimport { usePlugin } from './use-plugin';\n\ntype CapabilityState<T extends BasePlugin> = {\n provides: ReturnType<NonNullable<T['provides']>> | null;\n isLoading: boolean;\n ready: Promise<void>;\n};\n\n/**\n * Hook to access a plugin's capability.\n * @param pluginId The ID of the plugin to access\n * @returns The capability provided by the plugin or null during initialization\n * @example\n * // Get zoom capability\n * const zoom = useCapability<ZoomPlugin>(ZoomPlugin.id);\n */\nexport function useCapability<T extends BasePlugin>(pluginId: T['id']): CapabilityState<T> {\n const { plugin, isLoading, ready } = usePlugin<T>(pluginId);\n\n if (!plugin) {\n return {\n provides: null,\n isLoading,\n ready,\n };\n }\n\n if (!plugin.provides) {\n throw new Error(`Plugin ${pluginId} does not provide a capability`);\n }\n\n return {\n provides: plugin.provides() as ReturnType<NonNullable<T['provides']>>,\n isLoading,\n ready,\n };\n}\n","import { useMemo } from '@framework';\nimport { DocumentState } from '@embedpdf/core';\nimport { useCoreState } from './use-core-state';\n\n/**\n * Hook that provides reactive access to a specific document's state from the core store.\n *\n * @param documentId The ID of the document to retrieve.\n * @returns The reactive DocumentState object or null if not found.\n */\nexport function useDocumentState(documentId: string | null): DocumentState | null {\n const coreState = useCoreState();\n\n const documentState = useMemo(() => {\n if (!coreState || !documentId) return null;\n return coreState.documents[documentId] ?? null;\n }, [coreState, documentId]);\n\n return documentState;\n}\n","import { useState, useEffect } from '@framework';\nimport { CoreState, StoreState } from '@embedpdf/core';\nimport { useRegistry } from './use-registry';\n\n/**\n * Hook that provides access to the current global store state\n * and re-renders the component when the state changes\n */\nexport function useStoreState<T = CoreState>(): StoreState<T> | null {\n const { registry } = useRegistry();\n const [state, setState] = useState<StoreState<T> | null>(null);\n\n useEffect(() => {\n if (!registry) return;\n\n // Get initial state\n setState(registry.getStore().getState() as StoreState<T>);\n\n // Subscribe to store changes\n const unsubscribe = registry.getStore().subscribe((_action, newState) => {\n setState(newState as StoreState<T>);\n });\n\n return () => unsubscribe();\n }, [registry]);\n\n return state;\n}\n"],"names":["PDFContext","createContext","registry","coreState","isInitializing","pluginsReady","activeDocumentId","activeDocument","documents","documentStates","AutoMount","plugins","children","utilities","wrappers","useMemo","reg","pkg","package","hasAutoMountElements","elements","autoMountElements","element","type","push","component","wrappedContent","reduce","content","Wrapper","jsx","Fragment","map","Utility","i","useRegistry","contextValue","useContext","Error","usePlugin","pluginId","plugin","isLoading","ready","Promise","getPlugin","useCoreState","engine","logger","onInitialized","autoMountDomElements","setRegistry","useState","setCoreState","setIsInitializing","setPluginsReady","initRef","useRef","useEffect","current","pdfViewer","PluginRegistry","registerPluginBatch","cleanup","async","initialize","isDestroyed","store","getStore","getState","core","unsubscribe","subscribe","action","newState","oldState","isCoreAction","_a","call","then","unsub","catch","console","error","destroy","documentOrder","docId","filter","doc","Provider","value","provides","documentId","state","setState","_action"],"mappings":"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"}