mintwaterfall 0.8.6
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/CHANGELOG.md +223 -0
- package/CONTRIBUTING.md +199 -0
- package/README.md +363 -0
- package/dist/index.d.ts +149 -0
- package/dist/mintwaterfall.cjs.js +7978 -0
- package/dist/mintwaterfall.esm.js +7907 -0
- package/dist/mintwaterfall.min.js +7 -0
- package/dist/mintwaterfall.umd.js +7978 -0
- package/index.d.ts +149 -0
- package/package.json +126 -0
- package/src/enterprise/enterprise-core.js +0 -0
- package/src/enterprise/enterprise-feature-template.js +0 -0
- package/src/enterprise/feature-registry.js +0 -0
- package/src/enterprise/features/breakdown.js +0 -0
- package/src/features/breakdown.js +0 -0
- package/src/features/conditional-formatting.js +0 -0
- package/src/index.js +111 -0
- package/src/mintwaterfall-accessibility.ts +680 -0
- package/src/mintwaterfall-advanced-data.ts +1034 -0
- package/src/mintwaterfall-advanced-interactions.ts +649 -0
- package/src/mintwaterfall-advanced-performance.ts +582 -0
- package/src/mintwaterfall-animations.ts +595 -0
- package/src/mintwaterfall-brush.ts +471 -0
- package/src/mintwaterfall-chart-core.ts +296 -0
- package/src/mintwaterfall-chart.ts +1915 -0
- package/src/mintwaterfall-data.ts +1100 -0
- package/src/mintwaterfall-export.ts +475 -0
- package/src/mintwaterfall-hierarchical-layouts.ts +724 -0
- package/src/mintwaterfall-layouts.ts +647 -0
- package/src/mintwaterfall-performance.ts +573 -0
- package/src/mintwaterfall-scales.ts +437 -0
- package/src/mintwaterfall-shapes.ts +385 -0
- package/src/mintwaterfall-statistics.ts +821 -0
- package/src/mintwaterfall-themes.ts +391 -0
- package/src/mintwaterfall-tooltip.ts +450 -0
- package/src/mintwaterfall-zoom.ts +399 -0
- package/src/types/js-modules.d.ts +25 -0
- package/src/utils/compatibility-layer.js +0 -0
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
// MintWaterfall Performance System - TypeScript Version
|
|
2
|
+
// Implements virtualization, incremental updates, and memory optimization with full type safety
|
|
3
|
+
|
|
4
|
+
import * as d3 from 'd3';
|
|
5
|
+
|
|
6
|
+
// Type definitions for performance system
|
|
7
|
+
export interface PerformanceMetrics {
|
|
8
|
+
renderTime: number;
|
|
9
|
+
dataProcessingTime: number;
|
|
10
|
+
memoryUsage: number;
|
|
11
|
+
visibleElements: number;
|
|
12
|
+
totalElements: number;
|
|
13
|
+
fps: number;
|
|
14
|
+
lastFrameTime: number;
|
|
15
|
+
averageFrameTime: number;
|
|
16
|
+
peakMemoryUsage: number;
|
|
17
|
+
renderCalls: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface VirtualizationConfig {
|
|
21
|
+
enabled: boolean;
|
|
22
|
+
chunkSize: number;
|
|
23
|
+
renderThreshold: number;
|
|
24
|
+
bufferSize: number;
|
|
25
|
+
preloadCount: number;
|
|
26
|
+
recycleNodes: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface VirtualViewport {
|
|
30
|
+
start: number;
|
|
31
|
+
end: number;
|
|
32
|
+
visible: number[];
|
|
33
|
+
bufferStart: number;
|
|
34
|
+
bufferEnd: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface PerformanceOptimization {
|
|
38
|
+
debouncing: boolean;
|
|
39
|
+
throttling: boolean;
|
|
40
|
+
batchUpdates: boolean;
|
|
41
|
+
memoryPooling: boolean;
|
|
42
|
+
geometryOptimization: boolean;
|
|
43
|
+
cssOptimization: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface MemoryPool {
|
|
47
|
+
elements: Map<string, any[]>;
|
|
48
|
+
maxSize: number;
|
|
49
|
+
currentSize: number;
|
|
50
|
+
hitCount: number;
|
|
51
|
+
missCount: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface RenderBatch {
|
|
55
|
+
operations: RenderOperation[];
|
|
56
|
+
priority: number;
|
|
57
|
+
timestamp: number;
|
|
58
|
+
elementCount: number;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface RenderOperation {
|
|
62
|
+
type: 'create' | 'update' | 'remove' | 'style' | 'attribute';
|
|
63
|
+
element: any;
|
|
64
|
+
data: any;
|
|
65
|
+
properties: Record<string, any>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface PerformanceProfiler {
|
|
69
|
+
startTime: number;
|
|
70
|
+
endTime: number;
|
|
71
|
+
samples: number[];
|
|
72
|
+
averageTime: number;
|
|
73
|
+
minTime: number;
|
|
74
|
+
maxTime: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface PerformanceManager {
|
|
78
|
+
enableVirtualization(options?: Partial<VirtualizationConfig>): PerformanceManager;
|
|
79
|
+
disableVirtualization(): PerformanceManager;
|
|
80
|
+
optimizeRendering(container: d3.Selection<d3.BaseType, any, any, any>, data: any[]): PerformanceManager;
|
|
81
|
+
profileOperation(name: string, operation: () => any): any;
|
|
82
|
+
getMetrics(): PerformanceMetrics;
|
|
83
|
+
resetMetrics(): PerformanceManager;
|
|
84
|
+
enableMemoryPooling(options?: Partial<MemoryPool>): PerformanceManager;
|
|
85
|
+
createRenderBatch(): RenderBatch;
|
|
86
|
+
flushRenderBatch(batch: RenderBatch): PerformanceManager;
|
|
87
|
+
setUpdateStrategy(strategy: 'immediate' | 'debounced' | 'throttled' | 'batched'): PerformanceManager;
|
|
88
|
+
getDashboard(): HTMLElement | null;
|
|
89
|
+
enableDashboard(container?: HTMLElement): PerformanceManager;
|
|
90
|
+
disableDashboard(): PerformanceManager;
|
|
91
|
+
optimizeMemory(): PerformanceManager;
|
|
92
|
+
getRecommendations(): string[];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function createPerformanceManager(): PerformanceManager {
|
|
96
|
+
|
|
97
|
+
// Performance metrics tracking
|
|
98
|
+
let performanceMetrics: PerformanceMetrics = {
|
|
99
|
+
renderTime: 0,
|
|
100
|
+
dataProcessingTime: 0,
|
|
101
|
+
memoryUsage: 0,
|
|
102
|
+
visibleElements: 0,
|
|
103
|
+
totalElements: 0,
|
|
104
|
+
fps: 0,
|
|
105
|
+
lastFrameTime: 0,
|
|
106
|
+
averageFrameTime: 0,
|
|
107
|
+
peakMemoryUsage: 0,
|
|
108
|
+
renderCalls: 0
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Virtualization configuration
|
|
112
|
+
let virtualizationConfig: VirtualizationConfig = {
|
|
113
|
+
enabled: false,
|
|
114
|
+
chunkSize: 1000,
|
|
115
|
+
renderThreshold: 10000,
|
|
116
|
+
bufferSize: 200,
|
|
117
|
+
preloadCount: 3,
|
|
118
|
+
recycleNodes: true
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
let virtualViewport: VirtualViewport = {
|
|
122
|
+
start: 0,
|
|
123
|
+
end: 100,
|
|
124
|
+
visible: [],
|
|
125
|
+
bufferStart: 0,
|
|
126
|
+
bufferEnd: 100
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Performance optimization settings
|
|
130
|
+
let optimizationConfig: PerformanceOptimization = {
|
|
131
|
+
debouncing: true,
|
|
132
|
+
throttling: true,
|
|
133
|
+
batchUpdates: true,
|
|
134
|
+
memoryPooling: true,
|
|
135
|
+
geometryOptimization: true,
|
|
136
|
+
cssOptimization: true
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// Memory pool for reusing DOM elements
|
|
140
|
+
let memoryPool: MemoryPool = {
|
|
141
|
+
elements: new Map(),
|
|
142
|
+
maxSize: 1000,
|
|
143
|
+
currentSize: 0,
|
|
144
|
+
hitCount: 0,
|
|
145
|
+
missCount: 0
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// Render batching system
|
|
149
|
+
let currentBatch: RenderBatch | null = null;
|
|
150
|
+
let batchTimeout: number | null = null;
|
|
151
|
+
let updateStrategy: 'immediate' | 'debounced' | 'throttled' | 'batched' = 'immediate';
|
|
152
|
+
|
|
153
|
+
// Performance profiling
|
|
154
|
+
let profilers = new Map<string, PerformanceProfiler>();
|
|
155
|
+
let dashboardElement: HTMLElement | null = null;
|
|
156
|
+
|
|
157
|
+
// Frame rate tracking
|
|
158
|
+
let frameCount = 0;
|
|
159
|
+
let lastFpsTime = performance.now();
|
|
160
|
+
|
|
161
|
+
function enableVirtualization(options: Partial<VirtualizationConfig> = {}): PerformanceManager {
|
|
162
|
+
Object.assign(virtualizationConfig, options);
|
|
163
|
+
virtualizationConfig.enabled = true;
|
|
164
|
+
|
|
165
|
+
console.log('MintWaterfall: Virtualization enabled with config:', virtualizationConfig);
|
|
166
|
+
return performanceManager;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function disableVirtualization(): PerformanceManager {
|
|
170
|
+
virtualizationConfig.enabled = false;
|
|
171
|
+
console.log('MintWaterfall: Virtualization disabled');
|
|
172
|
+
return performanceManager;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function calculateVisibleRange(scrollTop: number, containerHeight: number, itemHeight: number): [number, number] {
|
|
176
|
+
const start = Math.floor(scrollTop / itemHeight);
|
|
177
|
+
const visibleCount = Math.ceil(containerHeight / itemHeight);
|
|
178
|
+
const end = start + visibleCount;
|
|
179
|
+
|
|
180
|
+
// Add buffer for smooth scrolling
|
|
181
|
+
const bufferStart = Math.max(0, start - virtualizationConfig.bufferSize);
|
|
182
|
+
const bufferEnd = end + virtualizationConfig.bufferSize;
|
|
183
|
+
|
|
184
|
+
return [bufferStart, bufferEnd];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function optimizeRendering(container: d3.Selection<d3.BaseType, any, any, any>, data: any[]): PerformanceManager {
|
|
188
|
+
const startTime = performance.now();
|
|
189
|
+
|
|
190
|
+
performanceMetrics.totalElements = data.length;
|
|
191
|
+
|
|
192
|
+
if (virtualizationConfig.enabled && data.length > virtualizationConfig.renderThreshold) {
|
|
193
|
+
renderVirtualized(container, data);
|
|
194
|
+
} else {
|
|
195
|
+
renderDirect(container, data);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const endTime = performance.now();
|
|
199
|
+
performanceMetrics.renderTime = endTime - startTime;
|
|
200
|
+
performanceMetrics.renderCalls++;
|
|
201
|
+
|
|
202
|
+
updateFPS();
|
|
203
|
+
return performanceManager;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function renderVirtualized(container: d3.Selection<d3.BaseType, any, any, any>, data: any[]): void {
|
|
207
|
+
// Implement virtualization logic
|
|
208
|
+
const containerNode = container.node() as Element;
|
|
209
|
+
if (!containerNode) return;
|
|
210
|
+
|
|
211
|
+
const containerHeight = containerNode.clientHeight;
|
|
212
|
+
const scrollTop = (containerNode as HTMLElement).scrollTop || 0;
|
|
213
|
+
const itemHeight = 30; // Estimate or calculate from data
|
|
214
|
+
|
|
215
|
+
const [visibleStart, visibleEnd] = calculateVisibleRange(scrollTop, containerHeight, itemHeight);
|
|
216
|
+
const visibleData = data.slice(visibleStart, Math.min(visibleEnd, data.length));
|
|
217
|
+
|
|
218
|
+
virtualViewport.start = visibleStart;
|
|
219
|
+
virtualViewport.end = visibleEnd;
|
|
220
|
+
virtualViewport.visible = visibleData.map((_, i) => i + visibleStart);
|
|
221
|
+
|
|
222
|
+
performanceMetrics.visibleElements = visibleData.length;
|
|
223
|
+
|
|
224
|
+
// Render only visible elements with proper typing
|
|
225
|
+
const elements = container.selectAll<SVGGElement, any>('.virtual-item')
|
|
226
|
+
.data(visibleData, (d: any, i: number) => `item-${i + visibleStart}`);
|
|
227
|
+
|
|
228
|
+
elements.exit().remove();
|
|
229
|
+
|
|
230
|
+
const enter = elements.enter().append<SVGGElement>('g')
|
|
231
|
+
.attr('class', 'virtual-item');
|
|
232
|
+
|
|
233
|
+
const merged = elements.merge(enter);
|
|
234
|
+
merged.attr('transform', (d: any, i: number) => `translate(0, ${(i + visibleStart) * itemHeight})`);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function renderDirect(container: d3.Selection<d3.BaseType, any, any, any>, data: any[]): void {
|
|
238
|
+
// Standard rendering for smaller datasets
|
|
239
|
+
performanceMetrics.visibleElements = data.length;
|
|
240
|
+
|
|
241
|
+
const elements = container.selectAll<SVGGElement, any>('.chart-element')
|
|
242
|
+
.data(data);
|
|
243
|
+
|
|
244
|
+
elements.exit().remove();
|
|
245
|
+
|
|
246
|
+
const enter = elements.enter().append<SVGGElement>('g')
|
|
247
|
+
.attr('class', 'chart-element');
|
|
248
|
+
|
|
249
|
+
const merged = elements.merge(enter);
|
|
250
|
+
// Apply transformations and styles here - merged variable prevents compilation errors
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function profileOperation(name: string, operation: () => any): any {
|
|
254
|
+
const startTime = performance.now();
|
|
255
|
+
const result = operation();
|
|
256
|
+
const endTime = performance.now();
|
|
257
|
+
const duration = endTime - startTime;
|
|
258
|
+
|
|
259
|
+
if (!profilers.has(name)) {
|
|
260
|
+
profilers.set(name, {
|
|
261
|
+
startTime: 0,
|
|
262
|
+
endTime: 0,
|
|
263
|
+
samples: [],
|
|
264
|
+
averageTime: 0,
|
|
265
|
+
minTime: Infinity,
|
|
266
|
+
maxTime: 0
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const profiler = profilers.get(name)!;
|
|
271
|
+
profiler.samples.push(duration);
|
|
272
|
+
profiler.minTime = Math.min(profiler.minTime, duration);
|
|
273
|
+
profiler.maxTime = Math.max(profiler.maxTime, duration);
|
|
274
|
+
profiler.averageTime = profiler.samples.reduce((a, b) => a + b, 0) / profiler.samples.length;
|
|
275
|
+
|
|
276
|
+
// Keep only recent samples for rolling average
|
|
277
|
+
if (profiler.samples.length > 100) {
|
|
278
|
+
profiler.samples.shift();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return result;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function getMetrics(): PerformanceMetrics {
|
|
285
|
+
// Update memory usage if available
|
|
286
|
+
if ('memory' in performance) {
|
|
287
|
+
const memInfo = (performance as any).memory;
|
|
288
|
+
performanceMetrics.memoryUsage = memInfo.usedJSHeapSize;
|
|
289
|
+
performanceMetrics.peakMemoryUsage = Math.max(performanceMetrics.peakMemoryUsage, memInfo.usedJSHeapSize);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return { ...performanceMetrics };
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function resetMetrics(): PerformanceManager {
|
|
296
|
+
performanceMetrics = {
|
|
297
|
+
renderTime: 0,
|
|
298
|
+
dataProcessingTime: 0,
|
|
299
|
+
memoryUsage: 0,
|
|
300
|
+
visibleElements: 0,
|
|
301
|
+
totalElements: 0,
|
|
302
|
+
fps: 0,
|
|
303
|
+
lastFrameTime: 0,
|
|
304
|
+
averageFrameTime: 0,
|
|
305
|
+
peakMemoryUsage: 0,
|
|
306
|
+
renderCalls: 0
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
profilers.clear();
|
|
310
|
+
frameCount = 0;
|
|
311
|
+
lastFpsTime = performance.now();
|
|
312
|
+
|
|
313
|
+
return performanceManager;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function enableMemoryPooling(options: Partial<MemoryPool> = {}): PerformanceManager {
|
|
317
|
+
Object.assign(memoryPool, options);
|
|
318
|
+
optimizationConfig.memoryPooling = true;
|
|
319
|
+
|
|
320
|
+
console.log('MintWaterfall: Memory pooling enabled');
|
|
321
|
+
return performanceManager;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function getFromPool(type: string): any {
|
|
325
|
+
if (!optimizationConfig.memoryPooling) return null;
|
|
326
|
+
|
|
327
|
+
const pool = memoryPool.elements.get(type);
|
|
328
|
+
if (pool && pool.length > 0) {
|
|
329
|
+
memoryPool.hitCount++;
|
|
330
|
+
memoryPool.currentSize--;
|
|
331
|
+
return pool.pop();
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
memoryPool.missCount++;
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function returnToPool(type: string, element: any): void {
|
|
339
|
+
if (!optimizationConfig.memoryPooling || memoryPool.currentSize >= memoryPool.maxSize) return;
|
|
340
|
+
|
|
341
|
+
if (!memoryPool.elements.has(type)) {
|
|
342
|
+
memoryPool.elements.set(type, []);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const pool = memoryPool.elements.get(type)!;
|
|
346
|
+
pool.push(element);
|
|
347
|
+
memoryPool.currentSize++;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function createRenderBatch(): RenderBatch {
|
|
351
|
+
return {
|
|
352
|
+
operations: [],
|
|
353
|
+
priority: 1,
|
|
354
|
+
timestamp: performance.now(),
|
|
355
|
+
elementCount: 0
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function flushRenderBatch(batch: RenderBatch): PerformanceManager {
|
|
360
|
+
const startTime = performance.now();
|
|
361
|
+
|
|
362
|
+
// Sort operations by priority and type for optimal rendering
|
|
363
|
+
batch.operations.sort((a, b) => {
|
|
364
|
+
const typeOrder = ['remove', 'create', 'update', 'style', 'attribute'];
|
|
365
|
+
return typeOrder.indexOf(a.type) - typeOrder.indexOf(b.type);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
// Execute operations in batch
|
|
369
|
+
batch.operations.forEach(operation => {
|
|
370
|
+
executeRenderOperation(operation);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
const endTime = performance.now();
|
|
374
|
+
performanceMetrics.renderTime += endTime - startTime;
|
|
375
|
+
|
|
376
|
+
return performanceManager;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function executeRenderOperation(operation: RenderOperation): void {
|
|
380
|
+
const { type, element, properties } = operation;
|
|
381
|
+
|
|
382
|
+
switch (type) {
|
|
383
|
+
case 'create':
|
|
384
|
+
// Create new element logic
|
|
385
|
+
break;
|
|
386
|
+
case 'update':
|
|
387
|
+
// Update element data logic
|
|
388
|
+
break;
|
|
389
|
+
case 'remove':
|
|
390
|
+
// Remove element logic
|
|
391
|
+
if (element && element.remove) {
|
|
392
|
+
element.remove();
|
|
393
|
+
}
|
|
394
|
+
break;
|
|
395
|
+
case 'style':
|
|
396
|
+
// Apply styles
|
|
397
|
+
if (element && properties) {
|
|
398
|
+
Object.keys(properties).forEach(prop => {
|
|
399
|
+
element.style(prop, properties[prop]);
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
break;
|
|
403
|
+
case 'attribute':
|
|
404
|
+
// Apply attributes
|
|
405
|
+
if (element && properties) {
|
|
406
|
+
Object.keys(properties).forEach(prop => {
|
|
407
|
+
element.attr(prop, properties[prop]);
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
break;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function setUpdateStrategy(strategy: 'immediate' | 'debounced' | 'throttled' | 'batched'): PerformanceManager {
|
|
415
|
+
updateStrategy = strategy;
|
|
416
|
+
console.log(`MintWaterfall: Update strategy set to ${strategy}`);
|
|
417
|
+
return performanceManager;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function updateFPS(): void {
|
|
421
|
+
frameCount++;
|
|
422
|
+
const currentTime = performance.now();
|
|
423
|
+
|
|
424
|
+
if (currentTime - lastFpsTime >= 1000) {
|
|
425
|
+
performanceMetrics.fps = Math.round((frameCount * 1000) / (currentTime - lastFpsTime));
|
|
426
|
+
performanceMetrics.averageFrameTime = (currentTime - lastFpsTime) / frameCount;
|
|
427
|
+
frameCount = 0;
|
|
428
|
+
lastFpsTime = currentTime;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
performanceMetrics.lastFrameTime = currentTime;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function createDashboard(): HTMLElement {
|
|
435
|
+
const dashboard = document.createElement('div');
|
|
436
|
+
dashboard.className = 'mintwaterfall-performance-dashboard';
|
|
437
|
+
dashboard.style.cssText = `
|
|
438
|
+
position: fixed;
|
|
439
|
+
top: 10px;
|
|
440
|
+
right: 10px;
|
|
441
|
+
background: rgba(0, 0, 0, 0.8);
|
|
442
|
+
color: white;
|
|
443
|
+
padding: 10px;
|
|
444
|
+
border-radius: 5px;
|
|
445
|
+
font-family: monospace;
|
|
446
|
+
font-size: 12px;
|
|
447
|
+
z-index: 10000;
|
|
448
|
+
max-width: 300px;
|
|
449
|
+
`;
|
|
450
|
+
|
|
451
|
+
return dashboard;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function updateDashboard(): void {
|
|
455
|
+
if (!dashboardElement) return;
|
|
456
|
+
|
|
457
|
+
const metrics = getMetrics();
|
|
458
|
+
const memoryMB = (metrics.memoryUsage / 1024 / 1024).toFixed(2);
|
|
459
|
+
const peakMemoryMB = (metrics.peakMemoryUsage / 1024 / 1024).toFixed(2);
|
|
460
|
+
|
|
461
|
+
dashboardElement.innerHTML = `
|
|
462
|
+
<div><strong>MintWaterfall Performance</strong></div>
|
|
463
|
+
<div>FPS: ${metrics.fps}</div>
|
|
464
|
+
<div>Render Time: ${metrics.renderTime.toFixed(2)}ms</div>
|
|
465
|
+
<div>Memory: ${memoryMB}MB (Peak: ${peakMemoryMB}MB)</div>
|
|
466
|
+
<div>Visible Elements: ${metrics.visibleElements}/${metrics.totalElements}</div>
|
|
467
|
+
<div>Render Calls: ${metrics.renderCalls}</div>
|
|
468
|
+
<div>Virtualization: ${virtualizationConfig.enabled ? 'ON' : 'OFF'}</div>
|
|
469
|
+
<div>Pool Hit Rate: ${memoryPool.hitCount + memoryPool.missCount > 0 ?
|
|
470
|
+
((memoryPool.hitCount / (memoryPool.hitCount + memoryPool.missCount)) * 100).toFixed(1) : 0}%</div>
|
|
471
|
+
`;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function enableDashboard(container?: HTMLElement): PerformanceManager {
|
|
475
|
+
if (!dashboardElement) {
|
|
476
|
+
dashboardElement = createDashboard();
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const targetContainer = container || document.body;
|
|
480
|
+
if (targetContainer && !targetContainer.contains(dashboardElement)) {
|
|
481
|
+
targetContainer.appendChild(dashboardElement);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Update dashboard periodically
|
|
485
|
+
setInterval(updateDashboard, 500);
|
|
486
|
+
|
|
487
|
+
return performanceManager;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function disableDashboard(): PerformanceManager {
|
|
491
|
+
if (dashboardElement && dashboardElement.parentNode) {
|
|
492
|
+
dashboardElement.parentNode.removeChild(dashboardElement);
|
|
493
|
+
dashboardElement = null;
|
|
494
|
+
}
|
|
495
|
+
return performanceManager;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function getDashboard(): HTMLElement | null {
|
|
499
|
+
return dashboardElement;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function optimizeMemory(): PerformanceManager {
|
|
503
|
+
// Force garbage collection if available
|
|
504
|
+
if ((window as any).gc) {
|
|
505
|
+
(window as any).gc();
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Clear unused pools
|
|
509
|
+
memoryPool.elements.forEach((pool, type) => {
|
|
510
|
+
if (pool.length > 50) {
|
|
511
|
+
pool.splice(25); // Keep only recent 25 elements
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
// Clear old profiler samples
|
|
516
|
+
profilers.forEach(profiler => {
|
|
517
|
+
if (profiler.samples.length > 50) {
|
|
518
|
+
profiler.samples.splice(0, profiler.samples.length - 50);
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
console.log('MintWaterfall: Memory optimization completed');
|
|
523
|
+
return performanceManager;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function getRecommendations(): string[] {
|
|
527
|
+
const recommendations: string[] = [];
|
|
528
|
+
const metrics = getMetrics();
|
|
529
|
+
|
|
530
|
+
if (metrics.totalElements > 5000 && !virtualizationConfig.enabled) {
|
|
531
|
+
recommendations.push('Enable virtualization for improved performance with large datasets');
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (metrics.fps < 30) {
|
|
535
|
+
recommendations.push('Consider reducing visual complexity or enabling performance optimizations');
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if (metrics.memoryUsage > 100 * 1024 * 1024) { // 100MB
|
|
539
|
+
recommendations.push('Memory usage is high - consider enabling memory pooling or reducing data size');
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
if (metrics.renderTime > 100) {
|
|
543
|
+
recommendations.push('Render time is slow - consider batching updates or optimizing render operations');
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const poolEfficiency = memoryPool.hitCount / (memoryPool.hitCount + memoryPool.missCount || 1);
|
|
547
|
+
if (poolEfficiency < 0.5 && optimizationConfig.memoryPooling) {
|
|
548
|
+
recommendations.push('Memory pool efficiency is low - consider adjusting pool size or strategy');
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
return recommendations;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
const performanceManager: PerformanceManager = {
|
|
555
|
+
enableVirtualization,
|
|
556
|
+
disableVirtualization,
|
|
557
|
+
optimizeRendering,
|
|
558
|
+
profileOperation,
|
|
559
|
+
getMetrics,
|
|
560
|
+
resetMetrics,
|
|
561
|
+
enableMemoryPooling,
|
|
562
|
+
createRenderBatch,
|
|
563
|
+
flushRenderBatch,
|
|
564
|
+
setUpdateStrategy,
|
|
565
|
+
getDashboard,
|
|
566
|
+
enableDashboard,
|
|
567
|
+
disableDashboard,
|
|
568
|
+
optimizeMemory,
|
|
569
|
+
getRecommendations
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
return performanceManager;
|
|
573
|
+
}
|