capture-flow 0.0.0-stage → 0.1.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.
- package/LICENSE +21 -0
- package/README.md +256 -2
- package/bin/capture-flow.mjs +12 -0
- package/package.json +56 -4
- package/skills/capture-flow/SKILL.md +248 -0
- package/skills/capture-flow/references/capture-recipes.md +155 -0
- package/skills/capture-flow/references/store-sizes.md +91 -0
- package/src/build.ts +186 -0
- package/src/capture.ts +77 -0
- package/src/cli.ts +139 -0
- package/src/index.ts +11 -0
- package/src/manifest.ts +98 -0
- package/src/render.ts +75 -0
- package/src/verify.ts +90 -0
- package/vendor/wajha/LICENSE +21 -0
- package/vendor/wajha/NOTICE +21 -0
- package/vendor/wajha/engine/background.ts +55 -0
- package/vendor/wajha/engine/bidi.ts +589 -0
- package/vendor/wajha/engine/cache.ts +96 -0
- package/vendor/wajha/engine/color.ts +125 -0
- package/vendor/wajha/engine/devices/android.ts +53 -0
- package/vendor/wajha/engine/devices/browser.ts +170 -0
- package/vendor/wajha/engine/devices/finish.ts +154 -0
- package/vendor/wajha/engine/devices/geometry.ts +326 -0
- package/vendor/wajha/engine/devices/index.ts +143 -0
- package/vendor/wajha/engine/devices/ipad.ts +21 -0
- package/vendor/wajha/engine/devices/iphone.ts +55 -0
- package/vendor/wajha/engine/devices/macbook.ts +187 -0
- package/vendor/wajha/engine/devices/mock/apps/calendar.ts +145 -0
- package/vendor/wajha/engine/devices/mock/apps/chat.ts +213 -0
- package/vendor/wajha/engine/devices/mock/apps/finance.ts +164 -0
- package/vendor/wajha/engine/devices/mock/apps/fitness.ts +145 -0
- package/vendor/wajha/engine/devices/mock/apps/index.ts +14 -0
- package/vendor/wajha/engine/devices/mock/apps/meditation.ts +167 -0
- package/vendor/wajha/engine/devices/mock/apps/music.ts +152 -0
- package/vendor/wajha/engine/devices/mock/apps/shop.ts +116 -0
- package/vendor/wajha/engine/devices/mock/apps/social.ts +152 -0
- package/vendor/wajha/engine/devices/mock/apps/tasks.ts +131 -0
- package/vendor/wajha/engine/devices/mock/apps/travel.ts +121 -0
- package/vendor/wajha/engine/devices/mock/art.ts +557 -0
- package/vendor/wajha/engine/devices/mock/chrome.ts +337 -0
- package/vendor/wajha/engine/devices/mock/icons.ts +614 -0
- package/vendor/wajha/engine/devices/mock/kit.ts +213 -0
- package/vendor/wajha/engine/devices/mock/rng.ts +40 -0
- package/vendor/wajha/engine/devices/mock/theme.ts +133 -0
- package/vendor/wajha/engine/devices/mock/widgets.ts +231 -0
- package/vendor/wajha/engine/devices/paths.ts +113 -0
- package/vendor/wajha/engine/devices/screen.ts +75 -0
- package/vendor/wajha/engine/devices/shadow.ts +124 -0
- package/vendor/wajha/engine/devices/slab.ts +200 -0
- package/vendor/wajha/engine/devices/vision.ts +43 -0
- package/vendor/wajha/engine/devices/watch.ts +14 -0
- package/vendor/wajha/engine/geometry.ts +152 -0
- package/vendor/wajha/engine/images.ts +96 -0
- package/vendor/wajha/engine/index.ts +20 -0
- package/vendor/wajha/engine/linebreak.ts +226 -0
- package/vendor/wajha/engine/markup.ts +84 -0
- package/vendor/wajha/engine/mock.ts +158 -0
- package/vendor/wajha/engine/paint.ts +474 -0
- package/vendor/wajha/engine/patterns.ts +263 -0
- package/vendor/wajha/engine/render.ts +364 -0
- package/vendor/wajha/engine/shapes.ts +260 -0
- package/vendor/wajha/engine/testing/canvasNode.ts +121 -0
- package/vendor/wajha/engine/testing/fakeDevices.ts +85 -0
- package/vendor/wajha/engine/text.ts +694 -0
- package/vendor/wajha/engine/types.ts +190 -0
- package/vendor/wajha/export/paths.ts +197 -0
- package/vendor/wajha/export/png.ts +223 -0
- package/vendor/wajha/export/types.ts +60 -0
- package/vendor/wajha/model/adapt.ts +889 -0
- package/vendor/wajha/model/catalog.ts +369 -0
- package/vendor/wajha/model/clone.ts +58 -0
- package/vendor/wajha/model/colorMath.ts +128 -0
- package/vendor/wajha/model/defaults.ts +386 -0
- package/vendor/wajha/model/geometry.ts +95 -0
- package/vendor/wajha/model/ids.ts +13 -0
- package/vendor/wajha/model/instantiate.ts +280 -0
- package/vendor/wajha/model/mirror.ts +43 -0
- package/vendor/wajha/model/resolve.ts +87 -0
- package/vendor/wajha/model/rules.ts +249 -0
- package/vendor/wajha/model/stub.ts +4 -0
- package/vendor/wajha/model/types.ts +456 -0
- package/vendor/wajha/model/typography.ts +28 -0
- package/vendor/wajha/model/validate.ts +917 -0
- package/vendor/wajha/templates/builtin/aurora.ts +163 -0
- package/vendor/wajha/templates/builtin/bubblegum.ts +132 -0
- package/vendor/wajha/templates/builtin/citrusPop.ts +124 -0
- package/vendor/wajha/templates/builtin/clayGarden.ts +114 -0
- package/vendor/wajha/templates/builtin/desertDune.ts +97 -0
- package/vendor/wajha/templates/builtin/glassPrism.ts +139 -0
- package/vendor/wajha/templates/builtin/launchpad.ts +140 -0
- package/vendor/wajha/templates/builtin/loudAndClear.ts +109 -0
- package/vendor/wajha/templates/builtin/midnightNeon.ts +108 -0
- package/vendor/wajha/templates/builtin/monoSwiss.ts +149 -0
- package/vendor/wajha/templates/builtin/noirGold.ts +149 -0
- package/vendor/wajha/templates/builtin/noor.ts +107 -0
- package/vendor/wajha/templates/builtin/obsidian.ts +137 -0
- package/vendor/wajha/templates/builtin/panoramaPeaks.ts +145 -0
- package/vendor/wajha/templates/builtin/panoramaRibbon.ts +138 -0
- package/vendor/wajha/templates/builtin/panoramaWaves.ts +138 -0
- package/vendor/wajha/templates/builtin/paperAndInk.ts +98 -0
- package/vendor/wajha/templates/builtin/sage.ts +94 -0
- package/vendor/wajha/templates/builtin/splitTone.ts +129 -0
- package/vendor/wajha/templates/builtin/sunsetBoulevard.ts +127 -0
- package/vendor/wajha/templates/index.ts +55 -0
- package/vendor/wajha/templates/kit.ts +761 -0
- package/vendor/wajha/ui/sortShots.ts +239 -0
|
@@ -0,0 +1,589 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Just enough of the Unicode Bidirectional Algorithm (UAX #9) to order the
|
|
3
|
+
* runs of one line. The text painter never hands mixed-direction text to a
|
|
4
|
+
* single fillText: the browser can only reorder text inside each call, so
|
|
5
|
+
* when highlight markup splits a line into runs, "اشترك في Wajha *Pro*"
|
|
6
|
+
* would come out as "Pro Wajha"; and WebKit draws the direction runs of one
|
|
7
|
+
* call from left to right, carrying the vertical offset of a run's first
|
|
8
|
+
* glyph (Arabic marks and dots) over to the next run, so in one call
|
|
9
|
+
* "iOS 18 جديد كلياً" puts "iOS 18" well above the baseline of the Arabic
|
|
10
|
+
* words to its left. Resolving implicit levels here (rules W1-W7, BD16 and
|
|
11
|
+
* N0 for bracket pairs, N1-N2, I1-I2, L1-L2; no explicit embeddings or
|
|
12
|
+
* isolates, which slide copy does not use) lets the painter cut lines at
|
|
13
|
+
* direction changes and place the pieces in visual order.
|
|
14
|
+
*
|
|
15
|
+
* Character classes come from a compact range table covering Latin, Greek,
|
|
16
|
+
* Cyrillic, Hebrew, Arabic (incl. Persian/Urdu digits), CJK, emoji and the
|
|
17
|
+
* common punctuation and symbol blocks. Unlisted code points are L.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export const L = 0;
|
|
21
|
+
export const R = 1;
|
|
22
|
+
export const AL = 2;
|
|
23
|
+
export const EN = 3;
|
|
24
|
+
export const ES = 4;
|
|
25
|
+
export const ET = 5;
|
|
26
|
+
export const AN = 6;
|
|
27
|
+
export const CS = 7;
|
|
28
|
+
export const NSM = 8;
|
|
29
|
+
export const BN = 9;
|
|
30
|
+
export const B = 10;
|
|
31
|
+
export const S = 11;
|
|
32
|
+
export const WS = 12;
|
|
33
|
+
export const ON = 13;
|
|
34
|
+
|
|
35
|
+
export type BidiClass = number;
|
|
36
|
+
|
|
37
|
+
/** [first, last, class] — sorted, non-overlapping; everything else is L. */
|
|
38
|
+
export const BIDI_RANGES: readonly (readonly [number, number, BidiClass])[] = [
|
|
39
|
+
[0x0000, 0x0008, BN],
|
|
40
|
+
[0x0009, 0x0009, S],
|
|
41
|
+
[0x000a, 0x000a, B],
|
|
42
|
+
[0x000b, 0x000b, S],
|
|
43
|
+
[0x000c, 0x000c, WS],
|
|
44
|
+
[0x000d, 0x000d, B],
|
|
45
|
+
[0x000e, 0x001b, BN],
|
|
46
|
+
[0x001c, 0x001e, B],
|
|
47
|
+
[0x001f, 0x001f, S],
|
|
48
|
+
[0x0020, 0x0020, WS],
|
|
49
|
+
[0x0021, 0x0022, ON],
|
|
50
|
+
[0x0023, 0x0025, ET],
|
|
51
|
+
[0x0026, 0x002a, ON],
|
|
52
|
+
[0x002b, 0x002b, ES],
|
|
53
|
+
[0x002c, 0x002c, CS],
|
|
54
|
+
[0x002d, 0x002d, ES],
|
|
55
|
+
[0x002e, 0x002f, CS],
|
|
56
|
+
[0x0030, 0x0039, EN],
|
|
57
|
+
[0x003a, 0x003a, CS],
|
|
58
|
+
[0x003b, 0x0040, ON],
|
|
59
|
+
[0x005b, 0x0060, ON],
|
|
60
|
+
[0x007b, 0x007e, ON],
|
|
61
|
+
[0x007f, 0x0084, BN],
|
|
62
|
+
[0x0085, 0x0085, B],
|
|
63
|
+
[0x0086, 0x009f, BN],
|
|
64
|
+
[0x00a0, 0x00a0, CS],
|
|
65
|
+
[0x00a1, 0x00a1, ON],
|
|
66
|
+
[0x00a2, 0x00a5, ET],
|
|
67
|
+
[0x00a6, 0x00a9, ON],
|
|
68
|
+
[0x00ab, 0x00ac, ON],
|
|
69
|
+
[0x00ad, 0x00ad, BN],
|
|
70
|
+
[0x00ae, 0x00af, ON],
|
|
71
|
+
[0x00b0, 0x00b1, ET],
|
|
72
|
+
[0x00b2, 0x00b3, EN],
|
|
73
|
+
[0x00b4, 0x00b4, ON],
|
|
74
|
+
[0x00b6, 0x00b8, ON],
|
|
75
|
+
[0x00b9, 0x00b9, EN],
|
|
76
|
+
[0x00bb, 0x00bf, ON],
|
|
77
|
+
[0x00d7, 0x00d7, ON],
|
|
78
|
+
[0x00f7, 0x00f7, ON],
|
|
79
|
+
[0x02b9, 0x02ba, ON],
|
|
80
|
+
[0x02c2, 0x02cf, ON],
|
|
81
|
+
[0x02d2, 0x02df, ON],
|
|
82
|
+
[0x02e5, 0x02ed, ON],
|
|
83
|
+
[0x02ef, 0x02ff, ON],
|
|
84
|
+
[0x0300, 0x036f, NSM],
|
|
85
|
+
[0x0374, 0x0375, ON],
|
|
86
|
+
[0x037e, 0x037e, ON],
|
|
87
|
+
[0x0384, 0x0385, ON],
|
|
88
|
+
[0x0387, 0x0387, ON],
|
|
89
|
+
[0x03f6, 0x03f6, ON],
|
|
90
|
+
[0x0483, 0x0489, NSM],
|
|
91
|
+
[0x058a, 0x058a, ON],
|
|
92
|
+
[0x058d, 0x058e, ON],
|
|
93
|
+
[0x058f, 0x058f, ET],
|
|
94
|
+
[0x0590, 0x0590, R],
|
|
95
|
+
[0x0591, 0x05bd, NSM],
|
|
96
|
+
[0x05be, 0x05be, R],
|
|
97
|
+
[0x05bf, 0x05bf, NSM],
|
|
98
|
+
[0x05c0, 0x05c0, R],
|
|
99
|
+
[0x05c1, 0x05c2, NSM],
|
|
100
|
+
[0x05c3, 0x05c3, R],
|
|
101
|
+
[0x05c4, 0x05c5, NSM],
|
|
102
|
+
[0x05c6, 0x05c6, R],
|
|
103
|
+
[0x05c7, 0x05c7, NSM],
|
|
104
|
+
[0x05c8, 0x05ff, R],
|
|
105
|
+
[0x0600, 0x0605, AN],
|
|
106
|
+
[0x0606, 0x0607, ON],
|
|
107
|
+
[0x0608, 0x0608, AL],
|
|
108
|
+
[0x0609, 0x060a, ET],
|
|
109
|
+
[0x060b, 0x060b, AL],
|
|
110
|
+
[0x060c, 0x060c, CS],
|
|
111
|
+
[0x060d, 0x060d, AL],
|
|
112
|
+
[0x060e, 0x060f, ON],
|
|
113
|
+
[0x0610, 0x061a, NSM],
|
|
114
|
+
[0x061b, 0x064a, AL],
|
|
115
|
+
[0x064b, 0x065f, NSM],
|
|
116
|
+
[0x0660, 0x0669, AN],
|
|
117
|
+
[0x066a, 0x066a, ET],
|
|
118
|
+
[0x066b, 0x066c, AN],
|
|
119
|
+
[0x066d, 0x066f, AL],
|
|
120
|
+
[0x0670, 0x0670, NSM],
|
|
121
|
+
[0x0671, 0x06d5, AL],
|
|
122
|
+
[0x06d6, 0x06dc, NSM],
|
|
123
|
+
[0x06dd, 0x06dd, AN],
|
|
124
|
+
[0x06de, 0x06de, ON],
|
|
125
|
+
[0x06df, 0x06e4, NSM],
|
|
126
|
+
[0x06e5, 0x06e6, AL],
|
|
127
|
+
[0x06e7, 0x06e8, NSM],
|
|
128
|
+
[0x06e9, 0x06e9, ON],
|
|
129
|
+
[0x06ea, 0x06ed, NSM],
|
|
130
|
+
[0x06ee, 0x06ef, AL],
|
|
131
|
+
[0x06f0, 0x06f9, EN],
|
|
132
|
+
[0x06fa, 0x0710, AL],
|
|
133
|
+
[0x0711, 0x0711, NSM],
|
|
134
|
+
[0x0712, 0x072f, AL],
|
|
135
|
+
[0x0730, 0x074a, NSM],
|
|
136
|
+
[0x074b, 0x07a5, AL],
|
|
137
|
+
[0x07a6, 0x07b0, NSM],
|
|
138
|
+
[0x07b1, 0x07bf, AL],
|
|
139
|
+
[0x07c0, 0x07ea, R],
|
|
140
|
+
[0x07eb, 0x07f3, NSM],
|
|
141
|
+
[0x07f4, 0x07f5, R],
|
|
142
|
+
[0x07f6, 0x07f9, ON],
|
|
143
|
+
[0x07fa, 0x07fc, R],
|
|
144
|
+
[0x07fd, 0x07fd, NSM],
|
|
145
|
+
[0x07fe, 0x0815, R],
|
|
146
|
+
[0x0816, 0x082d, NSM],
|
|
147
|
+
[0x082e, 0x0858, R],
|
|
148
|
+
[0x0859, 0x085b, NSM],
|
|
149
|
+
[0x085c, 0x085f, R],
|
|
150
|
+
[0x0860, 0x088f, AL],
|
|
151
|
+
[0x0890, 0x0891, AN],
|
|
152
|
+
[0x0892, 0x0897, AL],
|
|
153
|
+
[0x0898, 0x089f, NSM],
|
|
154
|
+
[0x08a0, 0x08c9, AL],
|
|
155
|
+
[0x08ca, 0x08e1, NSM],
|
|
156
|
+
[0x08e2, 0x08e2, AN],
|
|
157
|
+
[0x08e3, 0x08ff, NSM],
|
|
158
|
+
[0x0e3f, 0x0e3f, ET],
|
|
159
|
+
[0x1680, 0x1680, WS],
|
|
160
|
+
[0x180e, 0x180e, BN],
|
|
161
|
+
[0x1ab0, 0x1aff, NSM],
|
|
162
|
+
[0x1dc0, 0x1dff, NSM],
|
|
163
|
+
[0x2000, 0x200a, WS],
|
|
164
|
+
[0x200b, 0x200d, BN],
|
|
165
|
+
[0x200f, 0x200f, R],
|
|
166
|
+
[0x2010, 0x2027, ON],
|
|
167
|
+
[0x2028, 0x2028, WS],
|
|
168
|
+
[0x2029, 0x2029, B],
|
|
169
|
+
[0x202a, 0x202e, BN],
|
|
170
|
+
[0x202f, 0x202f, CS],
|
|
171
|
+
[0x2030, 0x2034, ET],
|
|
172
|
+
[0x2035, 0x2043, ON],
|
|
173
|
+
[0x2044, 0x2044, CS],
|
|
174
|
+
[0x2045, 0x205e, ON],
|
|
175
|
+
[0x205f, 0x205f, WS],
|
|
176
|
+
[0x2060, 0x206f, BN],
|
|
177
|
+
[0x2070, 0x2070, EN],
|
|
178
|
+
[0x2074, 0x2079, EN],
|
|
179
|
+
[0x207a, 0x207b, ES],
|
|
180
|
+
[0x207c, 0x207e, ON],
|
|
181
|
+
[0x2080, 0x2089, EN],
|
|
182
|
+
[0x208a, 0x208b, ES],
|
|
183
|
+
[0x208c, 0x208e, ON],
|
|
184
|
+
[0x20a0, 0x20cf, ET],
|
|
185
|
+
[0x20d0, 0x20f0, NSM],
|
|
186
|
+
[0x2100, 0x2101, ON],
|
|
187
|
+
[0x2103, 0x2106, ON],
|
|
188
|
+
[0x2108, 0x2109, ON],
|
|
189
|
+
[0x2114, 0x2114, ON],
|
|
190
|
+
[0x2116, 0x2118, ON],
|
|
191
|
+
[0x211e, 0x2123, ON],
|
|
192
|
+
[0x2125, 0x2125, ON],
|
|
193
|
+
[0x2127, 0x2127, ON],
|
|
194
|
+
[0x2129, 0x2129, ON],
|
|
195
|
+
[0x212e, 0x212e, ET],
|
|
196
|
+
[0x213a, 0x213b, ON],
|
|
197
|
+
[0x2140, 0x2144, ON],
|
|
198
|
+
[0x214a, 0x214d, ON],
|
|
199
|
+
[0x2150, 0x215f, ON],
|
|
200
|
+
[0x2189, 0x218b, ON],
|
|
201
|
+
[0x2190, 0x2211, ON],
|
|
202
|
+
[0x2212, 0x2212, ES],
|
|
203
|
+
[0x2213, 0x2213, ET],
|
|
204
|
+
[0x2214, 0x2335, ON],
|
|
205
|
+
[0x237b, 0x2394, ON],
|
|
206
|
+
[0x2396, 0x2487, ON],
|
|
207
|
+
[0x2488, 0x249b, EN],
|
|
208
|
+
[0x24ea, 0x26ab, ON],
|
|
209
|
+
[0x26ad, 0x27ff, ON],
|
|
210
|
+
[0x2900, 0x2b73, ON],
|
|
211
|
+
[0x2b76, 0x2bff, ON],
|
|
212
|
+
[0x2ce5, 0x2cea, ON],
|
|
213
|
+
[0x2cef, 0x2cf1, NSM],
|
|
214
|
+
[0x2cf9, 0x2cff, ON],
|
|
215
|
+
[0x2d7f, 0x2d7f, NSM],
|
|
216
|
+
[0x2de0, 0x2dff, NSM],
|
|
217
|
+
[0x2e00, 0x2e7f, ON],
|
|
218
|
+
[0x2e80, 0x2ffb, ON],
|
|
219
|
+
[0x3000, 0x3000, WS],
|
|
220
|
+
[0x3001, 0x3004, ON],
|
|
221
|
+
[0x3008, 0x3020, ON],
|
|
222
|
+
[0x302a, 0x302d, NSM],
|
|
223
|
+
[0x3030, 0x3030, ON],
|
|
224
|
+
[0x3036, 0x3037, ON],
|
|
225
|
+
[0x303d, 0x303f, ON],
|
|
226
|
+
[0x3099, 0x309a, NSM],
|
|
227
|
+
[0x309b, 0x309c, ON],
|
|
228
|
+
[0x30a0, 0x30a0, ON],
|
|
229
|
+
[0x30fb, 0x30fb, ON],
|
|
230
|
+
[0x31c0, 0x31e3, ON],
|
|
231
|
+
[0x321d, 0x321e, ON],
|
|
232
|
+
[0x3250, 0x325f, ON],
|
|
233
|
+
[0x327c, 0x327e, ON],
|
|
234
|
+
[0x32b1, 0x32bf, ON],
|
|
235
|
+
[0x32cc, 0x32cf, ON],
|
|
236
|
+
[0x3377, 0x337a, ON],
|
|
237
|
+
[0x33de, 0x33df, ON],
|
|
238
|
+
[0x33ff, 0x33ff, ON],
|
|
239
|
+
[0x4dc0, 0x4dff, ON],
|
|
240
|
+
[0xa490, 0xa4c6, ON],
|
|
241
|
+
[0xa60d, 0xa60f, ON],
|
|
242
|
+
[0xa66f, 0xa672, NSM],
|
|
243
|
+
[0xa673, 0xa673, ON],
|
|
244
|
+
[0xa674, 0xa67d, NSM],
|
|
245
|
+
[0xa67e, 0xa67f, ON],
|
|
246
|
+
[0xa69e, 0xa69f, NSM],
|
|
247
|
+
[0xa6f0, 0xa6f1, NSM],
|
|
248
|
+
[0xa700, 0xa721, ON],
|
|
249
|
+
[0xa788, 0xa788, ON],
|
|
250
|
+
[0xfb1d, 0xfb1d, R],
|
|
251
|
+
[0xfb1e, 0xfb1e, NSM],
|
|
252
|
+
[0xfb1f, 0xfb28, R],
|
|
253
|
+
[0xfb29, 0xfb29, ES],
|
|
254
|
+
[0xfb2a, 0xfb4f, R],
|
|
255
|
+
[0xfb50, 0xfd3d, AL],
|
|
256
|
+
[0xfd3e, 0xfd4f, ON],
|
|
257
|
+
[0xfd50, 0xfdce, AL],
|
|
258
|
+
[0xfdcf, 0xfdcf, ON],
|
|
259
|
+
[0xfdd0, 0xfdef, BN],
|
|
260
|
+
[0xfdf0, 0xfdfc, AL],
|
|
261
|
+
[0xfdfd, 0xfdff, ON],
|
|
262
|
+
[0xfe00, 0xfe0f, NSM],
|
|
263
|
+
[0xfe10, 0xfe19, ON],
|
|
264
|
+
[0xfe20, 0xfe2f, NSM],
|
|
265
|
+
[0xfe30, 0xfe4f, ON],
|
|
266
|
+
[0xfe50, 0xfe50, CS],
|
|
267
|
+
[0xfe51, 0xfe51, ON],
|
|
268
|
+
[0xfe52, 0xfe52, CS],
|
|
269
|
+
[0xfe54, 0xfe54, ON],
|
|
270
|
+
[0xfe55, 0xfe55, CS],
|
|
271
|
+
[0xfe56, 0xfe5e, ON],
|
|
272
|
+
[0xfe5f, 0xfe5f, ET],
|
|
273
|
+
[0xfe60, 0xfe61, ON],
|
|
274
|
+
[0xfe62, 0xfe63, ES],
|
|
275
|
+
[0xfe64, 0xfe66, ON],
|
|
276
|
+
[0xfe68, 0xfe68, ON],
|
|
277
|
+
[0xfe69, 0xfe6a, ET],
|
|
278
|
+
[0xfe6b, 0xfe6b, ON],
|
|
279
|
+
[0xfe70, 0xfefe, AL],
|
|
280
|
+
[0xfeff, 0xfeff, BN],
|
|
281
|
+
[0xff01, 0xff02, ON],
|
|
282
|
+
[0xff03, 0xff05, ET],
|
|
283
|
+
[0xff06, 0xff0a, ON],
|
|
284
|
+
[0xff0b, 0xff0b, ES],
|
|
285
|
+
[0xff0c, 0xff0c, CS],
|
|
286
|
+
[0xff0d, 0xff0d, ES],
|
|
287
|
+
[0xff0e, 0xff0f, CS],
|
|
288
|
+
[0xff10, 0xff19, EN],
|
|
289
|
+
[0xff1a, 0xff1a, CS],
|
|
290
|
+
[0xff1b, 0xff20, ON],
|
|
291
|
+
[0xff3b, 0xff40, ON],
|
|
292
|
+
[0xff5b, 0xff65, ON],
|
|
293
|
+
[0xffe0, 0xffe1, ET],
|
|
294
|
+
[0xffe2, 0xffe4, ON],
|
|
295
|
+
[0xffe5, 0xffe6, ET],
|
|
296
|
+
[0xffe8, 0xffee, ON],
|
|
297
|
+
[0xfff9, 0xfffd, ON],
|
|
298
|
+
[0x10800, 0x10cff, R],
|
|
299
|
+
[0x10d00, 0x10d23, AL],
|
|
300
|
+
[0x10d24, 0x10d27, NSM],
|
|
301
|
+
[0x10d28, 0x10d2f, AL],
|
|
302
|
+
[0x10d30, 0x10d39, AN],
|
|
303
|
+
[0x10d3a, 0x10e5f, R],
|
|
304
|
+
[0x10e60, 0x10e7e, AN],
|
|
305
|
+
[0x10e7f, 0x10f2f, R],
|
|
306
|
+
[0x10f30, 0x10f45, AL],
|
|
307
|
+
[0x10f46, 0x10f50, NSM],
|
|
308
|
+
[0x10f51, 0x10f6f, AL],
|
|
309
|
+
[0x10f70, 0x10fff, R],
|
|
310
|
+
[0x1e800, 0x1ec6f, R],
|
|
311
|
+
[0x1ec70, 0x1ecbf, AL],
|
|
312
|
+
[0x1ecc0, 0x1ecff, R],
|
|
313
|
+
[0x1ed00, 0x1ed4f, AL],
|
|
314
|
+
[0x1ed50, 0x1edff, R],
|
|
315
|
+
[0x1ee00, 0x1eeef, AL],
|
|
316
|
+
[0x1eef0, 0x1eef1, ON],
|
|
317
|
+
[0x1eef2, 0x1eeff, AL],
|
|
318
|
+
[0x1ef00, 0x1efff, R],
|
|
319
|
+
[0x1f000, 0x1f0ff, ON],
|
|
320
|
+
[0x1f100, 0x1f10a, EN],
|
|
321
|
+
[0x1f10b, 0x1f10f, ON],
|
|
322
|
+
[0x1f12f, 0x1f12f, ON],
|
|
323
|
+
[0x1f16a, 0x1f16f, ON],
|
|
324
|
+
[0x1f1ad, 0x1f1ad, ON],
|
|
325
|
+
[0x1f260, 0x1f265, ON],
|
|
326
|
+
[0x1f300, 0x1fbff, ON],
|
|
327
|
+
[0xe0001, 0xe007f, BN],
|
|
328
|
+
[0xe0100, 0xe01ef, NSM],
|
|
329
|
+
];
|
|
330
|
+
|
|
331
|
+
/** Bidi class of a code point (simplified, see BIDI_RANGES). */
|
|
332
|
+
export function bidiClass(cp: number): BidiClass {
|
|
333
|
+
// Fast path for ASCII letters, the bulk of Latin copy.
|
|
334
|
+
if ((cp >= 0x41 && cp <= 0x5a) || (cp >= 0x61 && cp <= 0x7a)) return L;
|
|
335
|
+
let lo = 0;
|
|
336
|
+
let hi = BIDI_RANGES.length - 1;
|
|
337
|
+
while (lo <= hi) {
|
|
338
|
+
const mid = (lo + hi) >> 1;
|
|
339
|
+
const r = BIDI_RANGES[mid];
|
|
340
|
+
if (cp < r[0]) hi = mid - 1;
|
|
341
|
+
else if (cp > r[1]) lo = mid + 1;
|
|
342
|
+
else return r[2];
|
|
343
|
+
}
|
|
344
|
+
return L;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/** Which strong directions occur in a string (R covers AL too). */
|
|
348
|
+
export function strongDirections(text: string): { ltr: boolean; rtl: boolean } {
|
|
349
|
+
let ltr = false;
|
|
350
|
+
let rtl = false;
|
|
351
|
+
for (let i = 0; i < text.length; i++) {
|
|
352
|
+
const cp = text.codePointAt(i) ?? 0;
|
|
353
|
+
if (cp > 0xffff) i++;
|
|
354
|
+
const c = bidiClass(cp);
|
|
355
|
+
if (c === L) ltr = true;
|
|
356
|
+
else if (c === R || c === AL) rtl = true;
|
|
357
|
+
if (ltr && rtl) break;
|
|
358
|
+
}
|
|
359
|
+
return { ltr, rtl };
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* True when every character of a line resolves to the paragraph level, so
|
|
364
|
+
* the line reads in one direction and needs no reordering (the same answer
|
|
365
|
+
* as resolveLevels, without resolving): an RTL line has no L, EN or AN
|
|
366
|
+
* characters, an LTR line no R, AL or AN.
|
|
367
|
+
*/
|
|
368
|
+
export function isUnidirectional(text: string, rtl: boolean): boolean {
|
|
369
|
+
for (let i = 0; i < text.length; i++) {
|
|
370
|
+
const cp = text.codePointAt(i) ?? 0;
|
|
371
|
+
if (cp > 0xffff) i++;
|
|
372
|
+
const c = bidiClass(cp);
|
|
373
|
+
if (c === AN || (rtl ? c === L || c === EN : c === R || c === AL)) return false;
|
|
374
|
+
}
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/** Classes per UTF-16 code unit; both halves of a surrogate pair get the pair's class. */
|
|
379
|
+
function classify(text: string): Uint8Array {
|
|
380
|
+
const out = new Uint8Array(text.length);
|
|
381
|
+
for (let i = 0; i < text.length; i++) {
|
|
382
|
+
const cp = text.codePointAt(i) ?? 0;
|
|
383
|
+
const c = bidiClass(cp);
|
|
384
|
+
out[i] = c;
|
|
385
|
+
if (cp > 0xffff && i + 1 < text.length) out[++i] = c;
|
|
386
|
+
}
|
|
387
|
+
return out;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const isNeutral = (t: number) => t === B || t === S || t === WS || t === ON;
|
|
391
|
+
|
|
392
|
+
/** Opening paired bracket -> its closing bracket (BidiBrackets.txt, the brackets the class table marks ON). */
|
|
393
|
+
const CLOSER = new Map<number, number>([
|
|
394
|
+
[0x005b, 0x005d],
|
|
395
|
+
[0x007b, 0x007d],
|
|
396
|
+
[0xff3b, 0xff3d],
|
|
397
|
+
[0xff5b, 0xff5d],
|
|
398
|
+
]);
|
|
399
|
+
// Blocks where every opening bracket is directly followed by its closing one.
|
|
400
|
+
for (const [first, last] of [
|
|
401
|
+
[0x0028, 0x0029], [0x2045, 0x2046], [0x207d, 0x207e], [0x208d, 0x208e], [0x2308, 0x230b], [0x2329, 0x232a],
|
|
402
|
+
[0x2768, 0x2775], [0x27c5, 0x27c6], [0x27e6, 0x27ef], [0x2983, 0x2998], [0x29d8, 0x29db], [0x29fc, 0x29fd],
|
|
403
|
+
[0x2e22, 0x2e29], [0x2e55, 0x2e5c], [0x3008, 0x3011], [0x3014, 0x301b], [0xfe59, 0xfe5e], [0xff08, 0xff09],
|
|
404
|
+
[0xff5f, 0xff60], [0xff62, 0xff63],
|
|
405
|
+
]) {
|
|
406
|
+
for (let cp = first; cp < last; cp += 2) CLOSER.set(cp, cp + 1);
|
|
407
|
+
}
|
|
408
|
+
const CLOSERS = new Set(CLOSER.values());
|
|
409
|
+
/** U+2329/U+232A are canonically equivalent to U+3008/U+3009 and pair with them. */
|
|
410
|
+
const canonicalBracket = (cp: number) => (cp === 0x2329 ? 0x3008 : cp === 0x232a ? 0x3009 : cp);
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Bracket pairs of a line (rule BD16), sorted by the opening bracket. Only
|
|
414
|
+
* brackets still typed ON take part; at most 63 may be open at once.
|
|
415
|
+
*/
|
|
416
|
+
function bracketPairs(text: string, t: Uint8Array): [number, number][] {
|
|
417
|
+
const pairs: [number, number][] = [];
|
|
418
|
+
const open: { close: number; at: number }[] = [];
|
|
419
|
+
for (let i = 0; i < text.length; i++) {
|
|
420
|
+
if (t[i] !== ON) continue;
|
|
421
|
+
const cp = text.charCodeAt(i);
|
|
422
|
+
const close = CLOSER.get(cp);
|
|
423
|
+
if (close !== undefined) {
|
|
424
|
+
if (open.length === 63) break;
|
|
425
|
+
open.push({ close: canonicalBracket(close), at: i });
|
|
426
|
+
} else if (CLOSERS.has(cp)) {
|
|
427
|
+
const c = canonicalBracket(cp);
|
|
428
|
+
for (let k = open.length - 1; k >= 0; k--) {
|
|
429
|
+
if (open[k].close !== c) continue;
|
|
430
|
+
pairs.push([open[k].at, i]);
|
|
431
|
+
open.length = k;
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return pairs.sort((a, b) => a[0] - b[0]);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Embedding level of every UTF-16 code unit of one line of text, for a
|
|
441
|
+
* paragraph of the given base direction (0 = LTR, 1 = RTL).
|
|
442
|
+
*/
|
|
443
|
+
export function resolveLevels(text: string, rtl: boolean): Uint8Array {
|
|
444
|
+
const n = text.length;
|
|
445
|
+
const orig = classify(text);
|
|
446
|
+
const t = orig.slice();
|
|
447
|
+
const sos = rtl ? R : L;
|
|
448
|
+
|
|
449
|
+
// W1: non-spacing marks (and ignorable format characters) take the previous type.
|
|
450
|
+
let prev: number = sos;
|
|
451
|
+
for (let i = 0; i < n; i++) {
|
|
452
|
+
if (t[i] === NSM || t[i] === BN) t[i] = prev;
|
|
453
|
+
else prev = t[i];
|
|
454
|
+
}
|
|
455
|
+
// W2: European digits after Arabic letters are Arabic digits. W3: AL becomes R.
|
|
456
|
+
let strong: number = sos;
|
|
457
|
+
for (let i = 0; i < n; i++) {
|
|
458
|
+
const c = t[i];
|
|
459
|
+
if (c === EN && strong === AL) t[i] = AN;
|
|
460
|
+
if (c === L || c === R || c === AL) strong = c;
|
|
461
|
+
}
|
|
462
|
+
for (let i = 0; i < n; i++) if (t[i] === AL) t[i] = R;
|
|
463
|
+
// W4: a single separator between two numbers joins them.
|
|
464
|
+
for (let i = 1; i < n - 1; i++) {
|
|
465
|
+
const c = t[i];
|
|
466
|
+
if (c !== ES && c !== CS) continue;
|
|
467
|
+
const a = t[i - 1];
|
|
468
|
+
const b = t[i + 1];
|
|
469
|
+
if (a === EN && b === EN) t[i] = EN;
|
|
470
|
+
else if (c === CS && a === AN && b === AN) t[i] = AN;
|
|
471
|
+
}
|
|
472
|
+
// W5: terminators (%, $, °) next to European numbers belong to them.
|
|
473
|
+
for (let i = 0; i < n; ) {
|
|
474
|
+
if (t[i] !== ET) {
|
|
475
|
+
i++;
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
let j = i;
|
|
479
|
+
while (j < n && t[j] === ET) j++;
|
|
480
|
+
if ((i > 0 && t[i - 1] === EN) || (j < n && t[j] === EN)) for (let k = i; k < j; k++) t[k] = EN;
|
|
481
|
+
i = j;
|
|
482
|
+
}
|
|
483
|
+
// W6: leftover separators and terminators are neutral.
|
|
484
|
+
for (let i = 0; i < n; i++) if (t[i] === ES || t[i] === ET || t[i] === CS) t[i] = ON;
|
|
485
|
+
// W7: European numbers in a left-to-right context behave as L.
|
|
486
|
+
strong = sos;
|
|
487
|
+
for (let i = 0; i < n; i++) {
|
|
488
|
+
const c = t[i];
|
|
489
|
+
if (c === EN && strong === L) t[i] = L;
|
|
490
|
+
if (c === L || c === R) strong = c;
|
|
491
|
+
}
|
|
492
|
+
// N0: a bracket pair takes the paragraph direction when the text inside it has that
|
|
493
|
+
// direction, else the direction of what it encloses if the text before it runs the same
|
|
494
|
+
// way: "جديد Wajha (Pro)" keeps both brackets with "Pro", as text fields show it.
|
|
495
|
+
// Numbers count as R here.
|
|
496
|
+
const strongOf = (c: number) => (c === L ? L : c === R || c === EN || c === AN ? R : -1);
|
|
497
|
+
for (const [open, close] of bracketPairs(text, t)) {
|
|
498
|
+
let dir = -1;
|
|
499
|
+
let opposite = false;
|
|
500
|
+
for (let k = open + 1; k < close && dir < 0; k++) {
|
|
501
|
+
const s = strongOf(t[k]);
|
|
502
|
+
if (s === sos) dir = sos;
|
|
503
|
+
else if (s >= 0) opposite = true;
|
|
504
|
+
}
|
|
505
|
+
if (dir < 0 && opposite) {
|
|
506
|
+
// The first strong type before the pair: the opposite direction if it continues
|
|
507
|
+
// the bracketed text, else the paragraph direction (sos when there is none).
|
|
508
|
+
dir = sos;
|
|
509
|
+
for (let k = open - 1; k >= 0; k--) {
|
|
510
|
+
const s = strongOf(t[k]);
|
|
511
|
+
if (s >= 0) {
|
|
512
|
+
dir = s;
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
if (dir < 0) continue;
|
|
518
|
+
t[open] = dir;
|
|
519
|
+
t[close] = dir;
|
|
520
|
+
// Marks on a bracket follow it (W1 gave them the bracket's old type).
|
|
521
|
+
for (let k = open + 1; k < n && orig[k] === NSM; k++) t[k] = dir;
|
|
522
|
+
for (let k = close + 1; k < n && orig[k] === NSM; k++) t[k] = dir;
|
|
523
|
+
}
|
|
524
|
+
// N1/N2: neutrals between same-direction text take it, else the paragraph direction.
|
|
525
|
+
const dirOf = (c: number) => (c === L ? L : R);
|
|
526
|
+
for (let i = 0; i < n; ) {
|
|
527
|
+
if (!isNeutral(t[i])) {
|
|
528
|
+
i++;
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
let j = i;
|
|
532
|
+
while (j < n && isNeutral(t[j])) j++;
|
|
533
|
+
const before = i > 0 ? dirOf(t[i - 1]) : sos;
|
|
534
|
+
const after = j < n ? dirOf(t[j]) : sos;
|
|
535
|
+
const d = before === after ? before : sos;
|
|
536
|
+
for (let k = i; k < j; k++) t[k] = d;
|
|
537
|
+
i = j;
|
|
538
|
+
}
|
|
539
|
+
// I1/I2: implicit levels.
|
|
540
|
+
const levels = new Uint8Array(n);
|
|
541
|
+
for (let i = 0; i < n; i++) {
|
|
542
|
+
const c = t[i];
|
|
543
|
+
if (rtl) levels[i] = c === R ? 1 : 2;
|
|
544
|
+
else levels[i] = c === L ? 0 : c === R ? 1 : 2;
|
|
545
|
+
}
|
|
546
|
+
// L1: trailing whitespace returns to the paragraph level.
|
|
547
|
+
const base = rtl ? 1 : 0;
|
|
548
|
+
for (let i = n - 1; i >= 0; i--) {
|
|
549
|
+
const c = orig[i];
|
|
550
|
+
if (c !== WS && c !== BN && c !== S && c !== B) break;
|
|
551
|
+
levels[i] = base;
|
|
552
|
+
}
|
|
553
|
+
return levels;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Visual order (indices into `levels`) of items with the given embedding
|
|
558
|
+
* levels: rule L2, reversing every maximal sequence at or above each level
|
|
559
|
+
* from the highest down to the lowest odd one.
|
|
560
|
+
*/
|
|
561
|
+
export function visualOrder(levels: ArrayLike<number>): number[] {
|
|
562
|
+
const n = levels.length;
|
|
563
|
+
const order = Array.from({ length: n }, (_, i) => i);
|
|
564
|
+
if (n < 2) return order;
|
|
565
|
+
let max = 0;
|
|
566
|
+
let min = 255;
|
|
567
|
+
for (let i = 0; i < n; i++) {
|
|
568
|
+
max = Math.max(max, levels[i]);
|
|
569
|
+
min = Math.min(min, levels[i]);
|
|
570
|
+
}
|
|
571
|
+
const lowestOdd = min % 2 === 1 ? min : min + 1;
|
|
572
|
+
for (let level = max; level >= lowestOdd; level--) {
|
|
573
|
+
for (let i = 0; i < n; ) {
|
|
574
|
+
if (levels[order[i]] < level) {
|
|
575
|
+
i++;
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
let j = i;
|
|
579
|
+
while (j < n && levels[order[j]] >= level) j++;
|
|
580
|
+
for (let a = i, b = j - 1; a < b; a++, b--) {
|
|
581
|
+
const tmp = order[a];
|
|
582
|
+
order[a] = order[b];
|
|
583
|
+
order[b] = tmp;
|
|
584
|
+
}
|
|
585
|
+
i = j;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
return order;
|
|
589
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Small bounded caches for the renderer. The editor repaints on every drag
|
|
3
|
+
* frame, so pattern tiles, glow bitmaps, blurred images, blob outlines and
|
|
4
|
+
* parsed markup are kept between frames, but always under an entry and a
|
|
5
|
+
* byte budget so a long session cannot grow memory without limit.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const registry = new Set<{ clear(): void }>();
|
|
9
|
+
|
|
10
|
+
/** Least-recently-used map with an entry limit and an optional cost (bytes) limit. */
|
|
11
|
+
export class LruCache<K, V> {
|
|
12
|
+
private readonly map = new Map<K, { value: V; cost: number }>();
|
|
13
|
+
private readonly maxEntries: number;
|
|
14
|
+
private readonly maxCost: number;
|
|
15
|
+
private total = 0;
|
|
16
|
+
|
|
17
|
+
constructor(maxEntries: number, maxCost = Number.POSITIVE_INFINITY) {
|
|
18
|
+
this.maxEntries = Math.max(1, Math.floor(maxEntries));
|
|
19
|
+
this.maxCost = maxCost;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get(key: K): V | undefined {
|
|
23
|
+
const entry = this.map.get(key);
|
|
24
|
+
if (!entry) return undefined;
|
|
25
|
+
// Re-insert to mark as most recently used (Map keeps insertion order).
|
|
26
|
+
this.map.delete(key);
|
|
27
|
+
this.map.set(key, entry);
|
|
28
|
+
return entry.value;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
has(key: K): boolean {
|
|
32
|
+
return this.map.has(key);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Stores a value; items costlier than the whole budget are not cached at all. */
|
|
36
|
+
set(key: K, value: V, cost = 0): void {
|
|
37
|
+
const old = this.map.get(key);
|
|
38
|
+
if (old) {
|
|
39
|
+
this.total -= old.cost;
|
|
40
|
+
this.map.delete(key);
|
|
41
|
+
}
|
|
42
|
+
const c = Number.isFinite(cost) && cost > 0 ? cost : 0;
|
|
43
|
+
if (c > this.maxCost) return;
|
|
44
|
+
this.map.set(key, { value, cost: c });
|
|
45
|
+
this.total += c;
|
|
46
|
+
while (this.map.size > this.maxEntries || this.total > this.maxCost) {
|
|
47
|
+
const oldest = this.map.keys().next();
|
|
48
|
+
if (oldest.done) break;
|
|
49
|
+
const entry = this.map.get(oldest.value);
|
|
50
|
+
this.map.delete(oldest.value);
|
|
51
|
+
if (entry) this.total -= entry.cost;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
clear(): void {
|
|
56
|
+
this.map.clear();
|
|
57
|
+
this.total = 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get size(): number {
|
|
61
|
+
return this.map.size;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Sum of the costs of the cached items. */
|
|
65
|
+
get cost(): number {
|
|
66
|
+
return this.total;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** A module-level cache that `clearEngineCaches` empties. */
|
|
71
|
+
export function engineCache<K, V>(maxEntries: number, maxCost?: number): LruCache<K, V> {
|
|
72
|
+
const cache = new LruCache<K, V>(maxEntries, maxCost);
|
|
73
|
+
registry.add(cache);
|
|
74
|
+
return cache;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const ids = new WeakMap<object, number>();
|
|
78
|
+
let nextId = 1;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Stable small number for an object, for cache keys that depend on an image
|
|
82
|
+
* instance. Weakly held, so images can still be garbage collected.
|
|
83
|
+
*/
|
|
84
|
+
export function objectId(obj: object): number {
|
|
85
|
+
let id = ids.get(obj);
|
|
86
|
+
if (id === undefined) {
|
|
87
|
+
id = nextId++;
|
|
88
|
+
ids.set(obj, id);
|
|
89
|
+
}
|
|
90
|
+
return id;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Drops every engine cache (pattern tiles, glows, blurred images, outlines, markup). */
|
|
94
|
+
export function clearEngineCaches(): void {
|
|
95
|
+
for (const cache of registry) cache.clear();
|
|
96
|
+
}
|