een-api-toolkit 0.3.49 → 0.3.51

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.
@@ -86,10 +86,43 @@ interface ListEventsParams {
86
86
  endTimestamp__lte?: string // Optional: filter by event end time
87
87
  pageSize?: number
88
88
  pageToken?: string
89
- include?: string[] // Include SVG overlays: ['data.een.fullFrameImageUrl.v1']
89
+ include?: string[] // Data schemas to include (see below)
90
90
  }
91
91
  ```
92
92
 
93
+ ### Include Parameter & Data Schemas
94
+
95
+ The `include` parameter controls which data schemas are populated in the `event.data[]` array.
96
+ Include values are derived from the event's `dataSchemas` array by adding the `data.` prefix.
97
+
98
+ **How it works:**
99
+ 1. Each event has a `dataSchemas` array listing available schemas (e.g., `['een.objectDetection.v1', 'een.fullFrameImageUrl.v1']`)
100
+ 2. To include that data, prefix with `data.` (e.g., `include: ['data.een.objectDetection.v1']`)
101
+ 3. Without includes, the event may return with minimal or empty `data[]`
102
+
103
+ **Common data schemas:**
104
+ | Schema | Include Value | Description |
105
+ |--------|---------------|-------------|
106
+ | `een.objectDetection.v1` | `data.een.objectDetection.v1` | Bounding boxes `[x1, y1, x2, y2]` (normalized 0-1) |
107
+ | `een.objectClassification.v1` | `data.een.objectClassification.v1` | Object labels (person, vehicle, etc.) |
108
+ | `een.fullFrameImageUrl.v1` | `data.een.fullFrameImageUrl.v1` | Full frame image URL |
109
+ | `een.croppedFrameImageUrl.v1` | `data.een.croppedFrameImageUrl.v1` | Cropped/zoomed image URL |
110
+ | `een.fullFrameImageUrlWithOverlay.v1` | `data.een.fullFrameImageUrlWithOverlay.v1` | Image URL with bounding box overlay |
111
+ | `een.eevaAttributes.v1` | `data.een.eevaAttributes.v1` | EEVA analytics attributes |
112
+ | `een.customLabels.v1` | `data.een.customLabels.v1` | Custom detection labels |
113
+
114
+ **Fetching full event details:**
115
+ ```typescript
116
+ import { getEvent } from 'een-api-toolkit'
117
+
118
+ // Get event with all available data based on its dataSchemas
119
+ const simpleEvent = events.value.find(e => e.id === eventId)
120
+ const includes = simpleEvent?.dataSchemas.map(schema => `data.${schema}`) || []
121
+
122
+ const { data: fullEvent } = await getEvent(eventId, { include: includes })
123
+ // fullEvent.data[] now contains all available data objects
124
+ ```
125
+
93
126
  ### EventMetric Interface
94
127
  ```typescript
