chat-layout 1.1.1 → 1.1.2
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/README.md +1 -0
- package/index.d.mts +3 -1
- package/index.mjs +77 -40
- package/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,6 +108,7 @@ Notes:
|
|
|
108
108
|
## Migration notes
|
|
109
109
|
|
|
110
110
|
- Use `memoRenderItemBy(keyOf, renderItem)` when list items are primitives.
|
|
111
|
+
- `memoRenderItemBy()` now uses a bounded LRU cache by default; pass `{ maxEntries: Infinity }` to keep the old unbounded behavior explicitly.
|
|
111
112
|
- `FlexItem` exposes `grow`, `shrink`, and `alignSelf`; `basis` is no longer public.
|
|
112
113
|
- `MultilineText` now uses `align` / `physicalAlign` instead of `alignment`.
|
|
113
114
|
- `ListState.position` uses `undefined` for the renderer default anchor.
|
package/index.d.mts
CHANGED
|
@@ -518,7 +518,9 @@ declare function memoRenderItem<C extends CanvasRenderingContext2D, T extends ob
|
|
|
518
518
|
/**
|
|
519
519
|
* Memoizes `renderItem` by a caller-provided cache key.
|
|
520
520
|
*/
|
|
521
|
-
declare function memoRenderItemBy<C extends CanvasRenderingContext2D, T, K>(keyOf: (item: T) => K, renderItem: (item: T) => Node<C
|
|
521
|
+
declare function memoRenderItemBy<C extends CanvasRenderingContext2D, T, K>(keyOf: (item: T) => K, renderItem: (item: T) => Node<C>, options?: {
|
|
522
|
+
maxEntries?: number;
|
|
523
|
+
}): ((item: T) => Node<C>) & {
|
|
522
524
|
reset: (item: T) => boolean;
|
|
523
525
|
resetKey: (key: K) => boolean;
|
|
524
526
|
};
|
package/index.mjs
CHANGED
|
@@ -818,17 +818,15 @@ var Place = class extends Wrapper {
|
|
|
818
818
|
};
|
|
819
819
|
const INTRINSIC_MAX_WIDTH = Number.POSITIVE_INFINITY;
|
|
820
820
|
const MIN_CONTENT_WIDTH_EPSILON = .001;
|
|
821
|
-
const fontShiftCache = /* @__PURE__ */ new Map();
|
|
822
|
-
const ellipsisWidthCache = /* @__PURE__ */ new Map();
|
|
823
821
|
let sharedGraphemeSegmenter;
|
|
824
|
-
function readLruValue(cache, key) {
|
|
822
|
+
function readLruValue$1(cache, key) {
|
|
825
823
|
const cached = cache.get(key);
|
|
826
824
|
if (cached == null) return;
|
|
827
825
|
cache.delete(key);
|
|
828
826
|
cache.set(key, cached);
|
|
829
827
|
return cached;
|
|
830
828
|
}
|
|
831
|
-
function writeLruValue(cache, key, value, capacity) {
|
|
829
|
+
function writeLruValue$1(cache, key, value, capacity) {
|
|
832
830
|
if (cache.has(key)) cache.delete(key);
|
|
833
831
|
else if (cache.size >= capacity) {
|
|
834
832
|
const firstKey = cache.keys().next().value;
|
|
@@ -838,17 +836,11 @@ function writeLruValue(cache, key, value, capacity) {
|
|
|
838
836
|
return value;
|
|
839
837
|
}
|
|
840
838
|
function measureFontShift(ctx) {
|
|
841
|
-
const font = ctx.graphics.font;
|
|
842
|
-
const cached = readLruValue(fontShiftCache, font);
|
|
843
|
-
if (cached != null) return cached;
|
|
844
839
|
const { fontBoundingBoxAscent: ascent = 0, fontBoundingBoxDescent: descent = 0 } = ctx.graphics.measureText("M");
|
|
845
|
-
return
|
|
840
|
+
return ascent - descent;
|
|
846
841
|
}
|
|
847
842
|
function measureEllipsisWidth(ctx) {
|
|
848
|
-
|
|
849
|
-
const cached = readLruValue(ellipsisWidthCache, font);
|
|
850
|
-
if (cached != null) return cached;
|
|
851
|
-
return writeLruValue(ellipsisWidthCache, font, ctx.graphics.measureText("…").width, 64);
|
|
843
|
+
return ctx.graphics.measureText("…").width;
|
|
852
844
|
}
|
|
853
845
|
function getGraphemeSegmenter() {
|
|
854
846
|
if (sharedGraphemeSegmenter !== void 0) return sharedGraphemeSegmenter;
|
|
@@ -936,15 +928,14 @@ const LINE_START_CURSOR$1 = {
|
|
|
936
928
|
graphemeIndex: 0
|
|
937
929
|
};
|
|
938
930
|
const preparedTextCache = /* @__PURE__ */ new Map();
|
|
939
|
-
const preparedUnitCache = /* @__PURE__ */ new WeakMap();
|
|
940
931
|
function getPreparedTextCacheKey(text, font, whiteSpace, wordBreak) {
|
|
941
932
|
return `${font}\u0000${whiteSpace}\u0000${wordBreak}\u0000${text}`;
|
|
942
933
|
}
|
|
943
934
|
function readPreparedText(text, font, whiteSpace, wordBreak) {
|
|
944
935
|
const key = getPreparedTextCacheKey(text, font, whiteSpace, wordBreak);
|
|
945
|
-
const cached = readLruValue(preparedTextCache, key);
|
|
936
|
+
const cached = readLruValue$1(preparedTextCache, key);
|
|
946
937
|
if (cached != null) return cached;
|
|
947
|
-
return writeLruValue(preparedTextCache, key, prepareWithSegments(text, font, {
|
|
938
|
+
return writeLruValue$1(preparedTextCache, key, prepareWithSegments(text, font, {
|
|
948
939
|
whiteSpace,
|
|
949
940
|
wordBreak
|
|
950
941
|
}), 512);
|
|
@@ -973,8 +964,6 @@ function measurePreparedMinContentWidth(prepared, overflowWrap = "break-word") {
|
|
|
973
964
|
return maxWidth > 0 ? maxWidth : maxAnyWidth;
|
|
974
965
|
}
|
|
975
966
|
function getPreparedUnits(prepared) {
|
|
976
|
-
const cached = preparedUnitCache.get(prepared);
|
|
977
|
-
if (cached != null) return cached;
|
|
978
967
|
const units = [];
|
|
979
968
|
for (let index = 0; index < prepared.segments.length; index += 1) {
|
|
980
969
|
const segment = prepared.segments[index] ?? "";
|
|
@@ -995,7 +984,6 @@ function getPreparedUnits(prepared) {
|
|
|
995
984
|
width: segmentWidth
|
|
996
985
|
});
|
|
997
986
|
}
|
|
998
|
-
preparedUnitCache.set(prepared, units);
|
|
999
987
|
return units;
|
|
1000
988
|
}
|
|
1001
989
|
function joinUnitText(units, start, end) {
|
|
@@ -1248,7 +1236,7 @@ function getRichPreparedCacheKey(spans, defaultFont) {
|
|
|
1248
1236
|
}
|
|
1249
1237
|
function readRichPrepared(spans, defaultFont) {
|
|
1250
1238
|
const key = getRichPreparedCacheKey(spans, defaultFont);
|
|
1251
|
-
const cached = readLruValue(richPreparedCache, key);
|
|
1239
|
+
const cached = readLruValue$1(richPreparedCache, key);
|
|
1252
1240
|
if (cached != null) return cached;
|
|
1253
1241
|
const items = spans.map((span) => ({
|
|
1254
1242
|
text: span.text,
|
|
@@ -1257,7 +1245,7 @@ function readRichPrepared(spans, defaultFont) {
|
|
|
1257
1245
|
extraWidth: span.extraWidth
|
|
1258
1246
|
}));
|
|
1259
1247
|
const preparedItemIndexBySourceItemIndex = buildPreparedItemIndexBySourceItemIndex(spans);
|
|
1260
|
-
return writeLruValue(richPreparedCache, key, {
|
|
1248
|
+
return writeLruValue$1(richPreparedCache, key, {
|
|
1261
1249
|
prepared: prepareRichInline(items),
|
|
1262
1250
|
preparedItemIndexBySourceItemIndex
|
|
1263
1251
|
}, RICH_PREPARED_CACHE_CAPACITY);
|
|
@@ -2155,27 +2143,56 @@ var DebugRenderer = class extends BaseRenderer {
|
|
|
2155
2143
|
}
|
|
2156
2144
|
};
|
|
2157
2145
|
//#endregion
|
|
2146
|
+
//#region src/renderer/weak-listeners.ts
|
|
2147
|
+
function pruneWeakListenerMap(listeners) {
|
|
2148
|
+
for (const [token, listener] of listeners) if (listener.ownerRef.deref() == null) listeners.delete(token);
|
|
2149
|
+
}
|
|
2150
|
+
function emitWeakListeners(listeners, event) {
|
|
2151
|
+
for (const [token, listener] of [...listeners]) {
|
|
2152
|
+
const owner = listener.ownerRef.deref();
|
|
2153
|
+
if (owner == null) {
|
|
2154
|
+
listeners.delete(token);
|
|
2155
|
+
continue;
|
|
2156
|
+
}
|
|
2157
|
+
listener.notify(owner, event);
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
//#endregion
|
|
2158
2161
|
//#region src/renderer/list-state.ts
|
|
2159
2162
|
const listStateListeners = /* @__PURE__ */ new WeakMap();
|
|
2163
|
+
const listStateListenerRegistry = typeof FinalizationRegistry === "function" ? new FinalizationRegistry(({ listRef, token }) => {
|
|
2164
|
+
const list = listRef.deref();
|
|
2165
|
+
if (list == null) return;
|
|
2166
|
+
deleteListStateListener(list, token);
|
|
2167
|
+
}) : null;
|
|
2168
|
+
function deleteListStateListener(list, token) {
|
|
2169
|
+
const listeners = listStateListeners.get(list);
|
|
2170
|
+
if (listeners == null) return;
|
|
2171
|
+
listeners.delete(token);
|
|
2172
|
+
if (listeners.size === 0) listStateListeners.delete(list);
|
|
2173
|
+
}
|
|
2160
2174
|
function emitListStateChange(list, change) {
|
|
2161
2175
|
const listeners = listStateListeners.get(list);
|
|
2162
|
-
if (listeners == null
|
|
2163
|
-
|
|
2176
|
+
if (listeners == null) return;
|
|
2177
|
+
emitWeakListeners(listeners, change);
|
|
2178
|
+
if (listeners.size === 0) listStateListeners.delete(list);
|
|
2164
2179
|
}
|
|
2165
|
-
function subscribeListState(list, listener) {
|
|
2180
|
+
function subscribeListState(list, owner, listener) {
|
|
2166
2181
|
const key = list;
|
|
2167
2182
|
let listeners = listStateListeners.get(key);
|
|
2168
2183
|
if (listeners == null) {
|
|
2169
|
-
listeners = /* @__PURE__ */ new
|
|
2184
|
+
listeners = /* @__PURE__ */ new Map();
|
|
2170
2185
|
listStateListeners.set(key, listeners);
|
|
2171
|
-
}
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2186
|
+
} else pruneWeakListenerMap(listeners);
|
|
2187
|
+
const token = Symbol();
|
|
2188
|
+
listeners.set(token, {
|
|
2189
|
+
ownerRef: new WeakRef(owner),
|
|
2190
|
+
notify: listener
|
|
2191
|
+
});
|
|
2192
|
+
listStateListenerRegistry?.register(owner, {
|
|
2193
|
+
listRef: new WeakRef(key),
|
|
2194
|
+
token
|
|
2195
|
+
});
|
|
2179
2196
|
}
|
|
2180
2197
|
var ListState = class {
|
|
2181
2198
|
#items;
|
|
@@ -2269,9 +2286,31 @@ var ListState = class {
|
|
|
2269
2286
|
};
|
|
2270
2287
|
//#endregion
|
|
2271
2288
|
//#region src/renderer/memo.ts
|
|
2289
|
+
const DEFAULT_MEMO_RENDER_ITEM_BY_MAX_ENTRIES = 512;
|
|
2272
2290
|
function isWeakMapKey(value) {
|
|
2273
2291
|
return typeof value === "object" && value !== null || typeof value === "function";
|
|
2274
2292
|
}
|
|
2293
|
+
function normalizeMaxEntries(maxEntries) {
|
|
2294
|
+
if (maxEntries === Number.POSITIVE_INFINITY) return Number.POSITIVE_INFINITY;
|
|
2295
|
+
if (maxEntries == null || !Number.isFinite(maxEntries)) return DEFAULT_MEMO_RENDER_ITEM_BY_MAX_ENTRIES;
|
|
2296
|
+
return Math.max(0, Math.trunc(maxEntries));
|
|
2297
|
+
}
|
|
2298
|
+
function readLruValue(cache, key) {
|
|
2299
|
+
const cached = cache.get(key);
|
|
2300
|
+
if (cached == null) return;
|
|
2301
|
+
cache.delete(key);
|
|
2302
|
+
cache.set(key, cached);
|
|
2303
|
+
return cached;
|
|
2304
|
+
}
|
|
2305
|
+
function writeLruValue(cache, key, value, maxEntries) {
|
|
2306
|
+
if (cache.has(key)) cache.delete(key);
|
|
2307
|
+
else if (Number.isFinite(maxEntries) && cache.size >= maxEntries) {
|
|
2308
|
+
const oldestKey = cache.keys().next().value;
|
|
2309
|
+
if (oldestKey != null) cache.delete(oldestKey);
|
|
2310
|
+
}
|
|
2311
|
+
if (maxEntries > 0) cache.set(key, value);
|
|
2312
|
+
return value;
|
|
2313
|
+
}
|
|
2275
2314
|
/**
|
|
2276
2315
|
* Memoizes `renderItem` by object identity.
|
|
2277
2316
|
*/
|
|
@@ -2291,15 +2330,14 @@ function memoRenderItem(renderItem) {
|
|
|
2291
2330
|
/**
|
|
2292
2331
|
* Memoizes `renderItem` by a caller-provided cache key.
|
|
2293
2332
|
*/
|
|
2294
|
-
function memoRenderItemBy(keyOf, renderItem) {
|
|
2333
|
+
function memoRenderItemBy(keyOf, renderItem, options = {}) {
|
|
2295
2334
|
const cache = /* @__PURE__ */ new Map();
|
|
2335
|
+
const maxEntries = normalizeMaxEntries(options.maxEntries);
|
|
2296
2336
|
function fn(item) {
|
|
2297
2337
|
const key = keyOf(item);
|
|
2298
|
-
const cached = cache
|
|
2338
|
+
const cached = readLruValue(cache, key);
|
|
2299
2339
|
if (cached != null) return cached;
|
|
2300
|
-
|
|
2301
|
-
cache.set(key, result);
|
|
2302
|
-
return result;
|
|
2340
|
+
return writeLruValue(cache, key, renderItem(item), maxEntries);
|
|
2303
2341
|
}
|
|
2304
2342
|
return Object.assign(fn, {
|
|
2305
2343
|
reset: (item) => cache.delete(keyOf(item)),
|
|
@@ -2341,11 +2379,10 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
2341
2379
|
#jumpAnimation;
|
|
2342
2380
|
#replacementAnimations = /* @__PURE__ */ new Map();
|
|
2343
2381
|
#nextReplacementLayerKey = 0;
|
|
2344
|
-
#unsubscribeListState;
|
|
2345
2382
|
constructor(graphics, options) {
|
|
2346
2383
|
super(graphics, options);
|
|
2347
|
-
|
|
2348
|
-
|
|
2384
|
+
subscribeListState(options.list, this, (owner, change) => {
|
|
2385
|
+
owner.#handleListStateChange(change);
|
|
2349
2386
|
});
|
|
2350
2387
|
}
|
|
2351
2388
|
/** Current anchor item index. */
|