@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.
- package/API.md +438 -0
- package/FEATURES.md +286 -0
- package/LICENSE +21 -0
- package/README.md +645 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/proteus.cjs.js +16014 -0
- package/dist/proteus.cjs.js.map +1 -0
- package/dist/proteus.d.ts +3018 -0
- package/dist/proteus.esm.js +16005 -0
- package/dist/proteus.esm.js.map +1 -0
- package/dist/proteus.esm.min.js +8 -0
- package/dist/proteus.esm.min.js.map +1 -0
- package/dist/proteus.js +16020 -0
- package/dist/proteus.js.map +1 -0
- package/dist/proteus.min.js +8 -0
- package/dist/proteus.min.js.map +1 -0
- package/package.json +98 -0
- package/src/__tests__/mvp-integration.test.ts +518 -0
- package/src/accessibility/AccessibilityEngine.ts +2106 -0
- package/src/accessibility/ScreenReaderSupport.ts +444 -0
- package/src/accessibility/__tests__/ScreenReaderSupport.test.ts +435 -0
- package/src/animations/FLIPAnimationSystem.ts +491 -0
- package/src/compatibility/BrowserCompatibility.ts +1076 -0
- package/src/containers/BreakpointSystem.ts +347 -0
- package/src/containers/ContainerBreakpoints.ts +726 -0
- package/src/containers/ContainerManager.ts +370 -0
- package/src/containers/ContainerUnits.ts +336 -0
- package/src/containers/ContextIsolation.ts +394 -0
- package/src/containers/ElementQueries.ts +411 -0
- package/src/containers/SmartContainer.ts +536 -0
- package/src/containers/SmartContainers.ts +376 -0
- package/src/containers/__tests__/ContainerBreakpoints.test.ts +411 -0
- package/src/containers/__tests__/SmartContainers.test.ts +281 -0
- package/src/content/ResponsiveImages.ts +570 -0
- package/src/core/EventSystem.ts +147 -0
- package/src/core/MemoryManager.ts +321 -0
- package/src/core/PerformanceMonitor.ts +238 -0
- package/src/core/PluginSystem.ts +275 -0
- package/src/core/ProteusJS.test.ts +164 -0
- package/src/core/ProteusJS.ts +962 -0
- package/src/developer/PerformanceProfiler.ts +567 -0
- package/src/developer/VisualDebuggingTools.ts +656 -0
- package/src/developer/ZeroConfigSystem.ts +593 -0
- package/src/index.ts +35 -0
- package/src/integration.test.ts +227 -0
- package/src/layout/AdaptiveGrid.ts +429 -0
- package/src/layout/ContentReordering.ts +532 -0
- package/src/layout/FlexboxEnhancer.ts +406 -0
- package/src/layout/FlowLayout.ts +545 -0
- package/src/layout/SpacingSystem.ts +512 -0
- package/src/observers/IntersectionObserverPolyfill.ts +289 -0
- package/src/observers/ObserverManager.ts +299 -0
- package/src/observers/ResizeObserverPolyfill.ts +179 -0
- package/src/performance/BatchDOMOperations.ts +519 -0
- package/src/performance/CSSOptimizationEngine.ts +646 -0
- package/src/performance/CacheOptimizationSystem.ts +601 -0
- package/src/performance/EfficientEventHandler.ts +740 -0
- package/src/performance/LazyEvaluationSystem.ts +532 -0
- package/src/performance/MemoryManagementSystem.ts +497 -0
- package/src/performance/PerformanceMonitor.ts +931 -0
- package/src/performance/__tests__/BatchDOMOperations.test.ts +309 -0
- package/src/performance/__tests__/EfficientEventHandler.test.ts +268 -0
- package/src/performance/__tests__/PerformanceMonitor.test.ts +422 -0
- package/src/polyfills/BrowserPolyfills.ts +586 -0
- package/src/polyfills/__tests__/BrowserPolyfills.test.ts +328 -0
- package/src/test/setup.ts +115 -0
- package/src/theming/SmartThemeSystem.ts +591 -0
- package/src/types/index.ts +134 -0
- package/src/typography/ClampScaling.ts +356 -0
- package/src/typography/FluidTypography.ts +759 -0
- package/src/typography/LineHeightOptimization.ts +430 -0
- package/src/typography/LineHeightOptimizer.ts +326 -0
- package/src/typography/TextFitting.ts +355 -0
- package/src/typography/TypographicScale.ts +428 -0
- package/src/typography/VerticalRhythm.ts +369 -0
- package/src/typography/__tests__/FluidTypography.test.ts +432 -0
- package/src/typography/__tests__/LineHeightOptimization.test.ts +436 -0
- package/src/utils/Logger.ts +173 -0
- package/src/utils/debounce.ts +259 -0
- package/src/utils/performance.ts +371 -0
- package/src/utils/support.ts +106 -0
- package/src/utils/version.ts +24 -0
@@ -0,0 +1,376 @@
|
|
1
|
+
/**
|
2
|
+
* SmartContainers - Automatic container detection and management system
|
3
|
+
* Core component of ProteusJS container query system
|
4
|
+
*/
|
5
|
+
|
6
|
+
import { logger } from '../utils/Logger';
|
7
|
+
|
8
|
+
export interface ContainerInfo {
|
9
|
+
element: Element;
|
10
|
+
type: 'block' | 'inline' | 'grid' | 'flex';
|
11
|
+
confidence: number;
|
12
|
+
priority: 'low' | 'medium' | 'high';
|
13
|
+
dimensions: {
|
14
|
+
width: number;
|
15
|
+
height: number;
|
16
|
+
aspectRatio: number;
|
17
|
+
};
|
18
|
+
children: number;
|
19
|
+
hasResponsiveContent: boolean;
|
20
|
+
}
|
21
|
+
|
22
|
+
export interface ContainerDetectionOptions {
|
23
|
+
includeInline?: boolean;
|
24
|
+
minConfidence?: number;
|
25
|
+
excludeSelectors?: string[];
|
26
|
+
includeSelectors?: string[];
|
27
|
+
}
|
28
|
+
|
29
|
+
export class SmartContainers {
|
30
|
+
private containers: Map<Element, ContainerInfo> = new Map();
|
31
|
+
private observer: ResizeObserver | null = null;
|
32
|
+
private mutationObserver: MutationObserver | null = null;
|
33
|
+
private isActive: boolean = false;
|
34
|
+
|
35
|
+
constructor() {
|
36
|
+
this.setupObservers();
|
37
|
+
}
|
38
|
+
|
39
|
+
/**
|
40
|
+
* Automatically detect containers in the document
|
41
|
+
*/
|
42
|
+
public async detectContainers(options: ContainerDetectionOptions = {}): Promise<ContainerInfo[]> {
|
43
|
+
const {
|
44
|
+
includeInline = false,
|
45
|
+
minConfidence = 0.5,
|
46
|
+
excludeSelectors = ['script', 'style', 'meta', 'link'],
|
47
|
+
includeSelectors = []
|
48
|
+
} = options;
|
49
|
+
|
50
|
+
const containers: ContainerInfo[] = [];
|
51
|
+
const elements = this.getAllElements(includeSelectors, excludeSelectors);
|
52
|
+
|
53
|
+
for (const element of elements) {
|
54
|
+
const containerInfo = this.analyzeElement(element, includeInline);
|
55
|
+
|
56
|
+
if (containerInfo && containerInfo.confidence >= minConfidence) {
|
57
|
+
containers.push(containerInfo);
|
58
|
+
this.containers.set(element, containerInfo);
|
59
|
+
|
60
|
+
// Start observing this container
|
61
|
+
if (this.observer) {
|
62
|
+
this.observer.observe(element);
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
// Sort by priority and confidence
|
68
|
+
containers.sort((a, b) => {
|
69
|
+
const priorityWeight = { high: 3, medium: 2, low: 1 };
|
70
|
+
const aPriority = priorityWeight[a.priority];
|
71
|
+
const bPriority = priorityWeight[b.priority];
|
72
|
+
|
73
|
+
if (aPriority !== bPriority) {
|
74
|
+
return bPriority - aPriority;
|
75
|
+
}
|
76
|
+
|
77
|
+
return b.confidence - a.confidence;
|
78
|
+
});
|
79
|
+
|
80
|
+
return containers;
|
81
|
+
}
|
82
|
+
|
83
|
+
/**
|
84
|
+
* Calculate container units (cw, ch, cmin, cmax)
|
85
|
+
*/
|
86
|
+
public calculateContainerUnit(element: Element, unit: string): number {
|
87
|
+
const rect = element.getBoundingClientRect();
|
88
|
+
const { width, height } = rect;
|
89
|
+
|
90
|
+
switch (unit) {
|
91
|
+
case 'cw': // Container width unit (1cw = 1% of container width)
|
92
|
+
return width / 100;
|
93
|
+
|
94
|
+
case 'ch': // Container height unit (1ch = 1% of container height)
|
95
|
+
return height / 100;
|
96
|
+
|
97
|
+
case 'cmin': // Container minimum unit (1cmin = 1% of smaller dimension)
|
98
|
+
return Math.min(width, height) / 100;
|
99
|
+
|
100
|
+
case 'cmax': // Container maximum unit (1cmax = 1% of larger dimension)
|
101
|
+
return Math.max(width, height) / 100;
|
102
|
+
|
103
|
+
case 'cqi': // Container inline size (same as cw for horizontal writing)
|
104
|
+
return width / 100;
|
105
|
+
|
106
|
+
case 'cqb': // Container block size (same as ch for horizontal writing)
|
107
|
+
return height / 100;
|
108
|
+
|
109
|
+
default:
|
110
|
+
logger.warn(`Unknown container unit "${unit}"`);
|
111
|
+
return 0;
|
112
|
+
}
|
113
|
+
}
|
114
|
+
|
115
|
+
/**
|
116
|
+
* Get container information for a specific element
|
117
|
+
*/
|
118
|
+
public getContainerInfo(element: Element): ContainerInfo | null {
|
119
|
+
return this.containers.get(element) || null;
|
120
|
+
}
|
121
|
+
|
122
|
+
/**
|
123
|
+
* Update container information when dimensions change
|
124
|
+
*/
|
125
|
+
public updateContainer(element: Element): ContainerInfo | null {
|
126
|
+
const existingInfo = this.containers.get(element);
|
127
|
+
if (!existingInfo) return null;
|
128
|
+
|
129
|
+
const updatedInfo = this.analyzeElement(element, true);
|
130
|
+
if (updatedInfo) {
|
131
|
+
this.containers.set(element, updatedInfo);
|
132
|
+
return updatedInfo;
|
133
|
+
}
|
134
|
+
|
135
|
+
return existingInfo;
|
136
|
+
}
|
137
|
+
|
138
|
+
/**
|
139
|
+
* Start monitoring containers for changes
|
140
|
+
*/
|
141
|
+
public startMonitoring(): void {
|
142
|
+
this.isActive = true;
|
143
|
+
|
144
|
+
if (this.observer) {
|
145
|
+
// Re-observe all registered containers
|
146
|
+
this.containers.forEach((_, element) => {
|
147
|
+
this.observer!.observe(element);
|
148
|
+
});
|
149
|
+
}
|
150
|
+
}
|
151
|
+
|
152
|
+
/**
|
153
|
+
* Stop monitoring containers
|
154
|
+
*/
|
155
|
+
public stopMonitoring(): void {
|
156
|
+
this.isActive = false;
|
157
|
+
|
158
|
+
if (this.observer) {
|
159
|
+
this.observer.disconnect();
|
160
|
+
}
|
161
|
+
}
|
162
|
+
|
163
|
+
/**
|
164
|
+
* Clean up resources
|
165
|
+
*/
|
166
|
+
public destroy(): void {
|
167
|
+
this.stopMonitoring();
|
168
|
+
|
169
|
+
if (this.mutationObserver) {
|
170
|
+
this.mutationObserver.disconnect();
|
171
|
+
}
|
172
|
+
|
173
|
+
this.containers.clear();
|
174
|
+
}
|
175
|
+
|
176
|
+
/**
|
177
|
+
* Setup ResizeObserver and MutationObserver
|
178
|
+
*/
|
179
|
+
private setupObservers(): void {
|
180
|
+
// Setup ResizeObserver for container dimension changes
|
181
|
+
if (typeof ResizeObserver !== 'undefined') {
|
182
|
+
this.observer = new ResizeObserver((entries) => {
|
183
|
+
if (!this.isActive) return;
|
184
|
+
|
185
|
+
for (const entry of entries) {
|
186
|
+
this.updateContainer(entry.target);
|
187
|
+
}
|
188
|
+
});
|
189
|
+
}
|
190
|
+
|
191
|
+
// Setup MutationObserver for DOM changes
|
192
|
+
if (typeof MutationObserver !== 'undefined') {
|
193
|
+
this.mutationObserver = new MutationObserver((mutations) => {
|
194
|
+
if (!this.isActive) return;
|
195
|
+
|
196
|
+
for (const mutation of mutations) {
|
197
|
+
if (mutation.type === 'childList') {
|
198
|
+
// Re-analyze containers when children change
|
199
|
+
mutation.target.parentElement && this.updateContainer(mutation.target.parentElement);
|
200
|
+
}
|
201
|
+
}
|
202
|
+
});
|
203
|
+
|
204
|
+
this.mutationObserver.observe(document.body, {
|
205
|
+
childList: true,
|
206
|
+
subtree: true
|
207
|
+
});
|
208
|
+
}
|
209
|
+
}
|
210
|
+
|
211
|
+
/**
|
212
|
+
* Get all elements to analyze
|
213
|
+
*/
|
214
|
+
private getAllElements(includeSelectors: string[], excludeSelectors: string[]): Element[] {
|
215
|
+
let elements: Element[];
|
216
|
+
|
217
|
+
if (includeSelectors.length > 0) {
|
218
|
+
elements = Array.from(document.querySelectorAll(includeSelectors.join(', ')));
|
219
|
+
} else {
|
220
|
+
elements = Array.from(document.querySelectorAll('*'));
|
221
|
+
}
|
222
|
+
|
223
|
+
// Filter out excluded elements
|
224
|
+
if (excludeSelectors.length > 0) {
|
225
|
+
const excludeSelector = excludeSelectors.join(', ');
|
226
|
+
elements = elements.filter(el => !el.matches(excludeSelector));
|
227
|
+
}
|
228
|
+
|
229
|
+
return elements;
|
230
|
+
}
|
231
|
+
|
232
|
+
/**
|
233
|
+
* Analyze an element to determine if it's a good container candidate
|
234
|
+
*/
|
235
|
+
private analyzeElement(element: Element, includeInline: boolean): ContainerInfo | null {
|
236
|
+
const rect = element.getBoundingClientRect();
|
237
|
+
const computedStyle = window.getComputedStyle(element);
|
238
|
+
const children = element.children.length;
|
239
|
+
|
240
|
+
// Skip elements that are too small or invisible
|
241
|
+
if (rect.width < 50 || rect.height < 50 || computedStyle.display === 'none') {
|
242
|
+
return null;
|
243
|
+
}
|
244
|
+
|
245
|
+
// Determine container type
|
246
|
+
const display = computedStyle.display;
|
247
|
+
let type: ContainerInfo['type'];
|
248
|
+
let confidence = 0;
|
249
|
+
|
250
|
+
if (display.includes('grid')) {
|
251
|
+
type = 'grid';
|
252
|
+
confidence += 0.4;
|
253
|
+
} else if (display.includes('flex')) {
|
254
|
+
type = 'flex';
|
255
|
+
confidence += 0.3;
|
256
|
+
} else if (display === 'block' || display === 'flow-root') {
|
257
|
+
type = 'block';
|
258
|
+
confidence += 0.2;
|
259
|
+
} else if (includeInline && (display === 'inline-block' || display === 'inline')) {
|
260
|
+
type = 'inline';
|
261
|
+
confidence += 0.1;
|
262
|
+
} else {
|
263
|
+
return null;
|
264
|
+
}
|
265
|
+
|
266
|
+
// Increase confidence based on various factors
|
267
|
+
if (children > 0) confidence += 0.2;
|
268
|
+
if (children > 3) confidence += 0.1;
|
269
|
+
if (rect.width > 200) confidence += 0.1;
|
270
|
+
if (rect.height > 200) confidence += 0.1;
|
271
|
+
if (element.classList.length > 0) confidence += 0.1;
|
272
|
+
if (element.id) confidence += 0.1;
|
273
|
+
|
274
|
+
// Check for responsive content indicators
|
275
|
+
const hasResponsiveContent = this.hasResponsiveContent(element);
|
276
|
+
if (hasResponsiveContent) confidence += 0.2;
|
277
|
+
|
278
|
+
// Determine priority
|
279
|
+
let priority: ContainerInfo['priority'] = 'low';
|
280
|
+
if (element.matches('main, article, section, aside, nav') ||
|
281
|
+
element.getAttribute('role') === 'main' ||
|
282
|
+
element.getAttribute('role') === 'banner' ||
|
283
|
+
element.getAttribute('role') === 'navigation' ||
|
284
|
+
element.getAttribute('role') === 'contentinfo') {
|
285
|
+
priority = 'high';
|
286
|
+
} else if (element.matches('div, header, footer, form') ||
|
287
|
+
element.getAttribute('role') === 'complementary') {
|
288
|
+
priority = 'medium';
|
289
|
+
}
|
290
|
+
|
291
|
+
return {
|
292
|
+
element,
|
293
|
+
type,
|
294
|
+
confidence: Math.min(confidence, 1),
|
295
|
+
priority,
|
296
|
+
dimensions: {
|
297
|
+
width: rect.width,
|
298
|
+
height: rect.height,
|
299
|
+
aspectRatio: rect.width / rect.height
|
300
|
+
},
|
301
|
+
children,
|
302
|
+
hasResponsiveContent
|
303
|
+
};
|
304
|
+
}
|
305
|
+
|
306
|
+
/**
|
307
|
+
* Check if element contains responsive content
|
308
|
+
*/
|
309
|
+
private hasResponsiveContent(element: Element): boolean {
|
310
|
+
// Check for responsive images
|
311
|
+
const images = element.querySelectorAll('img[srcset], picture, img[sizes]');
|
312
|
+
if (images.length > 0) return true;
|
313
|
+
|
314
|
+
// Check for responsive videos
|
315
|
+
const videos = element.querySelectorAll('video, iframe[src*="youtube"], iframe[src*="vimeo"]');
|
316
|
+
if (videos.length > 0) return true;
|
317
|
+
|
318
|
+
// Check for CSS Grid or Flexbox children
|
319
|
+
const gridFlexChildren = Array.from(element.children).some(child => {
|
320
|
+
const style = window.getComputedStyle(child);
|
321
|
+
return style.display.includes('grid') || style.display.includes('flex');
|
322
|
+
});
|
323
|
+
|
324
|
+
return gridFlexChildren;
|
325
|
+
}
|
326
|
+
|
327
|
+
/**
|
328
|
+
* Get container performance metrics
|
329
|
+
*/
|
330
|
+
public getMetrics(): {
|
331
|
+
totalContainers: number;
|
332
|
+
activeContainers: number;
|
333
|
+
averageConfidence: number;
|
334
|
+
typeDistribution: Record<string, number>;
|
335
|
+
} {
|
336
|
+
const containers = Array.from(this.containers.values());
|
337
|
+
const typeDistribution: Record<string, number> = {};
|
338
|
+
|
339
|
+
containers.forEach(container => {
|
340
|
+
typeDistribution[container.type] = (typeDistribution[container.type] || 0) + 1;
|
341
|
+
});
|
342
|
+
|
343
|
+
const averageConfidence = containers.length > 0
|
344
|
+
? containers.reduce((sum, c) => sum + c.confidence, 0) / containers.length
|
345
|
+
: 0;
|
346
|
+
|
347
|
+
return {
|
348
|
+
totalContainers: containers.length,
|
349
|
+
activeContainers: this.isActive ? containers.length : 0,
|
350
|
+
averageConfidence,
|
351
|
+
typeDistribution
|
352
|
+
};
|
353
|
+
}
|
354
|
+
|
355
|
+
/**
|
356
|
+
* Force re-analysis of all containers
|
357
|
+
*/
|
358
|
+
public refreshContainers(): Promise<ContainerInfo[]> {
|
359
|
+
this.containers.clear();
|
360
|
+
return this.detectContainers();
|
361
|
+
}
|
362
|
+
|
363
|
+
/**
|
364
|
+
* Get containers by type
|
365
|
+
*/
|
366
|
+
public getContainersByType(type: ContainerInfo['type']): ContainerInfo[] {
|
367
|
+
return Array.from(this.containers.values()).filter(c => c.type === type);
|
368
|
+
}
|
369
|
+
|
370
|
+
/**
|
371
|
+
* Get high-confidence containers
|
372
|
+
*/
|
373
|
+
public getHighConfidenceContainers(minConfidence: number = 0.7): ContainerInfo[] {
|
374
|
+
return Array.from(this.containers.values()).filter(c => c.confidence >= minConfidence);
|
375
|
+
}
|
376
|
+
}
|