@molecule/app-danger-zone-angular 0.0.0-stage → 0.0.1-stage

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/README.md CHANGED
@@ -1,3 +1,522 @@
1
- # Temporary Holding Version
1
+ <!--
2
+ AUTO-GENERATED — DO NOT EDIT THIS FILE.
3
+ Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
4
+ Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
5
+ To change this document, edit the module-level JSDoc in src/index.ts.
6
+ Generated: 2026-11-15T09:11:28.356Z
7
+ -->
2
8
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
9
+ # @molecule/app-danger-zone-angular
10
+
11
+ Angular destructive-action primitives — the counterpart of
12
+ `@molecule/app-danger-zone-react`: the same props and the same DOM for the
13
+ same input.
14
+
15
+ Exports `<DangerZoneSection>` — Card with title/description/button for
16
+ delete/reset/revoke — and `<ConfirmDialog>`, the standalone "Are you
17
+ sure?" modal.
18
+
19
+ > **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
20
+ > It is written to be read by coding agents as much as by people, and is generated from this
21
+ > package's source — edit `src/index.ts` JSDoc, not this file.
22
+
23
+ ## Quick Start
24
+
25
+ ```ts
26
+ import { Component } from '@angular/core'
27
+ import { DangerZoneSection } from '@molecule/app-danger-zone-angular'
28
+
29
+ @Component({
30
+ selector: 'app-account',
31
+ standalone: true,
32
+ imports: [DangerZoneSection],
33
+ template: `
34
+ <div
35
+ mol-danger-zone-section
36
+ title="Delete account"
37
+ description="This permanently removes your account and all data."
38
+ actionLabel="Delete account"
39
+ (click)="openConfirm()"
40
+ ></div>
41
+ `,
42
+ })
43
+ export class Account {}
44
+ ```
45
+
46
+ ## Type
47
+
48
+ `feature`
49
+
50
+ ## Installation
51
+
52
+ ```bash
53
+ npm install @molecule/app-danger-zone-angular @angular/common @angular/core @angular/platform-browser @molecule/app-angular @molecule/app-ui @molecule/app-ui-angular
54
+ ```
55
+
56
+ ## API
57
+
58
+ ### Interfaces
59
+
60
+ #### `ConfirmDialogProps`
61
+
62
+ Props accepted by {@link ConfirmDialog} — the same props
63
+ `@molecule/app-danger-zone-react` accepts. Node-valued props (`title`,
64
+ `description`) are projected content in Angular (an input cannot carry a
65
+ node), so they are typed `unknown`; `children` is the element's default
66
+ projection. Callbacks use React's names: the component's `close`/`confirm`
67
+ outputs are the same contract.
68
+
69
+ ```typescript
70
+ interface ConfirmDialogProps {
71
+ /** Whether the dialog is open. */
72
+ open: boolean
73
+ /** Called when the dialog should close (escape, backdrop, cancel). */
74
+ onClose?: (event?: unknown) => void
75
+ /** Title (nodes are `[slot=title]` content). */
76
+ title?: unknown
77
+ /** Body / warning text (nodes are `[slot=description]` content). */
78
+ description?: unknown
79
+ /** Confirm-button label (e.g. "Delete"). */
80
+ confirmLabel?: string
81
+ /** Cancel-button label. */
82
+ cancelLabel?: string
83
+ /** Called when the user confirms. */
84
+ onConfirm?: () => void | Promise<void>
85
+ /** Is the confirm action destructive? Defaults to true. */
86
+ destructive?: boolean
87
+ /** Extra body content between description and footer (default projection). */
88
+ children?: unknown
89
+ /** Disables both buttons; the caller owns the pending state. */
90
+ loading?: boolean
91
+ }
92
+ ```
93
+
94
+ #### `DangerZoneSectionProps`
95
+
96
+ Props accepted by {@link DangerZoneSection} — the same props
97
+ `@molecule/app-danger-zone-react` accepts. Node props (`title`,
98
+ `description`, `actionLabel`, `children`) are projected content in Angular
99
+ (an input cannot carry a node), so they are typed `unknown`: pass them in
100
+ the host template (`[slot=title]`, `[slot=description]`,
101
+ `[slot=actionLabel]` and default content), not as values.
102
+
103
+ ```typescript
104
+ interface DangerZoneSectionProps {
105
+ /** Section title (string/number renders as text; nodes are `[slot=title]` content). */
106
+ title?: unknown
107
+ /** Explanation of the destructive action (nodes are `[slot=description]` content). */
108
+ description?: unknown
109
+ /** Label on the action button (nodes are `[slot=actionLabel]` content). */
110
+ actionLabel?: unknown
111
+ /** Called when the button is clicked; receives the DOM click event. */
112
+ onAction?: (event: MouseEvent) => void
113
+ /** When true, the button disables and shows a loading label. */
114
+ loading?: boolean
115
+ /** When true, the button is disabled without loading. */
116
+ disabled?: boolean
117
+ /** Additional content between description and button (e.g. a confirm-text input), projected as the element's default content. */
118
+ children?: unknown
119
+ /** Extra classes on the Card (the host element). */
120
+ className?: string
121
+ /** `data-mol-id` for AI-agent selectors. */
122
+ dataMolId?: string
123
+ }
124
+ ```
125
+
126
+ ### Classes
127
+
128
+ #### `DangerZoneSection`
129
+
130
+ DangerZoneSection component.
131
+
132
+ Card-wrapped section for destructive actions — delete account, reset data,
133
+ revoke access. Uses the error/danger accent color and puts the action on
134
+ the right. The host IS the Card's `<div>`
135
+ (`div[mol-danger-zone-section]`), matching React's root exactly; the Card
136
+ classes (`cm.card({ variant: 'elevated' })` + `cm.cardPadding('md')`, the
137
+ UI Card's defaults) are applied through the host class binding.
138
+
139
+ The node props are projected slots: `title` renders inside the `<h3>`,
140
+ `description` inside the `<p>`, `actionLabel` inside the action `<button>`,
141
+ and `children` bare between the header and the button row (React renders
142
+ `{children}` with no wrapper). String inputs render as text instead — the
143
+ same values React renders. The action label's capture wrapper detaches
144
+ while `loading` is true: React shows the `…` glyph instead of the label.
145
+
146
+ ```typescript
147
+ @Component({
148
+ selector: 'div[mol-danger-zone-section]',
149
+ standalone: true,
150
+ imports: [Button],
151
+ inputs: [
152
+ 'title',
153
+ 'description',
154
+ 'actionLabel',
155
+ 'loading',
156
+ 'disabled',
157
+ 'className',
158
+ 'dataMolId',
159
+ 'onAction',
160
+ ],
161
+ host: {
162
+ '[class]': 'classes()',
163
+ '[attr.data-mol-id]': 'dataMolId ?? null',
164
+ },
165
+ template: `<div [class]="cm().stack(3)">
166
+ <header [class]="cm().stack(1)">
167
+ <h3 [class]="titleClasses()">
168
+ {{ titleText() }}<ng-content select="[slot=title]"></ng-content>
169
+ </h3>
170
+ <p [class]="cm().textSize('sm')">
171
+ {{ descriptionText() }}<ng-content select="[slot=description]"></ng-content>
172
+ </p>
173
+ </header>
174
+ <ng-content></ng-content>
175
+ <div [class]="cm().flex({ justify: 'end' })">
176
+ <button
177
+ mol-button
178
+ [variant]="'solid'"
179
+ [color]="'error'"
180
+ [disabled]="isDisabled()"
181
+ (click)="handleAction($event)"
182
+ >
183
+ {{ loading ? '…' : actionLabelText()
184
+ }}<span #labelSlot><ng-content select="[slot=actionLabel]"></ng-content></span>
185
+ </button>
186
+ </div>
187
+ </div>`,
188
+ })
189
+ export class DangerZoneSection implements AfterViewInit, DoCheck {
190
+ /** Section title (string/number renders as text; nodes are `[slot=title]` content). */
191
+ title?: unknown
192
+ /** Explanation of the destructive action (string/number as text; nodes are `[slot=description]` content). */
193
+ description?: unknown
194
+ /** Label on the action button (string/number as text; nodes are `[slot=actionLabel]` content). */
195
+ actionLabel?: unknown
196
+ /** When true, the button disables and shows a loading label. */
197
+ loading?: boolean
198
+ /** When true, the button is disabled without loading. */
199
+ disabled?: boolean
200
+ /** Extra classes on the Card. */
201
+ className?: string
202
+ /** `data-mol-id` for AI-agent selectors. */
203
+ dataMolId?: string
204
+ /** Called when the button is clicked; receives the DOM click event (React's `onAction`). */
205
+ onAction?: (event: MouseEvent) => void
206
+
207
+ /** The action label's capture wrapper (moved out of the DOM once read). */
208
+ @ViewChild('labelSlot') labelSlot?: ElementRef<HTMLSpanElement>
209
+
210
+ /** Whether the action label slot holds projected content (read once). */
211
+ private hasLabelSlot = false
212
+
213
+ /** The capture wrapper's parent (the action `<button>`), remembered across detaches. */
214
+ private labelParent?: Node
215
+
216
+ /** The projected label nodes while they render bare (React's `{actionLabel}`). */
217
+ private labelNodes: Node[] = []
218
+
219
+ /** Marks the wrapper's position in the button while the projected nodes render bare. */
220
+ private labelAnchor?: Comment
221
+
222
+ /** The wrapper sync for the action label's capture wrapper. */
223
+ private readonly sync = new SlotWrapper(inject(ElementRef), inject(Renderer2))
224
+
225
+ /** Creates the position anchor comment the DOM normalizer strips. */
226
+ private readonly renderer = inject(Renderer2)
227
+
228
+ /**
229
+ * The current class map.
230
+ * @returns The UIClassMap.
231
+ */
232
+ cm(): UIClassMap {
233
+ return getClassMap()
234
+ }
235
+
236
+ /**
237
+ * Computes the Card's class list (`cm.card({ variant: 'elevated' })` +
238
+ * `cm.cardPadding('md')` — the UI Card's defaults — plus `className`).
239
+ * @returns The class string.
240
+ */
241
+ classes(): string {
242
+ return this.cm().cn(
243
+ this.cm().card({ variant: 'elevated' }),
244
+ this.cm().cardPadding('md'),
245
+ this.className,
246
+ )
247
+ }
248
+
249
+ /**
250
+ * Computes the title's class (`cm.textSize('base')` + `cm.fontWeight('semibold')`).
251
+ * @returns The class string.
252
+ */
253
+ titleClasses(): string {
254
+ return this.cm().cn(this.cm().textSize('base'), this.cm().fontWeight('semibold'))
255
+ }
256
+
257
+ /**
258
+ * The title input's text (React's `{title}` for a text child: strings and
259
+ * numbers render; a node arrives as `[slot=title]` content instead).
260
+ * @returns The text, or `''` for a node title.
261
+ */
262
+ titleText(): string {
263
+ return typeof this.title === 'string' || typeof this.title === 'number'
264
+ ? String(this.title)
265
+ : ''
266
+ }
267
+
268
+ /**
269
+ * The description input's text (React's `{description}` for a text child).
270
+ * @returns The text, or `''` for a node description.
271
+ */
272
+ descriptionText(): string {
273
+ return typeof this.description === 'string' || typeof this.description === 'number'
274
+ ? String(this.description)
275
+ : ''
276
+ }
277
+
278
+ /**
279
+ * The action label input's text (React's `{actionLabel}` for a text child).
280
+ * @returns The text, or `''` for a node label.
281
+ */
282
+ actionLabelText(): string {
283
+ return typeof this.actionLabel === 'string' || typeof this.actionLabel === 'number'
284
+ ? String(this.actionLabel)
285
+ : ''
286
+ }
287
+
288
+ /**
289
+ * The button's disabled state (React's `disabled || loading`; an absent
290
+ * boolean input must not show a disabled button).
291
+ * @returns Whether the button is disabled.
292
+ */
293
+ isDisabled(): boolean {
294
+ return this.disabled === true || this.loading === true
295
+ }
296
+
297
+ /** Runs the click handler the way React forwards the DOM event. */
298
+ handleAction(event: MouseEvent): void {
299
+ this.onAction?.(event)
300
+ }
301
+
302
+ /**
303
+ * Reads the label slot's presence (view queries resolve only at view init)
304
+ * and syncs the capture wrapper against React's DOM.
305
+ */
306
+ ngAfterViewInit(): void {
307
+ const label = this.labelSlot?.nativeElement
308
+ if (label) {
309
+ this.labelParent = label.parentElement ?? undefined
310
+ this.hasLabelSlot = hasNodes(label)
311
+ }
312
+ this.syncLabels()
313
+ }
314
+
315
+ /**
316
+ * Re-syncs the wrapper when `loading` changes.
317
+ */
318
+ ngDoCheck(): void {
319
+ this.syncLabels()
320
+ }
321
+
322
+ /**
323
+ * Applies React's label branch: the projected label renders only while
324
+ * there is one and the button is not loading (`{loading ? '…' : actionLabel}`),
325
+ * and it renders BARE — the capture wrapper never shows in the DOM.
326
+ */
327
+ private syncLabels(): void {
328
+ const span = this.labelSlot?.nativeElement
329
+ const parent = this.labelParent
330
+ if (!span || !parent) return
331
+ if (this.hasLabelSlot && this.loading !== true) {
332
+ this.unwrapLabel(span, parent)
333
+ } else {
334
+ this.wrapLabel(span, parent)
335
+ }
336
+ }
337
+
338
+ /**
339
+ * Moves the projected nodes out of the capture wrapper to its position and
340
+ * detaches the empty wrapper (React renders the label with no element).
341
+ *
342
+ * @param span - The capture wrapper.
343
+ * @param parent - The wrapper's parent (the action `<button>`).
344
+ */
345
+ private unwrapLabel(span: HTMLSpanElement, parent: Node): void {
346
+ const anchor = this.labelAnchor ?? this.createLabelAnchor(parent, span)
347
+ this.labelNodes = Array.from(span.childNodes)
348
+ for (const node of this.labelNodes) parent.insertBefore(node, anchor)
349
+ if (span.isConnected) parent.removeChild(span)
350
+ }
351
+
352
+ /**
353
+ * Creates the position anchor comment and inserts it where the wrapper sits.
354
+ *
355
+ * @param parent - The wrapper's parent (the action `<button>`).
356
+ * @param span - The capture wrapper the anchor replaces.
357
+ * @returns The created anchor comment.
358
+ */
359
+ private createLabelAnchor(parent: Node, span: Node): Comment {
360
+ const anchor: Comment = this.renderer.createComment('slot')
361
+ this.labelAnchor = anchor
362
+ parent.insertBefore(anchor, span)
363
+ return anchor
364
+ }
365
+
366
+ /**
367
+ * Puts the projected nodes back inside the capture wrapper (hidden, as
368
+ * React hides the label while loading) and keeps the wrapper out of the DOM.
369
+ *
370
+ * @param span - The capture wrapper.
371
+ * @param parent - The wrapper's parent (the action `<button>`).
372
+ */
373
+ private wrapLabel(span: HTMLSpanElement, parent: Node): void {
374
+ for (const node of this.labelNodes) span.appendChild(node)
375
+ this.labelNodes = []
376
+ if (!span.isConnected && this.labelAnchor?.isConnected) {
377
+ parent.insertBefore(span, this.labelAnchor)
378
+ }
379
+ this.sync.sync(span, false, parent)
380
+ }
381
+ }
382
+ ```
383
+
384
+ #### `ConfirmDialog`
385
+
386
+ ConfirmDialog component.
387
+
388
+ Composed from `Modal` and `Button` exactly as React composes them; the
389
+ dialog's own title/description/children are projected content (`[slot=title]`,
390
+ `[slot=description]`, default). The host is unwrapped (`data-parity-unwrap`,
391
+ React's `Modal` root is a fragment/portal).
392
+
393
+ ```typescript
394
+ @Component({
395
+ selector: 'mol-confirm-dialog',
396
+ standalone: true,
397
+ imports: [Button, Modal],
398
+ inputs: ['open', 'title', 'description', 'confirmLabel', 'cancelLabel', 'destructive', 'loading'],
399
+ outputs: ['close', 'confirm'],
400
+ host: {
401
+ 'data-parity-unwrap': '',
402
+ style: 'display: contents',
403
+ },
404
+ template: `<mol-modal [open]="open" (close)="close.emit($event)"
405
+ ><div [class]="cm().stack(3)">
406
+ <h2 [class]="cm().cn(cm().textSize('lg'), cm().fontWeight('bold'))">
407
+ <ng-content select="[slot=title]"></ng-content>
408
+ </h2>
409
+ <p [class]="cm().textSize('sm')"><ng-content select="[slot=description]"></ng-content></p>
410
+ <ng-content></ng-content>
411
+ <div [class]="cm().flex({ justify: 'end', gap: 'sm' })">
412
+ <button mol-button [variant]="'ghost'" [disabled]="!!loading" (click)="close.emit($event)">
413
+ {{ cancelText() }}</button
414
+ ><button
415
+ mol-button
416
+ [variant]="'solid'"
417
+ [color]="destructive === false ? 'primary' : 'error'"
418
+ [disabled]="!!loading"
419
+ (click)="confirm.emit()"
420
+ >
421
+ {{ confirmText() }}
422
+ </button>
423
+ </div>
424
+ </div></mol-modal
425
+ >`,
426
+ })
427
+ export class ConfirmDialog {
428
+ /** Whether the dialog is open. */
429
+ open = false
430
+ /** Title (node-valued prop). */
431
+ title?: unknown
432
+ /** Body / warning text (node-valued prop). */
433
+ description?: unknown
434
+ /** Confirm-button label. */
435
+ confirmLabel?: string
436
+ /** Cancel-button label. */
437
+ cancelLabel?: string
438
+ /** Whether the confirm action is destructive (default true). */
439
+ destructive = true
440
+ /** Disables both buttons (the caller owns the pending state). */
441
+ loading?: boolean
442
+
443
+ /** Fires when the dialog should close (escape, backdrop, cancel). */
444
+ @Output() readonly close = new EventEmitter<unknown>()
445
+ /** Fires when the user confirms. */
446
+ @Output() readonly confirm = new EventEmitter<void>()
447
+
448
+ /**
449
+ * Reactive translation, read in the injection context. Its `t` reads the
450
+ * locale, so the view re-renders the text on a locale switch (the static `t`
451
+ * does not).
452
+ */
453
+ private readonly i18n = useTranslation()
454
+
455
+ /**
456
+ * The current class map.
457
+ * @returns The UIClassMap.
458
+ */
459
+ cm(): UIClassMap {
460
+ return getClassMap()
461
+ }
462
+
463
+ /**
464
+ * The cancel button's label.
465
+ * @returns The label text.
466
+ */
467
+ cancelText(): string {
468
+ return this.cancelLabel ?? this.i18n.t('confirm.cancel', {}, { defaultValue: 'Cancel' })
469
+ }
470
+
471
+ /**
472
+ * The confirm button's label (React shows an ellipsis while loading).
473
+ * @returns The label text.
474
+ */
475
+ confirmText(): string {
476
+ return this.loading
477
+ ? '…'
478
+ : (this.confirmLabel ?? this.i18n.t('confirm.confirm', {}, { defaultValue: 'Confirm' }))
479
+ }
480
+ }
481
+ ```
482
+
483
+ ## Injection Notes
484
+
485
+ ### Requirements
486
+
487
+ Peer dependencies:
488
+
489
+ - `@angular/common` ^22.0.0
490
+ - `@angular/core` ^22.0.0
491
+ - `@angular/platform-browser` ^22.0.0
492
+ - `@molecule/app-angular` ^3.5.0
493
+ - `@molecule/app-ui` ^1.0.1
494
+ - `@molecule/app-ui-angular` ^1.1.0
495
+
496
+ ### Runtime Dependencies
497
+
498
+ - `@angular/common`
499
+ - `@angular/core`
500
+ - `@angular/platform-browser`
501
+ - `@molecule/app-angular`
502
+ - `@molecule/app-ui`
503
+ - `@molecule/app-ui-angular`
504
+
505
+ - Requires a bonded ClassMap (the Card classes, `cm.card` +
506
+ `cm.cardPadding('md')`) — rendering throws otherwise.
507
+ - Node props are projected content: `title`, `description` and
508
+ `actionLabel` are selected by `[slot=title]` / `[slot=description]` /
509
+ `[slot=actionLabel]`, everything else lands bare between the header and
510
+ the button row as the default projection. String inputs render as text.
511
+ - `<ConfirmDialog>` does NOT track `onConfirm`'s promise and does NOT
512
+ close itself on confirm: set `loading` yourself while the action runs,
513
+ then call `onClose()` when it settles — otherwise users can double-fire
514
+ the destructive action or be left staring at an open dialog.
515
+ Cancel/Confirm fall back to the `confirm.*` i18n keys (companion bond:
516
+ `@molecule/app-locales-danger-zone`); pass `confirmLabel`/`cancelLabel`
517
+ for action-specific wording ("Delete", "Revoke"). `destructive`
518
+ (default true) only switches the confirm button color.
519
+
520
+ ## Translations
521
+
522
+ Translation strings are provided by `@molecule/app-locales-danger-zone`.
@@ -0,0 +1,23 @@
1
+ /**
2
+ * ConfirmDialog — re-exported from the framework bundle.
3
+ *
4
+ * The Angular port lives in `@molecule/app-ui-angular` (like React's, which
5
+ * lives in `@molecule/app-ui-react` after its 2026-09-24 promotion), so
6
+ * every Angular app can confirm destructive actions without installing this
7
+ * feature package. This re-export keeps
8
+ * `@molecule/app-danger-zone-angular`'s public API aligned with
9
+ * `@molecule/app-danger-zone-react`; prefer importing it from
10
+ * `@molecule/app-ui-angular` directly.
11
+ *
12
+ * Contract (unchanged): the dialog does NOT track `onConfirm`'s promise and
13
+ * does NOT close itself on confirm — set `loading` yourself while the action
14
+ * runs, then call `onClose()` when it settles. Cancel/Confirm fall back to
15
+ * the `confirm.*` i18n keys (companion bond:
16
+ * `@molecule/app-locales-danger-zone`); pass `confirmLabel`/`cancelLabel`
17
+ * for action-specific wording. `destructive` (default true) only switches
18
+ * the confirm button color.
19
+ *
20
+ * @module
21
+ */
22
+ export { ConfirmDialog } from '@molecule/app-ui-angular';
23
+ //# sourceMappingURL=confirm-dialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"confirm-dialog.d.ts","sourceRoot":"","sources":["../src/confirm-dialog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * ConfirmDialog — re-exported from the framework bundle.
3
+ *
4
+ * The Angular port lives in `@molecule/app-ui-angular` (like React's, which
5
+ * lives in `@molecule/app-ui-react` after its 2026-09-24 promotion), so
6
+ * every Angular app can confirm destructive actions without installing this
7
+ * feature package. This re-export keeps
8
+ * `@molecule/app-danger-zone-angular`'s public API aligned with
9
+ * `@molecule/app-danger-zone-react`; prefer importing it from
10
+ * `@molecule/app-ui-angular` directly.
11
+ *
12
+ * Contract (unchanged): the dialog does NOT track `onConfirm`'s promise and
13
+ * does NOT close itself on confirm — set `loading` yourself while the action
14
+ * runs, then call `onClose()` when it settles. Cancel/Confirm fall back to
15
+ * the `confirm.*` i18n keys (companion bond:
16
+ * `@molecule/app-locales-danger-zone`); pass `confirmLabel`/`cancelLabel`
17
+ * for action-specific wording. `destructive` (default true) only switches
18
+ * the confirm button color.
19
+ *
20
+ * @module
21
+ */
22
+ export { ConfirmDialog } from '@molecule/app-ui-angular';
23
+ //# sourceMappingURL=confirm-dialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"confirm-dialog.js","sourceRoot":"","sources":["../src/confirm-dialog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA"}