@happyvertical/smrt-agents 0.33.1 → 0.34.1

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 (33) hide show
  1. package/README.md +2 -1
  2. package/dist/chunks/{config-BYbOxt24.js → config-JYiYqNE-.js} +18 -8
  3. package/dist/chunks/config-JYiYqNE-.js.map +1 -0
  4. package/dist/config.d.ts +11 -2
  5. package/dist/config.d.ts.map +1 -1
  6. package/dist/index.js +19 -9
  7. package/dist/index.js.map +1 -1
  8. package/dist/manifest.json +2 -2
  9. package/dist/schedule.d.ts +11 -2
  10. package/dist/schedule.d.ts.map +1 -1
  11. package/dist/server.js +1 -1
  12. package/dist/smrt-knowledge.json +8 -4
  13. package/dist/svelte/admin.d.ts +20 -0
  14. package/dist/svelte/admin.d.ts.map +1 -0
  15. package/dist/svelte/admin.js +4 -0
  16. package/dist/svelte/components/AgentAdminPanel.svelte +111 -0
  17. package/dist/svelte/components/AgentAdminPanel.svelte.d.ts +25 -0
  18. package/dist/svelte/components/AgentAdminPanel.svelte.d.ts.map +1 -0
  19. package/dist/svelte/components/AgentAdminTabs.svelte +277 -0
  20. package/dist/svelte/components/AgentAdminTabs.svelte.d.ts +23 -0
  21. package/dist/svelte/components/AgentAdminTabs.svelte.d.ts.map +1 -0
  22. package/dist/svelte/components/AgentSettingsShell.svelte +322 -0
  23. package/dist/svelte/components/AgentSettingsShell.svelte.d.ts +33 -0
  24. package/dist/svelte/components/AgentSettingsShell.svelte.d.ts.map +1 -0
  25. package/dist/svelte/components/__tests__/AgentSettingsShell.tablist.test.js +94 -0
  26. package/dist/svelte/i18n.d.ts +6 -0
  27. package/dist/svelte/i18n.d.ts.map +1 -1
  28. package/dist/svelte/i18n.js +9 -0
  29. package/dist/svelte/index.d.ts +13 -3
  30. package/dist/svelte/index.d.ts.map +1 -1
  31. package/dist/svelte/index.js +9 -3
  32. package/package.json +17 -9
  33. package/dist/chunks/config-BYbOxt24.js.map +0 -1
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Side-effect-free agent-admin shells.
3
+ *
4
+ * Exposes the agent-admin composites (`AgentAdminPanel` / `AgentAdminTabs` /
5
+ * `AgentSettingsShell`) and the registry types they take WITHOUT the schedule
6
+ * component `ModuleUIRegistry` auto-registration that the `./svelte` barrel runs
7
+ * on import. Consumers that only render the admin shells should import from here
8
+ * — it preserves the side-effect-free semantics of the former
9
+ * `@happyvertical/smrt-svelte/admin` subpath (#1589).
10
+ */
11
+ import type { ComponentProps } from 'svelte';
12
+ import AgentAdminPanel from './components/AgentAdminPanel.svelte';
13
+ import AgentAdminTabs from './components/AgentAdminTabs.svelte';
14
+ import AgentSettingsShell from './components/AgentSettingsShell.svelte';
15
+ export { AgentAdminPanel, AgentAdminTabs, AgentSettingsShell };
16
+ export type AgentAdminPanelProps = ComponentProps<typeof AgentAdminPanel>;
17
+ export type AgentAdminTabsProps = ComponentProps<typeof AgentAdminTabs>;
18
+ export type AgentSettingsShellProps = ComponentProps<typeof AgentSettingsShell>;
19
+ export type { AdminPanelBaseProps, AgentUIComponentRegistry, AgentUISlot, AgentUISlots, } from '../ui.js';
20
+ //# sourceMappingURL=admin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../src/svelte/admin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,eAAe,MAAM,qCAAqC,CAAC;AAClE,OAAO,cAAc,MAAM,oCAAoC,CAAC;AAChE,OAAO,kBAAkB,MAAM,wCAAwC,CAAC;AAExE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC;AAE/D,MAAM,MAAM,oBAAoB,GAAG,cAAc,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1E,MAAM,MAAM,mBAAmB,GAAG,cAAc,CAAC,OAAO,cAAc,CAAC,CAAC;AACxE,MAAM,MAAM,uBAAuB,GAAG,cAAc,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAKhF,YAAY,EACV,mBAAmB,EACnB,wBAAwB,EACxB,WAAW,EACX,YAAY,GACb,MAAM,UAAU,CAAC"}
@@ -0,0 +1,4 @@
1
+ import AgentAdminPanel from './components/AgentAdminPanel.svelte';
2
+ import AgentAdminTabs from './components/AgentAdminTabs.svelte';
3
+ import AgentSettingsShell from './components/AgentSettingsShell.svelte';
4
+ export { AgentAdminPanel, AgentAdminTabs, AgentSettingsShell };
@@ -0,0 +1,111 @@
1
+ <script lang="ts">
2
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
3
+ import type {
4
+ AdminPanelBaseProps,
5
+ AgentUIComponentRegistry,
6
+ AgentUISlot,
7
+ } from '../../ui.js';
8
+ import { M } from '../i18n.js';
9
+
10
+ const { t } = useI18n();
11
+
12
+ export interface Props {
13
+ /** The registry to look up components from */
14
+ registry: AgentUIComponentRegistry;
15
+ /** The agent class name (e.g., 'Praeco') */
16
+ agentClass: string;
17
+ /** The slot ID to render */
18
+ slotId: string;
19
+ /** The slot definition */
20
+ slot: AgentUISlot;
21
+ /** The current configuration for this slot */
22
+ config: unknown;
23
+ /** Callback when config is saved */
24
+ onSave?: (config: unknown) => Promise<void>;
25
+ /** Whether the panel is read-only */
26
+ readonly?: boolean;
27
+ /** File-based config defaults */
28
+ fileConfig?: unknown;
29
+ /** Database-persisted config overrides */
30
+ dbConfig?: unknown;
31
+ }
32
+
33
+ const {
34
+ registry,
35
+ agentClass,
36
+ slotId,
37
+ slot,
38
+ config,
39
+ onSave,
40
+ readonly = false,
41
+ fileConfig,
42
+ dbConfig,
43
+ }: Props = $props();
44
+
45
+ const Component = $derived(registry.get(agentClass, slotId));
46
+
47
+ async function handleSave(newConfig: unknown) {
48
+ await onSave?.(newConfig);
49
+ }
50
+ </script>
51
+
52
+ {#if Component}
53
+ <Component
54
+ {config}
55
+ onSave={handleSave}
56
+ {readonly}
57
+ {fileConfig}
58
+ {dbConfig}
59
+ />
60
+ {:else}
61
+ <div class="no-panel">
62
+ <div class="no-panel-icon">⚙️</div>
63
+ <p class="no-panel-message">
64
+ {t(M['ui.agent_admin_panel.no_panel_message'])} <code>{agentClass}.{slotId}</code>
65
+ </p>
66
+ <p class="no-panel-hint">
67
+ {t(M['ui.agent_admin_panel.no_panel_hint'])}
68
+ </p>
69
+ </div>
70
+ {/if}
71
+
72
+ <style>
73
+ .no-panel {
74
+ display: flex;
75
+ flex-direction: column;
76
+ align-items: center;
77
+ justify-content: center;
78
+ padding: 3rem 2rem;
79
+ text-align: center;
80
+ background: var(--smrt-color-surface-container-low, #f8fafc);
81
+ border: 1px dashed var(--smrt-color-outline-variant, #e2e8f0);
82
+ border-radius: var(--smrt-radius-medium, 8px);
83
+ min-height: 200px;
84
+ }
85
+
86
+ .no-panel-icon {
87
+ font-size: var(--smrt-typography-display-medium-size, 2.5rem);
88
+ margin-bottom: 1rem;
89
+ opacity: 0.5;
90
+ }
91
+
92
+ .no-panel-message {
93
+ margin: 0 0 0.5rem 0;
94
+ font-size: var(--smrt-typography-body-large-size, 0.9375rem);
95
+ color: var(--smrt-color-on-surface-variant, #64748b);
96
+ }
97
+
98
+ .no-panel-message code {
99
+ background: var(--smrt-color-surface-container-high, #e2e8f0);
100
+ padding: var(--smrt-spacing-1, 0.125rem) var(--smrt-spacing-2, 0.375rem);
101
+ border-radius: var(--smrt-radius-small, 4px);
102
+ font-family: var(--smrt-font-family-mono, 'SF Mono', Monaco, Consolas, monospace);
103
+ font-size: var(--smrt-typography-body-medium-size, 0.875rem);
104
+ }
105
+
106
+ .no-panel-hint {
107
+ margin: 0;
108
+ font-size: var(--smrt-typography-body-medium-size, 0.8125rem);
109
+ color: var(--smrt-color-on-surface-variant, #94a3b8);
110
+ }
111
+ </style>
@@ -0,0 +1,25 @@
1
+ import type { AgentUIComponentRegistry, AgentUISlot } from '../../ui.js';
2
+ export interface Props {
3
+ /** The registry to look up components from */
4
+ registry: AgentUIComponentRegistry;
5
+ /** The agent class name (e.g., 'Praeco') */
6
+ agentClass: string;
7
+ /** The slot ID to render */
8
+ slotId: string;
9
+ /** The slot definition */
10
+ slot: AgentUISlot;
11
+ /** The current configuration for this slot */
12
+ config: unknown;
13
+ /** Callback when config is saved */
14
+ onSave?: (config: unknown) => Promise<void>;
15
+ /** Whether the panel is read-only */
16
+ readonly?: boolean;
17
+ /** File-based config defaults */
18
+ fileConfig?: unknown;
19
+ /** Database-persisted config overrides */
20
+ dbConfig?: unknown;
21
+ }
22
+ declare const AgentAdminPanel: import("svelte").Component<Props, {}, "">;
23
+ type AgentAdminPanel = ReturnType<typeof AgentAdminPanel>;
24
+ export default AgentAdminPanel;
25
+ //# sourceMappingURL=AgentAdminPanel.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentAdminPanel.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/AgentAdminPanel.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEV,wBAAwB,EACxB,WAAW,EACZ,MAAM,aAAa,CAAC;AAIrB,MAAM,WAAW,KAAK;IACpB,8CAA8C;IAC9C,QAAQ,EAAE,wBAAwB,CAAC;IACnC,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,4BAA4B;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,0BAA0B;IAC1B,IAAI,EAAE,WAAW,CAAC;IAClB,8CAA8C;IAC9C,MAAM,EAAE,OAAO,CAAC;IAChB,oCAAoC;IACpC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iCAAiC;IACjC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA8CD,QAAA,MAAM,eAAe,2CAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
@@ -0,0 +1,277 @@
1
+ <script lang="ts">
2
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
3
+ import type { AgentUIComponentRegistry, AgentUISlots } from '../../ui.js';
4
+ import { M } from '../i18n.js';
5
+ import AgentAdminPanel from './AgentAdminPanel.svelte';
6
+
7
+ const { t } = useI18n();
8
+
9
+ export interface Props {
10
+ /** The registry to look up components from */
11
+ registry: AgentUIComponentRegistry;
12
+ /** The agent class name (e.g., 'Praeco') */
13
+ agentClass: string;
14
+ /** UI slots declared by the agent */
15
+ slots: AgentUISlots;
16
+ /** Config data for each slot (keyed by slotId) */
17
+ configs: Record<string, unknown>;
18
+ /** Callback when a slot config is saved */
19
+ onSave?: (slotId: string, config: unknown) => Promise<void>;
20
+ /** Whether all panels are read-only */
21
+ readonly?: boolean;
22
+ /** File-based configs for each slot */
23
+ fileConfigs?: Record<string, unknown>;
24
+ /** Database configs for each slot */
25
+ dbConfigs?: Record<string, unknown>;
26
+ }
27
+
28
+ const {
29
+ registry,
30
+ agentClass,
31
+ slots,
32
+ configs,
33
+ onSave,
34
+ readonly = false,
35
+ fileConfigs = {},
36
+ dbConfigs = {},
37
+ }: Props = $props();
38
+
39
+ // Sort slots by order, then by label
40
+ const sortedSlots = $derived(
41
+ Object.entries(slots).sort(([, a], [, b]) => {
42
+ const orderA = a.order ?? 99;
43
+ const orderB = b.order ?? 99;
44
+ if (orderA !== orderB) return orderA - orderB;
45
+ return a.label.localeCompare(b.label);
46
+ }),
47
+ );
48
+
49
+ let activeSlotId = $state<string | null>(null);
50
+ let tablistEl: HTMLElement | null = $state(null);
51
+
52
+ // Initialize active slot to first sorted slot
53
+ $effect(() => {
54
+ if (activeSlotId === null && sortedSlots.length > 0) {
55
+ activeSlotId = sortedSlots[0][0];
56
+ }
57
+ });
58
+
59
+ function handleSlotClick(slotId: string) {
60
+ activeSlotId = slotId;
61
+ }
62
+
63
+ /**
64
+ * Handle keyboard navigation for accessibility
65
+ */
66
+ function handleKeydown(event: KeyboardEvent, currentSlotId: string) {
67
+ const enabledSlots = sortedSlots;
68
+ const currentIndex = enabledSlots.findIndex(([id]) => id === currentSlotId);
69
+
70
+ if (currentIndex === -1) return;
71
+
72
+ let nextIndex: number | null = null;
73
+
74
+ switch (event.key) {
75
+ case 'ArrowRight':
76
+ event.preventDefault();
77
+ nextIndex = (currentIndex + 1) % enabledSlots.length;
78
+ break;
79
+ case 'ArrowLeft':
80
+ event.preventDefault();
81
+ nextIndex =
82
+ (currentIndex - 1 + enabledSlots.length) % enabledSlots.length;
83
+ break;
84
+ case 'Home':
85
+ event.preventDefault();
86
+ nextIndex = 0;
87
+ break;
88
+ case 'End':
89
+ event.preventDefault();
90
+ nextIndex = enabledSlots.length - 1;
91
+ break;
92
+ }
93
+
94
+ if (nextIndex !== null && nextIndex !== currentIndex) {
95
+ const [nextSlotId] = enabledSlots[nextIndex];
96
+ activeSlotId = nextSlotId;
97
+ // Focus the next tab after DOM update
98
+ requestAnimationFrame(() => {
99
+ const tabButton = tablistEl?.querySelector(
100
+ `[data-tab-id="${CSS.escape(nextSlotId)}"]`,
101
+ ) as HTMLElement;
102
+ tabButton?.focus();
103
+ });
104
+ }
105
+ }
106
+
107
+ async function handleSave(config: unknown) {
108
+ if (activeSlotId && onSave) {
109
+ await onSave(activeSlotId, config);
110
+ }
111
+ }
112
+ </script>
113
+
114
+ <div class="agent-admin-tabs">
115
+ <div class="tabs-nav" role="tablist" aria-label={t(M['ui.agent_admin_tabs.tablist_label'])} bind:this={tablistEl}>
116
+ {#each sortedSlots as [slotId, slot]}
117
+ <button
118
+ class="tab-button"
119
+ class:active={activeSlotId === slotId}
120
+ role="tab"
121
+ aria-selected={activeSlotId === slotId}
122
+ aria-controls="panel-{slotId}"
123
+ id="tab-{slotId}"
124
+ data-tab-id={slotId}
125
+ tabindex={activeSlotId === slotId ? 0 : -1}
126
+ onclick={() => handleSlotClick(slotId)}
127
+ onkeydown={(e) => handleKeydown(e, slotId)}
128
+ >
129
+ {#if slot.icon}
130
+ <span class="tab-icon" aria-hidden="true">{slot.icon}</span>
131
+ {/if}
132
+ <span class="tab-label">{slot.label}</span>
133
+ </button>
134
+ {/each}
135
+ </div>
136
+
137
+ <div class="tab-content">
138
+ {#if activeSlotId && slots[activeSlotId]}
139
+ {@const activeSlot = slots[activeSlotId]}
140
+ <div
141
+ id="panel-{activeSlotId}"
142
+ class="tab-panel"
143
+ role="tabpanel"
144
+ aria-labelledby="tab-{activeSlotId}"
145
+ >
146
+ {#if activeSlot.description}
147
+ <p class="slot-description">{activeSlot.description}</p>
148
+ {/if}
149
+
150
+ <AgentAdminPanel
151
+ {registry}
152
+ {agentClass}
153
+ slotId={activeSlotId}
154
+ slot={activeSlot}
155
+ config={configs[activeSlotId] ?? {}}
156
+ onSave={handleSave}
157
+ {readonly}
158
+ fileConfig={fileConfigs[activeSlotId]}
159
+ dbConfig={dbConfigs[activeSlotId]}
160
+ />
161
+ </div>
162
+ {:else}
163
+ <div class="no-slots">
164
+ <p>{t(M['ui.agent_admin_tabs.no_slots'])}</p>
165
+ </div>
166
+ {/if}
167
+ </div>
168
+ </div>
169
+
170
+ <style>
171
+ .agent-admin-tabs {
172
+ display: flex;
173
+ flex-direction: column;
174
+ gap: 1rem;
175
+ }
176
+
177
+ .tabs-nav {
178
+ display: flex;
179
+ gap: 0.25rem;
180
+ border-bottom: 1px solid var(--smrt-color-outline-variant, #e2e8f0);
181
+ padding-bottom: 0;
182
+ }
183
+
184
+ .tab-button {
185
+ display: flex;
186
+ align-items: center;
187
+ gap: 0.5rem;
188
+ padding: 0.75rem 1rem;
189
+ border: none;
190
+ background: transparent;
191
+ color: var(--smrt-color-on-surface-variant, #64748b);
192
+ font-size: var(--smrt-typography-label-large-size, 0.875rem);
193
+ font-weight: var(--smrt-typography-weight-medium, 500);
194
+ cursor: pointer;
195
+ border-bottom: 2px solid transparent;
196
+ margin-bottom: -1px;
197
+ transition:
198
+ color var(--smrt-duration-short2, 150ms) var(--smrt-easing-standard, ease),
199
+ border-color var(--smrt-duration-short2, 150ms) var(--smrt-easing-standard, ease);
200
+ }
201
+
202
+ .tab-button:hover:not(:disabled) {
203
+ color: var(--smrt-color-on-surface, #334155);
204
+ }
205
+
206
+ .tab-button.active {
207
+ color: var(--smrt-color-primary, #3b82f6);
208
+ border-bottom-color: var(--smrt-color-primary, #3b82f6);
209
+ }
210
+
211
+ .tab-button:disabled {
212
+ opacity: 0.38;
213
+ cursor: not-allowed;
214
+ }
215
+
216
+ .tab-button:focus-visible {
217
+ outline: 2px solid var(--smrt-color-primary, #3b82f6);
218
+ outline-offset: 2px;
219
+ }
220
+
221
+ .tab-icon {
222
+ font-size: var(--smrt-typography-title-medium-size, 1rem);
223
+ opacity: 0.8;
224
+ }
225
+
226
+ .tab-label {
227
+ white-space: nowrap;
228
+ }
229
+
230
+ .tab-content {
231
+ min-height: 300px;
232
+ }
233
+
234
+ .tab-panel {
235
+ animation: fadeIn var(--smrt-duration-short2, 150ms) var(--smrt-easing-standard, ease-out);
236
+ }
237
+
238
+ @keyframes fadeIn {
239
+ from {
240
+ opacity: 0;
241
+ transform: translateY(4px);
242
+ }
243
+ to {
244
+ opacity: 1;
245
+ transform: translateY(0);
246
+ }
247
+ }
248
+
249
+ @media (prefers-reduced-motion: reduce) {
250
+ .tab-panel {
251
+ animation: none;
252
+ }
253
+ }
254
+
255
+ .slot-description {
256
+ margin: 0 0 1rem 0;
257
+ padding: 0.75rem 1rem;
258
+ background: var(--smrt-color-primary-container, #f0f9ff);
259
+ border-left: 3px solid var(--smrt-color-primary, #3b82f6);
260
+ border-radius: 0 var(--smrt-radius-md, 8px) var(--smrt-radius-md, 8px) 0;
261
+ font-size: var(--smrt-typography-body-medium-size, 0.875rem);
262
+ color: var(--smrt-color-on-primary-container, #1e40af);
263
+ }
264
+
265
+ .no-slots {
266
+ display: flex;
267
+ align-items: center;
268
+ justify-content: center;
269
+ min-height: 200px;
270
+ color: var(--smrt-color-on-surface-variant, #64748b);
271
+ font-size: var(--smrt-typography-body-medium-size, 0.9375rem);
272
+ }
273
+
274
+ .no-slots p {
275
+ margin: 0;
276
+ }
277
+ </style>
@@ -0,0 +1,23 @@
1
+ import type { AgentUIComponentRegistry, AgentUISlots } from '../../ui.js';
2
+ export interface Props {
3
+ /** The registry to look up components from */
4
+ registry: AgentUIComponentRegistry;
5
+ /** The agent class name (e.g., 'Praeco') */
6
+ agentClass: string;
7
+ /** UI slots declared by the agent */
8
+ slots: AgentUISlots;
9
+ /** Config data for each slot (keyed by slotId) */
10
+ configs: Record<string, unknown>;
11
+ /** Callback when a slot config is saved */
12
+ onSave?: (slotId: string, config: unknown) => Promise<void>;
13
+ /** Whether all panels are read-only */
14
+ readonly?: boolean;
15
+ /** File-based configs for each slot */
16
+ fileConfigs?: Record<string, unknown>;
17
+ /** Database configs for each slot */
18
+ dbConfigs?: Record<string, unknown>;
19
+ }
20
+ declare const AgentAdminTabs: import("svelte").Component<Props, {}, "">;
21
+ type AgentAdminTabs = ReturnType<typeof AgentAdminTabs>;
22
+ export default AgentAdminTabs;
23
+ //# sourceMappingURL=AgentAdminTabs.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentAdminTabs.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/AgentAdminTabs.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,wBAAwB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAK1E,MAAM,WAAW,KAAK;IACpB,8CAA8C;IAC9C,QAAQ,EAAE,wBAAwB,CAAC;IACnC,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,KAAK,EAAE,YAAY,CAAC;IACpB,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,2CAA2C;IAC3C,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAkID,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}