@oh-just-another/renderer-core 0.57.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/CHANGELOG.md +15 -0
- package/LICENSE +21 -0
- package/README.md +40 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/animation-adapter.d.ts +91 -0
- package/dist/animation-adapter.d.ts.map +1 -0
- package/dist/animation-adapter.js +90 -0
- package/dist/animation-adapter.js.map +1 -0
- package/dist/built-in-renderers.d.ts +6 -0
- package/dist/built-in-renderers.d.ts.map +1 -0
- package/dist/built-in-renderers.js +517 -0
- package/dist/built-in-renderers.js.map +1 -0
- package/dist/constants.d.ts +97 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +115 -0
- package/dist/constants.js.map +1 -0
- package/dist/edge-cache-bitmap.d.ts +34 -0
- package/dist/edge-cache-bitmap.d.ts.map +1 -0
- package/dist/edge-cache-bitmap.js +42 -0
- package/dist/edge-cache-bitmap.js.map +1 -0
- package/dist/edge-cache.d.ts +32 -0
- package/dist/edge-cache.d.ts.map +1 -0
- package/dist/edge-cache.js +90 -0
- package/dist/edge-cache.js.map +1 -0
- package/dist/edge-renderer.d.ts +73 -0
- package/dist/edge-renderer.d.ts.map +1 -0
- package/dist/edge-renderer.js +554 -0
- package/dist/edge-renderer.js.map +1 -0
- package/dist/grid-renderer.d.ts +55 -0
- package/dist/grid-renderer.d.ts.map +1 -0
- package/dist/grid-renderer.js +173 -0
- package/dist/grid-renderer.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/js-rasterizer.d.ts +8 -0
- package/dist/js-rasterizer.d.ts.map +1 -0
- package/dist/js-rasterizer.js +86 -0
- package/dist/js-rasterizer.js.map +1 -0
- package/dist/layer-cache-composite.d.ts +39 -0
- package/dist/layer-cache-composite.d.ts.map +1 -0
- package/dist/layer-cache-composite.js +47 -0
- package/dist/layer-cache-composite.js.map +1 -0
- package/dist/layers.d.ts +14 -0
- package/dist/layers.d.ts.map +1 -0
- package/dist/layers.js +2 -0
- package/dist/layers.js.map +1 -0
- package/dist/rasterizer.d.ts +27 -0
- package/dist/rasterizer.d.ts.map +1 -0
- package/dist/rasterizer.js +10 -0
- package/dist/rasterizer.js.map +1 -0
- package/dist/render-target.d.ts +94 -0
- package/dist/render-target.d.ts.map +1 -0
- package/dist/render-target.js +2 -0
- package/dist/render-target.js.map +1 -0
- package/dist/scene-renderer.d.ts +126 -0
- package/dist/scene-renderer.d.ts.map +1 -0
- package/dist/scene-renderer.js +149 -0
- package/dist/scene-renderer.js.map +1 -0
- package/dist/shape-cache-bitmap.d.ts +42 -0
- package/dist/shape-cache-bitmap.d.ts.map +1 -0
- package/dist/shape-cache-bitmap.js +61 -0
- package/dist/shape-cache-bitmap.js.map +1 -0
- package/dist/shape-cache.d.ts +40 -0
- package/dist/shape-cache.d.ts.map +1 -0
- package/dist/shape-cache.js +67 -0
- package/dist/shape-cache.js.map +1 -0
- package/dist/shape-renderer.d.ts +36 -0
- package/dist/shape-renderer.d.ts.map +1 -0
- package/dist/shape-renderer.js +15 -0
- package/dist/shape-renderer.js.map +1 -0
- package/dist/text-editing.d.ts +81 -0
- package/dist/text-editing.d.ts.map +1 -0
- package/dist/text-editing.js +191 -0
- package/dist/text-editing.js.map +1 -0
- package/dist/text-layout.d.ts +44 -0
- package/dist/text-layout.d.ts.map +1 -0
- package/dist/text-layout.js +57 -0
- package/dist/text-layout.js.map +1 -0
- package/dist/text-shaper.d.ts +53 -0
- package/dist/text-shaper.d.ts.map +1 -0
- package/dist/text-shaper.js +19 -0
- package/dist/text-shaper.js.map +1 -0
- package/dist/tile-renderer.d.ts +126 -0
- package/dist/tile-renderer.d.ts.map +1 -0
- package/dist/tile-renderer.js +192 -0
- package/dist/tile-renderer.js.map +1 -0
- package/dist/worker-render.d.ts +54 -0
- package/dist/worker-render.d.ts.map +1 -0
- package/dist/worker-render.js +8 -0
- package/dist/worker-render.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { TextAlign } from "./render-target.js";
|
|
2
|
+
/**
|
|
3
|
+
* Caret-aware text layout. Unlike {@link wrapText} (which collapses
|
|
4
|
+
* whitespace and is only good enough for *drawing*), this keeps every
|
|
5
|
+
* line as an exact substring of the source plus its `[start, end)`
|
|
6
|
+
* character offsets — so a caret index maps unambiguously to a line +
|
|
7
|
+
* column. Measurement is injected as a `measure(s) => width` callback
|
|
8
|
+
* so the same geometry can be computed against either backend's font
|
|
9
|
+
* metrics (Canvas2D `measureText` or WebGL2 MSDF advances).
|
|
10
|
+
*
|
|
11
|
+
* Convention for `\n`: a hard newline at source index `k` ends the
|
|
12
|
+
* current line at `end === k` (caret at `k` = end of line) and the next
|
|
13
|
+
* line starts at `start === k + 1` (caret at `k + 1` = start of next
|
|
14
|
+
* line). The `\n` itself never holds a caret.
|
|
15
|
+
*/
|
|
16
|
+
export interface LaidOutLine {
|
|
17
|
+
/** Exact source substring for this visual line (no whitespace collapsing). */
|
|
18
|
+
readonly text: string;
|
|
19
|
+
/** Source offset where the line begins (inclusive). */
|
|
20
|
+
readonly start: number;
|
|
21
|
+
/** Source offset where the line ends (exclusive; excludes a trailing `\n`). */
|
|
22
|
+
readonly end: number;
|
|
23
|
+
/** Measured width of `text` in CSS px. */
|
|
24
|
+
readonly width: number;
|
|
25
|
+
}
|
|
26
|
+
export interface EditableTextLayout {
|
|
27
|
+
readonly lines: readonly LaidOutLine[];
|
|
28
|
+
readonly lineHeight: number;
|
|
29
|
+
/** Width the lines are aligned within (maxWidth, or the widest line). */
|
|
30
|
+
readonly blockWidth: number;
|
|
31
|
+
}
|
|
32
|
+
export type MeasureText = (text: string) => number;
|
|
33
|
+
export interface LayoutTextOptions {
|
|
34
|
+
readonly fontSize: number;
|
|
35
|
+
/** Wrap budget in CSS px. `undefined` → no wrap (split on `\n` only). */
|
|
36
|
+
readonly maxWidth?: number;
|
|
37
|
+
/** Line-height multiplier. Default 1.2 (matches the text renderer). */
|
|
38
|
+
readonly lineHeightFactor?: number;
|
|
39
|
+
}
|
|
40
|
+
/** Default multiplier from font size to line height (matches `drawText`). */
|
|
41
|
+
export declare const DEFAULT_LINE_HEIGHT_FACTOR = 1.2;
|
|
42
|
+
/**
|
|
43
|
+
* Lay out `text` into visual lines with exact source offsets. Always
|
|
44
|
+
* returns at least one (possibly empty) line.
|
|
45
|
+
*/
|
|
46
|
+
export declare const layoutText: (text: string, measure: MeasureText, options: LayoutTextOptions) => EditableTextLayout;
|
|
47
|
+
export interface CaretGeometry {
|
|
48
|
+
/** Local x of the caret bar. */
|
|
49
|
+
readonly x: number;
|
|
50
|
+
/** Local y of the caret top (baseline-top line origin). */
|
|
51
|
+
readonly y: number;
|
|
52
|
+
/** Caret height (≈ font size). */
|
|
53
|
+
readonly height: number;
|
|
54
|
+
/** Index of the line the caret sits on. */
|
|
55
|
+
readonly line: number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Local-space geometry of the caret for a given source `caret` offset.
|
|
59
|
+
* `align` must match the renderer's `textAlign`.
|
|
60
|
+
*/
|
|
61
|
+
export declare const caretGeometry: (layout: EditableTextLayout, caret: number, measure: MeasureText, fontSize: number, align: TextAlign) => CaretGeometry;
|
|
62
|
+
/**
|
|
63
|
+
* Map a local-space point to the nearest source caret offset. Used for
|
|
64
|
+
* click-to-place-caret and drag-to-select.
|
|
65
|
+
*/
|
|
66
|
+
export declare const pointToCaretIndex: (layout: EditableTextLayout, point: {
|
|
67
|
+
readonly x: number;
|
|
68
|
+
readonly y: number;
|
|
69
|
+
}, measure: MeasureText, align: TextAlign) => number;
|
|
70
|
+
export interface SelectionRect {
|
|
71
|
+
readonly x: number;
|
|
72
|
+
readonly y: number;
|
|
73
|
+
readonly width: number;
|
|
74
|
+
readonly height: number;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Local-space highlight rectangles covering the source range `[from, to)`
|
|
78
|
+
* (order-independent), one per visual line it spans.
|
|
79
|
+
*/
|
|
80
|
+
export declare const selectionRects: (layout: EditableTextLayout, from: number, to: number, measure: MeasureText, align: TextAlign) => readonly SelectionRect[];
|
|
81
|
+
//# sourceMappingURL=text-editing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-editing.d.ts","sourceRoot":"","sources":["../src/text-editing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAOpD;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,WAAW;IAC1B,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uDAAuD;IACvD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IACvC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,yEAAyE;IACzE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;AAEnD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,uEAAuE;IACvE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,6EAA6E;AAC7E,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAsE9C;;;GAGG;AACH,eAAO,MAAM,UAAU,GACrB,MAAM,MAAM,EACZ,SAAS,WAAW,EACpB,SAAS,iBAAiB,KACzB,kBAoBF,CAAC;AAuBF,MAAM,WAAW,aAAa;IAC5B,gCAAgC;IAChC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,QAAQ,kBAAkB,EAC1B,OAAO,MAAM,EACb,SAAS,WAAW,EACpB,UAAU,MAAM,EAChB,OAAO,SAAS,KACf,aAOF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,kBAAkB,EAC1B,OAAO;IAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EACjD,SAAS,WAAW,EACpB,OAAO,SAAS,KACf,MAiBF,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,GACzB,QAAQ,kBAAkB,EAC1B,MAAM,MAAM,EACZ,IAAI,MAAM,EACV,SAAS,WAAW,EACpB,OAAO,SAAS,KACf,SAAS,aAAa,EA6BxB,CAAC"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
const req = (v) => {
|
|
2
|
+
if (v === undefined)
|
|
3
|
+
throw new Error("packages/renderer-core: index out of range");
|
|
4
|
+
return v;
|
|
5
|
+
};
|
|
6
|
+
/** Default multiplier from font size to line height (matches `drawText`). */
|
|
7
|
+
export const DEFAULT_LINE_HEIGHT_FACTOR = 1.2;
|
|
8
|
+
const wrapParagraph = (para, base, maxWidth, measure, out) => {
|
|
9
|
+
if (para === "") {
|
|
10
|
+
out.push({ text: "", start: base, end: base, width: 0 });
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
// Word spans (non-whitespace runs) with offsets relative to `para`.
|
|
14
|
+
const words = [];
|
|
15
|
+
const re = /\S+/g;
|
|
16
|
+
let m;
|
|
17
|
+
while ((m = re.exec(para)) !== null)
|
|
18
|
+
words.push({ s: m.index, e: m.index + m[0].length });
|
|
19
|
+
if (words.length === 0) {
|
|
20
|
+
// Whitespace-only paragraph — keep it as one line so offsets survive.
|
|
21
|
+
out.push({ text: para, start: base, end: base + para.length, width: measure(para) });
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
// `white-space: pre-wrap` + `overflow-wrap: break-word` (standard):
|
|
25
|
+
// preserve whitespace, wrap at word boundaries, and break a word that
|
|
26
|
+
// is itself wider than the line so narrowing the block always reflows
|
|
27
|
+
// (a single long word can't overflow forever). Lines are gapless
|
|
28
|
+
// slices of the source — inter-word whitespace at a soft break stays
|
|
29
|
+
// on the preceding line — so every character maps to exactly one line
|
|
30
|
+
// (clean caret offsets). First line starts at 0 (keeps leading
|
|
31
|
+
// whitespace); the last extends to the paragraph end (trailing ws).
|
|
32
|
+
let lineStart = 0;
|
|
33
|
+
const push = (start, end) => {
|
|
34
|
+
const text = para.slice(start, end);
|
|
35
|
+
out.push({ text, start: base + start, end: base + end, width: measure(text) });
|
|
36
|
+
};
|
|
37
|
+
let i = 0;
|
|
38
|
+
while (i < words.length) {
|
|
39
|
+
const w = req(words[i]);
|
|
40
|
+
if (measure(para.slice(lineStart, w.e)) <= maxWidth) {
|
|
41
|
+
i++; // word fits on the current line — keep it, try the next
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (w.s > lineStart) {
|
|
45
|
+
// Content precedes this word on the line → break before it. The
|
|
46
|
+
// whitespace up to its start stays on the current line (pre-wrap).
|
|
47
|
+
push(lineStart, w.s);
|
|
48
|
+
lineStart = w.s;
|
|
49
|
+
continue; // retry this word on the fresh line
|
|
50
|
+
}
|
|
51
|
+
// The word starts the line and alone overflows → break it by chars,
|
|
52
|
+
// keeping at least one char per line so we always make progress.
|
|
53
|
+
let e = w.s + 1;
|
|
54
|
+
while (e < w.e && measure(para.slice(lineStart, e + 1)) <= maxWidth)
|
|
55
|
+
e++;
|
|
56
|
+
if (e >= w.e) {
|
|
57
|
+
// Whole (remaining) word consumed — leave it on the current line
|
|
58
|
+
// and advance. Guards termination when the measurer is degenerate
|
|
59
|
+
// (e.g. a constant stub that never reports "fits").
|
|
60
|
+
i++;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
push(lineStart, e);
|
|
64
|
+
lineStart = e;
|
|
65
|
+
w.s = e; // remainder of the word continues on the next line
|
|
66
|
+
}
|
|
67
|
+
// Last line keeps everything through the end of the paragraph.
|
|
68
|
+
push(lineStart, para.length);
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Lay out `text` into visual lines with exact source offsets. Always
|
|
72
|
+
* returns at least one (possibly empty) line.
|
|
73
|
+
*/
|
|
74
|
+
export const layoutText = (text, measure, options) => {
|
|
75
|
+
const lineHeight = options.fontSize * (options.lineHeightFactor ?? DEFAULT_LINE_HEIGHT_FACTOR);
|
|
76
|
+
const lines = [];
|
|
77
|
+
let paraStart = 0;
|
|
78
|
+
for (let i = 0; i <= text.length; i++) {
|
|
79
|
+
if (i === text.length || text[i] === "\n") {
|
|
80
|
+
const para = text.slice(paraStart, i);
|
|
81
|
+
if (options.maxWidth === undefined) {
|
|
82
|
+
lines.push({ text: para, start: paraStart, end: i, width: measure(para) });
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
wrapParagraph(para, paraStart, options.maxWidth, measure, lines);
|
|
86
|
+
}
|
|
87
|
+
paraStart = i + 1;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (lines.length === 0)
|
|
91
|
+
lines.push({ text: "", start: 0, end: 0, width: 0 });
|
|
92
|
+
let widest = 0;
|
|
93
|
+
for (const l of lines)
|
|
94
|
+
widest = Math.max(widest, l.width);
|
|
95
|
+
const blockWidth = options.maxWidth ?? widest;
|
|
96
|
+
return { lines, lineHeight, blockWidth };
|
|
97
|
+
};
|
|
98
|
+
/** Left edge (local x) where a line's glyphs start, given the align. */
|
|
99
|
+
const lineLeftX = (lineWidth, blockWidth, align) => {
|
|
100
|
+
if (align === "center")
|
|
101
|
+
return blockWidth / 2 - lineWidth / 2;
|
|
102
|
+
if (align === "right")
|
|
103
|
+
return blockWidth - lineWidth;
|
|
104
|
+
return 0;
|
|
105
|
+
};
|
|
106
|
+
/** Index of the line a caret offset falls on (handles boundaries). */
|
|
107
|
+
const lineIndexForCaret = (layout, caret) => {
|
|
108
|
+
const { lines } = layout;
|
|
109
|
+
for (let i = 0; i < lines.length; i++) {
|
|
110
|
+
const l = req(lines[i]);
|
|
111
|
+
// Caret belongs to this line when it's within [start, end]; the
|
|
112
|
+
// upper bound is inclusive so end-of-line resolves here, while
|
|
113
|
+
// start-of-next-line (end + 1 for a hard `\n`) resolves to the
|
|
114
|
+
// next line on the following iteration.
|
|
115
|
+
if (caret <= l.end)
|
|
116
|
+
return i;
|
|
117
|
+
}
|
|
118
|
+
return lines.length - 1;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Local-space geometry of the caret for a given source `caret` offset.
|
|
122
|
+
* `align` must match the renderer's `textAlign`.
|
|
123
|
+
*/
|
|
124
|
+
export const caretGeometry = (layout, caret, measure, fontSize, align) => {
|
|
125
|
+
const i = lineIndexForCaret(layout, caret);
|
|
126
|
+
const line = req(layout.lines[i]);
|
|
127
|
+
const col = Math.max(0, Math.min(caret, line.end) - line.start);
|
|
128
|
+
const prefixWidth = col === 0 ? 0 : measure(line.text.slice(0, col));
|
|
129
|
+
const left = lineLeftX(line.width, layout.blockWidth, align);
|
|
130
|
+
return { x: left + prefixWidth, y: i * layout.lineHeight, height: fontSize, line: i };
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Map a local-space point to the nearest source caret offset. Used for
|
|
134
|
+
* click-to-place-caret and drag-to-select.
|
|
135
|
+
*/
|
|
136
|
+
export const pointToCaretIndex = (layout, point, measure, align) => {
|
|
137
|
+
const { lines, lineHeight } = layout;
|
|
138
|
+
const i = Math.max(0, Math.min(lines.length - 1, Math.floor(point.y / lineHeight)));
|
|
139
|
+
const line = req(lines[i]);
|
|
140
|
+
const left = lineLeftX(line.width, layout.blockWidth, align);
|
|
141
|
+
// Walk columns, picking the boundary whose x is closest to point.x.
|
|
142
|
+
let best = 0;
|
|
143
|
+
let bestDist = Math.abs(left - point.x);
|
|
144
|
+
for (let col = 1; col <= line.text.length; col++) {
|
|
145
|
+
const x = left + measure(line.text.slice(0, col));
|
|
146
|
+
const d = Math.abs(x - point.x);
|
|
147
|
+
if (d < bestDist) {
|
|
148
|
+
bestDist = d;
|
|
149
|
+
best = col;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return line.start + best;
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* Local-space highlight rectangles covering the source range `[from, to)`
|
|
156
|
+
* (order-independent), one per visual line it spans.
|
|
157
|
+
*/
|
|
158
|
+
export const selectionRects = (layout, from, to, measure, align) => {
|
|
159
|
+
const lo = Math.min(from, to);
|
|
160
|
+
const hi = Math.max(from, to);
|
|
161
|
+
if (lo === hi)
|
|
162
|
+
return [];
|
|
163
|
+
const rects = [];
|
|
164
|
+
for (let i = 0; i < layout.lines.length; i++) {
|
|
165
|
+
const line = req(layout.lines[i]);
|
|
166
|
+
const a = Math.max(lo, line.start);
|
|
167
|
+
const b = Math.min(hi, line.end);
|
|
168
|
+
if (a > b)
|
|
169
|
+
continue;
|
|
170
|
+
if (a === b && !(lo <= line.start && hi > line.end)) {
|
|
171
|
+
// Empty intersection on this line, unless the selection spans the
|
|
172
|
+
// hard break past it (then show a thin trailing marker).
|
|
173
|
+
if (!(hi > line.end && lo <= line.end))
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
const left = lineLeftX(line.width, layout.blockWidth, align);
|
|
177
|
+
const xa = left + (a === line.start ? 0 : measure(line.text.slice(0, a - line.start)));
|
|
178
|
+
const xb = left + (b === line.start ? 0 : measure(line.text.slice(0, b - line.start)));
|
|
179
|
+
// A line whose break is inside the selection gets a small trailing
|
|
180
|
+
// sliver so multi-line selections read continuously.
|
|
181
|
+
const trailing = hi > line.end ? layout.lineHeight * 0.25 : 0;
|
|
182
|
+
rects.push({
|
|
183
|
+
x: xa,
|
|
184
|
+
y: i * layout.lineHeight,
|
|
185
|
+
width: Math.max(0, xb - xa) + trailing,
|
|
186
|
+
height: layout.lineHeight,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
return rects;
|
|
190
|
+
};
|
|
191
|
+
//# sourceMappingURL=text-editing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-editing.js","sourceRoot":"","sources":["../src/text-editing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { RenderTarget } from "./render-target.js";
|
|
2
|
+
import type { TextShaper } from "./text-shaper.js";
|
|
3
|
+
export interface WrapOptions {
|
|
4
|
+
/** Max width in CSS pixels. */
|
|
5
|
+
readonly maxWidth: number;
|
|
6
|
+
/** Font size in CSS pixels — used to derive line height. */
|
|
7
|
+
readonly fontSize: number;
|
|
8
|
+
/** Multiplier applied to font size to derive line height. Default: 1.2. */
|
|
9
|
+
readonly lineHeightFactor?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Optional font family name — paired with `fontSize` to call
|
|
12
|
+
* `shaper.measure(text, { family, size })` when a `shaper` is
|
|
13
|
+
* supplied. Defaults to `"sans-serif"`. Has no effect when no
|
|
14
|
+
* shaper is in play (Canvas2D's `measureText` already knows the
|
|
15
|
+
* font via the host's prior `ctx.font = …` call).
|
|
16
|
+
*/
|
|
17
|
+
readonly fontFamily?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Optional `TextShaper` (WASM / harfbuzz / canvaskit) that
|
|
20
|
+
* replaces the default `target.measureText` path. When set,
|
|
21
|
+
* the wrap algorithm queries `shaper.measure(line, font)` per
|
|
22
|
+
* candidate line, so layouts stay deterministic across
|
|
23
|
+
* environments (server / headless / browser).
|
|
24
|
+
*/
|
|
25
|
+
readonly shaper?: TextShaper;
|
|
26
|
+
}
|
|
27
|
+
export interface WrappedLine {
|
|
28
|
+
readonly text: string;
|
|
29
|
+
readonly width: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Greedy word-wrap by `target.measureText`. Words longer than `maxWidth` are
|
|
33
|
+
* placed on their own line and overflow horizontally — they are not broken.
|
|
34
|
+
* Whitespace runs are collapsed; newlines (`\n`) in the input force a break.
|
|
35
|
+
*
|
|
36
|
+
* The caller is responsible for having set the font on the target before this
|
|
37
|
+
* call. Returns an array of lines plus the effective `lineHeight` so callers
|
|
38
|
+
* can compute total layout height.
|
|
39
|
+
*/
|
|
40
|
+
export declare const wrapText: (text: string, target: RenderTarget, options: WrapOptions) => {
|
|
41
|
+
readonly lines: readonly WrappedLine[];
|
|
42
|
+
readonly lineHeight: number;
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=text-layout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-layout.d.ts","sourceRoot":"","sources":["../src/text-layout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,WAAW,WAAW;IAC1B,+BAA+B;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,4DAA4D;IAC5D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,2EAA2E;IAC3E,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC;CAC9B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,QAAQ,GACnB,MAAM,MAAM,EACZ,QAAQ,YAAY,EACpB,SAAS,WAAW,KACnB;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAiDvE,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Greedy word-wrap by `target.measureText`. Words longer than `maxWidth` are
|
|
3
|
+
* placed on their own line and overflow horizontally — they are not broken.
|
|
4
|
+
* Whitespace runs are collapsed; newlines (`\n`) in the input force a break.
|
|
5
|
+
*
|
|
6
|
+
* The caller is responsible for having set the font on the target before this
|
|
7
|
+
* call. Returns an array of lines plus the effective `lineHeight` so callers
|
|
8
|
+
* can compute total layout height.
|
|
9
|
+
*/
|
|
10
|
+
export const wrapText = (text, target, options) => {
|
|
11
|
+
const { maxWidth } = options;
|
|
12
|
+
const lineHeightFactor = options.lineHeightFactor ?? 1.2;
|
|
13
|
+
const out = [];
|
|
14
|
+
const fontFamily = options.fontFamily ?? "sans-serif";
|
|
15
|
+
const shaper = options.shaper;
|
|
16
|
+
const measure = shaper
|
|
17
|
+
? (s) => shaper.measure(s, { family: fontFamily, size: options.fontSize }).width
|
|
18
|
+
: (s) => target.measureText(s).width;
|
|
19
|
+
for (const paragraph of text.split("\n")) {
|
|
20
|
+
if (paragraph === "") {
|
|
21
|
+
out.push({ text: "", width: 0 });
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const words = paragraph.split(/\s+/).filter((w) => w.length > 0);
|
|
25
|
+
if (words.length === 0) {
|
|
26
|
+
out.push({ text: "", width: 0 });
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const firstWord = words[0];
|
|
30
|
+
if (firstWord === undefined) {
|
|
31
|
+
out.push({ text: "", width: 0 });
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
let current = firstWord;
|
|
35
|
+
let currentWidth = measure(current);
|
|
36
|
+
for (let i = 1; i < words.length; i++) {
|
|
37
|
+
const word = words[i];
|
|
38
|
+
if (word === undefined)
|
|
39
|
+
continue;
|
|
40
|
+
const candidate = `${current} ${word}`;
|
|
41
|
+
const w = measure(candidate);
|
|
42
|
+
if (w <= maxWidth) {
|
|
43
|
+
current = candidate;
|
|
44
|
+
currentWidth = w;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
out.push({ text: current, width: currentWidth });
|
|
48
|
+
current = word;
|
|
49
|
+
currentWidth = measure(word);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
out.push({ text: current, width: currentWidth });
|
|
53
|
+
}
|
|
54
|
+
const lineHeight = options.fontSize * lineHeightFactor;
|
|
55
|
+
return { lines: out, lineHeight };
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=text-layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-layout.js","sourceRoot":"","sources":["../src/text-layout.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { TextAlign, TextBaseline } from "./render-target.js";
|
|
2
|
+
/**
|
|
3
|
+
* Text-shaping abstraction. Lets a host swap the default
|
|
4
|
+
* `canvas.measureText`-based path for a richer engine (HarfBuzz /
|
|
5
|
+
* ICU4X / harfbuzzjs / canvaskit) when consistent server-side ↔
|
|
6
|
+
* browser layout matters or when batching many measurements is the
|
|
7
|
+
* hot path.
|
|
8
|
+
*
|
|
9
|
+
* The kernel ships a `Canvas2DTextShaper` implementation that delegates to
|
|
10
|
+
* `ctx.measureText`. Hosts that need deterministic cross-environment layout
|
|
11
|
+
* can implement this interface and pass it via `EditorOptions.textShaper`.
|
|
12
|
+
*/
|
|
13
|
+
export interface TextShaper {
|
|
14
|
+
/**
|
|
15
|
+
* Measure a single line in the given font. Returns the advance
|
|
16
|
+
* width in CSS pixels. Hosts may also expose glyph runs for
|
|
17
|
+
* decoration positioning — the kernel only requires width today.
|
|
18
|
+
*/
|
|
19
|
+
measure(text: string, font: ShaperFont): {
|
|
20
|
+
width: number;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Returns the glyph layout for richer renderers that want to draw the
|
|
24
|
+
* actual glyphs themselves. Not invoked by the built-in Canvas2D renderer.
|
|
25
|
+
*/
|
|
26
|
+
shape?(text: string, font: ShaperFont): readonly ShapedGlyph[];
|
|
27
|
+
}
|
|
28
|
+
export interface ShaperFont {
|
|
29
|
+
readonly family: string;
|
|
30
|
+
readonly size: number;
|
|
31
|
+
readonly weight?: "normal" | "bold" | number;
|
|
32
|
+
readonly style?: "normal" | "italic";
|
|
33
|
+
}
|
|
34
|
+
export interface ShapedGlyph {
|
|
35
|
+
/** Glyph id in the resolved font (renderer-specific). */
|
|
36
|
+
readonly glyphId: number;
|
|
37
|
+
/** Advance width in CSS pixels. */
|
|
38
|
+
readonly advance: number;
|
|
39
|
+
/** X offset from the line origin. */
|
|
40
|
+
readonly x: number;
|
|
41
|
+
/** Y offset from the line baseline. */
|
|
42
|
+
readonly y: number;
|
|
43
|
+
}
|
|
44
|
+
export type { TextAlign, TextBaseline };
|
|
45
|
+
/**
|
|
46
|
+
* Install a process-global text shaper. Subsequent `getActiveTextShaper()`
|
|
47
|
+
* calls (used by the built-in `drawText` renderer) return it; passing `null`
|
|
48
|
+
* reverts to the Canvas2D `target.measureText` path. Idempotent — last write
|
|
49
|
+
* wins.
|
|
50
|
+
*/
|
|
51
|
+
export declare const setActiveTextShaper: (shaper: TextShaper | null) => void;
|
|
52
|
+
export declare const getActiveTextShaper: () => TextShaper | null;
|
|
53
|
+
//# sourceMappingURL=text-shaper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-shaper.d.ts","sourceRoot":"","sources":["../src/text-shaper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElE;;;;;;;;;;GAUG;AACH,MAAM,WAAW,UAAU;IACzB;;;;OAIG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAE3D;;;OAGG;IACH,KAAK,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,SAAS,WAAW,EAAE,CAAC;CAChE;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;IAC7C,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;CACtC;AAED,MAAM,WAAW,WAAW;IAC1B,yDAAyD;IACzD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,mCAAmC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,qCAAqC;IACrC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;CACpB;AAGD,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;AAYxC;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAAI,QAAQ,UAAU,GAAG,IAAI,KAAG,IAE/D,CAAC;AAEF,eAAO,MAAM,mBAAmB,QAAO,UAAU,GAAG,IAAoB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Process-global active shaper. `wrapText` accepts an optional `shaper`
|
|
2
|
+
// parameter, but the built-in `drawText` renderer in `built-in-renderers.ts`
|
|
3
|
+
// is an `ElementRenderer<TextElement>` whose signature is `(shape, target)`
|
|
4
|
+
// with no extra arg. Instead of threading a shaper through every
|
|
5
|
+
// ElementRenderer, a module-level registry that `drawText` consults at call
|
|
6
|
+
// time. Editor sets it via `setActiveTextShaper(options.textShaper)` on
|
|
7
|
+
// construction.
|
|
8
|
+
let activeShaper = null;
|
|
9
|
+
/**
|
|
10
|
+
* Install a process-global text shaper. Subsequent `getActiveTextShaper()`
|
|
11
|
+
* calls (used by the built-in `drawText` renderer) return it; passing `null`
|
|
12
|
+
* reverts to the Canvas2D `target.measureText` path. Idempotent — last write
|
|
13
|
+
* wins.
|
|
14
|
+
*/
|
|
15
|
+
export const setActiveTextShaper = (shaper) => {
|
|
16
|
+
activeShaper = shaper;
|
|
17
|
+
};
|
|
18
|
+
export const getActiveTextShaper = () => activeShaper;
|
|
19
|
+
//# sourceMappingURL=text-shaper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-shaper.js","sourceRoot":"","sources":["../src/text-shaper.ts"],"names":[],"mappings":"AAiDA,wEAAwE;AACxE,6EAA6E;AAC7E,4EAA4E;AAC5E,iEAAiE;AACjE,4EAA4E;AAC5E,wEAAwE;AACxE,gBAAgB;AAEhB,IAAI,YAAY,GAAsB,IAAI,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAAyB,EAAQ,EAAE;IACrE,YAAY,GAAG,MAAM,CAAC;AACxB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAsB,EAAE,CAAC,YAAY,CAAC"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { Bounds, ElementId } from "@oh-just-another/types";
|
|
2
|
+
/**
|
|
3
|
+
* Tile-based rendering scaffold. The scene is split into a fixed-size
|
|
4
|
+
* world-space grid; each tile is rasterised once into an
|
|
5
|
+
* OffscreenCanvas / ImageBitmap and re-composited on pan without
|
|
6
|
+
* re-rendering. Zoom changes invalidate the bucket.
|
|
7
|
+
*
|
|
8
|
+
* This file ships the constants and cache interface so backends can
|
|
9
|
+
* adopt the API.
|
|
10
|
+
*/
|
|
11
|
+
/** Side length of one tile in world units. */
|
|
12
|
+
export declare const TILE_SIZE = 2048;
|
|
13
|
+
/**
|
|
14
|
+
* Memory cap for cached tile bitmaps (bytes), adaptive per device.
|
|
15
|
+
*
|
|
16
|
+
* Formula: `min(256 MB, deviceMemoryGB × 64 MB)`.
|
|
17
|
+
* • iPhone Safari (no deviceMemory; A15 = 6 cores) → 2 GiB → 128 MB
|
|
18
|
+
* • Android low-RAM (deviceMemory=2) → 128 MB
|
|
19
|
+
* • Android mid-range (deviceMemory=4) → 256 MB
|
|
20
|
+
* • Desktop (deviceMemory=8 / hardwareConcurrency=8+) → 256 MB (cap)
|
|
21
|
+
* • Very low-end (deviceMemory=0.5) → 32 MB
|
|
22
|
+
*
|
|
23
|
+
* The adaptive cap caps the upper bound proportionally to device
|
|
24
|
+
* memory, leaving headroom for the rest of the JS heap.
|
|
25
|
+
*
|
|
26
|
+
* Evaluated once at module load — navigator state doesn't change during
|
|
27
|
+
* a session. Hosts that need a different cap can pass
|
|
28
|
+
* `new InMemoryTileCache(<custom bytes>)` directly.
|
|
29
|
+
*/
|
|
30
|
+
export declare const MAX_TILE_CACHE_BYTES: number;
|
|
31
|
+
/**
|
|
32
|
+
* Below this zoom we drop to LOD: cache tiles render only AABBs
|
|
33
|
+
* (no stroke / text / detail) so 1 M+ shape scenes stay smooth.
|
|
34
|
+
*/
|
|
35
|
+
export declare const LOD_THRESHOLD = 0.25;
|
|
36
|
+
export interface TileKey {
|
|
37
|
+
/** Tile column in world-space grid. */
|
|
38
|
+
readonly col: number;
|
|
39
|
+
/** Tile row in world-space grid. */
|
|
40
|
+
readonly row: number;
|
|
41
|
+
/** Zoom bucket (power of two). */
|
|
42
|
+
readonly zoom: number;
|
|
43
|
+
}
|
|
44
|
+
export interface TileCacheEntry<B = unknown> {
|
|
45
|
+
readonly key: TileKey;
|
|
46
|
+
readonly bitmap: B;
|
|
47
|
+
/** World bounds covered by this tile. */
|
|
48
|
+
readonly bounds: Bounds;
|
|
49
|
+
/** Bytes used by this bitmap (for LRU eviction accounting). */
|
|
50
|
+
readonly bytes: number;
|
|
51
|
+
/** Element ids currently visible in this tile (for invalidation). */
|
|
52
|
+
readonly elements: readonly ElementId[];
|
|
53
|
+
}
|
|
54
|
+
export interface TileCache<B = unknown> {
|
|
55
|
+
get(key: TileKey): TileCacheEntry<B> | undefined;
|
|
56
|
+
set(entry: TileCacheEntry<B>): void;
|
|
57
|
+
/**
|
|
58
|
+
* Drop any cached tile that contains the given shape id. Called
|
|
59
|
+
* by the editor when a shape's scene reference changes.
|
|
60
|
+
*/
|
|
61
|
+
invalidateForElement(id: ElementId): void;
|
|
62
|
+
/**
|
|
63
|
+
* Drop any cached tile whose `bounds` intersect the given world
|
|
64
|
+
* rectangle. Used by the editor's patch hook when a shape is
|
|
65
|
+
* added (no previous id in the reverse index) or moved (the
|
|
66
|
+
* before/after AABB union covers both old and new tiles).
|
|
67
|
+
*/
|
|
68
|
+
invalidateRect(rect: Bounds): void;
|
|
69
|
+
/**
|
|
70
|
+
* Convenience routing used by editor patch pipelines: dispatches
|
|
71
|
+
* to `invalidateForElement` (remove), `invalidateRect` (add), or
|
|
72
|
+
* both (move) based on which of `removedElementId` / `beforeBounds`
|
|
73
|
+
* / `afterBounds` are present.
|
|
74
|
+
*/
|
|
75
|
+
invalidateForPatch(options: {
|
|
76
|
+
readonly removedElementId?: ElementId;
|
|
77
|
+
readonly beforeBounds?: Bounds;
|
|
78
|
+
readonly afterBounds?: Bounds;
|
|
79
|
+
}): void;
|
|
80
|
+
/** Total bytes currently held. */
|
|
81
|
+
readonly bytesUsed: number;
|
|
82
|
+
clear(): void;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Reference TileCache backed by a Map with LRU eviction by total
|
|
86
|
+
* bytes. Bitmap-agnostic — the eviction logic tracks the `bytes`
|
|
87
|
+
* field reported by the host; whatever stores ImageBitmap /
|
|
88
|
+
* OffscreenCanvas / SharedArrayBuffer is the host's call.
|
|
89
|
+
*
|
|
90
|
+
* Invalidation is shape-id-indexed: every `set` also updates a
|
|
91
|
+
* reverse index `Map<ElementId, Set<tileKey>>` so the editor's
|
|
92
|
+
* patch hook can call `invalidateForElement(id)` in O(touched tiles)
|
|
93
|
+
* instead of scanning every tile.
|
|
94
|
+
*/
|
|
95
|
+
export declare class InMemoryTileCache<B = unknown> implements TileCache<B> {
|
|
96
|
+
private readonly entries;
|
|
97
|
+
private readonly tilesByElement;
|
|
98
|
+
private bytes;
|
|
99
|
+
private readonly cap;
|
|
100
|
+
constructor(byteCap?: number);
|
|
101
|
+
get bytesUsed(): number;
|
|
102
|
+
get(key: TileKey): TileCacheEntry<B> | undefined;
|
|
103
|
+
set(entry: TileCacheEntry<B>): void;
|
|
104
|
+
invalidateForElement(elementId: ElementId): void;
|
|
105
|
+
invalidateRect(rect: Bounds): void;
|
|
106
|
+
clear(): void;
|
|
107
|
+
/**
|
|
108
|
+
* Convenience hook for the editor's patch pipeline. Looks at a
|
|
109
|
+
* scene Patch and invalidates touched tiles via the right
|
|
110
|
+
* cheap path:
|
|
111
|
+
* • shape removed → invalidateForElement (id still in reverse index)
|
|
112
|
+
* • shape added → invalidateRect (no id yet — drop tiles that
|
|
113
|
+
* touch the new shape's bounds)
|
|
114
|
+
* • shape moved → both: removeForElement (old tiles) + rect
|
|
115
|
+
* (new bounds)
|
|
116
|
+
* Caller computes the bounds via `getElementWorldBounds(before|after)`
|
|
117
|
+
* since this package is pure and doesn't know about Element geometry.
|
|
118
|
+
*/
|
|
119
|
+
invalidateForPatch(options: {
|
|
120
|
+
readonly removedElementId?: ElementId;
|
|
121
|
+
readonly beforeBounds?: Bounds;
|
|
122
|
+
readonly afterBounds?: Bounds;
|
|
123
|
+
}): void;
|
|
124
|
+
private evictIfOverCap;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=tile-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tile-renderer.d.ts","sourceRoot":"","sources":["../src/tile-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEhE;;;;;;;;GAQG;AAEH,8CAA8C;AAC9C,eAAO,MAAM,SAAS,OAAO,CAAC;AAkC9B;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,oBAAoB,QAGhC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,OAAO,CAAC;AAElC,MAAM,WAAW,OAAO;IACtB,uCAAuC;IACvC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,OAAO;IACzC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACnB,yCAAyC;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,EAAE,SAAS,SAAS,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,SAAS,CAAC,CAAC,GAAG,OAAO;IACpC,GAAG,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IACjD,GAAG,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IACpC;;;OAGG;IACH,oBAAoB,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI,CAAC;IAC1C;;;;;OAKG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC;;;;;OAKG;IACH,kBAAkB,CAAC,OAAO,EAAE;QAC1B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,CAAC;QACtC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;KAC/B,GAAG,IAAI,CAAC;IACT,kCAAkC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,KAAK,IAAI,IAAI,CAAC;CACf;AAID;;;;;;;;;;GAUG;AACH,qBAAa,iBAAiB,CAAC,CAAC,GAAG,OAAO,CAAE,YAAW,SAAS,CAAC,CAAC,CAAC;IACjE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwC;IAChE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqC;IACpE,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;gBAEjB,OAAO,GAAE,MAA6B;IAIlD,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,GAAG,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,CAAC,CAAC,CAAC,GAAG,SAAS;IAUhD,GAAG,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,IAAI;IAoBnC,oBAAoB,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAehD,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAelC,KAAK,IAAI,IAAI;IAMb;;;;;;;;;;;OAWG;IACH,kBAAkB,CAChB,OAAO,EAAE;QACP,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,CAAC;QACtC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;KAC/B,GACA,IAAI;IAMP,OAAO,CAAC,cAAc;CAUvB"}
|