@happyvertical/smrt-assets 0.30.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.
Files changed (119) hide show
  1. package/AGENTS.md +78 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +7 -0
  4. package/README.md +136 -0
  5. package/dist/__smrt-register__.d.ts +2 -0
  6. package/dist/__smrt-register__.d.ts.map +1 -0
  7. package/dist/asset-association.d.ts +16 -0
  8. package/dist/asset-association.d.ts.map +1 -0
  9. package/dist/asset-associations.d.ts +27 -0
  10. package/dist/asset-associations.d.ts.map +1 -0
  11. package/dist/asset-capabilities.d.ts +137 -0
  12. package/dist/asset-capabilities.d.ts.map +1 -0
  13. package/dist/asset-conventions.d.ts +76 -0
  14. package/dist/asset-conventions.d.ts.map +1 -0
  15. package/dist/asset-metafield.d.ts +27 -0
  16. package/dist/asset-metafield.d.ts.map +1 -0
  17. package/dist/asset-metafields.d.ts +27 -0
  18. package/dist/asset-metafields.d.ts.map +1 -0
  19. package/dist/asset-runtime.d.ts +218 -0
  20. package/dist/asset-runtime.d.ts.map +1 -0
  21. package/dist/asset-serving.d.ts +146 -0
  22. package/dist/asset-serving.d.ts.map +1 -0
  23. package/dist/asset-status.d.ts +15 -0
  24. package/dist/asset-status.d.ts.map +1 -0
  25. package/dist/asset-statuses.d.ts +25 -0
  26. package/dist/asset-statuses.d.ts.map +1 -0
  27. package/dist/asset-store.d.ts +200 -0
  28. package/dist/asset-store.d.ts.map +1 -0
  29. package/dist/asset-type.d.ts +15 -0
  30. package/dist/asset-type.d.ts.map +1 -0
  31. package/dist/asset-types.d.ts +28 -0
  32. package/dist/asset-types.d.ts.map +1 -0
  33. package/dist/asset.d.ts +158 -0
  34. package/dist/asset.d.ts.map +1 -0
  35. package/dist/assets.d.ts +125 -0
  36. package/dist/assets.d.ts.map +1 -0
  37. package/dist/folder.d.ts +16 -0
  38. package/dist/folder.d.ts.map +1 -0
  39. package/dist/folders.d.ts +45 -0
  40. package/dist/folders.d.ts.map +1 -0
  41. package/dist/index.d.ts +21 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +2285 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/manifest.json +4079 -0
  46. package/dist/media-bundle-persistence.d.ts +99 -0
  47. package/dist/media-bundle-persistence.d.ts.map +1 -0
  48. package/dist/owned-asset-helpers.d.ts +20 -0
  49. package/dist/owned-asset-helpers.d.ts.map +1 -0
  50. package/dist/playground.d.ts +2 -0
  51. package/dist/playground.d.ts.map +1 -0
  52. package/dist/playground.js +127 -0
  53. package/dist/playground.js.map +1 -0
  54. package/dist/smrt-knowledge.json +1922 -0
  55. package/dist/svelte/ActionBar.svelte +203 -0
  56. package/dist/svelte/ActionBar.svelte.d.ts +5 -0
  57. package/dist/svelte/ActionBar.svelte.d.ts.map +1 -0
  58. package/dist/svelte/AssetDetail.svelte +521 -0
  59. package/dist/svelte/AssetDetail.svelte.d.ts +35 -0
  60. package/dist/svelte/AssetDetail.svelte.d.ts.map +1 -0
  61. package/dist/svelte/AssetGrid.svelte +351 -0
  62. package/dist/svelte/AssetGrid.svelte.d.ts +5 -0
  63. package/dist/svelte/AssetGrid.svelte.d.ts.map +1 -0
  64. package/dist/svelte/AssetList.svelte +436 -0
  65. package/dist/svelte/AssetList.svelte.d.ts +5 -0
  66. package/dist/svelte/AssetList.svelte.d.ts.map +1 -0
  67. package/dist/svelte/AssetManager.svelte +381 -0
  68. package/dist/svelte/AssetManager.svelte.d.ts +5 -0
  69. package/dist/svelte/AssetManager.svelte.d.ts.map +1 -0
  70. package/dist/svelte/AssetToolbar.svelte +388 -0
  71. package/dist/svelte/AssetToolbar.svelte.d.ts +5 -0
  72. package/dist/svelte/AssetToolbar.svelte.d.ts.map +1 -0
  73. package/dist/svelte/CreateAssetModal.svelte +373 -0
  74. package/dist/svelte/CreateAssetModal.svelte.d.ts +19 -0
  75. package/dist/svelte/CreateAssetModal.svelte.d.ts.map +1 -0
  76. package/dist/svelte/__tests__/ActionBar.test.js +72 -0
  77. package/dist/svelte/__tests__/AssetDetail.test.js +57 -0
  78. package/dist/svelte/__tests__/AssetGrid.test.js +69 -0
  79. package/dist/svelte/__tests__/AssetList.test.js +72 -0
  80. package/dist/svelte/__tests__/AssetManager.test.js +21 -0
  81. package/dist/svelte/__tests__/AssetManagerRoute.test.js +16 -0
  82. package/dist/svelte/__tests__/AssetToolbar.test.js +39 -0
  83. package/dist/svelte/__tests__/CreateAssetModal.test.js +42 -0
  84. package/dist/svelte/i18n.d.ts +76 -0
  85. package/dist/svelte/i18n.d.ts.map +1 -0
  86. package/dist/svelte/i18n.js +87 -0
  87. package/dist/svelte/index.d.ts +19 -0
  88. package/dist/svelte/index.d.ts.map +1 -0
  89. package/dist/svelte/index.js +30 -0
  90. package/dist/svelte/playground/AssetDetailPreview.svelte +131 -0
  91. package/dist/svelte/playground/AssetDetailPreview.svelte.d.ts +8 -0
  92. package/dist/svelte/playground/AssetDetailPreview.svelte.d.ts.map +1 -0
  93. package/dist/svelte/playground/CreateAssetModalPreview.svelte +151 -0
  94. package/dist/svelte/playground/CreateAssetModalPreview.svelte.d.ts +4 -0
  95. package/dist/svelte/playground/CreateAssetModalPreview.svelte.d.ts.map +1 -0
  96. package/dist/svelte/playground.d.ts +60 -0
  97. package/dist/svelte/playground.d.ts.map +1 -0
  98. package/dist/svelte/playground.js +93 -0
  99. package/dist/svelte/routes/AssetManagerRoute.svelte +209 -0
  100. package/dist/svelte/routes/AssetManagerRoute.svelte.d.ts +4 -0
  101. package/dist/svelte/routes/AssetManagerRoute.svelte.d.ts.map +1 -0
  102. package/dist/svelte/routes/index.d.ts +2 -0
  103. package/dist/svelte/routes/index.d.ts.map +1 -0
  104. package/dist/svelte/routes/index.js +1 -0
  105. package/dist/svelte/routes/shared.d.ts +25 -0
  106. package/dist/svelte/routes/shared.d.ts.map +1 -0
  107. package/dist/svelte/routes/shared.js +31 -0
  108. package/dist/svelte/types.d.ts +179 -0
  109. package/dist/svelte/types.d.ts.map +1 -0
  110. package/dist/svelte/types.js +6 -0
  111. package/dist/types.d.ts +80 -0
  112. package/dist/types.d.ts.map +1 -0
  113. package/dist/types.js +2 -0
  114. package/dist/types.js.map +1 -0
  115. package/dist/ui.d.ts +10 -0
  116. package/dist/ui.d.ts.map +1 -0
  117. package/dist/ui.js +85 -0
  118. package/dist/ui.js.map +1 -0
  119. package/package.json +102 -0
