@uibit/360-viewer 0.1.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,320 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { html } from 'lit';
8
+ import { customElement, fromLucide, getIcon, msg, UIBitElement } from '@uibit/core';
9
+ import { Move, ChevronLeft, ChevronRight } from 'lucide';
10
+ import { property, state } from 'lit/decorators.js';
11
+ import { styles } from './styles';
12
+ /**
13
+ * 360° product viewer driven by an array of sequential images.
14
+ * Supports drag, touch, auto-rotation, and keyboard controls.
15
+ *
16
+ * @fires {{ index: number, total: number }} change - Fired each time the displayed frame changes
17
+ *
18
+ * @cssprop [--uibit-360-viewer-bg=#f9fafb] - Background color of the viewer container
19
+ * @cssprop [--uibit-360-viewer-border=#e5e7eb] - Border color of the viewer container
20
+ * @cssprop [--uibit-360-viewer-button-bg=rgba(255,255,255,0.75)] - Background of control buttons
21
+ * @cssprop [--uibit-360-viewer-button-bg-hover=rgba(255,255,255,0.95)] - Hover background of control buttons
22
+ * @cssprop [--uibit-360-viewer-button-color=#374151] - Icon/text color of control buttons
23
+ * @cssprop [--uibit-360-viewer-focus-color=#000000] - Focus outline color for interactive elements
24
+ * @cssprop [--uibit-360-viewer-progress-track-bg=rgba(0,0,0,0.08)] - Background of the progress bar track
25
+ * @cssprop [--uibit-360-viewer-hint-bg=rgba(17,24,39,0.65)] - Background of the drag hint overlay
26
+ */
27
+ let Viewer360 = class Viewer360 extends UIBitElement {
28
+ constructor() {
29
+ super(...arguments);
30
+ this.images = [];
31
+ /** Automatically rotate through frames when no user interaction is occurring. */
32
+ this.autoRotate = false;
33
+ /** Milliseconds between frames during auto-rotation. Lower values spin faster. */
34
+ this.rotationSpeed = 150;
35
+ /** Horizontal pixel distance a drag must travel before advancing one frame. */
36
+ this.dragSensitivity = 15;
37
+ /** Show the play/pause and directional control buttons. */
38
+ this.showControls = true;
39
+ /** Show the frame progress bar at the bottom of the viewer. */
40
+ this.showProgressBar = true;
41
+ this.currentIndex = 0;
42
+ this.isDragging = false;
43
+ this.firstFrameReady = false;
44
+ this.aspectRatio = '16 / 9';
45
+ this.startX = 0;
46
+ this.startImageIndex = 0;
47
+ }
48
+ static { this.styles = styles; }
49
+ connectedCallback() {
50
+ super.connectedCallback();
51
+ }
52
+ disconnectedCallback() {
53
+ super.disconnectedCallback();
54
+ this.stopAutoRotate();
55
+ this.clearResumeTimer();
56
+ }
57
+ updated(changedProperties) {
58
+ if (changedProperties.has('autoRotate') || changedProperties.has('rotationSpeed')) {
59
+ if (this.autoRotate) {
60
+ this.startAutoRotate();
61
+ }
62
+ else {
63
+ this.stopAutoRotate();
64
+ }
65
+ }
66
+ if (changedProperties.has('images')) {
67
+ this.currentIndex = 0;
68
+ this.firstFrameReady = false;
69
+ this.stopAutoRotate();
70
+ }
71
+ }
72
+ startAutoRotate() {
73
+ this.stopAutoRotate();
74
+ if (!this.autoRotate || this.images.length === 0 || !this.firstFrameReady)
75
+ return;
76
+ this.autoRotateTimer = window.setInterval(() => {
77
+ this.next();
78
+ }, this.rotationSpeed);
79
+ }
80
+ stopAutoRotate() {
81
+ if (this.autoRotateTimer) {
82
+ clearInterval(this.autoRotateTimer);
83
+ this.autoRotateTimer = undefined;
84
+ }
85
+ }
86
+ scheduleAutoRotateResume() {
87
+ if (!this.autoRotate)
88
+ return;
89
+ this.clearResumeTimer();
90
+ this.resumeTimer = window.setTimeout(() => {
91
+ this.startAutoRotate();
92
+ }, 2000);
93
+ }
94
+ clearResumeTimer() {
95
+ if (this.resumeTimer) {
96
+ clearTimeout(this.resumeTimer);
97
+ this.resumeTimer = undefined;
98
+ }
99
+ }
100
+ handleFrameLoad(e, index) {
101
+ if (index === 0) {
102
+ const img = e.target;
103
+ if (img.naturalWidth && img.naturalHeight) {
104
+ this.aspectRatio = `${img.naturalWidth} / ${img.naturalHeight}`;
105
+ }
106
+ this.firstFrameReady = true;
107
+ if (this.autoRotate)
108
+ this.startAutoRotate();
109
+ }
110
+ }
111
+ next() {
112
+ if (this.images.length === 0)
113
+ return;
114
+ this.currentIndex = (this.currentIndex + 1) % this.images.length;
115
+ this.emitChange();
116
+ }
117
+ prev() {
118
+ if (this.images.length === 0)
119
+ return;
120
+ this.currentIndex = (this.currentIndex - 1 + this.images.length) % this.images.length;
121
+ this.emitChange();
122
+ }
123
+ emitChange() {
124
+ this.dispatchCustomEvent('change', {
125
+ index: this.currentIndex,
126
+ image: this.images[this.currentIndex]
127
+ });
128
+ }
129
+ handlePointerDown(e) {
130
+ if (this.images.length === 0)
131
+ return;
132
+ this.isDragging = true;
133
+ this.startX = e.clientX;
134
+ this.startImageIndex = this.currentIndex;
135
+ this.stopAutoRotate();
136
+ this.clearResumeTimer();
137
+ const viewer = e.currentTarget;
138
+ viewer.setPointerCapture(e.pointerId);
139
+ }
140
+ handlePointerMove(e) {
141
+ if (!this.isDragging || this.images.length === 0)
142
+ return;
143
+ const deltaX = e.clientX - this.startX;
144
+ const step = this.dragSensitivity;
145
+ const offset = Math.round(deltaX / step);
146
+ let newIndex = (this.startImageIndex - offset) % this.images.length;
147
+ if (newIndex < 0) {
148
+ newIndex += this.images.length;
149
+ }
150
+ if (newIndex !== this.currentIndex) {
151
+ this.currentIndex = newIndex;
152
+ }
153
+ }
154
+ handlePointerUp(e) {
155
+ if (!this.isDragging)
156
+ return;
157
+ this.isDragging = false;
158
+ try {
159
+ const viewer = e.currentTarget;
160
+ viewer.releasePointerCapture(e.pointerId);
161
+ }
162
+ catch {
163
+ // Ignore pointer capture release exceptions if target unmounted
164
+ }
165
+ this.scheduleAutoRotateResume();
166
+ }
167
+ handleKeyDown(e) {
168
+ if (e.key === 'ArrowRight') {
169
+ e.preventDefault();
170
+ this.stopAutoRotate();
171
+ this.clearResumeTimer();
172
+ this.next();
173
+ this.scheduleAutoRotateResume();
174
+ }
175
+ else if (e.key === 'ArrowLeft') {
176
+ e.preventDefault();
177
+ this.stopAutoRotate();
178
+ this.clearResumeTimer();
179
+ this.prev();
180
+ this.scheduleAutoRotateResume();
181
+ }
182
+ }
183
+ handleSlotChange(e) {
184
+ const slot = e.target;
185
+ const elements = slot.assignedElements({ flatten: true }).filter(el => el.tagName === 'IMG');
186
+ if (elements.length > 0) {
187
+ this.images = elements.map(el => el.getAttribute('src') || el.src || '');
188
+ this.currentIndex = 0;
189
+ this.firstFrameReady = false;
190
+ }
191
+ }
192
+ render() {
193
+ const progressPercent = this.images.length > 0
194
+ ? ((this.currentIndex + 1) / this.images.length) * 100
195
+ : 0;
196
+ return html `
197
+ <slot @slotchange=${this.handleSlotChange} style="display: none;"></slot>
198
+ <div
199
+ part="viewer"
200
+ class="viewer ${this.isDragging ? 'dragging' : ''}"
201
+ tabindex="0"
202
+ role="region"
203
+ aria-label=${msg('360 degree product view. Use drag, arrow keys, or buttons to rotate.')}
204
+ @pointerdown=${this.handlePointerDown}
205
+ @pointermove=${this.handlePointerMove}
206
+ @pointerup=${this.handlePointerUp}
207
+ @pointercancel=${this.handlePointerUp}
208
+ @keydown=${this.handleKeyDown}
209
+ >
210
+ ${this.images.length > 0 ? html `
211
+ <div
212
+ class="frames ${this.firstFrameReady ? 'ready' : ''}"
213
+ style="aspect-ratio: ${this.aspectRatio};"
214
+ >
215
+ ${this.images.map((src, i) => html `
216
+ <img
217
+ part="image"
218
+ class="frame ${i === this.currentIndex ? 'frame-active' : ''}"
219
+ src="${src}"
220
+ alt=""
221
+ aria-hidden="${i !== this.currentIndex}"
222
+ @load=${(e) => this.handleFrameLoad(e, i)}
223
+ />
224
+ `)}
225
+ </div>
226
+ ` : html `
227
+ <p style="padding: 24px; text-align: center; color: var(--uibit-360-viewer-button-color);">No images provided</p>
228
+ `}
229
+
230
+ ${!this.firstFrameReady && this.images.length > 0 ? html `
231
+ <div part="skeleton" class="skeleton"></div>
232
+ ` : ''}
233
+
234
+ ${this.firstFrameReady && !this.isDragging ? html `
235
+ <slot name="hint">
236
+ <div part="drag-hint" class="drag-hint">
237
+ ${getIcon('move', 16, fromLucide(Move))}
238
+ Drag to rotate
239
+ </div>
240
+ </slot>
241
+ ` : ''}
242
+
243
+ ${this.showControls && this.firstFrameReady ? html `
244
+ <slot name="prev" @click=${(e) => {
245
+ e.stopPropagation();
246
+ this.stopAutoRotate();
247
+ this.clearResumeTimer();
248
+ this.prev();
249
+ this.scheduleAutoRotateResume();
250
+ }}>
251
+ <button
252
+ part="nav-button nav-button-prev"
253
+ class="nav-button nav-button-prev"
254
+ aria-label=${msg('Rotate left')}
255
+ >
256
+ ${getIcon('chevron-left', 20, fromLucide(ChevronLeft))}
257
+ </button>
258
+ </slot>
259
+ <slot name="next" @click=${(e) => {
260
+ e.stopPropagation();
261
+ this.stopAutoRotate();
262
+ this.clearResumeTimer();
263
+ this.next();
264
+ this.scheduleAutoRotateResume();
265
+ }}>
266
+ <button
267
+ part="nav-button nav-button-next"
268
+ class="nav-button nav-button-next"
269
+ aria-label=${msg('Rotate right')}
270
+ >
271
+ ${getIcon('chevron-right', 20, fromLucide(ChevronRight))}
272
+ </button>
273
+ </slot>
274
+ ` : ''}
275
+
276
+ ${this.showProgressBar && this.firstFrameReady ? html `
277
+ <div part="progress-track" class="progress-track" role="progressbar" aria-valuemin="1" aria-valuemax="${this.images.length}" aria-valuenow="${this.currentIndex + 1}">
278
+ <div part="progress-bar" class="progress-bar" style="width: ${progressPercent}%"></div>
279
+ </div>
280
+ ` : ''}
281
+ </div>
282
+ `;
283
+ }
284
+ };
285
+ __decorate([
286
+ state()
287
+ ], Viewer360.prototype, "images", void 0);
288
+ __decorate([
289
+ property({ type: Boolean, attribute: 'auto-rotate' })
290
+ ], Viewer360.prototype, "autoRotate", void 0);
291
+ __decorate([
292
+ property({ type: Number, attribute: 'rotation-speed' })
293
+ ], Viewer360.prototype, "rotationSpeed", void 0);
294
+ __decorate([
295
+ property({ type: Number, attribute: 'drag-sensitivity' })
296
+ ], Viewer360.prototype, "dragSensitivity", void 0);
297
+ __decorate([
298
+ property({ type: Boolean, attribute: 'show-controls' })
299
+ ], Viewer360.prototype, "showControls", void 0);
300
+ __decorate([
301
+ property({ type: Boolean, attribute: 'show-progress-bar' })
302
+ ], Viewer360.prototype, "showProgressBar", void 0);
303
+ __decorate([
304
+ state()
305
+ ], Viewer360.prototype, "currentIndex", void 0);
306
+ __decorate([
307
+ state()
308
+ ], Viewer360.prototype, "isDragging", void 0);
309
+ __decorate([
310
+ state()
311
+ ], Viewer360.prototype, "firstFrameReady", void 0);
312
+ __decorate([
313
+ state()
314
+ ], Viewer360.prototype, "aspectRatio", void 0);
315
+ Viewer360 = __decorate([
316
+ customElement('uibit-360-viewer')
317
+ ], Viewer360);
318
+ export { Viewer360 };
319
+ export default Viewer360;
320
+ //# sourceMappingURL=360-viewer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"360-viewer.js","sourceRoot":"","sources":["../src/360-viewer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import { Component, ChangeDetectionStrategy, ElementRef, input, effect, output, booleanAttribute, numberAttribute } from '@angular/core';
2
+ import '@uibit/360-viewer';
3
+ import type { Viewer360 as HTMLElementClass } from '@uibit/360-viewer';
4
+
5
+ @Component({
6
+ selector: 'uibit-360-viewer',
7
+ template: '<ng-content></ng-content>',
8
+ changeDetection: ChangeDetectionStrategy.OnPush,
9
+ standalone: true,
10
+ host: {
11
+ '(change)': 'change.emit($event)'
12
+ }
13
+ })
14
+ export class NgxViewer360 {
15
+ constructor(private el: ElementRef<HTMLElementClass>) {
16
+ effect(() => {
17
+ if (this.el.nativeElement) {
18
+ this.el.nativeElement.autoRotate = this.autoRotate();
19
+ }
20
+ });
21
+ effect(() => {
22
+ if (this.el.nativeElement) {
23
+ this.el.nativeElement.rotationSpeed = this.rotationSpeed();
24
+ }
25
+ });
26
+ effect(() => {
27
+ if (this.el.nativeElement) {
28
+ this.el.nativeElement.dragSensitivity = this.dragSensitivity();
29
+ }
30
+ });
31
+ effect(() => {
32
+ if (this.el.nativeElement) {
33
+ this.el.nativeElement.showControls = this.showControls();
34
+ }
35
+ });
36
+ effect(() => {
37
+ if (this.el.nativeElement) {
38
+ this.el.nativeElement.showProgressBar = this.showProgressBar();
39
+ }
40
+ });
41
+ }
42
+
43
+ readonly autoRotate = input<boolean, any>(false, { transform: booleanAttribute });
44
+ readonly rotationSpeed = input<number, any>(150, { transform: numberAttribute });
45
+ readonly dragSensitivity = input<number, any>(15, { transform: numberAttribute });
46
+ readonly showControls = input<boolean, any>(true, { transform: booleanAttribute });
47
+ readonly showProgressBar = input<boolean, any>(true, { transform: booleanAttribute });
48
+
49
+ readonly change = output<CustomEvent<{ index: number, total: number }>>();
50
+ }
@@ -0,0 +1,10 @@
1
+ import type { Viewer360 as HTMLElementClass } from '@uibit/360-viewer';
2
+ import '@uibit/360-viewer';
3
+
4
+ declare global {
5
+ namespace astroHTML.JSX {
6
+ interface IntrinsicElements {
7
+ 'uibit-360-viewer': Partial<HTMLElementClass> & astroHTML.JSX.HTMLAttributes;
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,7 @@
1
+ import { defineNuxtPlugin } from '#app';
2
+
3
+ export default defineNuxtPlugin(() => {
4
+ if (process.client) {
5
+ import('@uibit/360-viewer');
6
+ }
7
+ });
@@ -0,0 +1,17 @@
1
+ import type { JSX } from 'preact';
2
+ import type { Viewer360 as HTMLElementClass } from '@uibit/360-viewer';
3
+ import '@uibit/360-viewer';
4
+
5
+ declare module 'preact' {
6
+ namespace JSX {
7
+ interface IntrinsicElements {
8
+ 'uibit-360-viewer': JSX.HTMLAttributes<HTMLElementClass> & {
9
+ autoRotate?: boolean;
10
+ rotationSpeed?: number;
11
+ dragSensitivity?: number;
12
+ showControls?: boolean;
13
+ showProgressBar?: boolean;
14
+ };
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,10 @@
1
+ import { component$, Slot } from '@builder.io/qwik';
2
+ import '@uibit/360-viewer';
3
+
4
+ export const Viewer360 = component$<any>((props) => {
5
+ return (
6
+ <uibit-360-viewer {...props}>
7
+ <Slot />
8
+ </uibit-360-viewer>
9
+ );
10
+ });
@@ -0,0 +1,22 @@
1
+ import type { HTMLAttributes, ClassAttributes } from 'react';
2
+ import type { Viewer360 as HTMLElementClass } from '@uibit/360-viewer';
3
+ import '@uibit/360-viewer';
4
+
5
+ declare global {
6
+ namespace React {
7
+ namespace JSX {
8
+ interface IntrinsicElements {
9
+ 'uibit-360-viewer': ClassAttributes<HTMLElementClass> & HTMLAttributes<HTMLElementClass> & {
10
+ children?: React.ReactNode;
11
+ class?: string;
12
+ autoRotate?: boolean;
13
+ rotationSpeed?: number;
14
+ dragSensitivity?: number;
15
+ showControls?: boolean;
16
+ showProgressBar?: boolean;
17
+ onChange?: (event: any) => void;
18
+ };
19
+ }
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,18 @@
1
+ import type { JSX } from 'solid-js';
2
+ import type { Viewer360 as HTMLElementClass } from '@uibit/360-viewer';
3
+ import '@uibit/360-viewer';
4
+
5
+ declare module 'solid-js' {
6
+ namespace JSX {
7
+ interface IntrinsicElements {
8
+ 'uibit-360-viewer': Partial<HTMLElementClass> & JSX.HTMLAttributes<HTMLElementClass> & {
9
+ autoRotate?: boolean;
10
+ rotationSpeed?: number;
11
+ dragSensitivity?: number;
12
+ showControls?: boolean;
13
+ showProgressBar?: boolean;
14
+ "on:change"?: (event: CustomEvent) => void;
15
+ };
16
+ }
17
+ }
18
+ }
@@ -0,0 +1,16 @@
1
+ import type { Viewer360 as HTMLElementClass } from '@uibit/360-viewer';
2
+ import '@uibit/360-viewer';
3
+
4
+ declare module '@stencil/core' {
5
+ export namespace JSX {
6
+ interface IntrinsicElements {
7
+ 'uibit-360-viewer': HTMLElementClass & {
8
+ autoRotate?: boolean;
9
+ rotationSpeed?: number;
10
+ dragSensitivity?: number;
11
+ showControls?: boolean;
12
+ showProgressBar?: boolean;
13
+ };
14
+ }
15
+ }
16
+ }
@@ -0,0 +1,51 @@
1
+ <script lang="ts">
2
+ import '@uibit/360-viewer';
3
+ import type { Viewer360 as HTMLElementClass } from '@uibit/360-viewer';
4
+
5
+ let {
6
+ autoRotate = undefined,
7
+ rotationSpeed = undefined,
8
+ dragSensitivity = undefined,
9
+ showControls = undefined,
10
+ showProgressBar = undefined,
11
+ children
12
+ } = $props<{
13
+ children?: any;
14
+ autoRotate?: boolean;
15
+ rotationSpeed?: number;
16
+ dragSensitivity?: number;
17
+ showControls?: boolean;
18
+ showProgressBar?: boolean;
19
+
20
+ }>();
21
+
22
+ let elementRef: HTMLElementClass | null = $state(null);
23
+
24
+ $effect(() => {
25
+ if (elementRef && autoRotate !== undefined) {
26
+ elementRef.autoRotate = autoRotate;
27
+ }
28
+ if (elementRef && rotationSpeed !== undefined) {
29
+ elementRef.rotationSpeed = rotationSpeed;
30
+ }
31
+ if (elementRef && dragSensitivity !== undefined) {
32
+ elementRef.dragSensitivity = dragSensitivity;
33
+ }
34
+ if (elementRef && showControls !== undefined) {
35
+ elementRef.showControls = showControls;
36
+ }
37
+ if (elementRef && showProgressBar !== undefined) {
38
+ elementRef.showProgressBar = showProgressBar;
39
+ }
40
+ });
41
+
42
+ </script>
43
+
44
+ <uibit-360-viewer bind:this={elementRef} {...$$restProps}>
45
+
46
+ {#if children}
47
+ {@render children()}
48
+ {:else}
49
+ <slot />
50
+ {/if}
51
+ </uibit-360-viewer>
@@ -0,0 +1,28 @@
1
+ import { defineComponent, h } from 'vue';
2
+ import type { Viewer360 as HTMLElementClass } from '@uibit/360-viewer';
3
+ import '@uibit/360-viewer';
4
+
5
+ export const Viewer360 = defineComponent({
6
+ name: 'Viewer360',
7
+ props: {
8
+ autoRotate: { type: [String, Number, Boolean, Array, Object] as any },
9
+ rotationSpeed: { type: [String, Number, Boolean, Array, Object] as any },
10
+ dragSensitivity: { type: [String, Number, Boolean, Array, Object] as any },
11
+ showControls: { type: [String, Number, Boolean, Array, Object] as any },
12
+ showProgressBar: { type: [String, Number, Boolean, Array, Object] as any }
13
+ },
14
+ emits: ['change'],
15
+ setup(props, { slots, emit }) {
16
+ return () => {
17
+ const eventListeners: Record<string, any> = {};
18
+ eventListeners['onChange'] = (event: Event) => {
19
+ emit('change', event);
20
+ };
21
+
22
+ return h('uibit-360-viewer', {
23
+ ...props,
24
+ ...eventListeners
25
+ }, slots.default?.());
26
+ };
27
+ }
28
+ });
@@ -0,0 +1,3 @@
1
+ export { default, Viewer360 } from './360-viewer';
2
+ export type { Viewer360Config, Hotspot360 } from './types';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAClD,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { default, Viewer360 } from './360-viewer';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const styles: import("lit").CSSResult;
2
+ //# sourceMappingURL=styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,MAAM,yBA+LlB,CAAC"}