@aphexcms/cms-core 0.1.15 → 0.1.17

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Benjamin Sinidol
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -132,7 +132,9 @@ export function generateTypes(schemas) {
132
132
  const objectSchemas = schemas.filter((s) => s.type === 'object');
133
133
  // Generate interfaces for all schemas
134
134
  const objectInterfaces = objectSchemas.map((s) => generateInterface(s, schemaMap)).join('\n\n');
135
- const documentInterfaces = documentSchemas.map((s) => generateInterface(s, schemaMap)).join('\n\n');
135
+ const documentInterfaces = documentSchemas
136
+ .map((s) => generateInterface(s, schemaMap))
137
+ .join('\n\n');
136
138
  // Generate Collections interface augmentation
137
139
  const collectionsAugmentation = generateCollectionsAugmentation(documentSchemas);
138
140
  // Build the complete file
package/dist/cli/index.js CHANGED
File without changes
@@ -1,10 +1,15 @@
1
1
  <script lang="ts">
2
2
  import { Button } from '@aphexcms/ui/shadcn/button';
3
+ import { Input } from '@aphexcms/ui/shadcn/input';
4
+ import { Textarea } from '@aphexcms/ui/shadcn/textarea';
5
+ import { Checkbox } from '@aphexcms/ui/shadcn/checkbox';
3
6
  import * as DropdownMenu from '@aphexcms/ui/shadcn/dropdown-menu';
7
+ import * as Card from '@aphexcms/ui/shadcn/card';
4
8
  import type { ArrayField as ArrayFieldType, SchemaType } from '../../../types/schemas';
5
9
  import { getArrayTypes, getSchemaByName } from '../../../schema-utils/utils';
6
10
  import { getSchemaContext } from '../../../schema-context.svelte';
7
11
  import ObjectModal from '../ObjectModal.svelte';
12
+ import ImageField from './ImageField.svelte';
8
13
 
9
14
  interface Props {
10
15
  field: ArrayFieldType;
@@ -19,19 +24,72 @@
19
24
  // Get schemas from context
20
25
  const schemas = getSchemaContext();
21
26
 
22
- // Get available types for this array field
27
+ // Determine if this is a primitive array or object array
28
+ // If the type is not found in schemas, it's a primitive type
29
+ const isPrimitiveArray = $derived(
30
+ field.of && field.of.length > 0 && field.of[0]?.type && !getSchemaByName(schemas, field.of[0].type)
31
+ );
32
+ const primitiveType = $derived(isPrimitiveArray ? field.of?.[0]?.type : null);
33
+
34
+ // Get available types for this array field (for object arrays)
23
35
  const availableTypes = $derived(getArrayTypes(schemas, field));
24
36
 
25
- // Modal state
37
+ // Modal state (for object arrays)
26
38
  let modalOpen = $state(false);
27
39
  let editingIndex = $state<number | null>(null);
28
40
  let editingType = $state<string | null>(null);
29
41
  let editingSchema = $state<SchemaType | null>(null);
30
42
  let editingValue = $state<Record<string, any>>({});
31
43
 
44
+ // Image modal state
45
+ let imageModalOpen = $state(false);
46
+ let imageModalValue = $state<any>(null);
47
+
32
48
  // Ensure value is always an array
33
49
  const arrayValue = $derived(Array.isArray(value) ? value : []);
34
50
 
51
+ // Primitive array functions
52
+ function handleAddPrimitive() {
53
+ if (readonly) return;
54
+
55
+ // For images, open the modal
56
+ if (primitiveType === 'image') {
57
+ imageModalValue = null;
58
+ imageModalOpen = true;
59
+ return;
60
+ }
61
+
62
+ // For other primitives, add a new empty item
63
+ const newArray = [...arrayValue];
64
+ const defaultValue = primitiveType === 'boolean' ? false : primitiveType === 'number' ? 0 : '';
65
+ newArray.push(defaultValue);
66
+ onUpdate(newArray);
67
+ }
68
+
69
+ function handleUpdatePrimitive(index: number, newValue: any) {
70
+ if (readonly) return;
71
+ const newArray = [...arrayValue];
72
+ newArray[index] = newValue;
73
+ onUpdate(newArray);
74
+ }
75
+
76
+ function handleImageModalClose() {
77
+ imageModalOpen = false;
78
+ imageModalValue = null;
79
+ }
80
+
81
+ function handleImageUpload(newValue: any) {
82
+ if (newValue) {
83
+ // Auto-add the image to the array
84
+ const newArray = [...arrayValue, newValue];
85
+ onUpdate(newArray);
86
+ // Close the modal
87
+ imageModalOpen = false;
88
+ imageModalValue = null;
89
+ }
90
+ }
91
+
92
+ // Object array functions
35
93
  function handleTypeSelected(selectedType: string) {
36
94
  if (readonly || !selectedType) return;
37
95
 
@@ -135,120 +193,249 @@
135
193
  <div class="border-border space-y-4 rounded-md border p-4">
136
194
  <h4 class="text-sm font-medium">{field.title}</h4>
137
195
 
138
- <!-- Array items -->
139
- {#if arrayValue.length > 0}
140
- <div class="space-y-2">
141
- {#each arrayValue as item, index (index)}
142
- <div class="border-border/50 space-y-2 rounded border p-3">
143
- <div class="flex items-center justify-between">
144
- <div class="flex items-center gap-2">
196
+ {#if isPrimitiveArray}
197
+ <!-- Primitive array UI -->
198
+ {#if arrayValue.length === 0}
199
+ <!-- Empty state -->
200
+ <div class="border-border/50 bg-muted/30 flex items-center justify-center rounded border border-dashed p-6">
201
+ <p class="text-muted-foreground text-sm">No items</p>
202
+ </div>
203
+ {:else}
204
+ <div class="space-y-2">
205
+ {#each arrayValue as item, index (index)}
206
+ {#if primitiveType === 'image'}
207
+ <!-- Image item in compact mode -->
208
+ <ImageField
209
+ field={{
210
+ ...field.of?.[0],
211
+ name: `image-${index}`,
212
+ type: 'image',
213
+ title: `Image ${index + 1}`
214
+ }}
215
+ value={item}
216
+ onUpdate={(newValue) => {
217
+ const newArray = [...arrayValue];
218
+ if (newValue === null) {
219
+ // Remove the image if null
220
+ newArray.splice(index, 1);
221
+ } else {
222
+ newArray[index] = newValue;
223
+ }
224
+ onUpdate(newArray);
225
+ }}
226
+ {readonly}
227
+ compact={true}
228
+ />
229
+ {:else}
230
+ <!-- Always-editable primitive with options menu -->
231
+ <div class="border-border/50 flex items-center gap-2 rounded border p-2">
145
232
  <span class="text-muted-foreground text-xs">#{index + 1}</span>
146
- <h5 class="text-sm font-medium">{getItemTitle(item)}</h5>
147
- {#if item._type}
148
- <span class="bg-muted rounded px-2 py-1 text-xs">{item._type}</span>
233
+ {#if primitiveType === 'boolean'}
234
+ <div class="flex flex-1 items-center gap-2">
235
+ <Checkbox
236
+ checked={item}
237
+ onCheckedChange={(checked) => handleUpdatePrimitive(index, checked)}
238
+ disabled={readonly}
239
+ />
240
+ <span class="text-sm">{item ? 'True' : 'False'}</span>
241
+ </div>
242
+ {:else if primitiveType === 'text'}
243
+ <Textarea
244
+ value={item}
245
+ oninput={(e) => handleUpdatePrimitive(index, e.currentTarget.value)}
246
+ readonly={readonly}
247
+ class="flex-1"
248
+ rows={3}
249
+ placeholder="Enter text..."
250
+ />
251
+ {:else if primitiveType === 'number'}
252
+ <Input
253
+ type="number"
254
+ value={item}
255
+ oninput={(e) => handleUpdatePrimitive(index, parseFloat(e.currentTarget.value) || 0)}
256
+ readonly={readonly}
257
+ class="flex-1"
258
+ placeholder="Enter number..."
259
+ />
260
+ {:else}
261
+ <Input
262
+ value={item}
263
+ oninput={(e) => handleUpdatePrimitive(index, e.currentTarget.value)}
264
+ readonly={readonly}
265
+ class="flex-1"
266
+ placeholder="Enter value..."
267
+ />
268
+ {/if}
269
+ {#if !readonly}
270
+ <DropdownMenu.Root>
271
+ <DropdownMenu.Trigger>
272
+ <Button variant="ghost" size="sm" class="h-8 w-8 p-0">
273
+ <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
274
+ <path
275
+ stroke-linecap="round"
276
+ stroke-linejoin="round"
277
+ stroke-width="2"
278
+ d="M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z"
279
+ />
280
+ </svg>
281
+ </Button>
282
+ </DropdownMenu.Trigger>
283
+ <DropdownMenu.Content align="end">
284
+ <DropdownMenu.Item onclick={() => handleRemoveItem(index)}>
285
+ <svg class="mr-2 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
286
+ <path
287
+ stroke-linecap="round"
288
+ stroke-linejoin="round"
289
+ stroke-width="2"
290
+ d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
291
+ />
292
+ </svg>
293
+ Remove
294
+ </DropdownMenu.Item>
295
+ </DropdownMenu.Content>
296
+ </DropdownMenu.Root>
149
297
  {/if}
150
298
  </div>
151
- <div class="flex items-center gap-2">
152
- <Button
153
- variant="ghost"
154
- size="sm"
155
- onclick={() => {
156
- handleEditItem(index);
157
- }}
158
- class="h-8 w-8 p-0"
159
- title={readonly ? 'View item' : 'Edit item'}
160
- >
161
- {#if readonly}
162
- <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
163
- <path
164
- stroke-linecap="round"
165
- stroke-linejoin="round"
166
- stroke-width="2"
167
- d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
168
- />
169
- <path
170
- stroke-linecap="round"
171
- stroke-linejoin="round"
172
- stroke-width="2"
173
- d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
174
- />
175
- </svg>
176
- {:else}
177
- <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
178
- <path
179
- stroke-linecap="round"
180
- stroke-linejoin="round"
181
- stroke-width="2"
182
- d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
183
- />
184
- </svg>
299
+ {/if}
300
+ {/each}
301
+ </div>
302
+ {/if}
303
+
304
+ <!-- Add primitive item section -->
305
+ {#if !readonly}
306
+ <Button variant="outline" class="w-full" onclick={handleAddPrimitive}>
307
+ <svg class="mr-2 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
308
+ <path
309
+ stroke-linecap="round"
310
+ stroke-linejoin="round"
311
+ stroke-width="2"
312
+ d="M12 4v16m8-8H4"
313
+ />
314
+ </svg>
315
+ Add Item
316
+ </Button>
317
+ {/if}
318
+ {:else}
319
+ <!-- Object array UI (existing code) -->
320
+ {#if arrayValue.length === 0}
321
+ <!-- Empty state -->
322
+ <div class="border-border/50 bg-muted/30 flex items-center justify-center rounded border border-dashed p-6">
323
+ <p class="text-muted-foreground text-sm">No items</p>
324
+ </div>
325
+ {:else}
326
+ <div class="space-y-2">
327
+ {#each arrayValue as item, index (index)}
328
+ <div class="border-border/50 space-y-2 rounded border p-3">
329
+ <div class="flex items-center justify-between">
330
+ <div class="flex items-center gap-2">
331
+ <span class="text-muted-foreground text-xs">#{index + 1}</span>
332
+ <h5 class="text-sm font-medium">{getItemTitle(item)}</h5>
333
+ {#if item._type}
334
+ <span class="bg-muted rounded px-2 py-1 text-xs">{item._type}</span>
185
335
  {/if}
186
- </Button>
187
-
188
- {#if !readonly}
336
+ </div>
337
+ <div class="flex items-center gap-2">
189
338
  <Button
190
339
  variant="ghost"
191
340
  size="sm"
192
- onclick={() => handleRemoveItem(index)}
193
- class="text-destructive hover:text-destructive h-8 w-8 p-0"
194
- title="Remove item"
341
+ onclick={() => {
342
+ handleEditItem(index);
343
+ }}
344
+ class="h-8 w-8 p-0"
345
+ title={readonly ? 'View item' : 'Edit item'}
195
346
  >
196
- <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
197
- <path
198
- stroke-linecap="round"
199
- stroke-linejoin="round"
200
- stroke-width="2"
201
- d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
202
- />
203
- </svg>
347
+ {#if readonly}
348
+ <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
349
+ <path
350
+ stroke-linecap="round"
351
+ stroke-linejoin="round"
352
+ stroke-width="2"
353
+ d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
354
+ />
355
+ <path
356
+ stroke-linecap="round"
357
+ stroke-linejoin="round"
358
+ stroke-width="2"
359
+ d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
360
+ />
361
+ </svg>
362
+ {:else}
363
+ <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
364
+ <path
365
+ stroke-linecap="round"
366
+ stroke-linejoin="round"
367
+ stroke-width="2"
368
+ d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
369
+ />
370
+ </svg>
371
+ {/if}
204
372
  </Button>
205
- {/if}
373
+
374
+ {#if !readonly}
375
+ <Button
376
+ variant="ghost"
377
+ size="sm"
378
+ onclick={() => handleRemoveItem(index)}
379
+ class="text-destructive hover:text-destructive h-8 w-8 p-0"
380
+ title="Remove item"
381
+ >
382
+ <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
383
+ <path
384
+ stroke-linecap="round"
385
+ stroke-linejoin="round"
386
+ stroke-width="2"
387
+ d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
388
+ />
389
+ </svg>
390
+ </Button>
391
+ {/if}
392
+ </div>
206
393
  </div>
207
- </div>
208
394
 
209
- <!-- Show a preview of the item content -->
210
- <div class="text-muted-foreground pl-6 text-xs">
211
- {#if item.title || item.heading}
212
- {item.title || item.heading}
213
- {:else if item.description}
214
- {item.description.substring(0, 100)}{item.description.length > 100 ? '...' : ''}
215
- {:else}
216
- {readonly ? 'Click view to see details' : 'Click edit to configure this item'}
217
- {/if}
395
+ <!-- Show a preview of the item content -->
396
+ <div class="text-muted-foreground pl-6 text-xs">
397
+ {#if item.title || item.heading}
398
+ {item.title || item.heading}
399
+ {:else if item.description}
400
+ {item.description.substring(0, 100)}{item.description.length > 100 ? '...' : ''}
401
+ {:else}
402
+ {readonly ? 'Click view to see details' : 'Click edit to configure this item'}
403
+ {/if}
404
+ </div>
218
405
  </div>
219
- </div>
220
- {/each}
221
- </div>
222
- {/if}
223
-
224
- <!-- Add Item section (hidden for read-only) -->
225
- {#if !readonly}
226
- <div class="border-border border-t pt-2">
227
- <DropdownMenu.Root>
228
- <DropdownMenu.Trigger>
229
- {#snippet child({ props })}
230
- <Button {...props} variant="outline" class="w-full cursor-pointer">
231
- <svg class="mr-2 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
232
- <path
233
- stroke-linecap="round"
234
- stroke-linejoin="round"
235
- stroke-width="2"
236
- d="M12 4v16m8-8H4"
237
- />
238
- </svg>
239
- Add Item
240
- </Button>
241
- {/snippet}
242
- </DropdownMenu.Trigger>
243
- <DropdownMenu.Content class="w-56">
244
- {#each availableTypes as type, index (index)}
245
- <DropdownMenu.Item onclick={() => handleTypeSelected(type.name)}>
246
- {type.title}
247
- </DropdownMenu.Item>
248
- {/each}
249
- </DropdownMenu.Content>
250
- </DropdownMenu.Root>
251
- </div>
406
+ {/each}
407
+ </div>
408
+ {/if}
409
+
410
+ <!-- Add Item section (hidden for read-only) -->
411
+ {#if !readonly}
412
+ <div class="border-border border-t pt-2">
413
+ <DropdownMenu.Root>
414
+ <DropdownMenu.Trigger>
415
+ {#snippet child({ props })}
416
+ <Button {...props} variant="outline" class="w-full cursor-pointer">
417
+ <svg class="mr-2 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
418
+ <path
419
+ stroke-linecap="round"
420
+ stroke-linejoin="round"
421
+ stroke-width="2"
422
+ d="M12 4v16m8-8H4"
423
+ />
424
+ </svg>
425
+ Add Item
426
+ </Button>
427
+ {/snippet}
428
+ </DropdownMenu.Trigger>
429
+ <DropdownMenu.Content class="w-56">
430
+ {#each availableTypes as type, index (index)}
431
+ <DropdownMenu.Item onclick={() => handleTypeSelected(type.name)}>
432
+ {type.title}
433
+ </DropdownMenu.Item>
434
+ {/each}
435
+ </DropdownMenu.Content>
436
+ </DropdownMenu.Root>
437
+ </div>
438
+ {/if}
252
439
  {/if}
253
440
  </div>
254
441
 
@@ -264,3 +451,52 @@
264
451
  {readonly}
265
452
  />
266
453
  {/if}
454
+
455
+ <!-- Image upload modal -->
456
+ {#if imageModalOpen}
457
+ <div
458
+ class="bg-background/80 fixed bottom-0 left-0 right-0 top-12 z-[100] flex items-center justify-center p-6 backdrop-blur-xs sm:absolute sm:top-0 sm:p-4"
459
+ onclick={(e) => {
460
+ if (e.target === e.currentTarget) handleImageModalClose();
461
+ }}
462
+ onkeydown={(e) => {
463
+ if (e.key === 'Escape') handleImageModalClose();
464
+ }}
465
+ role="button"
466
+ tabindex="-1"
467
+ >
468
+ <Card.Root class="flex max-h-[85vh] w-full max-w-2xl flex-col overflow-hidden shadow-lg">
469
+ <Card.Header class="border-b">
470
+ <div class="flex items-center justify-between">
471
+ <div>
472
+ <Card.Title>{field.title} - Add Image</Card.Title>
473
+ <Card.Description>Upload a new image to add to the array</Card.Description>
474
+ </div>
475
+ <Button variant="ghost" size="icon" onclick={handleImageModalClose}>
476
+ <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
477
+ <path
478
+ stroke-linecap="round"
479
+ stroke-linejoin="round"
480
+ stroke-width="2"
481
+ d="M6 18L18 6M6 6l12 12"
482
+ />
483
+ </svg>
484
+ </Button>
485
+ </div>
486
+ </Card.Header>
487
+
488
+ <Card.Content class="flex-1 overflow-auto">
489
+ <ImageField
490
+ field={{
491
+ ...field.of?.[0],
492
+ name: 'image',
493
+ type: 'image',
494
+ title: 'Image'
495
+ }}
496
+ value={imageModalValue}
497
+ onUpdate={handleImageUpload}
498
+ />
499
+ </Card.Content>
500
+ </Card.Root>
501
+ </div>
502
+ {/if}
@@ -1 +1 @@
1
- {"version":3,"file":"ArrayField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ArrayField.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAc,MAAM,wBAAwB,CAAC;AAMtF,UAAU,KAAK;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AA0NF,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"ArrayField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ArrayField.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAc,MAAM,wBAAwB,CAAC;AAOtF,UAAU,KAAK;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AA0YF,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}