chat-layout 0.0.18 → 0.1.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/README.md +14 -2
- package/index.d.mts +225 -0
- package/index.mjs +799 -0
- package/index.mjs.map +1 -0
- package/package.json +17 -5
- package/index.d.ts +0 -214
- package/index.js +0 -1030
- package/index.js.map +0 -14
- package/tsconfig.json +0 -28
package/index.mjs
ADDED
|
@@ -0,0 +1,799 @@
|
|
|
1
|
+
import { layoutWithLines, prepareWithSegments } from "@chenglou/pretext";
|
|
2
|
+
//#region src/text.ts
|
|
3
|
+
function preprocessSegments(text) {
|
|
4
|
+
return text.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
5
|
+
}
|
|
6
|
+
function layoutFirstLine(ctx, text, maxWidth) {
|
|
7
|
+
if (maxWidth < 0) maxWidth = 0;
|
|
8
|
+
const segment = preprocessSegments(text)[0];
|
|
9
|
+
if (!segment) return {
|
|
10
|
+
width: 0,
|
|
11
|
+
text: "",
|
|
12
|
+
shift: 0
|
|
13
|
+
};
|
|
14
|
+
const { fontBoundingBoxAscent: ascent = 0, fontBoundingBoxDescent: descent = 0 } = ctx.graphics.measureText(segment);
|
|
15
|
+
const shift = ascent - descent;
|
|
16
|
+
if (maxWidth === 0) return {
|
|
17
|
+
width: 0,
|
|
18
|
+
text: "",
|
|
19
|
+
shift
|
|
20
|
+
};
|
|
21
|
+
const { lines } = layoutWithLines(prepareWithSegments(segment, ctx.graphics.font), maxWidth, 0);
|
|
22
|
+
if (lines.length === 0) return {
|
|
23
|
+
width: 0,
|
|
24
|
+
text: "",
|
|
25
|
+
shift
|
|
26
|
+
};
|
|
27
|
+
return {
|
|
28
|
+
width: lines[0].width,
|
|
29
|
+
text: lines[0].text,
|
|
30
|
+
shift
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function layoutText(ctx, text, maxWidth) {
|
|
34
|
+
if (maxWidth < 0) maxWidth = 0;
|
|
35
|
+
const segments = preprocessSegments(text);
|
|
36
|
+
if (segments.length === 0 || maxWidth === 0) return {
|
|
37
|
+
width: 0,
|
|
38
|
+
lines: []
|
|
39
|
+
};
|
|
40
|
+
const font = ctx.graphics.font;
|
|
41
|
+
let width = 0;
|
|
42
|
+
const lines = [];
|
|
43
|
+
for (const segment of segments) {
|
|
44
|
+
const { fontBoundingBoxAscent: ascent = 0, fontBoundingBoxDescent: descent = 0 } = ctx.graphics.measureText(segment);
|
|
45
|
+
const shift = ascent - descent;
|
|
46
|
+
const { lines: segLines } = layoutWithLines(prepareWithSegments(segment, font), maxWidth, 0);
|
|
47
|
+
for (const segLine of segLines) {
|
|
48
|
+
width = Math.max(width, segLine.width);
|
|
49
|
+
lines.push({
|
|
50
|
+
width: segLine.width,
|
|
51
|
+
text: segLine.text,
|
|
52
|
+
shift
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
width,
|
|
58
|
+
lines
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/utils.ts
|
|
63
|
+
function shallow(object) {
|
|
64
|
+
return Object.create(object);
|
|
65
|
+
}
|
|
66
|
+
function shallowMerge(object, other) {
|
|
67
|
+
return {
|
|
68
|
+
__proto__: object,
|
|
69
|
+
...other
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/registry.ts
|
|
74
|
+
const registry = /* @__PURE__ */ new WeakMap();
|
|
75
|
+
function registerNodeParent(node, parent) {
|
|
76
|
+
registry.set(node, parent);
|
|
77
|
+
}
|
|
78
|
+
function unregisterNodeParent(node) {
|
|
79
|
+
registry.delete(node);
|
|
80
|
+
}
|
|
81
|
+
function getNodeParent(node) {
|
|
82
|
+
return registry.get(node);
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/nodes.ts
|
|
86
|
+
var Group = class {
|
|
87
|
+
constructor(children) {
|
|
88
|
+
this.children = children;
|
|
89
|
+
for (const child of children) registerNodeParent(child, this);
|
|
90
|
+
}
|
|
91
|
+
get flex() {
|
|
92
|
+
return this.children.some((item) => item.flex);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
var VStack = class extends Group {
|
|
96
|
+
constructor(children, options = {}) {
|
|
97
|
+
super(children);
|
|
98
|
+
this.options = options;
|
|
99
|
+
}
|
|
100
|
+
measure(ctx) {
|
|
101
|
+
let width = 0;
|
|
102
|
+
let height = 0;
|
|
103
|
+
if (this.options.alignment != null) ctx.alignment = this.options.alignment;
|
|
104
|
+
for (const [index, child] of this.children.entries()) {
|
|
105
|
+
if (this.options.gap != null && index !== 0) height += this.options.gap;
|
|
106
|
+
const result = shallow(ctx).measureNode(child);
|
|
107
|
+
height += result.height;
|
|
108
|
+
width = Math.max(width, result.width);
|
|
109
|
+
}
|
|
110
|
+
ctx.remainingWidth -= width;
|
|
111
|
+
return {
|
|
112
|
+
width,
|
|
113
|
+
height
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
draw(ctx, x, y) {
|
|
117
|
+
let result = false;
|
|
118
|
+
const fullWidth = ctx.measureNode(this).width;
|
|
119
|
+
const alignment = this.options.alignment ?? ctx.alignment;
|
|
120
|
+
if (this.options.alignment != null) ctx.alignment = this.options.alignment;
|
|
121
|
+
for (const [index, child] of this.children.entries()) {
|
|
122
|
+
if (this.options.gap != null && index !== 0) y += this.options.gap;
|
|
123
|
+
const { width, height } = shallow(ctx).measureNode(child);
|
|
124
|
+
const curCtx = shallow(ctx);
|
|
125
|
+
let requestRedraw;
|
|
126
|
+
if (alignment === "right") requestRedraw = child.draw(curCtx, x + fullWidth - width, y);
|
|
127
|
+
else if (alignment === "center") requestRedraw = child.draw(curCtx, x + (fullWidth - width) / 2, y);
|
|
128
|
+
else requestRedraw = child.draw(curCtx, x, y);
|
|
129
|
+
result ||= requestRedraw;
|
|
130
|
+
y += height;
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
hittest(ctx, test) {
|
|
135
|
+
let y = 0;
|
|
136
|
+
const fullWidth = ctx.measureNode(this).width;
|
|
137
|
+
const alignment = this.options.alignment ?? ctx.alignment;
|
|
138
|
+
if (this.options.alignment != null) ctx.alignment = this.options.alignment;
|
|
139
|
+
for (const [index, child] of this.children.entries()) {
|
|
140
|
+
if (this.options.gap != null && index !== 0) y += this.options.gap;
|
|
141
|
+
const { width, height } = shallow(ctx).measureNode(child);
|
|
142
|
+
const curCtx = shallow(ctx);
|
|
143
|
+
if (test.y >= y && test.y < y + height) {
|
|
144
|
+
let x;
|
|
145
|
+
if (alignment === "right") x = test.x - fullWidth + width;
|
|
146
|
+
else if (alignment === "center") x = test.x - (fullWidth - width) / 2;
|
|
147
|
+
else x = test.x;
|
|
148
|
+
if (x < 0 || x >= width) return false;
|
|
149
|
+
return child.hittest(curCtx, shallowMerge(test, {
|
|
150
|
+
x,
|
|
151
|
+
y: test.y - y
|
|
152
|
+
}));
|
|
153
|
+
}
|
|
154
|
+
y += height;
|
|
155
|
+
}
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
var HStack = class extends Group {
|
|
160
|
+
constructor(children, options = {}) {
|
|
161
|
+
super(children);
|
|
162
|
+
this.children = children;
|
|
163
|
+
this.options = options;
|
|
164
|
+
}
|
|
165
|
+
measure(ctx) {
|
|
166
|
+
let width = 0;
|
|
167
|
+
let height = 0;
|
|
168
|
+
let firstFlex;
|
|
169
|
+
for (const [index, child] of this.children.entries()) {
|
|
170
|
+
if (this.options.gap != null && index !== 0) width += this.options.gap;
|
|
171
|
+
if (firstFlex == null && child.flex) {
|
|
172
|
+
firstFlex = child;
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const curCtx = shallow(ctx);
|
|
176
|
+
curCtx.remainingWidth = ctx.remainingWidth - width;
|
|
177
|
+
const result = curCtx.measureNode(child);
|
|
178
|
+
width += result.width;
|
|
179
|
+
height = Math.max(height, result.height);
|
|
180
|
+
}
|
|
181
|
+
if (firstFlex != null) {
|
|
182
|
+
const curCtx = shallow(ctx);
|
|
183
|
+
curCtx.remainingWidth = ctx.remainingWidth - width;
|
|
184
|
+
const result = curCtx.measureNode(firstFlex);
|
|
185
|
+
width += result.width;
|
|
186
|
+
height = Math.max(height, result.height);
|
|
187
|
+
}
|
|
188
|
+
return {
|
|
189
|
+
width,
|
|
190
|
+
height
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
draw(ctx, x, y) {
|
|
194
|
+
let result = false;
|
|
195
|
+
const reverse = this.options.reverse ?? ctx.reverse;
|
|
196
|
+
if (this.options.reverse) ctx.reverse = this.options.reverse;
|
|
197
|
+
if (reverse) {
|
|
198
|
+
x += ctx.measureNode(this).width;
|
|
199
|
+
for (const [index, child] of this.children.entries()) {
|
|
200
|
+
const gap = this.options.gap != null && index !== 0 ? this.options.gap : void 0;
|
|
201
|
+
if (gap) {
|
|
202
|
+
x -= gap;
|
|
203
|
+
ctx.remainingWidth -= gap;
|
|
204
|
+
}
|
|
205
|
+
const { width } = shallow(ctx).measureNode(child);
|
|
206
|
+
x -= width;
|
|
207
|
+
const requestRedraw = child.draw(shallow(ctx), x, y);
|
|
208
|
+
result ||= requestRedraw;
|
|
209
|
+
ctx.remainingWidth -= width;
|
|
210
|
+
}
|
|
211
|
+
} else for (const [index, child] of this.children.entries()) {
|
|
212
|
+
const gap = this.options.gap != null && index !== 0 ? this.options.gap : void 0;
|
|
213
|
+
if (gap) {
|
|
214
|
+
x += gap;
|
|
215
|
+
ctx.remainingWidth -= gap;
|
|
216
|
+
}
|
|
217
|
+
const requestRedraw = child.draw(shallow(ctx), x, y);
|
|
218
|
+
result ||= requestRedraw;
|
|
219
|
+
const { width } = shallow(ctx).measureNode(child);
|
|
220
|
+
ctx.remainingWidth -= width;
|
|
221
|
+
x += width;
|
|
222
|
+
}
|
|
223
|
+
return result;
|
|
224
|
+
}
|
|
225
|
+
hittest(ctx, test) {
|
|
226
|
+
const reverse = this.options.reverse ?? ctx.reverse;
|
|
227
|
+
if (this.options.reverse) ctx.reverse = this.options.reverse;
|
|
228
|
+
if (reverse) {
|
|
229
|
+
let x = ctx.measureNode(this).width;
|
|
230
|
+
for (const [index, child] of this.children.entries()) {
|
|
231
|
+
const gap = this.options.gap != null && index !== 0 ? this.options.gap : void 0;
|
|
232
|
+
if (gap) {
|
|
233
|
+
x -= gap;
|
|
234
|
+
ctx.remainingWidth -= gap;
|
|
235
|
+
}
|
|
236
|
+
const { width, height } = shallow(ctx).measureNode(child);
|
|
237
|
+
x -= width;
|
|
238
|
+
if (x <= test.x && test.x < x + width) {
|
|
239
|
+
if (test.y >= height) return false;
|
|
240
|
+
return child.hittest(shallow(ctx), shallowMerge(test, { x: test.x - x }));
|
|
241
|
+
}
|
|
242
|
+
ctx.remainingWidth -= width;
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
let x = 0;
|
|
246
|
+
for (const [index, child] of this.children.entries()) {
|
|
247
|
+
const gap = this.options.gap != null && index !== 0 ? this.options.gap : void 0;
|
|
248
|
+
if (gap) {
|
|
249
|
+
x += gap;
|
|
250
|
+
ctx.remainingWidth -= gap;
|
|
251
|
+
}
|
|
252
|
+
const { width, height } = shallow(ctx).measureNode(child);
|
|
253
|
+
if (x <= test.x && test.x < x + width) {
|
|
254
|
+
if (test.y >= height) return false;
|
|
255
|
+
return child.hittest(shallow(ctx), shallowMerge(test, { x: test.x - x }));
|
|
256
|
+
}
|
|
257
|
+
x += width;
|
|
258
|
+
ctx.remainingWidth -= width;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
var Wrapper = class {
|
|
265
|
+
#inner;
|
|
266
|
+
constructor(inner) {
|
|
267
|
+
this.#inner = inner;
|
|
268
|
+
registerNodeParent(this.#inner, this);
|
|
269
|
+
}
|
|
270
|
+
get inner() {
|
|
271
|
+
return this.#inner;
|
|
272
|
+
}
|
|
273
|
+
set inner(newNode) {
|
|
274
|
+
if (newNode === this.#inner) return;
|
|
275
|
+
unregisterNodeParent(this.#inner);
|
|
276
|
+
this.#inner = newNode;
|
|
277
|
+
registerNodeParent(newNode, this);
|
|
278
|
+
}
|
|
279
|
+
get flex() {
|
|
280
|
+
return this.inner.flex;
|
|
281
|
+
}
|
|
282
|
+
measure(ctx) {
|
|
283
|
+
return this.inner.measure(ctx);
|
|
284
|
+
}
|
|
285
|
+
draw(ctx, x, y) {
|
|
286
|
+
return this.inner.draw(ctx, x, y);
|
|
287
|
+
}
|
|
288
|
+
hittest(ctx, test) {
|
|
289
|
+
return this.inner.hittest(ctx, test);
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
var PaddingBox = class extends Wrapper {
|
|
293
|
+
constructor(inner, padding = {}) {
|
|
294
|
+
super(inner);
|
|
295
|
+
this.padding = padding;
|
|
296
|
+
}
|
|
297
|
+
get #top() {
|
|
298
|
+
return this.padding.top ?? 0;
|
|
299
|
+
}
|
|
300
|
+
get #bottom() {
|
|
301
|
+
return this.padding.bottom ?? 0;
|
|
302
|
+
}
|
|
303
|
+
get #left() {
|
|
304
|
+
return this.padding.left ?? 0;
|
|
305
|
+
}
|
|
306
|
+
get #right() {
|
|
307
|
+
return this.padding.right ?? 0;
|
|
308
|
+
}
|
|
309
|
+
measure(ctx) {
|
|
310
|
+
ctx.remainingWidth -= this.#left + this.#right;
|
|
311
|
+
const { width, height } = ctx.measureNode(this.inner);
|
|
312
|
+
return {
|
|
313
|
+
width: width + this.#left + this.#right,
|
|
314
|
+
height: height + this.#top + this.#bottom
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
draw(ctx, x, y) {
|
|
318
|
+
ctx.remainingWidth -= this.#left + this.#right;
|
|
319
|
+
return this.inner.draw(ctx, x + this.#left, y + this.#top);
|
|
320
|
+
}
|
|
321
|
+
hittest(ctx, test) {
|
|
322
|
+
ctx.remainingWidth -= this.#left + this.#right;
|
|
323
|
+
const { width, height } = shallow(ctx).measureNode(this.inner);
|
|
324
|
+
if (0 <= test.x - this.#left && test.x - this.#left < width && 0 <= test.y - this.#top && test.y - this.#top < height) return this.inner.hittest(shallow(ctx), shallowMerge(test, {
|
|
325
|
+
x: test.x - this.#left,
|
|
326
|
+
y: test.y - this.#top
|
|
327
|
+
}));
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
var AlignBox = class extends Wrapper {
|
|
332
|
+
#shift = 0;
|
|
333
|
+
constructor(inner, options) {
|
|
334
|
+
super(inner);
|
|
335
|
+
this.options = options;
|
|
336
|
+
}
|
|
337
|
+
measure(ctx) {
|
|
338
|
+
ctx.alignment = this.options.alignment;
|
|
339
|
+
const { width, height } = ctx.measureNode(this.inner);
|
|
340
|
+
switch (this.options.alignment) {
|
|
341
|
+
case "center":
|
|
342
|
+
this.#shift = (ctx.remainingWidth - width) / 2;
|
|
343
|
+
break;
|
|
344
|
+
case "right":
|
|
345
|
+
this.#shift = ctx.remainingWidth - width;
|
|
346
|
+
break;
|
|
347
|
+
default: this.#shift = 0;
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
width: ctx.remainingWidth,
|
|
351
|
+
height
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
draw(ctx, x, y) {
|
|
355
|
+
ctx.alignment = this.options.alignment;
|
|
356
|
+
return this.inner.draw(ctx, x + this.#shift, y);
|
|
357
|
+
}
|
|
358
|
+
hittest(ctx, test) {
|
|
359
|
+
ctx.alignment = this.options.alignment;
|
|
360
|
+
const { width } = shallow(ctx).measureNode(this.inner);
|
|
361
|
+
if (0 <= test.x - this.#shift && test.x - this.#shift < width) return this.inner.hittest(shallow(ctx), shallowMerge(test, { x: test.x - this.#shift }));
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
var MultilineText = class {
|
|
366
|
+
#width = 0;
|
|
367
|
+
#lines = [];
|
|
368
|
+
constructor(text, options) {
|
|
369
|
+
this.text = text;
|
|
370
|
+
this.options = options;
|
|
371
|
+
}
|
|
372
|
+
get flex() {
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
measure(ctx) {
|
|
376
|
+
return ctx.with((g) => {
|
|
377
|
+
g.font = this.options.font;
|
|
378
|
+
const { width, lines } = layoutText(ctx, this.text, ctx.remainingWidth);
|
|
379
|
+
this.#width = width;
|
|
380
|
+
this.#lines = lines;
|
|
381
|
+
return {
|
|
382
|
+
width: this.#width,
|
|
383
|
+
height: this.#lines.length * this.options.lineHeight
|
|
384
|
+
};
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
draw(ctx, x, y) {
|
|
388
|
+
return ctx.with((g) => {
|
|
389
|
+
g.font = this.options.font;
|
|
390
|
+
g.fillStyle = ctx.resolveDynValue(this.options.style);
|
|
391
|
+
switch (this.options.alignment) {
|
|
392
|
+
case "left":
|
|
393
|
+
for (const { text, shift } of this.#lines) {
|
|
394
|
+
g.fillText(text, x, y + (this.options.lineHeight + shift) / 2);
|
|
395
|
+
y += this.options.lineHeight;
|
|
396
|
+
}
|
|
397
|
+
break;
|
|
398
|
+
case "right":
|
|
399
|
+
x += this.#width;
|
|
400
|
+
g.textAlign = "right";
|
|
401
|
+
for (const { text, shift } of this.#lines) {
|
|
402
|
+
g.fillText(text, x, y + (this.options.lineHeight + shift) / 2);
|
|
403
|
+
y += this.options.lineHeight;
|
|
404
|
+
}
|
|
405
|
+
break;
|
|
406
|
+
case "center":
|
|
407
|
+
x += this.#width / 2;
|
|
408
|
+
g.textAlign = "center";
|
|
409
|
+
for (const { text, shift } of this.#lines) {
|
|
410
|
+
g.fillText(text, x, y + (this.options.lineHeight + shift) / 2);
|
|
411
|
+
y += this.options.lineHeight;
|
|
412
|
+
}
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
return false;
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
hittest(_ctx, _test) {
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
var Text = class {
|
|
423
|
+
#width = 0;
|
|
424
|
+
#text = "";
|
|
425
|
+
#shift = 0;
|
|
426
|
+
constructor(text, options) {
|
|
427
|
+
this.text = text;
|
|
428
|
+
this.options = options;
|
|
429
|
+
}
|
|
430
|
+
get flex() {
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
measure(ctx) {
|
|
434
|
+
return ctx.with((g) => {
|
|
435
|
+
g.font = this.options.font;
|
|
436
|
+
const { width, text, shift } = layoutFirstLine(ctx, this.text, ctx.remainingWidth);
|
|
437
|
+
this.#width = width;
|
|
438
|
+
this.#text = text;
|
|
439
|
+
this.#shift = shift;
|
|
440
|
+
return {
|
|
441
|
+
width: this.#width,
|
|
442
|
+
height: this.options.lineHeight
|
|
443
|
+
};
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
draw(ctx, x, y) {
|
|
447
|
+
return ctx.with((g) => {
|
|
448
|
+
g.font = this.options.font;
|
|
449
|
+
g.fillStyle = ctx.resolveDynValue(this.options.style);
|
|
450
|
+
g.fillText(this.#text, x, y + (this.options.lineHeight + this.#shift) / 2);
|
|
451
|
+
return false;
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
hittest(_ctx, _test) {
|
|
455
|
+
return false;
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
var Fixed = class {
|
|
459
|
+
constructor(width, height) {
|
|
460
|
+
this.width = width;
|
|
461
|
+
this.height = height;
|
|
462
|
+
}
|
|
463
|
+
get flex() {
|
|
464
|
+
return false;
|
|
465
|
+
}
|
|
466
|
+
measure(_ctx) {
|
|
467
|
+
return {
|
|
468
|
+
width: this.width,
|
|
469
|
+
height: this.height
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
draw(_ctx, _x, _y) {
|
|
473
|
+
return false;
|
|
474
|
+
}
|
|
475
|
+
hittest(_ctx, _test) {
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
//#endregion
|
|
480
|
+
//#region src/renderer.ts
|
|
481
|
+
var BaseRenderer = class {
|
|
482
|
+
graphics;
|
|
483
|
+
#ctx;
|
|
484
|
+
#lastWidth;
|
|
485
|
+
#cache = /* @__PURE__ */ new WeakMap();
|
|
486
|
+
get context() {
|
|
487
|
+
return shallow(this.#ctx);
|
|
488
|
+
}
|
|
489
|
+
constructor(graphics, options) {
|
|
490
|
+
this.options = options;
|
|
491
|
+
this.graphics = graphics;
|
|
492
|
+
this.graphics.textRendering = "optimizeLegibility";
|
|
493
|
+
const self = this;
|
|
494
|
+
this.#ctx = {
|
|
495
|
+
graphics: this.graphics,
|
|
496
|
+
get remainingWidth() {
|
|
497
|
+
return this.graphics.canvas.clientWidth;
|
|
498
|
+
},
|
|
499
|
+
set remainingWidth(value) {
|
|
500
|
+
Object.defineProperty(this, "remainingWidth", {
|
|
501
|
+
value,
|
|
502
|
+
writable: true
|
|
503
|
+
});
|
|
504
|
+
},
|
|
505
|
+
alignment: "left",
|
|
506
|
+
reverse: false,
|
|
507
|
+
measureNode(node) {
|
|
508
|
+
return self.measureNode(node, this);
|
|
509
|
+
},
|
|
510
|
+
invalidateNode: this.invalidateNode.bind(this),
|
|
511
|
+
resolveDynValue(value) {
|
|
512
|
+
if (typeof value === "function") return value(this.graphics);
|
|
513
|
+
return value;
|
|
514
|
+
},
|
|
515
|
+
with(cb) {
|
|
516
|
+
this.graphics.save();
|
|
517
|
+
try {
|
|
518
|
+
return cb(this.graphics);
|
|
519
|
+
} finally {
|
|
520
|
+
this.graphics.restore();
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
this.#lastWidth = this.graphics.canvas.clientWidth;
|
|
525
|
+
}
|
|
526
|
+
invalidateNode(node) {
|
|
527
|
+
this.#cache.delete(node);
|
|
528
|
+
let it = node;
|
|
529
|
+
while (it = getNodeParent(it)) this.#cache.delete(it);
|
|
530
|
+
}
|
|
531
|
+
measureNode(node, ctx) {
|
|
532
|
+
if (this.#lastWidth !== this.graphics.canvas.clientWidth) {
|
|
533
|
+
this.#cache = /* @__PURE__ */ new WeakMap();
|
|
534
|
+
this.#lastWidth = this.graphics.canvas.clientWidth;
|
|
535
|
+
} else {
|
|
536
|
+
const result = this.#cache.get(node);
|
|
537
|
+
if (result != null) return result;
|
|
538
|
+
}
|
|
539
|
+
const result = node.measure(ctx ?? this.context);
|
|
540
|
+
this.#cache.set(node, result);
|
|
541
|
+
return result;
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
var DebugRenderer = class extends BaseRenderer {
|
|
545
|
+
draw(node) {
|
|
546
|
+
const { clientWidth: viewportWidth, clientHeight: viewportHeight } = this.graphics.canvas;
|
|
547
|
+
this.graphics.clearRect(0, 0, viewportWidth, viewportHeight);
|
|
548
|
+
return node.draw(this.context, 0, 0);
|
|
549
|
+
}
|
|
550
|
+
hittest(node, test) {
|
|
551
|
+
return node.hittest(this.context, test);
|
|
552
|
+
}
|
|
553
|
+
};
|
|
554
|
+
function memoRenderItem(renderItem) {
|
|
555
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
556
|
+
function fn(item) {
|
|
557
|
+
const key = item;
|
|
558
|
+
const cached = cache.get(key);
|
|
559
|
+
if (cached != null) return cached;
|
|
560
|
+
const result = renderItem(item);
|
|
561
|
+
cache.set(key, result);
|
|
562
|
+
return result;
|
|
563
|
+
}
|
|
564
|
+
return Object.assign(fn, { reset: (key) => cache.delete(key) });
|
|
565
|
+
}
|
|
566
|
+
var ListState = class {
|
|
567
|
+
offset = 0;
|
|
568
|
+
position = NaN;
|
|
569
|
+
items = [];
|
|
570
|
+
unshift(...items) {
|
|
571
|
+
this.unshiftAll(items);
|
|
572
|
+
}
|
|
573
|
+
unshiftAll(items) {
|
|
574
|
+
this.position += items.length;
|
|
575
|
+
this.items = items.concat(this.items);
|
|
576
|
+
}
|
|
577
|
+
push(...items) {
|
|
578
|
+
this.pushAll(items);
|
|
579
|
+
}
|
|
580
|
+
pushAll(items) {
|
|
581
|
+
this.items.push(...items);
|
|
582
|
+
}
|
|
583
|
+
reset() {
|
|
584
|
+
this.items = [];
|
|
585
|
+
this.offset = 0;
|
|
586
|
+
this.position = NaN;
|
|
587
|
+
}
|
|
588
|
+
resetScroll() {
|
|
589
|
+
this.offset = 0;
|
|
590
|
+
this.position = NaN;
|
|
591
|
+
}
|
|
592
|
+
applyScroll(delta) {
|
|
593
|
+
this.offset += delta;
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
var VirtualizedRenderer = class extends BaseRenderer {
|
|
597
|
+
get position() {
|
|
598
|
+
return this.options.list.position;
|
|
599
|
+
}
|
|
600
|
+
set position(value) {
|
|
601
|
+
this.options.list.position = value;
|
|
602
|
+
}
|
|
603
|
+
get offset() {
|
|
604
|
+
return this.options.list.offset;
|
|
605
|
+
}
|
|
606
|
+
set offset(value) {
|
|
607
|
+
this.options.list.offset = value;
|
|
608
|
+
}
|
|
609
|
+
get items() {
|
|
610
|
+
return this.options.list.items;
|
|
611
|
+
}
|
|
612
|
+
set items(value) {
|
|
613
|
+
this.options.list.items = value;
|
|
614
|
+
}
|
|
615
|
+
_renderDrawList(list, shift, feedback) {
|
|
616
|
+
let result = false;
|
|
617
|
+
const viewportHeight = this.graphics.canvas.clientHeight;
|
|
618
|
+
for (const { idx, node, offset, height } of list) {
|
|
619
|
+
const y = offset + shift;
|
|
620
|
+
if (y + height < 0 || y > viewportHeight) continue;
|
|
621
|
+
if (feedback != null) {
|
|
622
|
+
feedback.minIdx = Number.isNaN(feedback.minIdx) ? idx : Math.min(idx, feedback.minIdx);
|
|
623
|
+
feedback.maxIdx = Number.isNaN(feedback.maxIdx) ? idx : Math.max(idx, feedback.maxIdx);
|
|
624
|
+
if (feedback.minIdx === idx) feedback.min = idx - Math.min(0, y) / height;
|
|
625
|
+
if (feedback.maxIdx === idx) feedback.max = idx - Math.max(0, y + height - viewportHeight) / height;
|
|
626
|
+
}
|
|
627
|
+
if (node.draw(this.context, 0, y)) result = true;
|
|
628
|
+
}
|
|
629
|
+
return result;
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
var TimelineRenderer = class extends VirtualizedRenderer {
|
|
633
|
+
render(feedback) {
|
|
634
|
+
const { clientWidth: viewportWidth, clientHeight: viewportHeight } = this.graphics.canvas;
|
|
635
|
+
this.graphics.clearRect(0, 0, viewportWidth, viewportHeight);
|
|
636
|
+
let drawLength = 0;
|
|
637
|
+
if (Number.isNaN(this.position)) this.position = 0;
|
|
638
|
+
if (this.offset > 0) if (this.position === 0) this.offset = 0;
|
|
639
|
+
else {
|
|
640
|
+
for (let i = this.position - 1; i >= 0; i -= 1) {
|
|
641
|
+
const item = this.items[i];
|
|
642
|
+
const node = this.options.renderItem(item);
|
|
643
|
+
const { height } = this.measureNode(node);
|
|
644
|
+
this.position = i;
|
|
645
|
+
this.offset -= height;
|
|
646
|
+
if (this.offset <= 0) break;
|
|
647
|
+
}
|
|
648
|
+
if (this.position === 0 && this.offset > 0) this.offset = 0;
|
|
649
|
+
}
|
|
650
|
+
let y = this.offset;
|
|
651
|
+
const drawList = [];
|
|
652
|
+
for (let i = this.position; i < this.items.length; i += 1) {
|
|
653
|
+
const item = this.items[i];
|
|
654
|
+
const node = this.options.renderItem(item);
|
|
655
|
+
const { height } = this.measureNode(node);
|
|
656
|
+
if (y + height > 0) {
|
|
657
|
+
drawList.push({
|
|
658
|
+
idx: i,
|
|
659
|
+
node,
|
|
660
|
+
offset: y,
|
|
661
|
+
height
|
|
662
|
+
});
|
|
663
|
+
drawLength += height;
|
|
664
|
+
} else {
|
|
665
|
+
this.offset += height;
|
|
666
|
+
this.position = i + 1;
|
|
667
|
+
}
|
|
668
|
+
y += height;
|
|
669
|
+
if (y >= viewportHeight) break;
|
|
670
|
+
}
|
|
671
|
+
let shift = 0;
|
|
672
|
+
if (y < viewportHeight) if (this.position === 0 && drawLength < viewportHeight) {
|
|
673
|
+
shift = -this.offset;
|
|
674
|
+
this.offset = 0;
|
|
675
|
+
} else {
|
|
676
|
+
shift = viewportHeight - y;
|
|
677
|
+
y = this.offset += shift;
|
|
678
|
+
let lastIdx = -1;
|
|
679
|
+
for (let i = this.position - 1; i >= 0; i -= 1) {
|
|
680
|
+
const item = this.items[lastIdx = i];
|
|
681
|
+
const node = this.options.renderItem(item);
|
|
682
|
+
const { height } = this.measureNode(node);
|
|
683
|
+
drawLength += height;
|
|
684
|
+
y -= height;
|
|
685
|
+
drawList.push({
|
|
686
|
+
idx: i,
|
|
687
|
+
node,
|
|
688
|
+
offset: y - shift,
|
|
689
|
+
height
|
|
690
|
+
});
|
|
691
|
+
if (y < 0) break;
|
|
692
|
+
}
|
|
693
|
+
if (lastIdx === 0 && drawLength < viewportHeight) {
|
|
694
|
+
shift = -drawList[drawList.length - 1].offset;
|
|
695
|
+
this.position = 0;
|
|
696
|
+
this.offset = 0;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
return this._renderDrawList(drawList, shift, feedback);
|
|
700
|
+
}
|
|
701
|
+
hittest(test) {
|
|
702
|
+
const viewportHeight = this.graphics.canvas.clientHeight;
|
|
703
|
+
let y = this.offset;
|
|
704
|
+
for (let i = this.position; i < this.items.length; i += 1) {
|
|
705
|
+
const item = this.items[i];
|
|
706
|
+
const node = this.options.renderItem(item);
|
|
707
|
+
const { height } = this.measureNode(node);
|
|
708
|
+
if (test.y < y + height) return node.hittest(this.context, shallowMerge(test, { y: test.y - y }));
|
|
709
|
+
y += height;
|
|
710
|
+
if (y >= viewportHeight) break;
|
|
711
|
+
}
|
|
712
|
+
return false;
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
var ChatRenderer = class extends VirtualizedRenderer {
|
|
716
|
+
render(feedback) {
|
|
717
|
+
const { clientWidth: viewportWidth, clientHeight: viewportHeight } = this.graphics.canvas;
|
|
718
|
+
this.graphics.clearRect(0, 0, viewportWidth, viewportHeight);
|
|
719
|
+
let drawLength = 0;
|
|
720
|
+
if (Number.isNaN(this.position)) this.position = this.items.length - 1;
|
|
721
|
+
if (this.offset < 0) if (this.position === this.items.length - 1) this.offset = 0;
|
|
722
|
+
else for (let i = this.position + 1; i < this.items.length; i += 1) {
|
|
723
|
+
const item = this.items[i];
|
|
724
|
+
const node = this.options.renderItem(item);
|
|
725
|
+
const { height } = this.measureNode(node);
|
|
726
|
+
this.position = i;
|
|
727
|
+
this.offset += height;
|
|
728
|
+
if (this.offset > 0) break;
|
|
729
|
+
}
|
|
730
|
+
let y = viewportHeight + this.offset;
|
|
731
|
+
const drawList = [];
|
|
732
|
+
for (let i = this.position; i >= 0; i -= 1) {
|
|
733
|
+
const item = this.items[i];
|
|
734
|
+
const node = this.options.renderItem(item);
|
|
735
|
+
const { height } = this.measureNode(node);
|
|
736
|
+
y -= height;
|
|
737
|
+
if (y <= viewportHeight) {
|
|
738
|
+
drawList.push({
|
|
739
|
+
idx: i,
|
|
740
|
+
node,
|
|
741
|
+
offset: y,
|
|
742
|
+
height
|
|
743
|
+
});
|
|
744
|
+
drawLength += height;
|
|
745
|
+
} else {
|
|
746
|
+
this.offset -= height;
|
|
747
|
+
this.position = i - 1;
|
|
748
|
+
}
|
|
749
|
+
if (y < 0) break;
|
|
750
|
+
}
|
|
751
|
+
let shift = 0;
|
|
752
|
+
if (y > 0) {
|
|
753
|
+
shift = -y;
|
|
754
|
+
if (drawLength < viewportHeight) {
|
|
755
|
+
y = drawLength;
|
|
756
|
+
for (let i = this.position + 1; i < this.items.length; i += 1) {
|
|
757
|
+
const item = this.items[i];
|
|
758
|
+
const node = this.options.renderItem(item);
|
|
759
|
+
const { height } = this.measureNode(node);
|
|
760
|
+
drawList.push({
|
|
761
|
+
idx: i,
|
|
762
|
+
node,
|
|
763
|
+
offset: y - shift,
|
|
764
|
+
height
|
|
765
|
+
});
|
|
766
|
+
y = drawLength += height;
|
|
767
|
+
this.position = i;
|
|
768
|
+
if (y >= viewportHeight) break;
|
|
769
|
+
}
|
|
770
|
+
if (drawLength < viewportHeight) this.offset = 0;
|
|
771
|
+
else this.offset = drawLength - viewportHeight;
|
|
772
|
+
} else this.offset = drawLength - viewportHeight;
|
|
773
|
+
}
|
|
774
|
+
return this._renderDrawList(drawList, shift, feedback);
|
|
775
|
+
}
|
|
776
|
+
hittest(test) {
|
|
777
|
+
const viewportHeight = this.graphics.canvas.clientHeight;
|
|
778
|
+
let drawLength = 0;
|
|
779
|
+
const heights = [];
|
|
780
|
+
for (let i = this.position; i >= 0; i -= 1) {
|
|
781
|
+
const item = this.items[i];
|
|
782
|
+
const node = this.options.renderItem(item);
|
|
783
|
+
const { height } = this.measureNode(node);
|
|
784
|
+
drawLength += height;
|
|
785
|
+
heights.push([node, height]);
|
|
786
|
+
}
|
|
787
|
+
let y = drawLength < viewportHeight ? drawLength : viewportHeight + this.offset;
|
|
788
|
+
if (test.y > y) return false;
|
|
789
|
+
for (const [node, height] of heights) {
|
|
790
|
+
y -= height;
|
|
791
|
+
if (test.y > y) return node.hittest(this.context, shallowMerge(test, { y: test.y - y }));
|
|
792
|
+
}
|
|
793
|
+
return false;
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
//#endregion
|
|
797
|
+
export { AlignBox, BaseRenderer, ChatRenderer, DebugRenderer, Fixed, Group, HStack, ListState, MultilineText, PaddingBox, Text, TimelineRenderer, VStack, VirtualizedRenderer, Wrapper, getNodeParent, memoRenderItem, registerNodeParent, unregisterNodeParent };
|
|
798
|
+
|
|
799
|
+
//# sourceMappingURL=index.mjs.map
|