@editframe/elements 0.19.4-beta.0 → 0.20.1-beta.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 (132) hide show
  1. package/dist/elements/ContextProxiesController.d.ts +40 -0
  2. package/dist/elements/ContextProxiesController.js +69 -0
  3. package/dist/elements/EFCaptions.d.ts +45 -6
  4. package/dist/elements/EFCaptions.js +220 -26
  5. package/dist/elements/EFImage.js +4 -1
  6. package/dist/elements/EFMedia/AssetIdMediaEngine.d.ts +2 -1
  7. package/dist/elements/EFMedia/AssetIdMediaEngine.js +9 -0
  8. package/dist/elements/EFMedia/AssetMediaEngine.d.ts +1 -0
  9. package/dist/elements/EFMedia/AssetMediaEngine.js +11 -0
  10. package/dist/elements/EFMedia/BaseMediaEngine.d.ts +13 -1
  11. package/dist/elements/EFMedia/BaseMediaEngine.js +9 -0
  12. package/dist/elements/EFMedia/JitMediaEngine.d.ts +7 -1
  13. package/dist/elements/EFMedia/JitMediaEngine.js +15 -0
  14. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +2 -1
  15. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +2 -0
  16. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.d.ts +1 -1
  17. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +3 -1
  18. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +1 -1
  19. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.d.ts +1 -1
  20. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +6 -5
  21. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +3 -1
  22. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +2 -0
  23. package/dist/elements/EFMedia/shared/AudioSpanUtils.js +2 -2
  24. package/dist/elements/EFMedia/shared/GlobalInputCache.d.ts +39 -0
  25. package/dist/elements/EFMedia/shared/GlobalInputCache.js +57 -0
  26. package/dist/elements/EFMedia/shared/ThumbnailExtractor.d.ts +27 -0
  27. package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +106 -0
  28. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +1 -1
  29. package/dist/elements/EFMedia.d.ts +2 -2
  30. package/dist/elements/EFMedia.js +25 -1
  31. package/dist/elements/EFSurface.browsertest.d.ts +0 -0
  32. package/dist/elements/EFSurface.d.ts +30 -0
  33. package/dist/elements/EFSurface.js +96 -0
  34. package/dist/elements/EFTemporal.js +7 -6
  35. package/dist/elements/EFThumbnailStrip.browsertest.d.ts +0 -0
  36. package/dist/elements/EFThumbnailStrip.d.ts +86 -0
  37. package/dist/elements/EFThumbnailStrip.js +490 -0
  38. package/dist/elements/EFThumbnailStrip.media-engine.browsertest.d.ts +0 -0
  39. package/dist/elements/EFTimegroup.d.ts +6 -1
  40. package/dist/elements/EFTimegroup.js +53 -11
  41. package/dist/elements/updateAnimations.browsertest.d.ts +13 -0
  42. package/dist/elements/updateAnimations.d.ts +5 -0
  43. package/dist/elements/updateAnimations.js +37 -13
  44. package/dist/getRenderInfo.js +1 -1
  45. package/dist/gui/ContextMixin.js +27 -14
  46. package/dist/gui/EFControls.browsertest.d.ts +0 -0
  47. package/dist/gui/EFControls.d.ts +38 -0
  48. package/dist/gui/EFControls.js +51 -0
  49. package/dist/gui/EFFilmstrip.d.ts +40 -1
  50. package/dist/gui/EFFilmstrip.js +240 -3
  51. package/dist/gui/EFPreview.js +2 -1
  52. package/dist/gui/EFScrubber.d.ts +6 -5
  53. package/dist/gui/EFScrubber.js +31 -21
  54. package/dist/gui/EFTimeDisplay.browsertest.d.ts +0 -0
  55. package/dist/gui/EFTimeDisplay.d.ts +2 -6
  56. package/dist/gui/EFTimeDisplay.js +13 -23
  57. package/dist/gui/TWMixin.js +1 -1
  58. package/dist/gui/currentTimeContext.d.ts +3 -0
  59. package/dist/gui/currentTimeContext.js +3 -0
  60. package/dist/gui/durationContext.d.ts +3 -0
  61. package/dist/gui/durationContext.js +3 -0
  62. package/dist/index.d.ts +3 -0
  63. package/dist/index.js +4 -1
  64. package/dist/style.css +1 -1
  65. package/dist/transcoding/types/index.d.ts +11 -0
  66. package/dist/utils/LRUCache.d.ts +46 -0
  67. package/dist/utils/LRUCache.js +382 -1
  68. package/dist/utils/LRUCache.test.d.ts +1 -0
  69. package/package.json +2 -2
  70. package/src/elements/ContextProxiesController.ts +124 -0
  71. package/src/elements/EFCaptions.browsertest.ts +1820 -0
  72. package/src/elements/EFCaptions.ts +373 -36
  73. package/src/elements/EFImage.ts +4 -1
  74. package/src/elements/EFMedia/AssetIdMediaEngine.ts +30 -1
  75. package/src/elements/EFMedia/AssetMediaEngine.ts +33 -0
  76. package/src/elements/EFMedia/BaseMediaEngine.browsertest.ts +3 -8
  77. package/src/elements/EFMedia/BaseMediaEngine.ts +35 -0
  78. package/src/elements/EFMedia/JitMediaEngine.ts +34 -0
  79. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +6 -5
  80. package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +5 -0
  81. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +8 -5
  82. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +5 -5
  83. package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +11 -12
  84. package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +7 -4
  85. package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +5 -0
  86. package/src/elements/EFMedia/shared/AudioSpanUtils.ts +2 -2
  87. package/src/elements/EFMedia/shared/GlobalInputCache.ts +77 -0
  88. package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +2 -2
  89. package/src/elements/EFMedia/shared/RenditionHelpers.ts +2 -2
  90. package/src/elements/EFMedia/shared/ThumbnailExtractor.ts +227 -0
  91. package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +1 -1
  92. package/src/elements/EFMedia.ts +38 -1
  93. package/src/elements/EFSurface.browsertest.ts +155 -0
  94. package/src/elements/EFSurface.ts +141 -0
  95. package/src/elements/EFTemporal.ts +14 -8
  96. package/src/elements/EFThumbnailStrip.browsertest.ts +591 -0
  97. package/src/elements/EFThumbnailStrip.media-engine.browsertest.ts +713 -0
  98. package/src/elements/EFThumbnailStrip.ts +905 -0
  99. package/src/elements/EFTimegroup.browsertest.ts +56 -7
  100. package/src/elements/EFTimegroup.ts +88 -16
  101. package/src/elements/updateAnimations.browsertest.ts +333 -11
  102. package/src/elements/updateAnimations.ts +68 -19
  103. package/src/gui/ContextMixin.browsertest.ts +0 -25
  104. package/src/gui/ContextMixin.ts +44 -20
  105. package/src/gui/EFControls.browsertest.ts +175 -0
  106. package/src/gui/EFControls.ts +84 -0
  107. package/src/gui/EFFilmstrip.ts +323 -4
  108. package/src/gui/EFPreview.ts +2 -1
  109. package/src/gui/EFScrubber.ts +29 -25
  110. package/src/gui/EFTimeDisplay.browsertest.ts +237 -0
  111. package/src/gui/EFTimeDisplay.ts +12 -40
  112. package/src/gui/currentTimeContext.ts +5 -0
  113. package/src/gui/durationContext.ts +3 -0
  114. package/src/transcoding/types/index.ts +13 -0
  115. package/src/utils/LRUCache.test.ts +272 -0
  116. package/src/utils/LRUCache.ts +543 -0
  117. package/test/__cache__/GET__api_v1_transcode_high_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__26197f6f7c46cacb0a71134131c3f775/data.bin +0 -0
  118. package/test/__cache__/GET__api_v1_transcode_high_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__26197f6f7c46cacb0a71134131c3f775/metadata.json +1 -1
  119. package/test/__cache__/GET__api_v1_transcode_high_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__4cb6774cd3650ccf59c8f8dc6678c0b9/data.bin +0 -0
  120. package/test/__cache__/GET__api_v1_transcode_high_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__4cb6774cd3650ccf59c8f8dc6678c0b9/metadata.json +1 -1
  121. package/test/__cache__/GET__api_v1_transcode_high_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0b3b2b1c8933f7fcf8a9ecaa88d58b41/data.bin +0 -0
  122. package/test/__cache__/GET__api_v1_transcode_high_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0b3b2b1c8933f7fcf8a9ecaa88d58b41/metadata.json +1 -1
  123. package/test/__cache__/GET__api_v1_transcode_high_4_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a6fb05a22b18d850f7f2950bbcdbdeed/data.bin +0 -0
  124. package/test/__cache__/GET__api_v1_transcode_high_4_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a6fb05a22b18d850f7f2950bbcdbdeed/metadata.json +1 -1
  125. package/test/__cache__/GET__api_v1_transcode_high_5_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a50058c7c3602e90879fe3428ed891f4/data.bin +0 -0
  126. package/test/__cache__/GET__api_v1_transcode_high_5_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a50058c7c3602e90879fe3428ed891f4/metadata.json +1 -1
  127. package/test/__cache__/GET__api_v1_transcode_high_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0798c479b44aaeef850609a430f6e613/data.bin +0 -0
  128. package/test/__cache__/GET__api_v1_transcode_manifest_json_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__3be92a0437de726b431ed5af2369158a/data.bin +1 -1
  129. package/test/__cache__/GET__api_v1_transcode_manifest_json_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__3be92a0437de726b431ed5af2369158a/metadata.json +1 -1
  130. package/types.json +1 -1
  131. package/dist/transcoding/cache/CacheManager.d.ts +0 -73
  132. package/src/transcoding/cache/CacheManager.ts +0 -208
