osi-cards-lib 1.5.35 → 1.5.37

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
@@ -41,7 +41,7 @@ npm install osi-cards-lib
41
41
  ### Peer Dependencies
42
42
 
43
43
  ```bash
44
- npm install @angular/common@^20.0.0 @angular/core@^20.0.0 @angular/animations@^20.0.0 @angular/platform-browser@^20.0.0 lucide-angular@^0.548.0 rxjs@~7.8.0
44
+ npm install @angular/common@^17.0.0 @angular/core@^17.0.0 @angular/animations@^17.0.0 @angular/platform-browser@^17.0.0 lucide-angular@^0.292.0 rxjs@~7.8.0
45
45
  ```
46
46
 
47
47
  ### Optional Dependencies (for charts and maps)
@@ -52,7 +52,9 @@ npm install chart.js leaflet
52
52
 
53
53
  ---
54
54
 
55
- ## Quick Start
55
+ ## Integration Guide
56
+
57
+ This guide shows you exactly how to integrate OSI Cards into your Angular application, following the proven pattern used in production applications.
56
58
 
57
59
  ### Step 1: Configure Providers (Required)
58
60
 
@@ -60,70 +62,19 @@ In your `app.config.ts`:
60
62
 
61
63
  ```typescript
62
64
  import { ApplicationConfig } from '@angular/core';
63
- import { provideOSICards } from 'osi-cards-lib';
65
+ import { provideOsiCards } from 'osi-cards-lib';
64
66
 
65
67
  export const appConfig: ApplicationConfig = {
66
68
  providers: [
67
- provideOSICards(), // Required for animations and library functionality
69
+ provideOsiCards(), // Required for animations and library functionality
68
70
  // ... your other providers
69
71
  ]
70
72
  };
71
73
  ```
72
74
 
73
- ### Step 2: Import Styles
74
-
75
- **🎯 RECOMMENDED: Automatic Setup via angular.json**
76
-
77
- The most reliable way to include library styles is to add them directly to `angular.json`. This ensures styles are always loaded correctly, regardless of SASS/SCSS import resolution issues.
78
-
79
- ### Quick Setup Script
80
-
81
- We provide an automated setup script that configures `angular.json` for you:
82
-
83
- ```bash
84
- npx osi-cards-lib setup:styles
85
- ```
86
-
87
- Or manually run the script from the library:
88
-
89
- ```bash
90
- node node_modules/osi-cards-lib/scripts/setup-angular-styles.js
91
- ```
92
-
93
- This script will:
94
- - ✅ Add library styles to your `angular.json` styles array
95
- - ✅ Configure `stylePreprocessorOptions` with correct `includePaths`
96
- - ✅ Set up SASS deprecation silence
97
- - ✅ Work with all Angular projects in your workspace
75
+ ### Step 2: Add Styles to angular.json
98
76
 
99
- ### Manual Setup (Alternative)
100
-
101
- If you prefer to configure manually, choose one of the following options:
102
-
103
- #### Option A: Scoped Styles (Recommended for Integration)
104
-
105
- Use this when integrating into an existing application to prevent style conflicts. Styles are scoped to `.osi-cards-container`.
106
-
107
- **Method 1: Import in your styles file (Recommended)**
108
-
109
- In your `src/styles.scss` or `src/styles.sass`:
110
-
111
- ```scss
112
- // Import at the TOP of your styles file (before other styles)
113
- @import 'osi-cards-lib/styles/_styles-scoped';
114
-
115
- // If that doesn't work, try with tilde prefix:
116
- @import '~osi-cards-lib/styles/_styles-scoped';
117
-
118
- // Or with explicit .scss extension:
119
- @import 'osi-cards-lib/styles/_styles-scoped.scss';
120
- ```
121
-
122
- **Important**: Place the import at the **top** of your styles file, not at the bottom, to ensure proper CSS cascade.
123
-
124
- **Method 2: Add to angular.json (RECOMMENDED - Most Reliable)**
125
-
126
- This is the **most reliable method**, especially for SASS files. The styles are automatically included in every build:
77
+ Add the library styles directly to your `angular.json` file. This is the most reliable method and ensures styles are always loaded correctly:
127
78
 
128
79
  ```json
129
80
  {
@@ -152,746 +103,240 @@ This is the **most reliable method**, especially for SASS files. The styles are
152
103
  }
153
104
  ```
154
105
 
155
- **Note**:
156
- - The `includePaths` in `stylePreprocessorOptions` helps Angular resolve relative imports within the library's SCSS files.
157
- - **This is especially important when using SASS files** (`.sass` extension) as they may have different import resolution behavior.
158
- - Place library styles **after** your main styles file in the array to ensure proper cascade.
159
-
160
- **Important**:
161
- - ⚠️ **Case sensitive**: Use `osi-cards-lib` (lowercase), NOT `osi-cards-Lib`
162
- - ⚠️ **REQUIRED**: You must wrap your components in a container. **RECOMMENDED: Use `<osi-cards-container>` component** (automatically handles theme and tilt):
163
-
164
- ```html
165
- <!-- ✅ RECOMMENDED: Use osi-cards-container component -->
166
- <osi-cards-container [theme]="'day'">
167
- <app-ai-card-renderer [cardConfig]="card"></app-ai-card-renderer>
168
- </osi-cards-container>
169
-
170
- <!-- ✅ Also works with dynamic theme -->
171
- <osi-cards-container [theme]="cardTheme" *ngIf="cardConfig">
172
- <app-ai-card-renderer [cardConfig]="cardConfig"></app-ai-card-renderer>
173
- </osi-cards-container>
174
- ```
175
-
176
- **Why use the component?**
177
- - ✅ Automatically sets `data-theme` attribute correctly
178
- - ✅ Automatically adds `perspective: 1200px` for 3D tilt effects
179
- - ✅ Preserves 3D transform context (`transform-style: preserve-3d`)
180
- - ✅ Handles all container styling automatically
181
- - ✅ More reliable than manual div setup
182
-
183
- **Alternative (Manual Setup):**
184
- If you prefer a plain div, you must manually set both the class and `data-theme` attribute:
185
-
186
- ```html
187
- <!-- ⚠️ Manual setup - requires both class and data-theme -->
188
- <div class="osi-cards-container" data-theme="day">
189
- <app-ai-card-renderer [cardConfig]="card"></app-ai-card-renderer>
190
- </div>
191
-
192
- <!-- Dynamic theme with manual setup -->
193
- <div class="osi-cards-container" [attr.data-theme]="theme" *ngIf="cardConfig">
194
- <app-ai-card-renderer [cardConfig]="cardConfig"></app-ai-card-renderer>
195
- </div>
196
- ```
197
-
198
- **The `data-theme` attribute is REQUIRED** - without it, styles will not apply correctly. Use `"day"` for light theme or `"night"` for dark theme. The component handles this automatically.
199
-
200
- #### Option B: Global Styles (For Standalone Apps)
201
-
202
- Use this for standalone applications or when you want styles applied globally.
203
-
204
- In your `src/styles.scss`:
205
-
206
- ```scss
207
- // Try this first
208
- @import 'osi-cards-lib/styles/_styles';
209
-
210
- // If that doesn't work, try with tilde:
211
- @import '~osi-cards-lib/styles/_styles';
212
- ```
213
-
214
- **Note**: This applies styles to `:root`, so no container wrapper is needed, but styles may conflict with your existing application styles.
215
-
216
- ### Step 3: Use the Component
217
-
218
- ---
219
-
220
- ## Integration: Streaming vs Static
106
+ **Important Notes:**
107
+ - Place library styles **after** your main styles file in the array
108
+ - The `includePaths` helps Angular resolve relative imports within the library's SCSS files
109
+ - The `silenceDeprecations` setting suppresses SASS `@import` warnings
221
110
 
