vue2-bbl-editor 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,833 @@
1
+ # External Project Integration Guide
2
+
3
+ This guide shows how external projects can integrate the Vue2 Premium BBL Editor upload system into their applications.
4
+
5
+ ## Quick Start
6
+
7
+ ### 1. Installation
8
+
9
+ ```bash
10
+ npm install your-vue2-premium-bbl-editor
11
+ ```
12
+
13
+ ### 2. Basic Setup
14
+
15
+ ```vue
16
+ <template>
17
+ <PremiumBblEditor
18
+ v-model="content"
19
+ @ready="onEditorReady"
20
+ :config="editorConfig"
21
+ />
22
+ </template>
23
+
24
+ <script>
25
+ import {
26
+ PremiumBblEditor,
27
+ UploadManager,
28
+ LocalStorageAdapter,
29
+ createUploadConfig,
30
+ integrateTipTapUpload
31
+ } from 'your-vue2-premium-bbl-editor'
32
+
33
+ export default {
34
+ components: { PremiumBblEditor },
35
+ data() {
36
+ return {
37
+ content: '<p>Start typing...</p>',
38
+ uploadManager: null,
39
+ editorConfig: { toolbar: { image: true } }
40
+ }
41
+ },
42
+ async created() {
43
+ const config = createUploadConfig('localStorage')
44
+ this.uploadManager = new UploadManager(config)
45
+
46
+ const adapter = new LocalStorageAdapter()
47
+ this.uploadManager.registerAdapter(adapter)
48
+ this.uploadManager.setDefaultAdapter('local-storage')
49
+ },
50
+ methods: {
51
+ onEditorReady(editor) {
52
+ integrateTipTapUpload(editor, this.uploadManager)
53
+ }
54
+ }
55
+ }
56
+ </script>
57
+ ```
58
+
59
+ ## Integration Patterns
60
+
61
+ ### Pattern 1: Simple Integration (Recommended)
62
+
63
+ Use the `integrateTipTapUpload` helper function for automatic integration:
64
+
65
+ ```javascript
66
+ import { integrateTipTapUpload } from 'your-vue2-premium-bbl-editor'
67
+
68
+ onEditorReady(editor) {
69
+ integrateTipTapUpload(editor, this.uploadManager, {
70
+ onStart: () => console.log('Upload started'),
71
+ onEnd: () => console.log('Upload finished')
72
+ })
73
+ }
74
+ ```
75
+
76
+ ### Pattern 2: Manual Integration
77
+
78
+ For more control, use the factory pattern:
79
+
80
+ ```javascript
81
+ import { createTipTapUploadAdapterFactory } from 'your-vue2-premium-bbl-editor'
82
+
83
+ onEditorReady(editor) {
84
+ const createUploadAdapter = createTipTapUploadAdapterFactory(this.uploadManager)
85
+ editor.plugins.get("FileRepository").createUploadAdapter = createUploadAdapter
86
+ }
87
+ ```
88
+
89
+ ## Custom Upload Adapters
90
+
91
+ Create adapters for your specific backend:
92
+
93
+ ```javascript
94
+ class MyCustomAdapter {
95
+ constructor(options) {
96
+ this.name = 'my-custom-adapter'
97
+ this.endpoint = options.endpoint
98
+ this.apiKey = options.apiKey
99
+ }
100
+
101
+ async upload(file, options = {}) {
102
+ const formData = new FormData()
103
+ formData.append('file', file)
104
+
105
+ const response = await fetch(this.endpoint, {
106
+ method: 'POST',
107
+ headers: { 'Authorization': `Bearer ${this.apiKey}` },
108
+ body: formData
109
+ })
110
+
111
+ if (!response.ok) {
112
+ throw new Error(`Upload failed: ${response.statusText}`)
113
+ }
114
+
115
+ const result = await response.json()
116
+ return {
117
+ url: result.url,
118
+ size: file.size,
119
+ metadata: {
120
+ fileName: file.name,
121
+ fileType: file.type
122
+ }
123
+ }
124
+ }
125
+ }
126
+
127
+ // Register and use
128
+ const adapter = new MyCustomAdapter({
129
+ endpoint: '/api/upload',
130
+ apiKey: 'your-api-key'
131
+ })
132
+ uploadManager.registerAdapter(adapter)
133
+ uploadManager.setDefaultAdapter('my-custom-adapter')
134
+ ```
135
+
136
+ ## Configuration Options
137
+
138
+ ### Upload Configuration
139
+
140
+ ```javascript
141
+ const config = createUploadConfig('adapter-name', {
142
+ validation: {
143
+ maxSize: 10 * 1024 * 1024, // 10MB
144
+ allowedTypes: ['image/jpeg', 'image/png', 'video/mp4'],
145
+ customValidator: (file) => {
146
+ // Custom validation logic
147
+ return file.name.length < 100
148
+ }
149
+ },
150
+ retry: {
151
+ maxAttempts: 3,
152
+ baseDelay: 1000,
153
+ maxDelay: 5000
154
+ },
155
+ progress: {
156
+ throttleMs: 100 // Progress update frequency
157
+ }
158
+ })
159
+ ```
160
+
161
+ ### Editor Configuration
162
+
163
+ ```javascript
164
+ const editorConfig = {
165
+ toolbar: {
166
+ image: true,
167
+ video: true,
168
+ // Other toolbar options
169
+ },
170
+ // Other TipTap configuration options
171
+ }
172
+ ```
173
+
174
+ ## Event Handling
175
+
176
+ Listen to upload events for better user experience:
177
+
178
+ ```javascript
179
+ // Progress tracking
180
+ uploadManager.on('progress', (progress) => {
181
+ console.log(`Upload progress: ${progress.percentage}%`)
182
+ this.uploadProgress = progress.percentage
183
+ })
184
+
185
+ // Upload completion
186
+ uploadManager.on('uploadCompleted', (event) => {
187
+ console.log('Upload completed:', event.result.url)
188
+ this.showSuccessMessage('File uploaded successfully!')
189
+ })
190
+
191
+ // Upload failure
192
+ uploadManager.on('uploadFailed', (event) => {
193
+ console.error('Upload failed:', event.error.message)
194
+ this.showErrorMessage(`Upload failed: ${event.error.message}`)
195
+ })
196
+
197
+ // Retry attempts
198
+ uploadManager.on('retryAttemptStarted', (event) => {
199
+ console.log(`Retry attempt ${event.attempt}/${event.maxAttempts}`)
200
+ })
201
+ ```
202
+
203
+ ## Environment-Specific Setup
204
+
205
+ ### Development Environment
206
+
207
+ ```javascript
208
+ // Use local storage for development
209
+ const localAdapter = new LocalStorageAdapter({
210
+ generateObjectUrl: true,
211
+ persistToIndexedDB: false
212
+ })
213
+ uploadManager.registerAdapter(localAdapter)
214
+ uploadManager.setDefaultAdapter('local-storage')
215
+ ```
216
+
217
+ ### Production Environment
218
+
219
+ ```javascript
220
+ // Use your production API
221
+ const productionAdapter = new CustomApiAdapter({
222
+ endpoint: process.env.VUE_APP_UPLOAD_ENDPOINT,
223
+ apiKey: process.env.VUE_APP_API_KEY
224
+ })
225
+ uploadManager.registerAdapter(productionAdapter)
226
+ uploadManager.setDefaultAdapter('custom-api')
227
+ ```
228
+
229
+ ## Advanced Upload System Integration
230
+
231
+ The Vue2 Premium BBL Editor includes a comprehensive upload management system with multiple adapters, retry logic, progress tracking, and error handling.
232
+
233
+ ### Complete Upload System Setup
234
+
235
+ ```vue
236
+ <template>
237
+ <div class="upload-integration-demo">
238
+ <PremiumBblEditor
239
+ v-model="content"
240
+ @ready="onEditorReady"
241
+ />
242
+
243
+ <!-- Upload Progress -->
244
+ <div v-if="uploadStatus.active" class="upload-progress">
245
+ <div class="progress-bar">
246
+ <div
247
+ class="progress-fill"
248
+ :style="{ width: uploadStatus.progress + '%' }"
249
+ ></div>
250
+ </div>
251
+ <p>{{ uploadStatus.message }} - {{ uploadStatus.progress }}%</p>
252
+ </div>
253
+
254
+ <!-- Upload Statistics -->
255
+ <div class="upload-stats" v-if="uploadStats">
256
+ <h4>Upload Statistics</h4>
257
+ <p>Active Uploads: {{ uploadStats.activeUploads }}</p>
258
+ <p>Completed: {{ uploadStats.progressStats.completed }}</p>
259
+ <p>Failed: {{ uploadStats.progressStats.failed }}</p>
260
+ <p>Current Adapter: {{ uploadStats.defaultAdapter }}</p>
261
+ </div>
262
+ </div>
263
+ </template>
264
+
265
+ <script>
266
+ import {
267
+ PremiumBblEditor,
268
+ UploadManager,
269
+ LocalStorageAdapter,
270
+ integrateTipTapUpload,
271
+ createUploadConfig
272
+ } from 'your-vue2-premium-bbl-editor'
273
+
274
+ // Custom API Adapter for your backend
275
+ class CustomApiAdapter {
276
+ constructor(config = {}) {
277
+ this.name = 'custom-api'
278
+ this.apiUrl = config.apiUrl || '/api/upload'
279
+ this.headers = config.headers || {}
280
+ }
281
+
282
+ getName() {
283
+ return this.name
284
+ }
285
+
286
+ async upload(file, options) {
287
+ const formData = new FormData()
288
+ formData.append('file', file)
289
+ formData.append('uploadId', options.uploadId)
290
+
291
+ const response = await fetch(this.apiUrl, {
292
+ method: 'POST',
293
+ headers: this.headers,
294
+ body: formData
295
+ })
296
+
297
+ if (!response.ok) {
298
+ throw new Error(`Upload failed: ${response.statusText}`)
299
+ }
300
+
301
+ const data = await response.json()
302
+
303
+ return {
304
+ success: true,
305
+ url: data.url,
306
+ publicUrl: data.publicUrl || data.url,
307
+ uploadId: options.uploadId,
308
+ size: file.size,
309
+ adapter: this.name,
310
+ metadata: {
311
+ fileName: file.name,
312
+ fileType: file.type,
313
+ uploadedAt: new Date().toISOString()
314
+ }
315
+ }
316
+ }
317
+
318
+ supportsStrategy(strategy) {
319
+ return strategy === 'direct'
320
+ }
321
+
322
+ validateConfig() {
323
+ return true
324
+ }
325
+ }
326
+
327
+ export default {
328
+ components: {
329
+ PremiumBblEditor
330
+ },
331
+
332
+ data() {
333
+ return {
334
+ content: '<p>Advanced upload system integration example</p>',
335
+ uploadManager: null,
336
+ uploadStatus: {
337
+ active: false,
338
+ progress: 0,
339
+ message: ''
340
+ },
341
+ uploadStats: null
342
+ }
343
+ },
344
+
345
+ async created() {
346
+ await this.initializeUploadSystem()
347
+ },
348
+
349
+ methods: {
350
+ async initializeUploadSystem() {
351
+ try {
352
+ // Create comprehensive upload configuration
353
+ const config = createUploadConfig('localStorage', {
354
+ validation: {
355
+ maxSize: 10 * 1024 * 1024, // 10MB
356
+ allowedTypes: [
357
+ 'image/jpeg', 'image/png', 'image/gif', 'image/webp',
358
+ 'video/mp4', 'video/webm', 'video/ogg'
359
+ ],
360
+ customValidator: (file) => {
361
+ // Custom validation logic
362
+ if (file.name.length > 100) {
363
+ throw new Error('Filename too long')
364
+ }
365
+ return true
366
+ }
367
+ },
368
+ retry: {
369
+ maxAttempts: 3,
370
+ baseDelay: 1000,
371
+ maxDelay: 5000,
372
+ backoffFactor: 2
373
+ },
374
+ progress: {
375
+ throttleMs: 100 // Progress update frequency
376
+ },
377
+ debug: process.env.NODE_ENV === 'development'
378
+ })
379
+
380
+ // Initialize upload manager
381
+ this.uploadManager = new UploadManager(config)
382
+
383
+ // Register multiple adapters
384
+ await this.setupAdapters()
385
+
386
+ // Set up comprehensive event listeners
387
+ this.setupEventListeners()
388
+
389
+ // Update initial stats
390
+ this.updateStats()
391
+
392
+ console.log('✅ Upload system initialized successfully')
393
+
394
+ } catch (error) {
395
+ console.error('❌ Upload system initialization failed:', error)
396
+ }
397
+ },
398
+
399
+ async setupAdapters() {
400
+ // Local storage adapter for development
401
+ const localAdapter = new LocalStorageAdapter({
402
+ generateObjectUrl: true,
403
+ persistToIndexedDB: false
404
+ })
405
+ this.uploadManager.registerAdapter(localAdapter)
406
+
407
+ // Custom API adapter for production
408
+ const apiAdapter = new CustomApiAdapter({
409
+ apiUrl: process.env.VUE_APP_UPLOAD_URL || '/api/upload',
410
+ headers: {
411
+ 'Authorization': `Bearer ${this.getAuthToken()}`,
412
+ 'X-Client-Version': '1.0.0'
413
+ }
414
+ })
415
+ this.uploadManager.registerAdapter(apiAdapter)
416
+
417
+ // Set default adapter based on environment
418
+ const defaultAdapter = process.env.NODE_ENV === 'production'
419
+ ? 'custom-api'
420
+ : 'local-storage'
421
+
422
+ this.uploadManager.setDefaultAdapter(defaultAdapter)
423
+
424
+ console.log(`📡 Adapters registered, using: ${defaultAdapter}`)
425
+ },
426
+
427
+ setupEventListeners() {
428
+ // Upload started
429
+ this.uploadManager.on('uploadStarted', (event) => {
430
+ this.uploadStatus = {
431
+ active: true,
432
+ progress: 0,
433
+ message: `Starting upload: ${event.fileName}`
434
+ }
435
+ console.log('🚀 Upload started:', event)
436
+ })
437
+
438
+ // Progress tracking
439
+ this.uploadManager.on('progress', (progress) => {
440
+ this.uploadStatus.progress = Math.round(progress.percentage)
441
+ this.uploadStatus.message = `Uploading: ${progress.fileName}`
442
+
443
+ if (progress.speed) {
444
+ this.uploadStatus.message += ` (${this.formatSpeed(progress.speed)})`
445
+ }
446
+ })
447
+
448
+ // Upload completed
449
+ this.uploadManager.on('uploadCompleted', (event) => {
450
+ this.uploadStatus = {
451
+ active: false,
452
+ progress: 100,
453
+ message: 'Upload completed successfully!'
454
+ }
455
+
456
+ console.log('✅ Upload completed:', event.result.url)
457
+
458
+ // Clear status after delay
459
+ setTimeout(() => {
460
+ this.uploadStatus.active = false
461
+ }, 2000)
462
+
463
+ this.updateStats()
464
+ })
465
+
466
+ // Upload failed
467
+ this.uploadManager.on('uploadFailed', (event) => {
468
+ this.uploadStatus = {
469
+ active: false,
470
+ progress: 0,
471
+ message: `Upload failed: ${event.error.message}`
472
+ }
473
+
474
+ console.error('❌ Upload failed:', event.error)
475
+
476
+ // Show user-friendly error message
477
+ this.showErrorNotification(this.getUserFriendlyError(event.error))
478
+
479
+ // Clear status after delay
480
+ setTimeout(() => {
481
+ this.uploadStatus.active = false
482
+ }, 5000)
483
+
484
+ this.updateStats()
485
+ })
486
+
487
+ // Retry attempts
488
+ this.uploadManager.on('retryAttemptStarted', (event) => {
489
+ this.uploadStatus.message = `Retry attempt ${event.attempt}/${event.maxAttempts}`
490
+ console.log(`🔄 Retry attempt ${event.attempt}/${event.maxAttempts}`)
491
+ })
492
+
493
+ // Adapter changes
494
+ this.uploadManager.on('defaultAdapterChanged', (event) => {
495
+ console.log(`🔄 Switched to adapter: ${event.newDefault}`)
496
+ this.updateStats()
497
+ })
498
+
499
+ // Validation errors
500
+ this.uploadManager.on('validationFailed', (event) => {
501
+ console.warn('⚠️ Validation failed:', event.error.message)
502
+ this.showErrorNotification(event.error.message)
503
+ })
504
+ },
505
+
506
+ onEditorReady(editor) {
507
+ console.log('📝 Editor ready, integrating upload system...')
508
+
509
+ // Integrate upload system with TipTap
510
+ integrateTipTapUpload(editor, this.uploadManager, {
511
+ onStart: () => {
512
+ console.log('🎬 TipTap upload integration started')
513
+ },
514
+ onEnd: () => {
515
+ console.log('🎬 TipTap upload integration ended')
516
+ }
517
+ })
518
+
519
+ console.log('✅ TipTap upload integration completed')
520
+ },
521
+
522
+ updateStats() {
523
+ if (this.uploadManager) {
524
+ this.uploadStats = this.uploadManager.getStats()
525
+ }
526
+ },
527
+
528
+ getUserFriendlyError(error) {
529
+ const errorMap = {
530
+ 'FILE_TOO_LARGE': 'File is too large. Please choose a smaller file.',
531
+ 'INVALID_FILE_TYPE': 'File type not supported. Please choose a different file.',
532
+ 'NETWORK_ERROR': 'Network error. Please check your connection and try again.',
533
+ 'UPLOAD_TIMEOUT': 'Upload timed out. Please try again.',
534
+ 'SERVER_ERROR': 'Server error. Please try again later.'
535
+ }
536
+
537
+ return errorMap[error.code] || error.message || 'Upload failed. Please try again.'
538
+ },
539
+
540
+ showErrorNotification(message) {
541
+ // Implement your notification system
542
+ alert(message) // Replace with your notification component
543
+ },
544
+
545
+ formatSpeed(bytesPerSecond) {
546
+ const units = ['B/s', 'KB/s', 'MB/s', 'GB/s']
547
+ let size = bytesPerSecond
548
+ let unitIndex = 0
549
+
550
+ while (size >= 1024 && unitIndex < units.length - 1) {
551
+ size /= 1024
552
+ unitIndex++
553
+ }
554
+
555
+ return `${size.toFixed(1)} ${units[unitIndex]}`
556
+ },
557
+
558
+ getAuthToken() {
559
+ // Return your authentication token
560
+ return localStorage.getItem('authToken') || ''
561
+ }
562
+ },
563
+
564
+ beforeDestroy() {
565
+ if (this.uploadManager) {
566
+ this.uploadManager.destroy()
567
+ }
568
+ }
569
+ }
570
+ </script>
571
+
572
+ <style scoped>
573
+ .upload-integration-demo {
574
+ max-width: 1000px;
575
+ margin: 0 auto;
576
+ padding: 20px;
577
+ }
578
+
579
+ .upload-progress {
580
+ margin: 20px 0;
581
+ padding: 15px;
582
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
583
+ color: white;
584
+ border-radius: 8px;
585
+ }
586
+
587
+ .progress-bar {
588
+ width: 100%;
589
+ height: 8px;
590
+ background: rgba(255, 255, 255, 0.3);
591
+ border-radius: 4px;
592
+ margin-bottom: 10px;
593
+ overflow: hidden;
594
+ }
595
+
596
+ .progress-fill {
597
+ height: 100%;
598
+ background: white;
599
+ border-radius: 4px;
600
+ transition: width 0.3s ease;
601
+ }
602
+
603
+ .upload-stats {
604
+ margin-top: 20px;
605
+ padding: 15px;
606
+ background: #f8f9fa;
607
+ border: 1px solid #dee2e6;
608
+ border-radius: 6px;
609
+ }
610
+
611
+ .upload-stats h4 {
612
+ margin-top: 0;
613
+ color: #495057;
614
+ }
615
+
616
+ .upload-stats p {
617
+ margin: 5px 0;
618
+ font-size: 14px;
619
+ color: #6c757d;
620
+ }
621
+ </style>
622
+ ```
623
+
624
+ ### Upload Configuration Presets
625
+
626
+ The upload system includes pre-configured setups for common scenarios:
627
+
628
+ ```javascript
629
+ // Local Storage (Development)
630
+ const localConfig = createUploadConfig('localStorage', {
631
+ validation: {
632
+ maxSize: 10 * 1024 * 1024, // 10MB
633
+ allowedTypes: ['image/jpeg', 'image/png', 'image/gif', 'video/mp4']
634
+ },
635
+ retry: {
636
+ maxAttempts: 2,
637
+ baseDelay: 500
638
+ }
639
+ })
640
+
641
+ // AWS S3 (Production)
642
+ const s3Config = createUploadConfig('awsS3', {
643
+ adapters: {
644
+ 'aws-s3': {
645
+ type: 'aws-s3',
646
+ credentials: {
647
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
648
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
649
+ region: 'us-east-1',
650
+ bucket: 'my-uploads-bucket'
651
+ },
652
+ options: {
653
+ forcePathStyle: false,
654
+ usePresignedUrls: true
655
+ }
656
+ }
657
+ },
658
+ validation: {
659
+ maxSize: 50 * 1024 * 1024, // 50MB for S3
660
+ allowedTypes: [
661
+ 'image/jpeg', 'image/png', 'image/gif', 'image/webp',
662
+ 'video/mp4', 'video/webm', 'video/ogg'
663
+ ]
664
+ },
665
+ chunking: {
666
+ defaultChunkSize: 5 * 1024 * 1024, // 5MB chunks
667
+ autoChunkThreshold: 10 * 1024 * 1024, // 10MB threshold
668
+ parallelUploads: 3
669
+ }
670
+ })
671
+
672
+ // Cloudinary (Media Management)
673
+ const cloudinaryConfig = createUploadConfig('cloudinary', {
674
+ adapters: {
675
+ 'cloudinary': {
676
+ type: 'cloudinary',
677
+ credentials: {
678
+ cloudName: 'my-cloud',
679
+ apiKey: process.env.CLOUDINARY_API_KEY,
680
+ apiSecret: process.env.CLOUDINARY_API_SECRET
681
+ },
682
+ options: {
683
+ uploadPreset: 'my_preset',
684
+ folder: 'editor_uploads',
685
+ transformation: {
686
+ quality: 'auto',
687
+ fetch_format: 'auto'
688
+ }
689
+ }
690
+ }
691
+ },
692
+ validation: {
693
+ maxSize: 100 * 1024 * 1024 // 100MB for Cloudinary
694
+ }
695
+ })
696
+ ```
697
+
698
+ ### Multiple Upload Strategies
699
+
700
+ ```javascript
701
+ // Direct upload strategy
702
+ const directStrategy = new DirectUploadStrategy({
703
+ endpoint: '/api/upload',
704
+ method: 'POST',
705
+ headers: {
706
+ 'Authorization': 'Bearer token'
707
+ }
708
+ })
709
+
710
+ // Presigned URL strategy (for S3)
711
+ const presignedStrategy = new PresignedUrlStrategy({
712
+ getPresignedUrl: async (file) => {
713
+ const response = await fetch('/api/presigned-url', {
714
+ method: 'POST',
715
+ headers: { 'Content-Type': 'application/json' },
716
+ body: JSON.stringify({
717
+ fileName: file.name,
718
+ fileType: file.type,
719
+ fileSize: file.size
720
+ })
721
+ })
722
+ const data = await response.json()
723
+ return data.presignedUrl
724
+ }
725
+ })
726
+
727
+ // Register strategies with upload manager
728
+ uploadManager.registerStrategy('direct', directStrategy)
729
+ uploadManager.registerStrategy('presigned', presignedStrategy)
730
+ ```
731
+
732
+ ## Error Handling
733
+
734
+ Implement robust error handling:
735
+
736
+ ```javascript
737
+ uploadManager.on('uploadFailed', (event) => {
738
+ const { error, uploadId, adapter } = event
739
+
740
+ // Log for debugging
741
+ console.error('Upload failed:', {
742
+ uploadId,
743
+ adapter,
744
+ error: error.message,
745
+ stack: error.stack
746
+ })
747
+
748
+ // Show user-friendly message
749
+ let userMessage = 'Upload failed. Please try again.'
750
+
751
+ if (error.code === 'FILE_TOO_LARGE') {
752
+ userMessage = 'File is too large. Please choose a smaller file.'
753
+ } else if (error.code === 'INVALID_FILE_TYPE') {
754
+ userMessage = 'File type not supported. Please choose a different file.'
755
+ } else if (error.code === 'NETWORK_ERROR') {
756
+ userMessage = 'Network error. Please check your connection and try again.'
757
+ }
758
+
759
+ this.showErrorNotification(userMessage)
760
+ })
761
+ ```
762
+
763
+ ## TypeScript Support
764
+
765
+ If using TypeScript, import types:
766
+
767
+ ```typescript
768
+ import {
769
+ UploadManager,
770
+ UploadConfig,
771
+ UploadAdapter,
772
+ UploadResult
773
+ } from 'your-vue2-premium-bbl-editor'
774
+
775
+ interface CustomAdapterOptions {
776
+ endpoint: string
777
+ apiKey: string
778
+ }
779
+
780
+ class CustomAdapter implements UploadAdapter {
781
+ name = 'custom-adapter'
782
+
783
+ constructor(private options: CustomAdapterOptions) {}
784
+
785
+ async upload(file: File): Promise<UploadResult> {
786
+ // Implementation
787
+ }
788
+ }
789
+ ```
790
+
791
+ ## Best Practices
792
+
793
+ 1. **Always handle errors gracefully** - Show user-friendly error messages
794
+ 2. **Provide upload progress feedback** - Users expect to see progress
795
+ 3. **Validate files before upload** - Use the built-in validation system
796
+ 4. **Use appropriate adapters** - Local storage for dev, cloud services for production
797
+ 5. **Clean up resources** - Call `uploadManager.destroy()` when component unmounts
798
+ 6. **Test with different file types and sizes** - Ensure your validation works correctly
799
+
800
+ ## Troubleshooting
801
+
802
+ ### Common Issues
803
+
804
+ 1. **Upload not working**: Check if adapter is registered and set as default
805
+ 2. **Files not appearing in editor**: Ensure the upload result returns a valid URL
806
+ 3. **Progress not updating**: Check if progress events are being emitted
807
+ 4. **Memory issues**: Use appropriate file size limits and clean up resources
808
+
809
+ ### Debug Mode
810
+
811
+ Enable debug logging:
812
+
813
+ ```javascript
814
+ const config = createUploadConfig('adapter-name', {
815
+ debug: true // Enables detailed logging
816
+ })
817
+ ```
818
+
819
+ ## Examples
820
+
821
+ See the `examples/` directory for complete working examples:
822
+
823
+ - `examples/external-project-integration.vue` - Basic integration
824
+ - `examples/custom-adapter-integration.vue` - Custom adapter example
825
+ - `examples/tiptap-integration-example.vue` - Full-featured example
826
+
827
+ ## Support
828
+
829
+ For issues and questions:
830
+ 1. Check the troubleshooting section
831
+ 2. Review the examples
832
+ 3. Open an issue on GitHub
833
+ 4. Check the API documentation