@@ -112,4 +112,385 @@ var SizeAwareLRUCache = class {
112
112
  return this.maxSizeBytes;
113
113
  }
114
114
  };
115
- export { LRUCache, SizeAwareLRUCache };
115
+ /**
116
+ * Red-Black Tree node colors
117
+ */
118
+ var Color = /* @__PURE__ */ function(Color$1) {
119
+ Color$1["RED"] = "RED";
120
+ Color$1["BLACK"] = "BLACK";
121
+ return Color$1;
122
+ }(Color || {});
123
+ /**
124
+ * Red-Black Tree node for ordered key storage
125
+ */
126
+ var RBTreeNode = class {
127
+ constructor(key, color = Color.RED, left = null, right = null, parent = null) {
128
+ this.key = key;
129
+ this.color = color;
130
+ this.left = left;
131
+ this.right = right;
132
+ this.parent = parent;
133
+ }
134
+ };
135
+ /**
136
+ * Red-Black Tree implementation for O(log n) operations
137
+ * Supports insert, delete, search, range queries, and nearest neighbor
138
+ */
139
+ var RedBlackTree = class {
140
+ constructor(compareFn) {
141
+ this.root = null;
142
+ this.compareFn = compareFn;
143
+ }
144
+ insert(key) {
145
+ const node = new RBTreeNode(key);
146
+ if (!this.root) {
147
+ this.root = node;
148
+ node.color = Color.BLACK;
149
+ return;
150
+ }
151
+ this.insertNode(node);
152
+ this.fixInsert(node);
153
+ }
154
+ delete(key) {
155
+ const node = this.findNode(key);
156
+ if (!node) return false;
157
+ this.deleteNode(node);
158
+ return true;
159
+ }
160
+ find(key) {
161
+ const node = this.findNode(key);
162
+ return node ? node.key : null;
163
+ }
164
+ findNearestInRange(center, distance) {
165
+ const start = this.subtractDistance(center, distance);
166
+ const end = this.addDistance(center, distance);
167
+ return this.findRange(start, end);
168
+ }
169
+ subtractDistance(center, distance) {
170
+ if (typeof center === "number" && typeof distance === "number") return center - distance;
171
+ return center;
172
+ }
173
+ addDistance(center, distance) {
174
+ if (typeof center === "number" && typeof distance === "number") return center + distance;
175
+ return center;
176
+ }
177
+ findRange(start, end) {
178
+ const result = [];
179
+ this.inorderRange(this.root, start, end, result);
180
+ return result;
181
+ }
182
+ getAllSorted() {
183
+ const result = [];
184
+ this.inorder(this.root, result);
185
+ return result;
186
+ }
187
+ findNode(key) {
188
+ let current = this.root;
189
+ while (current) {
190
+ const cmp = this.compareFn(key, current.key);
191
+ if (cmp === 0) return current;
192
+ current = cmp < 0 ? current.left : current.right;
193
+ }
194
+ return null;
195
+ }
196
+ insertNode(node) {
197
+ let parent = null;
198
+ let current = this.root;
199
+ while (current) {
200
+ parent = current;
201
+ const cmp = this.compareFn(node.key, current.key);
202
+ current = cmp < 0 ? current.left : current.right;
203
+ }
204
+ node.parent = parent;
205
+ if (!parent) this.root = node;
206
+ else {
207
+ const cmp = this.compareFn(node.key, parent.key);
208
+ if (cmp < 0) parent.left = node;
209
+ else parent.right = node;
210
+ }
211
+ }
212
+ fixInsert(node) {
213
+ while (node.parent && node.parent.color === Color.RED) if (node.parent === node.parent.parent?.left) {
214
+ const uncle = node.parent.parent.right;
215
+ if (uncle?.color === Color.RED) {
216
+ node.parent.color = Color.BLACK;
217
+ uncle.color = Color.BLACK;
218
+ node.parent.parent.color = Color.RED;
219
+ node = node.parent.parent;
220
+ } else {
221
+ if (node === node.parent.right) {
222
+ node = node.parent;
223
+ this.rotateLeft(node);
224
+ }
225
+ if (node.parent) {
226
+ node.parent.color = Color.BLACK;
227
+ if (node.parent.parent) {
228
+ node.parent.parent.color = Color.RED;
229
+ this.rotateRight(node.parent.parent);
230
+ }
231
+ }
232
+ }
233
+ } else {
234
+ const uncle = node.parent.parent?.left;
235
+ if (uncle?.color === Color.RED) {
236
+ node.parent.color = Color.BLACK;
237
+ uncle.color = Color.BLACK;
238
+ if (node.parent.parent) {
239
+ node.parent.parent.color = Color.RED;
240
+ node = node.parent.parent;
241
+ }
242
+ } else {
243
+ if (node === node.parent.left) {
244
+ node = node.parent;
245
+ this.rotateRight(node);
246
+ }
247
+ if (node.parent) {
248
+ node.parent.color = Color.BLACK;
249
+ if (node.parent.parent) {
250
+ node.parent.parent.color = Color.RED;
251
+ this.rotateLeft(node.parent.parent);
252
+ }
253
+ }
254
+ }
255
+ }
256
+ if (this.root) this.root.color = Color.BLACK;
257
+ }
258
+ deleteNode(node) {
259
+ let y = node;
260
+ let yOriginalColor = y.color;
261
+ let x;
262
+ if (!node.left) {
263
+ x = node.right;
264
+ this.transplant(node, node.right);
265
+ } else if (!node.right) {
266
+ x = node.left;
267
+ this.transplant(node, node.left);
268
+ } else {
269
+ y = this.minimum(node.right);
270
+ yOriginalColor = y.color;
271
+ x = y.right;
272
+ if (y.parent === node) {
273
+ if (x) x.parent = y;
274
+ } else {
275
+ this.transplant(y, y.right);
276
+ y.right = node.right;
277
+ if (y.right) y.right.parent = y;
278
+ }
279
+ this.transplant(node, y);
280
+ y.left = node.left;
281
+ if (y.left) y.left.parent = y;
282
+ y.color = node.color;
283
+ }
284
+ if (yOriginalColor === Color.BLACK && x) this.fixDelete(x);
285
+ }
286
+ fixDelete(node) {
287
+ while (node !== this.root && node.color === Color.BLACK) if (node === node.parent?.left) {
288
+ let sibling = node.parent.right;
289
+ if (sibling?.color === Color.RED) {
290
+ sibling.color = Color.BLACK;
291
+ node.parent.color = Color.RED;
292
+ this.rotateLeft(node.parent);
293
+ sibling = node.parent.right;
294
+ }
295
+ if (sibling?.left?.color !== Color.RED && sibling?.right?.color !== Color.RED) {
296
+ if (sibling) sibling.color = Color.RED;
297
+ node = node.parent;
298
+ } else {
299
+ if (sibling?.right?.color !== Color.RED) {
300
+ if (sibling.left) sibling.left.color = Color.BLACK;
301
+ sibling.color = Color.RED;
302
+ this.rotateRight(sibling);
303
+ sibling = node.parent.right;
304
+ }
305
+ if (sibling) {
306
+ sibling.color = node.parent.color;
307
+ node.parent.color = Color.BLACK;
308
+ if (sibling.right) sibling.right.color = Color.BLACK;
309
+ this.rotateLeft(node.parent);
310
+ }
311
+ if (!this.root) throw new Error("Root is null");
312
+ node = this.root;
313
+ }
314
+ } else {
315
+ let sibling = node.parent?.left;
316
+ if (sibling?.color === Color.RED) {
317
+ sibling.color = Color.BLACK;
318
+ if (node.parent) node.parent.color = Color.RED;
319
+ if (node.parent) this.rotateRight(node.parent);
320
+ sibling = node.parent?.left;
321
+ }
322
+ if (sibling?.right?.color !== Color.RED && sibling?.left?.color !== Color.RED) {
323
+ if (sibling) sibling.color = Color.RED;
324
+ if (node.parent === null) throw new Error("Node parent is null");
325
+ node = node.parent;
326
+ } else {
327
+ if (sibling?.left?.color !== Color.RED) {
328
+ if (sibling.right) sibling.right.color = Color.BLACK;
329
+ sibling.color = Color.RED;
330
+ this.rotateLeft(sibling);
331
+ sibling = node.parent?.left;
332
+ }
333
+ if (sibling) {
334
+ sibling.color = node.parent?.color || Color.BLACK;
335
+ if (node.parent) node.parent.color = Color.BLACK;
336
+ if (sibling.left) sibling.left.color = Color.BLACK;
337
+ if (node.parent) this.rotateRight(node.parent);
338
+ }
339
+ if (!this.root) throw new Error("Root is null");
340
+ node = this.root;
341
+ }
342
+ }
343
+ node.color = Color.BLACK;
344
+ }
345
+ rotateLeft(node) {
346
+ const rightChild = node.right;
347
+ if (!rightChild) throw new Error("Right child is null");
348
+ node.right = rightChild.left;
349
+ if (rightChild.left) rightChild.left.parent = node;
350
+ rightChild.parent = node.parent;
351
+ if (!node.parent) this.root = rightChild;
352
+ else if (node === node.parent.left) node.parent.left = rightChild;
353
+ else node.parent.right = rightChild;
354
+ rightChild.left = node;
355
+ node.parent = rightChild;
356
+ }
357
+ rotateRight(node) {
358
+ const leftChild = node.left;
359
+ if (!leftChild) throw new Error("Left child is null");
360
+ node.left = leftChild.right;
361
+ if (leftChild.right) leftChild.right.parent = node;
362
+ leftChild.parent = node.parent;
363
+ if (!node.parent) this.root = leftChild;
364
+ else if (node === node.parent.right) node.parent.right = leftChild;
365
+ else node.parent.left = leftChild;
366
+ leftChild.right = node;
367
+ node.parent = leftChild;
368
+ }
369
+ transplant(u, v) {
370
+ if (!u.parent) this.root = v;
371
+ else if (u === u.parent.left) u.parent.left = v;
372
+ else u.parent.right = v;
373
+ if (v) v.parent = u.parent;
374
+ }
375
+ minimum(node) {
376
+ while (node.left) node = node.left;
377
+ return node;
378
+ }
379
+ inorder(node, result) {
380
+ if (node) {
381
+ this.inorder(node.left, result);
382
+ result.push(node.key);
383
+ this.inorder(node.right, result);
384
+ }
385
+ }
386
+ inorderRange(node, start, end, result) {
387
+ if (!node) return;
388
+ const startCmp = this.compareFn(node.key, start);
389
+ const endCmp = this.compareFn(node.key, end);
390
+ if (startCmp > 0) this.inorderRange(node.left, start, end, result);
391
+ if (startCmp >= 0 && endCmp <= 0) result.push(node.key);
392
+ if (endCmp < 0) this.inorderRange(node.right, start, end, result);
393
+ }
394
+ };
395
+ /**
396
+ * LRU cache with binary search capabilities using Red-Black tree
397
+ * All operations are O(log n) for ordered queries and O(1) for LRU operations
398
+ */
399
+ var OrderedLRUCache = class {
400
+ constructor(maxSize, compareFn) {
401
+ this.cache = /* @__PURE__ */ new Map();
402
+ this.maxSize = maxSize;
403
+ this.compareFn = compareFn || ((a, b) => a < b ? -1 : a > b ? 1 : 0);
404
+ this.tree = new RedBlackTree(this.compareFn);
405
+ }
406
+ /**
407
+ * Get value by exact key (O(1))
408
+ */
409
+ get(key) {
410
+ const value = this.cache.get(key);
411
+ if (value) {
412
+ this.cache.delete(key);
413
+ this.cache.set(key, value);
414
+ }
415
+ return value;
416
+ }
417
+ /**
418
+ * Set key-value pair (O(log n) for tree operations, O(1) for cache)
419
+ */
420
+ set(key, value) {
421
+ const isUpdate = this.cache.has(key);
422
+ if (isUpdate) this.cache.delete(key);
423
+ else {
424
+ if (this.cache.size >= this.maxSize) {
425
+ const firstKey = this.cache.keys().next().value;
426
+ if (firstKey) {
427
+ this.cache.delete(firstKey);
428
+ this.tree.delete(firstKey);
429
+ }
430
+ }
431
+ this.tree.insert(key);
432
+ }
433
+ this.cache.set(key, value);
434
+ }
435
+ /**
436
+ * Find exact key using tree search (O(log n))
437
+ */
438
+ findExact(key) {
439
+ const foundKey = this.tree.find(key);
440
+ if (foundKey !== null) return this.get(key);
441
+ return void 0;
442
+ }
443
+ /**
444
+ * Find keys within distance of center point (O(log n + k) where k is result count)
445
+ * Returns empty array if no keys found in range
446
+ */
447
+ findNearestInRange(center, distance) {
448
+ const nearestKeys = this.tree.findNearestInRange(center, distance);
449
+ const result = [];
450
+ for (const key of nearestKeys) {
451
+ const value = this.get(key);
452
+ if (value !== void 0) result.push({
453
+ key,
454
+ value
455
+ });
456
+ }
457
+ return result;
458
+ }
459
+ /**
460
+ * Find all key-value pairs in range [start, end] (O(log n + k) where k is result count)
461
+ */
462
+ findRange(start, end) {
463
+ const keys = this.tree.findRange(start, end);
464
+ const result = [];
465
+ for (const key of keys) {
466
+ const value = this.get(key);
467
+ if (value !== void 0) result.push({
468
+ key,
469
+ value
470
+ });
471
+ }
472
+ return result;
473
+ }
474
+ /**
475
+ * Get all keys in sorted order (O(n))
476
+ */
477
+ getSortedKeys() {
478
+ return this.tree.getAllSorted();
479
+ }
480
+ has(key) {
481
+ return this.cache.has(key);
482
+ }
483
+ delete(key) {
484
+ const deleted = this.cache.delete(key);
485
+ if (deleted) this.tree.delete(key);
486
+ return deleted;
487
+ }
488
+ clear() {
489
+ this.cache.clear();
490
+ this.tree = new RedBlackTree(this.compareFn);
491
+ }
492
+ get size() {
493
+ return this.cache.size;
494
+ }
495
+ };
496
+ export { LRUCache, OrderedLRUCache, SizeAwareLRUCache };
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@editframe/elements",
3
- "version": "0.19.4-beta.0",
3
+ "version": "0.20.1-beta.0",
4
4
  "description": "",
