@smartnet360/svelte-components 0.0.124 → 0.0.126

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 (53) hide show
  1. package/dist/apps/antenna-tools/components/AntennaSettingsModal.svelte +4 -174
  2. package/dist/apps/antenna-tools/components/DatabaseViewer.svelte +2 -2
  3. package/dist/apps/antenna-tools/components/MSIConverter.svelte +302 -43
  4. package/dist/apps/antenna-tools/db.js +4 -0
  5. package/dist/apps/antenna-tools/utils/db-utils.d.ts +19 -0
  6. package/dist/apps/antenna-tools/utils/db-utils.js +108 -0
  7. package/dist/core/Auth/LoginForm.svelte +397 -0
  8. package/dist/core/Auth/LoginForm.svelte.d.ts +16 -0
  9. package/dist/core/Auth/auth.svelte.d.ts +22 -0
  10. package/dist/core/Auth/auth.svelte.js +229 -0
  11. package/dist/core/Auth/config.d.ts +25 -0
  12. package/dist/core/Auth/config.js +256 -0
  13. package/dist/core/Auth/index.d.ts +4 -0
  14. package/dist/core/Auth/index.js +5 -0
  15. package/dist/core/Auth/types.d.ts +140 -0
  16. package/dist/core/Auth/types.js +2 -0
  17. package/dist/core/LandingPage/App.svelte +102 -0
  18. package/dist/core/LandingPage/App.svelte.d.ts +20 -0
  19. package/dist/core/LandingPage/LandingPage.svelte +480 -0
  20. package/dist/core/LandingPage/LandingPage.svelte.d.ts +21 -0
  21. package/dist/core/LandingPage/index.d.ts +2 -0
  22. package/dist/core/LandingPage/index.js +3 -0
  23. package/dist/core/index.d.ts +2 -0
  24. package/dist/core/index.js +4 -0
  25. package/dist/map-v3/demo/DemoMap.svelte +18 -0
  26. package/dist/map-v3/demo/demo-custom-cells.d.ts +21 -0
  27. package/dist/map-v3/demo/demo-custom-cells.js +48 -0
  28. package/dist/map-v3/features/cells/custom/components/CustomCellFilterControl.svelte +220 -0
  29. package/dist/map-v3/features/cells/custom/components/CustomCellFilterControl.svelte.d.ts +15 -0
  30. package/dist/map-v3/features/cells/custom/components/CustomCellSetManager.svelte +306 -0
  31. package/dist/map-v3/features/cells/custom/components/CustomCellSetManager.svelte.d.ts +10 -0
  32. package/dist/map-v3/features/cells/custom/components/index.d.ts +5 -0
  33. package/dist/map-v3/features/cells/custom/components/index.js +5 -0
  34. package/dist/map-v3/features/cells/custom/index.d.ts +32 -0
  35. package/dist/map-v3/features/cells/custom/index.js +35 -0
  36. package/dist/map-v3/features/cells/custom/layers/CustomCellsLayer.svelte +262 -0
  37. package/dist/map-v3/features/cells/custom/layers/CustomCellsLayer.svelte.d.ts +10 -0
  38. package/dist/map-v3/features/cells/custom/layers/index.d.ts +4 -0
  39. package/dist/map-v3/features/cells/custom/layers/index.js +4 -0
  40. package/dist/map-v3/features/cells/custom/logic/csv-parser.d.ts +20 -0
  41. package/dist/map-v3/features/cells/custom/logic/csv-parser.js +164 -0
  42. package/dist/map-v3/features/cells/custom/logic/index.d.ts +5 -0
  43. package/dist/map-v3/features/cells/custom/logic/index.js +5 -0
  44. package/dist/map-v3/features/cells/custom/logic/tree-adapter.d.ts +24 -0
  45. package/dist/map-v3/features/cells/custom/logic/tree-adapter.js +67 -0
  46. package/dist/map-v3/features/cells/custom/stores/custom-cell-sets.svelte.d.ts +78 -0
  47. package/dist/map-v3/features/cells/custom/stores/custom-cell-sets.svelte.js +242 -0
  48. package/dist/map-v3/features/cells/custom/stores/index.d.ts +4 -0
  49. package/dist/map-v3/features/cells/custom/stores/index.js +4 -0
  50. package/dist/map-v3/features/cells/custom/types.d.ts +83 -0
  51. package/dist/map-v3/features/cells/custom/types.js +23 -0
  52. package/dist/map-v3/shared/controls/MapControl.svelte +27 -3
  53. package/package.json +1 -1