222
- ### Option A: Static Card (No Streaming)
111
+ ### Step 3: Import Components in Your Module
223
112
 
224
- Use this when you already have the card data and don't need loading animations.
225
-
226
- **TypeScript:**
113
+ In the module where you want to use the card components, import them from `osi-cards-lib`:
227
114
 
228
115
  ```typescript
229
- import { Component } from '@angular/core';
230
- import { OsiCardsContainerComponent, AICardRendererComponent, AICardConfig } from 'osi-cards-lib';
231
-
232
- @Component({
233
- selector: 'app-static-card',
234
- standalone: true,
235
- imports: [OsiCardsContainerComponent, AICardRendererComponent],
236
- templateUrl: './static-card.component.html'
237
- })
238
- export class StaticCardComponent {
239
- cardTheme: 'day' | 'night' = 'day';
240
-
241
- card: AICardConfig = {
242
- cardTitle: 'Company Profile',
243
- sections: [
244
- {
245
- title: 'Overview',
246
- type: 'info',
247
- fields: [
248
- { label: 'Industry', value: 'Technology' },
249
- { label: 'Employees', value: '500+' }
250
- ]
251
- }
252
- ]
253
- };
254
-
255
- onCardAction(event: { action: string; card: AICardConfig }): void {
256
- console.log('Action:', event);
257
- }
258
- }
259
- ```
260
-
261
- **HTML (RECOMMENDED - using OsiCardsContainerComponent):**
262
-
263
- ```html
264
- <osi-cards-container [theme]="cardTheme">
265
- <app-ai-card-renderer
266
- [cardConfig]="card"
267
- [streamingStage]="'complete'"
268
- [showLoadingByDefault]="false"
269
- [tiltEnabled]="true"
270
- (cardInteraction)="onCardAction($event)">
271
- </app-ai-card-renderer>
272
- </osi-cards-container>
273
- ```
274
-
275
- **HTML (Alternative - manual div setup):**
276
-
277
- ```html
278
- <div class="osi-cards-container" [attr.data-theme]="cardTheme">
279
- <app-ai-card-renderer
280
- [cardConfig]="card"
281
- [streamingStage]="'complete'"
282
- [showLoadingByDefault]="false"
283
- [tiltEnabled]="true"
284
- (cardInteraction)="onCardAction($event)">
285
- </app-ai-card-renderer>
286
- </div>
287
- ```
288
-
289
- **Note**: The component approach is recommended because it automatically handles theme, perspective for tilt, and 3D transform context.
290
-
291
- **Key Settings:**
292
- - `[streamingStage]="'complete'"` → Card is fully loaded
293
- - `[showLoadingByDefault]="false"` → No loading spinner
294
-
295
- ---
296
-
297
- ### Option B: With Streaming (AI/LLM Integration)
298
-
299
- Use this when generating cards progressively from an AI service.
300
-
301
- **TypeScript:**
302
-
303
- ```typescript
304
- import { Component } from '@angular/core';
305
- import { OsiCardsContainerComponent, AICardRendererComponent, AICardConfig, StreamingStage } from 'osi-cards-lib';
306
-
307
- @Component({
308
- selector: 'app-streaming-card',
309
- standalone: true,
310
- imports: [OsiCardsContainerComponent, AICardRendererComponent],
311
- templateUrl: './streaming-card.component.html'
116
+ import { NgModule } from '@angular/core';
117
+ import { CommonModule } from '@angular/common';
118
+ import { AICardRendererComponent, OsiCardsContainerComponent } from 'osi-cards-lib';
119
+
120
+ @NgModule({
121
+ declarations: [
122
+ // ... your components
123
+ ],
124
+ imports: [
125
+ CommonModule,
126
+ AICardRendererComponent,
127
+ OsiCardsContainerComponent,
128
+ // ... your other imports
129
+ ],
130
+ exports: [
131
+ // ... your exports
132
+ ]
312
133
  })
313
- export class StreamingCardComponent {
314
- cardTheme: 'day' | 'night' = 'day';
315
-
316
- // Streaming state
317
- card: AICardConfig | undefined;
318
- isStreaming = false;
319
- streamingStage: StreamingStage = 'idle';
320
- streamingProgress = 0;
321
-
322
- // Custom loading messages (optional)
323
- loadingMessages = [
324
- 'Analyzing data...',
325
- 'Processing request...',
326
- 'Generating insights...'
327
- ];
328
-
329
- async generateCard(): Promise<void> {
330
- // Start streaming
331
- this.card = undefined;
332
- this.isStreaming = true;
333
- this.streamingStage = 'thinking';
334
- this.streamingProgress = 0;
335
-
336
- // Simulate AI thinking phase
337
- await this.delay(2000);
338
-
339
- // Start streaming content
340
- this.streamingStage = 'streaming';
341
- this.streamingProgress = 0.2;
342
-
343
- // Progressive card building (simulate chunks from AI)
344
- this.card = { cardTitle: 'Analysis Results', sections: [] };
345
-
346
- await this.delay(800);
347
- this.streamingProgress = 0.5;
348
- this.card.sections.push({
349
- title: 'Summary',
350
- type: 'info',
351
- fields: [{ label: 'Status', value: 'Processing...' }]
352
- });
353
-
354
- await this.delay(800);
355
- this.streamingProgress = 0.8;
356
- this.card.sections.push({
357
- title: 'Metrics',
358
- type: 'analytics',
359
- fields: [{ label: 'Score', value: '92%', trend: 'up' }]
360
- });
361
-
362
- await this.delay(500);
363
-
364
- // Complete
365
- this.streamingProgress = 1;
366
- this.streamingStage = 'complete';
367
- this.isStreaming = false;
368
-
369
- // Final card state
370
- this.card.sections[0].fields = [{ label: 'Status', value: 'Complete' }];
371
- }
372
-
373
- private delay(ms: number): Promise<void> {
374
- return new Promise(resolve => setTimeout(resolve, ms));
375
- }
376
-
377
- onCardAction(event: { action: string; card: AICardConfig }): void {
378
- console.log('Action:', event);
379
- }
380
- }
134
+ export class YourModule { }
381
135
  ```
