@dtducas/wh-forge-viewer 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -186,542 +186,1672 @@ function _unsupportedIterableToArray(r, a) {
186
186
  }
187
187
  }
188
188
 
189
- function registerToolbarExtension(Autodesk) {
190
- function ToolbarExtension(viewer, options) {
191
- Autodesk.Viewing.Extension.call(this, viewer, options);
192
- this.viewer = viewer;
193
- this.subToolbar = null;
194
- this.paginationGroup = null;
195
- this.viewables = [];
196
- this.currentIndex = 0;
197
- this.docBrowserShouldBeOpen = false; // Track if doc browser should remain open
198
- }
199
- ToolbarExtension.prototype = Object.create(Autodesk.Viewing.Extension.prototype);
200
- ToolbarExtension.prototype.constructor = ToolbarExtension;
201
- ToolbarExtension.prototype.load = function () {
202
- return true;
203
- };
204
- ToolbarExtension.prototype.unload = function () {
205
- this.cleanupToolbar();
206
- return true;
207
- };
208
- ToolbarExtension.prototype.cleanupToolbar = function () {
209
- if (this.viewer.toolbar) {
210
- if (this.subToolbar) {
211
- this.viewer.toolbar.removeControl(this.subToolbar);
212
- this.subToolbar = null;
213
- }
214
- if (this.paginationGroup) {
215
- this.viewer.toolbar.removeControl(this.paginationGroup);
216
- this.paginationGroup = null;
217
- }
189
+ /**
190
+ * Document Browser Configuration Constants
191
+ *
192
+ * @file document-browser.constants.ts
193
+ * @description Constants for Document Browser panel configuration and styling
194
+ */
195
+ const DOCUMENT_BROWSER_STYLES = {
196
+ top: '0',
197
+ left: 'unset',
198
+ right: '0px',
199
+ };
200
+
201
+ /**
202
+ * Event Names Constants
203
+ *
204
+ * @file events.constants.ts
205
+ * @description Constants for Autodesk Forge events and custom application events
206
+ */
207
+ const EVENT_NAMES = {
208
+ // Autodesk Forge Events
209
+ GEOMETRY_LOADED: 'Autodesk.Viewing.GEOMETRY_LOADED_EVENT',
210
+ MODEL_ADDED: 'Autodesk.Viewing.MODEL_ADDED_EVENT',
211
+ TOOLBAR_CREATED: 'Autodesk.Viewing.TOOLBAR_CREATED_EVENT',
212
+ // Custom Application Events
213
+ PAGE_CHANGED: 'page:changed',
214
+ VIEWABLES_SET: 'viewables:set',
215
+ DOC_BROWSER_OPENED: 'docBrowser:opened',
216
+ DOC_BROWSER_CLOSED: 'docBrowser:closed',
217
+ TOOLBAR_BUTTON_CLICKED: 'toolbar:button:clicked',
218
+ PAN_ACTIVATED: 'pan:activated',
219
+ DOWNLOAD_REQUESTED: 'download:requested',
220
+ };
221
+
222
+ /**
223
+ * Toolbar Configuration Constants
224
+ *
225
+ * @file toolbar.constants.ts
226
+ * @description Constants for custom toolbar buttons, groups, and refresh intervals
227
+ */
228
+ const DEFAULT_HIDDEN_TOOLBAR_GROUPS = [
229
+ 'settingsTools',
230
+ 'modelTools',
231
+ 'navTools',
232
+ ];
233
+ const CUSTOM_TOOLBAR_BUTTONS = {
234
+ PAN: {
235
+ id: 'custom-pan-btn',
236
+ icon: 'adsk-icon-pan',
237
+ tooltip: 'Pan',
238
+ },
239
+ DOC_BROWSER: {
240
+ id: 'custom-doc-browser-btn',
241
+ icon: 'adsk-icon-documentModels',
242
+ tooltip: 'Document Browser',
243
+ },
244
+ DOWNLOAD: {
245
+ id: 'custom-download-btn',
246
+ icon: 'adsk-icon-custom-download',
247
+ tooltip: 'Download File',
248
+ },
249
+ };
250
+ const PAGINATION_BUTTONS = {
251
+ PREV: {
252
+ id: 'prev-page-btn',
253
+ icon: 'adsk-icon-custom-prev',
254
+ tooltip: 'Previous Page',
255
+ },
256
+ NEXT: {
257
+ id: 'next-page-btn',
258
+ icon: 'adsk-icon-custom-next',
259
+ tooltip: 'Next Page',
260
+ },
261
+ LABEL: {
262
+ id: 'total-page-label',
263
+ tooltip: 'Page info',
264
+ },
265
+ };
266
+ const TOOLBAR_REFRESH_INTERVALS = {
267
+ HEALING_POLL: 8,
268
+ BUTTON_STATE_CHECK: 10,
269
+ DOM_SETTLE: 50,
270
+ GEOMETRY_SETTLE: 80,
271
+ EXTENSION_INIT: 150,
272
+ };
273
+ const TOOLBAR_CONTROL_GROUP_IDS = {
274
+ TOOLS: 'custom-tool-group',
275
+ PAGINATION: 'custom-pagination-group',
276
+ };
277
+
278
+ /**
279
+ * Autodesk Forge Viewer Configuration Constants
280
+ *
281
+ * @file viewer.constants.ts
282
+ * @description Constants for Forge Viewer initialization, CDN URLs, and file type mappings
283
+ */
284
+ const FORGE_VIEWER_VERSION = '7.*';
285
+ const FORGE_VIEWER_CDN = 'https://developer.api.autodesk.com/modelderivative/v2/viewers';
286
+ const FORGE_STYLE_URL = `${FORGE_VIEWER_CDN}/${FORGE_VIEWER_VERSION}/style.min.css`;
287
+ const FORGE_SCRIPT_URL = `${FORGE_VIEWER_CDN}/${FORGE_VIEWER_VERSION}/viewer3D.min.js`;
288
+ const SUPPORTED_FILE_EXTENSIONS = ['pdf', 'dwf', 'dwfx'];
289
+ const FILE_EXTENSION_TO_VIEWER_EXTENSION = {
290
+ pdf: 'Autodesk.PDF',
291
+ dwf: 'Autodesk.DWF',
292
+ dwfx: 'Autodesk.DWF',
293
+ };
294
+ const GEOMETRY_SEARCH_CRITERIA = {
295
+ VIEW_3D: { type: 'geometry', role: '3d', progress: 'complete' },
296
+ VIEW_2D: { type: 'geometry', role: '2d', progress: 'complete' },
297
+ };
298
+
299
+ /******************************************************************************
300
+ Copyright (c) Microsoft Corporation.
301
+
302
+ Permission to use, copy, modify, and/or distribute this software for any
303
+ purpose with or without fee is hereby granted.
304
+
305
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
306
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
307
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
308
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
309
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
310
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
311
+ PERFORMANCE OF THIS SOFTWARE.
312
+ ***************************************************************************** */
313
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
314
+
315
+
316
+ function __awaiter(thisArg, _arguments, P, generator) {
317
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
318
+ return new (P || (P = Promise))(function (resolve, reject) {
319
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
320
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
321
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
322
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
323
+ });
324
+ }
325
+
326
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
327
+ var e = new Error(message);
328
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
329
+ };
330
+
331
+ /**
332
+ * DOM Utility Functions
333
+ *
334
+ * @file dom.utils.ts
335
+ * @description Utilities for safe DOM manipulation and querying
336
+ */
337
+ /**
338
+ * Find element by text content
339
+ */
340
+ function findElementByText(selector, text) {
341
+ var _a;
342
+ const elements = Array.from(document.querySelectorAll(selector));
343
+ return (_a = elements.find((el) => { var _a; return (_a = el.textContent) === null || _a === void 0 ? void 0 : _a.includes(text); })) !== null && _a !== void 0 ? _a : null;
344
+ }
345
+ /**
346
+ * Find thumbnail tab element using multiple selectors
347
+ */
348
+ function findThumbnailTab() {
349
+ return (document.querySelector('.docking-panel-thumbnail-view') ||
350
+ document.querySelector('[data-i18n="Thumbnails"]') ||
351
+ findElementByText('.adsk-control-group .adsk-button', 'Thumbnails'));
352
+ }
353
+ /**
354
+ * Safely click an element
355
+ */
356
+ function clickElement(element) {
357
+ if (element && 'click' in element) {
358
+ element.click();
218
359
  }
219
- };
220
- ToolbarExtension.prototype.setViewables = function (viewables) {
221
- var _this = this;
222
- this.viewables = viewables;
223
- this.currentIndex = 0;
224
- // If multiple pages, document browser will be auto-opened
225
- if (viewables.length > 1) {
226
- this.docBrowserShouldBeOpen = true;
227
- }
228
- this.updatePaginationState();
229
- this.viewer.addEventListener(Autodesk.Viewing.MODEL_ADDED_EVENT, function (e) {
230
- _this.updateCurrentIndexFromModel();
231
- });
232
- };
233
- ToolbarExtension.prototype.updateCurrentIndexFromModel = function () {
234
- if (!this.viewer.model || this.viewables.length === 0) return;
235
- var currentNode = this.viewer.model.getDocumentNode();
236
- if (currentNode) {
237
- var currentGuid = currentNode.data.guid || currentNode.guid;
238
- var index = this.viewables.findIndex(function (v) {
239
- var vGuid = v.data && v.data.guid ? v.data.guid : v.guid;
240
- return vGuid === currentGuid;
241
- });
242
- if (index !== -1) {
243
- this.currentIndex = index;
244
- }
245
- this.updatePaginationState();
360
+ }
361
+ /**
362
+ * Apply multiple styles to an element
363
+ */
364
+ function applyStyles(element, styles) {
365
+ if (element) {
366
+ Object.assign(element.style, styles);
246
367
  }
247
- };
248
- ToolbarExtension.prototype.updatePaginationState = function () {
249
- if (!this.paginationGroup) return;
368
+ }
250
369
 
251
- // Hide pagination group if there's only 1 page or no pages
252
- if (this.viewables.length <= 1) {
253
- this.paginationGroup.setVisible(false);
254
- // Also hide the container via CSS to ensure it's completely hidden
255
- if (this.paginationGroup.container) {
256
- this.paginationGroup.container.style.display = 'none';
257
- }
258
- return;
370
+ /**
371
+ * File Utility Functions
372
+ *
373
+ * @file file.utils.ts
374
+ * @description Utilities for file handling and path operations
375
+ */
376
+ /**
377
+ * Extract filename from URL or path
378
+ */
379
+ function extractFilenameFromPath(path) {
380
+ try {
381
+ const urlPath = new URL(path).pathname;
382
+ const filename = urlPath.split('/').pop();
383
+ return decodeURIComponent(filename || 'document.pdf');
259
384
  }
260
-
261
- // Show pagination group for multi-page documents
262
- this.paginationGroup.setVisible(true);
263
- // Ensure container is visible
264
- if (this.paginationGroup.container) {
265
- this.paginationGroup.container.style.display = '';
266
- }
267
- var totalBtn = this.paginationGroup.getControl('total-page-label');
268
- if (totalBtn) {
269
- var current = this.viewables.length > 0 ? this.currentIndex + 1 : 0;
270
- var total = this.viewables.length;
271
- totalBtn.setToolTip("Page ".concat(current, " of ").concat(total));
272
- var domElem = totalBtn.container;
273
- if (domElem) {
274
- domElem.innerHTML = "<div style=\"display: flex; align-items: center; justify-content: center; height: 100%; padding: 0 10px; color: white; font-size: 14px; white-space: nowrap;\">".concat(current, " / ").concat(total, "</div>");
275
- }
385
+ catch (_a) {
386
+ const parts = path.split('/');
387
+ return parts[parts.length - 1] || 'document.pdf';
276
388
  }
277
- };
278
- ToolbarExtension.prototype.loadCurrentViewable = function () {
279
- var _this2 = this;
280
- if (this.viewables.length > 0 && this.viewables[this.currentIndex]) {
281
- var viewer = this.viewer;
282
- var toolbar = viewer.toolbar;
283
- var self = this;
389
+ }
284
390
 
285
- // Store Document Browser panel state before loading new page
286
- var docBrowserExt = viewer.getExtension('Autodesk.DocumentBrowser');
287
- var isCurrentlyOpen = docBrowserExt && docBrowserExt.ui && docBrowserExt.ui.panel && docBrowserExt.ui.panel.isVisible();
391
+ class DownloadService {
392
+ /**
393
+ * Download a file from URL
394
+ *
395
+ * @param fileUrl - URL of file to download
396
+ *
397
+ * @example
398
+ * const downloadService = new DownloadService();
399
+ * await downloadService.downloadFile('https://example.com/document.pdf');
400
+ */
401
+ downloadFile(fileUrl) {
402
+ return __awaiter(this, void 0, void 0, function* () {
403
+ try {
404
+ const response = yield fetch(fileUrl);
405
+ if (!response.ok) {
406
+ throw new Error(`HTTP error! status: ${response.status}`);
407
+ }
408
+ const blob = yield response.blob();
409
+ const filename = extractFilenameFromPath(fileUrl);
410
+ // Create download link
411
+ const blobUrl = URL.createObjectURL(blob);
412
+ const anchor = document.createElement('a');
413
+ anchor.href = blobUrl;
414
+ anchor.download = filename;
415
+ anchor.style.display = 'none';
416
+ // Trigger download
417
+ document.body.appendChild(anchor);
418
+ anchor.click();
419
+ document.body.removeChild(anchor);
420
+ // Cleanup
421
+ URL.revokeObjectURL(blobUrl);
422
+ }
423
+ catch (error) {
424
+ throw error;
425
+ }
426
+ });
427
+ }
428
+ }
288
429
 
289
- // Update flag - if currently open or flag is already set, keep it open
290
- if (isCurrentlyOpen) {
291
- this.docBrowserShouldBeOpen = true;
292
- }
430
+ class EventBus {
431
+ constructor() {
432
+ this.listeners = new Map();
433
+ // Private constructor for singleton pattern
434
+ }
435
+ /**
436
+ * Get singleton instance of EventBus
437
+ *
438
+ * @example
439
+ * const eventBus = EventBus.getInstance();
440
+ * eventBus.on('myEvent', (data) => console.log(data));
441
+ */
442
+ static getInstance() {
443
+ if (!EventBus.instance) {
444
+ EventBus.instance = new EventBus();
445
+ }
446
+ return EventBus.instance;
447
+ }
448
+ /**
449
+ * Subscribe to an event
450
+ *
451
+ * @param eventName - Event name to listen for
452
+ * @param listener - Callback function to execute when event is emitted
453
+ *
454
+ * @example
455
+ * eventBus.on('page:changed', (data) => {
456
+ * console.log('Page changed to:', data.index);
457
+ * });
458
+ */
459
+ on(eventName, listener) {
460
+ if (!this.listeners.has(eventName)) {
461
+ this.listeners.set(eventName, new Set());
462
+ }
463
+ this.listeners.get(eventName).add(listener);
464
+ }
465
+ /**
466
+ * Unsubscribe from an event
467
+ *
468
+ * @param eventName - Event name to stop listening for
469
+ * @param listener - Callback function to remove
470
+ *
471
+ * @example
472
+ * const handler = (data) => console.log(data);
473
+ * eventBus.on('myEvent', handler);
474
+ * eventBus.off('myEvent', handler);
475
+ */
476
+ off(eventName, listener) {
477
+ const listeners = this.listeners.get(eventName);
478
+ if (listeners) {
479
+ listeners.delete(listener);
480
+ if (listeners.size === 0) {
481
+ this.listeners.delete(eventName);
482
+ }
483
+ }
484
+ }
485
+ /**
486
+ * Emit an event to all subscribers
487
+ *
488
+ * @param eventName - Event name to emit
489
+ * @param data - Data to pass to subscribers
490
+ *
491
+ * @example
492
+ * eventBus.emit('page:changed', { index: 5, viewable: {...} });
493
+ */
494
+ emit(eventName, data) {
495
+ const listeners = this.listeners.get(eventName);
496
+ if (listeners) {
497
+ listeners.forEach((listener) => {
498
+ try {
499
+ listener(data);
500
+ }
501
+ catch (error) {
502
+ // Silent error handling
503
+ }
504
+ });
505
+ }
506
+ }
507
+ /**
508
+ * Clear all listeners for an event (or all events if no eventName provided)
509
+ *
510
+ * @param eventName - Optional event name to clear. If not provided, clears all events
511
+ */
512
+ clear(eventName) {
513
+ if (eventName) {
514
+ this.listeners.delete(eventName);
515
+ }
516
+ else {
517
+ this.listeners.clear();
518
+ }
519
+ }
520
+ /**
521
+ * Get number of listeners for an event
522
+ *
523
+ * @param eventName - Event name to count listeners for
524
+ * @returns Number of listeners registered for the event
525
+ */
526
+ listenerCount(eventName) {
527
+ var _a, _b;
528
+ return (_b = (_a = this.listeners.get(eventName)) === null || _a === void 0 ? void 0 : _a.size) !== null && _b !== void 0 ? _b : 0;
529
+ }
530
+ }
293
531
 
294
- // Add one-time listener for geometry loaded to restore states
295
- var _onGeometryLoaded = function onGeometryLoaded() {
296
- viewer.removeEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, _onGeometryLoaded);
297
- // Restore after geometry is fully loaded
298
- setTimeout(function () {
299
- self.restoreButtonStates(self.docBrowserShouldBeOpen);
300
- }, 200);
301
- };
302
- viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, _onGeometryLoaded);
303
- viewer.loadDocumentNode(viewer.model.getDocumentNode().getDocument(), this.viewables[this.currentIndex]).then(function () {
304
- var defaultGroups = ['settingsTools', 'modelTools', 'navTools'];
305
- defaultGroups.forEach(function (id) {
306
- var group = toolbar.getControl(id);
307
- if (group) group.setVisible(false);
532
+ class FileLoader {
533
+ constructor(viewer) {
534
+ this.viewer = viewer;
535
+ this.eventBus = EventBus.getInstance();
536
+ }
537
+ /**
538
+ * Load a file into the viewer
539
+ *
540
+ * @param filePath - Path or URL to the file
541
+ * @param fileExt - File extension (pdf, dwf, dwfx)
542
+ * @param callbacks - Optional success/error callbacks
543
+ *
544
+ * @example
545
+ * const fileLoader = new FileLoader(viewer);
546
+ * await fileLoader.loadFile('document.pdf', 'pdf', {
547
+ * onSuccess: (e) => console.log('Loaded successfully'),
548
+ * onError: (err) => console.error('Load failed', err)
549
+ * });
550
+ */
551
+ loadFile(filePath, fileExt, callbacks) {
552
+ return __awaiter(this, void 0, void 0, function* () {
553
+ const viewerExtension = FILE_EXTENSION_TO_VIEWER_EXTENSION[fileExt];
554
+ // Load appropriate viewer extension
555
+ yield this.viewer.loadExtension(viewerExtension);
556
+ // Determine loading method based on file type
557
+ if (fileExt === 'pdf') {
558
+ yield this.loadPDFFile(filePath, callbacks);
559
+ }
560
+ else {
561
+ yield this.loadDWFFile(filePath, callbacks);
562
+ }
308
563
  });
309
- var toolGroupExists = toolbar.getControl('custom-tool-group');
310
- var paginationGroupExists = toolbar.getControl('custom-pagination-group');
311
- if (!toolGroupExists && _this2.subToolbar) {
312
- toolbar.addControl(_this2.subToolbar);
313
- }
314
- if (!paginationGroupExists && _this2.paginationGroup) {
315
- toolbar.addControl(_this2.paginationGroup);
316
- }
317
- if (_this2.subToolbar) _this2.subToolbar.setVisible(true);
318
- if (_this2.paginationGroup) _this2.paginationGroup.setVisible(true);
319
- toolbar.setVisible(true);
320
- _this2.updatePaginationState();
321
-
322
- // Also restore immediately (may work for cached pages)
323
- _this2.restoreButtonStates(_this2.docBrowserShouldBeOpen);
324
- })["catch"](function (err) {
325
- return console.error('Error loading viewable:', err);
326
- });
327
564
  }
328
- };
329
- ToolbarExtension.prototype.restoreButtonStates = function (shouldOpenDocBrowser) {
330
- var viewer = this.viewer;
331
- var toolbar = viewer.toolbar;
332
- if (!toolbar) return;
333
- var self = this;
565
+ /**
566
+ * Load PDF file directly
567
+ */
568
+ loadPDFFile(filePath, callbacks) {
569
+ return __awaiter(this, void 0, void 0, function* () {
570
+ const handleLoadSuccess = this.createLoadSuccessHandler(callbacks === null || callbacks === void 0 ? void 0 : callbacks.onSuccess);
571
+ this.viewer.loadModel(filePath, {}, handleLoadSuccess);
572
+ });
573
+ }
574
+ /**
575
+ * Load DWF/DWFX file via blob
576
+ * Extracted from ViewerForgePDF.jsx lines 125-136
577
+ */
578
+ loadDWFFile(filePath, callbacks) {
579
+ return __awaiter(this, void 0, void 0, function* () {
580
+ return new Promise((resolve, reject) => {
581
+ const xhr = new XMLHttpRequest();
582
+ xhr.open('GET', filePath, true);
583
+ xhr.responseType = 'blob';
584
+ xhr.onload = () => {
585
+ var _a;
586
+ if (xhr.status === 200) {
587
+ const blob = xhr.response;
588
+ const blobUrl = window.URL.createObjectURL(blob);
589
+ const handleLoadSuccess = this.createLoadSuccessHandler(callbacks === null || callbacks === void 0 ? void 0 : callbacks.onSuccess);
590
+ this.viewer.loadModel(blobUrl + '#.dwf', {}, handleLoadSuccess);
591
+ resolve();
592
+ }
593
+ else {
594
+ const error = new Error(`Failed to load DWF file: HTTP ${xhr.status}`);
595
+ (_a = callbacks === null || callbacks === void 0 ? void 0 : callbacks.onError) === null || _a === void 0 ? void 0 : _a.call(callbacks, error);
596
+ reject(error);
597
+ }
598
+ };
599
+ xhr.onerror = () => {
600
+ var _a;
601
+ const error = new Error('Network error loading DWF file');
602
+ (_a = callbacks === null || callbacks === void 0 ? void 0 : callbacks.onError) === null || _a === void 0 ? void 0 : _a.call(callbacks, error);
603
+ reject(error);
604
+ };
605
+ xhr.send();
606
+ });
607
+ });
608
+ }
609
+ /**
610
+ * Create load success handler that extracts viewables
611
+ * Extracted from ViewerForgePDF.jsx lines 44-56
612
+ */
613
+ createLoadSuccessHandler(userCallback) {
614
+ return (event) => {
615
+ try {
616
+ // Enable reverse zoom
617
+ this.viewer.setReverseZoomDirection(true);
618
+ // Extract viewables
619
+ const viewables = this.extractViewables(event);
620
+ // Emit event with viewables
621
+ this.eventBus.emit(EVENT_NAMES.VIEWABLES_SET, { viewables });
622
+ // Show toolbar
623
+ if (this.viewer.toolbar) {
624
+ this.viewer.toolbar.setVisible(true);
625
+ }
626
+ // Call user callback if provided
627
+ userCallback === null || userCallback === void 0 ? void 0 : userCallback(event);
628
+ }
629
+ catch (error) {
630
+ // Silent error handling
631
+ }
632
+ };
633
+ }
634
+ /**
635
+ * Extract viewables from document
636
+ * Extracted from ViewerForgePDF.jsx lines 47-56
637
+ */
638
+ extractViewables(event) {
639
+ const root = event.getDocumentNode().getRootNode();
640
+ const view3d = root.search(GEOMETRY_SEARCH_CRITERIA.VIEW_3D, true);
641
+ const view2d = root.search(GEOMETRY_SEARCH_CRITERIA.VIEW_2D, true);
642
+ return view3d.concat(view2d);
643
+ }
644
+ }
334
645
 
335
- // Restore Document Browser state
336
- var openDocBrowser = /*#__PURE__*/function () {
337
- var _ref = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
338
- var docBrowserExt, _t;
339
- return _regenerator().w(function (_context) {
340
- while (1) switch (_context.p = _context.n) {
341
- case 0:
342
- if (shouldOpenDocBrowser) {
343
- _context.n = 1;
344
- break;
345
- }
346
- return _context.a(2);
347
- case 1:
348
- docBrowserExt = viewer.getExtension('Autodesk.DocumentBrowser'); // If extension is null, it was unloaded - we need to reload it
349
- if (docBrowserExt) {
350
- _context.n = 5;
351
- break;
352
- }
353
- _context.p = 2;
354
- _context.n = 3;
355
- return viewer.loadExtension('Autodesk.DocumentBrowser');
356
- case 3:
357
- docBrowserExt = _context.v;
358
- // After loading, we need to wait for the extension to fully initialize
359
- // and then open the panel
360
- setTimeout(function () {
361
- if (docBrowserExt && docBrowserExt.ui) {
362
- docBrowserExt.ui.togglePanel();
363
- setTimeout(function () {
364
- self.applyDocBrowserStyling();
365
- self.checkButtonState();
366
- }, 150);
646
+ class DocumentBrowserManager {
647
+ constructor(viewer, config, eventBus) {
648
+ this.shouldRemainOpen = false;
649
+ this.viewer = viewer;
650
+ this.config = config || {
651
+ autoOpen: true,
652
+ persistState: true,
653
+ defaultTab: 'thumbnails',
654
+ };
655
+ // Use provided EventBus instance or fallback to singleton
656
+ this.eventBus = eventBus || EventBus.getInstance();
657
+ }
658
+ autoOpenIfMultiPage(isMultiPage) {
659
+ return __awaiter(this, void 0, void 0, function* () {
660
+ // Auto-open Document Browser for both single and multi-page documents
661
+ if (!this.config.autoOpen) {
662
+ return;
663
+ }
664
+ setTimeout(() => {
665
+ const originalDocBtn = document.getElementById('toolbar-documentModels');
666
+ if (originalDocBtn && !originalDocBtn.classList.contains('active')) {
667
+ originalDocBtn.click();
668
+ this.shouldRemainOpen = true;
669
+ setTimeout(() => {
670
+ this.applyCustomStyling();
671
+ this.switchToThumbnailTab();
672
+ }, TOOLBAR_REFRESH_INTERVALS.EXTENSION_INIT);
673
+ this.eventBus.emit(EVENT_NAMES.DOC_BROWSER_OPENED);
367
674
  }
368
- }, 200);
369
- return _context.a(2);
370
- case 4:
371
- _context.p = 4;
372
- _t = _context.v;
373
- console.error('[ToolbarExt] Failed to reload DocumentBrowser:', _t);
374
- return _context.a(2);
375
- case 5:
376
- if (docBrowserExt.ui) {
377
- _context.n = 6;
378
- break;
379
- }
380
- return _context.a(2);
381
- case 6:
382
- if (!docBrowserExt.ui.panel) {
383
- _context.n = 8;
384
- break;
385
- }
386
- if (!docBrowserExt.ui.panel.isVisible()) {
387
- _context.n = 7;
388
- break;
389
- }
390
- self.applyDocBrowserStyling();
391
- self.checkButtonState();
392
- return _context.a(2);
393
- case 7:
394
- // Use setVisible(true) directly on the panel
395
- docBrowserExt.ui.panel.setVisible(true);
396
- setTimeout(function () {
397
- self.applyDocBrowserStyling();
398
- self.checkButtonState();
399
- }, 100);
400
- return _context.a(2);
401
- case 8:
402
- // If panel doesn't exist yet, use togglePanel to create it
403
- if (typeof docBrowserExt.ui.togglePanel === 'function') {
404
- docBrowserExt.ui.togglePanel();
405
- setTimeout(function () {
406
- self.applyDocBrowserStyling();
407
- self.checkButtonState();
408
- }, 150);
409
- }
410
- case 9:
411
- return _context.a(2);
412
- }
413
- }, _callee, null, [[2, 4]]);
414
- }));
415
- return function openDocBrowser() {
416
- return _ref.apply(this, arguments);
417
- };
418
- }();
675
+ else if (originalDocBtn &&
676
+ originalDocBtn.classList.contains('active')) {
677
+ this.shouldRemainOpen = true;
678
+ }
679
+ }, TOOLBAR_REFRESH_INTERVALS.GEOMETRY_SETTLE);
680
+ });
681
+ }
682
+ openPanel() {
683
+ return __awaiter(this, void 0, void 0, function* () {
684
+ var _a, _b, _c;
685
+ let ext = this.viewer.getExtension('Autodesk.DocumentBrowser');
686
+ if (!ext) {
687
+ try {
688
+ yield this.viewer.loadExtension('Autodesk.DocumentBrowser');
689
+ ext = this.viewer.getExtension('Autodesk.DocumentBrowser');
690
+ }
691
+ catch (e) {
692
+ return;
693
+ }
694
+ }
695
+ // Check if already open
696
+ if ((_b = (_a = ext === null || ext === void 0 ? void 0 : ext.ui) === null || _a === void 0 ? void 0 : _a.panel) === null || _b === void 0 ? void 0 : _b.isVisible()) {
697
+ this.shouldRemainOpen = true;
698
+ this.applyCustomStyling();
699
+ this.switchToThumbnailTab();
700
+ return;
701
+ }
702
+ // Try to use original button first for proper initialization
703
+ const originalDocBtn = document.getElementById('toolbar-documentModels');
704
+ if (originalDocBtn) {
705
+ originalDocBtn.click();
706
+ this.shouldRemainOpen = true;
707
+ setTimeout(() => {
708
+ this.applyCustomStyling();
709
+ this.switchToThumbnailTab();
710
+ this.eventBus.emit(EVENT_NAMES.DOC_BROWSER_OPENED);
711
+ }, TOOLBAR_REFRESH_INTERVALS.EXTENSION_INIT);
712
+ }
713
+ else {
714
+ if ((_c = ext === null || ext === void 0 ? void 0 : ext.ui) === null || _c === void 0 ? void 0 : _c.panel) {
715
+ ext.ui.panel.setVisible(true);
716
+ this.shouldRemainOpen = true;
717
+ setTimeout(() => {
718
+ this.applyCustomStyling();
719
+ this.switchToThumbnailTab();
720
+ this.eventBus.emit(EVENT_NAMES.DOC_BROWSER_OPENED);
721
+ }, TOOLBAR_REFRESH_INTERVALS.EXTENSION_INIT);
722
+ }
723
+ }
724
+ });
725
+ }
726
+ closePanel() {
727
+ return __awaiter(this, void 0, void 0, function* () {
728
+ var _a, _b, _c;
729
+ const ext = this.viewer.getExtension('Autodesk.DocumentBrowser');
730
+ if (!((_b = (_a = ext === null || ext === void 0 ? void 0 : ext.ui) === null || _a === void 0 ? void 0 : _a.panel) === null || _b === void 0 ? void 0 : _b.isVisible())) {
731
+ this.shouldRemainOpen = false;
732
+ return;
733
+ }
734
+ const originalDocBtn = document.getElementById('toolbar-documentModels');
735
+ if (originalDocBtn && originalDocBtn.classList.contains('active')) {
736
+ originalDocBtn.click();
737
+ this.shouldRemainOpen = false;
738
+ this.eventBus.emit(EVENT_NAMES.DOC_BROWSER_CLOSED);
739
+ }
740
+ else {
741
+ if ((_c = ext === null || ext === void 0 ? void 0 : ext.ui) === null || _c === void 0 ? void 0 : _c.panel) {
742
+ ext.ui.panel.setVisible(false);
743
+ this.shouldRemainOpen = false;
744
+ this.eventBus.emit(EVENT_NAMES.DOC_BROWSER_CLOSED);
745
+ }
746
+ }
747
+ });
748
+ }
749
+ togglePanel() {
750
+ return __awaiter(this, void 0, void 0, function* () {
751
+ const isCurrentlyOpen = this.isOpen();
752
+ if (isCurrentlyOpen) {
753
+ yield this.closePanel();
754
+ }
755
+ else {
756
+ yield this.openPanel();
757
+ }
758
+ });
759
+ }
760
+ isOpen() {
761
+ var _a, _b, _c;
762
+ const ext = this.viewer.getExtension('Autodesk.DocumentBrowser');
763
+ return (_c = (_b = (_a = ext === null || ext === void 0 ? void 0 : ext.ui) === null || _a === void 0 ? void 0 : _a.panel) === null || _b === void 0 ? void 0 : _b.isVisible()) !== null && _c !== void 0 ? _c : false;
764
+ }
765
+ applyCustomStyling() {
766
+ setTimeout(() => {
767
+ var _a, _b;
768
+ const ext = this.viewer.getExtension('Autodesk.DocumentBrowser');
769
+ if ((_b = (_a = ext === null || ext === void 0 ? void 0 : ext.ui) === null || _a === void 0 ? void 0 : _a.panel) === null || _b === void 0 ? void 0 : _b.container) {
770
+ applyStyles(ext.ui.panel.container, DOCUMENT_BROWSER_STYLES);
771
+ // Adjust panel height based on number of thumbnails
772
+ this.adjustPanelHeight();
773
+ }
774
+ }, TOOLBAR_REFRESH_INTERVALS.DOM_SETTLE);
775
+ }
776
+ /**
777
+ * Adjust Document Browser panel height based on content
778
+ */
779
+ adjustPanelHeight() {
780
+ setTimeout(() => {
781
+ var _a, _b;
782
+ const ext = this.viewer.getExtension('Autodesk.DocumentBrowser');
783
+ if (!((_b = (_a = ext === null || ext === void 0 ? void 0 : ext.ui) === null || _a === void 0 ? void 0 : _a.panel) === null || _b === void 0 ? void 0 : _b.container)) {
784
+ return;
785
+ }
786
+ const panel = ext.ui.panel;
787
+ const scrollContainer = panel.container.querySelector('.docking-panel-scroll');
788
+ if (!scrollContainer) {
789
+ return;
790
+ }
791
+ // Count thumbnails
792
+ const thumbnails = scrollContainer.querySelectorAll('.viewer-ext-docbrowser-thumbnail');
793
+ const thumbnailCount = thumbnails.length;
794
+ if (thumbnailCount === 0) {
795
+ return;
796
+ }
797
+ // Get thumbnail dimensions (typically 200x200 + label)
798
+ const firstThumbnail = thumbnails[0];
799
+ const thumbnailHeight = (firstThumbnail === null || firstThumbnail === void 0 ? void 0 : firstThumbnail.offsetHeight) || 220; // Default 220 if can't get
800
+ // Calculate optimal height
801
+ // For 1 thumbnail: just the thumbnail height + some padding
802
+ // For multiple: use a reasonable max height
803
+ const headerHeight = 110; // Panel header + tabs
804
+ const padding = 40; // Extra padding for aesthetics
805
+ let optimalHeight;
806
+ if (thumbnailCount === 1) {
807
+ // Single thumbnail: compact size
808
+ optimalHeight = thumbnailHeight + headerHeight + padding;
809
+ }
810
+ else {
811
+ // Multiple thumbnails: calculate based on count but cap at viewport height
812
+ const maxViewportHeight = window.innerHeight * 0.8; // Max 80% of viewport
813
+ const calculatedHeight = Math.min(thumbnailCount * thumbnailHeight + headerHeight + padding, maxViewportHeight);
814
+ optimalHeight = calculatedHeight;
815
+ }
816
+ // Apply the height
817
+ panel.container.style.height = `${optimalHeight}px`;
818
+ // Update scroll container height
819
+ const scrollHeight = optimalHeight - headerHeight;
820
+ scrollContainer.style.height = `${scrollHeight}px`;
821
+ }, TOOLBAR_REFRESH_INTERVALS.DOM_SETTLE + 50);
822
+ }
823
+ switchToThumbnailTab() {
824
+ return __awaiter(this, void 0, void 0, function* () {
825
+ if (this.config.defaultTab !== 'thumbnails')
826
+ return;
827
+ setTimeout(() => {
828
+ const thumbnailTab = findThumbnailTab();
829
+ if (thumbnailTab && !thumbnailTab.classList.contains('active')) {
830
+ clickElement(thumbnailTab);
831
+ }
832
+ }, TOOLBAR_REFRESH_INTERVALS.DOM_SETTLE);
833
+ });
834
+ }
835
+ restoreState() {
836
+ return __awaiter(this, void 0, void 0, function* () {
837
+ var _a, _b, _c;
838
+ if (!this.config.persistState || !this.shouldRemainOpen) {
839
+ return;
840
+ }
841
+ const ext = this.viewer.getExtension('Autodesk.DocumentBrowser');
842
+ const isCurrentlyOpen = (_c = (_b = (_a = ext === null || ext === void 0 ? void 0 : ext.ui) === null || _a === void 0 ? void 0 : _a.panel) === null || _b === void 0 ? void 0 : _b.isVisible()) !== null && _c !== void 0 ? _c : false;
843
+ if (isCurrentlyOpen) {
844
+ this.applyCustomStyling();
845
+ this.switchToThumbnailTab();
846
+ // Emit event MULTIPLE times to ensure button state syncs
847
+ this.eventBus.emit(EVENT_NAMES.DOC_BROWSER_OPENED);
848
+ setTimeout(() => this.eventBus.emit(EVENT_NAMES.DOC_BROWSER_OPENED), 50);
849
+ setTimeout(() => this.eventBus.emit(EVENT_NAMES.DOC_BROWSER_OPENED), 100);
850
+ setTimeout(() => this.eventBus.emit(EVENT_NAMES.DOC_BROWSER_OPENED), 150);
851
+ }
852
+ else {
853
+ yield this.openPanel();
854
+ }
855
+ });
856
+ }
857
+ setShouldRemainOpen(value) {
858
+ this.shouldRemainOpen = value;
859
+ }
860
+ getShouldRemainOpen() {
861
+ return this.shouldRemainOpen;
862
+ }
863
+ }
419
864
 
420
- // Restore Pan tool active state
421
- var restorePanTool = function restorePanTool() {
422
- self.activatePanTool();
423
- };
865
+ /**
866
+ * PaginationManager
867
+ *
868
+ * @file PaginationManager.ts
869
+ * @description Manages page navigation for multi-page documents
870
+ * Extracted from ToolbarExtension.js lines 34-148
871
+ */
872
+ class PaginationManager {
873
+ constructor(viewer, Autodesk, config, eventBus) {
874
+ this.currentIndex = 0;
875
+ this.viewables = [];
876
+ this.paginationGroup = null;
877
+ this.docBrowserShouldBeOpen = false;
878
+ this.viewer = viewer;
879
+ this.Autodesk = Autodesk;
880
+ this.config = config || {};
881
+ // Use provided EventBus instance or fallback to singleton
882
+ this.eventBus = eventBus || EventBus.getInstance();
883
+ this.modelAddedHandler = this.updateCurrentIndexFromModel.bind(this);
884
+ // Add MODEL_ADDED_EVENT listener immediately in constructor
885
+ // This ensures it's always listening for page changes
886
+ this.viewer.addEventListener(this.Autodesk.Viewing.MODEL_ADDED_EVENT, this.modelAddedHandler);
887
+ }
888
+ /**
889
+ * Set callback for page changes (alternative to EventBus)
890
+ */
891
+ setPageChangeCallback(callback) {
892
+ this.onPageChangeCallback = callback;
893
+ }
894
+ setViewables(viewables) {
895
+ this.viewables = viewables;
896
+ this.currentIndex = 0;
897
+ if (viewables.length > 1) {
898
+ this.docBrowserShouldBeOpen = true;
899
+ }
900
+ this.updatePaginationState();
901
+ // Remove existing listener to avoid duplicates
902
+ if (this.modelAddedHandler) {
903
+ this.viewer.removeEventListener(this.Autodesk.Viewing.MODEL_ADDED_EVENT, this.modelAddedHandler);
904
+ }
905
+ // Add MODEL_ADDED_EVENT listener
906
+ this.viewer.addEventListener(this.Autodesk.Viewing.MODEL_ADDED_EVENT, this.modelAddedHandler);
907
+ }
908
+ nextPage() {
909
+ if (this.viewables.length > 0) {
910
+ this.currentIndex = (this.currentIndex + 1) % this.viewables.length;
911
+ this.loadCurrentViewable();
912
+ }
913
+ }
914
+ previousPage() {
915
+ if (this.viewables.length > 0) {
916
+ this.currentIndex =
917
+ (this.currentIndex - 1 + this.viewables.length) % this.viewables.length;
918
+ this.loadCurrentViewable();
919
+ }
920
+ }
921
+ goToPage(index) {
922
+ if (index >= 0 && index < this.viewables.length) {
923
+ this.currentIndex = index;
924
+ this.loadCurrentViewable();
925
+ }
926
+ }
927
+ getCurrentPage() {
928
+ return this.currentIndex;
929
+ }
930
+ getTotalPages() {
931
+ return this.viewables.length;
932
+ }
933
+ isMultiPage() {
934
+ return this.viewables.length > 1;
935
+ }
936
+ getViewables() {
937
+ return this.viewables;
938
+ }
939
+ updateCurrentIndexFromModel() {
940
+ var _a, _b, _c, _d, _e, _f, _g;
941
+ if (!this.viewer.model || this.viewables.length === 0) {
942
+ return;
943
+ }
944
+ const currentNode = this.viewer.model.getDocumentNode();
945
+ if (currentNode) {
946
+ const currentGuid = ((_a = currentNode.data) === null || _a === void 0 ? void 0 : _a.guid) || currentNode.guid;
947
+ const index = this.viewables.findIndex((v) => {
948
+ var _a;
949
+ const vGuid = ((_a = v.data) === null || _a === void 0 ? void 0 : _a.guid) || v.guid;
950
+ return vGuid === currentGuid;
951
+ });
952
+ if (index !== -1 && index !== this.currentIndex) {
953
+ // Page changed via Document Browser panel
954
+ this.currentIndex = index;
955
+ // Check if Document Browser is currently open
956
+ const docBrowserExt = this.viewer.getExtension('Autodesk.DocumentBrowser');
957
+ const isCurrentlyOpen = (_d = (_c = (_b = docBrowserExt === null || docBrowserExt === void 0 ? void 0 : docBrowserExt.ui) === null || _b === void 0 ? void 0 : _b.panel) === null || _c === void 0 ? void 0 : _c.isVisible()) !== null && _d !== void 0 ? _d : false;
958
+ if (isCurrentlyOpen) {
959
+ this.docBrowserShouldBeOpen = true;
960
+ }
961
+ // Emit page changed event to trigger button state sync and toolbar refresh
962
+ const pageData = {
963
+ index: this.currentIndex,
964
+ viewable: this.viewables[this.currentIndex],
965
+ shouldRestoreDocBrowser: this.docBrowserShouldBeOpen,
966
+ fromDocBrowser: true,
967
+ };
968
+ // Use callback first (direct communication)
969
+ if (this.onPageChangeCallback) {
970
+ this.onPageChangeCallback(pageData);
971
+ }
972
+ // Also emit via EventBus as fallback
973
+ this.eventBus.emit(EVENT_NAMES.PAGE_CHANGED, pageData);
974
+ // Update pagination state multiple times to ensure it sticks
975
+ const updateTimes = [10, 50, 100, 150, 200, 250, 300, 350, 400, 500];
976
+ updateTimes.forEach((delay) => {
977
+ setTimeout(() => {
978
+ this.updatePaginationState();
979
+ }, delay);
980
+ });
981
+ }
982
+ else if (index !== -1) {
983
+ // Same page - but user might have clicked thumbnail while panel is open
984
+ const docBrowserExt = this.viewer.getExtension('Autodesk.DocumentBrowser');
985
+ const isCurrentlyOpen = (_g = (_f = (_e = docBrowserExt === null || docBrowserExt === void 0 ? void 0 : docBrowserExt.ui) === null || _e === void 0 ? void 0 : _e.panel) === null || _f === void 0 ? void 0 : _f.isVisible()) !== null && _g !== void 0 ? _g : false;
986
+ if (isCurrentlyOpen && !this.docBrowserShouldBeOpen) {
987
+ this.docBrowserShouldBeOpen = true;
988
+ // Emit a "refresh" event to ensure button state is synced
989
+ const pageData = {
990
+ index: this.currentIndex,
991
+ viewable: this.viewables[this.currentIndex],
992
+ shouldRestoreDocBrowser: true,
993
+ fromDocBrowser: true,
994
+ refresh: true,
995
+ };
996
+ if (this.onPageChangeCallback) {
997
+ this.onPageChangeCallback(pageData);
998
+ }
999
+ this.eventBus.emit(EVENT_NAMES.PAGE_CHANGED, pageData);
1000
+ }
1001
+ // Always update pagination state
1002
+ this.updatePaginationState();
1003
+ }
1004
+ }
1005
+ }
1006
+ updatePaginationState() {
1007
+ // IMPORTANT: First sync currentIndex with actual loaded model
1008
+ this.syncCurrentIndexFromModel();
1009
+ if (!this.paginationGroup) {
1010
+ return;
1011
+ }
1012
+ if (this.viewables.length <= 1) {
1013
+ this.paginationGroup.setVisible(false);
1014
+ if (this.paginationGroup.container) {
1015
+ this.paginationGroup.container.style.display = 'none';
1016
+ }
1017
+ return;
1018
+ }
1019
+ this.paginationGroup.setVisible(true);
1020
+ if (this.paginationGroup.container) {
1021
+ this.paginationGroup.container.style.display = '';
1022
+ }
1023
+ const totalBtn = this.paginationGroup.getControl(PAGINATION_BUTTONS.LABEL.id);
1024
+ if (totalBtn) {
1025
+ const current = this.viewables.length > 0 ? this.currentIndex + 1 : 0;
1026
+ const total = this.viewables.length;
1027
+ totalBtn.setToolTip(`Page ${current} of ${total}`);
1028
+ const domElem = totalBtn.container;
1029
+ if (domElem) {
1030
+ const newContent = `<div style="display: flex; align-items: center; justify-content: center; height: 100%; padding: 0 10px; color: white; font-size: 14px; white-space: nowrap;">${current} / ${total}</div>`;
1031
+ domElem.innerHTML = newContent;
1032
+ // Use requestAnimationFrame to ensure DOM update
1033
+ requestAnimationFrame(() => {
1034
+ domElem.innerHTML = newContent;
1035
+ });
1036
+ // Also try direct text update as fallback
1037
+ setTimeout(() => {
1038
+ if (domElem.innerHTML !== newContent) {
1039
+ domElem.innerHTML = newContent;
1040
+ }
1041
+ }, 20);
1042
+ }
1043
+ }
1044
+ else {
1045
+ // Try to get the control again in case it was just created
1046
+ setTimeout(() => {
1047
+ const totalBtnRetry = this.paginationGroup.getControl(PAGINATION_BUTTONS.LABEL.id);
1048
+ if (totalBtnRetry) {
1049
+ const current = this.viewables.length > 0 ? this.currentIndex + 1 : 0;
1050
+ const total = this.viewables.length;
1051
+ totalBtnRetry.setToolTip(`Page ${current} of ${total}`);
1052
+ const domElem = totalBtnRetry.container;
1053
+ if (domElem) {
1054
+ domElem.innerHTML = `<div style="display: flex; align-items: center; justify-content: center; height: 100%; padding: 0 10px; color: white; font-size: 14px; white-space: nowrap;">${current} / ${total}</div>`;
1055
+ }
1056
+ }
1057
+ }, 50);
1058
+ }
1059
+ }
1060
+ /**
1061
+ * Sync currentIndex with the actual loaded model (without emitting events)
1062
+ * This is used to ensure pagination display shows correct page number
1063
+ */
1064
+ syncCurrentIndexFromModel() {
1065
+ var _a;
1066
+ if (!this.viewer.model || this.viewables.length === 0) {
1067
+ return;
1068
+ }
1069
+ const currentNode = this.viewer.model.getDocumentNode();
1070
+ if (currentNode) {
1071
+ const currentGuid = ((_a = currentNode.data) === null || _a === void 0 ? void 0 : _a.guid) || currentNode.guid;
1072
+ const index = this.viewables.findIndex((v) => {
1073
+ var _a;
1074
+ const vGuid = ((_a = v.data) === null || _a === void 0 ? void 0 : _a.guid) || v.guid;
1075
+ return vGuid === currentGuid;
1076
+ });
1077
+ if (index !== -1 && index !== this.currentIndex) {
1078
+ this.currentIndex = index;
1079
+ }
1080
+ }
1081
+ }
1082
+ /**
1083
+ * Update thumbnail selection in Document Browser panel
1084
+ * This ensures the correct thumbnail is highlighted after navigation
1085
+ */
1086
+ updateThumbnailSelection(targetViewable) {
1087
+ var _a;
1088
+ try {
1089
+ const targetGuid = ((_a = targetViewable.data) === null || _a === void 0 ? void 0 : _a.guid) || targetViewable.guid;
1090
+ // Find all thumbnails
1091
+ const thumbnails = document.querySelectorAll('.viewer-ext-docbrowser-thumbnail');
1092
+ let selectedThumbnail = null;
1093
+ thumbnails.forEach((thumbnail) => {
1094
+ const bubbleGuid = thumbnail.getAttribute('bubble-guid');
1095
+ if (bubbleGuid === targetGuid) {
1096
+ thumbnail.classList.add('viewer-ext-docbrowser-thumbnail-selected');
1097
+ selectedThumbnail = thumbnail;
1098
+ }
1099
+ else {
1100
+ thumbnail.classList.remove('viewer-ext-docbrowser-thumbnail-selected');
1101
+ }
1102
+ });
1103
+ // CRITICAL: Scroll to selected thumbnail
1104
+ if (selectedThumbnail) {
1105
+ this.scrollToThumbnail(selectedThumbnail);
1106
+ }
1107
+ }
1108
+ catch (error) {
1109
+ // Silent error handling
1110
+ }
1111
+ }
1112
+ /**
1113
+ * Scroll the Document Browser panel to show the selected thumbnail
1114
+ */
1115
+ scrollToThumbnail(thumbnail) {
1116
+ try {
1117
+ // Find the scroll container
1118
+ const scrollContainer = document.querySelector('.docking-panel-scroll');
1119
+ if (!scrollContainer) {
1120
+ return;
1121
+ }
1122
+ // Get thumbnail position relative to container
1123
+ const thumbnailRect = thumbnail.getBoundingClientRect();
1124
+ const containerRect = scrollContainer.getBoundingClientRect();
1125
+ // Calculate scroll position to center the thumbnail in the viewport
1126
+ const thumbnailTop = thumbnailRect.top - containerRect.top + scrollContainer.scrollTop;
1127
+ const containerHeight = scrollContainer.clientHeight;
1128
+ const thumbnailHeight = thumbnailRect.height;
1129
+ const targetScrollTop = thumbnailTop - containerHeight / 2 + thumbnailHeight / 2;
1130
+ // Smooth scroll to target position
1131
+ scrollContainer.scrollTo({
1132
+ top: targetScrollTop,
1133
+ behavior: 'smooth',
1134
+ });
1135
+ }
1136
+ catch (error) {
1137
+ // Silent error handling
1138
+ }
1139
+ }
1140
+ /**
1141
+ * Try to use DocumentBrowser API to change page without refreshing panel
1142
+ * Returns true if successful, false otherwise
1143
+ */
1144
+ tryUseDocBrowserAPI(targetIndex) {
1145
+ var _a, _b, _c;
1146
+ const docBrowserExt = this.viewer.getExtension('Autodesk.DocumentBrowser');
1147
+ const isOpen = (_c = (_b = (_a = docBrowserExt === null || docBrowserExt === void 0 ? void 0 : docBrowserExt.ui) === null || _a === void 0 ? void 0 : _a.panel) === null || _b === void 0 ? void 0 : _b.isVisible()) !== null && _c !== void 0 ? _c : false;
1148
+ if (!isOpen || targetIndex < 0 || targetIndex >= this.viewables.length) {
1149
+ return false;
1150
+ }
1151
+ try {
1152
+ const targetViewable = this.viewables[targetIndex];
1153
+ if (!targetViewable) {
1154
+ return false;
1155
+ }
1156
+ // Try _changeModelFn (internal method used by DocumentBrowser)
1157
+ if (docBrowserExt.ui &&
1158
+ typeof docBrowserExt.ui._changeModelFn === 'function') {
1159
+ docBrowserExt.ui._changeModelFn(targetViewable);
1160
+ // Update thumbnail selection after model change
1161
+ setTimeout(() => {
1162
+ this.updateThumbnailSelection(targetViewable);
1163
+ }, 100);
1164
+ return true;
1165
+ }
1166
+ // Fallback: Try _changeModel
1167
+ if (docBrowserExt.ui &&
1168
+ typeof docBrowserExt.ui._changeModel === 'function') {
1169
+ docBrowserExt.ui._changeModel(targetViewable);
1170
+ return true;
1171
+ }
1172
+ // Fallback: Try setCurrentViewable
1173
+ if (docBrowserExt &&
1174
+ typeof docBrowserExt.setCurrentViewable === 'function') {
1175
+ docBrowserExt.setCurrentViewable(targetViewable);
1176
+ return true;
1177
+ }
1178
+ return false;
1179
+ }
1180
+ catch (error) {
1181
+ return false;
1182
+ }
1183
+ }
1184
+ loadCurrentViewable() {
1185
+ var _a, _b, _c, _d, _e;
1186
+ if (this.viewables.length === 0 || !this.viewables[this.currentIndex]) {
1187
+ return;
1188
+ }
1189
+ const viewer = this.viewer;
1190
+ // Check current Document Browser state before loading new page
1191
+ const docBrowserExt = viewer.getExtension('Autodesk.DocumentBrowser');
1192
+ const isCurrentlyOpen = (_c = (_b = (_a = docBrowserExt === null || docBrowserExt === void 0 ? void 0 : docBrowserExt.ui) === null || _a === void 0 ? void 0 : _a.panel) === null || _b === void 0 ? void 0 : _b.isVisible()) !== null && _c !== void 0 ? _c : false;
1193
+ if (isCurrentlyOpen) {
1194
+ this.docBrowserShouldBeOpen = true;
1195
+ }
1196
+ // OPTIMIZATION: If Document Browser is open, try to use its API to navigate
1197
+ // This prevents the panel from being refreshed (same behavior as manual thumbnail click)
1198
+ if (isCurrentlyOpen) {
1199
+ const apiSuccess = this.tryUseDocBrowserAPI(this.currentIndex);
1200
+ if (apiSuccess) {
1201
+ // Update pagination state after a short delay
1202
+ setTimeout(() => {
1203
+ this.updatePaginationState();
1204
+ }, 100);
1205
+ return;
1206
+ }
1207
+ }
1208
+ // FALLBACK: Use traditional loadDocumentNode if Document Browser is closed
1209
+ const onGeometryLoaded = () => {
1210
+ viewer.removeEventListener(this.Autodesk.Viewing.GEOMETRY_LOADED_EVENT, onGeometryLoaded);
1211
+ setTimeout(() => {
1212
+ const pageData = {
1213
+ index: this.currentIndex,
1214
+ viewable: this.viewables[this.currentIndex],
1215
+ shouldRestoreDocBrowser: this.docBrowserShouldBeOpen,
1216
+ fromPagination: true,
1217
+ };
1218
+ if (this.onPageChangeCallback) {
1219
+ this.onPageChangeCallback(pageData);
1220
+ }
1221
+ this.eventBus.emit(EVENT_NAMES.PAGE_CHANGED, pageData);
1222
+ this.updatePaginationState();
1223
+ }, TOOLBAR_REFRESH_INTERVALS.GEOMETRY_SETTLE);
1224
+ };
1225
+ viewer.addEventListener(this.Autodesk.Viewing.GEOMETRY_LOADED_EVENT, onGeometryLoaded);
1226
+ viewer
1227
+ .loadDocumentNode(viewer.model.getDocumentNode().getDocument(), this.viewables[this.currentIndex])
1228
+ .then(() => {
1229
+ const pageData = {
1230
+ index: this.currentIndex,
1231
+ viewable: this.viewables[this.currentIndex],
1232
+ shouldRestoreDocBrowser: this.docBrowserShouldBeOpen,
1233
+ immediate: true,
1234
+ fromPagination: true,
1235
+ };
1236
+ if (this.onPageChangeCallback) {
1237
+ this.onPageChangeCallback(pageData);
1238
+ }
1239
+ this.eventBus.emit(EVENT_NAMES.PAGE_CHANGED, pageData);
1240
+ this.updatePaginationState();
1241
+ })
1242
+ .catch((err) => {
1243
+ // Silent error handling
1244
+ });
1245
+ (_e = (_d = this.config).onPageChange) === null || _e === void 0 ? void 0 : _e.call(_d, this.currentIndex);
1246
+ }
1247
+ createPaginationGroup(toolbar) {
1248
+ this.paginationGroup = new this.Autodesk.Viewing.UI.ControlGroup(TOOLBAR_CONTROL_GROUP_IDS.PAGINATION);
1249
+ toolbar.addControl(this.paginationGroup);
1250
+ const prevBtn = new this.Autodesk.Viewing.UI.Button(PAGINATION_BUTTONS.PREV.id);
1251
+ prevBtn.setIcon(PAGINATION_BUTTONS.PREV.icon);
1252
+ prevBtn.addClass('custom-prev-btn');
1253
+ prevBtn.setToolTip(PAGINATION_BUTTONS.PREV.tooltip);
1254
+ prevBtn.onClick = () => this.previousPage();
1255
+ this.paginationGroup.addControl(prevBtn);
1256
+ const labelBtn = new this.Autodesk.Viewing.UI.Button(PAGINATION_BUTTONS.LABEL.id);
1257
+ labelBtn.setToolTip(PAGINATION_BUTTONS.LABEL.tooltip);
1258
+ this.paginationGroup.addControl(labelBtn);
1259
+ const nextBtn = new this.Autodesk.Viewing.UI.Button(PAGINATION_BUTTONS.NEXT.id);
1260
+ nextBtn.setIcon(PAGINATION_BUTTONS.NEXT.icon);
1261
+ nextBtn.addClass('custom-next-btn');
1262
+ nextBtn.setToolTip(PAGINATION_BUTTONS.NEXT.tooltip);
1263
+ nextBtn.onClick = () => this.nextPage();
1264
+ this.paginationGroup.addControl(nextBtn);
1265
+ // Immediately update pagination state after creating group
1266
+ setTimeout(() => {
1267
+ this.updatePaginationState();
1268
+ }, 10);
1269
+ }
1270
+ getPaginationGroup() {
1271
+ return this.paginationGroup;
1272
+ }
1273
+ setPaginationGroup(group) {
1274
+ this.paginationGroup = group;
1275
+ }
1276
+ cleanup() {
1277
+ if (this.modelAddedHandler) {
1278
+ this.viewer.removeEventListener(this.Autodesk.Viewing.MODEL_ADDED_EVENT, this.modelAddedHandler);
1279
+ }
1280
+ if (this.viewer.toolbar && this.paginationGroup) {
1281
+ this.viewer.toolbar.removeControl(this.paginationGroup);
1282
+ this.paginationGroup = null;
1283
+ }
1284
+ }
1285
+ }
424
1286
 
425
- // Call restore at multiple intervals to ensure it works
426
- // Use longer delays since geometry needs to fully load
427
- setTimeout(openDocBrowser, 300);
428
- setTimeout(openDocBrowser, 600);
429
- setTimeout(openDocBrowser, 1000);
430
- setTimeout(restorePanTool, 100);
431
- setTimeout(restorePanTool, 300);
432
- };
433
- ToolbarExtension.prototype.applyDocBrowserStyling = function () {
434
- var viewer = this.viewer;
435
- var ext = viewer.getExtension('Autodesk.DocumentBrowser');
436
- if (ext && ext.ui && ext.ui.panel && ext.ui.panel.container) {
437
- ext.ui.panel.container.style.top = '0';
438
- ext.ui.panel.container.style.left = 'unset';
439
- ext.ui.panel.container.style.right = '0px';
440
- ext.ui.panel.container.style.width = '200px';
441
- ext.ui.panel.container.style.height = '80%';
1287
+ /**
1288
+ * ToolbarManager
1289
+ *
1290
+ * @file ToolbarManager.ts
1291
+ * @description Manages custom toolbar creation and button state
1292
+ * Extracted from ToolbarExtension.js lines 256-479
1293
+ */
1294
+ class ToolbarManager {
1295
+ constructor(viewer, Autodesk, config) {
1296
+ this.subToolbar = null;
1297
+ this.viewer = viewer;
1298
+ this.Autodesk = Autodesk;
1299
+ this.config = config;
1300
+ this.eventBus = EventBus.getInstance();
1301
+ }
1302
+ createToolbar(isMultiPage = false) {
1303
+ const toolbar = this.viewer.toolbar;
1304
+ if (!toolbar)
1305
+ return;
1306
+ this.subToolbar = new this.Autodesk.Viewing.UI.ControlGroup(TOOLBAR_CONTROL_GROUP_IDS.TOOLS);
1307
+ const panBtn = this.createPanButton();
1308
+ const docBtn = this.createDocBrowserButton();
1309
+ const dlBtn = this.createDownloadButton();
1310
+ this.subToolbar.addControl(panBtn);
1311
+ this.subToolbar.addControl(docBtn);
1312
+ this.subToolbar.addControl(dlBtn);
1313
+ toolbar.addControl(this.subToolbar);
1314
+ }
1315
+ createPanButton() {
1316
+ const btn = new this.Autodesk.Viewing.UI.Button(CUSTOM_TOOLBAR_BUTTONS.PAN.id);
1317
+ btn.setIcon(CUSTOM_TOOLBAR_BUTTONS.PAN.icon);
1318
+ btn.addClass('custom-pan-btn');
1319
+ btn.setToolTip(CUSTOM_TOOLBAR_BUTTONS.PAN.tooltip);
1320
+ btn.onClick = () => {
1321
+ var _a, _b;
1322
+ (_b = (_a = this.config).onPanClick) === null || _b === void 0 ? void 0 : _b.call(_a);
1323
+ this.activatePanButton();
1324
+ };
1325
+ return btn;
1326
+ }
1327
+ createDocBrowserButton() {
1328
+ const btn = new this.Autodesk.Viewing.UI.Button(CUSTOM_TOOLBAR_BUTTONS.DOC_BROWSER.id);
1329
+ btn.setIcon(CUSTOM_TOOLBAR_BUTTONS.DOC_BROWSER.icon);
1330
+ btn.addClass('custom-doc-browser-btn');
1331
+ btn.setToolTip(CUSTOM_TOOLBAR_BUTTONS.DOC_BROWSER.tooltip);
1332
+ btn.onClick = () => {
1333
+ var _a, _b;
1334
+ (_b = (_a = this.config).onDocBrowserClick) === null || _b === void 0 ? void 0 : _b.call(_a);
1335
+ this.eventBus.emit(EVENT_NAMES.TOOLBAR_BUTTON_CLICKED, {
1336
+ buttonId: CUSTOM_TOOLBAR_BUTTONS.DOC_BROWSER.id,
1337
+ });
1338
+ };
1339
+ return btn;
1340
+ }
1341
+ createDownloadButton() {
1342
+ const btn = new this.Autodesk.Viewing.UI.Button(CUSTOM_TOOLBAR_BUTTONS.DOWNLOAD.id);
1343
+ btn.setIcon(CUSTOM_TOOLBAR_BUTTONS.DOWNLOAD.icon);
1344
+ btn.addClass('custom-download-btn');
1345
+ btn.setToolTip(CUSTOM_TOOLBAR_BUTTONS.DOWNLOAD.tooltip);
1346
+ btn.onClick = () => {
1347
+ var _a, _b;
1348
+ (_b = (_a = this.config).onDownloadClick) === null || _b === void 0 ? void 0 : _b.call(_a);
1349
+ this.eventBus.emit(EVENT_NAMES.DOWNLOAD_REQUESTED, {
1350
+ filePath: this.config.filePath,
1351
+ });
1352
+ };
1353
+ return btn;
1354
+ }
1355
+ updateButtonState(buttonId, isActive) {
1356
+ if (!this.subToolbar)
1357
+ return;
1358
+ const btn = this.subToolbar.getControl(buttonId);
1359
+ if (btn) {
1360
+ const newState = isActive
1361
+ ? this.Autodesk.Viewing.UI.Button.State.ACTIVE
1362
+ : this.Autodesk.Viewing.UI.Button.State.INACTIVE;
1363
+ btn.setState(newState);
1364
+ }
1365
+ }
1366
+ activatePanButton() {
1367
+ try {
1368
+ this.viewer.setActiveNavigationTool('pan');
1369
+ // Note: Removed EVENT_NAMES.PAN_ACTIVATED emit as no listeners were registered
1370
+ // If you need this event, add a listener first
1371
+ }
1372
+ catch (e) {
1373
+ const originalPanBtn = document.getElementById('toolbar-panTool');
1374
+ if (originalPanBtn) {
1375
+ originalPanBtn.click();
1376
+ }
1377
+ }
1378
+ this.updateButtonState(CUSTOM_TOOLBAR_BUTTONS.PAN.id, true);
1379
+ const customPanBtn = document.querySelector('.custom-pan-btn');
1380
+ if (customPanBtn) {
1381
+ customPanBtn.classList.add('active');
1382
+ customPanBtn.classList.remove('inactive');
1383
+ }
1384
+ }
1385
+ hideDefaultGroups() {
1386
+ const toolbar = this.viewer.toolbar;
1387
+ if (!toolbar)
1388
+ return;
1389
+ DEFAULT_HIDDEN_TOOLBAR_GROUPS.forEach((id) => {
1390
+ const group = toolbar.getControl(id);
1391
+ if (group) {
1392
+ group.setVisible(false);
1393
+ }
1394
+ });
1395
+ }
1396
+ getToolbar() {
1397
+ return this.subToolbar;
442
1398
  }
1399
+ cleanup() {
1400
+ if (this.viewer.toolbar && this.subToolbar) {
1401
+ this.viewer.toolbar.removeControl(this.subToolbar);
1402
+ this.subToolbar = null;
1403
+ }
1404
+ }
1405
+ }
443
1406
 
444
- // Switch to Thumbnail tab
445
- this.switchToThumbnailTab();
446
- };
447
- ToolbarExtension.prototype.switchToThumbnailTab = function () {
448
- // Try multiple selectors to find the thumbnail tab button
449
- var thumbnailTab = document.querySelector('.docking-panel-thumbnail-view') || document.querySelector('[data-i18n="Thumbnails"]') || Array.from(document.querySelectorAll('.adsk-control-group .adsk-button')).find(function (btn) {
450
- return btn.textContent.includes('Thumbnails') || btn.title.includes('Thumbnails');
1407
+ // CRITICAL: Global state to persist across extension unload/reload cycles
1408
+ // This is necessary because Forge Viewer unloads extensions when navigating via thumbnails
1409
+ var GLOBAL_STATE = {
1410
+ viewables: null,
1411
+ docBrowserShouldBeOpen: false,
1412
+ currentIndex: 0
1413
+ };
1414
+ function registerToolbarExtension(Autodesk) {
1415
+ function ToolbarExtension(viewer, options) {
1416
+ var _this = this;
1417
+ Autodesk.Viewing.Extension.call(this, viewer, options);
1418
+ this.viewer = viewer;
1419
+ this.Autodesk = Autodesk;
1420
+ this.options = options;
1421
+ // Initialize EventBus
1422
+ this.eventBus = EventBus.getInstance();
1423
+ // Initialize Managers (Dependency Injection)
1424
+ // CRITICAL: Pass EventBus instance to ensure all managers use the same instance
1425
+ this.paginationManager = new PaginationManager(viewer, Autodesk, {}, this.eventBus);
1426
+ // Register page change callback (direct communication instead of EventBus)
1427
+ this.paginationManager.setPageChangeCallback(function (data) {
1428
+ _this.onPageChanged(data);
451
1429
  });
452
- if (thumbnailTab && !thumbnailTab.classList.contains('active')) {
453
- thumbnailTab.click();
1430
+ this.toolbarManager = new ToolbarManager(viewer, Autodesk, {
1431
+ Autodesk: Autodesk,
1432
+ filePath: options.filePath,
1433
+ onPanClick: function onPanClick() {
1434
+ return _this.handlePanClick();
1435
+ },
1436
+ onDocBrowserClick: function onDocBrowserClick() {
1437
+ return _this.handleDocBrowserClick();
1438
+ },
1439
+ onDownloadClick: function onDownloadClick() {
1440
+ return _this.handleDownloadClick();
1441
+ }
1442
+ });
1443
+ this.docBrowserManager = new DocumentBrowserManager(viewer, {
1444
+ autoOpen: true,
1445
+ persistState: true,
1446
+ defaultTab: 'thumbnails'
1447
+ }, this.eventBus);
1448
+ // Initialize Services
1449
+ this.downloadService = new DownloadService();
1450
+ // Toolbar healing interval
1451
+ this._toolbarInterval = null;
1452
+ this._buttonStateInterval = null;
1453
+ // Store viewables temporarily if toolbar not ready yet
1454
+ this._pendingViewables = null;
1455
+ this._toolbarReady = false;
1456
+ // Track if page change is in progress
1457
+ this._isPageChanging = false;
1458
+ // Bind event handlers
1459
+ this.onToolbarCreatedHandler = this.onToolbarCreated.bind(this);
1460
+ this.onGeometryLoadedHandler = this.onGeometryLoaded.bind(this);
1461
+ this.onPageChangedHandler = this.onPageChanged.bind(this);
1462
+ this.onViewablesSetHandler = this.onViewablesSet.bind(this);
1463
+ }
1464
+ ToolbarExtension.prototype = Object.create(Autodesk.Viewing.Extension.prototype);
1465
+ ToolbarExtension.prototype.constructor = ToolbarExtension;
1466
+ ToolbarExtension.prototype.load = function () {
1467
+ var _this2 = this;
1468
+ this.viewer.addEventListener(Autodesk.Viewing.TOOLBAR_CREATED_EVENT, this.onToolbarCreatedHandler);
1469
+ this.viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, this.onGeometryLoadedHandler);
1470
+ // Subscribe to EventBus events
1471
+ this.eventBus.on(EVENT_NAMES.PAGE_CHANGED, this.onPageChangedHandler);
1472
+ this.eventBus.on(EVENT_NAMES.VIEWABLES_SET, this.onViewablesSetHandler);
1473
+ // Subscribe to Document Browser open/close events
1474
+ this.eventBus.on(EVENT_NAMES.DOC_BROWSER_OPENED, function () {
1475
+ GLOBAL_STATE.docBrowserShouldBeOpen = true;
1476
+ setTimeout(function () {
1477
+ return _this2.syncDocBrowserButtonState();
1478
+ }, TOOLBAR_REFRESH_INTERVALS.DOM_SETTLE);
1479
+ });
1480
+ this.eventBus.on(EVENT_NAMES.DOC_BROWSER_CLOSED, function () {
1481
+ GLOBAL_STATE.docBrowserShouldBeOpen = false;
1482
+ setTimeout(function () {
1483
+ return _this2.syncDocBrowserButtonState();
1484
+ }, TOOLBAR_REFRESH_INTERVALS.DOM_SETTLE);
1485
+ });
1486
+ // CRITICAL: Restore state from GLOBAL_STATE if available
1487
+ // This handles the case where extension was unloaded and reloaded (e.g., thumbnail navigation)
1488
+ if (GLOBAL_STATE.viewables && GLOBAL_STATE.viewables.length > 0) {
1489
+ // Restore viewables to pagination manager
1490
+ this._pendingViewables = GLOBAL_STATE.viewables;
1491
+ // Restore Document Browser state
1492
+ if (GLOBAL_STATE.docBrowserShouldBeOpen) {
1493
+ this.docBrowserManager.setShouldRemainOpen(true);
1494
+ // CRITICAL: Sync button state immediately and repeatedly after load
1495
+ // This ensures button is active when extension reloads after using _changeModelFn
1496
+ var syncTimes = [50, 100, 150, 200, 300, 400, 500];
1497
+ syncTimes.forEach(function (delay) {
1498
+ setTimeout(function () {
1499
+ _this2.syncDocBrowserButtonState();
1500
+ }, delay);
1501
+ });
1502
+ }
454
1503
  }
1504
+ return true;
455
1505
  };
456
- ToolbarExtension.prototype.activatePanTool = function () {
457
- var viewer = this.viewer;
458
- var toolbar = viewer.toolbar;
459
- if (!toolbar) return;
460
-
461
- // Activate pan tool using viewer API
462
- try {
463
- viewer.setActiveNavigationTool('pan');
464
- } catch (e) {
465
- // Fallback: click the original pan button
466
- var originalPanBtn = document.getElementById('toolbar-panTool');
467
- if (originalPanBtn) {
468
- originalPanBtn.click();
469
- }
1506
+ ToolbarExtension.prototype.unload = function () {
1507
+ // CRITICAL: Save state to GLOBAL_STATE before unloading
1508
+ // This allows state to persist across unload/reload cycles (e.g., thumbnail navigation)
1509
+ var totalPages = this.paginationManager.getTotalPages();
1510
+ if (totalPages > 0) {
1511
+ GLOBAL_STATE.viewables = this.paginationManager.getViewables();
1512
+ GLOBAL_STATE.currentIndex = this.paginationManager.getCurrentPage();
1513
+ GLOBAL_STATE.docBrowserShouldBeOpen = this.docBrowserManager.getShouldRemainOpen();
470
1514
  }
471
-
472
- // Update custom pan button visual state
473
- var toolGroup = toolbar.getControl('custom-tool-group');
474
- if (toolGroup) {
475
- var panBtn = toolGroup.getControl('custom-pan-btn');
476
- if (panBtn) {
477
- panBtn.setState(Autodesk.Viewing.UI.Button.State.ACTIVE);
478
- if (panBtn.container) {
479
- panBtn.container.classList.add('active');
480
- panBtn.container.classList.remove('inactive');
481
- }
482
- }
1515
+ // Cleanup event listeners
1516
+ this.viewer.removeEventListener(Autodesk.Viewing.TOOLBAR_CREATED_EVENT, this.onToolbarCreatedHandler);
1517
+ this.viewer.removeEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, this.onGeometryLoadedHandler);
1518
+ // Unsubscribe from EventBus
1519
+ this.eventBus.off(EVENT_NAMES.PAGE_CHANGED, this.onPageChangedHandler);
1520
+ this.eventBus.off(EVENT_NAMES.VIEWABLES_SET, this.onViewablesSetHandler);
1521
+ // Cleanup intervals
1522
+ if (this._toolbarInterval) {
1523
+ clearInterval(this._toolbarInterval);
1524
+ this._toolbarInterval = null;
483
1525
  }
1526
+ return true;
484
1527
  };
485
- ToolbarExtension.prototype.onToolbarCreated = function (toolbar) {
1528
+ /**
1529
+ * Called when toolbar is created
1530
+ * Delegates to managers for setup
1531
+ */
1532
+ ToolbarExtension.prototype.onToolbarCreated = function () {
486
1533
  var _this3 = this;
1534
+ // Initial toolbar setup
487
1535
  this.refreshToolbar();
1536
+ // Add listener for geometry loaded to refresh toolbar
488
1537
  this.viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, function () {
489
1538
  setTimeout(function () {
490
1539
  _this3.refreshToolbar();
491
- _this3.updateCurrentIndexFromModel();
492
- }, 100);
1540
+ }, TOOLBAR_REFRESH_INTERVALS.GEOMETRY_SETTLE);
493
1541
  });
1542
+ // Add listener for toolbar created event
494
1543
  this.viewer.addEventListener(Autodesk.Viewing.TOOLBAR_CREATED_EVENT, function () {
495
1544
  setTimeout(function () {
496
1545
  _this3.refreshToolbar();
497
- }, 10);
1546
+ }, TOOLBAR_REFRESH_INTERVALS.BUTTON_STATE_CHECK);
498
1547
  });
499
- if (this._toolbarInterval) clearInterval(this._toolbarInterval);
500
- this._toolbarInterval = setInterval(function () {
501
- if (_this3.viewer && _this3.viewer.toolbar) {
502
- var toolGroup = _this3.viewer.toolbar.getControl('custom-tool-group');
503
- var pagGroup = _this3.viewer.toolbar.getControl('custom-pagination-group');
504
- if (!toolGroup || !pagGroup) {
505
- _this3.refreshToolbar();
506
- }
507
- _this3.checkButtonState();
508
- }
509
- }, 200);
510
- };
511
- ToolbarExtension.prototype.checkButtonState = function () {
512
- var viewer = this.viewer;
513
- if (!viewer.toolbar) return;
514
- var toolGroup = viewer.toolbar.getControl('custom-tool-group');
515
- if (!toolGroup) return;
516
-
517
- // Check Document Browser button state
518
- var docBtn = toolGroup.getControl('custom-doc-browser-btn');
519
- if (docBtn) {
520
- var ext = viewer.getExtension('Autodesk.DocumentBrowser');
521
- var isVisible = ext && ext.ui && ext.ui.panel && ext.ui.panel.isVisible();
522
- var newState = isVisible ? Autodesk.Viewing.UI.Button.State.ACTIVE : Autodesk.Viewing.UI.Button.State.INACTIVE;
523
- if (docBtn.getState() !== newState) {
524
- docBtn.setState(newState);
525
- }
526
-
527
- // Force CSS class update to ensure visual state is correct
528
- if (docBtn.container) {
529
- if (isVisible) {
530
- docBtn.container.classList.add('active');
531
- docBtn.container.classList.remove('inactive');
532
- } else {
533
- docBtn.container.classList.remove('active');
534
- docBtn.container.classList.add('inactive');
535
- }
536
- }
537
- }
538
-
539
- // Check Pan button state - ensure it stays active
540
- var panBtn = toolGroup.getControl('custom-pan-btn');
541
- if (panBtn) {
542
- // Check if pan tool is the active tool
543
- var activeTool = viewer.getActiveNavigationTool();
544
- var isPanActive = activeTool === 'pan';
545
- if (isPanActive) {
546
- panBtn.setState(Autodesk.Viewing.UI.Button.State.ACTIVE);
547
- if (panBtn.container) {
548
- panBtn.container.classList.add('active');
549
- panBtn.container.classList.remove('inactive');
550
- }
551
- }
552
- }
553
- };
554
- var originalUnload = ToolbarExtension.prototype.unload;
555
- ToolbarExtension.prototype.unload = function () {
556
- if (this._toolbarInterval) {
557
- clearInterval(this._toolbarInterval);
558
- this._toolbarInterval = null;
559
- }
560
- return originalUnload.call(this);
1548
+ // Start toolbar healing mechanism (also handles button state sync)
1549
+ this.startToolbarHealing();
561
1550
  };
1551
+ /**
1552
+ * Refresh toolbar - recreate if needed
1553
+ * This is the core method that handles toolbar persistence
1554
+ */
562
1555
  ToolbarExtension.prototype.refreshToolbar = function () {
563
- var viewer = this.viewer;
564
- var toolbar = viewer.toolbar;
1556
+ var _this4 = this;
1557
+ var toolbar = this.viewer.toolbar;
565
1558
  if (!toolbar) return;
566
- var defaultGroups = ['settingsTools', 'modelTools', 'navTools'];
567
- defaultGroups.forEach(function (id) {
568
- var group = toolbar.getControl(id);
569
- if (group) {
570
- group.setVisible(false);
571
- if (group.container) {
572
- group.container.style.display = 'none';
573
- }
574
- }
575
- });
1559
+ // Hide default groups
1560
+ this.toolbarManager.hideDefaultGroups();
1561
+ // Check and recreate tool group if needed
576
1562
  var toolGroup = toolbar.getControl('custom-tool-group');
577
- var pagGroup = toolbar.getControl('custom-pagination-group');
578
1563
  if (!toolGroup) {
579
- if (this.subToolbar) {
580
- this.subToolbar = null;
581
- }
582
- this.createToolGroup(toolbar);
1564
+ this.toolbarManager.createToolbar();
583
1565
  } else {
584
1566
  toolGroup.setVisible(true);
585
- this.subToolbar = toolGroup;
586
1567
  }
1568
+ // Check and recreate pagination group if needed
1569
+ var pagGroup = toolbar.getControl('custom-pagination-group');
587
1570
  if (!pagGroup) {
588
- if (this.paginationGroup) {
589
- this.paginationGroup = null;
590
- }
591
- this.createPaginationGroup(toolbar);
1571
+ this.paginationManager.createPaginationGroup(toolbar);
592
1572
  } else {
593
- // Don't force visibility here - let updatePaginationState handle it
594
- this.paginationGroup = pagGroup;
1573
+ // Ensure pagination manager has correct reference to existing group
1574
+ this.paginationManager.setPaginationGroup(pagGroup);
1575
+ pagGroup.setVisible(true);
1576
+ }
1577
+ // Mark toolbar as ready
1578
+ this._toolbarReady = true;
1579
+ // Process pending viewables if any
1580
+ if (this._pendingViewables) {
1581
+ this.paginationManager.setViewables(this._pendingViewables);
1582
+ // Auto-open Document Browser for all documents (single or multi-page)
1583
+ var isMultiPage = this._pendingViewables.length > 1;
1584
+ this.docBrowserManager.autoOpenIfMultiPage(isMultiPage);
1585
+ this._pendingViewables = null;
595
1586
  }
1587
+ // Show toolbar
596
1588
  toolbar.setVisible(true);
597
- this.updatePaginationState();
1589
+ // ALWAYS update pagination state after toolbar refresh
1590
+ // Use multiple timeouts to ensure it sticks
1591
+ var updateTimes = [10, 30, 50, 100];
1592
+ updateTimes.forEach(function (delay) {
1593
+ setTimeout(function () {
1594
+ _this4.paginationManager.updatePaginationState();
1595
+ }, delay);
1596
+ });
1597
+ // CRITICAL: Sync Document Browser button state after toolbar refresh
1598
+ // This ensures button is active if panel is open (especially after reload)
1599
+ var syncTimes = [50, 100, 150, 200];
1600
+ syncTimes.forEach(function (delay) {
1601
+ setTimeout(function () {
1602
+ var isOpen = _this4.docBrowserManager.isOpen();
1603
+ if (isOpen) {
1604
+ _this4.syncDocBrowserButtonState();
1605
+ }
1606
+ }, delay);
1607
+ });
1608
+ // Activate pan tool
1609
+ this.toolbarManager.activatePanButton();
598
1610
  };
599
- ToolbarExtension.prototype.createToolGroup = function (toolbar) {
600
- var _this4 = this;
601
- var viewer = this.viewer;
602
- this.subToolbar = new Autodesk.Viewing.UI.ControlGroup('custom-tool-group');
603
- toolbar.addControl(this.subToolbar);
604
- var panBtn = new Autodesk.Viewing.UI.Button('custom-pan-btn');
605
- panBtn.setIcon('adsk-icon-pan');
606
- panBtn.setToolTip('Pan');
607
- panBtn.onClick = function () {
608
- var originalPanBtn = document.getElementById('toolbar-panTool');
609
- if (originalPanBtn) {
610
- originalPanBtn.click();
611
- }
612
-
613
- // Set this button to active state
614
- if (panBtn.container) {
615
- panBtn.container.classList.add('active');
616
- panBtn.container.classList.remove('inactive');
1611
+ /**
1612
+ * Called when geometry is loaded
1613
+ * Restores button states
1614
+ */
1615
+ ToolbarExtension.prototype.onGeometryLoaded = function () {
1616
+ var _this5 = this;
1617
+ setTimeout(function () {
1618
+ _this5.toolbarManager.activatePanButton();
1619
+ _this5.paginationManager.updatePaginationState();
1620
+ }, TOOLBAR_REFRESH_INTERVALS.GEOMETRY_SETTLE);
1621
+ };
1622
+ /**
1623
+ * Called when page changes
1624
+ * Restores Document Browser and toolbar states
1625
+ */
1626
+ ToolbarExtension.prototype.onPageChanged = function (data) {
1627
+ var _this6 = this;
1628
+ if (!data || data.test) {
1629
+ return;
1630
+ }
1631
+ // Mark that page change is in progress to prevent premature pagination updates
1632
+ this._isPageChanging = true;
1633
+ // CRITICAL FIX: If page changed from Document Browser (thumbnails), sync button state IMMEDIATELY
1634
+ // This ensures the button is active when user navigates via thumbnails
1635
+ if (data.fromDocBrowser) {
1636
+ // Sync button state right away (before any other operations)
1637
+ this.syncDocBrowserButtonState();
1638
+ // Force recreate pagination group to ensure display updates
1639
+ var toolbar = this.viewer.toolbar;
1640
+ if (toolbar) {
1641
+ var pagGroup = toolbar.getControl('custom-pagination-group');
1642
+ if (pagGroup) {
1643
+ toolbar.removeControl(pagGroup);
1644
+ this.paginationManager.setPaginationGroup(null);
1645
+ }
617
1646
  }
618
- panBtn.setState(Autodesk.Viewing.UI.Button.State.ACTIVE);
619
- };
620
- this.subToolbar.addControl(panBtn);
621
- var docBrowserBtn = new Autodesk.Viewing.UI.Button('custom-doc-browser-btn');
622
- docBrowserBtn.setIcon('adsk-icon-documentModels');
623
- docBrowserBtn.setToolTip('Document Browser');
624
- var self = this;
625
- docBrowserBtn.onClick = function () {
626
- var ext = viewer.getExtension('Autodesk.DocumentBrowser');
627
- if (ext && ext.ui) {
628
- ext.ui.togglePanel();
629
- // Update flag based on new state
1647
+ }
1648
+ // Refresh toolbar to ensure both tool and pagination groups are restored
1649
+ this.refreshToolbar();
1650
+ // Restore Document Browser panel if needed
1651
+ if (data.shouldRestoreDocBrowser) {
1652
+ this.docBrowserManager.restoreState();
1653
+ // Force sync button state multiple times after restore
1654
+ var syncDelays = data.fromDocBrowser ? [10, 30, 50, 80, 100, 150, 200, 250, 300] : [50, 100, 150, 200, 250, 300];
1655
+ syncDelays.forEach(function (delay) {
630
1656
  setTimeout(function () {
631
- self.docBrowserShouldBeOpen = ext.ui.panel && ext.ui.panel.isVisible();
632
- }, 50);
1657
+ _this6.syncDocBrowserButtonState();
1658
+ }, delay);
1659
+ });
1660
+ }
1661
+ // Sync Document Browser button state and update pagination display at multiple intervals
1662
+ var syncIntervals = data.fromDocBrowser ? [10, 30, 50, 80, 100, 150, 200, 250, 300] : [50, 100, 150, 200, 250, 300];
1663
+ syncIntervals.forEach(function (delay, index) {
1664
+ setTimeout(function () {
1665
+ _this6.syncDocBrowserButtonState();
1666
+ _this6.paginationManager.updatePaginationState();
1667
+ if (index === 0) {
1668
+ _this6._isPageChanging = false;
1669
+ }
1670
+ }, delay);
1671
+ });
1672
+ // Additional pagination updates at various intervals to handle race conditions
1673
+ var updateIntervals = data.fromDocBrowser ? [60, 100, 120, 150, 180, 200, 250, 300, 350, 400, 500] : [100, 150, 200, 300];
1674
+ updateIntervals.forEach(function (delay) {
1675
+ setTimeout(function () {
1676
+ _this6.paginationManager.updatePaginationState();
1677
+ }, delay);
1678
+ });
1679
+ };
1680
+ /**
1681
+ * Called when viewables are set (from FileLoader)
1682
+ * Sets viewables to pagination manager and auto-opens Document Browser
1683
+ */
1684
+ ToolbarExtension.prototype.onViewablesSet = function (data) {
1685
+ if (data && data.viewables) {
1686
+ // CRITICAL: Save viewables to GLOBAL_STATE for persistence across unload/reload
1687
+ GLOBAL_STATE.viewables = data.viewables;
1688
+ // Check if toolbar is ready
1689
+ if (this._toolbarReady) {
1690
+ // Toolbar ready - set viewables immediately
1691
+ this.paginationManager.setViewables(data.viewables);
1692
+ // Auto-open Document Browser for all documents
1693
+ var isMultiPage = data.viewables.length > 1;
1694
+ GLOBAL_STATE.docBrowserShouldBeOpen = true;
1695
+ this.docBrowserManager.autoOpenIfMultiPage(isMultiPage);
1696
+ } else {
1697
+ // Toolbar not ready yet - store viewables for later
1698
+ this._pendingViewables = data.viewables;
633
1699
  }
634
- };
635
- this.subToolbar.addControl(docBrowserBtn);
636
- var downloadBtn = new Autodesk.Viewing.UI.Button('custom-download-btn');
637
- downloadBtn.setIcon('adsk-icon-custom-download');
638
- downloadBtn.setToolTip('Download File');
639
- downloadBtn.onClick = /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
640
- var fileUrl, response, blob, urlPath, filename, blobUrl, anchor, _t2;
641
- return _regenerator().w(function (_context2) {
642
- while (1) switch (_context2.p = _context2.n) {
643
- case 0:
644
- if (!(_this4.options && _this4.options.filePath)) {
645
- _context2.n = 7;
646
- break;
647
- }
648
- _context2.p = 1;
649
- fileUrl = _this4.options.filePath;
650
- _context2.n = 2;
651
- return fetch(fileUrl);
652
- case 2:
653
- response = _context2.v;
654
- if (response.ok) {
655
- _context2.n = 3;
656
- break;
657
- }
658
- throw new Error("HTTP error! status: ".concat(response.status));
659
- case 3:
660
- _context2.n = 4;
661
- return response.blob();
662
- case 4:
663
- blob = _context2.v;
664
- urlPath = new URL(fileUrl).pathname;
665
- filename = decodeURIComponent(urlPath.split('/').pop()) || 'document.pdf';
666
- blobUrl = URL.createObjectURL(blob);
667
- anchor = document.createElement('a');
668
- anchor.href = blobUrl;
669
- anchor.download = filename;
670
- anchor.style.display = 'none';
671
- document.body.appendChild(anchor);
672
- anchor.click();
673
- document.body.removeChild(anchor);
674
- URL.revokeObjectURL(blobUrl);
675
- _context2.n = 6;
676
- break;
677
- case 5:
678
- _context2.p = 5;
679
- _t2 = _context2.v;
680
- console.error('Download error:', _t2);
681
- alert('Unable to download file: ' + _t2.message);
682
- case 6:
683
- _context2.n = 8;
684
- break;
685
- case 7:
686
- console.warn('FilePath not available in extension options');
687
- alert('File path not available for download');
688
- case 8:
689
- return _context2.a(2);
690
- }
691
- }, _callee2, null, [[1, 5]]);
692
- }));
693
- this.subToolbar.addControl(downloadBtn);
1700
+ }
694
1701
  };
695
- ToolbarExtension.prototype.createPaginationGroup = function (toolbar) {
696
- var _this5 = this;
697
- this.paginationGroup = new Autodesk.Viewing.UI.ControlGroup('custom-pagination-group');
698
- toolbar.addControl(this.paginationGroup);
699
- var prevBtn = new Autodesk.Viewing.UI.Button('prev-page-btn');
700
- prevBtn.setIcon('adsk-icon-custom-prev');
701
- prevBtn.addClass('custom-prev-btn');
702
- prevBtn.setToolTip('Previous Page');
703
- prevBtn.onClick = function () {
704
- if (_this5.viewables.length > 0) {
705
- _this5.currentIndex = (_this5.currentIndex - 1 + _this5.viewables.length) % _this5.viewables.length;
706
- _this5.loadCurrentViewable();
1702
+ /**
1703
+ * Start toolbar healing mechanism
1704
+ * Polls every 8ms to check if toolbar needs recreation
1705
+ */
1706
+ ToolbarExtension.prototype.startToolbarHealing = function () {
1707
+ var _this7 = this;
1708
+ if (this._toolbarInterval) {
1709
+ clearInterval(this._toolbarInterval);
1710
+ }
1711
+ // Store last known Document Browser state to detect changes
1712
+ this._lastDocBrowserState = null;
1713
+ this._healingCounter = 0;
1714
+ this._toolbarInterval = setInterval(function () {
1715
+ if (_this7.viewer && _this7.viewer.toolbar) {
1716
+ _this7._healingCounter++;
1717
+ var toolbar = _this7.viewer.toolbar;
1718
+ var toolGroup = toolbar.getControl('custom-tool-group');
1719
+ var pagGroup = toolbar.getControl('custom-pagination-group');
1720
+ // Refresh toolbar if either group is missing
1721
+ if (!toolGroup || !pagGroup) {
1722
+ _this7.refreshToolbar();
1723
+ }
1724
+ // Check if Document Browser panel state changed
1725
+ var currentDocBrowserState = _this7.docBrowserManager.isOpen();
1726
+ if (_this7._lastDocBrowserState !== null && _this7._lastDocBrowserState !== currentDocBrowserState) {
1727
+ // Update shouldRemainOpen flag based on current state
1728
+ if (currentDocBrowserState) {
1729
+ _this7.docBrowserManager.setShouldRemainOpen(true);
1730
+ }
1731
+ // Sync button state immediately when state changes
1732
+ _this7.syncDocBrowserButtonState();
1733
+ }
1734
+ _this7._lastDocBrowserState = currentDocBrowserState;
1735
+ // CRITICAL: Sync button state periodically (every ~50ms = every 6 healing cycles)
1736
+ // This catches thumbnail navigation where panel stays open but button might lose active state
1737
+ if (_this7._healingCounter % 6 === 0) {
1738
+ _this7.syncDocBrowserButtonState();
1739
+ }
1740
+ // ADDITIONAL: If panel is currently open, ensure button is always active
1741
+ // This is the ultimate fallback to handle thumbnail navigation
1742
+ if (currentDocBrowserState && _this7._healingCounter % 3 === 0) {
1743
+ // Check button state and force sync if needed (every ~24ms = every 3 healing cycles)
1744
+ var _toolGroup = toolbar.getControl('custom-tool-group');
1745
+ if (_toolGroup) {
1746
+ var docBtn = _toolGroup.getControl('custom-doc-browser-btn');
1747
+ if (docBtn) {
1748
+ var currentBtnState = docBtn.getState();
1749
+ var ACTIVE_STATE = _this7.Autodesk.Viewing.UI.Button.State.ACTIVE;
1750
+ // If panel is open but button is not active, force sync
1751
+ if (currentBtnState !== ACTIVE_STATE) {
1752
+ _this7.syncDocBrowserButtonState();
1753
+ }
1754
+ }
1755
+ }
1756
+ }
707
1757
  }
708
- };
709
- this.paginationGroup.addControl(prevBtn);
710
- var labelBtn = new Autodesk.Viewing.UI.Button('total-page-label');
711
- labelBtn.setToolTip('Page info');
712
- this.paginationGroup.addControl(labelBtn);
713
- var nextBtn = new Autodesk.Viewing.UI.Button('next-page-btn');
714
- nextBtn.setIcon('adsk-icon-custom-next');
715
- nextBtn.addClass('custom-next-btn');
716
- nextBtn.setToolTip('Next Page');
717
- nextBtn.onClick = function () {
718
- if (_this5.viewables.length > 0) {
719
- _this5.currentIndex = (_this5.currentIndex + 1) % _this5.viewables.length;
720
- _this5.loadCurrentViewable();
1758
+ }, TOOLBAR_REFRESH_INTERVALS.HEALING_POLL);
1759
+ };
1760
+ /**
1761
+ * Start button state synchronization
1762
+ * Polls to keep custom button states in sync with actual states
1763
+ */
1764
+ ToolbarExtension.prototype.startButtonStateSync = function () {
1765
+ var _this8 = this;
1766
+ if (this._buttonStateInterval) {
1767
+ clearInterval(this._buttonStateInterval);
1768
+ }
1769
+ this._buttonStateInterval = setInterval(function () {
1770
+ _this8.syncDocBrowserButtonState();
1771
+ }, TOOLBAR_REFRESH_INTERVALS.BUTTON_STATE_CHECK);
1772
+ };
1773
+ /**
1774
+ * Set Document Browser button active state
1775
+ */
1776
+ ToolbarExtension.prototype.setDocBrowserButtonActive = function (isActive) {
1777
+ var toolbar = this.viewer.toolbar;
1778
+ if (!toolbar) return;
1779
+ var toolGroup = toolbar.getControl('custom-tool-group');
1780
+ if (!toolGroup) return;
1781
+ var docBtn = toolGroup.getControl(CUSTOM_TOOLBAR_BUTTONS.DOC_BROWSER.id);
1782
+ if (!docBtn) return;
1783
+ var newState = isActive ? Autodesk.Viewing.UI.Button.State.ACTIVE : Autodesk.Viewing.UI.Button.State.INACTIVE;
1784
+ docBtn.setState(newState);
1785
+ };
1786
+ /**
1787
+ * Sync Document Browser button state with panel visibility
1788
+ */
1789
+ ToolbarExtension.prototype.syncDocBrowserButtonState = function () {
1790
+ var toolbar = this.viewer.toolbar;
1791
+ if (!toolbar) return;
1792
+ var toolGroup = toolbar.getControl('custom-tool-group');
1793
+ if (!toolGroup) return;
1794
+ var docBtn = toolGroup.getControl(CUSTOM_TOOLBAR_BUTTONS.DOC_BROWSER.id);
1795
+ if (!docBtn) return;
1796
+ var isOpen = this.docBrowserManager.isOpen();
1797
+ var ACTIVE_STATE = Autodesk.Viewing.UI.Button.State.ACTIVE;
1798
+ var INACTIVE_STATE = Autodesk.Viewing.UI.Button.State.INACTIVE;
1799
+ var expectedState = isOpen ? ACTIVE_STATE : INACTIVE_STATE;
1800
+ // Set button state via API
1801
+ docBtn.setState(expectedState);
1802
+ // Also update DOM class directly to ensure visual state
1803
+ var btnElement = docBtn.container;
1804
+ if (btnElement) {
1805
+ if (isOpen) {
1806
+ btnElement.classList.add('active');
1807
+ btnElement.classList.remove('inactive');
1808
+ } else {
1809
+ btnElement.classList.remove('active');
1810
+ btnElement.classList.add('inactive');
721
1811
  }
722
- };
723
- this.paginationGroup.addControl(nextBtn);
1812
+ }
1813
+ };
1814
+ /**
1815
+ * Handle Pan button click
1816
+ */
1817
+ ToolbarExtension.prototype.handlePanClick = function () {
1818
+ this.toolbarManager.activatePanButton();
724
1819
  };
1820
+ /**
1821
+ * Handle Document Browser button click
1822
+ */
1823
+ ToolbarExtension.prototype.handleDocBrowserClick = function () {
1824
+ var _this9 = this;
1825
+ this.docBrowserManager.togglePanel();
1826
+ // Sync button state with new panel state at multiple intervals
1827
+ [50, 100, 150, 200].forEach(function (delay) {
1828
+ setTimeout(function () {
1829
+ _this9.syncDocBrowserButtonState();
1830
+ }, delay);
1831
+ });
1832
+ };
1833
+ /**
1834
+ * Handle Download button click
1835
+ */
1836
+ ToolbarExtension.prototype.handleDownloadClick = /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
1837
+ return _regenerator().w(function (_context) {
1838
+ while (1) switch (_context.p = _context.n) {
1839
+ case 0:
1840
+ _context.p = 0;
1841
+ _context.n = 1;
1842
+ return this.downloadService.downloadFile(this.options.filePath);
1843
+ case 1:
1844
+ _context.n = 3;
1845
+ break;
1846
+ case 2:
1847
+ _context.p = 2;
1848
+ _context.v;
1849
+ case 3:
1850
+ return _context.a(2);
1851
+ }
1852
+ }, _callee, this, [[0, 2]]);
1853
+ }));
1854
+ // Register extension
725
1855
  Autodesk.Viewing.theExtensionManager.registerExtension('ToolbarExtension', ToolbarExtension);
726
1856
  }
727
1857
 
@@ -814,156 +1944,110 @@ function styleInject(css, ref) {
814
1944
  }
815
1945
  }
816
1946
 
817
- var css_248z$1 = "#navTools,\n#modelTools,\n#settingsTools,\n#measureTools {\n display: none !important;\n visibility: hidden !important;\n}\n#guiviewer3d-toolbar {\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n gap: 20px;\n position: fixed !important;\n bottom: 10px !important;\n left: 50% !important;\n transform: translateX(-50%) !important;\n width: auto !important;\n border-radius: 4px !important;\n padding: 8px 12px !important;\n}\n#custom-tool-group {\n display: flex !important;\n margin: 0 !important;\n padding: 0 !important;\n}\n#custom-pagination-group {\n display: flex;\n position: relative !important;\n margin: 0 !important;\n padding: 0 !important;\n transform: none !important;\n left: auto !important;\n}\n\n/* Document Browser Panel Styling */\n.docking-panel.document-browser-panel,\n.adsk-viewing-viewer .docking-panel.document-browser-panel {\n min-height: 400px !important;\n height: 80% !important;\n max-height: 80vh !important;\n}\n\n/* Ensure thumbnail container has proper height */\n.docking-panel.document-browser-panel .docking-panel-container-solid-color-a,\n.document-browser-panel .treeview,\n.document-browser-panel .thumbnails-container {\n height: calc(100% - 50px) !important;\n min-height: 350px !important;\n overflow-y: auto !important;\n}\n\n/* Thumbnail cards styling */\n.document-browser-panel .thumbnail-item,\n.document-browser-panel .thumbnail {\n display: flex !important;\n visibility: visible !important;\n}\n\n/* Ensure thumbnail images are visible */\n.document-browser-panel .thumbnail img,\n.document-browser-panel .thumbnail-item img {\n display: block !important;\n visibility: visible !important;\n max-width: 100% !important;\n height: auto !important;\n}\n";
1947
+ var css_248z$1 = "#navTools,\n#modelTools,\n#settingsTools,\n#measureTools {\n display: none !important;\n visibility: hidden !important;\n}\n#guiviewer3d-toolbar {\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n gap: 20px;\n position: fixed !important;\n bottom: 10px !important;\n left: 50% !important;\n transform: translateX(-50%) !important;\n width: auto !important;\n border-radius: 4px !important;\n padding: 8px 12px !important;\n}\n#custom-tool-group {\n display: flex !important;\n margin: 0 !important;\n padding: 0 !important;\n}\n#custom-pagination-group {\n display: flex;\n position: relative !important;\n margin: 0 !important;\n padding: 0 !important;\n transform: none !important;\n left: auto !important;\n}\n";
818
1948
  styleInject(css_248z$1);
819
1949
 
820
- var css_248z = "/* Custom icon styles for toolbar using SVG */\n\n/* Download icon - custom SVG */\n.adsk-icon-custom-download::before {\n content: '';\n display: inline-block;\n width: 24px;\n height: 24px;\n background-image: url('data:image/svg+xml;utf8,\\\n<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\">\\\n<path fill=\"%23FFFFFF\" d=\"M9.293 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V4.707A1 1 0 0 0 13.707 4L10 .293A1 1 0 0 0 9.293 0M9.5 3.5v-2l3 3h-2a1 1 0 0 1-1-1m-1 4v3.793l1.146-1.147a.5.5 0 0 1 .708.708l-2 2a.5.5 0 0 1-.708 0l-2-2a.5.5 0 0 1 .708-.708L7.5 11.293V7.5a.5.5 0 0 1 1 0\"/>\\\n</svg>');\n background-repeat: no-repeat;\n background-position: center;\n background-size: 16px 16px;\n}\n\n/* Previous page icon - Font Awesome chevron left */\n.adsk-icon-custom-prev::before {\n content: '';\n display: inline-block;\n width: 24px;\n height: 24px;\n background-image: url('data:image/svg+xml;utf8,\\\n<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\">\\\n<path fill=\"%23FFFFFF\" d=\"M16 14a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2zm-4.5-6.5H5.707l2.147-2.146a.5.5 0 1 0-.708-.708l-3 3a.5.5 0 0 0 0 .708l3 3a.5.5 0 0 0 .708-.708L5.707 8.5H11.5a.5.5 0 0 0 0-1\"/>\\\n</svg>');\n background-repeat: no-repeat;\n background-position: center;\n background-size: 16px 16px;\n}\n\n/* Next page icon - Font Awesome chevron right */\n.adsk-icon-custom-next::before {\n content: '';\n display: inline-block;\n width: 24px;\n height: 24px;\n background-image: url('data:image/svg+xml;utf8,\\\n<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\">\\\n<path fill=\"%23FFFFFF\" d=\"M0 14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2a2 2 0 0 0-2 2zm4.5-6.5h5.793L8.146 5.354a.5.5 0 1 1 .708-.708l3 3a.5.5 0 0 1 0 .708l-3 3a.5.5 0 0 1-.708-.708L10.293 8.5H4.5a.5.5 0 0 1 0-1\"/>\\\n</svg>');\n background-repeat: no-repeat;\n background-position: center;\n background-size: 16px 16px;\n}\n\n/* Fallback for adsk-icon-caret-left and adsk-icon-caret-right if not defined */\n.adsk-icon-caret-left::before {\n content: '[';\n font-family: 'adsk-viewing';\n}\n\n.adsk-icon-caret-right::before {\n content: ']';\n font-family: 'adsk-viewing';\n}\n\n/* Comment icon - custom SVG (Smiley) */\n.adsk-icon-custom-comment::before {\n content: '';\n display: inline-block;\n width: 24px;\n height: 24px;\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"%23FFFFFF\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"></circle><path d=\"M8 14s1.5 2 4 2 4-2 4-2\"></path><line x1=\"9\" y1=\"9\" x2=\"9.01\" y2=\"9\"></line><line x1=\"15\" y1=\"9\" x2=\"15.01\" y2=\"9\"></line></svg>');\n background-repeat: no-repeat;\n background-position: center;\n background-size: contain;\n}\n\n/* Save icon - custom SVG */\n.adsk-icon-custom-save::before {\n content: '';\n display: inline-block;\n width: 24px;\n height: 24px;\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"%23FFFFFF\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z\"></path><polyline points=\"17 21 17 13 7 13 7 21\"></polyline><polyline points=\"7 3 7 8 15 8\"></polyline></svg>');\n background-repeat: no-repeat;\n background-position: center;\n background-size: contain;\n}\n";
1950
+ var css_248z = "/* Custom icon styles for toolbar using SVG */\n/* Download icon - custom SVG (white, thicker stroke) */\n.adsk-icon-custom-download::before {\n content: '';\n display: inline-block;\n width: 24px;\n height: 24px;\n background-image: url('data:image/svg+xml;utf8,\\\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"%23ffffff\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\\\n <path d=\"M4.75 17.25a.75.75 0 0 1 .75.75v2.25c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25V18a.75.75 0 0 1 1.5 0v2.25A1.75 1.75 0 0 1 18.25 22H5.75A1.75 1.75 0 0 1 4 20.25V18a.75.75 0 0 1 .75-.75Z\"/>\\\n <path d=\"M5.22 9.97a.749.749 0 0 1 1.06 0l4.97 4.969V2.75a.75.75 0 0 1 1.5 0v12.189l4.97-4.969a.749.749 0 1 1 1.06 1.06l-6.25 6.25a.749.749 0 0 1-1.06 0l-6.25-6.25a.749.749 0 0 1 0-1.06Z\"/>\\\n</svg>');\n background-repeat: no-repeat;\n background-position: center;\n background-size: 16px 16px;\n}\n/* Previous page icon - custom SVG (white, filled) */\n.adsk-icon-custom-prev::before {\n content: '';\n display: inline-block;\n width: 24px;\n height: 24px;\n background-image: url('data:image/svg+xml;utf8,\\\n<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1024 1024\">\\\n<g transform=\"translate(512 512) scale(1.2) translate(-512 -512)\">\\\n<path fill=\"%23FFFFFF\" d=\"M685.248 104.704a64 64 0 010 90.496L368.448 512l316.8 316.8a64 64 0 01-90.496 90.496L232.704 557.248a64 64 0 010-90.496l362.048-362.048a64 64 0 0190.496 0z\"/>\\\n</g>\\\n</svg>');\n background-repeat: no-repeat;\n background-position: center;\n background-size: 16px 16px;\n}\n/* Next page icon - custom SVG (white, filled) */\n.adsk-icon-custom-next::before {\n content: '';\n display: inline-block;\n width: 24px;\n height: 24px;\n background-image: url('data:image/svg+xml;utf8,\\\n<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1024 1024\">\\\n<g transform=\"translate(512 512) scale(1.2) translate(-512 -512)\">\\\n<path fill=\"%23FFFFFF\" d=\"M338.752 104.704a64 64 0 000 90.496l316.8 316.8-316.8 316.8a64 64 0 0090.496 90.496l362.048-362.048a64 64 0 000-90.496L429.248 104.704a64 64 0 00-90.496 0z\"/>\\\n</g>\\\n</svg>');\n background-repeat: no-repeat;\n background-position: center;\n background-size: 16px 16px;\n}\n/* Fallback for adsk-icon-caret-left and adsk-icon-caret-right if not defined */\n.adsk-icon-caret-left::before {\n content: '[';\n font-family: 'adsk-viewing';\n}\n.adsk-icon-caret-right::before {\n content: ']';\n font-family: 'adsk-viewing';\n}\n/* Comment icon - custom SVG (Smiley) */\n.adsk-icon-custom-comment::before {\n content: '';\n display: inline-block;\n width: 24px;\n height: 24px;\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"%23FFFFFF\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"></circle><path d=\"M8 14s1.5 2 4 2 4-2 4-2\"></path><line x1=\"9\" y1=\"9\" x2=\"9.01\" y2=\"9\"></line><line x1=\"15\" y1=\"9\" x2=\"15.01\" y2=\"9\"></line></svg>');\n background-repeat: no-repeat;\n background-position: center;\n background-size: contain;\n}\n/* Save icon - custom SVG */\n.adsk-icon-custom-save::before {\n content: '';\n display: inline-block;\n width: 24px;\n height: 24px;\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"%23FFFFFF\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z\"></path><polyline points=\"17 21 17 13 7 13 7 21\"></polyline><polyline points=\"7 3 7 8 15 8\"></polyline></svg>');\n background-repeat: no-repeat;\n background-position: center;\n background-size: contain;\n}\n";
821
1951
  styleInject(css_248z);
822
1952
 
823
1953
  var ViewerForgePDF = function ViewerForgePDF(_ref) {
824
1954
  var filePath = _ref.filePath,
825
1955
  fileExt = _ref.fileExt,
826
1956
  setViewer = _ref.setViewer;
827
- useCss('https://developer.api.autodesk.com/modelderivative/v2/viewers/7.*/style.min.css');
828
- var status = useScript('https://developer.api.autodesk.com/modelderivative/v2/viewers/7.*/viewer3D.min.js');
1957
+ // Load Forge Viewer CSS and JS from CDN
1958
+ useCss(FORGE_STYLE_URL);
1959
+ var status = useScript(FORGE_SCRIPT_URL);
829
1960
  react.useEffect(function () {
830
- if (status === 'ready' && window.Autodesk) {
831
- var Autodesk = window.Autodesk;
832
- if (!fileExt) {
833
- antd.message.warning('You need to provide file extension');
834
- return;
835
- }
836
- var validExts = ['pdf', 'dwf', 'dwfx'];
837
- if (!validExts.includes(fileExt.toLowerCase())) {
838
- antd.message.warning('Only support pdf, dwf, dwfx format');
839
- return;
840
- }
841
- Autodesk.Viewing.Initializer({
842
- env: 'Local'
843
- }, /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
844
- var viewerDiv, viewer, isDWF, handleLoadSuccess, xhr;
845
- return _regenerator().w(function (_context) {
846
- while (1) switch (_context.n) {
1961
+ if (status !== 'ready' || !window.Autodesk || !filePath || !fileExt) return;
1962
+ var initializeViewer = /*#__PURE__*/function () {
1963
+ var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
1964
+ var Autodesk;
1965
+ return _regenerator().w(function (_context2) {
1966
+ while (1) switch (_context2.p = _context2.n) {
847
1967
  case 0:
848
- registerToolbarExtension(Autodesk);
849
- viewerDiv = document.getElementById('forgeViewerPDF');
850
- viewerDiv.innerHTML = '';
851
- viewer = new Autodesk.Viewing.GuiViewer3D(viewerDiv);
852
- viewer.start();
853
- viewer.loadExtension('Autodesk.DocumentBrowser');
854
- viewer.loadExtension('Autodesk.Viewing.MarkupsCore');
855
- viewer.loadExtension('Autodesk.Viewing.MarkupsGui');
856
- viewer.loadExtension('ToolbarExtension', {
857
- filePath: filePath
858
- });
859
- viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, function (e) {
860
- e.target.loadExtension('Autodesk.Viewing.MarkupsCore');
861
- e.target.loadExtension('Autodesk.Viewing.MarkupsGui');
862
- });
863
- isDWF = fileExt.toLowerCase() === 'pdf' ? 'Autodesk.PDF' : 'Autodesk.DWF';
864
- _context.n = 1;
865
- return viewer.loadExtension(isDWF);
1968
+ _context2.p = 0;
1969
+ Autodesk = window.Autodesk; // Validate file extension
1970
+ if (SUPPORTED_FILE_EXTENSIONS.includes(fileExt.toLowerCase())) {
1971
+ _context2.n = 1;
1972
+ break;
1973
+ }
1974
+ antd.message.warning('Only support pdf, dwf, dwfx format');
1975
+ return _context2.a(2);
866
1976
  case 1:
867
- handleLoadSuccess = function handleLoadSuccess(e) {
868
- try {
869
- viewer.setReverseZoomDirection(true);
870
- var root = e.getDocumentNode().getRootNode();
871
- var view3d = root.search({
872
- type: 'geometry',
873
- role: '3d',
874
- progress: 'complete'
875
- }, true);
876
- var view2d = root.search({
877
- type: 'geometry',
878
- role: '2d',
879
- progress: 'complete'
880
- }, true);
881
- var viewables = view3d.concat(view2d);
882
- var toolbarExt = viewer.getExtension('ToolbarExtension');
883
- if (toolbarExt && typeof toolbarExt.setViewables === 'function') {
884
- toolbarExt.setViewables(viewables);
885
- }
886
- if (viewer.toolbar) {
887
- viewer.toolbar.setVisible(true);
888
- }
889
- if (viewables.length > 1) {
890
- // Auto-open Document Browser by clicking the original button
891
- setTimeout(function () {
892
- var originalDocBtn = document.getElementById('toolbar-documentModels');
893
- if (originalDocBtn && !originalDocBtn.classList.contains('active')) {
894
- originalDocBtn.click();
895
- }
896
-
897
- // Apply custom styling to the panel
898
- var documentBrowser = viewer.getExtension('Autodesk.DocumentBrowser');
899
- if (documentBrowser && documentBrowser.ui && documentBrowser.ui.panel) {
900
- documentBrowser.ui.panel.container.style.top = '0';
901
- documentBrowser.ui.panel.container.style.left = 'unset';
902
- documentBrowser.ui.panel.container.style.right = '0px';
903
- documentBrowser.ui.panel.container.style.width = '200px';
904
- documentBrowser.ui.panel.container.style.height = '80%';
905
- documentBrowser.ui.panel.container.style.minHeight = '400px';
1977
+ // Initialize Forge Viewer
1978
+ Autodesk.Viewing.Initializer({
1979
+ env: 'Local'
1980
+ }, /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
1981
+ var viewerDiv, viewer, fileLoader;
1982
+ return _regenerator().w(function (_context) {
1983
+ while (1) switch (_context.n) {
1984
+ case 0:
1985
+ // Register custom toolbar extension
1986
+ registerToolbarExtension(Autodesk);
1987
+ // Create viewer instance
1988
+ viewerDiv = document.getElementById('forgeViewerPDF');
1989
+ if (viewerDiv) {
1990
+ _context.n = 1;
1991
+ break;
906
1992
  }
907
-
908
- // Switch to Thumbnail tab after a short delay
909
- setTimeout(function () {
910
- // Try multiple selectors to find the thumbnail tab button
911
- var thumbnailTab = document.querySelector('.docking-panel-thumbnail-view') || document.querySelector('[data-i18n="Thumbnails"]') || Array.from(document.querySelectorAll('.adsk-control-group .adsk-button')).find(function (btn) {
912
- return btn.textContent.includes('Thumbnails') || btn.title.includes('Thumbnails');
913
- });
914
- if (thumbnailTab && !thumbnailTab.classList.contains('active')) {
915
- thumbnailTab.click();
1993
+ return _context.a(2);
1994
+ case 1:
1995
+ viewerDiv.innerHTML = '';
1996
+ viewer = new Autodesk.Viewing.GuiViewer3D(viewerDiv);
1997
+ viewer.start();
1998
+ // Load required extensions
1999
+ _context.n = 2;
2000
+ return viewer.loadExtension('Autodesk.DocumentBrowser');
2001
+ case 2:
2002
+ _context.n = 3;
2003
+ return viewer.loadExtension('Autodesk.Viewing.MarkupsCore');
2004
+ case 3:
2005
+ _context.n = 4;
2006
+ return viewer.loadExtension('Autodesk.Viewing.MarkupsGui');
2007
+ case 4:
2008
+ _context.n = 5;
2009
+ return viewer.loadExtension('ToolbarExtension', {
2010
+ filePath: filePath
2011
+ });
2012
+ case 5:
2013
+ // Initialize FileLoader service
2014
+ fileLoader = new FileLoader(viewer); // Note: ToolbarExtension now subscribes to VIEWABLES_SET directly
2015
+ // No need to forward events here - prevents infinite recursion
2016
+ // Load the document using FileLoader service
2017
+ _context.n = 6;
2018
+ return fileLoader.loadFile(filePath, fileExt.toLowerCase(), {
2019
+ onSuccess: function onSuccess() {
2020
+ // Pass viewer instance to parent component
2021
+ if (setViewer) {
2022
+ setViewer(viewer);
2023
+ }
2024
+ },
2025
+ onError: function onError(error) {
2026
+ antd.message.error('Failed to load document');
916
2027
  }
917
- }, 200);
918
- }, 500);
919
- }
920
-
921
- // Ensure Custom Pan tool is active by default
922
- setTimeout(function () {
923
- var customPanBtn = document.getElementById('custom-pan-btn');
924
- if (customPanBtn) {
925
- customPanBtn.click();
926
- // Double check visual state
927
- if (!customPanBtn.classList.contains('active')) {
928
- customPanBtn.classList.add('active');
929
- customPanBtn.classList.remove('inactive');
930
- }
931
- } else {
932
- var _viewer$toolbar;
933
- // Fallback if custom button not found immediately, try accessing via viewer toolbar
934
- var toolGroup = (_viewer$toolbar = viewer.toolbar) === null || _viewer$toolbar === void 0 ? void 0 : _viewer$toolbar.getControl('custom-tool-group');
935
- var panBtnConf = toolGroup === null || toolGroup === void 0 ? void 0 : toolGroup.getControl('custom-pan-btn');
936
- if (panBtnConf) {
937
- panBtnConf.setState(1); // ACTIVE
938
- }
939
- }
940
- }, 600);
941
- if (setViewer) setViewer(viewer);
942
- } catch (err) {
943
- console.error('Error in handleLoadSuccess:', err);
944
- }
945
- };
946
- if (isDWF === 'Autodesk.DWF') {
947
- xhr = new XMLHttpRequest();
948
- xhr.open('GET', filePath, true);
949
- xhr.responseType = 'blob';
950
- xhr.onload = function () {
951
- if (this.status === 200) {
952
- var myBlob = this.response;
953
- var url1 = window.URL.createObjectURL(myBlob);
954
- viewer.loadModel(url1 + '#.dwf', {}, handleLoadSuccess);
2028
+ });
2029
+ case 6:
2030
+ return _context.a(2);
955
2031
  }
956
- };
957
- xhr.send();
958
- } else {
959
- viewer.loadModel(filePath, {}, handleLoadSuccess);
960
- }
2032
+ }, _callee);
2033
+ })));
2034
+ _context2.n = 3;
2035
+ break;
961
2036
  case 2:
962
- return _context.a(2);
2037
+ _context2.p = 2;
2038
+ _context2.v;
2039
+ antd.message.error('Failed to initialize viewer');
2040
+ case 3:
2041
+ return _context2.a(2);
963
2042
  }
964
- }, _callee);
965
- })));
966
- }
2043
+ }, _callee2, null, [[0, 2]]);
2044
+ }));
2045
+ return function initializeViewer() {
2046
+ return _ref2.apply(this, arguments);
2047
+ };
2048
+ }();
2049
+ initializeViewer();
2050
+ // No cleanup needed - ToolbarExtension handles its own subscriptions
967
2051
  // eslint-disable-next-line react-hooks/exhaustive-deps
968
2052
  }, [status, filePath, fileExt]);
969
2053
  return /*#__PURE__*/jsxRuntime.jsx("div", {