@@ -0,0 +1,102 @@
1
+ <script lang="ts">
2
+ import { browser } from '$app/environment';
3
+ import { LoginForm } from '../Auth/index.js';
4
+ import { LandingPage } from '../LandingPage/index.js';
5
+ import { createAuthState } from '../Auth/auth.svelte.js';
6
+ import { getDevConfig, isDevMode, DEFAULT_CATEGORIES, DEFAULT_APPS } from '../Auth/config.js';
7
+ import type { CategoryInfo, AppDefinition, AuthConfig } from '../Auth/types.js';
8
+
9
+ interface Props {
10
+ /** Page title */
11
+ title?: string;
12
+ /** Page subtitle */
13
+ subtitle?: string;
14
+ /** Logo URL */
15
+ logo?: string;
16
+ /** Categories to display */
17
+ categories?: CategoryInfo[];
18
+ /** Apps to display */
19
+ apps?: AppDefinition[];
20
+ /** Auth configuration */
21
+ authConfig?: Partial<AuthConfig>;
22
+ /** Force dev mode (auto-login) */
23
+ forceDevMode?: boolean;
24
+ }
25
+
26
+ let {
27
+ title = 'RF Tool Components',
28
+ subtitle = 'Network Planning and Visualization Suite',
29
+ logo,
30
+ categories = DEFAULT_CATEGORIES,
31
+ apps = DEFAULT_APPS,
32
+ authConfig = {},
33
+ forceDevMode = false
34
+ }: Props = $props();
35
+
36
+ // Create auth state
37
+ const auth = createAuthState(authConfig);
38
+
39
+ // Auto-login in dev mode
40
+ const devMode = forceDevMode || isDevMode();
41
+
42
+ $effect(() => {
43
+ if (browser && devMode && !auth.isAuthenticated && auth.initialized) {
44
+ const config = getDevConfig();
45
+ auth.loginDev(config.devUser, config.devPermissions);
46
+ }
47
+ });
48
+
49
+ // Show loading state until initialized
50
+ const showLoading = $derived(!auth.initialized);
51
+
52
+ // Show login form if not authenticated and not in dev mode
53
+ const showLogin = $derived(auth.initialized && !auth.isAuthenticated && !devMode);
54
+
55
+ // Show landing page if authenticated
56
+ const showLandingPage = $derived(auth.initialized && auth.isAuthenticated);
57
+ </script>
58
+
59
+ {#if showLoading}
60
+ <!-- Loading State -->
61
+ <div class="loading-container d-flex justify-content-center align-items-center vh-100">
62
+ <div class="text-center">
63
+ <div class="spinner-border text-primary mb-3" role="status">
64
+ <span class="visually-hidden">Loading...</span>
65
+ </div>
66
+ <p class="text-muted">Loading application...</p>
67
+ </div>
68
+ </div>
69
+ {:else if showLogin}
70
+ <!-- Login Form -->
71
+ <LoginForm
72
+ {auth}
73
+ {title}
74
+ {subtitle}
75
+ {logo}
76
+ />
77
+ {:else if showLandingPage}
78
+ <!-- Landing Page -->
79
+ <LandingPage
80
+ {auth}
81
+ {title}
82
+ {subtitle}
83
+ {logo}
84
+ {categories}
85
+ {apps}
86
+ />
87
+ {/if}
88
+
89
+ <style>
90
+ .loading-container {
91
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
92
+ }
93
+
94
+ .loading-container .spinner-border {
95
+ width: 3rem;
96
+ height: 3rem;
97
+ }
98
+
99
+ .loading-container p {
100
+ color: white !important;
101
+ }
102
+ </style>
@@ -0,0 +1,20 @@
1
+ import type { CategoryInfo, AppDefinition, AuthConfig } from '../Auth/types.js';
2
+ interface Props {
3
+ /** Page title */
4
+ title?: string;
5
+ /** Page subtitle */
6
+ subtitle?: string;
7
+ /** Logo URL */
8
+ logo?: string;
9
+ /** Categories to display */
10
+ categories?: CategoryInfo[];
11
+ /** Apps to display */
12
+ apps?: AppDefinition[];
13
+ /** Auth configuration */
14
+ authConfig?: Partial<AuthConfig>;
15
+ /** Force dev mode (auto-login) */
16
+ forceDevMode?: boolean;
17
+ }
18
+ declare const App: import("svelte").Component<Props, {}, "">;
19
+ type App = ReturnType<typeof App>;
20
+ export default App;
@@ -0,0 +1,480 @@
1
+ <script lang="ts">
2
+ import type { AppDefinition, CategoryInfo } from '../Auth/types.js';
3
+ import type { AuthState } from '../Auth/auth.svelte.js';
4
+ import { base } from '$app/paths';
5
+
6
+ interface Props {
7
+ /** Auth state instance */
8
+ auth: AuthState;
9
+ /** Page title */
10
+ title?: string;
11
+ /** Page subtitle */
12
+ subtitle?: string;
13
+ /** Logo URL */
14
+ logo?: string;
15
+ /** Categories to display */
16
+ categories: CategoryInfo[];
17
+ /** Apps to display */
18
+ apps: AppDefinition[];
19
+ /** Show only accessible apps (based on permissions) */
20
+ filterByPermissions?: boolean;
21
+ }
22
+
23
+ let {
24
+ auth,
25
+ title = 'RF Tool Components',
26
+ subtitle = 'Network Planning and Visualization Suite',
27
+ logo,
28
+ categories,
29
+ apps,
30
+ filterByPermissions = true
31
+ }: Props = $props();
32
+
33
+ // Filter apps based on permissions if enabled
34
+ const accessibleApps = $derived(
35
+ filterByPermissions && auth.isAuthenticated
36
+ ? apps.filter(app => {
37
+ if (!app.enabled) return false;
38
+ if (!app.requiredPermission) return true;
39
+ return auth.hasPermission(app.requiredPermission, 'view');
40
+ })
41
+ : apps.filter(app => app.enabled)
42
+ );
43
+
44
+ // Group apps by category
45
+ const appsByCategory = $derived(
46
+ categories
47
+ .map(cat => ({
48
+ category: cat,
49
+ apps: accessibleApps.filter(app => app.category === cat.id)
50
+ }))
51
+ .filter(group => group.apps.length > 0)
52
+ );
53
+
54
+ function handleLogout() {
55
+ auth.logout();
56
+ }
57
+ </script>
58
+
59
+ <div class="landing-page">
60
+ <!-- Navbar -->
61
+ <nav class="navbar">
62
+ <div class="navbar-brand">
63
+ {#if logo}
64
+ <img src={logo} alt="Logo" class="navbar-logo" />
65
+ {:else}
66
+ <i class="bi bi-broadcast-pin"></i>
67
+ {/if}
68
+ <div class="navbar-title">
69
+ <span class="title">{title}</span>
70
+ <span class="subtitle">{subtitle}</span>
71
+ </div>
72
+ </div>
73
+
74
+ {#if auth.isAuthenticated && auth.user}
75
+ <div class="navbar-user">
76
+ <div class="user-info">
77
+ <span class="user-name">{auth.user.displayName}</span>
78
+ <span class="user-email">{auth.user.email || auth.user.username}</span>
79
+ </div>
80
+ <button class="logout-btn" onclick={handleLogout} title="Sign Out" aria-label="Sign Out">
81
+ <i class="bi bi-box-arrow-right"></i>
82
+ </button>
83
+ </div>
84
+ {/if}
85
+ </nav>
86
+
87
+ <!-- Content -->
88
+ <main class="content">
89
+ {#each appsByCategory as { category, apps: categoryApps }, index (category.id)}
90
+ <!-- Category Section -->
91
+ <section class="category-section">
92
+ <div class="category-header">
93
+ <div class="category-icon" style="--cat-color: {category.color}">
94
+ <i class="bi {category.icon}"></i>
95
+ </div>
96
+ <div class="category-info">
97
+ <h2>{category.title}</h2>
98
+ <p>{category.description}</p>
99
+ </div>
100
+ <span class="category-count">{categoryApps.length}</span>
101
+ </div>
102
+
103
+ <div class="apps-grid">
104
+ {#each categoryApps as app (app.id)}
105
+ <a href="{base}{app.route}" class="app-card" style="--cat-color: {category.color}">
106
+ <div class="app-icon">
107
+ <i class="bi {app.icon}"></i>
108
+ </div>
109
+ <div class="app-info">
110
+ <h3>
111
+ {app.title}
112
+ {#if app.badge}
113
+ <span class="app-badge" class:new={app.badge === 'New'} class:beta={app.badge === 'Beta'}>
114
+ {app.badge}
115
+ </span>
116
+ {/if}
117
+ </h3>
118
+ <p>{app.description}</p>
119
+ </div>
120
+ <i class="bi bi-chevron-right app-arrow"></i>
121
+ </a>
122
+ {/each}
123
+ </div>
124
+ </section>
125
+
126
+ {#if index < appsByCategory.length - 1}
127
+ <div class="section-divider"></div>
128
+ {/if}
129
+ {/each}
130
+
131
+ <!-- Empty State -->
132
+ {#if appsByCategory.length === 0}
133
+ <div class="empty-state">
134
+ <i class="bi bi-inbox"></i>
135
+ <h3>No Applications Available</h3>
136
+ <p>You don't have access to any applications yet.</p>
137
+ </div>
138
+ {/if}
139
+ </main>
140
+
141
+ <!-- Footer -->
142
+ <footer class="footer">
143
+ <div class="footer-left">
144
+ <i class="bi bi-shield-check"></i>
145
+ {#if auth.isAuthenticated}
146
+ Signed in as <strong>{auth.user?.username}</strong>
147
+ {:else}
148
+ Not authenticated
149
+ {/if}
150
+ </div>
151
+ <div class="footer-right">
152
+ RF Tool Components © {new Date().getFullYear()}
153
+ </div>
154
+ </footer>
155
+ </div>
156
+
157
+ <style>
158
+ .landing-page {
159
+ min-height: 100vh;
160
+ display: flex;
161
+ flex-direction: column;
162
+ background: #f1f5f9;
163
+ }
164
+
165
+ /* Navbar */
166
+ .navbar {
167
+ display: flex;
168
+ align-items: center;
169
+ justify-content: space-between;
170
+ padding: 0 1.5rem;
171
+ height: 64px;
172
+ background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
173
+ color: #fff;
174
+ flex-shrink: 0;
175
+ }
176
+
177
+ .navbar-brand {
178
+ display: flex;
179
+ align-items: center;
180
+ gap: 0.75rem;
181
+ }
182
+
183
+ .navbar-brand > i {
184
+ font-size: 1.75rem;
185
+ color: #3b82f6;
186
+ }
187
+
188
+ .navbar-logo {
189
+ height: 36px;
190
+ }
191
+
192
+ .navbar-title {
193
+ display: flex;
194
+ flex-direction: column;
195
+ }
196
+
197
+ .navbar-title .title {
198
+ font-size: 1.125rem;
199
+ font-weight: 600;
200
+ line-height: 1.2;
201
+ }
202
+
203
+ .navbar-title .subtitle {
204
+ font-size: 0.75rem;
205
+ color: #94a3b8;
206
+ }
207
+
208
+ .navbar-user {
209
+ display: flex;
210
+ align-items: center;
211
+ gap: 0.75rem;
212
+ }
213
+
214
+ .user-info {
215
+ display: flex;
216
+ flex-direction: column;
217
+ align-items: flex-end;
218
+ }
219
+
220
+ .user-name {
221
+ font-size: 0.875rem;
222
+ font-weight: 500;
223
+ }
224
+
225
+ .user-email {
226
+ font-size: 0.75rem;
227
+ color: #94a3b8;
228
+ }
229
+
230
+ .logout-btn {
231
+ width: 36px;
232
+ height: 36px;
233
+ border-radius: 8px;
234
+ border: 1px solid rgba(255, 255, 255, 0.2);
235
+ background: rgba(255, 255, 255, 0.05);
236
+ color: #fff;
237
+ display: flex;
238
+ align-items: center;
239
+ justify-content: center;
240
+ cursor: pointer;
241
+ transition: all 0.15s;
242
+ }
243
+
244
+ .logout-btn:hover {
245
+ background: rgba(239, 68, 68, 0.2);
246
+ border-color: #ef4444;
247
+ color: #ef4444;
248
+ }
249
+
250
+ /* Content */
251
+ .content {
252
+ flex: 1;
253
+ overflow-y: auto;
254
+ padding: 2rem;
255
+ }
256
+
257
+ /* Category Section */
258
+ .category-section {
259
+ max-width: 1400px;
260
+ margin: 0 auto;
261
+ }
262
+
263
+ .category-header {
264
+ display: flex;
265
+ align-items: center;
266
+ gap: 1rem;
267
+ margin-bottom: 1.25rem;
268
+ }
269
+
270
+ .category-icon {
271
+ width: 48px;
272
+ height: 48px;
273
+ border-radius: 12px;
274
+ background: color-mix(in srgb, var(--cat-color, #3b82f6) 15%, transparent);
275
+ color: var(--cat-color, #3b82f6);
276
+ display: flex;
277
+ align-items: center;
278
+ justify-content: center;
279
+ font-size: 1.25rem;
280
+ }
281
+
282
+ .category-info h2 {
283
+ font-size: 1.25rem;
284
+ font-weight: 600;
285
+ color: #0f172a;
286
+ margin: 0;
287
+ }
288
+
289
+ .category-info p {
290
+ font-size: 0.875rem;
291
+ color: #64748b;
292
+ margin: 0.125rem 0 0;
293
+ }
294
+
295
+ .category-count {
296
+ margin-left: auto;
297
+ background: #e2e8f0;
298
+ color: #475569;
299
+ padding: 0.25rem 0.625rem;
300
+ border-radius: 999px;
301
+ font-size: 0.75rem;
302
+ font-weight: 500;
303
+ }
304
+
305
+ /* Apps Grid */
306
+ .apps-grid {
307
+ display: grid;
308
+ grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
309
+ gap: 1rem;
310
+ }
311
+
312
+ .app-card {
313
+ display: flex;
314
+ align-items: center;
315
+ gap: 1rem;
316
+ padding: 1rem 1.25rem;
317
+ background: #fff;
318
+ border-radius: 12px;
319
+ border: 1px solid #e2e8f0;
320
+ text-decoration: none;
321
+ color: inherit;
322
+ transition: all 0.2s;
323
+ }
324
+
325
+ .app-card:hover {
326
+ border-color: var(--cat-color, #3b82f6);
327
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
328
+ transform: translateY(-2px);
329
+ }
330
+
331
+ .app-icon {
332
+ width: 44px;
333
+ height: 44px;
334
+ border-radius: 10px;
335
+ background: color-mix(in srgb, var(--cat-color, #3b82f6) 12%, transparent);
336
+ color: var(--cat-color, #3b82f6);
337
+ display: flex;
338
+ align-items: center;
339
+ justify-content: center;
340
+ font-size: 1.25rem;
341
+ flex-shrink: 0;
342
+ }
343
+
344
+ .app-info {
345
+ flex: 1;
346
+ min-width: 0;
347
+ }
348
+
349
+ .app-info h3 {
350
+ font-size: 0.9375rem;
351
+ font-weight: 600;
352
+ color: #0f172a;
353
+ margin: 0;
354
+ display: flex;
355
+ align-items: center;
356
+ gap: 0.5rem;
357
+ }
358
+
359
+ .app-info p {
360
+ font-size: 0.8125rem;
361
+ color: #64748b;
362
+ margin: 0.25rem 0 0;
363
+ white-space: nowrap;
364
+ overflow: hidden;
365
+ text-overflow: ellipsis;
366
+ }
367
+
368
+ .app-badge {
369
+ font-size: 0.625rem;
370
+ font-weight: 600;
371
+ text-transform: uppercase;
372
+ padding: 0.125rem 0.375rem;
373
+ border-radius: 4px;
374
+ background: #e2e8f0;
375
+ color: #475569;
376
+ }
377
+
378
+ .app-badge.new {
379
+ background: #dcfce7;
380
+ color: #16a34a;
381
+ }
382
+
383
+ .app-badge.beta {
384
+ background: #fef3c7;
385
+ color: #d97706;
386
+ }
387
+
388
+ .app-arrow {
389
+ color: #cbd5e1;
390
+ transition: color 0.15s, transform 0.15s;
391
+ }
392
+
393
+ .app-card:hover .app-arrow {
394
+ color: var(--cat-color, #3b82f6);
395
+ transform: translateX(3px);
396
+ }
397
+
398
+ /* Section Divider */
399
+ .section-divider {
400
+ max-width: 1400px;
401
+ margin: 2rem auto;
402
+ height: 1px;
403
+ background: linear-gradient(90deg, transparent, #cbd5e1, transparent);
404
+ }
405
+
406
+ /* Empty State */
407
+ .empty-state {
408
+ text-align: center;
409
+ padding: 4rem 2rem;
410
+ color: #64748b;
411
+ }
412
+
413
+ .empty-state i {
414
+ font-size: 4rem;
415
+ color: #cbd5e1;
416
+ }
417
+
418
+ .empty-state h3 {
419
+ font-size: 1.25rem;
420
+ font-weight: 600;
421
+ margin: 1rem 0 0.5rem;
422
+ color: #475569;
423
+ }
424
+
425
+ .empty-state p {
426
+ margin: 0;
427
+ }
428
+
429
+ /* Footer */
430
+ .footer {
431
+ display: flex;
432
+ align-items: center;
433
+ justify-content: space-between;
434
+ padding: 1rem 2rem;
435
+ background: #fff;
436
+ border-top: 1px solid #e2e8f0;
437
+ font-size: 0.8125rem;
438
+ color: #64748b;
439
+ flex-shrink: 0;
440
+ }
441
+
442
+ .footer-left {
443
+ display: flex;
444
+ align-items: center;
445
+ gap: 0.375rem;
446
+ }
447
+
448
+ .footer-left strong {
449
+ color: #334155;
450
+ }
451
+
452
+ /* Responsive */
453
+ @media (max-width: 640px) {
454
+ .navbar {
455
+ padding: 0 1rem;
456
+ }
457
+
458
+ .navbar-title .subtitle {
459
+ display: none;
460
+ }
461
+
462
+ .user-info {
463
+ display: none;
464
+ }
465
+
466
+ .content {
467
+ padding: 1.5rem 1rem;
468
+ }
469
+
470
+ .apps-grid {
471
+ grid-template-columns: 1fr;
472
+ }
473
+
474
+ .footer {
475
+ flex-direction: column;
476
+ gap: 0.5rem;
477
+ text-align: center;
478
+ }
479
+ }
480
+ </style>
@@ -0,0 +1,21 @@
1
+ import type { AppDefinition, CategoryInfo } from '../Auth/types.js';
2
+ import type { AuthState } from '../Auth/auth.svelte.js';
3
+ interface Props {
4
+ /** Auth state instance */
5
+ auth: AuthState;
6
+ /** Page title */
7
+ title?: string;
8
+ /** Page subtitle */
9
+ subtitle?: string;
10
+ /** Logo URL */
11
+ logo?: string;
12
+ /** Categories to display */
13
+ categories: CategoryInfo[];
14
+ /** Apps to display */
15
+ apps: AppDefinition[];
16
+ /** Show only accessible apps (based on permissions) */
17
+ filterByPermissions?: boolean;
18
+ }
19
+ declare const LandingPage: import("svelte").Component<Props, {}, "">;
20
+ type LandingPage = ReturnType<typeof LandingPage>;
21
+ export default LandingPage;
@@ -0,0 +1,2 @@
1
+ export { default as LandingPage } from './LandingPage.svelte';
2
+ export { default as App } from './App.svelte';
@@ -0,0 +1,3 @@
1
+ // LandingPage module barrel export
2
+ export { default as LandingPage } from './LandingPage.svelte';
3
+ export { default as App } from './App.svelte';
@@ -6,3 +6,5 @@ export * from './logger/index.js';
6
6
  export * from './CellTable/index.js';
7
7
  export * from './FeatureRegistry/index.js';
8
8
  export * from './Benchmark/index.js';
9
+ export * from './Auth/index.js';
10
+ export * from './LandingPage/index.js';
@@ -19,3 +19,7 @@ export * from './CellTable/index.js';
19
19
  export * from './FeatureRegistry/index.js';
20
20
  // Benchmark - Performance testing for Svelte reactivity
21
21
  export * from './Benchmark/index.js';
22
+ // Auth - Authentication and authorization system
23
+ export * from './Auth/index.js';
24
+ // LandingPage - Main application landing page with app grid
25
+ export * from './LandingPage/index.js';
@@ -20,6 +20,12 @@
20
20
  import SiteFilterControl from '../features/sites/components/SiteFilterControl.svelte';
21
21
  import { createSiteRegistry } from '../features/sites/stores/site.registry.svelte';
22
22
  import FeatureSelectionControl from '../features/selection/components/FeatureSelectionControl.svelte';
23
+ // Custom Cells Feature
24
+ import {
25
+ CustomCellsLayer,
26
+ CustomCellSetManager,
27
+ createCustomCellSetsStore
28
+ } from '../features/cells/custom';
23
29
  import { demoCells } from './demo-cells';
24
30
  import { demoRepeaters } from './demo-repeaters';
25
31
 
@@ -42,6 +48,9 @@
42
48
  const repeaterRegistry = createRepeaterRegistry('demo-map');
43
49
  const repeaterDisplay = new RepeaterDisplayStore();
44
50
 
51
+ // Custom Cells Store
52
+ const customCellSets = createCustomCellSetsStore(cellData, 'demo-map');
53
+
45
54
  onMount(() => {
46
55
  // Load dummy data
47
56
  // Need to cast or map if types slightly differ, but they should match
@@ -81,6 +90,12 @@
81
90
  displayStore={repeaterDisplay}
82
91
  />
83
92
 
93
+ <!-- Custom Cells Manager (includes filter controls for each set) -->
94
+ <CustomCellSetManager
95
+ position="top-left"
96
+ setsStore={customCellSets}
97
+ />
98
+
84
99
  <FeatureSettingsControl
85
100
  position="top-right"
86
101
  cellDisplayStore={cellDisplay}
@@ -99,6 +114,9 @@
99
114
  <RepeatersLayer dataStore={repeaterData} registry={repeaterRegistry} displayStore={repeaterDisplay} />
100
115
  <RepeaterLabelsLayer dataStore={repeaterData} registry={repeaterRegistry} displayStore={repeaterDisplay} />
101
116
 
117
+ <!-- Custom Cells Layer (renders on top of regular cells) -->
118
+ <CustomCellsLayer setsStore={customCellSets} />
119
+
102
120
  <FeatureSelectionControl
103
121
  position="bottom-left"
104
122
  cellDataStore={cellData}