382
136
 
383
- **HTML (RECOMMENDED - using OsiCardsContainerComponent):**
137
+ ### Step 4: Use the Components in Your Template
384
138
 
385
- ```html
386
- <button (click)="generateCard()" [disabled]="isStreaming">
387
- {{ isStreaming ? 'Generating...' : 'Generate Card' }}
388
- </button>
389
-
390
- <osi-cards-container [theme]="cardTheme">
391
- <app-ai-card-renderer
392
- [cardConfig]="card"
393
- [isStreaming]="isStreaming"
394
- [streamingStage]="streamingStage"
395
- [streamingProgress]="streamingProgress"
396
- [showLoadingByDefault]="true"
397
- [loadingMessages]="loadingMessages"
398
- [loadingTitle]="'AI Analysis'"
399
- [tiltEnabled]="true"
400
- (cardInteraction)="onCardAction($event)">
401
- </app-ai-card-renderer>
402
- </osi-cards-container>
403
- ```
404
-
405
- **HTML (Alternative - manual div setup):**
139
+ Use the `<osi-cards-container>` component to wrap `<app-ai-card-renderer>`. The container component automatically handles theme and tilt effects:
406
140
 
407
141
  ```html
408
- <button (click)="generateCard()" [disabled]="isStreaming">
409
- {{ isStreaming ? 'Generating...' : 'Generate Card' }}
410
- </button>
411
-
412
- <div class="osi-cards-container" [attr.data-theme]="cardTheme">
413
- <app-ai-card-renderer
414
- [cardConfig]="card"
415
- [isStreaming]="isStreaming"
416
- [streamingStage]="streamingStage"
417
- [streamingProgress]="streamingProgress"
418
- [showLoadingByDefault]="true"
419
- [loadingMessages]="loadingMessages"
420
- [loadingTitle]="'AI Analysis'"
421
- [tiltEnabled]="true"
422
- (cardInteraction)="onCardAction($event)">
423
- </app-ai-card-renderer>
424
- </div>
425
- ```
426
-
427
- **Key Settings:**
428
- - `[isStreaming]="isStreaming"` → Controls streaming animation
429
- - `[streamingStage]="streamingStage"` → `'idle'` | `'thinking'` | `'streaming'` | `'complete'`
430
- - `[streamingProgress]="streamingProgress"` → Progress bar (0-1)
431
- - `[showLoadingByDefault]="true"` → Shows loading when no card data
432
- - `[loadingMessages]` → Custom messages during loading
433
-
434
- ---
435
-
436
- ### Comparison Table
437
-
438
- | Feature | Static Card | Streaming Card |
439
- |---------|-------------|----------------|
440
- | `[cardConfig]` | Always provided | Starts `undefined`, built progressively |
441
- | `[streamingStage]` | `'complete'` | `'idle'` → `'thinking'` → `'streaming'` → `'complete'` |
442
- | `[showLoadingByDefault]` | `false` | `true` |
443
- | `[isStreaming]` | Not needed | `true` during generation |
444
- | `[streamingProgress]` | Not needed | `0` to `1` |
445
- | Loading animation | None | Shows animated loading state |
446
- | Use case | Pre-loaded data | AI/LLM generation |
447
-
448
- ---
449
-
450
- ## Using Container + Renderer Pattern (Recommended for Lists)
451
-
452
- When displaying cards in lists or needing more control, use `osi-cards-container` with `app-ai-card-renderer`:
453
-
454
- ### TypeScript
455
-
456
- ```typescript
457
- import { Component } from '@angular/core';
458
- import {
459
- OsiCardsContainerComponent,
460
- AICardRendererComponent,
461
- AICardConfig
462
- } from 'osi-cards-lib';
463
-
464
- @Component({
465
- selector: 'app-card-list',
466
- standalone: true,
467
- imports: [OsiCardsContainerComponent, AICardRendererComponent],
468
- templateUrl: './card-list.component.html'
469
- })
470
- export class CardListComponent {
471
- cardTheme: 'day' | 'night' = 'day';
472
- cardContainerWidth = 600; // Optional: explicit width
473
-
474
- cards: { id: string; card: AICardConfig }[] = [
475
- {
476
- id: '1',
477
- card: {
478
- cardTitle: 'Card 1',
479
- sections: [{ title: 'Info', type: 'info', fields: [{ label: 'Status', value: 'Active' }] }]
480
- }
481
- },
482
- {
483
- id: '2',
484
- card: {
485
- cardTitle: 'Card 2',
486
- sections: [{ title: 'Info', type: 'info', fields: [{ label: 'Status', value: 'Pending' }] }]
487
- }
488
- }
489
- ];
490
-
491
- onCardActionClick(event: { action: string; card: AICardConfig }): void {
492
- console.log('Action clicked:', event);
493
- }
494
- }
495
- ```
496
-
497
- ### HTML
498
-
499
- ```html
500
- <!-- Loop through cards -->
501
- @for (item of cards; track item.id) {
142
+ <div class="col-12 p-0 mb-3" *ngIf="companyCard">
502
143
  <osi-cards-container [theme]="cardTheme">
503
144
  <app-ai-card-renderer
504
- [cardConfig]="item.card"
505
- [containerWidth]="cardContainerWidth"
145
+ [cardConfig]="companyCard"
506
146
  [streamingStage]="'complete'"
507
147
  [showLoadingByDefault]="false"
508
- (cardInteraction)="onCardActionClick($event)">
148
+ [tiltEnabled]="true">
509
149
  </app-ai-card-renderer>
510
150
  </osi-cards-container>
511
- }
151
+ </div>
512
152
  ```