95
128
  interface EventMetric {
package/CHANGELOG.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
- ## [0.3.49] - 2026-01-31
5
+ ## [0.3.51] - 2026-02-03
6
6
 
7
7
  ### Release Summary
8
8
 
@@ -11,14 +11,14 @@ No PR descriptions available for this release.
11
11
  ### Detailed Changes
12
12
 
13
13
  #### Features
14
- - feat: Add exports, jobs, files, and downloads API support
14
+ - feat: Add JSON viewer to vue-events example and document include parameter
15
15
 
16
16
  #### Bug Fixes
17
- - fix: Correct job file access patterns and download examples
17
+ - fix: Address code review security and error handling concerns
18
18
 
19
19
  ### Links
20
20
  - [npm package](https://www.npmjs.com/package/een-api-toolkit)
21
- - [Full Changelog](https://github.com/klaushofrichter/een-api-toolkit/compare/v0.3.47...v0.3.49)
21
+ - [Full Changelog](https://github.com/klaushofrichter/een-api-toolkit/compare/v0.3.49...v0.3.51)
22
22
 
23
23
  ---
24
- *Released: 2026-01-31 20:01:33 CST*
24
+ *Released: 2026-02-03 06:37:51 CST*
@@ -1,6 +1,6 @@
1
1
  # EEN API Toolkit - AI Reference
2
2
 
3
- > **Version:** 0.3.49
3
+ > **Version:** 0.3.51
4
4
  >
5
5
  > This documentation is optimized for AI assistants. It provides focused, domain-specific
6
6
  > references to help you understand and use the een-api-toolkit efficiently.
@@ -1,6 +1,6 @@
1
1
  # Authentication - EEN API Toolkit
2
2
 
3
- > **Version:** 0.3.49
3
+ > **Version:** 0.3.51
4
4
  >
5
5
  > OAuth flow implementation, token management, and session handling.
6
6
  > Load this document when implementing login, logout, or auth guards.
@@ -1,6 +1,6 @@
1
1
  # Automations API - EEN API Toolkit
2
2
 
3
- > **Version:** 0.3.49
3
+ > **Version:** 0.3.51
4
4
  >
5
5
  > Complete reference for automation rules and alert actions.
6
6
  > Load this document when working with automated alert workflows.
@@ -1,6 +1,6 @@
1
1
  # Cameras & Bridges API - EEN API Toolkit
2
2
 
3
- > **Version:** 0.3.49
3
+ > **Version:** 0.3.51
4
4
  >
5
5
  > Complete reference for camera and bridge management.
6
6
  > Load this document when working with devices.
@@ -1,6 +1,6 @@
1
1
  # Events, Alerts & Real-Time Streaming - EEN API Toolkit
2
2
 
3
- > **Version:** 0.3.49
3
+ > **Version:** 0.3.51
4
4
  >
5
5
  > Complete reference for events, alerts, metrics, and SSE subscriptions.
6
6
  > Load this document when implementing event-driven features.
@@ -292,6 +292,7 @@ import {
292
292
  listEventFieldValues,
293
293
  listEventTypes,
294
294
  getRecordedImage,
295
+ getEvent,
295
296
  type Camera,
296
297
  type Event,
297
298
  type EventType,
@@ -374,6 +375,13 @@ const imageLoadingIds = ref<Set<string>>(new Set()) // Track which images are cu
374
375
  const boundingBoxCache = ref<Map<string, BoundingBox[]>>(new Map()) // Cache bounding boxes per event
375
376
  const enlargedEventId = ref<string | null>(null)
376
377
 
378
+ // JSON viewer state
379
+ const jsonViewerEventId = ref<string | null>(null)
380
+ const jsonViewerFullEvent = ref<Event | null>(null)
381
+ const jsonViewerLoading = ref(false)
382
+ const jsonViewerError = ref<string | null>(null)
383
+ const copySuccess = ref(false)
384
+
377
385
  // Lightbox media state
378
386
  const showVideo = ref(false)
379
387
  const hdImageUrl = ref<string | null>(null)
@@ -396,6 +404,21 @@ const enlargedBoundingBoxes = computed(() => {
396
404
  if (!enlargedEvent.value) return []
397
405
  return getBoundingBoxes(enlargedEvent.value)
398
406
  })
407
+ const jsonViewerEvent = computed(() => {
408
+ if (!jsonViewerEventId.value) return null
409
+ return events.value.find(e => e.id === jsonViewerEventId.value) || null
410
+ })
411
+ const jsonViewerContent = computed(() => {
412
+ // Use full event if loaded, otherwise fall back to list event
413
+ const eventToShow = jsonViewerFullEvent.value || jsonViewerEvent.value
414
+ if (!eventToShow) return ''
415
+ try {
416
+ return JSON.stringify(eventToShow, null, 2)
417
+ } catch (err) {
418
+ // Safely handle any JSON serialization errors
419
+ return `Error serializing event data: ${String(err)}`
420
+ }
421
+ })
399
422
 
400
423
  // Get start timestamp based on time range
401
424
  function getStartTimestamp(range: TimeRange): string {
@@ -698,6 +721,77 @@ function closeEnlargedImage() {
698
721
  resetVideo()
699
722
  }
700
723
 
724
+ // Open JSON viewer and fetch full event details
725
+ async function openJsonViewer(eventId: string) {
726
+ jsonViewerEventId.value = eventId
727
+ jsonViewerFullEvent.value = null
728
+ jsonViewerError.value = null
729
+ copySuccess.value = false
730
+
731
+ // Find the event in the list to get its dataSchemas
732
+ const listEvent = events.value.find(e => e.id === eventId)
733
+ if (!listEvent) {
734
+ jsonViewerError.value = 'Event not found in current list'
735
+ return
736
+ }
737
+
738
+ // Build include array from dataSchemas (prefix with "data.")
739
+ const includes = listEvent.dataSchemas?.map(schema => `data.${schema}`) || []
740
+
741
+ if (includes.length === 0) {
742
+ // No additional data to fetch, use the list event as-is
743
+ return
744
+ }
745
+
746
+ // Fetch full event details with all data schemas
747
+ jsonViewerLoading.value = true
748
+ const { data, error } = await getEvent(eventId, { include: includes })
749
+ jsonViewerLoading.value = false
750
+
751
+ if (error) {
752
+ jsonViewerError.value = error.message
753
+ return
754
+ }
755
+
756
+ if (data) {
757
+ jsonViewerFullEvent.value = data
758
+ }
759
+ }
760
+
761
+ // Close JSON viewer
762
+ function closeJsonViewer() {
763
+ jsonViewerEventId.value = null
764
+ jsonViewerFullEvent.value = null
765
+ jsonViewerError.value = null
766
+ copySuccess.value = false
767
+ }
768
+
769
+ // Copy JSON to clipboard
770
+ async function copyJsonToClipboard() {
771
+ if (!jsonViewerContent.value) return
772
+ try {
773
+ await navigator.clipboard.writeText(jsonViewerContent.value)
774
+ copySuccess.value = true
775
+ setTimeout(() => {
776
+ copySuccess.value = false
777
+ }, 2000)
778
+ } catch {
779
+ // Fallback for older browsers
780
+ const textarea = document.createElement('textarea')
781
+ textarea.value = jsonViewerContent.value
782
+ textarea.style.position = 'fixed'
783
+ textarea.style.opacity = '0'
784
+ document.body.appendChild(textarea)
785
+ textarea.select()
786
+ document.execCommand('copy')
787
+ document.body.removeChild(textarea)
788
+ copySuccess.value = true
789
+ setTimeout(() => {
790
+ copySuccess.value = false
791
+ }, 2000)
792
+ }
793
+ }
794
+
701
795
  // Switch to preview mode
702
796
  function showPreview() {
703
797
  currentMediaType.value = 'preview'
@@ -747,8 +841,12 @@ async function showVideoPlayer() {
747
841
 
748
842
  // Handle keyboard events for accessibility
749
843
  function handleKeydown(event: KeyboardEvent) {
750
- if (event.key === 'Escape' && enlargedEventId.value) {
751
- closeEnlargedImage()
844
+ if (event.key === 'Escape') {
845
+ if (jsonViewerEventId.value) {
846
+ closeJsonViewer()
847
+ } else if (enlargedEventId.value) {
848
+ closeEnlargedImage()
849
+ }
752
850
  }
753
851
  }
754
852
 
@@ -759,6 +857,9 @@ onMounted(() => {
759
857
 
760
858
  onUnmounted(() => {
761
859
  window.removeEventListener('keydown', handleKeydown)
860
+ // Clean up JSON viewer state to prevent memory leaks
861
+ jsonViewerFullEvent.value = null
862
+ jsonViewerEventId.value = null
762
863
  })
763
864
 
764
865
  // Watch for modal open/close
@@ -784,6 +885,7 @@ watch(() => props.isOpen, async (isOpen) => {
784
885
  boundingBoxCache.value.clear()
785
886
  events.value = []
786
887
  enlargedEventId.value = null
888
+ jsonViewerEventId.value = null
787
889
  }
788
890
  }, { immediate: true })
789
891
 
@@ -883,7 +985,15 @@ watch([timeRange, selectedEventTypes], () => {
883
985
  </div>
884
986
  </div>
885
987
  <div class="event-info">
886
- <div class="event-type">{{ getEventTypeName(event.type) }}</div>
988
+ <div class="event-type-row">
989
+ <span class="event-type">{{ getEventTypeName(event.type) }}</span>
990
+ <button
991
+ class="json-button"
992
+ @click="openJsonViewer(event.id)"
993
+ title="View JSON data"
994
+ data-testid="json-button"
995
+ >{ }</button>
996
+ </div>
887
997
  <div class="event-time">{{ formatTimestamp(event.startTimestamp) }}</div>
888
998
  <div v-if="getBoundingBoxCount(event) > 0" class="event-detections" data-testid="event-detections">
889
999
  {{ getBoundingBoxCount(event) }} detection{{ getBoundingBoxCount(event) !== 1 ? 's' : '' }}
@@ -1,6 +1,6 @@
1
1
  # Layouts API - EEN API Toolkit
2
2
 
3
- > **Version:** 0.3.49
3
+ > **Version:** 0.3.51
4
4
  >
5
5
  > Complete reference for layout management (camera grouping).
6
6
  > Load this document when working with layouts.
@@ -1,6 +1,6 @@
1
1
  # Jobs, Exports, Files & Downloads - EEN API Toolkit
2
2
 
3
- > **Version:** 0.3.49
3
+ > **Version:** 0.3.51
4
4
  >
5
5
  > Complete reference for async jobs, video exports, and file management.
6
6
  > Load this document when implementing export workflows or file downloads.
@@ -1,6 +1,6 @@
1
1
  # Media & Live Video - EEN API Toolkit
2
2
 
3
- > **Version:** 0.3.49
3
+ > **Version:** 0.3.51
4
4
  >
5
5
  > Complete reference for media retrieval, live streaming, and video playback.
6
6
  > Load this document when implementing video features.
@@ -1,6 +1,6 @@
1
1
  # Vue 3 Application Setup - EEN API Toolkit
2
2
 
3
- > **Version:** 0.3.49
3
+ > **Version:** 0.3.51
4
4
  >
5
5
  > Complete guide for setting up a Vue 3 application with the een-api-toolkit.
6
6
  > Load this document when creating a new project or troubleshooting setup issues.
@@ -1,6 +1,6 @@
1
1
  # Users API - EEN API Toolkit
2
2
 
3
- > **Version:** 0.3.49
3
+ > **Version:** 0.3.51
4
4
  >
5
5
  > Complete reference for user management.
6
6
  > Load this document when working with user data.
@@ -5,6 +5,7 @@ import {
5
5
  listEventFieldValues,
6
6
  listEventTypes,
7
7
  getRecordedImage,
8
+ getEvent,
8
9
  type Camera,
9
10
  type Event,
10
11
  type EventType,
@@ -87,6 +88,13 @@ const imageLoadingIds = ref<Set<string>>(new Set()) // Track which images are cu
87
88
  const boundingBoxCache = ref<Map<string, BoundingBox[]>>(new Map()) // Cache bounding boxes per event
88
89
  const enlargedEventId = ref<string | null>(null)
89
90
 
91
+ // JSON viewer state
92
+ const jsonViewerEventId = ref<string | null>(null)
93
+ const jsonViewerFullEvent = ref<Event | null>(null)
94
+ const jsonViewerLoading = ref(false)
95
+ const jsonViewerError = ref<string | null>(null)
96
+ const copySuccess = ref(false)
97
+
90
98
  // Lightbox media state
91
99
  const showVideo = ref(false)
92
100
  const hdImageUrl = ref<string | null>(null)
@@ -109,6 +117,21 @@ const enlargedBoundingBoxes = computed(() => {
109
117
  if (!enlargedEvent.value) return []
110
118
  return getBoundingBoxes(enlargedEvent.value)
111
119
  })
120
+ const jsonViewerEvent = computed(() => {
121
+ if (!jsonViewerEventId.value) return null
122
+ return events.value.find(e => e.id === jsonViewerEventId.value) || null
123
+ })
124
+ const jsonViewerContent = computed(() => {
125
+ // Use full event if loaded, otherwise fall back to list event
126
+ const eventToShow = jsonViewerFullEvent.value || jsonViewerEvent.value
127
+ if (!eventToShow) return ''
128
+ try {
129
+ return JSON.stringify(eventToShow, null, 2)
130
+ } catch (err) {
131
+ // Safely handle any JSON serialization errors
132
+ return `Error serializing event data: ${String(err)}`
133
+ }
134
+ })
112
135
 
113
136
  // Get start timestamp based on time range
114
137
  function getStartTimestamp(range: TimeRange): string {
@@ -411,6 +434,77 @@ function closeEnlargedImage() {
411
434
  resetVideo()
412
435
  }
413
436
 
437
+ // Open JSON viewer and fetch full event details
438
+ async function openJsonViewer(eventId: string) {
439
+ jsonViewerEventId.value = eventId
440
+ jsonViewerFullEvent.value = null
441
+ jsonViewerError.value = null
442
+ copySuccess.value = false
443
+
444
+ // Find the event in the list to get its dataSchemas
445
+ const listEvent = events.value.find(e => e.id === eventId)
446
+ if (!listEvent) {
447
+ jsonViewerError.value = 'Event not found in current list'
448
+ return
449
+ }
450
+
451
+ // Build include array from dataSchemas (prefix with "data.")
452
+ const includes = listEvent.dataSchemas?.map(schema => `data.${schema}`) || []
453
+
454
+ if (includes.length === 0) {
455
+ // No additional data to fetch, use the list event as-is
456
+ return
457
+ }
458
+
459
+ // Fetch full event details with all data schemas
460
+ jsonViewerLoading.value = true
461
+ const { data, error } = await getEvent(eventId, { include: includes })
462
+ jsonViewerLoading.value = false
463
+
464
+ if (error) {
465
+ jsonViewerError.value = error.message
466
+ return
467
+ }
468
+
469
+ if (data) {
470
+ jsonViewerFullEvent.value = data
471
+ }
472
+ }
473
+
474
+ // Close JSON viewer
475
+ function closeJsonViewer() {
476
+ jsonViewerEventId.value = null
477
+ jsonViewerFullEvent.value = null
478
+ jsonViewerError.value = null
479
+ copySuccess.value = false
480
+ }
481
+
482
+ // Copy JSON to clipboard
483
+ async function copyJsonToClipboard() {
484
+ if (!jsonViewerContent.value) return
485
+ try {
486
+ await navigator.clipboard.writeText(jsonViewerContent.value)
487
+ copySuccess.value = true
488
+ setTimeout(() => {
489
+ copySuccess.value = false
490
+ }, 2000)
491
+ } catch {
492
+ // Fallback for older browsers
493
+ const textarea = document.createElement('textarea')
494
+ textarea.value = jsonViewerContent.value
495
+ textarea.style.position = 'fixed'
496
+ textarea.style.opacity = '0'
497
+ document.body.appendChild(textarea)
498
+ textarea.select()
499
+ document.execCommand('copy')
500
+ document.body.removeChild(textarea)
501
+ copySuccess.value = true
502
+ setTimeout(() => {
503
+ copySuccess.value = false
504
+ }, 2000)
505
+ }
506
+ }
507
+
414
508
  // Switch to preview mode
415
509
  function showPreview() {
416
510
  currentMediaType.value = 'preview'
@@ -460,8 +554,12 @@ async function showVideoPlayer() {
460
554
 
461
555
  // Handle keyboard events for accessibility
462
556
  function handleKeydown(event: KeyboardEvent) {
463
- if (event.key === 'Escape' && enlargedEventId.value) {
464
- closeEnlargedImage()
557
+ if (event.key === 'Escape') {
558
+ if (jsonViewerEventId.value) {
559
+ closeJsonViewer()
560
+ } else if (enlargedEventId.value) {
561
+ closeEnlargedImage()
562
+ }
465
563
  }
466
564
  }
467
565
 
@@ -472,6 +570,9 @@ onMounted(() => {
472
570
 
473
571
  onUnmounted(() => {
474
572
  window.removeEventListener('keydown', handleKeydown)
573
+ // Clean up JSON viewer state to prevent memory leaks
574
+ jsonViewerFullEvent.value = null
575
+ jsonViewerEventId.value = null
475
576
  })
476
577
 
477
578
  // Watch for modal open/close
@@ -497,6 +598,7 @@ watch(() => props.isOpen, async (isOpen) => {
497
598
  boundingBoxCache.value.clear()
498
599
  events.value = []
499
600
  enlargedEventId.value = null
601
+ jsonViewerEventId.value = null
500
602
  }
501
603
  }, { immediate: true })
502
604
 
@@ -596,7 +698,15 @@ watch([timeRange, selectedEventTypes], () => {
596
698
  </div>
597
699
  </div>
598
700
  <div class="event-info">
599
- <div class="event-type">{{ getEventTypeName(event.type) }}</div>
701
+ <div class="event-type-row">
702
+ <span class="event-type">{{ getEventTypeName(event.type) }}</span>
703
+ <button
704
+ class="json-button"
705
+ @click="openJsonViewer(event.id)"
706
+ title="View JSON data"
707
+ data-testid="json-button"
708
+ >{ }</button>
709
+ </div>
600
710
  <div class="event-time">{{ formatTimestamp(event.startTimestamp) }}</div>
601
711
  <div v-if="getBoundingBoxCount(event) > 0" class="event-detections" data-testid="event-detections">
602
712
  {{ getBoundingBoxCount(event) }} detection{{ getBoundingBoxCount(event) !== 1 ? 's' : '' }}
@@ -769,6 +879,50 @@ watch([timeRange, selectedEventTypes], () => {
769
879
  </div>
770
880
  </div>
771
881
  </div>
882
+
883
+ <!-- JSON viewer lightbox -->
884
+ <div
885
+ v-if="jsonViewerEventId"
886
+ class="json-viewer-overlay"
887
+ @click.self="closeJsonViewer"
888
+ data-testid="json-viewer-overlay"
889
+ >
890
+ <div class="json-viewer-content">
891
+ <div class="json-viewer-header">
892
+ <h3>Event JSON Data</h3>
893
+ <div class="json-viewer-actions">
894
+ <button
895
+ class="copy-button"
896
+ :class="{ success: copySuccess }"
897
+ @click="copyJsonToClipboard"
898
+ data-testid="copy-json-button"
899
+ >
900
+ {{ copySuccess ? 'Copied!' : 'Copy' }}
901
+ </button>
902
+ <button
903
+ class="json-viewer-close"
904
+ @click="closeJsonViewer"
905
+ aria-label="Close JSON viewer"
906
+ data-testid="json-viewer-close"
907
+ >&times;</button>
908
+ </div>
909
+ </div>
910
+ <div class="json-viewer-body">
911
+ <div v-if="jsonViewerLoading" class="json-viewer-loading">
912
+ Loading full event details...
913
+ </div>
914
+ <div v-else-if="jsonViewerError" class="json-viewer-error">
915
+ Error: {{ jsonViewerError }}
916
+ </div>
917
+ <pre v-else><code>{{ jsonViewerContent }}</code></pre>
918
+ </div>
919
+ <div v-if="jsonViewerEvent" class="json-viewer-footer">
920
+ <span class="json-event-type">{{ getEventTypeName(jsonViewerEvent.type) }}</span>
921
+ <span class="json-event-time">{{ formatTimestamp(jsonViewerEvent.startTimestamp) }}</span>
922
+ <span v-if="jsonViewerFullEvent" class="json-full-indicator">Full data loaded</span>
923
+ </div>
924
+ </div>
925
+ </div>
772
926
  </div>
773
927
  </div>
774
928
  </template>
@@ -1227,4 +1381,175 @@ watch([timeRange, selectedEventTypes], () => {
1227
1381
  opacity: 0.8;
1228
1382
  margin-left: 4px;
1229
1383
  }
1384
+
1385
+ /* Event type row with JSON button */
1386
+ .event-type-row {
1387
+ display: flex;
1388
+ align-items: center;
1389
+ justify-content: space-between;
1390
+ gap: 8px;
1391
+ margin-bottom: 5px;
1392
+ }
1393
+
1394
+ .event-type-row .event-type {
1395
+ margin-bottom: 0;
1396
+ flex: 1;
1397
+ }
1398
+
1399
+ /* JSON button on event cards */
1400
+ .json-button {
1401
+ padding: 2px 5px;
1402
+ background: #f0f0f0;
1403
+ border: 1px solid #ccc;
1404
+ border-radius: 3px;
1405
+ font-size: 0.65rem;
1406
+ font-family: monospace;
1407
+ color: #666;
1408
+ cursor: pointer;
1409
+ transition: background 0.2s, border-color 0.2s;
1410
+ line-height: 1;
1411
+ }
1412
+
1413
+ .json-button:hover {
1414
+ background: #e0e0e0;
1415
+ border-color: #999;
1416
+ color: #333;
1417
+ }
1418
+
1419
+ /* JSON viewer lightbox */
1420
+ .json-viewer-overlay {
1421
+ position: fixed;
1422
+ top: 0;
1423
+ left: 0;
1424
+ right: 0;
1425
+ bottom: 0;
1426
+ background: rgba(0, 0, 0, 0.85);
1427
+ display: flex;
1428
+ align-items: center;
1429
+ justify-content: center;
1430
+ z-index: 2100;
1431
+ }
1432
+
1433
+ .json-viewer-content {
1434
+ background: #1e1e1e;
1435
+ border-radius: 8px;
1436
+ width: 90%;
1437
+ max-width: 700px;
1438
+ max-height: 85vh;
1439
+ display: flex;
1440
+ flex-direction: column;
1441
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
1442
+ }
1443
+
1444
+ .json-viewer-header {
1445
+ display: flex;
1446
+ justify-content: space-between;
1447
+ align-items: center;
1448
+ padding: 15px 20px;
1449
+ border-bottom: 1px solid #333;
1450
+ }
1451
+
1452
+ .json-viewer-header h3 {
1453
+ margin: 0;
1454
+ color: #fff;
1455
+ font-size: 1rem;
1456
+ font-weight: 500;
1457
+ }
1458
+
1459
+ .json-viewer-actions {
1460
+ display: flex;
1461
+ gap: 10px;
1462
+ align-items: center;
1463
+ }
1464
+
1465
+ .copy-button {
1466
+ padding: 6px 14px;
1467
+ background: #42b883;
1468
+ color: white;
1469
+ border: none;
1470
+ border-radius: 4px;
1471
+ font-size: 0.85rem;
1472
+ font-weight: 500;
1473
+ cursor: pointer;
1474
+ transition: background 0.2s;
1475
+ }
1476
+
1477
+ .copy-button:hover {
1478
+ background: #3aa876;
1479
+ }
1480
+
1481
+ .copy-button.success {
1482
+ background: #2d8659;
1483
+ }
1484
+
1485
+ .json-viewer-close {
1486
+ background: none;
1487
+ border: none;
1488
+ color: #aaa;
1489
+ font-size: 1.5rem;
1490
+ cursor: pointer;
1491
+ padding: 0 5px;
1492
+ line-height: 1;
1493
+ }
1494
+
1495
+ .json-viewer-close:hover {
1496
+ color: #fff;
1497
+ }
1498
+
1499
+ .json-viewer-body {
1500
+ flex: 1;
1501
+ overflow: auto;
1502
+ padding: 15px 20px;
1503
+ }
1504
+
1505
+ .json-viewer-body pre {
1506
+ margin: 0;
1507
+ white-space: pre-wrap;
1508
+ word-break: break-word;
1509
+ }
1510
+
1511
+ .json-viewer-body code {
1512
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, monospace;
1513
+ font-size: 0.8rem;
1514
+ line-height: 1.5;
1515
+ color: #d4d4d4;
1516
+ }
1517
+
1518
+ .json-viewer-footer {
1519
+ padding: 12px 20px;
1520
+ border-top: 1px solid #333;
1521
+ display: flex;
1522
+ justify-content: space-between;
1523
+ align-items: center;
1524
+ color: #888;
1525
+ font-size: 0.8rem;
1526
+ }
1527
+
1528
+ .json-event-type {
1529
+ font-weight: 500;
1530
+ color: #aaa;
1531
+ }
1532
+
1533
+ .json-event-time {
1534
+ color: #666;
1535
+ }
1536
+
1537
+ .json-full-indicator {
1538
+ color: #42b883;
1539
+ font-size: 0.75rem;
1540
+ margin-left: auto;
1541
+ }
1542
+
1543
+ .json-viewer-loading {
1544
+ color: #aaa;
1545
+ font-size: 0.9rem;
1546
+ text-align: center;
1547
+ padding: 40px 20px;
1548
+ }
1549
+
1550
+ .json-viewer-error {
1551
+ color: #ff6b6b;
1552
+ font-size: 0.85rem;
1553
+ margin-bottom: 15px;
1554
+ }
1230
1555
  </style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "een-api-toolkit",
3
- "version": "0.3.49",
3
+ "version": "0.3.51",
4
4
  "description": "EEN Video platform API v3.0 library for Vue 3",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",