archetype-ecs-lib 0.5.0 → 0.6.1

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.
@@ -0,0 +1,548 @@
1
+ "use strict";
2
+ var __values = (this && this.__values) || function(o) {
3
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
4
+ if (m) return m.call(o);
5
+ if (o && typeof o.length === "number") return {
6
+ next: function () {
7
+ if (o && i >= o.length) o = void 0;
8
+ return { value: o && o[i++], done: !o };
9
+ }
10
+ };
11
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
12
+ };
13
+ var __read = (this && this.__read) || function (o, n) {
14
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
15
+ if (!m) return o;
16
+ var i = m.call(o), r, ar = [], e;
17
+ try {
18
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
19
+ }
20
+ catch (error) { e = { error: error }; }
21
+ finally {
22
+ try {
23
+ if (r && !r.done && (m = i["return"])) m.call(i);
24
+ }
25
+ finally { if (e) throw e.error; }
26
+ }
27
+ return ar;
28
+ };
29
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
30
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
31
+ if (ar || !(i in from)) {
32
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
33
+ ar[i] = from[i];
34
+ }
35
+ }
36
+ return to.concat(ar || Array.prototype.slice.call(from));
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.StatsOverlay = void 0;
40
+ var StatsOverlay = /** @class */ (function () {
41
+ function StatsOverlay(options) {
42
+ if (options === void 0) { options = {}; }
43
+ var _a, _b, _c, _d, _e, _f, _g, _h;
44
+ this.root = null;
45
+ this.header = null;
46
+ this.toggleButton = null;
47
+ this.debugToggleButton = null;
48
+ this.content = null;
49
+ this.text = null;
50
+ this.canvas = null;
51
+ this.ctx = null;
52
+ this.resizeObserver = null;
53
+ this.isExpanded = true;
54
+ this.debugLoggingEnabled = false;
55
+ this.isInitialized = false;
56
+ this.debugingEnabled = false;
57
+ // ---- Profiling / stats (last completed frame) ----
58
+ this._profilingEnabled = true;
59
+ this._frameCounter = 0;
60
+ this._lastDt = 0;
61
+ this._lastFrameMs = 0;
62
+ this._phaseMs = new Map();
63
+ this._systemMs = new Map();
64
+ // ---- Profiling history (rolling window) ----
65
+ this._historyCapacity = 120;
66
+ this._histDt = [];
67
+ this._histFrameMs = [];
68
+ this._histPhaseMs = new Map();
69
+ this._histSystemMs = new Map();
70
+ // Drag state
71
+ this.isDragging = false;
72
+ this.dragOffsetX = 0;
73
+ this.dragOffsetY = 0;
74
+ this.opts = {
75
+ parent: (_a = options.parent) !== null && _a !== void 0 ? _a : null,
76
+ left: (_b = options.left) !== null && _b !== void 0 ? _b : 8,
77
+ top: (_c = options.top) !== null && _c !== void 0 ? _c : 8,
78
+ width: (_d = options.width) !== null && _d !== void 0 ? _d : 320,
79
+ height: (_e = options.height) !== null && _e !== void 0 ? _e : 80,
80
+ targetFrameMs: (_f = options.targetFrameMs) !== null && _f !== void 0 ? _f : 16.67,
81
+ slowFrameMs: (_g = options.slowFrameMs) !== null && _g !== void 0 ? _g : 20,
82
+ maxSamples: (_h = options.maxSamples) !== null && _h !== void 0 ? _h : 240
83
+ };
84
+ }
85
+ StatsOverlay.prototype.initializeDom = function () {
86
+ var _this = this;
87
+ if (this.isInitialized || !this.opts.parent)
88
+ return;
89
+ this.isInitialized = true;
90
+ this.root = document.createElement("div");
91
+ this.root.style.position = "fixed";
92
+ this.root.style.left = "".concat(this.opts.left, "px");
93
+ this.root.style.top = "".concat(this.opts.top, "px");
94
+ this.root.style.zIndex = "9999";
95
+ this.root.style.fontFamily = "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace";
96
+ this.root.style.fontSize = "12px";
97
+ this.root.style.color = "#d7e3ff";
98
+ this.root.style.background = "rgba(10, 14, 24, 0.75)";
99
+ this.root.style.border = "1px solid rgba(140, 170, 255, 0.25)";
100
+ this.root.style.borderRadius = "8px";
101
+ this.root.style.backdropFilter = "blur(2px)";
102
+ this.root.style.maxWidth = "calc(100vw - 16px)";
103
+ this.header = document.createElement("div");
104
+ this.header.style.display = "flex";
105
+ this.header.style.alignItems = "center";
106
+ this.header.style.padding = "8px";
107
+ this.header.style.cursor = "pointer";
108
+ this.header.style.pointerEvents = "auto";
109
+ this.header.style.userSelect = "none";
110
+ var title = document.createElement("span");
111
+ title.textContent = "ECS Stats";
112
+ title.style.fontWeight = "600";
113
+ title.style.userSelect = "none";
114
+ title.style.marginRight = "10px";
115
+ this.toggleButton = document.createElement("button");
116
+ this.toggleButton.textContent = "−";
117
+ this.toggleButton.style.background = "rgba(140, 170, 255, 0.15)";
118
+ this.toggleButton.style.border = "1px solid rgba(140, 170, 255, 0.3)";
119
+ this.toggleButton.style.borderRadius = "4px";
120
+ this.toggleButton.style.color = "#d7e3ff";
121
+ this.toggleButton.style.width = "24px";
122
+ this.toggleButton.style.height = "24px";
123
+ this.toggleButton.style.cursor = "pointer";
124
+ this.toggleButton.style.fontSize = "16px";
125
+ this.toggleButton.style.lineHeight = "1";
126
+ this.toggleButton.style.padding = "0";
127
+ this.toggleButton.style.display = "flex";
128
+ this.toggleButton.style.alignItems = "center";
129
+ this.toggleButton.style.justifyContent = "center";
130
+ this.toggleButton.style.userSelect = "none";
131
+ this.toggleButton.addEventListener("mouseenter", function () {
132
+ _this.toggleButton.style.background = "rgba(140, 170, 255, 0.25)";
133
+ });
134
+ this.toggleButton.addEventListener("mouseleave", function () {
135
+ _this.toggleButton.style.background = "rgba(140, 170, 255, 0.15)";
136
+ });
137
+ this.toggleButton.addEventListener("click", function (e) {
138
+ e.stopPropagation();
139
+ _this.toggle();
140
+ });
141
+ this.debugToggleButton = this.createDebugToggleButton("🔇", "Toggle console debug logging");
142
+ this.debugToggleButton.style.marginRight = "5px";
143
+ this.debugToggleButton.addEventListener("click", function (e) {
144
+ e.stopPropagation();
145
+ _this.toggleDebugLogging();
146
+ });
147
+ this.header.appendChild(title);
148
+ this.header.appendChild(this.debugToggleButton);
149
+ this.header.appendChild(this.toggleButton);
150
+ this.content = document.createElement("div");
151
+ this.content.style.padding = "0 8px 8px 8px";
152
+ this.content.style.pointerEvents = "none";
153
+ this.text = document.createElement("pre");
154
+ this.text.style.margin = "0 0 6px 0";
155
+ this.text.style.whiteSpace = "pre";
156
+ this.text.style.lineHeight = "1.2";
157
+ this.canvas = document.createElement("canvas");
158
+ this.canvas.style.display = "block";
159
+ this.canvas.style.borderRadius = "6px";
160
+ this.canvas.style.background = "rgba(255,255,255,0.04)";
161
+ this.canvas.style.width = "100%";
162
+ this.canvas.style.height = "auto";
163
+ var ctx = this.canvas.getContext("2d");
164
+ if (!ctx)
165
+ throw new Error("StatsOverlay: canvas 2D context not available");
166
+ this.ctx = ctx;
167
+ this.content.appendChild(this.text);
168
+ this.content.appendChild(this.canvas);
169
+ this.root.appendChild(this.header);
170
+ this.root.appendChild(this.content);
171
+ this.opts.parent.appendChild(this.root);
172
+ this.resizeCanvas();
173
+ this.resizeObserver = new ResizeObserver(function () { return _this.resizeCanvas(); });
174
+ this.resizeObserver.observe(this.root);
175
+ this.setupDrag();
176
+ };
177
+ StatsOverlay.prototype.setDebugging = function (enabled) {
178
+ var _a;
179
+ if (enabled) {
180
+ // Only initialize DOM if we're in a browser environment
181
+ if (typeof document !== "undefined") {
182
+ this.opts.parent = (_a = this.opts.parent) !== null && _a !== void 0 ? _a : document.body;
183
+ this.initializeDom();
184
+ }
185
+ }
186
+ else {
187
+ this.destroyOverlay();
188
+ }
189
+ this.debugingEnabled = enabled;
190
+ };
191
+ StatsOverlay.prototype.setProfilingEnabled = function (enabled) {
192
+ this._profilingEnabled = enabled;
193
+ };
194
+ StatsOverlay.prototype.setProfilingHistorySize = function (frames) {
195
+ this._historyCapacity = Math.max(0, Math.floor(frames));
196
+ this._trimHistoryToCapacity();
197
+ };
198
+ StatsOverlay.prototype._trimHistoryToCapacity = function () {
199
+ var e_1, _a, e_2, _b;
200
+ var cap = this._historyCapacity;
201
+ var trimArray = function (arr) {
202
+ if (cap === 0) {
203
+ arr.length = 0;
204
+ return;
205
+ }
206
+ while (arr.length > cap)
207
+ arr.shift();
208
+ };
209
+ trimArray(this._histDt);
210
+ trimArray(this._histFrameMs);
211
+ try {
212
+ for (var _c = __values(this._histPhaseMs.values()), _d = _c.next(); !_d.done; _d = _c.next()) {
213
+ var arr = _d.value;
214
+ trimArray(arr);
215
+ }
216
+ }
217
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
218
+ finally {
219
+ try {
220
+ if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
221
+ }
222
+ finally { if (e_1) throw e_1.error; }
223
+ }
224
+ try {
225
+ for (var _e = __values(this._histSystemMs.values()), _f = _e.next(); !_f.done; _f = _e.next()) {
226
+ var arr = _f.value;
227
+ trimArray(arr);
228
+ }
229
+ }
230
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
231
+ finally {
232
+ try {
233
+ if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
234
+ }
235
+ finally { if (e_2) throw e_2.error; }
236
+ }
237
+ };
238
+ StatsOverlay.prototype._pushSeriesFrame = function (series, current) {
239
+ var e_3, _a, e_4, _b;
240
+ var _c;
241
+ var sizeBefore = this._histFrameMs.length; // same as dt length before push
242
+ try {
243
+ // Ensure existing keys get a value (0 if missing this frame)
244
+ for (var series_1 = __values(series), series_1_1 = series_1.next(); !series_1_1.done; series_1_1 = series_1.next()) {
245
+ var _d = __read(series_1_1.value, 2), k = _d[0], arr = _d[1];
246
+ var v = (_c = current.get(k)) !== null && _c !== void 0 ? _c : 0;
247
+ arr.push(v);
248
+ if (this._historyCapacity === 0)
249
+ arr.length = 0;
250
+ else
251
+ while (arr.length > this._historyCapacity)
252
+ arr.shift();
253
+ }
254
+ }
255
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
256
+ finally {
257
+ try {
258
+ if (series_1_1 && !series_1_1.done && (_a = series_1.return)) _a.call(series_1);
259
+ }
260
+ finally { if (e_3) throw e_3.error; }
261
+ }
262
+ try {
263
+ // New keys discovered this frame: backfill zeros so lengths align
264
+ for (var current_1 = __values(current), current_1_1 = current_1.next(); !current_1_1.done; current_1_1 = current_1.next()) {
265
+ var _e = __read(current_1_1.value, 2), k = _e[0], v = _e[1];
266
+ if (series.has(k))
267
+ continue;
268
+ var arr = new Array(sizeBefore).fill(0);
269
+ arr.push(v);
270
+ series.set(k, arr);
271
+ if (this._historyCapacity === 0)
272
+ arr.length = 0;
273
+ else
274
+ while (arr.length > this._historyCapacity)
275
+ arr.shift();
276
+ }
277
+ }
278
+ catch (e_4_1) { e_4 = { error: e_4_1 }; }
279
+ finally {
280
+ try {
281
+ if (current_1_1 && !current_1_1.done && (_b = current_1.return)) _b.call(current_1);
282
+ }
283
+ finally { if (e_4) throw e_4.error; }
284
+ }
285
+ };
286
+ /** @internal Called by Schedule/World.update to start a new profiling frame */
287
+ StatsOverlay.prototype._profBeginFrame = function (dt) {
288
+ this._frameCounter++;
289
+ this._lastDt = dt;
290
+ this._phaseMs.clear();
291
+ this._systemMs.clear();
292
+ if (!this._profilingEnabled) {
293
+ this._lastFrameMs = 0;
294
+ return 0;
295
+ }
296
+ return performance.now();
297
+ };
298
+ /** @internal Called by Schedule/World.update to end a new profiling frame */
299
+ StatsOverlay.prototype._profEndFrame = function (frameStartMs) {
300
+ if (!this._profilingEnabled)
301
+ return;
302
+ this._lastFrameMs = performance.now() - frameStartMs;
303
+ // Update history (aligned series)
304
+ this._histDt.push(this._lastDt);
305
+ this._histFrameMs.push(this._lastFrameMs);
306
+ this._trimHistoryToCapacity();
307
+ this._pushSeriesFrame(this._histPhaseMs, this._phaseMs);
308
+ this._pushSeriesFrame(this._histSystemMs, this._systemMs);
309
+ };
310
+ /** @internal */
311
+ StatsOverlay.prototype._profAddPhase = function (phase, ms) {
312
+ var _a;
313
+ if (!this._profilingEnabled)
314
+ return;
315
+ this._phaseMs.set(phase, ((_a = this._phaseMs.get(phase)) !== null && _a !== void 0 ? _a : 0) + ms);
316
+ };
317
+ /** @internal */
318
+ StatsOverlay.prototype._profAddSystem = function (name, ms) {
319
+ var _a;
320
+ if (!this._profilingEnabled)
321
+ return;
322
+ this._systemMs.set(name, ((_a = this._systemMs.get(name)) !== null && _a !== void 0 ? _a : 0) + ms);
323
+ };
324
+ StatsOverlay.prototype.createDebugToggleButton = function (text, title) {
325
+ var btn = document.createElement("button");
326
+ btn.textContent = text;
327
+ btn.title = title;
328
+ btn.style.background = "rgba(140, 170, 255, 0.15)";
329
+ btn.style.border = "1px solid rgba(140, 170, 255, 0.3)";
330
+ btn.style.borderRadius = "4px";
331
+ btn.style.color = "#d7e3ff";
332
+ btn.style.width = "24px";
333
+ btn.style.height = "24px";
334
+ btn.style.cursor = "pointer";
335
+ btn.style.fontSize = "12px";
336
+ btn.style.lineHeight = "1";
337
+ btn.style.padding = "0";
338
+ btn.style.display = "flex";
339
+ btn.style.alignItems = "center";
340
+ btn.style.justifyContent = "center";
341
+ btn.style.userSelect = "none";
342
+ btn.addEventListener("mouseenter", function () {
343
+ btn.style.background = "rgba(140, 170, 255, 0.25)";
344
+ });
345
+ btn.addEventListener("mouseleave", function () {
346
+ btn.style.background = "rgba(140, 170, 255, 0.15)";
347
+ });
348
+ return btn;
349
+ };
350
+ StatsOverlay.prototype.toggleDebugLogging = function () {
351
+ this.debugLoggingEnabled = !this.debugLoggingEnabled;
352
+ this.debugToggleButton.textContent = this.debugLoggingEnabled ? "🔊" : "🔇";
353
+ this.debugToggleButton.title = this.debugLoggingEnabled
354
+ ? "Console debug logging ON (click to disable)"
355
+ : "Console debug logging OFF (click to enable)";
356
+ };
357
+ StatsOverlay.prototype.setupDrag = function () {
358
+ var _this = this;
359
+ var onMouseDown = function (e) {
360
+ // Ignore if clicking the toggle button
361
+ if (e.target === _this.toggleButton || e.target === _this.debugToggleButton)
362
+ return;
363
+ _this.isDragging = true;
364
+ var rect = _this.root.getBoundingClientRect();
365
+ _this.dragOffsetX = e.clientX - rect.left;
366
+ _this.dragOffsetY = e.clientY - rect.top;
367
+ _this.header.style.cursor = "grabbing";
368
+ document.body.style.userSelect = "none";
369
+ };
370
+ var onMouseMove = function (e) {
371
+ if (!_this.isDragging)
372
+ return;
373
+ var newLeft = e.clientX - _this.dragOffsetX;
374
+ var newTop = e.clientY - _this.dragOffsetY;
375
+ // Clamp to viewport bounds
376
+ var rect = _this.root.getBoundingClientRect();
377
+ var maxLeft = window.innerWidth - rect.width;
378
+ var maxTop = window.innerHeight - rect.height;
379
+ _this.root.style.left = "".concat(Math.max(0, Math.min(newLeft, maxLeft)), "px");
380
+ _this.root.style.top = "".concat(Math.max(0, Math.min(newTop, maxTop)), "px");
381
+ };
382
+ var onMouseUp = function () {
383
+ if (!_this.isDragging)
384
+ return;
385
+ _this.isDragging = false;
386
+ _this.header.style.cursor = "grab";
387
+ document.body.style.userSelect = "";
388
+ };
389
+ this.header.addEventListener("mousedown", onMouseDown);
390
+ document.addEventListener("mousemove", onMouseMove);
391
+ document.addEventListener("mouseup", onMouseUp);
392
+ // Store references for cleanup
393
+ this._dragCleanup = function () {
394
+ _this.header.removeEventListener("mousedown", onMouseDown);
395
+ document.removeEventListener("mousemove", onMouseMove);
396
+ document.removeEventListener("mouseup", onMouseUp);
397
+ };
398
+ };
399
+ StatsOverlay.prototype.resizeCanvas = function () {
400
+ if (!this.canvas || !this.ctx)
401
+ return;
402
+ var dpr = window.devicePixelRatio || 1;
403
+ var rect = this.canvas.getBoundingClientRect();
404
+ var width = Math.min(this.opts.width, rect.width || this.opts.width);
405
+ var height = this.opts.height;
406
+ this.canvas.width = width * dpr;
407
+ this.canvas.height = height * dpr;
408
+ this.canvas.style.width = "".concat(width, "px");
409
+ this.canvas.style.height = "".concat(height, "px");
410
+ // Reset transform so repeated resizes don't accumulate scale.
411
+ var ctxAny = this.ctx;
412
+ if (typeof ctxAny.setTransform === "function") {
413
+ ctxAny.setTransform(1, 0, 0, 1, 0, 0);
414
+ }
415
+ else if (typeof ctxAny.resetTransform === "function") {
416
+ ctxAny.resetTransform();
417
+ }
418
+ this.ctx.scale(dpr, dpr);
419
+ };
420
+ StatsOverlay.prototype.toggle = function () {
421
+ if (!this.content || !this.toggleButton)
422
+ return;
423
+ this.isExpanded = !this.isExpanded;
424
+ this.content.style.display = this.isExpanded ? "block" : "none";
425
+ this.toggleButton.textContent = this.isExpanded ? "−" : "+";
426
+ };
427
+ StatsOverlay.prototype.destroyOverlay = function () {
428
+ var _a, _b, _c, _d;
429
+ (_b = (_a = this)._dragCleanup) === null || _b === void 0 ? void 0 : _b.call(_a);
430
+ (_c = this.resizeObserver) === null || _c === void 0 ? void 0 : _c.disconnect();
431
+ (_d = this.root) === null || _d === void 0 ? void 0 : _d.remove();
432
+ this.isInitialized = false;
433
+ };
434
+ /** Convenience: call each frame */
435
+ StatsOverlay.prototype.updateOverlay = function (stats, statsHistory) {
436
+ if (!this.debugingEnabled || !this.isInitialized)
437
+ return;
438
+ this.render(stats, statsHistory);
439
+ };
440
+ StatsOverlay.prototype.render = function (s, h) {
441
+ if (!this.debugingEnabled || !this.text || !this.ctx)
442
+ return;
443
+ var topPhases = Object.entries(s.phaseMs)
444
+ .sort(function (a, b) { return b[1] - a[1]; })
445
+ .slice(0, 6)
446
+ .map(function (_a) {
447
+ var _b = __read(_a, 2), k = _b[0], v = _b[1];
448
+ return "".concat(k, "=").concat(v.toFixed(2), "ms");
449
+ })
450
+ .join(" ");
451
+ this.text.textContent =
452
+ "Frame ".concat(s.frame, "\n") +
453
+ "Archetypes: ".concat(s.archetypes, "\n") +
454
+ "Rows: ".concat(s.rows, "\n") +
455
+ "Alive entities: ".concat(s.aliveEntities, "\n") +
456
+ "Systems: ".concat(s.systems, "\n") +
457
+ "Resources: ".concat(s.resources, "\n") +
458
+ "Event channels: ".concat(s.eventChannels, "\n") +
459
+ "Pending commands: ".concat(String(s.pendingCommands), "\n") +
460
+ "dt=".concat((s.dt * 1000).toFixed(2), "ms frame=").concat(s.frameMs.toFixed(2), "ms\n") +
461
+ "Phases: ".concat(topPhases);
462
+ if (this.debugLoggingEnabled) {
463
+ // tslint:disable-next-line:no-console
464
+ console.debug("Phases: ".concat(topPhases));
465
+ }
466
+ this.drawFrameGraph(h);
467
+ };
468
+ StatsOverlay.prototype.drawFrameGraph = function (h) {
469
+ var ctx = this.ctx;
470
+ var dpr = window.devicePixelRatio || 1;
471
+ var w = this.canvas.width / dpr;
472
+ var hh = this.canvas.height / dpr;
473
+ ctx.clearRect(0, 0, w, hh);
474
+ var n = Math.min(h.size, this.opts.maxSamples);
475
+ var series = h.frameMs.slice(h.size - n);
476
+ var maxMs = Math.max.apply(Math, __spreadArray([33.33, this.opts.targetFrameMs], __read(series), false));
477
+ var toY = function (ms) { return hh - (ms / maxMs) * (hh - 10) - 5; };
478
+ // colors (match bars)
479
+ var targetLineColor = "rgba(255,255,255,0.18)";
480
+ var okBarColor = "rgba(110,200,255,0.85)";
481
+ var slowBarColor = "rgba(255,110,110,0.85)";
482
+ var legendTextColor = "rgba(215,227,255,0.90)";
483
+ var legendBg = "rgba(10, 14, 24, 0.55)";
484
+ // target line
485
+ ctx.strokeStyle = targetLineColor;
486
+ ctx.beginPath();
487
+ ctx.moveTo(0, toY(this.opts.targetFrameMs));
488
+ ctx.lineTo(w, toY(this.opts.targetFrameMs));
489
+ ctx.stroke();
490
+ // bars (use actual series length; ensure width >= 1px)
491
+ var barW = Math.max(1, Math.floor(w / Math.max(1, series.length)));
492
+ var drawW = Math.max(1, barW);
493
+ for (var i = 0; i < series.length; i++) {
494
+ var ms = series[i];
495
+ var _x = i * barW;
496
+ var _y = toY(ms);
497
+ var bh = hh - _y;
498
+ var slow = ms > this.opts.slowFrameMs;
499
+ ctx.fillStyle = slow ? slowBarColor : okBarColor;
500
+ ctx.fillRect(_x, _y, drawW, bh);
501
+ }
502
+ // ---- Legend (top-left inside graph) ----
503
+ ctx.save();
504
+ ctx.font = "11px ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace";
505
+ ctx.textBaseline = "middle";
506
+ ctx.fillStyle = legendTextColor;
507
+ var pad = 6;
508
+ var lineH = 14;
509
+ var lineLabel = "Target ".concat(this.opts.targetFrameMs.toFixed(2), "ms");
510
+ var okLabel = "OK \u2264 ".concat(this.opts.slowFrameMs.toFixed(0), "ms");
511
+ var slowLabel = "Slow > ".concat(this.opts.slowFrameMs.toFixed(0), "ms");
512
+ // compute background box width roughly
513
+ var w1 = ctx.measureText(lineLabel).width;
514
+ var w2 = ctx.measureText(okLabel).width;
515
+ var w3 = ctx.measureText(slowLabel).width;
516
+ var boxW = Math.ceil(Math.max(w1, w2, w3) + 40);
517
+ var boxH = pad * 2 + lineH * 3;
518
+ // background
519
+ ctx.fillStyle = legendBg;
520
+ ctx.fillRect(6, 6, boxW, boxH);
521
+ // row 1: target line sample
522
+ var x = 6 + pad;
523
+ var y = 6 + pad + lineH * 0.5;
524
+ ctx.strokeStyle = targetLineColor;
525
+ ctx.lineWidth = 2;
526
+ ctx.beginPath();
527
+ ctx.moveTo(x, y);
528
+ ctx.lineTo(x + 18, y);
529
+ ctx.stroke();
530
+ ctx.fillStyle = legendTextColor;
531
+ ctx.fillText(lineLabel, x + 24, y);
532
+ // row 2: ok bar sample
533
+ y += lineH;
534
+ ctx.fillStyle = okBarColor;
535
+ ctx.fillRect(x, y - 5, 18, 10);
536
+ ctx.fillStyle = legendTextColor;
537
+ ctx.fillText(okLabel, x + 24, y);
538
+ // row 3: slow bar sample
539
+ y += lineH;
540
+ ctx.fillStyle = slowBarColor;
541
+ ctx.fillRect(x, y - 5, 18, 10);
542
+ ctx.fillStyle = legendTextColor;
543
+ ctx.fillText(slowLabel, x + 24, y);
544
+ ctx.restore();
545
+ };
546
+ return StatsOverlay;
547
+ }());
548
+ exports.StatsOverlay = StatsOverlay;
package/lib/index.d.ts CHANGED
@@ -4,3 +4,4 @@ export * from "./ecs/Commands";
4
4
  export * from "./ecs/World";