5
5
  "exports": {
6
6
  ".": {
@@ -27,7 +27,7 @@
27
27
  "license": "UNLICENSED",
28
28
  "dependencies": {
29
29
  "@bramus/style-observer": "^1.3.0",
30
- "@editframe/assets": "0.19.4-beta.0",
30
+ "@editframe/assets": "0.20.1-beta.0",
31
31
  "@lit/context": "^1.1.2",
32
32
  "@lit/task": "^1.0.1",
33
33
  "d3": "^7.9.0",
@@ -0,0 +1,124 @@
1
+ import type { Context } from "@lit/context";
2
+ import { ContextEvent } from "@lit/context";
3
+ import type { LitElement, ReactiveController } from "lit";
4
+
5
+ /**
6
+ * Configuration for context proxying
7
+ */
8
+ export type ContextProxyConfig = {
9
+ target: () => HTMLElement | null;
10
+ contexts: Context<any, any>[];
11
+ };
12
+
13
+ /**
14
+ * A ReactiveController that proxies context requests to a target element.
15
+ *
16
+ * Usage:
17
+ * ```typescript
18
+ * @customElement('my-proxy')
19
+ * class MyProxy extends LitElement {
20
+ * @state()
21
+ * targetElement: HTMLElement | null = null;
22
+ *
23
+ * // @ts-expect-error controller is intentionally not referenced directly
24
+ * #contextProxyController = new ContextProxyController(this, {
25
+ * target: () => this.targetElement,
26
+ * contexts: [playingContext, loopContext, targetTimegroupContext]
27
+ * });
28
+ * }
29
+ * ```
30
+ */
31
+ export class ContextProxyController implements ReactiveController {
32
+ private host: LitElement;
33
+ private proxyMap = new Map<Context<any, any>, () => HTMLElement | null>();
34
+ private boundHandler: EventListener;
35
+ private pendingRequests: ContextEvent<any>[] = [];
36
+
37
+ constructor(host: LitElement, config: ContextProxyConfig) {
38
+ this.host = host;
39
+ this.host.addController(this);
40
+
41
+ // Create the bound handler once
42
+ this.boundHandler = this.handleContextRequest.bind(this);
43
+
44
+ // Build the proxy map
45
+ for (const context of config.contexts) {
46
+ this.proxyMap.set(context, config.target);
47
+ }
48
+ }
49
+
50
+ hostConnected(): void {
51
+ this.host.addEventListener("context-request", this.boundHandler);
52
+ }
53
+
54
+ hostDisconnected(): void {
55
+ this.host.removeEventListener("context-request", this.boundHandler);
56
+ }
57
+
58
+ hostUpdate(): void {
59
+ // Process any pending requests when the host updates (e.g., when targetElement changes)
60
+ this.processPendingRequests();
61
+ }
62
+
63
+ private processPendingRequests(): void {
64
+ if (this.pendingRequests.length === 0) return;
65
+
66
+ // Process all pending requests
67
+ const requestsToProcess = [...this.pendingRequests];
68
+ this.pendingRequests = [];
69
+
70
+ for (const contextEvent of requestsToProcess) {
71
+ this.processContextRequest(contextEvent);
72
+ }
73
+ }
74
+
75
+ private handleContextRequest(event: Event): void {
76
+ const contextEvent = event as ContextEvent<any>;
77
+
78
+ // Check if we should proxy this context
79
+ const targetGetter = this.proxyMap.get(contextEvent.context);
80
+
81
+ if (targetGetter) {
82
+ // Always stop propagation for contexts we handle
83
+ contextEvent.stopPropagation();
84
+
85
+ // Try to process the request immediately
86
+ const processed = this.processContextRequest(contextEvent);
87
+
88
+ if (!processed) {
89
+ this.pendingRequests.push(contextEvent);
90
+ }
91
+ }
92
+ }
93
+
94
+ private processContextRequest(contextEvent: ContextEvent<any>): boolean {
95
+ const targetGetter = this.proxyMap.get(contextEvent.context);
96
+ if (!targetGetter) return false;
97
+
98
+ // Get the target element using the getter function
99
+ const targetElement = targetGetter();
100
+
101
+ if (!targetElement) {
102
+ return false;
103
+ }
104
+
105
+ // Use temporary element approach for all requests (both one-time and subscriptions)
106
+ // Let Lit's context system handle subscription lifecycle properly
107
+ const tempElement = document.createElement("div");
108
+ targetElement.appendChild(tempElement);
109
+
110
+ try {
111
+ const newEvent = new ContextEvent(
112
+ contextEvent.context,
113
+ // @ts-ignore (this fails a typecheck but works)
114
+ contextEvent.callback,
115
+ contextEvent.subscribe,
116
+ );
117
+
118
+ tempElement.dispatchEvent(newEvent);
119
+ return true;
120
+ } finally {
121
+ targetElement.removeChild(tempElement);
122
+ }
123
+ }
124
+ }