@editframe/elements 0.19.2-beta.0 → 0.20.0-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.
- package/dist/elements/ContextProxiesController.d.ts +40 -0
- package/dist/elements/ContextProxiesController.js +69 -0
- package/dist/elements/EFCaptions.d.ts +45 -6
- package/dist/elements/EFCaptions.js +220 -26
- package/dist/elements/EFImage.js +4 -1
- package/dist/elements/EFMedia/AssetIdMediaEngine.d.ts +2 -1
- package/dist/elements/EFMedia/AssetIdMediaEngine.js +9 -0
- package/dist/elements/EFMedia/AssetMediaEngine.d.ts +1 -0
- package/dist/elements/EFMedia/AssetMediaEngine.js +11 -0
- package/dist/elements/EFMedia/BaseMediaEngine.d.ts +13 -1
- package/dist/elements/EFMedia/BaseMediaEngine.js +9 -0
- package/dist/elements/EFMedia/JitMediaEngine.d.ts +7 -1
- package/dist/elements/EFMedia/JitMediaEngine.js +24 -0
- package/dist/elements/EFMedia/shared/GlobalInputCache.d.ts +39 -0
- package/dist/elements/EFMedia/shared/GlobalInputCache.js +57 -0
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.d.ts +27 -0
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +106 -0
- package/dist/elements/EFMedia.js +25 -1
- package/dist/elements/EFSurface.browsertest.d.ts +0 -0
- package/dist/elements/EFSurface.d.ts +30 -0
- package/dist/elements/EFSurface.js +96 -0
- package/dist/elements/EFTemporal.js +7 -6
- package/dist/elements/EFThumbnailStrip.browsertest.d.ts +0 -0
- package/dist/elements/EFThumbnailStrip.d.ts +86 -0
- package/dist/elements/EFThumbnailStrip.js +490 -0
- package/dist/elements/EFThumbnailStrip.media-engine.browsertest.d.ts +0 -0
- package/dist/elements/EFTimegroup.d.ts +7 -7
- package/dist/elements/EFTimegroup.js +59 -16
- package/dist/elements/updateAnimations.browsertest.d.ts +13 -0
- package/dist/elements/updateAnimations.d.ts +5 -0
- package/dist/elements/updateAnimations.js +37 -13
- package/dist/getRenderInfo.js +1 -1
- package/dist/gui/ContextMixin.js +27 -14
- package/dist/gui/EFControls.browsertest.d.ts +0 -0
- package/dist/gui/EFControls.d.ts +38 -0
- package/dist/gui/EFControls.js +51 -0
- package/dist/gui/EFFilmstrip.d.ts +40 -1
- package/dist/gui/EFFilmstrip.js +240 -3
- package/dist/gui/EFPreview.js +2 -1
- package/dist/gui/EFScrubber.d.ts +6 -5
- package/dist/gui/EFScrubber.js +31 -21
- package/dist/gui/EFTimeDisplay.browsertest.d.ts +0 -0
- package/dist/gui/EFTimeDisplay.d.ts +2 -6
- package/dist/gui/EFTimeDisplay.js +13 -23
- package/dist/gui/TWMixin.js +1 -1
- package/dist/gui/currentTimeContext.d.ts +3 -0
- package/dist/gui/currentTimeContext.js +3 -0
- package/dist/gui/durationContext.d.ts +3 -0
- package/dist/gui/durationContext.js +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4 -1
- package/dist/style.css +1 -1
- package/dist/transcoding/types/index.d.ts +11 -0
- package/dist/utils/LRUCache.d.ts +46 -0
- package/dist/utils/LRUCache.js +382 -1
- package/dist/utils/LRUCache.test.d.ts +1 -0
- package/package.json +2 -2
- package/src/elements/ContextProxiesController.ts +123 -0
- package/src/elements/EFCaptions.browsertest.ts +1820 -0
- package/src/elements/EFCaptions.ts +373 -36
- package/src/elements/EFImage.ts +4 -1
- package/src/elements/EFMedia/AssetIdMediaEngine.ts +30 -1
- package/src/elements/EFMedia/AssetMediaEngine.ts +33 -0
- package/src/elements/EFMedia/BaseMediaEngine.browsertest.ts +3 -8
- package/src/elements/EFMedia/BaseMediaEngine.ts +35 -0
- package/src/elements/EFMedia/JitMediaEngine.ts +48 -0
- package/src/elements/EFMedia/shared/GlobalInputCache.ts +77 -0
- package/src/elements/EFMedia/shared/ThumbnailExtractor.ts +227 -0
- package/src/elements/EFMedia.ts +38 -1
- package/src/elements/EFSurface.browsertest.ts +155 -0
- package/src/elements/EFSurface.ts +141 -0
- package/src/elements/EFTemporal.ts +14 -8
- package/src/elements/EFThumbnailStrip.browsertest.ts +591 -0
- package/src/elements/EFThumbnailStrip.media-engine.browsertest.ts +713 -0
- package/src/elements/EFThumbnailStrip.ts +905 -0
- package/src/elements/EFTimegroup.browsertest.ts +56 -7
- package/src/elements/EFTimegroup.ts +88 -18
- package/src/elements/updateAnimations.browsertest.ts +361 -12
- package/src/elements/updateAnimations.ts +68 -19
- package/src/gui/ContextMixin.browsertest.ts +0 -25
- package/src/gui/ContextMixin.ts +44 -20
- package/src/gui/EFControls.browsertest.ts +175 -0
- package/src/gui/EFControls.ts +84 -0
- package/src/gui/EFFilmstrip.ts +323 -4
- package/src/gui/EFPreview.ts +2 -1
- package/src/gui/EFScrubber.ts +29 -25
- package/src/gui/EFTimeDisplay.browsertest.ts +237 -0
- package/src/gui/EFTimeDisplay.ts +12 -40
- package/src/gui/currentTimeContext.ts +5 -0
- package/src/gui/durationContext.ts +3 -0
- package/src/transcoding/types/index.ts +13 -0
- package/src/utils/LRUCache.test.ts +272 -0
- package/src/utils/LRUCache.ts +543 -0
- package/types.json +1 -1
- package/dist/transcoding/cache/CacheManager.d.ts +0 -73
- package/src/transcoding/cache/CacheManager.ts +0 -208
package/dist/utils/LRUCache.js
CHANGED
|
@@ -112,4 +112,385 @@ var SizeAwareLRUCache = class {
|
|
|
112
112
|
return this.maxSizeBytes;
|
|
113
113
|
}
|
|
114
114
|
};
|
|
115
|
-
|
|
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.
|
|
3
|
+
"version": "0.20.0-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.
|
|
30
|
+
"@editframe/assets": "0.20.0-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,123 @@
|
|
|
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
|
+
contextEvent.callback,
|
|
114
|
+
contextEvent.subscribe,
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
tempElement.dispatchEvent(newEvent);
|
|
118
|
+
return true;
|
|
119
|
+
} finally {
|
|
120
|
+
targetElement.removeChild(tempElement);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|