@sc4rfurryx/proteusjs 1.0.0

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 (82) hide show
  1. package/API.md +438 -0
  2. package/FEATURES.md +286 -0
  3. package/LICENSE +21 -0
  4. package/README.md +645 -0
  5. package/dist/.tsbuildinfo +1 -0
  6. package/dist/proteus.cjs.js +16014 -0
  7. package/dist/proteus.cjs.js.map +1 -0
  8. package/dist/proteus.d.ts +3018 -0
  9. package/dist/proteus.esm.js +16005 -0
  10. package/dist/proteus.esm.js.map +1 -0
  11. package/dist/proteus.esm.min.js +8 -0
  12. package/dist/proteus.esm.min.js.map +1 -0
  13. package/dist/proteus.js +16020 -0
  14. package/dist/proteus.js.map +1 -0
  15. package/dist/proteus.min.js +8 -0
  16. package/dist/proteus.min.js.map +1 -0
  17. package/package.json +98 -0
  18. package/src/__tests__/mvp-integration.test.ts +518 -0
  19. package/src/accessibility/AccessibilityEngine.ts +2106 -0
  20. package/src/accessibility/ScreenReaderSupport.ts +444 -0
  21. package/src/accessibility/__tests__/ScreenReaderSupport.test.ts +435 -0
  22. package/src/animations/FLIPAnimationSystem.ts +491 -0
  23. package/src/compatibility/BrowserCompatibility.ts +1076 -0
  24. package/src/containers/BreakpointSystem.ts +347 -0
  25. package/src/containers/ContainerBreakpoints.ts +726 -0
  26. package/src/containers/ContainerManager.ts +370 -0
  27. package/src/containers/ContainerUnits.ts +336 -0
  28. package/src/containers/ContextIsolation.ts +394 -0
  29. package/src/containers/ElementQueries.ts +411 -0
  30. package/src/containers/SmartContainer.ts +536 -0
  31. package/src/containers/SmartContainers.ts +376 -0
  32. package/src/containers/__tests__/ContainerBreakpoints.test.ts +411 -0
  33. package/src/containers/__tests__/SmartContainers.test.ts +281 -0
  34. package/src/content/ResponsiveImages.ts +570 -0
  35. package/src/core/EventSystem.ts +147 -0
  36. package/src/core/MemoryManager.ts +321 -0
  37. package/src/core/PerformanceMonitor.ts +238 -0
  38. package/src/core/PluginSystem.ts +275 -0
  39. package/src/core/ProteusJS.test.ts +164 -0
  40. package/src/core/ProteusJS.ts +962 -0
  41. package/src/developer/PerformanceProfiler.ts +567 -0
  42. package/src/developer/VisualDebuggingTools.ts +656 -0
  43. package/src/developer/ZeroConfigSystem.ts +593 -0
  44. package/src/index.ts +35 -0
  45. package/src/integration.test.ts +227 -0
  46. package/src/layout/AdaptiveGrid.ts +429 -0
  47. package/src/layout/ContentReordering.ts +532 -0
  48. package/src/layout/FlexboxEnhancer.ts +406 -0
  49. package/src/layout/FlowLayout.ts +545 -0
  50. package/src/layout/SpacingSystem.ts +512 -0
  51. package/src/observers/IntersectionObserverPolyfill.ts +289 -0
  52. package/src/observers/ObserverManager.ts +299 -0
  53. package/src/observers/ResizeObserverPolyfill.ts +179 -0
  54. package/src/performance/BatchDOMOperations.ts +519 -0
  55. package/src/performance/CSSOptimizationEngine.ts +646 -0
  56. package/src/performance/CacheOptimizationSystem.ts +601 -0
  57. package/src/performance/EfficientEventHandler.ts +740 -0
  58. package/src/performance/LazyEvaluationSystem.ts +532 -0
  59. package/src/performance/MemoryManagementSystem.ts +497 -0
  60. package/src/performance/PerformanceMonitor.ts +931 -0
  61. package/src/performance/__tests__/BatchDOMOperations.test.ts +309 -0
  62. package/src/performance/__tests__/EfficientEventHandler.test.ts +268 -0
  63. package/src/performance/__tests__/PerformanceMonitor.test.ts +422 -0
  64. package/src/polyfills/BrowserPolyfills.ts +586 -0
  65. package/src/polyfills/__tests__/BrowserPolyfills.test.ts +328 -0
  66. package/src/test/setup.ts +115 -0
  67. package/src/theming/SmartThemeSystem.ts +591 -0
  68. package/src/types/index.ts +134 -0
  69. package/src/typography/ClampScaling.ts +356 -0
  70. package/src/typography/FluidTypography.ts +759 -0
  71. package/src/typography/LineHeightOptimization.ts +430 -0
  72. package/src/typography/LineHeightOptimizer.ts +326 -0
  73. package/src/typography/TextFitting.ts +355 -0
  74. package/src/typography/TypographicScale.ts +428 -0
  75. package/src/typography/VerticalRhythm.ts +369 -0
  76. package/src/typography/__tests__/FluidTypography.test.ts +432 -0
  77. package/src/typography/__tests__/LineHeightOptimization.test.ts +436 -0
  78. package/src/utils/Logger.ts +173 -0
  79. package/src/utils/debounce.ts +259 -0
  80. package/src/utils/performance.ts +371 -0
  81. package/src/utils/support.ts +106 -0
  82. package/src/utils/version.ts +24 -0
