@mentra/sdk 2.1.27 → 2.1.29-beta.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/dist/app/session/api-client.d.ts.map +1 -1
- package/dist/app/session/dashboard.d.ts +5 -8
- package/dist/app/session/dashboard.d.ts.map +1 -1
- package/dist/app/session/events.d.ts +10 -4
- package/dist/app/session/events.d.ts.map +1 -1
- package/dist/app/session/index.d.ts +64 -4
- package/dist/app/session/index.d.ts.map +1 -1
- package/dist/app/session/modules/audio.d.ts +33 -4
- package/dist/app/session/modules/audio.d.ts.map +1 -1
- package/dist/app/session/modules/camera-managed-extension.d.ts +2 -3
- package/dist/app/session/modules/camera-managed-extension.d.ts.map +1 -1
- package/dist/app/session/modules/camera.d.ts +11 -10
- package/dist/app/session/modules/camera.d.ts.map +1 -1
- package/dist/app/session/modules/led.d.ts +141 -0
- package/dist/app/session/modules/led.d.ts.map +1 -0
- package/dist/app/session/modules/location.d.ts +1 -2
- package/dist/app/session/modules/location.d.ts.map +1 -1
- package/dist/app/session/modules/simple-storage.d.ts +22 -1
- package/dist/app/session/modules/simple-storage.d.ts.map +1 -1
- package/dist/display-utils.d.ts +989 -0
- package/dist/display-utils.d.ts.map +1 -0
- package/dist/display-utils.js +1197 -0
- package/dist/display-utils.js.map +17 -0
- package/dist/index.d.ts +7 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5427 -112
- package/dist/index.js.map +45 -0
- package/dist/logging/logger.d.ts +1 -1
- package/dist/logging/logger.d.ts.map +1 -1
- package/dist/types/capabilities.d.ts +3 -0
- package/dist/types/capabilities.d.ts.map +1 -1
- package/dist/types/index.d.ts +4 -14
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/message-types.d.ts +8 -1
- package/dist/types/message-types.d.ts.map +1 -1
- package/dist/types/messages/app-to-cloud.d.ts +49 -3
- package/dist/types/messages/app-to-cloud.d.ts.map +1 -1
- package/dist/types/messages/cloud-to-app.d.ts +18 -6
- package/dist/types/messages/cloud-to-app.d.ts.map +1 -1
- package/dist/types/messages/cloud-to-glasses.d.ts +30 -2
- package/dist/types/messages/cloud-to-glasses.d.ts.map +1 -1
- package/dist/types/messages/glasses-to-cloud.d.ts +24 -1
- package/dist/types/messages/glasses-to-cloud.d.ts.map +1 -1
- package/dist/types/rtmp-stream.d.ts +1 -1
- package/dist/types/rtmp-stream.d.ts.map +1 -1
- package/dist/types/streams.d.ts +31 -2
- package/dist/types/streams.d.ts.map +1 -1
- package/package.json +34 -11
- package/dist/app/index.js +0 -24
- package/dist/app/server/index.js +0 -658
- package/dist/app/session/api-client.js +0 -101
- package/dist/app/session/dashboard.js +0 -149
- package/dist/app/session/events.js +0 -315
- package/dist/app/session/index.js +0 -1573
- package/dist/app/session/layouts.js +0 -372
- package/dist/app/session/modules/audio.js +0 -321
- package/dist/app/session/modules/camera-managed-extension.js +0 -310
- package/dist/app/session/modules/camera.js +0 -607
- package/dist/app/session/modules/index.js +0 -19
- package/dist/app/session/modules/location.js +0 -61
- package/dist/app/session/modules/simple-storage.js +0 -232
- package/dist/app/session/settings.js +0 -358
- package/dist/app/token/index.js +0 -22
- package/dist/app/token/utils.js +0 -144
- package/dist/app/webview/index.js +0 -382
- package/dist/constants/index.js +0 -16
- package/dist/constants/log-messages/color.js +0 -14
- package/dist/constants/log-messages/logos.js +0 -48
- package/dist/constants/log-messages/updates.js +0 -55
- package/dist/constants/log-messages/warning.js +0 -89
- package/dist/examples/managed-rtmp-streaming-example.js +0 -158
- package/dist/examples/managed-rtmp-streaming-with-restream-example.js +0 -124
- package/dist/examples/rtmp-streaming-example.js +0 -102
- package/dist/logging/logger.js +0 -79
- package/dist/types/capabilities.js +0 -9
- package/dist/types/dashboard/index.js +0 -12
- package/dist/types/enums.js +0 -75
- package/dist/types/index.js +0 -101
- package/dist/types/layouts.js +0 -3
- package/dist/types/message-types.js +0 -212
- package/dist/types/messages/app-to-cloud.js +0 -95
- package/dist/types/messages/base.js +0 -3
- package/dist/types/messages/cloud-to-app.js +0 -78
- package/dist/types/messages/cloud-to-glasses.js +0 -68
- package/dist/types/messages/glasses-to-cloud.js +0 -140
- package/dist/types/models.js +0 -101
- package/dist/types/photo-data.js +0 -2
- package/dist/types/rtmp-stream.js +0 -3
- package/dist/types/streams.js +0 -306
- package/dist/types/token.js +0 -7
- package/dist/types/webhooks.js +0 -28
- package/dist/utils/animation-utils.js +0 -340
- package/dist/utils/bitmap-utils.js +0 -475
- package/dist/utils/permissions-utils.js +0 -263
- package/dist/utils/resource-tracker.js +0 -153
|
@@ -0,0 +1,1197 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
7
|
+
var __toCommonJS = (from) => {
|
|
8
|
+
var entry = __moduleCache.get(from), desc;
|
|
9
|
+
if (entry)
|
|
10
|
+
return entry;
|
|
11
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
13
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
14
|
+
get: () => from[key],
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
}));
|
|
17
|
+
__moduleCache.set(from, entry);
|
|
18
|
+
return entry;
|
|
19
|
+
};
|
|
20
|
+
var __export = (target, all) => {
|
|
21
|
+
for (var name in all)
|
|
22
|
+
__defProp(target, name, {
|
|
23
|
+
get: all[name],
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
set: (newValue) => all[name] = () => newValue
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
30
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
31
|
+
|
|
32
|
+
// ../display-utils/src/profiles/g1.ts
|
|
33
|
+
var G1_GLYPH_WIDTHS = {
|
|
34
|
+
" ": 2,
|
|
35
|
+
"!": 1,
|
|
36
|
+
'"': 2,
|
|
37
|
+
"#": 6,
|
|
38
|
+
$: 5,
|
|
39
|
+
"%": 6,
|
|
40
|
+
"&": 7,
|
|
41
|
+
"'": 1,
|
|
42
|
+
"(": 2,
|
|
43
|
+
")": 2,
|
|
44
|
+
"*": 3,
|
|
45
|
+
"+": 4,
|
|
46
|
+
",": 1,
|
|
47
|
+
"-": 4,
|
|
48
|
+
".": 1,
|
|
49
|
+
"/": 3,
|
|
50
|
+
"0": 5,
|
|
51
|
+
"1": 3,
|
|
52
|
+
"2": 5,
|
|
53
|
+
"3": 5,
|
|
54
|
+
"4": 5,
|
|
55
|
+
"5": 5,
|
|
56
|
+
"6": 5,
|
|
57
|
+
"7": 5,
|
|
58
|
+
"8": 5,
|
|
59
|
+
"9": 5,
|
|
60
|
+
":": 1,
|
|
61
|
+
";": 1,
|
|
62
|
+
"<": 4,
|
|
63
|
+
"=": 4,
|
|
64
|
+
">": 4,
|
|
65
|
+
"?": 5,
|
|
66
|
+
"@": 7,
|
|
67
|
+
A: 6,
|
|
68
|
+
B: 5,
|
|
69
|
+
C: 5,
|
|
70
|
+
D: 5,
|
|
71
|
+
E: 4,
|
|
72
|
+
F: 4,
|
|
73
|
+
G: 5,
|
|
74
|
+
H: 5,
|
|
75
|
+
I: 2,
|
|
76
|
+
J: 3,
|
|
77
|
+
K: 5,
|
|
78
|
+
L: 4,
|
|
79
|
+
M: 7,
|
|
80
|
+
N: 5,
|
|
81
|
+
O: 5,
|
|
82
|
+
P: 5,
|
|
83
|
+
Q: 5,
|
|
84
|
+
R: 5,
|
|
85
|
+
S: 5,
|
|
86
|
+
T: 5,
|
|
87
|
+
U: 5,
|
|
88
|
+
V: 6,
|
|
89
|
+
W: 7,
|
|
90
|
+
X: 6,
|
|
91
|
+
Y: 6,
|
|
92
|
+
Z: 5,
|
|
93
|
+
"[": 2,
|
|
94
|
+
"\\": 3,
|
|
95
|
+
"]": 2,
|
|
96
|
+
"^": 4,
|
|
97
|
+
_: 3,
|
|
98
|
+
"`": 2,
|
|
99
|
+
a: 5,
|
|
100
|
+
b: 4,
|
|
101
|
+
c: 4,
|
|
102
|
+
d: 4,
|
|
103
|
+
e: 4,
|
|
104
|
+
f: 4,
|
|
105
|
+
g: 4,
|
|
106
|
+
h: 4,
|
|
107
|
+
i: 1,
|
|
108
|
+
j: 2,
|
|
109
|
+
k: 4,
|
|
110
|
+
l: 1,
|
|
111
|
+
m: 7,
|
|
112
|
+
n: 4,
|
|
113
|
+
o: 4,
|
|
114
|
+
p: 4,
|
|
115
|
+
q: 4,
|
|
116
|
+
r: 3,
|
|
117
|
+
s: 4,
|
|
118
|
+
t: 3,
|
|
119
|
+
u: 5,
|
|
120
|
+
v: 5,
|
|
121
|
+
w: 7,
|
|
122
|
+
x: 5,
|
|
123
|
+
y: 5,
|
|
124
|
+
z: 4,
|
|
125
|
+
"{": 3,
|
|
126
|
+
"|": 1,
|
|
127
|
+
"}": 3,
|
|
128
|
+
"~": 7
|
|
129
|
+
};
|
|
130
|
+
var G1_PROFILE = {
|
|
131
|
+
id: "even-realities-g1",
|
|
132
|
+
name: "Even Realities G1",
|
|
133
|
+
displayWidthPx: 576,
|
|
134
|
+
maxLines: 5,
|
|
135
|
+
maxPayloadBytes: 390,
|
|
136
|
+
bleChunkSize: 176,
|
|
137
|
+
fontMetrics: {
|
|
138
|
+
glyphWidths: new Map(Object.entries(G1_GLYPH_WIDTHS)),
|
|
139
|
+
defaultGlyphWidth: 7,
|
|
140
|
+
renderFormula: (glyphWidth) => (glyphWidth + 1) * 2,
|
|
141
|
+
uniformScripts: {
|
|
142
|
+
cjk: 18,
|
|
143
|
+
hiragana: 18,
|
|
144
|
+
katakana: 18,
|
|
145
|
+
korean: 24,
|
|
146
|
+
cyrillic: 18
|
|
147
|
+
},
|
|
148
|
+
fallback: {
|
|
149
|
+
latinMaxWidth: 16,
|
|
150
|
+
unknownBehavior: "useLatinMax"
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
constraints: {
|
|
154
|
+
minCharsBeforeHyphen: 3,
|
|
155
|
+
noStartChars: [".", ",", "!", "?", ":", ";", ")", "]", "}", "。", ",", "!", "?", ":", ";", ")", "】", "」"],
|
|
156
|
+
noEndChars: ["(", "[", "{", "(", "【", "「"]
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
var G1_PROFILE_LEGACY = {
|
|
160
|
+
id: "even-realities-g1-legacy",
|
|
161
|
+
name: "Even Realities G1 (Legacy Client Compatibility)",
|
|
162
|
+
displayWidthPx: 420,
|
|
163
|
+
maxLines: 5,
|
|
164
|
+
maxPayloadBytes: 390,
|
|
165
|
+
bleChunkSize: 176,
|
|
166
|
+
fontMetrics: {
|
|
167
|
+
glyphWidths: new Map(Object.entries(G1_GLYPH_WIDTHS)),
|
|
168
|
+
defaultGlyphWidth: 7,
|
|
169
|
+
renderFormula: (glyphWidth) => (glyphWidth + 1) * 2,
|
|
170
|
+
uniformScripts: {
|
|
171
|
+
cjk: 18,
|
|
172
|
+
hiragana: 18,
|
|
173
|
+
katakana: 18,
|
|
174
|
+
korean: 24,
|
|
175
|
+
cyrillic: 18
|
|
176
|
+
},
|
|
177
|
+
fallback: {
|
|
178
|
+
latinMaxWidth: 16,
|
|
179
|
+
unknownBehavior: "useLatinMax"
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
constraints: {
|
|
183
|
+
minCharsBeforeHyphen: 3,
|
|
184
|
+
noStartChars: [".", ",", "!", "?", ":", ";", ")", "]", "}", "。", ",", "!", "?", ":", ";", ")", "】", "」"],
|
|
185
|
+
noEndChars: ["(", "[", "{", "(", "【", "「"]
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
var G1_HYPHEN_WIDTH_PX = 10;
|
|
189
|
+
var G1_SPACE_WIDTH_PX = 6;
|
|
190
|
+
// ../display-utils/src/measurer/script-detection.ts
|
|
191
|
+
var SCRIPT_RANGES = {
|
|
192
|
+
cjk: [
|
|
193
|
+
[19968, 40959],
|
|
194
|
+
[13312, 19903],
|
|
195
|
+
[131072, 173791],
|
|
196
|
+
[173824, 177983],
|
|
197
|
+
[177984, 178207],
|
|
198
|
+
[63744, 64255]
|
|
199
|
+
],
|
|
200
|
+
hiragana: [[12352, 12447]],
|
|
201
|
+
katakana: [
|
|
202
|
+
[12448, 12543],
|
|
203
|
+
[12784, 12799]
|
|
204
|
+
],
|
|
205
|
+
korean: [
|
|
206
|
+
[44032, 55215],
|
|
207
|
+
[4352, 4607],
|
|
208
|
+
[12592, 12687],
|
|
209
|
+
[43360, 43391],
|
|
210
|
+
[55216, 55295]
|
|
211
|
+
],
|
|
212
|
+
cyrillic: [
|
|
213
|
+
[1024, 1279],
|
|
214
|
+
[1280, 1327]
|
|
215
|
+
],
|
|
216
|
+
numbers: [[48, 57]],
|
|
217
|
+
punctuation: [
|
|
218
|
+
[32, 47],
|
|
219
|
+
[58, 64],
|
|
220
|
+
[91, 96],
|
|
221
|
+
[123, 126]
|
|
222
|
+
],
|
|
223
|
+
arabic: [[1536, 1791]],
|
|
224
|
+
hebrew: [[1424, 1535]],
|
|
225
|
+
thai: [[3584, 3711]],
|
|
226
|
+
emoji: [
|
|
227
|
+
[128512, 128591],
|
|
228
|
+
[127744, 128511],
|
|
229
|
+
[128640, 128767],
|
|
230
|
+
[127456, 127487],
|
|
231
|
+
[9728, 9983],
|
|
232
|
+
[9984, 10175],
|
|
233
|
+
[65024, 65039],
|
|
234
|
+
[129280, 129535]
|
|
235
|
+
]
|
|
236
|
+
};
|
|
237
|
+
function inRanges(codePoint, ranges) {
|
|
238
|
+
for (const [start, end] of ranges) {
|
|
239
|
+
if (codePoint >= start && codePoint <= end) {
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
function detectScript(char) {
|
|
246
|
+
if (!char || char.length === 0) {
|
|
247
|
+
return "latin";
|
|
248
|
+
}
|
|
249
|
+
const codePoint = char.codePointAt(0);
|
|
250
|
+
if (codePoint === undefined) {
|
|
251
|
+
return "latin";
|
|
252
|
+
}
|
|
253
|
+
if (inRanges(codePoint, SCRIPT_RANGES.cjk)) {
|
|
254
|
+
return "cjk";
|
|
255
|
+
}
|
|
256
|
+
if (inRanges(codePoint, SCRIPT_RANGES.hiragana)) {
|
|
257
|
+
return "hiragana";
|
|
258
|
+
}
|
|
259
|
+
if (inRanges(codePoint, SCRIPT_RANGES.katakana)) {
|
|
260
|
+
return "katakana";
|
|
261
|
+
}
|
|
262
|
+
if (inRanges(codePoint, SCRIPT_RANGES.korean)) {
|
|
263
|
+
return "korean";
|
|
264
|
+
}
|
|
265
|
+
if (inRanges(codePoint, SCRIPT_RANGES.cyrillic)) {
|
|
266
|
+
return "cyrillic";
|
|
267
|
+
}
|
|
268
|
+
if (inRanges(codePoint, SCRIPT_RANGES.numbers)) {
|
|
269
|
+
return "numbers";
|
|
270
|
+
}
|
|
271
|
+
if (inRanges(codePoint, SCRIPT_RANGES.punctuation)) {
|
|
272
|
+
return "punctuation";
|
|
273
|
+
}
|
|
274
|
+
if (inRanges(codePoint, SCRIPT_RANGES.arabic) || inRanges(codePoint, SCRIPT_RANGES.hebrew) || inRanges(codePoint, SCRIPT_RANGES.thai) || inRanges(codePoint, SCRIPT_RANGES.emoji)) {
|
|
275
|
+
return "unsupported";
|
|
276
|
+
}
|
|
277
|
+
return "latin";
|
|
278
|
+
}
|
|
279
|
+
function isCJKCharacter(char) {
|
|
280
|
+
const script = detectScript(char);
|
|
281
|
+
return script === "cjk" || script === "hiragana" || script === "katakana";
|
|
282
|
+
}
|
|
283
|
+
function isKoreanCharacter(char) {
|
|
284
|
+
return detectScript(char) === "korean";
|
|
285
|
+
}
|
|
286
|
+
function isUniformWidthScript(char) {
|
|
287
|
+
const script = detectScript(char);
|
|
288
|
+
return script === "cjk" || script === "hiragana" || script === "katakana" || script === "korean" || script === "cyrillic";
|
|
289
|
+
}
|
|
290
|
+
function isUnsupportedScript(char) {
|
|
291
|
+
return detectScript(char) === "unsupported";
|
|
292
|
+
}
|
|
293
|
+
function needsHyphenForBreak(charBefore, charAfter) {
|
|
294
|
+
if (isCJKCharacter(charBefore)) {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
if (isCJKCharacter(charAfter)) {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
if (charBefore === " " || charBefore === "\t") {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
if (charAfter === " " || charAfter === "\t") {
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
const breakPunctuation = ["-", "–", "—", "/", "\\", "|"];
|
|
307
|
+
if (breakPunctuation.includes(charBefore)) {
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
return true;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// ../display-utils/src/measurer/TextMeasurer.ts
|
|
314
|
+
class TextMeasurer {
|
|
315
|
+
profile;
|
|
316
|
+
charCache = new Map;
|
|
317
|
+
constructor(profile) {
|
|
318
|
+
this.profile = profile;
|
|
319
|
+
this.buildCharCache();
|
|
320
|
+
}
|
|
321
|
+
buildCharCache() {
|
|
322
|
+
const { glyphWidths, renderFormula } = this.profile.fontMetrics;
|
|
323
|
+
for (const [char, glyphWidth] of glyphWidths.entries()) {
|
|
324
|
+
const renderedWidth = renderFormula(glyphWidth);
|
|
325
|
+
this.charCache.set(char, renderedWidth);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
measureText(text) {
|
|
329
|
+
if (!text || text.length === 0) {
|
|
330
|
+
return 0;
|
|
331
|
+
}
|
|
332
|
+
let totalWidth = 0;
|
|
333
|
+
for (const char of text) {
|
|
334
|
+
totalWidth += this.measureChar(char);
|
|
335
|
+
}
|
|
336
|
+
return totalWidth;
|
|
337
|
+
}
|
|
338
|
+
measureTextDetailed(text) {
|
|
339
|
+
if (!text || text.length === 0) {
|
|
340
|
+
return {
|
|
341
|
+
text: "",
|
|
342
|
+
totalWidthPx: 0,
|
|
343
|
+
charCount: 0,
|
|
344
|
+
chars: []
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
const chars = [];
|
|
348
|
+
let totalWidth = 0;
|
|
349
|
+
for (const char of text) {
|
|
350
|
+
const script = detectScript(char);
|
|
351
|
+
const widthPx = this.measureChar(char);
|
|
352
|
+
const fromGlyphMap = this.profile.fontMetrics.glyphWidths.has(char);
|
|
353
|
+
chars.push({
|
|
354
|
+
char,
|
|
355
|
+
widthPx,
|
|
356
|
+
script,
|
|
357
|
+
fromGlyphMap
|
|
358
|
+
});
|
|
359
|
+
totalWidth += widthPx;
|
|
360
|
+
}
|
|
361
|
+
return {
|
|
362
|
+
text,
|
|
363
|
+
totalWidthPx: totalWidth,
|
|
364
|
+
charCount: chars.length,
|
|
365
|
+
chars
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
measureChar(char) {
|
|
369
|
+
if (!char || char.length === 0) {
|
|
370
|
+
return 0;
|
|
371
|
+
}
|
|
372
|
+
const cached = this.charCache.get(char);
|
|
373
|
+
if (cached !== undefined) {
|
|
374
|
+
return cached;
|
|
375
|
+
}
|
|
376
|
+
const width = this.calculateCharWidth(char);
|
|
377
|
+
this.charCache.set(char, width);
|
|
378
|
+
return width;
|
|
379
|
+
}
|
|
380
|
+
calculateCharWidth(char) {
|
|
381
|
+
const { fontMetrics } = this.profile;
|
|
382
|
+
const { uniformScripts, fallback, renderFormula, glyphWidths } = fontMetrics;
|
|
383
|
+
const glyphWidth = glyphWidths.get(char);
|
|
384
|
+
if (glyphWidth !== undefined) {
|
|
385
|
+
return renderFormula(glyphWidth);
|
|
386
|
+
}
|
|
387
|
+
const script = detectScript(char);
|
|
388
|
+
switch (script) {
|
|
389
|
+
case "cjk":
|
|
390
|
+
return uniformScripts.cjk;
|
|
391
|
+
case "hiragana":
|
|
392
|
+
return uniformScripts.hiragana;
|
|
393
|
+
case "katakana":
|
|
394
|
+
return uniformScripts.katakana;
|
|
395
|
+
case "korean":
|
|
396
|
+
return uniformScripts.korean;
|
|
397
|
+
case "cyrillic":
|
|
398
|
+
return uniformScripts.cyrillic;
|
|
399
|
+
}
|
|
400
|
+
return fallback.latinMaxWidth;
|
|
401
|
+
}
|
|
402
|
+
getGlyphWidth(char) {
|
|
403
|
+
return this.profile.fontMetrics.glyphWidths.get(char);
|
|
404
|
+
}
|
|
405
|
+
fitsInWidth(text, maxWidthPx) {
|
|
406
|
+
return this.measureText(text) <= maxWidthPx;
|
|
407
|
+
}
|
|
408
|
+
charsThatFit(text, maxWidthPx, startIndex = 0) {
|
|
409
|
+
if (!text || startIndex >= text.length) {
|
|
410
|
+
return 0;
|
|
411
|
+
}
|
|
412
|
+
let currentWidth = 0;
|
|
413
|
+
let count = 0;
|
|
414
|
+
const chars = Array.from(text).slice(startIndex);
|
|
415
|
+
for (const char of chars) {
|
|
416
|
+
const charWidth = this.measureChar(char);
|
|
417
|
+
if (currentWidth + charWidth > maxWidthPx) {
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
420
|
+
currentWidth += charWidth;
|
|
421
|
+
count++;
|
|
422
|
+
}
|
|
423
|
+
return count;
|
|
424
|
+
}
|
|
425
|
+
getPixelOffset(text, index) {
|
|
426
|
+
if (!text || index <= 0) {
|
|
427
|
+
return 0;
|
|
428
|
+
}
|
|
429
|
+
const chars = Array.from(text).slice(0, index);
|
|
430
|
+
let offset = 0;
|
|
431
|
+
for (const char of chars) {
|
|
432
|
+
offset += this.measureChar(char);
|
|
433
|
+
}
|
|
434
|
+
return offset;
|
|
435
|
+
}
|
|
436
|
+
detectScript(char) {
|
|
437
|
+
return detectScript(char);
|
|
438
|
+
}
|
|
439
|
+
isUniformWidth(char) {
|
|
440
|
+
return isUniformWidthScript(char);
|
|
441
|
+
}
|
|
442
|
+
getProfile() {
|
|
443
|
+
return this.profile;
|
|
444
|
+
}
|
|
445
|
+
getDisplayWidthPx() {
|
|
446
|
+
return this.profile.displayWidthPx;
|
|
447
|
+
}
|
|
448
|
+
getMaxLines() {
|
|
449
|
+
return this.profile.maxLines;
|
|
450
|
+
}
|
|
451
|
+
getMaxPayloadBytes() {
|
|
452
|
+
return this.profile.maxPayloadBytes;
|
|
453
|
+
}
|
|
454
|
+
getByteSize(text) {
|
|
455
|
+
return new TextEncoder().encode(text).length;
|
|
456
|
+
}
|
|
457
|
+
getHyphenWidth() {
|
|
458
|
+
return this.measureChar("-");
|
|
459
|
+
}
|
|
460
|
+
getSpaceWidth() {
|
|
461
|
+
return this.measureChar(" ");
|
|
462
|
+
}
|
|
463
|
+
clearCache() {
|
|
464
|
+
this.charCache.clear();
|
|
465
|
+
this.buildCharCache();
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
// ../display-utils/src/wrapper/types.ts
|
|
469
|
+
var DEFAULT_WRAP_OPTIONS = {
|
|
470
|
+
breakMode: "character",
|
|
471
|
+
hyphenChar: "-",
|
|
472
|
+
minCharsBeforeHyphen: 3,
|
|
473
|
+
trimLines: true,
|
|
474
|
+
preserveNewlines: true
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
// ../display-utils/src/wrapper/TextWrapper.ts
|
|
478
|
+
class TextWrapper {
|
|
479
|
+
measurer;
|
|
480
|
+
defaultOptions;
|
|
481
|
+
constructor(measurer, defaultOptions) {
|
|
482
|
+
this.measurer = measurer;
|
|
483
|
+
const profile = measurer.getProfile();
|
|
484
|
+
this.defaultOptions = {
|
|
485
|
+
maxWidthPx: profile.displayWidthPx,
|
|
486
|
+
maxLines: profile.maxLines,
|
|
487
|
+
maxBytes: profile.maxPayloadBytes,
|
|
488
|
+
...DEFAULT_WRAP_OPTIONS,
|
|
489
|
+
...defaultOptions
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
wrap(text, options) {
|
|
493
|
+
const opts = this.mergeOptions(options);
|
|
494
|
+
const { maxWidthPx, maxLines, maxBytes, breakMode, preserveNewlines } = opts;
|
|
495
|
+
if (!text || text.length === 0) {
|
|
496
|
+
return this.createEmptyResult(opts);
|
|
497
|
+
}
|
|
498
|
+
const paragraphs = preserveNewlines ? text.split(`
|
|
499
|
+
`) : [text];
|
|
500
|
+
const allLines = [];
|
|
501
|
+
const allMetrics = [];
|
|
502
|
+
let totalBytes = 0;
|
|
503
|
+
let truncated = false;
|
|
504
|
+
for (let pIndex = 0;pIndex < paragraphs.length; pIndex++) {
|
|
505
|
+
const paragraph = paragraphs[pIndex];
|
|
506
|
+
const isFromNewline = pIndex > 0;
|
|
507
|
+
const paragraphLines = this.wrapParagraph(paragraph, maxWidthPx, breakMode, opts);
|
|
508
|
+
for (let lIndex = 0;lIndex < paragraphLines.length; lIndex++) {
|
|
509
|
+
const line = paragraphLines[lIndex];
|
|
510
|
+
const lineBytes = this.measurer.getByteSize(line);
|
|
511
|
+
if (allLines.length >= maxLines) {
|
|
512
|
+
truncated = true;
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
if (totalBytes + lineBytes > maxBytes) {
|
|
516
|
+
truncated = true;
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
519
|
+
const widthPx = this.measurer.measureText(line);
|
|
520
|
+
const metrics = {
|
|
521
|
+
text: line,
|
|
522
|
+
widthPx,
|
|
523
|
+
bytes: lineBytes,
|
|
524
|
+
utilizationPercent: Math.round(widthPx / maxWidthPx * 100),
|
|
525
|
+
endsWithHyphen: line.endsWith(opts.hyphenChar) && lIndex < paragraphLines.length - 1,
|
|
526
|
+
fromExplicitNewline: isFromNewline && lIndex === 0
|
|
527
|
+
};
|
|
528
|
+
allLines.push(line);
|
|
529
|
+
allMetrics.push(metrics);
|
|
530
|
+
totalBytes += lineBytes;
|
|
531
|
+
if (allLines.length < maxLines) {
|
|
532
|
+
totalBytes += 1;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
if (truncated)
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
const maxLineWidthPx = allMetrics.reduce((max, m) => Math.max(max, m.widthPx), 0);
|
|
539
|
+
return {
|
|
540
|
+
lines: allLines,
|
|
541
|
+
truncated,
|
|
542
|
+
maxLineWidthPx,
|
|
543
|
+
totalBytes,
|
|
544
|
+
lineMetrics: allMetrics,
|
|
545
|
+
originalText: text,
|
|
546
|
+
breakMode
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
wrapToLines(text, options) {
|
|
550
|
+
return this.wrap(text, options).lines;
|
|
551
|
+
}
|
|
552
|
+
needsWrap(text, maxWidthPx) {
|
|
553
|
+
const width = maxWidthPx ?? this.defaultOptions.maxWidthPx;
|
|
554
|
+
return this.measurer.measureText(text) > width || text.includes(`
|
|
555
|
+
`);
|
|
556
|
+
}
|
|
557
|
+
getOptions() {
|
|
558
|
+
return { ...this.defaultOptions };
|
|
559
|
+
}
|
|
560
|
+
getMeasurer() {
|
|
561
|
+
return this.measurer;
|
|
562
|
+
}
|
|
563
|
+
wrapParagraph(paragraph, maxWidthPx, breakMode, opts) {
|
|
564
|
+
const trimmed = opts.trimLines ? paragraph.trim() : paragraph;
|
|
565
|
+
if (!trimmed) {
|
|
566
|
+
return [""];
|
|
567
|
+
}
|
|
568
|
+
if (this.measurer.fitsInWidth(trimmed, maxWidthPx)) {
|
|
569
|
+
return [trimmed];
|
|
570
|
+
}
|
|
571
|
+
switch (breakMode) {
|
|
572
|
+
case "character":
|
|
573
|
+
return this.wrapCharacterMode(trimmed, maxWidthPx, opts);
|
|
574
|
+
case "word":
|
|
575
|
+
return this.wrapWordMode(trimmed, maxWidthPx, opts);
|
|
576
|
+
case "strict-word":
|
|
577
|
+
return this.wrapStrictWordMode(trimmed, maxWidthPx, opts);
|
|
578
|
+
default:
|
|
579
|
+
return this.wrapCharacterMode(trimmed, maxWidthPx, opts);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
wrapCharacterMode(text, maxWidthPx, opts) {
|
|
583
|
+
const lines = [];
|
|
584
|
+
const hyphenWidth = this.measurer.measureChar(opts.hyphenChar);
|
|
585
|
+
let currentLine = "";
|
|
586
|
+
let currentWidth = 0;
|
|
587
|
+
const chars = Array.from(text);
|
|
588
|
+
for (let i = 0;i < chars.length; i++) {
|
|
589
|
+
const char = chars[i];
|
|
590
|
+
const charWidth = this.measurer.measureChar(char);
|
|
591
|
+
if (currentWidth + charWidth <= maxWidthPx) {
|
|
592
|
+
currentLine += char;
|
|
593
|
+
currentWidth += charWidth;
|
|
594
|
+
} else {
|
|
595
|
+
const prevChar = currentLine.length > 0 ? currentLine[currentLine.length - 1] : "";
|
|
596
|
+
const needsHyphen = currentLine.length >= opts.minCharsBeforeHyphen && needsHyphenForBreak(prevChar, char);
|
|
597
|
+
if (needsHyphen) {
|
|
598
|
+
const result = this.backoffForHyphen(currentLine, currentWidth, maxWidthPx, hyphenWidth, opts);
|
|
599
|
+
if (result.skipHyphen) {
|
|
600
|
+
lines.push(result.line);
|
|
601
|
+
} else {
|
|
602
|
+
lines.push(result.line + opts.hyphenChar);
|
|
603
|
+
}
|
|
604
|
+
currentLine = result.remainder + char;
|
|
605
|
+
currentWidth = this.measurer.measureText(currentLine);
|
|
606
|
+
} else {
|
|
607
|
+
const trimmedLine = opts.trimLines ? currentLine.trimEnd() : currentLine;
|
|
608
|
+
if (trimmedLine) {
|
|
609
|
+
lines.push(trimmedLine);
|
|
610
|
+
}
|
|
611
|
+
currentLine = char === " " ? "" : char;
|
|
612
|
+
currentWidth = char === " " ? 0 : charWidth;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
if (currentLine) {
|
|
617
|
+
const trimmedLine = opts.trimLines ? currentLine.trim() : currentLine;
|
|
618
|
+
if (trimmedLine) {
|
|
619
|
+
lines.push(trimmedLine);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return lines.length > 0 ? lines : [""];
|
|
623
|
+
}
|
|
624
|
+
wrapWordMode(text, maxWidthPx, opts) {
|
|
625
|
+
const lines = [];
|
|
626
|
+
const words = this.splitIntoWords(text);
|
|
627
|
+
const spaceWidth = this.measurer.getSpaceWidth();
|
|
628
|
+
let currentLine = "";
|
|
629
|
+
let currentWidth = 0;
|
|
630
|
+
for (const word of words) {
|
|
631
|
+
const wordWidth = this.measurer.measureText(word);
|
|
632
|
+
const needsSpace = currentLine.length > 0;
|
|
633
|
+
const totalWidth = currentWidth + (needsSpace ? spaceWidth : 0) + wordWidth;
|
|
634
|
+
if (totalWidth <= maxWidthPx) {
|
|
635
|
+
if (needsSpace) {
|
|
636
|
+
currentLine += " ";
|
|
637
|
+
currentWidth += spaceWidth;
|
|
638
|
+
}
|
|
639
|
+
currentLine += word;
|
|
640
|
+
currentWidth += wordWidth;
|
|
641
|
+
} else {
|
|
642
|
+
if (currentLine.length > 0) {
|
|
643
|
+
lines.push(opts.trimLines ? currentLine.trim() : currentLine);
|
|
644
|
+
currentLine = "";
|
|
645
|
+
currentWidth = 0;
|
|
646
|
+
}
|
|
647
|
+
if (wordWidth > maxWidthPx) {
|
|
648
|
+
const hyphenatedLines = this.hyphenateLongWord(word, maxWidthPx, opts);
|
|
649
|
+
for (let i = 0;i < hyphenatedLines.length - 1; i++) {
|
|
650
|
+
lines.push(hyphenatedLines[i]);
|
|
651
|
+
}
|
|
652
|
+
currentLine = hyphenatedLines[hyphenatedLines.length - 1];
|
|
653
|
+
currentWidth = this.measurer.measureText(currentLine);
|
|
654
|
+
} else {
|
|
655
|
+
currentLine = word;
|
|
656
|
+
currentWidth = wordWidth;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
if (currentLine) {
|
|
661
|
+
const trimmedLine = opts.trimLines ? currentLine.trim() : currentLine;
|
|
662
|
+
if (trimmedLine) {
|
|
663
|
+
lines.push(trimmedLine);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
return lines.length > 0 ? lines : [""];
|
|
667
|
+
}
|
|
668
|
+
wrapStrictWordMode(text, maxWidthPx, opts) {
|
|
669
|
+
const lines = [];
|
|
670
|
+
const words = this.splitIntoWords(text);
|
|
671
|
+
const spaceWidth = this.measurer.getSpaceWidth();
|
|
672
|
+
let currentLine = "";
|
|
673
|
+
let currentWidth = 0;
|
|
674
|
+
for (const word of words) {
|
|
675
|
+
const wordWidth = this.measurer.measureText(word);
|
|
676
|
+
const needsSpace = currentLine.length > 0;
|
|
677
|
+
const totalWidth = currentWidth + (needsSpace ? spaceWidth : 0) + wordWidth;
|
|
678
|
+
if (totalWidth <= maxWidthPx) {
|
|
679
|
+
if (needsSpace) {
|
|
680
|
+
currentLine += " ";
|
|
681
|
+
currentWidth += spaceWidth;
|
|
682
|
+
}
|
|
683
|
+
currentLine += word;
|
|
684
|
+
currentWidth += wordWidth;
|
|
685
|
+
} else {
|
|
686
|
+
if (currentLine.length > 0) {
|
|
687
|
+
lines.push(opts.trimLines ? currentLine.trim() : currentLine);
|
|
688
|
+
}
|
|
689
|
+
currentLine = word;
|
|
690
|
+
currentWidth = wordWidth;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
if (currentLine) {
|
|
694
|
+
const trimmedLine = opts.trimLines ? currentLine.trim() : currentLine;
|
|
695
|
+
if (trimmedLine) {
|
|
696
|
+
lines.push(trimmedLine);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
return lines.length > 0 ? lines : [""];
|
|
700
|
+
}
|
|
701
|
+
splitIntoWords(text) {
|
|
702
|
+
const words = [];
|
|
703
|
+
let currentWord = "";
|
|
704
|
+
for (const char of text) {
|
|
705
|
+
if (char === " " || char === "\t") {
|
|
706
|
+
if (currentWord) {
|
|
707
|
+
words.push(currentWord);
|
|
708
|
+
currentWord = "";
|
|
709
|
+
}
|
|
710
|
+
} else if (isCJKCharacter(char)) {
|
|
711
|
+
if (currentWord) {
|
|
712
|
+
words.push(currentWord);
|
|
713
|
+
currentWord = "";
|
|
714
|
+
}
|
|
715
|
+
words.push(char);
|
|
716
|
+
} else {
|
|
717
|
+
currentWord += char;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
if (currentWord) {
|
|
721
|
+
words.push(currentWord);
|
|
722
|
+
}
|
|
723
|
+
return words;
|
|
724
|
+
}
|
|
725
|
+
hyphenateLongWord(word, maxWidthPx, opts) {
|
|
726
|
+
const lines = [];
|
|
727
|
+
const hyphenWidth = this.measurer.measureChar(opts.hyphenChar);
|
|
728
|
+
const chars = Array.from(word);
|
|
729
|
+
let currentLine = "";
|
|
730
|
+
let currentWidth = 0;
|
|
731
|
+
for (let i = 0;i < chars.length; i++) {
|
|
732
|
+
const char = chars[i];
|
|
733
|
+
const charWidth = this.measurer.measureChar(char);
|
|
734
|
+
const isLastChar = i === chars.length - 1;
|
|
735
|
+
const widthNeeded = isLastChar ? charWidth : charWidth + hyphenWidth;
|
|
736
|
+
if (currentWidth + widthNeeded <= maxWidthPx) {
|
|
737
|
+
currentLine += char;
|
|
738
|
+
currentWidth += charWidth;
|
|
739
|
+
} else {
|
|
740
|
+
if (currentLine.length >= opts.minCharsBeforeHyphen) {
|
|
741
|
+
const result = this.backoffForHyphen(currentLine, currentWidth, maxWidthPx, hyphenWidth, opts);
|
|
742
|
+
if (result.skipHyphen) {
|
|
743
|
+
lines.push(result.line);
|
|
744
|
+
} else {
|
|
745
|
+
lines.push(result.line + opts.hyphenChar);
|
|
746
|
+
}
|
|
747
|
+
currentLine = result.remainder + char;
|
|
748
|
+
currentWidth = this.measurer.measureText(currentLine);
|
|
749
|
+
} else {
|
|
750
|
+
if (currentLine) {
|
|
751
|
+
lines.push(currentLine);
|
|
752
|
+
}
|
|
753
|
+
currentLine = char;
|
|
754
|
+
currentWidth = charWidth;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
if (currentLine) {
|
|
759
|
+
lines.push(currentLine);
|
|
760
|
+
}
|
|
761
|
+
return lines.length > 0 ? lines : [word];
|
|
762
|
+
}
|
|
763
|
+
backoffForHyphen(line, lineWidth, maxWidthPx, hyphenWidth, opts) {
|
|
764
|
+
let adjustedLine = line;
|
|
765
|
+
let adjustedWidth = lineWidth;
|
|
766
|
+
let remainder = "";
|
|
767
|
+
while (adjustedWidth + hyphenWidth > maxWidthPx && adjustedLine.length > opts.minCharsBeforeHyphen) {
|
|
768
|
+
const lastChar = adjustedLine[adjustedLine.length - 1];
|
|
769
|
+
const lastCharWidth = this.measurer.measureChar(lastChar);
|
|
770
|
+
adjustedLine = adjustedLine.slice(0, -1);
|
|
771
|
+
adjustedWidth -= lastCharWidth;
|
|
772
|
+
remainder = lastChar + remainder;
|
|
773
|
+
if (adjustedLine.length > 0 && adjustedLine[adjustedLine.length - 1] === " ") {
|
|
774
|
+
adjustedLine = adjustedLine.trimEnd();
|
|
775
|
+
return { line: adjustedLine, remainder, skipHyphen: true };
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
return { line: adjustedLine, remainder, skipHyphen: false };
|
|
779
|
+
}
|
|
780
|
+
mergeOptions(options) {
|
|
781
|
+
return {
|
|
782
|
+
...this.defaultOptions,
|
|
783
|
+
...options
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
createEmptyResult(opts) {
|
|
787
|
+
return {
|
|
788
|
+
lines: [""],
|
|
789
|
+
truncated: false,
|
|
790
|
+
maxLineWidthPx: 0,
|
|
791
|
+
totalBytes: 0,
|
|
792
|
+
lineMetrics: [
|
|
793
|
+
{
|
|
794
|
+
text: "",
|
|
795
|
+
widthPx: 0,
|
|
796
|
+
bytes: 0,
|
|
797
|
+
utilizationPercent: 0,
|
|
798
|
+
endsWithHyphen: false,
|
|
799
|
+
fromExplicitNewline: false
|
|
800
|
+
}
|
|
801
|
+
],
|
|
802
|
+
originalText: "",
|
|
803
|
+
breakMode: opts.breakMode
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
// ../display-utils/src/helpers/DisplayHelpers.ts
|
|
808
|
+
class DisplayHelpers {
|
|
809
|
+
measurer;
|
|
810
|
+
wrapper;
|
|
811
|
+
profile;
|
|
812
|
+
constructor(measurer, wrapper) {
|
|
813
|
+
this.measurer = measurer;
|
|
814
|
+
this.wrapper = wrapper;
|
|
815
|
+
this.profile = measurer.getProfile();
|
|
816
|
+
}
|
|
817
|
+
truncateToLines(lines, maxLines, fromEnd = false) {
|
|
818
|
+
if (lines.length <= maxLines) {
|
|
819
|
+
return lines;
|
|
820
|
+
}
|
|
821
|
+
if (fromEnd) {
|
|
822
|
+
return lines.slice(-maxLines);
|
|
823
|
+
} else {
|
|
824
|
+
return lines.slice(0, maxLines);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
truncateWithEllipsis(text, maxWidthPx, ellipsis = "...") {
|
|
828
|
+
const width = maxWidthPx ?? this.profile.displayWidthPx;
|
|
829
|
+
const textWidth = this.measurer.measureText(text);
|
|
830
|
+
if (textWidth <= width) {
|
|
831
|
+
return {
|
|
832
|
+
text,
|
|
833
|
+
wasTruncated: false,
|
|
834
|
+
widthPx: textWidth,
|
|
835
|
+
originalLength: text.length,
|
|
836
|
+
truncatedLength: text.length
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
const ellipsisWidth = this.measurer.measureText(ellipsis);
|
|
840
|
+
const targetWidth = width - ellipsisWidth;
|
|
841
|
+
let truncated = "";
|
|
842
|
+
let currentWidth = 0;
|
|
843
|
+
for (const char of text) {
|
|
844
|
+
const charWidth = this.measurer.measureChar(char);
|
|
845
|
+
if (currentWidth + charWidth > targetWidth) {
|
|
846
|
+
break;
|
|
847
|
+
}
|
|
848
|
+
truncated += char;
|
|
849
|
+
currentWidth += charWidth;
|
|
850
|
+
}
|
|
851
|
+
truncated = truncated.trimEnd();
|
|
852
|
+
const finalText = truncated + ellipsis;
|
|
853
|
+
return {
|
|
854
|
+
text: finalText,
|
|
855
|
+
wasTruncated: true,
|
|
856
|
+
widthPx: this.measurer.measureText(finalText),
|
|
857
|
+
originalLength: text.length,
|
|
858
|
+
truncatedLength: truncated.length
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
estimateLineCount(text, maxWidthPx) {
|
|
862
|
+
if (!text)
|
|
863
|
+
return 1;
|
|
864
|
+
const width = maxWidthPx ?? this.profile.displayWidthPx;
|
|
865
|
+
const textWidth = this.measurer.measureText(text);
|
|
866
|
+
const newlineCount = (text.match(/\n/g) || []).length;
|
|
867
|
+
const wrappedLines = Math.ceil(textWidth / width);
|
|
868
|
+
return wrappedLines + newlineCount;
|
|
869
|
+
}
|
|
870
|
+
fitToScreen(text, options) {
|
|
871
|
+
const result = this.wrapper.wrap(text, options);
|
|
872
|
+
return result.lines.slice(0, this.profile.maxLines);
|
|
873
|
+
}
|
|
874
|
+
paginate(text, options) {
|
|
875
|
+
const wrapResult = this.wrapper.wrap(text, {
|
|
876
|
+
...options,
|
|
877
|
+
maxLines: Infinity,
|
|
878
|
+
maxBytes: Infinity
|
|
879
|
+
});
|
|
880
|
+
const linesPerPage = options?.maxLines ?? this.profile.maxLines;
|
|
881
|
+
const allLines = wrapResult.lines;
|
|
882
|
+
const pages = [];
|
|
883
|
+
for (let i = 0;i < allLines.length; i += linesPerPage) {
|
|
884
|
+
const pageLines = allLines.slice(i, i + linesPerPage);
|
|
885
|
+
const pageNumber = Math.floor(i / linesPerPage) + 1;
|
|
886
|
+
const totalPages = Math.ceil(allLines.length / linesPerPage);
|
|
887
|
+
pages.push({
|
|
888
|
+
lines: pageLines,
|
|
889
|
+
pageNumber,
|
|
890
|
+
totalPages,
|
|
891
|
+
isFirst: pageNumber === 1,
|
|
892
|
+
isLast: pageNumber === totalPages
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
return pages.length > 0 ? pages : [
|
|
896
|
+
{
|
|
897
|
+
lines: [""],
|
|
898
|
+
pageNumber: 1,
|
|
899
|
+
totalPages: 1,
|
|
900
|
+
isFirst: true,
|
|
901
|
+
isLast: true
|
|
902
|
+
}
|
|
903
|
+
];
|
|
904
|
+
}
|
|
905
|
+
calculateByteSize(text) {
|
|
906
|
+
return this.measurer.getByteSize(text);
|
|
907
|
+
}
|
|
908
|
+
exceedsByteLimit(text, maxBytes) {
|
|
909
|
+
const limit = maxBytes ?? this.profile.maxPayloadBytes;
|
|
910
|
+
return this.calculateByteSize(text) > limit;
|
|
911
|
+
}
|
|
912
|
+
splitIntoChunks(text, chunkSize) {
|
|
913
|
+
const size = chunkSize ?? this.profile.bleChunkSize;
|
|
914
|
+
const encoder = new TextEncoder;
|
|
915
|
+
const bytes = encoder.encode(text);
|
|
916
|
+
if (bytes.length <= size) {
|
|
917
|
+
return [
|
|
918
|
+
{
|
|
919
|
+
text,
|
|
920
|
+
index: 0,
|
|
921
|
+
totalChunks: 1,
|
|
922
|
+
bytes: bytes.length
|
|
923
|
+
}
|
|
924
|
+
];
|
|
925
|
+
}
|
|
926
|
+
const chunks = [];
|
|
927
|
+
let offset = 0;
|
|
928
|
+
while (offset < bytes.length) {
|
|
929
|
+
let endOffset = Math.min(offset + size, bytes.length);
|
|
930
|
+
if (endOffset < bytes.length) {
|
|
931
|
+
while (endOffset > offset && (bytes[endOffset] & 192) === 128) {
|
|
932
|
+
endOffset--;
|
|
933
|
+
}
|
|
934
|
+
let breakPoint = endOffset;
|
|
935
|
+
for (let i = endOffset - 1;i > offset + size / 2; i--) {
|
|
936
|
+
if (bytes[i] === 32 || bytes[i] === 10) {
|
|
937
|
+
breakPoint = i + 1;
|
|
938
|
+
break;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
if (breakPoint > offset) {
|
|
942
|
+
endOffset = breakPoint;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
const chunkBytes = bytes.slice(offset, endOffset);
|
|
946
|
+
const chunkText = new TextDecoder().decode(chunkBytes);
|
|
947
|
+
chunks.push({
|
|
948
|
+
text: chunkText,
|
|
949
|
+
index: chunks.length,
|
|
950
|
+
totalChunks: 0,
|
|
951
|
+
bytes: chunkBytes.length
|
|
952
|
+
});
|
|
953
|
+
offset = endOffset;
|
|
954
|
+
}
|
|
955
|
+
for (const chunk of chunks) {
|
|
956
|
+
chunk.totalChunks = chunks.length;
|
|
957
|
+
}
|
|
958
|
+
return chunks;
|
|
959
|
+
}
|
|
960
|
+
calculateUtilization(result) {
|
|
961
|
+
if (result.lines.length === 0 || result.lineMetrics.length === 0) {
|
|
962
|
+
return {
|
|
963
|
+
averageUtilization: 0,
|
|
964
|
+
minUtilization: 0,
|
|
965
|
+
maxUtilization: 0,
|
|
966
|
+
totalWastedPx: 0
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
const maxWidthPx = this.profile.displayWidthPx;
|
|
970
|
+
let totalUtilization = 0;
|
|
971
|
+
let minUtilization = 100;
|
|
972
|
+
let maxUtilization = 0;
|
|
973
|
+
let totalWastedPx = 0;
|
|
974
|
+
for (const metric of result.lineMetrics) {
|
|
975
|
+
totalUtilization += metric.utilizationPercent;
|
|
976
|
+
minUtilization = Math.min(minUtilization, metric.utilizationPercent);
|
|
977
|
+
maxUtilization = Math.max(maxUtilization, metric.utilizationPercent);
|
|
978
|
+
totalWastedPx += maxWidthPx - metric.widthPx;
|
|
979
|
+
}
|
|
980
|
+
return {
|
|
981
|
+
averageUtilization: Math.round(totalUtilization / result.lineMetrics.length),
|
|
982
|
+
minUtilization,
|
|
983
|
+
maxUtilization,
|
|
984
|
+
totalWastedPx
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
padToLineCount(lines, targetCount, padAtEnd = true) {
|
|
988
|
+
if (lines.length >= targetCount) {
|
|
989
|
+
return lines.slice(0, targetCount);
|
|
990
|
+
}
|
|
991
|
+
const padding = Array(targetCount - lines.length).fill("");
|
|
992
|
+
if (padAtEnd) {
|
|
993
|
+
return [...lines, ...padding];
|
|
994
|
+
} else {
|
|
995
|
+
return [...padding, ...lines];
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
joinLines(lines) {
|
|
999
|
+
return lines.join(`
|
|
1000
|
+
`);
|
|
1001
|
+
}
|
|
1002
|
+
getMeasurer() {
|
|
1003
|
+
return this.measurer;
|
|
1004
|
+
}
|
|
1005
|
+
getWrapper() {
|
|
1006
|
+
return this.wrapper;
|
|
1007
|
+
}
|
|
1008
|
+
getProfile() {
|
|
1009
|
+
return this.profile;
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
// ../display-utils/src/helpers/ScrollView.ts
|
|
1013
|
+
class ScrollView {
|
|
1014
|
+
measurer;
|
|
1015
|
+
wrapper;
|
|
1016
|
+
profile;
|
|
1017
|
+
viewportSize;
|
|
1018
|
+
allLines = [];
|
|
1019
|
+
wrapResult = null;
|
|
1020
|
+
scrollOffset = 0;
|
|
1021
|
+
constructor(measurer, wrapper, viewportSize) {
|
|
1022
|
+
this.measurer = measurer;
|
|
1023
|
+
this.wrapper = wrapper;
|
|
1024
|
+
this.profile = measurer.getProfile();
|
|
1025
|
+
this.viewportSize = viewportSize ?? this.profile.maxLines;
|
|
1026
|
+
}
|
|
1027
|
+
setContent(text, options) {
|
|
1028
|
+
this.wrapResult = this.wrapper.wrap(text, {
|
|
1029
|
+
...options,
|
|
1030
|
+
maxLines: Infinity,
|
|
1031
|
+
maxBytes: Infinity
|
|
1032
|
+
});
|
|
1033
|
+
this.allLines = this.wrapResult.lines;
|
|
1034
|
+
this.scrollOffset = 0;
|
|
1035
|
+
}
|
|
1036
|
+
appendContent(text, options, autoScroll = true) {
|
|
1037
|
+
const wasAtBottom = this.isAtBottom();
|
|
1038
|
+
const newResult = this.wrapper.wrap(text, {
|
|
1039
|
+
...options,
|
|
1040
|
+
maxLines: Infinity,
|
|
1041
|
+
maxBytes: Infinity
|
|
1042
|
+
});
|
|
1043
|
+
this.allLines = [...this.allLines, ...newResult.lines];
|
|
1044
|
+
if (autoScroll && wasAtBottom) {
|
|
1045
|
+
this.scrollToBottom();
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
getViewport() {
|
|
1049
|
+
const visibleLines = this.allLines.slice(this.scrollOffset, this.scrollOffset + this.viewportSize);
|
|
1050
|
+
while (visibleLines.length < this.viewportSize) {
|
|
1051
|
+
visibleLines.push("");
|
|
1052
|
+
}
|
|
1053
|
+
return {
|
|
1054
|
+
lines: visibleLines,
|
|
1055
|
+
position: this.getPosition(),
|
|
1056
|
+
contentTruncated: this.wrapResult?.truncated ?? false
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
getPosition() {
|
|
1060
|
+
const totalLines = this.allLines.length;
|
|
1061
|
+
const maxOffset = Math.max(0, totalLines - this.viewportSize);
|
|
1062
|
+
return {
|
|
1063
|
+
offset: this.scrollOffset,
|
|
1064
|
+
totalLines,
|
|
1065
|
+
visibleLines: this.viewportSize,
|
|
1066
|
+
maxOffset,
|
|
1067
|
+
atTop: this.scrollOffset === 0,
|
|
1068
|
+
atBottom: this.scrollOffset >= maxOffset,
|
|
1069
|
+
scrollPercent: maxOffset > 0 ? Math.round(this.scrollOffset / maxOffset * 100) : 100
|
|
1070
|
+
};
|
|
1071
|
+
}
|
|
1072
|
+
scrollTo(offset) {
|
|
1073
|
+
const maxOffset = Math.max(0, this.allLines.length - this.viewportSize);
|
|
1074
|
+
this.scrollOffset = Math.max(0, Math.min(offset, maxOffset));
|
|
1075
|
+
}
|
|
1076
|
+
scrollDown(lines = 1) {
|
|
1077
|
+
this.scrollTo(this.scrollOffset + lines);
|
|
1078
|
+
}
|
|
1079
|
+
scrollUp(lines = 1) {
|
|
1080
|
+
this.scrollTo(this.scrollOffset - lines);
|
|
1081
|
+
}
|
|
1082
|
+
pageDown() {
|
|
1083
|
+
this.scrollDown(this.viewportSize);
|
|
1084
|
+
}
|
|
1085
|
+
pageUp() {
|
|
1086
|
+
this.scrollUp(this.viewportSize);
|
|
1087
|
+
}
|
|
1088
|
+
scrollToTop() {
|
|
1089
|
+
this.scrollOffset = 0;
|
|
1090
|
+
}
|
|
1091
|
+
scrollToBottom() {
|
|
1092
|
+
const maxOffset = Math.max(0, this.allLines.length - this.viewportSize);
|
|
1093
|
+
this.scrollOffset = maxOffset;
|
|
1094
|
+
}
|
|
1095
|
+
scrollToLine(lineIndex, position = "top") {
|
|
1096
|
+
let targetOffset;
|
|
1097
|
+
switch (position) {
|
|
1098
|
+
case "top":
|
|
1099
|
+
targetOffset = lineIndex;
|
|
1100
|
+
break;
|
|
1101
|
+
case "center":
|
|
1102
|
+
targetOffset = lineIndex - Math.floor(this.viewportSize / 2);
|
|
1103
|
+
break;
|
|
1104
|
+
case "bottom":
|
|
1105
|
+
targetOffset = lineIndex - this.viewportSize + 1;
|
|
1106
|
+
break;
|
|
1107
|
+
}
|
|
1108
|
+
this.scrollTo(targetOffset);
|
|
1109
|
+
}
|
|
1110
|
+
scrollToPercent(percent) {
|
|
1111
|
+
const maxOffset = Math.max(0, this.allLines.length - this.viewportSize);
|
|
1112
|
+
const targetOffset = Math.round(percent / 100 * maxOffset);
|
|
1113
|
+
this.scrollTo(targetOffset);
|
|
1114
|
+
}
|
|
1115
|
+
isAtTop() {
|
|
1116
|
+
return this.scrollOffset === 0;
|
|
1117
|
+
}
|
|
1118
|
+
isAtBottom() {
|
|
1119
|
+
const maxOffset = Math.max(0, this.allLines.length - this.viewportSize);
|
|
1120
|
+
return this.scrollOffset >= maxOffset;
|
|
1121
|
+
}
|
|
1122
|
+
isScrollable() {
|
|
1123
|
+
return this.allLines.length > this.viewportSize;
|
|
1124
|
+
}
|
|
1125
|
+
getAllLines() {
|
|
1126
|
+
return [...this.allLines];
|
|
1127
|
+
}
|
|
1128
|
+
getTotalLines() {
|
|
1129
|
+
return this.allLines.length;
|
|
1130
|
+
}
|
|
1131
|
+
getViewportSize() {
|
|
1132
|
+
return this.viewportSize;
|
|
1133
|
+
}
|
|
1134
|
+
clear() {
|
|
1135
|
+
this.allLines = [];
|
|
1136
|
+
this.wrapResult = null;
|
|
1137
|
+
this.scrollOffset = 0;
|
|
1138
|
+
}
|
|
1139
|
+
getMeasurer() {
|
|
1140
|
+
return this.measurer;
|
|
1141
|
+
}
|
|
1142
|
+
getWrapper() {
|
|
1143
|
+
return this.wrapper;
|
|
1144
|
+
}
|
|
1145
|
+
getProfile() {
|
|
1146
|
+
return this.profile;
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
// ../display-utils/src/index.ts
|
|
1150
|
+
function createDisplayToolkit(profile = G1_PROFILE, wrapOptions) {
|
|
1151
|
+
const measurer = new TextMeasurer(profile);
|
|
1152
|
+
const wrapper = new TextWrapper(measurer, wrapOptions);
|
|
1153
|
+
const helpers = new DisplayHelpers(measurer, wrapper);
|
|
1154
|
+
return {
|
|
1155
|
+
measurer,
|
|
1156
|
+
wrapper,
|
|
1157
|
+
helpers,
|
|
1158
|
+
profile
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
function createG1Toolkit() {
|
|
1162
|
+
return createDisplayToolkit(G1_PROFILE, {
|
|
1163
|
+
breakMode: "character",
|
|
1164
|
+
hyphenChar: "-",
|
|
1165
|
+
minCharsBeforeHyphen: 3
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
function createG1LegacyToolkit() {
|
|
1169
|
+
return createDisplayToolkit(G1_PROFILE_LEGACY, {
|
|
1170
|
+
breakMode: "character",
|
|
1171
|
+
hyphenChar: "-",
|
|
1172
|
+
minCharsBeforeHyphen: 3
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1175
|
+
export {
|
|
1176
|
+
needsHyphenForBreak,
|
|
1177
|
+
isUnsupportedScript,
|
|
1178
|
+
isUniformWidthScript,
|
|
1179
|
+
isKoreanCharacter,
|
|
1180
|
+
isCJKCharacter,
|
|
1181
|
+
detectScript,
|
|
1182
|
+
createG1Toolkit,
|
|
1183
|
+
createG1LegacyToolkit,
|
|
1184
|
+
createDisplayToolkit,
|
|
1185
|
+
TextWrapper,
|
|
1186
|
+
TextMeasurer,
|
|
1187
|
+
ScrollView,
|
|
1188
|
+
SCRIPT_RANGES,
|
|
1189
|
+
G1_SPACE_WIDTH_PX,
|
|
1190
|
+
G1_PROFILE_LEGACY,
|
|
1191
|
+
G1_PROFILE,
|
|
1192
|
+
G1_HYPHEN_WIDTH_PX,
|
|
1193
|
+
DisplayHelpers,
|
|
1194
|
+
DEFAULT_WRAP_OPTIONS
|
|
1195
|
+
};
|
|
1196
|
+
|
|
1197
|
+
//# debugId=5DD60770487F882B64756E2164756E21
|