513
153
 
514
- ### Why Use This Pattern?
515
-
516
- - **Theme at container level** - Apply theme once for all nested cards
517
- - **CSS isolation** - Container provides style boundaries
518
- - **Explicit control** - Fine-grained control over each card's behavior
519
- - **No loading state** - `[showLoadingByDefault]="false"` for static data
520
- - **Complete stage** - `[streamingStage]="'complete'"` marks card as fully loaded
521
-
522
- ---
523
-
524
- ## Using AICardRendererComponent (Lower-Level API)
154
+ **Why use `<osi-cards-container>`?**
155
+ - ✅ Automatically sets `data-theme` attribute correctly
156
+ - Automatically adds `perspective: 1200px` for 3D tilt effects
157
+ - Preserves 3D transform context (`transform-style: preserve-3d`)
158
+ - Handles all container styling automatically
159
+ - More reliable than manual div setup
525
160
 
526
- For more control, use `AICardRendererComponent` directly:
161
+ ### Step 5: Define Your Card Configuration
527
162
 
528
- ### TypeScript
163
+ In your component TypeScript file, define the card configuration:
529
164
 
530
165
  ```typescript
531
166
  import { Component } from '@angular/core';
532
- import { AICardRendererComponent, AICardConfig, CardFieldInteractionEvent } from 'osi-cards-lib';
167
+ import { AICardConfig } from 'osi-cards-lib';
533
168
 
534
169
  @Component({
535
- selector: 'app-card-demo',
536
- standalone: true,
537
- imports: [AICardRendererComponent],
538
- templateUrl: './card-demo.component.html'
170
+ selector: 'app-your-component',
171
+ templateUrl: './your-component.html'
539
172
  })
540
- export class CardDemoComponent {
541
- card: AICardConfig = {
542
- cardTitle: 'My Card',
173
+ export class YourComponent {
174
+ cardTheme: 'day' | 'night' = 'night'; // or 'day' for light theme
175
+
176
+ companyCard: AICardConfig = {
177
+ cardTitle: 'Company Profile',
178
+ description: 'Complete company information and insights',
543
179
  sections: [
544
180
  {
545
181
  title: 'Overview',
546
- type: 'overview',
182
+ type: 'info',
547
183
  fields: [
548
- { label: 'Status', value: 'Active' },
549
- { label: 'Type', value: 'Premium' }
184
+ { label: 'Industry', value: 'Technology' },
185
+ { label: 'Employees', value: '500+' },
186
+ { label: 'Founded', value: '2010' }
187
+ ]
188
+ },
189
+ {
190
+ title: 'Key Metrics',
191
+ type: 'analytics',
192
+ fields: [
193
+ { label: 'Revenue', value: '$150M', trend: 'up', change: 25 },
194
+ { label: 'Market Share', value: '18%', trend: 'up', change: 3 }
550
195
  ]
551
196
  }
197
+ ],
198
+ actions: [
199
+ {
200
+ type: 'website',
201
+ label: 'Visit Website',
202
+ variant: 'primary',
203
+ url: 'https://example.com'
204
+ }
552
205
  ]
553
206
  };
554
-
555
- onFieldClick(event: CardFieldInteractionEvent): void {
556
- console.log('Field clicked:', event);
557
- }
558
-
559
- onAgentAction(event: any): void {
560
- console.log('Agent action triggered:', event);
561
- }
562
- }
563
- ```
564
-
565
- ### HTML
566
-
567
- ```html
568
- <app-ai-card-renderer
569
- [cardConfig]="card"
570
- [tiltEnabled]="true"
571
- (fieldInteraction)="onFieldClick($event)"
572
- (agentAction)="onAgentAction($event)">
573
- </app-ai-card-renderer>
574
- ```
575
-
576
- ---
577
-
578
- ## Usage With Streaming
579
-
580
- For AI/LLM streaming scenarios:
581
-
582
- ### TypeScript
583
-
584
- ```typescript
585
- import { Component } from '@angular/core';
586
- import { OsiCardsComponent, AICardConfig, StreamingStage } from 'osi-cards-lib';
587
-
588
- @Component({
589
- selector: 'app-streaming-demo',
590
- standalone: true,
591
- imports: [OsiCardsComponent],
592
- templateUrl: './streaming-demo.component.html'
593
- })
594
- export class StreamingDemoComponent {
595
- card: AICardConfig | undefined;
596
- isStreaming = false;
597
- streamingStage: StreamingStage = 'idle';
598
- streamingProgress = 0;
599
-
600
- // Custom loading messages (optional)
601
- loadingMessages = [
602
- 'Analyzing data...',
603
- 'Processing results...',
604
- 'Almost there...'
605
- ];
606
-
607
- startStreaming(): void {
608
- this.isStreaming = true;
609
- this.streamingStage = 'thinking';
610
-
611
- // Simulate streaming - in real app, this comes from your AI service
612
- setTimeout(() => {
613
- this.streamingStage = 'streaming';
614
- this.simulateCardStreaming();
615
- }, 2000);
616
- }
617
-
618
- private simulateCardStreaming(): void {
619
- // Progressively build card
620
- this.streamingProgress = 0.3;
621
- this.card = {
622
- cardTitle: 'Generating...',
623
- sections: []
624
- };
625
-
626
- setTimeout(() => {
627
- this.streamingProgress = 0.7;
628
- this.card = {
629
- cardTitle: 'Analysis Results',
630
- sections: [
631
- { title: 'Summary', type: 'info', fields: [{ label: 'Status', value: 'Processing' }] }
632
- ]
633
- };
634
- }, 1000);
635
-
636
- setTimeout(() => {
637
- this.streamingProgress = 1;
638
- this.streamingStage = 'complete';
639
- this.isStreaming = false;
640
- this.card = {
641
- cardTitle: 'Analysis Results',
642
- sections: [
643
- { title: 'Summary', type: 'info', fields: [{ label: 'Status', value: 'Complete' }] },
644
- { title: 'Metrics', type: 'analytics', fields: [{ label: 'Score', value: '95%' }] }
645
- ]
646
- };
647
- }, 2000);
648
- }
649
207
  }
650
208
  ```
