@termaxjs/editor-canvas 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Measure width and height for a monospace font in the browser.
3
+ */
4
+ export function measureMonospaceFont(fontFamily, fontSize, lineHeightRatio = 1.5) {
5
+ if (typeof document === "undefined") {
6
+ // SSR / Node fallback
7
+ const charWidth = Math.round(fontSize * 0.6);
8
+ const lineHeight = Math.round(fontSize * lineHeightRatio);
9
+ return { charWidth, lineHeight };
10
+ }
11
+ const canvas = document.createElement("canvas");
12
+ const ctx = canvas.getContext("2d");
13
+ if (!ctx) {
14
+ const charWidth = Math.round(fontSize * 0.6);
15
+ const lineHeight = Math.round(fontSize * lineHeightRatio);
16
+ return { charWidth, lineHeight };
17
+ }
18
+ const font = `${fontSize}px ${fontFamily}`;
19
+ ctx.font = font;
20
+ const metrics = ctx.measureText("M");
21
+ const charWidth = Math.max(1, metrics.width);
22
+ const lineHeight = Math.round(fontSize * lineHeightRatio);
23
+ return { charWidth, lineHeight };
24
+ }
25
+ /**
26
+ * Bitmap glyph atlas to accelerate monospace rendering on Canvas.
27
+ */
28
+ export class GlyphAtlas {
29
+ canvas = null;
30
+ ctx = null;
31
+ glyphMap = new Map();
32
+ font = "";
33
+ charWidth = 0;
34
+ lineHeight = 0;
35
+ dpr = 1;
36
+ curX = 0;
37
+ curY = 0;
38
+ cellW = 0;
39
+ cellH = 0;
40
+ constructor(fontFamily, fontSize, dpr = 1, charWidth, lineHeight) {
41
+ this.dpr = dpr;
42
+ this.font = `${fontSize}px ${fontFamily}`;
43
+ const m = measureMonospaceFont(fontFamily, fontSize);
44
+ this.charWidth = charWidth && charWidth > 0 ? charWidth : m.charWidth;
45
+ this.lineHeight = lineHeight && lineHeight > 0 ? lineHeight : m.lineHeight;
46
+ this.cellW = Math.ceil(this.charWidth * this.dpr);
47
+ this.cellH = Math.ceil(this.lineHeight * this.dpr);
48
+ this.initAtlas();
49
+ }
50
+ initAtlas() {
51
+ if (typeof document === "undefined" && typeof OffscreenCanvas === "undefined") {
52
+ return;
53
+ }
54
+ const atlasWidth = 1024 * this.dpr;
55
+ const atlasHeight = 512 * this.dpr;
56
+ if (typeof OffscreenCanvas !== "undefined") {
57
+ this.canvas = new OffscreenCanvas(atlasWidth, atlasHeight);
58
+ this.ctx = this.canvas.getContext("2d");
59
+ }
60
+ else if (typeof document !== "undefined") {
61
+ const c = document.createElement("canvas");
62
+ c.width = atlasWidth;
63
+ c.height = atlasHeight;
64
+ this.canvas = c;
65
+ this.ctx = c.getContext("2d");
66
+ }
67
+ if (this.ctx) {
68
+ this.ctx.font = `${Math.round(this.cellH * 0.7)}px monospace`;
69
+ this.ctx.textBaseline = "top";
70
+ }
71
+ }
72
+ /**
73
+ * Pre-warm the atlas with ASCII characters for a set of theme colors.
74
+ */
75
+ prewarm(colors) {
76
+ if (!this.ctx || !this.canvas)
77
+ return;
78
+ for (const color of colors) {
79
+ if (!color)
80
+ continue;
81
+ for (let code = 32; code <= 126; code++) {
82
+ const char = String.fromCharCode(code);
83
+ this.cacheGlyph(char, color);
84
+ }
85
+ }
86
+ }
87
+ cacheGlyph(char, color) {
88
+ if (!this.ctx || !this.canvas)
89
+ return null;
90
+ const key = `${char}:${color}`;
91
+ const existing = this.glyphMap.get(key);
92
+ if (existing)
93
+ return existing;
94
+ if (this.curX + this.cellW > this.canvas.width) {
95
+ this.curX = 0;
96
+ this.curY += this.cellH;
97
+ }
98
+ if (this.curY + this.cellH > this.canvas.height) {
99
+ // Atlas capacity reached
100
+ return null;
101
+ }
102
+ const x = this.curX;
103
+ const y = this.curY;
104
+ this.ctx.fillStyle = color;
105
+ this.ctx.fillText(char, x, y);
106
+ const loc = {
107
+ x,
108
+ y,
109
+ width: this.cellW,
110
+ height: this.cellH,
111
+ };
112
+ this.glyphMap.set(key, loc);
113
+ this.curX += this.cellW;
114
+ return loc;
115
+ }
116
+ getGlyph(char, color) {
117
+ const key = `${char}:${color}`;
118
+ const cached = this.glyphMap.get(key);
119
+ if (cached)
120
+ return cached;
121
+ return this.cacheGlyph(char, color);
122
+ }
123
+ /**
124
+ * Draw text on destination context using atlas when possible, falling back to fillText.
125
+ */
126
+ drawText(destCtx, text, color, x, y) {
127
+ if (!text)
128
+ return;
129
+ if (this.canvas && destCtx && typeof destCtx.drawImage === "function") {
130
+ let curX = x;
131
+ for (let i = 0; i < text.length; i++) {
132
+ const ch = text[i];
133
+ if (ch === " ") {
134
+ curX += this.charWidth;
135
+ continue;
136
+ }
137
+ const glyph = this.getGlyph(ch, color);
138
+ if (glyph) {
139
+ destCtx.drawImage(this.canvas, glyph.x, glyph.y, glyph.width, glyph.height, curX, y, this.charWidth, this.lineHeight);
140
+ }
141
+ else {
142
+ destCtx.fillStyle = color;
143
+ destCtx.font = this.font;
144
+ destCtx.textBaseline = "top";
145
+ destCtx.fillText(ch, curX, y);
146
+ }
147
+ curX += this.charWidth;
148
+ }
149
+ return;
150
+ }
151
+ // Fallback path when atlas is unavailable
152
+ destCtx.fillStyle = color;
153
+ destCtx.font = this.font;
154
+ destCtx.textBaseline = "top";
155
+ destCtx.fillText(text, x, y);
156
+ }
157
+ }
158
+ //# sourceMappingURL=GlyphAtlas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GlyphAtlas.js","sourceRoot":"","sources":["../src/GlyphAtlas.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Kinetic Momentum Scroller with spring-damped physics.
3
+ * Provides high-refresh (120Hz/144Hz) trackpad momentum interpolation.
4
+ */
5
+ export interface KineticScrollState {
6
+ scrollTop: number;
7
+ scrollLeft: number;
8
+ maxScrollTop: number;
9
+ maxScrollLeft: number;
10
+ }
11
+ export interface KineticScrollerOptions {
12
+ friction?: number;
13
+ minVelocity?: number;
14
+ bounceFriction?: number;
15
+ }
16
+ export declare class KineticScroller {
17
+ private vx;
18
+ private vy;
19
+ private animId;
20
+ private readonly friction;
21
+ private readonly minVelocity;
22
+ private readonly bounceFriction;
23
+ private onUpdate;
24
+ private state;
25
+ constructor(options?: KineticScrollerOptions);
26
+ setState(state: Partial<KineticScrollState>): void;
27
+ setUpdateCallback(cb: (state: {
28
+ scrollTop: number;
29
+ scrollLeft: number;
30
+ }) => void): void;
31
+ /**
32
+ * Pushes a scroll impulse (e.g. from wheel event or touch drag release).
33
+ */
34
+ pushImpulse(deltaX: number, deltaY: number): void;
35
+ stop(): void;
36
+ isScrolling(): boolean;
37
+ private startLoop;
38
+ }
39
+ //# sourceMappingURL=KineticScroller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KineticScroller.d.ts","sourceRoot":"","sources":["../src/KineticScroller.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,EAAE,CAAK;IACf,OAAO,CAAC,EAAE,CAAK;IACf,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAA6E;IAC7F,OAAO,CAAC,KAAK,CAKX;gBAEU,OAAO,GAAE,sBAA2B;IAMzC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,IAAI;IAIlD,iBAAiB,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,IAAI;IAI9F;;OAEG;IACI,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAYjD,IAAI,IAAI,IAAI;IASZ,WAAW,IAAI,OAAO;IAQ7B,OAAO,CAAC,SAAS;CAiDlB"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Kinetic Momentum Scroller with spring-damped physics.
3
+ * Provides high-refresh (120Hz/144Hz) trackpad momentum interpolation.
4
+ */
5
+ export class KineticScroller {
6
+ vx = 0;
7
+ vy = 0;
8
+ animId = null;
9
+ friction;
10
+ minVelocity;
11
+ bounceFriction;
12
+ onUpdate = null;
13
+ state = {
14
+ scrollTop: 0,
15
+ scrollLeft: 0,
16
+ maxScrollTop: 0,
17
+ maxScrollLeft: 0,
18
+ };
19
+ constructor(options = {}) {
20
+ this.friction = options.friction ?? 0.92;
21
+ this.minVelocity = options.minVelocity ?? 0.05;
22
+ this.bounceFriction = options.bounceFriction ?? 0.8;
23
+ }
24
+ setState(state) {
25
+ this.state = { ...this.state, ...state };
26
+ }
27
+ setUpdateCallback(cb) {
28
+ this.onUpdate = cb;
29
+ }
30
+ /**
31
+ * Pushes a scroll impulse (e.g. from wheel event or touch drag release).
32
+ */
33
+ pushImpulse(deltaX, deltaY) {
34
+ this.vx += deltaX;
35
+ this.vy += deltaY;
36
+ if (this.animId === null &&
37
+ (Math.abs(this.vx) > this.minVelocity || Math.abs(this.vy) > this.minVelocity)) {
38
+ this.startLoop();
39
+ }
40
+ }
41
+ stop() {
42
+ if (this.animId !== null && typeof cancelAnimationFrame !== "undefined") {
43
+ cancelAnimationFrame(this.animId);
44
+ this.animId = null;
45
+ }
46
+ this.vx = 0;
47
+ this.vy = 0;
48
+ }
49
+ isScrolling() {
50
+ return (this.animId !== null ||
51
+ Math.abs(this.vx) > this.minVelocity ||
52
+ Math.abs(this.vy) > this.minVelocity);
53
+ }
54
+ startLoop() {
55
+ const step = () => {
56
+ this.vx *= this.friction;
57
+ this.vy *= this.friction;
58
+ let nextTop = this.state.scrollTop + this.vy;
59
+ let nextLeft = this.state.scrollLeft + this.vx;
60
+ // Boundary clamping
61
+ if (nextTop < 0) {
62
+ nextTop = 0;
63
+ this.vy *= this.bounceFriction;
64
+ }
65
+ else if (nextTop > this.state.maxScrollTop) {
66
+ nextTop = this.state.maxScrollTop;
67
+ this.vy *= this.bounceFriction;
68
+ }
69
+ if (nextLeft < 0) {
70
+ nextLeft = 0;
71
+ this.vx *= this.bounceFriction;
72
+ }
73
+ else if (nextLeft > this.state.maxScrollLeft) {
74
+ nextLeft = this.state.maxScrollLeft;
75
+ this.vx *= this.bounceFriction;
76
+ }
77
+ this.state.scrollTop = nextTop;
78
+ this.state.scrollLeft = nextLeft;
79
+ if (this.onUpdate) {
80
+ this.onUpdate({ scrollTop: nextTop, scrollLeft: nextLeft });
81
+ }
82
+ if (Math.abs(this.vx) > this.minVelocity || Math.abs(this.vy) > this.minVelocity) {
83
+ if (typeof requestAnimationFrame !== "undefined") {
84
+ this.animId = requestAnimationFrame(step);
85
+ }
86
+ else {
87
+ this.animId = null;
88
+ }
89
+ }
90
+ else {
91
+ this.stop();
92
+ }
93
+ };
94
+ if (typeof requestAnimationFrame !== "undefined") {
95
+ this.animId = requestAnimationFrame(step);
96
+ }
97
+ else {
98
+ step();
99
+ }
100
+ }
101
+ }
102
+ //# sourceMappingURL=KineticScroller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KineticScroller.js","sourceRoot":"","sources":["../src/KineticScroller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Programming Font Ligature Engine.
3
+ * Detects multi-character ligature sequences and clusters them into unified glyph runs.
4
+ */
5
+ export interface LigatureMatch {
6
+ start: number;
7
+ length: number;
8
+ text: string;
9
+ }
10
+ export declare const DEFAULT_LIGATURE_SEQUENCES: readonly string[];
11
+ export declare class LigatureEngine {
12
+ private ligatures;
13
+ private maxLen;
14
+ constructor(sequences?: readonly string[]);
15
+ /**
16
+ * Scans a line or token string for ligature clusters.
17
+ */
18
+ findLigatures(text: string): LigatureMatch[];
19
+ /**
20
+ * Splits a string into segments of plain text and ligature clusters.
21
+ */
22
+ segmentText(text: string): Array<{
23
+ text: string;
24
+ isLigature: boolean;
25
+ }>;
26
+ }
27
+ //# sourceMappingURL=LigatureEngine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LigatureEngine.d.ts","sourceRoot":"","sources":["../src/LigatureEngine.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAGD,eAAO,MAAM,0BAA0B,EAAE,SAAS,MAAM,EA8BvD,CAAC;AAEF,qBAAa,cAAc;IACzB,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,MAAM,CAAS;gBAEX,SAAS,GAAE,SAAS,MAAM,EAA+B;IAKrE;;OAEG;IACI,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE;IA+BnD;;OAEG;IACI,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,OAAO,CAAA;KAAE,CAAC;CAgC/E"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Programming Font Ligature Engine.
3
+ * Detects multi-character ligature sequences and clusters them into unified glyph runs.
4
+ */
5
+ // Common programming ligature sequences ordered by length (longest first for greedy match)
6
+ export const DEFAULT_LIGATURE_SEQUENCES = [
7
+ "<!--",
8
+ "-->",
9
+ "===",
10
+ "!==",
11
+ "...",
12
+ "::=",
13
+ "<=>",
14
+ "->",
15
+ "<-",
16
+ "=>",
17
+ "<=",
18
+ ">=",
19
+ "==",
20
+ "!=",
21
+ ":=",
22
+ "::",
23
+ "/*",
24
+ "*/",
25
+ "//",
26
+ "**",
27
+ "??",
28
+ "||",
29
+ "&&",
30
+ "++",
31
+ "--",
32
+ "<<",
33
+ ">>",
34
+ "|>",
35
+ "<|",
36
+ ];
37
+ export class LigatureEngine {
38
+ ligatures;
39
+ maxLen;
40
+ constructor(sequences = DEFAULT_LIGATURE_SEQUENCES) {
41
+ this.ligatures = [...sequences].sort((a, b) => b.length - a.length);
42
+ this.maxLen = this.ligatures.reduce((max, s) => Math.max(max, s.length), 0);
43
+ }
44
+ /**
45
+ * Scans a line or token string for ligature clusters.
46
+ */
47
+ findLigatures(text) {
48
+ const matches = [];
49
+ const len = text.length;
50
+ let i = 0;
51
+ while (i < len) {
52
+ let matched = false;
53
+ const remaining = len - i;
54
+ const checkLen = Math.min(remaining, this.maxLen);
55
+ for (const seq of this.ligatures) {
56
+ if (seq.length <= checkLen && text.startsWith(seq, i)) {
57
+ matches.push({
58
+ start: i,
59
+ length: seq.length,
60
+ text: seq,
61
+ });
62
+ i += seq.length;
63
+ matched = true;
64
+ break;
65
+ }
66
+ }
67
+ if (!matched) {
68
+ i++;
69
+ }
70
+ }
71
+ return matches;
72
+ }
73
+ /**
74
+ * Splits a string into segments of plain text and ligature clusters.
75
+ */
76
+ segmentText(text) {
77
+ const matches = this.findLigatures(text);
78
+ if (matches.length === 0) {
79
+ return [{ text, isLigature: false }];
80
+ }
81
+ const segments = [];
82
+ let cur = 0;
83
+ for (const match of matches) {
84
+ if (match.start > cur) {
85
+ segments.push({
86
+ text: text.slice(cur, match.start),
87
+ isLigature: false,
88
+ });
89
+ }
90
+ segments.push({
91
+ text: match.text,
92
+ isLigature: true,
93
+ });
94
+ cur = match.start + match.length;
95
+ }
96
+ if (cur < text.length) {
97
+ segments.push({
98
+ text: text.slice(cur),
99
+ isLigature: false,
100
+ });
101
+ }
102
+ return segments;
103
+ }
104
+ }
105
+ //# sourceMappingURL=LigatureEngine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LigatureEngine.js","sourceRoot":"","sources":["../src/LigatureEngine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ export interface WebGLGlyphUV {
2
+ u0: number;
3
+ v0: number;
4
+ u1: number;
5
+ v1: number;
6
+ width: number;
7
+ height: number;
8
+ }
9
+ /**
10
+ * GPU-friendly Texture Atlas for WebGL 2 / WebGPU instanced glyph and ligature rendering.
11
+ */
12
+ export declare class WebGLGlyphAtlas {
13
+ private canvas;
14
+ private ctx;
15
+ private glyphMap;
16
+ charWidth: number;
17
+ lineHeight: number;
18
+ dpr: number;
19
+ readonly atlasWidth: number;
20
+ readonly atlasHeight: number;
21
+ private curX;
22
+ private curY;
23
+ private cellW;
24
+ private cellH;
25
+ private isDirty;
26
+ constructor(fontFamily: string, fontSize: number, dpr?: number, charWidth?: number, lineHeight?: number, atlasWidth?: number, atlasHeight?: number);
27
+ private initCanvas;
28
+ private prewarmAscii;
29
+ getOrRasterizeGlyph(char: string): WebGLGlyphUV | null;
30
+ getCanvasSource(): HTMLCanvasElement | OffscreenCanvas | null;
31
+ checkAndClearDirty(): boolean;
32
+ getGlyphUV(char: string): WebGLGlyphUV | null;
33
+ }
34
+ //# sourceMappingURL=WebGLGlyphAtlas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebGLGlyphAtlas.d.ts","sourceRoot":"","sources":["../src/WebGLGlyphAtlas.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAoD;IAClE,OAAO,CAAC,GAAG,CAA6E;IACxF,OAAO,CAAC,QAAQ,CAAmC;IAC5C,SAAS,EAAE,MAAM,CAAK;IACtB,UAAU,EAAE,MAAM,CAAK;IACvB,GAAG,EAAE,MAAM,CAAK;IACvB,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,WAAW,EAAE,MAAM,CAAC;IACpC,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,OAAO,CAAkB;gBAG/B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,GAAG,SAAI,EACP,SAAS,CAAC,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,EACnB,UAAU,SAAO,EACjB,WAAW,SAAO;IAgBpB,OAAO,CAAC,UAAU;IAmBlB,OAAO,CAAC,YAAY;IAMb,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAuCtD,eAAe,IAAI,iBAAiB,GAAG,eAAe,GAAG,IAAI;IAI7D,kBAAkB,IAAI,OAAO;IAM7B,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;CAGrD"}
@@ -0,0 +1,99 @@
1
+ import { measureMonospaceFont } from "./GlyphAtlas.js";
2
+ /**
3
+ * GPU-friendly Texture Atlas for WebGL 2 / WebGPU instanced glyph and ligature rendering.
4
+ */
5
+ export class WebGLGlyphAtlas {
6
+ canvas = null;
7
+ ctx = null;
8
+ glyphMap = new Map();
9
+ charWidth = 0;
10
+ lineHeight = 0;
11
+ dpr = 1;
12
+ atlasWidth;
13
+ atlasHeight;
14
+ curX = 0;
15
+ curY = 0;
16
+ cellW = 0;
17
+ cellH = 0;
18
+ isDirty = false;
19
+ constructor(fontFamily, fontSize, dpr = 1, charWidth, lineHeight, atlasWidth = 1024, atlasHeight = 1024) {
20
+ this.dpr = dpr;
21
+ this.atlasWidth = atlasWidth * dpr;
22
+ this.atlasHeight = atlasHeight * dpr;
23
+ const m = measureMonospaceFont(fontFamily, fontSize);
24
+ this.charWidth = charWidth && charWidth > 0 ? charWidth : m.charWidth;
25
+ this.lineHeight = lineHeight && lineHeight > 0 ? lineHeight : m.lineHeight;
26
+ this.cellW = Math.ceil(this.charWidth * this.dpr);
27
+ this.cellH = Math.ceil(this.lineHeight * this.dpr);
28
+ this.initCanvas(fontFamily, fontSize);
29
+ this.prewarmAscii();
30
+ }
31
+ initCanvas(fontFamily, fontSize) {
32
+ if (typeof OffscreenCanvas !== "undefined") {
33
+ this.canvas = new OffscreenCanvas(this.atlasWidth, this.atlasHeight);
34
+ this.ctx = this.canvas.getContext("2d");
35
+ }
36
+ else if (typeof document !== "undefined") {
37
+ const c = document.createElement("canvas");
38
+ c.width = this.atlasWidth;
39
+ c.height = this.atlasHeight;
40
+ this.canvas = c;
41
+ this.ctx = c.getContext("2d");
42
+ }
43
+ if (this.ctx) {
44
+ this.ctx.font = `${Math.round(fontSize * this.dpr)}px ${fontFamily}, monospace`;
45
+ this.ctx.textBaseline = "top";
46
+ this.ctx.fillStyle = "#ffffff";
47
+ }
48
+ }
49
+ prewarmAscii() {
50
+ for (let code = 32; code <= 126; code++) {
51
+ this.getOrRasterizeGlyph(String.fromCharCode(code));
52
+ }
53
+ }
54
+ getOrRasterizeGlyph(char) {
55
+ if (this.glyphMap.has(char)) {
56
+ return this.glyphMap.get(char);
57
+ }
58
+ if (!this.ctx)
59
+ return null;
60
+ const widthCells = char.length > 1 ? char.length : 1;
61
+ const requiredWidth = this.cellW * widthCells;
62
+ if (this.curX + requiredWidth > this.atlasWidth) {
63
+ this.curX = 0;
64
+ this.curY += this.cellH;
65
+ }
66
+ if (this.curY + this.cellH > this.atlasHeight) {
67
+ // Atlas capacity reached
68
+ return null;
69
+ }
70
+ const rx = this.curX;
71
+ const ry = this.curY;
72
+ this.ctx.fillStyle = "#ffffff";
73
+ this.ctx.fillText(char, rx, ry);
74
+ this.curX += requiredWidth;
75
+ this.isDirty = true;
76
+ const uv = {
77
+ u0: rx / this.atlasWidth,
78
+ v0: ry / this.atlasHeight,
79
+ u1: (rx + requiredWidth) / this.atlasWidth,
80
+ v1: (ry + this.cellH) / this.atlasHeight,
81
+ width: this.charWidth * widthCells,
82
+ height: this.lineHeight,
83
+ };
84
+ this.glyphMap.set(char, uv);
85
+ return uv;
86
+ }
87
+ getCanvasSource() {
88
+ return this.canvas;
89
+ }
90
+ checkAndClearDirty() {
91
+ const dirty = this.isDirty;
92
+ this.isDirty = false;
93
+ return dirty;
94
+ }
95
+ getGlyphUV(char) {
96
+ return this.glyphMap.get(char) ?? this.getOrRasterizeGlyph(char);
97
+ }
98
+ }
99
+ //# sourceMappingURL=WebGLGlyphAtlas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebGLGlyphAtlas.js","sourceRoot":"","sources":["../src/WebGLGlyphAtlas.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { CanvasRenderOptions, CanvasViewport, LineDrawData, CaretRenderOptions, SelectionRenderOptions } from "./types.js";
2
+ import type { WebGLGlyphAtlas } from "./WebGLGlyphAtlas.js";
3
+ /**
4
+ * High-performance WebGL 2 Instanced Viewport Renderer with Ligature Clustering.
5
+ * Renders all glyphs and multi-character ligatures in a single GPU draw call at 144Hz-240Hz.
6
+ */
7
+ export declare class WebGLViewportRenderer {
8
+ private gl;
9
+ private program;
10
+ private texture;
11
+ private quadVbo;
12
+ private instanceVbo;
13
+ private vao;
14
+ private atlas;
15
+ private ligatureEngine;
16
+ private maxInstances;
17
+ private instanceData;
18
+ private uResolutionLoc;
19
+ private uAtlasLoc;
20
+ constructor(canvas: HTMLCanvasElement, atlas: WebGLGlyphAtlas, _enableLigatures?: boolean);
21
+ private initGL;
22
+ private compileShader;
23
+ render(lines: LineDrawData[], viewport: CanvasViewport, options: CanvasRenderOptions, _caret?: CaretRenderOptions, _selection?: SelectionRenderOptions): void;
24
+ isSupported(): boolean;
25
+ }
26
+ //# sourceMappingURL=WebGLViewportRenderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebGLViewportRenderer.d.ts","sourceRoot":"","sources":["../src/WebGLViewportRenderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,sBAAsB,EACvB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AA2D5D;;;GAGG;AACH,qBAAa,qBAAqB;IAChC,OAAO,CAAC,EAAE,CAAuC;IACjD,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,GAAG,CAAuC;IAClD,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,cAAc,CAAiB;IAGvC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,YAAY,CAA4C;IAChE,OAAO,CAAC,cAAc,CAAqC;IAC3D,OAAO,CAAC,SAAS,CAAqC;gBAE1C,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,eAAe,EAAE,gBAAgB,UAAO;IAMtF,OAAO,CAAC,MAAM;IAmEd,OAAO,CAAC,aAAa;IAad,MAAM,CACX,KAAK,EAAE,YAAY,EAAE,EACrB,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,mBAAmB,EAC5B,MAAM,CAAC,EAAE,kBAAkB,EAC3B,UAAU,CAAC,EAAE,sBAAsB,GAClC,IAAI;IAsKA,WAAW,IAAI,OAAO;CAG9B"}