5
5
  export * from "./ecs/Schedule";
6
6
  export * from "./ecs/Events";
7
+ export * from "./ecs/stats/StatsOverlay";
package/lib/index.js CHANGED
@@ -20,3 +20,4 @@ __exportStar(require("./ecs/Commands"), exports);
20
20
  __exportStar(require("./ecs/World"), exports);
21
21
  __exportStar(require("./ecs/Schedule"), exports);
22
22
  __exportStar(require("./ecs/Events"), exports);
23
+ __exportStar(require("./ecs/stats/StatsOverlay"), exports);
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "archetype-ecs-lib",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "An Archetype Entity Component System (AECS)",
5
5
  "main": "./lib/index.js",
6
6
  "scripts": {
7
7
  "test": "jest --coverage",
8
8
  "build": "tsc",
9
- "lint": "tslint -c tslint.json src/**/*.ts",
9
+ "lint": "eslint src/ test/",
10
+ "lint:fix": "eslint src/ test/ --fix",
10
11
  "reload-packages": "rm -Rf node_modules && npm cache clean --force && npm ci"
11
12
  },
12
13
  "repository": {
@@ -31,7 +32,6 @@
31
32
  },
32
33
  "license": "MIT",
33
34
  "dependencies": {
34
- "tslint": "^6.1.3",
35
35
  "typescript": "^5.9.3"
36
36
  },
37
37
  "files": [
@@ -40,8 +40,12 @@
40
40
  ],
41
41
  "typings": "./lib/index.d.ts",
42
42
  "devDependencies": {
43
+ "@eslint/js": "^9.0.0",
43
44
  "@types/jest": "^30.0.0",
45
+ "eslint": "^9.0.0",
44
46
  "jest": "^30.2.0",
45
- "ts-jest": "^29.4.6"
47
+ "jest-environment-jsdom": "^30.2.0",
48
+ "ts-jest": "^29.4.6",
49
+ "typescript-eslint": "^8.0.0"
46
50
  }
47
51
  }