651
209
 
652
- ### HTML
653
-
654
- ```html
655
- <button (click)="startStreaming()">Start Analysis</button>
656
-
657
- <osi-cards
658
- [card]="card"
659
- [isStreaming]="isStreaming"
660
- [streamingStage]="streamingStage"
661
- [streamingProgress]="streamingProgress"
662
- [loadingMessages]="loadingMessages"
663
- [loadingTitle]="'Analyzing...'"
664
- [showLoadingByDefault]="true">
665
- </osi-cards>
666
- ```
667
-
668
210
  ---
669
211
 
670
- ## Theme Configuration
671
-
672
- **Theme is NOT mandatory.** Cards default to `'day'` (light theme).
673
-
674
- ### Per-Component Theme
675
-
676
- ```html
677
- <!-- Light theme (default) -->
678
- <osi-cards [card]="card"></osi-cards>
679
-
680
- <!-- Explicit light theme -->
681
- <osi-cards [card]="card" [theme]="'day'"></osi-cards>
212
+ ## Complete Example
682
213
 
683
- <!-- Dark theme -->
684
- <osi-cards [card]="card" [theme]="'night'"></osi-cards>
685
- ```
214
+ Here's a complete working example based on a production integration:
686
215
 
687
- ### Global Theme via Provider
216
+ ### `app.config.ts`
688
217
 
689
218
  ```typescript
690
- import { ApplicationConfig } from '@angular/core';
691
- import { provideOSICards } from 'osi-cards-lib';
219
+ import { ApplicationConfig, importProvidersFrom } from '@angular/core';
220
+ import { provideRouter } from '@angular/router';
221
+ import { provideHttpClient } from "@angular/common/http";
222
+ import { provideAnimations } from '@angular/platform-browser/animations';
223
+ import { routes } from './app.routes';
224
+ import { provideOsiCards } from 'osi-cards-lib';
692
225
 
693
226
  export const appConfig: ApplicationConfig = {
694
227
  providers: [
695
- provideOSICards({
696
- defaultTheme: 'night' // Set global default theme
697
- })
228
+ provideRouter(routes),
229
+ provideHttpClient(),
230
+ provideAnimations(),
231
+ provideOsiCards() // Required for animations and library functionality
698
232
  ]
699
233
  };
700
234
  ```
701
235
 
702
- ### Dynamic Theme Switching
703
-
704
- ```typescript
705
- import { Component, inject } from '@angular/core';
706
- import { ThemeService } from 'osi-cards-lib';
707
-
708
- @Component({...})
709
- export class MyComponent {
710
- private themeService = inject(ThemeService);
236
+ ### `angular.json` (excerpt)
711
237
 
712
- toggleTheme(): void {
713
- this.themeService.toggleTheme();
714
- }
715
-
716
- setDarkTheme(): void {
717
- this.themeService.setTheme('dark');
718
- }
719
-
720
- followSystem(): void {
721
- this.themeService.setTheme('system');
238
+ ```json
239
+ {
240
+ "projects": {
241
+ "your-app": {
242
+ "architect": {
243
+ "build": {
244
+ "options": {
245
+ "styles": [
246
+ "src/styles.sass",
247
+ "node_modules/osi-cards-lib/styles/_styles-scoped.scss"
248
+ ],
249
+ "stylePreprocessorOptions": {
250
+ "includePaths": [
251
+ "node_modules/osi-cards-lib/styles"
252
+ ],
253
+ "sass": {
254
+ "silenceDeprecations": ["import"]
255
+ }
256
+ }
257
+ }
258
+ }
259
+ }
260
+ }
722
261
  }
723
262
  }
724
263
  ```
725
264
 
726
- ---
727
-
728
- ## Complete Example
729
-
730
- ### `app.config.ts`
265
+ ### `your-module.ts`
731
266
 
732
267
  ```typescript