@@ -0,0 +1,726 @@
1
+ /**
2
+ * ContainerBreakpoints - Breakpoint management system for container queries
3
+ * Handles breakpoint registration, monitoring, and callback execution
4
+ */
5
+
6
+ import { logger } from '../utils/Logger';
7
+ import { PerformanceMonitor } from '../performance/PerformanceMonitor';
8
+
9
+ export interface BreakpointMap {
10
+ [key: string]: string;
11
+ }
12
+
13
+ export interface BreakpointConfig {
14
+ element: Element;
15
+ breakpoints: BreakpointMap;
16
+ callback?: BreakpointCallback;
17
+ currentBreakpoint?: string;
18
+ parsedBreakpoints: ParsedBreakpoint[];
19
+ }
20
+
21
+ export interface ParsedBreakpoint {
22
+ name: string;
23
+ value: number;
24
+ unit: string;
25
+ type: 'min' | 'max';
26
+ originalValue: string;
27
+ }
28
+
29
+ export interface BreakpointCallbackData {
30
+ width: number;
31
+ height: number;
32
+ breakpoint: string;
33
+ previousBreakpoint?: string;
34
+ element: Element;
35
+ }
36
+
37
+ export type BreakpointCallback = (breakpoint: string, data: BreakpointCallbackData) => void;
38
+
39
+ export class ContainerBreakpoints {
40
+ private breakpoints: Map<string, BreakpointConfig> = new Map();
41
+ private observer: ResizeObserver | null = null;
42
+ private idCounter: number = 0;
43
+ private cssRules: Map<string, CSSStyleSheet> = new Map();
44
+ private performanceMonitor?: PerformanceMonitor;
45
+ private performanceMetrics: {
46
+ evaluationTimes: number[];
47
+ averageTime: number;
48
+ totalEvaluations: number;
49
+ } = {
50
+ evaluationTimes: [],
51
+ averageTime: 0,
52
+ totalEvaluations: 0
53
+ };
54
+
55
+ constructor() {
56
+ this.setupResizeObserver();
57
+ this.setupPerformanceMonitoring();
58
+ this.injectBaseCSS();
59
+ }
60
+
61
+ /**
62
+ * Set performance monitor for integration
63
+ */
64
+ public setPerformanceMonitor(monitor: PerformanceMonitor): void {
65
+ this.performanceMonitor = monitor;
66
+ }
67
+
68
+ /**
69
+ * Register breakpoints for a container element (alias for register)
70
+ */
71
+ public registerContainer(
72
+ element: Element,
73
+ breakpoints: BreakpointMap,
74
+ callback?: BreakpointCallback
75
+ ): string {
76
+ return this.register(element, breakpoints, callback);
77
+ }
78
+
79
+ /**
80
+ * Register breakpoints for a container element
81
+ */
82
+ public register(
83
+ element: Element,
84
+ breakpoints: BreakpointMap,
85
+ callback?: BreakpointCallback
86
+ ): string {
87
+ const id = this.generateId();
88
+
89
+ try {
90
+ const parsedBreakpoints = this.parseBreakpoints(breakpoints);
91
+ const config: BreakpointConfig = {
92
+ element,
93
+ breakpoints,
94
+ ...(callback && { callback }),
95
+ parsedBreakpoints
96
+ };
97
+
98
+ this.breakpoints.set(id, config);
99
+
100
+ // Start observing the element
101
+ if (this.observer) {
102
+ this.observer.observe(element);
103
+ }
104
+
105
+ // Set initial breakpoint
106
+ this.updateBreakpoint(id);
107
+
108
+ return id;
109
+ } catch (error) {
110
+ logger.error('Failed to register breakpoints', error);
111
+ return '';
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Update container breakpoints (alias for updateElement)
117
+ */
118
+ public updateContainer(element: Element): void {
119
+ this.updateElement(element);
120
+ }
121
+
122
+ /**
123
+ * Unregister breakpoints for an element
124
+ */
125
+ public unregister(id: string): void {
126
+ const config = this.breakpoints.get(id);
127
+ if (!config) return;
128
+
129
+ // Stop observing the element if no other configs use it
130
+ const elementStillUsed = Array.from(this.breakpoints.values())
131
+ .some(c => c !== config && c.element === config.element);
132
+
133
+ if (!elementStillUsed && this.observer) {
134
+ this.observer.unobserve(config.element);
135
+ }
136
+
137
+ // Remove CSS classes
138
+ this.removeBreakpointClasses(config.element, config.parsedBreakpoints);
139
+
140
+ this.breakpoints.delete(id);
141
+ }
142
+
143
+ /**
144
+ * Get current breakpoint for a registered element
145
+ */
146
+ public getCurrentBreakpoint(id: string): string | null {
147
+ const config = this.breakpoints.get(id);
148
+ return config?.currentBreakpoint || null;
149
+ }
150
+
151
+ /**
152
+ * Update breakpoints for a specific element
153
+ */
154
+ public updateElement(element: Element): void {
155
+ for (const [id, config] of this.breakpoints) {
156
+ if (config.element === element) {
157
+ this.updateBreakpoint(id);
158
+ }
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Get all registered breakpoint configurations
164
+ */
165
+ public getAllConfigs(): Map<string, BreakpointConfig> {
166
+ return new Map(this.breakpoints);
167
+ }
168
+
169
+ // destroy method moved to end of class with enhanced functionality
170
+
171
+ /**
172
+ * Setup ResizeObserver to monitor element size changes
173
+ */
174
+ private setupResizeObserver(): void {
175
+ if (typeof ResizeObserver === 'undefined') {
176
+ logger.warn('ResizeObserver not supported. Container breakpoints may not work correctly.');
177
+ return;
178
+ }
179
+
180
+ this.observer = new ResizeObserver((entries) => {
181
+ for (const entry of entries) {
182
+ this.updateElement(entry.target);
183
+ }
184
+ });
185
+ }
186
+
187
+ /**
188
+ * Generate unique ID for breakpoint registration
189
+ */
190
+ private generateId(): string {
191
+ return `proteus-breakpoint-${++this.idCounter}-${Date.now()}`;
192
+ }
193
+
194
+ /**
195
+ * Parse breakpoint values into standardized format
196
+ */
197
+ private parseBreakpoints(breakpoints: BreakpointMap): ParsedBreakpoint[] {
198
+ const parsed: ParsedBreakpoint[] = [];
199
+
200
+ for (const [name, value] of Object.entries(breakpoints)) {
201
+ try {
202
+ const parsedValue = this.parseBreakpointValue(value);
203
+ parsed.push({
204
+ name,
205
+ value: parsedValue.value,
206
+ unit: parsedValue.unit,
207
+ type: parsedValue.type,
208
+ originalValue: value
209
+ });
210
+ } catch (error) {
211
+ logger.warn(`Invalid breakpoint value "${value}" for "${name}"`, error);
212
+ }
213
+ }
214
+
215
+ // Sort by value (ascending)
216
+ parsed.sort((a, b) => a.value - b.value);
217
+
218
+ return parsed;
219
+ }
220
+
221
+ /**
222
+ * Parse individual breakpoint value
223
+ */
224
+ private parseBreakpointValue(value: string): { value: number; unit: string; type: 'min' | 'max' } {
225
+ // Handle media query syntax like "(max-width: 200px)" or "(min-width: 400px)"
226
+ const mediaQueryMatch = value.match(/\((min|max)-width:\s*(\d+(?:\.\d+)?)(px|em|rem|%|vw|vh|ch|cw|cmin|cmax)?\)/);
227
+
228
+ if (mediaQueryMatch) {
229
+ const type = mediaQueryMatch[1] as 'min' | 'max';
230
+ const numericValue = parseFloat(mediaQueryMatch[2]!);
231
+ const unit = mediaQueryMatch[3] || 'px';
232
+
233
+ return { value: numericValue, unit, type };
234
+ }
235
+
236
+ // Handle simple numeric values like "200px"
237
+ const simpleMatch = value.match(/^(\d+(?:\.\d+)?)(px|em|rem|%|vw|vh|ch|cw|cmin|cmax)?$/);
238
+
239
+ if (!simpleMatch) {
240
+ throw new Error(`Invalid breakpoint value: ${value}`);
241
+ }
242
+
243
+ const numericValue = parseFloat(simpleMatch[1]!);
244
+ const unit = simpleMatch[2] || 'px';
245
+
246
+ // Convert relative units to pixels for comparison
247
+ let pixelValue = numericValue;
248
+
249
+ switch (unit) {
250
+ case 'em':
251
+ case 'rem':
252
+ pixelValue = numericValue * 16; // Assume 16px base font size
253
+ break;
254
+ case 'vw':
255
+ pixelValue = (numericValue / 100) * window.innerWidth;
256
+ break;
257
+ case 'vh':
258
+ pixelValue = (numericValue / 100) * window.innerHeight;
259
+ break;
260
+ case '%':
261
+ // For percentage, we'll need the parent element context
262
+ // For now, treat as pixels
263
+ pixelValue = numericValue;
264
+ break;
265
+ case 'px':
266
+ default:
267
+ pixelValue = numericValue;
268
+ break;
269
+ }
270
+
271
+ return { value: pixelValue, unit, type: 'min' }; // Default to min-width for simple values
272
+ }
273
+
274
+ /**
275
+ * Update breakpoint for a specific configuration
276
+ */
277
+ private updateBreakpoint(id: string): void {
278
+ const config = this.breakpoints.get(id);
279
+ if (!config) return;
280
+
281
+ const rect = config.element.getBoundingClientRect();
282
+ const width = rect.width;
283
+ const height = rect.height;
284
+
285
+ // Determine current breakpoint
286
+ const currentBreakpoint = this.determineBreakpoint(width, config.parsedBreakpoints);
287
+ const previousBreakpoint = config.currentBreakpoint;
288
+
289
+ // Record performance metrics
290
+ if (this.performanceMonitor) {
291
+ this.performanceMonitor.recordOperation();
292
+ }
293
+
294
+ // Only update if breakpoint changed
295
+ if (currentBreakpoint !== previousBreakpoint) {
296
+ config.currentBreakpoint = currentBreakpoint;
297
+
298
+ // Update CSS classes
299
+ this.updateBreakpointClasses(config.element, currentBreakpoint, config.parsedBreakpoints);
300
+
301
+ // Call callback if provided
302
+ if (config.callback) {
303
+ try {
304
+ // Create callback data object with core properties
305
+ const callbackData: any = {
306
+ width,
307
+ height,
308
+ breakpoint: currentBreakpoint
309
+ };
310
+
311
+ // Add optional properties only if they exist
312
+ if (previousBreakpoint) {
313
+ callbackData.previousBreakpoint = previousBreakpoint;
314
+ }
315
+
316
+ // Add element reference for advanced use cases
317
+ callbackData.element = config.element;
318
+
319
+ config.callback(currentBreakpoint, callbackData);
320
+ } catch (error) {
321
+ logger.error('Error in breakpoint callback', error);
322
+ }
323
+ }
324
+
325
+ // Dispatch custom event
326
+ this.dispatchBreakpointEvent(config.element, currentBreakpoint, {
327
+ width,
328
+ height,
329
+ breakpoint: currentBreakpoint,
330
+ ...(previousBreakpoint && { previousBreakpoint }),
331
+ element: config.element
332
+ });
333
+ }
334
+ }
335
+
336
+ /**
337
+ * Determine which breakpoint applies to the current width
338
+ */
339
+ private determineBreakpoint(width: number, breakpoints: ParsedBreakpoint[]): string {
340
+ let activeBreakpoint = 'default';
341
+
342
+ // Separate min-width and max-width breakpoints
343
+ const minWidthBreakpoints = breakpoints.filter(bp => bp.type === 'min');
344
+ const maxWidthBreakpoints = breakpoints.filter(bp => bp.type === 'max');
345
+
346
+ // For min-width breakpoints, find the largest one that matches
347
+ // (iterate in reverse order since they're sorted ascending)
348
+ for (let i = minWidthBreakpoints.length - 1; i >= 0; i--) {
349
+ const breakpoint = minWidthBreakpoints[i];
350
+ if (breakpoint && width >= breakpoint.value) {
351
+ activeBreakpoint = breakpoint.name;
352
+ break;
353
+ }
354
+ }
355
+
356
+ // For max-width breakpoints, find the first one that matches
357
+ // (iterate in normal order since they're sorted ascending)
358
+ for (const breakpoint of maxWidthBreakpoints) {
359
+ if (width <= breakpoint.value) {
360
+ return breakpoint.name; // Max-width takes precedence
361
+ }
362
+ }
363
+
364
+ return activeBreakpoint;
365
+ }
366
+
367
+ /**
368
+ * Update CSS classes on element based on current breakpoint
369
+ */
370
+ private updateBreakpointClasses(
371
+ element: Element,
372
+ currentBreakpoint: string,
373
+ breakpoints: ParsedBreakpoint[]
374
+ ): void {
375
+ // Remove all existing breakpoint classes
376
+ this.removeBreakpointClasses(element, breakpoints);
377
+
378
+ // Add current breakpoint class
379
+ element.classList.add(`proteus-${currentBreakpoint}`);
380
+ element.setAttribute('data-proteus-breakpoint', currentBreakpoint);
381
+ element.setAttribute('data-container', currentBreakpoint); // For test compatibility
382
+ }
383
+
384
+ /**
385
+ * Remove all breakpoint classes from element
386
+ */
387
+ private removeBreakpointClasses(element: Element, breakpoints: ParsedBreakpoint[]): void {
388
+ for (const breakpoint of breakpoints) {
389
+ element.classList.remove(`proteus-${breakpoint.name}`);
390
+ }
391
+ element.removeAttribute('data-proteus-breakpoint');
392
+ element.removeAttribute('data-container'); // Remove test compatibility attribute
393
+ }
394
+
395
+ /**
396
+ * Dispatch custom breakpoint change event
397
+ */
398
+ private dispatchBreakpointEvent(
399
+ element: Element,
400
+ _breakpoint: string,
401
+ data: BreakpointCallbackData
402
+ ): void {
403
+ const event = new CustomEvent('proteus:breakpoint-change', {
404
+ detail: data,
405
+ bubbles: true
406
+ });
407
+
408
+ element.dispatchEvent(event);
409
+ }
410
+
411
+ /**
412
+ * Get performance metrics
413
+ */
414
+ public getMetrics(): {
415
+ totalRegistrations: number;
416
+ activeElements: number;
417
+ averageBreakpoints: number;
418
+ breakpointDistribution: Record<string, number>;
419
+ } {
420
+ const configs = Array.from(this.breakpoints.values());
421
+ const breakpointDistribution: Record<string, number> = {};
422
+
423
+ configs.forEach(config => {
424
+ config.parsedBreakpoints.forEach(bp => {
425
+ breakpointDistribution[bp.name] = (breakpointDistribution[bp.name] || 0) + 1;
426
+ });
427
+ });
428
+
429
+ const averageBreakpoints = configs.length > 0
430
+ ? configs.reduce((sum, c) => sum + c.parsedBreakpoints.length, 0) / configs.length
431
+ : 0;
432
+
433
+ return {
434
+ totalRegistrations: configs.length,
435
+ activeElements: new Set(configs.map(c => c.element)).size,
436
+ averageBreakpoints,
437
+ breakpointDistribution
438
+ };
439
+ }
440
+
441
+ /**
442
+ * Register multiple breakpoint sets at once
443
+ */
444
+ public registerMultiple(
445
+ registrations: Array<{
446
+ element: Element;
447
+ breakpoints: BreakpointMap;
448
+ callback?: BreakpointCallback;
449
+ }>
450
+ ): string[] {
451
+ return registrations.map(reg =>
452
+ this.register(reg.element, reg.breakpoints, reg.callback)
453
+ );
454
+ }
455
+
456
+ /**
457
+ * Unregister all breakpoints for an element
458
+ */
459
+ public unregisterElement(element: Element): void {
460
+ const idsToRemove: string[] = [];
461
+
462
+ for (const [id, config] of this.breakpoints) {
463
+ if (config.element === element) {
464
+ idsToRemove.push(id);
465
+ }
466
+ }
467
+
468
+ idsToRemove.forEach(id => this.unregister(id));
469
+ }
470
+
471
+ /**
472
+ * Get all active breakpoints across all elements
473
+ */
474
+ public getAllActiveBreakpoints(): Array<{
475
+ id: string;
476
+ element: Element;
477
+ breakpoint: string;
478
+ width: number;
479
+ height: number;
480
+ }> {
481
+ const active: Array<{
482
+ id: string;
483
+ element: Element;
484
+ breakpoint: string;
485
+ width: number;
486
+ height: number;
487
+ }> = [];
488
+
489
+ for (const [id, config] of this.breakpoints) {
490
+ if (config.currentBreakpoint) {
491
+ const rect = config.element.getBoundingClientRect();
492
+ active.push({
493
+ id,
494
+ element: config.element,
495
+ breakpoint: config.currentBreakpoint,
496
+ width: rect.width,
497
+ height: rect.height
498
+ });
499
+ }
500
+ }
501
+
502
+ return active;
503
+ }
504
+
505
+ /**
506
+ * Force update all breakpoints
507
+ */
508
+ public updateAll(): void {
509
+ for (const id of this.breakpoints.keys()) {
510
+ this.updateBreakpoint(id);
511
+ }
512
+ }
513
+
514
+ /**
515
+ * Setup performance monitoring for container queries
516
+ */
517
+ private setupPerformanceMonitoring(): void {
518
+ // Monitor performance metrics
519
+ setInterval(() => {
520
+ if (this.performanceMetrics.evaluationTimes.length > 0) {
521
+ this.performanceMetrics.averageTime =
522
+ this.performanceMetrics.evaluationTimes.reduce((a, b) => a + b, 0) /
523
+ this.performanceMetrics.evaluationTimes.length;
524
+
525
+ // Log performance warnings
526
+ if (this.performanceMetrics.averageTime > 5) {
527
+ logger.warn(`Container query evaluation taking ${this.performanceMetrics.averageTime.toFixed(2)}ms on average`);
528
+ }
529
+
530
+ // Reset metrics periodically
531
+ if (this.performanceMetrics.evaluationTimes.length > 100) {
532
+ this.performanceMetrics.evaluationTimes = this.performanceMetrics.evaluationTimes.slice(-50);
533
+ }
534
+ }
535
+ }, 10000); // Check every 10 seconds
536
+ }
537
+
538
+ /**
539
+ * Inject base CSS for container query functionality
540
+ */
541
+ private injectBaseCSS(): void {
542
+ const style = document.createElement('style');
543
+ style.id = 'proteus-container-queries';
544
+ style.textContent = `
545
+ /* Base container query styles */
546
+ [data-proteus-container] {
547
+ container-type: inline-size;
548
+ position: relative;
549
+ }
550
+
551
+ /* Performance optimizations */
552
+ [data-proteus-container] * {
553
+ contain: layout style;
554
+ }
555
+
556
+ /* Responsive utility classes */
557
+ .proteus-container-small { /* Applied when container is small */ }
558
+ .proteus-container-medium { /* Applied when container is medium */ }
559
+ .proteus-container-large { /* Applied when container is large */ }
560
+
561
+ /* Transition support */
562
+ [data-proteus-container] {
563
+ transition: all 0.2s ease-out;
564
+ }
565
+
566
+ /* Debug mode styles */
567
+ [data-proteus-debug="true"] {
568
+ outline: 2px dashed rgba(255, 0, 0, 0.3);
569
+ position: relative;
570
+ }
571
+
572
+ [data-proteus-debug="true"]::before {
573
+ content: attr(data-proteus-breakpoint);
574
+ position: absolute;
575
+ top: 0;
576
+ left: 0;
577
+ background: rgba(255, 0, 0, 0.8);
578
+ color: white;
579
+ padding: 2px 6px;
580
+ font-size: 10px;
581
+ font-family: monospace;
582
+ z-index: 9999;
583
+ pointer-events: none;
584
+ }
585
+ `;
586
+
587
+ // Only inject if not already present
588
+ if (!document.getElementById('proteus-container-queries')) {
589
+ document.head.appendChild(style);
590
+ }
591
+ }
592
+
593
+ /**
594
+ * Generate responsive CSS classes for a container
595
+ */
596
+ public generateResponsiveCSS(elementId: string, breakpoints: BreakpointMap): void {
597
+ const cssRules: string[] = [];
598
+
599
+ Object.entries(breakpoints).forEach(([name, query]) => {
600
+ // Generate container query CSS
601
+ const containerQuery = this.parseContainerQuery(query);
602
+ if (containerQuery) {
603
+ cssRules.push(`
604
+ @container (${containerQuery}) {
605
+ [data-proteus-id="${elementId}"] {
606
+ --proteus-breakpoint: "${name}";
607
+ }
608
+
609
+ [data-proteus-id="${elementId}"] .proteus-${name} {
610
+ display: block;
611
+ }
612
+
613
+ [data-proteus-id="${elementId}"] .proteus-not-${name} {
614
+ display: none;
615
+ }
616
+ }
617
+ `);
618
+ }
619
+ });
620
+
621
+ // Inject CSS if we have rules
622
+ if (cssRules.length > 0) {
623
+ this.injectContainerCSS(elementId, cssRules.join('\n'));
624
+ }
625
+ }
626
+
627
+ /**
628
+ * Parse container query string
629
+ */
630
+ private parseContainerQuery(query: string): string | null {
631
+ // Convert media query syntax to container query syntax
632
+ const containerQuery = query
633
+ .replace(/max-width/g, 'max-inline-size')
634
+ .replace(/min-width/g, 'min-inline-size')
635
+ .replace(/width/g, 'inline-size')
636
+ .replace(/max-height/g, 'max-block-size')
637
+ .replace(/min-height/g, 'min-block-size')
638
+ .replace(/height/g, 'block-size');
639
+
640
+ return containerQuery;
641
+ }
642
+
643
+ /**
644
+ * Inject container-specific CSS
645
+ */
646
+ private injectContainerCSS(elementId: string, css: string): void {
647
+ const styleId = `proteus-container-${elementId}`;
648
+ let style = document.getElementById(styleId) as HTMLStyleElement;
649
+
650
+ if (!style) {
651
+ style = document.createElement('style');
652
+ style.id = styleId;
653
+ document.head.appendChild(style);
654
+ }
655
+
656
+ style.textContent = css;
657
+
658
+ // Store reference for cleanup
659
+ if (style.sheet) {
660
+ this.cssRules.set(elementId, style.sheet);
661
+ }
662
+ }
663
+
664
+ /**
665
+ * Record performance metrics
666
+ */
667
+ private recordPerformanceMetric(evaluationTime: number): void {
668
+ this.performanceMetrics.evaluationTimes.push(evaluationTime);
669
+ this.performanceMetrics.totalEvaluations++;
670
+
671
+ // Keep only recent measurements
672
+ if (this.performanceMetrics.evaluationTimes.length > 100) {
673
+ this.performanceMetrics.evaluationTimes.shift();
674
+ }
675
+ }
676
+
677
+ /**
678
+ * Get performance metrics
679
+ */
680
+ public getPerformanceMetrics(): typeof this.performanceMetrics {
681
+ return { ...this.performanceMetrics };
682
+ }
683
+
684
+ /**
685
+ * Enable debug mode for container queries
686
+ */
687
+ public enableDebugMode(enable: boolean = true): void {
688
+ this.breakpoints.forEach((config) => {
689
+ const element = config.element as HTMLElement;
690
+ if (enable) {
691
+ element.setAttribute('data-proteus-debug', 'true');
692
+ } else {
693
+ element.removeAttribute('data-proteus-debug');
694
+ }
695
+ });
696
+ }
697
+
698
+ /**
699
+ * Cleanup resources when destroying
700
+ */
701
+ public destroy(): void {
702
+ // Disconnect observer
703
+ if (this.observer) {
704
+ this.observer.disconnect();
705
+ this.observer = null;
706
+ }
707
+
708
+ // Clean up CSS rules
709
+ this.cssRules.forEach((_, elementId) => {
710
+ const styleElement = document.getElementById(`proteus-container-${elementId}`);
711
+ if (styleElement) {
712
+ styleElement.remove();
713
+ }
714
+ });
715
+
716
+ // Clear maps
717
+ this.breakpoints.clear();
718
+ this.cssRules.clear();
719
+
720
+ // Remove base CSS
721
+ const baseStyle = document.getElementById('proteus-container-queries');
722
+ if (baseStyle) {
723
+ baseStyle.remove();
724
+ }
725
+ }
726
+ }