@vibecook/ghosttea-react 0.12.0 → 0.13.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 +59 -4
- package/dist/TerminalSurface.d.ts +53 -0
- package/dist/TerminalSurface.d.ts.map +1 -1
- package/dist/TerminalSurface.js +76 -9
- package/dist/TerminalSurface.js.map +1 -1
- package/dist/appearance/AppearanceSettings.d.ts +2 -0
- package/dist/appearance/AppearanceSettings.d.ts.map +1 -1
- package/dist/appearance/AppearanceSettings.js +12 -2
- package/dist/appearance/AppearanceSettings.js.map +1 -1
- package/dist/appearance/types.d.ts +4 -0
- package/dist/appearance/types.d.ts.map +1 -1
- package/dist/catalog-budget.d.ts +2 -2
- package/dist/catalog-budget.d.ts.map +1 -1
- package/dist/catalog-budget.js +2 -2
- package/dist/catalog-budget.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/renderers/glyph-atlas.d.ts +43 -0
- package/dist/renderers/glyph-atlas.d.ts.map +1 -0
- package/dist/renderers/glyph-atlas.js +175 -0
- package/dist/renderers/glyph-atlas.js.map +1 -0
- package/dist/renderers/light-adaptation.d.ts +36 -0
- package/dist/renderers/light-adaptation.d.ts.map +1 -0
- package/dist/renderers/light-adaptation.js +332 -0
- package/dist/renderers/light-adaptation.js.map +1 -0
- package/dist/renderers/types.d.ts +21 -0
- package/dist/renderers/types.d.ts.map +1 -1
- package/dist/renderers/types.js.map +1 -1
- package/dist/renderers/webgpu-renderer.d.ts +4 -0
- package/dist/renderers/webgpu-renderer.d.ts.map +1 -1
- package/dist/renderers/webgpu-renderer.js +321 -237
- package/dist/renderers/webgpu-renderer.js.map +1 -1
- package/dist/routed-control.d.ts +26 -2
- package/dist/routed-control.d.ts.map +1 -1
- package/dist/routed-control.js +145 -27
- package/dist/routed-control.js.map +1 -1
- package/dist/routed-frames.d.ts +1 -1
- package/dist/routed-frames.d.ts.map +1 -1
- package/dist/routed-frames.js +3 -7
- package/dist/routed-frames.js.map +1 -1
- package/dist/row-damage.d.ts +21 -0
- package/dist/row-damage.d.ts.map +1 -0
- package/dist/row-damage.js +76 -0
- package/dist/row-damage.js.map +1 -0
- package/dist/runtime.d.ts +92 -3
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +439 -43
- package/dist/runtime.js.map +1 -1
- package/dist/styles.css +11 -0
- package/dist/terminal-render.worker.js +908 -288
- package/dist/terminal-render.worker.js.map +4 -4
- package/dist/worker-messages.d.ts +22 -1
- package/dist/worker-messages.d.ts.map +1 -1
- package/dist/workspace/Workspace.d.ts +15 -2
- package/dist/workspace/Workspace.d.ts.map +1 -1
- package/dist/workspace/Workspace.js +36 -12
- package/dist/workspace/Workspace.js.map +1 -1
- package/dist/workspace/pane-layout.d.ts +3 -1
- package/dist/workspace/pane-layout.d.ts.map +1 -1
- package/dist/workspace/pane-layout.js +16 -4
- package/dist/workspace/pane-layout.js.map +1 -1
- package/package.json +4 -4
|
@@ -23,6 +23,7 @@ var SectionKind;
|
|
|
23
23
|
SectionKind2[SectionKind2["AccessibilityText"] = 10] = "AccessibilityText";
|
|
24
24
|
SectionKind2[SectionKind2["ClipboardWrite"] = 11] = "ClipboardWrite";
|
|
25
25
|
SectionKind2[SectionKind2["LinkTargets"] = 12] = "LinkTargets";
|
|
26
|
+
SectionKind2[SectionKind2["StylePalette"] = 13] = "StylePalette";
|
|
26
27
|
})(SectionKind || (SectionKind = {}));
|
|
27
28
|
var GlyphFormat;
|
|
28
29
|
(function(GlyphFormat2) {
|
|
@@ -41,14 +42,15 @@ function assertRange(condition, message) {
|
|
|
41
42
|
throw new RangeError(`Invalid terminal frame: ${message}`);
|
|
42
43
|
}
|
|
43
44
|
function decodeFrame(buffer) {
|
|
44
|
-
|
|
45
|
-
assertRange(
|
|
46
|
-
|
|
45
|
+
const bytes = buffer instanceof Uint8Array ? new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength) : new Uint8Array(buffer);
|
|
46
|
+
assertRange(bytes.byteLength >= FRAME_HEADER_BYTES, "truncated header");
|
|
47
|
+
assertRange(bytes.byteLength <= MAX_FRAME_BYTES, "packet exceeds limit");
|
|
48
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
47
49
|
assertRange(view.getUint32(0, true) === FRAME_MAGIC, "bad magic");
|
|
48
50
|
assertRange(view.getUint16(4, true) === FRAME_PROTOCOL_VERSION, "unsupported protocol version");
|
|
49
51
|
const sectionCount = view.getUint16(60, true);
|
|
50
52
|
const tableEnd = FRAME_HEADER_BYTES + sectionCount * SECTION_HEADER_BYTES;
|
|
51
|
-
assertRange(tableEnd <=
|
|
53
|
+
assertRange(tableEnd <= bytes.byteLength, "truncated section table");
|
|
52
54
|
const sections = [];
|
|
53
55
|
for (let index = 0; index < sectionCount; index += 1) {
|
|
54
56
|
const base = FRAME_HEADER_BYTES + index * SECTION_HEADER_BYTES;
|
|
@@ -58,8 +60,8 @@ function decodeFrame(buffer) {
|
|
|
58
60
|
const length = view.getUint32(base + 8, true);
|
|
59
61
|
const itemCount = view.getUint32(base + 12, true);
|
|
60
62
|
const end = offset + length;
|
|
61
|
-
assertRange(end >= offset && offset >= tableEnd && end <=
|
|
62
|
-
sections.push({ kind, flags, itemCount, bytes:
|
|
63
|
+
assertRange(end >= offset && offset >= tableEnd && end <= bytes.byteLength, "section out of bounds");
|
|
64
|
+
sections.push({ kind, flags, itemCount, bytes: bytes.subarray(offset, end) });
|
|
63
65
|
}
|
|
64
66
|
return {
|
|
65
67
|
protocolVersion: view.getUint16(4, true),
|
|
@@ -152,14 +154,14 @@ function decodeGlyphDefinitions(section) {
|
|
|
152
154
|
bearingX: view.getInt16(offset - 12, true),
|
|
153
155
|
bearingY: view.getInt16(offset - 10, true),
|
|
154
156
|
format,
|
|
155
|
-
pixels: section.bytes.
|
|
157
|
+
pixels: new Uint8Array(section.bytes.subarray(offset, offset + pixelLength))
|
|
156
158
|
});
|
|
157
159
|
offset += pixelLength;
|
|
158
160
|
}
|
|
159
161
|
assertRange(offset === section.bytes.byteLength, "trailing glyph definition bytes");
|
|
160
162
|
return definitions;
|
|
161
163
|
}
|
|
162
|
-
function decodeStyleDefinitions(section) {
|
|
164
|
+
function decodeStyleDefinitions(section, paletteSection) {
|
|
163
165
|
assertRange(section.kind === SectionKind.StyleDefinitions, "wrong style definition section kind");
|
|
164
166
|
assertRange(section.bytes.byteLength >= 4, "truncated style definition header");
|
|
165
167
|
const view = new DataView(section.bytes.buffer, section.bytes.byteOffset, section.bytes.byteLength);
|
|
@@ -188,7 +190,39 @@ function decodeStyleDefinitions(section) {
|
|
|
188
190
|
...backgroundKind === 1 ? { background: [view.getUint8(offset + 11), view.getUint8(offset + 12), view.getUint8(offset + 13)] } : {}
|
|
189
191
|
});
|
|
190
192
|
}
|
|
191
|
-
|
|
193
|
+
if (!paletteSection)
|
|
194
|
+
return styles;
|
|
195
|
+
const palette = new Map(decodeStylePalette(paletteSection).map((entry) => [entry.styleId, entry]));
|
|
196
|
+
return styles.map((style) => {
|
|
197
|
+
const entry = palette.get(style.id);
|
|
198
|
+
if (!entry)
|
|
199
|
+
return style;
|
|
200
|
+
return {
|
|
201
|
+
...style,
|
|
202
|
+
...entry.foreground !== void 0 && style.foreground ? { foregroundPalette: entry.foreground } : {},
|
|
203
|
+
...entry.background !== void 0 && style.background ? { backgroundPalette: entry.background } : {}
|
|
204
|
+
};
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
function decodeStylePalette(section) {
|
|
208
|
+
assertRange(section.kind === SectionKind.StylePalette, "wrong style palette section kind");
|
|
209
|
+
assertRange(section.bytes.byteLength >= 4, "truncated style palette header");
|
|
210
|
+
const view = new DataView(section.bytes.buffer, section.bytes.byteOffset, section.bytes.byteLength);
|
|
211
|
+
const count = view.getUint32(0, true);
|
|
212
|
+
assertRange(count === section.itemCount, "style palette count mismatch");
|
|
213
|
+
assertRange(section.bytes.byteLength === 4 + count * 8, "invalid style palette length");
|
|
214
|
+
const entries = [];
|
|
215
|
+
for (let index = 0; index < count; index += 1) {
|
|
216
|
+
const offset = 4 + index * 8;
|
|
217
|
+
const flags = view.getUint8(offset + 4);
|
|
218
|
+
assertRange((flags & ~3) === 0, "invalid style palette flags");
|
|
219
|
+
entries.push({
|
|
220
|
+
styleId: view.getUint32(offset, true),
|
|
221
|
+
...flags & 1 ? { foreground: view.getUint8(offset + 5) } : {},
|
|
222
|
+
...flags & 2 ? { background: view.getUint8(offset + 6) } : {}
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
return entries;
|
|
192
226
|
}
|
|
193
227
|
function decodeCursorState(section) {
|
|
194
228
|
assertRange(section.kind === SectionKind.CursorState, "wrong cursor section kind");
|
|
@@ -688,8 +722,418 @@ fn sparks(position: vec2f, uv: vec2f) -> vec4f {
|
|
|
688
722
|
`
|
|
689
723
|
);
|
|
690
724
|
|
|
725
|
+
// src/renderers/light-adaptation.ts
|
|
726
|
+
var clamp = (value, lo = 0, hi = 1) => Math.min(hi, Math.max(lo, value));
|
|
727
|
+
var toLinear = (v) => v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
|
|
728
|
+
var toGamma = (v) => v <= 31308e-7 ? v * 12.92 : 1.055 * v ** (1 / 2.4) - 0.055;
|
|
729
|
+
function oklab([r8, g8, b8]) {
|
|
730
|
+
const r = toLinear(r8 / 255);
|
|
731
|
+
const g = toLinear(g8 / 255);
|
|
732
|
+
const b = toLinear(b8 / 255);
|
|
733
|
+
const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
|
|
734
|
+
const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
|
|
735
|
+
const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
|
|
736
|
+
return [
|
|
737
|
+
0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,
|
|
738
|
+
1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,
|
|
739
|
+
0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s
|
|
740
|
+
];
|
|
741
|
+
}
|
|
742
|
+
function oklabToLinear([L, a, b]) {
|
|
743
|
+
const l = (L + 0.3963377774 * a + 0.2158037573 * b) ** 3;
|
|
744
|
+
const m = (L - 0.1055613458 * a - 0.0638541728 * b) ** 3;
|
|
745
|
+
const s = (L - 0.0894841775 * a - 1.291485548 * b) ** 3;
|
|
746
|
+
return [
|
|
747
|
+
4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
|
|
748
|
+
-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
|
|
749
|
+
-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s
|
|
750
|
+
];
|
|
751
|
+
}
|
|
752
|
+
var inGamut = (linear) => linear.every((v) => v >= -1e-4 && v <= 1 + 1e-4);
|
|
753
|
+
var fromLch = (L, C, h) => [L, C * Math.cos(h), C * Math.sin(h)];
|
|
754
|
+
var toRgb = (lab) => oklabToLinear(lab).map((v) => Math.round(clamp(toGamma(clamp(v))) * 255));
|
|
755
|
+
function gamutMap(L, C, h) {
|
|
756
|
+
L = clamp(L);
|
|
757
|
+
if (inGamut(oklabToLinear(fromLch(L, C, h)))) return toRgb(fromLch(L, C, h));
|
|
758
|
+
let lo = 0;
|
|
759
|
+
let hi = C;
|
|
760
|
+
for (let i = 0; i < 20; i += 1) {
|
|
761
|
+
const mid = (lo + hi) / 2;
|
|
762
|
+
if (inGamut(oklabToLinear(fromLch(L, mid, h)))) lo = mid;
|
|
763
|
+
else hi = mid;
|
|
764
|
+
}
|
|
765
|
+
return toRgb(fromLch(L, lo, h));
|
|
766
|
+
}
|
|
767
|
+
function maxChroma(L, h) {
|
|
768
|
+
let lo = 0;
|
|
769
|
+
let hi = 0.4;
|
|
770
|
+
for (let i = 0; i < 16; i += 1) {
|
|
771
|
+
const mid = (lo + hi) / 2;
|
|
772
|
+
if (inGamut(oklabToLinear(fromLch(L, mid, h)))) lo = mid;
|
|
773
|
+
else hi = mid;
|
|
774
|
+
}
|
|
775
|
+
return lo;
|
|
776
|
+
}
|
|
777
|
+
var K1 = 0.206;
|
|
778
|
+
var K2 = 0.03;
|
|
779
|
+
var K3 = (1 + K1) / (1 + K2);
|
|
780
|
+
var toe = (L) => 0.5 * (K3 * L - K1 + Math.sqrt((K3 * L - K1) ** 2 + 4 * K2 * K3 * L));
|
|
781
|
+
var toeInv = (Lr) => (Lr * Lr + K1 * Lr) / (K3 * (Lr + K2));
|
|
782
|
+
function apcaY([r, g, b]) {
|
|
783
|
+
const y = 0.2126729 * (r / 255) ** 2.4 + 0.7151522 * (g / 255) ** 2.4 + 0.072175 * (b / 255) ** 2.4;
|
|
784
|
+
return y < 0.022 ? y + (0.022 - y) ** 1.414 : y;
|
|
785
|
+
}
|
|
786
|
+
function apca(text, background) {
|
|
787
|
+
const yt = apcaY(text);
|
|
788
|
+
const yb = apcaY(background);
|
|
789
|
+
if (Math.abs(yb - yt) < 5e-4) return 0;
|
|
790
|
+
if (yb > yt) {
|
|
791
|
+
const sapc2 = (yb ** 0.56 - yt ** 0.57) * 1.14;
|
|
792
|
+
return sapc2 < 0.1 ? 0 : (sapc2 - 0.027) * 100;
|
|
793
|
+
}
|
|
794
|
+
const sapc = (yb ** 0.65 - yt ** 0.62) * 1.14;
|
|
795
|
+
return sapc > -0.1 ? 0 : (sapc + 0.027) * 100;
|
|
796
|
+
}
|
|
797
|
+
var smoothstep = (e0, e1, x) => {
|
|
798
|
+
const t = clamp((x - e0) / (e1 - e0));
|
|
799
|
+
return t * t * (3 - 2 * t);
|
|
800
|
+
};
|
|
801
|
+
var toRgba = ([r, g, b]) => [r / 255, g / 255, b / 255, 1];
|
|
802
|
+
var fromRgba = (color) => [
|
|
803
|
+
Math.round(clamp(color[0]) * 255),
|
|
804
|
+
Math.round(clamp(color[1]) * 255),
|
|
805
|
+
Math.round(clamp(color[2]) * 255)
|
|
806
|
+
];
|
|
807
|
+
var keyOf = ([r, g, b]) => r << 16 | g << 8 | b;
|
|
808
|
+
var luminance = ([r, g, b]) => 0.2126 * toLinear(r / 255) + 0.7152 * toLinear(g / 255) + 0.0722 * toLinear(b / 255);
|
|
809
|
+
var contrastRatio = (a, b) => {
|
|
810
|
+
const [hi, lo] = [luminance(a), luminance(b)].sort((x, y) => y - x);
|
|
811
|
+
return (hi + 0.05) / (lo + 0.05);
|
|
812
|
+
};
|
|
813
|
+
function isLightBackground(color) {
|
|
814
|
+
const y = luminance(color);
|
|
815
|
+
return (y + 0.05) / 0.05 > 1.05 / (y + 0.05);
|
|
816
|
+
}
|
|
817
|
+
var contrasted = /* @__PURE__ */ new Map();
|
|
818
|
+
function ensureContrast(text, background, ratio) {
|
|
819
|
+
if (!(ratio > 1)) return text;
|
|
820
|
+
const fg = fromRgba(text);
|
|
821
|
+
const bg = fromRgba(background);
|
|
822
|
+
const cacheKey = `${keyOf(fg)}/${keyOf(bg)}/${ratio}`;
|
|
823
|
+
let result = contrasted.get(cacheKey);
|
|
824
|
+
if (result === void 0) {
|
|
825
|
+
if (contrastRatio(fg, bg) >= ratio) result = null;
|
|
826
|
+
else {
|
|
827
|
+
const towardWhite = contrastRatio([255, 255, 255], bg) > contrastRatio([0, 0, 0], bg);
|
|
828
|
+
const [L, a, b] = oklab(fg);
|
|
829
|
+
const C = Math.hypot(a, b);
|
|
830
|
+
const h = Math.atan2(b, a);
|
|
831
|
+
let near = L;
|
|
832
|
+
let far = towardWhite ? 1 : 0;
|
|
833
|
+
let best = towardWhite ? [255, 255, 255] : [0, 0, 0];
|
|
834
|
+
if (contrastRatio(best, bg) >= ratio) {
|
|
835
|
+
for (let i = 0; i < 20; i += 1) {
|
|
836
|
+
const mid = (near + far) / 2;
|
|
837
|
+
const candidate = gamutMap(mid, C, h);
|
|
838
|
+
if (contrastRatio(candidate, bg) >= ratio) {
|
|
839
|
+
far = mid;
|
|
840
|
+
best = candidate;
|
|
841
|
+
} else near = mid;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
result = best;
|
|
845
|
+
}
|
|
846
|
+
if (contrasted.size > 4096) contrasted.clear();
|
|
847
|
+
contrasted.set(cacheKey, result);
|
|
848
|
+
}
|
|
849
|
+
if (result === null) return text;
|
|
850
|
+
return [result[0] / 255, result[1] / 255, result[2] / 255, text[3]];
|
|
851
|
+
}
|
|
852
|
+
function createLightAdaptation(base, paperColor, inkColor) {
|
|
853
|
+
const paper = fromRgba(paperColor);
|
|
854
|
+
const inkRgb = fromRgba(inkColor);
|
|
855
|
+
const baseLab = oklab(base);
|
|
856
|
+
const paperLab = oklab(paper);
|
|
857
|
+
const inkLab = oklab(inkRgb);
|
|
858
|
+
const baseLr = toe(baseLab[0]);
|
|
859
|
+
const paperLr = toe(paperLab[0]);
|
|
860
|
+
const surfaces2 = /* @__PURE__ */ new Map();
|
|
861
|
+
const surfaceRgb = (color) => {
|
|
862
|
+
const cached = surfaces2.get(keyOf(color));
|
|
863
|
+
if (cached) return cached;
|
|
864
|
+
const [L, a, b] = oklab(color);
|
|
865
|
+
let Lr = clamp(paperLr - (toe(L) - baseLr));
|
|
866
|
+
const ap = a - baseLab[1] + paperLab[1];
|
|
867
|
+
const bp = b - baseLab[2] + paperLab[2];
|
|
868
|
+
const C = Math.hypot(ap, bp);
|
|
869
|
+
const h = Math.atan2(bp, ap);
|
|
870
|
+
if (C > 0.015) {
|
|
871
|
+
const want = Math.min(C, 0.045);
|
|
872
|
+
const stop = Lr - 0.15;
|
|
873
|
+
while (Lr > stop && maxChroma(toeInv(Lr), h) < want) Lr -= 5e-3;
|
|
874
|
+
}
|
|
875
|
+
const result = gamutMap(toeInv(Lr), C, h);
|
|
876
|
+
surfaces2.set(keyOf(color), result);
|
|
877
|
+
return result;
|
|
878
|
+
};
|
|
879
|
+
const solveLc = (want, C, h, background) => {
|
|
880
|
+
const backgroundL = oklab(background)[0];
|
|
881
|
+
let lo = want > 0 ? 0 : backgroundL;
|
|
882
|
+
let hi = want > 0 ? backgroundL : 1;
|
|
883
|
+
let best = gamutMap(want > 0 ? 0 : 1, C, h);
|
|
884
|
+
for (let i = 0; i < 18; i += 1) {
|
|
885
|
+
const mid = (lo + hi) / 2;
|
|
886
|
+
const candidate = gamutMap(mid, C, h);
|
|
887
|
+
const lc = apca(candidate, background);
|
|
888
|
+
const enough = want > 0 ? lc >= want : lc <= want;
|
|
889
|
+
if (enough) best = candidate;
|
|
890
|
+
if (want > 0 === enough) lo = mid;
|
|
891
|
+
else hi = mid;
|
|
892
|
+
}
|
|
893
|
+
return best;
|
|
894
|
+
};
|
|
895
|
+
const inks = /* @__PURE__ */ new Map();
|
|
896
|
+
const ink = (color, sourceBackground, paletteIndex) => {
|
|
897
|
+
const themeColor = paletteIndex !== void 0 && CHROMATIC_ANSI.has(paletteIndex);
|
|
898
|
+
const cacheKey = `${keyOf(color)}/${keyOf(sourceBackground)}${themeColor ? "/p" : ""}`;
|
|
899
|
+
const cached = inks.get(cacheKey);
|
|
900
|
+
if (cached) return cached;
|
|
901
|
+
const mapped = surfaceRgb(sourceBackground);
|
|
902
|
+
if (themeColor) {
|
|
903
|
+
const legibility = Math.min(45, Math.abs(apca(color, paper)));
|
|
904
|
+
if (Math.abs(apca(color, mapped)) >= legibility) {
|
|
905
|
+
const rgba2 = toRgba(color);
|
|
906
|
+
inks.set(cacheKey, rgba2);
|
|
907
|
+
return rgba2;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
const [L, a0, b0] = oklab(color);
|
|
911
|
+
const C0 = Math.hypot(a0, b0);
|
|
912
|
+
let a = a0;
|
|
913
|
+
let b = b0;
|
|
914
|
+
if (C0 < 0.03) {
|
|
915
|
+
const w2 = (1 - C0 / 0.03) * 0.7;
|
|
916
|
+
a = a0 * (1 - w2) + inkLab[1] * w2;
|
|
917
|
+
b = b0 * (1 - w2) + inkLab[2] * w2;
|
|
918
|
+
}
|
|
919
|
+
const C = Math.hypot(a, b);
|
|
920
|
+
const h = Math.atan2(b, a);
|
|
921
|
+
const delta = toe(L) - toe(oklab(sourceBackground)[0]);
|
|
922
|
+
const sign = delta >= 0 ? -1 : 1;
|
|
923
|
+
const Lr = clamp(toe(oklab(mapped)[0]) + sign * Math.max(Math.abs(delta), 0.06));
|
|
924
|
+
let result = gamutMap(toeInv(Lr), C, h);
|
|
925
|
+
const w = smoothstep(0.03, 0.08, C0);
|
|
926
|
+
if (w > 0) {
|
|
927
|
+
const source = apca(color, sourceBackground);
|
|
928
|
+
const lightOnDark = source < 0 || source === 0 && delta >= 0;
|
|
929
|
+
const magnitude = clamp(Math.abs(source) * 0.5 + 28, 45, 70);
|
|
930
|
+
const boosted = C0 + w * clamp(0.12 - C0, 0, C0 * 0.8);
|
|
931
|
+
const accent = solveLc(lightOnDark ? magnitude : -magnitude, boosted, Math.atan2(b0, a0), mapped);
|
|
932
|
+
if (w >= 1) result = accent;
|
|
933
|
+
else {
|
|
934
|
+
const p = oklab(result);
|
|
935
|
+
const q = oklab(accent);
|
|
936
|
+
result = toRgb([p[0] * (1 - w) + q[0] * w, p[1] * (1 - w) + q[1] * w, p[2] * (1 - w) + q[2] * w]);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
const rgba = toRgba(result);
|
|
940
|
+
inks.set(cacheKey, rgba);
|
|
941
|
+
return rgba;
|
|
942
|
+
};
|
|
943
|
+
return {
|
|
944
|
+
key: [base, paper, inkRgb].map((color) => keyOf(color).toString(16)).join("/"),
|
|
945
|
+
base,
|
|
946
|
+
surface: (color) => toRgba(surfaceRgb(color)),
|
|
947
|
+
ink
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
var CHROMATIC_ANSI = /* @__PURE__ */ new Set([1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14]);
|
|
951
|
+
var ENGAGE_SHARE = 0.6;
|
|
952
|
+
var RELEASE_SHARE = 0.35;
|
|
953
|
+
function darkPaintedBase(view, engaged) {
|
|
954
|
+
if ((view.theme.lightAdaptation ?? "off") === "off") return null;
|
|
955
|
+
if (!isLightBackground(fromRgba(view.theme.background))) return null;
|
|
956
|
+
const rows = Math.max(view.rows.length, view.nativeStyleRows.length);
|
|
957
|
+
const total = rows * view.cols;
|
|
958
|
+
if (total === 0) return null;
|
|
959
|
+
const areas = /* @__PURE__ */ new Map();
|
|
960
|
+
let dark = 0;
|
|
961
|
+
const darkness = /* @__PURE__ */ new Map();
|
|
962
|
+
for (const runs of view.nativeStyleRows) {
|
|
963
|
+
for (const run of runs) {
|
|
964
|
+
const style = view.styleDefinitions.get(run.styleId);
|
|
965
|
+
const background = style?.inverse ? style.foreground : style?.background;
|
|
966
|
+
if (!background) continue;
|
|
967
|
+
const key = keyOf(background);
|
|
968
|
+
let isDark = darkness.get(key);
|
|
969
|
+
if (isDark === void 0) {
|
|
970
|
+
isDark = !isLightBackground(background);
|
|
971
|
+
darkness.set(key, isDark);
|
|
972
|
+
}
|
|
973
|
+
if (!isDark) continue;
|
|
974
|
+
dark += run.cellSpan;
|
|
975
|
+
areas.set(key, (areas.get(key) ?? 0) + run.cellSpan);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
if (dark / total < (engaged ? RELEASE_SHARE : ENGAGE_SHARE)) return null;
|
|
979
|
+
let bestKey = 0;
|
|
980
|
+
let bestArea = -1;
|
|
981
|
+
for (const [key, area] of areas) {
|
|
982
|
+
if (area > bestArea) {
|
|
983
|
+
bestKey = key;
|
|
984
|
+
bestArea = area;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
return [bestKey >> 16 & 255, bestKey >> 8 & 255, bestKey & 255];
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
// src/renderers/glyph-atlas.ts
|
|
991
|
+
var AtlasCapacityError = class extends Error {
|
|
992
|
+
};
|
|
993
|
+
var AtlasBatchFullError = class extends Error {
|
|
994
|
+
};
|
|
995
|
+
var GlyphAtlasPages = class {
|
|
996
|
+
constructor(device, layout, label, format, maxPages, size = 2048) {
|
|
997
|
+
this.device = device;
|
|
998
|
+
this.layout = layout;
|
|
999
|
+
this.label = label;
|
|
1000
|
+
this.format = format;
|
|
1001
|
+
this.maxPages = maxPages;
|
|
1002
|
+
this.size = size;
|
|
1003
|
+
this.#sampler = device.createSampler({ minFilter: "linear", magFilter: "linear" });
|
|
1004
|
+
}
|
|
1005
|
+
#pages = [];
|
|
1006
|
+
#sampler;
|
|
1007
|
+
#current;
|
|
1008
|
+
#generation = 0;
|
|
1009
|
+
#clock = 0;
|
|
1010
|
+
get generation() {
|
|
1011
|
+
return this.#current?.generation ?? 0;
|
|
1012
|
+
}
|
|
1013
|
+
get bindGroup() {
|
|
1014
|
+
if (!this.#current) throw new Error(`${this.label} has not been prepared`);
|
|
1015
|
+
return this.#current.bindGroup;
|
|
1016
|
+
}
|
|
1017
|
+
prepare(input) {
|
|
1018
|
+
const glyphs = /* @__PURE__ */ new Map();
|
|
1019
|
+
for (const glyph of input) glyphs.set(glyph.key, glyph);
|
|
1020
|
+
if (glyphs.size === 0) return;
|
|
1021
|
+
let selected;
|
|
1022
|
+
let selectedPlan;
|
|
1023
|
+
let bestHits = -1;
|
|
1024
|
+
for (const page of this.#pages) {
|
|
1025
|
+
const plan2 = this.#plan(glyphs.values(), page);
|
|
1026
|
+
if (!plan2) continue;
|
|
1027
|
+
let hits = 0;
|
|
1028
|
+
for (const key of glyphs.keys()) if (page.cache.has(key)) hits += 1;
|
|
1029
|
+
if (hits > bestHits || hits === bestHits && page.used > (selected?.used ?? -1)) {
|
|
1030
|
+
selected = page;
|
|
1031
|
+
selectedPlan = plan2;
|
|
1032
|
+
bestHits = hits;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
if (!selected) {
|
|
1036
|
+
selectedPlan = this.#plan(glyphs.values());
|
|
1037
|
+
if (!selectedPlan) {
|
|
1038
|
+
throw new AtlasCapacityError(`${this.label} cannot fit the glyphs visible in one frame`);
|
|
1039
|
+
}
|
|
1040
|
+
if (this.#pages.length === this.maxPages) {
|
|
1041
|
+
const recyclable = this.#pages.filter((page) => !page.pinned).sort((a, b) => a.used - b.used)[0];
|
|
1042
|
+
if (!recyclable) throw new AtlasBatchFullError(this.label);
|
|
1043
|
+
recyclable.texture.destroy();
|
|
1044
|
+
this.#pages.splice(this.#pages.indexOf(recyclable), 1);
|
|
1045
|
+
}
|
|
1046
|
+
const texture = this.device.createTexture({
|
|
1047
|
+
label: this.label,
|
|
1048
|
+
size: [this.size, this.size],
|
|
1049
|
+
format: this.format,
|
|
1050
|
+
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST
|
|
1051
|
+
});
|
|
1052
|
+
selected = {
|
|
1053
|
+
texture,
|
|
1054
|
+
bindGroup: this.device.createBindGroup({
|
|
1055
|
+
label: `${this.label} bindings`,
|
|
1056
|
+
layout: this.layout,
|
|
1057
|
+
entries: [
|
|
1058
|
+
{ binding: 0, resource: texture.createView() },
|
|
1059
|
+
{ binding: 1, resource: this.#sampler }
|
|
1060
|
+
]
|
|
1061
|
+
}),
|
|
1062
|
+
cache: /* @__PURE__ */ new Map(),
|
|
1063
|
+
slots: /* @__PURE__ */ new Map(),
|
|
1064
|
+
generation: ++this.#generation,
|
|
1065
|
+
used: 0,
|
|
1066
|
+
pinned: false,
|
|
1067
|
+
x: 1,
|
|
1068
|
+
y: 1,
|
|
1069
|
+
rowHeight: 0
|
|
1070
|
+
};
|
|
1071
|
+
this.#pages.push(selected);
|
|
1072
|
+
}
|
|
1073
|
+
const plan = selectedPlan;
|
|
1074
|
+
for (const [key, slot] of plan.slots) selected.slots.set(key, slot);
|
|
1075
|
+
selected.x = plan.x;
|
|
1076
|
+
selected.y = plan.y;
|
|
1077
|
+
selected.rowHeight = plan.rowHeight;
|
|
1078
|
+
selected.used = ++this.#clock;
|
|
1079
|
+
this.#current = selected;
|
|
1080
|
+
}
|
|
1081
|
+
pin() {
|
|
1082
|
+
if (this.#current) this.#current.pinned = true;
|
|
1083
|
+
}
|
|
1084
|
+
#plan(glyphs, page) {
|
|
1085
|
+
const slots = /* @__PURE__ */ new Map();
|
|
1086
|
+
let x = page?.x ?? 1;
|
|
1087
|
+
let y = page?.y ?? 1;
|
|
1088
|
+
let rowHeight = page?.rowHeight ?? 0;
|
|
1089
|
+
for (const { key, width, height } of glyphs) {
|
|
1090
|
+
if (page?.slots.has(key)) continue;
|
|
1091
|
+
if (width + 2 > this.size || height + 2 > this.size) return void 0;
|
|
1092
|
+
if (x + width + 1 > this.size) {
|
|
1093
|
+
x = 1;
|
|
1094
|
+
y += rowHeight + 1;
|
|
1095
|
+
rowHeight = 0;
|
|
1096
|
+
}
|
|
1097
|
+
if (y + height + 1 > this.size) return void 0;
|
|
1098
|
+
slots.set(key, { origin: [x, y], width, height });
|
|
1099
|
+
x += width + 1;
|
|
1100
|
+
rowHeight = Math.max(rowHeight, height);
|
|
1101
|
+
}
|
|
1102
|
+
return { slots, x, y, rowHeight };
|
|
1103
|
+
}
|
|
1104
|
+
get(key) {
|
|
1105
|
+
return this.#current?.cache.get(key);
|
|
1106
|
+
}
|
|
1107
|
+
insert(key, width, height, cells) {
|
|
1108
|
+
const page = this.#current;
|
|
1109
|
+
if (!page) throw new Error(`${this.label} has not been prepared`);
|
|
1110
|
+
const slot = page.slots.get(key);
|
|
1111
|
+
if (!slot || slot.width !== width || slot.height !== height) {
|
|
1112
|
+
throw new Error(`${this.label} glyph does not match its prepared reservation`);
|
|
1113
|
+
}
|
|
1114
|
+
const origin = slot.origin;
|
|
1115
|
+
const [x, y] = origin;
|
|
1116
|
+
const location = {
|
|
1117
|
+
u0: x / this.size,
|
|
1118
|
+
v0: y / this.size,
|
|
1119
|
+
u1: (x + width) / this.size,
|
|
1120
|
+
v1: (y + height) / this.size,
|
|
1121
|
+
cells
|
|
1122
|
+
};
|
|
1123
|
+
page.cache.set(key, location);
|
|
1124
|
+
return { location, texture: page.texture, origin };
|
|
1125
|
+
}
|
|
1126
|
+
releaseBatch() {
|
|
1127
|
+
for (const page of this.#pages) page.pinned = false;
|
|
1128
|
+
}
|
|
1129
|
+
destroy() {
|
|
1130
|
+
for (const page of this.#pages) page.texture.destroy();
|
|
1131
|
+
this.#pages.length = 0;
|
|
1132
|
+
this.#current = void 0;
|
|
1133
|
+
}
|
|
1134
|
+
};
|
|
1135
|
+
|
|
691
1136
|
// src/renderers/webgpu-renderer.ts
|
|
692
|
-
var ATLAS_SIZE = 2048;
|
|
693
1137
|
var GEOMETRY_CACHE_LIMIT = 8;
|
|
694
1138
|
var FONT_STACK = "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace";
|
|
695
1139
|
var EFFECT_MODES = {
|
|
@@ -870,88 +1314,37 @@ var DynamicVertexBuffer = class {
|
|
|
870
1314
|
var FallbackGlyphAtlas = class {
|
|
871
1315
|
constructor(device, bindGroupLayout) {
|
|
872
1316
|
this.device = device;
|
|
873
|
-
this
|
|
874
|
-
label: "terminal glyph atlas",
|
|
875
|
-
size: [ATLAS_SIZE, ATLAS_SIZE],
|
|
876
|
-
format: "rgba8unorm",
|
|
877
|
-
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST
|
|
878
|
-
});
|
|
879
|
-
this.#sampler = device.createSampler({ minFilter: "linear", magFilter: "linear" });
|
|
880
|
-
this.#bindGroup = device.createBindGroup({
|
|
881
|
-
label: "terminal glyph atlas bindings",
|
|
882
|
-
layout: bindGroupLayout,
|
|
883
|
-
entries: [
|
|
884
|
-
{ binding: 0, resource: this.#texture.createView() },
|
|
885
|
-
{ binding: 1, resource: this.#sampler }
|
|
886
|
-
]
|
|
887
|
-
});
|
|
1317
|
+
this.pages = new GlyphAtlasPages(device, bindGroupLayout, "terminal diagnostic glyph atlas", "rgba8unorm", 1);
|
|
888
1318
|
}
|
|
889
|
-
|
|
890
|
-
#sampler;
|
|
891
|
-
#bindGroup;
|
|
892
|
-
#cache = /* @__PURE__ */ new Map();
|
|
1319
|
+
pages;
|
|
893
1320
|
#scratch = new OffscreenCanvas(1, 1);
|
|
894
|
-
#x = 1;
|
|
895
|
-
#y = 1;
|
|
896
|
-
#rowHeight = 0;
|
|
897
1321
|
#uploadBytes = 0;
|
|
898
1322
|
#uploadCalls = 0;
|
|
899
|
-
#generation = 0;
|
|
900
1323
|
get bindGroup() {
|
|
901
|
-
return this
|
|
1324
|
+
return this.pages.bindGroup;
|
|
902
1325
|
}
|
|
903
1326
|
get generation() {
|
|
904
|
-
return this
|
|
1327
|
+
return this.pages.generation;
|
|
905
1328
|
}
|
|
906
1329
|
prepare(values, dpr) {
|
|
907
1330
|
const scale = Math.max(1, Math.min(3, dpr));
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
this.#rowHeight = 0;
|
|
916
|
-
if (!this.#fits(all, scale, this.#x, this.#y, this.#rowHeight)) {
|
|
917
|
-
throw new Error("The diagnostic glyph atlas cannot fit the text visible in one frame");
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
#fits(values, scale, startX, startY, startRowHeight) {
|
|
921
|
-
let x = startX;
|
|
922
|
-
let y = startY;
|
|
923
|
-
let rowHeight = startRowHeight;
|
|
924
|
-
for (const value of values) {
|
|
925
|
-
const width = Math.max(1, Math.ceil(CELL_WIDTH * graphemeCellWidth(value) * scale));
|
|
926
|
-
const height = Math.max(1, Math.ceil(LINE_HEIGHT * scale));
|
|
927
|
-
if (x + width + 1 > ATLAS_SIZE) {
|
|
928
|
-
x = 1;
|
|
929
|
-
y += rowHeight + 1;
|
|
930
|
-
rowHeight = 0;
|
|
931
|
-
}
|
|
932
|
-
if (y + height + 1 > ATLAS_SIZE) return false;
|
|
933
|
-
x += width + 1;
|
|
934
|
-
rowHeight = Math.max(rowHeight, height);
|
|
935
|
-
}
|
|
936
|
-
return true;
|
|
1331
|
+
this.pages.prepare(
|
|
1332
|
+
[...new Set(values)].filter((value) => value.trim().length > 0).map((value) => ({
|
|
1333
|
+
key: `${scale.toFixed(2)}:${value}`,
|
|
1334
|
+
width: Math.max(1, Math.ceil(CELL_WIDTH * graphemeCellWidth(value) * scale)),
|
|
1335
|
+
height: Math.max(1, Math.ceil(LINE_HEIGHT * scale))
|
|
1336
|
+
}))
|
|
1337
|
+
);
|
|
937
1338
|
}
|
|
938
1339
|
glyph(grapheme, dpr) {
|
|
939
1340
|
if (grapheme.trim().length === 0) return void 0;
|
|
940
1341
|
const scale = Math.max(1, Math.min(3, dpr));
|
|
941
1342
|
const cells = graphemeCellWidth(grapheme);
|
|
942
1343
|
const key = `${scale.toFixed(2)}:${grapheme}`;
|
|
943
|
-
const cached = this
|
|
1344
|
+
const cached = this.pages.get(key);
|
|
944
1345
|
if (cached) return cached;
|
|
945
1346
|
const width = Math.max(1, Math.ceil(CELL_WIDTH * cells * scale));
|
|
946
1347
|
const height = Math.max(1, Math.ceil(LINE_HEIGHT * scale));
|
|
947
|
-
if (this.#x + width + 1 > ATLAS_SIZE) {
|
|
948
|
-
this.#x = 1;
|
|
949
|
-
this.#y += this.#rowHeight + 1;
|
|
950
|
-
this.#rowHeight = 0;
|
|
951
|
-
}
|
|
952
|
-
if (this.#y + height + 1 > ATLAS_SIZE) {
|
|
953
|
-
throw new Error("Temporary glyph atlas exhausted");
|
|
954
|
-
}
|
|
955
1348
|
this.#scratch.width = width;
|
|
956
1349
|
this.#scratch.height = height;
|
|
957
1350
|
const context = this.#scratch.getContext("2d", { willReadFrequently: true });
|
|
@@ -962,28 +1355,17 @@ var FallbackGlyphAtlas = class {
|
|
|
962
1355
|
context.textBaseline = "top";
|
|
963
1356
|
context.fillText(grapheme, 0, Math.max(0, Math.floor((height - 15 * scale) / 2)));
|
|
964
1357
|
const image = context.getImageData(0, 0, width, height);
|
|
965
|
-
this.
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
);
|
|
1358
|
+
const { location, texture, origin } = this.pages.insert(key, width, height, cells);
|
|
1359
|
+
this.device.queue.writeTexture({ texture, origin }, image.data, { bytesPerRow: width * 4, rowsPerImage: height }, [
|
|
1360
|
+
width,
|
|
1361
|
+
height
|
|
1362
|
+
]);
|
|
971
1363
|
this.#uploadBytes += image.data.byteLength;
|
|
972
1364
|
this.#uploadCalls += 1;
|
|
973
|
-
const location = {
|
|
974
|
-
u0: this.#x / ATLAS_SIZE,
|
|
975
|
-
v0: this.#y / ATLAS_SIZE,
|
|
976
|
-
u1: (this.#x + width) / ATLAS_SIZE,
|
|
977
|
-
v1: (this.#y + height) / ATLAS_SIZE,
|
|
978
|
-
cells
|
|
979
|
-
};
|
|
980
|
-
this.#cache.set(key, location);
|
|
981
|
-
this.#x += width + 1;
|
|
982
|
-
this.#rowHeight = Math.max(this.#rowHeight, height);
|
|
983
1365
|
return location;
|
|
984
1366
|
}
|
|
985
1367
|
destroy() {
|
|
986
|
-
this
|
|
1368
|
+
this.pages.destroy();
|
|
987
1369
|
}
|
|
988
1370
|
uploadMetrics() {
|
|
989
1371
|
return { bytes: this.#uploadBytes, calls: this.#uploadCalls };
|
|
@@ -994,106 +1376,44 @@ var NativeGlyphAtlas = class {
|
|
|
994
1376
|
this.device = device;
|
|
995
1377
|
this.label = label;
|
|
996
1378
|
this.format = format;
|
|
997
|
-
this
|
|
998
|
-
label,
|
|
999
|
-
size: [ATLAS_SIZE, ATLAS_SIZE],
|
|
1000
|
-
format,
|
|
1001
|
-
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST
|
|
1002
|
-
});
|
|
1003
|
-
this.#sampler = device.createSampler({ minFilter: "linear", magFilter: "linear" });
|
|
1004
|
-
this.#bindGroup = device.createBindGroup({
|
|
1005
|
-
label: `${label} bindings`,
|
|
1006
|
-
layout: bindGroupLayout,
|
|
1007
|
-
entries: [
|
|
1008
|
-
{ binding: 0, resource: this.#texture.createView() },
|
|
1009
|
-
{ binding: 1, resource: this.#sampler }
|
|
1010
|
-
]
|
|
1011
|
-
});
|
|
1379
|
+
this.pages = new GlyphAtlasPages(device, bindGroupLayout, label, format, format === "r8unorm" ? 4 : 2);
|
|
1012
1380
|
}
|
|
1013
|
-
|
|
1014
|
-
#sampler;
|
|
1015
|
-
#bindGroup;
|
|
1016
|
-
#cache = /* @__PURE__ */ new Map();
|
|
1017
|
-
#x = 1;
|
|
1018
|
-
#y = 1;
|
|
1019
|
-
#rowHeight = 0;
|
|
1381
|
+
pages;
|
|
1020
1382
|
#uploadBytes = 0;
|
|
1021
1383
|
#uploadCalls = 0;
|
|
1022
|
-
#generation = 0;
|
|
1023
1384
|
get bindGroup() {
|
|
1024
|
-
return this
|
|
1385
|
+
return this.pages.bindGroup;
|
|
1025
1386
|
}
|
|
1026
1387
|
get generation() {
|
|
1027
|
-
return this
|
|
1388
|
+
return this.pages.generation;
|
|
1028
1389
|
}
|
|
1029
1390
|
prepare(definitions) {
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
this.#cache.clear();
|
|
1038
|
-
this.#generation += 1;
|
|
1039
|
-
this.#x = 1;
|
|
1040
|
-
this.#y = 1;
|
|
1041
|
-
this.#rowHeight = 0;
|
|
1042
|
-
if (!this.#fits(all.values(), this.#x, this.#y, this.#rowHeight)) {
|
|
1043
|
-
throw new Error(`${this.label} cannot fit the glyphs visible in one frame`);
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
|
-
#fits(definitions, startX, startY, startRowHeight) {
|
|
1047
|
-
let x = startX;
|
|
1048
|
-
let y = startY;
|
|
1049
|
-
let rowHeight = startRowHeight;
|
|
1050
|
-
for (const { width, height } of definitions) {
|
|
1051
|
-
if (width + 2 > ATLAS_SIZE || height + 2 > ATLAS_SIZE) return false;
|
|
1052
|
-
if (x + width + 1 > ATLAS_SIZE) {
|
|
1053
|
-
x = 1;
|
|
1054
|
-
y += rowHeight + 1;
|
|
1055
|
-
rowHeight = 0;
|
|
1056
|
-
}
|
|
1057
|
-
if (y + height + 1 > ATLAS_SIZE) return false;
|
|
1058
|
-
x += width + 1;
|
|
1059
|
-
rowHeight = Math.max(rowHeight, height);
|
|
1060
|
-
}
|
|
1061
|
-
return true;
|
|
1391
|
+
this.pages.prepare(
|
|
1392
|
+
Array.from(definitions, (definition) => ({
|
|
1393
|
+
key: definition.id,
|
|
1394
|
+
width: definition.width,
|
|
1395
|
+
height: definition.height
|
|
1396
|
+
}))
|
|
1397
|
+
);
|
|
1062
1398
|
}
|
|
1063
1399
|
glyph(definition) {
|
|
1064
|
-
const cached = this
|
|
1400
|
+
const cached = this.pages.get(definition.id);
|
|
1065
1401
|
if (cached) return cached;
|
|
1066
1402
|
const { width, height } = definition;
|
|
1067
|
-
|
|
1068
|
-
this.#x = 1;
|
|
1069
|
-
this.#y += this.#rowHeight + 1;
|
|
1070
|
-
this.#rowHeight = 0;
|
|
1071
|
-
}
|
|
1072
|
-
if (this.#y + height + 1 > ATLAS_SIZE) throw new Error(`${this.label} exhausted`);
|
|
1073
|
-
const pixels = definition.pixels;
|
|
1403
|
+
const { location, texture, origin } = this.pages.insert(definition.id, width, height, 0);
|
|
1074
1404
|
const bytesPerPixel = this.format === "r8unorm" ? 1 : 4;
|
|
1075
1405
|
this.device.queue.writeTexture(
|
|
1076
|
-
{ texture
|
|
1077
|
-
pixels,
|
|
1406
|
+
{ texture, origin },
|
|
1407
|
+
definition.pixels,
|
|
1078
1408
|
{ bytesPerRow: width * bytesPerPixel, rowsPerImage: height },
|
|
1079
1409
|
[width, height]
|
|
1080
1410
|
);
|
|
1081
|
-
this.#uploadBytes += pixels.byteLength;
|
|
1411
|
+
this.#uploadBytes += definition.pixels.byteLength;
|
|
1082
1412
|
this.#uploadCalls += 1;
|
|
1083
|
-
const location = {
|
|
1084
|
-
u0: this.#x / ATLAS_SIZE,
|
|
1085
|
-
v0: this.#y / ATLAS_SIZE,
|
|
1086
|
-
u1: (this.#x + width) / ATLAS_SIZE,
|
|
1087
|
-
v1: (this.#y + height) / ATLAS_SIZE,
|
|
1088
|
-
cells: 0
|
|
1089
|
-
};
|
|
1090
|
-
this.#cache.set(definition.id, location);
|
|
1091
|
-
this.#x += width + 1;
|
|
1092
|
-
this.#rowHeight = Math.max(this.#rowHeight, height);
|
|
1093
1413
|
return location;
|
|
1094
1414
|
}
|
|
1095
1415
|
destroy() {
|
|
1096
|
-
this
|
|
1416
|
+
this.pages.destroy();
|
|
1097
1417
|
}
|
|
1098
1418
|
uploadMetrics() {
|
|
1099
1419
|
return { bytes: this.#uploadBytes, calls: this.#uploadCalls };
|
|
@@ -1120,7 +1440,7 @@ function createCachedVertexBuffer(device, label, data) {
|
|
|
1120
1440
|
device.queue.writeBuffer(buffer, 0, data);
|
|
1121
1441
|
return buffer;
|
|
1122
1442
|
}
|
|
1123
|
-
function geometryCacheKey(view, rows, hasNativeRows, atlasGenerations) {
|
|
1443
|
+
function geometryCacheKey(view, rows, hasNativeRows, atlasGenerations, adaptationKey) {
|
|
1124
1444
|
const selection = view.selection ? `${view.selection.anchor.row},${view.selection.anchor.column},${view.selection.focus.row},${view.selection.focus.column}` : "-";
|
|
1125
1445
|
return [
|
|
1126
1446
|
view.sessionEpoch.toString(),
|
|
@@ -1137,6 +1457,8 @@ function geometryCacheKey(view, rows, hasNativeRows, atlasGenerations) {
|
|
|
1137
1457
|
...view.theme.selectionForeground
|
|
1138
1458
|
].join(","),
|
|
1139
1459
|
view.theme.backgroundOpacityCells ? "opacity-cells" : "opaque-cells",
|
|
1460
|
+
view.theme.minimumContrast ?? 1,
|
|
1461
|
+
adaptationKey,
|
|
1140
1462
|
selection,
|
|
1141
1463
|
effectiveCursorStyle(view) === CursorStyle.Block ? `${view.cursor.x},${view.cursor.y}` : "-",
|
|
1142
1464
|
atlasGenerations.join(",")
|
|
@@ -1351,21 +1673,45 @@ function pushBlockCursorBackground(vertices, view, renderRows, scale, viewportWi
|
|
|
1351
1673
|
viewportHeight
|
|
1352
1674
|
);
|
|
1353
1675
|
}
|
|
1354
|
-
function resolveStyle(style, theme) {
|
|
1676
|
+
function resolveStyle(style, theme, adaptation = null) {
|
|
1355
1677
|
let foreground = style?.foreground ? rgb(style.foreground) : theme.foreground;
|
|
1356
1678
|
let background = style?.background ? rgb(style.background) : null;
|
|
1357
|
-
|
|
1679
|
+
let fill = foreground;
|
|
1680
|
+
if (adaptation) {
|
|
1681
|
+
const source = { foreground: style?.foreground, background: style?.background };
|
|
1682
|
+
if (style?.inverse) {
|
|
1683
|
+
background = source.foreground ? adaptation.surface(source.foreground) : theme.foreground;
|
|
1684
|
+
foreground = source.background ? adaptation.ink(source.background, source.foreground ?? adaptation.base, style?.backgroundPalette) : theme.background;
|
|
1685
|
+
fill = foreground;
|
|
1686
|
+
} else {
|
|
1687
|
+
foreground = source.foreground ? adaptation.ink(source.foreground, source.background ?? adaptation.base, style?.foregroundPalette) : theme.foreground;
|
|
1688
|
+
background = source.background ? adaptation.surface(source.background) : null;
|
|
1689
|
+
fill = source.foreground ? adaptation.surface(source.foreground) : theme.foreground;
|
|
1690
|
+
}
|
|
1691
|
+
} else if (style?.inverse) {
|
|
1358
1692
|
const originalForeground = foreground;
|
|
1359
1693
|
foreground = background ?? theme.background;
|
|
1360
1694
|
background = originalForeground;
|
|
1695
|
+
fill = foreground;
|
|
1361
1696
|
}
|
|
1362
1697
|
if (background && theme.backgroundOpacityCells) {
|
|
1363
1698
|
background = [background[0], background[1], background[2], theme.background[3]];
|
|
1364
1699
|
}
|
|
1365
|
-
|
|
1700
|
+
let text = ensureContrast(
|
|
1701
|
+
foreground,
|
|
1702
|
+
over(background ?? theme.background, theme.background),
|
|
1703
|
+
theme.minimumContrast ?? 1
|
|
1704
|
+
);
|
|
1705
|
+
if (style?.faint) {
|
|
1706
|
+
foreground = [foreground[0], foreground[1], foreground[2], foreground[3] * 0.55];
|
|
1707
|
+
fill = [fill[0], fill[1], fill[2], fill[3] * 0.55];
|
|
1708
|
+
text = [text[0], text[1], text[2], text[3] * 0.55];
|
|
1709
|
+
}
|
|
1366
1710
|
return {
|
|
1367
1711
|
foreground,
|
|
1368
1712
|
background,
|
|
1713
|
+
fill,
|
|
1714
|
+
text,
|
|
1369
1715
|
underline: style?.underline ?? false,
|
|
1370
1716
|
strikethrough: style?.strikethrough ?? false,
|
|
1371
1717
|
invisible: style?.invisible ?? false
|
|
@@ -1668,7 +2014,10 @@ var WebGpuTerminalRenderer = class _WebGpuTerminalRenderer {
|
|
|
1668
2014
|
#monoAtlas;
|
|
1669
2015
|
#colorAtlas;
|
|
1670
2016
|
#fallbackAtlas;
|
|
2017
|
+
#adaptations = /* @__PURE__ */ new Map();
|
|
1671
2018
|
#performanceMeasurementEnabled = false;
|
|
2019
|
+
#submission = { queueSubmits: 0, surfaceIds: [] };
|
|
2020
|
+
#scrollScratch;
|
|
1672
2021
|
static async create(onLost) {
|
|
1673
2022
|
if (!navigator.gpu) throw new Error("WebGPU is unavailable");
|
|
1674
2023
|
const adapter = await navigator.gpu.requestAdapter();
|
|
@@ -1708,7 +2057,11 @@ var WebGpuTerminalRenderer = class _WebGpuTerminalRenderer {
|
|
|
1708
2057
|
cursorBuffer: new DynamicVertexBuffer(this.device, `cursor ${id}`),
|
|
1709
2058
|
geometryCache: /* @__PURE__ */ new Map(),
|
|
1710
2059
|
geometryCandidates: /* @__PURE__ */ new Map(),
|
|
1711
|
-
sceneValid: false
|
|
2060
|
+
sceneValid: false,
|
|
2061
|
+
adaptation: null,
|
|
2062
|
+
sceneHadSelection: false,
|
|
2063
|
+
sceneHadOverhang: false,
|
|
2064
|
+
sceneCursorRow: null
|
|
1712
2065
|
};
|
|
1713
2066
|
this.#surfaces.set(id, surface2);
|
|
1714
2067
|
this.#configure(surface2);
|
|
@@ -1727,6 +2080,10 @@ var WebGpuTerminalRenderer = class _WebGpuTerminalRenderer {
|
|
|
1727
2080
|
surface2.cursorBuffer.destroy();
|
|
1728
2081
|
clearGeometryCache(surface2);
|
|
1729
2082
|
this.#surfaces.delete(id);
|
|
2083
|
+
if (this.#surfaces.size === 0) {
|
|
2084
|
+
this.#scrollScratch?.destroy();
|
|
2085
|
+
this.#scrollScratch = void 0;
|
|
2086
|
+
}
|
|
1730
2087
|
}
|
|
1731
2088
|
resize(id, size) {
|
|
1732
2089
|
const surface2 = this.#surfaces.get(id);
|
|
@@ -1755,7 +2112,7 @@ var WebGpuTerminalRenderer = class _WebGpuTerminalRenderer {
|
|
|
1755
2112
|
label: "terminal scene before Ghostty custom shaders",
|
|
1756
2113
|
size: [Math.max(1, width), Math.max(1, height)],
|
|
1757
2114
|
format: this.format,
|
|
1758
|
-
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
|
|
2115
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC | GPUTextureUsage.COPY_DST
|
|
1759
2116
|
});
|
|
1760
2117
|
}
|
|
1761
2118
|
#createEffectTexture(width, height, suffix) {
|
|
@@ -1865,32 +2222,186 @@ var WebGpuTerminalRenderer = class _WebGpuTerminalRenderer {
|
|
|
1865
2222
|
drawCalls: effectPassCount
|
|
1866
2223
|
};
|
|
1867
2224
|
}
|
|
2225
|
+
/**
|
|
2226
|
+
* Engage, re-key, or release light adaptation for a pane. Any change remaps
|
|
2227
|
+
* every color, so the persistent scene and cached geometry are invalidated.
|
|
2228
|
+
*/
|
|
2229
|
+
#prepareAdaptation(surface2, view) {
|
|
2230
|
+
const base = darkPaintedBase(view, surface2.adaptation !== null);
|
|
2231
|
+
let next = null;
|
|
2232
|
+
if (base) {
|
|
2233
|
+
const key = `${base.join(",")}|${view.theme.background.join(",")}|${view.theme.foreground.join(",")}`;
|
|
2234
|
+
next = this.#adaptations.get(key) ?? null;
|
|
2235
|
+
if (!next) {
|
|
2236
|
+
next = createLightAdaptation(base, view.theme.background, view.theme.foreground);
|
|
2237
|
+
this.#adaptations.set(key, next);
|
|
2238
|
+
if (this.#adaptations.size > 8) this.#adaptations.delete(this.#adaptations.keys().next().value);
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
if ((next?.key ?? null) === (surface2.adaptation?.key ?? null)) return;
|
|
2242
|
+
surface2.adaptation = next;
|
|
2243
|
+
surface2.sceneValid = false;
|
|
2244
|
+
clearGeometryCache(surface2);
|
|
2245
|
+
}
|
|
2246
|
+
submission() {
|
|
2247
|
+
return this.#submission;
|
|
2248
|
+
}
|
|
2249
|
+
#releaseAtlasBatch() {
|
|
2250
|
+
this.#monoAtlas.pages.releaseBatch();
|
|
2251
|
+
this.#colorAtlas.pages.releaseBatch();
|
|
2252
|
+
this.#fallbackAtlas.pages.releaseBatch();
|
|
2253
|
+
}
|
|
2254
|
+
#prepareAtlases(view, renderRows, dpr) {
|
|
2255
|
+
const mono = /* @__PURE__ */ new Map();
|
|
2256
|
+
const color = /* @__PURE__ */ new Map();
|
|
2257
|
+
for (const row of renderRows) {
|
|
2258
|
+
for (const glyph of view.nativeRows[row] ?? []) {
|
|
2259
|
+
const definition = view.glyphDefinitions.get(glyph.glyphId);
|
|
2260
|
+
if (definition) (definition.format === GlyphFormat.Alpha8 ? mono : color).set(definition.id, definition);
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
this.#monoAtlas.prepare(mono.values());
|
|
2264
|
+
this.#colorAtlas.prepare(color.values());
|
|
2265
|
+
const fallback = !view.nativeRows.some((row) => row.length > 0);
|
|
2266
|
+
if (fallback) {
|
|
2267
|
+
this.#fallbackAtlas.prepare(
|
|
2268
|
+
renderRows.flatMap((row) => splitGraphemes(view.rows[row] ?? "")),
|
|
2269
|
+
dpr
|
|
2270
|
+
);
|
|
2271
|
+
}
|
|
2272
|
+
if (mono.size > 0) this.#monoAtlas.pages.pin();
|
|
2273
|
+
if (color.size > 0) this.#colorAtlas.pages.pin();
|
|
2274
|
+
if (fallback && renderRows.length > 0) this.#fallbackAtlas.pages.pin();
|
|
2275
|
+
}
|
|
1868
2276
|
render(id, view) {
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
const
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
2277
|
+
return this.renderBatch([{ id, view }])[0];
|
|
2278
|
+
}
|
|
2279
|
+
#canCopyScroll(surface2, view) {
|
|
2280
|
+
const shift = view.damage?.scrollRows ?? 0;
|
|
2281
|
+
const top = ORIGIN_Y * surface2.dpr;
|
|
2282
|
+
const line = LINE_HEIGHT * surface2.dpr;
|
|
2283
|
+
const count = view.rows.length;
|
|
2284
|
+
return surface2.sceneValid && !view.damage?.full && Number.isSafeInteger(shift) && shift !== 0 && Math.abs(shift) < count && !view.selection && !surface2.sceneHadSelection && Number.isInteger(top) && Number.isInteger(line) && top + count * line <= surface2.canvas.height;
|
|
2285
|
+
}
|
|
2286
|
+
#prepareScrollScratch(entries) {
|
|
2287
|
+
let width = 0, height = 0;
|
|
2288
|
+
for (const { id, view } of entries) {
|
|
2289
|
+
const surface2 = this.#surfaces.get(id);
|
|
2290
|
+
if (!surface2 || !this.#canCopyScroll(surface2, view)) continue;
|
|
2291
|
+
width = Math.max(width, surface2.canvas.width);
|
|
2292
|
+
height = Math.max(height, view.rows.length * LINE_HEIGHT * surface2.dpr);
|
|
2293
|
+
}
|
|
2294
|
+
if (width === 0) return false;
|
|
2295
|
+
if (width * height * 4 > 64 * 1024 * 1024) return false;
|
|
2296
|
+
if (!this.#scrollScratch || this.#scrollScratch.width < width || this.#scrollScratch.height < height) {
|
|
2297
|
+
this.#scrollScratch?.destroy();
|
|
2298
|
+
this.#scrollScratch = this.device.createTexture({
|
|
2299
|
+
label: "terminal shared scroll staging",
|
|
2300
|
+
size: [width, height],
|
|
2301
|
+
format: this.format,
|
|
2302
|
+
usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.COPY_DST
|
|
2303
|
+
});
|
|
2304
|
+
}
|
|
2305
|
+
return true;
|
|
2306
|
+
}
|
|
2307
|
+
#copyScroll(surface2, view, encoder) {
|
|
2308
|
+
const dy = view.damage.scrollRows * LINE_HEIGHT * surface2.dpr;
|
|
2309
|
+
const top = ORIGIN_Y * surface2.dpr;
|
|
2310
|
+
const size = [surface2.canvas.width, view.rows.length * LINE_HEIGHT * surface2.dpr - Math.abs(dy)];
|
|
2311
|
+
encoder.copyTextureToTexture(
|
|
2312
|
+
{ texture: surface2.sceneTexture, origin: [0, top + Math.max(0, dy)] },
|
|
2313
|
+
{ texture: this.#scrollScratch },
|
|
2314
|
+
size
|
|
2315
|
+
);
|
|
2316
|
+
encoder.copyTextureToTexture(
|
|
2317
|
+
{ texture: this.#scrollScratch },
|
|
2318
|
+
{ texture: surface2.sceneTexture, origin: [0, top + Math.max(0, -dy)] },
|
|
2319
|
+
size
|
|
2320
|
+
);
|
|
1876
2321
|
}
|
|
1877
2322
|
renderBatch(entries) {
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
const
|
|
2323
|
+
this.#submission = { queueSubmits: 0, surfaceIds: [] };
|
|
2324
|
+
this.#releaseAtlasBatch();
|
|
2325
|
+
const prepared = entries.map(({ id, view }) => {
|
|
2326
|
+
const surface2 = this.#surfaces.get(id);
|
|
2327
|
+
if (surface2) this.#prepareAdaptation(surface2, view);
|
|
2328
|
+
const overhang = view.damage?.geometryChanged === false && surface2?.sceneValid ? surface2.sceneHadOverhang : view.nativeRows.some((row) => row.some((glyph) => glyph.y < 0 || glyph.y + glyph.height > LINE_HEIGHT));
|
|
2329
|
+
if (view.damage && !view.damage.full && (view.damage.scrollRows || view.damage.rows.size > 0) && (overhang || surface2?.sceneHadOverhang)) {
|
|
2330
|
+
view = { ...view, damage: { full: true, rows: /* @__PURE__ */ new Set(), geometryChanged: true } };
|
|
2331
|
+
}
|
|
2332
|
+
return { id, view, overhang };
|
|
2333
|
+
});
|
|
2334
|
+
const canStageScroll = this.#prepareScrollScratch(prepared);
|
|
2335
|
+
let encoder;
|
|
2336
|
+
let pendingIds = [];
|
|
1881
2337
|
const byId = /* @__PURE__ */ new Map();
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
2338
|
+
const submit = () => {
|
|
2339
|
+
if (encoder && pendingIds.length > 0) {
|
|
2340
|
+
this.device.queue.submit([encoder.finish()]);
|
|
2341
|
+
this.#submission.queueSubmits += 1;
|
|
2342
|
+
this.#submission.surfaceIds.push(...pendingIds);
|
|
2343
|
+
const first = pendingIds.map((id) => byId.get(id)).find((metric) => metric !== void 0);
|
|
2344
|
+
if (first) first.queueSubmits += 1;
|
|
2345
|
+
}
|
|
2346
|
+
encoder = void 0;
|
|
2347
|
+
pendingIds = [];
|
|
2348
|
+
this.#releaseAtlasBatch();
|
|
2349
|
+
};
|
|
2350
|
+
try {
|
|
2351
|
+
for (const { id, view: originalView, overhang } of prepared) {
|
|
2352
|
+
const surface2 = this.#surfaces.get(id);
|
|
2353
|
+
if (!surface2) continue;
|
|
2354
|
+
let view = originalView;
|
|
2355
|
+
const copyScroll = canStageScroll && this.#canCopyScroll(surface2, view);
|
|
2356
|
+
if (view.damage?.scrollRows) {
|
|
2357
|
+
if (copyScroll) {
|
|
2358
|
+
const rows2 = new Set(view.damage.rows);
|
|
2359
|
+
const shift = view.damage.scrollRows;
|
|
2360
|
+
rows2.add(0);
|
|
2361
|
+
rows2.add(view.rows.length - 1);
|
|
2362
|
+
for (let row = 0; row < view.rows.length; row++) {
|
|
2363
|
+
if (row + shift < 0 || row + shift >= view.rows.length) rows2.add(row);
|
|
2364
|
+
}
|
|
2365
|
+
if (surface2.sceneCursorRow !== null) rows2.add(surface2.sceneCursorRow - shift);
|
|
2366
|
+
if (effectiveCursorStyle(view) !== null) rows2.add(view.cursor.y);
|
|
2367
|
+
view = { ...view, damage: { ...view.damage, rows: rows2, geometryChanged: true } };
|
|
2368
|
+
} else {
|
|
2369
|
+
view = { ...view, damage: { full: true, rows: /* @__PURE__ */ new Set(), geometryChanged: true } };
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
const count = Math.max(view.rows.length, view.nativeRows.length, view.nativeStyleRows.length);
|
|
2373
|
+
const { rows } = rowsForDamage(count, view.damage, surface2.sceneValid);
|
|
2374
|
+
try {
|
|
2375
|
+
try {
|
|
2376
|
+
this.#prepareAtlases(view, rows, surface2.dpr);
|
|
2377
|
+
} catch (error) {
|
|
2378
|
+
if (!(error instanceof AtlasBatchFullError) || pendingIds.length === 0) throw error;
|
|
2379
|
+
submit();
|
|
2380
|
+
this.#prepareAtlases(view, rows, surface2.dpr);
|
|
2381
|
+
}
|
|
2382
|
+
} catch (error) {
|
|
2383
|
+
if (!(error instanceof AtlasCapacityError)) throw error;
|
|
2384
|
+
console.error(`[terminal-renderer] cannot render ${id}`, error);
|
|
2385
|
+
continue;
|
|
2386
|
+
}
|
|
2387
|
+
encoder ??= this.device.createCommandEncoder({ label: "terminal frame batch" });
|
|
2388
|
+
if (copyScroll) this.#copyScroll(surface2, view, encoder);
|
|
2389
|
+
byId.set(
|
|
2390
|
+
id,
|
|
2391
|
+
view.damage?.geometryChanged === false ? this.#encodeCachedRender(id, view, encoder) : this.#encodeRender(id, view, encoder)
|
|
2392
|
+
);
|
|
2393
|
+
surface2.sceneHadSelection = view.selection !== null;
|
|
2394
|
+
surface2.sceneHadOverhang = overhang;
|
|
2395
|
+
surface2.sceneCursorRow = effectiveCursorStyle(view) === null ? null : view.cursor.y;
|
|
2396
|
+
pendingIds.push(id);
|
|
2397
|
+
}
|
|
2398
|
+
submit();
|
|
2399
|
+
} finally {
|
|
2400
|
+
this.#releaseAtlasBatch();
|
|
1887
2401
|
}
|
|
1888
|
-
this.device.queue.submit([encoder.finish()]);
|
|
1889
|
-
const firstMetrics = [...byId.values()].find((metrics) => metrics !== void 0);
|
|
1890
|
-
if (firstMetrics) firstMetrics.queueSubmits = 1;
|
|
1891
2402
|
return entries.map(({ id }) => byId.get(id));
|
|
1892
2403
|
}
|
|
1893
|
-
#buildGeometry(view, renderRows, rowCount, fullRedraw, hasNativeRows, scale, viewportWidth, viewportHeight) {
|
|
2404
|
+
#buildGeometry(view, renderRows, rowCount, fullRedraw, hasNativeRows, scale, viewportWidth, viewportHeight, adaptation) {
|
|
1894
2405
|
const rectangleVertices = [];
|
|
1895
2406
|
const glyphVertices = [];
|
|
1896
2407
|
const colorGlyphVertices = [];
|
|
@@ -1899,24 +2410,10 @@ var WebGpuTerminalRenderer = class _WebGpuTerminalRenderer {
|
|
|
1899
2410
|
const styleFor = (styleId) => {
|
|
1900
2411
|
const cached = resolvedStyles.get(styleId);
|
|
1901
2412
|
if (cached) return cached;
|
|
1902
|
-
const resolved = resolveStyle(view.styleDefinitions.get(styleId), view.theme);
|
|
2413
|
+
const resolved = resolveStyle(view.styleDefinitions.get(styleId), view.theme, adaptation);
|
|
1903
2414
|
resolvedStyles.set(styleId, resolved);
|
|
1904
2415
|
return resolved;
|
|
1905
2416
|
};
|
|
1906
|
-
const monoDefinitions = /* @__PURE__ */ new Map();
|
|
1907
|
-
const colorDefinitions = /* @__PURE__ */ new Map();
|
|
1908
|
-
for (const row of renderRows) {
|
|
1909
|
-
for (const glyph of view.nativeRows[row] ?? []) {
|
|
1910
|
-
const definition = view.glyphDefinitions.get(glyph.glyphId);
|
|
1911
|
-
if (!definition) continue;
|
|
1912
|
-
(definition.format === GlyphFormat.Rgba8Premultiplied ? colorDefinitions : monoDefinitions).set(
|
|
1913
|
-
definition.id,
|
|
1914
|
-
definition
|
|
1915
|
-
);
|
|
1916
|
-
}
|
|
1917
|
-
}
|
|
1918
|
-
this.#monoAtlas.prepare(monoDefinitions.values());
|
|
1919
|
-
this.#colorAtlas.prepare(colorDefinitions.values());
|
|
1920
2417
|
if (!fullRedraw) {
|
|
1921
2418
|
for (const row of renderRows) {
|
|
1922
2419
|
const top = row === 0 ? 0 : (ORIGIN_Y + row * LINE_HEIGHT) * scale;
|
|
@@ -1974,12 +2471,6 @@ var WebGpuTerminalRenderer = class _WebGpuTerminalRenderer {
|
|
|
1974
2471
|
}
|
|
1975
2472
|
pushBlockCursorBackground(rectangleVertices, view, renderRows, scale, viewportWidth, viewportHeight);
|
|
1976
2473
|
const selectionInstanceCount = rectangleVertices.length / 10 - backgroundInstanceCount;
|
|
1977
|
-
if (!hasNativeRows) {
|
|
1978
|
-
this.#fallbackAtlas.prepare(
|
|
1979
|
-
renderRows.flatMap((row) => splitGraphemes(view.rows[row] ?? "")),
|
|
1980
|
-
scale
|
|
1981
|
-
);
|
|
1982
|
-
}
|
|
1983
2474
|
if (hasNativeRows) {
|
|
1984
2475
|
for (const row of renderRows) {
|
|
1985
2476
|
const { box: boxCells, block: blockCells } = specialDrawingCells(view.rows[row] ?? "");
|
|
@@ -2010,20 +2501,22 @@ var WebGpuTerminalRenderer = class _WebGpuTerminalRenderer {
|
|
|
2010
2501
|
if (blockElement) {
|
|
2011
2502
|
let backdrop = style.background ?? view.theme.background;
|
|
2012
2503
|
if (isSelected) backdrop = view.theme.selection;
|
|
2504
|
+
const blockForeground = foreground === style.foreground ? style.fill : foreground;
|
|
2013
2505
|
if (pushBlockElement(
|
|
2014
2506
|
rectangleVertices,
|
|
2015
2507
|
blockElement,
|
|
2016
2508
|
instance.cellStart,
|
|
2017
2509
|
row,
|
|
2018
2510
|
scale,
|
|
2019
|
-
over(
|
|
2511
|
+
over(blockForeground, backdrop),
|
|
2020
2512
|
viewportWidth,
|
|
2021
2513
|
viewportHeight
|
|
2022
2514
|
))
|
|
2023
2515
|
continue;
|
|
2024
2516
|
}
|
|
2025
|
-
const
|
|
2026
|
-
const
|
|
2517
|
+
const mono = definition.format === GlyphFormat.Alpha8;
|
|
2518
|
+
const atlas = mono ? this.#monoAtlas : this.#colorAtlas;
|
|
2519
|
+
const vertices = mono ? glyphVertices : colorGlyphVertices;
|
|
2027
2520
|
pushGlyph(
|
|
2028
2521
|
vertices,
|
|
2029
2522
|
(ORIGIN_X + instance.x) * scale,
|
|
@@ -2031,7 +2524,7 @@ var WebGpuTerminalRenderer = class _WebGpuTerminalRenderer {
|
|
|
2031
2524
|
instance.width * scale,
|
|
2032
2525
|
instance.height * scale,
|
|
2033
2526
|
atlas.glyph(definition),
|
|
2034
|
-
foreground,
|
|
2527
|
+
mono && foreground === style.foreground ? style.text : foreground,
|
|
2035
2528
|
viewportWidth,
|
|
2036
2529
|
viewportHeight
|
|
2037
2530
|
);
|
|
@@ -2076,7 +2569,7 @@ var WebGpuTerminalRenderer = class _WebGpuTerminalRenderer {
|
|
|
2076
2569
|
Math.round(rowTop + 16 * scale),
|
|
2077
2570
|
width,
|
|
2078
2571
|
stroke,
|
|
2079
|
-
style.
|
|
2572
|
+
style.text,
|
|
2080
2573
|
viewportWidth,
|
|
2081
2574
|
viewportHeight
|
|
2082
2575
|
);
|
|
@@ -2088,7 +2581,7 @@ var WebGpuTerminalRenderer = class _WebGpuTerminalRenderer {
|
|
|
2088
2581
|
Math.round(rowTop + 9 * scale),
|
|
2089
2582
|
width,
|
|
2090
2583
|
stroke,
|
|
2091
|
-
style.
|
|
2584
|
+
style.text,
|
|
2092
2585
|
viewportWidth,
|
|
2093
2586
|
viewportHeight
|
|
2094
2587
|
);
|
|
@@ -2133,24 +2626,10 @@ var WebGpuTerminalRenderer = class _WebGpuTerminalRenderer {
|
|
|
2133
2626
|
const styleFor = (styleId) => {
|
|
2134
2627
|
const cached = resolvedStyles.get(styleId);
|
|
2135
2628
|
if (cached) return cached;
|
|
2136
|
-
const resolved = resolveStyle(view.styleDefinitions.get(styleId), view.theme);
|
|
2629
|
+
const resolved = resolveStyle(view.styleDefinitions.get(styleId), view.theme, surface2.adaptation);
|
|
2137
2630
|
resolvedStyles.set(styleId, resolved);
|
|
2138
2631
|
return resolved;
|
|
2139
2632
|
};
|
|
2140
|
-
const monoDefinitions = /* @__PURE__ */ new Map();
|
|
2141
|
-
const colorDefinitions = /* @__PURE__ */ new Map();
|
|
2142
|
-
for (const row of renderRows) {
|
|
2143
|
-
for (const glyph of view.nativeRows[row] ?? []) {
|
|
2144
|
-
const definition = view.glyphDefinitions.get(glyph.glyphId);
|
|
2145
|
-
if (!definition) continue;
|
|
2146
|
-
(definition.format === GlyphFormat.Rgba8Premultiplied ? colorDefinitions : monoDefinitions).set(
|
|
2147
|
-
definition.id,
|
|
2148
|
-
definition
|
|
2149
|
-
);
|
|
2150
|
-
}
|
|
2151
|
-
}
|
|
2152
|
-
this.#monoAtlas.prepare(monoDefinitions.values());
|
|
2153
|
-
this.#colorAtlas.prepare(colorDefinitions.values());
|
|
2154
2633
|
if (!fullRedraw) {
|
|
2155
2634
|
for (const row of renderRows) {
|
|
2156
2635
|
const top = row === 0 ? 0 : (ORIGIN_Y + row * LINE_HEIGHT) * scale;
|
|
@@ -2209,12 +2688,6 @@ var WebGpuTerminalRenderer = class _WebGpuTerminalRenderer {
|
|
|
2209
2688
|
pushBlockCursorBackground(rectangleVertices, view, renderRows, scale, viewportWidth, viewportHeight);
|
|
2210
2689
|
const selectionInstanceCount = rectangleVertices.length / 10 - backgroundInstanceCount;
|
|
2211
2690
|
const hasNativeRows = view.nativeRows.some((row) => row.length > 0);
|
|
2212
|
-
if (!hasNativeRows) {
|
|
2213
|
-
this.#fallbackAtlas.prepare(
|
|
2214
|
-
renderRows.flatMap((row) => splitGraphemes(view.rows[row] ?? "")),
|
|
2215
|
-
scale
|
|
2216
|
-
);
|
|
2217
|
-
}
|
|
2218
2691
|
if (hasNativeRows) {
|
|
2219
2692
|
for (const row of renderRows) {
|
|
2220
2693
|
const { box: boxCells, block: blockCells } = specialDrawingCells(view.rows[row] ?? "");
|
|
@@ -2246,7 +2719,8 @@ var WebGpuTerminalRenderer = class _WebGpuTerminalRenderer {
|
|
|
2246
2719
|
if (blockElement) {
|
|
2247
2720
|
let backdrop = style.background ?? view.theme.background;
|
|
2248
2721
|
if (isSelected) backdrop = view.theme.selection;
|
|
2249
|
-
const
|
|
2722
|
+
const blockForeground = foreground === style.foreground ? style.fill : foreground;
|
|
2723
|
+
const blockColor = over(blockForeground, backdrop);
|
|
2250
2724
|
if (pushBlockElement(
|
|
2251
2725
|
rectangleVertices,
|
|
2252
2726
|
blockElement,
|
|
@@ -2259,8 +2733,9 @@ var WebGpuTerminalRenderer = class _WebGpuTerminalRenderer {
|
|
|
2259
2733
|
))
|
|
2260
2734
|
continue;
|
|
2261
2735
|
}
|
|
2262
|
-
const
|
|
2263
|
-
const
|
|
2736
|
+
const mono = definition.format === GlyphFormat.Alpha8;
|
|
2737
|
+
const atlas = mono ? this.#monoAtlas : this.#colorAtlas;
|
|
2738
|
+
const vertices = mono ? glyphVertices : colorGlyphVertices;
|
|
2264
2739
|
pushGlyph(
|
|
2265
2740
|
vertices,
|
|
2266
2741
|
(ORIGIN_X + instance.x) * scale,
|
|
@@ -2268,7 +2743,7 @@ var WebGpuTerminalRenderer = class _WebGpuTerminalRenderer {
|
|
|
2268
2743
|
instance.width * scale,
|
|
2269
2744
|
instance.height * scale,
|
|
2270
2745
|
atlas.glyph(definition),
|
|
2271
|
-
foreground,
|
|
2746
|
+
mono && foreground === style.foreground ? style.text : foreground,
|
|
2272
2747
|
viewportWidth,
|
|
2273
2748
|
viewportHeight
|
|
2274
2749
|
);
|
|
@@ -2313,7 +2788,7 @@ var WebGpuTerminalRenderer = class _WebGpuTerminalRenderer {
|
|
|
2313
2788
|
Math.round(rowTop + 16 * scale),
|
|
2314
2789
|
width,
|
|
2315
2790
|
stroke,
|
|
2316
|
-
style.
|
|
2791
|
+
style.text,
|
|
2317
2792
|
viewportWidth,
|
|
2318
2793
|
viewportHeight
|
|
2319
2794
|
);
|
|
@@ -2325,7 +2800,7 @@ var WebGpuTerminalRenderer = class _WebGpuTerminalRenderer {
|
|
|
2325
2800
|
Math.round(rowTop + 9 * scale),
|
|
2326
2801
|
width,
|
|
2327
2802
|
stroke,
|
|
2328
|
-
style.
|
|
2803
|
+
style.text,
|
|
2329
2804
|
viewportWidth,
|
|
2330
2805
|
viewportHeight
|
|
2331
2806
|
);
|
|
@@ -2515,7 +2990,7 @@ var WebGpuTerminalRenderer = class _WebGpuTerminalRenderer {
|
|
|
2515
2990
|
this.#colorAtlas.generation,
|
|
2516
2991
|
this.#fallbackAtlas.generation
|
|
2517
2992
|
];
|
|
2518
|
-
let key = geometryCacheKey(view, damage.rows, hasNativeRows, generations());
|
|
2993
|
+
let key = geometryCacheKey(view, damage.rows, hasNativeRows, generations(), surface2.adaptation?.key ?? "-");
|
|
2519
2994
|
const cached = surface2.geometryCache.get(key);
|
|
2520
2995
|
const cacheHit = cached !== void 0;
|
|
2521
2996
|
if (cached) {
|
|
@@ -2533,9 +3008,10 @@ var WebGpuTerminalRenderer = class _WebGpuTerminalRenderer {
|
|
|
2533
3008
|
hasNativeRows,
|
|
2534
3009
|
scale,
|
|
2535
3010
|
viewportWidth,
|
|
2536
|
-
viewportHeight
|
|
3011
|
+
viewportHeight,
|
|
3012
|
+
surface2.adaptation
|
|
2537
3013
|
);
|
|
2538
|
-
key = geometryCacheKey(view, damage.rows, hasNativeRows, generations());
|
|
3014
|
+
key = geometryCacheKey(view, damage.rows, hasNativeRows, generations(), surface2.adaptation?.key ?? "-");
|
|
2539
3015
|
const promote = admitted && key === candidateKey;
|
|
2540
3016
|
geometry = {
|
|
2541
3017
|
rectangleBuffer: promote ? createCachedVertexBuffer(this.device, `cached rectangles ${id}`, cpu.rectangleData) : surface2.rectangleBuffer.write(cpu.rectangleData),
|
|
@@ -2693,13 +3169,72 @@ function cursorActivityChangesPixels(cursor, focused, cursorBlinkVisible) {
|
|
|
2693
3169
|
return focused && cursor.visible && cursor.blinking && !cursorBlinkVisible;
|
|
2694
3170
|
}
|
|
2695
3171
|
|
|
3172
|
+
// src/row-damage.ts
|
|
3173
|
+
function sameRow(before, from, after, to) {
|
|
3174
|
+
if (from < 0 || from >= before.rows.length || before.rows[from] !== after.rows[to]) return false;
|
|
3175
|
+
const a = before.nativeRows[from] ?? [], b = after.nativeRows[to] ?? [];
|
|
3176
|
+
if (a.length !== b.length) return false;
|
|
3177
|
+
for (let i = 0; i < a.length; i++) {
|
|
3178
|
+
const x = a[i], y = b[i];
|
|
3179
|
+
if (x.glyphId !== y.glyphId || x.styleId !== y.styleId || x.x !== y.x || x.y !== y.y || x.width !== y.width || x.height !== y.height || x.cellStart !== y.cellStart || x.cellSpan !== y.cellSpan)
|
|
3180
|
+
return false;
|
|
3181
|
+
}
|
|
3182
|
+
const as = before.nativeStyleRows[from] ?? [], bs = after.nativeStyleRows[to] ?? [];
|
|
3183
|
+
if (as.length !== bs.length) return false;
|
|
3184
|
+
return as.every(
|
|
3185
|
+
(x, i) => x.styleId === bs[i].styleId && x.cellStart === bs[i].cellStart && x.cellSpan === bs[i].cellSpan
|
|
3186
|
+
);
|
|
3187
|
+
}
|
|
3188
|
+
function rowDamage(before, after) {
|
|
3189
|
+
const positions = /* @__PURE__ */ new Map();
|
|
3190
|
+
for (let row = 0; row < before.rows.length; row++) {
|
|
3191
|
+
const text = before.rows[row];
|
|
3192
|
+
positions.set(text, positions.has(text) ? -1 : row);
|
|
3193
|
+
}
|
|
3194
|
+
const votes = /* @__PURE__ */ new Map();
|
|
3195
|
+
for (let row = 0; row < after.rows.length; row++) {
|
|
3196
|
+
const from = positions.get(after.rows[row]);
|
|
3197
|
+
if (from !== void 0 && from >= 0) {
|
|
3198
|
+
const delta = from - row;
|
|
3199
|
+
votes.set(delta, (votes.get(delta) ?? 0) + 1);
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
let scrollRows = 0, best = votes.get(0) ?? 0;
|
|
3203
|
+
for (const [delta, count] of votes)
|
|
3204
|
+
if (count > best) {
|
|
3205
|
+
best = count;
|
|
3206
|
+
scrollRows = delta;
|
|
3207
|
+
}
|
|
3208
|
+
const changed = (delta) => after.rows.flatMap((_, row) => sameRow(before, row + delta, after, row) ? [] : [row]);
|
|
3209
|
+
if (scrollRows !== 0) {
|
|
3210
|
+
const rows = changed(scrollRows);
|
|
3211
|
+
if (after.rows.length - rows.length >= Math.ceil(after.rows.length / 2)) return { scrollRows, rows };
|
|
3212
|
+
}
|
|
3213
|
+
return { scrollRows: 0, rows: changed(0) };
|
|
3214
|
+
}
|
|
3215
|
+
function shiftDamage(rows, shift, rowCount) {
|
|
3216
|
+
const moved = [...rows].map((row) => row - shift).filter((row) => row >= 0 && row < rowCount);
|
|
3217
|
+
rows.clear();
|
|
3218
|
+
for (const row of moved) rows.add(row);
|
|
3219
|
+
}
|
|
3220
|
+
function selectionDamageRows(before, after, count) {
|
|
3221
|
+
const rows = /* @__PURE__ */ new Set();
|
|
3222
|
+
for (const selection of [before, after]) {
|
|
3223
|
+
if (!selection) continue;
|
|
3224
|
+
const start = Math.max(0, Math.min(selection.anchor.row, selection.focus.row));
|
|
3225
|
+
const end = Math.min(count - 1, Math.max(selection.anchor.row, selection.focus.row));
|
|
3226
|
+
for (let row = start; row <= end; row++) rows.add(row);
|
|
3227
|
+
}
|
|
3228
|
+
return [...rows];
|
|
3229
|
+
}
|
|
3230
|
+
|
|
2696
3231
|
// src/catalog-budget.ts
|
|
2697
3232
|
function catalogAdmission(resetsCatalog, alreadyUsingFallback, fits) {
|
|
2698
3233
|
if (alreadyUsingFallback && !resetsCatalog) return "text-fallback";
|
|
2699
3234
|
if (fits) return "install";
|
|
2700
3235
|
return resetsCatalog ? "text-fallback" : "request-full";
|
|
2701
3236
|
}
|
|
2702
|
-
function glyphCatalogFits(sessionDefinitions, sessionPixelBytes, sharedDefinitions, sharedPixelBytes, definitions, budget) {
|
|
3237
|
+
function glyphCatalogFits(sessionDefinitions, sessionPixelBytes, sharedDefinitions, sharedPixelBytes, definitions, budget, sharedKey = (id) => id) {
|
|
2703
3238
|
if (definitions.length === 0) return true;
|
|
2704
3239
|
let nextDefinitionCount = sessionDefinitions.size;
|
|
2705
3240
|
let nextSharedDefinitionCount = sharedDefinitions.size;
|
|
@@ -2709,7 +3244,7 @@ function glyphCatalogFits(sessionDefinitions, sessionPixelBytes, sharedDefinitio
|
|
|
2709
3244
|
for (const definition of definitions) {
|
|
2710
3245
|
if (sessionDefinitions.has(definition.id) || pending.has(definition.id)) continue;
|
|
2711
3246
|
pending.add(definition.id);
|
|
2712
|
-
const shared = sharedDefinitions.get(definition.id);
|
|
3247
|
+
const shared = sharedDefinitions.get(sharedKey(definition.id));
|
|
2713
3248
|
const pixelBytes = shared?.definition.pixels.byteLength ?? definition.pixels.byteLength;
|
|
2714
3249
|
nextDefinitionCount += 1;
|
|
2715
3250
|
nextSessionPixelBytes += pixelBytes;
|
|
@@ -3084,10 +3619,6 @@ var socketClosing = 2;
|
|
|
3084
3619
|
function exactStamp(left, right) {
|
|
3085
3620
|
return left !== void 0 && compareRoutedSceneContent(left, right) === 0;
|
|
3086
3621
|
}
|
|
3087
|
-
function copyArrayBuffer(bytes) {
|
|
3088
|
-
const copy = bytes.slice();
|
|
3089
|
-
return copy.buffer;
|
|
3090
|
-
}
|
|
3091
3622
|
function capacitiesFitWithin(accepted, advertised) {
|
|
3092
3623
|
return accepted.connectionCreditBytes <= advertised.connectionCreditBytes && accepted.perActivationCreditBytes <= advertised.perActivationCreditBytes && accepted.stagingBytesPerSession <= advertised.stagingBytesPerSession && accepted.stagingBytesTotal <= advertised.stagingBytesTotal && accepted.maxConcurrentActivations <= advertised.maxConcurrentActivations && accepted.maxConcurrentSeeds <= advertised.maxConcurrentSeeds;
|
|
3093
3624
|
}
|
|
@@ -3395,13 +3926,13 @@ var RoutedFramesTransport = class {
|
|
|
3395
3926
|
}
|
|
3396
3927
|
}
|
|
3397
3928
|
#incremental(connection, activation, header, payload, chargedBytes) {
|
|
3398
|
-
if (payload.byteLength > connection.accepted.protocolLimits.
|
|
3929
|
+
if (payload.byteLength > connection.accepted.protocolLimits.maxUrgentPresentationUnitBytes || !header.resultContent || header.baseContent === void 0 || header.baseContent !== null && !exactStamp(activation.appliedContent, header.baseContent)) {
|
|
3399
3930
|
this.#protocolFailure(connection, header.activationId, chargedBytes, "incremental base mismatch");
|
|
3400
3931
|
return;
|
|
3401
3932
|
}
|
|
3402
3933
|
try {
|
|
3403
3934
|
this.#emit({ type: "frames-state", activationId: header.activationId, state: "applying" });
|
|
3404
|
-
this.#applyFrame(
|
|
3935
|
+
this.#applyFrame(payload, activation.identity, void 0, header.resultContent.sceneEpoch.cellBootId);
|
|
3405
3936
|
activation.appliedContent = header.resultContent;
|
|
3406
3937
|
this.#returnCredit(connection, header.activationId, chargedBytes);
|
|
3407
3938
|
this.#sceneApplied(connection, activation);
|
|
@@ -3481,7 +4012,12 @@ var RoutedFramesTransport = class {
|
|
|
3481
4012
|
}
|
|
3482
4013
|
try {
|
|
3483
4014
|
this.#emit({ type: "frames-state", activationId: header.activationId, state: "applying" });
|
|
3484
|
-
this.#applyFrame(
|
|
4015
|
+
this.#applyFrame(
|
|
4016
|
+
staging.bytes.buffer,
|
|
4017
|
+
activation.identity,
|
|
4018
|
+
staging.targetLayout,
|
|
4019
|
+
staging.resultContent.sceneEpoch.cellBootId
|
|
4020
|
+
);
|
|
3485
4021
|
activation.appliedContent = staging.resultContent;
|
|
3486
4022
|
this.#releaseTransfer(activation);
|
|
3487
4023
|
this.#sceneApplied(connection, activation);
|
|
@@ -3686,7 +4222,10 @@ var MAX_PERFORMANCE_SAMPLES = 1e5;
|
|
|
3686
4222
|
var FRAME_CREDIT_BATCH_BYTES = 4 * 1024 * 1024;
|
|
3687
4223
|
var FRAME_CREDIT_MAX_DELAY_MS = 8;
|
|
3688
4224
|
var snapshots = /* @__PURE__ */ new Map();
|
|
4225
|
+
var drawnSessions = /* @__PURE__ */ new Set();
|
|
4226
|
+
var routedMouseTracking = /* @__PURE__ */ new Map();
|
|
3689
4227
|
var sharedGlyphDefinitions = /* @__PURE__ */ new Map();
|
|
4228
|
+
var nextRendererGlyphId = 1;
|
|
3690
4229
|
var sharedGlyphPixelBytes = 0;
|
|
3691
4230
|
var retainedStyleDefinitions = 0;
|
|
3692
4231
|
var surfaces = /* @__PURE__ */ new Map();
|
|
@@ -3882,6 +4421,7 @@ function emptySessionSnapshot() {
|
|
|
3882
4421
|
nativeRows: [],
|
|
3883
4422
|
nativeStyleRows: [],
|
|
3884
4423
|
glyphDefinitions: /* @__PURE__ */ new Map(),
|
|
4424
|
+
glyphNamespace: "",
|
|
3885
4425
|
glyphPixelBytes: 0,
|
|
3886
4426
|
styleDefinitions: /* @__PURE__ */ new Map(),
|
|
3887
4427
|
rowRevisions: [],
|
|
@@ -3905,29 +4445,34 @@ function snapshot(sessionHandle) {
|
|
|
3905
4445
|
}
|
|
3906
4446
|
function clearSessionCatalog(session) {
|
|
3907
4447
|
for (const glyphId of session.glyphDefinitions.keys()) {
|
|
3908
|
-
const
|
|
4448
|
+
const key = `${session.glyphNamespace}:${glyphId}`;
|
|
4449
|
+
const shared = sharedGlyphDefinitions.get(key);
|
|
3909
4450
|
if (!shared) continue;
|
|
3910
4451
|
shared.references -= 1;
|
|
3911
4452
|
if (shared.references <= 0) {
|
|
3912
|
-
sharedGlyphDefinitions.delete(
|
|
4453
|
+
sharedGlyphDefinitions.delete(key);
|
|
3913
4454
|
sharedGlyphPixelBytes -= shared.definition.pixels.byteLength;
|
|
3914
4455
|
}
|
|
3915
4456
|
}
|
|
3916
|
-
session.glyphDefinitions
|
|
4457
|
+
session.glyphDefinitions = /* @__PURE__ */ new Map();
|
|
3917
4458
|
session.glyphPixelBytes = 0;
|
|
3918
4459
|
retainedStyleDefinitions -= session.styleDefinitions.size;
|
|
3919
4460
|
session.styleDefinitions.clear();
|
|
3920
4461
|
}
|
|
3921
|
-
function installGlyphDefinitions(session, definitions) {
|
|
4462
|
+
function installGlyphDefinitions(session, definitions, previousDefinitions) {
|
|
3922
4463
|
for (const decoded of definitions) {
|
|
3923
4464
|
if (session.glyphDefinitions.has(decoded.id)) continue;
|
|
3924
|
-
|
|
4465
|
+
const key = `${session.glyphNamespace}:${decoded.id}`;
|
|
4466
|
+
let shared = sharedGlyphDefinitions.get(key);
|
|
3925
4467
|
const pixelBytes = shared?.definition.pixels.byteLength ?? decoded.pixels.byteLength;
|
|
3926
4468
|
if (shared) {
|
|
3927
4469
|
shared.references += 1;
|
|
3928
4470
|
} else {
|
|
3929
|
-
shared = {
|
|
3930
|
-
|
|
4471
|
+
shared = {
|
|
4472
|
+
definition: { ...decoded, id: previousDefinitions?.get(decoded.id)?.id ?? nextRendererGlyphId++ },
|
|
4473
|
+
references: 1
|
|
4474
|
+
};
|
|
4475
|
+
sharedGlyphDefinitions.set(key, shared);
|
|
3931
4476
|
sharedGlyphPixelBytes += decoded.pixels.byteLength;
|
|
3932
4477
|
}
|
|
3933
4478
|
session.glyphDefinitions.set(decoded.id, shared.definition);
|
|
@@ -3945,6 +4490,10 @@ function dropSessionSnapshot(sessionHandle) {
|
|
|
3945
4490
|
const session = snapshots.get(sessionHandle);
|
|
3946
4491
|
if (session) clearSessionCatalog(session);
|
|
3947
4492
|
snapshots.delete(sessionHandle);
|
|
4493
|
+
drawnSessions.delete(sessionHandle);
|
|
4494
|
+
}
|
|
4495
|
+
function rowShowsContent(row) {
|
|
4496
|
+
return row.glyphs.length > 0 || /\S/.test(row.text);
|
|
3948
4497
|
}
|
|
3949
4498
|
function defaults(sessionHandle) {
|
|
3950
4499
|
let value = presentationDefaults.get(sessionHandle);
|
|
@@ -4069,6 +4618,7 @@ async function recoverDevice(info) {
|
|
|
4069
4618
|
try {
|
|
4070
4619
|
const next = await rendererPromise;
|
|
4071
4620
|
renderer = next;
|
|
4621
|
+
next.setPerformanceMeasurementEnabled?.(performanceMeasurement !== void 0);
|
|
4072
4622
|
recoveryAttempts = 0;
|
|
4073
4623
|
recovering = false;
|
|
4074
4624
|
for (const [id, mount2] of mounts) {
|
|
@@ -4119,7 +4669,10 @@ function scheduleShaderAnimation() {
|
|
|
4119
4669
|
}
|
|
4120
4670
|
async function flush() {
|
|
4121
4671
|
const backend = await ensureRenderer();
|
|
4122
|
-
const ids = [...dirty].filter((id) =>
|
|
4672
|
+
const ids = [...dirty].filter((id) => {
|
|
4673
|
+
const size = mounts.get(id)?.size;
|
|
4674
|
+
return !occluded.has(id) && size !== void 0 && size.width > 0 && size.height > 0;
|
|
4675
|
+
});
|
|
4123
4676
|
if (performanceMeasurement && ids.length > 0) {
|
|
4124
4677
|
performanceMeasurement.scheduling.flushes += 1;
|
|
4125
4678
|
appendPerformanceSample(performanceMeasurement.scheduling.panesPerFlush, ids.length);
|
|
@@ -4139,14 +4692,23 @@ async function flush() {
|
|
|
4139
4692
|
const active = performanceMeasurement;
|
|
4140
4693
|
const beforeRender = active ? performance.now() : 0;
|
|
4141
4694
|
const metrics = backend.renderBatch ? backend.renderBatch(entries) : entries.map(({ id, view }) => backend.render(id, view));
|
|
4142
|
-
|
|
4695
|
+
const submission = backend.submission?.();
|
|
4696
|
+
if (submission) {
|
|
4697
|
+
lifetimeRenderer.queueSubmits += submission.queueSubmits;
|
|
4698
|
+
lifetimeRenderer.presents += submission.surfaceIds.length;
|
|
4699
|
+
} else {
|
|
4700
|
+
for (const metric of metrics) recordLifetimeRenderMetrics(metric ?? emptyRenderMetrics());
|
|
4701
|
+
}
|
|
4702
|
+
const submitted = submission ? new Set(submission.surfaceIds) : void 0;
|
|
4143
4703
|
const renderedAt = active ? performance.now() : 0;
|
|
4144
4704
|
for (const { id } of entries) {
|
|
4705
|
+
if (submitted && !submitted.has(id)) continue;
|
|
4145
4706
|
const damage = surfaces.get(id)?.damage;
|
|
4146
4707
|
if (!damage) continue;
|
|
4147
4708
|
damage.full = false;
|
|
4148
4709
|
damage.rows.clear();
|
|
4149
4710
|
damage.geometryChanged = false;
|
|
4711
|
+
delete damage.scrollRows;
|
|
4150
4712
|
}
|
|
4151
4713
|
if (active && performanceMeasurement === active) {
|
|
4152
4714
|
active.scheduling.renderCalls += entries.length;
|
|
@@ -4185,6 +4747,7 @@ function invalidateFull(id) {
|
|
|
4185
4747
|
damage.full = true;
|
|
4186
4748
|
damage.rows.clear();
|
|
4187
4749
|
damage.geometryChanged = true;
|
|
4750
|
+
delete damage.scrollRows;
|
|
4188
4751
|
markDirty(id);
|
|
4189
4752
|
}
|
|
4190
4753
|
function invalidateRows(id, rows, geometryChanged = false) {
|
|
@@ -4206,15 +4769,35 @@ function invalidateRows(id, rows, geometryChanged = false) {
|
|
|
4206
4769
|
function invalidateSessionFull(sessionHandle) {
|
|
4207
4770
|
for (const surfaceId of surfaceIdsForSession(sessionHandle)) invalidateFull(surfaceId);
|
|
4208
4771
|
}
|
|
4772
|
+
function invalidateScroll(id, shift, rows, rowCount) {
|
|
4773
|
+
const value = surfaces.get(id);
|
|
4774
|
+
if (!value) return;
|
|
4775
|
+
const damage = value.damage;
|
|
4776
|
+
if (!partialRenderingEnabled || damage.full || value.selection || Math.abs((damage.scrollRows ?? 0) + shift) >= rowCount) {
|
|
4777
|
+
invalidateFull(id);
|
|
4778
|
+
return;
|
|
4779
|
+
}
|
|
4780
|
+
shiftDamage(damage.rows, shift, rowCount);
|
|
4781
|
+
damage.scrollRows = (damage.scrollRows ?? 0) + shift;
|
|
4782
|
+
invalidateRows(id, rows, true);
|
|
4783
|
+
}
|
|
4784
|
+
function changeSelection(id, selection) {
|
|
4785
|
+
const value = surfaces.get(id);
|
|
4786
|
+
if (!value || value.selection === null && selection === null) return;
|
|
4787
|
+
const rows = selectionDamageRows(value.selection, selection, snapshots.get(value.sessionHandle)?.rows.length ?? 0);
|
|
4788
|
+
value.selection = selection;
|
|
4789
|
+
if (value.damage.scrollRows) invalidateFull(id);
|
|
4790
|
+
else invalidateRows(id, rows, true);
|
|
4791
|
+
}
|
|
4209
4792
|
function invalidateSessionRows(sessionHandle, rows, geometryChanged = false) {
|
|
4210
4793
|
const damagedRows = [...rows];
|
|
4211
4794
|
for (const surfaceId of surfaceIdsForSession(sessionHandle)) invalidateRows(surfaceId, damagedRows, geometryChanged);
|
|
4212
4795
|
}
|
|
4213
|
-
async function mount(surfaceId, sessionHandle, canvas) {
|
|
4796
|
+
async function mount(surfaceId, sessionHandle, canvas, size) {
|
|
4214
4797
|
if (mounts.has(surfaceId)) renderer?.unmount(surfaceId);
|
|
4215
4798
|
snapshot(sessionHandle);
|
|
4216
4799
|
surface(surfaceId, sessionHandle);
|
|
4217
|
-
const entry = { canvas, size
|
|
4800
|
+
const entry = { canvas, size, sessionHandle };
|
|
4218
4801
|
mounts.set(surfaceId, entry);
|
|
4219
4802
|
const backend = await ensureRenderer();
|
|
4220
4803
|
if (mounts.get(surfaceId) !== entry) return;
|
|
@@ -4223,7 +4806,7 @@ async function mount(surfaceId, sessionHandle, canvas) {
|
|
|
4223
4806
|
invalidateFull(surfaceId);
|
|
4224
4807
|
scheduleShaderAnimation();
|
|
4225
4808
|
}
|
|
4226
|
-
function applyFrame(packet, expectedIdentity, expectedLayout) {
|
|
4809
|
+
function applyFrame(packet, expectedIdentity, expectedLayout, glyphNamespace) {
|
|
4227
4810
|
const active = performanceMeasurement;
|
|
4228
4811
|
const applyStarted = active ? performance.now() : 0;
|
|
4229
4812
|
lifetimeFrames.received += 1;
|
|
@@ -4320,11 +4903,13 @@ function applyFrame(packet, expectedIdentity, expectedLayout) {
|
|
|
4320
4903
|
throw new Error("routed transfer row section is not a full snapshot");
|
|
4321
4904
|
}
|
|
4322
4905
|
const glyphDefinitions = glyphSection ? decodeGlyphDefinitions(glyphSection) : NO_GLYPH_DEFINITIONS;
|
|
4323
|
-
const
|
|
4906
|
+
const paletteSection = frame.sections.find((candidate) => candidate.kind === SectionKind.StylePalette);
|
|
4907
|
+
const styleDefinitions = styleSection ? decodeStyleDefinitions(styleSection, paletteSection) : NO_STYLE_DEFINITIONS;
|
|
4324
4908
|
const replacements = decodeRowReplacements(rowSection);
|
|
4325
4909
|
for (const replacement of replacements) {
|
|
4326
4910
|
if (replacement.row >= frame.rows) throw new RangeError("Row replacement exceeds viewport");
|
|
4327
4911
|
}
|
|
4912
|
+
const firstDrawn = !drawnSessions.has(id) && replacements.some(rowShowsContent);
|
|
4328
4913
|
const linkSection = frame.sections.find((candidate) => candidate.kind === SectionKind.LinkTargets);
|
|
4329
4914
|
const links = linkSection ? decodeLinkTargets(linkSection, frame.cols, frame.rows) : [];
|
|
4330
4915
|
const nextCursor = decodeCursorState(cursorSection);
|
|
@@ -4345,13 +4930,21 @@ function applyFrame(packet, expectedIdentity, expectedLayout) {
|
|
|
4345
4930
|
throw new Error("transfer scrollback layout does not match TRF1");
|
|
4346
4931
|
}
|
|
4347
4932
|
if (active) active.frames.glyphDefinitions += glyphDefinitions.length;
|
|
4348
|
-
const
|
|
4933
|
+
const namespace = glyphNamespace === void 0 ? `session:${id}:${frame.sessionEpoch}` : `cell:${glyphNamespace}`;
|
|
4934
|
+
const resetsCatalog = replacingScene || changedSession || completingResync || catalogReset || previous.glyphNamespace !== namespace;
|
|
4935
|
+
const styleChanged = styleDefinitions.some((definition) => {
|
|
4936
|
+
const old = previous.styleDefinitions.get(definition.id);
|
|
4937
|
+
return old !== void 0 && JSON.stringify(old) !== JSON.stringify(definition);
|
|
4938
|
+
});
|
|
4939
|
+
const canReuseRows = !resetsCatalog && !styleChanged && previous.cols === frame.cols && previous.rows.length === frame.rows && previous.layoutEpoch === frame.layoutEpoch;
|
|
4349
4940
|
const wasCatalogFallback = previous.catalogFallback;
|
|
4350
4941
|
if (resetsCatalog) {
|
|
4942
|
+
const previousDefinitions = previous.glyphNamespace === namespace ? previous.glyphDefinitions : void 0;
|
|
4351
4943
|
previous.rows = [];
|
|
4352
4944
|
previous.nativeRows = [];
|
|
4353
4945
|
previous.nativeStyleRows = [];
|
|
4354
4946
|
clearSessionCatalog(previous);
|
|
4947
|
+
previous.glyphNamespace = namespace;
|
|
4355
4948
|
previous.rowRevisions = [];
|
|
4356
4949
|
const catalogFits = glyphCatalogFits(
|
|
4357
4950
|
previous.glyphDefinitions,
|
|
@@ -4359,7 +4952,8 @@ function applyFrame(packet, expectedIdentity, expectedLayout) {
|
|
|
4359
4952
|
sharedGlyphDefinitions,
|
|
4360
4953
|
sharedGlyphPixelBytes,
|
|
4361
4954
|
glyphDefinitions,
|
|
4362
|
-
GLYPH_CATALOG_BUDGET
|
|
4955
|
+
GLYPH_CATALOG_BUDGET,
|
|
4956
|
+
(glyphId) => `${namespace}:${glyphId}`
|
|
4363
4957
|
) && definitionCatalogFits(
|
|
4364
4958
|
previous.styleDefinitions,
|
|
4365
4959
|
styleDefinitions,
|
|
@@ -4370,7 +4964,7 @@ function applyFrame(packet, expectedIdentity, expectedLayout) {
|
|
|
4370
4964
|
const admission = catalogAdmission(true, wasCatalogFallback, catalogFits);
|
|
4371
4965
|
previous.catalogFallback = admission === "text-fallback";
|
|
4372
4966
|
if (admission === "install") {
|
|
4373
|
-
installGlyphDefinitions(previous, glyphDefinitions);
|
|
4967
|
+
installGlyphDefinitions(previous, glyphDefinitions, previousDefinitions);
|
|
4374
4968
|
installStyleDefinitions(previous, styleDefinitions);
|
|
4375
4969
|
} else if (!wasCatalogFallback) {
|
|
4376
4970
|
postToRenderer({
|
|
@@ -4386,7 +4980,8 @@ function applyFrame(packet, expectedIdentity, expectedLayout) {
|
|
|
4386
4980
|
sharedGlyphDefinitions,
|
|
4387
4981
|
sharedGlyphPixelBytes,
|
|
4388
4982
|
glyphDefinitions,
|
|
4389
|
-
GLYPH_CATALOG_BUDGET
|
|
4983
|
+
GLYPH_CATALOG_BUDGET,
|
|
4984
|
+
(glyphId) => `${namespace}:${glyphId}`
|
|
4390
4985
|
) && definitionCatalogFits(
|
|
4391
4986
|
previous.styleDefinitions,
|
|
4392
4987
|
styleDefinitions,
|
|
@@ -4426,7 +5021,7 @@ function applyFrame(packet, expectedIdentity, expectedLayout) {
|
|
|
4426
5021
|
const nativeRows = fullRows ? Array.from({ length: frame.rows }, () => []) : previous.nativeRows.slice();
|
|
4427
5022
|
const nativeStyleRows = fullRows ? Array.from({ length: frame.rows }, () => []) : previous.nativeStyleRows.slice();
|
|
4428
5023
|
const rowRevisions = fullRows ? Array(frame.rows).fill(0n) : previous.rowRevisions.slice();
|
|
4429
|
-
|
|
5024
|
+
let damagedRows = [];
|
|
4430
5025
|
if (active) active.frames.rowsDecoded += replacements.length;
|
|
4431
5026
|
for (const replacement of replacements) {
|
|
4432
5027
|
if (replacement.revision < (rowRevisions[replacement.row] ?? 0n)) continue;
|
|
@@ -4436,12 +5031,24 @@ function applyFrame(packet, expectedIdentity, expectedLayout) {
|
|
|
4436
5031
|
rowRevisions[replacement.row] = replacement.revision;
|
|
4437
5032
|
damagedRows.push(replacement.row);
|
|
4438
5033
|
}
|
|
5034
|
+
let scrollRows = 0;
|
|
5035
|
+
if (fullRows && canReuseRows) {
|
|
5036
|
+
const damage = rowDamage(previous, { rows, nativeRows, nativeStyleRows });
|
|
5037
|
+
scrollRows = damage.scrollRows;
|
|
5038
|
+
damagedRows = damage.rows;
|
|
5039
|
+
const changed = new Set(damagedRows);
|
|
5040
|
+
for (let row = 0; row < rows.length; row++) {
|
|
5041
|
+
if (changed.has(row)) continue;
|
|
5042
|
+
nativeRows[row] = previous.nativeRows[row + scrollRows];
|
|
5043
|
+
nativeStyleRows[row] = previous.nativeStyleRows[row + scrollRows];
|
|
5044
|
+
}
|
|
5045
|
+
}
|
|
4439
5046
|
previous.cols = frame.cols;
|
|
4440
5047
|
previous.rows = rows;
|
|
4441
5048
|
previous.nativeRows = nativeRows;
|
|
4442
5049
|
previous.nativeStyleRows = nativeStyleRows;
|
|
4443
5050
|
previous.rowRevisions = rowRevisions;
|
|
4444
|
-
const geometryChanged = damagedRows.length > 0;
|
|
5051
|
+
const geometryChanged = damagedRows.length > 0 || scrollRows !== 0;
|
|
4445
5052
|
const previousCursor = previous.cursor;
|
|
4446
5053
|
const cursorChanged = nextCursor.x !== previousCursor.x || nextCursor.y !== previousCursor.y || nextCursor.visible !== previousCursor.visible || nextCursor.style !== previousCursor.style || nextCursor.blinking !== previousCursor.blinking;
|
|
4447
5054
|
previous.cursor = nextCursor;
|
|
@@ -4461,12 +5068,14 @@ function applyFrame(packet, expectedIdentity, expectedLayout) {
|
|
|
4461
5068
|
if (clipboardText !== void 0) postToRenderer({ type: "clipboard-write", text: clipboardText });
|
|
4462
5069
|
if (scrollbarChanged && scrollbar) postToRenderer({ type: "scrollbar-state", sessionHandle: id, scrollbar });
|
|
4463
5070
|
if (completingResync) postToRenderer({ type: "frame-resync-complete", sessionHandle: id });
|
|
4464
|
-
const requiresFullRedraw =
|
|
4465
|
-
if (!requiresFullRedraw && cursorChanged
|
|
5071
|
+
const requiresFullRedraw = !canReuseRows;
|
|
5072
|
+
if (!requiresFullRedraw && (cursorChanged || scrollRows !== 0))
|
|
5073
|
+
damagedRows.push(previousCursor.y - scrollRows, nextCursor.y);
|
|
4466
5074
|
const hasRowDamage = damagedRows.length > 0;
|
|
4467
5075
|
for (const surfaceId of surfaceIdsForSession(id)) {
|
|
4468
5076
|
if (cursorChanged) scheduleCursorBlink(surfaceId, true);
|
|
4469
5077
|
if (requiresFullRedraw) invalidateFull(surfaceId);
|
|
5078
|
+
else if (scrollRows !== 0) invalidateScroll(surfaceId, scrollRows, damagedRows, rows.length);
|
|
4470
5079
|
else if (hasRowDamage) invalidateRows(surfaceId, damagedRows, geometryChanged);
|
|
4471
5080
|
}
|
|
4472
5081
|
postToRenderer({
|
|
@@ -4479,6 +5088,17 @@ function applyFrame(packet, expectedIdentity, expectedLayout) {
|
|
|
4479
5088
|
// recovered screen apart from a partial update of the stale one.
|
|
4480
5089
|
fullSnapshot: fullFrame
|
|
4481
5090
|
});
|
|
5091
|
+
if (expectedIdentity) {
|
|
5092
|
+
const mouseTracking = (frame.flags & FrameFlag.MouseTracking) !== 0;
|
|
5093
|
+
if (routedMouseTracking.get(id) !== mouseTracking) {
|
|
5094
|
+
routedMouseTracking.set(id, mouseTracking);
|
|
5095
|
+
postToRenderer({ type: "terminal-modes", sessionHandle: id, mouseTracking });
|
|
5096
|
+
}
|
|
5097
|
+
}
|
|
5098
|
+
if (firstDrawn) {
|
|
5099
|
+
drawnSessions.add(id);
|
|
5100
|
+
postToRenderer({ type: "session-drawn", sessionHandle: id });
|
|
5101
|
+
}
|
|
4482
5102
|
lifetimeFrames.applies += 1;
|
|
4483
5103
|
counters.applies += 1;
|
|
4484
5104
|
if (active) appendPerformanceSample(active.samples.frameApplyMs, performance.now() - applyStarted);
|
|
@@ -4490,8 +5110,8 @@ function applyFrame(packet, expectedIdentity, expectedLayout) {
|
|
|
4490
5110
|
};
|
|
4491
5111
|
}
|
|
4492
5112
|
var routedFrames = new RoutedFramesTransport({
|
|
4493
|
-
applyFrame: (packet, identity, expectedLayout) => {
|
|
4494
|
-
const applied = applyFrame(packet, identity, expectedLayout);
|
|
5113
|
+
applyFrame: (packet, identity, expectedLayout, glyphNamespace) => {
|
|
5114
|
+
const applied = applyFrame(packet, identity, expectedLayout, glyphNamespace);
|
|
4495
5115
|
if (!applied) throw new Error("routed TRF1 frame was not applied");
|
|
4496
5116
|
return applied;
|
|
4497
5117
|
},
|
|
@@ -4505,7 +5125,7 @@ self.onmessage = (event) => {
|
|
|
4505
5125
|
const message = event.data;
|
|
4506
5126
|
try {
|
|
4507
5127
|
if (message.type === "mount") {
|
|
4508
|
-
void mount(message.surfaceId, message.sessionHandle, message.canvas).catch((error) => {
|
|
5128
|
+
void mount(message.surfaceId, message.sessionHandle, message.canvas, message.size).catch((error) => {
|
|
4509
5129
|
console.error("[terminal-renderer] mount failed", error);
|
|
4510
5130
|
});
|
|
4511
5131
|
} else if (message.type === "renderer-config") {
|
|
@@ -4588,13 +5208,12 @@ self.onmessage = (event) => {
|
|
|
4588
5208
|
scheduleShaderAnimation();
|
|
4589
5209
|
} else if (message.type === "selection") {
|
|
4590
5210
|
if (message.surfaceId) {
|
|
4591
|
-
surface(message.surfaceId, message.sessionHandle)
|
|
4592
|
-
|
|
5211
|
+
surface(message.surfaceId, message.sessionHandle);
|
|
5212
|
+
changeSelection(message.surfaceId, message.selection);
|
|
4593
5213
|
} else {
|
|
4594
5214
|
defaults(message.sessionHandle).selection = message.selection;
|
|
4595
5215
|
for (const surfaceId of surfaceIdsForSession(message.sessionHandle)) {
|
|
4596
|
-
|
|
4597
|
-
invalidateFull(surfaceId);
|
|
5216
|
+
changeSelection(surfaceId, message.selection);
|
|
4598
5217
|
}
|
|
4599
5218
|
}
|
|
4600
5219
|
} else if (message.type === "visibility") {
|
|
@@ -4660,6 +5279,7 @@ self.onmessage = (event) => {
|
|
|
4660
5279
|
} else if (message.type === "performance-counters") {
|
|
4661
5280
|
postToRenderer({ type: "performance-counters", requestId: message.requestId, snapshot: counterSnapshot() });
|
|
4662
5281
|
} else if (message.type === "routed-frames-attach") {
|
|
5282
|
+
routedMouseTracking.delete(message.request.sessionHandle);
|
|
4663
5283
|
routedFrames.attach(message.request);
|
|
4664
5284
|
} else if (message.type === "routed-frames-detach") {
|
|
4665
5285
|
routedFrames.detach(message.activationId);
|