@dtducas/wh-forge-viewer 2.0.0-beta.1 → 3.0.0-beta.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.
- package/README.md +165 -356
- package/dist/index.d.ts +796 -218
- package/dist/index.esm.js +87751 -43667
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +87769 -43709
- package/dist/index.js.map +1 -1
- package/dist/package.json +7 -0
- package/package.json +11 -1
package/README.md
CHANGED
|
@@ -1,38 +1,16 @@
|
|
|
1
1
|
# wh-forge-viewer
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
React wrapper for Autodesk Forge Viewer v7 with an Edit2D-first markup system, custom viewer toolbar, multi-page document navigation, and utility APIs for export, scene building, and property database queries.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Highlights
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
### Markup Tools
|
|
16
|
-
|
|
17
|
-
- **12 Markup Tools** - Arrow, Rectangle, Circle, Cloud, Text, Freehand, Line, Polyline, Polycloud, Callout, Image/Stamp
|
|
18
|
-
- **SelectTool with Multi-Select** - Drag rectangle to select multiple markups
|
|
19
|
-
- **Property Panel** - Excalidraw-inspired UI for editing markup properties
|
|
20
|
-
- **Undo/Redo** - Full undo/redo support for all operations
|
|
21
|
-
- **Custom Stamps** - Insert images as stamps with drag-drop support
|
|
22
|
-
|
|
23
|
-
### Collaboration
|
|
24
|
-
|
|
25
|
-
- **Real-Time Sync** - Subscribe to markup changes for WebSocket broadcasting
|
|
26
|
-
- **User Presence** - Track and display remote user cursors
|
|
27
|
-
- **Persistence API** - Save/load markups with JSON or SVG formats
|
|
28
|
-
- **Page Change Events** - Handle markup data during page navigation
|
|
29
|
-
|
|
30
|
-
### Architecture
|
|
31
|
-
|
|
32
|
-
- **SOLID Principles** - Manager pattern with clear separation of concerns
|
|
33
|
-
- **Type Safety** - Full TypeScript definitions included
|
|
34
|
-
- **Event-Driven** - Decoupled communication via EventBus
|
|
35
|
-
- **Extensible** - Factory pattern for adding custom tools
|
|
7
|
+
- Edit2D-first markup architecture (`MarkupManager` + `Edit2DMarkupEngine`)
|
|
8
|
+
- Custom Forge toolbar (Pan, Document Browser, Download/Export combo, Pagination)
|
|
9
|
+
- Direct local/document loading for `pdf`, `dwf`, `dwfx`, `gltf`, `glb`
|
|
10
|
+
- Excalidraw-inspired property panel and right-side markup toolbar
|
|
11
|
+
- Event-driven communication via `EventBus`
|
|
12
|
+
- Optional custom geometry APIs (`OverlayManager`, `SceneBuilderService`)
|
|
13
|
+
- Optional property worker query API (`PropertyDatabaseService`)
|
|
36
14
|
|
|
37
15
|
## Installation
|
|
38
16
|
|
|
@@ -40,394 +18,225 @@ A professional Autodesk Forge Viewer component for React with advanced PDF suppo
|
|
|
40
18
|
npm install @dtducas/wh-forge-viewer
|
|
41
19
|
```
|
|
42
20
|
|
|
21
|
+
Peer dependencies:
|
|
22
|
+
|
|
23
|
+
- `react` `^17 || ^18`
|
|
24
|
+
- `antd` `^5`
|
|
25
|
+
|
|
43
26
|
## Quick Start
|
|
44
27
|
|
|
45
28
|
### Basic Viewer
|
|
46
29
|
|
|
47
30
|
```jsx
|
|
31
|
+
import { useState } from 'react';
|
|
48
32
|
import { ViewerForgePDF } from '@dtducas/wh-forge-viewer';
|
|
49
33
|
|
|
50
|
-
function App() {
|
|
34
|
+
export default function App() {
|
|
35
|
+
const [viewer, setViewer] = useState(null);
|
|
36
|
+
|
|
51
37
|
return (
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
38
|
+
<div style={{ width: '100%', height: '100vh' }}>
|
|
39
|
+
<ViewerForgePDF
|
|
40
|
+
filePath='https://example.com/document.pdf'
|
|
41
|
+
fileExt='pdf'
|
|
42
|
+
setViewer={setViewer}
|
|
43
|
+
/>
|
|
44
|
+
</div>
|
|
57
45
|
);
|
|
58
46
|
}
|
|
59
47
|
```
|
|
60
48
|
|
|
61
|
-
###
|
|
49
|
+
### Enable Edit2D Markup
|
|
62
50
|
|
|
63
51
|
```jsx
|
|
64
|
-
import {
|
|
65
|
-
|
|
66
|
-
DrawingManager,
|
|
67
|
-
MarkupToolbar,
|
|
68
|
-
PropertyPanel,
|
|
69
|
-
EventBus,
|
|
70
|
-
} from '@dtducas/wh-forge-viewer';
|
|
71
|
-
|
|
72
|
-
function MarkupViewer() {
|
|
73
|
-
const [viewer, setViewer] = useState(null);
|
|
74
|
-
const [drawingManager, setDrawingManager] = useState(null);
|
|
75
|
-
const [activeTool, setActiveTool] = useState(null);
|
|
76
|
-
const [propertyConfig, setPropertyConfig] = useState(null);
|
|
77
|
-
const [propertyValues, setPropertyValues] = useState({});
|
|
78
|
-
|
|
79
|
-
const eventBus = EventBus.getInstance();
|
|
80
|
-
|
|
81
|
-
useEffect(() => {
|
|
82
|
-
if (!viewer) return;
|
|
83
|
-
|
|
84
|
-
const manager = new DrawingManager(viewer, eventBus, {
|
|
85
|
-
autoEnterEditMode: true,
|
|
86
|
-
defaultTool: 'arrow',
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
setDrawingManager(manager);
|
|
90
|
-
|
|
91
|
-
return () => manager.destroy();
|
|
92
|
-
}, [viewer]);
|
|
52
|
+
import { useState } from 'react';
|
|
53
|
+
import { ViewerForgePDF } from '@dtducas/wh-forge-viewer';
|
|
93
54
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
setActiveTool(tool);
|
|
97
|
-
setPropertyConfig(drawingManager?.getCurrentToolPropertyConfig());
|
|
98
|
-
setPropertyValues(drawingManager?.getCurrentToolPropertyValues() || {});
|
|
99
|
-
};
|
|
55
|
+
export default function MarkupApp() {
|
|
56
|
+
const [viewer, setViewer] = useState(null);
|
|
100
57
|
|
|
101
58
|
return (
|
|
102
|
-
<div style={{
|
|
59
|
+
<div style={{ width: '100%', height: '100vh' }}>
|
|
103
60
|
<ViewerForgePDF
|
|
104
|
-
filePath='/
|
|
61
|
+
filePath='/sample.pdf'
|
|
105
62
|
fileExt='pdf'
|
|
106
|
-
|
|
63
|
+
enableEdit2D
|
|
64
|
+
edit2dConfig={{ enableArcs: true }}
|
|
65
|
+
setViewer={setViewer}
|
|
107
66
|
/>
|
|
108
|
-
|
|
109
|
-
{drawingManager && (
|
|
110
|
-
<>
|
|
111
|
-
<MarkupToolbar
|
|
112
|
-
activeTool={activeTool}
|
|
113
|
-
onToolChange={handleToolChange}
|
|
114
|
-
onUndo={() => drawingManager.undo()}
|
|
115
|
-
onRedo={() => drawingManager.redo()}
|
|
116
|
-
/>
|
|
117
|
-
|
|
118
|
-
<PropertyPanel
|
|
119
|
-
config={propertyConfig}
|
|
120
|
-
values={propertyValues}
|
|
121
|
-
onChange={(key, value) => {
|
|
122
|
-
drawingManager.handlePropertyChange(key, value);
|
|
123
|
-
setPropertyValues(drawingManager.getCurrentToolPropertyValues());
|
|
124
|
-
}}
|
|
125
|
-
/>
|
|
126
|
-
</>
|
|
127
|
-
)}
|
|
128
67
|
</div>
|
|
129
68
|
);
|
|
130
69
|
}
|
|
131
70
|
```
|
|
132
71
|
|
|
133
|
-
|
|
72
|
+
When markup is enabled, `ViewerForgePDF` initializes `MarkupManager`, enters edit mode, and shows the built-in `MarkupToolbar` + `PropertyPanel`.
|
|
134
73
|
|
|
135
|
-
|
|
136
|
-
import {
|
|
137
|
-
DrawingManager,
|
|
138
|
-
EventBus,
|
|
139
|
-
CursorOverlay,
|
|
140
|
-
} from '@dtducas/wh-forge-viewer';
|
|
74
|
+
### Save/Load Markup Per Page
|
|
141
75
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
76
|
+
```jsx
|
|
77
|
+
import { useEffect } from 'react';
|
|
78
|
+
import { EventBus, EVENT_NAMES } from '@dtducas/wh-forge-viewer';
|
|
145
79
|
|
|
80
|
+
function useMarkupPersistence(viewer) {
|
|
146
81
|
useEffect(() => {
|
|
147
|
-
if (!
|
|
82
|
+
if (!viewer?.getMarkupData || !viewer?.loadMarkupData) return;
|
|
148
83
|
|
|
149
|
-
|
|
150
|
-
const unsubscribe = drawingManager.onMarkupChange((delta) => {
|
|
151
|
-
socket.emit('markup:delta', delta);
|
|
152
|
-
});
|
|
84
|
+
const eventBus = EventBus.getInstance();
|
|
153
85
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
86
|
+
const onPageChanging = async ({ fromViewable }) => {
|
|
87
|
+
const guid = fromViewable?.data?.guid ?? fromViewable?.guid;
|
|
88
|
+
if (!guid) return;
|
|
89
|
+
const data = viewer.getMarkupData(guid);
|
|
90
|
+
await saveToServer(guid, data);
|
|
91
|
+
};
|
|
158
92
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
93
|
+
const onPageChanged = async ({ viewable }) => {
|
|
94
|
+
const guid = viewable?.data?.guid ?? viewable?.guid;
|
|
95
|
+
if (!guid) return;
|
|
96
|
+
const data = await loadFromServer(guid);
|
|
97
|
+
if (data) {
|
|
98
|
+
await viewer.loadMarkupData(data, { mode: 'replace' });
|
|
99
|
+
}
|
|
100
|
+
};
|
|
163
101
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
drawingManager.updateUserCursor(cursor);
|
|
167
|
-
setRemoteCursors(new Map(drawingManager.getRemoteCursors()));
|
|
168
|
-
});
|
|
102
|
+
eventBus.on(EVENT_NAMES.PAGE_CHANGING, onPageChanging);
|
|
103
|
+
eventBus.on(EVENT_NAMES.PAGE_CHANGED, onPageChanged);
|
|
169
104
|
|
|
170
105
|
return () => {
|
|
171
|
-
|
|
172
|
-
|
|
106
|
+
eventBus.off(EVENT_NAMES.PAGE_CHANGING, onPageChanging);
|
|
107
|
+
eventBus.off(EVENT_NAMES.PAGE_CHANGED, onPageChanged);
|
|
173
108
|
};
|
|
174
|
-
}, [
|
|
175
|
-
|
|
176
|
-
return (
|
|
177
|
-
<>
|
|
178
|
-
{/* ... viewer components ... */}
|
|
179
|
-
<CursorOverlay cursors={remoteCursors} />
|
|
180
|
-
</>
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
### Persistence
|
|
186
|
-
|
|
187
|
-
```javascript
|
|
188
|
-
// Save to localStorage
|
|
189
|
-
const data = drawingManager.getMarkupData();
|
|
190
|
-
localStorage.setItem('markups', JSON.stringify(data));
|
|
191
|
-
|
|
192
|
-
// Load from localStorage
|
|
193
|
-
const saved = JSON.parse(localStorage.getItem('markups'));
|
|
194
|
-
await drawingManager.loadMarkupData(saved, { mode: 'replace' });
|
|
195
|
-
|
|
196
|
-
// Save to database (JSON format)
|
|
197
|
-
const jsonData = drawingManager.getMarkupDataAsJson('page-guid-123');
|
|
198
|
-
await db.collection('markups').doc('page-guid-123').set(jsonData);
|
|
199
|
-
|
|
200
|
-
// Load from database
|
|
201
|
-
const doc = await db.collection('markups').doc('page-guid-123').get();
|
|
202
|
-
await drawingManager.loadMarkupDataFromJson(doc.data());
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
### PDF Export with Markup
|
|
206
|
-
|
|
207
|
-
```javascript
|
|
208
|
-
// Export current page with markup as vector overlay
|
|
209
|
-
const result = await drawingManager.exportCurrentPagePDF(filePath, 0);
|
|
210
|
-
if (result.success && result.blob) {
|
|
211
|
-
const url = URL.createObjectURL(result.blob);
|
|
212
|
-
window.open(url); // Preview in new tab
|
|
109
|
+
}, [viewer]);
|
|
213
110
|
}
|
|
214
|
-
|
|
215
|
-
// Multi-page export
|
|
216
|
-
const markupsByPage = [
|
|
217
|
-
{ pageIndex: 0, svgString: savedPage0Svg },
|
|
218
|
-
{ pageIndex: 1, svgString: drawingManager.getMarkupSVG() }, // current page
|
|
219
|
-
];
|
|
220
|
-
await drawingManager.exportAndDownloadPDF(filePath, markupsByPage, {
|
|
221
|
-
filename: 'annotated-document',
|
|
222
|
-
});
|
|
223
111
|
```
|
|
224
112
|
|
|
225
|
-
##
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
|
232
|
-
|
|
|
233
|
-
| `
|
|
234
|
-
| `
|
|
235
|
-
| `
|
|
236
|
-
| `
|
|
237
|
-
| `
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
113
|
+
## `ViewerForgePDF` Props
|
|
114
|
+
|
|
115
|
+
| Prop | Type | Default | Description |
|
|
116
|
+
| --- | --- | --- | --- |
|
|
117
|
+
| `filePath` | `string` | required | URL/path to input document |
|
|
118
|
+
| `fileExt` | `'pdf' \| 'dwf' \| 'dwfx' \| 'gltf' \| 'glb'` | required | File extension |
|
|
119
|
+
| `setViewer` | `(viewer) => void` | `undefined` | Receives enhanced viewer instance after load |
|
|
120
|
+
| `enableCustomMarkups` | `boolean` | `false` | Enables markup mode (compat flag) |
|
|
121
|
+
| `enableEdit2D` | `boolean` | `false` | Enables Edit2D markup mode |
|
|
122
|
+
| `markupEngine` | `unknown` | ignored | Kept only for API compatibility |
|
|
123
|
+
| `markupConfig` | `unknown` | ignored | Kept only for API compatibility |
|
|
124
|
+
| `edit2dConfig` | `object` | `{ enableArcs: true }` | Forwarded to `MarkupManager` |
|
|
125
|
+
| `toolbarOptions` | `IToolbarOptions` | all visible | Show/hide custom toolbar controls |
|
|
126
|
+
| `enableSceneBuilder` | `boolean` | `false` | Initializes `SceneBuilderService` |
|
|
127
|
+
| `loadFilter` | `ILoadFilter` | `undefined` | For SVF2 selective loading workflows; Local env currently warns/ignores |
|
|
128
|
+
| `viewerPreset` | `IViewerPresetSettings` | `undefined` | Applies Autodesk profile settings |
|
|
129
|
+
|
|
130
|
+
`toolbarOptions` keys:
|
|
131
|
+
|
|
132
|
+
- `showToolbar`
|
|
133
|
+
- `showPan`
|
|
134
|
+
- `showDocBrowser`
|
|
135
|
+
- `showDownload`
|
|
136
|
+
- `showPagination`
|
|
137
|
+
|
|
138
|
+
For `gltf/glb`, Document Browser and Pagination are auto-disabled.
|
|
139
|
+
|
|
140
|
+
## Enhanced Viewer API (`setViewer`)
|
|
141
|
+
|
|
142
|
+
`setViewer` receives the Forge viewer instance with extra fields:
|
|
143
|
+
|
|
144
|
+
- `capabilities`
|
|
145
|
+
- `markupEngine` (when markup enabled)
|
|
146
|
+
- `getMarkupData(viewableGUID)`
|
|
147
|
+
- `loadMarkupData(data, options)`
|
|
148
|
+
- `onMarkupChange(callback)`
|
|
149
|
+
- `applyDelta(delta)`
|
|
150
|
+
- `overlay` (`OverlayManager`)
|
|
151
|
+
- `sceneBuilder` (`SceneBuilderService`, when enabled)
|
|
152
|
+
- `propertyDatabase` (`PropertyDatabaseService`)
|
|
153
|
+
- `queryPropertyDatabase(fn)`
|
|
154
|
+
- `presetManager` (`ViewerPresetManager`)
|
|
253
155
|
|
|
254
|
-
|
|
255
|
-
| ---------- | ------------------- | ------------------------ |
|
|
256
|
-
| `config` | IToolPropertyConfig | Property configuration |
|
|
257
|
-
| `values` | object | Current property values |
|
|
258
|
-
| `onChange` | function | Property change callback |
|
|
259
|
-
| `onAction` | function | Action button callback |
|
|
260
|
-
|
|
261
|
-
### CursorOverlay
|
|
262
|
-
|
|
263
|
-
Overlay for displaying remote cursors.
|
|
264
|
-
|
|
265
|
-
| Prop | Type | Description |
|
|
266
|
-
| -------------- | ------- | ----------------------------- |
|
|
267
|
-
| `cursors` | Map | Remote user cursors |
|
|
268
|
-
| `viewerBounds` | DOMRect | Viewer bounds for positioning |
|
|
269
|
-
|
|
270
|
-
## DrawingManager API
|
|
271
|
-
|
|
272
|
-
### Edit Mode
|
|
273
|
-
|
|
274
|
-
```typescript
|
|
275
|
-
await drawingManager.enterEditMode(); // Enter markup mode
|
|
276
|
-
drawingManager.leaveEditMode(); // Exit markup mode
|
|
277
|
-
drawingManager.isEditMode(); // Check if in edit mode
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
### Tools
|
|
281
|
-
|
|
282
|
-
```typescript
|
|
283
|
-
drawingManager.setActiveTool('arrow'); // Set active tool
|
|
284
|
-
drawingManager.getActiveTool(); // Get current tool
|
|
285
|
-
drawingManager.setStrokeStyle('#e03131', 2, 1.0); // Set stroke
|
|
286
|
-
drawingManager.setFillStyle('#228be6', 0.3); // Set fill
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
**Available Tools:**
|
|
290
|
-
`select`, `arrow`, `rectangle`, `ellipse`, `cloud`, `label`, `freehand`, `line`, `polyline`, `polycloud`, `callout`, `image`
|
|
291
|
-
|
|
292
|
-
### Markup Operations
|
|
293
|
-
|
|
294
|
-
```typescript
|
|
295
|
-
drawingManager.saveMarkups(); // Save current markups
|
|
296
|
-
drawingManager.loadMarkups(data); // Load markups
|
|
297
|
-
drawingManager.clearMarkups(); // Clear all markups
|
|
298
|
-
drawingManager.exportToSVG(); // Export to SVG string
|
|
299
|
-
drawingManager.undo(); // Undo last action
|
|
300
|
-
drawingManager.redo(); // Redo last undone action
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
### Collaborative API
|
|
304
|
-
|
|
305
|
-
```typescript
|
|
306
|
-
// Get markup data for persistence
|
|
307
|
-
const data = drawingManager.getMarkupData(viewableGUID);
|
|
308
|
-
|
|
309
|
-
// Load markup data
|
|
310
|
-
await drawingManager.loadMarkupData(data, { mode: 'replace' });
|
|
311
|
-
|
|
312
|
-
// Subscribe to changes
|
|
313
|
-
const unsubscribe = drawingManager.onMarkupChange((delta) => {
|
|
314
|
-
// delta: { type: 'create'|'update'|'delete', markupId, data, timestamp }
|
|
315
|
-
socket.emit('markup:delta', delta);
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
// Apply remote changes
|
|
319
|
-
drawingManager.applyDelta(delta);
|
|
320
|
-
|
|
321
|
-
// Batch apply (for initial load)
|
|
322
|
-
drawingManager.applyBatch(elements, true);
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
### User Presence
|
|
326
|
-
|
|
327
|
-
```typescript
|
|
328
|
-
// Broadcast local cursor
|
|
329
|
-
const unsub = drawingManager.onLocalCursorMove((position) => {
|
|
330
|
-
socket.emit('cursor:move', { userId, position });
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
// Update remote cursor
|
|
334
|
-
drawingManager.updateUserCursor({ userId, userName, color, position });
|
|
335
|
-
|
|
336
|
-
// Remove cursor when user leaves
|
|
337
|
-
drawingManager.removeUserCursor(userId);
|
|
156
|
+
## EventBus Events
|
|
338
157
|
|
|
339
|
-
|
|
340
|
-
const cursors = drawingManager.getRemoteCursors();
|
|
341
|
-
```
|
|
158
|
+
Core custom events exposed in `EVENT_NAMES`:
|
|
342
159
|
|
|
343
|
-
|
|
160
|
+
- `PAGE_CHANGING`
|
|
161
|
+
- `PAGE_CHANGED`
|
|
162
|
+
- `VIEWABLES_SET`
|
|
163
|
+
- `DOC_BROWSER_OPENED`
|
|
164
|
+
- `DOC_BROWSER_CLOSED`
|
|
165
|
+
- `DRAWING_TOOL_CHANGED`
|
|
166
|
+
- `DRAWING_PROPERTIES_SYNCED`
|
|
167
|
+
- `MARKUP_CHANGED`
|
|
168
|
+
- `MARKUP_DOCUMENT_LOADED`
|
|
344
169
|
|
|
345
|
-
|
|
346
|
-
| -------------------------- | ------------------------ | ---------------------- |
|
|
347
|
-
| `page:changing` | `{ fromIndex, toIndex }` | Before page navigation |
|
|
348
|
-
| `page:changed` | `{ index, viewable }` | After page navigation |
|
|
349
|
-
| `drawing:editModeEntered` | `{}` | Entered markup mode |
|
|
350
|
-
| `drawing:editModeExited` | `{}` | Exited markup mode |
|
|
351
|
-
| `drawing:toolChanged` | `{ tool }` | Tool changed |
|
|
352
|
-
| `drawing:propertiesSynced` | `{ values }` | Properties updated |
|
|
170
|
+
Example:
|
|
353
171
|
|
|
354
|
-
```
|
|
172
|
+
```ts
|
|
355
173
|
import { EventBus, EVENT_NAMES } from '@dtducas/wh-forge-viewer';
|
|
356
174
|
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
console.log('Now on page:', index);
|
|
175
|
+
const bus = EventBus.getInstance();
|
|
176
|
+
bus.on(EVENT_NAMES.PAGE_CHANGED, ({ index }) => {
|
|
177
|
+
console.log('Current page index:', index);
|
|
361
178
|
});
|
|
362
179
|
```
|
|
363
180
|
|
|
364
|
-
##
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
-
|
|
371
|
-
-
|
|
372
|
-
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
181
|
+
## Public Exports (Runtime)
|
|
182
|
+
|
|
183
|
+
Main entry exports from `src/index.js` include:
|
|
184
|
+
|
|
185
|
+
- Components: `ViewerForgePDF`, `MarkupToolbar`, `PropertyPanel`, `CursorOverlay`
|
|
186
|
+
- Markup engine/core: `Edit2DMarkupEngine`, `MarkupManager`, `HistoryManager`, `LayerManager`, `SelectionManager`, `SerializationManager`, `PropertyBridge`, `ContextMenuManager`
|
|
187
|
+
- Edit2D adapters: `Edit2DBootstrap`, `ShapeFactory`
|
|
188
|
+
- Services: `EventBus`, `MarkupExportService`, `SvgToPdfConverter`, `ImageStorageService`, `Edit2DSerializationService`, `Edit2DImportExportService`, `Edit2DStyleService`, `Edit2DLabelService`, `SceneBuilderService`, `PropertyDatabaseService`
|
|
189
|
+
- Managers: `OverlayManager`, `ViewerPresetManager`
|
|
190
|
+
- Constants and types: drawing presets, markup model types, toolbar visibility types, selective loading types, edit2d types
|
|
191
|
+
|
|
192
|
+
## Current Source Structure
|
|
193
|
+
|
|
194
|
+
```text
|
|
195
|
+
src/
|
|
196
|
+
├── components/
|
|
197
|
+
├── constants/
|
|
198
|
+
├── engines/
|
|
199
|
+
├── extensions/
|
|
200
|
+
├── hooks/
|
|
201
|
+
├── managers/
|
|
202
|
+
├── markup/
|
|
203
|
+
│ ├── core/
|
|
204
|
+
│ ├── edit2d/
|
|
205
|
+
│ ├── model/
|
|
206
|
+
│ └── tools/
|
|
207
|
+
├── services/
|
|
208
|
+
├── styles/
|
|
209
|
+
├── types/
|
|
210
|
+
├── utils/
|
|
211
|
+
├── index.js
|
|
212
|
+
└── index.d.ts
|
|
213
|
+
```
|
|
386
214
|
|
|
387
|
-
|
|
388
|
-
- Ant Design ^5.0.0 (peer dependency)
|
|
389
|
-
- Modern browsers (Chrome 90+, Firefox 88+, Safari 14+, Edge 90+)
|
|
215
|
+
Note: runtime architecture is Edit2D-first. Older docs or examples referencing `DrawingManager`/LMV MarkupsCore are legacy and no longer reflect the current `src` implementation.
|
|
390
216
|
|
|
391
217
|
## Development
|
|
392
218
|
|
|
393
219
|
```bash
|
|
394
|
-
# Install dependencies
|
|
395
220
|
npm install
|
|
396
|
-
|
|
397
|
-
# Build package
|
|
398
221
|
npm run build
|
|
399
|
-
|
|
400
|
-
# Watch mode
|
|
401
222
|
npm run dev
|
|
223
|
+
```
|
|
402
224
|
|
|
403
|
-
|
|
404
|
-
|
|
225
|
+
Run local test app:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
cd test/app
|
|
229
|
+
npm install
|
|
230
|
+
npm run dev
|
|
405
231
|
```
|
|
406
232
|
|
|
407
233
|
## Documentation
|
|
408
234
|
|
|
409
|
-
- [API Reference](
|
|
410
|
-
- [Architecture](ARCHITECTURE.md)
|
|
411
|
-
- [
|
|
412
|
-
- [
|
|
235
|
+
- [API Reference](API.md)
|
|
236
|
+
- [Architecture](ARCHITECTURE.md)
|
|
237
|
+
- [Collaborative API](docs/COLLABORATIVE-API.md)
|
|
238
|
+
- [Versioning Guide](docs/VERSIONING.md)
|
|
413
239
|
|
|
414
240
|
## License
|
|
415
241
|
|
|
416
242
|
MIT
|
|
417
|
-
|
|
418
|
-
## Author
|
|
419
|
-
|
|
420
|
-
**Duong Tran Quang**
|
|
421
|
-
|
|
422
|
-
- Email: baymax.contact@gmail.com
|
|
423
|
-
- GitHub: [@DTDucas](https://github.com/DTDucas)
|
|
424
|
-
- LinkedIn: [dtducas](https://www.linkedin.com/in/dtducas)
|
|
425
|
-
|
|
426
|
-
## Support
|
|
427
|
-
|
|
428
|
-
- [Report bugs](https://github.com/DTDucas/wh-forge-viewer/issues)
|
|
429
|
-
- [Request features](https://github.com/DTDucas/wh-forge-viewer/issues)
|
|
430
|
-
|
|
431
|
-
---
|
|
432
|
-
|
|
433
|
-
Made with care by [DTDucas](https://github.com/DTDucas)
|