733
- import { ApplicationConfig } from '@angular/core';
734
- import { provideRouter } from '@angular/router';
735
- import { provideOSICards } from 'osi-cards-lib';
736
-
737
- export const appConfig: ApplicationConfig = {
738
- providers: [
739
- provideOSICards(), // Enable library with animations
740
- provideRouter([])
268
+ import { NgModule } from '@angular/core';
269
+ import { CommonModule } from '@angular/common';
270
+ import { AICardRendererComponent, OsiCardsContainerComponent } from 'osi-cards-lib';
271
+
272
+ @NgModule({
273
+ imports: [
274
+ CommonModule,
275
+ AICardRendererComponent,
276
+ OsiCardsContainerComponent
741
277
  ]
742
- };
743
- ```
744
-
745
- ### `styles.scss`
746
-
747
- ```scss
748
- @import 'osi-cards-lib/styles/_styles';
749
-
750
- // Optional: Override theme variables
751
- :root {
752
- --osi-card-accent: #6366f1;
753
- }
278
+ })
279
+ export class YourModule { }
754
280
  ```
755
281
 
756
- ### `card-page.component.ts`
282
+ ### `your-component.ts`
757
283
 
758
284
  ```typescript
759
285
  import { Component } from '@angular/core';
760
- import { OsiCardsComponent, AICardConfig, CardFieldInteractionEvent } from 'osi-cards-lib';
286
+ import { AICardConfig } from 'osi-cards-lib';
761
287
 
762
288
  @Component({
763
- selector: 'app-card-page',
764
- standalone: true,
765
- imports: [OsiCardsComponent],
766
- template: `
767
- <div class="page-container">
768
- <h1>Company Profile</h1>
769
-
770
- <osi-cards
771
- [card]="companyCard"
772
- [tiltEnabled]="true"
773
- (fieldClick)="handleFieldClick($event)"
774
- (actionClick)="handleAction($event)">
775
- </osi-cards>
776
- </div>
777
- `,
778
- styles: [`
779
- .page-container {
780
- max-width: 900px;
781
- margin: 0 auto;
782
- padding: 2rem;
783
- }
784
- `]
289
+ selector: 'app-your-component',
290
+ templateUrl: './your-component.html'
785
291
  })
786
- export class CardPageComponent {
292
+ export class YourComponent {
293
+ cardTheme: 'day' | 'night' = 'night';
294
+
787
295
  companyCard: AICardConfig = {
788
- cardTitle: 'Acme Corporation',
789
- cardSubtitle: 'Global Technology Leader',
296
+ cardTitle: 'Company Profile',
297
+ description: 'Complete company information',
790
298
  sections: [
791
299
  {
792
- title: 'Company Overview',
793
- type: 'overview',
794
- fields: [
795
- { label: 'Industry', value: 'Enterprise Software' },
796
- { label: 'Founded', value: '2010' },
797
- { label: 'Headquarters', value: 'San Francisco, CA' },
798
- { label: 'Employees', value: '2,500+' }
799
- ]
800
- },
801
- {
802
- title: 'Key Metrics',
803
- type: 'analytics',
804
- fields: [
805
- { label: 'Annual Revenue', value: '$150M', trend: 'up', change: 25 },
806
- { label: 'Market Share', value: '18%', trend: 'up', change: 3 },
807
- { label: 'Customer Growth', value: '+340', trend: 'up' },
808
- { label: 'NPS Score', value: '72', trend: 'stable' }
809
- ]
810
- },
811
- {
812
- title: 'Leadership Team',
813
- type: 'contact-card',
300
+ title: 'Overview',
301
+ type: 'info',
814
302
  fields: [
815
- { name: 'Jane Smith', role: 'CEO', email: 'jane@acme.com' },
816
- { name: 'John Doe', role: 'CTO', email: 'john@acme.com' }
817
- ]
818
- },
819
- {
820
- title: 'Products & Services',
821
- type: 'list',
822
- items: [
823
- { title: 'Enterprise Platform', description: 'Core business solution' },
824
- { title: 'Analytics Suite', description: 'Data insights and reporting' },
825
- { title: 'Integration Hub', description: 'Connect your tools' }
303
+ { label: 'Industry', value: 'Technology' },
304
+ { label: 'Employees', value: '500+' }
826
305
  ]
827
306
  }
828
- ],
829
- actions: [
830
- { type: 'website', label: 'Visit Website', variant: 'primary', url: 'https://acme.com' },
831
- { type: 'mail', label: 'Contact Sales', variant: 'outline', email: { contact: { email: 'sales@acme.com' }, subject: 'Inquiry' } },
832
- { type: 'agent', label: 'Ask AI Assistant', variant: 'ghost', agentId: 'sales-bot' }
833
307
  ]
834
308
  };
309
+ }
310
+ ```
835
311
 
836
- handleFieldClick(event: CardFieldInteractionEvent): void {
837
- console.log('Field clicked:', event);
838
- // Handle field interactions
839
- }
312
+ ### `your-component.html`
840
313
 
841
- handleAction(event: { action: string; card: AICardConfig }): void {
842
- console.log('Action clicked:', event);
843
- // Handle action button clicks
844
- }
845
- }
314
+ ```html
315
+ <div class="col-12 p-0 mb-3" *ngIf="companyCard">
316
+ <osi-cards-container [theme]="cardTheme">
317
+ <app-ai-card-renderer
318
+ [cardConfig]="companyCard"
319
+ [streamingStage]="'complete'"
320
+ [showLoadingByDefault]="false"
321
+ [tiltEnabled]="true">
322
+ </app-ai-card-renderer>
323
+ </osi-cards-container>
324
+ </div>
846
325
  ```
847
326
 
848
327
  ---
849
328
 
850
329
  ## Component API Reference
851
330
 
852
- ### OsiCardsComponent (`<osi-cards>`)
853
-
854
- High-level wrapper component with simplified API.
855
-
856
- **Inputs:**
857
-
858
- | Input | Type | Default | Required | Description |
859
- |-------|------|---------|----------|-------------|
860
- | `card` | `AICardConfig` | `undefined` | No | The card configuration to render |
861
- | `theme` | `'day' \| 'night'` | `'day'` | No | Theme to apply |
862
- | `fullscreen` | `boolean` | `false` | No | Display in fullscreen mode |
863
- | `tiltEnabled` | `boolean` | `true` | No | Enable 3D tilt effect on hover |
864
- | `containerWidth` | `number` | auto | No | Explicit container width for layout |
865
- | `isStreaming` | `boolean` | `false` | No | Whether streaming is active |
866
- | `streamingStage` | `StreamingStage` | `undefined` | No | Current streaming stage |
867
- | `streamingProgress` | `number` | `0` | No | Streaming progress (0-1) |
868
- | `showLoadingByDefault` | `boolean` | `true` | No | Show loading state when no card |
869
- | `loadingMessages` | `string[]` | defaults | No | Custom loading messages |
870
- | `loadingTitle` | `string` | `'Creating OSI Card'` | No | Loading state title |
871
-
872
- **Outputs:**
873
-
874
- | Output | Type | Description |
875
- |--------|------|-------------|
876
- | `fieldClick` | `CardFieldInteractionEvent` | Emitted when a field is clicked |
877
- | `actionClick` | `{ action: string; card: AICardConfig }` | Emitted when an action button is clicked |
878
- | `fullscreenChange` | `boolean` | Emitted when fullscreen is toggled |
879
- | `agentAction` | `{ action, card, agentId?, context? }` | Emitted for agent-type actions |
880
- | `questionAction` | `{ action, card, question? }` | Emitted for question-type actions |
881
- | `export` | `void` | Emitted when export is requested |
882
-
883
- ---
884
-
885
331
  ### OsiCardsContainerComponent (`<osi-cards-container>`)
886
332
 
887
- Container wrapper for theme and CSS isolation.
333
+ Container wrapper for theme and CSS isolation. **Always use this component** to wrap your card renderer.
888
334
 
889
335
  **Inputs:**
890
336
 
891
337
  | Input | Type | Default | Required | Description |
892
338
  |-------|------|---------|----------|-------------|
893
339
  | `theme` | `'day' \| 'night'` | `'day'` | No | Theme to apply to container |
894
- | `strictIsolation` | `boolean` | `false` | No | Enable strict CSS containment |
895
340
 
896
341
  **Usage:**
897
342
 
@@ -901,8 +346,6 @@ Container wrapper for theme and CSS isolation.
901
346
  </osi-cards-container>
902
347
  ```
903
348
 
904
- ---
905
-
906
349
  ### AICardRendererComponent (`<app-ai-card-renderer>`)
907
350
 
908
351
  Core rendering component with full control.
@@ -912,36 +355,27 @@ Core rendering component with full control.
912
355
  | Input | Type | Default | Required | Description |
913
356
  |-------|------|---------|----------|-------------|
914
357
  | `cardConfig` | `AICardConfig` | `undefined` | No | The card configuration |
915
- | `isFullscreen` | `boolean` | `false` | No | Fullscreen mode |
916
- | `tiltEnabled` | `boolean` | `true` | No | Enable 3D tilt effect |
917
358
  | `streamingStage` | `StreamingStage` | `undefined` | No | `'idle'` \| `'thinking'` \| `'streaming'` \| `'complete'` |
918
- | `streamingProgress` | `number` | `undefined` | No | Progress 0-1 |
919
- | `isStreaming` | `boolean` | `false` | No | Streaming animation state |
920
- | `showLoadingByDefault` | `boolean` | `true` | No | Show loading when no data |
921
- | `containerWidth` | `number` | auto | No | Explicit width for masonry |
922
- | `loadingMessages` | `string[]` | defaults | No | Custom loading messages |
923
- | `loadingTitle` | `string` | `'Creating OSI Card'` | No | Loading title |
924
- | `updateSource` | `'stream' \| 'liveEdit'` | `'stream'` | No | Update source mode |
359
+ | `showLoadingByDefault` | `boolean` | `true` | No | Show loading when no card data |
360
+ | `tiltEnabled` | `boolean` | `true` | No | Enable 3D tilt effect on hover |
925
361
 
926
362
  **Outputs:**
927
363
 
928
364
  | Output | Type | Description |
929
365
  |--------|------|-------------|
930
- | `fieldInteraction` | `CardFieldInteractionEvent` | Field clicked |
931
366
  | `cardInteraction` | `{ action: string; card: AICardConfig }` | Action button clicked |
932
- | `fullscreenToggle` | `boolean` | Fullscreen toggled |
933
- | `agentAction` | `{ action, card, agentId?, context? }` | Agent action |
934
- | `questionAction` | `{ action, card, question? }` | Question action |
935
- | `export` | `void` | Export requested |
367
+ | `fieldInteraction` | `CardFieldInteractionEvent` | Field clicked |
936
368
 
937
369
  **Minimal Usage (Static Card, No Loading):**
938
370
 
939
371
  ```html
940
- <app-ai-card-renderer
941
- [cardConfig]="card"
942
- [streamingStage]="'complete'"
943
- [showLoadingByDefault]="false">
944
- </app-ai-card-renderer>
372
+ <osi-cards-container [theme]="cardTheme">
373
+ <app-ai-card-renderer
374
+ [cardConfig]="card"
375
+ [streamingStage]="'complete'"
376
+ [showLoadingByDefault]="false">
377
+ </app-ai-card-renderer>
378
+ </osi-cards-container>
945
379
  ```
946
380
 
947
381
  ---
@@ -970,255 +404,50 @@ Core rendering component with full control.
970
404
 
971
405
  ---
972
406
 
973
- ## Card Presets
974
-
975
- Quickly create common card types:
976
-
977
- ```typescript
978
- import { PresetFactory } from 'osi-cards-lib';
979
-
980
- // Company card
981
- const companyCard = PresetFactory.createCompany({
982
- name: 'Acme Corp',
983
- industry: 'Technology',
984
- employees: '500+',
985
- websiteUrl: 'https://acme.com'
986
- });
987
-
988
- // Analytics dashboard
989
- const analyticsCard = PresetFactory.createAnalytics({
990
- title: 'Sales Performance',
991
- kpis: [
992
- { label: 'Revenue', value: '$1.2M', percentage: 105, trend: 'up' }
993
- ]
994
- });
995
-
996
- // Contact card
997
- const contactCard = PresetFactory.createContact({
998
- name: 'John Doe',
999
- email: 'john@example.com'
1000
- });
1001
- ```
1002
-
1003
- ---
1004
-
1005
407
  ## Troubleshooting
1006
408
 
1007
409
  ### Animations not working
1008
410
 
1009
- Ensure you've added `provideOSICards()` to your `app.config.ts`:
411
+ Ensure you've added `provideOsiCards()` to your `app.config.ts`:
1010
412
 
1011
413
  ```typescript
1012
414
  providers: [
1013
- provideOSICards() // Required!
415
+ provideOsiCards() // Required!
1014
416
  ]
1015
417
  ```
1016
418
 
1017
419
  ### Styles not loading / Library looks unstyled
1018
420
 
1019
- **If using scoped styles (`_styles-scoped`):**
1020
-
1021
- **CRITICAL**: If styles are completely missing (card renders but has no styling), the SASS import is likely not resolving. Use one of these solutions:
1022
-
1023
- **Solution 1: Add to angular.json (RECOMMENDED for SASS files)**
1024
-
1025
- This is the most reliable method:
1026
-
1027
- ```json
1028
- {
1029
- "projects": {
1030
- "your-app": {
1031
- "architect": {
1032
- "build": {
1033
- "options": {
1034
- "styles": [
1035
- "src/styles.sass",
1036
- "node_modules/osi-cards-lib/styles/_styles-scoped.scss"
1037
- ],
1038
- "stylePreprocessorOptions": {
1039
- "includePaths": [
1040
- "node_modules/osi-cards-lib/styles"
1041
- ],
1042
- "sass": {
1043
- "silenceDeprecations": ["import"]
1044
- }
1045
- }
1046
- }
1047
- }
1048
- }
1049
- }
1050
- }
1051
- }
1052
- ```
1053
-
1054
- Then **remove** the `@import` from your `styles.sass` file.
1055
-
1056
- **Solution 2: Fix the SASS import**
1057
-
1058
- If you want to keep the import in your styles file:
1059
-
1060
- 1. **Use tilde prefix**:
1061
- ```sass
1062
- @import '~osi-cards-lib/styles/_styles-scoped';
1063
- ```
1064
-
1065
- 2. **Or use full path**:
1066
- ```sass
1067
- @import 'node_modules/osi-cards-lib/styles/_styles-scoped';
1068
- ```
1069
-
1070
- 3. **And add to angular.json**:
1071
- ```json
1072
- "stylePreprocessorOptions": {
1073
- "includePaths": ["node_modules"],
1074
- "sass": {
1075
- "silenceDeprecations": ["import"]
1076
- }
1077
- }
1078
- ```
1079
-
1080
- **Solution 3: Verify import path** - Use lowercase `osi-cards-lib` (not `osi-cards-Lib`):
1081
- ```scss
1082
- @import 'osi-cards-lib/styles/_styles-scoped';
1083
- ```
1084
-
1085
- 2. **Try alternative import methods** if the above doesn't work:
1086
-
1087
- **Option A: With tilde prefix** (for older Angular versions):
1088
- ```scss
1089
- @import '~osi-cards-lib/styles/_styles-scoped';
1090
- ```
1091
-
1092
- **Option B: With explicit extension**:
1093
- ```scss
1094
- @import 'osi-cards-lib/styles/_styles-scoped.scss';
1095
- ```
1096
-
1097
- **Option C: Add to angular.json** (if SCSS import fails):
1098
- ```json
1099
- {
1100
- "projects": {
1101
- "your-app": {
1102
- "architect": {
1103
- "build": {
1104
- "options": {
1105
- "styles": [
1106
- "node_modules/osi-cards-lib/styles/_styles-scoped.scss",
1107
- "src/styles.scss"
1108
- ],
1109
- "stylePreprocessorOptions": {
1110
- "includePaths": [
1111
- "node_modules/osi-cards-lib/styles"
1112
- ],
1113
- "sass": {
1114
- "silenceDeprecations": ["import"]
1115
- }
1116
- }
1117
- }
1118
- }
1119
- }
1120
- }
1121
- }
1122
- }
1123
- ```
1124
- Then remove the `@import` from your `styles.scss`.
1125
-
1126
- 3. **Wrap components in container** - You MUST wrap your components. **RECOMMENDED: Use `<osi-cards-container>` component** (automatically handles theme and tilt):
1127
- ```html
1128
- <!-- ✅ RECOMMENDED: Component automatically handles theme and tilt -->
1129
- <osi-cards-container [theme]="'day'">
1130
- <app-ai-card-renderer [cardConfig]="card"></app-ai-card-renderer>
1131
- </osi-cards-container>
1132
- ```
1133
-
1134
- **Alternative (Manual Setup):**
1135
- ```html
1136
- <!-- ⚠️ Manual setup - requires both class and data-theme -->
1137
- <div class="osi-cards-container" data-theme="day">
1138
- <app-ai-card-renderer [cardConfig]="card"></app-ai-card-renderer>
1139
- </div>
1140
- ```
1141
-
1142
- 4. **Set theme attribute** - The component handles this automatically via `[theme]` input. For manual setup, add `data-theme="day"` or `data-theme="night"`:
1143
- ```html
1144
- <div class="osi-cards-container" data-theme="day">
1145
- ```
1146
-
1147
- 5. **Verify package installation**:
1148
- ```bash
1149
- npm list osi-cards-lib
1150
- ```
1151
- Should show version `1.5.19` or higher.
1152
-
1153
- 6. **Check browser console** - Look for 404 errors on style files. If you see errors, the import path is incorrect.
1154
-
1155
- 7. **Rebuild your app** after adding the import:
421
+ 1. **Verify styles are in angular.json** - Check that `node_modules/osi-cards-lib/styles/_styles-scoped.scss` is in your `styles` array
422
+ 2. **Check stylePreprocessorOptions** - Ensure `includePaths` includes `node_modules/osi-cards-lib/styles`
423
+ 3. **Use the container component** - Always wrap your card in `<osi-cards-container [theme]="'day'">` or `<osi-cards-container [theme]="'night'">`
424
+ 4. **Rebuild your app** after adding the import:
1156
425
  ```bash
1157
426
  ng build
1158
427
  # or
1159
428
  npm start
1160
429
  ```
1161
430
 
1162
- **If using global styles (`_styles`):**
1163
-
1164
- Import styles in your `styles.scss`:
1165
- ```scss
1166
- @import 'osi-cards-lib/styles/_styles';
1167
- ```
1168
-
1169
- **Common Issues:**
1170
- - ❌ **Wrong import path**: `@import 'osi-cards-Lib/styles/_styles-scoped'` (wrong casing)
1171
- - ❌ **Missing container**: Components not wrapped in `.osi-cards-container` or `<osi-cards-container>` component
1172
- - ❌ **Missing theme**: No `data-theme` attribute on container (use `<osi-cards-container [theme]="'day'">` to fix automatically)
1173
- - ❌ **Tilt not working**: Missing `perspective` on container (use `<osi-cards-container>` component which adds it automatically)
1174
- - ❌ **SCSS not compiled**: Check that your build process compiles SCSS files
1175
-
1176
431
  ### Icons not showing
1177
432
 
1178
433
  Ensure `lucide-angular` is installed:
1179
434
 
1180
435
  ```bash
1181
- npm install lucide-angular@^0.548.0
436
+ npm install lucide-angular@^0.292.0
1182
437
  ```
1183
438
 
1184
- ### CSS Variables not working
439
+ ### Theme not applying
1185
440
 
1186
- If CSS variables (like `--color-brand`) aren't working:
1187
-
1188
- 1. **For scoped styles**: Ensure you're using `.osi-cards-container` wrapper
1189
- 2. **Check theme**: Verify `data-theme` attribute is set correctly
1190
- 3. **Override variables**: You can override variables in your own styles:
1191
- ```scss
1192
- .osi-cards-container {
1193
- --color-brand: #ff7900;
1194
- --background: #ffffff;
1195
- }
1196
- ```
441
+ - Ensure you're using `<osi-cards-container [theme]="'day'">` or `<osi-cards-container [theme]="'night'">`
442
+ - The `[theme]` input automatically sets the `data-theme` attribute
443
+ - Check browser console for any errors
1197
444
 
1198
445
  ---
1199
446
 
1200
- ## Documentation
1201
-
1202
- - [Detailed Usage Guide](./USAGE.md)
1203
- - [Import Examples](./IMPORT_EXAMPLE.md)
1204
-
1205
447
  ## License
1206
448
 
1207
449
  MIT
1208
450
 
1209
451
  ## Version
1210
452
 
1211
- 1.5.17
1212
-
1213
- ---
1214
-
1215
- ## Zero-Gap Packing (Advanced)
1216
-
1217
- If you need maximum layout density (minimal gaps), you can use the zero-gap packing helper:
1218
-
1219
- ```typescript
1220
- import { packWithZeroGapsGuarantee } from 'osi-cards-lib';
1221
-
1222
- const result = packWithZeroGapsGuarantee(sections, 4, 12);
1223
- // result.positionedSections, result.totalHeight, result.utilization, result.gapCount
1224
- ```
453
+ 1.5.37