mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 08:02:28 +00:00
fix(terminal): bound cached contrast color pairs
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -1,3 +1,50 @@
|
||||
diff --git a/src/browser/ColorContrastCache.ts b/src/browser/ColorContrastCache.ts
|
||||
index fdcd9d133199a6cd6ba9bea9606a02c03ad03b3d..0558a8873f680e8fb2a27cc833c49c5ba658e1dd 100644
|
||||
--- a/src/browser/ColorContrastCache.ts
|
||||
+++ b/src/browser/ColorContrastCache.ts
|
||||
@@ -7,11 +7,17 @@ import { IColorContrastCache } from './Types';
|
||||
import { IColor } from '../common/Types';
|
||||
import { TwoKeyMap } from '../common/MultiKeyMap';
|
||||
|
||||
+const CONTRAST_CACHE_MAX_ENTRIES = 4096;
|
||||
+
|
||||
export class ColorContrastCache implements IColorContrastCache {
|
||||
private _color: TwoKeyMap</* bg */number, /* fg */number, IColor | null> = new TwoKeyMap();
|
||||
private _css: TwoKeyMap</* bg */number, /* fg */number, string | null> = new TwoKeyMap();
|
||||
+ private _entryCount = 0;
|
||||
|
||||
public setCss(bg: number, fg: number, value: string | null): void {
|
||||
+ if (this._css.get(bg, fg) === undefined) {
|
||||
+ this._admitNewEntry();
|
||||
+ }
|
||||
this._css.set(bg, fg, value);
|
||||
}
|
||||
|
||||
@@ -20,6 +26,9 @@ export class ColorContrastCache implements IColorContrastCache {
|
||||
}
|
||||
|
||||
public setColor(bg: number, fg: number, value: IColor | null): void {
|
||||
+ if (this._color.get(bg, fg) === undefined) {
|
||||
+ this._admitNewEntry();
|
||||
+ }
|
||||
this._color.set(bg, fg, value);
|
||||
}
|
||||
|
||||
@@ -30,5 +39,14 @@ export class ColorContrastCache implements IColorContrastCache {
|
||||
public clear(): void {
|
||||
this._color.clear();
|
||||
this._css.clear();
|
||||
+ this._entryCount = 0;
|
||||
+ }
|
||||
+
|
||||
+ private _admitNewEntry(): void {
|
||||
+ // Color pairs outlive atlas pages, including cached misses and DOM-rendered colors.
|
||||
+ if (this._entryCount >= CONTRAST_CACHE_MAX_ENTRIES) {
|
||||
+ this.clear();
|
||||
+ }
|
||||
+ this._entryCount++;
|
||||
}
|
||||
}
|
||||
diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts
|
||||
index 67893b7966eb095db4459b8837de9766921f36c5..0abf7ecac1aa108f7caf711d0a5c610a688d34fd 100644
|
||||
--- a/src/browser/CoreBrowserTerminal.ts
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import { createRequire } from 'node:module'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { build } from 'esbuild'
|
||||
import { beforeAll, describe, expect, it } from 'vitest'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
let ColorContrastCache
|
||||
|
||||
beforeAll(async () => {
|
||||
const root = dirname(require.resolve('@xterm/xterm/package.json'))
|
||||
const result = await build({
|
||||
entryPoints: [join(root, 'src/browser/ColorContrastCache.ts')],
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
format: 'esm',
|
||||
write: false
|
||||
})
|
||||
;({ ColorContrastCache } = await import(
|
||||
`data:text/javascript;base64,${Buffer.from(result.outputFiles[0].text).toString('base64')}`
|
||||
))
|
||||
})
|
||||
|
||||
describe('vendored xterm contrast cache', () => {
|
||||
it('retains cached nulls and updates existing entries without spending capacity', () => {
|
||||
const cache = new ColorContrastCache()
|
||||
for (let index = 0; index < 4096; index++) {
|
||||
cache.setColor(0, index, null)
|
||||
}
|
||||
const corrected = { css: '#ffffff', rgba: 0xffffffff }
|
||||
for (let index = 0; index < 10000; index++) {
|
||||
cache.setColor(0, 4095, corrected)
|
||||
}
|
||||
expect(cache.getColor(0, 0)).toBeNull()
|
||||
expect(cache.getColor(0, 4095)).toBe(corrected)
|
||||
})
|
||||
|
||||
it('bounds the combined color and CSS cache across distinct backgrounds', () => {
|
||||
const cache = new ColorContrastCache()
|
||||
for (let index = 0; index < 2048; index++) {
|
||||
cache.setColor(index, 0, null)
|
||||
cache.setCss(index, 1, '#ffffff')
|
||||
}
|
||||
expect(cache.getColor(0, 0)).toBeNull()
|
||||
expect(cache.getCss(0, 1)).toBe('#ffffff')
|
||||
cache.setCss(2048, 1, '#eeeeee')
|
||||
expect(cache.getColor(0, 0)).toBeUndefined()
|
||||
expect(cache.getCss(0, 1)).toBeUndefined()
|
||||
expect(cache.getCss(2048, 1)).toBe('#eeeeee')
|
||||
})
|
||||
|
||||
it('resets capacity after a theme clear and preserves independent terminal caches', () => {
|
||||
const cache = new ColorContrastCache()
|
||||
const sibling = new ColorContrastCache()
|
||||
sibling.setColor(0, 0, null)
|
||||
cache.setCss(0, 0, null)
|
||||
cache.clear()
|
||||
for (let index = 0; index < 4096; index++) {
|
||||
cache.setColor(0, index, null)
|
||||
}
|
||||
expect(cache.getColor(0, 0)).toBeNull()
|
||||
expect(cache.getCss(0, 0)).toBeUndefined()
|
||||
cache.setColor(0, 4096, null)
|
||||
expect(cache.getColor(0, 0)).toBeUndefined()
|
||||
expect(sibling.getColor(0, 0)).toBeNull()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,70 @@
|
||||
# Terminal contrast-color cache retention
|
||||
|
||||
Xterm memoizes foreground/background contrast corrections in two caches: ordinary
|
||||
and dim text. Distinct true-color pairs grow these caches even when the result is
|
||||
`null` (no correction needed). Texture-page eviction does not clear them. Orca
|
||||
normally enables contrast correction, so this is separate from the invisible
|
||||
glyph cache fixed in #20965.
|
||||
|
||||
Each `ColorContrastCache` now admits at most 4,096 entries across its color and
|
||||
CSS maps. Inserting another distinct key clears that cache; replacing a key does
|
||||
not consume capacity. The normal and dim caches remain independent. Evicted
|
||||
colors are recalculated by the unchanged correction function. Theme settings,
|
||||
terminal output, and transport behavior do not change.
|
||||
|
||||
## Reproduce
|
||||
|
||||
```sh
|
||||
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/terminal-contrast-cache-retention/reproduce.mjs
|
||||
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/terminal-contrast-cache-retention/reproduce-dom.mjs
|
||||
```
|
||||
|
||||
Both scripts exercise the installed CJS and ESM bundles in headless Chromium.
|
||||
`ORCA_AUDIT_XTERM_BASELINE` can point to a pre-fix CJS bundle to add before runs.
|
||||
The WebGL script also accepts `ORCA_AUDIT_WEBGL_BUNDLE`. Captured results use the
|
||||
older WebGL bundle without #20965, clearing its atlas every 1,000 updates to
|
||||
isolate contrast storage from glyph storage. Thus this fix and reproduction do
|
||||
not depend on the other open PR. SHA-256 hashes are in the results files.
|
||||
|
||||
The WebGL test performs 100,000 colored-space redraws with two terminals sharing
|
||||
one atlas. One visible glyph uses a low-contrast color that requires correction.
|
||||
Normal and dim runs preserve both terminals' first-cell pixel hashes and the
|
||||
corrected glyph's pixels, including after clearing and recalculating its color.
|
||||
CDP collects garbage before measuring the JavaScript heap; no heap snapshots or
|
||||
Orca application windows are used.
|
||||
|
||||
| Bundle | Mode | Final contrast entries | Heap growth |
|
||||
| ---------- | ------ | ---------------------: | --------------: |
|
||||
| Before CJS | Normal | 100,002 | 5,487,228 bytes |
|
||||
| Before CJS | Dim | 100,001 | 4,023,452 bytes |
|
||||
| After CJS | Normal | 1,746 | 291,796 bytes |
|
||||
| After CJS | Dim | 1,721 | 316,176 bytes |
|
||||
| After ESM | Normal | 1,746 | 408,652 bytes |
|
||||
| After ESM | Dim | 1,721 | 436,188 bytes |
|
||||
|
||||
All fixed samples stay within the 4,096-entry limit per cache. The DOM test adds
|
||||
10,000 redraws per case for dark/light backgrounds and normal/dim text. It checks
|
||||
that an evicted color, and a color recalculated after explicit cache clearing,
|
||||
produce the same computed CSS color and rendered row HTML as before.
|
||||
All 12 DOM cases pass across the baseline and both fixed module formats; samples
|
||||
reach the exact 4,096-entry cap. Every page and browser is closed after the run.
|
||||
|
||||
## Validation and limits
|
||||
|
||||
- 122 tests pass across cache, regeneration, contrast, appearance, IME, and
|
||||
renderer suites. Two cache regressions fail against the previous source.
|
||||
- The authoritative source patch, generated CJS/ESM bundles and source maps, and
|
||||
lockfile hashes were regenerated together. Frozen install and the pinned
|
||||
regeneration `--check` pass.
|
||||
- Full desktop typecheck, formatting/lint, and changed-code quality pass.
|
||||
- Captured browser: Chromium 147.0.7727.15 on macOS. Heap measurements include
|
||||
GC/allocator variation and other terminal state.
|
||||
- This is a desktop renderer fix. Mobile resolves its own unpatched xterm package
|
||||
when generating its WebView engine; that separate bundle is not fixed here.
|
||||
- `v1.4.198` shipped the same xterm version and automatic 3/4.5 contrast settings.
|
||||
Its appearance path also skipped unchanged theme/ratio assignments, so ordinary
|
||||
reapplication did not periodically clear the cache. User contrast overrides were
|
||||
added later; their current behavior is unchanged.
|
||||
- Revisited evicted pairs incur the existing contrast calculation again. No
|
||||
incident report establishes the distinct-color traffic used in this proof;
|
||||
renderer retention does not explain #19768's separately measured main PID.
|
||||
@@ -0,0 +1,223 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { createHash } from 'node:crypto'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { resolve } from 'node:path'
|
||||
import { chromium } from 'playwright'
|
||||
|
||||
if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') {
|
||||
throw new Error('Run with ORCA_BACKGROUND_LAUNCH=1')
|
||||
}
|
||||
|
||||
const installed = resolve('node_modules/@xterm/xterm/lib/xterm.js')
|
||||
const bundles = [
|
||||
['after', installed],
|
||||
['after', installed.replace(/\.js$/, '.mjs')]
|
||||
]
|
||||
if (process.env.ORCA_AUDIT_XTERM_BASELINE) {
|
||||
bundles.unshift(['before', resolve(process.env.ORCA_AUDIT_XTERM_BASELINE)])
|
||||
}
|
||||
const browser = await chromium.launch({
|
||||
executablePath: process.env.ORCA_AUDIT_CHROMIUM,
|
||||
headless: true
|
||||
})
|
||||
const browserVersion = browser.version()
|
||||
const results = []
|
||||
const referenceColors = new Map()
|
||||
let pagesOpened = 0
|
||||
let pagesClosed = 0
|
||||
try {
|
||||
for (const [phase, bundle] of bundles) {
|
||||
const source = await readFile(bundle, 'utf8')
|
||||
const sha256 = createHash('sha256').update(source).digest('hex')
|
||||
for (const theme of ['dark', 'light']) {
|
||||
for (const mode of ['normal', 'dim']) {
|
||||
const page = await browser.newPage()
|
||||
pagesOpened++
|
||||
try {
|
||||
await page.setContent('<div id="terminal"></div>')
|
||||
await page.addStyleTag({ path: resolve('node_modules/@xterm/xterm/css/xterm.css') })
|
||||
await (bundle.endsWith('.mjs')
|
||||
? page.evaluate(
|
||||
async (url) => {
|
||||
window.Terminal = (await import(url)).Terminal
|
||||
},
|
||||
`data:text/javascript;base64,${Buffer.from(source).toString('base64')}`
|
||||
)
|
||||
: page.addScriptTag({ path: bundle }))
|
||||
const initial = await page.evaluate(
|
||||
async ({ theme, mode }) => {
|
||||
const terminal = new Terminal({
|
||||
minimumContrastRatio: theme === 'dark' ? 3 : 4.5,
|
||||
theme:
|
||||
theme === 'dark'
|
||||
? { background: '#000000', foreground: '#ffffff' }
|
||||
: { background: '#ffffff', foreground: '#000000' },
|
||||
cols: 4,
|
||||
rows: 1,
|
||||
scrollback: 0,
|
||||
allowProposedApi: true,
|
||||
logLevel: 'off'
|
||||
})
|
||||
terminal.open(document.getElementById('terminal'))
|
||||
await new Promise(requestAnimationFrame)
|
||||
const renderer = terminal._core._renderService._renderer.value
|
||||
const colors = terminal._core._themeService.colors
|
||||
const probe = theme === 'dark' ? [5, 50, 25] : [245, 250, 240]
|
||||
const probeRgba = ((probe[0] << 24) | (probe[1] << 16) | (probe[2] << 8) | 255) >>> 0
|
||||
const draw = (rgb) => {
|
||||
terminal._core.writeSync(
|
||||
`\x1b[?25l\x1b[H\x1b[${mode === 'dim' ? 2 : 22}m\x1b[38;2;${rgb.join(';')}mM`
|
||||
)
|
||||
renderer.renderRows(0, 0)
|
||||
}
|
||||
const count = (cache) =>
|
||||
Object.values(cache._color._data).reduce(
|
||||
(sum, row) => sum + Object.keys(row).length,
|
||||
0
|
||||
)
|
||||
const activeCache = () =>
|
||||
colors[mode === 'dim' ? 'halfContrastCache' : 'contrastCache']
|
||||
const state = () => ({
|
||||
contrast: count(colors.contrastCache),
|
||||
dimContrast: count(colors.halfContrastCache),
|
||||
probeCached: Object.values(activeCache()._color._data).some((row) =>
|
||||
Object.hasOwn(row, probeRgba)
|
||||
)
|
||||
})
|
||||
const visible = () => {
|
||||
const row = terminal.element.querySelector('.xterm-rows > div')
|
||||
const cell = [...row.querySelectorAll('span')].find(
|
||||
(span) => span.textContent === 'M'
|
||||
)
|
||||
if (!cell) {
|
||||
throw new Error('Expected rendered probe glyph')
|
||||
}
|
||||
return {
|
||||
color: getComputedStyle(cell).color,
|
||||
opacity: getComputedStyle(cell).opacity,
|
||||
html: row.innerHTML,
|
||||
text: terminal.buffer.active.getLine(0).getCell(0).getChars()
|
||||
}
|
||||
}
|
||||
draw(probe)
|
||||
window.domContrastAudit = {
|
||||
terminal,
|
||||
renderer,
|
||||
colors,
|
||||
probe,
|
||||
draw,
|
||||
state,
|
||||
visible,
|
||||
index: 0
|
||||
}
|
||||
return {
|
||||
minimumContrastRatio: terminal.options.minimumContrastRatio,
|
||||
rawColor: `rgb(${probe.join(', ')})`,
|
||||
...visible(),
|
||||
...state()
|
||||
}
|
||||
},
|
||||
{ theme, mode }
|
||||
)
|
||||
assert.equal(initial.text, 'M')
|
||||
assert.notEqual(
|
||||
initial.color,
|
||||
initial.rawColor,
|
||||
'Probe must exercise contrast correction'
|
||||
)
|
||||
assert.equal(initial.probeCached, true)
|
||||
const samples = []
|
||||
for (const count of [0, 1000, 4094, 4095, 4096, 5000, 10000]) {
|
||||
samples.push(
|
||||
await page.evaluate(
|
||||
({ count, theme }) => {
|
||||
const audit = window.domContrastAudit
|
||||
for (let index = audit.index; index < count; index++) {
|
||||
const color = theme === 'dark' ? index + 1 : 0xffffff - index
|
||||
audit.draw([color >> 16, (color >> 8) & 255, color & 255])
|
||||
}
|
||||
audit.index = count
|
||||
return { updates: count, ...audit.state() }
|
||||
},
|
||||
{ count, theme }
|
||||
)
|
||||
)
|
||||
}
|
||||
if (phase === 'after') {
|
||||
assert.ok(
|
||||
samples.every((sample) => sample.contrast <= 4096 && sample.dimContrast <= 4096)
|
||||
)
|
||||
assert.equal(samples.at(-1).probeCached, false, 'Original color must be evicted')
|
||||
} else {
|
||||
assert.ok(samples.at(-1)[mode === 'dim' ? 'dimContrast' : 'contrast'] >= 10000)
|
||||
assert.equal(samples.at(-1).probeCached, true)
|
||||
}
|
||||
const checks = await page.evaluate(() => {
|
||||
const audit = window.domContrastAudit
|
||||
audit.draw(audit.probe)
|
||||
const revisited = { ...audit.visible(), ...audit.state() }
|
||||
audit.colors.contrastCache.clear()
|
||||
audit.colors.halfContrastCache.clear()
|
||||
const cleared = audit.state()
|
||||
audit.renderer.renderRows(0, 0)
|
||||
return { revisited, cleared, recomputed: { ...audit.visible(), ...audit.state() } }
|
||||
})
|
||||
for (const snapshot of [checks.revisited, checks.recomputed]) {
|
||||
assert.equal(snapshot.color, initial.color)
|
||||
assert.equal(snapshot.opacity, initial.opacity)
|
||||
assert.equal(snapshot.html, initial.html)
|
||||
assert.equal(snapshot.text, 'M')
|
||||
assert.equal(snapshot.probeCached, true)
|
||||
}
|
||||
assert.equal(checks.cleared.contrast, 0)
|
||||
assert.equal(checks.cleared.dimContrast, 0)
|
||||
assert.equal(checks.cleared.probeCached, false)
|
||||
const key = `${theme}/${mode}`
|
||||
const visibleColor = { color: initial.color, opacity: initial.opacity }
|
||||
if (referenceColors.has(key)) {
|
||||
assert.deepEqual(visibleColor, referenceColors.get(key))
|
||||
} else {
|
||||
referenceColors.set(key, visibleColor)
|
||||
}
|
||||
results.push({
|
||||
phase,
|
||||
format: bundle.endsWith('.mjs') ? 'esm' : 'cjs',
|
||||
theme,
|
||||
mode,
|
||||
sha256,
|
||||
initial,
|
||||
samples,
|
||||
checks
|
||||
})
|
||||
} finally {
|
||||
await page
|
||||
.evaluate(() => window.domContrastAudit?.terminal.dispose())
|
||||
.catch(() => undefined)
|
||||
await page.close()
|
||||
assert.equal(page.isClosed(), true)
|
||||
pagesClosed++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
await browser.close()
|
||||
}
|
||||
assert.equal(pagesClosed, pagesOpened)
|
||||
assert.equal(browser.isConnected(), false)
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
{
|
||||
node: process.version,
|
||||
browser: browserVersion,
|
||||
headless: true,
|
||||
renderer: 'DOM',
|
||||
pagesOpened,
|
||||
pagesClosed,
|
||||
browserClosed: !browser.isConnected(),
|
||||
results
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,211 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { createHash } from 'node:crypto'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { resolve } from 'node:path'
|
||||
import { chromium } from 'playwright'
|
||||
|
||||
if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') {
|
||||
throw new Error('Run with ORCA_BACKGROUND_LAUNCH=1')
|
||||
}
|
||||
const installed = resolve('node_modules/@xterm/xterm/lib/xterm.js')
|
||||
const webglBundle = resolve(
|
||||
process.env.ORCA_AUDIT_WEBGL_BUNDLE ?? 'node_modules/@xterm/addon-webgl/lib/addon-webgl.js'
|
||||
)
|
||||
const current = [
|
||||
['after', installed],
|
||||
['after', installed.replace(/\.js$/, '.mjs')]
|
||||
]
|
||||
const bundles = process.env.ORCA_AUDIT_XTERM_BASELINE
|
||||
? [['before', resolve(process.env.ORCA_AUDIT_XTERM_BASELINE)], ...current]
|
||||
: current
|
||||
const browser = await chromium.launch({
|
||||
executablePath: process.env.ORCA_AUDIT_CHROMIUM,
|
||||
headless: true,
|
||||
args: ['--use-gl=angle', '--use-angle=swiftshader', '--enable-unsafe-swiftshader']
|
||||
})
|
||||
const results = []
|
||||
try {
|
||||
for (const [phase, bundle] of bundles) {
|
||||
const sha256 = createHash('sha256')
|
||||
.update(await readFile(bundle))
|
||||
.digest('hex')
|
||||
for (const mode of ['normal', 'dim']) {
|
||||
const page = await browser.newPage()
|
||||
try {
|
||||
await page.setContent('<div id="first"></div><div id="second"></div>')
|
||||
await page.addStyleTag({ path: resolve('node_modules/@xterm/xterm/css/xterm.css') })
|
||||
await page.addScriptTag({ path: webglBundle })
|
||||
if (bundle.endsWith('.mjs')) {
|
||||
const source = await readFile(bundle, 'utf8')
|
||||
await page.evaluate(
|
||||
async (url) => {
|
||||
window.Terminal = (await import(url)).Terminal
|
||||
},
|
||||
`data:text/javascript;base64,${Buffer.from(source).toString('base64')}`
|
||||
)
|
||||
} else {
|
||||
await page.addScriptTag({ path: bundle })
|
||||
}
|
||||
await page.evaluate(async (mode) => {
|
||||
const create = (id, text) => {
|
||||
const terminal = new Terminal({
|
||||
minimumContrastRatio: 3,
|
||||
cols: 4,
|
||||
rows: 1,
|
||||
scrollback: 0,
|
||||
allowProposedApi: true,
|
||||
logLevel: 'off'
|
||||
})
|
||||
terminal.open(document.getElementById(id))
|
||||
const addon = new WebglAddon.WebglAddon()
|
||||
terminal.loadAddon(addon)
|
||||
terminal._core.writeSync(text)
|
||||
addon._renderer.renderRows(0, 0)
|
||||
return { terminal, addon }
|
||||
}
|
||||
const first = create('first', `A \x1b[${mode === 'dim' ? 2 : 22}m\x1b[38;2;5;50;25mM`)
|
||||
const second = create('second', 'B')
|
||||
window.glyphAudit = { first, second, mode, index: 0 }
|
||||
await new Promise(requestAnimationFrame)
|
||||
await new Promise(requestIdleCallback)
|
||||
}, mode)
|
||||
const cdp = await page.context().newCDPSession(page)
|
||||
const samples = []
|
||||
for (const count of [0, 1000, 5000, 10000, 50000, 100000]) {
|
||||
const state = await page.evaluate((count) => {
|
||||
const { first, second, mode } = window.glyphAudit
|
||||
for (let index = window.glyphAudit.index; index < count; index++) {
|
||||
// Isolate contrast storage even when the older glyph cache has no entry cap.
|
||||
if (index > 0 && index % 1000 === 0) {
|
||||
first.addon.clearTextureAtlas()
|
||||
}
|
||||
const color = index + 1
|
||||
first.terminal._core.writeSync(
|
||||
`\x1b[1;2H\x1b[${mode === 'dim' ? 2 : 22}m\x1b[38;2;${color >> 16};${(color >> 8) & 255};${color & 255}m `
|
||||
)
|
||||
first.addon._renderer.renderRows(0, 0)
|
||||
}
|
||||
first.addon._renderer.renderRows(0, 0)
|
||||
second.addon._renderer.renderRows(0, 0)
|
||||
window.glyphAudit.index = count
|
||||
const atlas = first.addon._renderer._charAtlas
|
||||
const entries = (map) => {
|
||||
let result = 0
|
||||
for (const second of Object.values(map?._data._data ?? {})) {
|
||||
for (const inner of Object.values(second)) {
|
||||
for (const fourth of Object.values(inner._data)) {
|
||||
result += Object.keys(fourth).length
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
const countColors = (cache) =>
|
||||
Object.values(cache._color._data).reduce(
|
||||
(sum, row) => sum + Object.keys(row).length,
|
||||
0
|
||||
) +
|
||||
Object.values(cache._css._data).reduce((sum, row) => sum + Object.keys(row).length, 0)
|
||||
const pixels = ({ terminal, addon }, cellIndex = 0) => {
|
||||
const gl = addon._renderer._gl
|
||||
const width = Math.floor(gl.drawingBufferWidth / terminal.cols)
|
||||
const height = gl.drawingBufferHeight
|
||||
const bytes = new Uint8Array(width * height * 4)
|
||||
gl.readPixels(cellIndex * width, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, bytes)
|
||||
let hash = 2166136261
|
||||
for (const byte of bytes) {
|
||||
hash = Math.imul(hash ^ byte, 16777619)
|
||||
}
|
||||
return hash >>> 0
|
||||
}
|
||||
window.glyphAudit.pixelHash = pixels
|
||||
return {
|
||||
contrastProbePixels: pixels(first, 2),
|
||||
contrast: countColors(first.terminal._core._themeService.colors.contrastCache),
|
||||
dimContrast: countColors(first.terminal._core._themeService.colors.halfContrastCache),
|
||||
updates: count,
|
||||
firstCellPixels: pixels(first),
|
||||
secondCellPixels: pixels(second),
|
||||
regular: entries(atlas._cacheMap),
|
||||
combined: entries(atlas._cacheMapCombined),
|
||||
empty: entries(atlas._emptyCacheMap) + entries(atlas._emptyCacheMapCombined),
|
||||
pages: atlas.pages.length,
|
||||
glyphs: atlas.pages.reduce((n, page) => n + page.glyphs.length, 0),
|
||||
layoutVersion: atlas.pageLayoutVersion,
|
||||
shared: atlas === second.addon._renderer._charAtlas,
|
||||
preserved:
|
||||
first.terminal.buffer.active.getLine(0).getCell(0).getChars() === 'A' &&
|
||||
second.terminal.buffer.active.getLine(0).getCell(0).getChars() === 'B'
|
||||
}
|
||||
}, count)
|
||||
await cdp.send('HeapProfiler.collectGarbage')
|
||||
samples.push({ ...state, heap: (await cdp.send('Runtime.getHeapUsage')).usedSize })
|
||||
}
|
||||
assert.ok(samples.every((sample) => sample.shared && sample.preserved))
|
||||
assert.notEqual(samples[0].firstCellPixels, samples[0].secondCellPixels)
|
||||
assert.ok(
|
||||
samples.every(
|
||||
(sample) =>
|
||||
sample.firstCellPixels === samples[0].firstCellPixels &&
|
||||
sample.secondCellPixels === samples[0].secondCellPixels &&
|
||||
sample.contrastProbePixels === samples[0].contrastProbePixels
|
||||
)
|
||||
)
|
||||
if (phase === 'after') {
|
||||
assert.ok(
|
||||
samples.every((sample) => sample.contrast <= 4096 && sample.dimContrast <= 4096)
|
||||
)
|
||||
} else {
|
||||
assert.ok(samples.at(-1)[mode === 'dim' ? 'dimContrast' : 'contrast'] >= 100000)
|
||||
}
|
||||
const checks = await page.evaluate(() => {
|
||||
const { first, mode } = window.glyphAudit
|
||||
const cache =
|
||||
first.terminal._core._themeService.colors[
|
||||
mode === 'dim' ? 'halfContrastCache' : 'contrastCache'
|
||||
]
|
||||
const entries = () =>
|
||||
Object.values(cache._color._data).reduce((sum, row) => sum + Object.keys(row).length, 0)
|
||||
const beforeAtlasClear = entries()
|
||||
first.addon.clearTextureAtlas()
|
||||
const afterAtlasClear = entries()
|
||||
cache.clear()
|
||||
const afterThemeClear = entries()
|
||||
first.addon._renderer.renderRows(0, 0)
|
||||
const recomputedProbePixels = window.glyphAudit.pixelHash(first, 2)
|
||||
return { beforeAtlasClear, afterAtlasClear, afterThemeClear, recomputedProbePixels }
|
||||
})
|
||||
assert.equal(checks.beforeAtlasClear, checks.afterAtlasClear)
|
||||
assert.equal(checks.afterThemeClear, 0)
|
||||
assert.equal(checks.recomputedProbePixels, samples[0].contrastProbePixels)
|
||||
results.push({
|
||||
phase,
|
||||
format: bundle.endsWith('.mjs') ? 'esm' : 'cjs',
|
||||
mode,
|
||||
sha256,
|
||||
samples,
|
||||
checks
|
||||
})
|
||||
} finally {
|
||||
await page.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
{
|
||||
node: process.version,
|
||||
browser: browser.version(),
|
||||
webglSha256: createHash('sha256')
|
||||
.update(await readFile(webglBundle))
|
||||
.digest('hex'),
|
||||
atlasClearEveryUpdates: 1000,
|
||||
results
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
)
|
||||
} finally {
|
||||
await browser.close()
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,704 @@
|
||||
{
|
||||
"node": "v26.6.0",
|
||||
"browser": "147.0.7727.15",
|
||||
"webglSha256": "c4b646075065e9ed5f885880ff80e2edd54f481f982adaf193cc742de17e1a4f",
|
||||
"atlasClearEveryUpdates": 1000,
|
||||
"results": [
|
||||
{
|
||||
"phase": "before",
|
||||
"format": "cjs",
|
||||
"mode": "normal",
|
||||
"sha256": "c9ba43e25b417929c7ab282aa267c73f53106583aba003f6f1b2415850723707",
|
||||
"samples": [
|
||||
{
|
||||
"contrastProbePixels": 482738276,
|
||||
"contrast": 2,
|
||||
"dimContrast": 0,
|
||||
"updates": 0,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 95,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 94,
|
||||
"layoutVersion": 0,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3350504
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 482738276,
|
||||
"contrast": 1002,
|
||||
"dimContrast": 0,
|
||||
"updates": 1000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1095,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 94,
|
||||
"layoutVersion": 0,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3911268
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 482738276,
|
||||
"contrast": 5002,
|
||||
"dimContrast": 0,
|
||||
"updates": 5000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 4,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 4090760
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 482738276,
|
||||
"contrast": 10002,
|
||||
"dimContrast": 0,
|
||||
"updates": 10000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 9,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 4455848
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 482738276,
|
||||
"contrast": 50002,
|
||||
"dimContrast": 0,
|
||||
"updates": 50000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 49,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 6503096
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 482738276,
|
||||
"contrast": 100002,
|
||||
"dimContrast": 0,
|
||||
"updates": 100000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 99,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 8837732
|
||||
}
|
||||
],
|
||||
"checks": {
|
||||
"beforeAtlasClear": 100002,
|
||||
"afterAtlasClear": 100002,
|
||||
"afterThemeClear": 0,
|
||||
"recomputedProbePixels": 482738276
|
||||
}
|
||||
},
|
||||
{
|
||||
"phase": "before",
|
||||
"format": "cjs",
|
||||
"mode": "dim",
|
||||
"sha256": "c9ba43e25b417929c7ab282aa267c73f53106583aba003f6f1b2415850723707",
|
||||
"samples": [
|
||||
{
|
||||
"contrastProbePixels": 3123285829,
|
||||
"contrast": 1,
|
||||
"dimContrast": 1,
|
||||
"updates": 0,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 95,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 94,
|
||||
"layoutVersion": 0,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3353600
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 3123285829,
|
||||
"contrast": 1,
|
||||
"dimContrast": 1001,
|
||||
"updates": 1000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1095,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 94,
|
||||
"layoutVersion": 0,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3918408
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 3123285829,
|
||||
"contrast": 1,
|
||||
"dimContrast": 5001,
|
||||
"updates": 5000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 4,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 4050468
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 3123285829,
|
||||
"contrast": 1,
|
||||
"dimContrast": 10001,
|
||||
"updates": 10000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 9,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 4323072
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 3123285829,
|
||||
"contrast": 1,
|
||||
"dimContrast": 50001,
|
||||
"updates": 50000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 49,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 5791324
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 3123285829,
|
||||
"contrast": 1,
|
||||
"dimContrast": 100001,
|
||||
"updates": 100000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 99,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 7377052
|
||||
}
|
||||
],
|
||||
"checks": {
|
||||
"beforeAtlasClear": 100001,
|
||||
"afterAtlasClear": 100001,
|
||||
"afterThemeClear": 0,
|
||||
"recomputedProbePixels": 3123285829
|
||||
}
|
||||
},
|
||||
{
|
||||
"phase": "after",
|
||||
"format": "cjs",
|
||||
"mode": "normal",
|
||||
"sha256": "5a40045331d16d5c30e558576a07f6b13c7d9aa0c9027211af78b12a10ad8f12",
|
||||
"samples": [
|
||||
{
|
||||
"contrastProbePixels": 482738276,
|
||||
"contrast": 2,
|
||||
"dimContrast": 0,
|
||||
"updates": 0,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 95,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 94,
|
||||
"layoutVersion": 0,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3345812
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 482738276,
|
||||
"contrast": 1002,
|
||||
"dimContrast": 0,
|
||||
"updates": 1000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1095,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 94,
|
||||
"layoutVersion": 0,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3915296
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 482738276,
|
||||
"contrast": 907,
|
||||
"dimContrast": 0,
|
||||
"updates": 5000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 4,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3845328
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 482738276,
|
||||
"contrast": 1814,
|
||||
"dimContrast": 0,
|
||||
"updates": 10000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 9,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3970072
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 482738276,
|
||||
"contrast": 873,
|
||||
"dimContrast": 0,
|
||||
"updates": 50000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 49,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3912056
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 482738276,
|
||||
"contrast": 1746,
|
||||
"dimContrast": 0,
|
||||
"updates": 100000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 99,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3637608
|
||||
}
|
||||
],
|
||||
"checks": {
|
||||
"beforeAtlasClear": 1746,
|
||||
"afterAtlasClear": 1746,
|
||||
"afterThemeClear": 0,
|
||||
"recomputedProbePixels": 482738276
|
||||
}
|
||||
},
|
||||
{
|
||||
"phase": "after",
|
||||
"format": "cjs",
|
||||
"mode": "dim",
|
||||
"sha256": "5a40045331d16d5c30e558576a07f6b13c7d9aa0c9027211af78b12a10ad8f12",
|
||||
"samples": [
|
||||
{
|
||||
"contrastProbePixels": 3123285829,
|
||||
"contrast": 1,
|
||||
"dimContrast": 1,
|
||||
"updates": 0,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 95,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 94,
|
||||
"layoutVersion": 0,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3353972
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 3123285829,
|
||||
"contrast": 1,
|
||||
"dimContrast": 1001,
|
||||
"updates": 1000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1095,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 94,
|
||||
"layoutVersion": 0,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3921736
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 3123285829,
|
||||
"contrast": 1,
|
||||
"dimContrast": 905,
|
||||
"updates": 5000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 4,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3868104
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 3123285829,
|
||||
"contrast": 1,
|
||||
"dimContrast": 1811,
|
||||
"updates": 10000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 9,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3957332
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 3123285829,
|
||||
"contrast": 1,
|
||||
"dimContrast": 860,
|
||||
"updates": 50000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 49,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3945224
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 3123285829,
|
||||
"contrast": 1,
|
||||
"dimContrast": 1721,
|
||||
"updates": 100000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 99,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3670148
|
||||
}
|
||||
],
|
||||
"checks": {
|
||||
"beforeAtlasClear": 1721,
|
||||
"afterAtlasClear": 1721,
|
||||
"afterThemeClear": 0,
|
||||
"recomputedProbePixels": 3123285829
|
||||
}
|
||||
},
|
||||
{
|
||||
"phase": "after",
|
||||
"format": "esm",
|
||||
"mode": "normal",
|
||||
"sha256": "f644ab7b834e7866be59b48fd35456b4350554f22b6cc815bcdba00d36a2d827",
|
||||
"samples": [
|
||||
{
|
||||
"contrastProbePixels": 482738276,
|
||||
"contrast": 2,
|
||||
"dimContrast": 0,
|
||||
"updates": 0,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 95,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 94,
|
||||
"layoutVersion": 0,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3245196
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 482738276,
|
||||
"contrast": 1002,
|
||||
"dimContrast": 0,
|
||||
"updates": 1000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1095,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 94,
|
||||
"layoutVersion": 0,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3817160
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 482738276,
|
||||
"contrast": 907,
|
||||
"dimContrast": 0,
|
||||
"updates": 5000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 4,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3741948
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 482738276,
|
||||
"contrast": 1814,
|
||||
"dimContrast": 0,
|
||||
"updates": 10000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 9,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3866684
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 482738276,
|
||||
"contrast": 873,
|
||||
"dimContrast": 0,
|
||||
"updates": 50000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 49,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3811616
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 482738276,
|
||||
"contrast": 1746,
|
||||
"dimContrast": 0,
|
||||
"updates": 100000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 99,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3653848
|
||||
}
|
||||
],
|
||||
"checks": {
|
||||
"beforeAtlasClear": 1746,
|
||||
"afterAtlasClear": 1746,
|
||||
"afterThemeClear": 0,
|
||||
"recomputedProbePixels": 482738276
|
||||
}
|
||||
},
|
||||
{
|
||||
"phase": "after",
|
||||
"format": "esm",
|
||||
"mode": "dim",
|
||||
"sha256": "f644ab7b834e7866be59b48fd35456b4350554f22b6cc815bcdba00d36a2d827",
|
||||
"samples": [
|
||||
{
|
||||
"contrastProbePixels": 3123285829,
|
||||
"contrast": 1,
|
||||
"dimContrast": 1,
|
||||
"updates": 0,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 95,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 94,
|
||||
"layoutVersion": 0,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3248292
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 3123285829,
|
||||
"contrast": 1,
|
||||
"dimContrast": 1001,
|
||||
"updates": 1000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1095,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 94,
|
||||
"layoutVersion": 0,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3818640
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 3123285829,
|
||||
"contrast": 1,
|
||||
"dimContrast": 905,
|
||||
"updates": 5000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 4,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3767340
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 3123285829,
|
||||
"contrast": 1,
|
||||
"dimContrast": 1811,
|
||||
"updates": 10000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 9,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3854124
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 3123285829,
|
||||
"contrast": 1,
|
||||
"dimContrast": 860,
|
||||
"updates": 50000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 49,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3841040
|
||||
},
|
||||
{
|
||||
"contrastProbePixels": 3123285829,
|
||||
"contrast": 1,
|
||||
"dimContrast": 1721,
|
||||
"updates": 100000,
|
||||
"firstCellPixels": 1033360771,
|
||||
"secondCellPixels": 395161943,
|
||||
"regular": 1003,
|
||||
"combined": 0,
|
||||
"empty": 0,
|
||||
"pages": 1,
|
||||
"glyphs": 3,
|
||||
"layoutVersion": 99,
|
||||
"shared": true,
|
||||
"preserved": true,
|
||||
"heap": 3684480
|
||||
}
|
||||
],
|
||||
"checks": {
|
||||
"beforeAtlasClear": 1721,
|
||||
"afterAtlasClear": 1721,
|
||||
"afterThemeClear": 0,
|
||||
"recomputedProbePixels": 3123285829
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Generated
+26
-26
@@ -143,7 +143,7 @@ importers:
|
||||
version: 0.0.26
|
||||
'@xterm/addon-serialize':
|
||||
specifier: 0.15.0-beta.300
|
||||
version: 0.15.0-beta.300(patch_hash=851eac3d75e6d8c013b9f4c053e61d824b23965cb19ecc28e335e05059f3a294)(@xterm/xterm@6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4))
|
||||
version: 0.15.0-beta.300(patch_hash=851eac3d75e6d8c013b9f4c053e61d824b23965cb19ecc28e335e05059f3a294)(@xterm/xterm@6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96))
|
||||
'@xterm/headless':
|
||||
specifier: 6.1.0-beta.302
|
||||
version: 6.1.0-beta.302
|
||||
@@ -324,28 +324,28 @@ importers:
|
||||
version: 5.2.0(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@25.9.5)(esbuild@0.25.12)(jiti@2.7.0)(yaml@2.8.4))
|
||||
'@xterm/addon-fit':
|
||||
specifier: 0.12.0-beta.300
|
||||
version: 0.12.0-beta.300(@xterm/xterm@6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4))
|
||||
version: 0.12.0-beta.300(@xterm/xterm@6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96))
|
||||
'@xterm/addon-image':
|
||||
specifier: 0.10.0-beta.300
|
||||
version: 0.10.0-beta.300(patch_hash=5fc280019957573eefe8daa2969534d6090ddb29c6b5842b8f22e99dc1ce3146)(@xterm/xterm@6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4))
|
||||
version: 0.10.0-beta.300(patch_hash=5fc280019957573eefe8daa2969534d6090ddb29c6b5842b8f22e99dc1ce3146)(@xterm/xterm@6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96))
|
||||
'@xterm/addon-ligatures':
|
||||
specifier: 0.11.0-beta.300
|
||||
version: 0.11.0-beta.300(patch_hash=47405b9994b5acf1b4e90b49250358c1ca03649854d59560e7732b72fe336920)(@xterm/xterm@6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4))
|
||||
version: 0.11.0-beta.300(patch_hash=47405b9994b5acf1b4e90b49250358c1ca03649854d59560e7732b72fe336920)(@xterm/xterm@6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96))
|
||||
'@xterm/addon-search':
|
||||
specifier: 0.17.0-beta.300
|
||||
version: 0.17.0-beta.300(patch_hash=eee5338dd2621ece46e79c61ec06766cd7fadaf79ffdb24e2a8ab68e97ef31f0)(@xterm/xterm@6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4))
|
||||
version: 0.17.0-beta.300(patch_hash=eee5338dd2621ece46e79c61ec06766cd7fadaf79ffdb24e2a8ab68e97ef31f0)(@xterm/xterm@6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96))
|
||||
'@xterm/addon-unicode11':
|
||||
specifier: 0.10.0-beta.300
|
||||
version: 0.10.0-beta.300(@xterm/xterm@6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4))
|
||||
version: 0.10.0-beta.300(@xterm/xterm@6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96))
|
||||
'@xterm/addon-web-links':
|
||||
specifier: 0.13.0-beta.300
|
||||
version: 0.13.0-beta.300(@xterm/xterm@6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4))
|
||||
version: 0.13.0-beta.300(@xterm/xterm@6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96))
|
||||
'@xterm/addon-webgl':
|
||||
specifier: 0.20.0-beta.299
|
||||
version: 0.20.0-beta.299(patch_hash=94687e89a0115e6e6aa102837f986debdc029c091527ee5eb4a4e17ceaf9473e)(@xterm/xterm@6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4))
|
||||
version: 0.20.0-beta.299(patch_hash=94687e89a0115e6e6aa102837f986debdc029c091527ee5eb4a4e17ceaf9473e)(@xterm/xterm@6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96))
|
||||
'@xterm/xterm':
|
||||
specifier: 6.1.0-beta.303
|
||||
version: 6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4)
|
||||
version: 6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96)
|
||||
class-variance-authority:
|
||||
specifier: ^0.7.1
|
||||
version: 0.7.1
|
||||
@@ -10483,43 +10483,43 @@ snapshots:
|
||||
|
||||
'@xmldom/xmldom@0.8.15': {}
|
||||
|
||||
'@xterm/addon-fit@0.12.0-beta.300(@xterm/xterm@6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4))':
|
||||
'@xterm/addon-fit@0.12.0-beta.300(@xterm/xterm@6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96))':
|
||||
dependencies:
|
||||
'@xterm/xterm': 6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4)
|
||||
'@xterm/xterm': 6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96)
|
||||
|
||||
'@xterm/addon-image@0.10.0-beta.300(patch_hash=5fc280019957573eefe8daa2969534d6090ddb29c6b5842b8f22e99dc1ce3146)(@xterm/xterm@6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4))':
|
||||
'@xterm/addon-image@0.10.0-beta.300(patch_hash=5fc280019957573eefe8daa2969534d6090ddb29c6b5842b8f22e99dc1ce3146)(@xterm/xterm@6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96))':
|
||||
dependencies:
|
||||
'@xterm/xterm': 6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4)
|
||||
'@xterm/xterm': 6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96)
|
||||
|
||||
'@xterm/addon-ligatures@0.11.0-beta.300(patch_hash=47405b9994b5acf1b4e90b49250358c1ca03649854d59560e7732b72fe336920)(@xterm/xterm@6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4))':
|
||||
'@xterm/addon-ligatures@0.11.0-beta.300(patch_hash=47405b9994b5acf1b4e90b49250358c1ca03649854d59560e7732b72fe336920)(@xterm/xterm@6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96))':
|
||||
dependencies:
|
||||
'@xterm/xterm': 6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4)
|
||||
'@xterm/xterm': 6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96)
|
||||
lru-cache: 11.5.1
|
||||
opentype.js: 2.0.0
|
||||
|
||||
'@xterm/addon-search@0.17.0-beta.300(patch_hash=eee5338dd2621ece46e79c61ec06766cd7fadaf79ffdb24e2a8ab68e97ef31f0)(@xterm/xterm@6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4))':
|
||||
'@xterm/addon-search@0.17.0-beta.300(patch_hash=eee5338dd2621ece46e79c61ec06766cd7fadaf79ffdb24e2a8ab68e97ef31f0)(@xterm/xterm@6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96))':
|
||||
dependencies:
|
||||
'@xterm/xterm': 6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4)
|
||||
'@xterm/xterm': 6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96)
|
||||
|
||||
'@xterm/addon-serialize@0.15.0-beta.300(patch_hash=851eac3d75e6d8c013b9f4c053e61d824b23965cb19ecc28e335e05059f3a294)(@xterm/xterm@6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4))':
|
||||
'@xterm/addon-serialize@0.15.0-beta.300(patch_hash=851eac3d75e6d8c013b9f4c053e61d824b23965cb19ecc28e335e05059f3a294)(@xterm/xterm@6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96))':
|
||||
dependencies:
|
||||
'@xterm/xterm': 6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4)
|
||||
'@xterm/xterm': 6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96)
|
||||
|
||||
'@xterm/addon-unicode11@0.10.0-beta.300(@xterm/xterm@6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4))':
|
||||
'@xterm/addon-unicode11@0.10.0-beta.300(@xterm/xterm@6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96))':
|
||||
dependencies:
|
||||
'@xterm/xterm': 6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4)
|
||||
'@xterm/xterm': 6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96)
|
||||
|
||||
'@xterm/addon-web-links@0.13.0-beta.300(@xterm/xterm@6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4))':
|
||||
'@xterm/addon-web-links@0.13.0-beta.300(@xterm/xterm@6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96))':
|
||||
dependencies:
|
||||
'@xterm/xterm': 6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4)
|
||||
'@xterm/xterm': 6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96)
|
||||
|
||||
'@xterm/addon-webgl@0.20.0-beta.299(patch_hash=94687e89a0115e6e6aa102837f986debdc029c091527ee5eb4a4e17ceaf9473e)(@xterm/xterm@6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4))':
|
||||
'@xterm/addon-webgl@0.20.0-beta.299(patch_hash=94687e89a0115e6e6aa102837f986debdc029c091527ee5eb4a4e17ceaf9473e)(@xterm/xterm@6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96))':
|
||||
dependencies:
|
||||
'@xterm/xterm': 6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4)
|
||||
'@xterm/xterm': 6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96)
|
||||
|
||||
'@xterm/headless@6.1.0-beta.302': {}
|
||||
|
||||
'@xterm/xterm@6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4)': {}
|
||||
'@xterm/xterm@6.1.0-beta.303(patch_hash=6601602d8b599cdffec11997acdc7e70a8297422d70ebdbce35c653899d93a96)': {}
|
||||
|
||||
abbrev@4.0.0: {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user