@@ -0,0 +1,388 @@
1
+ <script lang="ts">
2
+ import { onDestroy } from 'svelte';
3
+ /**
4
+ * AssetToolbar - Filter bar and view controls for the Asset Manager
5
+ *
6
+ * Provides search, type/tag filtering, sort controls, view mode toggle,
7
+ * and an upload button.
8
+ */
9
+
10
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
11
+ import { M } from './i18n.js';
12
+ import type {
13
+ AssetFilters,
14
+ AssetSort,
15
+ AssetSortField,
16
+ AssetToolbarProps,
17
+ AssetViewMode,
18
+ } from './types';
19
+
20
+ const { t } = useI18n();
21
+
22
+ let {
23
+ view,
24
+ filters,
25
+ sort,
26
+ onviewchange,
27
+ onViewChange,
28
+ onfilterchange,
29
+ onFilterChange,
30
+ onsortchange,
31
+ onSortChange,
32
+ onupload,
33
+ onUpload,
34
+ }: AssetToolbarProps = $props();
35
+
36
+ function getInitialToolbarState() {
37
+ return {
38
+ searchValue: filters.search,
39
+ filters,
40
+ };
41
+ }
42
+
43
+ const initialToolbarState = getInitialToolbarState();
44
+ let searchValue = $state(initialToolbarState.searchValue);
45
+ let debounceTimer: ReturnType<typeof setTimeout> | null = null;
46
+ let appliedFilters: AssetFilters | undefined = initialToolbarState.filters;
47
+
48
+ $effect(() => {
49
+ if (appliedFilters === filters) {
50
+ return;
51
+ }
52
+
53
+ appliedFilters = filters;
54
+ searchValue = filters.search;
55
+ });
56
+
57
+ onDestroy(() => {
58
+ if (debounceTimer) clearTimeout(debounceTimer);
59
+ });
60
+
61
+ function handleSearch(e: Event) {
62
+ const target = e.target as HTMLInputElement;
63
+ searchValue = target.value;
64
+
65
+ if (debounceTimer) clearTimeout(debounceTimer);
66
+ debounceTimer = setTimeout(() => {
67
+ (onfilterchange ?? onFilterChange)({ ...filters, search: searchValue });
68
+ }, 300);
69
+ }
70
+
71
+ function handleClearSearch() {
72
+ searchValue = '';
73
+ (onfilterchange ?? onFilterChange)({ ...filters, search: '' });
74
+ }
75
+
76
+ function handleSortChange(e: Event) {
77
+ const target = e.target as HTMLSelectElement;
78
+ const [field, direction] = target.value.split(':') as [
79
+ AssetSortField,
80
+ 'asc' | 'desc',
81
+ ];
82
+ (onsortchange ?? onSortChange)({ field, direction });
83
+ }
84
+
85
+ function handleTypeFilter(e: Event) {
86
+ const target = e.target as HTMLSelectElement;
87
+ const types = target.value ? [target.value] : [];
88
+ (onfilterchange ?? onFilterChange)({ ...filters, types });
89
+ }
90
+
91
+ const sortValue = $derived(`${sort.field}:${sort.direction}`);
92
+ const typeValue = $derived(filters.types.length > 0 ? filters.types[0] : '');
93
+
94
+ const views: { key: AssetViewMode; label: string; icon: string }[] = [
95
+ { key: 'grid', label: 'Grid', icon: 'grid' },
96
+ { key: 'list', label: 'List', icon: 'list' },
97
+ ];
98
+ </script>
99
+
100
+ <div class="asset-toolbar">
101
+ <div class="asset-toolbar__left">
102
+ <!-- Search -->
103
+ <div class="asset-toolbar__search">
104
+ <span class="search-icon" aria-hidden="true">
105
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
106
+ <circle cx="11" cy="11" r="8"></circle>
107
+ <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
108
+ </svg>
109
+ </span>
110
+ <input
111
+ type="search"
112
+ class="search-field"
113
+ placeholder={t(M['assets.asset_toolbar.search_placeholder'])}
114
+ value={searchValue}
115
+ oninput={handleSearch}
116
+ aria-label={t(M['assets.asset_toolbar.search_assets'])}
117
+ />
118
+ {#if searchValue}
119
+ <button type="button" class="search-clear" onclick={handleClearSearch} aria-label={t(M['assets.asset_toolbar.clear_search'])}>
120
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
121
+ <line x1="18" y1="6" x2="6" y2="18"></line>
122
+ <line x1="6" y1="6" x2="18" y2="18"></line>
123
+ </svg>
124
+ </button>
125
+ {/if}
126
+ </div>
127
+
128
+ <!-- Type Filter -->
129
+ <select class="asset-toolbar__select" value={typeValue} onchange={handleTypeFilter} aria-label={t(M['assets.asset_toolbar.filter_by_type'])}>
130
+ <option value="">{t(M['assets.asset_toolbar.all_types'])}</option>
131
+ <option value="image">Images</option>
132
+ <option value="video">Videos</option>
133
+ <option value="document">Documents</option>
134
+ <option value="audio">Audio</option>
135
+ </select>
136
+
137
+ <!-- Sort -->
138
+ <select class="asset-toolbar__select" value={sortValue} onchange={handleSortChange} aria-label={t(M['assets.asset_toolbar.sort_assets'])}>
139
+ <option value="createdAt:desc">{t(M['assets.asset_toolbar.newest_first'])}</option>
140
+ <option value="createdAt:asc">{t(M['assets.asset_toolbar.oldest_first'])}</option>
141
+ <option value="name:asc">{t(M['assets.asset_toolbar.name_a_z'])}</option>
142
+ <option value="name:desc">{t(M['assets.asset_toolbar.name_z_a'])}</option>
143
+ <option value="updatedAt:desc">{t(M['assets.asset_toolbar.recently_updated'])}</option>
144
+ </select>
145
+ </div>
146
+
147
+ <div class="asset-toolbar__right">
148
+ <!-- View Toggle -->
149
+ <div class="view-toggle" role="group" aria-label={t(M['assets.asset_toolbar.view_mode'])}>
150
+ {#each views as v (v.key)}
151
+ <button
152
+ type="button"
153
+ class="view-toggle__btn"
154
+ class:view-toggle__btn--active={view === v.key}
155
+ onclick={() => (onviewchange ?? onViewChange)(v.key)}
156
+ aria-label={t(M['assets.asset_toolbar.view_label'], { label: v.label })}
157
+ aria-pressed={view === v.key}
158
+ >
159
+ {#if v.icon === 'grid'}
160
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
161
+ <rect x="3" y="3" width="7" height="7"></rect>
162
+ <rect x="14" y="3" width="7" height="7"></rect>
163
+ <rect x="3" y="14" width="7" height="7"></rect>
164
+ <rect x="14" y="14" width="7" height="7"></rect>
165
+ </svg>
166
+ {:else}
167
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
168
+ <line x1="8" y1="6" x2="21" y2="6"></line>
169
+ <line x1="8" y1="12" x2="21" y2="12"></line>
170
+ <line x1="8" y1="18" x2="21" y2="18"></line>
171
+ <line x1="3" y1="6" x2="3.01" y2="6"></line>
172
+ <line x1="3" y1="12" x2="3.01" y2="12"></line>
173
+ <line x1="3" y1="18" x2="3.01" y2="18"></line>
174
+ </svg>
175
+ {/if}
176
+ </button>
177
+ {/each}
178
+ </div>
179
+
180
+ <!-- Upload Button -->
181
+ <button
182
+ type="button"
183
+ class="upload-btn"
184
+ onclick={() => (onupload ?? onUpload)()}
185
+ >
186
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
187
+ <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"></path>
188
+ <polyline points="17 8 12 3 7 8"></polyline>
189
+ <line x1="12" y1="3" x2="12" y2="15"></line>
190
+ </svg>
191
+ Upload
192
+ </button>
193
+ </div>
194
+ </div>
195
+
196
+ <style>
197
+ .asset-toolbar {
198
+ display: flex;
199
+ align-items: center;
200
+ justify-content: space-between;
201
+ gap: var(--smrt-spacing-3, 0.75rem);
202
+ padding: var(--smrt-spacing-3, 0.75rem) var(--smrt-spacing-4, 1rem);
203
+ background: var(--smrt-color-surface, #ffffff);
204
+ border-bottom: 1px solid var(--smrt-color-outline-variant, #e5e7eb);
205
+ flex-wrap: wrap;
206
+ }
207
+
208
+ .asset-toolbar__left {
209
+ display: flex;
210
+ align-items: center;
211
+ gap: var(--smrt-spacing-2, 0.5rem);
212
+ flex: 1;
213
+ min-width: 0;
214
+ flex-wrap: wrap;
215
+ }
216
+
217
+ .asset-toolbar__right {
218
+ display: flex;
219
+ align-items: center;
220
+ gap: var(--smrt-spacing-2, 0.5rem);
221
+ flex-shrink: 0;
222
+ }
223
+
224
+ /* Search */
225
+ .asset-toolbar__search {
226
+ display: flex;
227
+ align-items: center;
228
+ flex: 1;
229
+ min-width: 180px;
230
+ max-width: 320px;
231
+ height: 36px;
232
+ background: var(--smrt-color-surface-container-low, #f9fafb);
233
+ border: 1px solid var(--smrt-color-outline-variant, #e5e7eb);
234
+ border-radius: var(--smrt-radius-medium, 0.5rem);
235
+ transition: border-color 150ms ease;
236
+ }
237
+
238
+ .asset-toolbar__search:focus-within {
239
+ border-color: var(--smrt-color-primary, #005ac1);
240
+ box-shadow: 0 0 0 2px var(--smrt-color-primary-container, rgba(0, 90, 193, 0.1));
241
+ }
242
+
243
+ .search-icon {
244
+ display: flex;
245
+ align-items: center;
246
+ padding-left: var(--smrt-spacing-2, 0.5rem);
247
+ color: var(--smrt-color-on-surface-variant, #6b7280);
248
+ }
249
+
250
+ .search-field {
251
+ flex: 1;
252
+ border: none;
253
+ background: transparent;
254
+ padding: var(--smrt-spacing-1, 0.25rem) var(--smrt-spacing-2, 0.5rem);
255
+ font-family: inherit;
256
+ font-size: var(--smrt-typography-body-medium-size, 0.875rem);
257
+ color: var(--smrt-color-on-surface, #111827);
258
+ outline: none;
259
+ min-width: 0;
260
+ }
261
+
262
+ .search-field::placeholder {
263
+ color: var(--smrt-color-on-surface-variant, #9ca3af);
264
+ }
265
+
266
+ .search-field::-webkit-search-cancel-button {
267
+ display: none;
268
+ }
269
+
270
+ .search-clear {
271
+ display: flex;
272
+ align-items: center;
273
+ justify-content: center;
274
+ width: 24px;
275
+ height: 24px;
276
+ margin-right: var(--smrt-spacing-1, 0.25rem);
277
+ padding: 0;
278
+ border: none;
279
+ background: transparent;
280
+ color: var(--smrt-color-on-surface-variant, #6b7280);
281
+ cursor: pointer;
282
+ border-radius: var(--smrt-radius-full, 9999px);
283
+ }
284
+
285
+ .search-clear:hover {
286
+ color: var(--smrt-color-on-surface, #111827);
287
+ background: var(--smrt-color-surface-container, #f3f4f6);
288
+ }
289
+
290
+ /* Selects */
291
+ .asset-toolbar__select {
292
+ height: 36px;
293
+ padding: 0 var(--smrt-spacing-3, 0.75rem);
294
+ background: var(--smrt-color-surface-container-low, #f9fafb);
295
+ border: 1px solid var(--smrt-color-outline-variant, #e5e7eb);
296
+ border-radius: var(--smrt-radius-medium, 0.5rem);
297
+ font-family: inherit;
298
+ font-size: var(--smrt-typography-body-medium-size, 0.875rem);
299
+ color: var(--smrt-color-on-surface, #111827);
300
+ cursor: pointer;
301
+ }
302
+
303
+ .asset-toolbar__select:focus {
304
+ outline: none;
305
+ border-color: var(--smrt-color-primary, #005ac1);
306
+ }
307
+
308
+ /* View Toggle */
309
+ .view-toggle {
310
+ display: flex;
311
+ border: 1px solid var(--smrt-color-outline-variant, #e5e7eb);
312
+ border-radius: var(--smrt-radius-medium, 0.5rem);
313
+ overflow: hidden;
314
+ }
315
+
316
+ .view-toggle__btn {
317
+ display: flex;
318
+ align-items: center;
319
+ justify-content: center;
320
+ width: 36px;
321
+ height: 36px;
322
+ padding: 0;
323
+ border: none;
324
+ background: var(--smrt-color-surface, #ffffff);
325
+ color: var(--smrt-color-on-surface-variant, #6b7280);
326
+ cursor: pointer;
327
+ transition: all 150ms ease;
328
+ }
329
+
330
+ .view-toggle__btn:not(:last-child) {
331
+ border-right: 1px solid var(--smrt-color-outline-variant, #e5e7eb);
332
+ }
333
+
334
+ .view-toggle__btn:hover {
335
+ background: var(--smrt-color-surface-container-low, #f9fafb);
336
+ }
337
+
338
+ .view-toggle__btn--active {
339
+ background: var(--smrt-color-primary-container, #dbeafe);
340
+ color: var(--smrt-color-on-primary-container, #002d6c);
341
+ }
342
+
343
+ /* Upload Button */
344
+ .upload-btn {
345
+ display: inline-flex;
346
+ align-items: center;
347
+ gap: var(--smrt-spacing-1, 0.25rem);
348
+ height: 36px;
349
+ padding: 0 var(--smrt-spacing-3, 0.75rem);
350
+ font-family: inherit;
351
+ font-size: var(--smrt-typography-body-medium-size, 0.875rem);
352
+ font-weight: var(--smrt-typography-weight-medium, 500);
353
+ color: var(--smrt-color-on-primary, #ffffff);
354
+ background: var(--smrt-color-primary, #005ac1);
355
+ border: none;
356
+ border-radius: var(--smrt-radius-medium, 0.5rem);
357
+ cursor: pointer;
358
+ transition: box-shadow 150ms ease;
359
+ }
360
+
361
+ .upload-btn:hover {
362
+ box-shadow: var(--smrt-elevation-2, 0 1px 3px rgba(0,0,0,0.1));
363
+ }
364
+
365
+ .upload-btn:focus-visible {
366
+ outline: 2px solid var(--smrt-color-primary, #005ac1);
367
+ outline-offset: 2px;
368
+ }
369
+
370
+ @media (max-width: 640px) {
371
+ .asset-toolbar {
372
+ flex-direction: column;
373
+ align-items: stretch;
374
+ }
375
+
376
+ .asset-toolbar__left {
377
+ flex-direction: column;
378
+ }
379
+
380
+ .asset-toolbar__search {
381
+ max-width: none;
382
+ }
383
+
384
+ .asset-toolbar__right {
385
+ justify-content: space-between;
386
+ }
387
+ }
388
+ </style>
@@ -0,0 +1,5 @@
1
+ import type { AssetToolbarProps } from './types';
2
+ declare const AssetToolbar: import("svelte").Component<AssetToolbarProps, {}, "">;
3
+ type AssetToolbar = ReturnType<typeof AssetToolbar>;
4
+ export default AssetToolbar;
5
+ //# sourceMappingURL=AssetToolbar.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AssetToolbar.svelte.d.ts","sourceRoot":"","sources":["../../src/svelte/AssetToolbar.svelte.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAIV,iBAAiB,EAElB,MAAM,SAAS,CAAC;AA4KjB,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}