@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,430 @@
1
+ /**
2
+ * LineHeightOptimization - Intelligent line height optimization system
3
+ * Automatically calculates optimal line heights based on font size, content, and accessibility
4
+ */
5
+
6
+ export interface LineHeightConfig {
7
+ density?: 'compact' | 'comfortable' | 'spacious';
8
+ accessibility?: 'none' | 'AA' | 'AAA';
9
+ language?: string;
10
+ contentType?: 'body' | 'heading' | 'caption' | 'code';
11
+ containerWidth?: number;
12
+ enforceAccessibility?: boolean;
13
+ respectUserPreferences?: boolean;
14
+ }
15
+
16
+ export interface OptimizationResult {
17
+ lineHeight: number;
18
+ lineHeightCSS: string;
19
+ reasoning: string[];
20
+ accessibilityCompliant: boolean;
21
+ adjustments: string[];
22
+ }
23
+
24
+ export class LineHeightOptimization {
25
+ private appliedElements: WeakSet<Element> = new WeakSet();
26
+ private resizeObserver: ResizeObserver | null = null;
27
+ private elementConfigs: WeakMap<Element, LineHeightConfig> = new WeakMap();
28
+
29
+ // Base line height ratios for different densities
30
+ private readonly densityRatios = {
31
+ compact: 1.2,
32
+ comfortable: 1.5,
33
+ spacious: 1.8
34
+ };
35
+
36
+ // Accessibility minimum line heights (WCAG guidelines)
37
+ private readonly accessibilityMinimums = {
38
+ AA: 1.5,
39
+ AAA: 1.6
40
+ };
41
+
42
+ // Language-specific adjustments
43
+ private readonly languageAdjustments = {
44
+ 'zh': 0.1, // Chinese - needs more space for characters
45
+ 'ja': 0.1, // Japanese - needs more space for characters
46
+ 'ko': 0.1, // Korean - needs more space for characters
47
+ 'ar': 0.05, // Arabic - slight adjustment for script
48
+ 'hi': 0.05, // Hindi - slight adjustment for script
49
+ 'th': 0.1, // Thai - needs more space for characters
50
+ 'default': 0
51
+ };
52
+
53
+ constructor() {
54
+ this.setupResizeObserver();
55
+ }
56
+
57
+ /**
58
+ * Optimize line height for an element
59
+ */
60
+ public optimizeLineHeight(element: Element, config: LineHeightConfig = {}): OptimizationResult {
61
+ const {
62
+ density = 'comfortable',
63
+ accessibility = 'AA',
64
+ language = 'en',
65
+ contentType = 'body',
66
+ containerWidth,
67
+ enforceAccessibility = true,
68
+ respectUserPreferences = true
69
+ } = config;
70
+
71
+ try {
72
+ // Store config for future updates
73
+ this.elementConfigs.set(element, config);
74
+
75
+ // Get current font size with robust error handling
76
+ const computedStyle = getComputedStyle(element);
77
+ let fontSize = parseFloat(computedStyle.fontSize);
78
+
79
+ // Handle NaN or invalid font size
80
+ if (!Number.isFinite(fontSize) || fontSize <= 0) {
81
+ // Try to get from inline style
82
+ const htmlElement = element as HTMLElement;
83
+ if (htmlElement.style.fontSize) {
84
+ fontSize = parseFloat(htmlElement.style.fontSize);
85
+ }
86
+
87
+ // Final fallback based on element type
88
+ if (!Number.isFinite(fontSize) || fontSize <= 0) {
89
+ switch (element.tagName.toLowerCase()) {
90
+ case 'h1': fontSize = 32; break;
91
+ case 'h2': fontSize = 24; break;
92
+ case 'h3': fontSize = 20; break;
93
+ case 'h4': fontSize = 18; break;
94
+ case 'h5': fontSize = 16; break;
95
+ case 'h6': fontSize = 14; break;
96
+ default: fontSize = 16; break;
97
+ }
98
+ }
99
+ }
100
+
101
+ const actualContainerWidth = containerWidth || element.getBoundingClientRect().width;
102
+
103
+ // Calculate optimal line height
104
+ const result = this.calculateOptimalLineHeight(
105
+ fontSize,
106
+ actualContainerWidth,
107
+ density,
108
+ accessibility,
109
+ language,
110
+ contentType,
111
+ enforceAccessibility,
112
+ respectUserPreferences
113
+ );
114
+
115
+ // Apply the line height
116
+ this.applyLineHeight(element, result.lineHeightCSS);
117
+ this.appliedElements.add(element);
118
+
119
+ // Start observing for resize if container width matters
120
+ if (this.resizeObserver && !containerWidth) {
121
+ this.resizeObserver.observe(element);
122
+ }
123
+
124
+ // Add debugging attributes
125
+ element.setAttribute('data-proteus-line-height', result.lineHeight.toString());
126
+ element.setAttribute('data-proteus-line-height-reasoning', result.reasoning.join(', '));
127
+
128
+ return result;
129
+
130
+ } catch (error) {
131
+ console.error('ProteusJS: Failed to optimize line height:', error);
132
+ return {
133
+ lineHeight: this.densityRatios[density],
134
+ lineHeightCSS: this.densityRatios[density].toString(),
135
+ reasoning: ['Error occurred, using default'],
136
+ accessibilityCompliant: false,
137
+ adjustments: ['Error fallback']
138
+ };
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Update line height when element or container changes
144
+ */
145
+ public updateLineHeight(element: Element): void {
146
+ const config = this.elementConfigs.get(element);
147
+ if (!config) return;
148
+
149
+ this.optimizeLineHeight(element, config);
150
+ }
151
+
152
+ /**
153
+ * Remove line height optimization from element
154
+ */
155
+ public removeOptimization(element: Element): void {
156
+ if (!this.appliedElements.has(element)) return;
157
+
158
+ // Remove line-height style
159
+ const style = element.getAttribute('style');
160
+ if (style) {
161
+ const newStyle = style.replace(/line-height:[^;]+;?/g, '');
162
+ if (newStyle.trim()) {
163
+ element.setAttribute('style', newStyle);
164
+ } else {
165
+ element.removeAttribute('style');
166
+ }
167
+ }
168
+
169
+ // Remove data attributes
170
+ element.removeAttribute('data-proteus-line-height');
171
+ element.removeAttribute('data-proteus-line-height-reasoning');
172
+
173
+ this.appliedElements.delete(element);
174
+ this.elementConfigs.delete(element);
175
+ }
176
+
177
+ /**
178
+ * Calculate optimal line height for an element (alias for optimizeLineHeight)
179
+ */
180
+ public calculateOptimal(element: Element, config: LineHeightConfig & { fontSize?: number } = {}): OptimizationResult | number {
181
+ // If fontSize is provided in config, return just the number for testing
182
+ if (config.fontSize !== undefined) {
183
+ const {
184
+ density = 'comfortable',
185
+ accessibility = 'AA',
186
+ language = 'en',
187
+ contentType = config.contentType || 'body', // Preserve passed contentType
188
+ containerWidth = 600,
189
+ enforceAccessibility = true,
190
+ respectUserPreferences = false
191
+ } = config;
192
+
193
+ const result = this.calculateOptimalLineHeight(
194
+ config.fontSize,
195
+ containerWidth,
196
+ density,
197
+ accessibility,
198
+ language,
199
+ contentType,
200
+ enforceAccessibility,
201
+ respectUserPreferences
202
+ );
203
+
204
+ return result.lineHeight;
205
+ }
206
+
207
+ return this.optimizeLineHeight(element, config);
208
+ }
209
+
210
+ /**
211
+ * Maintain vertical rhythm across multiple elements
212
+ */
213
+ public maintainVerticalRhythm(elements: Element[], config: { baselineGrid: number; baseSize: number }): void {
214
+ const { baselineGrid } = config;
215
+
216
+ elements.forEach(element => {
217
+ const computedStyle = getComputedStyle(element);
218
+ const fontSize = parseFloat(computedStyle.fontSize);
219
+
220
+ // Calculate line height that aligns to baseline grid
221
+ const optimalLines = Math.round(fontSize * 1.5 / baselineGrid);
222
+ const gridAlignedLineHeight = (optimalLines * baselineGrid) / fontSize;
223
+
224
+ // Apply the grid-aligned line height
225
+ const htmlElement = element as HTMLElement;
226
+ htmlElement.style.lineHeight = gridAlignedLineHeight.toString();
227
+
228
+ // Add data attribute for debugging
229
+ element.setAttribute('data-proteus-baseline-grid', baselineGrid.toString());
230
+ element.setAttribute('data-proteus-grid-lines', optimalLines.toString());
231
+ });
232
+ }
233
+
234
+ /**
235
+ * Clean up resources
236
+ */
237
+ public destroy(): void {
238
+ if (this.resizeObserver) {
239
+ this.resizeObserver.disconnect();
240
+ this.resizeObserver = null;
241
+ }
242
+
243
+ this.elementConfigs = new WeakMap();
244
+ }
245
+
246
+ /**
247
+ * Calculate optimal line height based on multiple factors
248
+ */
249
+ private calculateOptimalLineHeight(
250
+ fontSize: number,
251
+ containerWidth: number,
252
+ density: 'compact' | 'comfortable' | 'spacious',
253
+ accessibility: 'none' | 'AA' | 'AAA',
254
+ language: string,
255
+ contentType: string,
256
+ enforceAccessibility: boolean,
257
+ respectUserPreferences: boolean
258
+ ): OptimizationResult {
259
+ const reasoning: string[] = [];
260
+ const adjustments: string[] = [];
261
+
262
+ // Validate inputs
263
+ if (!Number.isFinite(fontSize) || fontSize <= 0) {
264
+ fontSize = 16; // Safe fallback
265
+ reasoning.push('Invalid fontSize, using fallback: 16px');
266
+ }
267
+
268
+ if (!Number.isFinite(containerWidth) || containerWidth <= 0) {
269
+ containerWidth = 600; // Safe fallback
270
+ reasoning.push('Invalid containerWidth, using fallback: 600px');
271
+ }
272
+
273
+ // Start with base density ratio
274
+ let lineHeight = this.densityRatios[density];
275
+ reasoning.push(`Base ${density} density: ${lineHeight}`);
276
+
277
+ // Adjust for content type
278
+ const contentAdjustment = this.getContentTypeAdjustment(contentType);
279
+ lineHeight += contentAdjustment;
280
+ if (contentAdjustment !== 0) {
281
+ adjustments.push(`Content type (${contentType}): ${contentAdjustment > 0 ? '+' : ''}${contentAdjustment}`);
282
+ }
283
+
284
+ // Adjust for language
285
+ const langCode = language.toLowerCase().split('-')[0];
286
+ const languageAdjustment = this.languageAdjustments[langCode as keyof typeof this.languageAdjustments] || this.languageAdjustments.default;
287
+ lineHeight += languageAdjustment;
288
+ if (languageAdjustment !== 0) {
289
+ adjustments.push(`Language (${langCode}): +${languageAdjustment}`);
290
+ }
291
+
292
+ // Adjust for line length (characters per line)
293
+ const lineLength = this.estimateLineLength(fontSize, containerWidth);
294
+ const lineLengthAdjustment = this.getLineLengthAdjustment(lineLength);
295
+ lineHeight += lineLengthAdjustment;
296
+ if (lineLengthAdjustment !== 0) {
297
+ adjustments.push(`Line length (${lineLength} chars): ${lineLengthAdjustment > 0 ? '+' : ''}${lineLengthAdjustment}`);
298
+ }
299
+
300
+ // Adjust for font size
301
+ const fontSizeAdjustment = this.getFontSizeAdjustment(fontSize);
302
+ lineHeight += fontSizeAdjustment;
303
+ if (fontSizeAdjustment !== 0) {
304
+ adjustments.push(`Font size (${fontSize}px): ${fontSizeAdjustment > 0 ? '+' : ''}${fontSizeAdjustment}`);
305
+ }
306
+
307
+ // Apply user preferences
308
+ if (respectUserPreferences) {
309
+ const userAdjustment = this.getUserPreferenceAdjustment();
310
+ lineHeight *= userAdjustment;
311
+ if (userAdjustment !== 1) {
312
+ adjustments.push(`User preferences: ×${userAdjustment}`);
313
+ }
314
+ }
315
+
316
+ // Enforce accessibility minimums
317
+ let accessibilityCompliant = true;
318
+ if (accessibility !== 'none' && enforceAccessibility) {
319
+ const minimum = this.accessibilityMinimums[accessibility];
320
+ if (lineHeight < minimum) {
321
+ lineHeight = minimum;
322
+ accessibilityCompliant = false;
323
+ adjustments.push(`Accessibility (${accessibility}): enforced minimum ${minimum}`);
324
+ reasoning.push(`Enforced ${accessibility} minimum: ${minimum}`);
325
+ } else {
326
+ reasoning.push(`${accessibility} compliant: ${lineHeight} >= ${minimum}`);
327
+ }
328
+ }
329
+
330
+ // Round to reasonable precision
331
+ lineHeight = Math.round(lineHeight * 1000) / 1000;
332
+
333
+ return {
334
+ lineHeight,
335
+ lineHeightCSS: lineHeight.toString(),
336
+ reasoning,
337
+ accessibilityCompliant,
338
+ adjustments
339
+ };
340
+ }
341
+
342
+ /**
343
+ * Get adjustment based on content type
344
+ */
345
+ private getContentTypeAdjustment(contentType: string): number {
346
+ const adjustments: Record<string, number> = {
347
+ body: 0,
348
+ 'body-text': 0, // Alias for body
349
+ heading: -0.1, // Headings can be tighter
350
+ caption: -0.05, // Captions slightly tighter
351
+ code: 0.1 // Code needs more space
352
+ };
353
+
354
+ return adjustments[contentType] ?? 0; // Default to 0 for unknown types
355
+ }
356
+
357
+ /**
358
+ * Estimate characters per line based on font size and container width
359
+ */
360
+ private estimateLineLength(fontSize: number, containerWidth: number): number {
361
+ // Rough estimate: average character width is about 0.6 * font size
362
+ const avgCharWidth = fontSize * 0.6;
363
+ return Math.floor(containerWidth / avgCharWidth);
364
+ }
365
+
366
+ /**
367
+ * Get adjustment based on line length (optimal is 45-75 characters)
368
+ */
369
+ private getLineLengthAdjustment(lineLength: number): number {
370
+ if (lineLength < 30) return 0.1; // Very short lines need more space
371
+ if (lineLength < 45) return 0.05; // Short lines need slightly more space
372
+ if (lineLength <= 75) return 0; // Optimal range
373
+ if (lineLength <= 100) return 0.05; // Long lines need more space
374
+ return 0.1; // Very long lines need much more space
375
+ }
376
+
377
+ /**
378
+ * Get adjustment based on font size
379
+ */
380
+ private getFontSizeAdjustment(fontSize: number): number {
381
+ if (fontSize < 12) return 0.1; // Very small text needs more space
382
+ if (fontSize < 14) return 0.05; // Small text needs slightly more space
383
+ if (fontSize <= 18) return 0; // Normal range
384
+ if (fontSize <= 24) return -0.05; // Large text can be tighter
385
+ return -0.1; // Very large text can be much tighter
386
+ }
387
+
388
+ /**
389
+ * Get user preference adjustment from system settings
390
+ */
391
+ private getUserPreferenceAdjustment(): number {
392
+ // Check for user's preferred line spacing
393
+ // This could be expanded to read from CSS custom properties or localStorage
394
+ const rootStyle = getComputedStyle(document.documentElement);
395
+ const userLineSpacing = rootStyle.getPropertyValue('--user-line-spacing');
396
+
397
+ if (userLineSpacing) {
398
+ const adjustment = parseFloat(userLineSpacing);
399
+ return isNaN(adjustment) ? 1 : Math.max(0.8, Math.min(2, adjustment));
400
+ }
401
+
402
+ return 1; // No adjustment
403
+ }
404
+
405
+ /**
406
+ * Apply line height to element
407
+ */
408
+ private applyLineHeight(element: Element, lineHeight: string): void {
409
+ const htmlElement = element as HTMLElement;
410
+ htmlElement.style.lineHeight = lineHeight;
411
+ }
412
+
413
+ /**
414
+ * Setup ResizeObserver for responsive line height adjustments
415
+ */
416
+ private setupResizeObserver(): void {
417
+ if (typeof ResizeObserver === 'undefined') {
418
+ console.warn('ProteusJS: ResizeObserver not supported. Responsive line height may not work correctly.');
419
+ return;
420
+ }
421
+
422
+ this.resizeObserver = new ResizeObserver((entries) => {
423
+ for (const entry of entries) {
424
+ if (this.appliedElements.has(entry.target)) {
425
+ this.updateLineHeight(entry.target);
426
+ }
427
+ }
428
+ });
429
+ }
430
+ }